Skip to content

dor ensure: add --restart to interrupt and re-run a matching surface#158

Merged
nedtwigg merged 10 commits into
mainfrom
ensure-fixup
Jun 20, 2026
Merged

dor ensure: add --restart to interrupt and re-run a matching surface#158
nedtwigg merged 10 commits into
mainfrom
ensure-fixup

Conversation

@nedtwigg

Copy link
Copy Markdown
Member

Summary

Adds a --restart flag to dor ensure. When a surface is already running the target command, --restart interrupts it (Ctrl+C), waits for the shell to return to its prompt, re-runs the command in place, and blocks until it's live again. If no surface matches, it behaves like a plain ensure and creates one.

  • --restart drives the live PTY directly, so it works for minimized doors too (their PTY keeps running). A restarted surface keeps its minimized/visible state.
  • The host blocks while interrupting/respawning, so the client request timeout is bumped to 60s for this one command; everything else keeps the snappy default.
  • Restart progress is tracked by polling the integration-derived terminal state (currentCommand clears on prompt, then reports the command live again) rather than guessing at timings.
  • New restarted status surfaces in text and JSON output, with help text and snapshots updated.

Test plan

  • dor/test/cli-output.test.mjs covers --restart plumbing (sends restart: true, renders restarted surface:3).
  • Help snapshots regenerated for the new flag.
  • tsc --noEmit passes in lib.

🤖 Generated with Claude Code

nedtwigg and others added 2 commits June 19, 2026 12:49
`dor ensure --restart -- <command>` now restarts a surface that is already
running the command instead of no-opping. The host interrupts the live
command (Ctrl+C), waits for the shell to return to its prompt, types the
command again, and waits for it to go live — driving the PTY directly so it
works for minimized doors too. The CLI blocks until the restart completes
(with a bumped 60s request timeout for that one command). With no matching
surface, --restart behaves like a plain ensure and creates one.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jun 19, 2026

Copy link
Copy Markdown

Deploying mouseterm with  Cloudflare Pages  Cloudflare Pages

Latest commit: aee4ac0
Status: ✅  Deploy successful!
Preview URL: https://4fb8baf5.mouseterm.pages.dev
Branch Preview URL: https://ensure-fixup.mouseterm.pages.dev

View logs

nedtwigg and others added 6 commits June 19, 2026 13:15
Spawn a real interactive shell for `dor split/ensure -- <command>` and type
the command into it once it reaches a prompt, rather than launching
`shell -c command`. A `-c` invocation has no prompt behind it, so
`dor ensure --restart`'s Ctrl+C would take the shell down with the command
instead of returning to a re-runnable prompt.

typeCommandWhenPromptReady waits for the seeded currentCommand to clear (the
first-prompt signal) before writing the keystrokes, falling back to a
best-effort send on timeout.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
`dor ensure -- <command>` types the command into a real interactive shell
rather than running `shell -c command`. That injection bypasses the keystroke
heuristic, so only a shell whose integration emits OSC 633 boundaries ever
reports the command back. Without it the surface can never be matched, so each
ensure silently spawns a duplicate (and --restart can never reuse it).

When ensure creates a surface, the host now waits for the new shell to report
OSC 633 and, if it never does, returns a `warning` the CLI prints to stderr
(stdout stays clean). Matched/existing surfaces still respond instantly; only a
non-integration shell waits out the detection window, so the plain-ensure client
timeout is raised to outlast it.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…cwd, casing)

On Windows + Git Bash the shell integration reports a pane's cwd as a POSIX path
(`/c/Users/...`) while the rest of the system speaks native Windows
(`C:\Users\...`). Three symptoms, all from that one dialect split, all verified
by measurement:

1. `dor ensure -- cmd` twice spawned two surfaces: surfaceRunsCommand compared
   the bash POSIX cwd against the CLI's Windows cwd by exact string and never
   matched. Canonicalize both sides (MSYS `/c/`->`C:\`, slashes, drive case)
   before the compare. Anchored so genuine POSIX paths stay untouched off-Windows.

2. The `dor` CLI mangled a pure-POSIX `$PWD`: `path.resolve('/c/Users/x')` on
   win32 yields `C:\c\Users\x`. Fold the MSYS drive form to a native drive before
   resolving.

3. The pty spawn passed the cwd through verbatim: a POSIX `/c/...` failed the
   directory check and silently fell back to the home dir, and a lowercase drive
   (e.g. VS Code's workspaceFolder.fsPath) propagated into process.cwd() of the
   shell and everything launched in it. Measured: the spawn cwd's drive-letter
   case is preserved verbatim into a child's process.cwd(), so this is what
   fragments Claude Code's per-directory state (`c--` vs `C--`). Canonicalize the
   spawn cwd to one Windows form in the shared resolveSpawnConfig (covers both the
   Tauri sidecar and the VS Code host via lib/pty-core.cjs).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
`dor ensure` needs OSC 633 shell integration to track a command. cmd.exe has
none, and the old behavior was a slow, confusing half-failure: the webview
blocked 15s waiting for OSC while the sidecar control server timed out at 10s
(printing "timed out waiting for surface.ensure"), and the command got typed into
the new split anyway on a separate best-effort timer. --restart additionally
fired Ctrl+C into cmd.exe, leaving a stuck "Terminate batch job (Y/N)?".

Now:
- cmd.exe (explicitly configured) errors instantly with a clear message and no
  split: "dor ensure requires OSC 633 shell integration, which cmd.exe does not
  provide. Run it from a shell with Dormouse integration, such as Git Bash or
  PowerShell."
- Any other shell that lacks integration: create the split, wait up to 8s for OSC
  633, then kill the throwaway split and return the same error.
- The command is only typed once OSC 633 is confirmed (requireIntegration gate in
  typeCommandWhenPromptReady); it is dropped, never best-effort typed, when
  integration is absent, so nothing half-runs. dor split keeps its best-effort
  typing into any shell.
- restartSurfaceInPlace guards on isPaneOscDriven so Ctrl+C never reaches a
  non-integration shell.
- The sidecar control-server timeout (10s) was preempting the CLI's 20s/60s
  budgets and is raised to 65s so the CLI deadline always governs.
- The earlier soft stderr "warning" plumbing is removed in favor of the hard
  error.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
nedtwigg and others added 2 commits June 20, 2026 07:53
`code --install-extension` installs by renaming the extension folder; on Windows
a running VS Code keeps the old Dormouse extension's node-pty native modules
loaded and locks that folder, so the rename fails with a cryptic EPERM retry
stack. Capture the install output and, on that lock signature, print a plain
"quit all VS Code windows and re-run" banner and leave the built .vsix in place
for a retry. Other failures still surface non-zero.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@nedtwigg nedtwigg merged commit 53e02bf into main Jun 20, 2026
9 checks passed
@nedtwigg nedtwigg deleted the ensure-fixup branch June 20, 2026 17:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants