From 361492247ebc6e7ed0f3b4ddd4424b8967711e0d Mon Sep 17 00:00:00 2001 From: yiliang114 <1204183885@qq.com> Date: Wed, 7 Jan 2026 01:35:05 +0800 Subject: [PATCH 1/7] fix(vscode-ide-companion): fix cross-platform CLI execution in terminal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add platform.ts utility with isWindows constant - Fix Windows PowerShell execution with & call operator - Fix macOS Electron helper with ELECTRON_RUN_AS_NODE=1 - Prefer system Node.js, fallback to VS Code runtime - Refactor platform detection in acpMessageHandler and acpSessionManager 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .../vscode-ide-companion/src/extension.ts | 37 ++++++++++++++++++- .../src/services/acpMessageHandler.ts | 3 +- .../src/services/acpSessionManager.ts | 3 +- .../src/utils/platform.ts | 8 ++++ 4 files changed, 47 insertions(+), 4 deletions(-) create mode 100644 packages/vscode-ide-companion/src/utils/platform.ts diff --git a/packages/vscode-ide-companion/src/extension.ts b/packages/vscode-ide-companion/src/extension.ts index 2f2b462f7..b158665d9 100644 --- a/packages/vscode-ide-companion/src/extension.ts +++ b/packages/vscode-ide-companion/src/extension.ts @@ -17,11 +17,25 @@ import { import { WebViewProvider } from './webview/WebViewProvider.js'; import { registerNewCommands } from './commands/index.js'; import { ReadonlyFileSystemProvider } from './services/readonlyFileSystemProvider.js'; +import { isWindows } from './utils/platform.js'; +import { execSync } from 'child_process'; const CLI_IDE_COMPANION_IDENTIFIER = 'qwenlm.qwen-code-vscode-ide-companion'; const INFO_MESSAGE_SHOWN_KEY = 'qwenCodeInfoMessageShown'; export const DIFF_SCHEME = 'qwen-diff'; +/** + * Check if Node.js is available in the system PATH + */ +function isNodeAvailable(): boolean { + try { + execSync(isWindows ? 'where node' : 'which node', { stdio: 'ignore' }); + return true; + } catch { + return false; + } +} + /** * IDE environments where the installation greeting is hidden. In these * environments we either are pre-installed and the installation message is @@ -312,8 +326,27 @@ export async function activate(context: vscode.ExtensionContext) { 'qwen-cli', 'cli.js', ).fsPath; - const quote = (s: string) => `"${s.replaceAll('"', '\\"')}"`; - const qwenCmd = `${quote(process.execPath)} ${quote(cliEntry)}`; + const quote = (s: string) => `"${s.replace(/"/g, '\\"')}"`; + + let qwenCmd: string; + if (isNodeAvailable()) { + // Prefer system Node.js + qwenCmd = `node ${quote(cliEntry)}`; + } else { + // Fallback to VS Code's bundled Node.js runtime + const execPath = process.execPath; + const baseCmd = `${quote(execPath)} ${quote(cliEntry)}`; + if (isWindows) { + // PowerShell requires & call operator for quoted paths + qwenCmd = `& ${baseCmd}`; + } else if (execPath.toLowerCase().includes('code helper')) { + // macOS Electron helper needs ELECTRON_RUN_AS_NODE=1 + qwenCmd = `ELECTRON_RUN_AS_NODE=1 ${baseCmd}`; + } else { + qwenCmd = baseCmd; + } + } + const terminal = vscode.window.createTerminal({ name: `Qwen Code (${selectedFolder.name})`, cwd: selectedFolder.uri.fsPath, diff --git a/packages/vscode-ide-companion/src/services/acpMessageHandler.ts b/packages/vscode-ide-companion/src/services/acpMessageHandler.ts index 8766fdf31..c2fad7701 100644 --- a/packages/vscode-ide-companion/src/services/acpMessageHandler.ts +++ b/packages/vscode-ide-companion/src/services/acpMessageHandler.ts @@ -26,6 +26,7 @@ import type { } from '../types/connectionTypes.js'; import { AcpFileHandler } from '../services/acpFileHandler.js'; import type { ChildProcess } from 'child_process'; +import { isWindows } from '../utils/platform.js'; /** * ACP Message Handler Class @@ -47,7 +48,7 @@ export class AcpMessageHandler { sendResponseMessage(child: ChildProcess | null, response: AcpResponse): void { if (child?.stdin) { const jsonString = JSON.stringify(response); - const lineEnding = process.platform === 'win32' ? '\r\n' : '\n'; + const lineEnding = isWindows ? '\r\n' : '\n'; child.stdin.write(jsonString + lineEnding); } } diff --git a/packages/vscode-ide-companion/src/services/acpSessionManager.ts b/packages/vscode-ide-companion/src/services/acpSessionManager.ts index e2055a3a2..2d85d20aa 100644 --- a/packages/vscode-ide-companion/src/services/acpSessionManager.ts +++ b/packages/vscode-ide-companion/src/services/acpSessionManager.ts @@ -19,6 +19,7 @@ import type { ApprovalModeValue } from '../types/approvalModeValueTypes.js'; import { AGENT_METHODS } from '../constants/acpSchema.js'; import type { PendingRequest } from '../types/connectionTypes.js'; import type { ChildProcess } from 'child_process'; +import { isWindows } from '../utils/platform.js'; /** * ACP Session Manager Class @@ -102,7 +103,7 @@ export class AcpSessionManager { ): void { if (child?.stdin) { const jsonString = JSON.stringify(message); - const lineEnding = process.platform === 'win32' ? '\r\n' : '\n'; + const lineEnding = isWindows ? '\r\n' : '\n'; child.stdin.write(jsonString + lineEnding); } } diff --git a/packages/vscode-ide-companion/src/utils/platform.ts b/packages/vscode-ide-companion/src/utils/platform.ts new file mode 100644 index 000000000..994aadc3f --- /dev/null +++ b/packages/vscode-ide-companion/src/utils/platform.ts @@ -0,0 +1,8 @@ +/** + * @license + * Copyright 2025 Qwen Team + * SPDX-License-Identifier: Apache-2.0 + */ + +/** Whether the current platform is Windows */ +export const isWindows = process.platform === 'win32'; From 052337861b090b76a69863c2c46495b8682c7949 Mon Sep 17 00:00:00 2001 From: yiliang114 <1204183885@qq.com> Date: Wed, 7 Jan 2026 21:05:49 +0800 Subject: [PATCH 2/7] Fix #1416 --- packages/vscode-ide-companion/src/extension.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/vscode-ide-companion/src/extension.ts b/packages/vscode-ide-companion/src/extension.ts index b158665d9..73c86949c 100644 --- a/packages/vscode-ide-companion/src/extension.ts +++ b/packages/vscode-ide-companion/src/extension.ts @@ -340,8 +340,8 @@ export async function activate(context: vscode.ExtensionContext) { // PowerShell requires & call operator for quoted paths qwenCmd = `& ${baseCmd}`; } else if (execPath.toLowerCase().includes('code helper')) { - // macOS Electron helper needs ELECTRON_RUN_AS_NODE=1 - qwenCmd = `ELECTRON_RUN_AS_NODE=1 ${baseCmd}`; + // macOS Electron helper needs ELECTRON_RUN_AS_NODE=1; add -i to force TUI mode + qwenCmd = `ELECTRON_RUN_AS_NODE=1 ${baseCmd} -i`; } else { qwenCmd = baseCmd; } From 95efe89ac01f7372016b451e891b0b11153dcfa7 Mon Sep 17 00:00:00 2001 From: tanzhenxin Date: Fri, 9 Jan 2026 14:49:57 +0800 Subject: [PATCH 3/7] fix positional argument problem due to special handling for Electron app of yargs --- packages/cli/src/config/config.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/packages/cli/src/config/config.ts b/packages/cli/src/config/config.ts index 7cd7d685a..3f781c46e 100755 --- a/packages/cli/src/config/config.ts +++ b/packages/cli/src/config/config.ts @@ -163,7 +163,17 @@ function normalizeOutputFormat( } export async function parseArguments(settings: Settings): Promise { - const rawArgv = hideBin(process.argv); + let rawArgv = hideBin(process.argv); + + // hack: if the first argument is the CLI entry point, remove it + if ( + rawArgv.length > 0 && + (rawArgv[0].endsWith('/dist/qwen-cli/cli.js') || + rawArgv[0].endsWith('/dist/cli.js')) + ) { + rawArgv = rawArgv.slice(1); + } + const yargsInstance = yargs(rawArgv) .locale('en') .scriptName('qwen') From 8ea9871d23bd2272637d4764aa9c9b29d40fe32f Mon Sep 17 00:00:00 2001 From: yiliang114 <1204183885@qq.com> Date: Sat, 10 Jan 2026 19:52:25 +0800 Subject: [PATCH 4/7] fix(vscode-ide-companion): fix positional argument problem due to special handling for Electron app of yargs - Remove isNodeAvailable function and related child_process import - Update command execution logic to properly handle ELECTRON_RUN_AS_NODE - Add proper quoting mechanisms for different platforms (PowerShell vs POSIX) - Bump version from 0.6.1 to 0.6.2 --- packages/vscode-ide-companion/package.json | 2 +- .../vscode-ide-companion/src/extension.ts | 46 ++++++++----------- 2 files changed, 21 insertions(+), 27 deletions(-) diff --git a/packages/vscode-ide-companion/package.json b/packages/vscode-ide-companion/package.json index 50982df00..0d227e4b6 100644 --- a/packages/vscode-ide-companion/package.json +++ b/packages/vscode-ide-companion/package.json @@ -2,7 +2,7 @@ "name": "qwen-code-vscode-ide-companion", "displayName": "Qwen Code Companion", "description": "Enable Qwen Code with direct access to your VS Code workspace.", - "version": "0.6.1", + "version": "0.6.2", "publisher": "qwenlm", "icon": "assets/icon.png", "repository": { diff --git a/packages/vscode-ide-companion/src/extension.ts b/packages/vscode-ide-companion/src/extension.ts index 73c86949c..b000b00c3 100644 --- a/packages/vscode-ide-companion/src/extension.ts +++ b/packages/vscode-ide-companion/src/extension.ts @@ -18,24 +18,11 @@ import { WebViewProvider } from './webview/WebViewProvider.js'; import { registerNewCommands } from './commands/index.js'; import { ReadonlyFileSystemProvider } from './services/readonlyFileSystemProvider.js'; import { isWindows } from './utils/platform.js'; -import { execSync } from 'child_process'; const CLI_IDE_COMPANION_IDENTIFIER = 'qwenlm.qwen-code-vscode-ide-companion'; const INFO_MESSAGE_SHOWN_KEY = 'qwenCodeInfoMessageShown'; export const DIFF_SCHEME = 'qwen-diff'; -/** - * Check if Node.js is available in the system PATH - */ -function isNodeAvailable(): boolean { - try { - execSync(isWindows ? 'where node' : 'which node', { stdio: 'ignore' }); - return true; - } catch { - return false; - } -} - /** * IDE environments where the installation greeting is hidden. In these * environments we either are pre-installed and the installation message is @@ -326,22 +313,29 @@ export async function activate(context: vscode.ExtensionContext) { 'qwen-cli', 'cli.js', ).fsPath; - const quote = (s: string) => `"${s.replace(/"/g, '\\"')}"`; + const execPath = process.execPath; + const lowerExecPath = execPath.toLowerCase(); + const needsElectronRunAsNode = + lowerExecPath.includes('code') || + lowerExecPath.includes('electron'); let qwenCmd: string; - if (isNodeAvailable()) { - // Prefer system Node.js - qwenCmd = `node ${quote(cliEntry)}`; + if (isWindows) { + // Wrap with PowerShell to avoid quoting issues in different Windows shells + const quotePwsh = (s: string) => `'${s.replace(/'/g, "''")}'`; + const psParts = [ + needsElectronRunAsNode ? '$Env:ELECTRON_RUN_AS_NODE=1;' : '', + `& ${quotePwsh(execPath)}`, + needsElectronRunAsNode ? '--ms-enable-electron-run-as-node' : '', + quotePwsh(cliEntry), + ].filter(Boolean); + qwenCmd = `powershell -NoLogo -NoProfile -Command "& { ${psParts.join(' ')} }"`; } else { - // Fallback to VS Code's bundled Node.js runtime - const execPath = process.execPath; - const baseCmd = `${quote(execPath)} ${quote(cliEntry)}`; - if (isWindows) { - // PowerShell requires & call operator for quoted paths - qwenCmd = `& ${baseCmd}`; - } else if (execPath.toLowerCase().includes('code helper')) { - // macOS Electron helper needs ELECTRON_RUN_AS_NODE=1; add -i to force TUI mode - qwenCmd = `ELECTRON_RUN_AS_NODE=1 ${baseCmd} -i`; + const quotePosix = (s: string) => `"${s.replace(/"/g, '\\"')}"`; + const baseCmd = `${quotePosix(execPath)} ${quotePosix(cliEntry)}`; + if (needsElectronRunAsNode) { + // macOS Electron helper needs ELECTRON_RUN_AS_NODE=1; + qwenCmd = `ELECTRON_RUN_AS_NODE=1 ${baseCmd}`; } else { qwenCmd = baseCmd; } From df75aa06b6cb5ba4a29b7bfb207daa4831730b26 Mon Sep 17 00:00:00 2001 From: yiliang114 <1204183885@qq.com> Date: Sat, 10 Jan 2026 22:08:14 +0800 Subject: [PATCH 5/7] fix(vscode-ide-companion): window qwen code run command --- .../vscode-ide-companion/src/extension.ts | 39 +++++++++++-------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/packages/vscode-ide-companion/src/extension.ts b/packages/vscode-ide-companion/src/extension.ts index b000b00c3..97b26bf7d 100644 --- a/packages/vscode-ide-companion/src/extension.ts +++ b/packages/vscode-ide-companion/src/extension.ts @@ -319,17 +319,26 @@ export async function activate(context: vscode.ExtensionContext) { lowerExecPath.includes('code') || lowerExecPath.includes('electron'); - let qwenCmd: string; + let qwenCmd: string | undefined; + const terminalOptions: vscode.TerminalOptions = { + name: `Qwen Code (${selectedFolder.name})`, + cwd: selectedFolder.uri.fsPath, + location, + }; + if (isWindows) { - // Wrap with PowerShell to avoid quoting issues in different Windows shells - const quotePwsh = (s: string) => `'${s.replace(/'/g, "''")}'`; - const psParts = [ - needsElectronRunAsNode ? '$Env:ELECTRON_RUN_AS_NODE=1;' : '', - `& ${quotePwsh(execPath)}`, - needsElectronRunAsNode ? '--ms-enable-electron-run-as-node' : '', - quotePwsh(cliEntry), - ].filter(Boolean); - qwenCmd = `powershell -NoLogo -NoProfile -Command "& { ${psParts.join(' ')} }"`; + // Use cmd.exe to avoid PowerShell parsing issues with quoted paths + const quoteCmd = (s: string) => `"${s.replace(/"/g, '""')}"`; + const execQuoted = quoteCmd(execPath); + const cliQuoted = quoteCmd(cliEntry); + + terminalOptions.shellPath = + process.env.ComSpec || 'C:\\\\Windows\\\\System32\\\\cmd.exe'; + const cmdLine = needsElectronRunAsNode + ? `set "ELECTRON_RUN_AS_NODE=1" && ${execQuoted} ${cliQuoted}` + : `${execQuoted} ${cliQuoted}`; + terminalOptions.shellArgs = ['/d', '/s', '/c', cmdLine]; + qwenCmd = undefined; } else { const quotePosix = (s: string) => `"${s.replace(/"/g, '\\"')}"`; const baseCmd = `${quotePosix(execPath)} ${quotePosix(cliEntry)}`; @@ -341,13 +350,11 @@ export async function activate(context: vscode.ExtensionContext) { } } - const terminal = vscode.window.createTerminal({ - name: `Qwen Code (${selectedFolder.name})`, - cwd: selectedFolder.uri.fsPath, - location, - }); + const terminal = vscode.window.createTerminal(terminalOptions); terminal.show(); - terminal.sendText(qwenCmd); + if (qwenCmd) { + terminal.sendText(qwenCmd); + } } }, ), From 82c524f87d3dbe693cc8d2267c7e69b9ea45b183 Mon Sep 17 00:00:00 2001 From: yiliang114 <1204183885@qq.com> Date: Sat, 10 Jan 2026 22:30:10 +0800 Subject: [PATCH 6/7] fix(vscode-ide-companion): window qwen code run command --- .../vscode-ide-companion/src/extension.ts | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/packages/vscode-ide-companion/src/extension.ts b/packages/vscode-ide-companion/src/extension.ts index 97b26bf7d..4668969d6 100644 --- a/packages/vscode-ide-companion/src/extension.ts +++ b/packages/vscode-ide-companion/src/extension.ts @@ -319,7 +319,7 @@ export async function activate(context: vscode.ExtensionContext) { lowerExecPath.includes('code') || lowerExecPath.includes('electron'); - let qwenCmd: string | undefined; + let qwenCmd: string; const terminalOptions: vscode.TerminalOptions = { name: `Qwen Code (${selectedFolder.name})`, cwd: selectedFolder.uri.fsPath, @@ -327,18 +327,11 @@ export async function activate(context: vscode.ExtensionContext) { }; if (isWindows) { - // Use cmd.exe to avoid PowerShell parsing issues with quoted paths + // Use system Node via cmd.exe; avoid PowerShell parsing issues const quoteCmd = (s: string) => `"${s.replace(/"/g, '""')}"`; - const execQuoted = quoteCmd(execPath); const cliQuoted = quoteCmd(cliEntry); - - terminalOptions.shellPath = - process.env.ComSpec || 'C:\\\\Windows\\\\System32\\\\cmd.exe'; - const cmdLine = needsElectronRunAsNode - ? `set "ELECTRON_RUN_AS_NODE=1" && ${execQuoted} ${cliQuoted}` - : `${execQuoted} ${cliQuoted}`; - terminalOptions.shellArgs = ['/d', '/s', '/c', cmdLine]; - qwenCmd = undefined; + qwenCmd = `node ${cliQuoted}`; + terminalOptions.shellPath = process.env.ComSpec; } else { const quotePosix = (s: string) => `"${s.replace(/"/g, '\\"')}"`; const baseCmd = `${quotePosix(execPath)} ${quotePosix(cliEntry)}`; @@ -352,9 +345,7 @@ export async function activate(context: vscode.ExtensionContext) { const terminal = vscode.window.createTerminal(terminalOptions); terminal.show(); - if (qwenCmd) { - terminal.sendText(qwenCmd); - } + terminal.sendText(qwenCmd); } }, ), From 8111511a89031c4792527ce2f377d7e1b82e02b2 Mon Sep 17 00:00:00 2001 From: yiliang114 <1204183885@qq.com> Date: Tue, 13 Jan 2026 10:19:43 +0800 Subject: [PATCH 7/7] chore(vscode-ide-companion): add comments under window --- packages/vscode-ide-companion/src/extension.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/vscode-ide-companion/src/extension.ts b/packages/vscode-ide-companion/src/extension.ts index 4668969d6..be0f669e6 100644 --- a/packages/vscode-ide-companion/src/extension.ts +++ b/packages/vscode-ide-companion/src/extension.ts @@ -330,6 +330,7 @@ export async function activate(context: vscode.ExtensionContext) { // Use system Node via cmd.exe; avoid PowerShell parsing issues const quoteCmd = (s: string) => `"${s.replace(/"/g, '""')}"`; const cliQuoted = quoteCmd(cliEntry); + // TODO: @yiliang114, temporarily run through node, and later hope to decouple from the local node qwenCmd = `node ${cliQuoted}`; terminalOptions.shellPath = process.env.ComSpec; } else {