@ryan_nookpi/pi-extension-subagent 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.
- package/package.json +1 -1
- package/tool-render.ts +99 -4
package/package.json
CHANGED
package/tool-render.ts
CHANGED
|
@@ -5,10 +5,11 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import { getMarkdownTheme, type ThemeColor } from "@earendil-works/pi-coding-agent";
|
|
8
|
-
import { Container, Markdown, Spacer, Text } from "@earendil-works/pi-tui";
|
|
8
|
+
import { Container, Markdown, Spacer, Text, visibleWidth } from "@earendil-works/pi-tui";
|
|
9
|
+
import { parseSubagentToolCommand } from "./cli.js";
|
|
9
10
|
import { formatToolCall, formatUsageStats } from "./format.js";
|
|
10
11
|
import { getDisplayItems, getFinalOutput } from "./runner.js";
|
|
11
|
-
import { COLLAPSED_ITEM_COUNT } from "./store.js";
|
|
12
|
+
import { COLLAPSED_ITEM_COUNT, truncateText } from "./store.js";
|
|
12
13
|
import type { DisplayItem, SubagentDetails } from "./types.js";
|
|
13
14
|
|
|
14
15
|
// ─── Helpers (internal) ──────────────────────────────────────────────────────
|
|
@@ -24,6 +25,90 @@ type ToolRenderResult = {
|
|
|
24
25
|
};
|
|
25
26
|
|
|
26
27
|
type ToolRenderArgs = { command?: unknown };
|
|
28
|
+
type ToolCallRenderContext = { expanded: boolean };
|
|
29
|
+
type CompactBlock = { agent: string; task: string };
|
|
30
|
+
|
|
31
|
+
const COMPACT_TASK_PREVIEW_WIDTH = 72;
|
|
32
|
+
const COMPACT_AGENT_WIDTH = 20;
|
|
33
|
+
const COMPACT_MAX_ITEMS = 3;
|
|
34
|
+
|
|
35
|
+
function normalizeTaskPreview(task: string): string {
|
|
36
|
+
return truncateText(task.replace(/\s+/g, " ").trim(), COMPACT_TASK_PREVIEW_WIDTH);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function getContextLabel(params: Record<string, unknown>): "main" | "isolated" {
|
|
40
|
+
return params.contextMode === "main" ? "main" : "isolated";
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function getCompactBlocks(value: unknown): CompactBlock[] | null {
|
|
44
|
+
if (!Array.isArray(value)) return null;
|
|
45
|
+
const blocks: CompactBlock[] = [];
|
|
46
|
+
for (const item of value) {
|
|
47
|
+
if (!item || typeof item !== "object") return null;
|
|
48
|
+
const agent = "agent" in item ? item.agent : undefined;
|
|
49
|
+
const task = "task" in item ? item.task : undefined;
|
|
50
|
+
if (typeof agent !== "string" || typeof task !== "string") return null;
|
|
51
|
+
blocks.push({ agent, task });
|
|
52
|
+
}
|
|
53
|
+
return blocks;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function formatCompactAgent(agent: string, width: number): string {
|
|
57
|
+
const label = truncateText(agent, COMPACT_AGENT_WIDTH);
|
|
58
|
+
return `${label}${" ".repeat(Math.max(0, width - visibleWidth(label)))}`;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function renderCompactBlocks(kind: "batch" | "chain", params: Record<string, unknown>): string | null {
|
|
62
|
+
const blocks = getCompactBlocks(kind === "batch" ? params.runs : params.steps);
|
|
63
|
+
if (!blocks || blocks.length === 0) return null;
|
|
64
|
+
|
|
65
|
+
const visibleBlocks = blocks.slice(0, COMPACT_MAX_ITEMS);
|
|
66
|
+
const agentWidth = Math.max(
|
|
67
|
+
...visibleBlocks.map((block) => visibleWidth(truncateText(block.agent, COMPACT_AGENT_WIDTH))),
|
|
68
|
+
);
|
|
69
|
+
const contextLabel = getContextLabel(params);
|
|
70
|
+
const executionLabel = kind === "batch" ? "parallel" : "sequential";
|
|
71
|
+
const lines = [` ${kind} · ${contextLabel} · ${blocks.length} ${executionLabel}`];
|
|
72
|
+
|
|
73
|
+
for (const [index, block] of visibleBlocks.entries()) {
|
|
74
|
+
const agent = formatCompactAgent(block.agent, agentWidth);
|
|
75
|
+
const task = normalizeTaskPreview(block.task);
|
|
76
|
+
if (kind === "batch") {
|
|
77
|
+
lines.push(` ∥ ${agent} ${task}`);
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const connector = index === 0 ? "" : `${" ".repeat(index - 1)}└─ `;
|
|
82
|
+
lines.push(` ${connector}${index + 1} ${agent} ${task}`);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const hiddenCount = blocks.length - visibleBlocks.length;
|
|
86
|
+
if (hiddenCount > 0) {
|
|
87
|
+
if (kind === "batch") lines.push(` ∥ +${hiddenCount} jobs`);
|
|
88
|
+
else lines.push(` ${" ".repeat(visibleBlocks.length - 1)}└─ +${hiddenCount} steps`);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return lines.join("\n");
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function renderCompactLaunch(command: unknown): string | null {
|
|
95
|
+
const parsed = parseSubagentToolCommand(command);
|
|
96
|
+
if (parsed.type !== "params") return null;
|
|
97
|
+
|
|
98
|
+
const { params } = parsed;
|
|
99
|
+
if (params.asyncAction === "batch") return renderCompactBlocks("batch", params);
|
|
100
|
+
if (params.asyncAction === "chain") return renderCompactBlocks("chain", params);
|
|
101
|
+
if (typeof params.task !== "string") return null;
|
|
102
|
+
|
|
103
|
+
const contextLabel = getContextLabel(params);
|
|
104
|
+
const task = normalizeTaskPreview(params.task);
|
|
105
|
+
if (typeof params.runId === "number") {
|
|
106
|
+
const agent = typeof params.agent === "string" ? ` · ${params.agent}` : "";
|
|
107
|
+
return ` continue · #${params.runId}${agent} · ${contextLabel}\n ${task}`;
|
|
108
|
+
}
|
|
109
|
+
if (typeof params.agent !== "string") return null;
|
|
110
|
+
return ` run · ${params.agent} · ${contextLabel}\n ${task}`;
|
|
111
|
+
}
|
|
27
112
|
|
|
28
113
|
function renderDisplayItems(items: DisplayItem[], expanded: boolean, theme: RenderTheme, limit?: number): string {
|
|
29
114
|
const toShow = limit ? items.slice(-limit) : items;
|
|
@@ -43,15 +128,25 @@ function renderDisplayItems(items: DisplayItem[], expanded: boolean, theme: Rend
|
|
|
43
128
|
|
|
44
129
|
// ─── renderCall ──────────────────────────────────────────────────────────────
|
|
45
130
|
|
|
46
|
-
export function renderSubagentToolCall(
|
|
131
|
+
export function renderSubagentToolCall(
|
|
132
|
+
args: ToolRenderArgs,
|
|
133
|
+
theme: RenderTheme,
|
|
134
|
+
context: ToolCallRenderContext = { expanded: false },
|
|
135
|
+
) {
|
|
47
136
|
const raw = typeof args.command === "string" ? args.command.trim() : "";
|
|
48
137
|
const command = raw || "subagent help";
|
|
138
|
+
let text = theme.fg("toolTitle", theme.bold("subagent ")) + theme.fg("accent", "cli");
|
|
139
|
+
|
|
140
|
+
if (!context.expanded) {
|
|
141
|
+
const compactLaunch = renderCompactLaunch(args.command);
|
|
142
|
+
if (compactLaunch) return new Text(`${text}\n${theme.fg("dim", compactLaunch)}`, 0, 0);
|
|
143
|
+
}
|
|
144
|
+
|
|
49
145
|
const MAX_CALL_LINES = 5;
|
|
50
146
|
const commandLines = command.split("\n");
|
|
51
147
|
const truncated = commandLines.length > MAX_CALL_LINES;
|
|
52
148
|
const preview = truncated ? commandLines.slice(0, MAX_CALL_LINES).join("\n") : command;
|
|
53
149
|
|
|
54
|
-
let text = theme.fg("toolTitle", theme.bold("subagent ")) + theme.fg("accent", "cli");
|
|
55
150
|
text += `\n ${theme.fg("dim", preview)}`;
|
|
56
151
|
if (truncated) text += `\n ${theme.fg("muted", `... +${commandLines.length - MAX_CALL_LINES} more lines`)}`;
|
|
57
152
|
return new Text(text, 0, 0);
|