@sienklogic/plan-build-run 2.12.0 → 2.12.1

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 (1222) hide show
  1. package/CHANGELOG.md +1102 -234
  2. package/CLAUDE.md +54 -34
  3. package/LICENSE +2 -1
  4. package/README.md +261 -182
  5. package/agents/pbr-audit.md +266 -0
  6. package/agents/pbr-codebase-mapper.md +236 -0
  7. package/agents/pbr-debugger.md +312 -0
  8. package/agents/pbr-dev-sync.md +220 -0
  9. package/agents/pbr-executor.md +591 -0
  10. package/agents/pbr-general.md +191 -0
  11. package/agents/pbr-integration-checker.md +237 -0
  12. package/agents/pbr-intel-updater.md +296 -0
  13. package/agents/pbr-nyquist-auditor.md +252 -0
  14. package/agents/pbr-plan-checker.md +312 -0
  15. package/agents/pbr-planner.md +539 -0
  16. package/agents/pbr-researcher.md +314 -0
  17. package/agents/pbr-roadmapper.md +346 -0
  18. package/agents/pbr-synthesizer.md +271 -0
  19. package/agents/pbr-ui-checker.md +202 -0
  20. package/agents/pbr-ui-researcher.md +223 -0
  21. package/agents/pbr-verifier.md +495 -0
  22. package/bin/install.js +2752 -0
  23. package/commands/pbr/add-phase.md +75 -0
  24. package/commands/pbr/add-todo.md +8 -0
  25. package/commands/pbr/audit-milestone.md +8 -0
  26. package/commands/pbr/audit.md +5 -0
  27. package/commands/pbr/autonomous.md +5 -0
  28. package/commands/pbr/begin.md +5 -0
  29. package/commands/pbr/build.md +5 -0
  30. package/commands/pbr/check-todos.md +8 -0
  31. package/commands/pbr/complete-milestone.md +8 -0
  32. package/commands/pbr/config.md +5 -0
  33. package/commands/pbr/continue.md +5 -0
  34. package/commands/pbr/dashboard.md +5 -0
  35. package/commands/pbr/debug.md +5 -0
  36. package/commands/pbr/discuss-phase.md +6 -0
  37. package/commands/pbr/discuss.md +5 -0
  38. package/commands/pbr/do.md +5 -0
  39. package/commands/pbr/execute-phase.md +6 -0
  40. package/commands/pbr/explore.md +5 -0
  41. package/commands/pbr/health.md +5 -0
  42. package/commands/pbr/help.md +5 -0
  43. package/commands/pbr/import.md +5 -0
  44. package/commands/pbr/insert-phase.md +65 -0
  45. package/commands/pbr/intel.md +5 -0
  46. package/commands/pbr/join-discord.md +11 -0
  47. package/commands/pbr/list-phase-assumptions.md +69 -0
  48. package/commands/pbr/map-codebase.md +6 -0
  49. package/commands/pbr/milestone.md +5 -0
  50. package/commands/pbr/new-milestone.md +8 -0
  51. package/commands/pbr/new-project.md +6 -0
  52. package/commands/pbr/note.md +5 -0
  53. package/commands/pbr/pause-work.md +5 -0
  54. package/commands/pbr/pause.md +5 -0
  55. package/commands/pbr/plan-milestone-gaps.md +7 -0
  56. package/commands/pbr/plan-phase.md +6 -0
  57. package/commands/pbr/plan.md +5 -0
  58. package/commands/pbr/profile-user.md +5 -0
  59. package/commands/pbr/profile.md +5 -0
  60. package/commands/pbr/progress.md +6 -0
  61. package/commands/pbr/quick.md +5 -0
  62. package/commands/pbr/reapply-patches.md +47 -0
  63. package/commands/pbr/release.md +6 -0
  64. package/commands/pbr/remove-phase.md +66 -0
  65. package/commands/pbr/research-phase.md +59 -0
  66. package/commands/pbr/resume-work.md +5 -0
  67. package/commands/pbr/resume.md +5 -0
  68. package/commands/pbr/review.md +5 -0
  69. package/commands/pbr/scan.md +5 -0
  70. package/commands/pbr/session-report.md +5 -0
  71. package/commands/pbr/set-profile.md +6 -0
  72. package/commands/pbr/settings.md +5 -0
  73. package/commands/pbr/setup.md +5 -0
  74. package/commands/pbr/ship.md +5 -0
  75. package/commands/pbr/status.md +5 -0
  76. package/commands/pbr/statusline.md +5 -0
  77. package/commands/pbr/test.md +5 -0
  78. package/commands/pbr/todo.md +5 -0
  79. package/commands/pbr/ui-phase.md +5 -0
  80. package/commands/pbr/ui-review.md +5 -0
  81. package/commands/pbr/undo.md +5 -0
  82. package/commands/pbr/update.md +37 -0
  83. package/commands/pbr/validate-phase.md +5 -0
  84. package/commands/pbr/verify-work.md +6 -0
  85. package/dashboard/bin/cli.cjs +96 -0
  86. package/dashboard/bin/stop.cjs +129 -0
  87. package/dashboard/eslint.config.js +37 -0
  88. package/dashboard/index.html +20 -0
  89. package/dashboard/package.json +28 -23
  90. package/dashboard/server/index.js +136 -0
  91. package/dashboard/server/lib/frontmatter.js +92 -0
  92. package/dashboard/server/middleware/static.js +35 -0
  93. package/dashboard/server/package.json +16 -0
  94. package/dashboard/server/routes/agents.js +213 -0
  95. package/dashboard/server/routes/config.js +64 -0
  96. package/dashboard/server/routes/health.js +95 -0
  97. package/dashboard/server/routes/memory.js +107 -0
  98. package/dashboard/server/routes/planning.js +234 -0
  99. package/dashboard/server/routes/progress.js +77 -0
  100. package/dashboard/server/routes/projects.js +36 -0
  101. package/dashboard/server/routes/requirements.js +40 -0
  102. package/dashboard/server/routes/roadmap.js +69 -0
  103. package/dashboard/server/routes/status.js +25 -0
  104. package/dashboard/server/routes/telemetry.js +171 -0
  105. package/dashboard/server/services/file-watcher.js +105 -0
  106. package/dashboard/server/services/planning-reader.js +741 -0
  107. package/dashboard/server/test/cli.test.js +34 -0
  108. package/dashboard/server/test/frontmatter.test.js +104 -0
  109. package/dashboard/server/test/isolation.test.js +32 -0
  110. package/dashboard/server/test/planning-reader.test.js +151 -0
  111. package/dashboard/server/test/routes.test.js +91 -0
  112. package/dashboard/server/test/ws.test.js +81 -0
  113. package/dashboard/server/ws.js +96 -0
  114. package/dashboard/src/App.jsx +154 -0
  115. package/dashboard/src/components/charts/BudgetBars.jsx +42 -0
  116. package/dashboard/src/components/charts/ContextRadar.jsx +34 -0
  117. package/dashboard/src/components/charts/PhaseDonut.jsx +66 -0
  118. package/dashboard/src/components/charts/SuccessTrend.jsx +45 -0
  119. package/dashboard/src/components/charts/TokenChart.jsx +55 -0
  120. package/dashboard/src/components/charts/index.js +5 -0
  121. package/dashboard/src/components/config/CfgSection.jsx +93 -0
  122. package/dashboard/src/components/layout/Header.jsx +89 -0
  123. package/dashboard/src/components/layout/ProjectSwitcher.jsx +160 -0
  124. package/dashboard/src/components/layout/Sidebar.jsx +161 -0
  125. package/dashboard/src/components/ui/AutoModeBanner.jsx +138 -0
  126. package/dashboard/src/components/ui/BackButton.jsx +27 -0
  127. package/dashboard/src/components/ui/Badge.jsx +27 -0
  128. package/dashboard/src/components/ui/Card.jsx +23 -0
  129. package/dashboard/src/components/ui/ChartTooltip.jsx +48 -0
  130. package/dashboard/src/components/ui/CheckpointBox.jsx +110 -0
  131. package/dashboard/src/components/ui/CodeBlock.jsx +27 -0
  132. package/dashboard/src/components/ui/ConfidenceBadge.jsx +20 -0
  133. package/dashboard/src/components/ui/ConfirmModal.jsx +161 -0
  134. package/dashboard/src/components/ui/ConnectionBanner.jsx +60 -0
  135. package/dashboard/src/components/ui/ErrorBoundary.jsx +106 -0
  136. package/dashboard/src/components/ui/ErrorBox.jsx +107 -0
  137. package/dashboard/src/components/ui/KeyValue.jsx +33 -0
  138. package/dashboard/src/components/ui/LoadingSkeleton.jsx +84 -0
  139. package/dashboard/src/components/ui/MetricCard.jsx +58 -0
  140. package/dashboard/src/components/ui/NextUpBlock.jsx +92 -0
  141. package/dashboard/src/components/ui/NumberInput.jsx +44 -0
  142. package/dashboard/src/components/ui/PBRBanner.jsx +47 -0
  143. package/dashboard/src/components/ui/PipelineView.jsx +130 -0
  144. package/dashboard/src/components/ui/ProgressBar.jsx +28 -0
  145. package/dashboard/src/components/ui/ProgressDisplay.jsx +47 -0
  146. package/dashboard/src/components/ui/QualityGateBadge.jsx +15 -0
  147. package/dashboard/src/components/ui/SectionTitle.jsx +35 -0
  148. package/dashboard/src/components/ui/SelectInput.jsx +45 -0
  149. package/dashboard/src/components/ui/StatusDot.jsx +51 -0
  150. package/dashboard/src/components/ui/StatusSymbol.jsx +49 -0
  151. package/dashboard/src/components/ui/TabBar.jsx +41 -0
  152. package/dashboard/src/components/ui/TextInput.jsx +42 -0
  153. package/dashboard/src/components/ui/Toast.jsx +117 -0
  154. package/dashboard/src/components/ui/Toggle.jsx +70 -0
  155. package/dashboard/src/components/ui/index.js +29 -0
  156. package/dashboard/src/hooks/useDocumentTitle.js +16 -0
  157. package/dashboard/src/hooks/useFetch.js +50 -0
  158. package/dashboard/src/hooks/useToast.jsx +43 -0
  159. package/dashboard/src/hooks/useWebSocket.js +103 -0
  160. package/dashboard/src/lib/api.js +112 -0
  161. package/dashboard/src/lib/configSchema.js +189 -0
  162. package/dashboard/src/lib/constants.js +18 -0
  163. package/dashboard/src/main.jsx +15 -0
  164. package/dashboard/src/pages/AgentsPage.jsx +191 -0
  165. package/dashboard/src/pages/ConfigPage.jsx +298 -0
  166. package/dashboard/src/pages/HooksPage.jsx +412 -0
  167. package/dashboard/src/pages/LiveFeed.jsx +274 -0
  168. package/dashboard/src/pages/MemoryPage.jsx +107 -0
  169. package/dashboard/src/pages/OnboardingPage.jsx +117 -0
  170. package/dashboard/src/pages/Overview.jsx +360 -0
  171. package/dashboard/src/pages/PhaseDetailView.jsx +216 -0
  172. package/dashboard/src/pages/PlanningPage.jsx +181 -0
  173. package/dashboard/src/pages/ProgressPage.jsx +249 -0
  174. package/dashboard/src/pages/RoadmapPage.jsx +251 -0
  175. package/dashboard/src/pages/Telemetry.jsx +113 -0
  176. package/dashboard/src/pages/planning/DecisionsTab.jsx +153 -0
  177. package/dashboard/src/pages/planning/FilesTab.jsx +420 -0
  178. package/dashboard/src/pages/planning/MilestoneDetail.jsx +319 -0
  179. package/dashboard/src/pages/planning/MilestonesTab.jsx +151 -0
  180. package/dashboard/src/pages/planning/NotesTab.jsx +251 -0
  181. package/dashboard/src/pages/planning/PhasesTab.jsx +218 -0
  182. package/dashboard/src/pages/planning/QuickTab.jsx +50 -0
  183. package/dashboard/src/pages/planning/ResearchTab.jsx +103 -0
  184. package/dashboard/src/pages/planning/TodosTab.jsx +297 -0
  185. package/dashboard/src/theme/ThemeProvider.jsx +38 -0
  186. package/dashboard/src/theme/tokens.js +17 -0
  187. package/dashboard/tests/components/ConfirmModal.test.jsx +179 -0
  188. package/dashboard/tests/components/ConnectionBanner.test.jsx +37 -0
  189. package/dashboard/tests/components/ErrorBoundary.test.jsx +59 -0
  190. package/dashboard/tests/components/LoadingSkeleton.test.jsx +46 -0
  191. package/dashboard/tests/components/ToastContainer.test.jsx +47 -0
  192. package/dashboard/tests/components/Toggle.test.jsx +61 -0
  193. package/dashboard/tests/hooks/useFetch.test.jsx +77 -0
  194. package/dashboard/tests/hooks/useToast.test.jsx +78 -0
  195. package/dashboard/tests/hooks/useWebSocket.test.jsx +128 -0
  196. package/dashboard/tests/pages/ConfigPage.test.jsx +199 -0
  197. package/dashboard/tests/pages/PlanningPage.test.jsx +119 -0
  198. package/dashboard/tests/pages/planning/FilesTab.test.jsx +198 -0
  199. package/dashboard/tests/pages/planning/NotesTab.test.jsx +178 -0
  200. package/dashboard/tests/pages/planning/TodosTab.test.jsx +188 -0
  201. package/dashboard/tests/performance.test.jsx +46 -0
  202. package/dashboard/tests/routes/config.test.js +98 -0
  203. package/dashboard/tests/routes/health.test.js +40 -0
  204. package/dashboard/tests/routes/planning.test.js +112 -0
  205. package/dashboard/tests/routes/roadmap.test.js +91 -0
  206. package/dashboard/tests/routes/status.test.js +131 -0
  207. package/dashboard/tests/server/planning-reader.test.js +153 -0
  208. package/dashboard/tests/setup.js +7 -0
  209. package/dashboard/vite.config.js +41 -0
  210. package/hooks/dist/auto-continue.js +277 -0
  211. package/hooks/dist/block-skill-self-read.js +80 -0
  212. package/hooks/dist/check-agent-state-write.js +63 -0
  213. package/hooks/dist/check-config-change.js +188 -0
  214. package/hooks/dist/check-dangerous-commands.js +185 -0
  215. package/hooks/dist/check-doc-sprawl.js +102 -0
  216. package/hooks/dist/check-phase-boundary.js +191 -0
  217. package/hooks/dist/check-plan-format.js +227 -0
  218. package/hooks/dist/check-roadmap-sync.js +503 -0
  219. package/hooks/dist/check-skill-workflow.js +354 -0
  220. package/hooks/dist/check-state-sync.js +637 -0
  221. package/hooks/dist/check-subagent-output.js +401 -0
  222. package/hooks/dist/check-summary-gate.js +199 -0
  223. package/hooks/dist/context-bridge.js +406 -0
  224. package/hooks/dist/context-budget-check.js +442 -0
  225. package/hooks/dist/context-quality.js +271 -0
  226. package/hooks/dist/enforce-pbr-workflow.js +277 -0
  227. package/hooks/dist/event-handler.js +203 -0
  228. package/hooks/dist/event-logger.js +125 -0
  229. package/hooks/dist/hook-logger.js +112 -0
  230. package/hooks/dist/hook-server-client.js +342 -0
  231. package/hooks/dist/hook-server.js +352 -0
  232. package/hooks/dist/hooks-schema.json +85 -0
  233. package/hooks/dist/hooks.json +309 -0
  234. package/hooks/dist/instructions-loaded.js +107 -0
  235. package/hooks/dist/intel-queue.js +152 -0
  236. package/hooks/dist/intercept-plan-mode.js +50 -0
  237. package/hooks/dist/log-notification.js +125 -0
  238. package/hooks/dist/log-subagent.js +306 -0
  239. package/hooks/dist/log-tool-failure.js +140 -0
  240. package/hooks/dist/milestone-learnings.js +569 -0
  241. package/hooks/dist/pbr-tools.js +5 -0
  242. package/hooks/dist/post-bash-triage.js +152 -0
  243. package/hooks/dist/post-compact.js +135 -0
  244. package/hooks/dist/post-write-dispatch.js +277 -0
  245. package/hooks/dist/post-write-quality.js +208 -0
  246. package/hooks/dist/pre-bash-dispatch.js +158 -0
  247. package/hooks/dist/pre-write-dispatch.js +165 -0
  248. package/hooks/dist/progress-tracker.js +198 -0
  249. package/hooks/dist/prompt-routing.js +209 -0
  250. package/hooks/dist/run-hook.js +144 -0
  251. package/hooks/dist/session-cleanup.js +617 -0
  252. package/hooks/dist/session-tracker.js +124 -0
  253. package/hooks/dist/status-line.js +793 -0
  254. package/hooks/dist/suggest-compact.js +296 -0
  255. package/hooks/dist/sync-context-to-claude.js +100 -0
  256. package/hooks/dist/task-completed.js +206 -0
  257. package/hooks/dist/track-context-budget.js +405 -0
  258. package/hooks/dist/trust-tracker.js +193 -0
  259. package/hooks/dist/validate-commit.js +270 -0
  260. package/hooks/dist/validate-skill-args.js +222 -0
  261. package/hooks/dist/validate-task.js +272 -0
  262. package/hooks/dist/worktree-create.js +144 -0
  263. package/hooks/dist/worktree-remove.js +147 -0
  264. package/package.json +59 -40
  265. package/plan-build-run/bin/config-schema.json +1416 -0
  266. package/plan-build-run/bin/dashboard-launch.cjs +114 -0
  267. package/plan-build-run/bin/event-logger.cjs +92 -0
  268. package/plan-build-run/bin/lib/alternatives.cjs +198 -0
  269. package/plan-build-run/bin/lib/auto-cleanup.cjs +7 -0
  270. package/plan-build-run/bin/lib/build.cjs +717 -0
  271. package/plan-build-run/bin/lib/circuit-state.cjs +133 -0
  272. package/plan-build-run/bin/lib/commands.cjs +482 -0
  273. package/plan-build-run/bin/lib/config.cjs +770 -0
  274. package/plan-build-run/bin/lib/context.cjs +216 -0
  275. package/plan-build-run/bin/lib/contextual-help.cjs +207 -0
  276. package/plan-build-run/bin/lib/core.cjs +1563 -0
  277. package/plan-build-run/bin/lib/decisions.cjs +194 -0
  278. package/plan-build-run/bin/lib/frontmatter.cjs +299 -0
  279. package/plan-build-run/bin/lib/gates/advisories.cjs +129 -0
  280. package/plan-build-run/bin/lib/gates/build-dependency.cjs +115 -0
  281. package/plan-build-run/bin/lib/gates/build-executor.cjs +104 -0
  282. package/plan-build-run/bin/lib/gates/doc-existence.cjs +46 -0
  283. package/plan-build-run/bin/lib/gates/helpers.cjs +93 -0
  284. package/plan-build-run/bin/lib/gates/inline-execution.cjs +185 -0
  285. package/plan-build-run/bin/lib/gates/milestone-complete.cjs +136 -0
  286. package/plan-build-run/bin/lib/gates/milestone-summary.cjs +119 -0
  287. package/plan-build-run/bin/lib/gates/plan-executor.cjs +36 -0
  288. package/plan-build-run/bin/lib/gates/quick-executor.cjs +76 -0
  289. package/plan-build-run/bin/lib/gates/review-planner.cjs +61 -0
  290. package/plan-build-run/bin/lib/gates/review-verifier.cjs +69 -0
  291. package/plan-build-run/bin/lib/graph-cli.cjs +89 -0
  292. package/plan-build-run/bin/lib/graph.cjs +554 -0
  293. package/plan-build-run/bin/lib/health-phase06.cjs +120 -0
  294. package/plan-build-run/bin/lib/health.cjs +133 -0
  295. package/plan-build-run/bin/lib/history.cjs +147 -0
  296. package/plan-build-run/bin/lib/hypothesis-runner.cjs +127 -0
  297. package/plan-build-run/bin/lib/impact-analysis.cjs +319 -0
  298. package/plan-build-run/bin/lib/incidents.cjs +190 -0
  299. package/plan-build-run/bin/lib/init.cjs +367 -0
  300. package/plan-build-run/bin/lib/intel.cjs +653 -0
  301. package/plan-build-run/bin/lib/learnings.cjs +511 -0
  302. package/plan-build-run/bin/lib/local-llm/health.cjs +12 -0
  303. package/plan-build-run/bin/lib/local-llm/index.cjs +89 -0
  304. package/plan-build-run/bin/lib/local-llm/metrics.cjs +20 -0
  305. package/plan-build-run/bin/lib/local-llm/operations/classify-artifact.cjs +4 -0
  306. package/plan-build-run/bin/lib/local-llm/operations/classify-commit.cjs +4 -0
  307. package/plan-build-run/bin/lib/local-llm/operations/classify-error.cjs +4 -0
  308. package/plan-build-run/bin/lib/local-llm/operations/classify-file-intent.cjs +4 -0
  309. package/plan-build-run/bin/lib/local-llm/operations/triage-test-output.cjs +12 -0
  310. package/plan-build-run/bin/lib/local-llm/operations/validate-task.cjs +4 -0
  311. package/plan-build-run/bin/lib/migrate.cjs +298 -0
  312. package/plan-build-run/bin/lib/milestone.cjs +306 -0
  313. package/plan-build-run/bin/lib/negative-knowledge.cjs +194 -0
  314. package/plan-build-run/bin/lib/onboarding-generator.cjs +288 -0
  315. package/plan-build-run/bin/lib/parse-args.cjs +134 -0
  316. package/plan-build-run/bin/lib/patterns.cjs +272 -0
  317. package/plan-build-run/bin/lib/phase.cjs +1021 -0
  318. package/plan-build-run/bin/lib/post-hoc.cjs +160 -0
  319. package/plan-build-run/bin/lib/preview.cjs +174 -0
  320. package/plan-build-run/bin/lib/progress-visualization.cjs +296 -0
  321. package/plan-build-run/bin/lib/quick-init.cjs +131 -0
  322. package/plan-build-run/bin/lib/reference.cjs +234 -0
  323. package/plan-build-run/bin/lib/requirements.cjs +153 -0
  324. package/plan-build-run/bin/lib/reverse-spec.cjs +259 -0
  325. package/plan-build-run/bin/lib/roadmap.cjs +1097 -0
  326. package/plan-build-run/bin/lib/security-scan.cjs +200 -0
  327. package/plan-build-run/bin/lib/skill-section.cjs +98 -0
  328. package/plan-build-run/bin/lib/spec-diff.cjs +209 -0
  329. package/plan-build-run/bin/lib/spec-engine.cjs +189 -0
  330. package/plan-build-run/bin/lib/spot-check.cjs +510 -0
  331. package/plan-build-run/bin/lib/state.cjs +1050 -0
  332. package/plan-build-run/bin/lib/status-render.cjs +527 -0
  333. package/plan-build-run/bin/lib/step-verify.cjs +149 -0
  334. package/plan-build-run/bin/lib/suggest-next.cjs +316 -0
  335. package/plan-build-run/bin/lib/team-composer.cjs +85 -0
  336. package/plan-build-run/bin/lib/team-coordinator.cjs +151 -0
  337. package/plan-build-run/bin/lib/template.cjs +222 -0
  338. package/plan-build-run/bin/lib/templates.cjs +362 -0
  339. package/plan-build-run/bin/lib/test-selection.cjs +163 -0
  340. package/plan-build-run/bin/lib/todo.cjs +300 -0
  341. package/plan-build-run/bin/lib/validation.cjs +187 -0
  342. package/plan-build-run/bin/lib/verify.cjs +1451 -0
  343. package/plan-build-run/bin/pbr-tools.cjs +1877 -0
  344. package/plan-build-run/references/CLAUDE.md +7 -0
  345. package/plan-build-run/references/agent-contracts.md +326 -0
  346. package/plan-build-run/references/agent-teams.md +54 -0
  347. package/plan-build-run/references/behavioral-contexts.md +53 -0
  348. package/plan-build-run/references/checkpoints.md +776 -0
  349. package/plan-build-run/references/config-reference.md +613 -0
  350. package/plan-build-run/references/continuation-format.md +249 -0
  351. package/plan-build-run/references/debugging/CLAUDE.md +7 -0
  352. package/plan-build-run/references/decimal-phase-calculation.md +65 -0
  353. package/plan-build-run/references/git-integration.md +309 -0
  354. package/plan-build-run/references/git-planning-commit.md +38 -0
  355. package/plan-build-run/references/model-profile-resolution.md +34 -0
  356. package/plan-build-run/references/model-profiles.md +182 -0
  357. package/plan-build-run/references/model-selection.md +53 -0
  358. package/plan-build-run/references/phase-argument-parsing.md +61 -0
  359. package/plan-build-run/references/plan-authoring.md +246 -0
  360. package/plan-build-run/references/plan-format.md +351 -0
  361. package/plan-build-run/references/planning-config.md +200 -0
  362. package/plan-build-run/references/questioning.md +162 -0
  363. package/plan-build-run/references/reading-verification.md +127 -0
  364. package/plan-build-run/references/stub-patterns.md +160 -0
  365. package/plan-build-run/references/tdd.md +263 -0
  366. package/plan-build-run/references/ui-brand.md +187 -0
  367. package/plan-build-run/references/verification-overrides.md +38 -0
  368. package/plan-build-run/references/verification-patterns.md +612 -0
  369. package/plan-build-run/references/wave-execution.md +52 -0
  370. package/plan-build-run/skills/audit/SKILL.md +347 -0
  371. package/plan-build-run/skills/autonomous/SKILL.md +460 -0
  372. package/plan-build-run/skills/begin/SKILL.md +926 -0
  373. package/plan-build-run/skills/begin/templates/PROJECT.md.tmpl +33 -0
  374. package/plan-build-run/skills/begin/templates/REQUIREMENTS.md.tmpl +18 -0
  375. package/plan-build-run/skills/begin/templates/STATE.md.tmpl +48 -0
  376. package/plan-build-run/skills/begin/templates/config.json.tmpl +451 -0
  377. package/plan-build-run/skills/begin/templates/project-CONTEXT.md.tmpl +19 -0
  378. package/plan-build-run/skills/begin/templates/researcher-prompt.md.tmpl +47 -0
  379. package/plan-build-run/skills/begin/templates/roadmap-prompt.md.tmpl +49 -0
  380. package/plan-build-run/skills/begin/templates/synthesis-prompt.md.tmpl +44 -0
  381. package/plan-build-run/skills/build/SKILL.md +1655 -0
  382. package/plan-build-run/skills/build/templates/continuation-prompt.md.tmpl +26 -0
  383. package/plan-build-run/skills/build/templates/executor-prompt.md.tmpl +70 -0
  384. package/plan-build-run/skills/build/templates/inline-verifier-prompt.md.tmpl +33 -0
  385. package/plan-build-run/skills/config/SKILL.md +357 -0
  386. package/plan-build-run/skills/continue/SKILL.md +266 -0
  387. package/plan-build-run/skills/dashboard/SKILL.md +12 -0
  388. package/plan-build-run/skills/debug/SKILL.md +573 -0
  389. package/plan-build-run/skills/debug/templates/continuation-prompt.md.tmpl +27 -0
  390. package/plan-build-run/skills/debug/templates/initial-investigation-prompt.md.tmpl +34 -0
  391. package/plan-build-run/skills/discuss/SKILL.md +489 -0
  392. package/plan-build-run/skills/discuss/templates/CONTEXT.md.tmpl +61 -0
  393. package/plan-build-run/skills/discuss/templates/decision-categories.md +9 -0
  394. package/plan-build-run/skills/discuss/templates/project-CONTEXT.md.tmpl +19 -0
  395. package/plan-build-run/skills/do/SKILL.md +165 -0
  396. package/plan-build-run/skills/explore/SKILL.md +449 -0
  397. package/plan-build-run/skills/health/SKILL.md +332 -0
  398. package/plan-build-run/skills/health/templates/check-pattern.md.tmpl +30 -0
  399. package/plan-build-run/skills/health/templates/output-format.md.tmpl +63 -0
  400. package/plan-build-run/skills/help/SKILL.md +236 -0
  401. package/plan-build-run/skills/import/SKILL.md +827 -0
  402. package/plan-build-run/skills/intel/SKILL.md +131 -0
  403. package/plan-build-run/skills/milestone/SKILL.md +825 -0
  404. package/plan-build-run/skills/milestone/templates/audit-output.md.tmpl +76 -0
  405. package/plan-build-run/skills/milestone/templates/complete-output.md.tmpl +32 -0
  406. package/plan-build-run/skills/milestone/templates/edge-cases.md +54 -0
  407. package/plan-build-run/skills/milestone/templates/gaps-output.md.tmpl +25 -0
  408. package/plan-build-run/skills/milestone/templates/integration-checker-prompt.md.tmpl +25 -0
  409. package/plan-build-run/skills/milestone/templates/new-output.md.tmpl +29 -0
  410. package/plan-build-run/skills/milestone/templates/stats-file.md.tmpl +30 -0
  411. package/plan-build-run/skills/note/SKILL.md +221 -0
  412. package/plan-build-run/skills/pause/SKILL.md +259 -0
  413. package/plan-build-run/skills/pause/templates/continue-here.md.tmpl +71 -0
  414. package/plan-build-run/skills/plan/SKILL.md +852 -0
  415. package/plan-build-run/skills/plan/decimal-phase-calc.md +98 -0
  416. package/plan-build-run/skills/plan/templates/checker-prompt.md.tmpl +21 -0
  417. package/plan-build-run/skills/plan/templates/completion-output.md.tmpl +27 -0
  418. package/plan-build-run/skills/plan/templates/gap-closure-prompt.md.tmpl +32 -0
  419. package/plan-build-run/skills/plan/templates/planner-prompt.md.tmpl +38 -0
  420. package/plan-build-run/skills/plan/templates/prompt-partials/phase-project-context.md.tmpl +21 -0
  421. package/plan-build-run/skills/plan/templates/researcher-prompt.md.tmpl +19 -0
  422. package/plan-build-run/skills/plan/templates/revision-prompt.md.tmpl +23 -0
  423. package/plan-build-run/skills/profile/SKILL.md +173 -0
  424. package/plan-build-run/skills/profile-user/SKILL.md +220 -0
  425. package/plan-build-run/skills/quick/SKILL.md +727 -0
  426. package/plan-build-run/skills/release/SKILL.md +206 -0
  427. package/plan-build-run/skills/resume/SKILL.md +499 -0
  428. package/plan-build-run/skills/review/SKILL.md +763 -0
  429. package/plan-build-run/skills/review/templates/debugger-prompt.md.tmpl +60 -0
  430. package/plan-build-run/skills/review/templates/gap-planner-prompt.md.tmpl +40 -0
  431. package/plan-build-run/skills/review/templates/verifier-prompt.md.tmpl +115 -0
  432. package/plan-build-run/skills/scan/SKILL.md +330 -0
  433. package/plan-build-run/skills/scan/templates/mapper-prompt.md.tmpl +201 -0
  434. package/plan-build-run/skills/session-report/SKILL.md +128 -0
  435. package/plan-build-run/skills/setup/SKILL.md +246 -0
  436. package/plan-build-run/skills/shared/agent-type-resolution.md +20 -0
  437. package/plan-build-run/skills/shared/commit-planning-docs.md +43 -0
  438. package/plan-build-run/skills/shared/config-loading.md +102 -0
  439. package/plan-build-run/skills/shared/context-budget.md +105 -0
  440. package/plan-build-run/skills/shared/context-loader-task.md +91 -0
  441. package/plan-build-run/skills/shared/digest-select.md +79 -0
  442. package/plan-build-run/skills/shared/domain-probes.md +125 -0
  443. package/plan-build-run/skills/shared/error-reporting.md +59 -0
  444. package/plan-build-run/skills/shared/gate-prompts.md +390 -0
  445. package/plan-build-run/skills/shared/phase-argument-parsing.md +45 -0
  446. package/plan-build-run/skills/shared/revision-loop.md +81 -0
  447. package/plan-build-run/skills/shared/state-update.md +154 -0
  448. package/plan-build-run/skills/shared/universal-anti-patterns.md +59 -0
  449. package/plan-build-run/skills/ship/SKILL.md +154 -0
  450. package/plan-build-run/skills/status/SKILL.md +520 -0
  451. package/plan-build-run/skills/statusline/SKILL.md +151 -0
  452. package/plan-build-run/skills/test/SKILL.md +254 -0
  453. package/plan-build-run/skills/todo/SKILL.md +285 -0
  454. package/plan-build-run/skills/ui-phase/SKILL.md +177 -0
  455. package/plan-build-run/skills/ui-review/SKILL.md +204 -0
  456. package/plan-build-run/skills/undo/SKILL.md +216 -0
  457. package/plan-build-run/skills/validate-phase/SKILL.md +358 -0
  458. package/plan-build-run/templates/CLAUDE.md +7 -0
  459. package/plan-build-run/templates/DEBUG.md +164 -0
  460. package/plan-build-run/templates/UAT.md +247 -0
  461. package/plan-build-run/templates/VALIDATION.md +76 -0
  462. package/plan-build-run/templates/codebase/architecture.md +255 -0
  463. package/plan-build-run/templates/codebase/concerns.md +310 -0
  464. package/plan-build-run/templates/codebase/conventions.md +307 -0
  465. package/plan-build-run/templates/codebase/integrations.md +280 -0
  466. package/plan-build-run/templates/codebase/stack.md +186 -0
  467. package/plan-build-run/templates/codebase/structure.md +285 -0
  468. package/plan-build-run/templates/codebase/testing.md +480 -0
  469. package/plan-build-run/templates/config.json +37 -0
  470. package/plan-build-run/templates/context.md +297 -0
  471. package/plan-build-run/templates/continue-here.md +78 -0
  472. package/plan-build-run/templates/crud-flow-verification.md +277 -0
  473. package/plan-build-run/templates/debug-subagent-prompt.md +91 -0
  474. package/plan-build-run/templates/deferred-items.md +19 -0
  475. package/plan-build-run/templates/discovery.md +146 -0
  476. package/plan-build-run/templates/milestone-archive.md +123 -0
  477. package/plan-build-run/templates/milestone.md +115 -0
  478. package/plan-build-run/templates/phase-prompt.md +569 -0
  479. package/plan-build-run/templates/planner-subagent-prompt.md +117 -0
  480. package/plan-build-run/templates/project.md +184 -0
  481. package/plan-build-run/templates/requirements.md +231 -0
  482. package/plan-build-run/templates/research-outputs/ARCHITECTURE.md.tmpl +86 -0
  483. package/plan-build-run/templates/research-outputs/FEATURES.md.tmpl +77 -0
  484. package/plan-build-run/templates/research-outputs/PITFALLS.md.tmpl +65 -0
  485. package/plan-build-run/templates/research-outputs/STACK.md.tmpl +80 -0
  486. package/plan-build-run/templates/research-project/ARCHITECTURE.md +204 -0
  487. package/plan-build-run/templates/research-project/FEATURES.md +147 -0
  488. package/plan-build-run/templates/research-project/PITFALLS.md +200 -0
  489. package/plan-build-run/templates/research-project/STACK.md +120 -0
  490. package/plan-build-run/templates/research-project/SUMMARY.md +170 -0
  491. package/plan-build-run/templates/research.md +552 -0
  492. package/plan-build-run/templates/retrospective.md +54 -0
  493. package/plan-build-run/templates/roadmap.md +202 -0
  494. package/plan-build-run/templates/seed.md +16 -0
  495. package/plan-build-run/templates/state.md +176 -0
  496. package/plan-build-run/templates/summary-complex.md +59 -0
  497. package/plan-build-run/templates/summary-minimal.md +41 -0
  498. package/plan-build-run/templates/summary-standard.md +48 -0
  499. package/plan-build-run/templates/summary.md +248 -0
  500. package/plan-build-run/templates/user-setup.md +311 -0
  501. package/plan-build-run/templates/verification-report.md +322 -0
  502. package/plan-build-run/workflows/add-phase.md +111 -0
  503. package/plan-build-run/workflows/add-todo.md +157 -0
  504. package/plan-build-run/workflows/audit-milestone.md +241 -0
  505. package/plan-build-run/workflows/check-todos.md +176 -0
  506. package/plan-build-run/workflows/complete-milestone.md +644 -0
  507. package/plan-build-run/workflows/diagnose-issues.md +219 -0
  508. package/plan-build-run/workflows/discovery-phase.md +289 -0
  509. package/plan-build-run/workflows/discuss-phase.md +429 -0
  510. package/plan-build-run/workflows/execute-phase.md +439 -0
  511. package/plan-build-run/workflows/execute-plan.md +437 -0
  512. package/plan-build-run/workflows/explore.md +150 -0
  513. package/plan-build-run/workflows/help.md +470 -0
  514. package/plan-build-run/workflows/insert-phase.md +129 -0
  515. package/plan-build-run/workflows/list-phase-assumptions.md +178 -0
  516. package/plan-build-run/workflows/map-codebase.md +327 -0
  517. package/plan-build-run/workflows/new-milestone.md +373 -0
  518. package/plan-build-run/workflows/new-project.md +1009 -0
  519. package/plan-build-run/workflows/note.md +90 -0
  520. package/plan-build-run/workflows/pause-work.md +122 -0
  521. package/plan-build-run/workflows/plan-milestone-gaps.md +256 -0
  522. package/plan-build-run/workflows/plan-phase.md +376 -0
  523. package/plan-build-run/workflows/progress.md +431 -0
  524. package/plan-build-run/workflows/quick.md +230 -0
  525. package/plan-build-run/workflows/remove-phase.md +154 -0
  526. package/plan-build-run/workflows/research-phase.md +74 -0
  527. package/plan-build-run/workflows/resume-project.md +306 -0
  528. package/plan-build-run/workflows/set-profile.md +80 -0
  529. package/plan-build-run/workflows/settings.md +145 -0
  530. package/plan-build-run/workflows/transition.md +539 -0
  531. package/plan-build-run/workflows/update.md +212 -0
  532. package/plan-build-run/workflows/verify-phase.md +226 -0
  533. package/plan-build-run/workflows/verify-work.md +465 -0
  534. package/plugins/pbr/.claude-plugin/plugin.json +1 -1
  535. package/plugins/pbr/CLAUDE.md +19 -0
  536. package/plugins/pbr/UI-CONSISTENCY-GAPS.md +1 -1
  537. package/plugins/pbr/agents/audit.md +285 -0
  538. package/plugins/pbr/agents/codebase-mapper.md +110 -18
  539. package/plugins/pbr/agents/debugger.md +231 -29
  540. package/plugins/pbr/agents/dev-sync.md +206 -0
  541. package/plugins/pbr/agents/executor.md +601 -39
  542. package/plugins/pbr/agents/general.md +71 -6
  543. package/plugins/pbr/agents/integration-checker.md +146 -30
  544. package/plugins/pbr/agents/intel-updater.md +333 -0
  545. package/plugins/pbr/agents/nyquist-auditor.md +253 -0
  546. package/plugins/pbr/agents/plan-checker.md +177 -60
  547. package/plugins/pbr/agents/planner.md +404 -42
  548. package/plugins/pbr/agents/researcher.md +239 -36
  549. package/plugins/pbr/agents/roadmapper.md +384 -0
  550. package/plugins/pbr/agents/synthesizer.md +169 -26
  551. package/plugins/pbr/agents/ui-checker.md +203 -0
  552. package/plugins/pbr/agents/ui-researcher.md +224 -0
  553. package/plugins/pbr/agents/verifier.md +452 -48
  554. package/plugins/pbr/commands/add-phase.md +75 -0
  555. package/plugins/pbr/commands/add-todo.md +8 -0
  556. package/plugins/pbr/commands/audit-milestone.md +8 -0
  557. package/plugins/pbr/commands/audit.md +5 -0
  558. package/plugins/pbr/commands/autonomous.md +5 -0
  559. package/plugins/pbr/commands/begin.md +1 -1
  560. package/plugins/pbr/commands/build.md +1 -1
  561. package/plugins/pbr/commands/check-todos.md +8 -0
  562. package/plugins/pbr/commands/complete-milestone.md +8 -0
  563. package/plugins/pbr/commands/config.md +2 -2
  564. package/plugins/pbr/commands/continue.md +1 -1
  565. package/plugins/pbr/commands/dashboard.md +1 -1
  566. package/plugins/pbr/commands/debug.md +1 -1
  567. package/plugins/pbr/commands/discuss-phase.md +6 -0
  568. package/plugins/pbr/commands/discuss.md +1 -1
  569. package/plugins/pbr/commands/do.md +5 -0
  570. package/plugins/pbr/commands/execute-phase.md +6 -0
  571. package/plugins/pbr/commands/explore.md +1 -1
  572. package/plugins/pbr/commands/health.md +1 -1
  573. package/plugins/pbr/commands/help.md +1 -1
  574. package/plugins/pbr/commands/import.md +2 -2
  575. package/plugins/pbr/commands/insert-phase.md +65 -0
  576. package/plugins/pbr/commands/intel.md +5 -0
  577. package/plugins/pbr/commands/join-discord.md +11 -0
  578. package/plugins/pbr/commands/list-phase-assumptions.md +69 -0
  579. package/plugins/pbr/commands/map-codebase.md +6 -0
  580. package/plugins/pbr/commands/milestone.md +1 -1
  581. package/plugins/pbr/commands/new-milestone.md +8 -0
  582. package/plugins/pbr/commands/new-project.md +6 -0
  583. package/plugins/pbr/commands/note.md +1 -1
  584. package/plugins/pbr/commands/pause-work.md +5 -0
  585. package/plugins/pbr/commands/pause.md +1 -1
  586. package/plugins/pbr/commands/plan-milestone-gaps.md +7 -0
  587. package/plugins/pbr/commands/plan-phase.md +6 -0
  588. package/plugins/pbr/commands/plan.md +1 -1
  589. package/plugins/pbr/commands/profile-user.md +5 -0
  590. package/plugins/pbr/commands/profile.md +5 -0
  591. package/plugins/pbr/commands/progress.md +6 -0
  592. package/plugins/pbr/commands/quick.md +2 -2
  593. package/plugins/pbr/commands/reapply-patches.md +47 -0
  594. package/plugins/pbr/commands/release.md +6 -0
  595. package/plugins/pbr/commands/remove-phase.md +66 -0
  596. package/plugins/pbr/commands/research-phase.md +59 -0
  597. package/plugins/pbr/commands/resume-work.md +5 -0
  598. package/plugins/pbr/commands/resume.md +1 -1
  599. package/plugins/pbr/commands/review.md +1 -1
  600. package/plugins/pbr/commands/scan.md +1 -1
  601. package/plugins/pbr/commands/session-report.md +5 -0
  602. package/plugins/pbr/commands/set-profile.md +6 -0
  603. package/plugins/pbr/commands/settings.md +5 -0
  604. package/plugins/pbr/commands/setup.md +2 -2
  605. package/plugins/pbr/commands/ship.md +5 -0
  606. package/plugins/pbr/commands/status.md +1 -1
  607. package/plugins/pbr/commands/statusline.md +1 -1
  608. package/plugins/pbr/commands/test.md +5 -0
  609. package/plugins/pbr/commands/todo.md +2 -2
  610. package/plugins/pbr/commands/ui-phase.md +5 -0
  611. package/plugins/pbr/commands/ui-review.md +5 -0
  612. package/plugins/pbr/commands/undo.md +5 -0
  613. package/plugins/pbr/commands/update.md +37 -0
  614. package/plugins/pbr/commands/validate-phase.md +5 -0
  615. package/plugins/pbr/commands/verify-work.md +6 -0
  616. package/plugins/pbr/dashboard/package-lock.json +6 -0
  617. package/plugins/pbr/hooks/hooks.json +102 -13
  618. package/plugins/pbr/references/agent-contracts.md +37 -8
  619. package/plugins/pbr/references/agent-teams.md +3 -3
  620. package/plugins/pbr/references/archive/checkpoints.md +189 -0
  621. package/plugins/pbr/references/archive/context-quality-tiers.md +45 -0
  622. package/plugins/pbr/references/archive/pbr-rules.md +194 -0
  623. package/plugins/pbr/references/archive/verification-patterns.md +277 -0
  624. package/plugins/pbr/references/config-reference.md +182 -10
  625. package/plugins/pbr/references/continuation-format.md +1 -0
  626. package/plugins/pbr/references/deviation-rules.md +12 -0
  627. package/plugins/pbr/references/git-integration.md +110 -27
  628. package/plugins/pbr/references/hook-ordering.md +89 -0
  629. package/plugins/pbr/references/limitations.md +106 -0
  630. package/plugins/pbr/references/model-profiles.md +90 -7
  631. package/plugins/pbr/references/model-selection.md +1 -1
  632. package/plugins/pbr/references/node-repair.md +48 -0
  633. package/plugins/pbr/references/pbr-tools-cli.md +132 -2
  634. package/plugins/pbr/references/plan-authoring.md +65 -0
  635. package/plugins/pbr/references/plan-format.md +161 -10
  636. package/plugins/pbr/references/pretooluse-jsonl-behavior.md +58 -0
  637. package/plugins/pbr/references/questioning.md +138 -49
  638. package/plugins/pbr/references/reading-verification.md +4 -4
  639. package/plugins/pbr/references/signal-files.md +41 -0
  640. package/plugins/pbr/references/tmux-setup.md +288 -0
  641. package/plugins/pbr/references/ui-brand.md +449 -0
  642. package/plugins/pbr/references/worktree-sparse-checkout.md +86 -0
  643. package/plugins/pbr/scripts/architecture-guard.js +257 -0
  644. package/plugins/pbr/scripts/audit-checks/behavioral-compliance.js +2098 -0
  645. package/plugins/pbr/scripts/audit-checks/error-analysis.js +895 -0
  646. package/plugins/pbr/scripts/audit-checks/feature-verification.js +723 -0
  647. package/plugins/pbr/scripts/audit-checks/index.js +433 -0
  648. package/plugins/pbr/scripts/audit-checks/infrastructure.js +816 -0
  649. package/plugins/pbr/scripts/audit-checks/quality-metrics.js +452 -0
  650. package/plugins/pbr/scripts/audit-checks/session-quality.js +980 -0
  651. package/plugins/pbr/scripts/audit-checks/si-agent-hook-config-checks.js +467 -0
  652. package/plugins/pbr/scripts/audit-checks/si-cross-cutting-checks.js +272 -0
  653. package/plugins/pbr/scripts/audit-checks/si-skill-checks.js +424 -0
  654. package/plugins/pbr/scripts/audit-checks/workflow-compliance.js +1210 -0
  655. package/plugins/pbr/scripts/audit-dimensions.js +552 -0
  656. package/plugins/pbr/scripts/auto-continue.js +211 -32
  657. package/plugins/pbr/scripts/block-skill-self-read.js +85 -0
  658. package/plugins/pbr/scripts/check-agent-state-write.js +74 -0
  659. package/plugins/pbr/scripts/check-config-change.js +188 -0
  660. package/plugins/pbr/scripts/check-cross-plugin-sync.js +93 -0
  661. package/plugins/pbr/scripts/check-dangerous-commands.js +9 -5
  662. package/plugins/pbr/scripts/check-direct-state-write.js +37 -0
  663. package/plugins/pbr/scripts/check-phase-boundary.js +2 -8
  664. package/plugins/pbr/scripts/check-plan-format.js +149 -274
  665. package/plugins/pbr/scripts/check-roadmap-sync.js +167 -10
  666. package/plugins/pbr/scripts/check-skill-workflow.js +38 -34
  667. package/plugins/pbr/scripts/check-state-sync.js +344 -216
  668. package/plugins/pbr/scripts/check-subagent-output.js +294 -283
  669. package/plugins/pbr/scripts/check-summary-gate.js +1 -1
  670. package/plugins/pbr/scripts/config-schema.json +1252 -95
  671. package/plugins/pbr/scripts/context-bridge.js +439 -0
  672. package/plugins/pbr/scripts/context-budget-check.js +89 -9
  673. package/plugins/pbr/scripts/context-quality.js +272 -0
  674. package/plugins/pbr/scripts/enforce-pbr-workflow.js +277 -0
  675. package/plugins/pbr/scripts/event-handler.js +129 -77
  676. package/plugins/pbr/scripts/event-logger.js +61 -26
  677. package/plugins/pbr/scripts/feedback-loop.js +172 -0
  678. package/plugins/pbr/scripts/graph-update.js +199 -0
  679. package/plugins/pbr/scripts/hook-logger.js +76 -35
  680. package/plugins/pbr/scripts/hook-server-client.js +258 -0
  681. package/plugins/pbr/scripts/hook-server.js +334 -0
  682. package/plugins/pbr/scripts/hooks-schema.json +5 -1
  683. package/plugins/pbr/scripts/instructions-loaded.js +107 -0
  684. package/plugins/pbr/scripts/intel-queue.js +152 -0
  685. package/plugins/pbr/scripts/intent-router.cjs +147 -0
  686. package/plugins/pbr/scripts/intercept-plan-mode.js +52 -0
  687. package/plugins/pbr/scripts/lib/alternatives.js +203 -0
  688. package/plugins/pbr/scripts/lib/auto-cleanup.js +221 -0
  689. package/plugins/pbr/scripts/lib/auto-verify.js +103 -0
  690. package/plugins/pbr/scripts/lib/autonomy.js +91 -0
  691. package/plugins/pbr/scripts/lib/build.js +719 -0
  692. package/plugins/pbr/scripts/lib/ci-fix-loop.js +228 -0
  693. package/plugins/pbr/scripts/lib/circuit-state.js +133 -0
  694. package/plugins/pbr/scripts/lib/config.js +901 -0
  695. package/plugins/pbr/scripts/lib/context.js +254 -0
  696. package/plugins/pbr/scripts/lib/convention-detector.js +413 -0
  697. package/plugins/pbr/scripts/lib/core.js +939 -0
  698. package/plugins/pbr/scripts/lib/dashboard-launch.js +170 -0
  699. package/plugins/pbr/scripts/lib/decision-extraction.js +267 -0
  700. package/plugins/pbr/scripts/lib/dependency-break.js +147 -0
  701. package/plugins/pbr/scripts/lib/format-validators.js +947 -0
  702. package/plugins/pbr/scripts/lib/gates/advisories.js +129 -0
  703. package/plugins/pbr/scripts/lib/gates/build-dependency.js +115 -0
  704. package/plugins/pbr/scripts/lib/gates/build-executor.js +104 -0
  705. package/plugins/pbr/scripts/lib/gates/doc-existence.js +46 -0
  706. package/plugins/pbr/scripts/lib/gates/helpers.js +141 -0
  707. package/plugins/pbr/scripts/lib/gates/inline-execution.js +185 -0
  708. package/plugins/pbr/scripts/lib/gates/milestone-complete.js +136 -0
  709. package/plugins/pbr/scripts/lib/gates/milestone-summary.js +119 -0
  710. package/plugins/pbr/scripts/lib/gates/multi-phase-loader.js +147 -0
  711. package/plugins/pbr/scripts/lib/gates/plan-executor.js +36 -0
  712. package/plugins/pbr/scripts/lib/gates/quick-executor.js +76 -0
  713. package/plugins/pbr/scripts/lib/gates/review-planner.js +61 -0
  714. package/plugins/pbr/scripts/lib/gates/review-verifier.js +69 -0
  715. package/plugins/pbr/scripts/lib/gates/rich-agent-context.js +143 -0
  716. package/plugins/pbr/scripts/lib/health-checks.js +215 -0
  717. package/plugins/pbr/scripts/lib/history.js +150 -0
  718. package/plugins/pbr/scripts/lib/init.js +302 -0
  719. package/plugins/pbr/scripts/lib/learnings.js +432 -0
  720. package/plugins/pbr/scripts/lib/migrate.js +169 -0
  721. package/plugins/pbr/scripts/lib/parse-args.js +134 -0
  722. package/plugins/pbr/scripts/lib/pattern-routing.js +55 -0
  723. package/plugins/pbr/scripts/lib/phase.js +935 -0
  724. package/plugins/pbr/scripts/lib/pre-research.js +133 -0
  725. package/plugins/pbr/scripts/lib/preview.js +174 -0
  726. package/plugins/pbr/scripts/lib/quick-init.js +131 -0
  727. package/plugins/pbr/scripts/lib/reference.js +236 -0
  728. package/plugins/pbr/scripts/lib/resolve-root.js +66 -0
  729. package/plugins/pbr/scripts/lib/roadmap.js +784 -0
  730. package/plugins/pbr/scripts/lib/session-briefing.js +879 -0
  731. package/plugins/pbr/scripts/lib/skill-section.js +99 -0
  732. package/plugins/pbr/scripts/lib/smart-next-task.js +225 -0
  733. package/plugins/pbr/scripts/lib/snapshot-manager.js +225 -0
  734. package/plugins/pbr/scripts/lib/spot-check.js +509 -0
  735. package/plugins/pbr/scripts/lib/state.js +565 -0
  736. package/plugins/pbr/scripts/lib/status-render.js +511 -0
  737. package/plugins/pbr/scripts/lib/step-verify.js +149 -0
  738. package/plugins/pbr/scripts/lib/subagent-validators.js +1010 -0
  739. package/plugins/pbr/scripts/lib/suggest-next.js +316 -0
  740. package/plugins/pbr/scripts/lib/tech-debt-scanner.js +116 -0
  741. package/plugins/pbr/scripts/lib/test-cache.js +54 -0
  742. package/plugins/pbr/scripts/lib/todo.js +300 -0
  743. package/plugins/pbr/scripts/lib/trust-gate.js +84 -0
  744. package/plugins/pbr/scripts/local-llm/client.js +237 -0
  745. package/plugins/pbr/scripts/local-llm/health.js +220 -0
  746. package/plugins/pbr/scripts/local-llm/metrics.js +340 -0
  747. package/plugins/pbr/scripts/local-llm/operations/classify-artifact.js +76 -0
  748. package/plugins/pbr/scripts/local-llm/operations/classify-commit.js +137 -0
  749. package/plugins/pbr/scripts/local-llm/operations/classify-error.js +75 -0
  750. package/plugins/pbr/scripts/local-llm/operations/classify-file-intent.js +171 -0
  751. package/plugins/pbr/scripts/local-llm/operations/score-source.js +72 -0
  752. package/plugins/pbr/scripts/local-llm/operations/summarize-context.js +62 -0
  753. package/plugins/pbr/scripts/local-llm/operations/triage-test-output.js +72 -0
  754. package/plugins/pbr/scripts/local-llm/operations/validate-task.js +59 -0
  755. package/plugins/pbr/scripts/local-llm/router.js +101 -0
  756. package/plugins/pbr/scripts/local-llm/shadow.js +60 -0
  757. package/plugins/pbr/scripts/local-llm/threshold-tuner.js +118 -0
  758. package/plugins/pbr/scripts/log-subagent.js +129 -26
  759. package/plugins/pbr/scripts/log-tool-failure.js +57 -4
  760. package/plugins/pbr/scripts/milestone-learnings.js +569 -0
  761. package/plugins/pbr/scripts/package.json +1 -0
  762. package/plugins/pbr/scripts/pbr-tools.js +1361 -1214
  763. package/plugins/pbr/scripts/post-bash-triage.js +163 -0
  764. package/plugins/pbr/scripts/post-compact.js +135 -0
  765. package/plugins/pbr/scripts/post-hoc.js +286 -0
  766. package/plugins/pbr/scripts/post-write-dispatch.js +315 -30
  767. package/plugins/pbr/scripts/post-write-quality.js +3 -3
  768. package/plugins/pbr/scripts/pre-bash-dispatch.js +81 -4
  769. package/plugins/pbr/scripts/pre-write-dispatch.js +76 -20
  770. package/plugins/pbr/scripts/progress-tracker.js +145 -291
  771. package/plugins/pbr/scripts/quick-status.js +179 -0
  772. package/plugins/pbr/scripts/record-incident.js +37 -0
  773. package/plugins/pbr/scripts/risk-classifier.cjs +123 -0
  774. package/plugins/pbr/scripts/run-hook.js +49 -8
  775. package/plugins/pbr/scripts/session-cleanup.js +379 -7
  776. package/plugins/pbr/scripts/session-tracker.js +124 -0
  777. package/plugins/pbr/scripts/status-line.js +540 -27
  778. package/plugins/pbr/scripts/suggest-compact.js +183 -7
  779. package/plugins/pbr/scripts/sync-context-to-claude.js +100 -0
  780. package/plugins/pbr/scripts/task-completed.js +125 -3
  781. package/plugins/pbr/scripts/test/config.test.js +126 -0
  782. package/plugins/pbr/scripts/test/cross-platform.test.js +131 -0
  783. package/plugins/pbr/scripts/test/fixtures/config.json +20 -0
  784. package/plugins/pbr/scripts/test/fixtures/plan.md +54 -0
  785. package/plugins/pbr/scripts/test/fixtures/project.md +30 -0
  786. package/plugins/pbr/scripts/test/fixtures/roadmap.md +55 -0
  787. package/plugins/pbr/scripts/test/fixtures/state.md +60 -0
  788. package/plugins/pbr/scripts/test/fixtures/summary.md +35 -0
  789. package/plugins/pbr/scripts/test/fixtures.test.js +184 -0
  790. package/plugins/pbr/scripts/test/phase.test.js +142 -0
  791. package/plugins/pbr/scripts/test/roadmap.test.js +96 -0
  792. package/plugins/pbr/scripts/test/state.test.js +163 -0
  793. package/plugins/pbr/scripts/track-context-budget.js +326 -100
  794. package/plugins/pbr/scripts/trust-tracker.js +193 -0
  795. package/plugins/pbr/scripts/validate-commit.js +76 -11
  796. package/plugins/pbr/scripts/validate-skill-args.js +18 -14
  797. package/plugins/pbr/scripts/validate-task.js +93 -626
  798. package/plugins/pbr/scripts/worktree-create.js +144 -0
  799. package/plugins/pbr/scripts/worktree-remove.js +147 -0
  800. package/plugins/pbr/skills/audit/SKILL.md +478 -0
  801. package/plugins/pbr/skills/autonomous/SKILL.md +460 -0
  802. package/plugins/pbr/skills/begin/SKILL.md +447 -142
  803. package/plugins/pbr/skills/begin/templates/STATE.md.tmpl +1 -2
  804. package/plugins/pbr/skills/begin/templates/config.json.tmpl +423 -36
  805. package/plugins/pbr/skills/begin/templates/researcher-prompt.md.tmpl +28 -0
  806. package/plugins/pbr/skills/begin/templates/roadmap-prompt.md.tmpl +26 -3
  807. package/plugins/pbr/skills/begin/templates/synthesis-prompt.md.tmpl +33 -5
  808. package/plugins/pbr/skills/build/SKILL.md +1010 -327
  809. package/plugins/pbr/skills/build/templates/continuation-prompt.md.tmpl +26 -0
  810. package/plugins/pbr/skills/build/templates/executor-prompt.md.tmpl +77 -0
  811. package/plugins/pbr/skills/build/templates/inline-verifier-prompt.md.tmpl +33 -0
  812. package/plugins/pbr/skills/config/SKILL.md +108 -9
  813. package/plugins/pbr/skills/continue/SKILL.md +118 -19
  814. package/plugins/pbr/skills/dashboard/SKILL.md +21 -9
  815. package/plugins/pbr/skills/debug/SKILL.md +62 -10
  816. package/plugins/pbr/skills/debug/templates/continuation-prompt.md.tmpl +12 -1
  817. package/plugins/pbr/skills/debug/templates/initial-investigation-prompt.md.tmpl +12 -5
  818. package/plugins/pbr/skills/discuss/SKILL.md +155 -23
  819. package/plugins/pbr/skills/discuss/templates/CONTEXT.md.tmpl +21 -1
  820. package/plugins/pbr/skills/do/SKILL.md +119 -24
  821. package/plugins/pbr/skills/explore/SKILL.md +83 -16
  822. package/plugins/pbr/skills/health/SKILL.md +74 -17
  823. package/plugins/pbr/skills/help/SKILL.md +123 -39
  824. package/plugins/pbr/skills/import/SKILL.md +327 -13
  825. package/plugins/pbr/skills/intel/SKILL.md +131 -0
  826. package/plugins/pbr/skills/milestone/SKILL.md +342 -260
  827. package/plugins/pbr/skills/milestone/templates/audit-output.md.tmpl +76 -0
  828. package/plugins/pbr/skills/milestone/templates/complete-output.md.tmpl +32 -0
  829. package/plugins/pbr/skills/milestone/templates/edge-cases.md +54 -0
  830. package/plugins/pbr/skills/milestone/templates/gaps-output.md.tmpl +25 -0
  831. package/plugins/pbr/skills/milestone/templates/integration-checker-prompt.md.tmpl +25 -0
  832. package/plugins/pbr/skills/milestone/templates/new-output.md.tmpl +29 -0
  833. package/plugins/pbr/skills/note/SKILL.md +10 -2
  834. package/plugins/pbr/skills/pause/SKILL.md +51 -7
  835. package/plugins/pbr/skills/pause/templates/continue-here.md.tmpl +33 -52
  836. package/plugins/pbr/skills/plan/SKILL.md +418 -268
  837. package/plugins/pbr/skills/plan/templates/checker-prompt.md.tmpl +5 -2
  838. package/plugins/pbr/skills/plan/templates/completion-output.md.tmpl +27 -0
  839. package/plugins/pbr/skills/plan/templates/revision-prompt.md.tmpl +21 -5
  840. package/plugins/pbr/skills/profile/SKILL.md +183 -0
  841. package/plugins/pbr/skills/profile-user/SKILL.md +224 -0
  842. package/plugins/pbr/skills/quick/SKILL.md +440 -95
  843. package/plugins/pbr/skills/release/SKILL.md +206 -0
  844. package/plugins/pbr/skills/resume/SKILL.md +122 -27
  845. package/plugins/pbr/skills/review/SKILL.md +219 -154
  846. package/plugins/pbr/skills/review/templates/verifier-prompt.md.tmpl +7 -0
  847. package/plugins/pbr/skills/scan/SKILL.md +36 -12
  848. package/plugins/pbr/skills/scan/templates/mapper-prompt.md.tmpl +1 -1
  849. package/plugins/pbr/skills/session-report/SKILL.md +128 -0
  850. package/plugins/pbr/skills/setup/SKILL.md +149 -202
  851. package/plugins/pbr/skills/shared/agent-context-enrichment.md +21 -0
  852. package/plugins/pbr/skills/shared/agent-type-resolution.md +20 -0
  853. package/plugins/pbr/skills/shared/commit-planning-docs.md +8 -0
  854. package/plugins/pbr/skills/shared/context-budget.md +66 -1
  855. package/plugins/pbr/skills/shared/context-loader-task.md +15 -8
  856. package/plugins/pbr/skills/shared/digest-select.md +2 -2
  857. package/plugins/pbr/skills/shared/domain-probes.md +1 -1
  858. package/plugins/pbr/skills/shared/error-reporting.md +38 -60
  859. package/plugins/pbr/skills/shared/gate-prompts.md +4 -2
  860. package/plugins/pbr/skills/shared/memory-capture.md +48 -0
  861. package/plugins/pbr/skills/shared/phase-argument-parsing.md +4 -4
  862. package/plugins/pbr/skills/shared/revision-loop.md +24 -6
  863. package/plugins/pbr/skills/shared/state-update.md +47 -54
  864. package/plugins/pbr/skills/shared/universal-anti-patterns.md +27 -4
  865. package/plugins/pbr/skills/ship/SKILL.md +154 -0
  866. package/plugins/pbr/skills/status/SKILL.md +201 -53
  867. package/plugins/pbr/skills/test/SKILL.md +254 -0
  868. package/plugins/pbr/skills/todo/SKILL.md +13 -11
  869. package/plugins/pbr/skills/ui-phase/SKILL.md +179 -0
  870. package/plugins/pbr/skills/ui-review/SKILL.md +206 -0
  871. package/plugins/pbr/skills/undo/SKILL.md +218 -0
  872. package/plugins/pbr/skills/validate-phase/SKILL.md +358 -0
  873. package/plugins/pbr/templates/CONTEXT.md.tmpl +45 -20
  874. package/plugins/pbr/templates/DISCOVERY.md.tmpl +29 -0
  875. package/plugins/pbr/templates/HANDOFF.json.tmpl +30 -0
  876. package/plugins/pbr/templates/INTEGRATION-REPORT.md.tmpl +18 -2
  877. package/plugins/pbr/templates/KNOWLEDGE.md.tmpl +39 -0
  878. package/plugins/pbr/templates/MILESTONE-AUDIT.md.tmpl +44 -0
  879. package/plugins/pbr/templates/PROJECT.md.tmpl +126 -0
  880. package/plugins/pbr/templates/REQUIREMENTS.md.tmpl +96 -0
  881. package/plugins/pbr/templates/RETROSPECTIVE.md.tmpl +43 -0
  882. package/plugins/pbr/templates/ROADMAP.md.tmpl +108 -14
  883. package/plugins/pbr/templates/SUMMARY-complex.md.tmpl +133 -0
  884. package/plugins/pbr/templates/SUMMARY-minimal.md.tmpl +55 -0
  885. package/plugins/pbr/templates/SUMMARY.md.tmpl +21 -0
  886. package/plugins/pbr/templates/VERIFICATION-DETAIL.md.tmpl +49 -13
  887. package/plugins/pbr/templates/project-CONTEXT.md.tmpl +59 -0
  888. package/plugins/pbr/templates/research-outputs/ARCHITECTURE.md.tmpl +91 -0
  889. package/plugins/pbr/templates/research-outputs/FEATURES.md.tmpl +64 -0
  890. package/plugins/pbr/templates/research-outputs/PITFALLS.md.tmpl +50 -0
  891. package/plugins/pbr/templates/research-outputs/STACK.md.tmpl +63 -0
  892. package/plugins/pbr/templates/research-outputs/SUMMARY.md.tmpl +98 -0
  893. package/scripts/build-hooks.js +61 -0
  894. package/scripts/check-ci.js +100 -0
  895. package/scripts/clean-changelog.js +362 -0
  896. package/scripts/generate-derivatives.js +581 -0
  897. package/scripts/posttest.js +93 -0
  898. package/scripts/release.js +196 -0
  899. package/scripts/run-tests.cjs +29 -0
  900. package/dashboard/bin/cli.js +0 -25
  901. package/dashboard/public/css/layout.css +0 -472
  902. package/dashboard/public/css/status-colors.css +0 -98
  903. package/dashboard/public/js/htmx-title.js +0 -5
  904. package/dashboard/public/js/sidebar-toggle.js +0 -20
  905. package/dashboard/src/app.js +0 -78
  906. package/dashboard/src/middleware/errorHandler.js +0 -52
  907. package/dashboard/src/middleware/notFoundHandler.js +0 -9
  908. package/dashboard/src/repositories/planning.repository.js +0 -130
  909. package/dashboard/src/routes/events.routes.js +0 -40
  910. package/dashboard/src/routes/index.routes.js +0 -31
  911. package/dashboard/src/routes/pages.routes.js +0 -308
  912. package/dashboard/src/server.js +0 -42
  913. package/dashboard/src/services/dashboard.service.js +0 -309
  914. package/dashboard/src/services/milestone.service.js +0 -154
  915. package/dashboard/src/services/phase.service.js +0 -226
  916. package/dashboard/src/services/project.service.js +0 -57
  917. package/dashboard/src/services/roadmap.service.js +0 -171
  918. package/dashboard/src/services/sse.service.js +0 -58
  919. package/dashboard/src/services/todo.service.js +0 -254
  920. package/dashboard/src/services/watcher.service.js +0 -48
  921. package/dashboard/src/views/coming-soon.ejs +0 -11
  922. package/dashboard/src/views/error.ejs +0 -13
  923. package/dashboard/src/views/index.ejs +0 -5
  924. package/dashboard/src/views/layout.ejs +0 -1
  925. package/dashboard/src/views/milestone-detail.ejs +0 -5
  926. package/dashboard/src/views/milestones.ejs +0 -5
  927. package/dashboard/src/views/partials/dashboard-content.ejs +0 -77
  928. package/dashboard/src/views/partials/footer.ejs +0 -3
  929. package/dashboard/src/views/partials/head.ejs +0 -27
  930. package/dashboard/src/views/partials/header.ejs +0 -12
  931. package/dashboard/src/views/partials/layout-bottom.ejs +0 -15
  932. package/dashboard/src/views/partials/layout-top.ejs +0 -8
  933. package/dashboard/src/views/partials/milestone-detail-content.ejs +0 -19
  934. package/dashboard/src/views/partials/milestones-content.ejs +0 -44
  935. package/dashboard/src/views/partials/phase-content.ejs +0 -189
  936. package/dashboard/src/views/partials/phase-doc-content.ejs +0 -38
  937. package/dashboard/src/views/partials/phases-content.ejs +0 -117
  938. package/dashboard/src/views/partials/roadmap-content.ejs +0 -142
  939. package/dashboard/src/views/partials/sidebar.ejs +0 -46
  940. package/dashboard/src/views/partials/todo-create-content.ejs +0 -53
  941. package/dashboard/src/views/partials/todo-detail-content.ejs +0 -38
  942. package/dashboard/src/views/partials/todos-content.ejs +0 -53
  943. package/dashboard/src/views/phase-detail.ejs +0 -5
  944. package/dashboard/src/views/phase-doc.ejs +0 -5
  945. package/dashboard/src/views/phases.ejs +0 -5
  946. package/dashboard/src/views/roadmap.ejs +0 -5
  947. package/dashboard/src/views/todo-create.ejs +0 -5
  948. package/dashboard/src/views/todo-detail.ejs +0 -5
  949. package/dashboard/src/views/todos.ejs +0 -5
  950. package/plugins/copilot-pbr/CHANGELOG.md +0 -19
  951. package/plugins/copilot-pbr/README.md +0 -129
  952. package/plugins/copilot-pbr/agents/codebase-mapper.agent.md +0 -151
  953. package/plugins/copilot-pbr/agents/debugger.agent.md +0 -172
  954. package/plugins/copilot-pbr/agents/executor.agent.md +0 -267
  955. package/plugins/copilot-pbr/agents/general.agent.md +0 -88
  956. package/plugins/copilot-pbr/agents/integration-checker.agent.md +0 -119
  957. package/plugins/copilot-pbr/agents/plan-checker.agent.md +0 -199
  958. package/plugins/copilot-pbr/agents/planner.agent.md +0 -238
  959. package/plugins/copilot-pbr/agents/researcher.agent.md +0 -186
  960. package/plugins/copilot-pbr/agents/synthesizer.agent.md +0 -126
  961. package/plugins/copilot-pbr/agents/verifier.agent.md +0 -228
  962. package/plugins/copilot-pbr/hooks/hooks.json +0 -144
  963. package/plugins/copilot-pbr/plugin.json +0 -30
  964. package/plugins/copilot-pbr/references/agent-anti-patterns.md +0 -25
  965. package/plugins/copilot-pbr/references/agent-contracts.md +0 -297
  966. package/plugins/copilot-pbr/references/agent-interactions.md +0 -135
  967. package/plugins/copilot-pbr/references/agent-teams.md +0 -55
  968. package/plugins/copilot-pbr/references/checkpoints.md +0 -158
  969. package/plugins/copilot-pbr/references/common-bug-patterns.md +0 -14
  970. package/plugins/copilot-pbr/references/config-reference.md +0 -442
  971. package/plugins/copilot-pbr/references/continuation-format.md +0 -213
  972. package/plugins/copilot-pbr/references/deviation-rules.md +0 -113
  973. package/plugins/copilot-pbr/references/git-integration.md +0 -227
  974. package/plugins/copilot-pbr/references/integration-patterns.md +0 -118
  975. package/plugins/copilot-pbr/references/model-profiles.md +0 -100
  976. package/plugins/copilot-pbr/references/model-selection.md +0 -32
  977. package/plugins/copilot-pbr/references/pbr-rules.md +0 -195
  978. package/plugins/copilot-pbr/references/pbr-tools-cli.md +0 -285
  979. package/plugins/copilot-pbr/references/plan-authoring.md +0 -182
  980. package/plugins/copilot-pbr/references/plan-format.md +0 -288
  981. package/plugins/copilot-pbr/references/planning-config.md +0 -214
  982. package/plugins/copilot-pbr/references/questioning.md +0 -215
  983. package/plugins/copilot-pbr/references/reading-verification.md +0 -128
  984. package/plugins/copilot-pbr/references/stub-patterns.md +0 -161
  985. package/plugins/copilot-pbr/references/subagent-coordination.md +0 -120
  986. package/plugins/copilot-pbr/references/ui-formatting.md +0 -444
  987. package/plugins/copilot-pbr/references/verification-patterns.md +0 -199
  988. package/plugins/copilot-pbr/references/wave-execution.md +0 -96
  989. package/plugins/copilot-pbr/rules/pbr-workflow.mdc +0 -48
  990. package/plugins/copilot-pbr/setup.ps1 +0 -93
  991. package/plugins/copilot-pbr/setup.sh +0 -93
  992. package/plugins/copilot-pbr/skills/begin/SKILL.md +0 -589
  993. package/plugins/copilot-pbr/skills/begin/templates/PROJECT.md.tmpl +0 -34
  994. package/plugins/copilot-pbr/skills/begin/templates/REQUIREMENTS.md.tmpl +0 -19
  995. package/plugins/copilot-pbr/skills/begin/templates/STATE.md.tmpl +0 -50
  996. package/plugins/copilot-pbr/skills/begin/templates/config.json.tmpl +0 -64
  997. package/plugins/copilot-pbr/skills/begin/templates/researcher-prompt.md.tmpl +0 -20
  998. package/plugins/copilot-pbr/skills/begin/templates/roadmap-prompt.md.tmpl +0 -31
  999. package/plugins/copilot-pbr/skills/begin/templates/synthesis-prompt.md.tmpl +0 -17
  1000. package/plugins/copilot-pbr/skills/build/SKILL.md +0 -955
  1001. package/plugins/copilot-pbr/skills/config/SKILL.md +0 -250
  1002. package/plugins/copilot-pbr/skills/continue/SKILL.md +0 -159
  1003. package/plugins/copilot-pbr/skills/dashboard/SKILL.md +0 -43
  1004. package/plugins/copilot-pbr/skills/debug/SKILL.md +0 -508
  1005. package/plugins/copilot-pbr/skills/debug/templates/continuation-prompt.md.tmpl +0 -17
  1006. package/plugins/copilot-pbr/skills/debug/templates/initial-investigation-prompt.md.tmpl +0 -28
  1007. package/plugins/copilot-pbr/skills/discuss/SKILL.md +0 -353
  1008. package/plugins/copilot-pbr/skills/discuss/templates/CONTEXT.md.tmpl +0 -62
  1009. package/plugins/copilot-pbr/skills/discuss/templates/decision-categories.md +0 -10
  1010. package/plugins/copilot-pbr/skills/do/SKILL.md +0 -66
  1011. package/plugins/copilot-pbr/skills/explore/SKILL.md +0 -373
  1012. package/plugins/copilot-pbr/skills/health/SKILL.md +0 -274
  1013. package/plugins/copilot-pbr/skills/health/templates/check-pattern.md.tmpl +0 -31
  1014. package/plugins/copilot-pbr/skills/health/templates/output-format.md.tmpl +0 -64
  1015. package/plugins/copilot-pbr/skills/help/SKILL.md +0 -152
  1016. package/plugins/copilot-pbr/skills/import/SKILL.md +0 -502
  1017. package/plugins/copilot-pbr/skills/milestone/SKILL.md +0 -745
  1018. package/plugins/copilot-pbr/skills/milestone/templates/audit-report.md.tmpl +0 -49
  1019. package/plugins/copilot-pbr/skills/milestone/templates/stats-file.md.tmpl +0 -31
  1020. package/plugins/copilot-pbr/skills/note/SKILL.md +0 -213
  1021. package/plugins/copilot-pbr/skills/pause/SKILL.md +0 -247
  1022. package/plugins/copilot-pbr/skills/pause/templates/continue-here.md.tmpl +0 -72
  1023. package/plugins/copilot-pbr/skills/plan/SKILL.md +0 -662
  1024. package/plugins/copilot-pbr/skills/plan/templates/checker-prompt.md.tmpl +0 -22
  1025. package/plugins/copilot-pbr/skills/plan/templates/gap-closure-prompt.md.tmpl +0 -33
  1026. package/plugins/copilot-pbr/skills/plan/templates/planner-prompt.md.tmpl +0 -39
  1027. package/plugins/copilot-pbr/skills/plan/templates/researcher-prompt.md.tmpl +0 -20
  1028. package/plugins/copilot-pbr/skills/plan/templates/revision-prompt.md.tmpl +0 -24
  1029. package/plugins/copilot-pbr/skills/quick/SKILL.md +0 -376
  1030. package/plugins/copilot-pbr/skills/resume/SKILL.md +0 -399
  1031. package/plugins/copilot-pbr/skills/review/SKILL.md +0 -653
  1032. package/plugins/copilot-pbr/skills/review/templates/debugger-prompt.md.tmpl +0 -61
  1033. package/plugins/copilot-pbr/skills/review/templates/gap-planner-prompt.md.tmpl +0 -41
  1034. package/plugins/copilot-pbr/skills/review/templates/verifier-prompt.md.tmpl +0 -116
  1035. package/plugins/copilot-pbr/skills/scan/SKILL.md +0 -299
  1036. package/plugins/copilot-pbr/skills/scan/templates/mapper-prompt.md.tmpl +0 -202
  1037. package/plugins/copilot-pbr/skills/setup/SKILL.md +0 -296
  1038. package/plugins/copilot-pbr/skills/shared/commit-planning-docs.md +0 -36
  1039. package/plugins/copilot-pbr/skills/shared/config-loading.md +0 -103
  1040. package/plugins/copilot-pbr/skills/shared/context-budget.md +0 -41
  1041. package/plugins/copilot-pbr/skills/shared/context-loader-task.md +0 -87
  1042. package/plugins/copilot-pbr/skills/shared/digest-select.md +0 -80
  1043. package/plugins/copilot-pbr/skills/shared/domain-probes.md +0 -126
  1044. package/plugins/copilot-pbr/skills/shared/error-reporting.md +0 -81
  1045. package/plugins/copilot-pbr/skills/shared/gate-prompts.md +0 -389
  1046. package/plugins/copilot-pbr/skills/shared/phase-argument-parsing.md +0 -46
  1047. package/plugins/copilot-pbr/skills/shared/progress-display.md +0 -53
  1048. package/plugins/copilot-pbr/skills/shared/revision-loop.md +0 -82
  1049. package/plugins/copilot-pbr/skills/shared/state-loading.md +0 -63
  1050. package/plugins/copilot-pbr/skills/shared/state-update.md +0 -162
  1051. package/plugins/copilot-pbr/skills/shared/universal-anti-patterns.md +0 -38
  1052. package/plugins/copilot-pbr/skills/status/SKILL.md +0 -362
  1053. package/plugins/copilot-pbr/skills/statusline/SKILL.md +0 -149
  1054. package/plugins/copilot-pbr/skills/todo/SKILL.md +0 -279
  1055. package/plugins/copilot-pbr/templates/CONTEXT.md.tmpl +0 -53
  1056. package/plugins/copilot-pbr/templates/INTEGRATION-REPORT.md.tmpl +0 -152
  1057. package/plugins/copilot-pbr/templates/RESEARCH-SUMMARY.md.tmpl +0 -98
  1058. package/plugins/copilot-pbr/templates/ROADMAP.md.tmpl +0 -41
  1059. package/plugins/copilot-pbr/templates/SUMMARY.md.tmpl +0 -82
  1060. package/plugins/copilot-pbr/templates/VERIFICATION-DETAIL.md.tmpl +0 -117
  1061. package/plugins/copilot-pbr/templates/codebase/ARCHITECTURE.md.tmpl +0 -98
  1062. package/plugins/copilot-pbr/templates/codebase/CONCERNS.md.tmpl +0 -93
  1063. package/plugins/copilot-pbr/templates/codebase/CONVENTIONS.md.tmpl +0 -104
  1064. package/plugins/copilot-pbr/templates/codebase/INTEGRATIONS.md.tmpl +0 -78
  1065. package/plugins/copilot-pbr/templates/codebase/STACK.md.tmpl +0 -78
  1066. package/plugins/copilot-pbr/templates/codebase/STRUCTURE.md.tmpl +0 -80
  1067. package/plugins/copilot-pbr/templates/codebase/TESTING.md.tmpl +0 -107
  1068. package/plugins/copilot-pbr/templates/continue-here.md.tmpl +0 -74
  1069. package/plugins/copilot-pbr/templates/prompt-partials/phase-project-context.md.tmpl +0 -38
  1070. package/plugins/copilot-pbr/templates/research/ARCHITECTURE.md.tmpl +0 -124
  1071. package/plugins/copilot-pbr/templates/research/STACK.md.tmpl +0 -71
  1072. package/plugins/copilot-pbr/templates/research/SUMMARY.md.tmpl +0 -112
  1073. package/plugins/copilot-pbr/templates/research-outputs/phase-research.md.tmpl +0 -81
  1074. package/plugins/copilot-pbr/templates/research-outputs/project-research.md.tmpl +0 -99
  1075. package/plugins/copilot-pbr/templates/research-outputs/synthesis.md.tmpl +0 -36
  1076. package/plugins/cursor-pbr/.cursor-plugin/plugin.json +0 -32
  1077. package/plugins/cursor-pbr/CHANGELOG.md +0 -15
  1078. package/plugins/cursor-pbr/README.md +0 -118
  1079. package/plugins/cursor-pbr/agents/codebase-mapper.md +0 -150
  1080. package/plugins/cursor-pbr/agents/debugger.md +0 -171
  1081. package/plugins/cursor-pbr/agents/executor.md +0 -266
  1082. package/plugins/cursor-pbr/agents/general.md +0 -87
  1083. package/plugins/cursor-pbr/agents/integration-checker.md +0 -118
  1084. package/plugins/cursor-pbr/agents/plan-checker.md +0 -198
  1085. package/plugins/cursor-pbr/agents/planner.md +0 -237
  1086. package/plugins/cursor-pbr/agents/researcher.md +0 -185
  1087. package/plugins/cursor-pbr/agents/synthesizer.md +0 -125
  1088. package/plugins/cursor-pbr/agents/verifier.md +0 -227
  1089. package/plugins/cursor-pbr/assets/.gitkeep +0 -0
  1090. package/plugins/cursor-pbr/assets/logo.svg +0 -21
  1091. package/plugins/cursor-pbr/hooks/hooks.json +0 -203
  1092. package/plugins/cursor-pbr/references/agent-anti-patterns.md +0 -25
  1093. package/plugins/cursor-pbr/references/agent-contracts.md +0 -297
  1094. package/plugins/cursor-pbr/references/agent-interactions.md +0 -135
  1095. package/plugins/cursor-pbr/references/agent-teams.md +0 -55
  1096. package/plugins/cursor-pbr/references/checkpoints.md +0 -158
  1097. package/plugins/cursor-pbr/references/common-bug-patterns.md +0 -14
  1098. package/plugins/cursor-pbr/references/config-reference.md +0 -442
  1099. package/plugins/cursor-pbr/references/continuation-format.md +0 -213
  1100. package/plugins/cursor-pbr/references/deviation-rules.md +0 -113
  1101. package/plugins/cursor-pbr/references/git-integration.md +0 -227
  1102. package/plugins/cursor-pbr/references/integration-patterns.md +0 -118
  1103. package/plugins/cursor-pbr/references/model-profiles.md +0 -100
  1104. package/plugins/cursor-pbr/references/model-selection.md +0 -32
  1105. package/plugins/cursor-pbr/references/pbr-rules.md +0 -195
  1106. package/plugins/cursor-pbr/references/pbr-tools-cli.md +0 -285
  1107. package/plugins/cursor-pbr/references/plan-authoring.md +0 -182
  1108. package/plugins/cursor-pbr/references/plan-format.md +0 -288
  1109. package/plugins/cursor-pbr/references/planning-config.md +0 -214
  1110. package/plugins/cursor-pbr/references/questioning.md +0 -215
  1111. package/plugins/cursor-pbr/references/reading-verification.md +0 -128
  1112. package/plugins/cursor-pbr/references/stub-patterns.md +0 -161
  1113. package/plugins/cursor-pbr/references/subagent-coordination.md +0 -120
  1114. package/plugins/cursor-pbr/references/ui-formatting.md +0 -444
  1115. package/plugins/cursor-pbr/references/verification-patterns.md +0 -199
  1116. package/plugins/cursor-pbr/references/wave-execution.md +0 -96
  1117. package/plugins/cursor-pbr/rules/pbr-workflow.mdc +0 -48
  1118. package/plugins/cursor-pbr/setup.ps1 +0 -78
  1119. package/plugins/cursor-pbr/setup.sh +0 -83
  1120. package/plugins/cursor-pbr/skills/begin/SKILL.md +0 -589
  1121. package/plugins/cursor-pbr/skills/begin/templates/PROJECT.md.tmpl +0 -34
  1122. package/plugins/cursor-pbr/skills/begin/templates/REQUIREMENTS.md.tmpl +0 -19
  1123. package/plugins/cursor-pbr/skills/begin/templates/STATE.md.tmpl +0 -50
  1124. package/plugins/cursor-pbr/skills/begin/templates/config.json.tmpl +0 -64
  1125. package/plugins/cursor-pbr/skills/begin/templates/researcher-prompt.md.tmpl +0 -20
  1126. package/plugins/cursor-pbr/skills/begin/templates/roadmap-prompt.md.tmpl +0 -31
  1127. package/plugins/cursor-pbr/skills/begin/templates/synthesis-prompt.md.tmpl +0 -17
  1128. package/plugins/cursor-pbr/skills/build/SKILL.md +0 -956
  1129. package/plugins/cursor-pbr/skills/config/SKILL.md +0 -252
  1130. package/plugins/cursor-pbr/skills/continue/SKILL.md +0 -159
  1131. package/plugins/cursor-pbr/skills/dashboard/SKILL.md +0 -44
  1132. package/plugins/cursor-pbr/skills/debug/SKILL.md +0 -512
  1133. package/plugins/cursor-pbr/skills/debug/templates/continuation-prompt.md.tmpl +0 -17
  1134. package/plugins/cursor-pbr/skills/debug/templates/initial-investigation-prompt.md.tmpl +0 -28
  1135. package/plugins/cursor-pbr/skills/discuss/SKILL.md +0 -354
  1136. package/plugins/cursor-pbr/skills/discuss/templates/CONTEXT.md.tmpl +0 -62
  1137. package/plugins/cursor-pbr/skills/discuss/templates/decision-categories.md +0 -10
  1138. package/plugins/cursor-pbr/skills/do/SKILL.md +0 -67
  1139. package/plugins/cursor-pbr/skills/explore/SKILL.md +0 -376
  1140. package/plugins/cursor-pbr/skills/health/SKILL.md +0 -274
  1141. package/plugins/cursor-pbr/skills/health/templates/check-pattern.md.tmpl +0 -31
  1142. package/plugins/cursor-pbr/skills/health/templates/output-format.md.tmpl +0 -64
  1143. package/plugins/cursor-pbr/skills/help/SKILL.md +0 -152
  1144. package/plugins/cursor-pbr/skills/import/SKILL.md +0 -505
  1145. package/plugins/cursor-pbr/skills/milestone/SKILL.md +0 -746
  1146. package/plugins/cursor-pbr/skills/milestone/templates/audit-report.md.tmpl +0 -49
  1147. package/plugins/cursor-pbr/skills/milestone/templates/stats-file.md.tmpl +0 -31
  1148. package/plugins/cursor-pbr/skills/note/SKILL.md +0 -214
  1149. package/plugins/cursor-pbr/skills/pause/SKILL.md +0 -248
  1150. package/plugins/cursor-pbr/skills/pause/templates/continue-here.md.tmpl +0 -72
  1151. package/plugins/cursor-pbr/skills/plan/SKILL.md +0 -663
  1152. package/plugins/cursor-pbr/skills/plan/templates/checker-prompt.md.tmpl +0 -22
  1153. package/plugins/cursor-pbr/skills/plan/templates/gap-closure-prompt.md.tmpl +0 -33
  1154. package/plugins/cursor-pbr/skills/plan/templates/planner-prompt.md.tmpl +0 -39
  1155. package/plugins/cursor-pbr/skills/plan/templates/researcher-prompt.md.tmpl +0 -20
  1156. package/plugins/cursor-pbr/skills/plan/templates/revision-prompt.md.tmpl +0 -24
  1157. package/plugins/cursor-pbr/skills/quick/SKILL.md +0 -376
  1158. package/plugins/cursor-pbr/skills/resume/SKILL.md +0 -399
  1159. package/plugins/cursor-pbr/skills/review/SKILL.md +0 -654
  1160. package/plugins/cursor-pbr/skills/review/templates/debugger-prompt.md.tmpl +0 -61
  1161. package/plugins/cursor-pbr/skills/review/templates/gap-planner-prompt.md.tmpl +0 -41
  1162. package/plugins/cursor-pbr/skills/review/templates/verifier-prompt.md.tmpl +0 -116
  1163. package/plugins/cursor-pbr/skills/scan/SKILL.md +0 -300
  1164. package/plugins/cursor-pbr/skills/scan/templates/mapper-prompt.md.tmpl +0 -202
  1165. package/plugins/cursor-pbr/skills/setup/SKILL.md +0 -296
  1166. package/plugins/cursor-pbr/skills/shared/commit-planning-docs.md +0 -36
  1167. package/plugins/cursor-pbr/skills/shared/config-loading.md +0 -103
  1168. package/plugins/cursor-pbr/skills/shared/context-budget.md +0 -41
  1169. package/plugins/cursor-pbr/skills/shared/context-loader-task.md +0 -87
  1170. package/plugins/cursor-pbr/skills/shared/digest-select.md +0 -80
  1171. package/plugins/cursor-pbr/skills/shared/domain-probes.md +0 -126
  1172. package/plugins/cursor-pbr/skills/shared/error-reporting.md +0 -81
  1173. package/plugins/cursor-pbr/skills/shared/gate-prompts.md +0 -389
  1174. package/plugins/cursor-pbr/skills/shared/phase-argument-parsing.md +0 -46
  1175. package/plugins/cursor-pbr/skills/shared/progress-display.md +0 -53
  1176. package/plugins/cursor-pbr/skills/shared/revision-loop.md +0 -82
  1177. package/plugins/cursor-pbr/skills/shared/state-loading.md +0 -63
  1178. package/plugins/cursor-pbr/skills/shared/state-update.md +0 -162
  1179. package/plugins/cursor-pbr/skills/shared/universal-anti-patterns.md +0 -38
  1180. package/plugins/cursor-pbr/skills/status/SKILL.md +0 -362
  1181. package/plugins/cursor-pbr/skills/statusline/SKILL.md +0 -150
  1182. package/plugins/cursor-pbr/skills/todo/SKILL.md +0 -280
  1183. package/plugins/cursor-pbr/templates/CONTEXT.md.tmpl +0 -53
  1184. package/plugins/cursor-pbr/templates/INTEGRATION-REPORT.md.tmpl +0 -152
  1185. package/plugins/cursor-pbr/templates/RESEARCH-SUMMARY.md.tmpl +0 -98
  1186. package/plugins/cursor-pbr/templates/ROADMAP.md.tmpl +0 -41
  1187. package/plugins/cursor-pbr/templates/SUMMARY.md.tmpl +0 -82
  1188. package/plugins/cursor-pbr/templates/VERIFICATION-DETAIL.md.tmpl +0 -117
  1189. package/plugins/cursor-pbr/templates/codebase/ARCHITECTURE.md.tmpl +0 -98
  1190. package/plugins/cursor-pbr/templates/codebase/CONCERNS.md.tmpl +0 -93
  1191. package/plugins/cursor-pbr/templates/codebase/CONVENTIONS.md.tmpl +0 -104
  1192. package/plugins/cursor-pbr/templates/codebase/INTEGRATIONS.md.tmpl +0 -78
  1193. package/plugins/cursor-pbr/templates/codebase/STACK.md.tmpl +0 -78
  1194. package/plugins/cursor-pbr/templates/codebase/STRUCTURE.md.tmpl +0 -80
  1195. package/plugins/cursor-pbr/templates/codebase/TESTING.md.tmpl +0 -107
  1196. package/plugins/cursor-pbr/templates/continue-here.md.tmpl +0 -74
  1197. package/plugins/cursor-pbr/templates/prompt-partials/phase-project-context.md.tmpl +0 -38
  1198. package/plugins/cursor-pbr/templates/research/ARCHITECTURE.md.tmpl +0 -124
  1199. package/plugins/cursor-pbr/templates/research/STACK.md.tmpl +0 -71
  1200. package/plugins/cursor-pbr/templates/research/SUMMARY.md.tmpl +0 -112
  1201. package/plugins/cursor-pbr/templates/research-outputs/phase-research.md.tmpl +0 -81
  1202. package/plugins/cursor-pbr/templates/research-outputs/project-research.md.tmpl +0 -99
  1203. package/plugins/cursor-pbr/templates/research-outputs/synthesis.md.tmpl +0 -36
  1204. package/plugins/pbr/references/agent-interactions.md +0 -134
  1205. package/plugins/pbr/references/checkpoints.md +0 -157
  1206. package/plugins/pbr/references/pbr-rules.md +0 -194
  1207. package/plugins/pbr/references/planning-config.md +0 -213
  1208. package/plugins/pbr/references/subagent-coordination.md +0 -119
  1209. package/plugins/pbr/references/ui-formatting.md +0 -444
  1210. package/plugins/pbr/references/verification-patterns.md +0 -198
  1211. package/plugins/pbr/scripts/validate-plugin-structure.js +0 -183
  1212. package/plugins/pbr/skills/milestone/templates/audit-report.md.tmpl +0 -48
  1213. package/plugins/pbr/skills/shared/progress-display.md +0 -53
  1214. package/plugins/pbr/skills/shared/state-loading.md +0 -62
  1215. package/plugins/pbr/templates/RESEARCH-SUMMARY.md.tmpl +0 -97
  1216. package/plugins/pbr/templates/research/ARCHITECTURE.md.tmpl +0 -124
  1217. package/plugins/pbr/templates/research/STACK.md.tmpl +0 -71
  1218. package/plugins/pbr/templates/research/SUMMARY.md.tmpl +0 -112
  1219. package/plugins/pbr/templates/research-outputs/phase-research.md.tmpl +0 -81
  1220. package/plugins/pbr/templates/research-outputs/project-research.md.tmpl +0 -99
  1221. package/plugins/pbr/templates/research-outputs/synthesis.md.tmpl +0 -36
  1222. /package/plugins/pbr/references/{agent-anti-patterns.md → archive/agent-anti-patterns.md} +0 -0
