Keel Skills ships two hooks registered inDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/quitohooded/keel-skills/llms.txt
Use this file to discover all available pages before exploring further.
hooks.json. These are the hard enforcement layer — they fire automatically without the agent having to decide to comply. Where the skills (like authorization-protocol) handle the reasoning side of governance, the hooks handle the mechanical enforcement side: the SessionStart hook ensures the policy is always in context, and the PreToolUse hook intercepts every tool call before it executes and emits a verdict.
hooks.json
The hook registrations live inhooks.json at the plugin root:
matcher: "*" on PreToolUse means the enforcement hook fires on every tool call, not just a named subset.
SessionStart hook (inject-policy.cjs)
When a Claude Code session starts, this hook runs automatically. It looks for anAGENT_POLICY.md at the project root (or the path the user designates). If the file is found, the hook injects its contents into context — so the policy is always present and the agent doesn’t have to remember to read it. When no policy file exists, the hook stays silent. Nothing breaks; the agent falls back to the defaults described in the authorization-protocol skill.
PreToolUse hook (enforce-policy.cjs)
This is the hard counterpart to the reasoning skill. It inspects every tool call before it runs and emits one of three verdicts:allow, ask, or deny.
Decision contract
The hook communicates with Claude Code through stdin and stdout:- stdin:
{ tool_name, tool_input, cwd, ... } - stdout:
Tool classification
The hook classifies incoming tool calls into one of four categories:- Read-only tools (
Read,Grep,Glob,LS,WebFetch,WebSearch,NotebookRead,TodoWrite) — always allowed without consulting the policy. - File write tools (
Write,Edit,MultiEdit,NotebookEdit) — checked againsthot_pathsglobs from the policy (plus anystanding_allow_pathsthat create scoped exceptions). - Bash commands — the command string is normalized and checked against
hot_commandspatterns (plusstanding_allow_commandsfor scoped exceptions). - MCP tools (tool names starting with
mcp__) — checked against a built-in list of outward/irreversible substrings:deploy,execute_sql,apply_migration,create_post,delete,send,publish,merge_branch,create_short_link,charge.
git push, git commit, git reset --hard, rm -rf, drop table, drop database, truncate, vercel deploy, vercel --prod, supabase db push, migrate, and npm publish.
Verdicts
| Verdict | Meaning |
|---|---|
allow | Proceed — the tool call is not in a hot set, or is covered by a standing approval |
ask | Request explicit human approval before the action runs (green light prompt) |
deny | Block outright — used in headless/CI mode when no human can provide a green light |
Headless and CI mode
WhenKEEL_NONINTERACTIVE=1 or the CI environment variable is set, the hook treats any ask verdict as deny. There is no human present to give a green light, so any hot action is blocked rather than paused for input.
Audit trail
Every decision the hook makes is appended to.keel/audit.jsonl in the project root. The .keel/ directory is created automatically if it does not already exist. Each line is a JSON record:
| Field | Content |
|---|---|
ts | ISO 8601 timestamp |
tool | Tool name as passed by Claude Code |
input | The file path, command string, or tool name (truncated to 200 chars) |
verdict | allow, ask, or deny |
rule | The rule that matched, e.g. hot_command:git push, hot_path:src/**, standing_allow:npm run build, no_match |
Fail-open design
When the hook receives malformed input or encounters an internal error, it fails open — it emitsallow and lets the action proceed. This is intentional. The hook is a backstop layer, not a strict gate: it should never wedge a workflow over an error in its own parsing logic. Internal errors are still logged to the audit trail where possible.