ai-cli-mcp 2.18.0 → 2.20.0

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 (101) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/README.ja.md +37 -11
  3. package/README.md +44 -11
  4. package/dist/app/cli.js +2 -1
  5. package/dist/app/mcp.js +65 -13
  6. package/dist/cli-builder.js +13 -6
  7. package/dist/cli-process-service.js +81 -95
  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 +111 -8
  12. package/dist/process-service.js +5 -4
  13. package/package.json +26 -2
  14. package/server.json +3 -3
  15. package/.gemini/settings.json +0 -11
  16. package/.github/dependabot.yml +0 -28
  17. package/.github/pull_request_template.md +0 -28
  18. package/.github/workflows/ci.yml +0 -34
  19. package/.github/workflows/dependency-review.yml +0 -22
  20. package/.github/workflows/publish.yml +0 -89
  21. package/.github/workflows/test.yml +0 -20
  22. package/.github/workflows/watch-session-prs.yml +0 -276
  23. package/.husky/pre-commit +0 -1
  24. package/.mcp.json +0 -11
  25. package/.releaserc.json +0 -18
  26. package/.vscode/settings.json +0 -3
  27. package/CONTRIBUTING.md +0 -81
  28. package/dist/__tests__/app-cli.test.js +0 -392
  29. package/dist/__tests__/cli-bin-smoke.test.js +0 -101
  30. package/dist/__tests__/cli-builder.test.js +0 -442
  31. package/dist/__tests__/cli-process-service.test.js +0 -655
  32. package/dist/__tests__/cli-utils.test.js +0 -171
  33. package/dist/__tests__/e2e.test.js +0 -256
  34. package/dist/__tests__/edge-cases.test.js +0 -130
  35. package/dist/__tests__/error-cases.test.js +0 -292
  36. package/dist/__tests__/mcp-contract.test.js +0 -636
  37. package/dist/__tests__/mocks.js +0 -32
  38. package/dist/__tests__/model-alias.test.js +0 -36
  39. package/dist/__tests__/parsers.test.js +0 -500
  40. package/dist/__tests__/peek.test.js +0 -36
  41. package/dist/__tests__/process-management.test.js +0 -871
  42. package/dist/__tests__/server.test.js +0 -809
  43. package/dist/__tests__/setup.js +0 -11
  44. package/dist/__tests__/utils/claude-mock.js +0 -80
  45. package/dist/__tests__/utils/mcp-client.js +0 -121
  46. package/dist/__tests__/utils/opencode-mock.js +0 -91
  47. package/dist/__tests__/utils/persistent-mock.js +0 -28
  48. package/dist/__tests__/utils/test-helpers.js +0 -11
  49. package/dist/__tests__/validation.test.js +0 -308
  50. package/dist/__tests__/version-print.test.js +0 -65
  51. package/dist/__tests__/wait.test.js +0 -260
  52. package/docs/RELEASE_CHECKLIST.md +0 -65
  53. package/docs/cli-architecture.md +0 -275
  54. package/docs/concept.md +0 -154
  55. package/docs/development.md +0 -156
  56. package/docs/e2e-testing.md +0 -148
  57. package/docs/prd.md +0 -146
  58. package/docs/session-stacking.md +0 -67
  59. package/src/__tests__/app-cli.test.ts +0 -495
  60. package/src/__tests__/cli-bin-smoke.test.ts +0 -136
  61. package/src/__tests__/cli-builder.test.ts +0 -549
  62. package/src/__tests__/cli-process-service.test.ts +0 -759
  63. package/src/__tests__/cli-utils.test.ts +0 -200
  64. package/src/__tests__/e2e.test.ts +0 -311
  65. package/src/__tests__/edge-cases.test.ts +0 -176
  66. package/src/__tests__/error-cases.test.ts +0 -370
  67. package/src/__tests__/mcp-contract.test.ts +0 -755
  68. package/src/__tests__/mocks.ts +0 -35
  69. package/src/__tests__/model-alias.test.ts +0 -44
  70. package/src/__tests__/parsers.test.ts +0 -564
  71. package/src/__tests__/peek.test.ts +0 -44
  72. package/src/__tests__/process-management.test.ts +0 -1043
  73. package/src/__tests__/server.test.ts +0 -1020
  74. package/src/__tests__/setup.ts +0 -13
  75. package/src/__tests__/utils/claude-mock.ts +0 -87
  76. package/src/__tests__/utils/mcp-client.ts +0 -159
  77. package/src/__tests__/utils/opencode-mock.ts +0 -108
  78. package/src/__tests__/utils/persistent-mock.ts +0 -33
  79. package/src/__tests__/utils/test-helpers.ts +0 -13
  80. package/src/__tests__/validation.test.ts +0 -369
  81. package/src/__tests__/version-print.test.ts +0 -81
  82. package/src/__tests__/wait.test.ts +0 -302
  83. package/src/app/cli.ts +0 -424
  84. package/src/app/mcp.ts +0 -466
  85. package/src/bin/ai-cli-mcp.ts +0 -7
  86. package/src/bin/ai-cli.ts +0 -11
  87. package/src/cli-builder.ts +0 -274
  88. package/src/cli-parse.ts +0 -105
  89. package/src/cli-process-service.ts +0 -708
  90. package/src/cli-utils.ts +0 -258
  91. package/src/cli.ts +0 -124
  92. package/src/model-catalog.ts +0 -87
  93. package/src/parsers.ts +0 -840
  94. package/src/peek.ts +0 -95
  95. package/src/process-result.ts +0 -88
  96. package/src/process-service.ts +0 -367
  97. package/src/server.ts +0 -10
  98. package/tsconfig.json +0 -16
  99. package/vitest.config.e2e.ts +0 -27
  100. package/vitest.config.ts +0 -22
  101. 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, and Gemini; Forge returns status with events: [].
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
- }