pi-crew 0.1.51 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (239) 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/doctor.ts +28 -3
  65. package/src/extension/team-tool/handle-settings.ts +195 -188
  66. package/src/extension/team-tool/inspect.ts +41 -41
  67. package/src/extension/team-tool/intent-policy.ts +42 -42
  68. package/src/extension/team-tool/lifecycle-actions.ts +27 -8
  69. package/src/extension/team-tool/plan.ts +19 -19
  70. package/src/extension/team-tool/run.ts +12 -1
  71. package/src/extension/team-tool.ts +11 -1
  72. package/src/i18n.ts +184 -184
  73. package/src/observability/exporters/otlp-exporter.ts +92 -77
  74. package/src/prompt/prompt-runtime.ts +72 -72
  75. package/src/runtime/agent-memory.ts +72 -72
  76. package/src/runtime/agent-observability.ts +114 -114
  77. package/src/runtime/async-marker.ts +26 -26
  78. package/src/runtime/attention-events.ts +28 -28
  79. package/src/runtime/auto-resume.ts +100 -0
  80. package/src/runtime/background-runner.ts +11 -1
  81. package/src/runtime/cancellation-token.ts +89 -89
  82. package/src/runtime/cancellation.ts +61 -61
  83. package/src/runtime/capability-inventory.ts +116 -116
  84. package/src/runtime/child-pi.ts +7 -2
  85. package/src/runtime/compaction-summary.ts +271 -0
  86. package/src/runtime/completion-guard.ts +190 -190
  87. package/src/runtime/crash-recovery.ts +33 -0
  88. package/src/runtime/delta-conflict.ts +360 -0
  89. package/src/runtime/direct-run.ts +35 -35
  90. package/src/runtime/foreground-control.ts +82 -82
  91. package/src/runtime/green-contract.ts +46 -46
  92. package/src/runtime/group-join.ts +106 -106
  93. package/src/runtime/heartbeat-gradient.ts +28 -28
  94. package/src/runtime/heartbeat-watcher.ts +124 -124
  95. package/src/runtime/iteration-hooks.ts +262 -0
  96. package/src/runtime/live-agent-control.ts +88 -88
  97. package/src/runtime/live-control-realtime.ts +36 -36
  98. package/src/runtime/live-extension-bridge.ts +150 -150
  99. package/src/runtime/live-irc.ts +92 -92
  100. package/src/runtime/live-session-health.ts +100 -100
  101. package/src/runtime/loop-gates.ts +129 -0
  102. package/src/runtime/metric-parser.ts +40 -0
  103. package/src/runtime/notebook-helpers.ts +90 -90
  104. package/src/runtime/orphan-sentinel.ts +7 -7
  105. package/src/runtime/parallel-research.ts +44 -44
  106. package/src/runtime/phase-progress.ts +217 -0
  107. package/src/runtime/pi-args.ts +38 -11
  108. package/src/runtime/pi-json-output.ts +111 -111
  109. package/src/runtime/pi-spawn.ts +57 -7
  110. package/src/runtime/policy-engine.ts +79 -79
  111. package/src/runtime/post-checks.ts +122 -0
  112. package/src/runtime/progress-event-coalescer.ts +43 -43
  113. package/src/runtime/prose-compressor.ts +164 -164
  114. package/src/runtime/recovery-recipes.ts +74 -74
  115. package/src/runtime/result-extractor.ts +121 -121
  116. package/src/runtime/role-permission.ts +39 -39
  117. package/src/runtime/sensitive-paths.ts +2 -2
  118. package/src/runtime/session-resources.ts +25 -25
  119. package/src/runtime/session-snapshot.ts +59 -59
  120. package/src/runtime/session-usage.ts +79 -79
  121. package/src/runtime/sidechain-output.ts +29 -29
  122. package/src/runtime/stream-preview.ts +177 -177
  123. package/src/runtime/supervisor-contact.ts +59 -59
  124. package/src/runtime/task-display.ts +38 -38
  125. package/src/runtime/task-graph.ts +207 -0
  126. package/src/runtime/task-quality.ts +207 -0
  127. package/src/runtime/task-runner/capabilities.ts +78 -78
  128. package/src/runtime/task-runner/live-executor.ts +7 -1
  129. package/src/runtime/task-runner/progress.ts +119 -119
  130. package/src/runtime/task-runner/prompt-pipeline.ts +64 -64
  131. package/src/runtime/task-runner/result-utils.ts +14 -14
  132. package/src/runtime/task-runner/run-projection.ts +103 -103
  133. package/src/runtime/task-runner/state-helpers.ts +22 -22
  134. package/src/runtime/team-runner.ts +117 -7
  135. package/src/runtime/worker-heartbeat.ts +21 -21
  136. package/src/runtime/worker-startup.ts +57 -57
  137. package/src/runtime/workflow-state.ts +187 -0
  138. package/src/runtime/workspace-tree.ts +298 -298
  139. package/src/schema/config-schema.ts +11 -0
  140. package/src/schema/validation-types.ts +148 -0
  141. package/src/skills/skill-templates.ts +374 -0
  142. package/src/state/active-run-registry.ts +35 -11
  143. package/src/state/atomic-write.ts +33 -26
  144. package/src/state/contracts.ts +1 -0
  145. package/src/state/event-reconstructor.ts +217 -0
  146. package/src/state/locks.ts +2 -13
  147. package/src/state/mailbox.ts +4 -3
  148. package/src/state/state-store.ts +32 -14
  149. package/src/state/task-claims.ts +44 -44
  150. package/src/state/types.ts +9 -0
  151. package/src/state/usage.ts +29 -29
  152. package/src/subagents/async-entry.ts +1 -1
  153. package/src/subagents/index.ts +3 -3
  154. package/src/subagents/live/control.ts +1 -1
  155. package/src/subagents/live/manager.ts +1 -1
  156. package/src/subagents/live/realtime.ts +1 -1
  157. package/src/subagents/live/session-runtime.ts +1 -1
  158. package/src/subagents/manager.ts +1 -1
  159. package/src/subagents/spawn.ts +1 -1
  160. package/src/teams/team-serializer.ts +38 -38
  161. package/src/types/diff.d.ts +18 -18
  162. package/src/ui/crew-footer.ts +101 -101
  163. package/src/ui/crew-select-list.ts +111 -111
  164. package/src/ui/crew-widget.ts +5 -2
  165. package/src/ui/dashboard-panes/cancellation-pane.ts +42 -42
  166. package/src/ui/dashboard-panes/capability-pane.ts +59 -59
  167. package/src/ui/dashboard-panes/mailbox-pane.ts +35 -35
  168. package/src/ui/dashboard-panes/metrics-pane.ts +34 -34
  169. package/src/ui/dashboard-panes/progress-pane.ts +11 -0
  170. package/src/ui/dynamic-border.ts +25 -25
  171. package/src/ui/layout-primitives.ts +106 -106
  172. package/src/ui/loaders.ts +158 -158
  173. package/src/ui/render-coalescer.ts +51 -51
  174. package/src/ui/render-diff.ts +119 -119
  175. package/src/ui/render-scheduler.ts +143 -143
  176. package/src/ui/run-action-dispatcher.ts +10 -1
  177. package/src/ui/spinner.ts +17 -17
  178. package/src/ui/status-colors.ts +58 -58
  179. package/src/ui/syntax-highlight.ts +116 -116
  180. package/src/ui/transcript-entries.ts +258 -258
  181. package/src/utils/completion-dedupe.ts +63 -63
  182. package/src/utils/frontmatter.ts +68 -68
  183. package/src/utils/git.ts +262 -262
  184. package/src/utils/ids.ts +17 -17
  185. package/src/utils/incremental-reader.ts +104 -104
  186. package/src/utils/names.ts +27 -27
  187. package/src/utils/redaction.ts +44 -44
  188. package/src/utils/safe-paths.ts +47 -47
  189. package/src/utils/scan-cache.ts +136 -136
  190. package/src/utils/sleep.ts +40 -26
  191. package/src/utils/task-name-generator.ts +337 -337
  192. package/src/workflows/validate-workflow.ts +40 -40
  193. package/src/worktree/branch-freshness.ts +45 -45
  194. package/teams/default.team.md +12 -12
  195. package/teams/fast-fix.team.md +11 -11
  196. package/teams/implementation.team.md +18 -18
  197. package/teams/parallel-research.team.md +14 -14
  198. package/teams/research.team.md +11 -11
  199. package/teams/review.team.md +12 -12
  200. package/workflows/default.workflow.md +30 -29
  201. package/workflows/fast-fix.workflow.md +23 -22
  202. package/workflows/implementation.workflow.md +43 -43
  203. package/workflows/parallel-research.workflow.md +46 -46
  204. package/workflows/research.workflow.md +22 -22
  205. package/workflows/review.workflow.md +30 -30
  206. package/docs/refactor-tasks-phase3.md +0 -394
  207. package/docs/refactor-tasks-phase4.md +0 -564
  208. package/docs/refactor-tasks-phase5.md +0 -402
  209. package/docs/refactor-tasks-phase6.md +0 -662
  210. package/docs/refactor-tasks.md +0 -1484
  211. package/docs/research/AGENT-EXECUTION-ARCHITECTURE.md +0 -261
  212. package/docs/research/AGENT-LIFECYCLE-COMPARISON.md +0 -111
  213. package/docs/research/AUDIT_OH_MY_PI.md +0 -261
  214. package/docs/research/AUDIT_PI_CREW.md +0 -457
  215. package/docs/research/CAVEMAN-DEEP-RESEARCH.md +0 -281
  216. package/docs/research/COMPARISON_OH_MY_PI_VS_PI_CREW.md +0 -264
  217. package/docs/research/DEEP-RESEARCH-PI-POWERBAR.md +0 -343
  218. package/docs/research/DEEP_RESEARCH_SUBAGENT_ARCHITECTURE.md +0 -480
  219. package/docs/research/GAP_CLOSURE_IMPLEMENTATION_PLAN.md +0 -354
  220. package/docs/research/IMPLEMENTATION_PLAN.md +0 -385
  221. package/docs/research/LIVE-SESSION-PRODUCTION-READY-PLAN.md +0 -502
  222. package/docs/research/OH-MY-PI-DEEP-RESEARCH-v14.7.6.md +0 -266
  223. package/docs/research/REMAINING-GAPS-PLAN.md +0 -363
  224. package/docs/research/SESSION-SUMMARY-2026-05-08.md +0 -146
  225. package/docs/research/UI-RESPONSIVENESS-AUDIT.md +0 -173
  226. package/docs/research-awesome-agent-skills-distillation.md +0 -100
  227. package/docs/research-extension-examples.md +0 -297
  228. package/docs/research-extension-system.md +0 -324
  229. package/docs/research-oh-my-pi-distillation.md +0 -369
  230. package/docs/research-optimization-plan.md +0 -548
  231. package/docs/research-phase10-distillation.md +0 -199
  232. package/docs/research-phase11-distillation.md +0 -201
  233. package/docs/research-phase8-operator-experience-plan.md +0 -819
  234. package/docs/research-phase9-observability-reliability-plan.md +0 -1190
  235. package/docs/research-pi-coding-agent.md +0 -357
  236. package/docs/research-source-pi-crew-reference.md +0 -174
  237. package/docs/research-ui-optimization-plan.md +0 -480
  238. package/docs/source-runtime-refactor-map.md +0 -107
  239. package/src/utils/atomic-write.ts +0 -33
