ai-cli-mcp 2.19.0 → 2.20.1

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 (100) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/README.ja.md +34 -8
  3. package/README.md +41 -8
  4. package/dist/app/cli.js +1 -0
  5. package/dist/app/mcp.js +64 -12
  6. package/dist/cli-builder.js +13 -6
  7. package/dist/cli-process-service.js +76 -91
  8. package/dist/cli-utils.js +6 -0
  9. package/dist/cli.js +1 -1
  10. package/dist/model-catalog.js +3 -2
  11. package/dist/parsers.js +8 -2
  12. package/package.json +27 -3
  13. package/server.json +3 -3
  14. package/.gemini/settings.json +0 -11
  15. package/.github/dependabot.yml +0 -28
  16. package/.github/pull_request_template.md +0 -28
  17. package/.github/workflows/ci.yml +0 -34
  18. package/.github/workflows/dependency-review.yml +0 -22
  19. package/.github/workflows/publish.yml +0 -89
  20. package/.github/workflows/test.yml +0 -20
  21. package/.github/workflows/watch-session-prs.yml +0 -276
  22. package/.husky/pre-commit +0 -1
  23. package/.mcp.json +0 -11
  24. package/.releaserc.json +0 -18
  25. package/.vscode/settings.json +0 -3
  26. package/CONTRIBUTING.md +0 -81
  27. package/dist/__tests__/app-cli.test.js +0 -392
  28. package/dist/__tests__/cli-bin-smoke.test.js +0 -101
  29. package/dist/__tests__/cli-builder.test.js +0 -442
  30. package/dist/__tests__/cli-process-service.test.js +0 -655
  31. package/dist/__tests__/cli-utils.test.js +0 -171
  32. package/dist/__tests__/e2e.test.js +0 -256
  33. package/dist/__tests__/edge-cases.test.js +0 -130
  34. package/dist/__tests__/error-cases.test.js +0 -292
  35. package/dist/__tests__/mcp-contract.test.js +0 -636
  36. package/dist/__tests__/mocks.js +0 -32
  37. package/dist/__tests__/model-alias.test.js +0 -36
  38. package/dist/__tests__/parsers.test.js +0 -646
  39. package/dist/__tests__/peek.test.js +0 -36
  40. package/dist/__tests__/process-management.test.js +0 -949
  41. package/dist/__tests__/server.test.js +0 -809
  42. package/dist/__tests__/setup.js +0 -11
  43. package/dist/__tests__/utils/claude-mock.js +0 -80
  44. package/dist/__tests__/utils/mcp-client.js +0 -121
  45. package/dist/__tests__/utils/opencode-mock.js +0 -91
  46. package/dist/__tests__/utils/persistent-mock.js +0 -28
  47. package/dist/__tests__/utils/test-helpers.js +0 -11
  48. package/dist/__tests__/validation.test.js +0 -308
  49. package/dist/__tests__/version-print.test.js +0 -65
  50. package/dist/__tests__/wait.test.js +0 -260
  51. package/docs/RELEASE_CHECKLIST.md +0 -65
  52. package/docs/cli-architecture.md +0 -275
  53. package/docs/concept.md +0 -154
  54. package/docs/development.md +0 -156
  55. package/docs/e2e-testing.md +0 -148
  56. package/docs/prd.md +0 -146
  57. package/docs/session-stacking.md +0 -67
  58. package/src/__tests__/app-cli.test.ts +0 -495
  59. package/src/__tests__/cli-bin-smoke.test.ts +0 -136
  60. package/src/__tests__/cli-builder.test.ts +0 -549
  61. package/src/__tests__/cli-process-service.test.ts +0 -759
  62. package/src/__tests__/cli-utils.test.ts +0 -200
  63. package/src/__tests__/e2e.test.ts +0 -311
  64. package/src/__tests__/edge-cases.test.ts +0 -176
  65. package/src/__tests__/error-cases.test.ts +0 -370
  66. package/src/__tests__/mcp-contract.test.ts +0 -755
  67. package/src/__tests__/mocks.ts +0 -35
  68. package/src/__tests__/model-alias.test.ts +0 -44
  69. package/src/__tests__/parsers.test.ts +0 -730
  70. package/src/__tests__/peek.test.ts +0 -44
  71. package/src/__tests__/process-management.test.ts +0 -1129
  72. package/src/__tests__/server.test.ts +0 -1020
  73. package/src/__tests__/setup.ts +0 -13
  74. package/src/__tests__/utils/claude-mock.ts +0 -87
  75. package/src/__tests__/utils/mcp-client.ts +0 -159
  76. package/src/__tests__/utils/opencode-mock.ts +0 -108
  77. package/src/__tests__/utils/persistent-mock.ts +0 -33
  78. package/src/__tests__/utils/test-helpers.ts +0 -13
  79. package/src/__tests__/validation.test.ts +0 -369
  80. package/src/__tests__/version-print.test.ts +0 -81
  81. package/src/__tests__/wait.test.ts +0 -302
  82. package/src/app/cli.ts +0 -424
  83. package/src/app/mcp.ts +0 -466
  84. package/src/bin/ai-cli-mcp.ts +0 -7
  85. package/src/bin/ai-cli.ts +0 -11
  86. package/src/cli-builder.ts +0 -274
  87. package/src/cli-parse.ts +0 -105
  88. package/src/cli-process-service.ts +0 -709
  89. package/src/cli-utils.ts +0 -258
  90. package/src/cli.ts +0 -124
  91. package/src/model-catalog.ts +0 -87
  92. package/src/parsers.ts +0 -965
  93. package/src/peek.ts +0 -95
  94. package/src/process-result.ts +0 -88
  95. package/src/process-service.ts +0 -368
  96. package/src/server.ts +0 -10
  97. package/tsconfig.json +0 -16
  98. package/vitest.config.e2e.ts +0 -27
  99. package/vitest.config.ts +0 -22
  100. package/vitest.config.unit.ts +0 -28
