Skip to content

Remove index-based CLI APIs, make commands workspace-relative - #89

Merged
lawrencecchen merged 9 commits into
mainfrom
remove-index-apis-workspace-relative
Feb 19, 2026
Merged

lawrencecchen merged 9 commits into
mainfrom
remove-index-apis-workspace-relative

Conversation

@lawrencecchen

Copy link
Copy Markdown
Contributor

Summary

  • Migrate 14 CLI commands from v1 text protocol to v2 JSON-RPC, making all commands workspace-relative via CMUX_WORKSPACE_ID env var fallback
  • Update 7 more commands to use normalize functions instead of legacy index-based resolvers
  • Remove dead code: 5 structs, 5 parsers, 3 utility functions (net -117 lines in cmux.swift)
  • Add regression tests for workspace-relative behavior and id-format defaults

Motivation

When Claude Code runs cmux send "echo hello" from a background workspace, the text currently goes to whichever workspace the user is looking at (v1 resolves to tabManager.selectedTabId). After this change, commands use the caller's CMUX_WORKSPACE_ID by default, targeting the correct workspace.

Commands migrated v1→v2 (14)

Command V2 Method New flags
send surface.send_text --workspace, --surface
send-key surface.send_key --workspace, --surface
send-panel surface.send_text --workspace
send-key-panel surface.send_key --workspace
new-split surface.split --workspace, --surface
new-pane pane.create --workspace
new-surface surface.create --workspace
close-surface surface.close --workspace
list-panes pane.list --workspace
list-pane-surfaces pane.surfaces --workspace
list-panels surface.list env fallback
surface-health surface.health env fallback
focus-pane pane.focus --workspace
focus-panel surface.focus --workspace

Commands updated (7)

move-workspace-to-window, list-workspaces, close-workspace, select-workspace, trigger-flash, resolveWorkspaceId, resolveSurfaceId — switched from v1 parsers to normalize functions with ref handle support.

Dead code removed

  • Structs: WorkspaceInfo, PanelInfo, PaneInfo, PaneSurfaceInfo, SurfaceHealthInfo
  • Parsers: parseWorkspaces, parsePanels, parsePanes, parsePaneSurfaces, parseSurfaceHealth
  • Functions: formatLegacySurfaceResponse, escapeText, remainingArgs

Test plan

  • xcodebuild CLI target builds successfully
  • Run tests_v2/test_workspace_relative.py on VM
  • Run tests_v2/test_cli_id_format_defaults.py on VM
  • Manual smoke test: cmux send, cmux list-panes, cmux new-split right from terminal inside cmux
  • Verify CMUX_WORKSPACE_ID env var scopes commands correctly

@cmux-agent

cmux-agent Bot commented Feb 19, 2026 •

Copy link
Copy Markdown
Contributor
Older manaflow preview screenshots (latest comment is below)

Preview Videos and Screenshots

Open Diff Heatmap

⏳ Preview screenshots are being captured...

Workspace and dev browser links will appear here once the preview environment is ready.


Generated by manaflow preview system

@vercel

vercel Bot commented Feb 19, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
cmux Ready Ready Preview, Comment Feb 19, 2026 6:36am

@cmux-agent

cmux-agent Bot commented Feb 19, 2026 •

Copy link
Copy Markdown
Contributor
Older manaflow preview screenshots (latest comment is below)

Preview Videos and Screenshots

Open Workspace (1 hr expiry) · Open Dev Browser (1 hr expiry) · Open Diff Heatmap

Screenshot status is unknown for this run.


Generated by manaflow preview system

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c1faae7e20

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread CLI/cmux.swift
Comment on lines +868 to +870
let sfId = try normalizeSurfaceHandle(panelArg, client: client, workspaceHandle: wsId)
if let sfId { params["surface_id"] = sfId }
let payload = try client.sendV2(method: "surface.send_text", params: params)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 Badge Reject invalid panel handles in send-panel

