@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
package/lib/knowledge.ts
ADDED
|
@@ -0,0 +1,768 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* GRD Knowledge Module -- KNOWHOW.md parsing, formatting, selection, and persistence.
|
|
5
|
+
*
|
|
6
|
+
* Provides four functions for managing structured knowledge entries mined from
|
|
7
|
+
* phase execution output. Entries are stored in KNOWHOW.md and injected into
|
|
8
|
+
* planning/execution prompts to compound improvements across phases.
|
|
9
|
+
*
|
|
10
|
+
* REQ-191: KNOWHOW.md Storage
|
|
11
|
+
*
|
|
12
|
+
* @module knowledge
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import type { KnowhowEntry } from './types';
|
|
16
|
+
|
|
17
|
+
const fs = require('fs') as typeof import('fs');
|
|
18
|
+
const path = require('path') as typeof import('path');
|
|
19
|
+
const { safeReadFile, output } = require('./utils') as {
|
|
20
|
+
safeReadFile: (p: string) => string | null;
|
|
21
|
+
output: (result: unknown, raw: boolean, rawValue?: unknown) => never;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const KNOWHOW_HEADER = '# KNOWHOW\n\n';
|
|
25
|
+
|
|
26
|
+
// Module-level cache: maps knowhowPath → parsed entries, invalidated on write.
|
|
27
|
+
const _knowhowCache = new Map<string, { entries: KnowhowEntry[]; mtime: number }>();
|
|
28
|
+
|
|
29
|
+
function _cachedParseKnowhow(knowhowPath: string): KnowhowEntry[] {
|
|
30
|
+
try {
|
|
31
|
+
const stat = fs.statSync(knowhowPath) as { mtimeMs: number };
|
|
32
|
+
const cached = _knowhowCache.get(knowhowPath);
|
|
33
|
+
if (cached && cached.mtime === stat.mtimeMs) return cached.entries;
|
|
34
|
+
const content = (fs.readFileSync(knowhowPath, 'utf8') as string) ?? '';
|
|
35
|
+
const entries = parseKnowhowEntries(content);
|
|
36
|
+
_knowhowCache.set(knowhowPath, { entries, mtime: stat.mtimeMs });
|
|
37
|
+
return entries;
|
|
38
|
+
} catch {
|
|
39
|
+
return [];
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Serialize a KnowhowEntry to a markdown level-3 heading block. */
|
|
44
|
+
function formatKnowhowEntry(entry: KnowhowEntry): string {
|
|
45
|
+
return (
|
|
46
|
+
`### ${entry.pattern_name}\n\n` +
|
|
47
|
+
`- **source:** ${entry.source}\n` +
|
|
48
|
+
`- **applicability:** ${entry.applicability}\n` +
|
|
49
|
+
`- **code_snippet:** ${entry.code_snippet}\n` +
|
|
50
|
+
`- **phase_number:** ${entry.phase_number}\n` +
|
|
51
|
+
`- **created_at:** ${entry.created_at}\n`
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Parse KNOWHOW.md content into an array of KnowhowEntry objects.
|
|
57
|
+
*
|
|
58
|
+
* Splits on `### ` level-3 headings. For each block, extracts pattern_name from
|
|
59
|
+
* the heading text and parses structured `- **field:** value` lines.
|
|
60
|
+
* Returns empty array for empty/missing content.
|
|
61
|
+
*
|
|
62
|
+
* Roundtrip guarantee: parseKnowhowEntries(formatKnowhowEntry(entry)) produces
|
|
63
|
+
* an equivalent entry (lossless).
|
|
64
|
+
*/
|
|
65
|
+
function parseKnowhowEntries(content: string): KnowhowEntry[] {
|
|
66
|
+
if (!content || !content.trim()) {
|
|
67
|
+
return [];
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const entries: KnowhowEntry[] = [];
|
|
71
|
+
|
|
72
|
+
const blocks = content.split(/(?=^### )/m);
|
|
73
|
+
|
|
74
|
+
for (const block of blocks) {
|
|
75
|
+
const trimmed = block.trim();
|
|
76
|
+
if (!trimmed.startsWith('### ')) {
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const lines = trimmed.split('\n');
|
|
81
|
+
const headingLine = lines[0];
|
|
82
|
+
const pattern_name = headingLine.replace(/^### /, '').trim();
|
|
83
|
+
|
|
84
|
+
if (!pattern_name) {
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const fieldPattern = /^- \*\*(\w+):\*\* (.*)$/;
|
|
89
|
+
const fields: Record<string, string> = {};
|
|
90
|
+
|
|
91
|
+
for (const line of lines.slice(1)) {
|
|
92
|
+
const match = line.match(fieldPattern);
|
|
93
|
+
if (match) {
|
|
94
|
+
fields[match[1]] = match[2].trim();
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const REQUIRED_FIELDS = ['source', 'applicability', 'code_snippet', 'phase_number', 'created_at'] as const;
|
|
99
|
+
if (REQUIRED_FIELDS.every((k) => k in fields)) {
|
|
100
|
+
const phase_number = parseInt(fields['phase_number'], 10);
|
|
101
|
+
if (Number.isNaN(phase_number)) {
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
entries.push({
|
|
105
|
+
pattern_name,
|
|
106
|
+
source: fields['source'],
|
|
107
|
+
applicability: fields['applicability'],
|
|
108
|
+
code_snippet: fields['code_snippet'],
|
|
109
|
+
phase_number,
|
|
110
|
+
created_at: fields['created_at'],
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return entries;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Read existing KNOWHOW.md at knowhowPath, merge new entries (deduplicating by
|
|
120
|
+
* pattern_name, keeping higher phase_number), and write back.
|
|
121
|
+
*
|
|
122
|
+
* - If the file does not exist, starts with an empty entry list.
|
|
123
|
+
* - Deduplication: when a new entry shares a pattern_name with an existing one,
|
|
124
|
+
* the entry with the higher phase_number is kept.
|
|
125
|
+
* - File always starts with `# KNOWHOW\n\n` header.
|
|
126
|
+
* - Parent directories are created if needed.
|
|
127
|
+
*/
|
|
128
|
+
function appendKnowhowEntries(knowhowPath: string, entries: KnowhowEntry[]): void {
|
|
129
|
+
if (entries.length === 0) return;
|
|
130
|
+
|
|
131
|
+
const existing = _cachedParseKnowhow(knowhowPath);
|
|
132
|
+
|
|
133
|
+
const byName = new Map<string, KnowhowEntry>();
|
|
134
|
+
|
|
135
|
+
for (const e of existing) {
|
|
136
|
+
byName.set(e.pattern_name, e);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const total = entries.length;
|
|
140
|
+
for (let i = 0; i < total; i++) {
|
|
141
|
+
const e = entries[i];
|
|
142
|
+
if (total > 50) {
|
|
143
|
+
process.stderr.write(`[knowledge] merging entry ${i + 1}/${total}: ${e.pattern_name}\n`);
|
|
144
|
+
}
|
|
145
|
+
const current = byName.get(e.pattern_name);
|
|
146
|
+
if (!current || e.phase_number >= current.phase_number) {
|
|
147
|
+
byName.set(e.pattern_name, e);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const merged = Array.from(byName.values());
|
|
152
|
+
|
|
153
|
+
merged.sort((a, b) => b.phase_number - a.phase_number);
|
|
154
|
+
|
|
155
|
+
const body = merged.map(formatKnowhowEntry).join('\n');
|
|
156
|
+
const fileContent = KNOWHOW_HEADER + body;
|
|
157
|
+
|
|
158
|
+
const dir = path.dirname(knowhowPath);
|
|
159
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
160
|
+
|
|
161
|
+
fs.writeFileSync(knowhowPath, fileContent, 'utf8');
|
|
162
|
+
_knowhowCache.delete(knowhowPath);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Return top-N entries from the input array sorted by recency.
|
|
167
|
+
*
|
|
168
|
+
* - Primary sort: phase_number descending (most recent first).
|
|
169
|
+
* - If moduleHints provided: entries whose source or applicability mentions any
|
|
170
|
+
* hint string are sorted first within each phase_number bucket.
|
|
171
|
+
* - Tertiary sort: when currentPhase is provided, entries closer to the current
|
|
172
|
+
* phase rank higher within the same phase_number bucket (proximity scoring).
|
|
173
|
+
* - Returns at most n entries.
|
|
174
|
+
* - If entries.length <= n, returns all entries sorted.
|
|
175
|
+
*/
|
|
176
|
+
function selectTopEntries(
|
|
177
|
+
entries: KnowhowEntry[],
|
|
178
|
+
n: number,
|
|
179
|
+
moduleHints?: string[],
|
|
180
|
+
currentPhase?: number,
|
|
181
|
+
): KnowhowEntry[] {
|
|
182
|
+
if (entries.length === 0 || n <= 0) {
|
|
183
|
+
return [];
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const lowerHints = moduleHints?.map((h) => h.toLowerCase()) ?? [];
|
|
187
|
+
const hintMatches = new Set<KnowhowEntry>();
|
|
188
|
+
if (lowerHints.length > 0) {
|
|
189
|
+
for (const entry of entries) {
|
|
190
|
+
const combined = `${entry.source} ${entry.applicability}`.toLowerCase();
|
|
191
|
+
if (lowerHints.some((hint) => combined.includes(hint))) {
|
|
192
|
+
hintMatches.add(entry);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const scoreEntry = (e: KnowhowEntry): number =>
|
|
198
|
+
e.phase_number * 1000 +
|
|
199
|
+
(hintMatches.has(e) ? 100 : 0) -
|
|
200
|
+
(currentPhase !== undefined ? Math.abs(currentPhase - e.phase_number) : 0);
|
|
201
|
+
const sorted = [...entries].sort((a, b) => scoreEntry(b) - scoreEntry(a));
|
|
202
|
+
|
|
203
|
+
return sorted.slice(0, n);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// ─── Knowledge Stats Tracking ─────────────────────────────────────────────────
|
|
207
|
+
|
|
208
|
+
interface KnowhowStat {
|
|
209
|
+
hit_count: number;
|
|
210
|
+
last_used: string;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function _loadKnowledgeStats(statsPath: string): Record<string, KnowhowStat> {
|
|
214
|
+
try {
|
|
215
|
+
const raw = fs.readFileSync(statsPath, 'utf8') as string;
|
|
216
|
+
return JSON.parse(raw) as Record<string, KnowhowStat>;
|
|
217
|
+
} catch {
|
|
218
|
+
return {};
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function _saveKnowledgeStats(statsPath: string, stats: Record<string, KnowhowStat>): void {
|
|
223
|
+
try {
|
|
224
|
+
fs.mkdirSync(path.dirname(statsPath), { recursive: true });
|
|
225
|
+
fs.writeFileSync(statsPath, JSON.stringify(stats, null, 2), 'utf8');
|
|
226
|
+
} catch {
|
|
227
|
+
/* stats are best-effort; never crash injection on write failure */
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Build a structured knowledge injection block from KNOWHOW.md for prompt injection.
|
|
233
|
+
*
|
|
234
|
+
* Reads KNOWHOW.md from path.join(cwd, 'KNOWHOW.md'), selects top 5 entries
|
|
235
|
+
* using selectTopEntries, and wraps them in <knowhow_context> XML tags.
|
|
236
|
+
* Increments hit_count and last_used in .planning/knowledge-stats.json after each injection.
|
|
237
|
+
*
|
|
238
|
+
* @param cwd - Project root directory
|
|
239
|
+
* @param _phaseNum - Current phase number (reserved for future phase-proximity scoring)
|
|
240
|
+
* @param moduleHints - Optional module name hints to boost relevant entries
|
|
241
|
+
* @returns Formatted injection block string, or '' if no entries
|
|
242
|
+
*/
|
|
243
|
+
function buildKnowledgeInjectionBlock(cwd: string, _phaseNum: string, moduleHints?: string[]): string {
|
|
244
|
+
const knowhowPath = path.join(cwd, 'KNOWHOW.md');
|
|
245
|
+
const entries = _cachedParseKnowhow(knowhowPath);
|
|
246
|
+
if (entries.length === 0) {
|
|
247
|
+
return '';
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
const parsedPhase = parseInt(_phaseNum, 10);
|
|
251
|
+
const currentPhase = Number.isNaN(parsedPhase) ? undefined : parsedPhase;
|
|
252
|
+
const top = selectTopEntries(entries, 5, moduleHints, currentPhase);
|
|
253
|
+
if (top.length === 0) {
|
|
254
|
+
return '';
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// Track which entries were injected
|
|
258
|
+
const statsPath = path.join(cwd, '.planning', 'knowledge-stats.json');
|
|
259
|
+
const stats = _loadKnowledgeStats(statsPath);
|
|
260
|
+
const now = new Date().toISOString();
|
|
261
|
+
for (const entry of top) {
|
|
262
|
+
const existing = stats[entry.pattern_name];
|
|
263
|
+
stats[entry.pattern_name] = {
|
|
264
|
+
hit_count: (existing?.hit_count ?? 0) + 1,
|
|
265
|
+
last_used: now,
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
_saveKnowledgeStats(statsPath, stats);
|
|
269
|
+
|
|
270
|
+
const formatted = top.map(formatKnowhowEntry).join('\n');
|
|
271
|
+
return `<knowhow_context>\n${formatted}\n</knowhow_context>`;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Extract module hints from PLAN.md frontmatter files_modified fields.
|
|
276
|
+
*
|
|
277
|
+
* Reads all *-PLAN.md files in phaseDir, extracts files_modified from YAML
|
|
278
|
+
* frontmatter, and returns unique basenames (without extensions).
|
|
279
|
+
*
|
|
280
|
+
* @param phaseDir - Path to phase directory containing PLAN.md files
|
|
281
|
+
* @returns Array of unique module basenames
|
|
282
|
+
*/
|
|
283
|
+
function extractModuleHints(phaseDir: string): string[] {
|
|
284
|
+
if (!fs.existsSync(phaseDir)) {
|
|
285
|
+
return [];
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
let files: string[];
|
|
289
|
+
try {
|
|
290
|
+
files = (fs.readdirSync(phaseDir) as string[]).filter(
|
|
291
|
+
(f: string) => f.endsWith('-PLAN.md') || f === 'PLAN.md'
|
|
292
|
+
);
|
|
293
|
+
} catch {
|
|
294
|
+
return [];
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
if (files.length === 0) {
|
|
298
|
+
return [];
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
const hints = new Set<string>();
|
|
302
|
+
|
|
303
|
+
for (const file of files) {
|
|
304
|
+
const filePath = path.join(phaseDir, file);
|
|
305
|
+
const content = safeReadFile(filePath);
|
|
306
|
+
if (!content) continue;
|
|
307
|
+
|
|
308
|
+
const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
309
|
+
if (!fmMatch) continue;
|
|
310
|
+
|
|
311
|
+
const fmContent = fmMatch[1];
|
|
312
|
+
const filesMatch = fmContent.match(/files_modified:\s*\[([^\]]*)\]/);
|
|
313
|
+
if (!filesMatch) continue;
|
|
314
|
+
|
|
315
|
+
const filesList = filesMatch[1].split(',').map((f: string) => f.trim()).filter(Boolean);
|
|
316
|
+
for (const f of filesList) {
|
|
317
|
+
const basename = path.basename(f).split('.')[0];
|
|
318
|
+
if (basename) {
|
|
319
|
+
hints.add(basename);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
return Array.from(hints);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// ─── Knowhow Audit ──────────────────────────────────────────────────────────
|
|
328
|
+
|
|
329
|
+
/** A stale or conflicting entry flagged by the auditor. */
|
|
330
|
+
interface KnowhowAuditFlag {
|
|
331
|
+
pattern_name: string;
|
|
332
|
+
source_file: string;
|
|
333
|
+
issue: 'broken_ref' | 'contradicts';
|
|
334
|
+
detail: string;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/** Result of the KNOWHOW.md audit command. */
|
|
338
|
+
interface KnowhowAuditResult {
|
|
339
|
+
total_entries: number;
|
|
340
|
+
stale_count: number;
|
|
341
|
+
contradiction_count: number;
|
|
342
|
+
flags: KnowhowAuditFlag[];
|
|
343
|
+
knowhow_files_scanned: number;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* CLI command: Audit all KNOWHOW.md files for stale references and contradictions.
|
|
348
|
+
*
|
|
349
|
+
* Scans all KNOWHOW.md files under .planning/milestones/ and the root research dir.
|
|
350
|
+
* For each entry:
|
|
351
|
+
* - Extracts file paths referenced in the source field and checks they exist.
|
|
352
|
+
* - Detects entries with the same pattern_name but different applicability advice.
|
|
353
|
+
*
|
|
354
|
+
* @param cwd - Project working directory
|
|
355
|
+
* @param raw - Output raw text instead of JSON
|
|
356
|
+
*/
|
|
357
|
+
function cmdKnowhowAudit(cwd: string, raw: boolean): void {
|
|
358
|
+
const milestonesBase = path.join(cwd, '.planning', 'milestones');
|
|
359
|
+
const flags: KnowhowAuditFlag[] = [];
|
|
360
|
+
|
|
361
|
+
// Collect all KNOWHOW.md paths
|
|
362
|
+
const knowhowPaths: string[] = [];
|
|
363
|
+
try {
|
|
364
|
+
const msDirs = (fs.readdirSync(milestonesBase) as string[]).filter((d) => {
|
|
365
|
+
try {
|
|
366
|
+
return fs.statSync(path.join(milestonesBase, d)).isDirectory();
|
|
367
|
+
} catch {
|
|
368
|
+
return false;
|
|
369
|
+
}
|
|
370
|
+
});
|
|
371
|
+
for (const ms of msDirs) {
|
|
372
|
+
const msKnowhow = path.join(milestonesBase, ms, 'KNOWHOW.md');
|
|
373
|
+
if (fs.existsSync(msKnowhow)) knowhowPaths.push(msKnowhow);
|
|
374
|
+
const resKnowhow = path.join(milestonesBase, ms, 'research', 'KNOWHOW.md');
|
|
375
|
+
if (fs.existsSync(resKnowhow)) knowhowPaths.push(resKnowhow);
|
|
376
|
+
// Codex r25 P2: per-phase KNOWHOW.md files are where the knowledge
|
|
377
|
+
// miner writes new entries; audit/dedup must scan them or they
|
|
378
|
+
// miss the common case entirely.
|
|
379
|
+
const phasesBase = path.join(milestonesBase, ms, 'phases');
|
|
380
|
+
try {
|
|
381
|
+
for (const ph of fs.readdirSync(phasesBase) as string[]) {
|
|
382
|
+
const phKnowhow = path.join(phasesBase, ph, 'KNOWHOW.md');
|
|
383
|
+
if (fs.existsSync(phKnowhow)) knowhowPaths.push(phKnowhow);
|
|
384
|
+
}
|
|
385
|
+
} catch { /* skip */ }
|
|
386
|
+
}
|
|
387
|
+
} catch {
|
|
388
|
+
// milestones dir doesn't exist
|
|
389
|
+
}
|
|
390
|
+
// Also check root planning research dir + project-root KNOWHOW.md
|
|
391
|
+
const rootResearchKnowhow = path.join(cwd, '.planning', 'research', 'KNOWHOW.md');
|
|
392
|
+
if (fs.existsSync(rootResearchKnowhow)) knowhowPaths.push(rootResearchKnowhow);
|
|
393
|
+
const rootKnowhow = path.join(cwd, '.planning', 'KNOWHOW.md');
|
|
394
|
+
if (fs.existsSync(rootKnowhow)) knowhowPaths.push(rootKnowhow);
|
|
395
|
+
const projectRootKnowhow = path.join(cwd, 'KNOWHOW.md');
|
|
396
|
+
if (fs.existsSync(projectRootKnowhow)) knowhowPaths.push(projectRootKnowhow);
|
|
397
|
+
|
|
398
|
+
let totalEntries = 0;
|
|
399
|
+
// Track all entries across files for contradiction detection (by pattern_name)
|
|
400
|
+
const allByName = new Map<string, { entry: KnowhowEntry; file: string }[]>();
|
|
401
|
+
|
|
402
|
+
for (const kPath of knowhowPaths) {
|
|
403
|
+
const content = safeReadFile(kPath);
|
|
404
|
+
if (!content) continue;
|
|
405
|
+
const entries = parseKnowhowEntries(content);
|
|
406
|
+
totalEntries += entries.length;
|
|
407
|
+
|
|
408
|
+
for (const entry of entries) {
|
|
409
|
+
// Check if source field references a real file path
|
|
410
|
+
const sourceRef = entry.source.split(':')[0].trim(); // strip line numbers
|
|
411
|
+
if (sourceRef && sourceRef.includes('/') && !sourceRef.startsWith('http')) {
|
|
412
|
+
const absRef = path.isAbsolute(sourceRef)
|
|
413
|
+
? sourceRef
|
|
414
|
+
: path.join(cwd, sourceRef);
|
|
415
|
+
if (!fs.existsSync(absRef)) {
|
|
416
|
+
flags.push({
|
|
417
|
+
pattern_name: entry.pattern_name,
|
|
418
|
+
source_file: path.relative(cwd, kPath),
|
|
419
|
+
issue: 'broken_ref',
|
|
420
|
+
detail: `Referenced source "${sourceRef}" does not exist`,
|
|
421
|
+
});
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
// Accumulate for contradiction detection
|
|
426
|
+
const existing = allByName.get(entry.pattern_name) ?? [];
|
|
427
|
+
existing.push({ entry, file: path.relative(cwd, kPath) });
|
|
428
|
+
allByName.set(entry.pattern_name, existing);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// Detect contradictions: same pattern_name, significantly different applicability
|
|
433
|
+
let contradictionCount = 0;
|
|
434
|
+
for (const [name, items] of allByName) {
|
|
435
|
+
if (items.length < 2) continue;
|
|
436
|
+
const applicabilities = items.map((i) => i.entry.applicability.toLowerCase());
|
|
437
|
+
const first = applicabilities[0];
|
|
438
|
+
// Heuristic: if one says "always/use" and another says "never/avoid", flag it
|
|
439
|
+
const hasPositive = applicabilities.some((a) => /\b(always|use|prefer|do)\b/.test(a));
|
|
440
|
+
const hasNegative = applicabilities.some((a) => /\b(never|avoid|don't|do not)\b/.test(a));
|
|
441
|
+
if (hasPositive && hasNegative) {
|
|
442
|
+
contradictionCount++;
|
|
443
|
+
flags.push({
|
|
444
|
+
pattern_name: name,
|
|
445
|
+
source_file: items.map((i) => i.file).join(', '),
|
|
446
|
+
issue: 'contradicts',
|
|
447
|
+
detail: `"${first.slice(0, 80)}" vs "${applicabilities.slice(1).join(' / ').slice(0, 80)}"`,
|
|
448
|
+
});
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
const staleCount = flags.filter((f) => f.issue === 'broken_ref').length;
|
|
453
|
+
const result: KnowhowAuditResult = {
|
|
454
|
+
total_entries: totalEntries,
|
|
455
|
+
stale_count: staleCount,
|
|
456
|
+
contradiction_count: contradictionCount,
|
|
457
|
+
flags,
|
|
458
|
+
knowhow_files_scanned: knowhowPaths.length,
|
|
459
|
+
};
|
|
460
|
+
|
|
461
|
+
const summary = `${knowhowPaths.length} files, ${totalEntries} entries: ${staleCount} stale refs, ${contradictionCount} contradictions`;
|
|
462
|
+
output(result, raw, summary);
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
// ─── Knowhow Dedup ──────────────────────────────────────────────────────────
|
|
466
|
+
|
|
467
|
+
/** A candidate merge pair from the dedup analysis. */
|
|
468
|
+
interface KnowhowDedupPair {
|
|
469
|
+
entry_a: string;
|
|
470
|
+
entry_b: string;
|
|
471
|
+
source_a: string;
|
|
472
|
+
source_b: string;
|
|
473
|
+
similarity: number;
|
|
474
|
+
suggested_merge: string;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
/** Result of the dedup command. */
|
|
478
|
+
interface KnowhowDedupResult {
|
|
479
|
+
files_scanned: number;
|
|
480
|
+
entries_total: number;
|
|
481
|
+
pairs_above_threshold: number;
|
|
482
|
+
pairs: KnowhowDedupPair[];
|
|
483
|
+
report_path: string | null;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
/** Compute trigram set from text for Jaccard similarity. */
|
|
487
|
+
function _trigrams(text: string): Set<string> {
|
|
488
|
+
const normalized = text.toLowerCase().replace(/[^a-z0-9 ]/g, ' ').replace(/\s+/g, ' ').trim();
|
|
489
|
+
const set = new Set<string>();
|
|
490
|
+
for (let i = 0; i + 2 < normalized.length; i++) {
|
|
491
|
+
set.add(normalized.slice(i, i + 3));
|
|
492
|
+
}
|
|
493
|
+
return set;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
/** Compute Jaccard similarity between two trigram sets. */
|
|
497
|
+
function _jaccard(a: Set<string>, b: Set<string>): number {
|
|
498
|
+
if (a.size === 0 && b.size === 0) return 1;
|
|
499
|
+
let intersection = 0;
|
|
500
|
+
for (const t of a) {
|
|
501
|
+
if (b.has(t)) intersection++;
|
|
502
|
+
}
|
|
503
|
+
const union = a.size + b.size - intersection;
|
|
504
|
+
return union === 0 ? 0 : intersection / union;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
/**
|
|
508
|
+
* CLI command: Find near-duplicate entries across all KNOWHOW.md files.
|
|
509
|
+
*
|
|
510
|
+
* Scans all KNOWHOW.md files under .planning/milestones/ and the root research dir.
|
|
511
|
+
* Computes pairwise trigram-Jaccard similarity on entry title + first 3 body lines.
|
|
512
|
+
* Writes a report to .planning/KNOWHOW-DEDUP.md listing pairs with similarity > threshold (default 0.75).
|
|
513
|
+
*
|
|
514
|
+
* @param cwd - Project working directory
|
|
515
|
+
* @param raw - Output raw text instead of JSON
|
|
516
|
+
* @param threshold - Similarity threshold (default 0.75)
|
|
517
|
+
*/
|
|
518
|
+
function cmdKnowhowDedup(cwd: string, raw: boolean, threshold = 0.75): void {
|
|
519
|
+
const milestonesBase = path.join(cwd, '.planning', 'milestones');
|
|
520
|
+
|
|
521
|
+
// Collect all KNOWHOW.md paths
|
|
522
|
+
const knowhowPaths: string[] = [];
|
|
523
|
+
try {
|
|
524
|
+
const msDirs = (fs.readdirSync(milestonesBase) as string[]).filter((d) => {
|
|
525
|
+
try { return fs.statSync(path.join(milestonesBase, d)).isDirectory(); } catch { return false; }
|
|
526
|
+
});
|
|
527
|
+
for (const ms of msDirs) {
|
|
528
|
+
const msKnowhow = path.join(milestonesBase, ms, 'KNOWHOW.md');
|
|
529
|
+
if (fs.existsSync(msKnowhow)) knowhowPaths.push(msKnowhow);
|
|
530
|
+
const resKnowhow = path.join(milestonesBase, ms, 'research', 'KNOWHOW.md');
|
|
531
|
+
if (fs.existsSync(resKnowhow)) knowhowPaths.push(resKnowhow);
|
|
532
|
+
// Codex r25 P2: include per-phase KNOWHOW.md — that's where the
|
|
533
|
+
// knowledge miner writes the entries that most often dup.
|
|
534
|
+
const phasesBase = path.join(milestonesBase, ms, 'phases');
|
|
535
|
+
try {
|
|
536
|
+
for (const ph of fs.readdirSync(phasesBase) as string[]) {
|
|
537
|
+
const phKnowhow = path.join(phasesBase, ph, 'KNOWHOW.md');
|
|
538
|
+
if (fs.existsSync(phKnowhow)) knowhowPaths.push(phKnowhow);
|
|
539
|
+
}
|
|
540
|
+
} catch { /* skip */ }
|
|
541
|
+
}
|
|
542
|
+
} catch {
|
|
543
|
+
// milestones dir doesn't exist
|
|
544
|
+
}
|
|
545
|
+
const rootResearchKnowhow = path.join(cwd, '.planning', 'research', 'KNOWHOW.md');
|
|
546
|
+
if (fs.existsSync(rootResearchKnowhow)) knowhowPaths.push(rootResearchKnowhow);
|
|
547
|
+
const rootKnowhow = path.join(cwd, '.planning', 'KNOWHOW.md');
|
|
548
|
+
if (fs.existsSync(rootKnowhow)) knowhowPaths.push(rootKnowhow);
|
|
549
|
+
const projectRootKnowhow = path.join(cwd, 'KNOWHOW.md');
|
|
550
|
+
if (fs.existsSync(projectRootKnowhow)) knowhowPaths.push(projectRootKnowhow);
|
|
551
|
+
|
|
552
|
+
// Load all entries with source file info
|
|
553
|
+
const allEntries: { entry: KnowhowEntry; file: string; fingerprint: string; trigrams: Set<string> }[] = [];
|
|
554
|
+
for (const kPath of knowhowPaths) {
|
|
555
|
+
const content = safeReadFile(kPath);
|
|
556
|
+
if (!content) continue;
|
|
557
|
+
const entries = parseKnowhowEntries(content);
|
|
558
|
+
for (const entry of entries) {
|
|
559
|
+
// Fingerprint: title + first 3 lines of applicability
|
|
560
|
+
const bodyLines = entry.applicability.split('\n').slice(0, 3).join(' ');
|
|
561
|
+
const fingerprint = `${entry.pattern_name} ${bodyLines}`;
|
|
562
|
+
allEntries.push({
|
|
563
|
+
entry,
|
|
564
|
+
file: path.relative(cwd, kPath),
|
|
565
|
+
fingerprint,
|
|
566
|
+
trigrams: _trigrams(fingerprint),
|
|
567
|
+
});
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
// Pairwise similarity (O(n^2), acceptable for typical KNOWHOW sizes <500 entries)
|
|
572
|
+
const pairs: KnowhowDedupPair[] = [];
|
|
573
|
+
const totalEntryCount = allEntries.length;
|
|
574
|
+
for (let i = 0; i < totalEntryCount; i++) {
|
|
575
|
+
if (totalEntryCount > 20 && i % Math.max(1, Math.floor(totalEntryCount / 20)) === 0) {
|
|
576
|
+
process.stderr.write(`[knowledge:dedup] scanning entry ${i + 1}/${totalEntryCount}\n`);
|
|
577
|
+
}
|
|
578
|
+
for (let j = i + 1; j < totalEntryCount; j++) {
|
|
579
|
+
const sim = _jaccard(allEntries[i].trigrams, allEntries[j].trigrams);
|
|
580
|
+
if (sim >= threshold) {
|
|
581
|
+
pairs.push({
|
|
582
|
+
entry_a: allEntries[i].entry.pattern_name,
|
|
583
|
+
entry_b: allEntries[j].entry.pattern_name,
|
|
584
|
+
source_a: allEntries[i].file,
|
|
585
|
+
source_b: allEntries[j].file,
|
|
586
|
+
similarity: Math.round(sim * 100) / 100,
|
|
587
|
+
suggested_merge: `Keep: "${allEntries[i].entry.pattern_name}" (phase ${allEntries[i].entry.phase_number} ≥ ${allEntries[j].entry.phase_number} ? A : B)`,
|
|
588
|
+
});
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
// Sort by similarity descending
|
|
594
|
+
pairs.sort((a, b) => b.similarity - a.similarity);
|
|
595
|
+
|
|
596
|
+
// Write report
|
|
597
|
+
let reportPath: string | null = null;
|
|
598
|
+
const reportDir = path.join(cwd, '.planning');
|
|
599
|
+
const reportFile = path.join(reportDir, 'KNOWHOW-DEDUP.md');
|
|
600
|
+
try {
|
|
601
|
+
const lines = [
|
|
602
|
+
`# KNOWHOW Deduplication Report`,
|
|
603
|
+
``,
|
|
604
|
+
`Generated: ${new Date().toISOString().split('T')[0]}`,
|
|
605
|
+
`Files scanned: ${knowhowPaths.length}`,
|
|
606
|
+
`Entries total: ${allEntries.length}`,
|
|
607
|
+
`Threshold: ${threshold}`,
|
|
608
|
+
`Pairs above threshold: ${pairs.length}`,
|
|
609
|
+
``,
|
|
610
|
+
];
|
|
611
|
+
if (pairs.length === 0) {
|
|
612
|
+
lines.push('No near-duplicate entries found.');
|
|
613
|
+
} else {
|
|
614
|
+
lines.push('## Merge Candidates\n');
|
|
615
|
+
for (const p of pairs) {
|
|
616
|
+
lines.push(`### Similarity ${p.similarity}`);
|
|
617
|
+
lines.push(`- **A**: \`${p.entry_a}\` (${p.source_a})`);
|
|
618
|
+
lines.push(`- **B**: \`${p.entry_b}\` (${p.source_b})`);
|
|
619
|
+
lines.push(`- **Suggested**: ${p.suggested_merge}`);
|
|
620
|
+
lines.push('');
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
fs.mkdirSync(reportDir, { recursive: true });
|
|
624
|
+
fs.writeFileSync(reportFile, lines.join('\n'), 'utf-8');
|
|
625
|
+
reportPath = path.relative(cwd, reportFile);
|
|
626
|
+
} catch {
|
|
627
|
+
// Non-fatal
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
const result: KnowhowDedupResult = {
|
|
631
|
+
files_scanned: knowhowPaths.length,
|
|
632
|
+
entries_total: allEntries.length,
|
|
633
|
+
pairs_above_threshold: pairs.length,
|
|
634
|
+
pairs,
|
|
635
|
+
report_path: reportPath,
|
|
636
|
+
};
|
|
637
|
+
|
|
638
|
+
output(
|
|
639
|
+
result,
|
|
640
|
+
raw,
|
|
641
|
+
`${allEntries.length} entries, ${pairs.length} duplicate pairs (threshold=${threshold}) → ${reportPath ?? 'report not written'}`
|
|
642
|
+
);
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
// ─── TF-IDF Relevance Ranking ────────────────────────────────────────────────
|
|
646
|
+
|
|
647
|
+
const STOPWORDS = new Set([
|
|
648
|
+
'a','an','the','and','or','but','in','on','at','to','for','of','with',
|
|
649
|
+
'is','are','was','were','be','been','being','have','has','had','do','does',
|
|
650
|
+
'did','will','would','could','should','may','might','it','its','this',
|
|
651
|
+
'that','these','those','from','by','as','if','when','where','which','who',
|
|
652
|
+
]);
|
|
653
|
+
|
|
654
|
+
/** Tokenize text into lowercase words, removing stopwords and punctuation. */
|
|
655
|
+
function _tokenize(text: string): string[] {
|
|
656
|
+
return text
|
|
657
|
+
.toLowerCase()
|
|
658
|
+
.replace(/[^a-z0-9 ]/g, ' ')
|
|
659
|
+
.split(/\s+/)
|
|
660
|
+
.filter((t) => t.length > 2 && !STOPWORDS.has(t));
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
/**
|
|
664
|
+
* Rank knowhow entries by TF-IDF relevance to a phase goal string.
|
|
665
|
+
*
|
|
666
|
+
* Tokenizes the goal and each entry's problem/solution text, computes IDF across
|
|
667
|
+
* all entries, then scores each entry by the sum of TF-IDF for query tokens.
|
|
668
|
+
* Returns at most topN entries sorted by descending relevance score.
|
|
669
|
+
*
|
|
670
|
+
* @param goal - Phase goal string to rank against
|
|
671
|
+
* @param entries - Array of KnowhowEntry to rank
|
|
672
|
+
* @param topN - Number of entries to return (default 5)
|
|
673
|
+
*/
|
|
674
|
+
function rankKnowhowByPhaseGoal(goal: string, entries: KnowhowEntry[], topN = 5): KnowhowEntry[] {
|
|
675
|
+
if (entries.length === 0 || !goal.trim()) return [];
|
|
676
|
+
|
|
677
|
+
const queryTokens = new Set(_tokenize(goal));
|
|
678
|
+
if (queryTokens.size === 0) return entries.slice(0, topN);
|
|
679
|
+
|
|
680
|
+
// Build per-entry token lists from pattern_name + applicability (proxy for Problem+Solution)
|
|
681
|
+
const entryTexts = entries.map((e) =>
|
|
682
|
+
_tokenize(`${e.pattern_name} ${e.applicability} ${e.source}`)
|
|
683
|
+
);
|
|
684
|
+
|
|
685
|
+
// Compute IDF for each query token
|
|
686
|
+
const idf = new Map<string, number>();
|
|
687
|
+
for (const token of queryTokens) {
|
|
688
|
+
const docsWithToken = entryTexts.filter((tokens) => tokens.includes(token)).length;
|
|
689
|
+
idf.set(token, Math.log((entries.length + 1) / (docsWithToken + 1)) + 1);
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
// Score each entry
|
|
693
|
+
const scored = entries.map((entry, i) => {
|
|
694
|
+
const tokens = entryTexts[i];
|
|
695
|
+
const tf = new Map<string, number>();
|
|
696
|
+
for (const t of tokens) {
|
|
697
|
+
tf.set(t, (tf.get(t) ?? 0) + 1);
|
|
698
|
+
}
|
|
699
|
+
let score = 0;
|
|
700
|
+
for (const token of queryTokens) {
|
|
701
|
+
const termFreq = (tf.get(token) ?? 0) / Math.max(tokens.length, 1);
|
|
702
|
+
score += termFreq * (idf.get(token) ?? 1);
|
|
703
|
+
}
|
|
704
|
+
return { entry, score };
|
|
705
|
+
});
|
|
706
|
+
|
|
707
|
+
scored.sort((a, b) => b.score - a.score);
|
|
708
|
+
return scored.slice(0, topN).map((s) => s.entry);
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
/**
|
|
712
|
+
* CLI command: Rank KNOWHOW entries by relevance to a query string using TF-IDF.
|
|
713
|
+
*
|
|
714
|
+
* @param cwd - Project working directory
|
|
715
|
+
* @param query - Query string to rank against
|
|
716
|
+
* @param topN - Number of top entries to return (default 5)
|
|
717
|
+
* @param raw - Output raw text instead of JSON
|
|
718
|
+
*/
|
|
719
|
+
function cmdKnowhowRank(cwd: string, query: string, topN: number, raw: boolean): void {
|
|
720
|
+
// Codex r21 P2: KNOWHOW lives in milestone/research dirs and per-phase
|
|
721
|
+
// dirs, not the project root. Match the multi-location scan from
|
|
722
|
+
// r18 (lib/context/agents.ts knowhow_block + lib/commands/knowledge-search.ts).
|
|
723
|
+
const fs = require('fs') as typeof import('fs');
|
|
724
|
+
const candidates: string[] = [
|
|
725
|
+
path.join(cwd, '.planning', 'KNOWHOW.md'),
|
|
726
|
+
path.join(cwd, 'KNOWHOW.md'),
|
|
727
|
+
];
|
|
728
|
+
const milestonesBase = path.join(cwd, '.planning', 'milestones');
|
|
729
|
+
try {
|
|
730
|
+
for (const ms of fs.readdirSync(milestonesBase) as string[]) {
|
|
731
|
+
candidates.push(path.join(milestonesBase, ms, 'KNOWHOW.md'));
|
|
732
|
+
candidates.push(path.join(milestonesBase, ms, 'research', 'KNOWHOW.md'));
|
|
733
|
+
const phasesBase = path.join(milestonesBase, ms, 'phases');
|
|
734
|
+
try {
|
|
735
|
+
for (const ph of fs.readdirSync(phasesBase) as string[]) {
|
|
736
|
+
candidates.push(path.join(phasesBase, ph, 'KNOWHOW.md'));
|
|
737
|
+
}
|
|
738
|
+
} catch { /* skip */ }
|
|
739
|
+
}
|
|
740
|
+
} catch { /* skip */ }
|
|
741
|
+
const entries: ReturnType<typeof _cachedParseKnowhow> = [];
|
|
742
|
+
for (const p of candidates) entries.push(..._cachedParseKnowhow(p));
|
|
743
|
+
if (entries.length === 0) {
|
|
744
|
+
output({ query, top_n: topN, entries: [], message: 'KNOWHOW.md not found or empty' }, raw, 'KNOWHOW.md not found');
|
|
745
|
+
return;
|
|
746
|
+
}
|
|
747
|
+
const ranked = rankKnowhowByPhaseGoal(query, entries, topN);
|
|
748
|
+
const summaries = ranked.map((e) => ({
|
|
749
|
+
pattern_name: e.pattern_name,
|
|
750
|
+
phase_number: e.phase_number,
|
|
751
|
+
applicability: e.applicability.slice(0, 100),
|
|
752
|
+
}));
|
|
753
|
+
output({ query, top_n: topN, total: entries.length, entries: summaries }, raw,
|
|
754
|
+
ranked.map((e, i) => `${i + 1}. ${e.pattern_name} (phase ${e.phase_number})`).join('\n') || 'No entries');
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
module.exports = {
|
|
758
|
+
formatKnowhowEntry,
|
|
759
|
+
parseKnowhowEntries,
|
|
760
|
+
appendKnowhowEntries,
|
|
761
|
+
selectTopEntries,
|
|
762
|
+
buildKnowledgeInjectionBlock,
|
|
763
|
+
extractModuleHints,
|
|
764
|
+
rankKnowhowByPhaseGoal,
|
|
765
|
+
cmdKnowhowAudit,
|
|
766
|
+
cmdKnowhowDedup,
|
|
767
|
+
cmdKnowhowRank,
|
|
768
|
+
};
|