turnbell 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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Paurush Rai
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,301 @@
1
+ # turnbell
2
+
3
+ **turnbell calls out the moment your CLI coding agent finishes or needs you.**
4
+
5
+ > **turnbell was previously named hollr.** Same tool, new name. If you installed
6
+ > `hollr-cli`, see [Migrating from hollr](#migrating-from-hollr) — your existing
7
+ > hooks keep working through a compatibility alias while you switch.
8
+
9
+ Kick off a long agent run, switch to something else, and stop babysitting the
10
+ terminal. The second your agent finishes a turn — or stalls waiting on your
11
+ input — turnbell speaks up: it reads the response aloud, fires a desktop
12
+ notification, plays a sound, and/or pings your phone. You get back to it exactly
13
+ when it needs you — not a minute sooner, not ten minutes later.
14
+
15
+ - **Local-first, zero telemetry.** Voice, notifications, and sound never leave
16
+ your machine. The only thing that ever goes off-device is a webhook you set up
17
+ yourself — and that payload is metadata only (see [Privacy](#privacy)).
18
+ - **Works with every agent.** 8 first-class integrations — Claude Code, Codex,
19
+ Gemini CLI, Copilot CLI, Cursor, opencode, Antigravity, Amp — plus a universal
20
+ wrapper (`turnbell run`) that adds done/error alerts to *any* command.
21
+ - **Nothing invasive in your setup.** turnbell wires each agent's own hook config
22
+ to call `turnbell emit` — no SDK, no background daemon. Every change is previewed
23
+ before it's written, and `turnbell uninstall` surgically removes only what turnbell
24
+ added — edits you make afterward are preserved (see
25
+ [Limitations & caveats](#limitations--caveats)).
26
+
27
+ ## Install
28
+
29
+ Requires **Node ≥ 20**.
30
+
31
+ ```bash
32
+ npm i -g turnbell
33
+ turnbell init # interactive setup wizard: pick agents, sounds, webhooks
34
+ ```
35
+
36
+ Or run it without a global install:
37
+
38
+ ```bash
39
+ npx turnbell init
40
+ ```
41
+
42
+ `turnbell init` detects which agents you have, shows you the exact config diff
43
+ before writing anything, and lets you choose what you hear and when.
44
+
45
+ ## Migrating from hollr
46
+
47
+ turnbell is the same tool as **hollr** (npm: `hollr-cli`), renamed. Upgrade:
48
+
49
+ ```bash
50
+ npm rm -g hollr-cli
51
+ npm i -g turnbell
52
+ turnbell init # re-wires hooks to the new name, cleans old hollr entries
53
+ ```
54
+
55
+ Everything is handled for you:
56
+
57
+ - **Old hooks keep working.** turnbell ships a `hollr` bin alias, so hooks
58
+ already wired into your agents keep firing (with a one-line rename notice on
59
+ stderr) until you re-run `turnbell init`.
60
+ - **Config migrates automatically.** `~/.config/hollr` is moved to
61
+ `~/.config/turnbell` on first run; a `$HOLLR_HOME` override is still honored.
62
+ - **Re-wiring replaces, never duplicates.** `turnbell init` removes old
63
+ `hollr emit …` hook entries, `hollr.*` command/plugin files, and the old
64
+ read-aloud memory block as it writes the new ones — no double notifications.
65
+ - **`turnbell uninstall` also cleans hollr-era wiring**, so a full removal works
66
+ even if you never re-ran init.
67
+
68
+ ## Turning it on and off
69
+
70
+ At setup, turnbell asks whether it should notify you **in every project** or
71
+ **only in projects you turn on**. You can change your mind per project or pause
72
+ everything for a while:
73
+
74
+ | Want | Command |
75
+ |------|---------|
76
+ | Turn turnbell on for this project | `turnbell on` |
77
+ | Turn it off for this project | `turnbell off` |
78
+ | Quiet everywhere for a bit | `turnbell quiet` · `turnbell quiet 30m` |
79
+ | Turn quiet back off | `turnbell quiet off` |
80
+ | See what turnbell is doing and why | `turnbell status` |
81
+
82
+ `turnbell quiet 30m` comes back on by itself after 30 minutes; a bare `turnbell quiet`
83
+ stays quiet until you run `turnbell quiet off`.
84
+
85
+ ## Agent support
86
+
87
+ Each integration is defined by what the agent's own hooks can actually deliver —
88
+ this table reflects the shipped adapter capabilities, not aspirations.
89
+
90
+ | Agent | done | blocked | read-aloud | slash cmd | Notes |
91
+ |---|:---:|:---:|:---:|:---:|---|
92
+ | **Claude Code** | ✅ | ✅ | ✅ | `/turnbell` | Reference integration; read-aloud from the JSONL transcript. |
93
+ | **Codex** (OpenAI) | ✅ | ✅ | ✅ | — | `notify` for done + read-aloud; `hooks.json` PermissionRequest for blocked (needs one-time trust in Codex). |
94
+ | **Copilot CLI** (GitHub) | ✅ | ✅ | ✅ | — | `agentStop` + notification hooks. |
95
+ | **Gemini CLI** (Google) | ✅ | ✅ | ✅ | `/turnbell` | `AfterAgent` done, `Notification` blocked. |
96
+ | **Antigravity** (agy) | ✅ | — | — | — | Announce-only: no needs-input event, opaque transcript. |
97
+ | **Cursor** (cursor-agent) | ✅ | — | — | — | Native stop-hook announce; blocked + read-aloud come via `turnbell run`. |
98
+ | **opencode** (sst) | ✅ | ✅ | — | — | Plugin bridges `session.idle` (done) + `permission.asked` (blocked); read-aloud off (opaque storage). |
99
+ | **Amp** (Sourcegraph) | ✅ | — | — | — | Announce-only via Amp's built-in notifications + `turnbell run`; not auto-wired. |
100
+ | **`turnbell run`** (wrapper) | ✅ | — | ✅ | — | Universal fallback for *any* command: done/error on exit. Cursor stream read-aloud via `--announce-stream cursor`. |
101
+
102
+ Read-aloud speaks the agent's last response. "blocked" fires when the agent is
103
+ waiting on your input. A dash means the agent's surface doesn't expose that
104
+ signal — not that turnbell chose to omit it.
105
+
106
+ **Platform status:** macOS is stable. **Linux and Windows are beta** — voice,
107
+ desktop notifications, and sound are implemented but not yet
108
+ hardware-end-to-end verified.
109
+
110
+ > Locally verified against a real install: Claude Code. The Codex, Copilot,
111
+ > Gemini, opencode, Amp, and Cursor integrations were built against their
112
+ > published hook/config docs.
113
+
114
+ ### Read-aloud "speakable mode"
115
+
116
+ When you pick **read-aloud** for the `done` event during `turnbell init`, turnbell can
117
+ add a small, clearly-marked instruction to each supported agent's global memory
118
+ file (Claude Code `~/.claude/CLAUDE.md`, Codex `~/.codex/AGENTS.md`, Gemini
119
+ `~/.gemini/GEMINI.md`). It asks the model to keep its final message speakable and
120
+ to write code or dense detail to a temp `.md` file it opens with your chosen
121
+ markdown command instead of speaking it.
122
+
123
+ - **Only three agents** support this today — **Claude Code, Codex, Gemini** (the
124
+ ones with a global standing-instructions file). Other agents don't offer it.
125
+ - **Opt-in** — offered only when read-aloud is your `done` mode, only for agents
126
+ you wire.
127
+ - **Reversible** — it's a marked block; re-running `turnbell init` with read-aloud
128
+ off removes just that block and leaves the rest of your file untouched.
129
+ `turnbell uninstall` reverses everything turnbell wired.
130
+ - **Best-effort, not a guarantee** — it's a prompt *nudge*. The model decides
131
+ whether to comply; it may still speak something technical, or over-use files.
132
+ - **turnbell opens the file, your editor renders it** — the model runs your
133
+ configured open command; whether that shows a *rendered* preview or raw source
134
+ is up to that app (VS Code needs a preview command, etc.). turnbell can't force a
135
+ rendered view.
136
+ - **Tidy, within its own directory** — temp files under
137
+ `~/.config/turnbell/readaloud/` are auto-removed after 24h. If the model ignores
138
+ that location and writes elsewhere, turnbell can't clean those up.
139
+
140
+ ## Webhooks
141
+
142
+ Get pinged on your phone or a chat channel when an agent finishes. turnbell
143
+ supports four providers:
144
+
145
+ - **ntfy** — push to the ntfy app/self-hosted server.
146
+ - **pushover** — Pushover push notifications.
147
+ - **slack** — Slack incoming webhook.
148
+ - **generic** — raw JSON POST to any endpoint you control.
149
+
150
+ Configure targets in `turnbell init`. Each target has a `name`, `provider`, `url`,
151
+ the `events` it fires on (`done` / `blocked`), and optional auth `headers`.
152
+ `https://` is required; plain `http://` is rejected unless you opt in with
153
+ `allowHttp`. Targets with auth headers cause the global config to be chmod'd to
154
+ `0600`.
155
+
156
+ ### The payload is metadata only
157
+
158
+ The webhook sink is the *only* part of turnbell that touches the network, and it
159
+ has a single serializer that emits exactly six fields — and nothing else:
160
+
161
+ ```json
162
+ {
163
+ "v": 1,
164
+ "ts": "2026-07-11T18:32:04.512Z",
165
+ "agent": "claude-code",
166
+ "event": "done",
167
+ "project": "my project",
168
+ "summary": "turnbell test"
169
+ }
170
+ ```
171
+
172
+ It **never** sends your working directory, your code, or the agent's response.
173
+ `project` is only the basename of the folder (dashes/underscores turned into
174
+ spaces for speaking). `summary` is the short status line the agent supplied.
175
+ Provider bodies (ntfy title, Slack text, etc.) are derived from these same
176
+ fields — there is no other path off the machine.
177
+
178
+ Preview and test before you trust it:
179
+
180
+ ```bash
181
+ turnbell test --show-payload # print the exact off-machine payload, send nothing
182
+ turnbell test --webhook # fire your configured webhook targets for real
183
+ turnbell test # drive the local sinks (voice + desktop notify)
184
+ ```
185
+
186
+ ## Hotkeys — control read-aloud
187
+
188
+ Read-aloud can run long. turnbell exposes `pause` / `resume` / `stop` so you can
189
+ bind them to system hotkeys:
190
+
191
+ - **macOS** — create Shortcuts in Shortcuts.app that run `turnbell pause`,
192
+ `turnbell resume`, and `turnbell stop`, then assign each a keyboard shortcut.
193
+ - **Linux** — bind `turnbell pause` / `turnbell resume` / `turnbell stop` to keys in your
194
+ desktop environment's keyboard settings (GNOME/KDE custom shortcuts).
195
+ - **Windows** — use AutoHotkey to map keys to the commands. **Windows is
196
+ stop-only** — `turnbell stop` works, but there is no pause/resume (no `SIGSTOP`
197
+ equivalent for the speech process).
198
+
199
+ ## Command reference
200
+
201
+ | Command | What it does |
202
+ |---|---|
203
+ | `turnbell init` | Interactive setup wizard: detect agents, wire hooks, configure sounds/webhooks. |
204
+ | `turnbell uninstall` | Reverse every wiring turnbell made — surgically removes only turnbell's own entries from shared files (your later edits survive) and deletes files turnbell created. |
205
+ | `turnbell emit` | Internal: agents' hooks call this to report an event (`--payload-stdin` / `--payload-argv`). Never breaks a turn. |
206
+ | `turnbell run -- <cmd>` | Universal wrapper: run any command and announce done/error on exit. `--announce-stream cursor` for Cursor read-aloud. |
207
+ | `turnbell test` | Fire a synthetic event to verify your setup. `--show-payload` / `--webhook`. |
208
+ | `turnbell status` | Explain, in plain words, whether turnbell is speaking here and why — scope, this project's state, and any active quiet. |
209
+ | `turnbell pause` | Pause the current read-aloud. |
210
+ | `turnbell resume` | Resume a paused read-aloud. |
211
+ | `turnbell stop` | Stop the current read-aloud. |
212
+ | `turnbell mute [on\|off]` | Mute/unmute all turnbell output for the current project (toggles if no arg). |
213
+ | `turnbell on` / `turnbell off` | Turn turnbell on or off for the current project (aliases: `unmute` / `mute`). |
214
+ | `turnbell quiet [duration\|off]` | Quiet all projects for a while (`turnbell quiet 30m`) or until `turnbell quiet off`. |
215
+ | `turnbell doctor` | Check prerequisites (voice/notify/sound tools, detected agents) with exact fix commands. |
216
+ | `turnbell --version` | Print the version (`-v`). |
217
+
218
+ turnbell also honors **quiet hours** (voice suppressed on a schedule; webhooks and
219
+ notifications configurable independently).
220
+
221
+ ## Limitations & caveats
222
+
223
+ Known boundaries, so nothing surprises you:
224
+
225
+ - **Read-aloud "speakable mode" is a nudge, not a contract.** It works by adding
226
+ an instruction to the agent's memory file (Claude Code / Codex / Gemini only).
227
+ The *model* chooses whether to keep responses speakable and move code to a
228
+ file — turnbell can't enforce it. It opens that file with your command, but
229
+ rendering is your editor's job, and turnbell only auto-cleans temp files kept
230
+ under `~/.config/turnbell/readaloud/` (see [above](#read-aloud-speakable-mode)).
231
+
232
+ - **`turnbell uninstall` is surgical, not a time machine.** It removes turnbell's *own*
233
+ additions from each shared config file using the file's current contents, so
234
+ edits you made after setup are preserved. Consequences:
235
+ - **Codex `notify` is preserved.** Codex allows a single top-level `notify`
236
+ command, so turnbell's setup temporarily *replaces* any `notify` you already
237
+ had — but it archives your original first and restores it on
238
+ `turnbell uninstall`. (If `config.toml` is deleted between setup and uninstall,
239
+ there's nothing to restore into.)
240
+ - **Config files turnbell *created* are left empty, not deleted.** If turnbell had to
241
+ create a shared file (e.g. an agent's `settings.json` that didn't exist),
242
+ uninstall strips turnbell's entries and leaves an empty `{}` rather than
243
+ guessing the file is safe to remove. Files turnbell owns outright (its slash
244
+ command, the opencode plugin) are deleted.
245
+ - **Retired legacy (v0.1.x) hooks stay retired.** Setup permanently migrates
246
+ away from the old Python integration; uninstall does not resurrect it.
247
+ - **A rare write failure is isolated.** If one adapter's file can't be written
248
+ during uninstall (e.g. permissions), turnbell skips it, reports it, and
249
+ continues; re-run `turnbell uninstall` after fixing the cause.
250
+
251
+ - **Claude Code done-alerts wait for background sub-agents.** turnbell holds the
252
+ `done` announcement while Claude Code reports in-flight delegated work
253
+ (sub-agents, workflows, teammates) so you're not pinged mid-run — you're
254
+ alerted once, at the real end. Long-lived `shell`/watcher and `monitor` tasks
255
+ are ignored (they'd otherwise silence every alert). Requires **Claude Code ≥
256
+ 2.1.145**; on older versions the alert fires as before.
257
+
258
+ - **Legacy `allowHttp` configs.** http opt-in is now per webhook target. A config
259
+ written before this change keeps its old global behavior until you re-run
260
+ `turnbell init`, which migrates it to per-target flags.
261
+
262
+ - **Codex "blocked" needs one-time trust.** Codex requires you to review and
263
+ trust its command hook once (run `codex` and approve it) before the blocked
264
+ alert fires.
265
+
266
+ - **Linux & Windows are beta**, and **Windows is stop-only** for read-aloud (no
267
+ pause/resume) — see [Agent support](#agent-support) and
268
+ [Hotkeys](#hotkeys--control-read-aloud).
269
+
270
+ ## Privacy
271
+
272
+ turnbell is local-first. Voice (`say`/equivalent), desktop notifications, and
273
+ sound all run on your machine with no telemetry and no network. The *only*
274
+ outbound traffic is webhooks you explicitly configure, and those carry the
275
+ six-field metadata payload above — never your cwd, your code, or the agent's
276
+ response. Read-aloud reads the local transcript and speaks it locally; the text
277
+ never leaves the machine.
278
+
279
+ ## Development
280
+
281
+ ```bash
282
+ npm test # vitest
283
+ npm run coverage # vitest + v8 coverage (gate: ≥ 80%)
284
+ npm run build # bundle to dist/index.js via tsup
285
+ ```
286
+
287
+ ## Feedback & contributing
288
+
289
+ Found a bug, hit a limitation, or have an idea to make turnbell better? Please
290
+ raise it — feedback and contributions are genuinely welcome:
291
+
292
+ - **GitHub Issues** — bug reports and feature requests:
293
+ [github.com/paurushrai/turnbell/issues](https://github.com/paurushrai/turnbell/issues)
294
+ - **Email** — [paurushrai96@gmail.com](mailto:paurushrai96@gmail.com)
295
+
296
+ Built by **Paurush Rai**. Portfolio and other work — and the fastest way to
297
+ reach me directly — at **[www.paurushrai.in](https://www.paurushrai.in)**.
298
+
299
+ ## License
300
+
301
+ MIT — see [LICENSE](LICENSE).