Kick it off. Walk away. Get pinged. Answer from anywhere.
TaskBell lets your AI coding agents reach you when you're not at your desk. It pings you the moment an agent finishes, errors, or needs you — and it's two-way: agent questions and command approvals arrive as push notifications you can answer straight from your lock screen. The agent gets your answer and keeps working.
Free, MIT-licensed, no account, no backend — everything runs on your machine.
Works with: Cursor · Claude Code · Codex CLI · Gemini CLI · Windsurf — and any other MCP-capable agent. Runs on: macOS · Windows · Linux (one Node runtime, native notifications on each).
npx taskbell@latest setupInteractive: picks up your installed agents automatically, generates a private push topic with a QR code, optionally enables the remote approval gate. Non-interactive:
npx taskbell setup --agents cursor,claude-code --ntfy auto --approvals --yesThen restart your agents. Phone side: install the free ntfy app — Android subscribes by scanning the printed QR with your camera; iPhone: open the app, tap +, enter the printed topic.
Updating: re-run the same command. Setup is idempotent — it refreshes the runtime in ~/.taskbell, keeps your topic and config, and never touches other hooks or MCP servers. Restart your agents afterwards.
| Event | Surface |
|---|---|
| ✅ Agent finished / ❌ errored | Desktop banner + phone push (suppressed while you're already looking at the agent) |
| ❓ Agent has a question | Push with tappable answers + desktop banner that opens the answer page — first answer from any surface wins, agent continues automatically. Late or duplicate answers see "already answered" instead of a false confirmation. |
| 🔐 Agent wants to run a risky command | Push showing the exact command; Approve/Deny from your phone; timeout = deny |
The ask tool supports 1–5 questions per round-trip, optional free-text answers, and an optional context block (error output, diffs, summaries) so you can decide from any device without opening the editor.
sequenceDiagram
participant Agent as Agent (any MCP client)
participant TB as taskbell (local process)
participant Ntfy as ntfy.sh (free)
participant Phone as Your phone
Agent->>TB: ask("Deploy to prod?", [Yes, No])
TB->>Ntfy: publish question + action buttons
Ntfy->>Phone: push notification
Phone->>Ntfy: tap Yes (POSTs reply)
TB->>Ntfy: poll reply topic (nonce-matched)
TB-->>Agent: "Yes" — agent continues
| Agent | done | error | question (turn-end) | ask from phone | remote approvals |
|---|---|---|---|---|---|
| Cursor | ✅ | ✅ | ✅¹ | ✅ (MCP) | ✅ (hook gate) |
| Claude Code | ✅ | — | ✅² | ✅ (MCP) | ✅ (hook gate) |
| Codex CLI | — | — | — | ✅ (MCP) | — |
| Gemini CLI | — | — | — | ✅ (MCP) | — |
| Windsurf | — | — | — | ✅ (MCP) | — |
¹ Fires only when a turn ends waiting on Cursor's native question card, which bypasses hooks and MCP entirely — this push is the only remote signal for it. Blocking questions normally go through the ask MCP tool instead.
² Via the Notification hook (⏳ waiting / 🔐 permission).
Adding an agent is one small classifier in TypeScript (lifecycle hooks) or nothing at all (MCP tools work wherever MCP does).
| OS | Desktop banner | Focus suppression | Phone push + asks |
|---|---|---|---|
| macOS | terminal-notifier (clickable, installed by setup) |
✅ | ✅ |
| Windows | Native toast (PowerShell WinRT, click opens answer page) | — | ✅ |
| Linux | notify-send (install libnotify-bin if missing) |
— | ✅ |
Everything else — MCP server, asks, approval gate, config — is identical on all three. Delivery is the only OS-specific code, and it lives in one module.
~/.config/taskbell/config (KEY="value" lines; env vars override):
| Key | Default | Meaning |
|---|---|---|
NTFY_TOPIC |
set by setup | Private push topic. Treat it like a password. |
NTFY_SERVER |
https://ntfy.sh |
Point at a self-hosted ntfy instance if you prefer. |
NTFY_TOKEN |
— | Access token for reserved topics / self-hosted auth. |
ANSWER_URL |
hosted answer page | Public answer page; off disables the phone answer link. |
NOTIFY_WHEN_FOCUSED |
0 |
1 = notify even while the agent's app is frontmost (macOS-only check). |
SOUND |
0 |
1 = banner plays a sound. |
AUDIBLE |
0 |
1 = chime + spoken alert, macOS (bypasses Notification Center). |
GATE_ALL |
0 |
1 = approval gate on every command, not just risky-looking ones. |
| Command | What it does |
|---|---|
taskbell setup |
Interactive installer (see flags above). |
taskbell mcp |
Run the MCP server (agents launch this; you rarely will). |
taskbell test-notify |
Smoke-test the banner + push path. |
taskbell test-ask [--multi] [--free] [--options "A,B"] [--context "..."] |
Fire a live two-way ask. |
- Your ntfy topic is a bearer token. Generated crypto-random; whoever knows it can read and answer. Use
NTFY_TOKEN/self-hosting for hard guarantees. - Replies are single-use. Random id + nonce per request; first valid reply wins; duplicates, replays, and late answers are dropped. The answer page detects resolved asks and shows "already answered" / "expired" instead of accepting input that would go nowhere.
- Fail-closed gate. Timeout, malformed reply, or bad nonce → deny. Every decision is appended to
~/.config/taskbell/approvals.log. - What-you-see-is-what-runs. Approval pushes show the exact command from the hook payload, never a paraphrase. Commands too long to display fully withhold direct Approve buttons and require the full review page.
- Injection-safe plumbing. LLM-authored text goes through argv arrays (
execFile), never shell interpolation; web surfaces render exclusively viatextContent; question data travels in URL fragments, which never reach any server. - Honest limitations. The risky-command regex is a checkpoint, not a sandbox (
GATE_ALL=1gates everything). TaskBell's gate runs in addition to your agent's native permission system: if the agent's own allowlist also prompts (e.g.sudoin Cursor), you'll approve twice, and that native in-chat card can't be pushed to your phone. For remote use, pair the gate with auto-run/allowlisted commands.
macOS: banners don't appear?
- Enable your agent's own notifications too (e.g. Cursor Settings → "Show system notifications") — registered GUI apps get through on managed machines that silently drop CLI notifications.
- macOS Sequoia ignores notifications from unregistered bundle IDs. Setup installs and registers
terminal-notifier.appto handle this; on some managed devices CLI banners still never appear — that's whatAUDIBLE=1and the phone push are for. - System Settings → Notifications → enable Allow notifications when mirroring or sharing if you use external displays.
- Chimes follow the system default audio output — check where your Bluetooth audio is routed.
- Every hook invocation logs to
~/.config/taskbell/hook.log, including why it stayed silent.
Windows: toasts respect Focus Assist / Do Not Disturb — check the Action Center if a banner seems missing. Linux: banners need notify-send (sudo apt install libnotify-bin / dnf install libnotify).
cd cli
npm run build # bundle dist/taskbell.cjs + copy assets
npm run typecheck
npm test # vitest: classifiers, per-OS banner commands, gate regexes, ask parsing
node dist/taskbell.cjs test-ask --multi --context "example context"Repo layout: [cli/](cli/) npm package (setup, MCP server, hooks, gates, notifier) · [answer/](answer/) static answer page · [site/](site/) landing page.
- Cross-platform delivery: macOS, Windows toasts, Linux
notify-send - Menu bar companion subscribing to the ntfy topic (live "waiting on" list)
- Rich context in asks (error output / diffs / summaries on every surface)
- Multi-question asks in one round-trip
- Remote approval gate with audit log
- One-command installer with agent auto-detection
MIT © Shubham Shetty