chook#
A PreToolUse hook for Claude Code that gives you granular control over what Claude can do, with an interactive review TUI for building rules from real usage.
Zero dependencies. Single JS file. Works with any Claude Code session on your machine.
Install#
ln -s /path/to/chook/chook.mjs /usr/local/bin/chook
cp example.toml ~/.config/chook.toml
Add to ~/.claude/settings.json:
{
"hooks": {
"PreToolUse": [
{
"matcher": "*",
"hooks": [
{
"type": "command",
"command": "node /path/to/chook/chook.mjs run"
}
]
}
],
"PostToolUse": [
{
"matcher": "*",
"hooks": [
{
"type": "command",
"command": "node /path/to/chook/chook.mjs post"
}
]
}
]
}
}
The PostToolUse hook enables macOS notifications when Claude is waiting for your approval.
How it works#
Every time Claude tries to use a tool, the hook checks your rules:
- Deny rules checked first — hard block, Claude sees your
reasonmessage - Allow rules checked next — auto-approved, no prompt
- No match — passthrough to Claude Code's normal permission flow
Claude tries tool → deny? → BLOCKED (with guidance)
→ allow? → AUTO-APPROVED
→ neither? → normal Claude Code prompt
Config#
Default location: ~/.config/chook.toml (override with --config)
Deny rules#
[[deny]]
tool = "Bash"
command_regex = "^rm .*-rf"
reason = "Use rm on specific files, or ask the user to run this manually."
[[deny]]
tool = "Bash"
command_regex = "&|;|\\||`|\\$\\("
reason = "BLOCKED: You MUST run exactly ONE command per Bash call."
[[deny]]
tool = "Read"
file_path_regex = "\\.(env|secret|pem|key)$"
reason = "Ask the user to provide the needed values."
The reason string is shown to Claude, so write it as guidance for what to do instead.
Allow rules#
# Auto-allow file ops in the project directory
[[allow]]
tool = "Read"
restrict_to_cwd = true
file_path_exclude_regex = "\\.\\."
# Auto-allow /tmp
[[allow]]
tool = "Read"
file_path_regex = "^/tmp/"
# Auto-allow safe bash commands
[[allow]]
tool = "Bash"
command_regex = "^git "
command_exclude_regex = "push.*--force"
# Auto-allow specific subagents
[[allow]]
tool = "Task"
subagent_type = "Explore"
Rule fields#
| Field | Tools | Description |
|---|---|---|
tool |
all | Tool name to match (required) |
reason |
all | Message shown to Claude on deny |
restrict_to_cwd |
file tools | Restrict to the directory where Claude was started |
file_path_regex |
Read, Write, Edit, Glob, NotebookEdit | Regex the file path must match |
file_path_exclude_regex |
file tools | Regex that rejects the match |
command_regex |
Bash | Regex the command must match |
command_exclude_regex |
Bash | Regex that rejects the match |
subagent_type |
Task | Exact match on subagent type |
prompt_regex |
Task | Regex the prompt must match |
prompt_exclude_regex |
Task | Regex that rejects the match |
Auditing#
[audit]
audit_file = "/tmp/chook-audit.json"
audit_level = "matched" # off | matched | all
Commands#
chook run#
Hook handler. Reads tool JSON from stdin, outputs permission decision to stdout. This is what Claude Code calls.
chook post#
PostToolUse handler. Cancels pending notifications when a tool completes.
chook validate#
Check your config for errors.
$ chook validate
Configuration is valid!
Deny rules: 5
Allow rules: 11
Audit file: /tmp/chook-audit.json
Audit level: matched
chook review#
Interactive TUI for reviewing denied/passthrough tool uses and adding rules.
Entries are grouped into smart suggestions:
- Bash commands grouped by command name (e.g.
git,npm,cargo) - File operations grouped by project directory with
restrict_to_cwd - Dangerous commands like
rmget automaticcommand_exclude_regexfor dangerous flags
Navigate with j/k, press a to allow, d to deny, s to skip, q to quit.
chook allow-last#
Add an allow rule from the most recent audit entry.
chook deny-last#
Add a deny rule from the most recent audit entry.
Workflow#
The fast feedback loop when Claude gets blocked:
- Claude tries something, gets prompted or blocked
Ctrl-Zto background Claudechook allow-lastto allow it (orchook reviewto see all recent entries)fgto resume
Rules are live immediately — the config is read on every hook invocation.
Notifications#
When a tool passes through to Claude's normal permission flow and Claude is waiting for your approval, a macOS notification fires after 15 seconds. This way you'll notice if you're in another window. The PostToolUse hook cancels the notification if the tool completes quickly (meaning it was auto-approved by Claude's own settings, not actually waiting).
Inspired by#
claude-code-permissions-hook by Korny Sietsma — a Rust implementation of the same idea. chook is a JS port with added interactive review and notifications.