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,370 +0,0 @@
1
- import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
2
- import { spawn } from 'node:child_process';
3
- import { existsSync } from 'node:fs';
4
- import { homedir } from 'node:os';
5
- import { EventEmitter } from 'node:events';
6
- import { McpError, ErrorCode } from '@modelcontextprotocol/sdk/types.js';
7
- import { Server } from '@modelcontextprotocol/sdk/server/index.js';
8
-
9
- // Mock dependencies
10
- vi.mock('node:child_process');
11
- vi.mock('node:fs');
12
- vi.mock('node:os');
13
- vi.mock('node:path', () => ({
14
- resolve: vi.fn((path) => path),
15
- join: vi.fn((...args) => args.join('/')),
16
- isAbsolute: vi.fn((path) => path.startsWith('/'))
17
- }));
18
- vi.mock('@modelcontextprotocol/sdk/server/index.js', () => ({
19
- Server: vi.fn().mockImplementation(function(this: any) {
20
- this.setRequestHandler = vi.fn();
21
- this.connect = vi.fn();
22
- this.close = vi.fn();
23
- this.onerror = undefined;
24
- return this;
25
- }),
26
- }));
27
-
28
- vi.mock('@modelcontextprotocol/sdk/types.js', () => ({
29
- ListToolsRequestSchema: { name: 'listTools' },
30
- CallToolRequestSchema: { name: 'callTool' },
31
- ErrorCode: {
32
- InternalError: 'InternalError',
33
- MethodNotFound: 'MethodNotFound',
34
- InvalidParams: 'InvalidParams'
35
- },
36
- McpError: class extends Error {
37
- code: any;
38
- constructor(code: any, message: string) {
39
- super(message);
40
- this.code = code;
41
- }
42
- }
43
- }));
44
-
45
- const mockExistsSync = vi.mocked(existsSync);
46
- const mockSpawn = vi.mocked(spawn);
47
- const mockHomedir = vi.mocked(homedir);
48
-
49
- describe('Error Handling Tests', () => {
50
- let consoleErrorSpy: any;
51
- let originalEnv: any;
52
- let errorHandler: any = null;
53
-
54
- function setupServerMock() {
55
- errorHandler = null;
56
- vi.mocked(Server).mockImplementation(function(this: any) {
57
- this.setRequestHandler = vi.fn();
58
- this.connect = vi.fn();
59
- this.close = vi.fn();
60
- Object.defineProperty(this, 'onerror', {
61
- get() { return errorHandler; },
62
- set(handler) { errorHandler = handler; },
63
- enumerable: true,
64
- configurable: true
65
- });
66
- return this;
67
- });
68
- }
69
-
70
- beforeEach(() => {
71
- vi.clearAllMocks();
72
- vi.resetModules();
73
- consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
74
- originalEnv = { ...process.env };
75
- process.env = { ...originalEnv };
76
- });
77
-
78
- afterEach(() => {
79
- consoleErrorSpy.mockRestore();
80
- process.env = originalEnv;
81
- });
82
-
83
- describe('CallToolRequest Error Cases', () => {
84
- it('should throw error for unknown tool name', async () => {
85
- mockHomedir.mockReturnValue('/home/user');
86
- mockExistsSync.mockReturnValue(true);
87
-
88
- // Set up Server mock before importing the module
89
- setupServerMock();
90
-
91
- const module = await import('../server.js');
92
- // @ts-ignore
93
- const { ClaudeCodeServer } = module;
94
-
95
- const server = new ClaudeCodeServer();
96
- const mockServerInstance = vi.mocked(Server).mock.results[0].value;
97
-
98
- const callToolCall = mockServerInstance.setRequestHandler.mock.calls.find(
99
- (call: any[]) => call[0].name === 'callTool'
100
- );
101
-
102
- const handler = callToolCall[1];
103
-
104
- await expect(
105
- handler({
106
- params: {
107
- name: 'unknown_tool',
108
- arguments: {}
109
- }
110
- })
111
- ).rejects.toThrow('Tool unknown_tool not found');
112
- });
113
-
114
- it('should handle timeout errors', async () => {
115
- mockHomedir.mockReturnValue('/home/user');
116
- mockExistsSync.mockReturnValue(true);
117
- setupServerMock();
118
-
119
- const module = await import('../server.js');
120
- // @ts-ignore
121
- const { ClaudeCodeServer } = module;
122
- const { McpError } = await import('@modelcontextprotocol/sdk/types.js');
123
-
124
- const server = new ClaudeCodeServer();
125
- const mockServerInstance = vi.mocked(Server).mock.results[0].value;
126
-
127
- // Find the callTool handler
128
- let callToolHandler: any;
129
- for (const call of mockServerInstance.setRequestHandler.mock.calls) {
130
- if (call[0].name === 'callTool') {
131
- callToolHandler = call[1];
132
- break;
133
- }
134
- }
135
-
136
- // Mock spawn to return process without PID
137
- mockSpawn.mockImplementation(() => {
138
- const mockProcess = new EventEmitter() as any;
139
- mockProcess.stdout = new EventEmitter();
140
- mockProcess.stderr = new EventEmitter();
141
-
142
- mockProcess.stdout.on = vi.fn();
143
- mockProcess.stderr.on = vi.fn();
144
- mockProcess.pid = undefined; // No PID to simulate process start failure
145
-
146
- return mockProcess;
147
- });
148
-
149
- // Call handler
150
- await expect(callToolHandler({
151
- params: {
152
- name: 'run',
153
- arguments: {
154
- prompt: 'test',
155
- workFolder: '/tmp'
156
- }
157
- }
158
- })).rejects.toThrow('Failed to start claude CLI process');
159
- });
160
-
161
- it('should handle invalid argument types', async () => {
162
- mockHomedir.mockReturnValue('/home/user');
163
- mockExistsSync.mockReturnValue(true);
164
- setupServerMock();
165
- const module = await import('../server.js');
166
- // @ts-ignore
167
- const { ClaudeCodeServer } = module;
168
-
169
- const server = new ClaudeCodeServer();
170
- const mockServerInstance = vi.mocked(Server).mock.results[0].value;
171
-
172
- const callToolCall = mockServerInstance.setRequestHandler.mock.calls.find(
173
- (call: any[]) => call[0].name === 'callTool'
174
- );
175
-
176
- const handler = callToolCall[1];
177
-
178
- await expect(
179
- handler({
180
- params: {
181
- name: 'run',
182
- arguments: 'invalid-should-be-object'
183
- }
184
- })
185
- ).rejects.toThrow();
186
- });
187
-
188
- it('should include CLI error details in error message', async () => {
189
- mockHomedir.mockReturnValue('/home/user');
190
- mockExistsSync.mockReturnValue(true);
191
- setupServerMock();
192
-
193
- const module = await import('../server.js');
194
- // @ts-ignore
195
- const { ClaudeCodeServer } = module;
196
-
197
- const server = new ClaudeCodeServer();
198
- const mockServerInstance = vi.mocked(Server).mock.results[0].value;
199
-
200
- const callToolCall = mockServerInstance.setRequestHandler.mock.calls.find(
201
- (call: any[]) => call[0].name === 'callTool'
202
- );
203
-
204
- const handler = callToolCall[1];
205
-
206
- // Create a simple mock process
207
- mockSpawn.mockImplementation(() => {
208
- const mockProcess = Object.create(EventEmitter.prototype);
209
- EventEmitter.call(mockProcess);
210
- mockProcess.stdout = Object.create(EventEmitter.prototype);
211
- EventEmitter.call(mockProcess.stdout);
212
- mockProcess.stderr = Object.create(EventEmitter.prototype);
213
- EventEmitter.call(mockProcess.stderr);
214
-
215
- mockProcess.stdout.on = vi.fn((event, callback) => {
216
- if (event === 'data') {
217
- // Send some stdout data
218
- process.nextTick(() => callback('stdout content'));
219
- }
220
- });
221
-
222
- mockProcess.stderr.on = vi.fn((event, callback) => {
223
- if (event === 'data') {
224
- // Send some stderr data
225
- process.nextTick(() => callback('stderr content'));
226
- }
227
- });
228
-
229
- // Emit error/close event after data is sent
230
- setTimeout(() => {
231
- mockProcess.emit('close', 1);
232
- }, 1);
233
-
234
- return mockProcess;
235
- });
236
-
237
- await expect(
238
- handler({
239
- params: {
240
- name: 'run',
241
- arguments: {
242
- prompt: 'test',
243
- workFolder: '/tmp'
244
- }
245
- }
246
- })
247
- ).rejects.toThrow();
248
- });
249
- });
250
-
251
- describe('Process Spawn Error Cases', () => {
252
- it('should handle spawn ENOENT error', async () => {
253
- const module = await import('../server.js');
254
- // @ts-ignore
255
- const { spawnAsync } = module;
256
-
257
- const mockProcess = new EventEmitter() as any;
258
- mockProcess.stdout = new EventEmitter();
259
- mockProcess.stderr = new EventEmitter();
260
- mockProcess.stdout.on = vi.fn();
261
- mockProcess.stderr.on = vi.fn();
262
-
263
- mockSpawn.mockReturnValue(mockProcess);
264
-
265
- const promise = spawnAsync('nonexistent-command', []);
266
-
267
- // Simulate ENOENT error
268
- setTimeout(() => {
269
- const error: any = new Error('spawn ENOENT');
270
- error.code = 'ENOENT';
271
- error.path = 'nonexistent-command';
272
- error.syscall = 'spawn';
273
- mockProcess.emit('error', error);
274
- }, 10);
275
-
276
- await expect(promise).rejects.toThrow('Spawn error');
277
- await expect(promise).rejects.toThrow('nonexistent-command');
278
- });
279
-
280
- it('should handle generic spawn errors', async () => {
281
- const module = await import('../server.js');
282
- // @ts-ignore
283
- const { spawnAsync } = module;
284
-
285
- const mockProcess = new EventEmitter() as any;
286
- mockProcess.stdout = new EventEmitter();
287
- mockProcess.stderr = new EventEmitter();
288
- mockProcess.stdout.on = vi.fn();
289
- mockProcess.stderr.on = vi.fn();
290
-
291
- mockSpawn.mockReturnValue(mockProcess);
292
-
293
- const promise = spawnAsync('test', []);
294
-
295
- // Simulate generic error
296
- setTimeout(() => {
297
- mockProcess.emit('error', new Error('Generic spawn error'));
298
- }, 10);
299
-
300
- await expect(promise).rejects.toThrow('Generic spawn error');
301
- });
302
-
303
- it('should accumulate stderr output before error', async () => {
304
- const module = await import('../server.js');
305
- // @ts-ignore
306
- const { spawnAsync } = module;
307
-
308
- const mockProcess = new EventEmitter() as any;
309
- mockProcess.stdout = new EventEmitter();
310
- mockProcess.stderr = new EventEmitter();
311
- let stderrHandler: any;
312
-
313
- mockProcess.stdout.on = vi.fn();
314
- mockProcess.stderr.on = vi.fn((event, handler) => {
315
- if (event === 'data') stderrHandler = handler;
316
- });
317
-
318
- mockSpawn.mockReturnValue(mockProcess);
319
-
320
- const promise = spawnAsync('test', []);
321
-
322
- // Simulate stderr data then error
323
- setTimeout(() => {
324
- stderrHandler('error line 1\n');
325
- stderrHandler('error line 2\n');
326
- mockProcess.emit('error', new Error('Command failed'));
327
- }, 10);
328
-
329
- await expect(promise).rejects.toThrow('error line 1\nerror line 2');
330
- });
331
- });
332
-
333
- describe('Server Initialization Errors', () => {
334
- it('should handle CLI path not found gracefully', async () => {
335
- // Mock no CLI found anywhere
336
- mockHomedir.mockReturnValue('/home/user');
337
- mockExistsSync.mockReturnValue(false);
338
- const consoleWarnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
339
- setupServerMock();
340
-
341
- const module = await import('../server.js');
342
- // @ts-ignore
343
- const { ClaudeCodeServer } = module;
344
-
345
- const server = new ClaudeCodeServer();
346
-
347
- expect(server).toBeDefined();
348
- expect(consoleWarnSpy).not.toHaveBeenCalled();
349
-
350
- consoleWarnSpy.mockRestore();
351
- });
352
-
353
- it('should handle server connection errors', async () => {
354
- mockHomedir.mockReturnValue('/home/user');
355
- mockExistsSync.mockReturnValue(true);
356
- setupServerMock();
357
- const module = await import('../server.js');
358
- // @ts-ignore
359
- const { ClaudeCodeServer } = module;
360
-
361
- const server = new ClaudeCodeServer();
362
-
363
- // Mock connection failure
364
- const mockServerInstance = vi.mocked(Server).mock.results[0].value;
365
- mockServerInstance.connect.mockRejectedValue(new Error('Connection failed'));
366
-
367
- await expect(server.run()).rejects.toThrow('Connection failed');
368
- });
369
- });
370
- });