Skip to content

fix: route new-workspace --command through workspace.create initial_command - #2433

Closed
jasonkuhrt wants to merge 4 commits into
manaflow-ai:mainfrom
jasonkuhrt:codex/1900-initial-command
Closed

jasonkuhrt wants to merge 4 commits into
manaflow-ai:mainfrom
jasonkuhrt:codex/1900-initial-command

Conversation

@jasonkuhrt

@jasonkuhrt jasonkuhrt commented Mar 31, 2026 •

Copy link
Copy Markdown

Summary

cmux new-workspace --command currently creates the workspace first and then injects the command with surface.send_text.

This PR routes --command through workspace.create.initial_command instead.

Why

workspace.create already supports initial_command, and cmux already threads that through the terminal startup path.

Using surface.send_text here is the wrong primitive:

  • it depends on shell/editor keybinding state
  • it breaks in vi-mode shells like fish with fish_vi_key_bindings
  • it adds an avoidable second step after workspace creation

Scope

This PR only changes cmux new-workspace --command.

It does not change:

Implementation

  • populate initial_command in the workspace.create params for new-workspace
  • remove the follow-up surface.send_text call for this path
  • update the CLI help text so --command is described as an initial terminal command
  • add a focused fake-socket CLI regression that verifies workspace.create receives initial_command and surface.send_text is not used

The existing runtime coverage in tests_v2/test_cli_new_workspace_command_queue.py still exercises the user-visible behavior that the command runs without selecting the new workspace.

Fixes #1900


Summary by cubic

Routes cmux new-workspace --command through workspace.create.initial_command to run the initial command reliably across shells, including vi-mode. Fixes #1900 and ignores empty/whitespace commands.

  • Bug Fixes
    • Set initial_command in workspace.create only when --command is non-empty; stop calling surface.send_text.
    • Update CLI help to describe --command as the initial terminal command.
    • Add a fake-socket regression test with subset-based assertions and thread-safe state; verifies workspace.create includes initial_command and surface.send_text is not used.

Written for commit 1a07088. Summary will update on new commits.

Summary by CodeRabbit

  • Bug Fixes
    • The CLI now runs the provided --command as the new workspace’s initial terminal command during creation; help text for --command updated to reflect this behavior.
  • Tests
    • Added a regression test ensuring the CLI includes the initial command in workspace creation and does not send the command as a separate post-creation "send text" action.

@vercel

vercel Bot commented Mar 31, 2026

Copy link
Copy Markdown

@jasonkuhrt is attempting to deploy a commit to the Manaflow Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai

coderabbitai Bot commented Mar 31, 2026 •

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0dfe0fd6-0eaf-4644-8a1c-17115c4c6ba7

📥 Commits

Reviewing files that changed from the base of the PR and between 90cd68f and 1a07088.

📒 Files selected for processing (1)
  • tests/test_cli_new_workspace_initial_command.py

📝 Walkthrough

Walkthrough

The cmux new-workspace --command option now sends the command as initial_command in the initial workspace.create V2 request, removing the previous post-creation surface.send_text keystroke-simulation path.

Changes

Cohort / File(s) Summary
CLI command handling
CLI/cmux.swift
new-workspace now places --command into params["initial_command"] for the workspace.create V2 call; removed the post-create surface.send_text / unescapeSendText path and updated --command help text.
Regression test
tests/test_cli_new_workspace_initial_command.py
Added a test that runs cmux new-workspace --command against a fake Unix-domain JSON-RPC server, asserting workspace.create receives cwd and initial_command and that surface.send_text is not invoked.

Sequence Diagram(s)

sequenceDiagram
  participant CLI as rgba(60,130,200,0.5) CLI
  participant RPC as rgba(120,200,80,0.5) JSON-RPC Server
  participant WS as rgba(200,120,80,0.5) Workspace Service
  participant TERM as rgba(160,60,200,0.5) Terminal

  CLI->>RPC: sendV2 "workspace.create" {cwd, initial_command}
  RPC->>WS: create workspace with params
  WS->>TERM: start terminal with initial_command (exec directly)
  WS-->>RPC: workspace created {wsId, ref}
  RPC-->>CLI: response {wsId, ref}
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰 I hopped along the JSON stream,
Pushed commands where they rightly seem—
No keystroke mimic, no vi surprise,
Initial command runs, straight and wise.
I munch a carrot while the test replies.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.29% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: routing new-workspace --command through workspace.create initial_command instead of surface.send_text.
Description check ✅ Passed The description includes Summary, Testing, and Checklist sections covering what changed, why, and implementation details, though manual testing verification details are limited.
Linked Issues check ✅ Passed The PR fully addresses issue #1900 by implementing the suggested fix: passing --command as initial_command in workspace.create and removing surface.send_text [#1900].
Out of Scope Changes check ✅ Passed All changes are directly scoped to fixing new-workspace --command routing; no out-of-scope modifications to tmux compat, new-surface, new-split, or other unrelated code detected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

No issues found across 2 files

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
tests/test_cli_new_workspace_initial_command.py (1)

138-143: Use subset validation for workspace.create params to avoid brittle failures.

Line 138 currently enforces exact dict equality, which can break if unrelated optional params are added later. Assert required keys (cwd, initial_command) instead.

