Question to resolve
Should Clawterm notify on a long-running shell command finishing in a background tab, even when the command isn't an AI agent emitting OSC 9;2?
Concrete example: I run npm test in tab B, switch to tab A. 5 minutes later the tests finish. Today: nothing. Pre-#547 also nothing — the old notifyCommandComplete was actually only wired to OSC 9;2 despite its name.
Why this is a real gap
Background long-running tasks are exactly when you want a notification. AI agents have OSC 9;2; other tools don't. Cargo builds, test runs, deploys — none of them speak OSC. If "Claude is done" gets a banner but "5-minute test suite finished" doesn't, the app feels inconsistent.
Why this is hard to do well
Detecting "command finished" reliably is the terminal-emulator problem. Options ranked by reliability:
A. PID-based idle transition
Watch the foreground process group via tcgetpgrp. When it transitions from "child of shell" → "shell itself", a command just finished. Already half-implemented (tab.ts central poll loop).
Caveats:
- REPLs (node, python) stay as foreground children but are "idle" between prompts — hard to distinguish from a still-working compile
- Multi-stage commands (
make && ./out) flap rapidly
- Shells with custom prompts may not register the transition cleanly
B. Output-quiescence
"No PTY output for N seconds while foreground PID is the shell." More reliable than pure PID, less reliable than direct shell integration. Some output during shell prompt rendering can confuse it.
C. Shell integration (OSC 133)
The right answer. OSC 133;A/B/C/D are the shell-integration prompt sequences (zsh/bash/fish all support via plugins). 133;D is "command finished" with optional exit code. We'd:
- Detect (or install) shell integration
- Listen for OSC 133;D on each pane
- Use exit code to differentiate "command finished cleanly" vs "command failed"
Cost: users need to enable shell integration (or we install it). Most terminal emulators (iTerm2, Wezterm, Ghostty, Warp) document this and either auto-install or guide the user.
Proposal
Two-phase:
Phase 1 — opt-in PID-based completion notification
Add notifications.commandCompletion: boolean config field, default false. When enabled, fire a banner when:
- Tab is hidden (background)
- Foreground PID transitions back to the shell
- The previous foreground command ran for >30s (configurable threshold)
Banner reads: Command finished: <last-foreground-process-name> (5m 12s).
Threshold + opt-in keeps the noise floor low. Users who want it turn it on; users who don't aren't surprised.
Phase 2 — OSC 133 shell integration
Detect OSC 133;D, prefer it over the PID heuristic when available. Document shell setup in README. Possibly add a "Set up shell integration" command in the palette that writes the relevant lines to .zshrc / .bashrc / config.fish.
Decision needed
- Do we want command-completion notifications at all? (Some users hate them; some want them badly.)
- If yes, opt-in or opt-out default?
- Phase 2 in scope, or separate?
My recommendation: opt-in, Phase 1 only for v1. Ship Phase 2 if users ask for it. The PID heuristic is good enough for the common "I ran a build, want to know when it's done" case.
Files (if we proceed with Phase 1)
| File |
Change |
src/config-types.ts / src/config.ts |
Add notifications.commandCompletion, notifications.commandCompletionThresholdMs |
src/tab.ts |
Track command start time on foreground PID change; on idle transition, compute duration |
src/notifications.ts |
Add notifyCommandComplete(name, durationMs, tabTitle, tabId) — different from the one we just removed, this one is opt-in and carries real info |
src/terminal-manager.ts |
Wire tab → notifications, with mute + active-tab + pane-focus checks (same gates as OSC) |
Files (Phase 2)
| File |
Change |
src/osc-handler.ts |
Add OSC 133 handlers |
docs/shell-integration.md |
New — how to enable |
src/command-palette.ts |
Add "Install shell integration" command |
Question to resolve
Should Clawterm notify on a long-running shell command finishing in a background tab, even when the command isn't an AI agent emitting OSC 9;2?
Concrete example: I run
npm testin tab B, switch to tab A. 5 minutes later the tests finish. Today: nothing. Pre-#547 also nothing — the oldnotifyCommandCompletewas actually only wired to OSC 9;2 despite its name.Why this is a real gap
Background long-running tasks are exactly when you want a notification. AI agents have OSC 9;2; other tools don't. Cargo builds, test runs, deploys — none of them speak OSC. If "Claude is done" gets a banner but "5-minute test suite finished" doesn't, the app feels inconsistent.
Why this is hard to do well
Detecting "command finished" reliably is the terminal-emulator problem. Options ranked by reliability:
A. PID-based idle transition
Watch the foreground process group via
tcgetpgrp. When it transitions from "child of shell" → "shell itself", a command just finished. Already half-implemented (tab.tscentral poll loop).Caveats:
make && ./out) flap rapidlyB. Output-quiescence
"No PTY output for N seconds while foreground PID is the shell." More reliable than pure PID, less reliable than direct shell integration. Some output during shell prompt rendering can confuse it.
C. Shell integration (OSC 133)
The right answer. OSC 133;A/B/C/D are the shell-integration prompt sequences (zsh/bash/fish all support via plugins). 133;D is "command finished" with optional exit code. We'd:
Cost: users need to enable shell integration (or we install it). Most terminal emulators (iTerm2, Wezterm, Ghostty, Warp) document this and either auto-install or guide the user.
Proposal
Two-phase:
Phase 1 — opt-in PID-based completion notification
Add
notifications.commandCompletion: booleanconfig field, default false. When enabled, fire a banner when:Banner reads:
Command finished: <last-foreground-process-name> (5m 12s).Threshold + opt-in keeps the noise floor low. Users who want it turn it on; users who don't aren't surprised.
Phase 2 — OSC 133 shell integration
Detect OSC 133;D, prefer it over the PID heuristic when available. Document shell setup in README. Possibly add a "Set up shell integration" command in the palette that writes the relevant lines to
.zshrc/.bashrc/config.fish.Decision needed
My recommendation: opt-in, Phase 1 only for v1. Ship Phase 2 if users ask for it. The PID heuristic is good enough for the common "I ran a build, want to know when it's done" case.
Files (if we proceed with Phase 1)
src/config-types.ts/src/config.tsnotifications.commandCompletion,notifications.commandCompletionThresholdMssrc/tab.tssrc/notifications.tsnotifyCommandComplete(name, durationMs, tabTitle, tabId)— different from the one we just removed, this one is opt-in and carries real infosrc/terminal-manager.tsFiles (Phase 2)
src/osc-handler.tsdocs/shell-integration.mdsrc/command-palette.ts