@@ -1,143 +1,143 @@
1
- import { logInternalError } from "../utils/internal-error.ts";
2
-
3
- export interface RenderSchedulerEventBus {
4
- on?: (event: string, handler: (payload: unknown) => void) => (() => void) | void;
5
- }
6
-
7
- export interface RenderSchedulerOptions {
8
- debounceMs?: number;
9
- fallbackMs?: number;
10
- events?: string[];
11
- onInvalidate?: (payload: unknown) => void;
12
- }
13
-
14
- const DEFAULT_EVENTS = [
15
- "crew.run.created",
16
- "crew.run.completed",
17
- "crew.run.failed",
18
- "crew.run.cancelled",
19
- "crew.subagent.completed",
20
- "crew.subagent.failed",
21
- "crew.mailbox.updated",
22
- "crew.mailbox.message",
23
- ];
24
-
25
- /**
26
- * Coordinates UI renders with debounce + fallback polling.
27
- *
28
- * Critical: uses recursive setTimeout instead of setInterval + a rendering
29
- * guard (`rendering` / `pendingRender`) so that when render() takes longer
30
- * than the fallback interval, callbacks do NOT pile up and storm the event
31
- * loop. Instead, overlapping schedules are collapsed into a single deferred
32
- * re-render.
33
- */
34
- export class RenderScheduler {
35
- private readonly render: () => void;
36
- private readonly onInvalidate?: (payload: unknown) => void;
37
- private readonly debounceMs: number;
38
- private readonly fallbackMs: number;
39
- private debounceTimer: ReturnType<typeof setTimeout> | undefined;
40
- private fallbackTimer: ReturnType<typeof setTimeout> | undefined;
41
- private disposed = false;
42
- private lastEventAt = 0;
43
- private rendering = false;
44
- private pendingRender = false;
45
- private readonly unsubs: Array<() => void> = [];
46
-
47
- constructor(events: RenderSchedulerEventBus | undefined, render: () => void, options: RenderSchedulerOptions = {}) {
48
- this.render = render;
49
- this.onInvalidate = options.onInvalidate;
50
- this.debounceMs = options.debounceMs ?? 75;
51
- this.fallbackMs = options.fallbackMs ?? 750;
52
- for (const event of options.events ?? DEFAULT_EVENTS) this.subscribe(events, event);
53
- this.fallbackTimer = setTimeout(() => this.fallbackLoop(), this.fallbackMs);
54
- this.fallbackTimer.unref();
55
- }
56
-
57
- private subscribe(events: RenderSchedulerEventBus | undefined, event: string): void {
58
- if (!events?.on) return;
59
- const handler = (payload: unknown): void => this.schedule(payload);
60
- try {
61
- const unsub = events.on(event, handler);
62
- if (typeof unsub === "function") this.unsubs.push(unsub);
63
- } catch (error) {
64
- logInternalError("render-scheduler.subscribe", error, event);
65
- }
66
- }
67
-
68
- /** Recursive setTimeout — avoids setInterval timer storms. */
69
- private fallbackLoop(): void {
70
- if (this.disposed) return;
71
- if (Date.now() - this.lastEventAt < this.fallbackMs) {
72
- if (this.disposed) return;
73
- this.fallbackTimer = setTimeout(() => this.fallbackLoop(), this.fallbackMs);
74
- this.fallbackTimer.unref();
75
- return;
76
- }
77
- this.schedule();
78
- if (this.disposed) return;
79
- this.fallbackTimer = setTimeout(() => this.fallbackLoop(), this.fallbackMs);
80
- this.fallbackTimer.unref();
81
- }
82
-
83
- schedule(payload?: unknown): void {
84
- if (this.disposed) return;
85
- this.lastEventAt = Date.now();
86
- try {
87
- this.onInvalidate?.(payload);
88
- } catch (error) {
89
- logInternalError("render-scheduler.invalidate", error);
90
- }
91
- if (this.debounceTimer) clearTimeout(this.debounceTimer);
92
- this.debounceTimer = setTimeout(() => {
93
- this.debounceTimer = undefined;
94
- this.flush();
95
- }, this.debounceMs);
96
- this.debounceTimer.unref();
97
- }
98
-
99
- /**
100
- * Flush a render. If a render is already in progress the request is
101
- * collapsed: `pendingRender` is set and the caller that holds
102
- * `rendering==true` will loop one more time after finishing.
103
- */
104
- flush(): void {
105
- if (this.disposed) return;
106
- if (this.rendering) {
107
- this.pendingRender = true;
108
- return;
109
- }
110
- this.rendering = true;
111
- this.pendingRender = false;
112
- let iterations = 0;
113
- try {
114
- do {
115
- this.pendingRender = false;
116
- this.render();
117
- iterations += 1;
118
- // Safety valve: 5 re-renders max per flush to prevent infinite loops
119
- // if render() itself calls flush() synchronously.
120
- } while (this.pendingRender && !this.disposed && iterations < 5);
121
- } catch (error) {
122
- logInternalError("render-scheduler.render", error);
123
- } finally {
124
- this.rendering = false;
125
- // If we hit the iteration cap, schedule one more render to drain.
126
- if (iterations >= 5 && this.pendingRender && !this.disposed) {
127
- this.schedule();
128
- }
129
- }
130
- }
131
-
132
- dispose(): void {
133
- if (this.disposed) return;
134
- this.disposed = true;
135
- if (this.debounceTimer) clearTimeout(this.debounceTimer);
136
- if (this.fallbackTimer) clearTimeout(this.fallbackTimer);
137
- this.debounceTimer = undefined;
138
- this.fallbackTimer = undefined;
139
- for (const unsub of this.unsubs.splice(0)) {
140
- try { unsub(); } catch (error) { logInternalError("render-scheduler.unsubscribe", error); }
141
- }
142
- }
143
- }
1
+ import { logInternalError } from "../utils/internal-error.ts";
2
+
3
+ export interface RenderSchedulerEventBus {
4
+ on?: (event: string, handler: (payload: unknown) => void) => (() => void) | void;
5
+ }
6
+
7
+ export interface RenderSchedulerOptions {
8
+ debounceMs?: number;
9
+ fallbackMs?: number;
10
+ events?: string[];
11
+ onInvalidate?: (payload: unknown) => void;
12
+ }
13
+
14
+ const DEFAULT_EVENTS = [
15
+ "crew.run.created",
16
+ "crew.run.completed",
17
+ "crew.run.failed",
18
+ "crew.run.cancelled",
19
+ "crew.subagent.completed",
20
+ "crew.subagent.failed",
21
+ "crew.mailbox.updated",
22
+ "crew.mailbox.message",
23
+ ];
24
+
25
+ /**
26
+ * Coordinates UI renders with debounce + fallback polling.
27
+ *
28
+ * Critical: uses recursive setTimeout instead of setInterval + a rendering
29
+ * guard (`rendering` / `pendingRender`) so that when render() takes longer
30
+ * than the fallback interval, callbacks do NOT pile up and storm the event
31
+ * loop. Instead, overlapping schedules are collapsed into a single deferred
32
+ * re-render.
33
+ */
34
+ export class RenderScheduler {
35
+ private readonly render: () => void;
36
+ private readonly onInvalidate?: (payload: unknown) => void;
37
+ private readonly debounceMs: number;
38
+ private readonly fallbackMs: number;
39
+ private debounceTimer: ReturnType<typeof setTimeout> | undefined;
40
+ private fallbackTimer: ReturnType<typeof setTimeout> | undefined;
41
+ private disposed = false;
42
+ private lastEventAt = 0;
43
+ private rendering = false;
44
+ private pendingRender = false;
45
+ private readonly unsubs: Array<() => void> = [];
46
+
47
+ constructor(events: RenderSchedulerEventBus | undefined, render: () => void, options: RenderSchedulerOptions = {}) {
48
+ this.render = render;
49
+ this.onInvalidate = options.onInvalidate;
50
+ this.debounceMs = options.debounceMs ?? 75;
51
+ this.fallbackMs = options.fallbackMs ?? 750;
52
+ for (const event of options.events ?? DEFAULT_EVENTS) this.subscribe(events, event);
53
+ this.fallbackTimer = setTimeout(() => this.fallbackLoop(), this.fallbackMs);
54
+ this.fallbackTimer.unref();
55
+ }
56
+
57
+ private subscribe(events: RenderSchedulerEventBus | undefined, event: string): void {
58
+ if (!events?.on) return;
59
+ const handler = (payload: unknown): void => this.schedule(payload);
60
+ try {
61
+ const unsub = events.on(event, handler);
62
+ if (typeof unsub === "function") this.unsubs.push(unsub);
63
+ } catch (error) {
64
+ logInternalError("render-scheduler.subscribe", error, event);
65
+ }
66
+ }
67
+
68
+ /** Recursive setTimeout — avoids setInterval timer storms. */
69
+ private fallbackLoop(): void {
70
+ if (this.disposed) return;
71
+ if (Date.now() - this.lastEventAt < this.fallbackMs) {
72
+ if (this.disposed) return;
73
+ this.fallbackTimer = setTimeout(() => this.fallbackLoop(), this.fallbackMs);
74
+ this.fallbackTimer.unref();
75
+ return;
76
+ }
77
+ this.schedule();
78
+ if (this.disposed) return;
79
+ this.fallbackTimer = setTimeout(() => this.fallbackLoop(), this.fallbackMs);
80
+ this.fallbackTimer.unref();
81
+ }
82
+
83
+ schedule(payload?: unknown): void {
84
+ if (this.disposed) return;
85
+ this.lastEventAt = Date.now();
86
+ try {
87
+ this.onInvalidate?.(payload);
88
+ } catch (error) {
89
+ logInternalError("render-scheduler.invalidate", error);
90
+ }
91
+ if (this.debounceTimer) clearTimeout(this.debounceTimer);
92
+ this.debounceTimer = setTimeout(() => {
93
+ this.debounceTimer = undefined;
94
+ this.flush();
95
+ }, this.debounceMs);
96
+ this.debounceTimer.unref();
97
+ }
98
+
99
+ /**
100
+ * Flush a render. If a render is already in progress the request is
101
+ * collapsed: `pendingRender` is set and the caller that holds
102
+ * `rendering==true` will loop one more time after finishing.
103
+ */
104
+ flush(): void {
105
+ if (this.disposed) return;
106
+ if (this.rendering) {
107
+ this.pendingRender = true;
108
+ return;
109
+ }
110
+ this.rendering = true;
111
+ this.pendingRender = false;
112
+ let iterations = 0;
113
+ try {
114
+ do {
115
+ this.pendingRender = false;
116
+ this.render();
117
+ iterations += 1;
118
+ // Safety valve: 5 re-renders max per flush to prevent infinite loops
119
+ // if render() itself calls flush() synchronously.
120
+ } while (this.pendingRender && !this.disposed && iterations < 5);
121
+ } catch (error) {
122
+ logInternalError("render-scheduler.render", error);
123
+ } finally {
124
+ this.rendering = false;
125
+ // If we hit the iteration cap, schedule one more render to drain.
126
+ if (iterations >= 5 && this.pendingRender && !this.disposed) {
127
+ this.schedule();
128
+ }
129
+ }
130
+ }
131
+
132
+ dispose(): void {
133
+ if (this.disposed) return;
134
+ this.disposed = true;
135
+ if (this.debounceTimer) clearTimeout(this.debounceTimer);
136
+ if (this.fallbackTimer) clearTimeout(this.fallbackTimer);
137
+ this.debounceTimer = undefined;
138
+ this.fallbackTimer = undefined;
139
+ for (const unsub of this.unsubs.splice(0)) {
140
+ try { unsub(); } catch (error) { logInternalError("render-scheduler.unsubscribe", error); }
141
+ }
142
+ }
143
+ }
@@ -1,6 +1,15 @@
1
1
  import type { ExtensionContext } from "@mariozechner/pi-coding-agent";
