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,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, unlinkSync, writeFileSync, } from 'node:fs';
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
- if (cmd.agent === 'opencode') {
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
- refreshed.status = 'failed';
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 startDetachedOpenCodeProcess(cmd, model) {
250
+ async startDetachedTrackedProcess(cmd, model) {
299
251
  const cwdKey = this.resolveCwdKey(cmd.cwd);
300
- const wrapperPath = this.ensureOpenCodeWrapperScript();
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
- if (!existsSync(stdoutPath)) {
316
- writeFileSync(stdoutPath, '');
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 = 'completed';
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
- resolveOpenCodeWrapperPath() {
475
- return join(this.stateDir, 'opencode-detached-wrapper.sh');
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
- resolveStderrPathForPidPlaceholder() {
481
- return join(this.stateDir, `pending-${Date.now()}-${Math.random().toString(36).slice(2)}.stderr.log`);
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
- "$@" >> "$stdout_path" 2>> "$stderr_path"
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
- printf '{\n "status": "%s",\n "exitCode": %s\n}\n' "$status" "$exit_code" > "$exit_meta_path"
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
- renamePlaceholderFile(fromPath, toPath) {
514
- renameSync(fromPath, toPath);
515
- }
516
- removeFileIfExists(filePath) {
517
- if (existsSync(filePath)) {
518
- unlinkSync(filePath);
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:
@@ -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: 'high' },
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 high 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
+ 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
- if (lastMessage || sessionId || tools.length > 0) {
581
+ const fallbackMessage = assistantTextBuffer.trim() ? assistantTextBuffer : null;
582
+ const message = lastMessage || fallbackMessage;
583
+ if (message || sessionId || tools.length > 0) {
578
584
  return {
579
- message: lastMessage,
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.19.0",
3
+ "version": "2.20.1",
4
4
  "mcpName": "io.github.mkXultra/ai-cli-mcp",
5
- "description": "MCP server for AI CLI tools (Claude, Codex, Gemini, Forge, and OpenCode) with background process management",
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 && vitest run",
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": "MCP server for AI CLI tools (Claude, Codex, Gemini, and Forge) with background process management",
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.8.2",
9
+ "version": "2.20.1",
10
10
  "packages": [
11
11
  {
12
12
  "registryType": "npm",
13
13
  "identifier": "ai-cli-mcp",
14
- "version": "2.8.2",
14
+ "version": "2.20.1",
15
15
  "transport": {
16
16
  "type": "stdio"
17
17
  }
@@ -1,11 +0,0 @@
1
- {
2
- "mcpServers": {
3
- "acm-dev": {
4
- "command": "npm",
5
- "args": [
6
- "run",
7
- "dev"
8
- ]
9
- }
10
- }
11
- }
@@ -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 -->
@@ -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