@xagent-ai/cli 1.3.0 → 1.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (190) hide show
  1. package/.github/release.yml +76 -0
  2. package/.github/workflows/ci.yml +3 -0
  3. package/.github/workflows/release.yml +11 -17
  4. package/README.md +2 -2
  5. package/README_CN.md +2 -2
  6. package/dist/agents.d.ts.map +1 -1
  7. package/dist/agents.js +7 -3
  8. package/dist/agents.js.map +1 -1
  9. package/dist/ai-client/factory.d.ts +0 -12
  10. package/dist/ai-client/factory.d.ts.map +1 -1
  11. package/dist/ai-client/factory.js +0 -32
  12. package/dist/ai-client/factory.js.map +1 -1
  13. package/dist/ai-client/index.js +1 -1
  14. package/dist/ai-client/index.js.map +1 -1
  15. package/dist/ai-client/providers/anthropic.d.ts.map +1 -1
  16. package/dist/ai-client/providers/anthropic.js +10 -4
  17. package/dist/ai-client/providers/anthropic.js.map +1 -1
  18. package/dist/ai-client/providers/openai.d.ts.map +1 -1
  19. package/dist/ai-client/providers/openai.js +8 -4
  20. package/dist/ai-client/providers/openai.js.map +1 -1
  21. package/dist/ai-client/providers/remote.d.ts +0 -1
  22. package/dist/ai-client/providers/remote.d.ts.map +1 -1
  23. package/dist/ai-client/providers/remote.js +11 -10
  24. package/dist/ai-client/providers/remote.js.map +1 -1
  25. package/dist/ai-client/types.d.ts +14 -0
  26. package/dist/ai-client/types.d.ts.map +1 -1
  27. package/dist/ai-client/types.js +17 -0
  28. package/dist/ai-client/types.js.map +1 -1
  29. package/dist/ai-client-factory.d.ts.map +1 -1
  30. package/dist/ai-client-factory.js +4 -4
  31. package/dist/ai-client-factory.js.map +1 -1
  32. package/dist/auth.d.ts.map +1 -1
  33. package/dist/auth.js +10 -12
  34. package/dist/auth.js.map +1 -1
  35. package/dist/cancellation.d.ts.map +1 -1
  36. package/dist/cancellation.js +3 -5
  37. package/dist/cancellation.js.map +1 -1
  38. package/dist/checkpoint.d.ts +1 -0
  39. package/dist/checkpoint.d.ts.map +1 -1
  40. package/dist/checkpoint.js +38 -4
  41. package/dist/checkpoint.js.map +1 -1
  42. package/dist/cli.js +132 -32
  43. package/dist/cli.js.map +1 -1
  44. package/dist/config.js +1 -1
  45. package/dist/config.js.map +1 -1
  46. package/dist/context-compressor.d.ts +1 -2
  47. package/dist/context-compressor.d.ts.map +1 -1
  48. package/dist/context-compressor.js +22 -17
  49. package/dist/context-compressor.js.map +1 -1
  50. package/dist/conversation.d.ts +1 -1
  51. package/dist/conversation.d.ts.map +1 -1
  52. package/dist/conversation.js +8 -7
  53. package/dist/conversation.js.map +1 -1
  54. package/dist/gui-subagent/action-parser/actionParser.js +2 -2
  55. package/dist/gui-subagent/action-parser/actionParser.js.map +1 -1
  56. package/dist/gui-subagent/agent/gui-agent.d.ts +10 -0
  57. package/dist/gui-subagent/agent/gui-agent.d.ts.map +1 -1
  58. package/dist/gui-subagent/agent/gui-agent.js +105 -32
  59. package/dist/gui-subagent/agent/gui-agent.js.map +1 -1
  60. package/dist/gui-subagent/index.d.ts +7 -0
  61. package/dist/gui-subagent/index.d.ts.map +1 -1
  62. package/dist/gui-subagent/index.js +2 -0
  63. package/dist/gui-subagent/index.js.map +1 -1
  64. package/dist/gui-subagent/operator/computer-operator.d.ts.map +1 -1
  65. package/dist/gui-subagent/operator/computer-operator.js +2 -0
  66. package/dist/gui-subagent/operator/computer-operator.js.map +1 -1
  67. package/dist/input-processor.js +2 -2
  68. package/dist/input-processor.js.map +1 -1
  69. package/dist/logger.d.ts.map +1 -1
  70. package/dist/logger.js +1 -1
  71. package/dist/logger.js.map +1 -1
  72. package/dist/mcp.d.ts +2 -1
  73. package/dist/mcp.d.ts.map +1 -1
  74. package/dist/mcp.js +84 -21
  75. package/dist/mcp.js.map +1 -1
  76. package/dist/memory.d.ts.map +1 -1
  77. package/dist/memory.js +3 -3
  78. package/dist/memory.js.map +1 -1
  79. package/dist/output-util.d.ts +27 -0
  80. package/dist/output-util.d.ts.map +1 -0
  81. package/dist/output-util.js +74 -0
  82. package/dist/output-util.js.map +1 -0
  83. package/dist/retry.js +1 -1
  84. package/dist/retry.js.map +1 -1
  85. package/dist/ripgrep.d.ts.map +1 -1
  86. package/dist/ripgrep.js +5 -3
  87. package/dist/ripgrep.js.map +1 -1
  88. package/dist/sdk-output-adapter.d.ts +265 -0
  89. package/dist/sdk-output-adapter.d.ts.map +1 -0
  90. package/dist/sdk-output-adapter.js +701 -0
  91. package/dist/sdk-output-adapter.js.map +1 -0
  92. package/dist/sdk-session.d.ts +13 -0
  93. package/dist/sdk-session.d.ts.map +1 -0
  94. package/dist/sdk-session.js +50 -0
  95. package/dist/sdk-session.js.map +1 -0
  96. package/dist/session-manager.js +3 -3
  97. package/dist/session-manager.js.map +1 -1
  98. package/dist/session.d.ts +96 -2
  99. package/dist/session.d.ts.map +1 -1
  100. package/dist/session.js +849 -262
  101. package/dist/session.js.map +1 -1
  102. package/dist/shell.d.ts.map +1 -1
  103. package/dist/shell.js +5 -4
  104. package/dist/shell.js.map +1 -1
  105. package/dist/skill-installer.js +3 -3
  106. package/dist/skill-installer.js.map +1 -1
  107. package/dist/skill-invoker.d.ts +1 -1
  108. package/dist/skill-invoker.d.ts.map +1 -1
  109. package/dist/skill-invoker.js +2 -2
  110. package/dist/skill-invoker.js.map +1 -1
  111. package/dist/skill-loader.js +6 -5
  112. package/dist/skill-loader.js.map +1 -1
  113. package/dist/skill-manager.d.ts.map +1 -1
  114. package/dist/skill-manager.js +3 -2
  115. package/dist/skill-manager.js.map +1 -1
  116. package/dist/slash-commands.d.ts +1 -1
  117. package/dist/slash-commands.d.ts.map +1 -1
  118. package/dist/slash-commands.js +24 -11
  119. package/dist/slash-commands.js.map +1 -1
  120. package/dist/smart-approval.d.ts +20 -1
  121. package/dist/smart-approval.d.ts.map +1 -1
  122. package/dist/smart-approval.js +58 -1
  123. package/dist/smart-approval.js.map +1 -1
  124. package/dist/system-prompt-generator.js +3 -3
  125. package/dist/system-prompt-generator.js.map +1 -1
  126. package/dist/theme.d.ts.map +1 -1
  127. package/dist/theme.js +9 -8
  128. package/dist/theme.js.map +1 -1
  129. package/dist/tools.d.ts +15 -0
  130. package/dist/tools.d.ts.map +1 -1
  131. package/dist/tools.js +487 -215
  132. package/dist/tools.js.map +1 -1
  133. package/dist/types.d.ts +57 -0
  134. package/dist/types.d.ts.map +1 -1
  135. package/dist/types.js +49 -0
  136. package/dist/types.js.map +1 -1
  137. package/dist/update.d.ts.map +1 -1
  138. package/dist/update.js +12 -9
  139. package/dist/update.js.map +1 -1
  140. package/dist/workflow.d.ts.map +1 -1
  141. package/dist/workflow.js +1 -2
  142. package/dist/workflow.js.map +1 -1
  143. package/docs/third-party-models.md +16 -15
  144. package/package.json +3 -1
  145. package/src/agents.ts +7 -3
  146. package/src/ai-client/factory.ts +1 -36
  147. package/src/ai-client/index.ts +1 -1
  148. package/src/ai-client/providers/anthropic.ts +12 -3
  149. package/src/ai-client/providers/openai.ts +10 -4
  150. package/src/ai-client/providers/remote.ts +13 -10
  151. package/src/ai-client/types.ts +19 -0
  152. package/src/ai-client-factory.ts +5 -5
  153. package/src/auth.ts +11 -13
  154. package/src/cancellation.ts +3 -6
  155. package/src/checkpoint.ts +41 -4
  156. package/src/cli.ts +154 -37
  157. package/src/config.ts +1 -1
  158. package/src/context-compressor.ts +27 -22
  159. package/src/conversation.ts +9 -7
  160. package/src/gui-subagent/action-parser/actionParser.ts +2 -2
  161. package/src/gui-subagent/agent/gui-agent.ts +117 -34
  162. package/src/gui-subagent/index.ts +8 -0
  163. package/src/gui-subagent/operator/computer-operator.ts +2 -1
  164. package/src/input-processor.ts +2 -2
  165. package/src/logger.ts +2 -4
  166. package/src/mcp.ts +87 -23
  167. package/src/memory.ts +3 -4
  168. package/src/output-util.ts +80 -0
  169. package/src/retry.ts +1 -1
  170. package/src/ripgrep.ts +5 -3
  171. package/src/sdk-output-adapter.ts +842 -0
  172. package/src/sdk-session.ts +62 -0
  173. package/src/session-manager.ts +3 -3
  174. package/src/session.ts +942 -302
  175. package/src/shell.ts +6 -5
  176. package/src/skill-installer.ts +3 -3
  177. package/src/skill-invoker.ts +3 -4
  178. package/src/skill-loader.ts +7 -7
  179. package/src/skill-manager.ts +4 -3
  180. package/src/slash-commands.ts +24 -16
  181. package/src/smart-approval.ts +76 -1
  182. package/src/system-prompt-generator.ts +3 -3
  183. package/src/theme.ts +10 -9
  184. package/src/tools.ts +563 -267
  185. package/src/types.ts +118 -0
  186. package/src/update.ts +12 -9
  187. package/src/workflow.ts +2 -4
  188. package/test/cli-launch.test.ts +279 -0
  189. package/vitest.config.ts +2 -0
  190. /package/{.eslintrc.js → .eslintrc.cjs} +0 -0
