@pareto4/pp-core 1.5.0
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/.claude-plugin/plugin.json +23 -0
- package/GEMINI.md +53 -0
- package/LICENSE +22 -0
- package/README.md +100 -0
- package/agents/pp-advisor-researcher.md +108 -0
- package/agents/pp-ai-researcher.md +114 -0
- package/agents/pp-assumptions-analyzer.md +105 -0
- package/agents/pp-code-fixer.md +669 -0
- package/agents/pp-code-reviewer.md +387 -0
- package/agents/pp-codebase-mapper.md +853 -0
- package/agents/pp-debug-session-manager.md +315 -0
- package/agents/pp-debugger.md +1453 -0
- package/agents/pp-doc-classifier.md +168 -0
- package/agents/pp-doc-synthesizer.md +204 -0
- package/agents/pp-doc-verifier.md +217 -0
- package/agents/pp-doc-writer.md +616 -0
- package/agents/pp-domain-researcher.md +147 -0
- package/agents/pp-eval-auditor.md +191 -0
- package/agents/pp-eval-planner.md +154 -0
- package/agents/pp-executor.md +808 -0
- package/agents/pp-framework-selector.md +160 -0
- package/agents/pp-integration-checker.md +470 -0
- package/agents/pp-intel-updater.md +339 -0
- package/agents/pp-mempalace-curator.md +47 -0
- package/agents/pp-nyquist-auditor.md +204 -0
- package/agents/pp-pattern-mapper.md +335 -0
- package/agents/pp-phase-researcher.md +867 -0
- package/agents/pp-plan-checker.md +988 -0
- package/agents/pp-planner.md +1052 -0
- package/agents/pp-project-researcher.md +612 -0
- package/agents/pp-research-synthesizer.md +260 -0
- package/agents/pp-roadmapper.md +741 -0
- package/agents/pp-security-auditor.md +156 -0
- package/agents/pp-ui-auditor.md +495 -0
- package/agents/pp-ui-checker.md +309 -0
- package/agents/pp-ui-researcher.md +375 -0
- package/agents/pp-user-profiler.md +171 -0
- package/agents/pp-verifier.md +967 -0
- package/assets/gsd-logo-2000-transparent.png +0 -0
- package/assets/gsd-logo-2000-transparent.svg +17 -0
- package/assets/gsd-logo-2000.png +0 -0
- package/assets/gsd-logo-2000.svg +21 -0
- package/assets/terminal.svg +68 -0
- package/bin/install.js +12370 -0
- package/bin/lib/ui-safety-gate.cjs +107 -0
- package/commands/pp-add-tests.md +42 -0
- package/commands/pp-ai-integration-phase.md +37 -0
- package/commands/pp-audit-fix.md +34 -0
- package/commands/pp-audit-milestone.md +37 -0
- package/commands/pp-audit-uat.md +24 -0
- package/commands/pp-autonomous.md +51 -0
- package/commands/pp-capture.md +62 -0
- package/commands/pp-cleanup.md +24 -0
- package/commands/pp-code-review.md +59 -0
- package/commands/pp-complete-milestone.md +143 -0
- package/commands/pp-config.md +56 -0
- package/commands/pp-debug.md +52 -0
- package/commands/pp-discovery.md +34 -0
- package/commands/pp-discuss-phase.md +77 -0
- package/commands/pp-docs-update.md +49 -0
- package/commands/pp-eval-review.md +33 -0
- package/commands/pp-execute-phase.md +65 -0
- package/commands/pp-explore.md +27 -0
- package/commands/pp-extract-learnings.md +23 -0
- package/commands/pp-fast.md +31 -0
- package/commands/pp-forensics.md +57 -0
- package/commands/pp-graphify.md +204 -0
- package/commands/pp-health.md +31 -0
- package/commands/pp-help.md +28 -0
- package/commands/pp-import.md +45 -0
- package/commands/pp-inbox.md +39 -0
- package/commands/pp-ingest-docs.md +42 -0
- package/commands/pp-manager.md +45 -0
- package/commands/pp-map-codebase.md +83 -0
- package/commands/pp-mempalace-capture.md +71 -0
- package/commands/pp-mempalace-recall.md +102 -0
- package/commands/pp-milestone-summary.md +51 -0
- package/commands/pp-mvp-phase.md +45 -0
- package/commands/pp-new-milestone.md +45 -0
- package/commands/pp-new-project.md +47 -0
- package/commands/pp-ns-context.md +25 -0
- package/commands/pp-ns-ideate.md +24 -0
- package/commands/pp-ns-manage.md +36 -0
- package/commands/pp-ns-project.md +27 -0
- package/commands/pp-ns-review.md +29 -0
- package/commands/pp-ns-workflow.md +35 -0
- package/commands/pp-pause-work.md +43 -0
- package/commands/pp-phase.md +56 -0
- package/commands/pp-plan-phase.md +63 -0
- package/commands/pp-plan-review-convergence.md +60 -0
- package/commands/pp-pr-branch.md +26 -0
- package/commands/pp-profile-user.md +46 -0
- package/commands/pp-progress.md +49 -0
- package/commands/pp-quick.md +174 -0
- package/commands/pp-resume-work.md +30 -0
- package/commands/pp-review-backlog.md +63 -0
- package/commands/pp-review.md +42 -0
- package/commands/pp-secure-phase.md +36 -0
- package/commands/pp-settings.md +29 -0
- package/commands/pp-ship.md +24 -0
- package/commands/pp-sketch.md +60 -0
- package/commands/pp-spec-phase.md +63 -0
- package/commands/pp-spike.md +57 -0
- package/commands/pp-stats.md +20 -0
- package/commands/pp-surface.md +162 -0
- package/commands/pp-thread.md +24 -0
- package/commands/pp-ui-phase.md +35 -0
- package/commands/pp-ui-review.md +33 -0
- package/commands/pp-ultraplan-phase.md +34 -0
- package/commands/pp-undo.md +35 -0
- package/commands/pp-update.md +49 -0
- package/commands/pp-validate-phase.md +36 -0
- package/commands/pp-verify-work.md +39 -0
- package/commands/pp-workspace.md +52 -0
- package/commands/pp-workstreams.md +70 -0
- package/gemini-extension.json +6 -0
- package/hooks/dist/lib/git-cmd.js +150 -0
- package/hooks/dist/lib/pp-graphify-rebuild.sh +65 -0
- package/hooks/dist/managed-hooks-registry.cjs +39 -0
- package/hooks/dist/pp-check-update-worker.js +108 -0
- package/hooks/dist/pp-check-update.js +66 -0
- package/hooks/dist/pp-config-reload.js +133 -0
- package/hooks/dist/pp-context-monitor.js +196 -0
- package/hooks/dist/pp-cursor-post-tool.js +75 -0
- package/hooks/dist/pp-cursor-session-start.js +52 -0
- package/hooks/dist/pp-ensure-canonical-path.js +305 -0
- package/hooks/dist/pp-graphify-update.sh +158 -0
- package/hooks/dist/pp-phase-boundary.sh +47 -0
- package/hooks/dist/pp-prompt-guard.js +97 -0
- package/hooks/dist/pp-read-guard.js +101 -0
- package/hooks/dist/pp-read-injection-scanner.js +203 -0
- package/hooks/dist/pp-session-state.sh +59 -0
- package/hooks/dist/pp-statusline.js +566 -0
- package/hooks/dist/pp-update-banner.js +138 -0
- package/hooks/dist/pp-validate-commit.sh +57 -0
- package/hooks/dist/pp-workflow-guard.js +167 -0
- package/hooks/dist/pp-worktree-path-guard.js +185 -0
- package/hooks/hooks.json +70 -0
- package/hooks/lib/git-cmd.js +150 -0
- package/hooks/lib/pp-graphify-rebuild.sh +65 -0
- package/hooks/managed-hooks-registry.cjs +39 -0
- package/hooks/pp-check-update-worker.js +108 -0
- package/hooks/pp-check-update.js +66 -0
- package/hooks/pp-config-reload.js +133 -0
- package/hooks/pp-context-monitor.js +196 -0
- package/hooks/pp-cursor-post-tool.js +75 -0
- package/hooks/pp-cursor-session-start.js +52 -0
- package/hooks/pp-ensure-canonical-path.js +305 -0
- package/hooks/pp-graphify-update.sh +158 -0
- package/hooks/pp-phase-boundary.sh +47 -0
- package/hooks/pp-prompt-guard.js +97 -0
- package/hooks/pp-read-guard.js +101 -0
- package/hooks/pp-read-injection-scanner.js +203 -0
- package/hooks/pp-session-state.sh +59 -0
- package/hooks/pp-statusline.js +566 -0
- package/hooks/pp-update-banner.js +138 -0
- package/hooks/pp-validate-commit.sh +57 -0
- package/hooks/pp-workflow-guard.js +167 -0
- package/hooks/pp-worktree-path-guard.js +185 -0
- package/package.json +125 -0
- package/pp-core/bin/check-latest-version.cjs +161 -0
- package/pp-core/bin/lib/active-workstream-store.cjs +297 -0
- package/pp-core/bin/lib/adr-parser.cjs +399 -0
- package/pp-core/bin/lib/agent-command-router.cjs +68 -0
- package/pp-core/bin/lib/agent-install-check.cjs +143 -0
- package/pp-core/bin/lib/artifacts.cjs +51 -0
- package/pp-core/bin/lib/audit-command-router.cjs +61 -0
- package/pp-core/bin/lib/audit.cjs +743 -0
- package/pp-core/bin/lib/capability-activation.cjs +113 -0
- package/pp-core/bin/lib/capability-registry.cjs +3727 -0
- package/pp-core/bin/lib/capability-state.cjs +497 -0
- package/pp-core/bin/lib/capability-writer.cjs +355 -0
- package/pp-core/bin/lib/check-command-router.cjs +800 -0
- package/pp-core/bin/lib/cjs-command-router-adapter.cjs +81 -0
- package/pp-core/bin/lib/cli-exit.cjs +61 -0
- package/pp-core/bin/lib/clock.cjs +95 -0
- package/pp-core/bin/lib/clusters.cjs +131 -0
- package/pp-core/bin/lib/code-review-flags.cjs +59 -0
- package/pp-core/bin/lib/command-aliases.cjs +809 -0
- package/pp-core/bin/lib/command-arg-projection.cjs +55 -0
- package/pp-core/bin/lib/command-roster.cjs +19 -0
- package/pp-core/bin/lib/command-routing-hub.cjs +300 -0
- package/pp-core/bin/lib/commands.cjs +1244 -0
- package/pp-core/bin/lib/config-loader.cjs +714 -0
- package/pp-core/bin/lib/config-schema.cjs +58 -0
- package/pp-core/bin/lib/config-types.cjs +20 -0
- package/pp-core/bin/lib/config.cjs +796 -0
- package/pp-core/bin/lib/configuration.cjs +209 -0
- package/pp-core/bin/lib/context-utilization.cjs +48 -0
- package/pp-core/bin/lib/core-utils.cjs +198 -0
- package/pp-core/bin/lib/decisions.cjs +136 -0
- package/pp-core/bin/lib/docs.cjs +264 -0
- package/pp-core/bin/lib/drift.cjs +364 -0
- package/pp-core/bin/lib/edge-probe.cjs +196 -0
- package/pp-core/bin/lib/fallow-runner.cjs +153 -0
- package/pp-core/bin/lib/federated-config.cjs +182 -0
- package/pp-core/bin/lib/frontmatter.cjs +494 -0
- package/pp-core/bin/lib/gap-checker.cjs +260 -0
- package/pp-core/bin/lib/git-base-branch.cjs +220 -0
- package/pp-core/bin/lib/graphify-command-router.cjs +72 -0
- package/pp-core/bin/lib/graphify.cjs +472 -0
- package/pp-core/bin/lib/gsd2-import.cjs +456 -0
- package/pp-core/bin/lib/init-command-router.cjs +62 -0
- package/pp-core/bin/lib/init.cjs +1912 -0
- package/pp-core/bin/lib/install-profiles.cjs +814 -0
- package/pp-core/bin/lib/installer-migration-authoring.cjs +122 -0
- package/pp-core/bin/lib/installer-migration-report.cjs +351 -0
- package/pp-core/bin/lib/installer-migrations/000-first-time-baseline.cjs +225 -0
- package/pp-core/bin/lib/installer-migrations/001-legacy-orphan-files.cjs +48 -0
- package/pp-core/bin/lib/installer-migrations/002-codex-legacy-hooks-json.cjs +94 -0
- package/pp-core/bin/lib/installer-migrations/003-rename-get-shit-done-to-gsd-core.cjs +108 -0
- package/pp-core/bin/lib/installer-migrations/004-prune-stale-pristine-snapshots.cjs +123 -0
- package/pp-core/bin/lib/installer-migrations/005-rename-gsd-core-to-pp-core.cjs +109 -0
- package/pp-core/bin/lib/installer-migrations/006-rename-gsd-command-namespace.cjs +135 -0
- package/pp-core/bin/lib/installer-migrations.cjs +823 -0
- package/pp-core/bin/lib/intel-command-router.cjs +119 -0
- package/pp-core/bin/lib/intel.cjs +584 -0
- package/pp-core/bin/lib/io.cjs +220 -0
- package/pp-core/bin/lib/learnings.cjs +270 -0
- package/pp-core/bin/lib/legacy-cleanup.cjs +253 -0
- package/pp-core/bin/lib/loop-host-contract.cjs +105 -0
- package/pp-core/bin/lib/loop-resolver.cjs +479 -0
- package/pp-core/bin/lib/milestone.cjs +381 -0
- package/pp-core/bin/lib/model-catalog.cjs +154 -0
- package/pp-core/bin/lib/model-profiles.cjs +24 -0
- package/pp-core/bin/lib/model-resolver.cjs +467 -0
- package/pp-core/bin/lib/observability/event.cjs +51 -0
- package/pp-core/bin/lib/observability/logger.cjs +146 -0
- package/pp-core/bin/lib/observability/redaction.cjs +48 -0
- package/pp-core/bin/lib/package-identity.cjs +35 -0
- package/pp-core/bin/lib/package-legitimacy.cjs +368 -0
- package/pp-core/bin/lib/phase-command-router.cjs +209 -0
- package/pp-core/bin/lib/phase-id.cjs +213 -0
- package/pp-core/bin/lib/phase-lifecycle.cjs +74 -0
- package/pp-core/bin/lib/phase-locator.cjs +145 -0
- package/pp-core/bin/lib/phase.cjs +1446 -0
- package/pp-core/bin/lib/phases-command-router.cjs +43 -0
- package/pp-core/bin/lib/plan-drift-guard.cjs +117 -0
- package/pp-core/bin/lib/plan-scan.cjs +91 -0
- package/pp-core/bin/lib/planning-workspace.cjs +245 -0
- package/pp-core/bin/lib/probe-core.cjs +402 -0
- package/pp-core/bin/lib/profile-output.cjs +1164 -0
- package/pp-core/bin/lib/profile-pipeline-command-router.cjs +138 -0
- package/pp-core/bin/lib/profile-pipeline.cjs +519 -0
- package/pp-core/bin/lib/prohibition-enforcement.cjs +660 -0
- package/pp-core/bin/lib/project-root.cjs +119 -0
- package/pp-core/bin/lib/prompt-budget.cjs +305 -0
- package/pp-core/bin/lib/research-provider.cjs +137 -0
- package/pp-core/bin/lib/research-store.cjs +167 -0
- package/pp-core/bin/lib/review-reviewer-selection.cjs +121 -0
- package/pp-core/bin/lib/roadmap-command-router.cjs +166 -0
- package/pp-core/bin/lib/roadmap-parser.cjs +456 -0
- package/pp-core/bin/lib/roadmap-upgrade.cjs +476 -0
- package/pp-core/bin/lib/roadmap.cjs +738 -0
- package/pp-core/bin/lib/runtime-artifact-conversion.cjs +1966 -0
- package/pp-core/bin/lib/runtime-artifact-layout.cjs +342 -0
- package/pp-core/bin/lib/runtime-config-adapter-registry.cjs +103 -0
- package/pp-core/bin/lib/runtime-homes.cjs +272 -0
- package/pp-core/bin/lib/runtime-hooks-surface.cjs +1460 -0
- package/pp-core/bin/lib/runtime-name-policy.cjs +97 -0
- package/pp-core/bin/lib/runtime-slash.cjs +137 -0
- package/pp-core/bin/lib/schema-detect.cjs +159 -0
- package/pp-core/bin/lib/secrets.cjs +34 -0
- package/pp-core/bin/lib/security.cjs +480 -0
- package/pp-core/bin/lib/semver-compare.cjs +42 -0
- package/pp-core/bin/lib/shell-command-projection.cjs +550 -0
- package/pp-core/bin/lib/state-command-router.cjs +172 -0
- package/pp-core/bin/lib/state-document.cjs +312 -0
- package/pp-core/bin/lib/state.cjs +2587 -0
- package/pp-core/bin/lib/surface.cjs +505 -0
- package/pp-core/bin/lib/task-command-router.cjs +85 -0
- package/pp-core/bin/lib/teams-status.cjs +74 -0
- package/pp-core/bin/lib/template.cjs +246 -0
- package/pp-core/bin/lib/uat-predicate.cjs +329 -0
- package/pp-core/bin/lib/uat.cjs +359 -0
- package/pp-core/bin/lib/ui-safety-gate.cjs +98 -0
- package/pp-core/bin/lib/update-context.cjs +221 -0
- package/pp-core/bin/lib/validate-command-router.cjs +91 -0
- package/pp-core/bin/lib/validate.cjs +122 -0
- package/pp-core/bin/lib/verification-command-router.cjs +31 -0
- package/pp-core/bin/lib/verification.cjs +196 -0
- package/pp-core/bin/lib/verify-command-router.cjs +44 -0
- package/pp-core/bin/lib/verify.cjs +1932 -0
- package/pp-core/bin/lib/workstream-inventory-builder.cjs +81 -0
- package/pp-core/bin/lib/workstream-inventory.cjs +147 -0
- package/pp-core/bin/lib/workstream-name-policy.cjs +91 -0
- package/pp-core/bin/lib/workstream.cjs +386 -0
- package/pp-core/bin/lib/worktree-base-ref.cjs +350 -0
- package/pp-core/bin/lib/worktree-safety.cjs +985 -0
- package/pp-core/bin/pp-tools.cjs +2239 -0
- package/pp-core/bin/pp_run +20 -0
- package/pp-core/bin/shared/config-defaults.manifest.json +97 -0
- package/pp-core/bin/shared/config-schema.manifest.json +176 -0
- package/pp-core/bin/shared/model-catalog.json +160 -0
- package/pp-core/bin/shared/runtime-aliases.manifest.json +79 -0
- package/pp-core/bin/verify-reapply-patches.cjs +400 -0
- package/pp-core/contexts/dev.md +21 -0
- package/pp-core/contexts/research.md +22 -0
- package/pp-core/contexts/review.md +23 -0
- package/pp-core/references/agent-contracts.md +79 -0
- package/pp-core/references/ai-evals.md +156 -0
- package/pp-core/references/ai-frameworks.md +186 -0
- package/pp-core/references/artifact-types.md +131 -0
- package/pp-core/references/autonomous-smart-discuss.md +277 -0
- package/pp-core/references/checkpoints.md +814 -0
- package/pp-core/references/common-bug-patterns.md +114 -0
- package/pp-core/references/context-budget.md +85 -0
- package/pp-core/references/continuation-format.md +253 -0
- package/pp-core/references/debugger-philosophy.md +76 -0
- package/pp-core/references/decimal-phase-calculation.md +64 -0
- package/pp-core/references/doc-conflict-engine.md +91 -0
- package/pp-core/references/domain-probes.md +125 -0
- package/pp-core/references/edge-probe-fixtures/01-round-half-even/expected-coverage.json +7 -0
- package/pp-core/references/edge-probe-fixtures/01-round-half-even/requirements.json +1 -0
- package/pp-core/references/edge-probe-fixtures/02-merge-intervals/expected-coverage.json +8 -0
- package/pp-core/references/edge-probe-fixtures/02-merge-intervals/requirements.json +1 -0
- package/pp-core/references/edge-probe-fixtures/03-truncate-graphemes/expected-coverage.json +7 -0
- package/pp-core/references/edge-probe-fixtures/03-truncate-graphemes/requirements.json +1 -0
- package/pp-core/references/edge-probe-fixtures/04-money-rounding/expected-coverage.json +7 -0
- package/pp-core/references/edge-probe-fixtures/04-money-rounding/requirements.json +1 -0
- package/pp-core/references/edge-probe-fixtures/05-list-dedupe/expected-coverage.json +8 -0
- package/pp-core/references/edge-probe-fixtures/05-list-dedupe/requirements.json +1 -0
- package/pp-core/references/edge-probe-fixtures/06-resolved-mixed/expected-coverage.json +8 -0
- package/pp-core/references/edge-probe-fixtures/06-resolved-mixed/requirements.json +1 -0
- package/pp-core/references/edge-probe-fixtures/06-resolved-mixed/resolutions.json +4 -0
- package/pp-core/references/edge-probe.md +272 -0
- package/pp-core/references/execute-mvp-tdd.md +81 -0
- package/pp-core/references/executor-examples.md +110 -0
- package/pp-core/references/few-shot-examples/plan-checker.md +73 -0
- package/pp-core/references/few-shot-examples/verifier.md +109 -0
- package/pp-core/references/gate-prompts.md +100 -0
- package/pp-core/references/gates.md +70 -0
- package/pp-core/references/git-integration.md +298 -0
- package/pp-core/references/git-planning-commit.md +40 -0
- package/pp-core/references/ios-scaffold.md +123 -0
- package/pp-core/references/loop-hook-dispatch.md +61 -0
- package/pp-core/references/mandatory-initial-read.md +2 -0
- package/pp-core/references/model-profile-resolution.md +38 -0
- package/pp-core/references/model-profiles.md +245 -0
- package/pp-core/references/mvp-concepts.md +49 -0
- package/pp-core/references/phase-argument-parsing.md +61 -0
- package/pp-core/references/planner-antipatterns.md +176 -0
- package/pp-core/references/planner-chunked.md +49 -0
- package/pp-core/references/planner-gap-closure.md +62 -0
- package/pp-core/references/planner-graphify-auto-update.md +67 -0
- package/pp-core/references/planner-guidance.md +186 -0
- package/pp-core/references/planner-human-verify-mode.md +57 -0
- package/pp-core/references/planner-interface-context.md +62 -0
- package/pp-core/references/planner-load-graph-context.md +36 -0
- package/pp-core/references/planner-mvp-mode.md +53 -0
- package/pp-core/references/planner-reviews.md +42 -0
- package/pp-core/references/planner-revision.md +87 -0
- package/pp-core/references/planner-source-audit.md +73 -0
- package/pp-core/references/planning-config.md +477 -0
- package/pp-core/references/prohibition-probe-fixtures/01-streak-reminder/expected.json +14 -0
- package/pp-core/references/prohibition-probe-fixtures/02-clean-utility/expected.json +4 -0
- package/pp-core/references/prohibition-probe-fixtures/03-multi-prohibition/expected.json +32 -0
- package/pp-core/references/prohibition-probe.md +326 -0
- package/pp-core/references/project-skills-discovery.md +19 -0
- package/pp-core/references/questioning.md +162 -0
- package/pp-core/references/research-documentation-lookup.md +29 -0
- package/pp-core/references/research-philosophy.md +29 -0
- package/pp-core/references/research-verification-protocol.md +27 -0
- package/pp-core/references/revision-loop.md +97 -0
- package/pp-core/references/scout-codebase.md +51 -0
- package/pp-core/references/skeleton-template.md +48 -0
- package/pp-core/references/sketch-interactivity.md +41 -0
- package/pp-core/references/sketch-theme-system.md +94 -0
- package/pp-core/references/sketch-tooling.md +45 -0
- package/pp-core/references/sketch-variant-patterns.md +81 -0
- package/pp-core/references/spidr-splitting.md +69 -0
- package/pp-core/references/tdd.md +330 -0
- package/pp-core/references/thinking-models-debug.md +44 -0
- package/pp-core/references/thinking-models-execution.md +50 -0
- package/pp-core/references/thinking-models-planning.md +62 -0
- package/pp-core/references/thinking-models-research.md +50 -0
- package/pp-core/references/thinking-models-verification.md +55 -0
- package/pp-core/references/thinking-partner.md +96 -0
- package/pp-core/references/ui-brand.md +162 -0
- package/pp-core/references/universal-anti-patterns.md +63 -0
- package/pp-core/references/user-profiling.md +681 -0
- package/pp-core/references/user-story-template.md +58 -0
- package/pp-core/references/verification-overrides.md +227 -0
- package/pp-core/references/verification-patterns.md +612 -0
- package/pp-core/references/verify-mvp-mode.md +85 -0
- package/pp-core/references/workstream-flag.md +111 -0
- package/pp-core/references/worktree-branch-check.md +44 -0
- package/pp-core/references/worktree-path-safety.md +67 -0
- package/pp-core/templates/AI-SPEC.md +246 -0
- package/pp-core/templates/DEBUG.md +169 -0
- package/pp-core/templates/README.md +77 -0
- package/pp-core/templates/SECURITY.md +61 -0
- package/pp-core/templates/UAT.md +265 -0
- package/pp-core/templates/UI-SPEC.md +100 -0
- package/pp-core/templates/VALIDATION.md +76 -0
- package/pp-core/templates/claude-md.md +145 -0
- package/pp-core/templates/codebase/architecture.md +255 -0
- package/pp-core/templates/codebase/concerns.md +310 -0
- package/pp-core/templates/codebase/conventions.md +307 -0
- package/pp-core/templates/codebase/integrations.md +280 -0
- package/pp-core/templates/codebase/stack.md +186 -0
- package/pp-core/templates/codebase/structure.md +285 -0
- package/pp-core/templates/codebase/testing.md +480 -0
- package/pp-core/templates/config.json +62 -0
- package/pp-core/templates/context.md +352 -0
- package/pp-core/templates/continue-here.md +78 -0
- package/pp-core/templates/copilot-instructions.md +7 -0
- package/pp-core/templates/debug-subagent-prompt.md +91 -0
- package/pp-core/templates/dev-preferences.md +21 -0
- package/pp-core/templates/discovery.md +146 -0
- package/pp-core/templates/discussion-log.md +63 -0
- package/pp-core/templates/milestone-archive.md +123 -0
- package/pp-core/templates/milestone.md +115 -0
- package/pp-core/templates/phase-prompt.md +610 -0
- package/pp-core/templates/planner-subagent-prompt.md +117 -0
- package/pp-core/templates/project.md +203 -0
- package/pp-core/templates/requirements.md +231 -0
- package/pp-core/templates/research-project/ARCHITECTURE.md +204 -0
- package/pp-core/templates/research-project/FEATURES.md +147 -0
- package/pp-core/templates/research-project/PITFALLS.md +200 -0
- package/pp-core/templates/research-project/STACK.md +120 -0
- package/pp-core/templates/research-project/SUMMARY.md +170 -0
- package/pp-core/templates/research.md +592 -0
- package/pp-core/templates/retrospective.md +54 -0
- package/pp-core/templates/roadmap.md +202 -0
- package/pp-core/templates/spec.md +333 -0
- package/pp-core/templates/state.md +195 -0
- package/pp-core/templates/summary-complex.md +60 -0
- package/pp-core/templates/summary-minimal.md +42 -0
- package/pp-core/templates/summary-standard.md +49 -0
- package/pp-core/templates/summary.md +249 -0
- package/pp-core/templates/user-profile.md +146 -0
- package/pp-core/templates/user-setup.md +311 -0
- package/pp-core/templates/verification-report.md +335 -0
- package/pp-core/workflows/_runtime-launcher.snippet.sh +1 -0
- package/pp-core/workflows/add-backlog.md +91 -0
- package/pp-core/workflows/add-phase.md +113 -0
- package/pp-core/workflows/add-tests.md +355 -0
- package/pp-core/workflows/add-todo.md +161 -0
- package/pp-core/workflows/ai-integration-phase.md +295 -0
- package/pp-core/workflows/analyze-dependencies.md +96 -0
- package/pp-core/workflows/audit-fix.md +178 -0
- package/pp-core/workflows/audit-milestone.md +362 -0
- package/pp-core/workflows/audit-uat.md +110 -0
- package/pp-core/workflows/autonomous.md +886 -0
- package/pp-core/workflows/check-todos.md +180 -0
- package/pp-core/workflows/cleanup.md +195 -0
- package/pp-core/workflows/code-review-fix.md +506 -0
- package/pp-core/workflows/code-review.md +696 -0
- package/pp-core/workflows/complete-milestone.md +857 -0
- package/pp-core/workflows/debug.md +237 -0
- package/pp-core/workflows/diagnose-issues.md +245 -0
- package/pp-core/workflows/discovery-phase.md +291 -0
- package/pp-core/workflows/discovery-sprint.md +1546 -0
- package/pp-core/workflows/discuss-phase/modes/advisor.md +176 -0
- package/pp-core/workflows/discuss-phase/modes/all.md +28 -0
- package/pp-core/workflows/discuss-phase/modes/analyze.md +44 -0
- package/pp-core/workflows/discuss-phase/modes/auto.md +57 -0
- package/pp-core/workflows/discuss-phase/modes/batch.md +52 -0
- package/pp-core/workflows/discuss-phase/modes/chain.md +98 -0
- package/pp-core/workflows/discuss-phase/modes/default.md +141 -0
- package/pp-core/workflows/discuss-phase/modes/power.md +44 -0
- package/pp-core/workflows/discuss-phase/modes/text.md +55 -0
- package/pp-core/workflows/discuss-phase/templates/checkpoint.json +18 -0
- package/pp-core/workflows/discuss-phase/templates/context.md +136 -0
- package/pp-core/workflows/discuss-phase/templates/discussion-log.md +50 -0
- package/pp-core/workflows/discuss-phase-assumptions.md +675 -0
- package/pp-core/workflows/discuss-phase-power.md +291 -0
- package/pp-core/workflows/discuss-phase.md +520 -0
- package/pp-core/workflows/do.md +111 -0
- package/pp-core/workflows/docs-update.md +1168 -0
- package/pp-core/workflows/edit-phase.md +295 -0
- package/pp-core/workflows/eval-review.md +159 -0
- package/pp-core/workflows/execute-phase/steps/codebase-drift-gate.md +95 -0
- package/pp-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +94 -0
- package/pp-core/workflows/execute-phase/steps/post-merge-gate.md +117 -0
- package/pp-core/workflows/execute-phase/steps/worktree-recovery-policy.md +9 -0
- package/pp-core/workflows/execute-phase.md +1707 -0
- package/pp-core/workflows/execute-plan.md +541 -0
- package/pp-core/workflows/explore.md +146 -0
- package/pp-core/workflows/extract-learnings.md +243 -0
- package/pp-core/workflows/fast.md +124 -0
- package/pp-core/workflows/forensics.md +279 -0
- package/pp-core/workflows/graduation.md +196 -0
- package/pp-core/workflows/health.md +224 -0
- package/pp-core/workflows/help/modes/brief.md +22 -0
- package/pp-core/workflows/help/modes/default.md +50 -0
- package/pp-core/workflows/help/modes/full.md +791 -0
- package/pp-core/workflows/help/modes/topic.md +74 -0
- package/pp-core/workflows/help.md +24 -0
- package/pp-core/workflows/import.md +256 -0
- package/pp-core/workflows/inbox.md +387 -0
- package/pp-core/workflows/ingest-docs.md +340 -0
- package/pp-core/workflows/insert-phase.md +152 -0
- package/pp-core/workflows/list-phase-assumptions.md +178 -0
- package/pp-core/workflows/list-workspaces.md +57 -0
- package/pp-core/workflows/manager.md +435 -0
- package/pp-core/workflows/map-codebase.md +444 -0
- package/pp-core/workflows/milestone-summary.md +224 -0
- package/pp-core/workflows/mvp-phase.md +222 -0
- package/pp-core/workflows/new-milestone.md +643 -0
- package/pp-core/workflows/new-project.md +1563 -0
- package/pp-core/workflows/new-workspace.md +240 -0
- package/pp-core/workflows/next.md +347 -0
- package/pp-core/workflows/node-repair.md +92 -0
- package/pp-core/workflows/note.md +158 -0
- package/pp-core/workflows/pause-work.md +250 -0
- package/pp-core/workflows/plan-milestone-gaps.md +281 -0
- package/pp-core/workflows/plan-phase.md +1755 -0
- package/pp-core/workflows/plan-review-convergence.md +369 -0
- package/pp-core/workflows/plant-seed.md +230 -0
- package/pp-core/workflows/pr-branch.md +159 -0
- package/pp-core/workflows/profile-user.md +455 -0
- package/pp-core/workflows/progress.md +756 -0
- package/pp-core/workflows/quick.md +1045 -0
- package/pp-core/workflows/reapply-patches.md +443 -0
- package/pp-core/workflows/remove-phase.md +156 -0
- package/pp-core/workflows/remove-workspace.md +108 -0
- package/pp-core/workflows/resume-project.md +348 -0
- package/pp-core/workflows/review.md +765 -0
- package/pp-core/workflows/scan.md +107 -0
- package/pp-core/workflows/secure-phase.md +184 -0
- package/pp-core/workflows/session-report.md +146 -0
- package/pp-core/workflows/settings-advanced.md +816 -0
- package/pp-core/workflows/settings-integrations.md +312 -0
- package/pp-core/workflows/settings.md +592 -0
- package/pp-core/workflows/ship.md +465 -0
- package/pp-core/workflows/sketch-wrap-up.md +286 -0
- package/pp-core/workflows/sketch.md +361 -0
- package/pp-core/workflows/spec-phase.md +495 -0
- package/pp-core/workflows/spike-wrap-up.md +307 -0
- package/pp-core/workflows/spike.md +453 -0
- package/pp-core/workflows/stats.md +80 -0
- package/pp-core/workflows/sync-skills.md +182 -0
- package/pp-core/workflows/thread.md +222 -0
- package/pp-core/workflows/transition.md +694 -0
- package/pp-core/workflows/ui-phase.md +328 -0
- package/pp-core/workflows/ui-review.md +193 -0
- package/pp-core/workflows/ultraplan-phase.md +199 -0
- package/pp-core/workflows/undo.md +314 -0
- package/pp-core/workflows/update.md +501 -0
- package/pp-core/workflows/validate-phase.md +183 -0
- package/pp-core/workflows/verify-phase.md +569 -0
- package/pp-core/workflows/verify-work.md +804 -0
- package/scripts/affected-tests-lib.cjs +554 -0
- package/scripts/audit-workflow-script-paths.cjs +73 -0
- package/scripts/base64-scan.sh +351 -0
- package/scripts/build-hooks.js +254 -0
- package/scripts/changeset/README.md +129 -0
- package/scripts/changeset/cli.cjs +597 -0
- package/scripts/changeset/github-release-notes.cjs +199 -0
- package/scripts/changeset/lint.cjs +147 -0
- package/scripts/changeset/new.cjs +151 -0
- package/scripts/changeset/parse.cjs +114 -0
- package/scripts/changeset/render.cjs +34 -0
- package/scripts/changeset/serialize.cjs +130 -0
- package/scripts/check-alias-drift.cjs +114 -0
- package/scripts/check-env.cjs +312 -0
- package/scripts/check-npm-integrity.cjs +215 -0
- package/scripts/ci-guard-runner.cjs +22 -0
- package/scripts/ci-prepare-test-scope.cjs +93 -0
- package/scripts/ci-rebase-check.cjs +86 -0
- package/scripts/ci-test-scope.cjs +441 -0
- package/scripts/command-contract-helpers.cjs +64 -0
- package/scripts/diff-touches-shipped-paths.cjs +149 -0
- package/scripts/fix-slash-commands.cjs +161 -0
- package/scripts/gen-capability-registry.cjs +2646 -0
- package/scripts/gen-commands-reference.cjs +139 -0
- package/scripts/gen-inventory-manifest.cjs +114 -0
- package/scripts/gen-loop-host-contract.cjs +526 -0
- package/scripts/gen-research-agents.cjs +276 -0
- package/scripts/generate-package-identity.cjs +134 -0
- package/scripts/issue-dedupe.cjs +278 -0
- package/scripts/issue-version-gate.cjs +140 -0
- package/scripts/lib/allowlist-ratchet.cjs +236 -0
- package/scripts/lib/cli-exit.cjs +56 -0
- package/scripts/lint-allow-test-rule-refs.allowlist.json +328 -0
- package/scripts/lint-allow-test-rule-refs.cjs +162 -0
- package/scripts/lint-command-contract.cjs +118 -0
- package/scripts/lint-descriptions.cjs +87 -0
- package/scripts/lint-docs-required.cjs +222 -0
- package/scripts/lint-legacy-dir-name.cjs +166 -0
- package/scripts/lint-package-identity-drift.cjs +141 -0
- package/scripts/lint-pr-check-project-dir.cjs +99 -0
- package/scripts/lint-regression-test-names.allowlist.json +269 -0
- package/scripts/lint-regression-test-names.cjs +117 -0
- package/scripts/lint-shell-command-projection-drift.cjs +62 -0
- package/scripts/lint-skill-deps.cjs +185 -0
- package/scripts/lint-test-file-count.allowlist.json +181 -0
- package/scripts/lint-test-file-count.cjs +246 -0
- package/scripts/lint-windows-test-portability.cjs +178 -0
- package/scripts/mutation-matrix.cjs +323 -0
- package/scripts/pr-target-policy.cjs +63 -0
- package/scripts/pr-template-policy.cjs +268 -0
- package/scripts/prompt-injection-scan.sh +207 -0
- package/scripts/registry-smoke.cjs +337 -0
- package/scripts/release-notes/discord-release-summary.cjs +373 -0
- package/scripts/release-notes/format-github-release-notes.cjs +261 -0
- package/scripts/release-tarball-smoke.cjs +763 -0
- package/scripts/research-profiles.cjs +149 -0
- package/scripts/run-affected-tests.cjs +7 -0
- package/scripts/run-tests.cjs +589 -0
- package/scripts/secret-scan-lint.sh +231 -0
- package/scripts/secret-scan.sh +358 -0
- package/scripts/setup-branch-protection.sh +234 -0
- package/scripts/strip-prose-atrefs.cjs +106 -0
- package/scripts/sync-manifest-versions.cjs +119 -0
- package/scripts/sync-next-version.cjs +133 -0
- package/scripts/sync-rulesets.sh +34 -0
- package/scripts/sync-runtime-launcher.cjs +425 -0
- package/scripts/test-failure-reasons.cjs +34 -0
- package/scripts/update-size-baseline.cjs +68 -0
- package/scripts/verify-npm-publish.cjs +240 -0
- package/scripts/verify-opengsd-sweep.cjs +288 -0
- package/scripts/verify-phase-02-rename.sh +258 -0
- package/scripts/verify-phase-03-rename.sh +376 -0
- package/scripts/workflow-policy.cjs +483 -0
- package/scripts/workflow-size.cjs +90 -0
|
@@ -0,0 +1,800 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Check subcommand router — auto-mode, decision-coverage-plan, decision-coverage-verify.
|
|
4
|
+
*
|
|
5
|
+
* ADR-457 build-at-publish: the hand-written bin/lib/check-command-router.cjs collapsed
|
|
6
|
+
* to a TypeScript source of truth. Behaviour is preserved byte-for-behaviour
|
|
7
|
+
* from the prior hand-written .cjs; only strict types are added.
|
|
8
|
+
*/
|
|
9
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
10
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
11
|
+
};
|
|
12
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
13
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
14
|
+
const node_child_process_1 = require("node:child_process");
|
|
15
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
16
|
+
const io = require("./io.cjs");
|
|
17
|
+
const { output, error, ERROR_REASON } = io;
|
|
18
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
19
|
+
const planningWorkspaceMod = require("./planning-workspace.cjs");
|
|
20
|
+
const { planningDir } = planningWorkspaceMod;
|
|
21
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
22
|
+
const phaseLocatorMod = require("./phase-locator.cjs");
|
|
23
|
+
const { findPhaseInternal } = phaseLocatorMod;
|
|
24
|
+
const decisions_cjs_1 = require("./decisions.cjs");
|
|
25
|
+
const ui_safety_gate_cjs_1 = require("./ui-safety-gate.cjs");
|
|
26
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
27
|
+
const verifyModule = require("./verify.cjs");
|
|
28
|
+
const { cmdVerifySchemaDrift, cmdVerifyCodebaseDrift } = verifyModule;
|
|
29
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
30
|
+
const roadmapModule = require("./roadmap.cjs");
|
|
31
|
+
const { getRoadmapPhaseWithFallback } = roadmapModule;
|
|
32
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
33
|
+
const gapCheckerModule = require("./gap-checker.cjs");
|
|
34
|
+
const { runGapAnalysis } = gapCheckerModule;
|
|
35
|
+
const prohibition_enforcement_cjs_1 = require("./prohibition-enforcement.cjs");
|
|
36
|
+
// ─── Helpers ──────────────────────────────────────────────────────────────────
|
|
37
|
+
function normalizePhrase(text) {
|
|
38
|
+
// eslint-disable-next-line @typescript-eslint/no-base-to-string
|
|
39
|
+
return String(text || '')
|
|
40
|
+
.toLowerCase()
|
|
41
|
+
.replace(/[^a-z0-9\s]/g, ' ')
|
|
42
|
+
.replace(/\s+/g, ' ')
|
|
43
|
+
.trim();
|
|
44
|
+
}
|
|
45
|
+
const SOFT_PHRASE_MIN_WORDS = 6;
|
|
46
|
+
function softPhrase(text) {
|
|
47
|
+
const words = normalizePhrase(text).split(' ').filter(Boolean);
|
|
48
|
+
if (words.length < SOFT_PHRASE_MIN_WORDS)
|
|
49
|
+
return '';
|
|
50
|
+
return words.slice(0, SOFT_PHRASE_MIN_WORDS).join(' ');
|
|
51
|
+
}
|
|
52
|
+
function decisionMentioned(haystack, decision) {
|
|
53
|
+
if (!haystack)
|
|
54
|
+
return false;
|
|
55
|
+
if (new RegExp(`\\b${decision.id}\\b`).test(haystack))
|
|
56
|
+
return true;
|
|
57
|
+
const phrase = softPhrase(decision.text);
|
|
58
|
+
return phrase ? normalizePhrase(haystack).includes(phrase) : false;
|
|
59
|
+
}
|
|
60
|
+
function readIfExists(filePath) {
|
|
61
|
+
try {
|
|
62
|
+
return node_fs_1.default.readFileSync(filePath, 'utf-8');
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
return '';
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
function resolvePath(inputPath, projectDir) {
|
|
69
|
+
return node_path_1.default.isAbsolute(inputPath) ? inputPath : node_path_1.default.join(projectDir, inputPath);
|
|
70
|
+
}
|
|
71
|
+
function readWorkflowConfig(projectDir) {
|
|
72
|
+
const configPath = node_path_1.default.join(projectDir, '.planning', 'config.json');
|
|
73
|
+
try {
|
|
74
|
+
const parsed = JSON.parse(node_fs_1.default.readFileSync(configPath, 'utf-8'));
|
|
75
|
+
const wf = parsed['workflow'] || {};
|
|
76
|
+
return {
|
|
77
|
+
...wf,
|
|
78
|
+
auto_advance: (wf['auto_advance'] ?? parsed['auto_advance']),
|
|
79
|
+
_auto_chain_active: (wf['_auto_chain_active'] ?? parsed['_auto_chain_active']),
|
|
80
|
+
context_coverage_gate: (wf['context_coverage_gate'] ?? parsed['context_coverage_gate']),
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
return {};
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
function cmdAutoMode(projectDir, raw) {
|
|
88
|
+
const workflow = readWorkflowConfig(projectDir);
|
|
89
|
+
const autoAdvance = Boolean(workflow.auto_advance ?? false);
|
|
90
|
+
const autoChainActive = Boolean(workflow._auto_chain_active ?? false);
|
|
91
|
+
let source = 'none';
|
|
92
|
+
if (autoChainActive && autoAdvance)
|
|
93
|
+
source = 'both';
|
|
94
|
+
else if (autoChainActive)
|
|
95
|
+
source = 'auto_chain';
|
|
96
|
+
else if (autoAdvance)
|
|
97
|
+
source = 'auto_advance';
|
|
98
|
+
output({
|
|
99
|
+
active: autoChainActive || autoAdvance,
|
|
100
|
+
source,
|
|
101
|
+
auto_chain_active: autoChainActive,
|
|
102
|
+
auto_advance: autoAdvance,
|
|
103
|
+
}, raw, undefined);
|
|
104
|
+
}
|
|
105
|
+
function gateEnabled(projectDir) {
|
|
106
|
+
const value = readWorkflowConfig(projectDir).context_coverage_gate;
|
|
107
|
+
if (typeof value === 'boolean')
|
|
108
|
+
return value;
|
|
109
|
+
if (typeof value === 'string') {
|
|
110
|
+
const lower = value.toLowerCase();
|
|
111
|
+
if (lower === 'false' || lower === 'true')
|
|
112
|
+
return lower !== 'false';
|
|
113
|
+
}
|
|
114
|
+
return true;
|
|
115
|
+
}
|
|
116
|
+
function loadPlanContents(phaseDir) {
|
|
117
|
+
if (!node_fs_1.default.existsSync(phaseDir))
|
|
118
|
+
return [];
|
|
119
|
+
try {
|
|
120
|
+
return node_fs_1.default.readdirSync(phaseDir)
|
|
121
|
+
.filter((entry) => /-PLAN\.md$/.test(entry))
|
|
122
|
+
.map((entry) => readIfExists(node_path_1.default.join(phaseDir, entry)));
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
return [];
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
const DESIGNATED_HEADINGS_RE = /^#{1,6}\s+(?:must[_ ]haves?|truths?|tasks?|objective)\b/i;
|
|
129
|
+
const XML_DECISION_TAGS_RE = /<(?:objective|tasks?|action)(?:\s[^>]*)?>([\s\S]*?)<\/(?:objective|tasks?|action)>/gi;
|
|
130
|
+
function stripCommentsAndFences(text) {
|
|
131
|
+
return text
|
|
132
|
+
.replace(/<!--[\s\S]*?-->/g, ' ')
|
|
133
|
+
.replace(/```[\s\S]*?```/g, ' ')
|
|
134
|
+
.replace(/~~~[\s\S]*?~~~/g, ' ');
|
|
135
|
+
}
|
|
136
|
+
function extractYamlBlock(frontmatter, key) {
|
|
137
|
+
const match = frontmatter.match(new RegExp(`^${key}\\s*:(.*)$`, 'm'));
|
|
138
|
+
if (!match)
|
|
139
|
+
return '';
|
|
140
|
+
const startIdx = (match.index || 0) + match[0].length;
|
|
141
|
+
const rest = frontmatter.slice(startIdx + 1).split(/\r?\n/);
|
|
142
|
+
const block = [match[1] || ''];
|
|
143
|
+
for (const line of rest) {
|
|
144
|
+
if (line === '' || /^\s/.test(line))
|
|
145
|
+
block.push(line);
|
|
146
|
+
else
|
|
147
|
+
break;
|
|
148
|
+
}
|
|
149
|
+
return block.join('\n');
|
|
150
|
+
}
|
|
151
|
+
function extractXmlTagBodies(text) {
|
|
152
|
+
const parts = [];
|
|
153
|
+
for (const match of text.matchAll(XML_DECISION_TAGS_RE)) {
|
|
154
|
+
if (match[1])
|
|
155
|
+
parts.push(match[1]);
|
|
156
|
+
}
|
|
157
|
+
return parts.join('\n');
|
|
158
|
+
}
|
|
159
|
+
function extractPlanDesignatedSections(planContent) {
|
|
160
|
+
if (!planContent)
|
|
161
|
+
return '';
|
|
162
|
+
const cleaned = stripCommentsAndFences(planContent);
|
|
163
|
+
const fmMatch = cleaned.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/);
|
|
164
|
+
const frontmatter = fmMatch ? fmMatch[1] : '';
|
|
165
|
+
const body = fmMatch ? fmMatch[2] : cleaned;
|
|
166
|
+
const parts = [];
|
|
167
|
+
for (const key of ['must_haves', 'truths', 'objective']) {
|
|
168
|
+
const block = extractYamlBlock(frontmatter, key);
|
|
169
|
+
if (block)
|
|
170
|
+
parts.push(block);
|
|
171
|
+
}
|
|
172
|
+
const bodyParts = [];
|
|
173
|
+
let inDesignated = false;
|
|
174
|
+
for (const line of body.split(/\r?\n/)) {
|
|
175
|
+
const heading = /^#{1,6}\s+/.test(line);
|
|
176
|
+
if (heading) {
|
|
177
|
+
inDesignated = DESIGNATED_HEADINGS_RE.test(line);
|
|
178
|
+
if (inDesignated)
|
|
179
|
+
bodyParts.push(line);
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
if (inDesignated)
|
|
183
|
+
bodyParts.push(line);
|
|
184
|
+
}
|
|
185
|
+
parts.push(bodyParts.join('\n'));
|
|
186
|
+
parts.push(extractXmlTagBodies(cleaned));
|
|
187
|
+
return parts.join('\n\n');
|
|
188
|
+
}
|
|
189
|
+
function buildPlanMessage(uncovered) {
|
|
190
|
+
if (uncovered.length === 0)
|
|
191
|
+
return 'All trackable CONTEXT.md decisions are covered by plans.';
|
|
192
|
+
return [
|
|
193
|
+
'## Decision Coverage Gap',
|
|
194
|
+
'',
|
|
195
|
+
`${uncovered.length} CONTEXT.md decision(s) are not covered by any plan:`,
|
|
196
|
+
'',
|
|
197
|
+
...uncovered.map((item) => `- **${item.id}** (${item.category || 'uncategorized'}): ${item.text}`),
|
|
198
|
+
'',
|
|
199
|
+
'Resolve by citing `D-NN:` in a relevant plan\'s `must_haves`/`truths` (or body),',
|
|
200
|
+
'OR move the decision to `### Claude\'s Discretion` / tag it `[informational]` if it should not be tracked.',
|
|
201
|
+
].join('\n');
|
|
202
|
+
}
|
|
203
|
+
function buildVerifyMessage(notHonored) {
|
|
204
|
+
if (notHonored.length === 0)
|
|
205
|
+
return 'All trackable CONTEXT.md decisions are honored by shipped artifacts.';
|
|
206
|
+
return [
|
|
207
|
+
'### Decision Coverage (warning)',
|
|
208
|
+
'',
|
|
209
|
+
`${notHonored.length} decision(s) not found in shipped artifacts:`,
|
|
210
|
+
'',
|
|
211
|
+
...notHonored.map((item) => `- **${item.id}** (${item.category || 'uncategorized'}): ${item.text}`),
|
|
212
|
+
'',
|
|
213
|
+
'This is a soft warning - verification status is unchanged.',
|
|
214
|
+
].join('\n');
|
|
215
|
+
}
|
|
216
|
+
function loadTrackableDecisions(contextPath) {
|
|
217
|
+
return (0, decisions_cjs_1.parseDecisions)(readIfExists(contextPath)).filter((decision) => decision.trackable);
|
|
218
|
+
}
|
|
219
|
+
function cmdDecisionCoveragePlan(projectDir, args, raw) {
|
|
220
|
+
const phaseDir = args[2] ? resolvePath(args[2], projectDir) : '';
|
|
221
|
+
const contextPath = args[3] ? resolvePath(args[3], projectDir) : '';
|
|
222
|
+
if (!gateEnabled(projectDir)) {
|
|
223
|
+
output({ passed: true, skipped: true, reason: 'workflow.context_coverage_gate is false', total: 0, covered: 0, uncovered: [], message: 'Decision coverage gate disabled by config.' }, raw, undefined);
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
if (!contextPath || !node_fs_1.default.existsSync(contextPath)) {
|
|
227
|
+
output({ passed: true, skipped: true, reason: 'CONTEXT.md missing', total: 0, covered: 0, uncovered: [], message: 'No CONTEXT.md - nothing to check.' }, raw, undefined);
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
const decisions = loadTrackableDecisions(contextPath);
|
|
231
|
+
if (decisions.length === 0) {
|
|
232
|
+
output({ passed: true, skipped: true, reason: 'no trackable decisions', total: 0, covered: 0, uncovered: [], message: 'No trackable decisions in CONTEXT.md.' }, raw, undefined);
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
const sections = loadPlanContents(phaseDir).map(extractPlanDesignatedSections);
|
|
236
|
+
const uncovered = [];
|
|
237
|
+
let covered = 0;
|
|
238
|
+
for (const decision of decisions) {
|
|
239
|
+
if (sections.some((section) => decisionMentioned(section, decision)))
|
|
240
|
+
covered++;
|
|
241
|
+
else
|
|
242
|
+
uncovered.push({ id: decision.id, text: decision.text, category: decision.category });
|
|
243
|
+
}
|
|
244
|
+
output({
|
|
245
|
+
passed: uncovered.length === 0,
|
|
246
|
+
skipped: false,
|
|
247
|
+
total: decisions.length,
|
|
248
|
+
covered,
|
|
249
|
+
uncovered,
|
|
250
|
+
message: buildPlanMessage(uncovered),
|
|
251
|
+
}, raw, undefined);
|
|
252
|
+
}
|
|
253
|
+
function recentCommitMessages(projectDir) {
|
|
254
|
+
try {
|
|
255
|
+
return (0, node_child_process_1.execFileSync)('git', ['log', '-n', '200', '--pretty=%s%n%b'], {
|
|
256
|
+
cwd: projectDir,
|
|
257
|
+
encoding: 'utf-8',
|
|
258
|
+
maxBuffer: 4 * 1024 * 1024,
|
|
259
|
+
windowsHide: true,
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
catch {
|
|
263
|
+
return '';
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
function isInsideRoot(candidatePath, rootDir) {
|
|
267
|
+
const root = node_path_1.default.resolve(rootDir);
|
|
268
|
+
const target = node_path_1.default.resolve(root, candidatePath);
|
|
269
|
+
return target === root || target.startsWith(`${root}${node_path_1.default.sep}`);
|
|
270
|
+
}
|
|
271
|
+
function readModifiedFilesContent(projectDir, summaries) {
|
|
272
|
+
const out = [];
|
|
273
|
+
let total = 0;
|
|
274
|
+
for (const summary of summaries) {
|
|
275
|
+
if (!summary)
|
|
276
|
+
continue;
|
|
277
|
+
for (const blockMatch of summary.matchAll(/files_modified:\s*\n((?:[ \t]*-\s+.+\n?)+)/g)) {
|
|
278
|
+
const files = [...(blockMatch[1] || '').matchAll(/-\s+(.+)/g)]
|
|
279
|
+
.map((match) => match[1].trim().replace(/^["']|["']$/g, ''));
|
|
280
|
+
for (const file of files) {
|
|
281
|
+
if (total >= 50)
|
|
282
|
+
break;
|
|
283
|
+
if (!file || !isInsideRoot(file, projectDir))
|
|
284
|
+
continue;
|
|
285
|
+
const raw = readIfExists(resolvePath(file, projectDir));
|
|
286
|
+
out.push(raw.length > 256 * 1024 ? raw.slice(0, 256 * 1024) : raw);
|
|
287
|
+
total++;
|
|
288
|
+
}
|
|
289
|
+
if (total >= 50)
|
|
290
|
+
break;
|
|
291
|
+
}
|
|
292
|
+
if (total >= 50)
|
|
293
|
+
break;
|
|
294
|
+
}
|
|
295
|
+
return out.join('\n\n');
|
|
296
|
+
}
|
|
297
|
+
function cmdDecisionCoverageVerify(projectDir, args, raw) {
|
|
298
|
+
const phaseDir = args[2] ? resolvePath(args[2], projectDir) : '';
|
|
299
|
+
const contextPath = args[3] ? resolvePath(args[3], projectDir) : '';
|
|
300
|
+
if (!gateEnabled(projectDir)) {
|
|
301
|
+
output({ skipped: true, blocking: false, reason: 'workflow.context_coverage_gate is false', total: 0, honored: 0, not_honored: [], message: 'Decision coverage gate disabled by config.' }, raw, undefined);
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
if (!contextPath || !node_fs_1.default.existsSync(contextPath)) {
|
|
305
|
+
output({ skipped: true, blocking: false, reason: 'CONTEXT.md missing', total: 0, honored: 0, not_honored: [], message: 'No CONTEXT.md - nothing to check.' }, raw, undefined);
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
const decisions = loadTrackableDecisions(contextPath);
|
|
309
|
+
if (decisions.length === 0) {
|
|
310
|
+
output({ skipped: true, blocking: false, reason: 'no trackable decisions', total: 0, honored: 0, not_honored: [], message: 'No trackable decisions in CONTEXT.md.' }, raw, undefined);
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
const planContents = loadPlanContents(phaseDir);
|
|
314
|
+
const summaryParts = node_fs_1.default.existsSync(phaseDir)
|
|
315
|
+
? node_fs_1.default.readdirSync(phaseDir).filter((entry) => /-SUMMARY\.md$/.test(entry)).map((entry) => readIfExists(node_path_1.default.join(phaseDir, entry)))
|
|
316
|
+
: [];
|
|
317
|
+
const haystack = [
|
|
318
|
+
planContents.join('\n\n'),
|
|
319
|
+
summaryParts.join('\n\n'),
|
|
320
|
+
readModifiedFilesContent(projectDir, summaryParts),
|
|
321
|
+
recentCommitMessages(projectDir),
|
|
322
|
+
].join('\n\n');
|
|
323
|
+
const notHonored = [];
|
|
324
|
+
let honored = 0;
|
|
325
|
+
for (const decision of decisions) {
|
|
326
|
+
if (decisionMentioned(haystack, decision))
|
|
327
|
+
honored++;
|
|
328
|
+
else
|
|
329
|
+
notHonored.push({ id: decision.id, text: decision.text, category: decision.category });
|
|
330
|
+
}
|
|
331
|
+
output({
|
|
332
|
+
skipped: false,
|
|
333
|
+
blocking: false,
|
|
334
|
+
total: decisions.length,
|
|
335
|
+
honored,
|
|
336
|
+
not_honored: notHonored,
|
|
337
|
+
message: buildVerifyMessage(notHonored),
|
|
338
|
+
}, raw, undefined);
|
|
339
|
+
}
|
|
340
|
+
// ─── ui-plan-gate ─────────────────────────────────────────────────────────────
|
|
341
|
+
/**
|
|
342
|
+
* ui-plan-gate: given a phase number, checks whether the phase has frontend
|
|
343
|
+
* indicators and whether a *-UI-SPEC.md already exists in the phase directory.
|
|
344
|
+
*
|
|
345
|
+
* Returns JSON: { frontend: boolean, hasUiSpec: boolean, block: boolean }
|
|
346
|
+
* block = frontend && !hasUiSpec (gate fires when UI work is detected but no spec exists)
|
|
347
|
+
*
|
|
348
|
+
* Invocable as: gsd_run check ui-plan-gate <phase>
|
|
349
|
+
*
|
|
350
|
+
* Uses checkUiPresence from ui-safety-gate.cjs — does NOT reimplement frontend detection.
|
|
351
|
+
* Uses getRoadmapPhaseWithFallback + findPhaseInternal from leaf modules for phase data.
|
|
352
|
+
*/
|
|
353
|
+
function findUiSpecInDir(phaseDir) {
|
|
354
|
+
if (!phaseDir || !node_fs_1.default.existsSync(phaseDir))
|
|
355
|
+
return '';
|
|
356
|
+
try {
|
|
357
|
+
const files = node_fs_1.default.readdirSync(phaseDir);
|
|
358
|
+
const found = files.find((f) => /-UI-SPEC\.md$/.test(f));
|
|
359
|
+
return found ? node_path_1.default.join(phaseDir, found) : '';
|
|
360
|
+
}
|
|
361
|
+
catch {
|
|
362
|
+
return '';
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
/**
|
|
366
|
+
* Pure logic for ui-plan-gate — exposed for direct behavioral testing.
|
|
367
|
+
*
|
|
368
|
+
* Given a projectDir and phase number:
|
|
369
|
+
* (a) Reads the phase section from ROADMAP.md via getRoadmapPhaseWithFallback —
|
|
370
|
+
* same two-pass lookup (current milestone → full roadmap) as `roadmap.get-phase`
|
|
371
|
+
* (cmdRoadmapGetPhase). Cross-milestone / older frontend phases resolve correctly.
|
|
372
|
+
* If ROADMAP.md is missing, phaseSection is '' (ROADMAP.md not present = project
|
|
373
|
+
* has no roadmap = cannot be frontend). If the phase truly can't be found after
|
|
374
|
+
* both passes, phaseSection is '' and phaseLookupFailed is set so callers can
|
|
375
|
+
* surface the miss — we do NOT silently degrade to frontend:false if the roadmap
|
|
376
|
+
* exists but the phase header is absent.
|
|
377
|
+
* (b) Runs checkUiPresence (frontend detection) — no reimplementation.
|
|
378
|
+
* (c) Resolves the phase directory via findPhaseInternal (phase-locator.cjs); checks for *-UI-SPEC.md.
|
|
379
|
+
*
|
|
380
|
+
* Returns: { frontend, hasUiSpec, block, uiSpecPath, phaseLookupFailed }
|
|
381
|
+
* block = frontend && !hasUiSpec
|
|
382
|
+
* phaseLookupFailed = ROADMAP.md present but phase header not found (surfaced for
|
|
383
|
+
* onError:halt gates so a missing phase doesn't silently bypass)
|
|
384
|
+
*/
|
|
385
|
+
function computeUiPlanGate(projectDir, phase) {
|
|
386
|
+
// (a) Read the phase section text using the same two-pass lookup as roadmap.get-phase.
|
|
387
|
+
// getRoadmapPhaseWithFallback: current-milestone first, then stripShippedMilestones
|
|
388
|
+
// fallback — mirrors cmdRoadmapGetPhase exactly.
|
|
389
|
+
let phaseSection = '';
|
|
390
|
+
let phaseLookupFailed;
|
|
391
|
+
try {
|
|
392
|
+
const section = getRoadmapPhaseWithFallback(projectDir, phase);
|
|
393
|
+
if (section === null) {
|
|
394
|
+
// Distinguish: ROADMAP.md missing (no-roadmap project) vs phase not found in ROADMAP.
|
|
395
|
+
// planningDir(cwd) resolves the .planning/ root for workstream-aware paths.
|
|
396
|
+
const planDir = planningDir(projectDir);
|
|
397
|
+
const roadmapPath = node_path_1.default.join(planDir, 'ROADMAP.md');
|
|
398
|
+
if (node_fs_1.default.existsSync(roadmapPath)) {
|
|
399
|
+
// ROADMAP.md exists but phase was not found → surface the miss
|
|
400
|
+
phaseLookupFailed = true;
|
|
401
|
+
}
|
|
402
|
+
// phaseSection stays ''
|
|
403
|
+
}
|
|
404
|
+
else {
|
|
405
|
+
phaseSection = section;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
catch { /* roadmap read failure → treat as empty (non-frontend) */ }
|
|
409
|
+
// (b) Run checkUiPresence (frontend detection) — reuse existing helper; no reimplementation
|
|
410
|
+
const presenceResult = (0, ui_safety_gate_cjs_1.checkUiPresence)(phaseSection);
|
|
411
|
+
const frontend = presenceResult.hasUI;
|
|
412
|
+
// (c) Resolve phase directory via findPhaseInternal and check for *-UI-SPEC.md
|
|
413
|
+
let phaseDir = '';
|
|
414
|
+
try {
|
|
415
|
+
const result = findPhaseInternal(projectDir, phase);
|
|
416
|
+
if (result && typeof result === 'object') {
|
|
417
|
+
// findPhaseInternal returns { directory: '<relative-posix-path>', ... }
|
|
418
|
+
// directory is relative to cwd — resolve it to absolute.
|
|
419
|
+
const relDir = typeof result['directory'] === 'string' ? result['directory'] : '';
|
|
420
|
+
if (relDir) {
|
|
421
|
+
phaseDir = node_path_1.default.resolve(projectDir, relDir);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
else if (typeof result === 'string') {
|
|
425
|
+
phaseDir = result;
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
catch { /* phase dir lookup failure → hasUiSpec=false */ }
|
|
429
|
+
const uiSpecPath = findUiSpecInDir(phaseDir);
|
|
430
|
+
const hasUiSpec = uiSpecPath !== '';
|
|
431
|
+
// block = frontend phase with no UI-SPEC
|
|
432
|
+
const block = frontend && !hasUiSpec;
|
|
433
|
+
const result = {
|
|
434
|
+
frontend, hasUiSpec, block, uiSpecPath: hasUiSpec ? uiSpecPath : null,
|
|
435
|
+
};
|
|
436
|
+
if (phaseLookupFailed)
|
|
437
|
+
result.phaseLookupFailed = true;
|
|
438
|
+
return result;
|
|
439
|
+
}
|
|
440
|
+
function cmdUiPlanGate(projectDir, args, raw) {
|
|
441
|
+
// args[0] = 'check', args[1] = 'ui-plan-gate', args[2] = phase
|
|
442
|
+
const phase = args[2] || '';
|
|
443
|
+
if (!phase) {
|
|
444
|
+
error('ui-plan-gate requires a phase argument: check ui-plan-gate <phase>', ERROR_REASON.SDK_MISSING_ARG);
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
447
|
+
output(computeUiPlanGate(projectDir, phase), raw, undefined);
|
|
448
|
+
}
|
|
449
|
+
// ─── ui-safety-gate ───────────────────────────────────────────────────────────
|
|
450
|
+
/**
|
|
451
|
+
* ui-safety-gate: post-wave check that verifies UI-changed files conform to
|
|
452
|
+
* the active UI-SPEC for the phase. Called after each wave by execute:wave:post.
|
|
453
|
+
*
|
|
454
|
+
* Returns JSON: { frontend: boolean, hasUiFiles: boolean, hasUiSpec: boolean, block: boolean, message?: string }
|
|
455
|
+
* block = frontend && hasUiFiles && !hasUiSpec
|
|
456
|
+
*
|
|
457
|
+
* Args: check ui-safety-gate <phase>
|
|
458
|
+
* Invocable as: gsd_run check ui-safety-gate <phase>
|
|
459
|
+
* or gsd_run check ui.safety-gate <phase> (dots normalized to hyphens)
|
|
460
|
+
*
|
|
461
|
+
* Uses checkUiPresence from ui-safety-gate.cjs — does NOT reimplement frontend detection.
|
|
462
|
+
* Checks whether any files changed in recent git history match frontend file patterns.
|
|
463
|
+
* Also checks whether a *-UI-SPEC.md exists in the phase directory (same as ui-plan-gate).
|
|
464
|
+
*
|
|
465
|
+
* Limitation: uses git diff HEAD~1..HEAD which covers only the last commit; in a
|
|
466
|
+
* multi-plan wave the wave-start commit would be more accurate but is not yet stored
|
|
467
|
+
* in the wave manifest. This is tracked as a known limitation.
|
|
468
|
+
*/
|
|
469
|
+
const UI_FILE_EXTENSIONS_RE = /\.(tsx|jsx|css|scss|sass|less|vue|svelte|html)$/i;
|
|
470
|
+
const UI_PATH_PATTERNS_RE = /\/(components|pages|views|screens|layouts|ui|frontend)\//i;
|
|
471
|
+
/**
|
|
472
|
+
* Pure logic for ui-safety-gate — exposed for direct behavioral testing.
|
|
473
|
+
*
|
|
474
|
+
* Given a projectDir and phase number:
|
|
475
|
+
* (a) Reads the phase section from ROADMAP.md via getRoadmapPhaseWithFallback —
|
|
476
|
+
* same lookup as computeUiPlanGate — to determine if this is a frontend phase.
|
|
477
|
+
* (b) Runs checkUiPresence (frontend detection) — no reimplementation.
|
|
478
|
+
* (c) Checks git diff HEAD~1..HEAD for UI file changes in the current worktree.
|
|
479
|
+
* (d) Resolves the phase directory via findPhaseInternal (phase-locator.cjs); checks for *-UI-SPEC.md.
|
|
480
|
+
*
|
|
481
|
+
* Returns: { frontend, hasUiFiles, hasUiSpec, block, message?, phaseLookupFailed? }
|
|
482
|
+
* block = frontend && hasUiFiles && !hasUiSpec
|
|
483
|
+
* phaseLookupFailed = ROADMAP.md present but phase header not found
|
|
484
|
+
*/
|
|
485
|
+
function computeUiSafetyGate(projectDir, phase) {
|
|
486
|
+
// (a) Read the phase section text (same two-pass lookup as computeUiPlanGate)
|
|
487
|
+
let phaseSection = '';
|
|
488
|
+
let phaseLookupFailed;
|
|
489
|
+
try {
|
|
490
|
+
const section = getRoadmapPhaseWithFallback(projectDir, phase);
|
|
491
|
+
if (section === null) {
|
|
492
|
+
const planDir = planningDir(projectDir);
|
|
493
|
+
const roadmapPath = node_path_1.default.join(planDir, 'ROADMAP.md');
|
|
494
|
+
if (node_fs_1.default.existsSync(roadmapPath)) {
|
|
495
|
+
phaseLookupFailed = true;
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
else {
|
|
499
|
+
phaseSection = section;
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
catch { /* roadmap read failure → treat as empty (non-frontend) */ }
|
|
503
|
+
// (b) Run checkUiPresence (frontend detection) — reuse existing helper; no reimplementation
|
|
504
|
+
const presenceResult = (0, ui_safety_gate_cjs_1.checkUiPresence)(phaseSection);
|
|
505
|
+
const frontend = presenceResult.hasUI;
|
|
506
|
+
// (c) Check whether any UI files were changed in recent git commits
|
|
507
|
+
// Uses git diff HEAD~1..HEAD to detect frontend file changes since last commit.
|
|
508
|
+
// Known limitation: multi-plan waves may need the wave-start commit for full coverage.
|
|
509
|
+
let hasUiFiles = false;
|
|
510
|
+
try {
|
|
511
|
+
const changed = (0, node_child_process_1.execFileSync)('git', ['diff', '--name-only', 'HEAD~1', 'HEAD'], {
|
|
512
|
+
cwd: projectDir,
|
|
513
|
+
encoding: 'utf-8',
|
|
514
|
+
maxBuffer: 2 * 1024 * 1024,
|
|
515
|
+
windowsHide: true,
|
|
516
|
+
});
|
|
517
|
+
hasUiFiles = changed.split('\n').some((f) => f.trim() && (UI_FILE_EXTENSIONS_RE.test(f) || UI_PATH_PATTERNS_RE.test(f)));
|
|
518
|
+
}
|
|
519
|
+
catch { /* git unavailable or no prior commit — treat as no UI files changed */ }
|
|
520
|
+
// (d) Resolve phase directory and check for *-UI-SPEC.md (same as computeUiPlanGate)
|
|
521
|
+
let phaseDir = '';
|
|
522
|
+
try {
|
|
523
|
+
const result = findPhaseInternal(projectDir, phase);
|
|
524
|
+
if (result && typeof result === 'object') {
|
|
525
|
+
const relDir = typeof result['directory'] === 'string' ? result['directory'] : '';
|
|
526
|
+
if (relDir) {
|
|
527
|
+
phaseDir = node_path_1.default.resolve(projectDir, relDir);
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
else if (typeof result === 'string') {
|
|
531
|
+
phaseDir = result;
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
catch { /* phase dir lookup failure → hasUiSpec=false */ }
|
|
535
|
+
const uiSpecPath = findUiSpecInDir(phaseDir);
|
|
536
|
+
const hasUiSpec = uiSpecPath !== '';
|
|
537
|
+
// block only when: this is a frontend phase AND UI files were changed AND no UI-SPEC exists
|
|
538
|
+
const block = frontend && hasUiFiles && !hasUiSpec;
|
|
539
|
+
const result = { frontend, hasUiFiles, hasUiSpec, block };
|
|
540
|
+
if (block) {
|
|
541
|
+
result.message = `UI files changed in this wave but no UI-SPEC.md exists for Phase ${phase}. ` +
|
|
542
|
+
`Run /gsd:ui-phase ${phase} to generate the design contract before continuing.`;
|
|
543
|
+
}
|
|
544
|
+
if (phaseLookupFailed)
|
|
545
|
+
result.phaseLookupFailed = true;
|
|
546
|
+
return result;
|
|
547
|
+
}
|
|
548
|
+
function cmdUiSafetyGate(projectDir, args, raw) {
|
|
549
|
+
// args[0] = 'check', args[1] = 'ui-safety-gate', args[2] = phase
|
|
550
|
+
const phase = args[2] || '';
|
|
551
|
+
if (!phase) {
|
|
552
|
+
error('ui-safety-gate requires a phase argument: check ui-safety-gate <phase>', ERROR_REASON.SDK_MISSING_ARG);
|
|
553
|
+
return;
|
|
554
|
+
}
|
|
555
|
+
output(computeUiSafetyGate(projectDir, phase), raw, undefined);
|
|
556
|
+
}
|
|
557
|
+
function cmdTddReviewCheckpoint(projectDir, args, raw) {
|
|
558
|
+
// args[0] = 'check', args[1] = 'tdd-review-checkpoint' (normalized), args[2] = phase
|
|
559
|
+
const phase = args[2] || '';
|
|
560
|
+
if (!phase) {
|
|
561
|
+
error('tdd.review-checkpoint requires a phase argument: check tdd.review-checkpoint <phase>', ERROR_REASON.SDK_MISSING_ARG);
|
|
562
|
+
return;
|
|
563
|
+
}
|
|
564
|
+
// Resolve phase directory
|
|
565
|
+
let phaseDir = '';
|
|
566
|
+
try {
|
|
567
|
+
const result = findPhaseInternal(projectDir, phase);
|
|
568
|
+
if (result && typeof result === 'object') {
|
|
569
|
+
const relDir = typeof result['directory'] === 'string' ? result['directory'] : '';
|
|
570
|
+
if (relDir)
|
|
571
|
+
phaseDir = node_path_1.default.resolve(projectDir, relDir);
|
|
572
|
+
}
|
|
573
|
+
else if (typeof result === 'string') {
|
|
574
|
+
phaseDir = result;
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
catch { /* phase dir lookup failure */ }
|
|
578
|
+
// Find all PLAN.md files with type: tdd in frontmatter
|
|
579
|
+
const tddPlanFiles = [];
|
|
580
|
+
if (phaseDir) {
|
|
581
|
+
try {
|
|
582
|
+
const files = node_fs_1.default.readdirSync(phaseDir).filter(f => f.endsWith('-PLAN.md'));
|
|
583
|
+
for (const file of files) {
|
|
584
|
+
const planPath = node_path_1.default.join(phaseDir, file);
|
|
585
|
+
const content = readIfExists(planPath);
|
|
586
|
+
// Check frontmatter for type: tdd
|
|
587
|
+
const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
588
|
+
if (frontmatterMatch) {
|
|
589
|
+
const fm = frontmatterMatch[1];
|
|
590
|
+
if (/^type:\s*tdd\s*$/m.test(fm)) {
|
|
591
|
+
tddPlanFiles.push(planPath);
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
catch { /* directory read failure */ }
|
|
597
|
+
}
|
|
598
|
+
if (tddPlanFiles.length === 0) {
|
|
599
|
+
const result = {
|
|
600
|
+
// Uniform gate contract: block = violations > 0 (advisory; never truly blocks).
|
|
601
|
+
block: false,
|
|
602
|
+
passed: true,
|
|
603
|
+
tddPlans: 0,
|
|
604
|
+
violations: 0,
|
|
605
|
+
table: '',
|
|
606
|
+
rows: [],
|
|
607
|
+
message: `No type:tdd plans found in phase ${phase}. TDD review skipped.`,
|
|
608
|
+
};
|
|
609
|
+
// Pass undefined as rawValue so --raw emits JSON (not plain text).
|
|
610
|
+
// The human-readable report is carried in `result.message` for the
|
|
611
|
+
// dispatch's advisory branch to surface.
|
|
612
|
+
output(result, raw, undefined);
|
|
613
|
+
return;
|
|
614
|
+
}
|
|
615
|
+
// For each TDD plan, extract the plan ID (padded plan number) and check git log
|
|
616
|
+
const rows = [];
|
|
617
|
+
for (const planPath of tddPlanFiles) {
|
|
618
|
+
// Extract plan ID from filename (e.g. "01-02-PLAN.md" → "01-02", or "03-PLAN.md" → "03")
|
|
619
|
+
const basename = node_path_1.default.basename(planPath, '-PLAN.md');
|
|
620
|
+
// planId for commit grep: phase-plan format, e.g. "01-02"
|
|
621
|
+
const planId = basename;
|
|
622
|
+
// Check for RED gate commit: test({planId}):
|
|
623
|
+
let red = false;
|
|
624
|
+
let green = false;
|
|
625
|
+
let refactor = false;
|
|
626
|
+
try {
|
|
627
|
+
const redCommit = (0, node_child_process_1.execFileSync)('git', ['log', '--oneline', `--grep=^test(${planId}):`, '--', '.'], { cwd: projectDir, encoding: 'utf-8', maxBuffer: 1024 * 1024, windowsHide: true });
|
|
628
|
+
red = redCommit.trim().length > 0;
|
|
629
|
+
}
|
|
630
|
+
catch { /* git unavailable or no match */ }
|
|
631
|
+
try {
|
|
632
|
+
const greenCommit = (0, node_child_process_1.execFileSync)('git', ['log', '--oneline', `--grep=^feat(${planId}):`, '--', '.'], { cwd: projectDir, encoding: 'utf-8', maxBuffer: 1024 * 1024, windowsHide: true });
|
|
633
|
+
green = greenCommit.trim().length > 0;
|
|
634
|
+
}
|
|
635
|
+
catch { /* git unavailable or no match */ }
|
|
636
|
+
try {
|
|
637
|
+
const refactorCommit = (0, node_child_process_1.execFileSync)('git', ['log', '--oneline', `--grep=^refactor(${planId}):`, '--', '.'], { cwd: projectDir, encoding: 'utf-8', maxBuffer: 1024 * 1024, windowsHide: true });
|
|
638
|
+
refactor = refactorCommit.trim().length > 0;
|
|
639
|
+
}
|
|
640
|
+
catch { /* git unavailable or no match */ }
|
|
641
|
+
const missing = [];
|
|
642
|
+
if (!red)
|
|
643
|
+
missing.push('RED');
|
|
644
|
+
if (!green)
|
|
645
|
+
missing.push('GREEN');
|
|
646
|
+
const status = missing.length === 0 ? 'Pass' : 'FAIL';
|
|
647
|
+
rows.push({ planId, red, green, refactor, status, missing });
|
|
648
|
+
}
|
|
649
|
+
const violations = rows.filter(r => r.status === 'FAIL').length;
|
|
650
|
+
// Build review table
|
|
651
|
+
const sep = '━'.repeat(53);
|
|
652
|
+
const tableHeader = '| Plan | RED | GREEN | REFACTOR | Status |';
|
|
653
|
+
const tableDivider = '|------|-----|-------|----------|--------|';
|
|
654
|
+
const tableRows = rows.map(r => `| ${r.planId.padEnd(4)} | ${r.red ? ' ✓ ' : ' ✗ '} | ${r.green ? ' ✓ ' : ' ✗ '} | ${r.refactor ? ' ✓ ' : ' — '} | ${r.status.padEnd(6)} |`);
|
|
655
|
+
let table = [
|
|
656
|
+
sep,
|
|
657
|
+
` TDD REVIEW — Phase ${phase}`,
|
|
658
|
+
sep,
|
|
659
|
+
'',
|
|
660
|
+
`TDD Plans: ${tddPlanFiles.length} | Gate violations: ${violations}`,
|
|
661
|
+
'',
|
|
662
|
+
tableHeader,
|
|
663
|
+
tableDivider,
|
|
664
|
+
...tableRows,
|
|
665
|
+
].join('\n');
|
|
666
|
+
if (violations > 0) {
|
|
667
|
+
table += '\n\n⚠ Gate violations are advisory — review before advancing.';
|
|
668
|
+
for (const r of rows.filter(row => row.status === 'FAIL')) {
|
|
669
|
+
table += `\n Plan ${r.planId} missing: ${r.missing.join(', ')} gate commit(s).`;
|
|
670
|
+
table += `\n Expected commit pattern: test(${r.planId}): ... → feat(${r.planId}): ...`;
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
const result = {
|
|
674
|
+
// Uniform gate contract: block = violations > 0.
|
|
675
|
+
// This gate is advisory (blocking: false in capability.json) so block:true
|
|
676
|
+
// only surfaces as a warning, never halts. Kept here so the host-loop
|
|
677
|
+
// dispatch can read a single consistent `block` field.
|
|
678
|
+
block: violations > 0,
|
|
679
|
+
passed: true,
|
|
680
|
+
tddPlans: tddPlanFiles.length,
|
|
681
|
+
violations,
|
|
682
|
+
table,
|
|
683
|
+
rows,
|
|
684
|
+
// Human-readable report in `message` so the dispatch's advisory branch
|
|
685
|
+
// can surface it. --raw emits JSON (rawValue=undefined), not plain text.
|
|
686
|
+
message: table,
|
|
687
|
+
};
|
|
688
|
+
// Pass undefined as rawValue so --raw emits JSON (not the raw table text).
|
|
689
|
+
// The review table is carried in `result.message` and `result.table` so
|
|
690
|
+
// the host-loop dispatch's advisory branch can surface it.
|
|
691
|
+
output(result, raw, undefined);
|
|
692
|
+
}
|
|
693
|
+
// ─── gap-analysis-plan-post ───────────────────────────────────────────────────
|
|
694
|
+
/**
|
|
695
|
+
* gap-analysis-plan-post: non-blocking advisory check that runs the post-planning
|
|
696
|
+
* gap analysis after all PLAN.md files are generated for a phase.
|
|
697
|
+
*
|
|
698
|
+
* Cross-references every REQ-ID and D-ID from REQUIREMENTS.md and CONTEXT.md
|
|
699
|
+
* against the concatenated text of all *-PLAN.md files, emitting a coverage table.
|
|
700
|
+
*
|
|
701
|
+
* This gate is always advisory (passed: true) — it never blocks phase advancement.
|
|
702
|
+
*
|
|
703
|
+
* Args: check gap-analysis.plan-post <phase-dir> [phase-req-ids]
|
|
704
|
+
* Invocable as: gsd_run check gap-analysis.plan-post <phase-dir> [phase-req-ids]
|
|
705
|
+
*/
|
|
706
|
+
function cmdGapAnalysisPlanPost(projectDir, args, raw) {
|
|
707
|
+
// args[0] = 'check', args[1] = 'gap-analysis-plan-post' (normalized), args[2] = phaseDir, args[3] = phaseReqIds
|
|
708
|
+
const phaseDir = args[2] || '';
|
|
709
|
+
if (!phaseDir) {
|
|
710
|
+
error('gap-analysis.plan-post requires a phase-dir argument: check gap-analysis.plan-post <phase-dir> [phase-req-ids]', ERROR_REASON.SDK_MISSING_ARG);
|
|
711
|
+
return;
|
|
712
|
+
}
|
|
713
|
+
const phaseReqIds = args[3] ?? undefined;
|
|
714
|
+
const result = runGapAnalysis(projectDir, phaseDir, { phaseReqIds });
|
|
715
|
+
// Uniform gate contract: block = false (gap-analysis is always advisory, never blocks).
|
|
716
|
+
// `message` carries the human-readable gap analysis report so the dispatch's
|
|
717
|
+
// advisory branch can surface it. --raw emits JSON (rawValue=undefined), not
|
|
718
|
+
// plain markdown text.
|
|
719
|
+
output({
|
|
720
|
+
block: false,
|
|
721
|
+
passed: true,
|
|
722
|
+
enabled: result.enabled,
|
|
723
|
+
table: result.table,
|
|
724
|
+
summary: result.summary,
|
|
725
|
+
counts: result.counts,
|
|
726
|
+
// Human-readable report in `message` for the host-loop advisory branch.
|
|
727
|
+
message: result.table || result.summary || '',
|
|
728
|
+
}, raw, undefined);
|
|
729
|
+
}
|
|
730
|
+
function routeCheckCommand({ args, cwd, raw }) {
|
|
731
|
+
// Normalize dots to hyphens in the subcommand so both forms are accepted.
|
|
732
|
+
// This makes `check.query = "ui.plan-gate"` (dotted form in capability.json gates)
|
|
733
|
+
// directly runnable as `gsd_run check ui.plan-gate` — the dot is normalized to
|
|
734
|
+
// `ui-plan-gate` before routing. The generic gate-dispatch in §5.6 reads
|
|
735
|
+
// `check.query` from the active gate hook and runs `gsd_run check ${hook.check.query}`,
|
|
736
|
+
// so the declared query must be dispatchable exactly as declared.
|
|
737
|
+
const rawSubcommand = args[1];
|
|
738
|
+
const subcommand = typeof rawSubcommand === 'string' ? rawSubcommand.replace(/\./g, '-') : rawSubcommand;
|
|
739
|
+
if (subcommand === 'auto-mode') {
|
|
740
|
+
cmdAutoMode(cwd, raw);
|
|
741
|
+
return;
|
|
742
|
+
}
|
|
743
|
+
if (subcommand === 'decision-coverage-plan') {
|
|
744
|
+
cmdDecisionCoveragePlan(cwd, args, raw);
|
|
745
|
+
return;
|
|
746
|
+
}
|
|
747
|
+
if (subcommand === 'decision-coverage-verify') {
|
|
748
|
+
cmdDecisionCoverageVerify(cwd, args, raw);
|
|
749
|
+
return;
|
|
750
|
+
}
|
|
751
|
+
if (subcommand === 'ui-plan-gate') {
|
|
752
|
+
cmdUiPlanGate(cwd, args, raw);
|
|
753
|
+
return;
|
|
754
|
+
}
|
|
755
|
+
if (subcommand === 'gap-analysis-plan-post') {
|
|
756
|
+
cmdGapAnalysisPlanPost(cwd, args, raw);
|
|
757
|
+
return;
|
|
758
|
+
}
|
|
759
|
+
if (subcommand === 'tdd-review-checkpoint') {
|
|
760
|
+
cmdTddReviewCheckpoint(cwd, args, raw);
|
|
761
|
+
return;
|
|
762
|
+
}
|
|
763
|
+
if (subcommand === 'ui-safety-gate') {
|
|
764
|
+
cmdUiSafetyGate(cwd, args, raw);
|
|
765
|
+
return;
|
|
766
|
+
}
|
|
767
|
+
if (subcommand === 'verify-schema-drift') {
|
|
768
|
+
// Delegates to verify.schema-drift — drift capability gate at execute:wave:post (blocking).
|
|
769
|
+
// Dot-to-hyphen normalization means query "verify.schema-drift" routes here.
|
|
770
|
+
// Honor GSD_SKIP_SCHEMA_CHECK=true to bypass the gate (preserves the original inline gate behavior).
|
|
771
|
+
const phaseArg = typeof args[2] === 'string' ? args[2] : '';
|
|
772
|
+
const skipSchemaCheck = process.env['GSD_SKIP_SCHEMA_CHECK'] === 'true';
|
|
773
|
+
cmdVerifySchemaDrift(cwd, phaseArg, skipSchemaCheck, raw);
|
|
774
|
+
return;
|
|
775
|
+
}
|
|
776
|
+
if (subcommand === 'verify-codebase-drift') {
|
|
777
|
+
// Delegates to verify.codebase-drift — drift capability gate at execute:wave:post (non-blocking).
|
|
778
|
+
// Dot-to-hyphen normalization means query "verify.codebase-drift" routes here.
|
|
779
|
+
cmdVerifyCodebaseDrift(cwd, raw);
|
|
780
|
+
return;
|
|
781
|
+
}
|
|
782
|
+
if (subcommand === 'prohibition-enforcement') {
|
|
783
|
+
// The deterministic test-tier prohibition PRODUCER/gate (#1259, ADR-550 D5d). Locates the
|
|
784
|
+
// wired mechanical check (node-test or lint-rule), confirms fail-first, runs it, builds
|
|
785
|
+
// enforcementEvidence, and emits the dispositionForProhibition verdict. Invocable as
|
|
786
|
+
// `gsd_run check prohibition-enforcement <request.json>`.
|
|
787
|
+
(0, prohibition_enforcement_cjs_1.routeProhibitionEnforcement)(args, raw);
|
|
788
|
+
return;
|
|
789
|
+
}
|
|
790
|
+
error('Unknown check subcommand. Available: auto-mode, decision-coverage-plan, decision-coverage-verify, gap-analysis-plan-post, prohibition-enforcement, tdd-review-checkpoint, ui-plan-gate, ui-safety-gate, verify-schema-drift, verify-codebase-drift', ERROR_REASON.SDK_UNKNOWN_COMMAND);
|
|
791
|
+
}
|
|
792
|
+
module.exports = {
|
|
793
|
+
routeCheckCommand,
|
|
794
|
+
decisionMentioned,
|
|
795
|
+
extractPlanDesignatedSections,
|
|
796
|
+
computeUiPlanGate,
|
|
797
|
+
computeUiSafetyGate,
|
|
798
|
+
cmdGapAnalysisPlanPost,
|
|
799
|
+
cmdTddReviewCheckpoint,
|
|
800
|
+
};
|