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.
Files changed (83) hide show
  1. package/LICENSE +21 -0
  2. package/console/assets/addon-fit-BlxrFPDK.js +1 -0
  3. package/console/assets/addon-web-links-BWjmmSgS.js +12 -0
  4. package/console/assets/index-Digctq9W.css +32 -0
  5. package/console/assets/index-ItVUTuQf.js +31 -0
  6. package/console/assets/xterm-DlVFs1Kw.js +9 -0
  7. package/console/index.html +13 -0
  8. package/cw/.bundle.json +4 -0
  9. package/cw/agents/presentation-curator.md +119 -0
  10. package/cw/agents/weather-agent.md +67 -0
  11. package/cw/cw +6188 -0
  12. package/cw/cw-shell-integration.sh +132 -0
  13. package/cw/hooks/HOOKS-README.md +471 -0
  14. package/cw/hooks/config/hooks-config.json +24 -0
  15. package/cw/hooks/scripts/auto-compact.py +133 -0
  16. package/cw/hooks/scripts/hooks.py +472 -0
  17. package/cw/hooks/scripts/review-autoclose.py +171 -0
  18. package/cw/hooks/sounds/agent_permissionrequest/agent_permissionrequest.mp3 +0 -0
  19. package/cw/hooks/sounds/agent_permissionrequest/agent_permissionrequest.wav +0 -0
  20. package/cw/hooks/sounds/agent_posttooluse/agent_posttooluse.mp3 +0 -0
  21. package/cw/hooks/sounds/agent_posttooluse/agent_posttooluse.wav +0 -0
  22. package/cw/hooks/sounds/agent_posttoolusefailure/agent_posttoolusefailure.mp3 +0 -0
  23. package/cw/hooks/sounds/agent_posttoolusefailure/agent_posttoolusefailure.wav +0 -0
  24. package/cw/hooks/sounds/agent_pretooluse/agent_pretooluse.mp3 +0 -0
  25. package/cw/hooks/sounds/agent_pretooluse/agent_pretooluse.wav +0 -0
  26. package/cw/hooks/sounds/agent_stop/agent_stop.mp3 +0 -0
  27. package/cw/hooks/sounds/agent_stop/agent_stop.wav +0 -0
  28. package/cw/hooks/sounds/agent_subagentstop/agent_subagentstop.mp3 +0 -0
  29. package/cw/hooks/sounds/agent_subagentstop/agent_subagentstop.wav +0 -0
  30. package/cw/hooks/sounds/configchange/configchange.mp3 +0 -0
  31. package/cw/hooks/sounds/configchange/configchange.wav +0 -0
  32. package/cw/hooks/sounds/notification/notification.mp3 +0 -0
  33. package/cw/hooks/sounds/notification/notification.wav +0 -0
  34. package/cw/hooks/sounds/permissionrequest/permissionrequest.mp3 +0 -0
  35. package/cw/hooks/sounds/permissionrequest/permissionrequest.wav +0 -0
  36. package/cw/hooks/sounds/posttooluse/posttooluse.mp3 +0 -0
  37. package/cw/hooks/sounds/posttooluse/posttooluse.wav +0 -0
  38. package/cw/hooks/sounds/posttoolusefailure/posttoolusefailure.mp3 +0 -0
  39. package/cw/hooks/sounds/posttoolusefailure/posttoolusefailure.wav +0 -0
  40. package/cw/hooks/sounds/precompact/precompact.mp3 +0 -0
  41. package/cw/hooks/sounds/precompact/precompact.wav +0 -0
  42. package/cw/hooks/sounds/pretooluse/pretooluse-git-committing.mp3 +0 -0
  43. package/cw/hooks/sounds/pretooluse/pretooluse-git-committing.wav +0 -0
  44. package/cw/hooks/sounds/pretooluse/pretooluse.mp3 +0 -0
  45. package/cw/hooks/sounds/pretooluse/pretooluse.wav +0 -0
  46. package/cw/hooks/sounds/sessionend/sessionend.mp3 +0 -0
  47. package/cw/hooks/sounds/sessionend/sessionend.wav +0 -0
  48. package/cw/hooks/sounds/sessionstart/sessionstart.mp3 +0 -0
  49. package/cw/hooks/sounds/sessionstart/sessionstart.wav +0 -0
  50. package/cw/hooks/sounds/setup/Setup.mp3 +0 -0
  51. package/cw/hooks/sounds/setup/Setup.wav +0 -0
  52. package/cw/hooks/sounds/stop/stop.mp3 +0 -0
  53. package/cw/hooks/sounds/stop/stop.wav +0 -0
  54. package/cw/hooks/sounds/subagentstart/subagentstart.mp3 +0 -0
  55. package/cw/hooks/sounds/subagentstart/subagentstart.wav +0 -0
  56. package/cw/hooks/sounds/subagentstop/subagentstop.mp3 +0 -0
  57. package/cw/hooks/sounds/subagentstop/subagentstop.wav +0 -0
  58. package/cw/hooks/sounds/taskcompleted/taskcompleted.mp3 +0 -0
  59. package/cw/hooks/sounds/taskcompleted/taskcompleted.wav +0 -0
  60. package/cw/hooks/sounds/teammateidle/teammateidle.mp3 +0 -0
  61. package/cw/hooks/sounds/teammateidle/teammateidle.wav +0 -0
  62. package/cw/hooks/sounds/userpromptsubmit/userpromptsubmit.mp3 +0 -0
  63. package/cw/hooks/sounds/userpromptsubmit/userpromptsubmit.wav +0 -0
  64. package/cw/hooks/sounds/worktreecreate/worktreecreate.mp3 +0 -0
  65. package/cw/hooks/sounds/worktreecreate/worktreecreate.wav +0 -0
  66. package/cw/hooks/sounds/worktreeremove/worktreeremove.mp3 +0 -0
  67. package/cw/hooks/sounds/worktreeremove/worktreeremove.wav +0 -0
  68. package/cw/install.sh +243 -0
  69. package/cw/lib/context/github.sh +51 -0
  70. package/cw/lib/context/linear.sh +49 -0
  71. package/cw/lib/context/notion.sh +38 -0
  72. package/cw/lib/dashboard/activity-hook.py +134 -0
  73. package/cw/lib/dashboard/index.html +1515 -0
  74. package/cw/lib/dashboard/server.py +379 -0
  75. package/cw/lib/dashboard/start.sh +15 -0
  76. package/cw/lib/harnesses/claude.sh +92 -0
  77. package/cw/lib/harnesses/codex.sh +245 -0
  78. package/cw/lib/harnesses/opencode.sh +120 -0
  79. package/cw/lib/harnesses/pi.sh +66 -0
  80. package/cw/lib/iterm2.sh +140 -0
  81. package/cw/templates/CLAUDE.template.md +48 -0
  82. package/dist/index.js +4376 -0
  83. 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
+ }