package/src/types.ts CHANGED
@@ -217,3 +217,121 @@ export interface ToolCallItem {
217
217
  type?: string;
218
218
  function: ToolCallFunction;
219
219
  }
220
+
221
+ // ============================================================================
222
+ // SDK Message Types (for programmatic access)
223
+ // ============================================================================
224
+
225
+ /**
226
+ * SDK input message from client
227
+ */
228
+ export interface SdkInputMessage {
229
+ type: 'user';
230
+ content: string;
231
+ request_id?: string; // Optional request ID for tracking
232
+ uuid?: string;
233
+ parent_tool_use_id?: string | null;
234
+ }
235
+
236
+ /**
237
+ * SDK control request message
238
+ */
239
+ export interface SdkControlRequest {
240
+ type: 'control_request';
241
+ request_id: string;
242
+ request: {
243
+ subtype: 'interrupt' | 'initialize' | 'set_permission_mode' | 'set_model';
244
+ [key: string]: unknown;
245
+ };
246
+ }
247
+
248
+ /**
249
+ * SDK ping message (heartbeat)
250
+ */
251
+ export interface SdkPingMessage {
252
+ type: 'ping';
253
+ request_id?: string;
254
+ timestamp: number;
255
+ }
256
+
257
+ /**
258
+ * SDK approval response message (for responding to approval requests in SDK mode)
259
+ */
260
+ export interface SdkApprovalResponse {
261
+ type: 'approval_response';
262
+ request_id: string;
263
+ approved: boolean;
264
+ }
265
+
266
+ /**
267
+ * SDK question response message (for responding to ask_user_question in SDK mode)
268
+ */
269
+ export interface SdkQuestionResponse {
270
+ type: 'question_response';
271
+ request_id: string;
272
+ answers: string[];
273
+ }
274
+
275
+ /**
276
+ * SDK input message union type
277
+ */
278
+ export type SdkInputMessageType = SdkInputMessage | SdkControlRequest | SdkPingMessage | SdkApprovalResponse | SdkQuestionResponse;
279
+
280
+ /**
281
+ * Check if a string is a JSON SDK message
282
+ */
283
+ export function isSdkMessage(input: string): boolean {
284
+ const trimmed = input.trim();
285
+ if (!trimmed.startsWith('{')) {
286
+ return false;
287
+ }
288
+
289
+ try {
290
+ const parsed = JSON.parse(trimmed);
291
+ // Check for known message types
292
+ return (
293
+ parsed.type === 'user' ||
294
+ parsed.type === 'control_request' ||
295
+ parsed.type === 'ping' ||
296
+ parsed.type === 'approval_response' ||
297
+ parsed.type === 'question_response'
298
+ );
299
+ } catch {
300
+ return false;
301
+ }
302
+ }
303
+
304
+ /**
305
+ * Try to parse SDK message from string
306
+ */
307
+ export function parseSdkMessage(input: string): SdkInputMessageType | null {
308
+ const trimmed = input.trim();
309
+
310
+ try {
311
+ const parsed = JSON.parse(trimmed);
312
+
313
+ if (parsed.type === 'user') {
314
+ return parsed as SdkInputMessage;
315
+ }
316
+
317
+ if (parsed.type === 'control_request') {
318
+ return parsed as SdkControlRequest;
319
+ }
320
+
321
+ if (parsed.type === 'ping') {
322
+ return parsed as SdkPingMessage;
323
+ }
324
+
325
+ if (parsed.type === 'approval_response') {
326
+ return parsed as SdkApprovalResponse;
327
+ }
328
+
329
+ if (parsed.type === 'question_response') {
330
+ return parsed as SdkQuestionResponse;
331
+ }
332
+
333
+ return null;
334
+ } catch {
335
+ return null;
336
+ }
337
+ }
package/src/update.ts CHANGED
@@ -37,7 +37,7 @@ export class UpdateManager {
37
37
  latestVersion,
38
38
  updateAvailable,
39
39
  releaseNotes: response.data.versions[latestVersion]?.description,
40
- downloadUrl: `https://www.npmjs.com/package/@xagent-ai/xagent-cli/v/${latestVersion}`,
40
+ downloadUrl: `https://www.npmjs.com/package/@xagent-ai/cli/v/${latestVersion}`,
41
41
  };
