索引を作らずに Claude Code と codex の過去セッションを探す session-grepをつくった
一言で: ~/.claude/projects と ~/.codex/sessions の jsonl を rg で直接読む CLI を作った。 語を含むセッションごとに「何の話か」と再開コマンドを出す。 Claude Code の skill として agent からも呼ぶ。
前提: hoshinotsuyoshi/session-grep (2026-11-09 時点でpublic)。 動かすには rg と、 Ruby 3.4 以上か配布しているバイナリのどちらかが要る。
困っていたこと
Claude Code と codex を並べて agent を何体も同時に動かしていると、 前の経緯がどのセッションにあったか分からなくなる。 経緯を確かめたい agent も同じ状態になる。
rg -l で jsonl を探すと、 分かるのはファイルパスだけで何の話かは開くまで分からない。 さらに skill 一覧や CLAUDE.md が毎セッションのログに入る。 そのため skill の名前で探すと大半のファイルに当たる。
直近 2 週間の Claude Code のログで、 ある自作 skill の名前を探した数 (2026-11-08)。
やり方
当たった数
rg -l -F <skill 名>
323 ファイル (subagent のログ込み)
session-grep <skill 名> --all
253 セッション
session-grep <skill 名> (既定)
5 セッション
この skill を実際に使ったり話題にしたりしたのは 5 セッションだった。 残りのセッションでは skill 一覧に名前が載っていただけ。
使い方
以下の出力例は架空のログで動かしたもの。
session-grep flaky Time.now
2026-11-02 10:40 claude ~/src/myapp main
title: flaky test の調査
first: CI の test が flaky。 再現手順を調べて
[human 10:10] CI の test が flaky。 再現手順を調べて
[agent 10:25] spec/models/user_spec.rb が時刻に依存していて flaky になっています。 Time.now を固定すると再現しなくなりました。
resume: cd /home/me/src/myapp && claude --resume 0a1b2c3d-1111-4222-8333-944455566677
1 セッションを 1 ブロックで出す。 見出しの行には最後の発言の時刻・tool・cwd・ブランチが並ぶ。 その下がタイトル・最初の発言・当たった発言・再開コマンド。 resume: の行をコピーすればそのセッションに戻れる。
検索語は固定文字列で、 複数指定すると AND になる。 全部の語が同じセッションのどこかにあれば当たり、 同じ行にある必要はない。 上の例でも flaky は人の発言に、 Time.now は agent の返答にある。 --repo は起動した cwd で絞る。 --since / --until はログの更新時刻で絞る。 --show <id の頭> は 1 セッションの人と agent の発言を時系列で出す。
agent には --json で JSON Lines (1 行 1 セッション) を渡す。
session-grep flaky Time.now --json | jq -c '{session_id,cwd_exists,title,hit_counts,resume}'
{"session_id":"0a1b2c3d-1111-4222-8333-944455566677","cwd_exists":false,"title":"flaky test の調査","hit_counts":{"human":1,"meta":1,"agent":1},"resume":"cd /home/me/src/myapp && claude --resume 0a1b2c3d-1111-4222-8333-944455566677"}
worktree を消したあとなどで cwd がもう無いと cwd_exists が false になる。 この例は架空のパスなので false になっている。
当たった行を種類で分ける
session-grep は当たった行を「誰の発言か」で分け、 既定では人と agent の発言だけを出す。
kind
中身
既定
human
人の入力
表示
hc
agent 間のメッセージ (SendMessage など)
表示
agent
agent の返答本文
表示
tool
tool の入力と出力
件数だけ
thinking
thinking / reasoning
件数だけ
meta
skill 一覧・CLAUDE.md・AGENTS.md・cwd などのメタデータ
数えるが出さない
human / hc / agent に 1 件も当たらないセッションは出さない。 skill 一覧にしか名前が無いセッションはこの規則で結果から外れる。 上の JSON の "meta":1 は、 タイトルなどのメタデータに当たった行の数。 tool の出力の中まで探すときは --all を付ける。
ログの形式に合わせた扱いもある。
Claude Code で fork したセッションは fork 元の行を複製して持つ。 複製された行は数えず、 fork of: <元の id> だけを出す
subagent のログに当たったら親セッションにまとめて agent@<agent_id> と表示する
codex の event_msg は response_item と同じ発言の重複なので数えない。 # AGENTS.md instructions で始まる発言は meta にする
人の発言の前に付く <system-reminder> のブロックは外して判定する
検索を頼んだ依頼文には検索語が入るので、 実行中のセッション ($CLAUDE_CODE_SESSION_ID) は既定で除く
似たツールとの違い
同じ目的のツールはほかにもある。
memex は Claude Code と codex の両方に対応し、 BM25 の索引で探す。 embedding を作れば semantic 検索もできる。 subagent の当たりを親セッションにまとめる点、 fork でコピーされた履歴を数えない点、 <system-reminder> を人の発言と分ける点は session-grep と同じ。 CLI・TUI・MCP があり、 Claude Code の skill も入れられる
cass は SQLite の索引を持つ TUI と CLI。 30 種類以上の agent のログに対応している
recall は全文検索の TUI。 agent 向けに recall search もある
機能だけを見れば memex がほぼ同じことをする。 session-grep の違いは次の 4 つ。
session-grep の場合
索引
作らない。 毎回 rg で jsonl を読むので初回の索引作りもデーモンも無い。 古い索引に当たることもない
依存
Ruby の標準ライブラリと rg だけ。 gem は使わない。 バイナリなら rg だけ
大きさ
本体は bin/session-grep の 1 ファイルで 716 行。 何をしているか読み切れる
使い方
agent が CLI として呼ぶ前提。 --json と SKILL.md を同梱している
Claude Code の claude --resume と codex の codex resume にもピッカーがある。 Claude Code のドキュメントによると、 ピッカーでは文字を打って絞り込める。 PR の URL を貼るとその PR を作ったセッションも出る。 会話の本文まで検索するかは Claude Code と codex のどちらのドキュメントにも書いていない。
索引なしでの速さ
session-grep は語ごとに rg -l で候補のファイルを絞り、 語を含む行だけを JSON としてパースする。 手元のログは Claude Code が 1.3GB、 codex が 460MB ある。 全期間と全 repo を対象に README のようなよくある語を探すと 4.9 秒かかる。 1 つの repo の 2 週間分に絞った検索は 0.16 秒だった。
Spinel のバイナリ
Spinel でビルドしたバイナリも配っている。 Ruby が無くても rg とバイナリだけで動く。 Ruby 版の bin/session-grep が正で、 バイナリは同じソースから作る。
Ruby 版との比較
起動
約 40ms → 約 4ms
--show
約 3.5 倍
検索
1.1〜1.4 倍
測った条件は実ログ約 1.8GB・macOS arm64・Ruby 3.4.4 と 4.0.7 で、 ログはページキャッシュに載った状態。 7 通りの引数をそれぞれ 7 回動かした中央値。 検索の時間の大半は当たったログの行を読んで JSON をパースする処理なので、 バイナリにしても検索はあまり速くならない。
最新は v0.1.1。 GitHub Releases に macOS arm64 と Linux x86_64 の tar.gz と SHA256SUMS を置いている。 tar.gz にはバイナリと LICENSE と THIRD_PARTY_NOTICES が入っている。 バイナリには session-grep 以外のコードも入るので、 その表示が要る。 入るのは Spinel のランタイム、 mruby の正規表現エンジンと多倍長整数、 Spinel に同梱された optparse・shellwords・time などの Ruby のライブラリ、 正規表現エンジンが使う Unicode の文字表。 Linux 版は glibc 2.34 以上で動く。
v* のタグを push すると GitHub Actions が両 OS でバイナリをビルドし、 既存のテストをバイナリに向けて流す。 さらに合成したログで Ruby 版とバイナリを同じ引数で動かす。 stdout・stderr・終了コードが一致することを確かめてから Release を作る。 Spinel のコミットは .spinel-version で固定している。
agent から使う
PR や issue の URL を渡して「どこまでやったっけ」と聞くと、 agent が SKILL.md を読んで session-grep <URL> を実行する。 agent に作業を渡すときは「経緯は session-grep で調べてよい」「細部は session-grep --show <id> で読む」と書く。 これで引き継ぎのメモに経緯を全部書かなくて済む。
SKILL.md には agent が勝手に resume せず人に渡すことも書いている。 session-grep は再開コマンドを出すところまでで、 戻るかどうかは人が決める。
残っていること
--repo は起動した cwd で判定する。 別の repo の cwd から dotfiles を直したセッションは --repo dotfiles では出ない。 漏らしたくなければ --repo を付けずに語で探す
よくある語を全期間で探すと数秒かかる
当たった箇所の前後だけを出す機能はまだ無い。 cass には view -n 42 -C 3 のような前後表示がある
Mentions
索引を作らずに Claude Code と codex の過去セッションを探す session-grepをつくった 一言で: ~/.claude/projects と ~/.codex/sessions の jsonl を rg で直接読む CLI を作った。 語を含むセッションごとに「何の話か」と再開コマンドを出す。 Claude Code の skill として agent からも呼ぶ。 前提: hoshinotsuyoshi/session-grep (2026-11-09 時点でpublic)。 動かすには rg と、 Ruby 3.4 以上か配布しているバイナリのどちらかが要る。 困っていたこと Claude Code と codex を並べて agent を何体も同時に動かしていると、 前の経緯がどのセッションにあったか分からなくなる。 経緯を確かめたい agent も同じ状態になる。 rg -l で jsonl を探すと、 分かるのはファイルパスだけで何の話かは開くまで分からない。 さらに skill 一覧や CLAUDE.md が毎セッションのログに入る。 そのため skill の名前で探すと大半のファイルに当たる。 直近 2 週間の Claude Code のログで、 ある自作 skill の名前を探した数 (2026-11-08)。 やり方 当たった数 rg -l -F <skill 名> 323 ファイル (subagent のログ込み) session-grep <skill 名> --all 253 セッション session-grep <skill 名> (既定) 5 セッション この skill を実際に使ったり話題にしたりしたのは 5 セッションだった。 残りのセッションでは skill 一覧に名前が載っていただけ。 使い方 以下の出力例は架空のログで動かしたもの。 session-grep flaky Time.now 2026-11-02 10:40 claude ~/src/myapp main title: flaky test の調査 first: CI の test が flaky。 再現手順を調べて [human 10:10] CI の test が flaky。 再現手順を調べて [agent 10:25] spec/models/user_spec.rb が時刻に依存していて flaky になっています。 Time.now を固定すると再現しなくなりました。 resume: cd /home/me/src/myapp && claude --resume 0a1b2c3d-1111-4222-8333-944455566677 1 セッションを 1 ブロックで出す。 見出しの行には最後の発言の時刻・tool・cwd・ブランチが並ぶ。 その下がタイトル・最初の発言・当たった発言・再開コマンド。 resume: の行をコピーすればそのセッションに戻れる。 検索語は固定文字列で、 複数指定すると AND になる。 全部の語が同じセッションのどこかにあれば当たり、 同じ行にある必要はない。 上の例でも flaky は人の発言に、 Time.now は agent の返答にある。 --repo は起動した cwd で絞る。 --since / --until はログの更新時刻で絞る。 --show <id の頭> は 1 セッションの人と agent の発言を時系列で出す。 agent には --json で JSON Lines (1 行 1 セッション) を渡す。 session-grep flaky Time.now --json | jq -c '{session_id,cwd_exists,title,hit_counts,resume}' {"session_id":"0a1b2c3d-1111-4222-8333-944455566677","cwd_exists":false,"title":"flaky test の調査","hit_counts":{"human":1,"meta":1,"agent":1},"resume":"cd /home/me/src/myapp && claude --resume 0a1b2c3d-1111-4222-8333-944455566677"} worktree を消したあとなどで cwd がもう無いと cwd_exists が false になる。 この例は架空のパスなので false になっている。 当たった行を種類で分ける session-grep は当たった行を「誰の発言か」で分け、 既定では人と agent の発言だけを出す。 kind 中身 既定 human 人の入力 表示 hc agent 間のメッセージ (SendMessage など) 表示 agent agent の返答本文 表示 tool tool の入力と出力 件数だけ thinking thinking / reasoning 件数だけ meta skill 一覧・CLAUDE.md・AGENTS.md・cwd などのメタデータ 数えるが出さない human / hc / agent に 1 件も当たらないセッションは出さない。 skill 一覧にしか名前が無いセッションはこの規則で結果から外れる。 上の JSON の "meta":1 は、 タイトルなどのメタデータに当たった行の数。 tool の出力の中まで探すときは --all を付ける。 ログの形式に合わせた扱いもある。 Claude Code で fork したセッションは fork 元の行を複製して持つ。 複製された行は数えず、 fork of: <元の id> だけを出す subagent のログに当たったら親セッションにまとめて agent@<agent_id> と表示する codex の event_msg は response_item と同じ発言の重複なので数えない。 # AGENTS.md instructions で始まる発言は meta にする 人の発言の前に付く <system-reminder> のブロックは外して判定する 検索を頼んだ依頼文には検索語が入るので、 実行中のセッション ($CLAUDE_CODE_SESSION_ID) は既定で除く 似たツールとの違い 同じ目的のツールはほかにもある。 memex は Claude Code と codex の両方に対応し、 BM25 の索引で探す。 embedding を作れば semantic 検索もできる。 subagent の当たりを親セッションにまとめる点、 fork でコピーされた履歴を数えない点、 <system-reminder> を人の発言と分ける点は session-grep と同じ。 CLI・TUI・MCP があり、 Claude Code の skill も入れられる cass は SQLite の索引を持つ TUI と CLI。 30 種類以上の agent のログに対応している recall は全文検索の TUI。 agent 向けに recall search もある 機能だけを見れば memex がほぼ同じことをする。 session-grep の違いは次の 4 つ。 session-grep の場合 索引 作らない。 毎回 rg で jsonl を読むので初回の索引作りもデーモンも無い。 古い索引に当たることもない 依存 Ruby の標準ライブラリと rg だけ。 gem は使わない。 バイナリなら rg だけ 大きさ 本体は bin/session-grep の 1 ファイルで 716 行。 何をしているか読み切れる 使い方 agent が CLI として呼ぶ前提。 --json と SKILL.md を同梱している Claude Code の claude --resume と codex の codex resume にもピッカーがある。 Claude Code のドキュメントによると、 ピッカーでは文字を打って絞り込める。 PR の URL を貼るとその PR を作ったセッションも出る。 会話の本文まで検索するかは Claude Code と codex のどちらのドキュメントにも書いていない。 索引なしでの速さ session-grep は語ごとに rg -l で候補のファイルを絞り、 語を含む行だけを JSON としてパースする。 手元のログは Claude Code が 1.3GB、 codex が 460MB ある。 全期間と全 repo を対象に README のようなよくある語を探すと 4.9 秒かかる。 1 つの repo の 2 週間分に絞った検索は 0.16 秒だった。 Spinel のバイナリ Spinel でビルドしたバイナリも配っている。 Ruby が無くても rg とバイナリだけで動く。 Ruby 版の bin/session-grep が正で、 バイナリは同じソースから作る。 Ruby 版との比較 起動 約 40ms → 約 4ms --show 約 3.5 倍 検索 1.1〜1.4 倍 測った条件は実ログ約 1.8GB・macOS arm64・Ruby 3.4.4 と 4.0.7 で、 ログはページキャッシュに載った状態。 7 通りの引数をそれぞれ 7 回動かした中央値。 検索の時間の大半は当たったログの行を読んで JSON をパースする処理なので、 バイナリにしても検索はあまり速くならない。 最新は v0.1.1。 GitHub Releases に macOS arm64 と Linux x86_64 の tar.gz と SHA256SUMS を置いている。 tar.gz にはバイナリと LICENSE と THIRD_PARTY_NOTICES が入っている。 バイナリには session-grep 以外のコードも入るので、 その表示が要る。 入るのは Spinel のランタイム、 mruby の正規表現エンジンと多倍長整数、 Spinel に同梱された optparse・shellwords・time などの Ruby のライブラリ、 正規表現エンジンが使う Unicode の文字表。 Linux 版は glibc 2.34 以上で動く。 v* のタグを push すると GitHub Actions が両 OS でバイナリをビルドし、 既存のテストをバイナリに向けて流す。 さらに合成したログで Ruby 版とバイナリを同じ引数で動かす。 stdout・stderr・終了コードが一致することを確かめてから Release を作る。 Spinel のコミットは .spinel-version で固定している。 agent から使う PR や issue の URL を渡して「どこまでやったっけ」と聞くと、 agent が SKILL.md を読んで session-grep <URL> を実行する。 agent に作業を渡すときは「経緯は session-grep で調べてよい」「細部は session-grep --show <id> で読む」と書く。 これで引き継ぎのメモに経緯を全部書かなくて済む。 SKILL.md には agent が勝手に resume せず人に渡すことも書いている。 session-grep は再開コマンドを出すところまでで、 戻るかどうかは人が決める。 残っていること --repo は起動した cwd で判定する。 別の repo の cwd から dotfiles を直したセッションは --repo dotfiles では出ない。 漏らしたくなければ --repo を付けずに語で探す よくある語を全期間で探すと数秒かかる 当たった箇所の前後だけを出す機能はまだ無い。 cass には view -n 42 -C 3 のような前後表示がある