Skip to content

feat: full hook coverage for Claude Code and Cursor (+ fix WorktreeCreate breakage)#48

Merged
scotthavird merged 2 commits into
mainfrom
claude/support-envelope-v1.2-Gzsge
May 11, 2026
Merged

feat: full hook coverage for Claude Code and Cursor (+ fix WorktreeCreate breakage)#48
scotthavird merged 2 commits into
mainfrom
claude/support-envelope-v1.2-Gzsge

Conversation

@scotthavird
Copy link
Copy Markdown
Contributor

@scotthavird scotthavird commented May 11, 2026

Summary

Brings hook coverage up to spec for both Claude Code and Cursor, and fixes a real bug where our WorktreeCreate registration was breaking claude --worktree for users.

Claude Code

Diffed buildClaudeCodeHooks against the canonical hooks reference.

Added 3 events that the spec exposes but we weren't registering:

Event When it fires
Setup --init-only, -p --init, or -p --maintenance (CI / scripted one-time setup)
UserPromptExpansion When a /skillname slash command expands into a prompt — captures a path PreToolUse does not
PostToolBatch Once after every tool call in a batch of parallel calls resolves

Removed 1 event because our handler is wrong for it:

Event Why
WorktreeCreate Per the spec, registering this hook replaces the default git worktree behavior — the hook must print the new worktree path on stdout. Our hook handler prints {"continue": true}, which Claude Code interprets as the path. Result: claude --worktree and isolation: "worktree" subagents fail for any user who installed our hooks. Until the handler can detect WorktreeCreate and return a real path, we don't register.

Tidied up:

  • Dropped matcher: "*" from events the spec marks as no-matcher (TaskCreated, TaskCompleted, TeammateIdle, PostToolBatch). The matcher was silently ignored — just noisy JSON.

Made install idempotent (self-healing): before merging the current hook set into the user's ~/.claude/settings.json, we strip any entry whose value already references "promptconduit". That means hook events we used to register but no longer ship (like WorktreeCreate) get cleaned up on re-install. User-owned hooks are untouched. Same fix applies to install cursor.

Coverage: 28 of 29 documented Claude Code events (the only omission is WorktreeCreate, by design).

Cursor

Previously registered 4 of 20 events. Expand buildCursorHooks to the full set from cursor.com/docs/hooks:

Group Events
Session lifecycle sessionStart, sessionEnd
Generic tool use preToolUse, postToolUse, postToolUseFailure
Subagent (Task) subagentStart, subagentStop
Shell beforeShellExecution, afterShellExecution
MCP beforeMCPExecution, afterMCPExecution
File access (Agent) beforeReadFile, afterFileEdit
Prompts / agent output beforeSubmitPrompt, afterAgentResponse, afterAgentThought
Context window preCompact
Stop stop
Tab (inline completions) beforeTabFileRead, afterTabFileEdit

We register both preToolUse/postToolUse and the tool-specific variants — the specific events carry richer payload (actual command / file path / MCP server name), the generic ones backfill anything without a dedicated hook. Platform dedupes server-side.

Coverage: 20 of 20 events.

Test plan

  • go build ./... && go vet ./... && go test ./... clean
  • Smoke: promptconduit install claude-code against a fake $HOME writes 28 hook entries to ~/.claude/settings.json (every documented event except WorktreeCreate)
  • Smoke: promptconduit install cursor against a fake $HOME writes 20 hook entries to ~/.cursor/hooks.json
  • Smoke: planted stale WorktreeCreate entry pointing at our binary + a user-owned UserCustomHook in settings.json, re-ran install claude-code. Result: stale WorktreeCreate removed, UserCustomHook preserved.
  • Smoke: uninstall cursor removes all 20 (reported Successfully removed 20 PromptConduit hook(s))
  • Run against a real Claude Code + Cursor install and confirm events arrive at the platform end-to-end

https://claude.ai/code/session_019CWBC2E8pQuShejfsKYrvp

claude added 2 commits May 11, 2026 01:01
We were registering only 4 of the 20 hook events Cursor exposes
(https://cursor.com/docs/hooks), so most of the Agent + Tab flow was
invisible to PromptConduit. Expand `buildCursorHooks` to cover the full
set:

Session lifecycle:
  sessionStart, sessionEnd
Generic tool use (Agent):
  preToolUse, postToolUse, postToolUseFailure
Subagent (Task tool):
  subagentStart, subagentStop
Shell:
  beforeShellExecution, afterShellExecution
MCP:
  beforeMCPExecution, afterMCPExecution
File access (Agent):
  beforeReadFile, afterFileEdit
Prompts and agent output:
  beforeSubmitPrompt, afterAgentResponse, afterAgentThought
Context window:
  preCompact
Stop:
  stop
Tab (inline completions, separate policy from Agent):
  beforeTabFileRead, afterTabFileEdit

We register both the generic `preToolUse`/`postToolUse` AND the
specific-tool variants (`beforeShellExecution`, `beforeMCPExecution`,
`beforeReadFile`, `afterFileEdit`). The specific events carry richer
payload (actual command / file path / MCP server name); the generic
ones backfill any tool kind without a dedicated hook. Platform dedupes
server-side by event id.

`uninstall cursor` already recursively removes any hook entry whose
value contains "promptconduit", so the new keys clean up without
further changes — verified end-to-end against a fake HOME (20 hooks
written, 20 removed).

https://claude.ai/code/session_019CWBC2E8pQuShejfsKYrvp
…egistration

Two changes to buildClaudeCodeHooks, plus an idempotency fix to install:

1. Add the 3 events from the current spec we weren't registering:
     - Setup (--init-only / -p --init / -p --maintenance)
     - UserPromptExpansion (slash-command expansion — covers /skillname
       direct invocations, a path PreToolUse does not capture)
     - PostToolBatch (fires once per resolved batch of parallel tool calls)

2. Remove WorktreeCreate. Per the canonical reference, configuring a
   WorktreeCreate hook *replaces* the default `git worktree` behavior —
   the hook must print the new worktree path on stdout, and Claude Code
   uses that as the working directory for the isolated session. Our
   generic hook handler always prints `{"continue": true}`, which would
   cause `claude --worktree` and `isolation: "worktree"` subagents to
   fail outright. Until our handler can detect WorktreeCreate events
   and return the correct path, we don't register for that event.
   (Source: https://code.claude.com/docs/en/hooks)

Also tidies up:
  - Matchers dropped from no-matcher events (TaskCreated, TaskCompleted,
    TeammateIdle, PostToolBatch). The spec says matchers on those are
    silently ignored; we were just emitting noisy JSON.
  - install is now self-healing: before merging the current hook set
    into the user's settings.json, it strips any of OUR previously-
    installed entries (anything whose value contains "promptconduit").
    That means an old WorktreeCreate registration left behind by a
    prior install gets cleaned up on re-install. Verified against a
    fake HOME: planted stale WorktreeCreate + a user-owned hook,
    re-installed, stale entry gone, user hook preserved.

Net coverage: 28 of 29 events from the current spec (the missing one
is WorktreeCreate, by design).

https://claude.ai/code/session_019CWBC2E8pQuShejfsKYrvp
@scotthavird scotthavird changed the title feat(cursor): register for all 20 hook events from the current spec feat: full hook coverage for Claude Code and Cursor (+ fix WorktreeCreate breakage) May 11, 2026
@scotthavird scotthavird marked this pull request as ready for review May 11, 2026 01:09
@scotthavird scotthavird merged commit 6d1be98 into main May 11, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants