ai-cli-mcp 2.13.0 → 2.14.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.
@@ -0,0 +1,51 @@
1
+ function compactAgentOutput(agentOutput) {
2
+ if (!agentOutput || typeof agentOutput !== 'object') {
3
+ return null;
4
+ }
5
+ const { tools: _tools, ...rest } = agentOutput;
6
+ const compact = Object.fromEntries(Object.entries(rest).filter(([, value]) => value !== undefined && value !== null));
7
+ return Object.keys(compact).length > 0 ? compact : null;
8
+ }
9
+ function hasMeaningfulParsedOutput(agentOutput) {
10
+ if (!agentOutput || typeof agentOutput !== 'object') {
11
+ return false;
12
+ }
13
+ return Object.entries(agentOutput).some(([key, value]) => {
14
+ if (value === undefined || value === null) {
15
+ return false;
16
+ }
17
+ if (key === 'session_id') {
18
+ return false;
19
+ }
20
+ if (key === 'tools') {
21
+ return Array.isArray(value) ? value.length > 0 : true;
22
+ }
23
+ return true;
24
+ });
25
+ }
26
+ export function buildProcessResult(context, agentOutput, verbose = false) {
27
+ const response = {
28
+ pid: context.pid,
29
+ agent: context.agent,
30
+ status: context.status,
31
+ exitCode: context.exitCode ?? null,
32
+ model: context.model ?? null,
33
+ };
34
+ if (verbose) {
35
+ response.startTime = context.startTime;
36
+ response.workFolder = context.workFolder;
37
+ response.prompt = context.prompt;
38
+ }
39
+ if (agentOutput?.session_id) {
40
+ response.session_id = agentOutput.session_id;
41
+ }
42
+ const shapedAgentOutput = verbose ? agentOutput : compactAgentOutput(agentOutput);
43
+ if (hasMeaningfulParsedOutput(shapedAgentOutput)) {
44
+ response.agentOutput = shapedAgentOutput;
45
+ }
46
+ if (!response.agentOutput) {
47
+ response.stdout = context.stdout;
48
+ response.stderr = context.stderr;
49
+ }
50
+ return response;
51
+ }
@@ -1,6 +1,7 @@
1
1
  import { spawn } from 'node:child_process';
2
2
  import { buildCliCommand } from './cli-builder.js';
3
3
  import { parseClaudeOutput, parseCodexOutput, parseForgeOutput, parseGeminiOutput } from './parsers.js';
4
+ import { buildProcessResult } from './process-result.js';
4
5
  export class ProcessService {
5
6
  processManager = new Map();
6
7
  cliPaths;
@@ -100,7 +101,7 @@ export class ProcessService {
100
101
  agentOutput = parseForgeOutput(process.stdout);
101
102
  }
102
103
  }
103
- const response = {
104
+ return buildProcessResult({
104
105
  pid,
105
106
  agent: process.toolType,
106
107
  status: process.status,
@@ -109,26 +110,11 @@ export class ProcessService {
109
110
  workFolder: process.workFolder,
110
111
  prompt: process.prompt,
111
112
  model: process.model,
112
- };
113
- if (agentOutput) {
114
- if (!verbose && agentOutput.tools) {
115
- const { tools, ...rest } = agentOutput;
116
- response.agentOutput = rest;
117
- }
118
- else {
119
- response.agentOutput = agentOutput;
120
- }
121
- if (agentOutput.session_id) {
122
- response.session_id = agentOutput.session_id;
123
- }
124
- }
125
- else {
126
- response.stdout = process.stdout;
127
- response.stderr = process.stderr;
128
- }
129
- return response;
113
+ stdout: process.stdout,
114
+ stderr: process.stderr,
115
+ }, agentOutput, verbose);
130
116
  }
