@harness-engineering/cli 1.15.0 → 1.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agents/commands/codex/AGENTS.md +39 -0
- package/dist/agents/commands/codex/harness/add-harness-component/SKILL.md +195 -0
- package/dist/agents/commands/codex/harness/add-harness-component/agents/openai.yaml +3 -0
- package/dist/agents/commands/codex/harness/cleanup-dead-code/SKILL.md +248 -0
- package/dist/agents/commands/codex/harness/cleanup-dead-code/agents/openai.yaml +3 -0
- package/dist/agents/commands/codex/harness/detect-doc-drift/SKILL.md +182 -0
- package/dist/agents/commands/codex/harness/detect-doc-drift/agents/openai.yaml +3 -0
- package/dist/agents/commands/codex/harness/enforce-architecture/SKILL.md +299 -0
- package/dist/agents/commands/codex/harness/enforce-architecture/agents/openai.yaml +3 -0
- package/dist/agents/commands/codex/harness/harness-architecture-advisor/SKILL.md +452 -0
- package/dist/agents/commands/codex/harness/harness-architecture-advisor/agents/openai.yaml +3 -0
- package/dist/agents/commands/codex/harness/harness-autopilot/SKILL.md +919 -0
- package/dist/agents/commands/codex/harness/harness-autopilot/agents/openai.yaml +3 -0
- package/dist/agents/commands/codex/harness/harness-brainstorming/SKILL.md +409 -0
- package/dist/agents/commands/codex/harness/harness-brainstorming/agents/openai.yaml +3 -0
- package/dist/agents/commands/codex/harness/harness-code-review/SKILL.md +860 -0
- package/dist/agents/commands/codex/harness/harness-code-review/agents/openai.yaml +3 -0
- package/dist/agents/commands/codex/harness/harness-codebase-cleanup/SKILL.md +227 -0
- package/dist/agents/commands/codex/harness/harness-codebase-cleanup/agents/openai.yaml +3 -0
- package/dist/agents/commands/codex/harness/harness-debugging/SKILL.md +369 -0
- package/dist/agents/commands/codex/harness/harness-debugging/agents/openai.yaml +3 -0
- package/dist/agents/commands/codex/harness/harness-dependency-health/SKILL.md +182 -0
- package/dist/agents/commands/codex/harness/harness-dependency-health/agents/openai.yaml +3 -0
- package/dist/agents/commands/codex/harness/harness-docs-pipeline/SKILL.md +463 -0
- package/dist/agents/commands/codex/harness/harness-docs-pipeline/agents/openai.yaml +3 -0
- package/dist/agents/commands/codex/harness/harness-execution/SKILL.md +513 -0
- package/dist/agents/commands/codex/harness/harness-execution/agents/openai.yaml +3 -0
- package/dist/agents/commands/codex/harness/harness-hotspot-detector/SKILL.md +164 -0
- package/dist/agents/commands/codex/harness/harness-hotspot-detector/agents/openai.yaml +3 -0
- package/dist/agents/commands/codex/harness/harness-impact-analysis/SKILL.md +187 -0
- package/dist/agents/commands/codex/harness/harness-impact-analysis/agents/openai.yaml +3 -0
- package/dist/agents/commands/codex/harness/harness-integrity/SKILL.md +170 -0
- package/dist/agents/commands/codex/harness/harness-integrity/agents/openai.yaml +3 -0
- package/dist/agents/commands/codex/harness/harness-onboarding/SKILL.md +291 -0
- package/dist/agents/commands/codex/harness/harness-onboarding/agents/openai.yaml +3 -0
- package/dist/agents/commands/codex/harness/harness-perf/SKILL.md +263 -0
- package/dist/agents/commands/codex/harness/harness-perf/agents/openai.yaml +3 -0
- package/dist/agents/commands/codex/harness/harness-planning/SKILL.md +582 -0
- package/dist/agents/commands/codex/harness/harness-planning/agents/openai.yaml +3 -0
- package/dist/agents/commands/codex/harness/harness-refactoring/SKILL.md +172 -0
- package/dist/agents/commands/codex/harness/harness-refactoring/agents/openai.yaml +3 -0
- package/dist/agents/commands/codex/harness/harness-release-readiness/SKILL.md +692 -0
- package/dist/agents/commands/codex/harness/harness-release-readiness/agents/openai.yaml +3 -0
- package/dist/agents/commands/codex/harness/harness-roadmap/SKILL.md +598 -0
- package/dist/agents/commands/codex/harness/harness-roadmap/agents/openai.yaml +3 -0
- package/dist/agents/commands/codex/harness/harness-security-scan/SKILL.md +157 -0
- package/dist/agents/commands/codex/harness/harness-security-scan/agents/openai.yaml +3 -0
- package/dist/agents/commands/codex/harness/harness-skill-authoring/SKILL.md +295 -0
- package/dist/agents/commands/codex/harness/harness-skill-authoring/agents/openai.yaml +3 -0
- package/dist/agents/commands/codex/harness/harness-soundness-review/SKILL.md +1270 -0
- package/dist/agents/commands/codex/harness/harness-soundness-review/agents/openai.yaml +3 -0
- package/dist/agents/commands/codex/harness/harness-supply-chain-audit/SKILL.md +247 -0
- package/dist/agents/commands/codex/harness/harness-supply-chain-audit/agents/openai.yaml +3 -0
- package/dist/agents/commands/codex/harness/harness-tdd/SKILL.md +180 -0
- package/dist/agents/commands/codex/harness/harness-tdd/agents/openai.yaml +3 -0
- package/dist/agents/commands/codex/harness/harness-test-advisor/SKILL.md +163 -0
- package/dist/agents/commands/codex/harness/harness-test-advisor/agents/openai.yaml +3 -0
- package/dist/agents/commands/codex/harness/harness-verification/SKILL.md +424 -0
- package/dist/agents/commands/codex/harness/harness-verification/agents/openai.yaml +3 -0
- package/dist/agents/commands/codex/harness/harness-verify/SKILL.md +162 -0
- package/dist/agents/commands/codex/harness/harness-verify/agents/openai.yaml +3 -0
- package/dist/agents/commands/codex/harness/initialize-harness-project/SKILL.md +235 -0
- package/dist/agents/commands/codex/harness/initialize-harness-project/agents/openai.yaml +3 -0
- package/dist/agents/commands/cursor/harness/add-harness-component.mdc +200 -0
- package/dist/agents/commands/cursor/harness/cleanup-dead-code.mdc +253 -0
- package/dist/agents/commands/cursor/harness/detect-doc-drift.mdc +187 -0
- package/dist/agents/commands/cursor/harness/enforce-architecture.mdc +304 -0
- package/dist/agents/commands/cursor/harness/harness-architecture-advisor.mdc +457 -0
- package/dist/agents/commands/cursor/harness/harness-autopilot.mdc +924 -0
- package/dist/agents/commands/cursor/harness/harness-brainstorming.mdc +414 -0
- package/dist/agents/commands/cursor/harness/harness-code-review.mdc +865 -0
- package/dist/agents/commands/cursor/harness/harness-codebase-cleanup.mdc +232 -0
- package/dist/agents/commands/cursor/harness/harness-debugging.mdc +374 -0
- package/dist/agents/commands/cursor/harness/harness-dependency-health.mdc +187 -0
- package/dist/agents/commands/cursor/harness/harness-docs-pipeline.mdc +468 -0
- package/dist/agents/commands/cursor/harness/harness-execution.mdc +518 -0
- package/dist/agents/commands/cursor/harness/harness-hotspot-detector.mdc +169 -0
- package/dist/agents/commands/cursor/harness/harness-impact-analysis.mdc +192 -0
- package/dist/agents/commands/cursor/harness/harness-integrity.mdc +175 -0
- package/dist/agents/commands/cursor/harness/harness-onboarding.mdc +296 -0
- package/dist/agents/commands/cursor/harness/harness-perf.mdc +268 -0
- package/dist/agents/commands/cursor/harness/harness-planning.mdc +587 -0
- package/dist/agents/commands/cursor/harness/harness-refactoring.mdc +177 -0
- package/dist/agents/commands/cursor/harness/harness-release-readiness.mdc +697 -0
- package/dist/agents/commands/cursor/harness/harness-roadmap.mdc +603 -0
- package/dist/agents/commands/cursor/harness/harness-security-scan.mdc +162 -0
- package/dist/agents/commands/cursor/harness/harness-skill-authoring.mdc +300 -0
- package/dist/agents/commands/cursor/harness/harness-soundness-review.mdc +1275 -0
- package/dist/agents/commands/cursor/harness/harness-supply-chain-audit.mdc +252 -0
- package/dist/agents/commands/cursor/harness/harness-tdd.mdc +185 -0
- package/dist/agents/commands/cursor/harness/harness-test-advisor.mdc +168 -0
- package/dist/agents/commands/cursor/harness/harness-verification.mdc +429 -0
- package/dist/agents/commands/cursor/harness/harness-verify.mdc +167 -0
- package/dist/agents/commands/cursor/harness/initialize-harness-project.mdc +240 -0
- package/dist/agents/skills/claude-code/enforce-architecture/SKILL.md +52 -0
- package/dist/agents/skills/claude-code/harness-api-design/SKILL.md +52 -0
- package/dist/agents/skills/claude-code/harness-architecture-advisor/SKILL.md +52 -0
- package/dist/agents/skills/claude-code/harness-auth/SKILL.md +52 -0
- package/dist/agents/skills/claude-code/harness-autopilot/SKILL.md +123 -14
- package/dist/agents/skills/claude-code/harness-autopilot/skill.yaml +6 -0
- package/dist/agents/skills/claude-code/harness-code-review/SKILL.md +97 -3
- package/dist/agents/skills/claude-code/harness-code-review/skill.yaml +6 -0
- package/dist/agents/skills/claude-code/harness-codebase-cleanup/SKILL.md +2 -4
- package/dist/agents/skills/claude-code/harness-database/SKILL.md +52 -0
- package/dist/agents/skills/claude-code/harness-deployment/SKILL.md +52 -0
- package/dist/agents/skills/claude-code/harness-planning/SKILL.md +99 -3
- package/dist/agents/skills/claude-code/harness-planning/skill.yaml +6 -0
- package/dist/agents/skills/claude-code/harness-pre-commit-review/SKILL.md +1 -1
- package/dist/agents/skills/claude-code/harness-roadmap-pilot/SKILL.md +204 -0
- package/dist/agents/skills/claude-code/harness-roadmap-pilot/skill.yaml +52 -0
- package/dist/agents/skills/claude-code/harness-security-review/SKILL.md +27 -7
- package/dist/agents/skills/claude-code/harness-security-scan/SKILL.md +52 -0
- package/dist/agents/skills/claude-code/harness-supply-chain-audit/SKILL.md +281 -0
- package/dist/agents/skills/claude-code/harness-supply-chain-audit/skill.yaml +51 -0
- package/dist/agents/skills/codex/add-harness-component/SKILL.md +192 -0
- package/dist/agents/skills/codex/add-harness-component/skill.yaml +33 -0
- package/dist/agents/skills/codex/align-documentation/SKILL.md +213 -0
- package/dist/agents/skills/codex/align-documentation/skill.yaml +32 -0
- package/dist/agents/skills/codex/check-mechanical-constraints/SKILL.md +191 -0
- package/dist/agents/skills/codex/check-mechanical-constraints/skill.yaml +33 -0
- package/dist/agents/skills/codex/cleanup-dead-code/SKILL.md +245 -0
- package/dist/agents/skills/codex/cleanup-dead-code/skill.yaml +34 -0
- package/dist/agents/skills/codex/detect-doc-drift/SKILL.md +179 -0
- package/dist/agents/skills/codex/detect-doc-drift/skill.yaml +31 -0
- package/dist/agents/skills/codex/enforce-architecture/SKILL.md +296 -0
- package/dist/agents/skills/codex/enforce-architecture/skill.yaml +35 -0
- package/dist/agents/skills/codex/harness-accessibility/SKILL.md +281 -0
- package/dist/agents/skills/codex/harness-accessibility/skill.yaml +52 -0
- package/dist/agents/skills/codex/harness-api-design/SKILL.md +356 -0
- package/dist/agents/skills/codex/harness-api-design/skill.yaml +74 -0
- package/dist/agents/skills/codex/harness-architecture-advisor/SKILL.md +449 -0
- package/dist/agents/skills/codex/harness-architecture-advisor/skill.yaml +49 -0
- package/dist/agents/skills/codex/harness-auth/SKILL.md +331 -0
- package/dist/agents/skills/codex/harness-auth/skill.yaml +81 -0
- package/dist/agents/skills/codex/harness-autopilot/SKILL.md +916 -0
- package/dist/agents/skills/codex/harness-autopilot/skill.yaml +67 -0
- package/dist/agents/skills/codex/harness-brainstorming/SKILL.md +406 -0
- package/dist/agents/skills/codex/harness-brainstorming/skill.yaml +50 -0
- package/dist/agents/skills/codex/harness-caching/SKILL.md +309 -0
- package/dist/agents/skills/codex/harness-caching/skill.yaml +73 -0
- package/dist/agents/skills/codex/harness-chaos/SKILL.md +295 -0
- package/dist/agents/skills/codex/harness-chaos/skill.yaml +72 -0
- package/dist/agents/skills/codex/harness-code-review/SKILL.md +857 -0
- package/dist/agents/skills/codex/harness-code-review/skill.yaml +52 -0
- package/dist/agents/skills/codex/harness-codebase-cleanup/SKILL.md +224 -0
- package/dist/agents/skills/codex/harness-codebase-cleanup/skill.yaml +65 -0
- package/dist/agents/skills/codex/harness-compliance/SKILL.md +303 -0
- package/dist/agents/skills/codex/harness-compliance/skill.yaml +78 -0
- package/dist/agents/skills/codex/harness-containerization/SKILL.md +284 -0
- package/dist/agents/skills/codex/harness-containerization/skill.yaml +80 -0
- package/dist/agents/skills/codex/harness-data-pipeline/SKILL.md +274 -0
- package/dist/agents/skills/codex/harness-data-pipeline/skill.yaml +81 -0
- package/dist/agents/skills/codex/harness-data-validation/SKILL.md +343 -0
- package/dist/agents/skills/codex/harness-data-validation/skill.yaml +75 -0
- package/dist/agents/skills/codex/harness-database/SKILL.md +310 -0
- package/dist/agents/skills/codex/harness-database/skill.yaml +80 -0
- package/dist/agents/skills/codex/harness-debugging/SKILL.md +366 -0
- package/dist/agents/skills/codex/harness-debugging/skill.yaml +48 -0
- package/dist/agents/skills/codex/harness-dependency-health/SKILL.md +179 -0
- package/dist/agents/skills/codex/harness-dependency-health/skill.yaml +42 -0
- package/dist/agents/skills/codex/harness-deployment/SKILL.md +307 -0
- package/dist/agents/skills/codex/harness-deployment/skill.yaml +77 -0
- package/dist/agents/skills/codex/harness-design/SKILL.md +265 -0
- package/dist/agents/skills/codex/harness-design/skill.yaml +54 -0
- package/dist/agents/skills/codex/harness-design-mobile/SKILL.md +336 -0
- package/dist/agents/skills/codex/harness-design-mobile/skill.yaml +50 -0
- package/dist/agents/skills/codex/harness-design-system/SKILL.md +282 -0
- package/dist/agents/skills/codex/harness-design-system/skill.yaml +51 -0
- package/dist/agents/skills/codex/harness-design-web/SKILL.md +360 -0
- package/dist/agents/skills/codex/harness-design-web/skill.yaml +53 -0
- package/dist/agents/skills/codex/harness-diagnostics/SKILL.md +318 -0
- package/dist/agents/skills/codex/harness-diagnostics/skill.yaml +51 -0
- package/dist/agents/skills/codex/harness-docs-pipeline/SKILL.md +460 -0
- package/dist/agents/skills/codex/harness-docs-pipeline/skill.yaml +70 -0
- package/dist/agents/skills/codex/harness-dx/SKILL.md +276 -0
- package/dist/agents/skills/codex/harness-dx/skill.yaml +76 -0
- package/dist/agents/skills/codex/harness-e2e/SKILL.md +245 -0
- package/dist/agents/skills/codex/harness-e2e/skill.yaml +78 -0
- package/dist/agents/skills/codex/harness-event-driven/SKILL.md +280 -0
- package/dist/agents/skills/codex/harness-event-driven/skill.yaml +77 -0
- package/dist/agents/skills/codex/harness-execution/SKILL.md +510 -0
- package/dist/agents/skills/codex/harness-execution/skill.yaml +52 -0
- package/dist/agents/skills/codex/harness-feature-flags/SKILL.md +287 -0
- package/dist/agents/skills/codex/harness-feature-flags/skill.yaml +74 -0
- package/dist/agents/skills/codex/harness-git-workflow/SKILL.md +268 -0
- package/dist/agents/skills/codex/harness-git-workflow/skill.yaml +32 -0
- package/dist/agents/skills/codex/harness-hotspot-detector/SKILL.md +161 -0
- package/dist/agents/skills/codex/harness-hotspot-detector/skill.yaml +45 -0
- package/dist/agents/skills/codex/harness-i18n/SKILL.md +484 -0
- package/dist/agents/skills/codex/harness-i18n/skill.yaml +55 -0
- package/dist/agents/skills/codex/harness-i18n-process/SKILL.md +388 -0
- package/dist/agents/skills/codex/harness-i18n-process/skill.yaml +44 -0
- package/dist/agents/skills/codex/harness-i18n-workflow/SKILL.md +512 -0
- package/dist/agents/skills/codex/harness-i18n-workflow/skill.yaml +54 -0
- package/dist/agents/skills/codex/harness-impact-analysis/SKILL.md +184 -0
- package/dist/agents/skills/codex/harness-impact-analysis/skill.yaml +45 -0
- package/dist/agents/skills/codex/harness-incident-response/SKILL.md +223 -0
- package/dist/agents/skills/codex/harness-incident-response/skill.yaml +78 -0
- package/dist/agents/skills/codex/harness-infrastructure-as-code/SKILL.md +279 -0
- package/dist/agents/skills/codex/harness-infrastructure-as-code/skill.yaml +80 -0
- package/dist/agents/skills/codex/harness-integration-test/SKILL.md +271 -0
- package/dist/agents/skills/codex/harness-integration-test/skill.yaml +73 -0
- package/dist/agents/skills/codex/harness-integrity/SKILL.md +167 -0
- package/dist/agents/skills/codex/harness-integrity/skill.yaml +48 -0
- package/dist/agents/skills/codex/harness-knowledge-mapper/SKILL.md +195 -0
- package/dist/agents/skills/codex/harness-knowledge-mapper/skill.yaml +50 -0
- package/dist/agents/skills/codex/harness-load-testing/SKILL.md +274 -0
- package/dist/agents/skills/codex/harness-load-testing/skill.yaml +79 -0
- package/dist/agents/skills/codex/harness-ml-ops/SKILL.md +341 -0
- package/dist/agents/skills/codex/harness-ml-ops/skill.yaml +79 -0
- package/dist/agents/skills/codex/harness-mobile-patterns/SKILL.md +326 -0
- package/dist/agents/skills/codex/harness-mobile-patterns/skill.yaml +82 -0
- package/dist/agents/skills/codex/harness-mutation-test/SKILL.md +251 -0
- package/dist/agents/skills/codex/harness-mutation-test/skill.yaml +70 -0
- package/dist/agents/skills/codex/harness-observability/SKILL.md +283 -0
- package/dist/agents/skills/codex/harness-observability/skill.yaml +78 -0
- package/dist/agents/skills/codex/harness-onboarding/SKILL.md +288 -0
- package/dist/agents/skills/codex/harness-onboarding/skill.yaml +31 -0
- package/dist/agents/skills/codex/harness-parallel-agents/SKILL.md +256 -0
- package/dist/agents/skills/codex/harness-parallel-agents/skill.yaml +34 -0
- package/dist/agents/skills/codex/harness-perf/SKILL.md +260 -0
- package/dist/agents/skills/codex/harness-perf/skill.yaml +51 -0
- package/dist/agents/skills/codex/harness-perf-tdd/SKILL.md +249 -0
- package/dist/agents/skills/codex/harness-perf-tdd/skill.yaml +48 -0
- package/dist/agents/skills/codex/harness-planning/SKILL.md +579 -0
- package/dist/agents/skills/codex/harness-planning/skill.yaml +56 -0
- package/dist/agents/skills/codex/harness-pre-commit-review/SKILL.md +324 -0
- package/dist/agents/skills/codex/harness-pre-commit-review/skill.yaml +34 -0
- package/dist/agents/skills/codex/harness-product-spec/SKILL.md +285 -0
- package/dist/agents/skills/codex/harness-product-spec/skill.yaml +72 -0
- package/dist/agents/skills/codex/harness-property-test/SKILL.md +281 -0
- package/dist/agents/skills/codex/harness-property-test/skill.yaml +71 -0
- package/dist/agents/skills/codex/harness-refactoring/SKILL.md +169 -0
- package/dist/agents/skills/codex/harness-refactoring/skill.yaml +34 -0
- package/dist/agents/skills/codex/harness-release-readiness/SKILL.md +689 -0
- package/dist/agents/skills/codex/harness-release-readiness/skill.yaml +58 -0
- package/dist/agents/skills/codex/harness-resilience/SKILL.md +255 -0
- package/dist/agents/skills/codex/harness-resilience/skill.yaml +76 -0
- package/dist/agents/skills/codex/harness-roadmap/SKILL.md +595 -0
- package/dist/agents/skills/codex/harness-roadmap/skill.yaml +44 -0
- package/dist/agents/skills/codex/harness-roadmap-pilot/SKILL.md +204 -0
- package/dist/agents/skills/codex/harness-roadmap-pilot/skill.yaml +52 -0
- package/dist/agents/skills/codex/harness-secrets/SKILL.md +293 -0
- package/dist/agents/skills/codex/harness-secrets/skill.yaml +76 -0
- package/dist/agents/skills/codex/harness-security-review/SKILL.md +260 -0
- package/dist/agents/skills/codex/harness-security-review/skill.yaml +53 -0
- package/dist/agents/skills/codex/harness-security-scan/SKILL.md +154 -0
- package/dist/agents/skills/codex/harness-security-scan/skill.yaml +42 -0
- package/dist/agents/skills/codex/harness-skill-authoring/SKILL.md +292 -0
- package/dist/agents/skills/codex/harness-skill-authoring/skill.yaml +33 -0
- package/dist/agents/skills/codex/harness-soundness-review/SKILL.md +1267 -0
- package/dist/agents/skills/codex/harness-soundness-review/skill.yaml +49 -0
- package/dist/agents/skills/codex/harness-sql-review/SKILL.md +315 -0
- package/dist/agents/skills/codex/harness-sql-review/skill.yaml +74 -0
- package/dist/agents/skills/codex/harness-state-management/SKILL.md +309 -0
- package/dist/agents/skills/codex/harness-state-management/skill.yaml +33 -0
- package/dist/agents/skills/codex/harness-supply-chain-audit/SKILL.md +281 -0
- package/dist/agents/skills/codex/harness-supply-chain-audit/skill.yaml +51 -0
- package/dist/agents/skills/codex/harness-tdd/SKILL.md +177 -0
- package/dist/agents/skills/codex/harness-tdd/skill.yaml +49 -0
- package/dist/agents/skills/codex/harness-test-advisor/SKILL.md +160 -0
- package/dist/agents/skills/codex/harness-test-advisor/skill.yaml +45 -0
- package/dist/agents/skills/codex/harness-test-data/SKILL.md +268 -0
- package/dist/agents/skills/codex/harness-test-data/skill.yaml +74 -0
- package/dist/agents/skills/codex/harness-ux-copy/SKILL.md +271 -0
- package/dist/agents/skills/codex/harness-ux-copy/skill.yaml +77 -0
- package/dist/agents/skills/codex/harness-verification/SKILL.md +421 -0
- package/dist/agents/skills/codex/harness-verification/skill.yaml +43 -0
- package/dist/agents/skills/codex/harness-verify/SKILL.md +159 -0
- package/dist/agents/skills/codex/harness-verify/skill.yaml +41 -0
- package/dist/agents/skills/codex/harness-visual-regression/SKILL.md +257 -0
- package/dist/agents/skills/codex/harness-visual-regression/skill.yaml +74 -0
- package/dist/agents/skills/codex/initialize-harness-project/SKILL.md +232 -0
- package/dist/agents/skills/codex/initialize-harness-project/skill.yaml +32 -0
- package/dist/agents/skills/codex/validate-context-engineering/SKILL.md +150 -0
- package/dist/agents/skills/codex/validate-context-engineering/skill.yaml +32 -0
- package/dist/agents/skills/cursor/add-harness-component/SKILL.md +192 -0
- package/dist/agents/skills/cursor/add-harness-component/skill.yaml +33 -0
- package/dist/agents/skills/cursor/align-documentation/SKILL.md +213 -0
- package/dist/agents/skills/cursor/align-documentation/skill.yaml +32 -0
- package/dist/agents/skills/cursor/check-mechanical-constraints/SKILL.md +191 -0
- package/dist/agents/skills/cursor/check-mechanical-constraints/skill.yaml +33 -0
- package/dist/agents/skills/cursor/cleanup-dead-code/SKILL.md +245 -0
- package/dist/agents/skills/cursor/cleanup-dead-code/skill.yaml +34 -0
- package/dist/agents/skills/cursor/detect-doc-drift/SKILL.md +179 -0
- package/dist/agents/skills/cursor/detect-doc-drift/skill.yaml +31 -0
- package/dist/agents/skills/cursor/enforce-architecture/SKILL.md +296 -0
- package/dist/agents/skills/cursor/enforce-architecture/skill.yaml +35 -0
- package/dist/agents/skills/cursor/harness-accessibility/SKILL.md +281 -0
- package/dist/agents/skills/cursor/harness-accessibility/skill.yaml +52 -0
- package/dist/agents/skills/cursor/harness-api-design/SKILL.md +356 -0
- package/dist/agents/skills/cursor/harness-api-design/skill.yaml +74 -0
- package/dist/agents/skills/cursor/harness-architecture-advisor/SKILL.md +449 -0
- package/dist/agents/skills/cursor/harness-architecture-advisor/skill.yaml +49 -0
- package/dist/agents/skills/cursor/harness-auth/SKILL.md +331 -0
- package/dist/agents/skills/cursor/harness-auth/skill.yaml +81 -0
- package/dist/agents/skills/cursor/harness-autopilot/SKILL.md +916 -0
- package/dist/agents/skills/cursor/harness-autopilot/skill.yaml +67 -0
- package/dist/agents/skills/cursor/harness-brainstorming/SKILL.md +406 -0
- package/dist/agents/skills/cursor/harness-brainstorming/skill.yaml +50 -0
- package/dist/agents/skills/cursor/harness-caching/SKILL.md +309 -0
- package/dist/agents/skills/cursor/harness-caching/skill.yaml +73 -0
- package/dist/agents/skills/cursor/harness-chaos/SKILL.md +295 -0
- package/dist/agents/skills/cursor/harness-chaos/skill.yaml +72 -0
- package/dist/agents/skills/cursor/harness-code-review/SKILL.md +857 -0
- package/dist/agents/skills/cursor/harness-code-review/skill.yaml +52 -0
- package/dist/agents/skills/cursor/harness-codebase-cleanup/SKILL.md +224 -0
- package/dist/agents/skills/cursor/harness-codebase-cleanup/skill.yaml +65 -0
- package/dist/agents/skills/cursor/harness-compliance/SKILL.md +303 -0
- package/dist/agents/skills/cursor/harness-compliance/skill.yaml +78 -0
- package/dist/agents/skills/cursor/harness-containerization/SKILL.md +284 -0
- package/dist/agents/skills/cursor/harness-containerization/skill.yaml +80 -0
- package/dist/agents/skills/cursor/harness-data-pipeline/SKILL.md +274 -0
- package/dist/agents/skills/cursor/harness-data-pipeline/skill.yaml +81 -0
- package/dist/agents/skills/cursor/harness-data-validation/SKILL.md +343 -0
- package/dist/agents/skills/cursor/harness-data-validation/skill.yaml +75 -0
- package/dist/agents/skills/cursor/harness-database/SKILL.md +310 -0
- package/dist/agents/skills/cursor/harness-database/skill.yaml +80 -0
- package/dist/agents/skills/cursor/harness-debugging/SKILL.md +366 -0
- package/dist/agents/skills/cursor/harness-debugging/skill.yaml +48 -0
- package/dist/agents/skills/cursor/harness-dependency-health/SKILL.md +179 -0
- package/dist/agents/skills/cursor/harness-dependency-health/skill.yaml +42 -0
- package/dist/agents/skills/cursor/harness-deployment/SKILL.md +307 -0
- package/dist/agents/skills/cursor/harness-deployment/skill.yaml +77 -0
- package/dist/agents/skills/cursor/harness-design/SKILL.md +265 -0
- package/dist/agents/skills/cursor/harness-design/skill.yaml +54 -0
- package/dist/agents/skills/cursor/harness-design-mobile/SKILL.md +336 -0
- package/dist/agents/skills/cursor/harness-design-mobile/skill.yaml +50 -0
- package/dist/agents/skills/cursor/harness-design-system/SKILL.md +282 -0
- package/dist/agents/skills/cursor/harness-design-system/skill.yaml +51 -0
- package/dist/agents/skills/cursor/harness-design-web/SKILL.md +360 -0
- package/dist/agents/skills/cursor/harness-design-web/skill.yaml +53 -0
- package/dist/agents/skills/cursor/harness-diagnostics/SKILL.md +318 -0
- package/dist/agents/skills/cursor/harness-diagnostics/skill.yaml +51 -0
- package/dist/agents/skills/cursor/harness-docs-pipeline/SKILL.md +460 -0
- package/dist/agents/skills/cursor/harness-docs-pipeline/skill.yaml +70 -0
- package/dist/agents/skills/cursor/harness-dx/SKILL.md +276 -0
- package/dist/agents/skills/cursor/harness-dx/skill.yaml +76 -0
- package/dist/agents/skills/cursor/harness-e2e/SKILL.md +245 -0
- package/dist/agents/skills/cursor/harness-e2e/skill.yaml +78 -0
- package/dist/agents/skills/cursor/harness-event-driven/SKILL.md +280 -0
- package/dist/agents/skills/cursor/harness-event-driven/skill.yaml +77 -0
- package/dist/agents/skills/cursor/harness-execution/SKILL.md +510 -0
- package/dist/agents/skills/cursor/harness-execution/skill.yaml +52 -0
- package/dist/agents/skills/cursor/harness-feature-flags/SKILL.md +287 -0
- package/dist/agents/skills/cursor/harness-feature-flags/skill.yaml +74 -0
- package/dist/agents/skills/cursor/harness-git-workflow/SKILL.md +268 -0
- package/dist/agents/skills/cursor/harness-git-workflow/skill.yaml +32 -0
- package/dist/agents/skills/cursor/harness-hotspot-detector/SKILL.md +161 -0
- package/dist/agents/skills/cursor/harness-hotspot-detector/skill.yaml +45 -0
- package/dist/agents/skills/cursor/harness-i18n/SKILL.md +484 -0
- package/dist/agents/skills/cursor/harness-i18n/skill.yaml +55 -0
- package/dist/agents/skills/cursor/harness-i18n-process/SKILL.md +388 -0
- package/dist/agents/skills/cursor/harness-i18n-process/skill.yaml +44 -0
- package/dist/agents/skills/cursor/harness-i18n-workflow/SKILL.md +512 -0
- package/dist/agents/skills/cursor/harness-i18n-workflow/skill.yaml +54 -0
- package/dist/agents/skills/cursor/harness-impact-analysis/SKILL.md +184 -0
- package/dist/agents/skills/cursor/harness-impact-analysis/skill.yaml +45 -0
- package/dist/agents/skills/cursor/harness-incident-response/SKILL.md +223 -0
- package/dist/agents/skills/cursor/harness-incident-response/skill.yaml +78 -0
- package/dist/agents/skills/cursor/harness-infrastructure-as-code/SKILL.md +279 -0
- package/dist/agents/skills/cursor/harness-infrastructure-as-code/skill.yaml +80 -0
- package/dist/agents/skills/cursor/harness-integration-test/SKILL.md +271 -0
- package/dist/agents/skills/cursor/harness-integration-test/skill.yaml +73 -0
- package/dist/agents/skills/cursor/harness-integrity/SKILL.md +167 -0
- package/dist/agents/skills/cursor/harness-integrity/skill.yaml +48 -0
- package/dist/agents/skills/cursor/harness-knowledge-mapper/SKILL.md +195 -0
- package/dist/agents/skills/cursor/harness-knowledge-mapper/skill.yaml +50 -0
- package/dist/agents/skills/cursor/harness-load-testing/SKILL.md +274 -0
- package/dist/agents/skills/cursor/harness-load-testing/skill.yaml +79 -0
- package/dist/agents/skills/cursor/harness-ml-ops/SKILL.md +341 -0
- package/dist/agents/skills/cursor/harness-ml-ops/skill.yaml +79 -0
- package/dist/agents/skills/cursor/harness-mobile-patterns/SKILL.md +326 -0
- package/dist/agents/skills/cursor/harness-mobile-patterns/skill.yaml +82 -0
- package/dist/agents/skills/cursor/harness-mutation-test/SKILL.md +251 -0
- package/dist/agents/skills/cursor/harness-mutation-test/skill.yaml +70 -0
- package/dist/agents/skills/cursor/harness-observability/SKILL.md +283 -0
- package/dist/agents/skills/cursor/harness-observability/skill.yaml +78 -0
- package/dist/agents/skills/cursor/harness-onboarding/SKILL.md +288 -0
- package/dist/agents/skills/cursor/harness-onboarding/skill.yaml +31 -0
- package/dist/agents/skills/cursor/harness-parallel-agents/SKILL.md +256 -0
- package/dist/agents/skills/cursor/harness-parallel-agents/skill.yaml +34 -0
- package/dist/agents/skills/cursor/harness-perf/SKILL.md +260 -0
- package/dist/agents/skills/cursor/harness-perf/skill.yaml +51 -0
- package/dist/agents/skills/cursor/harness-perf-tdd/SKILL.md +249 -0
- package/dist/agents/skills/cursor/harness-perf-tdd/skill.yaml +48 -0
- package/dist/agents/skills/cursor/harness-planning/SKILL.md +579 -0
- package/dist/agents/skills/cursor/harness-planning/skill.yaml +56 -0
- package/dist/agents/skills/cursor/harness-pre-commit-review/SKILL.md +324 -0
- package/dist/agents/skills/cursor/harness-pre-commit-review/skill.yaml +34 -0
- package/dist/agents/skills/cursor/harness-product-spec/SKILL.md +285 -0
- package/dist/agents/skills/cursor/harness-product-spec/skill.yaml +72 -0
- package/dist/agents/skills/cursor/harness-property-test/SKILL.md +281 -0
- package/dist/agents/skills/cursor/harness-property-test/skill.yaml +71 -0
- package/dist/agents/skills/cursor/harness-refactoring/SKILL.md +169 -0
- package/dist/agents/skills/cursor/harness-refactoring/skill.yaml +34 -0
- package/dist/agents/skills/cursor/harness-release-readiness/SKILL.md +689 -0
- package/dist/agents/skills/cursor/harness-release-readiness/skill.yaml +58 -0
- package/dist/agents/skills/cursor/harness-resilience/SKILL.md +255 -0
- package/dist/agents/skills/cursor/harness-resilience/skill.yaml +76 -0
- package/dist/agents/skills/cursor/harness-roadmap/SKILL.md +595 -0
- package/dist/agents/skills/cursor/harness-roadmap/skill.yaml +44 -0
- package/dist/agents/skills/cursor/harness-roadmap-pilot/SKILL.md +204 -0
- package/dist/agents/skills/cursor/harness-roadmap-pilot/skill.yaml +52 -0
- package/dist/agents/skills/cursor/harness-secrets/SKILL.md +293 -0
- package/dist/agents/skills/cursor/harness-secrets/skill.yaml +76 -0
- package/dist/agents/skills/cursor/harness-security-review/SKILL.md +260 -0
- package/dist/agents/skills/cursor/harness-security-review/skill.yaml +53 -0
- package/dist/agents/skills/cursor/harness-security-scan/SKILL.md +154 -0
- package/dist/agents/skills/cursor/harness-security-scan/skill.yaml +42 -0
- package/dist/agents/skills/cursor/harness-skill-authoring/SKILL.md +292 -0
- package/dist/agents/skills/cursor/harness-skill-authoring/skill.yaml +33 -0
- package/dist/agents/skills/cursor/harness-soundness-review/SKILL.md +1267 -0
- package/dist/agents/skills/cursor/harness-soundness-review/skill.yaml +49 -0
- package/dist/agents/skills/cursor/harness-sql-review/SKILL.md +315 -0
- package/dist/agents/skills/cursor/harness-sql-review/skill.yaml +74 -0
- package/dist/agents/skills/cursor/harness-state-management/SKILL.md +309 -0
- package/dist/agents/skills/cursor/harness-state-management/skill.yaml +33 -0
- package/dist/agents/skills/cursor/harness-supply-chain-audit/SKILL.md +281 -0
- package/dist/agents/skills/cursor/harness-supply-chain-audit/skill.yaml +51 -0
- package/dist/agents/skills/cursor/harness-tdd/SKILL.md +177 -0
- package/dist/agents/skills/cursor/harness-tdd/skill.yaml +49 -0
- package/dist/agents/skills/cursor/harness-test-advisor/SKILL.md +160 -0
- package/dist/agents/skills/cursor/harness-test-advisor/skill.yaml +45 -0
- package/dist/agents/skills/cursor/harness-test-data/SKILL.md +268 -0
- package/dist/agents/skills/cursor/harness-test-data/skill.yaml +74 -0
- package/dist/agents/skills/cursor/harness-ux-copy/SKILL.md +271 -0
- package/dist/agents/skills/cursor/harness-ux-copy/skill.yaml +77 -0
- package/dist/agents/skills/cursor/harness-verification/SKILL.md +421 -0
- package/dist/agents/skills/cursor/harness-verification/skill.yaml +43 -0
- package/dist/agents/skills/cursor/harness-verify/SKILL.md +159 -0
- package/dist/agents/skills/cursor/harness-verify/skill.yaml +41 -0
- package/dist/agents/skills/cursor/harness-visual-regression/SKILL.md +257 -0
- package/dist/agents/skills/cursor/harness-visual-regression/skill.yaml +74 -0
- package/dist/agents/skills/cursor/initialize-harness-project/SKILL.md +232 -0
- package/dist/agents/skills/cursor/initialize-harness-project/skill.yaml +32 -0
- package/dist/agents/skills/cursor/validate-context-engineering/SKILL.md +150 -0
- package/dist/agents/skills/cursor/validate-context-engineering/skill.yaml +32 -0
- package/dist/agents/skills/gemini-cli/enforce-architecture/SKILL.md +52 -0
- package/dist/agents/skills/gemini-cli/harness-api-design/SKILL.md +52 -0
- package/dist/agents/skills/gemini-cli/harness-architecture-advisor/SKILL.md +52 -0
- package/dist/agents/skills/gemini-cli/harness-auth/SKILL.md +52 -0
- package/dist/agents/skills/gemini-cli/harness-autopilot/SKILL.md +123 -14
- package/dist/agents/skills/gemini-cli/harness-autopilot/skill.yaml +6 -0
- package/dist/agents/skills/gemini-cli/harness-code-review/SKILL.md +97 -3
- package/dist/agents/skills/gemini-cli/harness-code-review/skill.yaml +6 -0
- package/dist/agents/skills/gemini-cli/harness-codebase-cleanup/SKILL.md +2 -4
- package/dist/agents/skills/gemini-cli/harness-database/SKILL.md +52 -0
- package/dist/agents/skills/gemini-cli/harness-deployment/SKILL.md +52 -0
- package/dist/agents/skills/gemini-cli/harness-planning/SKILL.md +99 -3
- package/dist/agents/skills/gemini-cli/harness-planning/skill.yaml +6 -0
- package/dist/agents/skills/gemini-cli/harness-pre-commit-review/SKILL.md +1 -1
- package/dist/agents/skills/gemini-cli/harness-roadmap-pilot/SKILL.md +204 -0
- package/dist/agents/skills/gemini-cli/harness-roadmap-pilot/skill.yaml +52 -0
- package/dist/agents/skills/gemini-cli/harness-security-review/SKILL.md +27 -7
- package/dist/agents/skills/gemini-cli/harness-security-scan/SKILL.md +52 -0
- package/dist/agents/skills/gemini-cli/harness-supply-chain-audit/SKILL.md +281 -0
- package/dist/agents/skills/gemini-cli/harness-supply-chain-audit/skill.yaml +51 -0
- package/dist/agents/skills/package.json +5 -5
- package/dist/agents/skills/templates/discipline-template.md +49 -0
- package/dist/agents/skills/tests/schema.ts +1 -1
- package/dist/{agents-md-ZGNIDWAF.js → agents-md-DUYNKHJZ.js} +1 -1
- package/dist/{architecture-ZLIH5533.js → architecture-UBO5KKUV.js} +2 -2
- package/dist/bin/harness-mcp.js +14 -14
- package/dist/bin/harness.js +20 -20
- package/dist/{check-phase-gate-ZOXVBDCN.js → check-phase-gate-OSHN2AEL.js} +3 -3
- package/dist/{chunk-NNHDDXYT.js → chunk-2DMIQ35P.js} +486 -132
- package/dist/{chunk-OFXQSFOW.js → chunk-5FM64G6D.js} +2 -2
- package/dist/{chunk-RCWZBSK5.js → chunk-6KWBH4EO.js} +1 -1
- package/dist/{chunk-LGYBN7Y6.js → chunk-ABQUCXRE.js} +2 -1
- package/dist/{chunk-VEPAJXBW.js → chunk-APNPXLB2.js} +4 -4
- package/dist/{chunk-ZOAWBDWU.js → chunk-CJDVBBPB.js} +5 -1
- package/dist/{chunk-FTMXDOR6.js → chunk-CZZXE6BL.js} +1 -1
- package/dist/{chunk-N25INEIX.js → chunk-GWXP3JVA.js} +3 -3
- package/dist/{chunk-XYLGHKG6.js → chunk-HKUX2X7O.js} +11 -2
- package/dist/{chunk-YBJ262QL.js → chunk-LRG3B43J.js} +1 -1
- package/dist/{chunk-AOZRDOIP.js → chunk-M6TIO6NF.js} +1 -1
- package/dist/{chunk-J4RAX7YB.js → chunk-OA3MOZGG.js} +1683 -507
- package/dist/{chunk-YLXFKVJE.js → chunk-OHZVGIPE.js} +9 -9
- package/dist/{chunk-2BKLWLY6.js → chunk-QSRRBNLY.js} +8 -8
- package/dist/{chunk-3ZZKVN62.js → chunk-TG7IUJ3J.js} +1 -1
- package/dist/{chunk-EDXIVMAP.js → chunk-TZIHFNEG.js} +20 -6
- package/dist/{chunk-ND2ENWDM.js → chunk-UX3JHYEA.js} +1 -1
- package/dist/{chunk-Z2OOPXJO.js → chunk-VF23UTNB.js} +1771 -164
- package/dist/{chunk-7MJAPE3Z.js → chunk-YLN34N65.js} +1 -0
- package/dist/{chunk-B2HKP423.js → chunk-ZA2I7S3E.js} +28 -1
- package/dist/{ci-workflow-765LSHRD.js → ci-workflow-FJZMNZPT.js} +1 -1
- package/dist/{create-skill-XSWHMSM5.js → create-skill-NDXQSTIK.js} +2 -2
- package/dist/{dist-ALQDD67R.js → dist-MF5BK5AD.js} +77 -1
- package/dist/{dist-B26DFXMP.js → dist-U7EAO6T2.js} +110 -60
- package/dist/{docs-NRMQCOJ6.js → docs-WZHW4N4P.js} +3 -3
- package/dist/{engine-3RB7MXPP.js → engine-VS6ZJ2VZ.js} +2 -2
- package/dist/{entropy-6AGX2ZUN.js → entropy-FCIGJIIT.js} +2 -2
- package/dist/{feedback-MY4QZIFD.js → feedback-O3FYTZIE.js} +1 -1
- package/dist/{generate-agent-definitions-ZAE726AU.js → generate-agent-definitions-EYG263XD.js} +3 -3
- package/dist/{graph-loader-2M2HXDQI.js → graph-loader-KMHDQYDT.js} +1 -1
- package/dist/index.d.ts +95 -15
- package/dist/index.js +20 -20
- package/dist/{loader-UUTVMQCC.js → loader-B4XWX4K6.js} +1 -1
- package/dist/{mcp-VU5FMO52.js → mcp-DVVUODN7.js} +14 -14
- package/dist/{performance-2D7G6NMJ.js → performance-NMJDV6HF.js} +4 -2
- package/dist/{review-pipeline-RAQ55ISU.js → review-pipeline-MSEJWTKM.js} +1 -1
- package/dist/{runtime-BCK5RRZQ.js → runtime-YHVLJNPG.js} +1 -1
- package/dist/{security-2RPQEN62.js → security-HTDKKGMX.js} +1 -1
- package/dist/{skill-executor-XZLYZYAK.js → skill-executor-XEVDGXUM.js} +2 -2
- package/dist/{validate-KBYQAEWE.js → validate-SPSTH2YW.js} +2 -2
- package/dist/{validate-cross-check-OABMREW4.js → validate-cross-check-YTDWIMFI.js} +1 -1
- package/package.json +20 -20
|
@@ -135,17 +135,17 @@ function resolveFileToLayer(file, layers) {
|
|
|
135
135
|
}
|
|
136
136
|
var accessAsync = promisify(access);
|
|
137
137
|
var readFileAsync = promisify(readFile);
|
|
138
|
-
async function fileExists(
|
|
138
|
+
async function fileExists(path26) {
|
|
139
139
|
try {
|
|
140
|
-
await accessAsync(
|
|
140
|
+
await accessAsync(path26, constants.F_OK);
|
|
141
141
|
return true;
|
|
142
142
|
} catch {
|
|
143
143
|
return false;
|
|
144
144
|
}
|
|
145
145
|
}
|
|
146
|
-
async function readFileContent(
|
|
146
|
+
async function readFileContent(path26) {
|
|
147
147
|
try {
|
|
148
|
-
const content = await readFileAsync(
|
|
148
|
+
const content = await readFileAsync(path26, "utf-8");
|
|
149
149
|
return Ok(content);
|
|
150
150
|
} catch (error) {
|
|
151
151
|
return Err(error);
|
|
@@ -1854,23 +1854,33 @@ import { minimatch as minimatch4 } from "minimatch";
|
|
|
1854
1854
|
import { z as z6 } from "zod";
|
|
1855
1855
|
import * as fs17 from "fs";
|
|
1856
1856
|
import * as path14 from "path";
|
|
1857
|
+
import { readFileSync as readFileSync142, writeFileSync as writeFileSync11, unlinkSync, mkdirSync as mkdirSync11, readdirSync as readdirSync3 } from "fs";
|
|
1858
|
+
import { join as join21, dirname as dirname8 } from "path";
|
|
1857
1859
|
import * as path15 from "path";
|
|
1858
1860
|
import * as path16 from "path";
|
|
1859
1861
|
import * as path17 from "path";
|
|
1860
1862
|
import * as path18 from "path";
|
|
1861
1863
|
import * as fs19 from "fs";
|
|
1862
1864
|
import * as path19 from "path";
|
|
1865
|
+
import * as fs20 from "fs";
|
|
1863
1866
|
import { z as z7 } from "zod";
|
|
1864
|
-
import * as fs20 from "fs/promises";
|
|
1865
|
-
import * as path20 from "path";
|
|
1866
1867
|
import * as fs21 from "fs/promises";
|
|
1868
|
+
import * as path20 from "path";
|
|
1869
|
+
import * as fs22 from "fs/promises";
|
|
1867
1870
|
import * as path21 from "path";
|
|
1868
1871
|
import * as ejs from "ejs";
|
|
1869
|
-
import * as
|
|
1872
|
+
import * as fs23 from "fs";
|
|
1870
1873
|
import * as path22 from "path";
|
|
1871
1874
|
import * as os from "os";
|
|
1872
1875
|
import { spawn } from "child_process";
|
|
1873
1876
|
import Parser from "web-tree-sitter";
|
|
1877
|
+
import * as fs24 from "fs/promises";
|
|
1878
|
+
import * as path23 from "path";
|
|
1879
|
+
import * as fs25 from "fs";
|
|
1880
|
+
import * as path24 from "path";
|
|
1881
|
+
import * as fs26 from "fs";
|
|
1882
|
+
import * as path25 from "path";
|
|
1883
|
+
import * as os2 from "os";
|
|
1874
1884
|
async function validateFileStructure(projectPath, conventions) {
|
|
1875
1885
|
const missing = [];
|
|
1876
1886
|
const unexpected = [];
|
|
@@ -1906,15 +1916,15 @@ function validateConfig(data, schema) {
|
|
|
1906
1916
|
let message = "Configuration validation failed";
|
|
1907
1917
|
const suggestions = [];
|
|
1908
1918
|
if (firstError) {
|
|
1909
|
-
const
|
|
1910
|
-
const pathDisplay =
|
|
1919
|
+
const path26 = firstError.path.join(".");
|
|
1920
|
+
const pathDisplay = path26 ? ` at "${path26}"` : "";
|
|
1911
1921
|
if (firstError.code === "invalid_type") {
|
|
1912
1922
|
const received = firstError.received;
|
|
1913
1923
|
const expected = firstError.expected;
|
|
1914
1924
|
if (received === "undefined") {
|
|
1915
1925
|
code = "MISSING_FIELD";
|
|
1916
1926
|
message = `Missing required field${pathDisplay}: ${firstError.message}`;
|
|
1917
|
-
suggestions.push(`Field "${
|
|
1927
|
+
suggestions.push(`Field "${path26}" is required and must be of type "${expected}"`);
|
|
1918
1928
|
} else {
|
|
1919
1929
|
code = "INVALID_TYPE";
|
|
1920
1930
|
message = `Invalid type${pathDisplay}: ${firstError.message}`;
|
|
@@ -2123,27 +2133,27 @@ function extractSections(content) {
|
|
|
2123
2133
|
}
|
|
2124
2134
|
return sections.map((section) => buildAgentMapSection(section, lines));
|
|
2125
2135
|
}
|
|
2126
|
-
function isExternalLink(
|
|
2127
|
-
return
|
|
2136
|
+
function isExternalLink(path26) {
|
|
2137
|
+
return path26.startsWith("http://") || path26.startsWith("https://") || path26.startsWith("#") || path26.startsWith("mailto:");
|
|
2128
2138
|
}
|
|
2129
2139
|
function resolveLinkPath(linkPath, baseDir) {
|
|
2130
2140
|
return linkPath.startsWith(".") ? join4(baseDir, linkPath) : linkPath;
|
|
2131
2141
|
}
|
|
2132
|
-
async function validateAgentsMap(
|
|
2133
|
-
const contentResult = await readFileContent(
|
|
2142
|
+
async function validateAgentsMap(path26 = "./AGENTS.md") {
|
|
2143
|
+
const contentResult = await readFileContent(path26);
|
|
2134
2144
|
if (!contentResult.ok) {
|
|
2135
2145
|
return Err(
|
|
2136
2146
|
createError(
|
|
2137
2147
|
"PARSE_ERROR",
|
|
2138
2148
|
`Failed to read AGENTS.md: ${contentResult.error.message}`,
|
|
2139
|
-
{ path:
|
|
2149
|
+
{ path: path26 },
|
|
2140
2150
|
["Ensure the file exists", "Check file permissions"]
|
|
2141
2151
|
)
|
|
2142
2152
|
);
|
|
2143
2153
|
}
|
|
2144
2154
|
const content = contentResult.value;
|
|
2145
2155
|
const sections = extractSections(content);
|
|
2146
|
-
const baseDir = dirname4(
|
|
2156
|
+
const baseDir = dirname4(path26);
|
|
2147
2157
|
const sectionTitles = sections.map((s) => s.title);
|
|
2148
2158
|
const missingSections = REQUIRED_SECTIONS.filter(
|
|
2149
2159
|
(required) => !sectionTitles.some((title) => title.toLowerCase().includes(required.toLowerCase()))
|
|
@@ -2277,8 +2287,8 @@ async function checkDocCoverage(domain, options = {}) {
|
|
|
2277
2287
|
);
|
|
2278
2288
|
}
|
|
2279
2289
|
}
|
|
2280
|
-
function suggestFix(
|
|
2281
|
-
const targetName = basename2(
|
|
2290
|
+
function suggestFix(path26, existingFiles) {
|
|
2291
|
+
const targetName = basename2(path26).toLowerCase();
|
|
2282
2292
|
const similar = existingFiles.find((file) => {
|
|
2283
2293
|
const fileName = basename2(file).toLowerCase();
|
|
2284
2294
|
return fileName.includes(targetName) || targetName.includes(fileName);
|
|
@@ -2286,7 +2296,7 @@ function suggestFix(path23, existingFiles) {
|
|
|
2286
2296
|
if (similar) {
|
|
2287
2297
|
return `Did you mean "${similar}"?`;
|
|
2288
2298
|
}
|
|
2289
|
-
return `Create the file "${
|
|
2299
|
+
return `Create the file "${path26}" or remove the link`;
|
|
2290
2300
|
}
|
|
2291
2301
|
async function validateKnowledgeMap(rootDir = process.cwd()) {
|
|
2292
2302
|
const agentsPath = join22(rootDir, "AGENTS.md");
|
|
@@ -2629,8 +2639,8 @@ function createBoundaryValidator(schema, name) {
|
|
|
2629
2639
|
return Ok(result.data);
|
|
2630
2640
|
}
|
|
2631
2641
|
const suggestions = result.error.issues.map((issue) => {
|
|
2632
|
-
const
|
|
2633
|
-
return
|
|
2642
|
+
const path26 = issue.path.join(".");
|
|
2643
|
+
return path26 ? `${path26}: ${issue.message}` : issue.message;
|
|
2634
2644
|
});
|
|
2635
2645
|
return Err(
|
|
2636
2646
|
createError(
|
|
@@ -3238,11 +3248,11 @@ function processExportListSpecifiers(exportDecl, exports) {
|
|
|
3238
3248
|
var TypeScriptParser = class {
|
|
3239
3249
|
name = "typescript";
|
|
3240
3250
|
extensions = [".ts", ".tsx", ".mts", ".cts"];
|
|
3241
|
-
async parseFile(
|
|
3242
|
-
const contentResult = await readFileContent(
|
|
3251
|
+
async parseFile(path26) {
|
|
3252
|
+
const contentResult = await readFileContent(path26);
|
|
3243
3253
|
if (!contentResult.ok) {
|
|
3244
3254
|
return Err(
|
|
3245
|
-
createParseError("NOT_FOUND", `File not found: ${
|
|
3255
|
+
createParseError("NOT_FOUND", `File not found: ${path26}`, { path: path26 }, [
|
|
3246
3256
|
"Check that the file exists",
|
|
3247
3257
|
"Verify the path is correct"
|
|
3248
3258
|
])
|
|
@@ -3252,7 +3262,7 @@ var TypeScriptParser = class {
|
|
|
3252
3262
|
const ast = parse(contentResult.value, {
|
|
3253
3263
|
loc: true,
|
|
3254
3264
|
range: true,
|
|
3255
|
-
jsx:
|
|
3265
|
+
jsx: path26.endsWith(".tsx"),
|
|
3256
3266
|
errorOnUnknownASTType: false
|
|
3257
3267
|
});
|
|
3258
3268
|
return Ok({
|
|
@@ -3263,7 +3273,7 @@ var TypeScriptParser = class {
|
|
|
3263
3273
|
} catch (e) {
|
|
3264
3274
|
const error = e;
|
|
3265
3275
|
return Err(
|
|
3266
|
-
createParseError("SYNTAX_ERROR", `Failed to parse ${
|
|
3276
|
+
createParseError("SYNTAX_ERROR", `Failed to parse ${path26}: ${error.message}`, { path: path26 }, [
|
|
3267
3277
|
"Check for syntax errors in the file",
|
|
3268
3278
|
"Ensure valid TypeScript syntax"
|
|
3269
3279
|
])
|
|
@@ -3444,22 +3454,22 @@ function extractInlineRefs(content) {
|
|
|
3444
3454
|
}
|
|
3445
3455
|
return refs;
|
|
3446
3456
|
}
|
|
3447
|
-
async function parseDocumentationFile(
|
|
3448
|
-
const contentResult = await readFileContent(
|
|
3457
|
+
async function parseDocumentationFile(path26) {
|
|
3458
|
+
const contentResult = await readFileContent(path26);
|
|
3449
3459
|
if (!contentResult.ok) {
|
|
3450
3460
|
return Err(
|
|
3451
3461
|
createEntropyError(
|
|
3452
3462
|
"PARSE_ERROR",
|
|
3453
|
-
`Failed to read documentation file: ${
|
|
3454
|
-
{ file:
|
|
3463
|
+
`Failed to read documentation file: ${path26}`,
|
|
3464
|
+
{ file: path26 },
|
|
3455
3465
|
["Check that the file exists"]
|
|
3456
3466
|
)
|
|
3457
3467
|
);
|
|
3458
3468
|
}
|
|
3459
3469
|
const content = contentResult.value;
|
|
3460
|
-
const type =
|
|
3470
|
+
const type = path26.endsWith(".md") ? "markdown" : "text";
|
|
3461
3471
|
return Ok({
|
|
3462
|
-
path:
|
|
3472
|
+
path: path26,
|
|
3463
3473
|
type,
|
|
3464
3474
|
content,
|
|
3465
3475
|
codeBlocks: extractCodeBlocks(content),
|
|
@@ -4717,7 +4727,7 @@ var EntropyAnalyzer = class {
|
|
|
4717
4727
|
};
|
|
4718
4728
|
var readFile32 = promisify2(fs3.readFile);
|
|
4719
4729
|
var writeFile32 = promisify2(fs3.writeFile);
|
|
4720
|
-
var
|
|
4730
|
+
var unlink22 = promisify2(fs3.unlink);
|
|
4721
4731
|
var mkdir22 = promisify2(fs3.mkdir);
|
|
4722
4732
|
var copyFile2 = promisify2(fs3.copyFile);
|
|
4723
4733
|
var DEFAULT_FIX_CONFIG = {
|
|
@@ -4860,7 +4870,7 @@ async function applySingleFix(fix, config) {
|
|
|
4860
4870
|
return Err({ fix, error: backupResult.error.message });
|
|
4861
4871
|
}
|
|
4862
4872
|
}
|
|
4863
|
-
await
|
|
4873
|
+
await unlink22(fix.file);
|
|
4864
4874
|
break;
|
|
4865
4875
|
case "delete-lines":
|
|
4866
4876
|
if (fix.line !== void 0) {
|
|
@@ -8593,6 +8603,204 @@ var mcpRules = [
|
|
|
8593
8603
|
references: ["CWE-319"]
|
|
8594
8604
|
}
|
|
8595
8605
|
];
|
|
8606
|
+
var insecureDefaultsRules = [
|
|
8607
|
+
{
|
|
8608
|
+
id: "SEC-DEF-001",
|
|
8609
|
+
name: "Security-Sensitive Fallback to Hardcoded Default",
|
|
8610
|
+
category: "insecure-defaults",
|
|
8611
|
+
severity: "warning",
|
|
8612
|
+
confidence: "medium",
|
|
8613
|
+
patterns: [
|
|
8614
|
+
/(?:SECRET|KEY|TOKEN|PASSWORD|SALT|PEPPER|SIGNING|ENCRYPTION|AUTH|JWT|SESSION).*(?:\|\||\?\?)\s*['"][^'"]+['"]/i
|
|
8615
|
+
],
|
|
8616
|
+
fileGlob: "**/*.{ts,js,mjs,cjs}",
|
|
8617
|
+
message: "Security-sensitive variable falls back to a hardcoded default when env var is missing",
|
|
8618
|
+
remediation: "Throw an error if the env var is missing instead of falling back to a default. Use a startup validation check.",
|
|
8619
|
+
references: ["CWE-1188"]
|
|
8620
|
+
},
|
|
8621
|
+
{
|
|
8622
|
+
id: "SEC-DEF-002",
|
|
8623
|
+
name: "TLS/SSL Disabled by Default",
|
|
8624
|
+
category: "insecure-defaults",
|
|
8625
|
+
severity: "warning",
|
|
8626
|
+
confidence: "medium",
|
|
8627
|
+
patterns: [
|
|
8628
|
+
/(?:tls|ssl|https|secure)\s*(?:=|:)\s*(?:false|config\??\.\w+\s*(?:\?\?|&&|\|\|)\s*false)/i
|
|
8629
|
+
],
|
|
8630
|
+
fileGlob: "**/*.{ts,js,mjs,cjs,go,py}",
|
|
8631
|
+
message: "Security feature defaults to disabled; missing configuration degrades to insecure mode",
|
|
8632
|
+
remediation: "Default security features to enabled (true). Require explicit opt-out, not opt-in.",
|
|
8633
|
+
references: ["CWE-1188"]
|
|
8634
|
+
},
|
|
8635
|
+
{
|
|
8636
|
+
id: "SEC-DEF-003",
|
|
8637
|
+
name: "Swallowed Authentication/Authorization Error",
|
|
8638
|
+
category: "insecure-defaults",
|
|
8639
|
+
severity: "warning",
|
|
8640
|
+
confidence: "low",
|
|
8641
|
+
patterns: [
|
|
8642
|
+
// Matches single-line empty catch: catch(e) { } or catch(e) { // ignore }
|
|
8643
|
+
// Note: multi-line catch blocks are handled by AI review, not this rule
|
|
8644
|
+
/catch\s*\([^)]*\)\s*\{\s*(?:\/\/\s*(?:ignore|skip|noop|todo)\b.*)?\s*\}/
|
|
8645
|
+
],
|
|
8646
|
+
fileGlob: "**/*auth*.{ts,js,mjs,cjs},**/*session*.{ts,js,mjs,cjs},**/*token*.{ts,js,mjs,cjs}",
|
|
8647
|
+
message: "Single-line empty catch block in authentication/authorization code may silently allow unauthorized access. Note: multi-line empty catch blocks are detected by AI review, not this mechanical rule.",
|
|
8648
|
+
remediation: "Re-throw the error or return an explicit denial. Never silently swallow auth errors.",
|
|
8649
|
+
references: ["CWE-754", "CWE-390"]
|
|
8650
|
+
},
|
|
8651
|
+
{
|
|
8652
|
+
id: "SEC-DEF-004",
|
|
8653
|
+
name: "Permissive CORS Fallback",
|
|
8654
|
+
category: "insecure-defaults",
|
|
8655
|
+
severity: "warning",
|
|
8656
|
+
confidence: "medium",
|
|
8657
|
+
patterns: [
|
|
8658
|
+
/(?:origin|cors)\s*(?:=|:)\s*(?:config|options|env)\??\.\w+\s*(?:\?\?|\|\|)\s*['"]\*/
|
|
8659
|
+
],
|
|
8660
|
+
fileGlob: "**/*.{ts,js,mjs,cjs}",
|
|
8661
|
+
message: "CORS origin falls back to wildcard (*) when configuration is missing",
|
|
8662
|
+
remediation: "Default to a restrictive origin list. Require explicit configuration for permissive CORS.",
|
|
8663
|
+
references: ["CWE-942"]
|
|
8664
|
+
},
|
|
8665
|
+
{
|
|
8666
|
+
id: "SEC-DEF-005",
|
|
8667
|
+
name: "Rate Limiting Disabled by Default",
|
|
8668
|
+
category: "insecure-defaults",
|
|
8669
|
+
severity: "info",
|
|
8670
|
+
confidence: "low",
|
|
8671
|
+
patterns: [
|
|
8672
|
+
/(?:rateLimit|rateLimiting|throttle)\s*(?:=|:)\s*(?:config|options|env)\??\.\w+\s*(?:\?\?|\|\|)\s*(?:false|0|null|undefined)/i
|
|
8673
|
+
],
|
|
8674
|
+
fileGlob: "**/*.{ts,js,mjs,cjs}",
|
|
8675
|
+
message: "Rate limiting defaults to disabled when configuration is missing",
|
|
8676
|
+
remediation: "Default to a sensible rate limit. Require explicit opt-out for disabling.",
|
|
8677
|
+
references: ["CWE-770"]
|
|
8678
|
+
}
|
|
8679
|
+
];
|
|
8680
|
+
var sharpEdgesRules = [
|
|
8681
|
+
// --- Deprecated Crypto APIs ---
|
|
8682
|
+
{
|
|
8683
|
+
id: "SEC-EDGE-001",
|
|
8684
|
+
name: "Deprecated createCipher API",
|
|
8685
|
+
category: "sharp-edges",
|
|
8686
|
+
severity: "error",
|
|
8687
|
+
confidence: "high",
|
|
8688
|
+
patterns: [/crypto\.createCipher\s*\(/],
|
|
8689
|
+
fileGlob: "**/*.{ts,js,mjs,cjs}",
|
|
8690
|
+
message: "crypto.createCipher is deprecated \u2014 uses weak key derivation (no IV)",
|
|
8691
|
+
remediation: "Use crypto.createCipheriv with a random IV and proper key derivation (scrypt/pbkdf2)",
|
|
8692
|
+
references: ["CWE-327"]
|
|
8693
|
+
},
|
|
8694
|
+
{
|
|
8695
|
+
id: "SEC-EDGE-002",
|
|
8696
|
+
name: "Deprecated createDecipher API",
|
|
8697
|
+
category: "sharp-edges",
|
|
8698
|
+
severity: "error",
|
|
8699
|
+
confidence: "high",
|
|
8700
|
+
patterns: [/crypto\.createDecipher\s*\(/],
|
|
8701
|
+
fileGlob: "**/*.{ts,js,mjs,cjs}",
|
|
8702
|
+
message: "crypto.createDecipher is deprecated \u2014 uses weak key derivation (no IV)",
|
|
8703
|
+
remediation: "Use crypto.createDecipheriv with the same IV used for encryption",
|
|
8704
|
+
references: ["CWE-327"]
|
|
8705
|
+
},
|
|
8706
|
+
{
|
|
8707
|
+
id: "SEC-EDGE-003",
|
|
8708
|
+
name: "ECB Mode Selection",
|
|
8709
|
+
category: "sharp-edges",
|
|
8710
|
+
severity: "warning",
|
|
8711
|
+
confidence: "high",
|
|
8712
|
+
patterns: [/-ecb['"]/],
|
|
8713
|
+
fileGlob: "**/*.{ts,js,mjs,cjs,go,py}",
|
|
8714
|
+
message: "ECB mode does not provide semantic security \u2014 identical plaintext blocks produce identical ciphertext",
|
|
8715
|
+
remediation: "Use CBC, CTR, or GCM mode instead of ECB",
|
|
8716
|
+
references: ["CWE-327"]
|
|
8717
|
+
},
|
|
8718
|
+
// --- Unsafe Deserialization ---
|
|
8719
|
+
{
|
|
8720
|
+
id: "SEC-EDGE-004",
|
|
8721
|
+
name: "yaml.load Without Safe Loader",
|
|
8722
|
+
category: "sharp-edges",
|
|
8723
|
+
severity: "error",
|
|
8724
|
+
confidence: "high",
|
|
8725
|
+
patterns: [
|
|
8726
|
+
/yaml\.load\s*\(/
|
|
8727
|
+
// Python: yaml.load() without SafeLoader
|
|
8728
|
+
],
|
|
8729
|
+
fileGlob: "**/*.py",
|
|
8730
|
+
message: "yaml.load() executes arbitrary Python objects \u2014 use yaml.safe_load() instead",
|
|
8731
|
+
remediation: "Replace yaml.load() with yaml.safe_load() or yaml.load(data, Loader=SafeLoader). Note: this rule will flag yaml.load(data, Loader=SafeLoader) \u2014 suppress with # harness-ignore SEC-EDGE-004: safe usage with SafeLoader",
|
|
8732
|
+
references: ["CWE-502"]
|
|
8733
|
+
},
|
|
8734
|
+
{
|
|
8735
|
+
id: "SEC-EDGE-005",
|
|
8736
|
+
name: "Pickle/Marshal Deserialization",
|
|
8737
|
+
category: "sharp-edges",
|
|
8738
|
+
severity: "error",
|
|
8739
|
+
confidence: "high",
|
|
8740
|
+
patterns: [/pickle\.loads?\s*\(/, /marshal\.loads?\s*\(/],
|
|
8741
|
+
fileGlob: "**/*.py",
|
|
8742
|
+
message: "pickle/marshal deserialization executes arbitrary code \u2014 never use on untrusted data",
|
|
8743
|
+
remediation: "Use JSON, MessagePack, or Protocol Buffers for untrusted data serialization",
|
|
8744
|
+
references: ["CWE-502"]
|
|
8745
|
+
},
|
|
8746
|
+
// --- TOCTOU (Time-of-Check to Time-of-Use) ---
|
|
8747
|
+
{
|
|
8748
|
+
id: "SEC-EDGE-006",
|
|
8749
|
+
name: "Check-Then-Act File Operation",
|
|
8750
|
+
category: "sharp-edges",
|
|
8751
|
+
severity: "warning",
|
|
8752
|
+
confidence: "medium",
|
|
8753
|
+
// Patterns use .{0,N} since scanner matches single lines only (no multiline mode)
|
|
8754
|
+
patterns: [
|
|
8755
|
+
/(?:existsSync|accessSync|statSync)\s*\([^)]+\).{0,50}(?:readFileSync|writeFileSync|unlinkSync|mkdirSync)\s*\(/
|
|
8756
|
+
],
|
|
8757
|
+
fileGlob: "**/*.{ts,js,mjs,cjs}",
|
|
8758
|
+
message: "Check-then-act pattern on filesystem is vulnerable to TOCTOU race conditions",
|
|
8759
|
+
remediation: "Use the operation directly and handle ENOENT/EEXIST errors instead of checking first",
|
|
8760
|
+
references: ["CWE-367"]
|
|
8761
|
+
},
|
|
8762
|
+
{
|
|
8763
|
+
id: "SEC-EDGE-007",
|
|
8764
|
+
name: "Check-Then-Act File Operation (Async)",
|
|
8765
|
+
category: "sharp-edges",
|
|
8766
|
+
severity: "warning",
|
|
8767
|
+
confidence: "medium",
|
|
8768
|
+
// Uses .{0,N} since scanner matches single lines only (no multiline mode)
|
|
8769
|
+
patterns: [/(?:access|stat)\s*\([^)]+\).{0,80}(?:readFile|writeFile|unlink|mkdir)\s*\(/],
|
|
8770
|
+
fileGlob: "**/*.{ts,js,mjs,cjs}",
|
|
8771
|
+
message: "Async check-then-act pattern on filesystem is vulnerable to TOCTOU race conditions",
|
|
8772
|
+
remediation: "Use the operation directly with try/catch instead of checking existence first",
|
|
8773
|
+
references: ["CWE-367"]
|
|
8774
|
+
},
|
|
8775
|
+
// --- Stringly-Typed Security ---
|
|
8776
|
+
{
|
|
8777
|
+
id: "SEC-EDGE-008",
|
|
8778
|
+
name: 'JWT Algorithm "none"',
|
|
8779
|
+
category: "sharp-edges",
|
|
8780
|
+
severity: "error",
|
|
8781
|
+
confidence: "high",
|
|
8782
|
+
patterns: [
|
|
8783
|
+
/algorithm[s]?\s*[:=]\s*\[?\s*['"]none['"]/i,
|
|
8784
|
+
/alg(?:orithm)?\s*[:=]\s*['"]none['"]/i
|
|
8785
|
+
],
|
|
8786
|
+
fileGlob: "**/*.{ts,js,mjs,cjs}",
|
|
8787
|
+
message: 'JWT "none" algorithm disables signature verification entirely',
|
|
8788
|
+
remediation: 'Specify an explicit algorithm (e.g., "HS256", "RS256") and set algorithms allowlist in verify options',
|
|
8789
|
+
references: ["CWE-345"]
|
|
8790
|
+
},
|
|
8791
|
+
{
|
|
8792
|
+
id: "SEC-EDGE-009",
|
|
8793
|
+
name: "DES/RC4 Algorithm Selection",
|
|
8794
|
+
category: "sharp-edges",
|
|
8795
|
+
severity: "error",
|
|
8796
|
+
confidence: "high",
|
|
8797
|
+
patterns: [/['"]\s*(?:des|des-ede|des-ede3|des3|rc4|rc2|blowfish)\s*['"]/i],
|
|
8798
|
+
fileGlob: "**/*.{ts,js,mjs,cjs,go,py}",
|
|
8799
|
+
message: "Weak/deprecated cipher algorithm selected \u2014 DES, RC4, RC2, and Blowfish are broken or deprecated",
|
|
8800
|
+
remediation: "Use AES-256-GCM or ChaCha20-Poly1305",
|
|
8801
|
+
references: ["CWE-327"]
|
|
8802
|
+
}
|
|
8803
|
+
];
|
|
8596
8804
|
var nodeRules = [
|
|
8597
8805
|
{
|
|
8598
8806
|
id: "SEC-NODE-001",
|
|
@@ -8697,6 +8905,14 @@ var goRules = [
|
|
|
8697
8905
|
references: ["CWE-134"]
|
|
8698
8906
|
}
|
|
8699
8907
|
];
|
|
8908
|
+
function parseHarnessIgnore(line, ruleId) {
|
|
8909
|
+
if (!line.includes("harness-ignore")) return null;
|
|
8910
|
+
if (!line.includes(ruleId)) return null;
|
|
8911
|
+
const match = line.match(/(?:\/\/|#)\s*harness-ignore\s+(SEC-[A-Z]+-\d+)(?::\s*(.+))?/);
|
|
8912
|
+
if (match?.[1] !== ruleId) return null;
|
|
8913
|
+
const text = match[2]?.trim();
|
|
8914
|
+
return { ruleId, justification: text || null };
|
|
8915
|
+
}
|
|
8700
8916
|
var SecurityScanner = class {
|
|
8701
8917
|
registry;
|
|
8702
8918
|
config;
|
|
@@ -8713,7 +8929,9 @@ var SecurityScanner = class {
|
|
|
8713
8929
|
...networkRules,
|
|
8714
8930
|
...deserializationRules,
|
|
8715
8931
|
...agentConfigRules,
|
|
8716
|
-
...mcpRules
|
|
8932
|
+
...mcpRules,
|
|
8933
|
+
...insecureDefaultsRules,
|
|
8934
|
+
...sharpEdgesRules
|
|
8717
8935
|
]);
|
|
8718
8936
|
this.registry.registerAll([...nodeRules, ...expressRules, ...reactRules, ...goRules]);
|
|
8719
8937
|
this.activeRules = this.registry.getAll();
|
|
@@ -8730,42 +8948,8 @@ var SecurityScanner = class {
|
|
|
8730
8948
|
*/
|
|
8731
8949
|
scanContent(content, filePath, startLine = 1) {
|
|
8732
8950
|
if (!this.config.enabled) return [];
|
|
8733
|
-
const findings = [];
|
|
8734
8951
|
const lines = content.split("\n");
|
|
8735
|
-
|
|
8736
|
-
const resolved = resolveRuleSeverity(
|
|
8737
|
-
rule.id,
|
|
8738
|
-
rule.severity,
|
|
8739
|
-
this.config.rules ?? {},
|
|
8740
|
-
this.config.strict
|
|
8741
|
-
);
|
|
8742
|
-
if (resolved === "off") continue;
|
|
8743
|
-
for (let i = 0; i < lines.length; i++) {
|
|
8744
|
-
const line = lines[i] ?? "";
|
|
8745
|
-
if (line.includes("harness-ignore") && line.includes(rule.id)) continue;
|
|
8746
|
-
for (const pattern of rule.patterns) {
|
|
8747
|
-
pattern.lastIndex = 0;
|
|
8748
|
-
if (pattern.test(line)) {
|
|
8749
|
-
findings.push({
|
|
8750
|
-
ruleId: rule.id,
|
|
8751
|
-
ruleName: rule.name,
|
|
8752
|
-
category: rule.category,
|
|
8753
|
-
severity: resolved,
|
|
8754
|
-
confidence: rule.confidence,
|
|
8755
|
-
file: filePath,
|
|
8756
|
-
line: startLine + i,
|
|
8757
|
-
match: line.trim(),
|
|
8758
|
-
context: line,
|
|
8759
|
-
message: rule.message,
|
|
8760
|
-
remediation: rule.remediation,
|
|
8761
|
-
...rule.references ? { references: rule.references } : {}
|
|
8762
|
-
});
|
|
8763
|
-
break;
|
|
8764
|
-
}
|
|
8765
|
-
}
|
|
8766
|
-
}
|
|
8767
|
-
}
|
|
8768
|
-
return findings;
|
|
8952
|
+
return this.scanLinesWithRules(lines, this.activeRules, filePath, startLine);
|
|
8769
8953
|
}
|
|
8770
8954
|
async scanFile(filePath) {
|
|
8771
8955
|
if (!this.config.enabled) return [];
|
|
@@ -8774,14 +8958,22 @@ var SecurityScanner = class {
|
|
|
8774
8958
|
}
|
|
8775
8959
|
scanContentForFile(content, filePath, startLine = 1) {
|
|
8776
8960
|
if (!this.config.enabled) return [];
|
|
8777
|
-
const findings = [];
|
|
8778
8961
|
const lines = content.split("\n");
|
|
8779
8962
|
const applicableRules = this.activeRules.filter((rule) => {
|
|
8780
8963
|
if (!rule.fileGlob) return true;
|
|
8781
8964
|
const globs = rule.fileGlob.split(",").map((g) => g.trim());
|
|
8782
8965
|
return globs.some((glob2) => minimatch4(filePath, glob2, { dot: true }));
|
|
8783
8966
|
});
|
|
8784
|
-
|
|
8967
|
+
return this.scanLinesWithRules(lines, applicableRules, filePath, startLine);
|
|
8968
|
+
}
|
|
8969
|
+
/**
|
|
8970
|
+
* Core scanning loop shared by scanContent and scanContentForFile.
|
|
8971
|
+
* Evaluates each rule against each line, handling suppression (FP gate)
|
|
8972
|
+
* and pattern matching uniformly.
|
|
8973
|
+
*/
|
|
8974
|
+
scanLinesWithRules(lines, rules, filePath, startLine) {
|
|
8975
|
+
const findings = [];
|
|
8976
|
+
for (const rule of rules) {
|
|
8785
8977
|
const resolved = resolveRuleSeverity(
|
|
8786
8978
|
rule.id,
|
|
8787
8979
|
rule.severity,
|
|
@@ -8791,7 +8983,25 @@ var SecurityScanner = class {
|
|
|
8791
8983
|
if (resolved === "off") continue;
|
|
8792
8984
|
for (let i = 0; i < lines.length; i++) {
|
|
8793
8985
|
const line = lines[i] ?? "";
|
|
8794
|
-
|
|
8986
|
+
const suppressionMatch = parseHarnessIgnore(line, rule.id);
|
|
8987
|
+
if (suppressionMatch) {
|
|
8988
|
+
if (!suppressionMatch.justification) {
|
|
8989
|
+
findings.push({
|
|
8990
|
+
ruleId: rule.id,
|
|
8991
|
+
ruleName: rule.name,
|
|
8992
|
+
category: rule.category,
|
|
8993
|
+
severity: this.config.strict ? "error" : "warning",
|
|
8994
|
+
confidence: "high",
|
|
8995
|
+
file: filePath,
|
|
8996
|
+
line: startLine + i,
|
|
8997
|
+
match: line.trim(),
|
|
8998
|
+
context: line,
|
|
8999
|
+
message: `Suppression of ${rule.id} requires justification: // harness-ignore ${rule.id}: <reason>`,
|
|
9000
|
+
remediation: `Add justification after colon: // harness-ignore ${rule.id}: false positive because ...`
|
|
9001
|
+
});
|
|
9002
|
+
}
|
|
9003
|
+
continue;
|
|
9004
|
+
}
|
|
8795
9005
|
for (const pattern of rule.patterns) {
|
|
8796
9006
|
pattern.lastIndex = 0;
|
|
8797
9007
|
if (pattern.test(line)) {
|
|
@@ -8836,81 +9046,481 @@ var SecurityScanner = class {
|
|
|
8836
9046
|
};
|
|
8837
9047
|
}
|
|
8838
9048
|
};
|
|
8839
|
-
var
|
|
8840
|
-
|
|
8841
|
-
|
|
8842
|
-
|
|
8843
|
-
|
|
8844
|
-
|
|
8845
|
-
|
|
8846
|
-
|
|
8847
|
-
|
|
9049
|
+
var hiddenUnicodePatterns = [
|
|
9050
|
+
{
|
|
9051
|
+
ruleId: "INJ-UNI-001",
|
|
9052
|
+
severity: "high",
|
|
9053
|
+
category: "hidden-unicode",
|
|
9054
|
+
description: "Zero-width characters that can hide malicious instructions",
|
|
9055
|
+
// eslint-disable-next-line no-misleading-character-class -- intentional: regex detects zero-width chars for security scanning
|
|
9056
|
+
pattern: /[\u200B\u200C\u200D\uFEFF\u2060]/
|
|
9057
|
+
},
|
|
9058
|
+
{
|
|
9059
|
+
ruleId: "INJ-UNI-002",
|
|
9060
|
+
severity: "high",
|
|
9061
|
+
category: "hidden-unicode",
|
|
9062
|
+
description: "RTL/LTR override characters that can disguise text direction",
|
|
9063
|
+
pattern: /[\u202A-\u202E\u2066-\u2069]/
|
|
9064
|
+
}
|
|
8848
9065
|
];
|
|
8849
|
-
|
|
8850
|
-
|
|
8851
|
-
|
|
8852
|
-
|
|
8853
|
-
|
|
8854
|
-
|
|
8855
|
-
|
|
8856
|
-
|
|
8857
|
-
|
|
8858
|
-
|
|
8859
|
-
|
|
8860
|
-
|
|
8861
|
-
|
|
8862
|
-
|
|
8863
|
-
|
|
8864
|
-
|
|
8865
|
-
|
|
8866
|
-
|
|
8867
|
-
|
|
8868
|
-
|
|
8869
|
-
|
|
8870
|
-
}
|
|
9066
|
+
var reRolingPatterns = [
|
|
9067
|
+
{
|
|
9068
|
+
ruleId: "INJ-REROL-001",
|
|
9069
|
+
severity: "high",
|
|
9070
|
+
category: "explicit-re-roling",
|
|
9071
|
+
description: "Instruction to ignore/disregard/forget previous instructions",
|
|
9072
|
+
pattern: /(?:ignore|disregard|forget)\s+(?:all\s+)?(?:previous|prior|above|earlier)\s+(?:instructions?|prompts?|context|rules?|guidelines?)/i
|
|
9073
|
+
},
|
|
9074
|
+
{
|
|
9075
|
+
ruleId: "INJ-REROL-002",
|
|
9076
|
+
severity: "high",
|
|
9077
|
+
category: "explicit-re-roling",
|
|
9078
|
+
description: "Attempt to reassign the AI role",
|
|
9079
|
+
pattern: /you\s+are\s+now\s+(?:a\s+|an\s+)?(?:new\s+)?(?:helpful\s+)?(?:my\s+)?(?:\w+\s+)?(?:assistant|agent|AI|bot|chatbot|system|persona)\b/i
|
|
9080
|
+
},
|
|
9081
|
+
{
|
|
9082
|
+
ruleId: "INJ-REROL-003",
|
|
9083
|
+
severity: "high",
|
|
9084
|
+
category: "explicit-re-roling",
|
|
9085
|
+
description: "Direct instruction override attempt",
|
|
9086
|
+
pattern: /(?:new\s+)?(?:system\s+)?(?:instruction|directive|role|persona)\s*[:=]\s*/i
|
|
8871
9087
|
}
|
|
8872
|
-
|
|
8873
|
-
|
|
8874
|
-
|
|
8875
|
-
|
|
8876
|
-
|
|
8877
|
-
|
|
8878
|
-
|
|
8879
|
-
|
|
8880
|
-
|
|
8881
|
-
|
|
8882
|
-
|
|
8883
|
-
|
|
8884
|
-
|
|
8885
|
-
|
|
8886
|
-
|
|
8887
|
-
|
|
8888
|
-
|
|
8889
|
-
|
|
8890
|
-
|
|
8891
|
-
|
|
8892
|
-
|
|
8893
|
-
|
|
8894
|
-
|
|
8895
|
-
|
|
8896
|
-
|
|
8897
|
-
|
|
8898
|
-
|
|
8899
|
-
|
|
9088
|
+
];
|
|
9089
|
+
var permissionEscalationPatterns = [
|
|
9090
|
+
{
|
|
9091
|
+
ruleId: "INJ-PERM-001",
|
|
9092
|
+
severity: "high",
|
|
9093
|
+
category: "permission-escalation",
|
|
9094
|
+
description: "Attempt to enable all tools or grant unrestricted access",
|
|
9095
|
+
pattern: /(?:allow|enable|grant)\s+all\s+(?:tools?|permissions?|access)/i
|
|
9096
|
+
},
|
|
9097
|
+
{
|
|
9098
|
+
ruleId: "INJ-PERM-002",
|
|
9099
|
+
severity: "high",
|
|
9100
|
+
category: "permission-escalation",
|
|
9101
|
+
description: "Attempt to disable safety or security features",
|
|
9102
|
+
pattern: /(?:disable|turn\s+off|remove|bypass)\s+(?:all\s+)?(?:safety|security|restrictions?|guardrails?|protections?|checks?)/i
|
|
9103
|
+
},
|
|
9104
|
+
{
|
|
9105
|
+
ruleId: "INJ-PERM-003",
|
|
9106
|
+
severity: "high",
|
|
9107
|
+
category: "permission-escalation",
|
|
9108
|
+
description: "Auto-approve directive that bypasses human review",
|
|
9109
|
+
pattern: /(?:auto[- ]?approve|--no-verify|--dangerously-skip-permissions)/i
|
|
9110
|
+
}
|
|
9111
|
+
];
|
|
9112
|
+
var encodedPayloadPatterns = [
|
|
9113
|
+
{
|
|
9114
|
+
ruleId: "INJ-ENC-001",
|
|
9115
|
+
severity: "high",
|
|
9116
|
+
category: "encoded-payloads",
|
|
9117
|
+
description: "Base64-encoded string long enough to contain instructions (>=28 chars)",
|
|
9118
|
+
// Match base64 strings of 28+ chars (7+ groups of 4).
|
|
9119
|
+
// Excludes JWT tokens (eyJ prefix) and Bearer-prefixed tokens.
|
|
9120
|
+
pattern: /(?<!Bearer\s)(?<![:])(?<![A-Za-z0-9/])(?!eyJ)(?:[A-Za-z0-9+/]{4}){7,}(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?(?![A-Za-z0-9/])/
|
|
9121
|
+
},
|
|
9122
|
+
{
|
|
9123
|
+
ruleId: "INJ-ENC-002",
|
|
9124
|
+
severity: "high",
|
|
9125
|
+
category: "encoded-payloads",
|
|
9126
|
+
description: "Hex-encoded string long enough to contain directives (>=20 hex chars)",
|
|
9127
|
+
// Excludes hash-prefixed hex (sha256:, sha512:, md5:, etc.) and hex preceded by 0x.
|
|
9128
|
+
// Note: 40-char git SHA hashes (e.g. in `git log` output) may match — downstream
|
|
9129
|
+
// callers should filter matches of exactly 40 hex chars if scanning git output.
|
|
9130
|
+
pattern: /(?<![:x])(?<![A-Fa-f0-9])(?:[0-9a-fA-F]{2}){10,}(?![A-Fa-f0-9])/
|
|
9131
|
+
}
|
|
9132
|
+
];
|
|
9133
|
+
var indirectInjectionPatterns = [
|
|
9134
|
+
{
|
|
9135
|
+
ruleId: "INJ-IND-001",
|
|
9136
|
+
severity: "medium",
|
|
9137
|
+
category: "indirect-injection",
|
|
9138
|
+
description: "Instruction to influence future responses",
|
|
9139
|
+
pattern: /(?:when\s+the\s+user\s+asks|if\s+(?:the\s+user|someone|anyone)\s+asks)\s*,?\s*(?:say|respond|reply|answer|tell)/i
|
|
9140
|
+
},
|
|
9141
|
+
{
|
|
9142
|
+
ruleId: "INJ-IND-002",
|
|
9143
|
+
severity: "medium",
|
|
9144
|
+
category: "indirect-injection",
|
|
9145
|
+
description: "Directive to include content in responses",
|
|
9146
|
+
pattern: /(?:include|insert|add|embed|put)\s+(?:this|the\s+following)\s+(?:in|into|to)\s+(?:your|the)\s+(?:response|output|reply|answer)/i
|
|
9147
|
+
},
|
|
9148
|
+
{
|
|
9149
|
+
ruleId: "INJ-IND-003",
|
|
9150
|
+
severity: "medium",
|
|
9151
|
+
category: "indirect-injection",
|
|
9152
|
+
description: "Standing instruction to always respond a certain way",
|
|
9153
|
+
pattern: /always\s+(?:respond|reply|answer|say|output)\s+(?:with|that|by)/i
|
|
9154
|
+
}
|
|
9155
|
+
];
|
|
9156
|
+
var contextManipulationPatterns = [
|
|
9157
|
+
{
|
|
9158
|
+
ruleId: "INJ-CTX-001",
|
|
9159
|
+
severity: "medium",
|
|
9160
|
+
category: "context-manipulation",
|
|
9161
|
+
description: "Claim about system prompt content",
|
|
9162
|
+
pattern: /(?:the\s+)?(?:system\s+prompt|system\s+message|hidden\s+instructions?)\s+(?:says?|tells?|instructs?|contains?|is)/i
|
|
9163
|
+
},
|
|
9164
|
+
{
|
|
9165
|
+
ruleId: "INJ-CTX-002",
|
|
9166
|
+
severity: "medium",
|
|
9167
|
+
category: "context-manipulation",
|
|
9168
|
+
description: "Claim about AI instructions",
|
|
9169
|
+
pattern: /your\s+(?:instructions?|directives?|guidelines?|rules?)\s+(?:are|say|tell|state)/i
|
|
9170
|
+
},
|
|
9171
|
+
{
|
|
9172
|
+
ruleId: "INJ-CTX-003",
|
|
9173
|
+
severity: "medium",
|
|
9174
|
+
category: "context-manipulation",
|
|
9175
|
+
description: "Fake XML/HTML system or instruction tags",
|
|
9176
|
+
// Case-sensitive: only match lowercase tags to avoid false positives on
|
|
9177
|
+
// React components like <User>, <Context>, <Role> etc.
|
|
9178
|
+
pattern: /<\/?(?:system|instruction|prompt|role|context|tool_call|function_call|assistant|human|user)[^>]*>/
|
|
9179
|
+
},
|
|
9180
|
+
{
|
|
9181
|
+
ruleId: "INJ-CTX-004",
|
|
9182
|
+
severity: "medium",
|
|
9183
|
+
category: "context-manipulation",
|
|
9184
|
+
description: "Fake JSON role assignment mimicking chat format",
|
|
9185
|
+
pattern: /[{,]\s*"role"\s*:\s*"(?:system|assistant|function)"/i
|
|
9186
|
+
}
|
|
9187
|
+
];
|
|
9188
|
+
var socialEngineeringPatterns = [
|
|
9189
|
+
{
|
|
9190
|
+
ruleId: "INJ-SOC-001",
|
|
9191
|
+
severity: "medium",
|
|
9192
|
+
category: "social-engineering",
|
|
9193
|
+
description: "Urgency pressure to bypass checks",
|
|
9194
|
+
pattern: /(?:this\s+is\s+(?:very\s+)?urgent|this\s+is\s+(?:an?\s+)?emergency|do\s+(?:this|it)\s+(?:now|immediately))\b/i
|
|
9195
|
+
},
|
|
9196
|
+
{
|
|
9197
|
+
ruleId: "INJ-SOC-002",
|
|
9198
|
+
severity: "medium",
|
|
9199
|
+
category: "social-engineering",
|
|
9200
|
+
description: "False authority claim",
|
|
9201
|
+
pattern: /(?:the\s+)?(?:admin|administrator|manager|CEO|CTO|owner|supervisor)\s+(?:authorized|approved|said|told|confirmed|requested)/i
|
|
9202
|
+
},
|
|
9203
|
+
{
|
|
9204
|
+
ruleId: "INJ-SOC-003",
|
|
9205
|
+
severity: "medium",
|
|
9206
|
+
category: "social-engineering",
|
|
9207
|
+
description: "Testing pretext to bypass safety",
|
|
9208
|
+
pattern: /(?:for\s+testing\s+purposes?|this\s+is\s+(?:just\s+)?a\s+test|in\s+test\s+mode)\b/i
|
|
9209
|
+
}
|
|
9210
|
+
];
|
|
9211
|
+
var suspiciousPatterns = [
|
|
9212
|
+
{
|
|
9213
|
+
ruleId: "INJ-SUS-001",
|
|
9214
|
+
severity: "low",
|
|
9215
|
+
category: "suspicious-patterns",
|
|
9216
|
+
description: "Excessive consecutive whitespace (>10 chars) mid-line that may hide content",
|
|
9217
|
+
// Only match whitespace runs not at the start of a line (indentation is normal)
|
|
9218
|
+
pattern: /\S\s{11,}/
|
|
9219
|
+
},
|
|
9220
|
+
{
|
|
9221
|
+
ruleId: "INJ-SUS-002",
|
|
9222
|
+
severity: "low",
|
|
9223
|
+
category: "suspicious-patterns",
|
|
9224
|
+
description: "Repeated delimiters (>5) that may indicate obfuscation",
|
|
9225
|
+
pattern: /([|;,=\-_~`])\1{5,}/
|
|
9226
|
+
},
|
|
9227
|
+
{
|
|
9228
|
+
ruleId: "INJ-SUS-003",
|
|
9229
|
+
severity: "low",
|
|
9230
|
+
category: "suspicious-patterns",
|
|
9231
|
+
description: "Mathematical alphanumeric symbols used as Latin character substitutes",
|
|
9232
|
+
// Mathematical bold/italic/script Unicode ranges (U+1D400-U+1D7FF)
|
|
9233
|
+
pattern: /[\uD835][\uDC00-\uDFFF]/
|
|
9234
|
+
}
|
|
9235
|
+
];
|
|
9236
|
+
var ALL_PATTERNS = [
|
|
9237
|
+
...hiddenUnicodePatterns,
|
|
9238
|
+
...reRolingPatterns,
|
|
9239
|
+
...permissionEscalationPatterns,
|
|
9240
|
+
...encodedPayloadPatterns,
|
|
9241
|
+
...indirectInjectionPatterns,
|
|
9242
|
+
...contextManipulationPatterns,
|
|
9243
|
+
...socialEngineeringPatterns,
|
|
9244
|
+
...suspiciousPatterns
|
|
9245
|
+
];
|
|
9246
|
+
function scanForInjection(text) {
|
|
9247
|
+
const findings = [];
|
|
9248
|
+
const lines = text.split("\n");
|
|
9249
|
+
for (let lineIdx = 0; lineIdx < lines.length; lineIdx++) {
|
|
9250
|
+
const line = lines[lineIdx];
|
|
9251
|
+
for (const rule of ALL_PATTERNS) {
|
|
9252
|
+
if (rule.pattern.test(line)) {
|
|
9253
|
+
findings.push({
|
|
9254
|
+
severity: rule.severity,
|
|
9255
|
+
ruleId: rule.ruleId,
|
|
9256
|
+
match: line.trim(),
|
|
9257
|
+
line: lineIdx + 1
|
|
8900
9258
|
});
|
|
8901
9259
|
}
|
|
8902
9260
|
}
|
|
8903
9261
|
}
|
|
8904
|
-
|
|
9262
|
+
const severityOrder = {
|
|
9263
|
+
high: 0,
|
|
9264
|
+
medium: 1,
|
|
9265
|
+
low: 2
|
|
9266
|
+
};
|
|
9267
|
+
findings.sort((a, b) => severityOrder[a.severity] - severityOrder[b.severity]);
|
|
9268
|
+
return findings;
|
|
8905
9269
|
}
|
|
8906
|
-
|
|
8907
|
-
|
|
8908
|
-
|
|
8909
|
-
|
|
8910
|
-
|
|
8911
|
-
|
|
8912
|
-
|
|
8913
|
-
|
|
9270
|
+
function getInjectionPatterns() {
|
|
9271
|
+
return ALL_PATTERNS;
|
|
9272
|
+
}
|
|
9273
|
+
var DESTRUCTIVE_BASH = [
|
|
9274
|
+
/\bgit\s+push\b/,
|
|
9275
|
+
/\bgit\s+commit\b/,
|
|
9276
|
+
/\brm\s+-rf?\b/,
|
|
9277
|
+
/\brm\s+-r\b/
|
|
9278
|
+
];
|
|
9279
|
+
var TAINT_DURATION_MS = 30 * 60 * 1e3;
|
|
9280
|
+
var DEFAULT_SESSION_ID = "default";
|
|
9281
|
+
function getTaintFilePath(projectRoot, sessionId) {
|
|
9282
|
+
const id = sessionId || DEFAULT_SESSION_ID;
|
|
9283
|
+
return join21(projectRoot, ".harness", `session-taint-${id}.json`);
|
|
9284
|
+
}
|
|
9285
|
+
function readTaint(projectRoot, sessionId) {
|
|
9286
|
+
const filePath = getTaintFilePath(projectRoot, sessionId);
|
|
9287
|
+
let content;
|
|
9288
|
+
try {
|
|
9289
|
+
content = readFileSync142(filePath, "utf8");
|
|
9290
|
+
} catch {
|
|
9291
|
+
return null;
|
|
9292
|
+
}
|
|
9293
|
+
let state;
|
|
9294
|
+
try {
|
|
9295
|
+
state = JSON.parse(content);
|
|
9296
|
+
} catch {
|
|
9297
|
+
try {
|
|
9298
|
+
unlinkSync(filePath);
|
|
9299
|
+
} catch {
|
|
9300
|
+
}
|
|
9301
|
+
return null;
|
|
9302
|
+
}
|
|
9303
|
+
if (!state.sessionId || !state.taintedAt || !state.expiresAt || !state.findings) {
|
|
9304
|
+
try {
|
|
9305
|
+
unlinkSync(filePath);
|
|
9306
|
+
} catch {
|
|
9307
|
+
}
|
|
9308
|
+
return null;
|
|
9309
|
+
}
|
|
9310
|
+
return state;
|
|
9311
|
+
}
|
|
9312
|
+
function checkTaint(projectRoot, sessionId) {
|
|
9313
|
+
const state = readTaint(projectRoot, sessionId);
|
|
9314
|
+
if (!state) {
|
|
9315
|
+
return { tainted: false, expired: false, state: null };
|
|
9316
|
+
}
|
|
9317
|
+
const now = /* @__PURE__ */ new Date();
|
|
9318
|
+
const expiresAt = new Date(state.expiresAt);
|
|
9319
|
+
if (now >= expiresAt) {
|
|
9320
|
+
const filePath = getTaintFilePath(projectRoot, sessionId);
|
|
9321
|
+
try {
|
|
9322
|
+
unlinkSync(filePath);
|
|
9323
|
+
} catch {
|
|
9324
|
+
}
|
|
9325
|
+
return { tainted: false, expired: true, state };
|
|
9326
|
+
}
|
|
9327
|
+
return { tainted: true, expired: false, state };
|
|
9328
|
+
}
|
|
9329
|
+
function writeTaint(projectRoot, sessionId, reason, findings, source) {
|
|
9330
|
+
const id = sessionId || DEFAULT_SESSION_ID;
|
|
9331
|
+
const filePath = getTaintFilePath(projectRoot, id);
|
|
9332
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
9333
|
+
const dir = dirname8(filePath);
|
|
9334
|
+
mkdirSync11(dir, { recursive: true });
|
|
9335
|
+
const existing = readTaint(projectRoot, id);
|
|
9336
|
+
const maxSeverity = findings.some((f) => f.severity === "high") ? "high" : "medium";
|
|
9337
|
+
const taintFindings = findings.map((f) => ({
|
|
9338
|
+
ruleId: f.ruleId,
|
|
9339
|
+
severity: f.severity,
|
|
9340
|
+
match: f.match,
|
|
9341
|
+
source,
|
|
9342
|
+
detectedAt: now
|
|
9343
|
+
}));
|
|
9344
|
+
const state = {
|
|
9345
|
+
sessionId: id,
|
|
9346
|
+
taintedAt: existing?.taintedAt || now,
|
|
9347
|
+
expiresAt: new Date(Date.now() + TAINT_DURATION_MS).toISOString(),
|
|
9348
|
+
reason,
|
|
9349
|
+
severity: existing?.severity === "high" || maxSeverity === "high" ? "high" : "medium",
|
|
9350
|
+
findings: [...existing?.findings || [], ...taintFindings]
|
|
9351
|
+
};
|
|
9352
|
+
writeFileSync11(filePath, JSON.stringify(state, null, 2) + "\n");
|
|
9353
|
+
return state;
|
|
9354
|
+
}
|
|
9355
|
+
function clearTaint(projectRoot, sessionId) {
|
|
9356
|
+
if (sessionId) {
|
|
9357
|
+
const filePath = getTaintFilePath(projectRoot, sessionId);
|
|
9358
|
+
try {
|
|
9359
|
+
unlinkSync(filePath);
|
|
9360
|
+
return 1;
|
|
9361
|
+
} catch {
|
|
9362
|
+
return 0;
|
|
9363
|
+
}
|
|
9364
|
+
}
|
|
9365
|
+
const harnessDir = join21(projectRoot, ".harness");
|
|
9366
|
+
let count = 0;
|
|
9367
|
+
try {
|
|
9368
|
+
const files = readdirSync3(harnessDir);
|
|
9369
|
+
for (const file of files) {
|
|
9370
|
+
if (file.startsWith("session-taint-") && file.endsWith(".json")) {
|
|
9371
|
+
try {
|
|
9372
|
+
unlinkSync(join21(harnessDir, file));
|
|
9373
|
+
count++;
|
|
9374
|
+
} catch {
|
|
9375
|
+
}
|
|
9376
|
+
}
|
|
9377
|
+
}
|
|
9378
|
+
} catch {
|
|
9379
|
+
}
|
|
9380
|
+
return count;
|
|
9381
|
+
}
|
|
9382
|
+
function listTaintedSessions(projectRoot) {
|
|
9383
|
+
const harnessDir = join21(projectRoot, ".harness");
|
|
9384
|
+
const sessions = [];
|
|
9385
|
+
try {
|
|
9386
|
+
const files = readdirSync3(harnessDir);
|
|
9387
|
+
for (const file of files) {
|
|
9388
|
+
if (file.startsWith("session-taint-") && file.endsWith(".json")) {
|
|
9389
|
+
const sessionId = file.replace("session-taint-", "").replace(".json", "");
|
|
9390
|
+
const result = checkTaint(projectRoot, sessionId);
|
|
9391
|
+
if (result.tainted) {
|
|
9392
|
+
sessions.push(sessionId);
|
|
9393
|
+
}
|
|
9394
|
+
}
|
|
9395
|
+
}
|
|
9396
|
+
} catch {
|
|
9397
|
+
}
|
|
9398
|
+
return sessions;
|
|
9399
|
+
}
|
|
9400
|
+
function mapSecuritySeverity(severity) {
|
|
9401
|
+
if (severity === "error") return "high";
|
|
9402
|
+
if (severity === "warning") return "medium";
|
|
9403
|
+
return "low";
|
|
9404
|
+
}
|
|
9405
|
+
function computeOverallSeverity(findings) {
|
|
9406
|
+
if (findings.length === 0) return "clean";
|
|
9407
|
+
if (findings.some((f) => f.severity === "high")) return "high";
|
|
9408
|
+
if (findings.some((f) => f.severity === "medium")) return "medium";
|
|
9409
|
+
return "low";
|
|
9410
|
+
}
|
|
9411
|
+
function computeScanExitCode(results) {
|
|
9412
|
+
for (const r of results) {
|
|
9413
|
+
if (r.overallSeverity === "high") return 2;
|
|
9414
|
+
}
|
|
9415
|
+
for (const r of results) {
|
|
9416
|
+
if (r.overallSeverity === "medium") return 1;
|
|
9417
|
+
}
|
|
9418
|
+
return 0;
|
|
9419
|
+
}
|
|
9420
|
+
function mapInjectionFindings(injectionFindings) {
|
|
9421
|
+
return injectionFindings.map((f) => ({
|
|
9422
|
+
ruleId: f.ruleId,
|
|
9423
|
+
severity: f.severity,
|
|
9424
|
+
message: `Injection pattern detected: ${f.ruleId}`,
|
|
9425
|
+
match: f.match,
|
|
9426
|
+
...f.line !== void 0 ? { line: f.line } : {}
|
|
9427
|
+
}));
|
|
9428
|
+
}
|
|
9429
|
+
function isDuplicateFinding(existing, secFinding) {
|
|
9430
|
+
return existing.some(
|
|
9431
|
+
(e) => e.line === secFinding.line && e.match === secFinding.match.trim() && e.ruleId.split("-")[0] === secFinding.ruleId.split("-")[0]
|
|
9432
|
+
);
|
|
9433
|
+
}
|
|
9434
|
+
function mapSecurityFindings(secFindings, existing) {
|
|
9435
|
+
const result = [];
|
|
9436
|
+
for (const f of secFindings) {
|
|
9437
|
+
if (!isDuplicateFinding(existing, f)) {
|
|
9438
|
+
result.push({
|
|
9439
|
+
ruleId: f.ruleId,
|
|
9440
|
+
severity: mapSecuritySeverity(f.severity),
|
|
9441
|
+
message: f.message,
|
|
9442
|
+
match: f.match,
|
|
9443
|
+
...f.line !== void 0 ? { line: f.line } : {}
|
|
9444
|
+
});
|
|
9445
|
+
}
|
|
9446
|
+
}
|
|
9447
|
+
return result;
|
|
9448
|
+
}
|
|
9449
|
+
var ALL_CHECKS = [
|
|
9450
|
+
"validate",
|
|
9451
|
+
"deps",
|
|
9452
|
+
"docs",
|
|
9453
|
+
"entropy",
|
|
9454
|
+
"security",
|
|
9455
|
+
"perf",
|
|
9456
|
+
"phase-gate",
|
|
9457
|
+
"arch"
|
|
9458
|
+
];
|
|
9459
|
+
async function runValidateCheck(projectRoot, config) {
|
|
9460
|
+
const issues = [];
|
|
9461
|
+
const agentsPath = path15.join(projectRoot, config.agentsMapPath ?? "AGENTS.md");
|
|
9462
|
+
const result = await validateAgentsMap(agentsPath);
|
|
9463
|
+
if (!result.ok) {
|
|
9464
|
+
issues.push({ severity: "error", message: result.error.message });
|
|
9465
|
+
} else if (!result.value.valid) {
|
|
9466
|
+
if (result.value.errors) {
|
|
9467
|
+
for (const err of result.value.errors) {
|
|
9468
|
+
issues.push({ severity: "error", message: err.message });
|
|
9469
|
+
}
|
|
9470
|
+
}
|
|
9471
|
+
for (const section of result.value.missingSections) {
|
|
9472
|
+
issues.push({ severity: "warning", message: `Missing section: ${section}` });
|
|
9473
|
+
}
|
|
9474
|
+
for (const link of result.value.brokenLinks) {
|
|
9475
|
+
issues.push({
|
|
9476
|
+
severity: "warning",
|
|
9477
|
+
message: `Broken link: ${link.text} \u2192 ${link.path}`,
|
|
9478
|
+
file: link.path
|
|
9479
|
+
});
|
|
9480
|
+
}
|
|
9481
|
+
}
|
|
9482
|
+
return issues;
|
|
9483
|
+
}
|
|
9484
|
+
async function runDepsCheck(projectRoot, config) {
|
|
9485
|
+
const issues = [];
|
|
9486
|
+
const rawLayers = config.layers;
|
|
9487
|
+
if (rawLayers && rawLayers.length > 0) {
|
|
9488
|
+
const parser = new TypeScriptParser();
|
|
9489
|
+
const layers = rawLayers.map(
|
|
9490
|
+
(l) => defineLayer(
|
|
9491
|
+
l.name,
|
|
9492
|
+
Array.isArray(l.patterns) ? l.patterns : [l.pattern],
|
|
9493
|
+
l.allowedDependencies
|
|
9494
|
+
)
|
|
9495
|
+
);
|
|
9496
|
+
const result = await validateDependencies({
|
|
9497
|
+
layers,
|
|
9498
|
+
rootDir: projectRoot,
|
|
9499
|
+
parser
|
|
9500
|
+
});
|
|
9501
|
+
if (!result.ok) {
|
|
9502
|
+
issues.push({ severity: "error", message: result.error.message });
|
|
9503
|
+
} else if (result.value.violations.length > 0) {
|
|
9504
|
+
for (const v of result.value.violations) {
|
|
9505
|
+
issues.push({
|
|
9506
|
+
severity: "error",
|
|
9507
|
+
message: `${v.reason}: ${v.file} imports ${v.imports} (${v.fromLayer} \u2192 ${v.toLayer})`,
|
|
9508
|
+
file: v.file,
|
|
9509
|
+
line: v.line
|
|
9510
|
+
});
|
|
9511
|
+
}
|
|
9512
|
+
}
|
|
9513
|
+
}
|
|
9514
|
+
return issues;
|
|
9515
|
+
}
|
|
9516
|
+
async function runDocsCheck(projectRoot, config) {
|
|
9517
|
+
const issues = [];
|
|
9518
|
+
const docsDir = path15.join(projectRoot, config.docsDir ?? "docs");
|
|
9519
|
+
const entropyConfig = config.entropy || {};
|
|
9520
|
+
const result = await checkDocCoverage("project", {
|
|
9521
|
+
docsDir,
|
|
9522
|
+
sourceDir: projectRoot,
|
|
9523
|
+
excludePatterns: entropyConfig.excludePatterns || [
|
|
8914
9524
|
"**/node_modules/**",
|
|
8915
9525
|
"**/dist/**",
|
|
8916
9526
|
"**/*.test.ts",
|
|
@@ -9017,7 +9627,7 @@ async function runPerfCheck(projectRoot, config) {
|
|
|
9017
9627
|
if (perfReport.complexity) {
|
|
9018
9628
|
for (const v of perfReport.complexity.violations) {
|
|
9019
9629
|
issues.push({
|
|
9020
|
-
severity:
|
|
9630
|
+
severity: "warning",
|
|
9021
9631
|
message: `[Tier ${v.tier}] ${v.metric}: ${v.function} in ${v.file} (${v.value} > ${v.threshold})`,
|
|
9022
9632
|
file: v.file,
|
|
9023
9633
|
line: v.line
|
|
@@ -10978,6 +11588,7 @@ var VALID_STATUSES = /* @__PURE__ */ new Set([
|
|
|
10978
11588
|
"blocked"
|
|
10979
11589
|
]);
|
|
10980
11590
|
var EM_DASH = "\u2014";
|
|
11591
|
+
var VALID_PRIORITIES = /* @__PURE__ */ new Set(["P0", "P1", "P2", "P3"]);
|
|
10981
11592
|
function parseRoadmap(markdown) {
|
|
10982
11593
|
const fmMatch = markdown.match(/^---\n([\s\S]*?)\n---/);
|
|
10983
11594
|
if (!fmMatch) {
|
|
@@ -10988,9 +11599,12 @@ function parseRoadmap(markdown) {
|
|
|
10988
11599
|
const body = markdown.slice(fmMatch[0].length);
|
|
10989
11600
|
const milestonesResult = parseMilestones(body);
|
|
10990
11601
|
if (!milestonesResult.ok) return milestonesResult;
|
|
11602
|
+
const historyResult = parseAssignmentHistory(body);
|
|
11603
|
+
if (!historyResult.ok) return historyResult;
|
|
10991
11604
|
return Ok({
|
|
10992
11605
|
frontmatter: fmResult.value,
|
|
10993
|
-
milestones: milestonesResult.value
|
|
11606
|
+
milestones: milestonesResult.value,
|
|
11607
|
+
assignmentHistory: historyResult.value
|
|
10994
11608
|
});
|
|
10995
11609
|
}
|
|
10996
11610
|
function parseFrontmatter2(raw) {
|
|
@@ -11030,12 +11644,17 @@ function parseMilestones(body) {
|
|
|
11030
11644
|
const h2Pattern = /^## (.+)$/gm;
|
|
11031
11645
|
const h2Matches = [];
|
|
11032
11646
|
let match;
|
|
11647
|
+
let bodyEnd = body.length;
|
|
11033
11648
|
while ((match = h2Pattern.exec(body)) !== null) {
|
|
11649
|
+
if (match[1] === "Assignment History") {
|
|
11650
|
+
bodyEnd = match.index;
|
|
11651
|
+
break;
|
|
11652
|
+
}
|
|
11034
11653
|
h2Matches.push({ heading: match[1], startIndex: match.index, fullMatch: match[0] });
|
|
11035
11654
|
}
|
|
11036
11655
|
for (let i = 0; i < h2Matches.length; i++) {
|
|
11037
11656
|
const h2 = h2Matches[i];
|
|
11038
|
-
const nextStart = i + 1 < h2Matches.length ? h2Matches[i + 1].startIndex :
|
|
11657
|
+
const nextStart = i + 1 < h2Matches.length ? h2Matches[i + 1].startIndex : bodyEnd;
|
|
11039
11658
|
const sectionBody = body.slice(h2.startIndex + h2.fullMatch.length, nextStart);
|
|
11040
11659
|
const isBacklog = h2.heading === "Backlog";
|
|
11041
11660
|
const milestoneName = isBacklog ? "Backlog" : h2.heading.replace(/^Milestone:\s*/, "");
|
|
@@ -11101,15 +11720,60 @@ function parseFeatureFields(name, body) {
|
|
|
11101
11720
|
const specRaw = fieldMap.get("Spec") ?? EM_DASH;
|
|
11102
11721
|
const plans = parseListField(fieldMap, "Plans", "Plan");
|
|
11103
11722
|
const blockedBy = parseListField(fieldMap, "Blocked by", "Blockers");
|
|
11723
|
+
const assigneeRaw = fieldMap.get("Assignee") ?? EM_DASH;
|
|
11724
|
+
const priorityRaw = fieldMap.get("Priority") ?? EM_DASH;
|
|
11725
|
+
const externalIdRaw = fieldMap.get("External-ID") ?? EM_DASH;
|
|
11726
|
+
if (priorityRaw !== EM_DASH && !VALID_PRIORITIES.has(priorityRaw)) {
|
|
11727
|
+
return Err(
|
|
11728
|
+
new Error(
|
|
11729
|
+
`Feature "${name}" has invalid priority: "${priorityRaw}". Valid priorities: ${[...VALID_PRIORITIES].join(", ")}`
|
|
11730
|
+
)
|
|
11731
|
+
);
|
|
11732
|
+
}
|
|
11104
11733
|
return Ok({
|
|
11105
11734
|
name,
|
|
11106
11735
|
status: statusRaw,
|
|
11107
11736
|
spec: specRaw === EM_DASH ? null : specRaw,
|
|
11108
11737
|
plans,
|
|
11109
11738
|
blockedBy,
|
|
11110
|
-
summary: fieldMap.get("Summary") ?? ""
|
|
11739
|
+
summary: fieldMap.get("Summary") ?? "",
|
|
11740
|
+
assignee: assigneeRaw === EM_DASH ? null : assigneeRaw,
|
|
11741
|
+
priority: priorityRaw === EM_DASH ? null : priorityRaw,
|
|
11742
|
+
externalId: externalIdRaw === EM_DASH ? null : externalIdRaw
|
|
11111
11743
|
});
|
|
11112
11744
|
}
|
|
11745
|
+
function parseAssignmentHistory(body) {
|
|
11746
|
+
const historyMatch = body.match(/^## Assignment History\s*\n/m);
|
|
11747
|
+
if (!historyMatch || historyMatch.index === void 0) return Ok([]);
|
|
11748
|
+
const historyStart = historyMatch.index + historyMatch[0].length;
|
|
11749
|
+
const rawHistoryBody = body.slice(historyStart);
|
|
11750
|
+
const nextH2 = rawHistoryBody.search(/^## /m);
|
|
11751
|
+
const historyBody = nextH2 === -1 ? rawHistoryBody : rawHistoryBody.slice(0, nextH2);
|
|
11752
|
+
const records = [];
|
|
11753
|
+
const lines = historyBody.split("\n");
|
|
11754
|
+
let pastHeader = false;
|
|
11755
|
+
for (const line of lines) {
|
|
11756
|
+
const trimmed = line.trim();
|
|
11757
|
+
if (!trimmed.startsWith("|")) continue;
|
|
11758
|
+
if (!pastHeader) {
|
|
11759
|
+
if (trimmed.match(/^\|[-\s|]+\|$/)) {
|
|
11760
|
+
pastHeader = true;
|
|
11761
|
+
}
|
|
11762
|
+
continue;
|
|
11763
|
+
}
|
|
11764
|
+
const cells = trimmed.split("|").map((c) => c.trim()).filter((c) => c.length > 0);
|
|
11765
|
+
if (cells.length < 4) continue;
|
|
11766
|
+
const action = cells[2];
|
|
11767
|
+
if (!["assigned", "completed", "unassigned"].includes(action)) continue;
|
|
11768
|
+
records.push({
|
|
11769
|
+
feature: cells[0],
|
|
11770
|
+
assignee: cells[1],
|
|
11771
|
+
action,
|
|
11772
|
+
date: cells[3]
|
|
11773
|
+
});
|
|
11774
|
+
}
|
|
11775
|
+
return Ok(records);
|
|
11776
|
+
}
|
|
11113
11777
|
var EM_DASH2 = "\u2014";
|
|
11114
11778
|
function serializeRoadmap(roadmap) {
|
|
11115
11779
|
const lines = [];
|
|
@@ -11135,6 +11799,10 @@ function serializeRoadmap(roadmap) {
|
|
|
11135
11799
|
lines.push(...serializeFeature(feature));
|
|
11136
11800
|
}
|
|
11137
11801
|
}
|
|
11802
|
+
if (roadmap.assignmentHistory && roadmap.assignmentHistory.length > 0) {
|
|
11803
|
+
lines.push("");
|
|
11804
|
+
lines.push(...serializeAssignmentHistory(roadmap.assignmentHistory));
|
|
11805
|
+
}
|
|
11138
11806
|
lines.push("");
|
|
11139
11807
|
return lines.join("\n");
|
|
11140
11808
|
}
|
|
@@ -11145,7 +11813,7 @@ function serializeFeature(feature) {
|
|
|
11145
11813
|
const spec = feature.spec ?? EM_DASH2;
|
|
11146
11814
|
const plans = feature.plans.length > 0 ? feature.plans.join(", ") : EM_DASH2;
|
|
11147
11815
|
const blockedBy = feature.blockedBy.length > 0 ? feature.blockedBy.join(", ") : EM_DASH2;
|
|
11148
|
-
|
|
11816
|
+
const lines = [
|
|
11149
11817
|
`### ${feature.name}`,
|
|
11150
11818
|
"",
|
|
11151
11819
|
`- **Status:** ${feature.status}`,
|
|
@@ -11154,6 +11822,35 @@ function serializeFeature(feature) {
|
|
|
11154
11822
|
`- **Blockers:** ${blockedBy}`,
|
|
11155
11823
|
`- **Plan:** ${plans}`
|
|
11156
11824
|
];
|
|
11825
|
+
const hasExtended = feature.assignee !== null || feature.priority !== null || feature.externalId !== null;
|
|
11826
|
+
if (hasExtended) {
|
|
11827
|
+
lines.push(`- **Assignee:** ${feature.assignee ?? EM_DASH2}`);
|
|
11828
|
+
lines.push(`- **Priority:** ${feature.priority ?? EM_DASH2}`);
|
|
11829
|
+
lines.push(`- **External-ID:** ${feature.externalId ?? EM_DASH2}`);
|
|
11830
|
+
}
|
|
11831
|
+
return lines;
|
|
11832
|
+
}
|
|
11833
|
+
function serializeAssignmentHistory(records) {
|
|
11834
|
+
const lines = [
|
|
11835
|
+
"## Assignment History",
|
|
11836
|
+
"| Feature | Assignee | Action | Date |",
|
|
11837
|
+
"|---------|----------|--------|------|"
|
|
11838
|
+
];
|
|
11839
|
+
for (const record of records) {
|
|
11840
|
+
lines.push(`| ${record.feature} | ${record.assignee} | ${record.action} | ${record.date} |`);
|
|
11841
|
+
}
|
|
11842
|
+
return lines;
|
|
11843
|
+
}
|
|
11844
|
+
var STATUS_RANK = {
|
|
11845
|
+
backlog: 0,
|
|
11846
|
+
planned: 1,
|
|
11847
|
+
blocked: 1,
|
|
11848
|
+
// lateral to planned — sync can move to/from blocked freely
|
|
11849
|
+
"in-progress": 2,
|
|
11850
|
+
done: 3
|
|
11851
|
+
};
|
|
11852
|
+
function isRegression(from, to) {
|
|
11853
|
+
return STATUS_RANK[to] < STATUS_RANK[from];
|
|
11157
11854
|
}
|
|
11158
11855
|
function inferStatus(feature, projectPath, allFeatures) {
|
|
11159
11856
|
if (feature.blockedBy.length > 0) {
|
|
@@ -11221,17 +11918,6 @@ function inferStatus(feature, projectPath, allFeatures) {
|
|
|
11221
11918
|
if (anyStarted) return "in-progress";
|
|
11222
11919
|
return null;
|
|
11223
11920
|
}
|
|
11224
|
-
var STATUS_RANK = {
|
|
11225
|
-
backlog: 0,
|
|
11226
|
-
planned: 1,
|
|
11227
|
-
blocked: 1,
|
|
11228
|
-
// lateral to planned — sync can move to/from blocked freely
|
|
11229
|
-
"in-progress": 2,
|
|
11230
|
-
done: 3
|
|
11231
|
-
};
|
|
11232
|
-
function isRegression(from, to) {
|
|
11233
|
-
return STATUS_RANK[to] < STATUS_RANK[from];
|
|
11234
|
-
}
|
|
11235
11921
|
function syncRoadmap(options) {
|
|
11236
11922
|
const { projectPath, roadmap, forceSync } = options;
|
|
11237
11923
|
const allFeatures = roadmap.milestones.flatMap((m) => m.features);
|
|
@@ -11261,6 +11947,428 @@ function applySyncChanges(roadmap, changes) {
|
|
|
11261
11947
|
}
|
|
11262
11948
|
roadmap.frontmatter.lastSynced = (/* @__PURE__ */ new Date()).toISOString();
|
|
11263
11949
|
}
|
|
11950
|
+
function resolveReverseStatus(externalStatus, labels, config) {
|
|
11951
|
+
const reverseMap = config.reverseStatusMap;
|
|
11952
|
+
if (!reverseMap) return null;
|
|
11953
|
+
if (reverseMap[externalStatus]) {
|
|
11954
|
+
return reverseMap[externalStatus];
|
|
11955
|
+
}
|
|
11956
|
+
const statusLabels = ["in-progress", "blocked", "planned"];
|
|
11957
|
+
const matchingLabels = labels.filter((l) => statusLabels.includes(l));
|
|
11958
|
+
if (matchingLabels.length === 1) {
|
|
11959
|
+
const compoundKey = `${externalStatus}:${matchingLabels[0]}`;
|
|
11960
|
+
if (reverseMap[compoundKey]) {
|
|
11961
|
+
return reverseMap[compoundKey];
|
|
11962
|
+
}
|
|
11963
|
+
}
|
|
11964
|
+
return null;
|
|
11965
|
+
}
|
|
11966
|
+
function parseExternalId(externalId) {
|
|
11967
|
+
const match = externalId.match(/^github:([^/]+)\/([^#]+)#(\d+)$/);
|
|
11968
|
+
if (!match) return null;
|
|
11969
|
+
return { owner: match[1], repo: match[2], number: parseInt(match[3], 10) };
|
|
11970
|
+
}
|
|
11971
|
+
function buildExternalId(owner, repo, number) {
|
|
11972
|
+
return `github:${owner}/${repo}#${number}`;
|
|
11973
|
+
}
|
|
11974
|
+
function labelsForStatus(status, config) {
|
|
11975
|
+
const base = config.labels ?? [];
|
|
11976
|
+
const externalStatus = config.statusMap[status];
|
|
11977
|
+
if (externalStatus === "open" && status !== "backlog") {
|
|
11978
|
+
return [...base, status];
|
|
11979
|
+
}
|
|
11980
|
+
return [...base];
|
|
11981
|
+
}
|
|
11982
|
+
var GitHubIssuesSyncAdapter = class {
|
|
11983
|
+
token;
|
|
11984
|
+
config;
|
|
11985
|
+
fetchFn;
|
|
11986
|
+
apiBase;
|
|
11987
|
+
owner;
|
|
11988
|
+
repo;
|
|
11989
|
+
constructor(options) {
|
|
11990
|
+
this.token = options.token;
|
|
11991
|
+
this.config = options.config;
|
|
11992
|
+
this.fetchFn = options.fetchFn ?? globalThis.fetch;
|
|
11993
|
+
this.apiBase = options.apiBase ?? "https://api.github.com";
|
|
11994
|
+
const repoParts = (options.config.repo ?? "").split("/");
|
|
11995
|
+
if (repoParts.length !== 2 || !repoParts[0] || !repoParts[1]) {
|
|
11996
|
+
throw new Error(`Invalid repo format: "${options.config.repo}". Expected "owner/repo".`);
|
|
11997
|
+
}
|
|
11998
|
+
this.owner = repoParts[0];
|
|
11999
|
+
this.repo = repoParts[1];
|
|
12000
|
+
}
|
|
12001
|
+
headers() {
|
|
12002
|
+
return {
|
|
12003
|
+
Authorization: `Bearer ${this.token}`,
|
|
12004
|
+
Accept: "application/vnd.github+json",
|
|
12005
|
+
"Content-Type": "application/json",
|
|
12006
|
+
"X-GitHub-Api-Version": "2022-11-28"
|
|
12007
|
+
};
|
|
12008
|
+
}
|
|
12009
|
+
async createTicket(feature, milestone) {
|
|
12010
|
+
try {
|
|
12011
|
+
const labels = labelsForStatus(feature.status, this.config);
|
|
12012
|
+
const body = [
|
|
12013
|
+
feature.summary,
|
|
12014
|
+
"",
|
|
12015
|
+
`**Milestone:** ${milestone}`,
|
|
12016
|
+
feature.spec ? `**Spec:** ${feature.spec}` : ""
|
|
12017
|
+
].filter(Boolean).join("\n");
|
|
12018
|
+
const response = await this.fetchFn(
|
|
12019
|
+
`${this.apiBase}/repos/${this.owner}/${this.repo}/issues`,
|
|
12020
|
+
{
|
|
12021
|
+
method: "POST",
|
|
12022
|
+
headers: this.headers(),
|
|
12023
|
+
body: JSON.stringify({
|
|
12024
|
+
title: feature.name,
|
|
12025
|
+
body,
|
|
12026
|
+
labels
|
|
12027
|
+
})
|
|
12028
|
+
}
|
|
12029
|
+
);
|
|
12030
|
+
if (!response.ok) {
|
|
12031
|
+
const text = await response.text();
|
|
12032
|
+
return Err(new Error(`GitHub API error ${response.status}: ${text}`));
|
|
12033
|
+
}
|
|
12034
|
+
const data = await response.json();
|
|
12035
|
+
const externalId = buildExternalId(this.owner, this.repo, data.number);
|
|
12036
|
+
return Ok({ externalId, url: data.html_url });
|
|
12037
|
+
} catch (error) {
|
|
12038
|
+
return Err(error instanceof Error ? error : new Error(String(error)));
|
|
12039
|
+
}
|
|
12040
|
+
}
|
|
12041
|
+
async updateTicket(externalId, changes) {
|
|
12042
|
+
try {
|
|
12043
|
+
const parsed = parseExternalId(externalId);
|
|
12044
|
+
if (!parsed) return Err(new Error(`Invalid externalId format: "${externalId}"`));
|
|
12045
|
+
const patch = {};
|
|
12046
|
+
if (changes.name !== void 0) patch.title = changes.name;
|
|
12047
|
+
if (changes.summary !== void 0) {
|
|
12048
|
+
const body = [changes.summary, "", changes.spec ? `**Spec:** ${changes.spec}` : ""].filter(Boolean).join("\n");
|
|
12049
|
+
patch.body = body;
|
|
12050
|
+
}
|
|
12051
|
+
if (changes.status !== void 0) {
|
|
12052
|
+
const externalStatus = this.config.statusMap[changes.status];
|
|
12053
|
+
patch.state = externalStatus;
|
|
12054
|
+
patch.labels = labelsForStatus(changes.status, this.config);
|
|
12055
|
+
}
|
|
12056
|
+
const response = await this.fetchFn(
|
|
12057
|
+
`${this.apiBase}/repos/${parsed.owner}/${parsed.repo}/issues/${parsed.number}`,
|
|
12058
|
+
{
|
|
12059
|
+
method: "PATCH",
|
|
12060
|
+
headers: this.headers(),
|
|
12061
|
+
body: JSON.stringify(patch)
|
|
12062
|
+
}
|
|
12063
|
+
);
|
|
12064
|
+
if (!response.ok) {
|
|
12065
|
+
const text = await response.text();
|
|
12066
|
+
return Err(new Error(`GitHub API error ${response.status}: ${text}`));
|
|
12067
|
+
}
|
|
12068
|
+
const data = await response.json();
|
|
12069
|
+
return Ok({ externalId, url: data.html_url });
|
|
12070
|
+
} catch (error) {
|
|
12071
|
+
return Err(error instanceof Error ? error : new Error(String(error)));
|
|
12072
|
+
}
|
|
12073
|
+
}
|
|
12074
|
+
async fetchTicketState(externalId) {
|
|
12075
|
+
try {
|
|
12076
|
+
const parsed = parseExternalId(externalId);
|
|
12077
|
+
if (!parsed) return Err(new Error(`Invalid externalId format: "${externalId}"`));
|
|
12078
|
+
const response = await this.fetchFn(
|
|
12079
|
+
`${this.apiBase}/repos/${parsed.owner}/${parsed.repo}/issues/${parsed.number}`,
|
|
12080
|
+
{
|
|
12081
|
+
method: "GET",
|
|
12082
|
+
headers: this.headers()
|
|
12083
|
+
}
|
|
12084
|
+
);
|
|
12085
|
+
if (!response.ok) {
|
|
12086
|
+
const text = await response.text();
|
|
12087
|
+
return Err(new Error(`GitHub API error ${response.status}: ${text}`));
|
|
12088
|
+
}
|
|
12089
|
+
const data = await response.json();
|
|
12090
|
+
return Ok({
|
|
12091
|
+
externalId,
|
|
12092
|
+
status: data.state,
|
|
12093
|
+
labels: data.labels.map((l) => l.name),
|
|
12094
|
+
assignee: data.assignee ? `@${data.assignee.login}` : null
|
|
12095
|
+
});
|
|
12096
|
+
} catch (error) {
|
|
12097
|
+
return Err(error instanceof Error ? error : new Error(String(error)));
|
|
12098
|
+
}
|
|
12099
|
+
}
|
|
12100
|
+
async fetchAllTickets() {
|
|
12101
|
+
try {
|
|
12102
|
+
const filterLabels = this.config.labels ?? [];
|
|
12103
|
+
const labelsParam = filterLabels.length > 0 ? `&labels=${filterLabels.join(",")}` : "";
|
|
12104
|
+
const tickets = [];
|
|
12105
|
+
let page = 1;
|
|
12106
|
+
const perPage = 100;
|
|
12107
|
+
while (true) {
|
|
12108
|
+
const response = await this.fetchFn(
|
|
12109
|
+
`${this.apiBase}/repos/${this.owner}/${this.repo}/issues?state=all&per_page=${perPage}&page=${page}${labelsParam}`,
|
|
12110
|
+
{
|
|
12111
|
+
method: "GET",
|
|
12112
|
+
headers: this.headers()
|
|
12113
|
+
}
|
|
12114
|
+
);
|
|
12115
|
+
if (!response.ok) {
|
|
12116
|
+
const text = await response.text();
|
|
12117
|
+
return Err(new Error(`GitHub API error ${response.status}: ${text}`));
|
|
12118
|
+
}
|
|
12119
|
+
const data = await response.json();
|
|
12120
|
+
const issues = data.filter((d) => !d.pull_request);
|
|
12121
|
+
for (const issue of issues) {
|
|
12122
|
+
tickets.push({
|
|
12123
|
+
externalId: buildExternalId(this.owner, this.repo, issue.number),
|
|
12124
|
+
status: issue.state,
|
|
12125
|
+
labels: issue.labels.map((l) => l.name),
|
|
12126
|
+
assignee: issue.assignee ? `@${issue.assignee.login}` : null
|
|
12127
|
+
});
|
|
12128
|
+
}
|
|
12129
|
+
if (data.length < perPage) break;
|
|
12130
|
+
page++;
|
|
12131
|
+
}
|
|
12132
|
+
return Ok(tickets);
|
|
12133
|
+
} catch (error) {
|
|
12134
|
+
return Err(error instanceof Error ? error : new Error(String(error)));
|
|
12135
|
+
}
|
|
12136
|
+
}
|
|
12137
|
+
async assignTicket(externalId, assignee) {
|
|
12138
|
+
try {
|
|
12139
|
+
const parsed = parseExternalId(externalId);
|
|
12140
|
+
if (!parsed) return Err(new Error(`Invalid externalId format: "${externalId}"`));
|
|
12141
|
+
const login = assignee.startsWith("@") ? assignee.slice(1) : assignee;
|
|
12142
|
+
const response = await this.fetchFn(
|
|
12143
|
+
`${this.apiBase}/repos/${parsed.owner}/${parsed.repo}/issues/${parsed.number}/assignees`,
|
|
12144
|
+
{
|
|
12145
|
+
method: "POST",
|
|
12146
|
+
headers: this.headers(),
|
|
12147
|
+
body: JSON.stringify({ assignees: [login] })
|
|
12148
|
+
}
|
|
12149
|
+
);
|
|
12150
|
+
if (!response.ok) {
|
|
12151
|
+
const text = await response.text();
|
|
12152
|
+
return Err(new Error(`GitHub API error ${response.status}: ${text}`));
|
|
12153
|
+
}
|
|
12154
|
+
return Ok(void 0);
|
|
12155
|
+
} catch (error) {
|
|
12156
|
+
return Err(error instanceof Error ? error : new Error(String(error)));
|
|
12157
|
+
}
|
|
12158
|
+
}
|
|
12159
|
+
};
|
|
12160
|
+
function emptySyncResult() {
|
|
12161
|
+
return { created: [], updated: [], assignmentChanges: [], errors: [] };
|
|
12162
|
+
}
|
|
12163
|
+
async function syncToExternal(roadmap, adapter, _config) {
|
|
12164
|
+
const result = emptySyncResult();
|
|
12165
|
+
for (const milestone of roadmap.milestones) {
|
|
12166
|
+
for (const feature of milestone.features) {
|
|
12167
|
+
if (!feature.externalId) {
|
|
12168
|
+
const createResult = await adapter.createTicket(feature, milestone.name);
|
|
12169
|
+
if (createResult.ok) {
|
|
12170
|
+
feature.externalId = createResult.value.externalId;
|
|
12171
|
+
result.created.push(createResult.value);
|
|
12172
|
+
} else {
|
|
12173
|
+
result.errors.push({ featureOrId: feature.name, error: createResult.error });
|
|
12174
|
+
}
|
|
12175
|
+
} else {
|
|
12176
|
+
const updateResult = await adapter.updateTicket(feature.externalId, feature);
|
|
12177
|
+
if (updateResult.ok) {
|
|
12178
|
+
result.updated.push(feature.externalId);
|
|
12179
|
+
} else {
|
|
12180
|
+
result.errors.push({ featureOrId: feature.externalId, error: updateResult.error });
|
|
12181
|
+
}
|
|
12182
|
+
}
|
|
12183
|
+
}
|
|
12184
|
+
}
|
|
12185
|
+
return result;
|
|
12186
|
+
}
|
|
12187
|
+
async function syncFromExternal(roadmap, adapter, config, options) {
|
|
12188
|
+
const result = emptySyncResult();
|
|
12189
|
+
const forceSync = options?.forceSync ?? false;
|
|
12190
|
+
const featureByExternalId = /* @__PURE__ */ new Map();
|
|
12191
|
+
for (const milestone of roadmap.milestones) {
|
|
12192
|
+
for (const feature of milestone.features) {
|
|
12193
|
+
if (feature.externalId) {
|
|
12194
|
+
featureByExternalId.set(feature.externalId, feature);
|
|
12195
|
+
}
|
|
12196
|
+
}
|
|
12197
|
+
}
|
|
12198
|
+
if (featureByExternalId.size === 0) return result;
|
|
12199
|
+
const fetchResult = await adapter.fetchAllTickets();
|
|
12200
|
+
if (!fetchResult.ok) {
|
|
12201
|
+
result.errors.push({ featureOrId: "*", error: fetchResult.error });
|
|
12202
|
+
return result;
|
|
12203
|
+
}
|
|
12204
|
+
for (const ticketState of fetchResult.value) {
|
|
12205
|
+
const feature = featureByExternalId.get(ticketState.externalId);
|
|
12206
|
+
if (!feature) continue;
|
|
12207
|
+
if (ticketState.assignee !== feature.assignee) {
|
|
12208
|
+
result.assignmentChanges.push({
|
|
12209
|
+
feature: feature.name,
|
|
12210
|
+
from: feature.assignee,
|
|
12211
|
+
to: ticketState.assignee
|
|
12212
|
+
});
|
|
12213
|
+
feature.assignee = ticketState.assignee;
|
|
12214
|
+
}
|
|
12215
|
+
const resolvedStatus = resolveReverseStatus(ticketState.status, ticketState.labels, config);
|
|
12216
|
+
if (resolvedStatus && resolvedStatus !== feature.status) {
|
|
12217
|
+
const newStatus = resolvedStatus;
|
|
12218
|
+
if (!forceSync && isRegression(feature.status, newStatus)) {
|
|
12219
|
+
continue;
|
|
12220
|
+
}
|
|
12221
|
+
feature.status = newStatus;
|
|
12222
|
+
}
|
|
12223
|
+
}
|
|
12224
|
+
return result;
|
|
12225
|
+
}
|
|
12226
|
+
var syncMutex = Promise.resolve();
|
|
12227
|
+
async function fullSync(roadmapPath, adapter, config, options) {
|
|
12228
|
+
const previousSync = syncMutex;
|
|
12229
|
+
let releaseMutex;
|
|
12230
|
+
syncMutex = new Promise((resolve5) => {
|
|
12231
|
+
releaseMutex = resolve5;
|
|
12232
|
+
});
|
|
12233
|
+
await previousSync;
|
|
12234
|
+
try {
|
|
12235
|
+
const raw = fs20.readFileSync(roadmapPath, "utf-8");
|
|
12236
|
+
const parseResult = parseRoadmap(raw);
|
|
12237
|
+
if (!parseResult.ok) {
|
|
12238
|
+
return {
|
|
12239
|
+
...emptySyncResult(),
|
|
12240
|
+
errors: [{ featureOrId: "*", error: parseResult.error }]
|
|
12241
|
+
};
|
|
12242
|
+
}
|
|
12243
|
+
const roadmap = parseResult.value;
|
|
12244
|
+
const pushResult = await syncToExternal(roadmap, adapter, config);
|
|
12245
|
+
const pullResult = await syncFromExternal(roadmap, adapter, config, options);
|
|
12246
|
+
fs20.writeFileSync(roadmapPath, serializeRoadmap(roadmap), "utf-8");
|
|
12247
|
+
return {
|
|
12248
|
+
created: pushResult.created,
|
|
12249
|
+
updated: pushResult.updated,
|
|
12250
|
+
assignmentChanges: pullResult.assignmentChanges,
|
|
12251
|
+
errors: [...pushResult.errors, ...pullResult.errors]
|
|
12252
|
+
};
|
|
12253
|
+
} finally {
|
|
12254
|
+
releaseMutex();
|
|
12255
|
+
}
|
|
12256
|
+
}
|
|
12257
|
+
var PRIORITY_RANK = {
|
|
12258
|
+
P0: 0,
|
|
12259
|
+
P1: 1,
|
|
12260
|
+
P2: 2,
|
|
12261
|
+
P3: 3
|
|
12262
|
+
};
|
|
12263
|
+
var POSITION_WEIGHT = 0.5;
|
|
12264
|
+
var DEPENDENTS_WEIGHT = 0.3;
|
|
12265
|
+
var AFFINITY_WEIGHT = 0.2;
|
|
12266
|
+
function scoreRoadmapCandidates(roadmap, options) {
|
|
12267
|
+
const allFeatures = roadmap.milestones.flatMap((m) => m.features);
|
|
12268
|
+
const allFeatureNames = new Set(allFeatures.map((f) => f.name.toLowerCase()));
|
|
12269
|
+
const doneFeatures = new Set(
|
|
12270
|
+
allFeatures.filter((f) => f.status === "done").map((f) => f.name.toLowerCase())
|
|
12271
|
+
);
|
|
12272
|
+
const dependentsCount = /* @__PURE__ */ new Map();
|
|
12273
|
+
for (const feature of allFeatures) {
|
|
12274
|
+
for (const blocker of feature.blockedBy) {
|
|
12275
|
+
const key = blocker.toLowerCase();
|
|
12276
|
+
dependentsCount.set(key, (dependentsCount.get(key) ?? 0) + 1);
|
|
12277
|
+
}
|
|
12278
|
+
}
|
|
12279
|
+
const maxDependents = Math.max(1, ...dependentsCount.values());
|
|
12280
|
+
const milestoneMap = /* @__PURE__ */ new Map();
|
|
12281
|
+
for (const ms of roadmap.milestones) {
|
|
12282
|
+
milestoneMap.set(
|
|
12283
|
+
ms.name,
|
|
12284
|
+
ms.features.map((f) => f.name.toLowerCase())
|
|
12285
|
+
);
|
|
12286
|
+
}
|
|
12287
|
+
const userCompletedFeatures = /* @__PURE__ */ new Set();
|
|
12288
|
+
if (options?.currentUser) {
|
|
12289
|
+
const user = options.currentUser.toLowerCase();
|
|
12290
|
+
for (const record of roadmap.assignmentHistory) {
|
|
12291
|
+
if (record.action === "completed" && record.assignee.toLowerCase() === user) {
|
|
12292
|
+
userCompletedFeatures.add(record.feature.toLowerCase());
|
|
12293
|
+
}
|
|
12294
|
+
}
|
|
12295
|
+
}
|
|
12296
|
+
let totalPositions = 0;
|
|
12297
|
+
for (const ms of roadmap.milestones) {
|
|
12298
|
+
totalPositions += ms.features.length;
|
|
12299
|
+
}
|
|
12300
|
+
totalPositions = Math.max(1, totalPositions);
|
|
12301
|
+
const candidates = [];
|
|
12302
|
+
let globalPosition = 0;
|
|
12303
|
+
for (const ms of roadmap.milestones) {
|
|
12304
|
+
for (let featureIdx = 0; featureIdx < ms.features.length; featureIdx++) {
|
|
12305
|
+
const feature = ms.features[featureIdx];
|
|
12306
|
+
globalPosition++;
|
|
12307
|
+
if (feature.status !== "planned" && feature.status !== "backlog") continue;
|
|
12308
|
+
const isBlocked = feature.blockedBy.some((blocker) => {
|
|
12309
|
+
const key = blocker.toLowerCase();
|
|
12310
|
+
return allFeatureNames.has(key) && !doneFeatures.has(key);
|
|
12311
|
+
});
|
|
12312
|
+
if (isBlocked) continue;
|
|
12313
|
+
const positionScore = 1 - (globalPosition - 1) / totalPositions;
|
|
12314
|
+
const deps = dependentsCount.get(feature.name.toLowerCase()) ?? 0;
|
|
12315
|
+
const dependentsScore = deps / maxDependents;
|
|
12316
|
+
let affinityScore = 0;
|
|
12317
|
+
if (userCompletedFeatures.size > 0) {
|
|
12318
|
+
const completedBlockers = feature.blockedBy.filter(
|
|
12319
|
+
(b) => userCompletedFeatures.has(b.toLowerCase())
|
|
12320
|
+
);
|
|
12321
|
+
if (completedBlockers.length > 0) {
|
|
12322
|
+
affinityScore = 1;
|
|
12323
|
+
} else {
|
|
12324
|
+
const siblings = milestoneMap.get(ms.name) ?? [];
|
|
12325
|
+
const completedSiblings = siblings.filter((s) => userCompletedFeatures.has(s));
|
|
12326
|
+
if (completedSiblings.length > 0) {
|
|
12327
|
+
affinityScore = 0.5;
|
|
12328
|
+
}
|
|
12329
|
+
}
|
|
12330
|
+
}
|
|
12331
|
+
const weightedScore = POSITION_WEIGHT * positionScore + DEPENDENTS_WEIGHT * dependentsScore + AFFINITY_WEIGHT * affinityScore;
|
|
12332
|
+
const priorityTier = feature.priority ? PRIORITY_RANK[feature.priority] : null;
|
|
12333
|
+
candidates.push({
|
|
12334
|
+
feature,
|
|
12335
|
+
milestone: ms.name,
|
|
12336
|
+
positionScore,
|
|
12337
|
+
dependentsScore,
|
|
12338
|
+
affinityScore,
|
|
12339
|
+
weightedScore,
|
|
12340
|
+
priorityTier
|
|
12341
|
+
});
|
|
12342
|
+
}
|
|
12343
|
+
}
|
|
12344
|
+
candidates.sort((a, b) => {
|
|
12345
|
+
if (a.priorityTier !== null && b.priorityTier === null) return -1;
|
|
12346
|
+
if (a.priorityTier === null && b.priorityTier !== null) return 1;
|
|
12347
|
+
if (a.priorityTier !== null && b.priorityTier !== null) {
|
|
12348
|
+
if (a.priorityTier !== b.priorityTier) return a.priorityTier - b.priorityTier;
|
|
12349
|
+
}
|
|
12350
|
+
return b.weightedScore - a.weightedScore;
|
|
12351
|
+
});
|
|
12352
|
+
return candidates;
|
|
12353
|
+
}
|
|
12354
|
+
function assignFeature(roadmap, feature, assignee, date) {
|
|
12355
|
+
if (feature.assignee === assignee) return;
|
|
12356
|
+
if (feature.assignee !== null) {
|
|
12357
|
+
roadmap.assignmentHistory.push({
|
|
12358
|
+
feature: feature.name,
|
|
12359
|
+
assignee: feature.assignee,
|
|
12360
|
+
action: "unassigned",
|
|
12361
|
+
date
|
|
12362
|
+
});
|
|
12363
|
+
}
|
|
12364
|
+
feature.assignee = assignee;
|
|
12365
|
+
roadmap.assignmentHistory.push({
|
|
12366
|
+
feature: feature.name,
|
|
12367
|
+
assignee,
|
|
12368
|
+
action: "assigned",
|
|
12369
|
+
date
|
|
12370
|
+
});
|
|
12371
|
+
}
|
|
11264
12372
|
var InteractionTypeSchema = z7.enum(["question", "confirmation", "transition"]);
|
|
11265
12373
|
var QuestionSchema = z7.object({
|
|
11266
12374
|
text: z7.string(),
|
|
@@ -11291,11 +12399,12 @@ var ProjectScanner = class {
|
|
|
11291
12399
|
constructor(rootDir) {
|
|
11292
12400
|
this.rootDir = rootDir;
|
|
11293
12401
|
}
|
|
12402
|
+
rootDir;
|
|
11294
12403
|
async scan() {
|
|
11295
12404
|
let projectName = path20.basename(this.rootDir);
|
|
11296
12405
|
try {
|
|
11297
12406
|
const pkgPath = path20.join(this.rootDir, "package.json");
|
|
11298
|
-
const pkgRaw = await
|
|
12407
|
+
const pkgRaw = await fs21.readFile(pkgPath, "utf-8");
|
|
11299
12408
|
const pkg = JSON.parse(pkgRaw);
|
|
11300
12409
|
if (pkg.name) projectName = pkg.name;
|
|
11301
12410
|
} catch {
|
|
@@ -11408,8 +12517,8 @@ var BlueprintGenerator = class {
|
|
|
11408
12517
|
styles: STYLES,
|
|
11409
12518
|
scripts: SCRIPTS
|
|
11410
12519
|
});
|
|
11411
|
-
await
|
|
11412
|
-
await
|
|
12520
|
+
await fs22.mkdir(options.outputDir, { recursive: true });
|
|
12521
|
+
await fs22.writeFile(path21.join(options.outputDir, "index.html"), html);
|
|
11413
12522
|
}
|
|
11414
12523
|
};
|
|
11415
12524
|
function getStatePath() {
|
|
@@ -11427,7 +12536,7 @@ function shouldRunCheck(state, intervalMs) {
|
|
|
11427
12536
|
}
|
|
11428
12537
|
function readCheckState() {
|
|
11429
12538
|
try {
|
|
11430
|
-
const raw =
|
|
12539
|
+
const raw = fs23.readFileSync(getStatePath(), "utf-8");
|
|
11431
12540
|
const parsed = JSON.parse(raw);
|
|
11432
12541
|
if (typeof parsed === "object" && parsed !== null && "lastCheckTime" in parsed && typeof parsed.lastCheckTime === "number" && "currentVersion" in parsed && typeof parsed.currentVersion === "string") {
|
|
11433
12542
|
const state = parsed;
|
|
@@ -11528,9 +12637,9 @@ async function resolveWasmPath(grammarName) {
|
|
|
11528
12637
|
const { createRequire } = await import("module");
|
|
11529
12638
|
const require2 = createRequire(import.meta.url ?? __filename);
|
|
11530
12639
|
const pkgPath = require2.resolve("tree-sitter-wasms/package.json");
|
|
11531
|
-
const
|
|
11532
|
-
const pkgDir =
|
|
11533
|
-
return
|
|
12640
|
+
const path26 = await import("path");
|
|
12641
|
+
const pkgDir = path26.dirname(pkgPath);
|
|
12642
|
+
return path26.join(pkgDir, "out", `${grammarName}.wasm`);
|
|
11534
12643
|
}
|
|
11535
12644
|
async function loadLanguage(lang) {
|
|
11536
12645
|
const grammarName = GRAMMAR_MAP[lang];
|
|
@@ -11887,6 +12996,466 @@ async function unfoldRange(filePath, startLine, endLine) {
|
|
|
11887
12996
|
fallback: false
|
|
11888
12997
|
};
|
|
11889
12998
|
}
|
|
12999
|
+
var TOKENS_PER_MILLION = 1e6;
|
|
13000
|
+
function parseLiteLLMData(raw) {
|
|
13001
|
+
const dataset = /* @__PURE__ */ new Map();
|
|
13002
|
+
for (const [modelName, entry] of Object.entries(raw)) {
|
|
13003
|
+
if (modelName === "sample_spec") continue;
|
|
13004
|
+
if (entry.mode && entry.mode !== "chat") continue;
|
|
13005
|
+
const inputCost = entry.input_cost_per_token;
|
|
13006
|
+
const outputCost = entry.output_cost_per_token;
|
|
13007
|
+
if (inputCost == null || outputCost == null) continue;
|
|
13008
|
+
const pricing = {
|
|
13009
|
+
inputPer1M: inputCost * TOKENS_PER_MILLION,
|
|
13010
|
+
outputPer1M: outputCost * TOKENS_PER_MILLION
|
|
13011
|
+
};
|
|
13012
|
+
if (entry.cache_read_input_token_cost != null) {
|
|
13013
|
+
pricing.cacheReadPer1M = entry.cache_read_input_token_cost * TOKENS_PER_MILLION;
|
|
13014
|
+
}
|
|
13015
|
+
if (entry.cache_creation_input_token_cost != null) {
|
|
13016
|
+
pricing.cacheWritePer1M = entry.cache_creation_input_token_cost * TOKENS_PER_MILLION;
|
|
13017
|
+
}
|
|
13018
|
+
dataset.set(modelName, pricing);
|
|
13019
|
+
}
|
|
13020
|
+
return dataset;
|
|
13021
|
+
}
|
|
13022
|
+
function getModelPrice(model, dataset) {
|
|
13023
|
+
if (!model) {
|
|
13024
|
+
console.warn("[harness pricing] No model specified \u2014 cannot look up pricing.");
|
|
13025
|
+
return null;
|
|
13026
|
+
}
|
|
13027
|
+
const pricing = dataset.get(model);
|
|
13028
|
+
if (!pricing) {
|
|
13029
|
+
console.warn(
|
|
13030
|
+
`[harness pricing] No pricing data for model "${model}". Consider updating pricing data.`
|
|
13031
|
+
);
|
|
13032
|
+
return null;
|
|
13033
|
+
}
|
|
13034
|
+
return pricing;
|
|
13035
|
+
}
|
|
13036
|
+
var fallback_default = {
|
|
13037
|
+
_generatedAt: "2026-03-31",
|
|
13038
|
+
_source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
13039
|
+
models: {
|
|
13040
|
+
"claude-opus-4-20250514": {
|
|
13041
|
+
inputPer1M: 15,
|
|
13042
|
+
outputPer1M: 75,
|
|
13043
|
+
cacheReadPer1M: 1.5,
|
|
13044
|
+
cacheWritePer1M: 18.75
|
|
13045
|
+
},
|
|
13046
|
+
"claude-sonnet-4-20250514": {
|
|
13047
|
+
inputPer1M: 3,
|
|
13048
|
+
outputPer1M: 15,
|
|
13049
|
+
cacheReadPer1M: 0.3,
|
|
13050
|
+
cacheWritePer1M: 3.75
|
|
13051
|
+
},
|
|
13052
|
+
"claude-3-5-haiku-20241022": {
|
|
13053
|
+
inputPer1M: 0.8,
|
|
13054
|
+
outputPer1M: 4,
|
|
13055
|
+
cacheReadPer1M: 0.08,
|
|
13056
|
+
cacheWritePer1M: 1
|
|
13057
|
+
},
|
|
13058
|
+
"gpt-4o": {
|
|
13059
|
+
inputPer1M: 2.5,
|
|
13060
|
+
outputPer1M: 10,
|
|
13061
|
+
cacheReadPer1M: 1.25
|
|
13062
|
+
},
|
|
13063
|
+
"gpt-4o-mini": {
|
|
13064
|
+
inputPer1M: 0.15,
|
|
13065
|
+
outputPer1M: 0.6,
|
|
13066
|
+
cacheReadPer1M: 0.075
|
|
13067
|
+
},
|
|
13068
|
+
"gemini-2.0-flash": {
|
|
13069
|
+
inputPer1M: 0.1,
|
|
13070
|
+
outputPer1M: 0.4,
|
|
13071
|
+
cacheReadPer1M: 0.025
|
|
13072
|
+
},
|
|
13073
|
+
"gemini-2.5-pro": {
|
|
13074
|
+
inputPer1M: 1.25,
|
|
13075
|
+
outputPer1M: 10,
|
|
13076
|
+
cacheReadPer1M: 0.3125
|
|
13077
|
+
}
|
|
13078
|
+
}
|
|
13079
|
+
};
|
|
13080
|
+
var LITELLM_PRICING_URL = "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json";
|
|
13081
|
+
var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
13082
|
+
var STALENESS_WARNING_DAYS = 7;
|
|
13083
|
+
function getCachePath(projectRoot) {
|
|
13084
|
+
return path23.join(projectRoot, ".harness", "cache", "pricing.json");
|
|
13085
|
+
}
|
|
13086
|
+
function getStalenessMarkerPath(projectRoot) {
|
|
13087
|
+
return path23.join(projectRoot, ".harness", "cache", "staleness-marker.json");
|
|
13088
|
+
}
|
|
13089
|
+
async function readDiskCache(projectRoot) {
|
|
13090
|
+
try {
|
|
13091
|
+
const raw = await fs24.readFile(getCachePath(projectRoot), "utf-8");
|
|
13092
|
+
return JSON.parse(raw);
|
|
13093
|
+
} catch {
|
|
13094
|
+
return null;
|
|
13095
|
+
}
|
|
13096
|
+
}
|
|
13097
|
+
async function writeDiskCache(projectRoot, data) {
|
|
13098
|
+
const cachePath = getCachePath(projectRoot);
|
|
13099
|
+
await fs24.mkdir(path23.dirname(cachePath), { recursive: true });
|
|
13100
|
+
await fs24.writeFile(cachePath, JSON.stringify(data, null, 2));
|
|
13101
|
+
}
|
|
13102
|
+
async function fetchFromNetwork() {
|
|
13103
|
+
try {
|
|
13104
|
+
const response = await fetch(LITELLM_PRICING_URL);
|
|
13105
|
+
if (!response.ok) return null;
|
|
13106
|
+
const data = await response.json();
|
|
13107
|
+
if (typeof data !== "object" || data === null || Array.isArray(data)) return null;
|
|
13108
|
+
return {
|
|
13109
|
+
fetchedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
13110
|
+
data
|
|
13111
|
+
};
|
|
13112
|
+
} catch {
|
|
13113
|
+
return null;
|
|
13114
|
+
}
|
|
13115
|
+
}
|
|
13116
|
+
function loadFallbackDataset() {
|
|
13117
|
+
const fb = fallback_default;
|
|
13118
|
+
const dataset = /* @__PURE__ */ new Map();
|
|
13119
|
+
for (const [model, pricing] of Object.entries(fb.models)) {
|
|
13120
|
+
dataset.set(model, pricing);
|
|
13121
|
+
}
|
|
13122
|
+
return dataset;
|
|
13123
|
+
}
|
|
13124
|
+
async function checkAndWarnStaleness(projectRoot) {
|
|
13125
|
+
const markerPath = getStalenessMarkerPath(projectRoot);
|
|
13126
|
+
try {
|
|
13127
|
+
const raw = await fs24.readFile(markerPath, "utf-8");
|
|
13128
|
+
const marker = JSON.parse(raw);
|
|
13129
|
+
const firstUse = new Date(marker.firstFallbackUse).getTime();
|
|
13130
|
+
const now = Date.now();
|
|
13131
|
+
const daysSinceFirstUse = (now - firstUse) / (24 * 60 * 60 * 1e3);
|
|
13132
|
+
if (daysSinceFirstUse > STALENESS_WARNING_DAYS) {
|
|
13133
|
+
console.warn(
|
|
13134
|
+
`[harness pricing] Pricing data is stale \u2014 using bundled fallback for ${Math.floor(daysSinceFirstUse)} days. Connect to the internet to refresh pricing data.`
|
|
13135
|
+
);
|
|
13136
|
+
}
|
|
13137
|
+
} catch {
|
|
13138
|
+
try {
|
|
13139
|
+
await fs24.mkdir(path23.dirname(markerPath), { recursive: true });
|
|
13140
|
+
await fs24.writeFile(
|
|
13141
|
+
markerPath,
|
|
13142
|
+
JSON.stringify({ firstFallbackUse: (/* @__PURE__ */ new Date()).toISOString() })
|
|
13143
|
+
);
|
|
13144
|
+
} catch {
|
|
13145
|
+
}
|
|
13146
|
+
}
|
|
13147
|
+
}
|
|
13148
|
+
async function clearStalenessMarker(projectRoot) {
|
|
13149
|
+
try {
|
|
13150
|
+
await fs24.unlink(getStalenessMarkerPath(projectRoot));
|
|
13151
|
+
} catch {
|
|
13152
|
+
}
|
|
13153
|
+
}
|
|
13154
|
+
async function loadPricingData(projectRoot) {
|
|
13155
|
+
const cache = await readDiskCache(projectRoot);
|
|
13156
|
+
if (cache) {
|
|
13157
|
+
const cacheAge = Date.now() - new Date(cache.fetchedAt).getTime();
|
|
13158
|
+
if (cacheAge < CACHE_TTL_MS) {
|
|
13159
|
+
await clearStalenessMarker(projectRoot);
|
|
13160
|
+
return parseLiteLLMData(cache.data);
|
|
13161
|
+
}
|
|
13162
|
+
}
|
|
13163
|
+
const fetched = await fetchFromNetwork();
|
|
13164
|
+
if (fetched) {
|
|
13165
|
+
await writeDiskCache(projectRoot, fetched);
|
|
13166
|
+
await clearStalenessMarker(projectRoot);
|
|
13167
|
+
return parseLiteLLMData(fetched.data);
|
|
13168
|
+
}
|
|
13169
|
+
if (cache) {
|
|
13170
|
+
return parseLiteLLMData(cache.data);
|
|
13171
|
+
}
|
|
13172
|
+
await checkAndWarnStaleness(projectRoot);
|
|
13173
|
+
return loadFallbackDataset();
|
|
13174
|
+
}
|
|
13175
|
+
var MICRODOLLARS_PER_DOLLAR = 1e6;
|
|
13176
|
+
var TOKENS_PER_MILLION2 = 1e6;
|
|
13177
|
+
function calculateCost(record, dataset) {
|
|
13178
|
+
if (!record.model) return null;
|
|
13179
|
+
const pricing = getModelPrice(record.model, dataset);
|
|
13180
|
+
if (!pricing) return null;
|
|
13181
|
+
let costUSD = 0;
|
|
13182
|
+
costUSD += record.tokens.inputTokens / TOKENS_PER_MILLION2 * pricing.inputPer1M;
|
|
13183
|
+
costUSD += record.tokens.outputTokens / TOKENS_PER_MILLION2 * pricing.outputPer1M;
|
|
13184
|
+
if (record.cacheReadTokens != null && pricing.cacheReadPer1M != null) {
|
|
13185
|
+
costUSD += record.cacheReadTokens / TOKENS_PER_MILLION2 * pricing.cacheReadPer1M;
|
|
13186
|
+
}
|
|
13187
|
+
if (record.cacheCreationTokens != null && pricing.cacheWritePer1M != null) {
|
|
13188
|
+
costUSD += record.cacheCreationTokens / TOKENS_PER_MILLION2 * pricing.cacheWritePer1M;
|
|
13189
|
+
}
|
|
13190
|
+
return Math.round(costUSD * MICRODOLLARS_PER_DOLLAR);
|
|
13191
|
+
}
|
|
13192
|
+
function aggregateBySession(records) {
|
|
13193
|
+
if (records.length === 0) return [];
|
|
13194
|
+
const sessionMap = /* @__PURE__ */ new Map();
|
|
13195
|
+
for (const record of records) {
|
|
13196
|
+
const tagged = record;
|
|
13197
|
+
const id = record.sessionId;
|
|
13198
|
+
if (!sessionMap.has(id)) {
|
|
13199
|
+
sessionMap.set(id, { harnessRecords: [], ccRecords: [], allRecords: [] });
|
|
13200
|
+
}
|
|
13201
|
+
const bucket = sessionMap.get(id);
|
|
13202
|
+
if (tagged._source === "claude-code") {
|
|
13203
|
+
bucket.ccRecords.push(tagged);
|
|
13204
|
+
} else {
|
|
13205
|
+
bucket.harnessRecords.push(tagged);
|
|
13206
|
+
}
|
|
13207
|
+
bucket.allRecords.push(tagged);
|
|
13208
|
+
}
|
|
13209
|
+
const results = [];
|
|
13210
|
+
for (const [sessionId, bucket] of sessionMap) {
|
|
13211
|
+
const hasHarness = bucket.harnessRecords.length > 0;
|
|
13212
|
+
const hasCC = bucket.ccRecords.length > 0;
|
|
13213
|
+
const isMerged = hasHarness && hasCC;
|
|
13214
|
+
const tokenSource = hasHarness ? bucket.harnessRecords : bucket.ccRecords;
|
|
13215
|
+
const tokens = { inputTokens: 0, outputTokens: 0, totalTokens: 0 };
|
|
13216
|
+
let cacheCreation;
|
|
13217
|
+
let cacheRead;
|
|
13218
|
+
let costMicroUSD = 0;
|
|
13219
|
+
let model;
|
|
13220
|
+
for (const r of tokenSource) {
|
|
13221
|
+
tokens.inputTokens += r.tokens.inputTokens;
|
|
13222
|
+
tokens.outputTokens += r.tokens.outputTokens;
|
|
13223
|
+
tokens.totalTokens += r.tokens.totalTokens;
|
|
13224
|
+
if (r.cacheCreationTokens != null) {
|
|
13225
|
+
cacheCreation = (cacheCreation ?? 0) + r.cacheCreationTokens;
|
|
13226
|
+
}
|
|
13227
|
+
if (r.cacheReadTokens != null) {
|
|
13228
|
+
cacheRead = (cacheRead ?? 0) + r.cacheReadTokens;
|
|
13229
|
+
}
|
|
13230
|
+
if (r.costMicroUSD != null && costMicroUSD != null) {
|
|
13231
|
+
costMicroUSD += r.costMicroUSD;
|
|
13232
|
+
} else if (r.costMicroUSD == null) {
|
|
13233
|
+
costMicroUSD = null;
|
|
13234
|
+
}
|
|
13235
|
+
if (!model && r.model) {
|
|
13236
|
+
model = r.model;
|
|
13237
|
+
}
|
|
13238
|
+
}
|
|
13239
|
+
if (!model && hasCC) {
|
|
13240
|
+
for (const r of bucket.ccRecords) {
|
|
13241
|
+
if (r.model) {
|
|
13242
|
+
model = r.model;
|
|
13243
|
+
break;
|
|
13244
|
+
}
|
|
13245
|
+
}
|
|
13246
|
+
}
|
|
13247
|
+
const timestamps = bucket.allRecords.map((r) => r.timestamp).sort();
|
|
13248
|
+
const source = isMerged ? "merged" : hasCC ? "claude-code" : "harness";
|
|
13249
|
+
const session = {
|
|
13250
|
+
sessionId,
|
|
13251
|
+
firstTimestamp: timestamps[0] ?? "",
|
|
13252
|
+
lastTimestamp: timestamps[timestamps.length - 1] ?? "",
|
|
13253
|
+
tokens,
|
|
13254
|
+
costMicroUSD,
|
|
13255
|
+
source
|
|
13256
|
+
};
|
|
13257
|
+
if (model) session.model = model;
|
|
13258
|
+
if (cacheCreation != null) session.cacheCreationTokens = cacheCreation;
|
|
13259
|
+
if (cacheRead != null) session.cacheReadTokens = cacheRead;
|
|
13260
|
+
results.push(session);
|
|
13261
|
+
}
|
|
13262
|
+
results.sort((a, b) => b.firstTimestamp.localeCompare(a.firstTimestamp));
|
|
13263
|
+
return results;
|
|
13264
|
+
}
|
|
13265
|
+
function aggregateByDay(records) {
|
|
13266
|
+
if (records.length === 0) return [];
|
|
13267
|
+
const dayMap = /* @__PURE__ */ new Map();
|
|
13268
|
+
for (const record of records) {
|
|
13269
|
+
const date = record.timestamp.slice(0, 10);
|
|
13270
|
+
if (!dayMap.has(date)) {
|
|
13271
|
+
dayMap.set(date, {
|
|
13272
|
+
sessions: /* @__PURE__ */ new Set(),
|
|
13273
|
+
tokens: { inputTokens: 0, outputTokens: 0, totalTokens: 0 },
|
|
13274
|
+
costMicroUSD: 0,
|
|
13275
|
+
models: /* @__PURE__ */ new Set()
|
|
13276
|
+
});
|
|
13277
|
+
}
|
|
13278
|
+
const day = dayMap.get(date);
|
|
13279
|
+
day.sessions.add(record.sessionId);
|
|
13280
|
+
day.tokens.inputTokens += record.tokens.inputTokens;
|
|
13281
|
+
day.tokens.outputTokens += record.tokens.outputTokens;
|
|
13282
|
+
day.tokens.totalTokens += record.tokens.totalTokens;
|
|
13283
|
+
if (record.cacheCreationTokens != null) {
|
|
13284
|
+
day.cacheCreation = (day.cacheCreation ?? 0) + record.cacheCreationTokens;
|
|
13285
|
+
}
|
|
13286
|
+
if (record.cacheReadTokens != null) {
|
|
13287
|
+
day.cacheRead = (day.cacheRead ?? 0) + record.cacheReadTokens;
|
|
13288
|
+
}
|
|
13289
|
+
if (record.costMicroUSD != null && day.costMicroUSD != null) {
|
|
13290
|
+
day.costMicroUSD += record.costMicroUSD;
|
|
13291
|
+
} else if (record.costMicroUSD == null) {
|
|
13292
|
+
day.costMicroUSD = null;
|
|
13293
|
+
}
|
|
13294
|
+
if (record.model) {
|
|
13295
|
+
day.models.add(record.model);
|
|
13296
|
+
}
|
|
13297
|
+
}
|
|
13298
|
+
const results = [];
|
|
13299
|
+
for (const [date, day] of dayMap) {
|
|
13300
|
+
const entry = {
|
|
13301
|
+
date,
|
|
13302
|
+
sessionCount: day.sessions.size,
|
|
13303
|
+
tokens: day.tokens,
|
|
13304
|
+
costMicroUSD: day.costMicroUSD,
|
|
13305
|
+
models: Array.from(day.models).sort()
|
|
13306
|
+
};
|
|
13307
|
+
if (day.cacheCreation != null) entry.cacheCreationTokens = day.cacheCreation;
|
|
13308
|
+
if (day.cacheRead != null) entry.cacheReadTokens = day.cacheRead;
|
|
13309
|
+
results.push(entry);
|
|
13310
|
+
}
|
|
13311
|
+
results.sort((a, b) => b.date.localeCompare(a.date));
|
|
13312
|
+
return results;
|
|
13313
|
+
}
|
|
13314
|
+
function parseLine(line, lineNumber) {
|
|
13315
|
+
let entry;
|
|
13316
|
+
try {
|
|
13317
|
+
entry = JSON.parse(line);
|
|
13318
|
+
} catch {
|
|
13319
|
+
console.warn(`[harness usage] Skipping malformed JSONL line ${lineNumber}`);
|
|
13320
|
+
return null;
|
|
13321
|
+
}
|
|
13322
|
+
const tokenUsage = entry.token_usage;
|
|
13323
|
+
if (!tokenUsage || typeof tokenUsage !== "object") {
|
|
13324
|
+
console.warn(
|
|
13325
|
+
`[harness usage] Skipping malformed JSONL line ${lineNumber}: missing token_usage`
|
|
13326
|
+
);
|
|
13327
|
+
return null;
|
|
13328
|
+
}
|
|
13329
|
+
const inputTokens = tokenUsage.input_tokens ?? 0;
|
|
13330
|
+
const outputTokens = tokenUsage.output_tokens ?? 0;
|
|
13331
|
+
const record = {
|
|
13332
|
+
sessionId: entry.session_id ?? "unknown",
|
|
13333
|
+
timestamp: entry.timestamp ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
13334
|
+
tokens: {
|
|
13335
|
+
inputTokens,
|
|
13336
|
+
outputTokens,
|
|
13337
|
+
totalTokens: inputTokens + outputTokens
|
|
13338
|
+
}
|
|
13339
|
+
};
|
|
13340
|
+
if (entry.cache_creation_tokens != null) {
|
|
13341
|
+
record.cacheCreationTokens = entry.cache_creation_tokens;
|
|
13342
|
+
}
|
|
13343
|
+
if (entry.cache_read_tokens != null) {
|
|
13344
|
+
record.cacheReadTokens = entry.cache_read_tokens;
|
|
13345
|
+
}
|
|
13346
|
+
if (entry.model != null) {
|
|
13347
|
+
record.model = entry.model;
|
|
13348
|
+
}
|
|
13349
|
+
return record;
|
|
13350
|
+
}
|
|
13351
|
+
function readCostRecords(projectRoot) {
|
|
13352
|
+
const costsFile = path24.join(projectRoot, ".harness", "metrics", "costs.jsonl");
|
|
13353
|
+
let raw;
|
|
13354
|
+
try {
|
|
13355
|
+
raw = fs25.readFileSync(costsFile, "utf-8");
|
|
13356
|
+
} catch {
|
|
13357
|
+
return [];
|
|
13358
|
+
}
|
|
13359
|
+
const records = [];
|
|
13360
|
+
const lines = raw.split("\n");
|
|
13361
|
+
for (let i = 0; i < lines.length; i++) {
|
|
13362
|
+
const line = lines[i]?.trim();
|
|
13363
|
+
if (!line) continue;
|
|
13364
|
+
const record = parseLine(line, i + 1);
|
|
13365
|
+
if (record) {
|
|
13366
|
+
records.push(record);
|
|
13367
|
+
}
|
|
13368
|
+
}
|
|
13369
|
+
return records;
|
|
13370
|
+
}
|
|
13371
|
+
function extractUsage(entry) {
|
|
13372
|
+
if (entry.type !== "assistant") return null;
|
|
13373
|
+
const message = entry.message;
|
|
13374
|
+
if (!message || typeof message !== "object") return null;
|
|
13375
|
+
const usage = message.usage;
|
|
13376
|
+
return usage && typeof usage === "object" && !Array.isArray(usage) ? usage : null;
|
|
13377
|
+
}
|
|
13378
|
+
function buildRecord(entry, usage) {
|
|
13379
|
+
const inputTokens = Number(usage.input_tokens) || 0;
|
|
13380
|
+
const outputTokens = Number(usage.output_tokens) || 0;
|
|
13381
|
+
const message = entry.message;
|
|
13382
|
+
const record = {
|
|
13383
|
+
sessionId: entry.sessionId ?? "unknown",
|
|
13384
|
+
timestamp: entry.timestamp ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
13385
|
+
tokens: { inputTokens, outputTokens, totalTokens: inputTokens + outputTokens },
|
|
13386
|
+
_source: "claude-code"
|
|
13387
|
+
};
|
|
13388
|
+
const model = message.model;
|
|
13389
|
+
if (model) record.model = model;
|
|
13390
|
+
const cacheCreate = usage.cache_creation_input_tokens;
|
|
13391
|
+
const cacheRead = usage.cache_read_input_tokens;
|
|
13392
|
+
if (typeof cacheCreate === "number" && cacheCreate > 0) record.cacheCreationTokens = cacheCreate;
|
|
13393
|
+
if (typeof cacheRead === "number" && cacheRead > 0) record.cacheReadTokens = cacheRead;
|
|
13394
|
+
return record;
|
|
13395
|
+
}
|
|
13396
|
+
function parseCCLine(line, filePath, lineNumber) {
|
|
13397
|
+
let entry;
|
|
13398
|
+
try {
|
|
13399
|
+
entry = JSON.parse(line);
|
|
13400
|
+
} catch {
|
|
13401
|
+
console.warn(
|
|
13402
|
+
`[harness usage] Skipping malformed CC JSONL line ${lineNumber} in ${path25.basename(filePath)}`
|
|
13403
|
+
);
|
|
13404
|
+
return null;
|
|
13405
|
+
}
|
|
13406
|
+
const usage = extractUsage(entry);
|
|
13407
|
+
if (!usage) return null;
|
|
13408
|
+
return {
|
|
13409
|
+
record: buildRecord(entry, usage),
|
|
13410
|
+
requestId: entry.requestId ?? null
|
|
13411
|
+
};
|
|
13412
|
+
}
|
|
13413
|
+
function readCCFile(filePath) {
|
|
13414
|
+
let raw;
|
|
13415
|
+
try {
|
|
13416
|
+
raw = fs26.readFileSync(filePath, "utf-8");
|
|
13417
|
+
} catch {
|
|
13418
|
+
return [];
|
|
13419
|
+
}
|
|
13420
|
+
const byRequestId = /* @__PURE__ */ new Map();
|
|
13421
|
+
const noRequestId = [];
|
|
13422
|
+
const lines = raw.split("\n");
|
|
13423
|
+
for (let i = 0; i < lines.length; i++) {
|
|
13424
|
+
const line = lines[i]?.trim();
|
|
13425
|
+
if (!line) continue;
|
|
13426
|
+
const parsed = parseCCLine(line, filePath, i + 1);
|
|
13427
|
+
if (!parsed) continue;
|
|
13428
|
+
if (parsed.requestId) {
|
|
13429
|
+
byRequestId.set(parsed.requestId, parsed.record);
|
|
13430
|
+
} else {
|
|
13431
|
+
noRequestId.push(parsed.record);
|
|
13432
|
+
}
|
|
13433
|
+
}
|
|
13434
|
+
return [...byRequestId.values(), ...noRequestId];
|
|
13435
|
+
}
|
|
13436
|
+
function parseCCRecords() {
|
|
13437
|
+
const homeDir = process.env.HOME ?? os2.homedir();
|
|
13438
|
+
const projectsDir = path25.join(homeDir, ".claude", "projects");
|
|
13439
|
+
let projectDirs;
|
|
13440
|
+
try {
|
|
13441
|
+
projectDirs = fs26.readdirSync(projectsDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => path25.join(projectsDir, d.name));
|
|
13442
|
+
} catch {
|
|
13443
|
+
return [];
|
|
13444
|
+
}
|
|
13445
|
+
const records = [];
|
|
13446
|
+
for (const dir of projectDirs) {
|
|
13447
|
+
let files;
|
|
13448
|
+
try {
|
|
13449
|
+
files = fs26.readdirSync(dir).filter((f) => f.endsWith(".jsonl")).map((f) => path25.join(dir, f));
|
|
13450
|
+
} catch {
|
|
13451
|
+
continue;
|
|
13452
|
+
}
|
|
13453
|
+
for (const file of files) {
|
|
13454
|
+
records.push(...readCCFile(file));
|
|
13455
|
+
}
|
|
13456
|
+
}
|
|
13457
|
+
return records;
|
|
13458
|
+
}
|
|
11890
13459
|
var VERSION = "0.15.0";
|
|
11891
13460
|
|
|
11892
13461
|
export {
|
|
@@ -12080,11 +13649,29 @@ export {
|
|
|
12080
13649
|
deserializationRules,
|
|
12081
13650
|
agentConfigRules,
|
|
12082
13651
|
mcpRules,
|
|
13652
|
+
insecureDefaultsRules,
|
|
13653
|
+
sharpEdgesRules,
|
|
12083
13654
|
nodeRules,
|
|
12084
13655
|
expressRules,
|
|
12085
13656
|
reactRules,
|
|
12086
13657
|
goRules,
|
|
13658
|
+
parseHarnessIgnore,
|
|
12087
13659
|
SecurityScanner,
|
|
13660
|
+
scanForInjection,
|
|
13661
|
+
getInjectionPatterns,
|
|
13662
|
+
DESTRUCTIVE_BASH,
|
|
13663
|
+
getTaintFilePath,
|
|
13664
|
+
readTaint,
|
|
13665
|
+
checkTaint,
|
|
13666
|
+
writeTaint,
|
|
13667
|
+
clearTaint,
|
|
13668
|
+
listTaintedSessions,
|
|
13669
|
+
mapSecuritySeverity,
|
|
13670
|
+
computeOverallSeverity,
|
|
13671
|
+
computeScanExitCode,
|
|
13672
|
+
mapInjectionFindings,
|
|
13673
|
+
isDuplicateFinding,
|
|
13674
|
+
mapSecurityFindings,
|
|
12088
13675
|
runCIChecks,
|
|
12089
13676
|
runMechanicalChecks,
|
|
12090
13677
|
ExclusionSet,
|
|
@@ -12118,8 +13705,17 @@ export {
|
|
|
12118
13705
|
runReviewPipeline,
|
|
12119
13706
|
parseRoadmap,
|
|
12120
13707
|
serializeRoadmap,
|
|
13708
|
+
STATUS_RANK,
|
|
13709
|
+
isRegression,
|
|
12121
13710
|
syncRoadmap,
|
|
12122
13711
|
applySyncChanges,
|
|
13712
|
+
resolveReverseStatus,
|
|
13713
|
+
GitHubIssuesSyncAdapter,
|
|
13714
|
+
syncToExternal,
|
|
13715
|
+
syncFromExternal,
|
|
13716
|
+
fullSync,
|
|
13717
|
+
scoreRoadmapCandidates,
|
|
13718
|
+
assignFeature,
|
|
12123
13719
|
InteractionTypeSchema,
|
|
12124
13720
|
QuestionSchema,
|
|
12125
13721
|
ConfirmationSchema,
|
|
@@ -12143,5 +13739,16 @@ export {
|
|
|
12143
13739
|
searchSymbols,
|
|
12144
13740
|
unfoldSymbol,
|
|
12145
13741
|
unfoldRange,
|
|
13742
|
+
parseLiteLLMData,
|
|
13743
|
+
getModelPrice,
|
|
13744
|
+
LITELLM_PRICING_URL,
|
|
13745
|
+
CACHE_TTL_MS,
|
|
13746
|
+
STALENESS_WARNING_DAYS,
|
|
13747
|
+
loadPricingData,
|
|
13748
|
+
calculateCost,
|
|
13749
|
+
aggregateBySession,
|
|
13750
|
+
aggregateByDay,
|
|
13751
|
+
readCostRecords,
|
|
13752
|
+
parseCCRecords,
|
|
12146
13753
|
VERSION
|
|
12147
13754
|
};
|