@@ -1,392 +0,0 @@
1
- import { describe, expect, it, vi } from 'vitest';
2
- import { CLI_HELP_TEXT, DOCTOR_HELP_TEXT, MODELS_HELP_TEXT, PEEK_HELP_TEXT, RESULT_HELP_TEXT, RUN_HELP_TEXT, WAIT_HELP_TEXT, runCli, } from '../app/cli.js';
3
- describe('ai-cli app', () => {
4
- it('prints help and exits successfully when no subcommand is provided', async () => {
5
- const stdout = vi.fn();
6
- const stderr = vi.fn();
7
- const startMcpServer = vi.fn();
8
- const exitCode = await runCli([], {
9
- stdout,
10
- stderr,
11
- startMcpServer,
12
- });
13
- expect(exitCode).toBe(0);
14
- expect(stdout).toHaveBeenCalledWith(CLI_HELP_TEXT);
15
- expect(stderr).not.toHaveBeenCalled();
16
- expect(startMcpServer).not.toHaveBeenCalled();
17
- });
18
- it('starts MCP mode when the mcp subcommand is provided', async () => {
19
- const stdout = vi.fn();
20
- const stderr = vi.fn();
21
- const startMcpServer = vi.fn().mockResolvedValue(undefined);
22
- const exitCode = await runCli(['mcp'], {
23
- stdout,
24
- stderr,
25
- startMcpServer,
26
- });
27
- expect(exitCode).toBe(0);
28
- expect(startMcpServer).toHaveBeenCalledTimes(1);
29
- expect(stdout).not.toHaveBeenCalled();
30
- expect(stderr).not.toHaveBeenCalled();
31
- });
32
- it('dispatches run with parsed CLI options', async () => {
33
- const stdout = vi.fn();
34
- const stderr = vi.fn();
35
- const startMcpServer = vi.fn();
36
- const runProcess = vi.fn().mockResolvedValue({
37
- pid: 123,
38
- status: 'started',
39
- agent: 'claude',
40
- message: 'claude process started successfully',
41
- });
42
- const exitCode = await runCli(['run', '--cwd', '/tmp/project', '--prompt', 'hello', '--model', 'sonnet'], {
43
- stdout,
44
- stderr,
45
- startMcpServer,
46
- runProcess,
47
- });
48
- expect(exitCode).toBe(0);
49
- expect(runProcess).toHaveBeenCalledWith({
50
- cwd: '/tmp/project',
51
- prompt: 'hello',
52
- model: 'sonnet',
53
- });
54
- expect(stdout).toHaveBeenCalledWith(expect.stringContaining('"pid": 123'));
55
- expect(stderr).not.toHaveBeenCalled();
56
- });
57
- it('accepts legacy run option aliases', async () => {
58
- const stdout = vi.fn();
59
- const stderr = vi.fn();
60
- const runProcess = vi.fn().mockResolvedValue({
61
- pid: 123,
62
- status: 'started',
63
- agent: 'claude',
64
- message: 'claude process started successfully',
65
- });
66
- const exitCode = await runCli([
67
- 'run',
68
- '--workFolder',
69
- '/tmp/project',
70
- '--prompt_file',
71
- '/tmp/prompt.txt',
72
- '--session_id',
73
- 'session-123',
74
- '--reasoning_effort',
75
- 'high',
76
- ], {
77
- stdout,
78
- stderr,
79
- runProcess,
80
- });
81
- expect(exitCode).toBe(0);
82
- expect(runProcess).toHaveBeenCalledWith({
83
- cwd: '/tmp/project',
84
- prompt_file: '/tmp/prompt.txt',
85
- session_id: 'session-123',
86
- reasoning_effort: 'high',
87
- });
88
- expect(stderr).not.toHaveBeenCalled();
89
- });
90
- it('requires a prompt or prompt file for run', async () => {
91
- const stdout = vi.fn();
92
- const stderr = vi.fn();
93
- const exitCode = await runCli(['run', '--cwd', '/tmp/project'], {
94
- stdout,
95
- stderr,
96
- });
97
- expect(exitCode).toBe(1);
98
- expect(stderr).toHaveBeenCalledWith('Missing required option: --prompt or --prompt-file\n');
99
- expect(stdout).toHaveBeenCalledWith(CLI_HELP_TEXT);
100
- });
101
- it('dispatches wait with pid arguments and timeout', async () => {
102
- const stdout = vi.fn();
103
- const stderr = vi.fn();
104
- const waitForProcesses = vi.fn().mockResolvedValue([{ pid: 123, status: 'completed' }]);
105
- const exitCode = await runCli(['wait', '123', '456', '--timeout', '5'], {
106
- stdout,
107
- stderr,
108
- waitForProcesses,
109
- });
110
- expect(exitCode).toBe(0);
111
- expect(waitForProcesses).toHaveBeenCalledWith([123, 456], 5, false);
112
- expect(stdout).toHaveBeenCalledWith(expect.stringContaining('"status": "completed"'));
113
- });
114
- it('passes verbose through to wait', async () => {
115
- const stdout = vi.fn();
116
- const stderr = vi.fn();
117
- const waitForProcesses = vi.fn().mockResolvedValue([{ pid: 123, status: 'completed' }]);
118
- const exitCode = await runCli(['wait', '123', '--verbose'], {
119
- stdout,
120
- stderr,
121
- waitForProcesses,
122
- });
123
- expect(exitCode).toBe(0);
124
- expect(waitForProcesses).toHaveBeenCalledWith([123], undefined, true);
125
- });
126
- it('rejects invalid wait timeout values', async () => {
127
- const stdout = vi.fn();
128
- const stderr = vi.fn();
129
- const waitForProcesses = vi.fn();
130
- const exitCode = await runCli(['wait', '123', '--timeout', 'abc'], {
131
- stdout,
132
- stderr,
133
- waitForProcesses,
134
- });
135
- expect(exitCode).toBe(1);
136
- expect(stderr).toHaveBeenCalledWith('Invalid --timeout value\n');
137
- expect(stdout).toHaveBeenCalledWith(CLI_HELP_TEXT);
138
- expect(waitForProcesses).not.toHaveBeenCalled();
139
- });
140
- it('rejects non-integer pid arguments for wait', async () => {
141
- const stdout = vi.fn();
142
- const stderr = vi.fn();
143
- const waitForProcesses = vi.fn();
144
- const exitCode = await runCli(['wait', '123', 'abc'], {
145
- stdout,
146
- stderr,
147
- waitForProcesses,
148
- });
149
- expect(exitCode).toBe(1);
150
- expect(stderr).toHaveBeenCalledWith('All pid arguments must be positive integers\n');
151
- expect(stdout).toHaveBeenCalledWith(CLI_HELP_TEXT);
152
- expect(waitForProcesses).not.toHaveBeenCalled();
153
- });
154
- it('dispatches peek with deduped pid arguments and time', async () => {
155
- const stdout = vi.fn();
156
- const stderr = vi.fn();
157
- const peekProcesses = vi.fn().mockResolvedValue({
158
- peek_started_at: '2026-04-11T12:34:56.789Z',
159
- observed_duration_sec: 0.01,
160
- processes: [],
161
- });
162
- const exitCode = await runCli(['peek', '123', '456', '123', '--time', '5', '--include-tool-calls'], {
163
- stdout,
164
- stderr,
165
- peekProcesses,
166
- });
167
- expect(exitCode).toBe(0);
168
- expect(peekProcesses).toHaveBeenCalledWith([123, 456], 5, true);
169
- expect(stdout).toHaveBeenCalledWith(expect.stringContaining('"peek_started_at"'));
170
- expect(stderr).not.toHaveBeenCalled();
171
- });
172
- it('defaults peek time and rejects --follow', async () => {
173
- const stdout = vi.fn();
174
- const stderr = vi.fn();
175
- const peekProcesses = vi.fn().mockResolvedValue({
176
- peek_started_at: '2026-04-11T12:34:56.789Z',
177
- observed_duration_sec: 0.01,
178
- processes: [],
179
- });
180
- const defaultExitCode = await runCli(['peek', '123'], { stdout, stderr, peekProcesses });
181
- expect(defaultExitCode).toBe(0);
182
- expect(peekProcesses).toHaveBeenCalledWith([123], 10, false);
183
- const followExitCode = await runCli(['peek', '123', '--follow'], { stdout, stderr, peekProcesses });
184
- expect(followExitCode).toBe(1);
185
- expect(stderr).toHaveBeenCalledWith('peek does not support --follow in v1\n');
186
- });
187
- it('rejects invalid peek time values', async () => {
188
- const stdout = vi.fn();
189
- const stderr = vi.fn();
190
- const peekProcesses = vi.fn();
191
- const exitCode = await runCli(['peek', '123', '--time', '1.5'], {
192
- stdout,
193
- stderr,
194
- peekProcesses,
195
- });
196
- expect(exitCode).toBe(1);
197
- expect(stderr).toHaveBeenCalledWith(expect.stringContaining('peek_time_sec must be a positive integer'));
198
- expect(peekProcesses).not.toHaveBeenCalled();
199
- });
200
- it('dispatches ps, result, and kill', async () => {
201
- const stdout = vi.fn();
202
- const stderr = vi.fn();
203
- const listProcesses = vi.fn().mockResolvedValue([{ pid: 123, agent: 'claude', status: 'running' }]);
204
- const getProcessResult = vi.fn().mockResolvedValue({ pid: 123, status: 'completed' });
205
- const killProcess = vi.fn().mockResolvedValue({ pid: 123, status: 'terminated' });
206
- const psExitCode = await runCli(['ps'], { stdout, stderr, listProcesses });
207
- expect(psExitCode).toBe(0);
208
- expect(listProcesses).toHaveBeenCalledTimes(1);
209
- const resultExitCode = await runCli(['result', '123'], { stdout, stderr, getProcessResult });
210
- expect(resultExitCode).toBe(0);
211
- expect(getProcessResult).toHaveBeenCalledWith(123, false);
212
- const killExitCode = await runCli(['kill', '123'], { stdout, stderr, killProcess });
213
- expect(killExitCode).toBe(0);
214
- expect(killProcess).toHaveBeenCalledWith(123);
215
- });
216
- it('dispatches cleanup', async () => {
217
- const stdout = vi.fn();
218
- const stderr = vi.fn();
219
- const cleanupProcesses = vi.fn().mockResolvedValue({ removed: 2, message: 'Removed 2 processes' });
220
- const exitCode = await runCli(['cleanup'], { stdout, stderr, cleanupProcesses });
221
- expect(exitCode).toBe(0);
222
- expect(cleanupProcesses).toHaveBeenCalledTimes(1);
223
- expect(stdout).toHaveBeenCalledWith(expect.stringContaining('"removed": 2'));
224
- });
225
- it('prints models as structured json', async () => {
226
- const stdout = vi.fn();
227
- const stderr = vi.fn();
228
- const exitCode = await runCli(['models'], { stdout, stderr });
229
- const payload = JSON.parse(stdout.mock.calls[0][0]);
230
- expect(exitCode).toBe(0);
231
- expect(payload.aliases).toEqual(expect.any(Array));
232
- expect(payload.claude).toContain('sonnet');
233
- expect(payload.codex).toContain('gpt-5.4');
234
- expect(payload.forge).toEqual(['forge']);
235
- expect(payload.opencode).toEqual(['opencode']);
236
- expect(payload.dynamicModelBackends).toEqual({
237
- opencode: {
238
- explicitPrefix: 'oc-',
239
- explicitPattern: 'oc-<provider/model>',
240
- discoveryCommand: 'opencode models',
241
- modelsAreDynamic: true,
242
- },
243
- });
244
- expect(stderr).not.toHaveBeenCalled();
245
- });
246
- it('prints doctor status as structured json', async () => {
247
- const stdout = vi.fn();
248
- const stderr = vi.fn();
249
- const getDoctorStatus = vi.fn().mockReturnValue({
250
- claude: {
251
- configuredCommand: 'claude',
252
- resolvedPath: '/tmp/bin/claude',
253
- available: true,
254
- lookup: 'path',
255
- },
256
- codex: {
257
- configuredCommand: 'codex',
258
- resolvedPath: null,
259
- available: false,
260
- lookup: 'path',
261
- },
262
- gemini: {
263
- configuredCommand: 'gemini',
264
- resolvedPath: '/tmp/bin/gemini',
265
- available: true,
266
- lookup: 'path',
267
- },
268
- forge: {
269
- configuredCommand: 'forge',
270
- resolvedPath: '/tmp/bin/forge',
271
- available: true,
272
- lookup: 'path',
273
- },
274
- opencode: {
275
- configuredCommand: 'opencode',
276
- resolvedPath: '/tmp/bin/opencode',
277
- available: true,
278
- lookup: 'path',
279
- },
280
- });
281
- const exitCode = await runCli(['doctor'], { stdout, stderr, getDoctorStatus });
282
- expect(exitCode).toBe(0);
283
- expect(getDoctorStatus).toHaveBeenCalledTimes(1);
284
- expect(stdout).toHaveBeenCalledWith(expect.stringContaining('"configuredCommand": "claude"'));
285
- expect(stdout).toHaveBeenCalledWith(expect.stringContaining('"available": false'));
286
- expect(stderr).not.toHaveBeenCalled();
287
- });
288
- it('passes verbose through to result', async () => {
289
- const stdout = vi.fn();
290
- const stderr = vi.fn();
291
- const getProcessResult = vi.fn().mockResolvedValue({ pid: 123, status: 'completed' });
292
- const exitCode = await runCli(['result', '123', '--verbose'], { stdout, stderr, getProcessResult });
293
- expect(exitCode).toBe(0);
294
- expect(getProcessResult).toHaveBeenCalledWith(123, true);
295
- });
296
- it('prints detailed help for run --help', async () => {
297
- const stdout = vi.fn();
298
- const stderr = vi.fn();
299
- const exitCode = await runCli(['run', '--help'], { stdout, stderr });
300
- expect(exitCode).toBe(0);
301
- expect(stdout).toHaveBeenCalledWith(RUN_HELP_TEXT);
302
- expect(stdout).toHaveBeenCalledWith(expect.stringContaining('claude-ultra'));
303
- expect(stdout).toHaveBeenCalledWith(expect.stringContaining('gpt-5.2-codex'));
304
- expect(stdout).toHaveBeenCalledWith(expect.stringContaining('gemini-2.5-pro'));
305
- expect(stdout).toHaveBeenCalledWith(expect.stringContaining('forge'));
306
- expect(stdout).toHaveBeenCalledWith(expect.stringContaining('opencode'));
307
- expect(stdout).toHaveBeenCalledWith(expect.stringContaining('oc-openai/gpt-5.4'));
308
- expect(stderr).not.toHaveBeenCalled();
309
- });
310
- it('prints detailed help for result --help', async () => {
311
- const stdout = vi.fn();
312
- const stderr = vi.fn();
313
- const exitCode = await runCli(['result', '--help'], { stdout, stderr });
314
- expect(exitCode).toBe(0);
315
- expect(stdout).toHaveBeenCalledWith(RESULT_HELP_TEXT);
316
- expect(stdout).toHaveBeenCalledWith(expect.stringContaining('compact result shape'));
317
- expect(stdout).toHaveBeenCalledWith(expect.stringContaining('--verbose'));
318
- expect(stderr).not.toHaveBeenCalled();
319
- });
320
- it('prints detailed help for wait --help', async () => {
321
- const stdout = vi.fn();
322
- const stderr = vi.fn();
323
- const exitCode = await runCli(['wait', '--help'], { stdout, stderr });
324
- expect(exitCode).toBe(0);
325
- expect(stdout).toHaveBeenCalledWith(WAIT_HELP_TEXT);
326
- expect(stdout).toHaveBeenCalledWith(expect.stringContaining('compact shape'));
327
- expect(stdout).toHaveBeenCalledWith(expect.stringContaining('--verbose'));
328
- expect(stderr).not.toHaveBeenCalled();
329
- });
330
- it('prints detailed help for peek --help', async () => {
331
- const stdout = vi.fn();
332
- const stderr = vi.fn();
333
- const exitCode = await runCli(['peek', '--help'], { stdout, stderr });
334
- expect(exitCode).toBe(0);
335
- expect(stdout).toHaveBeenCalledWith(PEEK_HELP_TEXT);
336
- expect(stdout).toHaveBeenCalledWith(expect.stringContaining('No --follow mode'));
337
- expect(stderr).not.toHaveBeenCalled();
338
- });
339
- it('prints detailed help for models --help', async () => {
340
- const stdout = vi.fn();
341
- const stderr = vi.fn();
342
- const exitCode = await runCli(['models', '--help'], { stdout, stderr });
343
- expect(exitCode).toBe(0);
344
- expect(stdout).toHaveBeenCalledWith(MODELS_HELP_TEXT);
345
- expect(stderr).not.toHaveBeenCalled();
346
- });
347
- it('prints detailed help for doctor --help', async () => {
348
- const stdout = vi.fn();
349
- const stderr = vi.fn();
350
- const exitCode = await runCli(['doctor', '--help'], { stdout, stderr });
351
- expect(exitCode).toBe(0);
352
- expect(stdout).toHaveBeenCalledWith(DOCTOR_HELP_TEXT);
353
- expect(stdout).toHaveBeenCalledWith(expect.stringContaining('OpenCode'));
354
- expect(stderr).not.toHaveBeenCalled();
355
- });
356
- it('prints detailed help for doctor -h', async () => {
357
- const stdout = vi.fn();
358
- const stderr = vi.fn();
359
- const exitCode = await runCli(['doctor', '-h'], { stdout, stderr });
360
- expect(exitCode).toBe(0);
361
- expect(stdout).toHaveBeenCalledWith(DOCTOR_HELP_TEXT);
362
- expect(stdout).toHaveBeenCalledWith(expect.stringContaining('OpenCode'));
363
- expect(stderr).not.toHaveBeenCalled();
364
- });
365
- it('prints help for --help', async () => {
366
- const stdout = vi.fn();
367
- const stderr = vi.fn();
368
- const startMcpServer = vi.fn();
369
- const exitCode = await runCli(['--help'], {
370
- stdout,
371
- stderr,
372
- startMcpServer,
373
- });
374
- expect(exitCode).toBe(0);
375
- expect(stdout).toHaveBeenCalledWith(CLI_HELP_TEXT);
376
- expect(stderr).not.toHaveBeenCalled();
377
- });
378
- it('returns a non-zero exit code for unknown subcommands', async () => {
379
- const stdout = vi.fn();
380
- const stderr = vi.fn();
381
- const startMcpServer = vi.fn();
382
- const exitCode = await runCli(['unknown'], {
383
- stdout,
384
- stderr,
385
- startMcpServer,
386
- });
387
- expect(exitCode).toBe(1);
388
- expect(stderr).toHaveBeenCalledWith(expect.stringContaining('Unknown subcommand: unknown'));
389
- expect(stdout).toHaveBeenCalledWith(CLI_HELP_TEXT);
390
- expect(startMcpServer).not.toHaveBeenCalled();
391
- });
392
- });
@@ -1,101 +0,0 @@
1
- import { execFileSync } from 'node:child_process';
2
- import { chmodSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
3
- import { tmpdir } from 'node:os';
4
- import { delimiter, join } from 'node:path';
5
- import { afterEach, describe, expect, it } from 'vitest';
6
- const tempDirs = [];
7
- function makeTempDir(prefix) {
8
- const dir = mkdtempSync(join(tmpdir(), prefix));
9
- tempDirs.push(dir);
10
- return dir;
11
- }
12
- function writeExecutable(dir, name) {
13
- const filePath = join(dir, name);
14
- writeFileSync(filePath, '#!/bin/sh\nexit 0\n', 'utf8');
15
- chmodSync(filePath, 0o755);
16
- }
17
- afterEach(() => {
18
- for (const dir of tempDirs.splice(0)) {
19
- rmSync(dir, { recursive: true, force: true });
20
- }
21
- });
22
- describe('cli helper entrypoint smoke', () => {
23
- it('prints help for cli.run with OpenCode examples', () => {
24
- const output = execFileSync('node', ['--import', 'tsx', 'src/cli.ts', '--help'], {
25
- cwd: process.cwd(),
26
- encoding: 'utf8',
27
- env: process.env,
28
- });
29
- expect(output).toContain('Usage: npm run -s cli.run -- --model <model> --workFolder <path> --prompt "..." [options]');
30
- expect(output).toContain('opencode');
31
- expect(output).toContain('oc-openai/gpt-5.4');
32
- expect(output).toContain('OpenCode');
33
- expect(output).toContain('npm run -s cli.run.parse -- --agent opencode < raw.txt');
34
- });
35
- it('prints help for cli.run.parse with OpenCode agent support', () => {
36
- const output = execFileSync('node', ['--import', 'tsx', 'src/cli-parse.ts', '--help'], {
37
- cwd: process.cwd(),
38
- encoding: 'utf8',
39
- env: process.env,
40
- });
41
- expect(output).toContain('Usage: npm run -s cli.run.parse -- --agent <claude|codex|gemini|forge|opencode>');
42
- expect(output).toContain('Agent type: claude, codex, gemini, forge, or opencode');
43
- expect(output).toContain('npm run -s cli.run.parse -- --agent opencode < raw.txt');
44
- });
45
- it('parses OpenCode NDJSON through cli.run.parse', () => {
46
- const output = execFileSync('node', ['--import', 'tsx', 'src/cli-parse.ts', '--agent', 'opencode'], {
47
- cwd: process.cwd(),
48
- encoding: 'utf8',
49
- env: process.env,
50
- input: '{"type":"step_start","sessionID":"ses_cli_parse"}\n{"type":"text","sessionID":"ses_cli_parse","part":{"type":"text","text":"Hello from cli.parse"}}\n{"type":"step_finish","sessionID":"ses_cli_parse","part":{"type":"step-finish","tokens":{"total":9},"cost":1}}\n',
51
- });
52
- expect(JSON.parse(output)).toEqual({
53
- message: 'Hello from cli.parse',
54
- session_id: 'ses_cli_parse',
55
- tokens: { total: 9 },
56
- cost: 1,
57
- });
58
- });
59
- });
60
- describe('ai-cli entrypoint smoke', () => {
61
- it('prints doctor output for the ai-cli entrypoint', () => {
62
- const fakeBinDir = makeTempDir('ai-cli-bin-');
63
- writeExecutable(fakeBinDir, 'claude');
64
- writeExecutable(fakeBinDir, 'codex');
65
- writeExecutable(fakeBinDir, 'gemini');
66
- writeExecutable(fakeBinDir, 'forge');
67
- writeExecutable(fakeBinDir, 'opencode');
68
- const output = execFileSync('node', ['--import', 'tsx', 'src/bin/ai-cli.ts', 'doctor'], {
69
- cwd: process.cwd(),
70
- encoding: 'utf8',
71
- env: {
72
- ...process.env,
73
- PATH: `${fakeBinDir}${delimiter}${process.env.PATH || ''}`,
74
- CLAUDE_CLI_NAME: 'claude',
75
- CODEX_CLI_NAME: 'codex',
76
- GEMINI_CLI_NAME: 'gemini',
77
- FORGE_CLI_NAME: 'forge',
78
- OPENCODE_CLI_NAME: 'opencode',
79
- },
80
- });
81
- expect(output).toContain('"claude"');
82
- expect(output).toContain('"codex"');
83
- expect(output).toContain('"gemini"');
84
- expect(output).toContain('"forge"');
85
- expect(output).toContain('"opencode"');
86
- expect(output).toContain('"available": true');
87
- });
88
- it('prints run help for the ai-cli entrypoint', () => {
89
- const output = execFileSync('node', ['--import', 'tsx', 'src/bin/ai-cli.ts', 'run', '--help'], {
90
- cwd: process.cwd(),
91
- encoding: 'utf8',
92
- env: process.env,
93
- });
94
- expect(output).toContain('Usage: ai-cli run --cwd <path> [options]');
95
- expect(output).toContain('--model <model>');
96
- expect(output).toContain('claude-ultra');
97
- expect(output).toContain('forge');
98
- expect(output).toContain('opencode');
99
- expect(output).toContain('oc-openai/gpt-5.4');
100
- });
101
- });