42
42
 
43
43
  return versionInfo;
@@ -103,7 +103,7 @@ export class UpdateManager {
103
103
  return false;
104
104
  }
105
105
 
106
- private async promptUpdate(versionInfo: VersionInfo): Promise<{ shouldUpdate: boolean }> {
106
+ private async promptUpdate(_versionInfo: VersionInfo): Promise<{ shouldUpdate: boolean }> {
107
107
  const shouldUpdate = await confirm({
108
108
  message: 'Do you want to update now?',
109
109
  });
@@ -120,7 +120,7 @@ export class UpdateManager {
120
120
  try {
121
121
  console.log('Updating xAgent CLI...');
122
122
 
123
- await execAsync('npm install -g @xagent-ai/xagent-cli@latest', {
123
+ await execAsync('npm install -g @xagent-ai/cli@latest', {
124
124
  timeout: 120000,
125
125
  });
126
126
 
@@ -135,8 +135,8 @@ export class UpdateManager {
135
135
 
136
136
  if (tryManual === true) {
137
137
  console.log('\nManual update instructions:');
138
- console.log('1. Run: npm uninstall -g @xagent-ai/xagent-cli');
139
- console.log('2. Run: npm install -g @xagent-ai/xagent-cli@latest');
138
+ console.log('1. Run: npm uninstall -g @xagent-ai/cli');
139
+ console.log('2. Run: npm install -g @xagent-ai/cli@latest');
140
140
  console.log('3. Restart: xagent\n');
141
141
  }
142
142
 
@@ -150,7 +150,7 @@ export class UpdateManager {
150
150
  if (versionInfo.updateAvailable) {
151
151
  console.log(`\n📦 A new version is available: ${versionInfo.latestVersion}`);
152
152
  console.log(`Current version: ${versionInfo.currentVersion}`);
153
- console.log(`Update with: npm install -g @xagent-ai/xagent-cli@latest\n`);
153
+ console.log(`Update with: npm install -g @xagent-ai/cli@latest\n`);
154
154
  }
155
155
  }
156
156
 
@@ -223,9 +223,12 @@ export class UpdateManager {
223
223
  }
224
224
 
225
225
  async enableAutoUpdate(enabled: boolean): Promise<void> {
226
- const fs = await import('fs/promises');
227
- const path = await import('path');
228
- const os = await import('os');
226
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
227
+ const _fs = await import('fs/promises');
228
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
229
+ const _path = await import('path');
230
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
231
+ const _os = await import('os');
229
232
  const { getConfigManager } = await import('./config.js');
230
233
 
231
234
  const configManager = getConfigManager();
package/src/workflow.ts CHANGED
@@ -1,10 +1,9 @@
1
1
  import fs from 'fs/promises';
2
- import fsSync from 'fs';
3
2
  import path from 'path';
4
3
  import os from 'os';
5
4
  import axios from 'axios';
6
5
  import { AgentConfig, MCPServerConfig } from './types.js';
7
- import { SkillLoader, getSkillLoader } from './skill-loader.js';
6
+ import { getSkillLoader } from './skill-loader.js';
8
7
  import { getConfigManager } from './config.js';
9
8
 
10
9
  export interface WorkflowConfig {
@@ -78,7 +77,7 @@ export class WorkflowManager {
78
77
  }
79
78
  }
80
79
 
81
- private async loadWorkflowsFromDirectory(dirPath: string, scope: 'global' | 'project'): Promise<void> {
80
+ private async loadWorkflowsFromDirectory(dirPath: string, _scope: 'global' | 'project'): Promise<void> {
82
81
  try {
83
82
  const files = await fs.readdir(dirPath);
84
83
 
@@ -449,7 +448,6 @@ export class WorkflowManager {
449
448
  }
450
449
 
451
450
  async createWorkflowPackage(projectRoot: string): Promise<Buffer> {
452
- const workflowId = path.basename(projectRoot);
453
451
  const workflowConfigPath = path.join(projectRoot, '.xagent', 'workflow.json');
454
452
 
455
453
  let workflowConfig: WorkflowConfig;
@@ -0,0 +1,279 @@
1
+ /**
2
+ * xAgent CLI Launch Tests
3
+ *
4
+ * 这些测试用于验证 CLI 的基本启动和命令行为。
5
+ * 测试不依赖外部服务,可以离线运行。
6
+ */
7
+
8
+ import { describe, it, expect } from 'vitest';
9
+ import { fileURLToPath } from 'url';
10
+ import { dirname, join } from 'path';
11
+ import { execSync } from 'child_process';
12
+
13
+ const __filename = fileURLToPath(import.meta.url);
14
+ const __dirname = dirname(__filename);
15
+ const cliPath = join(__dirname, '../dist/cli.js');
16
+
17
+ // commander.js 的 --help 行为会导致退出码为 1,这是正常行为
18
+ const HELP_EXIT_CODE = 1;
19
+
20
+ /**
21
+ * 运行 CLI 命令并返回结果(使用 execSync)
22
+ */
23
+ function runCli(args: string[], options: { env?: Record<string, string> } = {}): {
24
+ exitCode: number;
25
+ stdout: string;
26
+ stderr: string;
27
+ } {
28
+ try {
29
+ const env = { ...process.env, ...options.env, NODE_ENV: 'test' };
30
+ const stdout = execSync(`"${process.execPath}" "${cliPath}" ${args.join(' ')}`, {
31
+ encoding: 'utf-8',
32
+ env,
33
+ timeout: 30000,
34
+ stdio: ['pipe', 'pipe', 'pipe']
35
+ });
36
+ return {
37
+ exitCode: 0,
38
+ stdout,
39
+ stderr: ''
40
+ };
41
+ } catch (error: any) {
42
+ // execSync 在命令失败时会抛出错误
43
+ const stdout = error.stdout || '';
44
+ const stderr = error.stderr || '';
45
+ const exitCode = error.status || -1;
46
+ // 合并 stdout 和 stderr,因为 Windows PowerShell 可能将输出重定向
47
+ const combinedOutput = stdout + stderr;
48
+ return {
49
+ exitCode,
50
+ stdout: combinedOutput,
51
+ stderr
52
+ };
53
+ }
54
+ }
55
+
56
+ describe('xAgent CLI Launch Tests', () => {
57
+ /**
58
+ * 测试版本号输出
59
+ */
60
+ describe('version', () => {
61
+ it('should output version with --version flag', () => {
62
+ const result = runCli(['--version']);
63
+
64
+ expect(result.exitCode).toBe(0);
65
+ expect(result.stdout).toMatch(/\d+\.\d+\.\d+/); // 匹配语义化版本号
66
+ });
67
+
68
+ it('should exit with code 0 on --version', () => {
69
+ const result = runCli(['--version']);
70
+ expect(result.exitCode).toBe(0);
71
+ });
72
+ });
73
+
74
+ /**
75
+ * 测试帮助信息
76
+ */
77
+ describe('help', () => {
78
+ it('should output help with --help flag', () => {
79
+ const result = runCli(['--help']);
80
+
81
+ // commander.js --help 退出码为 1 是正常行为
82
+ expect([0, HELP_EXIT_CODE]).toContain(result.exitCode);
83
+ // stdout 可能为空,输出在 stderr 中
84
+ const output = result.stdout || result.stderr;
85
+ expect(output).toContain('xagent');
86
+ expect(output).toContain('AI-powered');
87
+ });
88
+
89
+ it('should list available commands in help', () => {
90
+ const result = runCli(['--help']);
91
+ const output = result.stdout || result.stderr;
92
+
93
+ expect(output).toContain('start');
94
+ expect(output).toContain('auth');
95
+ expect(output).toContain('agent');
96
+ expect(output).toContain('mcp');
97
+ expect(output).toContain('init');
98
+ });
99
+
100
+ it('should show help for auth command', () => {
101
+ const result = runCli(['auth', '--help']);
102
+ const output = result.stdout || result.stderr;
103
+
104
+ expect([0, HELP_EXIT_CODE]).toContain(result.exitCode);
105
+ expect(output).toContain('auth');
106
+ // 检查认证相关文本
107
+ expect(output).toMatch(/authentication|authenticate|Auth/i);
108
+ });
109
+
110
+ it('should show help for mcp command', () => {
111
+ const result = runCli(['mcp', '--help']);
112
+ const output = result.stdout || result.stderr;
113
+
114
+ expect([0, HELP_EXIT_CODE]).toContain(result.exitCode);
115
+ expect(output).toContain('mcp');
116
+ expect(output).toContain('--list');
117
+ expect(output).toContain('--add');
118
+ expect(output).toContain('--remove');
119
+ });
120
+ });
121
+
122
+ /**
123
+ * 测试 start 命令启动
124
+ */
125
+ describe('start command', () => {
126
+ it('should start and output when running xagent start', () => {
127
+ // start 命令会启动交互式会话
128
+ // 我们使用 timeout 强制终止并检查是否有输出
129
+ const result = runCli(['start']);
130
+
131
+ // 由于是交互式命令,退出码可能是 0 或超时 -1
132
+ // 关键是验证命令能够启动并产生输出
133
+ const output = result.stdout || result.stderr;
134
+
135
+ // 验证命令能够启动(没有立即崩溃)
136
+ expect([0, -1]).toContain(result.exitCode);
137
+
138
+ // 验证有输出内容(启动提示信息)
139
+ expect(output).toBeTruthy();
140
+
141
+ // 验证输出包含 CLI 相关内容
142
+ expect(output.toLowerCase()).toMatch(/xagent|agent|start|welcome/i);
143
+ });
144
+ });
145
+
146
+ /**
147
+ * 测试错误处理
148
+ */
149
+ describe('error handling', () => {
150
+ it('should show error for unknown command', () => {
151
+ const result = runCli(['unknown-command-xyz']);
152
+
153
+ expect(result.exitCode).toBeGreaterThan(0);
154
+ const output = result.stdout || result.stderr;
155
+ expect(output).toBeTruthy();
156
+ });
157
+
158
+ it('should show error for unknown option', () => {
159
+ const result = runCli(['--unknown-option-xyz']);
160
+
161
+ expect(result.exitCode).toBeGreaterThan(0);
162
+ const output = result.stdout || result.stderr;
163
+ expect(output).toBeTruthy();
164
+ });
165
+ });
166
+
167
+ /**
168
+ * 测试 agent 命令
169
+ */
170
+ describe('agent command', () => {
171
+ it('should show agent help', () => {
172
+ const result = runCli(['agent', '--help']);
173
+ const output = result.stdout || result.stderr;
174
+
175
+ expect([0, HELP_EXIT_CODE]).toContain(result.exitCode);
176
+ // 检查帮助输出中包含关键选项
177
+ expect(output).toContain('--list');
178
+ expect(output).toContain('--add');
179
+ expect(output).toContain('--remove');
180
+ // 检查输出与 agent 相关
181
+ expect(output.toLowerCase()).toMatch(/agent|specify an action/i);
182
+ });
183
+
184
+ it('should list agents with --list flag', () => {
185
+ const result = runCli(['agent', '--list']);
186
+
187
+ // agent --list 可能会因为没有配置而返回非零退出码
188
+ expect([0, HELP_EXIT_CODE]).toContain(result.exitCode);
189
+ const output = result.stdout || result.stderr;
190
+ expect(output).toBeTruthy();
191
+ });
192
+ });
193
+
194
+ /**
195
+ * 测试 mcp 命令
196
+ */
197
+ describe('mcp command', () => {
198
+ it('should show mcp help', () => {
199
+ const result = runCli(['mcp', '--help']);
200
+ const output = result.stdout || result.stderr;
201
+
202
+ expect([0, HELP_EXIT_CODE]).toContain(result.exitCode);
203
+ expect(output).toContain('--list');
204
+ // 检查输出中包含 mcp 相关的选项
205
+ expect(output.toLowerCase()).toMatch(/mcp|transport|server|--add/i);
206
+ });
207
+
208
+ it('should list MCP servers with --list flag', () => {
209
+ const result = runCli(['mcp', '--list']);
210
+
211
+ // mcp --list 可能会因为没有配置而返回非零退出码
212
+ expect([0, HELP_EXIT_CODE]).toContain(result.exitCode);
213
+ });
214
+ });
215
+
216
+ /**
217
+ * 测试 init 命令
218
+ */
219
+ describe('init command', () => {
220
+ it('should show init help', () => {
221
+ const result = runCli(['init', '--help']);
222
+ const output = result.stdout || result.stderr;
223
+
224
+ expect([0, HELP_EXIT_CODE]).toContain(result.exitCode);
225
+ expect(output.toLowerCase()).toContain('init');
226
+ // init 命令会实际执行初始化,检查是否有关键内容
227
+ expect(output.toLowerCase()).toMatch(/project|xagent\.md|initial/i);
228
+ });
229
+ });
230
+
231
+ /**
232
+ * 测试 workflow 命令
233
+ */
234
+ describe('workflow command', () => {
235
+ it('should show workflow help', () => {
236
+ const result = runCli(['workflow', '--help']);
237
+ const output = result.stdout || result.stderr;
238
+
239
+ expect([0, HELP_EXIT_CODE]).toContain(result.exitCode);
240
+ // workflow 命令可能输出"Please specify an action"或帮助信息
241
+ expect(output.toLowerCase()).toMatch(/workflow|specify an action|--list/i);
242
+ expect(output).toContain('--list');
243
+ });
244
+
245
+ it('should list workflows with --list flag', () => {
246
+ const result = runCli(['workflow', '--list']);
247
+
248
+ expect([0, HELP_EXIT_CODE]).toContain(result.exitCode);
249
+ });
250
+ });
251
+
252
+ /**
253
+ * 测试 skill 命令
254
+ */
255
+ describe('skill command', () => {
256
+ it('should show skill help', () => {
257
+ const result = runCli(['skill', '--help']);
258
+ const output = result.stdout || result.stderr;
259
+
260
+ expect([0, HELP_EXIT_CODE]).toContain(result.exitCode);
261
+ expect(output.toLowerCase()).toContain('skill');
262
+ // skill 命令使用 -l 而不是 --list
263
+ expect(output.toLowerCase()).toMatch(/skill|-l|--add|--remove/i);
264
+ });
265
+ });
266
+
267
+ /**
268
+ * 测试环境变量处理
269
+ */
270
+ describe('environment handling', () => {
271
+ it('should handle empty XAGENT_BASE_URL gracefully', () => {
272
+ const result = runCli(['--help'], {
273
+ env: { XAGENT_BASE_URL: '' }
274
+ });
275
+
276
+ expect([0, HELP_EXIT_CODE]).toContain(result.exitCode);
277
+ });
278
+ });
279
+ });
package/vitest.config.ts CHANGED
@@ -5,6 +5,8 @@ export default defineConfig({
5
5
  globals: true,
6
6
  environment: 'node',
7
7
  include: ['src/**/*.{test,spec}.{ts,tsx}', 'test/**/*.{test,spec}.{ts,tsx}'],
8
+ testTimeout: 60000,
9
+ hookTimeout: 30000,
8
10
  coverage: {
9
11
  provider: 'v8',
10
12
  reporter: ['text', 'json', 'html'],
File without changes