pi-subagents 0.14.1 → 0.16.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/CHANGELOG.md +31 -0
- package/README.md +113 -5
- package/agent-management.ts +28 -7
- package/agent-manager-detail.ts +4 -1
- package/agent-manager-edit.ts +46 -6
- package/agent-manager.ts +28 -2
- package/agent-serializer.ts +6 -0
- package/agents/context-builder.md +24 -26
- package/agents/delegate.md +4 -1
- package/agents/planner.md +21 -15
- package/agents/researcher.md +23 -25
- package/agents/reviewer.md +22 -14
- package/agents/scout.md +24 -19
- package/agents/worker.md +20 -8
- package/agents.ts +153 -25
- package/async-execution.ts +18 -12
- package/async-job-tracker.ts +3 -3
- package/async-status.ts +3 -3
- package/chain-execution.ts +5 -5
- package/execution.ts +3 -3
- package/fork-context.ts +7 -2
- package/formatters.ts +18 -14
- package/index.ts +2 -2
- package/package.json +1 -1
- package/parallel-utils.ts +4 -15
- package/pi-args.ts +13 -6
- package/render.ts +73 -49
- package/schemas.ts +2 -1
- package/settings.ts +2 -2
- package/slash-commands.ts +20 -25
- package/subagent-executor.ts +100 -38
- package/subagent-prompt-runtime.ts +67 -0
- package/subagent-runner.ts +3 -8
- package/types.ts +31 -0
- package/utils.ts +29 -2
- package/worktree.ts +2 -1
package/parallel-utils.ts
CHANGED
|
@@ -1,10 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Parallel execution utilities for the async runner.
|
|
3
|
-
* Kept minimal and self-contained so the standalone runner can use them
|
|
4
|
-
* without pulling in the full extension dependency tree.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
/** A single agent step in the runner config */
|
|
8
1
|
export interface RunnerSubagentStep {
|
|
9
2
|
agent: string;
|
|
10
3
|
task: string;
|
|
@@ -15,13 +8,15 @@ export interface RunnerSubagentStep {
|
|
|
15
8
|
extensions?: string[];
|
|
16
9
|
mcpDirectTools?: string[];
|
|
17
10
|
systemPrompt?: string | null;
|
|
11
|
+
systemPromptMode?: "append" | "replace";
|
|
12
|
+
inheritProjectContext: boolean;
|
|
13
|
+
inheritSkills: boolean;
|
|
18
14
|
skills?: string[];
|
|
19
15
|
outputPath?: string;
|
|
20
16
|
sessionFile?: string;
|
|
21
17
|
maxSubagentDepth?: number;
|
|
22
18
|
}
|
|
23
19
|
|
|
24
|
-
/** Parallel step group — multiple agents running concurrently */
|
|
25
20
|
export interface ParallelStepGroup {
|
|
26
21
|
parallel: RunnerSubagentStep[];
|
|
27
22
|
concurrency?: number;
|
|
@@ -32,10 +27,9 @@ export interface ParallelStepGroup {
|
|
|
32
27
|
export type RunnerStep = RunnerSubagentStep | ParallelStepGroup;
|
|
33
28
|
|
|
34
29
|
export function isParallelGroup(step: RunnerStep): step is ParallelStepGroup {
|
|
35
|
-
return "parallel" in step && Array.isArray(
|
|
30
|
+
return "parallel" in step && Array.isArray(step.parallel);
|
|
36
31
|
}
|
|
37
32
|
|
|
38
|
-
/** Flatten runner steps into individual SubagentSteps for status tracking */
|
|
39
33
|
export function flattenSteps(steps: RunnerStep[]): RunnerSubagentStep[] {
|
|
40
34
|
const flat: RunnerSubagentStep[] = [];
|
|
41
35
|
for (const step of steps) {
|
|
@@ -48,16 +42,12 @@ export function flattenSteps(steps: RunnerStep[]): RunnerSubagentStep[] {
|
|
|
48
42
|
return flat;
|
|
49
43
|
}
|
|
50
44
|
|
|
51
|
-
/** Run async tasks with bounded concurrency, preserving result order.
|
|
52
|
-
* `staggerMs` adds a small delay between each worker's start to avoid
|
|
53
|
-
* file-lock contention when multiple subagents read shared config. */
|
|
54
45
|
export async function mapConcurrent<T, R>(
|
|
55
46
|
items: T[],
|
|
56
47
|
limit: number,
|
|
57
48
|
fn: (item: T, i: number) => Promise<R>,
|
|
58
49
|
staggerMs = 150,
|
|
59
50
|
): Promise<R[]> {
|
|
60
|
-
// Clamp to at least 1; NaN/undefined/0/negative all become 1
|
|
61
51
|
const safeLimit = Math.max(1, Math.floor(limit) || 1);
|
|
62
52
|
const results: R[] = new Array(items.length);
|
|
63
53
|
let next = 0;
|
|
@@ -90,7 +80,6 @@ export interface ParallelTaskResult {
|
|
|
90
80
|
outputTargetExists?: boolean;
|
|
91
81
|
}
|
|
92
82
|
|
|
93
|
-
/** Aggregate outputs from parallel tasks into a single string for {previous} */
|
|
94
83
|
export function aggregateParallelOutputs(
|
|
95
84
|
results: ParallelTaskResult[],
|
|
96
85
|
headerFormat: (index: number, agent: string) => string = (i, agent) =>
|
package/pi-args.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
2
|
import * as os from "node:os";
|
|
3
3
|
import * as path from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
4
5
|
|
|
5
6
|
const THINKING_LEVELS = ["off", "minimal", "low", "medium", "high", "xhigh"];
|
|
6
7
|
const TASK_ARG_LIMIT = 8000;
|
|
8
|
+
const PROMPT_RUNTIME_EXTENSION_PATH = path.join(path.dirname(fileURLToPath(import.meta.url)), "subagent-prompt-runtime.ts");
|
|
7
9
|
|
|
8
10
|
export interface BuildPiArgsInput {
|
|
9
11
|
baseArgs: string[];
|
|
@@ -13,9 +15,11 @@ export interface BuildPiArgsInput {
|
|
|
13
15
|
sessionFile?: string;
|
|
14
16
|
model?: string;
|
|
15
17
|
thinking?: string;
|
|
18
|
+
systemPromptMode?: "append" | "replace";
|
|
19
|
+
inheritProjectContext: boolean;
|
|
20
|
+
inheritSkills: boolean;
|
|
16
21
|
tools?: string[];
|
|
17
22
|
extensions?: string[];
|
|
18
|
-
skills?: string[];
|
|
19
23
|
systemPrompt?: string | null;
|
|
20
24
|
mcpDirectTools?: string[];
|
|
21
25
|
promptFileStem?: string;
|
|
@@ -69,28 +73,29 @@ export function buildPiArgs(input: BuildPiArgsInput): BuildPiArgsResult {
|
|
|
69
73
|
}
|
|
70
74
|
}
|
|
71
75
|
|
|
76
|
+
const runtimeExtensions = [PROMPT_RUNTIME_EXTENSION_PATH];
|
|
72
77
|
if (input.extensions !== undefined) {
|
|
73
78
|
args.push("--no-extensions");
|
|
74
|
-
for (const extPath of input.extensions) {
|
|
79
|
+
for (const extPath of [...new Set([...runtimeExtensions, ...toolExtensionPaths, ...input.extensions])]) {
|
|
75
80
|
args.push("--extension", extPath);
|
|
76
81
|
}
|
|
77
82
|
} else {
|
|
78
|
-
for (const extPath of toolExtensionPaths) {
|
|
83
|
+
for (const extPath of [...new Set([...runtimeExtensions, ...toolExtensionPaths])]) {
|
|
79
84
|
args.push("--extension", extPath);
|
|
80
85
|
}
|
|
81
86
|
}
|
|
82
87
|
|
|
83
|
-
if (
|
|
88
|
+
if (!input.inheritSkills) {
|
|
84
89
|
args.push("--no-skills");
|
|
85
90
|
}
|
|
86
91
|
|
|
87
92
|
let tempDir: string | undefined;
|
|
88
|
-
if (input.systemPrompt) {
|
|
93
|
+
if (input.systemPrompt !== undefined && input.systemPrompt !== null) {
|
|
89
94
|
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "pi-subagent-"));
|
|
90
95
|
const stem = (input.promptFileStem ?? "prompt").replace(/[^\w.-]/g, "_");
|
|
91
96
|
const promptPath = path.join(tempDir, `${stem}.md`);
|
|
92
97
|
fs.writeFileSync(promptPath, input.systemPrompt, { mode: 0o600 });
|
|
93
|
-
args.push("--append-system-prompt", promptPath);
|
|
98
|
+
args.push(input.systemPromptMode === "replace" ? "--system-prompt" : "--append-system-prompt", promptPath);
|
|
94
99
|
}
|
|
95
100
|
|
|
96
101
|
if (input.task.length > TASK_ARG_LIMIT) {
|
|
@@ -105,6 +110,8 @@ export function buildPiArgs(input: BuildPiArgsInput): BuildPiArgsResult {
|
|
|
105
110
|
}
|
|
106
111
|
|
|
107
112
|
const env: Record<string, string | undefined> = {};
|
|
113
|
+
env.PI_SUBAGENT_INHERIT_PROJECT_CONTEXT = input.inheritProjectContext ? "1" : "0";
|
|
114
|
+
env.PI_SUBAGENT_INHERIT_SKILLS = input.inheritSkills ? "1" : "0";
|
|
108
115
|
if (input.mcpDirectTools?.length) {
|
|
109
116
|
env.MCP_DIRECT_TOOLS = input.mcpDirectTools.join(",");
|
|
110
117
|
} else {
|
package/render.ts
CHANGED
|
@@ -101,6 +101,18 @@ function hasEmptyTextOutputWithoutOutputTarget(task: string, output: string): bo
|
|
|
101
101
|
return !extractOutputTarget(task);
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
+
function getToolCallLines(
|
|
105
|
+
result: Pick<Details["results"][number], "messages" | "toolCalls">,
|
|
106
|
+
expanded: boolean,
|
|
107
|
+
): string[] {
|
|
108
|
+
if (result.messages) {
|
|
109
|
+
return getDisplayItems(result.messages)
|
|
110
|
+
.filter((item): item is { type: "tool"; name: string; args: Record<string, unknown> } => item.type === "tool")
|
|
111
|
+
.map((item) => formatToolCall(item.name, item.args, expanded));
|
|
112
|
+
}
|
|
113
|
+
return result.toolCalls?.map((toolCall) => expanded ? toolCall.expandedText : toolCall.text) ?? [];
|
|
114
|
+
}
|
|
115
|
+
|
|
104
116
|
/**
|
|
105
117
|
* Render the async jobs widget
|
|
106
118
|
*/
|
|
@@ -165,7 +177,7 @@ export function renderWidget(ctx: ExtensionContext, jobs: AsyncJobState[]): void
|
|
|
165
177
|
*/
|
|
166
178
|
export function renderSubagentResult(
|
|
167
179
|
result: AgentToolResult<Details>,
|
|
168
|
-
|
|
180
|
+
options: { expanded: boolean },
|
|
169
181
|
theme: Theme,
|
|
170
182
|
): Component {
|
|
171
183
|
const d = result.details;
|
|
@@ -176,6 +188,7 @@ export function renderSubagentResult(
|
|
|
176
188
|
return new Text(truncLine(`${contextPrefix}${text}`, getTermWidth() - 4), 0, 0);
|
|
177
189
|
}
|
|
178
190
|
|
|
191
|
+
const expanded = options.expanded;
|
|
179
192
|
const mdTheme = getMarkdownTheme();
|
|
180
193
|
|
|
181
194
|
if (d.mode === "single" && d.results.length === 1) {
|
|
@@ -198,15 +211,17 @@ export function renderSubagentResult(
|
|
|
198
211
|
: "";
|
|
199
212
|
|
|
200
213
|
const w = getTermWidth() - 4;
|
|
214
|
+
const fit = (text: string) => expanded ? text : truncLine(text, w);
|
|
215
|
+
const toolCallLines = getToolCallLines(r, expanded);
|
|
201
216
|
const c = new Container();
|
|
202
|
-
c.addChild(new Text(
|
|
217
|
+
c.addChild(new Text(fit(`${icon} ${theme.fg("toolTitle", theme.bold(r.agent))}${contextBadge}${progressInfo}`), 0, 0));
|
|
203
218
|
c.addChild(new Spacer(1));
|
|
204
219
|
const taskMaxLen = Math.max(20, w - 8);
|
|
205
|
-
const taskPreview = r.task.length
|
|
206
|
-
?
|
|
207
|
-
: r.task
|
|
220
|
+
const taskPreview = expanded || r.task.length <= taskMaxLen
|
|
221
|
+
? r.task
|
|
222
|
+
: `${r.task.slice(0, taskMaxLen)}...`;
|
|
208
223
|
c.addChild(
|
|
209
|
-
new Text(
|
|
224
|
+
new Text(fit(theme.fg("dim", `Task: ${taskPreview}`)), 0, 0),
|
|
210
225
|
);
|
|
211
226
|
c.addChild(new Spacer(1));
|
|
212
227
|
|
|
@@ -214,58 +229,58 @@ export function renderSubagentResult(
|
|
|
214
229
|
if (r.progress.currentTool) {
|
|
215
230
|
const maxToolArgsLen = Math.max(50, w - 20);
|
|
216
231
|
const toolArgsPreview = r.progress.currentToolArgs
|
|
217
|
-
? (r.progress.currentToolArgs.length
|
|
218
|
-
?
|
|
219
|
-
: r.progress.currentToolArgs)
|
|
232
|
+
? (expanded || r.progress.currentToolArgs.length <= maxToolArgsLen
|
|
233
|
+
? r.progress.currentToolArgs
|
|
234
|
+
: `${r.progress.currentToolArgs.slice(0, maxToolArgsLen)}...`)
|
|
220
235
|
: "";
|
|
221
236
|
const toolLine = toolArgsPreview
|
|
222
237
|
? `${r.progress.currentTool}: ${toolArgsPreview}`
|
|
223
238
|
: r.progress.currentTool;
|
|
224
|
-
c.addChild(new Text(
|
|
239
|
+
c.addChild(new Text(fit(theme.fg("warning", `> ${toolLine}`)), 0, 0));
|
|
225
240
|
}
|
|
226
241
|
if (r.progress.recentTools?.length) {
|
|
227
242
|
for (const t of r.progress.recentTools.slice(-3)) {
|
|
228
243
|
const maxArgsLen = Math.max(40, w - 24);
|
|
229
|
-
const argsPreview = t.args.length
|
|
230
|
-
?
|
|
231
|
-
: t.args
|
|
232
|
-
c.addChild(new Text(
|
|
244
|
+
const argsPreview = expanded || t.args.length <= maxArgsLen
|
|
245
|
+
? t.args
|
|
246
|
+
: `${t.args.slice(0, maxArgsLen)}...`;
|
|
247
|
+
c.addChild(new Text(fit(theme.fg("dim", `${t.tool}: ${argsPreview}`)), 0, 0));
|
|
233
248
|
}
|
|
234
249
|
}
|
|
235
250
|
for (const line of (r.progress.recentOutput ?? []).slice(-5)) {
|
|
236
|
-
c.addChild(new Text(
|
|
251
|
+
c.addChild(new Text(fit(theme.fg("dim", ` ${line}`)), 0, 0));
|
|
237
252
|
}
|
|
238
253
|
if (r.progress.currentTool || r.progress.recentTools?.length || r.progress.recentOutput?.length) {
|
|
239
254
|
c.addChild(new Spacer(1));
|
|
240
255
|
}
|
|
241
256
|
}
|
|
242
257
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
258
|
+
if (expanded) {
|
|
259
|
+
for (const line of toolCallLines) {
|
|
260
|
+
c.addChild(new Text(fit(theme.fg("muted", line)), 0, 0));
|
|
261
|
+
}
|
|
262
|
+
if (toolCallLines.length) c.addChild(new Spacer(1));
|
|
247
263
|
}
|
|
248
|
-
if (items.length) c.addChild(new Spacer(1));
|
|
249
264
|
|
|
250
265
|
if (output) c.addChild(new Markdown(output, 0, 0, mdTheme));
|
|
251
266
|
c.addChild(new Spacer(1));
|
|
252
267
|
if (r.skills?.length) {
|
|
253
|
-
c.addChild(new Text(
|
|
268
|
+
c.addChild(new Text(fit(theme.fg("dim", `Skills: ${r.skills.join(", ")}`)), 0, 0));
|
|
254
269
|
}
|
|
255
270
|
if (r.skillsWarning) {
|
|
256
|
-
c.addChild(new Text(
|
|
271
|
+
c.addChild(new Text(fit(theme.fg("warning", `Warning: ${r.skillsWarning}`)), 0, 0));
|
|
257
272
|
}
|
|
258
273
|
if (r.attemptedModels && r.attemptedModels.length > 1) {
|
|
259
|
-
c.addChild(new Text(
|
|
274
|
+
c.addChild(new Text(fit(theme.fg("dim", `Fallbacks: ${r.attemptedModels.join(" → ")}`)), 0, 0));
|
|
260
275
|
}
|
|
261
|
-
c.addChild(new Text(
|
|
276
|
+
c.addChild(new Text(fit(theme.fg("dim", formatUsage(r.usage, r.model))), 0, 0));
|
|
262
277
|
if (r.sessionFile) {
|
|
263
|
-
c.addChild(new Text(
|
|
278
|
+
c.addChild(new Text(fit(theme.fg("dim", `Session: ${shortenPath(r.sessionFile)}`)), 0, 0));
|
|
264
279
|
}
|
|
265
280
|
|
|
266
281
|
if (r.artifactPaths) {
|
|
267
282
|
c.addChild(new Spacer(1));
|
|
268
|
-
c.addChild(new Text(
|
|
283
|
+
c.addChild(new Text(fit(theme.fg("dim", `Artifacts: ${shortenPath(r.artifactPaths.outputPath)}`)), 0, 0));
|
|
269
284
|
}
|
|
270
285
|
return c;
|
|
271
286
|
}
|
|
@@ -341,16 +356,17 @@ export function renderSubagentResult(
|
|
|
341
356
|
: null;
|
|
342
357
|
|
|
343
358
|
const w = getTermWidth() - 4;
|
|
359
|
+
const fit = (text: string) => expanded ? text : truncLine(text, w);
|
|
344
360
|
const c = new Container();
|
|
345
361
|
c.addChild(
|
|
346
362
|
new Text(
|
|
347
|
-
|
|
363
|
+
fit(`${icon} ${theme.fg("toolTitle", theme.bold(modeLabel))}${contextBadge}${stepInfo}${summaryStr}`),
|
|
348
364
|
0,
|
|
349
365
|
0,
|
|
350
366
|
),
|
|
351
367
|
);
|
|
352
368
|
if (chainVis) {
|
|
353
|
-
c.addChild(new Text(
|
|
369
|
+
c.addChild(new Text(fit(` ${chainVis}`), 0, 0));
|
|
354
370
|
}
|
|
355
371
|
|
|
356
372
|
const useResultsDirectly = hasParallelInChain || !d.chainAgents?.length;
|
|
@@ -365,7 +381,7 @@ export function renderSubagentResult(
|
|
|
365
381
|
: (d.chainAgents![i] || r?.agent || `step-${i + 1}`);
|
|
366
382
|
|
|
367
383
|
if (!r) {
|
|
368
|
-
c.addChild(new Text(
|
|
384
|
+
c.addChild(new Text(fit(theme.fg("dim", ` Step ${i + 1}: ${agentName}`)), 0, 0));
|
|
369
385
|
c.addChild(new Text(theme.fg("dim", ` status: pending`), 0, 0));
|
|
370
386
|
c.addChild(new Spacer(1));
|
|
371
387
|
continue;
|
|
@@ -389,58 +405,66 @@ export function renderSubagentResult(
|
|
|
389
405
|
const stepHeader = rRunning
|
|
390
406
|
? `${statusIcon} Step ${i + 1}: ${theme.bold(theme.fg("warning", r.agent))}${modelDisplay}${stats}`
|
|
391
407
|
: `${statusIcon} Step ${i + 1}: ${theme.bold(r.agent)}${modelDisplay}${stats}`;
|
|
392
|
-
|
|
408
|
+
const toolCallLines = getToolCallLines(r, expanded);
|
|
409
|
+
c.addChild(new Text(fit(stepHeader), 0, 0));
|
|
393
410
|
|
|
394
411
|
const taskMaxLen = Math.max(20, w - 12);
|
|
395
|
-
const taskPreview = r.task.length
|
|
396
|
-
?
|
|
397
|
-
: r.task
|
|
398
|
-
c.addChild(new Text(
|
|
412
|
+
const taskPreview = expanded || r.task.length <= taskMaxLen
|
|
413
|
+
? r.task
|
|
414
|
+
: `${r.task.slice(0, taskMaxLen)}...`;
|
|
415
|
+
c.addChild(new Text(fit(theme.fg("dim", ` task: ${taskPreview}`)), 0, 0));
|
|
399
416
|
|
|
400
417
|
const outputTarget = extractOutputTarget(r.task);
|
|
401
418
|
if (outputTarget) {
|
|
402
|
-
c.addChild(new Text(
|
|
419
|
+
c.addChild(new Text(fit(theme.fg("dim", ` output: ${outputTarget}`)), 0, 0));
|
|
403
420
|
}
|
|
404
421
|
|
|
405
422
|
if (r.skills?.length) {
|
|
406
|
-
c.addChild(new Text(
|
|
423
|
+
c.addChild(new Text(fit(theme.fg("dim", ` skills: ${r.skills.join(", ")}`)), 0, 0));
|
|
407
424
|
}
|
|
408
425
|
if (r.skillsWarning) {
|
|
409
|
-
c.addChild(new Text(
|
|
426
|
+
c.addChild(new Text(fit(theme.fg("warning", ` Warning: ${r.skillsWarning}`)), 0, 0));
|
|
410
427
|
}
|
|
411
428
|
if (r.attemptedModels && r.attemptedModels.length > 1) {
|
|
412
|
-
c.addChild(new Text(
|
|
429
|
+
c.addChild(new Text(fit(theme.fg("dim", ` fallbacks: ${r.attemptedModels.join(" → ")}`)), 0, 0));
|
|
413
430
|
}
|
|
414
431
|
|
|
415
432
|
if (rRunning && rProg) {
|
|
416
433
|
if (rProg.skills?.length) {
|
|
417
|
-
c.addChild(new Text(
|
|
434
|
+
c.addChild(new Text(fit(theme.fg("accent", ` skills: ${rProg.skills.join(", ")}`)), 0, 0));
|
|
418
435
|
}
|
|
419
436
|
if (rProg.currentTool) {
|
|
420
437
|
const maxToolArgsLen = Math.max(50, w - 20);
|
|
421
438
|
const toolArgsPreview = rProg.currentToolArgs
|
|
422
|
-
? (rProg.currentToolArgs.length
|
|
423
|
-
?
|
|
424
|
-
: rProg.currentToolArgs)
|
|
439
|
+
? (expanded || rProg.currentToolArgs.length <= maxToolArgsLen
|
|
440
|
+
? rProg.currentToolArgs
|
|
441
|
+
: `${rProg.currentToolArgs.slice(0, maxToolArgsLen)}...`)
|
|
425
442
|
: "";
|
|
426
443
|
const toolLine = toolArgsPreview
|
|
427
444
|
? `${rProg.currentTool}: ${toolArgsPreview}`
|
|
428
445
|
: rProg.currentTool;
|
|
429
|
-
c.addChild(new Text(
|
|
446
|
+
c.addChild(new Text(fit(theme.fg("warning", ` > ${toolLine}`)), 0, 0));
|
|
430
447
|
}
|
|
431
448
|
if (rProg.recentTools?.length) {
|
|
432
449
|
for (const t of rProg.recentTools.slice(-3)) {
|
|
433
450
|
const maxArgsLen = Math.max(40, w - 30);
|
|
434
|
-
const argsPreview = t.args.length
|
|
435
|
-
?
|
|
436
|
-
: t.args
|
|
437
|
-
c.addChild(new Text(
|
|
451
|
+
const argsPreview = expanded || t.args.length <= maxArgsLen
|
|
452
|
+
? t.args
|
|
453
|
+
: `${t.args.slice(0, maxArgsLen)}...`;
|
|
454
|
+
c.addChild(new Text(fit(theme.fg("dim", ` ${t.tool}: ${argsPreview}`)), 0, 0));
|
|
438
455
|
}
|
|
439
456
|
}
|
|
440
457
|
const recentLines = (rProg.recentOutput ?? []).slice(-5);
|
|
441
458
|
for (const line of recentLines) {
|
|
442
|
-
c.addChild(new Text(
|
|
459
|
+
c.addChild(new Text(fit(theme.fg("dim", ` ${line}`)), 0, 0));
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
if (expanded && !rRunning) {
|
|
464
|
+
for (const line of toolCallLines) {
|
|
465
|
+
c.addChild(new Text(fit(theme.fg("muted", ` ${line}`)), 0, 0));
|
|
443
466
|
}
|
|
467
|
+
if (toolCallLines.length) c.addChild(new Spacer(1));
|
|
444
468
|
}
|
|
445
469
|
|
|
446
470
|
c.addChild(new Spacer(1));
|
|
@@ -448,7 +472,7 @@ export function renderSubagentResult(
|
|
|
448
472
|
|
|
449
473
|
if (d.artifacts) {
|
|
450
474
|
c.addChild(new Spacer(1));
|
|
451
|
-
c.addChild(new Text(
|
|
475
|
+
c.addChild(new Text(fit(theme.fg("dim", `Artifacts dir: ${shortenPath(d.artifacts.dir)}`)), 0, 0));
|
|
452
476
|
}
|
|
453
477
|
return c;
|
|
454
478
|
}
|
package/schemas.ts
CHANGED
|
@@ -70,9 +70,10 @@ export const SubagentParams = Type.Object({
|
|
|
70
70
|
})),
|
|
71
71
|
// Agent/chain configuration for create/update (nested to avoid conflicts with execution fields)
|
|
72
72
|
config: Type.Optional(Type.Any({
|
|
73
|
-
description: "Agent or chain config for create/update. Agent: name, description, scope ('user'|'project', default 'user'), systemPrompt, model, tools (comma-separated), extensions (comma-separated), skills (comma-separated), thinking, output, reads, progress, maxSubagentDepth. Chain: name, description, scope, steps (array of {agent, task?, output?, reads?, model?, skills?, progress?}). Presence of 'steps' creates a chain instead of an agent."
|
|
73
|
+
description: "Agent or chain config for create/update. Agent: name, description, scope ('user'|'project', default 'user'), systemPrompt, systemPromptMode, inheritProjectContext, inheritSkills, model, tools (comma-separated), extensions (comma-separated), skills (comma-separated), thinking, output, reads, progress, maxSubagentDepth. Chain: name, description, scope, steps (array of {agent, task?, output?, reads?, model?, skills?, progress?}). Presence of 'steps' creates a chain instead of an agent."
|
|
74
74
|
})),
|
|
75
75
|
tasks: Type.Optional(Type.Array(TaskItem, { description: "PARALLEL mode: [{agent, task, count?}, ...]" })),
|
|
76
|
+
concurrency: Type.Optional(Type.Integer({ minimum: 1, description: "Top-level PARALLEL mode only: max concurrent tasks. Defaults to config.parallel.concurrency or 4." })),
|
|
76
77
|
worktree: Type.Optional(Type.Boolean({
|
|
77
78
|
description: "Create isolated git worktrees for each parallel task. " +
|
|
78
79
|
"Prevents filesystem conflicts. Requires clean git state. " +
|
package/settings.ts
CHANGED
|
@@ -356,5 +356,5 @@ export function createParallelDirs(
|
|
|
356
356
|
}
|
|
357
357
|
}
|
|
358
358
|
|
|
359
|
-
export type { ParallelTaskResult } from "./parallel-utils.
|
|
360
|
-
export { aggregateParallelOutputs } from "./parallel-utils.
|
|
359
|
+
export type { ParallelTaskResult } from "./parallel-utils.ts";
|
|
360
|
+
export { aggregateParallelOutputs } from "./parallel-utils.ts";
|
package/slash-commands.ts
CHANGED
|
@@ -1,20 +1,19 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import type { ExtensionAPI, ExtensionContext } from "@mariozechner/pi-coding-agent";
|
|
3
3
|
import { Key, matchesKey } from "@mariozechner/pi-tui";
|
|
4
|
-
import { discoverAgents, discoverAgentsAll } from "./agents.
|
|
5
|
-
import { AgentManagerComponent, type ManagerResult } from "./agent-manager.
|
|
6
|
-
import { SubagentsStatusComponent } from "./subagents-status.
|
|
7
|
-
import { discoverAvailableSkills } from "./skills.
|
|
8
|
-
import type { SubagentParamsLike } from "./subagent-executor.
|
|
9
|
-
import type { SlashSubagentResponse, SlashSubagentUpdate } from "./slash-bridge.
|
|
4
|
+
import { discoverAgents, discoverAgentsAll } from "./agents.ts";
|
|
5
|
+
import { AgentManagerComponent, type ManagerResult } from "./agent-manager.ts";
|
|
6
|
+
import { SubagentsStatusComponent } from "./subagents-status.ts";
|
|
7
|
+
import { discoverAvailableSkills } from "./skills.ts";
|
|
8
|
+
import type { SubagentParamsLike } from "./subagent-executor.ts";
|
|
9
|
+
import type { SlashSubagentResponse, SlashSubagentUpdate } from "./slash-bridge.ts";
|
|
10
10
|
import {
|
|
11
11
|
applySlashUpdate,
|
|
12
12
|
buildSlashInitialResult,
|
|
13
13
|
failSlashResult,
|
|
14
14
|
finalizeSlashResult,
|
|
15
|
-
} from "./slash-live-state.
|
|
15
|
+
} from "./slash-live-state.ts";
|
|
16
16
|
import {
|
|
17
|
-
MAX_PARALLEL,
|
|
18
17
|
SLASH_RESULT_TYPE,
|
|
19
18
|
SLASH_SUBAGENT_CANCEL_EVENT,
|
|
20
19
|
SLASH_SUBAGENT_REQUEST_EVENT,
|
|
@@ -22,7 +21,7 @@ import {
|
|
|
22
21
|
SLASH_SUBAGENT_STARTED_EVENT,
|
|
23
22
|
SLASH_SUBAGENT_UPDATE_EVENT,
|
|
24
23
|
type SubagentState,
|
|
25
|
-
} from "./types.
|
|
24
|
+
} from "./types.ts";
|
|
26
25
|
|
|
27
26
|
interface InlineConfig {
|
|
28
27
|
output?: string | false;
|
|
@@ -453,22 +452,18 @@ export function registerSlashCommands(
|
|
|
453
452
|
pi.registerCommand("parallel", {
|
|
454
453
|
description: "Run agents in parallel: /parallel scout \"task1\" -> reviewer \"task2\" [--bg] [--fork]",
|
|
455
454
|
getArgumentCompletions: makeAgentCompletions(state, true),
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
...(config.progress !== undefined ? { progress: config.progress } : {}),
|
|
469
|
-
}));
|
|
470
|
-
const params: SubagentParamsLike = { tasks, clarify: false, agentScope: "both" };
|
|
471
|
-
if (bg) params.async = true;
|
|
455
|
+
handler: async (args, ctx) => {
|
|
456
|
+
const { args: cleanedArgs, bg, fork } = extractExecutionFlags(args);
|
|
457
|
+
const parsed = parseAgentArgs(state, cleanedArgs, "parallel", ctx);
|
|
458
|
+
if (!parsed) return;
|
|
459
|
+
const tasks = parsed.steps.map(({ name, config, task: stepTask }) => ({
|
|
460
|
+
agent: name,
|
|
461
|
+
task: stepTask ?? parsed.task,
|
|
462
|
+
...(config.model ? { model: config.model } : {}),
|
|
463
|
+
...(config.skill !== undefined ? { skill: config.skill } : {}),
|
|
464
|
+
}));
|
|
465
|
+
const params: SubagentParamsLike = { tasks, clarify: false, agentScope: "both" };
|
|
466
|
+
if (bg) params.async = true;
|
|
472
467
|
if (fork) params.context = "fork";
|
|
473
468
|
await runSlashSubagent(pi, ctx, params);
|
|
474
469
|
},
|