warhorn 1.0.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/README.md +177 -0
- package/assets/commands/warhorn/mute.md +14 -0
- package/assets/commands/warhorn/setup.md +314 -0
- package/assets/commands/warhorn/unmute.md +14 -0
- package/assets/scripts/generate_voices.py +512 -0
- package/assets/scripts/play-sound.sh +73 -0
- package/assets/sounds/Notification/bubble.wav +0 -0
- package/assets/sounds/Notification/ping.wav +0 -0
- package/assets/sounds/PermissionRequest/alert.wav +0 -0
- package/assets/sounds/PermissionRequest/doorbell.wav +0 -0
- package/assets/sounds/PostToolUse/.gitkeep +0 -0
- package/assets/sounds/PostToolUseFailure/buzz.wav +0 -0
- package/assets/sounds/PostToolUseFailure/sad.wav +0 -0
- package/assets/sounds/PostToolUseFailure/wonk.wav +0 -0
- package/assets/sounds/PreCompact/warning.wav +0 -0
- package/assets/sounds/PreToolUse/.gitkeep +0 -0
- package/assets/sounds/SessionEnd/shutdown.wav +0 -0
- package/assets/sounds/SessionStart/boot.wav +0 -0
- package/assets/sounds/SessionStart/ready.wav +0 -0
- package/assets/sounds/Stop/bell.wav +0 -0
- package/assets/sounds/Stop/chime.wav +0 -0
- package/assets/sounds/Stop/success.wav +0 -0
- package/assets/sounds/SubagentStart/spawn.wav +0 -0
- package/assets/sounds/SubagentStop/return.wav +0 -0
- package/assets/sounds/TaskCompleted/victory.wav +0 -0
- package/assets/sounds/TeammateIdle/.gitkeep +0 -0
- package/assets/sounds/UserPromptSubmit/tap.wav +0 -0
- package/assets/sounds/UserPromptSubmit/tick.wav +0 -0
- package/bin/install.js +184 -0
- package/bin/uninstall.js +148 -0
- package/package.json +41 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Alex Levadski
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
# WARHORN
|
|
4
|
+
|
|
5
|
+
**Sound notifications for [Claude Code](https://docs.anthropic.com/en/docs/claude-code)**
|
|
6
|
+
|
|
7
|
+
Never miss the end of a long task, a permission request, or a failed tool call again. Chimes, alerts, and AI voice lines — your choice.
|
|
8
|
+
|
|
9
|
+
<br>
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npx warhorn
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
<br>
|
|
16
|
+
|
|
17
|
+
</div>
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## What is warhorn?
|
|
22
|
+
|
|
23
|
+
Claude Code runs in the terminal — no visual cues, no push notifications, no sound. If you step away or switch tabs during a long task, you have no idea when it's done, when it needs approval, or when something breaks.
|
|
24
|
+
|
|
25
|
+
warhorn fixes that. It hooks into Claude Code's lifecycle events and plays sounds when things happen:
|
|
26
|
+
|
|
27
|
+
- **Instrumental chimes** — bundled WAV files that work immediately, no setup needed
|
|
28
|
+
- **AI voice lines** — short, punchy character voice clips generated via [edge-tts](https://github.com/rany2/edge-tts) (free Microsoft TTS)
|
|
29
|
+
- **Both** — instrumentals and voice lines mixed randomly per hook
|
|
30
|
+
- **Fully customizable** — pick which hooks trigger sounds, choose a voice preset and personality tone, or describe your own
|
|
31
|
+
|
|
32
|
+
## Getting Started
|
|
33
|
+
|
|
34
|
+
### 1. Install Claude Code (if you haven't already)
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
# macOS / Linux
|
|
38
|
+
curl -fsSL https://claude.ai/install.sh | bash
|
|
39
|
+
|
|
40
|
+
# Windows (PowerShell)
|
|
41
|
+
irm https://claude.ai/install.ps1 | iex
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Verify it's working:
|
|
45
|
+
```bash
|
|
46
|
+
claude --version
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
> Need help? See the [official setup guide](https://code.claude.com/docs/en/setup).
|
|
50
|
+
|
|
51
|
+
### 2. Install warhorn
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
npx warhorn
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
warhorn installs itself as a Claude Code plugin — sounds, scripts, commands, and hooks all set up automatically.
|
|
58
|
+
|
|
59
|
+
### 3. Configure
|
|
60
|
+
|
|
61
|
+
Open Claude Code in any project directory and run:
|
|
62
|
+
|
|
63
|
+
```
|
|
64
|
+
/warhorn:setup
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
The interactive wizard walks you through picking your hooks, sound type, voice, and personality.
|
|
68
|
+
|
|
69
|
+
## How It Works
|
|
70
|
+
|
|
71
|
+
**1. Pick your hooks** — Choose which Claude Code events should play sounds (task complete, permission needed, errors, etc.)
|
|
72
|
+
|
|
73
|
+
**2. Pick your sound type** — Instrumental chimes, AI voice lines, or both
|
|
74
|
+
|
|
75
|
+
**3. Pick your voice** — If using voice lines, choose a voice preset and personality tone (sarcastic, grumpy, enthusiastic, dramatic, or describe your own)
|
|
76
|
+
|
|
77
|
+
**4. Generate** — Claude Code writes custom voice lines and generates WAV files via edge-tts. Done.
|
|
78
|
+
|
|
79
|
+
```
|
|
80
|
+
~/.claude/warhorn/
|
|
81
|
+
├── sounds/
|
|
82
|
+
│ ├── Stop/ ← random .wav plays when Claude finishes
|
|
83
|
+
│ │ ├── chime.wav
|
|
84
|
+
│ │ └── voice_1.wav
|
|
85
|
+
│ ├── PostToolUseFailure/ ← plays when a tool fails
|
|
86
|
+
│ ├── PermissionRequest/ ← plays when Claude needs approval
|
|
87
|
+
│ └── ... ← 14 hook folders total
|
|
88
|
+
└── scripts/
|
|
89
|
+
├── play-sound.sh ← picks random file, plays it
|
|
90
|
+
└── generate_voices.py ← generates WAVs via edge-tts
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Commands
|
|
94
|
+
|
|
95
|
+
Once installed, you have access to these commands in Claude Code:
|
|
96
|
+
|
|
97
|
+
- `/warhorn:setup` — Interactive setup wizard — pick hooks, sound type, voice character
|
|
98
|
+
- `/warhorn:mute` — Mute all sounds
|
|
99
|
+
- `/warhorn:unmute` — Unmute sounds
|
|
100
|
+
|
|
101
|
+
## Voice Presets
|
|
102
|
+
|
|
103
|
+
| Preset | Voice |
|
|
104
|
+
|--------|-------|
|
|
105
|
+
| `male_deep` | Australian male, deep and gruff |
|
|
106
|
+
| `male_mid` | British male, mid-pitch (default) |
|
|
107
|
+
| `female_mid` | British female, mid-pitch |
|
|
108
|
+
| `female_high` | American female, bright and high |
|
|
109
|
+
|
|
110
|
+
### Personality tones
|
|
111
|
+
|
|
112
|
+
| Tone | Vibe | Example |
|
|
113
|
+
|------|------|---------|
|
|
114
|
+
| **sarcastic** | Makes fun of you | "Done. You're welcome." |
|
|
115
|
+
| **grumpy** | Hates everything | "Done. Go away." |
|
|
116
|
+
| **enthusiastic** | Annoyingly excited | "WOOHOO! Done!" |
|
|
117
|
+
| **informational** | Just the facts | "Response complete." |
|
|
118
|
+
| **dramatic** | Over the top epic | "The quest is complete!" |
|
|
119
|
+
|
|
120
|
+
You can also describe a **custom** personality during `/warhorn:setup` and Claude will write voice lines for it.
|
|
121
|
+
|
|
122
|
+
## Supported Hooks
|
|
123
|
+
|
|
124
|
+
| Hook | Default sound | When it fires |
|
|
125
|
+
|------|:---:|-------------|
|
|
126
|
+
| `Stop` | chime, success, bell | Claude finishes responding |
|
|
127
|
+
| `SessionStart` | boot, ready | Session begins |
|
|
128
|
+
| `SessionEnd` | shutdown | Session ends |
|
|
129
|
+
| `PostToolUseFailure` | buzz, sad, wonk | Tool fails |
|
|
130
|
+
| `PermissionRequest` | alert, doorbell | Claude needs approval |
|
|
131
|
+
| `Notification` | ping, bubble | Claude sends notification |
|
|
132
|
+
| `SubagentStart` | spawn | Subagent spawns |
|
|
133
|
+
| `SubagentStop` | return | Subagent finishes |
|
|
134
|
+
| `TaskCompleted` | victory | Task marked complete |
|
|
135
|
+
| `PreCompact` | warning | Context compaction starting |
|
|
136
|
+
| `UserPromptSubmit` | tick, tap | You submit a prompt |
|
|
137
|
+
| `PreToolUse` | _(empty)_ | Before every tool call |
|
|
138
|
+
| `PostToolUse` | _(empty)_ | After every tool call |
|
|
139
|
+
| `TeammateIdle` | _(empty)_ | Team member idles |
|
|
140
|
+
|
|
141
|
+
## Add Your Own Sounds
|
|
142
|
+
|
|
143
|
+
Drop any audio file into a hook folder:
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
cp ~/my-sound.wav ~/.claude/warhorn/sounds/Stop/
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Supported formats: `.wav` (recommended), `.mp3`, `.aiff`, `.ogg`
|
|
150
|
+
|
|
151
|
+
## Requirements
|
|
152
|
+
|
|
153
|
+
- **macOS** — Works out of the box (`afplay` is built in)
|
|
154
|
+
- **Linux** — Needs `pulseaudio` or `alsa-utils` for sounds
|
|
155
|
+
- **Voice lines** (optional) — `pip install edge-tts` + internet
|
|
156
|
+
|
|
157
|
+
## Uninstall
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
npx warhorn uninstall
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Cleanly removes sounds, commands, and hooks from `~/.claude/settings.json`.
|
|
164
|
+
|
|
165
|
+
## License
|
|
166
|
+
|
|
167
|
+
MIT © [Alex Levadski](https://github.com/alevadski)
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
<div align="center">
|
|
172
|
+
|
|
173
|
+
**Congrats, your Claude Code just got voice!**
|
|
174
|
+
|
|
175
|
+
[Report Bug](https://github.com/alexlevadski/warhorn/issues) · [Request Feature](https://github.com/alexlevadski/warhorn/issues)
|
|
176
|
+
|
|
177
|
+
</div>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: warhorn:mute
|
|
3
|
+
description: Mute all warhorn sounds
|
|
4
|
+
allowed-tools:
|
|
5
|
+
- Bash
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
Mute all warhorn sounds. Run this command:
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
touch "$HOME/.claude/warhorn/.muted"
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Confirm: "Sounds muted. Run `/warhorn:unmute` to re-enable."
|
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: warhorn:setup
|
|
3
|
+
description: Configure warhorn sound notifications
|
|
4
|
+
allowed-tools:
|
|
5
|
+
- AskUserQuestion
|
|
6
|
+
- Bash
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
<objective>
|
|
10
|
+
Configure warhorn sound notifications. Interview the user about their preferences, then apply the configuration.
|
|
11
|
+
NEVER modify, overwrite, or recreate ANY files in ~/.claude/warhorn/scripts/. Those scripts are pre-installed. Only CALL them.
|
|
12
|
+
</objective>
|
|
13
|
+
|
|
14
|
+
<process>
|
|
15
|
+
|
|
16
|
+
## Step 1: Greet and Ask About Hooks
|
|
17
|
+
|
|
18
|
+
Display:
|
|
19
|
+
```
|
|
20
|
+
Let's configure warhorn! I'll ask a few quick questions.
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Use the AskUserQuestion tool:
|
|
24
|
+
|
|
25
|
+
```json
|
|
26
|
+
{
|
|
27
|
+
"questions": [
|
|
28
|
+
{
|
|
29
|
+
"question": "Which hooks should play sounds?",
|
|
30
|
+
"header": "Hooks",
|
|
31
|
+
"multiSelect": true,
|
|
32
|
+
"options": [
|
|
33
|
+
{
|
|
34
|
+
"label": "Core (recommended)",
|
|
35
|
+
"description": "Stop, PermissionRequest, PostToolUseFailure — the essentials"
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"label": "Session",
|
|
39
|
+
"description": "SessionStart, SessionEnd — know when sessions begin/end"
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"label": "Notifications",
|
|
43
|
+
"description": "Notification, TaskCompleted — important alerts"
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
"label": "Subagents",
|
|
47
|
+
"description": "SubagentStart, SubagentStop — track agent activity"
|
|
48
|
+
}
|
|
49
|
+
]
|
|
50
|
+
}
|
|
51
|
+
]
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Wait for response using AskUserQuestion. If they select "Other" and type custom hooks, parse the hook names from their text.
|
|
56
|
+
|
|
57
|
+
Map selections to hook names:
|
|
58
|
+
- Core = Stop, PermissionRequest, PostToolUseFailure
|
|
59
|
+
- Session = SessionStart, SessionEnd
|
|
60
|
+
- Notifications = Notification, TaskCompleted
|
|
61
|
+
- Subagents = SubagentStart, SubagentStop
|
|
62
|
+
- If they mention "all" or "everything" = all 14 hooks
|
|
63
|
+
- If they mention "granular" = PreToolUse, PostToolUse, UserPromptSubmit, TeammateIdle, PreCompact
|
|
64
|
+
|
|
65
|
+
## Step 2: Ask About Sound Type
|
|
66
|
+
|
|
67
|
+
Use the AskUserQuestion tool:
|
|
68
|
+
|
|
69
|
+
```json
|
|
70
|
+
{
|
|
71
|
+
"questions": [
|
|
72
|
+
{
|
|
73
|
+
"question": "How should sounds work?",
|
|
74
|
+
"header": "Sound type",
|
|
75
|
+
"multiSelect": false,
|
|
76
|
+
"options": [
|
|
77
|
+
{
|
|
78
|
+
"label": "Instrumental only (recommended)",
|
|
79
|
+
"description": "Bundled chimes and pings. Works right away, no setup needed."
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
"label": "AI voice lines",
|
|
83
|
+
"description": "Character voice via edge-tts (needs pip install edge-tts + internet)"
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
"label": "Both",
|
|
87
|
+
"description": "Instrumentals + voice lines mixed in each folder"
|
|
88
|
+
}
|
|
89
|
+
]
|
|
90
|
+
}
|
|
91
|
+
]
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Wait for response using AskUserQuestion.
|
|
96
|
+
|
|
97
|
+
- If "Instrumental only" → skip to Step 5 (Apply).
|
|
98
|
+
- If "AI voice lines" or "Both" → continue to Step 3.
|
|
99
|
+
|
|
100
|
+
## Step 3: Ask About Voice Character (only if voice or both)
|
|
101
|
+
|
|
102
|
+
Use the AskUserQuestion tool:
|
|
103
|
+
|
|
104
|
+
```json
|
|
105
|
+
{
|
|
106
|
+
"questions": [
|
|
107
|
+
{
|
|
108
|
+
"question": "Pick a voice:",
|
|
109
|
+
"header": "Voice",
|
|
110
|
+
"multiSelect": false,
|
|
111
|
+
"options": [
|
|
112
|
+
{
|
|
113
|
+
"label": "Male mid-range",
|
|
114
|
+
"description": "British male, mid-pitch. Versatile default."
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
"label": "Male deep",
|
|
118
|
+
"description": "Australian male, deep and gruff."
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
"label": "Female mid-range",
|
|
122
|
+
"description": "British female, mid-pitch."
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
"label": "Female high",
|
|
126
|
+
"description": "American female, bright and high."
|
|
127
|
+
}
|
|
128
|
+
]
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
"question": "Pick a personality tone:",
|
|
132
|
+
"header": "Tone",
|
|
133
|
+
"multiSelect": false,
|
|
134
|
+
"options": [
|
|
135
|
+
{
|
|
136
|
+
"label": "sarcastic",
|
|
137
|
+
"description": "Makes fun of you — \"Done. You're welcome.\""
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
"label": "grumpy",
|
|
141
|
+
"description": "Hates everything — \"Done. Go away.\""
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
"label": "enthusiastic",
|
|
145
|
+
"description": "Annoyingly excited — \"WOOHOO! Done!\""
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
"label": "dramatic",
|
|
149
|
+
"description": "Over the top epic — \"The quest is complete!\""
|
|
150
|
+
}
|
|
151
|
+
]
|
|
152
|
+
}
|
|
153
|
+
]
|
|
154
|
+
}
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
Wait for response using AskUserQuestion. If they select "Other" for tone and type a custom description, save it for voice line generation.
|
|
158
|
+
|
|
159
|
+
Map voice selection to preset name:
|
|
160
|
+
- "Male mid-range" → `male_mid`
|
|
161
|
+
- "Male deep" → `male_deep`
|
|
162
|
+
- "Female mid-range" → `female_mid`
|
|
163
|
+
- "Female high" → `female_high`
|
|
164
|
+
|
|
165
|
+
## Step 4: Ask How Many Variations (only if voice or both)
|
|
166
|
+
|
|
167
|
+
Use the AskUserQuestion tool:
|
|
168
|
+
|
|
169
|
+
```json
|
|
170
|
+
{
|
|
171
|
+
"questions": [
|
|
172
|
+
{
|
|
173
|
+
"question": "How many voice line variations per hook?",
|
|
174
|
+
"header": "Variations",
|
|
175
|
+
"multiSelect": false,
|
|
176
|
+
"options": [
|
|
177
|
+
{
|
|
178
|
+
"label": "3 (recommended)",
|
|
179
|
+
"description": "Quick to generate, good variety"
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
"label": "5",
|
|
183
|
+
"description": "More variety, takes a bit longer"
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
"label": "8",
|
|
187
|
+
"description": "Maximum variety, slower generation"
|
|
188
|
+
}
|
|
189
|
+
]
|
|
190
|
+
}
|
|
191
|
+
]
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
Wait for response using AskUserQuestion. Parse the number from their selection.
|
|
196
|
+
|
|
197
|
+
## Step 5: Apply Configuration
|
|
198
|
+
|
|
199
|
+
Say: "Applying your configuration..."
|
|
200
|
+
|
|
201
|
+
### 5a. Update hooks in settings.json
|
|
202
|
+
|
|
203
|
+
```bash
|
|
204
|
+
python3 -c "
|
|
205
|
+
import json, os
|
|
206
|
+
|
|
207
|
+
settings_path = os.path.expanduser('~/.claude/settings.json')
|
|
208
|
+
script = os.path.expanduser('~/.claude/warhorn/scripts/play-sound.sh')
|
|
209
|
+
|
|
210
|
+
try:
|
|
211
|
+
settings = json.load(open(settings_path))
|
|
212
|
+
except:
|
|
213
|
+
settings = {}
|
|
214
|
+
|
|
215
|
+
if 'hooks' not in settings:
|
|
216
|
+
settings['hooks'] = {}
|
|
217
|
+
|
|
218
|
+
all_hooks = ['SessionStart','UserPromptSubmit','PreToolUse','PermissionRequest','PostToolUse','PostToolUseFailure','Notification','SubagentStart','SubagentStop','Stop','TeammateIdle','TaskCompleted','PreCompact','SessionEnd']
|
|
219
|
+
|
|
220
|
+
enabled = [ENABLED_HOOKS_LIST]
|
|
221
|
+
|
|
222
|
+
for hook in all_hooks:
|
|
223
|
+
entry = {'hooks': [{'type': 'command', 'command': f'{script} {hook}'}]}
|
|
224
|
+
if hook in enabled:
|
|
225
|
+
if hook not in settings['hooks']:
|
|
226
|
+
settings['hooks'][hook] = [entry]
|
|
227
|
+
else:
|
|
228
|
+
has_zz = any(h.get('hooks') and any('warhorn' in (i.get('command','')) for i in h['hooks']) for h in settings['hooks'][hook])
|
|
229
|
+
if not has_zz:
|
|
230
|
+
settings['hooks'][hook].append(entry)
|
|
231
|
+
else:
|
|
232
|
+
if hook in settings['hooks']:
|
|
233
|
+
settings['hooks'][hook] = [h for h in settings['hooks'][hook] if not (h.get('hooks') and any('warhorn' in (i.get('command','')) for i in h['hooks']))]
|
|
234
|
+
if not settings['hooks'][hook]:
|
|
235
|
+
del settings['hooks'][hook]
|
|
236
|
+
|
|
237
|
+
if not settings['hooks']:
|
|
238
|
+
del settings['hooks']
|
|
239
|
+
|
|
240
|
+
with open(settings_path, 'w') as f:
|
|
241
|
+
json.dump(settings, f, indent=2)
|
|
242
|
+
f.write('\n')
|
|
243
|
+
|
|
244
|
+
print(f'Hooks updated: {len(enabled)} enabled, {len(all_hooks) - len(enabled)} disabled')
|
|
245
|
+
"
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
Replace `[ENABLED_HOOKS_LIST]` with the user's choices, e.g.: `'Stop', 'PermissionRequest', 'PostToolUseFailure'`
|
|
249
|
+
|
|
250
|
+
### 5b. Generate voice lines (only if voice or both)
|
|
251
|
+
|
|
252
|
+
Install edge-tts:
|
|
253
|
+
```bash
|
|
254
|
+
pip install edge-tts 2>/dev/null || pip3 install edge-tts 2>/dev/null || pip install --user edge-tts
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
**YOU must write the voice lines.** Generate a JSON file with short, punchy lines — **maximum 4 words each**. Write at least N+2 lines per enabled hook (where N is the variation count the user chose), so the generator has enough to pick from randomly.
|
|
258
|
+
|
|
259
|
+
The lines must match the chosen tone. Examples for "sarcastic" tone:
|
|
260
|
+
- Stop: "Done. You're welcome.", "Finished. Hold applause."
|
|
261
|
+
- PermissionRequest: "Approve me. Chop chop.", "Permission. Now, boss."
|
|
262
|
+
- PostToolUseFailure: "Broke. Shocking.", "Not my fault."
|
|
263
|
+
|
|
264
|
+
Write the JSON and generate:
|
|
265
|
+
|
|
266
|
+
```bash
|
|
267
|
+
WARHORN_ROOT="$HOME/.claude/warhorn"
|
|
268
|
+
cat > "$WARHORN_ROOT/custom_lines.json" << 'LINES_EOF'
|
|
269
|
+
{
|
|
270
|
+
"Stop": ["line1", "line2", "line3", ...],
|
|
271
|
+
"PermissionRequest": ["line1", "line2", ...],
|
|
272
|
+
...
|
|
273
|
+
}
|
|
274
|
+
LINES_EOF
|
|
275
|
+
python3 "$WARHORN_ROOT/scripts/generate_voices.py" \
|
|
276
|
+
--hooks "<comma-separated enabled hooks>" \
|
|
277
|
+
--preset <chosen_preset> \
|
|
278
|
+
--lines-file "$WARHORN_ROOT/custom_lines.json" \
|
|
279
|
+
--count <variation_count>
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
Use the ALREADY INSTALLED generate_voices.py. Do NOT create a new script.
|
|
283
|
+
|
|
284
|
+
If voice generation fails, tell the user and keep instrumentals.
|
|
285
|
+
|
|
286
|
+
If voice-only (no instrumentals), remove bundled sounds:
|
|
287
|
+
```bash
|
|
288
|
+
find "$WARHORN_ROOT/sounds" -maxdepth 2 -type f \( -name "*.wav" -o -name "*.mp3" \) ! -name "voice_*" -delete
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
### 5c. Unmute and test
|
|
292
|
+
|
|
293
|
+
```bash
|
|
294
|
+
rm -f "$HOME/.claude/warhorn/.muted"
|
|
295
|
+
"$HOME/.claude/warhorn/scripts/play-sound.sh" Stop
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
### 5d. Summary
|
|
299
|
+
|
|
300
|
+
Tell the user what was configured and remind them about `/warhorn:mute` and `/warhorn:unmute`.
|
|
301
|
+
|
|
302
|
+
</process>
|
|
303
|
+
|
|
304
|
+
<critical_rules>
|
|
305
|
+
1. ONE question at a time — never ask multiple questions in same message
|
|
306
|
+
2. WAIT for answers — don't proceed until they respond
|
|
307
|
+
3. Use AskUserQuestion tool — for every question with predefined options
|
|
308
|
+
4. NEVER modify files in ~/.claude/warhorn/scripts/ — only CALL them
|
|
309
|
+
5. Always write voice lines yourself as JSON — never use --tone flag, always use --lines-file
|
|
310
|
+
6. Voice lines must be maximum 4 words each
|
|
311
|
+
7. If edge-tts fails → suggest pip3 install --break-system-packages edge-tts
|
|
312
|
+
8. If voice generation fails → keep instrumentals, tell user to retry later
|
|
313
|
+
9. If any script errors → tell user to reinstall with npx warhorn, do NOT rewrite scripts
|
|
314
|
+
</critical_rules>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: warhorn:unmute
|
|
3
|
+
description: Unmute all warhorn sounds
|
|
4
|
+
allowed-tools:
|
|
5
|
+
- Bash
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
Unmute all warhorn sounds. Run this command:
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
rm -f "$HOME/.claude/warhorn/.muted"
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Confirm: "Sounds unmuted. The horn sounds!"
|