@bahulam/code 2.6.13 → 2.6.15

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.
@@ -13,9 +13,11 @@
13
13
 
14
14
  import { paint } from './palette.mjs';
15
15
  import { icon, toolFamily } from './icons.mjs';
16
- import { toolDisplayLabel } from '../terminal/tool-display.mjs';
16
+ import { shellCommandProfile, toolDisplayLabel, toolDisplaySummary } from '../terminal/tool-display.mjs';
17
+ import { isSensitiveConfigPath } from '../core/safety.mjs';
17
18
 
18
19
  const MAX_DETAIL_LINES = 60;
20
+ const MAX_SHELL_DETAIL_LINES = 220;
19
21
  const MAX_LINE_WIDTH = 220;
20
22
 
21
23
  // ── Dispatch ─────────────────────────────────────────────────────────────
@@ -49,6 +51,10 @@ function renderBody(card) {
49
51
  case 'validate_file':
50
52
  case 'validate_structure': return detailValidator(card);
51
53
  case 'plan': return detailPlan(card);
54
+ case 'Agent':
55
+ case 'agent':
56
+ case 'task': return detailAgent(card);
57
+ case 'sub_agent_tools': return detailSubAgentTools(card);
52
58
  case 'explore':
53
59
  case 'verify':
54
60
  case 'debug':
@@ -96,6 +102,13 @@ function oneLineArgs(tool, args) {
96
102
  }
97
103
 
98
104
  function safeDetailArgs(tool, args) {
105
+ if (tool === 'shell') {
106
+ const profile = shellCommandProfile(args?.command || args?.cmd || '');
107
+ return {
108
+ command: profile.summary,
109
+ ...(profile.cwdLabel ? { cwd: profile.cwdLabel } : {}),
110
+ };
111
+ }
99
112
  if (tool === 'write_file') {
100
113
  const { content, ...rest } = args || {};
101
114
  return {
@@ -112,8 +125,24 @@ function safeDetailArgs(tool, args) {
112
125
  })),
113
126
  };
114
127
  }
