pi-compact-transcript 0.1.0 → 0.2.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.
package/README.md CHANGED
@@ -3,8 +3,9 @@
3
3
  A compact transcript extension for [pi](https://pi.dev):
4
4
 
5
5
  - Collapses hidden thinking blocks into `Thinking...` and consecutive thinking blocks into `Thinking... (Nx)`.
6
+ - Adds a short, visible next-step summary to collapsed thinking rows without exposing raw chain-of-thought.
6
7
  - Collapses built-in tool calls/results into one-line previews.
7
- - Consecutive tool uses are summarized as `Used N tools <latest tool preview>`.
8
+ - Consecutive tool uses in the same agent run are summarized as `Used N tools <latest tool preview>`.
8
9
  - Minimizes vertical space so long agent runs do not scroll away as quickly.
9
10
 
10
11
  ## Install from GitHub
@@ -50,4 +51,4 @@ Set these in `~/.pi/agent/settings.json`, or use `/settings` in pi.
50
51
 
51
52
  This extension overrides the built-in tool renderers for `bash`, `read`, `write`, `edit`, `grep`, `find`, and `ls`. It delegates execution to pi's original built-in tools; only display is changed.
52
53
 
53
- The consecutive-thinking collapse uses pi's current internal assistant-message renderer. If pi changes that internal path in a future release, the extension falls back to the normal hidden-thinking label behavior.
54
+ The consecutive-thinking collapse and compact self-rendered tool rows use pi's current internal TUI components. If pi changes those internal paths in a future release, the extension falls back to the normal hidden-thinking/tool rendering behavior.
@@ -119,7 +119,29 @@ function resultPreview(result: any): string {
119
119
  return `${lines.length} lines`;
120
120
  }
121
121
 
122
- async function patchAssistantThinkingRenderer() {
122
+ function toolCallName(block: any): string {
123
+ return block?.name ?? block?.toolName ?? block?.tool_name ?? block?.tool?.name ?? "tool";
124
+ }
125
+
126
+ function toolCallArgs(block: any): any {
127
+ return block?.args ?? block?.input ?? block?.arguments ?? block?.tool?.args ?? {};
128
+ }
129
+
130
+ function nextStepSummary(content: any[], fromIndex: number): string {
131
+ const tools = content.slice(fromIndex + 1).filter((c: any) => c?.type === "toolCall");
132
+ if (tools.length === 0) return " Next, I’ll continue from this reasoning and respond with the result. I’ll keep the visible output brief.";
133
+
134
+ const first = tools[0];
135
+ const preview = limitPlain(previewFor(toolCallName(first), toolCallArgs(first)), 90);
136
+ if (tools.length === 1) {
137
+ return ` Next, I’ll use ${preview}. I’ll inspect the result and decide the next visible step from there.`;
138
+ }
139
+ const last = tools[tools.length - 1];
140
+ const lastPreview = limitPlain(previewFor(toolCallName(last), toolCallArgs(last)), 70);
141
+ return ` Next, I’ll run ${tools.length} tool calls, starting with ${preview}. I’ll use the latest result to continue, ending this batch around ${lastPreview}.`;
142
+ }
143
+
144
+ async function patchInternalRenderers() {
123
145
  try {
124
146
  const require = createRequire(import.meta.url);
125
147
  const piMain = require.resolve("@earendil-works/pi-coding-agent");
@@ -127,7 +149,33 @@ async function patchAssistantThinkingRenderer() {
127
149
  const assistantModule = await import(
128
150
  pathToFileURL(join(distDir, "modes/interactive/components/assistant-message.js")).href
129
151
  );
152
+ const toolExecutionModule = await import(
153
+ pathToFileURL(join(distDir, "modes/interactive/components/tool-execution.js")).href
154
+ );
130
155
  const themeModule = await import(pathToFileURL(join(distDir, "modes/interactive/theme/theme.js")).href);
156
+
157
+ const ToolExecutionComponent = toolExecutionModule.ToolExecutionComponent;
158
+ if (ToolExecutionComponent?.prototype && !ToolExecutionComponent.prototype.__compactTranscriptPatched) {
159
+ const originalRender = ToolExecutionComponent.prototype.render;
160
+ ToolExecutionComponent.prototype.render = function (width: number) {
161
+ 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
+ }
174
+ return originalRender.call(this, width);
175
+ };
176
+ ToolExecutionComponent.prototype.__compactTranscriptPatched = true;
177
+ }
178
+
131
179
  const Component = assistantModule.AssistantMessageComponent;
132
180
  if (!Component?.prototype || Component.prototype.__compactTranscriptPatched) return;
133
181
  const original = Component.prototype.updateContent;
@@ -161,8 +209,9 @@ async function patchAssistantThinkingRenderer() {
161
209
  i += count - 1;
162
210
 
163
211
  const label = count > 1 ? `${LABEL} (${count}x)` : LABEL;
212
+ const summary = nextStepSummary(message.content, i);
164
213
  this.contentContainer.addChild(
165
- new Text(themeModule.theme.italic(themeModule.theme.fg("thinkingText", label)), this.outputPad, 0),
214
+ new Text(themeModule.theme.italic(themeModule.theme.fg("thinkingText", limitPlain(`${label}${summary}`))), this.outputPad, 0),
166
215
  );
167
216
 
168
217
  const hasVisibleAfter = message.content
@@ -180,7 +229,7 @@ async function patchAssistantThinkingRenderer() {
180
229
  }
181
230
 
182
231
  export default async function (pi: ExtensionAPI) {
183
- await patchAssistantThinkingRenderer();
232
+ await patchInternalRenderers();
184
233
  let current: ToolInfo[] = [];
185
234
  let byId = new Map<string, ToolInfo>();
186
235
  let consecutiveThinking = 0;
@@ -203,8 +252,11 @@ export default async function (pi: ExtensionAPI) {
203
252
  ctx.ui.setWorkingMessage(LABEL);
204
253
  });
205
254
 
206
- pi.on("turn_start", (_event, ctx) => {
255
+ pi.on("agent_start", () => {
207
256
  resetTools();
257
+ });
258
+
259
+ pi.on("turn_start", (_event, ctx) => {
208
260
  consecutiveThinking = 0;
209
261
  applyThinkingLabel(ctx);
210
262
  });
@@ -219,6 +271,7 @@ export default async function (pi: ExtensionAPI) {
219
271
  if (type && !type.startsWith("thinking_")) {
220
272
  consecutiveThinking = 0;
221
273
  applyThinkingLabel(ctx);
274
+ if (type === "text_delta" || type === "text_start") resetTools();
222
275
  }
223
276
  });
224
277
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pi-compact-transcript",
3
- "version": "0.1.0",
4
- "description": "A compact transcript extension for pi: collapsed thinking and one-line tool previews.",
3
+ "version": "0.2.0",
4
+ "description": "A compact transcript extension for pi: collapsed thinking summaries and one-line tool previews.",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
7
  "author": "Alan Hagedorn <avhagedorn@gmail.com>",