Skip to content

dor CLI: argv-over-protocol + command+cwd ensure (stacked on #121)#136

Merged
nedtwigg merged 10 commits into
mainfrom
add-dor-cli
Jun 16, 2026
Merged

dor CLI: argv-over-protocol + command+cwd ensure (stacked on #121)#136
nedtwigg merged 10 commits into
mainfrom
add-dor-cli

Conversation

@nedtwigg

@nedtwigg nedtwigg commented Jun 16, 2026

Copy link
Copy Markdown
Member

Stacked on #121 (cli-expanded). Review/merge #121 first; this PR's diff shows only the incremental work on top.

What this adds over #121

  • Argv carried across the control protocol: the command field now carries raw string[] argv; the host quotes once via a node-free shell-quote.ts (renamed from shell-command.ts, shared into the browser webview), fixing double shell-quoting.
  • ensure idempotency reworked from --title to command + cwd matching: a surface is matched by the live shell-integration–reported command and a cwd-normalized key, so re-running dor ensure from the same worktree reuses the surface and two different worktrees stay distinct (exact cwd match, no trailing-slash trimming).
  • Simplify passes: dedupe errorMessage, and share renderJson / requireControlClient across command modules.
  • Supporting lib/terminal-state* plumbing (command + exit-status header state).

Testing

  • pnpm run build && node --test test/*.test.mjs in dor/48/48 pass.

🤖 Generated with Claude Code

nedtwigg and others added 10 commits May 30, 2026 14:39
Hoist the repeated `error instanceof Error ? error.message : String(error)`
coercion into a shared errorMessage() helper, and remove the
requestedWorkingDirectory field from Surface which was computed but never
read by any renderer.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The protocol command field now carries raw string[] argv instead of a
pre-quoted string. The CLI sends argv verbatim; the host (Wall) does the
single shell-correct quote for the target pane's shell. This removes the
double-quoting that happened when the CLI quoted for the caller shell and
the host re-quoted for the target shell.

Quoting moves into a node-free shell-quote.ts module so it can bundle into
both the CLI and the browser webview. shell-command.ts (which pulled in
node:child_process for caller-shell detection) is deleted along with the
now-dead detectCallerShellKind/parentProcessName.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ensure.md snapshot rewrites the ensure semantics from --title idempotency
to --cwd + live-command matching. This is aspirational and does NOT match
the current code (still --title), so the cli-help snapshot test fails
until the code follows.

docs/specs/TODO.md is a planning scratch doc.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Dedupe two patterns repeated across the command runners:
- renderJson(payload) replaces the `JSON.stringify(..., null, 2) + "\n"`
  idiom in the split, ensure, and list-surfaces renderers.
- requireControlClient(options) collapses the resolve/check/new-Error
  boilerplate into one call returning ControlClient | Error, dropping the
  .value indirection at each call site. resolveControlClient is now
  internal to shared.ts.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
# Conflicts:
#	dor/src/cli.ts
#	lib/src/components/Wall.tsx
The enforced-title key meant a server the human started by hand never
matched the agent's `dor ensure`, so both ran their own copy. Key on the
shell-integration-reported running command plus working directory
instead: drop --title, add --cwd (defaults to the caller's directory,
which now travels in the request since the host can't know it), and
render the command rather than the title.

Also reconcile the cli-expanded merge: the new iframe/kill/read/send
commands used the old resolveControlClient API that this branch had
replaced with requireControlClient.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Rewrite the host-side surface.ensure handler to match the dor CLI's new
contract: a surface matches when it is currently running the requested
command in the requested cwd, instead of carrying a user-enforced title.

- surfaceRunsCommand(): the match predicate — live currentCommand only,
  exact rawCommandLine, normalized cwd. Pure and unit-tested.
- Thread --cwd through createSplitSurface so an explicit cwd (defaulting
  to the caller's directory) sets where the new command runs and is the
  cwd half of the key; drop the title path.
- Seed launched commands: a `-lc`/`/c` command never loads the OSC 633
  integration, so without help its pane reports nothing and ensure could
  not match a surface it just created. seedLaunchedCommand stamps the
  command run at spawn; clearing it on pty exit preserves liveness.

Hand-started interactive shells already report via the injected OSC 633
integration, so they match without seeding.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
createSplitSurface's title parameter is no longer passed by any caller
now that ensure keys on command+cwd, so remove it along with the orphaned
validateUserTitle helper and isReservedUserTitle import. Correct the
callerWorkingDirectory comment, which claimed symlink resolution that
neither the CLI nor the host actually performs.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Trailing-slash normalization was a half-measure: path.resolve in the CLI
already collapses trailing slashes (and `.`/`..`), and it never addressed
the real divergences (symlinks, case). Drop sameCwdPath/normalizeCwdPath
and compare cwd exactly, matching the command half. Canonicalize the
default cwd through resolvePath too so what the CLI sends is always
absolute, and correct the help to say symlinks are not resolved.

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

Copy link
Copy Markdown

Deploying mouseterm with  Cloudflare Pages  Cloudflare Pages

Latest commit: 52b414b
Status: ✅  Deploy successful!
Preview URL: https://774188fc.mouseterm.pages.dev
Branch Preview URL: https://add-dor-cli.mouseterm.pages.dev

View logs

@dormouse-bot dormouse-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the full diff — clean, cohesive expansion of the dor CLI. The argv-across-the-wire model (CLI sends raw string[], webview quotes once for the target shell via the shared shell-quote.ts) is a nice fix for the double-quoting problem, and the command+cwd ensure model is well-specified in both the spec and the help text. Tests are thorough (the surfaceRunsCommand liveness/exact-cwd cases and the seedLaunchedCommand/finishLaunchedCommandByPtyId lifecycle are exactly the edge cases I'd want covered), and CI is green.

Two non-blocking notes:

  • Overlap with #121. That PR ("Expand dor CLI commands") touches nearly the same file set with the same intent. This PR looks like its successor — worth closing #121 as superseded so the two don't drift.
  • One inline thought on the iframe allow permissions.

className="block h-full w-full border-0 bg-white"
src={url}
title={api.title ?? url}
allow="autoplay; clipboard-read; clipboard-write; fullscreen; geolocation; microphone; camera"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Non-blocking: this grants geolocation; microphone; camera (plus clipboard) to whatever URL the iframe loads, with no sandbox attribute. For an inspection surface aimed at localhost dev servers, those device permissions seem broader than needed — a page loaded here (or one that gets XSS'd) could prompt for camera/mic/location inside the webview. Consider trimming allow to what the use case actually needs (e.g. clipboard-read; clipboard-write; fullscreen). Your call given the URL is user-typed.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

iframe is going to get removed in a future commit

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense — if the iframe is going away, the permission scope is moot. Thanks for the context.

@nedtwigg nedtwigg changed the base branch from main to cli-expanded June 16, 2026 03:38
@nedtwigg nedtwigg changed the title Expand dor CLI: send/read/kill/iframe/version + command+cwd ensure dor CLI: argv-over-protocol + command+cwd ensure (stacked on #121) Jun 16, 2026
Base automatically changed from cli-expanded to main June 16, 2026 03:39
@nedtwigg nedtwigg merged commit 3683bf0 into main Jun 16, 2026
10 checks passed
@nedtwigg nedtwigg deleted the add-dor-cli branch June 16, 2026 03:40
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