agent-sh 0.12.17 → 0.12.18

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.
@@ -995,8 +995,7 @@ export class AgentLoop {
995
995
  const absPath = path.resolve(process.cwd(), args.path);
996
996
  this.fileReadCache.delete(absPath);
997
997
  }
998
- // Compute result display: tool-provided → default (none)
999
- const resultDisplay = tool.formatResult?.(args, result);
998
+ const resultDisplay = result.display ?? tool.formatResult?.(args, result);
1000
999
  // Emit completion events (via transform pipe so extensions can override)
1001
1000
  this.bus.emitTransform("agent:tool-completed", {
1002
1001
  toolCallId: id, exitCode: result.exitCode,
@@ -69,13 +69,7 @@ export function createEditFileTool(getCwd) {
69
69
  icon: "✎",
70
70
  locations: [{ path: args.path }],
71
71
  }),
72
- formatResult: (_args, result) => {
73
- if (result.isError)
74
- return {};
75
- const m = result.content.match(/\((\+\d+(?:\s-\d+)?)\)/);
76
- return m ? { summary: m[1] } : {};
77
- },
78
- async execute(args, onChunk) {
72
+ async execute(args) {
79
73
  const filePath = expandHome(args.path);
80
74
  const oldText = args.old_text;
81
75
  const newText = args.new_text;
@@ -120,26 +114,16 @@ export function createEditFileTool(getCwd) {
120
114
  ? newContent.replace(/\n/g, "\r\n")
121
115
  : newContent;
122
116
  await fs.writeFile(absPath, finalContent);
123
- // Compute and stream diff for display. Batch into one onChunk —
124
- // per-line emits trigger N TUI renders for large hunks.
125
117
  const diff = computeEditDiff(normalized, normalizedOld, normalizedNew, replaceAll);
126
- if (onChunk && diff.hunks.length > 0) {
127
- const parts = [];
128
- for (const hunk of diff.hunks) {
129
- for (const line of hunk.lines) {
130
- const prefix = line.type === "added" ? "+" : line.type === "removed" ? "-" : " ";
131
- parts.push(`${prefix}${line.text}\n`);
132
- }
133
- }
134
- onChunk(parts.join(""));
135
- }
136
- const stats = diff.isNewFile
137
- ? `+${diff.added}`
138
- : `+${diff.added} -${diff.removed}`;
118
+ const stats = diff.isNewFile ? `+${diff.added}` : `+${diff.added} -${diff.removed}`;
139
119
  return {
140
120
  content: `Edited ${absPath} (${stats})`,
141
121
  exitCode: 0,
142
122
  isError: false,
123
+ display: {
124
+ summary: stats,
125
+ body: { kind: "diff", diff, filePath: absPath },
126
+ },
143
127
  };
144
128
  }
145
129
  catch (err) {
@@ -29,13 +29,7 @@ export function createWriteFileTool(getCwd) {
29
29
  icon: "✎",
30
30
  locations: [{ path: args.path }],
31
31
  }),
32
- formatResult: (_args, result) => {
33
- if (result.isError)
34
- return {};
35
- const m = result.content.match(/\((\+\d+(?:\s-\d+)?)\)/);
36
- return m ? { summary: m[1] } : {};
37
- },
38
- async execute(args, onChunk) {
32
+ async execute(args) {
39
33
  const filePath = expandHome(args.path);
40
34
  const content = args.content;
41
35
  const absPath = path.resolve(getCwd(), filePath);
@@ -49,28 +43,18 @@ export function createWriteFileTool(getCwd) {
49
43
  }
50
44
  await fs.mkdir(path.dirname(absPath), { recursive: true });
51
45
  await fs.writeFile(absPath, content);
52
- // Compute and stream diff for display. Batch into one onChunk —
53
- // per-line emits trigger N TUI renders for large files.
54
46
  const diff = computeDiff(oldContent, content);
55
- if (onChunk && diff.hunks.length > 0) {
56
- const parts = [];
57
- for (const hunk of diff.hunks) {
58
- for (const line of hunk.lines) {
59
- const prefix = line.type === "added" ? "+" : line.type === "removed" ? "-" : " ";
60
- parts.push(`${prefix}${line.text}\n`);
61
- }
62
- }
63
- onChunk(parts.join(""));
64
- }
65
- const stats = diff.isNewFile
66
- ? `+${diff.added}`
67
- : `+${diff.added} -${diff.removed}`;
47
+ const stats = diff.isNewFile ? `+${diff.added}` : `+${diff.added} -${diff.removed}`;
68
48
  return {
69
49
  content: oldContent === null
70
50
  ? `Created ${absPath} (${stats})`
71
51
  : `Wrote ${absPath} (${stats})`,
72
52
  exitCode: 0,
73
53
  isError: false,
54
+ display: {
55
+ summary: stats,
56
+ body: { kind: "diff", diff, filePath: absPath },
57
+ },
74
58
  };
75
59
  }
76
60
  catch (err) {
@@ -19,6 +19,8 @@ export interface ToolResult {
19
19
  content: string;
20
20
  exitCode: number | null;
21
21
  isError: boolean;
22
+ /** When set, takes precedence over `tool.formatResult()`. */
23
+ display?: ToolResultDisplay;
22
24
  }
23
25
  /** Structured result display — returned by formatResult or computed by defaults. */
24
26
  export interface ToolResultDisplay {
@@ -652,22 +652,33 @@ export default function activate(ctx) {
652
652
  function renderDiffBody(diff, filePath, width) {
653
653
  if (diff.isIdentical)
654
654
  return [];
655
- const boxW = Math.min(120, width - 2); // -2 for writeLine indent
655
+ const boxW = Math.min(120, width - 2);
656
656
  const contentW = boxW - 4;
657
- const diffLines = renderDiff(diff, {
658
- width: contentW,
659
- filePath,
660
- maxLines: getSettings().diffMaxLines,
661
- trueColor: true,
662
- });
663
- const body = diffLines.length > 1 ? ["", ...diffLines.slice(1), ""] : diffLines;
664
- const footer = undefined;
657
+ let body;
658
+ if (diff.isNewFile) {
659
+ const lines = diff.hunks.flatMap(h => h.lines.map(l => l.text));
660
+ const preview = getSettings().newFilePreviewLines;
661
+ const head = lines.slice(0, preview);
662
+ const truncated = head.map(l => l.length > contentW ? l.slice(0, contentW - 1) + "…" : l);
663
+ const more = lines.length > preview
664
+ ? [`${p.dim}… ${lines.length - preview} more lines${p.reset}`]
665
+ : [];
666
+ body = ["", ...truncated, ...more, ""];
667
+ }
668
+ else {
669
+ const diffLines = renderDiff(diff, {
670
+ width: contentW,
671
+ filePath,
672
+ maxLines: getSettings().diffMaxLines,
673
+ trueColor: true,
674
+ });
675
+ body = diffLines.length > 1 ? ["", ...diffLines.slice(1), ""] : diffLines;
676
+ }
665
677
  return renderBoxFrame(body, {
666
678
  width: boxW,
667
679
  style: "rounded",
668
680
  borderColor: p.dim,
669
681
  title: diffTitle(filePath, diff),
670
- footer,
671
682
  });
672
683
  }
673
684
  /** Render output lines with truncation. */
@@ -67,6 +67,8 @@ export interface Settings {
67
67
  readOutputMaxLines?: number;
68
68
  /** Max diff lines rendered in the TUI (Infinity = no limit). */
69
69
  diffMaxLines?: number;
70
+ /** Lines of head content shown when a brand-new file is created. */
71
+ newFilePreviewLines?: number;
70
72
  /** Tool protocol:
71
73
  * "api" — all tools sent with full schema.
72
74
  * "deferred" — extensions dispatched through `use_extension(name, args)` meta-tool.
package/dist/settings.js CHANGED
@@ -30,6 +30,7 @@ const DEFAULTS = {
30
30
  maxCommandOutputLines: 3,
31
31
  readOutputMaxLines: 10,
32
32
  diffMaxLines: Infinity,
33
+ newFilePreviewLines: 5,
33
34
  skillPaths: [],
34
35
  diagnose: false,
35
36
  startupBanner: true,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-sh",
3
- "version": "0.12.17",
3
+ "version": "0.12.18",
4
4
  "description": "A shell-first terminal where AI is one keystroke away",
5
5
  "type": "module",
6
6
  "main": "dist/core.js",