@mcptoolshop/claude-sfx 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.
@@ -0,0 +1,188 @@
1
+ <p align="center">
2
+ <a href="README.ja.md">日本語</a> | <a href="README.zh.md">中文</a> | <a href="README.es.md">Español</a> | <a href="README.fr.md">Français</a> | <a href="README.hi.md">हिन्दी</a> | <a href="README.it.md">Italiano</a> | <a href="README.md">English</a>
3
+ </p>
4
+
5
+ <p align="center">
6
+ <img src="https://raw.githubusercontent.com/mcp-tool-shop-org/brand/main/logos/claude-sfx/readme.png" width="400" alt="Claude-SFX">
7
+ </p>
8
+
9
+ <p align="center">
10
+ <a href="https://www.npmjs.com/package/claude-sfx"><img src="https://img.shields.io/npm/v/claude-sfx" alt="npm version"></a>
11
+ <a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue" alt="MIT License"></a>
12
+ <a href="https://mcp-tool-shop-org.github.io/claude-sfx/"><img src="https://img.shields.io/badge/Landing_Page-live-blue" alt="Landing Page"></a>
13
+ </p>
14
+
15
+ Feedback de áudio procedural para [Claude Code](https://docs.anthropic.com/en/docs/claude-code). Cada chamada de ferramenta, edição de arquivo, pesquisa, envio para o Git e despacho de agente recebe um som distinto — sintetizado a partir de cálculos matemáticos, e não de arquivos de áudio.
16
+
17
+ ## Início Rápido
18
+
19
+ ```bash
20
+ npm install -g claude-sfx
21
+ cd your-project
22
+ claude-sfx init # install hooks into .claude/settings.json
23
+ claude-sfx demo # hear all 7 verbs
24
+ ```
25
+
26
+ Pronto. O Claude Code agora emitirá sons enquanto estiver em execução.
27
+
28
+ ## Por que o feedback de áudio?
29
+
30
+ Quando um agente de IA lê, escreve, pesquisa e implanta em seu nome, você perde a visibilidade. Você está apenas olhando para o texto passando na tela. O feedback de áudio restaura a consciência:
31
+
32
+ - **Acessibilidade** — ouça as mudanças de estado, erros e conclusões sem precisar observar o terminal.
33
+ - **Fluxo** — saiba se um teste foi aprovado ou se um envio foi concluído sem precisar alternar entre diferentes contextos.
34
+ - **Presença** — o agente parece um colaborador, e não uma caixa preta.
35
+
36
+ ## Os 7 Verbos
37
+
38
+ Cada ação do Claude Code corresponde a um dos 7 verbos principais. Modificadores (status, escopo, direção) alteram o som sem comprometer a coerência.
39
+
40
+ | Verbo | Gatilhos | Som |
41
+ | --- | --- | --- |
42
+ | **intake** | `Read`, `WebFetch`, `WebSearch` | Onda senoidal ascendente — algo chegando |
43
+ | **transform** | `Edit` | Pulso texturizado por FM — remodelando |
44
+ | **commit** | `Write`, `NotebookEdit`, `git commit` | Tom de sino agudo — selado |
45
+ | **navigate** | `Grep`, `Glob` | Sinal sonár — escaneando |
46
+ | **execute** | `Bash`, `npm test`, `tsc` | Ruído + tom — ação mecânica |
47
+ | **move** | `mv`, `cp`, criação de subagente | Sopro de vento — deslocamento de ar |
48
+ | **sync** | `git push`, `git pull` | Sopro dramático + âncora tonal |
49
+
50
+ ### Modificadores
51
+
52
+ ```bash
53
+ claude-sfx play navigate --status ok # bright ping (octave harmonic)
54
+ claude-sfx play navigate --status err # low detuned ping (dissonance)
55
+ claude-sfx play navigate --status warn # tremolo ping
56
+ claude-sfx play sync --direction up # rising whoosh (push)
57
+ claude-sfx play sync --direction down # falling whoosh (pull)
58
+ claude-sfx play intake --scope remote # longer tail (distance feel)
59
+ ```
60
+
61
+ ### Detecção Inteligente de Bash
62
+
63
+ O manipulador de eventos inspeciona os comandos Bash para escolher o som correto:
64
+
65
+ | Comando Bash | Verbo | Status |
66
+ | --- | --- | --- |
67
+ | `git push` | sync (para cima) | com base no código de saída |
68
+ | `git pull` | sync (para baixo) | com base no código de saída |
69
+ | `npm test`, `pytest` | executar | com base no código de saída |
70
+ | `tsc`, `npm run build` | executar | com base no código de saída |
71
+ | `mv`, `cp` | mover | — |
72
+ | `rm` | mover | aviso |
73
+ | tudo o mais | executar | com base no código de saída |
74
+
75
+ ## Perfis
76
+
77
+ Conjuntos de sons que alteram todo o comportamento com um único parâmetro.
78
+
79
+ | Perfil | Comportamento |
80
+ | --- | --- |
81
+ | **minimal** (default) | Tons de onda senoidal — sutis, profissionais, para uso diário |
82
+ | **retro** | Sons de onda quadrada em 8 bits — divertidos, mas controlados |
83
+
84
+ ```bash
85
+ claude-sfx demo --profile retro # hear retro palette
86
+ claude-sfx preview minimal # audition all sounds + modifiers
87
+ claude-sfx config set profile retro # change default globally
88
+ claude-sfx config repo retro # use retro in current directory only
89
+ ```
90
+
91
+ ### Perfis Personalizados
92
+
93
+ Copie `profiles/minimal.json`, edite os parâmetros de síntese e carregue-o:
94
+
95
+ ```bash
96
+ claude-sfx play navigate --profile ./my-profile.json
97
+ ```
98
+
99
+ Cada número no arquivo JSON corresponde diretamente ao motor de síntese — forma de onda, frequência, duração, envelope (ADSR), profundidade de modulação por frequência (FM), largura de banda, ganho.
100
+
101
+ ## Anti-Irritação
102
+
103
+ O que diferencia um produto de um brinquedo.
104
+
105
+ | Recurso | Comportamento |
106
+ | --- | --- |
107
+ | **Debounce** | O mesmo verbo dentro de 200ms → um som |
108
+ | **Rate limit** | Máximo de 8 sons em uma janela de 10 segundos |
109
+ | **Quiet hours** | Todos os sons são suprimidos durante as horas configuradas |
110
+ | **Mute** | Alternância instantânea, persiste após a reinicialização da sessão |
111
+ | **Volume** | Controle de ganho de 0 a 100 |
112
+ | **Per-verb disable** | Desative verbos específicos que você não deseja |
113
+
114
+ ```bash
115
+ claude-sfx mute # instant silence
116
+ claude-sfx unmute
117
+ claude-sfx volume 40 # quieter
118
+ claude-sfx config set quiet-start 22:00 # quiet after 10pm
119
+ claude-sfx config set quiet-end 07:00 # until 7am
120
+ claude-sfx disable navigate # no more search pings
121
+ claude-sfx enable navigate # bring it back
122
+ ```
123
+
124
+ ## Ambiente (Operações de Longa Duração)
125
+
126
+ Para comandos que levam um tempo (compilações, implantações, grandes conjuntos de testes):
127
+
128
+ ```bash
129
+ claude-sfx ambient-start # low drone fades in
130
+ # ... operation runs ...
131
+ claude-sfx ambient-resolve # drone stops, resolution stinger plays
132
+ claude-sfx ambient-stop # stop drone silently (no stinger)
133
+ ```
134
+
135
+ ## Sons da Sessão
136
+
137
+ ```bash
138
+ claude-sfx session-start # two-note ascending chime (boot)
139
+ claude-sfx session-end # two-note descending chime (closure)
140
+ ```
141
+
142
+ ## Todos os Comandos
143
+
144
+ ```
145
+ Setup:
146
+ init Install hooks into .claude/settings.json
147
+ uninstall Remove hooks
148
+
149
+ Playback:
150
+ play <verb> [options] Play a sound (goes through guard)
151
+ demo [--profile <name>] Play all 7 verbs
152
+ preview [profile] Audition all sounds in a profile
153
+ session-start / session-end Chimes
154
+ ambient-start / ambient-resolve / ambient-stop
155
+
156
+ Config:
157
+ mute / unmute Toggle all sounds
158
+ volume [0-100] Get or set volume
159
+ config Print current config
160
+ config set <key> <value> Set a value
161
+ config reset Reset to defaults
162
+ config repo <profile|clear> Per-directory profile override
163
+ disable / enable <verb> Toggle specific verbs
164
+ export [dir] [--profile] Export all sounds as .wav files
165
+ ```
166
+
167
+ ## Como Funciona
168
+
169
+ Nenhum arquivo de áudio. Cada som é sintetizado em tempo de execução a partir de cálculos matemáticos:
170
+
171
+ - **Osciladores** — senoidal, quadrado, dente de serra, triangular, ruído branco
172
+ - **Envelopes ADSR** — ataque, decaimento, sustentação, liberação
173
+ - **Síntese FM** — modulação de frequência para texturas
174
+ - **Filtro de variável de estado** — ruído filtrado em banda para efeitos sonoros
175
+ - **Varreduras de frequência** — interpolação linear para movimento
176
+ - **Limitador de volume** — compressão "soft-knee", limite máximo
177
+
178
+ O pacote completo tem aproximadamente 2.800 linhas de código TypeScript e não possui dependências para produção. Os sons são gerados como buffers PCM, codificados em WAV na memória e reproduzidos através do reprodutor de áudio nativo do sistema operacional (PowerShell no Windows, afplay no macOS, aplay no Linux).
179
+
180
+ ## Requisitos:
181
+
182
+ - Node.js 18 ou superior
183
+ - Claude Code
184
+ - Saída de áudio do sistema (alto-falantes ou fones de ouvido)
185
+
186
+ ## Licença:
187
+
188
+ [MIT](LICENSE)
package/README.zh.md ADDED
@@ -0,0 +1,188 @@
1
+ <p align="center">
2
+ <a href="README.ja.md">日本語</a> | <a href="README.md">English</a> | <a href="README.es.md">Español</a> | <a href="README.fr.md">Français</a> | <a href="README.hi.md">हिन्दी</a> | <a href="README.it.md">Italiano</a> | <a href="README.pt-BR.md">Português (BR)</a>
3
+ </p>
4
+
5
+ <p align="center">
6
+ <img src="https://raw.githubusercontent.com/mcp-tool-shop-org/brand/main/logos/claude-sfx/readme.png" width="400" alt="Claude-SFX">
7
+ </p>
8
+
9
+ <p align="center">
10
+ <a href="https://www.npmjs.com/package/claude-sfx"><img src="https://img.shields.io/npm/v/claude-sfx" alt="npm version"></a>
11
+ <a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue" alt="MIT License"></a>
12
+ <a href="https://mcp-tool-shop-org.github.io/claude-sfx/"><img src="https://img.shields.io/badge/Landing_Page-live-blue" alt="Landing Page"></a>
13
+ </p>
14
+
15
+ 对于 [Claude Code](https://docs.anthropic.com/en/docs/claude-code),我们提供了程序化的音频反馈。 每次工具调用、文件编辑、搜索、git推送以及代理任务都会产生不同的声音,这些声音是通过数学算法合成的,而不是音频文件。
16
+
17
+ ## 快速开始
18
+
19
+ ```bash
20
+ npm install -g claude-sfx
21
+ cd your-project
22
+ claude-sfx init # install hooks into .claude/settings.json
23
+ claude-sfx demo # hear all 7 verbs
24
+ ```
25
+
26
+ 就此完成了。现在,Claude Code 在工作时会发出声音。
27
+
28
+ ## 为什么使用音频反馈?
29
+
30
+ 当 AI 代理代表您读取、编写、搜索和部署时,您会失去可见性。 您只是盯着屏幕上滚动的文本。 音频反馈可以恢复您的意识:
31
+
32
+ - **可访问性**:无需观看终端,即可听到状态变化、错误和完成情况。
33
+ - **流畅性**:无需切换上下文,即可知道测试是否通过或推送是否成功。
34
+ - **沉浸感**:代理感觉更像是一个协作者,而不是一个黑盒子。
35
+
36
+ ## 7 种动词
37
+
38
+ Claude Code 的每个操作都对应于 7 种核心动词中的一种。 修饰符(状态、范围、方向)会改变声音,但不会破坏其连贯性。
39
+
40
+ | 动词 | 触发器 | 声音 |
41
+ | --- | --- | --- |
42
+ | **intake** | `Read`, `WebFetch`, `WebSearch` | 逐渐上升的正弦波——表示即将到来 |
43
+ | **transform** | `Edit` | FM 调制脉冲——表示重塑 |
44
+ | **commit** | `Write`, `NotebookEdit`, `git commit` | 清晰的敲击声——表示已完成 |
45
+ | **navigate** | `Grep`, `Glob` | 声纳回声——表示扫描 |
46
+ | **execute** | `Bash`, `npm test`, `tsc` | 噪声爆发 + 声音——表示机械动作 |
47
+ | **move** | `mv`、`cp`、子代理启动 | 气流声——表示空气移动 |
48
+ | **sync** | `git push`, `git pull` | 戏剧性的气流声 + 音调锚 |
49
+
50
+ ### 修饰符
51
+
52
+ ```bash
53
+ claude-sfx play navigate --status ok # bright ping (octave harmonic)
54
+ claude-sfx play navigate --status err # low detuned ping (dissonance)
55
+ claude-sfx play navigate --status warn # tremolo ping
56
+ claude-sfx play sync --direction up # rising whoosh (push)
57
+ claude-sfx play sync --direction down # falling whoosh (pull)
58
+ claude-sfx play intake --scope remote # longer tail (distance feel)
59
+ ```
60
+
61
+ ### 智能 Bash 检测
62
+
63
+ 处理程序会检查 Bash 命令,以选择正确的声音:
64
+
65
+ | Bash 命令 | 动词 | 状态 |
66
+ | --- | --- | --- |
67
+ | `git push` | sync (向上) | 来自退出码 |
68
+ | `git pull` | sync (向下) | 来自退出码 |
69
+ | `npm test`, `pytest` | execute (执行) | 来自退出码 |
70
+ | `tsc`, `npm run build` | execute (执行) | 来自退出码 |
71
+ | `mv`, `cp` | move (移动) | — |
72
+ | `rm` | move (移动) | warn (警告) |
73
+ | 其他所有情况 | execute (执行) | 来自退出码 |
74
+
75
+ ## 配置文件
76
+
77
+ 这些配置文件会改变整个声音,只需一个标志即可。
78
+
79
+ | 配置文件 | 声音风格 |
80
+ | --- | --- |
81
+ | **minimal** (default) | 正弦波声音——微妙、专业、日常使用 |
82
+ | **retro** | 方波 8 位声音——有趣但可控 |
83
+
84
+ ```bash
85
+ claude-sfx demo --profile retro # hear retro palette
86
+ claude-sfx preview minimal # audition all sounds + modifiers
87
+ claude-sfx config set profile retro # change default globally
88
+ claude-sfx config repo retro # use retro in current directory only
89
+ ```
90
+
91
+ ### 自定义配置文件
92
+
93
+ 复制 `profiles/minimal.json`,编辑合成参数,然后加载它:
94
+
95
+ ```bash
96
+ claude-sfx play navigate --profile ./my-profile.json
97
+ ```
98
+
99
+ JSON 中的每个数字都直接映射到合成引擎,包括波形、频率、持续时间、包络(ADSR)、FM 深度、带宽和增益。
100
+
101
+ ## 防厌烦
102
+
103
+ 区分产品和玩具的关键。
104
+
105
+ | 功能 | 行为 |
106
+ | --- | --- |
107
+ | **Debounce** | 相同动词在 200 毫秒内出现 → 产生一个声音 |
108
+ | **Rate limit** | 每 10 秒最多 8 个声音 |
109
+ | **Quiet hours** | 在配置的时间段内,所有声音都会被抑制 |
110
+ | **Mute** | 即时切换,重启会话后仍然有效 |
111
+ | **Volume** | 0–100 增益控制 |
112
+ | **Per-verb disable** | 可以关闭您不想听到的特定动词的声音 |
113
+
114
+ ```bash
115
+ claude-sfx mute # instant silence
116
+ claude-sfx unmute
117
+ claude-sfx volume 40 # quieter
118
+ claude-sfx config set quiet-start 22:00 # quiet after 10pm
119
+ claude-sfx config set quiet-end 07:00 # until 7am
120
+ claude-sfx disable navigate # no more search pings
121
+ claude-sfx enable navigate # bring it back
122
+ ```
123
+
124
+ ## 环境声音(长时间运行的操作)
125
+
126
+ 对于需要一段时间才能完成的命令(构建、部署、大型测试套件):
127
+
128
+ ```bash
129
+ claude-sfx ambient-start # low drone fades in
130
+ # ... operation runs ...
131
+ claude-sfx ambient-resolve # drone stops, resolution stinger plays
132
+ claude-sfx ambient-stop # stop drone silently (no stinger)
133
+ ```
134
+
135
+ ## 会话声音
136
+
137
+ ```bash
138
+ claude-sfx session-start # two-note ascending chime (boot)
139
+ claude-sfx session-end # two-note descending chime (closure)
140
+ ```
141
+
142
+ ## 所有命令
143
+
144
+ ```
145
+ Setup:
146
+ init Install hooks into .claude/settings.json
147
+ uninstall Remove hooks
148
+
149
+ Playback:
150
+ play <verb> [options] Play a sound (goes through guard)
151
+ demo [--profile <name>] Play all 7 verbs
152
+ preview [profile] Audition all sounds in a profile
153
+ session-start / session-end Chimes
154
+ ambient-start / ambient-resolve / ambient-stop
155
+
156
+ Config:
157
+ mute / unmute Toggle all sounds
158
+ volume [0-100] Get or set volume
159
+ config Print current config
160
+ config set <key> <value> Set a value
161
+ config reset Reset to defaults
162
+ config repo <profile|clear> Per-directory profile override
163
+ disable / enable <verb> Toggle specific verbs
164
+ export [dir] [--profile] Export all sounds as .wav files
165
+ ```
166
+
167
+ ## 工作原理
168
+
169
+ 没有音频文件。 每次声音都是在运行时通过数学算法合成的:
170
+
171
+ - **振荡器** — 正弦波、方波、锯齿波、三角波、白噪声
172
+ - **ADSR 包络线** — 攻击 (attack)、衰减 (decay)、维持 (sustain)、释放 (release)
173
+ - **FM 综合** — 用于产生纹理的频率调制
174
+ - **状态变量滤波器** — 用于产生“嗖嗖”声的带通滤波噪声
175
+ - **频率扫描** — 用于产生运动效果的线性插值
176
+ - **响度限制器** — 软压缩、硬限位
177
+
178
+ 整个软件包包含约 2800 行 TypeScript 代码,且没有任何生产依赖。声音以 PCM 缓冲区生成,在内存中编码为 WAV 格式,并通过操作系统自带的音频播放器播放(Windows 上的 PowerShell,macOS 上的 afplay,Linux 上的 aplay)。
179
+
180
+ ## 需求
181
+
182
+ - Node.js 18+
183
+ - Claude Code
184
+ - 系统音频输出(扬声器或耳机)
185
+
186
+ ## 许可证
187
+
188
+ [MIT](LICENSE)
Binary file
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Ambient drone system for long-running operations.
3
+ * Starts a low background drone that loops until resolved.
4
+ *
5
+ * Flow: claude-sfx ambient-start → drone plays in background
6
+ * claude-sfx ambient-resolve → drone stops, stinger plays
7
+ *
8
+ * Implementation: writes a PID file so `ambient-resolve` can find and kill the drone.
9
+ */
10
+ import type { Profile } from "./profiles.js";
11
+ /** Check if an ambient drone is currently running. */
12
+ export declare function isAmbientRunning(): boolean;
13
+ /**
14
+ * Start the ambient drone in a background process.
15
+ * The drone loops a WAV file using the system player until killed.
16
+ */
17
+ export declare function startAmbient(profile: Profile): {
18
+ started: boolean;
19
+ pid?: number;
20
+ };
21
+ /** Stop the ambient drone and play the resolution stinger. */
22
+ export declare function resolveAmbient(profile: Profile): {
23
+ resolved: boolean;
24
+ };
25
+ /** Stop the ambient drone without playing the stinger. */
26
+ export declare function stopAmbient(): {
27
+ stopped: boolean;
28
+ };
@@ -0,0 +1,179 @@
1
+ /**
2
+ * Ambient drone system for long-running operations.
3
+ * Starts a low background drone that loops until resolved.
4
+ *
5
+ * Flow: claude-sfx ambient-start → drone plays in background
6
+ * claude-sfx ambient-resolve → drone stops, stinger plays
7
+ *
8
+ * Implementation: writes a PID file so `ambient-resolve` can find and kill the drone.
9
+ */
10
+ import { writeFileSync, readFileSync, unlinkSync, existsSync } from "node:fs";
11
+ import { join } from "node:path";
12
+ import { tmpdir } from "node:os";
13
+ import { spawn, execSync } from "node:child_process";
14
+ import { generateAmbientChunk, generateAmbientResolve } from "./verbs.js";
15
+ import { encodeWav } from "./synth.js";
16
+ import { playSync } from "./player.js";
17
+ const PID_FILE = join(tmpdir(), "claude-sfx-ambient.pid");
18
+ const DRONE_WAV = join(tmpdir(), "claude-sfx-drone.wav");
19
+ /** Check if an ambient drone is currently running. */
20
+ export function isAmbientRunning() {
21
+ if (!existsSync(PID_FILE))
22
+ return false;
23
+ try {
24
+ const pid = parseInt(readFileSync(PID_FILE, "utf-8").trim(), 10);
25
+ // Check if process is alive (signal 0 = existence check)
26
+ process.kill(pid, 0);
27
+ return true;
28
+ }
29
+ catch {
30
+ // Process dead or PID file stale — clean up
31
+ try {
32
+ unlinkSync(PID_FILE);
33
+ }
34
+ catch { /* ignore */ }
35
+ return false;
36
+ }
37
+ }
38
+ /**
39
+ * Start the ambient drone in a background process.
40
+ * The drone loops a WAV file using the system player until killed.
41
+ */
42
+ export function startAmbient(profile) {
43
+ if (isAmbientRunning()) {
44
+ return { started: false };
45
+ }
46
+ // Generate the drone chunk WAV
47
+ const chunk = generateAmbientChunk(profile);
48
+ const wavData = encodeWav(chunk);
49
+ writeFileSync(DRONE_WAV, wavData);
50
+ // Spawn a looping player process
51
+ const platform = process.platform;
52
+ let child;
53
+ if (platform === "win32") {
54
+ // PowerShell loop: plays the WAV repeatedly until killed
55
+ child = spawn("powershell", [
56
+ "-NoProfile",
57
+ "-Command",
58
+ `while($true){(New-Object Media.SoundPlayer '${DRONE_WAV}').PlaySync()}`,
59
+ ], {
60
+ stdio: "ignore",
61
+ detached: true,
62
+ });
63
+ }
64
+ else if (platform === "darwin") {
65
+ // macOS: loop with afplay
66
+ child = spawn("bash", [
67
+ "-c",
68
+ `while true; do afplay "${DRONE_WAV}"; done`,
69
+ ], {
70
+ stdio: "ignore",
71
+ detached: true,
72
+ });
73
+ }
74
+ else {
75
+ // Linux: loop with aplay/paplay
76
+ const player = (() => {
77
+ try {
78
+ execSync("which paplay", { stdio: "ignore" });
79
+ return "paplay";
80
+ }
81
+ catch {
82
+ return "aplay -q";
83
+ }
84
+ })();
85
+ child = spawn("bash", [
86
+ "-c",
87
+ `while true; do ${player} "${DRONE_WAV}"; done`,
88
+ ], {
89
+ stdio: "ignore",
90
+ detached: true,
91
+ });
92
+ }
93
+ child.unref();
94
+ if (child.pid) {
95
+ writeFileSync(PID_FILE, String(child.pid));
96
+ return { started: true, pid: child.pid };
97
+ }
98
+ return { started: false };
99
+ }
100
+ /** Stop the ambient drone and play the resolution stinger. */
101
+ export function resolveAmbient(profile) {
102
+ if (!existsSync(PID_FILE)) {
103
+ return { resolved: false };
104
+ }
105
+ try {
106
+ const pid = parseInt(readFileSync(PID_FILE, "utf-8").trim(), 10);
107
+ // Kill the drone process tree
108
+ if (process.platform === "win32") {
109
+ try {
110
+ execSync(`taskkill /PID ${pid} /T /F`, { stdio: "ignore" });
111
+ }
112
+ catch { /* already dead */ }
113
+ }
114
+ else {
115
+ try {
116
+ // Kill the process group (negative PID)
117
+ process.kill(-pid, "SIGTERM");
118
+ }
119
+ catch {
120
+ try {
121
+ process.kill(pid, "SIGTERM");
122
+ }
123
+ catch { /* already dead */ }
124
+ }
125
+ }
126
+ }
127
+ catch {
128
+ // PID parse error or kill error — move on
129
+ }
130
+ // Clean up
131
+ try {
132
+ unlinkSync(PID_FILE);
133
+ }
134
+ catch { /* ignore */ }
135
+ try {
136
+ unlinkSync(DRONE_WAV);
137
+ }
138
+ catch { /* ignore */ }
139
+ // Play the resolution stinger
140
+ const stinger = generateAmbientResolve(profile);
141
+ playSync(stinger);
142
+ return { resolved: true };
143
+ }
144
+ /** Stop the ambient drone without playing the stinger. */
145
+ export function stopAmbient() {
146
+ if (!existsSync(PID_FILE)) {
147
+ return { stopped: false };
148
+ }
149
+ try {
150
+ const pid = parseInt(readFileSync(PID_FILE, "utf-8").trim(), 10);
151
+ if (process.platform === "win32") {
152
+ try {
153
+ execSync(`taskkill /PID ${pid} /T /F`, { stdio: "ignore" });
154
+ }
155
+ catch { /* already dead */ }
156
+ }
157
+ else {
158
+ try {
159
+ process.kill(-pid, "SIGTERM");
160
+ }
161
+ catch {
162
+ try {
163
+ process.kill(pid, "SIGTERM");
164
+ }
165
+ catch { /* already dead */ }
166
+ }
167
+ }
168
+ }
169
+ catch { /* ignore */ }
170
+ try {
171
+ unlinkSync(PID_FILE);
172
+ }
173
+ catch { /* ignore */ }
174
+ try {
175
+ unlinkSync(DRONE_WAV);
176
+ }
177
+ catch { /* ignore */ }
178
+ return { stopped: true };
179
+ }
package/dist/cli.d.ts ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * claude-sfx CLI
4
+ * Procedural audio feedback for Claude Code.
5
+ */
6
+ export {};