Use case
When I'm running an AI coding agent (Claude Code) inside a wmux terminal pane, I often step away while it works. I want wmux to notify me whenever the agent needs my input — for example when it pauses to ask a question, requests a permission, or finishes its turn and is waiting for me to respond.
Right now this simply doesn't happen. After investigating, the reason is not a misconfiguration on my side — there are several missing pieces in the wmux code. Nothing in the codebase ever turns an "agent needs you" event into a wmux notification.
This issue documents what wmux does today, why the "needs input" case slips through every existing path, and the concrete parts that need to change.
How notifications work today
wmux can currently raise a notification through only two mechanisms:
- OSC escape sequences —
src/renderer/hooks/useTerminal.ts:477-512 registers handlers for OSC 9 (iTerm2), 99 (kitty) and 777 (rxvt). If a program prints one of these, wmux fires a notification.
- Shell-integration "long command finished" —
src/renderer/App.tsx:334-368. The PowerShell/bash integration scripts report report_shell_state (running → idle/interrupted), and wmux notifies only if the foreground command ran ≥ 5 seconds.
The hooks that wmux installs into Claude Code are configured in src/main/claude-context.ts:116 (ensureClaudeHooks) and are PostToolUse only (trackedTools at claude-context.ts:157). Those feed the sidebar/diff view via hook.event (src/main/index.ts:1023) — they are not notification triggers.
Why "agent needs a response" never fires
Each existing path misses this specific case:
- No
Notification / Stop hook is installed. Claude Code signals "I need permission/input" via its Notification hook and "I finished my turn" via its Stop hook. ensureClaudeHooks only writes PostToolUse entries, so neither event ever reaches wmux.
- The terminal bell is swallowed. Claude Code's default "I'm waiting" signal is the terminal bell (
\x07). useTerminal.ts registers OSC handlers but no terminal.onBell handler, so the bell produces nothing.
- The shell-state notifier structurally can't catch it. Claude Code is itself a single long-running foreground process. While it pauses to ask a question, the shell is still in the
running state (it hasn't returned to the prompt), so report_shell_state never flips to idle mid-session. The 5-second "command finished" notification therefore never applies to an in-session prompt.
Bonus bug: wmux notify is a no-op
While investigating I found that the wmux notify <text> CLI command is silently dropped:
- The CLI sends it as a V1 message —
src/cli/wmux.ts:341 (notify <surfaceId> <text>).
- The main process forwards V1 messages to the renderer as
METADATA_UPDATE — src/main/index.ts:340-351.
- The renderer's
cmd.command switch — src/renderer/App.tsx:309 — has no notify case, so the message is dropped.
This means even a manual hook calling wmux notify would currently do nothing.
What needs to change
1. Install Notification and Stop hooks (src/main/claude-context.ts)
Extend ensureClaudeHooks so it also writes Notification and Stop entries into ~/.claude/settings.json (alongside the existing PostToolUse block, without clobbering user hooks). These should invoke the wmux helper so wmux learns when the agent is waiting / has finished.
2. Route those hook events to a real notification (src/main/index.ts)
The hook.event handler (index.ts:1023) currently only drives the diff view. It should recognize the new Notification/Stop event types and fire an actual wmux notification (NOTIFICATION_FIRE) tied to the originating surface/workspace.
3. Add a terminal-bell fallback (src/renderer/hooks/useTerminal.ts)
Register a terminal.onBell handler next to the existing OSC handlers (~useTerminal.ts:477) that fires a notification. This covers agents/programs that signal via the bell instead of a hook or OSC sequence.
4. Fix the dead wmux notify path (src/renderer/App.tsx)
Add a notify case to the metadata cmd.command switch (App.tsx:309) so wmux notify <text> actually raises a notification. This makes the CLI usable as a manual/scriptable notification trigger.
Acceptance criteria
Use case
When I'm running an AI coding agent (Claude Code) inside a wmux terminal pane, I often step away while it works. I want wmux to notify me whenever the agent needs my input — for example when it pauses to ask a question, requests a permission, or finishes its turn and is waiting for me to respond.
Right now this simply doesn't happen. After investigating, the reason is not a misconfiguration on my side — there are several missing pieces in the wmux code. Nothing in the codebase ever turns an "agent needs you" event into a wmux notification.
This issue documents what wmux does today, why the "needs input" case slips through every existing path, and the concrete parts that need to change.
How notifications work today
wmux can currently raise a notification through only two mechanisms:
src/renderer/hooks/useTerminal.ts:477-512registers handlers for OSC9(iTerm2),99(kitty) and777(rxvt). If a program prints one of these, wmux fires a notification.src/renderer/App.tsx:334-368. The PowerShell/bash integration scripts reportreport_shell_state(running→idle/interrupted), and wmux notifies only if the foreground command ran ≥ 5 seconds.The hooks that wmux installs into Claude Code are configured in
src/main/claude-context.ts:116(ensureClaudeHooks) and arePostToolUseonly (trackedToolsatclaude-context.ts:157). Those feed the sidebar/diff view viahook.event(src/main/index.ts:1023) — they are not notification triggers.Why "agent needs a response" never fires
Each existing path misses this specific case:
Notification/Stophook is installed. Claude Code signals "I need permission/input" via itsNotificationhook and "I finished my turn" via itsStophook.ensureClaudeHooksonly writesPostToolUseentries, so neither event ever reaches wmux.\x07).useTerminal.tsregisters OSC handlers but noterminal.onBellhandler, so the bell produces nothing.runningstate (it hasn't returned to the prompt), soreport_shell_statenever flips toidlemid-session. The 5-second "command finished" notification therefore never applies to an in-session prompt.Bonus bug:
wmux notifyis a no-opWhile investigating I found that the
wmux notify <text>CLI command is silently dropped:src/cli/wmux.ts:341(notify <surfaceId> <text>).METADATA_UPDATE—src/main/index.ts:340-351.cmd.commandswitch —src/renderer/App.tsx:309— has nonotifycase, so the message is dropped.This means even a manual hook calling
wmux notifywould currently do nothing.What needs to change
1. Install
NotificationandStophooks (src/main/claude-context.ts)Extend
ensureClaudeHooksso it also writesNotificationandStopentries into~/.claude/settings.json(alongside the existingPostToolUseblock, without clobbering user hooks). These should invoke the wmux helper so wmux learns when the agent is waiting / has finished.2. Route those hook events to a real notification (
src/main/index.ts)The
hook.eventhandler (index.ts:1023) currently only drives the diff view. It should recognize the newNotification/Stopevent types and fire an actual wmux notification (NOTIFICATION_FIRE) tied to the originating surface/workspace.3. Add a terminal-bell fallback (
src/renderer/hooks/useTerminal.ts)Register a
terminal.onBellhandler next to the existing OSC handlers (~useTerminal.ts:477) that fires a notification. This covers agents/programs that signal via the bell instead of a hook or OSC sequence.4. Fix the dead
wmux notifypath (src/renderer/App.tsx)Add a
notifycase to the metadatacmd.commandswitch (App.tsx:309) sowmux notify <text>actually raises a notification. This makes the CLI usable as a manual/scriptable notification trigger.Acceptance criteria
wmux notify "hello"produces a visible wmux notification.~/.claude/settings.jsonare preserved (not overwritten).