@jokerized/getresearchdone 0.4.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/.claude-plugin/plugin.json +103 -0
- package/README.md +211 -0
- package/agents/grd-baseline-assessor.md +684 -0
- package/agents/grd-code-reviewer.md +300 -0
- package/agents/grd-codebase-mapper.md +355 -0
- package/agents/grd-critique-agent.md +119 -0
- package/agents/grd-debugger.md +519 -0
- package/agents/grd-deep-diver.md +737 -0
- package/agents/grd-eval-planner.md +913 -0
- package/agents/grd-eval-reporter.md +717 -0
- package/agents/grd-executor.md +683 -0
- package/agents/grd-feasibility-analyst.md +624 -0
- package/agents/grd-integration-checker.md +367 -0
- package/agents/grd-knowledge-miner.md +81 -0
- package/agents/grd-migrator.md +88 -0
- package/agents/grd-phase-researcher.md +697 -0
- package/agents/grd-plan-checker.md +443 -0
- package/agents/grd-planner.md +1532 -0
- package/agents/grd-product-owner.md +562 -0
- package/agents/grd-project-researcher.md +513 -0
- package/agents/grd-research-synthesizer.md +273 -0
- package/agents/grd-roadmapper.md +798 -0
- package/agents/grd-surveyor.md +566 -0
- package/agents/grd-verifier.md +893 -0
- package/bin/gd.js +4 -0
- package/bin/gd.ts +227 -0
- package/bin/grd-manifest.js +4 -0
- package/bin/grd-manifest.ts +286 -0
- package/bin/grd-mcp-server.js +4 -0
- package/bin/grd-mcp-server.ts +124 -0
- package/bin/grd-tools.js +4 -0
- package/bin/grd-tools.ts +2471 -0
- package/bin/postinstall.js +4 -0
- package/bin/postinstall.ts +80 -0
- package/commands/add-phase.md +123 -0
- package/commands/add-todo.md +87 -0
- package/commands/assess-baseline.md +289 -0
- package/commands/autopilot.md +100 -0
- package/commands/autoplan.md +55 -0
- package/commands/check-todos.md +87 -0
- package/commands/compare-methods.md +262 -0
- package/commands/complete-milestone.md +225 -0
- package/commands/debug.md +372 -0
- package/commands/deep-dive.md +288 -0
- package/commands/discover.md +281 -0
- package/commands/discuss-phase.md +188 -0
- package/commands/discuss.md +55 -0
- package/commands/eval-report.md +310 -0
- package/commands/evolve.md +79 -0
- package/commands/execute-phase.md +1017 -0
- package/commands/feasibility.md +292 -0
- package/commands/help.md +407 -0
- package/commands/init.md +1508 -0
- package/commands/insert-phase.md +113 -0
- package/commands/iterate.md +327 -0
- package/commands/list-phase-assumptions.md +217 -0
- package/commands/long-term-roadmap.md +202 -0
- package/commands/map-codebase.md +111 -0
- package/commands/migrate.md +159 -0
- package/commands/new-milestone.md +169 -0
- package/commands/pause-work.md +83 -0
- package/commands/plan-milestone-gaps.md +373 -0
- package/commands/plan-phase.md +655 -0
- package/commands/principles.md +328 -0
- package/commands/product-plan.md +319 -0
- package/commands/progress.md +481 -0
- package/commands/quick.md +167 -0
- package/commands/reapply-patches.md +154 -0
- package/commands/remove-phase.md +97 -0
- package/commands/requirement.md +96 -0
- package/commands/resume-project.md +113 -0
- package/commands/settings.md +1144 -0
- package/commands/survey.md +242 -0
- package/commands/sync.md +246 -0
- package/commands/tracker-setup.md +322 -0
- package/commands/update.md +202 -0
- package/commands/verify-phase.md +335 -0
- package/commands/verify-work.md +701 -0
- package/commands/wireup.md +29 -0
- package/dist/bin/gd.d.ts +3 -0
- package/dist/bin/gd.d.ts.map +1 -0
- package/dist/bin/gd.js +178 -0
- package/dist/bin/gd.js.map +1 -0
- package/dist/bin/grd-manifest.d.ts +3 -0
- package/dist/bin/grd-manifest.d.ts.map +1 -0
- package/dist/bin/grd-manifest.js +202 -0
- package/dist/bin/grd-manifest.js.map +1 -0
- package/dist/bin/grd-mcp-server.d.ts +3 -0
- package/dist/bin/grd-mcp-server.d.ts.map +1 -0
- package/dist/bin/grd-mcp-server.js +71 -0
- package/dist/bin/grd-mcp-server.js.map +1 -0
- package/dist/bin/grd-tools.d.ts +3 -0
- package/dist/bin/grd-tools.d.ts.map +1 -0
- package/dist/bin/grd-tools.js +1680 -0
- package/dist/bin/grd-tools.js.map +1 -0
- package/dist/bin/postinstall.d.ts +3 -0
- package/dist/bin/postinstall.d.ts.map +1 -0
- package/dist/bin/postinstall.js +61 -0
- package/dist/bin/postinstall.js.map +1 -0
- package/dist/lib/autopilot-milestone.d.ts +2 -0
- package/dist/lib/autopilot-milestone.d.ts.map +1 -0
- package/dist/lib/autopilot-milestone.js +94 -0
- package/dist/lib/autopilot-milestone.js.map +1 -0
- package/dist/lib/autopilot-pipeline.d.ts +2 -0
- package/dist/lib/autopilot-pipeline.d.ts.map +1 -0
- package/dist/lib/autopilot-pipeline.js +830 -0
- package/dist/lib/autopilot-pipeline.js.map +1 -0
- package/dist/lib/autopilot-waves.d.ts +2 -0
- package/dist/lib/autopilot-waves.d.ts.map +1 -0
- package/dist/lib/autopilot-waves.js +266 -0
- package/dist/lib/autopilot-waves.js.map +1 -0
- package/dist/lib/autopilot.d.ts +2 -0
- package/dist/lib/autopilot.d.ts.map +1 -0
- package/dist/lib/autopilot.js +1314 -0
- package/dist/lib/autopilot.js.map +1 -0
- package/dist/lib/autoplan.d.ts +2 -0
- package/dist/lib/autoplan.d.ts.map +1 -0
- package/dist/lib/autoplan.js +198 -0
- package/dist/lib/autoplan.js.map +1 -0
- package/dist/lib/autoresearch.d.ts +2 -0
- package/dist/lib/autoresearch.d.ts.map +1 -0
- package/dist/lib/autoresearch.js +626 -0
- package/dist/lib/autoresearch.js.map +1 -0
- package/dist/lib/backend.d.ts +2 -0
- package/dist/lib/backend.d.ts.map +1 -0
- package/dist/lib/backend.js +1036 -0
- package/dist/lib/backend.js.map +1 -0
- package/dist/lib/benchmark.d.ts +99 -0
- package/dist/lib/benchmark.d.ts.map +1 -0
- package/dist/lib/benchmark.js +278 -0
- package/dist/lib/benchmark.js.map +1 -0
- package/dist/lib/citations.d.ts +2 -0
- package/dist/lib/citations.d.ts.map +1 -0
- package/dist/lib/citations.js +642 -0
- package/dist/lib/citations.js.map +1 -0
- package/dist/lib/cleanup.d.ts +2 -0
- package/dist/lib/cleanup.d.ts.map +1 -0
- package/dist/lib/cleanup.js +1222 -0
- package/dist/lib/cleanup.js.map +1 -0
- package/dist/lib/cli/adapters.d.ts +10 -0
- package/dist/lib/cli/adapters.d.ts.map +1 -0
- package/dist/lib/cli/adapters.js +27 -0
- package/dist/lib/cli/adapters.js.map +1 -0
- package/dist/lib/cli/agent.d.ts +17 -0
- package/dist/lib/cli/agent.d.ts.map +1 -0
- package/dist/lib/cli/agent.js +53 -0
- package/dist/lib/cli/agent.js.map +1 -0
- package/dist/lib/cli/index.d.ts +21 -0
- package/dist/lib/cli/index.d.ts.map +1 -0
- package/dist/lib/cli/index.js +264 -0
- package/dist/lib/cli/index.js.map +1 -0
- package/dist/lib/cli/output.d.ts +20 -0
- package/dist/lib/cli/output.d.ts.map +1 -0
- package/dist/lib/cli/output.js +22 -0
- package/dist/lib/cli/output.js.map +1 -0
- package/dist/lib/cli/scan-dispatch.d.ts +9 -0
- package/dist/lib/cli/scan-dispatch.d.ts.map +1 -0
- package/dist/lib/cli/scan-dispatch.js +107 -0
- package/dist/lib/cli/scan-dispatch.js.map +1 -0
- package/dist/lib/cli/tools.d.ts +16 -0
- package/dist/lib/cli/tools.d.ts.map +1 -0
- package/dist/lib/cli/tools.js +168 -0
- package/dist/lib/cli/tools.js.map +1 -0
- package/dist/lib/commands/_dashboard-parsers.d.ts +2 -0
- package/dist/lib/commands/_dashboard-parsers.d.ts.map +1 -0
- package/dist/lib/commands/_dashboard-parsers.js +192 -0
- package/dist/lib/commands/_dashboard-parsers.js.map +1 -0
- package/dist/lib/commands/analysis.d.ts +2 -0
- package/dist/lib/commands/analysis.d.ts.map +1 -0
- package/dist/lib/commands/analysis.js +1418 -0
- package/dist/lib/commands/analysis.js.map +1 -0
- package/dist/lib/commands/assumptions.d.ts +2 -0
- package/dist/lib/commands/assumptions.d.ts.map +1 -0
- package/dist/lib/commands/assumptions.js +166 -0
- package/dist/lib/commands/assumptions.js.map +1 -0
- package/dist/lib/commands/blame.d.ts +2 -0
- package/dist/lib/commands/blame.d.ts.map +1 -0
- package/dist/lib/commands/blame.js +133 -0
- package/dist/lib/commands/blame.js.map +1 -0
- package/dist/lib/commands/budget.d.ts +2 -0
- package/dist/lib/commands/budget.d.ts.map +1 -0
- package/dist/lib/commands/budget.js +100 -0
- package/dist/lib/commands/budget.js.map +1 -0
- package/dist/lib/commands/check-plans.d.ts +2 -0
- package/dist/lib/commands/check-plans.d.ts.map +1 -0
- package/dist/lib/commands/check-plans.js +190 -0
- package/dist/lib/commands/check-plans.js.map +1 -0
- package/dist/lib/commands/config.d.ts +2 -0
- package/dist/lib/commands/config.d.ts.map +1 -0
- package/dist/lib/commands/config.js +188 -0
- package/dist/lib/commands/config.js.map +1 -0
- package/dist/lib/commands/dashboard.d.ts +2 -0
- package/dist/lib/commands/dashboard.d.ts.map +1 -0
- package/dist/lib/commands/dashboard.js +466 -0
- package/dist/lib/commands/dashboard.js.map +1 -0
- package/dist/lib/commands/estimate.d.ts +2 -0
- package/dist/lib/commands/estimate.d.ts.map +1 -0
- package/dist/lib/commands/estimate.js +148 -0
- package/dist/lib/commands/estimate.js.map +1 -0
- package/dist/lib/commands/eval-diff.d.ts +2 -0
- package/dist/lib/commands/eval-diff.d.ts.map +1 -0
- package/dist/lib/commands/eval-diff.js +213 -0
- package/dist/lib/commands/eval-diff.js.map +1 -0
- package/dist/lib/commands/freshness.d.ts +2 -0
- package/dist/lib/commands/freshness.d.ts.map +1 -0
- package/dist/lib/commands/freshness.js +163 -0
- package/dist/lib/commands/freshness.js.map +1 -0
- package/dist/lib/commands/health.d.ts +2 -0
- package/dist/lib/commands/health.d.ts.map +1 -0
- package/dist/lib/commands/health.js +435 -0
- package/dist/lib/commands/health.js.map +1 -0
- package/dist/lib/commands/index.d.ts +2 -0
- package/dist/lib/commands/index.d.ts.map +1 -0
- package/dist/lib/commands/index.js +128 -0
- package/dist/lib/commands/index.js.map +1 -0
- package/dist/lib/commands/install.d.ts +56 -0
- package/dist/lib/commands/install.d.ts.map +1 -0
- package/dist/lib/commands/install.js +214 -0
- package/dist/lib/commands/install.js.map +1 -0
- package/dist/lib/commands/knowhow-aggregator.d.ts +2 -0
- package/dist/lib/commands/knowhow-aggregator.d.ts.map +1 -0
- package/dist/lib/commands/knowhow-aggregator.js +279 -0
- package/dist/lib/commands/knowhow-aggregator.js.map +1 -0
- package/dist/lib/commands/knowledge-search.d.ts +2 -0
- package/dist/lib/commands/knowledge-search.d.ts.map +1 -0
- package/dist/lib/commands/knowledge-search.js +113 -0
- package/dist/lib/commands/knowledge-search.js.map +1 -0
- package/dist/lib/commands/long-term-roadmap.d.ts +2 -0
- package/dist/lib/commands/long-term-roadmap.d.ts.map +1 -0
- package/dist/lib/commands/long-term-roadmap.js +272 -0
- package/dist/lib/commands/long-term-roadmap.js.map +1 -0
- package/dist/lib/commands/patterns.d.ts +91 -0
- package/dist/lib/commands/patterns.d.ts.map +1 -0
- package/dist/lib/commands/patterns.js +391 -0
- package/dist/lib/commands/patterns.js.map +1 -0
- package/dist/lib/commands/phase-info.d.ts +2 -0
- package/dist/lib/commands/phase-info.d.ts.map +1 -0
- package/dist/lib/commands/phase-info.js +509 -0
- package/dist/lib/commands/phase-info.js.map +1 -0
- package/dist/lib/commands/plan-lint.d.ts +56 -0
- package/dist/lib/commands/plan-lint.d.ts.map +1 -0
- package/dist/lib/commands/plan-lint.js +481 -0
- package/dist/lib/commands/plan-lint.js.map +1 -0
- package/dist/lib/commands/plan-phase.d.ts +53 -0
- package/dist/lib/commands/plan-phase.d.ts.map +1 -0
- package/dist/lib/commands/plan-phase.js +288 -0
- package/dist/lib/commands/plan-phase.js.map +1 -0
- package/dist/lib/commands/progress.d.ts +2 -0
- package/dist/lib/commands/progress.d.ts.map +1 -0
- package/dist/lib/commands/progress.js +266 -0
- package/dist/lib/commands/progress.js.map +1 -0
- package/dist/lib/commands/quality.d.ts +2 -0
- package/dist/lib/commands/quality.d.ts.map +1 -0
- package/dist/lib/commands/quality.js +80 -0
- package/dist/lib/commands/quality.js.map +1 -0
- package/dist/lib/commands/rollback.d.ts +2 -0
- package/dist/lib/commands/rollback.d.ts.map +1 -0
- package/dist/lib/commands/rollback.js +145 -0
- package/dist/lib/commands/rollback.js.map +1 -0
- package/dist/lib/commands/scan.d.ts +25 -0
- package/dist/lib/commands/scan.d.ts.map +1 -0
- package/dist/lib/commands/scan.js +28 -0
- package/dist/lib/commands/scan.js.map +1 -0
- package/dist/lib/commands/search.d.ts +2 -0
- package/dist/lib/commands/search.d.ts.map +1 -0
- package/dist/lib/commands/search.js +212 -0
- package/dist/lib/commands/search.js.map +1 -0
- package/dist/lib/commands/select-candidate.d.ts +128 -0
- package/dist/lib/commands/select-candidate.d.ts.map +1 -0
- package/dist/lib/commands/select-candidate.js +518 -0
- package/dist/lib/commands/select-candidate.js.map +1 -0
- package/dist/lib/commands/singularity.d.ts +2 -0
- package/dist/lib/commands/singularity.d.ts.map +1 -0
- package/dist/lib/commands/singularity.js +185 -0
- package/dist/lib/commands/singularity.js.map +1 -0
- package/dist/lib/commands/slug-timestamp.d.ts +2 -0
- package/dist/lib/commands/slug-timestamp.d.ts.map +1 -0
- package/dist/lib/commands/slug-timestamp.js +54 -0
- package/dist/lib/commands/slug-timestamp.js.map +1 -0
- package/dist/lib/commands/tail.d.ts +2 -0
- package/dist/lib/commands/tail.d.ts.map +1 -0
- package/dist/lib/commands/tail.js +100 -0
- package/dist/lib/commands/tail.js.map +1 -0
- package/dist/lib/commands/todo.d.ts +2 -0
- package/dist/lib/commands/todo.d.ts.map +1 -0
- package/dist/lib/commands/todo.js +200 -0
- package/dist/lib/commands/todo.js.map +1 -0
- package/dist/lib/commands/watch.d.ts +2 -0
- package/dist/lib/commands/watch.d.ts.map +1 -0
- package/dist/lib/commands/watch.js +72 -0
- package/dist/lib/commands/watch.js.map +1 -0
- package/dist/lib/complexity.d.ts +55 -0
- package/dist/lib/complexity.d.ts.map +1 -0
- package/dist/lib/complexity.js +80 -0
- package/dist/lib/complexity.js.map +1 -0
- package/dist/lib/context/agents.d.ts +2 -0
- package/dist/lib/context/agents.d.ts.map +1 -0
- package/dist/lib/context/agents.js +344 -0
- package/dist/lib/context/agents.js.map +1 -0
- package/dist/lib/context/base.d.ts +2 -0
- package/dist/lib/context/base.d.ts.map +1 -0
- package/dist/lib/context/base.js +81 -0
- package/dist/lib/context/base.js.map +1 -0
- package/dist/lib/context/execute.d.ts +2 -0
- package/dist/lib/context/execute.d.ts.map +1 -0
- package/dist/lib/context/execute.js +753 -0
- package/dist/lib/context/execute.js.map +1 -0
- package/dist/lib/context/index.d.ts +2 -0
- package/dist/lib/context/index.d.ts.map +1 -0
- package/dist/lib/context/index.js +88 -0
- package/dist/lib/context/index.js.map +1 -0
- package/dist/lib/context/progress.d.ts +2 -0
- package/dist/lib/context/progress.d.ts.map +1 -0
- package/dist/lib/context/progress.js +178 -0
- package/dist/lib/context/progress.js.map +1 -0
- package/dist/lib/context/project.d.ts +2 -0
- package/dist/lib/context/project.d.ts.map +1 -0
- package/dist/lib/context/project.js +413 -0
- package/dist/lib/context/project.js.map +1 -0
- package/dist/lib/context/research.d.ts +2 -0
- package/dist/lib/context/research.d.ts.map +1 -0
- package/dist/lib/context/research.js +466 -0
- package/dist/lib/context/research.js.map +1 -0
- package/dist/lib/dead-ends.d.ts +28 -0
- package/dist/lib/dead-ends.d.ts.map +1 -0
- package/dist/lib/dead-ends.js +451 -0
- package/dist/lib/dead-ends.js.map +1 -0
- package/dist/lib/deps.d.ts +2 -0
- package/dist/lib/deps.d.ts.map +1 -0
- package/dist/lib/deps.js +630 -0
- package/dist/lib/deps.js.map +1 -0
- package/dist/lib/discussion.d.ts +2 -0
- package/dist/lib/discussion.d.ts.map +1 -0
- package/dist/lib/discussion.js +1041 -0
- package/dist/lib/discussion.js.map +1 -0
- package/dist/lib/drift.d.ts +36 -0
- package/dist/lib/drift.d.ts.map +1 -0
- package/dist/lib/drift.js +481 -0
- package/dist/lib/drift.js.map +1 -0
- package/dist/lib/evolve/_dimensions-features.d.ts +2 -0
- package/dist/lib/evolve/_dimensions-features.d.ts.map +1 -0
- package/dist/lib/evolve/_dimensions-features.js +369 -0
- package/dist/lib/evolve/_dimensions-features.js.map +1 -0
- package/dist/lib/evolve/_dimensions.d.ts +2 -0
- package/dist/lib/evolve/_dimensions.d.ts.map +1 -0
- package/dist/lib/evolve/_dimensions.js +358 -0
- package/dist/lib/evolve/_dimensions.js.map +1 -0
- package/dist/lib/evolve/_product-ideation.d.ts +2 -0
- package/dist/lib/evolve/_product-ideation.d.ts.map +1 -0
- package/dist/lib/evolve/_product-ideation.js +281 -0
- package/dist/lib/evolve/_product-ideation.js.map +1 -0
- package/dist/lib/evolve/_prompts.d.ts +2 -0
- package/dist/lib/evolve/_prompts.d.ts.map +1 -0
- package/dist/lib/evolve/_prompts.js +153 -0
- package/dist/lib/evolve/_prompts.js.map +1 -0
- package/dist/lib/evolve/cli.d.ts +2 -0
- package/dist/lib/evolve/cli.d.ts.map +1 -0
- package/dist/lib/evolve/cli.js +224 -0
- package/dist/lib/evolve/cli.js.map +1 -0
- package/dist/lib/evolve/discovery.d.ts +2 -0
- package/dist/lib/evolve/discovery.d.ts.map +1 -0
- package/dist/lib/evolve/discovery.js +391 -0
- package/dist/lib/evolve/discovery.js.map +1 -0
- package/dist/lib/evolve/index.d.ts +2 -0
- package/dist/lib/evolve/index.d.ts.map +1 -0
- package/dist/lib/evolve/index.js +88 -0
- package/dist/lib/evolve/index.js.map +1 -0
- package/dist/lib/evolve/orchestrator.d.ts +2 -0
- package/dist/lib/evolve/orchestrator.d.ts.map +1 -0
- package/dist/lib/evolve/orchestrator.js +851 -0
- package/dist/lib/evolve/orchestrator.js.map +1 -0
- package/dist/lib/evolve/scoring.d.ts +2 -0
- package/dist/lib/evolve/scoring.d.ts.map +1 -0
- package/dist/lib/evolve/scoring.js +118 -0
- package/dist/lib/evolve/scoring.js.map +1 -0
- package/dist/lib/evolve/state.d.ts +2 -0
- package/dist/lib/evolve/state.d.ts.map +1 -0
- package/dist/lib/evolve/state.js +264 -0
- package/dist/lib/evolve/state.js.map +1 -0
- package/dist/lib/evolve/types.d.ts +249 -0
- package/dist/lib/evolve/types.d.ts.map +1 -0
- package/dist/lib/evolve/types.js +3 -0
- package/dist/lib/evolve/types.js.map +1 -0
- package/dist/lib/frontmatter.d.ts +2 -0
- package/dist/lib/frontmatter.d.ts.map +1 -0
- package/dist/lib/frontmatter.js +513 -0
- package/dist/lib/frontmatter.js.map +1 -0
- package/dist/lib/gates.d.ts +2 -0
- package/dist/lib/gates.d.ts.map +1 -0
- package/dist/lib/gates.js +578 -0
- package/dist/lib/gates.js.map +1 -0
- package/dist/lib/genome.d.ts +10 -0
- package/dist/lib/genome.d.ts.map +1 -0
- package/dist/lib/genome.js +368 -0
- package/dist/lib/genome.js.map +1 -0
- package/dist/lib/got.d.ts +2 -0
- package/dist/lib/got.d.ts.map +1 -0
- package/dist/lib/got.js +280 -0
- package/dist/lib/got.js.map +1 -0
- package/dist/lib/invariants.d.ts +2 -0
- package/dist/lib/invariants.d.ts.map +1 -0
- package/dist/lib/invariants.js +298 -0
- package/dist/lib/invariants.js.map +1 -0
- package/dist/lib/knowledge.d.ts +2 -0
- package/dist/lib/knowledge.d.ts.map +1 -0
- package/dist/lib/knowledge.js +658 -0
- package/dist/lib/knowledge.js.map +1 -0
- package/dist/lib/long-term-roadmap.d.ts +2 -0
- package/dist/lib/long-term-roadmap.d.ts.map +1 -0
- package/dist/lib/long-term-roadmap.js +602 -0
- package/dist/lib/long-term-roadmap.js.map +1 -0
- package/dist/lib/markdown-split.d.ts +2 -0
- package/dist/lib/markdown-split.d.ts.map +1 -0
- package/dist/lib/markdown-split.js +199 -0
- package/dist/lib/markdown-split.js.map +1 -0
- package/dist/lib/mcp-server.d.ts +2 -0
- package/dist/lib/mcp-server.d.ts.map +1 -0
- package/dist/lib/mcp-server.js +2424 -0
- package/dist/lib/mcp-server.js.map +1 -0
- package/dist/lib/metrics.d.ts +16 -0
- package/dist/lib/metrics.d.ts.map +1 -0
- package/dist/lib/metrics.js +48 -0
- package/dist/lib/metrics.js.map +1 -0
- package/dist/lib/overstory.d.ts +2 -0
- package/dist/lib/overstory.d.ts.map +1 -0
- package/dist/lib/overstory.js +211 -0
- package/dist/lib/overstory.js.map +1 -0
- package/dist/lib/parallel.d.ts +2 -0
- package/dist/lib/parallel.d.ts.map +1 -0
- package/dist/lib/parallel.js +349 -0
- package/dist/lib/parallel.js.map +1 -0
- package/dist/lib/paths.d.ts +2 -0
- package/dist/lib/paths.d.ts.map +1 -0
- package/dist/lib/paths.js +254 -0
- package/dist/lib/paths.js.map +1 -0
- package/dist/lib/phase-complete-llm.d.ts +22 -0
- package/dist/lib/phase-complete-llm.d.ts.map +1 -0
- package/dist/lib/phase-complete-llm.js +331 -0
- package/dist/lib/phase-complete-llm.js.map +1 -0
- package/dist/lib/phase-complete.d.ts +46 -0
- package/dist/lib/phase-complete.d.ts.map +1 -0
- package/dist/lib/phase-complete.js +278 -0
- package/dist/lib/phase-complete.js.map +1 -0
- package/dist/lib/phase-io.d.ts +2 -0
- package/dist/lib/phase-io.d.ts.map +1 -0
- package/dist/lib/phase-io.js +126 -0
- package/dist/lib/phase-io.js.map +1 -0
- package/dist/lib/phase.d.ts +2 -0
- package/dist/lib/phase.d.ts.map +1 -0
- package/dist/lib/phase.js +1344 -0
- package/dist/lib/phase.js.map +1 -0
- package/dist/lib/plan-tournament.d.ts +63 -0
- package/dist/lib/plan-tournament.d.ts.map +1 -0
- package/dist/lib/plan-tournament.js +353 -0
- package/dist/lib/plan-tournament.js.map +1 -0
- package/dist/lib/refinement.d.ts +74 -0
- package/dist/lib/refinement.d.ts.map +1 -0
- package/dist/lib/refinement.js +283 -0
- package/dist/lib/refinement.js.map +1 -0
- package/dist/lib/requirements.d.ts +2 -0
- package/dist/lib/requirements.d.ts.map +1 -0
- package/dist/lib/requirements.js +355 -0
- package/dist/lib/requirements.js.map +1 -0
- package/dist/lib/research-bundle.d.ts +2 -0
- package/dist/lib/research-bundle.d.ts.map +1 -0
- package/dist/lib/research-bundle.js +246 -0
- package/dist/lib/research-bundle.js.map +1 -0
- package/dist/lib/roadmap.d.ts +2 -0
- package/dist/lib/roadmap.d.ts.map +1 -0
- package/dist/lib/roadmap.js +541 -0
- package/dist/lib/roadmap.js.map +1 -0
- package/dist/lib/sample.d.ts +16 -0
- package/dist/lib/sample.d.ts.map +1 -0
- package/dist/lib/sample.js +20 -0
- package/dist/lib/sample.js.map +1 -0
- package/dist/lib/scaffold.d.ts +2 -0
- package/dist/lib/scaffold.d.ts.map +1 -0
- package/dist/lib/scaffold.js +355 -0
- package/dist/lib/scaffold.js.map +1 -0
- package/dist/lib/scan/_utils.d.ts +11 -0
- package/dist/lib/scan/_utils.d.ts.map +1 -0
- package/dist/lib/scan/_utils.js +36 -0
- package/dist/lib/scan/_utils.js.map +1 -0
- package/dist/lib/scan/base64.d.ts +15 -0
- package/dist/lib/scan/base64.d.ts.map +1 -0
- package/dist/lib/scan/base64.js +66 -0
- package/dist/lib/scan/base64.js.map +1 -0
- package/dist/lib/scan/ignorefile.d.ts +30 -0
- package/dist/lib/scan/ignorefile.d.ts.map +1 -0
- package/dist/lib/scan/ignorefile.js +101 -0
- package/dist/lib/scan/ignorefile.js.map +1 -0
- package/dist/lib/scan/injection.d.ts +14 -0
- package/dist/lib/scan/injection.d.ts.map +1 -0
- package/dist/lib/scan/injection.js +39 -0
- package/dist/lib/scan/injection.js.map +1 -0
- package/dist/lib/scan/patterns.d.ts +17 -0
- package/dist/lib/scan/patterns.d.ts.map +1 -0
- package/dist/lib/scan/patterns.js +123 -0
- package/dist/lib/scan/patterns.js.map +1 -0
- package/dist/lib/scan/strip-markdown.d.ts +7 -0
- package/dist/lib/scan/strip-markdown.d.ts.map +1 -0
- package/dist/lib/scan/strip-markdown.js +38 -0
- package/dist/lib/scan/strip-markdown.js.map +1 -0
- package/dist/lib/scan/types.d.ts +23 -0
- package/dist/lib/scan/types.d.ts.map +1 -0
- package/dist/lib/scan/types.js +3 -0
- package/dist/lib/scan/types.js.map +1 -0
- package/dist/lib/scheduler-wait.d.ts +2 -0
- package/dist/lib/scheduler-wait.d.ts.map +1 -0
- package/dist/lib/scheduler-wait.js +59 -0
- package/dist/lib/scheduler-wait.js.map +1 -0
- package/dist/lib/scheduler.d.ts +254 -0
- package/dist/lib/scheduler.d.ts.map +1 -0
- package/dist/lib/scheduler.js +1147 -0
- package/dist/lib/scheduler.js.map +1 -0
- package/dist/lib/state.d.ts +2 -0
- package/dist/lib/state.d.ts.map +1 -0
- package/dist/lib/state.js +744 -0
- package/dist/lib/state.js.map +1 -0
- package/dist/lib/think.d.ts +18 -0
- package/dist/lib/think.d.ts.map +1 -0
- package/dist/lib/think.js +317 -0
- package/dist/lib/think.js.map +1 -0
- package/dist/lib/tracker.d.ts +2 -0
- package/dist/lib/tracker.d.ts.map +1 -0
- package/dist/lib/tracker.js +1121 -0
- package/dist/lib/tracker.js.map +1 -0
- package/dist/lib/types.d.ts +1514 -0
- package/dist/lib/types.d.ts.map +1 -0
- package/dist/lib/types.js +4 -0
- package/dist/lib/types.js.map +1 -0
- package/dist/lib/utils.d.ts +2 -0
- package/dist/lib/utils.d.ts.map +1 -0
- package/dist/lib/utils.js +1363 -0
- package/dist/lib/utils.js.map +1 -0
- package/dist/lib/verify.d.ts +2 -0
- package/dist/lib/verify.d.ts.map +1 -0
- package/dist/lib/verify.js +1153 -0
- package/dist/lib/verify.js.map +1 -0
- package/dist/lib/wireup/autofix.d.ts +2 -0
- package/dist/lib/wireup/autofix.d.ts.map +1 -0
- package/dist/lib/wireup/autofix.js +188 -0
- package/dist/lib/wireup/autofix.js.map +1 -0
- package/dist/lib/wireup/cli.d.ts +2 -0
- package/dist/lib/wireup/cli.d.ts.map +1 -0
- package/dist/lib/wireup/cli.js +194 -0
- package/dist/lib/wireup/cli.js.map +1 -0
- package/dist/lib/wireup/detection.d.ts +47 -0
- package/dist/lib/wireup/detection.d.ts.map +1 -0
- package/dist/lib/wireup/detection.js +410 -0
- package/dist/lib/wireup/detection.js.map +1 -0
- package/dist/lib/wireup/discovery.d.ts +2 -0
- package/dist/lib/wireup/discovery.d.ts.map +1 -0
- package/dist/lib/wireup/discovery.js +934 -0
- package/dist/lib/wireup/discovery.js.map +1 -0
- package/dist/lib/wireup/execution.d.ts +2 -0
- package/dist/lib/wireup/execution.d.ts.map +1 -0
- package/dist/lib/wireup/execution.js +573 -0
- package/dist/lib/wireup/execution.js.map +1 -0
- package/dist/lib/wireup/index.d.ts +2 -0
- package/dist/lib/wireup/index.d.ts.map +1 -0
- package/dist/lib/wireup/index.js +85 -0
- package/dist/lib/wireup/index.js.map +1 -0
- package/dist/lib/wireup/orchestrator.d.ts +2 -0
- package/dist/lib/wireup/orchestrator.d.ts.map +1 -0
- package/dist/lib/wireup/orchestrator.js +366 -0
- package/dist/lib/wireup/orchestrator.js.map +1 -0
- package/dist/lib/wireup/report.d.ts +47 -0
- package/dist/lib/wireup/report.d.ts.map +1 -0
- package/dist/lib/wireup/report.js +201 -0
- package/dist/lib/wireup/report.js.map +1 -0
- package/dist/lib/wireup/scenarios.d.ts +2 -0
- package/dist/lib/wireup/scenarios.d.ts.map +1 -0
- package/dist/lib/wireup/scenarios.js +516 -0
- package/dist/lib/wireup/scenarios.js.map +1 -0
- package/dist/lib/wireup/state.d.ts +2 -0
- package/dist/lib/wireup/state.d.ts.map +1 -0
- package/dist/lib/wireup/state.js +102 -0
- package/dist/lib/wireup/state.js.map +1 -0
- package/dist/lib/wireup/types.d.ts +376 -0
- package/dist/lib/wireup/types.d.ts.map +1 -0
- package/dist/lib/wireup/types.js +3 -0
- package/dist/lib/wireup/types.js.map +1 -0
- package/dist/lib/worktree.d.ts +2 -0
- package/dist/lib/worktree.d.ts.map +1 -0
- package/dist/lib/worktree.js +999 -0
- package/dist/lib/worktree.js.map +1 -0
- package/lib/autopilot-milestone.ts +136 -0
- package/lib/autopilot-pipeline.ts +1179 -0
- package/lib/autopilot-waves.ts +361 -0
- package/lib/autopilot.ts +1874 -0
- package/lib/autoplan.ts +280 -0
- package/lib/autoresearch.js +4 -0
- package/lib/autoresearch.ts +886 -0
- package/lib/backend.ts +1252 -0
- package/lib/benchmark.ts +341 -0
- package/lib/citations.ts +760 -0
- package/lib/cleanup.ts +1588 -0
- package/lib/cli/adapters.ts +41 -0
- package/lib/cli/agent.ts +83 -0
- package/lib/cli/index.ts +273 -0
- package/lib/cli/output.ts +33 -0
- package/lib/cli/scan-dispatch.ts +130 -0
- package/lib/cli/tools.ts +198 -0
- package/lib/commands/_dashboard-parsers.ts +275 -0
- package/lib/commands/analysis.ts +1851 -0
- package/lib/commands/assumptions.ts +232 -0
- package/lib/commands/blame.ts +174 -0
- package/lib/commands/budget.ts +148 -0
- package/lib/commands/check-plans.ts +233 -0
- package/lib/commands/config.ts +287 -0
- package/lib/commands/dashboard.ts +680 -0
- package/lib/commands/estimate.ts +204 -0
- package/lib/commands/eval-diff.ts +252 -0
- package/lib/commands/freshness.ts +213 -0
- package/lib/commands/health.ts +607 -0
- package/lib/commands/index.ts +266 -0
- package/lib/commands/install.ts +307 -0
- package/lib/commands/knowhow-aggregator.ts +345 -0
- package/lib/commands/knowledge-search.ts +153 -0
- package/lib/commands/long-term-roadmap.ts +390 -0
- package/lib/commands/patterns.ts +465 -0
- package/lib/commands/phase-info.ts +698 -0
- package/lib/commands/plan-lint.ts +546 -0
- package/lib/commands/plan-phase.ts +375 -0
- package/lib/commands/progress.ts +319 -0
- package/lib/commands/quality.ts +138 -0
- package/lib/commands/rollback.ts +195 -0
- package/lib/commands/scan.ts +72 -0
- package/lib/commands/search.ts +300 -0
- package/lib/commands/select-candidate.ts +687 -0
- package/lib/commands/singularity.ts +222 -0
- package/lib/commands/slug-timestamp.ts +74 -0
- package/lib/commands/tail.ts +129 -0
- package/lib/commands/todo.ts +273 -0
- package/lib/commands/watch.ts +80 -0
- package/lib/complexity.ts +117 -0
- package/lib/context/agents.ts +505 -0
- package/lib/context/base.ts +123 -0
- package/lib/context/execute.ts +977 -0
- package/lib/context/index.ts +110 -0
- package/lib/context/progress.ts +278 -0
- package/lib/context/project.ts +531 -0
- package/lib/context/research.ts +646 -0
- package/lib/dead-ends.ts +506 -0
- package/lib/deps.ts +773 -0
- package/lib/discussion.ts +1275 -0
- package/lib/drift.ts +519 -0
- package/lib/evolve/_dimensions-features.ts +525 -0
- package/lib/evolve/_dimensions.ts +511 -0
- package/lib/evolve/_product-ideation.ts +405 -0
- package/lib/evolve/_prompts.ts +178 -0
- package/lib/evolve/cli.ts +330 -0
- package/lib/evolve/discovery.ts +571 -0
- package/lib/evolve/index.ts +105 -0
- package/lib/evolve/orchestrator.ts +1139 -0
- package/lib/evolve/scoring.ts +167 -0
- package/lib/evolve/state.ts +330 -0
- package/lib/evolve/types.ts +290 -0
- package/lib/frontmatter.ts +615 -0
- package/lib/gates.ts +695 -0
- package/lib/genome.ts +402 -0
- package/lib/got.js +4 -0
- package/lib/got.ts +361 -0
- package/lib/invariants.ts +378 -0
- package/lib/knowledge.ts +768 -0
- package/lib/long-term-roadmap.ts +806 -0
- package/lib/markdown-split.ts +273 -0
- package/lib/mcp-server.ts +3292 -0
- package/lib/metrics.ts +49 -0
- package/lib/overstory.ts +270 -0
- package/lib/parallel.ts +570 -0
- package/lib/paths.ts +293 -0
- package/lib/phase-complete-llm.ts +376 -0
- package/lib/phase-complete.ts +366 -0
- package/lib/phase-io.ts +101 -0
- package/lib/phase.ts +1981 -0
- package/lib/plan-tournament.ts +426 -0
- package/lib/refinement.ts +349 -0
- package/lib/requirements.ts +469 -0
- package/lib/research-bundle.ts +300 -0
- package/lib/roadmap.ts +775 -0
- package/lib/scaffold.ts +480 -0
- package/lib/scan/_utils.ts +37 -0
- package/lib/scan/base64.ts +90 -0
- package/lib/scan/ignorefile.ts +109 -0
- package/lib/scan/injection.ts +67 -0
- package/lib/scan/patterns.ts +139 -0
- package/lib/scan/strip-markdown.ts +39 -0
- package/lib/scan/types.ts +28 -0
- package/lib/scheduler-wait.ts +58 -0
- package/lib/scheduler.ts +1370 -0
- package/lib/state.ts +1000 -0
- package/lib/think.ts +365 -0
- package/lib/tracker.ts +1591 -0
- package/lib/types.ts +1663 -0
- package/lib/utils.ts +1479 -0
- package/lib/verify.ts +1434 -0
- package/lib/wireup/autofix.ts +241 -0
- package/lib/wireup/cli.ts +278 -0
- package/lib/wireup/detection.ts +542 -0
- package/lib/wireup/discovery.ts +1063 -0
- package/lib/wireup/execution.ts +686 -0
- package/lib/wireup/index.ts +117 -0
- package/lib/wireup/orchestrator.ts +519 -0
- package/lib/wireup/report.ts +286 -0
- package/lib/wireup/scenarios.ts +616 -0
- package/lib/wireup/state.ts +139 -0
- package/lib/wireup/types.ts +436 -0
- package/lib/worktree.ts +1309 -0
- package/package.json +67 -0
|
@@ -0,0 +1,687 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* GRD Commands/SelectCandidate — v0.4 Phase 3 deterministic selector.
|
|
5
|
+
*
|
|
6
|
+
* Picks one PLAN-N.md candidate from a phase directory and promotes it
|
|
7
|
+
* to canonical PLAN.md. Extends the v0.3.x lib/plan-tournament.ts
|
|
8
|
+
* scorer with four real-cost / real-signal axes per
|
|
9
|
+
* .planning/milestones/v0.4/phases/03-deterministic-selector/PLAN.md:
|
|
10
|
+
*
|
|
11
|
+
* 1. must_haves coverage — REQUIREMENTS.md artifacts referenced by
|
|
12
|
+
* the candidate's files_modified + task body
|
|
13
|
+
* 2. DEAD-ENDS hard-fail — slug citation OR forbidden_terms exact
|
|
14
|
+
* case-insensitive match; Jaccard advisory only
|
|
15
|
+
* 3. verification_commands axis — runs deterministic checks declared
|
|
16
|
+
* in the candidate's frontmatter (defaults to 0 when absent)
|
|
17
|
+
* 4. cost tiebreaker — fewer estimated tokens wins on score parity
|
|
18
|
+
*
|
|
19
|
+
* Pipeline:
|
|
20
|
+
* 1. Find PLAN-N.md files in phase dir.
|
|
21
|
+
* 2. Score each with the extended axes.
|
|
22
|
+
* 3. Filter out -Infinity (DEAD-ENDS violations).
|
|
23
|
+
* 4. Sort by total score; cost tiebreaker on parity.
|
|
24
|
+
* 5. Rename winner to PLAN.md.
|
|
25
|
+
* 6. Write PLAN-SELECTION.json audit trail (all candidates, all
|
|
26
|
+
* axis scores, hard-fail reasons, Jaccard advisory warnings).
|
|
27
|
+
*
|
|
28
|
+
* No LLM call anywhere. GENOME heuristic "no LLM-judged scoring on the
|
|
29
|
+
* core execution path" honored.
|
|
30
|
+
*
|
|
31
|
+
* Security: verification_commands run via argv-array spawnSync (no
|
|
32
|
+
* shell). Commands are split on whitespace; pipes / redirects / glob
|
|
33
|
+
* expansion are NOT supported (planner declares one binary + args
|
|
34
|
+
* per command).
|
|
35
|
+
*/
|
|
36
|
+
|
|
37
|
+
const fs = require('fs') as typeof import('fs');
|
|
38
|
+
const path = require('path') as typeof import('path');
|
|
39
|
+
|
|
40
|
+
const {
|
|
41
|
+
spawnSync,
|
|
42
|
+
}: { spawnSync: typeof import('child_process').spawnSync } = require('child_process');
|
|
43
|
+
|
|
44
|
+
const {
|
|
45
|
+
findPhaseDir,
|
|
46
|
+
getMilestoneInfo,
|
|
47
|
+
safeReadFile,
|
|
48
|
+
safeReadMarkdown,
|
|
49
|
+
output,
|
|
50
|
+
error,
|
|
51
|
+
}: {
|
|
52
|
+
findPhaseDir: (phasesDir: string, phaseArg: string) => string | null;
|
|
53
|
+
getMilestoneInfo: (cwd: string) => { version: string };
|
|
54
|
+
safeReadFile: (p: string) => string | null;
|
|
55
|
+
safeReadMarkdown: (p: string) => string | null;
|
|
56
|
+
output: (result: unknown, raw: boolean, rawValue?: unknown) => never;
|
|
57
|
+
error: (message: string) => never;
|
|
58
|
+
} = require('../utils');
|
|
59
|
+
|
|
60
|
+
const {
|
|
61
|
+
extractFrontmatter,
|
|
62
|
+
}: { extractFrontmatter: (content: string) => Record<string, unknown> } = require('../frontmatter');
|
|
63
|
+
|
|
64
|
+
const {
|
|
65
|
+
scorePlanCandidate,
|
|
66
|
+
DEFAULT_WEIGHTS,
|
|
67
|
+
extractPlanVocabulary,
|
|
68
|
+
clusterByJaccard,
|
|
69
|
+
PROXIMITY_THRESHOLD,
|
|
70
|
+
}: {
|
|
71
|
+
scorePlanCandidate: (
|
|
72
|
+
candidatePath: string,
|
|
73
|
+
cwd: string,
|
|
74
|
+
phase: string,
|
|
75
|
+
weights?: import('../plan-tournament').ScoringWeights
|
|
76
|
+
) => import('../plan-tournament').CandidateResult;
|
|
77
|
+
DEFAULT_WEIGHTS: import('../plan-tournament').ScoringWeights;
|
|
78
|
+
extractPlanVocabulary: (content: string, fm: Record<string, unknown>) => Set<string>;
|
|
79
|
+
clusterByJaccard: (vocabularies: Set<string>[], threshold?: number) => number[][];
|
|
80
|
+
PROXIMITY_THRESHOLD: number;
|
|
81
|
+
} = require('../plan-tournament');
|
|
82
|
+
|
|
83
|
+
const {
|
|
84
|
+
atomicWriteFileSync,
|
|
85
|
+
}: { atomicWriteFileSync: (filePath: string, data: string) => void } = require('../autopilot-waves');
|
|
86
|
+
|
|
87
|
+
// ─── Types ─────────────────────────────────────────────────────────────────
|
|
88
|
+
|
|
89
|
+
export interface DeadEndEntry {
|
|
90
|
+
slug: string;
|
|
91
|
+
hypothesis: string;
|
|
92
|
+
forbidden_terms: string[];
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export interface ExtendedAxes {
|
|
96
|
+
must_haves_coverage: number;
|
|
97
|
+
verification_commands: number;
|
|
98
|
+
estimated_tokens: number;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export interface AdvisoryWarning {
|
|
102
|
+
dead_end_slug: string;
|
|
103
|
+
jaccard: number;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export interface HardFailReason {
|
|
107
|
+
kind: 'slug_citation' | 'forbidden_term';
|
|
108
|
+
dead_end_slug: string;
|
|
109
|
+
matched: string;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export interface ClusterInfo {
|
|
113
|
+
/** Cluster index (assigned among DEAD-ENDS survivors only). */
|
|
114
|
+
cluster_id: number;
|
|
115
|
+
/** True if this candidate is its cluster's representative (highest score). */
|
|
116
|
+
is_representative: boolean;
|
|
117
|
+
/**
|
|
118
|
+
* relPath of the representative this candidate was merged into. null when
|
|
119
|
+
* this candidate IS the representative. Hard-failed candidates are not
|
|
120
|
+
* clustered and carry no ClusterInfo.
|
|
121
|
+
*/
|
|
122
|
+
merged_into: string | null;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export interface ExtendedCandidateResult {
|
|
126
|
+
path: string;
|
|
127
|
+
relPath: string;
|
|
128
|
+
base_score: number;
|
|
129
|
+
total_score: number;
|
|
130
|
+
base_breakdown: import('../plan-tournament').ScoreBreakdown;
|
|
131
|
+
extended: ExtendedAxes;
|
|
132
|
+
hard_fail: HardFailReason | null;
|
|
133
|
+
advisory_warnings: AdvisoryWarning[];
|
|
134
|
+
/** v0.4 Phase 4: set for DEAD-ENDS survivors after proximity clustering. */
|
|
135
|
+
cluster?: ClusterInfo;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export interface SelectionResult {
|
|
139
|
+
phase: string;
|
|
140
|
+
phaseDir: string;
|
|
141
|
+
candidates: ExtendedCandidateResult[];
|
|
142
|
+
winner: ExtendedCandidateResult | null;
|
|
143
|
+
promoted_to: string | null;
|
|
144
|
+
audit_trail_path: string;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export interface SelectCandidateOptions {
|
|
148
|
+
/** Skip the rename PLAN-N.md → PLAN.md and the audit-trail write. */
|
|
149
|
+
dryRun?: boolean;
|
|
150
|
+
/** Override the milestone (default: read from STATE.md). */
|
|
151
|
+
milestone?: string;
|
|
152
|
+
/**
|
|
153
|
+
* Allow the verification_commands axis to execute commands. Default
|
|
154
|
+
* false (codex review P1) — the axis would otherwise run
|
|
155
|
+
* planner-authored commands during selection, before a plan is chosen.
|
|
156
|
+
*/
|
|
157
|
+
runVerificationCommands?: boolean;
|
|
158
|
+
/**
|
|
159
|
+
* Overwrite an existing resolved PLAN.md when promoting the winner.
|
|
160
|
+
* Default false (codex review P2) — refuse to clobber a PLAN.md that
|
|
161
|
+
* a human or a prior selection already resolved.
|
|
162
|
+
*/
|
|
163
|
+
force?: boolean;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// ─── DEAD-ENDS parsing ────────────────────────────────────────────────────
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Parse DEAD-ENDS.md into structured entries. Format: per-entry
|
|
170
|
+
* `## <slug>` header followed by a fenced YAML block. We only need
|
|
171
|
+
* slug, hypothesis, and forbidden_terms for selector input.
|
|
172
|
+
*/
|
|
173
|
+
export function parseDeadEnds(content: string): DeadEndEntry[] {
|
|
174
|
+
const entries: DeadEndEntry[] = [];
|
|
175
|
+
const sectionRe = /^## ([a-z0-9][a-z0-9-]*)\s*$/gm;
|
|
176
|
+
const matches: Array<{ slug: string; start: number }> = [];
|
|
177
|
+
let m: RegExpExecArray | null;
|
|
178
|
+
while ((m = sectionRe.exec(content)) !== null) {
|
|
179
|
+
matches.push({ slug: m[1], start: m.index });
|
|
180
|
+
}
|
|
181
|
+
for (let i = 0; i < matches.length; i++) {
|
|
182
|
+
const start = matches[i].start;
|
|
183
|
+
const end = i + 1 < matches.length ? matches[i + 1].start : content.length;
|
|
184
|
+
const section = content.slice(start, end);
|
|
185
|
+
const yamlMatch = section.match(/```yaml\s*\n([\s\S]*?)\n```/);
|
|
186
|
+
if (!yamlMatch) continue;
|
|
187
|
+
const yaml = yamlMatch[1];
|
|
188
|
+
const slug = matches[i].slug;
|
|
189
|
+
const hypothesis = extractScalar(yaml, 'hypothesis') ?? '';
|
|
190
|
+
const forbidden_terms = extractStringList(yaml, 'forbidden_terms');
|
|
191
|
+
entries.push({ slug, hypothesis, forbidden_terms });
|
|
192
|
+
}
|
|
193
|
+
return entries;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function extractScalar(yaml: string, key: string): string | null {
|
|
197
|
+
const re = new RegExp(`^${key}:\\s*"([^"\\\\]*(?:\\\\.[^"\\\\]*)*)"\\s*$`, 'm');
|
|
198
|
+
const m = yaml.match(re);
|
|
199
|
+
if (!m) return null;
|
|
200
|
+
return m[1].replace(/\\"/g, '"').replace(/\\\\/g, '\\');
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function extractStringList(yaml: string, key: string): string[] {
|
|
204
|
+
const re = new RegExp(`^${key}:\\s*\\n((?: {2}- .*\\n?)+)`, 'm');
|
|
205
|
+
const m = yaml.match(re);
|
|
206
|
+
if (!m) return [];
|
|
207
|
+
const out: string[] = [];
|
|
208
|
+
for (const line of m[1].split('\n')) {
|
|
209
|
+
const item = line.match(/^ {2}- "([^"\\]*(?:\\.[^"\\]*)*)"\s*$/);
|
|
210
|
+
if (item) out.push(item[1].replace(/\\"/g, '"').replace(/\\\\/g, '\\'));
|
|
211
|
+
}
|
|
212
|
+
return out;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// ─── DEAD-ENDS hard-fail check ────────────────────────────────────────────
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Scan a candidate's text content for DEAD-ENDS violations. Returns the
|
|
219
|
+
* first hard-fail reason found (slug citation OR forbidden_term exact
|
|
220
|
+
* match) and any advisory Jaccard warnings.
|
|
221
|
+
*/
|
|
222
|
+
export function checkDeadEnds(
|
|
223
|
+
candidateText: string,
|
|
224
|
+
deadEnds: DeadEndEntry[]
|
|
225
|
+
): { hardFail: HardFailReason | null; advisory: AdvisoryWarning[] } {
|
|
226
|
+
const lower = candidateText.toLowerCase();
|
|
227
|
+
const advisory: AdvisoryWarning[] = [];
|
|
228
|
+
let hardFail: HardFailReason | null = null;
|
|
229
|
+
|
|
230
|
+
for (const entry of deadEnds) {
|
|
231
|
+
if (!hardFail) {
|
|
232
|
+
// Codex review P2: match the slug case-insensitively and on word
|
|
233
|
+
// boundaries so `Elo-Rated-Plan-Tournament` still confesses while an
|
|
234
|
+
// unrelated longer token merely *containing* the slug does not
|
|
235
|
+
// false-positive.
|
|
236
|
+
if (slugCited(lower, entry.slug)) {
|
|
237
|
+
hardFail = { kind: 'slug_citation', dead_end_slug: entry.slug, matched: entry.slug };
|
|
238
|
+
} else {
|
|
239
|
+
for (const term of entry.forbidden_terms) {
|
|
240
|
+
if (lower.includes(term.toLowerCase())) {
|
|
241
|
+
hardFail = { kind: 'forbidden_term', dead_end_slug: entry.slug, matched: term };
|
|
242
|
+
break;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
// Advisory Jaccard always computed for the audit trail (regardless of
|
|
248
|
+
// hard-fail). Threshold 0.6 is a v0.4 guess; Phase 5 may tune from data.
|
|
249
|
+
const j = jaccard(tokens(candidateText), tokens(entry.hypothesis));
|
|
250
|
+
if (j >= 0.6) {
|
|
251
|
+
advisory.push({ dead_end_slug: entry.slug, jaccard: j });
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
return { hardFail, advisory };
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
const STOP: Set<string> = new Set([
|
|
258
|
+
'the', 'and', 'for', 'with', 'from', 'into', 'over', 'this', 'that',
|
|
259
|
+
'are', 'was', 'were', 'has', 'have', 'had', 'will', 'would', 'should',
|
|
260
|
+
'phase', 'plan', 'summary', 'roadmap', 'task', 'goal',
|
|
261
|
+
]);
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* True when `slug` appears in `lowerText` (already lower-cased) bounded by
|
|
265
|
+
* non-slug-characters on both sides. Slug chars are [a-z0-9-]; a boundary
|
|
266
|
+
* is any character outside that class (or string edge). This avoids the
|
|
267
|
+
* substring false-positive where a slug is a fragment of a longer token.
|
|
268
|
+
*/
|
|
269
|
+
function slugCited(lowerText: string, slug: string): boolean {
|
|
270
|
+
const slugLower = slug.toLowerCase();
|
|
271
|
+
let from = 0;
|
|
272
|
+
for (;;) {
|
|
273
|
+
const idx = lowerText.indexOf(slugLower, from);
|
|
274
|
+
if (idx === -1) return false;
|
|
275
|
+
const before = idx === 0 ? '' : lowerText[idx - 1];
|
|
276
|
+
const after = idx + slugLower.length >= lowerText.length ? '' : lowerText[idx + slugLower.length];
|
|
277
|
+
const isSlugChar = (c: string): boolean => c !== '' && /[a-z0-9-]/.test(c);
|
|
278
|
+
if (!isSlugChar(before) && !isSlugChar(after)) return true;
|
|
279
|
+
from = idx + 1;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
function tokens(text: string): Set<string> {
|
|
284
|
+
const set = new Set<string>();
|
|
285
|
+
const matches = text.toLowerCase().match(/[a-z0-9][a-z0-9_-]{2,}/g);
|
|
286
|
+
if (!matches) return set;
|
|
287
|
+
for (const t of matches) {
|
|
288
|
+
if (STOP.has(t)) continue;
|
|
289
|
+
set.add(t);
|
|
290
|
+
}
|
|
291
|
+
return set;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
function jaccard(a: Set<string>, b: Set<string>): number {
|
|
295
|
+
if (a.size === 0 && b.size === 0) return 0;
|
|
296
|
+
let intersection = 0;
|
|
297
|
+
for (const x of a) if (b.has(x)) intersection++;
|
|
298
|
+
const union = a.size + b.size - intersection;
|
|
299
|
+
return union === 0 ? 0 : intersection / union;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// ─── must_haves coverage axis ─────────────────────────────────────────────
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Score the candidate's coverage of REQUIREMENTS.md must_haves artifacts.
|
|
306
|
+
* Each found artifact = +1, each missing = -10. Negative scores remain
|
|
307
|
+
* finite — they're NOT a hard-fail (DEAD-ENDS is the only hard-fail path).
|
|
308
|
+
*/
|
|
309
|
+
export function scoreMustHavesCoverage(
|
|
310
|
+
candidateText: string,
|
|
311
|
+
candidateFm: Record<string, unknown>,
|
|
312
|
+
requirementsText: string | null
|
|
313
|
+
): number {
|
|
314
|
+
if (!requirementsText) return 0;
|
|
315
|
+
const required: string[] = extractRequiredArtifacts(requirementsText);
|
|
316
|
+
if (required.length === 0) return 0;
|
|
317
|
+
|
|
318
|
+
const filesModified: string[] = Array.isArray(candidateFm['files_modified'])
|
|
319
|
+
? (candidateFm['files_modified'] as string[]).map(String)
|
|
320
|
+
: [];
|
|
321
|
+
const haystack = candidateText + '\n' + filesModified.join('\n');
|
|
322
|
+
|
|
323
|
+
let score = 0;
|
|
324
|
+
for (const art of required) {
|
|
325
|
+
if (haystack.includes(art)) score += 1;
|
|
326
|
+
else score -= 10;
|
|
327
|
+
}
|
|
328
|
+
return score;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
function extractRequiredArtifacts(requirementsText: string): string[] {
|
|
332
|
+
// Best-effort pull of `must_haves.artifacts:` YAML list.
|
|
333
|
+
const re = /must_haves:\s*\n(?:\s+\w+:\s*\n(?:\s+- .*\n)*)*\s+artifacts:\s*\n((?:\s+- .*\n)+)/;
|
|
334
|
+
const m = requirementsText.match(re);
|
|
335
|
+
const out: string[] = [];
|
|
336
|
+
if (m) {
|
|
337
|
+
for (const line of m[1].split('\n')) {
|
|
338
|
+
const item = line.match(/^\s+- ["']?([^"'\n]+?)["']?\s*$/);
|
|
339
|
+
if (item) out.push(item[1].trim());
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
return out;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// ─── verification_commands axis ───────────────────────────────────────────
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Allowlist of permitted verification-command executables. Codex review
|
|
349
|
+
* P1: a blocklist of dangerous binaries is bypassable via absolute paths
|
|
350
|
+
* (`/bin/rm`), relative paths (`./rm`), and unlisted destructive tools.
|
|
351
|
+
* v0.4 uses an ALLOWLIST instead — only these deterministic check tools
|
|
352
|
+
* may run, and only when the bare name matches (no path separators).
|
|
353
|
+
*/
|
|
354
|
+
const VERIFICATION_ALLOWLIST: Set<string> = new Set([
|
|
355
|
+
'npx', 'npm', 'pnpm', 'yarn', 'node', 'tsx', 'tsc', 'eslint', 'jest', 'prettier',
|
|
356
|
+
]);
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* Run each command from the candidate's `verification_commands:` YAML
|
|
360
|
+
* frontmatter and return pass-rate as a score in [0, 10].
|
|
361
|
+
*
|
|
362
|
+
* SECURITY (codex review P1): verification commands are planner-authored
|
|
363
|
+
* input that would otherwise execute during candidate *selection* —
|
|
364
|
+
* before a plan is even chosen, so a candidate that will be REJECTED
|
|
365
|
+
* could still run code. Two guards:
|
|
366
|
+
* 1. The axis is OFF unless `enabled` is true (selectCandidate reads
|
|
367
|
+
* `plan_selection.run_verification_commands` from config, default
|
|
368
|
+
* false). When off, returns 0 and runs nothing.
|
|
369
|
+
* 2. When on, only allowlisted executables run, and argv[0] must be a
|
|
370
|
+
* bare name (no `/` or `\\`) so `/bin/rm` / `./rm` cannot bypass.
|
|
371
|
+
*
|
|
372
|
+
* Each command is split on whitespace; argv[0] is the binary, the rest
|
|
373
|
+
* positional args. No shell — no pipes, redirects, or glob expansion.
|
|
374
|
+
* Per-command 10s timeout with SIGKILL (codex review P2: SIGTERM lets a
|
|
375
|
+
* child ignore the signal and hang selection).
|
|
376
|
+
*/
|
|
377
|
+
export function scoreVerificationCommands(
|
|
378
|
+
candidateFm: Record<string, unknown>,
|
|
379
|
+
cwd: string,
|
|
380
|
+
enabled: boolean
|
|
381
|
+
): number {
|
|
382
|
+
if (!enabled) return 0;
|
|
383
|
+
const cmds = candidateFm['verification_commands'];
|
|
384
|
+
if (!Array.isArray(cmds) || cmds.length === 0) return 0;
|
|
385
|
+
let passed = 0;
|
|
386
|
+
let total = 0;
|
|
387
|
+
for (const raw of cmds) {
|
|
388
|
+
if (typeof raw !== 'string' || !raw.trim()) continue;
|
|
389
|
+
total++;
|
|
390
|
+
const argv = raw.trim().split(/\s+/);
|
|
391
|
+
const exe = argv[0];
|
|
392
|
+
if (!exe) continue;
|
|
393
|
+
// Reject path separators: an absolute / relative path bypasses the
|
|
394
|
+
// allowlist (e.g. `/bin/rm`, `./rm`, `..\\rm`).
|
|
395
|
+
if (exe.includes('/') || exe.includes('\\')) continue;
|
|
396
|
+
if (!VERIFICATION_ALLOWLIST.has(exe)) continue;
|
|
397
|
+
try {
|
|
398
|
+
const result = spawnSync(exe, argv.slice(1), {
|
|
399
|
+
cwd,
|
|
400
|
+
timeout: 10000,
|
|
401
|
+
killSignal: 'SIGKILL',
|
|
402
|
+
stdio: 'pipe',
|
|
403
|
+
});
|
|
404
|
+
if (result.status === 0) passed++;
|
|
405
|
+
} catch {
|
|
406
|
+
// Treat exceptions as failure.
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
if (total === 0) return 0;
|
|
410
|
+
return Math.round((passed / total) * 10);
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// ─── Cost tiebreaker ──────────────────────────────────────────────────────
|
|
414
|
+
|
|
415
|
+
/**
|
|
416
|
+
* Cheap token estimate: whitespace-separated words × 1.3. Used only
|
|
417
|
+
* as a tiebreaker when total scores tie exactly.
|
|
418
|
+
*/
|
|
419
|
+
export function estimateTokens(text: string): number {
|
|
420
|
+
const words = text.split(/\s+/).filter((s) => s.length > 0).length;
|
|
421
|
+
return Math.round(words * 1.3);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// ─── Per-candidate scoring ─────────────────────────────────────────────────
|
|
425
|
+
|
|
426
|
+
export function scoreExtendedCandidate(
|
|
427
|
+
candidatePath: string,
|
|
428
|
+
cwd: string,
|
|
429
|
+
phase: string,
|
|
430
|
+
context: {
|
|
431
|
+
deadEnds: DeadEndEntry[];
|
|
432
|
+
requirementsText: string | null;
|
|
433
|
+
/** Whether the verification_commands axis may execute commands. */
|
|
434
|
+
runVerificationCommands: boolean;
|
|
435
|
+
}
|
|
436
|
+
): ExtendedCandidateResult {
|
|
437
|
+
const relPath: string = path.relative(cwd, candidatePath);
|
|
438
|
+
const content: string | null = safeReadFile(candidatePath);
|
|
439
|
+
if (!content) {
|
|
440
|
+
return {
|
|
441
|
+
path: candidatePath,
|
|
442
|
+
relPath,
|
|
443
|
+
base_score: 0,
|
|
444
|
+
total_score: -Infinity,
|
|
445
|
+
base_breakdown: {
|
|
446
|
+
completeness: 0,
|
|
447
|
+
goal_alignment: 0,
|
|
448
|
+
hypothesis_quality: 0,
|
|
449
|
+
conciseness: 0,
|
|
450
|
+
},
|
|
451
|
+
extended: { must_haves_coverage: 0, verification_commands: 0, estimated_tokens: 0 },
|
|
452
|
+
hard_fail: { kind: 'slug_citation', dead_end_slug: '(file-unreadable)', matched: '' },
|
|
453
|
+
advisory_warnings: [],
|
|
454
|
+
};
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
const base = scorePlanCandidate(candidatePath, cwd, phase, DEFAULT_WEIGHTS);
|
|
458
|
+
const fm = extractFrontmatter(content);
|
|
459
|
+
|
|
460
|
+
const { hardFail, advisory } = checkDeadEnds(content, context.deadEnds);
|
|
461
|
+
|
|
462
|
+
const mustHavesScore = scoreMustHavesCoverage(content, fm, context.requirementsText);
|
|
463
|
+
const verificationScore = scoreVerificationCommands(fm, cwd, context.runVerificationCommands);
|
|
464
|
+
const tokenEst = estimateTokens(content);
|
|
465
|
+
|
|
466
|
+
// Composition: base [0,1] + must_haves (potentially negative) + verification [0,1].
|
|
467
|
+
// Hard-fail short-circuits to -Infinity so the audit trail still records all axes.
|
|
468
|
+
const totalScore = hardFail
|
|
469
|
+
? -Infinity
|
|
470
|
+
: base.score + mustHavesScore + verificationScore / 10;
|
|
471
|
+
|
|
472
|
+
return {
|
|
473
|
+
path: candidatePath,
|
|
474
|
+
relPath,
|
|
475
|
+
base_score: base.score,
|
|
476
|
+
total_score: totalScore,
|
|
477
|
+
base_breakdown: base.breakdown,
|
|
478
|
+
extended: {
|
|
479
|
+
must_haves_coverage: mustHavesScore,
|
|
480
|
+
verification_commands: verificationScore,
|
|
481
|
+
estimated_tokens: tokenEst,
|
|
482
|
+
},
|
|
483
|
+
hard_fail: hardFail,
|
|
484
|
+
advisory_warnings: advisory,
|
|
485
|
+
};
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
// ─── Pipeline orchestrator ─────────────────────────────────────────────────
|
|
489
|
+
|
|
490
|
+
export function selectCandidate(
|
|
491
|
+
cwd: string,
|
|
492
|
+
phaseNum: string,
|
|
493
|
+
opts: SelectCandidateOptions = {}
|
|
494
|
+
): SelectionResult {
|
|
495
|
+
const milestone = opts.milestone ?? getMilestoneInfo(cwd).version;
|
|
496
|
+
const phasesDir: string = path.join(cwd, '.planning', 'milestones', milestone, 'phases');
|
|
497
|
+
const phaseDirName: string | null = findPhaseDir(phasesDir, phaseNum);
|
|
498
|
+
if (!phaseDirName) {
|
|
499
|
+
error(`phase ${phaseNum} not found under ${phasesDir}`);
|
|
500
|
+
}
|
|
501
|
+
const phaseDir: string = path.join(phasesDir, phaseDirName as string);
|
|
502
|
+
|
|
503
|
+
const candidates: string[] = listCandidateFiles(phaseDir);
|
|
504
|
+
if (candidates.length === 0) {
|
|
505
|
+
error(`no PLAN-N.md candidates found in ${phaseDir}`);
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
const deadEndsText: string | null = safeReadMarkdown(
|
|
509
|
+
path.join(cwd, '.planning', 'DEAD-ENDS.md')
|
|
510
|
+
);
|
|
511
|
+
const deadEnds: DeadEndEntry[] = deadEndsText ? parseDeadEnds(deadEndsText) : [];
|
|
512
|
+
|
|
513
|
+
const requirementsText: string | null = safeReadMarkdown(
|
|
514
|
+
path.join(phaseDir, 'REQUIREMENTS.md')
|
|
515
|
+
);
|
|
516
|
+
|
|
517
|
+
const runVerificationCommands: boolean = opts.runVerificationCommands ?? false;
|
|
518
|
+
const scored: ExtendedCandidateResult[] = candidates.map((p) =>
|
|
519
|
+
scoreExtendedCandidate(p, cwd, phaseNum, {
|
|
520
|
+
deadEnds,
|
|
521
|
+
requirementsText,
|
|
522
|
+
runVerificationCommands,
|
|
523
|
+
})
|
|
524
|
+
);
|
|
525
|
+
|
|
526
|
+
// v0.4 Phase 4 pipeline ordering (codex r1 P1 #4):
|
|
527
|
+
// 1. DEAD-ENDS hard-fail already happened during scoring (-Infinity).
|
|
528
|
+
// 2. Cluster the SURVIVORS by vocabulary Jaccard so near-clones don't
|
|
529
|
+
// each consume a full scoring slot. Hard-failed candidates are never
|
|
530
|
+
// clustered — a violator cannot eliminate an innocent clustermate.
|
|
531
|
+
// 3. Each cluster's representative = its highest-scoring member.
|
|
532
|
+
// 4. Winner = highest-scoring representative across clusters.
|
|
533
|
+
const survivorIdx: number[] = scored
|
|
534
|
+
.map((s, i) => (isFinite(s.total_score) ? i : -1))
|
|
535
|
+
.filter((i) => i >= 0);
|
|
536
|
+
|
|
537
|
+
// Compare two scored candidates: higher total_score wins; cheaper (fewer
|
|
538
|
+
// estimated tokens) breaks ties. Returns the index (into `scored`) of the
|
|
539
|
+
// better one.
|
|
540
|
+
const better = (a: number, b: number): number => {
|
|
541
|
+
if (scored[a].total_score !== scored[b].total_score) {
|
|
542
|
+
return scored[a].total_score > scored[b].total_score ? a : b;
|
|
543
|
+
}
|
|
544
|
+
return scored[a].extended.estimated_tokens <= scored[b].extended.estimated_tokens ? a : b;
|
|
545
|
+
};
|
|
546
|
+
|
|
547
|
+
const representativeIdx: number[] = [];
|
|
548
|
+
if (survivorIdx.length > 0) {
|
|
549
|
+
const survivorVocabs: Set<string>[] = survivorIdx.map((i) => {
|
|
550
|
+
const content = safeReadFile(scored[i].path) ?? '';
|
|
551
|
+
return extractPlanVocabulary(content, extractFrontmatter(content));
|
|
552
|
+
});
|
|
553
|
+
const clusters: number[][] = clusterByJaccard(survivorVocabs, PROXIMITY_THRESHOLD);
|
|
554
|
+
clusters.forEach((memberPositions, clusterId) => {
|
|
555
|
+
// memberPositions index into survivorIdx; map to scored indices.
|
|
556
|
+
const memberScoredIdx = memberPositions.map((p) => survivorIdx[p]);
|
|
557
|
+
let repScored = memberScoredIdx[0];
|
|
558
|
+
for (const m of memberScoredIdx) repScored = better(repScored, m);
|
|
559
|
+
representativeIdx.push(repScored);
|
|
560
|
+
for (const m of memberScoredIdx) {
|
|
561
|
+
scored[m].cluster = {
|
|
562
|
+
cluster_id: clusterId,
|
|
563
|
+
is_representative: m === repScored,
|
|
564
|
+
merged_into: m === repScored ? null : scored[repScored].relPath,
|
|
565
|
+
};
|
|
566
|
+
}
|
|
567
|
+
});
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
// Winner = best representative. (Representatives are the only candidates
|
|
571
|
+
// eligible to be promoted — merged-away members and hard-fails are out.)
|
|
572
|
+
let winner: ExtendedCandidateResult | null = null;
|
|
573
|
+
if (representativeIdx.length > 0) {
|
|
574
|
+
let best = representativeIdx[0];
|
|
575
|
+
for (const r of representativeIdx) best = better(best, r);
|
|
576
|
+
winner = scored[best];
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
// Present `scored` deterministically for the audit: total_score desc,
|
|
580
|
+
// cheaper first on parity. -Infinity (hard-fails) sink to the bottom.
|
|
581
|
+
scored.sort((a, b) => {
|
|
582
|
+
if (a.total_score !== b.total_score) {
|
|
583
|
+
if (!isFinite(a.total_score) && isFinite(b.total_score)) return 1;
|
|
584
|
+
if (isFinite(a.total_score) && !isFinite(b.total_score)) return -1;
|
|
585
|
+
return b.total_score - a.total_score;
|
|
586
|
+
}
|
|
587
|
+
return a.extended.estimated_tokens - b.extended.estimated_tokens;
|
|
588
|
+
});
|
|
589
|
+
|
|
590
|
+
let promotedTo: string | null = null;
|
|
591
|
+
if (winner && !opts.dryRun) {
|
|
592
|
+
const planPath = path.join(phaseDir, 'PLAN.md');
|
|
593
|
+
// Codex review P2: refuse to clobber an already-resolved PLAN.md unless
|
|
594
|
+
// --force. Autopilot never hits this (hasMultipleCandidates requires no
|
|
595
|
+
// resolved PLAN.md), but the public CLI could otherwise destroy a plan.
|
|
596
|
+
if (fs.existsSync(planPath) && !opts.force) {
|
|
597
|
+
error(
|
|
598
|
+
`${path.relative(cwd, planPath)} already exists. Refusing to overwrite a resolved plan. ` +
|
|
599
|
+
`Re-run with --force to replace it, or remove it first.`
|
|
600
|
+
);
|
|
601
|
+
}
|
|
602
|
+
promotedTo = planPath;
|
|
603
|
+
const winnerContent: string = fs.readFileSync(winner.path, 'utf-8');
|
|
604
|
+
atomicWriteFileSync(promotedTo, winnerContent);
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
const auditPath = path.join(phaseDir, 'PLAN-SELECTION.json');
|
|
608
|
+
const audit = {
|
|
609
|
+
phase: phaseNum,
|
|
610
|
+
phase_dir: phaseDir,
|
|
611
|
+
milestone,
|
|
612
|
+
timestamp: new Date().toISOString(),
|
|
613
|
+
candidates: scored,
|
|
614
|
+
winner: winner ? winner.relPath : null,
|
|
615
|
+
promoted_to: promotedTo ? path.relative(cwd, promotedTo) : null,
|
|
616
|
+
dead_ends_loaded: deadEnds.length,
|
|
617
|
+
requirements_loaded: requirementsText !== null,
|
|
618
|
+
proximity_threshold: PROXIMITY_THRESHOLD,
|
|
619
|
+
hard_failed: scored.filter((s) => s.hard_fail !== null).map((s) => s.relPath),
|
|
620
|
+
clusters_formed: representativeIdx.length,
|
|
621
|
+
};
|
|
622
|
+
if (!opts.dryRun) {
|
|
623
|
+
atomicWriteFileSync(auditPath, JSON.stringify(audit, null, 2));
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
return {
|
|
627
|
+
phase: phaseNum,
|
|
628
|
+
phaseDir,
|
|
629
|
+
candidates: scored,
|
|
630
|
+
winner,
|
|
631
|
+
promoted_to: promotedTo,
|
|
632
|
+
audit_trail_path: auditPath,
|
|
633
|
+
};
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
function listCandidateFiles(phaseDir: string): string[] {
|
|
637
|
+
let entries: string[];
|
|
638
|
+
try {
|
|
639
|
+
entries = fs.readdirSync(phaseDir);
|
|
640
|
+
} catch {
|
|
641
|
+
return [];
|
|
642
|
+
}
|
|
643
|
+
const candidateRe = /^PLAN-(\d+)\.md$/;
|
|
644
|
+
const matches: Array<{ idx: number; name: string }> = [];
|
|
645
|
+
for (const name of entries) {
|
|
646
|
+
const m = name.match(candidateRe);
|
|
647
|
+
if (m) matches.push({ idx: parseInt(m[1], 10), name });
|
|
648
|
+
}
|
|
649
|
+
matches.sort((a, b) => a.idx - b.idx);
|
|
650
|
+
return matches.map((m) => path.join(phaseDir, m.name));
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
// ─── CLI entry ─────────────────────────────────────────────────────────────
|
|
654
|
+
|
|
655
|
+
export function cmdSelectCandidate(
|
|
656
|
+
cwd: string,
|
|
657
|
+
phaseNum: string,
|
|
658
|
+
opts: { dryRun?: boolean; force?: boolean; runVerificationCommands?: boolean },
|
|
659
|
+
raw: boolean
|
|
660
|
+
): void {
|
|
661
|
+
const result = selectCandidate(cwd, phaseNum, {
|
|
662
|
+
dryRun: opts.dryRun,
|
|
663
|
+
force: opts.force,
|
|
664
|
+
runVerificationCommands: opts.runVerificationCommands,
|
|
665
|
+
});
|
|
666
|
+
output(
|
|
667
|
+
result,
|
|
668
|
+
raw,
|
|
669
|
+
result.winner
|
|
670
|
+
? `winner: ${result.winner.relPath} (score ${result.winner.total_score.toFixed(3)})` +
|
|
671
|
+
(result.promoted_to
|
|
672
|
+
? ` → promoted to ${path.relative(cwd, result.promoted_to)}`
|
|
673
|
+
: ' (dry-run, no promotion)')
|
|
674
|
+
: 'no viable winner (all candidates hard-failed)'
|
|
675
|
+
);
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
module.exports = {
|
|
679
|
+
parseDeadEnds,
|
|
680
|
+
checkDeadEnds,
|
|
681
|
+
scoreMustHavesCoverage,
|
|
682
|
+
scoreVerificationCommands,
|
|
683
|
+
estimateTokens,
|
|
684
|
+
scoreExtendedCandidate,
|
|
685
|
+
selectCandidate,
|
|
686
|
+
cmdSelectCandidate,
|
|
687
|
+
};
|