This path now forwards any non-empty --panel token to surface.send_text without guaranteeing it resolved to a real surface, so typos like --panel surface:abc silently fall through to the focused surface instead of erroring. In v2SurfaceSendText (Sources/TerminalController.swift), an unparseable surface_id is treated as nil and defaults to ws.focusedPanelId, which can send text to the wrong terminal and break workspace isolation expectations. The previous send_surface flow errored for invalid targets, so this is a behavioral regression for explicit-target sends.

Useful? React with 👍 / 👎.

@cmux-agent

cmux-agent Bot commented Feb 19, 2026 •

Copy link
Copy Markdown
Contributor
Older manaflow preview screenshots (latest comment is below)

Preview Videos and Screenshots

Open Workspace (1 hr expiry) · Open Dev Browser (1 hr expiry) · Open Diff Heatmap

Captured 8 screenshots and 1 video for commit da11c9e (2026-02-19 05:08:23.783 UTC).

Clicking navigation links: Docs, Changelog, and navigating through the docs site (video)

docs-navigation-flow.gif

Docs Cli Reference

docs-cli-reference.png

Docs Site

docs-site-homepage.png

Download Button Hover

download-button-hover-state.png

Homepage Light

homepage-light-theme.png

Homepage Main Remove Index Apis Workspace

homepage-main-remove-index-apis-workspace-relative.png

Homepage Mobile

homepage-mobile-light.png

Mobile Drawer Dark Mode

mobile-drawer-dark-mode.png

Mobile Drawer Open

mobile-drawer-open.png


Generated by manaflow preview system

@cmux-agent

cmux-agent Bot commented Feb 19, 2026 •

Copy link
Copy Markdown
Contributor
Older manaflow preview screenshots (latest comment is below)

Preview Videos and Screenshots

Open Diff Heatmap

⏳ Preview screenshots are being captured...

Workspace and dev browser links will appear here once the preview environment is ready.


Generated by manaflow preview system

@lawrencecchen

Copy link
Copy Markdown
Contributor Author

@codex review

@cmux-agent

cmux-agent Bot commented Feb 19, 2026 •

Copy link
Copy Markdown
Contributor
Older manaflow preview screenshots (latest comment is below)

Preview Videos and Screenshots

Open Diff Heatmap

⏳ Preview screenshots are being captured...

Workspace and dev browser links will appear here once the preview environment is ready.


Generated by manaflow preview system

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0217616f7c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread CLI/cmux.swift
let escaped = escapeText(text)
let response = try client.send(command: "send \(escaped)")
print(response)
var params: [String: Any] = ["text": text]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Badge Preserve escaped control sequences for send text

The migrated send path now forwards text verbatim to surface.send_text, but the previous CLI flow escaped then unescaped control sequences (\n, \r, \t) before injecting input. This changes runtime behavior for automation that relied on escaped control input (for example sending Enter via \n), which now gets typed as literal characters instead of control events; the same pattern is also used in send-panel.

Useful? React with 👍 / 👎.

@cmux-agent

cmux-agent Bot commented Feb 19, 2026 •

Copy link
Copy Markdown
Contributor
Older manaflow preview screenshots (latest comment is below)

Preview Videos and Screenshots

Open Workspace (1 hr expiry) · Open Dev Browser (1 hr expiry) · Open Diff Heatmap

Captured 4 screenshots and 1 video for commit fd4528c (2026-02-19 05:44:20.852 UTC).

Docs Site Navigation (video)

docs-site-navigation.gif

Docs Site Cli Reference Remove Index Apis Workspace

docs-site-cli-reference-remove-index-apis-workspace-relative.png

Docs Site Introduction Remove Index Apis Workspace

docs-site-introduction-remove-index-apis-workspace-relative.png

Docs Site Search Modal Remove Index Apis Workspace

docs-site-search-modal-remove-index-apis-workspace-relative.png

Web Homepage Remove Index Apis Workspace

web-homepage-remove-index-apis-workspace-relative.png


Generated by manaflow preview system

