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
@@ -1,13 +0,0 @@
1
- // Global test setup
2
- import { beforeAll, afterAll } from 'vitest';
3
- import { getSharedMock, cleanupSharedMock } from './utils/persistent-mock.js';
4
-
5
- beforeAll(async () => {
6
- console.error('[TEST SETUP] Creating shared mock for all tests...');
7
- await getSharedMock();
8
- });
9
-
10
- afterAll(async () => {
11
- console.error('[TEST SETUP] Cleaning up shared mock...');
12
- await cleanupSharedMock();
13
- });
@@ -1,87 +0,0 @@
1
- import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
2
- import { join, dirname } from 'node:path';
3
-
4
- /**
5
- * Mock Claude CLI for testing
6
- * This creates a fake Claude CLI that can be used during testing
7
- */
8
- export class ClaudeMock {
9
- private mockPath: string;
10
- private responses = new Map<string, string>();
11
-
12
- constructor(binaryName: string = 'claude') {
13
- // Always use /tmp directory for mocks in tests
14
- this.mockPath = join('/tmp', 'claude-code-test-mock', binaryName);
15
- }
16
-
17
- /**
18
- * Setup the mock Claude CLI
19
- */
20
- async setup(): Promise<void> {
21
- const dir = dirname(this.mockPath);
22
- if (!existsSync(dir)) {
23
- mkdirSync(dir, { recursive: true });
24
- }
25
-
26
- // Create a simple bash script that echoes responses
27
- const mockScript = `#!/bin/bash
28
- # Mock Claude CLI for testing
29
-
30
- # Extract the prompt from arguments
31
- prompt=""
32
- verbose=false
33
- while [[ $# -gt 0 ]]; do
34
- case $1 in
35
- -p|--prompt)
36
- prompt="$2"
37
- shift 2
38
- ;;
39
- --verbose)
40
- verbose=true
41
- shift
42
- ;;
43
- --yes|-y|--dangerously-skip-permissions)
44
- shift
45
- ;;
46
- *)
47
- shift
48
- ;;
49
- esac
50
- done
51
-
52
- # Mock responses based on prompt
53
- if [[ "$prompt" == *"create"* ]]; then
54
- echo "Created file successfully"
55
- elif [[ "$prompt" == *"Create"* ]]; then
56
- echo "Created file successfully"
57
- elif [[ "$prompt" == *"git"* ]] && [[ "$prompt" == *"commit"* ]]; then
58
- echo "Committed changes successfully"
59
- elif [[ "$prompt" == *"error"* ]]; then
60
- echo "Error: Mock error response" >&2
61
- exit 1
62
- else
63
- echo "Command executed successfully"
64
- fi
65
- `;
66
-
67
- writeFileSync(this.mockPath, mockScript);
68
- // Make executable
69
- const { chmod } = await import('node:fs/promises');
70
- await chmod(this.mockPath, 0o755);
71
- }
72
-
73
- /**
74
- * Cleanup the mock Claude CLI
75
- */
76
- async cleanup(): Promise<void> {
77
- const { rm } = await import('node:fs/promises');
78
- await rm(this.mockPath, { force: true });
79
- }
80
-
81
- /**
82
- * Add a mock response for a specific prompt pattern
83
- */
84
- addResponse(pattern: string, response: string): void {
85
- this.responses.set(pattern, response);
86
- }
87
- }
@@ -1,159 +0,0 @@
1
- import { spawn, ChildProcess } from 'node:child_process';
2
- import { EventEmitter } from 'node:events';
3
-
4
- export interface MCPResponse {
5
- jsonrpc: string;
6
- id: number;
7
- result?: any;
8
- error?: {
9
- code: number;
10
- message: string;
11
- data?: any;
12
- };
13
- }
14
-
15
- /**
16
- * Mock MCP client for testing the server
17
- */
18
- export class MCPTestClient extends EventEmitter {
19
- private server: ChildProcess | null = null;
20
- private requestId = 0;
21
- private pendingRequests = new Map<number, {
22
- resolve: (response: MCPResponse) => void;
23
- reject: (error: Error) => void;
24
- }>();
25
- private buffer = '';
26
-
27
- constructor(private serverPath: string, private env: Record<string, string> = {}) {
28
- super();
29
- }
30
-
31
- async connect(): Promise<void> {
32
- return new Promise((resolve, reject) => {
33
- this.server = spawn('node', [this.serverPath], {
34
- env: { ...process.env, ...this.env },
35
- stdio: ['pipe', 'pipe', 'pipe'],
36
- });
37
-
38
- this.server.stdout?.on('data', (data) => {
39
- this.handleData(data.toString());
40
- });
41
-
42
- this.server.stderr?.on('data', (data) => {
43
- console.error('Server stderr:', data.toString());
44
- });
45
-
46
- this.server.on('error', (error) => {
47
- reject(error);
48
- });
49
-
50
- this.server.on('spawn', () => {
51
- resolve();
52
- });
53
- });
54
- }
55
-
56
- async disconnect(): Promise<void> {
57
- if (this.server) {
58
- this.server.kill();
59
- await new Promise((resolve) => {
60
- this.server!.on('exit', resolve);
61
- });
62
- this.server = null;
63
- }
64
- }
65
-
66
- private handleData(data: string): void {
67
- this.buffer += data;
68
- const lines = this.buffer.split('\n');
69
- this.buffer = lines.pop() || '';
70
-
71
- for (const line of lines) {
72
- if (!line.trim()) continue;
73
- try {
74
- const response = JSON.parse(line);
75
- if (response.id && this.pendingRequests.has(response.id)) {
76
- const pending = this.pendingRequests.get(response.id)!;
77
- this.pendingRequests.delete(response.id);
78
- pending.resolve(response);
79
- } else {
80
- this.emit('notification', response);
81
- }
82
- } catch (error) {
83
- console.error('Failed to parse response:', line, error);
84
- }
85
- }
86
- }
87
-
88
- async sendRequest(method: string, params?: any): Promise<any> {
89
- const id = ++this.requestId;
90
- const request = {
91
- jsonrpc: '2.0',
92
- method,
93
- params,
94
- id,
95
- };
96
-
97
- return new Promise((resolve, reject) => {
98
- this.pendingRequests.set(id, { resolve, reject });
99
-
100
- this.server?.stdin?.write(JSON.stringify(request) + '\n');
101
-
102
- // Timeout after 30 seconds
103
- setTimeout(() => {
104
- if (this.pendingRequests.has(id)) {
105
- this.pendingRequests.delete(id);
106
- reject(new Error(`Request ${id} timed out`));
107
- }
108
- }, 30000);
109
- });
110
- }
111
-
112
- async callTool(name: string, args: any): Promise<any> {
113
- const response = await this.sendRequest('tools/call', {
114
- name,
115
- arguments: args,
116
- });
117
-
118
- if (response.error) {
119
- throw new Error(`Tool call failed: ${response.error.message}`);
120
- }
121
-
122
- return response.result?.content;
123
- }
124
-
125
- async listTools(): Promise<any> {
126
- const response = await this.sendRequest('tools/list');
127
- return response.result?.tools || [];
128
- }
129
- }
130
-
131
- /**
132
- * Default server path
133
- */
134
- const DEFAULT_SERVER_PATH = 'dist/server.js';
135
-
136
- /**
137
- * Create a test client with standard configuration
138
- * Automatically unsets VITEST env so the server actually starts
139
- */
140
- export function createTestClient(options: {
141
- serverPath?: string;
142
- claudeCliName?: string;
143
- debug?: boolean;
144
- env?: Record<string, string>;
145
- } = {}): MCPTestClient {
146
- const {
147
- serverPath = DEFAULT_SERVER_PATH,
148
- claudeCliName = process.env.TEST_CLAUDE_CLI_NAME || '/tmp/claude-code-test-mock/claudeMocked',
149
- debug = true,
150
- env = {},
151
- } = options;
152
-
153
- return new MCPTestClient(serverPath, {
154
- VITEST: '', // Unset so server starts
155
- MCP_CLAUDE_DEBUG: debug ? 'true' : '',
156
- CLAUDE_CLI_NAME: claudeCliName,
157
- ...env,
158
- });
159
- }
@@ -1,108 +0,0 @@
1
- import { chmodSync, writeFileSync } from 'node:fs';
2
- import { join } from 'node:path';
3
-
4
- export interface OpenCodeMockOptions {
5
- argsLogPath?: string;
6
- defaultSessionId?: string;
7
- }
8
-
9
- export interface OpenCodeMockResult {
10
- scriptPath: string;
11
- argsLogPath?: string;
12
- }
13
-
14
- export function createOpenCodeMock(dir: string, options: OpenCodeMockOptions = {}): OpenCodeMockResult {
15
- const scriptPath = join(dir, 'mock-opencode');
16
- const defaultSessionId = options.defaultSessionId || 'ses-opencode-default';
17
- const argsLogPath = options.argsLogPath;
18
- const argsLogSection = argsLogPath
19
- ? `printf '%s\n' "$*" >> "${argsLogPath}"\n`
20
- : '';
21
-
22
- writeFileSync(
23
- scriptPath,
24
- `#!/bin/bash
25
- set -euo pipefail
26
-
27
- prompt=""
28
- session_id=""
29
- session_provided=0
30
- model=""
31
- work_dir=""
32
-
33
- ${argsLogSection}if [[ "\${1:-}" == "run" ]]; then
34
- shift
35
- fi
36
-
37
- while [[ $# -gt 0 ]]; do
38
- case "$1" in
39
- --format)
40
- shift 2
41
- ;;
42
- --dir)
43
- work_dir="$2"
44
- shift 2
45
- ;;
46
- --session)
47
- session_id="$2"
48
- session_provided=1
49
- shift 2
50
- ;;
51
- --model)
52
- model="$2"
53
- shift 2
54
- ;;
55
- *)
56
- prompt="$1"
57
- shift
58
- ;;
59
- esac
60
- done
61
-
62
- if [[ -z "$session_id" ]]; then
63
- session_id="${defaultSessionId}"
64
- fi
65
-
66
- if [[ "$prompt" == *"sleep"* ]]; then
67
- sleep 5
68
- fi
69
-
70
- if [[ "$prompt" == *"fail"* ]]; then
71
- printf '{"type":"step_start","sessionID":"%s"}\n' "$session_id"
72
- printf '{"type":"text","sessionID":"%s","part":{"type":"text","text":"Partial failure output"}}\n' "$session_id"
73
- printf '{"type":"step_finish","sessionID":"%s","part":{"type":"step-finish","tokens":{"total":42},"cost":0}}\n' "$session_id"
74
- printf 'OpenCode failed for %s in %s\n' "$model" "$work_dir" >&2
75
- exit 7
76
- fi
77
-
78
- if [[ "$prompt" == *"multi-step"* ]]; then
79
- printf '{"type":"step_start","sessionID":"%s"}\n' "$session_id"
80
- printf '{"type":"text","sessionID":"%s","part":{"type":"text","text":"First step"}}\n' "$session_id"
81
- printf '{"type":"step_finish","sessionID":"%s","part":{"type":"step-finish","tokens":{"total":11},"cost":0}}\n' "$session_id"
82
- printf '{"type":"step_start","sessionID":"%s"}\n' "$session_id"
83
- printf '{"type":"text","sessionID":"%s","part":{"type":"text","text":"Second step"}}\n' "$session_id"
84
- printf '{"type":"step_finish","sessionID":"%s","part":{"type":"step-finish","tokens":{"total":22},"cost":1}}\n' "$session_id"
85
- exit 0
86
- fi
87
-
88
- message_prefix="Initial"
89
- if [[ $session_provided -eq 1 ]]; then
90
- message_prefix="Resumed"
91
- fi
92
- if [[ -n "$model" ]]; then
93
- message_prefix="Model $model"
94
- if [[ $session_provided -eq 1 ]]; then
95
- message_prefix="Resumed model $model"
96
- fi
97
- fi
98
-
99
- printf '{"type":"step_start","sessionID":"%s"}\n' "$session_id"
100
- printf '{"type":"text","sessionID":"%s","part":{"type":"text","text":"%s: %s"}}\n' "$session_id" "$message_prefix" "$prompt"
101
- printf '{"type":"step_finish","sessionID":"%s","part":{"type":"step-finish","tokens":{"total":11833},"cost":0}}\n' "$session_id"
102
- `,
103
- 'utf8',
104
- );
105
- chmodSync(scriptPath, 0o755);
106
-
107
- return { scriptPath, argsLogPath };
108
- }
@@ -1,33 +0,0 @@
1
- import { ClaudeMock } from './claude-mock.js';
2
- import { existsSync } from 'node:fs';
3
- import { join } from 'node:path';
4
-
5
- let sharedMock: ClaudeMock | null = null;
6
- const workerId = process.env.VITEST_WORKER_ID || process.env.VITEST_POOL_ID || process.pid.toString();
7
- const mockName = `claudeMocked-${workerId}`;
8
- const mockPath = join('/tmp', 'claude-code-test-mock', mockName);
9
-
10
- export async function getSharedMock(): Promise<ClaudeMock> {
11
- if (!sharedMock) {
12
- sharedMock = new ClaudeMock(mockName);
13
- }
14
-
15
- // Always ensure mock exists
16
- if (!existsSync(mockPath)) {
17
- console.error(`[DEBUG] Mock not found at ${mockPath}, creating it...`);
18
- await sharedMock.setup();
19
- } else {
20
- console.error(`[DEBUG] Mock already exists at ${mockPath}`);
21
- }
22
-
23
- process.env.TEST_CLAUDE_CLI_NAME = mockPath;
24
-
25
- return sharedMock;
26
- }
27
-
28
- export async function cleanupSharedMock(): Promise<void> {
29
- if (sharedMock) {
30
- await sharedMock.cleanup();
31
- sharedMock = null;
32
- }
33
- }
@@ -1,13 +0,0 @@
1
- import { existsSync } from 'node:fs';
2
- import { join } from 'node:path';
3
-
4
- export function verifyMockExists(binaryName: string): boolean {
5
- const mockPath = join('/tmp', 'claude-code-test-mock', binaryName);
6
- return existsSync(mockPath);
7
- }
8
-
9
- export async function ensureMockExists(mock: any): Promise<void> {
10
- if (!verifyMockExists('claudeMocked')) {
11
- await mock.setup();
12
- }
13
- }