@sienklogic/plan-build-run 2.21.1 → 2.21.2

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 (982) hide show
  1. package/CHANGELOG.md +1331 -323
  2. package/CLAUDE.md +75 -40
  3. package/LICENSE +2 -1
  4. package/README.md +412 -177
  5. package/bin/install.js +2752 -0
  6. package/dashboard/bin/cli.cjs +96 -0
  7. package/dashboard/bin/stop.cjs +129 -0
  8. package/dashboard/eslint.config.js +37 -0
  9. package/dashboard/index.html +20 -0
  10. package/dashboard/package.json +27 -25
  11. package/dashboard/server/index.js +151 -0
  12. package/dashboard/server/lib/frontmatter.js +92 -0
  13. package/dashboard/server/middleware/static.js +35 -0
  14. package/dashboard/server/package.json +16 -0
  15. package/dashboard/server/routes/agents.js +234 -0
  16. package/dashboard/server/routes/config.js +64 -0
  17. package/dashboard/server/routes/health.js +98 -0
  18. package/dashboard/server/routes/incidents.js +78 -0
  19. package/dashboard/server/routes/intel.js +69 -0
  20. package/dashboard/server/routes/memory.js +107 -0
  21. package/dashboard/server/routes/planning.js +234 -0
  22. package/dashboard/server/routes/progress.js +77 -0
  23. package/dashboard/server/routes/projects.js +36 -0
  24. package/dashboard/server/routes/requirements.js +40 -0
  25. package/dashboard/server/routes/roadmap.js +69 -0
  26. package/dashboard/server/routes/sessions.js +70 -0
  27. package/dashboard/server/routes/status.js +25 -0
  28. package/dashboard/server/routes/telemetry.js +233 -0
  29. package/dashboard/server/services/file-watcher.js +105 -0
  30. package/dashboard/server/services/planning-reader.js +727 -0
  31. package/dashboard/server/test/cli.test.js +34 -0
  32. package/dashboard/server/test/frontmatter.test.js +104 -0
  33. package/dashboard/server/test/isolation.test.js +32 -0
  34. package/dashboard/server/test/planning-reader.test.js +151 -0
  35. package/dashboard/server/test/routes.test.js +91 -0
  36. package/dashboard/server/test/ws.test.js +81 -0
  37. package/dashboard/server/ws.js +96 -0
  38. package/dashboard/src/App.jsx +165 -0
  39. package/dashboard/src/components/charts/BudgetBars.jsx +42 -0
  40. package/dashboard/src/components/charts/ContextRadar.jsx +34 -0
  41. package/dashboard/src/components/charts/PhaseDonut.jsx +66 -0
  42. package/dashboard/src/components/charts/SuccessTrend.jsx +45 -0
  43. package/dashboard/src/components/charts/TokenChart.jsx +55 -0
  44. package/dashboard/src/components/charts/index.js +5 -0
  45. package/dashboard/src/components/config/CfgSection.jsx +93 -0
  46. package/dashboard/src/components/layout/Header.jsx +89 -0
  47. package/dashboard/src/components/layout/ProjectSwitcher.jsx +160 -0
  48. package/dashboard/src/components/layout/Sidebar.jsx +161 -0
  49. package/dashboard/src/components/ui/AutoModeBanner.jsx +138 -0
  50. package/dashboard/src/components/ui/BackButton.jsx +27 -0
  51. package/dashboard/src/components/ui/Badge.jsx +27 -0
  52. package/dashboard/src/components/ui/Card.jsx +23 -0
  53. package/dashboard/src/components/ui/ChartTooltip.jsx +48 -0
  54. package/dashboard/src/components/ui/CheckpointBox.jsx +110 -0
  55. package/dashboard/src/components/ui/CodeBlock.jsx +27 -0
  56. package/dashboard/src/components/ui/ConfidenceBadge.jsx +20 -0
  57. package/dashboard/src/components/ui/ConfirmModal.jsx +161 -0
  58. package/dashboard/src/components/ui/ConnectionBanner.jsx +60 -0
  59. package/dashboard/src/components/ui/ErrorBoundary.jsx +106 -0
  60. package/dashboard/src/components/ui/ErrorBox.jsx +107 -0
  61. package/dashboard/src/components/ui/KeyValue.jsx +33 -0
  62. package/dashboard/src/components/ui/LoadingSkeleton.jsx +84 -0
  63. package/dashboard/src/components/ui/MetricCard.jsx +58 -0
  64. package/dashboard/src/components/ui/NextUpBlock.jsx +92 -0
  65. package/dashboard/src/components/ui/NumberInput.jsx +44 -0
  66. package/dashboard/src/components/ui/PBRBanner.jsx +47 -0
  67. package/dashboard/src/components/ui/PipelineView.jsx +130 -0
  68. package/dashboard/src/components/ui/ProgressBar.jsx +28 -0
  69. package/dashboard/src/components/ui/ProgressDisplay.jsx +47 -0
  70. package/dashboard/src/components/ui/QualityGateBadge.jsx +15 -0
  71. package/dashboard/src/components/ui/SectionTitle.jsx +35 -0
  72. package/dashboard/src/components/ui/SelectInput.jsx +45 -0
  73. package/dashboard/src/components/ui/StatusDot.jsx +51 -0
  74. package/dashboard/src/components/ui/StatusSymbol.jsx +49 -0
  75. package/dashboard/src/components/ui/TabBar.jsx +41 -0
  76. package/dashboard/src/components/ui/TextInput.jsx +42 -0
  77. package/dashboard/src/components/ui/Toast.jsx +117 -0
  78. package/dashboard/src/components/ui/Toggle.jsx +70 -0
  79. package/dashboard/src/components/ui/index.js +29 -0
  80. package/dashboard/src/hooks/useDocumentTitle.js +16 -0
  81. package/dashboard/src/hooks/useFetch.js +50 -0
  82. package/dashboard/src/hooks/useToast.jsx +43 -0
  83. package/dashboard/src/hooks/useWebSocket.js +103 -0
  84. package/dashboard/src/lib/api.js +112 -0
  85. package/dashboard/src/lib/configSchema.js +189 -0
  86. package/dashboard/src/lib/constants.js +22 -0
  87. package/dashboard/src/main.jsx +15 -0
  88. package/dashboard/src/pages/AgentsPage.jsx +191 -0
  89. package/dashboard/src/pages/ConfigPage.jsx +298 -0
  90. package/dashboard/src/pages/HooksPage.jsx +412 -0
  91. package/dashboard/src/pages/IncidentsPage.jsx +135 -0
  92. package/dashboard/src/pages/IntelPage.jsx +193 -0
  93. package/dashboard/src/pages/LiveFeed.jsx +274 -0
  94. package/dashboard/src/pages/MemoryPage.jsx +107 -0
  95. package/dashboard/src/pages/OnboardingPage.jsx +117 -0
  96. package/dashboard/src/pages/Overview.jsx +360 -0
  97. package/dashboard/src/pages/PhaseDetailView.jsx +216 -0
  98. package/dashboard/src/pages/PlanningPage.jsx +181 -0
  99. package/dashboard/src/pages/ProgressPage.jsx +249 -0
  100. package/dashboard/src/pages/ResearchPage.jsx +129 -0
  101. package/dashboard/src/pages/RoadmapPage.jsx +251 -0
  102. package/dashboard/src/pages/SessionsPage.jsx +117 -0
  103. package/dashboard/src/pages/Telemetry.jsx +166 -0
  104. package/dashboard/src/pages/planning/DecisionsTab.jsx +153 -0
  105. package/dashboard/src/pages/planning/FilesTab.jsx +420 -0
  106. package/dashboard/src/pages/planning/MilestoneDetail.jsx +319 -0
  107. package/dashboard/src/pages/planning/MilestonesTab.jsx +151 -0
  108. package/dashboard/src/pages/planning/NotesTab.jsx +251 -0
  109. package/dashboard/src/pages/planning/PhasesTab.jsx +218 -0
  110. package/dashboard/src/pages/planning/QuickTab.jsx +50 -0
  111. package/dashboard/src/pages/planning/ResearchTab.jsx +103 -0
  112. package/dashboard/src/pages/planning/TodosTab.jsx +297 -0
  113. package/dashboard/src/theme/ThemeProvider.jsx +38 -0
  114. package/dashboard/src/theme/tokens.js +17 -0
  115. package/dashboard/tests/components/ConfirmModal.test.jsx +179 -0
  116. package/dashboard/tests/components/ConnectionBanner.test.jsx +37 -0
  117. package/dashboard/tests/components/ErrorBoundary.test.jsx +59 -0
  118. package/dashboard/tests/components/LoadingSkeleton.test.jsx +46 -0
  119. package/dashboard/tests/components/ToastContainer.test.jsx +47 -0
  120. package/dashboard/tests/components/Toggle.test.jsx +61 -0
  121. package/dashboard/tests/hooks/useFetch.test.jsx +77 -0
  122. package/dashboard/tests/hooks/useToast.test.jsx +78 -0
  123. package/dashboard/tests/hooks/useWebSocket.test.jsx +128 -0
  124. package/dashboard/tests/pages/ConfigPage.test.jsx +199 -0
  125. package/dashboard/tests/pages/PlanningPage.test.jsx +119 -0
  126. package/dashboard/tests/pages/planning/FilesTab.test.jsx +198 -0
  127. package/dashboard/tests/pages/planning/NotesTab.test.jsx +178 -0
  128. package/dashboard/tests/pages/planning/TodosTab.test.jsx +188 -0
  129. package/dashboard/tests/performance.test.jsx +46 -0
  130. package/dashboard/tests/routes/config.test.js +98 -0
  131. package/dashboard/tests/routes/health.test.js +40 -0
  132. package/dashboard/tests/routes/planning.test.js +112 -0
  133. package/dashboard/tests/routes/roadmap.test.js +91 -0
  134. package/dashboard/tests/routes/status.test.js +131 -0
  135. package/dashboard/tests/server/planning-reader.test.js +153 -0
  136. package/dashboard/tests/setup.js +7 -0
  137. package/dashboard/vite.config.js +41 -0
  138. package/package.json +56 -41
  139. package/plan-build-run/bin/config-schema.json +1298 -0
  140. package/plugins/pbr/.claude-plugin/plugin.json +1 -1
  141. package/plugins/pbr/CLAUDE.md +19 -0
  142. package/plugins/pbr/UI-CONSISTENCY-GAPS.md +1 -1
  143. package/plugins/pbr/agents/advisor-researcher.md +101 -0
  144. package/plugins/pbr/agents/audit.md +207 -89
  145. package/plugins/pbr/agents/codebase-mapper.md +158 -23
  146. package/plugins/pbr/agents/debugger.md +210 -34
  147. package/plugins/pbr/agents/dev-sync.md +206 -0
  148. package/plugins/pbr/agents/executor.md +734 -38
  149. package/plugins/pbr/agents/general.md +69 -5
  150. package/plugins/pbr/agents/integration-checker.md +147 -31
  151. package/plugins/pbr/agents/intel-updater.md +332 -0
  152. package/plugins/pbr/agents/nyquist-auditor.md +254 -0
  153. package/plugins/pbr/agents/plan-checker.md +268 -65
  154. package/plugins/pbr/agents/planner.md +449 -41
  155. package/plugins/pbr/agents/researcher.md +218 -37
  156. package/plugins/pbr/agents/roadmapper.md +398 -0
  157. package/plugins/pbr/agents/synthesizer.md +166 -25
  158. package/plugins/pbr/agents/ui-checker.md +204 -0
  159. package/plugins/pbr/agents/ui-researcher.md +224 -0
  160. package/plugins/pbr/agents/verifier.md +570 -46
  161. package/plugins/pbr/commands/add-phase.md +75 -0
  162. package/plugins/pbr/commands/add-todo.md +8 -0
  163. package/plugins/pbr/commands/audit-fix.md +5 -0
  164. package/plugins/pbr/commands/audit-milestone.md +8 -0
  165. package/plugins/pbr/commands/autonomous.md +5 -0
  166. package/plugins/pbr/commands/backlog.md +6 -0
  167. package/plugins/pbr/commands/check-todos.md +8 -0
  168. package/plugins/pbr/commands/complete-milestone.md +8 -0
  169. package/plugins/pbr/commands/config.md +1 -1
  170. package/plugins/pbr/commands/discuss-phase.md +6 -0
  171. package/plugins/pbr/commands/execute-phase.md +6 -0
  172. package/plugins/pbr/commands/fast.md +6 -0
  173. package/plugins/pbr/commands/forensics.md +6 -0
  174. package/plugins/pbr/commands/import.md +1 -1
  175. package/plugins/pbr/commands/insert-phase.md +65 -0
  176. package/plugins/pbr/commands/intel.md +5 -0
  177. package/plugins/pbr/commands/join-discord.md +11 -0
  178. package/plugins/pbr/commands/list-phase-assumptions.md +5 -0
  179. package/plugins/pbr/commands/map-codebase.md +6 -0
  180. package/plugins/pbr/commands/milestone-summary.md +6 -0
  181. package/plugins/pbr/commands/new-milestone.md +8 -0
  182. package/plugins/pbr/commands/new-project.md +6 -0
  183. package/plugins/pbr/commands/pause-work.md +5 -0
  184. package/plugins/pbr/commands/plan-milestone-gaps.md +7 -0
  185. package/plugins/pbr/commands/plan-phase.md +6 -0
  186. package/plugins/pbr/commands/plant-seed.md +6 -0
  187. package/plugins/pbr/commands/profile-user.md +5 -0
  188. package/plugins/pbr/commands/profile.md +5 -0
  189. package/plugins/pbr/commands/progress.md +6 -0
  190. package/plugins/pbr/commands/quick.md +1 -1
  191. package/plugins/pbr/commands/reapply-patches.md +47 -0
  192. package/plugins/pbr/commands/release.md +6 -0
  193. package/plugins/pbr/commands/remove-phase.md +66 -0
  194. package/plugins/pbr/commands/research-phase.md +59 -0
  195. package/plugins/pbr/commands/resume-work.md +5 -0
  196. package/plugins/pbr/commands/seed.md +6 -0
  197. package/plugins/pbr/commands/session-report.md +5 -0
  198. package/plugins/pbr/commands/set-profile.md +6 -0
  199. package/plugins/pbr/commands/settings.md +5 -0
  200. package/plugins/pbr/commands/setup.md +1 -1
  201. package/plugins/pbr/commands/ship.md +5 -0
  202. package/plugins/pbr/commands/stats.md +6 -0
  203. package/plugins/pbr/commands/test.md +5 -0
  204. package/plugins/pbr/commands/thread.md +6 -0
  205. package/plugins/pbr/commands/todo.md +1 -1
  206. package/plugins/pbr/commands/ui-phase.md +5 -0
  207. package/plugins/pbr/commands/ui-review.md +5 -0
  208. package/plugins/pbr/commands/undo.md +5 -0
  209. package/plugins/pbr/commands/update.md +37 -0
  210. package/plugins/pbr/commands/validate-phase.md +5 -0
  211. package/plugins/pbr/commands/verify-work.md +6 -0
  212. package/plugins/pbr/dashboard/package-lock.json +6 -0
  213. package/plugins/pbr/dist/architecture-guard.js +76 -0
  214. package/plugins/pbr/dist/audit-dimensions.js +556 -0
  215. package/plugins/pbr/dist/auto-continue.js +277 -0
  216. package/plugins/pbr/dist/block-skill-self-read.js +124 -0
  217. package/plugins/pbr/dist/check-agent-state-write.js +63 -0
  218. package/plugins/pbr/dist/check-config-change.js +213 -0
  219. package/plugins/pbr/dist/check-cross-plugin-sync.js +93 -0
  220. package/plugins/pbr/dist/check-dangerous-commands.js +193 -0
  221. package/plugins/pbr/dist/check-direct-state-write.js +37 -0
  222. package/plugins/pbr/dist/check-doc-sprawl.js +102 -0
  223. package/plugins/pbr/dist/check-phase-boundary.js +191 -0
  224. package/plugins/pbr/dist/check-plan-format.js +227 -0
  225. package/plugins/pbr/dist/check-read-first.js +345 -0
  226. package/plugins/pbr/dist/check-roadmap-sync.js +507 -0
  227. package/plugins/pbr/dist/check-skill-workflow.js +354 -0
  228. package/plugins/pbr/dist/check-state-sync.js +676 -0
  229. package/plugins/pbr/dist/check-subagent-output.js +425 -0
  230. package/plugins/pbr/dist/check-summary-gate.js +188 -0
  231. package/plugins/pbr/dist/context-bridge.js +425 -0
  232. package/plugins/pbr/dist/context-budget-check.js +442 -0
  233. package/plugins/pbr/dist/context-quality.js +271 -0
  234. package/plugins/pbr/dist/enforce-context-budget.js +138 -0
  235. package/plugins/pbr/dist/enforce-pbr-workflow.js +277 -0
  236. package/plugins/pbr/dist/event-handler.js +212 -0
  237. package/plugins/pbr/dist/event-logger.js +125 -0
  238. package/plugins/pbr/dist/feedback-loop.js +155 -0
  239. package/plugins/pbr/dist/graph-update.js +422 -0
  240. package/plugins/pbr/dist/hook-logger.js +114 -0
  241. package/plugins/pbr/dist/hook-server-client.js +361 -0
  242. package/plugins/pbr/dist/hook-server.js +664 -0
  243. package/plugins/pbr/dist/hooks-schema.json +87 -0
  244. package/plugins/pbr/dist/instructions-loaded.js +173 -0
  245. package/plugins/pbr/dist/intercept-plan-mode.js +81 -0
  246. package/plugins/pbr/dist/log-notification.js +131 -0
  247. package/plugins/pbr/dist/log-subagent.js +367 -0
  248. package/plugins/pbr/dist/log-tool-failure.js +140 -0
  249. package/plugins/pbr/dist/milestone-learnings.js +519 -0
  250. package/plugins/pbr/dist/pbr-tools.js +493 -0
  251. package/plugins/pbr/dist/post-bash-triage.js +96 -0
  252. package/plugins/pbr/dist/post-compact.js +135 -0
  253. package/plugins/pbr/dist/post-hoc.js +237 -0
  254. package/plugins/pbr/dist/post-write-dispatch.js +243 -0
  255. package/plugins/pbr/dist/post-write-quality.js +208 -0
  256. package/plugins/pbr/dist/pre-bash-dispatch.js +212 -0
  257. package/plugins/pbr/dist/pre-skill-dispatch.js +114 -0
  258. package/plugins/pbr/dist/pre-task-dispatch.js +269 -0
  259. package/plugins/pbr/dist/pre-write-dispatch.js +234 -0
  260. package/plugins/pbr/dist/progress-tracker.js +173 -0
  261. package/plugins/pbr/dist/prompt-guard.js +114 -0
  262. package/plugins/pbr/dist/prompt-routing.js +209 -0
  263. package/plugins/pbr/dist/quick-status.js +179 -0
  264. package/plugins/pbr/dist/record-incident.js +37 -0
  265. package/plugins/pbr/dist/run-hook.js +132 -0
  266. package/plugins/pbr/dist/session-cleanup.js +653 -0
  267. package/plugins/pbr/dist/session-tracker.js +124 -0
  268. package/plugins/pbr/dist/status-line.js +849 -0
  269. package/plugins/pbr/dist/suggest-compact.js +307 -0
  270. package/plugins/pbr/dist/sync-context-to-claude.js +100 -0
  271. package/plugins/pbr/dist/task-completed.js +206 -0
  272. package/plugins/pbr/dist/track-context-budget.js +432 -0
  273. package/plugins/pbr/dist/track-user-gates.js +88 -0
  274. package/plugins/pbr/dist/trust-tracker.js +193 -0
  275. package/plugins/pbr/dist/validate-commit.js +233 -0
  276. package/plugins/pbr/dist/validate-skill-args.js +222 -0
  277. package/plugins/pbr/dist/validate-task.js +271 -0
  278. package/plugins/pbr/dist/worktree-create.js +144 -0
  279. package/plugins/pbr/dist/worktree-remove.js +147 -0
  280. package/plugins/pbr/hooks/hooks.json +137 -65
  281. package/plugins/pbr/references/agent-contracts.md +39 -8
  282. package/plugins/pbr/references/agent-teams.md +3 -3
  283. package/plugins/pbr/references/archive/checkpoints.md +189 -0
  284. package/plugins/pbr/references/archive/context-quality-tiers.md +45 -0
  285. package/plugins/pbr/references/archive/hook-ordering.md +89 -0
  286. package/plugins/pbr/references/archive/limitations.md +106 -0
  287. package/plugins/pbr/references/archive/pbr-rules.md +194 -0
  288. package/plugins/pbr/references/archive/pbr-tools-cli.md +415 -0
  289. package/plugins/pbr/references/archive/pretooluse-jsonl-behavior.md +58 -0
  290. package/plugins/pbr/references/archive/signal-files.md +41 -0
  291. package/plugins/pbr/references/archive/tmux-setup.md +288 -0
  292. package/plugins/pbr/references/archive/verification-matrix.md +34 -0
  293. package/plugins/pbr/references/archive/verification-patterns.md +277 -0
  294. package/plugins/pbr/references/archive/worktree-sparse-checkout.md +86 -0
  295. package/plugins/pbr/references/assumptions.md +42 -0
  296. package/plugins/pbr/references/checkpoints.md +723 -104
  297. package/plugins/pbr/references/config-reference.md +387 -10
  298. package/plugins/pbr/references/continuation-format.md +1 -0
  299. package/plugins/pbr/references/decimal-phase-calculation.md +65 -0
  300. package/plugins/pbr/references/deviation-rules.md +12 -0
  301. package/plugins/pbr/references/few-shot-examples/audit.md +77 -0
  302. package/plugins/pbr/references/few-shot-examples/check-plan-format.md +172 -0
  303. package/plugins/pbr/references/few-shot-examples/check-subagent-output.md +118 -0
  304. package/plugins/pbr/references/few-shot-examples/integration-checker.md +70 -0
  305. package/plugins/pbr/references/few-shot-examples/nyquist-auditor.md +83 -0
  306. package/plugins/pbr/references/few-shot-examples/plan-checker.md +73 -0
  307. package/plugins/pbr/references/few-shot-examples/ui-checker.md +71 -0
  308. package/plugins/pbr/references/few-shot-examples/verifier.md +109 -0
  309. package/plugins/pbr/references/git-integration.md +110 -27
  310. package/plugins/pbr/references/git-planning-commit.md +35 -0
  311. package/plugins/pbr/references/model-profile-resolution.md +34 -0
  312. package/plugins/pbr/references/model-profiles.md +90 -7
  313. package/plugins/pbr/references/model-selection.md +1 -1
  314. package/plugins/pbr/references/node-repair.md +48 -0
  315. package/plugins/pbr/references/plan-authoring.md +65 -0
  316. package/plugins/pbr/references/plan-format.md +184 -10
  317. package/plugins/pbr/references/questioning.md +138 -49
  318. package/plugins/pbr/references/reading-verification.md +4 -4
  319. package/plugins/pbr/references/tdd.md +263 -0
  320. package/plugins/pbr/references/thinking-models-planning.md +47 -0
  321. package/plugins/pbr/references/thinking-models-verification.md +44 -0
  322. package/plugins/pbr/references/ui-brand.md +449 -0
  323. package/plugins/pbr/references/verification-overrides.md +39 -0
  324. package/plugins/pbr/references/verification-patterns.md +529 -113
  325. package/plugins/pbr/scripts/architecture-guard.js +76 -0
  326. package/plugins/pbr/scripts/audit-checks/behavioral-compliance.js +2098 -0
  327. package/plugins/pbr/scripts/audit-checks/error-analysis.js +989 -0
  328. package/plugins/pbr/scripts/audit-checks/feature-verification.js +723 -0
  329. package/plugins/pbr/scripts/audit-checks/index.js +433 -0
  330. package/plugins/pbr/scripts/audit-checks/infrastructure.js +816 -0
  331. package/plugins/pbr/scripts/audit-checks/quality-metrics.js +455 -0
  332. package/plugins/pbr/scripts/audit-checks/session-quality.js +980 -0
  333. package/plugins/pbr/scripts/audit-checks/si-agent-hook-config-checks.js +396 -0
  334. package/plugins/pbr/scripts/audit-checks/si-cross-cutting-checks.js +272 -0
  335. package/plugins/pbr/scripts/audit-checks/si-skill-checks.js +424 -0
  336. package/plugins/pbr/scripts/audit-checks/workflow-compliance.js +1175 -0
  337. package/plugins/pbr/scripts/audit-dimensions.js +556 -0
  338. package/plugins/pbr/scripts/auto-continue.js +192 -37
  339. package/plugins/pbr/scripts/block-skill-self-read.js +124 -0
  340. package/plugins/pbr/scripts/check-agent-state-write.js +63 -0
  341. package/plugins/pbr/scripts/check-config-change.js +84 -1
  342. package/plugins/pbr/scripts/check-cross-plugin-sync.js +93 -0
  343. package/plugins/pbr/scripts/check-dangerous-commands.js +18 -5
  344. package/plugins/pbr/scripts/check-direct-state-write.js +37 -0
  345. package/plugins/pbr/scripts/check-phase-boundary.js +3 -2
  346. package/plugins/pbr/scripts/check-plan-format.js +153 -354
  347. package/plugins/pbr/scripts/check-read-first.js +345 -0
  348. package/plugins/pbr/scripts/check-roadmap-sync.js +174 -19
  349. package/plugins/pbr/scripts/check-skill-workflow.js +21 -16
  350. package/plugins/pbr/scripts/check-state-sync.js +352 -220
  351. package/plugins/pbr/scripts/check-subagent-output.js +296 -333
  352. package/plugins/pbr/scripts/check-summary-gate.js +5 -15
  353. package/plugins/pbr/scripts/commands/benchmarks.js +195 -0
  354. package/plugins/pbr/scripts/commands/calibrate.js +530 -0
  355. package/plugins/pbr/scripts/commands/config.js +72 -0
  356. package/plugins/pbr/scripts/commands/misc.js +779 -0
  357. package/plugins/pbr/scripts/commands/phase.js +293 -0
  358. package/plugins/pbr/scripts/commands/roadmap.js +75 -0
  359. package/plugins/pbr/scripts/commands/state.js +84 -0
  360. package/plugins/pbr/scripts/commands/stress-test.js +349 -0
  361. package/plugins/pbr/scripts/commands/todo.js +191 -0
  362. package/plugins/pbr/scripts/commands/verify.js +169 -0
  363. package/plugins/pbr/scripts/config-schema.json +1183 -95
  364. package/plugins/pbr/scripts/context-bridge.js +425 -0
  365. package/plugins/pbr/scripts/context-budget-check.js +171 -16
  366. package/plugins/pbr/scripts/context-quality.js +271 -0
  367. package/plugins/pbr/scripts/enforce-context-budget.js +138 -0
  368. package/plugins/pbr/scripts/enforce-pbr-workflow.js +277 -0
  369. package/plugins/pbr/scripts/event-handler.js +137 -87
  370. package/plugins/pbr/scripts/event-logger.js +58 -25
  371. package/plugins/pbr/scripts/feedback-loop.js +155 -0
  372. package/plugins/pbr/scripts/graph-update.js +422 -0
  373. package/plugins/pbr/scripts/hook-logger.js +69 -35
  374. package/plugins/pbr/scripts/hook-server-client.js +361 -0
  375. package/plugins/pbr/scripts/hook-server.js +664 -0
  376. package/plugins/pbr/scripts/hooks-schema.json +12 -5
  377. package/plugins/pbr/scripts/instructions-loaded.js +173 -0
  378. package/plugins/pbr/scripts/intent-router.cjs +147 -0
  379. package/plugins/pbr/scripts/intercept-plan-mode.js +52 -18
  380. package/plugins/pbr/scripts/lib/alternatives.js +203 -0
  381. package/plugins/pbr/scripts/lib/audit.js +65 -0
  382. package/plugins/pbr/scripts/lib/auto-cleanup.js +221 -0
  383. package/plugins/pbr/scripts/lib/auto-verify.js +123 -0
  384. package/plugins/pbr/scripts/lib/benchmark.js +190 -0
  385. package/plugins/pbr/scripts/lib/build.js +719 -0
  386. package/plugins/pbr/scripts/lib/ci-fix-loop.js +228 -0
  387. package/plugins/pbr/scripts/lib/commands.js +483 -0
  388. package/plugins/pbr/scripts/lib/compound.js +222 -0
  389. package/plugins/pbr/scripts/lib/config-cache.js +83 -0
  390. package/plugins/pbr/scripts/lib/config.js +1469 -0
  391. package/plugins/pbr/scripts/lib/context.js +254 -0
  392. package/plugins/pbr/scripts/lib/contextual-help.js +183 -0
  393. package/plugins/pbr/scripts/lib/convention-detector.js +413 -0
  394. package/plugins/pbr/scripts/lib/core.js +1585 -0
  395. package/plugins/pbr/scripts/lib/dashboard-launch.js +364 -0
  396. package/plugins/pbr/scripts/lib/data-hygiene.js +179 -0
  397. package/plugins/pbr/scripts/lib/decision-extraction.js +183 -0
  398. package/plugins/pbr/scripts/lib/decisions.js +194 -0
  399. package/plugins/pbr/scripts/lib/dependency-break.js +147 -0
  400. package/plugins/pbr/scripts/lib/format-validators.js +1049 -0
  401. package/plugins/pbr/scripts/lib/frontmatter.js +302 -0
  402. package/plugins/pbr/scripts/lib/gates/advisories.js +133 -0
  403. package/plugins/pbr/scripts/lib/gates/build-dependency.js +118 -0
  404. package/plugins/pbr/scripts/lib/gates/build-executor.js +106 -0
  405. package/plugins/pbr/scripts/lib/gates/doc-existence.js +46 -0
  406. package/plugins/pbr/scripts/lib/gates/helpers.js +98 -0
  407. package/plugins/pbr/scripts/lib/gates/inline-execution.js +187 -0
  408. package/plugins/pbr/scripts/lib/gates/milestone-complete.js +139 -0
  409. package/plugins/pbr/scripts/lib/gates/milestone-summary.js +121 -0
  410. package/plugins/pbr/scripts/lib/gates/multi-phase-loader.js +149 -0
  411. package/plugins/pbr/scripts/lib/gates/plan-executor.js +36 -0
  412. package/plugins/pbr/scripts/lib/gates/plan-validation.js +115 -0
  413. package/plugins/pbr/scripts/lib/gates/quick-executor.js +78 -0
  414. package/plugins/pbr/scripts/lib/gates/review-planner.js +63 -0
  415. package/plugins/pbr/scripts/lib/gates/review-verifier.js +71 -0
  416. package/plugins/pbr/scripts/lib/gates/rich-agent-context.js +148 -0
  417. package/plugins/pbr/scripts/lib/gates/sprint-preflight.js +30 -0
  418. package/plugins/pbr/scripts/lib/gates/user-confirmation.js +95 -0
  419. package/plugins/pbr/scripts/lib/graph-cli.js +89 -0
  420. package/plugins/pbr/scripts/lib/graph.js +553 -0
  421. package/plugins/pbr/scripts/lib/handoff-validators.js +224 -0
  422. package/plugins/pbr/scripts/lib/health-checks.js +107 -0
  423. package/plugins/pbr/scripts/lib/health-phase06.js +120 -0
  424. package/plugins/pbr/scripts/lib/health.js +132 -0
  425. package/plugins/pbr/scripts/lib/help.js +100 -0
  426. package/plugins/pbr/scripts/lib/history.js +150 -0
  427. package/plugins/pbr/scripts/lib/impact-analysis.js +319 -0
  428. package/plugins/pbr/scripts/lib/incidents.js +190 -0
  429. package/plugins/pbr/scripts/lib/init.js +643 -0
  430. package/plugins/pbr/scripts/lib/insights-parser.js +320 -0
  431. package/plugins/pbr/scripts/lib/intel.js +653 -0
  432. package/plugins/pbr/scripts/lib/learnings.js +511 -0
  433. package/plugins/pbr/scripts/lib/migrate.js +309 -0
  434. package/plugins/pbr/scripts/lib/milestone.js +306 -0
  435. package/plugins/pbr/scripts/lib/msys-path.js +20 -0
  436. package/plugins/pbr/scripts/lib/negative-knowledge.js +194 -0
  437. package/plugins/pbr/scripts/lib/notification-throttle.js +141 -0
  438. package/plugins/pbr/scripts/lib/onboarding-generator.js +288 -0
  439. package/plugins/pbr/scripts/lib/parse-args.js +134 -0
  440. package/plugins/pbr/scripts/lib/pattern-routing.js +55 -0
  441. package/plugins/pbr/scripts/lib/patterns.js +272 -0
  442. package/plugins/pbr/scripts/lib/perf.js +190 -0
  443. package/plugins/pbr/scripts/lib/phase.js +1043 -0
  444. package/plugins/pbr/scripts/lib/pid-lock.js +156 -0
  445. package/plugins/pbr/scripts/lib/post-hoc.js +160 -0
  446. package/plugins/pbr/scripts/lib/pre-commit-checks.js +220 -0
  447. package/plugins/pbr/scripts/lib/pre-research.js +126 -0
  448. package/plugins/pbr/scripts/lib/premature-completion.js +312 -0
  449. package/plugins/pbr/scripts/lib/preview.js +174 -0
  450. package/plugins/pbr/scripts/lib/progress-visualization.js +296 -0
  451. package/plugins/pbr/scripts/lib/quick-init.js +131 -0
  452. package/plugins/pbr/scripts/lib/reference.js +236 -0
  453. package/plugins/pbr/scripts/lib/requirements.js +153 -0
  454. package/plugins/pbr/scripts/lib/resolve-root.js +66 -0
  455. package/plugins/pbr/scripts/lib/reverse-spec.js +259 -0
  456. package/plugins/pbr/scripts/lib/roadmap.js +1089 -0
  457. package/plugins/pbr/scripts/lib/security-scan.js +200 -0
  458. package/plugins/pbr/scripts/lib/session-briefing.js +918 -0
  459. package/plugins/pbr/scripts/lib/skill-section.js +99 -0
  460. package/plugins/pbr/scripts/lib/smart-next-task.js +198 -0
  461. package/plugins/pbr/scripts/lib/snapshot-manager.js +232 -0
  462. package/plugins/pbr/scripts/lib/spec-diff.js +209 -0
  463. package/plugins/pbr/scripts/lib/spec-engine.js +189 -0
  464. package/plugins/pbr/scripts/lib/spot-check.js +539 -0
  465. package/plugins/pbr/scripts/lib/state-queue.js +171 -0
  466. package/plugins/pbr/scripts/lib/state.js +1082 -0
  467. package/plugins/pbr/scripts/lib/status-render.js +511 -0
  468. package/plugins/pbr/scripts/lib/step-verify.js +149 -0
  469. package/plugins/pbr/scripts/lib/subagent-validators.js +1119 -0
  470. package/plugins/pbr/scripts/lib/suggest-next.js +435 -0
  471. package/plugins/pbr/scripts/lib/tech-debt-scanner.js +116 -0
  472. package/plugins/pbr/scripts/lib/templates.js +362 -0
  473. package/plugins/pbr/scripts/lib/test-selection.js +163 -0
  474. package/plugins/pbr/scripts/lib/todo.js +300 -0
  475. package/plugins/pbr/scripts/lib/verify.js +1561 -0
  476. package/plugins/pbr/scripts/log-notification.js +131 -0
  477. package/plugins/pbr/scripts/log-subagent.js +221 -18
  478. package/plugins/pbr/scripts/log-tool-failure.js +60 -5
  479. package/plugins/pbr/scripts/milestone-learnings.js +519 -0
  480. package/plugins/pbr/scripts/package.json +1 -1
  481. package/plugins/pbr/scripts/pbr-tools.js +362 -1247
  482. package/plugins/pbr/scripts/post-bash-triage.js +96 -0
  483. package/plugins/pbr/scripts/post-compact.js +135 -0
  484. package/plugins/pbr/scripts/post-hoc.js +237 -0
  485. package/plugins/pbr/scripts/post-write-dispatch.js +201 -31
  486. package/plugins/pbr/scripts/post-write-quality.js +4 -3
  487. package/plugins/pbr/scripts/pre-bash-dispatch.js +147 -51
  488. package/plugins/pbr/scripts/pre-skill-dispatch.js +114 -0
  489. package/plugins/pbr/scripts/pre-task-dispatch.js +269 -0
  490. package/plugins/pbr/scripts/pre-write-dispatch.js +170 -72
  491. package/plugins/pbr/scripts/progress-tracker.js +121 -324
  492. package/plugins/pbr/scripts/prompt-guard.js +114 -0
  493. package/plugins/pbr/scripts/prompt-routing.js +209 -0
  494. package/plugins/pbr/scripts/quick-status.js +179 -0
  495. package/plugins/pbr/scripts/record-incident.js +37 -0
  496. package/plugins/pbr/scripts/risk-classifier.cjs +123 -0
  497. package/plugins/pbr/scripts/run-hook.js +63 -23
  498. package/plugins/pbr/scripts/session-cleanup.js +428 -29
  499. package/plugins/pbr/scripts/session-tracker.js +124 -0
  500. package/plugins/pbr/scripts/status-line.js +571 -43
  501. package/plugins/pbr/scripts/suggest-compact.js +201 -13
  502. package/plugins/pbr/scripts/sync-context-to-claude.js +100 -0
  503. package/plugins/pbr/scripts/task-completed.js +165 -4
  504. package/plugins/pbr/scripts/test/config.test.js +126 -0
  505. package/plugins/pbr/scripts/test/cross-platform.test.js +120 -0
  506. package/plugins/pbr/scripts/test/fixtures/config.json +20 -0
  507. package/plugins/pbr/scripts/test/fixtures/plan.md +54 -0
  508. package/plugins/pbr/scripts/test/fixtures/project.md +30 -0
  509. package/plugins/pbr/scripts/test/fixtures/roadmap.md +55 -0
  510. package/plugins/pbr/scripts/test/fixtures/state.md +60 -0
  511. package/plugins/pbr/scripts/test/fixtures/summary.md +35 -0
  512. package/plugins/pbr/scripts/test/fixtures.test.js +184 -0
  513. package/plugins/pbr/scripts/test/phase.test.js +142 -0
  514. package/plugins/pbr/scripts/test/roadmap.test.js +96 -0
  515. package/plugins/pbr/scripts/test/state.test.js +155 -0
  516. package/plugins/pbr/scripts/track-context-budget.js +368 -104
  517. package/plugins/pbr/scripts/track-user-gates.js +88 -0
  518. package/plugins/pbr/scripts/trust-tracker.js +193 -0
  519. package/plugins/pbr/scripts/validate-commit.js +51 -19
  520. package/plugins/pbr/scripts/validate-skill-args.js +85 -14
  521. package/plugins/pbr/scripts/validate-task.js +83 -622
  522. package/plugins/pbr/scripts/worktree-create.js +144 -0
  523. package/plugins/pbr/scripts/worktree-remove.js +147 -0
  524. package/plugins/pbr/skills/audit/SKILL.md +195 -24
  525. package/plugins/pbr/skills/audit-fix/SKILL.md +326 -0
  526. package/plugins/pbr/skills/autonomous/SKILL.md +551 -0
  527. package/plugins/pbr/skills/backlog/SKILL.md +56 -0
  528. package/plugins/pbr/skills/begin/SKILL.md +508 -153
  529. package/plugins/pbr/skills/begin/templates/STATE.md.tmpl +1 -2
  530. package/plugins/pbr/skills/begin/templates/config.json.tmpl +419 -36
  531. package/plugins/pbr/skills/begin/templates/researcher-prompt.md.tmpl +28 -0
  532. package/plugins/pbr/skills/begin/templates/roadmap-prompt.md.tmpl +28 -3
  533. package/plugins/pbr/skills/begin/templates/synthesis-prompt.md.tmpl +33 -5
  534. package/plugins/pbr/skills/build/SKILL.md +1180 -357
  535. package/plugins/pbr/skills/build/templates/continuation-prompt.md.tmpl +26 -0
  536. package/plugins/pbr/skills/build/templates/executor-prompt.md.tmpl +77 -0
  537. package/plugins/pbr/skills/build/templates/inline-verifier-prompt.md.tmpl +33 -0
  538. package/plugins/pbr/skills/build/templates/qa-round-context.md.tmpl +16 -0
  539. package/plugins/pbr/skills/config/SKILL.md +112 -9
  540. package/plugins/pbr/skills/continue/SKILL.md +113 -33
  541. package/plugins/pbr/skills/dashboard/SKILL.md +21 -9
  542. package/plugins/pbr/skills/debug/SKILL.md +70 -12
  543. package/plugins/pbr/skills/debug/templates/continuation-prompt.md.tmpl +12 -1
  544. package/plugins/pbr/skills/debug/templates/initial-investigation-prompt.md.tmpl +12 -5
  545. package/plugins/pbr/skills/discuss/SKILL.md +205 -24
  546. package/plugins/pbr/skills/discuss/templates/CONTEXT.md.tmpl +21 -1
  547. package/plugins/pbr/skills/do/SKILL.md +119 -24
  548. package/plugins/pbr/skills/explore/SKILL.md +95 -20
  549. package/plugins/pbr/skills/fast/SKILL.md +94 -0
  550. package/plugins/pbr/skills/forensics/SKILL.md +144 -0
  551. package/plugins/pbr/skills/health/SKILL.md +35 -115
  552. package/plugins/pbr/skills/help/SKILL.md +83 -123
  553. package/plugins/pbr/skills/import/SKILL.md +332 -13
  554. package/plugins/pbr/skills/intel/SKILL.md +131 -0
  555. package/plugins/pbr/skills/list-phase-assumptions/SKILL.md +231 -0
  556. package/plugins/pbr/skills/milestone/SKILL.md +383 -274
  557. package/plugins/pbr/skills/milestone/templates/audit-output.md.tmpl +76 -0
  558. package/plugins/pbr/skills/milestone/templates/complete-output.md.tmpl +32 -0
  559. package/plugins/pbr/skills/milestone/templates/edge-cases.md +54 -0
  560. package/plugins/pbr/skills/milestone/templates/gaps-output.md.tmpl +25 -0
  561. package/plugins/pbr/skills/milestone/templates/integration-checker-prompt.md.tmpl +25 -0
  562. package/plugins/pbr/skills/milestone/templates/new-output.md.tmpl +29 -0
  563. package/plugins/pbr/skills/milestone-summary/SKILL.md +86 -0
  564. package/plugins/pbr/skills/note/SKILL.md +20 -4
  565. package/plugins/pbr/skills/pause/SKILL.md +54 -14
  566. package/plugins/pbr/skills/pause/templates/continue-here.md.tmpl +33 -52
  567. package/plugins/pbr/skills/plan/SKILL.md +526 -280
  568. package/plugins/pbr/skills/plan/templates/checker-prompt.md.tmpl +20 -2
  569. package/plugins/pbr/skills/plan/templates/completion-output.md.tmpl +27 -0
  570. package/plugins/pbr/skills/plan/templates/planner-prompt.md.tmpl +43 -1
  571. package/plugins/pbr/skills/plan/templates/revision-prompt.md.tmpl +21 -5
  572. package/plugins/pbr/skills/profile/SKILL.md +185 -0
  573. package/plugins/pbr/skills/profile-user/SKILL.md +227 -0
  574. package/plugins/pbr/skills/quick/SKILL.md +435 -100
  575. package/plugins/pbr/skills/release/SKILL.md +206 -0
  576. package/plugins/pbr/skills/resume/SKILL.md +170 -46
  577. package/plugins/pbr/skills/review/SKILL.md +233 -165
  578. package/plugins/pbr/skills/review/templates/verifier-prompt.md.tmpl +7 -0
  579. package/plugins/pbr/skills/scan/SKILL.md +152 -106
  580. package/plugins/pbr/skills/scan/templates/mapper-prompt.md.tmpl +5 -56
  581. package/plugins/pbr/skills/seed/SKILL.md +87 -0
  582. package/plugins/pbr/skills/session-report/SKILL.md +130 -0
  583. package/plugins/pbr/skills/setup/SKILL.md +150 -202
  584. package/plugins/pbr/skills/shared/agent-context-enrichment.md +21 -0
  585. package/plugins/pbr/skills/shared/agent-type-resolution.md +32 -0
  586. package/plugins/pbr/skills/shared/commit-planning-docs.md +8 -0
  587. package/plugins/pbr/skills/shared/context-budget.md +66 -1
  588. package/plugins/pbr/skills/shared/context-loader-task.md +18 -11
  589. package/plugins/pbr/skills/shared/criterion-writing.md +58 -0
  590. package/plugins/pbr/skills/shared/digest-select.md +2 -2
  591. package/plugins/pbr/skills/shared/domain-probes.md +1 -1
  592. package/plugins/pbr/skills/shared/error-reporting.md +38 -60
  593. package/plugins/pbr/skills/shared/gate-prompts.md +4 -2
  594. package/plugins/pbr/skills/shared/memory-capture.md +48 -0
  595. package/plugins/pbr/skills/shared/phase-argument-parsing.md +4 -4
  596. package/plugins/pbr/skills/shared/revision-loop.md +24 -6
  597. package/plugins/pbr/skills/shared/state-update.md +46 -61
  598. package/plugins/pbr/skills/shared/universal-anti-patterns.md +27 -4
  599. package/plugins/pbr/skills/ship/SKILL.md +155 -0
  600. package/plugins/pbr/skills/stats/SKILL.md +110 -0
  601. package/plugins/pbr/skills/status/SKILL.md +185 -119
  602. package/plugins/pbr/skills/test/SKILL.md +254 -0
  603. package/plugins/pbr/skills/thread/SKILL.md +73 -0
  604. package/plugins/pbr/skills/todo/SKILL.md +28 -72
  605. package/plugins/pbr/skills/ui-phase/SKILL.md +180 -0
  606. package/plugins/pbr/skills/ui-review/SKILL.md +206 -0
  607. package/plugins/pbr/skills/undo/SKILL.md +221 -0
  608. package/plugins/pbr/skills/validate-phase/SKILL.md +362 -0
  609. package/plugins/pbr/templates/CONTEXT.md.tmpl +45 -20
  610. package/plugins/pbr/templates/DISCOVERY.md.tmpl +29 -0
  611. package/plugins/pbr/templates/DISCUSSION-LOG.md.tmpl +49 -0
  612. package/plugins/pbr/templates/HANDOFF.json.tmpl +30 -0
  613. package/plugins/pbr/templates/INTEGRATION-REPORT.md.tmpl +18 -2
  614. package/plugins/pbr/templates/MILESTONE-AUDIT.md.tmpl +44 -0
  615. package/plugins/pbr/templates/PROJECT.md.tmpl +126 -0
  616. package/plugins/pbr/templates/REQUIREMENTS.md.tmpl +96 -0
  617. package/plugins/pbr/templates/RETROSPECTIVE.md.tmpl +43 -0
  618. package/plugins/pbr/templates/ROADMAP.md.tmpl +106 -14
  619. package/plugins/pbr/templates/SUMMARY-complex.md.tmpl +133 -0
  620. package/plugins/pbr/templates/SUMMARY-minimal.md.tmpl +55 -0
  621. package/plugins/pbr/templates/SUMMARY.md.tmpl +21 -0
  622. package/plugins/pbr/templates/UAT.md.tmpl +94 -0
  623. package/plugins/pbr/templates/UI-SPEC.md.tmpl +144 -0
  624. package/plugins/pbr/templates/VALIDATION.md.tmpl +94 -0
  625. package/plugins/pbr/templates/VERIFICATION-DETAIL.md.tmpl +54 -13
  626. package/plugins/pbr/templates/project-CONTEXT.md.tmpl +59 -0
  627. package/plugins/pbr/templates/research-outputs/ARCHITECTURE.md.tmpl +91 -0
  628. package/plugins/pbr/templates/research-outputs/FEATURES.md.tmpl +64 -0
  629. package/plugins/pbr/templates/research-outputs/PITFALLS.md.tmpl +50 -0
  630. package/plugins/pbr/templates/research-outputs/STACK.md.tmpl +63 -0
  631. package/plugins/pbr/templates/research-outputs/SUMMARY.md.tmpl +98 -0
  632. package/scripts/build-hooks.js +61 -0
  633. package/scripts/check-ci.js +100 -0
  634. package/scripts/clean-changelog.js +364 -0
  635. package/scripts/generate-derivatives.js +581 -0
  636. package/scripts/posttest.js +93 -0
  637. package/scripts/release.js +262 -0
  638. package/scripts/run-tests.cjs +29 -0
  639. package/scripts/test-wrapper.js +43 -0
  640. package/dashboard/bin/cli.js +0 -25
  641. package/dashboard/public/css/layout.css +0 -704
  642. package/dashboard/public/css/status-colors.css +0 -98
  643. package/dashboard/public/css/tokens.css +0 -59
  644. package/dashboard/public/js/htmx-title.js +0 -5
  645. package/dashboard/public/js/sidebar-toggle.js +0 -34
  646. package/dashboard/public/js/sse-client.js +0 -100
  647. package/dashboard/public/js/theme-toggle.js +0 -46
  648. package/dashboard/src/app.js +0 -91
  649. package/dashboard/src/middleware/current-phase.js +0 -24
  650. package/dashboard/src/middleware/errorHandler.js +0 -52
  651. package/dashboard/src/middleware/notFoundHandler.js +0 -9
  652. package/dashboard/src/repositories/planning.repository.js +0 -130
  653. package/dashboard/src/routes/events.routes.js +0 -45
  654. package/dashboard/src/routes/index.routes.js +0 -35
  655. package/dashboard/src/routes/pages.routes.js +0 -426
  656. package/dashboard/src/server.js +0 -42
  657. package/dashboard/src/services/analytics.service.js +0 -141
  658. package/dashboard/src/services/dashboard.service.js +0 -309
  659. package/dashboard/src/services/milestone.service.js +0 -222
  660. package/dashboard/src/services/notes.service.js +0 -50
  661. package/dashboard/src/services/phase.service.js +0 -232
  662. package/dashboard/src/services/project.service.js +0 -57
  663. package/dashboard/src/services/roadmap.service.js +0 -258
  664. package/dashboard/src/services/sse.service.js +0 -58
  665. package/dashboard/src/services/todo.service.js +0 -272
  666. package/dashboard/src/services/watcher.service.js +0 -48
  667. package/dashboard/src/utils/cache.js +0 -55
  668. package/dashboard/src/views/analytics.ejs +0 -5
  669. package/dashboard/src/views/coming-soon.ejs +0 -11
  670. package/dashboard/src/views/dependencies.ejs +0 -5
  671. package/dashboard/src/views/error.ejs +0 -20
  672. package/dashboard/src/views/index.ejs +0 -5
  673. package/dashboard/src/views/milestone-detail.ejs +0 -5
  674. package/dashboard/src/views/milestones.ejs +0 -5
  675. package/dashboard/src/views/notes.ejs +0 -5
  676. package/dashboard/src/views/partials/analytics-content.ejs +0 -90
  677. package/dashboard/src/views/partials/breadcrumbs.ejs +0 -14
  678. package/dashboard/src/views/partials/dashboard-content.ejs +0 -84
  679. package/dashboard/src/views/partials/dependencies-content.ejs +0 -48
  680. package/dashboard/src/views/partials/empty-state.ejs +0 -7
  681. package/dashboard/src/views/partials/footer.ejs +0 -3
  682. package/dashboard/src/views/partials/head.ejs +0 -30
  683. package/dashboard/src/views/partials/header.ejs +0 -21
  684. package/dashboard/src/views/partials/layout-bottom.ejs +0 -43
  685. package/dashboard/src/views/partials/layout-top.ejs +0 -16
  686. package/dashboard/src/views/partials/milestone-detail-content.ejs +0 -20
  687. package/dashboard/src/views/partials/milestones-content.ejs +0 -88
  688. package/dashboard/src/views/partials/notes-content.ejs +0 -23
  689. package/dashboard/src/views/partials/phase-content.ejs +0 -193
  690. package/dashboard/src/views/partials/phase-doc-content.ejs +0 -38
  691. package/dashboard/src/views/partials/phases-content.ejs +0 -124
  692. package/dashboard/src/views/partials/roadmap-content.ejs +0 -180
  693. package/dashboard/src/views/partials/sidebar.ejs +0 -99
  694. package/dashboard/src/views/partials/todo-create-content.ejs +0 -54
  695. package/dashboard/src/views/partials/todo-detail-content.ejs +0 -42
  696. package/dashboard/src/views/partials/todos-content.ejs +0 -97
  697. package/dashboard/src/views/phase-detail.ejs +0 -5
  698. package/dashboard/src/views/phase-doc.ejs +0 -5
  699. package/dashboard/src/views/phases.ejs +0 -5
  700. package/dashboard/src/views/roadmap.ejs +0 -5
  701. package/dashboard/src/views/todo-create.ejs +0 -5
  702. package/dashboard/src/views/todo-detail.ejs +0 -5
  703. package/dashboard/src/views/todos.ejs +0 -5
  704. package/plugins/copilot-pbr/CHANGELOG.md +0 -19
  705. package/plugins/copilot-pbr/README.md +0 -139
  706. package/plugins/copilot-pbr/agents/audit.agent.md +0 -113
  707. package/plugins/copilot-pbr/agents/codebase-mapper.agent.md +0 -151
  708. package/plugins/copilot-pbr/agents/debugger.agent.md +0 -184
  709. package/plugins/copilot-pbr/agents/executor.agent.md +0 -269
  710. package/plugins/copilot-pbr/agents/general.agent.md +0 -89
  711. package/plugins/copilot-pbr/agents/integration-checker.agent.md +0 -121
  712. package/plugins/copilot-pbr/agents/plan-checker.agent.md +0 -208
  713. package/plugins/copilot-pbr/agents/planner.agent.md +0 -240
  714. package/plugins/copilot-pbr/agents/researcher.agent.md +0 -188
  715. package/plugins/copilot-pbr/agents/synthesizer.agent.md +0 -126
  716. package/plugins/copilot-pbr/agents/verifier.agent.md +0 -228
  717. package/plugins/copilot-pbr/hooks/hooks.json +0 -258
  718. package/plugins/copilot-pbr/plugin.json +0 -30
  719. package/plugins/copilot-pbr/references/agent-anti-patterns.md +0 -25
  720. package/plugins/copilot-pbr/references/agent-contracts.md +0 -297
  721. package/plugins/copilot-pbr/references/agent-interactions.md +0 -135
  722. package/plugins/copilot-pbr/references/agent-teams.md +0 -55
  723. package/plugins/copilot-pbr/references/checkpoints.md +0 -158
  724. package/plugins/copilot-pbr/references/common-bug-patterns.md +0 -14
  725. package/plugins/copilot-pbr/references/config-reference.md +0 -442
  726. package/plugins/copilot-pbr/references/continuation-format.md +0 -213
  727. package/plugins/copilot-pbr/references/deviation-rules.md +0 -113
  728. package/plugins/copilot-pbr/references/git-integration.md +0 -227
  729. package/plugins/copilot-pbr/references/integration-patterns.md +0 -118
  730. package/plugins/copilot-pbr/references/model-profiles.md +0 -100
  731. package/plugins/copilot-pbr/references/model-selection.md +0 -32
  732. package/plugins/copilot-pbr/references/pbr-rules.md +0 -195
  733. package/plugins/copilot-pbr/references/pbr-tools-cli.md +0 -285
  734. package/plugins/copilot-pbr/references/plan-authoring.md +0 -182
  735. package/plugins/copilot-pbr/references/plan-format.md +0 -288
  736. package/plugins/copilot-pbr/references/planning-config.md +0 -214
  737. package/plugins/copilot-pbr/references/questioning.md +0 -215
  738. package/plugins/copilot-pbr/references/reading-verification.md +0 -128
  739. package/plugins/copilot-pbr/references/stub-patterns.md +0 -161
  740. package/plugins/copilot-pbr/references/subagent-coordination.md +0 -120
  741. package/plugins/copilot-pbr/references/ui-formatting.md +0 -444
  742. package/plugins/copilot-pbr/references/verification-patterns.md +0 -199
  743. package/plugins/copilot-pbr/references/wave-execution.md +0 -96
  744. package/plugins/copilot-pbr/rules/pbr-workflow.mdc +0 -48
  745. package/plugins/copilot-pbr/setup.ps1 +0 -93
  746. package/plugins/copilot-pbr/setup.sh +0 -93
  747. package/plugins/copilot-pbr/skills/audit/SKILL.md +0 -330
  748. package/plugins/copilot-pbr/skills/begin/SKILL.md +0 -589
  749. package/plugins/copilot-pbr/skills/begin/templates/PROJECT.md.tmpl +0 -34
  750. package/plugins/copilot-pbr/skills/begin/templates/REQUIREMENTS.md.tmpl +0 -19
  751. package/plugins/copilot-pbr/skills/begin/templates/STATE.md.tmpl +0 -50
  752. package/plugins/copilot-pbr/skills/begin/templates/config.json.tmpl +0 -64
  753. package/plugins/copilot-pbr/skills/begin/templates/researcher-prompt.md.tmpl +0 -20
  754. package/plugins/copilot-pbr/skills/begin/templates/roadmap-prompt.md.tmpl +0 -31
  755. package/plugins/copilot-pbr/skills/begin/templates/synthesis-prompt.md.tmpl +0 -17
  756. package/plugins/copilot-pbr/skills/build/SKILL.md +0 -959
  757. package/plugins/copilot-pbr/skills/config/SKILL.md +0 -250
  758. package/plugins/copilot-pbr/skills/continue/SKILL.md +0 -159
  759. package/plugins/copilot-pbr/skills/dashboard/SKILL.md +0 -43
  760. package/plugins/copilot-pbr/skills/debug/SKILL.md +0 -508
  761. package/plugins/copilot-pbr/skills/debug/templates/continuation-prompt.md.tmpl +0 -17
  762. package/plugins/copilot-pbr/skills/debug/templates/initial-investigation-prompt.md.tmpl +0 -28
  763. package/plugins/copilot-pbr/skills/discuss/SKILL.md +0 -353
  764. package/plugins/copilot-pbr/skills/discuss/templates/CONTEXT.md.tmpl +0 -62
  765. package/plugins/copilot-pbr/skills/discuss/templates/decision-categories.md +0 -10
  766. package/plugins/copilot-pbr/skills/do/SKILL.md +0 -66
  767. package/plugins/copilot-pbr/skills/explore/SKILL.md +0 -373
  768. package/plugins/copilot-pbr/skills/health/SKILL.md +0 -283
  769. package/plugins/copilot-pbr/skills/health/templates/check-pattern.md.tmpl +0 -31
  770. package/plugins/copilot-pbr/skills/health/templates/output-format.md.tmpl +0 -64
  771. package/plugins/copilot-pbr/skills/help/SKILL.md +0 -193
  772. package/plugins/copilot-pbr/skills/import/SKILL.md +0 -502
  773. package/plugins/copilot-pbr/skills/milestone/SKILL.md +0 -806
  774. package/plugins/copilot-pbr/skills/milestone/templates/audit-report.md.tmpl +0 -49
  775. package/plugins/copilot-pbr/skills/milestone/templates/stats-file.md.tmpl +0 -31
  776. package/plugins/copilot-pbr/skills/note/SKILL.md +0 -213
  777. package/plugins/copilot-pbr/skills/pause/SKILL.md +0 -247
  778. package/plugins/copilot-pbr/skills/pause/templates/continue-here.md.tmpl +0 -72
  779. package/plugins/copilot-pbr/skills/plan/SKILL.md +0 -662
  780. package/plugins/copilot-pbr/skills/plan/templates/checker-prompt.md.tmpl +0 -22
  781. package/plugins/copilot-pbr/skills/plan/templates/gap-closure-prompt.md.tmpl +0 -33
  782. package/plugins/copilot-pbr/skills/plan/templates/planner-prompt.md.tmpl +0 -39
  783. package/plugins/copilot-pbr/skills/plan/templates/researcher-prompt.md.tmpl +0 -20
  784. package/plugins/copilot-pbr/skills/plan/templates/revision-prompt.md.tmpl +0 -24
  785. package/plugins/copilot-pbr/skills/quick/SKILL.md +0 -376
  786. package/plugins/copilot-pbr/skills/resume/SKILL.md +0 -399
  787. package/plugins/copilot-pbr/skills/review/SKILL.md +0 -653
  788. package/plugins/copilot-pbr/skills/review/templates/debugger-prompt.md.tmpl +0 -61
  789. package/plugins/copilot-pbr/skills/review/templates/gap-planner-prompt.md.tmpl +0 -41
  790. package/plugins/copilot-pbr/skills/review/templates/verifier-prompt.md.tmpl +0 -116
  791. package/plugins/copilot-pbr/skills/scan/SKILL.md +0 -299
  792. package/plugins/copilot-pbr/skills/scan/templates/mapper-prompt.md.tmpl +0 -202
  793. package/plugins/copilot-pbr/skills/setup/SKILL.md +0 -296
  794. package/plugins/copilot-pbr/skills/shared/commit-planning-docs.md +0 -36
  795. package/plugins/copilot-pbr/skills/shared/config-loading.md +0 -103
  796. package/plugins/copilot-pbr/skills/shared/context-budget.md +0 -41
  797. package/plugins/copilot-pbr/skills/shared/context-loader-task.md +0 -87
  798. package/plugins/copilot-pbr/skills/shared/digest-select.md +0 -80
  799. package/plugins/copilot-pbr/skills/shared/domain-probes.md +0 -126
  800. package/plugins/copilot-pbr/skills/shared/error-recovery-strategies.md +0 -51
  801. package/plugins/copilot-pbr/skills/shared/error-reporting.md +0 -81
  802. package/plugins/copilot-pbr/skills/shared/gate-prompts.md +0 -389
  803. package/plugins/copilot-pbr/skills/shared/phase-argument-parsing.md +0 -46
  804. package/plugins/copilot-pbr/skills/shared/progress-display.md +0 -53
  805. package/plugins/copilot-pbr/skills/shared/revision-loop.md +0 -82
  806. package/plugins/copilot-pbr/skills/shared/state-loading.md +0 -63
  807. package/plugins/copilot-pbr/skills/shared/state-update.md +0 -170
  808. package/plugins/copilot-pbr/skills/shared/universal-anti-patterns.md +0 -38
  809. package/plugins/copilot-pbr/skills/status/SKILL.md +0 -362
  810. package/plugins/copilot-pbr/skills/statusline/SKILL.md +0 -149
  811. package/plugins/copilot-pbr/skills/todo/SKILL.md +0 -279
  812. package/plugins/copilot-pbr/templates/CONTEXT.md.tmpl +0 -53
  813. package/plugins/copilot-pbr/templates/INTEGRATION-REPORT.md.tmpl +0 -152
  814. package/plugins/copilot-pbr/templates/RESEARCH-SUMMARY.md.tmpl +0 -98
  815. package/plugins/copilot-pbr/templates/ROADMAP.md.tmpl +0 -48
  816. package/plugins/copilot-pbr/templates/SUMMARY.md.tmpl +0 -82
  817. package/plugins/copilot-pbr/templates/VERIFICATION-DETAIL.md.tmpl +0 -117
  818. package/plugins/copilot-pbr/templates/codebase/ARCHITECTURE.md.tmpl +0 -98
  819. package/plugins/copilot-pbr/templates/codebase/CONCERNS.md.tmpl +0 -93
  820. package/plugins/copilot-pbr/templates/codebase/CONVENTIONS.md.tmpl +0 -104
  821. package/plugins/copilot-pbr/templates/codebase/INTEGRATIONS.md.tmpl +0 -78
  822. package/plugins/copilot-pbr/templates/codebase/STACK.md.tmpl +0 -78
  823. package/plugins/copilot-pbr/templates/codebase/STRUCTURE.md.tmpl +0 -80
  824. package/plugins/copilot-pbr/templates/codebase/TESTING.md.tmpl +0 -107
  825. package/plugins/copilot-pbr/templates/continue-here.md.tmpl +0 -74
  826. package/plugins/copilot-pbr/templates/prompt-partials/phase-project-context.md.tmpl +0 -38
  827. package/plugins/copilot-pbr/templates/research/ARCHITECTURE.md.tmpl +0 -124
  828. package/plugins/copilot-pbr/templates/research/STACK.md.tmpl +0 -71
  829. package/plugins/copilot-pbr/templates/research/SUMMARY.md.tmpl +0 -112
  830. package/plugins/copilot-pbr/templates/research-outputs/phase-research.md.tmpl +0 -81
  831. package/plugins/copilot-pbr/templates/research-outputs/project-research.md.tmpl +0 -99
  832. package/plugins/copilot-pbr/templates/research-outputs/synthesis.md.tmpl +0 -36
  833. package/plugins/cursor-pbr/.cursor-plugin/plugin.json +0 -32
  834. package/plugins/cursor-pbr/CHANGELOG.md +0 -15
  835. package/plugins/cursor-pbr/README.md +0 -123
  836. package/plugins/cursor-pbr/agents/audit.md +0 -178
  837. package/plugins/cursor-pbr/agents/codebase-mapper.md +0 -150
  838. package/plugins/cursor-pbr/agents/debugger.md +0 -183
  839. package/plugins/cursor-pbr/agents/executor.md +0 -268
  840. package/plugins/cursor-pbr/agents/general.md +0 -88
  841. package/plugins/cursor-pbr/agents/integration-checker.md +0 -120
  842. package/plugins/cursor-pbr/agents/plan-checker.md +0 -207
  843. package/plugins/cursor-pbr/agents/planner.md +0 -239
  844. package/plugins/cursor-pbr/agents/researcher.md +0 -187
  845. package/plugins/cursor-pbr/agents/synthesizer.md +0 -125
  846. package/plugins/cursor-pbr/agents/verifier.md +0 -227
  847. package/plugins/cursor-pbr/assets/.gitkeep +0 -0
  848. package/plugins/cursor-pbr/assets/logo.svg +0 -21
  849. package/plugins/cursor-pbr/hooks/hooks.json +0 -224
  850. package/plugins/cursor-pbr/references/agent-anti-patterns.md +0 -25
  851. package/plugins/cursor-pbr/references/agent-contracts.md +0 -297
  852. package/plugins/cursor-pbr/references/agent-interactions.md +0 -135
  853. package/plugins/cursor-pbr/references/agent-teams.md +0 -55
  854. package/plugins/cursor-pbr/references/checkpoints.md +0 -158
  855. package/plugins/cursor-pbr/references/common-bug-patterns.md +0 -14
  856. package/plugins/cursor-pbr/references/config-reference.md +0 -442
  857. package/plugins/cursor-pbr/references/continuation-format.md +0 -213
  858. package/plugins/cursor-pbr/references/deviation-rules.md +0 -113
  859. package/plugins/cursor-pbr/references/git-integration.md +0 -227
  860. package/plugins/cursor-pbr/references/integration-patterns.md +0 -118
  861. package/plugins/cursor-pbr/references/model-profiles.md +0 -100
  862. package/plugins/cursor-pbr/references/model-selection.md +0 -32
  863. package/plugins/cursor-pbr/references/pbr-rules.md +0 -195
  864. package/plugins/cursor-pbr/references/pbr-tools-cli.md +0 -285
  865. package/plugins/cursor-pbr/references/plan-authoring.md +0 -182
  866. package/plugins/cursor-pbr/references/plan-format.md +0 -288
  867. package/plugins/cursor-pbr/references/planning-config.md +0 -214
  868. package/plugins/cursor-pbr/references/questioning.md +0 -215
  869. package/plugins/cursor-pbr/references/reading-verification.md +0 -128
  870. package/plugins/cursor-pbr/references/stub-patterns.md +0 -161
  871. package/plugins/cursor-pbr/references/subagent-coordination.md +0 -120
  872. package/plugins/cursor-pbr/references/ui-formatting.md +0 -444
  873. package/plugins/cursor-pbr/references/verification-patterns.md +0 -199
  874. package/plugins/cursor-pbr/references/wave-execution.md +0 -96
  875. package/plugins/cursor-pbr/rules/pbr-workflow.mdc +0 -48
  876. package/plugins/cursor-pbr/setup.ps1 +0 -78
  877. package/plugins/cursor-pbr/setup.sh +0 -83
  878. package/plugins/cursor-pbr/skills/audit/SKILL.md +0 -331
  879. package/plugins/cursor-pbr/skills/begin/SKILL.md +0 -589
  880. package/plugins/cursor-pbr/skills/begin/templates/PROJECT.md.tmpl +0 -34
  881. package/plugins/cursor-pbr/skills/begin/templates/REQUIREMENTS.md.tmpl +0 -19
  882. package/plugins/cursor-pbr/skills/begin/templates/STATE.md.tmpl +0 -50
  883. package/plugins/cursor-pbr/skills/begin/templates/config.json.tmpl +0 -64
  884. package/plugins/cursor-pbr/skills/begin/templates/researcher-prompt.md.tmpl +0 -20
  885. package/plugins/cursor-pbr/skills/begin/templates/roadmap-prompt.md.tmpl +0 -31
  886. package/plugins/cursor-pbr/skills/begin/templates/synthesis-prompt.md.tmpl +0 -17
  887. package/plugins/cursor-pbr/skills/build/SKILL.md +0 -960
  888. package/plugins/cursor-pbr/skills/config/SKILL.md +0 -252
  889. package/plugins/cursor-pbr/skills/continue/SKILL.md +0 -159
  890. package/plugins/cursor-pbr/skills/dashboard/SKILL.md +0 -44
  891. package/plugins/cursor-pbr/skills/debug/SKILL.md +0 -512
  892. package/plugins/cursor-pbr/skills/debug/templates/continuation-prompt.md.tmpl +0 -17
  893. package/plugins/cursor-pbr/skills/debug/templates/initial-investigation-prompt.md.tmpl +0 -28
  894. package/plugins/cursor-pbr/skills/discuss/SKILL.md +0 -354
  895. package/plugins/cursor-pbr/skills/discuss/templates/CONTEXT.md.tmpl +0 -62
  896. package/plugins/cursor-pbr/skills/discuss/templates/decision-categories.md +0 -10
  897. package/plugins/cursor-pbr/skills/do/SKILL.md +0 -67
  898. package/plugins/cursor-pbr/skills/explore/SKILL.md +0 -376
  899. package/plugins/cursor-pbr/skills/health/SKILL.md +0 -283
  900. package/plugins/cursor-pbr/skills/health/templates/check-pattern.md.tmpl +0 -31
  901. package/plugins/cursor-pbr/skills/health/templates/output-format.md.tmpl +0 -64
  902. package/plugins/cursor-pbr/skills/help/SKILL.md +0 -193
  903. package/plugins/cursor-pbr/skills/import/SKILL.md +0 -505
  904. package/plugins/cursor-pbr/skills/milestone/SKILL.md +0 -807
  905. package/plugins/cursor-pbr/skills/milestone/templates/audit-report.md.tmpl +0 -49
  906. package/plugins/cursor-pbr/skills/milestone/templates/stats-file.md.tmpl +0 -31
  907. package/plugins/cursor-pbr/skills/note/SKILL.md +0 -214
  908. package/plugins/cursor-pbr/skills/pause/SKILL.md +0 -248
  909. package/plugins/cursor-pbr/skills/pause/templates/continue-here.md.tmpl +0 -72
  910. package/plugins/cursor-pbr/skills/plan/SKILL.md +0 -663
  911. package/plugins/cursor-pbr/skills/plan/templates/checker-prompt.md.tmpl +0 -22
  912. package/plugins/cursor-pbr/skills/plan/templates/gap-closure-prompt.md.tmpl +0 -33
  913. package/plugins/cursor-pbr/skills/plan/templates/planner-prompt.md.tmpl +0 -39
  914. package/plugins/cursor-pbr/skills/plan/templates/researcher-prompt.md.tmpl +0 -20
  915. package/plugins/cursor-pbr/skills/plan/templates/revision-prompt.md.tmpl +0 -24
  916. package/plugins/cursor-pbr/skills/quick/SKILL.md +0 -376
  917. package/plugins/cursor-pbr/skills/resume/SKILL.md +0 -399
  918. package/plugins/cursor-pbr/skills/review/SKILL.md +0 -654
  919. package/plugins/cursor-pbr/skills/review/templates/debugger-prompt.md.tmpl +0 -61
  920. package/plugins/cursor-pbr/skills/review/templates/gap-planner-prompt.md.tmpl +0 -41
  921. package/plugins/cursor-pbr/skills/review/templates/verifier-prompt.md.tmpl +0 -116
  922. package/plugins/cursor-pbr/skills/scan/SKILL.md +0 -300
  923. package/plugins/cursor-pbr/skills/scan/templates/mapper-prompt.md.tmpl +0 -202
  924. package/plugins/cursor-pbr/skills/setup/SKILL.md +0 -296
  925. package/plugins/cursor-pbr/skills/shared/commit-planning-docs.md +0 -36
  926. package/plugins/cursor-pbr/skills/shared/config-loading.md +0 -103
  927. package/plugins/cursor-pbr/skills/shared/context-budget.md +0 -41
  928. package/plugins/cursor-pbr/skills/shared/context-loader-task.md +0 -87
  929. package/plugins/cursor-pbr/skills/shared/digest-select.md +0 -80
  930. package/plugins/cursor-pbr/skills/shared/domain-probes.md +0 -126
  931. package/plugins/cursor-pbr/skills/shared/error-recovery-strategies.md +0 -51
  932. package/plugins/cursor-pbr/skills/shared/error-reporting.md +0 -81
  933. package/plugins/cursor-pbr/skills/shared/gate-prompts.md +0 -389
  934. package/plugins/cursor-pbr/skills/shared/phase-argument-parsing.md +0 -46
  935. package/plugins/cursor-pbr/skills/shared/progress-display.md +0 -53
  936. package/plugins/cursor-pbr/skills/shared/revision-loop.md +0 -82
  937. package/plugins/cursor-pbr/skills/shared/state-loading.md +0 -63
  938. package/plugins/cursor-pbr/skills/shared/state-update.md +0 -170
  939. package/plugins/cursor-pbr/skills/shared/universal-anti-patterns.md +0 -38
  940. package/plugins/cursor-pbr/skills/status/SKILL.md +0 -362
  941. package/plugins/cursor-pbr/skills/statusline/SKILL.md +0 -150
  942. package/plugins/cursor-pbr/skills/todo/SKILL.md +0 -280
  943. package/plugins/cursor-pbr/templates/CONTEXT.md.tmpl +0 -53
  944. package/plugins/cursor-pbr/templates/INTEGRATION-REPORT.md.tmpl +0 -152
  945. package/plugins/cursor-pbr/templates/RESEARCH-SUMMARY.md.tmpl +0 -98
  946. package/plugins/cursor-pbr/templates/ROADMAP.md.tmpl +0 -48
  947. package/plugins/cursor-pbr/templates/SUMMARY.md.tmpl +0 -82
  948. package/plugins/cursor-pbr/templates/VERIFICATION-DETAIL.md.tmpl +0 -117
  949. package/plugins/cursor-pbr/templates/codebase/ARCHITECTURE.md.tmpl +0 -98
  950. package/plugins/cursor-pbr/templates/codebase/CONCERNS.md.tmpl +0 -93
  951. package/plugins/cursor-pbr/templates/codebase/CONVENTIONS.md.tmpl +0 -104
  952. package/plugins/cursor-pbr/templates/codebase/INTEGRATIONS.md.tmpl +0 -78
  953. package/plugins/cursor-pbr/templates/codebase/STACK.md.tmpl +0 -78
  954. package/plugins/cursor-pbr/templates/codebase/STRUCTURE.md.tmpl +0 -80
  955. package/plugins/cursor-pbr/templates/codebase/TESTING.md.tmpl +0 -107
  956. package/plugins/cursor-pbr/templates/continue-here.md.tmpl +0 -74
  957. package/plugins/cursor-pbr/templates/prompt-partials/phase-project-context.md.tmpl +0 -38
  958. package/plugins/cursor-pbr/templates/research/ARCHITECTURE.md.tmpl +0 -124
  959. package/plugins/cursor-pbr/templates/research/STACK.md.tmpl +0 -71
  960. package/plugins/cursor-pbr/templates/research/SUMMARY.md.tmpl +0 -112
  961. package/plugins/cursor-pbr/templates/research-outputs/phase-research.md.tmpl +0 -81
  962. package/plugins/cursor-pbr/templates/research-outputs/project-research.md.tmpl +0 -99
  963. package/plugins/cursor-pbr/templates/research-outputs/synthesis.md.tmpl +0 -36
  964. package/plugins/pbr/references/agent-interactions.md +0 -134
  965. package/plugins/pbr/references/pbr-rules.md +0 -194
  966. package/plugins/pbr/references/pbr-tools-cli.md +0 -285
  967. package/plugins/pbr/references/planning-config.md +0 -213
  968. package/plugins/pbr/references/subagent-coordination.md +0 -119
  969. package/plugins/pbr/references/ui-formatting.md +0 -444
  970. package/plugins/pbr/scripts/validate-plugin-structure.js +0 -183
  971. package/plugins/pbr/skills/milestone/templates/audit-report.md.tmpl +0 -48
  972. package/plugins/pbr/skills/shared/error-recovery-strategies.md +0 -51
  973. package/plugins/pbr/skills/shared/progress-display.md +0 -53
  974. package/plugins/pbr/skills/shared/state-loading.md +0 -62
  975. package/plugins/pbr/templates/RESEARCH-SUMMARY.md.tmpl +0 -97
  976. package/plugins/pbr/templates/research/ARCHITECTURE.md.tmpl +0 -124
  977. package/plugins/pbr/templates/research/STACK.md.tmpl +0 -71
  978. package/plugins/pbr/templates/research/SUMMARY.md.tmpl +0 -112
  979. package/plugins/pbr/templates/research-outputs/phase-research.md.tmpl +0 -81
  980. package/plugins/pbr/templates/research-outputs/project-research.md.tmpl +0 -99
  981. package/plugins/pbr/templates/research-outputs/synthesis.md.tmpl +0 -36
  982. /package/plugins/pbr/references/{agent-anti-patterns.md → archive/agent-anti-patterns.md} +0 -0
