pi-crew 0.9.16 → 0.9.17

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 (432) hide show
  1. package/CHANGELOG.md +81 -0
  2. package/README.md +19 -0
  3. package/dist/build-meta.json +21790 -0
  4. package/dist/index.mjs +83070 -0
  5. package/dist/index.mjs.map +7 -0
  6. package/docs/A +358 -0
  7. package/docs/M-A +357 -0
  8. package/docs/REVIEW-FINDINGS-2026-06 +357 -0
  9. package/docs/Y +357 -0
  10. package/docs/a +357 -0
  11. package/docs/aA +358 -0
  12. package/docs/archive/README.md +91 -0
  13. package/docs/patterns/command-agent-skill.md +1 -1
  14. package/docs/skills/REFERENCE.md +0 -17
  15. package/docs//303/242mmaAAA/303/242 +357 -0
  16. package/index.bundle.ts +25 -0
  17. package/index.ts +95 -4
  18. package/package.json +15 -4
  19. package/skills/iterative-audit/SKILL.md +0 -1
  20. package/skills/widget-rendering/SKILL.md +17 -0
  21. package/src/adapters/claude-adapter.ts +1 -3
  22. package/src/adapters/export-util.ts +16 -16
  23. package/src/adapters/index.ts +5 -5
  24. package/src/agents/agent-config.ts +20 -14
  25. package/src/agents/agent-serializer.ts +1 -1
  26. package/src/agents/discover-agents.ts +83 -39
  27. package/src/benchmark/benchmark-runner.ts +51 -51
  28. package/src/benchmark/feedback-loop.ts +1 -1
  29. package/src/config/config.ts +166 -590
  30. package/src/config/defaults.ts +28 -2
  31. package/src/config/drift-detector.ts +11 -14
  32. package/src/config/markers.ts +201 -203
  33. package/src/config/resilient-parser.ts +14 -6
  34. package/src/config/role-tools.ts +3 -3
  35. package/src/config/suggestions.ts +5 -12
  36. package/src/config/types.ts +9 -25
  37. package/src/errors.ts +151 -157
  38. package/src/extension/action-suggestions.ts +54 -9
  39. package/src/extension/async-notifier.ts +43 -13
  40. package/src/extension/autonomous-policy.ts +77 -37
  41. package/src/extension/command-completions.ts +9 -8
  42. package/src/extension/context-status-injection.ts +14 -12
  43. package/src/extension/crew-autocomplete.ts +8 -16
  44. package/src/extension/crew-cleanup.ts +2 -1
  45. package/src/extension/crew-shortcuts.ts +9 -6
  46. package/src/extension/cross-extension-rpc.ts +136 -42
  47. package/src/extension/help.ts +1 -1
  48. package/src/extension/import-index.ts +10 -5
  49. package/src/extension/knowledge-injection.ts +88 -15
  50. package/src/extension/management.ts +89 -349
  51. package/src/extension/message-renderers.ts +3 -4
  52. package/src/extension/notification-router.ts +22 -5
  53. package/src/extension/notification-sink.ts +6 -3
  54. package/src/extension/pi-api.ts +17 -8
  55. package/src/extension/plan-orchestrate.ts +8 -27
  56. package/src/extension/project-init.ts +21 -6
  57. package/src/extension/register.ts +329 -975
  58. package/src/extension/registration/artifact-cleanup.ts +9 -4
  59. package/src/extension/registration/command-utils.ts +5 -1
  60. package/src/extension/registration/commands.ts +806 -314
  61. package/src/extension/registration/compaction-guard.ts +15 -15
  62. package/src/extension/registration/lifecycle.ts +259 -0
  63. package/src/extension/registration/observability.ts +293 -0
  64. package/src/extension/registration/subagent-helpers.ts +29 -7
  65. package/src/extension/registration/subagent-tools.ts +253 -44
  66. package/src/extension/registration/team-tool.ts +19 -84
  67. package/src/extension/registration/ui.ts +168 -0
  68. package/src/extension/registration/viewers.ts +64 -24
  69. package/src/extension/result-watcher.ts +18 -7
  70. package/src/extension/run-bundle-schema.ts +21 -6
  71. package/src/extension/run-export.ts +15 -6
  72. package/src/extension/run-import.ts +64 -29
  73. package/src/extension/run-index.ts +28 -9
  74. package/src/extension/run-maintenance.ts +32 -12
  75. package/src/extension/session-summary.ts +1 -1
  76. package/src/extension/team-manager-command.ts +64 -7
  77. package/src/extension/team-onboard.ts +151 -150
  78. package/src/extension/team-recommendation.ts +121 -21
  79. package/src/extension/team-tool/anchor.ts +31 -64
  80. package/src/extension/team-tool/api.ts +929 -141
  81. package/src/extension/team-tool/auto-summarize.ts +14 -26
  82. package/src/extension/team-tool/cache-control.ts +1 -5
  83. package/src/extension/team-tool/cancel.ts +138 -38
  84. package/src/extension/team-tool/chain-dispatch.ts +5 -13
  85. package/src/extension/team-tool/chain-executor.ts +29 -51
  86. package/src/extension/team-tool/config-patch.ts +1 -1
  87. package/src/extension/team-tool/context.ts +40 -20
  88. package/src/extension/team-tool/destructive-gate.ts +10 -5
  89. package/src/extension/team-tool/doctor.ts +170 -51
  90. package/src/extension/team-tool/explain.ts +228 -214
  91. package/src/extension/team-tool/failure-patterns.ts +3 -9
  92. package/src/extension/team-tool/goal-wrap.ts +71 -29
  93. package/src/extension/team-tool/goal.ts +185 -35
  94. package/src/extension/team-tool/handle-schedule.ts +34 -15
  95. package/src/extension/team-tool/handle-settings.ts +94 -56
  96. package/src/extension/team-tool/health-monitor.ts +27 -108
  97. package/src/extension/team-tool/inspect.ts +42 -9
  98. package/src/extension/team-tool/intent-policy.ts +4 -1
  99. package/src/extension/team-tool/lifecycle-actions.ts +244 -48
  100. package/src/extension/team-tool/orchestrate.ts +7 -18
  101. package/src/extension/team-tool/parallel-dispatch.ts +51 -29
  102. package/src/extension/team-tool/plan.ts +29 -5
  103. package/src/extension/team-tool/respond.ts +49 -16
  104. package/src/extension/team-tool/run-not-found.ts +3 -8
  105. package/src/extension/team-tool/run.ts +80 -317
  106. package/src/extension/team-tool/status.ts +135 -43
  107. package/src/extension/team-tool/workflow-manage.ts +92 -25
  108. package/src/extension/team-tool-types.ts +8 -1
  109. package/src/extension/team-tool.ts +142 -551
  110. package/src/extension/validate-resources.ts +39 -8
  111. package/src/hooks/registry.ts +66 -17
  112. package/src/hooks/types.ts +1 -1
  113. package/src/i18n.ts +25 -11
  114. package/src/observability/correlation.ts +17 -3
  115. package/src/observability/event-bus.ts +51 -56
  116. package/src/observability/event-to-metric.ts +117 -15
  117. package/src/observability/exporters/otlp-exporter.ts +56 -17
  118. package/src/observability/exporters/prometheus-exporter.ts +1 -1
  119. package/src/observability/metric-registry.ts +16 -4
  120. package/src/observability/metric-retention.ts +8 -2
  121. package/src/observability/metric-sink.ts +11 -3
  122. package/src/observability/metrics-primitives.ts +42 -9
  123. package/src/plugins/plugin-define.ts +2 -2
  124. package/src/plugins/plugin-registry.ts +25 -25
  125. package/src/plugins/plugins/index.ts +1 -1
  126. package/src/plugins/plugins/nextjs.ts +16 -16
  127. package/src/plugins/plugins/vite.ts +7 -11
  128. package/src/plugins/plugins/vitest.ts +6 -11
  129. package/src/runtime/adaptive-plan.ts +225 -40
  130. package/src/runtime/agent-control.ts +58 -15
  131. package/src/runtime/agent-memory.ts +15 -9
  132. package/src/runtime/agent-observability.ts +10 -2
  133. package/src/runtime/anchor-manager.ts +27 -25
  134. package/src/runtime/async-marker.ts +7 -1
  135. package/src/runtime/async-runner.ts +27 -23
  136. package/src/runtime/auto-summarize.ts +7 -13
  137. package/src/runtime/background-runner.ts +189 -251
  138. package/src/runtime/batch-barrier.ts +18 -16
  139. package/src/runtime/cancellation-token.ts +16 -6
  140. package/src/runtime/cancellation.ts +46 -8
  141. package/src/runtime/capability-inventory.ts +6 -4
  142. package/src/runtime/chain-parser.ts +44 -11
  143. package/src/runtime/chain-runner.ts +63 -69
  144. package/src/runtime/checkpoint.ts +12 -56
  145. package/src/runtime/child-pi.ts +343 -78
  146. package/src/runtime/coalesce-tasks.ts +268 -0
  147. package/src/runtime/code-summary.ts +71 -26
  148. package/src/runtime/compact-stages/index.ts +15 -5
  149. package/src/runtime/compact-stages/tail-capture-stage.ts +7 -2
  150. package/src/runtime/compact-stages/truncation-stage.ts +4 -1
  151. package/src/runtime/compaction-summary.ts +17 -10
  152. package/src/runtime/completion-guard.ts +33 -16
  153. package/src/runtime/crash-classification.ts +28 -7
  154. package/src/runtime/crash-recovery.ts +177 -37
  155. package/src/runtime/crew-agent-records.ts +145 -47
  156. package/src/runtime/crew-hooks.ts +9 -24
  157. package/src/runtime/cross-extension-rpc.ts +25 -23
  158. package/src/runtime/custom-tools/irc-tool.ts +43 -15
  159. package/src/runtime/custom-tools/submit-result-tool.ts +16 -5
  160. package/src/runtime/delivery-coordinator.ts +34 -7
  161. package/src/runtime/delta-conflict.ts +9 -21
  162. package/src/runtime/deterministic-ast.ts +1 -1
  163. package/src/runtime/diagnostic-export.ts +53 -20
  164. package/src/runtime/direct-run.ts +12 -2
  165. package/src/runtime/dwf-state-store.ts +1 -1
  166. package/src/runtime/dynamic-workflow-context.ts +186 -58
  167. package/src/runtime/dynamic-workflow-runner.ts +42 -20
  168. package/src/runtime/effectiveness.ts +16 -8
  169. package/src/runtime/errors/crew-errors.ts +7 -11
  170. package/src/runtime/event-stream-bridge.ts +9 -3
  171. package/src/runtime/foreground-control.ts +54 -14
  172. package/src/runtime/foreground-watchdog.ts +9 -6
  173. package/src/runtime/goal-achievement.ts +27 -10
  174. package/src/runtime/goal-evaluator.ts +54 -19
  175. package/src/runtime/goal-loop-runner.ts +280 -99
  176. package/src/runtime/goal-state-store.ts +27 -17
  177. package/src/runtime/green-contract.ts +11 -2
  178. package/src/runtime/group-join.ts +64 -31
  179. package/src/runtime/handoff-manager.ts +37 -42
  180. package/src/runtime/heartbeat-gradient.ts +10 -2
  181. package/src/runtime/heartbeat-watcher.ts +32 -6
  182. package/src/runtime/hidden-handoff.ts +12 -30
  183. package/src/runtime/important-line-classifier.ts +12 -2
  184. package/src/runtime/iteration-hooks.ts +25 -15
  185. package/src/runtime/live-agent-control.ts +55 -7
  186. package/src/runtime/live-agent-manager.ts +130 -27
  187. package/src/runtime/live-control-realtime.ts +23 -3
  188. package/src/runtime/live-irc.ts +4 -1
  189. package/src/runtime/live-session-health.ts +12 -4
  190. package/src/runtime/live-session-runtime.ts +379 -117
  191. package/src/runtime/manifest-cache.ts +28 -12
  192. package/src/runtime/mcp-proxy.ts +8 -16
  193. package/src/runtime/metric-parser.ts +1 -5
  194. package/src/runtime/model-fallback.ts +55 -18
  195. package/src/runtime/model-resolver.ts +2 -6
  196. package/src/runtime/model-scope.ts +19 -3
  197. package/src/runtime/notebook-helpers.ts +60 -62
  198. package/src/runtime/orphan-worker-registry.ts +37 -26
  199. package/src/runtime/output-validator.ts +18 -3
  200. package/src/runtime/overflow-recovery.ts +5 -4
  201. package/src/runtime/parallel-research.ts +29 -5
  202. package/src/runtime/parallel-utils.ts +9 -11
  203. package/src/runtime/path-overlap.ts +150 -0
  204. package/src/runtime/peer-dep.ts +12 -17
  205. package/src/runtime/per-write-validator.ts +1 -3
  206. package/src/runtime/phase-tracker.ts +342 -330
  207. package/src/runtime/pi-args.ts +26 -8
  208. package/src/runtime/pi-json-output.ts +1 -1
  209. package/src/runtime/pi-spawn.ts +30 -19
  210. package/src/runtime/pipeline-runner.ts +56 -55
  211. package/src/runtime/plan-templates.ts +5 -6
  212. package/src/runtime/policy-engine.ts +43 -7
  213. package/src/runtime/post-checks.ts +12 -4
  214. package/src/runtime/post-exit-stdio-guard.ts +2 -2
  215. package/src/runtime/process-lifecycle.ts +20 -10
  216. package/src/runtime/process-status.ts +16 -5
  217. package/src/runtime/progress-event-coalescer.ts +2 -1
  218. package/src/runtime/progress-tracker.ts +103 -103
  219. package/src/runtime/prose-compressor.ts +9 -11
  220. package/src/runtime/recovery-recipes.ts +118 -24
  221. package/src/runtime/replace.ts +25 -10
  222. package/src/runtime/resilient-edit.ts +10 -25
  223. package/src/runtime/result-extractor.ts +1 -2
  224. package/src/runtime/retry-executor.ts +20 -4
  225. package/src/runtime/retry-runner.ts +26 -50
  226. package/src/runtime/role-permission.ts +6 -1
  227. package/src/runtime/run-coalesced-task-group.ts +256 -0
  228. package/src/runtime/run-drift.ts +14 -15
  229. package/src/runtime/run-tracker.ts +6 -28
  230. package/src/runtime/runtime-policy.ts +1 -6
  231. package/src/runtime/runtime-resolver.ts +80 -15
  232. package/src/runtime/scheduler.ts +47 -18
  233. package/src/runtime/semaphore.ts +5 -7
  234. package/src/runtime/sensitive-paths.ts +2 -1
  235. package/src/runtime/session-usage.ts +1 -1
  236. package/src/runtime/settings-store.ts +16 -12
  237. package/src/runtime/sidechain-output.ts +6 -2
  238. package/src/runtime/single-agent-compose.ts +1 -4
  239. package/src/runtime/skill-effectiveness.ts +29 -97
  240. package/src/runtime/skill-instructions.ts +40 -83
  241. package/src/runtime/stale-reconciler.ts +73 -197
  242. package/src/runtime/stream-preview.ts +1 -1
  243. package/src/runtime/streaming-output.ts +1 -1
  244. package/src/runtime/subagent-manager.ts +44 -169
  245. package/src/runtime/subprocess-tool-registry.ts +4 -1
  246. package/src/runtime/supervisor-contact.ts +10 -5
  247. package/src/runtime/task-display.ts +19 -5
  248. package/src/runtime/task-graph-scheduler.ts +95 -21
  249. package/src/runtime/task-graph.ts +5 -11
  250. package/src/runtime/task-health.ts +54 -47
  251. package/src/runtime/task-id.ts +11 -18
  252. package/src/runtime/task-output-context.ts +265 -81
  253. package/src/runtime/task-packet.ts +12 -20
  254. package/src/runtime/task-quality.ts +6 -14
  255. package/src/runtime/task-runner/context-retrieval.ts +4 -13
  256. package/src/runtime/task-runner/live-executor.ts +114 -36
  257. package/src/runtime/task-runner/output-splitter.ts +152 -0
  258. package/src/runtime/task-runner/progress.ts +50 -12
  259. package/src/runtime/task-runner/prompt-builder.ts +31 -7
  260. package/src/runtime/task-runner/prompt-pipeline.ts +31 -7
  261. package/src/runtime/task-runner/result-utils.ts +3 -1
  262. package/src/runtime/task-runner/retrieval-orchestrator.ts +310 -0
  263. package/src/runtime/task-runner/run-projection.ts +27 -8
  264. package/src/runtime/task-runner/state-helpers.ts +29 -8
  265. package/src/runtime/task-runner/tail-read.ts +2 -6
  266. package/src/runtime/task-runner.ts +180 -326
  267. package/src/runtime/team-runner-artifacts.ts +13 -0
  268. package/src/runtime/team-runner.ts +456 -974
  269. package/src/runtime/tool-output-pruner.ts +6 -9
  270. package/src/runtime/tool-progress.ts +11 -21
  271. package/src/runtime/verification-gates.ts +33 -24
  272. package/src/runtime/verification-integrity.ts +1 -4
  273. package/src/runtime/verification-worktree.ts +62 -12
  274. package/src/runtime/worker-heartbeat.ts +5 -1
  275. package/src/runtime/worker-startup.ts +29 -5
  276. package/src/runtime/workflow-state.ts +17 -9
  277. package/src/runtime/workspace-lock.ts +30 -35
  278. package/src/runtime/workspace-tree.ts +20 -32
  279. package/src/runtime/yield-handler.ts +43 -9
  280. package/src/runtime/zombie-scanner.ts +5 -4
  281. package/src/schema/config-schema.ts +266 -172
  282. package/src/schema/team-tool-schema.ts +29 -74
  283. package/src/schema/validation-types.ts +25 -17
  284. package/src/skills/discover-skills.ts +35 -10
  285. package/src/skills/skill-templates.ts +109 -27
  286. package/src/skills/validate.ts +26 -8
  287. package/src/state/active-run-registry.ts +87 -28
  288. package/src/state/artifact-store.ts +9 -5
  289. package/src/state/atomic-write-v2.ts +85 -63
  290. package/src/state/atomic-write.ts +105 -26
  291. package/src/state/blob-store.ts +47 -20
  292. package/src/state/contracts.ts +20 -5
  293. package/src/state/crew-init.ts +5 -22
  294. package/src/state/decision-ledger.ts +19 -73
  295. package/src/state/event-log-rotation.ts +41 -15
  296. package/src/state/event-log.ts +168 -53
  297. package/src/state/event-reconstructor.ts +11 -1
  298. package/src/state/gitignore-manager.ts +2 -8
  299. package/src/state/health-store.ts +57 -57
  300. package/src/state/hook-instinct-bridge.ts +1 -1
  301. package/src/state/hook-integrations.ts +1 -1
  302. package/src/state/instinct-store.ts +17 -5
  303. package/src/state/jsonl-writer.ts +1 -1
  304. package/src/state/locks.ts +23 -9
  305. package/src/state/mailbox.ts +151 -28
  306. package/src/state/observation-store.ts +20 -14
  307. package/src/state/run-cache.ts +142 -135
  308. package/src/state/run-graph.ts +6 -15
  309. package/src/state/run-metrics.ts +5 -18
  310. package/src/state/schedule.ts +15 -9
  311. package/src/state/state-store.ts +129 -45
  312. package/src/state/task-claims.ts +12 -2
  313. package/src/state/tiered-eval.ts +52 -43
  314. package/src/state/types-eval.ts +2 -2
  315. package/src/state/types.ts +24 -20
  316. package/src/state/usage.ts +20 -4
  317. package/src/state/worker-atomic-writer.ts +6 -3
  318. package/src/subagents/index.ts +2 -2
  319. package/src/teams/discover-teams.ts +21 -6
  320. package/src/tools/safe-bash-extension.ts +5 -6
  321. package/src/tools/safe-bash.ts +10 -11
  322. package/src/types/new-api-types.ts +6 -10
  323. package/src/ui/agent-management-overlay.ts +52 -40
  324. package/src/ui/card-colors.ts +13 -4
  325. package/src/ui/crew-footer.ts +8 -7
  326. package/src/ui/crew-select-list.ts +1 -1
  327. package/src/ui/dashboard-panes/agents-pane.ts +41 -25
  328. package/src/ui/dashboard-panes/cancellation-pane.ts +1 -1
  329. package/src/ui/dashboard-panes/capability-pane.ts +32 -15
  330. package/src/ui/dashboard-panes/health-pane.ts +2 -1
  331. package/src/ui/dashboard-panes/metrics-pane.ts +4 -1
  332. package/src/ui/dashboard-panes/progress-pane.ts +12 -9
  333. package/src/ui/heartbeat-aggregator.ts +15 -4
  334. package/src/ui/keybinding-map.ts +40 -7
  335. package/src/ui/live-conversation-overlay.ts +36 -20
  336. package/src/ui/live-duration.ts +1 -4
  337. package/src/ui/live-run-sidebar.ts +88 -25
  338. package/src/ui/loaders.ts +2 -8
  339. package/src/ui/mascot.ts +20 -36
  340. package/src/ui/overlays/agent-picker-overlay.ts +11 -3
  341. package/src/ui/overlays/confirm-overlay.ts +4 -2
  342. package/src/ui/overlays/help-overlay.ts +25 -14
  343. package/src/ui/overlays/mailbox-compose-overlay.ts +48 -11
  344. package/src/ui/overlays/mailbox-compose-preview.ts +20 -5
  345. package/src/ui/overlays/mailbox-detail-overlay.ts +27 -6
  346. package/src/ui/pi-ui-compat.ts +7 -7
  347. package/src/ui/powerbar-publisher.ts +120 -42
  348. package/src/ui/render-diff.ts +10 -3
  349. package/src/ui/render-scheduler.ts +12 -4
  350. package/src/ui/run-action-dispatcher.ts +81 -18
  351. package/src/ui/run-dashboard.ts +172 -76
  352. package/src/ui/run-event-bus.ts +59 -37
  353. package/src/ui/run-snapshot-cache.ts +276 -86
  354. package/src/ui/settings-overlay.ts +361 -74
  355. package/src/ui/status-colors.ts +12 -1
  356. package/src/ui/syntax-highlight.ts +1 -1
  357. package/src/ui/theme-adapter.ts +16 -14
  358. package/src/ui/theme-discovery.ts +13 -2
  359. package/src/ui/tool-progress-formatter.ts +7 -7
  360. package/src/ui/tool-render.ts +128 -56
  361. package/src/ui/tool-renderers/brief-mode.ts +45 -27
  362. package/src/ui/tool-renderers/index.ts +109 -43
  363. package/src/ui/transcript-cache.ts +32 -6
  364. package/src/ui/transcript-entries.ts +25 -23
  365. package/src/ui/transcript-viewer.ts +78 -29
  366. package/src/ui/widget/index.ts +99 -40
  367. package/src/ui/widget/widget-formatters.ts +13 -6
  368. package/src/ui/widget/widget-model.ts +19 -8
  369. package/src/ui/widget/widget-renderer.ts +23 -13
  370. package/src/ui/widget/widget-types.ts +1 -1
  371. package/src/utils/bm25-search.ts +199 -199
  372. package/src/utils/conflict-detect.ts +22 -21
  373. package/src/utils/env-filter.ts +14 -7
  374. package/src/utils/file-coalescer.ts +5 -1
  375. package/src/utils/fingerprint.ts +3 -6
  376. package/src/utils/frontmatter.ts +3 -1
  377. package/src/utils/fs-watch.ts +2 -6
  378. package/src/utils/gh-protocol.ts +119 -44
  379. package/src/utils/git.ts +13 -15
  380. package/src/utils/guards.ts +2 -5
  381. package/src/utils/ids.ts +9 -2
  382. package/src/utils/incremental-reader.ts +14 -3
  383. package/src/utils/internal-error.ts +2 -1
  384. package/src/utils/names.ts +12 -3
  385. package/src/utils/paths.ts +49 -5
  386. package/src/utils/project-detector.ts +2 -2
  387. package/src/utils/redaction.ts +29 -19
  388. package/src/utils/resolve-shell.ts +9 -7
  389. package/src/utils/run-watcher-registry.ts +19 -31
  390. package/src/utils/safe-paths.ts +45 -24
  391. package/src/utils/scan-cache.ts +9 -2
  392. package/src/utils/session-utils.ts +2 -4
  393. package/src/utils/sleep.ts +12 -5
  394. package/src/utils/sse-parser.ts +5 -17
  395. package/src/utils/visual.ts +52 -32
  396. package/src/workflows/discover-workflows.ts +41 -109
  397. package/src/workflows/intermediate-store.ts +5 -21
  398. package/src/workflows/preflight-validator.ts +6 -33
  399. package/src/workflows/topology-analyzer.ts +5 -21
  400. package/src/workflows/workflow-config.ts +7 -6
  401. package/src/worktree/branch-freshness.ts +66 -8
  402. package/src/worktree/cleanup.ts +130 -20
  403. package/src/worktree/worktree-manager.ts +212 -69
  404. package/skills/artifact-analysis-loop/SKILL.md +0 -303
  405. package/skills/detection-pipeline-design/SKILL.md +0 -286
  406. package/skills/hunting-investigation-loop/SKILL.md +0 -402
  407. package/skills/incident-playbook-construction/SKILL.md +0 -384
  408. package/skills/security-review/SKILL.md +0 -561
  409. package/skills/threat-hypothesis-framework/SKILL.md +0 -176
  410. package/skills/ui-render-performance/SKILL.md +0 -58
  411. /package/docs/{followup-review-round3-2026-05-12.md → archive/followup-review-round3-2026-05-12.md} +0 -0
  412. /package/docs/{followup-review-round4-2026-05-13.md → archive/followup-review-round4-2026-05-13.md} +0 -0
  413. /package/docs/{pi-crew-bugs.md → archive/pi-crew-bugs.md} +0 -0
  414. /package/docs/{pi-crew-test-final.md → archive/pi-crew-test-final.md} +0 -0
  415. /package/docs/{pi-crew-test-results.md → archive/pi-crew-test-results.md} +0 -0
  416. /package/docs/{pi-crew-test-round2.md → archive/pi-crew-test-round2.md} +0 -0
  417. /package/docs/{pi-crew-test-round4.md → archive/pi-crew-test-round4.md} +0 -0
  418. /package/docs/{pi-crew-test-round5.md → archive/pi-crew-test-round5.md} +0 -0
  419. /package/docs/{pi-crew-test-round6.md → archive/pi-crew-test-round6.md} +0 -0
  420. /package/docs/{pi-crew-v0.5.10-audit-fix-plan.md → archive/pi-crew-v0.5.10-audit-fix-plan.md} +0 -0
  421. /package/docs/{pi-crew-v0.5.11-audit-fix-plan.md → archive/pi-crew-v0.5.11-audit-fix-plan.md} +0 -0
  422. /package/docs/{pi-crew-v0.5.12-audit-fix-plan.md → archive/pi-crew-v0.5.12-audit-fix-plan.md} +0 -0
  423. /package/docs/{pi-crew-v0.5.13-audit-fix-plan.md → archive/pi-crew-v0.5.13-audit-fix-plan.md} +0 -0
  424. /package/docs/{pi-crew-v0.5.14-audit-fix-plan.md → archive/pi-crew-v0.5.14-audit-fix-plan.md} +0 -0
  425. /package/docs/{pi-crew-v0.5.16-audit-fix-plan.md → archive/pi-crew-v0.5.16-audit-fix-plan.md} +0 -0
  426. /package/docs/{pi-crew-v0.5.17-audit-fix-plan.md → archive/pi-crew-v0.5.17-audit-fix-plan.md} +0 -0
  427. /package/docs/{pi-crew-v0.5.5-audit-fix-plan.md → archive/pi-crew-v0.5.5-audit-fix-plan.md} +0 -0
  428. /package/docs/{pi-crew-v0.5.9-audit-fix-plan.md → archive/pi-crew-v0.5.9-audit-fix-plan.md} +0 -0
  429. /package/docs/{pi-mono-opportunities.md → archive/pi-mono-opportunities.md} +0 -0
  430. /package/docs/{pi-mono-review.md → archive/pi-mono-review.md} +0 -0
  431. /package/docs/{pi-subagent4-comparison.md → archive/pi-subagent4-comparison.md} +0 -0
  432. /package/docs/{pi-subagents3-deep-analysis.md → archive/pi-subagents3-deep-analysis.md} +0 -0
@@ -3,12 +3,9 @@ import * as os from "node:os";
3
3
  import * as path from "node:path";
4
4
  import { type Static, type TSchema, Type } from "@sinclair/typebox";
5
5
  import { Value } from "@sinclair/typebox/value";
6
- import {
7
- PiTeamsAutonomyProfileSchema,
8
- PiTeamsConfigSchema,
9
- } from "../schema/config-schema.ts";
10
- import { withFileLockSync } from "../state/locks.ts";
6
+ import { PiTeamsAutonomyProfileSchema, PiTeamsConfigSchema } from "../schema/config-schema.ts";
11
7
  import { atomicWriteFile } from "../state/atomic-write.ts";
8
+ import { withFileLockSync } from "../state/locks.ts";
12
9
  import { logInternalError } from "../utils/internal-error.ts";
13
10
  import { projectCrewRoot, projectPiRoot } from "../utils/paths.ts";
14
11
  import { suggestConfigKey } from "./suggestions.ts";
@@ -112,14 +109,7 @@ export function configPath(): string {
112
109
  }
113
110
 
114
111
  export function legacyConfigPath(): string {
115
- return path.join(
116
- resolveHomeDir(),
117
- ".pi",
118
- "agent",
119
- "extensions",
120
- "pi-crew",
121
- "config.json",
122
- );
112
+ return path.join(resolveHomeDir(), ".pi", "agent", "extensions", "pi-crew", "config.json");
123
113
  }
