本記事は、Claude Code Meetup Osaka で LT した内容を記事向けに再構成したものです。スライドでは雰囲気だけ見せたコードも、記事では全文載せています。
当日のスライドはこちらです。
TL;DR
- Claude Code に作業を頼んで待っている間、「いつ頼んで・いま何をしていて・何分かかったか」が全然わからなかったので、見えるようにした話
- Claude Code の hookとstatuslineを組み合わせることで可能。bash と jq を組み合わせた、50行ほどのコードで実現可能
- プロンプト入力欄の下が「実況ボード」化する
以下がイメージ
⏱ 依頼 13:33:42 → 完了 実行中…
13:34:45 ソースコードの確認中...
13:36:37 初版ができました。サブエージェントに…
- 実測してみたら、1ターンの中央値は約2.3分、最長は62分という結果でした
- 仕込み方の手順書は GitHub Gist で公開しています(末尾に記載)
はじめに:ギャルに言われて気づいた
突然ですが、業務で AI を活用している人、どのくらいいますか?
普通にいますよね。というか、この記事を読んでいる時点でだいたいそうだと思います。と思って、Gemini にこんな質問をしてみました。
「AIを業務で活用してる人ってどういうイメージ?」
ええやん!
でもちょっと忖度しすぎと言うか、買いかぶりすぎちゃう?と思ったので、逆の質問もしてみたくなりました。
「逆にネガティブな要素ってある?ギャルっぽく教えて」
ギャルっぽくといったのは、こう言ったほうがぶっちゃけてくれそうな気がしたからです。そしてその予想は的中しました。
!?
!?!?
!?!?!?
※ 全部、実際のスクリーンショットです。
「めっちゃ、めっちゃボロクソ言うやんけ!」
と思ってしまいました。とはいえ、どれも実際に気をつけないといけないことではあったので、程よい戒めにもなったなと思いました。
その中でも個人的に一番刺さったのが、ここです。
そういえば自分は、AIがどんな作業に対して、どのぐらい時間を使っているか、というのをあまり把握してないな???と思いました。
なんとなく以前から「調べたいな〜」とは思っていたんですが、具体的には何も行動していませんでした。
そんな折、とある勉強会で Claude Codeのhook機能について教えてもらう機会があり、「あ、もしかしたら、こういうことできるかも!?」と思って取り組んだのが今回の内容になります。
ということで、今回のテーマはこちら
Claude Code の hook 機能を使って、処理状況を見える化する
まずそもそもhookとは何なのか
イベント駆動でコマンドを自動実行する仕組み
Claude Code の hook は、ざっくり言うと 「Claude Code の中で何かが起きたら、settings.json に書いておいたコマンドが必ず呼ばれる」 という仕組みです。
Git の pre-commit と同じ発想で、「〇〇したら△△する」というのを先に仕込んでおくような感じです。中身は bash でも python でもHTTPアクセスでも何でも構いません。
これはhookの価値が一番出るポイントなんですが、hookは Claude に「毎回ログ書いてね」とお願いするのではなく「仕組みで強制的に動く」 というのが通常のプロンプトとは違う部分です。
ようはプロンプトで指示したことは守られる保証がないんですが、hookはモデルを経由するものではないので、必ず実行されます。つまり、やり忘れられる心配をしなくても良いのが一番のメリットですね。
じゃあhook は「いつ」発火するのか
代表的なイベントだけ抜粋します。
これを見るだけでも色んな仕込みの有用性が考えられそうですね。
| イベント名 | 発火するタイミング | 今回 |
|---|---|---|
SessionStart |
Claude Code を起動・再開したとき | |
UserPromptSubmit |
人間がプロンプトを送った瞬間 | ✅ |
PreToolUse / PostToolUse
|
ツール(Bash・Edit…)を実行する直前 / 直後 | |
MessageDisplay |
Claude の発言が画面に表示されるたび | ✅ |
Stop |
Claude が応答を終えたとき | ✅ |
Notification |
権限確認などの通知が出たとき |
イベントは全部で 28種類(2026年8月時点)ありますが、今回使うのは ✅ の3つになります。とはいえやることは「時刻と状況を記録する」だけです。
ちなみに PreToolUse で「rm -rf を止める」みたいな使い方もできます。これは使ってる人多いかも・・・?
スクリプト側では3つのルールがあります
hook として呼ばれるスクリプトの決まりごとは、以下の3つ。
-
入力:標準入力に JSONのログが流れる(
session_idcwdhook_event_name+ イベント固有の項目) -
出力:
exit 0で正常、exit 2で「異常通知」(stderr の内容を Claude へ伝えることが可能) - 今回は記録するだけなので 読むこと・書くこと・正常終了・・・という流れを基本的にたどる形になります
簡単に言うと、settings.jsonにshellを呼ぶ設定を追加するだけで、気軽に始められます。
第1章:実装しようと思ったきっかけ
普段Claude が働いている間、人間は別に作業を監視しているわけではないです。みなさんもきっとそのはず・・・。
となると、
- いま何をしているのか、わからない
- いつから待っているのか、覚えてない
- 離席して戻ると、途中経過ログが めっちゃ出てる(主にコード差分)
- 1ターンにかかった時間は、記録には残らない
- 厳密に言うと残ってはいるけど、決して見やすいわけじゃない
という状況になっている。
「実況ボード」的なものとその履歴が・・・欲しい!!
めっちゃ困っているわけではないですが、例えるなら野球のスコアボードのように、状況が常に見えていてほしいな〜と思いました。具体的には、プロンプト入力欄のすぐ下にそれが出ていてくれれば尚良し!
| 知りたいこと | 表示イメージ |
|---|---|
| いつ頼んだか | 依頼 15:55:12 |
| いま何をしているか | Claude の途中報告(最新3件ぐらいでいい) |
| いつ終わったか |
完了 16:02:40(=最終的な所要時間がわかる) |
つまり、ロギング(記録)と画面への反映(見せる)がセットが欲しいなと。
欲しい=正義。正義なら実装するべき。
(筆者は『Dr.STONE』が大好きで、龍水のこの言葉が好きです。)
第2章:どうやって実現したか
全体像:hookの仕組みを使ってログを残し、statusline側でそれを読むようにする
構成はこれだけ・・・といっても図に起こすとまあまあ複雑な感じになりました(笑)
hookの仕組みでイベントが起こるとかならずログに記録がされます。
→statusline は毎秒それを読み込むようにする。
→ファイル経由の疎結合で実現できて、各スクリプト自体は10行程度の内容で済む(これが意外と気に入ってるところ)
settings.json:どのタイミングでやるか?と何をするか?を記載
~/.claude/settings.json(全プロジェクト共通)または .claude/settings.json(プロジェクト単位)に、こんな風に書きます。
{
"hooks": {
"UserPromptSubmit": [
{ "hooks": [ { "type": "command", "command": "~/.claude/hooks/turn-start.sh" } ] }
],
"Stop": [
{ "hooks": [ { "type": "command", "command": "~/.claude/hooks/turn-end.sh" } ] }
],
"MessageDisplay": [
{ "hooks": [ { "type": "command", "command": "~/.claude/hooks/turn-message.sh" } ] }
]
},
"statusLine": {
"type": "command",
"command": "~/.claude/hooks/turn-statusline.sh",
"refreshInterval": 1
}
}
UserPromptSubmit / Stop / MessageDisplay にそれぞれ記載します。
記録係:開始と終了に shell を仕込む
UserPromptSubmit(ユーザーがClaudeに依頼した瞬間に発火するやつ)→ turn-start.sh を仕込む
#!/bin/bash
input=$(cat)
sid=$(echo "$input" | jq -r '.session_id')
dir=~/.claude/logs/turn-times
mkdir -p "$dir"
# 画面表示用の状態ファイル(セッション単位): 開始をセットし、前回の終了時刻・工程履歴はクリア
echo "{\"start\":\"$(date +%H:%M:%S)\"}" > "$dir/$sid.json"
# ログ(JSONL)
echo "{\"ts\":\"$(date -Iseconds)\",\"event\":\"start\",\"session\":\"$sid\",\"cwd\":$(echo "$input" | jq '.cwd')}" >> ~/.claude/logs/turn-times.jsonl
Stop(応答完了時にはそれ用のスクリプトが今度は発火)→ turn-end.sh を仕込んでおく
#!/bin/bash
input=$(cat)
sid=$(echo "$input" | jq -r '.session_id')
f=~/.claude/logs/turn-times/$sid.json
if [ -f "$f" ]; then
jq --arg t "$(date +%H:%M:%S)" '.end = $t' "$f" > "$f.tmp" && mv "$f.tmp" "$f"
fi
echo "{\"ts\":\"$(date -Iseconds)\",\"event\":\"end\",\"session\":\"$sid\"}" >> ~/.claude/logs/turn-times.jsonl
基本bashが使えればそれでOKです(jqだけインストールしておく必要があります)
実況機能の実現:途中報告を反映するshellを仕込む
ログの仕組みが作れても、結局それがプロンプトの画面に出なければ意味がないので最後にその表示機能を仕込みます。
Claude は作業の合間に「〜を確認します」「〜を反映しました」というのをよく実況してくれます。みなさんも目にしたこと多いはず。これはつまり実況音声みたいなものだろうと。なので、その冒頭40字を時刻つきでロギングして、実況テロップ化します。
MessageDisplay(Claude の発言表示ごとに実行される)→ turn-message.sh
#!/bin/bash
input=$(cat)
# 1メッセージが複数チャンクで届くことがあるため、先頭チャンク(index==0)だけ記録する
idx=$(echo "$input" | jq -r '.index // 0')
[ "$idx" != "0" ] && exit 0
# 本文フィールドは実測では delta、ドキュメント上は assistant_message。両対応にしてある
text=$(echo "$input" | jq -r '(.delta // .assistant_message // "") | split("\n")[0] | .[0:40]')
[ -z "$text" ] && exit 0
sid=$(echo "$input" | jq -r '.session_id')
f=~/.claude/logs/turn-times/$sid.json
[ -f "$f" ] || echo '{}' > "$f"
# 状態ファイルの steps に {時刻, 報告文の冒頭40字} を追記し、直近3件だけ保持
jq --arg t "$(date +%H:%M:%S)" --arg m "$text" '.steps = (((.steps // []) + [{"t":$t,"m":$m}]) | .[-3:])' "$f" > "$f.tmp" && mv "$f.tmp" "$f"
echo "{\"ts\":\"$(date -Iseconds)\",\"event\":\"message\",\"session\":\"$sid\",\"text\":$(echo "$input" | jq -c '(.delta // .assistant_message // "") | .[0:80]')}" >> ~/.claude/logs/turn-times.jsonl
ポイントとしては以下です
- 冒頭40字だけにする:メッセージは結構長い場合があるので、statusline に収まるサイズにする
-
直近3件だけ保持:実況中継は何件も残すとスペースが圧迫されるので、3件程度にしておく。まあ3件あればだいたいわかるんじゃないかなと思ってます。
- もし全容が見たければログの方を
tail -fすることで見ることも可能です。
- もし全容が見たければログの方を
表示係:statuslineに反映する係
turn-statusline.sh
#!/bin/bash
input=$(cat)
sid=$(echo "$input" | jq -r '.session_id')
f=~/.claude/logs/turn-times/$sid.json
if [ -f "$f" ]; then
start=$(jq -r '.start // "--:--:--"' "$f")
end=$(jq -r '.end // "実行中…"' "$f")
echo "⏱ 依頼 $start → 完了 $end"
jq -r '(.steps // [])[] | " " + .t + " " + .m' "$f"
else
echo "⏱ (まだ依頼なし)"
fi
statusline は複数行に対応していて、echo した行数分が画面下部に常駐表示されます。1行目が依頼〜完了、2行目以降が直近の作業報告(最大3件)です。
今回紹介した上記4本のスクリプトが仕込めれば、準備は完了です。
第3章:実際に仕込んでみて、どんな変化があったか
プロンプトの下に実況が出るようになった
試しにこんな依頼を投げてみます。
サーバーサイド JS フレームワークの主要なもの5つを上げて、その特徴とどんなアプリを作るのに最適かの情報を整理してまとめて
↑が実際に出来た画面になるのですが、
- いつ頼んで
- 今何をしていて
- 終わったのがいつか
が、確認できるようになりました。
Claudeの作業が一通り完了すると 依頼 13:33 → 完了 13:37 という用に表示もかわって、所要時間も一目でわかるように!
実例:実際に依頼した時の流れを紹介
実際に、
「JSのフレームワーク比較レポートを作って」と依頼したときのログを紹介します。
このhookでは、1行1イベントの JSONL でログされます。
なので、じっさいに下記のtailコマンドでそれを追いかけることがまず可能です。
tail -f ~/.claude/logs/turn-times.jsonl | jq .
依頼では11分間で 18イベント(start 3 / message 12 / end 3)発生していました。
ただ、生ログのまま紹介しても、何が起きたか追いにくいので時系列に並べ直してみます。
実際に依頼した時のログ結果
| 時刻 | 所要 | 何をしていたか(実況ログより) |
|---|---|---|
| 13:33:42 | 2:44 | ▶ 依頼。ナレッジ参照・ログ記録・最新動向の調査 |
| 13:36:26 | 0:37 | 5フレームワーク軸で md レポート初版 を作成 → レビュー依頼 |
| 13:37:03 | 3:24 | ⏸ ターン終了。サブエージェントのレビュー待ち |
| 13:40:27 | 1:20 | ▶ レビュー結果は 要修正5件(高2・中2・低1)→ 全件反映 → 再レビューへ |
| 13:41:47 | 0:36 | ⏸ ターン終了。再レビュー待ち |
| 13:42:23 | 2:20 | ▶ 再レビュー「指摘なし(合格)」→ HTML 版を作成 |
| 13:44:43 | 計11:01 | 完成。md + HTML の2ファイル納品 |
こんな感じで詳しい内訳が追えるようになりました。
かかった時間も詳細にわかります。
待ち時間が見えるので、「レビューにこのくらいかかるのか」という今後の見積もりの目安にもなります。
直近の稼働6日間分を集計してみた
とりあえず溜まったログを集計してみました。
蓄積されたログ
- ターン数:94回
- 途中報告:339件
- またいだ作業場所:13ディレクトリ
わかったこと
- 1ターン中央値:約2.3分
- 平均:約4.6分
- 最長:62分(1時間!)
体感でしかなかった「Claudeの仕事の長さ」が、見事に実測値で見える化出来るようになりました。
この結果を見て思ったのは
- 5分以下なら、あまり今後の指示の出し方を深く考える必要はない
- 1時間級なら、ちょっと指示の出し方を考えたほうが良い・・・
- もしくは、その間にお昼ゴハンを食べに行く(笑)
副次的効果として、タスクの渡し方が変わった
この結果を見るようになってからは、実行に時間がかかる部分についてはあらかじめ内容を自分で深掘りしてから渡すようになりました。
そうすることで
- 全体的な時間短縮ができる
- 役割分担する基準が作れる
- 自分がやったほうが効率的な領域とClaudeに渡したほうが良い領域がわかる
- 早くできそうに見えて、意外と時間がかかる部分もある
- 軽微なコード修正は マジで自分で直したほうが速い
- Claude がトークンを解釈しているうちに、自分で grep して直したほうが良い
- なんでもかんでも投げてしまうと逆に効率が落ちてしまうなという自覚・・・
見えるようにすることで、その後のアクションに繋がる
まあ、当たり前のことではあります(笑)
そのための見える化やないかい。
まとめ:hook の活用による行動の見える化
1. hook とは? → イベント駆動で自分のシェルを呼べる
LLM への「お願い」ではなく、Claude Code に必ず課すことのできる縛り・ルールです。
2. 何をしたか? → 待ち時間のブラックボックス化を阻止
いつ頼んだ・いま何してる・何分かかった、を常時見える化しました。
3. どうやって → hook が書き、statusline が毎秒読む
bash + jq で実現。仕組みも割と原始的。
4. どう変わった → タスクの渡し方や待ち方が変わった
実況の結果で「任せるか」「自分でやるか」、もしくは離席の判断もできるようになりました。
結論
settings.json に数行設定を足すのと、bashの仕込みで始められます。
ぜひ、Claude の作業内容を hook で見える化してみてください!
おまけ:今回紹介した hook の仕込み方
手順書を GitHub Gist で公開しています。jq のインストールから settings.json のマージ、動作確認、トラブルシューティングまで一通り書いてます。
- Mac 版:https://gist.github.com/shinya/754503369efe7a76dcd3a939abfc3aef
- Windows 版:https://gist.github.com/shinya/6d2b5167189c08711f9a157c8da58f06
運用してみて、色々気づくこともあると思うので、その場合はご自身で都合のいいようにカスタマイズしてもらって大丈夫です!
OSSの宣伝:Bokuchi、ぜひ使ってみてください
軽量 Markdown エディター Bokuchi(Tauri 製・OSS・無料)を開発しています。
- 軽い・速い ― Electron より省メモリ
- 完全オフライン ― 書いたものは外に出ない
- 数式(KaTeX)・図(Mermaid)・スライド(Marp)対応
- 今回の LT のスライドも Bokuchi で 記述・発表 しました
開発には Claude Code の力も借りながらやっています。





