Your first block
git stash is where an agent’s uncommitted work goes to die: it stashes, forgets, and your changes are three pops deep by the time you notice. Declare it off-limits — the hook below is the complete file. Drop it in .claude/hooks/, and every Bash call the agent makes passes through it before running.
Type at it. Try git stash, then get sneaky — hide it behind cd, chain it after &&, pipe into it. The code is editable, too: change the pattern and the verdicts follow.
from captain_hook import block_command
block_command(
["git", "stash"],
reason="git stash hides work the rest of the team can't see",
hint="commit to a branch or use jj instead",
)
Two things worth noticing:
The family is covered. cd /tmp && git stash, true | git stash, git stash pop — one declaration catches the command anywhere in the line, not just at position zero.
A mention is not a command. echo "git stash" names the command without running it, and it does not block. A token list is argv, so capt-hook compares it against the argv the shell parsed: the words have to be the command, not appear in it.
Hand block_command a string instead of a list and you get the other behavior — a regex over the raw line, which does block that echo. Next: when each one is right.