Skip to main content

hooks-management

Manage hooks and automation for coding agents (Claude Code, Codex CLI, OpenCode, Devin CLI/Desktop). Use when users want to add, list, remove, update, or validate hooks. Triggers on requests like "add a hook", "create a hook that...", "list my hooks", "remove the hook", "validate hooks", or any mention of automating agent behavior with shell commands or plugins.

Source facts

Repository
CodeAlive-AI/ai-driven-development
Last source activity
September 16, 2026 at 18:19
Detected SKILL.md language
English
Stars
167
Forks
16

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

File Explorer
7 files

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
hooks-management
description
Manage hooks and automation for coding agents (Claude Code, Codex CLI, OpenCode, Devin CLI/Desktop). Use when users want to add, list, remove, update, or validate hooks. Triggers on requests like "add a hook", "create a hook that...", "list my hooks", "remove the hook", "validate hooks", or any mention of automating agent behavior with shell commands or plugins.
# Hooks Management Manage hooks and automation through natural language commands. **IMPORTANT**: After adding, modifying, or removing hooks, always inform the user that they need to **restart the agent** for changes to take effect. Hooks are loaded at startup. ## Quick Reference **Hook Events** (Claude Code, as of 2026-04 — 28 events): - *Session lifecycle*: SessionStart, SessionEnd, InstructionsLoaded - *User input*: UserPromptSubmit, UserPromptExpansion - *Tool execution*: PreToolUse, PostToolUse, PostToolUseFailure, PostToolBatch - *Permissions*: PermissionRequest, PermissionDenied - *Model output*: Stop, StopFailure - *Subagents/tasks*: SubagentStart, SubagentStop, TaskCreated, TaskCompleted, TeammateIdle - *Config/state*: ConfigChange, FileChanged, CwdChanged - *Compaction*: PreCompact, PostCompact - *Worktree*: WorktreeCreate, WorktreeRemove - *MCP*: Elicitation, ElicitationResult - *Notifications*: Notification **Handler types**: `command`, `http`, `mcp_tool`, `prompt`, `agent`. Some events are command-only (PostCompact, PermissionDenied, Elicitation/ElicitationResult, FileChanged, CwdChanged, ConfigChange, InstructionsLoaded, WorktreeCreate/Remove, SubagentStart, StopFailure, TeammateIdle, Setup, SessionStart, SessionEnd, Notification). **Claude Code Settings Files**: - User-wide: `~/.claude/settings.json` - Project: `.claude/settings.json` - Local (not committed): `.claude/settings.local.json` - Drop-in policy fragments: `~/.claude/managed-settings.d/` (managed-settings only) **Codex CLI / Codex App Settings Files (current as of 2026-06)**: - User config: `~/.codex/config.toml` - User hooks: `~/.codex/hooks.json` or inline `[hooks]` tables in `~/.codex/config.toml` - Project hooks: `<repo>/.codex/hooks.json` or inline `[hooks]` tables in `<repo>/.codex/config.toml` (trusted projects only) - Codex App and CLI share these config layers. In the App/IDE, the settings UI opens the same `config.toml`. - Hooks are enabled by default. Use `[features].hooks = false` to disable them. `codex_hooks` is a deprecated alias. - Non-managed Codex command hooks must be reviewed/trusted with `/hooks`; changed hook definitions are skipped until trusted. **Devin CLI / Desktop hook locations**: - Project: `.devin/hooks.v1.json` (standalone hooks object, recommended) or `"hooks"` in `.devin/config.json` / `.devin/config.local.json` - User: `"hooks"` in `~/.config/devin/config.json` (`%APPDATA%\devin\config.json` on Windows) - Claude-format hooks under `.claude/` are imported automatically when `read_config_from.claude` is on (default) - Events: PreToolUse, PostToolUse, PermissionRequest, UserPromptSubmit, Stop, PostCompaction, SessionStart, SessionEnd; `matcher` is a regex on `tool_name` - See [references/devin-hooks.md](references/devin-hooks.md) for the full event/output contract **Claude Code default control mechanism for PreToolUse**: emit JSON on stdout with `hookSpecificOutput.permissionDecision` set to `"allow"`, `"deny"`, **`"ask"`** (triggers the built-in user confirmation prompt), or **`"defer"`** (pause headless tool calls; resume with `-p --resume`). See [Decision Control](#decision-control-pretooluse). Do NOT roll your own confirmation schemes (env-var flags, interactive `osascript` prompts, bypass tokens) — those break the built-in UX and silently fail under existing `permissions.allow` entries. **Codex exception**: Codex `PreToolUse` does not support `"ask"` yet. In Codex configs, use `deny` / exit code 2 for hard blocks, `additionalContext` for advisory context, or Codex approval policy/permissions for native prompts. **Disable all hooks**: set `disableAllHooks: true` in settings.json. ## Workflow ### 1. Understand the Request Parse what the user wants: - **Add/Create**: New hook for specific event and tool - **List/Show**: Display current hooks configuration - **Remove/Delete**: Remove specific hook(s) - **Update/Modify**: Change existing hook - **Validate**: Check hooks for errors ### 2. Validate Before Writing Always run validation before saving: ```bash python3 "$SKILL_PATH/scripts/validate_hooks.py" ~/.claude/settings.json ``` ### 3. Read Current Configuration ```bash cat ~/.claude/settings.json 2>/dev/null || echo '{}' ``` ### 4. Apply Changes Use Edit tool for modifications, Write tool for new files. ## Adding Hooks ### Translate Natural Language to Hook Config | User Says | Event | Matcher | Notes | |-----------|-------|---------|-------| | "log all bash commands" | PreToolUse | Bash | Logging to file | | "format files after edit" | PostToolUse | Edit\|Write | Run formatter | | "block .env file changes" | PreToolUse | Edit\|Write | Exit code 2 blocks | | "notify me when done" | Notification | "" | Desktop notification | | "run tests after code changes" | PostToolUse | Edit\|Write | Filter by extension | | "ask before dangerous commands" | PreToolUse | Bash | Claude Code: emit JSON `permissionDecision: "ask"` (built-in confirm UI). Codex: use approval policy if possible; hook-level `ask` is unsupported. | | "require manual approval for X" | PreToolUse | Bash/Edit/Write | Claude Code: emit JSON `permissionDecision: "ask"`, NOT exit 2. Codex: choose policy prompt or hard block. | | "block unless confirmed" | PreToolUse | Bash | Claude Code: JSON `"ask"` lets the user approve per call. Codex: no hook-created confirmation prompt yet. | ### Hook Configuration Template ```json { "hooks": { "EVENT_NAME": [ { "matcher": "TOOL_PATTERN", "hooks": [ { "type": "command", "command": "SHELL_COMMAND", "timeout": 60 } ] } ] } } ``` ### Simple vs Complex Hooks **PREFER SCRIPT FILES** for complex hooks. Inline commands with nested quotes, `osascript`, or multi-step logic often break due to JSON escaping issues. | Complexity | Approach | Example | |------------|----------|---------| | Simple | Inline | `jq -r '.tool_input.command' >> log.txt` | | Medium | Inline | Single grep/jq pipe with basic conditionals | | Complex | **Script file** | Dialogs, multiple conditions, osascript, error handling | **Script location**: `~/.claude/hooks/` (create if needed) **Script template for PreToolUse** (`~/.claude/hooks/my-hook.sh`) — use JSON decision control as the primary mechanism; exit codes are a fallback for simple blocking only: ```bash #!/bin/bash set -euo pipefail # Read JSON input from stdin input=$(cat) cmd=$(echo "$input" | jq -r '.tool_input.command') # Your logic here if echo "$cmd" | grep -q 'pattern-requiring-confirmation'; then # PRIMARY PATTERN for "require user confirmation": emit JSON on stdout. # Claude Code will show its built-in confirm prompt to the user. jq -n '{ hookSpecificOutput: { hookEventName: "PreToolUse", permissionDecision: "ask", permissionDecisionReason: "Explain why this call is risky" } }' exit 0 fi if echo "$cmd" | grep -q 'pattern-to-hard-block'; then # Hard block (no user override possible): JSON deny, NOT exit 2. jq -n '{ hookSpecificOutput: { hookEventName: "PreToolUse", permissionDecision: "deny", permissionDecisionReason: "Reason shown to Claude" } }' exit 0 fi exit 0 # Allow (silent) ``` **Why JSON decisions, not exit 2 or home-grown prompts:** - `permissionDecision: "ask"` triggers the built-in Claude Code confirm UI — the user sees a clean prompt and can allow/deny per-call. - `exit 2` is a blunt block; the user cannot override it from the UI, and Claude often re-tries with workarounds. - Home-grown schemes (env-var flags like `CONFIRMED=1`, `osascript` dialogs, bypass tokens) break the native UX, leak into command history, and are silently bypassed if the tool already has a matching `permissions.allow` rule. **Hook config using script**: ```json { "type": "command", "command": "~/.claude/hooks/my-hook.sh" } ``` **Other handler types (2026)**: `http` (POSTs event JSON to a URL), `mcp_tool` (calls a tool on a configured MCP server), `prompt` (evaluates a prompt with an LLM, supports `$ARGUMENTS`), `agent` (runs an agentic verifier with tools). Some events are command-only (PostCompact, PermissionDenied, Elicitation/ElicitationResult, FileChanged, CwdChanged, ConfigChange, InstructionsLoaded, WorktreeCreate/Remove, SubagentStart, StopFailure, TeammateIdle, SessionStart/End, Notification). **Always**: 1. Create script in `~/.claude/hooks/` 2. Make executable: `chmod +x ~/.claude/hooks/my-hook.sh` 3. Test with sample input: `echo '{"tool_input":{"command":"test"}}' | ~/.claude/hooks/my-hook.sh` ### Common Patterns **Logging (PreToolUse)**: ```json { "matcher": "Bash", "hooks": [{ "type": "command", "command": "jq -r '.tool_input.command' >> ~/.claude/command-log.txt" }] } ``` **File Protection (PreToolUse, exit 2 to block)**: ```json { "matcher": "Edit|Write", "hooks": [{ "type": "command", "command": "jq -r '.tool_input.file_path' | grep -qE '(\\.env|secrets)' && exit 2 || exit 0" }] } ``` **Auto-format (PostToolUse)**: ```json { "matcher": "Edit|Write", "hooks": [{ "type": "command", "command": "file=$(jq -r '.tool_input.file_path'); [[ $file == *.ts ]] && npx prettier --write \"$file\" || true" }] } ``` **Desktop Notification (Notification)**: ```json { "matcher": "", "hooks": [{ "type": "command", "command": "osascript -e 'display notification \"Claude needs attention\" with title \"Claude Code\"'" }] } ``` ## Decision Control (Claude Code PreToolUse) Claude Code PreToolUse hooks control tool execution by emitting JSON on stdout. This is the **default mechanism** — use it instead of exit codes whenever the intent is richer than "silently allow / hard block", especially when the user should be asked to confirm. | `permissionDecision` | Behavior | Use for | |----------------------|----------|---------| | `"allow"` | Bypass permissions, proceed silently | Pre-approving a safe call | | `"deny"` | Block, reason shown to Claude | Hard block (no user override) | | `"ask"` | **Built-in Claude Code confirm UI** shown to user | "Require manual approval for X" — the canonical pattern | | `"defer"` | Pause headless tool call, resume via `-p --resume` | External-system integrations in headless (`-p`) sessions | Additional JSON fields: - `permissionDecisionReason` — shown to the user for `"allow"`/`"ask"`, shown to Claude for `"deny"` - `updatedInput` — modify tool input before execution - `additionalContext` — inject context for Claude before the tool executes ### Ask user before dangerous command (the canonical pattern) When the user says anything like **"require manual confirmation"**, **"ask before doing X"**, **"don't run Y without my approval"** — this is the pattern. Do not invent bypass env vars, `osascript` dialogs, or confirmation tokens. The built-in prompt already handles per-call allow/deny and is the only path that integrates with existing `permissions.allow` rules correctly. ```bash #!/bin/bash set -euo pipefail input=$(cat) cmd=$(echo "$input" | jq -r '.tool_input.command // empty') if echo "$cmd" | grep -qE 'supabase\s+db\s+reset'; then jq -n '{ hookSpecificOutput: { hookEventName: "PreToolUse", permissionDecision: "ask", permissionDecisionReason: "This will destroy and recreate the local database." } }' else exit 0 fi ``` ### Deny with reason (hard block) ```bash jq -n '{ hookSpecificOutput: { hookEventName: "PreToolUse", permissionDecision: "deny", permissionDecisionReason: "Destructive command blocked by hook" } }' ``` ### Gotcha: `"ask"` vs existing `permissions.allow` rules
View on GitHub
This SKILL.md is very large, so SkillsMP previews the first section here. View on GitHub