Skip to content

Hooks and tools

What runs without you asking, and the scripts behind it.

What runs when

 session starts          ----> SessionStart: session-preflight.sh (prints warnings)

 agent runs a Bash command ----> PreToolUse: git-guardrail.sh  (can deny)
                           ----> PreToolUse: mutation_gate.host_exec  (can deny or rewrite)
                           ----> PreToolUse: bare-rm-guard.sh  (can deny)

 git commit
   |-- pre-commit stage ----> rules-check, mutation-gate, no-new-docs
   '-- commit-msg stage ----> commit-msg, diff-discipline, no-leaks

 agent finishes a turn  ----> Stop: mutation-gate-hook.sh      (repos with .mutation-gate.toml)
                        ----> Stop: say-hook.sh                (speaks when armed)
                        ----> Stop: codex-review-hook.sh       (repos with agent-sdlc.codexReview)

Claude hooks

Event Script Declared in
SessionStart session-preflight.sh hooks/hooks.json
PreToolUse (Bash) git-guardrail.sh hooks/hooks.json
PreToolUse (Bash) mutation_gate.host_exec hooks/hooks.json
PreToolUse (Bash) bare-rm-guard.sh hooks/hooks.json
Stop mutation-gate-hook.sh hooks/hooks.json
Stop say-hook.sh hooks/hooks.json
Stop codex-review-hook.sh hooks/hooks.json
  • session-preflight.sh prints one preflight: ... line at session start, or nothing. It warns when $TMPDIR or $HOME sits on a filesystem over 85% used, or when a worktree of the repo holds a root-owned file.
  • mutation-gate-hook.sh exits quietly unless the repo has a .mutation-gate.toml, then runs mutation-gate --worktree.
  • Adding that file is all a repo needs for Stop-time gating.
  • codex-review-hook.sh exits quietly unless git config --bool agent-sdlc.codexReview is true. Set it with --global for every repo. Codex checks the change in two throwaway worktrees and blocks only on a failure that it reproduced on the change and not on the last tree it accepted. A failure that shows up in both comes back as a follow-up to file. It reviews nothing when the HEAD and the tree are the same as at the last review.

git-guardrail

A PreToolUse hook on every Bash call the agent makes. It denies:

Command Why
git add -A, git add ., git add --all Uncommitted edits ride along across branches
git reset --hard Throws away work
git clean -f… Deletes untracked files
git checkout ., git checkout -- ., git restore . Discards every edit
git push --force / -f without --force-with-lease Overwrites a shared branch
git push with a +refspec Same, by another spelling
git branch -D <b> Deletes unmerged work
git checkout / git switch off the default branch, in the primary worktree Strands the primary checkout on a feature branch; use git worktree add
  • branch -D is allowed when gh finds a merged PR for the branch and the branch tip is that PR's head, or an ancestor of it.
  • A checkout or switch that stays on the default branch, or runs in a linked worktree, is allowed. Restoring a path (git checkout -- <file>) is allowed.
  • A preceding cd <path> and a git -C <path> are followed, both for the checkout/switch check and for branch -D.
  • A denial names the rule and how to run it yourself:
git-guardrail: blocks git reset --hard
  git safety protocol: never reset --hard without explicit request
  run it yourself with: ! git ...
  • It matches command text, so the pattern inside a heredoc or sed script also trips it. Edit files with the editor tool instead.

host-exec

A PreToolUse hook on every Bash call (mutation_gate/host_exec.py). It acts only in a repo with a .mutation-gate.toml, and never inside a container.

Command Result
Host python3, pytest, make, cmake, colcon or pip install Rewritten to docker run in test_image, with --user $(id -u):$(id -g), the repo mounted at /w
Same, with test_image empty Denied, naming the config key
docker run that bind-mounts the repo or a worktree without --user Denied, with the command rewritten to carry --user
Host command piped from a docker command Denied: split the line
  • chown -R $(id -u):$(id -g) <mount target> inside a docker run is exempt from the --user deny. It is the teardown that hands a tree back.
  • A line with a docker segment is rewritten per element: each &&, ||, ; or newline element that has a host command and does not start with docker is wrapped; the rest stays.
  • is_test_image_used = false turns the rewrite and the no-image deny off. The --user deny stays.
  • It passes through with no .mutation-gate.toml, inside a container (/.dockerenv), and with a config it cannot read, which prints a notice.
  • A denial and a notice are prefixed host-exec:.

