SuperAPI AI Directory

plan-rename

Agents & models

View repository

Updated 10-15 19:58

About this skill

plan-rename — Plan Mode Session Auto-Rename Hook

Automatically rename the session from the plan's H1 heading when Plan Mode ends, and automatically repair the name after compaction via the sidecar mechanism.

How It Works

Three shell scripts work together:

  1. plan-rename-hook.sh(PreToolUse ExitPlanMode): extract the plan H1 heading and write it to the sidecar backup file (pending: true)。does not write directly to JSONL — avoids flickering when the user rejects the plan
  2. plan-rename-guard.sh(Stop hook): after each Claude response, check:
    • Phase 1 — Pending confirmation: when the sidecar is pending, scan the JSONL to confirm whether ExitPlanMode was accepted. If rejected, skip; only if accepted, write custom-title
    • Phase 2 — Fallback repair: when the sidecar is confirmed but the JSONL lacks custom-title, re-inject it (a safety net; normally handled by the compact hook)
  3. plan-rename-compact.sh(SessionStart compact): triggered specifically after compaction:
    • Read the confirmed sidecar → scan the tail of the JSONL → if custom-title is missing, re-inject it
    • Also clean up sidecars with mtime > 30 days (skip _pending_*)

Flowchart

Plan Mode exit
  → ExitPlanMode hook (PreToolUse)
    → 擷取 H1 標題(去除 "Plan:" 前綴,截斷 80 字元)
    → 寫入 sidecar(pending: true)
    → 不寫 JSONL(等 Stop guard 確認)

使用者 accept/reject plan
  → Claude 回應後 Stop hook 觸發
    → bash 快速檢查:有 pending sidecar 或 confirmed sidecar 嗎?
      → 都沒有 → exit 0(~5ms)
      → 有 pending → Python 分析 JSONL
        → rejected → 跳過
        → accepted → 寫入 custom-title,sidecar 設為 confirmed
      → 有 confirmed 但無 custom-title → 兜底重新注入

Compaction 發生
  → SessionStart compact hook 觸發
    → 有 confirmed sidecar?
      → 沒有 → exit 0(~5ms)
      → 有 → 掃描 JSONL 尾部
        → 有 custom-title → exit 0
        → 沒有 → 重新注入 custom-title
    → 清理 30 天以上的 sidecar

Hook Registration

After installation, you need tomanuallyin ~/.claude/settings.json 's hooks block add the following three settings:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "ExitPlanMode",
        "hooks": [
          {
            "type": "command",
            "command": "bash ~/.claude/skills/plan-rename/plan-rename-hook.sh"
          }
        ]
      }
    ],
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "bash ~/.claude/skills/plan-rename/plan-rename-guard.sh"
          }
        ]
      }
    ],
    "SessionStart": [
      {
        "matcher": "compact",
        "hooks": [
          {
            "type": "command",
            "command": "bash ~/.claude/skills/plan-rename/plan-rename-compact.sh"
          }
        ]
      }
    ]
  }
}

Note: npx skills add only installs files; it will not automatically modify settings.json。

Upgrading from v1 to v2

Main improvements in v2:

  1. Removed the jq dependency — guard.sh now uses grep + cut to parse JSON
  2. Added plan-rename-compact.sh — a dedicated SessionStart compact hook that triggers precisely after compaction instead of running on every response
  3. Improved Stop hook performance — Phase 2 (compaction repair) is downgraded to a fallback; 99% of cases are handled by the compact hook

Upgrade steps:

  1. Update plan-rename-guard.sh and add plan-rename-compact.sh(updating the files is sufficient)
  2. In settings.json add the SessionStart compact hook (see Hook Registration above)
  3. Confirm that jq is no longer needed (it can be removed without affecting other tools)

Hook stdin Schemas

PreToolUse ExitPlanMode:

{
  "session_id": "uuid-string",
  "transcript_path": "/Users/.../.claude/projects/<slug>/<session_id>.jsonl",
  "hook_event_name": "PreToolUse",
  "tool_name": "ExitPlanMode",
  "tool_input": { "plan": "# Plan Title\n...", "allowedPrompts": [] }
}

