mirror of
https://github.com/actualbudget/actual.git
synced 2026-11-09 10:45:04 -06:00
* [AI] Fail agent hooks with an actionable error when jq is missing The shared agent-hook scripts all parse their payload with jq but never checked it exists. A missing jq made git-guard.sh block every Bash call with a misleading 'could not parse the hook payload' message, while other hooks silently failed open and skipped their checks (issue #8629). Add require_jq to common.sh — called by every hook before its first jq use — so a missing jq fails closed with a clear install message, distinct from a malformed-payload error. The script name in the message is derived from $0 rather than passed by each caller. The Cursor adapters emit a static JSON deny (their deny() helper itself needs jq to build JSON) or an advisory where blocking isn't possible, and AGENTS.md now documents the prerequisite. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * [AI] Address review: advisory wording for PostToolUse hooks, hook tests PostToolUse hooks (format-edited-file, prefer-one-component) run after the edit already happened, so a missing jq now reports 'Agent hook skipped' instead of claiming a block. Exit 2 stays: it is the only exit code that feeds stderr back to the agent. Add scripts/agent-hooks/test-hooks.sh, a standalone runnable check covering the missing-jq path for every hook plus malformed-JSON, missing-field, and guard-behavior cases, and soften the release-note verb. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * [AI] Remove standalone hook test script Not wired into CI, so it only served as a manual check; the hook behavior was verified by hand and the script isn't worth maintaining. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
59 lines
2.5 KiB
Bash
Executable File
59 lines
2.5 KiB
Bash
Executable File
#!/bin/sh
|
|
# Shared agent guard for pull-request creation, wired for Claude (PreToolUse),
|
|
# Codex (PreToolUse) and Cursor (beforeMCPExecution via adapter).
|
|
#
|
|
# Agents must NOT fill in the PR template: when creating a pull request the body
|
|
# must be the repo's blank template (.github/PULL_REQUEST_TEMPLATE.md),
|
|
# unmodified, so the human who tested the change fills in the Description,
|
|
# Testing and Checklist sections (AGENTS.md / pr-and-commit-rules.md). Reads
|
|
# `.tool_input.body` on stdin and blocks (exit 2 + stderr) if it isn't the
|
|
# pristine template.
|
|
#
|
|
# Comparison ignores cosmetic differences only: CR/LF, trailing whitespace per
|
|
# line, and leading/trailing blank lines. Any real content (a filled-in section,
|
|
# a checked box, extra prose) makes the body differ and is blocked.
|
|
|
|
block() {
|
|
echo "$1" >&2
|
|
exit 2
|
|
}
|
|
|
|
input=$(cat)
|
|
|
|
. "$(dirname "$0")/common.sh"
|
|
# A missing jq fails closed here; the fail-open below is only for payloads jq
|
|
# itself can't make sense of.
|
|
require_jq
|
|
|
|
# Resolve the repo root regardless of which agent invoked us, then the template.
|
|
ROOT=$(resolve_repo_root)
|
|
template="$ROOT/.github/PULL_REQUEST_TEMPLATE.md"
|
|
|
|
# If we can't find the template, fail open — never block PR creation just because
|
|
# the comparison baseline is missing.
|
|
[ -f "$template" ] || exit 0
|
|
|
|
# Fail open when there's no body to compare: a parse failure or an absent/null
|
|
# `.tool_input.body` must not wedge PR creation. (The Cursor/Codex/Claude wiring
|
|
# still fails closed if THIS script can't execute at all.) `jq -re` exits
|
|
# non-zero for both cases; an empty-string body, however, is a real submission
|
|
# and still falls through to the template check below.
|
|
body=$(printf '%s' "$input" | jq -re '.tool_input.body' 2>/dev/null) || exit 0
|
|
|
|
# Canonicalise: drop CR, strip per-line trailing whitespace, and trim leading and
|
|
# trailing blank lines, so only meaningful content differences remain.
|
|
canon() {
|
|
printf '%s\n' "$1" | awk '
|
|
{ sub(/[[:space:]]+$/, ""); lines[NR] = $0 } # also strips a trailing CR
|
|
END {
|
|
s = 1; e = NR
|
|
while (s <= e && lines[s] == "") s++
|
|
while (e >= s && lines[e] == "") e--
|
|
for (i = s; i <= e; i++) print lines[i]
|
|
}'
|
|
}
|
|
|
|
[ "$(canon "$body")" = "$(canon "$(cat "$template")")" ] && exit 0
|
|
|
|
block "Blocked: don't fill in the PR template. Create the pull request with the repo's blank template (.github/PULL_REQUEST_TEMPLATE.md) as the body, unmodified — the human who tested the change fills in the Description, Testing and Checklist sections (AGENTS.md). (scripts/agent-hooks/pr-template-blank.sh)"
|