モジュール名変更、npmリリース等したためこの記事の内容は古い内容です。
マネージャーに「バズる記事書いてよ」と言われたので最新の内容は下記をご確認ください。
TL;DR
Claude Codeの/compact
コマンドで生成されるJSONLファイルから自動的に日報を作成したくて、リアルタイムファイル監視+プラグインシステムを持つTypeScriptサーバーを作成しました。chokidarでファイル監視、プラグインで柔軟なフィルタリング・出力処理を実現し、作業記録のある程度の自動化に成功しました。もちろん実装の大半は(このQiitaも)Claude Codeです。
Plugin+PipeLineシステムで無限の可能性があるので、自作のフィルター/アウトプットで日報作成以上の使い道を考えてほしい。
はじめに
開発者の皆さんは、日々の作業記録をどのように管理していますか?
私は普段、Claude Codeを使って開発を進めているのですが、ある日/compact
コマンドを実行した時にJSONLファイルが生成されることに気づきました。このJSONLファイルには、作業履歴やコミュニケーションの記録が含まれており、「これを使って自動的に日報を作成できるのではないか?」という発想が浮かんだのです。
最初はHooksに期待したのですが、Hooksでは情報が少なく日報を書くには物足りないため、JSONLファイルをリアルタイムで監視・処理するJSONLストリームサーバーを作成しました。
作った背景:/compactコマンドから日報を作りたい
Claude Codeの/compact
コマンドを実行すると、~/.claude/projects/
配下のJSONLファイルに記録されます。このファイルには以下のような情報が含まれています:
- 作業セッションの記録
- コード変更の履歴
- エラーや警告の情報
- 思考プロセス
これらの情報を自動的に解析し、日報やレポートを生成できれば、作業の振り返りや報告書作成が格段に効率化できるはずです。
JSONLストリームサーバーとは
JSONLストリームサーバーは、指定したディレクトリ内のJSONLファイルをリアルタイムで監視し、ストリーム処理を行うTypeScriptアプリケーションです。
主な機能
- リアルタイム監視: chokidarを使用したファイル変更の自動検知
- プラグインシステム: フィルタリングと出力処理を拡張可能
- パイプライン処理: 複数のフィルター・出力を組み合わせた柔軟な処理
- 型安全: TypeScriptによる完全な型定義
技術的な構成
アーキテクチャ
src/
├── core/ # コア機能
│ ├── FileWatcher.ts # ファイル監視
│ ├── JsonlStreamProcessor.ts # ストリーム処理
│ ├── JsonlStreamServer.ts # メインサーバー
│ └── ProcessingPipelineManager.ts # パイプライン管理
├── plugins/ # プラグインシステム
│ ├── filters/ # フィルタープラグイン
│ │ ├── BaseFilterPlugin.ts
│ │ ├── KeywordFilterPlugin.ts
│ │ └── FieldMatchFilterPlugin.ts
│ └── outputs/ # 出力プラグイン
│ ├── BaseOutputPlugin.ts
│ ├── ConsoleOutputPlugin.ts
│ ├── FileOutputPlugin.ts
│ ├── HttpOutputPlugin.ts
│ └── MarkdownOutputPlugin.ts
├── config/ # 設定管理
│ └── ConfigLoader.ts
├── types/ # 型定義
│ └── index.ts
└── index.ts # エントリーポイント
使用技術
- TypeScript: ES2022、ESNext
- chokidar: ファイル監視
- readline: ストリーム処理
- ESM: モジュール形式
実装のポイント
1. プラグインシステム
フィルタープラグインと出力プラグインを分離し、組み合わせ可能な設計にしました。
// フィルタープラグインの例
export class KeywordFilterPlugin extends BaseFilterPlugin {
readonly name = 'KeywordFilter';
filter(record: JsonlRecord): boolean {
const keywords = this.options.keywords || [];
const content = JSON.stringify(record).toLowerCase();
return keywords.some(keyword =>
content.includes(keyword.toLowerCase())
);
}
}
// 出力プラグインの例
export class MarkdownOutputPlugin extends BaseOutputPlugin {
readonly name = 'MarkdownOutput';
async output(record: JsonlRecord): Promise<void> {
const markdown = this.convertToMarkdown(record);
await this.writeToFile(markdown);
}
}
2. パイプライン管理
複数のフィルター・出力を組み合わせて、柔軟な処理フローを実現しました。
export class ProcessingPipelineManager {
private async processPipelines(record: JsonlRecord): Promise<PipelineResult[]> {
const results: PipelineResult[] = [];
for (const pipeline of this.pipelines) {
const filter = this.filters.get(pipeline.filter);
if (filter?.filter(record)) {
for (const outputName of pipeline.outputs) {
const output = this.outputs.get(outputName);
await output?.output(record);
}
}
}
return results;
}
}
3. 設定ファイル
JSON形式の設定ファイルで、プラグインの組み合わせとパイプラインを定義できます。
{
"watchDirectory": "/Users/username/.claude/projects",
"plugins": {
"filters": [
{
"name": "ErrorFilter",
"module": "./dist/plugins/filters/KeywordFilterPlugin.js",
"options": {
"keywords": ["error", "ERROR"],
"mode": "include",
"caseSensitive": false
}
}
],
"outputs": [
{
"name": "MarkdownOutput",
"module": "./dist/plugins/outputs/MarkdownOutputPlugin.js",
"options": {
"outputPath": "./output/daily-report.md",
"template": "daily-report"
}
}
]
},
"pipelines": [
{
"name": "DailyReportPipeline",
"filter": "ErrorFilter",
"outputs": ["MarkdownOutput"]
}
]
}
使用方法
1. インストール
git clone https://github.com/username/jsonl-stream-server
cd jsonl-stream-server
npm install
2. 設定ファイルの作成
npm run dev -- --generate-config
3. 実行
# 開発環境で実行
npm run dev
# 設定ファイルを指定して実行
npm run dev -- ./my-config.json
# 本番環境で実行
npm run build
npm start
実際の使用例
エラーログの抽出
{
"name": "ErrorPipeline",
"filter": "ErrorFilter",
"outputs": ["ErrorLogOutput", "ConsoleOutput"]
}
日報の自動生成
{
"name": "DailyReportPipeline",
"filter": "AllDataFilter",
"outputs": ["MarkdownOutput"]
}
高優先度イベントの通知
{
"name": "HighPriorityPipeline",
"filter": "HighSeverityFilter",
"outputs": ["SlackNotification", "EmailAlert"]
}
開発で苦労したポイント
1. プラグインの動的ロード
TypeScriptでESMモジュールを動的にロードする際に発生した「Plugin is not a constructor」エラーの解決:
// 解決策:デフォルトエクスポートの追加
export default KeywordFilterPlugin;
export { KeywordFilterPlugin };
2. ファイル監視の最適化
chokidarの設定調整により、深い階層のファイル監視とパフォーマンスを両立:
const watcher = chokidar.watch(watchDirectory, {
ignored: /node_modules/,
persistent: true,
ignoreInitial: true,
followSymlinks: false,
alwaysStat: false,
atomic: true
});
3. 型定義の整理
NodeJS.Timeout型の問題を解決:
// 解決策
private debounceTimer: ReturnType<typeof setTimeout> | null = null;
日報の品質は?
あくまでも /compact
時点のコンテキストの内容が得られるだけではあるので、これだけでは足らず、出力された情報を元にClaude Codeに日報を書かせることでそこそこ実用的な内容になりそうという感じです。
今後の展望
- テスト実装: Jestによるユニットテスト・統合テストの追加
- Web UI: ダッシュボードでの可視化
- AI統合: より高度な日報生成
- 他サービス連携: Slack、Discord、Notion等との連携
- テンプレート機能: 日報フォーマットのカスタマイズ
まとめ
Claude Codeの/compact
コマンドから得られるJSONLファイルを活用して、開発者の作業記録を自動化するツールを作成しました。
プラグインシステムにより拡張性を確保し、様々な用途に対応できる設計になっているので、日報作成以外にも使い所はありそうです。
JSON Schema公開してほしい。
参考リンク
この記事が皆さんの開発効率化の参考になれば幸いです。質問やフィードバックがあれば、コメントでお聞かせください!