Migrate 14 CLI commands from v1 text protocol to v2 JSON-RPC, making them
workspace-relative via CMUX_WORKSPACE_ID env var fallback. Update 7 more
commands to use normalize functions instead of legacy resolvers. Remove dead
code (5 structs, 5 parsers, 3 functions). Add regression tests.

Commands migrated v1→v2: send, send-key, send-panel, send-key-panel,
new-split, new-pane, new-surface, close-surface, list-panes,
list-pane-surfaces, list-panels, surface-health, focus-pane, focus-panel.

Commands updated: move-workspace-to-window, list-workspaces,
close-workspace, select-workspace, trigger-flash, resolveWorkspaceId,
resolveSurfaceId.
When --workspace is explicitly passed, don't fall back to CMUX_SURFACE_ID
from the caller's environment. The caller's surface belongs to a different
workspace, causing "surface not found" errors. Only use the env var fallback
when the workspace is implicit (from CMUX_WORKSPACE_ID or server default).

Affects: send, send-key, new-split, close-surface, trigger-flash, identify,
notify, claude-hook.
P1: close-surface now validates the surface handle exists before sending
surface.close, preventing silent fallback to focused surface when a stale
or mistyped ref is provided.

P2: parseOption now respects -- as an option terminator. send, send-key,
send-panel, send-key-panel strip the -- marker from payload args. This
prevents payload tokens like --workspace from being consumed as routing
flags (e.g. `cmux send -- echo --workspace foo` sends all text correctly).
The test was never called from main() and sent empty string which would
raise immediately via _run_cli. Send a space character instead and add
the test to the main() call list.
…windows

Text-mode list commands (list-workspaces, list-panes, list-panels,
list-pane-surfaces, surface-health) now honor --id-format for plain output,
not just --json. Added textHandle() helper that picks ref/id/both based on
the selected format.

resolveWorkspaceId now enumerates all windows when resolving a workspace
ref, so notify/claude-hook work correctly in multi-window sessions where
the target workspace may be in a non-active window.
The v1 server unescaped \n, \r, \t in send payloads before injecting into
the terminal. The v2 surface.send_text handler sends text verbatim. Add
CLI-side unescapeSendText() to restore the same behavior: \n and \r map
to carriage return (Enter key), \t maps to tab. Applied to send and
send-panel commands.
@lawrencecchen
lawrencecchen force-pushed the remove-index-apis-workspace-relative branch from fd4528c to 2b1528f Compare February 19, 2026 05:57
@cmux-agent

cmux-agent Bot commented Feb 19, 2026 •

Copy link
Copy Markdown
Contributor
Older manaflow preview screenshots (latest comment is below)

Preview Videos and Screenshots

Open Workspace (1 hr expiry) · Open Dev Browser (1 hr expiry) · Open Diff Heatmap

Screenshot capture was skipped.

No UI changes detected - screenshots skipped


Generated by manaflow preview system

All four normalize functions (window, workspace, pane, surface) now throw
a clear error for unrecognized handle formats instead of passing them
through. Previously, a typo like `--panel foo` would forward `foo` to the
server which would silently fall back to the focused surface.
@cmux-agent

cmux-agent Bot commented Feb 19, 2026 •

Copy link
Copy Markdown
Contributor
Older manaflow preview screenshots (latest comment is below)

Preview Videos and Screenshots

Open Workspace (1 hr expiry) · Open Dev Browser (1 hr expiry) · Open Diff Heatmap

Screenshot capture was skipped.

No UI changes detected - screenshots skipped


Generated by manaflow preview system

…ID arrays

Remove workspace-scoped pre-validation from close-surface so explicit
surface refs/UUIDs from other workspaces work without requiring
--workspace. The server resolves workspace from surface_id directly.
Malformed handles are already caught by normalizeSurfaceHandle.

Extend formatIDs to strip plural _ids/_refs array pairs (e.g.
surface_ids/surface_refs in pane.list output) based on --id-format,
matching the existing singular _id/_ref stripping behavior.
@cmux-agent

cmux-agent Bot commented Feb 19, 2026 •

Copy link
Copy Markdown
Contributor
Older manaflow preview screenshots (latest comment is below)

