pi-crew 0.1.51 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (240) hide show
  1. package/CHANGELOG.md +56 -1
  2. package/README.md +176 -781
  3. package/agents/analyst.md +11 -11
  4. package/agents/critic.md +11 -11
  5. package/agents/executor.md +11 -11
  6. package/agents/explorer.md +11 -11
  7. package/agents/planner.md +11 -11
  8. package/agents/reviewer.md +11 -11
  9. package/agents/security-reviewer.md +11 -11
  10. package/agents/test-engineer.md +11 -11
  11. package/agents/verifier.md +70 -11
  12. package/agents/writer.md +11 -11
  13. package/docs/actions-reference.md +595 -0
  14. package/docs/commands-reference.md +347 -0
  15. package/docs/runtime-flow.md +148 -148
  16. package/index.ts +6 -6
  17. package/package.json +99 -99
  18. package/skills/async-worker-recovery/SKILL.md +42 -42
  19. package/skills/context-artifact-hygiene/SKILL.md +52 -52
  20. package/skills/delegation-patterns/SKILL.md +54 -54
  21. package/skills/mailbox-interactive/SKILL.md +40 -40
  22. package/skills/model-routing-context/SKILL.md +39 -39
  23. package/skills/multi-perspective-review/SKILL.md +58 -58
  24. package/skills/observability-reliability/SKILL.md +41 -41
  25. package/skills/orchestration/SKILL.md +157 -157
  26. package/skills/ownership-session-security/SKILL.md +41 -41
  27. package/skills/pi-extension-lifecycle/SKILL.md +39 -39
  28. package/skills/requirements-to-task-packet/SKILL.md +63 -63
  29. package/skills/resource-discovery-config/SKILL.md +41 -41
  30. package/skills/runtime-state-reader/SKILL.md +44 -44
  31. package/skills/secure-agent-orchestration-review/SKILL.md +45 -45
  32. package/skills/state-mutation-locking/SKILL.md +42 -42
  33. package/skills/systematic-debugging/SKILL.md +67 -67
  34. package/skills/ui-render-performance/SKILL.md +39 -39
  35. package/skills/verification-before-done/SKILL.md +57 -57
  36. package/skills/worktree-isolation/SKILL.md +39 -39
  37. package/src/adapters/claude-adapter.ts +25 -0
  38. package/src/adapters/codex-adapter.ts +21 -0
  39. package/src/adapters/cursor-adapter.ts +17 -0
  40. package/src/adapters/export-util.ts +137 -0
  41. package/src/adapters/index.ts +15 -0
  42. package/src/adapters/registry.ts +18 -0
  43. package/src/adapters/types.ts +23 -0
  44. package/src/agents/agent-config.ts +2 -0
  45. package/src/agents/agent-search.ts +98 -98
  46. package/src/agents/discover-agents.ts +2 -1
  47. package/src/config/config.ts +13 -1
  48. package/src/config/drift-detector.ts +211 -0
  49. package/src/config/markers.ts +327 -0
  50. package/src/config/resilient-parser.ts +108 -0
  51. package/src/config/suggestions.ts +74 -0
  52. package/src/extension/cross-extension-rpc.ts +103 -94
  53. package/src/extension/project-init.ts +21 -1
  54. package/src/extension/register.ts +45 -14
  55. package/src/extension/registration/commands.ts +77 -8
  56. package/src/extension/registration/subagent-tools.ts +10 -1
  57. package/src/extension/registration/team-tool.ts +10 -1
  58. package/src/extension/registration/viewers.ts +48 -34
  59. package/src/extension/run-bundle-schema.ts +89 -89
  60. package/src/extension/run-import.ts +25 -1
  61. package/src/extension/run-index.ts +5 -1
  62. package/src/extension/run-maintenance.ts +142 -68
  63. package/src/extension/team-manager-command.ts +10 -1
  64. package/src/extension/team-tool/api.ts +441 -441
  65. package/src/extension/team-tool/doctor.ts +28 -3
  66. package/src/extension/team-tool/handle-settings.ts +195 -188
  67. package/src/extension/team-tool/inspect.ts +41 -41
  68. package/src/extension/team-tool/intent-policy.ts +42 -42
  69. package/src/extension/team-tool/lifecycle-actions.ts +27 -8
  70. package/src/extension/team-tool/plan.ts +19 -19
  71. package/src/extension/team-tool/run.ts +12 -1
  72. package/src/extension/team-tool.ts +332 -322
  73. package/src/i18n.ts +184 -184
  74. package/src/observability/exporters/otlp-exporter.ts +92 -77
  75. package/src/prompt/prompt-runtime.ts +72 -72
  76. package/src/runtime/agent-memory.ts +72 -72
  77. package/src/runtime/agent-observability.ts +114 -114
  78. package/src/runtime/async-marker.ts +26 -26
  79. package/src/runtime/attention-events.ts +28 -28
  80. package/src/runtime/auto-resume.ts +100 -0
  81. package/src/runtime/background-runner.ts +11 -1
  82. package/src/runtime/cancellation-token.ts +89 -89
  83. package/src/runtime/cancellation.ts +61 -61
  84. package/src/runtime/capability-inventory.ts +116 -116
  85. package/src/runtime/child-pi.ts +7 -2
  86. package/src/runtime/compaction-summary.ts +271 -0
  87. package/src/runtime/completion-guard.ts +190 -190
  88. package/src/runtime/crash-recovery.ts +33 -1
  89. package/src/runtime/delta-conflict.ts +360 -0
  90. package/src/runtime/direct-run.ts +35 -35
  91. package/src/runtime/foreground-control.ts +82 -82
  92. package/src/runtime/green-contract.ts +46 -46
  93. package/src/runtime/group-join.ts +106 -106
  94. package/src/runtime/heartbeat-gradient.ts +28 -28
  95. package/src/runtime/heartbeat-watcher.ts +124 -124
  96. package/src/runtime/iteration-hooks.ts +264 -0
  97. package/src/runtime/live-agent-control.ts +88 -88
  98. package/src/runtime/live-control-realtime.ts +36 -36
  99. package/src/runtime/live-extension-bridge.ts +150 -150
  100. package/src/runtime/live-irc.ts +92 -92
  101. package/src/runtime/live-session-health.ts +100 -100
  102. package/src/runtime/loop-gates.ts +129 -0
  103. package/src/runtime/metric-parser.ts +40 -0
  104. package/src/runtime/notebook-helpers.ts +90 -90
  105. package/src/runtime/orphan-sentinel.ts +7 -7
  106. package/src/runtime/parallel-research.ts +44 -44
  107. package/src/runtime/phase-progress.ts +217 -0
  108. package/src/runtime/pi-args.ts +38 -11
  109. package/src/runtime/pi-json-output.ts +111 -111
  110. package/src/runtime/pi-spawn.ts +57 -7
  111. package/src/runtime/policy-engine.ts +79 -79
  112. package/src/runtime/post-checks.ts +122 -0
  113. package/src/runtime/progress-event-coalescer.ts +43 -43
  114. package/src/runtime/prose-compressor.ts +164 -164
  115. package/src/runtime/recovery-recipes.ts +74 -74
  116. package/src/runtime/result-extractor.ts +121 -121
  117. package/src/runtime/role-permission.ts +39 -39
  118. package/src/runtime/sensitive-paths.ts +2 -2
  119. package/src/runtime/session-resources.ts +25 -25
  120. package/src/runtime/session-snapshot.ts +59 -59
  121. package/src/runtime/session-usage.ts +79 -79
  122. package/src/runtime/sidechain-output.ts +29 -29
  123. package/src/runtime/stream-preview.ts +177 -177
  124. package/src/runtime/supervisor-contact.ts +59 -59
  125. package/src/runtime/task-display.ts +38 -38
  126. package/src/runtime/task-graph.ts +207 -0
  127. package/src/runtime/task-quality.ts +207 -0
  128. package/src/runtime/task-runner/capabilities.ts +78 -78
  129. package/src/runtime/task-runner/live-executor.ts +7 -1
  130. package/src/runtime/task-runner/progress.ts +119 -119
  131. package/src/runtime/task-runner/prompt-pipeline.ts +64 -64
  132. package/src/runtime/task-runner/result-utils.ts +14 -14
  133. package/src/runtime/task-runner/run-projection.ts +103 -103
  134. package/src/runtime/task-runner/state-helpers.ts +22 -22
  135. package/src/runtime/team-runner.ts +117 -7
  136. package/src/runtime/worker-heartbeat.ts +21 -21
  137. package/src/runtime/worker-startup.ts +57 -57
  138. package/src/runtime/workflow-state.ts +187 -0
  139. package/src/runtime/workspace-tree.ts +298 -298
  140. package/src/schema/config-schema.ts +11 -0
  141. package/src/schema/validation-types.ts +148 -0
  142. package/src/skills/skill-templates.ts +374 -0
  143. package/src/state/active-run-registry.ts +35 -11
  144. package/src/state/atomic-write.ts +33 -26
  145. package/src/state/contracts.ts +1 -0
  146. package/src/state/event-reconstructor.ts +217 -0
  147. package/src/state/locks.ts +2 -13
  148. package/src/state/mailbox.ts +4 -3
  149. package/src/state/state-store.ts +16 -6
  150. package/src/state/task-claims.ts +44 -44
  151. package/src/state/types.ts +9 -0
  152. package/src/state/usage.ts +29 -29
  153. package/src/subagents/async-entry.ts +1 -1
  154. package/src/subagents/index.ts +3 -3
  155. package/src/subagents/live/control.ts +1 -1
  156. package/src/subagents/live/manager.ts +1 -1
  157. package/src/subagents/live/realtime.ts +1 -1
  158. package/src/subagents/live/session-runtime.ts +1 -1
  159. package/src/subagents/manager.ts +1 -1
  160. package/src/subagents/spawn.ts +1 -1
  161. package/src/teams/team-serializer.ts +38 -38
  162. package/src/types/diff.d.ts +18 -18
  163. package/src/ui/crew-footer.ts +101 -101
  164. package/src/ui/crew-select-list.ts +111 -111
  165. package/src/ui/crew-widget.ts +5 -2
  166. package/src/ui/dashboard-panes/cancellation-pane.ts +42 -42
  167. package/src/ui/dashboard-panes/capability-pane.ts +59 -59
  168. package/src/ui/dashboard-panes/mailbox-pane.ts +35 -35
  169. package/src/ui/dashboard-panes/metrics-pane.ts +34 -34
  170. package/src/ui/dashboard-panes/progress-pane.ts +11 -0
  171. package/src/ui/dynamic-border.ts +25 -25
  172. package/src/ui/layout-primitives.ts +106 -106
  173. package/src/ui/loaders.ts +158 -158
  174. package/src/ui/render-coalescer.ts +51 -51
  175. package/src/ui/render-diff.ts +119 -119
  176. package/src/ui/render-scheduler.ts +143 -143
  177. package/src/ui/run-action-dispatcher.ts +10 -1
  178. package/src/ui/spinner.ts +17 -17
  179. package/src/ui/status-colors.ts +58 -58
  180. package/src/ui/syntax-highlight.ts +116 -116
  181. package/src/ui/transcript-entries.ts +258 -258
  182. package/src/utils/completion-dedupe.ts +63 -63
  183. package/src/utils/frontmatter.ts +68 -68
  184. package/src/utils/git.ts +262 -262
  185. package/src/utils/ids.ts +17 -17
  186. package/src/utils/incremental-reader.ts +104 -104
  187. package/src/utils/names.ts +27 -27
  188. package/src/utils/redaction.ts +44 -44
  189. package/src/utils/safe-paths.ts +47 -47
  190. package/src/utils/scan-cache.ts +136 -136
  191. package/src/utils/sleep.ts +40 -26
  192. package/src/utils/task-name-generator.ts +337 -337
  193. package/src/workflows/validate-workflow.ts +40 -40
  194. package/src/worktree/branch-freshness.ts +45 -45
  195. package/teams/default.team.md +12 -12
  196. package/teams/fast-fix.team.md +11 -11
  197. package/teams/implementation.team.md +18 -18
  198. package/teams/parallel-research.team.md +14 -14
  199. package/teams/research.team.md +11 -11
  200. package/teams/review.team.md +12 -12
  201. package/workflows/default.workflow.md +30 -29
  202. package/workflows/fast-fix.workflow.md +23 -22
  203. package/workflows/implementation.workflow.md +43 -43
  204. package/workflows/parallel-research.workflow.md +46 -46
  205. package/workflows/research.workflow.md +22 -22
  206. package/workflows/review.workflow.md +30 -30
  207. package/docs/refactor-tasks-phase3.md +0 -394
  208. package/docs/refactor-tasks-phase4.md +0 -564
  209. package/docs/refactor-tasks-phase5.md +0 -402
  210. package/docs/refactor-tasks-phase6.md +0 -662
  211. package/docs/refactor-tasks.md +0 -1484
  212. package/docs/research/AGENT-EXECUTION-ARCHITECTURE.md +0 -261
  213. package/docs/research/AGENT-LIFECYCLE-COMPARISON.md +0 -111
  214. package/docs/research/AUDIT_OH_MY_PI.md +0 -261
  215. package/docs/research/AUDIT_PI_CREW.md +0 -457
  216. package/docs/research/CAVEMAN-DEEP-RESEARCH.md +0 -281
  217. package/docs/research/COMPARISON_OH_MY_PI_VS_PI_CREW.md +0 -264
  218. package/docs/research/DEEP-RESEARCH-PI-POWERBAR.md +0 -343
  219. package/docs/research/DEEP_RESEARCH_SUBAGENT_ARCHITECTURE.md +0 -480
  220. package/docs/research/GAP_CLOSURE_IMPLEMENTATION_PLAN.md +0 -354
  221. package/docs/research/IMPLEMENTATION_PLAN.md +0 -385
  222. package/docs/research/LIVE-SESSION-PRODUCTION-READY-PLAN.md +0 -502
  223. package/docs/research/OH-MY-PI-DEEP-RESEARCH-v14.7.6.md +0 -266
  224. package/docs/research/REMAINING-GAPS-PLAN.md +0 -363
  225. package/docs/research/SESSION-SUMMARY-2026-05-08.md +0 -146
  226. package/docs/research/UI-RESPONSIVENESS-AUDIT.md +0 -173
  227. package/docs/research-awesome-agent-skills-distillation.md +0 -100
  228. package/docs/research-extension-examples.md +0 -297
  229. package/docs/research-extension-system.md +0 -324
  230. package/docs/research-oh-my-pi-distillation.md +0 -369
  231. package/docs/research-optimization-plan.md +0 -548
  232. package/docs/research-phase10-distillation.md +0 -199
  233. package/docs/research-phase11-distillation.md +0 -201
  234. package/docs/research-phase8-operator-experience-plan.md +0 -819
  235. package/docs/research-phase9-observability-reliability-plan.md +0 -1190
  236. package/docs/research-pi-coding-agent.md +0 -357
  237. package/docs/research-source-pi-crew-reference.md +0 -174
  238. package/docs/research-ui-optimization-plan.md +0 -480
  239. package/docs/source-runtime-refactor-map.md +0 -107
  240. package/src/utils/atomic-write.ts +0 -33