♻️ Proposed change
-        expected_params = {
-            "cwd": str(cwd),
-            "initial_command": command_text,
-        }
-        if state.workspace_create_params != expected_params:
+        expected_params = {
+            "cwd": str(cwd),
+            "initial_command": command_text,
+        }
+        observed_params = state.workspace_create_params
+        missing_or_mismatched = {
+            k: (expected_params[k], observed_params.get(k))
+            for k in expected_params
+            if observed_params.get(k) != expected_params[k]
+        }
+        if missing_or_mismatched:
             print(
                 "FAIL: workspace.create params mismatch "
-                f"expected={expected_params!r} observed={state.workspace_create_params!r}"
+                f"expected_subset={expected_params!r} observed={observed_params!r} "
+                f"diff={missing_or_mismatched!r}"
             )
             return 1
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/test_cli_new_workspace_initial_command.py` around lines 138 - 143, The
test currently compares state.workspace_create_params to expected_params with
exact equality which is brittle; update the assertion to validate only the
required keys by checking that 'cwd' and 'initial_command' exist in
state.workspace_create_params and that their values equal expected_params['cwd']
and expected_params['initial_command'] (use state.workspace_create_params and
expected_params to locate the data), and update the failure print to show which
required key(s) mismatched rather than full-dict equality.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@tests/test_cli_new_workspace_initial_command.py`:
- Around line 138-143: The test currently compares state.workspace_create_params
to expected_params with exact equality which is brittle; update the assertion to
validate only the required keys by checking that 'cwd' and 'initial_command'
exist in state.workspace_create_params and that their values equal
expected_params['cwd'] and expected_params['initial_command'] (use
state.workspace_create_params and expected_params to locate the data), and
update the failure print to show which required key(s) mismatched rather than
full-dict equality.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 43c6aa47-9ac2-44be-8496-0bbdc0577fd4

📥 Commits

Reviewing files that changed from the base of the PR and between 5835bff and aceeab2.

📒 Files selected for processing (2)
  • CLI/cmux.swift
  • tests/test_cli_new_workspace_initial_command.py

@greptile-apps

greptile-apps Bot commented Mar 31, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes cmux new-workspace --command by routing the command through workspace.create.initial_command instead of a two-step create-then-surface.send_text sequence. The old approach was unreliable because surface.send_text depends on the shell's current input mode (breaking in vi-mode shells like fish with fish_vi_key_bindings), whereas initial_command is already wired into the terminal startup path in TerminalController.swift via tabManager.addWorkspace(initialTerminalCommand:) and is shell-state independent.

Key changes:

  • CLI/cmux.swift: Populates params[\"initial_command\"] before the workspace.create call and removes the follow-up surface.send_text call entirely. The whitespace-guard that skips empty commands is correctly applied. Help text updated to reflect the new semantics.
  • tests/test_cli_new_workspace_initial_command.py: New protocol-level regression test using a fake Unix socket. Verifies that workspace.create receives initial_command and that surface.send_text is never called. Two minor style issues were found: FakeCmuxState lacks a threading.Lock (inconsistent with the sibling test_cli_claude_teams_tmux_sequence.py), and the exact dict equality check for workspace_create_params will fail if the CLI ever adds additional default params to workspace.create in the future.

Confidence Score: 5/5

Safe to merge — core logic change is correct and the new protocol path is already exercised by the daemon; only minor test style issues remain.

The Swift change is minimal and correct: it removes a fragile two-step send_text approach and replaces it with the already-supported initial_command mechanism. The server-side handling in TerminalController.swift is unchanged and already handles this field properly. The only findings are P2 style concerns in the new test (missing lock, exact dict equality). Neither blocks merge.

tests/test_cli_new_workspace_initial_command.py — minor test robustness improvements suggested but not blocking.

Important Files Changed

Filename Overview
CLI/cmux.swift Removes the two-step create-then-send_text approach; passes --command as initial_command in workspace.create params and removes the follow-up surface.send_text call. Help text updated to match. Change is clean and correct.
tests/test_cli_new_workspace_initial_command.py Fake-socket regression test verifying the new protocol shape (initial_command in workspace.create, surface.send_text absent). Missing threading.Lock on shared state (inconsistent with sibling tests); exact dict equality check is fragile to future param additions.

Sequence Diagram

sequenceDiagram
    participant User
    participant CLI as cmux CLI
    participant Daemon as cmux Daemon

    Note over User,Daemon: Before this PR (surface.send_text path)
    User->>CLI: cmux new-workspace --command "npm test"
    CLI->>Daemon: workspace.create {cwd: ...}
    Daemon-->>CLI: {workspace_ref: "workspace:1", workspace_id: "..."}
    CLI->>Daemon: surface.send_text {text: "npm test\r", workspace_id: "..."}
    Note over Daemon: Depends on shell input state (breaks in vi-mode / fish)
    Daemon-->>CLI: {ok: true}
    CLI-->>User: OK workspace:1

    Note over User,Daemon: After this PR (initial_command path)
    User->>CLI: cmux new-workspace --command "npm test"
    CLI->>Daemon: workspace.create {cwd: ..., initial_command: "npm test"}
    Note over Daemon: Command injected at terminal startup, shell-state independent
    Daemon-->>CLI: {workspace_ref: "workspace:1", workspace_id: "..."}
    CLI-->>User: OK workspace:1
Loading

Reviews (1): Last reviewed commit: "fix: route new-workspace command through..." | Re-trigger Greptile

Comment thread tests/test_cli_new_workspace_initial_command.py
Comment thread tests/test_cli_new_workspace_initial_command.py
@jasonkuhrt

Copy link
Copy Markdown
Author

Closing. #9614 (v0.64.23) took a different route: --command is now delivered as spawn-time initial_input, which keeps the interactive shell alive. This PR routes through initial_command, which replaces the shell with the command, so it no longer fits. The case that remains, a prompt that starts in vi normal mode, is written up in #1900 (comment).

@jasonkuhrt jasonkuhrt closed this Sep 28, 2026
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.

new-workspace --command uses send_text instead of existing initial_command API, breaking vi-mode shells

2 participants