pi-crew 0.9.53 → 0.9.55

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 (493) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/package.json +6 -3
  3. package/scripts/clean-strip-types.mjs +60 -0
  4. package/src/adapters/claude-adapter.ts +23 -0
  5. package/src/adapters/codex-adapter.ts +21 -0
  6. package/src/adapters/cursor-adapter.ts +17 -0
  7. package/src/adapters/export-util.ts +143 -0
  8. package/src/adapters/index.ts +15 -0
  9. package/src/adapters/registry.ts +18 -0
  10. package/src/adapters/types.ts +23 -0
  11. package/src/agents/agent-config.ts +194 -0
  12. package/src/agents/agent-search.ts +98 -0
  13. package/src/agents/agent-serializer.ts +38 -0
  14. package/src/agents/discover-agents.ts +623 -0
  15. package/src/benchmark/benchmark-runner.ts +313 -0
  16. package/src/benchmark/feedback-loop.ts +73 -0
  17. package/src/config/config.ts +1270 -0
  18. package/src/config/defaults.ts +193 -0
  19. package/src/config/drift-detector.ts +290 -0
  20. package/src/config/markers.ts +330 -0
  21. package/src/config/resilient-parser.ts +117 -0
  22. package/src/config/role-tools.ts +118 -0
  23. package/src/config/suggestions.ts +75 -0
  24. package/src/config/types.ts +280 -0
  25. package/src/errors.ts +194 -0
  26. package/src/extension/action-suggestions.ts +117 -0
  27. package/src/extension/async-notifier.ts +179 -0
  28. package/src/extension/autonomous-policy.ts +209 -0
  29. package/src/extension/command-completions.ts +120 -0
  30. package/src/extension/context-status-injection.ts +185 -0
  31. package/src/extension/crew-autocomplete.ts +131 -0
  32. package/src/extension/crew-cleanup.ts +168 -0
  33. package/src/extension/crew-input-router.ts +93 -0
  34. package/src/extension/crew-shortcuts.ts +87 -0
  35. package/src/extension/crew-vibes/cat-frames.ts +18 -0
  36. package/src/extension/crew-vibes/config.ts +195 -0
  37. package/src/extension/crew-vibes/figures.ts +101 -0
  38. package/src/extension/crew-vibes/font-detect.ts +71 -0
  39. package/src/extension/crew-vibes/footer.ts +292 -0
  40. package/src/extension/crew-vibes/index.ts +432 -0
  41. package/src/extension/crew-vibes/provider-usage.ts +396 -0
  42. package/src/extension/crew-vibes/render.ts +206 -0
  43. package/src/extension/crew-vibes/speed.ts +286 -0
  44. package/src/extension/cross-extension-rpc.ts +350 -0
  45. package/src/extension/help.ts +62 -0
  46. package/src/extension/import-index.ts +74 -0
  47. package/src/extension/knowledge-injection.ts +402 -0
  48. package/src/extension/management.ts +571 -0
  49. package/src/extension/message-renderers.ts +108 -0
  50. package/src/extension/notification-router.ts +152 -0
  51. package/src/extension/notification-sink.ts +54 -0
  52. package/src/extension/pi-api.ts +56 -0
  53. package/src/extension/plan-orchestrate.ts +302 -0
  54. package/src/extension/project-init.ts +170 -0
  55. package/src/extension/register.ts +113 -0
  56. package/src/extension/registration/artifact-cleanup.ts +20 -0
  57. package/src/extension/registration/command-registration.ts +58 -0
  58. package/src/extension/registration/command-utils.ts +58 -0
  59. package/src/extension/registration/commands.ts +1228 -0
  60. package/src/extension/registration/compaction-guard.ts +333 -0
  61. package/src/extension/registration/context-builder.ts +135 -0
  62. package/src/extension/registration/crash-recovery-cache.ts +54 -0
  63. package/src/extension/registration/foreground-run-controller.ts +238 -0
  64. package/src/extension/registration/hook-registration.ts +119 -0
  65. package/src/extension/registration/lazy-configurers.ts +124 -0
  66. package/src/extension/registration/lifecycle-handlers.ts +923 -0
  67. package/src/extension/registration/lifecycle.ts +259 -0
  68. package/src/extension/registration/observability.ts +322 -0
  69. package/src/extension/registration/registration-types.ts +158 -0
  70. package/src/extension/registration/runtime-cleanup.ts +230 -0
  71. package/src/extension/registration/subagent-helpers.ts +125 -0
  72. package/src/extension/registration/subagent-manager-setup.ts +330 -0
  73. package/src/extension/registration/subagent-tools.ts +456 -0
  74. package/src/extension/registration/team-tool.ts +225 -0
  75. package/src/extension/registration/tool-registration.ts +50 -0
  76. package/src/extension/registration/ui.ts +172 -0
  77. package/src/extension/registration/viewers.ts +121 -0
  78. package/src/extension/registration/wire-cross-extension.ts +37 -0
  79. package/src/extension/result-watcher.ts +139 -0
  80. package/src/extension/rpc-hmac.ts +256 -0
  81. package/src/extension/run-bundle-schema.ts +104 -0
  82. package/src/extension/run-export.ts +97 -0
  83. package/src/extension/run-import.ts +168 -0
  84. package/src/extension/run-index.ts +131 -0
  85. package/src/extension/run-maintenance.ts +190 -0
  86. package/src/extension/session-summary.ts +18 -0
  87. package/src/extension/team-manager-command.ts +153 -0
  88. package/src/extension/team-onboard.ts +180 -0
  89. package/src/extension/team-recommendation.ts +288 -0
  90. package/src/extension/team-tool/anchor.ts +172 -0
  91. package/src/extension/team-tool/api.ts +1244 -0
  92. package/src/extension/team-tool/auto-summarize.ts +142 -0
  93. package/src/extension/team-tool/cache-control.ts +19 -0
  94. package/src/extension/team-tool/cancel.ts +337 -0
  95. package/src/extension/team-tool/chain-dispatch.ts +95 -0
  96. package/src/extension/team-tool/chain-executor.ts +379 -0
  97. package/src/extension/team-tool/config-patch.ts +50 -0
  98. package/src/extension/team-tool/context.ts +149 -0
  99. package/src/extension/team-tool/destructive-gate.ts +52 -0
  100. package/src/extension/team-tool/dispatch/automate.ts +80 -0
  101. package/src/extension/team-tool/dispatch/control.ts +42 -0
  102. package/src/extension/team-tool/dispatch/index.ts +95 -0
  103. package/src/extension/team-tool/dispatch/manage.ts +161 -0
  104. package/src/extension/team-tool/dispatch/run.ts +53 -0
  105. package/src/extension/team-tool/dispatch/status.ts +187 -0
  106. package/src/extension/team-tool/doctor.ts +431 -0
  107. package/src/extension/team-tool/explain.ts +282 -0
  108. package/src/extension/team-tool/failure-patterns.ts +118 -0
  109. package/src/extension/team-tool/goal-wrap.ts +330 -0
  110. package/src/extension/team-tool/goal.ts +556 -0
  111. package/src/extension/team-tool/handle-schedule.ts +332 -0
  112. package/src/extension/team-tool/handle-settings.ts +520 -0
  113. package/src/extension/team-tool/health-monitor.ts +481 -0
  114. package/src/extension/team-tool/inspect.ts +88 -0
  115. package/src/extension/team-tool/intent-policy.ts +45 -0
  116. package/src/extension/team-tool/lifecycle-actions.ts +615 -0
  117. package/src/extension/team-tool/orchestrate.ts +98 -0
  118. package/src/extension/team-tool/parallel-dispatch.ts +183 -0
  119. package/src/extension/team-tool/plan.ts +50 -0
  120. package/src/extension/team-tool/respond.ts +150 -0
  121. package/src/extension/team-tool/run-deadline.ts +66 -0
  122. package/src/extension/team-tool/run-not-found.ts +49 -0
  123. package/src/extension/team-tool/run.ts +1051 -0
  124. package/src/extension/team-tool/status.ts +260 -0
  125. package/src/extension/team-tool/workflow-manage.ts +261 -0
  126. package/src/extension/team-tool-types.ts +26 -0
  127. package/src/extension/team-tool.ts +750 -0
  128. package/src/extension/tool-result.ts +16 -0
  129. package/src/extension/validate-resources.ts +108 -0
  130. package/src/hooks/registry.ts +200 -0
  131. package/src/hooks/types.ts +69 -0
  132. package/src/i18n.ts +212 -0
  133. package/src/observability/correlation.ts +50 -0
  134. package/src/observability/event-bus.ts +86 -0
  135. package/src/observability/event-to-metric.ts +170 -0
  136. package/src/observability/exporters/adapter.ts +27 -0
  137. package/src/observability/exporters/otlp-exporter.ts +356 -0
  138. package/src/observability/exporters/prometheus-exporter.ts +54 -0
  139. package/src/observability/metric-registry.ts +100 -0
  140. package/src/observability/metric-retention.ts +64 -0
  141. package/src/observability/metric-sink.ts +99 -0
  142. package/src/observability/metrics-primitives.ts +219 -0
  143. package/src/plugins/plugin-define.ts +6 -0
  144. package/src/plugins/plugin-registry.ts +32 -0
  145. package/src/plugins/plugins/index.ts +3 -0
  146. package/src/plugins/plugins/nextjs.ts +19 -0
  147. package/src/plugins/plugins/vite.ts +10 -0
  148. package/src/plugins/plugins/vitest.ts +9 -0
  149. package/src/prompt/prompt-runtime.ts +380 -0
  150. package/src/runtime/adaptive-plan.ts +563 -0
  151. package/src/runtime/agent-control.ts +215 -0
  152. package/src/runtime/agent-memory.ts +81 -0
  153. package/src/runtime/agent-observability.ts +122 -0
  154. package/src/runtime/anchor-manager.ts +475 -0
  155. package/src/runtime/async-marker.ts +32 -0
  156. package/src/runtime/async-runner.ts +381 -0
  157. package/src/runtime/attention-events.ts +28 -0
  158. package/src/runtime/auto-summarize.ts +344 -0
  159. package/src/runtime/background-runner.ts +840 -0
  160. package/src/runtime/batch-barrier.ts +147 -0
  161. package/src/runtime/broker-issuer.ts +39 -0
  162. package/src/runtime/cancellation-token.ts +99 -0
  163. package/src/runtime/cancellation.ts +99 -0
  164. package/src/runtime/capability-inventory.ts +137 -0
  165. package/src/runtime/chain-parser.ts +237 -0
  166. package/src/runtime/chain-runner.ts +606 -0
  167. package/src/runtime/checkpoint.ts +291 -0
  168. package/src/runtime/child-pi-constants.ts +42 -0
  169. package/src/runtime/child-pi-kill.ts +180 -0
  170. package/src/runtime/child-pi-pool.ts +68 -0
  171. package/src/runtime/child-pi-spawn.ts +287 -0
  172. package/src/runtime/child-pi-steering.ts +128 -0
  173. package/src/runtime/child-pi-streams.ts +296 -0
  174. package/src/runtime/child-pi-transcript.ts +169 -0
  175. package/src/runtime/child-pi.ts +1105 -0
  176. package/src/runtime/coalesce-tasks.ts +268 -0
  177. package/src/runtime/code-summary.ts +292 -0
  178. package/src/runtime/command-trace.ts +105 -0
  179. package/src/runtime/compact-pipeline.ts +56 -0
  180. package/src/runtime/compact-stages/ansi-strip-stage.ts +25 -0
  181. package/src/runtime/compact-stages/blank-collapse-stage.ts +31 -0
  182. package/src/runtime/compact-stages/deduplicate-stage.ts +34 -0
  183. package/src/runtime/compact-stages/head-snap-stage.ts +57 -0
  184. package/src/runtime/compact-stages/index.ts +23 -0
  185. package/src/runtime/compact-stages/tail-capture-stage.ts +77 -0
  186. package/src/runtime/compact-stages/truncation-stage.ts +74 -0
  187. package/src/runtime/compaction-summary.ts +278 -0
  188. package/src/runtime/completion-guard.ts +207 -0
  189. package/src/runtime/concurrency.ts +58 -0
  190. package/src/runtime/crash-classification.ts +229 -0
  191. package/src/runtime/crash-recovery.ts +594 -0
  192. package/src/runtime/crew-agent-records.ts +595 -0
  193. package/src/runtime/crew-agent-runtime.ts +62 -0
  194. package/src/runtime/crew-broker-child.ts +88 -0
  195. package/src/runtime/crew-broker-client.ts +673 -0
  196. package/src/runtime/crew-broker-tokens.ts +159 -0
  197. package/src/runtime/crew-broker.ts +1304 -0
  198. package/src/runtime/crew-hooks.ts +227 -0
  199. package/src/runtime/cross-extension-rpc.ts +143 -0
  200. package/src/runtime/custom-tools/irc-tool.ts +282 -0
  201. package/src/runtime/custom-tools/submit-result-tool.ts +102 -0
  202. package/src/runtime/deadletter.ts +47 -0
  203. package/src/runtime/delivery-coordinator.ts +211 -0
  204. package/src/runtime/delta-conflict.ts +348 -0
  205. package/src/runtime/deterministic-ast.ts +161 -0
  206. package/src/runtime/diagnostic-export.ts +171 -0
  207. package/src/runtime/direct-run.ts +45 -0
  208. package/src/runtime/dwf-state-store.ts +102 -0
  209. package/src/runtime/dynamic-workflow-context.ts +1013 -0
  210. package/src/runtime/dynamic-workflow-runner.ts +325 -0
  211. package/src/runtime/effectiveness.ts +90 -0
  212. package/src/runtime/errors/crew-errors.ts +162 -0
  213. package/src/runtime/event-stream-bridge.ts +98 -0
  214. package/src/runtime/foreground-control.ts +193 -0
  215. package/src/runtime/foreground-watchdog.ts +132 -0
  216. package/src/runtime/global-worker-cap.ts +96 -0
  217. package/src/runtime/goal-achievement.ts +148 -0
  218. package/src/runtime/goal-evaluator.ts +365 -0
  219. package/src/runtime/goal-loop-runner.ts +825 -0
  220. package/src/runtime/goal-state-store.ts +219 -0
  221. package/src/runtime/green-contract.ts +55 -0
  222. package/src/runtime/group-join.ts +267 -0
  223. package/src/runtime/handoff-manager.ts +598 -0
  224. package/src/runtime/heartbeat-gradient.ts +36 -0
  225. package/src/runtime/heartbeat-watcher.ts +239 -0
  226. package/src/runtime/hidden-handoff.ts +407 -0
  227. package/src/runtime/important-line-classifier.ts +140 -0
  228. package/src/runtime/intercom-bridge.ts +187 -0
  229. package/src/runtime/iteration-hooks.ts +305 -0
  230. package/src/runtime/live-agent-control.ts +136 -0
  231. package/src/runtime/live-agent-manager.ts +704 -0
  232. package/src/runtime/live-control-realtime.ts +56 -0
  233. package/src/runtime/live-extension-bridge.ts +148 -0
  234. package/src/runtime/live-irc.ts +97 -0
  235. package/src/runtime/live-session-health.ts +108 -0
  236. package/src/runtime/live-session-runtime.ts +1146 -0
  237. package/src/runtime/loop-gates.ts +128 -0
  238. package/src/runtime/manifest-cache.ts +375 -0
  239. package/src/runtime/mcp-proxy.ts +105 -0
  240. package/src/runtime/metric-parser.ts +36 -0
  241. package/src/runtime/model-fallback.ts +409 -0
  242. package/src/runtime/model-resolver.ts +126 -0
  243. package/src/runtime/model-scope.ts +158 -0
  244. package/src/runtime/orphan-worker-registry.ts +465 -0
  245. package/src/runtime/output-validator.ts +202 -0
  246. package/src/runtime/overflow-recovery.ts +206 -0
  247. package/src/runtime/parallel-research.ts +68 -0
  248. package/src/runtime/parallel-utils.ts +160 -0
  249. package/src/runtime/parent-guard.ts +134 -0
  250. package/src/runtime/path-overlap.ts +150 -0
  251. package/src/runtime/peer-dep.ts +292 -0
  252. package/src/runtime/per-write-validator.ts +181 -0
  253. package/src/runtime/phase-progress.ts +217 -0
  254. package/src/runtime/phase-tracker.ts +385 -0
  255. package/src/runtime/pi-args.ts +655 -0
  256. package/src/runtime/pi-json-output.ts +170 -0
  257. package/src/runtime/pi-spawn.ts +273 -0
  258. package/src/runtime/pipeline-runner.ts +523 -0
  259. package/src/runtime/plan-templates.ts +201 -0
  260. package/src/runtime/policy-engine.ts +115 -0
  261. package/src/runtime/post-checks.ts +142 -0
  262. package/src/runtime/post-exit-stdio-guard.ts +109 -0
  263. package/src/runtime/process-lifecycle.ts +491 -0
  264. package/src/runtime/process-status.ts +154 -0
  265. package/src/runtime/progress-event-coalescer.ts +44 -0
  266. package/src/runtime/progress-tracker.ts +124 -0
  267. package/src/runtime/prose-compressor.ts +162 -0
  268. package/src/runtime/recovery-recipes.ts +203 -0
  269. package/src/runtime/replace.ts +570 -0
  270. package/src/runtime/resilient-edit.ts +153 -0
  271. package/src/runtime/result-extractor.ts +217 -0
  272. package/src/runtime/retry-executor.ts +109 -0
  273. package/src/runtime/retry-runner.ts +336 -0
  274. package/src/runtime/role-permission.ts +59 -0
  275. package/src/runtime/run-coalesced-task-group.ts +308 -0
  276. package/src/runtime/run-drift.ts +219 -0
  277. package/src/runtime/run-tracker.ts +116 -0
  278. package/src/runtime/run-worker.ts +78 -0
  279. package/src/runtime/runtime-policy.ts +29 -0
  280. package/src/runtime/runtime-resolver.ts +172 -0
  281. package/src/runtime/runtime-warmup.ts +160 -0
  282. package/src/runtime/scheduler.ts +353 -0
  283. package/src/runtime/semaphore.ts +141 -0
  284. package/src/runtime/sensitive-paths.ts +93 -0
  285. package/src/runtime/session-resources.ts +25 -0
  286. package/src/runtime/session-snapshot.ts +59 -0
  287. package/src/runtime/session-usage.ts +79 -0
  288. package/src/runtime/settings-store.ts +155 -0
  289. package/src/runtime/sidechain-output.ts +35 -0
  290. package/src/runtime/single-agent-compose.ts +84 -0
  291. package/src/runtime/skill-effectiveness.ts +524 -0
  292. package/src/runtime/skill-instructions.ts +394 -0
  293. package/src/runtime/stale-reconciler.ts +680 -0
  294. package/src/runtime/stream-preview.ts +184 -0
  295. package/src/runtime/streaming-output.ts +50 -0
  296. package/src/runtime/subagent-manager.ts +561 -0
  297. package/src/runtime/subprocess-tool-registry.ts +70 -0
  298. package/src/runtime/supervisor-contact.ts +64 -0
  299. package/src/runtime/task-display.ts +52 -0
  300. package/src/runtime/task-graph-scheduler.ts +227 -0
  301. package/src/runtime/task-graph.ts +290 -0
  302. package/src/runtime/task-health.ts +83 -0
  303. package/src/runtime/task-id.ts +161 -0
  304. package/src/runtime/task-output-context.ts +602 -0
  305. package/src/runtime/task-packet.ts +268 -0
  306. package/src/runtime/task-quality.ts +199 -0
  307. package/src/runtime/task-runner/capabilities.ts +78 -0
  308. package/src/runtime/task-runner/child-executor.ts +790 -0
  309. package/src/runtime/task-runner/context-retrieval.ts +163 -0
  310. package/src/runtime/task-runner/live-executor.ts +227 -0
  311. package/src/runtime/task-runner/output-splitter.ts +152 -0
  312. package/src/runtime/task-runner/post-execution.ts +480 -0
  313. package/src/runtime/task-runner/pre-execution.ts +365 -0
  314. package/src/runtime/task-runner/progress.ts +157 -0
  315. package/src/runtime/task-runner/prompt-builder.ts +278 -0
  316. package/src/runtime/task-runner/prompt-pipeline.ts +88 -0
  317. package/src/runtime/task-runner/result-utils.ts +16 -0
  318. package/src/runtime/task-runner/retrieval-orchestrator.ts +310 -0
  319. package/src/runtime/task-runner/run-projection.ts +125 -0
  320. package/src/runtime/task-runner/scaffold-executor.ts +25 -0
  321. package/src/runtime/task-runner/state-helpers.ts +176 -0
  322. package/src/runtime/task-runner/tail-read.ts +34 -0
  323. package/src/runtime/task-runner.ts +216 -0
  324. package/src/runtime/team-runner-artifacts.ts +13 -0
  325. package/src/runtime/team-runner.ts +2396 -0
  326. package/src/runtime/tool-output-pruner.ts +333 -0
  327. package/src/runtime/tool-progress.ts +278 -0
  328. package/src/runtime/usage-tracker.ts +73 -0
  329. package/src/runtime/verification-gates.ts +579 -0
  330. package/src/runtime/verification-integrity.ts +108 -0
  331. package/src/runtime/verification-worktree.ts +186 -0
  332. package/src/runtime/worker-heartbeat.ts +25 -0
  333. package/src/runtime/worker-startup.ts +83 -0
  334. package/src/runtime/workflow-state.ts +195 -0
  335. package/src/runtime/workspace-lock.ts +443 -0
  336. package/src/runtime/workspace-tree.ts +312 -0
  337. package/src/runtime/yield-handler.ts +224 -0
  338. package/src/runtime/zombie-scanner.ts +298 -0
  339. package/src/schema/config-schema.ts +327 -0
  340. package/src/schema/team-tool-schema.ts +492 -0
  341. package/src/schema/validation-types.ts +159 -0
  342. package/src/skills/discover-skills.ts +203 -0
  343. package/src/skills/skill-templates.ts +456 -0
  344. package/src/skills/validate.ts +285 -0
  345. package/src/state/active-run-registry.ts +439 -0
  346. package/src/state/artifact-store.ts +176 -0
  347. package/src/state/atomic-write.ts +970 -0
  348. package/src/state/blob-store.ts +308 -0
  349. package/src/state/contracts.ts +160 -0
  350. package/src/state/crew-init.ts +269 -0
  351. package/src/state/decision-ledger.ts +372 -0
  352. package/src/state/event-log-rotation.ts +399 -0
  353. package/src/state/event-log.ts +1465 -0
  354. package/src/state/event-reconstructor.ts +229 -0
  355. package/src/state/gitignore-manager.ts +46 -0
  356. package/src/state/health-store.ts +79 -0
  357. package/src/state/hook-instinct-bridge.ts +94 -0
  358. package/src/state/hook-integrations.ts +51 -0
  359. package/src/state/instinct-store.ts +275 -0
  360. package/src/state/jsonl-writer.ts +107 -0
  361. package/src/state/locks.ts +562 -0
  362. package/src/state/mailbox.ts +916 -0
  363. package/src/state/observation-store.ts +176 -0
  364. package/src/state/run-cache.ts +193 -0
  365. package/src/state/run-graph.ts +183 -0
  366. package/src/state/run-metrics.ts +165 -0
  367. package/src/state/schedule.ts +166 -0
  368. package/src/state/session-state-map.ts +51 -0
  369. package/src/state/state-store.ts +973 -0
  370. package/src/state/task-claims.ts +61 -0
  371. package/src/state/tiered-eval.ts +480 -0
  372. package/src/state/types-eval.ts +58 -0
  373. package/src/state/types.ts +447 -0
  374. package/src/state/usage.ts +138 -0
  375. package/src/state/worker-atomic-writer.ts +198 -0
  376. package/src/subagents/async-entry.ts +1 -0
  377. package/src/subagents/index.ts +3 -0
  378. package/src/subagents/live/control.ts +1 -0
  379. package/src/subagents/live/manager.ts +1 -0
  380. package/src/subagents/live/realtime.ts +1 -0
  381. package/src/subagents/live/session-runtime.ts +1 -0
  382. package/src/subagents/manager.ts +1 -0
  383. package/src/subagents/spawn.ts +1 -0
  384. package/src/teams/discover-teams.ts +187 -0
  385. package/src/teams/team-config.ts +27 -0
  386. package/src/teams/team-serializer.ts +38 -0
  387. package/src/tools/safe-bash-extension.ts +54 -0
  388. package/src/tools/safe-bash.ts +505 -0
  389. package/src/types/diff.d.ts +18 -0
  390. package/src/types/new-api-types.ts +35 -0
  391. package/src/ui/agent-management-overlay.ts +160 -0
  392. package/src/ui/card-colors.ts +139 -0
  393. package/src/ui/crew-footer.ts +102 -0
  394. package/src/ui/crew-select-list.ts +112 -0
  395. package/src/ui/dashboard-panes/agents-pane.ts +164 -0
  396. package/src/ui/dashboard-panes/cancellation-pane.ts +66 -0
  397. package/src/ui/dashboard-panes/capability-pane.ts +77 -0
  398. package/src/ui/dashboard-panes/health-pane.ts +31 -0
  399. package/src/ui/dashboard-panes/mailbox-pane.ts +35 -0
  400. package/src/ui/dashboard-panes/metrics-pane.ts +37 -0
  401. package/src/ui/dashboard-panes/progress-pane.ts +38 -0
  402. package/src/ui/dashboard-panes/transcript-pane.ts +10 -0
  403. package/src/ui/deploy-bundled-themes.ts +71 -0
  404. package/src/ui/dwf-phase-display.ts +151 -0
  405. package/src/ui/dynamic-border.ts +35 -0
  406. package/src/ui/format-helpers.ts +31 -0
  407. package/src/ui/heartbeat-aggregator.ts +74 -0
  408. package/src/ui/key-utils.ts +42 -0
  409. package/src/ui/keybinding-map.ts +248 -0
  410. package/src/ui/layout-primitives.ts +106 -0
  411. package/src/ui/live-conversation-overlay.ts +183 -0
  412. package/src/ui/live-duration.ts +55 -0
  413. package/src/ui/live-run-sidebar.ts +293 -0
  414. package/src/ui/loaders.ts +6 -0
  415. package/src/ui/mascot.ts +449 -0
  416. package/src/ui/overlays/agent-picker-overlay.ts +66 -0
  417. package/src/ui/overlays/confirm-overlay.ts +60 -0
  418. package/src/ui/overlays/help-overlay.ts +177 -0
  419. package/src/ui/overlays/mailbox-compose-overlay.ts +181 -0
  420. package/src/ui/overlays/mailbox-compose-preview.ts +78 -0
  421. package/src/ui/overlays/mailbox-detail-overlay.ts +155 -0
  422. package/src/ui/pi-ui-compat.ts +68 -0
  423. package/src/ui/powerbar-publisher.ts +459 -0
  424. package/src/ui/render-coalescer.ts +101 -0
  425. package/src/ui/render-diff.ts +160 -0
  426. package/src/ui/render-scheduler.ts +278 -0
  427. package/src/ui/run-action-dispatcher.ts +182 -0
  428. package/src/ui/run-dashboard.ts +910 -0
  429. package/src/ui/run-event-bus.ts +381 -0
  430. package/src/ui/run-snapshot-cache.ts +1057 -0
  431. package/src/ui/settings-overlay.ts +1067 -0
  432. package/src/ui/shared-overlay-scheduler.ts +96 -0
  433. package/src/ui/snapshot-types.ts +90 -0
  434. package/src/ui/spinner.ts +17 -0
  435. package/src/ui/status-colors.ts +143 -0
  436. package/src/ui/syntax-highlight.ts +136 -0
  437. package/src/ui/terminal-status.ts +269 -0
  438. package/src/ui/theme-adapter.ts +271 -0
  439. package/src/ui/theme-discovery.ts +199 -0
  440. package/src/ui/tool-progress-formatter.ts +93 -0
  441. package/src/ui/tool-renderers/brief-mode.ts +225 -0
  442. package/src/ui/tool-renderers/index.ts +726 -0
  443. package/src/ui/transcript-cache.ts +128 -0
  444. package/src/ui/transcript-entries.ts +256 -0
  445. package/src/ui/transcript-viewer.ts +424 -0
  446. package/src/ui/widget/index.ts +418 -0
  447. package/src/ui/widget/widget-formatters.ts +182 -0
  448. package/src/ui/widget/widget-model.ts +110 -0
  449. package/src/ui/widget/widget-renderer.ts +182 -0
  450. package/src/ui/widget/widget-types.ts +36 -0
  451. package/src/utils/bm25-search.ts +235 -0
  452. package/src/utils/completion-dedupe.ts +63 -0
  453. package/src/utils/conflict-detect.ts +668 -0
  454. package/src/utils/env-allowlist.ts +30 -0
  455. package/src/utils/env-filter.ts +155 -0
  456. package/src/utils/file-coalescer.ts +98 -0
  457. package/src/utils/fingerprint.ts +180 -0
  458. package/src/utils/frontmatter.ts +70 -0
  459. package/src/utils/fs-watch.ts +37 -0
  460. package/src/utils/gh-protocol.ts +556 -0
  461. package/src/utils/git.ts +260 -0
  462. package/src/utils/guards.ts +107 -0
  463. package/src/utils/ids.ts +24 -0
  464. package/src/utils/incremental-reader.ts +220 -0
  465. package/src/utils/internal-error.ts +10 -0
  466. package/src/utils/names.ts +36 -0
  467. package/src/utils/ndjson.ts +115 -0
  468. package/src/utils/paths.ts +227 -0
  469. package/src/utils/project-detector.ts +160 -0
  470. package/src/utils/redaction.ts +370 -0
  471. package/src/utils/resolve-shell.ts +36 -0
  472. package/src/utils/run-watcher-registry.ts +152 -0
  473. package/src/utils/safe-paths.ts +393 -0
  474. package/src/utils/scan-cache.ts +144 -0
  475. package/src/utils/session-utils.ts +110 -0
  476. package/src/utils/sleep.ts +54 -0
  477. package/src/utils/socket-path.ts +164 -0
  478. package/src/utils/sse-parser.ts +131 -0
  479. package/src/utils/task-name-generator.ts +337 -0
  480. package/src/utils/timings.ts +33 -0
  481. package/src/utils/token-counter.ts +200 -0
  482. package/src/utils/visual.ts +207 -0
  483. package/src/workflows/cost-estimator.ts +34 -0
  484. package/src/workflows/discover-workflows.ts +308 -0
  485. package/src/workflows/intermediate-store.ts +166 -0
  486. package/src/workflows/preflight-validator.ts +168 -0
  487. package/src/workflows/topology-analyzer.ts +203 -0
  488. package/src/workflows/validate-workflow.ts +50 -0
  489. package/src/workflows/workflow-config.ts +75 -0
  490. package/src/workflows/workflow-serializer.ts +32 -0
  491. package/src/worktree/branch-freshness.ts +112 -0
  492. package/src/worktree/cleanup.ts +341 -0
  493. package/src/worktree/worktree-manager.ts +1204 -0