131
- async waitForProcesses(pids, timeoutSeconds = 180) {
117
+ async waitForProcesses(pids, timeoutSeconds = 180, verbose = false) {
132
118
  for (const pid of pids) {
133
119
  if (!this.processManager.has(pid)) {
134
120
  throw new Error(`Process with PID ${pid} not found`);
@@ -155,7 +141,7 @@ export class ProcessService {
155
141
  });
156
142
  try {
157
143
  await Promise.race([Promise.all(waitPromises), timeoutPromise]);
158
- return pids.map((pid) => this.getProcessResult(pid, false));
144
+ return pids.map((pid) => this.getProcessResult(pid, verbose));
159
145
  }
160
146
  finally {
161
147
  if (timeoutHandle) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-cli-mcp",
3
- "version": "2.13.0",
3
+ "version": "2.14.0",
4
4
  "mcpName": "io.github.mkXultra/ai-cli-mcp",
5
5
  "description": "MCP server for AI CLI tools (Claude, Codex, Gemini, and Forge) with background process management",
6
6
  "author": "mkXultra",
@@ -3,6 +3,7 @@ import {
3
3
  CLI_HELP_TEXT,
4
4
  DOCTOR_HELP_TEXT,
5
5
  MODELS_HELP_TEXT,
6
+ RESULT_HELP_TEXT,
6
7
  RUN_HELP_TEXT,
7
8
  WAIT_HELP_TEXT,
8
9
  runCli,
@@ -142,10 +143,28 @@ describe('ai-cli app', () => {
142
143
  );
143
144
 
144
145
  expect(exitCode).toBe(0);
145
- expect(waitForProcesses).toHaveBeenCalledWith([123, 456], 5);
146
+ expect(waitForProcesses).toHaveBeenCalledWith([123, 456], 5, false);
146
147
  expect(stdout).toHaveBeenCalledWith(expect.stringContaining('"status": "completed"'));
147
148
  });
148
149
 
150
+ it('passes verbose through to wait', async () => {
151
+ const stdout = vi.fn();
152
+ const stderr = vi.fn();
153
+ const waitForProcesses = vi.fn().mockResolvedValue([{ pid: 123, status: 'completed' }]);
154
+
155
+ const exitCode = await runCli(
156
+ ['wait', '123', '--verbose'],
157
+ {
158
+ stdout,
159
+ stderr,
160
+ waitForProcesses,
161
+ }
162
+ );
163
+
164
+ expect(exitCode).toBe(0);
165
+ expect(waitForProcesses).toHaveBeenCalledWith([123], undefined, true);
166
+ });
167
+
149
168
  it('rejects invalid wait timeout values', async () => {
150
169
  const stdout = vi.fn();
151
170
  const stderr = vi.fn();
@@ -291,6 +310,19 @@ describe('ai-cli app', () => {
291
310
  expect(stderr).not.toHaveBeenCalled();
292
311
  });
293
312
 
313
+ it('prints detailed help for result --help', async () => {
314
+ const stdout = vi.fn();
315
+ const stderr = vi.fn();
316
+
317
+ const exitCode = await runCli(['result', '--help'], { stdout, stderr });
318
+
319
+ expect(exitCode).toBe(0);
320
+ expect(stdout).toHaveBeenCalledWith(RESULT_HELP_TEXT);
321
+ expect(stdout).toHaveBeenCalledWith(expect.stringContaining('compact result shape'));
322
+ expect(stdout).toHaveBeenCalledWith(expect.stringContaining('--verbose'));
323
+ expect(stderr).not.toHaveBeenCalled();
324
+ });
325
+
294
326
  it('prints detailed help for wait --help', async () => {
295
327
  const stdout = vi.fn();
296
328
  const stderr = vi.fn();
@@ -299,6 +331,8 @@ describe('ai-cli app', () => {
299
331
 
300
332
  expect(exitCode).toBe(0);
301
333
  expect(stdout).toHaveBeenCalledWith(WAIT_HELP_TEXT);
334
+ expect(stdout).toHaveBeenCalledWith(expect.stringContaining('compact shape'));
335
+ expect(stdout).toHaveBeenCalledWith(expect.stringContaining('--verbose'));
302
336
  expect(stderr).not.toHaveBeenCalled();
303
337
  });
304
338
 
@@ -84,8 +84,18 @@ describe('CliProcessService', () => {
84
84
 
85
85
  const waitResult = await service.waitForProcesses([runResult.pid], 5);
86
86
  expect(waitResult).toHaveLength(1);
87
- expect(waitResult[0].pid).toBe(runResult.pid);
88
- expect(waitResult[0].status).toBe('completed');
87
+ expect(waitResult[0]).toMatchObject({
88
+ pid: runResult.pid,
89
+ agent: 'claude',
90
+ status: 'completed',
91
+ exitCode: null,
92
+ model: 'sonnet',
93
+ stdout: expect.any(String),
94
+ stderr: expect.any(String),
95
+ });
96
+ expect(waitResult[0]).not.toHaveProperty('startTime');
97
+ expect(waitResult[0]).not.toHaveProperty('workFolder');
98
+ expect(waitResult[0]).not.toHaveProperty('prompt');
89
99
 
90
100
  const listed = await service.listProcesses();
91
101
  expect(listed).toContainEqual({
@@ -95,12 +105,141 @@ describe('CliProcessService', () => {
95
105
  });
96
106
 
97
107
  const result = await service.getProcessResult(runResult.pid, false);
98
- expect(result.pid).toBe(runResult.pid);
99
- expect(result.status).toBe('completed');
100
- expect(result.stdout).toContain('Command executed successfully');
108
+ expect(result).toMatchObject({
109
+ pid: runResult.pid,
110
+ agent: 'claude',
111
+ status: 'completed',
112
+ exitCode: null,
113
+ model: 'sonnet',
114
+ stdout: expect.stringContaining('Command executed successfully'),
115
+ stderr: expect.any(String),
116
+ });
117
+ expect(result).not.toHaveProperty('startTime');
118
+ expect(result).not.toHaveProperty('workFolder');
119
+ expect(result).not.toHaveProperty('prompt');
101
120
  expect(readFileSync(join(processDir, 'meta.json'), 'utf-8')).toContain('"status": "completed"');
102
121
  });
103
122
 
123
+ it('returns compact results by default and full results when verbose is true', async () => {
124
+ const root = mkdtempSync(join(tmpdir(), 'ai-cli-cli-service-'));
125
+ tempDirs.push(root);
126
+ const scriptPath = join(root, 'mock-claude-json');
127
+ writeFileSync(
128
+ scriptPath,
129
+ `#!/bin/bash
130
+ printf '%s\n' '{"type":"assistant","message":{"content":[{"type":"tool_use","id":"tool-1","name":"Read","input":{"file_path":"/tmp/demo.txt"}}]}}'
131
+ printf '%s\n' '{"type":"user","message":{"content":[{"type":"tool_result","tool_use_id":"tool-1","content":[{"type":"text","text":"demo output"}]}]}}'
132
+ printf '%s\n' '{"type":"result","result":"Completed cli-process-service test"}'
133
+ printf '%s\n' '{"type":"system","session_id":"session-cli-1"}'
134
+ `
135
+ );
136
+ chmodSync(scriptPath, 0o755);
137
+ const stateDir = join(root, 'state');
138
+ const workFolder = join(root, 'work');
139
+ mkdirSync(workFolder, { recursive: true });
140
+
141
+ const service = new CliProcessService({
142
+ stateDir,
143
+ cliPaths: {
144
+ claude: scriptPath,
145
+ codex: scriptPath,
146
+ gemini: scriptPath,
147
+ forge: scriptPath,
148
+ },
149
+ });
150
+
151
+ const runResult = await service.startProcess({
152
+ prompt: 'hello structured output',
153
+ cwd: workFolder,
154
+ });
155
+
156
+ const compactWait = await service.waitForProcesses([runResult.pid], 5);
157
+ expect(compactWait).toHaveLength(1);
158
+ expect(compactWait[0]).toMatchObject({
159
+ pid: runResult.pid,
160
+ agent: 'claude',
161
+ status: 'completed',
162
+ exitCode: null,
163
+ model: null,
164
+ session_id: 'session-cli-1',
165
+ agentOutput: {
166
+ message: 'Completed cli-process-service test',
167
+ session_id: 'session-cli-1',
168
+ },
169
+ });
170
+ expect(compactWait[0]).not.toHaveProperty('startTime');
171
+ expect(compactWait[0]).not.toHaveProperty('workFolder');
172
+ expect(compactWait[0]).not.toHaveProperty('prompt');
173
+ expect(compactWait[0].agentOutput).not.toHaveProperty('tools');
174
+
175
+ const compactResult = await service.getProcessResult(runResult.pid, false);
176
+ expect(compactResult).toMatchObject({
177
+ pid: runResult.pid,
178
+ agent: 'claude',
179
+ status: 'completed',
180
+ exitCode: null,
181
+ model: null,
182
+ session_id: 'session-cli-1',
183
+ agentOutput: {
184
+ message: 'Completed cli-process-service test',
185
+ session_id: 'session-cli-1',
186
+ },
187
+ });
188
+ expect(compactResult).not.toHaveProperty('startTime');
189
+ expect(compactResult).not.toHaveProperty('workFolder');
190
+ expect(compactResult).not.toHaveProperty('prompt');
191
+ expect(compactResult.agentOutput).not.toHaveProperty('tools');
192
+
193
+ const verboseWait = await service.waitForProcesses([runResult.pid], 5, true);
194
+ expect(verboseWait).toHaveLength(1);
195
+ expect(verboseWait[0]).toMatchObject({
196
+ pid: runResult.pid,
197
+ agent: 'claude',
198
+ status: 'completed',
199
+ exitCode: null,
200
+ model: null,
201
+ startTime: expect.any(String),
202
+ workFolder,
203
+ prompt: 'hello structured output',
204
+ session_id: 'session-cli-1',
205
+ agentOutput: {
206
+ message: 'Completed cli-process-service test',
207
+ session_id: 'session-cli-1',
208
+ tools: [
209
+ {
210
+ tool: 'Read',
211
+ input: { file_path: '/tmp/demo.txt' },
212
+ output: 'demo output',
213
+ },
214
+ ],
215
+ },
216
+ });
217
+
218
+ const verboseResult = await service.getProcessResult(runResult.pid, true);
219
+ expect(verboseResult).toMatchObject({
220
+ pid: runResult.pid,
221
+ agent: 'claude',
222
+ status: 'completed',
223
+ exitCode: null,
224
+ model: null,
225
+ startTime: expect.any(String),
226
+ workFolder,
227
+ prompt: 'hello structured output',
228
+ session_id: 'session-cli-1',
229
+ agentOutput: {
230
+ message: 'Completed cli-process-service test',
231
+ session_id: 'session-cli-1',
232
+ tools: [
233
+ {
234
+ tool: 'Read',
235
+ input: { file_path: '/tmp/demo.txt' },
236
+ output: 'demo output',
237
+ },
238
+ ],
239
+ },
240
+ });
241
+ });
242
+
104
243
  it('can terminate a tracked process', async () => {
105
244
  const root = mkdtempSync(join(tmpdir(), 'ai-cli-cli-service-'));
106
245
  tempDirs.push(root);
@@ -122,6 +122,7 @@ describe('MCP Contract Tests', () => {
122
122
  expect(Object.keys(waitTool.inputSchema.properties).sort()).toEqual([
123
123
  'pids',
124
124
  'timeout',
125
+ 'verbose',
125
126
  ]);
126
127
  });
127
128
 
@@ -155,22 +156,32 @@ describe('MCP Contract Tests', () => {
155
156
  pid: runData.pid,
156
157
  agent: 'claude',
157
158
  status: expect.any(String),
158
- startTime: expect.any(String),
159
- workFolder: testDir,
160
- prompt: 'create a file called contract.txt with content "hello"',
161
159
  model: 'haiku',
162
160
  stdout: expect.any(String),
163
161
  stderr: expect.any(String),
164
162
  });
163
+ expect(getResultData).toHaveProperty('exitCode');
164
+ expect(getResultData).not.toHaveProperty('startTime');
165
+ expect(getResultData).not.toHaveProperty('workFolder');
166
+ expect(getResultData).not.toHaveProperty('prompt');
165
167
 
166
168
  const waitResponse = await client.callTool('wait', { pids: [runData.pid], timeout: 5 });
167
169
  const waitData = parseToolJson(waitResponse);
168
170
 
169
171
  expect(Array.isArray(waitData)).toBe(true);
170
172
  expect(waitData).toHaveLength(1);
171
- expect(waitData[0].pid).toBe(runData.pid);
172
- expect(waitData[0].agent).toBe('claude');
173
- expect(waitData[0].status).toBe('completed');
173
+ expect(waitData[0]).toMatchObject({
174
+ pid: runData.pid,
175
+ agent: 'claude',
176
+ status: 'completed',
177
+ exitCode: 0,
178
+ model: 'haiku',
179
+ stdout: expect.any(String),
180
+ stderr: expect.any(String),
181
+ });
182
+ expect(waitData[0]).not.toHaveProperty('startTime');
183
+ expect(waitData[0]).not.toHaveProperty('workFolder');
184
+ expect(waitData[0]).not.toHaveProperty('prompt');
174
185
 
175
186
  const cleanupResponse = await client.callTool('cleanup_processes', {});
176
187
  const cleanupData = parseToolJson(cleanupResponse);
@@ -183,13 +194,14 @@ describe('MCP Contract Tests', () => {
183
194
  expect(cleanupData.removedPids).toContain(runData.pid);
184
195
  });
185
196
 
186
- it('accepts prompt_file and keeps the run response shape stable', async () => {
197
+ it('preserves successful prompt_file execution through the MCP process path', async () => {
187
198
  const promptFile = join(testDir, 'prompt.txt');
188
- writeFileSync(promptFile, 'create a file called from-file.txt');
199
+ writeFileSync(promptFile, 'Create a file from prompt_file');
189
200
 
190
201
  const runResponse = await client.callTool('run', {
191
202
  prompt_file: promptFile,
192
203
  workFolder: testDir,
204
+ model: 'haiku',
193
205
  });
194
206
  const runData = parseToolJson(runResponse);
195
207
 
@@ -199,6 +211,138 @@ describe('MCP Contract Tests', () => {
199
211
  agent: 'claude',
200
212
  message: expect.any(String),
201
213
  });
214
+
215
+ const waitResponse = await client.callTool('wait', { pids: [runData.pid], timeout: 5 });
216
+ const waitData = parseToolJson(waitResponse);
217
+
218
+ expect(waitData).toHaveLength(1);
219
+ expect(waitData[0]).toMatchObject({
220
+ pid: runData.pid,
221
+ agent: 'claude',
222
+ status: 'completed',
223
+ exitCode: 0,
224
+ model: 'haiku',
225
+ stdout: expect.stringContaining('Created file successfully'),
226
+ stderr: '',
227
+ });
228
+ expect(waitData[0]).not.toHaveProperty('prompt');
229
+ expect(waitData[0]).not.toHaveProperty('workFolder');
230
+ expect(waitData[0]).not.toHaveProperty('startTime');
231
+ });
232
+
233
+ it('returns compact results by default and full results when verbose is true for parsed output', async () => {
234
+ await client.disconnect();
235
+
236
+ const verboseMockPath = join(testDir, 'verbose-claude');
237
+ writeFileSync(
238
+ verboseMockPath,
239
+ `#!/bin/bash
240
+ printf '%s\n' '{"type":"assistant","message":{"content":[{"type":"tool_use","id":"tool-1","name":"Read","input":{"file_path":"/tmp/demo.txt"}}]}}'
241
+ printf '%s\n' '{"type":"user","message":{"content":[{"type":"tool_result","tool_use_id":"tool-1","content":[{"type":"text","text":"demo output"}]}]}}'
242
+ printf '%s\n' '{"type":"result","result":"Completed contract verbose test"}'
243
+ printf '%s\n' '{"type":"system","session_id":"session-verbose-1"}'
244
+ `
245
+ );
246
+ chmodSync(verboseMockPath, 0o755);
247
+
248
+ client = createTestClient({ claudeCliName: verboseMockPath, debug: false });
249
+ await client.connect();
250
+
251
+ const runResponse = await client.callTool('run', {
252
+ prompt: 'verbose-shape-test',
253
+ workFolder: testDir,
254
+ });
255
+ const runData = parseToolJson(runResponse);
256
+
257
+ const completedWait = parseToolJson(await client.callTool('wait', { pids: [runData.pid], timeout: 5 }));
258
+ expect(completedWait).toHaveLength(1);
259
+ expect(completedWait[0].status).toBe('completed');
260
+
261
+ const compactResult = parseToolJson(await client.callTool('get_result', { pid: runData.pid }));
262
+ expect(compactResult).toMatchObject({
263
+ pid: runData.pid,
264
+ agent: 'claude',
265
+ status: 'completed',
266
+ exitCode: 0,
267
+ model: null,
268
+ session_id: 'session-verbose-1',
269
+ agentOutput: {
270
+ message: 'Completed contract verbose test',
271
+ session_id: 'session-verbose-1',
272
+ },
273
+ });
274
+ expect(compactResult).not.toHaveProperty('startTime');
275
+ expect(compactResult).not.toHaveProperty('workFolder');
276
+ expect(compactResult).not.toHaveProperty('prompt');
277
+ expect(compactResult.agentOutput).not.toHaveProperty('tools');
278
+
279
+ const verboseResult = parseToolJson(await client.callTool('get_result', { pid: runData.pid, verbose: true }));
280
+ expect(verboseResult).toMatchObject({
281
+ pid: runData.pid,
282
+ agent: 'claude',
283
+ status: 'completed',
284
+ exitCode: 0,
285
+ model: null,
286
+ startTime: expect.any(String),
287
+ workFolder: testDir,
288
+ prompt: 'verbose-shape-test',
289
+ session_id: 'session-verbose-1',
290
+ agentOutput: {
291
+ message: 'Completed contract verbose test',
292
+ session_id: 'session-verbose-1',
293
+ tools: [
294
+ {
295
+ tool: 'Read',
296
+ input: { file_path: '/tmp/demo.txt' },
297
+ output: 'demo output',
298
+ },
299
+ ],
300
+ },
301
+ });
302
+
303
+ const compactWait = parseToolJson(await client.callTool('wait', { pids: [runData.pid], timeout: 5 }));
304
+ expect(compactWait).toHaveLength(1);
305
+ expect(compactWait[0]).toMatchObject({
306
+ pid: runData.pid,
307
+ agent: 'claude',
308
+ status: 'completed',
309
+ exitCode: 0,
310
+ model: null,
311
+ session_id: 'session-verbose-1',
312
+ agentOutput: {
313
+ message: 'Completed contract verbose test',
314
+ session_id: 'session-verbose-1',
315
+ },
316
+ });
317
+ expect(compactWait[0]).not.toHaveProperty('startTime');
318
+ expect(compactWait[0]).not.toHaveProperty('workFolder');
319
+ expect(compactWait[0]).not.toHaveProperty('prompt');
320
+ expect(compactWait[0].agentOutput).not.toHaveProperty('tools');
321
+
322
+ const verboseWait = parseToolJson(await client.callTool('wait', { pids: [runData.pid], timeout: 5, verbose: true }));
323
+ expect(verboseWait).toHaveLength(1);
324
+ expect(verboseWait[0]).toMatchObject({
325
+ pid: runData.pid,
326
+ agent: 'claude',
327
+ status: 'completed',
328
+ exitCode: 0,
329
+ model: null,
330
+ startTime: expect.any(String),
331
+ workFolder: testDir,
332
+ prompt: 'verbose-shape-test',
333
+ session_id: 'session-verbose-1',
334
+ agentOutput: {
335
+ message: 'Completed contract verbose test',
336
+ session_id: 'session-verbose-1',
337
+ tools: [
338
+ {
339
+ tool: 'Read',
340
+ input: { file_path: '/tmp/demo.txt' },
341
+ output: 'demo output',
342
+ },
343
+ ],
344
+ },
345
+ });
202
346
  });
203
347
 
204
348
  it('covers forge end-to-end through the MCP process path', async () => {
@@ -191,7 +191,8 @@ describe('Process Management Tests', () => {
191
191
  params: {
192
192
  name: 'get_result',
193
193
  arguments: {
194
- pid: 12360
194
+ pid: 12360,
195
+ verbose: true
195
196
  }
196
197
  }
197
198
  });
package/src/app/cli.ts CHANGED
@@ -41,18 +41,20 @@ Compatibility aliases:
41
41
  export const WAIT_HELP_TEXT = `Usage: ai-cli wait <pid...> [options]
42
42
 
43
43
  Wait for one or more tracked processes to finish.
44
+ By default each result uses the compact shape; set --verbose to include full metadata and detailed parsed output.
44
45
 
45
46
  Options:
46
47
  --timeout <seconds> Maximum wait time in seconds
48
+ --verbose Return full metadata and detailed parsed output
47
49
  --help, -h Show this help message
48
50
  `;
49
51
 
50
52
  export const RESULT_HELP_TEXT = `Usage: ai-cli result <pid> [options]
51
53
 
52
- Get the current result for a tracked process.
54
+ 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.
53
55
 
54
56
  Options:
55
- --verbose Include verbose parsed output
57
+ --verbose Return full metadata and detailed parsed output
56
58
  --help, -h Show this help message
57
59
  `;
58
60
 
@@ -115,7 +117,7 @@ interface CliDeps {
115
117
  }) => Promise<any>;
116
118
  listProcesses: () => Promise<any>;
117
119
  getProcessResult: (pid: number, verbose: boolean) => Promise<any>;
118
- waitForProcesses: (pids: number[], timeoutSeconds?: number) => Promise<any>;
120
+ waitForProcesses: (pids: number[], timeoutSeconds?: number, verbose?: boolean) => Promise<any>;
119
121
  killProcess: (pid: number) => Promise<any>;
120
122
  cleanupProcesses: () => Promise<any>;
121
123
  getDoctorStatus: () => any;
@@ -137,7 +139,7 @@ const defaultDeps: CliDeps = {
137
139
  runProcess: (options) => getCliProcessService().startProcess(options),
138
140
  listProcesses: () => getCliProcessService().listProcesses(),
139
141
  getProcessResult: (pid, verbose) => getCliProcessService().getProcessResult(pid, verbose),
140
- waitForProcesses: (pids, timeoutSeconds) => getCliProcessService().waitForProcesses(pids, timeoutSeconds),
142
+ waitForProcesses: (pids, timeoutSeconds, verbose) => getCliProcessService().waitForProcesses(pids, timeoutSeconds, verbose),
141
143
  killProcess: (pid) => getCliProcessService().killProcess(pid),
142
144
  cleanupProcesses: () => getCliProcessService().cleanupProcesses(),
143
145
  getDoctorStatus: () => getCliDoctorStatus(),
@@ -317,7 +319,7 @@ export async function runCli(argv: string[], deps: Partial<CliDeps> = {}): Promi
317
319
  return 1;
318
320
  }
319
321
 
320
- writeJson(stdout, await waitForProcesses(pids as number[], timeout));
322
+ writeJson(stdout, await waitForProcesses(pids as number[], timeout, 'verbose' in flags));
321
323
  return 0;
322
324
  }
323
325
 
package/src/app/mcp.ts CHANGED
@@ -187,7 +187,7 @@ ${getSupportedModelsDescription()}
187
187
  },
188
188
  {
189
189
  name: 'get_result',
190
- description: 'Get the current output and status of an AI agent process by PID. Returns the output from the agent including session_id (if applicable), along with process metadata.',
190
+ description: 'Get the current output and status of an AI agent process by PID. Defaults to a compact result shape; set verbose to true for full metadata and detailed parsed output.',
191
191
  inputSchema: {
192
192
  type: 'object',
193
193
  properties: {
@@ -197,7 +197,7 @@ ${getSupportedModelsDescription()}
197
197
  },
198
198
  verbose: {
199
199
  type: 'boolean',
200
- description: 'Optional: If true, returns detailed execution information including tool usage history. Defaults to false.',
200
+ description: 'Optional: If true, returns the full result shape including metadata fields and detailed parsed output such as tool usage history. Defaults to false.',
201
201
  }
202
202
  },
203
203
  required: ['pid'],
@@ -205,7 +205,7 @@ ${getSupportedModelsDescription()}
205
205
  },
206
206
  {
207
207
  name: 'wait',
208
- description: 'Wait for multiple AI agent processes to complete and return their results. Blocks until all specified PIDs finish or timeout occurs.',
208
+ description: 'Wait for multiple AI agent processes to complete and return their results. Defaults to compact result items; set verbose to true for full metadata and detailed parsed output.',
209
209
  inputSchema: {
210
210
  type: 'object',
211
211
  properties: {
@@ -218,6 +218,10 @@ ${getSupportedModelsDescription()}
218
218
  type: 'number',
219
219
  description: 'Optional: Maximum time to wait in seconds. Defaults to 180 (3 minutes).',
220
220
  },
221
+ verbose: {
222
+ type: 'boolean',
223
+ description: 'Optional: If true, each result item uses the full result shape including metadata fields and detailed parsed output. Defaults to false.',
224
+ },
221
225
  },
222
226
  required: ['pids'],
223
227
  },
@@ -336,7 +340,8 @@ ${getSupportedModelsDescription()}
336
340
  try {
337
341
  const results = await this.processService.waitForProcesses(
338
342
  toolArguments.pids,
339
- typeof toolArguments.timeout === 'number' ? toolArguments.timeout : 180
343
+ typeof toolArguments.timeout === 'number' ? toolArguments.timeout : 180,
344
+ !!toolArguments.verbose
340
345
  );
341
346
  return {
342
347
  content: [{