ai-cli-mcp 2.16.0 → 2.18.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.
@@ -4,9 +4,9 @@ import { join, basename, dirname } from 'node:path';
4
4
  import { homedir } from 'node:os';
5
5
  import { buildCliCommand } from './cli-builder.js';
6
6
  import { findClaudeCli, findCodexCli, findForgeCli, findGeminiCli, findOpencodeCli } from './cli-utils.js';
7
- import { parseClaudeOutput, parseCodexOutput, parseForgeOutput, parseGeminiOutput, parseOpenCodeOutput, PeekMessageExtractor } from './parsers.js';
7
+ import { parseClaudeOutput, parseCodexOutput, parseForgeOutput, parseGeminiOutput, parseOpenCodeOutput, PeekEventExtractor } from './parsers.js';
8
8
  import { buildProcessResult } from './process-result.js';
9
- import { appendPeekMessages, buildNotFoundPeekProcess, observedDurationSec, validatePeekPids, validatePeekTimeSec, } from './peek.js';
9
+ import { appendPeekEvents, buildNotFoundPeekProcess, observedDurationSec, validatePeekPids, validatePeekTimeSec, } from './peek.js';
10
10
  function resolveDefaultStateDir() {
11
11
  return process.env.AI_CLI_STATE_DIR || join(homedir(), '.local', 'state', 'ai-cli');
12
12
  }
@@ -175,7 +175,7 @@ export class CliProcessService {
175
175
  await new Promise((resolve) => setTimeout(resolve, 50));
176
176
  }
177
177
  }