@@ -1,94 +1,103 @@
1
- import type { ExtensionContext } from "@mariozechner/pi-coding-agent";
2
- import type { TeamToolParamsValue } from "../schema/team-tool-schema.ts";
3
- import { handleTeamTool } from "./team-tool.ts";
4
- import { parseLiveControlRealtimeMessage, publishLiveControlRealtime } from "../runtime/live-control-realtime.ts";
5
-
6
- export interface EventBusLike {
7
- on(event: string, handler: (data: unknown) => void): (() => void) | void;
8
- emit(event: string, data: unknown): void;
9
- }
10
-
11
- export type RpcReply<T = unknown> = { success: true; data?: T } | { success: false; error: string };
12
- export const PI_CREW_RPC_VERSION = 1;
13
-
14
- export interface PiCrewRpcHandle {
15
- unsubscribe(): void;
16
- }
17
-
18
- function requestId(raw: unknown): string | undefined {
19
- return raw && typeof raw === "object" && !Array.isArray(raw) && typeof (raw as { requestId?: unknown }).requestId === "string" ? (raw as { requestId: string }).requestId : undefined;
20
- }
21
-
22
- function reply(events: EventBusLike, channel: string, id: string | undefined, payload: RpcReply): void {
23
- if (!id) return;
24
- events.emit(`${channel}:reply:${id}`, payload);
25
- }
26
-
27
- function textOf(result: Awaited<ReturnType<typeof handleTeamTool>>): string {
28
- return result.content?.map((item) => item.type === "text" ? item.text : "").join("\n") ?? "";
29
- }
30
-
31
- function on(events: EventBusLike, channel: string, handler: (raw: unknown) => void): () => void {
32
- const unsub = events.on(channel, handler);
33
- return typeof unsub === "function" ? unsub : () => {};
34
- }
35
-
36
- export function registerPiCrewRpc(events: EventBusLike | undefined, getCtx: () => ExtensionContext | undefined): PiCrewRpcHandle | undefined {
37
- if (!events) return undefined;
38
- const unsubs = [
39
- on(events, "pi-crew:rpc:ping", (raw) => reply(events, "pi-crew:rpc:ping", requestId(raw), { success: true, data: { version: PI_CREW_RPC_VERSION } })),
40
- on(events, "pi-crew:rpc:run", async (raw) => {
41
- const id = requestId(raw);
42
- try {
43
- const ctx = getCtx();
44
- if (!ctx) throw new Error("No active pi-crew session context.");
45
- // Validate payload: only allow known fields from TeamToolParamsValue
46
- const ALLOWED_RPC_RUN_KEYS = new Set(["goal", "team", "workflow", "async", "cwd", "config", "skill", "model"]);
47
- let params: TeamToolParamsValue;
48
- if (raw && typeof raw === "object" && !Array.isArray(raw)) {
49
- const filtered: Record<string, unknown> = { ...(raw as object) };
50
- // Strip any keys not in the allowlist to prevent injection of unexpected fields
51
- for (const key of Object.keys(filtered)) {
52
- if (!ALLOWED_RPC_RUN_KEYS.has(key)) delete filtered[key];
53
- }
54
- params = { ...filtered, action: "run" } as TeamToolParamsValue;
55
- } else {
56
- params = { action: "run" };
57
- }
58
- const result = await handleTeamTool(params, ctx);
59
- reply(events, "pi-crew:rpc:run", id, result.isError ? { success: false, error: textOf(result) } : { success: true, data: result.details });
60
- } catch (error) {
61
- reply(events, "pi-crew:rpc:run", id, { success: false, error: error instanceof Error ? error.message : String(error) });
62
- }
63
- }),
64
- on(events, "pi-crew:rpc:status", async (raw) => {
65
- const id = requestId(raw);
66
- try {
67
- const ctx = getCtx();
68
- if (!ctx) throw new Error("No active pi-crew session context.");
69
- const runId = raw && typeof raw === "object" && !Array.isArray(raw) ? (raw as { runId?: string }).runId : undefined;
70
- const result = await handleTeamTool({ action: "status", runId }, ctx);
71
- reply(events, "pi-crew:rpc:status", id, result.isError ? { success: false, error: textOf(result) } : { success: true, data: { text: textOf(result), details: result.details } });
72
- } catch (error) {
73
- reply(events, "pi-crew:rpc:status", id, { success: false, error: error instanceof Error ? error.message : String(error) });
74
- }
75
- }),
76
- on(events, "pi-crew:live-control", (raw) => {
77
- const request = parseLiveControlRealtimeMessage(raw);
78
- if (request) publishLiveControlRealtime(request);
79
- }),
80
- on(events, "pi-crew:rpc:live-control", async (raw) => {
81
- const id = requestId(raw);
82
- try {
83
- const ctx = getCtx();
84
- if (!ctx) throw new Error("No active pi-crew session context.");
85
- const obj = raw && typeof raw === "object" && !Array.isArray(raw) ? raw as Record<string, unknown> : {};
86
- const result = await handleTeamTool({ action: "api", runId: typeof obj.runId === "string" ? obj.runId : undefined, config: { operation: typeof obj.operation === "string" ? obj.operation : "steer-agent", agentId: obj.agentId, message: obj.message, prompt: obj.prompt } }, ctx);
87
- reply(events, "pi-crew:rpc:live-control", id, result.isError ? { success: false, error: textOf(result) } : { success: true, data: { text: textOf(result), details: result.details } });
88
- } catch (error) {
89
- reply(events, "pi-crew:rpc:live-control", id, { success: false, error: error instanceof Error ? error.message : String(error) });
90
- }
91
- }),
92
- ];
93
- return { unsubscribe: () => unsubs.forEach((unsub) => unsub()) };
94
- }
1
+ import type { ExtensionContext } from "@mariozechner/pi-coding-agent";
2
+ import type { TeamToolParamsValue } from "../schema/team-tool-schema.ts";
3
+ // Lazy-loaded to avoid pulling team-tool.ts (and its entire runtime chain) into module load.
4
+ import type { handleTeamTool as HandleTeamToolFn } from "./team-tool.ts";
5
+ let _cachedHandleTeamTool: typeof HandleTeamToolFn | undefined;
6
+ async function handleTeamTool(params: Parameters<typeof HandleTeamToolFn>[0], ctx: Parameters<typeof HandleTeamToolFn>[1]): Promise<Awaited<ReturnType<typeof HandleTeamToolFn>>> {
7
+ if (!_cachedHandleTeamTool) {
8
+ const mod = await import("./team-tool.ts");
9
+ _cachedHandleTeamTool = mod.handleTeamTool;
10
+ }
11
+ return _cachedHandleTeamTool(params, ctx);
12
+ }
13
+ import { parseLiveControlRealtimeMessage, publishLiveControlRealtime } from "../runtime/live-control-realtime.ts";
14
+
15
+ export interface EventBusLike {
16
+ on(event: string, handler: (data: unknown) => void): (() => void) | void;
17
+ emit(event: string, data: unknown): void;
18
+ }
19
+
20
+ export type RpcReply<T = unknown> = { success: true; data?: T } | { success: false; error: string };
21
+ export const PI_CREW_RPC_VERSION = 1;
22
+
23
+ export interface PiCrewRpcHandle {
24
+ unsubscribe(): void;
25
+ }
26
+
27
+ function requestId(raw: unknown): string | undefined {
28
+ return raw && typeof raw === "object" && !Array.isArray(raw) && typeof (raw as { requestId?: unknown }).requestId === "string" ? (raw as { requestId: string }).requestId : undefined;
29
+ }
30
+
31
+ function reply(events: EventBusLike, channel: string, id: string | undefined, payload: RpcReply): void {
32
+ if (!id) return;
33
+ events.emit(`${channel}:reply:${id}`, payload);
34
+ }
35
+
36
+ function textOf(result: Awaited<ReturnType<typeof handleTeamTool>>): string {
37
+ return result.content?.map((item) => item.type === "text" ? item.text : "").join("\n") ?? "";
38
+ }
39
+
40
+ function on(events: EventBusLike, channel: string, handler: (raw: unknown) => void): () => void {
41
+ const unsub = events.on(channel, handler);
42
+ return typeof unsub === "function" ? unsub : () => {};
43
+ }
44
+
45
+ export function registerPiCrewRpc(events: EventBusLike | undefined, getCtx: () => ExtensionContext | undefined): PiCrewRpcHandle | undefined {
46
+ if (!events) return undefined;
47
+ const unsubs = [
48
+ on(events, "pi-crew:rpc:ping", (raw) => reply(events, "pi-crew:rpc:ping", requestId(raw), { success: true, data: { version: PI_CREW_RPC_VERSION } })),
49
+ on(events, "pi-crew:rpc:run", async (raw) => {
50
+ const id = requestId(raw);
51
+ try {
52
+ const ctx = getCtx();
53
+ if (!ctx) throw new Error("No active pi-crew session context.");
54
+ // Validate payload: only allow known fields from TeamToolParamsValue
55
+ const ALLOWED_RPC_RUN_KEYS = new Set(["goal", "team", "workflow", "async", "cwd", "config", "skill", "model"]);
56
+ let params: TeamToolParamsValue;
57
+ if (raw && typeof raw === "object" && !Array.isArray(raw)) {
58
+ const filtered: Record<string, unknown> = { ...(raw as object) };
59
+ // Strip any keys not in the allowlist to prevent injection of unexpected fields
60
+ for (const key of Object.keys(filtered)) {
61
+ if (!ALLOWED_RPC_RUN_KEYS.has(key)) delete filtered[key];
62
+ }
63
+ params = { ...filtered, action: "run" } as TeamToolParamsValue;
64
+ } else {
65
+ params = { action: "run" };
66
+ }
67
+ const result = await handleTeamTool(params, ctx);
68
+ reply(events, "pi-crew:rpc:run", id, result.isError ? { success: false, error: textOf(result) } : { success: true, data: result.details });
69
+ } catch (error) {
70
+ reply(events, "pi-crew:rpc:run", id, { success: false, error: error instanceof Error ? error.message : String(error) });
71
+ }
72
+ }),
73
+ on(events, "pi-crew:rpc:status", async (raw) => {
74
+ const id = requestId(raw);
75
+ try {
76
+ const ctx = getCtx();
77
+ if (!ctx) throw new Error("No active pi-crew session context.");
78
+ const runId = raw && typeof raw === "object" && !Array.isArray(raw) ? (raw as { runId?: string }).runId : undefined;
79
+ const result = await handleTeamTool({ action: "status", runId }, ctx);
80
+ reply(events, "pi-crew:rpc:status", id, result.isError ? { success: false, error: textOf(result) } : { success: true, data: { text: textOf(result), details: result.details } });
81
+ } catch (error) {
82
+ reply(events, "pi-crew:rpc:status", id, { success: false, error: error instanceof Error ? error.message : String(error) });
83
+ }
84
+ }),
85
+ on(events, "pi-crew:live-control", (raw) => {
86
+ const request = parseLiveControlRealtimeMessage(raw);
87
+ if (request) publishLiveControlRealtime(request);
88
+ }),
89
+ on(events, "pi-crew:rpc:live-control", async (raw) => {
90
+ const id = requestId(raw);
91
+ try {
92
+ const ctx = getCtx();
93
+ if (!ctx) throw new Error("No active pi-crew session context.");
94
+ const obj = raw && typeof raw === "object" && !Array.isArray(raw) ? raw as Record<string, unknown> : {};
95
+ const result = await handleTeamTool({ action: "api", runId: typeof obj.runId === "string" ? obj.runId : undefined, config: { operation: typeof obj.operation === "string" ? obj.operation : "steer-agent", agentId: obj.agentId, message: obj.message, prompt: obj.prompt } }, ctx);
96
+ reply(events, "pi-crew:rpc:live-control", id, result.isError ? { success: false, error: textOf(result) } : { success: true, data: { text: textOf(result), details: result.details } });
97
+ } catch (error) {
98
+ reply(events, "pi-crew:rpc:live-control", id, { success: false, error: error instanceof Error ? error.message : String(error) });
99
+ }
100
+ }),
101
+ ];
102
+ return { unsubscribe: () => unsubs.forEach((unsub) => unsub()) };
103
+ }
@@ -2,6 +2,7 @@ import * as fs from "node:fs";
2
2
  import * as path from "node:path";