2
2
  import type { MetricRegistry } from "../observability/metric-registry.ts";
3
- import { handleTeamTool } from "../extension/team-tool.ts";
3
+ // Lazy-loaded: team-tool.ts pulls in entire runtime chain.
4
+ import type { handleTeamTool as HandleTeamToolFn } from "../extension/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("../extension/team-tool.ts");
9
+ _cachedHandleTeamTool = mod.handleTeamTool;
10
+ }
11
+ return _cachedHandleTeamTool(params, ctx);
12
+ }
4
13
  import { isToolError, textFromToolResult } from "../extension/tool-result.ts";
5
14
  import { loadRunManifestById, saveRunTasks } from "../state/state-store.ts";
6
15
  import { appendEvent } from "../state/event-log.ts";
package/src/ui/spinner.ts CHANGED
@@ -1,17 +1,17 @@
1
- export const SUBAGENT_SPINNER_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"] as const;
2
- export const SUBAGENT_SPINNER_FRAME_MS = 160;
3
-
4
- export function spinnerBucket(now = Date.now(), frameMs = SUBAGENT_SPINNER_FRAME_MS): number {
5
- return Math.floor(now / Math.max(1, frameMs));
6
- }
7
-
8
- function hashKey(key: string): number {
9
- let hash = 0;
10
- for (let index = 0; index < key.length; index += 1) hash = (hash * 31 + key.charCodeAt(index)) >>> 0;
11
- return hash;
12
- }
13
-
14
- export function spinnerFrame(key = "", now = Date.now()): string {
15
- const offset = key ? hashKey(key) % SUBAGENT_SPINNER_FRAMES.length : 0;
16
- return SUBAGENT_SPINNER_FRAMES[(spinnerBucket(now) + offset) % SUBAGENT_SPINNER_FRAMES.length] ?? SUBAGENT_SPINNER_FRAMES[0];
17
- }
1
+ export const SUBAGENT_SPINNER_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"] as const;
2
+ export const SUBAGENT_SPINNER_FRAME_MS = 160;
3
+
4
+ export function spinnerBucket(now = Date.now(), frameMs = SUBAGENT_SPINNER_FRAME_MS): number {
5
+ return Math.floor(now / Math.max(1, frameMs));
6
+ }
7
+
8
+ function hashKey(key: string): number {
9
+ let hash = 0;
10
+ for (let index = 0; index < key.length; index += 1) hash = (hash * 31 + key.charCodeAt(index)) >>> 0;
11
+ return hash;
12
+ }
13
+
14
+ export function spinnerFrame(key = "", now = Date.now()): string {
15
+ const offset = key ? hashKey(key) % SUBAGENT_SPINNER_FRAMES.length : 0;
16
+ return SUBAGENT_SPINNER_FRAMES[(spinnerBucket(now) + offset) % SUBAGENT_SPINNER_FRAMES.length] ?? SUBAGENT_SPINNER_FRAMES[0];
17
+ }
@@ -1,58 +1,58 @@
1
- import type { CrewTheme, CrewThemeColor } from "./theme-adapter.ts";
2
-
3
- export type RunStatus = "queued" | "running" | "completed" | "failed" | "cancelled" | "stopped" | "blocked" | (string & {});
4
-
5
- export function colorForStatus(status: RunStatus): CrewThemeColor {
6
- switch (status) {
7
- case "running":
8
- return "accent";
9
- case "waiting":
10
- return "muted";
11
- case "completed":
12
- return "success";
13
- case "failed":
14
- case "stale":
15
- return "error";
16
- case "cancelled":
17
- case "blocked":
18
- case "stopped":
19
- return "warning";
20
- case "queued":
21
- default:
22
- return "dim";
23
- }
24
- }
25
-
26
- export function iconForStatus(status: RunStatus, options?: { runningGlyph?: string }): string {
27
- const glyph = options?.runningGlyph ?? "▶";
28
- switch (status) {
29
- case "completed":
30
- return "✓";
31
- case "failed":
32
- case "stale":
33
- return "✗";
34
- case "cancelled":
35
- case "stopped":
36
- return "■";
37
- case "running":
38
- return glyph;
39
- case "waiting":
40
- return "⏳";
41
- case "queued":
42
- return "◦";
43
- case "blocked":
44
- return "⏸";
45
- default:
46
- return "·";
47
- }
48
- }
49
-
50
- export function colorForActivity(activityState: string | undefined): CrewThemeColor {
51
- if (activityState === "needs_attention") return "warning";
52
- if (activityState === "stale") return "error";
53
- return "dim";
54
- }
55
-
56
- export function applyStatusColor(theme: CrewTheme, status: RunStatus, text: string): string {
57
- return theme.fg(colorForStatus(status), text);
58
- }
1
+ import type { CrewTheme, CrewThemeColor } from "./theme-adapter.ts";
2
+
3
+ export type RunStatus = "queued" | "running" | "completed" | "failed" | "cancelled" | "stopped" | "blocked" | (string & {});
4
+
5
+ export function colorForStatus(status: RunStatus): CrewThemeColor {
6
+ switch (status) {
7
+ case "running":
8
+ return "accent";
9
+ case "waiting":
10
+ return "muted";
11
+ case "completed":
12
+ return "success";
13
+ case "failed":
14
+ case "stale":
15
+ return "error";
16
+ case "cancelled":
17
+ case "blocked":
18
+ case "stopped":
19
+ return "warning";
20
+ case "queued":
21
+ default:
22
+ return "dim";
23
+ }
24
+ }
25
+
26
+ export function iconForStatus(status: RunStatus, options?: { runningGlyph?: string }): string {
27
+ const glyph = options?.runningGlyph ?? "▶";
28
+ switch (status) {
29
+ case "completed":
30
+ return "✓";
31
+ case "failed":
32
+ case "stale":
33
+ return "✗";
34
+ case "cancelled":
35
+ case "stopped":
36
+ return "■";
37
+ case "running":
38
+ return glyph;
39
+ case "waiting":
40
+ return "⏳";
41
+ case "queued":
42
+ return "◦";
43
+ case "blocked":
44
+ return "⏸";
45
+ default:
46
+ return "·";
47
+ }
48
+ }
49
+
50
+ export function colorForActivity(activityState: string | undefined): CrewThemeColor {
51
+ if (activityState === "needs_attention") return "warning";
52
+ if (activityState === "stale") return "error";
53
+ return "dim";
54
+ }
55
+
56
+ export function applyStatusColor(theme: CrewTheme, status: RunStatus, text: string): string {
57
+ return theme.fg(colorForStatus(status), text);
58
+ }