Preview Videos and Screenshots

Open Workspace (1 hr expiry) · Open Dev Browser (1 hr expiry) · Open Diff Heatmap

Screenshot capture was skipped.

No UI changes detected - screenshots skipped


Generated by manaflow preview system

When --window is passed globally, skip the CMUX_WORKSPACE_ID env var
fallback so commands operate on the targeted window's selected workspace
instead of the caller's workspace from a different window. Affects all
migrated commands that use workspaceFromArgsOrEnv or inline env fallback.
@cmux-agent

cmux-agent Bot commented Feb 19, 2026 •

Copy link
Copy Markdown
Contributor

Preview Videos and Screenshots

Open Workspace (1 hr expiry) · Open Dev Browser (1 hr expiry) · Open Diff Heatmap

Screenshot capture was skipped.

No UI changes detected - screenshots skipped


Generated by manaflow preview system

@lawrencecchen
lawrencecchen merged commit 97085be into main Feb 19, 2026
5 checks passed
@lawrencecchen
lawrencecchen deleted the remove-index-apis-workspace-relative branch February 19, 2026 06:50
bn-l pushed a commit to bn-l/cmux that referenced this pull request Apr 3, 2026
…ow-ai#89)

* Remove index-based CLI APIs and make commands workspace-relative

Migrate 14 CLI commands from v1 text protocol to v2 JSON-RPC, making them
workspace-relative via CMUX_WORKSPACE_ID env var fallback. Update 7 more
commands to use normalize functions instead of legacy resolvers. Remove dead
code (5 structs, 5 parsers, 3 functions). Add regression tests.

Commands migrated v1→v2: send, send-key, send-panel, send-key-panel,
new-split, new-pane, new-surface, close-surface, list-panes,
list-pane-surfaces, list-panels, surface-health, focus-pane, focus-panel.

Commands updated: move-workspace-to-window, list-workspaces,
close-workspace, select-workspace, trigger-flash, resolveWorkspaceId,
resolveSurfaceId.

* Fix CMUX_SURFACE_ID env fallback when --workspace is overridden

When --workspace is explicitly passed, don't fall back to CMUX_SURFACE_ID
from the caller's environment. The caller's surface belongs to a different
workspace, causing "surface not found" errors. Only use the env var fallback
when the workspace is implicit (from CMUX_WORKSPACE_ID or server default).

Affects: send, send-key, new-split, close-surface, trigger-flash, identify,
notify, claude-hook.

* Validate surface before close and respect -- option terminator in send

P1: close-surface now validates the surface handle exists before sending
surface.close, preventing silent fallback to focused surface when a stale
or mistyped ref is provided.

P2: parseOption now respects -- as an option terminator. send, send-key,
send-panel, send-key-panel strip the -- marker from payload args. This
prevents payload tokens like --workspace from being consumed as routing
flags (e.g. `cmux send -- echo --workspace foo` sends all text correctly).

* Fix test_send_workspace_relative: add to main() and send valid text

The test was never called from main() and sent empty string which would
raise immediately via _run_cli. Send a space character instead and add
the test to the main() call list.

* Respect --id-format in text output and resolve workspace refs across windows

Text-mode list commands (list-workspaces, list-panes, list-panels,
list-pane-surfaces, surface-health) now honor --id-format for plain output,
not just --json. Added textHandle() helper that picks ref/id/both based on
the selected format.

resolveWorkspaceId now enumerates all windows when resolving a workspace
ref, so notify/claude-hook work correctly in multi-window sessions where
the target workspace may be in a non-active window.

* Preserve escape-sequence semantics for send text

The v1 server unescaped \n, \r, \t in send payloads before injecting into
the terminal. The v2 surface.send_text handler sends text verbatim. Add
CLI-side unescapeSendText() to restore the same behavior: \n and \r map
to carriage return (Enter key), \t maps to tab. Applied to send and
send-panel commands.

* Reject malformed handles instead of passing through to server