@@ -3,8 +3,16 @@
3
3
  /**
4
4
  * pbr-tools.js — Structured JSON state operations for Plan-Build-Run skills.
5
5
  *
6
- * Provides read-only commands that return JSON, replacing LLM-based text parsing
7
- * of STATE.md, ROADMAP.md, and config.json. Skills call this via:
6
+ * Thin dispatcher that imports from lib/ modules. All core logic lives in:
7
+ * lib/core.js — Foundation utilities (parsers, file ops, constants)
8
+ * lib/config.js — Config loading, validation, depth profiles
9
+ * lib/state.js — STATE.md operations (load, update, patch, advance)
10
+ * lib/roadmap.js — ROADMAP.md operations (parse, update status/plans)
11
+ * lib/phase.js — Phase operations (add, remove, list, info, plan-index)
12
+ * lib/init.js — Compound init commands (execute-phase, plan-phase, etc.)
13
+ * lib/history.js — History operations (append to STATE.md ## History, load with HISTORY.md fallback)
14
+ *
15
+ * Skills call this via:
8
16
  * node ${CLAUDE_PLUGIN_ROOT}/scripts/pbr-tools.js <command> [args]
9
17
  *
10
18
  * Commands:
@@ -18,196 +26,777 @@
18
26
  * phase-info <phase> — Comprehensive single-phase status → JSON
19
27
  * roadmap update-status <phase> <status> — Update phase status in ROADMAP.md
20
28
  * roadmap update-plans <phase> <complete> <total> — Update phase plans in ROADMAP.md
21
- * history append <type> <title> [body] — Append record to HISTORY.md
22
- * history load — Load all HISTORY.md records as JSON
29
+ * history append <type> <title> [body] — Append record to STATE.md ## History (fallback: HISTORY.md)
30
+ * history load — Load history records as JSON (STATE.md first, HISTORY.md fallback)
31
+ * todo list [--theme X] [--status Y] — List todos as JSON (default: pending)
32
+ * todo get <NNN> — Get a specific todo by number
33
+ * todo add <title> [--priority P] [--theme T] — Add a new todo
34
+ * todo done <NNN> — Mark a todo as complete
35
+ * auto-cleanup --phase N | --milestone vN — Auto-close todos and archive notes matching phase/milestone deliverables
36
+ * llm metrics [--session <ISO>] — Lifetime or session-scoped LLM usage metrics
37
+ * validate-project — Comprehensive .planning/ integrity check
38
+ * phase add <slug> [--after N] [--goal "..."] [--depends-on N] — Add phase with ROADMAP.md integration
39
+ * phase remove <N> — Remove an empty phase directory (with renumbering)
40
+ * phase list — List all phase directories with status
41
+ * phase complete <N> — Mark phase N complete, advance STATE.md to next phase
42
+ * phase insert <N> <slug> [--goal "..."] [--depends-on N] — Insert phase at position N, renumber subsequent
43
+ * phase commits-for <N> — Read .phase-manifest.json for phase N, output commits JSON. Falls back to git log
44
+ * phase first-last-commit <N> — Output { first, last } commit hashes from manifest or git log
45
+ * learnings ingest <json-file> — Ingest a learning entry into global store
46
+ * learnings query [--tags X] [--min-confidence Y] [--stack S] [--type T] — Query learnings
47
+ * learnings check-thresholds — Check deferral trigger conditions
48
+ * learnings copy-global <path> <proj> — Copy cross_project LEARNINGS.md to ~/.claude/pbr-knowledge/
49
+ * learnings query-global [--tags X] [--project P] — Query global knowledge files
50
+ * spot-check <phaseSlug> <planId> — Verify SUMMARY, key_files, and commits exist for a plan
51
+ * staleness-check <phase-slug> — Check if phase plans are stale vs dependencies
52
+ * summary-gate <phase-slug> <plan-id> — Verify SUMMARY.md exists, non-empty, valid frontmatter
53
+ * checkpoint init <phase-slug> [--plans "id1,id2"] — Initialize checkpoint manifest
54
+ * checkpoint update <phase-slug> --wave N --resolved id [--sha hash] — Update manifest
55
+ * seeds match <phase-slug> <phase-number> — Find matching seed files for a phase
56
+ * session get <key> — Read a key from .planning/.session.json
57
+ * session set <key> <value> — Write a key to .planning/.session.json
58
+ * session clear [key] — Delete .session.json or set key to null
59
+ * session dump — Print entire .session.json content
60
+ * skill-section <skill> <section> — Extract a section from a skill's SKILL.md → JSON
61
+ * skill-section --list <skill> — List all headings in a skill → JSON
62
+ * step-verify [skill] [step] [checklist-json] — Validate per-step completion checklist → JSON
63
+ * build-preview [phase-slug] — Preview what /pbr:execute-phase would do for a phase → JSON
64
+ * claim acquire <phase-slug> --session-id <id> --skill <name> — Acquire phase claim
65
+ * claim release <phase-slug> --session-id <id> — Release phase claim
66
+ * claim list — List all active phase claims
67
+ * suggest-alternatives phase-not-found [slug] — List available phases for unknown slug → JSON
68
+ * suggest-alternatives missing-prereq [phase] — List missing prerequisites for a phase → JSON
69
+ * suggest-alternatives config-invalid [field] [val] — List valid values for invalid config field → JSON
70
+ *
71
+ * Environment: PBR_PROJECT_ROOT — Override project root directory (used when hooks fire from subagent cwd)
23
72
  */
