Files
chktime/CLAUDE.md
T
yoshikazuandClaude Opus 5 a458509014 Redmine へチケットを起票するボタンを追加
chktime のタスクと git 履歴から下書きを作り、まとめて Redmine へ登録できる
ようにした。外部通信はこの機能を使ったときだけで、使わなければ従来どおり
ローカルの jsonl を読むだけ。

- Redmine の REST API を叩く最小クライアント (標準ライブラリのみ)
- 下書きは「タスクから」(1タスク=1チケット) と「git 履歴から」
  (日ごと / 日 x 種別 / 期間一括) の 2 系統
- 起票は件数 x 間隔ぶん時間がかかるのでジョブにし、進捗だけ見に行く
- 作成時の status_id は Redmine 側で無視されるため、作成後に付け直す
- 連続アクセスに 403 を返すサーバがあるので 1 件ごとに間隔をあける (既定 7 秒)
- 自己署名証明書が普通なので、既定は証明書の検証なし
- API キーは redmine.json に置き、git 管理外にする

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-02 13:55:46 +09:00

102 lines
6.3 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 の会話ログ(`~/.claude/projects/<プロジェクト>/<セッションID>.jsonl`)を解析して、
タスクごとのやりとりの時刻を表示するローカル Web アプリ。使い方は [README.md](README.md) に書いてある。
## 構成
```
chktime.py サーバ + jsonl 解析 + Redmine 連携(これ1本で完結)
web/index.html 画面の骨組み
web/app.js 表示ロジック(フレームワークなし・素の DOM 操作)
web/style.css スタイル(ダークテーマ固定)
run.bat コンソール表示で起動
stop.cmd 起動中の chktime を終了
redmine.json Redmine 連携の設定(自動生成・git 管理外)
```
`chktime.py` の中身は上から
「解析ロジック → Store(キャッシュ)/ Watcher(更新検出)→ Redmine 連携 → HTTP」の順。
## 守ること
- **標準ライブラリのみ**。外部パッケージも CDN も使わない。ブラウザ側もフレームワークなし。
- **外部通信は Redmine 連携だけ**。それ以外はローカルの jsonl を読むだけで、どこにも接続しない。
新しい通信先を増やすときは、この行を書き換えるところから始める。
- **API キーをリポジトリに入れない**。`redmine.json` は `.gitignore` 済み。ログにも出さない
(`/api/redmine/config` はキーの有無だけ返す)。
- 会話ログは**読み取り専用**。検証で書き換えるときはスクラッチパッドにコピーしてから行う。
- `.bat` / `.cmd` は cmd の既定コードページで読まれるので**中身は ASCII のみ**(日本語を書かない)。
- コメント・コミットメッセージ・UI の文言は日本語。
- 機能を足したら README.md も更新する。
## 動かす
```bash
python chktime.py --port 8799 --no-browser
```
ユーザーは 8787 でデスクトップショートカットから常用しているので、**検証は 8799 など別ポート**で立てる。
停止は `stop.cmd`(ただし `chktime.py` を実行中の python/pythonw を**すべて**終了するので、
検証用だけを止めたいときはコマンドラインでポートを見て個別に kill する)。
## API
| ルート | 内容 |
| --- | --- |
| `/api/projects` | プロジェクト一覧 |
| `/api/sessions?project=` | そのプロジェクトのタスク一覧 |
| `/api/all` | 全プロジェクトのタスク一覧(横断モード) |
| `/api/session?project=&id=` | 1タスクのターン一覧 |
| `/api/daily?project=&days=&from=&to=&exclude=` | 日別集計 |
| `/api/timeline?project=&days=` | ガント(タスク単位)。ターン単位は `/api/session` から描く |
| `/api/watch` | SSE。ログが変わったら `change` を流す |
| `/api/rev` `/api/reload` | 現在の版 / キャッシュ破棄 |
いずれも `&format=csv` を付けると CSV を返す(Excel 用に BOM 付き)。
Redmine 連携(POST を使う唯一の系統):
| ルート | 内容 |
| --- | --- |
| `GET/POST /api/redmine/config` | 接続設定の取得(キーは返さない)/ 保存 |
| `GET /api/redmine/meta` | プロジェクト・トラッカー・ステータス・自分の情報 |
| `POST /api/redmine/draft` | 下書きを作る(`source=tasks` / `source=git`) |
| `POST /api/redmine/issues` | 起票をジョブとして開始。返り値の `id` で進捗を見る |
| `GET /api/redmine/job?id=` | 起票ジョブの進捗と結果 |
## 解析の前提
- ターンの起点は**人間の発言だけ**。ツール実行結果・システム挿入・サブエージェント(`isSidechain`)は起点にしない。
- 再開時に書かれるメタ行(attachment / system / mode / queue-operation)は**ターンの所要時間に数えない**。
セッション全体の開始・終了時刻には反映する。
- `tool_result` を含む行は巨大なので JSON パースせず、正規表現で `timestamp` だけ拾う。
- 解析結果は `(mtime, size)` でキャッシュし、追記されただけのファイルは**増えたぶんだけ**読み足す
(`SessionParser.read()`)。書きかけの行は消費せず次回に回す。先頭と解析済み末尾のバイト列が
変わっていたら書き換えとみなして頭から読み直す。
- 追記解析は状態を持つので、同じファイルを複数スレッドで同時に解析しない(`Entry.lock`)。
## 画面
- 3ペイン(プロジェクト / タスク一覧 / 詳細)。右ペインは **やりとり・日別集計・ガント**のタブ。
- 状態はすべて `app.js` 冒頭の `state` オブジェクト。再描画は `render*()` が `#detail-body` を作り直す方式。
- 自動追従(SSE)で作り直すときも、選択・スクロール位置・絞り込み・タブは保つ(`refreshFollow()`)。
- ガントの軸・背景・枠組みは `timeScale` / `axisSvg` / `gridSvg` / `ganttFrame` をタスク単位と
ターン単位で共有している。
- `/api/timeline` は表示範囲より前から続くセッションの全体時間・往復数・ツール数も範囲内に切り詰め、
`clipped` を立てて返す(作業時間だけ切り詰めると数字が食い違うため)。
- Redmine 起票のダイアログは `rm` オブジェクトが状態を持ち、`renderRedmine()` が中身を作り直す。
HTML 属性に NUL は入れられない(パーサが置換する)ので、タスクの識別は `data-project` /
`data-id` の 2 属性で持つ。
## Redmine 連携の落とし穴
実際に踏んだもの。同じところで転ばないように。
- **作成時の `status_id` は無視される**(既定の New で入る)。状態を指定したいなら作成後に
`PUT /issues/<id>.json` で付け直す(`Redmine.create()` がやっている)。
- **連続アクセスに 403 を返すサーバがある**。7〜8 秒あければ通る。`Redmine.throttle()` が
クライアント単位で間隔を保つので、クライアントは作り直さず使い回す。
- **自己署名証明書**が普通なので、既定は検証なし(`verifySsl: false`)。
- 起票は件数×間隔ぶん時間がかかるので HTTP を掴んだままにしない。`Jobs` に投げて進捗を取りに来てもらう。