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.