Skip to content

Start npm-shim vendor CLIs directly on Windows - #1487

Merged
Lougarou merged 3 commits into
mainfrom
windows-agent-launch
Oct 16, 2026
Merged

Lougarou merged 3 commits into
mainfrom
windows-agent-launch

Conversation

@Lougarou

@Lougarou Lougarou commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

Closes #1486 — AI-3780

What & why

On Windows the app offered OpenCode, Gemini and Copilot, and every launch failed: availability finds npm's .cmd shim through PATHEXT, but the daemon spawns the bare name without a shell, which reaches only .exe. WindowsLaunchCommand resolves the command with the same BinaryProbe and starts an npm shim's own target (node.exe script, or its native .exe), so cmd.exe never parses vendor arguments. It is a no-op off Windows. Antigravity, refused on Windows on platform, is no longer advertised there (#1485 tracks real support).

Where to look

  • The step runs at spawn time, after each factory's pure ProcessStartInfo builder, so builder tests are unchanged.
  • The vendor version probe now resolves too, so Copilot and Gemini versions are advertised instead of unknown.

Verification

Vendor launched through the daemon, Windows 11 Before After
OpenCode Win32Exception (2) handshake, first turn ends
Copilot Win32Exception (2) handshake, first turn ends
Gemini Win32Exception (2) starts; fails at Gemini auth
Claude, Codex, Pi runs runs

From the desktop app on this branch, OpenCode, Copilot, Kiro, Pi and Codex launch and answer. Daemon suite on Windows: 4,171 run, 15 failed — 14 symlink tests needing Developer Mode, 1 ConPTY job-object test on a path this change does not touch.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes
    • Improved Windows compatibility when launching CLI tools and hosted agents, including tools installed through npm command shims.
    • Windows Antigravity is now reported as unavailable when launches are blocked by the platform check.

Lougarou and others added 2 commits October 15, 2026 15:05
Availability resolves a bare name through PATHEXT and finds npm's .cmd shim, but a
shell-less spawn reaches only .exe, so OpenCode, Gemini and Copilot were offered and then
failed to launch. The shim's own target is started so cmd.exe never parses vendor arguments.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every Windows launch is refused on platform, yet availability looked only at the binary,
so the app offered a vendor that could only fail.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: d1b9bbef-c509-4c8f-b2ac-2123b4a2d7e9
📥 Commits

Reviewing files that changed from the base of the PR and between 34208b8 and 3864572.

📒 Files selected for processing: 8
  • src/Capacitor.Cli.Daemon/DaemonRunner.cs
  • src/Capacitor.Cli.Daemon/Harness/Codex/CodexHostedAgentRuntimeFactory.cs
  • src/Capacitor.Cli.Daemon/Harness/Codex/CodexMcpInventory.cs
  • src/Capacitor.Cli.Daemon/Harness/Muse/MuseHookRelay.cs
  • src/Capacitor.Cli.Daemon/Harness/Muse/MuseHostedAgentRuntimeFactory.cs
  • src/Capacitor.Cli.Daemon/Services/AcpHostedAgentRuntimeFactory.cs
  • src/Capacitor.Cli.Daemon/Services/AgentOrchestrator.cs
  • test/Capacitor.Cli.Daemon.Tests.Unit/Harness/Antigravity/AntigravityReviewerLaunchTests.cs
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 1 remain after this review.

📝 Walkthrough

Selected daemon process launch paths now apply Windows-specific command resolution before startup. WindowsLaunchCommand resolves commands and handles readable npm .cmd shims. Antigravity availability now requires a POSIX host.

Changes: Windows CLI launch handling

Layer / File(s) Summary
Windows command resolution
src/Capacitor.Cli.Daemon/Services/WindowsLaunchCommand.cs, test/Capacitor.Cli.Daemon.Tests.Unit/Services/WindowsLaunchCommandTests.cs
WindowsLaunchCommand leaves non-Windows process information unchanged. On Windows, it resolves commands, handles readable npm .cmd shims, and preserves unresolved commands. Unit tests cover shim and non-shim commands, Node resolution, and argument ordering.
Hosted-agent process integration
src/Capacitor.Cli.Daemon/Harness/Antigravity/AntigravityHostedAgentRuntimeFactory.cs, src/Capacitor.Cli.Daemon/Harness/Codex/CodexHostedAgentRuntimeFactory.cs, src/Capacitor.Cli.Daemon/Harness/Muse/MuseHostedAgentRuntimeFactory.cs, src/Capacitor.Cli.Daemon/Harness/Pi/PiRpcHostedAgentRuntimeFactory.cs, src/Capacitor.Cli.Daemon/Services/AcpHostedAgentRuntimeFactory.cs, test/Capacitor.Cli.Daemon.Tests.Unit/Harness/Antigravity/AntigravityReviewerLaunchTests.cs
Hosted-agent process creation applies WindowsLaunchCommand. Antigravity availability now requires a POSIX host, and a test verifies that Windows reports it unavailable.
Other daemon process integration
src/Capacitor.Cli.Daemon/DaemonRunner.cs, src/Capacitor.Cli.Daemon/Harness/Codex/CodexMcpInventory.cs, src/Capacitor.Cli.Daemon/Harness/Muse/MuseHookRelay.cs, src/Capacitor.Cli.Daemon/Services/AgentOrchestrator.cs
The version probe, Codex MCP inventory, Muse hook relay, and WhatsDone generator apply WindowsLaunchCommand before process startup.

Priority: ⬆️ High

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Bug fix · Severity of issue fixed: Medium

Sequence Diagram(s)

sequenceDiagram
  participant RuntimeFactory
  participant WindowsLaunchCommand
  participant BinaryProbe
  participant ProcessStart
  RuntimeFactory->>WindowsLaunchCommand: Apply process start information and binary settings
  WindowsLaunchCommand->>BinaryProbe: Resolve command on Windows
  BinaryProbe-->>WindowsLaunchCommand: Resolved command path
  WindowsLaunchCommand-->>RuntimeFactory: Return adjusted process start information
  RuntimeFactory->>ProcessStart: Start process
Loading

Suggested reviewers: realtonyyoung, inok

Merge Risk: ⚪ Minimal · up to 38645

The Windows launch changes are mergeable; no actionable correctness or availability risk is established beyond normal validation.

Security architecture risk: 🔵 Low · up to 38645

Direct execution avoids shell parsing, and the inspected launch paths retain their existing containment and identity controls. No introduced security defect was established. Residual uncertainty remains around startup environment trust and installation permissions on deployed Windows hosts.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — The relevant exposure is Windows child-process execution under the daemon's existing authority and prepared environment. The PR newly makes npm-shim targets reachable at these launch sites. Control of selected shim, script, native target, or Node files would therefore matter beyond argument integrity, but the review did not establish that an untrusted actor can modify those inputs or gain additional privileges.

Security Findings and Attack Paths

  • observed — The inspected helper launches do not convert hook payloads or session identifiers into shell commands. Muse hook JSON remains standard-input data, and the summary generator receives its session identifier as an ArgumentList entry. The new resolver does not add cmd.exe parsing to either path.

Trust Boundaries and Controls

  • observed — Borrowed-review sandbox support remains restricted to macOS ARM64 with sandbox and authentication-broker availability. The resolver returns unchanged start information on that platform. Applying it after the builder therefore does not bypass the inspected borrowed-review sandbox wrapper or state-directory preparation.
  • observed — The shim parser accepts native executable and JavaScript target extensions and constructs a direct process launch rather than interpreting batch commands. These restrictions preserve a no-shell boundary; they do not authenticate the selected installation or establish filesystem confinement of its contents.

Resilience and Maintainability Implications

  • observed — The Muse launch lifecycle retains per-call start information and process ownership, readiness-before-return ordering, failure disposal, and session-marker cleanup. Resolution occurs before its process consumer starts the child. The inspected change does not introduce shared mutable launch state or a new recovery owner.

Hardening Proposals

  • proposed — Document the trusted-ownership requirements for daemon executable-search directories, npm installations, and Node. Deployment validation of those permissions would close the remaining authority question; this is a hardening proposal, not an observed privilege-escalation finding.
Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 22 functions across 12 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 4 passed
Check name Status Explanation
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check Passed The title clearly and concisely describes the main change: direct Windows launches for npm-shim vendor CLIs.
Linked Issues check Passed Issue #1486 requires Windows launch support for OpenCode, Gemini, and Copilot. WindowsLaunchCommand.Apply resolves commands with BinaryProbe and starts npm shims through their Node script or nativ…
Out of Scope Changes check Passed The changes stay within #1486. The resolver, launch call-site updates, version-probe update, Antigravity Windows gate, and supporting tests all address the documented Windows bare-spawn and advertisin…
Full details: Linked Issues check

Explanation

Issue #1486 requires Windows launch support for OpenCode, Gemini, and Copilot. WindowsLaunchCommand.Apply resolves commands with BinaryProbe and starts npm shims through their Node script or native target. The call sites include ACP launches, version probing, Codex MCP inventory and app-server launches, Pi, and Muse. Unit tests cover shim resolution, native targets, unresolved commands, and argument ordering. Antigravity now returns unavailable on Windows, as required by #1486.

Full details: Out of Scope Changes check

Explanation

The changes stay within #1486. The resolver, launch call-site updates, version-probe update, Antigravity Windows gate, and supporting tests all address the documented Windows bare-spawn and advertising defects. No unrelated feature change is shown.

  • Fix all pre-merge checks with AI
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Launch npm-shim vendor CLIs directly on Windows

🐞 Bug fix 🧪 Tests 🕐 40+ Minutes

Grey Divider

AI Description

• Resolve Windows vendor commands to launchable targets, avoiding shell parsing of CLI arguments.
• Apply resolution at daemon spawn and version-probe sites without changing process-info builders.
• Hide Antigravity on Windows, where its launches are refused.
Diagram

graph TD
  B["Process builder"] --> A["Launch command"] --> W{"Windows host?"}
  W -->|yes| P["BinaryProbe"] --> S{"npm shim?"}
  S -->|yes| R["NpmCmdShim"] --> T["Direct target"] --> X["Process.Start"]
  W -->|no| X
  S -->|no| X
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Launch shims through cmd.exe
  • ➕ Uses Windows' command-script handling directly.
  • ➖ Exposes vendor arguments to shell parsing, including multiline prompts and special characters.
2. Centralize all daemon process creation
  • ➕ Could enforce launch resolution at one process-start boundary.
  • ➖ Requires a broader refactor of distinct runtime and utility-process paths.

Recommendation: Keep spawn-time shim resolution: it reuses BinaryProbe and the existing shim reader while preserving pure builders and avoiding shell parsing. A centralized process launcher may be worthwhile separately, but is not needed for this fix.

Files changed (12) +126 / -11

Bug fix (10) +49 / -11
DaemonRunner.csResolve the CLI before probing its version +3/-2

Resolve the CLI before probing its version

• Applies Windows launch resolution to the version subprocess so npm-shim vendors can report their versions.

src/Capacitor.Cli.Daemon/DaemonRunner.cs

AntigravityHostedAgentRuntimeFactory.csHide unsupported Windows launches +4/-2

Hide unsupported Windows launches

• Advertises Antigravity only on a POSIX host with an available binary. Its real process path also applies launch resolution.

src/Capacitor.Cli.Daemon/Harness/Antigravity/AntigravityHostedAgentRuntimeFactory.cs

CodexHostedAgentRuntimeFactory.csResolve Codex before spawning +2/-0

Resolve Codex before spawning

• Applies Windows launch resolution to the Codex subprocess after configuring its start information.

src/Capacitor.Cli.Daemon/Harness/Codex/CodexHostedAgentRuntimeFactory.cs

CodexMcpInventory.csResolve the Codex MCP inventory command +3/-2

Resolve the Codex MCP inventory command

• Uses Windows launch resolution when starting the command that lists inherited MCP servers.

src/Capacitor.Cli.Daemon/Harness/Codex/CodexMcpInventory.cs

MuseHookRelay.csResolve the Muse hook relay command +2/-1

Resolve the Muse hook relay command

• Applies Windows launch resolution before starting the hook relay subprocess.

src/Capacitor.Cli.Daemon/Harness/Muse/MuseHookRelay.cs

MuseHostedAgentRuntimeFactory.csResolve Muse's real process launch +1/-1

Resolve Muse's real process launch

• Applies launch resolution when constructing the real Muse process, leaving the injectable process source intact.

src/Capacitor.Cli.Daemon/Harness/Muse/MuseHostedAgentRuntimeFactory.cs

PiRpcHostedAgentRuntimeFactory.csResolve Pi's real process launch +1/-1

Resolve Pi's real process launch

• Applies launch resolution when constructing the real Pi RPC process.

src/Capacitor.Cli.Daemon/Harness/Pi/PiRpcHostedAgentRuntimeFactory.cs

AcpHostedAgentRuntimeFactory.csResolve ACP vendor commands at spawn time +1/-1

Resolve ACP vendor commands at spawn time

• Transforms the built process start information only when starting a real ACP vendor process.

src/Capacitor.Cli.Daemon/Services/AcpHostedAgentRuntimeFactory.cs

AgentOrchestrator.csResolve the what's-done subprocess +1/-1

Resolve the what's-done subprocess

• Applies Windows launch resolution before starting the what's-done generation command.

src/Capacitor.Cli.Daemon/Services/AgentOrchestrator.cs

WindowsLaunchCommand.csAdd Windows npm-shim launch resolution +31/-0

Add Windows npm-shim launch resolution

• Resolves configured commands through BinaryProbe and reads recognized npm shims to launch their executable or Node script directly. Leaves non-Windows start information unchanged.

src/Capacitor.Cli.Daemon/Services/WindowsLaunchCommand.cs

Tests (2) +77 / -0
AntigravityReviewerLaunchTests.csTest Antigravity's Windows availability +6/-0

Test Antigravity's Windows availability

• Verifies that a non-POSIX host does not advertise Antigravity even when its binary exists.

test/Capacitor.Cli.Daemon.Tests.Unit/Harness/Antigravity/AntigravityReviewerLaunchTests.cs

WindowsLaunchCommandTests.csTest Windows command and shim resolution +71/-0

Test Windows command and shim resolution

• Covers Node scripts, native targets, bare Node, non-npm cmd files, executables, and missing commands. A Windows-only test verifies that the script precedes vendor arguments.

test/Capacitor.Cli.Daemon.Tests.Unit/Services/WindowsLaunchCommandTests.cs

@qodo-code-review

qodo-code-review Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (1) 📎 Requirement gaps (1) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Remediation recommended

1. Windows version checks still fail 📎 Requirement gap ☼ Reliability
Description
VendorVersionResolver.Resolve starts the .cmd path returned by BinaryProbe without applying
WindowsLaunchCommand, so Windows still attempts to launch the npm shim directly without its Node
target. Any Muse, Pi, or Antigravity version probe that resolves to an npm .cmd shim therefore
reports an unknown version and can incorrectly make the vendor unavailable.
Code

src/Capacitor.Cli.Daemon/Services/WindowsLaunchCommand.cs[R15-20]

+        var (program, script) = Resolve(psi.FileName, binaries.Resolve, NpmCmdShim.TryRead);
+        psi.FileName = program;
+        if (script is not null) {
+            if (string.IsNullOrEmpty(psi.Arguments)) psi.ArgumentList.Insert(0, script);
+            else psi.Arguments = $"\"{script}\" {psi.Arguments}";
+        }
Relevance

●●● Strong

PR explicitly extends Windows shim handling to version probes; similar binary-probe consistency
fixes were accepted.

PR-#726
PR-#393

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The new launch helper transforms a ProcessStartInfo only when callers invoke
WindowsLaunchCommand.Apply, while the existing version resolver resolves a path and starts it
directly. The checklist requires the same Windows shim handling for vendor version probes, so a
.cmd result from BinaryProbe remains an unlaunchable direct target in that path.

Launch Windows CLI commands resolved through PATHEXT
src/Capacitor.Cli.Daemon/Services/WindowsLaunchCommand.cs[12-20]
src/Capacitor.Cli.Core/VendorVersionResolver.cs[10-22]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
WindowsLaunchCommand is applied to vendor launches, but VendorVersionResolver still starts the `.cmd` path returned by BinaryProbe directly. Version probes for npm-installed Muse, Pi, or Antigravity CLIs can therefore fail and report an unknown version on Windows.

## Fix Focus Areas
- src/Capacitor.Cli.Daemon/Services/WindowsLaunchCommand.cs[12-20]
- src/Capacitor.Cli.Core/VendorVersionResolver.cs[10-22]

## Recommended Fix
Apply the Windows launch transformation to the ProcessStartInfo used by VendorVersionResolver, passing the same BinaryProbe instance, so `.cmd` shims are replaced with their native executable or Node script target before Process.Start. Add a Windows-focused test that resolves a vendor version through an npm shim and verifies the transformed executable and arguments.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Dismiss ↗ | View ↗



Informational

2. The test bypasses managed temp cleanup 📘 Rule violation ▣ Testability
Description
WindowsLaunchCommandTests.Apply_puts_the_shim_script_ahead_of_the_vendor_arguments creates a
TempDir locally with new TempDir() instead of receiving the framework-managed injected property.
Because this test class is instantiated by TUnit, its temporary directory lifecycle is outside the
required injection mechanism and can leave cleanup behavior dependent on the test method's local
disposal.
Code

test/Capacitor.Cli.Daemon.Tests.Unit/Services/WindowsLaunchCommandTests.cs[R60-63]

+    public async Task Apply_puts_the_shim_script_ahead_of_the_vendor_arguments() {
+        using var tmp = new TempDir();
+        var node   = tmp.CreateFile("node.exe");
+        var script = tmp.CreateFile("vendor.js");
Relevance

● Weak

Recent same-repository TUnit findings rejected mandatory injected TempDir conversions, including
Windows daemon tests.

PR-#1176
PR-#869

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The changed test method creates a TempDir manually at lines 61-64. Compliance rule 2808173
requires test classes that need temporary directories to use a single public required [TempDir]
property and prohibits manually managed TempDir instances.

Rule 2808173: Use injected [TempDir] public required property in test classes instead of manual fields
test/Capacitor.Cli.Daemon.Tests.Unit/Services/WindowsLaunchCommandTests.cs[11-12]
test/Capacitor.Cli.Daemon.Tests.Unit/Services/WindowsLaunchCommandTests.cs[60-66]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The test class manually creates a `TempDir` inside the test method, but TUnit test classes must receive temporary directories through a public required property decorated with `[TempDir]`.

## Fix Focus Areas
- test/Capacitor.Cli.Daemon.Tests.Unit/Services/WindowsLaunchCommandTests.cs[11-66]

## Recommended Fix
Add a public required `[TempDir]` property to `WindowsLaunchCommandTests`, remove the local `new TempDir()` declaration, and use the injected property's helper methods for the temporary files and path.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Dismiss ↗ | View ↗


Grey Divider

Context sources
✅ Compliance rules (platform): 63 rules
✅ Cross-repo context — repo relationships
Review mode: Auto: ⚖️ Balanced: Downgraded extended -> standard: change is below the extended eligibility bar (hunks 17/18, lines 137/200; both must reach the floor). Router rationale: Cross-cutting Windows process changes span many independent launch and probing paths.

Grey Divider

Tip of the day
💡 Did you know, you can pick a display mode instead of tweaking every Display option by hand

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment on lines +15 to +20
var (program, script) = Resolve(psi.FileName, binaries.Resolve, NpmCmdShim.TryRead);
psi.FileName = program;
if (script is not null) {
if (string.IsNullOrEmpty(psi.Arguments)) psi.ArgumentList.Insert(0, script);
else psi.Arguments = $"\"{script}\" {psi.Arguments}";
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Remediation recommended

1. Windows version checks still fail 📎 Requirement gap ☼ Reliability

VendorVersionResolver.Resolve starts the .cmd path returned by BinaryProbe without applying
WindowsLaunchCommand, so Windows still attempts to launch the npm shim directly without its Node
target. Any Muse, Pi, or Antigravity version probe that resolves to an npm .cmd shim therefore
reports an unknown version and can incorrectly make the vendor unavailable.
Agent Prompt
## Issue description
WindowsLaunchCommand is applied to vendor launches, but VendorVersionResolver still starts the `.cmd` path returned by BinaryProbe directly. Version probes for npm-installed Muse, Pi, or Antigravity CLIs can therefore fail and report an unknown version on Windows.

## Fix Focus Areas
- src/Capacitor.Cli.Daemon/Services/WindowsLaunchCommand.cs[12-20]
- src/Capacitor.Cli.Core/VendorVersionResolver.cs[10-22]

## Recommended Fix
Apply the Windows launch transformation to the ProcessStartInfo used by VendorVersionResolver, passing the same BinaryProbe instance, so `.cmd` shims are replaced with their native executable or Node script target before Process.Start. Add a Windows-focused test that resolves a vendor version through an npm shim and verifies the transformed executable and arguments.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Dismiss ↗ | View ↗

@Lougarou
Lougarou merged commit 8d21762 into main Oct 16, 2026
10 checks passed
@Lougarou
Lougarou deleted the windows-agent-launch branch October 16, 2026 15:29
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.

OpenCode, Gemini and Copilot fail to launch from the Windows desktop app

2 participants