Stop hook:

{
  "session_id": "uuid-string",
  "transcript_path": "/Users/.../.claude/projects/<slug>/<session_id>.jsonl"
}

SessionStart compact:

{
  "session_id": "uuid-string",
  "transcript_path": "/Users/.../.claude/projects/<slug>/<session_id>.jsonl",
  "hook_event_name": "SessionStart",
  "source": "compact",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "model": "claude-sonnet-4-6"
}

JSONL custom-title Format

Uses the same format as the Claude Code /rename command:

{"type":"custom-title","customTitle":"<title>","sessionId":"<session_id>"}

Before you use it

Pros

  • Plan Mode exit automatically names the session — no manual /rename
  • Automatic repair after compaction — compact hook triggers precisely + Stop hook as a fallback
  • No API token consumption — all hooks are local shell commands
  • No external dependencies — no jqneeded (v2 uses grep + cut)

Cons / Limitations

  • Manual configuration required ( settings.json hook(npx skills add only installs files and does not change settings)
  • The Stop hook runs on every response (but exits in ~5ms when there is no sidecar)
  • /clear when creating a new session the title will not carry forward

Potential issues

  • Sidecar accumulation:~/.claude/session-titles/ will keep accumulating, about ~100 bytes each. The compact hook automatically cleans up sidecars older than 30 days
  • Compaction behavior: compaction rewrites the JSONL and discards non-message entries; this is an observation/inference rather than official documentation. Claude Code updates may change the behavior
  • /clear limitation:/clear when creating a new session the title will not carry forward

Token / IO cost

Item Cost
API Token Zero overhead — pure local shell commands
Stop hook (guard) No sidecar: grep fast exit (~5ms); with pending: Python JSONL tail scan (~100-150ms)
SessionStart compact hook Triggered only on compaction; no sidecar: bash exit (~5ms); with sidecar: Python scan (~80-120ms)
PreToolUse hook Triggered only on ExitPlanMode, Python runs once (~50-80ms)
Disk I/O guard reads sidecar (~100 bytes) + last 50 lines of JSONL; hook writes one JSONL line + one sidecar file (atomic temp+rename)
Summary Sessions not using plan rename are virtually unaffected (~5ms/response); compaction repair is handled on demand by the compact hook

Script stability risk

  • JSONL custom-title The format was obtained through observation of /rename behavior via reverse engineering, not an official API — Claude Code updates may change the format
  • Hook stdin JSON schema(session_id、transcript_path and other fields) likewise have no official guarantee
  • ExitPlanMode only triggers PreToolUse(does not trigger PostToolUse) is an observation result
  • SessionStart of compact matcher is a Claude Code v2.1.75+ feature
  • Suggestion: After a major Claude Code version update, run integration tests to confirm the hooks still work properly

System requirements

  • python3
  • bash

Security

  • Session ID validation:[a-zA-Z0-9_-]{8,128} regex
  • Path containment: transcript_path must be within ~/.claude/ inside
  • Control character cleanup: title removes Unicode category C characters
  • Sidecar permissions: directory 700, file 600
  • Atomic write: temp file + rename to prevent partial write

Common Pitfalls

  • Compaction drops custom-title: JSONL is managed by Claude Code; non-message entries may be cleared by compaction. Must be backed up using a sidecar
  • Stop hook bash pre-check: Using grep to parse JSON and skip Python is the key performance optimization
  • PostToolUse cannot intercept Plan Mode: Must use PreToolUse ExitPlanMode
  • PreToolUse triggers before user approval: Therefore the hook only writes the sidecar (pending), and the Stop guard writes to JSONL only after confirming accept. Avoids title flickering caused by rejected plans
  • Field naming: hook stdin uses session_id(snake_case), not sessionId(camelCase)
  • Path source: Use the transcript_pathprovided by stdin, do not manually concatenate
  • compact hook stdout: Keep blank to avoid triggering bug #15174