3
3
  import { configPath as globalConfigPath } from "../config/config.ts";
4
4
  import { DEFAULT_UI } from "../config/defaults.ts";
5
+ import { injectGuidance, standardGuidanceBlocks } from "../config/markers.ts";
5
6
  import { packageRoot, projectCrewRoot, projectPiRoot } from "../utils/paths.ts";
6
7
 
7
8
  export interface ProjectInitOptions {
@@ -21,6 +22,8 @@ export interface ProjectInitResult {
21
22
  configScope: "global" | "project" | "none";
22
23
  configCreated: boolean;
23
24
  configSkipped: boolean;
25
+ guidancePath: string;
26
+ guidanceModified: boolean;
24
27
  }
25
28
 
26
29
  function ensureDir(dir: string, createdDirs: string[]): void {
@@ -144,5 +147,22 @@ export function initializeProject(cwd: string, options: ProjectInitOptions = {})
144
147
  gitignoreUpdated = true;
145
148
  }
146
149
 
147
- return { createdDirs, copiedFiles, skippedFiles, gitignorePath, gitignoreUpdated, configPath, configScope, configCreated, configSkipped };
150
+ // Inject guidance into project AGENTS.md (or similar) using marker-based injection.
151
+ const guidancePath = path.join(cwd, "AGENTS.md");
152
+ const version = getPackageVersion();
153
+ const guidanceResult = injectGuidance(guidancePath, standardGuidanceBlocks(version));
154
+
155
+ return { createdDirs, copiedFiles, skippedFiles, gitignorePath, gitignoreUpdated, configPath, configScope, configCreated, configSkipped, guidancePath, guidanceModified: guidanceResult.modified };
156
+ }
157
+
158
+ /** Read the current package version from the nearest package.json. */
159
+ function getPackageVersion(): string {
160
+ try {
161
+ const pkgPath = path.join(packageRoot(), "package.json");
162
+ const raw = fs.readFileSync(pkgPath, "utf-8");
163
+ const parsed: { version?: string } = JSON.parse(raw) as { version?: string };
164
+ return parsed.version ?? "0.0.0";
165
+ } catch {
166
+ return "0.0.0";
167
+ }
148
168
  }
