@sienklogic/plan-build-run 2.21.0 → 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 (983) hide show
  1. package/CHANGELOG.md +1331 -299
  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 +212 -34
  147. package/plugins/pbr/agents/dev-sync.md +206 -0
  148. package/plugins/pbr/agents/executor.md +737 -39
  149. package/plugins/pbr/agents/general.md +71 -6
  150. package/plugins/pbr/agents/integration-checker.md +148 -30
  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 -66
  154. package/plugins/pbr/agents/planner.md +451 -42
  155. package/plugins/pbr/agents/researcher.md +219 -36
  156. package/plugins/pbr/agents/roadmapper.md +398 -0
  157. package/plugins/pbr/agents/synthesizer.md +166 -26
  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 +571 -47
  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/do.md +5 -0
  172. package/plugins/pbr/commands/execute-phase.md +6 -0
  173. package/plugins/pbr/commands/fast.md +6 -0
  174. package/plugins/pbr/commands/forensics.md +6 -0
  175. package/plugins/pbr/commands/import.md +1 -1
  176. package/plugins/pbr/commands/insert-phase.md +65 -0
  177. package/plugins/pbr/commands/intel.md +5 -0
  178. package/plugins/pbr/commands/join-discord.md +11 -0
  179. package/plugins/pbr/commands/list-phase-assumptions.md +5 -0
  180. package/plugins/pbr/commands/map-codebase.md +6 -0
  181. package/plugins/pbr/commands/milestone-summary.md +6 -0
  182. package/plugins/pbr/commands/new-milestone.md +8 -0
  183. package/plugins/pbr/commands/new-project.md +6 -0
  184. package/plugins/pbr/commands/pause-work.md +5 -0
  185. package/plugins/pbr/commands/plan-milestone-gaps.md +7 -0
  186. package/plugins/pbr/commands/plan-phase.md +6 -0
  187. package/plugins/pbr/commands/plant-seed.md +6 -0
  188. package/plugins/pbr/commands/profile-user.md +5 -0
  189. package/plugins/pbr/commands/profile.md +5 -0
  190. package/plugins/pbr/commands/progress.md +6 -0
  191. package/plugins/pbr/commands/quick.md +1 -1
  192. package/plugins/pbr/commands/reapply-patches.md +47 -0
  193. package/plugins/pbr/commands/release.md +6 -0
  194. package/plugins/pbr/commands/remove-phase.md +66 -0
  195. package/plugins/pbr/commands/research-phase.md +59 -0
  196. package/plugins/pbr/commands/resume-work.md +5 -0
  197. package/plugins/pbr/commands/seed.md +6 -0
  198. package/plugins/pbr/commands/session-report.md +5 -0
  199. package/plugins/pbr/commands/set-profile.md +6 -0
  200. package/plugins/pbr/commands/settings.md +5 -0
  201. package/plugins/pbr/commands/setup.md +1 -1
  202. package/plugins/pbr/commands/ship.md +5 -0
  203. package/plugins/pbr/commands/stats.md +6 -0
  204. package/plugins/pbr/commands/test.md +5 -0
  205. package/plugins/pbr/commands/thread.md +6 -0
  206. package/plugins/pbr/commands/todo.md +1 -1
  207. package/plugins/pbr/commands/ui-phase.md +5 -0
  208. package/plugins/pbr/commands/ui-review.md +5 -0
  209. package/plugins/pbr/commands/undo.md +5 -0
  210. package/plugins/pbr/commands/update.md +37 -0
  211. package/plugins/pbr/commands/validate-phase.md +5 -0
  212. package/plugins/pbr/commands/verify-work.md +6 -0
  213. package/plugins/pbr/dashboard/package-lock.json +6 -0
  214. package/plugins/pbr/dist/architecture-guard.js +76 -0
  215. package/plugins/pbr/dist/audit-dimensions.js +556 -0
  216. package/plugins/pbr/dist/auto-continue.js +277 -0
  217. package/plugins/pbr/dist/block-skill-self-read.js +124 -0
  218. package/plugins/pbr/dist/check-agent-state-write.js +63 -0
  219. package/plugins/pbr/dist/check-config-change.js +213 -0
  220. package/plugins/pbr/dist/check-cross-plugin-sync.js +93 -0
  221. package/plugins/pbr/dist/check-dangerous-commands.js +193 -0
  222. package/plugins/pbr/dist/check-direct-state-write.js +37 -0
  223. package/plugins/pbr/dist/check-doc-sprawl.js +102 -0
  224. package/plugins/pbr/dist/check-phase-boundary.js +191 -0
  225. package/plugins/pbr/dist/check-plan-format.js +227 -0
  226. package/plugins/pbr/dist/check-read-first.js +345 -0
  227. package/plugins/pbr/dist/check-roadmap-sync.js +507 -0
  228. package/plugins/pbr/dist/check-skill-workflow.js +354 -0
  229. package/plugins/pbr/dist/check-state-sync.js +676 -0
  230. package/plugins/pbr/dist/check-subagent-output.js +425 -0
  231. package/plugins/pbr/dist/check-summary-gate.js +188 -0
  232. package/plugins/pbr/dist/context-bridge.js +425 -0
  233. package/plugins/pbr/dist/context-budget-check.js +442 -0
  234. package/plugins/pbr/dist/context-quality.js +271 -0
  235. package/plugins/pbr/dist/enforce-context-budget.js +138 -0
  236. package/plugins/pbr/dist/enforce-pbr-workflow.js +277 -0
  237. package/plugins/pbr/dist/event-handler.js +212 -0
  238. package/plugins/pbr/dist/event-logger.js +125 -0
  239. package/plugins/pbr/dist/feedback-loop.js +155 -0
  240. package/plugins/pbr/dist/graph-update.js +422 -0
  241. package/plugins/pbr/dist/hook-logger.js +114 -0
  242. package/plugins/pbr/dist/hook-server-client.js +361 -0
  243. package/plugins/pbr/dist/hook-server.js +664 -0
  244. package/plugins/pbr/dist/hooks-schema.json +87 -0
  245. package/plugins/pbr/dist/instructions-loaded.js +173 -0
  246. package/plugins/pbr/dist/intercept-plan-mode.js +81 -0
  247. package/plugins/pbr/dist/log-notification.js +131 -0
  248. package/plugins/pbr/dist/log-subagent.js +367 -0
  249. package/plugins/pbr/dist/log-tool-failure.js +140 -0
  250. package/plugins/pbr/dist/milestone-learnings.js +519 -0
  251. package/plugins/pbr/dist/pbr-tools.js +493 -0
  252. package/plugins/pbr/dist/post-bash-triage.js +96 -0
  253. package/plugins/pbr/dist/post-compact.js +135 -0
  254. package/plugins/pbr/dist/post-hoc.js +237 -0
  255. package/plugins/pbr/dist/post-write-dispatch.js +243 -0
  256. package/plugins/pbr/dist/post-write-quality.js +208 -0
  257. package/plugins/pbr/dist/pre-bash-dispatch.js +212 -0
  258. package/plugins/pbr/dist/pre-skill-dispatch.js +114 -0
  259. package/plugins/pbr/dist/pre-task-dispatch.js +269 -0
  260. package/plugins/pbr/dist/pre-write-dispatch.js +234 -0
  261. package/plugins/pbr/dist/progress-tracker.js +173 -0
  262. package/plugins/pbr/dist/prompt-guard.js +114 -0
  263. package/plugins/pbr/dist/prompt-routing.js +209 -0
  264. package/plugins/pbr/dist/quick-status.js +179 -0
  265. package/plugins/pbr/dist/record-incident.js +37 -0
  266. package/plugins/pbr/dist/run-hook.js +132 -0
  267. package/plugins/pbr/dist/session-cleanup.js +653 -0
  268. package/plugins/pbr/dist/session-tracker.js +124 -0
  269. package/plugins/pbr/dist/status-line.js +849 -0
  270. package/plugins/pbr/dist/suggest-compact.js +307 -0
  271. package/plugins/pbr/dist/sync-context-to-claude.js +100 -0
  272. package/plugins/pbr/dist/task-completed.js +206 -0
  273. package/plugins/pbr/dist/track-context-budget.js +432 -0
  274. package/plugins/pbr/dist/track-user-gates.js +88 -0
  275. package/plugins/pbr/dist/trust-tracker.js +193 -0
  276. package/plugins/pbr/dist/validate-commit.js +233 -0
  277. package/plugins/pbr/dist/validate-skill-args.js +222 -0
  278. package/plugins/pbr/dist/validate-task.js +271 -0
  279. package/plugins/pbr/dist/worktree-create.js +144 -0
  280. package/plugins/pbr/dist/worktree-remove.js +147 -0
  281. package/plugins/pbr/hooks/hooks.json +137 -65
  282. package/plugins/pbr/references/agent-contracts.md +39 -8
  283. package/plugins/pbr/references/agent-teams.md +3 -3
  284. package/plugins/pbr/references/archive/checkpoints.md +189 -0
  285. package/plugins/pbr/references/archive/context-quality-tiers.md +45 -0
  286. package/plugins/pbr/references/archive/hook-ordering.md +89 -0
  287. package/plugins/pbr/references/archive/limitations.md +106 -0
  288. package/plugins/pbr/references/archive/pbr-rules.md +194 -0
  289. package/plugins/pbr/references/archive/pbr-tools-cli.md +415 -0
  290. package/plugins/pbr/references/archive/pretooluse-jsonl-behavior.md +58 -0
  291. package/plugins/pbr/references/archive/signal-files.md +41 -0
  292. package/plugins/pbr/references/archive/tmux-setup.md +288 -0
  293. package/plugins/pbr/references/archive/verification-matrix.md +34 -0
  294. package/plugins/pbr/references/archive/verification-patterns.md +277 -0
  295. package/plugins/pbr/references/archive/worktree-sparse-checkout.md +86 -0
  296. package/plugins/pbr/references/assumptions.md +42 -0
  297. package/plugins/pbr/references/checkpoints.md +723 -104
  298. package/plugins/pbr/references/config-reference.md +387 -10
  299. package/plugins/pbr/references/continuation-format.md +1 -0
  300. package/plugins/pbr/references/decimal-phase-calculation.md +65 -0
  301. package/plugins/pbr/references/deviation-rules.md +12 -0
  302. package/plugins/pbr/references/few-shot-examples/audit.md +77 -0
  303. package/plugins/pbr/references/few-shot-examples/check-plan-format.md +172 -0
  304. package/plugins/pbr/references/few-shot-examples/check-subagent-output.md +118 -0
  305. package/plugins/pbr/references/few-shot-examples/integration-checker.md +70 -0
  306. package/plugins/pbr/references/few-shot-examples/nyquist-auditor.md +83 -0
  307. package/plugins/pbr/references/few-shot-examples/plan-checker.md +73 -0
  308. package/plugins/pbr/references/few-shot-examples/ui-checker.md +71 -0
  309. package/plugins/pbr/references/few-shot-examples/verifier.md +109 -0
  310. package/plugins/pbr/references/git-integration.md +110 -27
  311. package/plugins/pbr/references/git-planning-commit.md +35 -0
  312. package/plugins/pbr/references/model-profile-resolution.md +34 -0
  313. package/plugins/pbr/references/model-profiles.md +90 -7
  314. package/plugins/pbr/references/model-selection.md +1 -1
  315. package/plugins/pbr/references/node-repair.md +48 -0
  316. package/plugins/pbr/references/plan-authoring.md +65 -0
  317. package/plugins/pbr/references/plan-format.md +184 -10
  318. package/plugins/pbr/references/questioning.md +138 -49
  319. package/plugins/pbr/references/reading-verification.md +4 -4
  320. package/plugins/pbr/references/tdd.md +263 -0
  321. package/plugins/pbr/references/thinking-models-planning.md +47 -0
  322. package/plugins/pbr/references/thinking-models-verification.md +44 -0
  323. package/plugins/pbr/references/ui-brand.md +449 -0
  324. package/plugins/pbr/references/verification-overrides.md +39 -0
  325. package/plugins/pbr/references/verification-patterns.md +529 -113
  326. package/plugins/pbr/scripts/architecture-guard.js +76 -0
  327. package/plugins/pbr/scripts/audit-checks/behavioral-compliance.js +2098 -0
  328. package/plugins/pbr/scripts/audit-checks/error-analysis.js +989 -0
  329. package/plugins/pbr/scripts/audit-checks/feature-verification.js +723 -0
  330. package/plugins/pbr/scripts/audit-checks/index.js +433 -0
  331. package/plugins/pbr/scripts/audit-checks/infrastructure.js +816 -0
  332. package/plugins/pbr/scripts/audit-checks/quality-metrics.js +455 -0
  333. package/plugins/pbr/scripts/audit-checks/session-quality.js +980 -0
  334. package/plugins/pbr/scripts/audit-checks/si-agent-hook-config-checks.js +396 -0
  335. package/plugins/pbr/scripts/audit-checks/si-cross-cutting-checks.js +272 -0
  336. package/plugins/pbr/scripts/audit-checks/si-skill-checks.js +424 -0
  337. package/plugins/pbr/scripts/audit-checks/workflow-compliance.js +1175 -0
  338. package/plugins/pbr/scripts/audit-dimensions.js +556 -0
  339. package/plugins/pbr/scripts/auto-continue.js +192 -37
  340. package/plugins/pbr/scripts/block-skill-self-read.js +124 -0
  341. package/plugins/pbr/scripts/check-agent-state-write.js +63 -0
  342. package/plugins/pbr/scripts/check-config-change.js +84 -1
  343. package/plugins/pbr/scripts/check-cross-plugin-sync.js +93 -0
  344. package/plugins/pbr/scripts/check-dangerous-commands.js +18 -5
  345. package/plugins/pbr/scripts/check-direct-state-write.js +37 -0
  346. package/plugins/pbr/scripts/check-phase-boundary.js +3 -8
  347. package/plugins/pbr/scripts/check-plan-format.js +153 -278
  348. package/plugins/pbr/scripts/check-read-first.js +345 -0
  349. package/plugins/pbr/scripts/check-roadmap-sync.js +174 -19
  350. package/plugins/pbr/scripts/check-skill-workflow.js +24 -27
  351. package/plugins/pbr/scripts/check-state-sync.js +360 -218
  352. package/plugins/pbr/scripts/check-subagent-output.js +308 -273
  353. package/plugins/pbr/scripts/check-summary-gate.js +5 -15
  354. package/plugins/pbr/scripts/commands/benchmarks.js +195 -0
  355. package/plugins/pbr/scripts/commands/calibrate.js +530 -0
  356. package/plugins/pbr/scripts/commands/config.js +72 -0
  357. package/plugins/pbr/scripts/commands/misc.js +779 -0
  358. package/plugins/pbr/scripts/commands/phase.js +293 -0
  359. package/plugins/pbr/scripts/commands/roadmap.js +75 -0
  360. package/plugins/pbr/scripts/commands/state.js +84 -0
  361. package/plugins/pbr/scripts/commands/stress-test.js +349 -0
  362. package/plugins/pbr/scripts/commands/todo.js +191 -0
  363. package/plugins/pbr/scripts/commands/verify.js +169 -0
  364. package/plugins/pbr/scripts/config-schema.json +1183 -95
  365. package/plugins/pbr/scripts/context-bridge.js +425 -0
  366. package/plugins/pbr/scripts/context-budget-check.js +171 -16
  367. package/plugins/pbr/scripts/context-quality.js +271 -0
  368. package/plugins/pbr/scripts/enforce-context-budget.js +138 -0
  369. package/plugins/pbr/scripts/enforce-pbr-workflow.js +277 -0
  370. package/plugins/pbr/scripts/event-handler.js +137 -87
  371. package/plugins/pbr/scripts/event-logger.js +58 -25
  372. package/plugins/pbr/scripts/feedback-loop.js +155 -0
  373. package/plugins/pbr/scripts/graph-update.js +422 -0
  374. package/plugins/pbr/scripts/hook-logger.js +69 -35
  375. package/plugins/pbr/scripts/hook-server-client.js +361 -0
  376. package/plugins/pbr/scripts/hook-server.js +664 -0
  377. package/plugins/pbr/scripts/hooks-schema.json +12 -5
  378. package/plugins/pbr/scripts/instructions-loaded.js +173 -0
  379. package/plugins/pbr/scripts/intent-router.cjs +147 -0
  380. package/plugins/pbr/scripts/intercept-plan-mode.js +52 -18
  381. package/plugins/pbr/scripts/lib/alternatives.js +203 -0
  382. package/plugins/pbr/scripts/lib/audit.js +65 -0
  383. package/plugins/pbr/scripts/lib/auto-cleanup.js +221 -0
  384. package/plugins/pbr/scripts/lib/auto-verify.js +123 -0
  385. package/plugins/pbr/scripts/lib/benchmark.js +190 -0
  386. package/plugins/pbr/scripts/lib/build.js +719 -0
  387. package/plugins/pbr/scripts/lib/ci-fix-loop.js +228 -0
  388. package/plugins/pbr/scripts/lib/commands.js +483 -0
  389. package/plugins/pbr/scripts/lib/compound.js +222 -0
  390. package/plugins/pbr/scripts/lib/config-cache.js +83 -0
  391. package/plugins/pbr/scripts/lib/config.js +1469 -0
  392. package/plugins/pbr/scripts/lib/context.js +254 -0
  393. package/plugins/pbr/scripts/lib/contextual-help.js +183 -0
  394. package/plugins/pbr/scripts/lib/convention-detector.js +413 -0
  395. package/plugins/pbr/scripts/lib/core.js +1585 -0
  396. package/plugins/pbr/scripts/lib/dashboard-launch.js +364 -0
  397. package/plugins/pbr/scripts/lib/data-hygiene.js +179 -0
  398. package/plugins/pbr/scripts/lib/decision-extraction.js +183 -0
  399. package/plugins/pbr/scripts/lib/decisions.js +194 -0
  400. package/plugins/pbr/scripts/lib/dependency-break.js +147 -0
  401. package/plugins/pbr/scripts/lib/format-validators.js +1049 -0
  402. package/plugins/pbr/scripts/lib/frontmatter.js +302 -0
  403. package/plugins/pbr/scripts/lib/gates/advisories.js +133 -0
  404. package/plugins/pbr/scripts/lib/gates/build-dependency.js +118 -0
  405. package/plugins/pbr/scripts/lib/gates/build-executor.js +106 -0
  406. package/plugins/pbr/scripts/lib/gates/doc-existence.js +46 -0
  407. package/plugins/pbr/scripts/lib/gates/helpers.js +98 -0
  408. package/plugins/pbr/scripts/lib/gates/inline-execution.js +187 -0
  409. package/plugins/pbr/scripts/lib/gates/milestone-complete.js +139 -0
  410. package/plugins/pbr/scripts/lib/gates/milestone-summary.js +121 -0
  411. package/plugins/pbr/scripts/lib/gates/multi-phase-loader.js +149 -0
  412. package/plugins/pbr/scripts/lib/gates/plan-executor.js +36 -0
  413. package/plugins/pbr/scripts/lib/gates/plan-validation.js +115 -0
  414. package/plugins/pbr/scripts/lib/gates/quick-executor.js +78 -0
  415. package/plugins/pbr/scripts/lib/gates/review-planner.js +63 -0
  416. package/plugins/pbr/scripts/lib/gates/review-verifier.js +71 -0
  417. package/plugins/pbr/scripts/lib/gates/rich-agent-context.js +148 -0
  418. package/plugins/pbr/scripts/lib/gates/sprint-preflight.js +30 -0
  419. package/plugins/pbr/scripts/lib/gates/user-confirmation.js +95 -0
  420. package/plugins/pbr/scripts/lib/graph-cli.js +89 -0
  421. package/plugins/pbr/scripts/lib/graph.js +553 -0
  422. package/plugins/pbr/scripts/lib/handoff-validators.js +224 -0
  423. package/plugins/pbr/scripts/lib/health-checks.js +107 -0
  424. package/plugins/pbr/scripts/lib/health-phase06.js +120 -0
  425. package/plugins/pbr/scripts/lib/health.js +132 -0
  426. package/plugins/pbr/scripts/lib/help.js +100 -0
  427. package/plugins/pbr/scripts/lib/history.js +150 -0
  428. package/plugins/pbr/scripts/lib/impact-analysis.js +319 -0
  429. package/plugins/pbr/scripts/lib/incidents.js +190 -0
  430. package/plugins/pbr/scripts/lib/init.js +643 -0
  431. package/plugins/pbr/scripts/lib/insights-parser.js +320 -0
  432. package/plugins/pbr/scripts/lib/intel.js +653 -0
  433. package/plugins/pbr/scripts/lib/learnings.js +511 -0
  434. package/plugins/pbr/scripts/lib/migrate.js +309 -0
  435. package/plugins/pbr/scripts/lib/milestone.js +306 -0
  436. package/plugins/pbr/scripts/lib/msys-path.js +20 -0
  437. package/plugins/pbr/scripts/lib/negative-knowledge.js +194 -0
  438. package/plugins/pbr/scripts/lib/notification-throttle.js +141 -0
  439. package/plugins/pbr/scripts/lib/onboarding-generator.js +288 -0
  440. package/plugins/pbr/scripts/lib/parse-args.js +134 -0
  441. package/plugins/pbr/scripts/lib/pattern-routing.js +55 -0
  442. package/plugins/pbr/scripts/lib/patterns.js +272 -0
  443. package/plugins/pbr/scripts/lib/perf.js +190 -0
  444. package/plugins/pbr/scripts/lib/phase.js +1043 -0
  445. package/plugins/pbr/scripts/lib/pid-lock.js +156 -0
  446. package/plugins/pbr/scripts/lib/post-hoc.js +160 -0
  447. package/plugins/pbr/scripts/lib/pre-commit-checks.js +220 -0
  448. package/plugins/pbr/scripts/lib/pre-research.js +126 -0
  449. package/plugins/pbr/scripts/lib/premature-completion.js +312 -0
  450. package/plugins/pbr/scripts/lib/preview.js +174 -0
  451. package/plugins/pbr/scripts/lib/progress-visualization.js +296 -0
  452. package/plugins/pbr/scripts/lib/quick-init.js +131 -0
  453. package/plugins/pbr/scripts/lib/reference.js +236 -0
  454. package/plugins/pbr/scripts/lib/requirements.js +153 -0
  455. package/plugins/pbr/scripts/lib/resolve-root.js +66 -0
  456. package/plugins/pbr/scripts/lib/reverse-spec.js +259 -0
  457. package/plugins/pbr/scripts/lib/roadmap.js +1089 -0
  458. package/plugins/pbr/scripts/lib/security-scan.js +200 -0
  459. package/plugins/pbr/scripts/lib/session-briefing.js +918 -0
  460. package/plugins/pbr/scripts/lib/skill-section.js +99 -0
  461. package/plugins/pbr/scripts/lib/smart-next-task.js +198 -0
  462. package/plugins/pbr/scripts/lib/snapshot-manager.js +232 -0
  463. package/plugins/pbr/scripts/lib/spec-diff.js +209 -0
  464. package/plugins/pbr/scripts/lib/spec-engine.js +189 -0
  465. package/plugins/pbr/scripts/lib/spot-check.js +539 -0
  466. package/plugins/pbr/scripts/lib/state-queue.js +171 -0
  467. package/plugins/pbr/scripts/lib/state.js +1082 -0
  468. package/plugins/pbr/scripts/lib/status-render.js +511 -0
  469. package/plugins/pbr/scripts/lib/step-verify.js +149 -0
  470. package/plugins/pbr/scripts/lib/subagent-validators.js +1119 -0
  471. package/plugins/pbr/scripts/lib/suggest-next.js +435 -0
  472. package/plugins/pbr/scripts/lib/tech-debt-scanner.js +116 -0
  473. package/plugins/pbr/scripts/lib/templates.js +362 -0
  474. package/plugins/pbr/scripts/lib/test-selection.js +163 -0
  475. package/plugins/pbr/scripts/lib/todo.js +300 -0
  476. package/plugins/pbr/scripts/lib/verify.js +1561 -0
  477. package/plugins/pbr/scripts/log-notification.js +131 -0
  478. package/plugins/pbr/scripts/log-subagent.js +221 -18
  479. package/plugins/pbr/scripts/log-tool-failure.js +60 -8
  480. package/plugins/pbr/scripts/milestone-learnings.js +519 -0
  481. package/plugins/pbr/scripts/package.json +1 -1
  482. package/plugins/pbr/scripts/pbr-tools.js +362 -1247
  483. package/plugins/pbr/scripts/post-bash-triage.js +96 -0
  484. package/plugins/pbr/scripts/post-compact.js +135 -0
  485. package/plugins/pbr/scripts/post-hoc.js +237 -0
  486. package/plugins/pbr/scripts/post-write-dispatch.js +201 -31
  487. package/plugins/pbr/scripts/post-write-quality.js +4 -3
  488. package/plugins/pbr/scripts/pre-bash-dispatch.js +147 -51
  489. package/plugins/pbr/scripts/pre-skill-dispatch.js +114 -0
  490. package/plugins/pbr/scripts/pre-task-dispatch.js +269 -0
  491. package/plugins/pbr/scripts/pre-write-dispatch.js +170 -73
  492. package/plugins/pbr/scripts/progress-tracker.js +121 -324
  493. package/plugins/pbr/scripts/prompt-guard.js +114 -0
  494. package/plugins/pbr/scripts/prompt-routing.js +209 -0
  495. package/plugins/pbr/scripts/quick-status.js +179 -0
  496. package/plugins/pbr/scripts/record-incident.js +37 -0
  497. package/plugins/pbr/scripts/risk-classifier.cjs +123 -0
  498. package/plugins/pbr/scripts/run-hook.js +63 -23
  499. package/plugins/pbr/scripts/session-cleanup.js +428 -29
  500. package/plugins/pbr/scripts/session-tracker.js +124 -0
  501. package/plugins/pbr/scripts/status-line.js +593 -32
  502. package/plugins/pbr/scripts/suggest-compact.js +201 -13
  503. package/plugins/pbr/scripts/sync-context-to-claude.js +100 -0
  504. package/plugins/pbr/scripts/task-completed.js +165 -4
  505. package/plugins/pbr/scripts/test/config.test.js +126 -0
  506. package/plugins/pbr/scripts/test/cross-platform.test.js +120 -0
  507. package/plugins/pbr/scripts/test/fixtures/config.json +20 -0
  508. package/plugins/pbr/scripts/test/fixtures/plan.md +54 -0
  509. package/plugins/pbr/scripts/test/fixtures/project.md +30 -0
  510. package/plugins/pbr/scripts/test/fixtures/roadmap.md +55 -0
  511. package/plugins/pbr/scripts/test/fixtures/state.md +60 -0
  512. package/plugins/pbr/scripts/test/fixtures/summary.md +35 -0
  513. package/plugins/pbr/scripts/test/fixtures.test.js +184 -0
  514. package/plugins/pbr/scripts/test/phase.test.js +142 -0
  515. package/plugins/pbr/scripts/test/roadmap.test.js +96 -0
  516. package/plugins/pbr/scripts/test/state.test.js +155 -0
  517. package/plugins/pbr/scripts/track-context-budget.js +368 -104
  518. package/plugins/pbr/scripts/track-user-gates.js +88 -0
  519. package/plugins/pbr/scripts/trust-tracker.js +193 -0
  520. package/plugins/pbr/scripts/validate-commit.js +59 -26
  521. package/plugins/pbr/scripts/validate-skill-args.js +87 -15
  522. package/plugins/pbr/scripts/validate-task.js +83 -627
  523. package/plugins/pbr/scripts/worktree-create.js +144 -0
  524. package/plugins/pbr/scripts/worktree-remove.js +147 -0
  525. package/plugins/pbr/skills/audit/SKILL.md +195 -24
  526. package/plugins/pbr/skills/audit-fix/SKILL.md +326 -0
  527. package/plugins/pbr/skills/autonomous/SKILL.md +551 -0
  528. package/plugins/pbr/skills/backlog/SKILL.md +56 -0
  529. package/plugins/pbr/skills/begin/SKILL.md +508 -153
  530. package/plugins/pbr/skills/begin/templates/STATE.md.tmpl +1 -2
  531. package/plugins/pbr/skills/begin/templates/config.json.tmpl +419 -36
  532. package/plugins/pbr/skills/begin/templates/researcher-prompt.md.tmpl +28 -0
  533. package/plugins/pbr/skills/begin/templates/roadmap-prompt.md.tmpl +28 -3
  534. package/plugins/pbr/skills/begin/templates/synthesis-prompt.md.tmpl +33 -5
  535. package/plugins/pbr/skills/build/SKILL.md +1180 -358
  536. package/plugins/pbr/skills/build/templates/continuation-prompt.md.tmpl +26 -0
  537. package/plugins/pbr/skills/build/templates/executor-prompt.md.tmpl +77 -0
  538. package/plugins/pbr/skills/build/templates/inline-verifier-prompt.md.tmpl +33 -0
  539. package/plugins/pbr/skills/build/templates/qa-round-context.md.tmpl +16 -0
  540. package/plugins/pbr/skills/config/SKILL.md +112 -9
  541. package/plugins/pbr/skills/continue/SKILL.md +113 -33
  542. package/plugins/pbr/skills/dashboard/SKILL.md +21 -9
  543. package/plugins/pbr/skills/debug/SKILL.md +70 -12
  544. package/plugins/pbr/skills/debug/templates/continuation-prompt.md.tmpl +12 -1
  545. package/plugins/pbr/skills/debug/templates/initial-investigation-prompt.md.tmpl +12 -5
  546. package/plugins/pbr/skills/discuss/SKILL.md +205 -24
  547. package/plugins/pbr/skills/discuss/templates/CONTEXT.md.tmpl +21 -1
  548. package/plugins/pbr/skills/do/SKILL.md +119 -24
  549. package/plugins/pbr/skills/explore/SKILL.md +95 -20
  550. package/plugins/pbr/skills/fast/SKILL.md +94 -0
  551. package/plugins/pbr/skills/forensics/SKILL.md +144 -0
  552. package/plugins/pbr/skills/health/SKILL.md +35 -117
  553. package/plugins/pbr/skills/help/SKILL.md +83 -123
  554. package/plugins/pbr/skills/import/SKILL.md +332 -13
  555. package/plugins/pbr/skills/intel/SKILL.md +131 -0
  556. package/plugins/pbr/skills/list-phase-assumptions/SKILL.md +231 -0
  557. package/plugins/pbr/skills/milestone/SKILL.md +383 -274
  558. package/plugins/pbr/skills/milestone/templates/audit-output.md.tmpl +76 -0
  559. package/plugins/pbr/skills/milestone/templates/complete-output.md.tmpl +32 -0
  560. package/plugins/pbr/skills/milestone/templates/edge-cases.md +54 -0
  561. package/plugins/pbr/skills/milestone/templates/gaps-output.md.tmpl +25 -0
  562. package/plugins/pbr/skills/milestone/templates/integration-checker-prompt.md.tmpl +25 -0
  563. package/plugins/pbr/skills/milestone/templates/new-output.md.tmpl +29 -0
  564. package/plugins/pbr/skills/milestone-summary/SKILL.md +86 -0
  565. package/plugins/pbr/skills/note/SKILL.md +20 -4
  566. package/plugins/pbr/skills/pause/SKILL.md +54 -14
  567. package/plugins/pbr/skills/pause/templates/continue-here.md.tmpl +33 -52
  568. package/plugins/pbr/skills/plan/SKILL.md +526 -280
  569. package/plugins/pbr/skills/plan/templates/checker-prompt.md.tmpl +20 -2
  570. package/plugins/pbr/skills/plan/templates/completion-output.md.tmpl +27 -0
  571. package/plugins/pbr/skills/plan/templates/planner-prompt.md.tmpl +43 -1
  572. package/plugins/pbr/skills/plan/templates/revision-prompt.md.tmpl +21 -5
  573. package/plugins/pbr/skills/profile/SKILL.md +185 -0
  574. package/plugins/pbr/skills/profile-user/SKILL.md +227 -0
  575. package/plugins/pbr/skills/quick/SKILL.md +435 -100
  576. package/plugins/pbr/skills/release/SKILL.md +206 -0
  577. package/plugins/pbr/skills/resume/SKILL.md +170 -46
  578. package/plugins/pbr/skills/review/SKILL.md +233 -165
  579. package/plugins/pbr/skills/review/templates/verifier-prompt.md.tmpl +7 -0
  580. package/plugins/pbr/skills/scan/SKILL.md +152 -106
  581. package/plugins/pbr/skills/scan/templates/mapper-prompt.md.tmpl +5 -56
  582. package/plugins/pbr/skills/seed/SKILL.md +87 -0
  583. package/plugins/pbr/skills/session-report/SKILL.md +130 -0
  584. package/plugins/pbr/skills/setup/SKILL.md +150 -202
  585. package/plugins/pbr/skills/shared/agent-context-enrichment.md +21 -0
  586. package/plugins/pbr/skills/shared/agent-type-resolution.md +32 -0
  587. package/plugins/pbr/skills/shared/commit-planning-docs.md +8 -0
  588. package/plugins/pbr/skills/shared/context-budget.md +66 -1
  589. package/plugins/pbr/skills/shared/context-loader-task.md +18 -11
  590. package/plugins/pbr/skills/shared/criterion-writing.md +58 -0
  591. package/plugins/pbr/skills/shared/digest-select.md +2 -2
  592. package/plugins/pbr/skills/shared/domain-probes.md +1 -1
  593. package/plugins/pbr/skills/shared/error-reporting.md +38 -60
  594. package/plugins/pbr/skills/shared/gate-prompts.md +4 -2
  595. package/plugins/pbr/skills/shared/memory-capture.md +48 -0
  596. package/plugins/pbr/skills/shared/phase-argument-parsing.md +4 -4
  597. package/plugins/pbr/skills/shared/revision-loop.md +24 -6
  598. package/plugins/pbr/skills/shared/state-update.md +49 -56
  599. package/plugins/pbr/skills/shared/universal-anti-patterns.md +27 -4
  600. package/plugins/pbr/skills/ship/SKILL.md +155 -0
  601. package/plugins/pbr/skills/stats/SKILL.md +110 -0
  602. package/plugins/pbr/skills/status/SKILL.md +185 -119
  603. package/plugins/pbr/skills/test/SKILL.md +254 -0
  604. package/plugins/pbr/skills/thread/SKILL.md +73 -0
  605. package/plugins/pbr/skills/todo/SKILL.md +28 -72
  606. package/plugins/pbr/skills/ui-phase/SKILL.md +180 -0
  607. package/plugins/pbr/skills/ui-review/SKILL.md +206 -0
  608. package/plugins/pbr/skills/undo/SKILL.md +221 -0
  609. package/plugins/pbr/skills/validate-phase/SKILL.md +362 -0
  610. package/plugins/pbr/templates/CONTEXT.md.tmpl +45 -20
  611. package/plugins/pbr/templates/DISCOVERY.md.tmpl +29 -0
  612. package/plugins/pbr/templates/DISCUSSION-LOG.md.tmpl +49 -0
  613. package/plugins/pbr/templates/HANDOFF.json.tmpl +30 -0
  614. package/plugins/pbr/templates/INTEGRATION-REPORT.md.tmpl +18 -2
  615. package/plugins/pbr/templates/MILESTONE-AUDIT.md.tmpl +44 -0
  616. package/plugins/pbr/templates/PROJECT.md.tmpl +126 -0
  617. package/plugins/pbr/templates/REQUIREMENTS.md.tmpl +96 -0
  618. package/plugins/pbr/templates/RETROSPECTIVE.md.tmpl +43 -0
  619. package/plugins/pbr/templates/ROADMAP.md.tmpl +108 -14
  620. package/plugins/pbr/templates/SUMMARY-complex.md.tmpl +133 -0
  621. package/plugins/pbr/templates/SUMMARY-minimal.md.tmpl +55 -0
  622. package/plugins/pbr/templates/SUMMARY.md.tmpl +21 -0
  623. package/plugins/pbr/templates/UAT.md.tmpl +94 -0
  624. package/plugins/pbr/templates/UI-SPEC.md.tmpl +144 -0
  625. package/plugins/pbr/templates/VALIDATION.md.tmpl +94 -0
  626. package/plugins/pbr/templates/VERIFICATION-DETAIL.md.tmpl +54 -13
  627. package/plugins/pbr/templates/project-CONTEXT.md.tmpl +59 -0
  628. package/plugins/pbr/templates/research-outputs/ARCHITECTURE.md.tmpl +91 -0
  629. package/plugins/pbr/templates/research-outputs/FEATURES.md.tmpl +64 -0
  630. package/plugins/pbr/templates/research-outputs/PITFALLS.md.tmpl +50 -0
  631. package/plugins/pbr/templates/research-outputs/STACK.md.tmpl +63 -0
  632. package/plugins/pbr/templates/research-outputs/SUMMARY.md.tmpl +98 -0
  633. package/scripts/build-hooks.js +61 -0
  634. package/scripts/check-ci.js +100 -0
  635. package/scripts/clean-changelog.js +364 -0
  636. package/scripts/generate-derivatives.js +581 -0
  637. package/scripts/posttest.js +93 -0
  638. package/scripts/release.js +262 -0
  639. package/scripts/run-tests.cjs +29 -0
  640. package/scripts/test-wrapper.js +43 -0
  641. package/dashboard/bin/cli.js +0 -25
  642. package/dashboard/public/css/layout.css +0 -704
  643. package/dashboard/public/css/status-colors.css +0 -98
  644. package/dashboard/public/css/tokens.css +0 -59
  645. package/dashboard/public/js/htmx-title.js +0 -5
  646. package/dashboard/public/js/sidebar-toggle.js +0 -34
  647. package/dashboard/public/js/sse-client.js +0 -100
  648. package/dashboard/public/js/theme-toggle.js +0 -46
  649. package/dashboard/src/app.js +0 -91
  650. package/dashboard/src/middleware/current-phase.js +0 -24
  651. package/dashboard/src/middleware/errorHandler.js +0 -52
  652. package/dashboard/src/middleware/notFoundHandler.js +0 -9
  653. package/dashboard/src/repositories/planning.repository.js +0 -130
  654. package/dashboard/src/routes/events.routes.js +0 -45
  655. package/dashboard/src/routes/index.routes.js +0 -35
  656. package/dashboard/src/routes/pages.routes.js +0 -426
  657. package/dashboard/src/server.js +0 -42
  658. package/dashboard/src/services/analytics.service.js +0 -141
  659. package/dashboard/src/services/dashboard.service.js +0 -309
  660. package/dashboard/src/services/milestone.service.js +0 -222
  661. package/dashboard/src/services/notes.service.js +0 -50
  662. package/dashboard/src/services/phase.service.js +0 -232
  663. package/dashboard/src/services/project.service.js +0 -57
  664. package/dashboard/src/services/roadmap.service.js +0 -258
  665. package/dashboard/src/services/sse.service.js +0 -58
  666. package/dashboard/src/services/todo.service.js +0 -272
  667. package/dashboard/src/services/watcher.service.js +0 -48
  668. package/dashboard/src/utils/cache.js +0 -55
  669. package/dashboard/src/views/analytics.ejs +0 -5
  670. package/dashboard/src/views/coming-soon.ejs +0 -11
  671. package/dashboard/src/views/dependencies.ejs +0 -5
  672. package/dashboard/src/views/error.ejs +0 -20
  673. package/dashboard/src/views/index.ejs +0 -5
  674. package/dashboard/src/views/milestone-detail.ejs +0 -5
  675. package/dashboard/src/views/milestones.ejs +0 -5
  676. package/dashboard/src/views/notes.ejs +0 -5
  677. package/dashboard/src/views/partials/analytics-content.ejs +0 -90
  678. package/dashboard/src/views/partials/breadcrumbs.ejs +0 -14
  679. package/dashboard/src/views/partials/dashboard-content.ejs +0 -84
  680. package/dashboard/src/views/partials/dependencies-content.ejs +0 -48
  681. package/dashboard/src/views/partials/empty-state.ejs +0 -7
  682. package/dashboard/src/views/partials/footer.ejs +0 -3
  683. package/dashboard/src/views/partials/head.ejs +0 -30
  684. package/dashboard/src/views/partials/header.ejs +0 -21
  685. package/dashboard/src/views/partials/layout-bottom.ejs +0 -43
  686. package/dashboard/src/views/partials/layout-top.ejs +0 -16
  687. package/dashboard/src/views/partials/milestone-detail-content.ejs +0 -20
  688. package/dashboard/src/views/partials/milestones-content.ejs +0 -88
  689. package/dashboard/src/views/partials/notes-content.ejs +0 -23
  690. package/dashboard/src/views/partials/phase-content.ejs +0 -193
  691. package/dashboard/src/views/partials/phase-doc-content.ejs +0 -38
  692. package/dashboard/src/views/partials/phases-content.ejs +0 -124
  693. package/dashboard/src/views/partials/roadmap-content.ejs +0 -180
  694. package/dashboard/src/views/partials/sidebar.ejs +0 -99
  695. package/dashboard/src/views/partials/todo-create-content.ejs +0 -54
  696. package/dashboard/src/views/partials/todo-detail-content.ejs +0 -42
  697. package/dashboard/src/views/partials/todos-content.ejs +0 -97
  698. package/dashboard/src/views/phase-detail.ejs +0 -5
  699. package/dashboard/src/views/phase-doc.ejs +0 -5
  700. package/dashboard/src/views/phases.ejs +0 -5
  701. package/dashboard/src/views/roadmap.ejs +0 -5
  702. package/dashboard/src/views/todo-create.ejs +0 -5
  703. package/dashboard/src/views/todo-detail.ejs +0 -5
  704. package/dashboard/src/views/todos.ejs +0 -5
  705. package/plugins/copilot-pbr/CHANGELOG.md +0 -19
  706. package/plugins/copilot-pbr/README.md +0 -139
  707. package/plugins/copilot-pbr/agents/audit.agent.md +0 -113
  708. package/plugins/copilot-pbr/agents/codebase-mapper.agent.md +0 -151
  709. package/plugins/copilot-pbr/agents/debugger.agent.md +0 -182
  710. package/plugins/copilot-pbr/agents/executor.agent.md +0 -267
  711. package/plugins/copilot-pbr/agents/general.agent.md +0 -88
  712. package/plugins/copilot-pbr/agents/integration-checker.agent.md +0 -119
  713. package/plugins/copilot-pbr/agents/plan-checker.agent.md +0 -208
  714. package/plugins/copilot-pbr/agents/planner.agent.md +0 -238
  715. package/plugins/copilot-pbr/agents/researcher.agent.md +0 -186
  716. package/plugins/copilot-pbr/agents/synthesizer.agent.md +0 -126
  717. package/plugins/copilot-pbr/agents/verifier.agent.md +0 -228
  718. package/plugins/copilot-pbr/hooks/hooks.json +0 -258
  719. package/plugins/copilot-pbr/plugin.json +0 -30
  720. package/plugins/copilot-pbr/references/agent-anti-patterns.md +0 -25
  721. package/plugins/copilot-pbr/references/agent-contracts.md +0 -297
  722. package/plugins/copilot-pbr/references/agent-interactions.md +0 -135
  723. package/plugins/copilot-pbr/references/agent-teams.md +0 -55
  724. package/plugins/copilot-pbr/references/checkpoints.md +0 -158
  725. package/plugins/copilot-pbr/references/common-bug-patterns.md +0 -14
  726. package/plugins/copilot-pbr/references/config-reference.md +0 -442
  727. package/plugins/copilot-pbr/references/continuation-format.md +0 -213
  728. package/plugins/copilot-pbr/references/deviation-rules.md +0 -113
  729. package/plugins/copilot-pbr/references/git-integration.md +0 -227
  730. package/plugins/copilot-pbr/references/integration-patterns.md +0 -118
  731. package/plugins/copilot-pbr/references/model-profiles.md +0 -100
  732. package/plugins/copilot-pbr/references/model-selection.md +0 -32
  733. package/plugins/copilot-pbr/references/pbr-rules.md +0 -195
  734. package/plugins/copilot-pbr/references/pbr-tools-cli.md +0 -285
  735. package/plugins/copilot-pbr/references/plan-authoring.md +0 -182
  736. package/plugins/copilot-pbr/references/plan-format.md +0 -288
  737. package/plugins/copilot-pbr/references/planning-config.md +0 -214
  738. package/plugins/copilot-pbr/references/questioning.md +0 -215
  739. package/plugins/copilot-pbr/references/reading-verification.md +0 -128
  740. package/plugins/copilot-pbr/references/stub-patterns.md +0 -161
  741. package/plugins/copilot-pbr/references/subagent-coordination.md +0 -120
  742. package/plugins/copilot-pbr/references/ui-formatting.md +0 -444
  743. package/plugins/copilot-pbr/references/verification-patterns.md +0 -199
  744. package/plugins/copilot-pbr/references/wave-execution.md +0 -96
  745. package/plugins/copilot-pbr/rules/pbr-workflow.mdc +0 -48
  746. package/plugins/copilot-pbr/setup.ps1 +0 -93
  747. package/plugins/copilot-pbr/setup.sh +0 -93
  748. package/plugins/copilot-pbr/skills/audit/SKILL.md +0 -330
  749. package/plugins/copilot-pbr/skills/begin/SKILL.md +0 -589
  750. package/plugins/copilot-pbr/skills/begin/templates/PROJECT.md.tmpl +0 -34
  751. package/plugins/copilot-pbr/skills/begin/templates/REQUIREMENTS.md.tmpl +0 -19
  752. package/plugins/copilot-pbr/skills/begin/templates/STATE.md.tmpl +0 -50
  753. package/plugins/copilot-pbr/skills/begin/templates/config.json.tmpl +0 -64
  754. package/plugins/copilot-pbr/skills/begin/templates/researcher-prompt.md.tmpl +0 -20
  755. package/plugins/copilot-pbr/skills/begin/templates/roadmap-prompt.md.tmpl +0 -31
  756. package/plugins/copilot-pbr/skills/begin/templates/synthesis-prompt.md.tmpl +0 -17
  757. package/plugins/copilot-pbr/skills/build/SKILL.md +0 -960
  758. package/plugins/copilot-pbr/skills/config/SKILL.md +0 -250
  759. package/plugins/copilot-pbr/skills/continue/SKILL.md +0 -159
  760. package/plugins/copilot-pbr/skills/dashboard/SKILL.md +0 -43
  761. package/plugins/copilot-pbr/skills/debug/SKILL.md +0 -508
  762. package/plugins/copilot-pbr/skills/debug/templates/continuation-prompt.md.tmpl +0 -17
  763. package/plugins/copilot-pbr/skills/debug/templates/initial-investigation-prompt.md.tmpl +0 -28
  764. package/plugins/copilot-pbr/skills/discuss/SKILL.md +0 -353
  765. package/plugins/copilot-pbr/skills/discuss/templates/CONTEXT.md.tmpl +0 -62
  766. package/plugins/copilot-pbr/skills/discuss/templates/decision-categories.md +0 -10
  767. package/plugins/copilot-pbr/skills/do/SKILL.md +0 -66
  768. package/plugins/copilot-pbr/skills/explore/SKILL.md +0 -373
  769. package/plugins/copilot-pbr/skills/health/SKILL.md +0 -283
  770. package/plugins/copilot-pbr/skills/health/templates/check-pattern.md.tmpl +0 -31
  771. package/plugins/copilot-pbr/skills/health/templates/output-format.md.tmpl +0 -64
  772. package/plugins/copilot-pbr/skills/help/SKILL.md +0 -193
  773. package/plugins/copilot-pbr/skills/import/SKILL.md +0 -502
  774. package/plugins/copilot-pbr/skills/milestone/SKILL.md +0 -806
  775. package/plugins/copilot-pbr/skills/milestone/templates/audit-report.md.tmpl +0 -49
  776. package/plugins/copilot-pbr/skills/milestone/templates/stats-file.md.tmpl +0 -31
  777. package/plugins/copilot-pbr/skills/note/SKILL.md +0 -213
  778. package/plugins/copilot-pbr/skills/pause/SKILL.md +0 -247
  779. package/plugins/copilot-pbr/skills/pause/templates/continue-here.md.tmpl +0 -72
  780. package/plugins/copilot-pbr/skills/plan/SKILL.md +0 -662
  781. package/plugins/copilot-pbr/skills/plan/templates/checker-prompt.md.tmpl +0 -22
  782. package/plugins/copilot-pbr/skills/plan/templates/gap-closure-prompt.md.tmpl +0 -33
  783. package/plugins/copilot-pbr/skills/plan/templates/planner-prompt.md.tmpl +0 -39
  784. package/plugins/copilot-pbr/skills/plan/templates/researcher-prompt.md.tmpl +0 -20
  785. package/plugins/copilot-pbr/skills/plan/templates/revision-prompt.md.tmpl +0 -24
  786. package/plugins/copilot-pbr/skills/quick/SKILL.md +0 -376
  787. package/plugins/copilot-pbr/skills/resume/SKILL.md +0 -399
  788. package/plugins/copilot-pbr/skills/review/SKILL.md +0 -653
  789. package/plugins/copilot-pbr/skills/review/templates/debugger-prompt.md.tmpl +0 -61
  790. package/plugins/copilot-pbr/skills/review/templates/gap-planner-prompt.md.tmpl +0 -41
  791. package/plugins/copilot-pbr/skills/review/templates/verifier-prompt.md.tmpl +0 -116
  792. package/plugins/copilot-pbr/skills/scan/SKILL.md +0 -299
  793. package/plugins/copilot-pbr/skills/scan/templates/mapper-prompt.md.tmpl +0 -202
  794. package/plugins/copilot-pbr/skills/setup/SKILL.md +0 -296
  795. package/plugins/copilot-pbr/skills/shared/commit-planning-docs.md +0 -36
  796. package/plugins/copilot-pbr/skills/shared/config-loading.md +0 -103
  797. package/plugins/copilot-pbr/skills/shared/context-budget.md +0 -41
  798. package/plugins/copilot-pbr/skills/shared/context-loader-task.md +0 -87
  799. package/plugins/copilot-pbr/skills/shared/digest-select.md +0 -80
  800. package/plugins/copilot-pbr/skills/shared/domain-probes.md +0 -126
  801. package/plugins/copilot-pbr/skills/shared/error-recovery-strategies.md +0 -51
  802. package/plugins/copilot-pbr/skills/shared/error-reporting.md +0 -81
  803. package/plugins/copilot-pbr/skills/shared/gate-prompts.md +0 -389
  804. package/plugins/copilot-pbr/skills/shared/phase-argument-parsing.md +0 -46
  805. package/plugins/copilot-pbr/skills/shared/progress-display.md +0 -53
  806. package/plugins/copilot-pbr/skills/shared/revision-loop.md +0 -82
  807. package/plugins/copilot-pbr/skills/shared/state-loading.md +0 -63
  808. package/plugins/copilot-pbr/skills/shared/state-update.md +0 -162
  809. package/plugins/copilot-pbr/skills/shared/universal-anti-patterns.md +0 -38
  810. package/plugins/copilot-pbr/skills/status/SKILL.md +0 -362
  811. package/plugins/copilot-pbr/skills/statusline/SKILL.md +0 -149
  812. package/plugins/copilot-pbr/skills/todo/SKILL.md +0 -279
  813. package/plugins/copilot-pbr/templates/CONTEXT.md.tmpl +0 -53
  814. package/plugins/copilot-pbr/templates/INTEGRATION-REPORT.md.tmpl +0 -152
  815. package/plugins/copilot-pbr/templates/RESEARCH-SUMMARY.md.tmpl +0 -98
  816. package/plugins/copilot-pbr/templates/ROADMAP.md.tmpl +0 -41
  817. package/plugins/copilot-pbr/templates/SUMMARY.md.tmpl +0 -82
  818. package/plugins/copilot-pbr/templates/VERIFICATION-DETAIL.md.tmpl +0 -117
  819. package/plugins/copilot-pbr/templates/codebase/ARCHITECTURE.md.tmpl +0 -98
  820. package/plugins/copilot-pbr/templates/codebase/CONCERNS.md.tmpl +0 -93
  821. package/plugins/copilot-pbr/templates/codebase/CONVENTIONS.md.tmpl +0 -104
  822. package/plugins/copilot-pbr/templates/codebase/INTEGRATIONS.md.tmpl +0 -78
  823. package/plugins/copilot-pbr/templates/codebase/STACK.md.tmpl +0 -78
  824. package/plugins/copilot-pbr/templates/codebase/STRUCTURE.md.tmpl +0 -80
  825. package/plugins/copilot-pbr/templates/codebase/TESTING.md.tmpl +0 -107
  826. package/plugins/copilot-pbr/templates/continue-here.md.tmpl +0 -74
  827. package/plugins/copilot-pbr/templates/prompt-partials/phase-project-context.md.tmpl +0 -38
  828. package/plugins/copilot-pbr/templates/research/ARCHITECTURE.md.tmpl +0 -124
  829. package/plugins/copilot-pbr/templates/research/STACK.md.tmpl +0 -71
  830. package/plugins/copilot-pbr/templates/research/SUMMARY.md.tmpl +0 -112
  831. package/plugins/copilot-pbr/templates/research-outputs/phase-research.md.tmpl +0 -81
  832. package/plugins/copilot-pbr/templates/research-outputs/project-research.md.tmpl +0 -99
  833. package/plugins/copilot-pbr/templates/research-outputs/synthesis.md.tmpl +0 -36
  834. package/plugins/cursor-pbr/.cursor-plugin/plugin.json +0 -32
  835. package/plugins/cursor-pbr/CHANGELOG.md +0 -15
  836. package/plugins/cursor-pbr/README.md +0 -123
  837. package/plugins/cursor-pbr/agents/audit.md +0 -178
  838. package/plugins/cursor-pbr/agents/codebase-mapper.md +0 -150
  839. package/plugins/cursor-pbr/agents/debugger.md +0 -181
  840. package/plugins/cursor-pbr/agents/executor.md +0 -266
  841. package/plugins/cursor-pbr/agents/general.md +0 -87
  842. package/plugins/cursor-pbr/agents/integration-checker.md +0 -118
  843. package/plugins/cursor-pbr/agents/plan-checker.md +0 -207
  844. package/plugins/cursor-pbr/agents/planner.md +0 -237
  845. package/plugins/cursor-pbr/agents/researcher.md +0 -185
  846. package/plugins/cursor-pbr/agents/synthesizer.md +0 -125
  847. package/plugins/cursor-pbr/agents/verifier.md +0 -227
  848. package/plugins/cursor-pbr/assets/.gitkeep +0 -0
  849. package/plugins/cursor-pbr/assets/logo.svg +0 -21
  850. package/plugins/cursor-pbr/hooks/hooks.json +0 -224
  851. package/plugins/cursor-pbr/references/agent-anti-patterns.md +0 -25
  852. package/plugins/cursor-pbr/references/agent-contracts.md +0 -297
  853. package/plugins/cursor-pbr/references/agent-interactions.md +0 -135
  854. package/plugins/cursor-pbr/references/agent-teams.md +0 -55
  855. package/plugins/cursor-pbr/references/checkpoints.md +0 -158
  856. package/plugins/cursor-pbr/references/common-bug-patterns.md +0 -14
  857. package/plugins/cursor-pbr/references/config-reference.md +0 -442
  858. package/plugins/cursor-pbr/references/continuation-format.md +0 -213
  859. package/plugins/cursor-pbr/references/deviation-rules.md +0 -113
  860. package/plugins/cursor-pbr/references/git-integration.md +0 -227
  861. package/plugins/cursor-pbr/references/integration-patterns.md +0 -118
  862. package/plugins/cursor-pbr/references/model-profiles.md +0 -100
  863. package/plugins/cursor-pbr/references/model-selection.md +0 -32
  864. package/plugins/cursor-pbr/references/pbr-rules.md +0 -195
  865. package/plugins/cursor-pbr/references/pbr-tools-cli.md +0 -285
  866. package/plugins/cursor-pbr/references/plan-authoring.md +0 -182
  867. package/plugins/cursor-pbr/references/plan-format.md +0 -288
  868. package/plugins/cursor-pbr/references/planning-config.md +0 -214
  869. package/plugins/cursor-pbr/references/questioning.md +0 -215
  870. package/plugins/cursor-pbr/references/reading-verification.md +0 -128
  871. package/plugins/cursor-pbr/references/stub-patterns.md +0 -161
  872. package/plugins/cursor-pbr/references/subagent-coordination.md +0 -120
  873. package/plugins/cursor-pbr/references/ui-formatting.md +0 -444
  874. package/plugins/cursor-pbr/references/verification-patterns.md +0 -199
  875. package/plugins/cursor-pbr/references/wave-execution.md +0 -96
  876. package/plugins/cursor-pbr/rules/pbr-workflow.mdc +0 -48
  877. package/plugins/cursor-pbr/setup.ps1 +0 -78
  878. package/plugins/cursor-pbr/setup.sh +0 -83
  879. package/plugins/cursor-pbr/skills/audit/SKILL.md +0 -331
  880. package/plugins/cursor-pbr/skills/begin/SKILL.md +0 -589
  881. package/plugins/cursor-pbr/skills/begin/templates/PROJECT.md.tmpl +0 -34
  882. package/plugins/cursor-pbr/skills/begin/templates/REQUIREMENTS.md.tmpl +0 -19
  883. package/plugins/cursor-pbr/skills/begin/templates/STATE.md.tmpl +0 -50
  884. package/plugins/cursor-pbr/skills/begin/templates/config.json.tmpl +0 -64
  885. package/plugins/cursor-pbr/skills/begin/templates/researcher-prompt.md.tmpl +0 -20
  886. package/plugins/cursor-pbr/skills/begin/templates/roadmap-prompt.md.tmpl +0 -31
  887. package/plugins/cursor-pbr/skills/begin/templates/synthesis-prompt.md.tmpl +0 -17
  888. package/plugins/cursor-pbr/skills/build/SKILL.md +0 -961
  889. package/plugins/cursor-pbr/skills/config/SKILL.md +0 -252
  890. package/plugins/cursor-pbr/skills/continue/SKILL.md +0 -159
  891. package/plugins/cursor-pbr/skills/dashboard/SKILL.md +0 -44
  892. package/plugins/cursor-pbr/skills/debug/SKILL.md +0 -512
  893. package/plugins/cursor-pbr/skills/debug/templates/continuation-prompt.md.tmpl +0 -17
  894. package/plugins/cursor-pbr/skills/debug/templates/initial-investigation-prompt.md.tmpl +0 -28
  895. package/plugins/cursor-pbr/skills/discuss/SKILL.md +0 -354
  896. package/plugins/cursor-pbr/skills/discuss/templates/CONTEXT.md.tmpl +0 -62
  897. package/plugins/cursor-pbr/skills/discuss/templates/decision-categories.md +0 -10
  898. package/plugins/cursor-pbr/skills/do/SKILL.md +0 -67
  899. package/plugins/cursor-pbr/skills/explore/SKILL.md +0 -376
  900. package/plugins/cursor-pbr/skills/health/SKILL.md +0 -283
  901. package/plugins/cursor-pbr/skills/health/templates/check-pattern.md.tmpl +0 -31
  902. package/plugins/cursor-pbr/skills/health/templates/output-format.md.tmpl +0 -64
  903. package/plugins/cursor-pbr/skills/help/SKILL.md +0 -193
  904. package/plugins/cursor-pbr/skills/import/SKILL.md +0 -505
  905. package/plugins/cursor-pbr/skills/milestone/SKILL.md +0 -807
  906. package/plugins/cursor-pbr/skills/milestone/templates/audit-report.md.tmpl +0 -49
  907. package/plugins/cursor-pbr/skills/milestone/templates/stats-file.md.tmpl +0 -31
  908. package/plugins/cursor-pbr/skills/note/SKILL.md +0 -214
  909. package/plugins/cursor-pbr/skills/pause/SKILL.md +0 -248
  910. package/plugins/cursor-pbr/skills/pause/templates/continue-here.md.tmpl +0 -72
  911. package/plugins/cursor-pbr/skills/plan/SKILL.md +0 -663
  912. package/plugins/cursor-pbr/skills/plan/templates/checker-prompt.md.tmpl +0 -22
  913. package/plugins/cursor-pbr/skills/plan/templates/gap-closure-prompt.md.tmpl +0 -33
  914. package/plugins/cursor-pbr/skills/plan/templates/planner-prompt.md.tmpl +0 -39
  915. package/plugins/cursor-pbr/skills/plan/templates/researcher-prompt.md.tmpl +0 -20
  916. package/plugins/cursor-pbr/skills/plan/templates/revision-prompt.md.tmpl +0 -24
  917. package/plugins/cursor-pbr/skills/quick/SKILL.md +0 -376
  918. package/plugins/cursor-pbr/skills/resume/SKILL.md +0 -399
  919. package/plugins/cursor-pbr/skills/review/SKILL.md +0 -654
  920. package/plugins/cursor-pbr/skills/review/templates/debugger-prompt.md.tmpl +0 -61
  921. package/plugins/cursor-pbr/skills/review/templates/gap-planner-prompt.md.tmpl +0 -41
  922. package/plugins/cursor-pbr/skills/review/templates/verifier-prompt.md.tmpl +0 -116
  923. package/plugins/cursor-pbr/skills/scan/SKILL.md +0 -300
  924. package/plugins/cursor-pbr/skills/scan/templates/mapper-prompt.md.tmpl +0 -202
  925. package/plugins/cursor-pbr/skills/setup/SKILL.md +0 -296
  926. package/plugins/cursor-pbr/skills/shared/commit-planning-docs.md +0 -36
  927. package/plugins/cursor-pbr/skills/shared/config-loading.md +0 -103
  928. package/plugins/cursor-pbr/skills/shared/context-budget.md +0 -41
  929. package/plugins/cursor-pbr/skills/shared/context-loader-task.md +0 -87
  930. package/plugins/cursor-pbr/skills/shared/digest-select.md +0 -80
  931. package/plugins/cursor-pbr/skills/shared/domain-probes.md +0 -126
  932. package/plugins/cursor-pbr/skills/shared/error-recovery-strategies.md +0 -51
  933. package/plugins/cursor-pbr/skills/shared/error-reporting.md +0 -81
  934. package/plugins/cursor-pbr/skills/shared/gate-prompts.md +0 -389
  935. package/plugins/cursor-pbr/skills/shared/phase-argument-parsing.md +0 -46
  936. package/plugins/cursor-pbr/skills/shared/progress-display.md +0 -53
  937. package/plugins/cursor-pbr/skills/shared/revision-loop.md +0 -82
  938. package/plugins/cursor-pbr/skills/shared/state-loading.md +0 -63
  939. package/plugins/cursor-pbr/skills/shared/state-update.md +0 -162
  940. package/plugins/cursor-pbr/skills/shared/universal-anti-patterns.md +0 -38
  941. package/plugins/cursor-pbr/skills/status/SKILL.md +0 -362
  942. package/plugins/cursor-pbr/skills/statusline/SKILL.md +0 -150
  943. package/plugins/cursor-pbr/skills/todo/SKILL.md +0 -280
  944. package/plugins/cursor-pbr/templates/CONTEXT.md.tmpl +0 -53
  945. package/plugins/cursor-pbr/templates/INTEGRATION-REPORT.md.tmpl +0 -152
  946. package/plugins/cursor-pbr/templates/RESEARCH-SUMMARY.md.tmpl +0 -98
  947. package/plugins/cursor-pbr/templates/ROADMAP.md.tmpl +0 -41
  948. package/plugins/cursor-pbr/templates/SUMMARY.md.tmpl +0 -82
  949. package/plugins/cursor-pbr/templates/VERIFICATION-DETAIL.md.tmpl +0 -117
  950. package/plugins/cursor-pbr/templates/codebase/ARCHITECTURE.md.tmpl +0 -98
  951. package/plugins/cursor-pbr/templates/codebase/CONCERNS.md.tmpl +0 -93
  952. package/plugins/cursor-pbr/templates/codebase/CONVENTIONS.md.tmpl +0 -104
  953. package/plugins/cursor-pbr/templates/codebase/INTEGRATIONS.md.tmpl +0 -78
  954. package/plugins/cursor-pbr/templates/codebase/STACK.md.tmpl +0 -78
  955. package/plugins/cursor-pbr/templates/codebase/STRUCTURE.md.tmpl +0 -80
  956. package/plugins/cursor-pbr/templates/codebase/TESTING.md.tmpl +0 -107
  957. package/plugins/cursor-pbr/templates/continue-here.md.tmpl +0 -74
  958. package/plugins/cursor-pbr/templates/prompt-partials/phase-project-context.md.tmpl +0 -38
  959. package/plugins/cursor-pbr/templates/research/ARCHITECTURE.md.tmpl +0 -124
  960. package/plugins/cursor-pbr/templates/research/STACK.md.tmpl +0 -71
  961. package/plugins/cursor-pbr/templates/research/SUMMARY.md.tmpl +0 -112
  962. package/plugins/cursor-pbr/templates/research-outputs/phase-research.md.tmpl +0 -81
  963. package/plugins/cursor-pbr/templates/research-outputs/project-research.md.tmpl +0 -99
  964. package/plugins/cursor-pbr/templates/research-outputs/synthesis.md.tmpl +0 -36
  965. package/plugins/pbr/references/agent-interactions.md +0 -134
  966. package/plugins/pbr/references/pbr-rules.md +0 -194
  967. package/plugins/pbr/references/pbr-tools-cli.md +0 -285
  968. package/plugins/pbr/references/planning-config.md +0 -213
  969. package/plugins/pbr/references/subagent-coordination.md +0 -119
  970. package/plugins/pbr/references/ui-formatting.md +0 -444
  971. package/plugins/pbr/scripts/validate-plugin-structure.js +0 -183
  972. package/plugins/pbr/skills/milestone/templates/audit-report.md.tmpl +0 -48
  973. package/plugins/pbr/skills/shared/error-recovery-strategies.md +0 -51
  974. package/plugins/pbr/skills/shared/progress-display.md +0 -53
  975. package/plugins/pbr/skills/shared/state-loading.md +0 -62
  976. package/plugins/pbr/templates/RESEARCH-SUMMARY.md.tmpl +0 -97
  977. package/plugins/pbr/templates/research/ARCHITECTURE.md.tmpl +0 -124
  978. package/plugins/pbr/templates/research/STACK.md.tmpl +0 -71
  979. package/plugins/pbr/templates/research/SUMMARY.md.tmpl +0 -112
  980. package/plugins/pbr/templates/research-outputs/phase-research.md.tmpl +0 -81
  981. package/plugins/pbr/templates/research-outputs/project-research.md.tmpl +0 -99
  982. package/plugins/pbr/templates/research-outputs/synthesis.md.tmpl +0 -36
  983. /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
+ };