aboutsummaryrefslogtreecommitdiff
path: root/hooks/ai-wrap-teardown.sh
diff options
context:
space:
mode:
Diffstat (limited to 'hooks/ai-wrap-teardown.sh')
-rwxr-xr-xhooks/ai-wrap-teardown.sh43
1 files changed, 42 insertions, 1 deletions
diff --git a/hooks/ai-wrap-teardown.sh b/hooks/ai-wrap-teardown.sh
index 6133075..ca73ae6 100755
--- a/hooks/ai-wrap-teardown.sh
+++ b/hooks/ai-wrap-teardown.sh
@@ -39,8 +39,10 @@
# "command": "~/.claude/hooks/ai-wrap-teardown.sh" } ] } ]
set -u
+payload="$(cat)"
+
# Stop-hook stdin JSON carries cwd; basename it to the project / aiv- session.
-cwd="$(jq -r '.cwd // empty' 2>/dev/null)"
+cwd="$(printf '%s' "$payload" | jq -r '.cwd // empty' 2>/dev/null)"
[ -z "$cwd" ] && cwd="$PWD"
proj="$(basename "$cwd")"
@@ -53,15 +55,54 @@ fire() {
emacsclient -e "$1" >/dev/null 2>&1 || true
}
+# A sentinel means wrap-up claimed completion. Re-prove that claim immediately
+# before consuming it: the certificate binds a prior strict check to HEAD, and
+# verify also performs a fresh strict check to catch late writes.
+verify_wrap() {
+ local gate="${GIT_WORKTREE_GATE:-}" detail
+ if [ -z "$gate" ]; then
+ gate="$(command -v git-worktree-gate 2>/dev/null || true)"
+ fi
+ if [ -z "$gate" ] || [ ! -x "$gate" ]; then
+ gate="$HOME/code/rulesets/claude-templates/bin/git-worktree-gate"
+ fi
+ if [ ! -x "$gate" ]; then
+ detail="wrap blocked: git-worktree-gate is unavailable; install rulesets tooling and retry"
+ else
+ detail="$("$gate" verify "$cwd" 2>&1)" && return 0
+ fi
+
+ # Codex and Claude consume different Stop-hook response fields. Codex
+ # command-hook payloads always include model; Claude's do not.
+ if printf '%s' "$payload" | jq -e '.model? != null' >/dev/null 2>&1; then
+ jq -n --arg reason "$detail" \
+ '{continue:false, stopReason:$reason, systemMessage:$reason}'
+ else
+ jq -n --arg reason "$detail" \
+ '{decision:"block", reason:$reason}'
+ fi
+ return 1
+}
+
+consume_certificate() {
+ local dir
+ dir="$(git -C "$cwd" rev-parse --absolute-git-dir 2>/dev/null)" || return 0
+ rm -f "$dir/ai-wrap-clean"
+}
+
# Shutdown supersedes teardown when both are somehow present.
if [ -f "$shutdown_sentinel" ]; then
+ verify_wrap || exit 0
rm -f "$shutdown_sentinel" "$teardown_sentinel"
+ consume_certificate
fire '(cj/ai-term-shutdown-countdown)'
exit 0
fi
if [ -f "$teardown_sentinel" ]; then
+ verify_wrap || exit 0
rm -f "$teardown_sentinel"
+ consume_certificate
fire "(cj/ai-term-quit \"${proj}\")"
exit 0
fi