Repository navigation
Remove index-based CLI APIs, make commands workspace-relative - #89
Conversation
Older manaflow preview screenshots (latest comment is below)Preview Videos and Screenshots⏳ Preview screenshots are being captured... Workspace and dev browser links will appear here once the preview environment is ready. Generated by manaflow preview system |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Older manaflow preview screenshots (latest comment is below)Preview Videos and ScreenshotsOpen 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 |
There was a problem hiding this comment.
💡 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".
| 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) |
There was a problem hiding this comment.
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 👍 / 👎.
Older manaflow preview screenshots (latest comment is below)Preview Videos and ScreenshotsOpen Workspace (1 hr expiry) · Open Dev Browser (1 hr expiry) · Open Diff Heatmap Captured 8 screenshots and 1 video for commit Clicking navigation links: Docs, Changelog, and navigating through the docs site (video) Docs Cli Reference Docs Site Download Button Hover Homepage Light Homepage Main Remove Index Apis Workspace Homepage Mobile Mobile Drawer Dark Mode Mobile Drawer Open Generated by manaflow preview system |
Older manaflow preview screenshots (latest comment is below)Preview Videos and Screenshots⏳ Preview screenshots are being captured... Workspace and dev browser links will appear here once the preview environment is ready. Generated by manaflow preview system |
|
@codex review |
Older manaflow preview screenshots (latest comment is below)Preview Videos and Screenshots⏳ Preview screenshots are being captured... Workspace and dev browser links will appear here once the preview environment is ready. Generated by manaflow preview system |
There was a problem hiding this comment.
💡 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".
| let escaped = escapeText(text) | ||
| let response = try client.send(command: "send \(escaped)") | ||
| print(response) | ||
| var params: [String: Any] = ["text": text] |
There was a problem hiding this comment.
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 👍 / 👎.
Older manaflow preview screenshots (latest comment is below)Preview Videos and ScreenshotsOpen Workspace (1 hr expiry) · Open Dev Browser (1 hr expiry) · Open Diff Heatmap Captured 4 screenshots and 1 video for commit Docs Site Navigation (video) Docs Site Cli Reference Remove Index Apis Workspace Docs Site Introduction Remove Index Apis Workspace Docs Site Search Modal Remove Index Apis Workspace Web Homepage Remove Index Apis Workspace 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.
fd4528c to
2b1528f
Compare
Older manaflow preview screenshots (latest comment is below)Preview Videos and ScreenshotsOpen Workspace (1 hr expiry) · Open Dev Browser (1 hr expiry) · Open Diff Heatmap Screenshot capture was 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.
Older manaflow preview screenshots (latest comment is below)Preview Videos and ScreenshotsOpen Workspace (1 hr expiry) · Open Dev Browser (1 hr expiry) · Open Diff Heatmap Screenshot capture was 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.
Older manaflow preview screenshots (latest comment is below)Preview Videos and ScreenshotsOpen Workspace (1 hr expiry) · Open Dev Browser (1 hr expiry) · Open Diff Heatmap Screenshot capture was 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.
Preview Videos and ScreenshotsOpen Workspace (1 hr expiry) · Open Dev Browser (1 hr expiry) · Open Diff Heatmap Screenshot capture was skipped.
Generated by manaflow preview system |
…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.
* 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>
* 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>


Summary
CMUX_WORKSPACE_IDenv var fallbackMotivation
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 totabManager.selectedTabId). After this change, commands use the caller'sCMUX_WORKSPACE_IDby default, targeting the correct workspace.Commands migrated v1→v2 (14)
sendsurface.send_text--workspace,--surfacesend-keysurface.send_key--workspace,--surfacesend-panelsurface.send_text--workspacesend-key-panelsurface.send_key--workspacenew-splitsurface.split--workspace,--surfacenew-panepane.create--workspacenew-surfacesurface.create--workspaceclose-surfacesurface.close--workspacelist-panespane.list--workspacelist-pane-surfacespane.surfaces--workspacelist-panelssurface.listsurface-healthsurface.healthfocus-panepane.focus--workspacefocus-panelsurface.focus--workspaceCommands 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
WorkspaceInfo,PanelInfo,PaneInfo,PaneSurfaceInfo,SurfaceHealthInfoparseWorkspaces,parsePanels,parsePanes,parsePaneSurfaces,parseSurfaceHealthformatLegacySurfaceResponse,escapeText,remainingArgsTest plan
xcodebuildCLI target builds successfullytests_v2/test_workspace_relative.pyon VMtests_v2/test_cli_id_format_defaults.pyon VMcmux send,cmux list-panes,cmux new-split rightfrom terminal inside cmux