--- name: read-transcript description: Claude Code のセッショントランスクリプト (.jsonl) を、全文を読み込まずに段階的に調べる。セッションの経緯を第三者として検証したいとき、過去のやりとりを掘りたいときに使う。 license: CC0-1.0 compatibility: Reads Claude Code session transcripts under ~/.claude/. Requires jq and ripgrep. allowed-tools: Bash, Read, Grep, Glob --- # セッショントランスクリプトの読み方 ## 禁止 - **全文を `Read` しない。** トランスクリプトは数MB〜数十MBになる。`jq` / `rg` で絞ってから読む。 - **`head` で切って結論を出さない。** 件数が多いならファイルに落として `rg` で絞る。 段階1から順に進み、必要になった段階で止める。 ## 場所 ``` ~/.claude/projects/<スラッグ>/<セッションID>.jsonl ``` スラッグは作業ディレクトリのパスの `/` を `-` に置換したもの(`/Users/foo/Documents/bar` → `-Users-foo-Documents-bar`)。分からなければ `ls ~/.claude/projects/`。 セッションIDが分からなければ、最終更新が最新のファイルを使う: ```bash ls -t ~/.claude/projects/<スラッグ>/*.jsonl | head -1 ``` ## 段階1: ユーザー発言 `~/.claude/history.jsonl` に全プロジェクト分のユーザー入力が蓄積されている。フィールドは `timestamp`(エポックミリ秒)/ `project` / `sessionId` / `display` / `pastedContents`。トランスクリプトが削除された後も残る。 ```bash SID=<セッションID> jq -r --arg S "$SID" 'select(.sessionId==$S) | "\(.timestamp)\t\(.display)"' ~/.claude/history.jsonl ``` プロジェクト単位で横断するとき: ```bash jq -r 'select(.project | test("<プロジェクト名>")) | "\(.sessionId)\t\(.display)"' ~/.claude/history.jsonl ``` トランスクリプトから直接抜く場合: ```bash L=<トランスクリプトのパス> jq -r 'select(.type=="user") | select(.isSidechain != true) | (.message.content) as $c | if ($c|type)=="string" then "[\(.timestamp)] \($c)" else ([$c[] | select(.type=="text") | .text] | if length>0 then "[\(.timestamp)] " + join("\n") else empty end) end' "$L" ``` ## 段階2: 構造の計測 ```bash # アシスタントのターン数 jq -r 'select(.type=="assistant") | select(.isSidechain != true) | .uuid' "$L" | wc -l # ツール使用の内訳 jq -r 'select(.type=="assistant") | select(.isSidechain != true) | .message.content[]? | select(.type=="tool_use") | .name' "$L" \ | sort | uniq -c | sort -rn # エラーになったツール実行 jq -r 'select(.isSidechain != true) | .message.content[]? | select(.type=="tool_result") | select(.is_error==true) | "ERROR: " + ((.content|tostring)[0:200])' "$L" ``` ## 段階3: 時刻範囲で開く ```bash jq -r 'select(.timestamp >= "2026-08-12T04:10" and .timestamp <= "2026-08-12T04:15") | select(.type=="user" or .type=="assistant") | select(.isSidechain != true) | "[\(.timestamp)] \(.message.role): " + ((.message.content | if type=="string" then . else (map(if .type=="text" then .text elif .type=="tool_use" then " "+(.input|tostring|.[0:300]) elif .type=="tool_result" then "" else "<"+.type+">" end) | join("\n")) end))' "$L" ``` ## 段階4: 全体をファイルに落として絞る ```bash jq -r 'select(.type=="user" or .type=="assistant") | select(.isSidechain != true) | "[\(.timestamp)] \(.message.role): " + ((.message.content | if type=="string" then . else (map(if .type=="text" then .text elif .type=="tool_use" then " "+(.input|tostring|.[0:300]) elif .type=="tool_result" then "" else "<"+.type+">" end) | join("\n")) end))' "$L" > /tmp/flow.txt wc -l /tmp/flow.txt rg -n '<キーワード>' /tmp/flow.txt ``` ## 読めるもの / 読めないもの | | | |---|---| | ユーザー発言 / アシスタント発言 / ツール名・入力 / ツール結果 | 読める | | アシスタントの思考 | **読めない。** `thinking` ブロックは存在するが中身は空文字(`display: "omitted"`)。思考を読んだつもりで語らないこと | | サブエージェントの内部 | `.isSidechain == true`。メイン会話を見るときは除外 | ## トランスクリプトが無いとき `.jsonl` は残らないことがある(`sessions-index.json` と `memory/` だけが残る)。代替: | ソース | 得られるもの | |---|---| | `~/.claude/history.jsonl` | ユーザー発言全件(`sessionId` で絞れる)。アシスタント側は無い | | `<プロジェクト>/memory/*.md` | 過去セッションから蒸留されたフィードバック | | `<プロジェクト>/session-memory/summary.md` | セッション要約 | | `<プロジェクト>/sessions-index.json` | セッション索引(`fullPath` が消えていることもある) | ユーザー発言しか無い場合、直前のアシスタントの行動は**ユーザーが名指ししている内容と、直前のユーザー指示との差分からのみ**復元できる。推測で補完せず、復元した箇所はそう明記する。