128
+ if (tool === 'Agent' || tool === 'agent' || tool === 'task') {
129
+ const prompt = args?.prompt || args?.task || args?.query || args?.description || args?.instruction || '';
130
+ return {
131
+ ...(args?.subagent_type ? { subagent_type: args.subagent_type } : {}),
132
+ ...(args?.agent ? { agent: args.agent } : {}),
133
+ ...(args?.name ? { name: args.name } : {}),
134
+ ...(Array.isArray(args?.allowed_tools) ? { allowed_tools: args.allowed_tools } : {}),
135
+ ...(prompt ? { prompt: `[${String(prompt).split('\n').length} lines] ${String(prompt).split('\n').find(Boolean)?.slice(0, 80) || ''}` } : {}),
136
+ };
137
+ }
115
138
  if (tool === 'edit_file') {
116
139
  const next = { ...args };
140
+ if (isSensitiveConfigPath(next.file_path || next.path)) {
141
+ for (const key of ['search', 'replace', 'old_string', 'new_string']) {
142
+ if (typeof next[key] === 'string') next[key] = '[redacted]';
143
+ }
144
+ return next;
145
+ }
117
146
  for (const key of ['search', 'replace', 'old_string', 'new_string']) {
118
147
  if (typeof next[key] === 'string' && next[key].length > 80) {
119
148
  next[key] = `[${next[key].split('\n').length} lines omitted]`;
@@ -191,7 +220,16 @@ function detailListFiles(card) {
191
220
  // ── Write / edit ────────────────────────────────────────────────────────
192
221
 
193
222
  function detailEditFile(card) {
194
- const diff = card.result?.file_diff?.unified || card.result?.diff || card.result?.patch || card.result?.output;
223
+ const redacted = redactedFileDiff(card.result?.file_diff)
224
+ || (isSensitiveConfigPath(card.args?.file_path || card.args?.path)
225
+ ? sensitiveFallbackDiff(card)
226
+ : null);
227
+ if (redacted) return renderRedactedDiff(redacted);
228
+
229
+ const diff = unifiedForFileDiff(card.result?.file_diff)
230
+ || card.result?.diff
231
+ || card.result?.patch
232
+ || card.result?.output;
195
233
  if (diff) return renderDiff(String(diff));
196
234
 
197
235
  const before = card.args?.search;
@@ -206,7 +244,13 @@ function detailEditFile(card) {
206
244
  }
207
245
 
208
246
  function detailWriteFile(card) {
209
- const diff = card.result?.file_diff?.unified || card.result?.diff;
247
+ const redacted = redactedFileDiff(card.result?.file_diff)
248
+ || (isSensitiveConfigPath(card.args?.file_path || card.args?.path)
249
+ ? sensitiveFallbackDiff(card)
250
+ : null);
251
+ if (redacted) return renderRedactedDiff(redacted);
252
+
253
+ const diff = unifiedForFileDiff(card.result?.file_diff) || card.result?.diff;
210
254
  if (diff) return renderDiff(String(diff));
211
255
  const content = card.args?.content;
212
256
  if (!content) return paint.text.dim(' (no content)');
@@ -216,7 +260,12 @@ function detailWriteFile(card) {
216
260
  function detailWriteProject(card) {
217
261
  const diffs = card.result?.file_diffs || [];
218
262
  if (diffs.length) {
219
- return renderDiff(diffs.map(diff => diff.unified).filter(Boolean).join('\n'));
263
+ return diffs.map(diff => {
264
+ const redacted = redactedFileDiff(diff);
265
+ if (redacted) return renderRedactedDiff(redacted);
266
+ const unified = unifiedForFileDiff(diff);
267
+ return unified ? renderDiff(unified) : '';
268
+ }).filter(Boolean).join('\n');
220
269
  }
221
270
  const files = card.args?.files || [];
222
271
  if (!files.length) return paint.text.dim(' (no files)');
@@ -227,6 +276,26 @@ function detailWriteProject(card) {
227
276
  }).join('\n');
228
277
  }
229
278
 
279
+ function redactedFileDiff(diff) {
280
+ return diff?.redacted ? diff : null;
281
+ }
282
+
283
+ function sensitiveFallbackDiff(card) {
284
+ return {
285
+ relative_path: card.args?.file_path || card.args?.path || 'sensitive config',
286
+ lines_added: card.result?.lines_added,
287
+ lines_removed: card.result?.lines_removed,
288
+ redacted: true,
289
+ };
290
+ }
291
+
292
+ function renderRedactedDiff(diff = {}) {
293
+ const file = diff.relative_path || diff.path || 'sensitive config';
294
+ const add = diff.lines_added ?? 0;
295
+ const rem = diff.lines_removed ?? 0;
296
+ return ` ${paint.brand.primary(file)} ${paint.text.dim(`+${add} −${rem}`)}\n ${paint.text.dim('diff redacted for sensitive config')}`;
297
+ }
298
+
230
299
  function detailDeleteFile(card) {
231
300
  const p = card.args?.file_path || card.args?.path || '';
232
301
  return ` ${paint.state.danger('✗')} ${paint.text.primary(p)}`;
@@ -235,17 +304,37 @@ function detailDeleteFile(card) {
235
304
  // ── Shell / validators ──────────────────────────────────────────────────
236
305
 
237
306
  function detailShell(card) {
307
+ const command = String(card.args?.command || card.args?.cmd || '').trim();
238
308
  const stdout = String(card.result?.stdout ?? card.result?.output ?? '');
239
309
  const stderr = String(card.result?.stderr ?? '');
240
310
  const out = [];
311
+ if (command) {
312
+ const profile = shellCommandProfile(command);
313
+ if (profile.cwdLabel) {
314
+ out.push(paint.text.dim(' cwd'));
315
+ out.push(` ${paint.brand.data(profile.cwdLabel)}`);
316
+ out.push('');
317
+ }
318
+
319
+ out.push(paint.text.dim(' command'));
320
+ if (profile.script?.body) {
321
+ out.push(` ${paint.text.primary(profile.script.invocation || profile.command.split('\n')[0] || profile.command)}`);
322
+ out.push('');
323
+ out.push(paint.text.dim(' script'));
324
+ out.push(numbered(profile.script.body, 1, { maxLines: MAX_SHELL_DETAIL_LINES }));
325
+ } else {
326
+ out.push(clip(profile.command, paint.text.primary, { maxLines: MAX_SHELL_DETAIL_LINES }));
327
+ }
328
+ }
241
329
  if (stdout) {
330
+ if (out.length) out.push('');
242
331
  out.push(paint.text.dim(' stdout'));
243
- out.push(clip(stdout));
332
+ out.push(clip(stdout, paint.text.primary, { maxLines: MAX_DETAIL_LINES }));
244
333
  }
245
334
  if (stderr) {
246
335
  if (out.length) out.push('');
247
336
  out.push(paint.state.warn(' stderr'));
248
- out.push(clip(stderr, paint.state.danger));
337
+ out.push(clip(stderr, paint.state.danger, { maxLines: MAX_DETAIL_LINES }));
249
338
  }
250
339
  return out.length ? out.join('\n') : paint.text.dim(' (no output)');
251
340
  }
@@ -267,6 +356,56 @@ function detailPlan(card) {
267
356
  }).join('\n');
268
357
  }
269
358
 
359
+ function detailAgent(card) {
360
+ const args = card.args || {};
361
+ const prompt = args.prompt || args.task || args.query || args.description || args.instruction || '';
362
+ const out = [];
363
+ const meta = [
364
+ args.subagent_type ? `type ${args.subagent_type}` : '',
365
+ args.agent || args.name || '',
366
+ Array.isArray(args.allowed_tools) && args.allowed_tools.length
367
+ ? `${args.allowed_tools.length} allowed tools`
368
+ : '',
369
+ ].filter(Boolean).join(' · ');
370
+ if (meta) out.push(` ${paint.text.dim(meta)}`);
371
+ if (prompt) {
372
+ out.push(paint.text.dim(' prompt'));
373
+ out.push(clip(prompt, paint.text.primary, { maxLines: MAX_DETAIL_LINES }));
374
+ }
375
+ const result = String(card.result?.output ?? card.result?.output_preview ?? '');
376
+ if (result) {
377
+ if (out.length) out.push('');
378
+ out.push(paint.text.dim(' result'));
379
+ out.push(clip(result));
380
+ }
381
+ return out.length ? out.join('\n') : detailGenericOutput(card);
382
+ }
383
+
384
+ function detailSubAgentTools(card) {
385
+ const entries = Array.isArray(card.result?.tools) ? card.result.tools : [];
386
+ if (!entries.length) return paint.text.dim(' (no folded tool calls)');
387
+
388
+ const out = [];
389
+ entries.forEach((entry, index) => {
390
+ const tool = entry.tool || 'tool';
391
+ const args = entry.args || {};
392
+ const summary = entry.summary || toolDisplaySummary(tool, args);
393
+ const outcome = entry.outcome ? ` ${paint.text.dim('—')} ${paint.text.muted(entry.outcome)}` : '';
394
+ const duration = entry.durationMs != null ? ` ${paint.text.dim('· ' + formatDuration(entry.durationMs))}` : '';
395
+ out.push(` ${paint.text.dim(`${index + 1}.`)} ${icon(tool)} ${toolDisplayLabel(tool)}${summary ? ` ${paint.text.muted(summary)}` : ''}${outcome}${duration}`);
396
+
397
+ const child = {
398
+ tool,
399
+ args,
400
+ result: entry.result || null,
401
+ durationMs: entry.durationMs ?? null,
402
+ };
403
+ const detail = renderBody(child);
404
+ if (detail) out.push(indentBlock(detail, ' '));
405
+ });
406
+ return out.join('\n');
407
+ }
408
+
270
409
  function detailGenericOutput(card) {
271
410
  const out = String(card.result?.output ?? card.result?.output_preview ?? '');
272
411
  return clip(out);
@@ -274,28 +413,35 @@ function detailGenericOutput(card) {
274
413
 
275
414
  // ── Helpers ─────────────────────────────────────────────────────────────
276
415
 
277
- function numbered(text, start) {
416
+ function numbered(text, start, { maxLines = MAX_DETAIL_LINES } = {}) {
278
417
  const lines = String(text).split('\n');
279
418
  const total = lines.length;
280
419
  const width = String(start + total - 1).length;
281
- return lines.slice(0, MAX_DETAIL_LINES).map((line, i) => {
420
+ return lines.slice(0, maxLines).map((line, i) => {
282
421
  const n = String(start + i).padStart(width);
283
422
  return ` ${paint.text.dim(n)} ${paint.text.primary(line.slice(0, MAX_LINE_WIDTH))}`;
284
- }).join('\n') + (total > MAX_DETAIL_LINES
285
- ? `\n ${paint.text.dim(`… ${total - MAX_DETAIL_LINES} more line(s)`)}`
423
+ }).join('\n') + (total > maxLines
424
+ ? `\n ${paint.text.dim(`… ${total - maxLines} more line(s)`)}`
286
425
  : '');
287
426
  }
288
427
 
289
- function clip(text, painter = paint.text.primary) {
428
+ function clip(text, painter = paint.text.primary, { maxLines = MAX_DETAIL_LINES } = {}) {
290
429
  if (!text) return paint.text.dim(' (empty)');
291
430
  const lines = String(text).split('\n');
292
- const head = lines.slice(0, MAX_DETAIL_LINES).map(l => ` ${painter(l.slice(0, MAX_LINE_WIDTH))}`);
293
- if (lines.length > MAX_DETAIL_LINES) {
294
- head.push(` ${paint.text.dim(`… ${lines.length - MAX_DETAIL_LINES} more line(s)`)}`);
431
+ const head = lines.slice(0, maxLines).map(l => ` ${painter(l.slice(0, MAX_LINE_WIDTH))}`);
432
+ if (lines.length > maxLines) {
433
+ head.push(` ${paint.text.dim(`… ${lines.length - maxLines} more line(s)`)}`);
295
434
  }
296
435
  return head.join('\n');
297
436
  }
298
437
 
438
+ function indentBlock(text, prefix) {
439
+ return String(text || '')
440
+ .split('\n')
441
+ .map(line => `${prefix}${line.trimStart()}`)
442
+ .join('\n');
443
+ }
444
+
299
445
  function renderDiff(text) {
300
446
  return text.split('\n').slice(0, MAX_DETAIL_LINES).map(line => {
301
447
  if (line.startsWith('+++') || line.startsWith('---')) return ` ${paint.bold(paint.text.muted(line))}`;
@@ -306,6 +452,52 @@ function renderDiff(text) {
306
452
  }).join('\n');
307
453
  }
308
454
 
455
+ function unifiedForFileDiff(diff) {
456
+ if (!diff) return '';
457
+ if (diff.unified) return String(diff.unified);
458
+ const hunks = Array.isArray(diff.hunks) ? diff.hunks : [];
459
+ if (!hunks.length) return '';
460
+ const file = diff.relative_path || diff.path || 'file';
461
+ const out = [`--- a/${file}`, `+++ b/${file}`];
462
+ for (const hunk of hunks) {
463
+ const lines = hunkLines(hunk);
464
+ if (!lines.length) continue;
465
+ const oldCount = hunk.old_count ?? hunk.old_lines ?? countLinesForSide(lines, 'old');
466
+ const newCount = hunk.new_count ?? hunk.new_lines ?? countLinesForSide(lines, 'new');
467
+ out.push(`@@ -${hunk.old_start ?? 1},${oldCount} +${hunk.new_start ?? 1},${newCount} @@`);
468
+ for (const line of lines) out.push(toUnifiedLine(line));
469
+ }
470
+ return out.length > 2 ? out.join('\n') : '';
471
+ }
472
+
473
+ function hunkLines(hunk = {}) {
474
+ if (Array.isArray(hunk.lines)) return hunk.lines;
475
+ if (typeof hunk.body !== 'string') return [];
476
+ return hunk.body.replace(/\r\n?/g, '\n').split('\n').filter(line => line && !line.startsWith('@@'));
477
+ }
478
+
479
+ function toUnifiedLine(line) {
480
+ if (typeof line === 'string') {
481
+ if (line.startsWith('+') || line.startsWith('-') || line.startsWith(' ')) return line;
482
+ return ` ${line}`;
483
+ }
484
+ const type = String(line?.type || '').toLowerCase();
485
+ const text = String(line?.text ?? line?.content ?? '');
486
+ if (type === 'add' || type === 'added') return `+${text}`;
487
+ if (type === 'remove' || type === 'removed' || type === 'delete') return `-${text}`;
488
+ return ` ${text}`;
489
+ }
490
+
491
+ function countLinesForSide(lines, side) {
492
+ return lines.filter(line => {
493
+ const type = typeof line === 'string'
494
+ ? (line.startsWith('+') ? 'add' : line.startsWith('-') ? 'remove' : 'context')
495
+ : String(line?.type || 'context').toLowerCase();
496
+ if (side === 'old') return type !== 'add' && type !== 'added';
497
+ return type !== 'remove' && type !== 'removed' && type !== 'delete';
498
+ }).length;
499
+ }
500
+
309
501
  function formatDuration(ms) {
310
502
  if (ms < 1000) return `${Math.round(ms)}ms`;
311
503
  return `${(ms / 1000).toFixed(1)}s`;
@@ -6,12 +6,11 @@ function tonePaint(tone) {
6
6
 
7
7
  export function transcriptHeader(label, { tone = 'assistant' } = {}) {
8
8
  const p = tonePaint(tone);
9
- return ` ${p('╭─')} ${paint.bold(p(label))}`;
9
+ return `${paint.bold(p(label))} ${p('›')}`;
10
10
  }
11
11
 
12
12
  export function transcriptLine(line = '', { tone = 'assistant' } = {}) {
13
- const p = tonePaint(tone);
14
- return ` ${p('│')} ${line}`;
13
+ return ` ${line}`;
15
14
  }
16
15
 
17
16
  export function transcriptLines(text, opts = {}) {