@@ -0,0 +1,170 @@
1
+ export interface ParsedPiUsage {
2
+ input?: number;
3
+ output?: number;
4
+ cacheRead?: number;
5
+ cacheWrite?: number;
6
+ cost?: number;
7
+ turns?: number;
8
+ }
9
+
10
+ export interface ParsedPiJsonOutput {
11
+ jsonEvents: number;
12
+ textEvents: string[];
13
+ finalText?: string;
14
+ usage?: ParsedPiUsage;
15
+ /** Unified patches extracted from tool_result events (edit tool patch field) */
16
+ patches?: string[];
17
+ /** Model/provider error messages extracted from message_end events (e.g.
18
+ * "429 ... overloaded"). Used to detect runs that exited 0 but produced
19
+ * nothing because the model was rate-limited — see task-runner 429 fix. */
20
+ errorMessages?: string[];
21
+ }
22
+
23
+ function asRecord(value: unknown): Record<string, unknown> | undefined {
24
+ return value && typeof value === "object" && !Array.isArray(value) ? (value as Record<string, unknown>) : undefined;
25
+ }
26
+
27
+ function numberField(obj: Record<string, unknown>, keys: string[]): number | undefined {
28
+ for (const key of keys) {
29
+ const value = obj[key];
30
+ if (typeof value === "number" && Number.isFinite(value)) return value;
31
+ }
32
+ return undefined;
33
+ }
34
+
35
+ function mergeUsage(target: ParsedPiUsage, source: ParsedPiUsage): ParsedPiUsage {
36
+ return {
37
+ input: source.input ?? target.input,
38
+ output: source.output ?? target.output,
39
+ cacheRead: source.cacheRead ?? target.cacheRead,
40
+ cacheWrite: source.cacheWrite ?? target.cacheWrite,
41
+ cost: source.cost ?? target.cost,
42
+ turns: source.turns ?? target.turns,
43
+ };
44
+ }
45
+
46
+ function extractUsage(value: unknown): ParsedPiUsage | undefined {
47
+ const obj = asRecord(value);
48
+ if (!obj) return undefined;
49
+ const direct: ParsedPiUsage = {
50
+ input: numberField(obj, ["input", "inputTokens", "input_tokens"]),
51
+ output: numberField(obj, ["output", "outputTokens", "output_tokens"]),
52
+ cacheRead: numberField(obj, ["cacheRead", "cache_read", "cacheReadTokens", "cache_read_tokens"]),
53
+ cacheWrite: numberField(obj, ["cacheWrite", "cache_write", "cacheWriteTokens", "cache_write_tokens"]),
54
+ cost: numberField(obj, ["cost", "costUsd", "cost_usd"]),
55
+ turns: numberField(obj, ["turns", "turnCount", "turn_count"]),
56
+ };
57
+ if (Object.values(direct).some((entry) => entry !== undefined)) return direct;
58
+ // Pi --mode json nests usage under message for message_end / turn_end events.
59
+ for (const key of ["usage", "message", "tokenUsage", "tokens", "stats"]) {
60
+ const nested = extractUsage(obj[key]);
61
+ if (nested) return nested;
62
+ }
63
+ return undefined;
64
+ }
65
+
66
+ function textFromContent(content: unknown): string[] {
67
+ if (typeof content === "string") return [content];
68
+ if (!Array.isArray(content)) return [];
69
+ const text: string[] = [];
70
+ for (const part of content) {
71
+ const obj = asRecord(part);
72
+ if (!obj) continue;
73
+ if (obj.type === "text" && typeof obj.text === "string") text.push(obj.text);
74
+ else if (typeof obj.content === "string") text.push(obj.content);
75
+ }
76
+ return text;
77
+ }
78
+
79
+ /** Extract assistant-text fragments from a parsed Pi JSON event.
80
+ * Exported so {@link ChildPiLineObserver} can capture the RAW (uncapped)
81
+ * assistant text for the authoritative result, mirroring the extraction
82
+ * order this function uses inside {@link parsePiJsonOutput}. */
83
+ export function extractText(value: unknown): string[] {
84
+ const obj = asRecord(value);
85
+ if (!obj) return [];
86
+ const message = asRecord(obj.message);
87
+ if (message?.role !== undefined && message.role !== "assistant") return [];
88
+ const text: string[] = [];
89
+ if (typeof obj.text === "string") text.push(obj.text);
90
+ if (typeof obj.output === "string") text.push(obj.output);
91
+ if (typeof obj.finalOutput === "string") text.push(obj.finalOutput);
92
+ if (typeof obj.final_output === "string") text.push(obj.final_output);
93
+ if (!message) text.push(...textFromContent(obj.content));
94
+ if (message) text.push(...textFromContent(message.content));
95
+ return text.filter((entry) => entry.trim().length > 0);
96
+ }
97
+
98
+ export function parsePiJsonOutput(stdout: string): ParsedPiJsonOutput {
99
+ let jsonEvents = 0;
100
+ const textEvents: string[] = [];
101
+ const patches: string[] = [];
102
+ const errorMessages: string[] = [];
103
+ let usage: ParsedPiUsage | undefined;
104
+ for (const line of stdout.split("\n")) {
105
+ const trimmed = line.trim();
106
+ if (!trimmed) continue;
107
+ let event: unknown;
108
+ try {
109
+ event = JSON.parse(trimmed) as unknown;
110
+ } catch {
111
+ continue;
112
+ }
113
+ jsonEvents++;
114
+ textEvents.push(...extractText(event));
115
+ // Extract unified patches from tool_result events
116
+ extractPatch(event, patches);
117
+ // Extract provider/model error messages from message_end events (429 fix).
118
+ const errMsg = extractErrorMessage(event);
119
+ if (errMsg) errorMessages.push(errMsg);
120
+ const eventUsage = extractUsage(event);
121
+ if (eventUsage) usage = mergeUsage(usage ?? {}, eventUsage);
122
+ }
123
+ return {
124
+ jsonEvents,
125
+ textEvents,
126
+ finalText: textEvents.length > 0 ? textEvents[textEvents.length - 1] : undefined,
127
+ usage,
128
+ patches: patches.length > 0 ? patches : undefined,
129
+ errorMessages: errorMessages.length > 0 ? errorMessages : undefined,
130
+ };
131
+ }
132
+
133
+ /**
134
+ * Pull the provider/model error message out of a `message_end` event. The shape
135
+ * is `{type:"message_end", message:{role:"assistant", content:[], errorMessage:"429 ...", stopReason:"error"}}`.
136
+ * Returns undefined for events without an errorMessage.
137
+ */
138
+ function extractErrorMessage(event: unknown): string | undefined {
139
+ const obj = asRecord(event);
140
+ if (!obj) return undefined;
141
+ // message_end events carry the error on the nested message object.
142
+ const message = asRecord(obj.message) ?? obj;
143
+ const errorMessage = message.errorMessage;
144
+ return typeof errorMessage === "string" && errorMessage.trim() ? errorMessage.trim() : undefined;
145
+ }
146
+
147
+ /**
148
+ * Extract unified patches from a tool_result event.
149
+ * pi's edit tool now includes a `patch` field (standard unified diff format).
150
+ * We detect it by looking for lines starting with "---" or "+++" which indicate
151
+ * unified diff format.
152
+ */
153
+ function extractPatch(event: unknown, patches: string[]): void {
154
+ const obj = asRecord(event);
155
+ if (obj?.type !== "tool_result") return;
156
+
157
+ const content = obj.content;
158
+ if (!Array.isArray(content)) return;
159
+
160
+ for (const item of content) {
161
+ const part = asRecord(item);
162
+ if (part?.type !== "text") continue;
163
+ const text = typeof part.text === "string" ? part.text : "";
164
+
165
+ // Check if this looks like a unified patch (starts with "---" or "+++")
166
+ if (text.includes("--- a/") || text.includes("diff ---")) {
167
+ patches.push(text);
168
+ }
169
+ }
170
+ }
@@ -0,0 +1,273 @@
1
+ import { execSync } from "node:child_process";
2
+ import * as fs from "node:fs";
3
+ import * as os from "node:os";
4
+ import * as path from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+
7
+ export interface PiSpawnCommand {
8
+ command: string;
9
+ args: string[];
10
+ }
11
+
12
+ const PI_PACKAGE_NAMES = ["@earendil-works/pi-coding-agent", "@mariozechner/pi-coding-agent"];
13
+
14
+ function isRunnableNodeScript(filePath: string): boolean {
15
+ return fs.existsSync(filePath) && /\.(?:mjs|cjs|js)$/i.test(filePath);
16
+ }
17
+
18
+ /**
19
+ * Check that a resolved path is within known safe prefixes.
20
+ * Allowed prefixes: npm global bin (APPDATA/npm), project node_modules/.bin,
21
+ * or the current process's execPath directory.
22
+ */
23
+ function isWithinAllowedPrefixes(resolvedPath: string): boolean {
24
+ const normalized = path.resolve(resolvedPath).toLowerCase();
25
+
26
+ const allowedPrefixes: string[] = [];
27
+
28
+ // Current process execPath directory (e.g. node installation)
29
+ try {
30
+ const execDir = path.dirname(fs.realpathSync.native(process.execPath));
31
+ allowedPrefixes.push(execDir.toLowerCase());
32
+ allowedPrefixes.push(path.join(path.dirname(execDir), "lib", "node_modules").toLowerCase());
33
+ } catch {
34
+ /* ignore */
35
+ }
36
+
37
+ // npm global bin via APPDATA
38
+ if (process.env.APPDATA) {
39
+ allowedPrefixes.push(path.join(process.env.APPDATA, "npm").toLowerCase());
40
+ }
41
+
42
+ const npmPrefix = process.env.npm_config_prefix ?? process.env.NPM_CONFIG_PREFIX;
43
+ if (npmPrefix) {
44
+ allowedPrefixes.push(path.resolve(npmPrefix).toLowerCase());
45
+ allowedPrefixes.push(path.join(path.resolve(npmPrefix), "lib", "node_modules").toLowerCase());
46
+ }
47
+
48
+ // Project-local node_modules/.bin
49
+ try {
50
+ const projectBin = path.resolve("node_modules", ".bin");
51
+ allowedPrefixes.push(projectBin.toLowerCase());
52
+ } catch {
53
+ /* ignore */
54
+ }
55
+
56
+ // User home npm-global
57
+ try {
58
+ const homeNpm = path.join(os.homedir(), ".npm-global", "bin");
59
+ allowedPrefixes.push(homeNpm.toLowerCase());
60
+ } catch {
61
+ /* ignore */
62
+ }
63
+
64
+ // User home .local/bin
65
+ try {
66
+ const homeLocal = path.join(os.homedir(), ".local", "bin");
67
+ allowedPrefixes.push(homeLocal.toLowerCase());
68
+ } catch {
69
+ /* ignore */
70
+ }
71
+
72
+ return allowedPrefixes.some((prefix) => normalized.startsWith(prefix));
73
+ }
74
+
75
+ function resolvePiPackageRoot(): string | undefined {
76
+ try {
77
+ const entry = process.argv[1];
78
+ if (!entry) return undefined;
79
+ let dir = path.dirname(fs.realpathSync(entry));
80
+ while (dir !== path.dirname(dir)) {
81
+ try {
82
+ const pkg = JSON.parse(fs.readFileSync(path.join(dir, "package.json"), "utf-8")) as { name?: string };
83
+ if (pkg.name && PI_PACKAGE_NAMES.includes(pkg.name)) return dir;
84
+ } catch {
85
+ // Continue walking upward.
86
+ }
87
+ dir = path.dirname(dir);
88
+ }
89
+ } catch {
90
+ return undefined;
91
+ }
92
+ return undefined;
93
+ }
94
+
95
+ function packageBinScript(packageJsonPath: string): string | undefined {
96
+ try {
97
+ const pkg = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8")) as {
98
+ bin?: string | Record<string, string>;
99
+ };
100
+ const binPath = typeof pkg.bin === "string" ? pkg.bin : (pkg.bin?.pi ?? Object.values(pkg.bin ?? {})[0]);
101
+ if (!binPath) return undefined;
102
+ const candidate = path.resolve(path.dirname(packageJsonPath), binPath);
103
+ return isRunnableNodeScript(candidate) ? candidate : undefined;
104
+ } catch {
105
+ return undefined;
106
+ }
107
+ }
108
+
109
+ function findPiPackageJsonFrom(startDir: string): string | undefined {
110
+ let dir = startDir;
111
+ while (dir !== path.dirname(dir)) {
112
+ const direct = path.join(dir, "package.json");
113
+ try {
114
+ const pkg = JSON.parse(fs.readFileSync(direct, "utf-8")) as {
115
+ name?: string;
116
+ };
117
+ if (pkg.name && PI_PACKAGE_NAMES.includes(pkg.name)) return direct;
118
+ } catch {
119
+ // Continue searching upward and in node_modules.
120
+ }
121
+ for (const pkgName of PI_PACKAGE_NAMES) {
122
+ const [scope, name] = pkgName.replace("@", "").split("/");
123
+ const dependency = path.join(dir, "node_modules", `@${scope}`, name, "package.json");
124
+ if (fs.existsSync(dependency)) return dependency;
125
+ }
126
+ dir = path.dirname(dir);
127
+ }
128
+ return undefined;
129
+ }
130
+
131
+ /**
132
+ * Discover the real npm global node_modules directory at runtime.
133
+ *
134
+ * Why this exists (Issue #33): on Windows, pi may be installed somewhere
135
+ * other than %APPDATA%\npm — e.g. nvm-windows puts the global node_modules
136
+ * under %NVM_HOME%/<version>/node_modules, Volta under
137
+ * %LOCALAPPDATA%\Volta, fnm under %LOCALAPPDATA%\fnm_multishells. The static
138
+ * %APPDATA%\npm paths in resolvePiCliScript() miss all of those, and the
139
+ * fallback spawn("pi") then fails with ENOENT because child_process.spawn does
140
+ * NOT do PATHEXT resolution on Windows (only exec/execSync via cmd.exe do).
141
+ *
142
+ * `npm root -g` is the canonical way to find the global node_modules dir and
143
+ * works across every npm-based install layout. We run it via execSync, which
144
+ * DOES resolve `npm.cmd` through PATHEXT. Capped at 5s; any failure (npm not
145
+ * on PATH, slow start, etc.) just falls through to the other resolution roots.
146
+ *
147
+ * Memoized: the npm global root does not change during a process lifetime, so
148
+ * this is a one-time ~200ms cost rather than per-worker.
149
+ *
150
+ * @internal — exported for unit-test injection via __setNpmGlobalRootForTest.
151
+ */
152
+ let cachedNpmGlobalRoot: string | undefined | null = null;
153
+ export function resolveNpmGlobalRoot(): string | undefined {
154
+ if (cachedNpmGlobalRoot !== null) {
155
+ return cachedNpmGlobalRoot ?? undefined;
156
+ }
157
+ let resolved: string | undefined;
158
+ try {
159
+ const out = execSync("npm root -g", {
160
+ encoding: "utf-8",
161
+ timeout: 5000,
162
+ stdio: ["pipe", "pipe", "pipe"], // suppress npm's stderr chatter
163
+ windowsHide: true,
164
+ }).trim();
165
+ resolved = out.length > 0 ? out : undefined;
166
+ } catch {
167
+ resolved = undefined;
168
+ }
169
+ cachedNpmGlobalRoot = resolved ?? null;
170
+ return resolved;
171
+ }
172
+
173
+ /**
174
+ * Given an npm global node_modules root, derive the candidate package dirs for
175
+ * each supported pi scope. Pure + exported so the mapping is unit-testable
176
+ * without spawning npm.
177
+ * @internal
178
+ */
179
+ export function buildNpmGlobalPackageDirs(npmGlobalRoot: string): string[] {
180
+ return PI_PACKAGE_NAMES.map((pkgName) => path.join(npmGlobalRoot, ...pkgName.split("/")));
181
+ }
182
+
183
+ /** @internal — test hook: inject a fake global root (or undefined) and reset the memo. */
184
+ export function __setNpmGlobalRootForTest(root: string | undefined): void {
185
+ cachedNpmGlobalRoot = root ?? null;
186
+ }
187
+
188
+ function resolvePiCliScript(): string | undefined {
189
+ const argv1 = process.argv[1];
190
+ if (argv1) {
191
+ const argvPath = path.isAbsolute(argv1) ? argv1 : path.resolve(argv1);
192
+ // Only trust argv1 if we can confirm the current process is running from
193
+ // the pi-coding-agent package directory. Otherwise, when pi-crew is invoked
194
+ // from a standalone test script (process.argv[1] = test script path),
195
+ // resolvePiCliScript would incorrectly return the test script as the pi
196
+ // CLI. resolvePiPackageRoot walks up from argv1 looking for a package.json
197
+ // named @earendil-works/pi-coding-agent or @mariozechner/pi-coding-agent.
198
+ if (resolvePiPackageRoot() && isRunnableNodeScript(argvPath)) return argvPath;
199
+ }
200
+
201
+ // npm-global package dirs derived from `npm root -g` — placed BEFORE the
202
+ // %APPDATA%\npm static paths and the cwd/import.meta fallbacks so that a pi
203
+ // install under nvm-windows / Volta / fnm is found even when %APPDATA%\npm
204
+ // doesn't contain it. Covers Issue #33.
205
+ const npmGlobalRoot = resolveNpmGlobalRoot();
206
+ const npmGlobalDirs = npmGlobalRoot ? buildNpmGlobalPackageDirs(npmGlobalRoot) : [];
207
+
208
+ const roots = [
209
+ resolvePiPackageRoot(),
210
+ ...npmGlobalDirs,
211
+ process.env.APPDATA ? path.join(process.env.APPDATA, "npm", "node_modules", "@earendil-works", "pi-coding-agent") : undefined,
212
+ process.env.APPDATA ? path.join(process.env.APPDATA, "npm", "node_modules", "@mariozechner", "pi-coding-agent") : undefined,
213
+ path.dirname(fileURLToPath(import.meta.url)),
214
+ process.cwd(),
215
+ ].filter((entry): entry is string => Boolean(entry));
216
+
217
+ for (const root of roots) {
218
+ const packageJsonPath = root.endsWith("package.json") ? root : (findPiPackageJsonFrom(root) ?? path.join(root, "package.json"));
219
+ const script = packageBinScript(packageJsonPath);
220
+ if (script) return script;
221
+ }
222
+ return undefined;
223
+ }
224
+
225
+ function validateExplicitBin(explicit: string): string | undefined {
226
+ const resolved = path.resolve(explicit);
227
+ if (!fs.existsSync(resolved)) return undefined;
228
+ // Reject paths outside allowed safe prefixes
229
+ if (!isWithinAllowedPrefixes(resolved)) {
230
+ throw new Error(
231
+ `PI_TEAMS_PI_BIN path '${resolved}' is outside allowed prefixes. ` +
232
+ `Allowed: npm global bin, project node_modules/.bin, APPDATA/npm, or process execPath directory.`,
233
+ );
234
+ }
235
+ // Reject if symlink points outside expected directories
236
+ try {
237
+ const real = fs.realpathSync(resolved);
238
+ if (!isWithinAllowedPrefixes(real)) {
239
+ throw new Error(`PI_TEAMS_PI_BIN symlink target '${real}' is outside allowed prefixes.`);
240
+ }
241
+ } catch (e) {
242
+ if (e instanceof Error && e.message.includes("allowed prefixes")) throw e;
243
+ console.error("[pi-spawn] validateExplicitBin: unexpected realpathSync error:", e);
244
+ return undefined;
245
+ }
246
+ return resolved;
247
+ }
248
+
249
+ export function getPiSpawnCommand(args: string[]): PiSpawnCommand {
250
+ const explicit = process.env.PI_TEAMS_PI_BIN?.trim();
251
+ if (explicit) {
252
+ const validated = validateExplicitBin(explicit);
253
+ if (validated) {
254
+ if (isRunnableNodeScript(validated))
255
+ return {
256
+ command: process.execPath,
257
+ args: [validated, ...args],
258
+ };
259
+ return { command: validated, args };
260
+ }
261
+ }
262
+ if (process.platform === "win32") {
263
+ // Windows: resolve via resolvePiCliScript to find the bundled .js entry point
264
+ const script = resolvePiCliScript();
265
+ if (script) return { command: process.execPath, args: [script, ...args] };
266
+ }
267
+ // Linux/macOS: also resolve the full path so child processes can find 'pi' even if
268
+ // PATH is minimal (e.g. in detached background-runner processes). Fall back to "pi"
269
+ // only if resolution fails.
270
+ const script = resolvePiCliScript();
271
+ if (script) return { command: process.execPath, args: [script, ...args] };
272
+ return { command: "pi", args };
273
+ }