@dommaker/harness 1.3.0 → 1.5.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/CHANGELOG.md +20 -0
- package/README.md +0 -2
- package/bin/harness.js +86 -32
- package/dist/cli/command-contract.d.ts +83 -0
- package/dist/cli/command-contract.d.ts.map +1 -0
- package/dist/cli/command-contract.js +66 -0
- package/dist/cli/command-contract.js.map +1 -0
- package/dist/cli/commands/acceptance.d.ts +3 -2
- package/dist/cli/commands/acceptance.d.ts.map +1 -1
- package/dist/cli/commands/acceptance.js +28 -24
- package/dist/cli/commands/acceptance.js.map +1 -1
- package/dist/cli/commands/analyze-sessions.d.ts +2 -1
- package/dist/cli/commands/analyze-sessions.d.ts.map +1 -1
- package/dist/cli/commands/analyze-sessions.js +25 -24
- package/dist/cli/commands/analyze-sessions.js.map +1 -1
- package/dist/cli/commands/check.d.ts +14 -2
- package/dist/cli/commands/check.d.ts.map +1 -1
- package/dist/cli/commands/check.js +104 -61
- package/dist/cli/commands/check.js.map +1 -1
- package/dist/cli/commands/command.d.ts +5 -1
- package/dist/cli/commands/command.d.ts.map +1 -1
- package/dist/cli/commands/command.js +26 -17
- package/dist/cli/commands/command.js.map +1 -1
- package/dist/cli/commands/constraints-report.d.ts +2 -1
- package/dist/cli/commands/constraints-report.d.ts.map +1 -1
- package/dist/cli/commands/constraints-report.js +37 -33
- package/dist/cli/commands/constraints-report.js.map +1 -1
- package/dist/cli/commands/constraints-retire.d.ts +6 -5
- package/dist/cli/commands/constraints-retire.d.ts.map +1 -1
- package/dist/cli/commands/constraints-retire.js +50 -52
- package/dist/cli/commands/constraints-retire.js.map +1 -1
- package/dist/cli/commands/constraints.d.ts +2 -1
- package/dist/cli/commands/constraints.d.ts.map +1 -1
- package/dist/cli/commands/constraints.js +10 -27
- package/dist/cli/commands/constraints.js.map +1 -1
- package/dist/cli/commands/contract.d.ts +3 -2
- package/dist/cli/commands/contract.d.ts.map +1 -1
- package/dist/cli/commands/contract.js +45 -42
- package/dist/cli/commands/contract.js.map +1 -1
- package/dist/cli/commands/definitions.d.ts +11 -11
- package/dist/cli/commands/definitions.d.ts.map +1 -1
- package/dist/cli/commands/definitions.js +27 -219
- package/dist/cli/commands/definitions.js.map +1 -1
- package/dist/cli/commands/failure.d.ts +5 -4
- package/dist/cli/commands/failure.d.ts.map +1 -1
- package/dist/cli/commands/failure.js +31 -25
- package/dist/cli/commands/failure.js.map +1 -1
- package/dist/cli/commands/init.d.ts +7 -5
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +147 -211
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/knowledge.d.ts +17 -30
- package/dist/cli/commands/knowledge.d.ts.map +1 -1
- package/dist/cli/commands/knowledge.js +183 -273
- package/dist/cli/commands/knowledge.js.map +1 -1
- package/dist/cli/commands/passes-gate.d.ts +8 -2
- package/dist/cli/commands/passes-gate.d.ts.map +1 -1
- package/dist/cli/commands/passes-gate.js +44 -66
- package/dist/cli/commands/passes-gate.js.map +1 -1
- package/dist/cli/commands/performance.d.ts +2 -1
- package/dist/cli/commands/performance.d.ts.map +1 -1
- package/dist/cli/commands/performance.js +20 -18
- package/dist/cli/commands/performance.js.map +1 -1
- package/dist/cli/commands/posteval-plan.d.ts +5 -1
- package/dist/cli/commands/posteval-plan.d.ts.map +1 -1
- package/dist/cli/commands/posteval-plan.js +26 -15
- package/dist/cli/commands/posteval-plan.js.map +1 -1
- package/dist/cli/commands/release.d.ts +6 -1
- package/dist/cli/commands/release.d.ts.map +1 -1
- package/dist/cli/commands/release.js +69 -59
- package/dist/cli/commands/release.js.map +1 -1
- package/dist/cli/commands/report.d.ts +2 -1
- package/dist/cli/commands/report.d.ts.map +1 -1
- package/dist/cli/commands/report.js +9 -5
- package/dist/cli/commands/report.js.map +1 -1
- package/dist/cli/commands/review.d.ts +3 -2
- package/dist/cli/commands/review.d.ts.map +1 -1
- package/dist/cli/commands/review.js +33 -29
- package/dist/cli/commands/review.js.map +1 -1
- package/dist/cli/commands/sdd.d.ts +2 -1
- package/dist/cli/commands/sdd.d.ts.map +1 -1
- package/dist/cli/commands/sdd.js +7 -5
- package/dist/cli/commands/sdd.js.map +1 -1
- package/dist/cli/commands/security.d.ts +3 -2
- package/dist/cli/commands/security.d.ts.map +1 -1
- package/dist/cli/commands/security.js +29 -26
- package/dist/cli/commands/security.js.map +1 -1
- package/dist/cli/commands/spec-baseline-check.d.ts +2 -1
- package/dist/cli/commands/spec-baseline-check.d.ts.map +1 -1
- package/dist/cli/commands/spec-baseline-check.js +15 -11
- package/dist/cli/commands/spec-baseline-check.js.map +1 -1
- package/dist/cli/commands/spec.d.ts +7 -2
- package/dist/cli/commands/spec.d.ts.map +1 -1
- package/dist/cli/commands/spec.js +61 -51
- package/dist/cli/commands/spec.js.map +1 -1
- package/dist/cli/commands/status.d.ts +2 -1
- package/dist/cli/commands/status.d.ts.map +1 -1
- package/dist/cli/commands/status.js +57 -53
- package/dist/cli/commands/status.js.map +1 -1
- package/dist/cli/commands/sync-docs/agents-syncer.d.ts.map +1 -1
- package/dist/cli/commands/sync-docs/agents-syncer.js +5 -2
- package/dist/cli/commands/sync-docs/agents-syncer.js.map +1 -1
- package/dist/cli/commands/sync-docs/context-syncer.d.ts.map +1 -1
- package/dist/cli/commands/sync-docs/context-syncer.js +3 -2
- package/dist/cli/commands/sync-docs/context-syncer.js.map +1 -1
- package/dist/cli/commands/sync-docs/index.d.ts +2 -1
- package/dist/cli/commands/sync-docs/index.d.ts.map +1 -1
- package/dist/cli/commands/sync-docs/index.js +68 -88
- package/dist/cli/commands/sync-docs/index.js.map +1 -1
- package/dist/cli/commands/sync-docs/project-reader.d.ts +6 -1
- package/dist/cli/commands/sync-docs/project-reader.d.ts.map +1 -1
- package/dist/cli/commands/sync-docs/project-reader.js +14 -16
- package/dist/cli/commands/sync-docs/project-reader.js.map +1 -1
- package/dist/cli/commands/update-user-model.d.ts +9 -3
- package/dist/cli/commands/update-user-model.d.ts.map +1 -1
- package/dist/cli/commands/update-user-model.js +56 -61
- package/dist/cli/commands/update-user-model.js.map +1 -1
- package/dist/cli/commands/validate.d.ts +4 -3
- package/dist/cli/commands/validate.d.ts.map +1 -1
- package/dist/cli/commands/validate.js +28 -26
- package/dist/cli/commands/validate.js.map +1 -1
- package/dist/cli/session-mining/transcript.d.ts +7 -1
- package/dist/cli/session-mining/transcript.d.ts.map +1 -1
- package/dist/cli/session-mining/transcript.js +21 -14
- package/dist/cli/session-mining/transcript.js.map +1 -1
- package/dist/context/session-manager.d.ts.map +1 -1
- package/dist/context/session-manager.js +19 -32
- package/dist/context/session-manager.js.map +1 -1
- package/dist/core/constraints/agent-prompt-renderer.d.ts.map +1 -1
- package/dist/core/constraints/agent-prompt-renderer.js +1 -4
- package/dist/core/constraints/agent-prompt-renderer.js.map +1 -1
- package/dist/core/constraints/capabilities-parser.d.ts +24 -7
- package/dist/core/constraints/capabilities-parser.d.ts.map +1 -1
- package/dist/core/constraints/capabilities-parser.js +32 -28
- package/dist/core/constraints/capabilities-parser.js.map +1 -1
- package/dist/core/constraints/capabilities-reconcile.d.ts +67 -0
- package/dist/core/constraints/capabilities-reconcile.d.ts.map +1 -0
- package/dist/core/constraints/capabilities-reconcile.js +105 -0
- package/dist/core/constraints/capabilities-reconcile.js.map +1 -0
- package/dist/core/constraints/checker.d.ts +21 -43
- package/dist/core/constraints/checker.d.ts.map +1 -1
- package/dist/core/constraints/checker.js +68 -117
- package/dist/core/constraints/checker.js.map +1 -1
- package/dist/core/constraints/checkers/capability-sync.d.ts +20 -8
- package/dist/core/constraints/checkers/capability-sync.d.ts.map +1 -1
- package/dist/core/constraints/checkers/capability-sync.js +66 -77
- package/dist/core/constraints/checkers/capability-sync.js.map +1 -1
- package/dist/core/constraints/checkers/context-doc-sync.d.ts +4 -2
- package/dist/core/constraints/checkers/context-doc-sync.d.ts.map +1 -1
- package/dist/core/constraints/checkers/context-doc-sync.js +13 -22
- package/dist/core/constraints/checkers/context-doc-sync.js.map +1 -1
- package/dist/core/constraints/checkers/docs-freshness.d.ts.map +1 -1
- package/dist/core/constraints/checkers/docs-freshness.js +49 -66
- package/dist/core/constraints/checkers/docs-freshness.js.map +1 -1
- package/dist/core/constraints/checkers/governance-presence.d.ts +2 -2
- package/dist/core/constraints/checkers/governance-presence.d.ts.map +1 -1
- package/dist/core/constraints/checkers/governance-presence.js +7 -28
- package/dist/core/constraints/checkers/governance-presence.js.map +1 -1
- package/dist/core/constraints/checkers/index.d.ts +2 -2
- package/dist/core/constraints/checkers/index.d.ts.map +1 -1
- package/dist/core/constraints/checkers/index.js +2 -1
- package/dist/core/constraints/checkers/index.js.map +1 -1
- package/dist/core/constraints/checkers/no-test-simplification.d.ts.map +1 -1
- package/dist/core/constraints/checkers/no-test-simplification.js +4 -3
- package/dist/core/constraints/checkers/no-test-simplification.js.map +1 -1
- package/dist/core/constraints/checkers/types.d.ts +43 -4
- package/dist/core/constraints/checkers/types.d.ts.map +1 -1
- package/dist/core/constraints/checkers/types.js +33 -0
- package/dist/core/constraints/checkers/types.js.map +1 -1
- package/dist/core/constraints/context-builder.d.ts +15 -14
- package/dist/core/constraints/context-builder.d.ts.map +1 -1
- package/dist/core/constraints/context-builder.js +32 -96
- package/dist/core/constraints/context-builder.js.map +1 -1
- package/dist/core/constraints/doc-freshness/runner.d.ts.map +1 -1
- package/dist/core/constraints/doc-freshness/runner.js +2 -1
- package/dist/core/constraints/doc-freshness/runner.js.map +1 -1
- package/dist/core/constraints/git-evidence.d.ts +60 -0
- package/dist/core/constraints/git-evidence.d.ts.map +1 -0
- package/dist/core/constraints/git-evidence.js +88 -0
- package/dist/core/constraints/git-evidence.js.map +1 -0
- package/dist/core/constraints/injection-drift.d.ts +3 -20
- package/dist/core/constraints/injection-drift.d.ts.map +1 -1
- package/dist/core/constraints/injection-drift.js +10 -57
- package/dist/core/constraints/injection-drift.js.map +1 -1
- package/dist/core/constraints/injection-writer.d.ts +113 -0
- package/dist/core/constraints/injection-writer.d.ts.map +1 -0
- package/dist/core/constraints/injection-writer.js +228 -0
- package/dist/core/constraints/injection-writer.js.map +1 -0
- package/dist/core/constraints/usage-report.d.ts +22 -1
- package/dist/core/constraints/usage-report.d.ts.map +1 -1
- package/dist/core/constraints/usage-report.js +27 -20
- package/dist/core/constraints/usage-report.js.map +1 -1
- package/dist/core/index.d.ts +2 -2
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +4 -2
- package/dist/core/index.js.map +1 -1
- package/dist/core/project-config-loader.d.ts +32 -1
- package/dist/core/project-config-loader.d.ts.map +1 -1
- package/dist/core/project-config-loader.js +39 -9
- package/dist/core/project-config-loader.js.map +1 -1
- package/dist/core/validators/passes-gate.d.ts +15 -64
- package/dist/core/validators/passes-gate.d.ts.map +1 -1
- package/dist/core/validators/passes-gate.js +84 -222
- package/dist/core/validators/passes-gate.js.map +1 -1
- package/dist/core/validators/test-output.d.ts +58 -0
- package/dist/core/validators/test-output.d.ts.map +1 -0
- package/dist/core/validators/test-output.js +109 -0
- package/dist/core/validators/test-output.js.map +1 -0
- package/dist/failure/recorder.d.ts +6 -0
- package/dist/failure/recorder.d.ts.map +1 -1
- package/dist/failure/recorder.js +12 -8
- package/dist/failure/recorder.js.map +1 -1
- package/dist/gates/acceptance.d.ts +1 -48
- package/dist/gates/acceptance.d.ts.map +1 -1
- package/dist/gates/acceptance.js +6 -38
- package/dist/gates/acceptance.js.map +1 -1
- package/dist/gates/checker-gate.d.ts +2 -1
- package/dist/gates/checker-gate.d.ts.map +1 -1
- package/dist/gates/checker-gate.js +11 -13
- package/dist/gates/checker-gate.js.map +1 -1
- package/dist/gates/command.d.ts.map +1 -1
- package/dist/gates/command.js +10 -22
- package/dist/gates/command.js.map +1 -1
- package/dist/gates/contract.d.ts.map +1 -1
- package/dist/gates/contract.js +22 -61
- package/dist/gates/contract.js.map +1 -1
- package/dist/gates/performance.d.ts.map +1 -1
- package/dist/gates/performance.js +13 -31
- package/dist/gates/performance.js.map +1 -1
- package/dist/gates/review.d.ts.map +1 -1
- package/dist/gates/review.js +21 -53
- package/dist/gates/review.js.map +1 -1
- package/dist/gates/security.d.ts.map +1 -1
- package/dist/gates/security.js +14 -32
- package/dist/gates/security.js.map +1 -1
- package/dist/gates/types.d.ts +28 -5
- package/dist/gates/types.d.ts.map +1 -1
- package/dist/gates/types.js +38 -1
- package/dist/gates/types.js.map +1 -1
- package/dist/hooks/bootstrap.d.ts +2 -0
- package/dist/hooks/bootstrap.d.ts.map +1 -1
- package/dist/hooks/bootstrap.js +5 -2
- package/dist/hooks/bootstrap.js.map +1 -1
- package/dist/index.d.ts +1 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/knowledge/audit.d.ts +7 -2
- package/dist/knowledge/audit.d.ts.map +1 -1
- package/dist/knowledge/audit.js +59 -79
- package/dist/knowledge/audit.js.map +1 -1
- package/dist/knowledge/flywheel-metrics.d.ts +39 -0
- package/dist/knowledge/flywheel-metrics.d.ts.map +1 -0
- package/dist/knowledge/flywheel-metrics.js +47 -0
- package/dist/knowledge/flywheel-metrics.js.map +1 -0
- package/dist/knowledge/import.d.ts.map +1 -1
- package/dist/knowledge/import.js +4 -14
- package/dist/knowledge/import.js.map +1 -1
- package/dist/knowledge/index-generator.d.ts.map +1 -1
- package/dist/knowledge/index-generator.js +17 -14
- package/dist/knowledge/index-generator.js.map +1 -1
- package/dist/knowledge/ingest.d.ts.map +1 -1
- package/dist/knowledge/ingest.js +9 -4
- package/dist/knowledge/ingest.js.map +1 -1
- package/dist/knowledge/migration.d.ts.map +1 -1
- package/dist/knowledge/migration.js +11 -8
- package/dist/knowledge/migration.js.map +1 -1
- package/dist/knowledge/reference-tracker.d.ts.map +1 -1
- package/dist/knowledge/reference-tracker.js +12 -9
- package/dist/knowledge/reference-tracker.js.map +1 -1
- package/dist/knowledge/store.d.ts +15 -0
- package/dist/knowledge/store.d.ts.map +1 -1
- package/dist/knowledge/store.js +59 -10
- package/dist/knowledge/store.js.map +1 -1
- package/dist/monitoring/context-tracker.d.ts.map +1 -1
- package/dist/monitoring/context-tracker.js +11 -20
- package/dist/monitoring/context-tracker.js.map +1 -1
- package/dist/monitoring/index.d.ts +1 -1
- package/dist/monitoring/index.d.ts.map +1 -1
- package/dist/monitoring/index.js +1 -2
- package/dist/monitoring/index.js.map +1 -1
- package/dist/monitoring/trace-analyzer.d.ts +15 -0
- package/dist/monitoring/trace-analyzer.d.ts.map +1 -1
- package/dist/monitoring/trace-analyzer.js +19 -2
- package/dist/monitoring/trace-analyzer.js.map +1 -1
- package/dist/monitoring/traces.d.ts +21 -2
- package/dist/monitoring/traces.d.ts.map +1 -1
- package/dist/monitoring/traces.js +38 -29
- package/dist/monitoring/traces.js.map +1 -1
- package/dist/release/index.d.ts +2 -2
- package/dist/release/index.d.ts.map +1 -1
- package/dist/release/index.js +1 -5
- package/dist/release/index.js.map +1 -1
- package/dist/sdd/index-generator.js +10 -11
- package/dist/sdd/index-generator.js.map +1 -1
- package/dist/test-setup/project-fixture.d.ts +48 -0
- package/dist/test-setup/project-fixture.d.ts.map +1 -0
- package/dist/test-setup/project-fixture.js +106 -0
- package/dist/test-setup/project-fixture.js.map +1 -0
- package/dist/types/constraint.d.ts +8 -18
- package/dist/types/constraint.d.ts.map +1 -1
- package/dist/types/constraint.js +6 -1
- package/dist/types/constraint.js.map +1 -1
- package/dist/types/passes-gate.d.ts +10 -25
- package/dist/types/passes-gate.d.ts.map +1 -1
- package/dist/types/project-config.d.ts +1 -1
- package/dist/types/session.d.ts +6 -5
- package/dist/types/session.d.ts.map +1 -1
- package/dist/types/trace.d.ts +9 -0
- package/dist/types/trace.d.ts.map +1 -1
- package/dist/utils/exec.d.ts +10 -0
- package/dist/utils/exec.d.ts.map +1 -1
- package/dist/utils/exec.js +12 -0
- package/dist/utils/exec.js.map +1 -1
- package/dist/utils/file-walk.d.ts +9 -1
- package/dist/utils/file-walk.d.ts.map +1 -1
- package/dist/utils/file-walk.js +10 -1
- package/dist/utils/file-walk.js.map +1 -1
- package/dist/utils/frontmatter.d.ts +73 -0
- package/dist/utils/frontmatter.d.ts.map +1 -0
- package/dist/utils/frontmatter.js +141 -0
- package/dist/utils/frontmatter.js.map +1 -0
- package/dist/utils/jsonl.d.ts +92 -0
- package/dist/utils/jsonl.d.ts.map +1 -0
- package/dist/utils/jsonl.js +166 -0
- package/dist/utils/jsonl.js.map +1 -0
- package/dist/utils/package-version.d.ts +16 -0
- package/dist/utils/package-version.d.ts.map +1 -0
- package/dist/utils/package-version.js +63 -0
- package/dist/utils/package-version.js.map +1 -0
- package/package.json +1 -1
- package/src/CONTEXT.md +8 -2
- package/src/__tests__/acceptance-gate.test.ts +3 -1
- package/src/__tests__/checker-extra.test.ts +7 -1218
- package/src/__tests__/context-files-resolution.test.ts +116 -0
- package/src/__tests__/effective-constraints.test.ts +10 -13
- package/src/__tests__/exec.test.ts +35 -1
- package/src/__tests__/layering.test.ts +152 -0
- package/src/__tests__/passes-gate-extra.test.ts +0 -145
- package/src/__tests__/passes-gate.test.ts +130 -101
- package/src/__tests__/skip-semantics.test.ts +5 -9
- package/src/cli/__tests__/command-contract.test.ts +170 -0
- package/src/cli/command-contract.ts +108 -0
- package/src/cli/commands/CONTEXT.md +23 -8
- package/src/cli/commands/__tests__/acceptance.test.ts +40 -33
- package/src/cli/commands/__tests__/analyze-sessions.test.ts +35 -29
- package/src/cli/commands/__tests__/bin-exit-mapping.test.ts +107 -0
- package/src/cli/commands/__tests__/check-drift.test.ts +27 -34
- package/src/cli/commands/__tests__/check-skip-output.test.ts +21 -24
- package/src/cli/commands/__tests__/check.test.ts +448 -378
- package/src/cli/commands/__tests__/command.test.ts +85 -70
- package/src/cli/commands/__tests__/constraints-report.test.ts +100 -71
- package/src/cli/commands/__tests__/constraints-retire.test.ts +152 -95
- package/src/cli/commands/__tests__/contract.test.ts +27 -34
- package/src/cli/commands/__tests__/failure.test.ts +31 -34
- package/src/cli/commands/__tests__/init-injection.test.ts +58 -28
- package/src/cli/commands/__tests__/init.test.ts +77 -48
- package/src/cli/commands/__tests__/knowledge-flywheel-consistency.test.ts +108 -0
- package/src/cli/commands/__tests__/knowledge.test.ts +69 -43
- package/src/cli/commands/__tests__/passes-gate.test.ts +43 -45
- package/src/cli/commands/__tests__/performance.test.ts +22 -23
- package/src/cli/commands/__tests__/posteval-plan.test.ts +39 -38
- package/src/cli/commands/__tests__/project-path-anchoring.test.ts +157 -0
- package/src/cli/commands/__tests__/project-path-convention.test.ts +231 -0
- package/src/cli/commands/__tests__/registry.test.ts +75 -35
- package/src/cli/commands/__tests__/release.test.ts +209 -80
- package/src/cli/commands/__tests__/report-extra.test.ts +16 -16
- package/src/cli/commands/__tests__/report.test.ts +25 -25
- package/src/cli/commands/__tests__/review.test.ts +33 -31
- package/src/cli/commands/__tests__/security.test.ts +23 -27
- package/src/cli/commands/__tests__/spec-baseline-check.test.ts +20 -20
- package/src/cli/commands/__tests__/spec.test.ts +41 -29
- package/src/cli/commands/__tests__/status-extra.test.ts +18 -18
- package/src/cli/commands/__tests__/status-skipped-lines.test.ts +72 -0
- package/src/cli/commands/__tests__/status.test.ts +34 -34
- package/src/cli/commands/__tests__/sync-docs-agents.test.ts +77 -78
- package/src/cli/commands/__tests__/sync-docs.test.ts +72 -74
- package/src/cli/commands/__tests__/update-user-model.test.ts +97 -34
- package/src/cli/commands/__tests__/validate.test.ts +27 -29
- package/src/cli/commands/acceptance.ts +35 -24
- package/src/cli/commands/analyze-sessions.ts +25 -24
- package/src/cli/commands/check.ts +127 -66
- package/src/cli/commands/command.ts +30 -17
- package/src/cli/commands/constraints-report.ts +37 -34
- package/src/cli/commands/constraints-retire.ts +58 -55
- package/src/cli/commands/constraints.ts +10 -27
- package/src/cli/commands/contract.ts +51 -42
- package/src/cli/commands/definitions.ts +34 -227
- package/src/cli/commands/failure.ts +33 -26
- package/src/cli/commands/init.ts +166 -220
- package/src/cli/commands/knowledge.ts +192 -289
- package/src/cli/commands/passes-gate.ts +58 -68
- package/src/cli/commands/performance.ts +23 -18
- package/src/cli/commands/posteval-plan.ts +29 -15
- package/src/cli/commands/release.ts +70 -59
- package/src/cli/commands/report.ts +9 -5
- package/src/cli/commands/review.ts +39 -29
- package/src/cli/commands/sdd.ts +7 -5
- package/src/cli/commands/security.ts +35 -26
- package/src/cli/commands/spec-baseline-check.ts +16 -11
- package/src/cli/commands/spec.ts +66 -53
- package/src/cli/commands/status.ts +59 -53
- package/src/cli/commands/sync-docs/agents-syncer.ts +5 -2
- package/src/cli/commands/sync-docs/context-syncer.ts +3 -2
- package/src/cli/commands/sync-docs/index.ts +73 -100
- package/src/cli/commands/sync-docs/project-reader.ts +17 -18
- package/src/cli/commands/update-user-model.ts +64 -61
- package/src/cli/commands/validate.ts +31 -25
- package/src/cli/session-mining/__tests__/transcript.test.ts +118 -0
- package/src/cli/session-mining/transcript.ts +26 -8
- package/src/context/session-manager.ts +19 -33
- package/src/core/CONTEXT.md +10 -6
- package/src/core/constraints/__tests__/capabilities-reconcile.test.ts +200 -0
- package/src/core/constraints/__tests__/capability-counts.test.ts +94 -0
- package/src/core/constraints/__tests__/git-evidence.test.ts +208 -0
- package/src/core/constraints/__tests__/injection-drift.test.ts +3 -10
- package/src/core/constraints/__tests__/injection-writer.test.ts +231 -0
- package/src/core/constraints/__tests__/trace-injection.test.ts +71 -0
- package/src/core/constraints/__tests__/usage-report.test.ts +112 -0
- package/src/core/constraints/agent-prompt-renderer.ts +2 -6
- package/src/core/constraints/capabilities-parser.ts +53 -35
- package/src/core/constraints/capabilities-reconcile.ts +166 -0
- package/src/core/constraints/checker.ts +82 -133
- package/src/core/constraints/checkers/__tests__/capability-sync.test.ts +273 -0
- package/src/core/constraints/checkers/__tests__/check-outcome.test.ts +176 -0
- package/src/core/constraints/checkers/__tests__/context-doc-sync.test.ts +77 -0
- package/src/core/constraints/checkers/__tests__/docs-freshness.test.ts +126 -0
- package/src/{__tests__ → core/constraints/checkers/__tests__}/governance-presence.test.ts +83 -9
- package/src/core/constraints/checkers/__tests__/iron-flags.test.ts +50 -0
- package/src/core/constraints/checkers/__tests__/no-bypass-checkpoint.test.ts +76 -0
- package/src/{__tests__ → core/constraints/checkers/__tests__}/no-hardcoded-credentials.test.ts +4 -3
- package/src/core/constraints/checkers/__tests__/no-test-simplification.test.ts +58 -0
- package/src/core/constraints/checkers/__tests__/three-state-consistency.test.ts +153 -0
- package/src/core/constraints/checkers/capability-sync.ts +75 -96
- package/src/core/constraints/checkers/context-doc-sync.ts +15 -25
- package/src/core/constraints/checkers/docs-freshness.ts +54 -70
- package/src/core/constraints/checkers/governance-presence.ts +7 -25
- package/src/core/constraints/checkers/index.ts +9 -2
- package/src/core/constraints/checkers/no-test-simplification.ts +4 -3
- package/src/core/constraints/checkers/types.ts +60 -4
- package/src/core/constraints/context-builder.ts +39 -91
- package/src/core/constraints/doc-freshness/runner.ts +2 -1
- package/src/core/constraints/git-evidence.ts +120 -0
- package/src/core/constraints/injection-drift.ts +13 -60
- package/src/core/constraints/injection-writer.ts +212 -0
- package/src/core/constraints/usage-report.ts +36 -17
- package/src/core/index.ts +4 -2
- package/src/core/project-config-loader.ts +49 -9
- package/src/core/validators/__tests__/passes-gate.test.ts +2 -56
- package/src/core/validators/__tests__/test-output.test.ts +372 -0
- package/src/core/validators/passes-gate.ts +86 -248
- package/src/core/validators/test-output.ts +128 -0
- package/src/failure/__tests__/recorder.test.ts +168 -0
- package/src/failure/recorder.ts +12 -10
- package/src/gates/CONTEXT.md +7 -4
- package/src/gates/__tests__/acceptance-types-single-source.test.ts +68 -0
- package/src/gates/__tests__/acceptance.test.ts +4 -86
- package/src/gates/__tests__/checker-gate.test.ts +24 -0
- package/src/gates/__tests__/effective-gates.test.ts +3 -8
- package/src/gates/__tests__/test-verdict-consistency.test.ts +140 -0
- package/src/gates/acceptance.ts +19 -84
- package/src/gates/checker-gate.ts +12 -15
- package/src/gates/command.ts +10 -16
- package/src/gates/contract.ts +22 -61
- package/src/gates/performance.ts +10 -22
- package/src/gates/review.ts +21 -41
- package/src/gates/security.ts +10 -22
- package/src/gates/types.ts +70 -5
- package/src/hooks/CONTEXT.md +1 -1
- package/src/hooks/bootstrap.ts +5 -2
- package/src/index.ts +0 -4
- package/src/knowledge/CONTEXT.md +8 -3
- package/src/knowledge/__tests__/audit-labels.test.ts +44 -0
- package/src/knowledge/__tests__/flywheel-metrics.test.ts +135 -0
- package/src/knowledge/__tests__/import.test.ts +44 -0
- package/src/knowledge/__tests__/index-generator.test.ts +53 -0
- package/src/knowledge/__tests__/ingest.test.ts +85 -0
- package/src/knowledge/__tests__/migration.test.ts +47 -0
- package/src/knowledge/__tests__/reference-tracker.test.ts +16 -0
- package/src/knowledge/__tests__/store.test.ts +194 -0
- package/src/knowledge/audit.ts +67 -71
- package/src/knowledge/flywheel-metrics.ts +71 -0
- package/src/knowledge/import.ts +4 -18
- package/src/knowledge/index-generator.ts +19 -14
- package/src/knowledge/ingest.ts +9 -4
- package/src/knowledge/migration.ts +11 -8
- package/src/knowledge/reference-tracker.ts +12 -8
- package/src/knowledge/store.ts +64 -11
- package/src/monitoring/CONTEXT.md +8 -3
- package/src/monitoring/__tests__/skipped-lines-report.test.ts +178 -0
- package/src/monitoring/__tests__/traces.test.ts +57 -0
- package/src/monitoring/context-tracker.ts +11 -23
- package/src/monitoring/index.ts +1 -1
- package/src/monitoring/trace-analyzer.ts +21 -2
- package/src/monitoring/traces.ts +39 -36
- package/src/release/CONTEXT.md +1 -0
- package/src/release/__tests__/barrel.test.ts +60 -0
- package/src/release/index.ts +2 -13
- package/src/sdd/__tests__/index-generator.test.ts +71 -0
- package/src/sdd/index-generator.ts +9 -9
- package/src/test-setup/__tests__/project-fixture.test.ts +178 -0
- package/src/test-setup/project-fixture.ts +87 -0
- package/src/tools/CONTEXT.md +6 -0
- package/src/tools/__tests__/registry.test.ts +221 -0
- package/src/types/__tests__/types.test.ts +4 -5
- package/src/types/constraint.ts +16 -27
- package/src/types/passes-gate.ts +10 -30
- package/src/types/project-config.ts +1 -1
- package/src/types/session.ts +5 -5
- package/src/types/trace.ts +10 -0
- package/src/utils/__tests__/frontmatter.test.ts +206 -0
- package/src/utils/__tests__/jsonl-skip-disposition.test.ts +175 -0
- package/src/utils/__tests__/jsonl.test.ts +187 -0
- package/src/utils/__tests__/package-version.test.ts +97 -0
- package/src/utils/exec.ts +15 -0
- package/src/utils/file-walk.ts +11 -2
- package/src/utils/frontmatter.ts +121 -0
- package/src/utils/jsonl.ts +167 -0
- package/src/utils/package-version.ts +27 -0
- package/dist/cli/commands/doc-freshness-check.d.ts +0 -53
- package/dist/cli/commands/doc-freshness-check.d.ts.map +0 -1
- package/dist/cli/commands/doc-freshness-check.js +0 -352
- package/dist/cli/commands/doc-freshness-check.js.map +0 -1
- package/dist/types/index.d.ts +0 -14
- package/dist/types/index.d.ts.map +0 -1
- package/dist/types/index.js +0 -39
- package/dist/types/index.js.map +0 -1
- package/src/__tests__/long-running.test.ts +0 -17
- package/src/__tests__/passes-gate-extension.test.ts +0 -151
- package/src/cli/commands/__tests__/doc-freshness-check.test.ts +0 -168
- package/src/cli/commands/doc-freshness-check.ts +0 -386
- package/src/spec/CONTEXT.md +0 -11
- package/src/types/index.ts +0 -47
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import { report } from '../report';
|
|
6
|
+
import { captureIO, type CapturingIO } from '../../command-contract';
|
|
6
7
|
import * as fs from 'fs/promises';
|
|
7
8
|
|
|
8
9
|
// Mock fs/promises
|
|
@@ -19,60 +20,59 @@ jest.mock('chalk', () => ({
|
|
|
19
20
|
|
|
20
21
|
const mockFs = fs as jest.Mocked<typeof fs>;
|
|
21
22
|
|
|
23
|
+
let io: CapturingIO;
|
|
24
|
+
beforeEach(() => {
|
|
25
|
+
io = captureIO();
|
|
26
|
+
});
|
|
27
|
+
|
|
22
28
|
describe('report command', () => {
|
|
23
|
-
let consoleSpy: jest.SpyInstance;
|
|
24
29
|
|
|
25
30
|
beforeEach(() => {
|
|
26
31
|
jest.clearAllMocks();
|
|
27
|
-
consoleSpy = jest.spyOn(console, 'log').mockImplementation();
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
afterEach(() => {
|
|
31
|
-
consoleSpy.mockRestore();
|
|
32
32
|
});
|
|
33
33
|
|
|
34
34
|
describe('report', () => {
|
|
35
35
|
it('应该生成 JSON 格式报告', async () => {
|
|
36
|
-
await report({ format: 'json' });
|
|
37
|
-
expect(
|
|
38
|
-
const output =
|
|
36
|
+
await report({ format: 'json' }, io);
|
|
37
|
+
expect(io.outText()).not.toBe('');
|
|
38
|
+
const output = io.outText();
|
|
39
39
|
expect(output).toContain('timestamp');
|
|
40
40
|
});
|
|
41
41
|
|
|
42
42
|
it('应该生成 Markdown 格式报告', async () => {
|
|
43
|
-
await report({ format: 'markdown' });
|
|
44
|
-
expect(
|
|
43
|
+
await report({ format: 'markdown' }, io);
|
|
44
|
+
expect(io.outText()).toContain('Harness 检查报告');
|
|
45
45
|
});
|
|
46
46
|
|
|
47
47
|
it('应该生成 HTML 格式报告', async () => {
|
|
48
|
-
await report({ format: 'html' });
|
|
49
|
-
expect(
|
|
48
|
+
await report({ format: 'html' }, io);
|
|
49
|
+
expect(io.outText()).toContain('<!DOCTYPE html>');
|
|
50
50
|
});
|
|
51
51
|
|
|
52
52
|
it('应该保存报告到文件', async () => {
|
|
53
53
|
mockFs.writeFile.mockResolvedValue(undefined);
|
|
54
54
|
|
|
55
|
-
await report({ format: 'json', output: 'report.json' });
|
|
55
|
+
await report({ format: 'json', output: 'report.json' }, io);
|
|
56
56
|
expect(mockFs.writeFile).toHaveBeenCalledWith('report.json', expect.any(String), 'utf-8');
|
|
57
|
-
expect(
|
|
57
|
+
expect(io.outText()).toContain('报告已保存');
|
|
58
58
|
});
|
|
59
59
|
|
|
60
60
|
it('应该使用自定义项目路径', async () => {
|
|
61
|
-
await report({ format: 'json', projectPath: '/custom/path' });
|
|
62
|
-
expect(
|
|
61
|
+
await report({ format: 'json', projectPath: '/custom/path' }, io);
|
|
62
|
+
expect(io.outText()).not.toBe('');
|
|
63
63
|
});
|
|
64
64
|
|
|
65
65
|
it('Markdown 报告应包含表格结构', async () => {
|
|
66
|
-
await report({ format: 'markdown' });
|
|
67
|
-
const output =
|
|
66
|
+
await report({ format: 'markdown' }, io);
|
|
67
|
+
const output = io.outText();
|
|
68
68
|
expect(output).toContain('| 指标 | 数值 |');
|
|
69
69
|
expect(output).toContain('| 总约束 |');
|
|
70
70
|
expect(output).toContain('报告由 @dommaker/harness 生成');
|
|
71
71
|
});
|
|
72
72
|
|
|
73
73
|
it('HTML 报告应包含完整结构', async () => {
|
|
74
|
-
await report({ format: 'html' });
|
|
75
|
-
const output =
|
|
74
|
+
await report({ format: 'html' }, io);
|
|
75
|
+
const output = io.outText();
|
|
76
76
|
expect(output).toContain('<html lang="zh-CN">');
|
|
77
77
|
expect(output).toContain('</html>');
|
|
78
78
|
expect(output).toContain('Harness 检查报告');
|
|
@@ -80,8 +80,8 @@ describe('report command', () => {
|
|
|
80
80
|
});
|
|
81
81
|
|
|
82
82
|
it('JSON 报告应包含约束统计', async () => {
|
|
83
|
-
await report({ format: 'json' });
|
|
84
|
-
const output =
|
|
83
|
+
await report({ format: 'json' }, io);
|
|
84
|
+
const output = io.outText();
|
|
85
85
|
// 过滤掉 chalk 的 emoji 行,只取 JSON 部分
|
|
86
86
|
const jsonStart = output.indexOf('{');
|
|
87
87
|
const jsonEnd = output.lastIndexOf('}');
|
|
@@ -96,13 +96,13 @@ describe('report command', () => {
|
|
|
96
96
|
|
|
97
97
|
it('应该支持输出到文件 (markdown)', async () => {
|
|
98
98
|
mockFs.writeFile.mockResolvedValue(undefined);
|
|
99
|
-
await report({ format: 'markdown', output: 'report.md' });
|
|
99
|
+
await report({ format: 'markdown', output: 'report.md' }, io);
|
|
100
100
|
expect(mockFs.writeFile).toHaveBeenCalledWith('report.md', expect.stringContaining('# Harness'), 'utf-8');
|
|
101
101
|
});
|
|
102
102
|
|
|
103
103
|
it('应该支持输出到文件 (html)', async () => {
|
|
104
104
|
mockFs.writeFile.mockResolvedValue(undefined);
|
|
105
|
-
await report({ format: 'html', output: 'report.html' });
|
|
105
|
+
await report({ format: 'html', output: 'report.html' }, io);
|
|
106
106
|
expect(mockFs.writeFile).toHaveBeenCalledWith('report.html', expect.stringContaining('<!DOCTYPE html>'), 'utf-8');
|
|
107
107
|
});
|
|
108
108
|
});
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import { review, reviewStatus } from '../review';
|
|
6
|
+
import { captureIO, type CapturingIO } from '../../command-contract';
|
|
6
7
|
import { execAsync } from '../../../utils/exec';
|
|
7
8
|
import { ReviewGate } from '../../../gates/review';
|
|
8
9
|
|
|
@@ -29,20 +30,12 @@ const mockExec = execAsync as jest.MockedFunction<typeof execAsync>;
|
|
|
29
30
|
const MockGate = ReviewGate as jest.MockedClass<typeof ReviewGate>;
|
|
30
31
|
|
|
31
32
|
describe('review command', () => {
|
|
32
|
-
let
|
|
33
|
-
let exitSpy: jest.SpyInstance;
|
|
33
|
+
let io: CapturingIO;
|
|
34
34
|
|
|
35
35
|
beforeEach(() => {
|
|
36
|
-
jest.clearAllMocks();
|
|
37
|
-
consoleSpy = jest.spyOn(console, 'log').mockImplementation();
|
|
38
|
-
exitSpy = jest.spyOn(process, 'exit').mockImplementation(() => undefined as never);
|
|
39
|
-
process.exitCode = 0;
|
|
40
|
-
});
|
|
41
36
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
exitSpy.mockRestore();
|
|
45
|
-
process.exitCode = 0;
|
|
37
|
+
io = captureIO();
|
|
38
|
+
jest.clearAllMocks();
|
|
46
39
|
});
|
|
47
40
|
|
|
48
41
|
describe('review', () => {
|
|
@@ -55,10 +48,10 @@ describe('review command', () => {
|
|
|
55
48
|
});
|
|
56
49
|
MockGate.mockImplementation(() => ({ check: mockCheck }) as any);
|
|
57
50
|
|
|
58
|
-
await review({});
|
|
51
|
+
const result = await review({}, io);
|
|
59
52
|
|
|
60
|
-
expect(
|
|
61
|
-
expect(
|
|
53
|
+
expect(io.outText()).toContain('代码审查门控检查通过');
|
|
54
|
+
expect(result.kind).toBe('ok');
|
|
62
55
|
});
|
|
63
56
|
|
|
64
57
|
it('should print failure and exit 1 when check fails', async () => {
|
|
@@ -70,10 +63,13 @@ describe('review command', () => {
|
|
|
70
63
|
});
|
|
71
64
|
MockGate.mockImplementation(() => ({ check: mockCheck }) as any);
|
|
72
65
|
|
|
73
|
-
await review({});
|
|
66
|
+
const result = await review({}, io);
|
|
74
67
|
|
|
75
|
-
expect(
|
|
76
|
-
expect(
|
|
68
|
+
expect(io.outText()).toContain('代码审查门控检查失败');
|
|
69
|
+
expect(result).toEqual({
|
|
70
|
+
kind: 'fail',
|
|
71
|
+
reason: 'review gate denied: needs approval',
|
|
72
|
+
});
|
|
77
73
|
});
|
|
78
74
|
|
|
79
75
|
it('should handle errors and exit 1', async () => {
|
|
@@ -81,19 +77,25 @@ describe('review command', () => {
|
|
|
81
77
|
const mockCheck = jest.fn().mockRejectedValue(new Error('gate error'));
|
|
82
78
|
MockGate.mockImplementation(() => ({ check: mockCheck }) as any);
|
|
83
79
|
|
|
84
|
-
await review({});
|
|
80
|
+
const result = await review({}, io);
|
|
85
81
|
|
|
86
|
-
expect(
|
|
87
|
-
expect(
|
|
82
|
+
expect(io.outText()).toContain('代码审查门控检查出错');
|
|
83
|
+
expect(result).toEqual({
|
|
84
|
+
kind: 'fail',
|
|
85
|
+
reason: 'review gate error: gate error',
|
|
86
|
+
});
|
|
88
87
|
});
|
|
89
88
|
|
|
90
89
|
it('should show hint when not in a git repo', async () => {
|
|
91
90
|
mockExec.mockRejectedValue(new Error('not a git repository'));
|
|
92
91
|
|
|
93
|
-
await review({});
|
|
92
|
+
const result = await review({}, io);
|
|
94
93
|
|
|
95
|
-
expect(
|
|
96
|
-
expect(
|
|
94
|
+
expect(io.outText()).toContain('需要在 Git 仓库中运行');
|
|
95
|
+
expect(result).toEqual({
|
|
96
|
+
kind: 'fail',
|
|
97
|
+
reason: 'review gate error: not a git repository',
|
|
98
|
+
});
|
|
97
99
|
});
|
|
98
100
|
|
|
99
101
|
it('should parse allowedReviewers from comma-separated string', async () => {
|
|
@@ -101,7 +103,7 @@ describe('review command', () => {
|
|
|
101
103
|
const mockCheck = jest.fn().mockResolvedValue({ passed: true, message: 'ok' });
|
|
102
104
|
MockGate.mockImplementation(() => ({ check: mockCheck }) as any);
|
|
103
105
|
|
|
104
|
-
await review({ allowedReviewers: 'alice, bob, charlie' });
|
|
106
|
+
await review({ allowedReviewers: 'alice, bob, charlie' }, io);
|
|
105
107
|
|
|
106
108
|
expect(MockGate).toHaveBeenCalledWith(
|
|
107
109
|
expect.objectContaining({
|
|
@@ -126,10 +128,10 @@ describe('review command', () => {
|
|
|
126
128
|
stderr: '',
|
|
127
129
|
});
|
|
128
130
|
|
|
129
|
-
await reviewStatus({});
|
|
131
|
+
await reviewStatus({}, io);
|
|
130
132
|
|
|
131
|
-
expect(
|
|
132
|
-
expect(
|
|
133
|
+
expect(io.outText()).toContain('PR #42');
|
|
134
|
+
expect(io.outText()).toContain('APPROVED');
|
|
133
135
|
});
|
|
134
136
|
|
|
135
137
|
it('should handle no PR found', async () => {
|
|
@@ -137,17 +139,17 @@ describe('review command', () => {
|
|
|
137
139
|
.mockResolvedValueOnce({ stdout: 'feature-branch\n', stderr: '' })
|
|
138
140
|
.mockRejectedValueOnce(new Error('no PR'));
|
|
139
141
|
|
|
140
|
-
await reviewStatus({});
|
|
142
|
+
await reviewStatus({}, io);
|
|
141
143
|
|
|
142
|
-
expect(
|
|
144
|
+
expect(io.outText()).toContain('未找到关联的 PR');
|
|
143
145
|
});
|
|
144
146
|
|
|
145
147
|
it('should handle git errors', async () => {
|
|
146
148
|
mockExec.mockRejectedValue(new Error('not a git repository'));
|
|
147
149
|
|
|
148
|
-
await reviewStatus({});
|
|
150
|
+
await reviewStatus({}, io);
|
|
149
151
|
|
|
150
|
-
expect(
|
|
152
|
+
expect(io.outText()).toContain('获取审查状态失败');
|
|
151
153
|
});
|
|
152
154
|
});
|
|
153
155
|
});
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import { security, auditDetails } from '../security';
|
|
6
|
+
import { captureIO, type CapturingIO } from '../../command-contract';
|
|
6
7
|
import { SecurityGate } from '../../../gates/security';
|
|
7
8
|
|
|
8
9
|
jest.mock('../../../gates/security', () => ({
|
|
@@ -22,20 +23,12 @@ jest.mock('chalk', () => ({
|
|
|
22
23
|
const MockGate = SecurityGate as jest.MockedClass<typeof SecurityGate>;
|
|
23
24
|
|
|
24
25
|
describe('security command', () => {
|
|
25
|
-
let
|
|
26
|
-
let exitSpy: jest.SpyInstance;
|
|
26
|
+
let io: CapturingIO;
|
|
27
27
|
|
|
28
28
|
beforeEach(() => {
|
|
29
|
-
jest.clearAllMocks();
|
|
30
|
-
consoleSpy = jest.spyOn(console, 'log').mockImplementation();
|
|
31
|
-
exitSpy = jest.spyOn(process, 'exit').mockImplementation(() => undefined as never);
|
|
32
|
-
process.exitCode = 0;
|
|
33
|
-
});
|
|
34
29
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
exitSpy.mockRestore();
|
|
38
|
-
process.exitCode = 0;
|
|
30
|
+
io = captureIO();
|
|
31
|
+
jest.clearAllMocks();
|
|
39
32
|
});
|
|
40
33
|
|
|
41
34
|
describe('security', () => {
|
|
@@ -47,10 +40,10 @@ describe('security command', () => {
|
|
|
47
40
|
});
|
|
48
41
|
MockGate.mockImplementation(() => ({ scan: mockScan }) as any);
|
|
49
42
|
|
|
50
|
-
await security({});
|
|
43
|
+
const result = await security({}, io);
|
|
51
44
|
|
|
52
|
-
expect(
|
|
53
|
-
expect(
|
|
45
|
+
expect(io.outText()).toContain('安全门控检查通过');
|
|
46
|
+
expect(result.kind).toBe('ok');
|
|
54
47
|
});
|
|
55
48
|
|
|
56
49
|
it('should print failure and exit 1 when scan fails', async () => {
|
|
@@ -68,19 +61,22 @@ describe('security command', () => {
|
|
|
68
61
|
});
|
|
69
62
|
MockGate.mockImplementation(() => ({ scan: mockScan }) as any);
|
|
70
63
|
|
|
71
|
-
await security({});
|
|
64
|
+
const result = await security({}, io);
|
|
72
65
|
|
|
73
|
-
expect(
|
|
74
|
-
expect(
|
|
75
|
-
expect(
|
|
76
|
-
expect(
|
|
66
|
+
expect(io.outText()).toContain('安全门控检查失败');
|
|
67
|
+
expect(io.outText()).toContain('pkg-a');
|
|
68
|
+
expect(io.outText()).toContain('pkg-b');
|
|
69
|
+
expect(result).toEqual({
|
|
70
|
+
kind: 'fail',
|
|
71
|
+
reason: 'security gate denied (threshold high): vulnerabilities found',
|
|
72
|
+
});
|
|
77
73
|
});
|
|
78
74
|
|
|
79
75
|
it('should default severity to high', async () => {
|
|
80
76
|
const mockScan = jest.fn().mockResolvedValue({ passed: true, message: 'ok' });
|
|
81
77
|
MockGate.mockImplementation(() => ({ scan: mockScan }) as any);
|
|
82
78
|
|
|
83
|
-
await security({});
|
|
79
|
+
await security({}, io);
|
|
84
80
|
|
|
85
81
|
expect(MockGate).toHaveBeenCalledWith(
|
|
86
82
|
expect.objectContaining({ severityThreshold: 'high' }),
|
|
@@ -91,7 +87,7 @@ describe('security command', () => {
|
|
|
91
87
|
const mockScan = jest.fn().mockResolvedValue({ passed: true, message: 'ok' });
|
|
92
88
|
MockGate.mockImplementation(() => ({ scan: mockScan }) as any);
|
|
93
89
|
|
|
94
|
-
await security({ severity: 'critical' });
|
|
90
|
+
await security({ severity: 'critical' }, io);
|
|
95
91
|
|
|
96
92
|
expect(MockGate).toHaveBeenCalledWith(
|
|
97
93
|
expect.objectContaining({ severityThreshold: 'critical' }),
|
|
@@ -108,9 +104,9 @@ describe('security command', () => {
|
|
|
108
104
|
});
|
|
109
105
|
MockGate.mockImplementation(() => ({ scan: mockScan }) as any);
|
|
110
106
|
|
|
111
|
-
await auditDetails({});
|
|
107
|
+
await auditDetails({}, io);
|
|
112
108
|
|
|
113
|
-
expect(
|
|
109
|
+
expect(io.outText()).toContain('未发现安全漏洞');
|
|
114
110
|
});
|
|
115
111
|
|
|
116
112
|
it('should list vulnerabilities when present', async () => {
|
|
@@ -127,17 +123,17 @@ describe('security command', () => {
|
|
|
127
123
|
});
|
|
128
124
|
MockGate.mockImplementation(() => ({ scan: mockScan }) as any);
|
|
129
125
|
|
|
130
|
-
await auditDetails({});
|
|
126
|
+
await auditDetails({}, io);
|
|
131
127
|
|
|
132
|
-
expect(
|
|
133
|
-
expect(
|
|
128
|
+
expect(io.outText()).toContain('发现 2 个漏洞');
|
|
129
|
+
expect(io.outText()).toContain('pkg-a');
|
|
134
130
|
});
|
|
135
131
|
|
|
136
132
|
it('should default severity to low for audit', async () => {
|
|
137
133
|
const mockScan = jest.fn().mockResolvedValue({ passed: true, message: 'ok', details: {} });
|
|
138
134
|
MockGate.mockImplementation(() => ({ scan: mockScan }) as any);
|
|
139
135
|
|
|
140
|
-
await auditDetails({});
|
|
136
|
+
await auditDetails({}, io);
|
|
141
137
|
|
|
142
138
|
expect(MockGate).toHaveBeenCalledWith(
|
|
143
139
|
expect.objectContaining({ severityThreshold: 'low' }),
|
|
@@ -3,13 +3,17 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import * as fs from 'fs';
|
|
6
|
+
import { captureIO, type CapturingIO } from '../../command-contract';
|
|
6
7
|
import * as path from 'path';
|
|
7
8
|
import { extractBaselineSection, specBaselineCheck } from '../spec-baseline-check';
|
|
8
9
|
|
|
10
|
+
let io: CapturingIO;
|
|
11
|
+
beforeEach(() => {
|
|
12
|
+
io = captureIO();
|
|
13
|
+
});
|
|
14
|
+
|
|
9
15
|
describe('spec-baseline-check', () => {
|
|
10
16
|
const tempDir = path.join(process.cwd(), 'temp-test-spec-baseline');
|
|
11
|
-
let consoleSpy: jest.SpyInstance;
|
|
12
|
-
let errorSpy: jest.SpyInstance;
|
|
13
17
|
let originalExitCode: string | number | undefined;
|
|
14
18
|
|
|
15
19
|
beforeAll(() => {
|
|
@@ -26,15 +30,10 @@ describe('spec-baseline-check', () => {
|
|
|
26
30
|
|
|
27
31
|
beforeEach(() => {
|
|
28
32
|
jest.clearAllMocks();
|
|
29
|
-
consoleSpy = jest.spyOn(console, 'log').mockImplementation();
|
|
30
|
-
errorSpy = jest.spyOn(console, 'error').mockImplementation();
|
|
31
33
|
originalExitCode = process.exitCode;
|
|
32
|
-
process.exitCode = 0;
|
|
33
34
|
});
|
|
34
35
|
|
|
35
36
|
afterEach(() => {
|
|
36
|
-
consoleSpy.mockRestore();
|
|
37
|
-
errorSpy.mockRestore();
|
|
38
37
|
process.exitCode = originalExitCode;
|
|
39
38
|
});
|
|
40
39
|
|
|
@@ -153,19 +152,20 @@ describe('spec-baseline-check', () => {
|
|
|
153
152
|
|
|
154
153
|
describe('specBaselineCheck CLI', () => {
|
|
155
154
|
it('应该在文件不存在时报错', async () => {
|
|
156
|
-
await specBaselineCheck('/nonexistent/spec.md');
|
|
155
|
+
const result = await specBaselineCheck('/nonexistent/spec.md', {}, io);
|
|
157
156
|
|
|
158
|
-
expect(
|
|
159
|
-
expect(
|
|
157
|
+
expect(io.errText()).toContain('文件不存在');
|
|
158
|
+
expect(result).toEqual({ kind: 'usage-error', reason: expect.stringContaining('spec 文件不存在') });
|
|
160
159
|
});
|
|
161
160
|
|
|
162
161
|
it('没有 Baseline section 应该提示', async () => {
|
|
163
162
|
const specPath = path.join(tempDir, 'no-baseline.md');
|
|
164
163
|
fs.writeFileSync(specPath, '# Spec\n## Overview\nNo baseline here.');
|
|
165
164
|
|
|
166
|
-
await specBaselineCheck(specPath, { projectPath: tempDir });
|
|
165
|
+
const result = await specBaselineCheck(specPath, { projectPath: tempDir }, io);
|
|
167
166
|
|
|
168
|
-
expect(
|
|
167
|
+
expect(io.outText()).toContain('未找到');
|
|
168
|
+
expect(result).toEqual({ kind: 'skip', reason: expect.stringContaining('无可判定项') });
|
|
169
169
|
});
|
|
170
170
|
|
|
171
171
|
it('应该输出 table 格式结果', async () => {
|
|
@@ -182,9 +182,9 @@ describe('spec-baseline-check', () => {
|
|
|
182
182
|
'- 普通文本描述',
|
|
183
183
|
].join('\n'));
|
|
184
184
|
|
|
185
|
-
await specBaselineCheck(specPath, { projectPath: subDir });
|
|
185
|
+
await specBaselineCheck(specPath, { projectPath: subDir }, io);
|
|
186
186
|
|
|
187
|
-
const output =
|
|
187
|
+
const output = io.outText();
|
|
188
188
|
expect(output).toContain('前置条件检查');
|
|
189
189
|
});
|
|
190
190
|
|
|
@@ -196,9 +196,9 @@ describe('spec-baseline-check', () => {
|
|
|
196
196
|
'- `package.json` exists',
|
|
197
197
|
].join('\n'));
|
|
198
198
|
|
|
199
|
-
await specBaselineCheck(specPath, { json: true, projectPath: tempDir });
|
|
199
|
+
await specBaselineCheck(specPath, { json: true, projectPath: tempDir }, io);
|
|
200
200
|
|
|
201
|
-
const jsonCall =
|
|
201
|
+
const jsonCall = io.outRecords().map(chunk => [chunk] as string[]).find((c: any[]) => {
|
|
202
202
|
try {
|
|
203
203
|
const parsed = JSON.parse(c[0]);
|
|
204
204
|
return Array.isArray(parsed);
|
|
@@ -207,7 +207,7 @@ describe('spec-baseline-check', () => {
|
|
|
207
207
|
}
|
|
208
208
|
});
|
|
209
209
|
expect(jsonCall).toBeTruthy();
|
|
210
|
-
const parsed = JSON.parse(jsonCall[0]);
|
|
210
|
+
const parsed = JSON.parse(jsonCall![0]);
|
|
211
211
|
expect(parsed.length).toBe(1);
|
|
212
212
|
expect(parsed[0]).toHaveProperty('prerequisite');
|
|
213
213
|
expect(parsed[0]).toHaveProperty('satisfied');
|
|
@@ -218,9 +218,9 @@ describe('spec-baseline-check', () => {
|
|
|
218
218
|
const specPath = path.join(tempDir, 'empty-baseline.md');
|
|
219
219
|
fs.writeFileSync(specPath, '# Test\n## Baseline\n\n## Next');
|
|
220
220
|
|
|
221
|
-
await specBaselineCheck(specPath, { json: true, projectPath: tempDir });
|
|
221
|
+
await specBaselineCheck(specPath, { json: true, projectPath: tempDir }, io);
|
|
222
222
|
|
|
223
|
-
const jsonCall =
|
|
223
|
+
const jsonCall = io.outRecords().map(chunk => [chunk] as string[]).find((c: any[]) => {
|
|
224
224
|
try {
|
|
225
225
|
const parsed = JSON.parse(c[0]);
|
|
226
226
|
return parsed.prerequisites !== undefined;
|
|
@@ -229,7 +229,7 @@ describe('spec-baseline-check', () => {
|
|
|
229
229
|
}
|
|
230
230
|
});
|
|
231
231
|
expect(jsonCall).toBeTruthy();
|
|
232
|
-
expect(JSON.parse(jsonCall[0]).prerequisites).toHaveLength(0);
|
|
232
|
+
expect(JSON.parse(jsonCall![0]).prerequisites).toHaveLength(0);
|
|
233
233
|
});
|
|
234
234
|
});
|
|
235
235
|
});
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import { specValidate, listSpecTypes } from '../spec';
|
|
6
|
+
import { captureIO, type CapturingIO } from '../../command-contract';
|
|
6
7
|
import { SpecValidator, validateAllSpecs } from '../../../core/spec/validator';
|
|
7
8
|
import type { BatchSpecValidationResult, SpecValidationResult } from '../../../types/spec';
|
|
8
9
|
|
|
@@ -27,8 +28,12 @@ jest.mock('chalk', () => ({
|
|
|
27
28
|
const MockSpecValidator = SpecValidator as jest.Mocked<typeof SpecValidator>;
|
|
28
29
|
const mockValidateAllSpecs = validateAllSpecs as jest.MockedFunction<typeof validateAllSpecs>;
|
|
29
30
|
|
|
31
|
+
let io: CapturingIO;
|
|
32
|
+
beforeEach(() => {
|
|
33
|
+
io = captureIO();
|
|
34
|
+
});
|
|
35
|
+
|
|
30
36
|
describe('spec command', () => {
|
|
31
|
-
let consoleSpy: jest.SpyInstance;
|
|
32
37
|
let mockValidator: {
|
|
33
38
|
setConfig: jest.Mock;
|
|
34
39
|
validateFile: jest.Mock;
|
|
@@ -36,8 +41,6 @@ describe('spec command', () => {
|
|
|
36
41
|
|
|
37
42
|
beforeEach(() => {
|
|
38
43
|
jest.clearAllMocks();
|
|
39
|
-
consoleSpy = jest.spyOn(console, 'log').mockImplementation();
|
|
40
|
-
process.exitCode = 0;
|
|
41
44
|
|
|
42
45
|
mockValidator = {
|
|
43
46
|
setConfig: jest.fn(),
|
|
@@ -46,11 +49,6 @@ describe('spec command', () => {
|
|
|
46
49
|
(MockSpecValidator.getInstance as jest.Mock).mockReturnValue(mockValidator);
|
|
47
50
|
});
|
|
48
51
|
|
|
49
|
-
afterEach(() => {
|
|
50
|
-
consoleSpy.mockRestore();
|
|
51
|
-
process.exitCode = 0;
|
|
52
|
-
});
|
|
53
|
-
|
|
54
52
|
describe('specValidate', () => {
|
|
55
53
|
describe('单文件验证', () => {
|
|
56
54
|
it('应该验证单个文件', async () => {
|
|
@@ -63,8 +61,8 @@ describe('spec command', () => {
|
|
|
63
61
|
};
|
|
64
62
|
mockValidator.validateFile.mockResolvedValue(mockResult);
|
|
65
63
|
|
|
66
|
-
await specValidate({ file: 'test.yml' });
|
|
67
|
-
expect(
|
|
64
|
+
await specValidate({ file: 'test.yml' }, io);
|
|
65
|
+
expect(io.outText()).toContain('验证通过');
|
|
68
66
|
});
|
|
69
67
|
|
|
70
68
|
it('应该显示验证错误', async () => {
|
|
@@ -77,9 +75,9 @@ describe('spec command', () => {
|
|
|
77
75
|
};
|
|
78
76
|
mockValidator.validateFile.mockResolvedValue(mockResult);
|
|
79
77
|
|
|
80
|
-
await specValidate({ file: 'test.yml' });
|
|
81
|
-
expect(
|
|
82
|
-
expect(
|
|
78
|
+
await specValidate({ file: 'test.yml' }, io);
|
|
79
|
+
expect(io.outText()).toContain('验证失败');
|
|
80
|
+
expect(io.outText()).toContain('错误');
|
|
83
81
|
});
|
|
84
82
|
|
|
85
83
|
it('应该显示警告', async () => {
|
|
@@ -92,8 +90,8 @@ describe('spec command', () => {
|
|
|
92
90
|
};
|
|
93
91
|
mockValidator.validateFile.mockResolvedValue(mockResult);
|
|
94
92
|
|
|
95
|
-
await specValidate({ file: 'test.yml' });
|
|
96
|
-
expect(
|
|
93
|
+
await specValidate({ file: 'test.yml' }, io);
|
|
94
|
+
expect(io.outText()).toContain('警告');
|
|
97
95
|
});
|
|
98
96
|
|
|
99
97
|
it('应该显示详细指标', async () => {
|
|
@@ -107,8 +105,8 @@ describe('spec command', () => {
|
|
|
107
105
|
};
|
|
108
106
|
mockValidator.validateFile.mockResolvedValue(mockResult);
|
|
109
107
|
|
|
110
|
-
await specValidate({ file: 'test.yml', verbose: true });
|
|
111
|
-
expect(
|
|
108
|
+
await specValidate({ file: 'test.yml', verbose: true }, io);
|
|
109
|
+
expect(io.outText()).toContain('指标');
|
|
112
110
|
});
|
|
113
111
|
});
|
|
114
112
|
|
|
@@ -123,8 +121,8 @@ describe('spec command', () => {
|
|
|
123
121
|
};
|
|
124
122
|
mockValidateAllSpecs.mockResolvedValue(mockResult);
|
|
125
123
|
|
|
126
|
-
await specValidate({});
|
|
127
|
-
expect(
|
|
124
|
+
await specValidate({}, io);
|
|
125
|
+
expect(io.outText()).toContain('所有 Spec 文件验证通过');
|
|
128
126
|
});
|
|
129
127
|
|
|
130
128
|
it('应该显示失败统计', async () => {
|
|
@@ -139,8 +137,8 @@ describe('spec command', () => {
|
|
|
139
137
|
};
|
|
140
138
|
mockValidateAllSpecs.mockResolvedValue(mockResult);
|
|
141
139
|
|
|
142
|
-
await specValidate({});
|
|
143
|
-
expect(
|
|
140
|
+
await specValidate({}, io);
|
|
141
|
+
expect(io.outText()).toContain('失败: 2');
|
|
144
142
|
});
|
|
145
143
|
|
|
146
144
|
it('应该处理无 Spec 文件情况', async () => {
|
|
@@ -153,11 +151,25 @@ describe('spec command', () => {
|
|
|
153
151
|
};
|
|
154
152
|
mockValidateAllSpecs.mockResolvedValue(mockResult);
|
|
155
153
|
|
|
156
|
-
await specValidate({});
|
|
157
|
-
expect(
|
|
154
|
+
await specValidate({}, io);
|
|
155
|
+
expect(io.outText()).toContain('没有找到 Spec 文件');
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it('批量验证有失败:fail + 失败数原因', async () => {
|
|
159
|
+
const mockResult: BatchSpecValidationResult = {
|
|
160
|
+
total: 2,
|
|
161
|
+
passed: 1,
|
|
162
|
+
failed: 1,
|
|
163
|
+
warnings: 0,
|
|
164
|
+
results: [],
|
|
165
|
+
};
|
|
166
|
+
mockValidateAllSpecs.mockResolvedValue(mockResult);
|
|
167
|
+
|
|
168
|
+
const result = await specValidate({}, io);
|
|
169
|
+
expect(result).toEqual({ kind: 'fail', reason: '1 个 Spec 文件验证失败' });
|
|
158
170
|
});
|
|
159
171
|
|
|
160
|
-
it('
|
|
172
|
+
it('--staged 有失败:ok(历史退出码面 0,冻结不变)', async () => {
|
|
161
173
|
const mockResult: BatchSpecValidationResult = {
|
|
162
174
|
total: 2,
|
|
163
175
|
passed: 1,
|
|
@@ -167,8 +179,8 @@ describe('spec command', () => {
|
|
|
167
179
|
};
|
|
168
180
|
mockValidateAllSpecs.mockResolvedValue(mockResult);
|
|
169
181
|
|
|
170
|
-
await specValidate({});
|
|
171
|
-
expect(
|
|
182
|
+
const result = await specValidate({ staged: true }, io);
|
|
183
|
+
expect(result).toEqual({ kind: 'ok' });
|
|
172
184
|
});
|
|
173
185
|
});
|
|
174
186
|
|
|
@@ -178,7 +190,7 @@ describe('spec command', () => {
|
|
|
178
190
|
total: 0, passed: 0, failed: 0, warnings: 0, results: [],
|
|
179
191
|
});
|
|
180
192
|
|
|
181
|
-
await specValidate({ schema: 'custom-schema.ts' });
|
|
193
|
+
await specValidate({ schema: 'custom-schema.ts' }, io);
|
|
182
194
|
expect(mockValidator.setConfig).toHaveBeenCalled();
|
|
183
195
|
});
|
|
184
196
|
});
|
|
@@ -186,8 +198,8 @@ describe('spec command', () => {
|
|
|
186
198
|
|
|
187
199
|
describe('listSpecTypes', () => {
|
|
188
200
|
it('应该列出支持的 Spec 类型', () => {
|
|
189
|
-
listSpecTypes();
|
|
190
|
-
expect(
|
|
201
|
+
listSpecTypes({}, io);
|
|
202
|
+
expect(io.outText()).toContain('支持的 Spec 类型');
|
|
191
203
|
});
|
|
192
204
|
});
|
|
193
205
|
});
|