bare-rm-guard

A PreToolUse hook on every Bash call. It denies a bare rm, cp or mv at command position, including after &&, ||, ;, |, a newline, ( and {. The interactive shell aliases them to -i, and the prompt waits out the full tool timeout.

  • Run command rm, command cp or command mv instead.
  • \rm, /bin/rm, git rm and a word in an argument or a quoted string (echo rm, grep -rn mv) pass.
  • A denial is prefixed bare-rm-guard:.

Pre-commit hooks

Wire the gate into a consuming repo's .pre-commit-config.yaml:

repos:
  - repo: https://github.com/PavelGuzenfeld/agent-sdlc
    rev: v0.5.3
    hooks:
      - id: rules-check
      - id: mutation-gate
      - id: no-new-docs
      - id: commit-msg
      - id: diff-discipline
      - id: no-leaks
pre-commit install
pre-commit install --hook-type commit-msg
Hook id Stage Runs
rules-check pre-commit mutation-gate rules check
mutation-gate pre-commit mutation-gate --staged
no-new-docs pre-commit mutation-gate no-new-docs
commit-msg commit-msg mutation-gate commit-msg
diff-discipline commit-msg mutation-gate diff-discipline
no-leaks commit-msg mutation-gate no-leaks
  • The three commit-msg hooks need the second pre-commit install line, unless the repo's config sets default_install_hook_types.

bin/ scripts

Script What it does
session-preflight.sh SessionStart hook: warns on a full disk or a root-owned file in a worktree
git-guardrail.sh The PreToolUse hook above
bare-rm-guard.sh PreToolUse hook: denies a bare rm, cp or mv
mutation-gate-hook.sh Stop hook: finds the repo root from the hook payload, runs the gate if opted in
codex-review-hook.sh, codex-review-prompt.md Stop hook: the codex review that blocks only on reproduced regressions
mutation-gate Runs the gate from this checkout without pip install
install-gdscript-parser Builds the tree-sitter GDScript parser in Docker so ast-grep can read .gd files
say.sh, ksay.py Speak a text file through Kokoro
say-trigger.sh, say-narrate.py, say-prompt.md, say-extract.jq The /say pipeline: pick the last answer, rewrite it for listening, speak it
say-key.sh Terminal key binding: stop, arm, or speak
say-hook.sh Stop hook that speaks the answer when armed
say-tones/ The raw arm, stop and error tones say-key.sh and say-narrate.py play
sdlc-bootstrap Creates missing model:*, follow-up and category labels, and writes missing issue forms and PR template into the repo
install-gdscript-parser                 # writes ~/.local/share/ast-grep/gdscript.so
bin/mutation-gate --dry-run             # run the gate from a checkout

install.sh

usage: ./install.sh [--target codex|all] [--deps | --deps=say] [--uninstall-legacy]
Flag Effect
--target claude Refused: prints the claude plugin install commands and exits 2
--target codex Link skills/ into ~/.codex/skills; render each command as a Codex skill
--target all Same as codex; the default
--deps Install git gh jq docker python3 ast-grep pytest pre-commit and the gate
--deps=say Also the Kokoro voice stack
--uninstall-legacy Remove the ~/.claude links and hooks an older install.sh added, then exit
  • Rerunning changes nothing.
  • ast-grep-cli also installs an sg shim. If ~/.local/bin comes before /usr/bin, it shadows the system sg. Always call ast-grep.

Plugins

Agent Manifest Load it
Claude Code .claude-plugin/plugin.json, .claude-plugin/marketplace.json claude plugin install agent-sdlc@agent-sdlc, or claude --plugin-dir /path/to/agent-sdlc for one session
Codex .codex-plugin/plugin.json, .agents/plugins/marketplace.json Add the repo as a marketplace source
  • The Claude plugin ships skills, commands and hooks. The Codex plugin ships skills and hooks; commands reach Codex only through install.sh.