pi-compact-transcript 0.2.0 → 0.2.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.
@@ -28,6 +28,9 @@ type ToolInfo = {
28
28
  const BUILT_INS: BuiltInName[] = ["bash", "read", "write", "edit", "grep", "find", "ls"];
29
29
  const LABEL = "Thinking...";
30
30
 
31
+ let currentTools: ToolInfo[] = [];
32
+ let toolsById = new Map<string, ToolInfo>();
33
+
31
34
  const toolCache = new Map<string, ReturnType<typeof createBuiltInTools>>();
32
35
 
33
36
  function createBuiltInTools(cwd: string) {
@@ -127,6 +130,38 @@ function toolCallArgs(block: any): any {
127
130
  return block?.args ?? block?.input ?? block?.arguments ?? block?.tool?.args ?? {};
128
131
  }
129
132
 
133
+ function resetToolRun() {
134
+ currentTools = [];
135
+ toolsById = new Map();
136
+ }
137
+
138
+ function upsertToolInfo(id: string, name: string, args: any, invalidate?: () => void): ToolInfo {
139
+ let info = toolsById.get(id);
140
+ if (!info) {
141
+ info = { id, name, args, preview: previewFor(name, args) };
142
+ toolsById.set(id, info);
143
+ if (!currentTools.some((tool) => tool.id === id)) currentTools.push(info);
144
+ }
145
+ info.name = name;
146
+ info.args = args;
147
+ info.preview = previewFor(name, args);
148
+ if (invalidate) info.invalidate = invalidate;
149
+ return info;
150
+ }
151
+
152
+ function compactToolLine(toolCallId: string, name: string, args: any, theme: any, invalidate?: () => void, result?: any): string {
153
+ const info = upsertToolInfo(toolCallId, name, args, invalidate);
154
+ const suffix = resultPreview(result);
155
+ if (suffix) info.result = suffix;
156
+
157
+ if (currentTools[currentTools.length - 1]?.id !== toolCallId) return "";
158
+
159
+ const details = info.result ? `${info.preview} {${oneLine(info.result)}}` : info.preview;
160
+ if (currentTools.length === 1) return theme.fg("muted", limitPlain(details));
161
+ const prefix = `Used ${currentTools.length} tools `;
162
+ return theme.fg("toolTitle", prefix) + theme.fg("muted", limitPlain(details, Math.max(20, (process.stdout.columns || 100) - prefix.length - 6)));
163
+ }
164
+
130
165
  function nextStepSummary(content: any[], fromIndex: number): string {
131
166
  const tools = content.slice(fromIndex + 1).filter((c: any) => c?.type === "toolCall");
132
167
  if (tools.length === 0) return " Next, I’ll continue from this reasoning and respond with the result. I’ll keep the visible output brief.";
@@ -157,20 +192,35 @@ async function patchInternalRenderers() {
157
192
  const ToolExecutionComponent = toolExecutionModule.ToolExecutionComponent;
158
193
  if (ToolExecutionComponent?.prototype && !ToolExecutionComponent.prototype.__compactTranscriptPatched) {
159
194
  const originalRender = ToolExecutionComponent.prototype.render;
195
+ ToolExecutionComponent.prototype.updateDisplay = function () {
196
+ this.__compactTranscriptForceSelf = true;
197
+ this.hideComponent = false;
198
+ this.selfRenderContainer.clear();
199
+ this.imageComponents = [];
200
+ this.imageSpacers = [];
201
+
202
+ const line = compactToolLine(
203
+ this.toolCallId,
204
+ this.toolName,
205
+ this.args,
206
+ themeModule.theme,
207
+ () => {
208
+ this.invalidate();
209
+ this.ui?.requestRender?.();
210
+ },
211
+ this.result,
212
+ );
213
+
214
+ if (!line) {
215
+ this.hideComponent = true;
216
+ return;
217
+ }
218
+
219
+ this.selfRenderContainer.addChild(new Text(line, 0, 0));
220
+ };
160
221
  ToolExecutionComponent.prototype.render = function (width: number) {
161
222
  if (this.hideComponent) return [];
162
- if (this.hasRendererDefinition?.() && this.getRenderShell?.() === "self") {
163
- const contentLines = this.selfRenderContainer.render(width);
164
- if (contentLines.length === 0 && this.imageComponents.length === 0) return [];
165
- const lines = [...contentLines];
166
- for (let i = 0; i < this.imageComponents.length; i++) {
167
- const spacer = this.imageSpacers[i];
168
- if (spacer) lines.push(...spacer.render(width));
169
- const imageComponent = this.imageComponents[i];
170
- if (imageComponent) lines.push(...imageComponent.render(width));
171
- }
172
- return lines;
173
- }
223
+ if (this.__compactTranscriptForceSelf) return this.selfRenderContainer.render(width);
174
224
  return originalRender.call(this, width);
175
225
  };
176
226
  ToolExecutionComponent.prototype.__compactTranscriptPatched = true;
@@ -230,8 +280,6 @@ async function patchInternalRenderers() {
230
280
 
231
281
  export default async function (pi: ExtensionAPI) {
232
282
  await patchInternalRenderers();
233
- let current: ToolInfo[] = [];
234
- let byId = new Map<string, ToolInfo>();
235
283
  let consecutiveThinking = 0;
236
284
 
237
285
  function thinkingLabel() {
@@ -239,8 +287,7 @@ export default async function (pi: ExtensionAPI) {
239
287
  }
240
288
 
241
289
  function resetTools() {
242
- current = [];
243
- byId = new Map();
290
+ resetToolRun();
244
291
  }
245
292
 
246
293
  function applyThinkingLabel(ctx: ExtensionContext) {
@@ -276,45 +323,19 @@ export default async function (pi: ExtensionAPI) {
276
323
  });
277
324
 
278
325
  pi.on("tool_execution_start", (event: any) => {
279
- const previous = current[current.length - 1];
280
- const info: ToolInfo = {
281
- id: event.toolCallId,
282
- name: event.toolName,
283
- args: event.args,
284
- preview: previewFor(event.toolName, event.args),
285
- };
286
- current.push(info);
287
- byId.set(info.id, info);
326
+ const previous = currentTools[currentTools.length - 1];
327
+ upsertToolInfo(event.toolCallId, event.toolName, event.args);
288
328
  previous?.invalidate?.();
289
329
  });
290
330
 
291
331
  pi.on("tool_execution_end", (event: any) => {
292
- const info = byId.get(event.toolCallId);
332
+ const info = toolsById.get(event.toolCallId);
293
333
  if (!info) return;
294
334
  const suffix = resultPreview(event.result);
295
335
  if (suffix) info.result = suffix;
296
336
  info.invalidate?.();
297
337
  });
298
338
 
299
- function compactLine(toolCallId: string, name: string, args: any, theme: any, context: any): string {
300
- let info = byId.get(toolCallId);
301
- if (!info) {
302
- info = { id: toolCallId, name, args, preview: previewFor(name, args) };
303
- byId.set(toolCallId, info);
304
- if (!current.some((t) => t.id === toolCallId)) current.push(info);
305
- }
306
- info.invalidate = context.invalidate;
307
- info.args = args;
308
- info.preview = previewFor(name, args);
309
-
310
- if (current[current.length - 1]?.id !== toolCallId) return "";
311
-
312
- const details = info.result ? `${info.preview} {${oneLine(info.result)}}` : info.preview;
313
- if (current.length === 1) return theme.fg("muted", limitPlain(details));
314
- const prefix = `Used ${current.length} tools `;
315
- return theme.fg("toolTitle", prefix) + theme.fg("muted", limitPlain(details, Math.max(20, (process.stdout.columns || 100) - prefix.length - 6)));
316
- }
317
-
318
339
  for (const name of BUILT_INS) {
319
340
  const base = getBuiltInTools(process.cwd())[name] as any;
320
341
  pi.registerTool({
@@ -328,7 +349,7 @@ export default async function (pi: ExtensionAPI) {
328
349
  },
329
350
  renderCall(args: any, theme: any, context: any) {
330
351
  const text = (context.lastComponent as Text | undefined) ?? new Text("", 0, 0);
331
- text.setText(compactLine(context.toolCallId, name, args, theme, context));
352
+ text.setText(compactToolLine(context.toolCallId, name, args, theme, context.invalidate));
332
353
  return text;
333
354
  },
334
355
  renderResult(_result: any, _options: any, _theme: any, _context: any) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-compact-transcript",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "A compact transcript extension for pi: collapsed thinking summaries and one-line tool previews.",
5
5
  "type": "module",
6
6
  "license": "MIT",