diff --git a/.github/workflows/release-sdk.yml b/.github/workflows/release-sdk.yml
index 69192520..c5e30ee9 100644
--- a/.github/workflows/release-sdk.yml
+++ b/.github/workflows/release-sdk.yml
@@ -121,6 +121,11 @@ jobs:
IS_PREVIEW: '${{ steps.vars.outputs.is_preview }}'
MANUAL_VERSION: '${{ inputs.version }}'
+ - name: 'Build CLI Bundle'
+ run: |
+ npm run build
+ npm run bundle
+
- name: 'Run Tests'
if: |-
${{ github.event.inputs.force_skip_tests != 'true' }}
@@ -132,13 +137,6 @@ jobs:
OPENAI_BASE_URL: '${{ secrets.OPENAI_BASE_URL }}'
OPENAI_MODEL: '${{ secrets.OPENAI_MODEL }}'
- - name: 'Build CLI for Integration Tests'
- if: |-
- ${{ github.event.inputs.force_skip_tests != 'true' }}
- run: |
- npm run build
- npm run bundle
-
- name: 'Run SDK Integration Tests'
if: |-
${{ github.event.inputs.force_skip_tests != 'true' }}
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 0c4ff85a..ffcda3dc 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -133,8 +133,8 @@ jobs:
${{ github.event.inputs.force_skip_tests != 'true' }}
run: |
npm run preflight
- npm run test:integration:sandbox:none
- npm run test:integration:sandbox:docker
+ npm run test:integration:cli:sandbox:none
+ npm run test:integration:cli:sandbox:docker
env:
OPENAI_API_KEY: '${{ secrets.OPENAI_API_KEY }}'
OPENAI_BASE_URL: '${{ secrets.OPENAI_BASE_URL }}'
diff --git a/docs/index.md b/docs/index.md
index 73a33775..ff8a4803 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -5,6 +5,7 @@ Welcome to the Qwen Code documentation. Qwen Code is an agentic coding tool that
## Documentation Sections
### [User Guide](./users/overview)
+
Learn how to use Qwen Code as an end user. This section covers:
- Basic installation and setup
diff --git a/docs/users/configuration/settings.md b/docs/users/configuration/settings.md
index ba3ea3a2..658e4835 100644
--- a/docs/users/configuration/settings.md
+++ b/docs/users/configuration/settings.md
@@ -69,7 +69,7 @@ Settings are organized into categories. All settings should be placed within the
| Setting | Type | Description | Default |
| ---------------------------------------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- |
-| `ui.theme` | string | The color theme for the UI. See [Themes](../configuration/themes) for available options. | `undefined` |
+| `ui.theme` | string | The color theme for the UI. See [Themes](../configuration/themes) for available options. | `undefined` |
| `ui.customThemes` | object | Custom theme definitions. | `{}` |
| `ui.hideWindowTitle` | boolean | Hide the window title bar. | `false` |
| `ui.hideTips` | boolean | Hide helpful tips in the UI. | `false` |
@@ -357,38 +357,38 @@ Arguments passed directly when running the CLI can override other configurations
### Command-Line Arguments Table
-| Argument | Alias | Description | Possible Values | Notes |
-| ---------------------------- | ----- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| `--model` | `-m` | Specifies the Qwen model to use for this session. | Model name | Example: `npm start -- --model qwen3-coder-plus` |
-| `--prompt` | `-p` | Used to pass a prompt directly to the command. This invokes Qwen Code in a non-interactive mode. | Your prompt text | For scripting examples, use the `--output-format json` flag to get structured output. |
-| `--prompt-interactive` | `-i` | Starts an interactive session with the provided prompt as the initial input. | Your prompt text | The prompt is processed within the interactive session, not before it. Cannot be used when piping input from stdin. Example: `qwen -i "explain this code"` |
+| Argument | Alias | Description | Possible Values | Notes |
+| ---------------------------- | ----- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `--model` | `-m` | Specifies the Qwen model to use for this session. | Model name | Example: `npm start -- --model qwen3-coder-plus` |
+| `--prompt` | `-p` | Used to pass a prompt directly to the command. This invokes Qwen Code in a non-interactive mode. | Your prompt text | For scripting examples, use the `--output-format json` flag to get structured output. |
+| `--prompt-interactive` | `-i` | Starts an interactive session with the provided prompt as the initial input. | Your prompt text | The prompt is processed within the interactive session, not before it. Cannot be used when piping input from stdin. Example: `qwen -i "explain this code"` |
| `--output-format` | `-o` | Specifies the format of the CLI output for non-interactive mode. | `text`, `json`, `stream-json` | `text`: (Default) The standard human-readable output. `json`: A machine-readable JSON output emitted at the end of execution. `stream-json`: Streaming JSON messages emitted as they occur during execution. For structured output and scripting, use the `--output-format json` or `--output-format stream-json` flag. See [Headless Mode](../features/headless) for detailed information. |
| `--input-format` | | Specifies the format consumed from standard input. | `text`, `stream-json` | `text`: (Default) Standard text input from stdin or command-line arguments. `stream-json`: JSON message protocol via stdin for bidirectional communication. Requirement: `--input-format stream-json` requires `--output-format stream-json` to be set. When using `stream-json`, stdin is reserved for protocol messages. See [Headless Mode](../features/headless) for detailed information. |
| `--include-partial-messages` | | Include partial assistant messages when using `stream-json` output format. When enabled, emits stream events (message_start, content_block_delta, etc.) as they occur during streaming. | | Default: `false`. Requirement: Requires `--output-format stream-json` to be set. See [Headless Mode](../features/headless) for detailed information about stream events. |
-| `--sandbox` | `-s` | Enables sandbox mode for this session. | | |
-| `--sandbox-image` | | Sets the sandbox image URI. | | |
-| `--debug` | `-d` | Enables debug mode for this session, providing more verbose output. | | |
-| `--all-files` | `-a` | If set, recursively includes all files within the current directory as context for the prompt. | | |
-| `--help` | `-h` | Displays help information about command-line arguments. | | |
-| `--show-memory-usage` | | Displays the current memory usage. | | |
-| `--yolo` | | Enables YOLO mode, which automatically approves all tool calls. | | |
+| `--sandbox` | `-s` | Enables sandbox mode for this session. | | |
+| `--sandbox-image` | | Sets the sandbox image URI. | | |
+| `--debug` | `-d` | Enables debug mode for this session, providing more verbose output. | | |
+| `--all-files` | `-a` | If set, recursively includes all files within the current directory as context for the prompt. | | |
+| `--help` | `-h` | Displays help information about command-line arguments. | | |
+| `--show-memory-usage` | | Displays the current memory usage. | | |
+| `--yolo` | | Enables YOLO mode, which automatically approves all tool calls. | | |
| `--approval-mode` | | Sets the approval mode for tool calls. | `plan`, `default`, `auto-edit`, `yolo` | Supported modes: `plan`: Analyze only—do not modify files or execute commands. `default`: Require approval for file edits or shell commands (default behavior). `auto-edit`: Automatically approve edit tools (edit, write_file) while prompting for others. `yolo`: Automatically approve all tool calls (equivalent to `--yolo`). Cannot be used together with `--yolo`. Use `--approval-mode=yolo` instead of `--yolo` for the new unified approach. Example: `qwen --approval-mode auto-edit`
See more about [Approval Mode](../features/approval-mode). |
-| `--allowed-tools` | | A comma-separated list of tool names that will bypass the confirmation dialog. | Tool names | Example: `qwen --allowed-tools "Shell(git status)"` |
-| `--telemetry` | | Enables [telemetry](/developers/development/telemetry). | | |
-| `--telemetry-target` | | Sets the telemetry target. | | See [telemetry](/developers/development/telemetry) for more information. |
-| `--telemetry-otlp-endpoint` | | Sets the OTLP endpoint for telemetry. | | See [telemetry](../../developers/development/telemetry) for more information. |
-| `--telemetry-otlp-protocol` | | Sets the OTLP protocol for telemetry (`grpc` or `http`). | | Defaults to `grpc`. See [telemetry](../../developers/development/telemetry) for more information. |
-| `--telemetry-log-prompts` | | Enables logging of prompts for telemetry. | | See [telemetry](../../developers/development/telemetry) for more information. |
-| `--checkpointing` | | Enables [checkpointing](../features/checkpointing). | | |
-| `--extensions` | `-e` | Specifies a list of extensions to use for the session. | Extension names | If not provided, all available extensions are used. Use the special term `qwen -e none` to disable all extensions. Example: `qwen -e my-extension -e my-other-extension` |
-| `--list-extensions` | `-l` | Lists all available extensions and exits. | | |
-| `--proxy` | | Sets the proxy for the CLI. | Proxy URL | Example: `--proxy http://localhost:7890`. |
-| `--include-directories` | | Includes additional directories in the workspace for multi-directory support. | Directory paths | Can be specified multiple times or as comma-separated values. 5 directories can be added at maximum. Example: `--include-directories /path/to/project1,/path/to/project2` or `--include-directories /path/to/project1 --include-directories /path/to/project2` |
-| `--screen-reader` | | Enables screen reader mode, which adjusts the TUI for better compatibility with screen readers. | | |
-| `--version` | | Displays the version of the CLI. | | |
-| `--openai-logging` | | Enables logging of OpenAI API calls for debugging and analysis. | | This flag overrides the `enableOpenAILogging` setting in `settings.json`. |
-| `--openai-logging-dir` | | Sets a custom directory path for OpenAI API logs. | Directory path | This flag overrides the `openAILoggingDir` setting in `settings.json`. Supports absolute paths, relative paths, and `~` expansion. Example: `qwen --openai-logging-dir "~/qwen-logs" --openai-logging` |
-| `--tavily-api-key` | | Sets the Tavily API key for web search functionality for this session. | API key | Example: `qwen --tavily-api-key tvly-your-api-key-here` |
+| `--allowed-tools` | | A comma-separated list of tool names that will bypass the confirmation dialog. | Tool names | Example: `qwen --allowed-tools "Shell(git status)"` |
+| `--telemetry` | | Enables [telemetry](/developers/development/telemetry). | | |
+| `--telemetry-target` | | Sets the telemetry target. | | See [telemetry](/developers/development/telemetry) for more information. |
+| `--telemetry-otlp-endpoint` | | Sets the OTLP endpoint for telemetry. | | See [telemetry](../../developers/development/telemetry) for more information. |
+| `--telemetry-otlp-protocol` | | Sets the OTLP protocol for telemetry (`grpc` or `http`). | | Defaults to `grpc`. See [telemetry](../../developers/development/telemetry) for more information. |
+| `--telemetry-log-prompts` | | Enables logging of prompts for telemetry. | | See [telemetry](../../developers/development/telemetry) for more information. |
+| `--checkpointing` | | Enables [checkpointing](../features/checkpointing). | | |
+| `--extensions` | `-e` | Specifies a list of extensions to use for the session. | Extension names | If not provided, all available extensions are used. Use the special term `qwen -e none` to disable all extensions. Example: `qwen -e my-extension -e my-other-extension` |
+| `--list-extensions` | `-l` | Lists all available extensions and exits. | | |
+| `--proxy` | | Sets the proxy for the CLI. | Proxy URL | Example: `--proxy http://localhost:7890`. |
+| `--include-directories` | | Includes additional directories in the workspace for multi-directory support. | Directory paths | Can be specified multiple times or as comma-separated values. 5 directories can be added at maximum. Example: `--include-directories /path/to/project1,/path/to/project2` or `--include-directories /path/to/project1 --include-directories /path/to/project2` |
+| `--screen-reader` | | Enables screen reader mode, which adjusts the TUI for better compatibility with screen readers. | | |
+| `--version` | | Displays the version of the CLI. | | |
+| `--openai-logging` | | Enables logging of OpenAI API calls for debugging and analysis. | | This flag overrides the `enableOpenAILogging` setting in `settings.json`. |
+| `--openai-logging-dir` | | Sets a custom directory path for OpenAI API logs. | Directory path | This flag overrides the `openAILoggingDir` setting in `settings.json`. Supports absolute paths, relative paths, and `~` expansion. Example: `qwen --openai-logging-dir "~/qwen-logs" --openai-logging` |
+| `--tavily-api-key` | | Sets the Tavily API key for web search functionality for this session. | API key | Example: `qwen --tavily-api-key tvly-your-api-key-here` |
## Context Files (Hierarchical Instructional Context)
diff --git a/packages/sdk-typescript/README.md b/packages/sdk-typescript/README.md
index bc3ef6aa..a9699b02 100644
--- a/packages/sdk-typescript/README.md
+++ b/packages/sdk-typescript/README.md
@@ -13,9 +13,8 @@ npm install @qwen-code/sdk
## Requirements
- Node.js >= 20.0.0
-- [Qwen Code](https://github.com/QwenLM/qwen-code) >= 0.4.0 (stable) installed and accessible in PATH
-> **Note for nvm users**: If you use nvm to manage Node.js versions, the SDK may not be able to auto-detect the Qwen Code executable. You should explicitly set the `pathToQwenExecutable` option to the full path of the `qwen` binary.
+> From v0.1.1, the CLI is bundled with the SDK. So no standalone CLI installation is needed.
## Quick Start
@@ -372,6 +371,23 @@ try {
}
```
+## FAQ / Troubleshooting
+
+### Version 0.1.0 Requirements
+
+If you're using SDK version **0.1.0**, please note the following requirements:
+
+#### Qwen Code Installation Required
+
+Version 0.1.0 requires [Qwen Code](https://github.com/QwenLM/qwen-code) **>= 0.4.0** to be installed separately and accessible in your PATH.
+
+```bash
+# Install Qwen Code globally
+npm install -g qwen-code@^0.4.0
+```
+
+**Note**: From version **0.1.1** onwards, the CLI is bundled with the SDK, so no separate Qwen Code installation is needed.
+
## License
Apache-2.0 - see [LICENSE](./LICENSE) for details.
diff --git a/packages/sdk-typescript/package.json b/packages/sdk-typescript/package.json
index b071b8a3..f80c61c4 100644
--- a/packages/sdk-typescript/package.json
+++ b/packages/sdk-typescript/package.json
@@ -1,6 +1,6 @@
{
"name": "@qwen-code/sdk",
- "version": "0.5.1",
+ "version": "0.1.1",
"description": "TypeScript SDK for programmatic access to qwen-code CLI",
"main": "./dist/index.cjs",
"module": "./dist/index.mjs",
@@ -45,7 +45,8 @@
"node": ">=18.0.0"
},
"dependencies": {
- "@modelcontextprotocol/sdk": "^1.0.4"
+ "@modelcontextprotocol/sdk": "^1.0.4",
+ "tiktoken": "^1.0.21"
},
"devDependencies": {
"@types/node": "^20.14.0",
diff --git a/packages/sdk-typescript/scripts/build.js b/packages/sdk-typescript/scripts/build.js
index beda8b0e..ae3a21e8 100755
--- a/packages/sdk-typescript/scripts/build.js
+++ b/packages/sdk-typescript/scripts/build.js
@@ -91,3 +91,35 @@ if (existsSync(licenseSource)) {
console.warn('Could not copy LICENSE:', error.message);
}
}
+
+console.log('Bundling CLI into SDK package...');
+const repoRoot = join(rootDir, '..', '..');
+const rootDistDir = join(repoRoot, 'dist');
+
+if (!existsSync(rootDistDir) || !existsSync(join(rootDistDir, 'cli.js'))) {
+ console.log('Building CLI bundle...');
+ try {
+ execSync('npm run bundle', { stdio: 'inherit', cwd: repoRoot });
+ } catch (error) {
+ console.error('Failed to build CLI bundle:', error.message);
+ throw error;
+ }
+}
+
+const cliDistDir = join(rootDir, 'dist', 'cli');
+mkdirSync(cliDistDir, { recursive: true });
+
+console.log('Copying CLI bundle...');
+cpSync(join(rootDistDir, 'cli.js'), join(cliDistDir, 'cli.js'));
+
+const vendorSource = join(rootDistDir, 'vendor');
+if (existsSync(vendorSource)) {
+ cpSync(vendorSource, join(cliDistDir, 'vendor'), { recursive: true });
+}
+
+const localesSource = join(rootDistDir, 'locales');
+if (existsSync(localesSource)) {
+ cpSync(localesSource, join(cliDistDir, 'locales'), { recursive: true });
+}
+
+console.log('CLI bundle copied successfully to SDK package');
diff --git a/packages/sdk-typescript/src/utils/cliPath.ts b/packages/sdk-typescript/src/utils/cliPath.ts
index 2d919413..4f031963 100644
--- a/packages/sdk-typescript/src/utils/cliPath.ts
+++ b/packages/sdk-typescript/src/utils/cliPath.ts
@@ -2,24 +2,16 @@
* CLI path auto-detection and subprocess spawning utilities
*
* Supports multiple execution modes:
- * 1. Native binary: 'qwen' (production)
- * 2. Node.js bundle: 'node /path/to/cli.js' (production validation)
+ * 1. Bundled CLI: Node.js bundle included in the SDK package (default)
+ * 2. Node.js bundle: 'node /path/to/cli.js' (custom path)
* 3. Bun bundle: 'bun /path/to/cli.js' (alternative runtime)
* 4. TypeScript source: 'tsx /path/to/index.ts' (development)
- *
- * Auto-detection locations for native binary:
- * 1. QWEN_CODE_CLI_PATH environment variable
- * 2. ~/.volta/bin/qwen
- * 3. ~/.npm-global/bin/qwen
- * 4. /usr/local/bin/qwen
- * 5. ~/.local/bin/qwen
- * 6. ~/node_modules/.bin/qwen
- * 7. ~/.yarn/bin/qwen
*/
import * as fs from 'node:fs';
import * as path from 'node:path';
import { execSync } from 'node:child_process';
+import { fileURLToPath } from 'node:url';
/**
* Executable types supported by the SDK
@@ -40,49 +32,38 @@ export type SpawnInfo = {
originalInput: string;
};
-export function findNativeCliPath(): string {
- const homeDir = process.env['HOME'] || process.env['USERPROFILE'] || '';
+function getBundledCliPath(): string | null {
+ try {
+ const currentFile =
+ typeof __filename !== 'undefined'
+ ? __filename
+ : fileURLToPath(import.meta.url);
- const candidates: Array = [
- // 1. Environment variable (highest priority)
- process.env['QWEN_CODE_CLI_PATH'],
+ const currentDir = path.dirname(currentFile);
- // 2. Volta bin
- path.join(homeDir, '.volta', 'bin', 'qwen'),
+ const bundledCliPath = path.join(currentDir, 'cli', 'cli.js');
- // 3. Global npm installations
- path.join(homeDir, '.npm-global', 'bin', 'qwen'),
-
- // 4. Common Unix binary locations
- '/usr/local/bin/qwen',
-
- // 5. User local bin
- path.join(homeDir, '.local', 'bin', 'qwen'),
-
- // 6. Node modules bin in home directory
- path.join(homeDir, 'node_modules', '.bin', 'qwen'),
-
- // 7. Yarn global bin
- path.join(homeDir, '.yarn', 'bin', 'qwen'),
- ];
-
- // Find first existing candidate
- for (const candidate of candidates) {
- if (candidate && fs.existsSync(candidate)) {
- return path.resolve(candidate);
+ if (fs.existsSync(bundledCliPath)) {
+ return bundledCliPath;
}
+
+ return null;
+ } catch {
+ return null;
+ }
+}
+
+export function findNativeCliPath(): string {
+ const bundledCli = getBundledCliPath();
+ if (bundledCli) {
+ return bundledCli;
}
- // Not found - throw helpful error
throw new Error(
- 'qwen CLI not found. Please:\n' +
- ' 1. Install qwen globally: npm install -g qwen\n' +
- ' 2. Or provide explicit executable: query({ pathToQwenExecutable: "/path/to/qwen" })\n' +
- ' 3. Or set environment variable: QWEN_CODE_CLI_PATH="/path/to/qwen"\n' +
- '\n' +
- 'For development/testing, you can also use:\n' +
+ 'Bundled qwen CLI not found. The CLI should be included in the SDK package.\n' +
+ 'If you need to use a custom CLI, provide explicit executable:\n' +
+ ' • query({ pathToQwenExecutable: "/path/to/cli.js" })\n' +
' • TypeScript source: query({ pathToQwenExecutable: "/path/to/index.ts" })\n' +
- ' • Node.js bundle: query({ pathToQwenExecutable: "/path/to/cli.js" })\n' +
' • Force specific runtime: query({ pathToQwenExecutable: "bun:/path/to/cli.js" })',
);
}
diff --git a/packages/sdk-typescript/test/unit/cliPath.test.ts b/packages/sdk-typescript/test/unit/cliPath.test.ts
index 43f50dec..c4253175 100644
--- a/packages/sdk-typescript/test/unit/cliPath.test.ts
+++ b/packages/sdk-typescript/test/unit/cliPath.test.ts
@@ -38,6 +38,8 @@ describe('CLI Path Utilities', () => {
mockFs.statSync.mockReturnValue({
isFile: () => true,
} as ReturnType);
+ // Default: return true for existsSync (can be overridden in specific tests)
+ mockFs.existsSync.mockReturnValue(true);
});
afterEach(() => {
@@ -50,28 +52,26 @@ describe('CLI Path Utilities', () => {
describe('parseExecutableSpec', () => {
describe('auto-detection (no spec provided)', () => {
- it('should auto-detect native CLI when no spec provided', () => {
- // Mock environment variable
- const originalEnv = process.env['QWEN_CODE_CLI_PATH'];
- process.env['QWEN_CODE_CLI_PATH'] = '/usr/local/bin/qwen';
- mockFs.existsSync.mockReturnValue(true);
+ it('should auto-detect bundled CLI when no spec provided', () => {
+ // Mock existsSync to return true for bundled CLI
+ mockFs.existsSync.mockImplementation((p) => {
+ const pathStr = p.toString();
+ return (
+ pathStr.includes('cli/cli.js') || pathStr.includes('cli\\cli.js')
+ );
+ });
const result = parseExecutableSpec();
- expect(result).toEqual({
- executablePath: path.resolve('/usr/local/bin/qwen'),
- isExplicitRuntime: false,
- });
-
- // Restore env
- process.env['QWEN_CODE_CLI_PATH'] = originalEnv;
+ expect(result.executablePath).toContain('cli.js');
+ expect(result.isExplicitRuntime).toBe(false);
});
- it('should throw when auto-detection fails', () => {
+ it('should throw when bundled CLI not found', () => {
mockFs.existsSync.mockReturnValue(false);
expect(() => parseExecutableSpec()).toThrow(
- 'qwen CLI not found. Please:',
+ 'Bundled qwen CLI not found',
);
});
});
@@ -361,65 +361,44 @@ describe('CLI Path Utilities', () => {
});
describe('auto-detection fallback', () => {
- it('should auto-detect when no spec provided', () => {
- // Mock environment variable
- const originalEnv = process.env['QWEN_CODE_CLI_PATH'];
- process.env['QWEN_CODE_CLI_PATH'] = '/usr/local/bin/qwen';
+ it('should auto-detect bundled CLI when no spec provided', () => {
+ // Mock existsSync to return true for bundled CLI
+ mockFs.existsSync.mockImplementation((p) => {
+ const pathStr = p.toString();
+ return (
+ pathStr.includes('cli/cli.js') || pathStr.includes('cli\\cli.js')
+ );
+ });
const result = prepareSpawnInfo();
- expect(result).toEqual({
- command: path.resolve('/usr/local/bin/qwen'),
- args: [],
- type: 'native',
- originalInput: '',
- });
-
- // Restore env
- process.env['QWEN_CODE_CLI_PATH'] = originalEnv;
+ expect(result.command).toBe(process.execPath);
+ expect(result.args[0]).toContain('cli.js');
+ expect(result.type).toBe('node');
+ expect(result.originalInput).toBe('');
});
});
});
describe('findNativeCliPath', () => {
- it('should find CLI from environment variable', () => {
- const originalEnv = process.env['QWEN_CODE_CLI_PATH'];
- process.env['QWEN_CODE_CLI_PATH'] = '/custom/path/to/qwen';
- mockFs.existsSync.mockReturnValue(true);
-
- const result = findNativeCliPath();
-
- expect(result).toBe(path.resolve('/custom/path/to/qwen'));
-
- process.env['QWEN_CODE_CLI_PATH'] = originalEnv;
- });
-
- it('should search common installation locations', () => {
- const originalEnv = process.env['QWEN_CODE_CLI_PATH'];
- delete process.env['QWEN_CODE_CLI_PATH'];
-
- // Mock fs.existsSync to return true for volta bin
- // Use path.join to match platform-specific path separators
- const voltaBinPath = path.join('.volta', 'bin', 'qwen');
+ it('should find bundled CLI', () => {
+ // Mock existsSync to return true for bundled CLI
mockFs.existsSync.mockImplementation((p) => {
- return p.toString().includes(voltaBinPath);
+ const pathStr = p.toString();
+ return (
+ pathStr.includes('cli/cli.js') || pathStr.includes('cli\\cli.js')
+ );
});
const result = findNativeCliPath();
- expect(result).toContain(voltaBinPath);
-
- process.env['QWEN_CODE_CLI_PATH'] = originalEnv;
+ expect(result).toContain('cli.js');
});
- it('should throw descriptive error when CLI not found', () => {
- const originalEnv = process.env['QWEN_CODE_CLI_PATH'];
- delete process.env['QWEN_CODE_CLI_PATH'];
+ it('should throw descriptive error when bundled CLI not found', () => {
mockFs.existsSync.mockReturnValue(false);
- expect(() => findNativeCliPath()).toThrow('qwen CLI not found. Please:');
-
- process.env['QWEN_CODE_CLI_PATH'] = originalEnv;
+ expect(() => findNativeCliPath()).toThrow('Bundled qwen CLI not found');
});
});
@@ -634,13 +613,10 @@ describe('CLI Path Utilities', () => {
mockFs.existsSync.mockReturnValue(false);
expect(() => parseExecutableSpec('/missing/file')).toThrow(
- 'Set QWEN_CODE_CLI_PATH environment variable',
+ 'Executable file not found at',
);
expect(() => parseExecutableSpec('/missing/file')).toThrow(
- 'Install qwen globally: npm install -g qwen',
- );
- expect(() => parseExecutableSpec('/missing/file')).toThrow(
- 'Force specific runtime: bun:/path/to/cli.js or tsx:/path/to/index.ts',
+ 'Please check the file path and ensure the file exists',
);
});
});
diff --git a/packages/vscode-ide-companion/src/webview/components/messages/Assistant/AssistantMessage.css b/packages/vscode-ide-companion/src/webview/components/messages/Assistant/AssistantMessage.css
index 56946662..67675816 100644
--- a/packages/vscode-ide-companion/src/webview/components/messages/Assistant/AssistantMessage.css
+++ b/packages/vscode-ide-companion/src/webview/components/messages/Assistant/AssistantMessage.css
@@ -48,5 +48,5 @@
}
.assistant-message-container.assistant-message-loading::after {
- display: none
+ display: none;
}
diff --git a/packages/vscode-ide-companion/src/webview/components/messages/toolcalls/shared/LayoutComponents.css b/packages/vscode-ide-companion/src/webview/components/messages/toolcalls/shared/LayoutComponents.css
index 39846d77..e5b2cce9 100644
--- a/packages/vscode-ide-companion/src/webview/components/messages/toolcalls/shared/LayoutComponents.css
+++ b/packages/vscode-ide-companion/src/webview/components/messages/toolcalls/shared/LayoutComponents.css
@@ -172,7 +172,8 @@
/* Loading animation for toolcall header */
@keyframes toolcallHeaderPulse {
- 0%, 100% {
+ 0%,
+ 100% {
opacity: 1;
}
50% {
diff --git a/packages/vscode-ide-companion/src/webview/styles/tailwind.css b/packages/vscode-ide-companion/src/webview/styles/tailwind.css
index a48c172f..5c9955b3 100644
--- a/packages/vscode-ide-companion/src/webview/styles/tailwind.css
+++ b/packages/vscode-ide-companion/src/webview/styles/tailwind.css
@@ -51,7 +51,8 @@
.composer-form:focus-within {
/* match existing highlight behavior */
border-color: var(--app-input-highlight);
- box-shadow: 0 1px 2px color-mix(in srgb, var(--app-input-highlight), transparent 80%);
+ box-shadow: 0 1px 2px
+ color-mix(in srgb, var(--app-input-highlight), transparent 80%);
}
/* Composer: input editable area */
@@ -66,7 +67,7 @@
The data attribute is needed because some browsers insert a
in
contentEditable, which breaks :empty matching. */
.composer-input:empty:before,
- .composer-input[data-empty="true"]::before {
+ .composer-input[data-empty='true']::before {
content: attr(data-placeholder);
color: var(--app-input-placeholder-foreground);
pointer-events: none;
@@ -80,7 +81,7 @@
outline: none;
}
.composer-input:disabled,
- .composer-input[contenteditable="false"] {
+ .composer-input[contenteditable='false'] {
color: #999;
cursor: not-allowed;
}