178
- async peekProcesses(pids, peekTimeSec = 10) {
178
+ async peekProcesses(pids, peekTimeSec = 10, includeToolCalls = false) {
179
179
  const targetPids = validatePeekPids(pids);
180
180
  const targetPeekTimeSec = validatePeekTimeSec(peekTimeSec);
181
181
  const processes = [];
@@ -193,7 +193,7 @@ export class CliProcessService {
193
193
  pid,
194
194
  agent: process.toolType,
195
195
  status: process.status,
196
- messages: [],
196
+ events: [],
197
197
  truncated: false,
198
198
  error: null,
199
199
  };
@@ -201,8 +201,8 @@ export class CliProcessService {
201
201
  observers.push({
202
202
  process,
203
203
  result,
204
- stdoutExtractor: new PeekMessageExtractor(process.toolType),
205
- stderrExtractor: new PeekMessageExtractor(process.toolType),
204
+ stdoutExtractor: new PeekEventExtractor(process.toolType, { includeToolCalls }),
205
+ stderrExtractor: new PeekEventExtractor(process.toolType, { includeToolCalls }),
206
206
  stdoutOffset: this.fileSizeSafe(process.stdoutPath),
207
207
  stderrOffset: this.fileSizeSafe(process.stderrPath),
208
208
  });
@@ -216,10 +216,10 @@ export class CliProcessService {
216
216
  for (const observer of observers) {
217
217
  const stdoutRead = this.readTextFromOffset(observer.process.stdoutPath, observer.stdoutOffset);
218
218
  observer.stdoutOffset = stdoutRead.offset;
219
- appendPeekMessages(observer.result, observer.stdoutExtractor.push(stdoutRead.text, observedAt));
219
+ appendPeekEvents(observer.result, observer.stdoutExtractor.push(stdoutRead.text, observedAt));
220
220
  const stderrRead = this.readTextFromOffset(observer.process.stderrPath, observer.stderrOffset);
221
221
  observer.stderrOffset = stderrRead.offset;
222
- appendPeekMessages(observer.result, observer.stderrExtractor.push(stderrRead.text, observedAt));
222
+ appendPeekEvents(observer.result, observer.stderrExtractor.push(stderrRead.text, observedAt));
223
223
  observer.process = this.refreshStatus(this.readProcess(observer.process.pid));
224
224
  observer.result.status = observer.process.status;
225
225
  if (observer.process.status === 'running') {
@@ -239,8 +239,8 @@ export class CliProcessService {
239
239
  for (const observer of observers) {
240
240
  observer.process = this.refreshStatus(this.readProcess(observer.process.pid));
241
241
  observer.result.status = observer.process.status;
242
- appendPeekMessages(observer.result, observer.stdoutExtractor.flush(flushTs));
243
- appendPeekMessages(observer.result, observer.stderrExtractor.flush(flushTs));
242
+ appendPeekEvents(observer.result, observer.stdoutExtractor.flush(flushTs));
243
+ appendPeekEvents(observer.result, observer.stderrExtractor.flush(flushTs));
244
244
  }
245
245
  return {
246
246
  peek_started_at: startedAt.toISOString(),
package/dist/parsers.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { debugLog } from './cli-utils.js';
2
+ const PEEK_TOOL_SUMMARY_MAX_LENGTH = 200;
2
3
  function isGeminiAssistantMessageEvent(parsed) {
3
4
  return parsed.type === 'message' && parsed.role === 'assistant' && typeof parsed.content === 'string';
4
5
  }
@@ -14,32 +15,248 @@ const GEMINI_STREAM_EVENT_TYPES = new Set([
14
15
  function isGeminiStreamJsonEvent(parsed) {
15
16
  return parsed && typeof parsed === 'object' && !Array.isArray(parsed) && GEMINI_STREAM_EVENT_TYPES.has(parsed.type);
16
17
  }
17
- function extractPeekMessagesFromParsedEvent(agent, parsed, observedAt) {
18
+ function oneLine(value) {
19
+ return String(value ?? '').replace(/\s+/g, ' ').trim();
20
+ }
21
+ function boundedSummary(value) {
22
+ const summary = oneLine(value);
23
+ if (summary.length <= PEEK_TOOL_SUMMARY_MAX_LENGTH) {
24
+ return { summary };
25
+ }
26
+ return {
27
+ summary: `${summary.slice(0, PEEK_TOOL_SUMMARY_MAX_LENGTH - 3)}...`,
28
+ summary_truncated: true,
29
+ };
30
+ }
31
+ function normalizeMcpToolName(tool, explicitServer) {
32
+ if (explicitServer) {
33
+ return {
34
+ server: explicitServer,
35
+ ...boundedSummary(`${explicitServer}.${tool}`),
36
+ };
37
+ }
38
+ const mcpDouble = tool.match(/^mcp__([^_]+)__(.+)$/);
39
+ if (mcpDouble) {
40
+ return {
41
+ server: mcpDouble[1],
42
+ ...boundedSummary(`${mcpDouble[1]}.${mcpDouble[2]}`),
43
+ };
44
+ }
45
+ const mcpSingle = tool.match(/^mcp_([^_]+)_(.+)$/);
46
+ if (mcpSingle) {
47
+ return {
48
+ server: mcpSingle[1],
49
+ ...boundedSummary(`${mcpSingle[1]}.${mcpSingle[2]}`),
50
+ };
51
+ }
52
+ const acmShort = tool.match(/^acm_(.+)$/);
53
+ if (acmShort) {
54
+ return {
55
+ server: 'acm',
56
+ ...boundedSummary(`acm.${acmShort[1]}`),
57
+ };
58
+ }
59
+ return null;
60
+ }
61
+ function buildToolSummary(tool, options = {}) {
62
+ if (typeof options.command === 'string' && options.command.trim()) {
63
+ return boundedSummary(options.command);
64
+ }
65
+ const mcpSummary = normalizeMcpToolName(tool, options.server);
66
+ if (mcpSummary) {
67
+ return mcpSummary;
68
+ }
69
+ return boundedSummary(tool || 'tool_call');
70
+ }
71
+ function normalizeToolStatus(rawStatus, exitCode, defaultStatus = 'unknown') {
72
+ if (typeof exitCode === 'number') {
73
+ return exitCode === 0 ? 'success' : 'failed';
74
+ }
75
+ const status = typeof rawStatus === 'string' ? rawStatus.toLowerCase() : '';
76
+ if (['success', 'succeeded', 'ok', 'completed'].includes(status)) {
77
+ return 'success';
78
+ }
79
+ if (['failed', 'failure', 'error', 'errored'].includes(status)) {
80
+ return 'failed';
81
+ }
82
+ if (['cancelled', 'canceled'].includes(status)) {
83
+ return 'cancelled';
84
+ }
85
+ return defaultStatus;
86
+ }
87
+ function createToolCallEvent(params) {
88
+ const tool = params.tool || 'tool_call';
89
+ const summary = buildToolSummary(tool, { server: params.server, command: params.command });
90
+ const event = {
91
+ kind: 'tool_call',
92
+ ts: params.ts,
93
+ phase: params.phase,
94
+ tool,
95
+ summary: summary.summary,
96
+ };
97
+ if (params.id) {
98
+ event.id = params.id;
99
+ }
100
+ if (summary.server) {
101
+ event.server = summary.server;
102
+ }
103
+ else if (params.server) {
104
+ event.server = params.server;
105
+ }
106
+ if (summary.summary_truncated) {
107
+ event.summary_truncated = true;
108
+ }
109
+ if (params.phase === 'completed') {
110
+ event.status = normalizeToolStatus(params.status, params.exit_code, params.defaultStatus);
111
+ if (typeof params.exit_code === 'number') {
112
+ event.exit_code = params.exit_code;
113
+ }
114
+ if (typeof params.duration_ms === 'number' && Number.isFinite(params.duration_ms)) {
115
+ event.duration_ms = params.duration_ms;
116
+ }
117
+ }
118
+ return event;
119
+ }
120
+ function rememberToolCall(event, memory) {
121
+ if (event.kind !== 'tool_call' || !event.id) {
122
+ return;
123
+ }
124
+ memory.set(event.id, {
125
+ tool: event.tool,
126
+ server: event.server,
127
+ summary: event.summary,
128
+ summary_truncated: event.summary_truncated,
129
+ });
130
+ }
131
+ function createRememberedCompletion(params) {
132
+ const remembered = params.id ? params.memory.get(params.id) : undefined;
133
+ const event = createToolCallEvent({
134
+ ts: params.ts,
135
+ phase: 'completed',
136
+ id: params.id,
137
+ tool: remembered?.tool || params.fallbackTool,
138
+ server: remembered?.server,
139
+ status: params.status,
140
+ defaultStatus: params.defaultStatus,
141
+ });
142
+ if (remembered) {
143
+ event.summary = remembered.summary;
144
+ if (remembered.summary_truncated) {
145
+ event.summary_truncated = true;
146
+ }
147
+ }
148
+ return event;
149
+ }
150
+ function extractPeekEventsFromParsedEvent(agent, parsed, observedAt, includeToolCalls, memory) {
18
151
  if (agent === 'codex') {
19
152
  if (parsed.item?.type === 'agent_message' && typeof parsed.item.text === 'string' && parsed.item.text.trim()) {
20
- return [{ ts: observedAt, text: parsed.item.text }];
153
+ return [{ kind: 'message', ts: observedAt, text: parsed.item.text }];
21
154
  }
22
155
  if (parsed.msg?.type === 'agent_message' && typeof parsed.msg.message === 'string' && parsed.msg.message.trim()) {
23
- return [{ ts: observedAt, text: parsed.msg.message }];
156
+ return [{ kind: 'message', ts: observedAt, text: parsed.msg.message }];
157
+ }
158
+ if (includeToolCalls && (parsed.type === 'item.started' || parsed.type === 'item.completed')) {
159
+ const item = parsed.item;
160
+ if (item?.type === 'command_execution') {
161
+ const event = createToolCallEvent({
162
+ ts: observedAt,
163
+ phase: parsed.type === 'item.started' ? 'started' : 'completed',
164
+ id: item.id,
165
+ tool: 'command_execution',
166
+ command: item.command,
167
+ status: item.status || item.error,
168
+ exit_code: typeof item.exit_code === 'number' ? item.exit_code : undefined,
169
+ defaultStatus: parsed.type === 'item.completed' ? 'success' : 'unknown',
170
+ });
171
+ rememberToolCall(event, memory);
172
+ return [event];
173
+ }
174
+ if (item?.type === 'mcp_tool_call') {
175
+ const event = createToolCallEvent({
176
+ ts: observedAt,
177
+ phase: parsed.type === 'item.started' ? 'started' : 'completed',
178
+ id: item.id,
179
+ tool: item.tool || 'mcp_tool_call',
180
+ server: item.server,
181
+ status: item.status || item.error,
182
+ defaultStatus: parsed.type === 'item.completed' ? 'success' : 'unknown',
183
+ });
184
+ rememberToolCall(event, memory);
185
+ return [event];
186
+ }
24
187
  }
25
188
  return [];
26
189
  }
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 }));
190
+ if (agent === 'claude') {
191
+ if (parsed.type === 'assistant' && Array.isArray(parsed.message?.content)) {
192
+ const events = [];
193
+ for (const content of parsed.message.content) {
194
+ if (content?.type === 'text' && typeof content.text === 'string' && content.text.trim()) {
195
+ events.push({ kind: 'message', ts: observedAt, text: content.text });
196
+ }
197
+ else if (includeToolCalls && content?.type === 'tool_use') {
198
+ const event = createToolCallEvent({
199
+ ts: observedAt,
200
+ phase: 'started',
201
+ id: content.id,
202
+ tool: content.name || 'tool_use',
203
+ command: content.input?.command,
204
+ });
205
+ rememberToolCall(event, memory);
206
+ events.push(event);
207
+ }
208
+ }
209
+ return events;
210
+ }
211
+ if (includeToolCalls && parsed.type === 'user' && Array.isArray(parsed.message?.content)) {
212
+ const events = [];
213
+ for (const content of parsed.message.content) {
214
+ if (content?.type === 'tool_result') {
215
+ events.push(createRememberedCompletion({
216
+ ts: observedAt,
217
+ id: content.tool_use_id,
218
+ memory,
219
+ fallbackTool: 'tool_result',
220
+ status: content.is_error === true ? 'failed' : undefined,
221
+ defaultStatus: content.is_error === true ? 'failed' : 'success',
222
+ }));
223
+ }
224
+ }
225
+ return events;
226
+ }
227
+ return [];
31
228
  }
32
229
  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 }];
230
+ return [{ kind: 'message', ts: observedAt, text: parsed.part.text }];
231
+ }
232
+ if (agent === 'opencode' && includeToolCalls && parsed.type === 'tool_use' && parsed.part?.type === 'tool') {
233
+ const state = parsed.part.state || {};
234
+ const start = state.time?.start;
235
+ const end = state.time?.end;
236
+ const event = createToolCallEvent({
237
+ ts: observedAt,
238
+ phase: state.status === 'running' || state.status === 'pending' ? 'started' : 'completed',
239
+ id: parsed.part.callID,
240
+ tool: parsed.part.tool || 'tool_use',
241
+ command: state.input?.command,
242
+ status: state.status,
243
+ defaultStatus: state.status === 'completed' ? 'success' : 'unknown',
244
+ duration_ms: typeof start === 'number' && typeof end === 'number' ? end - start : undefined,
245
+ });
246
+ rememberToolCall(event, memory);
247
+ return [event];
34
248
  }
35
249
  return [];
36
250
  }
37
- export class PeekMessageExtractor {
251
+ export class PeekEventExtractor {
38
252
  agent;
39
253
  pending = '';
40
254
  geminiAssistantBuffer = '';
41
- constructor(agent) {
255
+ includeToolCalls;
256
+ toolMemory = new Map();
257
+ constructor(agent, options = {}) {
42
258
  this.agent = agent;
259
+ this.includeToolCalls = options.includeToolCalls === true;
43
260
  }
44
261
  push(chunk, observedAt = new Date().toISOString()) {
45
262
  if (!chunk) {
@@ -50,40 +267,66 @@ export class PeekMessageExtractor {
50
267
  return this.extractLines(lines, observedAt);
51
268
  }
52
269
  flush(observedAt = new Date().toISOString()) {
53
- const messages = [];
270
+ const events = [];
54
271
  if (this.pending) {
55
272
  const line = this.pending;
56
273
  this.pending = '';
57
- messages.push(...this.extractLines([line], observedAt));
274
+ events.push(...this.extractLines([line], observedAt));
58
275
  }
59
- messages.push(...this.flushGeminiAssistantBuffer(observedAt));
60
- return messages;
276
+ events.push(...this.flushGeminiAssistantBuffer(observedAt));
277
+ return events;
61
278
  }
62
279
  extractLines(lines, observedAt) {
63
- const messages = [];
280
+ const events = [];
64
281
  for (const line of lines) {
65
282
  if (!line.trim()) {
66
283
  continue;
67
284
  }
68
285
  try {
69
- messages.push(...this.extractParsedEvent(JSON.parse(line), observedAt));
286
+ events.push(...this.extractParsedEvent(JSON.parse(line), observedAt));
70
287
  }
71
288
  catch {
72
289
  debugLog(`[Debug] Skipping invalid peek JSON line: ${line}`);
73
- messages.push(...this.flushGeminiAssistantBuffer(observedAt));
290
+ events.push(...this.flushGeminiAssistantBuffer(observedAt));
74
291
  }
75
292
  }
76
- return messages;
293
+ return events;
77
294
  }
78
295
  extractParsedEvent(parsed, observedAt) {
79
- if (this.agent !== 'gemini') {
80
- return extractPeekMessagesFromParsedEvent(this.agent, parsed, observedAt);
296
+ if (this.agent === 'gemini') {
297
+ const events = this.extractGeminiParsedEvent(parsed, observedAt);
298
+ return events;
81
299
  }
300
+ return extractPeekEventsFromParsedEvent(this.agent, parsed, observedAt, this.includeToolCalls, this.toolMemory);
301
+ }
302
+ extractGeminiParsedEvent(parsed, observedAt) {
82
303
  if (isGeminiAssistantMessageEvent(parsed)) {
83
304
  this.geminiAssistantBuffer += parsed.content;
84
305
  return [];
85
306
  }
86
- return this.flushGeminiAssistantBuffer(observedAt);
307
+ const events = this.flushGeminiAssistantBuffer(observedAt);
308
+ if (this.includeToolCalls && parsed.type === 'tool_use') {
309
+ const event = createToolCallEvent({
310
+ ts: observedAt,
311
+ phase: 'started',
312
+ id: parsed.tool_id,
313
+ tool: parsed.tool_name || parsed.name || 'tool_use',
314
+ command: parsed.parameters?.command,
315
+ });
316
+ rememberToolCall(event, this.toolMemory);
317
+ events.push(event);
318
+ }
319
+ else if (this.includeToolCalls && parsed.type === 'tool_result') {
320
+ events.push(createRememberedCompletion({
321
+ ts: observedAt,
322
+ id: parsed.tool_id,
323
+ memory: this.toolMemory,
324
+ fallbackTool: parsed.tool_name || parsed.name || 'tool_result',
325
+ status: parsed.status,
326
+ defaultStatus: 'unknown',
327
+ }));
328
+ }
329
+ return events;
87
330
  }
88
331
  flushGeminiAssistantBuffer(observedAt) {
89
332
  if (this.agent !== 'gemini' || !this.geminiAssistantBuffer) {
@@ -94,7 +337,24 @@ export class PeekMessageExtractor {
94
337
  if (!text.trim()) {
95
338
  return [];
96
339
  }
97
- return [{ ts: observedAt, text }];
340
+ return [{ kind: 'message', ts: observedAt, text }];
341
+ }
342
+ }
343
+ export class PeekMessageExtractor {
344
+ extractor;
345
+ constructor(agent) {
346
+ this.extractor = new PeekEventExtractor(agent, { includeToolCalls: false });
347
+ }
348
+ push(chunk, observedAt = new Date().toISOString()) {
349
+ return this.toMessages(this.extractor.push(chunk, observedAt));
350
+ }
351
+ flush(observedAt = new Date().toISOString()) {
352
+ return this.toMessages(this.extractor.flush(observedAt));
353
+ }
354
+ toMessages(events) {
355
+ return events
356
+ .filter((event) => event.kind === 'message')
357
+ .map((event) => ({ ts: event.ts, text: event.text }));
98
358
  }
99
359
  }
100
360
  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 });
180
+ const stderrExtractor = new PeekEventExtractor(entry.toolType, { includeToolCalls });
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,8 @@ 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
+ appendPeekEvents(observer.result, observer.stdoutExtractor.flush(flushTs));
214
+ appendPeekEvents(observer.result, observer.stderrExtractor.flush(flushTs));
215
215
  observer.result.status = observer.entry.status;
216
216
  }
217
217
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-cli-mcp",
3
- "version": "2.16.0",
3
+ "version": "2.18.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);
@@ -314,7 +314,8 @@ describe('cli-builder', () => {
314
314
  expect(cmd.agent).toBe('codex');
315
315
  expect(cmd.cliPath).toBe('/usr/bin/codex');
316
316
  expect(cmd.args).toContain('exec');
317
- expect(cmd.args).toContain('--full-auto');
317
+ expect(cmd.args).toContain('--dangerously-bypass-approvals-and-sandbox');
318
+ expect(cmd.args).not.toContain('--full-auto');
318
319
  expect(cmd.args).toContain('--json');
319
320
  expect(cmd.args).toContain('--model');
320
321
  expect(cmd.args).toContain('gpt-5.2-codex');
@@ -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
  ]);