pi-subagents 0.9.1 → 0.9.2
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/CHANGELOG.md +7 -1
- package/agent-manager-detail.ts +1 -1
- package/agent-manager-list.ts +2 -2
- package/package.json +1 -1
- package/render.ts +46 -28
package/CHANGELOG.md
CHANGED
|
@@ -2,12 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [0.9.2] - 2026-02-19
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
- TUI crash on async subagent completion: "Rendered line exceeds terminal width." `render.ts` never truncated output to fit the terminal — widget lines (`agents.join(" -> ")`), chain visualizations, skills lists, and task previews could all exceed the terminal width. Added `truncLine` helper using pi-tui's `truncateToWidth`/`visibleWidth` and applied it to every `Text` widget and widget string. Task preview lengths are now dynamic based on terminal width instead of hardcoded.
|
|
9
|
+
- Agent Manager scope badge showed `[built]` instead of `[builtin]` in list and detail views. Widened scope column to fit.
|
|
10
|
+
|
|
5
11
|
## [0.9.1] - 2026-02-17
|
|
6
12
|
|
|
7
13
|
### Fixed
|
|
8
14
|
- Builtin agents were silently excluded from management listings, chain validation, and agent resolution. Added `allAgents()` helper that includes all three tiers (builtin, user, project) and applied it to `handleList`, `findAgents`, `availableNames`, and `unknownChainAgents`.
|
|
9
15
|
- `resolveTarget` now blocks mutation of builtin agents with a clear error message suggesting the user create a same-named override, instead of allowing `fs.unlinkSync` or `fs.writeFileSync` on extension files.
|
|
10
|
-
- Agent Manager TUI guards: delete and edit actions on builtin agents are blocked with an error status. Detail screen hides `[e]dit` from the footer for builtins. Scope badge shows `[
|
|
16
|
+
- Agent Manager TUI guards: delete and edit actions on builtin agents are blocked with an error status. Detail screen hides `[e]dit` from the footer for builtins. Scope badge shows `[builtin]` instead of falling through to `[proj]`.
|
|
11
17
|
- Cloning a builtin agent set the scope to `"builtin"` at runtime (violating the `"user" | "project"` type), causing wrong badge display and the clone inheriting builtin protections until session reload. Now maps to `"user"`.
|
|
12
18
|
- Agent Manager `loadEntries` suppresses builtins overridden by user/project agents, preventing duplicate entries in the TUI list.
|
|
13
19
|
- `BUILTIN_AGENTS_DIR` resolved via `import.meta.url` instead of hardcoded `~/.pi/agent/extensions/subagent/agents` path. Works regardless of where the extension is installed.
|
package/agent-manager-detail.ts
CHANGED
|
@@ -129,7 +129,7 @@ export function renderDetail(
|
|
|
129
129
|
theme: Theme,
|
|
130
130
|
): string[] {
|
|
131
131
|
const lines: string[] = [];
|
|
132
|
-
const scopeBadge = agent.source === "builtin" ? "[
|
|
132
|
+
const scopeBadge = agent.source === "builtin" ? "[builtin]" : agent.source === "project" ? "[proj]" : "[user]";
|
|
133
133
|
const headerText = ` ${agent.name} ${scopeBadge} ${formatPath(agent.filePath)} `;
|
|
134
134
|
lines.push(renderHeader(headerText, width, theme));
|
|
135
135
|
lines.push(row("", width, theme));
|
package/agent-manager-list.ts
CHANGED
|
@@ -190,7 +190,7 @@ export function renderList(
|
|
|
190
190
|
const innerW = width - 2;
|
|
191
191
|
const nameWidth = 16;
|
|
192
192
|
const modelWidth = 12;
|
|
193
|
-
const scopeWidth =
|
|
193
|
+
const scopeWidth = 9;
|
|
194
194
|
|
|
195
195
|
for (let i = 0; i < visible.length; i++) {
|
|
196
196
|
const agent = visible[i]!;
|
|
@@ -208,7 +208,7 @@ export function renderList(
|
|
|
208
208
|
const modelDisplay = modelRaw.includes("/") ? modelRaw.split("/").pop() ?? modelRaw : modelRaw;
|
|
209
209
|
const nameText = isCursor ? theme.fg("accent", agent.name) : agent.name;
|
|
210
210
|
const modelText = theme.fg("dim", modelDisplay);
|
|
211
|
-
const scopeLabel = agent.kind === "chain" ? "[chain]" : agent.source === "builtin" ? "[
|
|
211
|
+
const scopeLabel = agent.kind === "chain" ? "[chain]" : agent.source === "builtin" ? "[builtin]" : agent.source === "project" ? "[proj]" : "[user]";
|
|
212
212
|
const scopeBadge = theme.fg("dim", scopeLabel);
|
|
213
213
|
const descText = theme.fg("dim", agent.description);
|
|
214
214
|
|
package/package.json
CHANGED
package/render.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import type { AgentToolResult } from "@mariozechner/pi-agent-core";
|
|
6
6
|
import { getMarkdownTheme, type ExtensionContext } from "@mariozechner/pi-coding-agent";
|
|
7
|
-
import { Container, Markdown, Spacer, Text, type Widget } from "@mariozechner/pi-tui";
|
|
7
|
+
import { Container, Markdown, Spacer, Text, truncateToWidth, visibleWidth, type Widget } from "@mariozechner/pi-tui";
|
|
8
8
|
import {
|
|
9
9
|
type AsyncJobState,
|
|
10
10
|
type Details,
|
|
@@ -16,6 +16,15 @@ import { getFinalOutput, getDisplayItems, getOutputTail, getLastActivity } from
|
|
|
16
16
|
|
|
17
17
|
type Theme = ExtensionContext["ui"]["theme"];
|
|
18
18
|
|
|
19
|
+
function getTermWidth(): number {
|
|
20
|
+
return process.stdout.columns || 120;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function truncLine(text: string, maxWidth: number): string {
|
|
24
|
+
if (visibleWidth(text) <= maxWidth) return text;
|
|
25
|
+
return truncateToWidth(text, maxWidth - 1) + "…";
|
|
26
|
+
}
|
|
27
|
+
|
|
19
28
|
// Track last rendered widget state to avoid no-op re-renders
|
|
20
29
|
let lastWidgetHash = "";
|
|
21
30
|
|
|
@@ -67,6 +76,7 @@ export function renderWidget(ctx: ExtensionContext, jobs: AsyncJobState[]): void
|
|
|
67
76
|
lastWidgetHash = newHash;
|
|
68
77
|
|
|
69
78
|
const theme = ctx.ui.theme;
|
|
79
|
+
const w = getTermWidth();
|
|
70
80
|
const lines: string[] = [];
|
|
71
81
|
lines.push(theme.fg("accent", "Async subagents"));
|
|
72
82
|
|
|
@@ -90,12 +100,12 @@ export function renderWidget(ctx: ExtensionContext, jobs: AsyncJobState[]): void
|
|
|
90
100
|
const activityText = job.status === "running" ? getLastActivity(job.outputFile) : "";
|
|
91
101
|
const activitySuffix = activityText ? ` | ${theme.fg("dim", activityText)}` : "";
|
|
92
102
|
|
|
93
|
-
lines.push(`- ${id} ${status} | ${agentLabel} | ${stepText}${elapsed ? ` | ${elapsed}` : ""}${tokenText}${activitySuffix}
|
|
103
|
+
lines.push(truncLine(`- ${id} ${status} | ${agentLabel} | ${stepText}${elapsed ? ` | ${elapsed}` : ""}${tokenText}${activitySuffix}`, w));
|
|
94
104
|
|
|
95
105
|
if (job.status === "running" && job.outputFile) {
|
|
96
106
|
const tail = getOutputTail(job.outputFile, 3);
|
|
97
107
|
for (const line of tail) {
|
|
98
|
-
lines.push(theme.fg("dim", ` > ${line}`));
|
|
108
|
+
lines.push(truncLine(theme.fg("dim", ` > ${line}`), w));
|
|
99
109
|
}
|
|
100
110
|
}
|
|
101
111
|
}
|
|
@@ -114,7 +124,8 @@ export function renderSubagentResult(
|
|
|
114
124
|
const d = result.details;
|
|
115
125
|
if (!d || !d.results.length) {
|
|
116
126
|
const t = result.content[0];
|
|
117
|
-
|
|
127
|
+
const text = t?.type === "text" ? t.text : "(no output)";
|
|
128
|
+
return new Text(truncLine(text, getTermWidth() - 4), 0, 0);
|
|
118
129
|
}
|
|
119
130
|
|
|
120
131
|
const mdTheme = getMarkdownTheme();
|
|
@@ -135,37 +146,42 @@ export function renderSubagentResult(
|
|
|
135
146
|
? ` | ${r.progressSummary.toolCount} tools, ${formatTokens(r.progressSummary.tokens)} tok, ${formatDuration(r.progressSummary.durationMs)}`
|
|
136
147
|
: "";
|
|
137
148
|
|
|
149
|
+
const w = getTermWidth() - 4;
|
|
138
150
|
const c = new Container();
|
|
139
|
-
c.addChild(new Text(`${icon} ${theme.fg("toolTitle", theme.bold(r.agent))}${progressInfo}`, 0, 0));
|
|
151
|
+
c.addChild(new Text(truncLine(`${icon} ${theme.fg("toolTitle", theme.bold(r.agent))}${progressInfo}`, w), 0, 0));
|
|
140
152
|
c.addChild(new Spacer(1));
|
|
153
|
+
const taskMaxLen = Math.max(20, w - 8);
|
|
154
|
+
const taskPreview = r.task.length > taskMaxLen
|
|
155
|
+
? `${r.task.slice(0, taskMaxLen)}...`
|
|
156
|
+
: r.task;
|
|
141
157
|
c.addChild(
|
|
142
|
-
new Text(theme.fg("dim", `Task: ${
|
|
158
|
+
new Text(truncLine(theme.fg("dim", `Task: ${taskPreview}`), w), 0, 0),
|
|
143
159
|
);
|
|
144
160
|
c.addChild(new Spacer(1));
|
|
145
161
|
|
|
146
162
|
const items = getDisplayItems(r.messages);
|
|
147
163
|
for (const item of items) {
|
|
148
164
|
if (item.type === "tool")
|
|
149
|
-
c.addChild(new Text(theme.fg("muted", formatToolCall(item.name, item.args)), 0, 0));
|
|
165
|
+
c.addChild(new Text(truncLine(theme.fg("muted", formatToolCall(item.name, item.args)), w), 0, 0));
|
|
150
166
|
}
|
|
151
167
|
if (items.length) c.addChild(new Spacer(1));
|
|
152
168
|
|
|
153
169
|
if (output) c.addChild(new Markdown(output, 0, 0, mdTheme));
|
|
154
170
|
c.addChild(new Spacer(1));
|
|
155
171
|
if (r.skills?.length) {
|
|
156
|
-
c.addChild(new Text(theme.fg("dim", `Skills: ${r.skills.join(", ")}`), 0, 0));
|
|
172
|
+
c.addChild(new Text(truncLine(theme.fg("dim", `Skills: ${r.skills.join(", ")}`), w), 0, 0));
|
|
157
173
|
}
|
|
158
174
|
if (r.skillsWarning) {
|
|
159
|
-
c.addChild(new Text(theme.fg("warning", `⚠️ ${r.skillsWarning}`), 0, 0));
|
|
175
|
+
c.addChild(new Text(truncLine(theme.fg("warning", `⚠️ ${r.skillsWarning}`), w), 0, 0));
|
|
160
176
|
}
|
|
161
|
-
c.addChild(new Text(theme.fg("dim", formatUsage(r.usage, r.model)), 0, 0));
|
|
177
|
+
c.addChild(new Text(truncLine(theme.fg("dim", formatUsage(r.usage, r.model)), w), 0, 0));
|
|
162
178
|
if (r.sessionFile) {
|
|
163
|
-
c.addChild(new Text(theme.fg("dim", `Session: ${shortenPath(r.sessionFile)}`), 0, 0));
|
|
179
|
+
c.addChild(new Text(truncLine(theme.fg("dim", `Session: ${shortenPath(r.sessionFile)}`), w), 0, 0));
|
|
164
180
|
}
|
|
165
181
|
|
|
166
182
|
if (r.artifactPaths) {
|
|
167
183
|
c.addChild(new Spacer(1));
|
|
168
|
-
c.addChild(new Text(theme.fg("dim", `Artifacts: ${shortenPath(r.artifactPaths.outputPath)}`), 0, 0));
|
|
184
|
+
c.addChild(new Text(truncLine(theme.fg("dim", `Artifacts: ${shortenPath(r.artifactPaths.outputPath)}`), w), 0, 0));
|
|
169
185
|
}
|
|
170
186
|
return c;
|
|
171
187
|
}
|
|
@@ -231,7 +247,7 @@ export function renderSubagentResult(
|
|
|
231
247
|
&& Boolean(isComplete)
|
|
232
248
|
&& hasEmptyTextOutputWithoutOutputTarget(result.task, getFinalOutput(result.messages));
|
|
233
249
|
const isCurrent = i === (d.currentStepIndex ?? d.results.length);
|
|
234
|
-
const
|
|
250
|
+
const stepIcon = isFailed
|
|
235
251
|
? theme.fg("error", "✗")
|
|
236
252
|
: isEmptyWithoutTarget
|
|
237
253
|
? theme.fg("warning", "⚠")
|
|
@@ -240,22 +256,23 @@ export function renderSubagentResult(
|
|
|
240
256
|
: isCurrent && hasRunning
|
|
241
257
|
? theme.fg("warning", "●")
|
|
242
258
|
: theme.fg("dim", "○");
|
|
243
|
-
return `${
|
|
259
|
+
return `${stepIcon} ${agent}`;
|
|
244
260
|
})
|
|
245
261
|
.join(theme.fg("dim", " → "))
|
|
246
262
|
: null;
|
|
247
263
|
|
|
264
|
+
const w = getTermWidth() - 4;
|
|
248
265
|
const c = new Container();
|
|
249
266
|
c.addChild(
|
|
250
267
|
new Text(
|
|
251
|
-
`${icon} ${theme.fg("toolTitle", theme.bold(modeLabel))}${stepInfo}${summaryStr}`,
|
|
268
|
+
truncLine(`${icon} ${theme.fg("toolTitle", theme.bold(modeLabel))}${stepInfo}${summaryStr}`, w),
|
|
252
269
|
0,
|
|
253
270
|
0,
|
|
254
271
|
),
|
|
255
272
|
);
|
|
256
273
|
// Show chain visualization
|
|
257
274
|
if (chainVis) {
|
|
258
|
-
c.addChild(new Text(` ${chainVis}`, 0, 0));
|
|
275
|
+
c.addChild(new Text(truncLine(` ${chainVis}`, w), 0, 0));
|
|
259
276
|
}
|
|
260
277
|
|
|
261
278
|
// === STATIC STEP LAYOUT (like clarification UI) ===
|
|
@@ -275,7 +292,7 @@ export function renderSubagentResult(
|
|
|
275
292
|
|
|
276
293
|
if (!r) {
|
|
277
294
|
// Pending step
|
|
278
|
-
c.addChild(new Text(theme.fg("dim", ` Step ${i + 1}: ${agentName}`), 0, 0));
|
|
295
|
+
c.addChild(new Text(truncLine(theme.fg("dim", ` Step ${i + 1}: ${agentName}`), w), 0, 0));
|
|
279
296
|
c.addChild(new Text(theme.fg("dim", ` status: ○ pending`), 0, 0));
|
|
280
297
|
c.addChild(new Spacer(1));
|
|
281
298
|
continue;
|
|
@@ -299,45 +316,46 @@ export function renderSubagentResult(
|
|
|
299
316
|
const stepHeader = rRunning
|
|
300
317
|
? `${statusIcon} Step ${i + 1}: ${theme.bold(theme.fg("warning", r.agent))}${modelDisplay}${stats}`
|
|
301
318
|
: `${statusIcon} Step ${i + 1}: ${theme.bold(r.agent)}${modelDisplay}${stats}`;
|
|
302
|
-
c.addChild(new Text(stepHeader, 0, 0));
|
|
319
|
+
c.addChild(new Text(truncLine(stepHeader, w), 0, 0));
|
|
303
320
|
|
|
304
|
-
const
|
|
305
|
-
|
|
321
|
+
const taskMaxLen = Math.max(20, w - 12);
|
|
322
|
+
const taskPreview = r.task.slice(0, taskMaxLen) + (r.task.length > taskMaxLen ? "..." : "");
|
|
323
|
+
c.addChild(new Text(truncLine(theme.fg("dim", ` task: ${taskPreview}`), w), 0, 0));
|
|
306
324
|
|
|
307
325
|
const outputTarget = extractOutputTarget(r.task);
|
|
308
326
|
if (outputTarget) {
|
|
309
|
-
c.addChild(new Text(theme.fg("dim", ` output: ${outputTarget}`), 0, 0));
|
|
327
|
+
c.addChild(new Text(truncLine(theme.fg("dim", ` output: ${outputTarget}`), w), 0, 0));
|
|
310
328
|
}
|
|
311
329
|
|
|
312
330
|
if (r.skills?.length) {
|
|
313
|
-
c.addChild(new Text(theme.fg("dim", ` skills: ${r.skills.join(", ")}`), 0, 0));
|
|
331
|
+
c.addChild(new Text(truncLine(theme.fg("dim", ` skills: ${r.skills.join(", ")}`), w), 0, 0));
|
|
314
332
|
}
|
|
315
333
|
if (r.skillsWarning) {
|
|
316
|
-
c.addChild(new Text(theme.fg("warning", ` ⚠️ ${r.skillsWarning}`), 0, 0));
|
|
334
|
+
c.addChild(new Text(truncLine(theme.fg("warning", ` ⚠️ ${r.skillsWarning}`), w), 0, 0));
|
|
317
335
|
}
|
|
318
336
|
|
|
319
337
|
if (rRunning && rProg) {
|
|
320
338
|
if (rProg.skills?.length) {
|
|
321
|
-
c.addChild(new Text(theme.fg("accent", ` skills: ${rProg.skills.join(", ")}`), 0, 0));
|
|
339
|
+
c.addChild(new Text(truncLine(theme.fg("accent", ` skills: ${rProg.skills.join(", ")}`), w), 0, 0));
|
|
322
340
|
}
|
|
323
341
|
// Current tool for running step
|
|
324
342
|
if (rProg.currentTool) {
|
|
325
343
|
const toolLine = rProg.currentToolArgs
|
|
326
344
|
? `${rProg.currentTool}: ${rProg.currentToolArgs.slice(0, 100)}${rProg.currentToolArgs.length > 100 ? "..." : ""}`
|
|
327
345
|
: rProg.currentTool;
|
|
328
|
-
c.addChild(new Text(theme.fg("warning", ` > ${toolLine}`), 0, 0));
|
|
346
|
+
c.addChild(new Text(truncLine(theme.fg("warning", ` > ${toolLine}`), w), 0, 0));
|
|
329
347
|
}
|
|
330
348
|
// Recent tools
|
|
331
349
|
if (rProg.recentTools?.length) {
|
|
332
350
|
for (const t of rProg.recentTools.slice(0, 3)) {
|
|
333
351
|
const args = t.args.slice(0, 90) + (t.args.length > 90 ? "..." : "");
|
|
334
|
-
c.addChild(new Text(theme.fg("dim", ` ${t.tool}: ${args}`), 0, 0));
|
|
352
|
+
c.addChild(new Text(truncLine(theme.fg("dim", ` ${t.tool}: ${args}`), w), 0, 0));
|
|
335
353
|
}
|
|
336
354
|
}
|
|
337
355
|
// Recent output (limited)
|
|
338
356
|
const recentLines = (rProg.recentOutput ?? []).slice(-5);
|
|
339
357
|
for (const line of recentLines) {
|
|
340
|
-
c.addChild(new Text(theme.fg("dim", ` ${line.slice(0, 100)}${line.length > 100 ? "..." : ""}`), 0, 0));
|
|
358
|
+
c.addChild(new Text(truncLine(theme.fg("dim", ` ${line.slice(0, 100)}${line.length > 100 ? "..." : ""}`), w), 0, 0));
|
|
341
359
|
}
|
|
342
360
|
}
|
|
343
361
|
|
|
@@ -346,7 +364,7 @@ export function renderSubagentResult(
|
|
|
346
364
|
|
|
347
365
|
if (d.artifacts) {
|
|
348
366
|
c.addChild(new Spacer(1));
|
|
349
|
-
c.addChild(new Text(theme.fg("dim", `Artifacts dir: ${shortenPath(d.artifacts.dir)}`), 0, 0));
|
|
367
|
+
c.addChild(new Text(truncLine(theme.fg("dim", `Artifacts dir: ${shortenPath(d.artifacts.dir)}`), w), 0, 0));
|
|
350
368
|
}
|
|
351
369
|
return c;
|
|
352
370
|
}
|