Files
chktime/README.md
T

190 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# chktime
Claude Code の **タスク(セッション)ごとのチャットのやりとりの時刻**を一覧表示するローカル Web アプリ。
`~/.claude/projects/<プロジェクト>/<セッションID>.jsonl`(Claude Code の会話ログ)を解析して、
「いつ何を頼んで、返事が来るまで何秒かかって、その作業が何分続いたか」を表にします。
追加ライブラリなし(Python 3.9+ の標準ライブラリのみ)。
外部への通信は **[Redmine へ起票](#redmine-へ起票)** と **[n8n へ送る](#n8n-へ送る)** を使ったときだけです。
それ以外はローカルの jsonl を読むだけで、どこにも接続しません。
## 起動
```bash
python chktime.py
```
ブラウザで <http://127.0.0.1:8787/> が開きます。
デスクトップに以下のショートカットを作成済みです。
| ショートカット | 動作 |
| --- | --- |
| **chktime 起動** | `pythonw chktime.py` をコンソールなしで起動し、ブラウザを開く。すでに起動中ならタブを開くだけ |
| **chktime 停止** | `stop.cmd` を実行。`chktime.py` を実行中の python/pythonw を終了(結果を2秒表示) |
`run.bat`(コンソール表示・ログを見たい時用)と `stop.cmd` は直接ダブルクリックでも使えます。
※ `.cmd` / `.bat` は cmd の既定コードページで読まれるため、中身は ASCII のみにしています。
| オプション | 既定 | 説明 |
| --- | --- | --- |
| `--port` | 8787 | ポート番号 |
| `--host` | 127.0.0.1 | 待ち受けアドレス |
| `--dir` | `~/.claude/projects` | ログの置き場所 |
| `--interval` | 2.0 | ログ更新を見に行く間隔(秒)。自動追従の反応速度 |
| `--redmine-config` | `redmine.json` | Redmine 連携の設定ファイル |
| `--n8n-config` | `n8n.json` | n8n 連携の設定ファイル |
| `--no-browser` | – | ブラウザを自動で開かない |
## 画面
- **左:プロジェクト** — 作業ディレクトリごと。タスク数・ログ容量・最終更新。先頭の **▦ すべてのプロジェクト** を選ぶと横断モードになります。
- **中央:タスク一覧** — 1 セッション = 1 行。タイトル(`/title` 指定 → AI 命名 → 最初のプロンプトの順)、開始日時、往復数、全体時間、作業時間。上の検索窓でタイトル/プロンプト本文を絞り込み。横断モードでは全プロジェクトのタスクを開始日時の新しい順に並べ、各行にプロジェクトを表示します(検索はパスにも効きます)。
- **右:やりとりの時刻** — 1 ターン(あなたの発言 → Claude の応答一式)が 1 行。ヘッダの **やりとり / 日別集計 / ガント** タブで表示を切り替えます。
### 右ペインの列
| 列 | 意味 |
| --- | --- |
| 発言時刻 | あなたが送信した日時 `MM/DD hh:mm:ss.s`(ヘッダの「経過時間で表示」で、セッション開始からの相対表示に切替) |
| 空き | 前のターンが終わってから次の発言までの間隔。前の応答中に送信した場合は **並行** |
| 初回応答 | 送信から Claude の最初のメッセージが返るまで |
| 応答時間 | 送信からそのターンの最後のメッセージ(ツール実行・サブエージェント含む)まで |
| 完了時刻 | そのターンの応答が終わった時刻(相対表示時はセッション開始からの経過) |
| タイムライン | 灰=空き、青=初回応答待ち、緑=応答中 |
| 内訳 | そのターンの応答メッセージ数 / ツール呼び出し回数 |
| やりとり | 送信内容(クリックで全文展開)と使用ツールの内訳 |
上部サマリの **作業時間合計** は各ターンの応答時間の合計(=実際に Claude が動いていた時間)、
**全体** は最初から最後までの経過時間です。放置時間が長いと両者は大きく離れます。
### 日別集計タブ
ターンをローカル日付で集計し、日ごとの **作業時間 / 往復数 / タスク数** を棒グラフと表で出します。
- 表示範囲は **14日 / 30日 / 90日 / 全期間**。活動のない日も 0 の列として並ぶので、間隔がそのまま日付軸になります。
- 右の**日付入力**で期間を直接指定できます(片方だけでも可。開始のみ→その日から今日まで、終了のみ→その日まで全部)。指定中は日数ボタンの選択が外れ、**✕** で解除すると日数指定に戻ります。
- 横断モードでは**凡例のプロジェクトをクリックすると集計から外せます**。外したものは下の「除外中」に並び、クリックで戻せます(「すべて戻す」でまとめて解除)。除外は棒グラフ・表・合計・CSV すべてに効きます。
- 横断モードでは棒を**プロジェクト別の積み上げ**にします(作業時間上位 7 件に色、残りは「その他」)。単一プロジェクト選択時は 1 色。
- 棒にカーソルを当てると、その日の内訳(プロジェクト別の作業時間)が出ます。土日は背景を薄く敷いています。
- 棒の高さは応答時間の合計なので、**セッションを並行して動かした日は 24 時間を超えることがあります**。
### ガントタブ
時間軸に並べたガントチャートです。左上の **タスク / ターン** で並べる単位を切り替えます。
#### タスク単位
タスク(セッション)を 1 行ずつ並べます。
- **薄いバー** = 開始から終了までの全体、**濃い部分** = 実際に応答が動いていた区間(各ターンの発言〜応答完了。1 秒以内の隙間は繋げています)。放置していた時間がそのまま隙間として見えます。
- 表示範囲は **1日 / 3日 / 7日 / 14日 / 30日 / 全期間**。1〜3日なら軸が時刻目盛りに切り替わります。土日の帯・日境界の線・現在時刻の点線付き。
- 横断モードでは行の色がプロジェクトを表します(凡例は作業時間上位 7 件)。
- 行にカーソルを当てるとタイトル・プロジェクト・開始→終了・全体/作業/往復数、クリックするとそのタスクの「やりとり」を開きます。
- **表示範囲より前から続いているタスクは、全体時間・往復数も範囲内だけを数えます**(ツールチップの表示が「全体」→「表示範囲内」に変わります)。上の「最長タスク」も同じ基準です。開始→終了の日時だけは実際の値を出すので、範囲外にはみ出していることはそこで分かります。
- 同じ時間帯に複数のバーが重なっていれば、セッションを並行して動かしていた時間です。
#### ターン単位
選択中のタスクのターンを 1 行ずつ、**絶対時刻の共通軸**に並べます。やりとりタブのタイムライン列は行ごとに正規化されているため長さを比べられませんが、こちらは横位置と長さがそのまま実時間です。
- **青** = 初回応答までの待ち、**緑** = 応答中(ツール実行・サブエージェント含む)、**灰** = 応答が返らなかったターン。行と行の空白がそのまま放置していた時間です。
- 表示範囲はそのタスクの開始〜終了に自動で合わせます(目盛りは秒〜日単位で自動選択)。バーが横に重なっていれば、前の応答中に次を送った(並行)ターンです。
- 行にカーソルを当てると発言内容・時刻・初回応答/応答時間/空き、クリックすると「やりとり」タブの該当行に移動します。
- タスクを選ばずに開いた場合は、中央のタスク一覧から選ぶと表示されます。ターン単位を表示している間は、タスクを選び直してもガントのまま切り替わります。
`CSV` ボタンでタスク一覧・ターン一覧・日別集計・ガントのタスク一覧をそれぞれ書き出せます(Excel 用 BOM 付き UTF-8)。
横断モードのタスク一覧 CSV にはプロジェクト列が付きます。日別集計 CSV はプロジェクトごとの列を持つ横持ち形式です。
ガントの CSV には `clipped` 列があり、`1` なら表示範囲で切り詰めた行です。
## 自動追従
ヘッダの **自動追従** をオンにすると、ログが増えるたびに表示を作り直します。Claude Code を
動かしながら開いておく用途向けです。オン/オフの状態はブラウザに記憶されます(既定はオン)。
- サーバが `--interval` 秒ごとに jsonl を stat して変化を検出し、SSE(`/api/watch`)でブラウザに通知します。ブラウザ側は 1.2 秒まとめてから読み直します。
- タブを閉じた接続は 5 秒以内に検出して片付けます(20 秒ごとの ping を待ちません)。
- 更新時も**選択中のプロジェクト・タスク・スクロール位置・絞り込み・開いているタブは保たれます**。やりとりを末尾まで見ていた場合は、追記された行に追従して末尾に留まります。
- ヘッダ右の表示が状態です。`● 追従中` → 変化を検出すると `● 更新あり…` → 反映後に `● 更新 hh:mm:ss`。切断されると `● 切断(再接続待ち)` となり、自動で再接続します。
- オフの間に増えたぶんは、次にオンにした時点でまとめて取り込みます。
- オフのときは従来どおり **再読込** ボタンで更新します(解析キャッシュも捨てる強制再読込)。
## Redmine へ起票
ヘッダの **Redmine へ起票** で、作業の記録を Redmine のチケットにまとめて登録できます。
外部へ通信する機能です(ほかに [n8n へ送る](#n8n-へ送る) があります)。使わなければ一切接続しません。
### 準備
ダイアログ右上の **設定** で、Redmine の URL と API アクセスキーを入れて保存します。
キーは Redmine の「個人設定 → API アクセスキー」で表示できます。
- 設定は `redmine.json`(`--redmine-config` で変更可)に保存されます。**キーが平文で入るので git 管理外**です。
- 社内 Redmine のような自己署名証明書のサーバでは「証明書を検証する」を**外して**ください。
- **間隔(秒)** は 1 件ごとに空ける待ち時間です。連続アクセスに 403 を返すサーバがあるため既定 7 秒。速いサーバなら短くできます。
### 起票する
**下書きを作る → 中身を確かめる → 起票する** の 3 段です。取得元は 2 つあります。
| 取得元 | 内容 |
| --- | --- |
| **タスクから** | 中央の一覧に出ているタスクから選び、1 タスク = 1 チケットにします。本文は期間・全体/作業時間・往復数・ツール呼び出し数と、やりとりの一覧(発言時刻・要約・応答時間、最大 60 件)。開始日/期日はタスクの開始・終了日 |
| **git 履歴から** | 指定したリポジトリのコミットをまとめてチケットにします。本文はコミットの一覧(日付・短縮ハッシュ・件名、最大 80 件) |
git 履歴のまとめ方は **日ごと / 日 × 種別(`feat:` `fix:` `docs:` …)/ 期間まとめて 1 件** から選びます。
分類は**件名の prefix と日付だけを見た機械的なもの**で、中身の判断はしません。期間は空にすると全履歴です。
下書きは件名をクリックすると本文を確認でき、チェックを外せば除外できます。
起票先のプロジェクト・トラッカー・ステータス・担当・進捗 100% はダイアログ上部で指定します(次回も同じ値で開きます)。
- 起票は 1 件ずつ間隔を空けて流すので、**数十件だと数分かかります**。進捗と結果(チケット番号/失敗理由)はダイアログに出ます。実行中もダイアログは閉じられます。
- Redmine は**作成時の状態指定を無視する**ため、状態を指定した場合は作成直後に更新をもう 1 回投げて付け直します(1 件あたり 2 回アクセスします)。
## n8n へ送る
右ペインの **n8n へ送る** タブで、Claude のセッションから作った「作業区間」を
n8n の work-tracker(`work_log`)に送れます。手で作業開始・終了を記録しなくても済むようにするための機能です。
- **作業区間**: 同じセッションの中で、応答が終わってから次の発言までの空きが 30 分未満ならつなぎ、
30 分以上で区切ります。5 分未満の区間は出しません(どちらも接続設定で変更可)。
まだ応答中・ログ更新から 30 分以内の最後の区間は「進行中」で、送れません。
一晩中動かし続けたセッションは 1 つの長い区間になるので、送る前に時間を確かめてください。
- **続きのセッション**: 会話を続けると前の履歴が新しいセッションのファイルに同じ時刻のまま写されます。
開始時刻が同じ区間は写しとみなし、いちばん後まで続いているものだけを出します。
- **タスク名**: 初期値は `[作業フォルダ名] セッションのタイトル`。「選ぶ」で Redmine のチケットを検索して選ぶと
`[Redmine のプロジェクト名] #番号 題名` になり、work-tracker の Redmine 工数同期とガントの実績に載ります。
決めた名前はセッションごとにブラウザが覚え、同じセッションの他の区間にも入ります(切り替え可)。
- **重なり**: すでに work_log にある記録や、一覧の他の区間と時間が重なる区間には印が付き、最初は送らない状態です。
- **二重登録の防止**: 区間ごとの `source`(`chktime:<セッションID>:<開始時刻>`)を work_log に保存し、
n8n 側で同じ `source` は追加しません。送信済みの区間は選べません。
ただし 2 つの画面から同時に送ると二重に入ることがあります(確認と追加が一度の処理ではないため)。
- **時刻**: 送るときはブラウザの値を使わず、サーバーがログから区間を計算し直します。
- **設定**: `n8n.json`(git 管理外。`--n8n-config` で場所を変更可)。タブの「接続設定」からも変えられます。
`{"url": "https://n8n.yfox.jp", "token": "<合言葉>", "verifySsl": false, "gapMinutes": 30, "minMinutes": 5}`
合言葉は n8n の Header Auth 認証情報(ヘッダー `X-WT-Token`)と同じ値。画面とログには出しません。
- 送り先は n8n のワークフロー `work-tracker--import`(`/webhook/wt-import-rows` と `/webhook/wt-import`)。
## 解析の仕様(ざっくり)
- ターンの起点は「人間の発言」。ツール実行結果・システム挿入・サブエージェントの発言は起点にしません。
- `/model` などのスラッシュコマンドは `command`、コンパクト後の自動継続は `resume` とラベル表示します。
- `[Request interrupted...]`(Esc 中断)が含まれるターンには ⚠ を付けます。
- セッション再開時に書かれるメタ行(attachment / system / mode 等)は、ターンの所要時間には数えません。
- 解析結果はファイルの更新時刻+サイズでキャッシュします。変わったファイルだけ再解析するので、自動追従がオンなら手動操作は不要です。
- 追記されただけのファイルは**増えたぶんだけ**読み足します(書きかけの行は次回に持ち越し)。先頭と解析済み末尾のバイト列が変わっていた場合=ログが書き換えられた場合は頭から読み直します。
## ファイル
```
chktime.py サーバ + jsonl 解析 + Redmine 連携 + n8n 連携
web/index.html 画面
web/app.js 表示ロジック
web/n8n.js 「n8n へ送る」タブ
web/style.css スタイル
redmine.json Redmine 連携の設定(APIキーを含む。git 管理外・自動生成)
n8n.json n8n 連携の設定(合言葉を含む。git 管理外)
tests/ unittest(python -m unittest discover -s tests -v)
```