fix: Windows compatibility — bypass wrapper, disable MCP bridge, CLI mode default, .qodersec support - #3
Conversation
The ensureQoderCLICompatWrapper creates a Unix-style shebang script (#!/usr/bin/env node) and passes it to the vendored SDK as the CLI path. On Windows, child_process.spawn() cannot execute shebang scripts directly, causing the Qoder CLI to never start — OpenCode hangs indefinitely waiting for a response with no error message. Since the vendored SDK's buildCommand() already omits --verbose, --storage-dir, and --resource-dir (the only flags the wrapper stripped), the compat wrapper is unnecessary on any platform. On Windows it actively breaks the spawn. Bypass it for win32 and use the real qodercli.exe.
There was a problem hiding this comment.
🟡 Changes recommended
On Windows, resolveQoderCLI() can still return a .cmd shim which the vendored SDK cannot spawn with shell:false, so the hang can persist unless the SDK path is guaranteed to be a real .exe (and tests should cover the new win32 branch).
Once you've addressed the issues Copilot identified, you can request another Copilot review.
This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.
Pull request overview
This PR addresses a Windows-specific hang when using the Qoder SDK transport by avoiding the Unix shebang-based compat wrapper and passing a spawnable CLI path to the vendored SDK.
Changes:
- Add a Windows (
win32) branch inresolveQoderCLIForSDK()to bypass the compat wrapper and use the real CLI path. - Document in-code why the wrapper is incompatible with Windows spawning and why it’s no longer needed for argument stripping.
File summaries
| File | Description |
|---|---|
src/qoder-language-model.ts |
Skips the shebang compat wrapper on Windows when resolving the CLI path for the vendored SDK to prevent spawn() hangs. |
Review details
- Files reviewed: 1/1 changed files
- Comments generated: 2
- Review effort level: Lite
We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.
| if (process.platform === 'win32') { | ||
| return cliPath | ||
| } |
| function resolveQoderCLIForSDK(cliPath: string): string { | ||
| // On Windows, use the real exe directly — the compat wrapper (shebang script | ||
| // `#!/usr/bin/env node`) can't be spawned by child_process on Windows. | ||
| // The wrapper only existed to strip --verbose/--storage-dir/--resource-dir, | ||
| // which are already removed in the vendored SDK's buildCommand(). |
…dePartialMessages Three fixes for OpenCode plugin stability on Windows: 1. Windows spawn hang (qoder-language-model.ts): Bypass the Unix shebang compat wrapper on Windows — child_process.spawn() cannot execute '#!/usr/bin/env node' scripts. Use the real qodercli.exe directly since the vendored SDK already omits the flags the wrapper stripped. 2. MCP bridge hang (mcp-bridge.ts): Don't bridge OpenCode config.mcp servers to qodercli. The CLI process receives --mcp-config and blocks trying to connect to each server (e.g. Docker containers). If any server is unreachable, qodercli never produces output beyond hook_started/hook_response, and OpenCode hangs indefinitely. OpenCode already handles MCP tools via the functionTools layer. 3. Remove includePartialMessages (qoder-language-model.ts): qodercli 1.1.15 rejects --include-partial-messages unless --print is also passed. Since this option only affects stream-json mode and offers minor benefit, drop it to avoid the flag incompatibility across CLI versions.
Problem
When using the
opencode-qoder-authplugin on Windows, selecting any Qoder model causes OpenCode to hang indefinitely with no error output.Root Causes & Fixes
1. Windows compat wrapper spawn hang
The plugin's
resolveQoderCLIForSDK()unconditionally creates a Unix-style shebang script (#!/usr/bin/env node) and passes it to the vendored SDK as the CLI path. On Windows,child_process.spawn()cannot execute shebang scripts — it needs a real executable or .cmd file. The process silently fails to start.Fix: On Windows, bypass the wrapper and use the real
qodercli.exedirectly. The wrapper only existed to strip--verbose,--storage-dir, and--resource-dir— but the vendored SDK'sbuildCommand()already omits these flags (line 565), so the wrapper serves no purpose on any platform.2. MCP bridge hangs qodercli
The plugin bridges OpenCode's
config.mcpservers toqoderclivia--mcp-config. The CLI process attempts to connect to each server during startup. If any server is unreachable (e.g. a Docker container not running),qodercliblocks on the connection and never produces output beyondhook_started/hook_response. OpenCode waits forever.Fix: Disable the MCP bridge in both
mcp-bridge.ts(return empty) andindex.ts(don't add bridged servers to provider options). OpenCode already handles MCP tools itself via the functionTools layer — qodercli doesn't need them.3.
--include-partial-messagesincompatibilityqodercli1.1.15 rejects--include-partial-messagesunless--printis also passed (Error: --include-partial-messages requires --print and --output-format=stream-json). The plugin hardcodesincludePartialMessages: true.Fix: Remove it. This option only affects
stream-jsonmode and provides minor benefit — dropping it avoids flag incompatibilities across CLI versions.4. Qoder CLI directory migration (
.qodersec)Recent Qoder versions moved the CLI from
~/.qoderto~/.qodersec. The plugin'sresolveQoderCLI()only checked the old paths, causing "Qoder CLI not found" errors.Fix: Updated
resolveQoderCLI()to search.qodersecfirst (new location), then fall back to.qoder(legacy).5. SDK streaming broken on Windows — use CLI mode by default
The vendored SDK's streaming mode (
--output-format stream-json --input-format stream-json) only produceshook_started+hook_responsethen exits on Windows, never emittinginitorassistantmessages. The root cause appears to be a stdin/stdout pipe issue in the SDK'sSubprocessTransport.Fix: On Windows, default to CLI mode (
mode: 'cli') which callsqodercli --print -p <prompt>and returns stdout directly. This works reliably. Users can still force SDK mode withprovider.qoder.options.mode: 'sdk'if needed.Testing
Tested on Windows 10 with
qodercli1.1.15 and OpenCode 1.18.11:opencode run -m qoder/lite 'hello'→ hangs forever at> build · liteConfiguration
No additional config needed beyond adding the plugin:
{ "plugin": ["opencode-qoder-auth"] }Optional: force SDK mode on Windows (not recommended):
{ "plugin": ["opencode-qoder-auth"], "provider": { "qoder": { "options": { "mode": "sdk" } } } }Files Changed
src/qoder-language-model.ts: Windows wrapper bypass, CLI mode default, .qodersec pathssrc/mcp-bridge.ts: Disable MCP bridgesrc/index.ts: Don't add bridged MCP to provider optionssrc/mcp-bridge.ts: Return empty from getMcpBridgeServers()