Watchman
Running several Claude Code sessions in VS Code terminal tabs? Watchman puts a bell on a tab when
Claude finishes (or needs your permission), keeps it there until you open that tab, and makes the
tab show Claude's own title, also when Claude runs inside tmux. It can also send a notification to
your phone through ntfy when Claude is waiting for you.
Claude's own spinner and tab titles are left alone, so you can read two things at a glance:
| Tab shows |
Meaning |
| ✳ only |
Idle, already seen |
| ✳ + bell |
Finished, not seen yet |
| spinner + bell |
Asked for something, not seen yet |
| spinner only |
Working |
Commands
Watchman turns itself on the first time it runs on a machine, over SSH or in a dev container
(everything under What Turn On changes), and tells you so. Restart Claude
Code sessions that were already running to get the bell. It doesn't turn back on where you turned
it off; set watchman.turnOnAutomatically to false to always do it by hand.
Everything is in the Command Palette (Cmd+Shift+P / Ctrl+Shift+P):
- Watchman: Turn On sets everything below up, for example after Turn Off.
- Watchman: Turn Off stops all bells immediately, puts your VS Code settings back and removes
the tmux block and the empty
TMUX. No Claude restart needed.
- Watchman: Show Status opens a report in the Output panel: what is set up on this machine, the
tmux server's settings, each Claude session (tmux pane or tab) and anything that would stop its
bell, title or spinner, and which terminal tabs have the ⚠ icon and why.
- Watchman: Mark Terminal as Unread puts the bell back on the current terminal tab. It stays
until you switch to another tab and open this one again.
- Watchman: Attach Detached tmux Sessions opens a terminal tab for every tmux session nothing
is attached to (for example after you reconnect), each showing its session's title (Claude's).
Sessions already attached somewhere are left alone. Detaching closes the tab.
- Watchman: Turn On Phone Notifications (ntfy) / Turn Off Phone Notifications (ntfy), and
Configure Phone Notifications (ntfy) to change the topic, message, title or server and send a
test. See Phone notifications.
What Turn On changes
Claude Code (~/.claude)
- Adds three hooks to
~/.claude/settings.json (Stop, Notification, UserPromptSubmit). Your
other settings and hooks are kept; a backup is saved as settings.json.watchman-backup.
- Installs
~/.claude/hooks/watchman-bell.sh.
- Replaces the hooks of the older "Unread Bell" extension (
claude-unread-bell.sh) and earlier
hand-made versions (unread.sh, tab-status.sh, tab-title.sh), so you don't get two bells.
- If tmux is installed:
"env": {"TMUX": ""} in ~/.claude/settings.json. Claude Code doesn't
animate its title (the ◐ working spinner) when it sees $TMUX; the empty value hides it, however
Claude was started, and sessions already running pick it up at their next prompt. Side effects:
commands Claude runs see an empty $TMUX (tmux commands still reach your default server), and
outside tmux Claude skips one rendering optimization (scroll regions), as it always does inside
tmux. Left alone if you've set TMUX there yourself.
"env": {"CLAUDE_CODE_IDE_SKIP_AUTO_INSTALL": "1"} in ~/.claude/settings.json, so Claude Code
stops installing Claude Code for VS Code each time it starts in a VS Code terminal. That
extension is what puts the ⚠ on your tabs (see the warning icon).
Watchman doesn't uninstall it: if you don't use its panel or editor diffs, uninstall it yourself
and it stays gone. If you keep it, VS Code still updates it. Turn it off with the
watchman.stopClaudeExtensionAutoInstall setting.
VS Code (your user settings; Turn Off puts back what was there before)
terminal.integrated.enableVisualBell: true, terminal.integrated.bellDuration: 1500, and the
terminal bell sound off (accessibility.signals.terminalBell).
terminal.integrated.tabs.title: "${sequence}" and terminal.integrated.tabs.description: "${process}", so a tab shows the title the program sets ("✳ Claude Code") instead of "bash" or
"tmux". Only if you haven't set these yourself. Plain shells then show the title your shell
sets (often user@host: dir).
tmux (only if tmux is installed)
- Adds a marked block to the end of
~/.tmux.conf (or ~/.config/tmux/tmux.conf if that's the
one you use) and applies it to a running tmux server:
set-titles on, set-titles-string "#T", allow-rename on (Claude's title reaches the tab),
bell-action any, visual-bell off, monitor-bell on (the bell reaches the tab).
Options you already set yourself are skipped, so yours win.
- Hooks at index 70, so your own hooks on these events are kept:
client-attached and
client-session-changed (attaching to a session counts as seeing it and clears its bell), and
pane-title-changed. With $TMUX hidden, Claude asks the terminal whether it can show images,
and tmux makes that query the pane title (Gi=31,s=1,v=1,a=q,t=d,f=24;AAAA); the hook puts the
pane's previous title back.
- If something rewrites your tmux config later (a dev container's setup script, say), Watchman
puts the block back the next time VS Code starts.
- Removes the
claude function Watchman 0.2 added to ~/.bashrc / ~/.zshrc (the empty TMUX
above replaces it).
Uninstalling removes the Claude hooks (bell and ntfy), the env values above, the scripts and the
tmux block. Run Turn Off before uninstalling if you also want your VS Code settings restored.
To have Watchman in every new dev container or SSH host without installing it by hand, add it to
the container's devcontainer.json (customizations.vscode.extensions), or to the
dev.containers.defaultExtensions / remote.SSH.defaultExtensions user settings.
Phone notifications (ntfy)
Off until you turn them on, separately on each machine or dev container.
- Turn On Phone Notifications (ntfy) uses the
watchman.ntfy.topic setting (default
bruno_agents; change it with Configure Phone Notifications), then adds Stop and
Notification hooks to ~/.claude/settings.json that run ~/.claude/hooks/watchman-ntfy.sh.
It sends one notification when Claude finishes or asks for permission, not Claude's
"still waiting" reminder a minute later.
- What is sent: only the title and message you set up, with
{env} (where the window runs:
the dev container profile, e.g. misc-hpcws2 from MISC_DEVCONTAINER=hpcws2, else the host
name) and {project} (the folder Claude is working in) filled in. Nothing Claude writes is
sent. Default: "Claude is waiting for your response ({env})".
- Settings:
watchman.ntfy.topic, .message, .title, .server (default https://ntfy.sh).
Watchman writes them to ~/.claude/watchman-ntfy.env for the hook.
- ntfy.sh topics are public: anyone who knows the name can read them, so use your own,
hard-to-guess topic. If the environment has
HIGH_RISK=true, Watchman asks before turning on.
- If another hook already posts to ntfy (for example a dev container's
~/.claude/hooks/notify.sh),
Watchman offers to remove it so you don't get two notifications.
Troubleshooting
Run Watchman: Show Status first; each ✗ says what to do.
No ◐ spinner inside tmux. Claude Code doesn't animate its title when it sees $TMUX; the
empty TMUX in ~/.claude/settings.json hides it. A session that was running when it was added
starts animating at its next prompt. Sessions started with a different CLAUDE_CONFIG_DIR don't
read ~/.claude/settings.json, so they get neither the spinner nor the bell.
Known issue: the warning icon on a terminal tab
VS Code shows a ⚠ on a terminal tab ("The following extensions want to relaunch the terminal…")
when an extension changes the environment of a terminal that is already running. Claude Code for
VS Code picks a new random port for CLAUDE_CODE_SSE_PORT each time the window (re)loads or
reconnects, so every tab that was already open gets the icon
(anthropics/claude-code#3301). The icon
takes the spot where Watchman's bell shows. VS Code 1.141 has no setting to hide
it (the old terminal.integrated.environmentChangesIndicator is gone;
terminal.integrated.environmentChangesRelaunch, on by default, only relaunches tabs you haven't
typed in), and an extension can't change another extension's environment, so Watchman can't turn
it off.
- See what it is: with the tab open, run Terminal: Show Environment Contributions, or
Watchman: Show Status (on Linux it lists each tab's port against the current one).
- Stop it: if you don't use Claude Code for VS Code's panel or editor diffs, uninstall it.
claude in a terminal still works; it just shows diffs in the terminal and doesn't see your
editor selection. Watchman keeps Claude Code from installing it again.
- Clear it for now: Terminal: Relaunch Active Terminal, or open a new tab. With tmux,
relaunching costs nothing; just attach again.
How it works
When Claude finishes, a hook rings the terminal bell once a second, which keeps VS Code's bell icon
on the tab. When you open the tab, or send Claude a message, the ringing stops and the bell fades.
Inside tmux, Claude rings its pane and tmux passes the bell on to the VS Code tab attached to that
session; opening that tab, or attaching to the session, stops it. A bell on a detached session
waits and shows up on whichever tab you attach from.
Claude Code sessions in other terminal apps (iTerm, Terminal.app, …), or in a tmux server started
outside VS Code, are not affected.
Works on macOS and Linux, including VS Code Remote-SSH and dev containers. Not supported on Windows.
This is a community extension and is not made by or affiliated with Anthropic.
| |