Skip to content

fix(cli): accept --server and --standalone as global flags - #428

Merged
shuv1337 merged 5 commits into
integration-v2from
cursor/standalone-global-flag-1969
Oct 5, 2026
Merged

shuv1337 merged 5 commits into
integration-v2from
cursor/standalone-global-flag-1969

Conversation

@shuv1337

@shuv1337 shuv1337 commented Oct 4, 2026 •

Copy link
Copy Markdown
Owner

Intent

The captain approved resuming the issue backlog (2026-10-04): "triage the ~22 Cursor draft PRs first, then small batches", and later answered yes that shuvbro's triage review counts as the first review for these Cursor PRs. The read-only triage report is at ~/repos/shuvbro/data/triage-cursor-shuvcode/report.md; read the section for each PR you handle. Verdicts: #424 (fix(cli): continue explicit --server after legacy health 404, issue #399) is land: only a modern-server UnexpectedStatus 404 on /api/info falls back to probing /api/health then /api/status and continues with a warning; 401/503 still fail; 10 local tests pass. #428 (fix(cli): accept --standalone before subcommands, issue #416) is fix-then-land with an open review finding (#428 (comment)): globally accepted --server/--standalone are silently ignored by handlers that bypass ServerConnection.resolve - service stop/restart/start/set/unset, pair, acp, serve - so e.g. shuvcode --server http://other:4096 service stop stops the LOCAL service. Required: keep true global parsing for supported client commands but reject explicitly requested unsupported connection flags BEFORE any side effect on those paths (or implement real endpoint support where valid), update help with the support boundary, and add regressions for rejection, no local effects, and real endpoint/private selection across flag placements. Both PRs change packages/cli/src/services/server-connection.ts. The triage's land verdict preserves explicit endpoint selection without managed-service replacement, warns on modern version skew, legacy version skew, versionless health, or all-404, and falls back only on modern UnexpectedStatus 404. For the global flag repair, every local lifecycle/config/pair/serve/ACP case listed must reject unsupported explicitly requested connection flags before effects; ignoring flags on unrelated non-server commands is allowed. Existing tests only prove placements reach mutual exclusion; actual requested remote endpoint and standalone selection must be exercised.

What Changed

  • --server and --standalone are now global CLI flags (packages/cli/src/services/server-flags.ts, registered in framework/runtime.ts), so they work before or after the subcommand. The per-command ServerParams were removed. ServerConnection.resolve now reads the global settings when the caller passes no override, and auth, api, models, reload, run, mini, stats, session, and default handlers stop passing the flags through by hand.
  • Commands that act on the local machine are marked connectionFlags: "unsupported": service (and its subcommands), pair, acp, serve, mcp add, and plugin add/update/remove. If either flag is passed to one of them, the runtime rejects the command before its handler loads, so nothing happens locally. For example, --server <url> service stop now errors out instead of stopping the local service.
  • The help text for the global flags and services/www CLI docs now state which commands don't support these flags. New test/server-flags.test.ts covers: rejection with no local side effects; --server and --standalone still being mutually exclusive in every flag placement; plugin list reaching the requested endpoint; and --standalone starting a private server instead of using the registered service, across flag placements.

Risk Assessment

✅ Low: The fix round marks only mcp add and plugin add/update/remove as unsupported. Its runtime change builds the full nested command path for the rejection message and still rejects before the handler loads. The help text now names the new boundary, and the new regression checks the error message and that the project and global config files are unchanged for every flag placement. I found no reachable incorrect behavior.

Testing

