Skip to content

fix(hooks): Parse Claude Code's nested tool_input payload - #114

Merged
JordanCoin merged 1 commit into
mainfrom
fix/hook-payload-tool-input-file-path
Aug 8, 2026
Merged

fix(hooks): Parse Claude Code's nested tool_input payload#114
JordanCoin merged 1 commit into
mainfrom
fix/hook-payload-tool-input-file-path

Conversation

@JordanCoin

Copy link
Copy Markdown
Owner

Closes #110.

The bug

parseHookFilePathsErr handled a top-level file_path and Codex's tool_input.command, but not tool_input.file_path — the shape Claude Code actually sends for Edit and Write.

The regex fallback that would have caught it only runs when json.Unmarshal fails. A well-formed payload in the unhandled shape parses cleanly, matches nothing, and returns no paths — so the hook returned early and did nothing, silently.

Same file, same hook, before this change:

$ printf '{"session_id":"s1","file_path":"<abs>/cmd/doctor.go"}' | codemap hook post-edit
   Imports 1 hub(s): internal/buildinfo/version.go
  recorded: 1

$ printf '{"session_id":"s1","tool_name":"Edit","tool_input":{"file_path":"<abs>/cmd/doctor.go"}}' | codemap hook post-edit
  recorded: 0        # no output at all

After:

$ printf '{"session_id":"s1","tool_name":"Edit","tool_input":{"file_path":"<abs>/cmd/doctor.go"}}' | codemap hook post-edit
   Imports 1 hub(s): internal/buildinfo/version.go
  recorded: 1

Impact

Two features were inert on Claude Code:

  • Agent-edit provenance.codemap/agent_edits.jsonl was never created, so the working-set summary always fell back to disk churn. That conflation is precisely what the feature was built to remove. Across a four-day session in this repo with a dozen-plus Edit/Write calls and four merged PRs, the file never appeared.
  • Post-edit blast radius — the "you just edited a file N others import" report never fired, which is the behavior CLAUDE.md tells agents to rely on.

Also now accepts tool_input.notebook_path for NotebookEdit.

Tests

Six payload shapes, covered end to end rather than only at the parser — the parser was arguably correct, it just never saw the shape it needed, so a parser-only test would have passed while the feature stayed broken:

Shape Expect
legacy top-level file_path resolves
tool_input.file_path (Edit) resolves
tool_input.file_path (Write) resolves
tool_input.notebook_path (NotebookEdit) resolves
tool_input.command (Codex apply_patch) resolves
tool_input.command (Bash, no path) no match

Plus TestHookPostEditRecordsAgentEditForNestedPayload, which drives hookPostEdit and asserts the record lands.

Docs

Updated now that the behavior actually works, rather than documenting the broken state:

Type of change

  • Bug fix
  • New feature
  • New language support
  • Documentation
  • Other

Verification

go test ./..., go vet, staticcheck, gofmt clean; go vet clean cross-compiled for windows/amd64 and linux/amd64. Behavior confirmed against a built binary with the exact payload Claude Code emits.

Closes #110.

parseHookFilePathsErr handled a top-level file_path and Codex's
tool_input.command, but not tool_input.file_path — the shape Claude Code
actually sends for Edit and Write. The regex fallback that would have
caught it only runs when json.Unmarshal fails, so a well-formed payload
in the unhandled shape was the one case that silently matched nothing and
returned no paths.

Two features depended on that parse and have therefore been inert on
Claude Code:

- Agent-edit provenance never recorded, so .codemap/agent_edits.jsonl was
  never created and the working-set summary always fell back to disk
  churn — the exact conflation the feature was built to remove.
- The post-edit blast-radius report never fired, which is the behavior
  CLAUDE.md instructs agents to rely on after editing.

Verified against the release binary before and after with the payload
Claude Code sends: previously no output and no record; now the hub report
prints and the edit is recorded.

Also accepts tool_input.notebook_path for NotebookEdit. Tests cover each
payload shape end to end rather than only the parser, since the parser
was arguably correct — it simply never saw the shape it needed.

Docs updated now that the behavior works: HOOKS.md describes working-set
provenance and the payload keys it depends on, notes doctor's scope
fallback from #100, and marks post-edit as recording provenance. The
manual hook JSON keeps the bare PATH form, which is correct for
hand-managed settings and accepted by doctor since #100.

Drive-by: CONTRIBUTING suggested Elixir and Scala as languages to add,
both already supported; plugin README said "skill" singular but ships two.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings August 8, 2026 16:17

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes the PostToolUse hook path extraction so Claude Code payloads for Edit/Write/NotebookEdit are correctly recognized, restoring agent-edit provenance recording and the post-edit blast-radius reporting that depends on it.

Changes:

  • Extend hook payload parsing to accept tool_input.file_path and tool_input.notebook_path in addition to the existing legacy and Codex shapes.
  • Add end-to-end and parser-level tests covering multiple host payload shapes, including a regression test that drives hookPostEdit.
  • Update hook and contribution documentation to reflect the now-working provenance behavior and current project state.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
plugins/codemap/README.md Fix wording to reflect multiple shipped skills.
docs/HOOKS.md Document provenance-based working set behavior and clarify doctor scope fallback behavior.
CONTRIBUTING.md Update example “languages to add” list to avoid listing already-supported languages.
cmd/hooks.go Parse nested tool_input.file_path / tool_input.notebook_path for Claude Code hook payloads.
cmd/hooks_payload_test.go Add regression tests for supported payload shapes and nested provenance recording.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread cmd/hooks.go
Comment on lines +1816 to 1819
// Legacy shape: some hosts put the path at the top level.
if filePath, ok := data["file_path"].(string); ok {
return []string{filePath}, nil
}
@JordanCoin
JordanCoin merged commit 4bef107 into main Aug 8, 2026
13 checks passed
@JordanCoin
JordanCoin deleted the fix/hook-payload-tool-input-file-path branch August 8, 2026 16:22
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.

post-edit hook silently does nothing on Claude Code: tool_input.file_path is not parsed

2 participants