@@ -0,0 +1,1585 @@
1
+ /**
2
+ * lib/core.cjs — Foundation utilities for Plan-Build-Run tools.
3
+ *
4
+ * Pure utility functions with no dependencies on other lib modules.
5
+ * Provides: output/error formatting, YAML frontmatter parsing, status transitions,
6
+ * file operations (atomicWrite, lockedFileUpdate, findFiles, tailLines),
7
+ * session management, phase claiming, path utilities, and shared constants.
8
+ *
9
+ * Hybrid module merging PBR reference features with GSD-unique utilities.
10
+ */
11
+
12
+ const fs = require('fs');
13
+ const os = require('os');
14
+ const path = require('path');
15
+ const { execSync } = require('child_process');
16
+ const { logHook } = require('../hook-logger');
17
+ const { normalizeMsysPath } = require('./msys-path');
18
+
19
+ // ─── Module-level planningDir with MSYS path bridging ─────────────────────────
20
+
21
+ let cwd = process.env.PBR_PROJECT_ROOT || process.cwd();
22
+ cwd = normalizeMsysPath(cwd);
23
+
24
+ let planningDir = path.join(cwd, '.planning');
25
+
26
+ /**
27
+ * Override the working directory for subagent use.
28
+ * Updates both cwd and planningDir.
29
+ *
30
+ * @param {string} newCwd - New working directory path
31
+ */
32
+ function setCwd(newCwd) {
33
+ cwd = newCwd;
34
+ cwd = normalizeMsysPath(cwd);
35
+ planningDir = path.join(cwd, '.planning');
36
+ }
37
+
38
+ // ─── Canonical agent list ─────────────────────────────────────────────────────
39
+
40
+ /**
41
+ * Canonical list of known PBR agent types.
42
+ * Used by validate-task and check-subagent-output to avoid drift.
43
+ */
44
+ const KNOWN_AGENTS = [
45
+ 'executor',
46
+ 'planner',
47
+ 'verifier',
48
+ 'researcher',
49
+ 'synthesizer',
50
+ 'plan-checker',
51
+ 'integration-checker',
52
+ 'debugger',
53
+ 'codebase-mapper',
54
+ 'audit',
55
+ 'general',
56
+ 'dev-sync',
57
+ 'roadmapper',
58
+ 'nyquist-auditor',
59
+ 'intel-updater',
60
+ 'ui-checker',
61
+ 'ui-researcher'
62
+ ];
63
+
64
+ // ─── Phase status transition state machine ────────────────────────────────────
65
+
66
+ /**
67
+ * Valid phase status transitions. Each key is a current status, and its value
68
+ * is an array of statuses that are legal to transition to. This is advisory —
69
+ * invalid transitions produce a stderr warning but are not blocked.
70
+ *
71
+ * State machine:
72
+ * pending -> planned, skipped
73
+ * planned -> building
74
+ * building -> built, partial, needs_fixes
75
+ * built -> verified, needs_fixes
76
+ * partial -> building, needs_fixes
77
+ * verified -> building (re-execution)
78
+ * needs_fixes -> planned, building
79
+ * skipped -> pending (unskip)
80
+ */
81
+ const VALID_STATUS_TRANSITIONS = {
82
+ not_started: ['discussed', 'ready_to_plan', 'planned', 'skipped'],
83
+ discussed: ['ready_to_plan', 'planning'],
84
+ ready_to_plan: ['planning', 'planned'],
85
+ planning: ['planned'],
86
+ planned: ['ready_to_execute', 'building'],
87
+ ready_to_execute: ['building'],
88
+ building: ['built', 'partial', 'needs_fixes'],
89
+ built: ['verified', 'needs_fixes'],
90
+ partial: ['building', 'needs_fixes'],
91
+ verified: ['complete', 'building'],
92
+ needs_fixes: ['planned', 'building', 'ready_to_plan'],
93
+ complete: [],
94
+ skipped: ['not_started', 'pending'],
95
+ // Legacy aliases (backward compat)
96
+ pending: ['planned', 'discussed', 'skipped', 'not_started']
97
+ };
98
+
99
+ /**
100
+ * Human-readable labels for plan/phase statuses.
101
+ */
102
+ const STATUS_LABELS = {
103
+ not_started: 'Not Started',
104
+ discussed: 'Discussed',
105
+ ready_to_plan: 'Ready to Plan',
106
+ planning: 'Planning',
107
+ planned: 'Planned',
108
+ ready_to_execute: 'Ready to Execute',
109
+ building: 'Building',
110
+ built: 'Built',
111
+ partial: 'Partial',
112
+ verified: 'Verified',
113
+ needs_fixes: 'Needs Fixes',
114
+ complete: 'Complete',
115
+ skipped: 'Skipped',
116
+ // Legacy aliases
117
+ pending: 'Not Started',
118
+ reviewed: 'Verified'
119
+ };
120
+
121
+ /**
122
+ * Check whether a phase status transition is valid according to the state machine.
123
+ * Returns { valid, warning? } — never blocks, only advises.
124
+ *
125
+ * @param {string} oldStatus - Current phase status
126
+ * @param {string} newStatus - Desired phase status
127
+ * @returns {{ valid: boolean, warning?: string }}
128
+ */
129
+ function validateStatusTransition(oldStatus, newStatus) {
130
+ const from = (oldStatus || '').trim().toLowerCase();
131
+ const to = (newStatus || '').trim().toLowerCase();
132
+
133
+ if (from === to) return { valid: true };
134
+
135
+ if (!VALID_STATUS_TRANSITIONS[from]) return { valid: true };
136
+
137
+ const allowed = VALID_STATUS_TRANSITIONS[from];
138
+ if (allowed.includes(to)) return { valid: true };
139
+
140
+ return {
141
+ valid: false,
142
+ warning: `Suspicious status transition: "${from}" -> "${to}". Expected one of: [${allowed.join(', ')}]. Proceeding anyway (advisory).`
143
+ };
144
+ }
145
+
146
+ // ─── Model Profile Table ─────────────────────────────────────────────────────
147
+
148
+ const MODEL_PROFILES = {
149
+ 'pbr-planner': { quality: 'opus', balanced: 'opus', budget: 'sonnet' },
150
+ 'pbr-roadmapper': { quality: 'opus', balanced: 'sonnet', budget: 'sonnet' },
151
+ 'pbr-executor': { quality: 'opus', balanced: 'sonnet', budget: 'sonnet' },
152
+ 'pbr-researcher': { quality: 'opus', balanced: 'sonnet', budget: 'haiku' },
153
+ 'pbr-synthesizer': { quality: 'sonnet', balanced: 'sonnet', budget: 'haiku' },
154
+ 'pbr-debugger': { quality: 'opus', balanced: 'sonnet', budget: 'sonnet' },
155
+ 'pbr-codebase-mapper': { quality: 'sonnet', balanced: 'haiku', budget: 'haiku' },
156
+ 'pbr-verifier': { quality: 'sonnet', balanced: 'sonnet', budget: 'haiku' },
157
+ 'pbr-plan-checker': { quality: 'sonnet', balanced: 'sonnet', budget: 'haiku' },
158
+ 'pbr-integration-checker': { quality: 'sonnet', balanced: 'sonnet', budget: 'haiku' },
159
+ 'pbr-nyquist-auditor': { quality: 'sonnet', balanced: 'sonnet', budget: 'haiku' },
160
+ };
161
+
162
+ // ─── Path helpers ─────────────────────────────────────────────────────────────
163
+
164
+ /** Normalize a path to always use forward slashes (cross-platform). */
165
+ function toPosixPath(p) {
166
+ return p.split(path.sep).join('/');
167
+ }
168
+
169
+ // ─── Output helpers ───────────────────────────────────────────────────────────
170
+
171
+ function output(data, raw, rawValue) {
172
+ if (raw && rawValue !== undefined) {
173
+ process.stdout.write(String(rawValue));
174
+ } else {
175
+ const json = JSON.stringify(data, null, 2);
176
+ if (json.length > 8192) {
177
+ const tmpPath = path.join(os.tmpdir(), `pbr-${Date.now()}.json`);
178
+ fs.writeFileSync(tmpPath, json, 'utf8');
179
+ process.stdout.write('@file:' + tmpPath + '\n');
180
+ } else {
181
+ process.stdout.write(json + '\n');
182
+ }
183
+ }
184
+ process.exit(0);
185
+ }
186
+
187
+ function error(msg) {
188
+ process.stderr.write('Error: ' + msg + '\n');
189
+ process.exit(1);
190
+ }
191
+
192
+ // ─── File & path utilities ────────────────────────────────────────────────────
193
+
194
+ /**
195
+ * Read a file safely, returning null on any error.
196
+ *
197
+ * @param {string} filePath - Absolute path to the file
198
+ * @returns {string|null} File contents or null
199
+ */
200
+ function safeReadFile(filePath) {
201
+ try {
202
+ return fs.readFileSync(filePath, 'utf8');
203
+ } catch {
204
+ // intentionally silent: file may not exist
205
+ return null;
206
+ }
207
+ }
208
+
209
+ /**
210
+ * Ensure a directory exists, creating it recursively if needed.
211
+ *
212
+ * @param {string} dirPath - Directory path to ensure
213
+ */
214
+ function ensureDir(dirPath) {
215
+ fs.mkdirSync(dirPath, { recursive: true });
216
+ }
217
+
218
+ /**
219
+ * Find files in a directory matching a regex pattern.
220
+ *
221
+ * @param {string} dir - Directory to search
222
+ * @param {RegExp} pattern - Pattern to match filenames against
223
+ * @returns {string[]} Sorted array of matching filenames
224
+ */
225
+ function findFiles(dir, pattern) {
226
+ try {
227
+ return fs.readdirSync(dir).filter(f => pattern.test(f)).sort();
228
+ } catch (_) {
229
+ // intentionally silent: directory may not exist
230
+ return [];
231
+ }
232
+ }
233
+
234
+ /**
235
+ * Read the last N lines from a file.
236
+ *
237
+ * @param {string} filePath - Absolute path to the file
238
+ * @param {number} n - Number of trailing lines to return
239
+ * @returns {string[]} Array of line strings
240
+ */
241
+ function tailLines(filePath, n) {
242
+ try {
243
+ if (!fs.existsSync(filePath)) return [];
244
+ const content = fs.readFileSync(filePath, 'utf8').trim();
245
+ if (!content) return [];
246
+ const lines = content.replace(/\r\n/g, '\n').split('\n');
247
+ if (lines.length <= n) return lines;
248
+ return lines.slice(lines.length - n);
249
+ } catch (_e) {
250
+ // intentionally silent: file may not exist
251
+ return [];
252
+ }
253
+ }
254
+
255
+ // ─── Git utilities ────────────────────────────────────────────────────────────
256
+
257
+ /**
258
+ * Execute a git command and return the result.
259
+ *
260
+ * @param {string} gitCwd - Working directory for git
261
+ * @param {string[]} args - Git command arguments
262
+ * @returns {{ exitCode: number, stdout: string, stderr: string }}
263
+ */
264
+ function execGit(gitCwd, args) {
265
+ try {
266
+ const escaped = args.map(a => {
267
+ if (/^[a-zA-Z0-9._\-/=:@]+$/.test(a)) return a;
268
+ return "'" + a.replace(/'/g, "'\\''") + "'";
269
+ });
270
+ const stdout = execSync('git ' + escaped.join(' '), {
271
+ cwd: gitCwd,
272
+ stdio: 'pipe',
273
+ encoding: 'utf-8',
274
+ });
275
+ return { exitCode: 0, stdout: stdout.trim(), stderr: '' };
276
+ } catch (err) {
277
+ // intentionally silent: git command failures are normal control flow
278
+ return {
279
+ exitCode: err.status ?? 1,
280
+ stdout: (err.stdout ?? '').toString().trim(),
281
+ stderr: (err.stderr ?? '').toString().trim(),
282
+ };
283
+ }
284
+ }
285
+
286
+ /**
287
+ * Check if a path is git-ignored.
288
+ *
289
+ * @param {string} gitCwd - Working directory
290
+ * @param {string} targetPath - Path to check
291
+ * @returns {boolean}
292
+ */
293
+ function isGitIgnored(gitCwd, targetPath) {
294
+ try {
295
+ execSync('git check-ignore -q --no-index -- ' + targetPath.replace(/[^a-zA-Z0-9._\-/]/g, ''), {
296
+ cwd: gitCwd,
297
+ stdio: 'pipe',
298
+ });
299
+ return true;
300
+ } catch {
301
+ // intentionally silent: non-zero exit means not ignored
302
+ return false;
303
+ }
304
+ }
305
+
306
+ // ─── YAML frontmatter parsing ─────────────────────────────────────────────────
307
+
308
+ /**
309
+ * Parse YAML frontmatter from markdown content.
310
+ * Handles flat key-value pairs, inline arrays, and multi-line arrays.
311
+ *
312
+ * @param {string} content - Markdown content with optional frontmatter
313
+ * @returns {object} Parsed frontmatter as a plain object
314
+ */
315
+ function parseYamlFrontmatter(content) {
316
+ const normalized = content.replace(/\r\n/g, '\n');
317
+ const match = normalized.match(/^---\s*\n([\s\S]*?)\n---/);
318
+ if (!match) return {};
319
+
320
+ const yaml = match[1];
321
+ const result = {};
322
+
323
+ const lines = yaml.split('\n');
324
+ let currentKey = null;
325
+
326
+ for (const line of lines) {
327
+ // Array item
328
+ if (/^\s+-\s+/.test(line) && currentKey) {
329
+ const val = line.replace(/^\s+-\s+/, '').trim().replace(/^["']|["']$/g, '');
330
+ if (!result[currentKey]) result[currentKey] = [];
331
+ if (Array.isArray(result[currentKey])) {
332
+ result[currentKey].push(val);
333
+ }
334
+ continue;
335
+ }
336
+
337
+ // Key-value pair
338
+ const kvMatch = line.match(/^(\w[\w_]*)\s*:\s*(.*)/);
339
+ if (kvMatch) {
340
+ currentKey = kvMatch[1];
341
+ let val = kvMatch[2].trim();
342
+
343
+ if (val === '' || val === '|') continue;
344
+
345
+ // Handle arrays on same line: [a, b, c]
346
+ if (val.startsWith('[') && val.endsWith(']')) {
347
+ result[currentKey] = val.slice(1, -1).split(',')
348
+ .map(v => v.trim().replace(/^["']|["']$/g, ''))
349
+ .filter(Boolean);
350
+ continue;
351
+ }
352
+
353
+ // Clean quotes
354
+ val = val.replace(/^["']|["']$/g, '');
355
+
356
+ // Type coercion
357
+ if (val === 'true') val = true;
358
+ else if (val === 'false') val = false;
359
+ else if (/^\d+$/.test(val)) val = parseInt(val, 10);
360
+
361
+ result[currentKey] = val;
362
+ }
363
+ }
364
+
365
+ // Handle must_haves as a nested object
366
+ if (yaml.includes('must_haves:')) {
367
+ result.must_haves = parseMustHaves(yaml);
368
+ }
369
+
370
+ return result;
371
+ }
372
+
373
+ /**
374
+ * Parse the must_haves section from YAML frontmatter.
375
+ *
376
+ * @param {string} yaml - Raw YAML content (without --- delimiters)
377
+ * @returns {{ truths: string[], artifacts: string[], key_links: string[] }}
378
+ */
379
+ function parseMustHaves(yaml) {
380
+ const result = { truths: [], artifacts: [], key_links: [] };
381
+ let section = null;
382
+
383
+ const inMustHaves = yaml.replace(/\r\n/g, '\n').split('\n');
384
+ let collecting = false;
385
+
386
+ for (const line of inMustHaves) {
387
+ if (/^\s*must_haves:/.test(line)) {
388
+ collecting = true;
389
+ continue;
390
+ }
391
+ if (collecting) {
392
+ if (/^\s{2}truths:/.test(line)) { section = 'truths'; continue; }
393
+ if (/^\s{2}artifacts:/.test(line)) { section = 'artifacts'; continue; }
394
+ if (/^\s{2}key_links:/.test(line)) { section = 'key_links'; continue; }
395
+ if (/^\w/.test(line)) break;
396
+
397
+ if (section && /^\s+-\s+/.test(line)) {
398
+ result[section].push(line.replace(/^\s+-\s+/, '').trim().replace(/^["']|["']$/g, ''));
399
+ }
400
+ }
401
+ }
402
+
403
+ return result;
404
+ }
405
+
406
+ /**
407
+ * Set/update YAML frontmatter fields in markdown content.
408
+ * Creates frontmatter block if none exists.
409
+ *
410
+ * @param {string} content - Markdown content
411
+ * @param {object} updates - Key-value pairs to set in frontmatter
412
+ * @returns {string} Updated content
413
+ */
414
+ function setYamlFrontmatter(content, updates) {
415
+ const normalized = content.replace(/\r\n/g, '\n');
416
+ const match = normalized.match(/^---\s*\n([\s\S]*?)\n---/);
417
+
418
+ if (!match) {
419
+ // No existing frontmatter — create one
420
+ const lines = Object.entries(updates).map(([k, v]) => {
421
+ if (Array.isArray(v)) {
422
+ return `${k}:\n${v.map(item => ` - ${item}`).join('\n')}`;
423
+ }
424
+ if (typeof v === 'string' && (v.includes(':') || v.includes('#'))) {
425
+ return `${k}: "${v}"`;
426
+ }
427
+ return `${k}: ${v}`;
428
+ });
429
+ return `---\n${lines.join('\n')}\n---\n${normalized}`;
430
+ }
431
+
432
+ let yaml = match[1];
433
+
434
+ for (const [key, value] of Object.entries(updates)) {
435
+ const keyRegex = new RegExp(`^(${key})\\s*:.*$`, 'm');
436
+ const formatted = typeof value === 'string' && (value.includes(':') || value.includes('#'))
437
+ ? `"${value}"`
438
+ : String(value);
439
+
440
+ if (keyRegex.test(yaml)) {
441
+ yaml = yaml.replace(keyRegex, `${key}: ${formatted}`);
442
+ } else {
443
+ yaml += `\n${key}: ${formatted}`;
444
+ }
445
+ }
446
+
447
+ return normalized.replace(/^---\s*\n[\s\S]*?\n---/, `---\n${yaml}\n---`);
448
+ }
449
+
450
+ // ─── Misc utilities ───────────────────────────────────────────────────────────
451
+
452
+ function countMustHaves(mustHaves) {
453
+ if (!mustHaves) return 0;
454
+ return (mustHaves.truths || []).length +
455
+ (mustHaves.artifacts || []).length +
456
+ (mustHaves.key_links || []).length;
457
+ }
458
+
459
+ function determinePhaseStatus(planCount, completedCount, summaryCount, hasVerification, phaseDir) {
460
+ if (planCount === 0) {
461
+ if (fs.existsSync(path.join(phaseDir, 'CONTEXT.md'))) return 'discussed';
462
+ return 'not_started';
463
+ }
464
+ if (completedCount === 0 && summaryCount === 0) return 'planned';
465
+ if (completedCount < planCount) return 'building';
466
+ if (!hasVerification) return 'built';
467
+ try {
468
+ const vContent = fs.readFileSync(path.join(phaseDir, 'VERIFICATION.md'), 'utf8');
469
+ if (/status:\s*["']?passed/i.test(vContent)) return 'verified';
470
+ if (/status:\s*["']?gaps_found/i.test(vContent)) return 'needs_fixes';
471
+ return 'reviewed';
472
+ } catch (_) {
473
+ // intentionally silent: VERIFICATION.md may not exist
474
+ return 'built';
475
+ }
476
+ }
477
+
478
+ function calculateProgress(pDir) {
479
+ const phasesDir = path.join(pDir, 'phases');
480
+ if (!fs.existsSync(phasesDir)) {
481
+ return { total: 0, completed: 0, percentage: 0 };
482
+ }
483
+
484
+ let total = 0;
485
+ let completed = 0;
486
+
487
+ const entries = fs.readdirSync(phasesDir, { withFileTypes: true })
488
+ .filter(e => e.isDirectory());
489
+
490
+ for (const entry of entries) {
491
+ const dir = path.join(phasesDir, entry.name);
492
+ const plans = findFiles(dir, /PLAN.*\.md$/i);
493
+ total += plans.length;
494
+
495
+ const summaries = findFiles(dir, /^SUMMARY-.*\.md$/);
496
+ for (const s of summaries) {
497
+ const content = fs.readFileSync(path.join(dir, s), 'utf8');
498
+ if (/status:\s*["']?complete/i.test(content)) completed++;
499
+ }
500
+ }
501
+
502
+ return {
503
+ total,
504
+ completed,
505
+ percentage: total > 0 ? Math.round((completed / total) * 100) : 0
506
+ };
507
+ }
508
+
509
+ /**
510
+ * Return an ISO 8601 UTC timestamp string.
511
+ *
512
+ * @returns {string} ISO timestamp
513
+ */
514
+ function currentTimestamp() {
515
+ return new Date().toISOString();
516
+ }
517
+
518
+ /**
519
+ * Generate a URL-safe slug from a string.
520
+ *
521
+ * @param {string} text - Input text
522
+ * @returns {string|null} Slugified string or null
523
+ */
524
+ function generateSlug(text) {
525
+ if (!text) return null;
526
+ return text.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '');
527
+ }
528
+
529
+ /**
530
+ * Resolve the model name for an agent type based on config.
531
+ *
532
+ * @param {string} agentType - Agent type key (e.g., 'pbr-executor')
533
+ * @param {object} config - Config object with model_profile and optional model_overrides
534
+ * @returns {string} Resolved model name
535
+ */
536
+ function resolveModel(agentType, config) {
537
+ // Check per-agent override first
538
+ const override = config && config.model_overrides && config.model_overrides[agentType];
539
+ if (override) {
540
+ return override === 'opus' ? 'inherit' : override;
541
+ }
542
+
543
+ // Fall back to profile lookup
544
+ const profile = (config && config.model_profile) || 'balanced';
545
+ const agentModels = MODEL_PROFILES[agentType];
546
+ if (!agentModels) return 'sonnet';
547
+ const resolved = agentModels[profile] || agentModels['balanced'] || 'sonnet';
548
+ return resolved === 'opus' ? 'inherit' : resolved;
549
+ }
550
+
551
+ // ─── Regex and phase utilities ────────────────────────────────────────────────
552
+
553
+ function escapeRegex(value) {
554
+ return String(value).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
555
+ }
556
+
557
+ function normalizePhaseName(phase) {
558
+ const match = String(phase).match(/^(\d+)([A-Z])?((?:\.\d+)*)/i);
559
+ if (!match) return phase;
560
+ const padded = match[1].padStart(2, '0');
561
+ const letter = match[2] ? match[2].toUpperCase() : '';
562
+ const decimal = match[3] || '';
563
+ return padded + letter + decimal;
564
+ }
565
+
566
+ function comparePhaseNum(a, b) {
567
+ const pa = String(a).match(/^(\d+)([A-Z])?((?:\.\d+)*)/i);
568
+ const pb = String(b).match(/^(\d+)([A-Z])?((?:\.\d+)*)/i);
569
+ if (!pa || !pb) return String(a).localeCompare(String(b));
570
+ const intDiff = parseInt(pa[1], 10) - parseInt(pb[1], 10);
571
+ if (intDiff !== 0) return intDiff;
572
+ const la = (pa[2] || '').toUpperCase();
573
+ const lb = (pb[2] || '').toUpperCase();
574
+ if (la !== lb) {
575
+ if (!la) return -1;
576
+ if (!lb) return 1;
577
+ return la < lb ? -1 : 1;
578
+ }
579
+ const aDecParts = pa[3] ? pa[3].slice(1).split('.').map(p => parseInt(p, 10)) : [];
580
+ const bDecParts = pb[3] ? pb[3].slice(1).split('.').map(p => parseInt(p, 10)) : [];
581
+ const maxLen = Math.max(aDecParts.length, bDecParts.length);
582
+ if (aDecParts.length === 0 && bDecParts.length > 0) return -1;
583
+ if (bDecParts.length === 0 && aDecParts.length > 0) return 1;
584
+ for (let i = 0; i < maxLen; i++) {
585
+ const av = Number.isFinite(aDecParts[i]) ? aDecParts[i] : 0;
586
+ const bv = Number.isFinite(bDecParts[i]) ? bDecParts[i] : 0;
587
+ if (av !== bv) return av - bv;
588
+ }
589
+ return 0;
590
+ }
591
+
592
+ function searchPhaseInDir(baseDir, relBase, normalized) {
593
+ try {
594
+ const entries = fs.readdirSync(baseDir, { withFileTypes: true });
595
+ const dirs = entries.filter(e => e.isDirectory()).map(e => e.name).sort((a, b) => comparePhaseNum(a, b));
596
+ const match = dirs.find(d => d.startsWith(normalized));
597
+ if (!match) return null;
598
+
599
+ const dirMatch = match.match(/^(\d+[A-Z]?(?:\.\d+)*)-?(.*)/i);
600
+ const phaseNumber = dirMatch ? dirMatch[1] : normalized;
601
+ const phaseName = dirMatch && dirMatch[2] ? dirMatch[2] : null;
602
+ const phaseDir = path.join(baseDir, match);
603
+ const phaseFiles = fs.readdirSync(phaseDir);
604
+
605
+ const plans = phaseFiles.filter(f => f.endsWith('-PLAN.md') || f === 'PLAN.md').sort();
606
+ const summaries = phaseFiles.filter(f => f.endsWith('-SUMMARY.md') || f === 'SUMMARY.md').sort();
607
+ const hasResearch = phaseFiles.some(f => f.endsWith('-RESEARCH.md') || f === 'RESEARCH.md');
608
+ const hasContext = phaseFiles.some(f => f.endsWith('-CONTEXT.md') || f === 'CONTEXT.md');
609
+ const hasVerification = phaseFiles.some(f => f.endsWith('-VERIFICATION.md') || f === 'VERIFICATION.md');
610
+
611
+ const completedPlanIds = new Set(
612
+ summaries.map(s => s.replace('-SUMMARY.md', '').replace('SUMMARY.md', ''))
613
+ );
614
+ const incompletePlans = plans.filter(p => {
615
+ const planId = p.replace('-PLAN.md', '').replace('PLAN.md', '');
616
+ return !completedPlanIds.has(planId);
617
+ });
618
+
619
+ return {
620
+ found: true,
621
+ directory: toPosixPath(path.join(relBase, match)),
622
+ phase_number: phaseNumber,
623
+ phase_name: phaseName,
624
+ phase_slug: phaseName ? phaseName.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '') : null,
625
+ plans,
626
+ summaries,
627
+ incomplete_plans: incompletePlans,
628
+ has_research: hasResearch,
629
+ has_context: hasContext,
630
+ has_verification: hasVerification,
631
+ };
632
+ } catch (e) {
633
+ logHook('core', 'debug', 'Failed to search phase in directory', { error: e.message });
634
+ return null;
635
+ }
636
+ }
637
+
638
+ function findPhaseInternal(phaseCwd, phase) {
639
+ if (!phase) return null;
640
+
641
+ const phasesDir = path.join(phaseCwd, '.planning', 'phases');
642
+ const normalized = normalizePhaseName(phase);
643
+
644
+ const current = searchPhaseInDir(phasesDir, '.planning/phases', normalized);
645
+ if (current) return current;
646
+
647
+ const milestonesDir = path.join(phaseCwd, '.planning', 'milestones');
648
+ if (!fs.existsSync(milestonesDir)) return null;
649
+
650
+ try {
651
+ const milestoneEntries = fs.readdirSync(milestonesDir, { withFileTypes: true });
652
+ const archiveDirs = milestoneEntries
653
+ .filter(e => e.isDirectory() && /^v[\d.]+-phases$/.test(e.name))
654
+ .map(e => e.name)
655
+ .sort()
656
+ .reverse();
657
+
658
+ for (const archiveName of archiveDirs) {
659
+ const version = archiveName.match(/^(v[\d.]+)-phases$/)[1];
660
+ const archivePath = path.join(milestonesDir, archiveName);
661
+ const relBase = '.planning/milestones/' + archiveName;
662
+ const result = searchPhaseInDir(archivePath, relBase, normalized);
663
+ if (result) {
664
+ result.archived = version;
665
+ return result;
666
+ }
667
+ }
668
+ } catch (e) { logHook('core', 'debug', 'Failed to search milestone archives', { error: e.message }); }
669
+
670
+ return null;
671
+ }
672
+
673
+ function getArchivedPhaseDirs(archCwd) {
674
+ const milestonesDir = path.join(archCwd, '.planning', 'milestones');
675
+ const results = [];
676
+
677
+ if (!fs.existsSync(milestonesDir)) return results;
678
+
679
+ try {
680
+ const milestoneEntries = fs.readdirSync(milestonesDir, { withFileTypes: true });
681
+ const phaseDirs = milestoneEntries
682
+ .filter(e => e.isDirectory() && /^v[\d.]+-phases$/.test(e.name))
683
+ .map(e => e.name)
684
+ .sort()
685
+ .reverse();
686
+
687
+ for (const archiveName of phaseDirs) {
688
+ const version = archiveName.match(/^(v[\d.]+)-phases$/)[1];
689
+ const archivePath = path.join(milestonesDir, archiveName);
690
+ const entries = fs.readdirSync(archivePath, { withFileTypes: true });
691
+ const dirs = entries.filter(e => e.isDirectory()).map(e => e.name).sort((a, b) => comparePhaseNum(a, b));
692
+
693
+ for (const dir of dirs) {
694
+ results.push({
695
+ name: dir,
696
+ milestone: version,
697
+ basePath: path.join('.planning', 'milestones', archiveName),
698
+ fullPath: path.join(archivePath, dir),
699
+ });
700
+ }
701
+ }
702
+ } catch (e) { logHook('core', 'debug', 'Failed to list archived phase dirs', { error: e.message }); }
703
+
704
+ return results;
705
+ }
706
+
707
+ // ─── Roadmap & milestone utilities ────────────────────────────────────────────
708
+
709
+ function getRoadmapPhaseInternal(rmCwd, phaseNum) {
710
+ if (!phaseNum) return null;
711
+ const roadmapPath = path.join(rmCwd, '.planning', 'ROADMAP.md');
712
+ if (!fs.existsSync(roadmapPath)) return null;
713
+
714
+ try {
715
+ const content = fs.readFileSync(roadmapPath, 'utf8');
716
+ const escapedPhase = escapeRegex(phaseNum.toString());
717
+ const phasePattern = new RegExp(`#{2,4}\\s*Phase\\s+${escapedPhase}:\\s*([^\\n]+)`, 'i');
718
+ const headerMatch = content.match(phasePattern);
719
+ if (!headerMatch) return null;
720
+
721
+ const phaseName = headerMatch[1].trim();
722
+ const headerIndex = headerMatch.index;
723
+ const restOfContent = content.slice(headerIndex);
724
+ const nextHeaderMatch = restOfContent.match(/\n#{2,4}\s+Phase\s+\d/i);
725
+ const sectionEnd = nextHeaderMatch ? headerIndex + nextHeaderMatch.index : content.length;
726
+ const section = content.slice(headerIndex, sectionEnd).trim();
727
+
728
+ const goalMatch = section.match(/\*\*Goal:\*\*\s*([^\n]+)/i);
729
+ const goal = goalMatch ? goalMatch[1].trim() : null;
730
+
731
+ return {
732
+ found: true,
733
+ phase_number: phaseNum.toString(),
734
+ phase_name: phaseName,
735
+ goal,
736
+ section,
737
+ };
738
+ } catch (e) {
739
+ logHook('core', 'debug', 'Failed to read roadmap for phase lookup', { error: e.message });
740
+ return null;
741
+ }
742
+ }
743
+
744
+ function getMilestoneInfo(miCwd) {
745
+ try {
746
+ const roadmap = fs.readFileSync(path.join(miCwd, '.planning', 'ROADMAP.md'), 'utf8');
747
+
748
+ const inProgressMatch = roadmap.match(/\u{1F6A7}\s*\*\*v(\d+\.\d+)\s+([^*]+)\*\*/u);
749
+ if (inProgressMatch) {
750
+ return {
751
+ version: 'v' + inProgressMatch[1],
752
+ name: inProgressMatch[2].trim(),
753
+ };
754
+ }
755
+
756
+ const cleaned = roadmap.replace(/<details>[\s\S]*?<\/details>/gi, '');
757
+ const headingMatch = cleaned.match(/## .*v(\d+\.\d+)[:\s]+([^\n(]+)/);
758
+ if (headingMatch) {
759
+ return {
760
+ version: 'v' + headingMatch[1],
761
+ name: headingMatch[2].trim(),
762
+ };
763
+ }
764
+ const versionMatch = cleaned.match(/v(\d+\.\d+)/);
765
+ return {
766
+ version: versionMatch ? versionMatch[0] : 'v1.0',
767
+ name: 'milestone',
768
+ };
769
+ } catch (e) {
770
+ logHook('core', 'debug', 'Failed to read milestone info', { error: e.message });
771
+ return { version: 'v1.0', name: 'milestone' };
772
+ }
773
+ }
774
+
775
+ /**
776
+ * Returns a filter function that checks whether a phase directory belongs
777
+ * to the current milestone based on ROADMAP.md phase headings.
778
+ */
779
+ function getMilestonePhaseFilter(filterCwd) {
780
+ const milestonePhaseNums = new Set();
781
+ try {
782
+ const roadmap = fs.readFileSync(path.join(filterCwd, '.planning', 'ROADMAP.md'), 'utf8');
783
+ const phasePattern = /#{2,4}\s*Phase\s+(\d+[A-Z]?(?:\.\d+)*)\s*:/gi;
784
+ let m;
785
+ while ((m = phasePattern.exec(roadmap)) !== null) {
786
+ milestonePhaseNums.add(m[1]);
787
+ }
788
+ } catch (e) { logHook('core', 'debug', 'Failed to read roadmap for milestone filter', { error: e.message }); }
789
+
790
+ if (milestonePhaseNums.size === 0) {
791
+ const passAll = () => true;
792
+ passAll.phaseCount = 0;
793
+ return passAll;
794
+ }
795
+
796
+ const normalized = new Set(
797
+ [...milestonePhaseNums].map(n => (n.replace(/^0+/, '') || '0').toLowerCase())
798
+ );
799
+
800
+ function isDirInMilestone(dirName) {
801
+ const dm = dirName.match(/^0*(\d+[A-Za-z]?(?:\.\d+)*)/);
802
+ if (!dm) return false;
803
+ return normalized.has(dm[1].toLowerCase());
804
+ }
805
+ isDirInMilestone.phaseCount = milestonePhaseNums.size;
806
+ return isDirInMilestone;
807
+ }
808
+
809
+ // ─── Atomic file operations ───────────────────────────────────────────────────
810
+
811
+ /**
812
+ * Write content to a file atomically: write to .tmp, backup original to .bak,
813
+ * rename .tmp over original. On failure, restore from .bak if available.
814
+ *
815
+ * @param {string} filePath - Target file path
816
+ * @param {string} content - Content to write
817
+ * @returns {{success: boolean, error?: string}} Result
818
+ */
819
+ function atomicWrite(filePath, content) {
820
+ const tmpPath = filePath + '.tmp';
821
+ const bakPath = filePath + '.bak';
822
+
823
+ try {
824
+ fs.writeFileSync(tmpPath, content, 'utf8');
825
+
826
+ if (fs.existsSync(filePath)) {
827
+ try { fs.copyFileSync(filePath, bakPath); } catch (_e) { /* intentionally silent: backup is non-fatal */ }
828
+ }
829
+
830
+ fs.renameSync(tmpPath, filePath);
831
+
832
+ try {
833
+ if (fs.existsSync(bakPath)) fs.unlinkSync(bakPath);
834
+ } catch (_e) { /* intentionally silent: non-fatal */ }
835
+
836
+ return { success: true };
837
+ } catch (e) {
838
+ try {
839
+ if (fs.existsSync(bakPath)) fs.copyFileSync(bakPath, filePath);
840
+ } catch (_restoreErr) { /* intentionally silent: restore is last resort */ }
841
+ try {
842
+ if (fs.existsSync(tmpPath)) fs.unlinkSync(tmpPath);
843
+ } catch (_cleanupErr) { /* intentionally silent: tmp cleanup is non-fatal */ }
844
+
845
+ return { success: false, error: e.message };
846
+ }
847
+ }
848
+
849
+ /**
850
+ * Locked file update: read-modify-write with exclusive lockfile.
851
+ * Prevents concurrent writes to STATE.md and ROADMAP.md.
852
+ *
853
+ * @param {string} filePath - Absolute path to the file to update
854
+ * @param {function} updateFn - Receives current content, returns new content
855
+ * @param {object} opts - Options: { retries: 3, retryDelayMs: 100, timeoutMs: 5000 }
856
+ * @returns {object} { success, content?, error? }
857
+ */
858
+ async function lockedFileUpdate(filePath, updateFn, opts = {}) {
859
+ const retries = opts.retries || 10;
860
+ const retryDelayMs = opts.retryDelayMs || 50;
861
+ const timeoutMs = opts.timeoutMs || 10000;
862
+ const lockPath = filePath + '.lock';
863
+
864
+ // Async sleep helper — does NOT block the event loop
865
+ const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
866
+
867
+ let lockFd = null;
868
+ let lockAcquired = false;
869
+
870
+ try {
871
+ for (let attempt = 0; attempt < retries; attempt++) {
872
+ try {
873
+ lockFd = await fs.promises.open(lockPath, 'wx');
874
+ lockAcquired = true;
875
+ break;
876
+ } catch (e) { // intentionally silent: lock contention is expected
877
+ if (e.code === 'EEXIST') {
878
+ // Check for stale lock
879
+ try {
880
+ const stats = await fs.promises.stat(lockPath);
881
+ if (Date.now() - stats.mtimeMs > timeoutMs) {
882
+ try { await fs.promises.unlink(lockPath); } catch (_unlinkErr) { /* best effort */ }
883
+ continue;
884
+ }
885
+ } catch (_statErr) { // intentionally silent: lock stat failed
886
+ continue;
887
+ }
888
+
889
+ if (attempt < retries - 1) {
890
+ const baseWait = retryDelayMs * Math.pow(2, attempt);
891
+ const jitter = Math.floor(Math.random() * retryDelayMs);
892
+ const waitMs = Math.min(baseWait + jitter, 2000);
893
+ await sleep(waitMs);
894
+ continue;
895
+ }
896
+ // Last retry exhausted — break to fall through to last-resort write
897
+ break;
898
+ }
899
+ throw e;
900
+ }
901
+ }
902
+
903
+ if (!lockAcquired) {
904
+ process.stderr.write(`[pbr] WARN: lock contention on ${path.basename(filePath)} after ${retries} attempts — writing without lock\n`);
905
+ // Fall through to read-modify-write below (last-resort write)
906
+ }
907
+
908
+ if (lockAcquired) {
909
+ await lockFd.write(`${process.pid}`);
910
+ await lockFd.close();
911
+ lockFd = null;
912
+ }
913
+
914
+ let content = '';
915
+ if (fs.existsSync(filePath)) {
916
+ content = fs.readFileSync(filePath, 'utf8');
917
+ }
918
+
919
+ const newContent = updateFn(content);
920
+
921
+ const writeResult = atomicWrite(filePath, newContent);
922
+ if (!writeResult.success) {
923
+ return { success: false, error: writeResult.error };
924
+ }
925
+
926
+ return { success: true, content: newContent };
927
+ } catch (e) {
928
+ logHook('core', 'debug', 'lockedFileUpdate failed', { error: e.message });
929
+ return { success: false, error: e.message };
930
+ } finally {
931
+ try {
932
+ if (lockFd !== null) {
933
+ try { await lockFd.close(); } catch (_e) { /* intentionally silent */ }
934
+ }
935
+ } catch (_e) { /* intentionally silent: fd close in finally */ }
936
+ if (lockAcquired) {
937
+ try { await fs.promises.unlink(lockPath); } catch (_e) { /* intentionally silent: lock cleanup in finally block */ }
938
+ }
939
+ }
940
+ }
941
+
942
+ // ─── Lightweight JSON Schema validator ────────────────────────────────────────
943
+
944
+ /**
945
+ * Validate an object against a simple JSON Schema subset.
946
+ * Supports type, enum, properties, additionalProperties, minimum, maximum.
947
+ *
948
+ * @param {*} value - Value to validate
949
+ * @param {object} schema - JSON Schema subset
950
+ * @param {string} prefix - Path prefix for error messages
951
+ * @param {string[]} errors - Array to push errors to
952
+ * @param {string[]} warnings - Array to push warnings to
953
+ */
954
+ function validateObject(value, schema, prefix, errors, warnings) {
955
+ if (schema.type) {
956
+ const types = Array.isArray(schema.type) ? schema.type : [schema.type];
957
+ const actualType = typeof value;
958
+ const typeMatch = types.some(t => {
959
+ if (t === 'integer') return actualType === 'number' && Number.isInteger(value);
960
+ return actualType === t;
961
+ });
962
+ if (!typeMatch) {
963
+ errors.push(`${prefix || 'root'}: expected ${types.join('|')}, got ${actualType}`);
964
+ return;
965
+ }
966
+ }
967
+
968
+ if (schema.enum && !schema.enum.includes(value)) {
969
+ errors.push(`${prefix || 'root'}: value "${value}" not in allowed values [${schema.enum.join(', ')}]`);
970
+ return;
971
+ }
972
+
973
+ if (schema.minimum !== undefined && value < schema.minimum) {
974
+ errors.push(`${prefix || 'root'}: value ${value} is below minimum ${schema.minimum}`);
975
+ }
976
+ if (schema.maximum !== undefined && value > schema.maximum) {
977
+ errors.push(`${prefix || 'root'}: value ${value} is above maximum ${schema.maximum}`);
978
+ }
979
+
980
+ if (schema.type === 'object' && schema.properties) {
981
+ const knownKeys = new Set(Object.keys(schema.properties));
982
+ for (const key of Object.keys(value)) {
983
+ const fullKey = prefix ? `${prefix}.${key}` : key;
984
+ if (!knownKeys.has(key)) {
985
+ if (schema.additionalProperties === false) {
986
+ warnings.push(`${fullKey}: unrecognized key (possible typo?)`);
987
+ }
988
+ continue;
989
+ }
990
+ validateObject(value[key], schema.properties[key], fullKey, errors, warnings);
991
+ }
992
+ }
993
+ }
994
+
995
+ // ─── Session-scoped path resolution ───────────────────────────────────────────
996
+
997
+ const STALE_SESSION_MS = 4 * 60 * 60 * 1000; // 4 hours
998
+
999
+ /**
1000
+ * Resolve a session-scoped file path.
1001
+ *
1002
+ * @param {string} pDir - Path to .planning/ directory
1003
+ * @param {string} filename - Filename to resolve
1004
+ * @param {string} sessionId - Session identifier
1005
+ * @returns {string} Resolved path
1006
+ */
1007
+ function resolveSessionPath(pDir, filename, sessionId) {
1008
+ return path.join(pDir, '.sessions', sessionId, filename);
1009
+ }
1010
+
1011
+ /**
1012
+ * Ensure session directory exists and write meta.json.
1013
+ *
1014
+ * @param {string} pDir - Path to .planning/ directory
1015
+ * @param {string} sessionId - Session identifier
1016
+ */
1017
+ function ensureSessionDir(pDir, sessionId) {
1018
+ const dirPath = path.join(pDir, '.sessions', sessionId);
1019
+ fs.mkdirSync(dirPath, { recursive: true });
1020
+ const metaPath = path.join(dirPath, 'meta.json');
1021
+ if (!fs.existsSync(metaPath)) {
1022
+ fs.writeFileSync(metaPath, JSON.stringify({
1023
+ session_id: sessionId,
1024
+ created: new Date().toISOString(),
1025
+ pid: process.pid
1026
+ }, null, 2), 'utf8');
1027
+ }
1028
+ }
1029
+
1030
+ /**
1031
+ * Remove a session directory and all its contents.
1032
+ *
1033
+ * @param {string} pDir - Path to .planning/ directory
1034
+ * @param {string} sessionId - Session identifier
1035
+ */
1036
+ function removeSessionDir(pDir, sessionId) {
1037
+ const dirPath = path.join(pDir, '.sessions', sessionId);
1038
+ if (fs.existsSync(dirPath)) {
1039
+ fs.rmSync(dirPath, { recursive: true, force: true });
1040
+ }
1041
+ }
1042
+
1043
+ /**
1044
+ * Remove stale session directories older than STALE_SESSION_MS.
1045
+ *
1046
+ * @param {string} pDir - Path to .planning/ directory
1047
+ * @returns {Array<{sessionId: string, age: number}>} Removed sessions
1048
+ */
1049
+ function cleanStaleSessions(pDir) {
1050
+ const sessionsDir = path.join(pDir, '.sessions');
1051
+ if (!fs.existsSync(sessionsDir)) return [];
1052
+
1053
+ const removed = [];
1054
+ try {
1055
+ const entries = fs.readdirSync(sessionsDir, { withFileTypes: true });
1056
+ for (const entry of entries) {
1057
+ if (!entry.isDirectory()) continue;
1058
+ const dirPath = path.join(sessionsDir, entry.name);
1059
+ let ageMs = 0;
1060
+
1061
+ const metaPath = path.join(dirPath, 'meta.json');
1062
+ try {
1063
+ const meta = JSON.parse(fs.readFileSync(metaPath, 'utf8'));
1064
+ ageMs = Date.now() - new Date(meta.created).getTime();
1065
+ } catch (_e) {
1066
+ // intentionally silent: meta.json may not exist or be malformed
1067
+ try {
1068
+ const stats = fs.statSync(dirPath);
1069
+ ageMs = Date.now() - stats.mtimeMs;
1070
+ } catch (_statErr) {
1071
+ // intentionally silent: stat failure means skip this session
1072
+ continue;
1073
+ }
1074
+ }
1075
+
1076
+ if (ageMs > STALE_SESSION_MS) {
1077
+ fs.rmSync(dirPath, { recursive: true, force: true });
1078
+ removed.push({ sessionId: entry.name, age: ageMs });
1079
+ }
1080
+ }
1081
+ } catch (_e) { logHook('core', 'debug', 'Failed during stale session cleanup'); }
1082
+
1083
+ return removed;
1084
+ }
1085
+
1086
+ // ─── Session state management ─────────────────────────────────────────────────
1087
+
1088
+ const SESSION_ALLOWED_KEYS = ['activeSkill', 'compactCounter', 'sessionStart', 'activeOperation', 'activePlan'];
1089
+
1090
+ /**
1091
+ * Load .session.json from .planning/ directory.
1092
+ *
1093
+ * @param {string} dir - Path to .planning/ directory
1094
+ * @param {string} [sessionId] - Session identifier for session-scoped path
1095
+ * @returns {object} Parsed session data or empty object
1096
+ */
1097
+ function sessionLoad(dir, sessionId) {
1098
+ const sessionPath = sessionId
1099
+ ? resolveSessionPath(dir, '.session.json', sessionId)
1100
+ : path.join(dir, '.session.json');
1101
+ try {
1102
+ if (!fs.existsSync(sessionPath)) return {};
1103
+ const content = fs.readFileSync(sessionPath, 'utf8');
1104
+ return JSON.parse(content);
1105
+ } catch (_e) {
1106
+ // intentionally silent: session file may not exist
1107
+ return {};
1108
+ }
1109
+ }
1110
+
1111
+ /**
1112
+ * Save data to .session.json using atomic write.
1113
+ * Merges provided data with existing session data.
1114
+ *
1115
+ * @param {string} dir - Path to .planning/ directory
1116
+ * @param {object} data - Key-value pairs to merge into session
1117
+ * @param {string} [sessionId] - Session identifier for session-scoped path
1118
+ * @returns {{ success: boolean, error?: string }}
1119
+ */
1120
+ function sessionSave(dir, data, sessionId) {
1121
+ const sessionPath = sessionId
1122
+ ? resolveSessionPath(dir, '.session.json', sessionId)
1123
+ : path.join(dir, '.session.json');
1124
+ const tmpPath = sessionPath + '.tmp';
1125
+ try {
1126
+ if (sessionId) ensureSessionDir(dir, sessionId);
1127
+ const existing = sessionLoad(dir, sessionId);
1128
+ const merged = Object.assign(existing, data);
1129
+ fs.writeFileSync(tmpPath, JSON.stringify(merged, null, 2), 'utf8');
1130
+ fs.renameSync(tmpPath, sessionPath);
1131
+ return { success: true };
1132
+ } catch (e) {
1133
+ try { if (fs.existsSync(tmpPath)) fs.unlinkSync(tmpPath); } catch (_) { /* intentionally silent: tmp cleanup is non-fatal */ }
1134
+ return { success: false, error: e.message };
1135
+ }
1136
+ }
1137
+
1138
+ /**
1139
+ * Clear session data by removing the .session.json file.
1140
+ *
1141
+ * @param {string} dir - Path to .planning/ directory
1142
+ * @param {string} [sessionId] - Session identifier for session-scoped path
1143
+ * @returns {{ success: boolean, error?: string }}
1144
+ */
1145
+ function sessionClear(dir, sessionId) {
1146
+ const sessionPath = sessionId
1147
+ ? resolveSessionPath(dir, '.session.json', sessionId)
1148
+ : path.join(dir, '.session.json');
1149
+ try {
1150
+ if (fs.existsSync(sessionPath)) fs.unlinkSync(sessionPath);
1151
+ return { success: true };
1152
+ } catch (e) {
1153
+ logHook('core', 'debug', 'Failed to clear session', { error: e.message });
1154
+ return { success: false, error: e.message };
1155
+ }
1156
+ }
1157
+
1158
+ /**
1159
+ * Dump all session data as a JSON object for debugging.
1160
+ *
1161
+ * @param {string} dir - Path to .planning/ directory
1162
+ * @param {string} [sessionId] - Session identifier for session-scoped path
1163
+ * @returns {object} Session data including metadata
1164
+ */
1165
+ function sessionDump(dir, sessionId) {
1166
+ const data = sessionLoad(dir, sessionId);
1167
+ const sessionPath = sessionId
1168
+ ? resolveSessionPath(dir, '.session.json', sessionId)
1169
+ : path.join(dir, '.session.json');
1170
+ return {
1171
+ path: sessionPath,
1172
+ exists: fs.existsSync(sessionPath),
1173
+ data,
1174
+ keys: Object.keys(data)
1175
+ };
1176
+ }
1177
+
1178
+ /**
1179
+ * Write .active-skill with OS-level mutual exclusion.
1180
+ *
1181
+ * @param {string} pDir - Path to .planning/ directory
1182
+ * @param {string} skillName - Skill name to write
1183
+ * @param {string} [sessionId] - Session identifier for session-scoped path
1184
+ * @returns {{success: boolean, warning?: string}} Result
1185
+ */
1186
+ function writeActiveSkill(pDir, skillName, sessionId) {
1187
+ const skillFile = sessionId
1188
+ ? resolveSessionPath(pDir, '.active-skill', sessionId)
1189
+ : path.join(pDir, '.active-skill');
1190
+ const lockFile = skillFile + '.lock';
1191
+ const staleThresholdMs = 60 * 60 * 1000;
1192
+
1193
+ if (sessionId) ensureSessionDir(pDir, sessionId);
1194
+
1195
+ let lockFd = null;
1196
+ try {
1197
+ lockFd = fs.openSync(lockFile, 'wx');
1198
+ fs.writeSync(lockFd, `${process.pid}`);
1199
+ fs.closeSync(lockFd);
1200
+ lockFd = null;
1201
+
1202
+ let warning = null;
1203
+ if (fs.existsSync(skillFile)) {
1204
+ try {
1205
+ const stats = fs.statSync(skillFile);
1206
+ const ageMs = Date.now() - stats.mtimeMs;
1207
+ if (ageMs < staleThresholdMs) {
1208
+ const existing = fs.readFileSync(skillFile, 'utf8').trim();
1209
+ warning = `.active-skill already set to "${existing}" (${Math.round(ageMs / 60000)}min ago). Overwriting.`;
1210
+ }
1211
+ } catch (_e) { /* intentionally silent: file may have been deleted concurrently */ }
1212
+ }
1213
+
1214
+ fs.writeFileSync(skillFile, skillName, 'utf8');
1215
+ try { sessionSave(pDir, { activeSkill: skillName }, sessionId); } catch (_e) { /* intentionally silent: session save is non-fatal */ }
1216
+ try { fs.unlinkSync(lockFile); } catch (_e) { /* intentionally silent: lock cleanup is non-fatal */ }
1217
+
1218
+ return { success: true, warning };
1219
+ } catch (e) {
1220
+ try { if (lockFd !== null) fs.closeSync(lockFd); } catch (_e) { /* intentionally silent: fd close on error path */ }
1221
+
1222
+ if (e.code === 'EEXIST') {
1223
+ try {
1224
+ const lockStats = fs.statSync(lockFile);
1225
+ const lockAgeMs = Date.now() - lockStats.mtimeMs;
1226
+ if (lockAgeMs > staleThresholdMs) {
1227
+ fs.unlinkSync(lockFile);
1228
+ return writeActiveSkill(pDir, skillName, sessionId);
1229
+ }
1230
+ } catch (_statErr) {
1231
+ // intentionally silent: lock stat failed, retry write
1232
+ return writeActiveSkill(pDir, skillName, sessionId);
1233
+ }
1234
+ return { success: false, warning: `.active-skill.lock held by another process.` };
1235
+ }
1236
+
1237
+ try {
1238
+ fs.writeFileSync(skillFile, skillName, 'utf8');
1239
+ return { success: true, warning: `Lock failed (${e.code}), wrote without lock` };
1240
+ } catch (writeErr) {
1241
+ logHook('core', 'warn', 'Failed to write .active-skill', { error: writeErr.message });
1242
+ return { success: false, warning: `Failed to write .active-skill: ${writeErr.message}` };
1243
+ }
1244
+ }
1245
+ }
1246
+
1247
+ // ─── Phase claiming ───────────────────────────────────────────────────────────
1248
+
1249
+ /**
1250
+ * Check whether a claim is stale (its session directory no longer exists).
1251
+ *
1252
+ * @param {object} claimData - Parsed .claim JSON (must have session_id)
1253
+ * @param {string} pDir - Path to .planning/ directory
1254
+ * @returns {{ stale: boolean, reason?: string }}
1255
+ */
1256
+ function isClaimStale(claimData, pDir) {
1257
+ const sessionDir = path.join(pDir, '.sessions', claimData.session_id);
1258
+ if (!fs.existsSync(sessionDir)) {
1259
+ return { stale: true, reason: 'session_dir_missing' };
1260
+ }
1261
+ return { stale: false };
1262
+ }
1263
+
1264
+ /**
1265
+ * Acquire a phase claim for a session. Auto-releases stale claims.
1266
+ *
1267
+ * @param {string} pDir - Path to .planning/ directory
1268
+ * @param {string} phaseDir - Absolute path to the phase directory
1269
+ * @param {string} sessionId - Session identifier
1270
+ * @param {string} skill - Skill name acquiring the claim
1271
+ * @returns {{ acquired: boolean, conflict?: object, auto_released?: object }}
1272
+ */
1273
+ function acquireClaim(pDir, phaseDir, sessionId, skill) {
1274
+ const claimPath = path.join(phaseDir, '.claim');
1275
+ let autoReleased = null;
1276
+
1277
+ if (fs.existsSync(claimPath)) {
1278
+ try {
1279
+ const existing = JSON.parse(fs.readFileSync(claimPath, 'utf8'));
1280
+ if (existing.session_id !== sessionId) {
1281
+ const staleCheck = isClaimStale(existing, pDir);
1282
+ if (staleCheck.stale) {
1283
+ fs.unlinkSync(claimPath);
1284
+ autoReleased = existing;
1285
+ } else {
1286
+ return {
1287
+ acquired: false,
1288
+ conflict: {
1289
+ session_id: existing.session_id,
1290
+ skill: existing.skill,
1291
+ started: existing.started,
1292
+ pid: existing.pid
1293
+ },
1294
+ auto_released: null
1295
+ };
1296
+ }
1297
+ }
1298
+ } catch (_e) {
1299
+ try { fs.unlinkSync(claimPath); } catch (_unlinkErr) { /* intentionally silent: claim cleanup */ }
1300
+ }
1301
+ }
1302
+
1303
+ const claimData = {
1304
+ session_id: sessionId,
1305
+ skill: skill,
1306
+ started: new Date().toISOString(),
1307
+ pid: process.pid
1308
+ };
1309
+ fs.writeFileSync(claimPath, JSON.stringify(claimData, null, 2), 'utf8');
1310
+
1311
+ return { acquired: true, conflict: null, auto_released: autoReleased };
1312
+ }
1313
+
1314
+ /**
1315
+ * Release a phase claim owned by a specific session.
1316
+ *
1317
+ * @param {string} _pDir - Path to .planning/ directory (unused, for API consistency)
1318
+ * @param {string} phaseDir - Absolute path to the phase directory
1319
+ * @param {string} sessionId - Session identifier
1320
+ * @returns {{ released: boolean, reason?: string, owner?: string }}
1321
+ */
1322
+ function releaseClaim(_pDir, phaseDir, sessionId) {
1323
+ const claimPath = path.join(phaseDir, '.claim');
1324
+
1325
+ if (!fs.existsSync(claimPath)) {
1326
+ return { released: false, reason: 'no_claim' };
1327
+ }
1328
+
1329
+ try {
1330
+ const claim = JSON.parse(fs.readFileSync(claimPath, 'utf8'));
1331
+ if (claim.session_id !== sessionId) {
1332
+ return { released: false, reason: 'not_owner', owner: claim.session_id };
1333
+ }
1334
+ fs.unlinkSync(claimPath);
1335
+ return { released: true };
1336
+ } catch (_e) {
1337
+ try { fs.unlinkSync(claimPath); } catch (_unlinkErr) { /* intentionally silent: claim cleanup */ }
1338
+ return { released: true };
1339
+ }
1340
+ }
1341
+
1342
+ /**
1343
+ * List all active phase claims.
1344
+ *
1345
+ * @param {string} pDir - Path to .planning/ directory
1346
+ * @returns {{ claims: Array<object> }}
1347
+ */
1348
+ function listClaims(pDir) {
1349
+ const phasesDir = path.join(pDir, 'phases');
1350
+ if (!fs.existsSync(phasesDir)) {
1351
+ return { claims: [] };
1352
+ }
1353
+
1354
+ const results = [];
1355
+ try {
1356
+ const entries = fs.readdirSync(phasesDir, { withFileTypes: true });
1357
+ for (const entry of entries) {
1358
+ if (!entry.isDirectory()) continue;
1359
+ const claimPath = path.join(phasesDir, entry.name, '.claim');
1360
+ if (!fs.existsSync(claimPath)) continue;
1361
+ try {
1362
+ const claimData = JSON.parse(fs.readFileSync(claimPath, 'utf8'));
1363
+ results.push({
1364
+ phase: entry.name,
1365
+ ...claimData,
1366
+ stale: isClaimStale(claimData, pDir).stale
1367
+ });
1368
+ } catch (_e) { logHook('core', 'debug', 'Skipping malformed claim file'); }
1369
+ }
1370
+ } catch (_e) { logHook('core', 'debug', 'Failed to list claims'); }
1371
+
1372
+ return { claims: results };
1373
+ }
1374
+
1375
+ /**
1376
+ * Release all claims held by a specific session across all phase directories.
1377
+ *
1378
+ * @param {string} pDir - Path to .planning/ directory
1379
+ * @param {string} sessionId - Session identifier
1380
+ * @returns {{ released: string[] }}
1381
+ */
1382
+ function releaseSessionClaims(pDir, sessionId) {
1383
+ const phasesDir = path.join(pDir, 'phases');
1384
+ if (!fs.existsSync(phasesDir)) {
1385
+ return { released: [] };
1386
+ }
1387
+
1388
+ const released = [];
1389
+ try {
1390
+ const entries = fs.readdirSync(phasesDir, { withFileTypes: true });
1391
+ for (const entry of entries) {
1392
+ if (!entry.isDirectory()) continue;
1393
+ const claimPath = path.join(phasesDir, entry.name, '.claim');
1394
+ if (!fs.existsSync(claimPath)) continue;
1395
+ try {
1396
+ const claimData = JSON.parse(fs.readFileSync(claimPath, 'utf8'));
1397
+ if (claimData.session_id === sessionId) {
1398
+ fs.unlinkSync(claimPath);
1399
+ released.push(entry.name);
1400
+ }
1401
+ } catch (_e) { logHook('core', 'debug', 'Skipping malformed claim'); }
1402
+ }
1403
+ } catch (_e) { logHook('core', 'debug', 'Failed to release session claims'); }
1404
+
1405
+ return { released };
1406
+ }
1407
+
1408
+ // ─── Config loader (lightweight, used by core only) ───────────────────────────
1409
+
1410
+ function loadConfig(configCwd) {
1411
+ const configPath = path.join(configCwd, '.planning', 'config.json');
1412
+ const defaults = {
1413
+ model_profile: 'balanced',
1414
+ commit_docs: true,
1415
+ search_gitignored: false,
1416
+ branching_strategy: 'none',
1417
+ phase_branch_template: 'pbr/phase-{phase}-{slug}',
1418
+ milestone_branch_template: 'pbr/{milestone}-{slug}',
1419
+ research: true,
1420
+ plan_checker: true,
1421
+ verifier: true,
1422
+ nyquist_validation: true,
1423
+ parallelization: true,
1424
+ brave_search: false,
1425
+ };
1426
+
1427
+ try {
1428
+ const raw = fs.readFileSync(configPath, 'utf8');
1429
+ const parsed = JSON.parse(raw);
1430
+
1431
+ if ('depth' in parsed && !('granularity' in parsed)) {
1432
+ const depthToGranularity = { quick: 'coarse', standard: 'standard', comprehensive: 'fine' };
1433
+ parsed.granularity = depthToGranularity[parsed.depth] || parsed.depth;
1434
+ delete parsed.depth;
1435
+ try { fs.writeFileSync(configPath, JSON.stringify(parsed, null, 2), 'utf8'); } catch (e) { logHook('core', 'debug', 'Failed to write migrated config', { error: e.message }); }
1436
+ }
1437
+
1438
+ const get = (key, nested) => {
1439
+ if (parsed[key] !== undefined) return parsed[key];
1440
+ if (nested && parsed[nested.section] && parsed[nested.section][nested.field] !== undefined) {
1441
+ return parsed[nested.section][nested.field];
1442
+ }
1443
+ return undefined;
1444
+ };
1445
+
1446
+ const parallelization = (() => {
1447
+ const val = get('parallelization');
1448
+ if (typeof val === 'boolean') return val;
1449
+ if (typeof val === 'object' && val !== null && 'enabled' in val) return val.enabled;
1450
+ return defaults.parallelization;
1451
+ })();
1452
+
1453
+ return {
1454
+ model_profile: get('model_profile') ?? defaults.model_profile,
1455
+ commit_docs: get('commit_docs', { section: 'planning', field: 'commit_docs' }) ?? defaults.commit_docs,
1456
+ search_gitignored: get('search_gitignored', { section: 'planning', field: 'search_gitignored' }) ?? defaults.search_gitignored,
1457
+ branching_strategy: get('branching_strategy', { section: 'git', field: 'branching_strategy' }) ?? defaults.branching_strategy,
1458
+ phase_branch_template: get('phase_branch_template', { section: 'git', field: 'phase_branch_template' }) ?? defaults.phase_branch_template,
1459
+ milestone_branch_template: get('milestone_branch_template', { section: 'git', field: 'milestone_branch_template' }) ?? defaults.milestone_branch_template,
1460
+ research: get('research', { section: 'workflow', field: 'research' }) ?? defaults.research,
1461
+ plan_checker: get('plan_checker', { section: 'workflow', field: 'plan_check' }) ?? defaults.plan_checker,
1462
+ verifier: get('verifier', { section: 'workflow', field: 'verifier' }) ?? defaults.verifier,
1463
+ nyquist_validation: get('nyquist_validation', { section: 'workflow', field: 'nyquist_validation' }) ?? defaults.nyquist_validation,
1464
+ parallelization,
1465
+ brave_search: get('brave_search') ?? defaults.brave_search,
1466
+ model_overrides: parsed.model_overrides || null,
1467
+ };
1468
+ } catch (e) {
1469
+ logHook('core', 'debug', 'Failed to load config, using defaults', { error: e.message });
1470
+ return defaults;
1471
+ }
1472
+ }
1473
+
1474
+ /**
1475
+ * Check if a path is git-ignored, scoped to a cwd.
1476
+ *
1477
+ * @param {string} igCwd - Working directory
1478
+ * @param {string} targetPath - Path to check
1479
+ * @returns {boolean}
1480
+ */
1481
+ function pathExistsInternal(peCwd, targetPath) {
1482
+ const fullPath = path.isAbsolute(targetPath) ? targetPath : path.join(peCwd, targetPath);
1483
+ try {
1484
+ fs.statSync(fullPath);
1485
+ return true;
1486
+ } catch {
1487
+ // intentionally silent: path existence check
1488
+ return false;
1489
+ }
1490
+ }
1491
+
1492
+ function resolveModelInternal(rmCwd, agentType) {
1493
+ const config = loadConfig(rmCwd);
1494
+ return resolveModel(agentType, config);
1495
+ }
1496
+
1497
+ // ─── Exports ──────────────────────────────────────────────────────────────────
1498
+
1499
+ module.exports = {
1500
+ // Module-level state
1501
+ setCwd,
1502
+
1503
+ // Constants
1504
+ KNOWN_AGENTS,
1505
+ VALID_STATUS_TRANSITIONS,
1506
+ STATUS_LABELS,
1507
+ MODEL_PROFILES,
1508
+ SESSION_ALLOWED_KEYS,
1509
+ STALE_SESSION_MS,
1510
+
1511
+ // Status transitions
1512
+ validateStatusTransition,
1513
+
1514
+ // Output
1515
+ output,
1516
+ error,
1517
+
1518
+ // Path & file utilities
1519
+ toPosixPath,
1520
+ safeReadFile,
1521
+ ensureDir,
1522
+ findFiles,
1523
+ tailLines,
1524
+ escapeRegex,
1525
+
1526
+ // Git utilities
1527
+ execGit,
1528
+ isGitIgnored,
1529
+
1530
+ // YAML frontmatter
1531
+ parseYamlFrontmatter,
1532
+ parseMustHaves,
1533
+ setYamlFrontmatter,
1534
+
1535
+ // Misc utilities
1536
+ countMustHaves,
1537
+ determinePhaseStatus,
1538
+ calculateProgress,
1539
+ currentTimestamp,
1540
+ generateSlug,
1541
+ resolveModel,
1542
+
1543
+ // Phase utilities
1544
+ normalizePhaseName,
1545
+ comparePhaseNum,
1546
+ searchPhaseInDir,
1547
+ findPhaseInternal,
1548
+ getArchivedPhaseDirs,
1549
+
1550
+ // Roadmap & milestone
1551
+ getRoadmapPhaseInternal,
1552
+ getMilestoneInfo,
1553
+ getMilestonePhaseFilter,
1554
+
1555
+ // Config loader (lightweight)
1556
+ loadConfig,
1557
+ pathExistsInternal,
1558
+ resolveModelInternal,
1559
+ generateSlugInternal: generateSlug,
1560
+
1561
+ // Atomic operations
1562
+ atomicWrite,
1563
+ lockedFileUpdate,
1564
+
1565
+ // Schema validation
1566
+ validateObject,
1567
+
1568
+ // Session management
1569
+ resolveSessionPath,
1570
+ ensureSessionDir,
1571
+ removeSessionDir,
1572
+ cleanStaleSessions,
1573
+ sessionLoad,
1574
+ sessionSave,
1575
+ sessionClear,
1576
+ sessionDump,
1577
+ writeActiveSkill,
1578
+
1579
+ // Phase claiming
1580
+ isClaimStale,
1581
+ acquireClaim,
1582
+ releaseClaim,
1583
+ listClaims,
1584
+ releaseSessionClaims,
1585
+ };