@sienklogic/plan-build-run 2.19.0 → 2.19.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.
- package/CHANGELOG.md +1265 -320
- package/CLAUDE.md +56 -34
- package/LICENSE +2 -1
- package/README.md +412 -177
- package/bin/install.js +2752 -0
- package/dashboard/bin/cli.cjs +96 -0
- package/dashboard/bin/stop.cjs +129 -0
- package/dashboard/eslint.config.js +37 -0
- package/dashboard/index.html +20 -0
- package/dashboard/package.json +27 -25
- package/dashboard/server/index.js +142 -0
- package/dashboard/server/lib/frontmatter.js +92 -0
- package/dashboard/server/middleware/static.js +35 -0
- package/dashboard/server/package.json +16 -0
- package/dashboard/server/routes/agents.js +213 -0
- package/dashboard/server/routes/config.js +64 -0
- package/dashboard/server/routes/health.js +95 -0
- package/dashboard/server/routes/incidents.js +78 -0
- package/dashboard/server/routes/intel.js +69 -0
- package/dashboard/server/routes/memory.js +107 -0
- package/dashboard/server/routes/planning.js +234 -0
- package/dashboard/server/routes/progress.js +77 -0
- package/dashboard/server/routes/projects.js +36 -0
- package/dashboard/server/routes/requirements.js +40 -0
- package/dashboard/server/routes/roadmap.js +69 -0
- package/dashboard/server/routes/sessions.js +70 -0
- package/dashboard/server/routes/status.js +25 -0
- package/dashboard/server/routes/telemetry.js +214 -0
- package/dashboard/server/services/file-watcher.js +105 -0
- package/dashboard/server/services/planning-reader.js +741 -0
- package/dashboard/server/test/cli.test.js +34 -0
- package/dashboard/server/test/frontmatter.test.js +104 -0
- package/dashboard/server/test/isolation.test.js +32 -0
- package/dashboard/server/test/planning-reader.test.js +151 -0
- package/dashboard/server/test/routes.test.js +91 -0
- package/dashboard/server/test/ws.test.js +81 -0
- package/dashboard/server/ws.js +96 -0
- package/dashboard/src/App.jsx +165 -0
- package/dashboard/src/components/charts/BudgetBars.jsx +42 -0
- package/dashboard/src/components/charts/ContextRadar.jsx +34 -0
- package/dashboard/src/components/charts/PhaseDonut.jsx +66 -0
- package/dashboard/src/components/charts/SuccessTrend.jsx +45 -0
- package/dashboard/src/components/charts/TokenChart.jsx +55 -0
- package/dashboard/src/components/charts/index.js +5 -0
- package/dashboard/src/components/config/CfgSection.jsx +93 -0
- package/dashboard/src/components/layout/Header.jsx +89 -0
- package/dashboard/src/components/layout/ProjectSwitcher.jsx +160 -0
- package/dashboard/src/components/layout/Sidebar.jsx +161 -0
- package/dashboard/src/components/ui/AutoModeBanner.jsx +138 -0
- package/dashboard/src/components/ui/BackButton.jsx +27 -0
- package/dashboard/src/components/ui/Badge.jsx +27 -0
- package/dashboard/src/components/ui/Card.jsx +23 -0
- package/dashboard/src/components/ui/ChartTooltip.jsx +48 -0
- package/dashboard/src/components/ui/CheckpointBox.jsx +110 -0
- package/dashboard/src/components/ui/CodeBlock.jsx +27 -0
- package/dashboard/src/components/ui/ConfidenceBadge.jsx +20 -0
- package/dashboard/src/components/ui/ConfirmModal.jsx +161 -0
- package/dashboard/src/components/ui/ConnectionBanner.jsx +60 -0
- package/dashboard/src/components/ui/ErrorBoundary.jsx +106 -0
- package/dashboard/src/components/ui/ErrorBox.jsx +107 -0
- package/dashboard/src/components/ui/KeyValue.jsx +33 -0
- package/dashboard/src/components/ui/LoadingSkeleton.jsx +84 -0
- package/dashboard/src/components/ui/MetricCard.jsx +58 -0
- package/dashboard/src/components/ui/NextUpBlock.jsx +92 -0
- package/dashboard/src/components/ui/NumberInput.jsx +44 -0
- package/dashboard/src/components/ui/PBRBanner.jsx +47 -0
- package/dashboard/src/components/ui/PipelineView.jsx +130 -0
- package/dashboard/src/components/ui/ProgressBar.jsx +28 -0
- package/dashboard/src/components/ui/ProgressDisplay.jsx +47 -0
- package/dashboard/src/components/ui/QualityGateBadge.jsx +15 -0
- package/dashboard/src/components/ui/SectionTitle.jsx +35 -0
- package/dashboard/src/components/ui/SelectInput.jsx +45 -0
- package/dashboard/src/components/ui/StatusDot.jsx +51 -0
- package/dashboard/src/components/ui/StatusSymbol.jsx +49 -0
- package/dashboard/src/components/ui/TabBar.jsx +41 -0
- package/dashboard/src/components/ui/TextInput.jsx +42 -0
- package/dashboard/src/components/ui/Toast.jsx +117 -0
- package/dashboard/src/components/ui/Toggle.jsx +70 -0
- package/dashboard/src/components/ui/index.js +29 -0
- package/dashboard/src/hooks/useDocumentTitle.js +16 -0
- package/dashboard/src/hooks/useFetch.js +50 -0
- package/dashboard/src/hooks/useToast.jsx +43 -0
- package/dashboard/src/hooks/useWebSocket.js +103 -0
- package/dashboard/src/lib/api.js +112 -0
- package/dashboard/src/lib/configSchema.js +189 -0
- package/dashboard/src/lib/constants.js +22 -0
- package/dashboard/src/main.jsx +15 -0
- package/dashboard/src/pages/AgentsPage.jsx +191 -0
- package/dashboard/src/pages/ConfigPage.jsx +298 -0
- package/dashboard/src/pages/HooksPage.jsx +412 -0
- package/dashboard/src/pages/IncidentsPage.jsx +135 -0
- package/dashboard/src/pages/IntelPage.jsx +193 -0
- package/dashboard/src/pages/LiveFeed.jsx +274 -0
- package/dashboard/src/pages/MemoryPage.jsx +107 -0
- package/dashboard/src/pages/OnboardingPage.jsx +117 -0
- package/dashboard/src/pages/Overview.jsx +360 -0
- package/dashboard/src/pages/PhaseDetailView.jsx +216 -0
- package/dashboard/src/pages/PlanningPage.jsx +181 -0
- package/dashboard/src/pages/ProgressPage.jsx +249 -0
- package/dashboard/src/pages/ResearchPage.jsx +129 -0
- package/dashboard/src/pages/RoadmapPage.jsx +251 -0
- package/dashboard/src/pages/SessionsPage.jsx +117 -0
- package/dashboard/src/pages/Telemetry.jsx +166 -0
- package/dashboard/src/pages/planning/DecisionsTab.jsx +153 -0
- package/dashboard/src/pages/planning/FilesTab.jsx +420 -0
- package/dashboard/src/pages/planning/MilestoneDetail.jsx +319 -0
- package/dashboard/src/pages/planning/MilestonesTab.jsx +151 -0
- package/dashboard/src/pages/planning/NotesTab.jsx +251 -0
- package/dashboard/src/pages/planning/PhasesTab.jsx +218 -0
- package/dashboard/src/pages/planning/QuickTab.jsx +50 -0
- package/dashboard/src/pages/planning/ResearchTab.jsx +103 -0
- package/dashboard/src/pages/planning/TodosTab.jsx +297 -0
- package/dashboard/src/theme/ThemeProvider.jsx +38 -0
- package/dashboard/src/theme/tokens.js +17 -0
- package/dashboard/tests/components/ConfirmModal.test.jsx +179 -0
- package/dashboard/tests/components/ConnectionBanner.test.jsx +37 -0
- package/dashboard/tests/components/ErrorBoundary.test.jsx +59 -0
- package/dashboard/tests/components/LoadingSkeleton.test.jsx +46 -0
- package/dashboard/tests/components/ToastContainer.test.jsx +47 -0
- package/dashboard/tests/components/Toggle.test.jsx +61 -0
- package/dashboard/tests/hooks/useFetch.test.jsx +77 -0
- package/dashboard/tests/hooks/useToast.test.jsx +78 -0
- package/dashboard/tests/hooks/useWebSocket.test.jsx +128 -0
- package/dashboard/tests/pages/ConfigPage.test.jsx +199 -0
- package/dashboard/tests/pages/PlanningPage.test.jsx +119 -0
- package/dashboard/tests/pages/planning/FilesTab.test.jsx +198 -0
- package/dashboard/tests/pages/planning/NotesTab.test.jsx +178 -0
- package/dashboard/tests/pages/planning/TodosTab.test.jsx +188 -0
- package/dashboard/tests/performance.test.jsx +46 -0
- package/dashboard/tests/routes/config.test.js +98 -0
- package/dashboard/tests/routes/health.test.js +40 -0
- package/dashboard/tests/routes/planning.test.js +112 -0
- package/dashboard/tests/routes/roadmap.test.js +91 -0
- package/dashboard/tests/routes/status.test.js +131 -0
- package/dashboard/tests/server/planning-reader.test.js +153 -0
- package/dashboard/tests/setup.js +7 -0
- package/dashboard/vite.config.js +41 -0
- package/package.json +55 -40
- package/plan-build-run/bin/config-schema.json +1420 -0
- package/plugins/pbr/.claude-plugin/plugin.json +1 -1
- package/plugins/pbr/CLAUDE.md +19 -0
- package/plugins/pbr/UI-CONSISTENCY-GAPS.md +1 -1
- package/plugins/pbr/agents/advisor-researcher.md +100 -0
- package/plugins/pbr/agents/audit.md +205 -89
- package/plugins/pbr/agents/codebase-mapper.md +158 -23
- package/plugins/pbr/agents/debugger.md +216 -34
- package/plugins/pbr/agents/dev-sync.md +206 -0
- package/plugins/pbr/agents/executor.md +717 -39
- package/plugins/pbr/agents/general.md +71 -6
- package/plugins/pbr/agents/integration-checker.md +146 -30
- package/plugins/pbr/agents/intel-updater.md +332 -0
- package/plugins/pbr/agents/nyquist-auditor.md +253 -0
- package/plugins/pbr/agents/plan-checker.md +265 -65
- package/plugins/pbr/agents/planner.md +440 -42
- package/plugins/pbr/agents/researcher.md +223 -36
- package/plugins/pbr/agents/roadmapper.md +397 -0
- package/plugins/pbr/agents/synthesizer.md +170 -26
- package/plugins/pbr/agents/ui-checker.md +203 -0
- package/plugins/pbr/agents/ui-researcher.md +224 -0
- package/plugins/pbr/agents/verifier.md +495 -47
- package/plugins/pbr/commands/add-phase.md +75 -0
- package/plugins/pbr/commands/add-todo.md +8 -0
- package/plugins/pbr/commands/audit-fix.md +5 -0
- package/plugins/pbr/commands/audit-milestone.md +8 -0
- package/plugins/pbr/commands/autonomous.md +5 -0
- package/plugins/pbr/commands/backlog.md +6 -0
- package/plugins/pbr/commands/check-todos.md +8 -0
- package/plugins/pbr/commands/complete-milestone.md +8 -0
- package/plugins/pbr/commands/config.md +1 -1
- package/plugins/pbr/commands/discuss-phase.md +6 -0
- package/plugins/pbr/commands/do.md +5 -0
- package/plugins/pbr/commands/execute-phase.md +6 -0
- package/plugins/pbr/commands/fast.md +6 -0
- package/plugins/pbr/commands/forensics.md +6 -0
- package/plugins/pbr/commands/import.md +1 -1
- package/plugins/pbr/commands/insert-phase.md +65 -0
- package/plugins/pbr/commands/intel.md +5 -0
- package/plugins/pbr/commands/join-discord.md +11 -0
- package/plugins/pbr/commands/list-phase-assumptions.md +5 -0
- package/plugins/pbr/commands/map-codebase.md +6 -0
- package/plugins/pbr/commands/milestone-summary.md +6 -0
- package/plugins/pbr/commands/new-milestone.md +8 -0
- package/plugins/pbr/commands/new-project.md +6 -0
- package/plugins/pbr/commands/pause-work.md +5 -0
- package/plugins/pbr/commands/plan-milestone-gaps.md +7 -0
- package/plugins/pbr/commands/plan-phase.md +6 -0
- package/plugins/pbr/commands/plant-seed.md +6 -0
- package/plugins/pbr/commands/profile-user.md +5 -0
- package/plugins/pbr/commands/profile.md +5 -0
- package/plugins/pbr/commands/progress.md +6 -0
- package/plugins/pbr/commands/quick.md +1 -1
- package/plugins/pbr/commands/reapply-patches.md +47 -0
- package/plugins/pbr/commands/release.md +6 -0
- package/plugins/pbr/commands/remove-phase.md +66 -0
- package/plugins/pbr/commands/research-phase.md +59 -0
- package/plugins/pbr/commands/resume-work.md +5 -0
- package/plugins/pbr/commands/seed.md +6 -0
- package/plugins/pbr/commands/session-report.md +5 -0
- package/plugins/pbr/commands/set-profile.md +6 -0
- package/plugins/pbr/commands/settings.md +5 -0
- package/plugins/pbr/commands/setup.md +1 -1
- package/plugins/pbr/commands/ship.md +5 -0
- package/plugins/pbr/commands/stats.md +6 -0
- package/plugins/pbr/commands/test.md +5 -0
- package/plugins/pbr/commands/thread.md +6 -0
- package/plugins/pbr/commands/todo.md +1 -1
- package/plugins/pbr/commands/ui-phase.md +5 -0
- package/plugins/pbr/commands/ui-review.md +5 -0
- package/plugins/pbr/commands/undo.md +5 -0
- package/plugins/pbr/commands/update.md +37 -0
- package/plugins/pbr/commands/validate-phase.md +5 -0
- package/plugins/pbr/commands/verify-work.md +6 -0
- package/plugins/pbr/dashboard/package-lock.json +6 -0
- package/plugins/pbr/dist/architecture-guard.js +59 -0
- package/plugins/pbr/dist/audit-dimensions.js +556 -0
- package/plugins/pbr/dist/auto-continue.js +277 -0
- package/plugins/pbr/dist/block-skill-self-read.js +124 -0
- package/plugins/pbr/dist/check-agent-state-write.js +63 -0
- package/plugins/pbr/dist/check-config-change.js +162 -0
- package/plugins/pbr/dist/check-cross-plugin-sync.js +93 -0
- package/plugins/pbr/dist/check-dangerous-commands.js +193 -0
- package/plugins/pbr/dist/check-direct-state-write.js +37 -0
- package/plugins/pbr/dist/check-doc-sprawl.js +102 -0
- package/plugins/pbr/dist/check-phase-boundary.js +191 -0
- package/plugins/pbr/dist/check-plan-format.js +241 -0
- package/plugins/pbr/dist/check-read-first.js +345 -0
- package/plugins/pbr/dist/check-roadmap-sync.js +503 -0
- package/plugins/pbr/dist/check-skill-workflow.js +354 -0
- package/plugins/pbr/dist/check-state-sync.js +658 -0
- package/plugins/pbr/dist/check-subagent-output.js +452 -0
- package/plugins/pbr/dist/check-summary-gate.js +199 -0
- package/plugins/pbr/dist/context-bridge.js +425 -0
- package/plugins/pbr/dist/context-budget-check.js +442 -0
- package/plugins/pbr/dist/context-quality.js +271 -0
- package/plugins/pbr/dist/enforce-context-budget.js +138 -0
- package/plugins/pbr/dist/enforce-pbr-workflow.js +277 -0
- package/plugins/pbr/dist/event-handler.js +202 -0
- package/plugins/pbr/dist/event-logger.js +125 -0
- package/plugins/pbr/dist/feedback-loop.js +172 -0
- package/plugins/pbr/dist/graph-update.js +422 -0
- package/plugins/pbr/dist/hook-logger.js +114 -0
- package/plugins/pbr/dist/hook-server-client.js +361 -0
- package/plugins/pbr/dist/hook-server.js +606 -0
- package/plugins/pbr/dist/hooks-schema.json +87 -0
- package/plugins/pbr/dist/instructions-loaded.js +173 -0
- package/plugins/pbr/dist/intercept-plan-mode.js +81 -0
- package/plugins/pbr/dist/log-notification.js +131 -0
- package/plugins/pbr/dist/log-subagent.js +349 -0
- package/plugins/pbr/dist/log-tool-failure.js +140 -0
- package/plugins/pbr/dist/milestone-learnings.js +569 -0
- package/plugins/pbr/dist/pbr-tools.js +2044 -0
- package/plugins/pbr/dist/post-bash-triage.js +154 -0
- package/plugins/pbr/dist/post-compact.js +135 -0
- package/plugins/pbr/dist/post-hoc.js +286 -0
- package/plugins/pbr/dist/post-write-dispatch.js +279 -0
- package/plugins/pbr/dist/post-write-quality.js +208 -0
- package/plugins/pbr/dist/pre-bash-dispatch.js +218 -0
- package/plugins/pbr/dist/pre-skill-dispatch.js +114 -0
- package/plugins/pbr/dist/pre-task-dispatch.js +297 -0
- package/plugins/pbr/dist/pre-write-dispatch.js +234 -0
- package/plugins/pbr/dist/progress-tracker.js +198 -0
- package/plugins/pbr/dist/prompt-guard.js +114 -0
- package/plugins/pbr/dist/prompt-routing.js +209 -0
- package/plugins/pbr/dist/quick-status.js +179 -0
- package/plugins/pbr/dist/record-incident.js +37 -0
- package/plugins/pbr/dist/run-hook.js +144 -0
- package/plugins/pbr/dist/session-cleanup.js +683 -0
- package/plugins/pbr/dist/session-tracker.js +124 -0
- package/plugins/pbr/dist/status-line.js +847 -0
- package/plugins/pbr/dist/suggest-compact.js +315 -0
- package/plugins/pbr/dist/sync-context-to-claude.js +100 -0
- package/plugins/pbr/dist/task-completed.js +206 -0
- package/plugins/pbr/dist/track-context-budget.js +432 -0
- package/plugins/pbr/dist/track-user-gates.js +88 -0
- package/plugins/pbr/dist/trust-tracker.js +193 -0
- package/plugins/pbr/dist/validate-commit.js +271 -0
- package/plugins/pbr/dist/validate-skill-args.js +222 -0
- package/plugins/pbr/dist/validate-task.js +301 -0
- package/plugins/pbr/dist/worktree-create.js +144 -0
- package/plugins/pbr/dist/worktree-remove.js +147 -0
- package/plugins/pbr/hooks/hooks.json +143 -60
- package/plugins/pbr/references/agent-contracts.md +39 -8
- package/plugins/pbr/references/agent-teams.md +3 -3
- package/plugins/pbr/references/archive/checkpoints.md +189 -0
- package/plugins/pbr/references/archive/context-quality-tiers.md +45 -0
- package/plugins/pbr/references/archive/hook-ordering.md +89 -0
- package/plugins/pbr/references/archive/limitations.md +106 -0
- package/plugins/pbr/references/archive/pbr-rules.md +194 -0
- package/plugins/pbr/references/archive/pbr-tools-cli.md +415 -0
- package/plugins/pbr/references/archive/pretooluse-jsonl-behavior.md +58 -0
- package/plugins/pbr/references/archive/signal-files.md +41 -0
- package/plugins/pbr/references/archive/tmux-setup.md +288 -0
- package/plugins/pbr/references/archive/verification-matrix.md +34 -0
- package/plugins/pbr/references/archive/verification-patterns.md +277 -0
- package/plugins/pbr/references/archive/worktree-sparse-checkout.md +86 -0
- package/plugins/pbr/references/behavioral-contexts.md +53 -0
- package/plugins/pbr/references/checkpoints.md +723 -104
- package/plugins/pbr/references/config-reference.md +472 -10
- package/plugins/pbr/references/continuation-format.md +1 -0
- package/plugins/pbr/references/decimal-phase-calculation.md +65 -0
- package/plugins/pbr/references/deviation-rules.md +12 -0
- package/plugins/pbr/references/git-integration.md +110 -27
- package/plugins/pbr/references/git-planning-commit.md +35 -0
- package/plugins/pbr/references/model-profile-resolution.md +34 -0
- package/plugins/pbr/references/model-profiles.md +90 -7
- package/plugins/pbr/references/model-selection.md +1 -1
- package/plugins/pbr/references/node-repair.md +48 -0
- package/plugins/pbr/references/plan-authoring.md +65 -0
- package/plugins/pbr/references/plan-format.md +161 -10
- package/plugins/pbr/references/questioning.md +138 -49
- package/plugins/pbr/references/reading-verification.md +4 -4
- package/plugins/pbr/references/tdd.md +263 -0
- package/plugins/pbr/references/ui-brand.md +449 -0
- package/plugins/pbr/references/verification-overrides.md +39 -0
- package/plugins/pbr/references/verification-patterns.md +529 -113
- package/plugins/pbr/scripts/architecture-guard.js +59 -0
- package/plugins/pbr/scripts/audit-checks/behavioral-compliance.js +2098 -0
- package/plugins/pbr/scripts/audit-checks/error-analysis.js +989 -0
- package/plugins/pbr/scripts/audit-checks/feature-verification.js +723 -0
- package/plugins/pbr/scripts/audit-checks/index.js +433 -0
- package/plugins/pbr/scripts/audit-checks/infrastructure.js +816 -0
- package/plugins/pbr/scripts/audit-checks/quality-metrics.js +452 -0
- package/plugins/pbr/scripts/audit-checks/session-quality.js +980 -0
- package/plugins/pbr/scripts/audit-checks/si-agent-hook-config-checks.js +466 -0
- package/plugins/pbr/scripts/audit-checks/si-cross-cutting-checks.js +272 -0
- package/plugins/pbr/scripts/audit-checks/si-skill-checks.js +424 -0
- package/plugins/pbr/scripts/audit-checks/workflow-compliance.js +1211 -0
- package/plugins/pbr/scripts/audit-dimensions.js +556 -0
- package/plugins/pbr/scripts/auto-continue.js +192 -31
- package/plugins/pbr/scripts/block-skill-self-read.js +124 -0
- package/plugins/pbr/scripts/check-agent-state-write.js +63 -0
- package/plugins/pbr/scripts/check-config-change.js +162 -0
- package/plugins/pbr/scripts/check-cross-plugin-sync.js +93 -0
- package/plugins/pbr/scripts/check-dangerous-commands.js +18 -5
- package/plugins/pbr/scripts/check-direct-state-write.js +37 -0
- package/plugins/pbr/scripts/check-phase-boundary.js +3 -8
- package/plugins/pbr/scripts/check-plan-format.js +166 -277
- package/plugins/pbr/scripts/check-read-first.js +345 -0
- package/plugins/pbr/scripts/check-roadmap-sync.js +167 -10
- package/plugins/pbr/scripts/check-skill-workflow.js +24 -27
- package/plugins/pbr/scripts/check-state-sync.js +339 -215
- package/plugins/pbr/scripts/check-subagent-output.js +338 -276
- package/plugins/pbr/scripts/check-summary-gate.js +2 -1
- package/plugins/pbr/scripts/config-schema.json +1247 -95
- package/plugins/pbr/scripts/context-bridge.js +425 -0
- package/plugins/pbr/scripts/context-budget-check.js +169 -14
- package/plugins/pbr/scripts/context-quality.js +271 -0
- package/plugins/pbr/scripts/enforce-context-budget.js +138 -0
- package/plugins/pbr/scripts/enforce-pbr-workflow.js +277 -0
- package/plugins/pbr/scripts/event-handler.js +127 -87
- package/plugins/pbr/scripts/event-logger.js +58 -25
- package/plugins/pbr/scripts/feedback-loop.js +172 -0
- package/plugins/pbr/scripts/graph-update.js +422 -0
- package/plugins/pbr/scripts/hook-logger.js +69 -35
- package/plugins/pbr/scripts/hook-server-client.js +361 -0
- package/plugins/pbr/scripts/hook-server.js +606 -0
- package/plugins/pbr/scripts/hooks-schema.json +13 -5
- package/plugins/pbr/scripts/instructions-loaded.js +173 -0
- package/plugins/pbr/scripts/intent-router.cjs +147 -0
- package/plugins/pbr/scripts/intercept-plan-mode.js +52 -18
- package/plugins/pbr/scripts/lib/alternatives.js +203 -0
- package/plugins/pbr/scripts/lib/audit.js +65 -0
- package/plugins/pbr/scripts/lib/auto-cleanup.js +221 -0
- package/plugins/pbr/scripts/lib/auto-verify.js +103 -0
- package/plugins/pbr/scripts/lib/autonomy.js +91 -0
- package/plugins/pbr/scripts/lib/build.js +719 -0
- package/plugins/pbr/scripts/lib/ci-fix-loop.js +228 -0
- package/plugins/pbr/scripts/lib/circuit-state.js +133 -0
- package/plugins/pbr/scripts/lib/commands.js +483 -0
- package/plugins/pbr/scripts/lib/completion.js +377 -0
- package/plugins/pbr/scripts/lib/compound.js +216 -0
- package/plugins/pbr/scripts/lib/config.js +1315 -0
- package/plugins/pbr/scripts/lib/context.js +254 -0
- package/plugins/pbr/scripts/lib/contextual-help.js +207 -0
- package/plugins/pbr/scripts/lib/convention-detector.js +413 -0
- package/plugins/pbr/scripts/lib/core.js +1569 -0
- package/plugins/pbr/scripts/lib/dashboard-launch.js +364 -0
- package/plugins/pbr/scripts/lib/data-hygiene.js +179 -0
- package/plugins/pbr/scripts/lib/decision-extraction.js +183 -0
- package/plugins/pbr/scripts/lib/decisions.js +194 -0
- package/plugins/pbr/scripts/lib/dependency-break.js +147 -0
- package/plugins/pbr/scripts/lib/format-validators.js +1050 -0
- package/plugins/pbr/scripts/lib/frontmatter.js +302 -0
- package/plugins/pbr/scripts/lib/gates/advisories.js +129 -0
- package/plugins/pbr/scripts/lib/gates/build-dependency.js +115 -0
- package/plugins/pbr/scripts/lib/gates/build-executor.js +104 -0
- package/plugins/pbr/scripts/lib/gates/doc-existence.js +46 -0
- package/plugins/pbr/scripts/lib/gates/helpers.js +93 -0
- package/plugins/pbr/scripts/lib/gates/inline-execution.js +185 -0
- package/plugins/pbr/scripts/lib/gates/milestone-complete.js +136 -0
- package/plugins/pbr/scripts/lib/gates/milestone-summary.js +119 -0
- package/plugins/pbr/scripts/lib/gates/multi-phase-loader.js +147 -0
- package/plugins/pbr/scripts/lib/gates/plan-executor.js +36 -0
- package/plugins/pbr/scripts/lib/gates/plan-validation.js +114 -0
- package/plugins/pbr/scripts/lib/gates/quick-executor.js +76 -0
- package/plugins/pbr/scripts/lib/gates/review-planner.js +61 -0
- package/plugins/pbr/scripts/lib/gates/review-verifier.js +69 -0
- package/plugins/pbr/scripts/lib/gates/rich-agent-context.js +143 -0
- package/plugins/pbr/scripts/lib/gates/user-confirmation.js +93 -0
- package/plugins/pbr/scripts/lib/graph-cli.js +89 -0
- package/plugins/pbr/scripts/lib/graph.js +553 -0
- package/plugins/pbr/scripts/lib/health-checks.js +107 -0
- package/plugins/pbr/scripts/lib/health-phase06.js +120 -0
- package/plugins/pbr/scripts/lib/health.js +133 -0
- package/plugins/pbr/scripts/lib/help.js +151 -0
- package/plugins/pbr/scripts/lib/history.js +150 -0
- package/plugins/pbr/scripts/lib/hypothesis-runner.js +127 -0
- package/plugins/pbr/scripts/lib/impact-analysis.js +319 -0
- package/plugins/pbr/scripts/lib/incidents.js +190 -0
- package/plugins/pbr/scripts/lib/init.js +643 -0
- package/plugins/pbr/scripts/lib/insights-parser.js +320 -0
- package/plugins/pbr/scripts/lib/intel.js +653 -0
- package/plugins/pbr/scripts/lib/learnings.js +511 -0
- package/plugins/pbr/scripts/lib/local-llm/client.js +237 -0
- package/plugins/pbr/scripts/lib/local-llm/health.js +12 -0
- package/plugins/pbr/scripts/lib/local-llm/index.js +89 -0
- package/plugins/pbr/scripts/lib/local-llm/metrics.js +20 -0
- package/plugins/pbr/scripts/lib/local-llm/operations/classify-artifact.js +4 -0
- package/plugins/pbr/scripts/lib/local-llm/operations/classify-commit.js +4 -0
- package/plugins/pbr/scripts/lib/local-llm/operations/classify-error.js +4 -0
- package/plugins/pbr/scripts/lib/local-llm/operations/classify-file-intent.js +4 -0
- package/plugins/pbr/scripts/lib/local-llm/operations/score-source.js +72 -0
- package/plugins/pbr/scripts/lib/local-llm/operations/summarize-context.js +62 -0
- package/plugins/pbr/scripts/lib/local-llm/operations/triage-test-output.js +12 -0
- package/plugins/pbr/scripts/lib/local-llm/operations/validate-task.js +4 -0
- package/plugins/pbr/scripts/lib/local-llm/router.js +101 -0
- package/plugins/pbr/scripts/lib/local-llm/shadow.js +60 -0
- package/plugins/pbr/scripts/lib/local-llm/threshold-tuner.js +118 -0
- package/plugins/pbr/scripts/lib/migrate.js +298 -0
- package/plugins/pbr/scripts/lib/milestone.js +306 -0
- package/plugins/pbr/scripts/lib/negative-knowledge.js +194 -0
- package/plugins/pbr/scripts/lib/notification-throttle.js +141 -0
- package/plugins/pbr/scripts/lib/onboarding-generator.js +288 -0
- package/plugins/pbr/scripts/lib/parse-args.js +134 -0
- package/plugins/pbr/scripts/lib/pattern-routing.js +55 -0
- package/plugins/pbr/scripts/lib/patterns.js +272 -0
- package/plugins/pbr/scripts/lib/perf.js +190 -0
- package/plugins/pbr/scripts/lib/phase.js +1027 -0
- package/plugins/pbr/scripts/lib/pid-lock.js +154 -0
- package/plugins/pbr/scripts/lib/post-hoc.js +160 -0
- package/plugins/pbr/scripts/lib/pre-commit-checks.js +220 -0
- package/plugins/pbr/scripts/lib/pre-research.js +133 -0
- package/plugins/pbr/scripts/lib/preview.js +174 -0
- package/plugins/pbr/scripts/lib/progress-visualization.js +296 -0
- package/plugins/pbr/scripts/lib/quick-init.js +131 -0
- package/plugins/pbr/scripts/lib/reference.js +236 -0
- package/plugins/pbr/scripts/lib/requirements.js +153 -0
- package/plugins/pbr/scripts/lib/resolve-root.js +66 -0
- package/plugins/pbr/scripts/lib/reverse-spec.js +259 -0
- package/plugins/pbr/scripts/lib/roadmap.js +1113 -0
- package/plugins/pbr/scripts/lib/security-scan.js +200 -0
- package/plugins/pbr/scripts/lib/session-briefing.js +895 -0
- package/plugins/pbr/scripts/lib/skill-section.js +99 -0
- package/plugins/pbr/scripts/lib/smart-next-task.js +207 -0
- package/plugins/pbr/scripts/lib/snapshot-manager.js +232 -0
- package/plugins/pbr/scripts/lib/spec-diff.js +209 -0
- package/plugins/pbr/scripts/lib/spec-engine.js +189 -0
- package/plugins/pbr/scripts/lib/spot-check.js +642 -0
- package/plugins/pbr/scripts/lib/state-queue.js +171 -0
- package/plugins/pbr/scripts/lib/state.js +1187 -0
- package/plugins/pbr/scripts/lib/status-render.js +511 -0
- package/plugins/pbr/scripts/lib/step-verify.js +149 -0
- package/plugins/pbr/scripts/lib/subagent-validators.js +1059 -0
- package/plugins/pbr/scripts/lib/suggest-next.js +435 -0
- package/plugins/pbr/scripts/lib/team-composer.js +87 -0
- package/plugins/pbr/scripts/lib/team-coordinator.js +153 -0
- package/plugins/pbr/scripts/lib/tech-debt-scanner.js +116 -0
- package/plugins/pbr/scripts/lib/template.js +222 -0
- package/plugins/pbr/scripts/lib/templates.js +362 -0
- package/plugins/pbr/scripts/lib/test-cache.js +54 -0
- package/plugins/pbr/scripts/lib/test-selection.js +163 -0
- package/plugins/pbr/scripts/lib/todo.js +300 -0
- package/plugins/pbr/scripts/lib/trust-gate.js +84 -0
- package/plugins/pbr/scripts/lib/verify.js +1473 -0
- package/plugins/pbr/scripts/lib/wiring-check.js +196 -0
- package/plugins/pbr/scripts/log-notification.js +131 -0
- package/plugins/pbr/scripts/log-subagent.js +203 -18
- package/plugins/pbr/scripts/log-tool-failure.js +60 -8
- package/plugins/pbr/scripts/milestone-learnings.js +569 -0
- package/plugins/pbr/scripts/package.json +1 -1
- package/plugins/pbr/scripts/pbr-tools.js +1833 -1167
- package/plugins/pbr/scripts/post-bash-triage.js +154 -0
- package/plugins/pbr/scripts/post-compact.js +135 -0
- package/plugins/pbr/scripts/post-hoc.js +286 -0
- package/plugins/pbr/scripts/post-write-dispatch.js +237 -31
- package/plugins/pbr/scripts/post-write-quality.js +4 -3
- package/plugins/pbr/scripts/pre-bash-dispatch.js +154 -52
- package/plugins/pbr/scripts/pre-skill-dispatch.js +114 -0
- package/plugins/pbr/scripts/pre-task-dispatch.js +297 -0
- package/plugins/pbr/scripts/pre-write-dispatch.js +170 -73
- package/plugins/pbr/scripts/progress-tracker.js +144 -307
- package/plugins/pbr/scripts/prompt-guard.js +114 -0
- package/plugins/pbr/scripts/prompt-routing.js +209 -0
- package/plugins/pbr/scripts/quick-status.js +179 -0
- package/plugins/pbr/scripts/record-incident.js +37 -0
- package/plugins/pbr/scripts/risk-classifier.cjs +123 -0
- package/plugins/pbr/scripts/run-hook.js +62 -10
- package/plugins/pbr/scripts/session-cleanup.js +458 -29
- package/plugins/pbr/scripts/session-tracker.js +124 -0
- package/plugins/pbr/scripts/status-line.js +591 -32
- package/plugins/pbr/scripts/suggest-compact.js +203 -7
- package/plugins/pbr/scripts/sync-context-to-claude.js +100 -0
- package/plugins/pbr/scripts/task-completed.js +165 -4
- package/plugins/pbr/scripts/test/config.test.js +126 -0
- package/plugins/pbr/scripts/test/cross-platform.test.js +131 -0
- package/plugins/pbr/scripts/test/fixtures/config.json +20 -0
- package/plugins/pbr/scripts/test/fixtures/plan.md +54 -0
- package/plugins/pbr/scripts/test/fixtures/project.md +30 -0
- package/plugins/pbr/scripts/test/fixtures/roadmap.md +55 -0
- package/plugins/pbr/scripts/test/fixtures/state.md +60 -0
- package/plugins/pbr/scripts/test/fixtures/summary.md +35 -0
- package/plugins/pbr/scripts/test/fixtures.test.js +184 -0
- package/plugins/pbr/scripts/test/phase.test.js +142 -0
- package/plugins/pbr/scripts/test/roadmap.test.js +96 -0
- package/plugins/pbr/scripts/test/state.test.js +163 -0
- package/plugins/pbr/scripts/track-context-budget.js +368 -99
- package/plugins/pbr/scripts/track-user-gates.js +88 -0
- package/plugins/pbr/scripts/trust-tracker.js +193 -0
- package/plugins/pbr/scripts/validate-commit.js +97 -26
- package/plugins/pbr/scripts/validate-skill-args.js +87 -15
- package/plugins/pbr/scripts/validate-task.js +112 -626
- package/plugins/pbr/scripts/worktree-create.js +144 -0
- package/plugins/pbr/scripts/worktree-remove.js +147 -0
- package/plugins/pbr/skills/audit/SKILL.md +195 -24
- package/plugins/pbr/skills/audit-fix/SKILL.md +326 -0
- package/plugins/pbr/skills/autonomous/SKILL.md +545 -0
- package/plugins/pbr/skills/backlog/SKILL.md +56 -0
- package/plugins/pbr/skills/begin/SKILL.md +507 -153
- package/plugins/pbr/skills/begin/templates/STATE.md.tmpl +1 -2
- package/plugins/pbr/skills/begin/templates/config.json.tmpl +415 -36
- package/plugins/pbr/skills/begin/templates/researcher-prompt.md.tmpl +28 -0
- package/plugins/pbr/skills/begin/templates/roadmap-prompt.md.tmpl +28 -3
- package/plugins/pbr/skills/begin/templates/synthesis-prompt.md.tmpl +33 -5
- package/plugins/pbr/skills/build/SKILL.md +1040 -354
- package/plugins/pbr/skills/build/templates/continuation-prompt.md.tmpl +26 -0
- package/plugins/pbr/skills/build/templates/executor-prompt.md.tmpl +77 -0
- package/plugins/pbr/skills/build/templates/inline-verifier-prompt.md.tmpl +33 -0
- package/plugins/pbr/skills/config/SKILL.md +111 -9
- package/plugins/pbr/skills/continue/SKILL.md +113 -33
- package/plugins/pbr/skills/dashboard/SKILL.md +21 -9
- package/plugins/pbr/skills/debug/SKILL.md +70 -12
- package/plugins/pbr/skills/debug/templates/continuation-prompt.md.tmpl +12 -1
- package/plugins/pbr/skills/debug/templates/initial-investigation-prompt.md.tmpl +12 -5
- package/plugins/pbr/skills/discuss/SKILL.md +206 -25
- package/plugins/pbr/skills/discuss/templates/CONTEXT.md.tmpl +21 -1
- package/plugins/pbr/skills/do/SKILL.md +119 -24
- package/plugins/pbr/skills/explore/SKILL.md +95 -20
- package/plugins/pbr/skills/fast/SKILL.md +94 -0
- package/plugins/pbr/skills/forensics/SKILL.md +144 -0
- package/plugins/pbr/skills/health/SKILL.md +35 -117
- package/plugins/pbr/skills/help/SKILL.md +83 -101
- package/plugins/pbr/skills/import/SKILL.md +332 -13
- package/plugins/pbr/skills/intel/SKILL.md +131 -0
- package/plugins/pbr/skills/list-phase-assumptions/SKILL.md +231 -0
- package/plugins/pbr/skills/milestone/SKILL.md +421 -263
- package/plugins/pbr/skills/milestone/templates/audit-output.md.tmpl +76 -0
- package/plugins/pbr/skills/milestone/templates/complete-output.md.tmpl +32 -0
- package/plugins/pbr/skills/milestone/templates/edge-cases.md +54 -0
- package/plugins/pbr/skills/milestone/templates/gaps-output.md.tmpl +25 -0
- package/plugins/pbr/skills/milestone/templates/integration-checker-prompt.md.tmpl +25 -0
- package/plugins/pbr/skills/milestone/templates/new-output.md.tmpl +29 -0
- package/plugins/pbr/skills/milestone-summary/SKILL.md +86 -0
- package/plugins/pbr/skills/note/SKILL.md +20 -4
- package/plugins/pbr/skills/pause/SKILL.md +53 -14
- package/plugins/pbr/skills/pause/templates/continue-here.md.tmpl +33 -52
- package/plugins/pbr/skills/plan/SKILL.md +526 -280
- package/plugins/pbr/skills/plan/templates/checker-prompt.md.tmpl +5 -2
- package/plugins/pbr/skills/plan/templates/completion-output.md.tmpl +27 -0
- package/plugins/pbr/skills/plan/templates/planner-prompt.md.tmpl +27 -1
- package/plugins/pbr/skills/plan/templates/revision-prompt.md.tmpl +21 -5
- package/plugins/pbr/skills/profile/SKILL.md +185 -0
- package/plugins/pbr/skills/profile-user/SKILL.md +226 -0
- package/plugins/pbr/skills/quick/SKILL.md +434 -100
- package/plugins/pbr/skills/release/SKILL.md +206 -0
- package/plugins/pbr/skills/resume/SKILL.md +169 -46
- package/plugins/pbr/skills/review/SKILL.md +217 -164
- package/plugins/pbr/skills/review/templates/verifier-prompt.md.tmpl +7 -0
- package/plugins/pbr/skills/scan/SKILL.md +151 -106
- package/plugins/pbr/skills/scan/templates/mapper-prompt.md.tmpl +5 -56
- package/plugins/pbr/skills/seed/SKILL.md +87 -0
- package/plugins/pbr/skills/session-report/SKILL.md +130 -0
- package/plugins/pbr/skills/setup/SKILL.md +149 -202
- package/plugins/pbr/skills/shared/agent-context-enrichment.md +21 -0
- package/plugins/pbr/skills/shared/agent-type-resolution.md +32 -0
- package/plugins/pbr/skills/shared/commit-planning-docs.md +8 -0
- package/plugins/pbr/skills/shared/context-budget.md +66 -1
- package/plugins/pbr/skills/shared/context-loader-task.md +18 -11
- package/plugins/pbr/skills/shared/digest-select.md +2 -2
- package/plugins/pbr/skills/shared/domain-probes.md +1 -1
- package/plugins/pbr/skills/shared/error-reporting.md +38 -60
- package/plugins/pbr/skills/shared/gate-prompts.md +4 -2
- package/plugins/pbr/skills/shared/memory-capture.md +48 -0
- package/plugins/pbr/skills/shared/phase-argument-parsing.md +4 -4
- package/plugins/pbr/skills/shared/revision-loop.md +24 -6
- package/plugins/pbr/skills/shared/state-update.md +47 -54
- package/plugins/pbr/skills/shared/universal-anti-patterns.md +27 -4
- package/plugins/pbr/skills/ship/SKILL.md +155 -0
- package/plugins/pbr/skills/stats/SKILL.md +80 -0
- package/plugins/pbr/skills/status/SKILL.md +184 -119
- package/plugins/pbr/skills/test/SKILL.md +254 -0
- package/plugins/pbr/skills/thread/SKILL.md +73 -0
- package/plugins/pbr/skills/todo/SKILL.md +28 -72
- package/plugins/pbr/skills/ui-phase/SKILL.md +180 -0
- package/plugins/pbr/skills/ui-review/SKILL.md +206 -0
- package/plugins/pbr/skills/undo/SKILL.md +221 -0
- package/plugins/pbr/skills/validate-phase/SKILL.md +362 -0
- package/plugins/pbr/templates/CONTEXT.md.tmpl +45 -20
- package/plugins/pbr/templates/DISCOVERY.md.tmpl +29 -0
- package/plugins/pbr/templates/DISCUSSION-LOG.md.tmpl +49 -0
- package/plugins/pbr/templates/HANDOFF.json.tmpl +30 -0
- package/plugins/pbr/templates/INTEGRATION-REPORT.md.tmpl +18 -2
- package/plugins/pbr/templates/MILESTONE-AUDIT.md.tmpl +44 -0
- package/plugins/pbr/templates/PROJECT.md.tmpl +126 -0
- package/plugins/pbr/templates/REQUIREMENTS.md.tmpl +96 -0
- package/plugins/pbr/templates/RETROSPECTIVE.md.tmpl +43 -0
- package/plugins/pbr/templates/ROADMAP.md.tmpl +108 -14
- package/plugins/pbr/templates/SUMMARY-complex.md.tmpl +133 -0
- package/plugins/pbr/templates/SUMMARY-minimal.md.tmpl +55 -0
- package/plugins/pbr/templates/SUMMARY.md.tmpl +21 -0
- package/plugins/pbr/templates/UAT.md.tmpl +94 -0
- package/plugins/pbr/templates/UI-SPEC.md.tmpl +144 -0
- package/plugins/pbr/templates/VALIDATION.md.tmpl +94 -0
- package/plugins/pbr/templates/VERIFICATION-DETAIL.md.tmpl +49 -13
- package/plugins/pbr/templates/project-CONTEXT.md.tmpl +59 -0
- package/plugins/pbr/templates/research-outputs/ARCHITECTURE.md.tmpl +91 -0
- package/plugins/pbr/templates/research-outputs/FEATURES.md.tmpl +64 -0
- package/plugins/pbr/templates/research-outputs/PITFALLS.md.tmpl +50 -0
- package/plugins/pbr/templates/research-outputs/STACK.md.tmpl +63 -0
- package/plugins/pbr/templates/research-outputs/SUMMARY.md.tmpl +98 -0
- package/scripts/build-hooks.js +61 -0
- package/scripts/check-ci.js +100 -0
- package/scripts/clean-changelog.js +364 -0
- package/scripts/generate-derivatives.js +581 -0
- package/scripts/posttest.js +93 -0
- package/scripts/release.js +262 -0
- package/scripts/run-tests.cjs +29 -0
- package/scripts/test-wrapper.js +43 -0
- package/dashboard/bin/cli.js +0 -25
- package/dashboard/public/css/layout.css +0 -704
- package/dashboard/public/css/status-colors.css +0 -98
- package/dashboard/public/css/tokens.css +0 -59
- package/dashboard/public/js/htmx-title.js +0 -5
- package/dashboard/public/js/sidebar-toggle.js +0 -34
- package/dashboard/public/js/sse-client.js +0 -100
- package/dashboard/public/js/theme-toggle.js +0 -46
- package/dashboard/src/app.js +0 -91
- package/dashboard/src/middleware/current-phase.js +0 -24
- package/dashboard/src/middleware/errorHandler.js +0 -52
- package/dashboard/src/middleware/notFoundHandler.js +0 -9
- package/dashboard/src/repositories/planning.repository.js +0 -130
- package/dashboard/src/routes/events.routes.js +0 -45
- package/dashboard/src/routes/index.routes.js +0 -35
- package/dashboard/src/routes/pages.routes.js +0 -426
- package/dashboard/src/server.js +0 -42
- package/dashboard/src/services/analytics.service.js +0 -141
- package/dashboard/src/services/dashboard.service.js +0 -309
- package/dashboard/src/services/milestone.service.js +0 -222
- package/dashboard/src/services/notes.service.js +0 -50
- package/dashboard/src/services/phase.service.js +0 -232
- package/dashboard/src/services/project.service.js +0 -57
- package/dashboard/src/services/roadmap.service.js +0 -258
- package/dashboard/src/services/sse.service.js +0 -58
- package/dashboard/src/services/todo.service.js +0 -272
- package/dashboard/src/services/watcher.service.js +0 -48
- package/dashboard/src/utils/cache.js +0 -55
- package/dashboard/src/views/analytics.ejs +0 -5
- package/dashboard/src/views/coming-soon.ejs +0 -11
- package/dashboard/src/views/dependencies.ejs +0 -5
- package/dashboard/src/views/error.ejs +0 -20
- package/dashboard/src/views/index.ejs +0 -5
- package/dashboard/src/views/milestone-detail.ejs +0 -5
- package/dashboard/src/views/milestones.ejs +0 -5
- package/dashboard/src/views/notes.ejs +0 -5
- package/dashboard/src/views/partials/analytics-content.ejs +0 -90
- package/dashboard/src/views/partials/breadcrumbs.ejs +0 -14
- package/dashboard/src/views/partials/dashboard-content.ejs +0 -84
- package/dashboard/src/views/partials/dependencies-content.ejs +0 -48
- package/dashboard/src/views/partials/empty-state.ejs +0 -7
- package/dashboard/src/views/partials/footer.ejs +0 -3
- package/dashboard/src/views/partials/head.ejs +0 -30
- package/dashboard/src/views/partials/header.ejs +0 -21
- package/dashboard/src/views/partials/layout-bottom.ejs +0 -43
- package/dashboard/src/views/partials/layout-top.ejs +0 -16
- package/dashboard/src/views/partials/milestone-detail-content.ejs +0 -20
- package/dashboard/src/views/partials/milestones-content.ejs +0 -88
- package/dashboard/src/views/partials/notes-content.ejs +0 -23
- package/dashboard/src/views/partials/phase-content.ejs +0 -193
- package/dashboard/src/views/partials/phase-doc-content.ejs +0 -38
- package/dashboard/src/views/partials/phases-content.ejs +0 -124
- package/dashboard/src/views/partials/roadmap-content.ejs +0 -180
- package/dashboard/src/views/partials/sidebar.ejs +0 -99
- package/dashboard/src/views/partials/todo-create-content.ejs +0 -54
- package/dashboard/src/views/partials/todo-detail-content.ejs +0 -42
- package/dashboard/src/views/partials/todos-content.ejs +0 -97
- package/dashboard/src/views/phase-detail.ejs +0 -5
- package/dashboard/src/views/phase-doc.ejs +0 -5
- package/dashboard/src/views/phases.ejs +0 -5
- package/dashboard/src/views/roadmap.ejs +0 -5
- package/dashboard/src/views/todo-create.ejs +0 -5
- package/dashboard/src/views/todo-detail.ejs +0 -5
- package/dashboard/src/views/todos.ejs +0 -5
- package/plugins/copilot-pbr/CHANGELOG.md +0 -19
- package/plugins/copilot-pbr/README.md +0 -139
- package/plugins/copilot-pbr/agents/audit.agent.md +0 -113
- package/plugins/copilot-pbr/agents/codebase-mapper.agent.md +0 -151
- package/plugins/copilot-pbr/agents/debugger.agent.md +0 -182
- package/plugins/copilot-pbr/agents/executor.agent.md +0 -267
- package/plugins/copilot-pbr/agents/general.agent.md +0 -88
- package/plugins/copilot-pbr/agents/integration-checker.agent.md +0 -119
- package/plugins/copilot-pbr/agents/plan-checker.agent.md +0 -208
- package/plugins/copilot-pbr/agents/planner.agent.md +0 -238
- package/plugins/copilot-pbr/agents/researcher.agent.md +0 -186
- package/plugins/copilot-pbr/agents/synthesizer.agent.md +0 -126
- package/plugins/copilot-pbr/agents/verifier.agent.md +0 -228
- package/plugins/copilot-pbr/hooks/hooks.json +0 -156
- package/plugins/copilot-pbr/plugin.json +0 -30
- package/plugins/copilot-pbr/references/agent-anti-patterns.md +0 -25
- package/plugins/copilot-pbr/references/agent-contracts.md +0 -297
- package/plugins/copilot-pbr/references/agent-interactions.md +0 -135
- package/plugins/copilot-pbr/references/agent-teams.md +0 -55
- package/plugins/copilot-pbr/references/checkpoints.md +0 -158
- package/plugins/copilot-pbr/references/common-bug-patterns.md +0 -14
- package/plugins/copilot-pbr/references/config-reference.md +0 -442
- package/plugins/copilot-pbr/references/continuation-format.md +0 -213
- package/plugins/copilot-pbr/references/deviation-rules.md +0 -113
- package/plugins/copilot-pbr/references/git-integration.md +0 -227
- package/plugins/copilot-pbr/references/integration-patterns.md +0 -118
- package/plugins/copilot-pbr/references/model-profiles.md +0 -100
- package/plugins/copilot-pbr/references/model-selection.md +0 -32
- package/plugins/copilot-pbr/references/pbr-rules.md +0 -195
- package/plugins/copilot-pbr/references/pbr-tools-cli.md +0 -285
- package/plugins/copilot-pbr/references/plan-authoring.md +0 -182
- package/plugins/copilot-pbr/references/plan-format.md +0 -288
- package/plugins/copilot-pbr/references/planning-config.md +0 -214
- package/plugins/copilot-pbr/references/questioning.md +0 -215
- package/plugins/copilot-pbr/references/reading-verification.md +0 -128
- package/plugins/copilot-pbr/references/stub-patterns.md +0 -161
- package/plugins/copilot-pbr/references/subagent-coordination.md +0 -120
- package/plugins/copilot-pbr/references/ui-formatting.md +0 -444
- package/plugins/copilot-pbr/references/verification-patterns.md +0 -199
- package/plugins/copilot-pbr/references/wave-execution.md +0 -96
- package/plugins/copilot-pbr/rules/pbr-workflow.mdc +0 -48
- package/plugins/copilot-pbr/setup.ps1 +0 -93
- package/plugins/copilot-pbr/setup.sh +0 -93
- package/plugins/copilot-pbr/skills/audit/SKILL.md +0 -330
- package/plugins/copilot-pbr/skills/begin/SKILL.md +0 -589
- package/plugins/copilot-pbr/skills/begin/templates/PROJECT.md.tmpl +0 -34
- package/plugins/copilot-pbr/skills/begin/templates/REQUIREMENTS.md.tmpl +0 -19
- package/plugins/copilot-pbr/skills/begin/templates/STATE.md.tmpl +0 -50
- package/plugins/copilot-pbr/skills/begin/templates/config.json.tmpl +0 -64
- package/plugins/copilot-pbr/skills/begin/templates/researcher-prompt.md.tmpl +0 -20
- package/plugins/copilot-pbr/skills/begin/templates/roadmap-prompt.md.tmpl +0 -31
- package/plugins/copilot-pbr/skills/begin/templates/synthesis-prompt.md.tmpl +0 -17
- package/plugins/copilot-pbr/skills/build/SKILL.md +0 -960
- package/plugins/copilot-pbr/skills/config/SKILL.md +0 -250
- package/plugins/copilot-pbr/skills/continue/SKILL.md +0 -159
- package/plugins/copilot-pbr/skills/dashboard/SKILL.md +0 -43
- package/plugins/copilot-pbr/skills/debug/SKILL.md +0 -508
- package/plugins/copilot-pbr/skills/debug/templates/continuation-prompt.md.tmpl +0 -17
- package/plugins/copilot-pbr/skills/debug/templates/initial-investigation-prompt.md.tmpl +0 -28
- package/plugins/copilot-pbr/skills/discuss/SKILL.md +0 -353
- package/plugins/copilot-pbr/skills/discuss/templates/CONTEXT.md.tmpl +0 -62
- package/plugins/copilot-pbr/skills/discuss/templates/decision-categories.md +0 -10
- package/plugins/copilot-pbr/skills/do/SKILL.md +0 -66
- package/plugins/copilot-pbr/skills/explore/SKILL.md +0 -373
- package/plugins/copilot-pbr/skills/health/SKILL.md +0 -283
- package/plugins/copilot-pbr/skills/health/templates/check-pattern.md.tmpl +0 -31
- package/plugins/copilot-pbr/skills/health/templates/output-format.md.tmpl +0 -64
- package/plugins/copilot-pbr/skills/help/SKILL.md +0 -170
- package/plugins/copilot-pbr/skills/import/SKILL.md +0 -502
- package/plugins/copilot-pbr/skills/milestone/SKILL.md +0 -745
- package/plugins/copilot-pbr/skills/milestone/templates/audit-report.md.tmpl +0 -49
- package/plugins/copilot-pbr/skills/milestone/templates/stats-file.md.tmpl +0 -31
- package/plugins/copilot-pbr/skills/note/SKILL.md +0 -213
- package/plugins/copilot-pbr/skills/pause/SKILL.md +0 -247
- package/plugins/copilot-pbr/skills/pause/templates/continue-here.md.tmpl +0 -72
- package/plugins/copilot-pbr/skills/plan/SKILL.md +0 -662
- package/plugins/copilot-pbr/skills/plan/templates/checker-prompt.md.tmpl +0 -22
- package/plugins/copilot-pbr/skills/plan/templates/gap-closure-prompt.md.tmpl +0 -33
- package/plugins/copilot-pbr/skills/plan/templates/planner-prompt.md.tmpl +0 -39
- package/plugins/copilot-pbr/skills/plan/templates/researcher-prompt.md.tmpl +0 -20
- package/plugins/copilot-pbr/skills/plan/templates/revision-prompt.md.tmpl +0 -24
- package/plugins/copilot-pbr/skills/quick/SKILL.md +0 -376
- package/plugins/copilot-pbr/skills/resume/SKILL.md +0 -399
- package/plugins/copilot-pbr/skills/review/SKILL.md +0 -653
- package/plugins/copilot-pbr/skills/review/templates/debugger-prompt.md.tmpl +0 -61
- package/plugins/copilot-pbr/skills/review/templates/gap-planner-prompt.md.tmpl +0 -41
- package/plugins/copilot-pbr/skills/review/templates/verifier-prompt.md.tmpl +0 -116
- package/plugins/copilot-pbr/skills/scan/SKILL.md +0 -299
- package/plugins/copilot-pbr/skills/scan/templates/mapper-prompt.md.tmpl +0 -202
- package/plugins/copilot-pbr/skills/setup/SKILL.md +0 -296
- package/plugins/copilot-pbr/skills/shared/commit-planning-docs.md +0 -36
- package/plugins/copilot-pbr/skills/shared/config-loading.md +0 -103
- package/plugins/copilot-pbr/skills/shared/context-budget.md +0 -41
- package/plugins/copilot-pbr/skills/shared/context-loader-task.md +0 -87
- package/plugins/copilot-pbr/skills/shared/digest-select.md +0 -80
- package/plugins/copilot-pbr/skills/shared/domain-probes.md +0 -126
- package/plugins/copilot-pbr/skills/shared/error-reporting.md +0 -81
- package/plugins/copilot-pbr/skills/shared/gate-prompts.md +0 -389
- package/plugins/copilot-pbr/skills/shared/phase-argument-parsing.md +0 -46
- package/plugins/copilot-pbr/skills/shared/progress-display.md +0 -53
- package/plugins/copilot-pbr/skills/shared/revision-loop.md +0 -82
- package/plugins/copilot-pbr/skills/shared/state-loading.md +0 -63
- package/plugins/copilot-pbr/skills/shared/state-update.md +0 -162
- package/plugins/copilot-pbr/skills/shared/universal-anti-patterns.md +0 -38
- package/plugins/copilot-pbr/skills/status/SKILL.md +0 -362
- package/plugins/copilot-pbr/skills/statusline/SKILL.md +0 -149
- package/plugins/copilot-pbr/skills/todo/SKILL.md +0 -279
- package/plugins/copilot-pbr/templates/CONTEXT.md.tmpl +0 -53
- package/plugins/copilot-pbr/templates/INTEGRATION-REPORT.md.tmpl +0 -152
- package/plugins/copilot-pbr/templates/RESEARCH-SUMMARY.md.tmpl +0 -98
- package/plugins/copilot-pbr/templates/ROADMAP.md.tmpl +0 -41
- package/plugins/copilot-pbr/templates/SUMMARY.md.tmpl +0 -82
- package/plugins/copilot-pbr/templates/VERIFICATION-DETAIL.md.tmpl +0 -117
- package/plugins/copilot-pbr/templates/codebase/ARCHITECTURE.md.tmpl +0 -98
- package/plugins/copilot-pbr/templates/codebase/CONCERNS.md.tmpl +0 -93
- package/plugins/copilot-pbr/templates/codebase/CONVENTIONS.md.tmpl +0 -104
- package/plugins/copilot-pbr/templates/codebase/INTEGRATIONS.md.tmpl +0 -78
- package/plugins/copilot-pbr/templates/codebase/STACK.md.tmpl +0 -78
- package/plugins/copilot-pbr/templates/codebase/STRUCTURE.md.tmpl +0 -80
- package/plugins/copilot-pbr/templates/codebase/TESTING.md.tmpl +0 -107
- package/plugins/copilot-pbr/templates/continue-here.md.tmpl +0 -74
- package/plugins/copilot-pbr/templates/prompt-partials/phase-project-context.md.tmpl +0 -38
- package/plugins/copilot-pbr/templates/research/ARCHITECTURE.md.tmpl +0 -124
- package/plugins/copilot-pbr/templates/research/STACK.md.tmpl +0 -71
- package/plugins/copilot-pbr/templates/research/SUMMARY.md.tmpl +0 -112
- package/plugins/copilot-pbr/templates/research-outputs/phase-research.md.tmpl +0 -81
- package/plugins/copilot-pbr/templates/research-outputs/project-research.md.tmpl +0 -99
- package/plugins/copilot-pbr/templates/research-outputs/synthesis.md.tmpl +0 -36
- package/plugins/cursor-pbr/.cursor-plugin/plugin.json +0 -32
- package/plugins/cursor-pbr/CHANGELOG.md +0 -15
- package/plugins/cursor-pbr/README.md +0 -123
- package/plugins/cursor-pbr/agents/audit.md +0 -178
- package/plugins/cursor-pbr/agents/codebase-mapper.md +0 -150
- package/plugins/cursor-pbr/agents/debugger.md +0 -181
- package/plugins/cursor-pbr/agents/executor.md +0 -266
- package/plugins/cursor-pbr/agents/general.md +0 -87
- package/plugins/cursor-pbr/agents/integration-checker.md +0 -118
- package/plugins/cursor-pbr/agents/plan-checker.md +0 -207
- package/plugins/cursor-pbr/agents/planner.md +0 -237
- package/plugins/cursor-pbr/agents/researcher.md +0 -185
- package/plugins/cursor-pbr/agents/synthesizer.md +0 -125
- package/plugins/cursor-pbr/agents/verifier.md +0 -227
- package/plugins/cursor-pbr/assets/.gitkeep +0 -0
- package/plugins/cursor-pbr/assets/logo.svg +0 -21
- package/plugins/cursor-pbr/hooks/hooks.json +0 -213
- package/plugins/cursor-pbr/references/agent-anti-patterns.md +0 -25
- package/plugins/cursor-pbr/references/agent-contracts.md +0 -297
- package/plugins/cursor-pbr/references/agent-interactions.md +0 -135
- package/plugins/cursor-pbr/references/agent-teams.md +0 -55
- package/plugins/cursor-pbr/references/checkpoints.md +0 -158
- package/plugins/cursor-pbr/references/common-bug-patterns.md +0 -14
- package/plugins/cursor-pbr/references/config-reference.md +0 -442
- package/plugins/cursor-pbr/references/continuation-format.md +0 -213
- package/plugins/cursor-pbr/references/deviation-rules.md +0 -113
- package/plugins/cursor-pbr/references/git-integration.md +0 -227
- package/plugins/cursor-pbr/references/integration-patterns.md +0 -118
- package/plugins/cursor-pbr/references/model-profiles.md +0 -100
- package/plugins/cursor-pbr/references/model-selection.md +0 -32
- package/plugins/cursor-pbr/references/pbr-rules.md +0 -195
- package/plugins/cursor-pbr/references/pbr-tools-cli.md +0 -285
- package/plugins/cursor-pbr/references/plan-authoring.md +0 -182
- package/plugins/cursor-pbr/references/plan-format.md +0 -288
- package/plugins/cursor-pbr/references/planning-config.md +0 -214
- package/plugins/cursor-pbr/references/questioning.md +0 -215
- package/plugins/cursor-pbr/references/reading-verification.md +0 -128
- package/plugins/cursor-pbr/references/stub-patterns.md +0 -161
- package/plugins/cursor-pbr/references/subagent-coordination.md +0 -120
- package/plugins/cursor-pbr/references/ui-formatting.md +0 -444
- package/plugins/cursor-pbr/references/verification-patterns.md +0 -199
- package/plugins/cursor-pbr/references/wave-execution.md +0 -96
- package/plugins/cursor-pbr/rules/pbr-workflow.mdc +0 -48
- package/plugins/cursor-pbr/setup.ps1 +0 -78
- package/plugins/cursor-pbr/setup.sh +0 -83
- package/plugins/cursor-pbr/skills/audit/SKILL.md +0 -331
- package/plugins/cursor-pbr/skills/begin/SKILL.md +0 -589
- package/plugins/cursor-pbr/skills/begin/templates/PROJECT.md.tmpl +0 -34
- package/plugins/cursor-pbr/skills/begin/templates/REQUIREMENTS.md.tmpl +0 -19
- package/plugins/cursor-pbr/skills/begin/templates/STATE.md.tmpl +0 -50
- package/plugins/cursor-pbr/skills/begin/templates/config.json.tmpl +0 -64
- package/plugins/cursor-pbr/skills/begin/templates/researcher-prompt.md.tmpl +0 -20
- package/plugins/cursor-pbr/skills/begin/templates/roadmap-prompt.md.tmpl +0 -31
- package/plugins/cursor-pbr/skills/begin/templates/synthesis-prompt.md.tmpl +0 -17
- package/plugins/cursor-pbr/skills/build/SKILL.md +0 -961
- package/plugins/cursor-pbr/skills/config/SKILL.md +0 -252
- package/plugins/cursor-pbr/skills/continue/SKILL.md +0 -159
- package/plugins/cursor-pbr/skills/dashboard/SKILL.md +0 -44
- package/plugins/cursor-pbr/skills/debug/SKILL.md +0 -512
- package/plugins/cursor-pbr/skills/debug/templates/continuation-prompt.md.tmpl +0 -17
- package/plugins/cursor-pbr/skills/debug/templates/initial-investigation-prompt.md.tmpl +0 -28
- package/plugins/cursor-pbr/skills/discuss/SKILL.md +0 -354
- package/plugins/cursor-pbr/skills/discuss/templates/CONTEXT.md.tmpl +0 -62
- package/plugins/cursor-pbr/skills/discuss/templates/decision-categories.md +0 -10
- package/plugins/cursor-pbr/skills/do/SKILL.md +0 -67
- package/plugins/cursor-pbr/skills/explore/SKILL.md +0 -376
- package/plugins/cursor-pbr/skills/health/SKILL.md +0 -283
- package/plugins/cursor-pbr/skills/health/templates/check-pattern.md.tmpl +0 -31
- package/plugins/cursor-pbr/skills/health/templates/output-format.md.tmpl +0 -64
- package/plugins/cursor-pbr/skills/help/SKILL.md +0 -170
- package/plugins/cursor-pbr/skills/import/SKILL.md +0 -505
- package/plugins/cursor-pbr/skills/milestone/SKILL.md +0 -746
- package/plugins/cursor-pbr/skills/milestone/templates/audit-report.md.tmpl +0 -49
- package/plugins/cursor-pbr/skills/milestone/templates/stats-file.md.tmpl +0 -31
- package/plugins/cursor-pbr/skills/note/SKILL.md +0 -214
- package/plugins/cursor-pbr/skills/pause/SKILL.md +0 -248
- package/plugins/cursor-pbr/skills/pause/templates/continue-here.md.tmpl +0 -72
- package/plugins/cursor-pbr/skills/plan/SKILL.md +0 -663
- package/plugins/cursor-pbr/skills/plan/templates/checker-prompt.md.tmpl +0 -22
- package/plugins/cursor-pbr/skills/plan/templates/gap-closure-prompt.md.tmpl +0 -33
- package/plugins/cursor-pbr/skills/plan/templates/planner-prompt.md.tmpl +0 -39
- package/plugins/cursor-pbr/skills/plan/templates/researcher-prompt.md.tmpl +0 -20
- package/plugins/cursor-pbr/skills/plan/templates/revision-prompt.md.tmpl +0 -24
- package/plugins/cursor-pbr/skills/quick/SKILL.md +0 -376
- package/plugins/cursor-pbr/skills/resume/SKILL.md +0 -399
- package/plugins/cursor-pbr/skills/review/SKILL.md +0 -654
- package/plugins/cursor-pbr/skills/review/templates/debugger-prompt.md.tmpl +0 -61
- package/plugins/cursor-pbr/skills/review/templates/gap-planner-prompt.md.tmpl +0 -41
- package/plugins/cursor-pbr/skills/review/templates/verifier-prompt.md.tmpl +0 -116
- package/plugins/cursor-pbr/skills/scan/SKILL.md +0 -300
- package/plugins/cursor-pbr/skills/scan/templates/mapper-prompt.md.tmpl +0 -202
- package/plugins/cursor-pbr/skills/setup/SKILL.md +0 -296
- package/plugins/cursor-pbr/skills/shared/commit-planning-docs.md +0 -36
- package/plugins/cursor-pbr/skills/shared/config-loading.md +0 -103
- package/plugins/cursor-pbr/skills/shared/context-budget.md +0 -41
- package/plugins/cursor-pbr/skills/shared/context-loader-task.md +0 -87
- package/plugins/cursor-pbr/skills/shared/digest-select.md +0 -80
- package/plugins/cursor-pbr/skills/shared/domain-probes.md +0 -126
- package/plugins/cursor-pbr/skills/shared/error-reporting.md +0 -81
- package/plugins/cursor-pbr/skills/shared/gate-prompts.md +0 -389
- package/plugins/cursor-pbr/skills/shared/phase-argument-parsing.md +0 -46
- package/plugins/cursor-pbr/skills/shared/progress-display.md +0 -53
- package/plugins/cursor-pbr/skills/shared/revision-loop.md +0 -82
- package/plugins/cursor-pbr/skills/shared/state-loading.md +0 -63
- package/plugins/cursor-pbr/skills/shared/state-update.md +0 -162
- package/plugins/cursor-pbr/skills/shared/universal-anti-patterns.md +0 -38
- package/plugins/cursor-pbr/skills/status/SKILL.md +0 -362
- package/plugins/cursor-pbr/skills/statusline/SKILL.md +0 -150
- package/plugins/cursor-pbr/skills/todo/SKILL.md +0 -280
- package/plugins/cursor-pbr/templates/CONTEXT.md.tmpl +0 -53
- package/plugins/cursor-pbr/templates/INTEGRATION-REPORT.md.tmpl +0 -152
- package/plugins/cursor-pbr/templates/RESEARCH-SUMMARY.md.tmpl +0 -98
- package/plugins/cursor-pbr/templates/ROADMAP.md.tmpl +0 -41
- package/plugins/cursor-pbr/templates/SUMMARY.md.tmpl +0 -82
- package/plugins/cursor-pbr/templates/VERIFICATION-DETAIL.md.tmpl +0 -117
- package/plugins/cursor-pbr/templates/codebase/ARCHITECTURE.md.tmpl +0 -98
- package/plugins/cursor-pbr/templates/codebase/CONCERNS.md.tmpl +0 -93
- package/plugins/cursor-pbr/templates/codebase/CONVENTIONS.md.tmpl +0 -104
- package/plugins/cursor-pbr/templates/codebase/INTEGRATIONS.md.tmpl +0 -78
- package/plugins/cursor-pbr/templates/codebase/STACK.md.tmpl +0 -78
- package/plugins/cursor-pbr/templates/codebase/STRUCTURE.md.tmpl +0 -80
- package/plugins/cursor-pbr/templates/codebase/TESTING.md.tmpl +0 -107
- package/plugins/cursor-pbr/templates/continue-here.md.tmpl +0 -74
- package/plugins/cursor-pbr/templates/prompt-partials/phase-project-context.md.tmpl +0 -38
- package/plugins/cursor-pbr/templates/research/ARCHITECTURE.md.tmpl +0 -124
- package/plugins/cursor-pbr/templates/research/STACK.md.tmpl +0 -71
- package/plugins/cursor-pbr/templates/research/SUMMARY.md.tmpl +0 -112
- package/plugins/cursor-pbr/templates/research-outputs/phase-research.md.tmpl +0 -81
- package/plugins/cursor-pbr/templates/research-outputs/project-research.md.tmpl +0 -99
- package/plugins/cursor-pbr/templates/research-outputs/synthesis.md.tmpl +0 -36
- package/plugins/pbr/references/agent-interactions.md +0 -134
- package/plugins/pbr/references/pbr-rules.md +0 -194
- package/plugins/pbr/references/pbr-tools-cli.md +0 -285
- package/plugins/pbr/references/planning-config.md +0 -213
- package/plugins/pbr/references/subagent-coordination.md +0 -119
- package/plugins/pbr/references/ui-formatting.md +0 -444
- package/plugins/pbr/scripts/validate-plugin-structure.js +0 -183
- package/plugins/pbr/skills/milestone/templates/audit-report.md.tmpl +0 -48
- package/plugins/pbr/skills/shared/progress-display.md +0 -53
- package/plugins/pbr/skills/shared/state-loading.md +0 -62
- package/plugins/pbr/templates/RESEARCH-SUMMARY.md.tmpl +0 -97
- package/plugins/pbr/templates/research/ARCHITECTURE.md.tmpl +0 -124
- package/plugins/pbr/templates/research/STACK.md.tmpl +0 -71
- package/plugins/pbr/templates/research/SUMMARY.md.tmpl +0 -112
- package/plugins/pbr/templates/research-outputs/phase-research.md.tmpl +0 -81
- package/plugins/pbr/templates/research-outputs/project-research.md.tmpl +0 -99
- package/plugins/pbr/templates/research-outputs/synthesis.md.tmpl +0 -36
- /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
|
-
*
|
|
7
|
-
*
|
|
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:
|
|
@@ -12,1367 +20,2025 @@
|
|
|
12
20
|
* state check-progress — Recalculate progress from filesystem
|
|
13
21
|
* state update <f> <v> — Atomically update a STATE.md field
|
|
14
22
|
* config validate — Validate config.json against schema
|
|
23
|
+
* config get <dot.path> — Read a config value by dot-path key
|
|
24
|
+
* intel query <term> — Search intel files for a term
|
|
25
|
+
* intel status — Report staleness of each intel file
|
|
26
|
+
* intel diff — Show changes since last refresh snapshot
|
|
27
|
+
* requirements mark-complete <ids> — Mark comma-separated REQ-IDs as complete
|
|
15
28
|
* plan-index <phase> — Plan inventory for a phase, grouped by wave
|
|
16
29
|
* frontmatter <filepath> — Parse .md file's YAML frontmatter → JSON
|
|
17
30
|
* must-haves <phase> — Collect all must-haves from phase plans → JSON
|
|
18
31
|
* phase-info <phase> — Comprehensive single-phase status → JSON
|
|
19
32
|
* roadmap update-status <phase> <status> — Update phase status in ROADMAP.md
|
|
20
33
|
* roadmap update-plans <phase> <complete> <total> — Update phase plans in ROADMAP.md
|
|
21
|
-
*
|
|
22
|
-
*
|
|
34
|
+
* roadmap reconcile — Reconcile ROADMAP statuses against disk state
|
|
35
|
+
* roadmap get-phase <N> — Get comprehensive phase info JSON (alias for phase-info)
|
|
36
|
+
* roadmap append-phase [--goal "..."] [--name "..."] [--depends-on N] — Append a new phase to the roadmap
|
|
37
|
+
* history append <type> <title> [body] — Append record to STATE.md ## History (fallback: HISTORY.md)
|
|
38
|
+
* history load — Load history records as JSON (STATE.md first, HISTORY.md fallback)
|
|
39
|
+
* todo list [--theme X] [--status Y] — List todos as JSON (default: pending)
|
|
40
|
+
* todo get <NNN> — Get a specific todo by number
|
|
41
|
+
* todo add <title> [--priority P] [--theme T] — Add a new todo
|
|
42
|
+
* todo done <NNN> — Mark a todo as complete
|
|
43
|
+
* auto-cleanup --phase N | --milestone vN — Auto-close todos and archive notes matching phase/milestone deliverables
|
|
44
|
+
* state reconcile — Detect and repair STATE.md/ROADMAP.md desync
|
|
45
|
+
* state backup — Create timestamped backup of STATE.md and ROADMAP.md
|
|
46
|
+
* llm <subcommand> — DEPRECATED: local_llm removed (no-op)
|
|
47
|
+
* validate-project — Comprehensive .planning/ integrity check
|
|
48
|
+
* phase add <slug> [--after N] [--goal "..."] [--depends-on N] — Add phase with ROADMAP.md integration
|
|
49
|
+
* phase remove <N> — Remove an empty phase directory (with renumbering)
|
|
50
|
+
* phase list [--status S] [--before N] — List phase directories with optional status/before filters
|
|
51
|
+
* phase complete <N> — Mark phase N complete, advance STATE.md to next phase
|
|
52
|
+
* phase insert <N> <slug> [--goal "..."] [--depends-on N] — Insert phase at position N, renumber subsequent
|
|
53
|
+
* phase commits-for <N> — Read .phase-manifest.json for phase N, output commits JSON. Falls back to git log
|
|
54
|
+
* phase first-last-commit <N> — Output { first, last } commit hashes from manifest or git log
|
|
55
|
+
* compound init-phase <N> <slug> [--goal "..."] [--depends-on N] — Atomically create phase dir + STATE + ROADMAP
|
|
56
|
+
* compound complete-phase <N> — Atomically validate SUMMARY + update STATE + ROADMAP
|
|
57
|
+
* compound init-milestone <version> [--name "..."] [--phases "N-M"] — Atomically create milestone archive structure
|
|
58
|
+
* learnings ingest <json-file> — Ingest a learning entry into global store
|
|
59
|
+
* learnings query [--tags X] [--min-confidence Y] [--stack S] [--type T] — Query learnings
|
|
60
|
+
* learnings check-thresholds — Check deferral trigger conditions
|
|
61
|
+
* learnings copy-global <path> <proj> — Copy cross_project LEARNINGS.md to ~/.claude/pbr-knowledge/
|
|
62
|
+
* learnings query-global [--tags X] [--project P] — Query global knowledge files
|
|
63
|
+
* data status — Freshness report for research/, intel/, codebase/ directories
|
|
64
|
+
* data prune --before <ISO-date> [--dry-run] — Archive stale research/codebase files
|
|
65
|
+
* incidents list [--limit N] — List recent incidents as JSON array
|
|
66
|
+
* incidents summary — Aggregate incident stats by type/severity/source
|
|
67
|
+
* incidents query [--type T] [--severity S] [--source S] [--last Nd|Nh] — Filter incidents
|
|
68
|
+
* nk record --title "..." --category "..." --files "f1,f2" --tried "..." --failed "..." — Record negative knowledge entry
|
|
69
|
+
* nk list [--category X] [--phase X] [--status X] — List negative knowledge entries
|
|
70
|
+
* nk resolve <slug> — Mark a negative knowledge entry as resolved
|
|
71
|
+
* insights import <html-path> [--project <name>] — Parse insights HTML report into learnings
|
|
72
|
+
* audit plan-checks [--last N] — List all plan-check results from hooks.jsonl (default: last 30 days)
|
|
73
|
+
* hooks perf [--last N] [--json] — Show P50/P95/P99 hook performance report from hooks-*.jsonl
|
|
74
|
+
* spot-check <phaseSlug> <planId> — Verify SUMMARY, key_files, and commits exist for a plan
|
|
75
|
+
* staleness-check <phase-slug> — Check if phase plans are stale vs dependencies
|
|
76
|
+
* summary-gate <phase-slug> <plan-id> — Verify SUMMARY.md exists, non-empty, valid frontmatter
|
|
77
|
+
* checkpoint init <phase-slug> [--plans "id1,id2"] — Initialize checkpoint manifest
|
|
78
|
+
* checkpoint update <phase-slug> --wave N --resolved id [--sha hash] — Update manifest
|
|
79
|
+
* seeds match <phase-slug> <phase-number> — Find matching seed files for a phase
|
|
80
|
+
* session get <key> — Read a key from .planning/.session.json
|
|
81
|
+
* session set <key> <value> — Write a key to .planning/.session.json
|
|
82
|
+
* session clear [key] — Delete .session.json or set key to null
|
|
83
|
+
* session dump — Print entire .session.json content
|
|
84
|
+
* skill-section <skill> <section> — Extract a section from a skill's SKILL.md → JSON
|
|
85
|
+
* skill-section --list <skill> — List all headings in a skill → JSON
|
|
86
|
+
* step-verify [skill] [step] [checklist-json] — Validate per-step completion checklist → JSON
|
|
87
|
+
* build-preview [phase-slug] — Preview what /pbr:execute-phase would do for a phase → JSON
|
|
88
|
+
* claim acquire <phase-slug> --session-id <id> --skill <name> — Acquire phase claim
|
|
89
|
+
* claim release <phase-slug> --session-id <id> — Release phase claim
|
|
90
|
+
* claim list — List all active phase claims
|
|
91
|
+
* suggest-alternatives phase-not-found [slug] — List available phases for unknown slug → JSON
|
|
92
|
+
* suggest-alternatives missing-prereq [phase] — List missing prerequisites for a phase → JSON
|
|
93
|
+
* suggest-alternatives config-invalid [field] [val] — List valid values for invalid config field → JSON
|
|
94
|
+
* help — List all skills with name, description, tools, argument-hint → JSON
|
|
95
|
+
* skill-metadata <name> — Get metadata for a single skill → JSON
|
|
96
|
+
*
|
|
97
|
+
* Environment: PBR_PROJECT_ROOT — Override project root directory (used when hooks fire from subagent cwd)
|
|
23
98
|
*/
|
|
24
99
|
|
|
25
100
|
const fs = require('fs');
|
|
26
101
|
const path = require('path');
|
|
27
102
|
|
|
28
|
-
|
|
29
|
-
const
|
|
103
|
+
// --- Import lib modules ---
|
|
104
|
+
const {
|
|
105
|
+
KNOWN_AGENTS,
|
|
106
|
+
VALID_STATUS_TRANSITIONS,
|
|
107
|
+
validateStatusTransition,
|
|
108
|
+
output,
|
|
109
|
+
error,
|
|
110
|
+
parseYamlFrontmatter,
|
|
111
|
+
parseMustHaves,
|
|
112
|
+
findFiles,
|
|
113
|
+
tailLines,
|
|
114
|
+
countMustHaves,
|
|
115
|
+
determinePhaseStatus,
|
|
116
|
+
atomicWrite,
|
|
117
|
+
lockedFileUpdate,
|
|
118
|
+
writeActiveSkill,
|
|
119
|
+
sessionLoad,
|
|
120
|
+
sessionSave,
|
|
121
|
+
SESSION_ALLOWED_KEYS,
|
|
122
|
+
STALE_SESSION_MS,
|
|
123
|
+
resolveSessionPath,
|
|
124
|
+
acquireClaim,
|
|
125
|
+
releaseClaim,
|
|
126
|
+
releaseSessionClaims: _releaseSessionClaims,
|
|
127
|
+
listClaims: _listClaims
|
|
128
|
+
} = require('./lib/core');
|
|
129
|
+
|
|
130
|
+
const {
|
|
131
|
+
configLoad: _configLoad,
|
|
132
|
+
configClearCache: _configClearCache,
|
|
133
|
+
configValidate: _configValidate,
|
|
134
|
+
configFormat: _configFormat,
|
|
135
|
+
configWrite: _configWrite,
|
|
136
|
+
resolveDepthProfile,
|
|
137
|
+
DEPTH_PROFILE_DEFAULTS,
|
|
138
|
+
loadUserDefaults,
|
|
139
|
+
saveUserDefaults,
|
|
140
|
+
mergeUserDefaults,
|
|
141
|
+
USER_DEFAULTS_PATH
|
|
142
|
+
} = require('./lib/config');
|
|
143
|
+
|
|
144
|
+
const {
|
|
145
|
+
parseStateMd,
|
|
146
|
+
updateLegacyStateField,
|
|
147
|
+
updateFrontmatterField,
|
|
148
|
+
stateLoad: _stateLoad,
|
|
149
|
+
stateCheckProgress: _stateCheckProgress,
|
|
150
|
+
stateUpdate: _stateUpdate,
|
|
151
|
+
statePatch: _statePatch,
|
|
152
|
+
stateAdvancePlan: _stateAdvancePlan,
|
|
153
|
+
stateRecordMetric: _stateRecordMetric,
|
|
154
|
+
stateRecordActivity: _stateRecordActivity,
|
|
155
|
+
stateUpdateProgress: _stateUpdateProgress
|
|
156
|
+
} = require('./lib/state');
|
|
157
|
+
|
|
158
|
+
const {
|
|
159
|
+
stateReconcile: _stateReconcile,
|
|
160
|
+
stateBackup: _stateBackup
|
|
161
|
+
} = require('./lib/state');
|
|
162
|
+
|
|
163
|
+
const {
|
|
164
|
+
parseRoadmapMd,
|
|
165
|
+
findRoadmapRow,
|
|
166
|
+
updateTableRow,
|
|
167
|
+
roadmapUpdateStatus: _roadmapUpdateStatus,
|
|
168
|
+
roadmapUpdatePlans: _roadmapUpdatePlans,
|
|
169
|
+
roadmapAnalyze: _roadmapAnalyze,
|
|
170
|
+
roadmapAppendPhase: _roadmapAppendPhase,
|
|
171
|
+
roadmapRemovePhase: _roadmapRemovePhase,
|
|
172
|
+
roadmapRenumberPhases: _roadmapRenumberPhases,
|
|
173
|
+
roadmapInsertPhase: _roadmapInsertPhase,
|
|
174
|
+
reconcileRoadmapStatuses: _reconcileRoadmapStatuses
|
|
175
|
+
} = require('./lib/roadmap');
|
|
176
|
+
|
|
177
|
+
const {
|
|
178
|
+
frontmatter: _frontmatter,
|
|
179
|
+
planIndex: _planIndex,
|
|
180
|
+
mustHavesCollect: _mustHavesCollect,
|
|
181
|
+
phaseInfo: _phaseInfo,
|
|
182
|
+
phaseAdd: _phaseAdd,
|
|
183
|
+
phaseRemove: _phaseRemove,
|
|
184
|
+
phaseList: _phaseList,
|
|
185
|
+
milestoneStats: _milestoneStats,
|
|
186
|
+
phaseComplete: _phaseComplete,
|
|
187
|
+
phaseInsert: _phaseInsert,
|
|
188
|
+
phaseNextNumber: _phaseNextNumber
|
|
189
|
+
} = require('./lib/phase');
|
|
190
|
+
|
|
191
|
+
const {
|
|
192
|
+
compoundInitPhase: _compoundInitPhase,
|
|
193
|
+
compoundCompletePhase: _compoundCompletePhase,
|
|
194
|
+
compoundInitMilestone: _compoundInitMilestone
|
|
195
|
+
} = require('./lib/compound');
|
|
196
|
+
|
|
197
|
+
const {
|
|
198
|
+
initExecutePhase: _initExecutePhase,
|
|
199
|
+
initPlanPhase: _initPlanPhase,
|
|
200
|
+
initQuick: _initQuick,
|
|
201
|
+
initVerifyWork: _initVerifyWork,
|
|
202
|
+
initResume: _initResume,
|
|
203
|
+
initProgress: _initProgress,
|
|
204
|
+
initStateBundle: _initStateBundle,
|
|
205
|
+
initContinue: _initContinue,
|
|
206
|
+
initMilestone: _initMilestone,
|
|
207
|
+
initBegin: _initBegin,
|
|
208
|
+
initStatus: _initStatus,
|
|
209
|
+
initMapCodebase: _initMapCodebase
|
|
210
|
+
} = require('./lib/init');
|
|
211
|
+
|
|
212
|
+
const {
|
|
213
|
+
historyAppend: _historyAppend,
|
|
214
|
+
historyLoad: _historyLoad
|
|
215
|
+
} = require('./lib/history');
|
|
216
|
+
|
|
217
|
+
const {
|
|
218
|
+
todoList: _todoList,
|
|
219
|
+
todoGet: _todoGet,
|
|
220
|
+
todoAdd: _todoAdd,
|
|
221
|
+
todoDone: _todoDone
|
|
222
|
+
} = require('./lib/todo');
|
|
223
|
+
|
|
224
|
+
const {
|
|
225
|
+
autoCloseTodos: _autoCloseTodos,
|
|
226
|
+
autoArchiveNotes: _autoArchiveNotes
|
|
227
|
+
} = require('./lib/auto-cleanup');
|
|
228
|
+
|
|
229
|
+
const {
|
|
230
|
+
applyMigrations: _applyMigrations
|
|
231
|
+
} = require('./lib/migrate');
|
|
232
|
+
|
|
233
|
+
const {
|
|
234
|
+
spotCheck: _spotCheck,
|
|
235
|
+
verifySpotCheck: _verifySpotCheck
|
|
236
|
+
} = require('./lib/spot-check');
|
|
237
|
+
|
|
238
|
+
const {
|
|
239
|
+
cmdVerifySummary: _cmdVerifySummary,
|
|
240
|
+
cmdVerifyPlanStructure: _cmdVerifyPlanStructure,
|
|
241
|
+
cmdVerifyPhaseCompleteness: _cmdVerifyPhaseCompleteness,
|
|
242
|
+
cmdVerifyArtifacts: _cmdVerifyArtifacts,
|
|
243
|
+
cmdVerifyKeyLinks: _cmdVerifyKeyLinks,
|
|
244
|
+
cmdVerifyCommits: _cmdVerifyCommits,
|
|
245
|
+
cmdVerifyReferences: _cmdVerifyReferences,
|
|
246
|
+
} = require('./lib/verify');
|
|
247
|
+
|
|
248
|
+
const {
|
|
249
|
+
learningsIngest: _learningsIngest,
|
|
250
|
+
learningsQuery: _learningsQuery,
|
|
251
|
+
checkDeferralThresholds: _checkDeferralThresholds,
|
|
252
|
+
copyToGlobal: _copyToGlobal,
|
|
253
|
+
queryGlobal: _queryGlobal
|
|
254
|
+
} = require('./lib/learnings');
|
|
255
|
+
|
|
256
|
+
const {
|
|
257
|
+
insightsImport: _insightsImport
|
|
258
|
+
} = require('./lib/insights-parser');
|
|
259
|
+
|
|
260
|
+
const {
|
|
261
|
+
referenceGet: _referenceGet
|
|
262
|
+
} = require('./lib/reference');
|
|
263
|
+
|
|
264
|
+
const {
|
|
265
|
+
skillSection: _skillSection,
|
|
266
|
+
listAvailableSkills: _listAvailableSkills
|
|
267
|
+
} = require('./lib/skill-section');
|
|
268
|
+
|
|
269
|
+
const {
|
|
270
|
+
helpList: _helpList,
|
|
271
|
+
skillMetadata: _skillMetadata
|
|
272
|
+
} = require('./lib/help');
|
|
273
|
+
|
|
274
|
+
const {
|
|
275
|
+
stepVerify: _stepVerify
|
|
276
|
+
} = require('./lib/step-verify');
|
|
277
|
+
|
|
278
|
+
const {
|
|
279
|
+
buildPreview: _buildPreview
|
|
280
|
+
} = require('./lib/preview');
|
|
281
|
+
|
|
282
|
+
const {
|
|
283
|
+
contextTriage: _contextTriage
|
|
284
|
+
} = require('./lib/context');
|
|
285
|
+
|
|
286
|
+
const {
|
|
287
|
+
phaseAlternatives: _phaseAlternatives,
|
|
288
|
+
prerequisiteAlternatives: _prereqAlternatives,
|
|
289
|
+
configAlternatives: _configAlternatives
|
|
290
|
+
} = require('./lib/alternatives');
|
|
291
|
+
|
|
292
|
+
const {
|
|
293
|
+
stalenessCheck: _stalenessCheck,
|
|
294
|
+
summaryGate: _summaryGate,
|
|
295
|
+
checkpointInit: _checkpointInit,
|
|
296
|
+
checkpointUpdate: _checkpointUpdate,
|
|
297
|
+
seedsMatch: _seedsMatch,
|
|
298
|
+
ciPoll: _ciPoll,
|
|
299
|
+
rollback: _rollback
|
|
300
|
+
} = require('./lib/build');
|
|
301
|
+
|
|
302
|
+
const {
|
|
303
|
+
parseJestOutput: _parseJestOutput,
|
|
304
|
+
parseLintOutput: _parseLintOutput,
|
|
305
|
+
autoFixLint: _autoFixLint,
|
|
306
|
+
runCiFixLoop: _runCiFixLoop
|
|
307
|
+
} = require('./lib/ci-fix-loop');
|
|
308
|
+
|
|
309
|
+
const {
|
|
310
|
+
statusRender: _statusRender
|
|
311
|
+
} = require('./lib/status-render');
|
|
312
|
+
|
|
313
|
+
const {
|
|
314
|
+
suggestNext: _suggestNext
|
|
315
|
+
} = require('./lib/suggest-next');
|
|
316
|
+
|
|
317
|
+
const {
|
|
318
|
+
quickStatus: _quickStatus
|
|
319
|
+
} = require('./quick-status');
|
|
320
|
+
|
|
321
|
+
// --- Local LLM imports removed (feature archived in phase 53) ---
|
|
322
|
+
|
|
323
|
+
const {
|
|
324
|
+
dataStatus: _dataStatus,
|
|
325
|
+
dataPrune: _dataPrune
|
|
326
|
+
} = require('./lib/data-hygiene');
|
|
327
|
+
|
|
328
|
+
const {
|
|
329
|
+
list: _incidentsList,
|
|
330
|
+
query: _incidentsQuery,
|
|
331
|
+
summary: _incidentsSummary
|
|
332
|
+
} = require('./lib/incidents');
|
|
333
|
+
|
|
334
|
+
// --- Module-level state (for backwards compatibility) ---
|
|
335
|
+
|
|
336
|
+
let cwd = process.env.PBR_PROJECT_ROOT || process.cwd();
|
|
337
|
+
// MSYS path bridging: Git Bash on Windows can produce /d/Repos/... paths
|
|
338
|
+
// that Node.js cannot resolve. Convert to D:\Repos\... form.
|
|
339
|
+
const _msysCwdMatch = cwd.match(/^\/([a-zA-Z])\/(.*)/);
|
|
340
|
+
if (_msysCwdMatch) cwd = _msysCwdMatch[1] + ':' + path.sep + _msysCwdMatch[2].replace(/\//g, path.sep);
|
|
341
|
+
let planningDir = path.join(cwd, '.planning');
|
|
342
|
+
|
|
343
|
+
// --- Wrapper functions that pass planningDir to lib modules ---
|
|
344
|
+
// These preserve the original function signatures (no planningDir param)
|
|
345
|
+
// so existing callers (hook scripts, tests) continue to work.
|
|
30
346
|
|
|
31
|
-
|
|
347
|
+
function configLoad(dir) {
|
|
348
|
+
return _configLoad(dir || planningDir);
|
|
349
|
+
}
|
|
32
350
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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
|
-
};
|
|
351
|
+
function configClearCache() {
|
|
352
|
+
_configClearCache();
|
|
353
|
+
cwd = process.env.PBR_PROJECT_ROOT || process.cwd();
|
|
354
|
+
const _msysResetMatch = cwd.match(/^\/([a-zA-Z])\/(.*)/);
|
|
355
|
+
if (_msysResetMatch) cwd = _msysResetMatch[1] + ':' + path.sep + _msysResetMatch[2].replace(/\//g, path.sep);
|
|
356
|
+
planningDir = path.join(cwd, '.planning');
|
|
357
|
+
}
|
|
59
358
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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();
|
|
359
|
+
function configValidate(preloadedConfig) {
|
|
360
|
+
return _configValidate(preloadedConfig, planningDir);
|
|
361
|
+
}
|
|
71
362
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
}
|
|
363
|
+
function insightsImport(htmlPath, projectName) {
|
|
364
|
+
return _insightsImport(htmlPath, projectName, planningDir);
|
|
365
|
+
}
|
|
76
366
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
}
|
|
367
|
+
function stateLoad() {
|
|
368
|
+
return _stateLoad(planningDir);
|
|
369
|
+
}
|
|
81
370
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
}
|
|
371
|
+
function stateCheckProgress() {
|
|
372
|
+
return _stateCheckProgress(planningDir);
|
|
373
|
+
}
|
|
86
374
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
warning: `Suspicious status transition: "${from}" -> "${to}". Expected one of: [${allowed.join(', ')}]. Proceeding anyway (advisory).`
|
|
90
|
-
};
|
|
375
|
+
function stateUpdate(field, value) {
|
|
376
|
+
return _stateUpdate(field, value, planningDir);
|
|
91
377
|
}
|
|
92
378
|
|
|
93
|
-
|
|
379
|
+
function statePatch(jsonStr) {
|
|
380
|
+
return _statePatch(jsonStr, planningDir);
|
|
381
|
+
}
|
|
94
382
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
383
|
+
function stateAdvancePlan() {
|
|
384
|
+
return _stateAdvancePlan(planningDir);
|
|
385
|
+
}
|
|
98
386
|
|
|
99
|
-
|
|
100
|
-
|
|
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;
|
|
122
|
-
}
|
|
387
|
+
function stateRecordMetric(metricArgs) {
|
|
388
|
+
return _stateRecordMetric(metricArgs, planningDir);
|
|
123
389
|
}
|
|
124
390
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
* Useful in tests where multiple temp directories are used in rapid succession.
|
|
128
|
-
*/
|
|
129
|
-
function configClearCache() {
|
|
130
|
-
_configCache = null;
|
|
131
|
-
_configMtime = 0;
|
|
132
|
-
_configPath = null;
|
|
391
|
+
function stateRecordActivity(description) {
|
|
392
|
+
return _stateRecordActivity(description, planningDir);
|
|
133
393
|
}
|
|
134
394
|
|
|
135
|
-
|
|
136
|
-
|
|
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)
|
|
144
|
-
*/
|
|
145
|
-
function tailLines(filePath, n) {
|
|
146
|
-
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);
|
|
153
|
-
} catch (_e) {
|
|
154
|
-
return [];
|
|
155
|
-
}
|
|
395
|
+
function stateUpdateProgress() {
|
|
396
|
+
return _stateUpdateProgress(planningDir);
|
|
156
397
|
}
|
|
157
398
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
*/
|
|
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
|
-
};
|
|
399
|
+
function stateReconcile() {
|
|
400
|
+
return _stateReconcile(planningDir);
|
|
401
|
+
}
|
|
191
402
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
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.
|
|
198
|
-
*/
|
|
199
|
-
function resolveDepthProfile(config) {
|
|
200
|
-
const depth = (config && config.depth) || 'standard';
|
|
201
|
-
const defaults = DEPTH_PROFILE_DEFAULTS[depth] || DEPTH_PROFILE_DEFAULTS.standard;
|
|
403
|
+
function stateBackup() {
|
|
404
|
+
return _stateBackup(planningDir);
|
|
405
|
+
}
|
|
202
406
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
407
|
+
function roadmapAnalyze() {
|
|
408
|
+
return _roadmapAnalyze(planningDir);
|
|
409
|
+
}
|
|
206
410
|
|
|
207
|
-
|
|
411
|
+
function roadmapUpdateStatus(phaseNum, newStatus) {
|
|
412
|
+
return _roadmapUpdateStatus(phaseNum, newStatus, planningDir);
|
|
208
413
|
}
|
|
209
414
|
|
|
210
|
-
function
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
const subcommand = args[1];
|
|
415
|
+
function roadmapUpdatePlans(phaseNum, complete, total) {
|
|
416
|
+
return _roadmapUpdatePlans(phaseNum, complete, total, planningDir);
|
|
417
|
+
}
|
|
214
418
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
output(stateLoad());
|
|
218
|
-
} else if (command === 'state' && subcommand === 'check-progress') {
|
|
219
|
-
output(stateCheckProgress());
|
|
220
|
-
} else if (command === 'state' && subcommand === 'update') {
|
|
221
|
-
const field = args[2];
|
|
222
|
-
const value = args[3];
|
|
223
|
-
if (!field || value === undefined) {
|
|
224
|
-
error('Usage: pbr-tools.js state update <field> <value>\nFields: current_phase, status, plans_complete, last_activity');
|
|
225
|
-
}
|
|
226
|
-
output(stateUpdate(field, value));
|
|
227
|
-
} else if (command === 'config' && subcommand === 'validate') {
|
|
228
|
-
output(configValidate());
|
|
229
|
-
} else if (command === 'config' && subcommand === 'resolve-depth') {
|
|
230
|
-
const dir = args[2] || undefined;
|
|
231
|
-
const config = configLoad(dir);
|
|
232
|
-
output(resolveDepthProfile(config));
|
|
233
|
-
} else if (command === 'plan-index') {
|
|
234
|
-
const phase = args[1];
|
|
235
|
-
if (!phase) {
|
|
236
|
-
error('Usage: pbr-tools.js plan-index <phase-number>');
|
|
237
|
-
}
|
|
238
|
-
output(planIndex(phase));
|
|
239
|
-
} else if (command === 'frontmatter') {
|
|
240
|
-
const filePath = args[1];
|
|
241
|
-
if (!filePath) {
|
|
242
|
-
error('Usage: pbr-tools.js frontmatter <filepath>');
|
|
243
|
-
}
|
|
244
|
-
output(frontmatter(filePath));
|
|
245
|
-
} else if (command === 'must-haves') {
|
|
246
|
-
const phase = args[1];
|
|
247
|
-
if (!phase) {
|
|
248
|
-
error('Usage: pbr-tools.js must-haves <phase-number>');
|
|
249
|
-
}
|
|
250
|
-
output(mustHavesCollect(phase));
|
|
251
|
-
} else if (command === 'phase-info') {
|
|
252
|
-
const phase = args[1];
|
|
253
|
-
if (!phase) {
|
|
254
|
-
error('Usage: pbr-tools.js phase-info <phase-number>');
|
|
255
|
-
}
|
|
256
|
-
output(phaseInfo(phase));
|
|
257
|
-
} else if (command === 'roadmap' && subcommand === 'update-status') {
|
|
258
|
-
const phase = args[2];
|
|
259
|
-
const status = args[3];
|
|
260
|
-
if (!phase || !status) {
|
|
261
|
-
error('Usage: pbr-tools.js roadmap update-status <phase-number> <status>');
|
|
262
|
-
}
|
|
263
|
-
output(roadmapUpdateStatus(phase, status));
|
|
264
|
-
} else if (command === 'roadmap' && subcommand === 'update-plans') {
|
|
265
|
-
const phase = args[2];
|
|
266
|
-
const complete = args[3];
|
|
267
|
-
const total = args[4];
|
|
268
|
-
if (!phase || complete === undefined || total === undefined) {
|
|
269
|
-
error('Usage: pbr-tools.js roadmap update-plans <phase-number> <complete> <total>');
|
|
270
|
-
}
|
|
271
|
-
output(roadmapUpdatePlans(phase, complete, total));
|
|
272
|
-
} else if (command === 'history' && subcommand === 'append') {
|
|
273
|
-
const type = args[2]; // 'milestone' or 'phase'
|
|
274
|
-
const title = args[3];
|
|
275
|
-
const body = args[4] || '';
|
|
276
|
-
if (!type || !title) {
|
|
277
|
-
error('Usage: pbr-tools.js history append <milestone|phase> <title> [body]');
|
|
278
|
-
}
|
|
279
|
-
output(historyAppend({ type, title, body }));
|
|
280
|
-
} else if (command === 'history' && subcommand === 'load') {
|
|
281
|
-
output(historyLoad());
|
|
282
|
-
} else if (command === 'event') {
|
|
283
|
-
const category = args[1];
|
|
284
|
-
const event = args[2];
|
|
285
|
-
let details = {};
|
|
286
|
-
if (args[3]) {
|
|
287
|
-
try { details = JSON.parse(args[3]); } catch (_e) { details = { raw: args[3] }; }
|
|
288
|
-
}
|
|
289
|
-
if (!category || !event) {
|
|
290
|
-
error('Usage: pbr-tools.js event <category> <event> [JSON-details]');
|
|
291
|
-
}
|
|
292
|
-
const { logEvent } = require('./event-logger');
|
|
293
|
-
logEvent(category, event, details);
|
|
294
|
-
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
|
-
}
|
|
419
|
+
function roadmapReconcile() {
|
|
420
|
+
return _reconcileRoadmapStatuses(planningDir);
|
|
301
421
|
}
|
|
302
422
|
|
|
303
|
-
|
|
423
|
+
function frontmatter(filePath) {
|
|
424
|
+
return _frontmatter(filePath);
|
|
425
|
+
}
|
|
304
426
|
|
|
305
|
-
function
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
config: null,
|
|
309
|
-
state: null,
|
|
310
|
-
roadmap: null,
|
|
311
|
-
phase_count: 0,
|
|
312
|
-
current_phase: null,
|
|
313
|
-
progress: null
|
|
314
|
-
};
|
|
427
|
+
function planIndex(phaseNum) {
|
|
428
|
+
return _planIndex(phaseNum, planningDir);
|
|
429
|
+
}
|
|
315
430
|
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
result.exists = true;
|
|
431
|
+
function mustHavesCollect(phaseNum) {
|
|
432
|
+
return _mustHavesCollect(phaseNum, planningDir);
|
|
433
|
+
}
|
|
320
434
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
try {
|
|
325
|
-
result.config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
326
|
-
} catch (_) {
|
|
327
|
-
result.config = { _error: 'Failed to parse config.json' };
|
|
328
|
-
}
|
|
329
|
-
}
|
|
435
|
+
function phaseInfo(phaseNum) {
|
|
436
|
+
return _phaseInfo(phaseNum, planningDir);
|
|
437
|
+
}
|
|
330
438
|
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
const content = fs.readFileSync(statePath, 'utf8');
|
|
335
|
-
result.state = parseStateMd(content);
|
|
336
|
-
}
|
|
439
|
+
function phaseAdd(slug, afterPhase, options) {
|
|
440
|
+
return _phaseAdd(slug, afterPhase, planningDir, options);
|
|
441
|
+
}
|
|
337
442
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
const content = fs.readFileSync(roadmapPath, 'utf8');
|
|
342
|
-
result.roadmap = parseRoadmapMd(content);
|
|
343
|
-
result.phase_count = result.roadmap.phases.length;
|
|
344
|
-
}
|
|
443
|
+
function phaseRemove(phaseNum) {
|
|
444
|
+
return _phaseRemove(phaseNum, planningDir);
|
|
445
|
+
}
|
|
345
446
|
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
}
|
|
447
|
+
function phaseList(opts) {
|
|
448
|
+
return _phaseList(planningDir, opts);
|
|
449
|
+
}
|
|
350
450
|
|
|
351
|
-
|
|
352
|
-
|
|
451
|
+
function phaseNextNumber() {
|
|
452
|
+
return _phaseNextNumber(planningDir);
|
|
453
|
+
}
|
|
353
454
|
|
|
354
|
-
|
|
455
|
+
function phaseComplete(phaseNum) {
|
|
456
|
+
return _phaseComplete(phaseNum, planningDir);
|
|
355
457
|
}
|
|
356
458
|
|
|
357
|
-
function
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
return { phases: [], total_plans: 0, completed_plans: 0, percentage: 0 };
|
|
361
|
-
}
|
|
459
|
+
function phaseInsert(position, slug, options) {
|
|
460
|
+
return _phaseInsert(position, slug, planningDir, options);
|
|
461
|
+
}
|
|
362
462
|
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
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
|
-
}
|
|
463
|
+
function milestoneStats(version) {
|
|
464
|
+
return _milestoneStats(version, planningDir);
|
|
465
|
+
}
|
|
395
466
|
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
total_plans: totalPlans,
|
|
399
|
-
completed_plans: completedPlans,
|
|
400
|
-
percentage: totalPlans > 0 ? Math.round((completedPlans / totalPlans) * 100) : 0
|
|
401
|
-
};
|
|
467
|
+
function compoundInitPhase(phaseNum, slug, opts) {
|
|
468
|
+
return _compoundInitPhase(phaseNum, slug, planningDir, opts);
|
|
402
469
|
}
|
|
403
470
|
|
|
404
|
-
function
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
return { error: 'No phases directory found' };
|
|
408
|
-
}
|
|
471
|
+
function compoundCompletePhase(phaseNum) {
|
|
472
|
+
return _compoundCompletePhase(phaseNum, planningDir);
|
|
473
|
+
}
|
|
409
474
|
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
475
|
+
function compoundInitMilestone(version, opts) {
|
|
476
|
+
return _compoundInitMilestone(version, planningDir, opts);
|
|
477
|
+
}
|
|
413
478
|
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
}
|
|
479
|
+
function initExecutePhase(phaseNum, overridePlanningDir, overrideModel) {
|
|
480
|
+
return _initExecutePhase(phaseNum, overridePlanningDir || planningDir, overrideModel);
|
|
481
|
+
}
|
|
418
482
|
|
|
419
|
-
|
|
420
|
-
|
|
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
|
-
}
|
|
483
|
+
function initPlanPhase(phaseNum, overridePlanningDir, overrideModel) {
|
|
484
|
+
return _initPlanPhase(phaseNum, overridePlanningDir || planningDir, overrideModel);
|
|
485
|
+
}
|
|
447
486
|
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
total_plans: plans.length,
|
|
451
|
-
plans,
|
|
452
|
-
waves
|
|
453
|
-
};
|
|
487
|
+
function initQuick(description) {
|
|
488
|
+
return _initQuick(description, planningDir);
|
|
454
489
|
}
|
|
455
490
|
|
|
456
|
-
function
|
|
457
|
-
|
|
458
|
-
|
|
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
|
-
}
|
|
491
|
+
function initVerifyWork(phaseNum, overridePlanningDir, overrideModel) {
|
|
492
|
+
return _initVerifyWork(phaseNum, overridePlanningDir || planningDir, overrideModel);
|
|
493
|
+
}
|
|
465
494
|
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
return { valid: false, errors: [`config.json is not valid JSON: ${e.message}`], warnings: [] };
|
|
470
|
-
}
|
|
471
|
-
}
|
|
495
|
+
function initResume() {
|
|
496
|
+
return _initResume(planningDir);
|
|
497
|
+
}
|
|
472
498
|
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
499
|
+
function initProgress() {
|
|
500
|
+
return _initProgress(planningDir);
|
|
501
|
+
}
|
|
476
502
|
|
|
477
|
-
|
|
503
|
+
function initContinue() {
|
|
504
|
+
return _initContinue(planningDir);
|
|
505
|
+
}
|
|
478
506
|
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
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
|
-
}
|
|
507
|
+
function initMilestone() {
|
|
508
|
+
return _initMilestone(planningDir);
|
|
509
|
+
}
|
|
498
510
|
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
errors,
|
|
502
|
-
warnings
|
|
503
|
-
};
|
|
511
|
+
function initBegin() {
|
|
512
|
+
return _initBegin(planningDir);
|
|
504
513
|
}
|
|
505
514
|
|
|
506
|
-
|
|
515
|
+
function initStatus() {
|
|
516
|
+
return _initStatus(planningDir);
|
|
517
|
+
}
|
|
507
518
|
|
|
508
|
-
|
|
509
|
-
|
|
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
|
+
function initMapCodebase() {
|
|
520
|
+
return _initMapCodebase(planningDir);
|
|
519
521
|
}
|
|
520
522
|
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
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
|
-
}
|
|
523
|
+
function stateBundle(phaseNum) {
|
|
524
|
+
return _initStateBundle(phaseNum, planningDir);
|
|
525
|
+
}
|
|
530
526
|
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
if (!phaseDir) {
|
|
535
|
-
return { error: `No phase directory found matching phase ${phaseNum}` };
|
|
536
|
-
}
|
|
527
|
+
function historyAppend(entry, dir) {
|
|
528
|
+
return _historyAppend(entry, dir || planningDir);
|
|
529
|
+
}
|
|
537
530
|
|
|
538
|
-
|
|
539
|
-
|
|
531
|
+
function historyLoad(dir) {
|
|
532
|
+
return _historyLoad(dir || planningDir);
|
|
533
|
+
}
|
|
540
534
|
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
const allKeyLinks = new Set();
|
|
535
|
+
function todoList(opts) {
|
|
536
|
+
return _todoList(planningDir, opts);
|
|
537
|
+
}
|
|
545
538
|
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
const planId = fm.plan || file.replace(/-PLAN\.md$/, '');
|
|
550
|
-
const mh = fm.must_haves || { truths: [], artifacts: [], key_links: [] };
|
|
539
|
+
function todoGet(num) {
|
|
540
|
+
return _todoGet(planningDir, num);
|
|
541
|
+
}
|
|
551
542
|
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
(mh.key_links || []).forEach(k => allKeyLinks.add(k));
|
|
556
|
-
}
|
|
543
|
+
function todoAdd(title, opts) {
|
|
544
|
+
return _todoAdd(planningDir, title, opts);
|
|
545
|
+
}
|
|
557
546
|
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
key_links: [...allKeyLinks]
|
|
562
|
-
};
|
|
547
|
+
function todoDone(num) {
|
|
548
|
+
return _todoDone(planningDir, num);
|
|
549
|
+
}
|
|
563
550
|
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
plans: perPlan,
|
|
567
|
-
all,
|
|
568
|
-
total: all.truths.length + all.artifacts.length + all.key_links.length
|
|
569
|
-
};
|
|
551
|
+
function autoCloseTodos(context) {
|
|
552
|
+
return _autoCloseTodos(planningDir, context);
|
|
570
553
|
}
|
|
571
554
|
|
|
572
|
-
|
|
573
|
-
|
|
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
|
-
}
|
|
555
|
+
function autoArchiveNotes(context) {
|
|
556
|
+
return _autoArchiveNotes(planningDir, context);
|
|
557
|
+
}
|
|
580
558
|
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
559
|
+
function incidentsList(opts) {
|
|
560
|
+
return _incidentsList({ ...opts, planningDir });
|
|
561
|
+
}
|
|
562
|
+
function incidentsQuery(filter, opts) {
|
|
563
|
+
return _incidentsQuery(filter, { ...opts, planningDir });
|
|
564
|
+
}
|
|
565
|
+
function incidentsSummary(opts) {
|
|
566
|
+
return _incidentsSummary({ ...opts, planningDir });
|
|
567
|
+
}
|
|
587
568
|
|
|
588
|
-
|
|
569
|
+
function intelQuery(term) {
|
|
570
|
+
const { intelQuery: _intelQuery } = require('./lib/intel');
|
|
571
|
+
return _intelQuery(term, planningDir);
|
|
572
|
+
}
|
|
589
573
|
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
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
|
-
}
|
|
574
|
+
function intelStatus() {
|
|
575
|
+
const { intelStatus: _intelStatus } = require('./lib/intel');
|
|
576
|
+
return _intelStatus(planningDir);
|
|
577
|
+
}
|
|
598
578
|
|
|
599
|
-
|
|
600
|
-
const
|
|
579
|
+
function intelDiff() {
|
|
580
|
+
const { intelDiff: _intelDiff } = require('./lib/intel');
|
|
581
|
+
return _intelDiff(planningDir);
|
|
582
|
+
}
|
|
601
583
|
|
|
602
|
-
|
|
603
|
-
const
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
const vContent = fs.readFileSync(verificationPath, 'utf8');
|
|
607
|
-
verification = parseYamlFrontmatter(vContent);
|
|
608
|
-
}
|
|
584
|
+
function requirementsMarkComplete(ids) {
|
|
585
|
+
const { updateRequirementStatus } = require('./lib/requirements');
|
|
586
|
+
return updateRequirementStatus(planningDir, ids, 'done');
|
|
587
|
+
}
|
|
609
588
|
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
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
|
-
});
|
|
589
|
+
function roadmapAppendPhase(phaseNum, name, goal, dependsOn) {
|
|
590
|
+
return _roadmapAppendPhase(planningDir, phaseNum, name, goal, dependsOn);
|
|
591
|
+
}
|
|
617
592
|
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
const hasVerification = fs.existsSync(verificationPath);
|
|
622
|
-
const fsStatus = determinePhaseStatus(planCount, completedCount, summaryFiles.length, hasVerification, fullDir);
|
|
593
|
+
function dataStatus() {
|
|
594
|
+
return _dataStatus(planningDir);
|
|
595
|
+
}
|
|
623
596
|
|
|
624
|
-
|
|
625
|
-
|
|
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
|
-
};
|
|
597
|
+
function dataPrune(options) {
|
|
598
|
+
return _dataPrune(planningDir, options);
|
|
637
599
|
}
|
|
638
600
|
|
|
639
|
-
|
|
601
|
+
function migrate(options) {
|
|
602
|
+
return _applyMigrations(planningDir, options);
|
|
603
|
+
}
|
|
640
604
|
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
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
|
-
});
|
|
605
|
+
function spotCheck(phaseDir, planId) {
|
|
606
|
+
return _spotCheck(planningDir, phaseDir, planId);
|
|
607
|
+
}
|
|
671
608
|
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
}
|
|
675
|
-
return { success: false, error: result.error };
|
|
609
|
+
function verifySpotCheck(type, dirPath) {
|
|
610
|
+
return _verifySpotCheck(type, dirPath);
|
|
676
611
|
}
|
|
677
612
|
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
const historyPath = path.join(dir || planningDir, 'HISTORY.md');
|
|
688
|
-
const timestamp = new Date().toISOString().slice(0, 10);
|
|
613
|
+
function referenceGet(name, options) {
|
|
614
|
+
// Resolve plugin root — try CLAUDE_PLUGIN_ROOT env, then walk up from __dirname
|
|
615
|
+
const pluginRoot = process.env.CLAUDE_PLUGIN_ROOT || path.resolve(__dirname, '..');
|
|
616
|
+
// Fix MSYS paths on Windows (same pattern as run-hook.js)
|
|
617
|
+
let root = pluginRoot;
|
|
618
|
+
const msysMatch = root.match(/^\/([a-zA-Z])\/(.*)/);
|
|
619
|
+
if (msysMatch) root = msysMatch[1] + ':' + path.sep + msysMatch[2];
|
|
620
|
+
return _referenceGet(name, options, root);
|
|
621
|
+
}
|
|
689
622
|
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
623
|
+
function resolvePluginRoot() {
|
|
624
|
+
// Resolve plugin root — try CLAUDE_PLUGIN_ROOT env, then walk up from __dirname
|
|
625
|
+
const pluginRoot = process.env.CLAUDE_PLUGIN_ROOT || path.resolve(__dirname, '..');
|
|
626
|
+
// Fix MSYS paths on Windows (same pattern as run-hook.js)
|
|
627
|
+
let root = pluginRoot;
|
|
628
|
+
const msysMatch = root.match(/^\/([a-zA-Z])\/(.*)/);
|
|
629
|
+
if (msysMatch) root = msysMatch[1] + ':' + path.sep + msysMatch[2];
|
|
630
|
+
return root;
|
|
631
|
+
}
|
|
694
632
|
|
|
695
|
-
|
|
633
|
+
function skillSectionGet(skillName, sectionQuery) {
|
|
634
|
+
return _skillSection(skillName, sectionQuery, resolvePluginRoot());
|
|
635
|
+
}
|
|
696
636
|
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
637
|
+
function listSkillHeadings(skillName) {
|
|
638
|
+
const { listHeadings } = require('./lib/reference');
|
|
639
|
+
const root = resolvePluginRoot();
|
|
640
|
+
const skillPath = require('path').join(root, 'skills', skillName, 'SKILL.md');
|
|
641
|
+
if (!require('fs').existsSync(skillPath)) {
|
|
642
|
+
return { error: `Skill not found: ${skillName}`, available: _listAvailableSkills(root) };
|
|
702
643
|
}
|
|
644
|
+
const content = require('fs').readFileSync(skillPath, 'utf8');
|
|
645
|
+
return { skill: skillName, headings: listHeadings(content) };
|
|
703
646
|
}
|
|
704
647
|
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
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
|
-
}
|
|
648
|
+
function contextTriage(options) {
|
|
649
|
+
return _contextTriage(options, planningDir);
|
|
650
|
+
}
|
|
729
651
|
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
652
|
+
function stalenessCheck(phaseSlug) { return _stalenessCheck(phaseSlug, planningDir); }
|
|
653
|
+
function summaryGate(phaseSlug, planId) { return _summaryGate(phaseSlug, planId, planningDir); }
|
|
654
|
+
function checkpointInit(phaseSlug, plans) { return _checkpointInit(phaseSlug, plans, planningDir); }
|
|
655
|
+
function checkpointUpdate(phaseSlug, opts) { return _checkpointUpdate(phaseSlug, opts, planningDir); }
|
|
656
|
+
function seedsMatch(phaseSlug, phaseNum) { return _seedsMatch(phaseSlug, phaseNum, planningDir); }
|
|
657
|
+
function ciPoll(runId, timeoutSecs) { return _ciPoll(runId, timeoutSecs, planningDir); }
|
|
658
|
+
function ciFix(options) { return _runCiFixLoop({ ...options, cwd: path.resolve('.') }); }
|
|
659
|
+
function rollbackPlan(manifestPath) { return _rollback(manifestPath, planningDir); }
|
|
660
|
+
|
|
661
|
+
function helpListCmd() {
|
|
662
|
+
const root = resolvePluginRoot();
|
|
663
|
+
return _helpList(root);
|
|
664
|
+
}
|
|
665
|
+
function skillMetadataCmd(skillName) {
|
|
666
|
+
const root = resolvePluginRoot();
|
|
667
|
+
return _skillMetadata(skillName, root);
|
|
734
668
|
}
|
|
735
669
|
|
|
670
|
+
function quickStatus() { return _quickStatus(planningDir); }
|
|
671
|
+
|
|
736
672
|
/**
|
|
737
|
-
*
|
|
673
|
+
* Build cleanup context from phase SUMMARY files and git log.
|
|
674
|
+
* @param {string} phaseNum - Phase number (e.g. "38")
|
|
675
|
+
* @returns {{ phaseName: string, phaseNum: string, keyFiles: string[], commitMessages: string[], summaryDescriptions: string[] }}
|
|
738
676
|
*/
|
|
739
|
-
function
|
|
740
|
-
const
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
}
|
|
677
|
+
function buildCleanupContext(phaseNum) {
|
|
678
|
+
const padded = String(phaseNum).padStart(2, '0');
|
|
679
|
+
const phasesDir = path.join(planningDir, 'phases');
|
|
680
|
+
if (!fs.existsSync(phasesDir)) throw new Error('No phases directory found');
|
|
744
681
|
|
|
745
|
-
|
|
682
|
+
const phaseDir = fs.readdirSync(phasesDir).find(d => d.startsWith(padded + '-'));
|
|
683
|
+
if (!phaseDir) throw new Error(`Phase ${phaseNum} directory not found`);
|
|
746
684
|
|
|
747
|
-
const
|
|
748
|
-
|
|
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
|
-
}
|
|
685
|
+
const phaseName = phaseDir.replace(/^\d+-/, '').replace(/-/g, ' ');
|
|
686
|
+
const phaseDirPath = path.join(phasesDir, phaseDir);
|
|
762
687
|
|
|
763
|
-
//
|
|
764
|
-
const
|
|
765
|
-
|
|
766
|
-
|
|
688
|
+
// Collect key_files and descriptions from all SUMMARY files
|
|
689
|
+
const keyFiles = [];
|
|
690
|
+
const summaryDescriptions = [];
|
|
691
|
+
const summaryFiles = fs.readdirSync(phaseDirPath).filter(f => /^SUMMARY/i.test(f) && f.endsWith('.md'));
|
|
692
|
+
for (const sf of summaryFiles) {
|
|
693
|
+
try {
|
|
694
|
+
const content = fs.readFileSync(path.join(phaseDirPath, sf), 'utf8');
|
|
695
|
+
const fm = parseYamlFrontmatter(content);
|
|
696
|
+
if (fm.key_files && Array.isArray(fm.key_files)) {
|
|
697
|
+
keyFiles.push(...fm.key_files.map(kf => typeof kf === 'string' ? kf.split(':')[0].trim() : ''));
|
|
698
|
+
}
|
|
699
|
+
if (fm.provides && Array.isArray(fm.provides)) {
|
|
700
|
+
summaryDescriptions.push(...fm.provides);
|
|
701
|
+
}
|
|
702
|
+
} catch (_e) { /* skip unreadable summaries */ }
|
|
767
703
|
}
|
|
768
704
|
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
}
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
705
|
+
// Get recent commit messages
|
|
706
|
+
let commitMessages = [];
|
|
707
|
+
try {
|
|
708
|
+
const { execSync } = require('child_process');
|
|
709
|
+
const log = execSync('git log --oneline -20', { encoding: 'utf8', cwd: path.join(planningDir, '..') });
|
|
710
|
+
commitMessages = log.split('\n').filter(l => l.trim()).map(l => {
|
|
711
|
+
const parts = l.match(/^[0-9a-f]+\s+(.*)/);
|
|
712
|
+
return parts ? parts[1] : '';
|
|
713
|
+
}).filter(Boolean);
|
|
714
|
+
} catch (_e) { /* git not available */ }
|
|
715
|
+
|
|
716
|
+
return { phaseName, phaseNum: String(phaseNum), keyFiles, commitMessages, summaryDescriptions };
|
|
777
717
|
}
|
|
778
718
|
|
|
719
|
+
// --- Phase commit query functions ---
|
|
720
|
+
|
|
779
721
|
/**
|
|
780
|
-
*
|
|
722
|
+
* Read .phase-manifest.json for phase N, output JSON array of commits.
|
|
723
|
+
* Falls back to git log if no manifest exists.
|
|
781
724
|
*/
|
|
782
|
-
function
|
|
783
|
-
const
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
}
|
|
725
|
+
function _phaseCommitsFor(phaseNum) {
|
|
726
|
+
const padded = String(phaseNum).padStart(2, '0');
|
|
727
|
+
const phasesDir = path.join(planningDir, 'phases');
|
|
728
|
+
if (!fs.existsSync(phasesDir)) return { error: 'No phases directory found' };
|
|
787
729
|
|
|
788
|
-
|
|
789
|
-
|
|
730
|
+
const phaseDir = fs.readdirSync(phasesDir).find(d => d.startsWith(padded + '-'));
|
|
731
|
+
if (!phaseDir) return { error: `Phase ${phaseNum} directory not found` };
|
|
790
732
|
|
|
791
|
-
const
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
return
|
|
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` };
|
|
733
|
+
const manifestPath = path.join(phasesDir, phaseDir, '.phase-manifest.json');
|
|
734
|
+
if (fs.existsSync(manifestPath)) {
|
|
735
|
+
try {
|
|
736
|
+
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
|
|
737
|
+
return { source: 'manifest', commits: manifest.commits || [] };
|
|
738
|
+
} catch (_e) { /* fall through to git log */ }
|
|
805
739
|
}
|
|
806
740
|
|
|
807
|
-
|
|
808
|
-
|
|
741
|
+
// Fallback: scan git log for commits matching this phase scope
|
|
742
|
+
try {
|
|
743
|
+
const { execSync } = require('child_process');
|
|
744
|
+
const log = execSync('git log --oneline --no-merges -100', { encoding: 'utf8' });
|
|
745
|
+
const phasePattern = new RegExp(`\\((${padded}-|phase.*${phaseNum})`, 'i');
|
|
746
|
+
const commits = log.split('\n')
|
|
747
|
+
.filter(l => l.trim() && phasePattern.test(l))
|
|
748
|
+
.map(l => {
|
|
749
|
+
const parts = l.match(/^([0-9a-f]+)\s+(.*)/);
|
|
750
|
+
return parts ? { hash: parts[1], message: parts[2] } : null;
|
|
751
|
+
})
|
|
752
|
+
.filter(Boolean);
|
|
753
|
+
return { source: 'git_log', commits };
|
|
754
|
+
} catch (_e) {
|
|
755
|
+
return { error: 'Could not read git log', commits: [] };
|
|
809
756
|
}
|
|
810
|
-
return { success: false, error: result.error };
|
|
811
757
|
}
|
|
812
758
|
|
|
813
|
-
// --- Mutation helpers ---
|
|
814
|
-
|
|
815
759
|
/**
|
|
816
|
-
*
|
|
817
|
-
* Pure function: content in, content out.
|
|
760
|
+
* Output { first, last } commit hashes from phase manifest or git log.
|
|
818
761
|
*/
|
|
819
|
-
function
|
|
820
|
-
const
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
}
|
|
830
|
-
case 'status': {
|
|
831
|
-
const idx = lines.findIndex(l => /^Status:/i.test(l));
|
|
832
|
-
if (idx !== -1) {
|
|
833
|
-
lines[idx] = `Status: ${value}`;
|
|
834
|
-
} 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
|
-
}
|
|
841
|
-
}
|
|
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+/, (_, prefix) => `${prefix}${value}`);
|
|
848
|
-
}
|
|
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}`;
|
|
855
|
-
} else {
|
|
856
|
-
const statusIdx = lines.findIndex(l => /^Status:/i.test(l));
|
|
857
|
-
if (statusIdx !== -1) {
|
|
858
|
-
lines.splice(statusIdx + 1, 0, `Last Activity: ${value}`);
|
|
859
|
-
} else {
|
|
860
|
-
lines.push(`Last Activity: ${value}`);
|
|
861
|
-
}
|
|
862
|
-
}
|
|
863
|
-
break;
|
|
864
|
-
}
|
|
865
|
-
}
|
|
866
|
-
|
|
867
|
-
return lines.join('\n');
|
|
762
|
+
function _phaseFirstLastCommit(phaseNum) {
|
|
763
|
+
const result = _phaseCommitsFor(phaseNum);
|
|
764
|
+
if (result.error && !result.commits) return result;
|
|
765
|
+
const commits = result.commits || [];
|
|
766
|
+
return {
|
|
767
|
+
source: result.source,
|
|
768
|
+
first: commits.length > 0 ? commits[0].hash : null,
|
|
769
|
+
last: commits.length > 0 ? commits[commits.length - 1].hash : null,
|
|
770
|
+
total: commits.length
|
|
771
|
+
};
|
|
868
772
|
}
|
|
869
773
|
|
|
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
|
-
}
|
|
774
|
+
// --- Claim wrapper functions ---
|
|
893
775
|
|
|
894
|
-
|
|
776
|
+
function claimAcquire(phaseSlug, sessionId, skill) {
|
|
777
|
+
const phaseDir = path.join(planningDir, 'phases', phaseSlug);
|
|
778
|
+
if (!fs.existsSync(phaseDir)) return { error: `Phase directory not found: ${phaseSlug}` };
|
|
779
|
+
return acquireClaim(planningDir, phaseDir, sessionId, skill);
|
|
895
780
|
}
|
|
896
781
|
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
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;
|
|
782
|
+
function claimRelease(phaseSlug, sessionId) {
|
|
783
|
+
const phaseDir = path.join(planningDir, 'phases', phaseSlug);
|
|
784
|
+
if (!fs.existsSync(phaseDir)) return { error: `Phase directory not found: ${phaseSlug}` };
|
|
785
|
+
return releaseClaim(planningDir, phaseDir, sessionId);
|
|
913
786
|
}
|
|
914
787
|
|
|
915
|
-
|
|
916
|
-
|
|
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('|');
|
|
788
|
+
function claimList() {
|
|
789
|
+
return _listClaims(planningDir);
|
|
930
790
|
}
|
|
931
791
|
|
|
792
|
+
// --- validateProject stays here (cross-cutting across modules) ---
|
|
793
|
+
|
|
932
794
|
/**
|
|
933
|
-
*
|
|
934
|
-
*
|
|
795
|
+
* Comprehensive .planning/ integrity check.
|
|
796
|
+
* Returns { valid, errors, warnings, checks } — errors mean workflow should not proceed.
|
|
935
797
|
*/
|
|
936
|
-
function
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
return;
|
|
941
|
-
}
|
|
942
|
-
}
|
|
798
|
+
function validateProject() {
|
|
799
|
+
const checks = [];
|
|
800
|
+
const errors = [];
|
|
801
|
+
const warnings = [];
|
|
943
802
|
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
return;
|
|
803
|
+
// 1. .planning/ directory exists
|
|
804
|
+
if (!fs.existsSync(planningDir)) {
|
|
805
|
+
return { valid: false, errors: ['.planning/ directory not found'], warnings: [], checks: ['directory_exists: FAIL'] };
|
|
947
806
|
}
|
|
807
|
+
checks.push('directory_exists: PASS');
|
|
948
808
|
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
809
|
+
// 2. config.json exists and is valid
|
|
810
|
+
const config = configLoad();
|
|
811
|
+
if (!config) {
|
|
812
|
+
errors.push('config.json missing or invalid JSON');
|
|
813
|
+
checks.push('config_valid: FAIL');
|
|
814
|
+
} else {
|
|
815
|
+
const configResult = configValidate(config);
|
|
816
|
+
if (!configResult.valid) {
|
|
817
|
+
errors.push(...configResult.errors.map(e => 'config: ' + e));
|
|
818
|
+
}
|
|
819
|
+
warnings.push(...(configResult.warnings || []).map(w => 'config: ' + w));
|
|
820
|
+
checks.push('config_valid: ' + (configResult.valid ? 'PASS' : 'FAIL'));
|
|
954
821
|
}
|
|
955
822
|
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
823
|
+
// 3. STATE.md exists and has valid frontmatter
|
|
824
|
+
const statePath = path.join(planningDir, 'STATE.md');
|
|
825
|
+
if (!fs.existsSync(statePath)) {
|
|
826
|
+
errors.push('STATE.md not found');
|
|
827
|
+
checks.push('state_exists: FAIL');
|
|
828
|
+
} else {
|
|
829
|
+
try {
|
|
830
|
+
const stateContent = fs.readFileSync(statePath, 'utf8');
|
|
831
|
+
const fm = parseYamlFrontmatter(stateContent);
|
|
832
|
+
if (!fm || !fm.current_phase) {
|
|
833
|
+
warnings.push('STATE.md frontmatter missing current_phase');
|
|
834
|
+
checks.push('state_frontmatter: WARN');
|
|
835
|
+
} else {
|
|
836
|
+
checks.push('state_frontmatter: PASS');
|
|
966
837
|
}
|
|
967
|
-
|
|
838
|
+
} catch (e) {
|
|
839
|
+
errors.push('STATE.md unreadable: ' + e.message);
|
|
840
|
+
checks.push('state_readable: FAIL');
|
|
968
841
|
}
|
|
969
842
|
}
|
|
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
843
|
|
|
987
|
-
|
|
988
|
-
|
|
844
|
+
// 4. ROADMAP.md exists
|
|
845
|
+
const roadmapPath = path.join(planningDir, 'ROADMAP.md');
|
|
846
|
+
if (!fs.existsSync(roadmapPath)) {
|
|
847
|
+
warnings.push('ROADMAP.md not found (may be a new project)');
|
|
848
|
+
checks.push('roadmap_exists: WARN');
|
|
849
|
+
} else {
|
|
850
|
+
checks.push('roadmap_exists: PASS');
|
|
851
|
+
}
|
|
989
852
|
|
|
853
|
+
// 5. Phase directory matches STATE.md current_phase
|
|
990
854
|
try {
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
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;
|
|
855
|
+
if (fs.existsSync(statePath)) {
|
|
856
|
+
const stateContent = fs.readFileSync(statePath, 'utf8');
|
|
857
|
+
const fm = parseYamlFrontmatter(stateContent);
|
|
858
|
+
if (fm && fm.current_phase) {
|
|
859
|
+
const phaseNum = String(fm.current_phase).padStart(2, '0');
|
|
860
|
+
const phasesDir = path.join(planningDir, 'phases');
|
|
861
|
+
if (fs.existsSync(phasesDir)) {
|
|
862
|
+
const dirs = fs.readdirSync(phasesDir).filter(d => d.startsWith(phaseNum + '-'));
|
|
863
|
+
if (dirs.length === 0) {
|
|
864
|
+
warnings.push(`Phase directory for current_phase ${fm.current_phase} not found in .planning/phases/`);
|
|
865
|
+
checks.push('phase_directory: WARN');
|
|
866
|
+
} else {
|
|
867
|
+
checks.push('phase_directory: PASS');
|
|
1020
868
|
}
|
|
1021
|
-
return { success: false, error: `Could not acquire lock for ${path.basename(filePath)} after ${retries} attempts` };
|
|
1022
869
|
}
|
|
1023
|
-
throw e;
|
|
1024
870
|
}
|
|
1025
871
|
}
|
|
872
|
+
} catch (_e) { /* best effort */ }
|
|
1026
873
|
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
874
|
+
// 6. No stale .active-skill (>2 hours old)
|
|
875
|
+
const activeSkillPath = path.join(planningDir, '.active-skill');
|
|
876
|
+
if (fs.existsSync(activeSkillPath)) {
|
|
877
|
+
try {
|
|
878
|
+
const stat = fs.statSync(activeSkillPath);
|
|
879
|
+
const ageMs = Date.now() - stat.mtimeMs;
|
|
880
|
+
if (ageMs > 2 * 60 * 60 * 1000) {
|
|
881
|
+
const ageHours = Math.round(ageMs / (60 * 60 * 1000));
|
|
882
|
+
warnings.push(`.active-skill is ${ageHours}h old — may be stale from a crashed session`);
|
|
883
|
+
checks.push('active_skill_fresh: WARN');
|
|
884
|
+
} else {
|
|
885
|
+
checks.push('active_skill_fresh: PASS');
|
|
886
|
+
}
|
|
887
|
+
} catch (_e) { checks.push('active_skill_fresh: SKIP'); }
|
|
888
|
+
} else {
|
|
889
|
+
checks.push('active_skill_fresh: SKIP');
|
|
890
|
+
}
|
|
1035
891
|
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
892
|
+
// 7. No .tmp files left from atomic writes
|
|
893
|
+
try {
|
|
894
|
+
const tmpFiles = fs.readdirSync(planningDir).filter(f => f.endsWith('.tmp.' + process.pid) || f.match(/\.tmp\.\d+$/));
|
|
895
|
+
if (tmpFiles.length > 0) {
|
|
896
|
+
warnings.push(`Found ${tmpFiles.length} leftover temp files in .planning/: ${tmpFiles.join(', ')}`);
|
|
897
|
+
checks.push('no_temp_files: WARN');
|
|
898
|
+
} else {
|
|
899
|
+
checks.push('no_temp_files: PASS');
|
|
1040
900
|
}
|
|
901
|
+
} catch (_e) { /* best effort */ }
|
|
1041
902
|
|
|
1042
|
-
|
|
1043
|
-
|
|
903
|
+
// 8. Session directory scan — count active, flag stale
|
|
904
|
+
const sessionsResult = { count: 0, active: [], stale: [] };
|
|
905
|
+
const sessionsDir = path.join(planningDir, '.sessions');
|
|
906
|
+
try {
|
|
907
|
+
if (fs.existsSync(sessionsDir)) {
|
|
908
|
+
const entries = fs.readdirSync(sessionsDir, { withFileTypes: true });
|
|
909
|
+
for (const entry of entries) {
|
|
910
|
+
if (!entry.isDirectory()) continue;
|
|
911
|
+
sessionsResult.count++;
|
|
912
|
+
sessionsResult.active.push(entry.name);
|
|
913
|
+
|
|
914
|
+
// Check for staleness via meta.json
|
|
915
|
+
const metaPath = path.join(sessionsDir, entry.name, 'meta.json');
|
|
916
|
+
try {
|
|
917
|
+
const meta = JSON.parse(fs.readFileSync(metaPath, 'utf8'));
|
|
918
|
+
const ageMs = Date.now() - new Date(meta.created).getTime();
|
|
919
|
+
if (ageMs > STALE_SESSION_MS) {
|
|
920
|
+
sessionsResult.stale.push(entry.name);
|
|
921
|
+
}
|
|
922
|
+
} catch (_metaErr) {
|
|
923
|
+
// Fall back to directory mtime
|
|
924
|
+
try {
|
|
925
|
+
const stats = fs.statSync(path.join(sessionsDir, entry.name));
|
|
926
|
+
const ageMs = Date.now() - stats.mtimeMs;
|
|
927
|
+
if (ageMs > STALE_SESSION_MS) {
|
|
928
|
+
sessionsResult.stale.push(entry.name);
|
|
929
|
+
}
|
|
930
|
+
} catch (_statErr) { /* skip */ }
|
|
931
|
+
}
|
|
932
|
+
}
|
|
1044
933
|
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
934
|
+
if (sessionsResult.stale.length > 0) {
|
|
935
|
+
warnings.push(`${sessionsResult.stale.length} stale session(s) found: ${sessionsResult.stale.join(', ')}. Run cleanStaleSessions to remove.`);
|
|
936
|
+
checks.push('sessions_stale: WARN');
|
|
937
|
+
} else {
|
|
938
|
+
checks.push('sessions_stale: PASS');
|
|
939
|
+
}
|
|
1050
940
|
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
941
|
+
// Check for singleton .active-skill coexisting with session-scoped ones
|
|
942
|
+
if (fs.existsSync(path.join(planningDir, '.active-skill'))) {
|
|
943
|
+
let hasSessionSkill = false;
|
|
944
|
+
for (const sid of sessionsResult.active) {
|
|
945
|
+
if (fs.existsSync(path.join(sessionsDir, sid, '.active-skill'))) {
|
|
946
|
+
hasSessionSkill = true;
|
|
947
|
+
break;
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
if (hasSessionSkill) {
|
|
951
|
+
warnings.push('.active-skill exists at both singleton and session-scoped paths — possible migration artifact. Consider removing the singleton .planning/.active-skill.');
|
|
952
|
+
checks.push('active_skill_dual: WARN');
|
|
953
|
+
}
|
|
954
|
+
}
|
|
1064
955
|
}
|
|
1065
|
-
}
|
|
1066
|
-
}
|
|
1067
|
-
|
|
1068
|
-
// --- Parsers ---
|
|
956
|
+
} catch (_e) { /* best effort */ }
|
|
1069
957
|
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
line_count: content.split('\n').length,
|
|
1077
|
-
format: 'legacy' // 'legacy' or 'frontmatter'
|
|
958
|
+
return {
|
|
959
|
+
valid: errors.length === 0,
|
|
960
|
+
errors,
|
|
961
|
+
warnings,
|
|
962
|
+
checks,
|
|
963
|
+
sessions: sessionsResult
|
|
1078
964
|
};
|
|
965
|
+
}
|
|
1079
966
|
|
|
1080
|
-
|
|
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
|
-
}
|
|
967
|
+
// --- Spec subcommand handler ---
|
|
1108
968
|
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
969
|
+
/**
|
|
970
|
+
* Handle spec subcommands: parse, diff, reverse, impact.
|
|
971
|
+
* @param {string[]} args - raw CLI args (args[0] is 'spec', args[1] is subcommand)
|
|
972
|
+
* @param {string} pDir - planningDir
|
|
973
|
+
* @param {string} projectRoot - cwd
|
|
974
|
+
* @param {Function} outputFn - output function
|
|
975
|
+
* @param {Function} errorFn - error function
|
|
976
|
+
*/
|
|
977
|
+
function handleSpec(args, pDir, projectRoot, outputFn, errorFn) {
|
|
978
|
+
const subcommand = args[1];
|
|
1114
979
|
|
|
1115
|
-
//
|
|
1116
|
-
const
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
980
|
+
// Parse common flags
|
|
981
|
+
const formatIdx = args.indexOf('--format');
|
|
982
|
+
const format = formatIdx !== -1 ? args[formatIdx + 1] : 'json';
|
|
983
|
+
const projectRootIdx = args.indexOf('--project-root');
|
|
984
|
+
const effectiveRoot = projectRootIdx !== -1 ? args[projectRootIdx + 1] : projectRoot;
|
|
1120
985
|
|
|
1121
|
-
//
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
986
|
+
// Load config for feature toggle checks
|
|
987
|
+
let config = {};
|
|
988
|
+
try {
|
|
989
|
+
const configPath = path.join(pDir, 'config.json');
|
|
990
|
+
if (fs.existsSync(configPath)) {
|
|
991
|
+
config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
992
|
+
}
|
|
993
|
+
} catch (_e) {
|
|
994
|
+
// Config unreadable — use defaults
|
|
1125
995
|
}
|
|
996
|
+
const features = config.features || {};
|
|
1126
997
|
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
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'
|
|
998
|
+
// Audit log helper
|
|
999
|
+
function writeAuditLog(cmd, featureName, status, fileCount) {
|
|
1000
|
+
try {
|
|
1001
|
+
const logDir = path.join(pDir, 'logs');
|
|
1002
|
+
if (fs.existsSync(logDir)) {
|
|
1003
|
+
const logFile = path.join(logDir, 'spec-engine.jsonl');
|
|
1004
|
+
const entry = JSON.stringify({
|
|
1005
|
+
ts: new Date().toISOString(),
|
|
1006
|
+
cmd: `spec.${cmd}`,
|
|
1007
|
+
feature: featureName,
|
|
1008
|
+
status,
|
|
1009
|
+
files: fileCount || 0,
|
|
1148
1010
|
});
|
|
1011
|
+
fs.appendFileSync(logFile, entry + '\n', 'utf-8');
|
|
1149
1012
|
}
|
|
1013
|
+
} catch (_e2) {
|
|
1014
|
+
// No-op if log dir missing or unwritable
|
|
1150
1015
|
}
|
|
1151
1016
|
}
|
|
1152
1017
|
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1018
|
+
if (!subcommand || subcommand === '--help' || subcommand === 'help') {
|
|
1019
|
+
const usageText = [
|
|
1020
|
+
'Usage: spec <subcommand> [args] [--format json|markdown]',
|
|
1021
|
+
'',
|
|
1022
|
+
'Subcommands:',
|
|
1023
|
+
' parse <plan-file> Parse PLAN.md into structured JSON',
|
|
1024
|
+
' diff <file-a> <file-b> Semantic diff between two PLAN.md versions',
|
|
1025
|
+
' reverse <file...> Generate spec from source files',
|
|
1026
|
+
' impact <file...> Predict impact of changed files',
|
|
1027
|
+
'',
|
|
1028
|
+
'Flags:',
|
|
1029
|
+
' --format json|markdown Output format (default: json)',
|
|
1030
|
+
' --project-root <path> Project root for impact analysis',
|
|
1031
|
+
].join('\n');
|
|
1032
|
+
outputFn(null, true, usageText);
|
|
1033
|
+
return;
|
|
1034
|
+
}
|
|
1169
1035
|
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
if (
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1036
|
+
if (subcommand === 'parse') {
|
|
1037
|
+
const planFile = args[2];
|
|
1038
|
+
if (!planFile) { errorFn('Usage: spec parse <plan-file>'); return; }
|
|
1039
|
+
const { parsePlanToSpec } = require('./lib/spec-engine');
|
|
1040
|
+
let content;
|
|
1041
|
+
try {
|
|
1042
|
+
content = fs.readFileSync(planFile, 'utf-8');
|
|
1043
|
+
} catch (e) {
|
|
1044
|
+
errorFn(`Cannot read file: ${planFile}: ${e.message}`);
|
|
1045
|
+
return;
|
|
1179
1046
|
}
|
|
1047
|
+
const spec = parsePlanToSpec(content);
|
|
1048
|
+
writeAuditLog('parse', 'machine_executable_plans', 'ok', 1);
|
|
1049
|
+
outputFn({ frontmatter: spec.frontmatter, tasks: spec.tasks });
|
|
1050
|
+
return;
|
|
1051
|
+
}
|
|
1180
1052
|
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
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;
|
|
1053
|
+
if (subcommand === 'diff') {
|
|
1054
|
+
if (features.spec_diffing === false) {
|
|
1055
|
+
outputFn({ error: 'Feature disabled. Enable features.spec_diffing in config.json' });
|
|
1056
|
+
return;
|
|
1057
|
+
}
|
|
1058
|
+
const fileA = args[2];
|
|
1059
|
+
const fileB = args[3];
|
|
1060
|
+
if (!fileA || !fileB) { errorFn('Usage: spec diff <file-a> <file-b>'); return; }
|
|
1061
|
+
const { diffPlanFiles, formatDiff } = require('./lib/spec-diff');
|
|
1062
|
+
let contentA, contentB;
|
|
1063
|
+
try {
|
|
1064
|
+
contentA = fs.readFileSync(fileA, 'utf-8');
|
|
1065
|
+
contentB = fs.readFileSync(fileB, 'utf-8');
|
|
1066
|
+
} catch (e) {
|
|
1067
|
+
errorFn(`Cannot read file: ${e.message}`);
|
|
1068
|
+
return;
|
|
1069
|
+
}
|
|
1070
|
+
const diff = diffPlanFiles(contentA, contentB);
|
|
1071
|
+
writeAuditLog('diff', 'spec_diffing', 'ok', 2);
|
|
1072
|
+
if (format === 'markdown') {
|
|
1073
|
+
outputFn(null, true, formatDiff(diff, 'markdown'));
|
|
1074
|
+
} else {
|
|
1075
|
+
outputFn(diff);
|
|
1209
1076
|
}
|
|
1077
|
+
return;
|
|
1210
1078
|
}
|
|
1211
1079
|
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1080
|
+
if (subcommand === 'reverse') {
|
|
1081
|
+
if (features.reverse_spec === false) {
|
|
1082
|
+
outputFn({ error: 'Feature disabled. Enable features.reverse_spec in config.json' });
|
|
1083
|
+
return;
|
|
1084
|
+
}
|
|
1085
|
+
const files = [];
|
|
1086
|
+
for (let i = 2; i < args.length; i++) {
|
|
1087
|
+
if (!args[i].startsWith('--')) files.push(args[i]);
|
|
1088
|
+
}
|
|
1089
|
+
if (files.length === 0) { errorFn('Usage: spec reverse <file...>'); return; }
|
|
1090
|
+
const { generateReverseSpec } = require('./lib/reverse-spec');
|
|
1091
|
+
const { serializeSpec } = require('./lib/spec-engine');
|
|
1092
|
+
const spec = generateReverseSpec(files, { readFile: (p) => fs.readFileSync(p, 'utf-8') });
|
|
1093
|
+
writeAuditLog('reverse', 'reverse_spec', 'ok', files.length);
|
|
1094
|
+
if (format === 'markdown') {
|
|
1095
|
+
outputFn(null, true, serializeSpec(spec));
|
|
1096
|
+
} else {
|
|
1097
|
+
outputFn(spec);
|
|
1098
|
+
}
|
|
1099
|
+
return;
|
|
1215
1100
|
}
|
|
1216
1101
|
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
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;
|
|
1102
|
+
if (subcommand === 'impact') {
|
|
1103
|
+
if (features.predictive_impact === false) {
|
|
1104
|
+
outputFn({ error: 'Feature disabled. Enable features.predictive_impact in config.json' });
|
|
1105
|
+
return;
|
|
1231
1106
|
}
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
if (
|
|
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, ''));
|
|
1240
|
-
}
|
|
1107
|
+
const files = [];
|
|
1108
|
+
for (let i = 2; i < args.length; i++) {
|
|
1109
|
+
if (!args[i].startsWith('--') && args[i - 1] !== '--project-root') files.push(args[i]);
|
|
1241
1110
|
}
|
|
1111
|
+
if (files.length === 0) { errorFn('Usage: spec impact <file...>'); return; }
|
|
1112
|
+
const { analyzeImpact } = require('./lib/impact-analysis');
|
|
1113
|
+
const report = analyzeImpact(files, effectiveRoot);
|
|
1114
|
+
writeAuditLog('impact', 'predictive_impact', 'ok', files.length);
|
|
1115
|
+
outputFn(report);
|
|
1116
|
+
return;
|
|
1242
1117
|
}
|
|
1243
1118
|
|
|
1244
|
-
|
|
1119
|
+
errorFn(`Unknown spec subcommand: ${subcommand}\nAvailable: parse, diff, reverse, impact`);
|
|
1245
1120
|
}
|
|
1246
1121
|
|
|
1247
|
-
// ---
|
|
1122
|
+
// --- CLI entry point ---
|
|
1248
1123
|
|
|
1249
|
-
function
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
return [];
|
|
1254
|
-
}
|
|
1255
|
-
}
|
|
1124
|
+
async function main() {
|
|
1125
|
+
const args = process.argv.slice(2);
|
|
1126
|
+
const command = args[0];
|
|
1127
|
+
const subcommand = args[1];
|
|
1256
1128
|
|
|
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
1129
|
try {
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
if (
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
(
|
|
1282
|
-
}
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1130
|
+
if (command === 'state' && subcommand === 'load') {
|
|
1131
|
+
output(stateLoad());
|
|
1132
|
+
} else if (command === 'state' && subcommand === 'check-progress') {
|
|
1133
|
+
output(stateCheckProgress());
|
|
1134
|
+
} else if (command === 'state' && subcommand === 'update') {
|
|
1135
|
+
const field = args[2];
|
|
1136
|
+
const value = args[3];
|
|
1137
|
+
if (!field || value === undefined) {
|
|
1138
|
+
error('Usage: pbr-tools.js state update <field> <value>\nFields: current_phase, status, plans_complete, last_activity, progress_percent, phase_slug, last_command, blockers');
|
|
1139
|
+
}
|
|
1140
|
+
output(stateUpdate(field, value));
|
|
1141
|
+
} else if (command === 'config' && subcommand === 'validate') {
|
|
1142
|
+
output(configValidate());
|
|
1143
|
+
} else if (command === 'validate' && subcommand === 'health') {
|
|
1144
|
+
const { getAllPhase10Checks } = require('./lib/health-checks');
|
|
1145
|
+
const checks = getAllPhase10Checks(planningDir);
|
|
1146
|
+
output({ phase10: checks, timestamp: new Date().toISOString() });
|
|
1147
|
+
} else if (command === 'config' && subcommand === 'load-defaults') {
|
|
1148
|
+
const defaults = loadUserDefaults();
|
|
1149
|
+
output(defaults || { exists: false, path: USER_DEFAULTS_PATH });
|
|
1150
|
+
} else if (command === 'config' && subcommand === 'save-defaults') {
|
|
1151
|
+
const config = configLoad();
|
|
1152
|
+
if (!config) error('No config.json found. Run /pbr:setup first.');
|
|
1153
|
+
output(saveUserDefaults(config));
|
|
1154
|
+
} else if (command === 'config' && subcommand === 'format') {
|
|
1155
|
+
const config = configLoad();
|
|
1156
|
+
if (!config) error('No config.json found.');
|
|
1157
|
+
_configWrite(planningDir, config);
|
|
1158
|
+
output({ formatted: true, path: path.join(planningDir, 'config.json') });
|
|
1159
|
+
} else if (command === 'config' && subcommand === 'resolve-depth') {
|
|
1160
|
+
const dir = args[2] || undefined;
|
|
1161
|
+
const config = configLoad(dir);
|
|
1162
|
+
output(resolveDepthProfile(config));
|
|
1163
|
+
} else if (command === 'config' && subcommand === 'get') {
|
|
1164
|
+
const key = args[2];
|
|
1165
|
+
if (!key) { error('Usage: config get <dot.path.key>'); }
|
|
1166
|
+
const cfg = configLoad();
|
|
1167
|
+
if (!cfg) { error('No config.json found'); }
|
|
1168
|
+
const parts = key.split('.');
|
|
1169
|
+
let val = cfg;
|
|
1170
|
+
for (const p of parts) {
|
|
1171
|
+
if (val == null || typeof val !== 'object') { val = undefined; break; }
|
|
1172
|
+
val = val[p];
|
|
1173
|
+
}
|
|
1174
|
+
if (val === undefined) { error(`Config key not found: ${key}`); }
|
|
1175
|
+
output(typeof val === 'object' ? val : { value: val });
|
|
1176
|
+
|
|
1177
|
+
} else if (command === 'intel') {
|
|
1178
|
+
const subCmd = args[1];
|
|
1179
|
+
if (subCmd === 'query') {
|
|
1180
|
+
const term = args[2];
|
|
1181
|
+
if (!term) { error('Usage: intel query <term>'); }
|
|
1182
|
+
output(intelQuery(term));
|
|
1183
|
+
} else if (subCmd === 'status') {
|
|
1184
|
+
output(intelStatus());
|
|
1185
|
+
} else if (subCmd === 'diff') {
|
|
1186
|
+
output(intelDiff());
|
|
1187
|
+
} else {
|
|
1188
|
+
error('Usage: intel <query|status|diff>');
|
|
1189
|
+
}
|
|
1289
1190
|
|
|
1290
|
-
|
|
1291
|
-
|
|
1191
|
+
} else if (command === 'requirements' && subcommand === 'mark-complete') {
|
|
1192
|
+
const ids = args[2];
|
|
1193
|
+
if (!ids) { error('Usage: requirements mark-complete <comma-separated-REQ-IDs>'); }
|
|
1194
|
+
const idList = ids.split(',').map(s => s.trim());
|
|
1195
|
+
output(requirementsMarkComplete(idList));
|
|
1292
1196
|
|
|
1293
|
-
|
|
1294
|
-
|
|
1197
|
+
} else if (command === 'plan-index') {
|
|
1198
|
+
const phase = args[1];
|
|
1199
|
+
if (!phase) {
|
|
1200
|
+
error('Usage: pbr-tools.js plan-index <phase-number>');
|
|
1201
|
+
}
|
|
1202
|
+
output(planIndex(phase));
|
|
1203
|
+
} else if (command === 'frontmatter') {
|
|
1204
|
+
const filePath = args[1];
|
|
1205
|
+
if (!filePath) {
|
|
1206
|
+
error('Usage: pbr-tools.js frontmatter <filepath>');
|
|
1207
|
+
}
|
|
1208
|
+
output(frontmatter(filePath));
|
|
1209
|
+
} else if (command === 'must-haves') {
|
|
1210
|
+
const phase = args[1];
|
|
1211
|
+
if (!phase) {
|
|
1212
|
+
error('Usage: pbr-tools.js must-haves <phase-number>');
|
|
1213
|
+
}
|
|
1214
|
+
output(mustHavesCollect(phase));
|
|
1215
|
+
} else if (command === 'phase-info') {
|
|
1216
|
+
const phase = args[1];
|
|
1217
|
+
if (!phase) {
|
|
1218
|
+
error('Usage: pbr-tools.js phase-info <phase-number>');
|
|
1219
|
+
}
|
|
1220
|
+
output(phaseInfo(phase));
|
|
1221
|
+
} else if (command === 'roadmap' && subcommand === 'update-status') {
|
|
1222
|
+
const phase = args[2];
|
|
1223
|
+
const status = args[3];
|
|
1224
|
+
if (!phase || !status) {
|
|
1225
|
+
error('Usage: pbr-tools.js roadmap update-status <phase-number> <status>');
|
|
1226
|
+
}
|
|
1227
|
+
output(roadmapUpdateStatus(phase, status));
|
|
1228
|
+
} else if (command === 'roadmap' && subcommand === 'update-plans') {
|
|
1229
|
+
const phase = args[2];
|
|
1230
|
+
const complete = args[3];
|
|
1231
|
+
const total = args[4];
|
|
1232
|
+
if (!phase || complete === undefined || total === undefined) {
|
|
1233
|
+
error('Usage: pbr-tools.js roadmap update-plans <phase-number> <complete> <total>');
|
|
1234
|
+
}
|
|
1235
|
+
output(roadmapUpdatePlans(phase, complete, total));
|
|
1236
|
+
} else if (command === 'roadmap' && subcommand === 'analyze') {
|
|
1237
|
+
output(roadmapAnalyze());
|
|
1238
|
+
} else if (command === 'roadmap' && subcommand === 'reconcile') {
|
|
1239
|
+
const result = roadmapReconcile();
|
|
1240
|
+
if (result.fixed > 0) {
|
|
1241
|
+
process.stderr.write(`Reconciled ${result.fixed} ROADMAP entries\n`);
|
|
1242
|
+
for (const m of result.mismatches) {
|
|
1243
|
+
process.stderr.write(` Phase ${m.phase}: ${m.from} -> ${m.to}\n`);
|
|
1244
|
+
}
|
|
1245
|
+
} else {
|
|
1246
|
+
process.stderr.write('ROADMAP statuses are consistent\n');
|
|
1247
|
+
}
|
|
1248
|
+
output(result);
|
|
1249
|
+
} else if (command === 'roadmap' && subcommand === 'get-phase') {
|
|
1250
|
+
const phaseNum = args[2];
|
|
1251
|
+
if (!phaseNum) { error('Usage: roadmap get-phase <phase_num>'); }
|
|
1252
|
+
output(phaseInfo(phaseNum));
|
|
1253
|
+
} else if (command === 'roadmap' && subcommand === 'append-phase') {
|
|
1254
|
+
const goalIdx = args.indexOf('--goal');
|
|
1255
|
+
const goal = goalIdx >= 0 ? args[goalIdx + 1] : args[2] || '';
|
|
1256
|
+
const nameIdx = args.indexOf('--name');
|
|
1257
|
+
const name = nameIdx >= 0 ? args[nameIdx + 1] : '';
|
|
1258
|
+
const depIdx = args.indexOf('--depends-on');
|
|
1259
|
+
const dependsOn = depIdx >= 0 ? args[depIdx + 1] : null;
|
|
1260
|
+
const analysis = roadmapAnalyze();
|
|
1261
|
+
const maxPhase = analysis.phases ? Math.max(...analysis.phases.map(p => p.num || 0), 0) : 0;
|
|
1262
|
+
const nextNum = maxPhase + 1;
|
|
1263
|
+
output(roadmapAppendPhase(nextNum, name || goal, goal, dependsOn));
|
|
1295
1264
|
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1265
|
+
} else if (command === 'history' && subcommand === 'append') {
|
|
1266
|
+
const type = args[2]; // 'milestone' or 'phase'
|
|
1267
|
+
const title = args[3];
|
|
1268
|
+
const body = args[4] || '';
|
|
1269
|
+
if (!type || !title) {
|
|
1270
|
+
error('Usage: pbr-tools.js history append <milestone|phase> <title> [body]');
|
|
1271
|
+
}
|
|
1272
|
+
output(historyAppend({ type, title, body }));
|
|
1273
|
+
} else if (command === 'history' && subcommand === 'load') {
|
|
1274
|
+
output(historyLoad());
|
|
1275
|
+
} else if (command === 'event') {
|
|
1276
|
+
const category = args[1];
|
|
1277
|
+
const event = args[2];
|
|
1278
|
+
let details = {};
|
|
1279
|
+
if (args[3]) {
|
|
1280
|
+
try { details = JSON.parse(args[3]); } catch (_e) { details = { raw: args[3] }; }
|
|
1281
|
+
}
|
|
1282
|
+
if (!category || !event) {
|
|
1283
|
+
error('Usage: pbr-tools.js event <category> <event> [JSON-details]');
|
|
1284
|
+
}
|
|
1285
|
+
const { logEvent } = require('./event-logger');
|
|
1286
|
+
logEvent(category, event, details);
|
|
1287
|
+
output({ logged: true, category, event });
|
|
1288
|
+
} else if (command === 'llm') {
|
|
1289
|
+
// local_llm feature has been removed (phase 53). All subcommands are no-ops.
|
|
1290
|
+
output({ deprecated: true, message: 'local_llm feature has been removed. This subcommand is a no-op.' });
|
|
1291
|
+
// --- Compound init commands ---
|
|
1292
|
+
} else if (command === "init" && subcommand === "execute-phase") {
|
|
1293
|
+
const phase = args[2];
|
|
1294
|
+
if (!phase) error("Usage: pbr-tools.js init execute-phase <phase-number>");
|
|
1295
|
+
output(initExecutePhase(phase));
|
|
1296
|
+
} else if (command === "init" && subcommand === "plan-phase") {
|
|
1297
|
+
const phase = args[2];
|
|
1298
|
+
if (!phase) error("Usage: pbr-tools.js init plan-phase <phase-number>");
|
|
1299
|
+
output(initPlanPhase(phase));
|
|
1300
|
+
} else if (command === "init" && subcommand === "quick") {
|
|
1301
|
+
const desc = args.slice(2).join(" ") || "";
|
|
1302
|
+
output(initQuick(desc));
|
|
1303
|
+
} else if (command === "init" && subcommand === "verify-work") {
|
|
1304
|
+
const phase = args[2];
|
|
1305
|
+
if (!phase) error("Usage: pbr-tools.js init verify-work <phase-number>");
|
|
1306
|
+
output(initVerifyWork(phase));
|
|
1307
|
+
} else if (command === "init" && subcommand === "resume") {
|
|
1308
|
+
output(initResume());
|
|
1309
|
+
} else if (command === "init" && subcommand === "progress") {
|
|
1310
|
+
output(initProgress());
|
|
1311
|
+
} else if (command === "init" && subcommand === "continue") {
|
|
1312
|
+
output(initContinue());
|
|
1313
|
+
} else if (command === "init" && subcommand === "milestone") {
|
|
1314
|
+
output(initMilestone());
|
|
1315
|
+
} else if (command === "init" && subcommand === "begin") {
|
|
1316
|
+
output(initBegin());
|
|
1317
|
+
} else if (command === "init" && subcommand === "status") {
|
|
1318
|
+
output(initStatus());
|
|
1319
|
+
} else if (command === "init" && subcommand === "map-codebase") {
|
|
1320
|
+
output(initMapCodebase());
|
|
1321
|
+
} else if (command === 'state-bundle') {
|
|
1322
|
+
const phaseNum = args[1];
|
|
1323
|
+
if (!phaseNum) error('Usage: pbr-tools.js state-bundle <phase-number>');
|
|
1324
|
+
output(stateBundle(phaseNum));
|
|
1325
|
+
// --- State patch/advance/metric ---
|
|
1326
|
+
} else if (command === "state" && subcommand === "patch") {
|
|
1327
|
+
const jsonStr = args[2];
|
|
1328
|
+
if (!jsonStr) error("Usage: pbr-tools.js state patch JSON");
|
|
1329
|
+
output(statePatch(jsonStr));
|
|
1330
|
+
} else if (command === "state" && subcommand === "advance-plan") {
|
|
1331
|
+
output(stateAdvancePlan());
|
|
1332
|
+
} else if (command === "state" && subcommand === "record-metric") {
|
|
1333
|
+
output(stateRecordMetric(args.slice(2)));
|
|
1334
|
+
} else if (command === "state" && subcommand === "record-activity") {
|
|
1335
|
+
const description = args.slice(2).join(' ');
|
|
1336
|
+
if (!description) error("Usage: pbr-tools.js state record-activity <description>");
|
|
1337
|
+
output(stateRecordActivity(description));
|
|
1338
|
+
} else if (command === "state" && subcommand === "update-progress") {
|
|
1339
|
+
output(stateUpdateProgress());
|
|
1340
|
+
} else if (command === 'state' && subcommand === 'reconcile') {
|
|
1341
|
+
output(stateReconcile());
|
|
1342
|
+
} else if (command === 'state' && subcommand === 'backup') {
|
|
1343
|
+
output(stateBackup());
|
|
1344
|
+
} else if (command === 'state' && subcommand === 'enqueue') {
|
|
1345
|
+
const field = args[2];
|
|
1346
|
+
const value = args[3];
|
|
1347
|
+
if (!field || value === undefined) { error('Usage: state enqueue <field> <value>'); }
|
|
1348
|
+
const { stateEnqueue } = require('./lib/state-queue');
|
|
1349
|
+
output(stateEnqueue(field, value, planningDir));
|
|
1350
|
+
} else if (command === 'state' && subcommand === 'drain') {
|
|
1351
|
+
const { stateDrain } = require('./lib/state-queue');
|
|
1352
|
+
output(stateDrain(planningDir));
|
|
1353
|
+
} else if (command === 'phase' && subcommand === 'add') {
|
|
1354
|
+
const slug = args[2];
|
|
1355
|
+
if (!slug) { error('Usage: phase add <slug> [--after N] [--goal "..."] [--depends-on N]'); }
|
|
1356
|
+
const afterIdx = args.indexOf('--after');
|
|
1357
|
+
const afterPhase = afterIdx !== -1 ? args[afterIdx + 1] : null;
|
|
1358
|
+
const goalIdx = args.indexOf('--goal');
|
|
1359
|
+
const goal = goalIdx !== -1 ? args[goalIdx + 1] : null;
|
|
1360
|
+
const depIdx = args.indexOf('--depends-on');
|
|
1361
|
+
const dependsOn = depIdx !== -1 ? args[depIdx + 1] : null;
|
|
1362
|
+
const addOpts = {};
|
|
1363
|
+
if (goal) addOpts.goal = goal;
|
|
1364
|
+
if (dependsOn) addOpts.dependsOn = dependsOn;
|
|
1365
|
+
output(phaseAdd(slug, afterPhase, Object.keys(addOpts).length > 0 ? addOpts : undefined));
|
|
1366
|
+
} else if (command === 'phase' && subcommand === 'remove') {
|
|
1367
|
+
const phaseNum = args[2];
|
|
1368
|
+
if (!phaseNum) { error('Usage: phase remove <phase_num>'); }
|
|
1369
|
+
output(phaseRemove(phaseNum));
|
|
1370
|
+
} else if (command === 'phase' && subcommand === 'list') {
|
|
1371
|
+
const statusIdx = args.indexOf('--status');
|
|
1372
|
+
const beforeIdx = args.indexOf('--before');
|
|
1373
|
+
const listOpts = {};
|
|
1374
|
+
if (statusIdx >= 0) listOpts.status = args[statusIdx + 1];
|
|
1375
|
+
if (beforeIdx >= 0) listOpts.before = args[beforeIdx + 1];
|
|
1376
|
+
output(phaseList(listOpts));
|
|
1377
|
+
} else if (command === 'phase' && subcommand === 'next-number') {
|
|
1378
|
+
output(phaseNextNumber());
|
|
1379
|
+
} else if (command === 'phase' && subcommand === 'complete') {
|
|
1380
|
+
const phaseNum = args[2];
|
|
1381
|
+
if (!phaseNum) { error('Usage: phase complete <phase_num>'); }
|
|
1382
|
+
output(phaseComplete(phaseNum));
|
|
1383
|
+
} else if (command === 'phase' && subcommand === 'insert') {
|
|
1384
|
+
const position = args[2];
|
|
1385
|
+
const slug = args[3];
|
|
1386
|
+
if (!position || !slug) { error('Usage: phase insert <N> <slug> [--goal "..."] [--depends-on N]'); }
|
|
1387
|
+
const goalIdx = args.indexOf('--goal');
|
|
1388
|
+
const goal = goalIdx !== -1 ? args[goalIdx + 1] : null;
|
|
1389
|
+
const depIdx = args.indexOf('--depends-on');
|
|
1390
|
+
const dependsOn = depIdx !== -1 ? args[depIdx + 1] : null;
|
|
1391
|
+
const insertOpts = {};
|
|
1392
|
+
if (goal) insertOpts.goal = goal;
|
|
1393
|
+
if (dependsOn) insertOpts.dependsOn = dependsOn;
|
|
1394
|
+
output(phaseInsert(parseInt(position, 10), slug, Object.keys(insertOpts).length > 0 ? insertOpts : undefined));
|
|
1395
|
+
} else if (command === 'phase' && subcommand === 'commits-for') {
|
|
1396
|
+
const phaseNum = args[2];
|
|
1397
|
+
if (!phaseNum) { error('Usage: phase commits-for <N>'); }
|
|
1398
|
+
output(_phaseCommitsFor(phaseNum));
|
|
1399
|
+
} else if (command === 'phase' && subcommand === 'first-last-commit') {
|
|
1400
|
+
const phaseNum = args[2];
|
|
1401
|
+
if (!phaseNum) { error('Usage: phase first-last-commit <N>'); }
|
|
1402
|
+
output(_phaseFirstLastCommit(phaseNum));
|
|
1403
|
+
} else if (command === 'compound' && subcommand === 'init-phase') {
|
|
1404
|
+
const phaseNum = args[2];
|
|
1405
|
+
const slug = args[3];
|
|
1406
|
+
if (!phaseNum || !slug) error('Usage: compound init-phase <N> <slug> [--goal "..."] [--depends-on N]');
|
|
1407
|
+
const goalIdx = args.indexOf('--goal');
|
|
1408
|
+
const goal = goalIdx !== -1 ? args[goalIdx + 1] : null;
|
|
1409
|
+
const depIdx = args.indexOf('--depends-on');
|
|
1410
|
+
const dependsOn = depIdx !== -1 ? args[depIdx + 1] : null;
|
|
1411
|
+
output(compoundInitPhase(phaseNum, slug, { goal, dependsOn }));
|
|
1412
|
+
} else if (command === 'compound' && subcommand === 'complete-phase') {
|
|
1413
|
+
const phaseNum = args[2];
|
|
1414
|
+
if (!phaseNum) error('Usage: compound complete-phase <N>');
|
|
1415
|
+
output(compoundCompletePhase(phaseNum));
|
|
1416
|
+
} else if (command === 'compound' && subcommand === 'init-milestone') {
|
|
1417
|
+
const version = args[2];
|
|
1418
|
+
if (!version) error('Usage: compound init-milestone <version> [--name "..."] [--phases "N-M"]');
|
|
1419
|
+
const nameIdx = args.indexOf('--name');
|
|
1420
|
+
const name = nameIdx !== -1 ? args[nameIdx + 1] : null;
|
|
1421
|
+
const phasesIdx = args.indexOf('--phases');
|
|
1422
|
+
const phases = phasesIdx !== -1 ? args[phasesIdx + 1] : null;
|
|
1423
|
+
output(compoundInitMilestone(version, { name, phases }));
|
|
1424
|
+
} else if (command === 'todo' && subcommand === 'list') {
|
|
1425
|
+
const opts = {};
|
|
1426
|
+
const themeIdx = args.indexOf('--theme');
|
|
1427
|
+
if (themeIdx !== -1 && args[themeIdx + 1]) opts.theme = args[themeIdx + 1];
|
|
1428
|
+
const statusIdx = args.indexOf('--status');
|
|
1429
|
+
if (statusIdx !== -1 && args[statusIdx + 1]) opts.status = args[statusIdx + 1];
|
|
1430
|
+
output(todoList(opts));
|
|
1431
|
+
} else if (command === 'todo' && subcommand === 'get') {
|
|
1432
|
+
const num = args[2];
|
|
1433
|
+
if (!num) error('Usage: pbr-tools.js todo get <NNN>');
|
|
1434
|
+
output(todoGet(num));
|
|
1435
|
+
} else if (command === 'todo' && subcommand === 'add') {
|
|
1436
|
+
const titleParts = [];
|
|
1437
|
+
const opts = {};
|
|
1438
|
+
// Parse: todo add <title words...> [--priority P1] [--theme security] [--source cli]
|
|
1439
|
+
for (let i = 2; i < args.length; i++) {
|
|
1440
|
+
if (args[i] === '--priority' && args[i + 1]) { opts.priority = args[++i]; }
|
|
1441
|
+
else if (args[i] === '--theme' && args[i + 1]) { opts.theme = args[++i]; }
|
|
1442
|
+
else if (args[i] === '--source' && args[i + 1]) { opts.source = args[++i]; }
|
|
1443
|
+
else { titleParts.push(args[i]); }
|
|
1444
|
+
}
|
|
1445
|
+
const title = titleParts.join(' ');
|
|
1446
|
+
if (!title) error('Usage: pbr-tools.js todo add <title> [--priority P1|P2|P3] [--theme <theme>]');
|
|
1447
|
+
output(todoAdd(title, opts));
|
|
1448
|
+
} else if (command === 'todo' && subcommand === 'done') {
|
|
1449
|
+
const num = args[2];
|
|
1450
|
+
if (!num) error('Usage: pbr-tools.js todo done <NNN>');
|
|
1451
|
+
output(todoDone(num));
|
|
1452
|
+
} else if (command === 'auto-cleanup') {
|
|
1453
|
+
const phaseFlag = args.indexOf('--phase');
|
|
1454
|
+
const milestoneFlag = args.indexOf('--milestone');
|
|
1455
|
+
if (phaseFlag !== -1 && args[phaseFlag + 1]) {
|
|
1456
|
+
const phaseNum = args[phaseFlag + 1];
|
|
1457
|
+
const context = buildCleanupContext(phaseNum);
|
|
1458
|
+
const todoResult = autoCloseTodos(context);
|
|
1459
|
+
const noteResult = autoArchiveNotes(context);
|
|
1460
|
+
output({ phase: phaseNum, todos: todoResult, notes: noteResult });
|
|
1461
|
+
} else if (milestoneFlag !== -1 && args[milestoneFlag + 1]) {
|
|
1462
|
+
const version = args[milestoneFlag + 1];
|
|
1463
|
+
// Parse ROADMAP.md to find phases in this milestone
|
|
1464
|
+
const roadmapPath = path.join(planningDir, 'ROADMAP.md');
|
|
1465
|
+
if (!fs.existsSync(roadmapPath)) { error('ROADMAP.md not found'); }
|
|
1466
|
+
const roadmap = fs.readFileSync(roadmapPath, 'utf8');
|
|
1467
|
+
const milestoneMatch = roadmap.match(new RegExp('Milestone.*' + version.replace(/\./g, '\\.') + '[\\s\\S]*?Phases:\\s*(\\d+)\\s*-\\s*(\\d+)'));
|
|
1468
|
+
if (!milestoneMatch) { error('Milestone ' + version + ' not found in ROADMAP.md'); }
|
|
1469
|
+
const startPhase = parseInt(milestoneMatch[1]);
|
|
1470
|
+
const endPhase = parseInt(milestoneMatch[2]);
|
|
1471
|
+
const allResults = { milestone: version, phases: [] };
|
|
1472
|
+
for (let p = startPhase; p <= endPhase; p++) {
|
|
1473
|
+
try {
|
|
1474
|
+
const ctx = buildCleanupContext(String(p));
|
|
1475
|
+
const todoRes = autoCloseTodos(ctx);
|
|
1476
|
+
const noteRes = autoArchiveNotes(ctx);
|
|
1477
|
+
allResults.phases.push({ phase: p, todos: todoRes, notes: noteRes });
|
|
1478
|
+
} catch (_e) { /* skip phases without SUMMARY */ }
|
|
1479
|
+
}
|
|
1480
|
+
output(allResults);
|
|
1481
|
+
} else {
|
|
1482
|
+
error('Usage: auto-cleanup --phase N | --milestone vN');
|
|
1483
|
+
}
|
|
1484
|
+
} else if (command === 'migrate') {
|
|
1485
|
+
const dryRun = args.includes('--dry-run');
|
|
1486
|
+
const force = args.includes('--force');
|
|
1487
|
+
const result = await migrate({ dryRun, force });
|
|
1488
|
+
output(result);
|
|
1489
|
+
} else if (command === 'learnings') {
|
|
1490
|
+
const subCmd = args[1];
|
|
1491
|
+
|
|
1492
|
+
if (subCmd === 'ingest') {
|
|
1493
|
+
// learnings ingest <json-file-path>
|
|
1494
|
+
const jsonFile = args[2];
|
|
1495
|
+
if (!jsonFile) { error('Usage: learnings ingest <json-file>'); process.exit(1); }
|
|
1496
|
+
const raw = fs.readFileSync(jsonFile, 'utf8');
|
|
1497
|
+
const entry = JSON.parse(raw);
|
|
1498
|
+
const result = _learningsIngest(entry);
|
|
1499
|
+
output(result);
|
|
1500
|
+
|
|
1501
|
+
} else if (subCmd === 'query') {
|
|
1502
|
+
// learnings query [--tags tag1,tag2] [--min-confidence low|medium|high] [--stack react] [--type tech-pattern]
|
|
1503
|
+
const filters = {};
|
|
1504
|
+
for (let i = 2; i < args.length; i++) {
|
|
1505
|
+
if (args[i] === '--tags' && args[i + 1]) { filters.tags = args[++i].split(',').map(t => t.trim()); }
|
|
1506
|
+
else if (args[i] === '--min-confidence' && args[i + 1]) { filters.minConfidence = args[++i]; }
|
|
1507
|
+
else if (args[i] === '--stack' && args[i + 1]) { filters.stack = args[++i]; }
|
|
1508
|
+
else if (args[i] === '--type' && args[i + 1]) { filters.type = args[++i]; }
|
|
1509
|
+
}
|
|
1510
|
+
const results = _learningsQuery(filters);
|
|
1511
|
+
output(results);
|
|
1512
|
+
|
|
1513
|
+
} else if (subCmd === 'check-thresholds') {
|
|
1514
|
+
// learnings check-thresholds — for progress-tracker to call
|
|
1515
|
+
const triggered = _checkDeferralThresholds();
|
|
1516
|
+
output(triggered);
|
|
1517
|
+
|
|
1518
|
+
} else if (subCmd === 'copy-global') {
|
|
1519
|
+
const filePath = args[2];
|
|
1520
|
+
const projectName = args[3];
|
|
1521
|
+
if (!filePath || !projectName) { error('Usage: learnings copy-global <learnings-md-path> <project-name>'); process.exit(1); }
|
|
1522
|
+
output(_copyToGlobal(filePath, projectName));
|
|
1523
|
+
|
|
1524
|
+
} else if (subCmd === 'query-global') {
|
|
1525
|
+
const filters = {};
|
|
1526
|
+
for (let i = 2; i < args.length; i++) {
|
|
1527
|
+
if (args[i] === '--tags' && args[i + 1]) { filters.tags = args[++i].split(',').map(t => t.trim()); }
|
|
1528
|
+
else if (args[i] === '--project' && args[i + 1]) { filters.project = args[++i]; }
|
|
1529
|
+
}
|
|
1530
|
+
output(_queryGlobal(filters));
|
|
1300
1531
|
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
}
|
|
1306
|
-
|
|
1532
|
+
} else {
|
|
1533
|
+
error('Usage: learnings <ingest|query|check-thresholds|copy-global|query-global>');
|
|
1534
|
+
process.exit(1);
|
|
1535
|
+
}
|
|
1536
|
+
} else if (command === 'insights') {
|
|
1537
|
+
const subCmd = args[1];
|
|
1538
|
+
if (subCmd === 'import') {
|
|
1539
|
+
const htmlPath = args[2];
|
|
1540
|
+
if (!htmlPath) { error('Usage: insights import <html-file-path> [--project <name>]'); process.exit(1); }
|
|
1541
|
+
let projectName;
|
|
1542
|
+
for (let i = 3; i < args.length; i++) {
|
|
1543
|
+
if (args[i] === '--project' && args[i + 1]) { projectName = args[++i]; }
|
|
1544
|
+
}
|
|
1545
|
+
const result = insightsImport(htmlPath, projectName);
|
|
1546
|
+
output(result);
|
|
1547
|
+
} else {
|
|
1548
|
+
error('Usage: insights <import>');
|
|
1549
|
+
process.exit(1);
|
|
1550
|
+
}
|
|
1551
|
+
} else if (command === 'verify' && subcommand === 'spot-check') {
|
|
1552
|
+
const scType = args[2];
|
|
1553
|
+
const scPath = args[3];
|
|
1554
|
+
if (!scType || !scPath) { error('Usage: verify spot-check <type> <path> (types: plan, summary, verification, quick)'); }
|
|
1555
|
+
const result = verifySpotCheck(scType, scPath);
|
|
1556
|
+
if (result.error) { process.stdout.write(JSON.stringify(result, null, 2) + '\n'); process.exit(1); }
|
|
1557
|
+
output(result);
|
|
1558
|
+
|
|
1559
|
+
} else if (command === 'verify' && subcommand === 'summary') {
|
|
1560
|
+
const spath = args[2];
|
|
1561
|
+
const cfIdx = args.indexOf('--check-files');
|
|
1562
|
+
const cf = cfIdx !== -1 ? parseInt(args[cfIdx + 1], 10) : 2;
|
|
1563
|
+
if (!spath) { error('Usage: verify summary <path> [--check-files N]'); }
|
|
1564
|
+
_cmdVerifySummary(cwd, spath, cf, false);
|
|
1565
|
+
|
|
1566
|
+
} else if (command === 'verify' && subcommand === 'plan-structure') {
|
|
1567
|
+
const ppath = args[2];
|
|
1568
|
+
if (!ppath) { error('Usage: verify plan-structure <path>'); }
|
|
1569
|
+
_cmdVerifyPlanStructure(cwd, ppath, false);
|
|
1570
|
+
|
|
1571
|
+
} else if (command === 'verify' && subcommand === 'phase-completeness') {
|
|
1572
|
+
const phase = args[2];
|
|
1573
|
+
if (!phase) { error('Usage: verify phase-completeness <phase>'); }
|
|
1574
|
+
_cmdVerifyPhaseCompleteness(cwd, phase, false);
|
|
1575
|
+
|
|
1576
|
+
} else if (command === 'verify' && subcommand === 'artifacts') {
|
|
1577
|
+
const planPath = args[2];
|
|
1578
|
+
if (!planPath) { error('Usage: verify artifacts <plan-path>'); }
|
|
1579
|
+
_cmdVerifyArtifacts(cwd, planPath, false);
|
|
1580
|
+
|
|
1581
|
+
} else if (command === 'verify' && subcommand === 'key-links') {
|
|
1582
|
+
const planPath = args[2];
|
|
1583
|
+
if (!planPath) { error('Usage: verify key-links <plan-path>'); }
|
|
1584
|
+
_cmdVerifyKeyLinks(cwd, planPath, false);
|
|
1585
|
+
|
|
1586
|
+
} else if (command === 'verify' && subcommand === 'commits') {
|
|
1587
|
+
const hashes = args.slice(2);
|
|
1588
|
+
if (!hashes.length) { error('Usage: verify commits <hash1> [hash2] ...'); }
|
|
1589
|
+
_cmdVerifyCommits(cwd, hashes, false);
|
|
1590
|
+
|
|
1591
|
+
} else if (command === 'verify' && subcommand === 'references') {
|
|
1592
|
+
const rpath = args[2];
|
|
1593
|
+
if (!rpath) { error('Usage: verify references <path>'); }
|
|
1594
|
+
_cmdVerifyReferences(cwd, rpath, false);
|
|
1595
|
+
|
|
1596
|
+
} else if (command === 'spot-check') {
|
|
1597
|
+
// spot-check <phaseSlug> <planId>
|
|
1598
|
+
// Returns JSON: { ok, summary_exists, key_files_checked, commits_present, detail }
|
|
1599
|
+
const phaseSlug = args[1];
|
|
1600
|
+
const planId = args[2];
|
|
1601
|
+
if (!phaseSlug || !planId) {
|
|
1602
|
+
error('Usage: spot-check <phaseSlug> <planId>');
|
|
1603
|
+
}
|
|
1604
|
+
output(spotCheck(phaseSlug, planId));
|
|
1605
|
+
} else if (command === 'staleness-check') {
|
|
1606
|
+
const slug = args[1];
|
|
1607
|
+
if (!slug) { error('Usage: staleness-check <phase-slug>'); process.exit(1); }
|
|
1608
|
+
output(stalenessCheck(slug));
|
|
1609
|
+
} else if (command === 'summary-gate') {
|
|
1610
|
+
const [slug, planId] = args.slice(1);
|
|
1611
|
+
if (!slug || !planId) { error('Usage: summary-gate <phase-slug> <plan-id>'); process.exit(1); }
|
|
1612
|
+
output(summaryGate(slug, planId));
|
|
1613
|
+
} else if (command === 'checkpoint') {
|
|
1614
|
+
const sub = args[1];
|
|
1615
|
+
const slug = args[2];
|
|
1616
|
+
if (sub === 'init') {
|
|
1617
|
+
const plans = args[3] || '';
|
|
1618
|
+
output(checkpointInit(slug, plans));
|
|
1619
|
+
} else if (sub === 'update') {
|
|
1620
|
+
const waveIdx = args.indexOf('--wave');
|
|
1621
|
+
const wave = waveIdx !== -1 ? parseInt(args[waveIdx + 1], 10) : 1;
|
|
1622
|
+
const resolvedIdx = args.indexOf('--resolved');
|
|
1623
|
+
const resolved = resolvedIdx !== -1 ? args[resolvedIdx + 1] : '';
|
|
1624
|
+
const shaIdx = args.indexOf('--sha');
|
|
1625
|
+
const sha = shaIdx !== -1 ? args[shaIdx + 1] : '';
|
|
1626
|
+
output(checkpointUpdate(slug, { wave, resolved, sha }));
|
|
1627
|
+
} else {
|
|
1628
|
+
error('Usage: checkpoint init|update <phase-slug> [options]'); process.exit(1);
|
|
1629
|
+
}
|
|
1630
|
+
} else if (command === 'seeds') {
|
|
1631
|
+
const sub = args[1];
|
|
1632
|
+
if (sub === 'match') {
|
|
1633
|
+
const slug = args[2];
|
|
1634
|
+
const num = args[3];
|
|
1635
|
+
if (!slug) { error('Usage: seeds match <phase-slug> <phase-number>'); process.exit(1); }
|
|
1636
|
+
output(seedsMatch(slug, num));
|
|
1637
|
+
} else {
|
|
1638
|
+
error('Usage: seeds match <phase-slug> <phase-number>'); process.exit(1);
|
|
1639
|
+
}
|
|
1640
|
+
} else if (command === 'ci-poll') {
|
|
1641
|
+
const runId = args[1];
|
|
1642
|
+
const timeoutIdx = args.indexOf('--timeout');
|
|
1643
|
+
const timeoutSecs = timeoutIdx !== -1 ? parseInt(args[timeoutIdx + 1], 10) : 300;
|
|
1644
|
+
if (!runId) { error('Usage: pbr-tools.js ci-poll <run-id> [--timeout <seconds>]'); return; }
|
|
1645
|
+
output(ciPoll(runId, timeoutSecs));
|
|
1646
|
+
} else if (command === 'rollback') {
|
|
1647
|
+
const manifestPath = args[1];
|
|
1648
|
+
if (!manifestPath) { error('Usage: pbr-tools.js rollback <manifest-path>'); return; }
|
|
1649
|
+
output(rollbackPlan(manifestPath));
|
|
1650
|
+
} else if (command === 'session') {
|
|
1651
|
+
const sub = args[1];
|
|
1652
|
+
// Extract --session-id flag from remaining args
|
|
1653
|
+
const sessionIdIdx = args.indexOf('--session-id');
|
|
1654
|
+
const sessionId = sessionIdIdx !== -1 ? args[sessionIdIdx + 1] : null;
|
|
1655
|
+
// Filter out --session-id and its value from positional args
|
|
1656
|
+
const positional = sessionIdIdx !== -1
|
|
1657
|
+
? args.filter((_a, i) => i !== sessionIdIdx && i !== sessionIdIdx + 1)
|
|
1658
|
+
: args;
|
|
1659
|
+
const key = positional[2];
|
|
1660
|
+
const value = positional[3];
|
|
1661
|
+
const dir = planningDir;
|
|
1662
|
+
if (sub === 'get') {
|
|
1663
|
+
if (!key) { error('Usage: pbr-tools.js session get <key> [--session-id <id>]'); return; }
|
|
1664
|
+
if (!SESSION_ALLOWED_KEYS.includes(key)) { error(`Unknown session key: ${key}. Allowed: ${SESSION_ALLOWED_KEYS.join(', ')}`); return; }
|
|
1665
|
+
const data = sessionLoad(dir, sessionId);
|
|
1666
|
+
const val = Object.prototype.hasOwnProperty.call(data, key) ? data[key] : null;
|
|
1667
|
+
output({ key, value: val });
|
|
1668
|
+
} else if (sub === 'set') {
|
|
1669
|
+
if (!key || value === undefined) { error('Usage: pbr-tools.js session set <key> <value> [--session-id <id>]'); return; }
|
|
1670
|
+
if (!SESSION_ALLOWED_KEYS.includes(key)) { error(`Unknown session key: ${key}. Allowed: ${SESSION_ALLOWED_KEYS.join(', ')}`); return; }
|
|
1671
|
+
// Coerce numeric strings
|
|
1672
|
+
let coerced = value;
|
|
1673
|
+
if (/^\d+$/.test(value)) coerced = parseInt(value, 10);
|
|
1674
|
+
else if (value === 'null') coerced = null;
|
|
1675
|
+
const result = sessionSave(dir, { [key]: coerced }, sessionId);
|
|
1676
|
+
if (!result.success) { error(result.error || 'Failed to save session'); return; }
|
|
1677
|
+
output({ ok: true });
|
|
1678
|
+
} else if (sub === 'clear') {
|
|
1679
|
+
if (key) {
|
|
1680
|
+
// Clear a specific key — set to null
|
|
1681
|
+
if (!SESSION_ALLOWED_KEYS.includes(key)) { error(`Unknown session key: ${key}. Allowed: ${SESSION_ALLOWED_KEYS.join(', ')}`); return; }
|
|
1682
|
+
const result = sessionSave(dir, { [key]: null }, sessionId);
|
|
1683
|
+
if (!result.success) { error(result.error || 'Failed to clear session key'); return; }
|
|
1684
|
+
} else {
|
|
1685
|
+
// Clear entire session file
|
|
1686
|
+
const sessionPath = sessionId
|
|
1687
|
+
? resolveSessionPath(dir, '.session.json', sessionId)
|
|
1688
|
+
: path.join(dir, '.session.json');
|
|
1689
|
+
try { if (fs.existsSync(sessionPath)) fs.unlinkSync(sessionPath); } catch (e) { error(e.message); return; }
|
|
1690
|
+
}
|
|
1691
|
+
output({ ok: true });
|
|
1692
|
+
} else if (sub === 'dump') {
|
|
1693
|
+
const data = sessionLoad(dir, sessionId);
|
|
1694
|
+
output(data);
|
|
1695
|
+
} else {
|
|
1696
|
+
error('Usage: pbr-tools.js session get|set|clear|dump <key> [value] [--session-id <id>]');
|
|
1697
|
+
}
|
|
1698
|
+
} else if (command === 'context-triage') {
|
|
1699
|
+
const options = {};
|
|
1700
|
+
const agentsIdx = args.indexOf('--agents-done');
|
|
1701
|
+
if (agentsIdx !== -1) options.agentsDone = parseInt(args[agentsIdx + 1], 10);
|
|
1702
|
+
const plansIdx = args.indexOf('--plans-total');
|
|
1703
|
+
if (plansIdx !== -1) options.plansTotal = parseInt(args[plansIdx + 1], 10);
|
|
1704
|
+
const stepIdx = args.indexOf('--step');
|
|
1705
|
+
if (stepIdx !== -1) options.currentStep = args[stepIdx + 1];
|
|
1706
|
+
output(contextTriage(options));
|
|
1707
|
+
} else if (command === 'reference') {
|
|
1708
|
+
const name = args[1];
|
|
1709
|
+
if (!name) error('Usage: pbr-tools.js reference <name> [--section <heading>] [--list]');
|
|
1710
|
+
const listFlag = args.includes('--list');
|
|
1711
|
+
const sectionIdx = args.indexOf('--section');
|
|
1712
|
+
const section = sectionIdx !== -1 ? args.slice(sectionIdx + 1).join(' ') : null;
|
|
1713
|
+
output(referenceGet(name, { section: section, list: listFlag }));
|
|
1714
|
+
} else if (command === 'milestone-stats') {
|
|
1715
|
+
const version = args[1];
|
|
1716
|
+
if (!version) error('Usage: pbr-tools.js milestone-stats <version>');
|
|
1717
|
+
output(milestoneStats(version));
|
|
1718
|
+
} else if (command === 'validate-project') {
|
|
1719
|
+
output(validateProject());
|
|
1720
|
+
} else if (command === 'skill-section') {
|
|
1721
|
+
// skill-section --list <skill> — list all headings
|
|
1722
|
+
if (args[1] === '--list') {
|
|
1723
|
+
const skillName = args[2];
|
|
1724
|
+
if (!skillName) { error('Usage: pbr-tools.js skill-section --list <skill>'); return; }
|
|
1725
|
+
const listResult = listSkillHeadings(skillName);
|
|
1726
|
+
output(listResult);
|
|
1727
|
+
if (listResult.error) process.exit(1);
|
|
1728
|
+
} else {
|
|
1729
|
+
// skill-section <skill> <section...>
|
|
1730
|
+
const skillName = args[1];
|
|
1731
|
+
const sectionQuery = args.slice(2).join(' ');
|
|
1732
|
+
if (!skillName || !sectionQuery) {
|
|
1733
|
+
error('Usage: pbr-tools.js skill-section <skill> <section>');
|
|
1734
|
+
return;
|
|
1735
|
+
}
|
|
1736
|
+
const secResult = skillSectionGet(skillName, sectionQuery);
|
|
1737
|
+
output(secResult);
|
|
1738
|
+
if (secResult.error) process.exit(1);
|
|
1739
|
+
}
|
|
1740
|
+
} else if (command === 'step-verify') {
|
|
1741
|
+
// step-verify <skill> <step> <checklist-json>
|
|
1742
|
+
const skill = args[1];
|
|
1743
|
+
const step = args[2];
|
|
1744
|
+
const checklistStr = args[3] || '[]';
|
|
1745
|
+
let checklist;
|
|
1746
|
+
try {
|
|
1747
|
+
checklist = JSON.parse(checklistStr);
|
|
1748
|
+
} catch (_e) {
|
|
1749
|
+
output({ error: 'Invalid checklist JSON' });
|
|
1750
|
+
process.exit(1);
|
|
1751
|
+
return;
|
|
1752
|
+
}
|
|
1753
|
+
const svPlanningDir = planningDir;
|
|
1754
|
+
const svContext = {
|
|
1755
|
+
planningDir: svPlanningDir,
|
|
1756
|
+
phaseSlug: process.env.PBR_PHASE_SLUG || '',
|
|
1757
|
+
planId: process.env.PBR_PLAN_ID || ''
|
|
1758
|
+
};
|
|
1759
|
+
const svResult = _stepVerify(skill, step, checklist, svContext);
|
|
1760
|
+
output(svResult);
|
|
1761
|
+
if (svResult.error || svResult.all_passed === false) process.exit(1);
|
|
1762
|
+
} else if (command === 'build-preview') {
|
|
1763
|
+
const phaseSlug = args[1];
|
|
1764
|
+
if (!phaseSlug) {
|
|
1765
|
+
error('Usage: pbr-tools.js build-preview <phase-slug>');
|
|
1766
|
+
return;
|
|
1767
|
+
}
|
|
1768
|
+
const previewPlanningDir = planningDir;
|
|
1769
|
+
const previewPluginRoot = path.resolve(__dirname, '..');
|
|
1770
|
+
const result = _buildPreview(phaseSlug, {}, previewPlanningDir, previewPluginRoot);
|
|
1771
|
+
if (result && result.error) {
|
|
1772
|
+
output(result);
|
|
1773
|
+
process.exit(1);
|
|
1774
|
+
}
|
|
1775
|
+
output(result);
|
|
1776
|
+
} else if (command === 'suggest-alternatives') {
|
|
1777
|
+
const errorType = args[1];
|
|
1778
|
+
const altPlanningDir = planningDir;
|
|
1779
|
+
if (errorType === 'phase-not-found') {
|
|
1780
|
+
output(_phaseAlternatives(args[2] || '', altPlanningDir));
|
|
1781
|
+
} else if (errorType === 'missing-prereq') {
|
|
1782
|
+
output(_prereqAlternatives(args[2] || '', altPlanningDir));
|
|
1783
|
+
} else if (errorType === 'config-invalid') {
|
|
1784
|
+
output(_configAlternatives(args[2] || '', args[3] || '', altPlanningDir));
|
|
1785
|
+
} else {
|
|
1786
|
+
output({ error: 'Unknown error type. Valid: phase-not-found, missing-prereq, config-invalid' });
|
|
1787
|
+
process.exit(1);
|
|
1788
|
+
}
|
|
1789
|
+
} else if (command === 'claim' && subcommand === 'acquire') {
|
|
1790
|
+
const phaseSlug = args[2];
|
|
1791
|
+
const sidIdx = args.indexOf('--session-id');
|
|
1792
|
+
const sessionId = sidIdx !== -1 ? args[sidIdx + 1] : null;
|
|
1793
|
+
const skillIdx = args.indexOf('--skill');
|
|
1794
|
+
const skill = skillIdx !== -1 ? args[skillIdx + 1] : 'unknown';
|
|
1795
|
+
if (!phaseSlug || !sessionId) {
|
|
1796
|
+
error('Usage: pbr-tools.js claim acquire <phase-slug> --session-id <id> --skill <name>');
|
|
1797
|
+
}
|
|
1798
|
+
output(claimAcquire(phaseSlug, sessionId, skill));
|
|
1799
|
+
} else if (command === 'claim' && subcommand === 'release') {
|
|
1800
|
+
const phaseSlug = args[2];
|
|
1801
|
+
const sidIdx = args.indexOf('--session-id');
|
|
1802
|
+
const sessionId = sidIdx !== -1 ? args[sidIdx + 1] : null;
|
|
1803
|
+
if (!phaseSlug || !sessionId) {
|
|
1804
|
+
error('Usage: pbr-tools.js claim release <phase-slug> --session-id <id>');
|
|
1805
|
+
}
|
|
1806
|
+
output(claimRelease(phaseSlug, sessionId));
|
|
1807
|
+
} else if (command === 'claim' && subcommand === 'list') {
|
|
1808
|
+
output(claimList());
|
|
1809
|
+
} else if (command === 'status' && subcommand === 'render') {
|
|
1810
|
+
output(_statusRender(planningDir));
|
|
1811
|
+
} else if (command === 'suggest-next') {
|
|
1812
|
+
output(_suggestNext(planningDir));
|
|
1813
|
+
} else if (command === 'tmux' && subcommand === 'detect') {
|
|
1814
|
+
const tmuxEnv = process.env.TMUX || '';
|
|
1815
|
+
const result = {
|
|
1816
|
+
in_tmux: !!tmuxEnv,
|
|
1817
|
+
pane: process.env.TMUX_PANE || null,
|
|
1818
|
+
session: null
|
|
1819
|
+
};
|
|
1820
|
+
if (tmuxEnv) {
|
|
1821
|
+
// TMUX env format: /socket/path,PID,INDEX
|
|
1822
|
+
const parts = tmuxEnv.split(',');
|
|
1823
|
+
if (parts.length >= 1) {
|
|
1824
|
+
result.session = parts[0].split('/').pop() || null;
|
|
1825
|
+
}
|
|
1826
|
+
}
|
|
1827
|
+
output(result);
|
|
1828
|
+
|
|
1829
|
+
// ─── Quick Task Operations ─────────────────────────────────────────────────
|
|
1830
|
+
} else if (command === 'quick' && subcommand === 'init') {
|
|
1831
|
+
const desc = args.slice(2).join(' ') || '';
|
|
1832
|
+
const quickInitMod = require('./lib/quick-init.js');
|
|
1833
|
+
output(quickInitMod.quickInit(desc, planningDir));
|
|
1834
|
+
|
|
1835
|
+
// ─── Slug Generation ─────────────────────────────────────────────────────
|
|
1836
|
+
} else if (command === 'generate-slug' || command === 'slug-generate') {
|
|
1837
|
+
const text = args.slice(1).join(' ');
|
|
1838
|
+
if (!text) error('Usage: pbr-tools.js generate-slug <text>');
|
|
1839
|
+
const slug = text.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '');
|
|
1840
|
+
output({ slug });
|
|
1841
|
+
|
|
1842
|
+
// ─── Parse Args ────────────────────────────────────────────────────────────
|
|
1843
|
+
} else if (command === 'parse-args') {
|
|
1844
|
+
const type = args[1];
|
|
1845
|
+
const rawInput = args.slice(2).join(' ');
|
|
1846
|
+
if (!type) error('Usage: pbr-tools.js parse-args <type> <args>\nTypes: plan, quick');
|
|
1847
|
+
const { parseArgs } = require('./lib/parse-args');
|
|
1848
|
+
output(parseArgs(type, rawInput));
|
|
1849
|
+
|
|
1850
|
+
// ─── Status Fingerprint ──────────────────────────────────────────────────
|
|
1851
|
+
} else if (command === 'status' && subcommand === 'fingerprint') {
|
|
1852
|
+
const crypto = require('crypto');
|
|
1853
|
+
const files = {};
|
|
1854
|
+
let combinedContent = '';
|
|
1855
|
+
for (const name of ['STATE.md', 'ROADMAP.md']) {
|
|
1856
|
+
const filePath = path.join(planningDir, name);
|
|
1857
|
+
try {
|
|
1858
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
1859
|
+
const stat = fs.statSync(filePath);
|
|
1860
|
+
const hash = crypto.createHash('sha256').update(content).digest('hex').slice(0, 8);
|
|
1861
|
+
files[name] = {
|
|
1862
|
+
hash,
|
|
1863
|
+
mtime: stat.mtime.toISOString(),
|
|
1864
|
+
lines: content.split('\n').length
|
|
1865
|
+
};
|
|
1866
|
+
combinedContent += content;
|
|
1867
|
+
} catch {
|
|
1868
|
+
files[name] = { hash: null, mtime: null, lines: 0 };
|
|
1869
|
+
}
|
|
1870
|
+
}
|
|
1871
|
+
const fingerprint = combinedContent
|
|
1872
|
+
? crypto.createHash('sha256').update(combinedContent).digest('hex').slice(0, 8)
|
|
1873
|
+
: null;
|
|
1874
|
+
let phaseDirs = 0;
|
|
1875
|
+
const phasesDir = path.join(planningDir, 'phases');
|
|
1876
|
+
try {
|
|
1877
|
+
const entries = fs.readdirSync(phasesDir, { withFileTypes: true });
|
|
1878
|
+
phaseDirs = entries.filter(e => e.isDirectory()).length;
|
|
1879
|
+
} catch { /* no phases dir */ }
|
|
1880
|
+
output({
|
|
1881
|
+
fingerprint,
|
|
1882
|
+
files,
|
|
1883
|
+
phase_dirs: phaseDirs,
|
|
1884
|
+
timestamp: new Date().toISOString()
|
|
1885
|
+
});
|
|
1886
|
+
|
|
1887
|
+
} else if (command === 'quick-status') {
|
|
1888
|
+
const result = quickStatus();
|
|
1889
|
+
process.stdout.write(result.text + '\n');
|
|
1890
|
+
|
|
1891
|
+
} else if (command === 'ci-fix') {
|
|
1892
|
+
const dryRun = args.includes('--dry-run');
|
|
1893
|
+
const maxIterIdx = args.indexOf('--max-iterations');
|
|
1894
|
+
const maxIterations = maxIterIdx !== -1 ? parseInt(args[maxIterIdx + 1], 10) : 3;
|
|
1895
|
+
output(ciFix({ dryRun, maxIterations }));
|
|
1896
|
+
|
|
1897
|
+
// ─── Incidents ───────────────────────────────────────────────────────────
|
|
1898
|
+
} else if (command === 'incidents') {
|
|
1899
|
+
const sub = args[1];
|
|
1900
|
+
if (sub === 'list') {
|
|
1901
|
+
const limitIdx = args.indexOf('--limit');
|
|
1902
|
+
const limit = limitIdx !== -1 ? parseInt(args[limitIdx + 1], 10) : 50;
|
|
1903
|
+
output(incidentsList({ limit }));
|
|
1904
|
+
} else if (sub === 'summary') {
|
|
1905
|
+
output(incidentsSummary());
|
|
1906
|
+
} else if (sub === 'query') {
|
|
1907
|
+
const filter = {};
|
|
1908
|
+
for (let i = 2; i < args.length; i++) {
|
|
1909
|
+
if (args[i] === '--type' && args[i + 1]) { filter.type = args[++i]; }
|
|
1910
|
+
else if (args[i] === '--severity' && args[i + 1]) { filter.severity = args[++i]; }
|
|
1911
|
+
else if (args[i] === '--source' && args[i + 1]) { filter.source = args[++i]; }
|
|
1912
|
+
else if (args[i] === '--last' && args[i + 1]) { filter.last = args[++i]; }
|
|
1913
|
+
}
|
|
1914
|
+
output(incidentsQuery(filter));
|
|
1915
|
+
} else {
|
|
1916
|
+
error('Usage: incidents <list|summary|query> [--type T] [--severity S] [--source S] [--last Nd|Nh] [--limit N]');
|
|
1917
|
+
}
|
|
1307
1918
|
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1919
|
+
} else if (command === 'nk') {
|
|
1920
|
+
// Lazy require to avoid loading for unrelated commands
|
|
1921
|
+
const nkLib = require(path.join(__dirname, 'lib', 'negative-knowledge'));
|
|
1922
|
+
|
|
1923
|
+
if (subcommand === 'record') {
|
|
1924
|
+
const titleIdx = args.indexOf('--title');
|
|
1925
|
+
const categoryIdx = args.indexOf('--category');
|
|
1926
|
+
const filesIdx = args.indexOf('--files');
|
|
1927
|
+
const triedIdx = args.indexOf('--tried');
|
|
1928
|
+
const failedIdx = args.indexOf('--failed');
|
|
1929
|
+
const workedIdx = args.indexOf('--worked');
|
|
1930
|
+
const phaseIdx = args.indexOf('--phase');
|
|
1931
|
+
|
|
1932
|
+
const title = titleIdx !== -1 ? args[titleIdx + 1] : null;
|
|
1933
|
+
const category = categoryIdx !== -1 ? args[categoryIdx + 1] : null;
|
|
1934
|
+
const tried = triedIdx !== -1 ? args[triedIdx + 1] : null;
|
|
1935
|
+
const failed = failedIdx !== -1 ? args[failedIdx + 1] : null;
|
|
1936
|
+
|
|
1937
|
+
if (!title || !category || !tried || !failed) {
|
|
1938
|
+
error('Usage: pbr-tools.js nk record --title "..." --category "build-failure" --files "f1,f2" --tried "..." --failed "..."\nRequired: --title, --category, --tried, --failed\nCategories: build-failure, verification-gap, plan-revision, debug-finding');
|
|
1939
|
+
}
|
|
1314
1940
|
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
}
|
|
1941
|
+
const filesInvolved = filesIdx !== -1 ? (args[filesIdx + 1] || '').split(',').filter(Boolean) : [];
|
|
1942
|
+
const whatWorked = workedIdx !== -1 ? args[workedIdx + 1] : '';
|
|
1943
|
+
const phase = phaseIdx !== -1 ? args[phaseIdx + 1] : '';
|
|
1319
1944
|
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1945
|
+
const result = nkLib.recordFailure(planningDir, {
|
|
1946
|
+
title, category, filesInvolved,
|
|
1947
|
+
whatTried: tried, whyFailed: failed,
|
|
1948
|
+
whatWorked, phase
|
|
1949
|
+
});
|
|
1950
|
+
output({ ok: true, path: result.path, slug: result.slug });
|
|
1951
|
+
|
|
1952
|
+
} else if (subcommand === 'list') {
|
|
1953
|
+
const catIdx = args.indexOf('--category');
|
|
1954
|
+
const phIdx = args.indexOf('--phase');
|
|
1955
|
+
const stIdx = args.indexOf('--status');
|
|
1956
|
+
const filters = {};
|
|
1957
|
+
if (catIdx !== -1) filters.category = args[catIdx + 1];
|
|
1958
|
+
if (phIdx !== -1) filters.phase = args[phIdx + 1];
|
|
1959
|
+
if (stIdx !== -1) filters.status = args[stIdx + 1];
|
|
1960
|
+
output(nkLib.listFailures(planningDir, filters));
|
|
1961
|
+
|
|
1962
|
+
} else if (subcommand === 'resolve') {
|
|
1963
|
+
const slug = args[2];
|
|
1964
|
+
if (!slug) {
|
|
1965
|
+
error('Usage: pbr-tools.js nk resolve <slug>');
|
|
1966
|
+
}
|
|
1967
|
+
nkLib.resolveEntry(planningDir, slug);
|
|
1968
|
+
output({ ok: true });
|
|
1324
1969
|
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
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';
|
|
1970
|
+
} else {
|
|
1971
|
+
error('Usage: pbr-tools.js nk <record|list|resolve>\n nk record --title "..." --category "..." --files "f1,f2" --tried "..." --failed "..."\n nk list [--category X] [--phase X] [--status X]\n nk resolve <slug>');
|
|
1972
|
+
}
|
|
1336
1973
|
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1974
|
+
} else if (command === 'data') {
|
|
1975
|
+
const sub = args[1];
|
|
1976
|
+
if (sub === 'status') {
|
|
1977
|
+
output(dataStatus());
|
|
1978
|
+
} else if (sub === 'prune') {
|
|
1979
|
+
const beforeIdx = args.indexOf('--before');
|
|
1980
|
+
const before = beforeIdx !== -1 ? args[beforeIdx + 1] : null;
|
|
1981
|
+
const dryRun = args.includes('--dry-run');
|
|
1982
|
+
if (!before) {
|
|
1983
|
+
error('Usage: pbr-tools.js data prune --before <ISO-date> [--dry-run]');
|
|
1984
|
+
}
|
|
1985
|
+
output(dataPrune({ before, dryRun }));
|
|
1986
|
+
} else {
|
|
1987
|
+
error('Usage: pbr-tools.js data <status|prune>\n data status — freshness report for research/, intel/, codebase/\n data prune --before <ISO-date> [--dry-run] — archive stale files');
|
|
1988
|
+
}
|
|
1340
1989
|
|
|
1341
|
-
//
|
|
1342
|
-
if (
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1990
|
+
// --- Graph Operations ---
|
|
1991
|
+
} else if (command === 'graph') {
|
|
1992
|
+
const graphCli = require('./lib/graph-cli');
|
|
1993
|
+
graphCli.handleGraphCommand(subcommand, args, planningDir, cwd, output, error);
|
|
1994
|
+
|
|
1995
|
+
// --- Audit Operations ---
|
|
1996
|
+
} else if (command === 'audit' && subcommand === 'plan-checks') {
|
|
1997
|
+
const { auditPlanChecks } = require('./lib/audit');
|
|
1998
|
+
const lastIdx = args.indexOf('--last');
|
|
1999
|
+
const last = lastIdx !== -1 ? parseInt(args[lastIdx + 1], 10) : 30;
|
|
2000
|
+
output(auditPlanChecks({ last }));
|
|
2001
|
+
|
|
2002
|
+
// --- Hooks Perf Report ---
|
|
2003
|
+
} else if (command === 'hooks' && subcommand === 'perf') {
|
|
2004
|
+
const { summarizeHookPerf, formatPerfTable, loadPerfEntries } = require('./lib/perf');
|
|
2005
|
+
const lastIdx = args.indexOf('--last');
|
|
2006
|
+
const last = lastIdx !== -1 ? parseInt(args[lastIdx + 1], 10) : undefined;
|
|
2007
|
+
const jsonFlag = args.includes('--json');
|
|
2008
|
+
const entries = loadPerfEntries(planningDir, { last });
|
|
2009
|
+
const summary = summarizeHookPerf(entries);
|
|
2010
|
+
if (jsonFlag) {
|
|
2011
|
+
output(summary);
|
|
2012
|
+
} else {
|
|
2013
|
+
const table = formatPerfTable(summary);
|
|
2014
|
+
process.stdout.write(table + '\n');
|
|
2015
|
+
process.stdout.write(`\nEntries analyzed: ${entries.length}\n`);
|
|
1347
2016
|
}
|
|
1348
|
-
}
|
|
1349
2017
|
|
|
1350
|
-
//
|
|
1351
|
-
|
|
2018
|
+
// --- Spec Operations ---
|
|
2019
|
+
} else if (command === 'spec') {
|
|
2020
|
+
handleSpec(args, planningDir, cwd, output, error);
|
|
1352
2021
|
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
try {
|
|
1357
|
-
if (fs.existsSync(bakPath)) {
|
|
1358
|
-
fs.copyFileSync(bakPath, filePath);
|
|
1359
|
-
}
|
|
1360
|
-
} catch (_restoreErr) {
|
|
1361
|
-
// Restore also failed — nothing more we can do
|
|
1362
|
-
}
|
|
2022
|
+
// --- Help Operations ---
|
|
2023
|
+
} else if (command === 'help') {
|
|
2024
|
+
output(helpListCmd());
|
|
1363
2025
|
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
if (
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
// Best-effort cleanup
|
|
1371
|
-
}
|
|
2026
|
+
} else if (command === 'skill-metadata') {
|
|
2027
|
+
const skillName = args[1];
|
|
2028
|
+
if (!skillName) { error('Usage: pbr-tools.js skill-metadata <name>'); return; }
|
|
2029
|
+
const result = skillMetadataCmd(skillName);
|
|
2030
|
+
output(result);
|
|
2031
|
+
if (result.error) process.exit(1);
|
|
1372
2032
|
|
|
1373
|
-
|
|
2033
|
+
} else {
|
|
2034
|
+
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, verify summary|plan-structure|phase-completeness|artifacts|key-links|commits|references|spot-check, migrate [--dry-run] [--force], init execute-phase|plan-phase|quick|verify-work|resume|progress|map-codebase, 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, incidents list|summary|query, nk record|list|resolve, data status|prune, graph build|query|impact|stats, hooks perf [--last N] [--json], spec parse|diff|reverse|impact, 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, help, skill-metadata <name>`);
|
|
2035
|
+
}
|
|
2036
|
+
} catch (e) {
|
|
2037
|
+
error(e.message);
|
|
1374
2038
|
}
|
|
1375
2039
|
}
|
|
1376
2040
|
|
|
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 };
|
|
2041
|
+
if (require.main === module || process.argv[1] === __filename) { main().catch(err => { process.stderr.write(err.message + '\n'); process.exit(1); }); }
|
|
2042
|
+
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, incidentsList, incidentsQuery, incidentsSummary, helpListCmd, skillMetadataCmd, initMapCodebase };
|
|
2043
|
+
// NOTE: validateProject, phaseAdd, phaseRemove, phaseList were previously CLI-only (not exported).
|
|
2044
|
+
// They are now exported for testability. This is additive and backwards-compatible.
|