All four normalize functions (window, workspace, pane, surface) now throw
a clear error for unrecognized handle formats instead of passing them
through. Previously, a typo like `--panel foo` would forward `foo` to the
server which would silently fall back to the focused surface.

* Allow cross-workspace surface refs in close-surface and strip plural ID arrays

Remove workspace-scoped pre-validation from close-surface so explicit
surface refs/UUIDs from other workspaces work without requiring
--workspace. The server resolves workspace from surface_id directly.
Malformed handles are already caught by normalizeSurfaceHandle.

Extend formatIDs to strip plural _ids/_refs array pairs (e.g.
surface_ids/surface_refs in pane.list output) based on --id-format,
matching the existing singular _id/_ref stripping behavior.

* Honor explicit --window over CMUX_WORKSPACE_ID env fallback

When --window is passed globally, skip the CMUX_WORKSPACE_ID env var
fallback so commands operate on the targeted window's selected workspace
instead of the caller's workspace from a different window. Affects all
migrated commands that use workspaceFromArgsOrEnv or inline env fallback.
teamleaderleo added a commit that referenced this pull request Oct 22, 2026
* cmux NEXT Chief turn tests: a turn carries its time, effort, tokens and error, and an older brain still decodes

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15cfc-5b39-7e21-bc3d-02eb374c820c&host=cmux16

* cmux NEXT Chief: read a turn's time, effort, tokens and error from the brain and the trace

chief.engine.get's recent already sends ts, effort, error and usage output (engine_control.rs
turn_summary); ChiefEngineReport.Turn decodes them when present, so an older brain still decodes.
HomeEngineTurn moves to its own file and carries them, from the brain or this Mac's trace line.
The data half of #89 item 2, split out of #23883 (Leo's ~800-line rule).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15cfc-5b39-7e21-bc3d-02eb374c820c&host=cmux16

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
teamleaderleo added a commit that referenced this pull request Oct 27, 2026
* cmux NEXT Chief names tests: harnesses and models read by a name, never their raw id

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15cfc-5b39-7e21-bc3d-02eb374c820c&host=cmux16

* cmux NEXT Chief: show every harness and model by a readable name

Leo 2026-10-21 (#89): the Chief settings showed raw ids (claude-sr2-pipe, claude-opus-5-5). Official
engines keep their titles, acpmux's built-in harnesses read by acpmux's names, a model reads by its
catalog name, else its id made readable (String.chiefHarnessName / chiefModelName). The raw id stays
in the popup item's tooltip. Split out of #23883 (Leo's ~800-line rule).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15cfc-5b39-7e21-bc3d-02eb374c820c&host=cmux16

* cmux NEXT Chief names tests: two ids with one name stay two items, and a model's tooltip is the id it writes

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15cfc-5b39-7e21-bc3d-02eb374c820c&host=cmux16

* cmux NEXT Chief: two ids with one name stay two popup items, and a model's tooltip names the id it writes

Codex review: addItem(withTitle:) keeps one item per title, so claude-cr and claude-cr- (both
"Claude (CodeRouter)") lost one and the selection index went wrong; items are now added as
NSMenuItems. A model row's tooltip came from the alias the row started with, and a description
hid the id; it is now built after the alias and its release share the row: description, then id.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15cfc-5b39-7e21-bc3d-02eb374c820c&host=cmux16

* Chief names: a date suffix needs a real month and day; plan rows read by readable(model)

Review findings: an eight-digit suffix was dropped as a release date without
checking its month and day (foo-20241301 read as "Foo"); it now goes through
the same month 1-12, day 1-31 check as the dashed form. And a plan row other
than opusplan took acpmux's name as is, so a plan named by its id (foo-plan)
still showed the raw id; planTitle now uses readable(model).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-f68ebdd9a3c1704ffb42

* Chief settings: a "default" harness reads as plain Default; plan rows' tooltips name the id they write

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-f68ebdd9a3c1704ffb42

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

This branch was successfully deployed

1 active deployment
Preview — 2ee99242 Deployed Feb 19, 2026 by vercel[bot]
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.

1 participant