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/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((step as ParallelStepGroup).parallel);
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 ((input.skills?.length ?? 0) > 0) {
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
- _options: { expanded: boolean },
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(truncLine(`${icon} ${theme.fg("toolTitle", theme.bold(r.agent))}${contextBadge}${progressInfo}`, w), 0, 0));
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 > taskMaxLen
206
- ? `${r.task.slice(0, taskMaxLen)}...`
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(truncLine(theme.fg("dim", `Task: ${taskPreview}`), w), 0, 0),
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 > maxToolArgsLen
218
- ? `${r.progress.currentToolArgs.slice(0, maxToolArgsLen)}...`
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(truncLine(theme.fg("warning", `> ${toolLine}`), w), 0, 0));
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 > maxArgsLen
230
- ? `${t.args.slice(0, maxArgsLen)}...`
231
- : t.args;
232
- c.addChild(new Text(truncLine(theme.fg("dim", `${t.tool}: ${argsPreview}`), w), 0, 0));
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(truncLine(theme.fg("dim", ` ${line}`), w), 0, 0));
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
- const items = getDisplayItems(r.messages);
244
- for (const item of items) {
245
- if (item.type === "tool")
246
- c.addChild(new Text(truncLine(theme.fg("muted", formatToolCall(item.name, item.args)), w), 0, 0));
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(truncLine(theme.fg("dim", `Skills: ${r.skills.join(", ")}`), w), 0, 0));
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(truncLine(theme.fg("warning", `Warning: ${r.skillsWarning}`), w), 0, 0));
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(truncLine(theme.fg("dim", `Fallbacks: ${r.attemptedModels.join(" → ")}`), w), 0, 0));
274
+ c.addChild(new Text(fit(theme.fg("dim", `Fallbacks: ${r.attemptedModels.join(" → ")}`)), 0, 0));
260
275
  }
261
- c.addChild(new Text(truncLine(theme.fg("dim", formatUsage(r.usage, r.model)), w), 0, 0));
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(truncLine(theme.fg("dim", `Session: ${shortenPath(r.sessionFile)}`), w), 0, 0));
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(truncLine(theme.fg("dim", `Artifacts: ${shortenPath(r.artifactPaths.outputPath)}`), w), 0, 0));
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
- truncLine(`${icon} ${theme.fg("toolTitle", theme.bold(modeLabel))}${contextBadge}${stepInfo}${summaryStr}`, w),
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(truncLine(` ${chainVis}`, w), 0, 0));
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(truncLine(theme.fg("dim", ` Step ${i + 1}: ${agentName}`), w), 0, 0));
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
- c.addChild(new Text(truncLine(stepHeader, w), 0, 0));
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 > taskMaxLen
396
- ? `${r.task.slice(0, taskMaxLen)}...`
397
- : r.task;
398
- c.addChild(new Text(truncLine(theme.fg("dim", ` task: ${taskPreview}`), w), 0, 0));
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(truncLine(theme.fg("dim", ` output: ${outputTarget}`), w), 0, 0));
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(truncLine(theme.fg("dim", ` skills: ${r.skills.join(", ")}`), w), 0, 0));
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(truncLine(theme.fg("warning", ` Warning: ${r.skillsWarning}`), w), 0, 0));
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(truncLine(theme.fg("dim", ` fallbacks: ${r.attemptedModels.join(" → ")}`), w), 0, 0));
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(truncLine(theme.fg("accent", ` skills: ${rProg.skills.join(", ")}`), w), 0, 0));
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 > maxToolArgsLen
423
- ? `${rProg.currentToolArgs.slice(0, maxToolArgsLen)}...`
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(truncLine(theme.fg("warning", ` > ${toolLine}`), w), 0, 0));
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 > maxArgsLen
435
- ? `${t.args.slice(0, maxArgsLen)}...`
436
- : t.args;
437
- c.addChild(new Text(truncLine(theme.fg("dim", ` ${t.tool}: ${argsPreview}`), w), 0, 0));
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(truncLine(theme.fg("dim", ` ${line}`), w), 0, 0));
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(truncLine(theme.fg("dim", `Artifacts dir: ${shortenPath(d.artifacts.dir)}`), w), 0, 0));
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.js";
360
- export { aggregateParallelOutputs } from "./parallel-utils.js";
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.js";
5
- import { AgentManagerComponent, type ManagerResult } from "./agent-manager.js";
6
- import { SubagentsStatusComponent } from "./subagents-status.js";
7
- import { discoverAvailableSkills } from "./skills.js";
8
- import type { SubagentParamsLike } from "./subagent-executor.js";
9
- import type { SlashSubagentResponse, SlashSubagentUpdate } from "./slash-bridge.js";
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.js";
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.js";
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
- handler: async (args, ctx) => {
457
- const { args: cleanedArgs, bg, fork } = extractExecutionFlags(args);
458
- const parsed = parseAgentArgs(state, cleanedArgs, "parallel", ctx);
459
- if (!parsed) return;
460
- if (parsed.steps.length > MAX_PARALLEL) { ctx.ui.notify(`Max ${MAX_PARALLEL} parallel tasks`, "error"); return; }
461
- const tasks = parsed.steps.map(({ name, config, task: stepTask }) => ({
462
- agent: name,
463
- task: stepTask ?? parsed.task,
464
- ...(config.output !== undefined ? { output: config.output } : {}),
465
- ...(config.reads !== undefined ? { reads: config.reads } : {}),
466
- ...(config.model ? { model: config.model } : {}),
467
- ...(config.skill !== undefined ? { skill: config.skill } : {}),
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
  },