@dommaker/harness 1.2.3 → 1.4.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 +15 -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 +76 -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 +30 -215
- 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 +33 -26
- 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 +21 -15
- package/dist/cli/commands/knowledge.d.ts.map +1 -1
- package/dist/cli/commands/knowledge.js +237 -199
- 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 +73 -77
- 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 +31 -16
- 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 +2 -1
- package/dist/cli/commands/update-user-model.d.ts.map +1 -1
- package/dist/cli/commands/update-user-model.js +31 -45
- 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/completion-checkers/index.d.ts +3 -3
- package/dist/completion-checkers/index.js +3 -3
- package/dist/completion-checkers/phase-format.d.ts +1 -1
- package/dist/completion-checkers/phase-format.js +2 -2
- package/dist/completion-checkers/phase-format.js.map +1 -1
- package/dist/completion-checkers/tdd-chain.d.ts +1 -1
- package/dist/completion-checkers/tdd-chain.js +2 -2
- package/dist/completion-checkers/tdd-chain.js.map +1 -1
- package/dist/completion-checkers/types.d.ts +5 -5
- package/dist/completion-checkers/types.d.ts.map +1 -1
- package/dist/completion-checkers/types.js +2 -2
- 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 +64 -115
- package/dist/core/constraints/checker.js.map +1 -1
- package/dist/core/constraints/checkers/capability-sync.d.ts +4 -3
- package/dist/core/constraints/checkers/capability-sync.d.ts.map +1 -1
- package/dist/core/constraints/checkers/capability-sync.js +29 -69
- 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 +37 -63
- 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/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 +2 -2
- 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 +7 -1
- package/dist/failure/recorder.d.ts.map +1 -1
- package/dist/failure/recorder.js +13 -9
- 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.map +1 -1
- package/dist/gates/checker-gate.js +6 -11
- 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 +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +13 -4
- 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 +18 -15
- 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 +3 -0
- package/dist/release/index.d.ts.map +1 -0
- package/dist/release/index.js +7 -0
- package/dist/release/index.js.map +1 -0
- package/dist/release/integrity.d.ts +65 -0
- package/dist/release/integrity.d.ts.map +1 -0
- package/dist/release/integrity.js +151 -0
- package/dist/release/integrity.js.map +1 -0
- 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 +0 -18
- package/dist/types/constraint.d.ts.map +1 -1
- package/dist/types/constraint.js.map +1 -1
- package/dist/types/failure.d.ts +7 -0
- package/dist/types/failure.d.ts.map +1 -1
- package/dist/types/failure.js +8 -1
- package/dist/types/failure.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/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 +7 -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__/failure-recorder.test.ts +9 -0
- 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 +142 -106
- package/src/__tests__/public-exports.test.ts +3 -0
- 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 +22 -7
- 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 +101 -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 +378 -382
- 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 +96 -38
- 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 +169 -0
- package/src/cli/commands/__tests__/registry.test.ts +69 -35
- package/src/cli/commands/__tests__/release.test.ts +224 -74
- 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 +108 -86
- package/src/cli/commands/__tests__/sync-docs.test.ts +72 -74
- package/src/cli/commands/__tests__/update-user-model.test.ts +62 -33
- 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 +88 -65
- 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 +37 -223
- package/src/cli/commands/failure.ts +35 -27
- package/src/cli/commands/init.ts +166 -220
- package/src/cli/commands/knowledge.ts +250 -203
- 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 +75 -78
- 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 +31 -15
- 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 +33 -45
- 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/completion-checkers/CONTEXT.md +1 -1
- package/src/completion-checkers/__tests__/contract-presence.test.ts +1 -1
- package/src/completion-checkers/__tests__/phase-format.test.ts +1 -1
- package/src/completion-checkers/__tests__/tdd-chain.test.ts +2 -2
- package/src/completion-checkers/index.ts +3 -3
- package/src/completion-checkers/phase-format.ts +2 -2
- package/src/completion-checkers/tdd-chain.ts +2 -2
- package/src/completion-checkers/types.ts +5 -5
- package/src/context/session-manager.ts +19 -33
- package/src/core/CONTEXT.md +9 -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 +75 -130
- package/src/core/constraints/checkers/__tests__/capability-sync.test.ts +214 -0
- package/src/core/constraints/checkers/__tests__/context-doc-sync.test.ts +77 -0
- package/src/core/constraints/checkers/__tests__/docs-freshness.test.ts +117 -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 +145 -0
- package/src/core/constraints/checkers/capability-sync.ts +29 -88
- package/src/core/constraints/checkers/context-doc-sync.ts +15 -25
- package/src/core/constraints/checkers/docs-freshness.ts +40 -69
- package/src/core/constraints/checkers/governance-presence.ts +7 -25
- package/src/core/constraints/checkers/no-test-simplification.ts +4 -3
- package/src/core/constraints/checkers/types.ts +2 -2
- 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 +13 -11
- package/src/gates/CONTEXT.md +6 -3
- 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__/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 +8 -7
- 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 +11 -6
- 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 +55 -2
- 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 +20 -15
- 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 +24 -0
- package/src/release/__tests__/barrel.test.ts +60 -0
- package/src/release/__tests__/integrity.test.ts +157 -0
- package/src/release/index.ts +2 -0
- package/src/release/integrity.ts +141 -0
- 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 +0 -25
- package/src/types/failure.ts +8 -0
- 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/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
|
@@ -12,10 +12,13 @@ import { FileKnowledgeStore as KnowledgeStore } from '../../knowledge/store';
|
|
|
12
12
|
import { KnowledgeQuery } from '../../knowledge/query';
|
|
13
13
|
import { KnowledgeLifecycle } from '../../knowledge/lifecycle';
|
|
14
14
|
import { ColdStartImporter } from '../../knowledge/import';
|
|
15
|
-
import { KnowledgeAudit } from '../../knowledge/audit';
|
|
15
|
+
import { KnowledgeAudit, AUDIT_RULE_LABELS } from '../../knowledge/audit';
|
|
16
|
+
import type { AuditReport, AuditRuleName } from '../../knowledge/audit';
|
|
17
|
+
import { evaluateFlywheel } from '../../knowledge/flywheel-metrics';
|
|
16
18
|
import { migrateKnowledgeEntries } from '../../knowledge/migration';
|
|
17
19
|
import { KnowledgeIndexGenerator } from '../../knowledge/index-generator';
|
|
18
20
|
import type { KnowledgeSubsystem, MaturityLevel, QueryFilter } from '../../knowledge/types';
|
|
21
|
+
import { log, logError, processIO, type CommandIO, type CommandResult } from '../command-contract';
|
|
19
22
|
|
|
20
23
|
export interface KnowledgeOptions {
|
|
21
24
|
/** 项目路径 */
|
|
@@ -31,8 +34,8 @@ export async function knowledgeList(options: KnowledgeOptions & {
|
|
|
31
34
|
type?: string;
|
|
32
35
|
maturity?: string;
|
|
33
36
|
tag?: string;
|
|
34
|
-
}): Promise<
|
|
35
|
-
const store = new KnowledgeStore({ baseDir: getKnowledgeDir(options.projectPath) });
|
|
37
|
+
}, io: CommandIO = processIO): Promise<CommandResult> {
|
|
38
|
+
const store = new KnowledgeStore({ baseDir: getKnowledgeDir(options.projectPath, io) });
|
|
36
39
|
|
|
37
40
|
const filter: QueryFilter = { excludeArchived: false };
|
|
38
41
|
if (options.type) {
|
|
@@ -48,27 +51,28 @@ export async function knowledgeList(options: KnowledgeOptions & {
|
|
|
48
51
|
const entries = store.list(filter);
|
|
49
52
|
|
|
50
53
|
if (options.json) {
|
|
51
|
-
|
|
52
|
-
return;
|
|
54
|
+
log(io, JSON.stringify({ total: entries.length, entries }, null, 2));
|
|
55
|
+
return { kind: 'ok' };
|
|
53
56
|
}
|
|
54
57
|
|
|
55
58
|
if (entries.length === 0) {
|
|
56
|
-
|
|
57
|
-
return;
|
|
59
|
+
log(io, chalk.yellow('知识库为空'));
|
|
60
|
+
return { kind: 'ok' };
|
|
58
61
|
}
|
|
59
62
|
|
|
60
|
-
|
|
63
|
+
log(io, chalk.blue(`📚 知识库 (${entries.length} 条)\n`));
|
|
61
64
|
for (const entry of entries) {
|
|
62
65
|
const maturityColor = entry.maturity === 'proven' ? chalk.green
|
|
63
66
|
: entry.maturity === 'verified' ? chalk.cyan
|
|
64
67
|
: entry.maturity === 'archived' ? chalk.gray
|
|
65
68
|
: chalk.yellow;
|
|
66
|
-
|
|
67
|
-
|
|
69
|
+
log(io, ` ${maturityColor(`[${entry.maturity}]`)} ${chalk.bold(entry.title)}`);
|
|
70
|
+
log(io, ` ${chalk.gray(`id: ${entry.id} | type: ${entry.type} | layer: ${entry.layer}`)}`);
|
|
68
71
|
if (entry.tags.length > 0) {
|
|
69
|
-
|
|
72
|
+
log(io, ` ${chalk.gray(`tags: ${entry.tags.join(', ')}`)}`);
|
|
70
73
|
}
|
|
71
74
|
}
|
|
75
|
+
return { kind: 'ok' };
|
|
72
76
|
}
|
|
73
77
|
|
|
74
78
|
/**
|
|
@@ -77,28 +81,51 @@ export async function knowledgeList(options: KnowledgeOptions & {
|
|
|
77
81
|
export async function knowledgeSearch(
|
|
78
82
|
query: string,
|
|
79
83
|
options: KnowledgeOptions & { limit?: number },
|
|
80
|
-
|
|
81
|
-
|
|
84
|
+
io: CommandIO = processIO,
|
|
85
|
+
): Promise<CommandResult> {
|
|
86
|
+
const store = new KnowledgeStore({ baseDir: getKnowledgeDir(options.projectPath, io) });
|
|
82
87
|
const queryEngine = new KnowledgeQuery(store);
|
|
83
88
|
|
|
84
89
|
const matched = queryEngine.search(query, { limit: options.limit || 20 });
|
|
85
90
|
|
|
86
91
|
if (options.json) {
|
|
87
|
-
|
|
88
|
-
return;
|
|
92
|
+
log(io, JSON.stringify({ query, total: matched.length, entries: matched }, null, 2));
|
|
93
|
+
return { kind: 'ok' };
|
|
89
94
|
}
|
|
90
95
|
|
|
91
96
|
if (matched.length === 0) {
|
|
92
|
-
|
|
93
|
-
return;
|
|
97
|
+
log(io, chalk.yellow(`未找到匹配 "${query}" 的知识条目`));
|
|
98
|
+
return { kind: 'ok' };
|
|
94
99
|
}
|
|
95
100
|
|
|
96
|
-
|
|
101
|
+
log(io, chalk.blue(`🔍 搜索 "${query}" (${matched.length} 条结果)\n`));
|
|
97
102
|
for (const entry of matched) {
|
|
98
|
-
|
|
103
|
+
log(io, ` ${chalk.bold(entry.title)} ${chalk.gray(`[${entry.maturity}]`)}`);
|
|
99
104
|
const preview = entry.content.slice(0, 100).replace(/\n/g, ' ');
|
|
100
|
-
|
|
105
|
+
log(io, ` ${chalk.gray(preview)}${entry.content.length > 100 ? '...' : ''}`);
|
|
101
106
|
}
|
|
107
|
+
return { kind: 'ok' };
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* knowledge search 子命令入口(候选7):缺参闸门 + limit 强转编组,
|
|
112
|
+
* 自 definitions.ts 的 args 复印块移回命令模块(interface/测试面所在)。
|
|
113
|
+
*/
|
|
114
|
+
export async function knowledgeSearchCommand(
|
|
115
|
+
positionals: (string | undefined)[],
|
|
116
|
+
options: Record<string, unknown>,
|
|
117
|
+
io: CommandIO = processIO,
|
|
118
|
+
): Promise<CommandResult> {
|
|
119
|
+
const query = positionals[0];
|
|
120
|
+
if (!query) {
|
|
121
|
+
logError(io, '请提供搜索关键词');
|
|
122
|
+
return { kind: 'usage-error', reason: 'knowledge search 缺少关键词位置参数' };
|
|
123
|
+
}
|
|
124
|
+
return knowledgeSearch(String(query), {
|
|
125
|
+
projectPath: options.projectPath as string | undefined,
|
|
126
|
+
json: options.json as boolean | undefined,
|
|
127
|
+
limit: parseInt(String(options.limit), 10),
|
|
128
|
+
});
|
|
102
129
|
}
|
|
103
130
|
|
|
104
131
|
/**
|
|
@@ -106,9 +133,10 @@ export async function knowledgeSearch(
|
|
|
106
133
|
*/
|
|
107
134
|
export async function knowledgeImport(
|
|
108
135
|
options: KnowledgeOptions & { sources?: string; reset?: boolean },
|
|
109
|
-
|
|
136
|
+
io: CommandIO = processIO,
|
|
137
|
+
): Promise<CommandResult> {
|
|
110
138
|
const projectPath = options.projectPath || process.cwd();
|
|
111
|
-
const store = new KnowledgeStore({ baseDir: getKnowledgeDir(options.projectPath) });
|
|
139
|
+
const store = new KnowledgeStore({ baseDir: getKnowledgeDir(options.projectPath, io) });
|
|
112
140
|
|
|
113
141
|
const sources = options.sources
|
|
114
142
|
? options.sources.split(',') as Array<'code' | 'git' | 'docs' | 'manual'>
|
|
@@ -123,12 +151,12 @@ export async function knowledgeImport(
|
|
|
123
151
|
if (options.reset) {
|
|
124
152
|
importer.resetState();
|
|
125
153
|
if (!options.json) {
|
|
126
|
-
|
|
154
|
+
log(io, chalk.yellow('🔄 已重置导入状态'));
|
|
127
155
|
}
|
|
128
156
|
}
|
|
129
157
|
|
|
130
158
|
if (!options.json) {
|
|
131
|
-
|
|
159
|
+
log(io, chalk.blue(`📥 开始导入知识 (源: ${sources.join(', ')})...`));
|
|
132
160
|
}
|
|
133
161
|
|
|
134
162
|
const results = await importer.importAll();
|
|
@@ -137,59 +165,61 @@ export async function knowledgeImport(
|
|
|
137
165
|
const totalErrors = results.reduce((sum, r) => sum + r.errors.length, 0);
|
|
138
166
|
|
|
139
167
|
if (options.json) {
|
|
140
|
-
|
|
141
|
-
return;
|
|
168
|
+
log(io, JSON.stringify({ totalImported, totalErrors, results }, null, 2));
|
|
169
|
+
return { kind: 'ok' };
|
|
142
170
|
}
|
|
143
171
|
|
|
144
172
|
for (const result of results) {
|
|
145
173
|
if (result.entries.length > 0) {
|
|
146
|
-
|
|
174
|
+
log(io, chalk.green(` ✅ ${result.source.type}: ${result.entries.length} 条`));
|
|
147
175
|
}
|
|
148
176
|
if (result.errors.length > 0) {
|
|
149
177
|
for (const err of result.errors) {
|
|
150
|
-
|
|
178
|
+
log(io, chalk.red(` ❌ ${result.source.type}: ${err.message}`));
|
|
151
179
|
}
|
|
152
180
|
}
|
|
153
181
|
}
|
|
154
182
|
|
|
155
|
-
|
|
183
|
+
log(io, chalk.green(`\n✅ 导入完成: ${totalImported} 条,${totalErrors} 个错误`));
|
|
184
|
+
return { kind: 'ok' };
|
|
156
185
|
}
|
|
157
186
|
|
|
158
187
|
/**
|
|
159
188
|
* 知识库衰减周期
|
|
160
189
|
*/
|
|
161
|
-
export async function knowledgeDecay(options: KnowledgeOptions): Promise<
|
|
162
|
-
const store = new KnowledgeStore({ baseDir: getKnowledgeDir(options.projectPath) });
|
|
190
|
+
export async function knowledgeDecay(options: KnowledgeOptions, io: CommandIO = processIO): Promise<CommandResult> {
|
|
191
|
+
const store = new KnowledgeStore({ baseDir: getKnowledgeDir(options.projectPath, io) });
|
|
163
192
|
const lifecycle = new KnowledgeLifecycle(store);
|
|
164
193
|
|
|
165
194
|
if (!options.json) {
|
|
166
|
-
|
|
195
|
+
log(io, chalk.blue('🔄 运行衰减周期...'));
|
|
167
196
|
}
|
|
168
197
|
|
|
169
198
|
const changes = lifecycle.runDecayCycle();
|
|
170
199
|
|
|
171
200
|
if (options.json) {
|
|
172
|
-
|
|
173
|
-
return;
|
|
201
|
+
log(io, JSON.stringify({ changes }, null, 2));
|
|
202
|
+
return { kind: 'ok' };
|
|
174
203
|
}
|
|
175
204
|
|
|
176
205
|
if (changes.length === 0) {
|
|
177
|
-
|
|
178
|
-
return;
|
|
206
|
+
log(io, chalk.green('✅ 没有需要衰减的知识条目'));
|
|
207
|
+
return { kind: 'ok' };
|
|
179
208
|
}
|
|
180
209
|
|
|
181
|
-
|
|
210
|
+
log(io, chalk.yellow(`📉 ${changes.length} 条知识发生衰减:\n`));
|
|
182
211
|
for (const change of changes) {
|
|
183
|
-
|
|
184
|
-
|
|
212
|
+
log(io, ` ${change.entryId}: ${chalk.red(change.from)} → ${chalk.green(change.to)}`);
|
|
213
|
+
log(io, ` ${chalk.gray(change.reason)}`);
|
|
185
214
|
}
|
|
215
|
+
return { kind: 'ok' };
|
|
186
216
|
}
|
|
187
217
|
|
|
188
218
|
/**
|
|
189
219
|
* 知识库统计
|
|
190
220
|
*/
|
|
191
|
-
export async function knowledgeStats(options: KnowledgeOptions): Promise<
|
|
192
|
-
const store = new KnowledgeStore({ baseDir: getKnowledgeDir(options.projectPath) });
|
|
221
|
+
export async function knowledgeStats(options: KnowledgeOptions, io: CommandIO = processIO): Promise<CommandResult> {
|
|
222
|
+
const store = new KnowledgeStore({ baseDir: getKnowledgeDir(options.projectPath, io) });
|
|
193
223
|
const entries = store.list({ excludeArchived: false });
|
|
194
224
|
const active = entries.filter(e => e.maturity !== 'archived');
|
|
195
225
|
|
|
@@ -203,57 +233,57 @@ export async function knowledgeStats(options: KnowledgeOptions): Promise<void> {
|
|
|
203
233
|
byLayer[entry.layer] = (byLayer[entry.layer] || 0) + 1;
|
|
204
234
|
}
|
|
205
235
|
|
|
206
|
-
//
|
|
207
|
-
|
|
208
|
-
const refCoverage = active.length > 0 ? Math.round(withRefs / active.length * 100) : 0;
|
|
209
|
-
const avgRefs = active.length > 0
|
|
210
|
-
? Math.round(active.reduce((sum, e) => sum + e.referencedBy.length, 0) / active.length * 10) / 10
|
|
211
|
-
: 0;
|
|
212
|
-
let consumptionHitRate = 0;
|
|
236
|
+
// 飞轮指标:计算唯一实现在 knowledge/flywheel-metrics,此处只做 fs 读取 + 展示层单位映射
|
|
237
|
+
let dailyConsumptionEvents = 0;
|
|
213
238
|
try {
|
|
214
239
|
const statsPath = path.join(store.getBaseDir(), '.consumption-stats.json');
|
|
215
240
|
if (fs.existsSync(statsPath)) {
|
|
216
241
|
const stats = JSON.parse(fs.readFileSync(statsPath, 'utf-8'));
|
|
217
|
-
|
|
218
|
-
consumptionHitRate = active.length > 0 ? Math.min(Math.round(dailyEvents / active.length * 100), 100) : 0;
|
|
242
|
+
dailyConsumptionEvents = stats.dailyEvents || 0;
|
|
219
243
|
}
|
|
220
244
|
} catch { /* best-effort */ }
|
|
221
245
|
|
|
246
|
+
const metrics = evaluateFlywheel({ entries: active, dailyConsumptionEvents });
|
|
247
|
+
const refCoverage = Math.round(metrics.refCoverage * 100);
|
|
248
|
+
const avgRefs = Math.round(metrics.avgRefs * 10) / 10;
|
|
249
|
+
const consumptionHitRate = Math.round(metrics.consumptionHitRate * 100);
|
|
250
|
+
|
|
222
251
|
const flywheel = { refCoverage, avgRefs, consumptionHitRate };
|
|
223
252
|
|
|
224
253
|
if (options.json) {
|
|
225
|
-
|
|
226
|
-
return;
|
|
254
|
+
log(io, JSON.stringify({ total: entries.length, byType, byMaturity, byLayer, flywheel }, null, 2));
|
|
255
|
+
return { kind: 'ok' };
|
|
227
256
|
}
|
|
228
257
|
|
|
229
|
-
|
|
230
|
-
|
|
258
|
+
log(io, chalk.blue(`📊 知识库统计\n`));
|
|
259
|
+
log(io, chalk.bold(` 总计: ${entries.length} 条 (活跃: ${active.length})\n`));
|
|
231
260
|
|
|
232
|
-
|
|
261
|
+
log(io, chalk.bold(' 按类型:'));
|
|
233
262
|
for (const [type, count] of Object.entries(byType)) {
|
|
234
|
-
|
|
263
|
+
log(io, ` ${type}: ${count}`);
|
|
235
264
|
}
|
|
236
265
|
|
|
237
|
-
|
|
266
|
+
log(io, chalk.bold('\n 按成熟度:'));
|
|
238
267
|
for (const [maturity, count] of Object.entries(byMaturity)) {
|
|
239
268
|
const color = maturity === 'proven' ? chalk.green : maturity === 'archived' ? chalk.gray : chalk.yellow;
|
|
240
|
-
|
|
269
|
+
log(io, ` ${color(maturity)}: ${count}`);
|
|
241
270
|
}
|
|
242
271
|
|
|
243
|
-
|
|
272
|
+
log(io, chalk.bold('\n 按层级:'));
|
|
244
273
|
for (const [layer, count] of Object.entries(byLayer)) {
|
|
245
|
-
|
|
274
|
+
log(io, ` ${layer}: ${count}`);
|
|
246
275
|
}
|
|
247
276
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
277
|
+
log(io, chalk.bold('\n 飞轮指标:'));
|
|
278
|
+
log(io, ` 引用覆盖: ${refCoverage}%`);
|
|
279
|
+
log(io, ` 平均引用: ${avgRefs}`);
|
|
280
|
+
log(io, ` 消费命中率: ${consumptionHitRate}%`);
|
|
281
|
+
return { kind: 'ok' };
|
|
252
282
|
}
|
|
253
283
|
|
|
254
284
|
export interface KnowledgeUpsertOptions {
|
|
255
|
-
scope
|
|
256
|
-
title
|
|
285
|
+
scope?: string;
|
|
286
|
+
title?: string;
|
|
257
287
|
content?: string;
|
|
258
288
|
file?: string;
|
|
259
289
|
type?: string;
|
|
@@ -265,7 +295,7 @@ export interface KnowledgeUpsertOptions {
|
|
|
265
295
|
*
|
|
266
296
|
* 调用 Studio API POST /api/knowledge/upsert(内部端点,无 auth)
|
|
267
297
|
*/
|
|
268
|
-
export async function knowledgeUpsert(options: KnowledgeUpsertOptions): Promise<
|
|
298
|
+
export async function knowledgeUpsert(options: KnowledgeUpsertOptions, io: CommandIO = processIO): Promise<CommandResult> {
|
|
269
299
|
const apiPort = process.env.API_PORT || '3001';
|
|
270
300
|
const url = `http://localhost:${apiPort}/api/knowledge/upsert`;
|
|
271
301
|
|
|
@@ -275,15 +305,15 @@ export async function knowledgeUpsert(options: KnowledgeUpsertOptions): Promise<
|
|
|
275
305
|
try {
|
|
276
306
|
content = fs.readFileSync(options.file, 'utf-8');
|
|
277
307
|
} catch (e: any) {
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
308
|
+
logError(io, chalk.red(`Failed to read file: ${options.file}`));
|
|
309
|
+
logError(io, chalk.red(String(e)));
|
|
310
|
+
return { kind: 'fail', reason: `无法读取 --file ${options.file}: ${String(e)}` };
|
|
281
311
|
}
|
|
282
312
|
}
|
|
283
313
|
|
|
284
314
|
if (!options.scope || !options.title || !content) {
|
|
285
|
-
|
|
286
|
-
|
|
315
|
+
logError(io, chalk.red('--scope, --title, and --content (or --file) are required'));
|
|
316
|
+
return { kind: 'usage-error', reason: '--scope/--title/--content(--file) 缺一不可' };
|
|
287
317
|
}
|
|
288
318
|
|
|
289
319
|
try {
|
|
@@ -301,21 +331,22 @@ export async function knowledgeUpsert(options: KnowledgeUpsertOptions): Promise<
|
|
|
301
331
|
|
|
302
332
|
if (!res.ok) {
|
|
303
333
|
const err: any = await res.json().catch(() => ({ error: res.statusText }));
|
|
304
|
-
|
|
305
|
-
|
|
334
|
+
logError(io, chalk.red(`API error ${res.status}: ${err.error || res.statusText}`));
|
|
335
|
+
return { kind: 'fail', reason: `upsert API error ${res.status}: ${err.error || res.statusText}` };
|
|
306
336
|
}
|
|
307
337
|
|
|
308
338
|
const result: any = await res.json();
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
339
|
+
log(io, chalk.green(`✅ Knowledge upserted`));
|
|
340
|
+
log(io, ` KnowledgeStore: ${result.knowledgeStore?.action} → ${result.knowledgeStore?.entryId}`);
|
|
341
|
+
log(io, ` Studio UI: ${result.prismaDocument?.action} → ${result.prismaDocument?.docId || 'skipped'}`);
|
|
342
|
+
return { kind: 'ok' };
|
|
312
343
|
} catch (e: any) {
|
|
313
344
|
if (e?.code === 'ECONNREFUSED') {
|
|
314
|
-
|
|
345
|
+
logError(io, chalk.red(`Cannot reach Studio API at ${url}. Is the API running?`));
|
|
315
346
|
} else {
|
|
316
|
-
|
|
347
|
+
logError(io, chalk.red(`Upsert failed: ${e.message}`));
|
|
317
348
|
}
|
|
318
|
-
|
|
349
|
+
return { kind: 'fail', reason: `knowledge upsert 失败: ${e?.code === 'ECONNREFUSED' ? `Studio API 不可达 ${url}` : e.message}` };
|
|
319
350
|
}
|
|
320
351
|
}
|
|
321
352
|
|
|
@@ -323,38 +354,39 @@ export async function knowledgeUpsert(options: KnowledgeUpsertOptions): Promise<
|
|
|
323
354
|
/**
|
|
324
355
|
* RAG 同步:扫描 .harness/knowledge-docs/ 输出需要 ingest 的文件列表
|
|
325
356
|
*/
|
|
326
|
-
export async function knowledgeSyncRag(options: KnowledgeOptions): Promise<
|
|
357
|
+
export async function knowledgeSyncRag(options: KnowledgeOptions, io: CommandIO = processIO): Promise<CommandResult> {
|
|
327
358
|
const projectPath = options.projectPath || process.cwd();
|
|
328
359
|
const docsDir = `${projectPath}/.harness/knowledge-docs`;
|
|
329
360
|
|
|
330
361
|
if (!fs.existsSync(docsDir)) {
|
|
331
|
-
|
|
332
|
-
return;
|
|
362
|
+
log(io, chalk.yellow('No .harness/knowledge-docs/ directory'));
|
|
363
|
+
return { kind: 'ok' };
|
|
333
364
|
}
|
|
334
365
|
|
|
335
366
|
const files = fs.readdirSync(docsDir).filter(f => f.endsWith('.md'));
|
|
336
367
|
if (files.length === 0) {
|
|
337
|
-
|
|
338
|
-
return;
|
|
368
|
+
log(io, chalk.gray('No knowledge docs found'));
|
|
369
|
+
return { kind: 'ok' };
|
|
339
370
|
}
|
|
340
371
|
|
|
341
372
|
if (options.json) {
|
|
342
|
-
|
|
343
|
-
return;
|
|
373
|
+
log(io, JSON.stringify({ directory: docsDir, files }, null, 2));
|
|
374
|
+
return { kind: 'ok' };
|
|
344
375
|
}
|
|
345
376
|
|
|
346
|
-
|
|
377
|
+
log(io, chalk.blue(`📄 RAG sync candidates: ${files.length} files in ${docsDir}\n`));
|
|
347
378
|
for (const file of files) {
|
|
348
|
-
|
|
379
|
+
log(io, chalk.cyan(` ${file}`));
|
|
349
380
|
}
|
|
350
|
-
|
|
351
|
-
|
|
381
|
+
log(io);
|
|
382
|
+
log(io, chalk.gray('Run `mcp__local-rag__ingest_file` for each to sync to RAG'));
|
|
383
|
+
return { kind: 'ok' };
|
|
352
384
|
}
|
|
353
385
|
|
|
354
386
|
/**
|
|
355
387
|
* 知识同步状态:检测所有 tracked scope 的新鲜度
|
|
356
388
|
*/
|
|
357
|
-
export async function knowledgeSyncStatus(options: KnowledgeOptions): Promise<
|
|
389
|
+
export async function knowledgeSyncStatus(options: KnowledgeOptions, io: CommandIO = processIO): Promise<CommandResult> {
|
|
358
390
|
const apiPort = process.env.API_PORT || '3001';
|
|
359
391
|
const url = `http://localhost:${apiPort}/api/knowledge/sync-status`;
|
|
360
392
|
|
|
@@ -362,48 +394,49 @@ export async function knowledgeSyncStatus(options: KnowledgeOptions): Promise<vo
|
|
|
362
394
|
const res = await fetch(url);
|
|
363
395
|
if (!res.ok) {
|
|
364
396
|
const err: any = await res.json().catch(() => ({ error: res.statusText }));
|
|
365
|
-
|
|
366
|
-
|
|
397
|
+
logError(io, chalk.red(`API error ${res.status}: ${err.error || res.statusText}`));
|
|
398
|
+
return { kind: 'fail', reason: `sync-status API error ${res.status}: ${err.error || res.statusText}` };
|
|
367
399
|
}
|
|
368
400
|
|
|
369
401
|
const data: any = await res.json();
|
|
370
402
|
|
|
371
403
|
if (options.json) {
|
|
372
|
-
|
|
373
|
-
return;
|
|
404
|
+
log(io, JSON.stringify(data, null, 2));
|
|
405
|
+
return { kind: 'ok' };
|
|
374
406
|
}
|
|
375
407
|
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
408
|
+
log(io, chalk.blue(`🔄 KnowledgeSync Status\n`));
|
|
409
|
+
log(io, chalk.bold(`Tracked scopes: ${data.trackedScopes?.join(', ') || 'none'}`));
|
|
410
|
+
log(io, chalk.bold(`Stale entries: ${data.stale?.length || 0}`));
|
|
379
411
|
|
|
380
412
|
if (data.stale?.length > 0) {
|
|
381
|
-
|
|
413
|
+
log(io, chalk.yellow(`\n⚠️ Stale knowledge:\n`));
|
|
382
414
|
for (const s of data.stale) {
|
|
383
|
-
|
|
415
|
+
log(io, chalk.yellow(` ${s.scope} (${s.title}): ${s.stalenessHours}h old, files changed: ${s.changedFiles.join(', ')}`));
|
|
384
416
|
}
|
|
385
417
|
} else {
|
|
386
|
-
|
|
418
|
+
log(io, chalk.green('\n✅ All knowledge fresh'));
|
|
387
419
|
}
|
|
388
420
|
|
|
389
421
|
if (data.healed?.length > 0) {
|
|
390
|
-
|
|
422
|
+
log(io, chalk.cyan(`\n🩹 Auto-healed: ${data.healed.join(', ')}`));
|
|
391
423
|
}
|
|
392
424
|
} catch (e: any) {
|
|
393
425
|
if (e?.code === 'ECONNREFUSED') {
|
|
394
|
-
|
|
426
|
+
logError(io, chalk.red(`Cannot reach Studio API at ${url}`));
|
|
395
427
|
} else {
|
|
396
|
-
|
|
428
|
+
logError(io, chalk.red(`Sync check failed: ${e.message}`));
|
|
397
429
|
}
|
|
398
|
-
|
|
430
|
+
return { kind: 'fail', reason: `knowledge sync status 检查失败: ${e?.code === 'ECONNREFUSED' ? `Studio API 不可达 ${url}` : e.message}` };
|
|
399
431
|
}
|
|
432
|
+
return { kind: 'ok' };
|
|
400
433
|
}
|
|
401
434
|
|
|
402
435
|
/**
|
|
403
436
|
* 飞轮健康检查 — 零 token 检测知识飞轮数据流状态
|
|
404
437
|
*/
|
|
405
|
-
export async function knowledgeHealth(options: KnowledgeOptions & { dir?: string }): Promise<
|
|
406
|
-
const baseDir = options.dir || getKnowledgeDir(options.projectPath);
|
|
438
|
+
export async function knowledgeHealth(options: KnowledgeOptions & { dir?: string }, io: CommandIO = processIO): Promise<CommandResult> {
|
|
439
|
+
const baseDir = options.dir || getKnowledgeDir(options.projectPath, io);
|
|
407
440
|
const store = new KnowledgeStore({ baseDir });
|
|
408
441
|
const entries = store.list({ excludeArchived: true });
|
|
409
442
|
|
|
@@ -439,12 +472,10 @@ export async function knowledgeHealth(options: KnowledgeOptions & { dir?: string
|
|
|
439
472
|
issues.push({ severity: 'info', entry: '-', detail: `消费追踪数据不存在(${statsPath})` });
|
|
440
473
|
}
|
|
441
474
|
|
|
442
|
-
// D4:
|
|
443
|
-
const
|
|
444
|
-
const refCoverage =
|
|
445
|
-
const avgRefs =
|
|
446
|
-
? Math.round(entries.reduce((sum, e) => sum + e.referencedBy.length, 0) / entries.length * 10) / 10
|
|
447
|
-
: 0;
|
|
475
|
+
// D4: 飞轮指标(计算唯一实现在 knowledge/flywheel-metrics;人口为本命令的 excludeArchived 切片)
|
|
476
|
+
const metrics = evaluateFlywheel({ entries });
|
|
477
|
+
const refCoverage = Math.round(metrics.refCoverage * 100);
|
|
478
|
+
const avgRefs = Math.round(metrics.avgRefs * 10) / 10;
|
|
448
479
|
|
|
449
480
|
// 健康分计算
|
|
450
481
|
const totalIssues = issues.filter(i => i.severity === 'error').length * 3
|
|
@@ -452,7 +483,7 @@ export async function knowledgeHealth(options: KnowledgeOptions & { dir?: string
|
|
|
452
483
|
const healthScore = Math.max(0, 100 - totalIssues);
|
|
453
484
|
|
|
454
485
|
if (options.json) {
|
|
455
|
-
|
|
486
|
+
log(io, JSON.stringify({
|
|
456
487
|
healthScore,
|
|
457
488
|
summary: {
|
|
458
489
|
total: entries.length,
|
|
@@ -464,45 +495,71 @@ export async function knowledgeHealth(options: KnowledgeOptions & { dir?: string
|
|
|
464
495
|
},
|
|
465
496
|
issues: issues.slice(0, 50),
|
|
466
497
|
}, null, 2));
|
|
467
|
-
return;
|
|
498
|
+
return { kind: 'ok' };
|
|
468
499
|
}
|
|
469
500
|
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
501
|
+
log(io, chalk.blue('🏥 飞轮健康检查\n'));
|
|
502
|
+
log(io, chalk.bold(` 健康分: ${healthScore >= 80 ? chalk.green(healthScore) : healthScore >= 60 ? chalk.yellow(healthScore) : chalk.red(healthScore)}/100`));
|
|
503
|
+
log(io, chalk.bold(` 活跃条目: ${entries.length}`));
|
|
504
|
+
log(io);
|
|
474
505
|
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
506
|
+
log(io, chalk.bold(' 数据流状态:'));
|
|
507
|
+
log(io, ` 引用密度: ${lowRefEntries === 0 ? chalk.green('✓') : chalk.yellow(`⚠ ${lowRefEntries} 个零引用 verified`)}`);
|
|
508
|
+
log(io, ` 新鲜度: ${staleEntries === 0 ? chalk.green('✓') : chalk.yellow(`⚠ ${staleEntries} 个过期 draft`)}`);
|
|
509
|
+
log(io, ` 消费追踪: ${consumptionData ? chalk.green('✓') : chalk.gray('○ 未启用')}`);
|
|
510
|
+
log(io);
|
|
480
511
|
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
512
|
+
log(io, chalk.bold(' 飞轮指标:'));
|
|
513
|
+
log(io, ` 引用覆盖: ${refCoverage}%`);
|
|
514
|
+
log(io, ` 平均引用: ${avgRefs}`);
|
|
515
|
+
log(io);
|
|
485
516
|
|
|
486
517
|
if (issues.length > 0) {
|
|
487
|
-
|
|
518
|
+
log(io, chalk.bold(` 问题 (前 20):`));
|
|
488
519
|
for (const issue of issues.slice(0, 20)) {
|
|
489
520
|
const sevColor = issue.severity === 'error' ? chalk.red
|
|
490
521
|
: issue.severity === 'warn' ? chalk.yellow
|
|
491
522
|
: chalk.gray;
|
|
492
|
-
|
|
523
|
+
log(io, ` ${sevColor(`[${issue.severity}]`)} ${issue.entry}: ${issue.detail}`);
|
|
493
524
|
}
|
|
494
525
|
if (issues.length > 20) {
|
|
495
|
-
|
|
526
|
+
log(io, chalk.gray(` ... 还有 ${issues.length - 20} 条`));
|
|
496
527
|
}
|
|
497
528
|
} else {
|
|
498
|
-
|
|
529
|
+
log(io, chalk.green(' ✓ 无问题'));
|
|
530
|
+
}
|
|
531
|
+
return { kind: 'ok' };
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
/** 缺省知识库数据根(相对用户 home) */
|
|
535
|
+
const KNOWLEDGE_DATA_DIR = path.join('.harness', 'knowledge');
|
|
536
|
+
/** 旧缺省数据根(曾寄居 studio home),仍有数据时兼容沿用,免迁移 */
|
|
537
|
+
const LEGACY_KNOWLEDGE_DATA_DIR = path.join('.studio', 'knowledge');
|
|
538
|
+
|
|
539
|
+
let legacyKnowledgeDirNotified = false;
|
|
540
|
+
|
|
541
|
+
function hasKnowledgeData(dir: string): boolean {
|
|
542
|
+
try {
|
|
543
|
+
return fs.existsSync(dir) && fs.readdirSync(dir).length > 0;
|
|
544
|
+
} catch {
|
|
545
|
+
return false;
|
|
499
546
|
}
|
|
500
547
|
}
|
|
501
548
|
|
|
502
|
-
function getKnowledgeDir(projectPath
|
|
549
|
+
function getKnowledgeDir(projectPath: string | undefined, io: CommandIO): string {
|
|
503
550
|
if (projectPath) return `${projectPath}/.harness/knowledge`;
|
|
504
551
|
if (process.env.KNOWLEDGE_BASE_DIR) return process.env.KNOWLEDGE_BASE_DIR;
|
|
505
|
-
|
|
552
|
+
const defaultDir = path.join(os.homedir(), KNOWLEDGE_DATA_DIR);
|
|
553
|
+
const legacyDir = path.join(os.homedir(), LEGACY_KNOWLEDGE_DATA_DIR);
|
|
554
|
+
if (hasKnowledgeData(legacyDir)) {
|
|
555
|
+
if (!legacyKnowledgeDirNotified) {
|
|
556
|
+
legacyKnowledgeDirNotified = true;
|
|
557
|
+
// stderr:不污染 --json 的 stdout 输出
|
|
558
|
+
logError(io, chalk.yellow(`⚠ 缺省知识库根已改为 ${defaultDir};旧目录 ${legacyDir} 仍有数据,本次沿用(免迁移,可用 KNOWLEDGE_BASE_DIR 覆盖)`));
|
|
559
|
+
}
|
|
560
|
+
return legacyDir;
|
|
561
|
+
}
|
|
562
|
+
return defaultDir;
|
|
506
563
|
}
|
|
507
564
|
|
|
508
565
|
/**
|
|
@@ -513,154 +570,143 @@ export async function knowledgeAudit(options: KnowledgeOptions & {
|
|
|
513
570
|
dryRun?: boolean;
|
|
514
571
|
threshold?: number;
|
|
515
572
|
dir?: string;
|
|
516
|
-
}): Promise<
|
|
573
|
+
}, io: CommandIO = processIO): Promise<CommandResult> {
|
|
517
574
|
const audit = new KnowledgeAudit({
|
|
518
|
-
baseDir: options.dir || getKnowledgeDir(options.projectPath),
|
|
575
|
+
baseDir: options.dir || getKnowledgeDir(options.projectPath, io),
|
|
519
576
|
shortContentThreshold: options.threshold ? parseInt(options.threshold as any, 10) : undefined,
|
|
520
577
|
});
|
|
521
578
|
|
|
522
579
|
const isDryRun = options.dryRun && !options.fix;
|
|
523
580
|
|
|
524
581
|
if (!options.json && !isDryRun) {
|
|
525
|
-
|
|
582
|
+
log(io, chalk.blue('🔍 知识库质量审计...\n'));
|
|
526
583
|
}
|
|
527
584
|
|
|
528
585
|
const report = audit.run({ autoFix: options.fix && !isDryRun });
|
|
529
586
|
|
|
530
587
|
if (options.json) {
|
|
531
|
-
|
|
532
|
-
return;
|
|
588
|
+
log(io, JSON.stringify(report, null, 2));
|
|
589
|
+
return { kind: 'ok' };
|
|
533
590
|
}
|
|
534
591
|
|
|
535
592
|
// Summary
|
|
536
|
-
|
|
537
|
-
|
|
593
|
+
log(io, chalk.bold(` 总条目: ${report.totalEntries}`));
|
|
594
|
+
log(io, chalk.bold(` 健康分: ${report.healthScore.before}/100`));
|
|
538
595
|
if (report.autoFixed > 0) {
|
|
539
|
-
|
|
540
|
-
|
|
596
|
+
log(io, chalk.green(` 自动修复: ${report.autoFixed} 条`));
|
|
597
|
+
log(io, chalk.bold(` 修复后: ${report.healthScore.after}/100`));
|
|
541
598
|
}
|
|
542
|
-
|
|
599
|
+
log(io);
|
|
543
600
|
|
|
544
|
-
// Dimension scores
|
|
545
|
-
const dimLabels: Record<
|
|
601
|
+
// Dimension scores(Record 全键标注 → 编译期闭环,#109)
|
|
602
|
+
const dimLabels: Record<keyof AuditReport['dimensions'], string> = {
|
|
546
603
|
structure: 'D1 结构完整性',
|
|
547
604
|
content: 'D2 内容质量',
|
|
548
605
|
dedup: 'D3 去重有效性',
|
|
549
606
|
maturity: 'D4 成熟度健康',
|
|
550
607
|
freshness: 'D5 新鲜度',
|
|
551
608
|
flywheel: 'D6 飞轮验证',
|
|
609
|
+
incremental: 'D7 增量存活',
|
|
552
610
|
};
|
|
553
|
-
|
|
611
|
+
log(io, chalk.bold(' 维度评分:'));
|
|
554
612
|
for (const [key, dim] of Object.entries(report.dimensions)) {
|
|
555
|
-
const label = dimLabels[key
|
|
613
|
+
const label = dimLabels[key as keyof AuditReport['dimensions']];
|
|
556
614
|
const scoreColor = dim.score >= 80 ? chalk.green : dim.score >= 60 ? chalk.yellow : chalk.red;
|
|
557
|
-
|
|
558
|
-
}
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
// Issues by rule
|
|
562
|
-
const ruleLabels: Record<string, string> = {
|
|
563
|
-
'frontmatter-missing': 'frontmatter 缺失',
|
|
564
|
-
'test-data-pollution': '测试数据污染',
|
|
565
|
-
'daily-audit-noise': '每日审计噪音',
|
|
566
|
-
'zero-content-proven': '零内容 proven',
|
|
567
|
-
'short-content': '短内容',
|
|
568
|
-
'maturity-inflation': '成熟度虚高',
|
|
569
|
-
'title-duplicate': '标题重复',
|
|
570
|
-
'source-refs-bloat': 'sourceReferences 膨胀',
|
|
571
|
-
'fragment-cluster': '碎片集群',
|
|
572
|
-
'promotion-blocked': 'promotion 受阻',
|
|
573
|
-
'orphan-draft': '孤儿 draft',
|
|
574
|
-
'stale-entry': '过期条目',
|
|
575
|
-
};
|
|
615
|
+
log(io, ` ${label}: ${scoreColor(`${dim.score}/100`)} (${dim.issues} 问题)`);
|
|
616
|
+
}
|
|
617
|
+
log(io);
|
|
576
618
|
|
|
619
|
+
// Issues by rule(label 正本在 audit.ts 规则定义,AUDIT_RULE_LABELS 编译期闭环,#109)
|
|
577
620
|
for (const [rule, count] of Object.entries(report.summary)) {
|
|
578
621
|
if (count === 0) continue;
|
|
579
|
-
const label =
|
|
580
|
-
|
|
622
|
+
const label = AUDIT_RULE_LABELS[rule as AuditRuleName];
|
|
623
|
+
log(io, ` ${chalk.red(`${label}: ${count}`)}`);
|
|
581
624
|
}
|
|
582
625
|
|
|
583
626
|
// Detail (first 20)
|
|
584
627
|
if (report.issues.length > 0) {
|
|
585
|
-
|
|
628
|
+
log(io, chalk.bold(`\n 问题详情 (前 20 条):\n`));
|
|
586
629
|
for (const issue of report.issues.slice(0, 20)) {
|
|
587
630
|
const severityColor = issue.severity === 'critical' ? chalk.red
|
|
588
631
|
: issue.severity === 'high' ? chalk.yellow
|
|
589
632
|
: chalk.gray;
|
|
590
|
-
|
|
591
|
-
|
|
633
|
+
log(io, ` ${severityColor(`[${issue.severity}]`)} ${issue.entryId}: ${issue.title}`);
|
|
634
|
+
log(io, ` ${chalk.gray(issue.detail)} → ${issue.action}`);
|
|
592
635
|
}
|
|
593
636
|
if (report.issues.length > 20) {
|
|
594
|
-
|
|
637
|
+
log(io, chalk.gray(`\n ... 还有 ${report.issues.length - 20} 条`));
|
|
595
638
|
}
|
|
596
639
|
}
|
|
597
640
|
|
|
598
641
|
if (!options.fix && report.issues.length > 0) {
|
|
599
|
-
|
|
642
|
+
log(io, chalk.yellow(`\n 使用 --fix 自动修复`));
|
|
600
643
|
}
|
|
601
644
|
|
|
602
645
|
// Auto-rebuild index after audit (files may have changed)
|
|
603
|
-
const baseDir = options.dir || getKnowledgeDir(options.projectPath);
|
|
646
|
+
const baseDir = options.dir || getKnowledgeDir(options.projectPath, io);
|
|
604
647
|
const idxGen = new KnowledgeIndexGenerator(baseDir);
|
|
605
648
|
idxGen.regenerate();
|
|
606
649
|
if (!options.json) {
|
|
607
|
-
|
|
650
|
+
log(io, chalk.gray(` 📇 索引已重建`));
|
|
608
651
|
}
|
|
652
|
+
return { kind: 'ok' };
|
|
609
653
|
}
|
|
610
654
|
|
|
611
655
|
/**
|
|
612
656
|
* 知识库快照(KR4 存活率追踪)
|
|
613
657
|
*/
|
|
614
|
-
export function knowledgeSnapshot(options: KnowledgeOptions & { dir?: string }):
|
|
615
|
-
const store = new KnowledgeStore({ baseDir: options.dir || getKnowledgeDir(options.projectPath) });
|
|
658
|
+
export function knowledgeSnapshot(options: KnowledgeOptions & { dir?: string }, io: CommandIO = processIO): CommandResult {
|
|
659
|
+
const store = new KnowledgeStore({ baseDir: options.dir || getKnowledgeDir(options.projectPath, io) });
|
|
616
660
|
const snapPath = store.snapshot();
|
|
617
661
|
|
|
618
662
|
if (options.json) {
|
|
619
|
-
|
|
620
|
-
return;
|
|
663
|
+
log(io, JSON.stringify({ snapshotPath: snapPath }));
|
|
664
|
+
return { kind: 'ok' };
|
|
621
665
|
}
|
|
622
666
|
|
|
623
|
-
|
|
667
|
+
log(io, chalk.green(`✅ 快照已保存: ${snapPath}`));
|
|
668
|
+
return { kind: 'ok' };
|
|
624
669
|
}
|
|
625
670
|
|
|
626
671
|
/**
|
|
627
672
|
* 知识库迁移 — 为现有条目添加 AS-021 新字段
|
|
628
673
|
*/
|
|
629
|
-
export function knowledgeMigrate(options: KnowledgeOptions & { dir?: string }):
|
|
630
|
-
const baseDir = options.dir || getKnowledgeDir(options.projectPath);
|
|
674
|
+
export function knowledgeMigrate(options: KnowledgeOptions & { dir?: string }, io: CommandIO = processIO): CommandResult {
|
|
675
|
+
const baseDir = options.dir || getKnowledgeDir(options.projectPath, io);
|
|
631
676
|
|
|
632
677
|
if (options.json) {
|
|
633
678
|
const result = migrateKnowledgeEntries(baseDir);
|
|
634
|
-
|
|
635
|
-
return;
|
|
679
|
+
log(io, JSON.stringify(result, null, 2));
|
|
680
|
+
return { kind: 'ok' };
|
|
636
681
|
}
|
|
637
682
|
|
|
638
|
-
|
|
683
|
+
log(io, chalk.blue('🔄 迁移知识条目(添加 consumptionMode/origin 字段)\n'));
|
|
639
684
|
const result = migrateKnowledgeEntries(baseDir);
|
|
640
685
|
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
686
|
+
log(io, chalk.green(` 总计: ${result.total} 条`));
|
|
687
|
+
log(io, chalk.green(` 已迁移: ${result.migrated} 条`));
|
|
688
|
+
log(io, chalk.gray(` 已跳过: ${result.skipped} 条`));
|
|
644
689
|
|
|
645
690
|
if (result.errors.length > 0) {
|
|
646
|
-
|
|
691
|
+
log(io, chalk.red(`\n 错误: ${result.errors.length} 条`));
|
|
647
692
|
for (const err of result.errors.slice(0, 5)) {
|
|
648
|
-
|
|
693
|
+
log(io, chalk.red(` ${err}`));
|
|
649
694
|
}
|
|
650
695
|
}
|
|
651
696
|
|
|
652
697
|
if (result.migrated === 0 && result.errors.length === 0) {
|
|
653
|
-
|
|
698
|
+
log(io, chalk.green('\n✅ 所有条目已是最新,无需迁移'));
|
|
654
699
|
} else if (result.migrated > 0) {
|
|
655
|
-
|
|
700
|
+
log(io, chalk.green(`\n✅ 迁移完成`));
|
|
656
701
|
}
|
|
702
|
+
return { kind: 'ok' };
|
|
657
703
|
}
|
|
658
704
|
|
|
659
705
|
/**
|
|
660
706
|
* 知识库索引重建 — 生成 _index.md 供 Agent grep 使用,同时同步 index.json
|
|
661
707
|
*/
|
|
662
|
-
export function knowledgeIndex(options: KnowledgeOptions & { dir?: string }):
|
|
663
|
-
const baseDir = options.dir || getKnowledgeDir(options.projectPath);
|
|
708
|
+
export function knowledgeIndex(options: KnowledgeOptions & { dir?: string }, io: CommandIO = processIO): CommandResult {
|
|
709
|
+
const baseDir = options.dir || getKnowledgeDir(options.projectPath, io);
|
|
664
710
|
const gen = new KnowledgeIndexGenerator(baseDir);
|
|
665
711
|
|
|
666
712
|
const beforeSize = (() => {
|
|
@@ -678,20 +724,21 @@ export function knowledgeIndex(options: KnowledgeOptions & { dir?: string }): vo
|
|
|
678
724
|
const lineCount = output.split('\n').filter(l => !l.startsWith('#')).length;
|
|
679
725
|
|
|
680
726
|
if (options.json) {
|
|
681
|
-
|
|
727
|
+
log(io, JSON.stringify({
|
|
682
728
|
path: path.join(baseDir, '_index.md'),
|
|
683
729
|
entries: lineCount,
|
|
684
730
|
size: afterSize,
|
|
685
731
|
previousSize: beforeSize,
|
|
686
732
|
}));
|
|
687
|
-
return;
|
|
733
|
+
return { kind: 'ok' };
|
|
688
734
|
}
|
|
689
735
|
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
736
|
+
log(io, chalk.blue(`📇 索引已重建`));
|
|
737
|
+
log(io, chalk.gray(` 路径: ${path.join(baseDir, '_index.md')}`));
|
|
738
|
+
log(io, chalk.green(` 条目: ${lineCount}`));
|
|
739
|
+
log(io, chalk.green(` 大小: ${(afterSize / 1024).toFixed(1)} KB`));
|
|
694
740
|
if (beforeSize > 0) {
|
|
695
|
-
|
|
741
|
+
log(io, chalk.gray(` 旧大小: ${(beforeSize / 1024).toFixed(1)} KB`));
|
|
696
742
|
}
|
|
743
|
+
return { kind: 'ok' };
|
|
697
744
|
}
|