I ran the focused server-flags regression suite (25/25 pass), then drove the real CLI end-to-end. A real shuvcode serve ran in one isolated sandbox and the client ran from another. In all three placements, --server reached the requested remote (api GET /api/info returned the remote serve's pid 3832825). --standalone in all three placements started a separate private server (a different pid), and a dead --server URL failed rather than falling back. Combining the two flags was rejected. Every unsupported service/pair/acp/serve/mcp add/plugin add|update|remove invocation exited 1 with "<cmd> does not support --server or --standalone", left the config untouched and left the remote server alive. Help shows the support boundary on both the root command and subcommands. Everything passed, and the sandboxes and serve processes were cleaned up. There is no visual UI surface, so the evidence is CLI transcripts.

  • Live validation: ✅ go - 8 of 8 scenarios driven live against the product
Scenario Result Live Evidence
--server before, between, or after the subcommand reaches the requested real remote server ✅ pass live transcript-trimmed.txt: all three placements of api GET /api/info --server return the remote serve pid 3832825; session list and plugin list exit 0
Adversarial: --server pointing at a dead URL is honored and does not fall back to a local service ✅ pass live transcript-trimmed.txt: --server http://127.0.0.1:1 session list → 'Could not reach server', exit 1
--standalone before, between, or after the subcommand runs on a private server ✅ pass live transcript-trimmed.txt: standalone api info shows a different pid and URL from the remote; session/plugin list exit 0; the server-flags test also shows a registered service is never contacted
Adversarial: combining --standalone and --server is rejected ✅ pass live transcript-trimmed.txt: '--server and --standalone cannot be combined', exit 1
service stop/status/set/unset, pair, acp, and serve reject explicit connection flags before any side effect ✅ pass live transcript-trimmed.txt: each exits 1 with '<cmd> does not support --server or --standalone', no service config is written, and the remote serve stays alive; server-flags.test.ts covers the case of a r…
mcp add and plugin add/remove/update reject connection flags without writing config ✅ pass live transcript-trimmed.txt: rejection for all of them, and 'global config unchanged? yes'
Help documents the support boundary for global --server/--standalone ✅ pass live help.txt: root and service stop --help list both flags as 'unsupported by service, pair, acp, serve, mcp add, and plugin add/update/remove'
Unrelated commands without flags still work ✅ pass live transcript-trimmed.txt: shuvcode plugin list → 'No plugins found', exit 0
Evidence: Live CLI transcript (stack frames trimmed)
== Remote: real 'shuvcode serve --port 47830' in its own sandbox
remote /api/info (curl): {"version":"local","pid":3832825,"urls":["http://127.0.0.1:47830"],"paths":{"tmp":"/tmp/shuvcode"}}

== Scenario: --server selects the requested remote at every placement (client sandbox has no service)
$ shuvcode --server http://127.0.0.1:47830 api GET /api/info
{"version":"local","pid":3832825,"urls":["http://127.0.0.1:47830"],"paths":{"tmp":"/tmp/shuvcode"}}
[exit 0]

$ shuvcode api --server http://127.0.0.1:47830 GET /api/info
{"version":"local","pid":3832825,"urls":["http://127.0.0.1:47830"],"paths":{"tmp":"/tmp/shuvcode"}}
[exit 0]

$ shuvcode api GET /api/info --server http://127.0.0.1:47830
{"version":"local","pid":3832825,"urls":["http://127.0.0.1:47830"],"paths":{"tmp":"/tmp/shuvcode"}}
[exit 0]

$ shuvcode --server http://127.0.0.1:47830 session list
[exit 0]

$ shuvcode session --server http://127.0.0.1:47830 list
[exit 0]

$ shuvcode session list --server http://127.0.0.1:47830
[exit 0]

$ shuvcode --server http://127.0.0.1:47830 plugin list
No plugins found
[exit 0]

client sandbox service registration exists? locks

== Adversarial: --server pointing at a dead port really is used (fails, no fallback to a local service)
$ shuvcode --server http://127.0.0.1:1 session list
Could not reach server at http://127.0.0.1:1
[exit 1]

== Scenario: --standalone before / between / after subcommand uses a private server
$ shuvcode --standalone api GET /api/info
{"version":"local","pid":3838600,"urls":["http://127.0.0.1:35941"],"paths":{"tmp":"/tmp/shuvcode"}}
[exit 0]

$ shuvcode session --standalone list
[exit 0]

$ shuvcode plugin list --standalone
No plugins found
[exit 0]

== Adversarial: combined flags rejected
$ shuvcode --standalone session list --server http://127.0.0.1:47830
--server and --standalone cannot be combined
[exit 1]

== Scenario: unsupported commands reject explicit flags before side effects
$ shuvcode --server http://127.0.0.1:47830 service stop
Error: service does not support --server or --standalone; use a client command instead
[exit 1]

$ shuvcode service stop --standalone
Error: service does not support --server or --standalone; use a client command instead
[exit 1]

$ shuvcode --server http://127.0.0.1:47830 service status
Error: service does not support --server or --standalone; use a client command instead
[exit 1]

$ shuvcode service status --standalone
Error: service does not support --server or --standalone; use a client command instead
[exit 1]

$ shuvcode --server http://127.0.0.1:47830 service set port 1234
Error: service does not support --server or --standalone; use a client command instead
[exit 1]

$ shuvcode service set port 1234 --standalone
Error: service does not support --server or --standalone; use a client command instead
[exit 1]

$ shuvcode --server http://127.0.0.1:47830 service unset disabled
Error: service does not support --server or --standalone; use a client command instead
[exit 1]

$ shuvcode service unset disabled --standalone
Error: service does not support --server or --standalone; use a client command instead
[exit 1]

$ shuvcode --server http://127.0.0.1:47830 pair
Error: pair does not support --server or --standalone; use a client command instead
[exit 1]

$ shuvcode pair --standalone
Error: pair does not support --server or --standalone; use a client command instead
[exit 1]

$ shuvcode --server http://127.0.0.1:47830 acp
Error: acp does not support --server or --standalone; use a client command instead
[exit 1]

$ shuvcode acp --standalone
Error: acp does not support --server or --standalone; use a client command instead
[exit 1]

$ shuvcode --server http://127.0.0.1:47830 serve --port 0
Error: serve does not support --server or --standalone; use a client command instead
[exit 1]

$ shuvcode serve --port 0 --standalone
Error: serve does not support --server or --standalone; use a client command instead
[exit 1]

$ shuvcode --server http://127.0.0.1:47830 mcp add x --url http://127.0.0.1:1/mcp --global
Error: mcp add does not support --server or --standalone; use a client command instead
[exit 1]

$ shuvcode mcp add x --url http://127.0.0.1:1/mcp --global --standalone
Error: mcp add does not support --server or --standalone; use a client command instead
[exit 1]

$ shuvcode --server http://127.0.0.1:47830 plugin add foo
Error: plugin add does not support --server or --standalone; use a client command instead
[exit 1]

$ shuvcode plugin add foo --standalone
Error: plugin add does not support --server or --standalone; use a client command instead
[exit 1]

$ shuvcode --server http://127.0.0.1:47830 plugin remove keep-me
Error: plugin remove does not support --server or --standalone; use a client command instead
[exit 1]

$ shuvcode plugin remove keep-me --standalone
Error: plugin remove does not support --server or --standalone; use a client command instead
[exit 1]

$ shuvcode --server http://127.0.0.1:47830 plugin update keep-me
Error: plugin update does not support --server or --standalone; use a client command instead
[exit 1]

$ shuvcode plugin update keep-me --standalone
Error: plugin update does not support --server or --standalone; use a client command instead
[exit 1]

global config unchanged? yes
service config written? none
remote serve still alive? yes

== Scenario: unrelated commands without flags still run; --help shows boundary
$ shuvcode plugin list
No plugins found
[exit 0]

GLOBAL FLAGS
  --help, -h                                                          Show help information
  --version, -v                                                       Show version information
  --wizard                                                            Start wizard mode for a command
  --completions <bash|zsh|fish|sh>                                    Print shell completion script (choices: bash, zsh, fish, sh)
Evidence: Full live CLI transcript
== Remote: real 'shuvcode serve --port 47830' in its own sandbox
remote /api/info (curl): {"version":"local","pid":3832825,"urls":["http://127.0.0.1:47830"],"paths":{"tmp":"/tmp/shuvcode"}}

== Scenario: --server selects the requested remote at every placement (client sandbox has no service)
$ shuvcode --server http://127.0.0.1:47830 api GET /api/info
{"version":"local","pid":3832825,"urls":["http://127.0.0.1:47830"],"paths":{"tmp":"/tmp/shuvcode"}}
[exit 0]

$ shuvcode api --server http://127.0.0.1:47830 GET /api/info
{"version":"local","pid":3832825,"urls":["http://127.0.0.1:47830"],"paths":{"tmp":"/tmp/shuvcode"}}
[exit 0]

$ shuvcode api GET /api/info --server http://127.0.0.1:47830
{"version":"local","pid":3832825,"urls":["http://127.0.0.1:47830"],"paths":{"tmp":"/tmp/shuvcode"}}
[exit 0]

$ shuvcode --server http://127.0.0.1:47830 session list
[exit 0]

$ shuvcode session --server http://127.0.0.1:47830 list
[exit 0]

$ shuvcode session list --server http://127.0.0.1:47830
[exit 0]

$ shuvcode --server http://127.0.0.1:47830 plugin list
No plugins found
[exit 0]

client sandbox service registration exists? locks

== Adversarial: --server pointing at a dead port really is used (fails, no fallback to a local service)
$ shuvcode --server http://127.0.0.1:1 session list
Could not reach server at http://127.0.0.1:1
[exit 1]

== Scenario: --standalone before / between / after subcommand uses a private server
$ shuvcode --standalone api GET /api/info
{"version":"local","pid":3838600,"urls":["http://127.0.0.1:35941"],"paths":{"tmp":"/tmp/shuvcode"}}
[exit 0]

$ shuvcode session --standalone list
[exit 0]

$ shuvcode plugin list --standalone
No plugins found
[exit 0]

== Adversarial: combined flags rejected
$ shuvcode --standalone session list --server http://127.0.0.1:47830
--server and --standalone cannot be combined
[exit 1]

== Scenario: unsupported commands reject explicit flags before side effects
$ shuvcode --server http://127.0.0.1:47830 service stop
Error: service does not support --server or --standalone; use a client command instead
    at <anonymous> (~/.no-mistakes/worktrees/284027b67ce8/01M44ZKZKPA3BCM50S23E98B4N/packages/cli/src/services/server-flags.ts:32:9)
    at cli.server-flags.reject (~/.no-mistakes/worktrees/284027b67ce8/01M44ZKZKPA3BCM50S23E98B4N/packages/cli/src/framework/runtime.ts:107:49)
    at cli.server-flags.reject (definition) (~/.no-mistakes/worktrees/284027b67ce8/01M44ZKZKPA3BCM50S23E98B4N/packages/cli/src/services/server-flags.ts:27:30)
[exit 1]

$ shuvcode service stop --standalone
Error: service does not support --server or --standalone; use a client command instead
    at <anonymous> (~/.no-mistakes/worktrees/284027b67ce8/01M44ZKZKPA3BCM50S23E98B4N/packages/cli/src/services/server-flags.ts:32:9)
    at cli.server-flags.reject (~/.no-mistakes/worktrees/284027b67ce8/01M44ZKZKPA3BCM50S23E98B4N/packages/cli/src/framework/runtime.ts:107:49)
    at cli.server-flags.reject (definition) (~/.no-mistakes/worktrees/284027b67ce8/01M44ZKZKPA3BCM50S23E98B4N/packages/cli/src/services/server-flags.ts:27:30)
[exit 1]

$ shuvcode --server http://127.0.0.1:47830 service status
Error: service does not support --server or --standalone; use a client command instead
    at <anonymous> (~/.no-mistakes/worktrees/284027b67ce8/01M44ZKZKPA3BCM50S23E98B4N/packages/cli/src/services/server-flags.ts:32:9)
    at cli.server-flags.reject (~/.no-mistakes/worktrees/284027b67ce8/01M44ZKZKPA3BCM50S23E98B4N/packages/cli/src/framework/runtime.ts:107:49)
    at cli.server-flags.reject (definition) (~/.no-mistakes/worktrees/284027b67ce8/01M44ZKZKPA3BCM50S23E98B4N/packages/cli/src/services/server-flags.ts:27:30)
[exit 1]

$ shuvcode service status --standalone
Error: service does not support --server or --standalone; use a client command instead
    at <anonymous> (~/.no-mistakes/worktrees/284027b67ce8/01M44ZKZKPA3BCM50S23E98B4N/packages/cli/src/services/server-flags.ts:32:9)
    at cli.server-flags.reject (~/.no-mistakes/worktrees/284027b67ce8/01M44ZKZKPA3BCM50S23E98B4N/packages/cli/src/framework/runtime.ts:107:49)
    at cli.server-flags.reject (definition) (~/.no-mistakes/worktrees/284027b67ce8/01M44ZKZKPA3BCM50S23E98B4N/packages/cli/src/services/server-flags.ts:27:30)
[exit 1]

$ shuvcode --server http://127.0.0.1:47830 service set port 1234
Error: service does not support --server or --standalone; use a client command instead
    at <anonymous> (~/.no-mistakes/worktrees/284027b67ce8/01M44ZKZKPA3BCM50S23E98B4N/packages/cli/src/services/server-flags.ts:32:9)
    at cli.server-flags.reject (~/.no-mistakes/worktrees/284027b67ce8/01M44ZKZKPA3BCM50S23E98B4N/packages/cli/src/framework/runtime.ts:107:49)
    at cli.server-flags.reject (definition) (~/.no-mistakes/worktrees/284027b67ce8/01M44ZKZKPA3BCM50S23E98B4N/packages/cli/src/services/server-flags.ts:27:30)
[exit 1]

$ shuvcode service set port 1234 --standalone
Error: service does not support --server or --standalone; use a client command instead
    at <anonymous> (~/.no-mistakes/worktrees/284027b67ce8/01M44ZKZKPA3BCM50S23E98B4N/packages/cli/src/services/server-flags.ts:32:9)
    at cli.server-flags.reject (~/.no-mistakes/worktrees/284027b67ce8/01M44ZKZKPA3BCM50S23E98B4N/packages/cli/src/framework/runtime.ts:107:49)
    at cli.server-flags.reject (definition) (~/.no-mistakes/worktrees/284027b67ce8/01M44ZKZKPA3BCM50S23E98B4N/packages/cli/src/services/server-flags.ts:27:30)
[exit 1]

$ shuvcode --server http://127.0.0.1:47830 service unset disabled
Error: service does not support --server or --standalone; use a client command instead
    at <anonymous> (~/.no-mistakes/worktrees/284027b67ce8/01M44ZKZKPA3BCM50S23E98B4N/packages/cli/src/services/server-flags.ts:32:9)
    at cli.server-flags.reject (~/.no-mistakes/worktrees/284027b67ce8/01M44ZKZKPA3BCM50S23E98B4N/packages/cli/src/framework/runtime.ts:107:49)
    at cli.server-flags.reject (definition) (~/.no-mistakes/worktrees/284027b67ce8/01M44ZKZKPA3BCM50S23E98B4N/packages/cli/src/services/server-flags.ts:27:30)
[exit 1]

$ shuvcode service unset disabled --standalone
Error: service does not support --server or --standalone; use a client command instead
    at <anonymous> (~/.no-mistakes/worktrees/284027b67ce8/01M44ZKZKPA3BCM50S23E98B4N/packages/cli/src/services/server-flags.ts:32:9)
    at cli.server-flags.reject (~/.no-mistakes/worktrees/284027b67ce8/01M44ZKZKPA3BCM50S23E98B4N/packages/cli/src/framework/runtime.ts:107:49)
    at cli.server-flags.reject (definition) (~/.no-mistakes/worktrees/284027b67ce8/01M44ZKZKPA3BCM50S23E98B4N/packages/cli/src/services/server-flags.ts:27:30)
[exit 1]

$ shuvcode --server http://127.0.0.1:47830 pair
Error: pair does not support --server or --standalone; use a client command instead
    at <anonymous> (~/.no-mistakes/worktrees/284027b67ce8/01M44ZKZKPA3BCM50S23E98B4N/packages/cli/src/services/server-flags.ts:32:9)
    at cli.server-flags.reject (~/.no-mistakes/worktrees/284027b67ce8/01M44ZKZKPA3BCM50S23E98B4N/packages/cli/src/framework/runtime.ts:107:49)
    at cli.server-flags.reject (definition) (~/.no-mistakes/worktrees/284027b67ce8/01M44ZKZKPA3BCM50S23E98B4N/packages/cli/src/services/server-flags.ts:27:30)
[exit 1]

$ shuvcode pair --standalone
Error: pair does not support --server or --standalone; use a client command instead
    at <anonymous> (~/.no-mistakes/worktrees/284027b67ce8/01M44ZKZKPA3BCM50S23E98B4N/packages/cli/src/services/server-flags.ts:32:9)
    at cli.server-flags.reject (~/.no-mistakes/worktrees/284027b67ce8/01M44ZKZKPA3BCM50S23E98B4N/packages/cli/src/framework/runtime.ts:107:49)
    at cli.server-flags.reject (definition) (~/.no-mistakes/worktrees/284027b67ce8/01M44ZKZKPA3BCM50S23E98B4N/packages/cli/src/services/server-flags.ts:27:30)
[exit 1]

$ shuvcode --server http://127.0.0.1:47830 acp
Error: acp does not support --server or --standalone; use a client command instead
    at <anonymous> (~/.no-mistakes/worktrees/284027b67ce8/01M44ZKZKPA3BCM50S23E98B4N/packages/cli/src/services/server-flags.ts:32:9)
    at cli.server-flags.reject (~/.no-mistakes/worktrees/284027b67ce8/01M44ZKZKPA3BCM50S23E98B4N/packages/cli/src/framework/runtime.ts:107:49)
    at cli.server-flags.reject (definition) (~/.no-mistakes/worktrees/284027b67ce8/01M44ZKZKPA3BCM50S23E98B4N/packages/cli/src/services/server-flags.ts:27:30)
[exit 1]

$ shuvcode acp --standalone
Error: acp does not support --server or --standalone; use a client command instead
    at <anonymous> (~/.no-mistakes/worktrees/284027b67ce8/01M44ZKZKPA3BCM50S23E98B4N/packages/cli/src/services/server-flags.ts:32:9)
    at cli.server-flags.reject (~/.no-mistakes/worktrees/284027b67ce8/01M44ZKZKPA3BCM50S23E98B4N/packages/cli/src/framework/runtime.ts:107:49)
    at cli.server-flags.reject (definition) (~/.no-mistakes/worktrees/284027b67ce8/01M44ZKZKPA3BCM50S23E98B4N/packages/cli/src/services/server-flags.ts:27:30)
[exit 1]

$ shuvcode --server http://127.0.0.1:47830 serve --port 0
Error: serve does not support --server or --standalone; use a client command instead
    at <anonymous> (~/.no-mistakes/worktrees/284027b67ce8/01M44ZKZKPA3BCM50S23E98B4N/packages/cli/src/services/server-flags.ts:32:9)
    at cli.server-flags.reject (~/.no-mistakes/worktrees/284027b67ce8/01M44ZKZKPA3BCM50S23E98B4N/packages/cli/src/framework/runtime.ts:107:49)
    at cli.server-flags.reject (definition) (~/.no-mistakes/worktrees/284027b67ce8/01M44ZKZKPA3BCM50S23E98B4N/packages/cli/src/services/server-flags.ts:27:30)
[exit 1]

$ shuvcode serve --port 0 --standalone
Error: serve does not support --server or --standalone; use a client command instead
    at <anonymous> (~/.no-mistakes/worktrees/284027b67ce8/01M44ZKZKPA3BCM50S23E98B4N/packages/cli/src/services/server-flags.ts:32:9)
    at cli.server-flags.reject (~/.no-mistakes/worktrees/284027b67ce8/01M44ZKZKPA3BCM50S23E98B4N/packages/cli/src/framework/runtime.ts:107:49)
    at cli.server-flags.reject (definition) (~/.no-mistakes/worktrees/284027b67ce8/01M44ZKZKPA3BCM50S23E98B4N/packages/cli/src/services/server-flags.ts:27:30)
[exit 1]

$ shuvcode --server http://127.0.0.1:47830 mcp add x --url http://127.0.0.1:1/mcp --global
Error: mcp add does not support --server or --standalone; use a client command instead
    at <anonymous> (~/.no-mistakes/worktrees/284027b67ce8/01M44ZKZKPA3BCM50S23E98B4N/packages/cli/src/services/server-flags.ts:32:9)
    at cli.server-flags.reject (~/.no-mistakes/worktrees/284027b67ce8/01M44ZKZKPA3BCM50S23E98B4N/packages/cli/src/framework/runtime.ts:107:49)
    at cli.server-flags.reject (definition) (~/.no-mistakes/worktrees/284027b67ce8/01M44ZKZKPA3BCM50S23E98B4N/packages/cli/src/services/server-flags.ts:27:30)
[exit 1]

$ shuvcode mcp add x --url http://127.0.0.1:1/mcp --global --standalone
Error: mcp add does not support --server or --standalone; use a client command instead
    at <anonymous> (~/.no-mistakes/worktrees/284027b67ce8/01M44ZKZKPA3BCM50S23E98B4N/packages/cli/src/services/server-flags.ts:32:9)
    at cli.server-flags.reject (~/.no-mistakes/worktrees/284027b67ce8/01M44ZKZKPA3BCM50S23E98B4N/packages/cli/src/framework/runtime.ts:107:49)
    at cli.server-flags.reject (definition) (~/.no-mistakes/worktrees/284027b67ce8/01M44ZKZKPA3BCM50S23E98B4N/packages/cli/src/services/server-flags.ts:27:30)
[exit 1]

$ shuvcode --server http://127.0.0.1:47830 plugin add foo
Error: plugin add does not support --server or --standalone; use a client command instead
    at <anonymous> (~/.no-mistakes/worktrees/284027b67ce8/01M44ZKZKPA3BCM50S23E98B4N/packages/cli/src/services/server-flags.ts:32:9)
    at cli.server-flags.reject (~/.no-mistakes/worktrees/284027b67ce8/01M44ZKZKPA3BCM50S23E98B4N/packages/cli/src/framework/runtime.ts:107:49)
    at cli.server-flags.reject (definition) (~/.no-mistakes/worktrees/284027b67ce8/01M44ZKZKPA3BCM50S23E98B4N/packages/cli/src/services/server-flags.ts:27:30)
[exit 1]

$ shuvcode plugin add foo --standalone
Error: plugin add does not support --server or --standalone; use a client command instead
    at <anonymous> (~/.no-mistakes/worktrees/284027b67ce8/01M44ZKZKPA3BCM50S23E98B4N/packages/cli/src/services/server-flags.ts:32:9)
    at cli.server-flags.reject (~/.no-mistakes/worktrees/284027b67ce8/01M44ZKZKPA3BCM50S23E98B4N/packages/cli/src/framework/runtime.ts:107:49)
    at cli.server-flags.reject (definition) (~/.no-mistakes/worktrees/284027b67ce8/01M44ZKZKPA3BCM50S23E98B4N/packages/cli/src/services/server-flags.ts:27:30)
[exit 1]

$ shuvcode --server http://127.0.0.1:47830 plugin remove keep-me
Error: plugin remove does not support --server or --standalone; use a client command instead
    at <anonymous> (~/.no-mistakes/worktrees/284027b67ce8/01M44ZKZKPA3BCM50S23E98B4N/packages/cli/src/services/server-flags.ts:32:9)
    at cli.server-flags.reject (~/.no-mistakes/worktrees/284027b67ce8/01M44ZKZKPA3BCM50S23E98B4N/packages/cli/src/framework/runtime.ts:107:49)
    at cli.server-flags.reject (definition) (~/.no-mistakes/worktrees/284027b67ce8/01M44ZKZKPA3BCM50S23E98B4N/packages/cli/src/services/server-flags.ts:27:30)
[exit 1]

$ shuvcode plugin remove keep-me --standalone
Error: plugin remove does not support --server or --standalone; use a client command instead
    at <anonymous> (~/.no-mistakes/worktrees/284027b67ce8/01M44ZKZKPA3BCM50S23E98B4N/packages/cli/src/services/server-flags.ts:32:9)
    at cli.server-flags.reject (~/.no-mistakes/worktrees/284027b67ce8/01M44ZKZKPA3BCM50S23E98B4N/packages/cli/src/framework/runtime.ts:107:49)
    at cli.server-flags.reject (definition) (~/.no-mistakes/worktrees/284027b67ce8/01M44ZKZKPA3BCM50S23E98B4N/packages/cli/src/services/server-flags.ts:27:30)
[exit 1]

$ shuvcode --server http://127.0.0.1:47830 plugin update keep-me
Error: plugin update does not support --server or --standalone; use a client command instead
    at <anonymous> (~/.no-mistakes/worktrees/284027b67ce8/01M44ZKZKPA3BCM50S23E98B4N/packages/cli/src/services/server-flags.ts:32:9)
    at cli.server-flags.reject (~/.no-mistakes/worktrees/284027b67ce8/01M44ZKZKPA3BCM50S23E98B4N/packages/cli/src/framework/runtime.ts:107:49)
    at cli.server-flags.reject (definition) (~/.no-mistakes/worktrees/284027b67ce8/01M44ZKZKPA3BCM50S23E98B4N/packages/cli/src/services/server-flags.ts:27:30)
[exit 1]

$ shuvcode plugin update keep-me --standalone
Error: plugin update does not support --server or --standalone; use a client command instead
    at <anonymous> (~/.no-mistakes/worktrees/284027b67ce8/01M44ZKZKPA3BCM50S23E98B4N/packages/cli/src/services/server-flags.ts:32:9)
    at cli.server-flags.reject (~/.no-mistakes/worktrees/284027b67ce8/01M44ZKZKPA3BCM50S23E98B4N/packages/cli/src/framework/runtime.ts:107:49)
    at cli.server-flags.reject (definition) (~/.no-mistakes/worktrees/284027b67ce8/01M44ZKZKPA3BCM50S23E98B4N/packages/cli/src/services/server-flags.ts:27:30)
[exit 1]

global config unchanged? yes
service config written? none
remote serve still alive? yes

== Scenario: unrelated commands without flags still run; --help shows boundary
$ shuvcode plugin list
No plugins found
[exit 0]

GLOBAL FLAGS
  --help, -h                                                          Show help information
  --version, -v                                                       Show version information
  --wizard                                                            Start wizard mode for a command
  --completions <bash|zsh|fish|sh>                                    Print shell completion script (choices: bash, zsh, fish, sh)
Evidence: Help GLOBAL FLAGS output
$ shuvcode --help
GLOBAL FLAGS
  --help, -h                                                          Show help information
  --version, -v                                                       Show version information
  --wizard                                                            Start wizard mode for a command
  --completions <bash|zsh|fish|sh>                                    Print shell completion script (choices: bash, zsh, fish, sh)
  --log-level <all|trace|debug|info|warn|warning|error|fatal|none>    Sets the minimum log level (choices: all, trace, debug, info, warn, warning, error, fatal, none)
  --print-logs                                                        Print logs to stderr (server logs require --standalone)
  --standalone                                                        Run client commands with a private server; unsupported by service, pair, acp, serve, mcp add, and plugin add/update/remove
  --server string                                                     Connect client commands to a server URL; unsupported by service, pair, acp, serve, mcp add, and plugin add/update/remove


$ shuvcode service stop --help
GLOBAL FLAGS
  --help, -h                                                          Show help information
  --version, -v                                                       Show version information
  --wizard                                                            Start wizard mode for a command
  --completions <bash|zsh|fish|sh>                                    Print shell completion script (choices: bash, zsh, fish, sh)
  --log-level <all|trace|debug|info|warn|warning|error|fatal|none>    Sets the minimum log level (choices: all, trace, debug, info, warn, warning, error, fatal, none)
  --print-logs                                                        Print logs to stderr (server logs require --standalone)
  --standalone                                                        Run client commands with a private server; unsupported by service, pair, acp, serve, mcp add, and plugin add/update/remove
  --server string                                                     Connect client commands to a server URL; unsupported by service, pair, acp, serve, mcp add, and plugin add/update/remove
Evidence: Live drive script
#!/usr/bin/env bash
# Live drive of global --server/--standalone against the real shuvcode CLI.
CLI=~/.no-mistakes/worktrees/284027b67ce8/01M44ZKZKPA3BCM50S23E98B4N/packages/cli/src/index.ts
mk() { local r; r=$(mktemp -d /tmp/sf-live-XXXX); mkdir -p $r/config; echo $r; }
sc() { local r=$1; shift; ( cd $r && env -u OPENCODE_CONFIG_DIR HOME=$r OPENCODE_TEST_HOME=$r OPENCODE_CONFIG_DIR=$r/config OPENCODE_DISABLE_MODELS_FETCH=true OPENCODE_DISABLE_FILEWATCHER=true OPENCODE_DB=$r/opencode.db OPENCODE_PASSWORD=livetest XDG_CACHE_HOME=$r/cache XDG_CONFIG_HOME=$r/config XDG_DATA_HOME=$r/data XDG_STATE_HOME=$r/state timeout ${T:-30} bun run $CLI "$@" ); }
run() { local r=$1; shift; echo "\$ shuvcode $*"; sc $r "$@" 2>&1 | head -12; echo "[exit ${PIPESTATUS[0]}]"; echo; }

SRV=$(mk); CL=$(mk)
PORT=47$((RANDOM%900+100))
echo "== Remote: real 'shuvcode serve --port $PORT' in its own sandbox"
T=600 sc $SRV serve --hostname 127.0.0.1 --port $PORT >$SRV/serve.log 2>&1 &
SPID=$!
for i in $(seq 1 90); do [ "$(curl -s -o /dev/null -w %{http_code} -u opencode:livetest http://127.0.0.1:$PORT/api/info)" = 200 ] && break; sleep 0.5; done
echo "remote /api/info (curl): $(curl -s -u opencode:livetest http://127.0.0.1:$PORT/api/info | head -c 200)"; echo
URL=http://127.0.0.1:$PORT

echo "== Scenario: --server selects the requested remote at every placement (client sandbox has no service)"
run $CL --server $URL api GET /api/info
run $CL api --server $URL GET /api/info
run $CL api GET /api/info --server $URL
run $CL --server $URL session list
run $CL session --server $URL list
run $CL session list --server $URL
run $CL --server $URL plugin list
echo "client sandbox service registration exists? $(ls $CL/state/shuvcode 2>/dev/null || echo none)"; echo

echo "== Adversarial: --server pointing at a dead port really is used (fails, no fallback to a local service)"
run $CL --server http://127.0.0.1:1 session list

echo "== Scenario: --standalone before / between / after subcommand uses a private server"
run $CL --standalone api GET /api/info
run $CL session --standalone list
run $CL plugin list --standalone

echo "== Adversarial: combined flags rejected"
run $CL --standalone session list --server $URL

echo "== Scenario: unsupported commands reject explicit flags before side effects"
echo '{"plugin":["keep-me"]}' > $CL/config/opencode.json
cp $CL/config/opencode.json $CL/before.json
for c in "service stop" "service status" "service set port 1234" "service unset disabled" "pair" "acp" "serve --port 0" "mcp add x --url http://127.0.0.1:1/mcp --global" "plugin add foo" "plugin remove keep-me" "plugin update keep-me"; do
  run $CL --server $URL $c
  run $CL $c --standalone
done
echo "global config unchanged? $(cmp -s $CL/before.json $CL/config/opencode.json && echo yes || echo NO)"
echo "service config written? $(ls $CL/config/shuvcode 2>/dev/null || echo none)"
echo "remote serve still alive? $(curl -sf -u opencode:livetest $URL/api/info >/dev/null && echo yes || echo NO)"; echo

echo "== Scenario: unrelated commands without flags still run; --help shows boundary"
run $CL plugin list
sc $CL --help 2>&1 | grep -A4 "GLOBAL FLAGS"

kill $SPID; wait $SPID 2>/dev/null

rm -rf $SRV $CL
Evidence: Endpoint selection proof
remote /api/info (curl): {"version":"local","pid":3832825,...}
$ shuvcode api GET /api/info --server http://127.0.0.1:47830
{"version":"local","pid":3832825,"urls":["http://127.0.0.1:47830"]...}
$ shuvcode --standalone api GET /api/info
{"version":"local","pid":3838600,"urls":["http://127.0.0.1:35941"]...}
$ shuvcode --server http://127.0.0.1:47830 service stop
Error: service does not support --server or --standalone; use a client command instead
[exit 1]

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

⏭️ **Rebase** - skipped

Step was skipped.

🔧 **Review** - 1 issue found → auto-fixed ✅
  • ⚠️ packages/cli/src/services/server-flags.ts:14 - Before this change, shuvcode --server http://remote mcp add … and shuvcode --server … plugin add|remove|update … failed with "Unrecognized flag", because --server was only a root or per-command param. With global parsing those commands now accept the flag and ignore it: they write the LOCAL project or global opencode config (mcp/add.ts resolveConfigPath and write, plugin add/remove through Config.Service) and exit 0. Meanwhile sibling mcp auth/mcp list/plugin list do go to the requested remote. A user targeting a remote server gets a silent local config mutation instead of an error. The intent allows ignoring flags only on "unrelated non-server commands", and these are config writers that live in the same groups as remote-honoring commands, so whether they should also be marked connectionFlags: &#34;unsupported&#34; is a product decision. Recommended remedy: mark mcp add and plugin add/remove/update unsupported (and list them in the help boundary), or confirm that ignoring the flag there is intended.

🔧 Fix applied.
✅ Re-checked - no issues remain.

✅ **Test** - passed

✅ No issues found.

  • Live validation: ✅ go - 8 of 8 scenarios driven live against the product
Scenario Result Live Evidence
--server before, between, or after the subcommand reaches the requested real remote server ✅ pass live transcript-trimmed.txt: all three placements of api GET /api/info --server return the remote serve pid 3832825; session list and plugin list exit 0
Adversarial: --server pointing at a dead URL is honored and does not fall back to a local service ✅ pass live transcript-trimmed.txt: --server http://127.0.0.1:1 session list → 'Could not reach server', exit 1
--standalone before, between, or after the subcommand runs on a private server ✅ pass live transcript-trimmed.txt: standalone api info shows a different pid and URL from the remote; session/plugin list exit 0; the server-flags test also shows a registered service is never contacted
Adversarial: combining --standalone and --server is rejected ✅ pass live transcript-trimmed.txt: '--server and --standalone cannot be combined', exit 1
service stop/status/set/unset, pair, acp, and serve reject explicit connection flags before any side effect ✅ pass live transcript-trimmed.txt: each exits 1 with '<cmd> does not support --server or --standalone', no service config is written, and the remote serve stays alive; server-flags.test.ts covers the case of a r…
mcp add and plugin add/remove/update reject connection flags without writing config ✅ pass live transcript-trimmed.txt: rejection for all of them, and 'global config unchanged? yes'
Help documents the support boundary for global --server/--standalone ✅ pass live help.txt: root and service stop --help list both flags as 'unsupported by service, pair, acp, serve, mcp add, and plugin add/update/remove'
Unrelated commands without flags still work ✅ pass live transcript-trimmed.txt: shuvcode plugin list → 'No plugins found', exit 0
  • cd packages/cli &amp;&amp; bun test test/server-flags.test.ts (25 pass: rejection with no local service/config/registration effects across 3 placements for service/pair/acp/serve/mcp add/plugin add|update|remove; requested-endpoint and standalone selection across placements; mutual exclusion; help boundary)
  • Live: started a real shuvcode serve --hostname 127.0.0.1 --port 47830 in an isolated XDG sandbox (OPENCODE_PASSWORD set), then ran the real CLI from a separate sandbox with --server &lt;url&gt; before the command, between the command and subcommand, and after the subcommand, for api GET /api/info, session list and plugin list
  • Live: shuvcode --server http://127.0.0.1:1 session list (dead endpoint is honored, no fallback)
  • Live: --standalone placed before, between and after the command for api GET /api/info, session list and plugin list
  • Live: shuvcode --standalone session list --server &lt;url&gt; (mutual exclusion)
  • Live: --server &lt;url&gt; (leading) and --standalone (trailing) for service stop/status/set port/unset disabled, pair, acp, serve, mcp add --global, plugin add/remove/update; checked that global opencode.json was unchanged, that no service config was written, and that the remote serve was still alive
  • Live: shuvcode --help and shuvcode service stop --help GLOBAL FLAGS section
✅ **Document** - passed

✅ No issues found.

✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

Papercut pc_16ace9037047

Register --standalone and --server as global settings so they parse
before or after a subcommand, including plugin list.
@cursor

cursor Bot commented Oct 4, 2026

Copy link
Copy Markdown

Global --server and --standalone are now accepted on every command, but several commands that still pick a server never read them. Those invocations used to fail with an unrecognized flag. They now succeed and act on the local background service (or a newly started private server) anyway.

ServerConnection.resolve() is the only place that honors the settings (packages/cli/src/services/server-connection.ts). These handlers bypass it:

  • service stop and service restart always stop the local background service (packages/cli/src/commands/handlers/service/stop.ts, restart.ts). shuvcode --server http://other:4096 service stop used to be rejected; it now stops the local service and leaves the URL alone.
  • service start always starts that same local service, including with --standalone (start.ts).
  • service set / unset write the local service config (password, disabled, port, env) the same way.
  • pair always pairs the local background service and prints a one-time connect code (packages/cli/src/commands/handlers/pair.ts).
  • acp always calls Standalone.start() and never attaches to --server (packages/cli/src/commands/handlers/acp.ts).
  • serve always binds its own listener (packages/cli/src/commands/handlers/serve.ts).

Help makes this worse: both flags are documented on every command, including these, as “Connect to a server URL instead of the background service” / “Run with a private server…”.

Ignoring them on commands that do not touch a server (upgrade, debug paths, plugin add/remove, mcp add) matches the note on #416. These do not. They should either use the resolved endpoint or fail when either flag is set.

packages/cli/test/server-flags.test.ts only checks that plugin list help lists the flags and that combining them reaches the conflict error. It does not cover a command that still ignores them.

@shuv1337 shuv1337 changed the title fix(cli): accept --standalone before subcommands fix(cli): accept --server and --standalone as global flags Oct 5, 2026
@shuv1337

shuv1337 commented Oct 5, 2026

Copy link
Copy Markdown
Owner Author

Follow-up to #428 (comment), addressed in the pushed head e6fc678:

  • service stop / restart: explicit connection flags now fail before handler loading, so neither the local shutdown request nor Service.stop can run.
  • service start: rejects the flags before Service.ensure can start a local service.
  • service set / unset: rejects the flags before config reads/writes or service lifecycle effects. Regression cases cover disabled, password, port, and env settings. The same boundary covers service get / status.
  • pair, acp, and serve: reject before their handlers can ensure/pair a managed service, spawn a private ACP server, or bind a listener.
  • mcp add and plugin add / remove / update: also reject before local project/global config writes, with added regressions for unchanged config files.
  • Help and the CLI reference state the unsupported command boundary. Global parsing is retained for supported client commands, including before, between, and after command words.
  • The tests now execute actual CLI behavior: explicit endpoint selection for plugin list, private endpoint selection for api and plugin list, mutual exclusion, and unsupported command rejection. Isolated HOME/XDG fixtures verify unchanged service config/registration, no requests to a fake local endpoint, and a live dummy owner after rejection. An explicitly negated --standalone is also rejected on unsupported commands.

Validation: 54 focused CLI tests passed before the pipeline extension; the final server-flags suite has 25 passing tests. The no-mistakes fix review reported no remaining findings, and its runtime scenarios passed against an isolated server. The service-config regression fails against the pre-fix runtime. Fresh CI is running on this pushed head. The captain's installed binary, real config, and live service were not used for these probes.

@shuv1337
shuv1337 merged commit 3c0cfc6 into integration-v2 Oct 5, 2026
6 checks passed
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