ai-cli-mcp 2.17.0 → 2.19.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.
package/dist/parsers.js CHANGED
@@ -1,4 +1,7 @@
1
1
  import { debugLog } from './cli-utils.js';
2
+ const PEEK_TOOL_SUMMARY_MAX_LENGTH = 200;
3
+ const FORGE_EXECUTE_PATTERN = /^● \[[^\]]+\] Execute \[([^\]]*)\]\s+(.+)$/;
4
+ const FORGE_FINISHED_PATTERN = /^● \[[^\]]+\] Finished(?:\s+\S+)?\s*$/;
2
5
  function isGeminiAssistantMessageEvent(parsed) {
3
6
  return parsed.type === 'message' && parsed.role === 'assistant' && typeof parsed.content === 'string';
4
7
  }
@@ -14,34 +17,257 @@ const GEMINI_STREAM_EVENT_TYPES = new Set([
14
17
  function isGeminiStreamJsonEvent(parsed) {
15
18
  return parsed && typeof parsed === 'object' && !Array.isArray(parsed) && GEMINI_STREAM_EVENT_TYPES.has(parsed.type);
16
19
  }
17
- function extractPeekMessagesFromParsedEvent(agent, parsed, observedAt) {
20
+ function oneLine(value) {
21
+ return String(value ?? '').replace(/\s+/g, ' ').trim();
22
+ }
23
+ function boundedSummary(value) {
24
+ const summary = oneLine(value);
25
+ if (summary.length <= PEEK_TOOL_SUMMARY_MAX_LENGTH) {
26
+ return { summary };
27
+ }
28
+ return {
29
+ summary: `${summary.slice(0, PEEK_TOOL_SUMMARY_MAX_LENGTH - 3)}...`,
30
+ summary_truncated: true,
31
+ };
32
+ }
33
+ function normalizeMcpToolName(tool, explicitServer) {
34
+ if (explicitServer) {
35
+ return {
36
+ server: explicitServer,
37
+ ...boundedSummary(`${explicitServer}.${tool}`),
38
+ };
39
+ }
40
+ const mcpDouble = tool.match(/^mcp__([^_]+)__(.+)$/);
41
+ if (mcpDouble) {
42
+ return {
43
+ server: mcpDouble[1],
44
+ ...boundedSummary(`${mcpDouble[1]}.${mcpDouble[2]}`),
45
+ };
46
+ }
47
+ const mcpSingle = tool.match(/^mcp_([^_]+)_(.+)$/);
48
+ if (mcpSingle) {
49
+ return {
50
+ server: mcpSingle[1],
51
+ ...boundedSummary(`${mcpSingle[1]}.${mcpSingle[2]}`),
52
+ };
53
+ }
54
+ const acmShort = tool.match(/^acm_(.+)$/);
55
+ if (acmShort) {
56
+ return {
57
+ server: 'acm',
58
+ ...boundedSummary(`acm.${acmShort[1]}`),
59
+ };
60
+ }
61
+ return null;
62
+ }
63
+ function buildToolSummary(tool, options = {}) {
64
+ if (typeof options.command === 'string' && options.command.trim()) {
65
+ return boundedSummary(options.command);
66
+ }
67
+ const mcpSummary = normalizeMcpToolName(tool, options.server);
68
+ if (mcpSummary) {
69
+ return mcpSummary;
70
+ }
71
+ return boundedSummary(tool || 'tool_call');
72
+ }
73
+ function normalizeToolStatus(rawStatus, exitCode, defaultStatus = 'unknown') {
74
+ if (typeof exitCode === 'number') {
75
+ return exitCode === 0 ? 'success' : 'failed';
76
+ }
77
+ const status = typeof rawStatus === 'string' ? rawStatus.toLowerCase() : '';
78
+ if (['success', 'succeeded', 'ok', 'completed'].includes(status)) {
79
+ return 'success';
80
+ }
81
+ if (['failed', 'failure', 'error', 'errored'].includes(status)) {
82
+ return 'failed';
83
+ }
84
+ if (['cancelled', 'canceled'].includes(status)) {
85
+ return 'cancelled';
86
+ }
87
+ return defaultStatus;
88
+ }
89
+ function createToolCallEvent(params) {
90
+ const tool = params.tool || 'tool_call';
91
+ const summary = buildToolSummary(tool, { server: params.server, command: params.command });
92
+ const event = {
93
+ kind: 'tool_call',
94
+ ts: params.ts,
95
+ phase: params.phase,
96
+ tool,
97
+ summary: summary.summary,
98
+ };
99
+ if (params.id) {
100
+ event.id = params.id;
101
+ }
102
+ if (summary.server) {
103
+ event.server = summary.server;
104
+ }
105
+ else if (params.server) {
106
+ event.server = params.server;
107
+ }
108
+ if (summary.summary_truncated) {
109
+ event.summary_truncated = true;
110
+ }
111
+ if (params.phase === 'completed') {
112
+ event.status = normalizeToolStatus(params.status, params.exit_code, params.defaultStatus);
113
+ if (typeof params.exit_code === 'number') {
114
+ event.exit_code = params.exit_code;
115
+ }
116
+ if (typeof params.duration_ms === 'number' && Number.isFinite(params.duration_ms)) {
117
+ event.duration_ms = params.duration_ms;
118
+ }
119
+ }
120
+ return event;
121
+ }
122
+ function rememberToolCall(event, memory) {
123
+ if (event.kind !== 'tool_call' || !event.id) {
124
+ return;
125
+ }
126
+ memory.set(event.id, {
127
+ tool: event.tool,
128
+ server: event.server,
129
+ summary: event.summary,
130
+ summary_truncated: event.summary_truncated,
131
+ });
132
+ }
133
+ function createRememberedCompletion(params) {
134
+ const remembered = params.id ? params.memory.get(params.id) : undefined;
135
+ const event = createToolCallEvent({
136
+ ts: params.ts,
137
+ phase: 'completed',
138
+ id: params.id,
139
+ tool: remembered?.tool || params.fallbackTool,
140
+ server: remembered?.server,
141
+ status: params.status,
142
+ defaultStatus: params.defaultStatus,
143
+ });
144
+ if (remembered) {
145
+ event.summary = remembered.summary;
146
+ if (remembered.summary_truncated) {
147
+ event.summary_truncated = true;
148
+ }
149
+ }
150
+ return event;
151
+ }
152
+ function extractPeekEventsFromParsedEvent(agent, parsed, observedAt, includeToolCalls, memory) {
18
153
  if (agent === 'codex') {
19
154
  if (parsed.item?.type === 'agent_message' && typeof parsed.item.text === 'string' && parsed.item.text.trim()) {
20
- return [{ ts: observedAt, text: parsed.item.text }];
155
+ return [{ kind: 'message', ts: observedAt, text: parsed.item.text }];
21
156
  }
22
157
  if (parsed.msg?.type === 'agent_message' && typeof parsed.msg.message === 'string' && parsed.msg.message.trim()) {
23
- return [{ ts: observedAt, text: parsed.msg.message }];
158
+ return [{ kind: 'message', ts: observedAt, text: parsed.msg.message }];
159
+ }
160
+ if (includeToolCalls && (parsed.type === 'item.started' || parsed.type === 'item.completed')) {
161
+ const item = parsed.item;
162
+ if (item?.type === 'command_execution') {
163
+ const event = createToolCallEvent({
164
+ ts: observedAt,
165
+ phase: parsed.type === 'item.started' ? 'started' : 'completed',
166
+ id: item.id,
167
+ tool: 'command_execution',
168
+ command: item.command,
169
+ status: item.status || item.error,
170
+ exit_code: typeof item.exit_code === 'number' ? item.exit_code : undefined,
171
+ defaultStatus: parsed.type === 'item.completed' ? 'success' : 'unknown',
172
+ });
173
+ rememberToolCall(event, memory);
174
+ return [event];
175
+ }
176
+ if (item?.type === 'mcp_tool_call') {
177
+ const event = createToolCallEvent({
178
+ ts: observedAt,
179
+ phase: parsed.type === 'item.started' ? 'started' : 'completed',
180
+ id: item.id,
181
+ tool: item.tool || 'mcp_tool_call',
182
+ server: item.server,
183
+ status: item.status || item.error,
184
+ defaultStatus: parsed.type === 'item.completed' ? 'success' : 'unknown',
185
+ });
186
+ rememberToolCall(event, memory);
187
+ return [event];
188
+ }
24
189
  }
25
190
  return [];
26
191
  }
27
- if (agent === 'claude' && parsed.type === 'assistant' && Array.isArray(parsed.message?.content)) {
28
- return parsed.message.content
29
- .filter((content) => content?.type === 'text' && typeof content.text === 'string' && content.text.trim())
30
- .map((content) => ({ ts: observedAt, text: content.text }));
192
+ if (agent === 'claude') {
193
+ if (parsed.type === 'assistant' && Array.isArray(parsed.message?.content)) {
194
+ const events = [];
195
+ for (const content of parsed.message.content) {
196
+ if (content?.type === 'text' && typeof content.text === 'string' && content.text.trim()) {
197
+ events.push({ kind: 'message', ts: observedAt, text: content.text });
198
+ }
199
+ else if (includeToolCalls && content?.type === 'tool_use') {
200
+ const event = createToolCallEvent({
201
+ ts: observedAt,
202
+ phase: 'started',
203
+ id: content.id,
204
+ tool: content.name || 'tool_use',
205
+ command: content.input?.command,
206
+ });
207
+ rememberToolCall(event, memory);
208
+ events.push(event);
209
+ }
210
+ }
211
+ return events;
212
+ }
213
+ if (includeToolCalls && parsed.type === 'user' && Array.isArray(parsed.message?.content)) {
214
+ const events = [];
215
+ for (const content of parsed.message.content) {
216
+ if (content?.type === 'tool_result') {
217
+ events.push(createRememberedCompletion({
218
+ ts: observedAt,
219
+ id: content.tool_use_id,
220
+ memory,
221
+ fallbackTool: 'tool_result',
222
+ status: content.is_error === true ? 'failed' : undefined,
223
+ defaultStatus: content.is_error === true ? 'failed' : 'success',
224
+ }));
225
+ }
226
+ }
227
+ return events;
228
+ }
229
+ return [];
31
230
  }
32
231
  if (agent === 'opencode' && parsed.type === 'text' && parsed.part?.type === 'text' && typeof parsed.part.text === 'string' && parsed.part.text.trim()) {
33
- return [{ ts: observedAt, text: parsed.part.text }];
232
+ return [{ kind: 'message', ts: observedAt, text: parsed.part.text }];
233
+ }
234
+ if (agent === 'opencode' && includeToolCalls && parsed.type === 'tool_use' && parsed.part?.type === 'tool') {
235
+ const state = parsed.part.state || {};
236
+ const start = state.time?.start;
237
+ const end = state.time?.end;
238
+ const event = createToolCallEvent({
239
+ ts: observedAt,
240
+ phase: state.status === 'running' || state.status === 'pending' ? 'started' : 'completed',
241
+ id: parsed.part.callID,
242
+ tool: parsed.part.tool || 'tool_use',
243
+ command: state.input?.command,
244
+ status: state.status,
245
+ defaultStatus: state.status === 'completed' ? 'success' : 'unknown',
246
+ duration_ms: typeof start === 'number' && typeof end === 'number' ? end - start : undefined,
247
+ });
248
+ rememberToolCall(event, memory);
249
+ return [event];
34
250
  }
35
251
  return [];
36
252
  }
37
- export class PeekMessageExtractor {
253
+ export class PeekEventExtractor {
38
254
  agent;
39
255
  pending = '';
40
256
  geminiAssistantBuffer = '';
41
- constructor(agent) {
257
+ includeToolCalls;
258
+ source;
259
+ toolMemory = new Map();
260
+ forgePendingTool = null;
261
+ forgeToolSequence = 0;
262
+ constructor(agent, options = {}) {
42
263
  this.agent = agent;
264
+ this.includeToolCalls = options.includeToolCalls === true;
265
+ this.source = options.source || 'stdout';
43
266
  }
44
267
  push(chunk, observedAt = new Date().toISOString()) {
268
+ if (this.agent === 'forge' && this.source === 'stderr') {
269
+ return [];
270
+ }
45
271
  if (!chunk) {
46
272
  return [];
47
273
  }
@@ -49,41 +275,129 @@ export class PeekMessageExtractor {
49
275
  this.pending = lines.pop() || '';
50
276
  return this.extractLines(lines, observedAt);
51
277
  }
52
- flush(observedAt = new Date().toISOString()) {
53
- const messages = [];
54
- if (this.pending) {
55
- const line = this.pending;
278
+ flush(observedAt = new Date().toISOString(), options = {}) {
279
+ if (this.agent === 'forge' && this.source === 'stderr') {
56
280
  this.pending = '';
57
- messages.push(...this.extractLines([line], observedAt));
281
+ return [];
58
282
  }
59
- messages.push(...this.flushGeminiAssistantBuffer(observedAt));
60
- return messages;
283
+ const events = [];
284
+ if (this.pending) {
285
+ if (this.agent !== 'forge' || options.terminal === true) {
286
+ const line = this.pending;
287
+ this.pending = '';
288
+ events.push(...this.extractLines([line], observedAt));
289
+ }
290
+ }
291
+ events.push(...this.flushGeminiAssistantBuffer(observedAt));
292
+ events.push(...this.flushForgePendingTool(observedAt, options.terminal === true));
293
+ return events;
61
294
  }
62
295
  extractLines(lines, observedAt) {
63
- const messages = [];
296
+ if (this.agent === 'forge') {
297
+ return this.extractForgeLines(lines, observedAt);
298
+ }
299
+ const events = [];
64
300
  for (const line of lines) {
65
301
  if (!line.trim()) {
66
302
  continue;
67
303
  }
68
304
  try {
69
- messages.push(...this.extractParsedEvent(JSON.parse(line), observedAt));
305
+ events.push(...this.extractParsedEvent(JSON.parse(line), observedAt));
70
306
  }
71
307
  catch {
72
308
  debugLog(`[Debug] Skipping invalid peek JSON line: ${line}`);
73
- messages.push(...this.flushGeminiAssistantBuffer(observedAt));
309
+ events.push(...this.flushGeminiAssistantBuffer(observedAt));
74
310
  }
75
311
  }
76
- return messages;
312
+ return events;
313
+ }
314
+ extractForgeLines(lines, observedAt) {
315
+ const events = [];
316
+ for (const line of lines) {
317
+ if (!line.trim()) {
318
+ continue;
319
+ }
320
+ const summary = this.extractForgeMessage(line, 'Summary:');
321
+ if (summary !== null) {
322
+ events.push({ kind: 'message', ts: observedAt, text: summary });
323
+ continue;
324
+ }
325
+ const completed = this.extractForgeMessage(line, 'Completed successfully:');
326
+ if (completed !== null) {
327
+ events.push({ kind: 'message', ts: observedAt, text: completed });
328
+ continue;
329
+ }
330
+ if (this.includeToolCalls) {
331
+ const executeMatch = line.match(FORGE_EXECUTE_PATTERN);
332
+ if (executeMatch) {
333
+ events.push(...this.completeForgePendingTool(observedAt));
334
+ const [, rawTool, rawSummary] = executeMatch;
335
+ const tool = rawTool.trim() && !/\s/.test(rawTool.trim()) ? rawTool.trim() : 'shell';
336
+ const event = createToolCallEvent({
337
+ ts: observedAt,
338
+ phase: 'started',
339
+ id: `forge_${this.forgeToolSequence++}`,
340
+ tool,
341
+ command: rawSummary,
342
+ });
343
+ this.forgePendingTool = {
344
+ id: event.id,
345
+ tool: event.tool,
346
+ summary: event.summary,
347
+ summary_truncated: event.summary_truncated,
348
+ };
349
+ events.push(event);
350
+ continue;
351
+ }
352
+ if (FORGE_FINISHED_PATTERN.test(line)) {
353
+ events.push(...this.completeForgePendingTool(observedAt));
354
+ }
355
+ }
356
+ }
357
+ return events;
358
+ }
359
+ extractForgeMessage(line, prefix) {
360
+ if (!line.startsWith(prefix)) {
361
+ return null;
362
+ }
363
+ const text = line.slice(prefix.length).trim();
364
+ return text || null;
77
365
  }
78
366
  extractParsedEvent(parsed, observedAt) {
79
- if (this.agent !== 'gemini') {
80
- return extractPeekMessagesFromParsedEvent(this.agent, parsed, observedAt);
367
+ if (this.agent === 'gemini') {
368
+ const events = this.extractGeminiParsedEvent(parsed, observedAt);
369
+ return events;
81
370
  }
371
+ return extractPeekEventsFromParsedEvent(this.agent, parsed, observedAt, this.includeToolCalls, this.toolMemory);
372
+ }
373
+ extractGeminiParsedEvent(parsed, observedAt) {
82
374
  if (isGeminiAssistantMessageEvent(parsed)) {
83
375
  this.geminiAssistantBuffer += parsed.content;
84
376
  return [];
85
377
  }
86
- return this.flushGeminiAssistantBuffer(observedAt);
378
+ const events = this.flushGeminiAssistantBuffer(observedAt);
379
+ if (this.includeToolCalls && parsed.type === 'tool_use') {
380
+ const event = createToolCallEvent({
381
+ ts: observedAt,
382
+ phase: 'started',
383
+ id: parsed.tool_id,
384
+ tool: parsed.tool_name || parsed.name || 'tool_use',
385
+ command: parsed.parameters?.command,
386
+ });
387
+ rememberToolCall(event, this.toolMemory);
388
+ events.push(event);
389
+ }
390
+ else if (this.includeToolCalls && parsed.type === 'tool_result') {
391
+ events.push(createRememberedCompletion({
392
+ ts: observedAt,
393
+ id: parsed.tool_id,
394
+ memory: this.toolMemory,
395
+ fallbackTool: parsed.tool_name || parsed.name || 'tool_result',
396
+ status: parsed.status,
397
+ defaultStatus: 'unknown',
398
+ }));
399
+ }
400
+ return events;
87
401
  }
88
402
  flushGeminiAssistantBuffer(observedAt) {
89
403
  if (this.agent !== 'gemini' || !this.geminiAssistantBuffer) {
@@ -94,7 +408,50 @@ export class PeekMessageExtractor {
94
408
  if (!text.trim()) {
95
409
  return [];
96
410
  }
97
- return [{ ts: observedAt, text }];
411
+ return [{ kind: 'message', ts: observedAt, text }];
412
+ }
413
+ completeForgePendingTool(observedAt) {
414
+ if (!this.forgePendingTool) {
415
+ return [];
416
+ }
417
+ const pending = this.forgePendingTool;
418
+ this.forgePendingTool = null;
419
+ const event = createToolCallEvent({
420
+ ts: observedAt,
421
+ phase: 'completed',
422
+ id: pending.id,
423
+ tool: pending.tool,
424
+ status: 'unknown',
425
+ defaultStatus: 'unknown',
426
+ });
427
+ event.summary = pending.summary;
428
+ if (pending.summary_truncated) {
429
+ event.summary_truncated = true;
430
+ }
431
+ return [event];
432
+ }
433
+ flushForgePendingTool(observedAt, terminal) {
434
+ if (this.agent !== 'forge' || !terminal) {
435
+ return [];
436
+ }
437
+ return this.completeForgePendingTool(observedAt);
438
+ }
439
+ }
440
+ export class PeekMessageExtractor {
441
+ extractor;
442
+ constructor(agent) {
443
+ this.extractor = new PeekEventExtractor(agent, { includeToolCalls: false });
444
+ }
445
+ push(chunk, observedAt = new Date().toISOString()) {
446
+ return this.toMessages(this.extractor.push(chunk, observedAt));
447
+ }
448
+ flush(observedAt = new Date().toISOString(), options = {}) {
449
+ return this.toMessages(this.extractor.flush(observedAt, options));
450
+ }
451
+ toMessages(events) {
452
+ return events
453
+ .filter((event) => event.kind === 'message')
454
+ .map((event) => ({ ts: event.ts, text: event.text }));
98
455
  }
99
456
  }
100
457
  export function parseCodexOutput(stdout) {
package/dist/peek.js CHANGED
@@ -36,21 +36,24 @@ export function buildNotFoundPeekProcess(pid) {
36
36
  pid,
37
37
  agent: null,
38
38
  status: 'not_found',
39
- messages: [],
39
+ events: [],
40
40
  truncated: false,
41
41
  error: 'process not found',
42
42
  };
43
43
  }
44
- export function appendPeekMessages(target, messages) {
45
- for (const message of messages) {
46
- if (target.messages.length < PEEK_MESSAGE_CAP) {
47
- target.messages.push(message);
44
+ export function appendPeekEvents(target, events) {
45
+ for (const event of events) {
46
+ if (target.events.length < PEEK_MESSAGE_CAP) {
47
+ target.events.push(event);
48
48
  }
49
49
  else {
50
50
  target.truncated = true;
51
51
  }
52
52
  }
53
53
  }
54
+ export function appendPeekMessages(target, messages) {
55
+ appendPeekEvents(target, messages.map((message) => ({ kind: 'message', ...message })));
56
+ }
54
57
  export function observedDurationSec(startedAtMs, endedAtMs = Date.now()) {
55
58
  return Number(((endedAtMs - startedAtMs) / 1000).toFixed(2));
56
59
  }
@@ -1,7 +1,7 @@
1
1
  import { spawn } from 'node:child_process';
2
2
  import { buildCliCommand } from './cli-builder.js';
3
- import { parseClaudeOutput, parseCodexOutput, parseForgeOutput, parseGeminiOutput, parseOpenCodeOutput, PeekMessageExtractor } from './parsers.js';
4
- import { appendPeekMessages, buildNotFoundPeekProcess, observedDurationSec, validatePeekPids, validatePeekTimeSec, } from './peek.js';
3
+ import { parseClaudeOutput, parseCodexOutput, parseForgeOutput, parseGeminiOutput, parseOpenCodeOutput, PeekEventExtractor } from './parsers.js';
4
+ import { appendPeekEvents, buildNotFoundPeekProcess, observedDurationSec, validatePeekPids, validatePeekTimeSec, } from './peek.js';
5
5
  import { buildProcessResult } from './process-result.js';
6
6
  function parseAgentOutput(agent, stdout, stderr) {
7
7
  if (agent === 'codex') {
@@ -156,7 +156,7 @@ export class ProcessService {
156
156
  }
157
157
  }
158
158
  }
159
- async peekProcesses(pids, peekTimeSec = 10) {
159
+ async peekProcesses(pids, peekTimeSec = 10, includeToolCalls = false) {
160
160
  const targetPids = validatePeekPids(pids);
161
161
  const targetPeekTimeSec = validatePeekTimeSec(peekTimeSec);
162
162
  const processes = [];
@@ -171,18 +171,18 @@ export class ProcessService {
171
171
  pid,
172
172
  agent: entry.toolType,
173
173
  status: entry.status,
174
- messages: [],
174
+ events: [],
175
175
  truncated: false,
176
176
  error: null,
177
177
  };
178
178
  processes.push(result);
179
- const stdoutExtractor = new PeekMessageExtractor(entry.toolType);
180
- const stderrExtractor = new PeekMessageExtractor(entry.toolType);
179
+ const stdoutExtractor = new PeekEventExtractor(entry.toolType, { includeToolCalls, source: 'stdout' });
180
+ const stderrExtractor = new PeekEventExtractor(entry.toolType, { includeToolCalls, source: 'stderr' });
181
181
  const onStdout = (data) => {
182
- appendPeekMessages(result, stdoutExtractor.push(data.toString(), new Date().toISOString()));
182
+ appendPeekEvents(result, stdoutExtractor.push(data.toString(), new Date().toISOString()));
183
183
  };
184
184
  const onStderr = (data) => {
185
- appendPeekMessages(result, stderrExtractor.push(data.toString(), new Date().toISOString()));
185
+ appendPeekEvents(result, stderrExtractor.push(data.toString(), new Date().toISOString()));
186
186
  };
187
187
  if (entry.status === 'running') {
188
188
  entry.process.stdout?.on('data', onStdout);
@@ -210,8 +210,9 @@ export class ProcessService {
210
210
  for (const observer of observers) {
211
211
  observer.entry.process.stdout?.off('data', observer.onStdout);
212
212
  observer.entry.process.stderr?.off('data', observer.onStderr);
213
- appendPeekMessages(observer.result, observer.stdoutExtractor.flush(flushTs));
214
- appendPeekMessages(observer.result, observer.stderrExtractor.flush(flushTs));
213
+ const terminal = observer.entry.status !== 'running';
214
+ appendPeekEvents(observer.result, observer.stdoutExtractor.flush(flushTs, { terminal }));
215
+ appendPeekEvents(observer.result, observer.stderrExtractor.flush(flushTs, { terminal }));
215
216
  observer.result.status = observer.entry.status;
216
217
  }
217
218
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-cli-mcp",
3
- "version": "2.17.0",
3
+ "version": "2.19.0",
4
4
  "mcpName": "io.github.mkXultra/ai-cli-mcp",
5
5
  "description": "MCP server for AI CLI tools (Claude, Codex, Gemini, Forge, and OpenCode) with background process management",
6
6
  "author": "mkXultra",
@@ -210,7 +210,7 @@ describe('ai-cli app', () => {
210
210
  });
211
211
 
212
212
  const exitCode = await runCli(
213
- ['peek', '123', '456', '123', '--time', '5'],
213
+ ['peek', '123', '456', '123', '--time', '5', '--include-tool-calls'],
214
214
  {
215
215
  stdout,
216
216
  stderr,
@@ -219,7 +219,7 @@ describe('ai-cli app', () => {
219
219
  );
220
220
 
221
221
  expect(exitCode).toBe(0);
222
- expect(peekProcesses).toHaveBeenCalledWith([123, 456], 5);
222
+ expect(peekProcesses).toHaveBeenCalledWith([123, 456], 5, true);
223
223
  expect(stdout).toHaveBeenCalledWith(expect.stringContaining('"peek_started_at"'));
224
224
  expect(stderr).not.toHaveBeenCalled();
225
225
  });
@@ -235,7 +235,7 @@ describe('ai-cli app', () => {
235
235
 
236
236
  const defaultExitCode = await runCli(['peek', '123'], { stdout, stderr, peekProcesses });
237
237
  expect(defaultExitCode).toBe(0);
238
- expect(peekProcesses).toHaveBeenCalledWith([123], 10);
238
+ expect(peekProcesses).toHaveBeenCalledWith([123], 10, false);
239
239
 
240
240
  const followExitCode = await runCli(['peek', '123', '--follow'], { stdout, stderr, peekProcesses });
241
241
  expect(followExitCode).toBe(1);
@@ -171,8 +171,9 @@ printf '%s\n' '{"type":"user","message":{"content":[{"type":"tool_result","tool_
171
171
  pid: runResult.pid,
172
172
  agent: 'claude',
173
173
  status: 'completed',
174
- messages: [
174
+ events: [
175
175
  {
176
+ kind: 'message',
176
177
  ts: expect.any(String),
177
178
  text: 'new cli message',
178
179
  },
@@ -184,7 +185,7 @@ printf '%s\n' '{"type":"user","message":{"content":[{"type":"tool_result","tool_
184
185
  pid: 999999,
185
186
  agent: null,
186
187
  status: 'not_found',
187
- messages: [],
188
+ events: [],
188
189
  truncated: false,
189
190
  error: 'process not found',
190
191
  });
@@ -137,6 +137,7 @@ describe('MCP Contract Tests', () => {
137
137
  const peekTool = tools.find((tool: any) => tool.name === 'peek');
138
138
  expect(peekTool.inputSchema.required).toEqual(['pids']);
139
139
  expect(Object.keys(peekTool.inputSchema.properties).sort()).toEqual([
140
+ 'include_tool_calls',
140
141
  'peek_time_sec',
141
142
  'pids',
142
143
  ]);