pi-subagents 0.37.1 → 0.37.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 +8 -0
- package/package.json +1 -1
- package/src/agents/skills.ts +14 -12
- package/src/runs/shared/pi-args.ts +3 -0
- package/src/tui/render.ts +23 -26
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [0.37.2] - 2026-07-28
|
|
6
|
+
|
|
7
|
+
### Changed
|
|
8
|
+
- Reduced repeated scanning and file reads in live TUI rendering and skill loading.
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
- Passed `--no-context-files` to child Pi runs when an agent disables inherited project context, avoiding stale prompt-header parsing as Pi's context block format changes. Thanks to @KorenKrita for #667.
|
|
12
|
+
|
|
5
13
|
## [0.37.1] - 2026-07-27
|
|
6
14
|
|
|
7
15
|
### Added
|
package/package.json
CHANGED
package/src/agents/skills.ts
CHANGED
|
@@ -379,19 +379,21 @@ function chooseHigherPrioritySkill(existing: CachedSkillEntry | undefined, candi
|
|
|
379
379
|
return candidate.order < existing.order ? candidate : existing;
|
|
380
380
|
}
|
|
381
381
|
|
|
382
|
-
function
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
382
|
+
function parseSkillDescription(content: string): string | undefined {
|
|
383
|
+
const normalized = content.replace(/\r\n/g, "\n");
|
|
384
|
+
if (!normalized.startsWith("---")) return undefined;
|
|
385
|
+
|
|
386
|
+
const endIndex = normalized.indexOf("\n---", 3);
|
|
387
|
+
if (endIndex === -1) return undefined;
|
|
387
388
|
|
|
388
|
-
|
|
389
|
-
|
|
389
|
+
const frontmatter = normalized.slice(3, endIndex).trim();
|
|
390
|
+
const match = frontmatter.match(/^description:\s*(.+)$/m);
|
|
391
|
+
return match?.[1]?.trim().replace(/^['\"]|['\"]$/g, "");
|
|
392
|
+
}
|
|
390
393
|
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
return match[1]?.trim().replace(/^['\"]|['\"]$/g, "");
|
|
394
|
+
function maybeReadSkillDescription(filePath: string): string | undefined {
|
|
395
|
+
try {
|
|
396
|
+
return parseSkillDescription(fs.readFileSync(filePath, "utf-8"));
|
|
395
397
|
} catch {
|
|
396
398
|
// Description parsing is best-effort metadata extraction.
|
|
397
399
|
return undefined;
|
|
@@ -585,7 +587,7 @@ function readSkill(
|
|
|
585
587
|
|
|
586
588
|
const raw = fs.readFileSync(skillPath, "utf-8");
|
|
587
589
|
const content = stripSkillFrontmatter(raw);
|
|
588
|
-
const description =
|
|
590
|
+
const description = parseSkillDescription(raw);
|
|
589
591
|
const skill: ResolvedSkill = {
|
|
590
592
|
name: skillName,
|
|
591
593
|
path: skillPath,
|
|
@@ -253,6 +253,9 @@ export function buildPiArgs(input: BuildPiArgsInput): BuildPiArgsResult {
|
|
|
253
253
|
}
|
|
254
254
|
for (const extPath of toolPlan.extensionArgs) args.push("--extension", extPath);
|
|
255
255
|
|
|
256
|
+
if (!input.inheritProjectContext) {
|
|
257
|
+
args.push("--no-context-files");
|
|
258
|
+
}
|
|
256
259
|
if (!input.inheritSkills) {
|
|
257
260
|
args.push("--no-skills");
|
|
258
261
|
}
|
package/src/tui/render.ts
CHANGED
|
@@ -40,6 +40,7 @@ function getTermWidth(): number {
|
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
const segmenter = new Intl.Segmenter(undefined, { granularity: "grapheme" });
|
|
43
|
+
const ansiStylePattern = /\x1b\[[0-9;]*m/y;
|
|
43
44
|
|
|
44
45
|
/**
|
|
45
46
|
* Truncate a line to maxWidth, preserving ANSI styling through the ellipsis.
|
|
@@ -50,7 +51,7 @@ const segmenter = new Intl.Segmenter(undefined, { granularity: "grapheme" });
|
|
|
50
51
|
*
|
|
51
52
|
* Uses Intl.Segmenter for proper Unicode/emoji handling (not char-by-char).
|
|
52
53
|
*/
|
|
53
|
-
function truncLine(text: string, maxWidth: number): string {
|
|
54
|
+
export function truncLine(text: string, maxWidth: number): string {
|
|
54
55
|
if (visibleWidth(text) <= maxWidth) return text;
|
|
55
56
|
|
|
56
57
|
const targetWidth = maxWidth - 1;
|
|
@@ -60,7 +61,8 @@ function truncLine(text: string, maxWidth: number): string {
|
|
|
60
61
|
let i = 0;
|
|
61
62
|
|
|
62
63
|
while (i < text.length) {
|
|
63
|
-
|
|
64
|
+
ansiStylePattern.lastIndex = i;
|
|
65
|
+
const ansiMatch = ansiStylePattern.exec(text);
|
|
64
66
|
if (ansiMatch) {
|
|
65
67
|
const code = ansiMatch[0];
|
|
66
68
|
result += code;
|
|
@@ -74,11 +76,9 @@ function truncLine(text: string, maxWidth: number): string {
|
|
|
74
76
|
continue;
|
|
75
77
|
}
|
|
76
78
|
|
|
77
|
-
let end = i;
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
}
|
|
81
|
-
|
|
79
|
+
let end = text.indexOf("\x1b[", i);
|
|
80
|
+
if (end === i) end = text.indexOf("\x1b[", i + 2);
|
|
81
|
+
if (end === -1) end = text.length;
|
|
82
82
|
const textPortion = text.slice(i, end);
|
|
83
83
|
for (const seg of segmenter.segment(textPortion)) {
|
|
84
84
|
const grapheme = seg.segment;
|
|
@@ -529,17 +529,15 @@ function buildChainStepSpans(details: Pick<Details, "chainAgents" | "workflowGra
|
|
|
529
529
|
return spans;
|
|
530
530
|
}
|
|
531
531
|
|
|
532
|
-
function isChainParallelGroupActive(details: Pick<Details, "mode" | "chainAgents" | "currentStepIndex" | "workflowGraph">): boolean {
|
|
533
|
-
if (details.mode !== "chain") return false;
|
|
534
|
-
if (details.currentStepIndex === undefined) return false;
|
|
535
|
-
return buildChainStepSpans(details).some((span) => span.stepIndex === details.currentStepIndex && span.isParallel);
|
|
536
|
-
}
|
|
537
|
-
|
|
538
532
|
function buildAsyncChainStepSpans(total: number, stepCount: number, parallelGroups: AsyncParallelGroupStatus[] = []): ChainStepSpan[] {
|
|
533
|
+
const groupsByStep = new Map<number, AsyncParallelGroupStatus>();
|
|
534
|
+
for (const group of parallelGroups) {
|
|
535
|
+
if (!groupsByStep.has(group.stepIndex)) groupsByStep.set(group.stepIndex, group);
|
|
536
|
+
}
|
|
539
537
|
const spans: ChainStepSpan[] = [];
|
|
540
538
|
let flatIndex = 0;
|
|
541
539
|
for (let stepIndex = 0; stepIndex < total; stepIndex++) {
|
|
542
|
-
const group =
|
|
540
|
+
const group = groupsByStep.get(stepIndex);
|
|
543
541
|
if (group) {
|
|
544
542
|
spans.push({ stepIndex, start: group.start, count: group.count, isParallel: true });
|
|
545
543
|
flatIndex = Math.max(flatIndex, group.start + group.count);
|
|
@@ -567,6 +565,7 @@ interface ChainRenderResultEntry {
|
|
|
567
565
|
kind: "result";
|
|
568
566
|
resultIndex: number;
|
|
569
567
|
rowNumber: number;
|
|
568
|
+
rowLabel?: string;
|
|
570
569
|
agentName: string;
|
|
571
570
|
}
|
|
572
571
|
|
|
@@ -601,6 +600,7 @@ function buildChainRenderEntries(details: Details, label: MultiProgressLabel): C
|
|
|
601
600
|
kind: "result",
|
|
602
601
|
resultIndex: index,
|
|
603
602
|
rowNumber: index + 1,
|
|
603
|
+
rowLabel: span.isParallel ? `Agent ${index - span.start + 1}/${span.count}` : `Step ${span.stepIndex + 1}`,
|
|
604
604
|
agentName: details.results[index]?.agent ?? details.chainAgents?.[span.stepIndex] ?? `step-${span.stepIndex + 1}`,
|
|
605
605
|
});
|
|
606
606
|
}
|
|
@@ -622,7 +622,9 @@ interface MultiProgressLabel {
|
|
|
622
622
|
function buildMultiProgressLabel(details: Pick<Details, "mode" | "results" | "progress" | "totalSteps" | "currentStepIndex" | "chainAgents" | "workflowGraph">, hasRunning: boolean): MultiProgressLabel {
|
|
623
623
|
const stepSpans = buildChainStepSpans(details);
|
|
624
624
|
const hasParallelInChain = details.mode === "chain" && stepSpans.some((span) => span.isParallel);
|
|
625
|
-
const activeParallelGroup =
|
|
625
|
+
const activeParallelGroup = details.mode === "chain"
|
|
626
|
+
&& details.currentStepIndex !== undefined
|
|
627
|
+
&& stepSpans.some((span) => span.stepIndex === details.currentStepIndex && span.isParallel);
|
|
626
628
|
const itemTitle: "Step" | "Agent" = details.mode === "parallel" || activeParallelGroup ? "Agent" : "Step";
|
|
627
629
|
|
|
628
630
|
if (details.mode === "parallel") {
|
|
@@ -708,15 +710,10 @@ function buildMultiProgressLabel(details: Pick<Details, "mode" | "results" | "pr
|
|
|
708
710
|
return { headerLabel, itemTitle, totalCount, hasParallelInChain, activeParallelGroup, groupStartIndex: 0, groupEndIndex: details.results.length, showActiveGroupOnly: false };
|
|
709
711
|
}
|
|
710
712
|
|
|
711
|
-
function resultRowLabel(
|
|
712
|
-
if (details.mode === "chain" && label.hasParallelInChain) {
|
|
713
|
-
const span = buildChainStepSpans(details).find((candidate) => resultIndex >= candidate.start && resultIndex < candidate.start + candidate.count);
|
|
714
|
-
if (span?.isParallel) return `Agent ${resultIndex - span.start + 1}/${span.count}`;
|
|
715
|
-
if (span) return `Step ${span.stepIndex + 1}`;
|
|
716
|
-
}
|
|
713
|
+
function resultRowLabel(label: MultiProgressLabel, resultIndex: number, stepNumber: number): string {
|
|
717
714
|
if (label.itemTitle === "Agent") {
|
|
718
715
|
const localStepNumber = label.activeParallelGroup
|
|
719
|
-
?
|
|
716
|
+
? resultIndex - label.groupStartIndex + 1
|
|
720
717
|
: stepNumber;
|
|
721
718
|
return `Agent ${localStepNumber}/${label.totalCount}`;
|
|
722
719
|
}
|
|
@@ -1417,7 +1414,7 @@ function renderMultiCompact(d: Details, theme: Theme, frame?: number): Component
|
|
|
1417
1414
|
const rowNumber = entry.rowNumber;
|
|
1418
1415
|
const agentName = entry.agentName;
|
|
1419
1416
|
if (!r) {
|
|
1420
|
-
const pendingLabel =
|
|
1417
|
+
const pendingLabel = entry.rowLabel ?? `${itemTitle} ${rowNumber}`;
|
|
1421
1418
|
c.addChild(new Text(truncLine(theme.fg("dim", ` ◦ ${pendingLabel}: ${agentName} · pending`), width), 0, 0));
|
|
1422
1419
|
continue;
|
|
1423
1420
|
}
|
|
@@ -1430,7 +1427,7 @@ function renderMultiCompact(d: Details, theme: Theme, frame?: number): Component
|
|
|
1430
1427
|
const stepStats = formatProgressStats(theme, rProg);
|
|
1431
1428
|
const glyph = rPending ? theme.fg("dim", "◦") : resultGlyph(r, output, theme, rRunning, progressRunningSeed(rProg), frame);
|
|
1432
1429
|
const pendingLabel = rPending ? ` ${theme.fg("dim", "· pending")}` : "";
|
|
1433
|
-
const stepLabel = resultRowLabel(
|
|
1430
|
+
const stepLabel = entry.rowLabel ?? resultRowLabel(multiLabel, i, stepNumber);
|
|
1434
1431
|
const line = `${glyph} ${stepLabel}: ${themeBold(theme, agentName)}${contextModeBadge(theme, r.context)}${stepStats ? ` ${theme.fg("dim", "·")} ${stepStats}` : ""}${pendingLabel}`;
|
|
1435
1432
|
c.addChild(new Text(truncLine(` ${line}`, width), 0, 0));
|
|
1436
1433
|
if (rRunning && rProg && "status" in rProg) {
|
|
@@ -1717,7 +1714,7 @@ export function renderSubagentResult(
|
|
|
1717
1714
|
const agentName = entry.agentName;
|
|
1718
1715
|
|
|
1719
1716
|
if (!r) {
|
|
1720
|
-
const pendingLabel =
|
|
1717
|
+
const pendingLabel = entry.rowLabel ?? `${itemTitle} ${rowNumber}`;
|
|
1721
1718
|
c.addChild(new Text(fit(theme.fg("dim", ` ${pendingLabel}: ${agentName}`)), 0, 0));
|
|
1722
1719
|
c.addChild(new Text(theme.fg("dim", ` status: pending`), 0, 0));
|
|
1723
1720
|
c.addChild(new Spacer(1));
|
|
@@ -1740,7 +1737,7 @@ export function renderSubagentResult(
|
|
|
1740
1737
|
: theme.fg("success", "done");
|
|
1741
1738
|
const stats = rProg ? ` | ${rProg.toolCount} tools, ${formatDuration(rProg.durationMs)}` : "";
|
|
1742
1739
|
const modelDisplay = modelThinkingBadge(theme, r.model);
|
|
1743
|
-
const stepLabel = resultRowLabel(
|
|
1740
|
+
const stepLabel = entry.rowLabel ?? resultRowLabel(multiLabel, i, stepNumber);
|
|
1744
1741
|
const contextBadge = contextModeBadge(theme, r.context);
|
|
1745
1742
|
const stepHeader = rRunning
|
|
1746
1743
|
? `${statusIcon} ${stepLabel}: ${theme.bold(theme.fg("warning", r.agent))}${contextBadge}${modelDisplay}${stats}`
|