はじめに
ここ最近取り入れた Claude Code の tips を4つ紹介します。1つだけツールの話です。
どれも大がかりな仕組みではないので、気になったものだけ拾ってもらえればと思います。
この記事をざっくり図解
1. セッション名を自動で付ける
知っている方も多いかと思いますが、Claude Code のセッションには名前を付けられます。
/rename で設定すると、プロンプト入力欄の右下にチップとして表示されます。
複数のセッションを並行して動かしていると、どれが何の話だったか分からなくなります。それで名前を付けるようになったのですが、毎回手で打つのが面倒でした。
ここを自動化したい、というのが出発点です。
ai-title と custom-title は別物
先に仕組みの話をします。
Claude Code はセッションのタイトルを2種類持っています。
| 種類 | 誰が作るか | どこに出るか |
|---|---|---|
| ai-title | Claude Code が1往復目のあとに自動生成 |
--resume の一覧、ターミナルのタイトル、 /status など |
| custom-title |
/rename やフックで明示的に設定 |
上記に加えてチップ |
自動生成された ai-title は、Claude Code が会話の記録を残しているファイルに書き込まれています。
~/.claude/projects/ の下にセッションごとの .jsonl があり、発言や応答と同じ場所にタイトルの行も並びます。
{"type":"ai-title","aiTitle":"検索インデックスの調査"}
ただし、この文言はチップには出ません。
チップが読んでいるのは custom-title のほうだけです。
つまり文言はすでに作られているのに、表示される場所へ渡されていない状態です。
フックで ai-title を custom-title に移す
UserPromptSubmit フックは、標準出力に次の形の JSON を返すとセッション名を設定できます。
{"hookSpecificOutput": {"hookEventName": "UserPromptSubmit", "sessionTitle": "検索インデックスの調査"}}
この sessionTitle を受け付けるのは UserPromptSubmit と SessionStart の2つだけです。Stop などほかのフックで返しても読み捨てられます。
(補足) フックイベントの一覧
太字の2つのイベントでしか sessionTitle を受け付けません。
| イベント | 発火するタイミング |
|---|---|
| SessionStart | セッションの開始・再開時 |
| Setup |
--init-only での起動時、-p での --init / --maintenance 実行時 |
| UserPromptSubmit | プロンプトを送信した直後、Claude が処理を始める前 |
| UserPromptExpansion | 入力したコマンドがプロンプトに展開されるとき |
| PreToolUse | ツール実行の前 |
| PermissionRequest | ツール実行の許可判断が必要になったとき |
| PermissionDenied | auto モードがツール実行を拒否したとき |
| PostToolUse | ツール実行が成功したあと |
| PostToolUseFailure | ツール実行が失敗したあと |
| PostToolBatch | 並列のツール呼び出しがひとまとまり終わったあと |
| Notification | Claude Code が通知を出すとき |
| MessageDisplay | 応答テキストが表示されている間 |
| SubagentStart | サブエージェントが起動したとき |
| SubagentStop | サブエージェントが終了したとき |
| TaskCreated |
TaskCreate でタスクが作られるとき |
| TaskCompleted | タスクが完了扱いになるとき |
| Stop | Claude が応答を終えたとき |
| StopFailure | API エラーでターンが終わったとき |
| TeammateIdle | チームメイトのエージェントがアイドルに入る直前 |
| InstructionsLoaded | CLAUDE.md や .claude/rules/*.md が読み込まれたとき |
| ConfigChange | セッション中に設定ファイルが変わったとき |
| CwdChanged | 作業ディレクトリが変わったとき |
| DirectoryAdded | セッション中に作業ディレクトリが追加されたとき |
| FileChanged | 監視対象のファイルがディスク上で変わったとき |
| WorktreeCreate | ワークツリーが作られるとき |
| WorktreeRemove | ワークツリーが削除されるとき |
| PreCompact | コンテキスト圧縮の前 |
| PostCompact | コンテキスト圧縮の完了後 |
| PreModelSwitch | モデル切り替えが適用される前 |
| PostModelSwitch | セッションのモデルが変わったあと |
| Elicitation | MCP サーバーがツール実行中に入力を求めたとき |
| ElicitationResult | MCP の入力要求にユーザーが応えたあと |
| SessionEnd | セッションが終了したとき |
イベントは今後増えることがあるので、最新は公式ドキュメントを見てください。
書いたスクリプトがこちらです。
ai-title を読んで返しているだけで、タイトルの生成そのものはしていません。
#!/bin/bash
input=$(cat)
session_id=$(printf '%s' "$input" | jq -r '.session_id // empty')
transcript=$(printf '%s' "$input" | jq -r '.transcript_path // empty')
[ -z "$session_id" ] && exit 0
[ -f "$transcript" ] || exit 0
# 同じセッションで何度も名前を変えないよう、一度付けたら印を残す
marker="$HOME/.claude/.session-titled/$session_id"
[ -f "$marker" ] && exit 0
mkdir -p "$(dirname "$marker")"
# 組み込みが生成した最新の ai-title を拾う。まだ無ければ次のプロンプトに持ち越す
title=$(jq -r 'select(.type == "ai-title") | .aiTitle' "$transcript" | tail -1)
[ -z "$title" ] && exit 0
# ブランチ名に #123 が含まれていれば、タイトルの先頭に付ける
cwd=$(printf '%s' "$input" | jq -r '.cwd // "."')
branch=$(git -C "$cwd" rev-parse --abbrev-ref HEAD 2>/dev/null)
number=$(printf '%s' "$branch" | grep -oE '#[0-9]+' | head -1 | tr -d '#')
[ -n "$number" ] && title="#$number $title"
touch "$marker"
jq -n --arg t "$title" \
'{hookSpecificOutput: {hookEventName: "UserPromptSubmit", sessionTitle: $t}}'
~/.claude/settings.json に登録します。
パスは自分の環境のものに置き換えてください。
{
"hooks": {
"UserPromptSubmit": [
{
"hooks": [
{
"type": "command",
"command": "/Users/you/.claude/hooks/auto-session-title.sh",
"timeout": 10
}
]
}
]
}
}
名前が変わるのは2通目を送ったとき
ai-title が生成されるのは、1往復目の応答が終わったあとです。
登録しているフックが動くのはプロンプトを送った瞬間なので、1通目の時点ではまだ何もありません。実際に名前が変わるのは2通目を送ったタイミングになります。
最後の数行では、ブランチ名に含まれる Issue 番号をタイトルの先頭に足しています。feature/#123_add_search というブランチで作業していれば #123 検索インデックスの調査 になります。あとから --resume で探すとき、番号で引けるほうが早いためです。
2. ステータスラインに Issue と PR を出す
ステータスラインにリンクを置けると X で見かけました。
それなら Issue と PR のリンクも動的に出せるのではないか、と思って作ったのがこれです。
2行目の #123 と PR#456 がリンクになっていて、クリックすると GitHub の該当ページが開きます。
Issue 番号はブランチ名から取る
ブランチ名に Issue 番号を入れる運用にしているので、そこから抜き出しています。
issue_number_from_branch() {
printf '%s' "$1" | sed -n 's/.*#\([0-9][0-9]*\).*/\1/p'
}
リンク先の URL は、ステータスラインに渡ってくる JSON の workspace.repo から組み立てます。ホスト名・オーナー・リポジトリ名がそれぞれ入っているので、GitHub に限定せずに済みます。
PR のほうは、同じ JSON の pr.number と pr.url を使います。これが渡ってこない環境向けに、gh pr list の結果を自前でキャッシュする経路も用意しました。ステータスラインは表示のたびに実行されるため、gh を毎回同期で叩くと目に見えて重くなります。キャッシュを読んで表示し、期限切れならバックグラウンドで更新する形にしています。
リンク化は OSC 8
ターミナルでテキストをリンクにするには、OSC 8 というエスケープシーケンスを使います。
osc8_link() {
printf '\033]8;;%s\033\\%s\033]8;;\033\\' "$1" "$2"
}
ここで一度ハマりました。OSC 8 の終端はバックスラッシュを含みます。そのため色付きの文字列を printf '%b' に渡す書き方をしていると、エスケープの解釈がぶつかってリンクが壊れます。
色を $'\033[33m' の形で実バイトとして持ち、出力は %s に統一することで直りました。
3. レートリミットのリセット時刻を揃える
レートリミットのリセット時刻を揃えるtipsは、以下の記事で知りました。
Claude のレートリミットは5時間のローリングウィンドウで、初回メッセージを送った時刻が起点になります。仕事を始めてから最初のメッセージを送ると、そこから5時間が計測されるわけです。
そこで、ルーチン機能で毎朝決まった時刻に ping を1通送り、起点を固定してしまう、という話でした。クラウド側で実行されるので、PC が起動していなくても動きます。
私の設定例
私は8時前後に始業することが多いので、6時に設定しています。
06:00 - 11:00 ← 3h(08:00 - 11:00)
11:00 - 16:00 ← 4h(1h休憩)
16:00 - 21:00 ← 2h(16:00 - 18:00)
効果は分かりやすく出ました。拘束時間を10時間(実働8時間+休憩1時間+残業1時間)とすると、ルーチンがない場合はその間にリセットが1回しか挟まりません。
始業と同時に起点が決まってしまうためです。
6時に固定しておくと、同じ10時間のあいだにリセットが2回挟まります。
5時間ぶんの使用量を気にする場面が、体感でかなり減りました。
4. Orca に情報をまとめる
これも X で見かけて試したものです。
Orca は、複数のワークツリーで AI エージェントを並行して動かすためのデスクトップアプリです。
それまでは同じ用途で herdr というターミナルマルチプレクサを、Ghostty上で起動して使用していました。
乗り換えてみたところ、herdr で私が使用していた機能はすべて Orca でもできました。
そのうえで、次のものが最初から内蔵されています。
- ファイル操作(エクスプローラー表示・書き込み・削除といった IDE 的な機能)
- ブラウザ
- Macのスリープ制御
- AIモデルの現在の使用量表示
- GitHub連携
- モバイル対応
herdr でもプラグインやほかのツールと組み合わせれば同じことはできます。
ただ、内蔵されているぶん Orca のほうが圧倒的に楽です。
いちばん効いているのは、情報が1か所にまとまることです。
ウィンドウを行き来して探し直す回数が減り、タスクごとに1つの画面に情報(ターミナル、ブラウザ、mdファイル等)をまとめることが出来、頭の中の負荷が軽くなったと感じています。
最後に
セッション名とステータスラインは自作なので手間はかかりますが、レートリミットの設定と Orca は入れるだけです。気になったものから試してみてください。
参考文献
株式会社シンシア
株式会社シンシアでは、実務未経験のエンジニアの方や学生エンジニアインターンを採用し一緒に働いています。
※ シンシアにおける働き方の様子はこちら
弊社には年間100人以上の実務未経験の方に応募いただき、技術面接を実施しております。
この記事が少しでも学びになったという方は、ぜひ wantedly のストーリーもご覧いただけるととても嬉しいです!