@@ -38,6 +38,7 @@ import { OTLPExporter } from "../observability/exporters/otlp-exporter.ts";
38
38
  import { HeartbeatWatcher } from "../runtime/heartbeat-watcher.ts";
39
39
  import { appendDeadletter } from "../runtime/deadletter.ts";
40
40
  import { cancelOrphanedRuns, detectInterruptedRuns, purgeStaleActiveRunIndex } from "../runtime/crash-recovery.ts";
41
+ import { pruneFinishedRuns, pruneUserLevelRuns } from "../extension/run-maintenance.ts";
41
42
  import { DeliveryCoordinator } from "../runtime/delivery-coordinator.ts";
42
43
  import { OverflowRecoveryTracker } from "../runtime/overflow-recovery.ts";
43
44
  import { tryRegisterSessionCleanup } from "../runtime/session-resources.ts";
@@ -415,26 +416,56 @@ export function registerPiTeams(pi: ExtensionAPI): void {
415
416
 
416
417
  // Auto-cancel orphaned runs from dead sessions
417
418
  const currentSessionId = (typeof ctx === "object" && ctx !== null && "sessionId" in ctx ? (ctx as Record<string, unknown>).sessionId : undefined) as string | undefined;
418
- if (currentSessionId) {
419
+
420
+ // Defer ALL heavy cleanup to after the session_start handler returns.
421
+ // These operations involve synchronous directory scanning (readdirSync, readFileSync)
422
+ // which can take 100ms–1s+ on Windows. They MUST NOT block the session_start event.
423
+ setTimeout(() => {
424
+ if (cleanedUp || sessionGeneration !== ownerGeneration) return; // session switched while we waited
425
+
426
+ // Auto-cancel orphaned runs
427
+ if (currentSessionId) {
428
+ try {
429
+ const { cancelled } = cancelOrphanedRuns(ctx.cwd, getManifestCache(ctx.cwd), currentSessionId);
430
+ if (cancelled.length > 0) {
431
+ notifyOperator({ id: `orphan_cleanup`, severity: "info", source: "crash-recovery", title: `Cleaned up ${cancelled.length} orphaned run(s)`, body: `Runs from previous sessions were auto-cancelled: ${cancelled.join(", ")}` });
432
+ }
433
+ } catch (error) {
434
+ logInternalError("register.sessionStart.orphanCleanup", error);
435
+ }
436
+ }
437
+
438
+ // Global purge of stale active-run-index entries
419
439
  try {
420
- const { cancelled } = cancelOrphanedRuns(ctx.cwd, getManifestCache(ctx.cwd), currentSessionId);
421
- if (cancelled.length > 0) {
422
- notifyOperator({ id: `orphan_cleanup`, severity: "info", source: "crash-recovery", title: `Cleaned up ${cancelled.length} orphaned run(s)`, body: `Runs from previous sessions were auto-cancelled: ${cancelled.join(", ")}` });
440
+ const { purged } = purgeStaleActiveRunIndex();
441
+ if (purged.length > 0) {
442
+ notifyOperator({ id: `active_index_purge`, severity: "info", source: "crash-recovery", title: `Purged ${purged.length} stale active-run-index entr${purged.length === 1 ? "y" : "ies"}`, body: `Cleaned up global active run index` });
423
443
  }
424
444
  } catch (error) {
425
- logInternalError("register.sessionStart.orphanCleanup", error);
445
+ logInternalError("register.sessionStart.globalIndexPurge", error);
426
446
  }
427
- }
428
447
 
429
- // Global purge of stale active-run-index entries (temp dirs, dead workers, etc.)
430
- try {
431
- const { purged } = purgeStaleActiveRunIndex();
432
- if (purged.length > 0) {
433
- notifyOperator({ id: `active_index_purge`, severity: "info", source: "crash-recovery", title: `Purged ${purged.length} stale active-run-index entr${purged.length === 1 ? "y" : "ies"}`, body: `Cleaned up global active run index` });
448
+ // Auto-prune finished project-level run directories (keep 10 most recent)
449
+ try {
450
+ const { removed } = pruneFinishedRuns(ctx.cwd, 10);
451
+ if (removed.length > 0) {
452
+ notifyOperator({ id: `auto_prune_project`, severity: "info", source: "run-maintenance", title: `Auto-pruned ${removed.length} finished project run(s)`, body: `Removed old finished runs: ${removed.join(", ")}` });
453
+ }
454
+ } catch (error) {
455
+ logInternalError("register.sessionStart.autoPruneProject", error);
434
456
  }
435
- } catch (error) {
436
- logInternalError("register.sessionStart.globalIndexPurge", error);
437
- }
457
+
458
+ // Auto-prune finished user-level run directories (keep 10 most recent)
459
+ try {
460
+ const { removed } = pruneUserLevelRuns(10);
461
+ if (removed.length > 0) {
462
+ notifyOperator({ id: `auto_prune_user`, severity: "info", source: "run-maintenance", title: `Auto-pruned ${removed.length} finished user-level run(s)`, body: `Removed old finished runs: ${removed.join(", ")}` });
463
+ }
464
+ } catch (error) {
465
+ logInternalError("register.sessionStart.autoPruneUser", error);
466
+ }
467
+ }, 0);
468
+
438
469
 
439
470
  const loadedConfig = loadConfig(ctx.cwd);
440
471
  autoRecoveryLast.clear();
@@ -1,20 +1,39 @@
1
1
  import type { ExtensionAPI, ExtensionCommandContext, ExtensionContext } from "@mariozechner/pi-coding-agent";
2
2
  import { loadConfig } from "../../config/config.ts";
3
- import { handleTeamTool } from "../team-tool.ts";
3
+ // Lazy-loaded: team-tool.ts pulls in entire runtime chain (1.4s+).
4
+ import type { handleTeamTool as HandleTeamToolFn } from "../team-tool.ts";
5
+ let _cachedHandleTeamTool: typeof HandleTeamToolFn | undefined;
6
+ let _handleTeamToolPromise: Promise<typeof HandleTeamToolFn> | undefined;
7
+ async function handleTeamTool(params: Parameters<typeof HandleTeamToolFn>[0], ctx: Parameters<typeof HandleTeamToolFn>[1]): Promise<Awaited<ReturnType<typeof HandleTeamToolFn>>> {
8
+ if (!_cachedHandleTeamTool) {
9
+ if (!_handleTeamToolPromise) {
10
+ _handleTeamToolPromise = import("../team-tool.ts").then((mod) => {
11
+ _cachedHandleTeamTool = mod.handleTeamTool;
12
+ return mod.handleTeamTool;
13
+ });
14
+ }
15
+ const fn = await _handleTeamToolPromise;
16
+ return fn(params, ctx);
17
+ }
18
+ return _cachedHandleTeamTool(params, ctx);
19
+ }
4
20
  import { withSessionId } from "../team-tool/context.ts";
5
21
  import { piTeamsHelp } from "../help.ts";
6
22
  import { handleTeamManagerCommand } from "../team-manager-command.ts";
7
23
  import { loadRunManifestById } from "../../state/state-store.ts";
8
24
  import type { TeamRunManifest } from "../../state/types.ts";
9
25
  import { readCrewAgents } from "../../runtime/crew-agent-records.ts";
10
- import { AnimatedMascot } from "../../ui/mascot.ts";
11
26
  import * as path from "node:path";
12
- import { RunDashboard, type RunDashboardSelection } from "../../ui/run-dashboard.ts";
13
- import { DurableTextViewer } from "../../ui/transcript-viewer.ts";
14
- import { ConfirmOverlay, type ConfirmOptions } from "../../ui/overlays/confirm-overlay.ts";
15
- import { MailboxDetailOverlay, type MailboxAction } from "../../ui/overlays/mailbox-detail-overlay.ts";
16
- import { MailboxComposeOverlay, type MailboxComposeResult } from "../../ui/overlays/mailbox-compose-overlay.ts";
17
- import { AgentPickerOverlay } from "../../ui/overlays/agent-picker-overlay.ts";
27
+ // Heavy UI modules lazy-loaded because they're only used in /crew commands.
28
+ // RunDashboard (288ms), DurableTextViewer (658ms), Overlays are unnecessary at Pi startup.
29
+ import type { RunDashboard as RunDashboardType, RunDashboardSelection } from "../../ui/run-dashboard.ts";
30
+ import type { DurableTextViewer as DurableTextViewerType } from "../../ui/transcript-viewer.ts";
31
+ import type { ConfirmOverlay as ConfirmOverlayType, ConfirmOptions } from "../../ui/overlays/confirm-overlay.ts";
32
+ import type { MailboxDetailOverlay as MailboxDetailOverlayType, MailboxAction } from "../../ui/overlays/mailbox-detail-overlay.ts";
33
+ import type { MailboxComposeOverlay as MailboxComposeOverlayType, MailboxComposeResult } from "../../ui/overlays/mailbox-compose-overlay.ts";
34
+ import type { AgentPickerOverlay as AgentPickerOverlayType } from "../../ui/overlays/agent-picker-overlay.ts";
35
+ import type { AnimatedMascot as AnimatedMascotType } from "../../ui/mascot.ts";
36
+ // Eagerly import lightweight modules
18
37
  import { dispatchDiagnosticExport, dispatchHealthRecovery, dispatchKillStaleWorkers, dispatchMailboxAck, dispatchMailboxAckAll, dispatchMailboxCompose, dispatchMailboxNudge } from "../../ui/run-action-dispatcher.ts";
19
38
  import { DEFAULT_UI } from "../../config/defaults.ts";
20
39
  import { listRecentDiagnostic } from "../../runtime/diagnostic-export.ts";
@@ -35,13 +54,58 @@ export interface RegisterTeamCommandsDeps {
35
54
  dismissNotifications?: () => void;
36
55
  }
37
56
 
57
+ // Lazy-loaded UI module cache — avoids importing 900ms+ of UI at Pi startup.
58
+ // These modules are only needed when user invokes /crew commands.
59
+ let _uiCache: {
60
+ RunDashboard: typeof RunDashboardType;
61
+ DurableTextViewer: typeof DurableTextViewerType;
62
+ ConfirmOverlay: typeof ConfirmOverlayType;
63
+ MailboxDetailOverlay: typeof MailboxDetailOverlayType;
64
+ MailboxComposeOverlay: typeof MailboxComposeOverlayType;
65
+ AgentPickerOverlay: typeof AgentPickerOverlayType;
66
+ AnimatedMascot: typeof AnimatedMascotType;
67
+ } | undefined;
68
+ let _uiCachePromise: Promise<NonNullable<typeof _uiCache>> | undefined;
69
+ async function ui(): Promise<NonNullable<typeof _uiCache>> {
70
+ if (!_uiCache) {
71
+ if (!_uiCachePromise) {
72
+ _uiCachePromise = (async () => {
73
+ const [rd, tv, co, md, mc, ap, ma] = await Promise.all([
74
+ import("../../ui/run-dashboard.ts"),
75
+ import("../../ui/transcript-viewer.ts"),
76
+ import("../../ui/overlays/confirm-overlay.ts"),
77
+ import("../../ui/overlays/mailbox-detail-overlay.ts"),
78
+ import("../../ui/overlays/mailbox-compose-overlay.ts"),
79
+ import("../../ui/overlays/agent-picker-overlay.ts"),
80
+ import("../../ui/mascot.ts"),
81
+ ]);
82
+ const cache = {
83
+ RunDashboard: rd.RunDashboard,
84
+ DurableTextViewer: tv.DurableTextViewer,
85
+ ConfirmOverlay: co.ConfirmOverlay,
86
+ MailboxDetailOverlay: md.MailboxDetailOverlay,
87
+ MailboxComposeOverlay: mc.MailboxComposeOverlay,
88
+ AgentPickerOverlay: ap.AgentPickerOverlay,
89
+ AnimatedMascot: ma.AnimatedMascot,
90
+ };
91
+ _uiCache = cache;
92
+ return cache;
93
+ })();
94
+ }
95
+ return _uiCachePromise;
96
+ }
97
+ return _uiCache;
98
+ }
99
+
38
100
  async function openConfirm(ctx: ExtensionCommandContext, options: ConfirmOptions): Promise<boolean> {
39
101
  if (!ctx.hasUI) return false;
102
+ const { ConfirmOverlay } = await ui();
40
103
  return await ctx.ui.custom<boolean>((_tui, theme, _keybindings, done) => new ConfirmOverlay(options, done, theme), { overlay: true, overlayOptions: { width: 64, maxHeight: "70%", anchor: "center" } });
41
104
  }
42
105
 
43
106
  async function handleMailboxDashboardAction(ctx: ExtensionCommandContext, runId: string): Promise<void> {
44
107
  if (!ctx.hasUI) return;
108
+ const { MailboxDetailOverlay } = await ui();
45
109
  const action = await ctx.ui.custom<MailboxAction | undefined>((_tui, theme, _keybindings, done) => new MailboxDetailOverlay({ runId, cwd: ctx.cwd, done, theme }), { overlay: true, overlayOptions: { width: "90%", maxHeight: "85%", anchor: "center" } });
46
110
  if (!action || action.type === "close") return;
47
111
  let resultMessage: string | undefined;
@@ -57,6 +121,7 @@ async function handleMailboxDashboardAction(ctx: ExtensionCommandContext, runId:
57
121
  ok = result.ok;
58
122
  resultMessage = result.message;
59
123
  } else if (action.type === "compose") {
124
+ const { MailboxComposeOverlay } = await ui();
60
125
  const compose = await ctx.ui.custom<MailboxComposeResult>((_tui, theme, _keybindings, done) => new MailboxComposeOverlay({ done, theme }), { overlay: true, overlayOptions: { width: "90%", maxHeight: "85%", anchor: "center" } });
61
126
  if (compose.type === "cancel") return;
62
127
  const result = await dispatchMailboxCompose(ctx as ExtensionContext, runId, compose.payload);
@@ -65,6 +130,7 @@ async function handleMailboxDashboardAction(ctx: ExtensionCommandContext, runId:
65
130
  } else if (action.type === "nudge") {
66
131
  let agentId = action.agentId;
67
132
  if (!agentId) {
133
+ const { AgentPickerOverlay } = await ui();
68
134
  const picked = await ctx.ui.custom<{ agentId: string } | undefined>((_tui, theme, _keybindings, done) => new AgentPickerOverlay({ cwd: ctx.cwd, runId, done, theme }), { overlay: true, overlayOptions: { width: 72, maxHeight: "75%", anchor: "center" } });
69
135
  agentId = picked?.agentId;
70
136
  }
@@ -272,6 +338,7 @@ export function registerTeamCommands(pi: ExtensionAPI, deps: RegisterTeamCommand
272
338
  if (ctx.hasUI && loaded) {
273
339
  const agent = readCrewAgents(loaded.manifest).find((item) => item.taskId === selected?.taskId || item.id === selected?.taskId) ?? readCrewAgents(loaded.manifest)[0];
274
340
  const resultText = agent?.resultArtifactPath ? commandText(await handleTeamTool({ action: "api", runId: selected?.runId ?? "", config: { operation: "read-agent-output", agentId: agent.taskId, maxBytes: 64_000 } }, teamCommandContext(ctx))) : "(no result)";
341
+ const { DurableTextViewer } = await ui();
275
342
  await ctx.ui.custom<undefined>((_tui, theme, _keybindings, done) => new DurableTextViewer("pi-crew result", `${selected?.runId ?? ""}:${agent?.taskId ?? "unknown"}`, resultText.split(/\r?\n/), theme, done), { overlay: true, overlayOptions: { width: "90%", maxHeight: "85%", anchor: "center" } });
276
343
  return;
277
344
  }
@@ -292,6 +359,7 @@ export function registerTeamCommands(pi: ExtensionAPI, deps: RegisterTeamCommand
292
359
  const uiConfig = loadConfig(ctx.cwd).config.ui;
293
360
  const rightPanel = (uiConfig?.dashboardPlacement ?? DEFAULT_UI.dashboardPlacement) === "right";
294
361
  const width = rightPanel ? Math.min(90, Math.max(40, uiConfig?.dashboardWidth ?? DEFAULT_UI.dashboardWidth)) : "90%";
362
+ const { RunDashboard } = await ui();
295
363
  const selection = await ctx.ui.custom<RunDashboardSelection | undefined>((_tui, theme, _keybindings, done) => new RunDashboard(runs, done, theme, { placement: rightPanel ? "right" : "center", showModel: uiConfig?.showModel, showTokens: uiConfig?.showTokens, showTools: uiConfig?.showTools, snapshotCache: deps.getRunSnapshotCache?.(ctx.cwd), runProvider: () => deps.getManifestCache(ctx.cwd).list(50), registry: deps.getMetricRegistry?.() }), { overlay: true, overlayOptions: rightPanel ? { width, minWidth: 40, maxHeight: "100%", anchor: "top-right", offsetX: 0, offsetY: 0, margin: { top: 0, right: 0, bottom: 0, left: 0 } } : { width, maxHeight: "90%", anchor: "center", margin: 2 } });
296
364
  if (!selection) return;
297
365
  if (selection.action === "reload") continue;
@@ -325,6 +393,7 @@ export function registerTeamCommands(pi: ExtensionAPI, deps: RegisterTeamCommand
325
393
  const effectArg = tokens.find((t) => ["random", "none", "typewriter", "scanline", "rain", "fade", "crt", "glitch", "dissolve"].includes(t));
326
394
  const style = (styleArg as "cat" | "armin" | undefined) ?? uiConfig?.mascotStyle ?? DEFAULT_UI.mascotStyle;
327
395
  const effect = (effectArg as "random" | "none" | "typewriter" | "scanline" | "rain" | "fade" | "crt" | "glitch" | "dissolve" | undefined) ?? uiConfig?.mascotEffect ?? DEFAULT_UI.mascotEffect;
396
+ const { AnimatedMascot } = await ui();
328
397
  await ctx.ui.custom<undefined>((tui, theme, _keybindings, done) => new AnimatedMascot(theme, () => done(undefined), { frameIntervalMs: style === "armin" ? 33 : 180, autoCloseMs: 7000, requestRender: () => requestRenderTarget(tui), style, effect }), { overlay: true, overlayOptions: { width: style === "armin" ? 48 : 62, maxHeight: "85%", anchor: "center" } });
329
398
  } });
330
399
 
@@ -1,7 +1,16 @@
1
1
  import type { ExtensionAPI, ToolDefinition } from "@mariozechner/pi-coding-agent";
2
2
  import { Type } from "typebox";
3
3
  import type { TeamToolParamsValue } from "../../schema/team-tool-schema.ts";
4
- import { handleTeamTool } from "../team-tool.ts";
4
+ // Lazy-loaded: team-tool.ts pulls in entire runtime chain.
5
+ import type { handleTeamTool as HandleTeamToolFn } from "../team-tool.ts";
6
+ let _cachedHandleTeamTool: typeof HandleTeamToolFn | undefined;
7
+ async function handleTeamTool(params: Parameters<typeof HandleTeamToolFn>[0], ctx: Parameters<typeof HandleTeamToolFn>[1]): Promise<Awaited<ReturnType<typeof HandleTeamToolFn>>> {
8
+ if (!_cachedHandleTeamTool) {
9
+ const mod = await import("../team-tool.ts");
10
+ _cachedHandleTeamTool = mod.handleTeamTool;
11
+ }
12
+ return _cachedHandleTeamTool(params, ctx);
13
+ }
5
14
  import { checkSubagentSpawnPermission, currentCrewRole } from "../../runtime/role-permission.ts";
6
15
  import { readPersistedSubagentRecord, savePersistedSubagentRecord, type SubagentManager, type SubagentSpawnOptions } from "../../subagents/manager.ts";
7
16
  import { loadConfig } from "../../config/config.ts";
@@ -9,7 +9,16 @@ import type { createManifestCache } from "../../runtime/manifest-cache.ts";
9
9
  import type { createRunSnapshotCache } from "../../ui/run-snapshot-cache.ts";
10
10
  import type { MetricRegistry } from "../../observability/metric-registry.ts";
11
11
  import { resolveRealContainedPath } from "../../utils/safe-paths.ts";
12
- import { handleTeamTool } from "../team-tool.ts";
12
+ // Team tool handler lazy-loaded because team-tool.ts imports many modules
13
+ import type { handleTeamTool as HandleTeamToolFn } from "../team-tool.ts";
14
+ let _cachedHandleTeamTool: typeof HandleTeamToolFn | undefined;
15
+ async function handleTeamTool(params: Parameters<typeof HandleTeamToolFn>[0], ctx: Parameters<typeof HandleTeamToolFn>[1]): Promise<ReturnType<typeof HandleTeamToolFn>> {
16
+ if (!_cachedHandleTeamTool) {
17
+ const mod = await import("../team-tool.ts");
18
+ _cachedHandleTeamTool = mod.handleTeamTool;
19
+ }
20
+ return _cachedHandleTeamTool(params, ctx);
21
+ }
13
22
  import { withSessionId } from "../team-tool/context.ts";
14
23
  import { toolResult } from "../tool-result.ts";
15
24
 
@@ -1,34 +1,48 @@
1
- import type { ExtensionCommandContext } from "@mariozechner/pi-coding-agent";
2
- import { loadRunManifestById } from "../../state/state-store.ts";
3
- import { readCrewAgents } from "../../runtime/crew-agent-records.ts";
4
- import { loadConfig } from "../../config/config.ts";
5
- import { DurableTranscriptViewer } from "../../ui/transcript-viewer.ts";
6
-
7
- export async function selectAgentTask(ctx: ExtensionCommandContext, runId: string | undefined, taskId?: string): Promise<{ runId: string; taskId?: string } | undefined> {
8
- if (!runId) return undefined;
9
- if (taskId) return { runId, taskId };
10
- const loaded = loadRunManifestById(ctx.cwd, runId);
11
- if (!loaded) return { runId };
12
- const agents = readCrewAgents(loaded.manifest);
13
- if (ctx.hasUI && agents.length > 1) {
14
- const choice = await ctx.ui.select("Select pi-crew agent", agents.map((agent) => `${agent.taskId} ${agent.role}→${agent.agent} [${agent.status}]`));
15
- return { runId, taskId: choice?.split(" ")[0] };
16
- }
17
- return { runId, taskId: agents[0]?.taskId };
18
- }
19
-
20
- export async function openTranscriptViewer(ctx: ExtensionCommandContext, initialRunId: string | undefined, initialTaskId?: string): Promise<boolean> {
21
- const selected = await selectAgentTask(ctx, initialRunId, initialTaskId);
22
- if (!selected) return false;
23
- const runId = selected.runId;
24
- const taskId = selected.taskId;
25
- if (!runId || !ctx.hasUI) return false;
26
- const loaded = loadRunManifestById(ctx.cwd, runId);
27
- if (!loaded) return false;
28
- const uiConfig = loadConfig(ctx.cwd).config.ui;
29
- await ctx.ui.custom<undefined>((_tui, theme, _keybindings, done) => new DurableTranscriptViewer(loaded.manifest, theme, done, taskId, { maxTailBytes: uiConfig?.transcriptTailBytes }), {
30
- overlay: true,
31
- overlayOptions: { width: "90%", maxHeight: "85%", anchor: "center" },
32
- });
33
- return true;
34
- }
1
+ import type { ExtensionCommandContext } from "@mariozechner/pi-coding-agent";
2
+ import { loadRunManifestById } from "../../state/state-store.ts";
3
+ import { readCrewAgents } from "../../runtime/crew-agent-records.ts";
4
+ import { loadConfig } from "../../config/config.ts";
5
+ // Lazy-loaded: DurableTranscriptViewer is 658ms — only needed for /crew transcript command
6
+ import type { DurableTranscriptViewer as DurableTranscriptViewerType } from "../../ui/transcript-viewer.ts";
7
+ let _cachedViewer: typeof DurableTranscriptViewerType | undefined;
8
+ let _viewerPromise: Promise<typeof DurableTranscriptViewerType> | undefined;
9
+ async function getViewer(): Promise<typeof DurableTranscriptViewerType> {
10
+ if (_cachedViewer) return _cachedViewer;
11
+ if (!_viewerPromise) {
12
+ _viewerPromise = import("../../ui/transcript-viewer.ts").then((mod) => {
13
+ _cachedViewer = mod.DurableTranscriptViewer;
14
+ return mod.DurableTranscriptViewer;
15
+ });
16
+ }
17
+ return _viewerPromise;
18
+ }
19
+
20
+ export async function selectAgentTask(ctx: ExtensionCommandContext, runId: string | undefined, taskId?: string): Promise<{ runId: string; taskId?: string } | undefined> {
21
+ if (!runId) return undefined;
22
+ if (taskId) return { runId, taskId };
23
+ const loaded = loadRunManifestById(ctx.cwd, runId);
24
+ if (!loaded) return { runId };
25
+ const agents = readCrewAgents(loaded.manifest);
26
+ if (ctx.hasUI && agents.length > 1) {
27
+ const choice = await ctx.ui.select("Select pi-crew agent", agents.map((agent) => `${agent.taskId} ${agent.role}→${agent.agent} [${agent.status}]`));
28
+ return { runId, taskId: choice?.split(" ")[0] };
29
+ }
30
+ return { runId, taskId: agents[0]?.taskId };
31
+ }
32
+
33
+ export async function openTranscriptViewer(ctx: ExtensionCommandContext, initialRunId: string | undefined, initialTaskId?: string): Promise<boolean> {
34
+ const selected = await selectAgentTask(ctx, initialRunId, initialTaskId);
35
+ if (!selected) return false;
36
+ const runId = selected.runId;
37
+ const taskId = selected.taskId;
38
+ if (!runId || !ctx.hasUI) return false;
39
+ const loaded = loadRunManifestById(ctx.cwd, runId);
40
+ if (!loaded) return false;
41
+ const uiConfig = loadConfig(ctx.cwd).config.ui;
42
+ const DurableTranscriptViewer = await getViewer();
43
+ await ctx.ui.custom<undefined>((_tui, theme, _keybindings, done) => new DurableTranscriptViewer(loaded.manifest, theme, done, taskId, { maxTailBytes: uiConfig?.transcriptTailBytes }), {
44
+ overlay: true,
45
+ overlayOptions: { width: "90%", maxHeight: "85%", anchor: "center" },
46
+ });
47
+ return true;
48
+ }