Skip to content

TLGINO/claude-bell

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 
 
 
 
 

Repository files navigation

🔔 claude-bell

A tiny ping for Claude Code and opencode. Get a small bell sound the moment your agent needs your attention — a permission prompt, or finishing and going idle waiting for you — so you can look away and come back when it matters. It stays quiet during normal work.

  • One script, two targets. claude (a hook inlined into settings.json) or opencode (a single auto-loaded plugin file). No files copied around for either beyond what each tool expects.
  • Works over SSH. The bell travels through your terminal, so a session on a remote server still pings your laptop.
  • Any OS. macOS, Linux, Windows (WSL / Git Bash) — the sound is made by your local terminal, not the machine the agent runs on.

Install

Pick a target: claude (default), opencode, or all (both).

sh install.sh              # claude (default)
sh install.sh opencode     # opencode
sh install.sh all          # both

or via the one-liner (defaults to claude; pass a target with sh -s --):

curl -fsSL https://raw.githubusercontent.com/TLGINO/claude-bell/master/install.sh | sh
curl -fsSL https://raw.githubusercontent.com/TLGINO/claude-bell/master/install.sh | sh -s -- opencode

You can also set TARGET=opencode sh install.sh. Then restart the tool (or start a new session). That's it.

No installer for opencode? Just copy opencode-bell.js into ~/.config/opencode/plugins/ (global) or .opencode/plugins/ (project) and restart opencode.

How it works

claude target

Adds a single hook to your Claude Code settings.json — the Notification event, scoped with the matcher permission_prompt|idle_prompt:

  • permission_prompt → Claude is blocked needing your approval.
  • idle_prompt → Claude finished and has been waiting for your next prompt (Claude Code debounces this ~60s, so it only fires once you've actually left).

It deliberately does not bind the Stop hook. Stop fires on every assistant turn — and after every subagent — so a bell on Stop rings constantly during normal work. Notification fires only when Claude actually wants you.

As a fallback for older Claude Code (before typed Notification matchers), the hook also reads the stdin payload and rings only when notification_type is permission_prompt, idle_prompt, or absent — staying silent for auth_success and MCP elicitation_*.

opencode target

opencode has no hooks field in its config; instead it auto-loads plugins (JS/TS files) from:

  • ~/.config/opencode/plugins/ — global
  • .opencode/plugins/ — project

This installs one plugin, opencode-bell.js, that subscribes to the opencode event bus and rings on two events:

  • session.idle → opencode finished a turn and is waiting for your next prompt (≡ Claude Code's idle_prompt). Only root sessions ring — subagent/child idles (e.g. a finished Task agent) are tracked via session.created and suppressed, so you only hear it when the main session actually needs you back.
  • permission.asked → opencode is blocked needing your approval (≡ Claude Code's permission_prompt).

It deliberately does not ring on message.updated or tool.execute.* — those fire constantly during normal work. session.idle fires only when opencode actually wants you back, and subagent idles are filtered out so multi-step tasks with delegation stay quiet until they're truly done.

The bell itself

Both targets run a self-contained one-liner (Claude: detached hook subprocess; opencode: shell spawned via the plugin's Bun $) that writes the terminal bell character to your terminal. There's no controlling terminal, so /dev/tty is usually gone (No such device or address). The bell builds an ordered list of candidate terminals and rings the first one that accepts a write:

# 1. $SSH_TTY                     remote / SSH
# 2. ancestor terminals: walk parent PIDs (ps -o ppid=), read each tty (ps -o tty=);
#    try both /dev/$d (Linux -> /dev/pts/0) and /dev/tty$d (macOS s000 -> /dev/ttys000)
# 3. /dev/tty                     Git Bash/MSYS, mintty, real controlling ttys
# 4. stdout                       last resort
printf '\a' > "$t"   # ×2, with a short gap

This is why it works across OSes and over SSH: on a remote box $SSH_TTY points back at your laptop's terminal; on a local session the ancestor walk finds the claude/opencode process's terminal (/dev/pts/0 etc.) that the detached/in-process hook can't reach via /dev/tty alone.

Configure

Edit the variables at the top of install.sh before running:

Variable Default Meaning
BEEPS 2 beeps per ping
GAP 0.3 seconds between beeps
  • claude respects CLAUDE_CONFIG_DIR if you've moved your Claude config.
  • opencode respects OPENCODE_CONFIG_DIR / XDG_CONFIG_HOME, and also reads OPENCODE_BELL_BEEPS / OPENCODE_BELL_GAP env vars at runtime (opencode inherits your shell environment), so you can tune without reinstalling.

To change which events ring for opencode, edit the filter in the event hook of opencode-bell.js.

Hear nothing?

The script sends a test bell at the end of install. If it's silent, your local terminal emulator's audible bell is turned off:

  • iTerm2: Profiles → Terminal → uncheck Silence bell
  • Windows Terminal: settings → "bellStyle": "audible"
  • GNOME Terminal / many Linux: Preferences → Sound → enable Terminal bell
  • macOS Terminal.app: Settings → Profiles → Advanced → Audible bell

Uninstall

sh uninstall.sh              # claude (default)
sh uninstall.sh opencode     # opencode
sh uninstall.sh all          # both
  • claude: removes only the bell hook (tagged claude-bell) from Notification and Stop; your other hooks are left untouched. Backs up settings.json before editing.
  • opencode: removes opencode-bell.js (and any .bak copies) from the global plugins dir, plus a project-local install if present.

Requirements

  • both: sh plus ps/tr/sleep (standard on macOS, Linux, WSL, Git Bash).
  • claude: additionally jq, python3/python, or node (any one) to edit the JSON. If none are present, the installer prints the snippet to paste manually.
  • opencode: nothing extra — the installer just writes a file.

License

MIT

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors