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.
- package/CHANGELOG.md +26 -0
- package/README.ja.md +34 -8
- package/README.md +41 -8
- package/dist/app/cli.js +1 -0
- package/dist/app/mcp.js +64 -12
- package/dist/cli-builder.js +13 -6
- package/dist/cli-process-service.js +76 -91
- package/dist/cli-utils.js +6 -0
- package/dist/cli.js +1 -1
- package/dist/model-catalog.js +3 -2
- package/dist/parsers.js +8 -2
- package/package.json +27 -3
- package/server.json +3 -3
- package/.gemini/settings.json +0 -11
- package/.github/dependabot.yml +0 -28
- package/.github/pull_request_template.md +0 -28
- package/.github/workflows/ci.yml +0 -34
- package/.github/workflows/dependency-review.yml +0 -22
- package/.github/workflows/publish.yml +0 -89
- package/.github/workflows/test.yml +0 -20
- package/.github/workflows/watch-session-prs.yml +0 -276
- package/.husky/pre-commit +0 -1
- package/.mcp.json +0 -11
- package/.releaserc.json +0 -18
- package/.vscode/settings.json +0 -3
- package/CONTRIBUTING.md +0 -81
- package/dist/__tests__/app-cli.test.js +0 -392
- package/dist/__tests__/cli-bin-smoke.test.js +0 -101
- package/dist/__tests__/cli-builder.test.js +0 -442
- package/dist/__tests__/cli-process-service.test.js +0 -655
- package/dist/__tests__/cli-utils.test.js +0 -171
- package/dist/__tests__/e2e.test.js +0 -256
- package/dist/__tests__/edge-cases.test.js +0 -130
- package/dist/__tests__/error-cases.test.js +0 -292
- package/dist/__tests__/mcp-contract.test.js +0 -636
- package/dist/__tests__/mocks.js +0 -32
- package/dist/__tests__/model-alias.test.js +0 -36
- package/dist/__tests__/parsers.test.js +0 -646
- package/dist/__tests__/peek.test.js +0 -36
- package/dist/__tests__/process-management.test.js +0 -949
- package/dist/__tests__/server.test.js +0 -809
- package/dist/__tests__/setup.js +0 -11
- package/dist/__tests__/utils/claude-mock.js +0 -80
- package/dist/__tests__/utils/mcp-client.js +0 -121
- package/dist/__tests__/utils/opencode-mock.js +0 -91
- package/dist/__tests__/utils/persistent-mock.js +0 -28
- package/dist/__tests__/utils/test-helpers.js +0 -11
- package/dist/__tests__/validation.test.js +0 -308
- package/dist/__tests__/version-print.test.js +0 -65
- package/dist/__tests__/wait.test.js +0 -260
- package/docs/RELEASE_CHECKLIST.md +0 -65
- package/docs/cli-architecture.md +0 -275
- package/docs/concept.md +0 -154
- package/docs/development.md +0 -156
- package/docs/e2e-testing.md +0 -148
- package/docs/prd.md +0 -146
- package/docs/session-stacking.md +0 -67
- package/src/__tests__/app-cli.test.ts +0 -495
- package/src/__tests__/cli-bin-smoke.test.ts +0 -136
- package/src/__tests__/cli-builder.test.ts +0 -549
- package/src/__tests__/cli-process-service.test.ts +0 -759
- package/src/__tests__/cli-utils.test.ts +0 -200
- package/src/__tests__/e2e.test.ts +0 -311
- package/src/__tests__/edge-cases.test.ts +0 -176
- package/src/__tests__/error-cases.test.ts +0 -370
- package/src/__tests__/mcp-contract.test.ts +0 -755
- package/src/__tests__/mocks.ts +0 -35
- package/src/__tests__/model-alias.test.ts +0 -44
- package/src/__tests__/parsers.test.ts +0 -730
- package/src/__tests__/peek.test.ts +0 -44
- package/src/__tests__/process-management.test.ts +0 -1129
- package/src/__tests__/server.test.ts +0 -1020
- package/src/__tests__/setup.ts +0 -13
- package/src/__tests__/utils/claude-mock.ts +0 -87
- package/src/__tests__/utils/mcp-client.ts +0 -159
- package/src/__tests__/utils/opencode-mock.ts +0 -108
- package/src/__tests__/utils/persistent-mock.ts +0 -33
- package/src/__tests__/utils/test-helpers.ts +0 -13
- package/src/__tests__/validation.test.ts +0 -369
- package/src/__tests__/version-print.test.ts +0 -81
- package/src/__tests__/wait.test.ts +0 -302
- package/src/app/cli.ts +0 -424
- package/src/app/mcp.ts +0 -466
- package/src/bin/ai-cli-mcp.ts +0 -7
- package/src/bin/ai-cli.ts +0 -11
- package/src/cli-builder.ts +0 -274
- package/src/cli-parse.ts +0 -105
- package/src/cli-process-service.ts +0 -709
- package/src/cli-utils.ts +0 -258
- package/src/cli.ts +0 -124
- package/src/model-catalog.ts +0 -87
- package/src/parsers.ts +0 -965
- package/src/peek.ts +0 -95
- package/src/process-result.ts +0 -88
- package/src/process-service.ts +0 -368
- package/src/server.ts +0 -10
- package/tsconfig.json +0 -16
- package/vitest.config.e2e.ts +0 -27
- package/vitest.config.ts +0 -22
- package/vitest.config.unit.ts +0 -28
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { spawn } from 'node:child_process';
|
|
2
|
-
import { chmodSync, closeSync, existsSync, mkdirSync, openSync, readSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, statSync,
|
|
2
|
+
import { appendFileSync, chmodSync, closeSync, existsSync, mkdirSync, openSync, readSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, statSync, writeFileSync, } from 'node:fs';
|
|
3
3
|
import { join, basename, dirname } from 'node:path';
|
|
4
4
|
import { homedir } from 'node:os';
|
|
5
5
|
import { buildCliCommand } from './cli-builder.js';
|
|
@@ -7,6 +7,7 @@ import { findClaudeCli, findCodexCli, findForgeCli, findGeminiCli, findOpencodeC
|
|
|
7
7
|
import { parseClaudeOutput, parseCodexOutput, parseForgeOutput, parseGeminiOutput, parseOpenCodeOutput, PeekEventExtractor } from './parsers.js';
|
|
8
8
|
import { buildProcessResult } from './process-result.js';
|
|
9
9
|
import { appendPeekEvents, buildNotFoundPeekProcess, observedDurationSec, validatePeekPids, validatePeekTimeSec, } from './peek.js';
|
|
10
|
+
const SIGTERM_EXIT_CODE = 143;
|
|
10
11
|
function resolveDefaultStateDir() {
|
|
11
12
|
return process.env.AI_CLI_STATE_DIR || join(homedir(), '.local', 'state', 'ai-cli');
|
|
12
13
|
}
|
|
@@ -73,65 +74,7 @@ export class CliProcessService {
|
|
|
73
74
|
reasoning_effort: options.reasoning_effort,
|
|
74
75
|
cliPaths: this.cliPaths,
|
|
75
76
|
});
|
|
76
|
-
|
|
77
|
-
return this.startDetachedOpenCodeProcess(cmd, options.model);
|
|
78
|
-
}
|
|
79
|
-
const stdoutPath = this.resolveStdoutPathForPidPlaceholder();
|
|
80
|
-
const stderrPath = this.resolveStderrPathForPidPlaceholder();
|
|
81
|
-
let stdoutFd;
|
|
82
|
-
let stderrFd;
|
|
83
|
-
try {
|
|
84
|
-
stdoutFd = openSync(stdoutPath, 'w');
|
|
85
|
-
stderrFd = openSync(stderrPath, 'w');
|
|
86
|
-
const childProcess = spawn(cmd.cliPath, cmd.args, {
|
|
87
|
-
cwd: cmd.cwd,
|
|
88
|
-
detached: true,
|
|
89
|
-
stdio: ['ignore', stdoutFd, stderrFd],
|
|
90
|
-
});
|
|
91
|
-
const pid = childProcess.pid;
|
|
92
|
-
childProcess.unref();
|
|
93
|
-
if (!pid) {
|
|
94
|
-
throw new Error(`Failed to start ${cmd.agent} CLI process`);
|
|
95
|
-
}
|
|
96
|
-
const processDir = this.resolveProcessDir(cmd.cwd, pid);
|
|
97
|
-
mkdirSync(processDir, { recursive: true });
|
|
98
|
-
const finalStdoutPath = this.resolveStdoutPath(processDir);
|
|
99
|
-
const finalStderrPath = this.resolveStderrPath(processDir);
|
|
100
|
-
this.renamePlaceholderFile(stdoutPath, finalStdoutPath);
|
|
101
|
-
this.renamePlaceholderFile(stderrPath, finalStderrPath);
|
|
102
|
-
const storedProcess = {
|
|
103
|
-
pid,
|
|
104
|
-
prompt: cmd.prompt,
|
|
105
|
-
workFolder: cmd.cwd,
|
|
106
|
-
cwdKey: this.resolveCwdKey(cmd.cwd),
|
|
107
|
-
model: options.model,
|
|
108
|
-
toolType: cmd.agent,
|
|
109
|
-
startTime: new Date().toISOString(),
|
|
110
|
-
stdoutPath: finalStdoutPath,
|
|
111
|
-
stderrPath: finalStderrPath,
|
|
112
|
-
status: 'running',
|
|
113
|
-
};
|
|
114
|
-
this.writeProcess(storedProcess);
|
|
115
|
-
return {
|
|
116
|
-
pid,
|
|
117
|
-
status: 'started',
|
|
118
|
-
agent: cmd.agent,
|
|
119
|
-
message: `${cmd.agent} process started successfully`,
|
|
120
|
-
};
|
|
121
|
-
}
|
|
122
|
-
catch (error) {
|
|
123
|
-
this.removeFileIfExists(stdoutPath);
|
|
124
|
-
this.removeFileIfExists(stderrPath);
|
|
125
|
-
throw error;
|
|
126
|
-
}
|
|
127
|
-
finally {
|
|
128
|
-
if (stdoutFd !== undefined) {
|
|
129
|
-
closeSync(stdoutFd);
|
|
130
|
-
}
|
|
131
|
-
if (stderrFd !== undefined) {
|
|
132
|
-
closeSync(stderrFd);
|
|
133
|
-
}
|
|
134
|
-
}
|
|
77
|
+
return this.startDetachedTrackedProcess(cmd, options.model);
|
|
135
78
|
}
|
|
136
79
|
async listProcesses() {
|
|
137
80
|
return this.readAllProcesses().map((process) => ({
|
|
@@ -268,7 +211,16 @@ export class CliProcessService {
|
|
|
268
211
|
message: 'Signal sent but process is still running',
|
|
269
212
|
};
|
|
270
213
|
}
|
|
271
|
-
|
|
214
|
+
const exitStatus = this.readExitStatus(refreshed);
|
|
215
|
+
if (exitStatus) {
|
|
216
|
+
refreshed.status = exitStatus.status;
|
|
217
|
+
refreshed.exitCode = exitStatus.exitCode;
|
|
218
|
+
}
|
|
219
|
+
else {
|
|
220
|
+
refreshed.status = 'failed';
|
|
221
|
+
refreshed.exitCode = SIGTERM_EXIT_CODE;
|
|
222
|
+
this.writeExitStatus(refreshed, { status: 'failed', exitCode: SIGTERM_EXIT_CODE });
|
|
223
|
+
}
|
|
272
224
|
this.writeProcess(refreshed);
|
|
273
225
|
return {
|
|
274
226
|
pid,
|
|
@@ -295,9 +247,9 @@ export class CliProcessService {
|
|
|
295
247
|
message: `Removed ${removed} processes`,
|
|
296
248
|
};
|
|
297
249
|
}
|
|
298
|
-
async
|
|
250
|
+
async startDetachedTrackedProcess(cmd, model) {
|
|
299
251
|
const cwdKey = this.resolveCwdKey(cmd.cwd);
|
|
300
|
-
const wrapperPath = this.
|
|
252
|
+
const wrapperPath = this.ensureDetachedWrapperScript();
|
|
301
253
|
const childProcess = spawn(wrapperPath, [this.stateDir, cwdKey, cmd.cliPath, ...cmd.args], {
|
|
302
254
|
cwd: cmd.cwd,
|
|
303
255
|
detached: true,
|
|
@@ -312,12 +264,8 @@ export class CliProcessService {
|
|
|
312
264
|
mkdirSync(processDir, { recursive: true });
|
|
313
265
|
const stdoutPath = this.resolveStdoutPath(processDir);
|
|
314
266
|
const stderrPath = this.resolveStderrPath(processDir);
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
}
|
|
318
|
-
if (!existsSync(stderrPath)) {
|
|
319
|
-
writeFileSync(stderrPath, '');
|
|
320
|
-
}
|
|
267
|
+
this.touchFile(stdoutPath);
|
|
268
|
+
this.touchFile(stderrPath);
|
|
321
269
|
const storedProcess = {
|
|
322
270
|
pid,
|
|
323
271
|
prompt: cmd.prompt,
|
|
@@ -386,15 +334,13 @@ export class CliProcessService {
|
|
|
386
334
|
return process;
|
|
387
335
|
}
|
|
388
336
|
if (!isProcessRunning(process.pid)) {
|
|
389
|
-
process.status = '
|
|
337
|
+
process.status = 'failed';
|
|
338
|
+
this.appendTextFileSafe(process.stderrPath, '\nProcess exited without exit-status metadata; marking as failed.\n');
|
|
390
339
|
this.writeProcess(process);
|
|
391
340
|
}
|
|
392
341
|
return process;
|
|
393
342
|
}
|
|
394
343
|
readExitStatus(process) {
|
|
395
|
-
if (process.toolType !== 'opencode') {
|
|
396
|
-
return null;
|
|
397
|
-
}
|
|
398
344
|
const exitMetaPath = this.resolveExitStatusPath(this.resolveStoredProcessDir(process));
|
|
399
345
|
if (!existsSync(exitMetaPath)) {
|
|
400
346
|
return null;
|
|
@@ -410,12 +356,28 @@ export class CliProcessService {
|
|
|
410
356
|
}
|
|
411
357
|
return null;
|
|
412
358
|
}
|
|
359
|
+
writeExitStatus(process, exitStatus) {
|
|
360
|
+
const exitStatusPath = this.resolveExitStatusPath(this.resolveStoredProcessDir(process));
|
|
361
|
+
const tempPath = `${exitStatusPath}.${process.pid}.${Date.now()}.tmp`;
|
|
362
|
+
writeFileSync(tempPath, JSON.stringify(exitStatus, null, 2) + '\n');
|
|
363
|
+
renameSync(tempPath, exitStatusPath);
|
|
364
|
+
}
|
|
413
365
|
readTextFileSafe(filePath) {
|
|
414
366
|
if (!existsSync(filePath)) {
|
|
415
367
|
return '';
|
|
416
368
|
}
|
|
417
369
|
return readFileSync(filePath, 'utf-8');
|
|
418
370
|
}
|
|
371
|
+
touchFile(filePath) {
|
|
372
|
+
closeSync(openSync(filePath, 'a'));
|
|
373
|
+
}
|
|
374
|
+
appendTextFileSafe(filePath, text) {
|
|
375
|
+
try {
|
|
376
|
+
appendFileSync(filePath, text);
|
|
377
|
+
}
|
|
378
|
+
catch {
|
|
379
|
+
}
|
|
380
|
+
}
|
|
419
381
|
fileSizeSafe(filePath) {
|
|
420
382
|
if (!existsSync(filePath)) {
|
|
421
383
|
return 0;
|
|
@@ -471,17 +433,12 @@ export class CliProcessService {
|
|
|
471
433
|
resolveExitStatusPath(processDir) {
|
|
472
434
|
return join(processDir, 'exit-status.json');
|
|
473
435
|
}
|
|
474
|
-
|
|
475
|
-
return join(this.stateDir, '
|
|
476
|
-
}
|
|
477
|
-
resolveStdoutPathForPidPlaceholder() {
|
|
478
|
-
return join(this.stateDir, `pending-${Date.now()}-${Math.random().toString(36).slice(2)}.stdout.log`);
|
|
436
|
+
resolveDetachedWrapperPath() {
|
|
437
|
+
return join(this.stateDir, 'detached-runner-v2.sh');
|
|
479
438
|
}
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
ensureOpenCodeWrapperScript() {
|
|
484
|
-
const wrapperPath = this.resolveOpenCodeWrapperPath();
|
|
439
|
+
ensureDetachedWrapperScript() {
|
|
440
|
+
const wrapperPath = this.resolveDetachedWrapperPath();
|
|
441
|
+
this.removeLegacyDetachedWrappers();
|
|
485
442
|
if (existsSync(wrapperPath)) {
|
|
486
443
|
return wrapperPath;
|
|
487
444
|
}
|
|
@@ -498,24 +455,52 @@ exit_meta_path="$process_dir/exit-status.json"
|
|
|
498
455
|
mkdir -p "$process_dir"
|
|
499
456
|
: > "$stdout_path"
|
|
500
457
|
: > "$stderr_path"
|
|
501
|
-
|
|
458
|
+
write_exit_status() {
|
|
459
|
+
status="$1"
|
|
460
|
+
exit_code="$2"
|
|
461
|
+
tmp_exit_meta_path="$exit_meta_path.$$"
|
|
462
|
+
printf '{\n "status": "%s",\n "exitCode": %s\n}\n' "$status" "$exit_code" > "$tmp_exit_meta_path"
|
|
463
|
+
mv "$tmp_exit_meta_path" "$exit_meta_path"
|
|
464
|
+
}
|
|
465
|
+
handle_signal() {
|
|
466
|
+
signal="$1"
|
|
467
|
+
exit_code="$2"
|
|
468
|
+
if [ -n "\${child_pid:-}" ]; then
|
|
469
|
+
kill "-$signal" "$child_pid" 2>/dev/null || true
|
|
470
|
+
wait "$child_pid" 2>/dev/null
|
|
471
|
+
fi
|
|
472
|
+
write_exit_status "failed" "$exit_code"
|
|
473
|
+
exit "$exit_code"
|
|
474
|
+
}
|
|
475
|
+
trap 'handle_signal TERM 143' TERM
|
|
476
|
+
trap 'handle_signal INT 130' INT
|
|
477
|
+
trap 'handle_signal HUP 129' HUP
|
|
478
|
+
"$@" >> "$stdout_path" 2>> "$stderr_path" &
|
|
479
|
+
child_pid="$!"
|
|
480
|
+
wait "$child_pid"
|
|
502
481
|
exit_code="$?"
|
|
482
|
+
trap - TERM INT HUP
|
|
503
483
|
status="completed"
|
|
504
484
|
if [ "$exit_code" -ne 0 ]; then
|
|
505
485
|
status="failed"
|
|
506
486
|
fi
|
|
507
|
-
|
|
487
|
+
write_exit_status "$status" "$exit_code"
|
|
508
488
|
exit "$exit_code"
|
|
509
489
|
`);
|
|
510
490
|
chmodSync(wrapperPath, 0o755);
|
|
511
491
|
return wrapperPath;
|
|
512
492
|
}
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
493
|
+
removeLegacyDetachedWrappers() {
|
|
494
|
+
for (const fileName of ['detached-runner-v1.sh']) {
|
|
495
|
+
const legacyPath = join(this.stateDir, fileName);
|
|
496
|
+
if (!existsSync(legacyPath)) {
|
|
497
|
+
continue;
|
|
498
|
+
}
|
|
499
|
+
try {
|
|
500
|
+
rmSync(legacyPath, { force: true });
|
|
501
|
+
}
|
|
502
|
+
catch {
|
|
503
|
+
}
|
|
519
504
|
}
|
|
520
505
|
}
|
|
521
506
|
killPidOrGroup(pid, signal) {
|
package/dist/cli-utils.js
CHANGED
|
@@ -153,6 +153,12 @@ function getCliBinaryStatus(name) {
|
|
|
153
153
|
}
|
|
154
154
|
export function getCliDoctorStatus() {
|
|
155
155
|
return {
|
|
156
|
+
checks: {
|
|
157
|
+
binaryAvailability: true,
|
|
158
|
+
pathResolution: true,
|
|
159
|
+
loginState: false,
|
|
160
|
+
termsAcceptance: false,
|
|
161
|
+
},
|
|
156
162
|
claude: getCliBinaryStatus('claude'),
|
|
157
163
|
codex: getCliBinaryStatus('codex'),
|
|
158
164
|
gemini: getCliBinaryStatus('gemini'),
|
package/dist/cli.js
CHANGED
|
@@ -40,7 +40,7 @@ Options:
|
|
|
40
40
|
--prompt Prompt string (mutually exclusive with --prompt_file)
|
|
41
41
|
--prompt_file Path to a file containing the prompt
|
|
42
42
|
--session_id Session ID to resume, including OpenCode in-place resumes
|
|
43
|
-
--reasoning_effort Claude/Codex only: Claude=low|medium|high, Codex=low|medium|high|xhigh; unsupported for Gemini, Forge, and OpenCode
|
|
43
|
+
--reasoning_effort Claude/Codex only: Claude=low|medium|high|xhigh|max, Codex=low|medium|high|xhigh; unsupported for Gemini, Forge, and OpenCode
|
|
44
44
|
--help Show this help message
|
|
45
45
|
|
|
46
46
|
Raw CLI output goes to stdout. Use cli.run.parse to parse the output:
|
package/dist/model-catalog.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export const CLAUDE_MODELS = ['sonnet', 'sonnet[1m]', 'opus', 'opusplan', 'haiku'];
|
|
2
2
|
export const CODEX_MODELS = [
|
|
3
|
+
'codex',
|
|
3
4
|
'gpt-5.4',
|
|
4
5
|
'gpt-5.3-codex',
|
|
5
6
|
'gpt-5.2-codex',
|
|
@@ -27,7 +28,7 @@ export const MODEL_ALIASES = {
|
|
|
27
28
|
'gemini-ultra': 'gemini-3.1-pro-preview',
|
|
28
29
|
};
|
|
29
30
|
export const MODEL_ALIAS_DETAILS = [
|
|
30
|
-
{ name: 'claude-ultra', resolvesTo: 'opus', agent: 'claude', defaultReasoningEffort: '
|
|
31
|
+
{ name: 'claude-ultra', resolvesTo: 'opus', agent: 'claude', defaultReasoningEffort: 'max' },
|
|
31
32
|
{ name: 'codex-ultra', resolvesTo: 'gpt-5.4', agent: 'codex', defaultReasoningEffort: 'xhigh' },
|
|
32
33
|
{ name: 'gemini-ultra', resolvesTo: 'gemini-3.1-pro-preview', agent: 'gemini' },
|
|
33
34
|
];
|
|
@@ -43,7 +44,7 @@ export function getSupportedModelsDescription() {
|
|
|
43
44
|
].join(', ');
|
|
44
45
|
}
|
|
45
46
|
export function getModelParameterDescription() {
|
|
46
|
-
return `The model to use. Aliases: "claude-ultra" (auto
|
|
47
|
+
return `The model to use. Aliases: "claude-ultra" (auto max effort), "codex-ultra" (auto xhigh reasoning), "gemini-ultra". Standard: ${[...CLAUDE_MODELS, ...CODEX_MODELS, ...GEMINI_MODELS, ...FORGE_MODELS, ...OPENCODE_MODELS].map((model) => `"${model}"`).join(', ')}. OpenCode also accepts explicit dynamic models using "oc-<provider/model>". "forge" is a provider key, not a Forge model family selector.`;
|
|
47
48
|
}
|
|
48
49
|
export function getModelsPayload() {
|
|
49
50
|
return {
|
package/dist/parsers.js
CHANGED
|
@@ -528,6 +528,7 @@ export function parseClaudeOutput(stdout) {
|
|
|
528
528
|
try {
|
|
529
529
|
const lines = stdout.trim().split('\n');
|
|
530
530
|
let lastMessage = null;
|
|
531
|
+
let assistantTextBuffer = '';
|
|
531
532
|
let sessionId = null;
|
|
532
533
|
const toolsMap = new Map();
|
|
533
534
|
for (const line of lines) {
|
|
@@ -543,6 +544,9 @@ export function parseClaudeOutput(stdout) {
|
|
|
543
544
|
}
|
|
544
545
|
if (parsed.type === 'assistant' && parsed.message?.content) {
|
|
545
546
|
for (const content of parsed.message.content) {
|
|
547
|
+
if (content.type === 'text' && typeof content.text === 'string') {
|
|
548
|
+
assistantTextBuffer += content.text;
|
|
549
|
+
}
|
|
546
550
|
if (content.type === 'tool_use') {
|
|
547
551
|
toolsMap.set(content.id, {
|
|
548
552
|
tool: content.name,
|
|
@@ -574,9 +578,11 @@ export function parseClaudeOutput(stdout) {
|
|
|
574
578
|
}
|
|
575
579
|
}
|
|
576
580
|
const tools = Array.from(toolsMap.values());
|
|
577
|
-
|
|
581
|
+
const fallbackMessage = assistantTextBuffer.trim() ? assistantTextBuffer : null;
|
|
582
|
+
const message = lastMessage || fallbackMessage;
|
|
583
|
+
if (message || sessionId || tools.length > 0) {
|
|
578
584
|
return {
|
|
579
|
-
message
|
|
585
|
+
message,
|
|
580
586
|
session_id: sessionId,
|
|
581
587
|
tools: tools.length > 0 ? tools : undefined
|
|
582
588
|
};
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ai-cli-mcp",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.20.1",
|
|
4
4
|
"mcpName": "io.github.mkXultra/ai-cli-mcp",
|
|
5
|
-
"description": "
|
|
5
|
+
"description": "Run Claude, Codex, Gemini, Forge, and OpenCode CLIs through MCP with background jobs",
|
|
6
6
|
"author": "mkXultra",
|
|
7
7
|
"license": "MIT",
|
|
8
8
|
"main": "dist/server.js",
|
|
@@ -10,14 +10,38 @@
|
|
|
10
10
|
"ai-cli": "dist/bin/ai-cli.js",
|
|
11
11
|
"ai-cli-mcp": "dist/bin/ai-cli-mcp.js"
|
|
12
12
|
},
|
|
13
|
+
"files": [
|
|
14
|
+
"dist/app",
|
|
15
|
+
"dist/bin",
|
|
16
|
+
"dist/cli-builder.js",
|
|
17
|
+
"dist/cli-parse.js",
|
|
18
|
+
"dist/cli-process-service.js",
|
|
19
|
+
"dist/cli-utils.js",
|
|
20
|
+
"dist/cli.js",
|
|
21
|
+
"dist/model-catalog.js",
|
|
22
|
+
"dist/parsers.js",
|
|
23
|
+
"dist/peek.js",
|
|
24
|
+
"dist/process-result.js",
|
|
25
|
+
"dist/process-service.js",
|
|
26
|
+
"dist/server.js",
|
|
27
|
+
"server.json",
|
|
28
|
+
"README.ja.md",
|
|
29
|
+
"CHANGELOG.md"
|
|
30
|
+
],
|
|
13
31
|
"scripts": {
|
|
14
32
|
"build": "tsc",
|
|
15
33
|
"start": "node dist/server.js",
|
|
16
34
|
"dev": "tsx src/server.ts",
|
|
17
|
-
"test": "npm run build &&
|
|
35
|
+
"test": "npm run build && npm run test:run",
|
|
36
|
+
"test:run": "vitest run",
|
|
18
37
|
"test:unit": "vitest run --config vitest.config.unit.ts",
|
|
19
38
|
"test:e2e": "npm run build && vitest run --config vitest.config.e2e.ts",
|
|
20
39
|
"test:e2e:local": "npm run build && vitest run --config vitest.config.e2e.ts",
|
|
40
|
+
"test:live": "npm run build && npm run test:live:run",
|
|
41
|
+
"test:live:run": "vitest run --config vitest.config.live.ts",
|
|
42
|
+
"test:package": "npm run build && npm run test:package:run",
|
|
43
|
+
"test:package:run": "vitest run --config vitest.config.package.ts",
|
|
44
|
+
"test:release": "npm run build && npm run test:run && npm run test:live:run && npm run test:package:run",
|
|
21
45
|
"test:coverage": "npm run build && vitest --coverage --config vitest.config.unit.ts",
|
|
22
46
|
"test:watch": "vitest --watch",
|
|
23
47
|
"cli.run": "tsx src/cli.ts",
|
package/server.json
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
|
|
3
3
|
"name": "io.github.mkXultra/ai-cli-mcp",
|
|
4
|
-
"description": "
|
|
4
|
+
"description": "Run Claude, Codex, Gemini, Forge, and OpenCode CLIs through MCP with background jobs",
|
|
5
5
|
"repository": {
|
|
6
6
|
"url": "https://github.com/mkXultra/ai-cli-mcp",
|
|
7
7
|
"source": "github"
|
|
8
8
|
},
|
|
9
|
-
"version": "2.
|
|
9
|
+
"version": "2.20.1",
|
|
10
10
|
"packages": [
|
|
11
11
|
{
|
|
12
12
|
"registryType": "npm",
|
|
13
13
|
"identifier": "ai-cli-mcp",
|
|
14
|
-
"version": "2.
|
|
14
|
+
"version": "2.20.1",
|
|
15
15
|
"transport": {
|
|
16
16
|
"type": "stdio"
|
|
17
17
|
}
|
package/.gemini/settings.json
DELETED
package/.github/dependabot.yml
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
version: 2
|
|
2
|
-
updates:
|
|
3
|
-
- package-ecosystem: "npm"
|
|
4
|
-
directory: "/"
|
|
5
|
-
target-branch: "develop"
|
|
6
|
-
schedule:
|
|
7
|
-
interval: "weekly"
|
|
8
|
-
open-pull-requests-limit: 5
|
|
9
|
-
groups:
|
|
10
|
-
npm-production:
|
|
11
|
-
dependency-type: "production"
|
|
12
|
-
patterns:
|
|
13
|
-
- "*"
|
|
14
|
-
npm-development:
|
|
15
|
-
dependency-type: "development"
|
|
16
|
-
patterns:
|
|
17
|
-
- "*"
|
|
18
|
-
|
|
19
|
-
- package-ecosystem: "github-actions"
|
|
20
|
-
directory: "/"
|
|
21
|
-
target-branch: "develop"
|
|
22
|
-
schedule:
|
|
23
|
-
interval: "weekly"
|
|
24
|
-
open-pull-requests-limit: 2
|
|
25
|
-
groups:
|
|
26
|
-
github-actions:
|
|
27
|
-
patterns:
|
|
28
|
-
- "*"
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
## Summary
|
|
2
|
-
|
|
3
|
-
<!-- Briefly describe what this PR does -->
|
|
4
|
-
|
|
5
|
-
## Type of Change
|
|
6
|
-
|
|
7
|
-
- [ ] feat: New feature
|
|
8
|
-
- [ ] fix: Bug fix
|
|
9
|
-
- [ ] refactor: Refactoring
|
|
10
|
-
- [ ] docs: Documentation
|
|
11
|
-
- [ ] test: Add or update tests
|
|
12
|
-
- [ ] chore: Build, CI, dependencies, etc.
|
|
13
|
-
|
|
14
|
-
## Changes
|
|
15
|
-
|
|
16
|
-
<!-- List the main changes -->
|
|
17
|
-
|
|
18
|
-
-
|
|
19
|
-
|
|
20
|
-
## Test Plan
|
|
21
|
-
|
|
22
|
-
- [ ] `npm run test:unit` passes
|
|
23
|
-
- [ ] `npm run build` passes
|
|
24
|
-
- [ ] Manual testing (if applicable)
|
|
25
|
-
|
|
26
|
-
## Notes
|
|
27
|
-
|
|
28
|
-
<!-- Any additional context for reviewers -->
|
package/.github/workflows/ci.yml
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
name: Node.js CI
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
push:
|
|
5
|
-
branches: [ develop ]
|
|
6
|
-
pull_request:
|
|
7
|
-
branches: [ develop ]
|
|
8
|
-
|
|
9
|
-
jobs:
|
|
10
|
-
build:
|
|
11
|
-
runs-on: ubuntu-latest
|
|
12
|
-
|
|
13
|
-
strategy:
|
|
14
|
-
matrix:
|
|
15
|
-
node-version: [20.19.0, 22.12.0, 24.x]
|
|
16
|
-
|
|
17
|
-
steps:
|
|
18
|
-
- name: Checkout repository
|
|
19
|
-
uses: actions/checkout@v4
|
|
20
|
-
|
|
21
|
-
- name: Set up Node.js ${{ matrix.node-version }}
|
|
22
|
-
uses: actions/setup-node@v4
|
|
23
|
-
with:
|
|
24
|
-
node-version: ${{ matrix.node-version }}
|
|
25
|
-
cache: 'npm'
|
|
26
|
-
|
|
27
|
-
- name: Install dependencies
|
|
28
|
-
run: npm ci
|
|
29
|
-
|
|
30
|
-
- name: Build project
|
|
31
|
-
run: npm run build
|
|
32
|
-
|
|
33
|
-
- name: Run unit tests
|
|
34
|
-
run: npm run test:unit
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
name: Dependency Review
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
pull_request:
|
|
5
|
-
branches:
|
|
6
|
-
- develop
|
|
7
|
-
|
|
8
|
-
permissions:
|
|
9
|
-
contents: read
|
|
10
|
-
|
|
11
|
-
jobs:
|
|
12
|
-
dependency-review:
|
|
13
|
-
runs-on: ubuntu-latest
|
|
14
|
-
steps:
|
|
15
|
-
- name: Checkout
|
|
16
|
-
uses: actions/checkout@v4
|
|
17
|
-
|
|
18
|
-
- name: Dependency Review
|
|
19
|
-
uses: actions/dependency-review-action@v4
|
|
20
|
-
with:
|
|
21
|
-
fail-on-severity: moderate
|
|
22
|
-
fail-on-scopes: runtime
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
name: Release
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
push:
|
|
5
|
-
branches:
|
|
6
|
-
- develop
|
|
7
|
-
|
|
8
|
-
permissions:
|
|
9
|
-
contents: read
|
|
10
|
-
|
|
11
|
-
jobs:
|
|
12
|
-
release:
|
|
13
|
-
name: Release
|
|
14
|
-
runs-on: ubuntu-latest
|
|
15
|
-
permissions:
|
|
16
|
-
contents: write # to create release and push changelog
|
|
17
|
-
issues: write # to comment on released issues
|
|
18
|
-
pull-requests: write # to comment on released PRs
|
|
19
|
-
id-token: write # for OIDC trusted publishing
|
|
20
|
-
steps:
|
|
21
|
-
- name: Checkout
|
|
22
|
-
uses: actions/checkout@v4
|
|
23
|
-
with:
|
|
24
|
-
fetch-depth: 0
|
|
25
|
-
fetch-tags: true
|
|
26
|
-
persist-credentials: false
|
|
27
|
-
|
|
28
|
-
- name: Setup Node.js
|
|
29
|
-
uses: actions/setup-node@v4
|
|
30
|
-
with:
|
|
31
|
-
node-version: '24'
|
|
32
|
-
registry-url: 'https://registry.npmjs.org'
|
|
33
|
-
|
|
34
|
-
- name: Install dependencies
|
|
35
|
-
run: npm clean-install
|
|
36
|
-
|
|
37
|
-
- name: Build
|
|
38
|
-
run: npm run build
|
|
39
|
-
|
|
40
|
-
- name: Run tests
|
|
41
|
-
run: npm test
|
|
42
|
-
|
|
43
|
-
- name: Verify provenance attestations
|
|
44
|
-
run: npm audit signatures
|
|
45
|
-
|
|
46
|
-
- name: Capture version before release
|
|
47
|
-
id: release_before
|
|
48
|
-
run: |
|
|
49
|
-
VERSION=$(node -p 'require("./package.json").version')
|
|
50
|
-
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
|
|
51
|
-
|
|
52
|
-
- name: Release
|
|
53
|
-
env:
|
|
54
|
-
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
55
|
-
run: npx semantic-release
|
|
56
|
-
|
|
57
|
-
- name: Capture version after release
|
|
58
|
-
id: release_after
|
|
59
|
-
run: |
|
|
60
|
-
VERSION=$(node -p 'require("./package.json").version')
|
|
61
|
-
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
|
|
62
|
-
|
|
63
|
-
- name: Detect whether a release was published
|
|
64
|
-
id: release_status
|
|
65
|
-
run: |
|
|
66
|
-
if [ "${{ steps.release_before.outputs.version }}" != "${{ steps.release_after.outputs.version }}" ]; then
|
|
67
|
-
echo "published=true" >> "$GITHUB_OUTPUT"
|
|
68
|
-
else
|
|
69
|
-
echo "published=false" >> "$GITHUB_OUTPUT"
|
|
70
|
-
fi
|
|
71
|
-
|
|
72
|
-
- name: Install mcp-publisher
|
|
73
|
-
if: steps.release_status.outputs.published == 'true'
|
|
74
|
-
run: |
|
|
75
|
-
curl -L "https://github.com/modelcontextprotocol/registry/releases/latest/download/mcp-publisher_$(uname -s | tr '[:upper:]' '[:lower:]')_$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/').tar.gz" | tar xz mcp-publisher
|
|
76
|
-
|
|
77
|
-
- name: Authenticate to MCP Registry
|
|
78
|
-
if: steps.release_status.outputs.published == 'true'
|
|
79
|
-
run: ./mcp-publisher login github-oidc
|
|
80
|
-
|
|
81
|
-
- name: Set version in server.json
|
|
82
|
-
if: steps.release_status.outputs.published == 'true'
|
|
83
|
-
run: |
|
|
84
|
-
VERSION=$(node -p "require('./package.json').version")
|
|
85
|
-
jq --arg v "$VERSION" '.version = $v | .packages[0].version = $v' server.json > server.tmp && mv server.tmp server.json
|
|
86
|
-
|
|
87
|
-
- name: Publish to MCP Registry
|
|
88
|
-
if: steps.release_status.outputs.published == 'true'
|
|
89
|
-
run: ./mcp-publisher publish
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
name: test
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
pull_request:
|
|
5
|
-
|
|
6
|
-
jobs:
|
|
7
|
-
test:
|
|
8
|
-
runs-on: ubuntu-latest
|
|
9
|
-
steps:
|
|
10
|
-
- name: Checkout
|
|
11
|
-
uses: actions/checkout@v4
|
|
12
|
-
- name: Setup Node
|
|
13
|
-
uses: actions/setup-node@v4
|
|
14
|
-
with:
|
|
15
|
-
node-version: '20'
|
|
16
|
-
cache: 'npm'
|
|
17
|
-
- name: Install
|
|
18
|
-
run: npm ci
|
|
19
|
-
- name: Test
|
|
20
|
-
run: npm test
|