@dommaker/harness 0.16.6 → 0.16.8
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 +54 -14
- package/bin/harness.js +56 -96
- package/dist/architecture/constraint-engine.d.ts.map +1 -1
- package/dist/architecture/constraint-engine.js +0 -4
- package/dist/architecture/constraint-engine.js.map +1 -1
- package/dist/architecture/cross-project-checker.d.ts.map +1 -1
- package/dist/architecture/cross-project-checker.js +5 -25
- package/dist/architecture/cross-project-checker.js.map +1 -1
- package/dist/cli/commands/analyze-sessions.d.ts +2 -0
- package/dist/cli/commands/analyze-sessions.d.ts.map +1 -1
- package/dist/cli/commands/analyze-sessions.js +25 -181
- package/dist/cli/commands/analyze-sessions.js.map +1 -1
- package/dist/cli/commands/check.d.ts +1 -0
- package/dist/cli/commands/check.d.ts.map +1 -1
- package/dist/cli/commands/check.js +24 -216
- package/dist/cli/commands/check.js.map +1 -1
- package/dist/cli/commands/doc-freshness-check.d.ts.map +1 -1
- package/dist/cli/commands/doc-freshness-check.js +14 -28
- package/dist/cli/commands/doc-freshness-check.js.map +1 -1
- package/dist/cli/commands/flow.d.ts.map +1 -1
- package/dist/cli/commands/flow.js +2 -1
- package/dist/cli/commands/flow.js.map +1 -1
- package/dist/cli/commands/init.js +2 -2
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/posteval-plan.d.ts.map +1 -1
- package/dist/cli/commands/posteval-plan.js +0 -1
- package/dist/cli/commands/posteval-plan.js.map +1 -1
- package/dist/cli/commands/release.js +1 -1
- package/dist/cli/commands/release.js.map +1 -1
- package/dist/cli/commands/report.d.ts.map +1 -1
- package/dist/cli/commands/report.js +2 -0
- package/dist/cli/commands/report.js.map +1 -1
- package/dist/cli/commands/spec-baseline-check.d.ts.map +1 -1
- package/dist/cli/commands/spec-baseline-check.js +12 -23
- package/dist/cli/commands/spec-baseline-check.js.map +1 -1
- package/dist/cli/commands/sync-docs/agents-syncer.d.ts +19 -0
- package/dist/cli/commands/sync-docs/agents-syncer.d.ts.map +1 -0
- package/dist/cli/commands/sync-docs/agents-syncer.js +288 -0
- package/dist/cli/commands/sync-docs/agents-syncer.js.map +1 -0
- package/dist/cli/commands/sync-docs/capabilities-syncer.d.ts +37 -0
- package/dist/cli/commands/sync-docs/capabilities-syncer.d.ts.map +1 -0
- package/dist/cli/commands/sync-docs/capabilities-syncer.js +235 -0
- package/dist/cli/commands/sync-docs/capabilities-syncer.js.map +1 -0
- package/dist/cli/commands/sync-docs/context-syncer.d.ts +17 -0
- package/dist/cli/commands/sync-docs/context-syncer.d.ts.map +1 -0
- package/dist/cli/commands/sync-docs/context-syncer.js +143 -0
- package/dist/cli/commands/sync-docs/context-syncer.js.map +1 -0
- package/dist/cli/commands/{sync-docs.d.ts → sync-docs/index.d.ts} +9 -2
- package/dist/cli/commands/sync-docs/index.d.ts.map +1 -0
- package/dist/cli/commands/sync-docs/index.js +338 -0
- package/dist/cli/commands/sync-docs/index.js.map +1 -0
- package/dist/cli/commands/sync-docs/preserve-block.d.ts +24 -0
- package/dist/cli/commands/sync-docs/preserve-block.d.ts.map +1 -0
- package/dist/cli/commands/sync-docs/preserve-block.js +55 -0
- package/dist/cli/commands/sync-docs/preserve-block.js.map +1 -0
- package/dist/cli/commands/sync-docs/project-reader.d.ts +41 -0
- package/dist/cli/commands/sync-docs/project-reader.d.ts.map +1 -0
- package/dist/cli/commands/sync-docs/project-reader.js +183 -0
- package/dist/cli/commands/sync-docs/project-reader.js.map +1 -0
- package/dist/cli/commands/update-user-model.d.ts +2 -0
- package/dist/cli/commands/update-user-model.d.ts.map +1 -1
- package/dist/cli/commands/update-user-model.js +19 -90
- package/dist/cli/commands/update-user-model.js.map +1 -1
- package/dist/cli/commands/validate.d.ts +3 -0
- package/dist/cli/commands/validate.d.ts.map +1 -1
- package/dist/cli/commands/validate.js +6 -39
- package/dist/cli/commands/validate.js.map +1 -1
- package/dist/cli/session-mining/corrections.d.ts +17 -0
- package/dist/cli/session-mining/corrections.d.ts.map +1 -0
- package/dist/cli/session-mining/corrections.js +58 -0
- package/dist/cli/session-mining/corrections.js.map +1 -0
- package/dist/cli/session-mining/index.d.ts +13 -0
- package/dist/cli/session-mining/index.d.ts.map +1 -0
- package/dist/{core/spec → cli/session-mining}/index.js +9 -3
- package/dist/cli/session-mining/index.js.map +1 -0
- package/dist/cli/session-mining/text.d.ts +31 -0
- package/dist/cli/session-mining/text.d.ts.map +1 -0
- package/dist/cli/session-mining/text.js +126 -0
- package/dist/cli/session-mining/text.js.map +1 -0
- package/dist/cli/session-mining/transcript.d.ts +28 -0
- package/dist/cli/session-mining/transcript.d.ts.map +1 -0
- package/dist/cli/session-mining/transcript.js +123 -0
- package/dist/cli/session-mining/transcript.js.map +1 -0
- package/dist/constraints/lifecycle-runner.d.ts +1 -1
- package/dist/constraints/lifecycle-runner.d.ts.map +1 -1
- package/dist/context/compaction.d.ts.map +1 -1
- package/dist/context/compaction.js +2 -3
- package/dist/context/compaction.js.map +1 -1
- package/dist/context/knowledge-injector.d.ts.map +1 -1
- package/dist/context/tool-output-budget.d.ts +1 -1
- package/dist/context/tool-output-budget.d.ts.map +1 -1
- package/dist/context/tool-output-budget.js +1 -1
- package/dist/context/tool-output-budget.js.map +1 -1
- package/dist/core/constraints/capabilities-parser.d.ts +24 -0
- package/dist/core/constraints/capabilities-parser.d.ts.map +1 -0
- package/dist/core/constraints/capabilities-parser.js +95 -0
- package/dist/core/constraints/capabilities-parser.js.map +1 -0
- package/dist/core/constraints/checker.d.ts +37 -100
- package/dist/core/constraints/checker.d.ts.map +1 -1
- package/dist/core/constraints/checker.js +150 -737
- package/dist/core/constraints/checker.js.map +1 -1
- package/dist/core/constraints/checkers/always-pass.d.ts +17 -0
- package/dist/core/constraints/checkers/always-pass.d.ts.map +1 -0
- package/dist/core/constraints/checkers/always-pass.js +23 -0
- package/dist/core/constraints/checkers/always-pass.js.map +1 -0
- package/dist/core/constraints/checkers/capability-sync.d.ts +10 -0
- package/dist/core/constraints/checkers/capability-sync.d.ts.map +1 -0
- package/dist/core/constraints/checkers/capability-sync.js +60 -0
- package/dist/core/constraints/checkers/capability-sync.js.map +1 -0
- package/dist/core/constraints/checkers/context-doc-sync.d.ts +6 -0
- package/dist/core/constraints/checkers/context-doc-sync.d.ts.map +1 -0
- package/dist/core/constraints/checkers/context-doc-sync.js +35 -0
- package/dist/core/constraints/checkers/context-doc-sync.js.map +1 -0
- package/dist/core/constraints/checkers/docs-freshness.d.ts +10 -0
- package/dist/core/constraints/checkers/docs-freshness.d.ts.map +1 -0
- package/dist/core/constraints/checkers/docs-freshness.js +111 -0
- package/dist/core/constraints/checkers/docs-freshness.js.map +1 -0
- package/dist/core/constraints/checkers/guideline-flags.d.ts +9 -0
- package/dist/core/constraints/checkers/guideline-flags.d.ts.map +1 -0
- package/dist/core/constraints/checkers/guideline-flags.js +13 -0
- package/dist/core/constraints/checkers/guideline-flags.js.map +1 -0
- package/dist/core/constraints/checkers/index.d.ts +17 -0
- package/dist/core/constraints/checkers/index.d.ts.map +1 -0
- package/dist/core/constraints/checkers/index.js +82 -0
- package/dist/core/constraints/checkers/index.js.map +1 -0
- package/dist/core/constraints/checkers/iron-flags.d.ts +14 -0
- package/dist/core/constraints/checkers/iron-flags.d.ts.map +1 -0
- package/dist/core/constraints/checkers/iron-flags.js +24 -0
- package/dist/core/constraints/checkers/iron-flags.js.map +1 -0
- package/dist/core/constraints/checkers/no-any-type.d.ts +6 -0
- package/dist/core/constraints/checkers/no-any-type.d.ts.map +1 -0
- package/dist/core/constraints/checkers/no-any-type.js +42 -0
- package/dist/core/constraints/checkers/no-any-type.js.map +1 -0
- package/dist/core/constraints/checkers/no-bypass-checkpoint.d.ts +6 -0
- package/dist/core/constraints/checkers/no-bypass-checkpoint.d.ts.map +1 -0
- package/dist/core/constraints/checkers/no-bypass-checkpoint.js +40 -0
- package/dist/core/constraints/checkers/no-bypass-checkpoint.js.map +1 -0
- package/dist/core/constraints/checkers/no-excuse-patterns.d.ts +6 -0
- package/dist/core/constraints/checkers/no-excuse-patterns.d.ts.map +1 -0
- package/dist/core/constraints/checkers/no-excuse-patterns.js +25 -0
- package/dist/core/constraints/checkers/no-excuse-patterns.js.map +1 -0
- package/dist/core/constraints/checkers/no-fuzzy-completion-claim.d.ts +6 -0
- package/dist/core/constraints/checkers/no-fuzzy-completion-claim.d.ts.map +1 -0
- package/dist/core/constraints/checkers/no-fuzzy-completion-claim.js +34 -0
- package/dist/core/constraints/checkers/no-fuzzy-completion-claim.js.map +1 -0
- package/dist/core/constraints/checkers/no-performative-agreement.d.ts +8 -0
- package/dist/core/constraints/checkers/no-performative-agreement.d.ts.map +1 -0
- package/dist/core/constraints/checkers/no-performative-agreement.js +23 -0
- package/dist/core/constraints/checkers/no-performative-agreement.js.map +1 -0
- package/dist/core/constraints/checkers/no-simplification-without-approval.d.ts +6 -0
- package/dist/core/constraints/checkers/no-simplification-without-approval.d.ts.map +1 -0
- package/dist/core/constraints/checkers/no-simplification-without-approval.js +30 -0
- package/dist/core/constraints/checkers/no-simplification-without-approval.js.map +1 -0
- package/dist/core/constraints/checkers/no-test-simplification.d.ts +6 -0
- package/dist/core/constraints/checkers/no-test-simplification.d.ts.map +1 -0
- package/dist/core/constraints/checkers/no-test-simplification.js +29 -0
- package/dist/core/constraints/checkers/no-test-simplification.js.map +1 -0
- package/dist/core/constraints/checkers/test-coverage.d.ts +6 -0
- package/dist/core/constraints/checkers/test-coverage.d.ts.map +1 -0
- package/dist/core/constraints/checkers/test-coverage.js +33 -0
- package/dist/core/constraints/checkers/test-coverage.js.map +1 -0
- package/dist/core/constraints/checkers/types.d.ts +43 -0
- package/dist/core/constraints/checkers/types.d.ts.map +1 -0
- package/dist/core/constraints/checkers/types.js +23 -0
- package/dist/core/constraints/checkers/types.js.map +1 -0
- package/dist/core/constraints/checkers/yagni.d.ts +8 -0
- package/dist/core/constraints/checkers/yagni.d.ts.map +1 -0
- package/dist/core/constraints/checkers/yagni.js +96 -0
- package/dist/core/constraints/checkers/yagni.js.map +1 -0
- package/dist/core/constraints/context-builder.d.ts +67 -0
- package/dist/core/constraints/context-builder.d.ts.map +1 -0
- package/dist/core/constraints/context-builder.js +294 -0
- package/dist/core/constraints/context-builder.js.map +1 -0
- package/dist/core/constraints/definitions/guidelines.d.ts +7 -0
- package/dist/core/constraints/definitions/guidelines.d.ts.map +1 -0
- package/dist/core/constraints/definitions/guidelines.js +641 -0
- package/dist/core/constraints/definitions/guidelines.js.map +1 -0
- package/dist/core/constraints/definitions/iron-laws.d.ts +7 -0
- package/dist/core/constraints/definitions/iron-laws.d.ts.map +1 -0
- package/dist/core/constraints/definitions/iron-laws.js +322 -0
- package/dist/core/constraints/definitions/iron-laws.js.map +1 -0
- package/dist/core/constraints/definitions/tips.d.ts +7 -0
- package/dist/core/constraints/definitions/tips.d.ts.map +1 -0
- package/dist/core/constraints/definitions/tips.js +62 -0
- package/dist/core/constraints/definitions/tips.js.map +1 -0
- package/dist/core/constraints/definitions.d.ts +8 -21
- package/dist/core/constraints/definitions.d.ts.map +1 -1
- package/dist/core/constraints/definitions.js +15 -1048
- package/dist/core/constraints/definitions.js.map +1 -1
- package/dist/core/constraints/doc-freshness/runner.d.ts.map +1 -1
- package/dist/core/constraints/doc-freshness/runner.js +0 -1
- package/dist/core/constraints/doc-freshness/runner.js.map +1 -1
- package/dist/core/constraints/index.d.ts +4 -5
- package/dist/core/constraints/index.d.ts.map +1 -1
- package/dist/core/constraints/index.js +1 -16
- package/dist/core/constraints/index.js.map +1 -1
- package/dist/core/constraints/interceptor.d.ts.map +1 -1
- package/dist/core/constraints/interceptor.js.map +1 -1
- package/dist/core/index.js +0 -1
- package/dist/core/index.js.map +1 -1
- package/dist/core/project-config-loader.d.ts +6 -4
- package/dist/core/project-config-loader.d.ts.map +1 -1
- package/dist/core/project-config-loader.js +46 -13
- package/dist/core/project-config-loader.js.map +1 -1
- package/dist/core/validators/check-handlers/command.d.ts +7 -0
- package/dist/core/validators/check-handlers/command.d.ts.map +1 -0
- package/dist/core/validators/check-handlers/command.js +58 -0
- package/dist/core/validators/check-handlers/command.js.map +1 -0
- package/dist/core/validators/check-handlers/file.d.ts +10 -0
- package/dist/core/validators/check-handlers/file.d.ts.map +1 -0
- package/dist/core/validators/check-handlers/file.js +128 -0
- package/dist/core/validators/check-handlers/file.js.map +1 -0
- package/dist/core/validators/check-handlers/http.d.ts +7 -0
- package/dist/core/validators/check-handlers/http.d.ts.map +1 -0
- package/dist/core/validators/check-handlers/http.js +88 -0
- package/dist/core/validators/check-handlers/http.js.map +1 -0
- package/dist/core/validators/check-handlers/output.d.ts +15 -0
- package/dist/core/validators/check-handlers/output.d.ts.map +1 -0
- package/dist/core/validators/check-handlers/output.js +152 -0
- package/dist/core/validators/check-handlers/output.js.map +1 -0
- package/dist/core/validators/checkpoint.d.ts +4 -72
- package/dist/core/validators/checkpoint.d.ts.map +1 -1
- package/dist/core/validators/checkpoint.js +21 -400
- package/dist/core/validators/checkpoint.js.map +1 -1
- package/dist/core/validators/cso.js +1 -1
- package/dist/core/validators/cso.js.map +1 -1
- package/dist/core/validators/passes-gate.js +2 -2
- package/dist/core/validators/passes-gate.js.map +1 -1
- package/dist/dashboard/stats.d.ts.map +1 -1
- package/dist/dashboard/types.d.ts.map +1 -1
- package/dist/evolution/auto-evolve.d.ts +1 -2
- package/dist/evolution/auto-evolve.d.ts.map +1 -1
- package/dist/evolution/auto-evolve.js +2 -1
- package/dist/evolution/auto-evolve.js.map +1 -1
- package/dist/failure/types.d.ts +5 -85
- package/dist/failure/types.d.ts.map +1 -1
- package/dist/failure/types.js +19 -115
- package/dist/failure/types.js.map +1 -1
- package/dist/gates/contract.d.ts.map +1 -1
- package/dist/gates/contract.js.map +1 -1
- package/dist/gates/index.d.ts.map +1 -1
- package/dist/gates/index.js.map +1 -1
- package/dist/gates/performance.js +1 -1
- package/dist/gates/performance.js.map +1 -1
- package/dist/gates/security.d.ts.map +1 -1
- package/dist/gates/security.js +1 -1
- package/dist/gates/security.js.map +1 -1
- package/dist/hooks/bootstrap.d.ts.map +1 -1
- package/dist/hooks/bootstrap.js +6 -48
- package/dist/hooks/bootstrap.js.map +1 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/knowledge/audit.d.ts.map +1 -1
- package/dist/knowledge/doctor.d.ts.map +1 -1
- package/dist/knowledge/doctor.js.map +1 -1
- package/dist/knowledge/import.d.ts.map +1 -1
- package/dist/knowledge/import.js +1 -1
- package/dist/knowledge/import.js.map +1 -1
- package/dist/knowledge/index-generator.d.ts.map +1 -1
- package/dist/knowledge/index-generator.js +0 -1
- package/dist/knowledge/index-generator.js.map +1 -1
- package/dist/knowledge/ingest.d.ts.map +1 -1
- package/dist/knowledge/ingest.js +1 -1
- package/dist/knowledge/ingest.js.map +1 -1
- package/dist/knowledge/lint.d.ts +1 -1
- package/dist/knowledge/lint.d.ts.map +1 -1
- package/dist/knowledge/lint.js +2 -2
- package/dist/knowledge/lint.js.map +1 -1
- package/dist/knowledge/primitives/code-structure.d.ts.map +1 -1
- package/dist/knowledge/primitives/code-structure.js +29 -12
- package/dist/knowledge/primitives/code-structure.js.map +1 -1
- package/dist/knowledge/query.d.ts.map +1 -1
- package/dist/knowledge/query.js.map +1 -1
- package/dist/knowledge/store.d.ts.map +1 -1
- package/dist/llm/adapter.d.ts +3 -3
- package/dist/llm/adapter.d.ts.map +1 -1
- package/dist/llm/adapter.js +3 -3
- package/dist/llm/adapter.js.map +1 -1
- package/dist/monitoring/analyzer-base.d.ts +50 -0
- package/dist/monitoring/analyzer-base.d.ts.map +1 -0
- package/dist/monitoring/analyzer-base.js +146 -0
- package/dist/monitoring/analyzer-base.js.map +1 -0
- package/dist/monitoring/constraint-doctor.d.ts +3 -46
- package/dist/monitoring/constraint-doctor.d.ts.map +1 -1
- package/dist/monitoring/constraint-doctor.js +11 -113
- package/dist/monitoring/constraint-doctor.js.map +1 -1
- package/dist/monitoring/constraint-evolver.d.ts +9 -63
- package/dist/monitoring/constraint-evolver.d.ts.map +1 -1
- package/dist/monitoring/constraint-evolver.js +8 -10
- package/dist/monitoring/constraint-evolver.js.map +1 -1
- package/dist/monitoring/diagnosis-rules.d.ts +18 -0
- package/dist/monitoring/diagnosis-rules.d.ts.map +1 -0
- package/dist/monitoring/diagnosis-rules.js +119 -0
- package/dist/monitoring/diagnosis-rules.js.map +1 -0
- package/dist/monitoring/knowledge-doctor.d.ts.map +1 -1
- package/dist/monitoring/knowledge-doctor.js.map +1 -1
- package/dist/monitoring/performance-analyzer.d.ts +0 -12
- package/dist/monitoring/performance-analyzer.d.ts.map +1 -1
- package/dist/monitoring/performance-analyzer.js +13 -93
- package/dist/monitoring/performance-analyzer.js.map +1 -1
- package/dist/monitoring/performance-collector.d.ts +0 -8
- package/dist/monitoring/performance-collector.d.ts.map +1 -1
- package/dist/monitoring/performance-collector.js +0 -21
- package/dist/monitoring/performance-collector.js.map +1 -1
- package/dist/monitoring/trace-analyzer.d.ts +0 -8
- package/dist/monitoring/trace-analyzer.d.ts.map +1 -1
- package/dist/monitoring/trace-analyzer.js +9 -91
- package/dist/monitoring/trace-analyzer.js.map +1 -1
- package/dist/presets/index.d.ts +1 -1
- package/dist/presets/index.d.ts.map +1 -1
- package/dist/presets/index.js +1 -4
- package/dist/presets/index.js.map +1 -1
- package/dist/presets/standard.d.ts +0 -9
- package/dist/presets/standard.d.ts.map +1 -1
- package/dist/presets/standard.js +1 -16
- package/dist/presets/standard.js.map +1 -1
- package/dist/safety/output-guardrail.d.ts +0 -1
- package/dist/safety/output-guardrail.d.ts.map +1 -1
- package/dist/safety/output-guardrail.js +0 -3
- package/dist/safety/output-guardrail.js.map +1 -1
- package/dist/tools/registry.d.ts.map +1 -1
- package/dist/types/constraint.d.ts +5 -59
- package/dist/types/constraint.d.ts.map +1 -1
- package/dist/types/constraint.js +1 -39
- package/dist/types/constraint.js.map +1 -1
- package/dist/types/failure.d.ts +89 -0
- package/dist/types/failure.d.ts.map +1 -0
- package/dist/types/failure.js +121 -0
- package/dist/types/failure.js.map +1 -0
- package/dist/types/index.d.ts +2 -3
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +7 -6
- package/dist/types/index.js.map +1 -1
- package/dist/types/monitoring-types.d.ts +114 -0
- package/dist/types/monitoring-types.d.ts.map +1 -0
- package/dist/types/monitoring-types.js +10 -0
- package/dist/types/monitoring-types.js.map +1 -0
- package/dist/utils/detect-source-roots.js.map +1 -1
- package/dist/utils/file-walk.d.ts +33 -0
- package/dist/utils/file-walk.d.ts.map +1 -0
- package/dist/utils/file-walk.js +89 -0
- package/dist/utils/file-walk.js.map +1 -0
- package/dist/verification/rules-based.d.ts.map +1 -1
- package/dist/verification/rules-based.js.map +1 -1
- package/package.json +3 -7
- package/src/__tests__/checker-extra.test.ts +0 -30
- package/src/__tests__/checker.test.ts +0 -33
- package/src/__tests__/checkpoint.test.ts +46 -46
- package/src/__tests__/constraint-evolver.test.ts +22 -21
- package/src/__tests__/doctor-evolver.test.ts +3 -3
- package/src/__tests__/iron-laws.test.ts +0 -27
- package/src/__tests__/presets.test.ts +1 -40
- package/src/architecture/constraint-engine.ts +0 -5
- package/src/architecture/cross-project-checker.ts +6 -24
- package/src/cli/commands/__tests__/check.test.ts +3 -1
- package/src/cli/commands/__tests__/sync-docs.test.ts +77 -0
- package/src/cli/commands/__tests__/validate.test.ts +5 -0
- package/src/cli/commands/analyze-sessions.ts +31 -185
- package/src/cli/commands/check.ts +26 -225
- package/src/cli/commands/doc-freshness-check.ts +14 -25
- package/src/cli/commands/flow.ts +5 -3
- package/src/cli/commands/init.ts +3 -3
- package/src/cli/commands/posteval-plan.ts +0 -1
- package/src/cli/commands/release.ts +1 -1
- package/src/cli/commands/report.ts +2 -0
- package/src/cli/commands/spec-baseline-check.ts +12 -19
- package/src/cli/commands/sync-docs/agents-syncer.ts +280 -0
- package/src/cli/commands/sync-docs/capabilities-syncer.ts +229 -0
- package/src/cli/commands/sync-docs/context-syncer.ts +108 -0
- package/src/cli/commands/sync-docs/index.ts +355 -0
- package/src/cli/commands/sync-docs/preserve-block.ts +59 -0
- package/src/cli/commands/sync-docs/project-reader.ts +163 -0
- package/src/cli/commands/update-user-model.ts +23 -87
- package/src/cli/commands/validate.ts +6 -40
- package/src/cli/session-mining/corrections.ts +56 -0
- package/src/cli/session-mining/index.ts +13 -0
- package/src/cli/session-mining/text.ts +117 -0
- package/src/cli/session-mining/transcript.ts +107 -0
- package/src/constraints/__tests__/lifecycle-runner.test.ts +1 -1
- package/src/constraints/lifecycle-runner.ts +1 -1
- package/src/context/compaction.ts +2 -3
- package/src/context/knowledge-injector.ts +1 -1
- package/src/context/tool-output-budget.ts +1 -1
- package/src/core/constraints/capabilities-parser.ts +70 -0
- package/src/core/constraints/checker.ts +170 -779
- package/src/core/constraints/checkers/always-pass.ts +22 -0
- package/src/core/constraints/checkers/capability-sync.ts +71 -0
- package/src/core/constraints/checkers/context-doc-sync.ts +36 -0
- package/src/core/constraints/checkers/docs-freshness.ts +116 -0
- package/src/core/constraints/checkers/guideline-flags.ts +30 -0
- package/src/core/constraints/checkers/index.ts +114 -0
- package/src/core/constraints/checkers/iron-flags.ts +62 -0
- package/src/core/constraints/checkers/no-any-type.ts +42 -0
- package/src/core/constraints/checkers/no-bypass-checkpoint.ts +40 -0
- package/src/core/constraints/checkers/no-excuse-patterns.ts +24 -0
- package/src/core/constraints/checkers/no-fuzzy-completion-claim.ts +33 -0
- package/src/core/constraints/checkers/no-performative-agreement.ts +22 -0
- package/src/core/constraints/checkers/no-simplification-without-approval.ts +29 -0
- package/src/core/constraints/checkers/no-test-simplification.ts +28 -0
- package/src/core/constraints/checkers/test-coverage.ts +35 -0
- package/src/core/constraints/checkers/types.ts +54 -0
- package/src/core/constraints/checkers/yagni.ts +65 -0
- package/src/core/constraints/context-builder.ts +262 -0
- package/src/core/constraints/definitions/guidelines.ts +668 -0
- package/src/core/constraints/definitions/iron-laws.ts +333 -0
- package/src/core/constraints/definitions/tips.ts +63 -0
- package/src/core/constraints/definitions.ts +9 -1086
- package/src/core/constraints/doc-freshness/runner.ts +2 -3
- package/src/core/constraints/index.ts +1 -27
- package/src/core/constraints/interceptor.ts +0 -2
- package/src/core/index.ts +1 -1
- package/src/core/project-config-loader.ts +49 -13
- package/src/core/validators/check-handlers/command.ts +58 -0
- package/src/core/validators/check-handlers/file.ts +104 -0
- package/src/core/validators/check-handlers/http.ts +88 -0
- package/src/core/validators/check-handlers/output.ts +165 -0
- package/src/core/validators/checkpoint.ts +32 -407
- package/src/core/validators/cso.ts +1 -1
- package/src/core/validators/passes-gate.ts +2 -2
- package/src/dashboard/stats.ts +1 -1
- package/src/dashboard/types.ts +1 -1
- package/src/evolution/__tests__/auto-evolve.test.ts +1 -2
- package/src/evolution/auto-evolve.ts +3 -3
- package/src/failure/types.ts +5 -161
- package/src/gates/contract.ts +0 -1
- package/src/gates/index.ts +0 -2
- package/src/gates/performance.ts +1 -1
- package/src/gates/security.ts +1 -3
- package/src/hooks/bootstrap.ts +6 -16
- package/src/index.ts +4 -1
- package/src/knowledge/__tests__/lifecycle.test.ts +4 -1
- package/src/knowledge/audit.ts +1 -1
- package/src/knowledge/doctor.ts +0 -1
- package/src/knowledge/import.ts +2 -2
- package/src/knowledge/index-generator.ts +0 -1
- package/src/knowledge/ingest.ts +1 -2
- package/src/knowledge/lint.ts +2 -2
- package/src/knowledge/primitives/__tests__/code-structure-degrade.test.ts +24 -0
- package/src/knowledge/primitives/code-structure.ts +36 -12
- package/src/knowledge/query.ts +0 -3
- package/src/knowledge/store.ts +1 -1
- package/src/llm/adapter.ts +3 -3
- package/src/monitoring/analyzer-base.ts +112 -0
- package/src/monitoring/constraint-doctor.ts +14 -175
- package/src/monitoring/constraint-evolver.ts +20 -92
- package/src/monitoring/diagnosis-rules.ts +132 -0
- package/src/monitoring/knowledge-doctor.ts +0 -1
- package/src/monitoring/performance-analyzer.ts +22 -72
- package/src/monitoring/performance-collector.ts +0 -22
- package/src/monitoring/trace-analyzer.ts +17 -73
- package/src/presets/__tests__/standard.test.ts +2 -17
- package/src/presets/index.ts +1 -3
- package/src/presets/standard.ts +1 -26
- package/src/safety/output-guardrail.ts +0 -4
- package/src/tools/registry.ts +1 -1
- package/src/types/__tests__/types.test.ts +1 -33
- package/src/types/constraint.ts +6 -75
- package/src/types/failure.ts +165 -0
- package/src/types/index.ts +7 -5
- package/src/types/monitoring-types.ts +150 -0
- package/src/utils/detect-source-roots.ts +1 -1
- package/src/utils/file-walk.ts +78 -0
- package/src/verification/rules-based.ts +0 -1
- package/bin/harness-knowledge-capture.js +0 -99
- package/bin/harness-knowledge-check.js +0 -164
- package/bin/harness-knowledge-track.js +0 -118
- package/dist/cli/commands/sync-docs.d.ts.map +0 -1
- package/dist/cli/commands/sync-docs.js +0 -1074
- package/dist/cli/commands/sync-docs.js.map +0 -1
- package/dist/constraints/index.d.ts +0 -4
- package/dist/constraints/index.d.ts.map +0 -1
- package/dist/constraints/index.js +0 -20
- package/dist/constraints/index.js.map +0 -1
- package/dist/core/constraints/doc-freshness/auto-fix.d.ts +0 -23
- package/dist/core/constraints/doc-freshness/auto-fix.d.ts.map +0 -1
- package/dist/core/constraints/doc-freshness/auto-fix.js +0 -59
- package/dist/core/constraints/doc-freshness/auto-fix.js.map +0 -1
- package/dist/core/constraints/prompt-injection.d.ts +0 -16
- package/dist/core/constraints/prompt-injection.d.ts.map +0 -1
- package/dist/core/constraints/prompt-injection.js +0 -69
- package/dist/core/constraints/prompt-injection.js.map +0 -1
- package/dist/core/spec/index.d.ts +0 -7
- package/dist/core/spec/index.d.ts.map +0 -1
- package/dist/core/spec/index.js.map +0 -1
- package/dist/governance/executor.d.ts +0 -45
- package/dist/governance/executor.d.ts.map +0 -1
- package/dist/governance/executor.js +0 -243
- package/dist/governance/executor.js.map +0 -1
- package/dist/governance/index.d.ts +0 -9
- package/dist/governance/index.d.ts.map +0 -1
- package/dist/governance/index.js +0 -13
- package/dist/governance/index.js.map +0 -1
- package/dist/governance/types.d.ts +0 -46
- package/dist/governance/types.d.ts.map +0 -1
- package/dist/governance/types.js +0 -9
- package/dist/governance/types.js.map +0 -1
- package/dist/tools/definitions/definitions/core/code/fingerprint.sh +0 -273
- package/dist/tools/definitions/definitions/core/code/fingerprint.yml +0 -72
- package/dist/tools/definitions/definitions/core/code/parse.yml +0 -68
- package/dist/tools/definitions/definitions/core/code/register-language.sh +0 -475
- package/dist/tools/definitions/definitions/core/code/register-language.yml +0 -74
- package/dist/tools/definitions/definitions/core/docker/build.yml +0 -73
- package/dist/tools/definitions/definitions/core/docker/run.yml +0 -81
- package/dist/tools/definitions/definitions/core/file/copy.yml +0 -88
- package/dist/tools/definitions/definitions/core/file/read.yml +0 -69
- package/dist/tools/definitions/definitions/core/file/write.yml +0 -75
- package/dist/tools/definitions/definitions/core/git/branch.yml +0 -61
- package/dist/tools/definitions/definitions/core/git/cleanup-worktree.yml +0 -50
- package/dist/tools/definitions/definitions/core/git/clone.yml +0 -69
- package/dist/tools/definitions/definitions/core/git/commit.yml +0 -65
- package/dist/tools/definitions/definitions/core/git/push.yml +0 -79
- package/dist/tools/definitions/definitions/core/git/worktree.yml +0 -96
- package/dist/tools/definitions/definitions/core/notification/notify.sh +0 -152
- package/dist/tools/definitions/definitions/core/notification/notify.yml +0 -60
- package/dist/tools/definitions/definitions/core/npm/build.yml +0 -75
- package/dist/tools/definitions/definitions/core/npm/install.yml +0 -58
- package/dist/tools/definitions/definitions/core/npm/test.yml +0 -75
- package/dist/tools/definitions/definitions/core/validation/check-reuse.yml +0 -170
- package/dist/tools/definitions/definitions/core/validation/validate-handlers.yml +0 -118
- package/dist/tools/definitions/definitions/core/validation/validate-readme-stats.yml +0 -155
- package/dist/tools/definitions/definitions/core/validation/validate-yaml-syntax.yml +0 -134
- package/dist/tools/definitions/definitions/ext/browser/browser-automate.sh +0 -255
- package/dist/tools/definitions/definitions/ext/browser/browser-automate.yml +0 -166
- package/dist/tools/definitions/definitions/ext/browser/browser-close.sh +0 -104
- package/dist/tools/definitions/definitions/ext/browser/browser-close.yml +0 -55
- package/dist/tools/definitions/definitions/ext/browser/browser-mcp-debug.sh +0 -682
- package/dist/tools/definitions/definitions/ext/browser/browser-mcp-debug.yml +0 -167
- package/dist/tools/definitions/definitions/ext/browser/browser-start.sh +0 -101
- package/dist/tools/definitions/definitions/ext/browser/browser-start.yml +0 -62
- package/dist/tools/definitions/definitions/registry.json +0 -795
- package/dist/tools/definitions/definitions/std/README.md +0 -350
- package/dist/tools/definitions/definitions/std/analysis/analyze-codebase.yml +0 -72
- package/dist/tools/definitions/definitions/std/analysis/analyze-diff.yml +0 -182
- package/dist/tools/definitions/definitions/std/analysis/analyze-git-diff.yml +0 -70
- package/dist/tools/definitions/definitions/std/analysis/analyze-impact.yml +0 -78
- package/dist/tools/definitions/definitions/std/analysis/analyze-requirements.yml +0 -105
- package/dist/tools/definitions/definitions/std/analysis/brainstorm-options.yml +0 -106
- package/dist/tools/definitions/definitions/std/analysis/build-graph.yml +0 -154
- package/dist/tools/definitions/definitions/std/analysis/clarify-requirements.yml +0 -90
- package/dist/tools/definitions/definitions/std/analysis/detect-context.yml +0 -109
- package/dist/tools/definitions/definitions/std/analysis/detect-layers.yml +0 -125
- package/dist/tools/definitions/definitions/std/analysis/generate-tour.yml +0 -102
- package/dist/tools/definitions/definitions/std/analysis/search-enhance.yml +0 -105
- package/dist/tools/definitions/definitions/std/backlog/add.yml +0 -51
- package/dist/tools/definitions/definitions/std/backlog/decide.yml +0 -39
- package/dist/tools/definitions/definitions/std/backlog/github-sync.yml +0 -161
- package/dist/tools/definitions/definitions/std/backlog/list.yml +0 -53
- package/dist/tools/definitions/definitions/std/backlog/load.yml +0 -46
- package/dist/tools/definitions/definitions/std/backlog/resolve.yml +0 -37
- package/dist/tools/definitions/definitions/std/backlog/update.yml +0 -42
- package/dist/tools/definitions/definitions/std/bugfix/diagnose.yml +0 -68
- package/dist/tools/definitions/definitions/std/bugfix/fix.yml +0 -66
- package/dist/tools/definitions/definitions/std/bugfix/verify.yml +0 -69
- package/dist/tools/definitions/definitions/std/constraint/check-decomposition.yml +0 -103
- package/dist/tools/definitions/definitions/std/constraint/check-feature-list.yml +0 -77
- package/dist/tools/definitions/definitions/std/constraint/check-progress.yml +0 -97
- package/dist/tools/definitions/definitions/std/constraint/generate-report.yml +0 -147
- package/dist/tools/definitions/definitions/std/deploy/commit-push.yml +0 -33
- package/dist/tools/definitions/definitions/std/deploy/deploy-backend.yml +0 -37
- package/dist/tools/definitions/definitions/std/deploy/deploy-frontend.yml +0 -37
- package/dist/tools/definitions/definitions/std/deploy/rollback.yml +0 -148
- package/dist/tools/definitions/definitions/std/design/analyze-architecture-impact.yml +0 -113
- package/dist/tools/definitions/definitions/std/design/design-api.yml +0 -55
- package/dist/tools/definitions/definitions/std/design/design-architecture.yml +0 -97
- package/dist/tools/definitions/definitions/std/design/design-db.yml +0 -81
- package/dist/tools/definitions/definitions/std/design/generate-spec.yml +0 -182
- package/dist/tools/definitions/definitions/std/development/develop-api.yml +0 -85
- package/dist/tools/definitions/definitions/std/development/develop-backend.yml +0 -88
- package/dist/tools/definitions/definitions/std/development/develop-frontend.yml +0 -87
- package/dist/tools/definitions/definitions/std/development/develop-shared-types.yml +0 -74
- package/dist/tools/definitions/definitions/std/development/develop-task.yml +0 -122
- package/dist/tools/definitions/definitions/std/development/develop-tests.yml +0 -76
- package/dist/tools/definitions/definitions/std/development/execute-batch.yml +0 -152
- package/dist/tools/definitions/definitions/std/development/fix-issues.yml +0 -100
- package/dist/tools/definitions/definitions/std/development/implement-task.yml +0 -124
- package/dist/tools/definitions/definitions/std/development/review-fix-cycle.yml +0 -232
- package/dist/tools/definitions/definitions/std/evolution/implement.yml +0 -125
- package/dist/tools/definitions/definitions/std/evolution/prioritize.yml +0 -112
- package/dist/tools/definitions/definitions/std/evolution/report-gap.yml +0 -166
- package/dist/tools/definitions/definitions/std/file/read.yml +0 -21
- package/dist/tools/definitions/definitions/std/governance/aggregate-opinions.sh +0 -201
- package/dist/tools/definitions/definitions/std/governance/aggregate-opinions.yml +0 -79
- package/dist/tools/definitions/definitions/std/governance/audit-task.yml +0 -178
- package/dist/tools/definitions/definitions/std/governance/cast-vote.yml +0 -129
- package/dist/tools/definitions/definitions/std/governance/collect-issues.yml +0 -16
- package/dist/tools/definitions/definitions/std/governance/create-signed-document.yml +0 -23
- package/dist/tools/definitions/definitions/std/governance/create-voting-session.yml +0 -101
- package/dist/tools/definitions/definitions/std/governance/define-stance.sh +0 -243
- package/dist/tools/definitions/definitions/std/governance/define-stance.yml +0 -82
- package/dist/tools/definitions/definitions/std/governance/generate-review-report.yml +0 -23
- package/dist/tools/definitions/definitions/std/governance/impeach.yml +0 -179
- package/dist/tools/definitions/definitions/std/governance/multi-stance-review.yml +0 -154
- package/dist/tools/definitions/definitions/std/governance/prepare-artifact.yml +0 -20
- package/dist/tools/definitions/definitions/std/governance/review-spec-approval.yml +0 -262
- package/dist/tools/definitions/definitions/std/governance/rollback.yml +0 -186
- package/dist/tools/definitions/definitions/std/governance/stance-review.yml +0 -149
- package/dist/tools/definitions/definitions/std/governance/track-effect.yml +0 -199
- package/dist/tools/definitions/definitions/std/governance/vote-tally.yml +0 -144
- package/dist/tools/definitions/definitions/std/patch/execute.yml +0 -51
- package/dist/tools/definitions/definitions/std/planning/generate-tasks.yml +0 -36
- package/dist/tools/definitions/definitions/std/planning/load-tasks.yml +0 -44
- package/dist/tools/definitions/definitions/std/planning/split-batch.yml +0 -481
- package/dist/tools/definitions/definitions/std/planning/split-tasks.yml +0 -359
- package/dist/tools/definitions/definitions/std/planning/update-tasks.yml +0 -51
- package/dist/tools/definitions/definitions/std/planning/validate-tasks.yml +0 -46
- package/dist/tools/definitions/definitions/std/project/decide-next-workflow.yml +0 -38
- package/dist/tools/definitions/definitions/std/project/load-state.yml +0 -31
- package/dist/tools/definitions/definitions/std/project/save-state.yml +0 -32
- package/dist/tools/definitions/definitions/std/quality/check-quality.yml +0 -241
- package/dist/tools/definitions/definitions/std/quality/debug-systematic.yml +0 -163
- package/dist/tools/definitions/definitions/std/quality/handle-feedback.yml +0 -108
- package/dist/tools/definitions/definitions/std/quality/review-code.yml +0 -128
- package/dist/tools/definitions/definitions/std/quality/review-spec.yml +0 -140
- package/dist/tools/definitions/definitions/std/quality/review-task.yml +0 -190
- package/dist/tools/definitions/definitions/std/quality/run-tests.yml +0 -80
- package/dist/tools/definitions/definitions/std/quick/analyze.yml +0 -56
- package/dist/tools/definitions/definitions/std/quick/implement.yml +0 -67
- package/dist/tools/definitions/definitions/std/quick/verify.yml +0 -71
- package/dist/tools/definitions/definitions/std/verification/verify_completion.yml +0 -113
- package/dist/tools/definitions/definitions/std/verification/wait-condition.yml +0 -107
- package/dist/types/iron-law.d.ts +0 -25
- package/dist/types/iron-law.d.ts.map +0 -1
- package/dist/types/iron-law.js +0 -16
- package/dist/types/iron-law.js.map +0 -1
- package/src/cli/commands/sync-docs.ts +0 -1175
- package/src/constraints/index.ts +0 -3
- package/src/core/constraints/__tests__/prompt-injection.test.ts +0 -89
- package/src/core/constraints/doc-freshness/auto-fix.ts +0 -69
- package/src/core/constraints/prompt-injection.ts +0 -73
- package/src/core/spec/index.ts +0 -7
- package/src/governance/CONTEXT.md +0 -22
- package/src/governance/__tests__/executor.test.ts +0 -249
- package/src/governance/executor.ts +0 -229
- package/src/governance/index.ts +0 -13
- package/src/governance/types.ts +0 -53
- package/src/tools/core/package.json +0 -7
- package/src/tools/core/pnpm-lock.yaml +0 -232
- package/src/types/iron-law.ts +0 -50
|
@@ -1,1074 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* harness sync-docs 命令
|
|
4
|
-
*
|
|
5
|
-
* 自动同步项目文档:CAPABILITIES.md、CONTEXT.md 检查、CHANGELOG 辅助
|
|
6
|
-
* --check 模式输出结构化信息,供 LLM 或 CI 消费
|
|
7
|
-
*/
|
|
8
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
9
|
-
if (k2 === undefined) k2 = k;
|
|
10
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
11
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
12
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
13
|
-
}
|
|
14
|
-
Object.defineProperty(o, k2, desc);
|
|
15
|
-
}) : (function(o, m, k, k2) {
|
|
16
|
-
if (k2 === undefined) k2 = k;
|
|
17
|
-
o[k2] = m[k];
|
|
18
|
-
}));
|
|
19
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
20
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
21
|
-
}) : function(o, v) {
|
|
22
|
-
o["default"] = v;
|
|
23
|
-
});
|
|
24
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
25
|
-
var ownKeys = function(o) {
|
|
26
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
27
|
-
var ar = [];
|
|
28
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
29
|
-
return ar;
|
|
30
|
-
};
|
|
31
|
-
return ownKeys(o);
|
|
32
|
-
};
|
|
33
|
-
return function (mod) {
|
|
34
|
-
if (mod && mod.__esModule) return mod;
|
|
35
|
-
var result = {};
|
|
36
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
37
|
-
__setModuleDefault(result, mod);
|
|
38
|
-
return result;
|
|
39
|
-
};
|
|
40
|
-
})();
|
|
41
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
42
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
43
|
-
};
|
|
44
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
|
-
exports.syncDocs = syncDocs;
|
|
46
|
-
const chalk_1 = __importDefault(require("chalk"));
|
|
47
|
-
const fs = __importStar(require("fs/promises"));
|
|
48
|
-
const fs_1 = require("fs");
|
|
49
|
-
const path = __importStar(require("path"));
|
|
50
|
-
const yaml = __importStar(require("js-yaml"));
|
|
51
|
-
const definitions_1 = require("../../core/constraints/definitions");
|
|
52
|
-
const runner_1 = require("../../core/constraints/doc-freshness/runner");
|
|
53
|
-
const detect_source_roots_1 = require("../../utils/detect-source-roots");
|
|
54
|
-
/**
|
|
55
|
-
* 同步文档
|
|
56
|
-
*/
|
|
57
|
-
async function syncDocs(options) {
|
|
58
|
-
const projectPath = options.projectPath || process.cwd();
|
|
59
|
-
const isCheck = options.check === true;
|
|
60
|
-
const isJson = options.json === true;
|
|
61
|
-
if (!isJson) {
|
|
62
|
-
if (isCheck) {
|
|
63
|
-
console.log(chalk_1.default.blue('🔍 检查文档新鲜度...'));
|
|
64
|
-
}
|
|
65
|
-
else {
|
|
66
|
-
console.log(chalk_1.default.blue('📝 同步文档...'));
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
const result = {
|
|
70
|
-
added: [],
|
|
71
|
-
removed: [],
|
|
72
|
-
contextMissing: [],
|
|
73
|
-
contextStale: [],
|
|
74
|
-
};
|
|
75
|
-
// 1. 扫描源码模块(从 governance config 读取目录列表,默认 src/)
|
|
76
|
-
const srcDirs = await getSourceDirs(projectPath);
|
|
77
|
-
let currentModules = [];
|
|
78
|
-
for (const srcDir of srcDirs) {
|
|
79
|
-
try {
|
|
80
|
-
const modules = await scanSourceModules(path.join(projectPath, srcDir), projectPath);
|
|
81
|
-
currentModules.push(...modules);
|
|
82
|
-
}
|
|
83
|
-
catch {
|
|
84
|
-
if (!isJson) {
|
|
85
|
-
console.log(chalk_1.default.yellow(`⚠️ 未找到 ${srcDir} 目录,跳过`));
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
// 2. 解析现有 CAPABILITIES.md,检测格式
|
|
90
|
-
const capabilitiesPath = path.join(projectPath, 'CAPABILITIES.md');
|
|
91
|
-
let existingFiles = [];
|
|
92
|
-
let capsContent = '';
|
|
93
|
-
let capsIsCapabilityListing = false;
|
|
94
|
-
try {
|
|
95
|
-
capsContent = await fs.readFile(capabilitiesPath, 'utf-8');
|
|
96
|
-
if (isCapabilityListingFormat(capsContent)) {
|
|
97
|
-
capsIsCapabilityListing = true;
|
|
98
|
-
}
|
|
99
|
-
else {
|
|
100
|
-
existingFiles = await parseCapabilitiesFiles(capabilitiesPath);
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
catch {
|
|
104
|
-
// CAPABILITIES.md 不存在,将创建
|
|
105
|
-
}
|
|
106
|
-
// 3. 对比差异(分两种格式)
|
|
107
|
-
let capCountMismatches = [];
|
|
108
|
-
if (capsIsCapabilityListing) {
|
|
109
|
-
// 能力清单格式:委托 FreshnessRunner 对比计数
|
|
110
|
-
const countCheck = checkCapabilityCounts(projectPath);
|
|
111
|
-
capCountMismatches = countCheck.mismatches;
|
|
112
|
-
// 不填充 result.added/removed(文件级对比不适用于此格式)
|
|
113
|
-
}
|
|
114
|
-
else {
|
|
115
|
-
// 传统的文件表格格式
|
|
116
|
-
const getBasename = (f) => {
|
|
117
|
-
const clean = f.endsWith('/') ? f.slice(0, -1) : f;
|
|
118
|
-
return clean.split('/').pop();
|
|
119
|
-
};
|
|
120
|
-
const currentBasenames = currentModules.map(m => getBasename(m.file));
|
|
121
|
-
result.added = currentBasenames.filter(f => !existingFiles.includes(f));
|
|
122
|
-
result.removed = existingFiles.filter(f => !currentBasenames.includes(f));
|
|
123
|
-
}
|
|
124
|
-
// 4. 检查 CONTEXT.md(缺失 + 过时)
|
|
125
|
-
// 4a. 配置中要求的目录:检查缺失
|
|
126
|
-
const contextDirs = await getRequiredContextDirs(projectPath);
|
|
127
|
-
for (const dir of contextDirs) {
|
|
128
|
-
const contextPath = path.join(projectPath, dir, 'CONTEXT.md');
|
|
129
|
-
try {
|
|
130
|
-
await fs.access(contextPath);
|
|
131
|
-
}
|
|
132
|
-
catch {
|
|
133
|
-
result.contextMissing.push(dir);
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
// 4b. 自动发现已有的 CONTEXT.md:检查过时
|
|
137
|
-
const existingContextFiles = await findExistingContextFiles(projectPath, srcDirs);
|
|
138
|
-
for (const dir of existingContextFiles) {
|
|
139
|
-
const contextPath = path.join(projectPath, dir, 'CONTEXT.md');
|
|
140
|
-
try {
|
|
141
|
-
const contextStat = await fs.stat(contextPath);
|
|
142
|
-
const dirPath = path.join(projectPath, dir);
|
|
143
|
-
const latestTsMtime = await getLatestTsMtime(dirPath);
|
|
144
|
-
if (latestTsMtime && latestTsMtime > contextStat.mtimeMs) {
|
|
145
|
-
result.contextStale.push(dir);
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
catch {
|
|
149
|
-
// 目录不存在或无法访问,跳过
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
// 4c. AGENTS.md(--agents 启用):缺失或内容漂移
|
|
153
|
-
// 漂移比对基于"生成内容 + 既有 PRESERVE 标记块"的组合结果:
|
|
154
|
-
// PRESERVE 块原样穿过,块内手改不报漂移;块外手改/仓库状态变化报漂移。
|
|
155
|
-
let agentsMdExpected = null;
|
|
156
|
-
let agentsMdExists = false;
|
|
157
|
-
let agentsMdStale = false;
|
|
158
|
-
let agentsMdMalformedPreserve = [];
|
|
159
|
-
if (options.agents) {
|
|
160
|
-
const generated = await buildAgentsMd(projectPath, srcDirs);
|
|
161
|
-
let existing = null;
|
|
162
|
-
try {
|
|
163
|
-
existing = await fs.readFile(path.join(projectPath, 'AGENTS.md'), 'utf-8');
|
|
164
|
-
agentsMdExists = true;
|
|
165
|
-
}
|
|
166
|
-
catch {
|
|
167
|
-
// 缺失视为漂移
|
|
168
|
-
}
|
|
169
|
-
const { blocks, malformed } = existing !== null
|
|
170
|
-
? extractPreserveBlocks(existing)
|
|
171
|
-
: { blocks: [], malformed: [] };
|
|
172
|
-
agentsMdMalformedPreserve = malformed;
|
|
173
|
-
agentsMdExpected = composeAgentsMd(generated, blocks);
|
|
174
|
-
agentsMdStale = existing !== agentsMdExpected;
|
|
175
|
-
}
|
|
176
|
-
const hasAgentsIssues = options.agents === true && agentsMdStale;
|
|
177
|
-
const hasTableIssues = result.added.length > 0 || result.removed.length > 0;
|
|
178
|
-
const hasCapIssues = capCountMismatches.length > 0;
|
|
179
|
-
const hasContextIssues = result.contextMissing.length > 0 || result.contextStale.length > 0;
|
|
180
|
-
const hasIssues = hasTableIssues || hasCapIssues || hasContextIssues || hasAgentsIssues;
|
|
181
|
-
// 5. JSON 输出模式:结构化输出供 LLM 消费
|
|
182
|
-
if (isJson) {
|
|
183
|
-
const jsonOutput = {
|
|
184
|
-
stale: hasIssues,
|
|
185
|
-
format: capsIsCapabilityListing ? 'capability-listing' : 'file-table',
|
|
186
|
-
summary: {
|
|
187
|
-
added: result.added.length,
|
|
188
|
-
removed: result.removed.length,
|
|
189
|
-
capCountMismatches: capCountMismatches.length,
|
|
190
|
-
contextMissing: result.contextMissing.length,
|
|
191
|
-
contextStale: result.contextStale.length,
|
|
192
|
-
},
|
|
193
|
-
contextMissing: result.contextMissing.map(d => ({
|
|
194
|
-
dir: d,
|
|
195
|
-
file: `${d}/CONTEXT.md`,
|
|
196
|
-
})),
|
|
197
|
-
contextStale: result.contextStale.map(d => ({
|
|
198
|
-
dir: d,
|
|
199
|
-
file: `${d}/CONTEXT.md`,
|
|
200
|
-
})),
|
|
201
|
-
resolution: [],
|
|
202
|
-
};
|
|
203
|
-
if (capsIsCapabilityListing && hasCapIssues) {
|
|
204
|
-
jsonOutput.capCountMismatches = capCountMismatches.map(m => ({ mismatch: m }));
|
|
205
|
-
jsonOutput.resolution.push({
|
|
206
|
-
action: 'sync-capability-counts',
|
|
207
|
-
command: 'harness sync-docs',
|
|
208
|
-
details: 'CAPABILITIES.md 计数与代码不一致,运行 harness sync-docs 自动更新',
|
|
209
|
-
});
|
|
210
|
-
}
|
|
211
|
-
if (!capsIsCapabilityListing && hasTableIssues) {
|
|
212
|
-
jsonOutput.added = result.added.map(f => {
|
|
213
|
-
const getBasenameLocal = (s) => s.split('/').pop();
|
|
214
|
-
return {
|
|
215
|
-
file: f,
|
|
216
|
-
module: currentModules.find(m => getBasenameLocal(m.file) === f),
|
|
217
|
-
};
|
|
218
|
-
});
|
|
219
|
-
jsonOutput.removed = result.removed.map(f => ({ file: f }));
|
|
220
|
-
jsonOutput.resolution.push({
|
|
221
|
-
action: 'sync-capabilities',
|
|
222
|
-
command: 'harness sync-docs',
|
|
223
|
-
});
|
|
224
|
-
}
|
|
225
|
-
if (hasContextIssues) {
|
|
226
|
-
jsonOutput.resolution.push(...(result.contextMissing.length > 0
|
|
227
|
-
? [{ action: 'create-context-md', command: 'harness sync-docs', dirs: result.contextMissing }]
|
|
228
|
-
: []), ...(result.contextStale.length > 0
|
|
229
|
-
? [{ action: 'update-context-md', command: 'harness sync-docs', dirs: result.contextStale }]
|
|
230
|
-
: []));
|
|
231
|
-
}
|
|
232
|
-
if (options.agents) {
|
|
233
|
-
jsonOutput.agentsMd = { file: 'AGENTS.md', exists: agentsMdExists, stale: agentsMdStale };
|
|
234
|
-
if (agentsMdStale) {
|
|
235
|
-
jsonOutput.resolution.push({
|
|
236
|
-
action: 'sync-agents-md',
|
|
237
|
-
command: 'harness sync-docs --agents',
|
|
238
|
-
details: '生成/更新 AGENTS.md(agent 导读)',
|
|
239
|
-
});
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
console.log(JSON.stringify(jsonOutput, null, 2));
|
|
243
|
-
return !hasIssues;
|
|
244
|
-
}
|
|
245
|
-
// 6. 人读输出模式
|
|
246
|
-
if (capsIsCapabilityListing && hasCapIssues) {
|
|
247
|
-
console.log(chalk_1.default.yellow(`\n📊 CAPABILITIES.md 计数不一致:`));
|
|
248
|
-
capCountMismatches.forEach(m => console.log(chalk_1.default.gray(` - ${m}`)));
|
|
249
|
-
}
|
|
250
|
-
if (result.added.length > 0) {
|
|
251
|
-
console.log(chalk_1.default.yellow(`\n📄 CAPABILITIES.md 缺少以下模块:`));
|
|
252
|
-
result.added.forEach(f => console.log(chalk_1.default.gray(` + ${f}`)));
|
|
253
|
-
}
|
|
254
|
-
if (result.removed.length > 0) {
|
|
255
|
-
console.log(chalk_1.default.yellow(`\n📄 CAPABILITIES.md 包含已删除的模块:`));
|
|
256
|
-
result.removed.forEach(f => console.log(chalk_1.default.gray(` - ${f}`)));
|
|
257
|
-
}
|
|
258
|
-
if (result.contextMissing.length > 0) {
|
|
259
|
-
console.log(chalk_1.default.yellow(`\n📋 缺少 CONTEXT.md:`));
|
|
260
|
-
result.contextMissing.forEach(d => console.log(chalk_1.default.gray(` - ${d}/CONTEXT.md`)));
|
|
261
|
-
}
|
|
262
|
-
if (result.contextStale.length > 0) {
|
|
263
|
-
console.log(chalk_1.default.yellow(`\n📋 CONTEXT.md 可能过时(源码比文档新):`));
|
|
264
|
-
result.contextStale.forEach(d => console.log(chalk_1.default.gray(` - ${d}/CONTEXT.md`)));
|
|
265
|
-
}
|
|
266
|
-
if (hasAgentsIssues) {
|
|
267
|
-
console.log(chalk_1.default.yellow(agentsMdExists
|
|
268
|
-
? `\n🤖 AGENTS.md 与当前项目状态不一致:`
|
|
269
|
-
: `\n🤖 缺少 AGENTS.md(agent 导读):`));
|
|
270
|
-
console.log(chalk_1.default.gray(` - AGENTS.md`));
|
|
271
|
-
}
|
|
272
|
-
if (agentsMdMalformedPreserve.length > 0) {
|
|
273
|
-
console.log(chalk_1.default.yellow(`\n⚠️ AGENTS.md 中 PRESERVE 标记块未闭合(不予保留,重新生成将丢弃): ${agentsMdMalformedPreserve.join(', ')}`));
|
|
274
|
-
}
|
|
275
|
-
if (!hasIssues) {
|
|
276
|
-
console.log(chalk_1.default.green('✅ 所有文档都是最新的'));
|
|
277
|
-
return true;
|
|
278
|
-
}
|
|
279
|
-
// 7. 检查模式:只报告,不修改
|
|
280
|
-
if (isCheck) {
|
|
281
|
-
console.log(chalk_1.default.red('\n❌ 文档不是最新的,请运行 harness sync-docs 更新'));
|
|
282
|
-
return false;
|
|
283
|
-
}
|
|
284
|
-
// 8. 写入模式:更新文档
|
|
285
|
-
if (capsIsCapabilityListing && hasCapIssues) {
|
|
286
|
-
capsContent = updateCapabilityCounts(capsContent, projectPath);
|
|
287
|
-
await fs.writeFile(capabilitiesPath, capsContent, 'utf-8');
|
|
288
|
-
console.log(chalk_1.default.green(`\n✅ 已更新 CAPABILITIES.md 计数`));
|
|
289
|
-
}
|
|
290
|
-
if (!capsIsCapabilityListing && hasTableIssues) {
|
|
291
|
-
await updateCapabilitiesFile(capabilitiesPath, currentModules, existingFiles, result);
|
|
292
|
-
console.log(chalk_1.default.green(`\n✅ 已更新 CAPABILITIES.md`));
|
|
293
|
-
}
|
|
294
|
-
for (const dir of result.contextMissing) {
|
|
295
|
-
await createContextMd(projectPath, dir);
|
|
296
|
-
console.log(chalk_1.default.green(`✅ 已创建 ${dir}/CONTEXT.md`));
|
|
297
|
-
}
|
|
298
|
-
if (hasAgentsIssues && agentsMdExpected !== null) {
|
|
299
|
-
await fs.writeFile(path.join(projectPath, 'AGENTS.md'), agentsMdExpected, 'utf-8');
|
|
300
|
-
console.log(chalk_1.default.green(agentsMdExists ? `✅ 已更新 AGENTS.md` : `✅ 已生成 AGENTS.md`));
|
|
301
|
-
}
|
|
302
|
-
return !hasIssues;
|
|
303
|
-
}
|
|
304
|
-
/**
|
|
305
|
-
* 扫描源码目录,提取模块信息
|
|
306
|
-
*/
|
|
307
|
-
async function scanSourceModules(srcDir, projectPath) {
|
|
308
|
-
const modules = [];
|
|
309
|
-
let entries;
|
|
310
|
-
try {
|
|
311
|
-
entries = await fs.readdir(srcDir);
|
|
312
|
-
}
|
|
313
|
-
catch {
|
|
314
|
-
return [];
|
|
315
|
-
}
|
|
316
|
-
for (const entry of entries) {
|
|
317
|
-
// 跳过测试目录和非源码目录
|
|
318
|
-
if (entry === '__tests__' || entry === 'node_modules' || entry === 'dist')
|
|
319
|
-
continue;
|
|
320
|
-
const entryPath = path.join(srcDir, entry);
|
|
321
|
-
const stat = await fs.stat(entryPath);
|
|
322
|
-
if (stat.isDirectory()) {
|
|
323
|
-
// 子目录:递归扫描 .ts 文件(不报告目录条目本身)
|
|
324
|
-
const subFiles = await findTsFiles(entryPath);
|
|
325
|
-
for (const f of subFiles) {
|
|
326
|
-
modules.push({
|
|
327
|
-
name: path.basename(f, '.ts'),
|
|
328
|
-
file: path.relative(projectPath, f),
|
|
329
|
-
description: await extractFileDescription(f),
|
|
330
|
-
});
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
else if (entry.endsWith('.ts') && !entry.endsWith('.d.ts') && entry !== 'index.ts') {
|
|
334
|
-
modules.push({
|
|
335
|
-
name: path.basename(entry, '.ts'),
|
|
336
|
-
file: path.relative(projectPath, entryPath),
|
|
337
|
-
description: await extractFileDescription(entryPath),
|
|
338
|
-
});
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
return modules;
|
|
342
|
-
}
|
|
343
|
-
/**
|
|
344
|
-
* 递归查找 .ts 文件
|
|
345
|
-
*/
|
|
346
|
-
async function findTsFiles(dir) {
|
|
347
|
-
const results = [];
|
|
348
|
-
let entries;
|
|
349
|
-
try {
|
|
350
|
-
entries = await fs.readdir(dir);
|
|
351
|
-
}
|
|
352
|
-
catch {
|
|
353
|
-
return [];
|
|
354
|
-
}
|
|
355
|
-
for (const entry of entries) {
|
|
356
|
-
if (entry === 'node_modules' || entry === '__tests__' || entry === 'dist')
|
|
357
|
-
continue;
|
|
358
|
-
if (entry === 'index.ts')
|
|
359
|
-
continue; // barrel export
|
|
360
|
-
const entryPath = path.join(dir, entry);
|
|
361
|
-
const stat = await fs.stat(entryPath);
|
|
362
|
-
if (stat.isDirectory()) {
|
|
363
|
-
results.push(...await findTsFiles(entryPath));
|
|
364
|
-
}
|
|
365
|
-
else if (entry.endsWith('.ts') && !entry.endsWith('.d.ts')) {
|
|
366
|
-
results.push(entryPath);
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
return results;
|
|
370
|
-
}
|
|
371
|
-
/**
|
|
372
|
-
* 从文件提取描述
|
|
373
|
-
*/
|
|
374
|
-
async function extractFileDescription(filePath) {
|
|
375
|
-
try {
|
|
376
|
-
const content = await fs.readFile(filePath, 'utf-8');
|
|
377
|
-
return extractFirstComment(content) || path.basename(filePath, '.ts');
|
|
378
|
-
}
|
|
379
|
-
catch {
|
|
380
|
-
return path.basename(filePath, '.ts');
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
-
/**
|
|
384
|
-
* 提取文件第一行注释
|
|
385
|
-
*/
|
|
386
|
-
function extractFirstComment(content) {
|
|
387
|
-
// 匹配 /** ... */ 或 // ...
|
|
388
|
-
const jsdocMatch = content.match(/\/\*\*\s*\n\s*\*\s*(.+?)\s*\n/);
|
|
389
|
-
if (jsdocMatch)
|
|
390
|
-
return jsdocMatch[1];
|
|
391
|
-
const lineMatch = content.match(/^\/\/\s*(.+)$/m);
|
|
392
|
-
if (lineMatch)
|
|
393
|
-
return lineMatch[1];
|
|
394
|
-
return null;
|
|
395
|
-
}
|
|
396
|
-
/**
|
|
397
|
-
* 解析 CAPABILITIES.md 中的文件路径
|
|
398
|
-
*
|
|
399
|
-
* 提取所有表格行中提到的 .ts/.tsx/.js/.jsx 文件名(不含路径前缀)
|
|
400
|
-
* 用于模糊匹配:只要文档中提到了该文件名就算已记录
|
|
401
|
-
*/
|
|
402
|
-
async function parseCapabilitiesFiles(capabilitiesPath) {
|
|
403
|
-
const content = await fs.readFile(capabilitiesPath, 'utf-8');
|
|
404
|
-
const files = [];
|
|
405
|
-
// 匹配表格行中的文件名(可能带路径前缀)
|
|
406
|
-
const tableRowRegex = /\|\s*([^|]+?\.(?:ts|tsx|js|jsx))\s*\|/g;
|
|
407
|
-
let match;
|
|
408
|
-
while ((match = tableRowRegex.exec(content)) !== null) {
|
|
409
|
-
const raw = match[1].trim();
|
|
410
|
-
// 提取基础文件名(如 core/constraints/definitions.ts → definitions.ts)
|
|
411
|
-
const basename = raw.split('/').pop();
|
|
412
|
-
if (!files.includes(basename)) {
|
|
413
|
-
files.push(basename);
|
|
414
|
-
}
|
|
415
|
-
}
|
|
416
|
-
// 也匹配目录条目(如 agents/、gates/)
|
|
417
|
-
const dirRegex = /\|\s*([^|]+?\/)\s*\|/g;
|
|
418
|
-
while ((match = dirRegex.exec(content)) !== null) {
|
|
419
|
-
const dir = match[1].trim();
|
|
420
|
-
if (!files.includes(dir)) {
|
|
421
|
-
files.push(dir);
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
return files;
|
|
425
|
-
}
|
|
426
|
-
/**
|
|
427
|
-
* 自动发现已有 CONTEXT.md 文件的目录(相对于 projectPath)
|
|
428
|
-
*/
|
|
429
|
-
async function findExistingContextFiles(projectPath, srcDirs) {
|
|
430
|
-
const dirs = [];
|
|
431
|
-
async function scan(dir) {
|
|
432
|
-
let entries;
|
|
433
|
-
try {
|
|
434
|
-
entries = await fs.readdir(dir);
|
|
435
|
-
}
|
|
436
|
-
catch {
|
|
437
|
-
return;
|
|
438
|
-
}
|
|
439
|
-
for (const entry of entries) {
|
|
440
|
-
if (entry === 'node_modules' || entry === '__tests__' || entry === 'dist')
|
|
441
|
-
continue;
|
|
442
|
-
const entryPath = path.join(dir, entry);
|
|
443
|
-
const stat = await fs.stat(entryPath);
|
|
444
|
-
if (stat.isDirectory()) {
|
|
445
|
-
// 检查该目录是否有 CONTEXT.md
|
|
446
|
-
try {
|
|
447
|
-
await fs.access(path.join(entryPath, 'CONTEXT.md'));
|
|
448
|
-
dirs.push(path.relative(projectPath, entryPath));
|
|
449
|
-
}
|
|
450
|
-
catch {
|
|
451
|
-
// 没有,继续递归
|
|
452
|
-
}
|
|
453
|
-
await scan(entryPath);
|
|
454
|
-
}
|
|
455
|
-
}
|
|
456
|
-
}
|
|
457
|
-
for (const srcDir of srcDirs) {
|
|
458
|
-
await scan(path.join(projectPath, srcDir));
|
|
459
|
-
}
|
|
460
|
-
return dirs;
|
|
461
|
-
}
|
|
462
|
-
/**
|
|
463
|
-
* 获取目录下最新 .ts 文件的修改时间
|
|
464
|
-
* 返回 null 如果目录不存在或没有 .ts 文件
|
|
465
|
-
*/
|
|
466
|
-
async function getLatestTsMtime(dirPath) {
|
|
467
|
-
let latest = null;
|
|
468
|
-
async function scan(dir) {
|
|
469
|
-
let entries;
|
|
470
|
-
try {
|
|
471
|
-
entries = await fs.readdir(dir);
|
|
472
|
-
}
|
|
473
|
-
catch {
|
|
474
|
-
return;
|
|
475
|
-
}
|
|
476
|
-
for (const entry of entries) {
|
|
477
|
-
if (entry === 'node_modules' || entry === '__tests__' || entry === 'dist')
|
|
478
|
-
continue;
|
|
479
|
-
const entryPath = path.join(dir, entry);
|
|
480
|
-
const stat = await fs.stat(entryPath);
|
|
481
|
-
if (stat.isDirectory()) {
|
|
482
|
-
await scan(entryPath);
|
|
483
|
-
}
|
|
484
|
-
else if (entry.endsWith('.ts') && !entry.endsWith('.d.ts')) {
|
|
485
|
-
if (latest === null || stat.mtimeMs > latest) {
|
|
486
|
-
latest = stat.mtimeMs;
|
|
487
|
-
}
|
|
488
|
-
}
|
|
489
|
-
}
|
|
490
|
-
}
|
|
491
|
-
await scan(dirPath);
|
|
492
|
-
return latest;
|
|
493
|
-
}
|
|
494
|
-
/**
|
|
495
|
-
* 获取需要 CONTEXT.md 的目录列表
|
|
496
|
-
*/
|
|
497
|
-
async function getRequiredContextDirs(projectPath) {
|
|
498
|
-
const configPath = path.join(projectPath, '.harness', 'config.yml');
|
|
499
|
-
try {
|
|
500
|
-
const content = await fs.readFile(configPath, 'utf-8');
|
|
501
|
-
const config = yaml.load(content);
|
|
502
|
-
const governance = config.governance;
|
|
503
|
-
const contextFiles = governance?.context_files;
|
|
504
|
-
if (contextFiles?.enabled) {
|
|
505
|
-
if (Array.isArray(contextFiles.required_dirs) && contextFiles.required_dirs.length > 0) {
|
|
506
|
-
return contextFiles.required_dirs;
|
|
507
|
-
}
|
|
508
|
-
// Fallback: auto-discover from source roots
|
|
509
|
-
return (0, detect_source_roots_1.detectSourceRoots)(projectPath);
|
|
510
|
-
}
|
|
511
|
-
}
|
|
512
|
-
catch {
|
|
513
|
-
// 配置不存在或无法解析
|
|
514
|
-
}
|
|
515
|
-
return [];
|
|
516
|
-
}
|
|
517
|
-
/**
|
|
518
|
-
* 获取源码扫描目录列表
|
|
519
|
-
* 优先从 governance.context_files.required_dirs 读取,默认 ['src']
|
|
520
|
-
*/
|
|
521
|
-
async function getSourceDirs(projectPath) {
|
|
522
|
-
const requiredDirs = await getRequiredContextDirs(projectPath);
|
|
523
|
-
if (requiredDirs.length > 0)
|
|
524
|
-
return requiredDirs;
|
|
525
|
-
return (0, detect_source_roots_1.detectSourceRoots)(projectPath);
|
|
526
|
-
}
|
|
527
|
-
/**
|
|
528
|
-
* 更新 CAPABILITIES.md 文件
|
|
529
|
-
*/
|
|
530
|
-
async function updateCapabilitiesFile(capabilitiesPath, currentModules, existingFiles, result) {
|
|
531
|
-
let content;
|
|
532
|
-
try {
|
|
533
|
-
content = await fs.readFile(capabilitiesPath, 'utf-8');
|
|
534
|
-
}
|
|
535
|
-
catch {
|
|
536
|
-
// 文件不存在,创建新的
|
|
537
|
-
content = generateCapabilitiesContent(currentModules);
|
|
538
|
-
await fs.writeFile(capabilitiesPath, content, 'utf-8');
|
|
539
|
-
return;
|
|
540
|
-
}
|
|
541
|
-
// 如果有表格行,更新表格
|
|
542
|
-
if (existingFiles.length > 0) {
|
|
543
|
-
// 移除已删除文件的行
|
|
544
|
-
for (const removed of result.removed) {
|
|
545
|
-
const escapedFile = removed.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
546
|
-
// 第二列存完整路径,basename 只在末尾出现,用 [^|]* 匹配路径前缀
|
|
547
|
-
const rowRegex = new RegExp(`^\\|[^|]*\\|[^|]*\\b${escapedFile}\\s*\\|.*$`, 'gm');
|
|
548
|
-
content = content.replace(rowRegex, '');
|
|
549
|
-
}
|
|
550
|
-
// 添加新文件的行(在最后一个表格行之后)
|
|
551
|
-
if (result.added.length > 0) {
|
|
552
|
-
const getBasenameLocal = (f) => f.split('/').pop();
|
|
553
|
-
const addedModules = currentModules.filter(m => result.added.includes(getBasenameLocal(m.file)));
|
|
554
|
-
const tableEndRegex = /(^\|[^|]+\|[^|]+\|[^|]+\|\s*$)/gm;
|
|
555
|
-
let lastTableRow = '';
|
|
556
|
-
let match;
|
|
557
|
-
while ((match = tableEndRegex.exec(content)) !== null) {
|
|
558
|
-
lastTableRow = match[0];
|
|
559
|
-
}
|
|
560
|
-
if (lastTableRow) {
|
|
561
|
-
const newRows = addedModules.map(m => `| ${m.name} | ${m.file} | ${m.description} |`).join('\n');
|
|
562
|
-
content = content.replace(lastTableRow, lastTableRow + '\n' + newRows);
|
|
563
|
-
}
|
|
564
|
-
}
|
|
565
|
-
// 清理多余空行
|
|
566
|
-
content = content.replace(/\n{3,}/g, '\n\n');
|
|
567
|
-
}
|
|
568
|
-
else {
|
|
569
|
-
// 没有表格,追加模块表格
|
|
570
|
-
content += '\n\n' + generateModuleTable(currentModules);
|
|
571
|
-
}
|
|
572
|
-
// 更新最后更新时间
|
|
573
|
-
const now = new Date().toISOString().split('T')[0];
|
|
574
|
-
content = content.replace(/最后更新[::].*/, `最后更新: ${now}`);
|
|
575
|
-
await fs.writeFile(capabilitiesPath, content, 'utf-8');
|
|
576
|
-
}
|
|
577
|
-
/**
|
|
578
|
-
* 生成 CAPABILITIES.md 内容
|
|
579
|
-
*/
|
|
580
|
-
function generateCapabilitiesContent(modules) {
|
|
581
|
-
const now = new Date().toISOString().split('T')[0];
|
|
582
|
-
return `# CAPABILITIES.md
|
|
583
|
-
|
|
584
|
-
> 最后更新: ${now}
|
|
585
|
-
|
|
586
|
-
---
|
|
587
|
-
|
|
588
|
-
${generateModuleTable(modules)}
|
|
589
|
-
`;
|
|
590
|
-
}
|
|
591
|
-
/**
|
|
592
|
-
* 生成模块表格
|
|
593
|
-
*/
|
|
594
|
-
function generateModuleTable(modules) {
|
|
595
|
-
if (modules.length === 0)
|
|
596
|
-
return '';
|
|
597
|
-
const rows = modules.map(m => `| ${m.name} | ${m.file} | ${m.description} |`).join('\n');
|
|
598
|
-
return `| 模块 | 文件 | 说明 |\n|------|------|------|\n${rows}`;
|
|
599
|
-
}
|
|
600
|
-
/**
|
|
601
|
-
* 创建 CONTEXT.md 模板
|
|
602
|
-
*/
|
|
603
|
-
async function createContextMd(projectPath, dir) {
|
|
604
|
-
const contextPath = path.join(projectPath, dir, 'CONTEXT.md');
|
|
605
|
-
const dirName = path.basename(dir);
|
|
606
|
-
const content = `# ${dirName}
|
|
607
|
-
|
|
608
|
-
> 此文件描述 ${dir} 目录的职责和上下文。
|
|
609
|
-
> 请阅读本目录的源代码,然后填写以下各节。
|
|
610
|
-
> 如果使用 AI 编码助手,将本文件内容作为 prompt 请求它分析并填写。
|
|
611
|
-
|
|
612
|
-
## 职责
|
|
613
|
-
|
|
614
|
-
本目录的核心职责是?
|
|
615
|
-
|
|
616
|
-
## 核心导出
|
|
617
|
-
|
|
618
|
-
本目录对外暴露的主要模块/函数:
|
|
619
|
-
|
|
620
|
-
## 依赖关系
|
|
621
|
-
|
|
622
|
-
本目录依赖哪些其他模块,谁依赖本目录?
|
|
623
|
-
|
|
624
|
-
## 注意事项
|
|
625
|
-
|
|
626
|
-
开发时需要注意的约束或约定:
|
|
627
|
-
`;
|
|
628
|
-
await fs.mkdir(path.join(projectPath, dir), { recursive: true });
|
|
629
|
-
await fs.writeFile(contextPath, content, 'utf-8');
|
|
630
|
-
}
|
|
631
|
-
// ========================================
|
|
632
|
-
// 能力清单格式支持 (capability-listing)
|
|
633
|
-
// ========================================
|
|
634
|
-
/**
|
|
635
|
-
* 检测 CAPABILITIES.md 是否使用能力清单格式(计数行),而非文件表格格式
|
|
636
|
-
*/
|
|
637
|
-
function isCapabilityListingFormat(content) {
|
|
638
|
-
// 能力清单格式特征:包含 "CLI Commands (N)" / "Iron Laws (N)" 等计数行
|
|
639
|
-
return /CLI Commands\s*\(\d+\)/.test(content) ||
|
|
640
|
-
/Iron Laws?\s*\(\d+\)/.test(content) ||
|
|
641
|
-
/Guidelines?\s*\(\d+\)/.test(content);
|
|
642
|
-
}
|
|
643
|
-
/**
|
|
644
|
-
* 从文件表格格式中提取文件路径
|
|
645
|
-
*/
|
|
646
|
-
function extractTableFiles(content) {
|
|
647
|
-
const files = [];
|
|
648
|
-
const tableRowRegex = /\|\s*([^|]+?\.(?:ts|tsx|js|jsx))\s*\|/g;
|
|
649
|
-
let match;
|
|
650
|
-
while ((match = tableRowRegex.exec(content)) !== null) {
|
|
651
|
-
const raw = match[1].trim();
|
|
652
|
-
const basename = raw.split('/').pop();
|
|
653
|
-
if (!files.includes(basename)) {
|
|
654
|
-
files.push(basename);
|
|
655
|
-
}
|
|
656
|
-
}
|
|
657
|
-
return files;
|
|
658
|
-
}
|
|
659
|
-
/**
|
|
660
|
-
* 构建 CAPABILITIES.md 能力清单格式的检查配置
|
|
661
|
-
*/
|
|
662
|
-
function buildCapabilityChecks() {
|
|
663
|
-
return [
|
|
664
|
-
{
|
|
665
|
-
type: 'doc_regex_count',
|
|
666
|
-
doc: 'CAPABILITIES.md',
|
|
667
|
-
label: 'CLI Commands',
|
|
668
|
-
pattern: 'CLI Commands\\s*\\((\\d+)\\)',
|
|
669
|
-
actual: { kind: 'dir_count', path: 'src/cli/commands', extension: '.ts', exclude: ['index.ts'] },
|
|
670
|
-
},
|
|
671
|
-
{
|
|
672
|
-
type: 'doc_regex_count',
|
|
673
|
-
doc: 'CAPABILITIES.md',
|
|
674
|
-
label: 'Quality Gates',
|
|
675
|
-
pattern: 'Quality Gates?\\s*\\((\\d+)\\)',
|
|
676
|
-
actual: { kind: 'dir_count', path: 'src/gates', extension: '.ts', exclude: ['index.ts', 'types.ts'] },
|
|
677
|
-
},
|
|
678
|
-
{
|
|
679
|
-
type: 'doc_regex_count',
|
|
680
|
-
doc: 'CAPABILITIES.md',
|
|
681
|
-
label: 'Iron Laws',
|
|
682
|
-
pattern: 'Iron Laws?\\s*\\((\\d+)\\)',
|
|
683
|
-
actual: { kind: 'const_count', value: Object.keys(definitions_1.IRON_LAWS).length },
|
|
684
|
-
},
|
|
685
|
-
{
|
|
686
|
-
type: 'doc_regex_count',
|
|
687
|
-
doc: 'CAPABILITIES.md',
|
|
688
|
-
label: 'Guidelines',
|
|
689
|
-
pattern: 'Guidelines?\\s*\\((\\d+)\\)',
|
|
690
|
-
actual: { kind: 'const_count', value: Object.keys(definitions_1.GUIDELINES).length },
|
|
691
|
-
},
|
|
692
|
-
{
|
|
693
|
-
type: 'doc_regex_count',
|
|
694
|
-
doc: 'CAPABILITIES.md',
|
|
695
|
-
label: 'Tips',
|
|
696
|
-
pattern: 'Tips?\\s*\\((\\d+)\\)',
|
|
697
|
-
actual: { kind: 'const_count', value: Object.keys(definitions_1.TIPS).length },
|
|
698
|
-
},
|
|
699
|
-
];
|
|
700
|
-
}
|
|
701
|
-
/**
|
|
702
|
-
* 使用 FreshnessRunner 检查能力清单计数是否与代码一致(--check 模式)
|
|
703
|
-
*/
|
|
704
|
-
function checkCapabilityCounts(projectPath) {
|
|
705
|
-
const runner = new runner_1.FreshnessRunner();
|
|
706
|
-
const checks = buildCapabilityChecks();
|
|
707
|
-
const results = runner.runAll({ checks }, projectPath);
|
|
708
|
-
const mismatches = [];
|
|
709
|
-
for (const r of results) {
|
|
710
|
-
if (!r.pass && r.message) {
|
|
711
|
-
mismatches.push(r.message);
|
|
712
|
-
}
|
|
713
|
-
}
|
|
714
|
-
return { match: mismatches.length === 0, mismatches };
|
|
715
|
-
}
|
|
716
|
-
/**
|
|
717
|
-
* 更新 CAPABILITIES.md 中的能力清单计数(write 模式)
|
|
718
|
-
*
|
|
719
|
-
* 使用 FreshnessRunner 获取实际计数,然后 regex 替换文档计数行。
|
|
720
|
-
*/
|
|
721
|
-
function updateCapabilityCounts(content, projectPath) {
|
|
722
|
-
const runner = new runner_1.FreshnessRunner();
|
|
723
|
-
const cliCount = runner.countFromDir('src/cli/commands', '.ts', ['index.ts'], projectPath);
|
|
724
|
-
const gateCount = runner.countFromDir('src/gates', '.ts', ['index.ts', 'types.ts'], projectPath);
|
|
725
|
-
const ironCount = Object.keys(definitions_1.IRON_LAWS).length;
|
|
726
|
-
const guideCount = Object.keys(definitions_1.GUIDELINES).length;
|
|
727
|
-
const tipsCount = Object.keys(definitions_1.TIPS).length;
|
|
728
|
-
const replacements = [
|
|
729
|
-
[/CLI Commands\s*\(\d+\)/, `CLI Commands (${cliCount})`],
|
|
730
|
-
[/Quality Gates?\s*\(\d+\)/, `Quality Gates (${gateCount})`],
|
|
731
|
-
[/Iron Laws?\s*\(\d+\)/, `Iron Laws (${ironCount})`],
|
|
732
|
-
[/Guidelines?\s*\(\d+\)/, `Guidelines (${guideCount})`],
|
|
733
|
-
[/Tips?\s*\(\d+\)/, `Tips (${tipsCount})`],
|
|
734
|
-
];
|
|
735
|
-
for (const [regex, replacement] of replacements) {
|
|
736
|
-
if (regex.test(content)) {
|
|
737
|
-
content = content.replace(regex, replacement);
|
|
738
|
-
}
|
|
739
|
-
}
|
|
740
|
-
return content;
|
|
741
|
-
}
|
|
742
|
-
// ========================================
|
|
743
|
-
// AGENTS.md 生成(--agents)
|
|
744
|
-
// ========================================
|
|
745
|
-
/** 生成 AGENTS.md 目录结构表时跳过的目录(依赖/构建产物/报告等) */
|
|
746
|
-
const AGENTS_MD_SKIP_DIRS = new Set([
|
|
747
|
-
'node_modules', 'dist', 'build', 'out', 'coverage',
|
|
748
|
-
'playwright-report', 'test-results', '.git', '.next', '.turbo',
|
|
749
|
-
]);
|
|
750
|
-
/** 知名顶层目录的一句话说明 */
|
|
751
|
-
const AGENTS_MD_DIR_ROLES = {
|
|
752
|
-
docs: '项目文档',
|
|
753
|
-
scripts: '工具脚本',
|
|
754
|
-
tests: '测试',
|
|
755
|
-
test: '测试',
|
|
756
|
-
bin: '可执行入口/脚本',
|
|
757
|
-
templates: '项目模板',
|
|
758
|
-
'.github': 'CI/CD 配置',
|
|
759
|
-
'.harness': 'harness 配置与运行时状态',
|
|
760
|
-
};
|
|
761
|
-
/** 常用命令候选(按展示顺序),仅列出 package.json 中实际存在的脚本 */
|
|
762
|
-
const AGENTS_MD_COMMANDS = [
|
|
763
|
-
['dev', '启动开发环境'],
|
|
764
|
-
['build', '构建'],
|
|
765
|
-
['test', '运行测试'],
|
|
766
|
-
['test:e2e', '端到端测试'],
|
|
767
|
-
['test:unit', '单元测试'],
|
|
768
|
-
['typecheck', '类型检查'],
|
|
769
|
-
['lint', '代码检查'],
|
|
770
|
-
['start', '启动生产服务'],
|
|
771
|
-
];
|
|
772
|
-
/**
|
|
773
|
-
* 生成 AGENTS.md 内容(agent 导读:项目简介/目录结构/常用命令/约束治理/知识入口)
|
|
774
|
-
*
|
|
775
|
-
* 纯静态提取(零 LLM),确定性输出:同一代码库状态生成同一内容(无时间戳),
|
|
776
|
-
* 因此重跑幂等。既有文件中的 PRESERVE 标记块由 composeAgentsMd 组合回输出,
|
|
777
|
-
* --check 对组合结果做文本对比检测漂移(块内手改不报漂移)。
|
|
778
|
-
*/
|
|
779
|
-
async function buildAgentsMd(projectPath, srcDirs) {
|
|
780
|
-
const pkg = await readPackageJsonLite(projectPath);
|
|
781
|
-
const [dirs, governance, hasKnowledge, contextDocCount] = await Promise.all([
|
|
782
|
-
getTopLevelDirEntries(projectPath, srcDirs),
|
|
783
|
-
getGovernanceInfo(projectPath),
|
|
784
|
-
hasKnowledgeDir(projectPath),
|
|
785
|
-
countContextDocs(projectPath, srcDirs),
|
|
786
|
-
]);
|
|
787
|
-
const name = pkg?.name || path.basename(projectPath);
|
|
788
|
-
const description = pkg?.description || await getConfigDescription(projectPath);
|
|
789
|
-
const lines = [
|
|
790
|
-
'# AGENTS.md',
|
|
791
|
-
'',
|
|
792
|
-
'> 本文件由 `harness sync-docs --agents` 自动生成,请勿手改。`<!-- PRESERVE:名称 -->` 与 `<!-- /PRESERVE:名称 -->` 之间的内容在重新生成时原样保留。内容漂移时重新运行该命令更新。',
|
|
793
|
-
'',
|
|
794
|
-
'## 项目简介',
|
|
795
|
-
'',
|
|
796
|
-
description ? `**${name}** — ${description}` : `**${name}**`,
|
|
797
|
-
'',
|
|
798
|
-
'## 目录结构',
|
|
799
|
-
'',
|
|
800
|
-
];
|
|
801
|
-
if (dirs.length > 0) {
|
|
802
|
-
lines.push('| 目录 | 说明 |', '|------|------|');
|
|
803
|
-
for (const d of dirs) {
|
|
804
|
-
lines.push(`| \`${d.dir}/\` | ${d.role} |`);
|
|
805
|
-
}
|
|
806
|
-
}
|
|
807
|
-
else {
|
|
808
|
-
lines.push('(未检测到顶层目录)');
|
|
809
|
-
}
|
|
810
|
-
lines.push('', '## 常用命令', '');
|
|
811
|
-
const pm = detectPackageManager(projectPath);
|
|
812
|
-
const scripts = pkg?.scripts || {};
|
|
813
|
-
const commands = AGENTS_MD_COMMANDS.filter(([n]) => scripts[n]);
|
|
814
|
-
if (commands.length > 0) {
|
|
815
|
-
lines.push('```bash');
|
|
816
|
-
for (const [n, comment] of commands) {
|
|
817
|
-
lines.push(`${formatScriptCommand(pm, n)} # ${comment}`);
|
|
818
|
-
}
|
|
819
|
-
lines.push('```');
|
|
820
|
-
}
|
|
821
|
-
else {
|
|
822
|
-
lines.push('(package.json 中未检测到常用脚本)');
|
|
823
|
-
}
|
|
824
|
-
lines.push('', '## 约束与治理', '');
|
|
825
|
-
if (governance.hasConfig) {
|
|
826
|
-
lines.push(`- 治理配置:\`.harness/config.yml\`${governance.preset ? `(preset: ${governance.preset})` : ''}`);
|
|
827
|
-
}
|
|
828
|
-
if (governance.hasClaudeGovernance) {
|
|
829
|
-
const counts = [
|
|
830
|
-
governance.ironLaws !== undefined ? `Iron Laws ${governance.ironLaws} 条` : null,
|
|
831
|
-
governance.guidelines !== undefined ? `Guidelines ${governance.guidelines} 条` : null,
|
|
832
|
-
].filter(Boolean).join('、');
|
|
833
|
-
lines.push(`- 约束清单:\`CLAUDE.md\` Governance Rules 块${counts ? `(${counts})` : ''}`);
|
|
834
|
-
}
|
|
835
|
-
if (!governance.hasConfig && !governance.hasClaudeGovernance) {
|
|
836
|
-
lines.push('- 未检测到 harness 治理配置,可运行 `harness init` 初始化');
|
|
837
|
-
}
|
|
838
|
-
lines.push('', '## 知识入口', '');
|
|
839
|
-
if (hasKnowledge) {
|
|
840
|
-
// 不写条目数:.harness/knowledge 通常不入 git,条数是环境状态,
|
|
841
|
-
// 写进受 --check 漂移比对的文档会制造「必然过期」竞态(任何知识写入都使已提交文档过期)
|
|
842
|
-
lines.push('- `.harness/knowledge/`:项目知识库,用 `harness knowledge` 查询');
|
|
843
|
-
}
|
|
844
|
-
lines.push(contextDocCount > 0
|
|
845
|
-
? `- 各源码目录的 \`CONTEXT.md\` 是权威模块文档(现有 ${contextDocCount} 个),改动代码时同步更新`
|
|
846
|
-
: '- 各源码目录的 `CONTEXT.md` 是权威模块文档,缺失目录可由 `harness sync-docs` 生成模板');
|
|
847
|
-
lines.push('');
|
|
848
|
-
return lines.join('\n');
|
|
849
|
-
}
|
|
850
|
-
// AGENTS.md PRESERVE 标记块(--agents)
|
|
851
|
-
//
|
|
852
|
-
// 使用者在 AGENTS.md 中用 `<!-- PRESERVE:名称 -->` / `<!-- /PRESERVE:名称 -->`
|
|
853
|
-
// 圈出的区段属于使用者自有内容:重新生成时原样保留(附于生成内容之后,保持相对顺序),
|
|
854
|
-
// 漂移比对对块内改动免疫。名称仅允许字母/数字/下划线/连字符,标记须独占一行。
|
|
855
|
-
/** PRESERVE 开始标记:独占一行,捕获名称 */
|
|
856
|
-
const PRESERVE_BEGIN_RE = /^<!-- PRESERVE:([A-Za-z0-9_-]+) -->\s*$/;
|
|
857
|
-
/**
|
|
858
|
-
* 从既有 AGENTS.md 提取 PRESERVE 标记块(逐行扫描,结束标记须与开始标记同名)。
|
|
859
|
-
* 块内容原样保留(含标记行);未闭合的开始标记按普通内容处理并记入 malformed。
|
|
860
|
-
*/
|
|
861
|
-
function extractPreserveBlocks(content) {
|
|
862
|
-
const blocks = [];
|
|
863
|
-
const malformed = [];
|
|
864
|
-
const lines = content.split('\n');
|
|
865
|
-
let i = 0;
|
|
866
|
-
while (i < lines.length) {
|
|
867
|
-
const m = lines[i].match(PRESERVE_BEGIN_RE);
|
|
868
|
-
if (!m) {
|
|
869
|
-
i++;
|
|
870
|
-
continue;
|
|
871
|
-
}
|
|
872
|
-
const name = m[1];
|
|
873
|
-
const endMarker = `<!-- /PRESERVE:${name} -->`;
|
|
874
|
-
let j = i + 1;
|
|
875
|
-
while (j < lines.length && lines[j].trim() !== endMarker) {
|
|
876
|
-
j++;
|
|
877
|
-
}
|
|
878
|
-
if (j >= lines.length) {
|
|
879
|
-
malformed.push(name);
|
|
880
|
-
i++; // 未闭合:跳过开始标记行,其余行按普通内容继续扫描
|
|
881
|
-
continue;
|
|
882
|
-
}
|
|
883
|
-
blocks.push(lines.slice(i, j + 1).join('\n'));
|
|
884
|
-
i = j + 1;
|
|
885
|
-
}
|
|
886
|
-
return { blocks, malformed };
|
|
887
|
-
}
|
|
888
|
-
/**
|
|
889
|
-
* 组合 AGENTS.md 最终内容:生成部分在前,PRESERVE 块按原序附在文末(空行分隔)。
|
|
890
|
-
* 无保留块时原样返回生成内容(与历史行为一致);组合结果重跑幂等。
|
|
891
|
-
*/
|
|
892
|
-
function composeAgentsMd(generated, blocks) {
|
|
893
|
-
if (blocks.length === 0)
|
|
894
|
-
return generated;
|
|
895
|
-
const head = generated.replace(/\s*$/, '');
|
|
896
|
-
return head + '\n\n' + blocks.join('\n\n') + '\n';
|
|
897
|
-
}
|
|
898
|
-
/** 读取 package.json(不存在或无法解析时返回 null) */
|
|
899
|
-
async function readPackageJsonLite(dir) {
|
|
900
|
-
try {
|
|
901
|
-
return JSON.parse(await fs.readFile(path.join(dir, 'package.json'), 'utf-8'));
|
|
902
|
-
}
|
|
903
|
-
catch {
|
|
904
|
-
return null;
|
|
905
|
-
}
|
|
906
|
-
}
|
|
907
|
-
/** 从 .harness/config.yml 读取项目描述(package.json 无 description 时的兜底) */
|
|
908
|
-
async function getConfigDescription(projectPath) {
|
|
909
|
-
try {
|
|
910
|
-
const content = await fs.readFile(path.join(projectPath, '.harness', 'config.yml'), 'utf-8');
|
|
911
|
-
const config = yaml.load(content);
|
|
912
|
-
return typeof config?.description === 'string' ? config.description : '';
|
|
913
|
-
}
|
|
914
|
-
catch {
|
|
915
|
-
return '';
|
|
916
|
-
}
|
|
917
|
-
}
|
|
918
|
-
/** 检测包管理器(决定命令前缀):pnpm workspace/lockfile → yarn lockfile → 默认 npm */
|
|
919
|
-
function detectPackageManager(projectPath) {
|
|
920
|
-
if ((0, fs_1.existsSync)(path.join(projectPath, 'pnpm-workspace.yaml')) || (0, fs_1.existsSync)(path.join(projectPath, 'pnpm-lock.yaml'))) {
|
|
921
|
-
return 'pnpm';
|
|
922
|
-
}
|
|
923
|
-
if ((0, fs_1.existsSync)(path.join(projectPath, 'yarn.lock')))
|
|
924
|
-
return 'yarn';
|
|
925
|
-
return 'npm';
|
|
926
|
-
}
|
|
927
|
-
/** 格式化脚本调用方式(npm 的 start/test 可省略 run) */
|
|
928
|
-
function formatScriptCommand(pm, name) {
|
|
929
|
-
if (pm === 'pnpm')
|
|
930
|
-
return `pnpm ${name}`;
|
|
931
|
-
if (pm === 'yarn')
|
|
932
|
-
return `yarn ${name}`;
|
|
933
|
-
return name === 'start' || name === 'test' ? `npm ${name}` : `npm run ${name}`;
|
|
934
|
-
}
|
|
935
|
-
/** 收集顶层目录及其一句话说明(排序保证确定性) */
|
|
936
|
-
async function getTopLevelDirEntries(projectPath, srcDirs) {
|
|
937
|
-
let dirents;
|
|
938
|
-
try {
|
|
939
|
-
dirents = await fs.readdir(projectPath, { withFileTypes: true });
|
|
940
|
-
}
|
|
941
|
-
catch {
|
|
942
|
-
return [];
|
|
943
|
-
}
|
|
944
|
-
const entries = [];
|
|
945
|
-
for (const d of dirents) {
|
|
946
|
-
if (!d.isDirectory())
|
|
947
|
-
continue;
|
|
948
|
-
if (AGENTS_MD_SKIP_DIRS.has(d.name))
|
|
949
|
-
continue;
|
|
950
|
-
// 隐藏目录只保留 .harness / .github
|
|
951
|
-
if (d.name.startsWith('.') && d.name !== '.harness' && d.name !== '.github')
|
|
952
|
-
continue;
|
|
953
|
-
entries.push({ dir: d.name, role: await describeTopLevelDir(projectPath, d.name, srcDirs) });
|
|
954
|
-
}
|
|
955
|
-
entries.sort((a, b) => (a.dir < b.dir ? -1 : a.dir > b.dir ? 1 : 0));
|
|
956
|
-
return entries;
|
|
957
|
-
}
|
|
958
|
-
/** 顶层目录的一句话说明:知名目录 → monorepo 成员 → 源码根 → 子包 description → 占位 */
|
|
959
|
-
async function describeTopLevelDir(projectPath, dir, srcDirs) {
|
|
960
|
-
const known = AGENTS_MD_DIR_ROLES[dir];
|
|
961
|
-
if (known)
|
|
962
|
-
return known;
|
|
963
|
-
if (dir === 'apps' || dir === 'packages') {
|
|
964
|
-
const members = await listWorkspaceMembers(path.join(projectPath, dir));
|
|
965
|
-
if (members.length > 0) {
|
|
966
|
-
return `monorepo ${dir === 'apps' ? '应用' : '共享包'}:${members.join('、')}`;
|
|
967
|
-
}
|
|
968
|
-
return 'monorepo 工作区';
|
|
969
|
-
}
|
|
970
|
-
if (srcDirs.includes(dir))
|
|
971
|
-
return '源码目录';
|
|
972
|
-
const subPkg = await readPackageJsonLite(path.join(projectPath, dir));
|
|
973
|
-
if (subPkg?.description)
|
|
974
|
-
return subPkg.description;
|
|
975
|
-
return '—';
|
|
976
|
-
}
|
|
977
|
-
/** 列出 monorepo 工作区成员(含 package.json 的子目录名,排序) */
|
|
978
|
-
async function listWorkspaceMembers(dir) {
|
|
979
|
-
try {
|
|
980
|
-
const dirents = await fs.readdir(dir, { withFileTypes: true });
|
|
981
|
-
const members = [];
|
|
982
|
-
for (const d of dirents) {
|
|
983
|
-
if (!d.isDirectory() || d.name.startsWith('.'))
|
|
984
|
-
continue;
|
|
985
|
-
if ((0, fs_1.existsSync)(path.join(dir, d.name, 'package.json'))) {
|
|
986
|
-
members.push(d.name);
|
|
987
|
-
}
|
|
988
|
-
}
|
|
989
|
-
return members.sort();
|
|
990
|
-
}
|
|
991
|
-
catch {
|
|
992
|
-
return [];
|
|
993
|
-
}
|
|
994
|
-
}
|
|
995
|
-
/** 收集治理信息:.harness/config.yml 与 CLAUDE.md 治理块(含铁律/指南计数) */
|
|
996
|
-
async function getGovernanceInfo(projectPath) {
|
|
997
|
-
const info = { hasConfig: false, hasClaudeGovernance: false };
|
|
998
|
-
try {
|
|
999
|
-
const content = await fs.readFile(path.join(projectPath, '.harness', 'config.yml'), 'utf-8');
|
|
1000
|
-
const config = yaml.load(content);
|
|
1001
|
-
info.hasConfig = true;
|
|
1002
|
-
if (config && typeof config.preset === 'string') {
|
|
1003
|
-
info.preset = config.preset;
|
|
1004
|
-
}
|
|
1005
|
-
}
|
|
1006
|
-
catch {
|
|
1007
|
-
// 配置不存在
|
|
1008
|
-
}
|
|
1009
|
-
try {
|
|
1010
|
-
const claude = await fs.readFile(path.join(projectPath, 'CLAUDE.md'), 'utf-8');
|
|
1011
|
-
if (/^##\s+Governance Rules/m.test(claude) || claude.includes('HARNESS_CONSTRAINTS_START')) {
|
|
1012
|
-
info.hasClaudeGovernance = true;
|
|
1013
|
-
info.ironLaws = countConstraintBullets(claude, 'Iron Laws');
|
|
1014
|
-
info.guidelines = countConstraintBullets(claude, 'Guidelines');
|
|
1015
|
-
}
|
|
1016
|
-
}
|
|
1017
|
-
catch {
|
|
1018
|
-
// CLAUDE.md 不存在
|
|
1019
|
-
}
|
|
1020
|
-
return info;
|
|
1021
|
-
}
|
|
1022
|
-
/** 统计 CLAUDE.md 指定约束分节下的条目数(`- **key**:` 行),分节不存在时返回 undefined */
|
|
1023
|
-
function countConstraintBullets(content, section) {
|
|
1024
|
-
const heading = new RegExp(`^###\\s+${section}[^\\n]*$`, 'm').exec(content);
|
|
1025
|
-
if (!heading)
|
|
1026
|
-
return undefined;
|
|
1027
|
-
const rest = content.slice(heading.index + heading[0].length);
|
|
1028
|
-
const end = rest.search(/^###\s|^\s*<!--\s*HARNESS_CONSTRAINTS_END/m);
|
|
1029
|
-
const block = end === -1 ? rest : rest.slice(0, end);
|
|
1030
|
-
const items = block.match(/^-\s+\*\*[A-Za-z0-9_]+\*\*/gm);
|
|
1031
|
-
return items ? items.length : 0;
|
|
1032
|
-
}
|
|
1033
|
-
/** 判断 .harness/knowledge 目录是否存在(只判存在性,不统计条数——条数易变,见 buildAgentsMd 注释) */
|
|
1034
|
-
async function hasKnowledgeDir(projectPath) {
|
|
1035
|
-
try {
|
|
1036
|
-
return (await fs.stat(path.join(projectPath, '.harness', 'knowledge'))).isDirectory();
|
|
1037
|
-
}
|
|
1038
|
-
catch {
|
|
1039
|
-
return false;
|
|
1040
|
-
}
|
|
1041
|
-
}
|
|
1042
|
-
/** 统计源码根内的 CONTEXT.md 数量(含源码根本身,跨根去重) */
|
|
1043
|
-
async function countContextDocs(projectPath, srcDirs) {
|
|
1044
|
-
const found = new Set();
|
|
1045
|
-
async function scan(dir) {
|
|
1046
|
-
let entries;
|
|
1047
|
-
try {
|
|
1048
|
-
entries = await fs.readdir(dir, { withFileTypes: true });
|
|
1049
|
-
}
|
|
1050
|
-
catch {
|
|
1051
|
-
return;
|
|
1052
|
-
}
|
|
1053
|
-
for (const entry of entries) {
|
|
1054
|
-
if (!entry.isDirectory())
|
|
1055
|
-
continue;
|
|
1056
|
-
if (entry.name === 'node_modules' || entry.name === '__tests__' || entry.name === 'dist')
|
|
1057
|
-
continue;
|
|
1058
|
-
const entryPath = path.join(dir, entry.name);
|
|
1059
|
-
if ((0, fs_1.existsSync)(path.join(entryPath, 'CONTEXT.md'))) {
|
|
1060
|
-
found.add(entryPath);
|
|
1061
|
-
}
|
|
1062
|
-
await scan(entryPath);
|
|
1063
|
-
}
|
|
1064
|
-
}
|
|
1065
|
-
for (const root of srcDirs) {
|
|
1066
|
-
const rootPath = path.join(projectPath, root);
|
|
1067
|
-
if ((0, fs_1.existsSync)(path.join(rootPath, 'CONTEXT.md'))) {
|
|
1068
|
-
found.add(rootPath);
|
|
1069
|
-
}
|
|
1070
|
-
await scan(rootPath);
|
|
1071
|
-
}
|
|
1072
|
-
return found.size;
|
|
1073
|
-
}
|
|
1074
|
-
//# sourceMappingURL=sync-docs.js.map
|