forge-cw 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/console/assets/addon-fit-BlxrFPDK.js +1 -0
- package/console/assets/addon-web-links-BWjmmSgS.js +12 -0
- package/console/assets/index-Digctq9W.css +32 -0
- package/console/assets/index-ItVUTuQf.js +31 -0
- package/console/assets/xterm-DlVFs1Kw.js +9 -0
- package/console/index.html +13 -0
- package/cw/.bundle.json +4 -0
- package/cw/agents/presentation-curator.md +119 -0
- package/cw/agents/weather-agent.md +67 -0
- package/cw/cw +6188 -0
- package/cw/cw-shell-integration.sh +132 -0
- package/cw/hooks/HOOKS-README.md +471 -0
- package/cw/hooks/config/hooks-config.json +24 -0
- package/cw/hooks/scripts/auto-compact.py +133 -0
- package/cw/hooks/scripts/hooks.py +472 -0
- package/cw/hooks/scripts/review-autoclose.py +171 -0
- package/cw/hooks/sounds/agent_permissionrequest/agent_permissionrequest.mp3 +0 -0
- package/cw/hooks/sounds/agent_permissionrequest/agent_permissionrequest.wav +0 -0
- package/cw/hooks/sounds/agent_posttooluse/agent_posttooluse.mp3 +0 -0
- package/cw/hooks/sounds/agent_posttooluse/agent_posttooluse.wav +0 -0
- package/cw/hooks/sounds/agent_posttoolusefailure/agent_posttoolusefailure.mp3 +0 -0
- package/cw/hooks/sounds/agent_posttoolusefailure/agent_posttoolusefailure.wav +0 -0
- package/cw/hooks/sounds/agent_pretooluse/agent_pretooluse.mp3 +0 -0
- package/cw/hooks/sounds/agent_pretooluse/agent_pretooluse.wav +0 -0
- package/cw/hooks/sounds/agent_stop/agent_stop.mp3 +0 -0
- package/cw/hooks/sounds/agent_stop/agent_stop.wav +0 -0
- package/cw/hooks/sounds/agent_subagentstop/agent_subagentstop.mp3 +0 -0
- package/cw/hooks/sounds/agent_subagentstop/agent_subagentstop.wav +0 -0
- package/cw/hooks/sounds/configchange/configchange.mp3 +0 -0
- package/cw/hooks/sounds/configchange/configchange.wav +0 -0
- package/cw/hooks/sounds/notification/notification.mp3 +0 -0
- package/cw/hooks/sounds/notification/notification.wav +0 -0
- package/cw/hooks/sounds/permissionrequest/permissionrequest.mp3 +0 -0
- package/cw/hooks/sounds/permissionrequest/permissionrequest.wav +0 -0
- package/cw/hooks/sounds/posttooluse/posttooluse.mp3 +0 -0
- package/cw/hooks/sounds/posttooluse/posttooluse.wav +0 -0
- package/cw/hooks/sounds/posttoolusefailure/posttoolusefailure.mp3 +0 -0
- package/cw/hooks/sounds/posttoolusefailure/posttoolusefailure.wav +0 -0
- package/cw/hooks/sounds/precompact/precompact.mp3 +0 -0
- package/cw/hooks/sounds/precompact/precompact.wav +0 -0
- package/cw/hooks/sounds/pretooluse/pretooluse-git-committing.mp3 +0 -0
- package/cw/hooks/sounds/pretooluse/pretooluse-git-committing.wav +0 -0
- package/cw/hooks/sounds/pretooluse/pretooluse.mp3 +0 -0
- package/cw/hooks/sounds/pretooluse/pretooluse.wav +0 -0
- package/cw/hooks/sounds/sessionend/sessionend.mp3 +0 -0
- package/cw/hooks/sounds/sessionend/sessionend.wav +0 -0
- package/cw/hooks/sounds/sessionstart/sessionstart.mp3 +0 -0
- package/cw/hooks/sounds/sessionstart/sessionstart.wav +0 -0
- package/cw/hooks/sounds/setup/Setup.mp3 +0 -0
- package/cw/hooks/sounds/setup/Setup.wav +0 -0
- package/cw/hooks/sounds/stop/stop.mp3 +0 -0
- package/cw/hooks/sounds/stop/stop.wav +0 -0
- package/cw/hooks/sounds/subagentstart/subagentstart.mp3 +0 -0
- package/cw/hooks/sounds/subagentstart/subagentstart.wav +0 -0
- package/cw/hooks/sounds/subagentstop/subagentstop.mp3 +0 -0
- package/cw/hooks/sounds/subagentstop/subagentstop.wav +0 -0
- package/cw/hooks/sounds/taskcompleted/taskcompleted.mp3 +0 -0
- package/cw/hooks/sounds/taskcompleted/taskcompleted.wav +0 -0
- package/cw/hooks/sounds/teammateidle/teammateidle.mp3 +0 -0
- package/cw/hooks/sounds/teammateidle/teammateidle.wav +0 -0
- package/cw/hooks/sounds/userpromptsubmit/userpromptsubmit.mp3 +0 -0
- package/cw/hooks/sounds/userpromptsubmit/userpromptsubmit.wav +0 -0
- package/cw/hooks/sounds/worktreecreate/worktreecreate.mp3 +0 -0
- package/cw/hooks/sounds/worktreecreate/worktreecreate.wav +0 -0
- package/cw/hooks/sounds/worktreeremove/worktreeremove.mp3 +0 -0
- package/cw/hooks/sounds/worktreeremove/worktreeremove.wav +0 -0
- package/cw/install.sh +243 -0
- package/cw/lib/context/github.sh +51 -0
- package/cw/lib/context/linear.sh +49 -0
- package/cw/lib/context/notion.sh +38 -0
- package/cw/lib/dashboard/activity-hook.py +134 -0
- package/cw/lib/dashboard/index.html +1515 -0
- package/cw/lib/dashboard/server.py +379 -0
- package/cw/lib/dashboard/start.sh +15 -0
- package/cw/lib/harnesses/claude.sh +92 -0
- package/cw/lib/harnesses/codex.sh +245 -0
- package/cw/lib/harnesses/opencode.sh +120 -0
- package/cw/lib/harnesses/pi.sh +66 -0
- package/cw/lib/iterm2.sh +140 -0
- package/cw/templates/CLAUDE.template.md +48 -0
- package/dist/index.js +4376 -0
- package/package.json +51 -0
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# CW v3 Shell Integration — source from .zshrc / .bashrc
|
|
3
|
+
|
|
4
|
+
export CW_HOME="${CW_HOME:-$HOME/.cw}"
|
|
5
|
+
export PATH="$CW_HOME/bin:$PATH"
|
|
6
|
+
|
|
7
|
+
# ── Account aliases ─────────────────────────────────────────────────────────
|
|
8
|
+
_cw_load_aliases() {
|
|
9
|
+
[[ -d "$CW_HOME/accounts" ]] || return
|
|
10
|
+
for d in "$CW_HOME/accounts"/*/; do
|
|
11
|
+
[[ -d "$d" ]] || continue
|
|
12
|
+
local n; n="$(basename "$d")"
|
|
13
|
+
local cdir="$d"
|
|
14
|
+
[[ -d "$d/claude" ]] && cdir="$d/claude"
|
|
15
|
+
alias "claude-$n"="CLAUDE_CONFIG_DIR=${cdir} claude"
|
|
16
|
+
done
|
|
17
|
+
}
|
|
18
|
+
_cw_load_aliases
|
|
19
|
+
|
|
20
|
+
# ── Quick aliases ───────────────────────────────────────────────────────────
|
|
21
|
+
alias cwd='cw dashboard'
|
|
22
|
+
alias cws='cw status'
|
|
23
|
+
alias cwrl='cw review --list' # list active reviews
|
|
24
|
+
alias cwsp='cw spaces' # show active spaces
|
|
25
|
+
alias cwl='cw project list'
|
|
26
|
+
|
|
27
|
+
# ── Mode shortcuts: cwc = code, cwr = review, etc. ─────────────────────────
|
|
28
|
+
cwc() { cw open "$1" --mode code "${@:2}"; }
|
|
29
|
+
cwr() { cw open "$1" --mode review "${@:2}"; }
|
|
30
|
+
cwpr() { cw review "$@"; } # PR review con worktree
|
|
31
|
+
cww() { cw work "$@"; } # work task con worktree
|
|
32
|
+
cwi() { cw open "$1" --mode research "${@:2}"; }
|
|
33
|
+
cwdoc(){ cw open "$1" --mode docs "${@:2}"; }
|
|
34
|
+
cwp() { cw open "$1" --mode planning "${@:2}"; }
|
|
35
|
+
cwm() { cw open "$1" --mode comms "${@:2}"; }
|
|
36
|
+
cwf() { cw open "$1" --mode full "${@:2}"; }
|
|
37
|
+
|
|
38
|
+
# ── Quick launch ────────────────────────────────────────────────────────────
|
|
39
|
+
cc() { cw launch "${1:-work}" "${@:2}"; }
|
|
40
|
+
|
|
41
|
+
# ── Fuzzy project opener (requires fzf) ─────────────────────────────────────
|
|
42
|
+
cwo() {
|
|
43
|
+
command -v fzf &>/dev/null || { echo "Requires fzf"; return 1; }
|
|
44
|
+
[[ -f "$CW_HOME/projects.json" ]] || { echo "No projects found"; return 1; }
|
|
45
|
+
local project; project=$(python3 -c "
|
|
46
|
+
import json
|
|
47
|
+
with open('$CW_HOME/projects.json') as f:
|
|
48
|
+
for n in json.load(f): print(n)
|
|
49
|
+
" 2>/dev/null | fzf --prompt="Project ❯ " --height=10)
|
|
50
|
+
[[ -n "$project" ]] && cw open "$project" "$@"
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
# ── Prompt info (for custom prompts) ────────────────────────────────────────
|
|
54
|
+
cw_prompt_info() {
|
|
55
|
+
[[ -n "${CLAUDE_CONFIG_DIR:-}" ]] && echo " [claude:$(basename "$CLAUDE_CONFIG_DIR")]"
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
# ── Bash completions ────────────────────────────────────────────────────────
|
|
59
|
+
if [[ -n "${BASH_VERSION:-}" ]]; then
|
|
60
|
+
_cw_comp() {
|
|
61
|
+
local cur="${COMP_WORDS[COMP_CWORD]}" prev="${COMP_WORDS[COMP_CWORD-1]}"
|
|
62
|
+
case "$prev" in
|
|
63
|
+
cw) COMPREPLY=($(compgen -W "init account project open work review plan spaces launch dashboard status stats doctor harness help" -- "$cur")) ;;
|
|
64
|
+
account) COMPREPLY=($(compgen -W "add list remove login migrate" -- "$cur")) ;;
|
|
65
|
+
harness) COMPREPLY=($(compgen -W "list doctor" -- "$cur")) ;;
|
|
66
|
+
project) COMPREPLY=($(compgen -W "register list scaffold setup-mcps setup-agents info" -- "$cur")) ;;
|
|
67
|
+
open|info|setup-mcps|setup-agents)
|
|
68
|
+
[[ -f "$CW_HOME/projects.json" ]] && \
|
|
69
|
+
COMPREPLY=($(compgen -W "$(python3 -c "import json;[print(n) for n in json.load(open('$CW_HOME/projects.json'))]" 2>/dev/null)" -- "$cur")) ;;
|
|
70
|
+
work)
|
|
71
|
+
if [[ $COMP_CWORD -eq 2 ]]; then
|
|
72
|
+
[[ -f "$CW_HOME/projects.json" ]] && \
|
|
73
|
+
COMPREPLY=($(compgen -W "$(python3 -c "import json;[print(n) for n in json.load(open('$CW_HOME/projects.json'))]" 2>/dev/null)" -- "$cur"))
|
|
74
|
+
elif [[ $COMP_CWORD -eq 3 ]]; then
|
|
75
|
+
local proj="${COMP_WORDS[2]}"
|
|
76
|
+
[[ -d "$CW_HOME/sessions/$proj" ]] && \
|
|
77
|
+
COMPREPLY=($(compgen -W "$(for d in $CW_HOME/sessions/$proj/task-*/session.json; do [[ -f "$d" ]] && python3 -c "import json; m=json.load(open('$d')); m.get('status')=='active' and print(m.get('task',''))" 2>/dev/null; done)" -- "$cur"))
|
|
78
|
+
fi ;;
|
|
79
|
+
review)
|
|
80
|
+
if [[ $COMP_CWORD -eq 2 ]]; then
|
|
81
|
+
[[ -f "$CW_HOME/projects.json" ]] && \
|
|
82
|
+
COMPREPLY=($(compgen -W "$(python3 -c "import json;[print(n) for n in json.load(open('$CW_HOME/projects.json'))]" 2>/dev/null)" -- "$cur"))
|
|
83
|
+
elif [[ $COMP_CWORD -eq 3 ]]; then
|
|
84
|
+
local proj="${COMP_WORDS[2]}"
|
|
85
|
+
[[ -d "$CW_HOME/sessions/$proj" ]] && \
|
|
86
|
+
COMPREPLY=($(compgen -W "$(for d in $CW_HOME/sessions/$proj/review-*/session.json; do [[ -f "$d" ]] && python3 -c "import json; m=json.load(open('$d')); m.get('status')=='active' and print(m.get('pr',''))" 2>/dev/null; done)" -- "$cur"))
|
|
87
|
+
fi ;;
|
|
88
|
+
launch|add)
|
|
89
|
+
[[ -d "$CW_HOME/accounts" ]] && \
|
|
90
|
+
COMPREPLY=($(compgen -W "$(ls "$CW_HOME/accounts" 2>/dev/null)" -- "$cur")) ;;
|
|
91
|
+
--mode|-m) COMPREPLY=($(compgen -W "code review research docs planning comms full" -- "$cur")) ;;
|
|
92
|
+
--task|-t)
|
|
93
|
+
# Complete with active tasks for the project
|
|
94
|
+
local proj="${COMP_WORDS[2]}"
|
|
95
|
+
[[ -d "$CW_HOME/sessions/$proj" ]] && \
|
|
96
|
+
COMPREPLY=($(compgen -W "$(for d in $CW_HOME/sessions/$proj/task-*/session.json; do [[ -f "$d" ]] && python3 -c "import json; m=json.load(open('$d')); m.get('status')=='active' and print(m.get('task',''))" 2>/dev/null; done)" -- "$cur")) ;;
|
|
97
|
+
--pr)
|
|
98
|
+
local proj="${COMP_WORDS[2]}"
|
|
99
|
+
[[ -d "$CW_HOME/sessions/$proj" ]] && \
|
|
100
|
+
COMPREPLY=($(compgen -W "$(for d in $CW_HOME/sessions/$proj/review-*/session.json; do [[ -f "$d" ]] && python3 -c "import json; m=json.load(open('$d')); m.get('status')=='active' and print(m.get('pr',''))" 2>/dev/null; done)" -- "$cur")) ;;
|
|
101
|
+
--type|-t) COMPREPLY=($(compgen -W "fullstack api knowledge infra agents" -- "$cur")) ;;
|
|
102
|
+
--workflow|-w) COMPREPLY=($(compgen -W "$(ls "$CW_HOME/templates/workflows/" 2>/dev/null | sed 's/\.md$//')" -- "$cur")) ;;
|
|
103
|
+
--account|-a) [[ -d "$CW_HOME/accounts" ]] && COMPREPLY=($(compgen -W "$(ls "$CW_HOME/accounts" 2>/dev/null)" -- "$cur")) ;;
|
|
104
|
+
--harness|-H) COMPREPLY=($(compgen -W "claude codex pi opencode" -- "$cur")) ;;
|
|
105
|
+
esac
|
|
106
|
+
}
|
|
107
|
+
complete -F _cw_comp cw
|
|
108
|
+
fi
|
|
109
|
+
|
|
110
|
+
# ── Zsh completions ─────────────────────────────────────────────────────────
|
|
111
|
+
if [[ -n "${ZSH_VERSION:-}" ]]; then
|
|
112
|
+
_cw_comp_zsh() {
|
|
113
|
+
local -a cmds=('init' 'account' 'project' 'open' 'work' 'review' 'plan' 'spaces' 'launch' 'dashboard' 'status' 'stats' 'doctor' 'harness' 'help')
|
|
114
|
+
_arguments '1:command:($cmds)' '*::arg:->args'
|
|
115
|
+
case "$state" in
|
|
116
|
+
args)
|
|
117
|
+
case "${words[1]}" in
|
|
118
|
+
account) _values 'sub' add list remove login migrate ;;
|
|
119
|
+
project) _values 'sub' register list scaffold setup-mcps setup-agents info ;;
|
|
120
|
+
harness) _values 'sub' list doctor ;;
|
|
121
|
+
open|work|review)
|
|
122
|
+
[[ -f "$CW_HOME/projects.json" ]] && {
|
|
123
|
+
local -a projs=($(python3 -c "import json;[print(n) for n in json.load(open('$CW_HOME/projects.json'))]" 2>/dev/null))
|
|
124
|
+
_values 'project' $projs
|
|
125
|
+
} ;;
|
|
126
|
+
esac ;;
|
|
127
|
+
esac
|
|
128
|
+
}
|
|
129
|
+
if (( $+functions[compdef] )); then
|
|
130
|
+
compdef _cw_comp_zsh cw
|
|
131
|
+
fi
|
|
132
|
+
fi
|
|
@@ -0,0 +1,471 @@
|
|
|
1
|
+
# HOOKS-README
|
|
2
|
+
contains all the details, scripts, and instructions for the hooks
|
|
3
|
+
|
|
4
|
+
## Hook Events Overview - [Official 18 Hooks](https://code.claude.com/docs/en/hooks)
|
|
5
|
+
Claude Code provides several hook events that run at different points in the workflow:
|
|
6
|
+
|
|
7
|
+
| # | Hook | Description | Options |
|
|
8
|
+
|:-:|------|-------------|---------|
|
|
9
|
+
| 1 | `PreToolUse` | Runs before tool calls (can block them) | `async`, `timeout: 5000` |
|
|
10
|
+
| 2 | `PermissionRequest` | Runs when Claude Code requests permission from the user | `async`, `timeout: 5000`, `permission_suggestions` |
|
|
11
|
+
| 3 | `PostToolUse` | Runs after tool calls complete successfully | `async`, `timeout: 5000`, `tool_response` |
|
|
12
|
+
| 4 | `PostToolUseFailure` | Runs after tool calls fail | `async`, `timeout: 5000`, `error`, `is_interrupt` |
|
|
13
|
+
| 5 | `UserPromptSubmit` | Runs when the user submits a prompt, before Claude processes it | `async`, `timeout: 5000`, `prompt` |
|
|
14
|
+
| 6 | `Notification` | Runs when Claude Code sends notifications | `async`, `timeout: 5000`, `notification_type`, `message`, `title` |
|
|
15
|
+
| 7 | `Stop` | Runs when Claude Code finishes responding | `async`, `timeout: 5000`, `last_assistant_message`, `stop_hook_active` |
|
|
16
|
+
| 8 | `SubagentStart` | Runs when subagent tasks start | `async`, `timeout: 5000`, `agent_id`, `agent_type` |
|
|
17
|
+
| 9 | `SubagentStop` | Runs when subagent tasks complete | `async`, `timeout: 5000`, `agent_id`, `agent_type`, `last_assistant_message`, `agent_transcript_path`, `stop_hook_active` |
|
|
18
|
+
| 10 | `PreCompact` | Runs before Claude Code is about to run a compact operation | `async`, `timeout: 5000`, `once`, `trigger`, `custom_instructions` |
|
|
19
|
+
| 11 | `SessionStart` | Runs when Claude Code starts a new session or resumes an existing session | `async`, `timeout: 5000`, `once`, `agent_type`, `model`, `source` |
|
|
20
|
+
| 12 | `SessionEnd` | Runs when Claude Code session ends | `async`, `timeout: 5000`, `once`, `reason` |
|
|
21
|
+
| 13 | `Setup` | Runs when Claude Code runs the /setup command for project initialization | `async`, `timeout: 30000` |
|
|
22
|
+
| 14 | `TeammateIdle` | Runs when a teammate agent becomes idle (experimental agent teams) | `async`, `timeout: 5000`, `teammate_name`, `team_name` |
|
|
23
|
+
| 15 | `TaskCompleted` | Runs when a background task completes (experimental agent teams) | `async`, `timeout: 5000`, `task_id`, `task_subject`, `task_description`, `teammate_name`, `team_name` |
|
|
24
|
+
| 16 | `ConfigChange` | Runs when a configuration file changes during a session | `async`, `timeout: 5000`, `file_path`, `source` |
|
|
25
|
+
| 17 | `WorktreeCreate` | Runs when agent worktree isolation creates worktrees for custom VCS setup | `async`, `timeout: 5000`, `name` |
|
|
26
|
+
| 18 | `WorktreeRemove` | Runs when agent worktree isolation removes worktrees for custom VCS teardown | `async`, `timeout: 5000`, `worktree_path` |
|
|
27
|
+
|
|
28
|
+
> **Note:** Hooks 14-15 (`TeammateIdle` and `TaskCompleted`) require the experimental agent teams feature. Set `CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1` when launching Claude Code to enable them.
|
|
29
|
+
|
|
30
|
+
### Not in Official Docs
|
|
31
|
+
|
|
32
|
+
The following items exist in the [Claude Code Changelog](https://github.com/anthropics/claude-code/blob/main/CHANGELOG.md) but are **not listed** in the [Official Hooks Reference](https://code.claude.com/docs/en/hooks):
|
|
33
|
+
|
|
34
|
+
| Item | Added In | Changelog Reference | Notes |
|
|
35
|
+
|------|----------|-------------------|-------|
|
|
36
|
+
| `Setup` hook | [v2.1.10](https://github.com/anthropics/claude-code/blob/main/CHANGELOG.md#2110) | "Added new Setup hook event that can be triggered via `--init`, `--init-only`, or `--maintenance` CLI flags for repository setup and maintenance operations" | Not listed in official hooks reference page (17 hooks listed, Setup excluded) |
|
|
37
|
+
| Agent frontmatter hooks | [v2.1.0](https://github.com/anthropics/claude-code/blob/main/CHANGELOG.md#210) | "Added hooks support to agent frontmatter, allowing agents to define PreToolUse, PostToolUse, and Stop hooks scoped to the agent's lifecycle" | Changelog only mentions 3 hooks, but testing confirms **6 hooks** actually fire in agent sessions: PreToolUse, PostToolUse, PermissionRequest, PostToolUseFailure, Stop, SubagentStop. Not all 16 hooks are supported. |
|
|
38
|
+
|
|
39
|
+
## Prerequisites
|
|
40
|
+
|
|
41
|
+
Before using hooks, ensure you have **Python 3** installed on your system.
|
|
42
|
+
|
|
43
|
+
### Required Software
|
|
44
|
+
|
|
45
|
+
#### All Platforms (Windows, macOS, Linux)
|
|
46
|
+
- **Python 3**: Required for running the hook scripts
|
|
47
|
+
- Verify installation: `python3 --version`
|
|
48
|
+
|
|
49
|
+
**Installation Instructions:**
|
|
50
|
+
- **Windows**: Download from [python.org](https://www.python.org/downloads/) or install via `winget install Python.Python.3`
|
|
51
|
+
- **macOS**: Install via `brew install python3` (requires [Homebrew](https://brew.sh/))
|
|
52
|
+
- **Linux**: Install via `sudo apt install python3` (Ubuntu/Debian) or `sudo yum install python3` (RHEL/CentOS)
|
|
53
|
+
|
|
54
|
+
### Audio Players (Optional - Automatically Detected)
|
|
55
|
+
|
|
56
|
+
The hook scripts automatically detect and use the appropriate audio player for your platform:
|
|
57
|
+
|
|
58
|
+
- **macOS**: Uses `afplay` (built-in, no installation needed)
|
|
59
|
+
- **Linux**: Uses `paplay` from `pulseaudio-utils` - install via `sudo apt install pulseaudio-utils`
|
|
60
|
+
- **Windows**: Uses built-in `winsound` module (included with Python)
|
|
61
|
+
|
|
62
|
+
### How Hooks Are Executed
|
|
63
|
+
|
|
64
|
+
The hooks are configured in `.claude/settings.json` to run directly with Python 3:
|
|
65
|
+
|
|
66
|
+
```json
|
|
67
|
+
{
|
|
68
|
+
"type": "command",
|
|
69
|
+
"command": "python3 .claude/hooks/scripts/hooks.py"
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Configuring Hooks (Enable/Disable)
|
|
74
|
+
|
|
75
|
+
Hooks can be easily enabled or disabled at both the global and individual levels.
|
|
76
|
+
|
|
77
|
+
### Disable All Hooks at Once
|
|
78
|
+
|
|
79
|
+
Edit `.claude/settings.local.json` and set:
|
|
80
|
+
```json
|
|
81
|
+
{
|
|
82
|
+
"disableAllHooks": true
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
**Note:** The `.claude/settings.local.json` file is git-ignored, so each user can configure their own hook preferences without affecting the team's shared settings in `.claude/settings.json`.
|
|
87
|
+
|
|
88
|
+
> **Managed Settings:** If an administrator has configured hooks through managed policy settings, `disableAllHooks` set in user, project, or local settings cannot disable those managed hooks (fixed in v2.1.49).
|
|
89
|
+
|
|
90
|
+
### Disable Individual Hooks
|
|
91
|
+
|
|
92
|
+
For granular control, you can disable specific hooks by editing the hooks configuration files.
|
|
93
|
+
|
|
94
|
+
#### Configuration Files
|
|
95
|
+
|
|
96
|
+
There are two configuration files for managing individual hooks:
|
|
97
|
+
|
|
98
|
+
1. **`.claude/hooks/config/hooks-config.json`** - The shared/default configuration that is committed to git
|
|
99
|
+
2. **`.claude/hooks/config/hooks-config.local.json`** - Your personal overrides (git-ignored)
|
|
100
|
+
|
|
101
|
+
The local config file (`.local.json`) takes precedence over the shared config, allowing each developer to customize their hook behavior without affecting the team.
|
|
102
|
+
|
|
103
|
+
#### Shared Configuration
|
|
104
|
+
|
|
105
|
+
Edit `.claude/hooks/config/hooks-config.json` for team-wide defaults:
|
|
106
|
+
|
|
107
|
+
```json
|
|
108
|
+
{
|
|
109
|
+
"disableLogging": false,
|
|
110
|
+
"disablePreToolUseHook": false,
|
|
111
|
+
"disablePermissionRequestHook": false,
|
|
112
|
+
"disablePostToolUseHook": false,
|
|
113
|
+
"disablePostToolUseFailureHook": false,
|
|
114
|
+
"disableUserPromptSubmitHook": false,
|
|
115
|
+
"disableNotificationHook": false,
|
|
116
|
+
"disableStopHook": false,
|
|
117
|
+
"disableSubagentStartHook": false,
|
|
118
|
+
"disableSubagentStopHook": false,
|
|
119
|
+
"disablePreCompactHook": false,
|
|
120
|
+
"disableSessionStartHook": false,
|
|
121
|
+
"disableSessionEndHook": false,
|
|
122
|
+
"disableSetupHook": false,
|
|
123
|
+
"disableTeammateIdleHook": false,
|
|
124
|
+
"disableTaskCompletedHook": false,
|
|
125
|
+
"disableConfigChangeHook": false,
|
|
126
|
+
"disableWorktreeCreateHook": false,
|
|
127
|
+
"disableWorktreeRemoveHook": false
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
**Configuration Options:**
|
|
132
|
+
- `disableLogging`: Set to `true` to disable logging hook events to `.claude/hooks/logs/hooks-log.jsonl` (useful to prevent log file growth)
|
|
133
|
+
|
|
134
|
+
#### Local Configuration (Personal Overrides)
|
|
135
|
+
|
|
136
|
+
Create or edit `.claude/hooks/config/hooks-config.local.json` for personal preferences:
|
|
137
|
+
|
|
138
|
+
```json
|
|
139
|
+
{
|
|
140
|
+
"disableLogging": true,
|
|
141
|
+
"disablePostToolUseHook": true,
|
|
142
|
+
"disableSessionStartHook": true
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
In this example, logging is disabled, and the PostToolUse and SessionStart hooks are overridden locally. All other hooks will use the shared configuration values.
|
|
147
|
+
|
|
148
|
+
**Note:** Individual hook toggles are checked by the hook script (`.claude/hooks/scripts/hooks.py`). Local settings override shared settings, and if a hook is disabled, the script exits silently without playing any sounds or executing hook logic.
|
|
149
|
+
|
|
150
|
+
### Text to Speech (TTS)
|
|
151
|
+
website used to generate sounds: https://elevenlabs.io/
|
|
152
|
+
voice used: Samara X
|
|
153
|
+
|
|
154
|
+
## Agent Frontmatter Hooks
|
|
155
|
+
|
|
156
|
+
Claude Code 2.1.0 introduced support for agent-specific hooks defined in agent frontmatter files. These hooks only run within the agent's lifecycle and support a subset of hook events.
|
|
157
|
+
|
|
158
|
+
### Supported Agent Hooks
|
|
159
|
+
|
|
160
|
+
Agent frontmatter hooks support **6 hooks** (not all 16). The changelog originally mentioned only 3, but testing confirms 6 hooks actually fire in agent sessions:
|
|
161
|
+
- `PreToolUse`: Runs before the agent uses a tool
|
|
162
|
+
- `PostToolUse`: Runs after the agent completes a tool use
|
|
163
|
+
- `PermissionRequest`: Runs when a tool requires user permission
|
|
164
|
+
- `PostToolUseFailure`: Runs after a tool call fails
|
|
165
|
+
- `Stop`: Runs when the agent finishes
|
|
166
|
+
- `SubagentStop`: Runs when a subagent completes
|
|
167
|
+
|
|
168
|
+
> **Note:** The [v2.1.0 changelog](https://github.com/anthropics/claude-code/blob/main/CHANGELOG.md#210) only mentions 3 hooks: *"Added hooks support to agent frontmatter, allowing agents to define PreToolUse, PostToolUse, and Stop hooks scoped to the agent's lifecycle"*. However, testing with the `claude-code-voice-hook-agent` confirms that 6 hooks actually fire in agent sessions. The remaining 10 hooks (e.g., Notification, SessionStart, SessionEnd, etc.) do not fire in agent contexts.
|
|
169
|
+
>
|
|
170
|
+
> **Update (Feb 2026):** The [official hooks reference](https://code.claude.com/docs/en/hooks) now states *"All hook events are supported"* for skill/agent frontmatter hooks. This may mean support has expanded beyond the 6 hooks originally tested. Re-testing recommended to verify if additional hooks now fire in agent sessions.
|
|
171
|
+
|
|
172
|
+
### Agent Sound Folders
|
|
173
|
+
|
|
174
|
+
Agent-specific sounds are stored in separate folders:
|
|
175
|
+
- `.claude/hooks/sounds/agent_pretooluse/`
|
|
176
|
+
- `.claude/hooks/sounds/agent_posttooluse/`
|
|
177
|
+
- `.claude/hooks/sounds/agent_permissionrequest/`
|
|
178
|
+
- `.claude/hooks/sounds/agent_posttoolusefailure/`
|
|
179
|
+
- `.claude/hooks/sounds/agent_stop/`
|
|
180
|
+
- `.claude/hooks/sounds/agent_subagentstop/`
|
|
181
|
+
|
|
182
|
+
### Creating an Agent with Hooks
|
|
183
|
+
|
|
184
|
+
1. Create an agent definition file in `.claude/agents/`:
|
|
185
|
+
|
|
186
|
+
```markdown
|
|
187
|
+
---
|
|
188
|
+
name: my-agent
|
|
189
|
+
description: Description of what this agent does
|
|
190
|
+
hooks:
|
|
191
|
+
PreToolUse:
|
|
192
|
+
- type: command
|
|
193
|
+
command: python3 ${CLAUDE_PROJECT_DIR}/.claude/hooks/scripts/hooks.py --agent=my-agent
|
|
194
|
+
timeout: 5000
|
|
195
|
+
async: true
|
|
196
|
+
statusMessage: PreToolUse
|
|
197
|
+
PostToolUse:
|
|
198
|
+
- type: command
|
|
199
|
+
command: python3 ${CLAUDE_PROJECT_DIR}/.claude/hooks/scripts/hooks.py --agent=my-agent
|
|
200
|
+
timeout: 5000
|
|
201
|
+
async: true
|
|
202
|
+
statusMessage: PostToolUse
|
|
203
|
+
PermissionRequest:
|
|
204
|
+
- type: command
|
|
205
|
+
command: python3 ${CLAUDE_PROJECT_DIR}/.claude/hooks/scripts/hooks.py --agent=my-agent
|
|
206
|
+
timeout: 5000
|
|
207
|
+
async: true
|
|
208
|
+
statusMessage: PermissionRequest
|
|
209
|
+
PostToolUseFailure:
|
|
210
|
+
- type: command
|
|
211
|
+
command: python3 ${CLAUDE_PROJECT_DIR}/.claude/hooks/scripts/hooks.py --agent=my-agent
|
|
212
|
+
timeout: 5000
|
|
213
|
+
async: true
|
|
214
|
+
statusMessage: PostToolUseFailure
|
|
215
|
+
Stop:
|
|
216
|
+
- type: command
|
|
217
|
+
command: python3 ${CLAUDE_PROJECT_DIR}/.claude/hooks/scripts/hooks.py --agent=my-agent
|
|
218
|
+
timeout: 5000
|
|
219
|
+
async: true
|
|
220
|
+
statusMessage: Stop
|
|
221
|
+
SubagentStop:
|
|
222
|
+
- type: command
|
|
223
|
+
command: python3 ${CLAUDE_PROJECT_DIR}/.claude/hooks/scripts/hooks.py --agent=my-agent
|
|
224
|
+
timeout: 5000
|
|
225
|
+
async: true
|
|
226
|
+
statusMessage: SubagentStop
|
|
227
|
+
---
|
|
228
|
+
|
|
229
|
+
Your agent instructions here...
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
2. Add sound files to the agent sound folders:
|
|
233
|
+
- `agent_pretooluse/agent_pretooluse.wav`
|
|
234
|
+
- `agent_posttooluse/agent_posttooluse.wav`
|
|
235
|
+
- `agent_permissionrequest/agent_permissionrequest.wav`
|
|
236
|
+
- `agent_posttoolusefailure/agent_posttoolusefailure.wav`
|
|
237
|
+
- `agent_stop/agent_stop.wav`
|
|
238
|
+
- `agent_subagentstop/agent_subagentstop.wav`
|
|
239
|
+
|
|
240
|
+
### Example: Weather Fetcher Agent
|
|
241
|
+
|
|
242
|
+
See `.claude/agents/claude-code-voice-hook-agent.md` for a complete example of an agent with hooks configured.
|
|
243
|
+
|
|
244
|
+
### Hook Option: `once: true`
|
|
245
|
+
|
|
246
|
+
The `once: true` option ensures a hook only runs once per session:
|
|
247
|
+
|
|
248
|
+
```json
|
|
249
|
+
{
|
|
250
|
+
"type": "command",
|
|
251
|
+
"command": "python3 .claude/hooks/scripts/hooks.py",
|
|
252
|
+
"timeout": 5000,
|
|
253
|
+
"once": true
|
|
254
|
+
}
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
This is useful for hooks like `SessionStart`, `SessionEnd`, and `PreCompact` that should only trigger once.
|
|
258
|
+
|
|
259
|
+
> **Note:** The `once` option is for **skills only, not agents**. It works in settings-based hooks and skill frontmatter, but is not supported in agent frontmatter hooks.
|
|
260
|
+
|
|
261
|
+
### Hook Option: `async: true`
|
|
262
|
+
|
|
263
|
+
Hooks can run in the background without blocking Claude Code's execution by adding `"async": true`:
|
|
264
|
+
|
|
265
|
+
```json
|
|
266
|
+
{
|
|
267
|
+
"type": "command",
|
|
268
|
+
"command": "python3 .claude/hooks/scripts/hooks.py",
|
|
269
|
+
"timeout": 5000,
|
|
270
|
+
"async": true
|
|
271
|
+
}
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
**When to use async hooks:**
|
|
275
|
+
- Logging and analytics
|
|
276
|
+
- Notifications and sound effects
|
|
277
|
+
- Any side-effect that shouldn't slow down Claude Code
|
|
278
|
+
|
|
279
|
+
This project uses `async: true` for all hooks since voice notifications are side-effects that don't need to block execution. The `timeout` specifies how long the async hook can run before being terminated.
|
|
280
|
+
|
|
281
|
+
### Hook Option: `statusMessage`
|
|
282
|
+
|
|
283
|
+
The `statusMessage` field sets a custom spinner message displayed to the user while the hook is running:
|
|
284
|
+
|
|
285
|
+
```json
|
|
286
|
+
{
|
|
287
|
+
"type": "command",
|
|
288
|
+
"command": "python3 .claude/hooks/scripts/hooks.py",
|
|
289
|
+
"timeout": 5000,
|
|
290
|
+
"async": true,
|
|
291
|
+
"statusMessage": "PreToolUse"
|
|
292
|
+
}
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
This project sets `statusMessage` to the hook event name on all hooks, so the spinner briefly shows which hook is firing (e.g., "PreToolUse", "SessionStart", "Stop"). This is most visible for synchronous hooks; for async hooks the message flashes briefly before the hook runs in the background.
|
|
296
|
+
|
|
297
|
+
## Hook Types
|
|
298
|
+
|
|
299
|
+
Claude Code supports three hook handler types. This project uses `command` hooks for all sound playback.
|
|
300
|
+
|
|
301
|
+
### `type: "command"` (used by this project)
|
|
302
|
+
|
|
303
|
+
Runs a shell command. Receives JSON input via stdin, communicates results through exit codes and stdout.
|
|
304
|
+
|
|
305
|
+
```json
|
|
306
|
+
{
|
|
307
|
+
"type": "command",
|
|
308
|
+
"command": "python3 .claude/hooks/scripts/hooks.py",
|
|
309
|
+
"timeout": 5000,
|
|
310
|
+
"async": true
|
|
311
|
+
}
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
### `type: "prompt"`
|
|
315
|
+
|
|
316
|
+
Sends a prompt to a Claude model for single-turn evaluation. The model returns a yes/no decision as JSON (`{"ok": true/false, "reason": "..."}`). Useful for decisions that require judgment rather than deterministic rules.
|
|
317
|
+
|
|
318
|
+
```json
|
|
319
|
+
{
|
|
320
|
+
"type": "prompt",
|
|
321
|
+
"prompt": "Check if all tasks are complete. $ARGUMENTS",
|
|
322
|
+
"timeout": 30
|
|
323
|
+
}
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
**Supported events:** PreToolUse, PostToolUse, PostToolUseFailure, PermissionRequest, UserPromptSubmit, Stop, SubagentStop, TaskCompleted. **Command-only events (not supported for prompt/agent types):** ConfigChange, Notification, PreCompact, SessionEnd, SessionStart, SubagentStart, TeammateIdle, WorktreeCreate, WorktreeRemove.
|
|
327
|
+
|
|
328
|
+
### `type: "agent"`
|
|
329
|
+
|
|
330
|
+
Spawns a subagent with multi-turn tool access (Read, Grep, Glob) to verify conditions before returning a decision. Same response format as prompt hooks. Useful when verification requires inspecting actual files or test output.
|
|
331
|
+
|
|
332
|
+
```json
|
|
333
|
+
{
|
|
334
|
+
"type": "agent",
|
|
335
|
+
"prompt": "Verify that all unit tests pass. $ARGUMENTS",
|
|
336
|
+
"timeout": 120
|
|
337
|
+
}
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
## Environment Variables
|
|
341
|
+
|
|
342
|
+
Claude Code provides these environment variables to hook scripts:
|
|
343
|
+
|
|
344
|
+
| Variable | Availability | Description |
|
|
345
|
+
|----------|-------------|-------------|
|
|
346
|
+
| `$CLAUDE_PROJECT_DIR` | All hooks | Project root directory. Wrap in quotes for paths with spaces |
|
|
347
|
+
| `$CLAUDE_ENV_FILE` | SessionStart only | File path for persisting environment variables for subsequent Bash commands. Use append (`>>`) to preserve variables from other hooks |
|
|
348
|
+
| `${CLAUDE_PLUGIN_ROOT}` | Plugin hooks | Plugin's root directory, for scripts bundled with a plugin |
|
|
349
|
+
| `$CLAUDE_CODE_REMOTE` | All hooks | Set to `"true"` in remote web environments, not set in local CLI |
|
|
350
|
+
| `session_id` (via stdin JSON) | All hooks | Current session ID, received as part of the JSON input on stdin (not an environment variable) |
|
|
351
|
+
|
|
352
|
+
### Common Input Fields (stdin JSON)
|
|
353
|
+
|
|
354
|
+
Every hook receives a JSON object on stdin containing these common fields, in addition to any hook-specific fields listed in the Options column above:
|
|
355
|
+
|
|
356
|
+
| Field | Type | Description |
|
|
357
|
+
|-------|------|-------------|
|
|
358
|
+
| `hook_event_name` | string | Name of the hook event that fired (e.g., `"PreToolUse"`, `"Stop"`) |
|
|
359
|
+
| `session_id` | string | Current session identifier |
|
|
360
|
+
| `transcript_path` | string | Path to the conversation transcript JSON file |
|
|
361
|
+
| `cwd` | string | Current working directory |
|
|
362
|
+
| `permission_mode` | string | Current permission mode: `default`, `plan`, `acceptEdits`, `dontAsk`, or `bypassPermissions` |
|
|
363
|
+
|
|
364
|
+
> **Note:** Hook-specific fields (e.g., `tool_name` for PreToolUse, `last_assistant_message` for Stop) are listed in the Options column of the [Hook Events Overview](#hook-events-overview---official-18-hooks) table above.
|
|
365
|
+
|
|
366
|
+
## Hooks Management Commands
|
|
367
|
+
|
|
368
|
+
Claude Code provides built-in commands for managing hooks:
|
|
369
|
+
|
|
370
|
+
- **`/hooks`** — Interactive hook management UI. View, add, and delete hooks without editing JSON files. Hooks are labeled by source: `[User]`, `[Project]`, `[Local]`, `[Plugin]`. You can also toggle `disableAllHooks` from this menu.
|
|
371
|
+
- **`claude hooks reload`** — Reload hooks configuration without restarting the session. Useful after editing settings files (since v2.0.47).
|
|
372
|
+
|
|
373
|
+
## MCP Tool Matchers
|
|
374
|
+
|
|
375
|
+
For `PreToolUse`, `PostToolUse`, and `PermissionRequest` hooks, you can match MCP (Model Context Protocol) tools using the pattern `mcp__<server>__<tool>`:
|
|
376
|
+
|
|
377
|
+
```json
|
|
378
|
+
{
|
|
379
|
+
"hooks": {
|
|
380
|
+
"PreToolUse": [{
|
|
381
|
+
"matcher": "mcp__memory__.*",
|
|
382
|
+
"hooks": [{ "type": "command", "command": "echo 'MCP memory tool used'" }]
|
|
383
|
+
}]
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
Full regex is supported: `mcp__memory__.*` (all tools from memory server), `mcp__.*__write.*` (any write tool from any server).
|
|
389
|
+
|
|
390
|
+
### Per-Hook Matcher Reference
|
|
391
|
+
|
|
392
|
+
Matchers filter which events trigger a hook. Not all hooks support matchers — hooks without matcher support always fire.
|
|
393
|
+
|
|
394
|
+
| Hook | Matcher Field | Possible Values | Example |
|
|
395
|
+
|------|--------------|-----------------|---------|
|
|
396
|
+
| `PreToolUse` | `tool_name` | Any tool name: `Bash`, `Read`, `Edit`, `Write`, `Glob`, `Grep`, `mcp__*` | `"matcher": "Bash"` |
|
|
397
|
+
| `PermissionRequest` | `tool_name` | Same as PreToolUse | `"matcher": "mcp__memory__.*"` |
|
|
398
|
+
| `PostToolUse` | `tool_name` | Same as PreToolUse | `"matcher": "Write"` |
|
|
399
|
+
| `PostToolUseFailure` | `tool_name` | Same as PreToolUse | `"matcher": "Bash"` |
|
|
400
|
+
| `Notification` | `notification_type` | `permission_prompt`, `idle_prompt`, `auth_success`, `elicitation_dialog` | `"matcher": "permission_prompt"` |
|
|
401
|
+
| `SubagentStart` | `agent_type` | `Bash`, `Explore`, `Plan`, or custom agent name | `"matcher": "Bash"` |
|
|
402
|
+
| `SubagentStop` | `agent_type` | `Bash`, `Explore`, `Plan`, or custom agent name | `"matcher": "Bash"` |
|
|
403
|
+
| `SessionStart` | `source` | `startup`, `resume`, `clear`, `compact` | `"matcher": "startup"` |
|
|
404
|
+
| `SessionEnd` | `reason` | `clear`, `logout`, `prompt_input_exit`, `bypass_permissions_disabled`, `other` | `"matcher": "logout"` |
|
|
405
|
+
| `PreCompact` | `trigger` | `manual`, `auto` | `"matcher": "auto"` |
|
|
406
|
+
| `ConfigChange` | `source` | `user_settings`, `project_settings`, `local_settings`, `policy_settings`, `skills` | `"matcher": "project_settings"` |
|
|
407
|
+
| `UserPromptSubmit` | — | No matcher support | Always fires |
|
|
408
|
+
| `Stop` | — | No matcher support | Always fires |
|
|
409
|
+
| `TeammateIdle` | — | No matcher support | Always fires |
|
|
410
|
+
| `TaskCompleted` | — | No matcher support | Always fires |
|
|
411
|
+
| `WorktreeCreate` | — | No matcher support | Always fires |
|
|
412
|
+
| `WorktreeRemove` | — | No matcher support | Always fires |
|
|
413
|
+
| `Setup` | — | No matcher support | Always fires |
|
|
414
|
+
|
|
415
|
+
## Known Issues & Workarounds
|
|
416
|
+
|
|
417
|
+
### Agent Stop Hook Bug (SubagentStop vs Stop)
|
|
418
|
+
|
|
419
|
+
**Bug Report:** [GitHub Issue #19220](https://github.com/anthropics/claude-code/issues/19220)
|
|
420
|
+
|
|
421
|
+
**Issue:** When defining a `Stop` hook in an agent's frontmatter, the `hook_event_name` passed to the hook script is `"SubagentStop"` instead of `"Stop"`. This contradicts the official documentation and breaks consistency with other agent hooks (`PreToolUse` and `PostToolUse`), which correctly pass their configured names.
|
|
422
|
+
|
|
423
|
+
| Hook | Defined As | Received As | Status |
|
|
424
|
+
|------|------------|-------------|--------|
|
|
425
|
+
| PreToolUse | `PreToolUse:` | `"PreToolUse"` | ✅ Correct |
|
|
426
|
+
| PostToolUse | `PostToolUse:` | `"PostToolUse"` | ✅ Correct |
|
|
427
|
+
| Stop | `Stop:` | `"SubagentStop"` | ❌ Inconsistent |
|
|
428
|
+
|
|
429
|
+
**Status:** The [official hooks reference](https://code.claude.com/docs/en/hooks#hooks-in-skills-and-agents) now documents this as expected behavior: *"For subagents, Stop hooks are automatically converted to SubagentStop since that is the event that fires when a subagent completes."* This project handles it via the `AGENT_HOOK_SOUND_MAP` in `hooks.py`, which has a separate `SubagentStop` entry that maps to the `agent_subagentstop` sound folder.
|
|
430
|
+
|
|
431
|
+
### PreToolUse Decision Control Deprecation
|
|
432
|
+
|
|
433
|
+
The `PreToolUse` hook previously used top-level `decision` and `reason` fields for blocking tool calls. These are now **deprecated**. Use `hookSpecificOutput.permissionDecision` and `hookSpecificOutput.permissionDecisionReason` instead:
|
|
434
|
+
|
|
435
|
+
| Deprecated | Current |
|
|
436
|
+
|-----------|---------|
|
|
437
|
+
| `"decision": "approve"` | `"hookSpecificOutput": { "permissionDecision": "allow" }` |
|
|
438
|
+
| `"decision": "block"` | `"hookSpecificOutput": { "permissionDecision": "deny" }` |
|
|
439
|
+
|
|
440
|
+
This does not affect this project since `hooks.py` uses async sound playback and does not use decision control.
|
|
441
|
+
|
|
442
|
+
## Decision Control Patterns
|
|
443
|
+
|
|
444
|
+
Different hooks use different output schemas for blocking or controlling execution. This project does not use decision control (all hooks are async sound playback), but for reference:
|
|
445
|
+
|
|
446
|
+
| Hook(s) | Control Method | Values |
|
|
447
|
+
|---------|---------------|--------|
|
|
448
|
+
| PreToolUse | `hookSpecificOutput.permissionDecision` | `allow`, `deny`, `ask` |
|
|
449
|
+
| PreToolUse | `hookSpecificOutput.autoAllow` | `true` — auto-approve future uses of this tool (since v2.0.76) |
|
|
450
|
+
| PermissionRequest | `hookSpecificOutput.decision.behavior` | `allow`, `deny` |
|
|
451
|
+
| PostToolUse, Stop, SubagentStop, ConfigChange | Top-level `decision` | `block` |
|
|
452
|
+
| TeammateIdle, TaskCompleted | Exit code 2 only | No JSON decision control |
|
|
453
|
+
| UserPromptSubmit | Can modify `prompt` field | Returns modified prompt via stdout |
|
|
454
|
+
| WorktreeCreate | Non-zero exit + stdout path | Non-zero exit fails creation; stdout provides worktree path |
|
|
455
|
+
|
|
456
|
+
### Universal JSON Output Fields
|
|
457
|
+
|
|
458
|
+
All hooks can return these fields via stdout JSON:
|
|
459
|
+
|
|
460
|
+
| Field | Type | Description |
|
|
461
|
+
|-------|------|-------------|
|
|
462
|
+
| `continue` | bool | If `false`, stops Claude entirely |
|
|
463
|
+
| `stopReason` | string | Message shown when `continue` is false |
|
|
464
|
+
| `suppressOutput` | bool | Hides stdout from verbose mode |
|
|
465
|
+
| `systemMessage` | string | Warning message shown to user |
|
|
466
|
+
| `additionalContext` | string | Context added to Claude's conversation |
|
|
467
|
+
|
|
468
|
+
## Hook Deduplication & External Changes
|
|
469
|
+
|
|
470
|
+
- **Hook deduplication:** Identical hook handlers defined in multiple settings locations run only once in parallel, preventing duplicate execution.
|
|
471
|
+
- **External change detection:** Claude Code warns when hooks are modified externally (e.g., by another process editing settings files) during an active session.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"disableSessionStartHook": false,
|
|
3
|
+
"disableUserPromptSubmitHook": false,
|
|
4
|
+
"disablePreToolUseHook": false,
|
|
5
|
+
"disablePostToolUseHook": false,
|
|
6
|
+
"disablePostToolUseFailureHook": false,
|
|
7
|
+
"disablePermissionRequestHook": false,
|
|
8
|
+
"disableNotificationHook": false,
|
|
9
|
+
"disableSubagentStartHook": false,
|
|
10
|
+
"disableSubagentStopHook": false,
|
|
11
|
+
"disableStopHook": false,
|
|
12
|
+
"disablePreCompactHook": false,
|
|
13
|
+
"disableSessionEndHook": false,
|
|
14
|
+
"disableSetupHook": false,
|
|
15
|
+
"disableTeammateIdleHook": false,
|
|
16
|
+
"disableTaskCompletedHook": false,
|
|
17
|
+
"disableConfigChangeHook": false,
|
|
18
|
+
"disableWorktreeCreateHook": false,
|
|
19
|
+
"disableWorktreeRemoveHook": false,
|
|
20
|
+
"disableLogging": true,
|
|
21
|
+
"disableAutoCompactHook": false,
|
|
22
|
+
"autoCompactThreshold": 100,
|
|
23
|
+
"autoCompactCooldown": 60
|
|
24
|
+
}
|