@harness-engineering/cli 1.13.0 → 1.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agents/skills/claude-code/add-harness-component/skill.yaml +1 -0
- package/dist/agents/skills/claude-code/align-documentation/skill.yaml +1 -0
- package/dist/agents/skills/claude-code/check-mechanical-constraints/skill.yaml +1 -0
- package/dist/agents/skills/claude-code/cleanup-dead-code/skill.yaml +1 -0
- package/dist/agents/skills/claude-code/detect-doc-drift/skill.yaml +1 -0
- package/dist/agents/skills/claude-code/enforce-architecture/skill.yaml +1 -0
- package/dist/agents/skills/claude-code/harness-accessibility/skill.yaml +1 -0
- package/dist/agents/skills/claude-code/harness-api-design/SKILL.md +304 -0
- package/dist/agents/skills/claude-code/harness-api-design/skill.yaml +74 -0
- package/dist/agents/skills/claude-code/harness-architecture-advisor/skill.yaml +1 -0
- package/dist/agents/skills/claude-code/harness-auth/SKILL.md +279 -0
- package/dist/agents/skills/claude-code/harness-auth/skill.yaml +81 -0
- package/dist/agents/skills/claude-code/harness-autopilot/skill.yaml +1 -0
- package/dist/agents/skills/claude-code/harness-brainstorming/SKILL.md +39 -0
- package/dist/agents/skills/claude-code/harness-brainstorming/skill.yaml +1 -0
- package/dist/agents/skills/claude-code/harness-caching/SKILL.md +309 -0
- package/dist/agents/skills/claude-code/harness-caching/skill.yaml +73 -0
- package/dist/agents/skills/claude-code/harness-chaos/SKILL.md +295 -0
- package/dist/agents/skills/claude-code/harness-chaos/skill.yaml +72 -0
- package/dist/agents/skills/claude-code/harness-code-review/SKILL.md +44 -0
- package/dist/agents/skills/claude-code/harness-code-review/skill.yaml +1 -0
- package/dist/agents/skills/claude-code/harness-codebase-cleanup/skill.yaml +1 -0
- package/dist/agents/skills/claude-code/harness-compliance/SKILL.md +303 -0
- package/dist/agents/skills/claude-code/harness-compliance/skill.yaml +78 -0
- package/dist/agents/skills/claude-code/harness-containerization/SKILL.md +284 -0
- package/dist/agents/skills/claude-code/harness-containerization/skill.yaml +80 -0
- package/dist/agents/skills/claude-code/harness-data-pipeline/SKILL.md +274 -0
- package/dist/agents/skills/claude-code/harness-data-pipeline/skill.yaml +81 -0
- package/dist/agents/skills/claude-code/harness-data-validation/SKILL.md +343 -0
- package/dist/agents/skills/claude-code/harness-data-validation/skill.yaml +75 -0
- package/dist/agents/skills/claude-code/harness-database/SKILL.md +258 -0
- package/dist/agents/skills/claude-code/harness-database/skill.yaml +80 -0
- package/dist/agents/skills/claude-code/harness-debugging/skill.yaml +1 -0
- package/dist/agents/skills/claude-code/harness-dependency-health/skill.yaml +1 -0
- package/dist/agents/skills/claude-code/harness-deployment/SKILL.md +255 -0
- package/dist/agents/skills/claude-code/harness-deployment/skill.yaml +77 -0
- package/dist/agents/skills/claude-code/harness-design/skill.yaml +1 -0
- package/dist/agents/skills/claude-code/harness-design-mobile/skill.yaml +1 -0
- package/dist/agents/skills/claude-code/harness-design-system/skill.yaml +1 -0
- package/dist/agents/skills/claude-code/harness-design-web/skill.yaml +1 -0
- package/dist/agents/skills/claude-code/harness-diagnostics/skill.yaml +1 -0
- package/dist/agents/skills/claude-code/harness-docs-pipeline/skill.yaml +1 -0
- package/dist/agents/skills/claude-code/harness-dx/SKILL.md +276 -0
- package/dist/agents/skills/claude-code/harness-dx/skill.yaml +76 -0
- package/dist/agents/skills/claude-code/harness-e2e/SKILL.md +245 -0
- package/dist/agents/skills/claude-code/harness-e2e/skill.yaml +78 -0
- package/dist/agents/skills/claude-code/harness-event-driven/SKILL.md +280 -0
- package/dist/agents/skills/claude-code/harness-event-driven/skill.yaml +77 -0
- package/dist/agents/skills/claude-code/harness-execution/SKILL.md +44 -0
- package/dist/agents/skills/claude-code/harness-execution/skill.yaml +1 -0
- package/dist/agents/skills/claude-code/harness-feature-flags/SKILL.md +287 -0
- package/dist/agents/skills/claude-code/harness-feature-flags/skill.yaml +74 -0
- package/dist/agents/skills/claude-code/harness-git-workflow/skill.yaml +1 -0
- package/dist/agents/skills/claude-code/harness-hotspot-detector/skill.yaml +1 -0
- package/dist/agents/skills/claude-code/harness-i18n/skill.yaml +1 -0
- package/dist/agents/skills/claude-code/harness-i18n-process/skill.yaml +1 -0
- package/dist/agents/skills/claude-code/harness-i18n-workflow/skill.yaml +1 -0
- package/dist/agents/skills/claude-code/harness-impact-analysis/skill.yaml +1 -0
- package/dist/agents/skills/claude-code/harness-incident-response/SKILL.md +223 -0
- package/dist/agents/skills/claude-code/harness-incident-response/skill.yaml +78 -0
- package/dist/agents/skills/claude-code/harness-infrastructure-as-code/SKILL.md +279 -0
- package/dist/agents/skills/claude-code/harness-infrastructure-as-code/skill.yaml +80 -0
- package/dist/agents/skills/claude-code/harness-integration-test/SKILL.md +271 -0
- package/dist/agents/skills/claude-code/harness-integration-test/skill.yaml +73 -0
- package/dist/agents/skills/claude-code/harness-integrity/skill.yaml +1 -0
- package/dist/agents/skills/claude-code/harness-knowledge-mapper/skill.yaml +1 -0
- package/dist/agents/skills/claude-code/harness-load-testing/SKILL.md +274 -0
- package/dist/agents/skills/claude-code/harness-load-testing/skill.yaml +79 -0
- package/dist/agents/skills/claude-code/harness-ml-ops/SKILL.md +341 -0
- package/dist/agents/skills/claude-code/harness-ml-ops/skill.yaml +79 -0
- package/dist/agents/skills/claude-code/harness-mobile-patterns/SKILL.md +326 -0
- package/dist/agents/skills/claude-code/harness-mobile-patterns/skill.yaml +82 -0
- package/dist/agents/skills/claude-code/harness-mutation-test/SKILL.md +251 -0
- package/dist/agents/skills/claude-code/harness-mutation-test/skill.yaml +70 -0
- package/dist/agents/skills/claude-code/harness-observability/SKILL.md +283 -0
- package/dist/agents/skills/claude-code/harness-observability/skill.yaml +78 -0
- package/dist/agents/skills/claude-code/harness-onboarding/skill.yaml +1 -0
- package/dist/agents/skills/claude-code/harness-parallel-agents/skill.yaml +1 -0
- package/dist/agents/skills/claude-code/harness-perf/skill.yaml +1 -0
- package/dist/agents/skills/claude-code/harness-perf-tdd/skill.yaml +1 -0
- package/dist/agents/skills/claude-code/harness-planning/SKILL.md +39 -0
- package/dist/agents/skills/claude-code/harness-planning/skill.yaml +1 -0
- package/dist/agents/skills/claude-code/harness-pre-commit-review/skill.yaml +1 -0
- package/dist/agents/skills/claude-code/harness-product-spec/SKILL.md +285 -0
- package/dist/agents/skills/claude-code/harness-product-spec/skill.yaml +72 -0
- package/dist/agents/skills/claude-code/harness-property-test/SKILL.md +281 -0
- package/dist/agents/skills/claude-code/harness-property-test/skill.yaml +71 -0
- package/dist/agents/skills/claude-code/harness-refactoring/skill.yaml +1 -0
- package/dist/agents/skills/claude-code/harness-release-readiness/SKILL.md +3 -3
- package/dist/agents/skills/claude-code/harness-release-readiness/skill.yaml +1 -0
- package/dist/agents/skills/claude-code/harness-resilience/SKILL.md +255 -0
- package/dist/agents/skills/claude-code/harness-resilience/skill.yaml +76 -0
- package/dist/agents/skills/claude-code/harness-roadmap/skill.yaml +1 -0
- package/dist/agents/skills/claude-code/harness-secrets/SKILL.md +293 -0
- package/dist/agents/skills/claude-code/harness-secrets/skill.yaml +76 -0
- package/dist/agents/skills/claude-code/harness-security-review/skill.yaml +1 -0
- package/dist/agents/skills/claude-code/harness-security-scan/skill.yaml +1 -0
- package/dist/agents/skills/claude-code/harness-skill-authoring/skill.yaml +1 -0
- package/dist/agents/skills/claude-code/harness-soundness-review/skill.yaml +1 -0
- package/dist/agents/skills/claude-code/harness-sql-review/SKILL.md +315 -0
- package/dist/agents/skills/claude-code/harness-sql-review/skill.yaml +74 -0
- package/dist/agents/skills/claude-code/harness-state-management/skill.yaml +1 -0
- package/dist/agents/skills/claude-code/harness-tdd/skill.yaml +1 -0
- package/dist/agents/skills/claude-code/harness-test-advisor/skill.yaml +1 -0
- package/dist/agents/skills/claude-code/harness-test-data/SKILL.md +268 -0
- package/dist/agents/skills/claude-code/harness-test-data/skill.yaml +74 -0
- package/dist/agents/skills/claude-code/harness-ux-copy/SKILL.md +271 -0
- package/dist/agents/skills/claude-code/harness-ux-copy/skill.yaml +77 -0
- package/dist/agents/skills/claude-code/harness-verification/SKILL.md +35 -0
- package/dist/agents/skills/claude-code/harness-verification/skill.yaml +1 -0
- package/dist/agents/skills/claude-code/harness-verify/skill.yaml +1 -0
- package/dist/agents/skills/claude-code/harness-visual-regression/SKILL.md +257 -0
- package/dist/agents/skills/claude-code/harness-visual-regression/skill.yaml +74 -0
- package/dist/agents/skills/claude-code/initialize-harness-project/SKILL.md +11 -3
- package/dist/agents/skills/claude-code/initialize-harness-project/skill.yaml +1 -0
- package/dist/agents/skills/claude-code/validate-context-engineering/skill.yaml +1 -0
- package/dist/agents/skills/gemini-cli/add-harness-component/skill.yaml +1 -0
- package/dist/agents/skills/gemini-cli/align-documentation/skill.yaml +1 -0
- package/dist/agents/skills/gemini-cli/check-mechanical-constraints/skill.yaml +1 -0
- package/dist/agents/skills/gemini-cli/cleanup-dead-code/skill.yaml +1 -0
- package/dist/agents/skills/gemini-cli/detect-doc-drift/skill.yaml +1 -0
- package/dist/agents/skills/gemini-cli/enforce-architecture/skill.yaml +1 -0
- package/dist/agents/skills/gemini-cli/harness-accessibility/skill.yaml +1 -0
- package/dist/agents/skills/gemini-cli/harness-api-design/SKILL.md +304 -0
- package/dist/agents/skills/gemini-cli/harness-api-design/skill.yaml +74 -0
- package/dist/agents/skills/gemini-cli/harness-architecture-advisor/skill.yaml +1 -0
- package/dist/agents/skills/gemini-cli/harness-auth/SKILL.md +279 -0
- package/dist/agents/skills/gemini-cli/harness-auth/skill.yaml +81 -0
- package/dist/agents/skills/gemini-cli/harness-autopilot/skill.yaml +1 -0
- package/dist/agents/skills/gemini-cli/harness-brainstorming/SKILL.md +39 -0
- package/dist/agents/skills/gemini-cli/harness-brainstorming/skill.yaml +1 -0
- package/dist/agents/skills/gemini-cli/harness-caching/SKILL.md +309 -0
- package/dist/agents/skills/gemini-cli/harness-caching/skill.yaml +73 -0
- package/dist/agents/skills/gemini-cli/harness-chaos/SKILL.md +295 -0
- package/dist/agents/skills/gemini-cli/harness-chaos/skill.yaml +72 -0
- package/dist/agents/skills/gemini-cli/harness-code-review/SKILL.md +44 -0
- package/dist/agents/skills/gemini-cli/harness-code-review/skill.yaml +1 -0
- package/dist/agents/skills/gemini-cli/harness-codebase-cleanup/skill.yaml +1 -0
- package/dist/agents/skills/gemini-cli/harness-compliance/SKILL.md +303 -0
- package/dist/agents/skills/gemini-cli/harness-compliance/skill.yaml +78 -0
- package/dist/agents/skills/gemini-cli/harness-containerization/SKILL.md +284 -0
- package/dist/agents/skills/gemini-cli/harness-containerization/skill.yaml +80 -0
- package/dist/agents/skills/gemini-cli/harness-data-pipeline/SKILL.md +274 -0
- package/dist/agents/skills/gemini-cli/harness-data-pipeline/skill.yaml +81 -0
- package/dist/agents/skills/gemini-cli/harness-data-validation/SKILL.md +343 -0
- package/dist/agents/skills/gemini-cli/harness-data-validation/skill.yaml +75 -0
- package/dist/agents/skills/gemini-cli/harness-database/SKILL.md +258 -0
- package/dist/agents/skills/gemini-cli/harness-database/skill.yaml +80 -0
- package/dist/agents/skills/gemini-cli/harness-debugging/skill.yaml +1 -0
- package/dist/agents/skills/gemini-cli/harness-dependency-health/skill.yaml +1 -0
- package/dist/agents/skills/gemini-cli/harness-deployment/SKILL.md +255 -0
- package/dist/agents/skills/gemini-cli/harness-deployment/skill.yaml +77 -0
- package/dist/agents/skills/gemini-cli/harness-design/skill.yaml +1 -0
- package/dist/agents/skills/gemini-cli/harness-design-mobile/skill.yaml +1 -0
- package/dist/agents/skills/gemini-cli/harness-design-system/skill.yaml +1 -0
- package/dist/agents/skills/gemini-cli/harness-design-web/skill.yaml +1 -0
- package/dist/agents/skills/gemini-cli/harness-diagnostics/skill.yaml +1 -0
- package/dist/agents/skills/gemini-cli/harness-docs-pipeline/skill.yaml +1 -0
- package/dist/agents/skills/gemini-cli/harness-dx/SKILL.md +276 -0
- package/dist/agents/skills/gemini-cli/harness-dx/skill.yaml +76 -0
- package/dist/agents/skills/gemini-cli/harness-e2e/SKILL.md +245 -0
- package/dist/agents/skills/gemini-cli/harness-e2e/skill.yaml +78 -0
- package/dist/agents/skills/gemini-cli/harness-event-driven/SKILL.md +280 -0
- package/dist/agents/skills/gemini-cli/harness-event-driven/skill.yaml +77 -0
- package/dist/agents/skills/gemini-cli/harness-execution/SKILL.md +44 -0
- package/dist/agents/skills/gemini-cli/harness-execution/skill.yaml +1 -0
- package/dist/agents/skills/gemini-cli/harness-feature-flags/SKILL.md +287 -0
- package/dist/agents/skills/gemini-cli/harness-feature-flags/skill.yaml +74 -0
- package/dist/agents/skills/gemini-cli/harness-git-workflow/skill.yaml +1 -0
- package/dist/agents/skills/gemini-cli/harness-hotspot-detector/skill.yaml +1 -0
- package/dist/agents/skills/gemini-cli/harness-i18n/skill.yaml +1 -0
- package/dist/agents/skills/gemini-cli/harness-i18n-process/skill.yaml +1 -0
- package/dist/agents/skills/gemini-cli/harness-i18n-workflow/skill.yaml +1 -0
- package/dist/agents/skills/gemini-cli/harness-impact-analysis/skill.yaml +1 -0
- package/dist/agents/skills/gemini-cli/harness-incident-response/SKILL.md +223 -0
- package/dist/agents/skills/gemini-cli/harness-incident-response/skill.yaml +78 -0
- package/dist/agents/skills/gemini-cli/harness-infrastructure-as-code/SKILL.md +279 -0
- package/dist/agents/skills/gemini-cli/harness-infrastructure-as-code/skill.yaml +80 -0
- package/dist/agents/skills/gemini-cli/harness-integration-test/SKILL.md +271 -0
- package/dist/agents/skills/gemini-cli/harness-integration-test/skill.yaml +73 -0
- package/dist/agents/skills/gemini-cli/harness-integrity/skill.yaml +1 -0
- package/dist/agents/skills/gemini-cli/harness-knowledge-mapper/skill.yaml +1 -0
- package/dist/agents/skills/gemini-cli/harness-load-testing/SKILL.md +274 -0
- package/dist/agents/skills/gemini-cli/harness-load-testing/skill.yaml +79 -0
- package/dist/agents/skills/gemini-cli/harness-ml-ops/SKILL.md +341 -0
- package/dist/agents/skills/gemini-cli/harness-ml-ops/skill.yaml +79 -0
- package/dist/agents/skills/gemini-cli/harness-mobile-patterns/SKILL.md +326 -0
- package/dist/agents/skills/gemini-cli/harness-mobile-patterns/skill.yaml +82 -0
- package/dist/agents/skills/gemini-cli/harness-mutation-test/SKILL.md +251 -0
- package/dist/agents/skills/gemini-cli/harness-mutation-test/skill.yaml +70 -0
- package/dist/agents/skills/gemini-cli/harness-observability/SKILL.md +283 -0
- package/dist/agents/skills/gemini-cli/harness-observability/skill.yaml +78 -0
- package/dist/agents/skills/gemini-cli/harness-onboarding/skill.yaml +1 -0
- package/dist/agents/skills/gemini-cli/harness-parallel-agents/skill.yaml +1 -0
- package/dist/agents/skills/gemini-cli/harness-perf/skill.yaml +1 -0
- package/dist/agents/skills/gemini-cli/harness-perf-tdd/skill.yaml +1 -0
- package/dist/agents/skills/gemini-cli/harness-planning/SKILL.md +39 -0
- package/dist/agents/skills/gemini-cli/harness-planning/skill.yaml +1 -0
- package/dist/agents/skills/gemini-cli/harness-pre-commit-review/skill.yaml +1 -0
- package/dist/agents/skills/gemini-cli/harness-product-spec/SKILL.md +285 -0
- package/dist/agents/skills/gemini-cli/harness-product-spec/skill.yaml +72 -0
- package/dist/agents/skills/gemini-cli/harness-property-test/SKILL.md +281 -0
- package/dist/agents/skills/gemini-cli/harness-property-test/skill.yaml +71 -0
- package/dist/agents/skills/gemini-cli/harness-refactoring/skill.yaml +1 -0
- package/dist/agents/skills/gemini-cli/harness-release-readiness/SKILL.md +3 -3
- package/dist/agents/skills/gemini-cli/harness-release-readiness/skill.yaml +1 -0
- package/dist/agents/skills/gemini-cli/harness-resilience/SKILL.md +255 -0
- package/dist/agents/skills/gemini-cli/harness-resilience/skill.yaml +76 -0
- package/dist/agents/skills/gemini-cli/harness-roadmap/skill.yaml +1 -0
- package/dist/agents/skills/gemini-cli/harness-secrets/SKILL.md +293 -0
- package/dist/agents/skills/gemini-cli/harness-secrets/skill.yaml +76 -0
- package/dist/agents/skills/gemini-cli/harness-security-review/SKILL.md +240 -0
- package/dist/agents/skills/gemini-cli/harness-security-review/skill.yaml +1 -0
- package/dist/agents/skills/gemini-cli/harness-security-scan/skill.yaml +1 -0
- package/dist/agents/skills/gemini-cli/harness-skill-authoring/skill.yaml +1 -0
- package/dist/agents/skills/gemini-cli/harness-soundness-review/skill.yaml +1 -0
- package/dist/agents/skills/gemini-cli/harness-sql-review/SKILL.md +315 -0
- package/dist/agents/skills/gemini-cli/harness-sql-review/skill.yaml +74 -0
- package/dist/agents/skills/gemini-cli/harness-state-management/skill.yaml +1 -0
- package/dist/agents/skills/gemini-cli/harness-tdd/skill.yaml +1 -0
- package/dist/agents/skills/gemini-cli/harness-test-advisor/skill.yaml +1 -0
- package/dist/agents/skills/gemini-cli/harness-test-data/SKILL.md +268 -0
- package/dist/agents/skills/gemini-cli/harness-test-data/skill.yaml +74 -0
- package/dist/agents/skills/gemini-cli/harness-ux-copy/SKILL.md +271 -0
- package/dist/agents/skills/gemini-cli/harness-ux-copy/skill.yaml +77 -0
- package/dist/agents/skills/gemini-cli/harness-verification/SKILL.md +35 -0
- package/dist/agents/skills/gemini-cli/harness-verification/skill.yaml +1 -0
- package/dist/agents/skills/gemini-cli/harness-verify/skill.yaml +1 -0
- package/dist/agents/skills/gemini-cli/harness-visual-regression/SKILL.md +257 -0
- package/dist/agents/skills/gemini-cli/harness-visual-regression/skill.yaml +74 -0
- package/dist/agents/skills/gemini-cli/initialize-harness-project/SKILL.md +11 -3
- package/dist/agents/skills/gemini-cli/initialize-harness-project/skill.yaml +1 -0
- package/dist/agents/skills/gemini-cli/validate-context-engineering/skill.yaml +1 -0
- package/dist/agents-md-YTYQDA3P.js +8 -0
- package/dist/{architecture-ESOOE26S.js → architecture-JQZYM4US.js} +4 -4
- package/dist/bin/harness-mcp.js +16 -15
- package/dist/bin/harness.js +31 -30
- package/dist/{check-phase-gate-S2MZKLFQ.js → check-phase-gate-L3RADYWO.js} +4 -3
- package/dist/{chunk-WPPDRIJL.js → chunk-3C2MLBPJ.js} +4 -4
- package/dist/chunk-6KTUUFRN.js +217 -0
- package/dist/{chunk-MI5XJQDY.js → chunk-7IP4JIFL.js} +24 -10
- package/dist/{chunk-C2ERUR3L.js → chunk-7MJAPE3Z.js} +165 -49
- package/dist/{chunk-KELT6K6M.js → chunk-ABQHQ6I5.js} +1861 -1418
- package/dist/{chunk-L2KLU56K.js → chunk-AOZRDOIP.js} +2 -2
- package/dist/{chunk-QPEH2QPG.js → chunk-DBSOCI3G.js} +53 -54
- package/dist/{chunk-MHBMTPW7.js → chunk-ERS5EVUZ.js} +9 -0
- package/dist/{chunk-JSTQ3AWB.js → chunk-FIAPHX37.js} +1 -1
- package/dist/{chunk-2YPZKGAG.js → chunk-FTMXDOR6.js} +1 -1
- package/dist/{chunk-72GHBOL2.js → chunk-GZKSBLQL.js} +1 -1
- package/dist/{chunk-K6XAPGML.js → chunk-H7Y5CKTM.js} +1 -1
- package/dist/{chunk-HD4IBGLA.js → chunk-N5G5QMS3.js} +24 -1
- package/dist/{chunk-LD3DKUK5.js → chunk-NLVUVUGD.js} +1 -1
- package/dist/{chunk-3KOLLWWE.js → chunk-O5OJVPL6.js} +26 -211
- package/dist/{chunk-NKDM3FMH.js → chunk-OD3S2NHN.js} +1 -1
- package/dist/{chunk-5VY23YK3.js → chunk-OSXBPAMK.js} +2 -2
- package/dist/{chunk-MACVXDZK.js → chunk-OXLLOSSR.js} +45 -47
- package/dist/{chunk-GNGELAXY.js → chunk-RCWZBSK5.js} +2 -2
- package/dist/{chunk-PSNN4LWX.js → chunk-S2FXOWOR.js} +3 -3
- package/dist/{chunk-VUCPTQ6G.js → chunk-SD3SQOZ2.js} +1 -1
- package/dist/{chunk-7PZWR4LI.js → chunk-TPOTOBR7.js} +9 -9
- package/dist/{chunk-RZSUJBZZ.js → chunk-XKECDXJS.js} +452 -353
- package/dist/{chunk-VRFZWGMS.js → chunk-XYLGHKG6.js} +5 -1
- package/dist/{chunk-6N4R6FVX.js → chunk-YBJ262QL.js} +1 -1
- package/dist/{chunk-2VU4MFM3.js → chunk-YPYGXRDR.js} +7 -7
- package/dist/{chunk-Q6AB7W5Z.js → chunk-YQ6KC6TE.js} +1 -1
- package/dist/{chunk-7KQSUZVG.js → chunk-YZD2MRNQ.js} +1528 -1010
- package/dist/ci-workflow-EQZFVX3P.js +8 -0
- package/dist/{create-skill-WPXHSLX2.js → create-skill-XSWHMSM5.js} +2 -2
- package/dist/{dist-M6BQODWC.js → dist-B26DFXMP.js} +573 -480
- package/dist/{dist-L7LAAQAS.js → dist-DZ63LLUD.js} +1 -1
- package/dist/{dist-WF4C7A4A.js → dist-HWXF2C3R.js} +18 -2
- package/dist/{dist-D4RYGUZE.js → dist-USY2C5JL.js} +3 -1
- package/dist/{docs-BPYCN2DR.js → docs-7ECGYMAV.js} +5 -3
- package/dist/engine-EG4EH4IX.js +8 -0
- package/dist/{entropy-4VDVV5CR.js → entropy-5USWKLVS.js} +3 -3
- package/dist/{feedback-63QB5RCA.js → feedback-UTBXZZHF.js} +1 -1
- package/dist/{generate-agent-definitions-QABOJG56.js → generate-agent-definitions-3PM5EU7V.js} +5 -5
- package/dist/{glob-helper-5OHBUQAI.js → glob-helper-R5FXNUPS.js} +1 -1
- package/dist/{graph-loader-KO4GJ5N2.js → graph-loader-2M2HXDQI.js} +1 -1
- package/dist/index.d.ts +183 -17
- package/dist/index.js +32 -30
- package/dist/loader-ZPALXIVR.js +10 -0
- package/dist/mcp-362EZHF4.js +35 -0
- package/dist/{performance-26BH47O4.js → performance-OQAFMJUD.js} +3 -3
- package/dist/{review-pipeline-GHR3WFBI.js → review-pipeline-C4GCFVGP.js} +1 -1
- package/dist/runtime-7YLVK453.js +9 -0
- package/dist/{security-UQFUZXEN.js → security-PZOX7AQS.js} +1 -1
- package/dist/skill-executor-XZLYZYAK.js +8 -0
- package/dist/templates/axum/Cargo.toml.hbs +8 -0
- package/dist/templates/axum/src/main.rs +12 -0
- package/dist/templates/axum/template.json +16 -0
- package/dist/templates/django/manage.py.hbs +19 -0
- package/dist/templates/django/requirements.txt.hbs +1 -0
- package/dist/templates/django/src/settings.py.hbs +44 -0
- package/dist/templates/django/src/urls.py +6 -0
- package/dist/templates/django/src/wsgi.py.hbs +9 -0
- package/dist/templates/django/template.json +21 -0
- package/dist/templates/express/package.json.hbs +15 -0
- package/dist/templates/express/src/app.ts +12 -0
- package/dist/templates/express/src/lib/.gitkeep +0 -0
- package/dist/templates/express/template.json +16 -0
- package/dist/templates/fastapi/requirements.txt.hbs +2 -0
- package/dist/templates/fastapi/src/main.py +8 -0
- package/dist/templates/fastapi/template.json +20 -0
- package/dist/templates/gin/go.mod.hbs +5 -0
- package/dist/templates/gin/main.go +15 -0
- package/dist/templates/gin/template.json +19 -0
- package/dist/templates/go-base/.golangci.yml +16 -0
- package/dist/templates/go-base/AGENTS.md.hbs +35 -0
- package/dist/templates/go-base/go.mod.hbs +3 -0
- package/dist/templates/go-base/harness.config.json.hbs +17 -0
- package/dist/templates/go-base/main.go +7 -0
- package/dist/templates/go-base/template.json +14 -0
- package/dist/templates/java-base/AGENTS.md.hbs +35 -0
- package/dist/templates/java-base/checkstyle.xml +20 -0
- package/dist/templates/java-base/harness.config.json.hbs +16 -0
- package/dist/templates/java-base/pom.xml.hbs +39 -0
- package/dist/templates/java-base/src/main/java/App.java.hbs +5 -0
- package/dist/templates/java-base/template.json +13 -0
- package/dist/templates/nestjs/nest-cli.json +5 -0
- package/dist/templates/nestjs/package.json.hbs +18 -0
- package/dist/templates/nestjs/src/app.module.ts +8 -0
- package/dist/templates/nestjs/src/lib/.gitkeep +0 -0
- package/dist/templates/nestjs/src/main.ts +11 -0
- package/dist/templates/nestjs/template.json +16 -0
- package/dist/templates/nextjs/template.json +15 -1
- package/dist/templates/python-base/.python-version +1 -0
- package/dist/templates/python-base/AGENTS.md.hbs +32 -0
- package/dist/templates/python-base/harness.config.json.hbs +16 -0
- package/dist/templates/python-base/pyproject.toml.hbs +18 -0
- package/dist/templates/python-base/ruff.toml +5 -0
- package/dist/templates/python-base/src/__init__.py +0 -0
- package/dist/templates/python-base/template.json +13 -0
- package/dist/templates/react-vite/index.html +12 -0
- package/dist/templates/react-vite/package.json.hbs +18 -0
- package/dist/templates/react-vite/src/App.tsx +7 -0
- package/dist/templates/react-vite/src/lib/.gitkeep +0 -0
- package/dist/templates/react-vite/src/main.tsx +9 -0
- package/dist/templates/react-vite/template.json +19 -0
- package/dist/templates/react-vite/vite.config.ts +6 -0
- package/dist/templates/rust-base/AGENTS.md.hbs +35 -0
- package/dist/templates/rust-base/Cargo.toml.hbs +6 -0
- package/dist/templates/rust-base/clippy.toml +2 -0
- package/dist/templates/rust-base/harness.config.json.hbs +17 -0
- package/dist/templates/rust-base/src/main.rs +3 -0
- package/dist/templates/rust-base/template.json +14 -0
- package/dist/templates/spring-boot/pom.xml.hbs +50 -0
- package/dist/templates/spring-boot/src/main/java/Application.java.hbs +19 -0
- package/dist/templates/spring-boot/template.json +15 -0
- package/dist/templates/vue/index.html +12 -0
- package/dist/templates/vue/package.json.hbs +16 -0
- package/dist/templates/vue/src/App.vue +7 -0
- package/dist/templates/vue/src/lib/.gitkeep +0 -0
- package/dist/templates/vue/src/main.ts +4 -0
- package/dist/templates/vue/template.json +19 -0
- package/dist/templates/vue/vite.config.ts +6 -0
- package/dist/{validate-N7QJOKFZ.js → validate-FD3Z6VJD.js} +4 -4
- package/dist/validate-cross-check-WNJM6H2D.js +8 -0
- package/package.json +6 -6
- package/dist/agents-md-P2RHSUV7.js +0 -8
- package/dist/ci-workflow-4NYBUG6R.js +0 -8
- package/dist/engine-LXLIWQQ3.js +0 -8
- package/dist/loader-Z2IT7QX3.js +0 -10
- package/dist/mcp-KQHEL5IF.js +0 -34
- package/dist/runtime-PDWD7UIK.js +0 -9
- package/dist/skill-executor-RG45LUO5.js +0 -8
- package/dist/validate-cross-check-EDQ5QGTM.js +0 -8
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
detectEntropyDefinition,
|
|
3
3
|
handleDetectEntropy
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-OXLLOSSR.js";
|
|
5
5
|
import {
|
|
6
6
|
checkPerformanceDefinition,
|
|
7
7
|
getCriticalPathsDefinition,
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
handleGetPerfBaselines,
|
|
12
12
|
handleUpdatePerfBaselines,
|
|
13
13
|
updatePerfBaselinesDefinition
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-YPYGXRDR.js";
|
|
15
15
|
import {
|
|
16
16
|
analyzeDiffDefinition,
|
|
17
17
|
createSelfReviewDefinition,
|
|
@@ -19,15 +19,15 @@ import {
|
|
|
19
19
|
handleCreateSelfReview,
|
|
20
20
|
handleRequestPeerReview,
|
|
21
21
|
requestPeerReviewDefinition
|
|
22
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-TPOTOBR7.js";
|
|
23
23
|
import {
|
|
24
24
|
handleRunSecurityScan,
|
|
25
25
|
runSecurityScanDefinition
|
|
26
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-OSXBPAMK.js";
|
|
27
27
|
import {
|
|
28
28
|
handleRunCodeReview,
|
|
29
29
|
runCodeReviewDefinition
|
|
30
|
-
} from "./chunk-
|
|
30
|
+
} from "./chunk-NLVUVUGD.js";
|
|
31
31
|
import {
|
|
32
32
|
GENERATED_HEADER_CLAUDE,
|
|
33
33
|
GENERATED_HEADER_GEMINI,
|
|
@@ -38,21 +38,24 @@ import {
|
|
|
38
38
|
import {
|
|
39
39
|
handleValidateProject,
|
|
40
40
|
validateToolDefinition
|
|
41
|
-
} from "./chunk-
|
|
41
|
+
} from "./chunk-S2FXOWOR.js";
|
|
42
42
|
import {
|
|
43
43
|
loadGraphStore
|
|
44
|
-
} from "./chunk-
|
|
44
|
+
} from "./chunk-FTMXDOR6.js";
|
|
45
45
|
import {
|
|
46
46
|
checkDependenciesDefinition,
|
|
47
47
|
handleCheckDependencies
|
|
48
|
-
} from "./chunk-
|
|
48
|
+
} from "./chunk-3C2MLBPJ.js";
|
|
49
49
|
import {
|
|
50
50
|
resolveProjectConfig
|
|
51
|
-
} from "./chunk-
|
|
51
|
+
} from "./chunk-H7Y5CKTM.js";
|
|
52
52
|
import {
|
|
53
53
|
checkDocsDefinition,
|
|
54
54
|
handleCheckDocs
|
|
55
|
-
} from "./chunk-
|
|
55
|
+
} from "./chunk-7IP4JIFL.js";
|
|
56
|
+
import {
|
|
57
|
+
resolveConfig
|
|
58
|
+
} from "./chunk-O5OJVPL6.js";
|
|
56
59
|
import {
|
|
57
60
|
resultToMcpResponse
|
|
58
61
|
} from "./chunk-IDZNPTYD.js";
|
|
@@ -60,13 +63,14 @@ import {
|
|
|
60
63
|
sanitizePath
|
|
61
64
|
} from "./chunk-W6Y7ZW3Y.js";
|
|
62
65
|
import {
|
|
66
|
+
resolveAllSkillsDirs,
|
|
63
67
|
resolveCommunitySkillsDir,
|
|
64
68
|
resolveGlobalSkillsDir,
|
|
65
69
|
resolvePersonasDir,
|
|
66
70
|
resolveProjectSkillsDir,
|
|
67
71
|
resolveSkillsDir,
|
|
68
72
|
resolveTemplatesDir
|
|
69
|
-
} from "./chunk-
|
|
73
|
+
} from "./chunk-N5G5QMS3.js";
|
|
70
74
|
import {
|
|
71
75
|
CLIError,
|
|
72
76
|
ExitCode,
|
|
@@ -74,11 +78,11 @@ import {
|
|
|
74
78
|
} from "./chunk-3WGJMBKH.js";
|
|
75
79
|
import {
|
|
76
80
|
SkillMetadataSchema
|
|
77
|
-
} from "./chunk-
|
|
81
|
+
} from "./chunk-XYLGHKG6.js";
|
|
78
82
|
import {
|
|
79
83
|
Err,
|
|
80
84
|
Ok
|
|
81
|
-
} from "./chunk-
|
|
85
|
+
} from "./chunk-ERS5EVUZ.js";
|
|
82
86
|
|
|
83
87
|
// src/mcp/server.ts
|
|
84
88
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
@@ -105,7 +109,7 @@ var generateLinterDefinition = {
|
|
|
105
109
|
};
|
|
106
110
|
async function handleGenerateLinter(input) {
|
|
107
111
|
try {
|
|
108
|
-
const { generate } = await import("./dist-
|
|
112
|
+
const { generate } = await import("./dist-DZ63LLUD.js");
|
|
109
113
|
const result = await generate({
|
|
110
114
|
configPath: sanitizePath(input.configPath),
|
|
111
115
|
...input.outputDir !== void 0 && { outputDir: sanitizePath(input.outputDir) }
|
|
@@ -139,7 +143,7 @@ var validateLinterConfigDefinition = {
|
|
|
139
143
|
};
|
|
140
144
|
async function handleValidateLinterConfig(input) {
|
|
141
145
|
try {
|
|
142
|
-
const { validate } = await import("./dist-
|
|
146
|
+
const { validate } = await import("./dist-DZ63LLUD.js");
|
|
143
147
|
const result = await validate({ configPath: sanitizePath(input.configPath) });
|
|
144
148
|
if ("success" in result && result.success) {
|
|
145
149
|
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
@@ -159,7 +163,179 @@ async function handleValidateLinterConfig(input) {
|
|
|
159
163
|
}
|
|
160
164
|
|
|
161
165
|
// src/mcp/tools/init.ts
|
|
166
|
+
import * as path2 from "path";
|
|
167
|
+
import * as fs2 from "fs";
|
|
168
|
+
|
|
169
|
+
// src/templates/post-write.ts
|
|
170
|
+
import * as fs from "fs";
|
|
162
171
|
import * as path from "path";
|
|
172
|
+
|
|
173
|
+
// src/templates/agents-append.ts
|
|
174
|
+
var FRAMEWORK_SECTIONS = {
|
|
175
|
+
nextjs: {
|
|
176
|
+
title: "Next.js Conventions",
|
|
177
|
+
content: [
|
|
178
|
+
"- Use the App Router (`src/app/`) for all routes",
|
|
179
|
+
'- Server Components by default; add `"use client"` only when needed',
|
|
180
|
+
"- Use `next/image` for images and `next/link` for navigation",
|
|
181
|
+
"- API routes go in `src/app/api/`",
|
|
182
|
+
"- Run `next dev` for development, `next build` for production"
|
|
183
|
+
].join("\n")
|
|
184
|
+
},
|
|
185
|
+
"react-vite": {
|
|
186
|
+
title: "React + Vite Conventions",
|
|
187
|
+
content: [
|
|
188
|
+
"- Component files use `.tsx` extension in `src/`",
|
|
189
|
+
"- Use Vite for dev server and bundling (`npm run dev`)",
|
|
190
|
+
"- Prefer function components with hooks",
|
|
191
|
+
"- CSS modules or styled-components for styling",
|
|
192
|
+
"- Tests use Vitest (`npm test`)"
|
|
193
|
+
].join("\n")
|
|
194
|
+
},
|
|
195
|
+
vue: {
|
|
196
|
+
title: "Vue Conventions",
|
|
197
|
+
content: [
|
|
198
|
+
"- Single File Components (`.vue`) in `src/`",
|
|
199
|
+
"- Use `<script setup>` with Composition API",
|
|
200
|
+
"- Vite for dev server and bundling (`npm run dev`)",
|
|
201
|
+
"- Vue Router for routing, Pinia for state management",
|
|
202
|
+
"- Tests use Vitest (`npm test`)"
|
|
203
|
+
].join("\n")
|
|
204
|
+
},
|
|
205
|
+
express: {
|
|
206
|
+
title: "Express Conventions",
|
|
207
|
+
content: [
|
|
208
|
+
"- Entry point at `src/app.ts`",
|
|
209
|
+
"- Routes in `src/routes/`, middleware in `src/middleware/`",
|
|
210
|
+
"- Use `express.json()` for body parsing",
|
|
211
|
+
"- Error handling via centralized error middleware",
|
|
212
|
+
"- Tests use Vitest with supertest (`npm test`)"
|
|
213
|
+
].join("\n")
|
|
214
|
+
},
|
|
215
|
+
nestjs: {
|
|
216
|
+
title: "NestJS Conventions",
|
|
217
|
+
content: [
|
|
218
|
+
"- Module-based architecture: each feature in its own module",
|
|
219
|
+
"- Use decorators (`@Controller`, `@Injectable`, `@Module`)",
|
|
220
|
+
"- Entry point at `src/main.ts`, root module at `src/app.module.ts`",
|
|
221
|
+
"- Use Nest CLI for generating components (`nest g`)",
|
|
222
|
+
"- Tests use Vitest (`npm test`)"
|
|
223
|
+
].join("\n")
|
|
224
|
+
},
|
|
225
|
+
fastapi: {
|
|
226
|
+
title: "FastAPI Conventions",
|
|
227
|
+
content: [
|
|
228
|
+
"- Entry point at `src/main.py` with FastAPI app instance",
|
|
229
|
+
"- Use Pydantic models for request/response validation",
|
|
230
|
+
"- Async endpoints preferred; sync is acceptable for CPU-bound work",
|
|
231
|
+
"- Run with `uvicorn src.main:app --reload` for development",
|
|
232
|
+
"- Tests use pytest (`pytest`)"
|
|
233
|
+
].join("\n")
|
|
234
|
+
},
|
|
235
|
+
django: {
|
|
236
|
+
title: "Django Conventions",
|
|
237
|
+
content: [
|
|
238
|
+
"- Settings at `src/settings.py`, URLs at `src/urls.py`",
|
|
239
|
+
"- Use `manage.py` for management commands",
|
|
240
|
+
"- Apps in `src/` directory; each app has models, views, urls",
|
|
241
|
+
"- Run with `python manage.py runserver` for development",
|
|
242
|
+
"- Tests use pytest with pytest-django (`pytest`)"
|
|
243
|
+
].join("\n")
|
|
244
|
+
},
|
|
245
|
+
gin: {
|
|
246
|
+
title: "Gin Conventions",
|
|
247
|
+
content: [
|
|
248
|
+
"- Entry point at `main.go` with Gin router setup",
|
|
249
|
+
"- Group routes by feature using `router.Group()`",
|
|
250
|
+
"- Use middleware for logging, auth, error recovery",
|
|
251
|
+
"- Run with `go run main.go` for development",
|
|
252
|
+
"- Tests use `go test ./...`"
|
|
253
|
+
].join("\n")
|
|
254
|
+
},
|
|
255
|
+
axum: {
|
|
256
|
+
title: "Axum Conventions",
|
|
257
|
+
content: [
|
|
258
|
+
"- Entry point at `src/main.rs` with Axum router",
|
|
259
|
+
"- Use extractors for request parsing (`Path`, `Query`, `Json`)",
|
|
260
|
+
"- Shared state via `Extension` or `State`",
|
|
261
|
+
"- Run with `cargo run` for development",
|
|
262
|
+
"- Tests use `cargo test`"
|
|
263
|
+
].join("\n")
|
|
264
|
+
},
|
|
265
|
+
"spring-boot": {
|
|
266
|
+
title: "Spring Boot Conventions",
|
|
267
|
+
content: [
|
|
268
|
+
"- Entry point annotated with `@SpringBootApplication`",
|
|
269
|
+
"- Controllers in `controller/` package, services in `service/`",
|
|
270
|
+
"- Use constructor injection for dependencies",
|
|
271
|
+
"- Run with `mvn spring-boot:run` for development",
|
|
272
|
+
"- Tests use JUnit 5 with Spring Boot Test (`mvn test`)"
|
|
273
|
+
].join("\n")
|
|
274
|
+
}
|
|
275
|
+
};
|
|
276
|
+
function buildFrameworkSection(framework) {
|
|
277
|
+
const entry = FRAMEWORK_SECTIONS[framework];
|
|
278
|
+
if (!entry) return "";
|
|
279
|
+
return `## ${entry.title}
|
|
280
|
+
|
|
281
|
+
<!-- framework: ${framework} -->
|
|
282
|
+
${entry.content}
|
|
283
|
+
`;
|
|
284
|
+
}
|
|
285
|
+
function appendFrameworkSection(existingContent, framework, _language) {
|
|
286
|
+
if (!framework) return existingContent;
|
|
287
|
+
const startMarker = `<!-- harness:framework-conventions:${framework} -->`;
|
|
288
|
+
const endMarker = `<!-- /harness:framework-conventions:${framework} -->`;
|
|
289
|
+
if (existingContent.includes(startMarker)) return existingContent;
|
|
290
|
+
const section = buildFrameworkSection(framework);
|
|
291
|
+
if (!section) return existingContent;
|
|
292
|
+
const block = `
|
|
293
|
+
${startMarker}
|
|
294
|
+
${section}${endMarker}
|
|
295
|
+
`;
|
|
296
|
+
return existingContent.trimEnd() + "\n" + block;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// src/templates/post-write.ts
|
|
300
|
+
function persistToolingConfig(targetDir, resolveResult, framework) {
|
|
301
|
+
const configPath = path.join(targetDir, "harness.config.json");
|
|
302
|
+
if (!fs.existsSync(configPath)) return;
|
|
303
|
+
try {
|
|
304
|
+
const config = JSON.parse(fs.readFileSync(configPath, "utf-8"));
|
|
305
|
+
const overlayMeta = resolveResult.overlayMetadata;
|
|
306
|
+
if (framework) {
|
|
307
|
+
config.template = config.template || {};
|
|
308
|
+
config.template.framework = framework;
|
|
309
|
+
}
|
|
310
|
+
if (overlayMeta?.tooling) {
|
|
311
|
+
config.tooling = { ...config.tooling, ...overlayMeta.tooling };
|
|
312
|
+
delete config.tooling.lockFile;
|
|
313
|
+
} else if (resolveResult.metadata.tooling && !config.tooling) {
|
|
314
|
+
config.tooling = { ...resolveResult.metadata.tooling };
|
|
315
|
+
delete config.tooling.lockFile;
|
|
316
|
+
}
|
|
317
|
+
if (config.template?.level === null || config.template?.level === void 0) {
|
|
318
|
+
delete config.template.level;
|
|
319
|
+
}
|
|
320
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
321
|
+
} catch {
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
function appendFrameworkAgents(targetDir, framework, language) {
|
|
325
|
+
if (!framework) return;
|
|
326
|
+
const agentsPath = path.join(targetDir, "AGENTS.md");
|
|
327
|
+
if (!fs.existsSync(agentsPath)) return;
|
|
328
|
+
try {
|
|
329
|
+
const existing = fs.readFileSync(agentsPath, "utf-8");
|
|
330
|
+
const updated = appendFrameworkSection(existing, framework, language);
|
|
331
|
+
if (updated !== existing) {
|
|
332
|
+
fs.writeFileSync(agentsPath, updated);
|
|
333
|
+
}
|
|
334
|
+
} catch {
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// src/mcp/tools/init.ts
|
|
163
339
|
var initProjectDefinition = {
|
|
164
340
|
name: "init_project",
|
|
165
341
|
description: "Scaffold a new harness engineering project from a template",
|
|
@@ -171,54 +347,109 @@ var initProjectDefinition = {
|
|
|
171
347
|
level: {
|
|
172
348
|
type: "string",
|
|
173
349
|
enum: ["basic", "intermediate", "advanced"],
|
|
174
|
-
description: "Adoption level"
|
|
350
|
+
description: "Adoption level (JS/TS only)"
|
|
175
351
|
},
|
|
176
|
-
framework: { type: "string", description: "Framework overlay (e.g., nextjs)" }
|
|
352
|
+
framework: { type: "string", description: "Framework overlay (e.g., nextjs, fastapi, gin)" },
|
|
353
|
+
language: {
|
|
354
|
+
type: "string",
|
|
355
|
+
enum: ["typescript", "python", "go", "rust", "java"],
|
|
356
|
+
description: "Target language"
|
|
357
|
+
}
|
|
177
358
|
},
|
|
178
359
|
required: ["path"]
|
|
179
360
|
}
|
|
180
361
|
};
|
|
362
|
+
function mcpText(text, isError = false) {
|
|
363
|
+
return { content: [{ type: "text", text }], isError };
|
|
364
|
+
}
|
|
365
|
+
function findFrameworkLanguage(engine, framework) {
|
|
366
|
+
const templates = engine.listTemplates();
|
|
367
|
+
if (!templates.ok || !templates.value) return void 0;
|
|
368
|
+
return templates.value.find((t) => t.framework === framework)?.language;
|
|
369
|
+
}
|
|
370
|
+
function tryDetectFramework(engine, safePath, input) {
|
|
371
|
+
if (input.framework || input.language || !fs2.existsSync(safePath)) return null;
|
|
372
|
+
const result = engine.detectFramework(safePath);
|
|
373
|
+
if (!result.ok || !result.value || result.value.length === 0) return null;
|
|
374
|
+
const candidates = result.value.map((c) => `${c.framework} (${c.language}, score: ${c.score})`).join(", ");
|
|
375
|
+
return mcpText(
|
|
376
|
+
`Detected frameworks: ${candidates}
|
|
377
|
+
|
|
378
|
+
Re-invoke with --framework <name> to scaffold, or specify --language for a bare scaffold.`
|
|
379
|
+
);
|
|
380
|
+
}
|
|
381
|
+
function checkFrameworkLanguageConflict(engine, input) {
|
|
382
|
+
if (!input.framework || !input.language) return null;
|
|
383
|
+
const fwLang = findFrameworkLanguage(engine, input.framework);
|
|
384
|
+
if (fwLang && fwLang !== input.language) {
|
|
385
|
+
return mcpText(
|
|
386
|
+
`Framework "${input.framework}" is a ${fwLang} framework, but language "${input.language}" was specified. Use language "${fwLang}" instead.`,
|
|
387
|
+
true
|
|
388
|
+
);
|
|
389
|
+
}
|
|
390
|
+
return null;
|
|
391
|
+
}
|
|
392
|
+
function inferLanguage(engine, input) {
|
|
393
|
+
if (input.language) return input.language;
|
|
394
|
+
if (!input.framework) return void 0;
|
|
395
|
+
return findFrameworkLanguage(engine, input.framework);
|
|
396
|
+
}
|
|
397
|
+
function scaffoldMcp(engine, safePath, i, language) {
|
|
398
|
+
const isNonJs = language && language !== "typescript";
|
|
399
|
+
const level = isNonJs ? void 0 : i.level ?? "basic";
|
|
400
|
+
const resolveResult = engine.resolveTemplate(level, i.framework, language);
|
|
401
|
+
if (!resolveResult.ok) return resultToMcpResponse(resolveResult);
|
|
402
|
+
const renderResult = engine.render(resolveResult.value, {
|
|
403
|
+
projectName: i.name ?? path2.basename(safePath),
|
|
404
|
+
level: level ?? "",
|
|
405
|
+
...i.framework !== void 0 && { framework: i.framework },
|
|
406
|
+
...language !== void 0 && { language }
|
|
407
|
+
});
|
|
408
|
+
if (!renderResult.ok) return resultToMcpResponse(renderResult);
|
|
409
|
+
const writeResult = engine.write(renderResult.value, safePath, {
|
|
410
|
+
overwrite: false,
|
|
411
|
+
...language !== void 0 && { language }
|
|
412
|
+
});
|
|
413
|
+
if (writeResult.ok) {
|
|
414
|
+
persistToolingConfig(safePath, resolveResult.value, i.framework);
|
|
415
|
+
appendFrameworkAgents(safePath, i.framework, language);
|
|
416
|
+
}
|
|
417
|
+
if (writeResult.ok && writeResult.value.skippedConfigs.length > 0) {
|
|
418
|
+
const skippedMsg = writeResult.value.skippedConfigs.map((f) => ` - ${f}`).join("\n");
|
|
419
|
+
return mcpText(
|
|
420
|
+
`Files written: ${writeResult.value.written.join(", ")}
|
|
421
|
+
|
|
422
|
+
Skipped existing config files (add harness dependencies manually):
|
|
423
|
+
${skippedMsg}`
|
|
424
|
+
);
|
|
425
|
+
}
|
|
426
|
+
return resultToMcpResponse(writeResult);
|
|
427
|
+
}
|
|
181
428
|
async function handleInitProject(input) {
|
|
429
|
+
const i = input;
|
|
182
430
|
try {
|
|
183
|
-
const { TemplateEngine } = await import("./engine-
|
|
184
|
-
const
|
|
185
|
-
const
|
|
186
|
-
const
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
projectName: input.name ?? path.basename(safePath),
|
|
192
|
-
level,
|
|
193
|
-
...input.framework !== void 0 && { framework: input.framework }
|
|
194
|
-
});
|
|
195
|
-
if (!renderResult.ok) return resultToMcpResponse(renderResult);
|
|
196
|
-
const writeResult = engine.write(renderResult.value, safePath, {
|
|
197
|
-
overwrite: false
|
|
198
|
-
});
|
|
199
|
-
return resultToMcpResponse(writeResult);
|
|
431
|
+
const { TemplateEngine } = await import("./engine-EG4EH4IX.js");
|
|
432
|
+
const engine = new TemplateEngine(resolveTemplatesDir());
|
|
433
|
+
const safePath = sanitizePath(i.path);
|
|
434
|
+
const detected = tryDetectFramework(engine, safePath, i);
|
|
435
|
+
if (detected) return detected;
|
|
436
|
+
const conflict = checkFrameworkLanguageConflict(engine, i);
|
|
437
|
+
if (conflict) return conflict;
|
|
438
|
+
return scaffoldMcp(engine, safePath, i, inferLanguage(engine, i));
|
|
200
439
|
} catch (error) {
|
|
201
|
-
return {
|
|
202
|
-
content: [
|
|
203
|
-
{
|
|
204
|
-
type: "text",
|
|
205
|
-
text: `Init failed: ${error instanceof Error ? error.message : String(error)}`
|
|
206
|
-
}
|
|
207
|
-
],
|
|
208
|
-
isError: true
|
|
209
|
-
};
|
|
440
|
+
return mcpText(`Init failed: ${error instanceof Error ? error.message : String(error)}`, true);
|
|
210
441
|
}
|
|
211
442
|
}
|
|
212
443
|
|
|
213
444
|
// src/mcp/tools/persona.ts
|
|
214
|
-
import * as
|
|
445
|
+
import * as path3 from "path";
|
|
215
446
|
var listPersonasDefinition = {
|
|
216
447
|
name: "list_personas",
|
|
217
448
|
description: "List available agent personas",
|
|
218
449
|
inputSchema: { type: "object", properties: {} }
|
|
219
450
|
};
|
|
220
451
|
async function handleListPersonas() {
|
|
221
|
-
const { listPersonas } = await import("./loader-
|
|
452
|
+
const { listPersonas } = await import("./loader-ZPALXIVR.js");
|
|
222
453
|
const result = listPersonas(resolvePersonasDir());
|
|
223
454
|
return resultToMcpResponse(result);
|
|
224
455
|
}
|
|
@@ -242,12 +473,12 @@ async function handleGeneratePersonaArtifacts(input) {
|
|
|
242
473
|
if (!/^[a-z0-9][a-z0-9._-]*$/i.test(input.name)) {
|
|
243
474
|
return resultToMcpResponse(Err(new Error(`Invalid persona name: ${input.name}`)));
|
|
244
475
|
}
|
|
245
|
-
const { loadPersona } = await import("./loader-
|
|
246
|
-
const { generateRuntime } = await import("./runtime-
|
|
247
|
-
const { generateAgentsMd } = await import("./agents-md-
|
|
248
|
-
const { generateCIWorkflow } = await import("./ci-workflow-
|
|
476
|
+
const { loadPersona } = await import("./loader-ZPALXIVR.js");
|
|
477
|
+
const { generateRuntime } = await import("./runtime-7YLVK453.js");
|
|
478
|
+
const { generateAgentsMd } = await import("./agents-md-YTYQDA3P.js");
|
|
479
|
+
const { generateCIWorkflow } = await import("./ci-workflow-EQZFVX3P.js");
|
|
249
480
|
const personasDir = resolvePersonasDir();
|
|
250
|
-
const filePath =
|
|
481
|
+
const filePath = path3.join(personasDir, `${input.name}.yaml`);
|
|
251
482
|
if (!filePath.startsWith(personasDir)) {
|
|
252
483
|
return resultToMcpResponse(Err(new Error(`Invalid persona path: ${input.name}`)));
|
|
253
484
|
}
|
|
@@ -300,11 +531,11 @@ async function handleRunPersona(input) {
|
|
|
300
531
|
if (!/^[a-z0-9][a-z0-9._-]*$/i.test(input.persona)) {
|
|
301
532
|
return resultToMcpResponse(Err(new Error(`Invalid persona name: ${input.persona}`)));
|
|
302
533
|
}
|
|
303
|
-
const { loadPersona } = await import("./loader-
|
|
534
|
+
const { loadPersona } = await import("./loader-ZPALXIVR.js");
|
|
304
535
|
const { runPersona } = await import("./runner-VMYLHWOC.js");
|
|
305
|
-
const { executeSkill } = await import("./skill-executor-
|
|
536
|
+
const { executeSkill } = await import("./skill-executor-XZLYZYAK.js");
|
|
306
537
|
const personasDir = resolvePersonasDir();
|
|
307
|
-
const filePath =
|
|
538
|
+
const filePath = path3.join(personasDir, `${input.persona}.yaml`);
|
|
308
539
|
if (!filePath.startsWith(personasDir)) {
|
|
309
540
|
return resultToMcpResponse(Err(new Error(`Invalid persona path: ${input.persona}`)));
|
|
310
541
|
}
|
|
@@ -466,8 +697,243 @@ async function handleRunAgentTask(input) {
|
|
|
466
697
|
}
|
|
467
698
|
|
|
468
699
|
// src/mcp/tools/skill.ts
|
|
469
|
-
import * as
|
|
470
|
-
import * as
|
|
700
|
+
import * as fs5 from "fs";
|
|
701
|
+
import * as path6 from "path";
|
|
702
|
+
|
|
703
|
+
// src/skill/dispatcher.ts
|
|
704
|
+
var TIER_1_SKILLS = /* @__PURE__ */ new Set([
|
|
705
|
+
"harness-brainstorming",
|
|
706
|
+
"harness-planning",
|
|
707
|
+
"harness-execution",
|
|
708
|
+
"harness-autopilot",
|
|
709
|
+
"harness-tdd",
|
|
710
|
+
"harness-debugging",
|
|
711
|
+
"harness-refactoring"
|
|
712
|
+
]);
|
|
713
|
+
function isTier1Skill(skillName) {
|
|
714
|
+
return TIER_1_SKILLS.has(skillName);
|
|
715
|
+
}
|
|
716
|
+
function scoreSkill(entry, queryTerms, profile, recentFiles) {
|
|
717
|
+
const matchedKeywords = entry.keywords.filter(
|
|
718
|
+
(kw) => queryTerms.some(
|
|
719
|
+
(term) => kw.toLowerCase().includes(term.toLowerCase()) || term.toLowerCase().includes(kw.toLowerCase())
|
|
720
|
+
)
|
|
721
|
+
);
|
|
722
|
+
const keywordScore = queryTerms.length > 0 ? matchedKeywords.length / queryTerms.length : 0;
|
|
723
|
+
let stackScore = 0;
|
|
724
|
+
if (profile && entry.stackSignals.length > 0) {
|
|
725
|
+
const matchedSignals = entry.stackSignals.filter((signal) => {
|
|
726
|
+
if (profile.signals[signal]) return true;
|
|
727
|
+
return profile.detectedDomains.some(
|
|
728
|
+
(domain) => entry.keywords.some((kw) => kw.toLowerCase() === domain.toLowerCase())
|
|
729
|
+
);
|
|
730
|
+
});
|
|
731
|
+
stackScore = matchedSignals.length / entry.stackSignals.length;
|
|
732
|
+
}
|
|
733
|
+
let recencyBoost = 0;
|
|
734
|
+
if (recentFiles.length > 0) {
|
|
735
|
+
const hasRecentMatch = entry.stackSignals.some((signal) => {
|
|
736
|
+
const cleanSignal = signal.replace(/\*/g, "").replace(/\*\*/g, "");
|
|
737
|
+
return recentFiles.some((f) => f.includes(cleanSignal));
|
|
738
|
+
});
|
|
739
|
+
recencyBoost = hasRecentMatch ? 1 : 0;
|
|
740
|
+
}
|
|
741
|
+
return 0.5 * keywordScore + 0.3 * stackScore + 0.2 * recencyBoost;
|
|
742
|
+
}
|
|
743
|
+
function suggest(index, taskDescription, profile, recentFiles, config) {
|
|
744
|
+
const queryTerms = taskDescription.toLowerCase().split(/\s+/).filter((t) => t.length > 2);
|
|
745
|
+
const scored = [];
|
|
746
|
+
for (const [name, entry] of Object.entries(index.skills)) {
|
|
747
|
+
if (config?.neverSuggest?.includes(name)) continue;
|
|
748
|
+
const score = scoreSkill(entry, queryTerms, profile, recentFiles);
|
|
749
|
+
const isForced = config?.alwaysSuggest?.includes(name);
|
|
750
|
+
if (score >= 0.4 || isForced) {
|
|
751
|
+
scored.push({
|
|
752
|
+
name,
|
|
753
|
+
description: entry.description,
|
|
754
|
+
score: isForced ? Math.max(score, 1) : score
|
|
755
|
+
});
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
return scored.sort((a, b) => b.score - a.score).slice(0, 3);
|
|
759
|
+
}
|
|
760
|
+
function formatSuggestions(suggestions) {
|
|
761
|
+
if (suggestions.length === 0) return "";
|
|
762
|
+
const lines = suggestions.map((s) => `- **${s.name}** \u2014 ${s.description}`);
|
|
763
|
+
return [
|
|
764
|
+
"",
|
|
765
|
+
"---",
|
|
766
|
+
"## Suggested Domain Skills",
|
|
767
|
+
"Based on your task and project stack, these catalog skills may be relevant:",
|
|
768
|
+
...lines,
|
|
769
|
+
"",
|
|
770
|
+
'To load a skill: call search_skills("<skill-name>") for full details.',
|
|
771
|
+
"---"
|
|
772
|
+
].join("\n");
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
// src/skill/index-builder.ts
|
|
776
|
+
import fs3 from "fs";
|
|
777
|
+
import path4 from "path";
|
|
778
|
+
import crypto from "crypto";
|
|
779
|
+
import { parse } from "yaml";
|
|
780
|
+
function computeSkillsDirHash(skillsDirs) {
|
|
781
|
+
const hash = crypto.createHash("sha256");
|
|
782
|
+
for (const dir of skillsDirs) {
|
|
783
|
+
if (!fs3.existsSync(dir)) continue;
|
|
784
|
+
for (const entry of fs3.readdirSync(dir, { withFileTypes: true })) {
|
|
785
|
+
if (!entry.isDirectory()) continue;
|
|
786
|
+
const yamlPath = path4.join(dir, entry.name, "skill.yaml");
|
|
787
|
+
if (!fs3.existsSync(yamlPath)) continue;
|
|
788
|
+
const stat = fs3.statSync(yamlPath);
|
|
789
|
+
hash.update(`${yamlPath}:${stat.mtimeMs}`);
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
return hash.digest("hex");
|
|
793
|
+
}
|
|
794
|
+
function parseSkillEntry(yamlPath, source, tierOverrides) {
|
|
795
|
+
const raw = fs3.readFileSync(yamlPath, "utf-8");
|
|
796
|
+
const parsed = parse(raw);
|
|
797
|
+
const result = SkillMetadataSchema.safeParse(parsed);
|
|
798
|
+
if (!result.success) return null;
|
|
799
|
+
const meta = result.data;
|
|
800
|
+
const effectiveTier = tierOverrides?.[meta.name] ?? meta.tier;
|
|
801
|
+
if (meta.internal) return null;
|
|
802
|
+
if (effectiveTier !== 3 && source !== "community") return null;
|
|
803
|
+
return {
|
|
804
|
+
tier: effectiveTier ?? 3,
|
|
805
|
+
description: meta.description,
|
|
806
|
+
keywords: meta.keywords ?? [],
|
|
807
|
+
stackSignals: meta.stack_signals ?? [],
|
|
808
|
+
cognitiveMode: meta.cognitive_mode,
|
|
809
|
+
phases: (meta.phases ?? []).map((p) => p.name),
|
|
810
|
+
source
|
|
811
|
+
};
|
|
812
|
+
}
|
|
813
|
+
function scanDirectory(dir, source, index, tierOverrides) {
|
|
814
|
+
if (!fs3.existsSync(dir)) return;
|
|
815
|
+
for (const entry of fs3.readdirSync(dir, { withFileTypes: true })) {
|
|
816
|
+
if (!entry.isDirectory()) continue;
|
|
817
|
+
if (index.skills[entry.name]) continue;
|
|
818
|
+
const yamlPath = path4.join(dir, entry.name, "skill.yaml");
|
|
819
|
+
if (!fs3.existsSync(yamlPath)) continue;
|
|
820
|
+
try {
|
|
821
|
+
const parsed = parseSkillEntry(yamlPath, source, tierOverrides);
|
|
822
|
+
if (parsed) index.skills[entry.name] = parsed;
|
|
823
|
+
} catch {
|
|
824
|
+
continue;
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
function buildIndex(platform, _projectRoot, tierOverrides) {
|
|
829
|
+
const skillsDirs = resolveAllSkillsDirs(platform);
|
|
830
|
+
const sourceMap = ["project", "community", "bundled"];
|
|
831
|
+
const index = {
|
|
832
|
+
version: 1,
|
|
833
|
+
hash: computeSkillsDirHash(skillsDirs),
|
|
834
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
835
|
+
skills: {}
|
|
836
|
+
};
|
|
837
|
+
for (let i = 0; i < skillsDirs.length; i++) {
|
|
838
|
+
scanDirectory(skillsDirs[i], sourceMap[i] ?? "bundled", index, tierOverrides);
|
|
839
|
+
}
|
|
840
|
+
return index;
|
|
841
|
+
}
|
|
842
|
+
function loadOrRebuildIndex(platform, projectRoot, tierOverrides) {
|
|
843
|
+
const indexPath = path4.join(projectRoot, ".harness", "skills-index.json");
|
|
844
|
+
const skillsDirs = resolveAllSkillsDirs(platform);
|
|
845
|
+
const currentHash = computeSkillsDirHash(skillsDirs);
|
|
846
|
+
if (fs3.existsSync(indexPath)) {
|
|
847
|
+
try {
|
|
848
|
+
const existing = JSON.parse(fs3.readFileSync(indexPath, "utf-8"));
|
|
849
|
+
if (existing.hash === currentHash) return existing;
|
|
850
|
+
} catch {
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
const index = buildIndex(platform, projectRoot, tierOverrides);
|
|
854
|
+
fs3.mkdirSync(path4.dirname(indexPath), { recursive: true });
|
|
855
|
+
fs3.writeFileSync(indexPath, JSON.stringify(index, null, 2));
|
|
856
|
+
return index;
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
// src/skill/stack-profile.ts
|
|
860
|
+
import fs4 from "fs";
|
|
861
|
+
import path5 from "path";
|
|
862
|
+
var SIGNAL_DOMAIN_MAP = {
|
|
863
|
+
"prisma/schema.prisma": ["database"],
|
|
864
|
+
"drizzle.config.ts": ["database"],
|
|
865
|
+
"knexfile.js": ["database"],
|
|
866
|
+
"knexfile.ts": ["database"],
|
|
867
|
+
migrations: ["database"],
|
|
868
|
+
Dockerfile: ["containerization"],
|
|
869
|
+
"docker-compose.yml": ["containerization"],
|
|
870
|
+
"docker-compose.yaml": ["containerization"],
|
|
871
|
+
k8s: ["containerization"],
|
|
872
|
+
kubernetes: ["containerization"],
|
|
873
|
+
helm: ["containerization"],
|
|
874
|
+
".github/workflows": ["deployment"],
|
|
875
|
+
".gitlab-ci.yml": ["deployment"],
|
|
876
|
+
Jenkinsfile: ["deployment"],
|
|
877
|
+
terraform: ["infrastructure-as-code"],
|
|
878
|
+
"Pulumi.yaml": ["infrastructure-as-code"],
|
|
879
|
+
"cdk.json": ["infrastructure-as-code"],
|
|
880
|
+
"openapi.yaml": ["api-design"],
|
|
881
|
+
"openapi.json": ["api-design"],
|
|
882
|
+
"swagger.yaml": ["api-design"],
|
|
883
|
+
"swagger.json": ["api-design"],
|
|
884
|
+
"schema.graphql": ["api-design"],
|
|
885
|
+
".env": ["secrets"],
|
|
886
|
+
".env.example": ["secrets"],
|
|
887
|
+
"vault.hcl": ["secrets"],
|
|
888
|
+
e2e: ["e2e"],
|
|
889
|
+
cypress: ["e2e"],
|
|
890
|
+
"playwright.config.ts": ["e2e"],
|
|
891
|
+
"playwright.config.js": ["e2e"],
|
|
892
|
+
"stryker.conf.js": ["mutation-test"],
|
|
893
|
+
"stryker.conf.mjs": ["mutation-test"],
|
|
894
|
+
k6: ["load-testing"],
|
|
895
|
+
"artillery.yml": ["load-testing"],
|
|
896
|
+
"dbt_project.yml": ["data-pipeline"],
|
|
897
|
+
airflow: ["data-pipeline"],
|
|
898
|
+
ios: ["mobile-patterns"],
|
|
899
|
+
android: ["mobile-patterns"],
|
|
900
|
+
"pubspec.yaml": ["mobile-patterns"],
|
|
901
|
+
"App.tsx": ["mobile-patterns"],
|
|
902
|
+
runbooks: ["incident-response"],
|
|
903
|
+
"docs/runbooks": ["incident-response"]
|
|
904
|
+
};
|
|
905
|
+
function generateStackProfile(projectRoot) {
|
|
906
|
+
const signals = {};
|
|
907
|
+
const domainSet = /* @__PURE__ */ new Set();
|
|
908
|
+
for (const [pattern, domains] of Object.entries(SIGNAL_DOMAIN_MAP)) {
|
|
909
|
+
const fullPath = path5.join(projectRoot, pattern);
|
|
910
|
+
const exists = fs4.existsSync(fullPath);
|
|
911
|
+
signals[pattern] = exists;
|
|
912
|
+
if (exists) {
|
|
913
|
+
for (const domain of domains) domainSet.add(domain);
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
return {
|
|
917
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
918
|
+
signals,
|
|
919
|
+
detectedDomains: [...domainSet].sort()
|
|
920
|
+
};
|
|
921
|
+
}
|
|
922
|
+
function loadOrGenerateProfile(projectRoot) {
|
|
923
|
+
const profilePath = path5.join(projectRoot, ".harness", "stack-profile.json");
|
|
924
|
+
if (fs4.existsSync(profilePath)) {
|
|
925
|
+
try {
|
|
926
|
+
return JSON.parse(fs4.readFileSync(profilePath, "utf-8"));
|
|
927
|
+
} catch {
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
const profile = generateStackProfile(projectRoot);
|
|
931
|
+
fs4.mkdirSync(path5.dirname(profilePath), { recursive: true });
|
|
932
|
+
fs4.writeFileSync(profilePath, JSON.stringify(profile, null, 2));
|
|
933
|
+
return profile;
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
// src/mcp/tools/skill.ts
|
|
471
937
|
var runSkillDefinition = {
|
|
472
938
|
name: "run_skill",
|
|
473
939
|
description: "Load and return the content of a skill (SKILL.md), optionally with project state context",
|
|
@@ -492,23 +958,23 @@ async function handleRunSkill(input) {
|
|
|
492
958
|
if (!/^[a-z0-9][a-z0-9._-]*$/i.test(input.skill)) {
|
|
493
959
|
return resultToMcpResponse(Err(new Error(`Invalid skill name: ${input.skill}`)));
|
|
494
960
|
}
|
|
495
|
-
const skillDir =
|
|
961
|
+
const skillDir = path6.join(skillsDir, input.skill);
|
|
496
962
|
if (!skillDir.startsWith(skillsDir)) {
|
|
497
963
|
return resultToMcpResponse(Err(new Error(`Invalid skill path: ${input.skill}`)));
|
|
498
964
|
}
|
|
499
|
-
if (!
|
|
965
|
+
if (!fs5.existsSync(skillDir)) {
|
|
500
966
|
return resultToMcpResponse(Err(new Error(`Skill not found: ${input.skill}`)));
|
|
501
967
|
}
|
|
502
|
-
const skillMdPath =
|
|
503
|
-
if (!
|
|
968
|
+
const skillMdPath = path6.join(skillDir, "SKILL.md");
|
|
969
|
+
if (!fs5.existsSync(skillMdPath)) {
|
|
504
970
|
return resultToMcpResponse(Err(new Error(`SKILL.md not found for skill: ${input.skill}`)));
|
|
505
971
|
}
|
|
506
|
-
let content =
|
|
972
|
+
let content = fs5.readFileSync(skillMdPath, "utf-8");
|
|
507
973
|
if (input.path) {
|
|
508
974
|
const projectPath = sanitizePath(input.path);
|
|
509
|
-
const stateFile =
|
|
510
|
-
if (
|
|
511
|
-
const stateContent =
|
|
975
|
+
const stateFile = path6.join(projectPath, ".harness", "state.json");
|
|
976
|
+
if (fs5.existsSync(stateFile)) {
|
|
977
|
+
const stateContent = fs5.readFileSync(stateFile, "utf-8");
|
|
512
978
|
content += `
|
|
513
979
|
|
|
514
980
|
---
|
|
@@ -519,6 +985,23 @@ ${stateContent}
|
|
|
519
985
|
`;
|
|
520
986
|
}
|
|
521
987
|
}
|
|
988
|
+
if (isTier1Skill(input.skill)) {
|
|
989
|
+
try {
|
|
990
|
+
const projectRoot = input.path ? sanitizePath(input.path) : process.cwd();
|
|
991
|
+
const platform = "claude-code";
|
|
992
|
+
const configResult = resolveConfig();
|
|
993
|
+
const skillsConfig = configResult.ok ? configResult.value.skills : void 0;
|
|
994
|
+
const index = loadOrRebuildIndex(platform, projectRoot, skillsConfig?.tierOverrides);
|
|
995
|
+
const profile = loadOrGenerateProfile(projectRoot);
|
|
996
|
+
const taskDesc = [input.skill, input.phase].filter(Boolean).join(" ");
|
|
997
|
+
const suggestions = suggest(index, taskDesc, profile, [], skillsConfig);
|
|
998
|
+
const suggestionText = formatSuggestions(suggestions);
|
|
999
|
+
if (suggestionText) {
|
|
1000
|
+
content += suggestionText;
|
|
1001
|
+
}
|
|
1002
|
+
} catch {
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
522
1005
|
return resultToMcpResponse(Ok(content));
|
|
523
1006
|
}
|
|
524
1007
|
var createSkillDefinition = {
|
|
@@ -548,7 +1031,7 @@ var createSkillDefinition = {
|
|
|
548
1031
|
};
|
|
549
1032
|
async function handleCreateSkill(input) {
|
|
550
1033
|
try {
|
|
551
|
-
const { generateSkillFiles } = await import("./create-skill-
|
|
1034
|
+
const { generateSkillFiles } = await import("./create-skill-XSWHMSM5.js");
|
|
552
1035
|
const result = generateSkillFiles({
|
|
553
1036
|
name: input.name,
|
|
554
1037
|
description: input.description,
|
|
@@ -570,22 +1053,22 @@ async function handleCreateSkill(input) {
|
|
|
570
1053
|
}
|
|
571
1054
|
|
|
572
1055
|
// src/mcp/resources/skills.ts
|
|
573
|
-
import * as
|
|
574
|
-
import * as
|
|
1056
|
+
import * as fs6 from "fs";
|
|
1057
|
+
import * as path7 from "path";
|
|
575
1058
|
import * as yaml from "yaml";
|
|
576
1059
|
async function getSkillsResource(projectRoot) {
|
|
577
|
-
const skillsDir =
|
|
1060
|
+
const skillsDir = path7.join(projectRoot, "agents", "skills", "claude-code");
|
|
578
1061
|
const skills = [];
|
|
579
|
-
if (!
|
|
1062
|
+
if (!fs6.existsSync(skillsDir)) {
|
|
580
1063
|
return JSON.stringify(skills, null, 2);
|
|
581
1064
|
}
|
|
582
|
-
const entries =
|
|
1065
|
+
const entries = fs6.readdirSync(skillsDir, { withFileTypes: true });
|
|
583
1066
|
for (const entry of entries) {
|
|
584
1067
|
if (!entry.isDirectory()) continue;
|
|
585
|
-
const skillYamlPath =
|
|
586
|
-
if (!
|
|
1068
|
+
const skillYamlPath = path7.join(skillsDir, entry.name, "skill.yaml");
|
|
1069
|
+
if (!fs6.existsSync(skillYamlPath)) continue;
|
|
587
1070
|
try {
|
|
588
|
-
const content =
|
|
1071
|
+
const content = fs6.readFileSync(skillYamlPath, "utf-8");
|
|
589
1072
|
const parsed = yaml.parse(content);
|
|
590
1073
|
skills.push({
|
|
591
1074
|
name: parsed.name,
|
|
@@ -601,14 +1084,14 @@ async function getSkillsResource(projectRoot) {
|
|
|
601
1084
|
}
|
|
602
1085
|
|
|
603
1086
|
// src/mcp/resources/rules.ts
|
|
604
|
-
import * as
|
|
605
|
-
import * as
|
|
1087
|
+
import * as fs7 from "fs";
|
|
1088
|
+
import * as path8 from "path";
|
|
606
1089
|
async function getRulesResource(projectRoot) {
|
|
607
1090
|
const rules = [];
|
|
608
|
-
const configPath =
|
|
609
|
-
if (
|
|
1091
|
+
const configPath = path8.join(projectRoot, "harness.config.json");
|
|
1092
|
+
if (fs7.existsSync(configPath)) {
|
|
610
1093
|
try {
|
|
611
|
-
const config = JSON.parse(
|
|
1094
|
+
const config = JSON.parse(fs7.readFileSync(configPath, "utf-8"));
|
|
612
1095
|
if (config.layers) {
|
|
613
1096
|
rules.push({ type: "layer-enforcement", config: config.layers });
|
|
614
1097
|
}
|
|
@@ -621,10 +1104,10 @@ async function getRulesResource(projectRoot) {
|
|
|
621
1104
|
} catch {
|
|
622
1105
|
}
|
|
623
1106
|
}
|
|
624
|
-
const linterPath =
|
|
625
|
-
if (
|
|
1107
|
+
const linterPath = path8.join(projectRoot, ".harness", "linter.json");
|
|
1108
|
+
if (fs7.existsSync(linterPath)) {
|
|
626
1109
|
try {
|
|
627
|
-
const linterConfig = JSON.parse(
|
|
1110
|
+
const linterConfig = JSON.parse(fs7.readFileSync(linterPath, "utf-8"));
|
|
628
1111
|
rules.push({ type: "linter", config: linterConfig });
|
|
629
1112
|
} catch {
|
|
630
1113
|
}
|
|
@@ -633,32 +1116,37 @@ async function getRulesResource(projectRoot) {
|
|
|
633
1116
|
}
|
|
634
1117
|
|
|
635
1118
|
// src/mcp/resources/project.ts
|
|
636
|
-
import * as
|
|
637
|
-
import * as
|
|
1119
|
+
import * as fs8 from "fs";
|
|
1120
|
+
import * as path9 from "path";
|
|
638
1121
|
async function getProjectResource(projectRoot) {
|
|
639
|
-
const agentsPath =
|
|
640
|
-
if (
|
|
641
|
-
return
|
|
1122
|
+
const agentsPath = path9.join(projectRoot, "AGENTS.md");
|
|
1123
|
+
if (fs8.existsSync(agentsPath)) {
|
|
1124
|
+
return fs8.readFileSync(agentsPath, "utf-8");
|
|
642
1125
|
}
|
|
643
1126
|
return "# No AGENTS.md found";
|
|
644
1127
|
}
|
|
645
1128
|
|
|
646
1129
|
// src/mcp/resources/learnings.ts
|
|
647
|
-
import * as
|
|
648
|
-
import * as
|
|
1130
|
+
import * as fs9 from "fs";
|
|
1131
|
+
import * as path10 from "path";
|
|
649
1132
|
async function getLearningsResource(projectRoot) {
|
|
650
1133
|
const sections = [];
|
|
651
|
-
const reviewPath =
|
|
652
|
-
if (
|
|
653
|
-
sections.push("## Review Learnings\n\n" +
|
|
1134
|
+
const reviewPath = path10.join(projectRoot, ".harness", "review-learnings.md");
|
|
1135
|
+
if (fs9.existsSync(reviewPath)) {
|
|
1136
|
+
sections.push("## Review Learnings\n\n" + fs9.readFileSync(reviewPath, "utf-8"));
|
|
654
1137
|
}
|
|
655
|
-
const antiPath =
|
|
656
|
-
if (
|
|
657
|
-
sections.push("## Anti-Pattern Log\n\n" +
|
|
1138
|
+
const antiPath = path10.join(projectRoot, ".harness", "anti-patterns.md");
|
|
1139
|
+
if (fs9.existsSync(antiPath)) {
|
|
1140
|
+
sections.push("## Anti-Pattern Log\n\n" + fs9.readFileSync(antiPath, "utf-8"));
|
|
658
1141
|
}
|
|
659
1142
|
return sections.length > 0 ? sections.join("\n\n---\n\n") : "No learnings files found.";
|
|
660
1143
|
}
|
|
661
1144
|
|
|
1145
|
+
// src/mcp/utils.ts
|
|
1146
|
+
function mcpError(text) {
|
|
1147
|
+
return { content: [{ type: "text", text }], isError: true };
|
|
1148
|
+
}
|
|
1149
|
+
|
|
662
1150
|
// src/mcp/tools/state.ts
|
|
663
1151
|
var manageStateDefinition = {
|
|
664
1152
|
name: "manage_state",
|
|
@@ -677,7 +1165,12 @@ var manageStateDefinition = {
|
|
|
677
1165
|
"reset",
|
|
678
1166
|
"gate",
|
|
679
1167
|
"save-handoff",
|
|
680
|
-
"load-handoff"
|
|
1168
|
+
"load-handoff",
|
|
1169
|
+
"append_entry",
|
|
1170
|
+
"update_entry_status",
|
|
1171
|
+
"read_section",
|
|
1172
|
+
"read_sections",
|
|
1173
|
+
"archive_session"
|
|
681
1174
|
],
|
|
682
1175
|
description: "Action to perform"
|
|
683
1176
|
},
|
|
@@ -694,138 +1187,175 @@ var manageStateDefinition = {
|
|
|
694
1187
|
session: {
|
|
695
1188
|
type: "string",
|
|
696
1189
|
description: "Session slug for session-scoped state (takes priority over stream when provided)"
|
|
1190
|
+
},
|
|
1191
|
+
section: {
|
|
1192
|
+
type: "string",
|
|
1193
|
+
enum: ["terminology", "decisions", "constraints", "risks", "openQuestions", "evidence"],
|
|
1194
|
+
description: "Session section name (terminology, decisions, constraints, risks, openQuestions, evidence)"
|
|
1195
|
+
},
|
|
1196
|
+
authorSkill: {
|
|
1197
|
+
type: "string",
|
|
1198
|
+
description: "Name of the skill authoring the entry (required for append_entry)"
|
|
1199
|
+
},
|
|
1200
|
+
content: {
|
|
1201
|
+
type: "string",
|
|
1202
|
+
description: "Entry content text (required for append_entry)"
|
|
1203
|
+
},
|
|
1204
|
+
entryId: {
|
|
1205
|
+
type: "string",
|
|
1206
|
+
description: "ID of the entry to update (required for update_entry_status)"
|
|
1207
|
+
},
|
|
1208
|
+
newStatus: {
|
|
1209
|
+
type: "string",
|
|
1210
|
+
enum: ["active", "resolved", "superseded"],
|
|
1211
|
+
description: "New status for the entry: active, resolved, or superseded (required for update_entry_status)"
|
|
697
1212
|
}
|
|
698
1213
|
},
|
|
699
1214
|
required: ["path", "action"]
|
|
700
1215
|
}
|
|
701
1216
|
};
|
|
1217
|
+
async function handleShow(projectPath, input) {
|
|
1218
|
+
const { loadState } = await import("./dist-HWXF2C3R.js");
|
|
1219
|
+
return resultToMcpResponse(await loadState(projectPath, input.stream, input.session));
|
|
1220
|
+
}
|
|
1221
|
+
async function handleLearn(projectPath, input) {
|
|
1222
|
+
if (!input.learning) return mcpError("Error: learning is required for learn action");
|
|
1223
|
+
const { appendLearning } = await import("./dist-HWXF2C3R.js");
|
|
1224
|
+
const result = await appendLearning(
|
|
1225
|
+
projectPath,
|
|
1226
|
+
input.learning,
|
|
1227
|
+
input.skillName,
|
|
1228
|
+
input.outcome,
|
|
1229
|
+
input.stream,
|
|
1230
|
+
input.session
|
|
1231
|
+
);
|
|
1232
|
+
if (!result.ok) return resultToMcpResponse(result);
|
|
1233
|
+
return resultToMcpResponse(Ok({ recorded: true }));
|
|
1234
|
+
}
|
|
1235
|
+
async function handleFailure(projectPath, input) {
|
|
1236
|
+
if (!input.description) return mcpError("Error: description is required for failure action");
|
|
1237
|
+
if (!input.failureType) return mcpError("Error: failureType is required for failure action");
|
|
1238
|
+
const { appendFailure } = await import("./dist-HWXF2C3R.js");
|
|
1239
|
+
const result = await appendFailure(
|
|
1240
|
+
projectPath,
|
|
1241
|
+
input.description,
|
|
1242
|
+
input.skillName ?? "unknown",
|
|
1243
|
+
input.failureType,
|
|
1244
|
+
input.stream,
|
|
1245
|
+
input.session
|
|
1246
|
+
);
|
|
1247
|
+
if (!result.ok) return resultToMcpResponse(result);
|
|
1248
|
+
return resultToMcpResponse(Ok({ recorded: true }));
|
|
1249
|
+
}
|
|
1250
|
+
async function handleArchive(projectPath, input) {
|
|
1251
|
+
const { archiveFailures } = await import("./dist-HWXF2C3R.js");
|
|
1252
|
+
const result = await archiveFailures(projectPath, input.stream, input.session);
|
|
1253
|
+
if (!result.ok) return resultToMcpResponse(result);
|
|
1254
|
+
return resultToMcpResponse(Ok({ archived: true }));
|
|
1255
|
+
}
|
|
1256
|
+
async function handleReset(projectPath, input) {
|
|
1257
|
+
const { saveState, DEFAULT_STATE } = await import("./dist-HWXF2C3R.js");
|
|
1258
|
+
const result = await saveState(projectPath, { ...DEFAULT_STATE }, input.stream, input.session);
|
|
1259
|
+
if (!result.ok) return resultToMcpResponse(result);
|
|
1260
|
+
return resultToMcpResponse(Ok({ reset: true }));
|
|
1261
|
+
}
|
|
1262
|
+
async function handleGate(projectPath, _input) {
|
|
1263
|
+
const { runMechanicalGate } = await import("./dist-HWXF2C3R.js");
|
|
1264
|
+
return resultToMcpResponse(await runMechanicalGate(projectPath));
|
|
1265
|
+
}
|
|
1266
|
+
async function handleSaveHandoff(projectPath, input) {
|
|
1267
|
+
if (!input.handoff) return mcpError("Error: handoff is required for save-handoff action");
|
|
1268
|
+
const { saveHandoff } = await import("./dist-HWXF2C3R.js");
|
|
1269
|
+
const result = await saveHandoff(
|
|
1270
|
+
projectPath,
|
|
1271
|
+
input.handoff,
|
|
1272
|
+
input.stream,
|
|
1273
|
+
input.session
|
|
1274
|
+
);
|
|
1275
|
+
return resultToMcpResponse(result.ok ? Ok({ saved: true }) : result);
|
|
1276
|
+
}
|
|
1277
|
+
async function handleLoadHandoff(projectPath, input) {
|
|
1278
|
+
const { loadHandoff } = await import("./dist-HWXF2C3R.js");
|
|
1279
|
+
return resultToMcpResponse(await loadHandoff(projectPath, input.stream, input.session));
|
|
1280
|
+
}
|
|
1281
|
+
async function handleAppendEntry(projectPath, input) {
|
|
1282
|
+
if (!input.session) return mcpError("Error: session is required for append_entry action");
|
|
1283
|
+
if (!input.section) return mcpError("Error: section is required for append_entry action");
|
|
1284
|
+
if (!input.authorSkill) return mcpError("Error: authorSkill is required for append_entry action");
|
|
1285
|
+
if (!input.content) return mcpError("Error: content is required for append_entry action");
|
|
1286
|
+
const { appendSessionEntry } = await import("./dist-HWXF2C3R.js");
|
|
1287
|
+
const result = await appendSessionEntry(
|
|
1288
|
+
projectPath,
|
|
1289
|
+
input.session,
|
|
1290
|
+
input.section,
|
|
1291
|
+
input.authorSkill,
|
|
1292
|
+
input.content
|
|
1293
|
+
);
|
|
1294
|
+
return resultToMcpResponse(result);
|
|
1295
|
+
}
|
|
1296
|
+
async function handleUpdateEntryStatus(projectPath, input) {
|
|
1297
|
+
if (!input.session) return mcpError("Error: session is required for update_entry_status action");
|
|
1298
|
+
if (!input.section) return mcpError("Error: section is required for update_entry_status action");
|
|
1299
|
+
if (!input.entryId) return mcpError("Error: entryId is required for update_entry_status action");
|
|
1300
|
+
if (!input.newStatus)
|
|
1301
|
+
return mcpError("Error: newStatus is required for update_entry_status action");
|
|
1302
|
+
const { updateSessionEntryStatus } = await import("./dist-HWXF2C3R.js");
|
|
1303
|
+
const result = await updateSessionEntryStatus(
|
|
1304
|
+
projectPath,
|
|
1305
|
+
input.session,
|
|
1306
|
+
input.section,
|
|
1307
|
+
input.entryId,
|
|
1308
|
+
input.newStatus
|
|
1309
|
+
);
|
|
1310
|
+
return resultToMcpResponse(result);
|
|
1311
|
+
}
|
|
1312
|
+
async function handleReadSection(projectPath, input) {
|
|
1313
|
+
if (!input.session) return mcpError("Error: session is required for read_section action");
|
|
1314
|
+
if (!input.section) return mcpError("Error: section is required for read_section action");
|
|
1315
|
+
const { readSessionSection } = await import("./dist-HWXF2C3R.js");
|
|
1316
|
+
const result = await readSessionSection(
|
|
1317
|
+
projectPath,
|
|
1318
|
+
input.session,
|
|
1319
|
+
input.section
|
|
1320
|
+
);
|
|
1321
|
+
return resultToMcpResponse(result);
|
|
1322
|
+
}
|
|
1323
|
+
async function handleReadSections(projectPath, input) {
|
|
1324
|
+
if (!input.session) return mcpError("Error: session is required for read_sections action");
|
|
1325
|
+
const { readSessionSections } = await import("./dist-HWXF2C3R.js");
|
|
1326
|
+
const result = await readSessionSections(projectPath, input.session);
|
|
1327
|
+
return resultToMcpResponse(result);
|
|
1328
|
+
}
|
|
1329
|
+
async function handleArchiveSession(projectPath, input) {
|
|
1330
|
+
if (!input.session) return mcpError("Error: session is required for archive_session action");
|
|
1331
|
+
const { archiveSession } = await import("./dist-HWXF2C3R.js");
|
|
1332
|
+
const result = await archiveSession(projectPath, input.session);
|
|
1333
|
+
if (!result.ok) return resultToMcpResponse(result);
|
|
1334
|
+
return resultToMcpResponse(Ok({ archived: true }));
|
|
1335
|
+
}
|
|
1336
|
+
var ACTION_HANDLERS = {
|
|
1337
|
+
show: handleShow,
|
|
1338
|
+
learn: handleLearn,
|
|
1339
|
+
failure: handleFailure,
|
|
1340
|
+
archive: handleArchive,
|
|
1341
|
+
reset: handleReset,
|
|
1342
|
+
gate: handleGate,
|
|
1343
|
+
"save-handoff": handleSaveHandoff,
|
|
1344
|
+
"load-handoff": handleLoadHandoff,
|
|
1345
|
+
append_entry: handleAppendEntry,
|
|
1346
|
+
update_entry_status: handleUpdateEntryStatus,
|
|
1347
|
+
read_section: handleReadSection,
|
|
1348
|
+
read_sections: handleReadSections,
|
|
1349
|
+
archive_session: handleArchiveSession
|
|
1350
|
+
};
|
|
702
1351
|
async function handleManageState(input) {
|
|
703
1352
|
try {
|
|
704
|
-
const {
|
|
705
|
-
loadState,
|
|
706
|
-
saveState,
|
|
707
|
-
appendLearning,
|
|
708
|
-
appendFailure,
|
|
709
|
-
archiveFailures,
|
|
710
|
-
runMechanicalGate,
|
|
711
|
-
DEFAULT_STATE
|
|
712
|
-
} = await import("./dist-WF4C7A4A.js");
|
|
713
1353
|
const projectPath = sanitizePath(input.path);
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
return resultToMcpResponse(result);
|
|
718
|
-
}
|
|
719
|
-
case "learn": {
|
|
720
|
-
if (!input.learning) {
|
|
721
|
-
return {
|
|
722
|
-
content: [
|
|
723
|
-
{ type: "text", text: "Error: learning is required for learn action" }
|
|
724
|
-
],
|
|
725
|
-
isError: true
|
|
726
|
-
};
|
|
727
|
-
}
|
|
728
|
-
const result = await appendLearning(
|
|
729
|
-
projectPath,
|
|
730
|
-
input.learning,
|
|
731
|
-
input.skillName,
|
|
732
|
-
input.outcome,
|
|
733
|
-
input.stream,
|
|
734
|
-
input.session
|
|
735
|
-
);
|
|
736
|
-
if (!result.ok) return resultToMcpResponse(result);
|
|
737
|
-
return resultToMcpResponse(Ok({ recorded: true }));
|
|
738
|
-
}
|
|
739
|
-
case "failure": {
|
|
740
|
-
if (!input.description) {
|
|
741
|
-
return {
|
|
742
|
-
content: [
|
|
743
|
-
{ type: "text", text: "Error: description is required for failure action" }
|
|
744
|
-
],
|
|
745
|
-
isError: true
|
|
746
|
-
};
|
|
747
|
-
}
|
|
748
|
-
if (!input.failureType) {
|
|
749
|
-
return {
|
|
750
|
-
content: [
|
|
751
|
-
{
|
|
752
|
-
type: "text",
|
|
753
|
-
text: "Error: failureType is required for failure action"
|
|
754
|
-
}
|
|
755
|
-
],
|
|
756
|
-
isError: true
|
|
757
|
-
};
|
|
758
|
-
}
|
|
759
|
-
const result = await appendFailure(
|
|
760
|
-
projectPath,
|
|
761
|
-
input.description,
|
|
762
|
-
input.skillName ?? "unknown",
|
|
763
|
-
input.failureType,
|
|
764
|
-
input.stream,
|
|
765
|
-
input.session
|
|
766
|
-
);
|
|
767
|
-
if (!result.ok) return resultToMcpResponse(result);
|
|
768
|
-
return resultToMcpResponse(Ok({ recorded: true }));
|
|
769
|
-
}
|
|
770
|
-
case "archive": {
|
|
771
|
-
const result = await archiveFailures(projectPath, input.stream, input.session);
|
|
772
|
-
if (!result.ok) return resultToMcpResponse(result);
|
|
773
|
-
return resultToMcpResponse(Ok({ archived: true }));
|
|
774
|
-
}
|
|
775
|
-
case "reset": {
|
|
776
|
-
const result = await saveState(
|
|
777
|
-
projectPath,
|
|
778
|
-
{ ...DEFAULT_STATE },
|
|
779
|
-
input.stream,
|
|
780
|
-
input.session
|
|
781
|
-
);
|
|
782
|
-
if (!result.ok) return resultToMcpResponse(result);
|
|
783
|
-
return resultToMcpResponse(Ok({ reset: true }));
|
|
784
|
-
}
|
|
785
|
-
case "gate": {
|
|
786
|
-
const result = await runMechanicalGate(projectPath);
|
|
787
|
-
return resultToMcpResponse(result);
|
|
788
|
-
}
|
|
789
|
-
case "save-handoff": {
|
|
790
|
-
if (!input.handoff) {
|
|
791
|
-
return {
|
|
792
|
-
content: [
|
|
793
|
-
{ type: "text", text: "Error: handoff is required for save-handoff action" }
|
|
794
|
-
],
|
|
795
|
-
isError: true
|
|
796
|
-
};
|
|
797
|
-
}
|
|
798
|
-
const { saveHandoff } = await import("./dist-WF4C7A4A.js");
|
|
799
|
-
const result = await saveHandoff(
|
|
800
|
-
projectPath,
|
|
801
|
-
input.handoff,
|
|
802
|
-
input.stream,
|
|
803
|
-
input.session
|
|
804
|
-
);
|
|
805
|
-
return resultToMcpResponse(result.ok ? Ok({ saved: true }) : result);
|
|
806
|
-
}
|
|
807
|
-
case "load-handoff": {
|
|
808
|
-
const { loadHandoff } = await import("./dist-WF4C7A4A.js");
|
|
809
|
-
const result = await loadHandoff(projectPath, input.stream, input.session);
|
|
810
|
-
return resultToMcpResponse(result);
|
|
811
|
-
}
|
|
812
|
-
default: {
|
|
813
|
-
return {
|
|
814
|
-
content: [{ type: "text", text: `Error: unknown action` }],
|
|
815
|
-
isError: true
|
|
816
|
-
};
|
|
817
|
-
}
|
|
818
|
-
}
|
|
1354
|
+
const handler = ACTION_HANDLERS[input.action];
|
|
1355
|
+
if (!handler) return mcpError("Error: unknown action");
|
|
1356
|
+
return await handler(projectPath, input);
|
|
819
1357
|
} catch (error) {
|
|
820
|
-
return {
|
|
821
|
-
content: [
|
|
822
|
-
{
|
|
823
|
-
type: "text",
|
|
824
|
-
text: `Error: ${error instanceof Error ? error.message : String(error)}`
|
|
825
|
-
}
|
|
826
|
-
],
|
|
827
|
-
isError: true
|
|
828
|
-
};
|
|
1358
|
+
return mcpError(`Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
829
1359
|
}
|
|
830
1360
|
}
|
|
831
1361
|
var listStreamsDefinition = {
|
|
@@ -841,7 +1371,7 @@ var listStreamsDefinition = {
|
|
|
841
1371
|
};
|
|
842
1372
|
async function handleListStreams(input) {
|
|
843
1373
|
try {
|
|
844
|
-
const { listStreams, loadStreamIndex } = await import("./dist-
|
|
1374
|
+
const { listStreams, loadStreamIndex } = await import("./dist-HWXF2C3R.js");
|
|
845
1375
|
const projectPath = sanitizePath(input.path);
|
|
846
1376
|
const indexResult = await loadStreamIndex(projectPath);
|
|
847
1377
|
const streamsResult = await listStreams(projectPath);
|
|
@@ -879,7 +1409,7 @@ var checkPhaseGateDefinition = {
|
|
|
879
1409
|
};
|
|
880
1410
|
async function handleCheckPhaseGate(input) {
|
|
881
1411
|
try {
|
|
882
|
-
const { runCheckPhaseGate } = await import("./check-phase-gate-
|
|
1412
|
+
const { runCheckPhaseGate } = await import("./check-phase-gate-L3RADYWO.js");
|
|
883
1413
|
const result = await runCheckPhaseGate({ cwd: sanitizePath(input.path) });
|
|
884
1414
|
if (result.ok) {
|
|
885
1415
|
return { content: [{ type: "text", text: JSON.stringify(result.value) }] };
|
|
@@ -899,7 +1429,7 @@ async function handleCheckPhaseGate(input) {
|
|
|
899
1429
|
}
|
|
900
1430
|
|
|
901
1431
|
// src/mcp/tools/cross-check.ts
|
|
902
|
-
import * as
|
|
1432
|
+
import * as path11 from "path";
|
|
903
1433
|
var validateCrossCheckDefinition = {
|
|
904
1434
|
name: "validate_cross_check",
|
|
905
1435
|
description: "Validate plan-to-implementation coverage: checks that specs have plans and plans have implementations, detects staleness",
|
|
@@ -935,15 +1465,15 @@ async function handleValidateCrossCheck(input) {
|
|
|
935
1465
|
};
|
|
936
1466
|
}
|
|
937
1467
|
try {
|
|
938
|
-
const { runCrossCheck } = await import("./validate-cross-check-
|
|
939
|
-
const specsDir =
|
|
1468
|
+
const { runCrossCheck } = await import("./validate-cross-check-WNJM6H2D.js");
|
|
1469
|
+
const specsDir = path11.resolve(projectPath, input.specsDir ?? "docs/specs");
|
|
940
1470
|
if (!specsDir.startsWith(projectPath)) {
|
|
941
1471
|
return {
|
|
942
1472
|
content: [{ type: "text", text: "Error: specsDir escapes project root" }],
|
|
943
1473
|
isError: true
|
|
944
1474
|
};
|
|
945
1475
|
}
|
|
946
|
-
const plansDir =
|
|
1476
|
+
const plansDir = path11.resolve(projectPath, input.plansDir ?? "docs/plans");
|
|
947
1477
|
if (!plansDir.startsWith(projectPath)) {
|
|
948
1478
|
return {
|
|
949
1479
|
content: [{ type: "text", text: "Error: plansDir escapes project root" }],
|
|
@@ -974,15 +1504,15 @@ async function handleValidateCrossCheck(input) {
|
|
|
974
1504
|
|
|
975
1505
|
// src/commands/generate-slash-commands.ts
|
|
976
1506
|
import { Command } from "commander";
|
|
977
|
-
import
|
|
978
|
-
import
|
|
1507
|
+
import fs11 from "fs";
|
|
1508
|
+
import path13 from "path";
|
|
979
1509
|
import os from "os";
|
|
980
1510
|
import readline from "readline";
|
|
981
1511
|
|
|
982
1512
|
// src/slash-commands/normalize.ts
|
|
983
|
-
import
|
|
984
|
-
import
|
|
985
|
-
import { parse as
|
|
1513
|
+
import fs10 from "fs";
|
|
1514
|
+
import path12 from "path";
|
|
1515
|
+
import { parse as parse3 } from "yaml";
|
|
986
1516
|
|
|
987
1517
|
// src/slash-commands/normalize-name.ts
|
|
988
1518
|
function normalizeName(skillName) {
|
|
@@ -998,116 +1528,125 @@ function normalizeName(skillName) {
|
|
|
998
1528
|
}
|
|
999
1529
|
|
|
1000
1530
|
// src/slash-commands/normalize.ts
|
|
1531
|
+
function readSkillYaml(yamlPath) {
|
|
1532
|
+
let raw;
|
|
1533
|
+
try {
|
|
1534
|
+
raw = fs10.readFileSync(yamlPath, "utf-8");
|
|
1535
|
+
} catch {
|
|
1536
|
+
return null;
|
|
1537
|
+
}
|
|
1538
|
+
return SkillMetadataSchema.safeParse(parse3(raw));
|
|
1539
|
+
}
|
|
1540
|
+
function shouldSkipSkill(meta, platforms) {
|
|
1541
|
+
const matchesPlatform = platforms.some((p) => meta.platforms.includes(p));
|
|
1542
|
+
if (!matchesPlatform) return true;
|
|
1543
|
+
if (meta.tier === 3 || meta.internal) return true;
|
|
1544
|
+
return false;
|
|
1545
|
+
}
|
|
1546
|
+
function checkNameCollision(normalized, metaName, source, nameMap) {
|
|
1547
|
+
const existing = nameMap.get(normalized);
|
|
1548
|
+
if (!existing) {
|
|
1549
|
+
nameMap.set(normalized, { skillName: metaName, source });
|
|
1550
|
+
return "ok";
|
|
1551
|
+
}
|
|
1552
|
+
if (existing.source === source) {
|
|
1553
|
+
throw new Error(
|
|
1554
|
+
`Name collision: skills "${existing.skillName}" and "${metaName}" both normalize to "${normalized}"`
|
|
1555
|
+
);
|
|
1556
|
+
}
|
|
1557
|
+
return "skip";
|
|
1558
|
+
}
|
|
1559
|
+
function buildContextLines(meta) {
|
|
1560
|
+
const lines = [];
|
|
1561
|
+
if (meta.cognitive_mode) lines.push(`Cognitive mode: ${meta.cognitive_mode}`);
|
|
1562
|
+
if (meta.type) lines.push(`Type: ${meta.type}`);
|
|
1563
|
+
if (meta.state?.persistent) {
|
|
1564
|
+
const files = meta.state.files?.join(", ") ?? "";
|
|
1565
|
+
lines.push(`State: persistent${files ? ` (files: ${files})` : ""}`);
|
|
1566
|
+
}
|
|
1567
|
+
return lines;
|
|
1568
|
+
}
|
|
1569
|
+
function buildObjectiveLines(meta) {
|
|
1570
|
+
const lines = [meta.description];
|
|
1571
|
+
if (meta.phases && meta.phases.length > 0) {
|
|
1572
|
+
lines.push("", "Phases:");
|
|
1573
|
+
for (const phase of meta.phases) {
|
|
1574
|
+
const req = phase.required !== false ? "" : " (optional)";
|
|
1575
|
+
lines.push(`- ${phase.name}: ${phase.description}${req}`);
|
|
1576
|
+
}
|
|
1577
|
+
}
|
|
1578
|
+
return lines;
|
|
1579
|
+
}
|
|
1580
|
+
function buildProcessLines(meta) {
|
|
1581
|
+
if (meta.mcp?.tool) {
|
|
1582
|
+
return [
|
|
1583
|
+
`1. Try: invoke mcp__harness__${meta.mcp.tool} with skill: "${meta.name}"`,
|
|
1584
|
+
`2. If MCP unavailable: read SKILL.md and follow its workflow directly`,
|
|
1585
|
+
`3. Pass through any arguments provided by the user`
|
|
1586
|
+
];
|
|
1587
|
+
}
|
|
1588
|
+
return [
|
|
1589
|
+
`1. Read SKILL.md and follow its workflow directly`,
|
|
1590
|
+
`2. Pass through any arguments provided by the user`
|
|
1591
|
+
];
|
|
1592
|
+
}
|
|
1593
|
+
function buildSpec(meta, normalized, entry, skillsDir, source) {
|
|
1594
|
+
const skillMdPath = path12.join(skillsDir, entry.name, "SKILL.md");
|
|
1595
|
+
const skillMdContent = fs10.existsSync(skillMdPath) ? fs10.readFileSync(skillMdPath, "utf-8") : "";
|
|
1596
|
+
const skillMdRelative = path12.relative(process.cwd(), skillMdPath).replaceAll("\\", "/");
|
|
1597
|
+
const skillYamlRelative = path12.relative(process.cwd(), path12.join(skillsDir, entry.name, "skill.yaml")).replaceAll("\\", "/");
|
|
1598
|
+
const args = (meta.cli?.args ?? []).map((a) => ({
|
|
1599
|
+
name: a.name,
|
|
1600
|
+
description: a.description ?? "",
|
|
1601
|
+
required: a.required ?? false
|
|
1602
|
+
}));
|
|
1603
|
+
const tools = [...meta.tools ?? []];
|
|
1604
|
+
if (!tools.includes("Read")) tools.push("Read");
|
|
1605
|
+
const executionContextLines = [];
|
|
1606
|
+
if (skillMdContent) {
|
|
1607
|
+
executionContextLines.push(`@${skillMdRelative}`, `@${skillYamlRelative}`);
|
|
1608
|
+
}
|
|
1609
|
+
return {
|
|
1610
|
+
name: normalized,
|
|
1611
|
+
namespace: "harness",
|
|
1612
|
+
fullName: `harness:${normalized}`,
|
|
1613
|
+
description: meta.description,
|
|
1614
|
+
version: meta.version,
|
|
1615
|
+
...meta.cognitive_mode ? { cognitiveMode: meta.cognitive_mode } : {},
|
|
1616
|
+
tools,
|
|
1617
|
+
args,
|
|
1618
|
+
skillYamlName: meta.name,
|
|
1619
|
+
sourceDir: entry.name,
|
|
1620
|
+
skillsBaseDir: skillsDir,
|
|
1621
|
+
source,
|
|
1622
|
+
prompt: {
|
|
1623
|
+
context: buildContextLines(meta).join("\n"),
|
|
1624
|
+
objective: buildObjectiveLines(meta).join("\n"),
|
|
1625
|
+
executionContext: executionContextLines.join("\n"),
|
|
1626
|
+
process: buildProcessLines(meta).join("\n")
|
|
1627
|
+
}
|
|
1628
|
+
};
|
|
1629
|
+
}
|
|
1001
1630
|
function normalizeSkills(skillSources, platforms) {
|
|
1002
1631
|
const specs = [];
|
|
1003
1632
|
const nameMap = /* @__PURE__ */ new Map();
|
|
1004
1633
|
for (const { dir: skillsDir, source } of skillSources) {
|
|
1005
|
-
if (!
|
|
1006
|
-
const entries =
|
|
1634
|
+
if (!fs10.existsSync(skillsDir)) continue;
|
|
1635
|
+
const entries = fs10.readdirSync(skillsDir, { withFileTypes: true }).filter((d) => d.isDirectory());
|
|
1007
1636
|
for (const entry of entries) {
|
|
1008
|
-
const yamlPath =
|
|
1009
|
-
if (!
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
raw = fs6.readFileSync(yamlPath, "utf-8");
|
|
1013
|
-
} catch {
|
|
1014
|
-
continue;
|
|
1015
|
-
}
|
|
1016
|
-
const parsed = parse2(raw);
|
|
1017
|
-
const result = SkillMetadataSchema.safeParse(parsed);
|
|
1637
|
+
const yamlPath = path12.join(skillsDir, entry.name, "skill.yaml");
|
|
1638
|
+
if (!fs10.existsSync(yamlPath)) continue;
|
|
1639
|
+
const result = readSkillYaml(yamlPath);
|
|
1640
|
+
if (!result) continue;
|
|
1018
1641
|
if (!result.success) {
|
|
1019
1642
|
console.warn(`Skipping ${entry.name}: invalid skill.yaml`);
|
|
1020
1643
|
continue;
|
|
1021
1644
|
}
|
|
1022
1645
|
const meta = result.data;
|
|
1023
|
-
|
|
1024
|
-
if (!matchesPlatform) continue;
|
|
1646
|
+
if (shouldSkipSkill(meta, platforms)) continue;
|
|
1025
1647
|
const normalized = normalizeName(meta.name);
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
if (existing.source === source) {
|
|
1029
|
-
throw new Error(
|
|
1030
|
-
`Name collision: skills "${existing.skillName}" and "${meta.name}" both normalize to "${normalized}"`
|
|
1031
|
-
);
|
|
1032
|
-
}
|
|
1033
|
-
continue;
|
|
1034
|
-
}
|
|
1035
|
-
nameMap.set(normalized, { skillName: meta.name, source });
|
|
1036
|
-
const skillMdPath = path9.join(skillsDir, entry.name, "SKILL.md");
|
|
1037
|
-
const skillMdContent = fs6.existsSync(skillMdPath) ? fs6.readFileSync(skillMdPath, "utf-8") : "";
|
|
1038
|
-
const skillMdRelative = path9.relative(
|
|
1039
|
-
process.cwd(),
|
|
1040
|
-
path9.join(skillsDir, entry.name, "SKILL.md")
|
|
1041
|
-
);
|
|
1042
|
-
const skillYamlRelative = path9.relative(
|
|
1043
|
-
process.cwd(),
|
|
1044
|
-
path9.join(skillsDir, entry.name, "skill.yaml")
|
|
1045
|
-
);
|
|
1046
|
-
const args = (meta.cli?.args ?? []).map((a) => ({
|
|
1047
|
-
name: a.name,
|
|
1048
|
-
description: a.description ?? "",
|
|
1049
|
-
required: a.required ?? false
|
|
1050
|
-
}));
|
|
1051
|
-
const tools = [...meta.tools ?? []];
|
|
1052
|
-
if (!tools.includes("Read")) {
|
|
1053
|
-
tools.push("Read");
|
|
1054
|
-
}
|
|
1055
|
-
const contextLines = [];
|
|
1056
|
-
if (meta.cognitive_mode) {
|
|
1057
|
-
contextLines.push(`Cognitive mode: ${meta.cognitive_mode}`);
|
|
1058
|
-
}
|
|
1059
|
-
if (meta.type) {
|
|
1060
|
-
contextLines.push(`Type: ${meta.type}`);
|
|
1061
|
-
}
|
|
1062
|
-
if (meta.state?.persistent) {
|
|
1063
|
-
const files = meta.state.files?.join(", ") ?? "";
|
|
1064
|
-
contextLines.push(`State: persistent${files ? ` (files: ${files})` : ""}`);
|
|
1065
|
-
}
|
|
1066
|
-
const objectiveLines = [meta.description];
|
|
1067
|
-
if (meta.phases && meta.phases.length > 0) {
|
|
1068
|
-
objectiveLines.push("");
|
|
1069
|
-
objectiveLines.push("Phases:");
|
|
1070
|
-
for (const phase of meta.phases) {
|
|
1071
|
-
const req = phase.required !== false ? "" : " (optional)";
|
|
1072
|
-
objectiveLines.push(`- ${phase.name}: ${phase.description}${req}`);
|
|
1073
|
-
}
|
|
1074
|
-
}
|
|
1075
|
-
const executionContextLines = [];
|
|
1076
|
-
if (skillMdContent) {
|
|
1077
|
-
executionContextLines.push(`@${skillMdRelative}`);
|
|
1078
|
-
executionContextLines.push(`@${skillYamlRelative}`);
|
|
1079
|
-
}
|
|
1080
|
-
const processLines = [];
|
|
1081
|
-
if (meta.mcp?.tool) {
|
|
1082
|
-
processLines.push(
|
|
1083
|
-
`1. Try: invoke mcp__harness__${meta.mcp.tool} with skill: "${meta.name}"`
|
|
1084
|
-
);
|
|
1085
|
-
processLines.push(`2. If MCP unavailable: read SKILL.md and follow its workflow directly`);
|
|
1086
|
-
processLines.push(`3. Pass through any arguments provided by the user`);
|
|
1087
|
-
} else {
|
|
1088
|
-
processLines.push(`1. Read SKILL.md and follow its workflow directly`);
|
|
1089
|
-
processLines.push(`2. Pass through any arguments provided by the user`);
|
|
1090
|
-
}
|
|
1091
|
-
specs.push({
|
|
1092
|
-
name: normalized,
|
|
1093
|
-
namespace: "harness",
|
|
1094
|
-
fullName: `harness:${normalized}`,
|
|
1095
|
-
description: meta.description,
|
|
1096
|
-
version: meta.version,
|
|
1097
|
-
...meta.cognitive_mode ? { cognitiveMode: meta.cognitive_mode } : {},
|
|
1098
|
-
tools,
|
|
1099
|
-
args,
|
|
1100
|
-
skillYamlName: meta.name,
|
|
1101
|
-
sourceDir: entry.name,
|
|
1102
|
-
skillsBaseDir: skillsDir,
|
|
1103
|
-
source,
|
|
1104
|
-
prompt: {
|
|
1105
|
-
context: contextLines.join("\n"),
|
|
1106
|
-
objective: objectiveLines.join("\n"),
|
|
1107
|
-
executionContext: executionContextLines.join("\n"),
|
|
1108
|
-
process: processLines.join("\n")
|
|
1109
|
-
}
|
|
1110
|
-
});
|
|
1648
|
+
if (checkNameCollision(normalized, meta.name, source, nameMap) === "skip") continue;
|
|
1649
|
+
specs.push(buildSpec(meta, normalized, entry, skillsDir, source));
|
|
1111
1650
|
}
|
|
1112
1651
|
}
|
|
1113
1652
|
return specs;
|
|
@@ -1207,13 +1746,13 @@ function renderGemini(spec, skillMdContent, skillYamlContent) {
|
|
|
1207
1746
|
// src/commands/generate-slash-commands.ts
|
|
1208
1747
|
function resolveOutputDir(platform, opts) {
|
|
1209
1748
|
if (opts.output) {
|
|
1210
|
-
return
|
|
1749
|
+
return path13.join(opts.output, "harness");
|
|
1211
1750
|
}
|
|
1212
1751
|
if (opts.global) {
|
|
1213
1752
|
const home = os.homedir();
|
|
1214
|
-
return platform === "claude-code" ?
|
|
1753
|
+
return platform === "claude-code" ? path13.join(home, ".claude", "commands", "harness") : path13.join(home, ".gemini", "commands", "harness");
|
|
1215
1754
|
}
|
|
1216
|
-
return platform === "claude-code" ?
|
|
1755
|
+
return platform === "claude-code" ? path13.join("agents", "commands", "claude-code", "harness") : path13.join("agents", "commands", "gemini-cli", "harness");
|
|
1217
1756
|
}
|
|
1218
1757
|
function fileExtension(platform) {
|
|
1219
1758
|
return platform === "claude-code" ? ".md" : ".toml";
|
|
@@ -1228,26 +1767,29 @@ Remove ${files.length} orphaned command(s)? (y/N) `, (answer) => {
|
|
|
1228
1767
|
});
|
|
1229
1768
|
});
|
|
1230
1769
|
}
|
|
1231
|
-
function
|
|
1232
|
-
const skillSources = [];
|
|
1770
|
+
function resolveSkillSources(opts) {
|
|
1233
1771
|
if (opts.skillsDir) {
|
|
1234
|
-
|
|
1235
|
-
}
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
}
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
}
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
}
|
|
1772
|
+
return [{ dir: opts.skillsDir, source: "project" }];
|
|
1773
|
+
}
|
|
1774
|
+
const sources = [];
|
|
1775
|
+
const projectDir = resolveProjectSkillsDir();
|
|
1776
|
+
if (projectDir) {
|
|
1777
|
+
sources.push({ dir: projectDir, source: "project" });
|
|
1778
|
+
}
|
|
1779
|
+
const communityDir = resolveCommunitySkillsDir();
|
|
1780
|
+
if (fs11.existsSync(communityDir)) {
|
|
1781
|
+
sources.push({ dir: communityDir, source: "community" });
|
|
1782
|
+
}
|
|
1783
|
+
if (opts.includeGlobal || sources.length === 0) {
|
|
1784
|
+
const globalDir = resolveGlobalSkillsDir();
|
|
1785
|
+
if (!projectDir || path13.resolve(globalDir) !== path13.resolve(projectDir)) {
|
|
1786
|
+
sources.push({ dir: globalDir, source: "global" });
|
|
1249
1787
|
}
|
|
1250
1788
|
}
|
|
1789
|
+
return sources;
|
|
1790
|
+
}
|
|
1791
|
+
function generateSlashCommands(opts) {
|
|
1792
|
+
const skillSources = resolveSkillSources(opts);
|
|
1251
1793
|
const specs = normalizeSkills(skillSources, opts.platforms);
|
|
1252
1794
|
const results = [];
|
|
1253
1795
|
for (const platform of opts.platforms) {
|
|
@@ -1265,7 +1807,7 @@ function generateSlashCommands(opts) {
|
|
|
1265
1807
|
executionContext: spec.prompt.executionContext.split("\n").map((line) => {
|
|
1266
1808
|
if (line.startsWith("@")) {
|
|
1267
1809
|
const relPath = line.slice(1);
|
|
1268
|
-
return `@${
|
|
1810
|
+
return `@${path13.resolve(relPath)}`;
|
|
1269
1811
|
}
|
|
1270
1812
|
return line;
|
|
1271
1813
|
}).join("\n")
|
|
@@ -1273,10 +1815,10 @@ function generateSlashCommands(opts) {
|
|
|
1273
1815
|
} : spec;
|
|
1274
1816
|
rendered.set(filename, renderClaudeCode(renderSpec));
|
|
1275
1817
|
} else {
|
|
1276
|
-
const mdPath =
|
|
1277
|
-
const yamlPath =
|
|
1278
|
-
const mdContent =
|
|
1279
|
-
const yamlContent =
|
|
1818
|
+
const mdPath = path13.join(spec.skillsBaseDir, spec.sourceDir, "SKILL.md");
|
|
1819
|
+
const yamlPath = path13.join(spec.skillsBaseDir, spec.sourceDir, "skill.yaml");
|
|
1820
|
+
const mdContent = fs11.existsSync(mdPath) ? fs11.readFileSync(mdPath, "utf-8") : "";
|
|
1821
|
+
const yamlContent = fs11.existsSync(yamlPath) ? fs11.readFileSync(yamlPath, "utf-8") : "";
|
|
1280
1822
|
rendered.set(filename, renderGemini(spec, mdContent, yamlContent));
|
|
1281
1823
|
}
|
|
1282
1824
|
}
|
|
@@ -1302,9 +1844,9 @@ async function handleOrphanDeletion(results, opts) {
|
|
|
1302
1844
|
const shouldDelete = opts.yes || await confirmDeletion(result.removed);
|
|
1303
1845
|
if (shouldDelete) {
|
|
1304
1846
|
for (const filename of result.removed) {
|
|
1305
|
-
const filePath =
|
|
1306
|
-
if (
|
|
1307
|
-
|
|
1847
|
+
const filePath = path13.join(result.outputDir, filename);
|
|
1848
|
+
if (fs11.existsSync(filePath)) {
|
|
1849
|
+
fs11.unlinkSync(filePath);
|
|
1308
1850
|
}
|
|
1309
1851
|
}
|
|
1310
1852
|
}
|
|
@@ -1430,7 +1972,7 @@ async function handleGenerateSlashCommands(input) {
|
|
|
1430
1972
|
// src/mcp/resources/state.ts
|
|
1431
1973
|
async function getStateResource(projectRoot) {
|
|
1432
1974
|
try {
|
|
1433
|
-
const { loadState, migrateToStreams } = await import("./dist-
|
|
1975
|
+
const { loadState, migrateToStreams } = await import("./dist-HWXF2C3R.js");
|
|
1434
1976
|
await migrateToStreams(projectRoot);
|
|
1435
1977
|
const result = await loadState(projectRoot);
|
|
1436
1978
|
if (result.ok) {
|
|
@@ -1454,8 +1996,7 @@ async function getStateResource(projectRoot) {
|
|
|
1454
1996
|
}
|
|
1455
1997
|
}
|
|
1456
1998
|
|
|
1457
|
-
// src/mcp/tools/graph.ts
|
|
1458
|
-
import * as path11 from "path";
|
|
1999
|
+
// src/mcp/tools/graph/shared.ts
|
|
1459
2000
|
function graphNotFoundError() {
|
|
1460
2001
|
return {
|
|
1461
2002
|
content: [
|
|
@@ -1467,6 +2008,8 @@ function graphNotFoundError() {
|
|
|
1467
2008
|
isError: true
|
|
1468
2009
|
};
|
|
1469
2010
|
}
|
|
2011
|
+
|
|
2012
|
+
// src/mcp/tools/graph/query-graph.ts
|
|
1470
2013
|
var queryGraphDefinition = {
|
|
1471
2014
|
name: "query_graph",
|
|
1472
2015
|
description: "Query the project knowledge graph using ContextQL. Traverses from root nodes outward, filtering by node/edge types.",
|
|
@@ -1517,7 +2060,7 @@ async function handleQueryGraph(input) {
|
|
|
1517
2060
|
const projectPath = sanitizePath(input.path);
|
|
1518
2061
|
const store = await loadGraphStore(projectPath);
|
|
1519
2062
|
if (!store) return graphNotFoundError();
|
|
1520
|
-
const { ContextQL } = await import("./dist-
|
|
2063
|
+
const { ContextQL } = await import("./dist-B26DFXMP.js");
|
|
1521
2064
|
const cql = new ContextQL(store);
|
|
1522
2065
|
const result = cql.execute({
|
|
1523
2066
|
rootNodeIds: input.rootNodeIds,
|
|
@@ -1583,6 +2126,8 @@ async function handleQueryGraph(input) {
|
|
|
1583
2126
|
};
|
|
1584
2127
|
}
|
|
1585
2128
|
}
|
|
2129
|
+
|
|
2130
|
+
// src/mcp/tools/graph/search-similar.ts
|
|
1586
2131
|
var searchSimilarDefinition = {
|
|
1587
2132
|
name: "search_similar",
|
|
1588
2133
|
description: "Search the knowledge graph for nodes similar to a query string using keyword and semantic fusion.",
|
|
@@ -1606,7 +2151,7 @@ async function handleSearchSimilar(input) {
|
|
|
1606
2151
|
const projectPath = sanitizePath(input.path);
|
|
1607
2152
|
const store = await loadGraphStore(projectPath);
|
|
1608
2153
|
if (!store) return graphNotFoundError();
|
|
1609
|
-
const { FusionLayer } = await import("./dist-
|
|
2154
|
+
const { FusionLayer } = await import("./dist-B26DFXMP.js");
|
|
1610
2155
|
const fusion = new FusionLayer(store);
|
|
1611
2156
|
const results = fusion.search(input.query, input.topK ?? 10);
|
|
1612
2157
|
if (input.mode === "summary") {
|
|
@@ -1638,6 +2183,8 @@ async function handleSearchSimilar(input) {
|
|
|
1638
2183
|
};
|
|
1639
2184
|
}
|
|
1640
2185
|
}
|
|
2186
|
+
|
|
2187
|
+
// src/mcp/tools/graph/find-context-for.ts
|
|
1641
2188
|
var findContextForDefinition = {
|
|
1642
2189
|
name: "find_context_for",
|
|
1643
2190
|
description: "Find relevant context for a given intent by searching the graph and expanding around top results. Returns assembled context within a token budget.",
|
|
@@ -1659,7 +2206,7 @@ async function handleFindContextFor(input) {
|
|
|
1659
2206
|
const projectPath = sanitizePath(input.path);
|
|
1660
2207
|
const store = await loadGraphStore(projectPath);
|
|
1661
2208
|
if (!store) return graphNotFoundError();
|
|
1662
|
-
const { FusionLayer, ContextQL } = await import("./dist-
|
|
2209
|
+
const { FusionLayer, ContextQL } = await import("./dist-B26DFXMP.js");
|
|
1663
2210
|
const fusion = new FusionLayer(store);
|
|
1664
2211
|
const cql = new ContextQL(store);
|
|
1665
2212
|
const tokenBudget = input.tokenBudget ?? 4e3;
|
|
@@ -1725,6 +2272,8 @@ async function handleFindContextFor(input) {
|
|
|
1725
2272
|
};
|
|
1726
2273
|
}
|
|
1727
2274
|
}
|
|
2275
|
+
|
|
2276
|
+
// src/mcp/tools/graph/get-relationships.ts
|
|
1728
2277
|
var getRelationshipsDefinition = {
|
|
1729
2278
|
name: "get_relationships",
|
|
1730
2279
|
description: "Get relationships for a specific node in the knowledge graph, with configurable direction and depth.",
|
|
@@ -1753,7 +2302,7 @@ async function handleGetRelationships(input) {
|
|
|
1753
2302
|
const projectPath = sanitizePath(input.path);
|
|
1754
2303
|
const store = await loadGraphStore(projectPath);
|
|
1755
2304
|
if (!store) return graphNotFoundError();
|
|
1756
|
-
const { ContextQL } = await import("./dist-
|
|
2305
|
+
const { ContextQL } = await import("./dist-B26DFXMP.js");
|
|
1757
2306
|
const cql = new ContextQL(store);
|
|
1758
2307
|
const direction = input.direction ?? "both";
|
|
1759
2308
|
const bidirectional = direction === "both" || direction === "inbound";
|
|
@@ -1820,6 +2369,8 @@ async function handleGetRelationships(input) {
|
|
|
1820
2369
|
};
|
|
1821
2370
|
}
|
|
1822
2371
|
}
|
|
2372
|
+
|
|
2373
|
+
// src/mcp/tools/graph/get-impact.ts
|
|
1823
2374
|
var getImpactDefinition = {
|
|
1824
2375
|
name: "get_impact",
|
|
1825
2376
|
description: "Analyze the impact of changing a node or file. Returns affected tests, docs, code, and other nodes grouped by type.",
|
|
@@ -1857,7 +2408,7 @@ async function handleGetImpact(input) {
|
|
|
1857
2408
|
const projectPath = sanitizePath(input.path);
|
|
1858
2409
|
const store = await loadGraphStore(projectPath);
|
|
1859
2410
|
if (!store) return graphNotFoundError();
|
|
1860
|
-
const { ContextQL } = await import("./dist-
|
|
2411
|
+
const { ContextQL } = await import("./dist-B26DFXMP.js");
|
|
1861
2412
|
let targetNodeId = input.nodeId;
|
|
1862
2413
|
if (!targetNodeId && input.filePath) {
|
|
1863
2414
|
const fileNodes = store.findNodes({ type: "file" });
|
|
@@ -1966,6 +2517,9 @@ async function handleGetImpact(input) {
|
|
|
1966
2517
|
};
|
|
1967
2518
|
}
|
|
1968
2519
|
}
|
|
2520
|
+
|
|
2521
|
+
// src/mcp/tools/graph/ingest-source.ts
|
|
2522
|
+
import * as path14 from "path";
|
|
1969
2523
|
var ingestSourceDefinition = {
|
|
1970
2524
|
name: "ingest_source",
|
|
1971
2525
|
description: "Ingest sources into the project knowledge graph. Supports code analysis, knowledge documents, git history, or all at once.",
|
|
@@ -1985,10 +2539,10 @@ var ingestSourceDefinition = {
|
|
|
1985
2539
|
async function handleIngestSource(input) {
|
|
1986
2540
|
try {
|
|
1987
2541
|
const projectPath = sanitizePath(input.path);
|
|
1988
|
-
const graphDir =
|
|
1989
|
-
const { GraphStore, CodeIngestor, TopologicalLinker, KnowledgeIngestor, GitIngestor } = await import("./dist-
|
|
1990
|
-
const
|
|
1991
|
-
await
|
|
2542
|
+
const graphDir = path14.join(projectPath, ".harness", "graph");
|
|
2543
|
+
const { GraphStore, CodeIngestor, TopologicalLinker, KnowledgeIngestor, GitIngestor } = await import("./dist-B26DFXMP.js");
|
|
2544
|
+
const fs14 = await import("fs/promises");
|
|
2545
|
+
await fs14.mkdir(graphDir, { recursive: true });
|
|
1992
2546
|
const store = new GraphStore();
|
|
1993
2547
|
await store.load(graphDir);
|
|
1994
2548
|
const results = [];
|
|
@@ -2037,6 +2591,8 @@ async function handleIngestSource(input) {
|
|
|
2037
2591
|
};
|
|
2038
2592
|
}
|
|
2039
2593
|
}
|
|
2594
|
+
|
|
2595
|
+
// src/mcp/tools/graph/detect-anomalies.ts
|
|
2040
2596
|
var detectAnomaliesDefinition = {
|
|
2041
2597
|
name: "detect_anomalies",
|
|
2042
2598
|
description: "Detect structural anomalies \u2014 statistical outliers across code metrics and topological single points of failure in the import graph",
|
|
@@ -2059,7 +2615,7 @@ async function handleDetectAnomalies(input) {
|
|
|
2059
2615
|
const projectPath = sanitizePath(input.path);
|
|
2060
2616
|
const store = await loadGraphStore(projectPath);
|
|
2061
2617
|
if (!store) return graphNotFoundError();
|
|
2062
|
-
const { GraphAnomalyAdapter } = await import("./dist-
|
|
2618
|
+
const { GraphAnomalyAdapter } = await import("./dist-B26DFXMP.js");
|
|
2063
2619
|
const adapter = new GraphAnomalyAdapter(store);
|
|
2064
2620
|
const report = adapter.detect({
|
|
2065
2621
|
...input.threshold !== void 0 && { threshold: input.threshold },
|
|
@@ -2080,6 +2636,8 @@ async function handleDetectAnomalies(input) {
|
|
|
2080
2636
|
};
|
|
2081
2637
|
}
|
|
2082
2638
|
}
|
|
2639
|
+
|
|
2640
|
+
// src/mcp/tools/graph/ask-graph.ts
|
|
2083
2641
|
var askGraphDefinition = {
|
|
2084
2642
|
name: "ask_graph",
|
|
2085
2643
|
description: 'Ask a natural language question about the codebase knowledge graph. Supports questions about impact ("what breaks if I change X?"), finding entities ("where is the auth middleware?"), relationships ("what calls UserService?"), explanations ("what is GraphStore?"), and anomalies ("what looks wrong?"). Returns a human-readable summary and raw graph data.',
|
|
@@ -2097,7 +2655,7 @@ async function handleAskGraph(input) {
|
|
|
2097
2655
|
const projectPath = sanitizePath(input.path);
|
|
2098
2656
|
const store = await loadGraphStore(projectPath);
|
|
2099
2657
|
if (!store) return graphNotFoundError();
|
|
2100
|
-
const { askGraph } = await import("./dist-
|
|
2658
|
+
const { askGraph } = await import("./dist-B26DFXMP.js");
|
|
2101
2659
|
const result = await askGraph(store, input.question);
|
|
2102
2660
|
return {
|
|
2103
2661
|
content: [{ type: "text", text: JSON.stringify(result) }]
|
|
@@ -2116,8 +2674,8 @@ async function handleAskGraph(input) {
|
|
|
2116
2674
|
}
|
|
2117
2675
|
|
|
2118
2676
|
// src/mcp/resources/graph.ts
|
|
2119
|
-
import * as
|
|
2120
|
-
import * as
|
|
2677
|
+
import * as fs12 from "fs/promises";
|
|
2678
|
+
import * as path15 from "path";
|
|
2121
2679
|
var MAX_ITEMS = 5e3;
|
|
2122
2680
|
function formatStaleness(isoTimestamp) {
|
|
2123
2681
|
const then = new Date(isoTimestamp).getTime();
|
|
@@ -2140,11 +2698,11 @@ async function getGraphResource(projectRoot) {
|
|
|
2140
2698
|
message: "No knowledge graph found. Run harness scan to build one."
|
|
2141
2699
|
});
|
|
2142
2700
|
}
|
|
2143
|
-
const graphDir =
|
|
2144
|
-
const metadataPath =
|
|
2701
|
+
const graphDir = path15.join(projectRoot, ".harness", "graph");
|
|
2702
|
+
const metadataPath = path15.join(graphDir, "metadata.json");
|
|
2145
2703
|
let lastScanTimestamp = null;
|
|
2146
2704
|
try {
|
|
2147
|
-
const raw = JSON.parse(await
|
|
2705
|
+
const raw = JSON.parse(await fs12.readFile(metadataPath, "utf-8"));
|
|
2148
2706
|
lastScanTimestamp = raw.lastScanTimestamp ?? null;
|
|
2149
2707
|
} catch {
|
|
2150
2708
|
}
|
|
@@ -2233,7 +2791,7 @@ var generateAgentDefinitionsDefinition = {
|
|
|
2233
2791
|
}
|
|
2234
2792
|
};
|
|
2235
2793
|
async function handleGenerateAgentDefinitions(input) {
|
|
2236
|
-
const { generateAgentDefinitions } = await import("./generate-agent-definitions-
|
|
2794
|
+
const { generateAgentDefinitions } = await import("./generate-agent-definitions-3PM5EU7V.js");
|
|
2237
2795
|
const platforms = input.platform === "all" || !input.platform ? ["claude-code", "gemini-cli"] : [input.platform];
|
|
2238
2796
|
const results = generateAgentDefinitions({
|
|
2239
2797
|
platforms: [...platforms],
|
|
@@ -2244,8 +2802,8 @@ async function handleGenerateAgentDefinitions(input) {
|
|
|
2244
2802
|
}
|
|
2245
2803
|
|
|
2246
2804
|
// src/mcp/tools/roadmap.ts
|
|
2247
|
-
import * as
|
|
2248
|
-
import * as
|
|
2805
|
+
import * as fs13 from "fs";
|
|
2806
|
+
import * as path16 from "path";
|
|
2249
2807
|
var manageRoadmapDefinition = {
|
|
2250
2808
|
name: "manage_roadmap",
|
|
2251
2809
|
description: "Manage the project roadmap: show, add, update, remove, sync features, or query by filter. Reads and writes docs/roadmap.md.",
|
|
@@ -2300,309 +2858,261 @@ var manageRoadmapDefinition = {
|
|
|
2300
2858
|
}
|
|
2301
2859
|
};
|
|
2302
2860
|
function roadmapPath(projectRoot) {
|
|
2303
|
-
return
|
|
2861
|
+
return path16.join(projectRoot, "docs", "roadmap.md");
|
|
2304
2862
|
}
|
|
2305
2863
|
function readRoadmapFile(projectRoot) {
|
|
2306
2864
|
const filePath = roadmapPath(projectRoot);
|
|
2307
2865
|
try {
|
|
2308
|
-
return
|
|
2866
|
+
return fs13.readFileSync(filePath, "utf-8");
|
|
2309
2867
|
} catch {
|
|
2310
2868
|
return null;
|
|
2311
2869
|
}
|
|
2312
2870
|
}
|
|
2313
2871
|
function writeRoadmapFile(projectRoot, content) {
|
|
2314
2872
|
const filePath = roadmapPath(projectRoot);
|
|
2315
|
-
const dir =
|
|
2316
|
-
|
|
2317
|
-
|
|
2873
|
+
const dir = path16.dirname(filePath);
|
|
2874
|
+
fs13.mkdirSync(dir, { recursive: true });
|
|
2875
|
+
fs13.writeFileSync(filePath, content, "utf-8");
|
|
2318
2876
|
}
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
case "show": {
|
|
2326
|
-
const raw = readRoadmapFile(projectPath);
|
|
2327
|
-
if (raw === null) {
|
|
2328
|
-
return {
|
|
2329
|
-
content: [
|
|
2330
|
-
{
|
|
2331
|
-
type: "text",
|
|
2332
|
-
text: "Error: docs/roadmap.md not found. Create a roadmap first."
|
|
2333
|
-
}
|
|
2334
|
-
],
|
|
2335
|
-
isError: true
|
|
2336
|
-
};
|
|
2337
|
-
}
|
|
2338
|
-
const result = parseRoadmap(raw);
|
|
2339
|
-
if (!result.ok) return resultToMcpResponse(result);
|
|
2340
|
-
let roadmap = result.value;
|
|
2341
|
-
if (input.milestone) {
|
|
2342
|
-
const milestoneFilter = input.milestone;
|
|
2343
|
-
roadmap = {
|
|
2344
|
-
...roadmap,
|
|
2345
|
-
milestones: roadmap.milestones.filter(
|
|
2346
|
-
(m) => m.name.toLowerCase() === milestoneFilter.toLowerCase()
|
|
2347
|
-
)
|
|
2348
|
-
};
|
|
2349
|
-
}
|
|
2350
|
-
if (input.status) {
|
|
2351
|
-
const statusFilter = input.status;
|
|
2352
|
-
roadmap = {
|
|
2353
|
-
...roadmap,
|
|
2354
|
-
milestones: roadmap.milestones.map((m) => ({
|
|
2355
|
-
...m,
|
|
2356
|
-
features: m.features.filter((f) => f.status === statusFilter)
|
|
2357
|
-
})).filter((m) => m.features.length > 0)
|
|
2358
|
-
};
|
|
2359
|
-
}
|
|
2360
|
-
return resultToMcpResponse(Ok2(roadmap));
|
|
2361
|
-
}
|
|
2362
|
-
case "add": {
|
|
2363
|
-
if (!input.feature) {
|
|
2364
|
-
return {
|
|
2365
|
-
content: [{ type: "text", text: "Error: feature is required for add action" }],
|
|
2366
|
-
isError: true
|
|
2367
|
-
};
|
|
2368
|
-
}
|
|
2369
|
-
if (!input.milestone) {
|
|
2370
|
-
return {
|
|
2371
|
-
content: [
|
|
2372
|
-
{ type: "text", text: "Error: milestone is required for add action" }
|
|
2373
|
-
],
|
|
2374
|
-
isError: true
|
|
2375
|
-
};
|
|
2376
|
-
}
|
|
2377
|
-
if (!input.status) {
|
|
2378
|
-
return {
|
|
2379
|
-
content: [{ type: "text", text: "Error: status is required for add action" }],
|
|
2380
|
-
isError: true
|
|
2381
|
-
};
|
|
2382
|
-
}
|
|
2383
|
-
if (!input.summary) {
|
|
2384
|
-
return {
|
|
2385
|
-
content: [{ type: "text", text: "Error: summary is required for add action" }],
|
|
2386
|
-
isError: true
|
|
2387
|
-
};
|
|
2388
|
-
}
|
|
2389
|
-
const raw = readRoadmapFile(projectPath);
|
|
2390
|
-
if (raw === null) {
|
|
2391
|
-
return {
|
|
2392
|
-
content: [
|
|
2393
|
-
{
|
|
2394
|
-
type: "text",
|
|
2395
|
-
text: "Error: docs/roadmap.md not found. Create a roadmap first."
|
|
2396
|
-
}
|
|
2397
|
-
],
|
|
2398
|
-
isError: true
|
|
2399
|
-
};
|
|
2400
|
-
}
|
|
2401
|
-
const result = parseRoadmap(raw);
|
|
2402
|
-
if (!result.ok) return resultToMcpResponse(result);
|
|
2403
|
-
const roadmap = result.value;
|
|
2404
|
-
const milestone = roadmap.milestones.find(
|
|
2405
|
-
(m) => m.name.toLowerCase() === input.milestone.toLowerCase()
|
|
2406
|
-
);
|
|
2407
|
-
if (!milestone) {
|
|
2408
|
-
return {
|
|
2409
|
-
content: [
|
|
2410
|
-
{ type: "text", text: `Error: milestone "${input.milestone}" not found` }
|
|
2411
|
-
],
|
|
2412
|
-
isError: true
|
|
2413
|
-
};
|
|
2414
|
-
}
|
|
2415
|
-
milestone.features.push({
|
|
2416
|
-
name: input.feature,
|
|
2417
|
-
status: input.status,
|
|
2418
|
-
spec: input.spec ?? null,
|
|
2419
|
-
plans: input.plans ?? [],
|
|
2420
|
-
blockedBy: input.blocked_by ?? [],
|
|
2421
|
-
summary: input.summary
|
|
2422
|
-
});
|
|
2423
|
-
roadmap.frontmatter.lastManualEdit = (/* @__PURE__ */ new Date()).toISOString();
|
|
2424
|
-
writeRoadmapFile(projectPath, serializeRoadmap(roadmap));
|
|
2425
|
-
return resultToMcpResponse(Ok2(roadmap));
|
|
2426
|
-
}
|
|
2427
|
-
case "update": {
|
|
2428
|
-
if (!input.feature) {
|
|
2429
|
-
return {
|
|
2430
|
-
content: [
|
|
2431
|
-
{ type: "text", text: "Error: feature is required for update action" }
|
|
2432
|
-
],
|
|
2433
|
-
isError: true
|
|
2434
|
-
};
|
|
2435
|
-
}
|
|
2436
|
-
const raw = readRoadmapFile(projectPath);
|
|
2437
|
-
if (raw === null) {
|
|
2438
|
-
return {
|
|
2439
|
-
content: [
|
|
2440
|
-
{
|
|
2441
|
-
type: "text",
|
|
2442
|
-
text: "Error: docs/roadmap.md not found. Create a roadmap first."
|
|
2443
|
-
}
|
|
2444
|
-
],
|
|
2445
|
-
isError: true
|
|
2446
|
-
};
|
|
2447
|
-
}
|
|
2448
|
-
const result = parseRoadmap(raw);
|
|
2449
|
-
if (!result.ok) return resultToMcpResponse(result);
|
|
2450
|
-
const roadmap = result.value;
|
|
2451
|
-
let found = false;
|
|
2452
|
-
for (const m of roadmap.milestones) {
|
|
2453
|
-
const feature = m.features.find(
|
|
2454
|
-
(f) => f.name.toLowerCase() === input.feature.toLowerCase()
|
|
2455
|
-
);
|
|
2456
|
-
if (feature) {
|
|
2457
|
-
if (input.status) feature.status = input.status;
|
|
2458
|
-
if (input.summary !== void 0) feature.summary = input.summary;
|
|
2459
|
-
if (input.spec !== void 0) feature.spec = input.spec || null;
|
|
2460
|
-
if (input.plans !== void 0) feature.plans = input.plans;
|
|
2461
|
-
if (input.blocked_by !== void 0) feature.blockedBy = input.blocked_by;
|
|
2462
|
-
found = true;
|
|
2463
|
-
break;
|
|
2464
|
-
}
|
|
2465
|
-
}
|
|
2466
|
-
if (!found) {
|
|
2467
|
-
return {
|
|
2468
|
-
content: [
|
|
2469
|
-
{ type: "text", text: `Error: feature "${input.feature}" not found` }
|
|
2470
|
-
],
|
|
2471
|
-
isError: true
|
|
2472
|
-
};
|
|
2473
|
-
}
|
|
2474
|
-
roadmap.frontmatter.lastManualEdit = (/* @__PURE__ */ new Date()).toISOString();
|
|
2475
|
-
writeRoadmapFile(projectPath, serializeRoadmap(roadmap));
|
|
2476
|
-
return resultToMcpResponse(Ok2(roadmap));
|
|
2477
|
-
}
|
|
2478
|
-
case "remove": {
|
|
2479
|
-
if (!input.feature) {
|
|
2480
|
-
return {
|
|
2481
|
-
content: [
|
|
2482
|
-
{ type: "text", text: "Error: feature is required for remove action" }
|
|
2483
|
-
],
|
|
2484
|
-
isError: true
|
|
2485
|
-
};
|
|
2486
|
-
}
|
|
2487
|
-
const raw = readRoadmapFile(projectPath);
|
|
2488
|
-
if (raw === null) {
|
|
2489
|
-
return {
|
|
2490
|
-
content: [
|
|
2491
|
-
{
|
|
2492
|
-
type: "text",
|
|
2493
|
-
text: "Error: docs/roadmap.md not found. Create a roadmap first."
|
|
2494
|
-
}
|
|
2495
|
-
],
|
|
2496
|
-
isError: true
|
|
2497
|
-
};
|
|
2498
|
-
}
|
|
2499
|
-
const result = parseRoadmap(raw);
|
|
2500
|
-
if (!result.ok) return resultToMcpResponse(result);
|
|
2501
|
-
const roadmap = result.value;
|
|
2502
|
-
let found = false;
|
|
2503
|
-
for (const m of roadmap.milestones) {
|
|
2504
|
-
const idx = m.features.findIndex(
|
|
2505
|
-
(f) => f.name.toLowerCase() === input.feature.toLowerCase()
|
|
2506
|
-
);
|
|
2507
|
-
if (idx !== -1) {
|
|
2508
|
-
m.features.splice(idx, 1);
|
|
2509
|
-
found = true;
|
|
2510
|
-
break;
|
|
2511
|
-
}
|
|
2512
|
-
}
|
|
2513
|
-
if (!found) {
|
|
2514
|
-
return {
|
|
2515
|
-
content: [
|
|
2516
|
-
{ type: "text", text: `Error: feature "${input.feature}" not found` }
|
|
2517
|
-
],
|
|
2518
|
-
isError: true
|
|
2519
|
-
};
|
|
2520
|
-
}
|
|
2521
|
-
roadmap.frontmatter.lastManualEdit = (/* @__PURE__ */ new Date()).toISOString();
|
|
2522
|
-
writeRoadmapFile(projectPath, serializeRoadmap(roadmap));
|
|
2523
|
-
return resultToMcpResponse(Ok2(roadmap));
|
|
2877
|
+
function roadmapNotFoundError() {
|
|
2878
|
+
return {
|
|
2879
|
+
content: [
|
|
2880
|
+
{
|
|
2881
|
+
type: "text",
|
|
2882
|
+
text: "Error: docs/roadmap.md not found. Create a roadmap first."
|
|
2524
2883
|
}
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2884
|
+
],
|
|
2885
|
+
isError: true
|
|
2886
|
+
};
|
|
2887
|
+
}
|
|
2888
|
+
function handleShow2(projectPath, input, deps) {
|
|
2889
|
+
const { parseRoadmap, Ok: Ok2 } = deps;
|
|
2890
|
+
const raw = readRoadmapFile(projectPath);
|
|
2891
|
+
if (raw === null) return roadmapNotFoundError();
|
|
2892
|
+
const result = parseRoadmap(raw);
|
|
2893
|
+
if (!result.ok) return resultToMcpResponse(result);
|
|
2894
|
+
let roadmap = result.value;
|
|
2895
|
+
if (input.milestone) {
|
|
2896
|
+
const milestoneFilter = input.milestone;
|
|
2897
|
+
roadmap = {
|
|
2898
|
+
...roadmap,
|
|
2899
|
+
milestones: roadmap.milestones.filter(
|
|
2900
|
+
(m) => m.name.toLowerCase() === milestoneFilter.toLowerCase()
|
|
2901
|
+
)
|
|
2902
|
+
};
|
|
2903
|
+
}
|
|
2904
|
+
if (input.status) {
|
|
2905
|
+
const statusFilter = input.status;
|
|
2906
|
+
roadmap = {
|
|
2907
|
+
...roadmap,
|
|
2908
|
+
milestones: roadmap.milestones.map((m) => ({
|
|
2909
|
+
...m,
|
|
2910
|
+
features: m.features.filter((f) => f.status === statusFilter)
|
|
2911
|
+
})).filter((m) => m.features.length > 0)
|
|
2912
|
+
};
|
|
2913
|
+
}
|
|
2914
|
+
return resultToMcpResponse(Ok2(roadmap));
|
|
2915
|
+
}
|
|
2916
|
+
function handleAdd(projectPath, input, deps) {
|
|
2917
|
+
const { parseRoadmap, serializeRoadmap, Ok: Ok2 } = deps;
|
|
2918
|
+
if (!input.feature) {
|
|
2919
|
+
return {
|
|
2920
|
+
content: [{ type: "text", text: "Error: feature is required for add action" }],
|
|
2921
|
+
isError: true
|
|
2922
|
+
};
|
|
2923
|
+
}
|
|
2924
|
+
if (!input.milestone) {
|
|
2925
|
+
return {
|
|
2926
|
+
content: [{ type: "text", text: "Error: milestone is required for add action" }],
|
|
2927
|
+
isError: true
|
|
2928
|
+
};
|
|
2929
|
+
}
|
|
2930
|
+
if (!input.status) {
|
|
2931
|
+
return {
|
|
2932
|
+
content: [{ type: "text", text: "Error: status is required for add action" }],
|
|
2933
|
+
isError: true
|
|
2934
|
+
};
|
|
2935
|
+
}
|
|
2936
|
+
if (!input.summary) {
|
|
2937
|
+
return {
|
|
2938
|
+
content: [{ type: "text", text: "Error: summary is required for add action" }],
|
|
2939
|
+
isError: true
|
|
2940
|
+
};
|
|
2941
|
+
}
|
|
2942
|
+
const raw = readRoadmapFile(projectPath);
|
|
2943
|
+
if (raw === null) return roadmapNotFoundError();
|
|
2944
|
+
const result = parseRoadmap(raw);
|
|
2945
|
+
if (!result.ok) return resultToMcpResponse(result);
|
|
2946
|
+
const roadmap = result.value;
|
|
2947
|
+
const milestone = roadmap.milestones.find(
|
|
2948
|
+
(m) => m.name.toLowerCase() === input.milestone.toLowerCase()
|
|
2949
|
+
);
|
|
2950
|
+
if (!milestone) {
|
|
2951
|
+
return {
|
|
2952
|
+
content: [{ type: "text", text: `Error: milestone "${input.milestone}" not found` }],
|
|
2953
|
+
isError: true
|
|
2954
|
+
};
|
|
2955
|
+
}
|
|
2956
|
+
milestone.features.push({
|
|
2957
|
+
name: input.feature,
|
|
2958
|
+
status: input.status,
|
|
2959
|
+
spec: input.spec ?? null,
|
|
2960
|
+
plans: input.plans ?? [],
|
|
2961
|
+
blockedBy: input.blocked_by ?? [],
|
|
2962
|
+
summary: input.summary
|
|
2963
|
+
});
|
|
2964
|
+
roadmap.frontmatter.lastManualEdit = (/* @__PURE__ */ new Date()).toISOString();
|
|
2965
|
+
writeRoadmapFile(projectPath, serializeRoadmap(roadmap));
|
|
2966
|
+
return resultToMcpResponse(Ok2(roadmap));
|
|
2967
|
+
}
|
|
2968
|
+
function handleUpdate(projectPath, input, deps) {
|
|
2969
|
+
const { parseRoadmap, serializeRoadmap, Ok: Ok2 } = deps;
|
|
2970
|
+
if (!input.feature) {
|
|
2971
|
+
return {
|
|
2972
|
+
content: [{ type: "text", text: "Error: feature is required for update action" }],
|
|
2973
|
+
isError: true
|
|
2974
|
+
};
|
|
2975
|
+
}
|
|
2976
|
+
const raw = readRoadmapFile(projectPath);
|
|
2977
|
+
if (raw === null) return roadmapNotFoundError();
|
|
2978
|
+
const result = parseRoadmap(raw);
|
|
2979
|
+
if (!result.ok) return resultToMcpResponse(result);
|
|
2980
|
+
const roadmap = result.value;
|
|
2981
|
+
let found = false;
|
|
2982
|
+
for (const m of roadmap.milestones) {
|
|
2983
|
+
const feature = m.features.find((f) => f.name.toLowerCase() === input.feature.toLowerCase());
|
|
2984
|
+
if (feature) {
|
|
2985
|
+
if (input.status) feature.status = input.status;
|
|
2986
|
+
if (input.summary !== void 0) feature.summary = input.summary;
|
|
2987
|
+
if (input.spec !== void 0) feature.spec = input.spec || null;
|
|
2988
|
+
if (input.plans !== void 0) feature.plans = input.plans;
|
|
2989
|
+
if (input.blocked_by !== void 0) feature.blockedBy = input.blocked_by;
|
|
2990
|
+
found = true;
|
|
2991
|
+
break;
|
|
2992
|
+
}
|
|
2993
|
+
}
|
|
2994
|
+
if (!found) {
|
|
2995
|
+
return {
|
|
2996
|
+
content: [{ type: "text", text: `Error: feature "${input.feature}" not found` }],
|
|
2997
|
+
isError: true
|
|
2998
|
+
};
|
|
2999
|
+
}
|
|
3000
|
+
roadmap.frontmatter.lastManualEdit = (/* @__PURE__ */ new Date()).toISOString();
|
|
3001
|
+
writeRoadmapFile(projectPath, serializeRoadmap(roadmap));
|
|
3002
|
+
return resultToMcpResponse(Ok2(roadmap));
|
|
3003
|
+
}
|
|
3004
|
+
function handleRemove(projectPath, input, deps) {
|
|
3005
|
+
const { parseRoadmap, serializeRoadmap, Ok: Ok2 } = deps;
|
|
3006
|
+
if (!input.feature) {
|
|
3007
|
+
return {
|
|
3008
|
+
content: [{ type: "text", text: "Error: feature is required for remove action" }],
|
|
3009
|
+
isError: true
|
|
3010
|
+
};
|
|
3011
|
+
}
|
|
3012
|
+
const raw = readRoadmapFile(projectPath);
|
|
3013
|
+
if (raw === null) return roadmapNotFoundError();
|
|
3014
|
+
const result = parseRoadmap(raw);
|
|
3015
|
+
if (!result.ok) return resultToMcpResponse(result);
|
|
3016
|
+
const roadmap = result.value;
|
|
3017
|
+
let found = false;
|
|
3018
|
+
for (const m of roadmap.milestones) {
|
|
3019
|
+
const idx = m.features.findIndex((f) => f.name.toLowerCase() === input.feature.toLowerCase());
|
|
3020
|
+
if (idx !== -1) {
|
|
3021
|
+
m.features.splice(idx, 1);
|
|
3022
|
+
found = true;
|
|
3023
|
+
break;
|
|
3024
|
+
}
|
|
3025
|
+
}
|
|
3026
|
+
if (!found) {
|
|
3027
|
+
return {
|
|
3028
|
+
content: [{ type: "text", text: `Error: feature "${input.feature}" not found` }],
|
|
3029
|
+
isError: true
|
|
3030
|
+
};
|
|
3031
|
+
}
|
|
3032
|
+
roadmap.frontmatter.lastManualEdit = (/* @__PURE__ */ new Date()).toISOString();
|
|
3033
|
+
writeRoadmapFile(projectPath, serializeRoadmap(roadmap));
|
|
3034
|
+
return resultToMcpResponse(Ok2(roadmap));
|
|
3035
|
+
}
|
|
3036
|
+
function handleQuery(projectPath, input, deps) {
|
|
3037
|
+
const { parseRoadmap, Ok: Ok2 } = deps;
|
|
3038
|
+
if (!input.filter) {
|
|
3039
|
+
return {
|
|
3040
|
+
content: [{ type: "text", text: "Error: filter is required for query action" }],
|
|
3041
|
+
isError: true
|
|
3042
|
+
};
|
|
3043
|
+
}
|
|
3044
|
+
const raw = readRoadmapFile(projectPath);
|
|
3045
|
+
if (raw === null) return roadmapNotFoundError();
|
|
3046
|
+
const result = parseRoadmap(raw);
|
|
3047
|
+
if (!result.ok) return resultToMcpResponse(result);
|
|
3048
|
+
const roadmap = result.value;
|
|
3049
|
+
const allFeatures = roadmap.milestones.flatMap(
|
|
3050
|
+
(m) => m.features.map((f) => ({ ...f, milestone: m.name }))
|
|
3051
|
+
);
|
|
3052
|
+
const filter = input.filter.toLowerCase();
|
|
3053
|
+
let filtered;
|
|
3054
|
+
if (filter.startsWith("milestone:")) {
|
|
3055
|
+
const milestoneName = filter.slice("milestone:".length).trim();
|
|
3056
|
+
filtered = allFeatures.filter((f) => f.milestone.toLowerCase().includes(milestoneName));
|
|
3057
|
+
} else {
|
|
3058
|
+
filtered = allFeatures.filter((f) => f.status === filter);
|
|
3059
|
+
}
|
|
3060
|
+
return resultToMcpResponse(Ok2(filtered));
|
|
3061
|
+
}
|
|
3062
|
+
function handleSync(projectPath, input, deps) {
|
|
3063
|
+
const { parseRoadmap, serializeRoadmap, syncRoadmap, Ok: Ok2 } = deps;
|
|
3064
|
+
const raw = readRoadmapFile(projectPath);
|
|
3065
|
+
if (raw === null) return roadmapNotFoundError();
|
|
3066
|
+
const result = parseRoadmap(raw);
|
|
3067
|
+
if (!result.ok) return resultToMcpResponse(result);
|
|
3068
|
+
const roadmap = result.value;
|
|
3069
|
+
const syncResult = syncRoadmap({
|
|
3070
|
+
projectPath,
|
|
3071
|
+
roadmap,
|
|
3072
|
+
forceSync: input.force_sync ?? false
|
|
3073
|
+
});
|
|
3074
|
+
if (!syncResult.ok) return resultToMcpResponse(syncResult);
|
|
3075
|
+
const changes = syncResult.value;
|
|
3076
|
+
if (changes.length === 0) {
|
|
3077
|
+
return resultToMcpResponse(Ok2({ changes: [], message: "Roadmap is up to date." }));
|
|
3078
|
+
}
|
|
3079
|
+
if (input.apply) {
|
|
3080
|
+
for (const change of changes) {
|
|
3081
|
+
for (const m of roadmap.milestones) {
|
|
3082
|
+
const feature = m.features.find(
|
|
3083
|
+
(f) => f.name.toLowerCase() === change.feature.toLowerCase()
|
|
2551
3084
|
);
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
const milestoneName = filter.slice("milestone:".length).trim();
|
|
2556
|
-
filtered = allFeatures.filter((f) => f.milestone.toLowerCase().includes(milestoneName));
|
|
2557
|
-
} else {
|
|
2558
|
-
filtered = allFeatures.filter((f) => f.status === filter);
|
|
2559
|
-
}
|
|
2560
|
-
return resultToMcpResponse(Ok2(filtered));
|
|
2561
|
-
}
|
|
2562
|
-
case "sync": {
|
|
2563
|
-
const raw = readRoadmapFile(projectPath);
|
|
2564
|
-
if (raw === null) {
|
|
2565
|
-
return {
|
|
2566
|
-
content: [
|
|
2567
|
-
{
|
|
2568
|
-
type: "text",
|
|
2569
|
-
text: "Error: docs/roadmap.md not found. Create a roadmap first."
|
|
2570
|
-
}
|
|
2571
|
-
],
|
|
2572
|
-
isError: true
|
|
2573
|
-
};
|
|
2574
|
-
}
|
|
2575
|
-
const result = parseRoadmap(raw);
|
|
2576
|
-
if (!result.ok) return resultToMcpResponse(result);
|
|
2577
|
-
const roadmap = result.value;
|
|
2578
|
-
const syncResult = syncRoadmap({
|
|
2579
|
-
projectPath,
|
|
2580
|
-
roadmap,
|
|
2581
|
-
forceSync: input.force_sync ?? false
|
|
2582
|
-
});
|
|
2583
|
-
if (!syncResult.ok) return resultToMcpResponse(syncResult);
|
|
2584
|
-
const changes = syncResult.value;
|
|
2585
|
-
if (changes.length === 0) {
|
|
2586
|
-
return resultToMcpResponse(Ok2({ changes: [], message: "Roadmap is up to date." }));
|
|
2587
|
-
}
|
|
2588
|
-
if (input.apply) {
|
|
2589
|
-
for (const change of changes) {
|
|
2590
|
-
for (const m of roadmap.milestones) {
|
|
2591
|
-
const feature = m.features.find(
|
|
2592
|
-
(f) => f.name.toLowerCase() === change.feature.toLowerCase()
|
|
2593
|
-
);
|
|
2594
|
-
if (feature) {
|
|
2595
|
-
feature.status = change.to;
|
|
2596
|
-
break;
|
|
2597
|
-
}
|
|
2598
|
-
}
|
|
2599
|
-
}
|
|
2600
|
-
roadmap.frontmatter.lastSynced = (/* @__PURE__ */ new Date()).toISOString();
|
|
2601
|
-
writeRoadmapFile(projectPath, serializeRoadmap(roadmap));
|
|
2602
|
-
return resultToMcpResponse(Ok2({ changes, applied: true, roadmap }));
|
|
3085
|
+
if (feature) {
|
|
3086
|
+
feature.status = change.to;
|
|
3087
|
+
break;
|
|
2603
3088
|
}
|
|
2604
|
-
return resultToMcpResponse(Ok2({ changes, applied: false }));
|
|
2605
3089
|
}
|
|
3090
|
+
}
|
|
3091
|
+
roadmap.frontmatter.lastSynced = (/* @__PURE__ */ new Date()).toISOString();
|
|
3092
|
+
writeRoadmapFile(projectPath, serializeRoadmap(roadmap));
|
|
3093
|
+
return resultToMcpResponse(Ok2({ changes, applied: true, roadmap }));
|
|
3094
|
+
}
|
|
3095
|
+
return resultToMcpResponse(Ok2({ changes, applied: false }));
|
|
3096
|
+
}
|
|
3097
|
+
async function handleManageRoadmap(input) {
|
|
3098
|
+
try {
|
|
3099
|
+
const { parseRoadmap, serializeRoadmap, syncRoadmap } = await import("./dist-HWXF2C3R.js");
|
|
3100
|
+
const { Ok: Ok2 } = await import("./dist-USY2C5JL.js");
|
|
3101
|
+
const projectPath = sanitizePath(input.path);
|
|
3102
|
+
const deps = { parseRoadmap, serializeRoadmap, syncRoadmap, Ok: Ok2 };
|
|
3103
|
+
switch (input.action) {
|
|
3104
|
+
case "show":
|
|
3105
|
+
return handleShow2(projectPath, input, deps);
|
|
3106
|
+
case "add":
|
|
3107
|
+
return handleAdd(projectPath, input, deps);
|
|
3108
|
+
case "update":
|
|
3109
|
+
return handleUpdate(projectPath, input, deps);
|
|
3110
|
+
case "remove":
|
|
3111
|
+
return handleRemove(projectPath, input, deps);
|
|
3112
|
+
case "query":
|
|
3113
|
+
return handleQuery(projectPath, input, deps);
|
|
3114
|
+
case "sync":
|
|
3115
|
+
return handleSync(projectPath, input, deps);
|
|
2606
3116
|
default: {
|
|
2607
3117
|
return {
|
|
2608
3118
|
content: [{ type: "text", text: `Error: unknown action` }],
|
|
@@ -2972,226 +3482,119 @@ var emitInteractionDefinition = {
|
|
|
2972
3482
|
required: ["path", "type"]
|
|
2973
3483
|
}
|
|
2974
3484
|
};
|
|
3485
|
+
function formatZodErrors(issues) {
|
|
3486
|
+
return issues.map((i) => i.path.length > 0 ? `${i.path.join(".")}: ${i.message}` : i.message).join("; ");
|
|
3487
|
+
}
|
|
3488
|
+
async function handleQuestion(validInput, projectPath, id) {
|
|
3489
|
+
if (!validInput.question)
|
|
3490
|
+
return mcpError("Error: question payload is required when type is question");
|
|
3491
|
+
const questionResult = InteractionQuestionWithOptionsSchema.safeParse(validInput.question);
|
|
3492
|
+
if (!questionResult.success)
|
|
3493
|
+
return mcpError(`Error: ${formatZodErrors(questionResult.error.issues)}`);
|
|
3494
|
+
const prompt = renderQuestion(questionResult.data);
|
|
3495
|
+
await recordInteraction(projectPath, id, "question", questionResult.data.text, validInput.stream);
|
|
3496
|
+
return { content: [{ type: "text", text: JSON.stringify({ id, prompt }) }] };
|
|
3497
|
+
}
|
|
3498
|
+
async function handleConfirmation(validInput, projectPath, id) {
|
|
3499
|
+
if (!validInput.confirmation)
|
|
3500
|
+
return mcpError("Error: confirmation payload is required when type is confirmation");
|
|
3501
|
+
const confirmResult = InteractionConfirmationSchema.safeParse(validInput.confirmation);
|
|
3502
|
+
if (!confirmResult.success)
|
|
3503
|
+
return mcpError(
|
|
3504
|
+
`Error: ${confirmResult.error.issues.map((i) => i.message).join("; ")}`
|
|
3505
|
+
);
|
|
3506
|
+
const prompt = renderConfirmation(confirmResult.data);
|
|
3507
|
+
await recordInteraction(
|
|
3508
|
+
projectPath,
|
|
3509
|
+
id,
|
|
3510
|
+
"confirmation",
|
|
3511
|
+
confirmResult.data.text,
|
|
3512
|
+
validInput.stream
|
|
3513
|
+
);
|
|
3514
|
+
return { content: [{ type: "text", text: JSON.stringify({ id, prompt }) }] };
|
|
3515
|
+
}
|
|
3516
|
+
async function handleTransition(validInput, projectPath, id) {
|
|
3517
|
+
if (!validInput.transition)
|
|
3518
|
+
return mcpError("Error: transition payload is required when type is transition");
|
|
3519
|
+
const transitionResult = InteractionTransitionSchema.safeParse(validInput.transition);
|
|
3520
|
+
if (!transitionResult.success)
|
|
3521
|
+
return mcpError(
|
|
3522
|
+
`Error: ${transitionResult.error.issues.map((i) => i.message).join("; ")}`
|
|
3523
|
+
);
|
|
3524
|
+
const transition = transitionResult.data;
|
|
3525
|
+
const prompt = renderTransition(transition);
|
|
3526
|
+
try {
|
|
3527
|
+
const { saveHandoff } = await import("./dist-HWXF2C3R.js");
|
|
3528
|
+
await saveHandoff(
|
|
3529
|
+
projectPath,
|
|
3530
|
+
{
|
|
3531
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3532
|
+
fromSkill: "emit_interaction",
|
|
3533
|
+
phase: transition.completedPhase,
|
|
3534
|
+
summary: transition.reason,
|
|
3535
|
+
completed: [transition.completedPhase],
|
|
3536
|
+
pending: [transition.suggestedNext],
|
|
3537
|
+
concerns: [],
|
|
3538
|
+
decisions: [],
|
|
3539
|
+
blockers: [],
|
|
3540
|
+
contextKeywords: []
|
|
3541
|
+
},
|
|
3542
|
+
validInput.stream,
|
|
3543
|
+
validInput.session
|
|
3544
|
+
);
|
|
3545
|
+
} catch {
|
|
3546
|
+
}
|
|
3547
|
+
await recordInteraction(
|
|
3548
|
+
projectPath,
|
|
3549
|
+
id,
|
|
3550
|
+
"transition",
|
|
3551
|
+
`${transition.completedPhase} -> ${transition.suggestedNext}`,
|
|
3552
|
+
validInput.stream
|
|
3553
|
+
);
|
|
3554
|
+
const responsePayload = { id, prompt, handoffWritten: true };
|
|
3555
|
+
if (!transition.requiresConfirmation) {
|
|
3556
|
+
responsePayload.autoTransition = true;
|
|
3557
|
+
responsePayload.nextAction = `Invoke harness-${transition.suggestedNext} skill now`;
|
|
3558
|
+
}
|
|
3559
|
+
return { content: [{ type: "text", text: JSON.stringify(responsePayload) }] };
|
|
3560
|
+
}
|
|
3561
|
+
async function handleBatch(validInput, projectPath, id) {
|
|
3562
|
+
if (!validInput.batch) return mcpError("Error: batch payload is required when type is batch");
|
|
3563
|
+
const batchResult = InteractionBatchSchema.safeParse(validInput.batch);
|
|
3564
|
+
if (!batchResult.success)
|
|
3565
|
+
return mcpError(
|
|
3566
|
+
`Error: ${batchResult.error.issues.map((i) => i.message).join("; ")}`
|
|
3567
|
+
);
|
|
3568
|
+
const prompt = renderBatch(batchResult.data);
|
|
3569
|
+
await recordInteraction(projectPath, id, "batch", batchResult.data.text, validInput.stream);
|
|
3570
|
+
return {
|
|
3571
|
+
content: [{ type: "text", text: JSON.stringify({ id, prompt, batchMode: true }) }]
|
|
3572
|
+
};
|
|
3573
|
+
}
|
|
3574
|
+
var INTERACTION_HANDLERS = {
|
|
3575
|
+
question: handleQuestion,
|
|
3576
|
+
confirmation: handleConfirmation,
|
|
3577
|
+
transition: handleTransition,
|
|
3578
|
+
batch: handleBatch
|
|
3579
|
+
};
|
|
2975
3580
|
async function handleEmitInteraction(input) {
|
|
2976
3581
|
try {
|
|
2977
3582
|
const parseResult = EmitInteractionInputSchema.safeParse(input);
|
|
2978
|
-
if (!parseResult.success)
|
|
2979
|
-
return {
|
|
2980
|
-
content: [
|
|
2981
|
-
{
|
|
2982
|
-
type: "text",
|
|
2983
|
-
text: `Error: ${parseResult.error.issues.map((i) => i.path.length > 0 ? `${i.path.join(".")}: ${i.message}` : i.message).join("; ")}`
|
|
2984
|
-
}
|
|
2985
|
-
],
|
|
2986
|
-
isError: true
|
|
2987
|
-
};
|
|
2988
|
-
}
|
|
3583
|
+
if (!parseResult.success)
|
|
3584
|
+
return mcpError(`Error: ${formatZodErrors(parseResult.error.issues)}`);
|
|
2989
3585
|
const validInput = parseResult.data;
|
|
2990
3586
|
const projectPath = sanitizePath(validInput.path);
|
|
2991
3587
|
const id = randomUUID();
|
|
2992
|
-
|
|
2993
|
-
|
|
2994
|
-
|
|
2995
|
-
return {
|
|
2996
|
-
content: [
|
|
2997
|
-
{
|
|
2998
|
-
type: "text",
|
|
2999
|
-
text: "Error: question payload is required when type is question"
|
|
3000
|
-
}
|
|
3001
|
-
],
|
|
3002
|
-
isError: true
|
|
3003
|
-
};
|
|
3004
|
-
}
|
|
3005
|
-
const questionResult = InteractionQuestionWithOptionsSchema.safeParse(validInput.question);
|
|
3006
|
-
if (!questionResult.success) {
|
|
3007
|
-
return {
|
|
3008
|
-
content: [
|
|
3009
|
-
{
|
|
3010
|
-
type: "text",
|
|
3011
|
-
text: `Error: ${questionResult.error.issues.map((i) => i.path.length > 0 ? `${i.path.join(".")}: ${i.message}` : i.message).join("; ")}`
|
|
3012
|
-
}
|
|
3013
|
-
],
|
|
3014
|
-
isError: true
|
|
3015
|
-
};
|
|
3016
|
-
}
|
|
3017
|
-
const prompt = renderQuestion(questionResult.data);
|
|
3018
|
-
await recordInteraction(
|
|
3019
|
-
projectPath,
|
|
3020
|
-
id,
|
|
3021
|
-
"question",
|
|
3022
|
-
questionResult.data.text,
|
|
3023
|
-
validInput.stream
|
|
3024
|
-
);
|
|
3025
|
-
return {
|
|
3026
|
-
content: [{ type: "text", text: JSON.stringify({ id, prompt }) }]
|
|
3027
|
-
};
|
|
3028
|
-
}
|
|
3029
|
-
case "confirmation": {
|
|
3030
|
-
if (!validInput.confirmation) {
|
|
3031
|
-
return {
|
|
3032
|
-
content: [
|
|
3033
|
-
{
|
|
3034
|
-
type: "text",
|
|
3035
|
-
text: "Error: confirmation payload is required when type is confirmation"
|
|
3036
|
-
}
|
|
3037
|
-
],
|
|
3038
|
-
isError: true
|
|
3039
|
-
};
|
|
3040
|
-
}
|
|
3041
|
-
const confirmResult = InteractionConfirmationSchema.safeParse(validInput.confirmation);
|
|
3042
|
-
if (!confirmResult.success) {
|
|
3043
|
-
return {
|
|
3044
|
-
content: [
|
|
3045
|
-
{
|
|
3046
|
-
type: "text",
|
|
3047
|
-
text: `Error: ${confirmResult.error.issues.map((i) => i.message).join("; ")}`
|
|
3048
|
-
}
|
|
3049
|
-
],
|
|
3050
|
-
isError: true
|
|
3051
|
-
};
|
|
3052
|
-
}
|
|
3053
|
-
const prompt = renderConfirmation(confirmResult.data);
|
|
3054
|
-
await recordInteraction(
|
|
3055
|
-
projectPath,
|
|
3056
|
-
id,
|
|
3057
|
-
"confirmation",
|
|
3058
|
-
confirmResult.data.text,
|
|
3059
|
-
validInput.stream
|
|
3060
|
-
);
|
|
3061
|
-
return {
|
|
3062
|
-
content: [{ type: "text", text: JSON.stringify({ id, prompt }) }]
|
|
3063
|
-
};
|
|
3064
|
-
}
|
|
3065
|
-
case "transition": {
|
|
3066
|
-
if (!validInput.transition) {
|
|
3067
|
-
return {
|
|
3068
|
-
content: [
|
|
3069
|
-
{
|
|
3070
|
-
type: "text",
|
|
3071
|
-
text: "Error: transition payload is required when type is transition"
|
|
3072
|
-
}
|
|
3073
|
-
],
|
|
3074
|
-
isError: true
|
|
3075
|
-
};
|
|
3076
|
-
}
|
|
3077
|
-
const transitionResult = InteractionTransitionSchema.safeParse(validInput.transition);
|
|
3078
|
-
if (!transitionResult.success) {
|
|
3079
|
-
return {
|
|
3080
|
-
content: [
|
|
3081
|
-
{
|
|
3082
|
-
type: "text",
|
|
3083
|
-
text: `Error: ${transitionResult.error.issues.map((i) => i.message).join("; ")}`
|
|
3084
|
-
}
|
|
3085
|
-
],
|
|
3086
|
-
isError: true
|
|
3087
|
-
};
|
|
3088
|
-
}
|
|
3089
|
-
const transition = transitionResult.data;
|
|
3090
|
-
const prompt = renderTransition(transition);
|
|
3091
|
-
try {
|
|
3092
|
-
const { saveHandoff } = await import("./dist-WF4C7A4A.js");
|
|
3093
|
-
await saveHandoff(
|
|
3094
|
-
projectPath,
|
|
3095
|
-
{
|
|
3096
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3097
|
-
fromSkill: "emit_interaction",
|
|
3098
|
-
phase: transition.completedPhase,
|
|
3099
|
-
summary: transition.reason,
|
|
3100
|
-
completed: [transition.completedPhase],
|
|
3101
|
-
pending: [transition.suggestedNext],
|
|
3102
|
-
concerns: [],
|
|
3103
|
-
decisions: [],
|
|
3104
|
-
blockers: [],
|
|
3105
|
-
contextKeywords: []
|
|
3106
|
-
},
|
|
3107
|
-
validInput.stream,
|
|
3108
|
-
validInput.session
|
|
3109
|
-
);
|
|
3110
|
-
} catch {
|
|
3111
|
-
}
|
|
3112
|
-
await recordInteraction(
|
|
3113
|
-
projectPath,
|
|
3114
|
-
id,
|
|
3115
|
-
"transition",
|
|
3116
|
-
`${transition.completedPhase} -> ${transition.suggestedNext}`,
|
|
3117
|
-
validInput.stream
|
|
3118
|
-
);
|
|
3119
|
-
const responsePayload = { id, prompt, handoffWritten: true };
|
|
3120
|
-
if (!transition.requiresConfirmation) {
|
|
3121
|
-
responsePayload.autoTransition = true;
|
|
3122
|
-
responsePayload.nextAction = `Invoke harness-${transition.suggestedNext} skill now`;
|
|
3123
|
-
}
|
|
3124
|
-
return {
|
|
3125
|
-
content: [
|
|
3126
|
-
{
|
|
3127
|
-
type: "text",
|
|
3128
|
-
text: JSON.stringify(responsePayload)
|
|
3129
|
-
}
|
|
3130
|
-
]
|
|
3131
|
-
};
|
|
3132
|
-
}
|
|
3133
|
-
case "batch": {
|
|
3134
|
-
if (!validInput.batch) {
|
|
3135
|
-
return {
|
|
3136
|
-
content: [
|
|
3137
|
-
{
|
|
3138
|
-
type: "text",
|
|
3139
|
-
text: "Error: batch payload is required when type is batch"
|
|
3140
|
-
}
|
|
3141
|
-
],
|
|
3142
|
-
isError: true
|
|
3143
|
-
};
|
|
3144
|
-
}
|
|
3145
|
-
const batchResult = InteractionBatchSchema.safeParse(validInput.batch);
|
|
3146
|
-
if (!batchResult.success) {
|
|
3147
|
-
return {
|
|
3148
|
-
content: [
|
|
3149
|
-
{
|
|
3150
|
-
type: "text",
|
|
3151
|
-
text: `Error: ${batchResult.error.issues.map((i) => i.message).join("; ")}`
|
|
3152
|
-
}
|
|
3153
|
-
],
|
|
3154
|
-
isError: true
|
|
3155
|
-
};
|
|
3156
|
-
}
|
|
3157
|
-
const prompt = renderBatch(batchResult.data);
|
|
3158
|
-
await recordInteraction(projectPath, id, "batch", batchResult.data.text, validInput.stream);
|
|
3159
|
-
return {
|
|
3160
|
-
content: [
|
|
3161
|
-
{
|
|
3162
|
-
type: "text",
|
|
3163
|
-
text: JSON.stringify({ id, prompt, batchMode: true })
|
|
3164
|
-
}
|
|
3165
|
-
]
|
|
3166
|
-
};
|
|
3167
|
-
}
|
|
3168
|
-
default: {
|
|
3169
|
-
return {
|
|
3170
|
-
content: [
|
|
3171
|
-
{
|
|
3172
|
-
type: "text",
|
|
3173
|
-
text: `Error: unknown interaction type: ${String(validInput.type)}`
|
|
3174
|
-
}
|
|
3175
|
-
],
|
|
3176
|
-
isError: true
|
|
3177
|
-
};
|
|
3178
|
-
}
|
|
3179
|
-
}
|
|
3588
|
+
const handler = INTERACTION_HANDLERS[validInput.type];
|
|
3589
|
+
if (!handler) return mcpError(`Error: unknown interaction type: ${String(validInput.type)}`);
|
|
3590
|
+
return await handler(validInput, projectPath, id);
|
|
3180
3591
|
} catch (error) {
|
|
3181
|
-
return {
|
|
3182
|
-
content: [
|
|
3183
|
-
{
|
|
3184
|
-
type: "text",
|
|
3185
|
-
text: `Error: ${error instanceof Error ? error.message : String(error)}`
|
|
3186
|
-
}
|
|
3187
|
-
],
|
|
3188
|
-
isError: true
|
|
3189
|
-
};
|
|
3592
|
+
return mcpError(`Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
3190
3593
|
}
|
|
3191
3594
|
}
|
|
3192
3595
|
async function recordInteraction(projectPath, id, type, decision, stream) {
|
|
3193
3596
|
try {
|
|
3194
|
-
const { loadState, saveState } = await import("./dist-
|
|
3597
|
+
const { loadState, saveState } = await import("./dist-HWXF2C3R.js");
|
|
3195
3598
|
const stateResult = await loadState(projectPath, stream);
|
|
3196
3599
|
if (stateResult.ok) {
|
|
3197
3600
|
const state = stateResult.value;
|
|
@@ -3209,7 +3612,7 @@ async function recordInteraction(projectPath, id, type, decision, stream) {
|
|
|
3209
3612
|
// src/mcp/tools/gather-context.ts
|
|
3210
3613
|
var gatherContextDefinition = {
|
|
3211
3614
|
name: "gather_context",
|
|
3212
|
-
description: "Assemble all working context an agent needs in a single call: state, learnings, handoff, graph context, and
|
|
3615
|
+
description: "Assemble all working context an agent needs in a single call: state, learnings, handoff, graph context, project validation, and session sections. Runs constituents in parallel.",
|
|
3213
3616
|
inputSchema: {
|
|
3214
3617
|
type: "object",
|
|
3215
3618
|
properties: {
|
|
@@ -3230,7 +3633,7 @@ var gatherContextDefinition = {
|
|
|
3230
3633
|
type: "array",
|
|
3231
3634
|
items: {
|
|
3232
3635
|
type: "string",
|
|
3233
|
-
enum: ["state", "learnings", "handoff", "graph", "validation"]
|
|
3636
|
+
enum: ["state", "learnings", "handoff", "graph", "validation", "sessions"]
|
|
3234
3637
|
},
|
|
3235
3638
|
description: "Which constituents to include (default: all)"
|
|
3236
3639
|
},
|
|
@@ -3271,10 +3674,10 @@ async function handleGatherContext(input) {
|
|
|
3271
3674
|
input.include ?? ["state", "learnings", "handoff", "graph", "validation"]
|
|
3272
3675
|
);
|
|
3273
3676
|
const errors = [];
|
|
3274
|
-
const statePromise = includeSet.has("state") ? import("./dist-
|
|
3677
|
+
const statePromise = includeSet.has("state") ? import("./dist-HWXF2C3R.js").then(
|
|
3275
3678
|
(core) => core.loadState(projectPath, void 0, input.session)
|
|
3276
3679
|
) : Promise.resolve(null);
|
|
3277
|
-
const learningsPromise = includeSet.has("learnings") ? import("./dist-
|
|
3680
|
+
const learningsPromise = includeSet.has("learnings") ? import("./dist-HWXF2C3R.js").then(
|
|
3278
3681
|
(core) => core.loadBudgetedLearnings(projectPath, {
|
|
3279
3682
|
intent: input.intent,
|
|
3280
3683
|
tokenBudget: input.learningsBudget ?? 1e3,
|
|
@@ -3282,14 +3685,14 @@ async function handleGatherContext(input) {
|
|
|
3282
3685
|
...input.session !== void 0 && { session: input.session }
|
|
3283
3686
|
})
|
|
3284
3687
|
) : Promise.resolve(null);
|
|
3285
|
-
const handoffPromise = includeSet.has("handoff") ? import("./dist-
|
|
3688
|
+
const handoffPromise = includeSet.has("handoff") ? import("./dist-HWXF2C3R.js").then(
|
|
3286
3689
|
(core) => core.loadHandoff(projectPath, void 0, input.session)
|
|
3287
3690
|
) : Promise.resolve(null);
|
|
3288
3691
|
const graphPromise = includeSet.has("graph") ? (async () => {
|
|
3289
|
-
const { loadGraphStore: loadGraphStore2 } = await import("./graph-loader-
|
|
3692
|
+
const { loadGraphStore: loadGraphStore2 } = await import("./graph-loader-2M2HXDQI.js");
|
|
3290
3693
|
const store = await loadGraphStore2(projectPath);
|
|
3291
3694
|
if (!store) return null;
|
|
3292
|
-
const { FusionLayer, ContextQL } = await import("./dist-
|
|
3695
|
+
const { FusionLayer, ContextQL } = await import("./dist-B26DFXMP.js");
|
|
3293
3696
|
const fusion = new FusionLayer(store);
|
|
3294
3697
|
const cql = new ContextQL(store);
|
|
3295
3698
|
const tokenBudget = input.tokenBudget ?? 4e3;
|
|
@@ -3326,18 +3729,29 @@ async function handleGatherContext(input) {
|
|
|
3326
3729
|
context: contextBlocks
|
|
3327
3730
|
};
|
|
3328
3731
|
})() : Promise.resolve(null);
|
|
3732
|
+
const sessionsPromise = includeSet.has("sessions") && input.session ? import("./dist-HWXF2C3R.js").then(
|
|
3733
|
+
(core) => core.readSessionSections(projectPath, input.session)
|
|
3734
|
+
) : Promise.resolve(null);
|
|
3329
3735
|
const validationPromise = includeSet.has("validation") ? (async () => {
|
|
3330
|
-
const { handleValidateProject: handleValidateProject2 } = await import("./validate-
|
|
3736
|
+
const { handleValidateProject: handleValidateProject2 } = await import("./validate-FD3Z6VJD.js");
|
|
3331
3737
|
const result = await handleValidateProject2({ path: projectPath });
|
|
3332
3738
|
const first = result.content[0];
|
|
3333
3739
|
return first ? JSON.parse(first.text) : null;
|
|
3334
3740
|
})() : Promise.resolve(null);
|
|
3335
|
-
const [
|
|
3741
|
+
const [
|
|
3742
|
+
stateResult,
|
|
3743
|
+
learningsResult,
|
|
3744
|
+
handoffResult,
|
|
3745
|
+
graphResult,
|
|
3746
|
+
validationResult,
|
|
3747
|
+
sessionsResult
|
|
3748
|
+
] = await Promise.allSettled([
|
|
3336
3749
|
statePromise,
|
|
3337
3750
|
learningsPromise,
|
|
3338
3751
|
handoffPromise,
|
|
3339
3752
|
graphPromise,
|
|
3340
|
-
validationPromise
|
|
3753
|
+
validationPromise,
|
|
3754
|
+
sessionsPromise
|
|
3341
3755
|
]);
|
|
3342
3756
|
function extract(settled, name) {
|
|
3343
3757
|
if (settled.status === "rejected") {
|
|
@@ -3351,6 +3765,7 @@ async function handleGatherContext(input) {
|
|
|
3351
3765
|
const handoffRaw = extract(handoffResult, "handoff");
|
|
3352
3766
|
const graphContextRaw = extract(graphResult, "graph");
|
|
3353
3767
|
const validationRaw = extract(validationResult, "validation");
|
|
3768
|
+
const sessionsRaw = extract(sessionsResult, "sessions");
|
|
3354
3769
|
const state = stateRaw && typeof stateRaw === "object" && "ok" in stateRaw ? stateRaw.ok ? stateRaw.value : (() => {
|
|
3355
3770
|
errors.push(`state: ${stateRaw.error.message}`);
|
|
3356
3771
|
return null;
|
|
@@ -3367,6 +3782,12 @@ async function handleGatherContext(input) {
|
|
|
3367
3782
|
})() : handoffRaw;
|
|
3368
3783
|
const graphContext = graphContextRaw;
|
|
3369
3784
|
const validation = validationRaw;
|
|
3785
|
+
const sessionSections = sessionsRaw && typeof sessionsRaw === "object" && "ok" in sessionsRaw ? sessionsRaw.ok ? sessionsRaw.value : (() => {
|
|
3786
|
+
errors.push(
|
|
3787
|
+
`sessions: ${sessionsRaw.error.message}`
|
|
3788
|
+
);
|
|
3789
|
+
return null;
|
|
3790
|
+
})() : sessionsRaw;
|
|
3370
3791
|
const assembledIn = Date.now() - start;
|
|
3371
3792
|
const mode = input.mode ?? "summary";
|
|
3372
3793
|
const outputState = state ?? null;
|
|
@@ -3391,6 +3812,7 @@ async function handleGatherContext(input) {
|
|
|
3391
3812
|
handoff: outputHandoff,
|
|
3392
3813
|
graphContext: outputGraphContext,
|
|
3393
3814
|
validation: outputValidation,
|
|
3815
|
+
sessionSections: sessionSections ?? null,
|
|
3394
3816
|
meta: {
|
|
3395
3817
|
assembledIn,
|
|
3396
3818
|
graphAvailable: graphContext !== null,
|
|
@@ -3401,7 +3823,7 @@ async function handleGatherContext(input) {
|
|
|
3401
3823
|
};
|
|
3402
3824
|
if (input.session) {
|
|
3403
3825
|
try {
|
|
3404
|
-
const core = await import("./dist-
|
|
3826
|
+
const core = await import("./dist-HWXF2C3R.js");
|
|
3405
3827
|
core.updateSessionIndex(
|
|
3406
3828
|
projectPath,
|
|
3407
3829
|
input.session,
|
|
@@ -3471,7 +3893,7 @@ async function handleAssessProject(input) {
|
|
|
3471
3893
|
let validateResult = null;
|
|
3472
3894
|
if (checksToRun.has("validate")) {
|
|
3473
3895
|
try {
|
|
3474
|
-
const { handleValidateProject: handleValidateProject2 } = await import("./validate-
|
|
3896
|
+
const { handleValidateProject: handleValidateProject2 } = await import("./validate-FD3Z6VJD.js");
|
|
3475
3897
|
const result = await handleValidateProject2({ path: projectPath });
|
|
3476
3898
|
const first = result.content[0];
|
|
3477
3899
|
const parsed = first ? JSON.parse(first.text) : {};
|
|
@@ -3496,7 +3918,7 @@ async function handleAssessProject(input) {
|
|
|
3496
3918
|
parallelChecks.push(
|
|
3497
3919
|
(async () => {
|
|
3498
3920
|
try {
|
|
3499
|
-
const { handleCheckDependencies: handleCheckDependencies2 } = await import("./architecture-
|
|
3921
|
+
const { handleCheckDependencies: handleCheckDependencies2 } = await import("./architecture-JQZYM4US.js");
|
|
3500
3922
|
const result = await handleCheckDependencies2({ path: projectPath });
|
|
3501
3923
|
const first = result.content[0];
|
|
3502
3924
|
const parsed = first ? JSON.parse(first.text) : {};
|
|
@@ -3523,7 +3945,7 @@ async function handleAssessProject(input) {
|
|
|
3523
3945
|
parallelChecks.push(
|
|
3524
3946
|
(async () => {
|
|
3525
3947
|
try {
|
|
3526
|
-
const { handleCheckDocs: handleCheckDocs2 } = await import("./docs-
|
|
3948
|
+
const { handleCheckDocs: handleCheckDocs2 } = await import("./docs-7ECGYMAV.js");
|
|
3527
3949
|
const result = await handleCheckDocs2({ path: projectPath, scope: "coverage" });
|
|
3528
3950
|
const first = result.content[0];
|
|
3529
3951
|
const parsed = first ? JSON.parse(first.text) : {};
|
|
@@ -3550,14 +3972,14 @@ async function handleAssessProject(input) {
|
|
|
3550
3972
|
parallelChecks.push(
|
|
3551
3973
|
(async () => {
|
|
3552
3974
|
try {
|
|
3553
|
-
const { handleDetectEntropy: handleDetectEntropy2 } = await import("./entropy-
|
|
3975
|
+
const { handleDetectEntropy: handleDetectEntropy2 } = await import("./entropy-5USWKLVS.js");
|
|
3554
3976
|
const result = await handleDetectEntropy2({ path: projectPath, type: "all" });
|
|
3555
3977
|
const first = result.content[0];
|
|
3556
3978
|
const parsed = first ? JSON.parse(first.text) : {};
|
|
3557
3979
|
const issues = (parsed.drift?.staleReferences?.length ?? 0) + (parsed.drift?.missingTargets?.length ?? 0) + (parsed.deadCode?.unusedImports?.length ?? 0) + (parsed.deadCode?.unusedExports?.length ?? 0) + (parsed.patterns?.violations?.length ?? 0);
|
|
3558
3980
|
return {
|
|
3559
3981
|
name: "entropy",
|
|
3560
|
-
passed: !result.isError && issues === 0,
|
|
3982
|
+
passed: !("isError" in result && result.isError) && issues === 0,
|
|
3561
3983
|
issueCount: issues,
|
|
3562
3984
|
...issues > 0 ? { topIssue: "Entropy detected -- run detect_entropy for details" } : {},
|
|
3563
3985
|
...mode === "detailed" ? { detailed: parsed } : {}
|
|
@@ -3577,7 +3999,7 @@ async function handleAssessProject(input) {
|
|
|
3577
3999
|
parallelChecks.push(
|
|
3578
4000
|
(async () => {
|
|
3579
4001
|
try {
|
|
3580
|
-
const { handleRunSecurityScan: handleRunSecurityScan2 } = await import("./security-
|
|
4002
|
+
const { handleRunSecurityScan: handleRunSecurityScan2 } = await import("./security-PZOX7AQS.js");
|
|
3581
4003
|
const result = await handleRunSecurityScan2({ path: projectPath });
|
|
3582
4004
|
const first = result.content[0];
|
|
3583
4005
|
const parsed = first ? JSON.parse(first.text) : {};
|
|
@@ -3609,14 +4031,14 @@ async function handleAssessProject(input) {
|
|
|
3609
4031
|
parallelChecks.push(
|
|
3610
4032
|
(async () => {
|
|
3611
4033
|
try {
|
|
3612
|
-
const { handleCheckPerformance: handleCheckPerformance2 } = await import("./performance-
|
|
4034
|
+
const { handleCheckPerformance: handleCheckPerformance2 } = await import("./performance-OQAFMJUD.js");
|
|
3613
4035
|
const result = await handleCheckPerformance2({ path: projectPath });
|
|
3614
4036
|
const first = result.content[0];
|
|
3615
4037
|
const parsed = first ? JSON.parse(first.text) : {};
|
|
3616
4038
|
const issues = parsed.violations?.length ?? parsed.issues?.length ?? 0;
|
|
3617
4039
|
return {
|
|
3618
4040
|
name: "perf",
|
|
3619
|
-
passed: !result.isError && issues === 0,
|
|
4041
|
+
passed: !("isError" in result && result.isError) && issues === 0,
|
|
3620
4042
|
issueCount: issues,
|
|
3621
4043
|
...issues > 0 ? { topIssue: "Performance issues detected" } : {},
|
|
3622
4044
|
...mode === "detailed" ? { detailed: parsed } : {}
|
|
@@ -3783,81 +4205,8 @@ async function handleReviewChanges(input) {
|
|
|
3783
4205
|
downgraded = true;
|
|
3784
4206
|
}
|
|
3785
4207
|
try {
|
|
3786
|
-
|
|
3787
|
-
|
|
3788
|
-
const result2 = await handleAnalyzeDiff2({ diff, path: projectPath });
|
|
3789
|
-
const firstContent = result2.content[0];
|
|
3790
|
-
if (!firstContent) throw new Error("Empty analyze_diff response");
|
|
3791
|
-
const parsed2 = JSON.parse(firstContent.text);
|
|
3792
|
-
return {
|
|
3793
|
-
content: [
|
|
3794
|
-
{
|
|
3795
|
-
type: "text",
|
|
3796
|
-
text: JSON.stringify({
|
|
3797
|
-
depth: "quick",
|
|
3798
|
-
downgraded,
|
|
3799
|
-
findings: parsed2.findings ?? parsed2.warnings ?? [],
|
|
3800
|
-
fileCount: parsed2.summary?.filesChanged ?? parsed2.files?.length ?? 0,
|
|
3801
|
-
lineCount: diffLines,
|
|
3802
|
-
...result2.isError ? { error: parsed2 } : {}
|
|
3803
|
-
})
|
|
3804
|
-
}
|
|
3805
|
-
]
|
|
3806
|
-
};
|
|
3807
|
-
}
|
|
3808
|
-
if (effectiveDepth === "standard") {
|
|
3809
|
-
const { handleAnalyzeDiff: handleAnalyzeDiff2, handleCreateSelfReview: handleCreateSelfReview2 } = await import("./feedback-63QB5RCA.js");
|
|
3810
|
-
const [diffResult, reviewResult] = await Promise.all([
|
|
3811
|
-
handleAnalyzeDiff2({ diff, path: projectPath }),
|
|
3812
|
-
handleCreateSelfReview2({ path: projectPath, diff })
|
|
3813
|
-
]);
|
|
3814
|
-
const diffContent = diffResult.content[0];
|
|
3815
|
-
const reviewContent = reviewResult.content[0];
|
|
3816
|
-
if (!diffContent || !reviewContent) throw new Error("Empty review response");
|
|
3817
|
-
const diffParsed = JSON.parse(diffContent.text);
|
|
3818
|
-
const reviewParsed = JSON.parse(reviewContent.text);
|
|
3819
|
-
const findings = [
|
|
3820
|
-
...diffParsed.findings ?? diffParsed.warnings ?? [],
|
|
3821
|
-
...reviewParsed.findings ?? reviewParsed.items ?? []
|
|
3822
|
-
];
|
|
3823
|
-
return {
|
|
3824
|
-
content: [
|
|
3825
|
-
{
|
|
3826
|
-
type: "text",
|
|
3827
|
-
text: JSON.stringify({
|
|
3828
|
-
depth: "standard",
|
|
3829
|
-
downgraded,
|
|
3830
|
-
findings,
|
|
3831
|
-
diffAnalysis: diffParsed,
|
|
3832
|
-
selfReview: reviewParsed,
|
|
3833
|
-
fileCount: diffParsed.summary?.filesChanged ?? diffParsed.files?.length ?? 0,
|
|
3834
|
-
lineCount: diffLines
|
|
3835
|
-
})
|
|
3836
|
-
}
|
|
3837
|
-
]
|
|
3838
|
-
};
|
|
3839
|
-
}
|
|
3840
|
-
const { handleRunCodeReview: handleRunCodeReview2 } = await import("./review-pipeline-GHR3WFBI.js");
|
|
3841
|
-
const result = await handleRunCodeReview2({ path: projectPath, diff });
|
|
3842
|
-
const deepContent = result.content[0];
|
|
3843
|
-
if (!deepContent) throw new Error("Empty code review response");
|
|
3844
|
-
const parsed = JSON.parse(deepContent.text);
|
|
3845
|
-
return {
|
|
3846
|
-
content: [
|
|
3847
|
-
{
|
|
3848
|
-
type: "text",
|
|
3849
|
-
text: JSON.stringify({
|
|
3850
|
-
depth: "deep",
|
|
3851
|
-
downgraded: false,
|
|
3852
|
-
findings: parsed.findings ?? [],
|
|
3853
|
-
assessment: parsed.assessment,
|
|
3854
|
-
findingCount: parsed.findingCount,
|
|
3855
|
-
lineCount: diffLines,
|
|
3856
|
-
pipeline: parsed
|
|
3857
|
-
})
|
|
3858
|
-
}
|
|
3859
|
-
]
|
|
3860
|
-
};
|
|
4208
|
+
const reviewFn = DEPTH_HANDLERS[effectiveDepth];
|
|
4209
|
+
return await reviewFn(projectPath, diff, diffLines, downgraded);
|
|
3861
4210
|
} catch (error) {
|
|
3862
4211
|
return {
|
|
3863
4212
|
content: [
|
|
@@ -3870,6 +4219,97 @@ async function handleReviewChanges(input) {
|
|
|
3870
4219
|
};
|
|
3871
4220
|
}
|
|
3872
4221
|
}
|
|
4222
|
+
async function runQuickReview(projectPath, diff, diffLines, downgraded) {
|
|
4223
|
+
const { handleAnalyzeDiff: handleAnalyzeDiff2 } = await import("./feedback-UTBXZZHF.js");
|
|
4224
|
+
const result = await handleAnalyzeDiff2({ diff, path: projectPath });
|
|
4225
|
+
const firstContent = result.content[0];
|
|
4226
|
+
if (!firstContent) throw new Error("Empty analyze_diff response");
|
|
4227
|
+
const parsed = JSON.parse(firstContent.text);
|
|
4228
|
+
return {
|
|
4229
|
+
content: [
|
|
4230
|
+
{
|
|
4231
|
+
type: "text",
|
|
4232
|
+
text: JSON.stringify({
|
|
4233
|
+
depth: "quick",
|
|
4234
|
+
downgraded,
|
|
4235
|
+
findings: parsed.findings ?? parsed.warnings ?? [],
|
|
4236
|
+
fileCount: parsed.summary?.filesChanged ?? parsed.files?.length ?? 0,
|
|
4237
|
+
lineCount: diffLines,
|
|
4238
|
+
...result.isError ? { error: parsed } : {}
|
|
4239
|
+
})
|
|
4240
|
+
}
|
|
4241
|
+
]
|
|
4242
|
+
};
|
|
4243
|
+
}
|
|
4244
|
+
function extractFindings(parsed, primaryKey, fallbackKey) {
|
|
4245
|
+
return parsed[primaryKey] ?? parsed[fallbackKey] ?? [];
|
|
4246
|
+
}
|
|
4247
|
+
function extractFileCount(diffParsed) {
|
|
4248
|
+
const summary = diffParsed.summary;
|
|
4249
|
+
if (summary?.filesChanged !== void 0) return summary.filesChanged;
|
|
4250
|
+
const files = diffParsed.files;
|
|
4251
|
+
return files?.length ?? 0;
|
|
4252
|
+
}
|
|
4253
|
+
async function runStandardReview(projectPath, diff, diffLines, downgraded) {
|
|
4254
|
+
const { handleAnalyzeDiff: handleAnalyzeDiff2, handleCreateSelfReview: handleCreateSelfReview2 } = await import("./feedback-UTBXZZHF.js");
|
|
4255
|
+
const [diffResult, reviewResult] = await Promise.all([
|
|
4256
|
+
handleAnalyzeDiff2({ diff, path: projectPath }),
|
|
4257
|
+
handleCreateSelfReview2({ path: projectPath, diff })
|
|
4258
|
+
]);
|
|
4259
|
+
const diffContent = diffResult.content[0];
|
|
4260
|
+
const reviewContent = reviewResult.content[0];
|
|
4261
|
+
if (!diffContent || !reviewContent) throw new Error("Empty review response");
|
|
4262
|
+
const diffParsed = JSON.parse(diffContent.text);
|
|
4263
|
+
const reviewParsed = JSON.parse(reviewContent.text);
|
|
4264
|
+
const findings = [
|
|
4265
|
+
...extractFindings(diffParsed, "findings", "warnings"),
|
|
4266
|
+
...extractFindings(reviewParsed, "findings", "items")
|
|
4267
|
+
];
|
|
4268
|
+
return {
|
|
4269
|
+
content: [
|
|
4270
|
+
{
|
|
4271
|
+
type: "text",
|
|
4272
|
+
text: JSON.stringify({
|
|
4273
|
+
depth: "standard",
|
|
4274
|
+
downgraded,
|
|
4275
|
+
findings,
|
|
4276
|
+
diffAnalysis: diffParsed,
|
|
4277
|
+
selfReview: reviewParsed,
|
|
4278
|
+
fileCount: extractFileCount(diffParsed),
|
|
4279
|
+
lineCount: diffLines
|
|
4280
|
+
})
|
|
4281
|
+
}
|
|
4282
|
+
]
|
|
4283
|
+
};
|
|
4284
|
+
}
|
|
4285
|
+
async function runDeepReview(projectPath, diff, diffLines, _downgraded) {
|
|
4286
|
+
const { handleRunCodeReview: handleRunCodeReview2 } = await import("./review-pipeline-C4GCFVGP.js");
|
|
4287
|
+
const result = await handleRunCodeReview2({ path: projectPath, diff });
|
|
4288
|
+
const deepContent = result.content[0];
|
|
4289
|
+
if (!deepContent) throw new Error("Empty code review response");
|
|
4290
|
+
const parsed = JSON.parse(deepContent.text);
|
|
4291
|
+
return {
|
|
4292
|
+
content: [
|
|
4293
|
+
{
|
|
4294
|
+
type: "text",
|
|
4295
|
+
text: JSON.stringify({
|
|
4296
|
+
depth: "deep",
|
|
4297
|
+
downgraded: false,
|
|
4298
|
+
findings: parsed.findings ?? [],
|
|
4299
|
+
assessment: parsed.assessment,
|
|
4300
|
+
findingCount: parsed.findingCount,
|
|
4301
|
+
lineCount: diffLines,
|
|
4302
|
+
pipeline: parsed
|
|
4303
|
+
})
|
|
4304
|
+
}
|
|
4305
|
+
]
|
|
4306
|
+
};
|
|
4307
|
+
}
|
|
4308
|
+
var DEPTH_HANDLERS = {
|
|
4309
|
+
quick: runQuickReview,
|
|
4310
|
+
standard: runStandardReview,
|
|
4311
|
+
deep: runDeepReview
|
|
4312
|
+
};
|
|
3873
4313
|
|
|
3874
4314
|
// src/mcp/tools/task-independence.ts
|
|
3875
4315
|
var checkTaskIndependenceDefinition = {
|
|
@@ -3914,7 +4354,7 @@ async function handleCheckTaskIndependence(input) {
|
|
|
3914
4354
|
try {
|
|
3915
4355
|
const projectPath = sanitizePath(input.path);
|
|
3916
4356
|
const store = await loadGraphStore(projectPath);
|
|
3917
|
-
const { TaskIndependenceAnalyzer } = await import("./dist-
|
|
4357
|
+
const { TaskIndependenceAnalyzer } = await import("./dist-B26DFXMP.js");
|
|
3918
4358
|
const analyzer = new TaskIndependenceAnalyzer(store ?? void 0);
|
|
3919
4359
|
const result = analyzer.analyze({
|
|
3920
4360
|
tasks: input.tasks,
|
|
@@ -4002,7 +4442,7 @@ async function handlePredictConflicts(input) {
|
|
|
4002
4442
|
try {
|
|
4003
4443
|
const projectPath = sanitizePath(input.path);
|
|
4004
4444
|
const store = await loadGraphStore(projectPath);
|
|
4005
|
-
const { ConflictPredictor } = await import("./dist-
|
|
4445
|
+
const { ConflictPredictor } = await import("./dist-B26DFXMP.js");
|
|
4006
4446
|
const predictor = new ConflictPredictor(store ?? void 0);
|
|
4007
4447
|
const result = predictor.predict({
|
|
4008
4448
|
tasks: input.tasks,
|
|
@@ -4108,7 +4548,7 @@ async function handleDetectStaleConstraints(input) {
|
|
|
4108
4548
|
isError: true
|
|
4109
4549
|
};
|
|
4110
4550
|
}
|
|
4111
|
-
const { loadGraphStore: loadGraphStore2 } = await import("./graph-loader-
|
|
4551
|
+
const { loadGraphStore: loadGraphStore2 } = await import("./graph-loader-2M2HXDQI.js");
|
|
4112
4552
|
const store = await loadGraphStore2(projectPath);
|
|
4113
4553
|
if (!store) {
|
|
4114
4554
|
return {
|
|
@@ -4129,7 +4569,7 @@ async function handleDetectStaleConstraints(input) {
|
|
|
4129
4569
|
]
|
|
4130
4570
|
};
|
|
4131
4571
|
}
|
|
4132
|
-
const { detectStaleConstraints } = await import("./dist-
|
|
4572
|
+
const { detectStaleConstraints } = await import("./dist-HWXF2C3R.js");
|
|
4133
4573
|
const result = detectStaleConstraints(
|
|
4134
4574
|
store,
|
|
4135
4575
|
windowDays,
|
|
@@ -4156,6 +4596,76 @@ async function handleDetectStaleConstraints(input) {
|
|
|
4156
4596
|
}
|
|
4157
4597
|
}
|
|
4158
4598
|
|
|
4599
|
+
// src/mcp/tools/search-skills.ts
|
|
4600
|
+
var searchSkillsDefinition = {
|
|
4601
|
+
name: "search_skills",
|
|
4602
|
+
description: "Search the skill catalog for domain-specific skills. Returns ranked results based on keyword and stack-signal matching. Use this to discover catalog skills that are not loaded as slash commands.",
|
|
4603
|
+
inputSchema: {
|
|
4604
|
+
type: "object",
|
|
4605
|
+
properties: {
|
|
4606
|
+
query: {
|
|
4607
|
+
type: "string",
|
|
4608
|
+
description: "Natural language or keyword query to search for skills"
|
|
4609
|
+
},
|
|
4610
|
+
path: {
|
|
4611
|
+
type: "string",
|
|
4612
|
+
description: "Project root path (defaults to cwd)"
|
|
4613
|
+
},
|
|
4614
|
+
platform: {
|
|
4615
|
+
type: "string",
|
|
4616
|
+
enum: ["claude-code", "gemini-cli"],
|
|
4617
|
+
description: "Target platform (defaults to claude-code)"
|
|
4618
|
+
}
|
|
4619
|
+
},
|
|
4620
|
+
required: ["query"]
|
|
4621
|
+
}
|
|
4622
|
+
};
|
|
4623
|
+
async function handleSearchSkills(input) {
|
|
4624
|
+
const query = input.query;
|
|
4625
|
+
const projectRoot = input.path || process.cwd();
|
|
4626
|
+
const platform = input.platform || "claude-code";
|
|
4627
|
+
const configResult = resolveConfig();
|
|
4628
|
+
const tierOverrides = configResult.ok ? configResult.value.skills?.tierOverrides : void 0;
|
|
4629
|
+
const index = loadOrRebuildIndex(platform, projectRoot, tierOverrides);
|
|
4630
|
+
const profile = loadOrGenerateProfile(projectRoot);
|
|
4631
|
+
const queryTerms = query.toLowerCase().split(/\s+/).filter((t) => t.length > 2);
|
|
4632
|
+
const results = [];
|
|
4633
|
+
for (const [name, entry] of Object.entries(index.skills)) {
|
|
4634
|
+
const matchedKeywords = entry.keywords.filter(
|
|
4635
|
+
(kw) => queryTerms.some(
|
|
4636
|
+
(term) => kw.toLowerCase().includes(term.toLowerCase()) || term.toLowerCase().includes(kw.toLowerCase())
|
|
4637
|
+
)
|
|
4638
|
+
);
|
|
4639
|
+
const keywordScore = queryTerms.length > 0 ? matchedKeywords.length / queryTerms.length : 0;
|
|
4640
|
+
let stackScore = 0;
|
|
4641
|
+
if (entry.stackSignals.length > 0) {
|
|
4642
|
+
const matchedSignals = entry.stackSignals.filter((signal) => profile.signals[signal]);
|
|
4643
|
+
stackScore = matchedSignals.length / entry.stackSignals.length;
|
|
4644
|
+
}
|
|
4645
|
+
const score = 0.6 * keywordScore + 0.4 * stackScore;
|
|
4646
|
+
if (score > 0 || queryTerms.length === 0) {
|
|
4647
|
+
results.push({
|
|
4648
|
+
name,
|
|
4649
|
+
description: entry.description,
|
|
4650
|
+
keywords: entry.keywords,
|
|
4651
|
+
phases: entry.phases,
|
|
4652
|
+
score: Math.round(score * 100) / 100,
|
|
4653
|
+
source: entry.source
|
|
4654
|
+
});
|
|
4655
|
+
}
|
|
4656
|
+
}
|
|
4657
|
+
results.sort((a, b) => b.score - a.score);
|
|
4658
|
+
const top5 = results.slice(0, 5);
|
|
4659
|
+
return {
|
|
4660
|
+
content: [
|
|
4661
|
+
{
|
|
4662
|
+
type: "text",
|
|
4663
|
+
text: JSON.stringify({ results: top5 }, null, 2)
|
|
4664
|
+
}
|
|
4665
|
+
]
|
|
4666
|
+
};
|
|
4667
|
+
}
|
|
4668
|
+
|
|
4159
4669
|
// src/mcp/server.ts
|
|
4160
4670
|
var TOOL_DEFINITIONS = [
|
|
4161
4671
|
validateToolDefinition,
|
|
@@ -4202,7 +4712,8 @@ var TOOL_DEFINITIONS = [
|
|
|
4202
4712
|
askGraphDefinition,
|
|
4203
4713
|
checkTaskIndependenceDefinition,
|
|
4204
4714
|
predictConflictsDefinition,
|
|
4205
|
-
detectStaleConstraintsDefinition
|
|
4715
|
+
detectStaleConstraintsDefinition,
|
|
4716
|
+
searchSkillsDefinition
|
|
4206
4717
|
];
|
|
4207
4718
|
var TOOL_HANDLERS = {
|
|
4208
4719
|
validate_project: handleValidateProject,
|
|
@@ -4249,7 +4760,8 @@ var TOOL_HANDLERS = {
|
|
|
4249
4760
|
ask_graph: handleAskGraph,
|
|
4250
4761
|
check_task_independence: handleCheckTaskIndependence,
|
|
4251
4762
|
predict_conflicts: handlePredictConflicts,
|
|
4252
|
-
detect_stale_constraints: handleDetectStaleConstraints
|
|
4763
|
+
detect_stale_constraints: handleDetectStaleConstraints,
|
|
4764
|
+
search_skills: handleSearchSkills
|
|
4253
4765
|
};
|
|
4254
4766
|
var RESOURCE_DEFINITIONS = [
|
|
4255
4767
|
{
|
|
@@ -4314,6 +4826,45 @@ var RESOURCE_HANDLERS = {
|
|
|
4314
4826
|
function getToolDefinitions() {
|
|
4315
4827
|
return TOOL_DEFINITIONS;
|
|
4316
4828
|
}
|
|
4829
|
+
function readConfigInterval(resolvedRoot) {
|
|
4830
|
+
try {
|
|
4831
|
+
const configResult = resolveProjectConfig(resolvedRoot);
|
|
4832
|
+
if (configResult.ok) {
|
|
4833
|
+
const raw = configResult.value.updateCheckInterval;
|
|
4834
|
+
if (typeof raw === "number" && Number.isInteger(raw) && raw >= 0) {
|
|
4835
|
+
return raw;
|
|
4836
|
+
}
|
|
4837
|
+
}
|
|
4838
|
+
} catch {
|
|
4839
|
+
}
|
|
4840
|
+
return void 0;
|
|
4841
|
+
}
|
|
4842
|
+
async function appendUpdateNotification(result, resolvedRoot) {
|
|
4843
|
+
try {
|
|
4844
|
+
const {
|
|
4845
|
+
getUpdateNotification,
|
|
4846
|
+
isUpdateCheckEnabled,
|
|
4847
|
+
shouldRunCheck,
|
|
4848
|
+
readCheckState,
|
|
4849
|
+
spawnBackgroundCheck
|
|
4850
|
+
} = await import("./dist-HWXF2C3R.js");
|
|
4851
|
+
const { CLI_VERSION } = await import("./version-KFFPOQAX.js");
|
|
4852
|
+
const configInterval = readConfigInterval(resolvedRoot);
|
|
4853
|
+
const DEFAULT_INTERVAL = 864e5;
|
|
4854
|
+
if (!isUpdateCheckEnabled(configInterval)) return;
|
|
4855
|
+
const state = readCheckState();
|
|
4856
|
+
if (shouldRunCheck(state, configInterval ?? DEFAULT_INTERVAL)) {
|
|
4857
|
+
spawnBackgroundCheck(CLI_VERSION);
|
|
4858
|
+
}
|
|
4859
|
+
const notification = getUpdateNotification(CLI_VERSION);
|
|
4860
|
+
if (notification) {
|
|
4861
|
+
result.content.push({ type: "text", text: `
|
|
4862
|
+
---
|
|
4863
|
+
${notification}` });
|
|
4864
|
+
}
|
|
4865
|
+
} catch {
|
|
4866
|
+
}
|
|
4867
|
+
}
|
|
4317
4868
|
function createHarnessServer(projectRoot) {
|
|
4318
4869
|
const resolvedRoot = projectRoot ?? process.cwd();
|
|
4319
4870
|
let sessionChecked = false;
|
|
@@ -4333,42 +4884,7 @@ function createHarnessServer(projectRoot) {
|
|
|
4333
4884
|
const result = await handler(args ?? {});
|
|
4334
4885
|
if (!sessionChecked) {
|
|
4335
4886
|
sessionChecked = true;
|
|
4336
|
-
|
|
4337
|
-
const {
|
|
4338
|
-
getUpdateNotification,
|
|
4339
|
-
isUpdateCheckEnabled,
|
|
4340
|
-
shouldRunCheck,
|
|
4341
|
-
readCheckState,
|
|
4342
|
-
spawnBackgroundCheck
|
|
4343
|
-
} = await import("./dist-WF4C7A4A.js");
|
|
4344
|
-
const { CLI_VERSION: version } = await import("./version-KFFPOQAX.js");
|
|
4345
|
-
let CLI_VERSION = version;
|
|
4346
|
-
let configInterval;
|
|
4347
|
-
try {
|
|
4348
|
-
const configResult = resolveProjectConfig(resolvedRoot);
|
|
4349
|
-
if (configResult.ok) {
|
|
4350
|
-
const raw = configResult.value.updateCheckInterval;
|
|
4351
|
-
if (typeof raw === "number" && Number.isInteger(raw) && raw >= 0) {
|
|
4352
|
-
configInterval = raw;
|
|
4353
|
-
}
|
|
4354
|
-
}
|
|
4355
|
-
} catch {
|
|
4356
|
-
}
|
|
4357
|
-
const DEFAULT_INTERVAL = 864e5;
|
|
4358
|
-
if (isUpdateCheckEnabled(configInterval)) {
|
|
4359
|
-
const state = readCheckState();
|
|
4360
|
-
if (shouldRunCheck(state, configInterval ?? DEFAULT_INTERVAL)) {
|
|
4361
|
-
spawnBackgroundCheck(CLI_VERSION);
|
|
4362
|
-
}
|
|
4363
|
-
const notification = getUpdateNotification(CLI_VERSION);
|
|
4364
|
-
if (notification) {
|
|
4365
|
-
result.content.push({ type: "text", text: `
|
|
4366
|
-
---
|
|
4367
|
-
${notification}` });
|
|
4368
|
-
}
|
|
4369
|
-
}
|
|
4370
|
-
} catch {
|
|
4371
|
-
}
|
|
4887
|
+
await appendUpdateNotification(result, resolvedRoot);
|
|
4372
4888
|
}
|
|
4373
4889
|
return result;
|
|
4374
4890
|
});
|
|
@@ -4397,6 +4913,8 @@ async function startServer() {
|
|
|
4397
4913
|
}
|
|
4398
4914
|
|
|
4399
4915
|
export {
|
|
4916
|
+
persistToolingConfig,
|
|
4917
|
+
appendFrameworkAgents,
|
|
4400
4918
|
generateSlashCommands,
|
|
4401
4919
|
handleOrphanDeletion,
|
|
4402
4920
|
createGenerateSlashCommandsCommand,
|