oira666_pi-subagent 0.2.22 → 0.2.25

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/README.md CHANGED
@@ -117,7 +117,7 @@ pi --no-subagent-prevent-cycles # allow cycles (not recommended)
117
117
 
118
118
  | Env Var | Default | Description |
119
119
  | -------------------------------- | ------- | ---------------------------------------- |
120
- | `PI_SUBAGENT_MAX_PARALLEL_TASKS` | `16` | Max tasks per single call |
120
+ | `PI_SUBAGENT_MAX_PARALLEL_TASKS` | `30` | Max tasks per single call |
121
121
  | `PI_SUBAGENT_MAX_CONCURRENCY` | `8` | Max subagents running simultaneously |
122
122
 
123
123
  ## Steering Running Subagents
package/index.ts CHANGED
@@ -22,7 +22,7 @@ import {
22
22
  SUBAGENT_RESUME_PROMPT_ENV,
23
23
  branchEntries,
24
24
  buildSubagentSessionDir,
25
- findLatestResumableSubagentCall,
25
+ findLatestResumableSubagentCalls,
26
26
  getDefaultSubagentSessionRoot,
27
27
  isFinishedResult,
28
28
  parseBooleanEnv,
@@ -55,7 +55,7 @@ import {
55
55
  // ---------------------------------------------------------------------------
56
56
 
57
57
  const DEFAULT_MAX_DELEGATION_DEPTH = 3;
58
- const DEFAULT_PREVENT_CYCLE_DELEGATION = false;
58
+ const DEFAULT_PREVENT_CYCLE_DELEGATION = true;
59
59
  const DEFAULT_PROJECT_AGENT_CONFIRMATION = "ask";
60
60
  const SUBAGENT_DEPTH_ENV = "PI_SUBAGENT_DEPTH";
61
61
  const SUBAGENT_MAX_DEPTH_ENV = "PI_SUBAGENT_MAX_DEPTH";
@@ -355,6 +355,15 @@ function ensureSubagentToolActive(pi: ExtensionAPI): void {
355
355
  }
356
356
  }
357
357
 
358
+ function isRpcMode(argv: string[]): boolean {
359
+ for (let i = 2; i < argv.length; i++) {
360
+ const arg = argv[i];
361
+ if (arg === "--mode" && argv[i + 1] === "rpc") return true;
362
+ if (arg === "--mode=rpc") return true;
363
+ }
364
+ return false;
365
+ }
366
+
358
367
  function hasCliInitialPrompt(argv: string[]): boolean {
359
368
  for (let i = 2; i < argv.length; i++) {
360
369
  const arg = argv[i];
@@ -381,14 +390,14 @@ const SUBAGENT_FALLBACK_MODEL_ENV = "PI_SUBAGENT_FALLBACK_MODEL";
381
390
  const RESUME_INTERACTIVE_DELAY_MS = 50;
382
391
 
383
392
  type SyntheticResumeState = {
384
- plan: ResumableSubagentCall | null;
393
+ plans: ResumableSubagentCall[];
385
394
  phase: "tool" | "final";
386
395
  trigger: "resumePrompt" | "nextRequest";
387
396
  };
388
397
 
389
398
  function clearSyntheticResumeState(): void {
390
399
  const state = getSyntheticResumeState();
391
- state.plan = null;
400
+ state.plans = [];
392
401
  state.phase = "tool";
393
402
  state.trigger = "resumePrompt";
394
403
  }
@@ -396,7 +405,7 @@ function clearSyntheticResumeState(): void {
396
405
  function getSyntheticResumeState(): SyntheticResumeState {
397
406
  const g = globalThis as any;
398
407
  if (!g[RESUME_STATE_KEY]) {
399
- g[RESUME_STATE_KEY] = { plan: null, phase: "tool", trigger: "resumePrompt" } satisfies SyntheticResumeState;
408
+ g[RESUME_STATE_KEY] = { plans: [], phase: "tool", trigger: "resumePrompt" } satisfies SyntheticResumeState;
400
409
  }
401
410
  return g[RESUME_STATE_KEY] as SyntheticResumeState;
402
411
  }
@@ -518,27 +527,34 @@ export default function (pi: ExtensionAPI) {
518
527
  streamSimple: async (model, context, options) => {
519
528
  const stream = createAssistantMessageEventStream();
520
529
  const state = getSyntheticResumeState();
521
- const discoveredPlan = state.plan ?? pendingResumePlan ?? (latestSessionCtx ? findLatestResumableSubagentCall(latestSessionCtx) : null);
522
- if (discoveredPlan && !state.plan) {
523
- state.plan = discoveredPlan;
524
- pendingResumePlan = discoveredPlan;
530
+ const discoveredPlans = state.plans.length > 0
531
+ ? state.plans
532
+ : pendingResumePlans.length > 0
533
+ ? pendingResumePlans
534
+ : latestSessionCtx
535
+ ? findLatestResumableSubagentCalls(latestSessionCtx)
536
+ : [];
537
+ if (discoveredPlans.length > 0 && state.plans.length === 0) {
538
+ state.plans = [...discoveredPlans];
539
+ pendingResumePlans = [...discoveredPlans];
525
540
  }
526
- const plan = discoveredPlan;
541
+ const plans = discoveredPlans;
542
+ const totalTaskCount = plans.reduce((sum, plan) => sum + plan.tasks.length, 0);
527
543
  const phase = state.phase;
528
544
  const triggerMatches =
529
545
  state.trigger === "nextRequest" ||
530
- (plan ? isSyntheticResumePrompt(context, plan.tasks.length) : false);
531
- if (plan && phase === "tool" && triggerMatches) {
546
+ (totalTaskCount > 0 ? isSyntheticResumePrompt(context, totalTaskCount) : false);
547
+ if (plans.length > 0 && phase === "tool" && triggerMatches) {
532
548
  state.phase = "final";
533
- const toolCall = {
549
+ const toolCalls = plans.map((plan, index) => ({
534
550
  type: "toolCall" as const,
535
- id: `resume_subagent_${Date.now()}`,
551
+ id: `resume_subagent_${Date.now()}_${index}`,
536
552
  name: "subagent",
537
553
  arguments: { tasks: plan.tasks },
538
- };
554
+ }));
539
555
  const message = {
540
556
  role: "assistant" as const,
541
- content: [toolCall],
557
+ content: toolCalls,
542
558
  api: model.api,
543
559
  provider: model.provider,
544
560
  model: model.id,
@@ -548,13 +564,15 @@ export default function (pi: ExtensionAPI) {
548
564
  };
549
565
  queueMicrotask(() => {
550
566
  stream.push({ type: "start", partial: message });
551
- stream.push({ type: "toolcall_start", contentIndex: 0, partial: message });
552
- stream.push({ type: "toolcall_end", contentIndex: 0, toolCall, partial: message });
567
+ toolCalls.forEach((toolCall, contentIndex) => {
568
+ stream.push({ type: "toolcall_start", contentIndex, partial: message });
569
+ stream.push({ type: "toolcall_end", contentIndex, toolCall, partial: message });
570
+ });
553
571
  stream.push({ type: "done", reason: "toolUse", message });
554
572
  stream.end(message);
555
573
 
556
574
  // The synthetic provider's only job is to inject the resumed subagent
557
- // tool call. Restore the real model immediately after that handoff so
575
+ // tool calls. Restore the real model immediately after that handoff so
558
576
  // the TUI does not appear stuck on `pi-subagent-resume` while the
559
577
  // subagent tool execution is still running.
560
578
  void restoreVisibleModelForResume();
@@ -572,8 +590,8 @@ export default function (pi: ExtensionAPI) {
572
590
  const fallback = await streamWithRealModelFallback(context, options, restore ?? lastRestorableModel);
573
591
  if (fallback) return fallback;
574
592
 
575
- if (!(plan && phase === "tool")) {
576
- state.plan = null;
593
+ if (!(plans.length > 0 && phase === "tool")) {
594
+ state.plans = [];
577
595
  state.phase = "tool";
578
596
  }
579
597
  const message = {
@@ -622,7 +640,7 @@ export default function (pi: ExtensionAPI) {
622
640
  let discoveredAgents: AgentConfig[] = [];
623
641
  let currentSessionId = "ephemeral";
624
642
  let currentSubagentSessionRoot = "";
625
- let pendingResumePlan: ResumableSubagentCall | null = null;
643
+ let pendingResumePlans: ResumableSubagentCall[] = [];
626
644
  let modelToRestoreAfterResume: any | undefined;
627
645
  const approvedProjectAgentDirsForSession = new Set<string>();
628
646
  const activeSubagents = new Map<number, { agent: string; task: string; handle: RunningSubagentHandle }>();
@@ -632,6 +650,73 @@ export default function (pi: ExtensionAPI) {
632
650
  };
633
651
  let nextActiveSubagentId = 1;
634
652
 
653
+ /**
654
+ * Build a lightweight version of SubagentDetails for live progress bubbling.
655
+ *
656
+ * Full message histories can be very large in long agent trees. For live TUI
657
+ * rendering we only need subagent tool-call structure, nested subagent
658
+ * results, live logs, metadata, and usage counters. Text conversations are
659
+ * intentionally omitted; final durable results still arrive via normal
660
+ * tool_result_end messages.
661
+ */
662
+ function slimDetailsForProgress(details: SubagentDetails): SubagentDetails {
663
+ const slimResult = (result: SingleResult): SingleResult => {
664
+ const slimMessages = result.messages
665
+ .map((message: any) => {
666
+ if (message?.role === "assistant" && Array.isArray(message.content)) {
667
+ const subagentCalls = message.content.filter(
668
+ (part: any) => part?.type === "toolCall" && part?.name === "subagent",
669
+ );
670
+ return subagentCalls.length > 0
671
+ ? { ...message, content: subagentCalls }
672
+ : null;
673
+ }
674
+ if (message?.role === "toolResult" && message.toolName === "subagent") {
675
+ return isSubagentDetails(message.details)
676
+ ? { ...message, details: slimDetailsForProgress(message.details) }
677
+ : message;
678
+ }
679
+ return null;
680
+ })
681
+ .filter(Boolean) as SingleResult["messages"];
682
+
683
+ const liveNestedSubagents = result.liveNestedSubagents
684
+ ? Object.fromEntries(
685
+ Object.entries(result.liveNestedSubagents).map(([nestedToolCallId, nested]) => [
686
+ nestedToolCallId,
687
+ slimDetailsForProgress(nested),
688
+ ]),
689
+ )
690
+ : undefined;
691
+
692
+ return {
693
+ ...result,
694
+ messages: slimMessages,
695
+ stderr: result.stderr ? result.stderr.slice(-1000) : "",
696
+ liveLog: [...(result.liveLog ?? [])],
697
+ liveNestedSubagents,
698
+ };
699
+ };
700
+
701
+ return {
702
+ ...details,
703
+ results: details.results.map(slimResult),
704
+ };
705
+ }
706
+
707
+ function emitNestedProgressToParent(toolCallId: string, details: SubagentDetails): void {
708
+ if (currentDepth <= 0) return;
709
+ try {
710
+ process.stdout.write(`${JSON.stringify({
711
+ type: "subagent_progress",
712
+ toolCallId,
713
+ details: slimDetailsForProgress(details),
714
+ })}\n`);
715
+ } catch {
716
+ // Best-effort only. Normal final tool_result_end still carries the durable result.
717
+ }
718
+ }
719
+
635
720
  const BROADCAST_STEER_PREFIX = "__PI_SUBAGENT_BROADCAST_STEER__";
636
721
 
637
722
  interface BroadcastTarget {
@@ -784,7 +869,7 @@ export default function (pi: ExtensionAPI) {
784
869
  return true;
785
870
  }
786
871
 
787
- function updateLatestBroadcastTargets(details: SubagentDetails | undefined): void {
872
+ function updateLatestBroadcastTargets(details: SubagentDetails | undefined, topLevelBaseId = 1): void {
788
873
  latestBroadcastTargets.all = [];
789
874
  latestBroadcastTargets.youngest = [];
790
875
  if (!details) {
@@ -796,7 +881,7 @@ export default function (pi: ExtensionAPI) {
796
881
  return;
797
882
  }
798
883
  details.results.forEach((result, index) => {
799
- collectRunningBroadcastTargetsFromResult(result, [index + 1], latestBroadcastTargets);
884
+ collectRunningBroadcastTargetsFromResult(result, [topLevelBaseId + index], latestBroadcastTargets);
800
885
  });
801
886
  const dedupe = (targets: BroadcastTarget[]) =>
802
887
  Array.from(new Map(targets.map((target) => [target.display, target])).values())
@@ -909,7 +994,7 @@ export default function (pi: ExtensionAPI) {
909
994
  async function restoreModelAfterResumeFailure(ctx?: { ui?: { notify?: (message: string, type?: "info" | "warning" | "error") => void } }) {
910
995
  const restore = modelToRestoreAfterResume;
911
996
  modelToRestoreAfterResume = undefined;
912
- pendingResumePlan = null;
997
+ pendingResumePlans = [];
913
998
  clearSyntheticResumeState();
914
999
  if (!restore) return;
915
1000
  try {
@@ -957,15 +1042,17 @@ export default function (pi: ExtensionAPI) {
957
1042
  const resumeDisabled = parseBooleanEnv(process.env[SUBAGENT_RESUME_DISABLE_ENV]) === true;
958
1043
  if (resumeDisabled || (event.reason !== "resume" && event.reason !== "startup")) return;
959
1044
 
960
- const plan = findLatestResumableSubagentCall(ctx);
961
- if (!plan) return;
1045
+ const plans = findLatestResumableSubagentCalls(ctx);
1046
+ if (plans.length === 0) return;
1047
+ const totalTaskCount = plans.reduce((sum, plan) => sum + plan.tasks.length, 0);
962
1048
 
963
1049
  let shouldResume = true;
964
1050
  const shouldPrompt = parseBooleanEnv(process.env[SUBAGENT_RESUME_PROMPT_ENV]) !== false;
965
- if (ctx.hasUI && shouldPrompt) {
1051
+ const rpcMode = isRpcMode(process.argv);
1052
+ if (ctx.hasUI && !rpcMode && shouldPrompt) {
966
1053
  shouldResume = await ctx.ui.confirm(
967
1054
  "Resume subagents?",
968
- `The resumed session has an unfinished subagent call (${plan.tasks.length} task${plan.tasks.length === 1 ? "" : "s"}). Resume it from saved subagent sessions?`,
1055
+ `The resumed session has ${plans.length === 1 ? "an" : String(plans.length)} unfinished subagent call${plans.length === 1 ? "" : "s"} (${totalTaskCount} task${totalTaskCount === 1 ? "" : "s"}). Resume from saved subagent sessions?`,
969
1056
  );
970
1057
  }
971
1058
  if (!shouldResume) {
@@ -990,11 +1077,17 @@ export default function (pi: ExtensionAPI) {
990
1077
  return;
991
1078
  }
992
1079
 
993
- pendingResumePlan = plan;
1080
+ pendingResumePlans = [...plans];
994
1081
  const resumeState = getSyntheticResumeState();
995
- resumeState.plan = plan;
1082
+ resumeState.plans = [...plans];
996
1083
  resumeState.phase = "tool";
997
- resumeState.trigger = hasCliInitialPrompt(process.argv) ? "nextRequest" : "resumePrompt";
1084
+ // Headless subprocess/RPC subagents cannot answer a visible resume
1085
+ // prompt. They already receive an initial RPC prompt from the parent
1086
+ // runner, so inject the synthetic resume tool call into that next model
1087
+ // request. Interactive top-level sessions keep using a visible prompt so
1088
+ // the user sees exactly what is happening.
1089
+ const injectOnNextRequest = rpcMode || hasCliInitialPrompt(process.argv) || !ctx.hasUI;
1090
+ resumeState.trigger = injectOnNextRequest ? "nextRequest" : "resumePrompt";
998
1091
  modelToRestoreAfterResume = restorableModel ?? ctx.model;
999
1092
  resumeModelRegistry = ctx.modelRegistry;
1000
1093
  ensureSubagentToolActive(pi);
@@ -1009,15 +1102,15 @@ export default function (pi: ExtensionAPI) {
1009
1102
  // to be sent. That prompt will be answered by the synthetic provider with
1010
1103
  // a real assistant subagent tool call. In interactive mode, submit a short
1011
1104
  // visible prompt that triggers the same synthetic provider path.
1012
- if (hasCliInitialPrompt(process.argv)) {
1013
- if (ctx.hasUI) ctx.ui.notify(`Resuming ${plan.tasks.length} subagents...`, "info");
1105
+ if (injectOnNextRequest) {
1106
+ if (ctx.hasUI) ctx.ui.notify(`Resuming ${totalTaskCount} subagents...`, "info");
1014
1107
  } else {
1015
1108
  // Do not start the synthetic resume turn from session_start. Pi renders
1016
1109
  // the resumed chat only after session_start/resources_discover complete;
1017
1110
  // starting now lets that render wipe out the live tool component, so no
1018
1111
  // real-time updates appear. Queue it for resources_discover instead,
1019
1112
  // which is the last extension hook before the initial chat render.
1020
- pendingInteractiveResumePrompt = `Resuming ${plan.tasks.length} subagents...`;
1113
+ pendingInteractiveResumePrompt = `Resuming ${totalTaskCount} subagents...`;
1021
1114
  }
1022
1115
  } catch (err) {
1023
1116
  console.error("[pi-subagent] Error in session_start:", err);
@@ -1131,9 +1224,7 @@ calls one after another. Do NOT put dependent tasks in the same array.
1131
1224
 
1132
1225
  async execute(toolCallId, params, signal, onUpdate, ctx) {
1133
1226
  try {
1134
- activeSubagents.clear();
1135
1227
  updateLatestBroadcastTargets(undefined);
1136
- nextActiveSubagentId = 1;
1137
1228
  const discovery = discoverAgents(ctx.cwd, "both");
1138
1229
  const { agents } = discovery;
1139
1230
 
@@ -1156,8 +1247,13 @@ calls one after another. Do NOT put dependent tasks in the same array.
1156
1247
  }
1157
1248
 
1158
1249
  const executionMode = tasks.length === 1 ? "single" : "parallel";
1250
+ const topLevelBaseId = nextActiveSubagentId;
1251
+ nextActiveSubagentId += tasks.length;
1159
1252
  const trackedOnUpdate = (partial: any) => {
1160
- if (isSubagentDetails(partial?.details)) updateLatestBroadcastTargets(partial.details);
1253
+ if (isSubagentDetails(partial?.details)) {
1254
+ updateLatestBroadcastTargets(partial.details, topLevelBaseId);
1255
+ emitNestedProgressToParent(toolCallId, partial.details);
1256
+ }
1161
1257
  onUpdate?.(partial);
1162
1258
  };
1163
1259
 
@@ -1205,7 +1301,7 @@ This guard prevents self-recursion and cyclic handoffs (for example A -> B -> A)
1205
1301
  projectAgentConfirmationSetting === "ask" &&
1206
1302
  !approvedProjectAgentDirsForSession.has(projectAgentSessionKey);
1207
1303
  if (shouldConfirmProjectAgents) {
1208
- if (ctx.hasUI) {
1304
+ if (ctx.hasUI && !isRpcMode(process.argv)) {
1209
1305
  const approval = await confirmProjectAgentsIfNeeded(
1210
1306
  requestedProjectAgents,
1211
1307
  discovery.projectAgentsDir,
@@ -1241,12 +1337,10 @@ This guard prevents self-recursion and cyclic handoffs (for example A -> B -> A)
1241
1337
  }
1242
1338
  }
1243
1339
 
1244
- const resumePlan =
1245
- pendingResumePlan && sameTasks(pendingResumePlan.tasks, tasks)
1246
- ? pendingResumePlan
1247
- : null;
1248
- if (resumePlan) {
1249
- pendingResumePlan = null;
1340
+ const resumePlanIndex = pendingResumePlans.findIndex((plan) => sameTasks(plan.tasks, tasks));
1341
+ const resumePlan = resumePlanIndex >= 0 ? pendingResumePlans[resumePlanIndex] : null;
1342
+ if (resumePlanIndex >= 0) {
1343
+ pendingResumePlans.splice(resumePlanIndex, 1);
1250
1344
  }
1251
1345
 
1252
1346
  if (tasks.length === 1) {
@@ -1267,6 +1361,7 @@ This guard prevents self-recursion and cyclic handoffs (for example A -> B -> A)
1267
1361
  // defaulting to whatever settings.json says at spawn time, which
1268
1362
  // can change while the parent session is long-running.
1269
1363
  formatModelFlag(modelToRestoreAfterResume ?? ctx.model ?? lastRestorableModel),
1364
+ topLevelBaseId,
1270
1365
  );
1271
1366
  }
1272
1367
 
@@ -1281,6 +1376,7 @@ This guard prevents self-recursion and cyclic handoffs (for example A -> B -> A)
1281
1376
  (index) => getSessionDirForTask(resumePlan?.previousToolCallId ?? toolCallId, index),
1282
1377
  !!resumePlan,
1283
1378
  formatModelFlag(modelToRestoreAfterResume ?? ctx.model ?? lastRestorableModel),
1379
+ topLevelBaseId,
1284
1380
  );
1285
1381
  } catch (err) {
1286
1382
  const msg = err instanceof Error ? err.message : String(err);
@@ -1322,7 +1418,8 @@ This guard prevents self-recursion and cyclic handoffs (for example A -> B -> A)
1322
1418
  previousResult: SingleResult | undefined,
1323
1419
  sessionDir: string,
1324
1420
  resumeExistingSession: boolean,
1325
- fallbackModel?: string,
1421
+ fallbackModel: string | undefined,
1422
+ topLevelBaseId: number,
1326
1423
  ) {
1327
1424
  if (previousResult && isFinishedResult(previousResult)) {
1328
1425
  return {
@@ -1355,7 +1452,7 @@ This guard prevents self-recursion and cyclic handoffs (for example A -> B -> A)
1355
1452
  initialResult: previousResult,
1356
1453
  fallbackModel,
1357
1454
  onHandle: (handle) => {
1358
- activeId = 1;
1455
+ activeId = topLevelBaseId;
1359
1456
  activeSubagents.set(activeId, { agent: agentName, task, handle });
1360
1457
  updateLatestBroadcastTargets(undefined);
1361
1458
  },
@@ -1403,7 +1500,8 @@ This guard prevents self-recursion and cyclic handoffs (for example A -> B -> A)
1403
1500
  resumeResults: SingleResult[] | undefined,
1404
1501
  getSessionDir: (index: number) => string,
1405
1502
  resumeExistingSessions: boolean,
1406
- fallbackModel?: string,
1503
+ fallbackModel: string | undefined,
1504
+ topLevelBaseId: number,
1407
1505
  ) {
1408
1506
  const taskIds = new Map<number, number>();
1409
1507
  try {
@@ -1424,7 +1522,7 @@ This guard prevents self-recursion and cyclic handoffs (for example A -> B -> A)
1424
1522
  currentSubagentSessionRoot,
1425
1523
  fallbackModel,
1426
1524
  (index, task, handle) => {
1427
- const id = index + 1;
1525
+ const id = topLevelBaseId + index;
1428
1526
  taskIds.set(index, id);
1429
1527
  activeSubagents.set(id, { agent: task.agent, task: task.task, handle });
1430
1528
  updateLatestBroadcastTargets(undefined);
package/package.json CHANGED
@@ -1,74 +1,76 @@
1
- {
2
- "name": "oira666_pi-subagent",
3
- "version": "0.2.22",
4
- "description": "Subagent extension for Pi coding agent. Delegate tasks to specialized agents.",
5
- "type": "module",
6
- "main": "index.ts",
7
- "files": [
8
- "index.ts",
9
- "agents.ts",
10
- "runner.ts",
11
- "resume.ts",
12
- "shared.ts",
13
- "render.ts",
14
- "types.ts",
15
- "agents/*.md",
16
- "README.md",
17
- "LICENSE"
18
- ],
19
- "pi": {
20
- "extensions": [
21
- "./index.ts"
22
- ]
23
- },
24
- "keywords": [
25
- "pi",
26
- "subagent",
27
- "delegation",
28
- "pi-package"
29
- ],
30
- "repository": {
31
- "type": "git",
32
- "url": "git+https://github.com/gee666/pi-subagent.git"
33
- },
34
- "bugs": {
35
- "url": "https://github.com/gee666/pi-subagent/issues"
36
- },
37
- "homepage": "https://github.com/gee666/pi-subagent#readme",
38
- "publishConfig": {
39
- "access": "public"
40
- },
41
- "license": "MIT",
42
- "scripts": {
43
- "test": "node --import tsx/esm --test tests/*.test.ts test/*.test.ts"
44
- },
45
- "devDependencies": {
46
- "@types/node": "^25.2.3",
47
- "tsx": "^4.21.0",
48
- "typescript": "^5.9.3"
49
- },
50
- "peerDependencies": {
51
- "@mariozechner/pi-agent-core": ">=0.37.0",
52
- "@mariozechner/pi-ai": ">=0.37.0",
53
- "@mariozechner/pi-coding-agent": ">=0.37.0",
54
- "@mariozechner/pi-tui": ">=0.37.0",
55
- "@sinclair/typebox": ">=0.34.0"
56
- },
57
- "peerDependenciesMeta": {
58
- "@mariozechner/pi-agent-core": {
59
- "optional": true
60
- },
61
- "@mariozechner/pi-coding-agent": {
62
- "optional": true
63
- },
64
- "@mariozechner/pi-tui": {
65
- "optional": true
66
- },
67
- "@mariozechner/pi-ai": {
68
- "optional": true
69
- },
70
- "@sinclair/typebox": {
71
- "optional": true
72
- }
73
- }
74
- }
1
+ {
2
+ "name": "oira666_pi-subagent",
3
+ "version": "0.2.25",
4
+ "description": "Subagent extension for Pi coding agent. Delegate tasks to specialized agents.",
5
+ "type": "module",
6
+ "main": "index.ts",
7
+ "files": [
8
+ "index.ts",
9
+ "agents.ts",
10
+ "runner.ts",
11
+ "resume.ts",
12
+ "shared.ts",
13
+ "render.ts",
14
+ "tree.ts",
15
+ "types.ts",
16
+ "shims.d.ts",
17
+ "agents/*.md",
18
+ "README.md",
19
+ "LICENSE"
20
+ ],
21
+ "pi": {
22
+ "extensions": [
23
+ "./index.ts"
24
+ ]
25
+ },
26
+ "keywords": [
27
+ "pi",
28
+ "subagent",
29
+ "delegation",
30
+ "pi-package"
31
+ ],
32
+ "repository": {
33
+ "type": "git",
34
+ "url": "git+https://github.com/gee666/pi-subagent.git"
35
+ },
36
+ "bugs": {
37
+ "url": "https://github.com/gee666/pi-subagent/issues"
38
+ },
39
+ "homepage": "https://github.com/gee666/pi-subagent#readme",
40
+ "publishConfig": {
41
+ "access": "public"
42
+ },
43
+ "license": "MIT",
44
+ "scripts": {
45
+ "test": "node --import tsx/esm --test tests/*.test.ts test/*.test.ts",
46
+ "typecheck": "tsc --noEmit"
47
+ },
48
+ "devDependencies": {
49
+ "@types/node": "^25.2.3",
50
+ "tsx": "^4.21.0",
51
+ "typescript": "^5.9.3"
52
+ },
53
+ "peerDependencies": {
54
+ "@mariozechner/pi-agent-core": ">=0.37.0",
55
+ "@mariozechner/pi-ai": ">=0.37.0",
56
+ "@mariozechner/pi-coding-agent": ">=0.37.0",
57
+ "@mariozechner/pi-tui": ">=0.37.0"
58
+ },
59
+ "peerDependenciesMeta": {
60
+ "@mariozechner/pi-agent-core": {
61
+ "optional": true
62
+ },
63
+ "@mariozechner/pi-coding-agent": {
64
+ "optional": true
65
+ },
66
+ "@mariozechner/pi-tui": {
67
+ "optional": true
68
+ },
69
+ "@mariozechner/pi-ai": {
70
+ "optional": true
71
+ }
72
+ },
73
+ "dependencies": {
74
+ "@sinclair/typebox": ">=0.34.0"
75
+ }
76
+ }