Skip to content

feat(hooks): Claude Code API parity audit (July 2026) - #2

Open
darko-mijic wants to merge 6 commits into
mainfrom
feat/hook-api-parity
Open

feat(hooks): Claude Code API parity audit (July 2026)#2
darko-mijic wants to merge 6 commits into
mainfrom
feat/hook-api-parity

Conversation

@darko-mijic

@darko-mijic darko-mijic commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Summary

Completes the Claude Code hook API parity audit left uncommitted after PR #1, then polishes builder/schema contracts and fixes Greptile P1 regressions for the npm release cut.

API parity (commits 1–2)

  • Refresh and align contracts with official mirrors: prompt_id, eight Notification types, Stop/SubagentStop registries and dual feedback modes, six permission-update variants, event-aware handler matrix, tool-input deltas, and project docs.
  • Finish verification follow-ups: tool schemas accept real Claude payloads, SubagentStop scores final-message prose errors, PreCompact uses SessionStart re-injection (source: "compact"), MCP names enforce mcp__server__tool, dead retry helpers removed.
  • Maintainer notes: docs/internal/feat-hook-api-parity-review.md (historical mid-flight notes at e67fb61) and updated docs/internal/api-update-checklist.md.

Release polish (commit 3 — adversarial review fixes)

Severity Fix
Major Split PostToolUse builders: feedback() stays block; new postToolUseContext() for official replace/context-only output (no decision)
Minor New failureContext() for PostToolUseFailure context-only; failureFeedback() remains block
Minor Event-safe PreCompact: strict schema + type ban on hookSpecificOutput; tests assert reject (no false-green)
Minor Docs for taskBlock / teammateStop match universal { continue: false, stopReason } only
Nits ExitPlanMode strips unknown keys (not .strict()); Task vs Agent isolation docs; sessionStartContext preserves empty strings / empty watchPaths

Greptile P1 fixes (commit 4)

Finding Fix
Detailed PreCompact restore context overwritten by abbreviated board Single savePreCompactContext write: detailed body + abbreviated board
Custom notification placeholders no longer expand Restore {title}/{message}/{priority}/{icon} expansion; keep CLAUDE_NOTIFICATION_* env exports

Builder surface (block vs non-block)

// PostToolUse
HookOutputBuilder.feedback(reason, context?, mcpOut?, toolOut?)  // decision: "block"
HookOutputBuilder.postToolUseContext({ additionalContext?, updatedMCPToolOutput?, updatedToolOutput? })

// PostToolUseFailure
HookOutputBuilder.failureFeedback(reason, context?)  // decision: "block"
HookOutputBuilder.failureContext(context)

Test plan

  • pnpm run type-check
  • pnpm run lint (0 errors; 1 pre-existing warning in subagent-stop.ts)
  • pnpm run test:run (34 files / 1426 tests)
  • pnpm run build (prior commit; type-check green on latest)
  • Adversarial review workflow findings fixed
  • Greptile P1 findings fixed with regression tests
  • Optional: re-run pnpm run docs:sync-upstream and re-diff if Claude Code docs moved after 2026-07-12
  • CI green on this PR before marking ready / publishing

Notes

  • Single PR (schemas + reference-handler behavior + builders + docs).
  • Does not commit plans/ agent scratch or local .grok/ workflow definitions.
  • Upstream mirrors under docs/upstream/ are official Anthropic content used as audit inputs.
  • Ready for npm once CI matches local gates; Grok session parsing / hooks expansion should land on a post-release base.

Greptile Summary

This update expands Claude Code hook API parity across types, validation, output builders, lifecycle handlers, and documentation.

Two changed runtime paths were exercised directly:

  • Notification commands now expand legacy placeholders in unquoted, double-quoted, and single-quoted forms while treating hostile-looking notification text as literal data. The executed shell path did not create the command-substitution marker and preserved all expected values.
  • PreCompact now persists one combined detailed restore payload, and a compact-triggered SessionStart consumes and injects it once. The same runtime probe reproduced the prior two-write overwrite behavior before the change and passed on this revision.

No defects were found.

Confidence Score: 5/5

T-Rex T-Rex Logs

What T-Rex did

  • Ran the TypeScript notification placeholder expander along with the POSIX sh execution path using hostile-looking placeholder values, and observed that all six substitutions matched their source values and no command-substitution marker file was created, then ran the focused hook suite and TypeScript checks which passed.
  • Executed a Vitest probe against handlePreCompact and handleSessionStart; on the parent revision the probe failed due to two persistence writes, while on the PR head it passed with one write, preserving context and demonstrating expected compact restoration behavior.
  • Compared environment expansion behavior before and after; after state, all six values exactly matched the hostile source strings and markerExists is false, proving data is passed literally rather than re-parsed as shell source; production code was unchanged and only the runtime validation source was added.
  • Compared the parent revision and PR head for the one-write assertion; after changes, the PR head exits with code 0 and reports persistenceWrites: 1, and the temporary test copy used for Vitest discovery was removed with no production code changes.

View all artifacts

T-Rex Ran code and verified through T-Rex

Reviews (4): Last reviewed commit: "fix(hooks): quote-aware notification pla..." | Re-trigger Greptile

Harden permission-update variants, notification types, Stop/SubagentStop
feedback modes, and related schemas/docs so the library matches the
refreshed official hook surface after the publication-blockers merge.
Finish the parity audit by aligning tool schemas with official examples,
scoring SubagentStop final-message errors, tightening MCP name parsing,
updating lifecycle/env docs, and making type-check, lint, tests, and build green.
…olish

Add postToolUseContext and failureContext, enforce event-safe PreCompact
output, align ExitPlanMode/Task/sessionStart edge cases, and document the
block vs non-block builder paths before npm release.
@darko-mijic
darko-mijic marked this pull request as ready for review July 30, 2026 06:37
Comment thread src/lifecycle/pre-compact.ts Outdated
Comment thread src/lifecycle/notification-handler.ts Outdated
Stop overwriting detailed SessionStart restore context with the abbreviated
PreCompact board, and re-expand {title}/{message}/{priority}/{icon} in custom
notification commands while keeping CLAUDE_NOTIFICATION_* env exports.
@darko-mijic

Copy link
Copy Markdown
Contributor Author

Greptile P1 follow-up

Addressed both verified P1 findings in 453473f:

Finding Fix
PreCompact detailed context overwritten by abbreviated board Single savePreCompactContext write: detailed restore body + abbreviated board
Custom notification placeholders no longer expand Restore {title}/{message}/{priority}/{icon} expansion; keep CLAUDE_NOTIFICATION_* env vars

Local: type-check + hooks tests + lint green. Full suite running/CI will re-verify.

Comment thread src/lifecycle/notification-handler.ts Outdated
Map {title}/{message}/{priority}/{icon} to double-quoted
CLAUDE_NOTIFICATION_* expansions instead of interpolating raw notification
text into sh -c, closing command-injection via hostile titles or messages.
Comment thread src/lifecycle/notification-handler.ts Outdated
Expand {title}/{message}/{priority}/{icon} with shell quote context so
single-quoted legacy commands still receive env refs, without splicing raw
notification text into sh -c. Document Greptile as primary review tool.
@darko-mijic

Copy link
Copy Markdown
Contributor Author

@greptileai

All Greptile findings addressed on this branch (latest: a4fca4b — quote-aware notification placeholders; earlier PreCompact restore + injection-safe env refs + builder polish).

Please run a full re-review and refresh the PR overview/summary against current HEAD.

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.

1 participant