Conversation
Make spawned zsh shells emit the OSC 633 command/prompt sequences the parser already consumes, so command tracking has real boundaries, exit codes, and cwd instead of the keystroke heuristic's best-effort guesses. Injection (standalone/sidecar/pty-core.js): for zsh, point ZDOTDIR at shipped integration dotfiles and pass the user's real ZDOTDIR through USER_ZDOTDIR. Our .zshenv/.zprofile/.zshrc chain to the user's dotfiles, then install precmd/preexec hooks that emit OSC 633 A/B/C/D;<exit>/E/P. ZDOTDIR is handed back before the user's rc runs so zsh writes .zcompdump/.zsh_history to the user's dir, our precmd runs first so $? is the command's real exit code, and missing scripts fail safe to today's behavior. Pure-env, as reliable as the DORMOUSE_CLI_BIN PATH prepend. Both distributions spawn through here; the VS Code build copies the scripts into dist/shell-integration and points DORMOUSE_SHELL_INTEGRATION_DIR at them. Keystroke gating (terminal-state-store.ts): the first real OSC boundary a pane sees retires the keystroke command heuristic for that pane, so the two never both synthesize a command start. The heuristic's own synthesized prompt markers are flagged so they don't trip the detection. Exit status glyph (terminal-state.ts, TerminalPaneHeader.tsx): the idle title gains a trailing "x" when the last command exited non-zero (<idle> false x). It's a plain glyph in the title string so tab/OS titles carry it too; the pane header re-colors it red. Shown only with a real exit code, so it doubles as a live signal that integration is driving. bash/fish/PowerShell injection are stubbed in the docs table as follow-ups; cmd.exe has no per-command hook and stays on the keystroke fallback. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Bash gets the same authoritative command tracking as zsh: real exit codes, command boundaries, and cwd, with the keystroke heuristic as the automatic fallback when injection can't apply. - shell-integration/bash/shellIntegration.bash: bash 3.2-safe hooks (DEBUG trap for preexec, string PROMPT_COMMAND for precmd). Since --init-file conflicts with -l, the script replicates login-profile startup (/etc/profile + first of .bash_profile/.bash_login/.profile) before installing OSC 633 hooks. Disarms across PROMPT_COMMAND so the trap doesn't trip on the prompt itself or the user's PROMPT_COMMAND. - pty-core.js: applyShellIntegration injects ['--init-file', script] for bash when no explicit args are present, dropping -l; fail-safe to today's behavior if the script is missing. - pty-core.js: detectUnixShells surfaces $SHELL plus common shells that exist on disk (de-duped by basename) so the macOS picker can offer bash even when the login shell is zsh. - vscode-ext/package.json: rm -rf dist/shell-integration before cp to avoid the cp-nesting bug on rebuild. - docs + tests updated; vsix packaging verified at the flat path. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The pane header recovered "did the last command fail?" by string-matching
the fail glyph back off the rendered title (displayTitle.endsWith(' ✗')),
undoing a transform terminal-state.ts had just applied. That round-trip is
fragile: a user-renamed title ending in ✗ would be mis-detected and have
its glyph stripped.
deriveHeader now returns lastCommandFailed as a structured flag (headerPrimary
returns { text, failed }), and the header colors/strips the glyph off the flag
instead of inferring it from the string. primary still carries the glyph, so
the other title consumers (Baseboard/Wall/MobileWall) are unchanged.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Deploying mouseterm with
|
| Latest commit: |
1b6350f
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://e52efb7a.mouseterm.pages.dev |
| Branch Preview URL: | https://osc-633.mouseterm.pages.dev |
dormouse-bot
left a comment
There was a problem hiding this comment.
The injection logic is carefully built — the per-pane OSC-driven promotion gating (and the keystrokeHeuristic flag that keeps the fallback's own synthesized prompt markers from self-retiring the path that emits them) is exactly right, and the zsh ZDOTDIR hand-back / bash login-profile replication both look correct. One spec-sync gap:
docs/specs/terminal-state.md is now out of date with the header changes. This PR adds the ✗ glyph and a lastCommandFailed field, but the Header Derivation section still documents the old shape:
- The
DerivedHeadertype block declares only{ primary; secondary? }— nolastCommandFailed. - The prose says exit codes are surfaced through the alert machinery and "the header itself stays peaceful", and lists "exit-code badges" among consumers that "read it from [
pane.activity]". The new trailing✗onprimaryfor a non-zerolastCommandis the header now surfacing exit status directly, which contradicts that text.
terminal-state.md is the canonical spec for header derivation (it's the one AGENTS.md points to for terminal-state.ts / terminal-state-store.ts), and AGENTS.md asks that the spec be updated in the same change. The terminal-escapes.md update covers the OSC-633 emit side and the keystroke fallback well; the header/DerivedHeader change just needs the matching edit here. Worth also noting the per-pane keystroke-retirement invariant in this spec's Command Input Fallback section, since that's the store behavior this spec owns.
Not a correctness issue — the code and tests look sound (sidecar suite green locally; the store gating matches the new tests). Flagging the doc drift rather than blocking.
Header Derivation was still documenting the pre-OSC-633 shape: - DerivedHeader now lists lastCommandFailed; prose describes the trailing ✗ glyph appended for a non-zero last exit code and why the header reads the structured flag rather than re-parsing the title string. - Replaced the "header stays peaceful / exit-code badges read pane.activity" text, which the ✗ glyph now contradicts. - Documented the per-pane keystroke-retirement invariant in Command Input Fallback: first real OSC boundary promotes the pane to OSC-driven, the keystrokeHeuristic flag keeps the fallback's own synthesized markers from self-retiring the emitting path. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
What
Makes shells emit real OSC 633 shell-integration sequences instead of relying on the keystroke heuristic. The terminal parser already consumed the full OSC 633 family (prompt/command boundaries A/B/C/D, command line E, cwd P) — this wires up the emit side per shell, giving authoritative command boundaries, real exit codes, and reliable cwd. The keystroke heuristic stays as the automatic fallback when injection can't apply.
This lands zsh and bash from the 5-shell plan; fish and PowerShell are follow-ups, and cmd.exe stays on keystrokes (no per-command hook exists).
ZDOTDIR→ our.zshrcchains to the user's--init-file; script replicates login profileXDG_DATA_DIRSvendor conf.d-NoExit -Commanddot-sourceHow
shell-integration/zsh/):ZDOTDIRpoints at our dir;.zshrchandsZDOTDIRback to the user before sourcing their rc (so.zcompdump/.zsh_historyland in their dir, not our read-only shipped one), then installsprecmd/preexechooks. Our precmd runs first so$?is the command's real exit code before user hooks (e.g. oh-my-zsh) clobber it.shell-integration/bash/shellIntegration.bash): injected via--init-file. Since that conflicts with-l, the script replicates login-profile startup (/etc/profile+ first of.bash_profile/.bash_login/.profile) then installs OSC 633 hooks. Written for bash 3.2 (macOS system bash): DEBUG trap for preexec, stringPROMPT_COMMANDfor precmd. Disarms across the prompt hook so the trap doesn't trip on itself or the user'sPROMPT_COMMAND.pty-core.js):applyShellIntegrationsets the env var (zsh) orshellArgs(bash); fail-safe to today's behavior if the scripts are missing, and skipped when explicit args are present.detectUnixShellssurfaces$SHELLplus common shells on disk so the picker can offer bash even when the login shell is zsh.terminal-state-store.ts): once a pane sees a real OSC 633 boundary it's promoted to OSC-driven and the keystroke-synthesizedcommandStartpath is suppressed, so we never double-count.DerivedHeader.lastCommandFailedflag (not re-parsed off the title string).dist/shell-integration; the extension passesDORMOUSE_SHELL_INTEGRATION_DIR. Verified the.vsixships the scripts at the flat path.Testing
pnpm test— 545 lib tests + 68 sidecar tests green; lib typecheck clean.OSC 633;C/;D;<exit>(notsource:'user_input'), correct exit codes, user profile/rc loads intact (PATH/Homebrew/asdf)..vsixpackaged and inspected:dist/shell-integration/{bash/shellIntegration.bash,zsh/.zshrc,…}present at the flat path.🤖 Generated with Claude Code