@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,9 +3,15 @@
|
|
|
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 { syncDocs } from '../sync-docs';
|
|
8
9
|
|
|
10
|
+
let io: CapturingIO;
|
|
11
|
+
beforeEach(() => {
|
|
12
|
+
io = captureIO();
|
|
13
|
+
});
|
|
14
|
+
|
|
9
15
|
describe('sync-docs command', () => {
|
|
10
16
|
const tempDir = path.join(process.cwd(), 'temp-test-sync-docs');
|
|
11
17
|
|
|
@@ -21,15 +27,8 @@ describe('sync-docs command', () => {
|
|
|
21
27
|
}
|
|
22
28
|
});
|
|
23
29
|
|
|
24
|
-
let consoleSpy: jest.SpyInstance;
|
|
25
|
-
|
|
26
30
|
beforeEach(() => {
|
|
27
31
|
jest.clearAllMocks();
|
|
28
|
-
consoleSpy = jest.spyOn(console, 'log').mockImplementation();
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
afterEach(() => {
|
|
32
|
-
consoleSpy.mockRestore();
|
|
33
32
|
});
|
|
34
33
|
|
|
35
34
|
describe('generateModuleTable', () => {
|
|
@@ -38,11 +37,11 @@ describe('sync-docs command', () => {
|
|
|
38
37
|
fs.mkdirSync(testDir, { recursive: true });
|
|
39
38
|
|
|
40
39
|
// No src dir → no modules → nothing to report
|
|
41
|
-
const result = await syncDocs({ projectPath: testDir });
|
|
40
|
+
const result = await syncDocs({ projectPath: testDir }, io);
|
|
42
41
|
|
|
43
|
-
expect(result).
|
|
42
|
+
expect(result).toEqual({ kind: 'ok' });
|
|
44
43
|
// When no modules and no changes, all docs are current
|
|
45
|
-
expect(
|
|
44
|
+
expect(io.outText()).toContain('所有文档');
|
|
46
45
|
|
|
47
46
|
fs.rmSync(testDir, { recursive: true, force: true });
|
|
48
47
|
});
|
|
@@ -55,7 +54,7 @@ describe('sync-docs command', () => {
|
|
|
55
54
|
// Create a source file with JSDoc comment
|
|
56
55
|
fs.writeFileSync(path.join(srcDir, 'mymodule.ts'), '/**\n * My test module\n */\nexport const x = 1;');
|
|
57
56
|
|
|
58
|
-
await syncDocs({ projectPath: testDir });
|
|
57
|
+
await syncDocs({ projectPath: testDir }, io);
|
|
59
58
|
|
|
60
59
|
const capPath = path.join(testDir, 'CAPABILITIES.md');
|
|
61
60
|
const content = fs.readFileSync(capPath, 'utf-8');
|
|
@@ -75,7 +74,7 @@ describe('sync-docs command', () => {
|
|
|
75
74
|
|
|
76
75
|
fs.writeFileSync(path.join(srcDir, 'jsdoc-mod.ts'), '/**\n * JSDoc module description\n */\nexport const y = 2;');
|
|
77
76
|
|
|
78
|
-
await syncDocs({ projectPath: testDir });
|
|
77
|
+
await syncDocs({ projectPath: testDir }, io);
|
|
79
78
|
|
|
80
79
|
const capPath = path.join(testDir, 'CAPABILITIES.md');
|
|
81
80
|
const content = fs.readFileSync(capPath, 'utf-8');
|
|
@@ -93,7 +92,7 @@ describe('sync-docs command', () => {
|
|
|
93
92
|
const filePath = path.join(srcDir, 'line-mod.ts');
|
|
94
93
|
fs.writeFileSync(filePath, '// Line comment module\nexport const z = 3;');
|
|
95
94
|
|
|
96
|
-
await syncDocs({ projectPath: testDir });
|
|
95
|
+
await syncDocs({ projectPath: testDir }, io);
|
|
97
96
|
|
|
98
97
|
const capPath = path.join(testDir, 'CAPABILITIES.md');
|
|
99
98
|
const content = fs.readFileSync(capPath, 'utf-8');
|
|
@@ -109,7 +108,7 @@ describe('sync-docs command', () => {
|
|
|
109
108
|
|
|
110
109
|
fs.writeFileSync(path.join(srcDir, 'bare.ts'), 'export const a = 1;');
|
|
111
110
|
|
|
112
|
-
await syncDocs({ projectPath: testDir });
|
|
111
|
+
await syncDocs({ projectPath: testDir }, io);
|
|
113
112
|
|
|
114
113
|
const capPath = path.join(testDir, 'CAPABILITIES.md');
|
|
115
114
|
const content = fs.readFileSync(capPath, 'utf-8');
|
|
@@ -120,16 +119,15 @@ describe('sync-docs command', () => {
|
|
|
120
119
|
});
|
|
121
120
|
});
|
|
122
121
|
|
|
123
|
-
|
|
124
122
|
describe('syncDocs', () => {
|
|
125
123
|
it('无 src/ 目录应该跳过模块扫描', async () => {
|
|
126
124
|
const emptyDir = path.join(tempDir, 'empty');
|
|
127
125
|
fs.mkdirSync(emptyDir, { recursive: true });
|
|
128
126
|
|
|
129
|
-
const result = await syncDocs({ projectPath: emptyDir });
|
|
130
|
-
expect(result).
|
|
127
|
+
const result = await syncDocs({ projectPath: emptyDir }, io);
|
|
128
|
+
expect(result).toEqual({ kind: 'ok' });
|
|
131
129
|
// 无 src/ 目录,模块扫描返回空,无差异
|
|
132
|
-
expect(
|
|
130
|
+
expect(io.outText()).toContain('所有文档都是最新的');
|
|
133
131
|
|
|
134
132
|
fs.rmSync(emptyDir, { recursive: true, force: true });
|
|
135
133
|
});
|
|
@@ -142,8 +140,8 @@ describe('sync-docs command', () => {
|
|
|
142
140
|
// 创建 CAPABILITIES.md,无表格
|
|
143
141
|
fs.writeFileSync(path.join(testDir, 'CAPABILITIES.md'), '# Capabilities\n\nNo table.');
|
|
144
142
|
|
|
145
|
-
const result = await syncDocs({ projectPath: testDir });
|
|
146
|
-
expect(result).
|
|
143
|
+
const result = await syncDocs({ projectPath: testDir }, io);
|
|
144
|
+
expect(result).toEqual({ kind: 'ok' });
|
|
147
145
|
|
|
148
146
|
fs.rmSync(testDir, { recursive: true, force: true });
|
|
149
147
|
});
|
|
@@ -162,9 +160,9 @@ describe('sync-docs command', () => {
|
|
|
162
160
|
'# Capabilities\n\n| 模块 | 文件 | 说明 |\n|------|------|------|\n| old | src/old.ts | old |'
|
|
163
161
|
);
|
|
164
162
|
|
|
165
|
-
const result = await syncDocs({ projectPath: testDir, check: true });
|
|
166
|
-
expect(result).
|
|
167
|
-
expect(
|
|
163
|
+
const result = await syncDocs({ projectPath: testDir, check: true }, io);
|
|
164
|
+
expect(result).toEqual({ kind: 'fail', reason: expect.stringContaining('文档不是最新') });
|
|
165
|
+
expect(io.outText()).toContain('文档不是最新的');
|
|
168
166
|
|
|
169
167
|
fs.rmSync(testDir, { recursive: true, force: true });
|
|
170
168
|
});
|
|
@@ -183,15 +181,15 @@ describe('sync-docs command', () => {
|
|
|
183
181
|
);
|
|
184
182
|
|
|
185
183
|
// 第一轮:生成 CAPABILITIES.md(描述行以 / 结尾)
|
|
186
|
-
await syncDocs({ projectPath: testDir });
|
|
184
|
+
await syncDocs({ projectPath: testDir }, io);
|
|
187
185
|
const cap = fs.readFileSync(path.join(testDir, 'CAPABILITIES.md'), 'utf-8');
|
|
188
186
|
expect(cap).toContain('workspace.ts');
|
|
189
187
|
expect(cap).toMatch(/gitRepo\s*\/\s*\|/);
|
|
190
188
|
|
|
191
189
|
// 第二轮:生成后立即 --check 必须收敛(修复前此处必 false)
|
|
192
|
-
|
|
193
|
-
const check = await syncDocs({ projectPath: testDir, check: true });
|
|
194
|
-
expect(check).
|
|
190
|
+
io = captureIO();
|
|
191
|
+
const check = await syncDocs({ projectPath: testDir, check: true }, io);
|
|
192
|
+
expect(check).toEqual({ kind: 'ok' });
|
|
195
193
|
|
|
196
194
|
fs.rmSync(testDir, { recursive: true, force: true });
|
|
197
195
|
});
|
|
@@ -210,8 +208,8 @@ describe('sync-docs command', () => {
|
|
|
210
208
|
);
|
|
211
209
|
|
|
212
210
|
// src/ 目录条目覆盖 mymodule.ts → 无 added,check 通过
|
|
213
|
-
const result = await syncDocs({ projectPath: testDir, check: true });
|
|
214
|
-
expect(result).
|
|
211
|
+
const result = await syncDocs({ projectPath: testDir, check: true }, io);
|
|
212
|
+
expect(result).toEqual({ kind: 'ok' });
|
|
215
213
|
|
|
216
214
|
fs.rmSync(testDir, { recursive: true, force: true });
|
|
217
215
|
});
|
|
@@ -239,9 +237,9 @@ describe('sync-docs command', () => {
|
|
|
239
237
|
|
|
240
238
|
// 不创建 CONTEXT.md
|
|
241
239
|
|
|
242
|
-
const result = await syncDocs({ projectPath: testDir });
|
|
243
|
-
expect(result).
|
|
244
|
-
expect(
|
|
240
|
+
const result = await syncDocs({ projectPath: testDir }, io);
|
|
241
|
+
expect(result).toEqual({ kind: 'ok' });
|
|
242
|
+
expect(io.outText()).toContain('缺少 CONTEXT.md');
|
|
245
243
|
|
|
246
244
|
fs.rmSync(testDir, { recursive: true, force: true });
|
|
247
245
|
});
|
|
@@ -269,8 +267,8 @@ describe('sync-docs command', () => {
|
|
|
269
267
|
// 创建 CONTEXT.md
|
|
270
268
|
fs.writeFileSync(path.join(srcDir, 'CONTEXT.md'), '# src\n\nTest context');
|
|
271
269
|
|
|
272
|
-
const result = await syncDocs({ projectPath: testDir });
|
|
273
|
-
expect(result).
|
|
270
|
+
const result = await syncDocs({ projectPath: testDir }, io);
|
|
271
|
+
expect(result).toEqual({ kind: 'ok' });
|
|
274
272
|
|
|
275
273
|
fs.rmSync(testDir, { recursive: true, force: true });
|
|
276
274
|
});
|
|
@@ -281,8 +279,8 @@ describe('sync-docs command', () => {
|
|
|
281
279
|
|
|
282
280
|
// 无 .harness/config.yml
|
|
283
281
|
|
|
284
|
-
const result = await syncDocs({ projectPath: testDir });
|
|
285
|
-
expect(result).
|
|
282
|
+
const result = await syncDocs({ projectPath: testDir }, io);
|
|
283
|
+
expect(result).toEqual({ kind: 'ok' });
|
|
286
284
|
|
|
287
285
|
fs.rmSync(testDir, { recursive: true, force: true });
|
|
288
286
|
});
|
|
@@ -307,7 +305,7 @@ describe('sync-docs command', () => {
|
|
|
307
305
|
})
|
|
308
306
|
);
|
|
309
307
|
|
|
310
|
-
await syncDocs({ projectPath: testDir });
|
|
308
|
+
await syncDocs({ projectPath: testDir }, io);
|
|
311
309
|
|
|
312
310
|
// 验证 CONTEXT.md 被创建
|
|
313
311
|
const contextPath = path.join(srcDir, 'CONTEXT.md');
|
|
@@ -326,7 +324,7 @@ describe('sync-docs command', () => {
|
|
|
326
324
|
// 创建源文件
|
|
327
325
|
fs.writeFileSync(path.join(srcDir, 'module.ts'), 'export const x = 1;');
|
|
328
326
|
|
|
329
|
-
await syncDocs({ projectPath: testDir });
|
|
327
|
+
await syncDocs({ projectPath: testDir }, io);
|
|
330
328
|
|
|
331
329
|
// 验证 CAPABILITIES.md 被创建
|
|
332
330
|
const capPath = path.join(testDir, 'CAPABILITIES.md');
|
|
@@ -346,11 +344,11 @@ describe('sync-docs command', () => {
|
|
|
346
344
|
'# Capabilities\n\n| 模块 | 文件 | 说明 |\n|------|------|------|\n| old | src/old.ts | old |'
|
|
347
345
|
);
|
|
348
346
|
|
|
349
|
-
const result = await syncDocs({ projectPath: testDir, check: true, json: true });
|
|
350
|
-
expect(result).
|
|
347
|
+
const result = await syncDocs({ projectPath: testDir, check: true, json: true }, io);
|
|
348
|
+
expect(result).toEqual({ kind: 'fail', reason: expect.stringContaining('文档不是最新') });
|
|
351
349
|
|
|
352
350
|
// 验证 console.log 被调用了 JSON
|
|
353
|
-
const jsonCall =
|
|
351
|
+
const jsonCall = io.outRecords().map(chunk => [chunk] as string[]).find(
|
|
354
352
|
(call: unknown[]) => typeof call[0] === 'string' && call[0].startsWith('{')
|
|
355
353
|
);
|
|
356
354
|
expect(jsonCall).toBeDefined();
|
|
@@ -366,10 +364,10 @@ describe('sync-docs command', () => {
|
|
|
366
364
|
fs.mkdirSync(testDir, { recursive: true });
|
|
367
365
|
fs.writeFileSync(path.join(testDir, 'CAPABILITIES.md'), '# Capabilities\n\nNo table.');
|
|
368
366
|
|
|
369
|
-
const result = await syncDocs({ projectPath: testDir, check: true, json: true });
|
|
370
|
-
expect(result).
|
|
367
|
+
const result = await syncDocs({ projectPath: testDir, check: true, json: true }, io);
|
|
368
|
+
expect(result).toEqual({ kind: 'ok' });
|
|
371
369
|
|
|
372
|
-
const jsonCall =
|
|
370
|
+
const jsonCall = io.outRecords().map(chunk => [chunk] as string[]).find(
|
|
373
371
|
(call: unknown[]) => typeof call[0] === 'string' && call[0].startsWith('{')
|
|
374
372
|
);
|
|
375
373
|
expect(jsonCall).toBeDefined();
|
|
@@ -389,7 +387,7 @@ describe('sync-docs command', () => {
|
|
|
389
387
|
fs.writeFileSync(path.join(srcDir, 'removed.ts'), 'export const removed = 1;');
|
|
390
388
|
|
|
391
389
|
// 先让 syncDocs 创建初始 CAPABILITIES.md(包含两个文件)
|
|
392
|
-
await syncDocs({ projectPath: testDir });
|
|
390
|
+
await syncDocs({ projectPath: testDir }, io);
|
|
393
391
|
let content = fs.readFileSync(path.join(testDir, 'CAPABILITIES.md'), 'utf-8');
|
|
394
392
|
expect(content).toContain('kept.ts');
|
|
395
393
|
expect(content).toContain('removed.ts');
|
|
@@ -398,7 +396,7 @@ describe('sync-docs command', () => {
|
|
|
398
396
|
fs.unlinkSync(path.join(srcDir, 'removed.ts'));
|
|
399
397
|
|
|
400
398
|
// 重新运行 syncDocs(写入模式)
|
|
401
|
-
await syncDocs({ projectPath: testDir });
|
|
399
|
+
await syncDocs({ projectPath: testDir }, io);
|
|
402
400
|
content = fs.readFileSync(path.join(testDir, 'CAPABILITIES.md'), 'utf-8');
|
|
403
401
|
expect(content).toContain('kept.ts');
|
|
404
402
|
expect(content).not.toContain('removed.ts');
|
|
@@ -430,14 +428,14 @@ describe('sync-docs command', () => {
|
|
|
430
428
|
].join('\n')
|
|
431
429
|
);
|
|
432
430
|
|
|
433
|
-
await syncDocs({ projectPath: testDir });
|
|
431
|
+
await syncDocs({ projectPath: testDir }, io);
|
|
434
432
|
const content = fs.readFileSync(path.join(testDir, 'CAPABILITIES.md'), 'utf-8');
|
|
435
433
|
expect(content).toContain('DistillProposalCard.tsx');
|
|
436
434
|
|
|
437
435
|
// 修复前:--check 每跑必报「包含已删除的模块」不收敛
|
|
438
|
-
|
|
439
|
-
const check = await syncDocs({ projectPath: testDir, check: true });
|
|
440
|
-
expect(check).
|
|
436
|
+
io = captureIO();
|
|
437
|
+
const check = await syncDocs({ projectPath: testDir, check: true }, io);
|
|
438
|
+
expect(check).toEqual({ kind: 'ok' });
|
|
441
439
|
|
|
442
440
|
fs.rmSync(testDir, { recursive: true, force: true });
|
|
443
441
|
});
|
|
@@ -454,11 +452,11 @@ describe('sync-docs command', () => {
|
|
|
454
452
|
'# Capabilities\n\n| 模块 | 文件 | 说明 |\n|------|------|------|\n| util | src/util.ts | util |'
|
|
455
453
|
);
|
|
456
454
|
|
|
457
|
-
const check = await syncDocs({ projectPath: testDir, check: true });
|
|
458
|
-
expect(check).
|
|
459
|
-
expect(
|
|
455
|
+
const check = await syncDocs({ projectPath: testDir, check: true }, io);
|
|
456
|
+
expect(check).toEqual({ kind: 'fail', reason: expect.stringContaining('文档不是最新') });
|
|
457
|
+
expect(io.outText()).toContain('Card.tsx');
|
|
460
458
|
|
|
461
|
-
await syncDocs({ projectPath: testDir });
|
|
459
|
+
await syncDocs({ projectPath: testDir }, io);
|
|
462
460
|
const content = fs.readFileSync(path.join(testDir, 'CAPABILITIES.md'), 'utf-8');
|
|
463
461
|
expect(content).toContain('Card.tsx');
|
|
464
462
|
|
|
@@ -484,14 +482,14 @@ describe('sync-docs command', () => {
|
|
|
484
482
|
].join('\n')
|
|
485
483
|
);
|
|
486
484
|
|
|
487
|
-
await syncDocs({ projectPath: testDir });
|
|
485
|
+
await syncDocs({ projectPath: testDir }, io);
|
|
488
486
|
const content = fs.readFileSync(path.join(testDir, 'CAPABILITIES.md'), 'utf-8');
|
|
489
487
|
expect(content).toContain('src/helper.js');
|
|
490
488
|
expect(content).toContain('src/Helper.jsx');
|
|
491
489
|
|
|
492
|
-
|
|
493
|
-
const check = await syncDocs({ projectPath: testDir, check: true });
|
|
494
|
-
expect(check).
|
|
490
|
+
io = captureIO();
|
|
491
|
+
const check = await syncDocs({ projectPath: testDir, check: true }, io);
|
|
492
|
+
expect(check).toEqual({ kind: 'ok' });
|
|
495
493
|
|
|
496
494
|
fs.rmSync(testDir, { recursive: true, force: true });
|
|
497
495
|
});
|
|
@@ -518,7 +516,7 @@ describe('sync-docs command', () => {
|
|
|
518
516
|
].join('\n'),
|
|
519
517
|
);
|
|
520
518
|
|
|
521
|
-
await syncDocs({ projectPath: testDir });
|
|
519
|
+
await syncDocs({ projectPath: testDir }, io);
|
|
522
520
|
const content = fs.readFileSync(path.join(testDir, 'CAPABILITIES.md'), 'utf-8');
|
|
523
521
|
expect(content).toContain('src/a/routes.ts');
|
|
524
522
|
expect(content).not.toContain('src/ghost/routes.ts');
|
|
@@ -551,9 +549,9 @@ describe('sync-docs command', () => {
|
|
|
551
549
|
'# Capabilities\n\n| 模块 | 文件 | 说明 |\n|------|------|------|\n| 核心 | src/core/ | 核心模块 |'
|
|
552
550
|
);
|
|
553
551
|
|
|
554
|
-
const result = await syncDocs({ projectPath: testDir });
|
|
552
|
+
const result = await syncDocs({ projectPath: testDir }, io);
|
|
555
553
|
// 有未覆盖目录,仍报不同步
|
|
556
|
-
expect(result).
|
|
554
|
+
expect(result).toEqual({ kind: 'ok' });
|
|
557
555
|
// 但不自动给 b.ts 加表格行
|
|
558
556
|
const content = fs.readFileSync(path.join(testDir, 'CAPABILITIES.md'), 'utf-8');
|
|
559
557
|
expect(content).not.toContain('b.ts');
|
|
@@ -575,10 +573,10 @@ describe('sync-docs command', () => {
|
|
|
575
573
|
'# Capabilities\n\n| 模块 | 文件 | 说明 |\n|------|------|------|\n| 核心 | src/core/ | 核心模块 |'
|
|
576
574
|
);
|
|
577
575
|
|
|
578
|
-
const result = await syncDocs({ projectPath: testDir, check: true });
|
|
579
|
-
expect(result).
|
|
580
|
-
expect(
|
|
581
|
-
expect(
|
|
576
|
+
const result = await syncDocs({ projectPath: testDir, check: true }, io);
|
|
577
|
+
expect(result).toEqual({ kind: 'fail', reason: expect.stringContaining('文档不是最新') });
|
|
578
|
+
expect(io.outText()).toContain('未登记以下模块(目录)');
|
|
579
|
+
expect(io.outText()).toContain('src/newdir/');
|
|
582
580
|
|
|
583
581
|
fs.rmSync(testDir, { recursive: true, force: true });
|
|
584
582
|
});
|
|
@@ -596,10 +594,10 @@ describe('sync-docs command', () => {
|
|
|
596
594
|
'# Capabilities\n\n| 模块 | 文件 | 说明 |\n|------|------|------|\n| 核心 | src/core/ | 核心模块 |'
|
|
597
595
|
);
|
|
598
596
|
|
|
599
|
-
const result = await syncDocs({ projectPath: testDir, check: true, json: true });
|
|
600
|
-
expect(result).
|
|
597
|
+
const result = await syncDocs({ projectPath: testDir, check: true, json: true }, io);
|
|
598
|
+
expect(result).toEqual({ kind: 'fail', reason: expect.stringContaining('文档不是最新') });
|
|
601
599
|
|
|
602
|
-
const jsonCall =
|
|
600
|
+
const jsonCall = io.outRecords().map(chunk => [chunk] as string[]).find(
|
|
603
601
|
(call: unknown[]) => typeof call[0] === 'string' && call[0].startsWith('{')
|
|
604
602
|
);
|
|
605
603
|
expect(jsonCall).toBeDefined();
|
|
@@ -628,7 +626,7 @@ describe('sync-docs command', () => {
|
|
|
628
626
|
].join('\n')
|
|
629
627
|
);
|
|
630
628
|
|
|
631
|
-
await syncDocs({ projectPath: testDir });
|
|
629
|
+
await syncDocs({ projectPath: testDir }, io);
|
|
632
630
|
const content = fs.readFileSync(path.join(testDir, 'CAPABILITIES.md'), 'utf-8');
|
|
633
631
|
expect(content).toContain('src/a/kept.ts');
|
|
634
632
|
expect(content).not.toContain('ghost.ts');
|
|
@@ -658,8 +656,8 @@ describe('sync-docs command', () => {
|
|
|
658
656
|
].join('\n')
|
|
659
657
|
);
|
|
660
658
|
|
|
661
|
-
const result = await syncDocs({ projectPath: testDir, compact: true });
|
|
662
|
-
expect(result).
|
|
659
|
+
const result = await syncDocs({ projectPath: testDir, compact: true }, io);
|
|
660
|
+
expect(result).toEqual({ kind: 'ok' });
|
|
663
661
|
|
|
664
662
|
const content = fs.readFileSync(path.join(testDir, 'CAPABILITIES.md'), 'utf-8');
|
|
665
663
|
// 折叠为目录条目(说明取组内第一行)
|
|
@@ -672,7 +670,7 @@ describe('sync-docs command', () => {
|
|
|
672
670
|
expect(content).toContain('手工说明');
|
|
673
671
|
|
|
674
672
|
// 幂等:再跑一遍内容不变
|
|
675
|
-
await syncDocs({ projectPath: testDir, compact: true });
|
|
673
|
+
await syncDocs({ projectPath: testDir, compact: true }, io);
|
|
676
674
|
const content2 = fs.readFileSync(path.join(testDir, 'CAPABILITIES.md'), 'utf-8');
|
|
677
675
|
expect(content2).toBe(content);
|
|
678
676
|
|
|
@@ -683,8 +681,8 @@ describe('sync-docs command', () => {
|
|
|
683
681
|
const testDir = path.join(tempDir, 'compact-missing');
|
|
684
682
|
fs.mkdirSync(testDir, { recursive: true });
|
|
685
683
|
|
|
686
|
-
const result = await syncDocs({ projectPath: testDir, compact: true });
|
|
687
|
-
expect(result).
|
|
684
|
+
const result = await syncDocs({ projectPath: testDir, compact: true }, io);
|
|
685
|
+
expect(result).toEqual({ kind: 'ok' });
|
|
688
686
|
|
|
689
687
|
fs.rmSync(testDir, { recursive: true, force: true });
|
|
690
688
|
});
|
|
@@ -8,8 +8,9 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import * as fs from 'fs';
|
|
11
|
+
import { captureIO, lastJsonOutput, type CapturingIO } from '../../command-contract';
|
|
11
12
|
import * as path from 'path';
|
|
12
|
-
import { updateUserModel } from '../update-user-model';
|
|
13
|
+
import { updateUserModel, resolveUserModelPaths } from '../update-user-model';
|
|
13
14
|
import { readTranscriptSessions, type MinedSession } from '../../session-mining';
|
|
14
15
|
|
|
15
16
|
const TEST_HOME = '/tmp/harness-uum-test-home';
|
|
@@ -53,6 +54,7 @@ const mockReadTranscriptSessions = readTranscriptSessions as jest.MockedFunction
|
|
|
53
54
|
>;
|
|
54
55
|
|
|
55
56
|
const STATE_FILE = path.join(TEST_HOME, '.claude', 'user-model-state.json');
|
|
57
|
+
const PROFILE_FILE = path.join(TEST_HOME, '.claude', 'projects', '-root-projects', 'memory', 'user_profile.md');
|
|
56
58
|
|
|
57
59
|
function todayStr(offsetDays = 0): string {
|
|
58
60
|
return new Date(Date.now() - offsetDays * 86_400_000).toISOString().slice(0, 10);
|
|
@@ -72,33 +74,63 @@ function mkSession(partial: Partial<MinedSession> = {}): MinedSession {
|
|
|
72
74
|
};
|
|
73
75
|
}
|
|
74
76
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
}
|
|
77
|
+
let io: CapturingIO;
|
|
78
|
+
beforeEach(() => {
|
|
79
|
+
io = captureIO();
|
|
80
|
+
});
|
|
79
81
|
|
|
80
82
|
describe('update-user-model command', () => {
|
|
81
|
-
let consoleSpy: jest.SpyInstance;
|
|
82
83
|
|
|
83
84
|
beforeEach(() => {
|
|
84
85
|
jest.clearAllMocks();
|
|
85
86
|
fs.rmSync(TEST_HOME, { recursive: true, force: true });
|
|
86
87
|
fs.mkdirSync(path.join(TEST_HOME, '.claude', 'projects', '-root-projects', 'memory'), { recursive: true });
|
|
87
88
|
process.env.CLAUDE_TRANSCRIPTS_DIR = path.join(TEST_HOME, 'transcripts');
|
|
88
|
-
consoleSpy = jest.spyOn(console, 'log').mockImplementation();
|
|
89
89
|
});
|
|
90
90
|
|
|
91
91
|
afterEach(() => {
|
|
92
|
-
consoleSpy.mockRestore();
|
|
93
92
|
delete process.env.CLAUDE_TRANSCRIPTS_DIR;
|
|
93
|
+
delete process.env.HARNESS_UUM_STATE_FILE;
|
|
94
|
+
delete process.env.HARNESS_UUM_PROFILE_FILE;
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
// 路径解析(harness#116):默认值保持原行为,env 覆盖生效
|
|
98
|
+
test('resolveUserModelPaths 默认:homedir 下的原路径(现行为不变)', () => {
|
|
99
|
+
expect(resolveUserModelPaths({})).toEqual({
|
|
100
|
+
stateFile: STATE_FILE,
|
|
101
|
+
profileFile: PROFILE_FILE,
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
test('resolveUserModelPaths env 覆盖:HARNESS_UUM_STATE_FILE / HARNESS_UUM_PROFILE_FILE', () => {
|
|
106
|
+
expect(resolveUserModelPaths({
|
|
107
|
+
HARNESS_UUM_STATE_FILE: '/custom/state.json',
|
|
108
|
+
HARNESS_UUM_PROFILE_FILE: '/custom/profile.md',
|
|
109
|
+
})).toEqual({
|
|
110
|
+
stateFile: '/custom/state.json',
|
|
111
|
+
profileFile: '/custom/profile.md',
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
test('env 覆盖端到端:state 落 env 指定路径,不写默认路径', async () => {
|
|
116
|
+
const customState = path.join(TEST_HOME, 'custom', 'state.json');
|
|
117
|
+
process.env.HARNESS_UUM_STATE_FILE = customState;
|
|
118
|
+
mockReadTranscriptSessions.mockReturnValue([mkSession({ id: 'session-a' })]);
|
|
119
|
+
|
|
120
|
+
await updateUserModel({}, io);
|
|
121
|
+
|
|
122
|
+
expect(fs.existsSync(customState)).toBe(true);
|
|
123
|
+
expect(fs.existsSync(STATE_FILE)).toBe(false);
|
|
124
|
+
const state = JSON.parse(fs.readFileSync(customState, 'utf-8'));
|
|
125
|
+
expect(state.sessionsProcessed).toContain('session-a');
|
|
94
126
|
});
|
|
95
127
|
|
|
96
128
|
test('无新会话:提示 No new sessions to process', async () => {
|
|
97
129
|
mockReadTranscriptSessions.mockReturnValue([]);
|
|
98
130
|
|
|
99
|
-
await updateUserModel({});
|
|
131
|
+
await updateUserModel({}, io);
|
|
100
132
|
|
|
101
|
-
expect(
|
|
133
|
+
expect(io.outText()).toContain('No new sessions to process');
|
|
102
134
|
expect(fs.existsSync(STATE_FILE)).toBe(false);
|
|
103
135
|
});
|
|
104
136
|
|
|
@@ -108,9 +140,9 @@ describe('update-user-model command', () => {
|
|
|
108
140
|
mkSession({ id: 'session-b' }),
|
|
109
141
|
]);
|
|
110
142
|
|
|
111
|
-
await updateUserModel({ json: true, dryRun: true });
|
|
143
|
+
await updateUserModel({ json: true, dryRun: true }, io);
|
|
112
144
|
|
|
113
|
-
const output = lastJsonOutput(
|
|
145
|
+
const output = lastJsonOutput(io);
|
|
114
146
|
expect(output.newSessions).toBe(2);
|
|
115
147
|
expect(output.changes).toEqual(
|
|
116
148
|
expect.arrayContaining([
|
|
@@ -125,23 +157,37 @@ describe('update-user-model command', () => {
|
|
|
125
157
|
test('--dry-run:只展示变化,不写 state 文件', async () => {
|
|
126
158
|
mockReadTranscriptSessions.mockReturnValue([mkSession({ id: 'session-a' })]);
|
|
127
159
|
|
|
128
|
-
await updateUserModel({ dryRun: true });
|
|
160
|
+
await updateUserModel({ dryRun: true }, io);
|
|
129
161
|
|
|
130
|
-
expect(
|
|
162
|
+
expect(io.outText()).toContain('Processed 1 new sessions');
|
|
131
163
|
expect(fs.existsSync(STATE_FILE)).toBe(false);
|
|
132
164
|
});
|
|
133
165
|
|
|
134
166
|
test('默认(非 dry-run):落盘 state 并记录已处理会话', async () => {
|
|
135
167
|
mockReadTranscriptSessions.mockReturnValue([mkSession({ id: 'session-a' })]);
|
|
136
168
|
|
|
137
|
-
await updateUserModel({});
|
|
169
|
+
await updateUserModel({}, io);
|
|
138
170
|
|
|
139
171
|
expect(fs.existsSync(STATE_FILE)).toBe(true);
|
|
140
172
|
const state = JSON.parse(fs.readFileSync(STATE_FILE, 'utf-8'));
|
|
141
173
|
expect(state.sessionsProcessed).toContain('session-a');
|
|
142
174
|
});
|
|
143
175
|
|
|
144
|
-
test('--days 1
|
|
176
|
+
test('--days 1:since 过滤下推到 seam(自然日窗口,含今天)', async () => {
|
|
177
|
+
mockReadTranscriptSessions.mockReturnValue([mkSession({ id: 'today' })]);
|
|
178
|
+
|
|
179
|
+
await updateUserModel({ json: true, dryRun: true, days: 1 }, io);
|
|
180
|
+
|
|
181
|
+
const output = lastJsonOutput(io);
|
|
182
|
+
expect(output.newSessions).toBe(1);
|
|
183
|
+
// date >= 今日 ⟺ mtimeMs >= 今日 UTC 零点,过滤在 seam 内 stat 级完成
|
|
184
|
+
expect(mockReadTranscriptSessions).toHaveBeenCalledWith(
|
|
185
|
+
process.env.CLAUDE_TRANSCRIPTS_DIR,
|
|
186
|
+
{ excludeIds: [], since: Date.parse(todayStr()) },
|
|
187
|
+
);
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
test('缺省 --days:处理全部未处理会话(向后兼容)', async () => {
|
|
145
191
|
mockReadTranscriptSessions.mockReturnValue([
|
|
146
192
|
mkSession({ id: 'today' }),
|
|
147
193
|
mkSession({
|
|
@@ -154,32 +200,48 @@ describe('update-user-model command', () => {
|
|
|
154
200
|
}),
|
|
155
201
|
]);
|
|
156
202
|
|
|
157
|
-
await updateUserModel({ json: true, dryRun: true,
|
|
203
|
+
await updateUserModel({ json: true, dryRun: true }, io);
|
|
158
204
|
|
|
159
|
-
const output = lastJsonOutput(
|
|
160
|
-
expect(output.newSessions).toBe(
|
|
205
|
+
const output = lastJsonOutput(io);
|
|
206
|
+
expect(output.newSessions).toBe(2);
|
|
207
|
+
// 缺省 days:不下推 since(向后兼容全量)
|
|
208
|
+
expect(mockReadTranscriptSessions).toHaveBeenCalledWith(
|
|
209
|
+
process.env.CLAUDE_TRANSCRIPTS_DIR,
|
|
210
|
+
{ excludeIds: [] },
|
|
211
|
+
);
|
|
161
212
|
});
|
|
162
213
|
|
|
163
|
-
test('
|
|
214
|
+
test('同一 concept 只计一次:occurrences 等于 merged 聚合值(#113 双计 bug)', async () => {
|
|
215
|
+
mockReadTranscriptSessions.mockReturnValue([mkSession({ id: 'session-a' })]);
|
|
216
|
+
|
|
217
|
+
await updateUserModel({}, io);
|
|
218
|
+
|
|
219
|
+
const state = JSON.parse(fs.readFileSync(STATE_FILE, 'utf-8'));
|
|
220
|
+
// userText 整条中文序列恰为一个 concept(count=1);聚合后只应出现一次,
|
|
221
|
+
// 双计 bug 下会被 merged 循环 + per-session 循环各加一次变成 2
|
|
222
|
+
expect(state.patterns['数据库迁移方案需要执行'].occurrences).toBe(1);
|
|
223
|
+
expect(state.patterns['数据库迁移方案需要执行'].sessions).toEqual(['session-a']);
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
test('correction phrase 的 ×3 加权不受影响(buildMergedConcepts 内)', async () => {
|
|
164
227
|
mockReadTranscriptSessions.mockReturnValue([
|
|
165
|
-
mkSession({ id: 'today' }),
|
|
166
228
|
mkSession({
|
|
167
|
-
id: '
|
|
168
|
-
date: todayStr(3),
|
|
229
|
+
id: 'session-a',
|
|
169
230
|
turns: [
|
|
170
|
-
{ role: 'user', content: '
|
|
231
|
+
{ role: 'user', content: '这种问题反复出现' },
|
|
171
232
|
{ role: 'assistant', content: '' },
|
|
172
233
|
],
|
|
173
234
|
}),
|
|
174
235
|
]);
|
|
175
236
|
|
|
176
|
-
await updateUserModel({
|
|
237
|
+
await updateUserModel({}, io);
|
|
177
238
|
|
|
178
|
-
const
|
|
179
|
-
|
|
239
|
+
const state = JSON.parse(fs.readFileSync(STATE_FILE, 'utf-8'));
|
|
240
|
+
// 纠正语句提取的 concept 按 ×3 计入(裁决:权重在 buildMergedConcepts,删循环不影响)
|
|
241
|
+
expect(state.patterns['这种问题'].occurrences).toBe(3);
|
|
180
242
|
});
|
|
181
243
|
|
|
182
|
-
test('sessionsProcessed
|
|
244
|
+
test('sessionsProcessed 去重:已处理会话经 excludeIds 下推排除', async () => {
|
|
183
245
|
fs.writeFileSync(
|
|
184
246
|
STATE_FILE,
|
|
185
247
|
JSON.stringify({
|
|
@@ -192,14 +254,15 @@ describe('update-user-model command', () => {
|
|
|
192
254
|
}),
|
|
193
255
|
'utf-8',
|
|
194
256
|
);
|
|
195
|
-
mockReadTranscriptSessions.mockReturnValue([
|
|
196
|
-
mkSession({ id: 'session-a' }),
|
|
197
|
-
mkSession({ id: 'session-b' }),
|
|
198
|
-
]);
|
|
257
|
+
mockReadTranscriptSessions.mockReturnValue([mkSession({ id: 'session-b' })]);
|
|
199
258
|
|
|
200
|
-
await updateUserModel({ json: true, dryRun: true, days: 7 });
|
|
259
|
+
await updateUserModel({ json: true, dryRun: true, days: 7 }, io);
|
|
201
260
|
|
|
202
|
-
const output = lastJsonOutput(
|
|
261
|
+
const output = lastJsonOutput(io);
|
|
203
262
|
expect(output.newSessions).toBe(1);
|
|
263
|
+
expect(mockReadTranscriptSessions).toHaveBeenCalledWith(
|
|
264
|
+
process.env.CLAUDE_TRANSCRIPTS_DIR,
|
|
265
|
+
{ excludeIds: ['session-a'], since: expect.any(Number) },
|
|
266
|
+
);
|
|
204
267
|
});
|
|
205
268
|
});
|