@harness-engineering/cli 1.12.0 → 1.13.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/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.md +57 -9
- package/dist/agents/skills/claude-code/harness-autopilot/skill.yaml +1 -0
- package/dist/agents/skills/claude-code/harness-brainstorming/SKILL.md +1 -1
- 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 +19 -2
- 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 +39 -12
- 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 +28 -11
- 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.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.md +34 -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 +42 -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.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.md +57 -9
- package/dist/agents/skills/gemini-cli/harness-autopilot/skill.yaml +1 -0
- package/dist/agents/skills/gemini-cli/harness-brainstorming/SKILL.md +1 -1
- 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 +19 -2
- 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 +39 -12
- 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 +28 -11
- 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.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.md +34 -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 +42 -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.yaml +1 -0
- package/dist/agents/skills/gemini-cli/validate-context-engineering/skill.yaml +1 -0
- package/dist/{agents-md-KIS2RSMG.js → agents-md-XU3BHE22.js} +1 -1
- package/dist/{architecture-AJAUDRQQ.js → architecture-2R5Z4ZAF.js} +2 -2
- package/dist/bin/harness-mcp.js +14 -13
- package/dist/bin/harness.js +22 -21
- package/dist/{check-phase-gate-K7QCSYRJ.js → check-phase-gate-2OFZ7OWW.js} +3 -2
- package/dist/{chunk-TJVVU3HB.js → chunk-4ZMOCPYO.js} +1 -1
- package/dist/{chunk-EAURF4LH.js → chunk-65FRIL4D.js} +2 -2
- package/dist/{chunk-L2KLU56K.js → chunk-AOZRDOIP.js} +2 -2
- package/dist/{chunk-JLXOEO5C.js → chunk-DZS7CJKL.js} +4 -4
- package/dist/{chunk-FLOEMHDF.js → chunk-IM32EEDM.js} +9 -9
- package/dist/{chunk-2YPZKGAG.js → chunk-IMFVFNJE.js} +1 -1
- package/dist/{chunk-HD4IBGLA.js → chunk-N5G5QMS3.js} +24 -1
- package/dist/{chunk-CTTFXXKJ.js → chunk-ND6PNADU.js} +23 -9
- package/dist/{chunk-747VBPA4.js → chunk-NERR4TAO.js} +783 -444
- package/dist/{chunk-YXOG2277.js → chunk-NOPU4RZ4.js} +2 -2
- package/dist/{chunk-AE2OWWDH.js → chunk-PQ5YK4AY.js} +870 -504
- package/dist/{chunk-OIGVQF5V.js → chunk-QY4T6YAZ.js} +3 -3
- package/dist/{chunk-B5SBNH4S.js → chunk-SSKDAOX5.js} +93 -30
- package/dist/{chunk-2SWJ4VO7.js → chunk-TKJZKICB.js} +6 -6
- package/dist/{chunk-GNGELAXY.js → chunk-TS3XWPW5.js} +1 -1
- package/dist/chunk-UAX4I5ZE.js +217 -0
- package/dist/{chunk-VRFZWGMS.js → chunk-XYLGHKG6.js} +5 -1
- package/dist/{chunk-6N4R6FVX.js → chunk-YBJ262QL.js} +1 -1
- package/dist/{chunk-ZU2UBYBY.js → chunk-Z77YQRQT.js} +11 -207
- package/dist/{ci-workflow-NBL4OT4A.js → ci-workflow-EHV65NQB.js} +1 -1
- package/dist/{create-skill-WPXHSLX2.js → create-skill-XSWHMSM5.js} +2 -2
- package/dist/{dist-IJ4J4C5G.js → dist-2B363XUH.js} +25 -1
- package/dist/{dist-M6BQODWC.js → dist-HXHWB7SV.js} +2 -2
- package/dist/{docs-CPTMH3VY.js → docs-FZOPM4GK.js} +4 -2
- package/dist/{engine-BUWPAAGD.js → engine-OL4T6NZS.js} +1 -1
- package/dist/{entropy-Z4FYVQ7L.js → entropy-LVHJMFGH.js} +2 -2
- package/dist/{feedback-TT6WF5YX.js → feedback-IHLVLMRD.js} +1 -1
- package/dist/{generate-agent-definitions-J5HANRNR.js → generate-agent-definitions-64S3CLEZ.js} +3 -3
- package/dist/{glob-helper-5OHBUQAI.js → glob-helper-R5FXNUPS.js} +1 -1
- package/dist/{graph-loader-KO4GJ5N2.js → graph-loader-GJZ4FN4Y.js} +1 -1
- package/dist/index.d.ts +60 -33
- package/dist/index.js +23 -21
- package/dist/{loader-PCU5YWRH.js → loader-DPYFB6R6.js} +1 -1
- package/dist/{mcp-YM6QLHLZ.js → mcp-JQUI7BVZ.js} +14 -13
- package/dist/{performance-YJVXOKIB.js → performance-ZTVSUANN.js} +2 -2
- package/dist/{review-pipeline-KGMIMLIE.js → review-pipeline-76JHKGSV.js} +1 -1
- package/dist/{runtime-F6R27LD6.js → runtime-X7U6SC7K.js} +1 -1
- package/dist/{security-MX5VVXBC.js → security-FWQZF2IZ.js} +1 -1
- package/dist/skill-executor-XZLYZYAK.js +8 -0
- package/dist/{validate-EFNMSFKD.js → validate-GCHZJIL7.js} +2 -2
- package/dist/{validate-cross-check-LJX65SBS.js → validate-cross-check-STFHYMAZ.js} +1 -1
- package/package.json +4 -4
- package/dist/skill-executor-RG45LUO5.js +0 -8
|
@@ -3,27 +3,23 @@ import {
|
|
|
3
3
|
Ok
|
|
4
4
|
} from "./chunk-MHBMTPW7.js";
|
|
5
5
|
|
|
6
|
-
// ../core/dist/chunk-
|
|
6
|
+
// ../core/dist/chunk-D6VFA6AS.mjs
|
|
7
7
|
import { z } from "zod";
|
|
8
|
-
import { relative as relative2 } from "path";
|
|
9
8
|
import { createHash } from "crypto";
|
|
10
9
|
import { minimatch } from "minimatch";
|
|
11
10
|
import { access, constants, readFile } from "fs";
|
|
12
11
|
import { promisify } from "util";
|
|
12
|
+
import { relative } from "path";
|
|
13
13
|
import { glob } from "glob";
|
|
14
|
-
import { dirname, resolve
|
|
15
|
-
import { relative as relative3 } from "path";
|
|
14
|
+
import { dirname, resolve } from "path";
|
|
16
15
|
import { readFileSync, writeFileSync, renameSync, mkdirSync, existsSync } from "fs";
|
|
17
16
|
import { randomBytes } from "crypto";
|
|
18
17
|
import { join, dirname as dirname2 } from "path";
|
|
19
|
-
import { relative as relative4 } from "path";
|
|
20
18
|
import { readFile as readFile2 } from "fs/promises";
|
|
21
|
-
import { relative as relative5 } from "path";
|
|
22
|
-
import { relative as relative6 } from "path";
|
|
23
19
|
import { readFile as readFile3, readdir } from "fs/promises";
|
|
24
|
-
import { join as join2
|
|
20
|
+
import { join as join2 } from "path";
|
|
25
21
|
import { readFile as readFile4, readdir as readdir2 } from "fs/promises";
|
|
26
|
-
import { join as join3,
|
|
22
|
+
import { join as join3, dirname as dirname3, resolve as resolve2 } from "path";
|
|
27
23
|
var ArchMetricCategorySchema = z.enum([
|
|
28
24
|
"circular-deps",
|
|
29
25
|
"layer-violations",
|
|
@@ -106,8 +102,7 @@ var ConstraintRuleSchema = z.object({
|
|
|
106
102
|
// forward-compat for governs edges
|
|
107
103
|
});
|
|
108
104
|
function violationId(relativePath, category, normalizedDetail) {
|
|
109
|
-
const
|
|
110
|
-
const input = `${path13}:${category}:${normalizedDetail}`;
|
|
105
|
+
const input = `${relativePath}:${category}:${normalizedDetail}`;
|
|
111
106
|
return createHash("sha256").update(input).digest("hex");
|
|
112
107
|
}
|
|
113
108
|
function constraintRuleId(category, scope, description) {
|
|
@@ -139,17 +134,17 @@ function resolveFileToLayer(file, layers) {
|
|
|
139
134
|
}
|
|
140
135
|
var accessAsync = promisify(access);
|
|
141
136
|
var readFileAsync = promisify(readFile);
|
|
142
|
-
async function fileExists(
|
|
137
|
+
async function fileExists(path20) {
|
|
143
138
|
try {
|
|
144
|
-
await accessAsync(
|
|
139
|
+
await accessAsync(path20, constants.F_OK);
|
|
145
140
|
return true;
|
|
146
141
|
} catch {
|
|
147
142
|
return false;
|
|
148
143
|
}
|
|
149
144
|
}
|
|
150
|
-
async function readFileContent(
|
|
145
|
+
async function readFileContent(path20) {
|
|
151
146
|
try {
|
|
152
|
-
const content = await readFileAsync(
|
|
147
|
+
const content = await readFileAsync(path20, "utf-8");
|
|
153
148
|
return Ok(content);
|
|
154
149
|
} catch (error) {
|
|
155
150
|
return Err(error);
|
|
@@ -158,6 +153,9 @@ async function readFileContent(path13) {
|
|
|
158
153
|
async function findFiles(pattern, cwd = process.cwd()) {
|
|
159
154
|
return glob(pattern, { cwd, absolute: true });
|
|
160
155
|
}
|
|
156
|
+
function relativePosix(from, to) {
|
|
157
|
+
return relative(from, to).replaceAll("\\", "/");
|
|
158
|
+
}
|
|
161
159
|
function resolveImportPath(importSource, fromFile, _rootDir) {
|
|
162
160
|
if (!importSource.startsWith(".") && !importSource.startsWith("/")) {
|
|
163
161
|
return null;
|
|
@@ -209,8 +207,8 @@ async function buildDependencyGraph(files, parser, graphDependencyData) {
|
|
|
209
207
|
function checkLayerViolations(graph, layers, rootDir) {
|
|
210
208
|
const violations = [];
|
|
211
209
|
for (const edge of graph.edges) {
|
|
212
|
-
const fromRelative =
|
|
213
|
-
const toRelative =
|
|
210
|
+
const fromRelative = relativePosix(rootDir, edge.from);
|
|
211
|
+
const toRelative = relativePosix(rootDir, edge.to);
|
|
214
212
|
const fromLayer = resolveFileToLayer(fromRelative, layers);
|
|
215
213
|
const toLayer = resolveFileToLayer(toRelative, layers);
|
|
216
214
|
if (!fromLayer || !toLayer) continue;
|
|
@@ -436,8 +434,8 @@ var CircularDepsCollector = class {
|
|
|
436
434
|
}
|
|
437
435
|
const { cycles, largestCycle } = result.value;
|
|
438
436
|
const violations = cycles.map((cycle) => {
|
|
439
|
-
const cyclePath = cycle.cycle.map((f) =>
|
|
440
|
-
const firstFile =
|
|
437
|
+
const cyclePath = cycle.cycle.map((f) => relativePosix(rootDir, f)).join(" -> ");
|
|
438
|
+
const firstFile = relativePosix(rootDir, cycle.cycle[0]);
|
|
441
439
|
return {
|
|
442
440
|
id: violationId(firstFile, this.category, cyclePath),
|
|
443
441
|
file: firstFile,
|
|
@@ -499,8 +497,8 @@ var LayerViolationCollector = class {
|
|
|
499
497
|
(v) => v.reason === "WRONG_LAYER"
|
|
500
498
|
);
|
|
501
499
|
const violations = layerViolations.map((v) => {
|
|
502
|
-
const relFile =
|
|
503
|
-
const relImport =
|
|
500
|
+
const relFile = relativePosix(rootDir, v.file);
|
|
501
|
+
const relImport = relativePosix(rootDir, v.imports);
|
|
504
502
|
const detail = `${v.fromLayer} -> ${v.toLayer}: ${relFile} imports ${relImport}`;
|
|
505
503
|
return {
|
|
506
504
|
id: violationId(relFile, this.category, detail),
|
|
@@ -980,7 +978,7 @@ var ComplexityCollector = class {
|
|
|
980
978
|
(v) => v.severity === "error" || v.severity === "warning"
|
|
981
979
|
);
|
|
982
980
|
const violations = filtered.map((v) => {
|
|
983
|
-
const relFile =
|
|
981
|
+
const relFile = relativePosix(rootDir, v.file);
|
|
984
982
|
const idDetail = `${v.metric}:${v.function}`;
|
|
985
983
|
return {
|
|
986
984
|
id: violationId(relFile, this.category, idDetail),
|
|
@@ -1202,7 +1200,7 @@ var CouplingCollector = class {
|
|
|
1202
1200
|
(v) => v.severity === "error" || v.severity === "warning"
|
|
1203
1201
|
);
|
|
1204
1202
|
const violations = filtered.map((v) => {
|
|
1205
|
-
const relFile =
|
|
1203
|
+
const relFile = relativePosix(rootDir, v.file);
|
|
1206
1204
|
const idDetail = `${v.metric}`;
|
|
1207
1205
|
return {
|
|
1208
1206
|
id: violationId(relFile, this.category, idDetail),
|
|
@@ -1266,8 +1264,8 @@ var ForbiddenImportCollector = class {
|
|
|
1266
1264
|
(v) => v.reason === "FORBIDDEN_IMPORT"
|
|
1267
1265
|
);
|
|
1268
1266
|
const violations = forbidden.map((v) => {
|
|
1269
|
-
const relFile =
|
|
1270
|
-
const relImport =
|
|
1267
|
+
const relFile = relativePosix(rootDir, v.file);
|
|
1268
|
+
const relImport = relativePosix(rootDir, v.imports);
|
|
1271
1269
|
const detail = `forbidden import: ${relFile} -> ${relImport}`;
|
|
1272
1270
|
return {
|
|
1273
1271
|
id: violationId(relFile, this.category, detail),
|
|
@@ -1319,10 +1317,10 @@ async function discoverModules(rootDir) {
|
|
|
1319
1317
|
}
|
|
1320
1318
|
}
|
|
1321
1319
|
modules.push({
|
|
1322
|
-
modulePath:
|
|
1320
|
+
modulePath: relativePosix(rootDir, dir),
|
|
1323
1321
|
fileCount: tsFiles.length,
|
|
1324
1322
|
totalLoc,
|
|
1325
|
-
files: tsFiles.map((f) =>
|
|
1323
|
+
files: tsFiles.map((f) => relativePosix(rootDir, f))
|
|
1326
1324
|
});
|
|
1327
1325
|
}
|
|
1328
1326
|
for (const sub of subdirs) {
|
|
@@ -1494,7 +1492,7 @@ var DepDepthCollector = class {
|
|
|
1494
1492
|
}
|
|
1495
1493
|
const moduleMap = /* @__PURE__ */ new Map();
|
|
1496
1494
|
for (const file of allFiles) {
|
|
1497
|
-
const relDir =
|
|
1495
|
+
const relDir = relativePosix(rootDir, dirname3(file));
|
|
1498
1496
|
if (!moduleMap.has(relDir)) moduleMap.set(relDir, []);
|
|
1499
1497
|
moduleMap.get(relDir).push(file);
|
|
1500
1498
|
}
|
|
@@ -1743,19 +1741,18 @@ var archMatchers = {
|
|
|
1743
1741
|
// ../core/dist/index.mjs
|
|
1744
1742
|
import { join as join4, dirname as dirname4 } from "path";
|
|
1745
1743
|
import { minimatch as minimatch2 } from "minimatch";
|
|
1746
|
-
import { basename
|
|
1747
|
-
import { join as join22, basename as basename2
|
|
1748
|
-
import {
|
|
1744
|
+
import { basename } from "path";
|
|
1745
|
+
import { join as join22, basename as basename2 } from "path";
|
|
1746
|
+
import { basename as basename3, dirname as dirname22 } from "path";
|
|
1749
1747
|
import { z as z2 } from "zod";
|
|
1750
1748
|
import * as fs from "fs/promises";
|
|
1751
1749
|
import * as fs2 from "fs/promises";
|
|
1752
1750
|
import { parse } from "@typescript-eslint/typescript-estree";
|
|
1753
|
-
import { join as join32, resolve as resolve3
|
|
1751
|
+
import { join as join32, resolve as resolve3 } from "path";
|
|
1754
1752
|
import { minimatch as minimatch22 } from "minimatch";
|
|
1755
1753
|
import { dirname as dirname32, resolve as resolve22 } from "path";
|
|
1756
1754
|
import { dirname as dirname42, resolve as resolve32 } from "path";
|
|
1757
1755
|
import { minimatch as minimatch3 } from "minimatch";
|
|
1758
|
-
import { relative as relative52 } from "path";
|
|
1759
1756
|
import { readdirSync, statSync } from "fs";
|
|
1760
1757
|
import { join as join42 } from "path";
|
|
1761
1758
|
import * as fs3 from "fs";
|
|
@@ -1770,31 +1767,45 @@ import * as path from "path";
|
|
|
1770
1767
|
import { appendFileSync, writeFileSync as writeFileSync22, existsSync as existsSync22, mkdirSync as mkdirSync22 } from "fs";
|
|
1771
1768
|
import { dirname as dirname7 } from "path";
|
|
1772
1769
|
import { z as z3 } from "zod";
|
|
1773
|
-
import * as
|
|
1774
|
-
import * as
|
|
1775
|
-
import
|
|
1770
|
+
import * as fs8 from "fs";
|
|
1771
|
+
import * as path5 from "path";
|
|
1772
|
+
import * as fs7 from "fs";
|
|
1773
|
+
import * as path4 from "path";
|
|
1776
1774
|
import * as fs5 from "fs";
|
|
1777
1775
|
import * as path2 from "path";
|
|
1778
1776
|
import { execSync } from "child_process";
|
|
1779
1777
|
import { z as z4 } from "zod";
|
|
1780
|
-
import * as
|
|
1781
|
-
import
|
|
1782
|
-
import * as
|
|
1783
|
-
import * as path4 from "path";
|
|
1784
|
-
import * as path5 from "path";
|
|
1778
|
+
import * as fs6 from "fs";
|
|
1779
|
+
import * as path3 from "path";
|
|
1780
|
+
import * as fs9 from "fs";
|
|
1785
1781
|
import * as path6 from "path";
|
|
1782
|
+
import * as fs10 from "fs";
|
|
1786
1783
|
import * as path7 from "path";
|
|
1784
|
+
import * as fs11 from "fs";
|
|
1787
1785
|
import * as path8 from "path";
|
|
1788
|
-
import * as
|
|
1786
|
+
import * as fs12 from "fs";
|
|
1789
1787
|
import * as path9 from "path";
|
|
1790
|
-
import {
|
|
1791
|
-
import * as
|
|
1788
|
+
import { execSync as execSync2 } from "child_process";
|
|
1789
|
+
import * as fs13 from "fs";
|
|
1792
1790
|
import * as path10 from "path";
|
|
1793
|
-
import * as
|
|
1791
|
+
import * as fs15 from "fs/promises";
|
|
1792
|
+
import { z as z5 } from "zod";
|
|
1793
|
+
import * as fs14 from "fs";
|
|
1794
1794
|
import * as path11 from "path";
|
|
1795
|
-
import * as ejs from "ejs";
|
|
1796
|
-
import * as fs12 from "fs";
|
|
1797
1795
|
import * as path12 from "path";
|
|
1796
|
+
import * as path13 from "path";
|
|
1797
|
+
import * as path14 from "path";
|
|
1798
|
+
import * as path15 from "path";
|
|
1799
|
+
import * as fs16 from "fs";
|
|
1800
|
+
import * as path16 from "path";
|
|
1801
|
+
import { z as z6 } from "zod";
|
|
1802
|
+
import * as fs17 from "fs/promises";
|
|
1803
|
+
import * as path17 from "path";
|
|
1804
|
+
import * as fs18 from "fs/promises";
|
|
1805
|
+
import * as path18 from "path";
|
|
1806
|
+
import * as ejs from "ejs";
|
|
1807
|
+
import * as fs19 from "fs";
|
|
1808
|
+
import * as path19 from "path";
|
|
1798
1809
|
import * as os from "os";
|
|
1799
1810
|
import { spawn } from "child_process";
|
|
1800
1811
|
async function validateFileStructure(projectPath, conventions) {
|
|
@@ -1832,15 +1843,15 @@ function validateConfig(data, schema) {
|
|
|
1832
1843
|
let message = "Configuration validation failed";
|
|
1833
1844
|
const suggestions = [];
|
|
1834
1845
|
if (firstError) {
|
|
1835
|
-
const
|
|
1836
|
-
const pathDisplay =
|
|
1846
|
+
const path20 = firstError.path.join(".");
|
|
1847
|
+
const pathDisplay = path20 ? ` at "${path20}"` : "";
|
|
1837
1848
|
if (firstError.code === "invalid_type") {
|
|
1838
1849
|
const received = firstError.received;
|
|
1839
1850
|
const expected = firstError.expected;
|
|
1840
1851
|
if (received === "undefined") {
|
|
1841
1852
|
code = "MISSING_FIELD";
|
|
1842
1853
|
message = `Missing required field${pathDisplay}: ${firstError.message}`;
|
|
1843
|
-
suggestions.push(`Field "${
|
|
1854
|
+
suggestions.push(`Field "${path20}" is required and must be of type "${expected}"`);
|
|
1844
1855
|
} else {
|
|
1845
1856
|
code = "INVALID_TYPE";
|
|
1846
1857
|
message = `Invalid type${pathDisplay}: ${firstError.message}`;
|
|
@@ -2046,30 +2057,27 @@ function extractSections(content) {
|
|
|
2046
2057
|
return result;
|
|
2047
2058
|
});
|
|
2048
2059
|
}
|
|
2049
|
-
function isExternalLink(
|
|
2050
|
-
return
|
|
2060
|
+
function isExternalLink(path20) {
|
|
2061
|
+
return path20.startsWith("http://") || path20.startsWith("https://") || path20.startsWith("#") || path20.startsWith("mailto:");
|
|
2051
2062
|
}
|
|
2052
2063
|
function resolveLinkPath(linkPath, baseDir) {
|
|
2053
2064
|
return linkPath.startsWith(".") ? join4(baseDir, linkPath) : linkPath;
|
|
2054
2065
|
}
|
|
2055
|
-
async function validateAgentsMap(
|
|
2056
|
-
|
|
2057
|
-
"[harness] validateAgentsMap() is deprecated. Use graph-based validation via Assembler.checkCoverage() from @harness-engineering/graph"
|
|
2058
|
-
);
|
|
2059
|
-
const contentResult = await readFileContent(path13);
|
|
2066
|
+
async function validateAgentsMap(path20 = "./AGENTS.md") {
|
|
2067
|
+
const contentResult = await readFileContent(path20);
|
|
2060
2068
|
if (!contentResult.ok) {
|
|
2061
2069
|
return Err(
|
|
2062
2070
|
createError(
|
|
2063
2071
|
"PARSE_ERROR",
|
|
2064
2072
|
`Failed to read AGENTS.md: ${contentResult.error.message}`,
|
|
2065
|
-
{ path:
|
|
2073
|
+
{ path: path20 },
|
|
2066
2074
|
["Ensure the file exists", "Check file permissions"]
|
|
2067
2075
|
)
|
|
2068
2076
|
);
|
|
2069
2077
|
}
|
|
2070
2078
|
const content = contentResult.value;
|
|
2071
2079
|
const sections = extractSections(content);
|
|
2072
|
-
const baseDir = dirname4(
|
|
2080
|
+
const baseDir = dirname4(path20);
|
|
2073
2081
|
const sectionTitles = sections.map((s) => s.title);
|
|
2074
2082
|
const missingSections = REQUIRED_SECTIONS.filter(
|
|
2075
2083
|
(required) => !sectionTitles.some((title) => title.toLowerCase().includes(required.toLowerCase()))
|
|
@@ -2146,7 +2154,7 @@ async function checkDocCoverage(domain, options = {}) {
|
|
|
2146
2154
|
try {
|
|
2147
2155
|
const sourceFiles = await findFiles("**/*.{ts,js,tsx,jsx}", sourceDir);
|
|
2148
2156
|
const filteredSourceFiles = sourceFiles.filter((file) => {
|
|
2149
|
-
const relativePath =
|
|
2157
|
+
const relativePath = relativePosix(sourceDir, file);
|
|
2150
2158
|
return !excludePatterns.some((pattern) => {
|
|
2151
2159
|
return minimatch2(relativePath, pattern, { dot: true }) || minimatch2(file, pattern, { dot: true });
|
|
2152
2160
|
});
|
|
@@ -2169,7 +2177,7 @@ async function checkDocCoverage(domain, options = {}) {
|
|
|
2169
2177
|
const undocumented = [];
|
|
2170
2178
|
const gaps = [];
|
|
2171
2179
|
for (const sourceFile of filteredSourceFiles) {
|
|
2172
|
-
const relativePath =
|
|
2180
|
+
const relativePath = relativePosix(sourceDir, sourceFile);
|
|
2173
2181
|
const fileName = basename(sourceFile);
|
|
2174
2182
|
const isDocumented = documentedPaths.has(relativePath) || documentedPaths.has(fileName) || documentedPaths.has(`src/${relativePath}`);
|
|
2175
2183
|
if (isDocumented) {
|
|
@@ -2203,8 +2211,8 @@ async function checkDocCoverage(domain, options = {}) {
|
|
|
2203
2211
|
);
|
|
2204
2212
|
}
|
|
2205
2213
|
}
|
|
2206
|
-
function suggestFix(
|
|
2207
|
-
const targetName = basename2(
|
|
2214
|
+
function suggestFix(path20, existingFiles) {
|
|
2215
|
+
const targetName = basename2(path20).toLowerCase();
|
|
2208
2216
|
const similar = existingFiles.find((file) => {
|
|
2209
2217
|
const fileName = basename2(file).toLowerCase();
|
|
2210
2218
|
return fileName.includes(targetName) || targetName.includes(fileName);
|
|
@@ -2212,12 +2220,9 @@ function suggestFix(path13, existingFiles) {
|
|
|
2212
2220
|
if (similar) {
|
|
2213
2221
|
return `Did you mean "${similar}"?`;
|
|
2214
2222
|
}
|
|
2215
|
-
return `Create the file "${
|
|
2223
|
+
return `Create the file "${path20}" or remove the link`;
|
|
2216
2224
|
}
|
|
2217
2225
|
async function validateKnowledgeMap(rootDir = process.cwd()) {
|
|
2218
|
-
console.warn(
|
|
2219
|
-
"[harness] validateKnowledgeMap() is deprecated. Use graph-based validation via Assembler.checkCoverage() from @harness-engineering/graph"
|
|
2220
|
-
);
|
|
2221
2226
|
const agentsPath = join22(rootDir, "AGENTS.md");
|
|
2222
2227
|
const agentsResult = await validateAgentsMap(agentsPath);
|
|
2223
2228
|
if (!agentsResult.ok) {
|
|
@@ -2229,7 +2234,7 @@ async function validateKnowledgeMap(rootDir = process.cwd()) {
|
|
|
2229
2234
|
totalLinks: agentsTotalLinks
|
|
2230
2235
|
} = agentsResult.value;
|
|
2231
2236
|
const existingFiles = await findFiles("**/*", rootDir);
|
|
2232
|
-
const relativeExistingFiles = existingFiles.map((f) =>
|
|
2237
|
+
const relativeExistingFiles = existingFiles.map((f) => relativePosix(rootDir, f));
|
|
2233
2238
|
const brokenLinks = agentsBrokenLinks.map((link) => {
|
|
2234
2239
|
const section = sections.find(
|
|
2235
2240
|
(s) => s.links.some((l) => l.path === link.path && l.line === link.line)
|
|
@@ -2267,7 +2272,7 @@ var DEFAULT_SECTIONS = [
|
|
|
2267
2272
|
function groupByDirectory(files, rootDir) {
|
|
2268
2273
|
const groups = /* @__PURE__ */ new Map();
|
|
2269
2274
|
for (const file of files) {
|
|
2270
|
-
const relativePath =
|
|
2275
|
+
const relativePath = relativePosix(rootDir, file);
|
|
2271
2276
|
const dir = dirname22(relativePath);
|
|
2272
2277
|
if (!groups.has(dir)) {
|
|
2273
2278
|
groups.set(dir, []);
|
|
@@ -2323,7 +2328,7 @@ async function generateAgentsMap(config, graphSections) {
|
|
|
2323
2328
|
allFiles.push(...files);
|
|
2324
2329
|
}
|
|
2325
2330
|
const filteredFiles = allFiles.filter((file) => {
|
|
2326
|
-
const relativePath =
|
|
2331
|
+
const relativePath = relativePosix(rootDir, file);
|
|
2327
2332
|
return !matchesExcludePattern(relativePath, excludePaths);
|
|
2328
2333
|
});
|
|
2329
2334
|
lines.push("## Repository Structure");
|
|
@@ -2351,11 +2356,11 @@ async function generateAgentsMap(config, graphSections) {
|
|
|
2351
2356
|
}
|
|
2352
2357
|
const sectionFiles = await findFiles(section.pattern, rootDir);
|
|
2353
2358
|
const filteredSectionFiles = sectionFiles.filter((file) => {
|
|
2354
|
-
const relativePath =
|
|
2359
|
+
const relativePath = relativePosix(rootDir, file);
|
|
2355
2360
|
return !matchesExcludePattern(relativePath, excludePaths);
|
|
2356
2361
|
});
|
|
2357
2362
|
for (const file of filteredSectionFiles.slice(0, 20)) {
|
|
2358
|
-
lines.push(formatFileLink(
|
|
2363
|
+
lines.push(formatFileLink(relativePosix(rootDir, file)));
|
|
2359
2364
|
}
|
|
2360
2365
|
if (filteredSectionFiles.length > 20) {
|
|
2361
2366
|
lines.push(`- _... and ${filteredSectionFiles.length - 20} more files_`);
|
|
@@ -2558,8 +2563,8 @@ function createBoundaryValidator(schema, name) {
|
|
|
2558
2563
|
return Ok(result.data);
|
|
2559
2564
|
}
|
|
2560
2565
|
const suggestions = result.error.issues.map((issue) => {
|
|
2561
|
-
const
|
|
2562
|
-
return
|
|
2566
|
+
const path20 = issue.path.join(".");
|
|
2567
|
+
return path20 ? `${path20}: ${issue.message}` : issue.message;
|
|
2563
2568
|
});
|
|
2564
2569
|
return Err(
|
|
2565
2570
|
createError(
|
|
@@ -3089,11 +3094,11 @@ function walk(node, visitor) {
|
|
|
3089
3094
|
var TypeScriptParser = class {
|
|
3090
3095
|
name = "typescript";
|
|
3091
3096
|
extensions = [".ts", ".tsx", ".mts", ".cts"];
|
|
3092
|
-
async parseFile(
|
|
3093
|
-
const contentResult = await readFileContent(
|
|
3097
|
+
async parseFile(path20) {
|
|
3098
|
+
const contentResult = await readFileContent(path20);
|
|
3094
3099
|
if (!contentResult.ok) {
|
|
3095
3100
|
return Err(
|
|
3096
|
-
createParseError("NOT_FOUND", `File not found: ${
|
|
3101
|
+
createParseError("NOT_FOUND", `File not found: ${path20}`, { path: path20 }, [
|
|
3097
3102
|
"Check that the file exists",
|
|
3098
3103
|
"Verify the path is correct"
|
|
3099
3104
|
])
|
|
@@ -3103,7 +3108,7 @@ var TypeScriptParser = class {
|
|
|
3103
3108
|
const ast = parse(contentResult.value, {
|
|
3104
3109
|
loc: true,
|
|
3105
3110
|
range: true,
|
|
3106
|
-
jsx:
|
|
3111
|
+
jsx: path20.endsWith(".tsx"),
|
|
3107
3112
|
errorOnUnknownASTType: false
|
|
3108
3113
|
});
|
|
3109
3114
|
return Ok({
|
|
@@ -3114,7 +3119,7 @@ var TypeScriptParser = class {
|
|
|
3114
3119
|
} catch (e) {
|
|
3115
3120
|
const error = e;
|
|
3116
3121
|
return Err(
|
|
3117
|
-
createParseError("SYNTAX_ERROR", `Failed to parse ${
|
|
3122
|
+
createParseError("SYNTAX_ERROR", `Failed to parse ${path20}: ${error.message}`, { path: path20 }, [
|
|
3118
3123
|
"Check for syntax errors in the file",
|
|
3119
3124
|
"Ensure valid TypeScript syntax"
|
|
3120
3125
|
])
|
|
@@ -3394,22 +3399,22 @@ function extractInlineRefs(content) {
|
|
|
3394
3399
|
}
|
|
3395
3400
|
return refs;
|
|
3396
3401
|
}
|
|
3397
|
-
async function parseDocumentationFile(
|
|
3398
|
-
const contentResult = await readFileContent(
|
|
3402
|
+
async function parseDocumentationFile(path20) {
|
|
3403
|
+
const contentResult = await readFileContent(path20);
|
|
3399
3404
|
if (!contentResult.ok) {
|
|
3400
3405
|
return Err(
|
|
3401
3406
|
createEntropyError(
|
|
3402
3407
|
"PARSE_ERROR",
|
|
3403
|
-
`Failed to read documentation file: ${
|
|
3404
|
-
{ file:
|
|
3408
|
+
`Failed to read documentation file: ${path20}`,
|
|
3409
|
+
{ file: path20 },
|
|
3405
3410
|
["Check that the file exists"]
|
|
3406
3411
|
)
|
|
3407
3412
|
);
|
|
3408
3413
|
}
|
|
3409
3414
|
const content = contentResult.value;
|
|
3410
|
-
const type =
|
|
3415
|
+
const type = path20.endsWith(".md") ? "markdown" : "text";
|
|
3411
3416
|
return Ok({
|
|
3412
|
-
path:
|
|
3417
|
+
path: path20,
|
|
3413
3418
|
type,
|
|
3414
3419
|
content,
|
|
3415
3420
|
codeBlocks: extractCodeBlocks(content),
|
|
@@ -3540,7 +3545,7 @@ async function buildSnapshot(config) {
|
|
|
3540
3545
|
sourceFilePaths.push(...files2);
|
|
3541
3546
|
}
|
|
3542
3547
|
sourceFilePaths = sourceFilePaths.filter((f) => {
|
|
3543
|
-
const rel =
|
|
3548
|
+
const rel = relativePosix(rootDir, f);
|
|
3544
3549
|
return !excludePatterns.some((p) => minimatch22(rel, p));
|
|
3545
3550
|
});
|
|
3546
3551
|
const files = [];
|
|
@@ -4064,7 +4069,7 @@ async function detectDeadCode(snapshot, graphDeadCodeData) {
|
|
|
4064
4069
|
return Ok(report);
|
|
4065
4070
|
}
|
|
4066
4071
|
function fileMatchesPattern(filePath, pattern, rootDir) {
|
|
4067
|
-
const relativePath =
|
|
4072
|
+
const relativePath = relativePosix(rootDir, filePath);
|
|
4068
4073
|
return minimatch3(relativePath, pattern);
|
|
4069
4074
|
}
|
|
4070
4075
|
function checkConfigPattern(pattern, file, rootDir) {
|
|
@@ -4210,15 +4215,34 @@ async function detectPatternViolations(snapshot, config) {
|
|
|
4210
4215
|
}
|
|
4211
4216
|
}
|
|
4212
4217
|
}
|
|
4218
|
+
if (config?.customPatterns) {
|
|
4219
|
+
for (const file of snapshot.files) {
|
|
4220
|
+
for (const custom of config.customPatterns) {
|
|
4221
|
+
const matches = custom.check(file, snapshot);
|
|
4222
|
+
for (const match of matches) {
|
|
4223
|
+
violations.push({
|
|
4224
|
+
pattern: custom.name,
|
|
4225
|
+
file: file.path,
|
|
4226
|
+
line: match.line,
|
|
4227
|
+
message: match.message,
|
|
4228
|
+
suggestion: match.suggestion || "Review and fix this pattern violation",
|
|
4229
|
+
severity: custom.severity
|
|
4230
|
+
});
|
|
4231
|
+
}
|
|
4232
|
+
}
|
|
4233
|
+
}
|
|
4234
|
+
}
|
|
4213
4235
|
const errorCount = violations.filter((v) => v.severity === "error").length;
|
|
4214
4236
|
const warningCount = violations.filter((v) => v.severity === "warning").length;
|
|
4215
|
-
const
|
|
4216
|
-
const
|
|
4237
|
+
const customCount = config?.customPatterns?.length ?? 0;
|
|
4238
|
+
const allPatternsCount = patterns.length + customCount;
|
|
4239
|
+
const totalChecks = snapshot.files.length * allPatternsCount;
|
|
4240
|
+
const passRate = totalChecks > 0 ? Math.max(0, (totalChecks - violations.length) / totalChecks) : 1;
|
|
4217
4241
|
return Ok({
|
|
4218
4242
|
violations,
|
|
4219
4243
|
stats: {
|
|
4220
4244
|
filesChecked: snapshot.files.length,
|
|
4221
|
-
patternsApplied:
|
|
4245
|
+
patternsApplied: allPatternsCount,
|
|
4222
4246
|
violationCount: violations.length,
|
|
4223
4247
|
errorCount,
|
|
4224
4248
|
warningCount
|
|
@@ -6349,8 +6373,16 @@ var DEFAULT_STREAM_INDEX = {
|
|
|
6349
6373
|
streams: {}
|
|
6350
6374
|
};
|
|
6351
6375
|
var HARNESS_DIR = ".harness";
|
|
6352
|
-
var
|
|
6376
|
+
var STATE_FILE = "state.json";
|
|
6377
|
+
var LEARNINGS_FILE = "learnings.md";
|
|
6378
|
+
var FAILURES_FILE = "failures.md";
|
|
6379
|
+
var HANDOFF_FILE = "handoff.json";
|
|
6380
|
+
var GATE_CONFIG_FILE = "gate.json";
|
|
6353
6381
|
var INDEX_FILE = "index.json";
|
|
6382
|
+
var SESSIONS_DIR = "sessions";
|
|
6383
|
+
var SESSION_INDEX_FILE = "index.md";
|
|
6384
|
+
var SUMMARY_FILE = "summary.md";
|
|
6385
|
+
var STREAMS_DIR = "streams";
|
|
6354
6386
|
var STREAM_NAME_REGEX = /^[a-z0-9][a-z0-9._-]*$/;
|
|
6355
6387
|
function streamsDir(projectPath) {
|
|
6356
6388
|
return path2.join(projectPath, HARNESS_DIR, STREAMS_DIR);
|
|
@@ -6576,25 +6608,60 @@ async function migrateToStreams(projectPath) {
|
|
|
6576
6608
|
};
|
|
6577
6609
|
return saveStreamIndex(projectPath, index);
|
|
6578
6610
|
}
|
|
6579
|
-
|
|
6580
|
-
|
|
6581
|
-
|
|
6582
|
-
|
|
6583
|
-
|
|
6584
|
-
|
|
6585
|
-
|
|
6611
|
+
function resolveSessionDir(projectPath, sessionSlug, options) {
|
|
6612
|
+
if (!sessionSlug || sessionSlug.trim() === "") {
|
|
6613
|
+
return Err(new Error("Session slug must not be empty"));
|
|
6614
|
+
}
|
|
6615
|
+
if (sessionSlug.includes("..") || sessionSlug.includes("/") || sessionSlug.includes("\\")) {
|
|
6616
|
+
return Err(
|
|
6617
|
+
new Error(`Invalid session slug '${sessionSlug}': must not contain path traversal characters`)
|
|
6618
|
+
);
|
|
6619
|
+
}
|
|
6620
|
+
const sessionDir = path3.join(projectPath, HARNESS_DIR, SESSIONS_DIR, sessionSlug);
|
|
6621
|
+
if (options?.create) {
|
|
6622
|
+
fs6.mkdirSync(sessionDir, { recursive: true });
|
|
6623
|
+
}
|
|
6624
|
+
return Ok(sessionDir);
|
|
6625
|
+
}
|
|
6626
|
+
function updateSessionIndex(projectPath, sessionSlug, description) {
|
|
6627
|
+
const sessionsDir = path3.join(projectPath, HARNESS_DIR, SESSIONS_DIR);
|
|
6628
|
+
fs6.mkdirSync(sessionsDir, { recursive: true });
|
|
6629
|
+
const indexPath2 = path3.join(sessionsDir, SESSION_INDEX_FILE);
|
|
6630
|
+
const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
6631
|
+
const newLine = `- [${sessionSlug}](${sessionSlug}/summary.md) \u2014 ${description} (${date})`;
|
|
6632
|
+
if (!fs6.existsSync(indexPath2)) {
|
|
6633
|
+
fs6.writeFileSync(indexPath2, `## Active Sessions
|
|
6634
|
+
|
|
6635
|
+
${newLine}
|
|
6636
|
+
`);
|
|
6637
|
+
return;
|
|
6638
|
+
}
|
|
6639
|
+
const content = fs6.readFileSync(indexPath2, "utf-8");
|
|
6640
|
+
const lines = content.split("\n");
|
|
6641
|
+
const slugPattern = `- [${sessionSlug}]`;
|
|
6642
|
+
const existingIdx = lines.findIndex((l) => l.startsWith(slugPattern));
|
|
6643
|
+
if (existingIdx >= 0) {
|
|
6644
|
+
lines[existingIdx] = newLine;
|
|
6645
|
+
} else {
|
|
6646
|
+
const lastNonEmpty = lines.reduce((last, line, i) => line.trim() !== "" ? i : last, 0);
|
|
6647
|
+
lines.splice(lastNonEmpty + 1, 0, newLine);
|
|
6648
|
+
}
|
|
6649
|
+
fs6.writeFileSync(indexPath2, lines.join("\n"));
|
|
6650
|
+
}
|
|
6586
6651
|
var MAX_CACHE_ENTRIES = 8;
|
|
6587
|
-
var learningsCacheMap = /* @__PURE__ */ new Map();
|
|
6588
|
-
var failuresCacheMap = /* @__PURE__ */ new Map();
|
|
6589
6652
|
function evictIfNeeded(map) {
|
|
6590
6653
|
if (map.size > MAX_CACHE_ENTRIES) {
|
|
6591
6654
|
const oldest = map.keys().next().value;
|
|
6592
6655
|
if (oldest !== void 0) map.delete(oldest);
|
|
6593
6656
|
}
|
|
6594
6657
|
}
|
|
6595
|
-
async function getStateDir(projectPath, stream) {
|
|
6596
|
-
|
|
6597
|
-
|
|
6658
|
+
async function getStateDir(projectPath, stream, session) {
|
|
6659
|
+
if (session) {
|
|
6660
|
+
const sessionResult = resolveSessionDir(projectPath, session, { create: true });
|
|
6661
|
+
return sessionResult;
|
|
6662
|
+
}
|
|
6663
|
+
const streamsIndexPath = path4.join(projectPath, HARNESS_DIR, "streams", INDEX_FILE);
|
|
6664
|
+
const hasStreams = fs7.existsSync(streamsIndexPath);
|
|
6598
6665
|
if (stream || hasStreams) {
|
|
6599
6666
|
const result = await resolveStreamPath(projectPath, stream ? { stream } : void 0);
|
|
6600
6667
|
if (result.ok) {
|
|
@@ -6604,18 +6671,18 @@ async function getStateDir(projectPath, stream) {
|
|
|
6604
6671
|
return result;
|
|
6605
6672
|
}
|
|
6606
6673
|
}
|
|
6607
|
-
return Ok(
|
|
6674
|
+
return Ok(path4.join(projectPath, HARNESS_DIR));
|
|
6608
6675
|
}
|
|
6609
|
-
async function loadState(projectPath, stream) {
|
|
6676
|
+
async function loadState(projectPath, stream, session) {
|
|
6610
6677
|
try {
|
|
6611
|
-
const dirResult = await getStateDir(projectPath, stream);
|
|
6678
|
+
const dirResult = await getStateDir(projectPath, stream, session);
|
|
6612
6679
|
if (!dirResult.ok) return dirResult;
|
|
6613
6680
|
const stateDir = dirResult.value;
|
|
6614
|
-
const statePath =
|
|
6615
|
-
if (!
|
|
6681
|
+
const statePath = path5.join(stateDir, STATE_FILE);
|
|
6682
|
+
if (!fs8.existsSync(statePath)) {
|
|
6616
6683
|
return Ok({ ...DEFAULT_STATE });
|
|
6617
6684
|
}
|
|
6618
|
-
const raw =
|
|
6685
|
+
const raw = fs8.readFileSync(statePath, "utf-8");
|
|
6619
6686
|
const parsed = JSON.parse(raw);
|
|
6620
6687
|
const result = HarnessStateSchema.safeParse(parsed);
|
|
6621
6688
|
if (!result.success) {
|
|
@@ -6628,14 +6695,14 @@ async function loadState(projectPath, stream) {
|
|
|
6628
6695
|
);
|
|
6629
6696
|
}
|
|
6630
6697
|
}
|
|
6631
|
-
async function saveState(projectPath, state, stream) {
|
|
6698
|
+
async function saveState(projectPath, state, stream, session) {
|
|
6632
6699
|
try {
|
|
6633
|
-
const dirResult = await getStateDir(projectPath, stream);
|
|
6700
|
+
const dirResult = await getStateDir(projectPath, stream, session);
|
|
6634
6701
|
if (!dirResult.ok) return dirResult;
|
|
6635
6702
|
const stateDir = dirResult.value;
|
|
6636
|
-
const statePath =
|
|
6637
|
-
|
|
6638
|
-
|
|
6703
|
+
const statePath = path5.join(stateDir, STATE_FILE);
|
|
6704
|
+
fs8.mkdirSync(stateDir, { recursive: true });
|
|
6705
|
+
fs8.writeFileSync(statePath, JSON.stringify(state, null, 2));
|
|
6639
6706
|
return Ok(void 0);
|
|
6640
6707
|
} catch (error) {
|
|
6641
6708
|
return Err(
|
|
@@ -6643,13 +6710,17 @@ async function saveState(projectPath, state, stream) {
|
|
|
6643
6710
|
);
|
|
6644
6711
|
}
|
|
6645
6712
|
}
|
|
6646
|
-
|
|
6713
|
+
var learningsCacheMap = /* @__PURE__ */ new Map();
|
|
6714
|
+
function clearLearningsCache() {
|
|
6715
|
+
learningsCacheMap.clear();
|
|
6716
|
+
}
|
|
6717
|
+
async function appendLearning(projectPath, learning, skillName, outcome, stream, session) {
|
|
6647
6718
|
try {
|
|
6648
|
-
const dirResult = await getStateDir(projectPath, stream);
|
|
6719
|
+
const dirResult = await getStateDir(projectPath, stream, session);
|
|
6649
6720
|
if (!dirResult.ok) return dirResult;
|
|
6650
6721
|
const stateDir = dirResult.value;
|
|
6651
|
-
const learningsPath =
|
|
6652
|
-
|
|
6722
|
+
const learningsPath = path6.join(stateDir, LEARNINGS_FILE);
|
|
6723
|
+
fs9.mkdirSync(stateDir, { recursive: true });
|
|
6653
6724
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
6654
6725
|
let entry;
|
|
6655
6726
|
if (skillName && outcome) {
|
|
@@ -6665,11 +6736,11 @@ async function appendLearning(projectPath, learning, skillName, outcome, stream)
|
|
|
6665
6736
|
- **${timestamp}:** ${learning}
|
|
6666
6737
|
`;
|
|
6667
6738
|
}
|
|
6668
|
-
if (!
|
|
6669
|
-
|
|
6739
|
+
if (!fs9.existsSync(learningsPath)) {
|
|
6740
|
+
fs9.writeFileSync(learningsPath, `# Learnings
|
|
6670
6741
|
${entry}`);
|
|
6671
6742
|
} else {
|
|
6672
|
-
|
|
6743
|
+
fs9.appendFileSync(learningsPath, entry);
|
|
6673
6744
|
}
|
|
6674
6745
|
learningsCacheMap.delete(learningsPath);
|
|
6675
6746
|
return Ok(void 0);
|
|
@@ -6681,23 +6752,92 @@ ${entry}`);
|
|
|
6681
6752
|
);
|
|
6682
6753
|
}
|
|
6683
6754
|
}
|
|
6684
|
-
|
|
6755
|
+
function estimateTokens(text) {
|
|
6756
|
+
return Math.ceil(text.length / 4);
|
|
6757
|
+
}
|
|
6758
|
+
function scoreRelevance(entry, intent) {
|
|
6759
|
+
if (!intent || intent.trim() === "") return 0;
|
|
6760
|
+
const intentWords = intent.toLowerCase().split(/\s+/).filter((w) => w.length > 2);
|
|
6761
|
+
if (intentWords.length === 0) return 0;
|
|
6762
|
+
const entryLower = entry.toLowerCase();
|
|
6763
|
+
const matches = intentWords.filter((word) => entryLower.includes(word));
|
|
6764
|
+
return matches.length / intentWords.length;
|
|
6765
|
+
}
|
|
6766
|
+
function parseDateFromEntry(entry) {
|
|
6767
|
+
const match = entry.match(/(\d{4}-\d{2}-\d{2})/);
|
|
6768
|
+
return match ? match[1] ?? null : null;
|
|
6769
|
+
}
|
|
6770
|
+
function analyzeLearningPatterns(entries) {
|
|
6771
|
+
const tagGroups = /* @__PURE__ */ new Map();
|
|
6772
|
+
for (const entry of entries) {
|
|
6773
|
+
const tagMatches = entry.matchAll(/\[(skill:[^\]]+)\]|\[(outcome:[^\]]+)\]/g);
|
|
6774
|
+
for (const match of tagMatches) {
|
|
6775
|
+
const tag = match[1] ?? match[2];
|
|
6776
|
+
if (tag) {
|
|
6777
|
+
const group = tagGroups.get(tag) ?? [];
|
|
6778
|
+
group.push(entry);
|
|
6779
|
+
tagGroups.set(tag, group);
|
|
6780
|
+
}
|
|
6781
|
+
}
|
|
6782
|
+
}
|
|
6783
|
+
const patterns = [];
|
|
6784
|
+
for (const [tag, groupEntries] of tagGroups) {
|
|
6785
|
+
if (groupEntries.length >= 3) {
|
|
6786
|
+
patterns.push({ tag, count: groupEntries.length, entries: groupEntries });
|
|
6787
|
+
}
|
|
6788
|
+
}
|
|
6789
|
+
return patterns.sort((a, b) => b.count - a.count);
|
|
6790
|
+
}
|
|
6791
|
+
async function loadBudgetedLearnings(projectPath, options) {
|
|
6792
|
+
const { intent, tokenBudget = 1e3, skill, session, stream } = options;
|
|
6793
|
+
const sortByRecencyAndRelevance = (entries) => {
|
|
6794
|
+
return [...entries].sort((a, b) => {
|
|
6795
|
+
const dateA = parseDateFromEntry(a) ?? "0000-00-00";
|
|
6796
|
+
const dateB = parseDateFromEntry(b) ?? "0000-00-00";
|
|
6797
|
+
const dateCompare = dateB.localeCompare(dateA);
|
|
6798
|
+
if (dateCompare !== 0) return dateCompare;
|
|
6799
|
+
return scoreRelevance(b, intent) - scoreRelevance(a, intent);
|
|
6800
|
+
});
|
|
6801
|
+
};
|
|
6802
|
+
const allEntries = [];
|
|
6803
|
+
if (session) {
|
|
6804
|
+
const sessionResult = await loadRelevantLearnings(projectPath, skill, stream, session);
|
|
6805
|
+
if (sessionResult.ok) {
|
|
6806
|
+
allEntries.push(...sortByRecencyAndRelevance(sessionResult.value));
|
|
6807
|
+
}
|
|
6808
|
+
}
|
|
6809
|
+
const globalResult = await loadRelevantLearnings(projectPath, skill, stream);
|
|
6810
|
+
if (globalResult.ok) {
|
|
6811
|
+
allEntries.push(...sortByRecencyAndRelevance(globalResult.value));
|
|
6812
|
+
}
|
|
6813
|
+
const budgeted = [];
|
|
6814
|
+
let totalTokens = 0;
|
|
6815
|
+
for (const entry of allEntries) {
|
|
6816
|
+
const separator = budgeted.length > 0 ? "\n" : "";
|
|
6817
|
+
const entryCost = estimateTokens(entry + separator);
|
|
6818
|
+
if (totalTokens + entryCost > tokenBudget) break;
|
|
6819
|
+
budgeted.push(entry);
|
|
6820
|
+
totalTokens += entryCost;
|
|
6821
|
+
}
|
|
6822
|
+
return Ok(budgeted);
|
|
6823
|
+
}
|
|
6824
|
+
async function loadRelevantLearnings(projectPath, skillName, stream, session) {
|
|
6685
6825
|
try {
|
|
6686
|
-
const dirResult = await getStateDir(projectPath, stream);
|
|
6826
|
+
const dirResult = await getStateDir(projectPath, stream, session);
|
|
6687
6827
|
if (!dirResult.ok) return dirResult;
|
|
6688
6828
|
const stateDir = dirResult.value;
|
|
6689
|
-
const learningsPath =
|
|
6690
|
-
if (!
|
|
6829
|
+
const learningsPath = path6.join(stateDir, LEARNINGS_FILE);
|
|
6830
|
+
if (!fs9.existsSync(learningsPath)) {
|
|
6691
6831
|
return Ok([]);
|
|
6692
6832
|
}
|
|
6693
|
-
const stats =
|
|
6833
|
+
const stats = fs9.statSync(learningsPath);
|
|
6694
6834
|
const cacheKey = learningsPath;
|
|
6695
6835
|
const cached = learningsCacheMap.get(cacheKey);
|
|
6696
6836
|
let entries;
|
|
6697
6837
|
if (cached && cached.mtimeMs === stats.mtimeMs) {
|
|
6698
6838
|
entries = cached.entries;
|
|
6699
6839
|
} else {
|
|
6700
|
-
const content =
|
|
6840
|
+
const content = fs9.readFileSync(learningsPath, "utf-8");
|
|
6701
6841
|
const lines = content.split("\n");
|
|
6702
6842
|
entries = [];
|
|
6703
6843
|
let currentBlock = [];
|
|
@@ -6733,23 +6873,106 @@ async function loadRelevantLearnings(projectPath, skillName, stream) {
|
|
|
6733
6873
|
);
|
|
6734
6874
|
}
|
|
6735
6875
|
}
|
|
6736
|
-
|
|
6737
|
-
|
|
6876
|
+
async function archiveLearnings(projectPath, entries, stream) {
|
|
6877
|
+
try {
|
|
6878
|
+
const dirResult = await getStateDir(projectPath, stream);
|
|
6879
|
+
if (!dirResult.ok) return dirResult;
|
|
6880
|
+
const stateDir = dirResult.value;
|
|
6881
|
+
const archiveDir = path6.join(stateDir, "learnings-archive");
|
|
6882
|
+
fs9.mkdirSync(archiveDir, { recursive: true });
|
|
6883
|
+
const now = /* @__PURE__ */ new Date();
|
|
6884
|
+
const yearMonth = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}`;
|
|
6885
|
+
const archivePath = path6.join(archiveDir, `${yearMonth}.md`);
|
|
6886
|
+
const archiveContent = entries.join("\n\n") + "\n";
|
|
6887
|
+
if (fs9.existsSync(archivePath)) {
|
|
6888
|
+
fs9.appendFileSync(archivePath, "\n" + archiveContent);
|
|
6889
|
+
} else {
|
|
6890
|
+
fs9.writeFileSync(archivePath, `# Learnings Archive
|
|
6891
|
+
|
|
6892
|
+
${archiveContent}`);
|
|
6893
|
+
}
|
|
6894
|
+
return Ok(void 0);
|
|
6895
|
+
} catch (error) {
|
|
6896
|
+
return Err(
|
|
6897
|
+
new Error(
|
|
6898
|
+
`Failed to archive learnings: ${error instanceof Error ? error.message : String(error)}`
|
|
6899
|
+
)
|
|
6900
|
+
);
|
|
6901
|
+
}
|
|
6902
|
+
}
|
|
6903
|
+
async function pruneLearnings(projectPath, stream) {
|
|
6738
6904
|
try {
|
|
6739
6905
|
const dirResult = await getStateDir(projectPath, stream);
|
|
6740
6906
|
if (!dirResult.ok) return dirResult;
|
|
6741
6907
|
const stateDir = dirResult.value;
|
|
6742
|
-
const
|
|
6743
|
-
|
|
6908
|
+
const learningsPath = path6.join(stateDir, LEARNINGS_FILE);
|
|
6909
|
+
if (!fs9.existsSync(learningsPath)) {
|
|
6910
|
+
return Ok({ kept: 0, archived: 0, patterns: [] });
|
|
6911
|
+
}
|
|
6912
|
+
const loadResult = await loadRelevantLearnings(projectPath, void 0, stream);
|
|
6913
|
+
if (!loadResult.ok) return loadResult;
|
|
6914
|
+
const allEntries = loadResult.value;
|
|
6915
|
+
if (allEntries.length <= 20) {
|
|
6916
|
+
const cutoffDate = /* @__PURE__ */ new Date();
|
|
6917
|
+
cutoffDate.setDate(cutoffDate.getDate() - 14);
|
|
6918
|
+
const cutoffStr = cutoffDate.toISOString().split("T")[0];
|
|
6919
|
+
const hasOld = allEntries.some((entry) => {
|
|
6920
|
+
const date = parseDateFromEntry(entry);
|
|
6921
|
+
return date !== null && date < cutoffStr;
|
|
6922
|
+
});
|
|
6923
|
+
if (!hasOld) {
|
|
6924
|
+
return Ok({ kept: allEntries.length, archived: 0, patterns: [] });
|
|
6925
|
+
}
|
|
6926
|
+
}
|
|
6927
|
+
const sorted = [...allEntries].sort((a, b) => {
|
|
6928
|
+
const dateA = parseDateFromEntry(a) ?? "0000-00-00";
|
|
6929
|
+
const dateB = parseDateFromEntry(b) ?? "0000-00-00";
|
|
6930
|
+
return dateB.localeCompare(dateA);
|
|
6931
|
+
});
|
|
6932
|
+
const toKeep = sorted.slice(0, 20);
|
|
6933
|
+
const toArchive = sorted.slice(20);
|
|
6934
|
+
const patterns = analyzeLearningPatterns(allEntries);
|
|
6935
|
+
if (toArchive.length > 0) {
|
|
6936
|
+
const archiveResult = await archiveLearnings(projectPath, toArchive, stream);
|
|
6937
|
+
if (!archiveResult.ok) return archiveResult;
|
|
6938
|
+
}
|
|
6939
|
+
const newContent = "# Learnings\n\n" + toKeep.join("\n\n") + "\n";
|
|
6940
|
+
fs9.writeFileSync(learningsPath, newContent);
|
|
6941
|
+
learningsCacheMap.delete(learningsPath);
|
|
6942
|
+
return Ok({
|
|
6943
|
+
kept: toKeep.length,
|
|
6944
|
+
archived: toArchive.length,
|
|
6945
|
+
patterns
|
|
6946
|
+
});
|
|
6947
|
+
} catch (error) {
|
|
6948
|
+
return Err(
|
|
6949
|
+
new Error(
|
|
6950
|
+
`Failed to prune learnings: ${error instanceof Error ? error.message : String(error)}`
|
|
6951
|
+
)
|
|
6952
|
+
);
|
|
6953
|
+
}
|
|
6954
|
+
}
|
|
6955
|
+
var failuresCacheMap = /* @__PURE__ */ new Map();
|
|
6956
|
+
function clearFailuresCache() {
|
|
6957
|
+
failuresCacheMap.clear();
|
|
6958
|
+
}
|
|
6959
|
+
var FAILURE_LINE_REGEX = /^- \*\*(\d{4}-\d{2}-\d{2}) \[skill:([^\]]+)\] \[type:([^\]]+)\]:\*\* (.+)$/;
|
|
6960
|
+
async function appendFailure(projectPath, description, skillName, type, stream, session) {
|
|
6961
|
+
try {
|
|
6962
|
+
const dirResult = await getStateDir(projectPath, stream, session);
|
|
6963
|
+
if (!dirResult.ok) return dirResult;
|
|
6964
|
+
const stateDir = dirResult.value;
|
|
6965
|
+
const failuresPath = path7.join(stateDir, FAILURES_FILE);
|
|
6966
|
+
fs10.mkdirSync(stateDir, { recursive: true });
|
|
6744
6967
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
6745
6968
|
const entry = `
|
|
6746
6969
|
- **${timestamp} [skill:${skillName}] [type:${type}]:** ${description}
|
|
6747
6970
|
`;
|
|
6748
|
-
if (!
|
|
6749
|
-
|
|
6971
|
+
if (!fs10.existsSync(failuresPath)) {
|
|
6972
|
+
fs10.writeFileSync(failuresPath, `# Failures
|
|
6750
6973
|
${entry}`);
|
|
6751
6974
|
} else {
|
|
6752
|
-
|
|
6975
|
+
fs10.appendFileSync(failuresPath, entry);
|
|
6753
6976
|
}
|
|
6754
6977
|
failuresCacheMap.delete(failuresPath);
|
|
6755
6978
|
return Ok(void 0);
|
|
@@ -6761,22 +6984,22 @@ ${entry}`);
|
|
|
6761
6984
|
);
|
|
6762
6985
|
}
|
|
6763
6986
|
}
|
|
6764
|
-
async function loadFailures(projectPath, stream) {
|
|
6987
|
+
async function loadFailures(projectPath, stream, session) {
|
|
6765
6988
|
try {
|
|
6766
|
-
const dirResult = await getStateDir(projectPath, stream);
|
|
6989
|
+
const dirResult = await getStateDir(projectPath, stream, session);
|
|
6767
6990
|
if (!dirResult.ok) return dirResult;
|
|
6768
6991
|
const stateDir = dirResult.value;
|
|
6769
|
-
const failuresPath =
|
|
6770
|
-
if (!
|
|
6992
|
+
const failuresPath = path7.join(stateDir, FAILURES_FILE);
|
|
6993
|
+
if (!fs10.existsSync(failuresPath)) {
|
|
6771
6994
|
return Ok([]);
|
|
6772
6995
|
}
|
|
6773
|
-
const stats =
|
|
6996
|
+
const stats = fs10.statSync(failuresPath);
|
|
6774
6997
|
const cacheKey = failuresPath;
|
|
6775
6998
|
const cached = failuresCacheMap.get(cacheKey);
|
|
6776
6999
|
if (cached && cached.mtimeMs === stats.mtimeMs) {
|
|
6777
7000
|
return Ok(cached.entries);
|
|
6778
7001
|
}
|
|
6779
|
-
const content =
|
|
7002
|
+
const content = fs10.readFileSync(failuresPath, "utf-8");
|
|
6780
7003
|
const entries = [];
|
|
6781
7004
|
for (const line of content.split("\n")) {
|
|
6782
7005
|
const match = line.match(FAILURE_LINE_REGEX);
|
|
@@ -6800,25 +7023,25 @@ async function loadFailures(projectPath, stream) {
|
|
|
6800
7023
|
);
|
|
6801
7024
|
}
|
|
6802
7025
|
}
|
|
6803
|
-
async function archiveFailures(projectPath, stream) {
|
|
7026
|
+
async function archiveFailures(projectPath, stream, session) {
|
|
6804
7027
|
try {
|
|
6805
|
-
const dirResult = await getStateDir(projectPath, stream);
|
|
7028
|
+
const dirResult = await getStateDir(projectPath, stream, session);
|
|
6806
7029
|
if (!dirResult.ok) return dirResult;
|
|
6807
7030
|
const stateDir = dirResult.value;
|
|
6808
|
-
const failuresPath =
|
|
6809
|
-
if (!
|
|
7031
|
+
const failuresPath = path7.join(stateDir, FAILURES_FILE);
|
|
7032
|
+
if (!fs10.existsSync(failuresPath)) {
|
|
6810
7033
|
return Ok(void 0);
|
|
6811
7034
|
}
|
|
6812
|
-
const archiveDir =
|
|
6813
|
-
|
|
7035
|
+
const archiveDir = path7.join(stateDir, "archive");
|
|
7036
|
+
fs10.mkdirSync(archiveDir, { recursive: true });
|
|
6814
7037
|
const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
6815
7038
|
let archiveName = `failures-${date}.md`;
|
|
6816
7039
|
let counter = 2;
|
|
6817
|
-
while (
|
|
7040
|
+
while (fs10.existsSync(path7.join(archiveDir, archiveName))) {
|
|
6818
7041
|
archiveName = `failures-${date}-${counter}.md`;
|
|
6819
7042
|
counter++;
|
|
6820
7043
|
}
|
|
6821
|
-
|
|
7044
|
+
fs10.renameSync(failuresPath, path7.join(archiveDir, archiveName));
|
|
6822
7045
|
failuresCacheMap.delete(failuresPath);
|
|
6823
7046
|
return Ok(void 0);
|
|
6824
7047
|
} catch (error) {
|
|
@@ -6829,14 +7052,14 @@ async function archiveFailures(projectPath, stream) {
|
|
|
6829
7052
|
);
|
|
6830
7053
|
}
|
|
6831
7054
|
}
|
|
6832
|
-
async function saveHandoff(projectPath, handoff, stream) {
|
|
7055
|
+
async function saveHandoff(projectPath, handoff, stream, session) {
|
|
6833
7056
|
try {
|
|
6834
|
-
const dirResult = await getStateDir(projectPath, stream);
|
|
7057
|
+
const dirResult = await getStateDir(projectPath, stream, session);
|
|
6835
7058
|
if (!dirResult.ok) return dirResult;
|
|
6836
7059
|
const stateDir = dirResult.value;
|
|
6837
|
-
const handoffPath =
|
|
6838
|
-
|
|
6839
|
-
|
|
7060
|
+
const handoffPath = path8.join(stateDir, HANDOFF_FILE);
|
|
7061
|
+
fs11.mkdirSync(stateDir, { recursive: true });
|
|
7062
|
+
fs11.writeFileSync(handoffPath, JSON.stringify(handoff, null, 2));
|
|
6840
7063
|
return Ok(void 0);
|
|
6841
7064
|
} catch (error) {
|
|
6842
7065
|
return Err(
|
|
@@ -6844,16 +7067,16 @@ async function saveHandoff(projectPath, handoff, stream) {
|
|
|
6844
7067
|
);
|
|
6845
7068
|
}
|
|
6846
7069
|
}
|
|
6847
|
-
async function loadHandoff(projectPath, stream) {
|
|
7070
|
+
async function loadHandoff(projectPath, stream, session) {
|
|
6848
7071
|
try {
|
|
6849
|
-
const dirResult = await getStateDir(projectPath, stream);
|
|
7072
|
+
const dirResult = await getStateDir(projectPath, stream, session);
|
|
6850
7073
|
if (!dirResult.ok) return dirResult;
|
|
6851
7074
|
const stateDir = dirResult.value;
|
|
6852
|
-
const handoffPath =
|
|
6853
|
-
if (!
|
|
7075
|
+
const handoffPath = path8.join(stateDir, HANDOFF_FILE);
|
|
7076
|
+
if (!fs11.existsSync(handoffPath)) {
|
|
6854
7077
|
return Ok(null);
|
|
6855
7078
|
}
|
|
6856
|
-
const raw =
|
|
7079
|
+
const raw = fs11.readFileSync(handoffPath, "utf-8");
|
|
6857
7080
|
const parsed = JSON.parse(raw);
|
|
6858
7081
|
const result = HandoffSchema.safeParse(parsed);
|
|
6859
7082
|
if (!result.success) {
|
|
@@ -6866,73 +7089,77 @@ async function loadHandoff(projectPath, stream) {
|
|
|
6866
7089
|
);
|
|
6867
7090
|
}
|
|
6868
7091
|
}
|
|
7092
|
+
var SAFE_GATE_COMMAND = /^(?:npm|pnpm|yarn)\s+(?:test|run\s+[\w.-]+|run-script\s+[\w.-]+)$|^go\s+(?:test|build|vet|fmt)\s+[\w./ -]+$|^(?:python|python3)\s+-m\s+[\w.-]+$|^make\s+[\w.-]+$|^cargo\s+(?:test|build|check|clippy)(?:\s+[\w./ -]+)?$|^(?:gradle|mvn)\s+[\w:.-]+$/;
|
|
7093
|
+
function loadChecksFromConfig(gateConfigPath) {
|
|
7094
|
+
if (!fs12.existsSync(gateConfigPath)) return [];
|
|
7095
|
+
const raw = JSON.parse(fs12.readFileSync(gateConfigPath, "utf-8"));
|
|
7096
|
+
const config = GateConfigSchema.safeParse(raw);
|
|
7097
|
+
if (config.success && config.data.checks) return config.data.checks;
|
|
7098
|
+
return [];
|
|
7099
|
+
}
|
|
7100
|
+
function discoverChecksFromProject(projectPath) {
|
|
7101
|
+
const checks = [];
|
|
7102
|
+
const packageJsonPath = path9.join(projectPath, "package.json");
|
|
7103
|
+
if (fs12.existsSync(packageJsonPath)) {
|
|
7104
|
+
const pkg = JSON.parse(fs12.readFileSync(packageJsonPath, "utf-8"));
|
|
7105
|
+
const scripts = pkg.scripts || {};
|
|
7106
|
+
if (scripts.test) checks.push({ name: "test", command: "npm test" });
|
|
7107
|
+
if (scripts.lint) checks.push({ name: "lint", command: "npm run lint" });
|
|
7108
|
+
if (scripts.typecheck) checks.push({ name: "typecheck", command: "npm run typecheck" });
|
|
7109
|
+
if (scripts.build) checks.push({ name: "build", command: "npm run build" });
|
|
7110
|
+
}
|
|
7111
|
+
if (fs12.existsSync(path9.join(projectPath, "go.mod"))) {
|
|
7112
|
+
checks.push({ name: "test", command: "go test ./..." });
|
|
7113
|
+
checks.push({ name: "build", command: "go build ./..." });
|
|
7114
|
+
}
|
|
7115
|
+
if (fs12.existsSync(path9.join(projectPath, "pyproject.toml")) || fs12.existsSync(path9.join(projectPath, "setup.py"))) {
|
|
7116
|
+
checks.push({ name: "test", command: "python -m pytest" });
|
|
7117
|
+
}
|
|
7118
|
+
return checks;
|
|
7119
|
+
}
|
|
7120
|
+
function executeCheck(check, projectPath) {
|
|
7121
|
+
if (!SAFE_GATE_COMMAND.test(check.command)) {
|
|
7122
|
+
return {
|
|
7123
|
+
name: check.name,
|
|
7124
|
+
passed: false,
|
|
7125
|
+
command: check.command,
|
|
7126
|
+
output: `Blocked: command does not match safe gate pattern. Allowed prefixes: npm, pnpm, yarn, go, python, python3, make, cargo, gradle, mvn`,
|
|
7127
|
+
duration: 0
|
|
7128
|
+
};
|
|
7129
|
+
}
|
|
7130
|
+
const start = Date.now();
|
|
7131
|
+
try {
|
|
7132
|
+
execSync2(check.command, {
|
|
7133
|
+
cwd: projectPath,
|
|
7134
|
+
stdio: "pipe",
|
|
7135
|
+
timeout: 12e4
|
|
7136
|
+
});
|
|
7137
|
+
return {
|
|
7138
|
+
name: check.name,
|
|
7139
|
+
passed: true,
|
|
7140
|
+
command: check.command,
|
|
7141
|
+
duration: Date.now() - start
|
|
7142
|
+
};
|
|
7143
|
+
} catch (error) {
|
|
7144
|
+
const output = error instanceof Error ? error.stderr?.toString() || error.message : String(error);
|
|
7145
|
+
return {
|
|
7146
|
+
name: check.name,
|
|
7147
|
+
passed: false,
|
|
7148
|
+
command: check.command,
|
|
7149
|
+
output: output.slice(0, 2e3),
|
|
7150
|
+
duration: Date.now() - start
|
|
7151
|
+
};
|
|
7152
|
+
}
|
|
7153
|
+
}
|
|
6869
7154
|
async function runMechanicalGate(projectPath) {
|
|
6870
|
-
const harnessDir =
|
|
6871
|
-
const gateConfigPath =
|
|
7155
|
+
const harnessDir = path9.join(projectPath, HARNESS_DIR);
|
|
7156
|
+
const gateConfigPath = path9.join(harnessDir, GATE_CONFIG_FILE);
|
|
6872
7157
|
try {
|
|
6873
|
-
let checks =
|
|
6874
|
-
if (fs6.existsSync(gateConfigPath)) {
|
|
6875
|
-
const raw = JSON.parse(fs6.readFileSync(gateConfigPath, "utf-8"));
|
|
6876
|
-
const config = GateConfigSchema.safeParse(raw);
|
|
6877
|
-
if (config.success && config.data.checks) {
|
|
6878
|
-
checks = config.data.checks;
|
|
6879
|
-
}
|
|
6880
|
-
}
|
|
7158
|
+
let checks = loadChecksFromConfig(gateConfigPath);
|
|
6881
7159
|
if (checks.length === 0) {
|
|
6882
|
-
|
|
6883
|
-
if (fs6.existsSync(packageJsonPath)) {
|
|
6884
|
-
const pkg = JSON.parse(fs6.readFileSync(packageJsonPath, "utf-8"));
|
|
6885
|
-
const scripts = pkg.scripts || {};
|
|
6886
|
-
if (scripts.test) checks.push({ name: "test", command: "npm test" });
|
|
6887
|
-
if (scripts.lint) checks.push({ name: "lint", command: "npm run lint" });
|
|
6888
|
-
if (scripts.typecheck) checks.push({ name: "typecheck", command: "npm run typecheck" });
|
|
6889
|
-
if (scripts.build) checks.push({ name: "build", command: "npm run build" });
|
|
6890
|
-
}
|
|
6891
|
-
if (fs6.existsSync(path3.join(projectPath, "go.mod"))) {
|
|
6892
|
-
checks.push({ name: "test", command: "go test ./..." });
|
|
6893
|
-
checks.push({ name: "build", command: "go build ./..." });
|
|
6894
|
-
}
|
|
6895
|
-
if (fs6.existsSync(path3.join(projectPath, "pyproject.toml")) || fs6.existsSync(path3.join(projectPath, "setup.py"))) {
|
|
6896
|
-
checks.push({ name: "test", command: "python -m pytest" });
|
|
6897
|
-
}
|
|
6898
|
-
}
|
|
6899
|
-
const results = [];
|
|
6900
|
-
const SAFE_GATE_COMMAND = /^(?:npm|pnpm|yarn)\s+(?:test|run\s+[\w.-]+|run-script\s+[\w.-]+)$|^go\s+(?:test|build|vet|fmt)\s+[\w./ -]+$|^(?:python|python3)\s+-m\s+[\w.-]+$|^make\s+[\w.-]+$|^cargo\s+(?:test|build|check|clippy)(?:\s+[\w./ -]+)?$|^(?:gradle|mvn)\s+[\w:.-]+$/;
|
|
6901
|
-
for (const check of checks) {
|
|
6902
|
-
if (!SAFE_GATE_COMMAND.test(check.command)) {
|
|
6903
|
-
results.push({
|
|
6904
|
-
name: check.name,
|
|
6905
|
-
passed: false,
|
|
6906
|
-
command: check.command,
|
|
6907
|
-
output: `Blocked: command does not match safe gate pattern. Allowed prefixes: npm, npx, pnpm, yarn, go, python, python3, make, cargo, gradle, mvn`,
|
|
6908
|
-
duration: 0
|
|
6909
|
-
});
|
|
6910
|
-
continue;
|
|
6911
|
-
}
|
|
6912
|
-
const start = Date.now();
|
|
6913
|
-
try {
|
|
6914
|
-
execSync2(check.command, {
|
|
6915
|
-
cwd: projectPath,
|
|
6916
|
-
stdio: "pipe",
|
|
6917
|
-
timeout: 12e4
|
|
6918
|
-
});
|
|
6919
|
-
results.push({
|
|
6920
|
-
name: check.name,
|
|
6921
|
-
passed: true,
|
|
6922
|
-
command: check.command,
|
|
6923
|
-
duration: Date.now() - start
|
|
6924
|
-
});
|
|
6925
|
-
} catch (error) {
|
|
6926
|
-
const output = error instanceof Error ? error.stderr?.toString() || error.message : String(error);
|
|
6927
|
-
results.push({
|
|
6928
|
-
name: check.name,
|
|
6929
|
-
passed: false,
|
|
6930
|
-
command: check.command,
|
|
6931
|
-
output: output.slice(0, 2e3),
|
|
6932
|
-
duration: Date.now() - start
|
|
6933
|
-
});
|
|
6934
|
-
}
|
|
7160
|
+
checks = discoverChecksFromProject(projectPath);
|
|
6935
7161
|
}
|
|
7162
|
+
const results = checks.map((check) => executeCheck(check, projectPath));
|
|
6936
7163
|
return Ok({
|
|
6937
7164
|
passed: results.length === 0 || results.every((r) => r.passed),
|
|
6938
7165
|
checks: results
|
|
@@ -6945,6 +7172,92 @@ async function runMechanicalGate(projectPath) {
|
|
|
6945
7172
|
);
|
|
6946
7173
|
}
|
|
6947
7174
|
}
|
|
7175
|
+
function formatSummary(data) {
|
|
7176
|
+
const lines = [
|
|
7177
|
+
"## Session Summary",
|
|
7178
|
+
"",
|
|
7179
|
+
`**Session:** ${data.session}`,
|
|
7180
|
+
`**Last active:** ${data.lastActive}`,
|
|
7181
|
+
`**Skill:** ${data.skill}`
|
|
7182
|
+
];
|
|
7183
|
+
if (data.phase) {
|
|
7184
|
+
lines.push(`**Phase:** ${data.phase}`);
|
|
7185
|
+
}
|
|
7186
|
+
lines.push(`**Status:** ${data.status}`);
|
|
7187
|
+
if (data.spec) {
|
|
7188
|
+
lines.push(`**Spec:** ${data.spec}`);
|
|
7189
|
+
}
|
|
7190
|
+
if (data.plan) {
|
|
7191
|
+
lines.push(`**Plan:** ${data.plan}`);
|
|
7192
|
+
}
|
|
7193
|
+
lines.push(`**Key context:** ${data.keyContext}`);
|
|
7194
|
+
lines.push(`**Next step:** ${data.nextStep}`);
|
|
7195
|
+
lines.push("");
|
|
7196
|
+
return lines.join("\n");
|
|
7197
|
+
}
|
|
7198
|
+
function deriveIndexDescription(data) {
|
|
7199
|
+
const skillShort = data.skill.replace("harness-", "");
|
|
7200
|
+
const parts = [skillShort];
|
|
7201
|
+
if (data.phase) {
|
|
7202
|
+
parts.push(`phase ${data.phase}`);
|
|
7203
|
+
}
|
|
7204
|
+
parts.push(data.status.toLowerCase());
|
|
7205
|
+
return parts.join(", ");
|
|
7206
|
+
}
|
|
7207
|
+
function writeSessionSummary(projectPath, sessionSlug, data) {
|
|
7208
|
+
try {
|
|
7209
|
+
const dirResult = resolveSessionDir(projectPath, sessionSlug, { create: true });
|
|
7210
|
+
if (!dirResult.ok) return dirResult;
|
|
7211
|
+
const sessionDir = dirResult.value;
|
|
7212
|
+
const summaryPath = path10.join(sessionDir, SUMMARY_FILE);
|
|
7213
|
+
const content = formatSummary(data);
|
|
7214
|
+
fs13.writeFileSync(summaryPath, content);
|
|
7215
|
+
const description = deriveIndexDescription(data);
|
|
7216
|
+
updateSessionIndex(projectPath, sessionSlug, description);
|
|
7217
|
+
return Ok(void 0);
|
|
7218
|
+
} catch (error) {
|
|
7219
|
+
return Err(
|
|
7220
|
+
new Error(
|
|
7221
|
+
`Failed to write session summary: ${error instanceof Error ? error.message : String(error)}`
|
|
7222
|
+
)
|
|
7223
|
+
);
|
|
7224
|
+
}
|
|
7225
|
+
}
|
|
7226
|
+
function loadSessionSummary(projectPath, sessionSlug) {
|
|
7227
|
+
try {
|
|
7228
|
+
const dirResult = resolveSessionDir(projectPath, sessionSlug);
|
|
7229
|
+
if (!dirResult.ok) return dirResult;
|
|
7230
|
+
const sessionDir = dirResult.value;
|
|
7231
|
+
const summaryPath = path10.join(sessionDir, SUMMARY_FILE);
|
|
7232
|
+
if (!fs13.existsSync(summaryPath)) {
|
|
7233
|
+
return Ok(null);
|
|
7234
|
+
}
|
|
7235
|
+
const content = fs13.readFileSync(summaryPath, "utf-8");
|
|
7236
|
+
return Ok(content);
|
|
7237
|
+
} catch (error) {
|
|
7238
|
+
return Err(
|
|
7239
|
+
new Error(
|
|
7240
|
+
`Failed to load session summary: ${error instanceof Error ? error.message : String(error)}`
|
|
7241
|
+
)
|
|
7242
|
+
);
|
|
7243
|
+
}
|
|
7244
|
+
}
|
|
7245
|
+
function listActiveSessions(projectPath) {
|
|
7246
|
+
try {
|
|
7247
|
+
const indexPath2 = path10.join(projectPath, HARNESS_DIR, SESSIONS_DIR, SESSION_INDEX_FILE);
|
|
7248
|
+
if (!fs13.existsSync(indexPath2)) {
|
|
7249
|
+
return Ok(null);
|
|
7250
|
+
}
|
|
7251
|
+
const content = fs13.readFileSync(indexPath2, "utf-8");
|
|
7252
|
+
return Ok(content);
|
|
7253
|
+
} catch (error) {
|
|
7254
|
+
return Err(
|
|
7255
|
+
new Error(
|
|
7256
|
+
`Failed to list active sessions: ${error instanceof Error ? error.message : String(error)}`
|
|
7257
|
+
)
|
|
7258
|
+
);
|
|
7259
|
+
}
|
|
7260
|
+
}
|
|
6948
7261
|
async function executeWorkflow(workflow, executor) {
|
|
6949
7262
|
const stepResults = [];
|
|
6950
7263
|
const startTime = Date.now();
|
|
@@ -7166,11 +7479,11 @@ function resolveRuleSeverity(ruleId, defaultSeverity, overrides, strict) {
|
|
|
7166
7479
|
}
|
|
7167
7480
|
function detectStack(projectRoot) {
|
|
7168
7481
|
const stacks = [];
|
|
7169
|
-
const pkgJsonPath =
|
|
7170
|
-
if (
|
|
7482
|
+
const pkgJsonPath = path11.join(projectRoot, "package.json");
|
|
7483
|
+
if (fs14.existsSync(pkgJsonPath)) {
|
|
7171
7484
|
stacks.push("node");
|
|
7172
7485
|
try {
|
|
7173
|
-
const pkgJson = JSON.parse(
|
|
7486
|
+
const pkgJson = JSON.parse(fs14.readFileSync(pkgJsonPath, "utf-8"));
|
|
7174
7487
|
const allDeps = {
|
|
7175
7488
|
...pkgJson.dependencies,
|
|
7176
7489
|
...pkgJson.devDependencies
|
|
@@ -7185,13 +7498,13 @@ function detectStack(projectRoot) {
|
|
|
7185
7498
|
} catch {
|
|
7186
7499
|
}
|
|
7187
7500
|
}
|
|
7188
|
-
const goModPath =
|
|
7189
|
-
if (
|
|
7501
|
+
const goModPath = path11.join(projectRoot, "go.mod");
|
|
7502
|
+
if (fs14.existsSync(goModPath)) {
|
|
7190
7503
|
stacks.push("go");
|
|
7191
7504
|
}
|
|
7192
|
-
const requirementsPath =
|
|
7193
|
-
const pyprojectPath =
|
|
7194
|
-
if (
|
|
7505
|
+
const requirementsPath = path11.join(projectRoot, "requirements.txt");
|
|
7506
|
+
const pyprojectPath = path11.join(projectRoot, "pyproject.toml");
|
|
7507
|
+
if (fs14.existsSync(requirementsPath) || fs14.existsSync(pyprojectPath)) {
|
|
7195
7508
|
stacks.push("python");
|
|
7196
7509
|
}
|
|
7197
7510
|
return stacks;
|
|
@@ -7594,7 +7907,7 @@ var SecurityScanner = class {
|
|
|
7594
7907
|
}
|
|
7595
7908
|
async scanFile(filePath) {
|
|
7596
7909
|
if (!this.config.enabled) return [];
|
|
7597
|
-
const content = await
|
|
7910
|
+
const content = await fs15.readFile(filePath, "utf-8");
|
|
7598
7911
|
return this.scanContent(content, filePath, 1);
|
|
7599
7912
|
}
|
|
7600
7913
|
async scanFiles(filePaths) {
|
|
@@ -7627,238 +7940,270 @@ var ALL_CHECKS = [
|
|
|
7627
7940
|
"phase-gate",
|
|
7628
7941
|
"arch"
|
|
7629
7942
|
];
|
|
7630
|
-
async function
|
|
7631
|
-
const start = Date.now();
|
|
7943
|
+
async function runValidateCheck(projectRoot, config) {
|
|
7632
7944
|
const issues = [];
|
|
7633
|
-
|
|
7634
|
-
|
|
7635
|
-
|
|
7636
|
-
|
|
7637
|
-
|
|
7638
|
-
|
|
7639
|
-
|
|
7640
|
-
|
|
7641
|
-
if (result.value.errors) {
|
|
7642
|
-
for (const err of result.value.errors) {
|
|
7643
|
-
issues.push({ severity: "error", message: err.message });
|
|
7644
|
-
}
|
|
7645
|
-
}
|
|
7646
|
-
for (const section of result.value.missingSections) {
|
|
7647
|
-
issues.push({ severity: "warning", message: `Missing section: ${section}` });
|
|
7648
|
-
}
|
|
7649
|
-
for (const link of result.value.brokenLinks) {
|
|
7650
|
-
issues.push({
|
|
7651
|
-
severity: "warning",
|
|
7652
|
-
message: `Broken link: ${link.text} \u2192 ${link.path}`,
|
|
7653
|
-
file: link.path
|
|
7654
|
-
});
|
|
7655
|
-
}
|
|
7656
|
-
}
|
|
7657
|
-
break;
|
|
7945
|
+
const agentsPath = path12.join(projectRoot, config.agentsMapPath ?? "AGENTS.md");
|
|
7946
|
+
const result = await validateAgentsMap(agentsPath);
|
|
7947
|
+
if (!result.ok) {
|
|
7948
|
+
issues.push({ severity: "error", message: result.error.message });
|
|
7949
|
+
} else if (!result.value.valid) {
|
|
7950
|
+
if (result.value.errors) {
|
|
7951
|
+
for (const err of result.value.errors) {
|
|
7952
|
+
issues.push({ severity: "error", message: err.message });
|
|
7658
7953
|
}
|
|
7659
|
-
|
|
7660
|
-
|
|
7661
|
-
|
|
7662
|
-
|
|
7663
|
-
|
|
7664
|
-
|
|
7665
|
-
|
|
7666
|
-
|
|
7667
|
-
|
|
7668
|
-
|
|
7669
|
-
|
|
7670
|
-
|
|
7671
|
-
|
|
7672
|
-
|
|
7673
|
-
|
|
7674
|
-
|
|
7675
|
-
|
|
7676
|
-
|
|
7677
|
-
|
|
7678
|
-
|
|
7679
|
-
|
|
7680
|
-
|
|
7681
|
-
|
|
7682
|
-
|
|
7683
|
-
|
|
7684
|
-
|
|
7685
|
-
|
|
7686
|
-
|
|
7687
|
-
|
|
7688
|
-
|
|
7954
|
+
}
|
|
7955
|
+
for (const section of result.value.missingSections) {
|
|
7956
|
+
issues.push({ severity: "warning", message: `Missing section: ${section}` });
|
|
7957
|
+
}
|
|
7958
|
+
for (const link of result.value.brokenLinks) {
|
|
7959
|
+
issues.push({
|
|
7960
|
+
severity: "warning",
|
|
7961
|
+
message: `Broken link: ${link.text} \u2192 ${link.path}`,
|
|
7962
|
+
file: link.path
|
|
7963
|
+
});
|
|
7964
|
+
}
|
|
7965
|
+
}
|
|
7966
|
+
return issues;
|
|
7967
|
+
}
|
|
7968
|
+
async function runDepsCheck(projectRoot, config) {
|
|
7969
|
+
const issues = [];
|
|
7970
|
+
const rawLayers = config.layers;
|
|
7971
|
+
if (rawLayers && rawLayers.length > 0) {
|
|
7972
|
+
const parser = new TypeScriptParser();
|
|
7973
|
+
const layers = rawLayers.map(
|
|
7974
|
+
(l) => defineLayer(
|
|
7975
|
+
l.name,
|
|
7976
|
+
Array.isArray(l.patterns) ? l.patterns : [l.pattern],
|
|
7977
|
+
l.allowedDependencies
|
|
7978
|
+
)
|
|
7979
|
+
);
|
|
7980
|
+
const result = await validateDependencies({
|
|
7981
|
+
layers,
|
|
7982
|
+
rootDir: projectRoot,
|
|
7983
|
+
parser
|
|
7984
|
+
});
|
|
7985
|
+
if (!result.ok) {
|
|
7986
|
+
issues.push({ severity: "error", message: result.error.message });
|
|
7987
|
+
} else if (result.value.violations.length > 0) {
|
|
7988
|
+
for (const v of result.value.violations) {
|
|
7989
|
+
issues.push({
|
|
7990
|
+
severity: "error",
|
|
7991
|
+
message: `${v.reason}: ${v.file} imports ${v.imports} (${v.fromLayer} \u2192 ${v.toLayer})`,
|
|
7992
|
+
file: v.file,
|
|
7993
|
+
line: v.line
|
|
7994
|
+
});
|
|
7689
7995
|
}
|
|
7690
|
-
|
|
7691
|
-
|
|
7692
|
-
|
|
7693
|
-
|
|
7694
|
-
|
|
7695
|
-
|
|
7696
|
-
|
|
7697
|
-
|
|
7698
|
-
|
|
7699
|
-
|
|
7700
|
-
|
|
7701
|
-
|
|
7996
|
+
}
|
|
7997
|
+
}
|
|
7998
|
+
return issues;
|
|
7999
|
+
}
|
|
8000
|
+
async function runDocsCheck(projectRoot, config) {
|
|
8001
|
+
const issues = [];
|
|
8002
|
+
const docsDir = path12.join(projectRoot, config.docsDir ?? "docs");
|
|
8003
|
+
const entropyConfig = config.entropy || {};
|
|
8004
|
+
const result = await checkDocCoverage("project", {
|
|
8005
|
+
docsDir,
|
|
8006
|
+
sourceDir: projectRoot,
|
|
8007
|
+
excludePatterns: entropyConfig.excludePatterns || [
|
|
8008
|
+
"**/node_modules/**",
|
|
8009
|
+
"**/dist/**",
|
|
8010
|
+
"**/*.test.ts",
|
|
8011
|
+
"**/fixtures/**"
|
|
8012
|
+
]
|
|
8013
|
+
});
|
|
8014
|
+
if (!result.ok) {
|
|
8015
|
+
issues.push({ severity: "warning", message: result.error.message });
|
|
8016
|
+
} else if (result.value.gaps.length > 0) {
|
|
8017
|
+
for (const gap of result.value.gaps) {
|
|
8018
|
+
issues.push({
|
|
8019
|
+
severity: "warning",
|
|
8020
|
+
message: `Undocumented: ${gap.file} (suggested: ${gap.suggestedSection})`,
|
|
8021
|
+
file: gap.file
|
|
8022
|
+
});
|
|
8023
|
+
}
|
|
8024
|
+
}
|
|
8025
|
+
return issues;
|
|
8026
|
+
}
|
|
8027
|
+
async function runEntropyCheck(projectRoot, _config) {
|
|
8028
|
+
const issues = [];
|
|
8029
|
+
const analyzer = new EntropyAnalyzer({
|
|
8030
|
+
rootDir: projectRoot,
|
|
8031
|
+
analyze: { drift: true, deadCode: true, patterns: false }
|
|
8032
|
+
});
|
|
8033
|
+
const result = await analyzer.analyze();
|
|
8034
|
+
if (!result.ok) {
|
|
8035
|
+
issues.push({ severity: "warning", message: result.error.message });
|
|
8036
|
+
} else {
|
|
8037
|
+
const report = result.value;
|
|
8038
|
+
if (report.drift) {
|
|
8039
|
+
for (const drift of report.drift.drifts) {
|
|
8040
|
+
issues.push({
|
|
8041
|
+
severity: "warning",
|
|
8042
|
+
message: `Doc drift (${drift.type}): ${drift.details}`,
|
|
8043
|
+
file: drift.docFile,
|
|
8044
|
+
line: drift.line
|
|
7702
8045
|
});
|
|
7703
|
-
if (!result.ok) {
|
|
7704
|
-
issues.push({ severity: "warning", message: result.error.message });
|
|
7705
|
-
} else if (result.value.gaps.length > 0) {
|
|
7706
|
-
for (const gap of result.value.gaps) {
|
|
7707
|
-
issues.push({
|
|
7708
|
-
severity: "warning",
|
|
7709
|
-
message: `Undocumented: ${gap.file} (suggested: ${gap.suggestedSection})`,
|
|
7710
|
-
file: gap.file
|
|
7711
|
-
});
|
|
7712
|
-
}
|
|
7713
|
-
}
|
|
7714
|
-
break;
|
|
7715
8046
|
}
|
|
7716
|
-
|
|
7717
|
-
|
|
7718
|
-
|
|
7719
|
-
|
|
8047
|
+
}
|
|
8048
|
+
if (report.deadCode) {
|
|
8049
|
+
for (const dead of report.deadCode.deadExports) {
|
|
8050
|
+
issues.push({
|
|
8051
|
+
severity: "warning",
|
|
8052
|
+
message: `Dead export: ${dead.name}`,
|
|
8053
|
+
file: dead.file,
|
|
8054
|
+
line: dead.line
|
|
7720
8055
|
});
|
|
7721
|
-
const result = await analyzer.analyze();
|
|
7722
|
-
if (!result.ok) {
|
|
7723
|
-
issues.push({ severity: "warning", message: result.error.message });
|
|
7724
|
-
} else {
|
|
7725
|
-
const report = result.value;
|
|
7726
|
-
if (report.drift) {
|
|
7727
|
-
for (const drift of report.drift.drifts) {
|
|
7728
|
-
issues.push({
|
|
7729
|
-
severity: "warning",
|
|
7730
|
-
message: `Doc drift (${drift.type}): ${drift.details}`,
|
|
7731
|
-
file: drift.docFile,
|
|
7732
|
-
line: drift.line
|
|
7733
|
-
});
|
|
7734
|
-
}
|
|
7735
|
-
}
|
|
7736
|
-
if (report.deadCode) {
|
|
7737
|
-
for (const dead of report.deadCode.deadExports) {
|
|
7738
|
-
issues.push({
|
|
7739
|
-
severity: "warning",
|
|
7740
|
-
message: `Dead export: ${dead.name}`,
|
|
7741
|
-
file: dead.file,
|
|
7742
|
-
line: dead.line
|
|
7743
|
-
});
|
|
7744
|
-
}
|
|
7745
|
-
}
|
|
7746
|
-
}
|
|
7747
|
-
break;
|
|
7748
8056
|
}
|
|
7749
|
-
|
|
7750
|
-
|
|
7751
|
-
|
|
7752
|
-
|
|
7753
|
-
|
|
7754
|
-
|
|
7755
|
-
|
|
7756
|
-
|
|
7757
|
-
|
|
7758
|
-
|
|
7759
|
-
|
|
7760
|
-
|
|
7761
|
-
|
|
7762
|
-
|
|
7763
|
-
|
|
8057
|
+
}
|
|
8058
|
+
}
|
|
8059
|
+
return issues;
|
|
8060
|
+
}
|
|
8061
|
+
async function runSecurityCheck(projectRoot, config) {
|
|
8062
|
+
const issues = [];
|
|
8063
|
+
const securityConfig = parseSecurityConfig(config.security);
|
|
8064
|
+
if (!securityConfig.enabled) return issues;
|
|
8065
|
+
const scanner = new SecurityScanner(securityConfig);
|
|
8066
|
+
scanner.configureForProject(projectRoot);
|
|
8067
|
+
const { glob: globFn } = await import("glob");
|
|
8068
|
+
const sourceFiles = await globFn("**/*.{ts,tsx,js,jsx,go,py}", {
|
|
8069
|
+
cwd: projectRoot,
|
|
8070
|
+
ignore: securityConfig.exclude ?? [
|
|
8071
|
+
"**/node_modules/**",
|
|
8072
|
+
"**/dist/**",
|
|
8073
|
+
"**/*.test.ts",
|
|
8074
|
+
"**/fixtures/**"
|
|
8075
|
+
],
|
|
8076
|
+
absolute: true
|
|
8077
|
+
});
|
|
8078
|
+
const scanResult = await scanner.scanFiles(sourceFiles);
|
|
8079
|
+
for (const finding of scanResult.findings) {
|
|
8080
|
+
issues.push({
|
|
8081
|
+
severity: finding.severity === "info" ? "warning" : finding.severity,
|
|
8082
|
+
message: `[${finding.ruleId}] ${finding.message}: ${finding.match}`,
|
|
8083
|
+
file: finding.file,
|
|
8084
|
+
line: finding.line
|
|
8085
|
+
});
|
|
8086
|
+
}
|
|
8087
|
+
return issues;
|
|
8088
|
+
}
|
|
8089
|
+
async function runPerfCheck(projectRoot, config) {
|
|
8090
|
+
const issues = [];
|
|
8091
|
+
const perfConfig = config.performance || {};
|
|
8092
|
+
const perfAnalyzer = new EntropyAnalyzer({
|
|
8093
|
+
rootDir: projectRoot,
|
|
8094
|
+
analyze: {
|
|
8095
|
+
complexity: perfConfig.complexity || true,
|
|
8096
|
+
coupling: perfConfig.coupling || true,
|
|
8097
|
+
sizeBudget: perfConfig.sizeBudget || false
|
|
8098
|
+
}
|
|
8099
|
+
});
|
|
8100
|
+
const perfResult = await perfAnalyzer.analyze();
|
|
8101
|
+
if (!perfResult.ok) {
|
|
8102
|
+
issues.push({ severity: "warning", message: perfResult.error.message });
|
|
8103
|
+
} else {
|
|
8104
|
+
const perfReport = perfResult.value;
|
|
8105
|
+
if (perfReport.complexity) {
|
|
8106
|
+
for (const v of perfReport.complexity.violations) {
|
|
8107
|
+
issues.push({
|
|
8108
|
+
severity: v.severity === "info" ? "warning" : v.severity,
|
|
8109
|
+
message: `[Tier ${v.tier}] ${v.metric}: ${v.function} in ${v.file} (${v.value} > ${v.threshold})`,
|
|
8110
|
+
file: v.file,
|
|
8111
|
+
line: v.line
|
|
7764
8112
|
});
|
|
7765
|
-
const scanResult = await scanner.scanFiles(sourceFiles);
|
|
7766
|
-
for (const finding of scanResult.findings) {
|
|
7767
|
-
issues.push({
|
|
7768
|
-
severity: finding.severity === "info" ? "warning" : finding.severity,
|
|
7769
|
-
message: `[${finding.ruleId}] ${finding.message}: ${finding.match}`,
|
|
7770
|
-
file: finding.file,
|
|
7771
|
-
line: finding.line
|
|
7772
|
-
});
|
|
7773
|
-
}
|
|
7774
|
-
break;
|
|
7775
8113
|
}
|
|
7776
|
-
|
|
7777
|
-
|
|
7778
|
-
|
|
7779
|
-
|
|
7780
|
-
|
|
7781
|
-
|
|
7782
|
-
|
|
7783
|
-
sizeBudget: perfConfig.sizeBudget || false
|
|
7784
|
-
}
|
|
8114
|
+
}
|
|
8115
|
+
if (perfReport.coupling) {
|
|
8116
|
+
for (const v of perfReport.coupling.violations) {
|
|
8117
|
+
issues.push({
|
|
8118
|
+
severity: v.severity === "info" ? "warning" : v.severity,
|
|
8119
|
+
message: `[Tier ${v.tier}] ${v.metric}: ${v.file} (${v.value} > ${v.threshold})`,
|
|
8120
|
+
file: v.file
|
|
7785
8121
|
});
|
|
7786
|
-
const perfResult = await perfAnalyzer.analyze();
|
|
7787
|
-
if (!perfResult.ok) {
|
|
7788
|
-
issues.push({ severity: "warning", message: perfResult.error.message });
|
|
7789
|
-
} else {
|
|
7790
|
-
const perfReport = perfResult.value;
|
|
7791
|
-
if (perfReport.complexity) {
|
|
7792
|
-
for (const v of perfReport.complexity.violations) {
|
|
7793
|
-
issues.push({
|
|
7794
|
-
severity: v.severity === "info" ? "warning" : v.severity,
|
|
7795
|
-
message: `[Tier ${v.tier}] ${v.metric}: ${v.function} in ${v.file} (${v.value} > ${v.threshold})`,
|
|
7796
|
-
file: v.file,
|
|
7797
|
-
line: v.line
|
|
7798
|
-
});
|
|
7799
|
-
}
|
|
7800
|
-
}
|
|
7801
|
-
if (perfReport.coupling) {
|
|
7802
|
-
for (const v of perfReport.coupling.violations) {
|
|
7803
|
-
issues.push({
|
|
7804
|
-
severity: v.severity === "info" ? "warning" : v.severity,
|
|
7805
|
-
message: `[Tier ${v.tier}] ${v.metric}: ${v.file} (${v.value} > ${v.threshold})`,
|
|
7806
|
-
file: v.file
|
|
7807
|
-
});
|
|
7808
|
-
}
|
|
7809
|
-
}
|
|
7810
|
-
}
|
|
7811
|
-
break;
|
|
7812
8122
|
}
|
|
7813
|
-
|
|
7814
|
-
|
|
7815
|
-
|
|
7816
|
-
|
|
7817
|
-
|
|
8123
|
+
}
|
|
8124
|
+
}
|
|
8125
|
+
return issues;
|
|
8126
|
+
}
|
|
8127
|
+
async function runPhaseGateCheck(_projectRoot, config) {
|
|
8128
|
+
const issues = [];
|
|
8129
|
+
const phaseGates = config.phaseGates;
|
|
8130
|
+
if (!phaseGates?.enabled) {
|
|
8131
|
+
return issues;
|
|
8132
|
+
}
|
|
8133
|
+
issues.push({
|
|
8134
|
+
severity: "warning",
|
|
8135
|
+
message: "Phase gate is enabled but requires CLI context. Run `harness check-phase-gate` separately for full validation."
|
|
8136
|
+
});
|
|
8137
|
+
return issues;
|
|
8138
|
+
}
|
|
8139
|
+
async function runArchCheck(projectRoot, config) {
|
|
8140
|
+
const issues = [];
|
|
8141
|
+
const rawArchConfig = config.architecture;
|
|
8142
|
+
const archConfig = ArchConfigSchema.parse(rawArchConfig ?? {});
|
|
8143
|
+
if (!archConfig.enabled) return issues;
|
|
8144
|
+
const results = await runAll(archConfig, projectRoot);
|
|
8145
|
+
const baselineManager = new ArchBaselineManager(projectRoot, archConfig.baselinePath);
|
|
8146
|
+
const baseline = baselineManager.load();
|
|
8147
|
+
if (baseline) {
|
|
8148
|
+
const diffResult = diff(results, baseline);
|
|
8149
|
+
if (!diffResult.passed) {
|
|
8150
|
+
for (const v of diffResult.newViolations) {
|
|
7818
8151
|
issues.push({
|
|
7819
|
-
severity:
|
|
7820
|
-
message:
|
|
8152
|
+
severity: v.severity,
|
|
8153
|
+
message: `[${v.category || "arch"}] NEW: ${v.detail}`,
|
|
8154
|
+
file: v.file
|
|
7821
8155
|
});
|
|
7822
|
-
break;
|
|
7823
8156
|
}
|
|
7824
|
-
|
|
7825
|
-
|
|
7826
|
-
|
|
7827
|
-
|
|
7828
|
-
|
|
7829
|
-
|
|
7830
|
-
|
|
7831
|
-
|
|
7832
|
-
|
|
7833
|
-
|
|
7834
|
-
|
|
7835
|
-
|
|
7836
|
-
|
|
7837
|
-
|
|
7838
|
-
|
|
7839
|
-
});
|
|
7840
|
-
}
|
|
7841
|
-
for (const r of diffResult.regressions) {
|
|
7842
|
-
issues.push({
|
|
7843
|
-
severity: "error",
|
|
7844
|
-
message: `[${r.category}] REGRESSION: ${r.currentValue} > ${r.baselineValue} (delta: ${r.delta})`
|
|
7845
|
-
});
|
|
7846
|
-
}
|
|
7847
|
-
}
|
|
7848
|
-
} else {
|
|
7849
|
-
for (const result of results) {
|
|
7850
|
-
for (const v of result.violations) {
|
|
7851
|
-
issues.push({
|
|
7852
|
-
severity: v.severity,
|
|
7853
|
-
message: `[${result.category}] ${v.detail}`,
|
|
7854
|
-
file: v.file
|
|
7855
|
-
});
|
|
7856
|
-
}
|
|
7857
|
-
}
|
|
7858
|
-
}
|
|
7859
|
-
break;
|
|
8157
|
+
for (const r of diffResult.regressions) {
|
|
8158
|
+
issues.push({
|
|
8159
|
+
severity: "error",
|
|
8160
|
+
message: `[${r.category}] REGRESSION: ${r.currentValue} > ${r.baselineValue} (delta: ${r.delta})`
|
|
8161
|
+
});
|
|
8162
|
+
}
|
|
8163
|
+
}
|
|
8164
|
+
} else {
|
|
8165
|
+
for (const result of results) {
|
|
8166
|
+
for (const v of result.violations) {
|
|
8167
|
+
issues.push({
|
|
8168
|
+
severity: v.severity,
|
|
8169
|
+
message: `[${result.category}] ${v.detail}`,
|
|
8170
|
+
file: v.file
|
|
8171
|
+
});
|
|
7860
8172
|
}
|
|
7861
8173
|
}
|
|
8174
|
+
}
|
|
8175
|
+
return issues;
|
|
8176
|
+
}
|
|
8177
|
+
async function runSingleCheck(name, projectRoot, config) {
|
|
8178
|
+
const start = Date.now();
|
|
8179
|
+
const issues = [];
|
|
8180
|
+
try {
|
|
8181
|
+
switch (name) {
|
|
8182
|
+
case "validate":
|
|
8183
|
+
issues.push(...await runValidateCheck(projectRoot, config));
|
|
8184
|
+
break;
|
|
8185
|
+
case "deps":
|
|
8186
|
+
issues.push(...await runDepsCheck(projectRoot, config));
|
|
8187
|
+
break;
|
|
8188
|
+
case "docs":
|
|
8189
|
+
issues.push(...await runDocsCheck(projectRoot, config));
|
|
8190
|
+
break;
|
|
8191
|
+
case "entropy":
|
|
8192
|
+
issues.push(...await runEntropyCheck(projectRoot, config));
|
|
8193
|
+
break;
|
|
8194
|
+
case "security":
|
|
8195
|
+
issues.push(...await runSecurityCheck(projectRoot, config));
|
|
8196
|
+
break;
|
|
8197
|
+
case "perf":
|
|
8198
|
+
issues.push(...await runPerfCheck(projectRoot, config));
|
|
8199
|
+
break;
|
|
8200
|
+
case "phase-gate":
|
|
8201
|
+
issues.push(...await runPhaseGateCheck(projectRoot, config));
|
|
8202
|
+
break;
|
|
8203
|
+
case "arch":
|
|
8204
|
+
issues.push(...await runArchCheck(projectRoot, config));
|
|
8205
|
+
break;
|
|
8206
|
+
}
|
|
7862
8207
|
} catch (error) {
|
|
7863
8208
|
issues.push({
|
|
7864
8209
|
severity: "error",
|
|
@@ -7935,7 +8280,7 @@ async function runMechanicalChecks(options) {
|
|
|
7935
8280
|
};
|
|
7936
8281
|
if (!skip.includes("validate")) {
|
|
7937
8282
|
try {
|
|
7938
|
-
const agentsPath =
|
|
8283
|
+
const agentsPath = path13.join(projectRoot, config.agentsMapPath ?? "AGENTS.md");
|
|
7939
8284
|
const result = await validateAgentsMap(agentsPath);
|
|
7940
8285
|
if (!result.ok) {
|
|
7941
8286
|
statuses.validate = "fail";
|
|
@@ -7972,7 +8317,7 @@ async function runMechanicalChecks(options) {
|
|
|
7972
8317
|
statuses.validate = "fail";
|
|
7973
8318
|
findings.push({
|
|
7974
8319
|
tool: "validate",
|
|
7975
|
-
file:
|
|
8320
|
+
file: path13.join(projectRoot, "AGENTS.md"),
|
|
7976
8321
|
message: err instanceof Error ? err.message : String(err),
|
|
7977
8322
|
severity: "error"
|
|
7978
8323
|
});
|
|
@@ -8036,7 +8381,7 @@ async function runMechanicalChecks(options) {
|
|
|
8036
8381
|
(async () => {
|
|
8037
8382
|
const localFindings = [];
|
|
8038
8383
|
try {
|
|
8039
|
-
const docsDir =
|
|
8384
|
+
const docsDir = path13.join(projectRoot, config.docsDir ?? "docs");
|
|
8040
8385
|
const result = await checkDocCoverage("project", { docsDir });
|
|
8041
8386
|
if (!result.ok) {
|
|
8042
8387
|
statuses["check-docs"] = "warn";
|
|
@@ -8063,7 +8408,7 @@ async function runMechanicalChecks(options) {
|
|
|
8063
8408
|
statuses["check-docs"] = "warn";
|
|
8064
8409
|
localFindings.push({
|
|
8065
8410
|
tool: "check-docs",
|
|
8066
|
-
file:
|
|
8411
|
+
file: path13.join(projectRoot, "docs"),
|
|
8067
8412
|
message: err instanceof Error ? err.message : String(err),
|
|
8068
8413
|
severity: "warning"
|
|
8069
8414
|
});
|
|
@@ -8212,18 +8557,18 @@ function computeContextBudget(diffLines) {
|
|
|
8212
8557
|
return diffLines;
|
|
8213
8558
|
}
|
|
8214
8559
|
function isWithinProject(absPath, projectRoot) {
|
|
8215
|
-
const resolvedRoot =
|
|
8216
|
-
const resolvedPath =
|
|
8217
|
-
return resolvedPath.startsWith(resolvedRoot) || resolvedPath ===
|
|
8560
|
+
const resolvedRoot = path14.resolve(projectRoot) + path14.sep;
|
|
8561
|
+
const resolvedPath = path14.resolve(absPath);
|
|
8562
|
+
return resolvedPath.startsWith(resolvedRoot) || resolvedPath === path14.resolve(projectRoot);
|
|
8218
8563
|
}
|
|
8219
8564
|
async function readContextFile(projectRoot, filePath, reason) {
|
|
8220
|
-
const absPath =
|
|
8565
|
+
const absPath = path14.isAbsolute(filePath) ? filePath : path14.join(projectRoot, filePath);
|
|
8221
8566
|
if (!isWithinProject(absPath, projectRoot)) return null;
|
|
8222
8567
|
const result = await readFileContent(absPath);
|
|
8223
8568
|
if (!result.ok) return null;
|
|
8224
8569
|
const content = result.value;
|
|
8225
8570
|
const lines = content.split("\n").length;
|
|
8226
|
-
const relPath =
|
|
8571
|
+
const relPath = path14.isAbsolute(filePath) ? relativePosix(projectRoot, filePath) : filePath;
|
|
8227
8572
|
return { path: relPath, content, reason, lines };
|
|
8228
8573
|
}
|
|
8229
8574
|
function extractImportSources2(content) {
|
|
@@ -8238,18 +8583,18 @@ function extractImportSources2(content) {
|
|
|
8238
8583
|
}
|
|
8239
8584
|
async function resolveImportPath2(projectRoot, fromFile, importSource) {
|
|
8240
8585
|
if (!importSource.startsWith(".")) return null;
|
|
8241
|
-
const fromDir =
|
|
8242
|
-
const basePath =
|
|
8586
|
+
const fromDir = path14.dirname(path14.join(projectRoot, fromFile));
|
|
8587
|
+
const basePath = path14.resolve(fromDir, importSource);
|
|
8243
8588
|
if (!isWithinProject(basePath, projectRoot)) return null;
|
|
8244
|
-
const relBase =
|
|
8589
|
+
const relBase = relativePosix(projectRoot, basePath);
|
|
8245
8590
|
const candidates = [
|
|
8246
8591
|
relBase + ".ts",
|
|
8247
8592
|
relBase + ".tsx",
|
|
8248
8593
|
relBase + ".mts",
|
|
8249
|
-
|
|
8594
|
+
path14.join(relBase, "index.ts")
|
|
8250
8595
|
];
|
|
8251
8596
|
for (const candidate of candidates) {
|
|
8252
|
-
const absCandidate =
|
|
8597
|
+
const absCandidate = path14.join(projectRoot, candidate);
|
|
8253
8598
|
if (await fileExists(absCandidate)) {
|
|
8254
8599
|
return candidate;
|
|
8255
8600
|
}
|
|
@@ -8257,10 +8602,10 @@ async function resolveImportPath2(projectRoot, fromFile, importSource) {
|
|
|
8257
8602
|
return null;
|
|
8258
8603
|
}
|
|
8259
8604
|
async function findTestFiles(projectRoot, sourceFile) {
|
|
8260
|
-
const baseName =
|
|
8605
|
+
const baseName = path14.basename(sourceFile, path14.extname(sourceFile));
|
|
8261
8606
|
const pattern = `**/${baseName}.{test,spec}.{ts,tsx,mts}`;
|
|
8262
8607
|
const results = await findFiles(pattern, projectRoot);
|
|
8263
|
-
return results.map((f) =>
|
|
8608
|
+
return results.map((f) => relativePosix(projectRoot, f));
|
|
8264
8609
|
}
|
|
8265
8610
|
async function gatherImportContext(projectRoot, changedFiles, budget) {
|
|
8266
8611
|
const contextFiles = [];
|
|
@@ -9052,7 +9397,7 @@ function normalizePath(filePath, projectRoot) {
|
|
|
9052
9397
|
let normalized = filePath;
|
|
9053
9398
|
normalized = normalized.replace(/\\/g, "/");
|
|
9054
9399
|
const normalizedRoot = projectRoot.replace(/\\/g, "/");
|
|
9055
|
-
if (
|
|
9400
|
+
if (path15.isAbsolute(normalized)) {
|
|
9056
9401
|
const root = normalizedRoot.endsWith("/") ? normalizedRoot : normalizedRoot + "/";
|
|
9057
9402
|
if (normalized.startsWith(root)) {
|
|
9058
9403
|
normalized = normalized.slice(root.length);
|
|
@@ -9077,12 +9422,12 @@ function followImportChain(fromFile, fileContents, maxDepth = 2) {
|
|
|
9077
9422
|
while ((match = importRegex.exec(content)) !== null) {
|
|
9078
9423
|
const importPath = match[1];
|
|
9079
9424
|
if (!importPath.startsWith(".")) continue;
|
|
9080
|
-
const dir =
|
|
9081
|
-
let resolved =
|
|
9425
|
+
const dir = path15.dirname(current.file);
|
|
9426
|
+
let resolved = path15.join(dir, importPath).replace(/\\/g, "/");
|
|
9082
9427
|
if (!resolved.match(/\.(ts|tsx|js|jsx)$/)) {
|
|
9083
9428
|
resolved += ".ts";
|
|
9084
9429
|
}
|
|
9085
|
-
resolved =
|
|
9430
|
+
resolved = path15.normalize(resolved).replace(/\\/g, "/");
|
|
9086
9431
|
if (!visited.has(resolved) && current.depth + 1 <= maxDepth) {
|
|
9087
9432
|
queue.push({ file: resolved, depth: current.depth + 1 });
|
|
9088
9433
|
}
|
|
@@ -9099,7 +9444,7 @@ async function validateFindings(options) {
|
|
|
9099
9444
|
if (exclusionSet.isExcluded(normalizedFile, finding.lineRange) || exclusionSet.isExcluded(finding.file, finding.lineRange)) {
|
|
9100
9445
|
continue;
|
|
9101
9446
|
}
|
|
9102
|
-
const absoluteFile =
|
|
9447
|
+
const absoluteFile = path15.isAbsolute(finding.file) ? finding.file : path15.join(projectRoot, finding.file).replace(/\\/g, "/");
|
|
9103
9448
|
if (exclusionSet.isExcluded(absoluteFile, finding.lineRange)) {
|
|
9104
9449
|
continue;
|
|
9105
9450
|
}
|
|
@@ -9603,6 +9948,8 @@ function parseFrontmatter(raw) {
|
|
|
9603
9948
|
const versionStr = map.get("version");
|
|
9604
9949
|
const lastSynced = map.get("last_synced");
|
|
9605
9950
|
const lastManualEdit = map.get("last_manual_edit");
|
|
9951
|
+
const created = map.get("created");
|
|
9952
|
+
const updated = map.get("updated");
|
|
9606
9953
|
if (!project || !versionStr || !lastSynced || !lastManualEdit) {
|
|
9607
9954
|
return Err(
|
|
9608
9955
|
new Error(
|
|
@@ -9614,7 +9961,10 @@ function parseFrontmatter(raw) {
|
|
|
9614
9961
|
if (isNaN(version)) {
|
|
9615
9962
|
return Err(new Error("Frontmatter version must be a number"));
|
|
9616
9963
|
}
|
|
9617
|
-
|
|
9964
|
+
const fm = { project, version, lastSynced, lastManualEdit };
|
|
9965
|
+
if (created) fm.created = created;
|
|
9966
|
+
if (updated) fm.updated = updated;
|
|
9967
|
+
return Ok(fm);
|
|
9618
9968
|
}
|
|
9619
9969
|
function parseMilestones(body) {
|
|
9620
9970
|
const milestones = [];
|
|
@@ -9622,12 +9972,12 @@ function parseMilestones(body) {
|
|
|
9622
9972
|
const h2Matches = [];
|
|
9623
9973
|
let match;
|
|
9624
9974
|
while ((match = h2Pattern.exec(body)) !== null) {
|
|
9625
|
-
h2Matches.push({ heading: match[1], startIndex: match.index });
|
|
9975
|
+
h2Matches.push({ heading: match[1], startIndex: match.index, fullMatch: match[0] });
|
|
9626
9976
|
}
|
|
9627
9977
|
for (let i = 0; i < h2Matches.length; i++) {
|
|
9628
9978
|
const h2 = h2Matches[i];
|
|
9629
9979
|
const nextStart = i + 1 < h2Matches.length ? h2Matches[i + 1].startIndex : body.length;
|
|
9630
|
-
const sectionBody = body.slice(h2.startIndex + h2.
|
|
9980
|
+
const sectionBody = body.slice(h2.startIndex + h2.fullMatch.length, nextStart);
|
|
9631
9981
|
const isBacklog = h2.heading === "Backlog";
|
|
9632
9982
|
const milestoneName = isBacklog ? "Backlog" : h2.heading.replace(/^Milestone:\s*/, "");
|
|
9633
9983
|
const featuresResult = parseFeatures(sectionBody);
|
|
@@ -9642,19 +9992,16 @@ function parseMilestones(body) {
|
|
|
9642
9992
|
}
|
|
9643
9993
|
function parseFeatures(sectionBody) {
|
|
9644
9994
|
const features = [];
|
|
9645
|
-
const h3Pattern = /^### Feature: (.+)$/gm;
|
|
9995
|
+
const h3Pattern = /^### (?:Feature: )?(.+)$/gm;
|
|
9646
9996
|
const h3Matches = [];
|
|
9647
9997
|
let match;
|
|
9648
9998
|
while ((match = h3Pattern.exec(sectionBody)) !== null) {
|
|
9649
|
-
h3Matches.push({ name: match[1], startIndex: match.index });
|
|
9999
|
+
h3Matches.push({ name: match[1], startIndex: match.index, fullMatch: match[0] });
|
|
9650
10000
|
}
|
|
9651
10001
|
for (let i = 0; i < h3Matches.length; i++) {
|
|
9652
10002
|
const h3 = h3Matches[i];
|
|
9653
10003
|
const nextStart = i + 1 < h3Matches.length ? h3Matches[i + 1].startIndex : sectionBody.length;
|
|
9654
|
-
const featureBody = sectionBody.slice(
|
|
9655
|
-
h3.startIndex + `### Feature: ${h3.name}`.length,
|
|
9656
|
-
nextStart
|
|
9657
|
-
);
|
|
10004
|
+
const featureBody = sectionBody.slice(h3.startIndex + h3.fullMatch.length, nextStart);
|
|
9658
10005
|
const featureResult = parseFeatureFields(h3.name, featureBody);
|
|
9659
10006
|
if (!featureResult.ok) return featureResult;
|
|
9660
10007
|
features.push(featureResult.value);
|
|
@@ -9679,10 +10026,10 @@ function parseFeatureFields(name, body) {
|
|
|
9679
10026
|
const status = statusRaw;
|
|
9680
10027
|
const specRaw = fieldMap.get("Spec") ?? EM_DASH;
|
|
9681
10028
|
const spec = specRaw === EM_DASH ? null : specRaw;
|
|
9682
|
-
const plansRaw = fieldMap.get("Plans") ?? EM_DASH;
|
|
9683
|
-
const plans = plansRaw === EM_DASH ? [] : plansRaw.split(",").map((p) => p.trim());
|
|
9684
|
-
const blockedByRaw = fieldMap.get("Blocked by") ?? EM_DASH;
|
|
9685
|
-
const blockedBy = blockedByRaw === EM_DASH ? [] : blockedByRaw.split(",").map((b) => b.trim());
|
|
10029
|
+
const plansRaw = fieldMap.get("Plans") ?? fieldMap.get("Plan") ?? EM_DASH;
|
|
10030
|
+
const plans = plansRaw === EM_DASH || plansRaw === "none" ? [] : plansRaw.split(",").map((p) => p.trim());
|
|
10031
|
+
const blockedByRaw = fieldMap.get("Blocked by") ?? fieldMap.get("Blockers") ?? EM_DASH;
|
|
10032
|
+
const blockedBy = blockedByRaw === EM_DASH || blockedByRaw === "none" ? [] : blockedByRaw.split(",").map((b) => b.trim());
|
|
9686
10033
|
const summary = fieldMap.get("Summary") ?? "";
|
|
9687
10034
|
return Ok({ name, status, spec, plans, blockedBy, summary });
|
|
9688
10035
|
}
|
|
@@ -9692,11 +10039,17 @@ function serializeRoadmap(roadmap) {
|
|
|
9692
10039
|
lines.push("---");
|
|
9693
10040
|
lines.push(`project: ${roadmap.frontmatter.project}`);
|
|
9694
10041
|
lines.push(`version: ${roadmap.frontmatter.version}`);
|
|
10042
|
+
if (roadmap.frontmatter.created) {
|
|
10043
|
+
lines.push(`created: ${roadmap.frontmatter.created}`);
|
|
10044
|
+
}
|
|
10045
|
+
if (roadmap.frontmatter.updated) {
|
|
10046
|
+
lines.push(`updated: ${roadmap.frontmatter.updated}`);
|
|
10047
|
+
}
|
|
9695
10048
|
lines.push(`last_synced: ${roadmap.frontmatter.lastSynced}`);
|
|
9696
10049
|
lines.push(`last_manual_edit: ${roadmap.frontmatter.lastManualEdit}`);
|
|
9697
10050
|
lines.push("---");
|
|
9698
10051
|
lines.push("");
|
|
9699
|
-
lines.push("#
|
|
10052
|
+
lines.push("# Roadmap");
|
|
9700
10053
|
for (const milestone of roadmap.milestones) {
|
|
9701
10054
|
lines.push("");
|
|
9702
10055
|
lines.push(serializeMilestoneHeading(milestone));
|
|
@@ -9709,19 +10062,20 @@ function serializeRoadmap(roadmap) {
|
|
|
9709
10062
|
return lines.join("\n");
|
|
9710
10063
|
}
|
|
9711
10064
|
function serializeMilestoneHeading(milestone) {
|
|
9712
|
-
return milestone.isBacklog ? "## Backlog" : `##
|
|
10065
|
+
return milestone.isBacklog ? "## Backlog" : `## ${milestone.name}`;
|
|
9713
10066
|
}
|
|
9714
10067
|
function serializeFeature(feature) {
|
|
9715
10068
|
const spec = feature.spec ?? EM_DASH2;
|
|
9716
10069
|
const plans = feature.plans.length > 0 ? feature.plans.join(", ") : EM_DASH2;
|
|
9717
10070
|
const blockedBy = feature.blockedBy.length > 0 ? feature.blockedBy.join(", ") : EM_DASH2;
|
|
9718
10071
|
return [
|
|
9719
|
-
`###
|
|
10072
|
+
`### ${feature.name}`,
|
|
10073
|
+
"",
|
|
9720
10074
|
`- **Status:** ${feature.status}`,
|
|
9721
10075
|
`- **Spec:** ${spec}`,
|
|
9722
|
-
`- **
|
|
9723
|
-
`- **
|
|
9724
|
-
`- **
|
|
10076
|
+
`- **Summary:** ${feature.summary}`,
|
|
10077
|
+
`- **Blockers:** ${blockedBy}`,
|
|
10078
|
+
`- **Plan:** ${plans}`
|
|
9725
10079
|
];
|
|
9726
10080
|
}
|
|
9727
10081
|
function inferStatus(feature, projectPath, allFeatures) {
|
|
@@ -9737,10 +10091,10 @@ function inferStatus(feature, projectPath, allFeatures) {
|
|
|
9737
10091
|
const featuresWithPlans = allFeatures.filter((f) => f.plans.length > 0);
|
|
9738
10092
|
const useRootState = featuresWithPlans.length <= 1;
|
|
9739
10093
|
if (useRootState) {
|
|
9740
|
-
const rootStatePath =
|
|
9741
|
-
if (
|
|
10094
|
+
const rootStatePath = path16.join(projectPath, ".harness", "state.json");
|
|
10095
|
+
if (fs16.existsSync(rootStatePath)) {
|
|
9742
10096
|
try {
|
|
9743
|
-
const raw =
|
|
10097
|
+
const raw = fs16.readFileSync(rootStatePath, "utf-8");
|
|
9744
10098
|
const state = JSON.parse(raw);
|
|
9745
10099
|
if (state.progress) {
|
|
9746
10100
|
for (const status of Object.values(state.progress)) {
|
|
@@ -9751,16 +10105,16 @@ function inferStatus(feature, projectPath, allFeatures) {
|
|
|
9751
10105
|
}
|
|
9752
10106
|
}
|
|
9753
10107
|
}
|
|
9754
|
-
const sessionsDir =
|
|
9755
|
-
if (
|
|
10108
|
+
const sessionsDir = path16.join(projectPath, ".harness", "sessions");
|
|
10109
|
+
if (fs16.existsSync(sessionsDir)) {
|
|
9756
10110
|
try {
|
|
9757
|
-
const sessionDirs =
|
|
10111
|
+
const sessionDirs = fs16.readdirSync(sessionsDir, { withFileTypes: true });
|
|
9758
10112
|
for (const entry of sessionDirs) {
|
|
9759
10113
|
if (!entry.isDirectory()) continue;
|
|
9760
|
-
const autopilotPath =
|
|
9761
|
-
if (!
|
|
10114
|
+
const autopilotPath = path16.join(sessionsDir, entry.name, "autopilot-state.json");
|
|
10115
|
+
if (!fs16.existsSync(autopilotPath)) continue;
|
|
9762
10116
|
try {
|
|
9763
|
-
const raw =
|
|
10117
|
+
const raw = fs16.readFileSync(autopilotPath, "utf-8");
|
|
9764
10118
|
const autopilot = JSON.parse(raw);
|
|
9765
10119
|
if (!autopilot.phases) continue;
|
|
9766
10120
|
const linkedPhases = autopilot.phases.filter(
|
|
@@ -9840,10 +10194,10 @@ var ProjectScanner = class {
|
|
|
9840
10194
|
this.rootDir = rootDir;
|
|
9841
10195
|
}
|
|
9842
10196
|
async scan() {
|
|
9843
|
-
let projectName =
|
|
10197
|
+
let projectName = path17.basename(this.rootDir);
|
|
9844
10198
|
try {
|
|
9845
|
-
const pkgPath =
|
|
9846
|
-
const pkgRaw = await
|
|
10199
|
+
const pkgPath = path17.join(this.rootDir, "package.json");
|
|
10200
|
+
const pkgRaw = await fs17.readFile(pkgPath, "utf-8");
|
|
9847
10201
|
const pkg = JSON.parse(pkgRaw);
|
|
9848
10202
|
if (pkg.name) projectName = pkg.name;
|
|
9849
10203
|
} catch {
|
|
@@ -9956,13 +10310,13 @@ var BlueprintGenerator = class {
|
|
|
9956
10310
|
styles: STYLES,
|
|
9957
10311
|
scripts: SCRIPTS
|
|
9958
10312
|
});
|
|
9959
|
-
await
|
|
9960
|
-
await
|
|
10313
|
+
await fs18.mkdir(options.outputDir, { recursive: true });
|
|
10314
|
+
await fs18.writeFile(path18.join(options.outputDir, "index.html"), html);
|
|
9961
10315
|
}
|
|
9962
10316
|
};
|
|
9963
10317
|
function getStatePath() {
|
|
9964
10318
|
const home = process.env["HOME"] || os.homedir();
|
|
9965
|
-
return
|
|
10319
|
+
return path19.join(home, ".harness", "update-check.json");
|
|
9966
10320
|
}
|
|
9967
10321
|
function isUpdateCheckEnabled(configInterval) {
|
|
9968
10322
|
if (process.env["HARNESS_NO_UPDATE_CHECK"] === "1") return false;
|
|
@@ -9975,7 +10329,7 @@ function shouldRunCheck(state, intervalMs) {
|
|
|
9975
10329
|
}
|
|
9976
10330
|
function readCheckState() {
|
|
9977
10331
|
try {
|
|
9978
|
-
const raw =
|
|
10332
|
+
const raw = fs19.readFileSync(getStatePath(), "utf-8");
|
|
9979
10333
|
const parsed = JSON.parse(raw);
|
|
9980
10334
|
if (typeof parsed === "object" && parsed !== null && "lastCheckTime" in parsed && typeof parsed.lastCheckTime === "number" && "currentVersion" in parsed && typeof parsed.currentVersion === "string") {
|
|
9981
10335
|
const state = parsed;
|
|
@@ -9992,7 +10346,7 @@ function readCheckState() {
|
|
|
9992
10346
|
}
|
|
9993
10347
|
function spawnBackgroundCheck(currentVersion) {
|
|
9994
10348
|
const statePath = getStatePath();
|
|
9995
|
-
const stateDir =
|
|
10349
|
+
const stateDir = path19.dirname(statePath);
|
|
9996
10350
|
const script = `
|
|
9997
10351
|
const { execSync } = require('child_process');
|
|
9998
10352
|
const fs = require('fs');
|
|
@@ -10044,7 +10398,7 @@ function getUpdateNotification(currentVersion) {
|
|
|
10044
10398
|
return `Update available: v${currentVersion} -> v${state.latestVersion}
|
|
10045
10399
|
Run "harness update" to upgrade.`;
|
|
10046
10400
|
}
|
|
10047
|
-
var VERSION = "0.
|
|
10401
|
+
var VERSION = "0.13.0";
|
|
10048
10402
|
|
|
10049
10403
|
export {
|
|
10050
10404
|
ArchMetricCategorySchema,
|
|
@@ -10182,16 +10536,28 @@ export {
|
|
|
10182
10536
|
archiveStream,
|
|
10183
10537
|
getStreamForBranch,
|
|
10184
10538
|
migrateToStreams,
|
|
10539
|
+
resolveSessionDir,
|
|
10540
|
+
updateSessionIndex,
|
|
10185
10541
|
loadState,
|
|
10186
10542
|
saveState,
|
|
10543
|
+
clearLearningsCache,
|
|
10187
10544
|
appendLearning,
|
|
10545
|
+
parseDateFromEntry,
|
|
10546
|
+
analyzeLearningPatterns,
|
|
10547
|
+
loadBudgetedLearnings,
|
|
10188
10548
|
loadRelevantLearnings,
|
|
10549
|
+
archiveLearnings,
|
|
10550
|
+
pruneLearnings,
|
|
10551
|
+
clearFailuresCache,
|
|
10189
10552
|
appendFailure,
|
|
10190
10553
|
loadFailures,
|
|
10191
10554
|
archiveFailures,
|
|
10192
10555
|
saveHandoff,
|
|
10193
10556
|
loadHandoff,
|
|
10194
10557
|
runMechanicalGate,
|
|
10558
|
+
writeSessionSummary,
|
|
10559
|
+
loadSessionSummary,
|
|
10560
|
+
listActiveSessions,
|
|
10195
10561
|
executeWorkflow,
|
|
10196
10562
|
runPipeline,
|
|
10197
10563
|
runMultiTurnPipeline,
|