@dommaker/harness 1.3.0 → 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 +8 -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 +29 -214
- 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 +21 -15
- package/dist/cli/commands/knowledge.d.ts.map +1 -1
- package/dist/cli/commands/knowledge.js +214 -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 +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 +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/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 +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.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 +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 +0 -18
- package/dist/types/constraint.d.ts.map +1 -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/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__/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__/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 +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 +169 -0
- package/src/cli/commands/__tests__/registry.test.ts +69 -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 +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 +36 -222
- package/src/cli/commands/failure.ts +33 -26
- package/src/cli/commands/init.ts +166 -220
- package/src/cli/commands/knowledge.ts +225 -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 +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 +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/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 +12 -10
- 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 +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 +0 -25
- 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,39 +495,40 @@ 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(' ✓ 无问题'));
|
|
499
530
|
}
|
|
531
|
+
return { kind: 'ok' };
|
|
500
532
|
}
|
|
501
533
|
|
|
502
534
|
/** 缺省知识库数据根(相对用户 home) */
|
|
@@ -514,7 +546,7 @@ function hasKnowledgeData(dir: string): boolean {
|
|
|
514
546
|
}
|
|
515
547
|
}
|
|
516
548
|
|
|
517
|
-
function getKnowledgeDir(projectPath
|
|
549
|
+
function getKnowledgeDir(projectPath: string | undefined, io: CommandIO): string {
|
|
518
550
|
if (projectPath) return `${projectPath}/.harness/knowledge`;
|
|
519
551
|
if (process.env.KNOWLEDGE_BASE_DIR) return process.env.KNOWLEDGE_BASE_DIR;
|
|
520
552
|
const defaultDir = path.join(os.homedir(), KNOWLEDGE_DATA_DIR);
|
|
@@ -523,7 +555,7 @@ function getKnowledgeDir(projectPath?: string): string {
|
|
|
523
555
|
if (!legacyKnowledgeDirNotified) {
|
|
524
556
|
legacyKnowledgeDirNotified = true;
|
|
525
557
|
// stderr:不污染 --json 的 stdout 输出
|
|
526
|
-
|
|
558
|
+
logError(io, chalk.yellow(`⚠ 缺省知识库根已改为 ${defaultDir};旧目录 ${legacyDir} 仍有数据,本次沿用(免迁移,可用 KNOWLEDGE_BASE_DIR 覆盖)`));
|
|
527
559
|
}
|
|
528
560
|
return legacyDir;
|
|
529
561
|
}
|
|
@@ -538,154 +570,143 @@ export async function knowledgeAudit(options: KnowledgeOptions & {
|
|
|
538
570
|
dryRun?: boolean;
|
|
539
571
|
threshold?: number;
|
|
540
572
|
dir?: string;
|
|
541
|
-
}): Promise<
|
|
573
|
+
}, io: CommandIO = processIO): Promise<CommandResult> {
|
|
542
574
|
const audit = new KnowledgeAudit({
|
|
543
|
-
baseDir: options.dir || getKnowledgeDir(options.projectPath),
|
|
575
|
+
baseDir: options.dir || getKnowledgeDir(options.projectPath, io),
|
|
544
576
|
shortContentThreshold: options.threshold ? parseInt(options.threshold as any, 10) : undefined,
|
|
545
577
|
});
|
|
546
578
|
|
|
547
579
|
const isDryRun = options.dryRun && !options.fix;
|
|
548
580
|
|
|
549
581
|
if (!options.json && !isDryRun) {
|
|
550
|
-
|
|
582
|
+
log(io, chalk.blue('🔍 知识库质量审计...\n'));
|
|
551
583
|
}
|
|
552
584
|
|
|
553
585
|
const report = audit.run({ autoFix: options.fix && !isDryRun });
|
|
554
586
|
|
|
555
587
|
if (options.json) {
|
|
556
|
-
|
|
557
|
-
return;
|
|
588
|
+
log(io, JSON.stringify(report, null, 2));
|
|
589
|
+
return { kind: 'ok' };
|
|
558
590
|
}
|
|
559
591
|
|
|
560
592
|
// Summary
|
|
561
|
-
|
|
562
|
-
|
|
593
|
+
log(io, chalk.bold(` 总条目: ${report.totalEntries}`));
|
|
594
|
+
log(io, chalk.bold(` 健康分: ${report.healthScore.before}/100`));
|
|
563
595
|
if (report.autoFixed > 0) {
|
|
564
|
-
|
|
565
|
-
|
|
596
|
+
log(io, chalk.green(` 自动修复: ${report.autoFixed} 条`));
|
|
597
|
+
log(io, chalk.bold(` 修复后: ${report.healthScore.after}/100`));
|
|
566
598
|
}
|
|
567
|
-
|
|
599
|
+
log(io);
|
|
568
600
|
|
|
569
|
-
// Dimension scores
|
|
570
|
-
const dimLabels: Record<
|
|
601
|
+
// Dimension scores(Record 全键标注 → 编译期闭环,#109)
|
|
602
|
+
const dimLabels: Record<keyof AuditReport['dimensions'], string> = {
|
|
571
603
|
structure: 'D1 结构完整性',
|
|
572
604
|
content: 'D2 内容质量',
|
|
573
605
|
dedup: 'D3 去重有效性',
|
|
574
606
|
maturity: 'D4 成熟度健康',
|
|
575
607
|
freshness: 'D5 新鲜度',
|
|
576
608
|
flywheel: 'D6 飞轮验证',
|
|
609
|
+
incremental: 'D7 增量存活',
|
|
577
610
|
};
|
|
578
|
-
|
|
611
|
+
log(io, chalk.bold(' 维度评分:'));
|
|
579
612
|
for (const [key, dim] of Object.entries(report.dimensions)) {
|
|
580
|
-
const label = dimLabels[key
|
|
613
|
+
const label = dimLabels[key as keyof AuditReport['dimensions']];
|
|
581
614
|
const scoreColor = dim.score >= 80 ? chalk.green : dim.score >= 60 ? chalk.yellow : chalk.red;
|
|
582
|
-
|
|
583
|
-
}
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
// Issues by rule
|
|
587
|
-
const ruleLabels: Record<string, string> = {
|
|
588
|
-
'frontmatter-missing': 'frontmatter 缺失',
|
|
589
|
-
'test-data-pollution': '测试数据污染',
|
|
590
|
-
'daily-audit-noise': '每日审计噪音',
|
|
591
|
-
'zero-content-proven': '零内容 proven',
|
|
592
|
-
'short-content': '短内容',
|
|
593
|
-
'maturity-inflation': '成熟度虚高',
|
|
594
|
-
'title-duplicate': '标题重复',
|
|
595
|
-
'source-refs-bloat': 'sourceReferences 膨胀',
|
|
596
|
-
'fragment-cluster': '碎片集群',
|
|
597
|
-
'promotion-blocked': 'promotion 受阻',
|
|
598
|
-
'orphan-draft': '孤儿 draft',
|
|
599
|
-
'stale-entry': '过期条目',
|
|
600
|
-
};
|
|
615
|
+
log(io, ` ${label}: ${scoreColor(`${dim.score}/100`)} (${dim.issues} 问题)`);
|
|
616
|
+
}
|
|
617
|
+
log(io);
|
|
601
618
|
|
|
619
|
+
// Issues by rule(label 正本在 audit.ts 规则定义,AUDIT_RULE_LABELS 编译期闭环,#109)
|
|
602
620
|
for (const [rule, count] of Object.entries(report.summary)) {
|
|
603
621
|
if (count === 0) continue;
|
|
604
|
-
const label =
|
|
605
|
-
|
|
622
|
+
const label = AUDIT_RULE_LABELS[rule as AuditRuleName];
|
|
623
|
+
log(io, ` ${chalk.red(`${label}: ${count}`)}`);
|
|
606
624
|
}
|
|
607
625
|
|
|
608
626
|
// Detail (first 20)
|
|
609
627
|
if (report.issues.length > 0) {
|
|
610
|
-
|
|
628
|
+
log(io, chalk.bold(`\n 问题详情 (前 20 条):\n`));
|
|
611
629
|
for (const issue of report.issues.slice(0, 20)) {
|
|
612
630
|
const severityColor = issue.severity === 'critical' ? chalk.red
|
|
613
631
|
: issue.severity === 'high' ? chalk.yellow
|
|
614
632
|
: chalk.gray;
|
|
615
|
-
|
|
616
|
-
|
|
633
|
+
log(io, ` ${severityColor(`[${issue.severity}]`)} ${issue.entryId}: ${issue.title}`);
|
|
634
|
+
log(io, ` ${chalk.gray(issue.detail)} → ${issue.action}`);
|
|
617
635
|
}
|
|
618
636
|
if (report.issues.length > 20) {
|
|
619
|
-
|
|
637
|
+
log(io, chalk.gray(`\n ... 还有 ${report.issues.length - 20} 条`));
|
|
620
638
|
}
|
|
621
639
|
}
|
|
622
640
|
|
|
623
641
|
if (!options.fix && report.issues.length > 0) {
|
|
624
|
-
|
|
642
|
+
log(io, chalk.yellow(`\n 使用 --fix 自动修复`));
|
|
625
643
|
}
|
|
626
644
|
|
|
627
645
|
// Auto-rebuild index after audit (files may have changed)
|
|
628
|
-
const baseDir = options.dir || getKnowledgeDir(options.projectPath);
|
|
646
|
+
const baseDir = options.dir || getKnowledgeDir(options.projectPath, io);
|
|
629
647
|
const idxGen = new KnowledgeIndexGenerator(baseDir);
|
|
630
648
|
idxGen.regenerate();
|
|
631
649
|
if (!options.json) {
|
|
632
|
-
|
|
650
|
+
log(io, chalk.gray(` 📇 索引已重建`));
|
|
633
651
|
}
|
|
652
|
+
return { kind: 'ok' };
|
|
634
653
|
}
|
|
635
654
|
|
|
636
655
|
/**
|
|
637
656
|
* 知识库快照(KR4 存活率追踪)
|
|
638
657
|
*/
|
|
639
|
-
export function knowledgeSnapshot(options: KnowledgeOptions & { dir?: string }):
|
|
640
|
-
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) });
|
|
641
660
|
const snapPath = store.snapshot();
|
|
642
661
|
|
|
643
662
|
if (options.json) {
|
|
644
|
-
|
|
645
|
-
return;
|
|
663
|
+
log(io, JSON.stringify({ snapshotPath: snapPath }));
|
|
664
|
+
return { kind: 'ok' };
|
|
646
665
|
}
|
|
647
666
|
|
|
648
|
-
|
|
667
|
+
log(io, chalk.green(`✅ 快照已保存: ${snapPath}`));
|
|
668
|
+
return { kind: 'ok' };
|
|
649
669
|
}
|
|
650
670
|
|
|
651
671
|
/**
|
|
652
672
|
* 知识库迁移 — 为现有条目添加 AS-021 新字段
|
|
653
673
|
*/
|
|
654
|
-
export function knowledgeMigrate(options: KnowledgeOptions & { dir?: string }):
|
|
655
|
-
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);
|
|
656
676
|
|
|
657
677
|
if (options.json) {
|
|
658
678
|
const result = migrateKnowledgeEntries(baseDir);
|
|
659
|
-
|
|
660
|
-
return;
|
|
679
|
+
log(io, JSON.stringify(result, null, 2));
|
|
680
|
+
return { kind: 'ok' };
|
|
661
681
|
}
|
|
662
682
|
|
|
663
|
-
|
|
683
|
+
log(io, chalk.blue('🔄 迁移知识条目(添加 consumptionMode/origin 字段)\n'));
|
|
664
684
|
const result = migrateKnowledgeEntries(baseDir);
|
|
665
685
|
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
686
|
+
log(io, chalk.green(` 总计: ${result.total} 条`));
|
|
687
|
+
log(io, chalk.green(` 已迁移: ${result.migrated} 条`));
|
|
688
|
+
log(io, chalk.gray(` 已跳过: ${result.skipped} 条`));
|
|
669
689
|
|
|
670
690
|
if (result.errors.length > 0) {
|
|
671
|
-
|
|
691
|
+
log(io, chalk.red(`\n 错误: ${result.errors.length} 条`));
|
|
672
692
|
for (const err of result.errors.slice(0, 5)) {
|
|
673
|
-
|
|
693
|
+
log(io, chalk.red(` ${err}`));
|
|
674
694
|
}
|
|
675
695
|
}
|
|
676
696
|
|
|
677
697
|
if (result.migrated === 0 && result.errors.length === 0) {
|
|
678
|
-
|
|
698
|
+
log(io, chalk.green('\n✅ 所有条目已是最新,无需迁移'));
|
|
679
699
|
} else if (result.migrated > 0) {
|
|
680
|
-
|
|
700
|
+
log(io, chalk.green(`\n✅ 迁移完成`));
|
|
681
701
|
}
|
|
702
|
+
return { kind: 'ok' };
|
|
682
703
|
}
|
|
683
704
|
|
|
684
705
|
/**
|
|
685
706
|
* 知识库索引重建 — 生成 _index.md 供 Agent grep 使用,同时同步 index.json
|
|
686
707
|
*/
|
|
687
|
-
export function knowledgeIndex(options: KnowledgeOptions & { dir?: string }):
|
|
688
|
-
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);
|
|
689
710
|
const gen = new KnowledgeIndexGenerator(baseDir);
|
|
690
711
|
|
|
691
712
|
const beforeSize = (() => {
|
|
@@ -703,20 +724,21 @@ export function knowledgeIndex(options: KnowledgeOptions & { dir?: string }): vo
|
|
|
703
724
|
const lineCount = output.split('\n').filter(l => !l.startsWith('#')).length;
|
|
704
725
|
|
|
705
726
|
if (options.json) {
|
|
706
|
-
|
|
727
|
+
log(io, JSON.stringify({
|
|
707
728
|
path: path.join(baseDir, '_index.md'),
|
|
708
729
|
entries: lineCount,
|
|
709
730
|
size: afterSize,
|
|
710
731
|
previousSize: beforeSize,
|
|
711
732
|
}));
|
|
712
|
-
return;
|
|
733
|
+
return { kind: 'ok' };
|
|
713
734
|
}
|
|
714
735
|
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
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`));
|
|
719
740
|
if (beforeSize > 0) {
|
|
720
|
-
|
|
741
|
+
log(io, chalk.gray(` 旧大小: ${(beforeSize / 1024).toFixed(1)} KB`));
|
|
721
742
|
}
|
|
743
|
+
return { kind: 'ok' };
|
|
722
744
|
}
|