tau-all-agent 0.4.0 → 0.4.1

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.
@@ -6,7 +6,7 @@ import {
6
6
  type AppKeybinding,
7
7
  CustomEditor,
8
8
  type AgentToolResult,
9
- type BashToolDetails,
9
+ type EditToolDetails,
10
10
  type ExtensionAPI,
11
11
  type ExtensionContext,
12
12
  type KeybindingsManager,
@@ -19,6 +19,7 @@ import {
19
19
  Loader,
20
20
  Text,
21
21
  truncateToWidth,
22
+ visibleWidth,
22
23
  type AutocompleteProvider,
23
24
  type Component,
24
25
  type EditorComponent,
@@ -42,7 +43,7 @@ const FIND_NO_MATCHES_OUTPUT = "No files found matching pattern"; // core/tools/
42
43
  const LS_EMPTY_DIRECTORY_OUTPUT = "(empty directory)"; // core/tools/ls.ts
43
44
 
44
45
  // PowerShell is intentionally omitted because Tau does not officially support Windows.
45
- const TOOL_NAMES = ["read", "bash", "grep", "find", "ls"] as const;
46
+ const TOOL_NAMES = ["read", "bash", "write", "edit", "grep", "find", "ls", "websearch"] as const;
46
47
 
47
48
  // --- Types ---
48
49
 
@@ -131,9 +132,28 @@ function createToolDisplayRenderers(
131
132
  base: ToolRenderers,
132
133
  getMode: () => Mode | undefined,
133
134
  ): ToolRenderers {
135
+ const renderCall = base.renderCall;
134
136
  return {
135
137
  ...base,
136
138
 
139
+ renderCall:
140
+ (name === "bash" || name === "write" || name === "edit") && renderCall
141
+ ? (args, theme, context) => {
142
+ const previous = context.lastComponent;
143
+ const component =
144
+ previous instanceof MinimalCallComponent
145
+ ? previous
146
+ : new MinimalCallComponent(previous, getMode, theme, name);
147
+ component.nativeComponent = renderCall(args, theme, {
148
+ ...context,
149
+ lastComponent: component.nativeComponent,
150
+ });
151
+ component.expanded = context.expanded;
152
+ component.theme = theme;
153
+ return component;
154
+ }
155
+ : renderCall,
156
+
137
157
  renderResult(result, options, theme, context) {
138
158
  const mode = getMode();
139
159
  const renderer = base.renderResult;
@@ -149,6 +169,7 @@ function createToolDisplayRenderers(
149
169
  ...context,
150
170
  lastComponent: component.nativeComponent,
151
171
  });
172
+ component.setPaddingX(base.renderShell === "self" ? context.outputPad : 0);
152
173
  component.setText(formatMinimalResult(name, result, options, theme, context));
153
174
  return component;
154
175
  }
@@ -197,6 +218,43 @@ function reportSaveError(ctx: ExtensionContext, error: unknown): void {
197
218
 
198
219
  // --- Minimal rendering ---
199
220
 
221
+ class MinimalCallComponent implements Component {
222
+ expanded = false;
223
+
224
+ constructor(
225
+ public nativeComponent: Component | undefined,
226
+ private readonly getMode: () => Mode | undefined,
227
+ public theme: Theme,
228
+ private readonly name: ToolName,
229
+ ) {}
230
+
231
+ render(width: number): string[] {
232
+ if (width <= 0) return [];
233
+ const lines = this.nativeComponent?.render(width) ?? [];
234
+ // Read the mode here: switching between collapsed modes need not rerun renderCall.
235
+ if (this.getMode() !== "minimal" || this.expanded || lines.length <= 1) return lines;
236
+
237
+ // Edit's native box has padding before its file header.
238
+ const first = lines.findIndex((line) => stripVTControlCharacters(line).trim());
239
+ if (first < 0) return [];
240
+ const header = lines[first]!;
241
+ if (!lines.slice(first + 1).some((line) => stripVTControlCharacters(line).trim()))
242
+ return [header];
243
+
244
+ const hidden = lines.length - first - 1;
245
+ let hint = this.theme.fg(
246
+ "muted",
247
+ this.name === "bash" ? ` … (+${hidden} ${plural(hidden, "line")})` : " …",
248
+ );
249
+ if (visibleWidth(hint) >= width) hint = this.theme.fg("muted", "…");
250
+ return [truncateToWidth(header, width - visibleWidth(hint), "") + hint];
251
+ }
252
+
253
+ invalidate(): void {
254
+ this.nativeComponent?.invalidate();
255
+ }
256
+ }
257
+
200
258
  class MinimalResultComponent extends Text {
201
259
  constructor(public nativeComponent: Component | undefined) {
202
260
  super("", 0, 0);
@@ -221,11 +279,18 @@ function formatMinimalResult(
221
279
  let summary: string;
222
280
  switch (name) {
223
281
  case "bash":
224
- summary = bashSummary(result);
282
+ case "websearch":
283
+ summary = textSummary(result);
225
284
  break;
226
285
  case "read":
227
286
  summary = readSummary(result);
228
287
  break;
288
+ case "write":
289
+ summary = writeSummary(context.args);
290
+ break;
291
+ case "edit":
292
+ summary = editSummary(result);
293
+ break;
229
294
  case "grep":
230
295
  summary = grepSummary(result);
231
296
  break;
@@ -240,11 +305,11 @@ function formatMinimalResult(
240
305
  return theme.fg("muted", `↳ ${summary}`);
241
306
  }
242
307
 
243
- function bashSummary(result: AgentToolResult<any>): string {
308
+ function textSummary(result: AgentToolResult<any>): string {
244
309
  const text = textOutput(result).trim();
245
310
  if (!text || text === "(no output)") return "no output";
246
311
 
247
- const details = result.details as BashToolDetails | undefined;
312
+ const details = result.details as { truncation?: { outputLines?: number } } | undefined;
248
313
  const lines = details?.truncation?.outputLines ?? countLines(text);
249
314
  return `${lines} ${plural(lines, "line")}`;
250
315
  }
@@ -261,6 +326,22 @@ function readSummary(result: AgentToolResult<any>): string {
261
326
  return `${lines} ${plural(lines, "line")}`;
262
327
  }
263
328
 
329
+ function writeSummary(args: unknown): string {
330
+ if (!isObject(args) || typeof args.content !== "string") return "done";
331
+ const lines = countLines(args.content);
332
+ return `${lines} ${plural(lines, "line")}`;
333
+ }
334
+
335
+ function editSummary(result: AgentToolResult<any>): string {
336
+ const diff = (result.details as EditToolDetails | undefined)?.diff;
337
+ if (typeof diff !== "string") return "done";
338
+
339
+ // Pi's structured diff prefixes changed lines with a sign and line number.
340
+ const added = diff.match(/^\+ *\d+ /gm)?.length ?? 0;
341
+ const removed = diff.match(/^- *\d+ /gm)?.length ?? 0;
342
+ return added || removed ? `+${added} -${removed} ${plural(added + removed, "line")}` : "done";
343
+ }
344
+
264
345
  function grepSummary(result: AgentToolResult<any>): string {
265
346
  const text = stripTrailingNotice(textOutput(result)).trim();
266
347
  if (!text || text === GREP_NO_MATCHES_OUTPUT) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tau-all-agent",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "description": "Full Tau package for Pi with coding workflows, web search, sandboxing, memory, Telegram, Home Assistant, OpenSCAD, skills, and a theme",
5
5
  "keywords": [
6
6
  "agent-tools",