24
73
 
25
74
  const fs = require('fs');
26
75
  const path = require('path');
27
76
 
28
- const cwd = process.cwd();
29
- const planningDir = path.join(cwd, '.planning');
77
+ // --- Import lib modules ---
78
+ const {
79
+ KNOWN_AGENTS,
80
+ VALID_STATUS_TRANSITIONS,
81
+ validateStatusTransition,
82
+ output,
83
+ error,
84
+ parseYamlFrontmatter,
85
+ parseMustHaves,
86
+ findFiles,
87
+ tailLines,
88
+ countMustHaves,
89
+ determinePhaseStatus,
90
+ atomicWrite,
91
+ lockedFileUpdate,
92
+ writeActiveSkill,
93
+ sessionLoad,
94
+ sessionSave,
95
+ SESSION_ALLOWED_KEYS,
96
+ STALE_SESSION_MS,
97
+ resolveSessionPath,
98
+ acquireClaim,
99
+ releaseClaim,
100
+ releaseSessionClaims: _releaseSessionClaims,
101
+ listClaims: _listClaims
102
+ } = require('./lib/core');
103
+
104
+ const {
105
+ configLoad: _configLoad,
106
+ configClearCache: _configClearCache,
107
+ configValidate: _configValidate,
108
+ configFormat: _configFormat,
109
+ configWrite: _configWrite,
110
+ resolveDepthProfile,
111
+ DEPTH_PROFILE_DEFAULTS,
112
+ loadUserDefaults,
113
+ saveUserDefaults,
114
+ mergeUserDefaults,
115
+ USER_DEFAULTS_PATH
116
+ } = require('./lib/config');
117
+
118
+ const {
119
+ parseStateMd,
120
+ updateLegacyStateField,
121
+ updateFrontmatterField,
122
+ stateLoad: _stateLoad,
123
+ stateCheckProgress: _stateCheckProgress,
124
+ stateUpdate: _stateUpdate,
125
+ statePatch: _statePatch,
126
+ stateAdvancePlan: _stateAdvancePlan,
127
+ stateRecordMetric: _stateRecordMetric,
128
+ stateRecordActivity: _stateRecordActivity,
129
+ stateUpdateProgress: _stateUpdateProgress
130
+ } = require('./lib/state');
131
+
132
+ const {
133
+ parseRoadmapMd,
134
+ findRoadmapRow,
135
+ updateTableRow,
136
+ roadmapUpdateStatus: _roadmapUpdateStatus,
137
+ roadmapUpdatePlans: _roadmapUpdatePlans,
138
+ roadmapAnalyze: _roadmapAnalyze,
139
+ roadmapAppendPhase: _roadmapAppendPhase,
140
+ roadmapRemovePhase: _roadmapRemovePhase,
141
+ roadmapRenumberPhases: _roadmapRenumberPhases,
142
+ roadmapInsertPhase: _roadmapInsertPhase
143
+ } = require('./lib/roadmap');
144
+
145
+ const {
146
+ frontmatter: _frontmatter,
147
+ planIndex: _planIndex,
148
+ mustHavesCollect: _mustHavesCollect,
149
+ phaseInfo: _phaseInfo,
150
+ phaseAdd: _phaseAdd,
151
+ phaseRemove: _phaseRemove,
152
+ phaseList: _phaseList,
153
+ milestoneStats: _milestoneStats,
154
+ phaseComplete: _phaseComplete,
155
+ phaseInsert: _phaseInsert
156
+ } = require('./lib/phase');
157
+
158
+ const {
159
+ initExecutePhase: _initExecutePhase,
160
+ initPlanPhase: _initPlanPhase,
161
+ initQuick: _initQuick,
162
+ initVerifyWork: _initVerifyWork,
163
+ initResume: _initResume,
164
+ initProgress: _initProgress,
165
+ initStateBundle: _initStateBundle
166
+ } = require('./lib/init');
167
+
168
+ const {
169
+ historyAppend: _historyAppend,
170
+ historyLoad: _historyLoad
171
+ } = require('./lib/history');
172
+
173
+ const {
174
+ todoList: _todoList,
175
+ todoGet: _todoGet,
176
+ todoAdd: _todoAdd,
177
+ todoDone: _todoDone
178
+ } = require('./lib/todo');
179
+
180
+ const {
181
+ autoCloseTodos: _autoCloseTodos,
182
+ autoArchiveNotes: _autoArchiveNotes
183
+ } = require('./lib/auto-cleanup');
184
+
185
+ const {
186
+ applyMigrations: _applyMigrations
187
+ } = require('./lib/migrate');
188
+
189
+ const {
190
+ spotCheck: _spotCheck,
191
+ verifySpotCheck: _verifySpotCheck
192
+ } = require('./lib/spot-check');
193
+
194
+ const {
195
+ learningsIngest: _learningsIngest,
196
+ learningsQuery: _learningsQuery,
197
+ checkDeferralThresholds: _checkDeferralThresholds,
198
+ copyToGlobal: _copyToGlobal,
199
+ queryGlobal: _queryGlobal
200
+ } = require('./lib/learnings');
201
+
202
+ const {
203
+ referenceGet: _referenceGet
204
+ } = require('./lib/reference');
205
+
206
+ const {
207
+ skillSection: _skillSection,
208
+ listAvailableSkills: _listAvailableSkills
209
+ } = require('./lib/skill-section');
210
+
211
+ const {
212
+ stepVerify: _stepVerify
213
+ } = require('./lib/step-verify');
214
+
215
+ const {
216
+ buildPreview: _buildPreview
217
+ } = require('./lib/preview');
218
+
219
+ const {
220
+ contextTriage: _contextTriage
221
+ } = require('./lib/context');
222
+
223
+ const {
224
+ phaseAlternatives: _phaseAlternatives,
225
+ prerequisiteAlternatives: _prereqAlternatives,
226
+ configAlternatives: _configAlternatives
227
+ } = require('./lib/alternatives');
228
+
229
+ const {
230
+ stalenessCheck: _stalenessCheck,
231
+ summaryGate: _summaryGate,
232
+ checkpointInit: _checkpointInit,
233
+ checkpointUpdate: _checkpointUpdate,
234
+ seedsMatch: _seedsMatch,
235
+ ciPoll: _ciPoll,
236
+ rollback: _rollback
237
+ } = require('./lib/build');
238
+
239
+ const {
240
+ parseJestOutput: _parseJestOutput,
241
+ parseLintOutput: _parseLintOutput,
242
+ autoFixLint: _autoFixLint,
243
+ runCiFixLoop: _runCiFixLoop
244
+ } = require('./lib/ci-fix-loop');
245
+
246
+ const {
247
+ statusRender: _statusRender
248
+ } = require('./lib/status-render');
249
+
250
+ const {
251
+ suggestNext: _suggestNext
252
+ } = require('./lib/suggest-next');
253
+
254
+ const {
255
+ quickStatus: _quickStatus
256
+ } = require('./quick-status');
257
+
258
+ // --- Local LLM imports (not extracted — separate module tree) ---
259
+ const { resolveConfig, checkHealth } = require('./local-llm/health');
260
+ const { classifyArtifact } = require('./local-llm/operations/classify-artifact');
261
+ const { scoreSource } = require('./local-llm/operations/score-source');
262
+ const { classifyError } = require('./local-llm/operations/classify-error');
263
+ const { summarizeContext } = require('./local-llm/operations/summarize-context');
264
+ const { readSessionMetrics, summarizeMetrics, computeLifetimeMetrics } = require('./local-llm/metrics');
265
+ const { computeThresholdAdjustments } = require('./local-llm/threshold-tuner');
266
+
267
+ // --- Module-level state (for backwards compatibility) ---
268
+
269
+ let cwd = process.env.PBR_PROJECT_ROOT || process.cwd();
270
+ // MSYS path bridging: Git Bash on Windows can produce /d/Repos/... paths
271
+ // that Node.js cannot resolve. Convert to D:\Repos\... form.
272
+ const _msysCwdMatch = cwd.match(/^\/([a-zA-Z])\/(.*)/);
273
+ if (_msysCwdMatch) cwd = _msysCwdMatch[1] + ':' + path.sep + _msysCwdMatch[2].replace(/\//g, path.sep);
274
+ let planningDir = path.join(cwd, '.planning');
275
+
276
+ // --- Wrapper functions that pass planningDir to lib modules ---
277
+ // These preserve the original function signatures (no planningDir param)
278
+ // so existing callers (hook scripts, tests) continue to work.
30
279
 
31
- // --- Phase status transition state machine ---
280
+ function configLoad(dir) {
281
+ return _configLoad(dir || planningDir);
282
+ }
32
283
 
33
- /**
34
- * Valid phase status transitions. Each key is a current status, and its value
35
- * is an array of statuses that are legal to transition to. This is advisory —
36
- * invalid transitions produce a stderr warning but are not blocked, to avoid
37
- * breaking existing workflows.
38
- *
39
- * State machine:
40
- * pending -> planned, skipped
41
- * planned -> building
42
- * building -> built, partial, needs_fixes
43
- * built -> verified, needs_fixes
44
- * partial -> building, needs_fixes
45
- * verified -> building (re-execution)
46
- * needs_fixes -> planned, building
47
- * skipped -> pending (unskip)
48
- */
49
- const VALID_STATUS_TRANSITIONS = {
50
- pending: ['planned', 'skipped'],
51
- planned: ['building'],
52
- building: ['built', 'partial', 'needs_fixes'],
53
- built: ['verified', 'needs_fixes'],
54
- partial: ['building', 'needs_fixes'],
55
- verified: ['building'],
56
- needs_fixes: ['planned', 'building'],
57
- skipped: ['pending']
58
- };
284
+ function configClearCache() {
285
+ _configClearCache();
286
+ cwd = process.env.PBR_PROJECT_ROOT || process.cwd();
287
+ const _msysResetMatch = cwd.match(/^\/([a-zA-Z])\/(.*)/);
288
+ if (_msysResetMatch) cwd = _msysResetMatch[1] + ':' + path.sep + _msysResetMatch[2].replace(/\//g, path.sep);
289
+ planningDir = path.join(cwd, '.planning');
290
+ }
59
291
 
60
- /**
61
- * Check whether a phase status transition is valid according to the state machine.
62
- * Returns { valid, warning? } — never blocks, only advises.
63
- *
64
- * @param {string} oldStatus - Current phase status
65
- * @param {string} newStatus - Desired phase status
66
- * @returns {{ valid: boolean, warning?: string }}
67
- */
68
- function validateStatusTransition(oldStatus, newStatus) {
69
- const from = (oldStatus || '').trim().toLowerCase();
70
- const to = (newStatus || '').trim().toLowerCase();
292
+ function configValidate(preloadedConfig) {
293
+ return _configValidate(preloadedConfig, planningDir);
294
+ }
71
295
 
72
- // If the status isn't changing, that's always fine
73
- if (from === to) {
74
- return { valid: true };
75
- }
296
+ function stateLoad() {
297
+ return _stateLoad(planningDir);
298
+ }
76
299
 
77
- // If the old status is unknown to our map, we can't validate — allow it
78
- if (!VALID_STATUS_TRANSITIONS[from]) {
79
- return { valid: true };
80
- }
300
+ function stateCheckProgress() {
301
+ return _stateCheckProgress(planningDir);
302
+ }
81
303
 
82
- const allowed = VALID_STATUS_TRANSITIONS[from];
83
- if (allowed.includes(to)) {
84
- return { valid: true };
85
- }
304
+ function stateUpdate(field, value) {
305
+ return _stateUpdate(field, value, planningDir);
306
+ }
86
307
 
87
- return {
88
- valid: false,
89
- warning: `Suspicious status transition: "${from}" -> "${to}". Expected one of: [${allowed.join(', ')}]. Proceeding anyway (advisory).`
90
- };
308
+ function statePatch(jsonStr) {
309
+ return _statePatch(jsonStr, planningDir);
91
310
  }
92
311
 
93
- // --- Cached config loader ---
312
+ function stateAdvancePlan() {
313
+ return _stateAdvancePlan(planningDir);
314
+ }
94
315
 
95
- let _configCache = null;
96
- let _configMtime = 0;
97
- let _configPath = null;
316
+ function stateRecordMetric(metricArgs) {
317
+ return _stateRecordMetric(metricArgs, planningDir);
318
+ }
98
319
 
99
- /**
100
- * Load config.json with in-process mtime-based caching.
101
- * Returns the parsed config object, or null if not found / parse error.
102
- * Cache invalidates when file mtime changes or path differs.
103
- *
104
- * @param {string} [dir] - Path to .planning directory (defaults to cwd/.planning)
105
- * @returns {object|null} Parsed config or null
106
- */
107
- function configLoad(dir) {
108
- const configPath = path.join(dir || planningDir, 'config.json');
109
- try {
110
- if (!fs.existsSync(configPath)) return null;
111
- const stat = fs.statSync(configPath);
112
- const mtime = stat.mtimeMs;
113
- if (_configCache && mtime === _configMtime && configPath === _configPath) {
114
- return _configCache;
115
- }
116
- _configCache = JSON.parse(fs.readFileSync(configPath, 'utf8'));
117
- _configMtime = mtime;
118
- _configPath = configPath;
119
- return _configCache;
120
- } catch (_e) {
121
- return null;
320
+ function stateRecordActivity(description) {
321
+ return _stateRecordActivity(description, planningDir);
322
+ }
323
+
324
+ function stateUpdateProgress() {
325
+ return _stateUpdateProgress(planningDir);
326
+ }
327
+
328
+ function roadmapAnalyze() {
329
+ return _roadmapAnalyze(planningDir);
330
+ }
331
+
332
+ function roadmapUpdateStatus(phaseNum, newStatus) {
333
+ return _roadmapUpdateStatus(phaseNum, newStatus, planningDir);
334
+ }
335
+
336
+ function roadmapUpdatePlans(phaseNum, complete, total) {
337
+ return _roadmapUpdatePlans(phaseNum, complete, total, planningDir);
338
+ }
339
+
340
+ function frontmatter(filePath) {
341
+ return _frontmatter(filePath);
342
+ }
343
+
344
+ function planIndex(phaseNum) {
345
+ return _planIndex(phaseNum, planningDir);
346
+ }
347
+
348
+ function mustHavesCollect(phaseNum) {
349
+ return _mustHavesCollect(phaseNum, planningDir);
350
+ }
351
+
352
+ function phaseInfo(phaseNum) {
353
+ return _phaseInfo(phaseNum, planningDir);
354
+ }
355
+
356
+ function phaseAdd(slug, afterPhase, options) {
357
+ return _phaseAdd(slug, afterPhase, planningDir, options);
358
+ }
359
+
360
+ function phaseRemove(phaseNum) {
361
+ return _phaseRemove(phaseNum, planningDir);
362
+ }
363
+
364
+ function phaseList() {
365
+ return _phaseList(planningDir);
366
+ }
367
+
368
+ function phaseComplete(phaseNum) {
369
+ return _phaseComplete(phaseNum, planningDir);
370
+ }
371
+
372
+ function phaseInsert(position, slug, options) {
373
+ return _phaseInsert(position, slug, planningDir, options);
374
+ }
375
+
376
+ function milestoneStats(version) {
377
+ return _milestoneStats(version, planningDir);
378
+ }
379
+
380
+ function initExecutePhase(phaseNum, overridePlanningDir, overrideModel) {
381
+ return _initExecutePhase(phaseNum, overridePlanningDir || planningDir, overrideModel);
382
+ }
383
+
384
+ function initPlanPhase(phaseNum, overridePlanningDir, overrideModel) {
385
+ return _initPlanPhase(phaseNum, overridePlanningDir || planningDir, overrideModel);
386
+ }
387
+
388
+ function initQuick(description) {
389
+ return _initQuick(description, planningDir);
390
+ }
391
+
392
+ function initVerifyWork(phaseNum, overridePlanningDir, overrideModel) {
393
+ return _initVerifyWork(phaseNum, overridePlanningDir || planningDir, overrideModel);
394
+ }
395
+
396
+ function initResume() {
397
+ return _initResume(planningDir);
398
+ }
399
+
400
+ function initProgress() {
401
+ return _initProgress(planningDir);
402
+ }
403
+
404
+ function stateBundle(phaseNum) {
405
+ return _initStateBundle(phaseNum, planningDir);
406
+ }
407
+
408
+ function historyAppend(entry, dir) {
409
+ return _historyAppend(entry, dir || planningDir);
410
+ }
411
+
412
+ function historyLoad(dir) {
413
+ return _historyLoad(dir || planningDir);
414
+ }
415
+
416
+ function todoList(opts) {
417
+ return _todoList(planningDir, opts);
418
+ }
419
+
420
+ function todoGet(num) {
421
+ return _todoGet(planningDir, num);
422
+ }
423
+
424
+ function todoAdd(title, opts) {
425
+ return _todoAdd(planningDir, title, opts);
426
+ }
427
+
428
+ function todoDone(num) {
429
+ return _todoDone(planningDir, num);
430
+ }
431
+
432
+ function autoCloseTodos(context) {
433
+ return _autoCloseTodos(planningDir, context);
434
+ }
435
+
436
+ function autoArchiveNotes(context) {
437
+ return _autoArchiveNotes(planningDir, context);
438
+ }
439
+
440
+ function migrate(options) {
441
+ return _applyMigrations(planningDir, options);
442
+ }
443
+
444
+ function spotCheck(phaseDir, planId) {
445
+ return _spotCheck(planningDir, phaseDir, planId);
446
+ }
447
+
448
+ function verifySpotCheck(type, dirPath) {
449
+ return _verifySpotCheck(type, dirPath);
450
+ }
451
+
452
+ function referenceGet(name, options) {
453
+ // Resolve plugin root — try CLAUDE_PLUGIN_ROOT env, then walk up from __dirname
454
+ const pluginRoot = process.env.CLAUDE_PLUGIN_ROOT || path.resolve(__dirname, '..');
455
+ // Fix MSYS paths on Windows (same pattern as run-hook.js)
456
+ let root = pluginRoot;
457
+ const msysMatch = root.match(/^\/([a-zA-Z])\/(.*)/);
458
+ if (msysMatch) root = msysMatch[1] + ':' + path.sep + msysMatch[2];
459
+ return _referenceGet(name, options, root);
460
+ }
461
+
462
+ function resolvePluginRoot() {
463
+ // Resolve plugin root — try CLAUDE_PLUGIN_ROOT env, then walk up from __dirname
464
+ const pluginRoot = process.env.CLAUDE_PLUGIN_ROOT || path.resolve(__dirname, '..');
465
+ // Fix MSYS paths on Windows (same pattern as run-hook.js)
466
+ let root = pluginRoot;
467
+ const msysMatch = root.match(/^\/([a-zA-Z])\/(.*)/);
468
+ if (msysMatch) root = msysMatch[1] + ':' + path.sep + msysMatch[2];
469
+ return root;
470
+ }
471
+
472
+ function skillSectionGet(skillName, sectionQuery) {
473
+ return _skillSection(skillName, sectionQuery, resolvePluginRoot());
474
+ }
475
+
476
+ function listSkillHeadings(skillName) {
477
+ const { listHeadings } = require('./lib/reference');
478
+ const root = resolvePluginRoot();
479
+ const skillPath = require('path').join(root, 'skills', skillName, 'SKILL.md');
480
+ if (!require('fs').existsSync(skillPath)) {
481
+ return { error: `Skill not found: ${skillName}`, available: _listAvailableSkills(root) };
122
482
  }
483
+ const content = require('fs').readFileSync(skillPath, 'utf8');
484
+ return { skill: skillName, headings: listHeadings(content) };
485
+ }
486
+
487
+ function contextTriage(options) {
488
+ return _contextTriage(options, planningDir);
123
489
  }
124
490
 
491
+ function stalenessCheck(phaseSlug) { return _stalenessCheck(phaseSlug, planningDir); }
492
+ function summaryGate(phaseSlug, planId) { return _summaryGate(phaseSlug, planId, planningDir); }
493
+ function checkpointInit(phaseSlug, plans) { return _checkpointInit(phaseSlug, plans, planningDir); }
494
+ function checkpointUpdate(phaseSlug, opts) { return _checkpointUpdate(phaseSlug, opts, planningDir); }
495
+ function seedsMatch(phaseSlug, phaseNum) { return _seedsMatch(phaseSlug, phaseNum, planningDir); }
496
+ function ciPoll(runId, timeoutSecs) { return _ciPoll(runId, timeoutSecs, planningDir); }
497
+ function ciFix(options) { return _runCiFixLoop({ ...options, cwd: path.resolve('.') }); }
498
+ function rollbackPlan(manifestPath) { return _rollback(manifestPath, planningDir); }
499
+
500
+ function quickStatus() { return _quickStatus(planningDir); }
501
+
125
502
  /**
126
- * Clear the configLoad() in-process cache.
127
- * Useful in tests where multiple temp directories are used in rapid succession.
503
+ * Build cleanup context from phase SUMMARY files and git log.
504
+ * @param {string} phaseNum - Phase number (e.g. "38")
505
+ * @returns {{ phaseName: string, phaseNum: string, keyFiles: string[], commitMessages: string[], summaryDescriptions: string[] }}
128
506
  */
129
- function configClearCache() {
130
- _configCache = null;
131
- _configMtime = 0;
132
- _configPath = null;
507
+ function buildCleanupContext(phaseNum) {
508
+ const padded = String(phaseNum).padStart(2, '0');
509
+ const phasesDir = path.join(planningDir, 'phases');
510
+ if (!fs.existsSync(phasesDir)) throw new Error('No phases directory found');
511
+
512
+ const phaseDir = fs.readdirSync(phasesDir).find(d => d.startsWith(padded + '-'));
513
+ if (!phaseDir) throw new Error(`Phase ${phaseNum} directory not found`);
514
+
515
+ const phaseName = phaseDir.replace(/^\d+-/, '').replace(/-/g, ' ');
516
+ const phaseDirPath = path.join(phasesDir, phaseDir);
517
+
518
+ // Collect key_files and descriptions from all SUMMARY files
519
+ const keyFiles = [];
520
+ const summaryDescriptions = [];
521
+ const summaryFiles = fs.readdirSync(phaseDirPath).filter(f => /^SUMMARY/i.test(f) && f.endsWith('.md'));
522
+ for (const sf of summaryFiles) {
523
+ try {
524
+ const content = fs.readFileSync(path.join(phaseDirPath, sf), 'utf8');
525
+ const fm = parseYamlFrontmatter(content);
526
+ if (fm.key_files && Array.isArray(fm.key_files)) {
527
+ keyFiles.push(...fm.key_files.map(kf => typeof kf === 'string' ? kf.split(':')[0].trim() : ''));
528
+ }
529
+ if (fm.provides && Array.isArray(fm.provides)) {
530
+ summaryDescriptions.push(...fm.provides);
531
+ }
532
+ } catch (_e) { /* skip unreadable summaries */ }
533
+ }
534
+
535
+ // Get recent commit messages
536
+ let commitMessages = [];
537
+ try {
538
+ const { execSync } = require('child_process');
539
+ const log = execSync('git log --oneline -20', { encoding: 'utf8', cwd: path.join(planningDir, '..') });
540
+ commitMessages = log.split('\n').filter(l => l.trim()).map(l => {
541
+ const parts = l.match(/^[0-9a-f]+\s+(.*)/);
542
+ return parts ? parts[1] : '';
543
+ }).filter(Boolean);
544
+ } catch (_e) { /* git not available */ }
545
+
546
+ return { phaseName, phaseNum: String(phaseNum), keyFiles, commitMessages, summaryDescriptions };
133
547
  }
134
548
 
549
+ // --- Phase commit query functions ---
550
+
135
551
  /**
136
- * Read the last N lines from a file efficiently.
137
- * Reads the entire file but only parses (JSON.parse) the trailing entries.
138
- * For JSONL files where full parsing is expensive, this avoids parsing
139
- * all lines when you only need recent entries.
140
- *
141
- * @param {string} filePath - Absolute path to the file
142
- * @param {number} n - Number of trailing lines to return
143
- * @returns {string[]} Array of raw line strings (last n lines)
552
+ * Read .phase-manifest.json for phase N, output JSON array of commits.
553
+ * Falls back to git log if no manifest exists.
144
554
  */
145
- function tailLines(filePath, n) {
555
+ function _phaseCommitsFor(phaseNum) {
556
+ const padded = String(phaseNum).padStart(2, '0');
557
+ const phasesDir = path.join(planningDir, 'phases');
558
+ if (!fs.existsSync(phasesDir)) return { error: 'No phases directory found' };
559
+
560
+ const phaseDir = fs.readdirSync(phasesDir).find(d => d.startsWith(padded + '-'));
561
+ if (!phaseDir) return { error: `Phase ${phaseNum} directory not found` };
562
+
563
+ const manifestPath = path.join(phasesDir, phaseDir, '.phase-manifest.json');
564
+ if (fs.existsSync(manifestPath)) {
565
+ try {
566
+ const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
567
+ return { source: 'manifest', commits: manifest.commits || [] };
568
+ } catch (_e) { /* fall through to git log */ }
569
+ }
570
+
571
+ // Fallback: scan git log for commits matching this phase scope
146
572
  try {
147
- if (!fs.existsSync(filePath)) return [];
148
- const content = fs.readFileSync(filePath, 'utf8').trim();
149
- if (!content) return [];
150
- const lines = content.split('\n');
151
- if (lines.length <= n) return lines;
152
- return lines.slice(lines.length - n);
573
+ const { execSync } = require('child_process');
574
+ const log = execSync('git log --oneline --no-merges -100', { encoding: 'utf8' });
575
+ const phasePattern = new RegExp(`\\((${padded}-|phase.*${phaseNum})`, 'i');
576
+ const commits = log.split('\n')
577
+ .filter(l => l.trim() && phasePattern.test(l))
578
+ .map(l => {
579
+ const parts = l.match(/^([0-9a-f]+)\s+(.*)/);
580
+ return parts ? { hash: parts[1], message: parts[2] } : null;
581
+ })
582
+ .filter(Boolean);
583
+ return { source: 'git_log', commits };
153
584
  } catch (_e) {
154
- return [];
585
+ return { error: 'Could not read git log', commits: [] };
155
586
  }
156
587
  }
157
588
 
158
589
  /**
159
- * Built-in depth profile defaults. These define the effective settings
160
- * for each depth level. User config.depth_profiles overrides these.
590
+ * Output { first, last } commit hashes from phase manifest or git log.
161
591
  */
162
- const DEPTH_PROFILE_DEFAULTS = {
163
- quick: {
164
- 'features.research_phase': false,
165
- 'features.plan_checking': false,
166
- 'features.goal_verification': false,
167
- 'features.inline_verify': false,
168
- 'scan.mapper_count': 2,
169
- 'scan.mapper_areas': ['tech', 'arch'],
170
- 'debug.max_hypothesis_rounds': 3
171
- },
172
- standard: {
173
- 'features.research_phase': true,
174
- 'features.plan_checking': true,
175
- 'features.goal_verification': true,
176
- 'features.inline_verify': false,
177
- 'scan.mapper_count': 4,
178
- 'scan.mapper_areas': ['tech', 'arch', 'quality', 'concerns'],
179
- 'debug.max_hypothesis_rounds': 5
180
- },
181
- comprehensive: {
182
- 'features.research_phase': true,
183
- 'features.plan_checking': true,
184
- 'features.goal_verification': true,
185
- 'features.inline_verify': true,
186
- 'scan.mapper_count': 4,
187
- 'scan.mapper_areas': ['tech', 'arch', 'quality', 'concerns'],
188
- 'debug.max_hypothesis_rounds': 10
189
- }
190
- };
592
+ function _phaseFirstLastCommit(phaseNum) {
593
+ const result = _phaseCommitsFor(phaseNum);
594
+ if (result.error && !result.commits) return result;
595
+ const commits = result.commits || [];
596
+ return {
597
+ source: result.source,
598
+ first: commits.length > 0 ? commits[0].hash : null,
599
+ last: commits.length > 0 ? commits[commits.length - 1].hash : null,
600
+ total: commits.length
601
+ };
602
+ }
603
+
604
+ // --- Claim wrapper functions ---
605
+
606
+ function claimAcquire(phaseSlug, sessionId, skill) {
607
+ const phaseDir = path.join(planningDir, 'phases', phaseSlug);
608
+ if (!fs.existsSync(phaseDir)) return { error: `Phase directory not found: ${phaseSlug}` };
609
+ return acquireClaim(planningDir, phaseDir, sessionId, skill);
610
+ }
611
+
612
+ function claimRelease(phaseSlug, sessionId) {
613
+ const phaseDir = path.join(planningDir, 'phases', phaseSlug);
614
+ if (!fs.existsSync(phaseDir)) return { error: `Phase directory not found: ${phaseSlug}` };
615
+ return releaseClaim(planningDir, phaseDir, sessionId);
616
+ }
617
+
618
+ function claimList() {
619
+ return _listClaims(planningDir);
620
+ }
621
+
622
+ // --- validateProject stays here (cross-cutting across modules) ---
191
623
 
192
624
  /**
193
- * Resolve the effective depth profile for the current config.
194
- * Merges built-in defaults with any user overrides from config.depth_profiles.
195
- *
196
- * @param {object|null} config - Parsed config.json (from configLoad). If null, returns 'standard' defaults.
197
- * @returns {{ depth: string, profile: object }} The resolved depth name and flattened profile settings.
625
+ * Comprehensive .planning/ integrity check.
626
+ * Returns { valid, errors, warnings, checks } errors mean workflow should not proceed.
198
627
  */
199
- function resolveDepthProfile(config) {
200
- const depth = (config && config.depth) || 'standard';
201
- const defaults = DEPTH_PROFILE_DEFAULTS[depth] || DEPTH_PROFILE_DEFAULTS.standard;
628
+ function validateProject() {
629
+ const checks = [];
630
+ const errors = [];
631
+ const warnings = [];
632
+
633
+ // 1. .planning/ directory exists
634
+ if (!fs.existsSync(planningDir)) {
635
+ return { valid: false, errors: ['.planning/ directory not found'], warnings: [], checks: ['directory_exists: FAIL'] };
636
+ }
637
+ checks.push('directory_exists: PASS');
638
+
639
+ // 2. config.json exists and is valid
640
+ const config = configLoad();
641
+ if (!config) {
642
+ errors.push('config.json missing or invalid JSON');
643
+ checks.push('config_valid: FAIL');
644
+ } else {
645
+ const configResult = configValidate(config);
646
+ if (!configResult.valid) {
647
+ errors.push(...configResult.errors.map(e => 'config: ' + e));
648
+ }
649
+ warnings.push(...(configResult.warnings || []).map(w => 'config: ' + w));
650
+ checks.push('config_valid: ' + (configResult.valid ? 'PASS' : 'FAIL'));
651
+ }
652
+
653
+ // 3. STATE.md exists and has valid frontmatter
654
+ const statePath = path.join(planningDir, 'STATE.md');
655
+ if (!fs.existsSync(statePath)) {
656
+ errors.push('STATE.md not found');
657
+ checks.push('state_exists: FAIL');
658
+ } else {
659
+ try {
660
+ const stateContent = fs.readFileSync(statePath, 'utf8');
661
+ const fm = parseYamlFrontmatter(stateContent);
662
+ if (!fm || !fm.current_phase) {
663
+ warnings.push('STATE.md frontmatter missing current_phase');
664
+ checks.push('state_frontmatter: WARN');
665
+ } else {
666
+ checks.push('state_frontmatter: PASS');
667
+ }
668
+ } catch (e) {
669
+ errors.push('STATE.md unreadable: ' + e.message);
670
+ checks.push('state_readable: FAIL');
671
+ }
672
+ }
673
+
674
+ // 4. ROADMAP.md exists
675
+ const roadmapPath = path.join(planningDir, 'ROADMAP.md');
676
+ if (!fs.existsSync(roadmapPath)) {
677
+ warnings.push('ROADMAP.md not found (may be a new project)');
678
+ checks.push('roadmap_exists: WARN');
679
+ } else {
680
+ checks.push('roadmap_exists: PASS');
681
+ }
682
+
683
+ // 5. Phase directory matches STATE.md current_phase
684
+ try {
685
+ if (fs.existsSync(statePath)) {
686
+ const stateContent = fs.readFileSync(statePath, 'utf8');
687
+ const fm = parseYamlFrontmatter(stateContent);
688
+ if (fm && fm.current_phase) {
689
+ const phaseNum = String(fm.current_phase).padStart(2, '0');
690
+ const phasesDir = path.join(planningDir, 'phases');
691
+ if (fs.existsSync(phasesDir)) {
692
+ const dirs = fs.readdirSync(phasesDir).filter(d => d.startsWith(phaseNum + '-'));
693
+ if (dirs.length === 0) {
694
+ warnings.push(`Phase directory for current_phase ${fm.current_phase} not found in .planning/phases/`);
695
+ checks.push('phase_directory: WARN');
696
+ } else {
697
+ checks.push('phase_directory: PASS');
698
+ }
699
+ }
700
+ }
701
+ }
702
+ } catch (_e) { /* best effort */ }
703
+
704
+ // 6. No stale .active-skill (>2 hours old)
705
+ const activeSkillPath = path.join(planningDir, '.active-skill');
706
+ if (fs.existsSync(activeSkillPath)) {
707
+ try {
708
+ const stat = fs.statSync(activeSkillPath);
709
+ const ageMs = Date.now() - stat.mtimeMs;
710
+ if (ageMs > 2 * 60 * 60 * 1000) {
711
+ const ageHours = Math.round(ageMs / (60 * 60 * 1000));
712
+ warnings.push(`.active-skill is ${ageHours}h old — may be stale from a crashed session`);
713
+ checks.push('active_skill_fresh: WARN');
714
+ } else {
715
+ checks.push('active_skill_fresh: PASS');
716
+ }
717
+ } catch (_e) { checks.push('active_skill_fresh: SKIP'); }
718
+ } else {
719
+ checks.push('active_skill_fresh: SKIP');
720
+ }
721
+
722
+ // 7. No .tmp files left from atomic writes
723
+ try {
724
+ const tmpFiles = fs.readdirSync(planningDir).filter(f => f.endsWith('.tmp.' + process.pid) || f.match(/\.tmp\.\d+$/));
725
+ if (tmpFiles.length > 0) {
726
+ warnings.push(`Found ${tmpFiles.length} leftover temp files in .planning/: ${tmpFiles.join(', ')}`);
727
+ checks.push('no_temp_files: WARN');
728
+ } else {
729
+ checks.push('no_temp_files: PASS');
730
+ }
731
+ } catch (_e) { /* best effort */ }
732
+
733
+ // 8. Session directory scan — count active, flag stale
734
+ const sessionsResult = { count: 0, active: [], stale: [] };
735
+ const sessionsDir = path.join(planningDir, '.sessions');
736
+ try {
737
+ if (fs.existsSync(sessionsDir)) {
738
+ const entries = fs.readdirSync(sessionsDir, { withFileTypes: true });
739
+ for (const entry of entries) {
740
+ if (!entry.isDirectory()) continue;
741
+ sessionsResult.count++;
742
+ sessionsResult.active.push(entry.name);
743
+
744
+ // Check for staleness via meta.json
745
+ const metaPath = path.join(sessionsDir, entry.name, 'meta.json');
746
+ try {
747
+ const meta = JSON.parse(fs.readFileSync(metaPath, 'utf8'));
748
+ const ageMs = Date.now() - new Date(meta.created).getTime();
749
+ if (ageMs > STALE_SESSION_MS) {
750
+ sessionsResult.stale.push(entry.name);
751
+ }
752
+ } catch (_metaErr) {
753
+ // Fall back to directory mtime
754
+ try {
755
+ const stats = fs.statSync(path.join(sessionsDir, entry.name));
756
+ const ageMs = Date.now() - stats.mtimeMs;
757
+ if (ageMs > STALE_SESSION_MS) {
758
+ sessionsResult.stale.push(entry.name);
759
+ }
760
+ } catch (_statErr) { /* skip */ }
761
+ }
762
+ }
763
+
764
+ if (sessionsResult.stale.length > 0) {
765
+ warnings.push(`${sessionsResult.stale.length} stale session(s) found: ${sessionsResult.stale.join(', ')}. Run cleanStaleSessions to remove.`);
766
+ checks.push('sessions_stale: WARN');
767
+ } else {
768
+ checks.push('sessions_stale: PASS');
769
+ }
202
770
 
203
- // Merge user overrides if present
204
- const userOverrides = (config && config.depth_profiles && config.depth_profiles[depth]) || {};
205
- const profile = { ...defaults, ...userOverrides };
771
+ // Check for singleton .active-skill coexisting with session-scoped ones
772
+ if (fs.existsSync(path.join(planningDir, '.active-skill'))) {
773
+ let hasSessionSkill = false;
774
+ for (const sid of sessionsResult.active) {
775
+ if (fs.existsSync(path.join(sessionsDir, sid, '.active-skill'))) {
776
+ hasSessionSkill = true;
777
+ break;
778
+ }
779
+ }
780
+ if (hasSessionSkill) {
781
+ warnings.push('.active-skill exists at both singleton and session-scoped paths — possible migration artifact. Consider removing the singleton .planning/.active-skill.');
782
+ checks.push('active_skill_dual: WARN');
783
+ }
784
+ }
785
+ }
786
+ } catch (_e) { /* best effort */ }
206
787
 
207
- return { depth, profile };
788
+ return {
789
+ valid: errors.length === 0,
790
+ errors,
791
+ warnings,
792
+ checks,
793
+ sessions: sessionsResult
794
+ };
208
795
  }
209
796
 
210
- function main() {
797
+ // --- CLI entry point ---
798
+
799
+ async function main() {
211
800
  const args = process.argv.slice(2);
212
801
  const command = args[0];
213
802
  const subcommand = args[1];
@@ -221,11 +810,27 @@ function main() {
221
810
  const field = args[2];
222
811
  const value = args[3];
223
812
  if (!field || value === undefined) {
224
- error('Usage: pbr-tools.js state update <field> <value>\nFields: current_phase, status, plans_complete, last_activity');
813
+ error('Usage: pbr-tools.js state update <field> <value>\nFields: current_phase, status, plans_complete, last_activity, progress_percent, phase_slug, last_command, blockers');
225
814
  }
226
815
  output(stateUpdate(field, value));
227
816
  } else if (command === 'config' && subcommand === 'validate') {
228
817
  output(configValidate());
818
+ } else if (command === 'validate' && subcommand === 'health') {
819
+ const { getAllPhase10Checks } = require('./lib/health-checks');
820
+ const checks = getAllPhase10Checks(planningDir);
821
+ output({ phase10: checks, timestamp: new Date().toISOString() });
822
+ } else if (command === 'config' && subcommand === 'load-defaults') {
823
+ const defaults = loadUserDefaults();
824
+ output(defaults || { exists: false, path: USER_DEFAULTS_PATH });
825
+ } else if (command === 'config' && subcommand === 'save-defaults') {
826
+ const config = configLoad();
827
+ if (!config) error('No config.json found. Run /pbr:setup first.');
828
+ output(saveUserDefaults(config));
829
+ } else if (command === 'config' && subcommand === 'format') {
830
+ const config = configLoad();
831
+ if (!config) error('No config.json found.');
832
+ _configWrite(planningDir, config);
833
+ output({ formatted: true, path: path.join(planningDir, 'config.json') });
229
834
  } else if (command === 'config' && subcommand === 'resolve-depth') {
230
835
  const dir = args[2] || undefined;
231
836
  const config = configLoad(dir);
@@ -269,6 +874,8 @@ function main() {
269
874
  error('Usage: pbr-tools.js roadmap update-plans <phase-number> <complete> <total>');
270
875
  }
271
876
  output(roadmapUpdatePlans(phase, complete, total));
877
+ } else if (command === 'roadmap' && subcommand === 'analyze') {
878
+ output(roadmapAnalyze());
272
879
  } else if (command === 'history' && subcommand === 'append') {
273
880
  const type = args[2]; // 'milestone' or 'phase'
274
881
  const title = args[3];
@@ -292,1087 +899,627 @@ function main() {
292
899
  const { logEvent } = require('./event-logger');
293
900
  logEvent(category, event, details);
294
901
  output({ logged: true, category, event });
295
- } else {
296
- error(`Unknown command: ${args.join(' ')}\nCommands: state load|check-progress|update, config validate, plan-index, frontmatter, must-haves, phase-info, roadmap update-status|update-plans, history append|load, event`);
297
- }
298
- } catch (e) {
299
- error(e.message);
300
- }
301
- }
302
-
303
- // --- Commands ---
304
-
305
- function stateLoad() {
306
- const result = {
307
- exists: false,
308
- config: null,
309
- state: null,
310
- roadmap: null,
311
- phase_count: 0,
312
- current_phase: null,
313
- progress: null
314
- };
315
-
316
- if (!fs.existsSync(planningDir)) {
317
- return result;
318
- }
319
- result.exists = true;
320
-
321
- // Load config.json
322
- const configPath = path.join(planningDir, 'config.json');
323
- if (fs.existsSync(configPath)) {
324
- try {
325
- result.config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
326
- } catch (_) {
327
- result.config = { _error: 'Failed to parse config.json' };
328
- }
329
- }
330
-
331
- // Load STATE.md
332
- const statePath = path.join(planningDir, 'STATE.md');
333
- if (fs.existsSync(statePath)) {
334
- const content = fs.readFileSync(statePath, 'utf8');
335
- result.state = parseStateMd(content);
336
- }
902
+ } else if (command === 'llm' && subcommand === 'health') {
903
+ let rawConfig = {};
904
+ try { rawConfig = configLoad(planningDir) || {}; } catch (_e) { /* use defaults */ }
905
+ const llmConfig = resolveConfig(rawConfig.local_llm);
906
+ const health = await checkHealth(llmConfig);
907
+ output(health);
908
+ } else if (command === 'llm' && subcommand === 'status') {
909
+ let rawConfig = {};
910
+ try { rawConfig = configLoad(planningDir) || {}; } catch (_e) { /* use defaults */ }
911
+ const llmConfig = resolveConfig(rawConfig.local_llm);
912
+ output({
913
+ enabled: llmConfig.enabled,
914
+ model: llmConfig.model,
915
+ endpoint: llmConfig.endpoint,
916
+ features: llmConfig.features,
917
+ metrics_file: path.join(planningDir, 'logs', 'local-llm-metrics.jsonl'),
918
+ timeout_ms: llmConfig.timeout_ms,
919
+ disable_after_failures: llmConfig.advanced.disable_after_failures
920
+ });
921
+ } else if (command === 'llm' && subcommand === 'classify') {
922
+ const fileType = args[2];
923
+ const filePath = args[3];
924
+ if (!fileType || !filePath) {
925
+ error('Usage: pbr-tools.js llm classify <PLAN|SUMMARY> <filepath>');
926
+ }
927
+ const upperType = fileType.toUpperCase();
928
+ if (upperType !== 'PLAN' && upperType !== 'SUMMARY') {
929
+ error('llm classify: fileType must be PLAN or SUMMARY');
930
+ }
931
+ let content = '';
932
+ try {
933
+ content = fs.readFileSync(filePath, 'utf8');
934
+ } catch (_e) {
935
+ error('llm classify: cannot read file: ' + filePath);
936
+ }
937
+ let rawConfig = {};
938
+ try { rawConfig = configLoad(planningDir) || {}; } catch (_e) { /* use defaults */ }
939
+ const llmConfig = resolveConfig(rawConfig.local_llm);
940
+ const result = await classifyArtifact(llmConfig, planningDir, content, upperType, undefined);
941
+ output(result || { classification: null, reason: 'LLM disabled or unavailable' });
942
+ } else if (command === 'llm' && subcommand === 'score-source') {
943
+ const sourceUrl = args[2];
944
+ const filePath = args[3];
945
+ if (!sourceUrl || !filePath) {
946
+ error('Usage: pbr-tools.js llm score-source <url> <file-path>');
947
+ }
948
+ if (!fs.existsSync(filePath)) {
949
+ error('File not found: ' + filePath);
950
+ }
951
+ const content = fs.readFileSync(filePath, 'utf8');
952
+ let rawConfig = {};
953
+ try { rawConfig = configLoad(planningDir) || {}; } catch (_e) { /* use defaults */ }
954
+ const llmConfig = resolveConfig(rawConfig.local_llm);
955
+ const result = await scoreSource(llmConfig, planningDir, content, sourceUrl, undefined);
956
+ output(result || { level: null, reason: 'LLM disabled or unavailable' });
957
+ } else if (command === 'llm' && subcommand === 'classify-error') {
958
+ const filePath = args[2];
959
+ const agentType = args[3] || 'unknown';
960
+ if (!filePath) {
961
+ error('Usage: pbr-tools.js llm classify-error <file-path> [agent-type]');
962
+ }
963
+ if (!fs.existsSync(filePath)) {
964
+ error('File not found: ' + filePath);
965
+ }
966
+ const errorText = fs.readFileSync(filePath, 'utf8');
967
+ let rawConfig = {};
968
+ try { rawConfig = configLoad(planningDir) || {}; } catch (_e) { /* use defaults */ }
969
+ const llmConfig = resolveConfig(rawConfig.local_llm);
970
+ const result = await classifyError(llmConfig, planningDir, errorText, agentType, undefined);
971
+ output(result || { category: null, reason: 'LLM disabled or unavailable' });
972
+ } else if (command === 'llm' && subcommand === 'summarize') {
973
+ const filePath = args[2];
974
+ const maxWords = args[3] ? parseInt(args[3], 10) : undefined;
975
+ if (!filePath) {
976
+ error('Usage: pbr-tools.js llm summarize <file-path> [max-words]');
977
+ }
978
+ if (!fs.existsSync(filePath)) {
979
+ error('File not found: ' + filePath);
980
+ }
981
+ const contextText = fs.readFileSync(filePath, 'utf8');
982
+ let rawConfig = {};
983
+ try { rawConfig = configLoad(planningDir) || {}; } catch (_e) { /* use defaults */ }
984
+ const llmConfig = resolveConfig(rawConfig.local_llm);
985
+ const result = await summarizeContext(llmConfig, planningDir, contextText, maxWords, undefined);
986
+ output(result || { summary: null, reason: 'LLM disabled or unavailable' });
987
+ } else if (command === 'llm' && subcommand === 'metrics') {
988
+ const sessionFlag = args[2]; // '--session'
989
+ const sessionStart = args[3]; // ISO timestamp
990
+ let rawConfig = {};
991
+ try { rawConfig = configLoad(planningDir) || {}; } catch (_e) { /* defaults */ }
992
+ const rate = rawConfig.local_llm && rawConfig.local_llm.metrics && rawConfig.local_llm.metrics.frontier_token_rate
993
+ ? rawConfig.local_llm.metrics.frontier_token_rate : 3.0;
994
+ if (sessionFlag === '--session' && sessionStart) {
995
+ const entries = readSessionMetrics(planningDir, sessionStart);
996
+ const summary = summarizeMetrics(entries, rate);
997
+ output({ scope: 'session', session_start: sessionStart, ...summary });
998
+ } else {
999
+ const lifetime = computeLifetimeMetrics(planningDir, rate);
1000
+ output({ scope: 'lifetime', ...lifetime });
1001
+ }
1002
+ } else if (command === 'llm' && subcommand === 'adjust-thresholds') {
1003
+ let rawConfig = {};
1004
+ try { rawConfig = configLoad(planningDir) || {}; } catch (_e) { /* use defaults */ }
1005
+ const llmConfig = resolveConfig(rawConfig.local_llm);
1006
+ const currentThreshold = llmConfig.advanced.confidence_threshold;
1007
+ const suggestions = computeThresholdAdjustments(planningDir, currentThreshold);
1008
+ output(suggestions.length > 0
1009
+ ? { suggestions }
1010
+ : { suggestions: [], message: 'Not enough shadow samples yet (need >= 20 per operation)' });
1011
+ // --- Compound init commands ---
1012
+ } else if (command === "init" && subcommand === "execute-phase") {
1013
+ const phase = args[2];
1014
+ if (!phase) error("Usage: pbr-tools.js init execute-phase <phase-number>");
1015
+ output(initExecutePhase(phase));
1016
+ } else if (command === "init" && subcommand === "plan-phase") {
1017
+ const phase = args[2];
1018
+ if (!phase) error("Usage: pbr-tools.js init plan-phase <phase-number>");
1019
+ output(initPlanPhase(phase));
1020
+ } else if (command === "init" && subcommand === "quick") {
1021
+ const desc = args.slice(2).join(" ") || "";
1022
+ output(initQuick(desc));
1023
+ } else if (command === "init" && subcommand === "verify-work") {
1024
+ const phase = args[2];
1025
+ if (!phase) error("Usage: pbr-tools.js init verify-work <phase-number>");
1026
+ output(initVerifyWork(phase));
1027
+ } else if (command === "init" && subcommand === "resume") {
1028
+ output(initResume());
1029
+ } else if (command === "init" && subcommand === "progress") {
1030
+ output(initProgress());
1031
+ } else if (command === 'state-bundle') {
1032
+ const phaseNum = args[1];
1033
+ if (!phaseNum) error('Usage: pbr-tools.js state-bundle <phase-number>');
1034
+ output(stateBundle(phaseNum));
1035
+ // --- State patch/advance/metric ---
1036
+ } else if (command === "state" && subcommand === "patch") {
1037
+ const jsonStr = args[2];
1038
+ if (!jsonStr) error("Usage: pbr-tools.js state patch JSON");
1039
+ output(statePatch(jsonStr));
1040
+ } else if (command === "state" && subcommand === "advance-plan") {
1041
+ output(stateAdvancePlan());
1042
+ } else if (command === "state" && subcommand === "record-metric") {
1043
+ output(stateRecordMetric(args.slice(2)));
1044
+ } else if (command === "state" && subcommand === "record-activity") {
1045
+ const description = args.slice(2).join(' ');
1046
+ if (!description) error("Usage: pbr-tools.js state record-activity <description>");
1047
+ output(stateRecordActivity(description));
1048
+ } else if (command === "state" && subcommand === "update-progress") {
1049
+ output(stateUpdateProgress());
1050
+ } else if (command === 'phase' && subcommand === 'add') {
1051
+ const slug = args[2];
1052
+ if (!slug) { error('Usage: phase add <slug> [--after N] [--goal "..."] [--depends-on N]'); }
1053
+ const afterIdx = args.indexOf('--after');
1054
+ const afterPhase = afterIdx !== -1 ? args[afterIdx + 1] : null;
1055
+ const goalIdx = args.indexOf('--goal');
1056
+ const goal = goalIdx !== -1 ? args[goalIdx + 1] : null;
1057
+ const depIdx = args.indexOf('--depends-on');
1058
+ const dependsOn = depIdx !== -1 ? args[depIdx + 1] : null;
1059
+ const addOpts = {};
1060
+ if (goal) addOpts.goal = goal;
1061
+ if (dependsOn) addOpts.dependsOn = dependsOn;
1062
+ output(phaseAdd(slug, afterPhase, Object.keys(addOpts).length > 0 ? addOpts : undefined));
1063
+ } else if (command === 'phase' && subcommand === 'remove') {
1064
+ const phaseNum = args[2];
1065
+ if (!phaseNum) { error('Usage: phase remove <phase_num>'); }
1066
+ output(phaseRemove(phaseNum));
1067
+ } else if (command === 'phase' && subcommand === 'list') {
1068
+ output(phaseList());
1069
+ } else if (command === 'phase' && subcommand === 'complete') {
1070
+ const phaseNum = args[2];
1071
+ if (!phaseNum) { error('Usage: phase complete <phase_num>'); }
1072
+ output(phaseComplete(phaseNum));
1073
+ } else if (command === 'phase' && subcommand === 'insert') {
1074
+ const position = args[2];
1075
+ const slug = args[3];
1076
+ if (!position || !slug) { error('Usage: phase insert <N> <slug> [--goal "..."] [--depends-on N]'); }
1077
+ const goalIdx = args.indexOf('--goal');
1078
+ const goal = goalIdx !== -1 ? args[goalIdx + 1] : null;
1079
+ const depIdx = args.indexOf('--depends-on');
1080
+ const dependsOn = depIdx !== -1 ? args[depIdx + 1] : null;
1081
+ const insertOpts = {};
1082
+ if (goal) insertOpts.goal = goal;
1083
+ if (dependsOn) insertOpts.dependsOn = dependsOn;
1084
+ output(phaseInsert(parseInt(position, 10), slug, Object.keys(insertOpts).length > 0 ? insertOpts : undefined));
1085
+ } else if (command === 'phase' && subcommand === 'commits-for') {
1086
+ const phaseNum = args[2];
1087
+ if (!phaseNum) { error('Usage: phase commits-for <N>'); }
1088
+ output(_phaseCommitsFor(phaseNum));
1089
+ } else if (command === 'phase' && subcommand === 'first-last-commit') {
1090
+ const phaseNum = args[2];
1091
+ if (!phaseNum) { error('Usage: phase first-last-commit <N>'); }
1092
+ output(_phaseFirstLastCommit(phaseNum));
1093
+ } else if (command === 'todo' && subcommand === 'list') {
1094
+ const opts = {};
1095
+ const themeIdx = args.indexOf('--theme');
1096
+ if (themeIdx !== -1 && args[themeIdx + 1]) opts.theme = args[themeIdx + 1];
1097
+ const statusIdx = args.indexOf('--status');
1098
+ if (statusIdx !== -1 && args[statusIdx + 1]) opts.status = args[statusIdx + 1];
1099
+ output(todoList(opts));
1100
+ } else if (command === 'todo' && subcommand === 'get') {
1101
+ const num = args[2];
1102
+ if (!num) error('Usage: pbr-tools.js todo get <NNN>');
1103
+ output(todoGet(num));
1104
+ } else if (command === 'todo' && subcommand === 'add') {
1105
+ const titleParts = [];
1106
+ const opts = {};
1107
+ // Parse: todo add <title words...> [--priority P1] [--theme security] [--source cli]
1108
+ for (let i = 2; i < args.length; i++) {
1109
+ if (args[i] === '--priority' && args[i + 1]) { opts.priority = args[++i]; }
1110
+ else if (args[i] === '--theme' && args[i + 1]) { opts.theme = args[++i]; }
1111
+ else if (args[i] === '--source' && args[i + 1]) { opts.source = args[++i]; }
1112
+ else { titleParts.push(args[i]); }
1113
+ }
1114
+ const title = titleParts.join(' ');
1115
+ if (!title) error('Usage: pbr-tools.js todo add <title> [--priority P1|P2|P3] [--theme <theme>]');
1116
+ output(todoAdd(title, opts));
1117
+ } else if (command === 'todo' && subcommand === 'done') {
1118
+ const num = args[2];
1119
+ if (!num) error('Usage: pbr-tools.js todo done <NNN>');
1120
+ output(todoDone(num));
1121
+ } else if (command === 'auto-cleanup') {
1122
+ const phaseFlag = args.indexOf('--phase');
1123
+ const milestoneFlag = args.indexOf('--milestone');
1124
+ if (phaseFlag !== -1 && args[phaseFlag + 1]) {
1125
+ const phaseNum = args[phaseFlag + 1];
1126
+ const context = buildCleanupContext(phaseNum);
1127
+ const todoResult = autoCloseTodos(context);
1128
+ const noteResult = autoArchiveNotes(context);
1129
+ output({ phase: phaseNum, todos: todoResult, notes: noteResult });
1130
+ } else if (milestoneFlag !== -1 && args[milestoneFlag + 1]) {
1131
+ const version = args[milestoneFlag + 1];
1132
+ // Parse ROADMAP.md to find phases in this milestone
1133
+ const roadmapPath = path.join(planningDir, 'ROADMAP.md');
1134
+ if (!fs.existsSync(roadmapPath)) { error('ROADMAP.md not found'); }
1135
+ const roadmap = fs.readFileSync(roadmapPath, 'utf8');
1136
+ const milestoneMatch = roadmap.match(new RegExp('Milestone.*' + version.replace(/\./g, '\\.') + '[\\s\\S]*?Phases:\\s*(\\d+)\\s*-\\s*(\\d+)'));
1137
+ if (!milestoneMatch) { error('Milestone ' + version + ' not found in ROADMAP.md'); }
1138
+ const startPhase = parseInt(milestoneMatch[1]);
1139
+ const endPhase = parseInt(milestoneMatch[2]);
1140
+ const allResults = { milestone: version, phases: [] };
1141
+ for (let p = startPhase; p <= endPhase; p++) {
1142
+ try {
1143
+ const ctx = buildCleanupContext(String(p));
1144
+ const todoRes = autoCloseTodos(ctx);
1145
+ const noteRes = autoArchiveNotes(ctx);
1146
+ allResults.phases.push({ phase: p, todos: todoRes, notes: noteRes });
1147
+ } catch (_e) { /* skip phases without SUMMARY */ }
1148
+ }
1149
+ output(allResults);
1150
+ } else {
1151
+ error('Usage: auto-cleanup --phase N | --milestone vN');
1152
+ }
1153
+ } else if (command === 'migrate') {
1154
+ const dryRun = args.includes('--dry-run');
1155
+ const force = args.includes('--force');
1156
+ const result = await migrate({ dryRun, force });
1157
+ output(result);
1158
+ } else if (command === 'learnings') {
1159
+ const subCmd = args[1];
1160
+
1161
+ if (subCmd === 'ingest') {
1162
+ // learnings ingest <json-file-path>
1163
+ const jsonFile = args[2];
1164
+ if (!jsonFile) { error('Usage: learnings ingest <json-file>'); process.exit(1); }
1165
+ const raw = fs.readFileSync(jsonFile, 'utf8');
1166
+ const entry = JSON.parse(raw);
1167
+ const result = _learningsIngest(entry);
1168
+ output(result);
1169
+
1170
+ } else if (subCmd === 'query') {
1171
+ // learnings query [--tags tag1,tag2] [--min-confidence low|medium|high] [--stack react] [--type tech-pattern]
1172
+ const filters = {};
1173
+ for (let i = 2; i < args.length; i++) {
1174
+ if (args[i] === '--tags' && args[i + 1]) { filters.tags = args[++i].split(',').map(t => t.trim()); }
1175
+ else if (args[i] === '--min-confidence' && args[i + 1]) { filters.minConfidence = args[++i]; }
1176
+ else if (args[i] === '--stack' && args[i + 1]) { filters.stack = args[++i]; }
1177
+ else if (args[i] === '--type' && args[i + 1]) { filters.type = args[++i]; }
1178
+ }
1179
+ const results = _learningsQuery(filters);
1180
+ output(results);
1181
+
1182
+ } else if (subCmd === 'check-thresholds') {
1183
+ // learnings check-thresholds — for progress-tracker to call
1184
+ const triggered = _checkDeferralThresholds();
1185
+ output(triggered);
1186
+
1187
+ } else if (subCmd === 'copy-global') {
1188
+ const filePath = args[2];
1189
+ const projectName = args[3];
1190
+ if (!filePath || !projectName) { error('Usage: learnings copy-global <learnings-md-path> <project-name>'); process.exit(1); }
1191
+ output(_copyToGlobal(filePath, projectName));
1192
+
1193
+ } else if (subCmd === 'query-global') {
1194
+ const filters = {};
1195
+ for (let i = 2; i < args.length; i++) {
1196
+ if (args[i] === '--tags' && args[i + 1]) { filters.tags = args[++i].split(',').map(t => t.trim()); }
1197
+ else if (args[i] === '--project' && args[i + 1]) { filters.project = args[++i]; }
1198
+ }
1199
+ output(_queryGlobal(filters));
337
1200
 
338
- // Load ROADMAP.md
339
- const roadmapPath = path.join(planningDir, 'ROADMAP.md');
340
- if (fs.existsSync(roadmapPath)) {
341
- const content = fs.readFileSync(roadmapPath, 'utf8');
342
- result.roadmap = parseRoadmapMd(content);
343
- result.phase_count = result.roadmap.phases.length;
344
- }
345
-
346
- // Extract current phase
347
- if (result.state && result.state.current_phase) {
348
- result.current_phase = result.state.current_phase;
349
- }
350
-
351
- // Calculate progress
352
- result.progress = calculateProgress();
353
-
354
- return result;
355
- }
356
-
357
- function stateCheckProgress() {
358
- const phasesDir = path.join(planningDir, 'phases');
359
- if (!fs.existsSync(phasesDir)) {
360
- return { phases: [], total_plans: 0, completed_plans: 0, percentage: 0 };
361
- }
362
-
363
- const phases = [];
364
- let totalPlans = 0;
365
- let completedPlans = 0;
366
-
367
- const entries = fs.readdirSync(phasesDir, { withFileTypes: true })
368
- .filter(e => e.isDirectory())
369
- .sort((a, b) => a.name.localeCompare(b.name));
370
-
371
- for (const entry of entries) {
372
- const phaseDir = path.join(phasesDir, entry.name);
373
- const plans = findFiles(phaseDir, /-PLAN\.md$/);
374
- const summaries = findFiles(phaseDir, /^SUMMARY-.*\.md$/);
375
- const verification = fs.existsSync(path.join(phaseDir, 'VERIFICATION.md'));
376
-
377
- const completedSummaries = summaries.filter(s => {
378
- const content = fs.readFileSync(path.join(phaseDir, s), 'utf8');
379
- return /status:\s*["']?complete/i.test(content);
380
- });
381
-
382
- const phaseInfo = {
383
- directory: entry.name,
384
- plans: plans.length,
385
- summaries: summaries.length,
386
- completed: completedSummaries.length,
387
- has_verification: verification,
388
- status: determinePhaseStatus(plans.length, completedSummaries.length, summaries.length, verification, phaseDir)
389
- };
390
-
391
- phases.push(phaseInfo);
392
- totalPlans += plans.length;
393
- completedPlans += completedSummaries.length;
394
- }
395
-
396
- return {
397
- phases,
398
- total_plans: totalPlans,
399
- completed_plans: completedPlans,
400
- percentage: totalPlans > 0 ? Math.round((completedPlans / totalPlans) * 100) : 0
401
- };
402
- }
403
-
404
- function planIndex(phaseNum) {
405
- const phasesDir = path.join(planningDir, 'phases');
406
- if (!fs.existsSync(phasesDir)) {
407
- return { error: 'No phases directory found' };
408
- }
409
-
410
- // Find phase directory matching the number
411
- const entries = fs.readdirSync(phasesDir, { withFileTypes: true })
412
- .filter(e => e.isDirectory());
413
-
414
- const phaseDir = entries.find(e => e.name.startsWith(phaseNum.padStart(2, '0') + '-'));
415
- if (!phaseDir) {
416
- return { error: `No phase directory found matching phase ${phaseNum}` };
417
- }
418
-
419
- const fullDir = path.join(phasesDir, phaseDir.name);
420
- const planFiles = findFiles(fullDir, /-PLAN\.md$/);
421
-
422
- const plans = [];
423
- const waves = {};
424
-
425
- for (const file of planFiles) {
426
- const content = fs.readFileSync(path.join(fullDir, file), 'utf8');
427
- const frontmatter = parseYamlFrontmatter(content);
428
-
429
- const plan = {
430
- file,
431
- plan_id: frontmatter.plan || file.replace(/-PLAN\.md$/, ''),
432
- wave: parseInt(frontmatter.wave, 10) || 1,
433
- type: frontmatter.type || 'unknown',
434
- autonomous: frontmatter.autonomous !== false,
435
- depends_on: frontmatter.depends_on || [],
436
- gap_closure: frontmatter.gap_closure || false,
437
- has_summary: fs.existsSync(path.join(fullDir, `SUMMARY-${frontmatter.plan || ''}.md`)),
438
- must_haves_count: countMustHaves(frontmatter.must_haves)
439
- };
440
-
441
- plans.push(plan);
442
-
443
- const waveKey = `wave_${plan.wave}`;
444
- if (!waves[waveKey]) waves[waveKey] = [];
445
- waves[waveKey].push(plan.plan_id);
446
- }
447
-
448
- return {
449
- phase: phaseDir.name,
450
- total_plans: plans.length,
451
- plans,
452
- waves
453
- };
454
- }
455
-
456
- function configValidate(preloadedConfig) {
457
- let config;
458
- if (preloadedConfig) {
459
- config = preloadedConfig;
460
- } else {
461
- const configPath = path.join(planningDir, 'config.json');
462
- if (!fs.existsSync(configPath)) {
463
- return { valid: false, errors: ['config.json not found'], warnings: [] };
464
- }
465
-
466
- try {
467
- config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
468
- } catch (e) {
469
- return { valid: false, errors: [`config.json is not valid JSON: ${e.message}`], warnings: [] };
470
- }
471
- }
472
-
473
- const schema = JSON.parse(fs.readFileSync(path.join(__dirname, 'config-schema.json'), 'utf8'));
474
- const warnings = [];
475
- const errors = [];
476
-
477
- validateObject(config, schema, '', errors, warnings);
478
-
479
- // Semantic conflict detection — logical contradictions that pass schema validation
480
- // Clear contradictions → errors; ambiguous/preference issues → warnings
481
- if (config.mode === 'autonomous' && config.gates) {
482
- const activeGates = Object.entries(config.gates || {}).filter(([, v]) => v === true).map(([k]) => k);
483
- if (activeGates.length > 0) {
484
- errors.push(`mode=autonomous with active gates (${activeGates.join(', ')}): gates are unreachable in autonomous mode`);
485
- }
486
- }
487
- if (config.features && config.features.auto_continue && config.mode === 'interactive') {
488
- warnings.push('features.auto_continue=true with mode=interactive: auto_continue only fires in autonomous mode');
489
- }
490
- if (config.parallelization) {
491
- if (config.parallelization.enabled === false && config.parallelization.plan_level === true) {
492
- warnings.push('parallelization.enabled=false with plan_level=true: plan_level is ignored when parallelization is disabled');
493
- }
494
- if (config.parallelization.max_concurrent_agents === 1 && config.teams && config.teams.coordination) {
495
- errors.push('parallelization.max_concurrent_agents=1 with teams.coordination set: teams require concurrent agents to be useful');
496
- }
497
- }
498
-
499
- return {
500
- valid: errors.length === 0,
501
- errors,
502
- warnings
503
- };
504
- }
505
-
506
- // --- New read-only commands ---
507
-
508
- /**
509
- * Parse a markdown file's YAML frontmatter and return as JSON.
510
- * Wraps parseYamlFrontmatter() + parseMustHaves().
511
- */
512
- function frontmatter(filePath) {
513
- const resolved = path.resolve(filePath);
514
- if (!fs.existsSync(resolved)) {
515
- return { error: `File not found: ${resolved}` };
516
- }
517
- const content = fs.readFileSync(resolved, 'utf8');
518
- return parseYamlFrontmatter(content);
519
- }
520
-
521
- /**
522
- * Collect all must-haves from all PLAN.md files in a phase.
523
- * Returns per-plan grouping + flat deduplicated list + total count.
524
- */
525
- function mustHavesCollect(phaseNum) {
526
- const phasesDir = path.join(planningDir, 'phases');
527
- if (!fs.existsSync(phasesDir)) {
528
- return { error: 'No phases directory found' };
529
- }
530
-
531
- const entries = fs.readdirSync(phasesDir, { withFileTypes: true })
532
- .filter(e => e.isDirectory());
533
- const phaseDir = entries.find(e => e.name.startsWith(phaseNum.padStart(2, '0') + '-'));
534
- if (!phaseDir) {
535
- return { error: `No phase directory found matching phase ${phaseNum}` };
536
- }
537
-
538
- const fullDir = path.join(phasesDir, phaseDir.name);
539
- const planFiles = findFiles(fullDir, /-PLAN\.md$/);
540
-
541
- const perPlan = {};
542
- const allTruths = new Set();
543
- const allArtifacts = new Set();
544
- const allKeyLinks = new Set();
545
-
546
- for (const file of planFiles) {
547
- const content = fs.readFileSync(path.join(fullDir, file), 'utf8');
548
- const fm = parseYamlFrontmatter(content);
549
- const planId = fm.plan || file.replace(/-PLAN\.md$/, '');
550
- const mh = fm.must_haves || { truths: [], artifacts: [], key_links: [] };
551
-
552
- perPlan[planId] = mh;
553
- (mh.truths || []).forEach(t => allTruths.add(t));
554
- (mh.artifacts || []).forEach(a => allArtifacts.add(a));
555
- (mh.key_links || []).forEach(k => allKeyLinks.add(k));
556
- }
557
-
558
- const all = {
559
- truths: [...allTruths],
560
- artifacts: [...allArtifacts],
561
- key_links: [...allKeyLinks]
562
- };
563
-
564
- return {
565
- phase: phaseDir.name,
566
- plans: perPlan,
567
- all,
568
- total: all.truths.length + all.artifacts.length + all.key_links.length
569
- };
570
- }
571
-
572
- /**
573
- * Comprehensive single-phase status combining roadmap, filesystem, and plan data.
574
- */
575
- function phaseInfo(phaseNum) {
576
- const phasesDir = path.join(planningDir, 'phases');
577
- if (!fs.existsSync(phasesDir)) {
578
- return { error: 'No phases directory found' };
579
- }
580
-
581
- const entries = fs.readdirSync(phasesDir, { withFileTypes: true })
582
- .filter(e => e.isDirectory());
583
- const phaseDir = entries.find(e => e.name.startsWith(phaseNum.padStart(2, '0') + '-'));
584
- if (!phaseDir) {
585
- return { error: `No phase directory found matching phase ${phaseNum}` };
586
- }
587
-
588
- const fullDir = path.join(phasesDir, phaseDir.name);
589
-
590
- // Get roadmap info
591
- let roadmapInfo = null;
592
- const roadmapPath = path.join(planningDir, 'ROADMAP.md');
593
- if (fs.existsSync(roadmapPath)) {
594
- const roadmapContent = fs.readFileSync(roadmapPath, 'utf8');
595
- const roadmap = parseRoadmapMd(roadmapContent);
596
- roadmapInfo = roadmap.phases.find(p => p.number === phaseNum.padStart(2, '0')) || null;
597
- }
598
-
599
- // Get plan index
600
- const plans = planIndex(phaseNum);
601
-
602
- // Check for verification
603
- const verificationPath = path.join(fullDir, 'VERIFICATION.md');
604
- let verification = null;
605
- if (fs.existsSync(verificationPath)) {
606
- const vContent = fs.readFileSync(verificationPath, 'utf8');
607
- verification = parseYamlFrontmatter(vContent);
608
- }
609
-
610
- // Check summaries
611
- const summaryFiles = findFiles(fullDir, /^SUMMARY-.*\.md$/);
612
- const summaries = summaryFiles.map(f => {
613
- const content = fs.readFileSync(path.join(fullDir, f), 'utf8');
614
- const fm = parseYamlFrontmatter(content);
615
- return { file: f, plan: fm.plan || f.replace(/^SUMMARY-|\.md$/g, ''), status: fm.status || 'unknown' };
616
- });
617
-
618
- // Determine filesystem status
619
- const planCount = plans.total_plans || 0;
620
- const completedCount = summaries.filter(s => s.status === 'complete').length;
621
- const hasVerification = fs.existsSync(verificationPath);
622
- const fsStatus = determinePhaseStatus(planCount, completedCount, summaryFiles.length, hasVerification, fullDir);
623
-
624
- return {
625
- phase: phaseDir.name,
626
- name: roadmapInfo ? roadmapInfo.name : phaseDir.name.replace(/^\d+-/, ''),
627
- goal: roadmapInfo ? roadmapInfo.goal : null,
628
- roadmap_status: roadmapInfo ? roadmapInfo.status : null,
629
- filesystem_status: fsStatus,
630
- plans: plans.plans || [],
631
- plan_count: planCount,
632
- summaries,
633
- completed: completedCount,
634
- verification,
635
- has_context: fs.existsSync(path.join(fullDir, 'CONTEXT.md'))
636
- };
637
- }
638
-
639
- // --- Mutation commands ---
640
-
641
- /**
642
- * Atomically update a field in STATE.md using lockedFileUpdate.
643
- * Supports both legacy and frontmatter (v2) formats.
644
- *
645
- * @param {string} field - One of: current_phase, status, plans_complete, last_activity
646
- * @param {string} value - New value (use 'now' for last_activity to auto-timestamp)
647
- */
648
- function stateUpdate(field, value) {
649
- const statePath = path.join(planningDir, 'STATE.md');
650
- if (!fs.existsSync(statePath)) {
651
- return { success: false, error: 'STATE.md not found' };
652
- }
653
-
654
- const validFields = ['current_phase', 'status', 'plans_complete', 'last_activity'];
655
- if (!validFields.includes(field)) {
656
- return { success: false, error: `Invalid field: ${field}. Valid fields: ${validFields.join(', ')}` };
657
- }
658
-
659
- // Auto-timestamp
660
- if (field === 'last_activity' && value === 'now') {
661
- value = new Date().toISOString().slice(0, 19).replace('T', ' ');
662
- }
663
-
664
- const result = lockedFileUpdate(statePath, (content) => {
665
- const fm = parseYamlFrontmatter(content);
666
- if (fm.version === 2 || fm.current_phase !== undefined) {
667
- return updateFrontmatterField(content, field, value);
668
- }
669
- return updateLegacyStateField(content, field, value);
670
- });
671
-
672
- if (result.success) {
673
- return { success: true, field, value };
674
- }
675
- return { success: false, error: result.error };
676
- }
677
-
678
- /**
679
- * Append a record to HISTORY.md. Creates the file if it doesn't exist.
680
- * Each entry is a markdown section appended at the end.
681
- *
682
- * @param {object} entry - { type: 'milestone'|'phase', title: string, body: string }
683
- * @param {string} [dir] - Path to .planning directory (defaults to cwd/.planning)
684
- * @returns {{success: boolean, error?: string}}
685
- */
686
- function historyAppend(entry, dir) {
687
- const historyPath = path.join(dir || planningDir, 'HISTORY.md');
688
- const timestamp = new Date().toISOString().slice(0, 10);
689
-
690
- let header = '';
691
- if (!fs.existsSync(historyPath)) {
692
- header = '# Project History\n\nCompleted milestones and phase records. This file is append-only.\n\n';
693
- }
694
-
695
- const section = `${header}## ${entry.type === 'milestone' ? 'Milestone' : 'Phase'}: ${entry.title}\n_Completed: ${timestamp}_\n\n${entry.body.trim()}\n\n---\n\n`;
696
-
697
- try {
698
- fs.appendFileSync(historyPath, section, 'utf8');
699
- return { success: true };
700
- } catch (e) {
701
- return { success: false, error: e.message };
702
- }
703
- }
704
-
705
- /**
706
- * Load HISTORY.md and parse it into structured records.
707
- * Returns null if HISTORY.md doesn't exist.
708
- *
709
- * @param {string} [dir] - Path to .planning directory
710
- * @returns {object|null} { records: [{type, title, date, body}], line_count }
711
- */
712
- function historyLoad(dir) {
713
- const historyPath = path.join(dir || planningDir, 'HISTORY.md');
714
- if (!fs.existsSync(historyPath)) return null;
715
-
716
- const content = fs.readFileSync(historyPath, 'utf8');
717
- const records = [];
718
- const sectionRegex = /^## (Milestone|Phase): (.+)\n_Completed: (\d{4}-\d{2}-\d{2})_\n\n([\s\S]*?)(?=\n---|\s*$)/gm;
719
-
720
- let match;
721
- while ((match = sectionRegex.exec(content)) !== null) {
722
- records.push({
723
- type: match[1].toLowerCase(),
724
- title: match[2].trim(),
725
- date: match[3],
726
- body: match[4].trim()
727
- });
728
- }
729
-
730
- return {
731
- records,
732
- line_count: content.split('\n').length
733
- };
734
- }
735
-
736
- /**
737
- * Update the Status column for a phase in ROADMAP.md's Phase Overview table.
738
- */
739
- function roadmapUpdateStatus(phaseNum, newStatus) {
740
- const roadmapPath = path.join(planningDir, 'ROADMAP.md');
741
- if (!fs.existsSync(roadmapPath)) {
742
- return { success: false, error: 'ROADMAP.md not found' };
743
- }
744
-
745
- let oldStatus = null;
746
-
747
- const result = lockedFileUpdate(roadmapPath, (content) => {
748
- const lines = content.split('\n');
749
- const rowIdx = findRoadmapRow(lines, phaseNum);
750
- if (rowIdx === -1) {
751
- return content; // No matching row found
752
- }
753
- const parts = lines[rowIdx].split('|');
754
- oldStatus = parts[6] ? parts[6].trim() : 'unknown';
755
- lines[rowIdx] = updateTableRow(lines[rowIdx], 5, newStatus);
756
- return lines.join('\n');
757
- });
758
-
759
- if (!oldStatus) {
760
- return { success: false, error: `Phase ${phaseNum} not found in ROADMAP.md table` };
761
- }
762
-
763
- // Advisory transition validation — warn on suspicious transitions but don't block
764
- const transition = validateStatusTransition(oldStatus, newStatus);
765
- if (!transition.valid && transition.warning) {
766
- process.stderr.write(`[pbr-tools] WARNING: ${transition.warning}\n`);
767
- }
768
-
769
- if (result.success) {
770
- const response = { success: true, old_status: oldStatus, new_status: newStatus };
771
- if (!transition.valid) {
772
- response.transition_warning = transition.warning;
773
- }
774
- return response;
775
- }
776
- return { success: false, error: result.error };
777
- }
778
-
779
- /**
780
- * Update the Plans column for a phase in ROADMAP.md's Phase Overview table.
781
- */
782
- function roadmapUpdatePlans(phaseNum, complete, total) {
783
- const roadmapPath = path.join(planningDir, 'ROADMAP.md');
784
- if (!fs.existsSync(roadmapPath)) {
785
- return { success: false, error: 'ROADMAP.md not found' };
786
- }
787
-
788
- let oldPlans = null;
789
- const newPlans = `${complete}/${total}`;
790
-
791
- const result = lockedFileUpdate(roadmapPath, (content) => {
792
- const lines = content.split('\n');
793
- const rowIdx = findRoadmapRow(lines, phaseNum);
794
- if (rowIdx === -1) {
795
- return content;
796
- }
797
- const parts = lines[rowIdx].split('|');
798
- oldPlans = parts[4] ? parts[4].trim() : 'unknown';
799
- lines[rowIdx] = updateTableRow(lines[rowIdx], 3, newPlans);
800
- return lines.join('\n');
801
- });
802
-
803
- if (!oldPlans) {
804
- return { success: false, error: `Phase ${phaseNum} not found in ROADMAP.md table` };
805
- }
806
-
807
- if (result.success) {
808
- return { success: true, old_plans: oldPlans, new_plans: newPlans };
809
- }
810
- return { success: false, error: result.error };
811
- }
812
-
813
- // --- Mutation helpers ---
814
-
815
- /**
816
- * Update a field in legacy (non-frontmatter) STATE.md content.
817
- * Pure function: content in, content out.
818
- */
819
- function updateLegacyStateField(content, field, value) {
820
- const lines = content.split('\n');
821
-
822
- switch (field) {
823
- case 'current_phase': {
824
- const idx = lines.findIndex(l => /Phase:\s*\d+\s+of\s+\d+/.test(l));
825
- if (idx !== -1) {
826
- lines[idx] = lines[idx].replace(/(Phase:\s*)\d+/, `$1${value}`);
827
- }
828
- break;
829
- }
830
- case 'status': {
831
- const idx = lines.findIndex(l => /^Status:/i.test(l));
832
- if (idx !== -1) {
833
- lines[idx] = `Status: ${value}`;
834
1201
  } else {
835
- const phaseIdx = lines.findIndex(l => /Phase:/.test(l));
836
- if (phaseIdx !== -1) {
837
- lines.splice(phaseIdx + 1, 0, `Status: ${value}`);
838
- } else {
839
- lines.push(`Status: ${value}`);
840
- }
1202
+ error('Usage: learnings <ingest|query|check-thresholds|copy-global|query-global>');
1203
+ process.exit(1);
841
1204
  }
842
- break;
843
- }
844
- case 'plans_complete': {
845
- const idx = lines.findIndex(l => /Plan:\s*\d+\s+of\s+\d+/.test(l));
846
- if (idx !== -1) {
847
- lines[idx] = lines[idx].replace(/(Plan:\s*)\d+/, `$1${value}`);
1205
+ } else if (command === 'verify' && subcommand === 'spot-check') {
1206
+ const scType = args[2];
1207
+ const scPath = args[3];
1208
+ if (!scType || !scPath) { error('Usage: verify spot-check <type> <path> (types: plan, summary, verification, quick)'); }
1209
+ const result = verifySpotCheck(scType, scPath);
1210
+ if (result.error) { process.stdout.write(JSON.stringify(result, null, 2) + '\n'); process.exit(1); }
1211
+ output(result);
1212
+
1213
+ } else if (command === 'spot-check') {
1214
+ // spot-check <phaseSlug> <planId>
1215
+ // Returns JSON: { ok, summary_exists, key_files_checked, commits_present, detail }
1216
+ const phaseSlug = args[1];
1217
+ const planId = args[2];
1218
+ if (!phaseSlug || !planId) {
1219
+ error('Usage: spot-check <phaseSlug> <planId>');
848
1220
  }
849
- break;
850
- }
851
- case 'last_activity': {
852
- const idx = lines.findIndex(l => /^Last Activity:/i.test(l));
853
- if (idx !== -1) {
854
- lines[idx] = `Last Activity: ${value}`;
1221
+ output(spotCheck(phaseSlug, planId));
1222
+ } else if (command === 'staleness-check') {
1223
+ const slug = args[1];
1224
+ if (!slug) { error('Usage: staleness-check <phase-slug>'); process.exit(1); }
1225
+ output(stalenessCheck(slug));
1226
+ } else if (command === 'summary-gate') {
1227
+ const [slug, planId] = args.slice(1);
1228
+ if (!slug || !planId) { error('Usage: summary-gate <phase-slug> <plan-id>'); process.exit(1); }
1229
+ output(summaryGate(slug, planId));
1230
+ } else if (command === 'checkpoint') {
1231
+ const sub = args[1];
1232
+ const slug = args[2];
1233
+ if (sub === 'init') {
1234
+ const plans = args[3] || '';
1235
+ output(checkpointInit(slug, plans));
1236
+ } else if (sub === 'update') {
1237
+ const waveIdx = args.indexOf('--wave');
1238
+ const wave = waveIdx !== -1 ? parseInt(args[waveIdx + 1], 10) : 1;
1239
+ const resolvedIdx = args.indexOf('--resolved');
1240
+ const resolved = resolvedIdx !== -1 ? args[resolvedIdx + 1] : '';
1241
+ const shaIdx = args.indexOf('--sha');
1242
+ const sha = shaIdx !== -1 ? args[shaIdx + 1] : '';
1243
+ output(checkpointUpdate(slug, { wave, resolved, sha }));
1244
+ } else {
1245
+ error('Usage: checkpoint init|update <phase-slug> [options]'); process.exit(1);
1246
+ }
1247
+ } else if (command === 'seeds') {
1248
+ const sub = args[1];
1249
+ if (sub === 'match') {
1250
+ const slug = args[2];
1251
+ const num = args[3];
1252
+ if (!slug) { error('Usage: seeds match <phase-slug> <phase-number>'); process.exit(1); }
1253
+ output(seedsMatch(slug, num));
855
1254
  } else {
856
- const statusIdx = lines.findIndex(l => /^Status:/i.test(l));
857
- if (statusIdx !== -1) {
858
- lines.splice(statusIdx + 1, 0, `Last Activity: ${value}`);
1255
+ error('Usage: seeds match <phase-slug> <phase-number>'); process.exit(1);
1256
+ }
1257
+ } else if (command === 'ci-poll') {
1258
+ const runId = args[1];
1259
+ const timeoutIdx = args.indexOf('--timeout');
1260
+ const timeoutSecs = timeoutIdx !== -1 ? parseInt(args[timeoutIdx + 1], 10) : 300;
1261
+ if (!runId) { error('Usage: pbr-tools.js ci-poll <run-id> [--timeout <seconds>]'); return; }
1262
+ output(ciPoll(runId, timeoutSecs));
1263
+ } else if (command === 'rollback') {
1264
+ const manifestPath = args[1];
1265
+ if (!manifestPath) { error('Usage: pbr-tools.js rollback <manifest-path>'); return; }
1266
+ output(rollbackPlan(manifestPath));
1267
+ } else if (command === 'session') {
1268
+ const sub = args[1];
1269
+ // Extract --session-id flag from remaining args
1270
+ const sessionIdIdx = args.indexOf('--session-id');
1271
+ const sessionId = sessionIdIdx !== -1 ? args[sessionIdIdx + 1] : null;
1272
+ // Filter out --session-id and its value from positional args
1273
+ const positional = sessionIdIdx !== -1
1274
+ ? args.filter((_a, i) => i !== sessionIdIdx && i !== sessionIdIdx + 1)
1275
+ : args;
1276
+ const key = positional[2];
1277
+ const value = positional[3];
1278
+ const dir = planningDir;
1279
+ if (sub === 'get') {
1280
+ if (!key) { error('Usage: pbr-tools.js session get <key> [--session-id <id>]'); return; }
1281
+ if (!SESSION_ALLOWED_KEYS.includes(key)) { error(`Unknown session key: ${key}. Allowed: ${SESSION_ALLOWED_KEYS.join(', ')}`); return; }
1282
+ const data = sessionLoad(dir, sessionId);
1283
+ const val = Object.prototype.hasOwnProperty.call(data, key) ? data[key] : null;
1284
+ output({ key, value: val });
1285
+ } else if (sub === 'set') {
1286
+ if (!key || value === undefined) { error('Usage: pbr-tools.js session set <key> <value> [--session-id <id>]'); return; }
1287
+ if (!SESSION_ALLOWED_KEYS.includes(key)) { error(`Unknown session key: ${key}. Allowed: ${SESSION_ALLOWED_KEYS.join(', ')}`); return; }
1288
+ // Coerce numeric strings
1289
+ let coerced = value;
1290
+ if (/^\d+$/.test(value)) coerced = parseInt(value, 10);
1291
+ else if (value === 'null') coerced = null;
1292
+ const result = sessionSave(dir, { [key]: coerced }, sessionId);
1293
+ if (!result.success) { error(result.error || 'Failed to save session'); return; }
1294
+ output({ ok: true });
1295
+ } else if (sub === 'clear') {
1296
+ if (key) {
1297
+ // Clear a specific key — set to null
1298
+ if (!SESSION_ALLOWED_KEYS.includes(key)) { error(`Unknown session key: ${key}. Allowed: ${SESSION_ALLOWED_KEYS.join(', ')}`); return; }
1299
+ const result = sessionSave(dir, { [key]: null }, sessionId);
1300
+ if (!result.success) { error(result.error || 'Failed to clear session key'); return; }
859
1301
  } else {
860
- lines.push(`Last Activity: ${value}`);
1302
+ // Clear entire session file
1303
+ const sessionPath = sessionId
1304
+ ? resolveSessionPath(dir, '.session.json', sessionId)
1305
+ : path.join(dir, '.session.json');
1306
+ try { if (fs.existsSync(sessionPath)) fs.unlinkSync(sessionPath); } catch (e) { error(e.message); return; }
861
1307
  }
1308
+ output({ ok: true });
1309
+ } else if (sub === 'dump') {
1310
+ const data = sessionLoad(dir, sessionId);
1311
+ output(data);
1312
+ } else {
1313
+ error('Usage: pbr-tools.js session get|set|clear|dump <key> [value] [--session-id <id>]');
862
1314
  }
863
- break;
864
- }
865
- }
866
-
867
- return lines.join('\n');
868
- }
869
-
870
- /**
871
- * Update a field in YAML frontmatter content.
872
- * Pure function: content in, content out.
873
- */
874
- function updateFrontmatterField(content, field, value) {
875
- const match = content.match(/^(---\s*\n)([\s\S]*?)(\n---)/);
876
- if (!match) return content;
877
-
878
- const before = match[1];
879
- let yaml = match[2];
880
- const after = match[3];
881
- const rest = content.slice(match[0].length);
882
-
883
- // Format value: integers stay bare, strings get quotes
884
- const isNum = /^\d+$/.test(String(value));
885
- const formatted = isNum ? value : `"${value}"`;
886
-
887
- const fieldRegex = new RegExp(`^(${field})\\s*:.*$`, 'm');
888
- if (fieldRegex.test(yaml)) {
889
- yaml = yaml.replace(fieldRegex, `${field}: ${formatted}`);
890
- } else {
891
- yaml = yaml + `\n${field}: ${formatted}`;
892
- }
893
-
894
- return before + yaml + after + rest;
895
- }
896
-
897
- /**
898
- * Find the row index of a phase in a ROADMAP.md table.
899
- * @returns {number} Line index or -1 if not found
900
- */
901
- function findRoadmapRow(lines, phaseNum) {
902
- const paddedPhase = phaseNum.padStart(2, '0');
903
- for (let i = 0; i < lines.length; i++) {
904
- if (!lines[i].includes('|')) continue;
905
- const parts = lines[i].split('|');
906
- if (parts.length < 3) continue;
907
- const phaseCol = parts[1] ? parts[1].trim() : '';
908
- if (phaseCol === paddedPhase) {
909
- return i;
910
- }
911
- }
912
- return -1;
913
- }
914
-
915
- /**
916
- * Update a specific column in a markdown table row.
917
- * @param {string} row - The full table row string (e.g., "| 01 | Setup | ... |")
918
- * @param {number} columnIndex - 0-based column index (Phase=0, Name=1, ..., Status=5)
919
- * @param {string} newValue - New cell value
920
- * @returns {string} Updated row
921
- */
922
- function updateTableRow(row, columnIndex, newValue) {
923
- const parts = row.split('|');
924
- // parts[0] is empty (before first |), data starts at parts[1]
925
- const partIndex = columnIndex + 1;
926
- if (partIndex < parts.length) {
927
- parts[partIndex] = ` ${newValue} `;
928
- }
929
- return parts.join('|');
930
- }
931
-
932
- /**
933
- * Lightweight JSON Schema validator — supports type, enum, properties,
934
- * additionalProperties, minimum, maximum for the config schema.
935
- */
936
- function validateObject(value, schema, prefix, errors, warnings) {
937
- if (schema.type && typeof value !== schema.type) {
938
- if (!(schema.type === 'integer' && typeof value === 'number' && Number.isInteger(value))) {
939
- errors.push(`${prefix || 'root'}: expected ${schema.type}, got ${typeof value}`);
940
- return;
941
- }
942
- }
943
-
944
- if (schema.enum && !schema.enum.includes(value)) {
945
- errors.push(`${prefix || 'root'}: value "${value}" not in allowed values [${schema.enum.join(', ')}]`);
946
- return;
947
- }
948
-
949
- if (schema.minimum !== undefined && value < schema.minimum) {
950
- errors.push(`${prefix || 'root'}: value ${value} is below minimum ${schema.minimum}`);
951
- }
952
- if (schema.maximum !== undefined && value > schema.maximum) {
953
- errors.push(`${prefix || 'root'}: value ${value} is above maximum ${schema.maximum}`);
954
- }
955
-
956
- if (schema.type === 'object' && schema.properties) {
957
- const knownKeys = new Set(Object.keys(schema.properties));
958
-
959
- for (const key of Object.keys(value)) {
960
- const fullKey = prefix ? `${prefix}.${key}` : key;
961
- if (!knownKeys.has(key)) {
962
- if (schema.additionalProperties === false) {
963
- warnings.push(`${fullKey}: unrecognized key (possible typo?)`);
1315
+ } else if (command === 'context-triage') {
1316
+ const options = {};
1317
+ const agentsIdx = args.indexOf('--agents-done');
1318
+ if (agentsIdx !== -1) options.agentsDone = parseInt(args[agentsIdx + 1], 10);
1319
+ const plansIdx = args.indexOf('--plans-total');
1320
+ if (plansIdx !== -1) options.plansTotal = parseInt(args[plansIdx + 1], 10);
1321
+ const stepIdx = args.indexOf('--step');
1322
+ if (stepIdx !== -1) options.currentStep = args[stepIdx + 1];
1323
+ output(contextTriage(options));
1324
+ } else if (command === 'reference') {
1325
+ const name = args[1];
1326
+ if (!name) error('Usage: pbr-tools.js reference <name> [--section <heading>] [--list]');
1327
+ const listFlag = args.includes('--list');
1328
+ const sectionIdx = args.indexOf('--section');
1329
+ const section = sectionIdx !== -1 ? args.slice(sectionIdx + 1).join(' ') : null;
1330
+ output(referenceGet(name, { section: section, list: listFlag }));
1331
+ } else if (command === 'milestone-stats') {
1332
+ const version = args[1];
1333
+ if (!version) error('Usage: pbr-tools.js milestone-stats <version>');
1334
+ output(milestoneStats(version));
1335
+ } else if (command === 'validate-project') {
1336
+ output(validateProject());
1337
+ } else if (command === 'skill-section') {
1338
+ // skill-section --list <skill> — list all headings
1339
+ if (args[1] === '--list') {
1340
+ const skillName = args[2];
1341
+ if (!skillName) { error('Usage: pbr-tools.js skill-section --list <skill>'); return; }
1342
+ const listResult = listSkillHeadings(skillName);
1343
+ output(listResult);
1344
+ if (listResult.error) process.exit(1);
1345
+ } else {
1346
+ // skill-section <skill> <section...>
1347
+ const skillName = args[1];
1348
+ const sectionQuery = args.slice(2).join(' ');
1349
+ if (!skillName || !sectionQuery) {
1350
+ error('Usage: pbr-tools.js skill-section <skill> <section>');
1351
+ return;
964
1352
  }
965
- continue;
1353
+ const secResult = skillSectionGet(skillName, sectionQuery);
1354
+ output(secResult);
1355
+ if (secResult.error) process.exit(1);
966
1356
  }
967
- validateObject(value[key], schema.properties[key], fullKey, errors, warnings);
968
- }
969
- }
970
- }
971
-
972
- /**
973
- * Locked file update: read-modify-write with exclusive lockfile.
974
- * Prevents concurrent writes to STATE.md and ROADMAP.md.
975
- *
976
- * @param {string} filePath - Absolute path to the file to update
977
- * @param {function} updateFn - Receives current content, returns new content
978
- * @param {object} opts - Options: { retries: 3, retryDelayMs: 100, timeoutMs: 5000 }
979
- * @returns {object} { success, content?, error? }
980
- */
981
- function lockedFileUpdate(filePath, updateFn, opts = {}) {
982
- const retries = opts.retries || 3;
983
- const retryDelayMs = opts.retryDelayMs || 100;
984
- const timeoutMs = opts.timeoutMs || 5000;
985
- const lockPath = filePath + '.lock';
986
-
987
- let lockFd = null;
988
- let lockAcquired = false;
989
-
990
- try {
991
- // Acquire lock with retries
992
- for (let attempt = 0; attempt < retries; attempt++) {
1357
+ } else if (command === 'step-verify') {
1358
+ // step-verify <skill> <step> <checklist-json>
1359
+ const skill = args[1];
1360
+ const step = args[2];
1361
+ const checklistStr = args[3] || '[]';
1362
+ let checklist;
993
1363
  try {
994
- lockFd = fs.openSync(lockPath, 'wx');
995
- lockAcquired = true;
996
- break;
997
- } catch (e) {
998
- if (e.code === 'EEXIST') {
999
- // Lock exists — check if stale (older than timeoutMs)
1000
- try {
1001
- const stats = fs.statSync(lockPath);
1002
- if (Date.now() - stats.mtimeMs > timeoutMs) {
1003
- // Stale lock — remove and retry
1004
- fs.unlinkSync(lockPath);
1005
- continue;
1006
- }
1007
- } catch (_statErr) {
1008
- // Lock disappeared between check — retry
1009
- continue;
1010
- }
1011
-
1012
- if (attempt < retries - 1) {
1013
- // Wait and retry
1014
- const waitMs = retryDelayMs * (attempt + 1);
1015
- const start = Date.now();
1016
- while (Date.now() - start < waitMs) {
1017
- // Busy wait (synchronous context)
1018
- }
1019
- continue;
1020
- }
1021
- return { success: false, error: `Could not acquire lock for ${path.basename(filePath)} after ${retries} attempts` };
1022
- }
1023
- throw e;
1364
+ checklist = JSON.parse(checklistStr);
1365
+ } catch (_e) {
1366
+ output({ error: 'Invalid checklist JSON' });
1367
+ process.exit(1);
1368
+ return;
1024
1369
  }
1025
- }
1026
-
1027
- if (!lockAcquired) {
1028
- return { success: false, error: `Could not acquire lock for ${path.basename(filePath)}` };
1029
- }
1030
-
1031
- // Write PID to lock file for debugging
1032
- fs.writeSync(lockFd, `${process.pid}`);
1033
- fs.closeSync(lockFd);
1034
- lockFd = null;
1035
-
1036
- // Read current content
1037
- let content = '';
1038
- if (fs.existsSync(filePath)) {
1039
- content = fs.readFileSync(filePath, 'utf8');
1040
- }
1041
-
1042
- // Apply update
1043
- const newContent = updateFn(content);
1044
-
1045
- // Write back atomically
1046
- const writeResult = atomicWrite(filePath, newContent);
1047
- if (!writeResult.success) {
1048
- return { success: false, error: writeResult.error };
1049
- }
1050
-
1051
- return { success: true, content: newContent };
1052
- } catch (e) {
1053
- return { success: false, error: e.message };
1054
- } finally {
1055
- // Close fd if still open
1056
- try {
1057
- if (lockFd !== null) fs.closeSync(lockFd);
1058
- } catch (_e) { /* ignore */ }
1059
- // Only release lock if we acquired it
1060
- if (lockAcquired) {
1061
- try {
1062
- fs.unlinkSync(lockPath);
1063
- } catch (_e) { /* ignore — may already be cleaned up */ }
1064
- }
1065
- }
1066
- }
1067
-
1068
- // --- Parsers ---
1069
-
1070
- function parseStateMd(content) {
1071
- const result = {
1072
- current_phase: null,
1073
- phase_name: null,
1074
- progress: null,
1075
- status: null,
1076
- line_count: content.split('\n').length,
1077
- format: 'legacy' // 'legacy' or 'frontmatter'
1078
- };
1079
-
1080
- // Check for YAML frontmatter (version 2 format)
1081
- const frontmatter = parseYamlFrontmatter(content);
1082
- if (frontmatter.version === 2 || frontmatter.current_phase !== undefined) {
1083
- result.format = 'frontmatter';
1084
- result.current_phase = frontmatter.current_phase || null;
1085
- result.total_phases = frontmatter.total_phases || null;
1086
- result.phase_name = frontmatter.phase_slug || frontmatter.phase_name || null;
1087
- result.status = frontmatter.status || null;
1088
- result.progress = frontmatter.progress_percent !== undefined ? frontmatter.progress_percent : null;
1089
- result.plans_total = frontmatter.plans_total || null;
1090
- result.plans_complete = frontmatter.plans_complete || null;
1091
- result.last_activity = frontmatter.last_activity || null;
1092
- result.last_command = frontmatter.last_command || null;
1093
- result.blockers = frontmatter.blockers || [];
1094
- return result;
1095
- }
1096
-
1097
- // Legacy regex-based parsing (version 1 format, no frontmatter)
1098
- // DEPRECATED (2026-02): v1 STATE.md format (no YAML frontmatter) is deprecated.
1099
- // New projects should use v2 (frontmatter) format, generated by /pbr:setup.
1100
- // v1 support will be removed in a future major version.
1101
- process.stderr.write('[pbr] WARNING: STATE.md uses legacy v1 format. Run /pbr:setup to migrate to v2 format.\n');
1102
- // Extract "Phase: N of M"
1103
- const phaseMatch = content.match(/Phase:\s*(\d+)\s+of\s+(\d+)/);
1104
- if (phaseMatch) {
1105
- result.current_phase = parseInt(phaseMatch[1], 10);
1106
- result.total_phases = parseInt(phaseMatch[2], 10);
1107
- }
1108
-
1109
- // Extract phase name (line after "Phase:")
1110
- const nameMatch = content.match(/--\s+(.+?)(?:\n|$)/);
1111
- if (nameMatch) {
1112
- result.phase_name = nameMatch[1].trim();
1113
- }
1114
-
1115
- // Extract progress percentage
1116
- const progressMatch = content.match(/(\d+)%/);
1117
- if (progressMatch) {
1118
- result.progress = parseInt(progressMatch[1], 10);
1119
- }
1120
-
1121
- // Extract plan status
1122
- const statusMatch = content.match(/Status:\s*(.+?)(?:\n|$)/i);
1123
- if (statusMatch) {
1124
- result.status = statusMatch[1].trim();
1125
- }
1126
-
1127
- return result;
1128
- }
1129
-
1130
- function parseRoadmapMd(content) {
1131
- const result = { phases: [], has_progress_table: false };
1132
-
1133
- // Find Phase Overview table
1134
- const overviewMatch = content.match(/## Phase Overview[\s\S]*?\|[\s\S]*?(?=\n##|\s*$)/);
1135
- if (overviewMatch) {
1136
- const rows = overviewMatch[0].split('\n').filter(r => r.includes('|'));
1137
- // Skip header and separator rows
1138
- for (let i = 2; i < rows.length; i++) {
1139
- const cols = rows[i].split('|').map(c => c.trim()).filter(Boolean);
1140
- if (cols.length >= 3) {
1141
- result.phases.push({
1142
- number: cols[0],
1143
- name: cols[1],
1144
- goal: cols[2],
1145
- plans: cols[3] || '',
1146
- wave: cols[4] || '',
1147
- status: cols[5] || 'pending'
1148
- });
1370
+ const svPlanningDir = planningDir;
1371
+ const svContext = {
1372
+ planningDir: svPlanningDir,
1373
+ phaseSlug: process.env.PBR_PHASE_SLUG || '',
1374
+ planId: process.env.PBR_PLAN_ID || ''
1375
+ };
1376
+ const svResult = _stepVerify(skill, step, checklist, svContext);
1377
+ output(svResult);
1378
+ if (svResult.error || svResult.all_passed === false) process.exit(1);
1379
+ } else if (command === 'build-preview') {
1380
+ const phaseSlug = args[1];
1381
+ if (!phaseSlug) {
1382
+ error('Usage: pbr-tools.js build-preview <phase-slug>');
1383
+ return;
1149
1384
  }
1150
- }
1151
- }
1152
-
1153
- // Check for Progress table
1154
- result.has_progress_table = /## Progress/.test(content);
1155
-
1156
- return result;
1157
- }
1158
-
1159
- function parseYamlFrontmatter(content) {
1160
- const match = content.match(/^---\s*\n([\s\S]*?)\n---/);
1161
- if (!match) return {};
1162
-
1163
- const yaml = match[1];
1164
- const result = {};
1165
-
1166
- // Simple YAML parser for flat and basic nested values
1167
- const lines = yaml.split('\n');
1168
- let currentKey = null;
1169
-
1170
- for (const line of lines) {
1171
- // Array item
1172
- if (/^\s+-\s+/.test(line) && currentKey) {
1173
- const val = line.replace(/^\s+-\s+/, '').trim().replace(/^["']|["']$/g, '');
1174
- if (!result[currentKey]) result[currentKey] = [];
1175
- if (Array.isArray(result[currentKey])) {
1176
- result[currentKey].push(val);
1385
+ const previewPlanningDir = planningDir;
1386
+ const previewPluginRoot = path.resolve(__dirname, '..');
1387
+ const result = _buildPreview(phaseSlug, {}, previewPlanningDir, previewPluginRoot);
1388
+ if (result && result.error) {
1389
+ output(result);
1390
+ process.exit(1);
1177
1391
  }
1178
- continue;
1179
- }
1180
-
1181
- // Key-value pair
1182
- const kvMatch = line.match(/^(\w[\w_]*)\s*:\s*(.*)/);
1183
- if (kvMatch) {
1184
- currentKey = kvMatch[1];
1185
- let val = kvMatch[2].trim();
1186
-
1187
- if (val === '' || val === '|') {
1188
- // Possible array or block follows
1189
- continue;
1392
+ output(result);
1393
+ } else if (command === 'suggest-alternatives') {
1394
+ const errorType = args[1];
1395
+ const altPlanningDir = planningDir;
1396
+ if (errorType === 'phase-not-found') {
1397
+ output(_phaseAlternatives(args[2] || '', altPlanningDir));
1398
+ } else if (errorType === 'missing-prereq') {
1399
+ output(_prereqAlternatives(args[2] || '', altPlanningDir));
1400
+ } else if (errorType === 'config-invalid') {
1401
+ output(_configAlternatives(args[2] || '', args[3] || '', altPlanningDir));
1402
+ } else {
1403
+ output({ error: 'Unknown error type. Valid: phase-not-found, missing-prereq, config-invalid' });
1404
+ process.exit(1);
1190
1405
  }
1191
-
1192
- // Handle arrays on same line: [a, b, c]
1193
- if (val.startsWith('[') && val.endsWith(']')) {
1194
- result[currentKey] = val.slice(1, -1).split(',')
1195
- .map(v => v.trim().replace(/^["']|["']$/g, ''))
1196
- .filter(Boolean);
1197
- continue;
1406
+ } else if (command === 'claim' && subcommand === 'acquire') {
1407
+ const phaseSlug = args[2];
1408
+ const sidIdx = args.indexOf('--session-id');
1409
+ const sessionId = sidIdx !== -1 ? args[sidIdx + 1] : null;
1410
+ const skillIdx = args.indexOf('--skill');
1411
+ const skill = skillIdx !== -1 ? args[skillIdx + 1] : 'unknown';
1412
+ if (!phaseSlug || !sessionId) {
1413
+ error('Usage: pbr-tools.js claim acquire <phase-slug> --session-id <id> --skill <name>');
1198
1414
  }
1199
-
1200
- // Clean quotes
1201
- val = val.replace(/^["']|["']$/g, '');
1202
-
1203
- // Type coercion
1204
- if (val === 'true') val = true;
1205
- else if (val === 'false') val = false;
1206
- else if (/^\d+$/.test(val)) val = parseInt(val, 10);
1207
-
1208
- result[currentKey] = val;
1209
- }
1210
- }
1211
-
1212
- // Handle must_haves as a nested object
1213
- if (yaml.includes('must_haves:')) {
1214
- result.must_haves = parseMustHaves(yaml);
1215
- }
1216
-
1217
- return result;
1218
- }
1219
-
1220
- function parseMustHaves(yaml) {
1221
- const result = { truths: [], artifacts: [], key_links: [] };
1222
- let section = null;
1223
-
1224
- const inMustHaves = yaml.split('\n');
1225
- let collecting = false;
1226
-
1227
- for (const line of inMustHaves) {
1228
- if (/^\s*must_haves:/.test(line)) {
1229
- collecting = true;
1230
- continue;
1231
- }
1232
- if (collecting) {
1233
- if (/^\s{2}truths:/.test(line)) { section = 'truths'; continue; }
1234
- if (/^\s{2}artifacts:/.test(line)) { section = 'artifacts'; continue; }
1235
- if (/^\s{2}key_links:/.test(line)) { section = 'key_links'; continue; }
1236
- if (/^\w/.test(line)) break; // New top-level key, stop
1237
-
1238
- if (section && /^\s+-\s+/.test(line)) {
1239
- result[section].push(line.replace(/^\s+-\s+/, '').trim().replace(/^["']|["']$/g, ''));
1415
+ output(claimAcquire(phaseSlug, sessionId, skill));
1416
+ } else if (command === 'claim' && subcommand === 'release') {
1417
+ const phaseSlug = args[2];
1418
+ const sidIdx = args.indexOf('--session-id');
1419
+ const sessionId = sidIdx !== -1 ? args[sidIdx + 1] : null;
1420
+ if (!phaseSlug || !sessionId) {
1421
+ error('Usage: pbr-tools.js claim release <phase-slug> --session-id <id>');
1240
1422
  }
1241
- }
1242
- }
1243
-
1244
- return result;
1245
- }
1246
-
1247
- // --- Helpers ---
1248
-
1249
- function findFiles(dir, pattern) {
1250
- try {
1251
- return fs.readdirSync(dir).filter(f => pattern.test(f)).sort();
1252
- } catch (_) {
1253
- return [];
1254
- }
1255
- }
1256
-
1257
- function determinePhaseStatus(planCount, completedCount, summaryCount, hasVerification, phaseDir) {
1258
- if (planCount === 0) {
1259
- // Check for CONTEXT.md (discussed only)
1260
- if (fs.existsSync(path.join(phaseDir, 'CONTEXT.md'))) return 'discussed';
1261
- return 'not_started';
1262
- }
1263
- if (completedCount === 0 && summaryCount === 0) return 'planned';
1264
- if (completedCount < planCount) return 'building';
1265
- if (!hasVerification) return 'built';
1266
- // Check verification status
1267
- try {
1268
- const vContent = fs.readFileSync(path.join(phaseDir, 'VERIFICATION.md'), 'utf8');
1269
- if (/status:\s*["']?passed/i.test(vContent)) return 'verified';
1270
- if (/status:\s*["']?gaps_found/i.test(vContent)) return 'needs_fixes';
1271
- return 'reviewed';
1272
- } catch (_) {
1273
- return 'built';
1274
- }
1275
- }
1276
-
1277
- function countMustHaves(mustHaves) {
1278
- if (!mustHaves) return 0;
1279
- return (mustHaves.truths || []).length +
1280
- (mustHaves.artifacts || []).length +
1281
- (mustHaves.key_links || []).length;
1282
- }
1283
-
1284
- function calculateProgress() {
1285
- const phasesDir = path.join(planningDir, 'phases');
1286
- if (!fs.existsSync(phasesDir)) {
1287
- return { total: 0, completed: 0, percentage: 0 };
1288
- }
1289
-
1290
- let total = 0;
1291
- let completed = 0;
1292
-
1293
- const entries = fs.readdirSync(phasesDir, { withFileTypes: true })
1294
- .filter(e => e.isDirectory());
1295
-
1296
- for (const entry of entries) {
1297
- const dir = path.join(phasesDir, entry.name);
1298
- const plans = findFiles(dir, /-PLAN\.md$/);
1299
- total += plans.length;
1300
-
1301
- const summaries = findFiles(dir, /^SUMMARY-.*\.md$/);
1302
- for (const s of summaries) {
1303
- const content = fs.readFileSync(path.join(dir, s), 'utf8');
1304
- if (/status:\s*["']?complete/i.test(content)) completed++;
1305
- }
1306
- }
1307
-
1308
- return {
1309
- total,
1310
- completed,
1311
- percentage: total > 0 ? Math.round((completed / total) * 100) : 0
1312
- };
1313
- }
1314
-
1315
- function output(data) {
1316
- process.stdout.write(JSON.stringify(data, null, 2));
1317
- process.exit(0);
1318
- }
1319
-
1320
- function error(msg) {
1321
- process.stdout.write(JSON.stringify({ error: msg }));
1322
- process.exit(1);
1323
- }
1324
-
1325
- /**
1326
- * Write content to a file atomically: write to .tmp, backup original to .bak,
1327
- * rename .tmp over original. On failure, restore from .bak if available.
1328
- *
1329
- * @param {string} filePath - Target file path
1330
- * @param {string} content - Content to write
1331
- * @returns {{success: boolean, error?: string}} Result
1332
- */
1333
- function atomicWrite(filePath, content) {
1334
- const tmpPath = filePath + '.tmp';
1335
- const bakPath = filePath + '.bak';
1336
-
1337
- try {
1338
- // 1. Write to temp file
1339
- fs.writeFileSync(tmpPath, content, 'utf8');
1340
-
1341
- // 2. Backup original if it exists
1342
- if (fs.existsSync(filePath)) {
1343
- try {
1344
- fs.copyFileSync(filePath, bakPath);
1345
- } catch (_e) {
1346
- // Backup failure is non-fatal — proceed with rename
1423
+ output(claimRelease(phaseSlug, sessionId));
1424
+ } else if (command === 'claim' && subcommand === 'list') {
1425
+ output(claimList());
1426
+ } else if (command === 'status' && subcommand === 'render') {
1427
+ output(_statusRender(planningDir));
1428
+ } else if (command === 'suggest-next') {
1429
+ output(_suggestNext(planningDir));
1430
+ } else if (command === 'tmux' && subcommand === 'detect') {
1431
+ const tmuxEnv = process.env.TMUX || '';
1432
+ const result = {
1433
+ in_tmux: !!tmuxEnv,
1434
+ pane: process.env.TMUX_PANE || null,
1435
+ session: null
1436
+ };
1437
+ if (tmuxEnv) {
1438
+ // TMUX env format: /socket/path,PID,INDEX
1439
+ const parts = tmuxEnv.split(',');
1440
+ if (parts.length >= 1) {
1441
+ result.session = parts[0].split('/').pop() || null;
1442
+ }
1347
1443
  }
1348
- }
1349
-
1350
- // 3. Rename temp over original (atomic on most filesystems)
1351
- fs.renameSync(tmpPath, filePath);
1352
-
1353
- return { success: true };
1354
- } catch (e) {
1355
- // Rename failed — try to restore from backup
1356
- try {
1357
- if (fs.existsSync(bakPath)) {
1358
- fs.copyFileSync(bakPath, filePath);
1444
+ output(result);
1445
+
1446
+ // ─── Quick Task Operations ─────────────────────────────────────────────────
1447
+ } else if (command === 'quick' && subcommand === 'init') {
1448
+ const desc = args.slice(2).join(' ') || '';
1449
+ const quickInitMod = require('./lib/quick-init.js');
1450
+ output(quickInitMod.quickInit(desc, planningDir));
1451
+
1452
+ // ─── Slug Generation ─────────────────────────────────────────────────────
1453
+ } else if (command === 'generate-slug' || command === 'slug-generate') {
1454
+ const text = args.slice(1).join(' ');
1455
+ if (!text) error('Usage: pbr-tools.js generate-slug <text>');
1456
+ const slug = text.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '');
1457
+ output({ slug });
1458
+
1459
+ // ─── Parse Args ────────────────────────────────────────────────────────────
1460
+ } else if (command === 'parse-args') {
1461
+ const type = args[1];
1462
+ const rawInput = args.slice(2).join(' ');
1463
+ if (!type) error('Usage: pbr-tools.js parse-args <type> <args>\nTypes: plan, quick');
1464
+ const { parseArgs } = require('./lib/parse-args');
1465
+ output(parseArgs(type, rawInput));
1466
+
1467
+ // ─── Status Fingerprint ──────────────────────────────────────────────────
1468
+ } else if (command === 'status' && subcommand === 'fingerprint') {
1469
+ const crypto = require('crypto');
1470
+ const files = {};
1471
+ let combinedContent = '';
1472
+ for (const name of ['STATE.md', 'ROADMAP.md']) {
1473
+ const filePath = path.join(planningDir, name);
1474
+ try {
1475
+ const content = fs.readFileSync(filePath, 'utf8');
1476
+ const stat = fs.statSync(filePath);
1477
+ const hash = crypto.createHash('sha256').update(content).digest('hex').slice(0, 8);
1478
+ files[name] = {
1479
+ hash,
1480
+ mtime: stat.mtime.toISOString(),
1481
+ lines: content.split('\n').length
1482
+ };
1483
+ combinedContent += content;
1484
+ } catch {
1485
+ files[name] = { hash: null, mtime: null, lines: 0 };
1486
+ }
1359
1487
  }
1360
- } catch (_restoreErr) {
1361
- // Restore also failed — nothing more we can do
1362
- }
1488
+ const fingerprint = combinedContent
1489
+ ? crypto.createHash('sha256').update(combinedContent).digest('hex').slice(0, 8)
1490
+ : null;
1491
+ let phaseDirs = 0;
1492
+ const phasesDir = path.join(planningDir, 'phases');
1493
+ try {
1494
+ const entries = fs.readdirSync(phasesDir, { withFileTypes: true });
1495
+ phaseDirs = entries.filter(e => e.isDirectory()).length;
1496
+ } catch { /* no phases dir */ }
1497
+ output({
1498
+ fingerprint,
1499
+ files,
1500
+ phase_dirs: phaseDirs,
1501
+ timestamp: new Date().toISOString()
1502
+ });
1503
+
1504
+ } else if (command === 'quick-status') {
1505
+ const result = quickStatus();
1506
+ process.stdout.write(result.text + '\n');
1507
+
1508
+ } else if (command === 'ci-fix') {
1509
+ const dryRun = args.includes('--dry-run');
1510
+ const maxIterIdx = args.indexOf('--max-iterations');
1511
+ const maxIterations = maxIterIdx !== -1 ? parseInt(args[maxIterIdx + 1], 10) : 3;
1512
+ output(ciFix({ dryRun, maxIterations }));
1363
1513
 
1364
- // Clean up temp file if it still exists
1365
- try {
1366
- if (fs.existsSync(tmpPath)) {
1367
- fs.unlinkSync(tmpPath);
1368
- }
1369
- } catch (_cleanupErr) {
1370
- // Best-effort cleanup
1514
+ } else {
1515
+ error(`Unknown command: ${args.join(' ')}\nCommands: state load|check-progress|update|patch|advance-plan|record-metric, config validate|load-defaults|save-defaults|resolve-depth, validate health, validate-project, migrate [--dry-run] [--force], init execute-phase|plan-phase|quick|verify-work|resume|progress, state-bundle <phase>, plan-index, frontmatter, must-haves, phase-info, phase add|remove|list|complete, roadmap update-status|update-plans, history append|load, todo list|get|add|done, auto-cleanup --phase N|--milestone vN, event, llm health|status|classify|score-source|classify-error|summarize|metrics [--session <ISO>]|adjust-thresholds, learnings ingest|query|check-thresholds, milestone-stats <version>, context-triage [--agents-done N] [--plans-total N] [--step NAME], ci-poll <run-id> [--timeout <seconds>], ci-fix [--dry-run] [--max-iterations N], rollback <manifest-path>, session get|set|clear|dump, claim acquire|release|list, skill-section <skill> <section>|--list <skill>, step-verify <skill> <step> <checklist-json>, suggest-alternatives phase-not-found|missing-prereq|config-invalid [args], tmux detect, quick init, generate-slug|slug-generate, parse-args plan|quick, status fingerprint, quick-status`);
1371
1516
  }
1372
-
1373
- return { success: false, error: e.message };
1517
+ } catch (e) {
1518
+ error(e.message);
1374
1519
  }
1375
1520
  }
1376
1521
 
1377
- if (require.main === module || process.argv[1] === __filename) { main(); }
1378
- module.exports = { parseStateMd, parseRoadmapMd, parseYamlFrontmatter, parseMustHaves, countMustHaves, stateLoad, stateCheckProgress, configLoad, configClearCache, configValidate, lockedFileUpdate, planIndex, determinePhaseStatus, findFiles, atomicWrite, tailLines, frontmatter, mustHavesCollect, phaseInfo, stateUpdate, roadmapUpdateStatus, roadmapUpdatePlans, updateLegacyStateField, updateFrontmatterField, updateTableRow, findRoadmapRow, resolveDepthProfile, DEPTH_PROFILE_DEFAULTS, historyAppend, historyLoad, VALID_STATUS_TRANSITIONS, validateStatusTransition };
1522
+ if (require.main === module || process.argv[1] === __filename) { main().catch(err => { process.stderr.write(err.message + '\n'); process.exit(1); }); }
1523
+ module.exports = { KNOWN_AGENTS, initExecutePhase, initPlanPhase, initQuick, initVerifyWork, initResume, initProgress, initStateBundle: stateBundle, stateBundle, statePatch, stateAdvancePlan, stateRecordMetric, stateRecordActivity, stateUpdateProgress, parseStateMd, parseRoadmapMd, parseYamlFrontmatter, parseMustHaves, countMustHaves, stateLoad, stateCheckProgress, configLoad, configClearCache, configValidate, lockedFileUpdate, planIndex, determinePhaseStatus, findFiles, atomicWrite, tailLines, frontmatter, mustHavesCollect, phaseInfo, stateUpdate, roadmapUpdateStatus, roadmapUpdatePlans, roadmapAnalyze, updateLegacyStateField, updateFrontmatterField, updateTableRow, findRoadmapRow, resolveDepthProfile, DEPTH_PROFILE_DEFAULTS, historyAppend, historyLoad, VALID_STATUS_TRANSITIONS, validateStatusTransition, writeActiveSkill, validateProject, phaseAdd, phaseRemove, phaseList, loadUserDefaults, saveUserDefaults, mergeUserDefaults, USER_DEFAULTS_PATH, todoList, todoGet, todoAdd, todoDone, migrate, spotCheck, referenceGet, milestoneStats, contextTriage, stalenessCheck, summaryGate, checkpointInit, checkpointUpdate, seedsMatch, ciPoll, ciFix, parseJestOutput: _parseJestOutput, parseLintOutput: _parseLintOutput, autoFixLint: _autoFixLint, runCiFixLoop: _runCiFixLoop, rollbackPlan, sessionLoad, sessionSave, SESSION_ALLOWED_KEYS, claimAcquire, claimRelease, claimList, skillSectionGet, listSkillHeadings, stepVerify: _stepVerify, phaseAlternatives: _phaseAlternatives, prerequisiteAlternatives: _prereqAlternatives, configAlternatives: _configAlternatives, phaseComplete, phaseInsert, quickStatus, autoCloseTodos, autoArchiveNotes, buildCleanupContext };
1524
+ // NOTE: validateProject, phaseAdd, phaseRemove, phaseList were previously CLI-only (not exported).
1525
+ // They are now exported for testability. This is additive and backwards-compatible.