2026-05-30 22:14:40 +02:00
#!/usr/bin/env bash
#
2026-06-17 17:49:55 +02:00
# PreToolUse guard (Bash): block `git commit` ONLY when the rbw vault agent is locked
# AND the commit would actually need the vault. The pre-commit ansible-lint hook decrypts
# vault.yml via rbw — but it is scoped (`files: ^(roles|playbooks|inventories)/.*\.ya?ml$`,
# always_run:false), so a docs-/config-only commit never triggers it and needs no vault.
# (2026-06-17 kaizen, docs/FRICTION.md: the old guard blocked *every* locked commit, so a
# docs-only commit got snagged needing a vault password it never uses.)
2026-05-30 22:14:40 +02:00
#
2026-06-17 17:49:55 +02:00
# Fails OPEN: blocks only on a definitive "Ansible content staged AND rbw locked" signal.
# rbw missing, not a plain `git commit`, `--no-verify`, or no Ansible content staged → allow.
# When unsure it errs toward blocking (asking for an unlock is cheap; a deep pre-commit
# failure is not).
2026-05-30 22:14:40 +02:00
#
set -uo pipefail
input = $( cat 2>/dev/null) || exit 0
cmd = $( printf '%s' " $input " | jq -r '.tool_input.command // empty' 2>/dev/null) || exit 0
case " $cmd " in
*"git commit" *) : ; ; # a git commit — check further
*) exit 0 ; ; # not a commit — allow
esac
case " $cmd " in
*"--no-verify" *) exit 0 ; ; # hooks skipped anyway — allow
esac
command -v rbw >/dev/null 2>& 1 || exit 0 # rbw not installed — allow
2026-06-17 17:49:55 +02:00
rbw unlocked >/dev/null 2>& 1 && exit 0 # unlocked — allow
2026-05-30 22:14:40 +02:00
2026-06-17 17:49:55 +02:00
# rbw is LOCKED. Only block if this commit would run the vault-decrypting ansible-lint
# hook — i.e. staged content matches its `files:` scope. Mirror that regex exactly.
ANSIBLE_RE = '^(roles|playbooks|inventories)/.*\.ya?ml$'
2026-05-30 22:14:40 +02:00
2026-06-17 17:49:55 +02:00
cd " ${ CLAUDE_PROJECT_DIR :- . } " 2>/dev/null || exit 0
files = $( git diff --cached --name-only 2>/dev/null) || exit 0
# `git commit -a/--all` also sweeps in modified tracked files that aren't staged yet.
# (Substring match — errs toward including them, which only ever over-blocks. Safe.)
case " $cmd " in
*" -a" *| *"--all" *) files = " $files " $'\n' " $( git diff --name-only 2>/dev/null) " ; ;
esac
# No Ansible content in the fileset → ansible-lint hook won't run → no vault needed → allow.
printf '%s\n' " $files " | grep -Eq " $ANSIBLE_RE " || exit 0
# Ansible content staged AND rbw locked — the commit would fail deep in pre-commit. Block.
2026-05-30 22:14:40 +02:00
cat <<'JSON'
2026-06-17 17:49:55 +02:00
{ "hookSpecificOutput" :{ "hookEventName" :"PreToolUse" ,"permissionDecision" :"deny" ,"permissionDecisionReason" :"rbw is locked and this commit stages Ansible content — the pre-commit ansible-lint hook needs the vault password to decrypt vault.yml. Run: rbw unlock (docs-/config-only commits are exempt and won't hit this guard.)" } }
2026-05-30 22:14:40 +02:00
JSON
exit 0