Created
May 12, 2026 22:20
-
-
Save haydenholligan/b676acb48d250490787a9a83feb8923b to your computer and use it in GitHub Desktop.
fix: skip hook injection for non-interactive (sdk-cli) sessions
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Bug: Caveman hooks fire on sdk-cli probe sessions | |
| ## Summary | |
| Caveman's `SessionStart` and `UserPromptSubmit` hooks fire on every Claude | |
| session, including non-interactive `sdk-cli` sessions spawned by external | |
| tools. This causes those tools to receive caveman-mode responses instead of | |
| the plain output they expect, silently breaking their functionality. | |
| ## How it was discovered | |
| CodexBar (a macOS menu bar app that tracks Claude usage) shows the error: | |
| > Could not parse Claude usage: Missing Current session. | |
| Investigation traced the failure to CodexBar's probe mechanism. Every ~2 | |
| minutes, CodexBar spawns a Claude `sdk-cli` session with `cwd` set to | |
| `~/Library/Application Support/CodexBar/ClaudeProbe` and sends the prompt | |
| `"show usage\n\n"`. It then parses the response text using regex patterns | |
| to extract current session token/cost data. | |
| ## Root cause | |
| The `~/.claude/sessions/` directory confirmed the probe sessions have | |
| `"entrypoint":"sdk-cli"` in their session files. Inspecting the probe | |
| session's JSONL (`~/.claude/projects/-Users-hayden/<session-id>.jsonl`) | |
| revealed the full injection chain: | |
| **Attachment: `SessionStart:startup` hook** | |
| ``` | |
| CAVEMAN MODE ACTIVE — level: full | |
| Respond terse like smart caveman... | |
| ``` | |
| **Attachment: `UserPromptSubmit` hook** | |
| ``` | |
| CAVEMAN MODE ACTIVE (full). Drop articles/filler/pleasantries/hedging... | |
| ``` | |
| **Attachment: `skill_listing`** (injected by UserPromptSubmit hook) | |
| ``` | |
| - caveman:caveman-stats: Show real token usage... | |
| ``` | |
| With the caveman ruleset and skill listing injected, Claude matched | |
| `"show usage"` → "token usage/savings" → `caveman:caveman-stats` skill. | |
| The `caveman-stats` hook then blocked the turn and injected stats output | |
| instead of a response. CodexBar received: | |
| > Hook blocked — stats injected directly. If nothing displayed, hook may | |
| > not have fired correctly. Run `/caveman-stats` in the input instead. | |
| This is unparseable by CodexBar's regex patterns → "Missing Current session." | |
| ## Affected tools | |
| Any tool that spawns a Claude `sdk-cli` session hits this: | |
| - CodexBar (usage monitoring) | |
| - CI/CD pipelines using Claude SDK | |
| - Automation scripts using `claude -p` or the Claude SDK | |
| - Other IDE extensions or menu bar apps with Claude integrations | |
| ## Fix | |
| Both hooks check `transcript_path` from stdin to identify the session, look | |
| up the session file in `~/.claude/sessions/`, and read the `entrypoint` | |
| field. If `entrypoint !== "cli"`, the hooks exit immediately without | |
| injecting any caveman context. | |
| `SessionStart` uses async stdin reading with a 200ms fallback timeout so it | |
| does not block on sessions that may send no stdin data. | |
| `UserPromptSubmit` returns `{}` (no-op) for non-interactive sessions. | |
| Interactive `cli` sessions are unaffected. | |
| ## Testing | |
| Verified with a synthetic sdk-cli session file: | |
| ```bash | |
| # Create fake sdk-cli session | |
| echo '{"pid":99999,"sessionId":"test-id","entrypoint":"sdk-cli",...}' \ | |
| > ~/.claude/sessions/99999.json | |
| # UserPromptSubmit hook returns {} (no caveman injection) | |
| echo '{"transcript_path":".../test-id.jsonl","prompt":"show usage"}' \ | |
| | node src/hooks/caveman-mode-tracker.js | |
| # → {} | |
| # Interactive session still gets caveman context | |
| echo '{"transcript_path":".../real-session.jsonl","prompt":"show usage"}' \ | |
| | node src/hooks/caveman-mode-tracker.js | |
| # → {"hookSpecificOutput":{"additionalContext":"CAVEMAN MODE ACTIVE..."}} | |
| ``` |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment