oira666_pi-subagent 0.2.19 → 0.2.21

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/index.ts CHANGED
@@ -15,8 +15,8 @@ import {
15
15
  } from "@mariozechner/pi-ai";
16
16
  import { Type } from "@sinclair/typebox";
17
17
  import { type AgentConfig, discoverAgents } from "./agents.js";
18
- import { renderCall, renderResult } from "./render.js";
19
- import { runAgentSubprocess, executeParallelSubprocess } from "./runner.js";
18
+ import { renderCall, renderResult, setBroadcastNumberingActive } from "./render.js";
19
+ import { runAgentSubprocess, executeParallelSubprocess, type RunningSubagentHandle } from "./runner.js";
20
20
  import {
21
21
  SUBAGENT_RESUME_DISABLE_ENV,
22
22
  SUBAGENT_RESUME_PROMPT_ENV,
@@ -45,7 +45,9 @@ import {
45
45
  DEFAULT_DELEGATION_MODE,
46
46
  buildSubagentDetails,
47
47
  getFinalOutput,
48
+ getNestedSubagentResults,
48
49
  isResultError,
50
+ isSubagentDetails,
49
51
  } from "./types.js";
50
52
 
51
53
  // ---------------------------------------------------------------------------
@@ -76,10 +78,7 @@ const TaskItem = Type.Object({
76
78
  description:
77
79
  "Task description for this delegated run. Include all required context; the subagent receives only this prompt.",
78
80
  }),
79
- cwd: Type.Optional(
80
- Type.String({ description: "Working directory for this agent's process" }),
81
- ),
82
- });
81
+ }, { additionalProperties: false });
83
82
 
84
83
  const SubagentParams = Type.Object({
85
84
  tasks: Type.Array(TaskItem, {
@@ -87,7 +86,7 @@ const SubagentParams = Type.Object({
87
86
  description:
88
87
  "Array of {agent, task} objects. One task behaves like a single-agent delegation; multiple tasks run concurrently.",
89
88
  }),
90
- });
89
+ }, { additionalProperties: false });
91
90
 
92
91
  // ---------------------------------------------------------------------------
93
92
  // Helpers
@@ -613,6 +612,286 @@ export default function (pi: ExtensionAPI) {
613
612
  let pendingResumePlan: ResumableSubagentCall | null = null;
614
613
  let modelToRestoreAfterResume: any | undefined;
615
614
  const approvedProjectAgentDirsForSession = new Set<string>();
615
+ const activeSubagents = new Map<number, { agent: string; task: string; handle: RunningSubagentHandle }>();
616
+ const latestBroadcastTargets = {
617
+ all: [] as BroadcastTarget[],
618
+ youngest: [] as BroadcastTarget[],
619
+ };
620
+ let nextActiveSubagentId = 1;
621
+
622
+ const BROADCAST_STEER_PREFIX = "__PI_SUBAGENT_BROADCAST_STEER__";
623
+
624
+ interface BroadcastTarget {
625
+ display: string;
626
+ topLevelId: number;
627
+ restPath: number[];
628
+ }
629
+
630
+ interface ParsedBroadcastSelection {
631
+ targets: BroadcastTarget[];
632
+ errors: string[];
633
+ }
634
+
635
+ function parseBroadcastPath(raw: string): number[] | null {
636
+ const parts = raw.trim().split(".");
637
+ if (parts.length === 0) return null;
638
+ const path: number[] = [];
639
+ for (const part of parts) {
640
+ if (!/^\d+$/.test(part)) return null;
641
+ const value = Number(part);
642
+ if (!Number.isSafeInteger(value) || value < 1) return null;
643
+ path.push(value);
644
+ }
645
+ return path;
646
+ }
647
+
648
+ function parseBroadcastSelection(input: string, available: number[]): ParsedBroadcastSelection {
649
+ const normalized = input.trim().toUpperCase();
650
+ if (normalized === "ALL") {
651
+ return {
652
+ targets: available.map((id) => ({ display: String(id), topLevelId: id, restPath: [] })),
653
+ errors: [],
654
+ };
655
+ }
656
+
657
+ const errors: string[] = [];
658
+ const targetMap = new Map<string, BroadcastTarget>();
659
+ for (const rawPart of input.split(",")) {
660
+ const part = rawPart.trim();
661
+ if (!part) continue;
662
+
663
+ if (part.includes("-")) {
664
+ const [startRaw, endRaw, extra] = part.split("-").map((p) => p.trim());
665
+ const startPath = parseBroadcastPath(startRaw);
666
+ const endPath = parseBroadcastPath(endRaw);
667
+ if (extra !== undefined || !startPath || !endPath || startPath.length !== 1 || endPath.length !== 1) {
668
+ errors.push(`Invalid range "${part}". Use top-level ranges like 1-3.`);
669
+ continue;
670
+ }
671
+ const start = startPath[0];
672
+ const end = endPath[0];
673
+ for (let id = Math.min(start, end); id <= Math.max(start, end); id++) {
674
+ if (!available.includes(id)) {
675
+ errors.push(`Subagent ${id} is not running.`);
676
+ continue;
677
+ }
678
+ targetMap.set(String(id), { display: String(id), topLevelId: id, restPath: [] });
679
+ }
680
+ continue;
681
+ }
682
+
683
+ const path = parseBroadcastPath(part);
684
+ if (!path) {
685
+ errors.push(`Invalid target "${part}". Use ALL, numbers, paths, or top-level ranges (e.g. 1, 2, 4.1, 1-3).`);
686
+ continue;
687
+ }
688
+ const [topLevelId, ...restPath] = path;
689
+ if (!available.includes(topLevelId)) {
690
+ errors.push(`Subagent ${topLevelId} is not running.`);
691
+ continue;
692
+ }
693
+ const display = path.join(".");
694
+ targetMap.set(display, { display, topLevelId, restPath });
695
+ }
696
+
697
+ return {
698
+ targets: Array.from(targetMap.values()).sort((a, b) => a.display.localeCompare(b.display, undefined, { numeric: true })),
699
+ errors,
700
+ };
701
+ }
702
+
703
+ function encodeNestedBroadcast(message: string, path: number[]): string {
704
+ return `${BROADCAST_STEER_PREFIX}${JSON.stringify({ path, message })}`;
705
+ }
706
+
707
+ function decodeNestedBroadcast(text: string): { path: number[]; message: string } | null {
708
+ if (!text.startsWith(BROADCAST_STEER_PREFIX)) return null;
709
+ try {
710
+ const parsed = JSON.parse(text.slice(BROADCAST_STEER_PREFIX.length));
711
+ if (!Array.isArray(parsed?.path) || !parsed.path.every((n: unknown) => Number.isSafeInteger(n) && (n as number) >= 1)) return null;
712
+ if (typeof parsed?.message !== "string") return null;
713
+ return { path: parsed.path, message: parsed.message };
714
+ } catch {
715
+ return null;
716
+ }
717
+ }
718
+
719
+ function extractPendingSubagentTaskCounts(result: SingleResult): number[] {
720
+ const completedToolCallIds = new Set(getNestedSubagentResults(result.messages).map((nested) => nested.toolCallId));
721
+ const counts: number[] = [];
722
+ for (const message of result.messages as any[]) {
723
+ if (message?.role !== "assistant" || !Array.isArray(message.content)) continue;
724
+ for (const part of message.content) {
725
+ if (part?.type !== "toolCall" || part?.name !== "subagent") continue;
726
+ const toolCallId = typeof part.toolCallId === "string" ? part.toolCallId : typeof part.id === "string" ? part.id : undefined;
727
+ if (toolCallId && completedToolCallIds.has(toolCallId)) continue;
728
+ const tasks = Array.isArray(part.arguments?.tasks) ? part.arguments.tasks : [];
729
+ if (tasks.length > 0) counts.push(tasks.length);
730
+ }
731
+ }
732
+ return counts;
733
+ }
734
+
735
+ function collectRunningBroadcastTargetsFromResult(
736
+ result: SingleResult,
737
+ path: number[],
738
+ targets: { all: BroadcastTarget[]; youngest: BroadcastTarget[] },
739
+ ): boolean {
740
+ const nestedRunningPaths: number[][] = [];
741
+
742
+ for (const nested of getNestedSubagentResults(result.messages)) {
743
+ if (!isSubagentDetails(nested.details)) continue;
744
+ nested.details.results.forEach((child, index) => {
745
+ const childPath = [...path, index + 1];
746
+ if (collectRunningBroadcastTargetsFromResult(child, childPath, targets)) {
747
+ nestedRunningPaths.push(childPath);
748
+ }
749
+ });
750
+ }
751
+
752
+ if (result.exitCode === -1) {
753
+ for (const taskCount of extractPendingSubagentTaskCounts(result)) {
754
+ for (let i = 1; i <= taskCount; i++) {
755
+ const childPath = [...path, i];
756
+ const [topLevelId, ...restPath] = childPath;
757
+ targets.all.push({ display: childPath.join("."), topLevelId, restPath });
758
+ targets.youngest.push({ display: childPath.join("."), topLevelId, restPath });
759
+ nestedRunningPaths.push(childPath);
760
+ }
761
+ }
762
+ }
763
+
764
+ const isRunning = result.exitCode === -1;
765
+ if (!isRunning) return nestedRunningPaths.length > 0;
766
+
767
+ const [topLevelId, ...restPath] = path;
768
+ const self = { display: path.join("."), topLevelId, restPath };
769
+ targets.all.push(self);
770
+ if (nestedRunningPaths.length === 0) targets.youngest.push(self);
771
+ return true;
772
+ }
773
+
774
+ function updateLatestBroadcastTargets(details: SubagentDetails | undefined): void {
775
+ latestBroadcastTargets.all = [];
776
+ latestBroadcastTargets.youngest = [];
777
+ if (!details) {
778
+ for (const id of activeSubagents.keys()) {
779
+ const target = { display: String(id), topLevelId: id, restPath: [] };
780
+ latestBroadcastTargets.all.push(target);
781
+ latestBroadcastTargets.youngest.push(target);
782
+ }
783
+ return;
784
+ }
785
+ details.results.forEach((result, index) => {
786
+ collectRunningBroadcastTargetsFromResult(result, [index + 1], latestBroadcastTargets);
787
+ });
788
+ const dedupe = (targets: BroadcastTarget[]) =>
789
+ Array.from(new Map(targets.map((target) => [target.display, target])).values())
790
+ .filter((target) => activeSubagents.has(target.topLevelId))
791
+ .sort((a, b) => a.display.localeCompare(b.display, undefined, { numeric: true }));
792
+ latestBroadcastTargets.all = dedupe(latestBroadcastTargets.all);
793
+ latestBroadcastTargets.youngest = dedupe(latestBroadcastTargets.youngest);
794
+ }
795
+
796
+ function getFallbackTopLevelTargets(): BroadcastTarget[] {
797
+ return Array.from(activeSubagents.keys())
798
+ .sort((a, b) => a - b)
799
+ .map((id) => ({ display: String(id), topLevelId: id, restPath: [] }));
800
+ }
801
+
802
+ function sendBroadcastToTargets(message: string, targets: BroadcastTarget[], ctx: any): void {
803
+ const delivered: string[] = [];
804
+ const missed: string[] = [];
805
+ for (const target of targets) {
806
+ const item = activeSubagents.get(target.topLevelId);
807
+ if (!item) {
808
+ missed.push(target.display);
809
+ continue;
810
+ }
811
+ item.handle.steer(
812
+ target.restPath.length > 0
813
+ ? encodeNestedBroadcast(message, target.restPath)
814
+ : message,
815
+ );
816
+ delivered.push(target.display);
817
+ }
818
+ if (delivered.length > 0) {
819
+ ctx.ui.notify(`Broadcasted steering message to subagent(s): ${delivered.join(", ")}`, "info");
820
+ }
821
+ if (missed.length > 0) {
822
+ ctx.ui.notify(`Some selected subagents are no longer running: ${missed.join(", ")}`, "warning");
823
+ }
824
+ }
825
+
826
+ async function askBroadcastForSteering(message: string, ctx: any): Promise<"continue" | "handled"> {
827
+ const nested = decodeNestedBroadcast(message);
828
+ if (nested) {
829
+ const available = Array.from(activeSubagents.keys()).sort((a, b) => a - b);
830
+ if (available.length === 0) return "handled";
831
+ const [nextId, ...restPath] = nested.path;
832
+ if (!available.includes(nextId)) return "handled";
833
+ sendBroadcastToTargets(nested.message, [{ display: nested.path.join("."), topLevelId: nextId, restPath }], ctx);
834
+ return "handled";
835
+ }
836
+
837
+ if (!ctx.hasUI || activeSubagents.size === 0) return "continue";
838
+
839
+ setBroadcastNumberingActive(true);
840
+ try {
841
+ const available = Array.from(activeSubagents.keys()).sort((a, b) => a - b);
842
+ const choice = await ctx.ui.select(
843
+ "Broadcast this steering message to subagents?",
844
+ ["No", "All (+nested)", "Youngest", "Numbers (e.g. 1, 2, 4.1)"],
845
+ );
846
+ if (choice === "All (+nested)" || choice === "Youngest") {
847
+ const fallback = getFallbackTopLevelTargets();
848
+ const selected = choice === "Youngest"
849
+ ? (latestBroadcastTargets.youngest.length > 0 ? latestBroadcastTargets.youngest : fallback)
850
+ : (latestBroadcastTargets.all.length > 0 ? latestBroadcastTargets.all : fallback);
851
+ const current = selected.filter((target) => activeSubagents.has(target.topLevelId));
852
+ if (current.length === 0) {
853
+ ctx.ui.notify("No selected subagents are still running. Continuing with normal steering.", "warning");
854
+ return "continue";
855
+ }
856
+ sendBroadcastToTargets(message, current, ctx);
857
+ return "handled";
858
+ }
859
+ if (choice !== "Numbers (e.g. 1, 2, 4.1)") return "continue";
860
+
861
+ const answer = await ctx.ui.input(
862
+ "Subagent numbers/ranges to broadcast to (e.g. 1, 2, 4.1)",
863
+ "",
864
+ );
865
+ if (!answer) return "continue";
866
+ const current = Array.from(new Set([
867
+ ...available,
868
+ ...latestBroadcastTargets.all.map((target) => target.topLevelId),
869
+ ])).sort((a, b) => a - b);
870
+ const parsed = parseBroadcastSelection(answer, current);
871
+ const knownDisplays = new Set(latestBroadcastTargets.all.map((target) => target.display));
872
+ const validatedTargets = knownDisplays.size > 0
873
+ ? parsed.targets.filter((target) => knownDisplays.has(target.display))
874
+ : parsed.targets;
875
+ const unknownNested = knownDisplays.size > 0
876
+ ? parsed.targets.filter((target) => !knownDisplays.has(target.display)).map((target) => target.display)
877
+ : [];
878
+ const errors = [
879
+ ...parsed.errors,
880
+ ...unknownNested.map((display) => `Subagent ${display} is not running.`),
881
+ ];
882
+ if (errors.length > 0) {
883
+ ctx.ui.notify(errors.slice(0, 4).join("\n"), "warning");
884
+ }
885
+ if (validatedTargets.length === 0) {
886
+ ctx.ui.notify("No valid running subagents selected. Continuing with normal steering.", "warning");
887
+ return "continue";
888
+ }
889
+ sendBroadcastToTargets(message, validatedTargets, ctx);
890
+ return "handled";
891
+ } finally {
892
+ setBroadcastNumberingActive(false);
893
+ }
894
+ }
616
895
 
617
896
  async function restoreModelAfterResumeFailure(ctx?: { ui?: { notify?: (message: string, type?: "info" | "warning" | "error") => void } }) {
618
897
  const restore = modelToRestoreAfterResume;
@@ -751,6 +1030,23 @@ export default function (pi: ExtensionAPI) {
751
1030
  }, RESUME_INTERACTIVE_DELAY_MS);
752
1031
  });
753
1032
 
1033
+ pi.on("input", async (event, ctx) => {
1034
+ try {
1035
+ // Pi emits this before it applies the built-in streaming behavior. When a
1036
+ // subagent tool is running, user input is a normal steering message to the
1037
+ // parent. Give the user a chance to route it to child RPC sessions instead.
1038
+ if (activeSubagents.size === 0) return { action: "continue" as const };
1039
+ if (ctx.isIdle()) return { action: "continue" as const };
1040
+ const result = await askBroadcastForSteering(event.text, ctx);
1041
+ return result === "handled"
1042
+ ? { action: "handled" as const }
1043
+ : { action: "continue" as const };
1044
+ } catch (err) {
1045
+ console.error("[pi-subagent] Error while handling steering broadcast:", err);
1046
+ return { action: "continue" as const };
1047
+ }
1048
+ });
1049
+
754
1050
  // Inject available agents into the system prompt
755
1051
  pi.on("before_agent_start", async (event) => {
756
1052
  try {
@@ -821,6 +1117,9 @@ calls one after another. Do NOT put dependent tasks in the same array.
821
1117
 
822
1118
  async execute(toolCallId, params, signal, onUpdate, ctx) {
823
1119
  try {
1120
+ activeSubagents.clear();
1121
+ updateLatestBroadcastTargets(undefined);
1122
+ nextActiveSubagentId = 1;
824
1123
  const discovery = discoverAgents(ctx.cwd, "both");
825
1124
  const { agents } = discovery;
826
1125
 
@@ -843,6 +1142,10 @@ calls one after another. Do NOT put dependent tasks in the same array.
843
1142
  }
844
1143
 
845
1144
  const executionMode = tasks.length === 1 ? "single" : "parallel";
1145
+ const trackedOnUpdate = (partial: any) => {
1146
+ if (isSubagentDetails(partial?.details)) updateLatestBroadcastTargets(partial.details);
1147
+ onUpdate?.(partial);
1148
+ };
846
1149
 
847
1150
  // Security: guard project-local agents before running
848
1151
  const requested = new Set<string>();
@@ -937,11 +1240,10 @@ This guard prevents self-recursion and cyclic handoffs (for example A -> B -> A)
937
1240
  return executeSingle(
938
1241
  task.agent,
939
1242
  task.task,
940
- task.cwd,
941
1243
  agents,
942
1244
  ctx.cwd,
943
1245
  signal,
944
- onUpdate,
1246
+ trackedOnUpdate,
945
1247
  makeDetails,
946
1248
  resumePlan?.details?.results[0],
947
1249
  getSessionDirForTask(resumePlan?.previousToolCallId ?? toolCallId, 0),
@@ -950,7 +1252,7 @@ This guard prevents self-recursion and cyclic handoffs (for example A -> B -> A)
950
1252
  // active model (normal runs). This prevents children from
951
1253
  // defaulting to whatever settings.json says at spawn time, which
952
1254
  // can change while the parent session is long-running.
953
- formatModelFlag(modelToRestoreAfterResume ?? lastRestorableModel),
1255
+ formatModelFlag(modelToRestoreAfterResume ?? ctx.model ?? lastRestorableModel),
954
1256
  );
955
1257
  }
956
1258
 
@@ -959,12 +1261,12 @@ This guard prevents self-recursion and cyclic handoffs (for example A -> B -> A)
959
1261
  agents,
960
1262
  ctx.cwd,
961
1263
  signal,
962
- onUpdate,
1264
+ trackedOnUpdate,
963
1265
  makeDetails,
964
1266
  resumePlan?.details?.results,
965
1267
  (index) => getSessionDirForTask(resumePlan?.previousToolCallId ?? toolCallId, index),
966
1268
  !!resumePlan,
967
- formatModelFlag(modelToRestoreAfterResume ?? lastRestorableModel),
1269
+ formatModelFlag(modelToRestoreAfterResume ?? ctx.model ?? lastRestorableModel),
968
1270
  );
969
1271
  } catch (err) {
970
1272
  const msg = err instanceof Error ? err.message : String(err);
@@ -998,7 +1300,6 @@ This guard prevents self-recursion and cyclic handoffs (for example A -> B -> A)
998
1300
  async function executeSingle(
999
1301
  agentName: string,
1000
1302
  task: string,
1001
- cwd: string | undefined,
1002
1303
  agents: AgentConfig[],
1003
1304
  defaultCwd: string,
1004
1305
  signal: AbortSignal | undefined,
@@ -1021,12 +1322,12 @@ This guard prevents self-recursion and cyclic handoffs (for example A -> B -> A)
1021
1322
  };
1022
1323
  }
1023
1324
 
1325
+ let activeId: number | undefined;
1024
1326
  const result = await runAgentSubprocess({
1025
1327
  cwd: defaultCwd,
1026
1328
  agents,
1027
1329
  agentName,
1028
1330
  task,
1029
- taskCwd: cwd,
1030
1331
  parentDepth: currentDepth,
1031
1332
  parentAgentStack: ancestorAgentStack,
1032
1333
  maxDepth,
@@ -1039,7 +1340,16 @@ This guard prevents self-recursion and cyclic handoffs (for example A -> B -> A)
1039
1340
  resumeSession: resumeExistingSession,
1040
1341
  initialResult: previousResult,
1041
1342
  fallbackModel,
1343
+ onHandle: (handle) => {
1344
+ activeId = 1;
1345
+ activeSubagents.set(activeId, { agent: agentName, task, handle });
1346
+ updateLatestBroadcastTargets(undefined);
1347
+ },
1042
1348
  });
1349
+ if (activeId !== undefined) {
1350
+ activeSubagents.delete(activeId);
1351
+ updateLatestBroadcastTargets(undefined);
1352
+ }
1043
1353
 
1044
1354
  if (isResultError(result)) {
1045
1355
  const errorMsg =
@@ -1070,7 +1380,7 @@ This guard prevents self-recursion and cyclic handoffs (for example A -> B -> A)
1070
1380
  }
1071
1381
 
1072
1382
  async function executeParallel(
1073
- tasks: Array<{ agent: string; task: string; cwd?: string }>,
1383
+ tasks: Array<{ agent: string; task: string }>,
1074
1384
  agents: AgentConfig[],
1075
1385
  defaultCwd: string,
1076
1386
  signal: AbortSignal | undefined,
@@ -1081,22 +1391,40 @@ This guard prevents self-recursion and cyclic handoffs (for example A -> B -> A)
1081
1391
  resumeExistingSessions: boolean,
1082
1392
  fallbackModel?: string,
1083
1393
  ) {
1084
- return executeParallelSubprocess(
1085
- tasks,
1086
- agents,
1087
- defaultCwd,
1088
- currentDepth,
1089
- maxDepth,
1090
- ancestorAgentStack,
1091
- preventCycles,
1092
- signal,
1093
- onUpdate,
1094
- makeDetails("parallel"),
1095
- resumeResults,
1096
- (index) => getSessionDir(index),
1097
- resumeExistingSessions,
1098
- currentSubagentSessionRoot,
1099
- fallbackModel,
1100
- );
1394
+ const taskIds = new Map<number, number>();
1395
+ try {
1396
+ return await executeParallelSubprocess(
1397
+ tasks,
1398
+ agents,
1399
+ defaultCwd,
1400
+ currentDepth,
1401
+ maxDepth,
1402
+ ancestorAgentStack,
1403
+ preventCycles,
1404
+ signal,
1405
+ onUpdate,
1406
+ makeDetails("parallel"),
1407
+ resumeResults,
1408
+ (index) => getSessionDir(index),
1409
+ resumeExistingSessions,
1410
+ currentSubagentSessionRoot,
1411
+ fallbackModel,
1412
+ (index, task, handle) => {
1413
+ const id = index + 1;
1414
+ taskIds.set(index, id);
1415
+ activeSubagents.set(id, { agent: task.agent, task: task.task, handle });
1416
+ updateLatestBroadcastTargets(undefined);
1417
+ },
1418
+ (index) => {
1419
+ const id = taskIds.get(index);
1420
+ if (id !== undefined) {
1421
+ activeSubagents.delete(id);
1422
+ updateLatestBroadcastTargets(undefined);
1423
+ }
1424
+ },
1425
+ );
1426
+ } finally {
1427
+ for (const id of taskIds.values()) activeSubagents.delete(id);
1428
+ }
1101
1429
  }
1102
1430
  }
package/package.json CHANGED
@@ -1,74 +1,74 @@
1
- {
2
- "name": "oira666_pi-subagent",
3
- "version": "0.2.19",
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.21",
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
+ }