fix(agent hooks): wire hooks into real harness entry points and fix payload parsing

Claude Code never reads a standalone .claude/hooks.json, so the PostToolUse and
Stop pipeline is moved into .claude/settings.json and hooks.json is removed;
.cursor/hooks.json is rewritten in Cursor's version+afterFileEdit/stop schema;
.codex/hooks.json is already Codex-valid and stays. The shared scripts now
parse both the Cursor file_path and Claude/Codex tool_input.file_path stdin
shapes and normalize absolute paths, so the format, yarn-install, and
react-pattern-review hooks stop being silent no-ops. verify.sh blocks with
exit 2 plus a stderr reason, guards stop_hook_active, and skips clean trees;
react-pattern-review surfaces its reminder via hookSpecificOutput on
PostToolUse; sync-git-branches no longer misreports open PRs as merged. The
validator now checks that the three harness-specific entry points wire the
same hook scripts instead of requiring byte-identical hooks.json copies.
This commit is contained in:
Tommaso Casaburi
2026-07-03 13:58:36 +07:00
parent 0166f2f4e8
commit 1ac3e5883b
9 changed files with 190 additions and 123 deletions
+13 -4
View File
@@ -1,14 +1,16 @@
#!/bin/bash
# afterFileEdit hook: Auto-format files after AI edits them
# Receives JSON via stdin: {"file_path": "...", "edits": [...]}
# afterFileEdit/PostToolUse hook: Auto-format files after AI edits them
# Stdin JSON differs per harness:
# Cursor afterFileEdit: {"file_path": "...", "edits": [...]}
# Claude/Codex PostToolUse: {"tool_input": {"file_path": "..."}, ...}
input=$(cat)
if ! command -v jq >/dev/null 2>&1; then
exit 0
fi
file_path=$(printf '%s' "$input" | jq -r '.file_path // empty' 2>/dev/null)
file_path=$(printf '%s' "$input" | jq -r '.tool_input.file_path // .file_path // empty' 2>/dev/null)
if [ -z "$file_path" ]; then
exit 0
@@ -17,8 +19,15 @@ fi
repo_root="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
cd "$repo_root" || exit 0
# Claude Code and Codex deliver absolute paths; make them repo-relative and
# skip anything outside the repo.
case "$file_path" in
*.js|*.ts|*.tsx|*.mjs)
"$repo_root"/*) file_path="${file_path#"$repo_root"/}" ;;
/*) exit 0 ;;
esac
case "$file_path" in
*.js|*.jsx|*.cjs|*.mjs|*.ts|*.tsx)
dir_part="${file_path%/*}"
base_name="${file_path##*/}"
if [ "$dir_part" = "$file_path" ]; then