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
package/src/app/cli.ts DELETED
@@ -1,424 +0,0 @@
1
- import { runMcpServer } from './mcp.js';
2
- import { CliProcessService } from '../cli-process-service.js';
3
- import { getCliDoctorStatus } from '../cli-utils.js';
4
- import { getModelsPayload } from '../model-catalog.js';
5
- import { validatePeekPids, validatePeekTimeSec } from '../peek.js';
6
-
7
- export const CLI_HELP_TEXT = `Usage: ai-cli <command> [options]
8
-
9
- Commands:
10
- run Start an AI CLI process in the background
11
- wait Wait for one or more pids
12
- peek Observe new agent events for a short window
13
- ps List tracked processes
14
- result Get the current result for a pid
15
- kill Terminate a tracked pid
16
- cleanup Remove completed and failed tracked processes
17
- doctor Check supported AI CLI binaries
18
- models List supported models and aliases
19
- mcp Start the MCP server
20
- help Show this help message
21
- `;
22
-
23
- export const RUN_HELP_TEXT = `Usage: ai-cli run --cwd <path> [options]
24
-
25
- Start an AI CLI process in the background.
26
-
27
- Options:
28
- --cwd <path> Working directory
29
- --prompt <text> Prompt text
30
- --prompt-file <path> Path to a prompt file
31
- --model <model> Model name or alias (e.g. sonnet, claude-ultra, gpt-5.2-codex, codex-ultra, gemini-2.5-pro, gemini-ultra, forge, opencode, oc-openai/gpt-5.4)
32
- --session-id <id> Resume a previous session, including OpenCode in-place resumes
33
- --reasoning-effort <level> Reasoning level for Claude/Codex only; unsupported for Gemini, Forge, and OpenCode
34
- --help, -h Show this help message
35
-
36
- Compatibility aliases:
37
- --workFolder, --work-folder
38
- --prompt_file
39
- --session_id
40
- --reasoning_effort
41
- `;
42
-
43
- export const WAIT_HELP_TEXT = `Usage: ai-cli wait <pid...> [options]
44
-
45
- Wait for one or more tracked processes to finish.
46
- By default each result uses the compact shape; set --verbose to include full metadata and detailed parsed output.
47
-
48
- Options:
49
- --timeout <seconds> Maximum wait time in seconds
50
- --verbose Return full metadata and detailed parsed output
51
- --help, -h Show this help message
52
- `;
53
-
54
- export const RESULT_HELP_TEXT = `Usage: ai-cli result <pid> [options]
55
-
56
- Get the current output and status of a tracked process. By default this returns a compact result shape; set --verbose to include full metadata and detailed parsed output.
57
-
58
- Options:
59
- --verbose Return full metadata and detailed parsed output
60
- --help, -h Show this help message
61
- `;
62
-
63
- export const PEEK_HELP_TEXT = `Usage: ai-cli peek <pid...> [options]
64
-
65
- Observe new natural-language agent messages, and optionally tool calls, for a short one-shot window.
66
- In v1, message extraction is supported for Codex, Claude, OpenCode, Gemini, and best-effort Forge Summary/Completed successfully lines. Forge tool calls are low-precision Execute/Finished markers and never include command output.
67
- This is not a history API, gapless streaming, or stdout/stderr tailing. No --follow mode is available in v1.
68
-
69
- Options:
70
- --time <seconds> Observation window in seconds. Defaults to 10, maximum 60
71
- --include-tool-calls Include normalized tool_call events without raw tool output
72
- --help, -h Show this help message
73
- `;
74
-
75
- export const KILL_HELP_TEXT = `Usage: ai-cli kill <pid>
76
-
77
- Terminate a tracked process.
78
-
79
- Options:
80
- --help, -h Show this help message
81
- `;
82
-
83
- export const CLEANUP_HELP_TEXT = `Usage: ai-cli cleanup
84
-
85
- Remove completed and failed tracked processes.
86
-
87
- Options:
88
- --help, -h Show this help message
89
- `;
90
-
91
- export const PS_HELP_TEXT = `Usage: ai-cli ps
92
-
93
- List tracked processes.
94
-
95
- Options:
96
- --help, -h Show this help message
97
- `;
98
-
99
- export const MODELS_HELP_TEXT = `Usage: ai-cli models
100
-
101
- List supported models and aliases.
102
-
103
- Options:
104
- --help, -h Show this help message
105
- `;
106
-
107
- export const DOCTOR_HELP_TEXT = `Usage: ai-cli doctor
108
-
109
- Check whether supported AI CLI binaries are available, including OpenCode.
110
-
111
- Options:
112
- --help, -h Show this help message
113
- `;
114
-
115
- export const MCP_HELP_TEXT = `Usage: ai-cli mcp
116
-
117
- Start the MCP server.
118
- `;
119
-
120
- interface CliDeps {
121
- stdout: (text: string) => void;
122
- stderr: (text: string) => void;
123
- startMcpServer: () => Promise<void>;
124
- runProcess: (options: {
125
- cwd: string;
126
- prompt?: string;
127
- prompt_file?: string;
128
- model?: string;
129
- session_id?: string;
130
- reasoning_effort?: string;
131
- }) => Promise<any>;
132
- listProcesses: () => Promise<any>;
133
- getProcessResult: (pid: number, verbose: boolean) => Promise<any>;
134
- waitForProcesses: (pids: number[], timeoutSeconds?: number, verbose?: boolean) => Promise<any>;
135
- peekProcesses: (pids: number[], peekTimeSec?: number, includeToolCalls?: boolean) => Promise<any>;
136
- killProcess: (pid: number) => Promise<any>;
137
- cleanupProcesses: () => Promise<any>;
138
- getDoctorStatus: () => any;
139
- }
140
-
141
- let cliProcessService: CliProcessService | null = null;
142
-
143
- function getCliProcessService(): CliProcessService {
144
- if (!cliProcessService) {
145
- cliProcessService = new CliProcessService();
146
- }
147
- return cliProcessService;
148
- }
149
-
150
- const defaultDeps: CliDeps = {
151
- stdout: (text: string) => process.stdout.write(text),
152
- stderr: (text: string) => process.stderr.write(text),
153
- startMcpServer: () => runMcpServer(),
154
- runProcess: (options) => getCliProcessService().startProcess(options),
155
- listProcesses: () => getCliProcessService().listProcesses(),
156
- getProcessResult: (pid, verbose) => getCliProcessService().getProcessResult(pid, verbose),
157
- waitForProcesses: (pids, timeoutSeconds, verbose) => getCliProcessService().waitForProcesses(pids, timeoutSeconds, verbose),
158
- peekProcesses: (pids, peekTimeSec, includeToolCalls) => getCliProcessService().peekProcesses(pids, peekTimeSec, includeToolCalls),
159
- killProcess: (pid) => getCliProcessService().killProcess(pid),
160
- cleanupProcesses: () => getCliProcessService().cleanupProcesses(),
161
- getDoctorStatus: () => getCliDoctorStatus(),
162
- };
163
-
164
- function parseArgs(argv: string[]): { positionals: string[]; flags: Record<string, string> } {
165
- const positionals: string[] = [];
166
- const flags: Record<string, string> = {};
167
-
168
- for (let i = 0; i < argv.length; i++) {
169
- const arg = argv[i];
170
- if (arg === '-h') {
171
- flags.h = '';
172
- continue;
173
- }
174
-
175
- if (!arg.startsWith('--')) {
176
- positionals.push(arg);
177
- continue;
178
- }
179
-
180
- const eqIdx = arg.indexOf('=');
181
- if (eqIdx !== -1) {
182
- flags[arg.slice(2, eqIdx)] = arg.slice(eqIdx + 1);
183
- continue;
184
- }
185
-
186
- const next = argv[i + 1];
187
- if (next !== undefined && !next.startsWith('--')) {
188
- flags[arg.slice(2)] = next;
189
- i++;
190
- } else {
191
- flags[arg.slice(2)] = '';
192
- }
193
- }
194
-
195
- return { positionals, flags };
196
- }
197
-
198
- function getFirstFlag(flags: Record<string, string>, names: string[]): string | undefined {
199
- for (const name of names) {
200
- if (name in flags) {
201
- return flags[name];
202
- }
203
- }
204
- return undefined;
205
- }
206
-
207
- function parsePositivePid(value: string | undefined): number | null {
208
- const pid = Number(value);
209
- if (!Number.isInteger(pid) || pid <= 0) {
210
- return null;
211
- }
212
- return pid;
213
- }
214
-
215
- function writeJson(stdout: (text: string) => void, value: unknown): void {
216
- stdout(`${JSON.stringify(value, null, 2)}\n`);
217
- }
218
-
219
- function hasHelpFlag(flags: Record<string, string>): boolean {
220
- return 'help' in flags || 'h' in flags;
221
- }
222
-
223
- function parsePeekCliPids(values: string[]): number[] {
224
- return validatePeekPids(values.map((value) => Number(value)));
225
- }
226
-
227
- export async function runCli(argv: string[], deps: Partial<CliDeps> = {}): Promise<number> {
228
- const {
229
- stdout,
230
- stderr,
231
- startMcpServer,
232
- runProcess,
233
- listProcesses,
234
- getProcessResult,
235
- waitForProcesses,
236
- peekProcesses,
237
- killProcess,
238
- cleanupProcesses,
239
- getDoctorStatus,
240
- } = { ...defaultDeps, ...deps };
241
- const [command] = argv;
242
-
243
- if (!command || command === 'help' || command === '--help' || command === '-h') {
244
- stdout(CLI_HELP_TEXT);
245
- return 0;
246
- }
247
-
248
- if (command === 'mcp') {
249
- const { flags } = parseArgs(argv.slice(1));
250
- if (hasHelpFlag(flags)) {
251
- stdout(MCP_HELP_TEXT);
252
- return 0;
253
- }
254
- await startMcpServer();
255
- return 0;
256
- }
257
-
258
- if (command === 'run') {
259
- const { flags } = parseArgs(argv.slice(1));
260
- if (hasHelpFlag(flags)) {
261
- stdout(RUN_HELP_TEXT);
262
- return 0;
263
- }
264
- const cwd = getFirstFlag(flags, ['cwd', 'workFolder', 'work-folder']);
265
- if (!cwd) {
266
- stderr('Missing required option: --cwd\n');
267
- stdout(CLI_HELP_TEXT);
268
- return 1;
269
- }
270
-
271
- const prompt = getFirstFlag(flags, ['prompt']);
272
- const promptFile = getFirstFlag(flags, ['prompt-file', 'prompt_file']);
273
- if (!prompt && !promptFile) {
274
- stderr('Missing required option: --prompt or --prompt-file\n');
275
- stdout(CLI_HELP_TEXT);
276
- return 1;
277
- }
278
-
279
- const result = await runProcess({
280
- cwd,
281
- prompt: prompt || undefined,
282
- prompt_file: promptFile || undefined,
283
- model: getFirstFlag(flags, ['model']) || undefined,
284
- session_id: getFirstFlag(flags, ['session-id', 'session_id']) || undefined,
285
- reasoning_effort: getFirstFlag(flags, ['reasoning-effort', 'reasoning_effort']) || undefined,
286
- });
287
- writeJson(stdout, result);
288
- return 0;
289
- }
290
-
291
- if (command === 'ps') {
292
- const { flags } = parseArgs(argv.slice(1));
293
- if (hasHelpFlag(flags)) {
294
- stdout(PS_HELP_TEXT);
295
- return 0;
296
- }
297
- writeJson(stdout, await listProcesses());
298
- return 0;
299
- }
300
-
301
- if (command === 'result') {
302
- const { positionals, flags } = parseArgs(argv.slice(1));
303
- if (hasHelpFlag(flags)) {
304
- stdout(RESULT_HELP_TEXT);
305
- return 0;
306
- }
307
- const pid = parsePositivePid(positionals[0]);
308
- if (pid === null) {
309
- stderr('Missing required pid argument\n');
310
- stdout(CLI_HELP_TEXT);
311
- return 1;
312
- }
313
- writeJson(stdout, await getProcessResult(pid, 'verbose' in flags));
314
- return 0;
315
- }
316
-
317
- if (command === 'wait') {
318
- const { positionals, flags } = parseArgs(argv.slice(1));
319
- if (hasHelpFlag(flags)) {
320
- stdout(WAIT_HELP_TEXT);
321
- return 0;
322
- }
323
- const pids = positionals.map((value) => parsePositivePid(value));
324
- if (pids.length === 0) {
325
- stderr('Missing required pid arguments\n');
326
- stdout(CLI_HELP_TEXT);
327
- return 1;
328
- }
329
- if (pids.some((pid) => pid === null)) {
330
- stderr('All pid arguments must be positive integers\n');
331
- stdout(CLI_HELP_TEXT);
332
- return 1;
333
- }
334
-
335
- const timeoutRaw = getFirstFlag(flags, ['timeout']);
336
- const timeout = timeoutRaw ? Number(timeoutRaw) : undefined;
337
- if (timeout !== undefined && (!Number.isFinite(timeout) || timeout <= 0)) {
338
- stderr('Invalid --timeout value\n');
339
- stdout(CLI_HELP_TEXT);
340
- return 1;
341
- }
342
-
343
- writeJson(stdout, await waitForProcesses(pids as number[], timeout, 'verbose' in flags));
344
- return 0;
345
- }
346
-
347
- if (command === 'peek') {
348
- const { positionals, flags } = parseArgs(argv.slice(1));
349
- if (hasHelpFlag(flags)) {
350
- stdout(PEEK_HELP_TEXT);
351
- return 0;
352
- }
353
- if ('follow' in flags) {
354
- stderr('peek does not support --follow in v1\n');
355
- stdout(CLI_HELP_TEXT);
356
- return 1;
357
- }
358
-
359
- let pids: number[];
360
- let peekTimeSec: number;
361
- try {
362
- pids = parsePeekCliPids(positionals);
363
- const timeRaw = getFirstFlag(flags, ['time']);
364
- peekTimeSec = validatePeekTimeSec(timeRaw === undefined ? undefined : Number(timeRaw));
365
- } catch (error: any) {
366
- stderr(`${error.message}\n`);
367
- stdout(CLI_HELP_TEXT);
368
- return 1;
369
- }
370
-
371
- writeJson(stdout, await peekProcesses(pids, peekTimeSec, 'include-tool-calls' in flags || 'include_tool_calls' in flags));
372
- return 0;
373
- }
374
-
375
- if (command === 'kill') {
376
- const { positionals, flags } = parseArgs(argv.slice(1));
377
- if (hasHelpFlag(flags)) {
378
- stdout(KILL_HELP_TEXT);
379
- return 0;
380
- }
381
- const pid = parsePositivePid(positionals[0]);
382
- if (pid === null) {
383
- stderr('Missing required pid argument\n');
384
- stdout(CLI_HELP_TEXT);
385
- return 1;
386
- }
387
- writeJson(stdout, await killProcess(pid));
388
- return 0;
389
- }
390
-
391
- if (command === 'cleanup') {
392
- const { flags } = parseArgs(argv.slice(1));
393
- if (hasHelpFlag(flags)) {
394
- stdout(CLEANUP_HELP_TEXT);
395
- return 0;
396
- }
397
- writeJson(stdout, await cleanupProcesses());
398
- return 0;
399
- }
400
-
401
- if (command === 'models') {
402
- const { flags } = parseArgs(argv.slice(1));
403
- if (hasHelpFlag(flags)) {
404
- stdout(MODELS_HELP_TEXT);
405
- return 0;
406
- }
407
- writeJson(stdout, getModelsPayload());
408
- return 0;
409
- }
410
-
411
- if (command === 'doctor') {
412
- const { flags } = parseArgs(argv.slice(1));
413
- if (hasHelpFlag(flags)) {
414
- stdout(DOCTOR_HELP_TEXT);
415
- return 0;
416
- }
417
- writeJson(stdout, getDoctorStatus());
418
- return 0;
419
- }
420
-
421
- stderr(`Unknown subcommand: ${command}\n`);
422
- stdout(CLI_HELP_TEXT);
423
- return 1;
424
- }