124
114
 
125
115
  export function projectConfigPath(cwd: string): string {
@@ -134,23 +124,14 @@ export function projectPiCrewJsonPath(cwd: string): string {
134
124
  return path.join(projectPiRoot(cwd), "pi-crew.json");
135
125
  }
136
126
 
137
- function withoutUndefined<T extends Record<string, unknown>>(
138
- value: T,
139
- ): Partial<T> {
140
- return Object.fromEntries(
141
- Object.entries(value).filter(([, entry]) => entry !== undefined),
142
- ) as Partial<T>;
127
+ function withoutUndefined<T extends Record<string, unknown>>(value: T): Partial<T> {
128
+ return Object.fromEntries(Object.entries(value).filter(([, entry]) => entry !== undefined)) as Partial<T>;
143
129
  }
144
130
 
145
131
  function errorPathFromValidation(error: unknown): string {
146
132
  if (error && typeof error === "object") {
147
- if (typeof (error as { path?: unknown }).path === "string")
148
- return (error as { path: string }).path;
149
- if (
150
- typeof (error as { instancePath?: unknown }).instancePath ===
151
- "string"
152
- )
153
- return (error as { instancePath: string }).instancePath;
133
+ if (typeof (error as { path?: unknown }).path === "string") return (error as { path: string }).path;
134
+ if (typeof (error as { instancePath?: unknown }).instancePath === "string") return (error as { instancePath: string }).instancePath;
154
135
  if (
155
136
  typeof (error as { keyword?: unknown }).keyword === "string" &&
156
137
  typeof (error as { schemaPath?: unknown }).schemaPath === "string"
@@ -161,28 +142,18 @@ function errorPathFromValidation(error: unknown): string {
161
142
  }
162
143
 
163
144
  /** Known top-level config keys from the schema — used for fuzzy suggestions. */
164
- const KNOWN_TOP_LEVEL_KEYS = Object.keys(
165
- PiTeamsConfigSchema.properties ?? {},
166
- ) as string[];
145
+ const KNOWN_TOP_LEVEL_KEYS = Object.keys(PiTeamsConfigSchema.properties ?? {}) as string[];
167
146
 
168
147
  function validateConfigWithWarnings(raw: unknown): string[] {
169
148
  if (!Value.Check(PiTeamsConfigSchema, raw)) {
170
149
  return [...Value.Errors(PiTeamsConfigSchema, raw)].map((error) => {
171
150
  const path = errorPathFromValidation(error);
172
- const message =
173
- (error as { message?: unknown }).message ?? "invalid value";
151
+ const message = (error as { message?: unknown }).message ?? "invalid value";
174
152
  // Enhance "additionalProperties" errors with fuzzy suggestions
175
- if (
176
- (error as { keyword?: unknown }).keyword ===
177
- "additionalProperties"
178
- ) {
153
+ if ((error as { keyword?: unknown }).keyword === "additionalProperties") {
179
154
  const offendingKey = path.split("/").pop() ?? path;
180
- const suggestion = suggestConfigKey(
181
- offendingKey,
182
- KNOWN_TOP_LEVEL_KEYS,
183
- );
184
- if (suggestion)
185
- return `${path}: ${message} (did you mean '${suggestion}'?)`;
155
+ const suggestion = suggestConfigKey(offendingKey, KNOWN_TOP_LEVEL_KEYS);
156
+ if (suggestion) return `${path}: ${message} (did you mean '${suggestion}'?)`;
186
157
  }
187
158
  return `${path}: ${message}`;
188
159
  });
@@ -190,18 +161,11 @@ function validateConfigWithWarnings(raw: unknown): string[] {
190
161
  return [];
191
162
  }
192
163
 
193
- function projectOverrideWarning(
194
- projectPath: string,
195
- dottedPath: string,
196
- ): string {
164
+ function projectOverrideWarning(projectPath: string, dottedPath: string): string {
197
165
  return `${projectPath}: project-level sensitive config '${dottedPath}' is ignored; set it in user config to trust it explicitly`;
198
166
  }
199
167
 
200
- function sanitizeProjectConfig(
201
- projectPath: string,
202
- userConfig: PiTeamsConfig,
203
- config: PiTeamsConfig,
204
- ): ConfigValidationResult {
168
+ function sanitizeProjectConfig(projectPath: string, userConfig: PiTeamsConfig, config: PiTeamsConfig): ConfigValidationResult {
205
169
  const sanitized: PiTeamsConfig = { ...config };
206
170
  const warnings: string[] = [];
207
171
  const dropTopLevel = (key: keyof PiTeamsConfig): void => {
@@ -214,73 +178,36 @@ function sanitizeProjectConfig(
214
178
  dropTopLevel("requireCleanWorktreeLeader");
215
179
  if (config.runtime) {
216
180
  const runtime = { ...config.runtime };
217
- for (const key of [
218
- "mode",
219
- "preferLiveSession",
220
- "allowChildProcessFallback",
221
- "inheritContext",
222
- "isolationPolicy",
223
- ] as const) {
181
+ for (const key of ["mode", "preferLiveSession", "allowChildProcessFallback", "inheritContext", "isolationPolicy"] as const) {
224
182
  if (runtime[key] !== undefined) {
225
183
  delete runtime[key];
226
- warnings.push(
227
- projectOverrideWarning(projectPath, `runtime.${key}`),
228
- );
184
+ warnings.push(projectOverrideWarning(projectPath, `runtime.${key}`));
229
185
  }
230
186
  }
231
187
  if (runtime.requirePlanApproval === false) {
232
188
  delete runtime.requirePlanApproval;
233
- warnings.push(
234
- projectOverrideWarning(
235
- projectPath,
236
- "runtime.requirePlanApproval",
237
- ),
238
- );
189
+ warnings.push(projectOverrideWarning(projectPath, "runtime.requirePlanApproval"));
239
190
  }
240
- sanitized.runtime = Object.values(runtime).some(
241
- (entry) => entry !== undefined,
242
- )
243
- ? runtime
244
- : undefined;
191
+ sanitized.runtime = Object.values(runtime).some((entry) => entry !== undefined) ? runtime : undefined;
245
192
  }
246
193
  if (config.autonomous) {
247
194
  const autonomous = { ...config.autonomous };
248
- for (const key of [
249
- "profile",
250
- "enabled",
251
- "injectPolicy",
252
- "preferAsyncForLongTasks",
253
- "allowWorktreeSuggestion",
254
- ] as const) {
195
+ for (const key of ["profile", "enabled", "injectPolicy", "preferAsyncForLongTasks", "allowWorktreeSuggestion"] as const) {
255
196
  if (autonomous[key] !== undefined) {
256
197
  delete autonomous[key];
257
- warnings.push(
258
- projectOverrideWarning(projectPath, `autonomous.${key}`),
259
- );
198
+ warnings.push(projectOverrideWarning(projectPath, `autonomous.${key}`));
260
199
  }
261
200
  }
262
- sanitized.autonomous = Object.values(autonomous).some(
263
- (entry) => entry !== undefined,
264
- )
265
- ? autonomous
266
- : undefined;
201
+ sanitized.autonomous = Object.values(autonomous).some((entry) => entry !== undefined) ? autonomous : undefined;
267
202
  }
268
203
  if (config.worktree?.setupHook !== undefined) {
269
204
  sanitized.worktree = { ...config.worktree, setupHook: undefined };
270
- if (
271
- !Object.values(sanitized.worktree).some(
272
- (entry) => entry !== undefined,
273
- )
274
- )
275
- sanitized.worktree = undefined;
276
- warnings.push(
277
- projectOverrideWarning(projectPath, "worktree.setupHook"),
278
- );
205
+ if (!Object.values(sanitized.worktree).some((entry) => entry !== undefined)) sanitized.worktree = undefined;
206
+ warnings.push(projectOverrideWarning(projectPath, "worktree.setupHook"));
279
207
  }
280
208
  if (config.otlp?.headers !== undefined) {
281
209
  sanitized.otlp = { ...config.otlp, headers: undefined };
282
- if (!Object.values(sanitized.otlp).some((entry) => entry !== undefined))
283
- sanitized.otlp = undefined;
210
+ if (!Object.values(sanitized.otlp).some((entry) => entry !== undefined)) sanitized.otlp = undefined;
284
211
  warnings.push(projectOverrideWarning(projectPath, "otlp.headers"));
285
212
  }
286
213
  // FIX: Block project config from setting otlp.endpoint — it controls where
@@ -288,66 +215,37 @@ function sanitizeProjectConfig(
288
215
  if (config.otlp?.endpoint !== undefined) {
289
216
  if (!sanitized.otlp) sanitized.otlp = { ...config.otlp, endpoint: undefined };
290
217
  else sanitized.otlp = { ...sanitized.otlp, endpoint: undefined };
291
- if (!Object.values(sanitized.otlp).some((entry) => entry !== undefined))
292
- sanitized.otlp = undefined;
218
+ if (!Object.values(sanitized.otlp).some((entry) => entry !== undefined)) sanitized.otlp = undefined;
293
219
  warnings.push(projectOverrideWarning(projectPath, "otlp.endpoint"));
294
220
  }
295
- if (
296
- config.agents?.disableBuiltins !== undefined ||
297
- config.agents?.overrides !== undefined
298
- ) {
221
+ if (config.agents?.disableBuiltins !== undefined || config.agents?.overrides !== undefined) {
299
222
  const agents = { ...config.agents };
300
223
  if (agents.disableBuiltins !== undefined) {
301
224
  delete agents.disableBuiltins;
302
- warnings.push(
303
- projectOverrideWarning(projectPath, "agents.disableBuiltins"),
304
- );
225
+ warnings.push(projectOverrideWarning(projectPath, "agents.disableBuiltins"));
305
226
  }
306
227
  if (agents.overrides !== undefined) {
307
228
  delete agents.overrides;
308
- warnings.push(
309
- projectOverrideWarning(projectPath, "agents.overrides"),
310
- );
229
+ warnings.push(projectOverrideWarning(projectPath, "agents.overrides"));
311
230
  }
312
- sanitized.agents = Object.values(agents).some(
313
- (entry) => entry !== undefined,
314
- )
315
- ? agents
316
- : undefined;
231
+ sanitized.agents = Object.values(agents).some((entry) => entry !== undefined) ? agents : undefined;
317
232
  }
318
- if (
319
- config.tools?.enableSteer !== undefined ||
320
- config.tools?.terminateOnForeground !== undefined
321
- ) {
233
+ if (config.tools?.enableSteer !== undefined || config.tools?.terminateOnForeground !== undefined) {
322
234
  const tools = { ...config.tools };
323
235
  if (tools.enableSteer !== undefined) {
324
236
  delete tools.enableSteer;
325
- warnings.push(
326
- projectOverrideWarning(projectPath, "tools.enableSteer"),
327
- );
237
+ warnings.push(projectOverrideWarning(projectPath, "tools.enableSteer"));
328
238
  }
329
239
  if (tools.terminateOnForeground !== undefined) {
330
240
  delete tools.terminateOnForeground;
331
- warnings.push(
332
- projectOverrideWarning(
333
- projectPath,
334
- "tools.terminateOnForeground",
335
- ),
336
- );
241
+ warnings.push(projectOverrideWarning(projectPath, "tools.terminateOnForeground"));
337
242
  }
338
- sanitized.tools = Object.values(tools).some(
339
- (entry) => entry !== undefined,
340
- )
341
- ? tools
342
- : undefined;
243
+ sanitized.tools = Object.values(tools).some((entry) => entry !== undefined) ? tools : undefined;
343
244
  }
344
245
  return { config: sanitized, warnings };
345
246
  }
346
247
 
347
- function mergeConfig(
348
- base: PiTeamsConfig,
349
- override: PiTeamsConfig,
350
- ): PiTeamsConfig {
248
+ function mergeConfig(base: PiTeamsConfig, override: PiTeamsConfig): PiTeamsConfig {
351
249
  const warnings: string[] = [];
352
250
  const merged: PiTeamsConfig = {
353
251
  ...base,
@@ -356,41 +254,31 @@ function mergeConfig(
356
254
  if (base.autonomous || override.autonomous) {
357
255
  merged.autonomous = {
358
256
  ...(base.autonomous ?? {}),
359
- ...withoutUndefined(
360
- (override.autonomous ?? {}) as Record<string, unknown>,
361
- ),
257
+ ...withoutUndefined((override.autonomous ?? {}) as Record<string, unknown>),
362
258
  };
363
259
  }
364
260
  if (base.limits || override.limits) {
365
261
  merged.limits = {
366
262
  ...(base.limits ?? {}),
367
- ...withoutUndefined(
368
- (override.limits ?? {}) as Record<string, unknown>,
369
- ),
263
+ ...withoutUndefined((override.limits ?? {}) as Record<string, unknown>),
370
264
  };
371
265
  }
372
266
  if (base.runtime || override.runtime) {
373
267
  merged.runtime = {
374
268
  ...(base.runtime ?? {}),
375
- ...withoutUndefined(
376
- (override.runtime ?? {}) as Record<string, unknown>,
377
- ),
269
+ ...withoutUndefined((override.runtime ?? {}) as Record<string, unknown>),
378
270
  };
379
271
  }
380
272
  if (base.control || override.control) {
381
273
  merged.control = {
382
274
  ...(base.control ?? {}),
383
- ...withoutUndefined(
384
- (override.control ?? {}) as Record<string, unknown>,
385
- ),
275
+ ...withoutUndefined((override.control ?? {}) as Record<string, unknown>),
386
276
  };
387
277
  }
388
278
  if (base.worktree || override.worktree) {
389
279
  merged.worktree = {
390
280
  ...(base.worktree ?? {}),
391
- ...withoutUndefined(
392
- (override.worktree ?? {}) as Record<string, unknown>,
393
- ),
281
+ ...withoutUndefined((override.worktree ?? {}) as Record<string, unknown>),
394
282
  };
395
283
  }
396
284
  if (base.ui || override.ui) {
@@ -402,75 +290,52 @@ function mergeConfig(
402
290
  if (base.agents || override.agents) {
403
291
  merged.agents = {
404
292
  ...(base.agents ?? {}),
405
- ...withoutUndefined(
406
- (override.agents ?? {}) as Record<string, unknown>,
407
- ),
293
+ ...withoutUndefined((override.agents ?? {}) as Record<string, unknown>),
408
294
  overrides: {
409
295
  ...(base.agents?.overrides ?? {}),
410
- ...(withoutUndefined(
411
- (override.agents?.overrides ?? {}) as Record<
412
- string,
413
- unknown
414
- >,
415
- ) as Record<string, AgentOverrideConfig>),
296
+ ...(withoutUndefined((override.agents?.overrides ?? {}) as Record<string, unknown>) as Record<string, AgentOverrideConfig>),
416
297
  },
417
298
  };
418
299
  }
419
300
  if (base.tools || override.tools) {
420
301
  merged.tools = {
421
302
  ...(base.tools ?? {}),
422
- ...withoutUndefined(
423
- (override.tools ?? {}) as Record<string, unknown>,
424
- ),
303
+ ...withoutUndefined((override.tools ?? {}) as Record<string, unknown>),
425
304
  };
426
305
  }
427
306
  if (base.telemetry || override.telemetry) {
428
307
  merged.telemetry = {
429
308
  ...(base.telemetry ?? {}),
430
- ...withoutUndefined(
431
- (override.telemetry ?? {}) as Record<string, unknown>,
432
- ),
309
+ ...withoutUndefined((override.telemetry ?? {}) as Record<string, unknown>),
433
310
  };
434
311
  }
435
312
  if (base.policy || override.policy) {
436
313
  merged.policy = {
437
314
  ...(base.policy ?? {}),
438
- ...withoutUndefined(
439
- (override.policy ?? {}) as Record<string, unknown>,
440
- ),
315
+ ...withoutUndefined((override.policy ?? {}) as Record<string, unknown>),
441
316
  };
442
317
  }
443
318
  if (base.notifications || override.notifications) {
444
319
  merged.notifications = {
445
320
  ...(base.notifications ?? {}),
446
- ...withoutUndefined(
447
- (override.notifications ?? {}) as Record<string, unknown>,
448
- ),
321
+ ...withoutUndefined((override.notifications ?? {}) as Record<string, unknown>),
449
322
  };
450
323
  }
451
324
  if (base.observability || override.observability) {
452
325
  merged.observability = {
453
326
  ...(base.observability ?? {}),
454
- ...withoutUndefined(
455
- (override.observability ?? {}) as Record<string, unknown>,
456
- ),
327
+ ...withoutUndefined((override.observability ?? {}) as Record<string, unknown>),
457
328
  };
458
329
  }
459
330
  if (base.reliability || override.reliability) {
460
331
  merged.reliability = {
461
332
  ...(base.reliability ?? {}),
462
- ...withoutUndefined(
463
- (override.reliability ?? {}) as Record<string, unknown>,
464
- ),
333
+ ...withoutUndefined((override.reliability ?? {}) as Record<string, unknown>),
465
334
  retryPolicy:
466
- base.reliability?.retryPolicy ||
467
- override.reliability?.retryPolicy
335
+ base.reliability?.retryPolicy || override.reliability?.retryPolicy
468
336
  ? {
469
337
  ...(base.reliability?.retryPolicy ?? {}),
470
- ...withoutUndefined(
471
- (override.reliability?.retryPolicy ??
472
- {}) as Record<string, unknown>,
473
- ),
338
+ ...withoutUndefined((override.reliability?.retryPolicy ?? {}) as Record<string, unknown>),
474
339
  }
475
340
  : undefined,
476
341
  };
@@ -478,16 +343,13 @@ function mergeConfig(
478
343
  if (base.otlp || override.otlp) {
479
344
  merged.otlp = {
480
345
  ...(base.otlp ?? {}),
481
- ...withoutUndefined(
482
- (override.otlp ?? {}) as Record<string, unknown>,
483
- ),
346
+ ...withoutUndefined((override.otlp ?? {}) as Record<string, unknown>),
484
347
  headers: {
485
348
  ...(base.otlp?.headers ?? {}),
486
349
  ...(override.otlp?.headers ?? {}),
487
350
  },
488
351
  };
489
- if (Object.keys(merged.otlp.headers ?? {}).length === 0)
490
- delete merged.otlp.headers;
352
+ if (Object.keys(merged.otlp.headers ?? {}).length === 0) delete merged.otlp.headers;
491
353
  // Validate OTLP headers for injection attacks:
492
354
  // - Check top-level keys for dangerous prototype pollution patterns
493
355
  // - Block ALL control characters except tab (0x09) to prevent header
@@ -506,22 +368,23 @@ function mergeConfig(
506
368
  if (DANGEROUS_OBJECT_KEYS.has(lowerKey)) return true;
507
369
  return false;
508
370
  };
509
- if (checkKey(k)) { invalidHeaders.push(k); continue; }
371
+ if (checkKey(k)) {
372
+ invalidHeaders.push(k);
373
+ continue;
374
+ }
510
375
  // Block any control characters except tab (0x09) in values.
511
376
  // Round 28 fix: /[\x00-\x08\x0a-\x1f]/ blocks LF (0x0A) and CR (0x0D) too.
512
377
  const valStr = String(v);
513
- if (/[\x00-\x08\x0a-\x1f]/.test(valStr)) { invalidHeaders.push(k); }
378
+ if (/[\x00-\x08\x0a-\x1f]/.test(valStr)) {
379
+ invalidHeaders.push(k);
380
+ }
514
381
  }
515
382
  if (invalidHeaders.length > 0) {
516
383
  delete merged.otlp.headers;
517
384
  warnings.push(`OTLP headers blocked due to invalid characters: ${invalidHeaders.join(", ")}`);
518
385
  }
519
386
  }
520
- if (
521
- merged.agents?.overrides &&
522
- Object.keys(merged.agents.overrides).length === 0
523
- )
524
- delete merged.agents.overrides;
387
+ if (merged.agents?.overrides && Object.keys(merged.agents.overrides).length === 0) delete merged.agents.overrides;
525
388
  return merged;
526
389
  }
527
390
 
@@ -580,8 +443,7 @@ function sanitizeObject(obj: Record<string, unknown>): Record<string, unknown> {
580
443
  }
581
444
 
582
445
  export function asRecord(value: unknown): Record<string, unknown> | undefined {
583
- if (!value || typeof value !== "object" || Array.isArray(value))
584
- return undefined;
446
+ if (!value || typeof value !== "object" || Array.isArray(value)) return undefined;
585
447
  // Defensive: create a sanitized copy to prevent prototype pollution.
586
448
  // Uses Object.create(null) so the result has no prototype chain.
587
449
  // WARNING: The returned object has no prototype methods (no hasOwnProperty,
@@ -590,11 +452,7 @@ export function asRecord(value: unknown): Record<string, unknown> | undefined {
590
452
  return sanitizeObject(value as Record<string, unknown>);
591
453
  }
592
454
 
593
- function parseWithSchema<T extends TSchema>(
594
- schema: T,
595
- value: unknown,
596
- context?: string,
597
- ): Static<T> | undefined {
455
+ function parseWithSchema<T extends TSchema>(schema: T, value: unknown, context?: string): Static<T> | undefined {
598
456
  if (!Value.Check(schema, value)) {
599
457
  if (context) {
600
458
  logInternalError("config.parseWithSchema", undefined, `${context}: schema validation failed`);
@@ -604,18 +462,11 @@ function parseWithSchema<T extends TSchema>(
604
462
  return Value.Decode(schema, value);
605
463
  }
606
464
 
607
- function parseIntegerInRange(
608
- value: unknown,
609
- minimum = 1,
610
- maximum = Number.MAX_SAFE_INTEGER,
611
- ): number | undefined {
465
+ function parseIntegerInRange(value: unknown, minimum = 1, maximum = Number.MAX_SAFE_INTEGER): number | undefined {
612
466
  return parseWithSchema(Type.Integer({ minimum, maximum }), value);
613
467
  }
614
468
 
615
- function parsePositiveInteger(
616
- value: unknown,
617
- max = Number.MAX_SAFE_INTEGER,
618
- ): number | undefined {
469
+ function parsePositiveInteger(value: unknown, max = Number.MAX_SAFE_INTEGER): number | undefined {
619
470
  return parseIntegerInRange(value, 1, max);
620
471
  }
621
472
 
@@ -626,34 +477,21 @@ function parseProfile(value: unknown): PiTeamsAutonomyProfile | undefined {
626
477
  function parseStringList(value: unknown): string[] | undefined {
627
478
  const items = parseWithSchema(Type.Array(Type.String()), value);
628
479
  if (!items || items.length === 0) return undefined;
629
- const normalized = items
630
- .map((entry) => entry.trim())
631
- .filter((entry) => entry.length > 0);
480
+ const normalized = items.map((entry) => entry.trim()).filter((entry) => entry.length > 0);
632
481
  return normalized.length > 0 ? normalized : undefined;
633
482
  }
634
483
 
635
484
  function parseStringArrayOrFalse(value: unknown): string[] | false | undefined {
636
485
  if (value === false) return false;
637
- if (typeof value === "string")
638
- return value.trim() === "" ? [] : parseStringList(value.split(","));
486
+ if (typeof value === "string") return value.trim() === "" ? [] : parseStringList(value.split(","));
639
487
  return parseStringList(value);
640
488
  }
641
489
 
642
490
  export function effectiveAutonomousConfig(
643
491
  config: PiTeamsAutonomousConfig | undefined,
644
- ): Required<
645
- Pick<
646
- PiTeamsAutonomousConfig,
647
- | "profile"
648
- | "enabled"
649
- | "injectPolicy"
650
- | "preferAsyncForLongTasks"
651
- | "allowWorktreeSuggestion"
652
- >
653
- > &
492
+ ): Required<Pick<PiTeamsAutonomousConfig, "profile" | "enabled" | "injectPolicy" | "preferAsyncForLongTasks" | "allowWorktreeSuggestion">> &
654
493
  Pick<PiTeamsAutonomousConfig, "magicKeywords"> {
655
- const profile =
656
- config?.enabled === false ? "manual" : (config?.profile ?? "suggested");
494
+ const profile = config?.enabled === false ? "manual" : (config?.profile ?? "suggested");
657
495
  const profileDefaults: Record<
658
496
  PiTeamsAutonomyProfile,
659
497
  {
@@ -693,21 +531,14 @@ export function effectiveAutonomousConfig(
693
531
  profile,
694
532
  enabled: config?.enabled ?? defaults.enabled,
695
533
  injectPolicy: config?.injectPolicy ?? defaults.injectPolicy,
696
- preferAsyncForLongTasks:
697
- config?.preferAsyncForLongTasks ?? defaults.preferAsyncForLongTasks,
698
- allowWorktreeSuggestion:
699
- config?.allowWorktreeSuggestion ?? defaults.allowWorktreeSuggestion,
534
+ preferAsyncForLongTasks: config?.preferAsyncForLongTasks ?? defaults.preferAsyncForLongTasks,
535
+ allowWorktreeSuggestion: config?.allowWorktreeSuggestion ?? defaults.allowWorktreeSuggestion,
700
536
  magicKeywords: config?.magicKeywords,
701
537
  };
702
538
  }
703
539
 
704
- function parseStringArrayRecord(
705
- value: unknown,
706
- ): Record<string, string[]> | undefined {
707
- const record = parseWithSchema(
708
- Type.Record(Type.String({ minLength: 1 }), Type.Array(Type.String())),
709
- value,
710
- );
540
+ function parseStringArrayRecord(value: unknown): Record<string, string[]> | undefined {
541
+ const record = parseWithSchema(Type.Record(Type.String({ minLength: 1 }), Type.Array(Type.String())), value);
711
542
  if (!record) return undefined;
712
543
  const result: Record<string, string[]> = {};
713
544
  for (const [key, rawValues] of Object.entries(record)) {
@@ -717,87 +548,42 @@ function parseStringArrayRecord(
717
548
  return Object.keys(result).length > 0 ? result : undefined;
718
549
  }
719
550
 
720
- function parseAutonomousConfig(
721
- value: unknown,
722
- ): PiTeamsAutonomousConfig | undefined {
551
+ function parseAutonomousConfig(value: unknown): PiTeamsAutonomousConfig | undefined {
723
552
  const obj = asRecord(value);
724
553
  if (!obj) return undefined;
725
554
  const config: PiTeamsAutonomousConfig = {
726
555
  profile: parseProfile(obj.profile),
727
556
  enabled: parseWithSchema(Type.Boolean(), obj.enabled),
728
557
  injectPolicy: parseWithSchema(Type.Boolean(), obj.injectPolicy),
729
- preferAsyncForLongTasks: parseWithSchema(
730
- Type.Boolean(),
731
- obj.preferAsyncForLongTasks,
732
- ),
733
- allowWorktreeSuggestion: parseWithSchema(
734
- Type.Boolean(),
735
- obj.allowWorktreeSuggestion,
736
- ),
558
+ preferAsyncForLongTasks: parseWithSchema(Type.Boolean(), obj.preferAsyncForLongTasks),
559
+ allowWorktreeSuggestion: parseWithSchema(Type.Boolean(), obj.allowWorktreeSuggestion),
737
560
  magicKeywords: parseStringArrayRecord(obj.magicKeywords),
738
561
  };
739
- return Object.values(config).some((entry) => entry !== undefined)
740
- ? config
741
- : undefined;
562
+ return Object.values(config).some((entry) => entry !== undefined) ? config : undefined;
742
563
  }
743
564
 
744
565
  function parseLimitsConfig(value: unknown): CrewLimitsConfig | undefined {
745
566
  const obj = asRecord(value);
746
567
  if (!obj) return undefined;
747
568
  const limits: CrewLimitsConfig = {
748
- maxConcurrentWorkers: parsePositiveInteger(
749
- obj.maxConcurrentWorkers,
750
- LIMIT_CEILINGS.maxConcurrentWorkers,
751
- ),
752
- allowUnboundedConcurrency: parseWithSchema(
753
- Type.Boolean(),
754
- obj.allowUnboundedConcurrency,
755
- ),
756
- maxTaskDepth: parsePositiveInteger(
757
- obj.maxTaskDepth,
758
- LIMIT_CEILINGS.maxTaskDepth,
759
- ),
760
- maxChildrenPerTask: parsePositiveInteger(
761
- obj.maxChildrenPerTask,
762
- LIMIT_CEILINGS.maxChildrenPerTask,
763
- ),
764
- maxRunMinutes: parsePositiveInteger(
765
- obj.maxRunMinutes,
766
- LIMIT_CEILINGS.maxRunMinutes,
767
- ),
768
- maxRetriesPerTask: parsePositiveInteger(
769
- obj.maxRetriesPerTask,
770
- LIMIT_CEILINGS.maxRetriesPerTask,
771
- ),
772
- maxTasksPerRun: parsePositiveInteger(
773
- obj.maxTasksPerRun,
774
- LIMIT_CEILINGS.maxTasksPerRun,
775
- ),
776
- heartbeatStaleMs: parsePositiveInteger(
777
- obj.heartbeatStaleMs,
778
- LIMIT_CEILINGS.heartbeatStaleMs,
779
- ),
569
+ maxConcurrentWorkers: parsePositiveInteger(obj.maxConcurrentWorkers, LIMIT_CEILINGS.maxConcurrentWorkers),
570
+ allowUnboundedConcurrency: parseWithSchema(Type.Boolean(), obj.allowUnboundedConcurrency),
571
+ maxTaskDepth: parsePositiveInteger(obj.maxTaskDepth, LIMIT_CEILINGS.maxTaskDepth),
572
+ maxChildrenPerTask: parsePositiveInteger(obj.maxChildrenPerTask, LIMIT_CEILINGS.maxChildrenPerTask),
573
+ maxRunMinutes: parsePositiveInteger(obj.maxRunMinutes, LIMIT_CEILINGS.maxRunMinutes),
574
+ maxRetriesPerTask: parsePositiveInteger(obj.maxRetriesPerTask, LIMIT_CEILINGS.maxRetriesPerTask),
575
+ maxTasksPerRun: parsePositiveInteger(obj.maxTasksPerRun, LIMIT_CEILINGS.maxTasksPerRun),
576
+ heartbeatStaleMs: parsePositiveInteger(obj.heartbeatStaleMs, LIMIT_CEILINGS.heartbeatStaleMs),
780
577
  };
781
- return Object.values(limits).some((entry) => entry !== undefined)
782
- ? limits
783
- : undefined;
578
+ return Object.values(limits).some((entry) => entry !== undefined) ? limits : undefined;
784
579
  }
785
580
 
786
- function parseIsolationPolicy(
787
- value: unknown,
788
- ): CrewRuntimeConfig["isolationPolicy"] | undefined {
581
+ function parseIsolationPolicy(value: unknown): CrewRuntimeConfig["isolationPolicy"] | undefined {
789
582
  const obj = asRecord(value);
790
583
  if (!obj) return undefined;
791
584
  const isolatedRoles = parseStringList(obj.isolatedRoles);
792
- const defaultRuntime = parseWithSchema(
793
- Type.Union([
794
- Type.Literal("live-session"),
795
- Type.Literal("child-process"),
796
- ]),
797
- obj.defaultRuntime,
798
- );
799
- if (isolatedRoles === undefined && defaultRuntime === undefined)
800
- return undefined;
585
+ const defaultRuntime = parseWithSchema(Type.Union([Type.Literal("live-session"), Type.Literal("child-process")]), obj.defaultRuntime);
586
+ if (isolatedRoles === undefined && defaultRuntime === undefined) return undefined;
801
587
  return {
802
588
  ...(isolatedRoles !== undefined ? { isolatedRoles } : {}),
803
589
  ...(defaultRuntime !== undefined ? { defaultRuntime } : {}),
@@ -809,73 +595,29 @@ function parseRuntimeConfig(value: unknown): CrewRuntimeConfig | undefined {
809
595
  if (!obj) return undefined;
810
596
  const runtime: CrewRuntimeConfig = {
811
597
  mode: parseWithSchema(
812
- Type.Union([
813
- Type.Literal("auto"),
814
- Type.Literal("scaffold"),
815
- Type.Literal("child-process"),
816
- Type.Literal("live-session"),
817
- ]),
598
+ Type.Union([Type.Literal("auto"), Type.Literal("scaffold"), Type.Literal("child-process"), Type.Literal("live-session")]),
818
599
  obj.mode,
819
600
  ),
820
- preferLiveSession: parseWithSchema(
821
- Type.Boolean(),
822
- obj.preferLiveSession,
823
- ),
824
- allowChildProcessFallback: parseWithSchema(
825
- Type.Boolean(),
826
- obj.allowChildProcessFallback,
827
- ),
828
- maxTurns: parsePositiveInteger(
829
- obj.maxTurns,
830
- LIMIT_CEILINGS.runtimeMaxTurns,
831
- ),
832
- graceTurns: parsePositiveInteger(
833
- obj.graceTurns,
834
- LIMIT_CEILINGS.runtimeGraceTurns,
835
- ),
601
+ preferLiveSession: parseWithSchema(Type.Boolean(), obj.preferLiveSession),
602
+ allowChildProcessFallback: parseWithSchema(Type.Boolean(), obj.allowChildProcessFallback),
603
+ maxTurns: parsePositiveInteger(obj.maxTurns, LIMIT_CEILINGS.runtimeMaxTurns),
604
+ graceTurns: parsePositiveInteger(obj.graceTurns, LIMIT_CEILINGS.runtimeGraceTurns),
836
605
  inheritContext: parseWithSchema(Type.Boolean(), obj.inheritContext),
837
- promptMode: parseWithSchema(
838
- Type.Union([Type.Literal("replace"), Type.Literal("append")]),
839
- obj.promptMode,
840
- ),
841
- groupJoin: parseWithSchema(
842
- Type.Union([
843
- Type.Literal("off"),
844
- Type.Literal("group"),
845
- Type.Literal("smart"),
846
- ]),
847
- obj.groupJoin,
848
- ),
849
- groupJoinAckTimeoutMs: parsePositiveInteger(
850
- obj.groupJoinAckTimeoutMs,
851
- 86_400_000,
852
- ),
853
- requirePlanApproval: parseWithSchema(
854
- Type.Boolean(),
855
- obj.requirePlanApproval,
856
- ),
606
+ promptMode: parseWithSchema(Type.Union([Type.Literal("replace"), Type.Literal("append")]), obj.promptMode),
607
+ groupJoin: parseWithSchema(Type.Union([Type.Literal("off"), Type.Literal("group"), Type.Literal("smart")]), obj.groupJoin),
608
+ groupJoinAckTimeoutMs: parsePositiveInteger(obj.groupJoinAckTimeoutMs, 86_400_000),
609
+ requirePlanApproval: parseWithSchema(Type.Boolean(), obj.requirePlanApproval),
857
610
  completionMutationGuard: parseWithSchema(
858
- Type.Union([
859
- Type.Literal("off"),
860
- Type.Literal("warn"),
861
- Type.Literal("fail"),
862
- ]),
611
+ Type.Union([Type.Literal("off"), Type.Literal("warn"), Type.Literal("fail")]),
863
612
  obj.completionMutationGuard,
864
613
  ),
865
614
  effectivenessGuard: parseWithSchema(
866
- Type.Union([
867
- Type.Literal("off"),
868
- Type.Literal("warn"),
869
- Type.Literal("block"),
870
- Type.Literal("fail"),
871
- ]),
615
+ Type.Union([Type.Literal("off"), Type.Literal("warn"), Type.Literal("block"), Type.Literal("fail")]),
872
616
  obj.effectivenessGuard,
873
617
  ),
874
618
  isolationPolicy: parseIsolationPolicy(obj.isolationPolicy),
875
619
  };
876
- return Object.values(runtime).some((entry) => entry !== undefined)
877
- ? runtime
878
- : undefined;
620
+ return Object.values(runtime).some((entry) => entry !== undefined) ? runtime : undefined;
879
621
  }
880
622
 
881
623
  function parseControlConfig(value: unknown): CrewControlConfig | undefined {
@@ -885,9 +627,7 @@ function parseControlConfig(value: unknown): CrewControlConfig | undefined {
885
627
  enabled: parseWithSchema(Type.Boolean(), obj.enabled),
886
628
  needsAttentionAfterMs: parsePositiveInteger(obj.needsAttentionAfterMs),
887
629
  };
888
- return Object.values(control).some((entry) => entry !== undefined)
889
- ? control
890
- : undefined;
630
+ return Object.values(control).some((entry) => entry !== undefined) ? control : undefined;
891
631
  }
892
632
 
893
633
  function parseWorktreeConfig(value: unknown): CrewWorktreeConfig | undefined {
@@ -897,21 +637,14 @@ function parseWorktreeConfig(value: unknown): CrewWorktreeConfig | undefined {
897
637
  const setupHook = rawSetupHook?.trim();
898
638
  const worktree: CrewWorktreeConfig = {
899
639
  setupHook: setupHook ? setupHook : undefined,
900
- setupHookTimeoutMs: parsePositiveInteger(
901
- obj.setupHookTimeoutMs,
902
- 300_000,
903
- ),
640
+ setupHookTimeoutMs: parsePositiveInteger(obj.setupHookTimeoutMs, 300_000),
904
641
  linkNodeModules: parseWithSchema(Type.Boolean(), obj.linkNodeModules),
905
642
  };
906
- return Object.values(worktree).some((entry) => entry !== undefined)
907
- ? worktree
908
- : undefined;
643
+ return Object.values(worktree).some((entry) => entry !== undefined) ? worktree : undefined;
909
644
  }
910
645
 
911
646
  /** Parse goalWrap config (RFC v0.5 vision: apply goal completion-guarantee to builtins). */
912
- function parseGoalWrapConfig(
913
- value: unknown,
914
- ): Record<string, GoalWrapWorkflowConfig> | undefined {
647
+ function parseGoalWrapConfig(value: unknown): Record<string, GoalWrapWorkflowConfig> | undefined {
915
648
  const obj = asRecord(value);
916
649
  if (!obj) return undefined;
917
650
  const result: Record<string, GoalWrapWorkflowConfig> = {};
@@ -932,9 +665,12 @@ function parseGoalWrapConfig(
932
665
  const commands = Array.isArray(verObj.commands)
933
666
  ? verObj.commands.filter((c): c is string => typeof c === "string" && c.length > 0)
934
667
  : undefined;
935
- const mode = verObj.mode === "text-only" ? "text-only" as const : undefined;
668
+ const mode = verObj.mode === "text-only" ? ("text-only" as const) : undefined;
936
669
  if (commands || mode) {
937
- parsed.verification = { ...(commands ? { commands } : { commands: [] }), ...(mode ? { mode } : {}) };
670
+ parsed.verification = {
671
+ ...(commands ? { commands } : { commands: [] }),
672
+ ...(mode ? { mode } : {}),
673
+ };
938
674
  }
939
675
  }
940
676
  if (Object.values(parsed).some((v) => v !== undefined)) {
@@ -950,65 +686,34 @@ function parseAgentOverride(value: unknown): AgentOverrideConfig | undefined {
950
686
  if (!obj) return undefined;
951
687
  const override: AgentOverrideConfig = {
952
688
  disabled: parseWithSchema(Type.Boolean(), obj.disabled),
953
- model: parseWithSchema(
954
- Type.Union([Type.String(), Type.Literal(false)]),
955
- obj.model,
956
- ),
689
+ model: parseWithSchema(Type.Union([Type.String(), Type.Literal(false)]), obj.model),
957
690
  fallbackModels: parseStringArrayOrFalse(obj.fallbackModels),
958
- thinking: parseWithSchema(
959
- Type.Union([Type.String(), Type.Literal(false)]),
960
- obj.thinking,
961
- ),
691
+ thinking: parseWithSchema(Type.Union([Type.String(), Type.Literal(false)]), obj.thinking),
962
692
  tools: parseStringArrayOrFalse(obj.tools),
963
693
  skills: parseStringArrayOrFalse(obj.skills),
964
694
  };
965
- return Object.values(override).some((entry) => entry !== undefined)
966
- ? override
967
- : undefined;
695
+ return Object.values(override).some((entry) => entry !== undefined) ? override : undefined;
968
696
  }
969
697
 
970
698
  function parseUiConfig(value: unknown): CrewUiConfig | undefined {
971
699
  const obj = asRecord(value);
972
700
  if (!obj) return undefined;
973
- const rawWidgetPlacement = parseWithSchema(
974
- Type.Union([Type.Literal("aboveEditor"), Type.Literal("belowEditor")]),
975
- obj.widgetPlacement,
976
- );
977
- const rawDashboardPlacement = parseWithSchema(
978
- Type.Union([Type.Literal("center"), Type.Literal("right")]),
979
- obj.dashboardPlacement,
980
- );
701
+ const rawWidgetPlacement = parseWithSchema(Type.Union([Type.Literal("aboveEditor"), Type.Literal("belowEditor")]), obj.widgetPlacement);
702
+ const rawDashboardPlacement = parseWithSchema(Type.Union([Type.Literal("center"), Type.Literal("right")]), obj.dashboardPlacement);
981
703
  const ui: CrewUiConfig = {
982
704
  widgetPlacement: rawWidgetPlacement,
983
705
  widgetMaxLines: parsePositiveInteger(obj.widgetMaxLines, 50),
984
706
  powerbar: parseWithSchema(Type.Boolean(), obj.powerbar),
985
707
  dashboardPlacement: rawDashboardPlacement,
986
708
  dashboardWidth: parseIntegerInRange(obj.dashboardWidth, 32, 120),
987
- dashboardLiveRefreshMs: parseIntegerInRange(
988
- obj.dashboardLiveRefreshMs,
989
- 250,
990
- 60_000,
991
- ),
992
- autoOpenDashboard: parseWithSchema(
993
- Type.Boolean(),
994
- obj.autoOpenDashboard,
995
- ),
996
- autoOpenDashboardForForegroundRuns: parseWithSchema(
997
- Type.Boolean(),
998
- obj.autoOpenDashboardForForegroundRuns,
999
- ),
709
+ dashboardLiveRefreshMs: parseIntegerInRange(obj.dashboardLiveRefreshMs, 250, 60_000),
710
+ autoOpenDashboard: parseWithSchema(Type.Boolean(), obj.autoOpenDashboard),
711
+ autoOpenDashboardForForegroundRuns: parseWithSchema(Type.Boolean(), obj.autoOpenDashboardForForegroundRuns),
1000
712
  showModel: parseWithSchema(Type.Boolean(), obj.showModel),
1001
713
  showTokens: parseWithSchema(Type.Boolean(), obj.showTokens),
1002
714
  showTools: parseWithSchema(Type.Boolean(), obj.showTools),
1003
- transcriptTailBytes: parseIntegerInRange(
1004
- obj.transcriptTailBytes,
1005
- 1024,
1006
- 50 * 1024 * 1024,
1007
- ),
1008
- mascotStyle: parseWithSchema(
1009
- Type.Union([Type.Literal("cat"), Type.Literal("armin")]),
1010
- obj.mascotStyle,
1011
- ),
715
+ transcriptTailBytes: parseIntegerInRange(obj.transcriptTailBytes, 1024, 50 * 1024 * 1024),
716
+ mascotStyle: parseWithSchema(Type.Union([Type.Literal("cat"), Type.Literal("armin")]), obj.mascotStyle),
1012
717
  mascotEffect: parseWithSchema(
1013
718
  Type.Union([
1014
719
  Type.Literal("random"),
@@ -1024,23 +729,15 @@ function parseUiConfig(value: unknown): CrewUiConfig | undefined {
1024
729
  obj.mascotEffect,
1025
730
  ),
1026
731
  };
1027
- return Object.values(ui).some((entry) => entry !== undefined)
1028
- ? ui
1029
- : undefined;
732
+ return Object.values(ui).some((entry) => entry !== undefined) ? ui : undefined;
1030
733
  }
1031
734
 
1032
735
  function parseAgentsConfig(value: unknown): CrewAgentsConfig | undefined {
1033
736
  const obj = asRecord(value);
1034
737
  if (!obj) return undefined;
1035
738
  const overrides: Record<string, AgentOverrideConfig> = {};
1036
- if (
1037
- obj.overrides &&
1038
- typeof obj.overrides === "object" &&
1039
- !Array.isArray(obj.overrides)
1040
- ) {
1041
- for (const [name, rawOverride] of Object.entries(
1042
- obj.overrides as Record<string, unknown>,
1043
- )) {
739
+ if (obj.overrides && typeof obj.overrides === "object" && !Array.isArray(obj.overrides)) {
740
+ for (const [name, rawOverride] of Object.entries(obj.overrides as Record<string, unknown>)) {
1044
741
  const parsed = parseAgentOverride(rawOverride);
1045
742
  if (parsed && name.trim()) overrides[name.trim()] = parsed;
1046
743
  }
@@ -1049,28 +746,18 @@ function parseAgentsConfig(value: unknown): CrewAgentsConfig | undefined {
1049
746
  disableBuiltins: parseWithSchema(Type.Boolean(), obj.disableBuiltins),
1050
747
  overrides: Object.keys(overrides).length > 0 ? overrides : undefined,
1051
748
  };
1052
- return Object.values(agents).some((entry) => entry !== undefined)
1053
- ? agents
1054
- : undefined;
749
+ return Object.values(agents).some((entry) => entry !== undefined) ? agents : undefined;
1055
750
  }
1056
751
 
1057
752
  function parseToolsConfig(value: unknown): CrewToolsConfig | undefined {
1058
753
  const obj = asRecord(value);
1059
754
  if (!obj) return undefined;
1060
755
  const tools: CrewToolsConfig = {
1061
- enableClaudeStyleAliases: parseWithSchema(
1062
- Type.Boolean(),
1063
- obj.enableClaudeStyleAliases,
1064
- ),
756
+ enableClaudeStyleAliases: parseWithSchema(Type.Boolean(), obj.enableClaudeStyleAliases),
1065
757
  enableSteer: parseWithSchema(Type.Boolean(), obj.enableSteer),
1066
- terminateOnForeground: parseWithSchema(
1067
- Type.Boolean(),
1068
- obj.terminateOnForeground,
1069
- ),
758
+ terminateOnForeground: parseWithSchema(Type.Boolean(), obj.terminateOnForeground),
1070
759
  };
1071
- return Object.values(tools).some((entry) => entry !== undefined)
1072
- ? tools
1073
- : undefined;
760
+ return Object.values(tools).some((entry) => entry !== undefined) ? tools : undefined;
1074
761
  }
1075
762
 
1076
763
  function parseTelemetryConfig(value: unknown): CrewTelemetryConfig | undefined {
@@ -1079,125 +766,68 @@ function parseTelemetryConfig(value: unknown): CrewTelemetryConfig | undefined {
1079
766
  const telemetry: CrewTelemetryConfig = {
1080
767
  enabled: parseWithSchema(Type.Boolean(), obj.enabled),
1081
768
  };
1082
- return Object.values(telemetry).some((entry) => entry !== undefined)
1083
- ? telemetry
1084
- : undefined;
769
+ return Object.values(telemetry).some((entry) => entry !== undefined) ? telemetry : undefined;
1085
770
  }
1086
771
 
1087
772
  function parsePolicyConfig(value: unknown): CrewPolicyConfig | undefined {
1088
773
  const obj = asRecord(value);
1089
774
  if (!obj) return undefined;
1090
775
  const policy: CrewPolicyConfig = {
1091
- requireIntentForDestructiveActions: parseWithSchema(
1092
- Type.Boolean(),
1093
- obj.requireIntentForDestructiveActions,
1094
- ),
1095
- disabledCapabilities: parseWithSchema(
1096
- Type.Array(Type.String()),
1097
- obj.disabledCapabilities,
1098
- ),
776
+ requireIntentForDestructiveActions: parseWithSchema(Type.Boolean(), obj.requireIntentForDestructiveActions),
777
+ disabledCapabilities: parseWithSchema(Type.Array(Type.String()), obj.disabledCapabilities),
1099
778
  };
1100
- return Object.values(policy).some((entry) => entry !== undefined)
1101
- ? policy
1102
- : undefined;
779
+ return Object.values(policy).some((entry) => entry !== undefined) ? policy : undefined;
1103
780
  }
1104
781
 
1105
- function parseNotificationsConfig(
1106
- value: unknown,
1107
- ): CrewNotificationsConfig | undefined {
782
+ function parseNotificationsConfig(value: unknown): CrewNotificationsConfig | undefined {
1108
783
  const obj = asRecord(value);
1109
784
  if (!obj) return undefined;
1110
785
  const notifications: CrewNotificationsConfig = {
1111
786
  enabled: parseWithSchema(Type.Boolean(), obj.enabled),
1112
787
  severityFilter: parseWithSchema(
1113
- Type.Array(
1114
- Type.Union([
1115
- Type.Literal("info"),
1116
- Type.Literal("warning"),
1117
- Type.Literal("error"),
1118
- Type.Literal("critical"),
1119
- ]),
1120
- ),
788
+ Type.Array(Type.Union([Type.Literal("info"), Type.Literal("warning"), Type.Literal("error"), Type.Literal("critical")])),
1121
789
  obj.severityFilter,
1122
790
  ),
1123
- dedupWindowMs: parsePositiveInteger(
1124
- obj.dedupWindowMs,
1125
- 24 * 60 * 60 * 1000,
1126
- ),
1127
- batchWindowMs: parseWithSchema(
1128
- Type.Integer({ minimum: 0, maximum: 60_000 }),
1129
- obj.batchWindowMs,
1130
- ),
1131
- quietHours: parseWithSchema(
1132
- Type.String({ pattern: "^\\d{2}:\\d{2}-\\d{2}:\\d{2}$" }),
1133
- obj.quietHours,
1134
- ),
791
+ dedupWindowMs: parsePositiveInteger(obj.dedupWindowMs, 24 * 60 * 60 * 1000),
792
+ batchWindowMs: parseWithSchema(Type.Integer({ minimum: 0, maximum: 60_000 }), obj.batchWindowMs),
793
+ quietHours: parseWithSchema(Type.String({ pattern: "^\\d{2}:\\d{2}-\\d{2}:\\d{2}$" }), obj.quietHours),
1135
794
  sinkRetentionDays: parsePositiveInteger(obj.sinkRetentionDays, 90),
1136
795
  };
1137
- return Object.values(notifications).some((entry) => entry !== undefined)
1138
- ? notifications
1139
- : undefined;
796
+ return Object.values(notifications).some((entry) => entry !== undefined) ? notifications : undefined;
1140
797
  }
1141
798
 
1142
- function parseObservabilityConfig(
1143
- value: unknown,
1144
- ): CrewObservabilityConfig | undefined {
799
+ function parseObservabilityConfig(value: unknown): CrewObservabilityConfig | undefined {
1145
800
  const obj = asRecord(value);
1146
801
  if (!obj) return undefined;
1147
802
  const observability: CrewObservabilityConfig = {
1148
803
  enabled: parseWithSchema(Type.Boolean(), obj.enabled),
1149
- pollIntervalMs: parseWithSchema(
1150
- Type.Integer({ minimum: 1000, maximum: 60_000 }),
1151
- obj.pollIntervalMs,
1152
- ),
804
+ pollIntervalMs: parseWithSchema(Type.Integer({ minimum: 1000, maximum: 60_000 }), obj.pollIntervalMs),
1153
805
  metricRetentionDays: parsePositiveInteger(obj.metricRetentionDays, 365),
1154
806
  };
1155
- return Object.values(observability).some((entry) => entry !== undefined)
1156
- ? observability
1157
- : undefined;
807
+ return Object.values(observability).some((entry) => entry !== undefined) ? observability : undefined;
1158
808
  }
1159
809
 
1160
- function parseReliabilityConfig(
1161
- value: unknown,
1162
- ): CrewReliabilityConfig | undefined {
810
+ function parseReliabilityConfig(value: unknown): CrewReliabilityConfig | undefined {
1163
811
  const obj = asRecord(value);
1164
812
  if (!obj) return undefined;
1165
813
  const retryObj = asRecord(obj.retryPolicy);
1166
814
  const retryPolicy: CrewRetryPolicyConfig | undefined = retryObj
1167
815
  ? {
1168
816
  maxAttempts: parsePositiveInteger(retryObj.maxAttempts, 10),
1169
- backoffMs: parseWithSchema(
1170
- Type.Integer({ minimum: 100, maximum: 60_000 }),
1171
- retryObj.backoffMs,
1172
- ),
1173
- jitterRatio: parseWithSchema(
1174
- Type.Number({ minimum: 0, maximum: 1 }),
1175
- retryObj.jitterRatio,
1176
- ),
1177
- exponentialFactor: parseWithSchema(
1178
- Type.Number({ minimum: 1, maximum: 5 }),
1179
- retryObj.exponentialFactor,
1180
- ),
817
+ backoffMs: parseWithSchema(Type.Integer({ minimum: 100, maximum: 60_000 }), retryObj.backoffMs),
818
+ jitterRatio: parseWithSchema(Type.Number({ minimum: 0, maximum: 1 }), retryObj.jitterRatio),
819
+ exponentialFactor: parseWithSchema(Type.Number({ minimum: 1, maximum: 5 }), retryObj.exponentialFactor),
1181
820
  retryableErrors: parseStringList(retryObj.retryableErrors),
1182
821
  }
1183
822
  : undefined;
1184
823
  const reliability: CrewReliabilityConfig = {
1185
824
  autoRetry: parseWithSchema(Type.Boolean(), obj.autoRetry),
1186
- retryPolicy:
1187
- retryPolicy &&
1188
- Object.values(retryPolicy).some((entry) => entry !== undefined)
1189
- ? retryPolicy
1190
- : undefined,
825
+ retryPolicy: retryPolicy && Object.values(retryPolicy).some((entry) => entry !== undefined) ? retryPolicy : undefined,
1191
826
  autoRecover: parseWithSchema(Type.Boolean(), obj.autoRecover),
1192
827
  deadletterThreshold: parsePositiveInteger(obj.deadletterThreshold),
1193
- cleanupOrphanedTempDirs: parseWithSchema(
1194
- Type.Boolean(),
1195
- obj.cleanupOrphanedTempDirs,
1196
- ),
828
+ cleanupOrphanedTempDirs: parseWithSchema(Type.Boolean(), obj.cleanupOrphanedTempDirs),
1197
829
  };
1198
- return Object.values(reliability).some((entry) => entry !== undefined)
1199
- ? reliability
1200
- : undefined;
830
+ return Object.values(reliability).some((entry) => entry !== undefined) ? reliability : undefined;
1201
831
  }
1202
832
 
1203
833
  function parseOtlpConfig(value: unknown): CrewOtlpConfig | undefined {
@@ -1236,14 +866,9 @@ function parseOtlpConfig(value: unknown): CrewOtlpConfig | undefined {
1236
866
  enabled: parseWithSchema(Type.Boolean(), obj.enabled),
1237
867
  endpoint: parseWithSchema(Type.String({ minLength: 1 }), obj.endpoint),
1238
868
  headers: Object.keys(headers).length > 0 ? headers : undefined,
1239
- intervalMs: parseWithSchema(
1240
- Type.Integer({ minimum: 5000 }),
1241
- obj.intervalMs,
1242
- ),
869
+ intervalMs: parseWithSchema(Type.Integer({ minimum: 5000 }), obj.intervalMs),
1243
870
  };
1244
- return Object.values(otlp).some((entry) => entry !== undefined)
1245
- ? otlp
1246
- : undefined;
871
+ return Object.values(otlp).some((entry) => entry !== undefined) ? otlp : undefined;
1247
872
  }
1248
873
 
1249
874
  export function parseConfig(raw: unknown): PiTeamsConfig {
@@ -1252,14 +877,8 @@ export function parseConfig(raw: unknown): PiTeamsConfig {
1252
877
  return {
1253
878
  asyncByDefault: parseWithSchema(Type.Boolean(), obj.asyncByDefault),
1254
879
  executeWorkers: parseWithSchema(Type.Boolean(), obj.executeWorkers),
1255
- notifierIntervalMs: parseWithSchema(
1256
- Type.Number({ minimum: 1_000 }),
1257
- obj.notifierIntervalMs,
1258
- ),
1259
- requireCleanWorktreeLeader: parseWithSchema(
1260
- Type.Boolean(),
1261
- obj.requireCleanWorktreeLeader,
1262
- ),
880
+ notifierIntervalMs: parseWithSchema(Type.Number({ minimum: 1_000 }), obj.notifierIntervalMs),
881
+ requireCleanWorktreeLeader: parseWithSchema(Type.Boolean(), obj.requireCleanWorktreeLeader),
1263
882
  autonomous: parseAutonomousConfig(obj.autonomous),
1264
883
  limits: parseLimitsConfig(obj.limits),
1265
884
  runtime: parseRuntimeConfig(obj.runtime),
@@ -1279,8 +898,7 @@ export function parseConfig(raw: unknown): PiTeamsConfig {
1279
898
  }
1280
899
 
1281
900
  export function parseConfigWithWarnings(raw: unknown): ConfigValidationResult {
1282
- if (!raw || typeof raw !== "object" || Array.isArray(raw))
1283
- return { config: {}, warnings: [] };
901
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) return { config: {}, warnings: [] };
1284
902
  const parsed = parseConfig(raw);
1285
903
  const warnings = validateConfigWithWarnings(raw as Record<string, unknown>);
1286
904
  return { config: parsed, warnings };
@@ -1292,8 +910,7 @@ function unsetPath(record: Record<string, unknown>, dottedPath: string): void {
1292
910
  let target: Record<string, unknown> = record;
1293
911
  for (const part of parts.slice(0, -1)) {
1294
912
  const current = target[part];
1295
- if (!current || typeof current !== "object" || Array.isArray(current))
1296
- return;
913
+ if (!current || typeof current !== "object" || Array.isArray(current)) return;
1297
914
  target = current as Record<string, unknown>;
1298
915
  }
1299
916
  delete target[parts[parts.length - 1]!];
@@ -1333,17 +950,14 @@ function readOptionalConfig(filePath: string): {
1333
950
  config: PiTeamsConfig;
1334
951
  warnings: string[];
1335
952
  } {
1336
- if (!fs.existsSync(filePath))
1337
- return { exists: false, config: {}, warnings: [] };
953
+ if (!fs.existsSync(filePath)) return { exists: false, config: {}, warnings: [] };
1338
954
  try {
1339
955
  const raw = readConfigRecord(filePath);
1340
956
  const parsed = parseConfigWithWarnings(raw);
1341
957
  return {
1342
958
  exists: true,
1343
959
  config: parsed.config,
1344
- warnings: parsed.warnings.map(
1345
- (warning) => `${filePath}: ${warning}`,
1346
- ),
960
+ warnings: parsed.warnings.map((warning) => `${filePath}: ${warning}`),
1347
961
  };
1348
962
  } catch (error) {
1349
963
  const message = error instanceof Error ? error.message : String(error);
@@ -1367,12 +981,7 @@ export function loadConfig(cwd?: string): LoadedPiTeamsConfig {
1367
981
  }
1368
982
  const userConfig = readOptionalConfig(filePath);
1369
983
  warnings.push(...userConfig.warnings);
1370
- let config = mergeConfig(
1371
- legacyConfig.exists && legacyPath !== filePath
1372
- ? legacyConfig.config
1373
- : {},
1374
- userConfig.config,
1375
- );
984
+ let config = mergeConfig(legacyConfig.exists && legacyPath !== filePath ? legacyConfig.config : {}, userConfig.config);
1376
985
  if (cwd) {
1377
986
  const projectPath = projectConfigPath(cwd);
1378
987
  const projectConfig = readOptionalConfig(projectPath);
@@ -1381,15 +990,8 @@ export function loadConfig(cwd?: string): LoadedPiTeamsConfig {
1381
990
  // Sensitive fields have already been sanitized by sanitizeProjectConfig.
1382
991
  let effectiveConfig = {};
1383
992
  if (projectConfig.exists) {
1384
- const projectSafeConfig = sanitizeProjectConfig(
1385
- projectPath,
1386
- config,
1387
- projectConfig.config,
1388
- );
1389
- warnings.push(
1390
- ...projectConfig.warnings,
1391
- ...projectSafeConfig.warnings,
1392
- );
993
+ const projectSafeConfig = sanitizeProjectConfig(projectPath, config, projectConfig.config);
994
+ warnings.push(...projectConfig.warnings, ...projectSafeConfig.warnings);
1393
995
  // merge(base=projectConfig, override=userConfig) → override wins
1394
996
  effectiveConfig = mergeConfig(effectiveConfig, projectSafeConfig.config);
1395
997
  }
@@ -1397,7 +999,6 @@ export function loadConfig(cwd?: string): LoadedPiTeamsConfig {
1397
999
  effectiveConfig = mergeConfig(effectiveConfig, config);
1398
1000
  config = effectiveConfig;
1399
1001
 
1400
-
1401
1002
  // `.pi/pi-crew.json` is the project-owned config file.
1402
1003
  // Merge project config FIRST (base), then user config on top (override).
1403
1004
  // This ensures user preferences always take precedence over project settings.
@@ -1406,11 +1007,7 @@ export function loadConfig(cwd?: string): LoadedPiTeamsConfig {
1406
1007
  const piCrewJsonConfig = readOptionalConfig(piCrewJsonPath);
1407
1008
  if (piCrewJsonConfig.exists) {
1408
1009
  warnings.push(...piCrewJsonConfig.warnings);
1409
- const projectPart = sanitizeProjectConfig(
1410
- piCrewJsonPath,
1411
- config,
1412
- piCrewJsonConfig.config,
1413
- );
1010
+ const projectPart = sanitizeProjectConfig(piCrewJsonPath, config, piCrewJsonConfig.config);
1414
1011
  warnings.push(...projectPart.warnings);
1415
1012
  // base=project config, override=user config → user wins
1416
1013
  const mergedProject = mergeConfig(projectPart.config, config);
@@ -1426,45 +1023,30 @@ export function loadConfig(cwd?: string): LoadedPiTeamsConfig {
1426
1023
  };
1427
1024
  }
1428
1025
 
1429
- export function updateConfig(
1430
- patch: PiTeamsConfig,
1431
- options: UpdateConfigOptions = {},
1432
- ): SavedPiTeamsConfig {
1433
- const filePath =
1434
- options.scope === "project" && options.cwd
1435
- ? projectConfigPath(options.cwd)
1436
- : configPath();
1026
+ export function updateConfig(patch: PiTeamsConfig, options: UpdateConfigOptions = {}): SavedPiTeamsConfig {
1027
+ const filePath = options.scope === "project" && options.cwd ? projectConfigPath(options.cwd) : configPath();
1437
1028
  const lockPath = filePath + ".lock";
1438
1029
  return withFileLockSync(lockPath, () => {
1439
1030
  let current: Record<string, unknown>;
1440
1031
  try {
1441
1032
  current = readConfigRecord(filePath);
1442
1033
  } catch (error) {
1443
- const message =
1444
- error instanceof Error ? error.message : String(error);
1034
+ const message = error instanceof Error ? error.message : String(error);
1445
1035
  throw new Error(`Could not update pi-crew config: ${message}`);
1446
1036
  }
1447
1037
  let merged = mergeConfig(parseConfig(current), patch);
1448
1038
  if (options.unsetPaths?.length) {
1449
- const raw = JSON.parse(JSON.stringify(merged)) as Record<
1450
- string,
1451
- unknown
1452
- >;
1039
+ const raw = JSON.parse(JSON.stringify(merged)) as Record<string, unknown>;
1453
1040
  for (const unset of options.unsetPaths) unsetPath(raw, unset);
1454
1041
  merged = parseConfig(raw);
1455
1042
  }
1456
1043
  fs.mkdirSync(path.dirname(filePath), { recursive: true });
1457
- atomicWriteFile(
1458
- filePath,
1459
- `${JSON.stringify(merged, null, 2)}\n`,
1460
- );
1044
+ atomicWriteFile(filePath, `${JSON.stringify(merged, null, 2)}\n`);
1461
1045
  return { path: filePath, config: merged };
1462
1046
  });
1463
1047
  }
1464
1048
 
1465
- export function updateAutonomousConfig(
1466
- patch: PiTeamsAutonomousConfig,
1467
- ): SavedPiTeamsConfig {
1049
+ export function updateAutonomousConfig(patch: PiTeamsAutonomousConfig): SavedPiTeamsConfig {
1468
1050
  const filePath = configPath();
1469
1051
  const lockPath = filePath + ".lock";
1470
1052
  return withFileLockSync(lockPath, () => {
@@ -1472,21 +1054,15 @@ export function updateAutonomousConfig(
1472
1054
  try {
1473
1055
  current = readConfigRecord(filePath);
1474
1056
  } catch (error) {
1475
- const message =
1476
- error instanceof Error ? error.message : String(error);
1057
+ const message = error instanceof Error ? error.message : String(error);
1477
1058
  throw new Error(`Could not update pi-crew config: ${message}`);
1478
1059
  }
1479
1060
  const currentAutonomous =
1480
- current.autonomous &&
1481
- typeof current.autonomous === "object" &&
1482
- !Array.isArray(current.autonomous)
1061
+ current.autonomous && typeof current.autonomous === "object" && !Array.isArray(current.autonomous)
1483
1062
  ? (current.autonomous as Record<string, unknown>)
1484
1063
  : {};
1485
1064
  current.autonomous = { ...currentAutonomous, ...patch };
1486
- atomicWriteFile(
1487
- filePath,
1488
- `${JSON.stringify(current, null, 2)}\n`,
1489
- );
1065
+ atomicWriteFile(filePath, `${JSON.stringify(current, null, 2)}\n`);
1490
1066
  return { path: filePath, config: parseConfig(current) };
1491
1067
  });
1492
1068
  }