code-auditor-mcp 3.1.1 → 3.4.1
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/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +724 -1
- package/README.md +85 -11
- package/dist/__test_tmp__/spec18_test.d.ts +2 -0
- package/dist/__test_tmp__/spec18_test.d.ts.map +1 -0
- package/dist/__test_tmp__/spec18_test.js +5 -0
- package/dist/__test_tmp__/spec18_test.js.map +1 -0
- package/dist/__tests__/fixtures/spec-20/scripts/tool.d.ts +6 -0
- package/dist/__tests__/fixtures/spec-20/scripts/tool.d.ts.map +1 -0
- package/dist/__tests__/fixtures/spec-20/scripts/tool.js +12 -0
- package/dist/__tests__/fixtures/spec-20/scripts/tool.js.map +1 -0
- package/dist/__tests__/fixtures/spec-20/src/demo-module.d.ts +7 -0
- package/dist/__tests__/fixtures/spec-20/src/demo-module.d.ts.map +1 -0
- package/dist/__tests__/fixtures/spec-20/src/demo-module.js +13 -0
- package/dist/__tests__/fixtures/spec-20/src/demo-module.js.map +1 -0
- package/dist/analyzers/analyzerUtils.js +2 -2
- package/dist/analyzers/analyzerUtils.js.map +1 -1
- package/dist/analyzers/cross-language/APIContractAnalyzer.d.ts +1 -1
- package/dist/analyzers/cross-language/APIContractAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/cross-language/APIContractAnalyzer.js +2 -2
- package/dist/analyzers/cross-language/APIContractAnalyzer.js.map +1 -1
- package/dist/analyzers/cross-language/CrossLanguageSOLIDAnalyzer.d.ts +3 -0
- package/dist/analyzers/cross-language/CrossLanguageSOLIDAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/cross-language/CrossLanguageSOLIDAnalyzer.js +3 -3
- package/dist/analyzers/cross-language/CrossLanguageSOLIDAnalyzer.js.map +1 -1
- package/dist/analyzers/cross-language/SchemaValidator.d.ts +1 -1
- package/dist/analyzers/cross-language/SchemaValidator.d.ts.map +1 -1
- package/dist/analyzers/cross-language/SchemaValidator.js +2 -2
- package/dist/analyzers/cross-language/SchemaValidator.js.map +1 -1
- package/dist/analyzers/crossDomain/CrossDomainAnalyzer.d.ts +83 -0
- package/dist/analyzers/crossDomain/CrossDomainAnalyzer.d.ts.map +1 -0
- package/dist/analyzers/crossDomain/CrossDomainAnalyzer.js +611 -0
- package/dist/analyzers/crossDomain/CrossDomainAnalyzer.js.map +1 -0
- package/dist/analyzers/documentationAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/documentationAnalyzer.js +8 -4
- package/dist/analyzers/documentationAnalyzer.js.map +1 -1
- package/dist/analyzers/orm/adapterRegistry.d.ts +41 -0
- package/dist/analyzers/orm/adapterRegistry.d.ts.map +1 -0
- package/dist/analyzers/orm/adapterRegistry.js +73 -0
- package/dist/analyzers/orm/adapterRegistry.js.map +1 -0
- package/dist/analyzers/orm/drizzleAdapter.d.ts +42 -0
- package/dist/analyzers/orm/drizzleAdapter.d.ts.map +1 -0
- package/dist/analyzers/orm/drizzleAdapter.js +166 -0
- package/dist/analyzers/orm/drizzleAdapter.js.map +1 -0
- package/dist/analyzers/orm/index.d.ts +14 -0
- package/dist/analyzers/orm/index.d.ts.map +1 -0
- package/dist/analyzers/orm/index.js +24 -0
- package/dist/analyzers/orm/index.js.map +1 -0
- package/dist/analyzers/orm/prismaAdapter.d.ts +45 -0
- package/dist/analyzers/orm/prismaAdapter.d.ts.map +1 -0
- package/dist/analyzers/orm/prismaAdapter.js +170 -0
- package/dist/analyzers/orm/prismaAdapter.js.map +1 -0
- package/dist/analyzers/orm/types.d.ts +63 -0
- package/dist/analyzers/orm/types.d.ts.map +1 -0
- package/dist/analyzers/orm/types.js +10 -0
- package/dist/analyzers/orm/types.js.map +1 -0
- package/dist/analyzers/provenance.d.ts +133 -0
- package/dist/analyzers/provenance.d.ts.map +1 -0
- package/dist/analyzers/provenance.js +1041 -0
- package/dist/analyzers/provenance.js.map +1 -0
- package/dist/analyzers/reactAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/reactAnalyzer.js +117 -1
- package/dist/analyzers/reactAnalyzer.js.map +1 -1
- package/dist/analyzers/ruleRegistry.d.ts +38 -0
- package/dist/analyzers/ruleRegistry.d.ts.map +1 -0
- package/dist/analyzers/ruleRegistry.js +123 -0
- package/dist/analyzers/ruleRegistry.js.map +1 -0
- package/dist/analyzers/universal/UniversalConventionsAnalyzer.d.ts +58 -0
- package/dist/analyzers/universal/UniversalConventionsAnalyzer.d.ts.map +1 -0
- package/dist/analyzers/universal/UniversalConventionsAnalyzer.js +445 -0
- package/dist/analyzers/universal/UniversalConventionsAnalyzer.js.map +1 -0
- package/dist/analyzers/universal/UniversalDRYAnalyzer.d.ts +52 -0
- package/dist/analyzers/universal/UniversalDRYAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalDRYAnalyzer.js +108 -26
- package/dist/analyzers/universal/UniversalDRYAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/UniversalDataAccessAnalyzer.d.ts +64 -0
- package/dist/analyzers/universal/UniversalDataAccessAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalDataAccessAnalyzer.js +259 -53
- package/dist/analyzers/universal/UniversalDataAccessAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/UniversalDocumentationAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalDocumentationAnalyzer.js +5 -15
- package/dist/analyzers/universal/UniversalDocumentationAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/UniversalSOLIDAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalSOLIDAnalyzer.js +10 -10
- package/dist/analyzers/universal/UniversalSOLIDAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/UniversalSchemaAnalyzer.d.ts +25 -0
- package/dist/analyzers/universal/UniversalSchemaAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalSchemaAnalyzer.js +250 -59
- package/dist/analyzers/universal/UniversalSchemaAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/UniversalStylesAnalyzer.d.ts +103 -0
- package/dist/analyzers/universal/UniversalStylesAnalyzer.d.ts.map +1 -0
- package/dist/analyzers/universal/UniversalStylesAnalyzer.js +750 -0
- package/dist/analyzers/universal/UniversalStylesAnalyzer.js.map +1 -0
- package/dist/auditRunner.d.ts.map +1 -1
- package/dist/auditRunner.js +499 -4
- package/dist/auditRunner.js.map +1 -1
- package/dist/baseline.d.ts +98 -0
- package/dist/baseline.d.ts.map +1 -0
- package/dist/baseline.js +184 -0
- package/dist/baseline.js.map +1 -0
- package/dist/churn/churnExtractor.d.ts +32 -0
- package/dist/churn/churnExtractor.d.ts.map +1 -0
- package/dist/churn/churnExtractor.js +264 -0
- package/dist/churn/churnExtractor.js.map +1 -0
- package/dist/cli.js +1218 -11
- package/dist/cli.js.map +1 -1
- package/dist/codeIndexDB-enhanced.js +1 -1
- package/dist/codeIndexDB-enhanced.js.map +1 -1
- package/dist/codeIndexDB.d.ts +101 -0
- package/dist/codeIndexDB.d.ts.map +1 -1
- package/dist/codeIndexDB.js +665 -5
- package/dist/codeIndexDB.js.map +1 -1
- package/dist/config/configLoader.d.ts.map +1 -1
- package/dist/config/configLoader.js +104 -7
- package/dist/config/configLoader.js.map +1 -1
- package/dist/config/defaults.d.ts +72 -1
- package/dist/config/defaults.d.ts.map +1 -1
- package/dist/config/defaults.js +146 -15
- package/dist/config/defaults.js.map +1 -1
- package/dist/config/pathProfiles.d.ts +38 -0
- package/dist/config/pathProfiles.d.ts.map +1 -0
- package/dist/config/pathProfiles.js +47 -0
- package/dist/config/pathProfiles.js.map +1 -0
- package/dist/conventions/conventionMiner.d.ts +63 -0
- package/dist/conventions/conventionMiner.d.ts.map +1 -0
- package/dist/conventions/conventionMiner.js +696 -0
- package/dist/conventions/conventionMiner.js.map +1 -0
- package/dist/coverage/coverageService.d.ts +49 -0
- package/dist/coverage/coverageService.d.ts.map +1 -0
- package/dist/coverage/coverageService.js +117 -0
- package/dist/coverage/coverageService.js.map +1 -0
- package/dist/coverage/istanbulParser.d.ts +84 -0
- package/dist/coverage/istanbulParser.d.ts.map +1 -0
- package/dist/coverage/istanbulParser.js +120 -0
- package/dist/coverage/istanbulParser.js.map +1 -0
- package/dist/coverage/lcovParser.d.ts +40 -0
- package/dist/coverage/lcovParser.d.ts.map +1 -0
- package/dist/coverage/lcovParser.js +168 -0
- package/dist/coverage/lcovParser.js.map +1 -0
- package/dist/fingerprint.d.ts +17 -0
- package/dist/fingerprint.d.ts.map +1 -1
- package/dist/fingerprint.js +42 -0
- package/dist/fingerprint.js.map +1 -1
- package/dist/grammars/tree-sitter-css.wasm +0 -0
- package/dist/graph/blastRadius.d.ts +27 -0
- package/dist/graph/blastRadius.d.ts.map +1 -0
- package/dist/graph/blastRadius.js +150 -0
- package/dist/graph/blastRadius.js.map +1 -0
- package/dist/graph/callGraph.d.ts +78 -0
- package/dist/graph/callGraph.d.ts.map +1 -0
- package/dist/graph/callGraph.js +484 -0
- package/dist/graph/callGraph.js.map +1 -0
- package/dist/graph/importGraph.d.ts +91 -0
- package/dist/graph/importGraph.d.ts.map +1 -0
- package/dist/graph/importGraph.js +602 -0
- package/dist/graph/importGraph.js.map +1 -0
- package/dist/graph/outputFormatter.d.ts +55 -0
- package/dist/graph/outputFormatter.d.ts.map +1 -0
- package/dist/graph/outputFormatter.js +283 -0
- package/dist/graph/outputFormatter.js.map +1 -0
- package/dist/hooks/codex.d.ts +36 -1
- package/dist/hooks/codex.d.ts.map +1 -1
- package/dist/hooks/codex.js +88 -54
- package/dist/hooks/codex.js.map +1 -1
- package/dist/hotspots/hotspotScorer.d.ts +20 -0
- package/dist/hotspots/hotspotScorer.d.ts.map +1 -0
- package/dist/hotspots/hotspotScorer.js +202 -0
- package/dist/hotspots/hotspotScorer.js.map +1 -0
- package/dist/invariants/invariant-rules.schema.json +42 -1
- package/dist/invariants/ruleEngine.d.ts +4 -1
- package/dist/invariants/ruleEngine.d.ts.map +1 -1
- package/dist/invariants/ruleEngine.js +141 -5
- package/dist/invariants/ruleEngine.js.map +1 -1
- package/dist/invariants/ruleValidator.d.ts.map +1 -1
- package/dist/invariants/ruleValidator.js +62 -1
- package/dist/invariants/ruleValidator.js.map +1 -1
- package/dist/invariants/types.d.ts +23 -2
- package/dist/invariants/types.d.ts.map +1 -1
- package/dist/languages/UniversalAnalyzer.d.ts +1 -1
- package/dist/languages/UniversalAnalyzer.d.ts.map +1 -1
- package/dist/languages/UniversalAnalyzer.js +61 -4
- package/dist/languages/UniversalAnalyzer.js.map +1 -1
- package/dist/languages/index.d.ts +1 -0
- package/dist/languages/index.d.ts.map +1 -1
- package/dist/languages/index.js +3 -0
- package/dist/languages/index.js.map +1 -1
- package/dist/languages/tree-sitter/TreeSitterCssAdapter.d.ts +59 -0
- package/dist/languages/tree-sitter/TreeSitterCssAdapter.d.ts.map +1 -0
- package/dist/languages/tree-sitter/TreeSitterCssAdapter.js +236 -0
- package/dist/languages/tree-sitter/TreeSitterCssAdapter.js.map +1 -0
- package/dist/languages/tree-sitter/parser.d.ts.map +1 -1
- package/dist/languages/tree-sitter/parser.js +2 -0
- package/dist/languages/tree-sitter/parser.js.map +1 -1
- package/dist/languages/typescript/TreeSitterTypeScriptAdapter.d.ts.map +1 -1
- package/dist/languages/typescript/TreeSitterTypeScriptAdapter.js +61 -10
- package/dist/languages/typescript/TreeSitterTypeScriptAdapter.js.map +1 -1
- package/dist/ledger.d.ts +128 -0
- package/dist/ledger.d.ts.map +1 -0
- package/dist/ledger.js +345 -0
- package/dist/ledger.js.map +1 -0
- package/dist/mcp-tools/projectTasks.d.ts.map +1 -1
- package/dist/mcp-tools/projectTasks.js +9 -14
- package/dist/mcp-tools/projectTasks.js.map +1 -1
- package/dist/mcp.js +0 -0
- package/dist/mcpAuditJobs.d.ts.map +1 -1
- package/dist/mcpAuditJobs.js +3 -0
- package/dist/mcpAuditJobs.js.map +1 -1
- package/dist/reporting/csvReportGenerator.d.ts.map +1 -1
- package/dist/reporting/csvReportGenerator.js +7 -1
- package/dist/reporting/csvReportGenerator.js.map +1 -1
- package/dist/reporting/htmlReportGenerator.d.ts.map +1 -1
- package/dist/reporting/htmlReportGenerator.js +5 -1
- package/dist/reporting/htmlReportGenerator.js.map +1 -1
- package/dist/reporting/jsonReportGenerator.d.ts.map +1 -1
- package/dist/reporting/jsonReportGenerator.js +10 -2
- package/dist/reporting/jsonReportGenerator.js.map +1 -1
- package/dist/reporting/sarifReportGenerator.d.ts.map +1 -1
- package/dist/reporting/sarifReportGenerator.js +17 -17
- package/dist/reporting/sarifReportGenerator.js.map +1 -1
- package/dist/scripts/runBench.d.ts +94 -0
- package/dist/scripts/runBench.d.ts.map +1 -0
- package/dist/scripts/runBench.js +1559 -0
- package/dist/scripts/runBench.js.map +1 -0
- package/dist/search/QueryParser.d.ts.map +1 -1
- package/dist/search/QueryParser.js +51 -4
- package/dist/search/QueryParser.js.map +1 -1
- package/dist/services/CodeMapGenerator.d.ts +33 -0
- package/dist/services/CodeMapGenerator.d.ts.map +1 -1
- package/dist/services/CodeMapGenerator.js +404 -0
- package/dist/services/CodeMapGenerator.js.map +1 -1
- package/dist/styles/normalizer.d.ts +37 -0
- package/dist/styles/normalizer.d.ts.map +1 -0
- package/dist/styles/normalizer.js +301 -0
- package/dist/styles/normalizer.js.map +1 -0
- package/dist/styles/styleExtractor.d.ts +38 -0
- package/dist/styles/styleExtractor.d.ts.map +1 -0
- package/dist/styles/styleExtractor.js +630 -0
- package/dist/styles/styleExtractor.js.map +1 -0
- package/dist/styles/styleIndexer.d.ts +35 -0
- package/dist/styles/styleIndexer.d.ts.map +1 -0
- package/dist/styles/styleIndexer.js +266 -0
- package/dist/styles/styleIndexer.js.map +1 -0
- package/dist/styles/tailwindConfigLoader.d.ts +38 -0
- package/dist/styles/tailwindConfigLoader.d.ts.map +1 -0
- package/dist/styles/tailwindConfigLoader.js +374 -0
- package/dist/styles/tailwindConfigLoader.js.map +1 -0
- package/dist/styles/tailwindExpander.d.ts +38 -0
- package/dist/styles/tailwindExpander.d.ts.map +1 -0
- package/dist/styles/tailwindExpander.js +637 -0
- package/dist/styles/tailwindExpander.js.map +1 -0
- package/dist/styles/types.d.ts +92 -0
- package/dist/styles/types.d.ts.map +1 -0
- package/dist/styles/types.js +10 -0
- package/dist/styles/types.js.map +1 -0
- package/dist/symbols.d.ts +21 -0
- package/dist/symbols.d.ts.map +1 -0
- package/dist/symbols.js +31 -0
- package/dist/symbols.js.map +1 -0
- package/dist/types.d.ts +326 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/utils/fileDiscovery.d.ts +1 -0
- package/dist/utils/fileDiscovery.d.ts.map +1 -1
- package/dist/utils/fileDiscovery.js +8 -2
- package/dist/utils/fileDiscovery.js.map +1 -1
- package/package.json +9 -5
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/scripts/hook-audit.sh +5 -1
- package/plugin/skills/code-auditor/SKILL.md +51 -0
- package/dist/analyzers/__tests__/fixtures/spec-17/anonymous-callback-map.d.ts +0 -21
- package/dist/analyzers/__tests__/fixtures/spec-17/anonymous-callback-map.d.ts.map +0 -1
- package/dist/analyzers/__tests__/fixtures/spec-17/anonymous-callback-map.js +0 -21
- package/dist/analyzers/__tests__/fixtures/spec-17/anonymous-callback-map.js.map +0 -1
- package/dist/analyzers/__tests__/fixtures/spec-17/barrel-test-migration.d.ts +0 -13
- package/dist/analyzers/__tests__/fixtures/spec-17/barrel-test-migration.d.ts.map +0 -1
- package/dist/analyzers/__tests__/fixtures/spec-17/barrel-test-migration.js +0 -12
- package/dist/analyzers/__tests__/fixtures/spec-17/barrel-test-migration.js.map +0 -1
- package/dist/analyzers/__tests__/fixtures/spec-17/direct-access-allow-config.d.ts +0 -11
- package/dist/analyzers/__tests__/fixtures/spec-17/direct-access-allow-config.d.ts.map +0 -1
- package/dist/analyzers/__tests__/fixtures/spec-17/direct-access-allow-config.js +0 -22
- package/dist/analyzers/__tests__/fixtures/spec-17/direct-access-allow-config.js.map +0 -1
- package/dist/analyzers/__tests__/fixtures/spec-17/exported-undocumented.d.ts +0 -9
- package/dist/analyzers/__tests__/fixtures/spec-17/exported-undocumented.d.ts.map +0 -1
- package/dist/analyzers/__tests__/fixtures/spec-17/exported-undocumented.js +0 -13
- package/dist/analyzers/__tests__/fixtures/spec-17/exported-undocumented.js.map +0 -1
- package/dist/analyzers/__tests__/fixtures/spec-17/for-loop-query.d.ts +0 -13
- package/dist/analyzers/__tests__/fixtures/spec-17/for-loop-query.d.ts.map +0 -1
- package/dist/analyzers/__tests__/fixtures/spec-17/for-loop-query.js +0 -18
- package/dist/analyzers/__tests__/fixtures/spec-17/for-loop-query.js.map +0 -1
- package/dist/analyzers/__tests__/fixtures/spec-17/import-node-builtin.d.ts +0 -10
- package/dist/analyzers/__tests__/fixtures/spec-17/import-node-builtin.d.ts.map +0 -1
- package/dist/analyzers/__tests__/fixtures/spec-17/import-node-builtin.js +0 -16
- package/dist/analyzers/__tests__/fixtures/spec-17/import-node-builtin.js.map +0 -1
- package/dist/analyzers/__tests__/fixtures/spec-17/jsx-event-handler.d.ts +0 -14
- package/dist/analyzers/__tests__/fixtures/spec-17/jsx-event-handler.d.ts.map +0 -1
- package/dist/analyzers/__tests__/fixtures/spec-17/jsx-event-handler.js +0 -18
- package/dist/analyzers/__tests__/fixtures/spec-17/jsx-event-handler.js.map +0 -1
- package/dist/analyzers/__tests__/fixtures/spec-17/nested-loops-query.d.ts +0 -13
- package/dist/analyzers/__tests__/fixtures/spec-17/nested-loops-query.d.ts.map +0 -1
- package/dist/analyzers/__tests__/fixtures/spec-17/nested-loops-query.js +0 -22
- package/dist/analyzers/__tests__/fixtures/spec-17/nested-loops-query.js.map +0 -1
- package/dist/analyzers/__tests__/fixtures/spec-17/nine-line-repeated.d.ts +0 -10
- package/dist/analyzers/__tests__/fixtures/spec-17/nine-line-repeated.d.ts.map +0 -1
- package/dist/analyzers/__tests__/fixtures/spec-17/nine-line-repeated.js +0 -20
- package/dist/analyzers/__tests__/fixtures/spec-17/nine-line-repeated.js.map +0 -1
- package/dist/analyzers/__tests__/fixtures/spec-17/private-methods.d.ts +0 -18
- package/dist/analyzers/__tests__/fixtures/spec-17/private-methods.d.ts.map +0 -1
- package/dist/analyzers/__tests__/fixtures/spec-17/private-methods.js +0 -28
- package/dist/analyzers/__tests__/fixtures/spec-17/private-methods.js.map +0 -1
- package/dist/analyzers/__tests__/fixtures/spec-17/scope-all-config.d.ts +0 -11
- package/dist/analyzers/__tests__/fixtures/spec-17/scope-all-config.d.ts.map +0 -1
- package/dist/analyzers/__tests__/fixtures/spec-17/scope-all-config.js +0 -31
- package/dist/analyzers/__tests__/fixtures/spec-17/scope-all-config.js.map +0 -1
- package/dist/analyzers/__tests__/fixtures/spec-17/sql-tagged-template.d.ts +0 -11
- package/dist/analyzers/__tests__/fixtures/spec-17/sql-tagged-template.d.ts.map +0 -1
- package/dist/analyzers/__tests__/fixtures/spec-17/sql-tagged-template.js +0 -18
- package/dist/analyzers/__tests__/fixtures/spec-17/sql-tagged-template.js.map +0 -1
- package/dist/analyzers/__tests__/fixtures/spec-17/structural-similar-methods.d.ts +0 -13
- package/dist/analyzers/__tests__/fixtures/spec-17/structural-similar-methods.d.ts.map +0 -1
- package/dist/analyzers/__tests__/fixtures/spec-17/structural-similar-methods.js +0 -61
- package/dist/analyzers/__tests__/fixtures/spec-17/structural-similar-methods.js.map +0 -1
- package/dist/analyzers/__tests__/fixtures/spec-17/template-prefix-table.d.ts +0 -10
- package/dist/analyzers/__tests__/fixtures/spec-17/template-prefix-table.d.ts.map +0 -1
- package/dist/analyzers/__tests__/fixtures/spec-17/template-prefix-table.js +0 -15
- package/dist/analyzers/__tests__/fixtures/spec-17/template-prefix-table.js.map +0 -1
- package/dist/analyzers/__tests__/fixtures/spec-17/token-identical-20-lines.d.ts +0 -11
- package/dist/analyzers/__tests__/fixtures/spec-17/token-identical-20-lines.d.ts.map +0 -1
- package/dist/analyzers/__tests__/fixtures/spec-17/token-identical-20-lines.js +0 -49
- package/dist/analyzers/__tests__/fixtures/spec-17/token-identical-20-lines.js.map +0 -1
- package/dist/analyzers/__tests__/fixtures/spec-17/tsx-no-db-usage.d.ts +0 -10
- package/dist/analyzers/__tests__/fixtures/spec-17/tsx-no-db-usage.d.ts.map +0 -1
- package/dist/analyzers/__tests__/fixtures/spec-17/tsx-no-db-usage.js +0 -19
- package/dist/analyzers/__tests__/fixtures/spec-17/tsx-no-db-usage.js.map +0 -1
- package/dist/analyzers/__tests__/fixtures/spec-17/twenty-methods-vs-complex.d.ts +0 -37
- package/dist/analyzers/__tests__/fixtures/spec-17/twenty-methods-vs-complex.d.ts.map +0 -1
- package/dist/analyzers/__tests__/fixtures/spec-17/twenty-methods-vs-complex.js +0 -73
- package/dist/analyzers/__tests__/fixtures/spec-17/twenty-methods-vs-complex.js.map +0 -1
- package/dist/analyzers/__tests__/fixtures/spec-17/unique-method-74-lines.d.ts +0 -12
- package/dist/analyzers/__tests__/fixtures/spec-17/unique-method-74-lines.d.ts.map +0 -1
- package/dist/analyzers/__tests__/fixtures/spec-17/unique-method-74-lines.js +0 -82
- package/dist/analyzers/__tests__/fixtures/spec-17/unique-method-74-lines.js.map +0 -1
- package/dist/analyzers/__tests__/fixtures/spec-17/word-the-in-comment.d.ts +0 -10
- package/dist/analyzers/__tests__/fixtures/spec-17/word-the-in-comment.d.ts.map +0 -1
- package/dist/analyzers/__tests__/fixtures/spec-17/word-the-in-comment.js +0 -15
- package/dist/analyzers/__tests__/fixtures/spec-17/word-the-in-comment.js.map +0 -1
- package/dist/analyzers/dataAccessAnalyzer.d.ts +0 -46
- package/dist/analyzers/dataAccessAnalyzer.d.ts.map +0 -1
- package/dist/analyzers/dataAccessAnalyzer.js +0 -474
- package/dist/analyzers/dataAccessAnalyzer.js.map +0 -1
- package/dist/analyzers/dryAnalyzer.d.ts +0 -31
- package/dist/analyzers/dryAnalyzer.d.ts.map +0 -1
- package/dist/analyzers/dryAnalyzer.js +0 -734
- package/dist/analyzers/dryAnalyzer.js.map +0 -1
- package/dist/analyzers/schemaAnalyzer.d.ts +0 -23
- package/dist/analyzers/schemaAnalyzer.d.ts.map +0 -1
- package/dist/analyzers/schemaAnalyzer.js +0 -564
- package/dist/analyzers/schemaAnalyzer.js.map +0 -1
- package/dist/analyzers/solidAnalyzer.d.ts +0 -33
- package/dist/analyzers/solidAnalyzer.d.ts.map +0 -1
- package/dist/analyzers/solidAnalyzer.js +0 -847
- package/dist/analyzers/solidAnalyzer.js.map +0 -1
package/dist/auditRunner.js
CHANGED
|
@@ -2,15 +2,20 @@
|
|
|
2
2
|
* Audit Runner (Functional)
|
|
3
3
|
* Main orchestrator for running code audits
|
|
4
4
|
*/
|
|
5
|
+
import { promises as fs } from 'fs';
|
|
5
6
|
import { statSync, readFileSync } from 'fs';
|
|
6
7
|
import path from 'path';
|
|
8
|
+
import { fileURLToPath } from 'url';
|
|
7
9
|
import { execSync } from 'child_process';
|
|
10
|
+
import { randomUUID } from 'node:crypto';
|
|
8
11
|
import { AuditAbortedError, AuditHandoffError, } from './types.js';
|
|
9
12
|
import { discoverFiles } from './utils/fileDiscovery.js';
|
|
10
13
|
import { loadConfig } from './config/configLoader.js';
|
|
11
14
|
import { generateReport } from './reporting/reportGenerator.js';
|
|
12
15
|
import { extractFunctionsFromFile } from './functionScanner.js';
|
|
13
16
|
import { isMcpDebugEnabled, logMcpDebug, logMcpInfo } from './mcpDiagnostics.js';
|
|
17
|
+
import { loadBaseline, matchFindings, hashBaseline } from './baseline.js';
|
|
18
|
+
import { computeImpact, LATENCY_BUDGET_MS } from './graph/blastRadius.js';
|
|
14
19
|
// Import universal analyzers
|
|
15
20
|
import { initializeLanguages } from './languages/index.js';
|
|
16
21
|
import { UniversalSOLIDAnalyzer } from './analyzers/universal/UniversalSOLIDAnalyzer.js';
|
|
@@ -18,12 +23,24 @@ import { UniversalDRYAnalyzer } from './analyzers/universal/UniversalDRYAnalyzer
|
|
|
18
23
|
import { UniversalDataAccessAnalyzer } from './analyzers/universal/UniversalDataAccessAnalyzer.js';
|
|
19
24
|
import { UniversalDocumentationAnalyzer } from './analyzers/universal/UniversalDocumentationAnalyzer.js';
|
|
20
25
|
import { UniversalSchemaAnalyzer } from './analyzers/universal/UniversalSchemaAnalyzer.js';
|
|
26
|
+
import { UniversalStylesAnalyzer } from './analyzers/universal/UniversalStylesAnalyzer.js';
|
|
27
|
+
import { UniversalConventionsAnalyzer } from './analyzers/universal/UniversalConventionsAnalyzer.js';
|
|
28
|
+
import { CrossDomainAnalyzer } from './analyzers/crossDomain/CrossDomainAnalyzer.js';
|
|
29
|
+
import { initializeOrmAdapters } from './analyzers/orm/index.js';
|
|
30
|
+
import { syncStyleIndex } from './styles/styleIndexer.js';
|
|
21
31
|
import { reactAnalyzer } from './analyzers/reactAnalyzer.js';
|
|
22
32
|
import { invariantsAnalyzer } from './analyzers/invariantsAnalyzer.js';
|
|
23
33
|
import { hasRules } from './invariants/ruleEngine.js';
|
|
24
34
|
import { CodeIndexDB } from './codeIndexDB.js';
|
|
35
|
+
import { writeAuditToLedger, detectRunInput } from './ledger.js';
|
|
36
|
+
// Package version — read once at module load
|
|
37
|
+
const __auditRunnerDirname = path.dirname(fileURLToPath(import.meta.url));
|
|
38
|
+
const _pkg = JSON.parse(readFileSync(path.join(__auditRunnerDirname, '..', 'package.json'), 'utf-8'));
|
|
39
|
+
const TOOL_VERSION = String(_pkg.version || '0.0.0');
|
|
25
40
|
// Initialize the canonical language system once
|
|
26
41
|
initializeLanguages();
|
|
42
|
+
// Initialize ORM adapters for cross-domain schema extraction (Spec 15 R2)
|
|
43
|
+
initializeOrmAdapters();
|
|
27
44
|
/**
|
|
28
45
|
* Shared progress callback adapter: translates universal analyzer's (number) into
|
|
29
46
|
* the object-based callback that auditRunner passes downstream.
|
|
@@ -75,6 +92,13 @@ const DEFAULT_ANALYZERS = {
|
|
|
75
92
|
universalConfig.checkInterfaceSegregation = config.checkInterfaceSegregation;
|
|
76
93
|
if (config.checkLiskovSubstitution !== undefined)
|
|
77
94
|
universalConfig.checkLiskovSubstitution = config.checkLiskovSubstitution;
|
|
95
|
+
// Forward base-class properties (Spec-20 path profiles + severity overrides)
|
|
96
|
+
if (config.severityOverrides !== undefined)
|
|
97
|
+
universalConfig.severityOverrides = config.severityOverrides;
|
|
98
|
+
if (config.pathProfiles !== undefined)
|
|
99
|
+
universalConfig.pathProfiles = config.pathProfiles;
|
|
100
|
+
if (config.projectRoot !== undefined)
|
|
101
|
+
universalConfig.projectRoot = config.projectRoot;
|
|
78
102
|
const result = await analyzer.analyze(files, universalConfig, {
|
|
79
103
|
progressCallback: createProgressAdapter('solid', progressCallback),
|
|
80
104
|
...options,
|
|
@@ -127,6 +151,16 @@ const DEFAULT_ANALYZERS = {
|
|
|
127
151
|
// R4.3: directAccess — "flag" (default) or "allow"
|
|
128
152
|
if (config.directAccess !== undefined)
|
|
129
153
|
universalConfig.directAccess = config.directAccess;
|
|
154
|
+
// Forward base-class properties (Spec-20 path profiles + severity overrides)
|
|
155
|
+
if (config.severityOverrides !== undefined)
|
|
156
|
+
universalConfig.severityOverrides = config.severityOverrides;
|
|
157
|
+
if (config.pathProfiles !== undefined)
|
|
158
|
+
universalConfig.pathProfiles = config.pathProfiles;
|
|
159
|
+
if (config.projectRoot !== undefined)
|
|
160
|
+
universalConfig.projectRoot = config.projectRoot;
|
|
161
|
+
// Spec 21: Forward shared provenance timing accumulator
|
|
162
|
+
if (config._provenanceTiming !== undefined)
|
|
163
|
+
universalConfig._provenanceTiming = config._provenanceTiming;
|
|
130
164
|
return analyzer.analyze(files, universalConfig, {
|
|
131
165
|
progressCallback: createProgressAdapter('data-access', progressCallback),
|
|
132
166
|
...options,
|
|
@@ -157,6 +191,13 @@ const DEFAULT_ANALYZERS = {
|
|
|
157
191
|
fileHeaders: config.fileHeaders ?? config.requireFileDocs ?? false, // R1.5
|
|
158
192
|
headerSkipGlobs: config.headerSkipGlobs, // R1.5 (undefined → analyzer default)
|
|
159
193
|
};
|
|
194
|
+
// Forward base-class properties (Spec-20 path profiles + severity overrides)
|
|
195
|
+
if (config.severityOverrides !== undefined)
|
|
196
|
+
universalConfig.severityOverrides = config.severityOverrides;
|
|
197
|
+
if (config.pathProfiles !== undefined)
|
|
198
|
+
universalConfig.pathProfiles = config.pathProfiles;
|
|
199
|
+
if (config.projectRoot !== undefined)
|
|
200
|
+
universalConfig.projectRoot = config.projectRoot;
|
|
160
201
|
return analyzer.analyze(files, universalConfig, {
|
|
161
202
|
progressCallback: createProgressAdapter('documentation', progressCallback),
|
|
162
203
|
...options,
|
|
@@ -190,7 +231,6 @@ const DEFAULT_ANALYZERS = {
|
|
|
190
231
|
// If database isn't available, continue without schemas
|
|
191
232
|
}
|
|
192
233
|
const universalConfig = {
|
|
193
|
-
enableTableUsageTracking: config.enableTableUsageTracking ?? true,
|
|
194
234
|
checkMissingReferences: config.checkMissingReferences ?? true,
|
|
195
235
|
checkNamingConventions: config.checkNamingConventions ?? true,
|
|
196
236
|
detectUnusedTables: config.detectUnusedTables ?? false,
|
|
@@ -205,6 +245,16 @@ const DEFAULT_ANALYZERS = {
|
|
|
205
245
|
dbBindingNames: config.dbBindingNames ?? ['env.DB'], // R2.2
|
|
206
246
|
fileGateGlobs: config.fileGateGlobs, // R2.2 (let analyzer use defaults)
|
|
207
247
|
};
|
|
248
|
+
// Forward base-class properties (Spec-20 path profiles + severity overrides)
|
|
249
|
+
if (config.severityOverrides !== undefined)
|
|
250
|
+
universalConfig.severityOverrides = config.severityOverrides;
|
|
251
|
+
if (config.pathProfiles !== undefined)
|
|
252
|
+
universalConfig.pathProfiles = config.pathProfiles;
|
|
253
|
+
if (config.projectRoot !== undefined)
|
|
254
|
+
universalConfig.projectRoot = config.projectRoot;
|
|
255
|
+
// Spec 21: Forward shared provenance timing accumulator
|
|
256
|
+
if (config._provenanceTiming !== undefined)
|
|
257
|
+
universalConfig._provenanceTiming = config._provenanceTiming;
|
|
208
258
|
const result = await analyzer.analyze(files, universalConfig);
|
|
209
259
|
return {
|
|
210
260
|
...result,
|
|
@@ -217,6 +267,98 @@ const DEFAULT_ANALYZERS = {
|
|
|
217
267
|
};
|
|
218
268
|
},
|
|
219
269
|
},
|
|
270
|
+
'styles': {
|
|
271
|
+
name: 'styles',
|
|
272
|
+
description: 'Detects style fragmentation, value drift, token bypass, dead classes, off-scale values, mechanism fragmentation, and z-index sprawl',
|
|
273
|
+
category: 'style',
|
|
274
|
+
analyze: async (files, config, options, progressCallback) => {
|
|
275
|
+
const analyzer = new UniversalStylesAnalyzer();
|
|
276
|
+
const universalConfig = {};
|
|
277
|
+
if (config.minCorpus !== undefined)
|
|
278
|
+
universalConfig.minCorpus = config.minCorpus;
|
|
279
|
+
if (config.colorDeltaE !== undefined)
|
|
280
|
+
universalConfig.colorDeltaE = config.colorDeltaE;
|
|
281
|
+
if (config.outlierMaxShare !== undefined)
|
|
282
|
+
universalConfig.outlierMaxShare = config.outlierMaxShare;
|
|
283
|
+
if (config.modeMinCount !== undefined)
|
|
284
|
+
universalConfig.modeMinCount = config.modeMinCount;
|
|
285
|
+
if (config.scaleProperties !== undefined)
|
|
286
|
+
universalConfig.scaleProperties = config.scaleProperties;
|
|
287
|
+
if (config.zIndexMaxDistinct !== undefined)
|
|
288
|
+
universalConfig.zIndexMaxDistinct = config.zIndexMaxDistinct;
|
|
289
|
+
if (config.mechanismFragmentationMinMechanisms !== undefined)
|
|
290
|
+
universalConfig.mechanismFragmentationMinMechanisms = config.mechanismFragmentationMinMechanisms;
|
|
291
|
+
if (config.declarationSetMinDeclarations !== undefined)
|
|
292
|
+
universalConfig.declarationSetMinDeclarations = config.declarationSetMinDeclarations;
|
|
293
|
+
if (config.declarationSetSimilarityThreshold !== undefined)
|
|
294
|
+
universalConfig.declarationSetSimilarityThreshold = config.declarationSetSimilarityThreshold;
|
|
295
|
+
// Forward base-class properties (Spec-20 path profiles + severity overrides)
|
|
296
|
+
if (config.severityOverrides !== undefined)
|
|
297
|
+
universalConfig.severityOverrides = config.severityOverrides;
|
|
298
|
+
if (config.pathProfiles !== undefined)
|
|
299
|
+
universalConfig.pathProfiles = config.pathProfiles;
|
|
300
|
+
if (config.projectRoot !== undefined)
|
|
301
|
+
universalConfig.projectRoot = config.projectRoot;
|
|
302
|
+
return analyzer.analyze(files, universalConfig, {
|
|
303
|
+
progressCallback: createProgressAdapter('styles', progressCallback),
|
|
304
|
+
...options,
|
|
305
|
+
});
|
|
306
|
+
},
|
|
307
|
+
},
|
|
308
|
+
'conventions': {
|
|
309
|
+
name: 'conventions',
|
|
310
|
+
description: 'Mines codebase conventions and flags deviations at suggestion severity',
|
|
311
|
+
category: 'style',
|
|
312
|
+
analyze: async (files, config, options, progressCallback) => {
|
|
313
|
+
const analyzer = new UniversalConventionsAnalyzer();
|
|
314
|
+
const universalConfig = {};
|
|
315
|
+
if (config.minCorpus !== undefined)
|
|
316
|
+
universalConfig.minCorpus = config.minCorpus;
|
|
317
|
+
if (config.pairConfidence !== undefined)
|
|
318
|
+
universalConfig.pairConfidence = config.pairConfidence;
|
|
319
|
+
if (config.modeShare !== undefined)
|
|
320
|
+
universalConfig.modeShare = config.modeShare;
|
|
321
|
+
if (config.maxConventionsPerDomain !== undefined)
|
|
322
|
+
universalConfig.maxConventionsPerDomain = config.maxConventionsPerDomain;
|
|
323
|
+
// Forward base-class properties (Spec-20 path profiles + severity overrides)
|
|
324
|
+
if (config.severityOverrides !== undefined)
|
|
325
|
+
universalConfig.severityOverrides = config.severityOverrides;
|
|
326
|
+
if (config.pathProfiles !== undefined)
|
|
327
|
+
universalConfig.pathProfiles = config.pathProfiles;
|
|
328
|
+
if (config.projectRoot !== undefined)
|
|
329
|
+
universalConfig.projectRoot = config.projectRoot;
|
|
330
|
+
return analyzer.analyze(files, universalConfig, {
|
|
331
|
+
progressCallback: createProgressAdapter('conventions', progressCallback),
|
|
332
|
+
...options,
|
|
333
|
+
});
|
|
334
|
+
},
|
|
335
|
+
},
|
|
336
|
+
'cross-domain': {
|
|
337
|
+
name: 'cross-domain',
|
|
338
|
+
description: 'Detects cross-domain issues (schema lifecycle, validation bypass, coverage gaps)',
|
|
339
|
+
category: 'architecture',
|
|
340
|
+
analyze: async (files, config, options, progressCallback) => {
|
|
341
|
+
const analyzer = new CrossDomainAnalyzer();
|
|
342
|
+
const universalConfig = {};
|
|
343
|
+
if (config.schemaLifecycle !== undefined)
|
|
344
|
+
universalConfig.schemaLifecycle = config.schemaLifecycle;
|
|
345
|
+
if (config.validatorBypass !== undefined)
|
|
346
|
+
universalConfig.validatorBypass = config.validatorBypass;
|
|
347
|
+
if (config.coverage !== undefined)
|
|
348
|
+
universalConfig.coverage = config.coverage;
|
|
349
|
+
// Forward base-class properties (Spec-20 path profiles + severity overrides)
|
|
350
|
+
if (config.severityOverrides !== undefined)
|
|
351
|
+
universalConfig.severityOverrides = config.severityOverrides;
|
|
352
|
+
if (config.pathProfiles !== undefined)
|
|
353
|
+
universalConfig.pathProfiles = config.pathProfiles;
|
|
354
|
+
if (config.projectRoot !== undefined)
|
|
355
|
+
universalConfig.projectRoot = config.projectRoot;
|
|
356
|
+
return analyzer.analyze(files, universalConfig, {
|
|
357
|
+
progressCallback: createProgressAdapter('cross-domain', progressCallback),
|
|
358
|
+
...options,
|
|
359
|
+
});
|
|
360
|
+
},
|
|
361
|
+
},
|
|
220
362
|
};
|
|
221
363
|
/**
|
|
222
364
|
* Create an audit runner with the given options
|
|
@@ -240,7 +382,20 @@ export function createAuditRunner(options = {}) {
|
|
|
240
382
|
* Run the audit
|
|
241
383
|
*/
|
|
242
384
|
async function run(runOptions) {
|
|
243
|
-
|
|
385
|
+
// Auto-load .codeauditor.json from project root so the programmatic API
|
|
386
|
+
// respects the same config the CLI surface reads. RunOptions (caller)
|
|
387
|
+
// override file config, and createAuditRunner options override both.
|
|
388
|
+
const rootForConfig = runOptions?.projectRoot || options.projectRoot || process.cwd();
|
|
389
|
+
let fileConfig = {};
|
|
390
|
+
try {
|
|
391
|
+
const configPath = path.join(rootForConfig, '.codeauditor.json');
|
|
392
|
+
await fs.access(configPath);
|
|
393
|
+
fileConfig = await loadConfig({ configPath });
|
|
394
|
+
}
|
|
395
|
+
catch {
|
|
396
|
+
// No config file — proceed with defaults
|
|
397
|
+
}
|
|
398
|
+
const mergedOptions = { ...fileConfig, ...options, ...runOptions };
|
|
244
399
|
const startTime = Date.now();
|
|
245
400
|
// ── Scope resolution ─────────────────────────────────────────────
|
|
246
401
|
const scope = mergedOptions.scope ?? 'all';
|
|
@@ -305,6 +460,44 @@ export function createAuditRunner(options = {}) {
|
|
|
305
460
|
});
|
|
306
461
|
}
|
|
307
462
|
}
|
|
463
|
+
// ── Blast-radius impact (Spec 14 R6) ──────────────────────────────
|
|
464
|
+
// Compute transitive-caller impact for changed functions.
|
|
465
|
+
// If latency exceeds the 100ms budget, skip and emit a warning;
|
|
466
|
+
// the feature is disabled-by-default when over budget.
|
|
467
|
+
let blastRadius;
|
|
468
|
+
if (isScoped && changedFunctions && changedFunctions.length > 0) {
|
|
469
|
+
try {
|
|
470
|
+
const db = CodeIndexDB.getInstance();
|
|
471
|
+
const rawDb = db.rawDb;
|
|
472
|
+
const functionIds = [];
|
|
473
|
+
for (const fn of changedFunctions) {
|
|
474
|
+
const row = rawDb.prepare('SELECT id FROM functions WHERE name = ? AND file_path = ?').get(fn.name, fn.filePath);
|
|
475
|
+
if (row)
|
|
476
|
+
functionIds.push(row.id);
|
|
477
|
+
}
|
|
478
|
+
if (functionIds.length > 0) {
|
|
479
|
+
const impact = computeImpact(rawDb, functionIds);
|
|
480
|
+
if (impact.latencyMs > LATENCY_BUDGET_MS) {
|
|
481
|
+
logMcpInfo('blast-radius', `latency ${impact.latencyMs}ms exceeds ${LATENCY_BUDGET_MS}ms budget — blast radius disabled for this run`, {});
|
|
482
|
+
}
|
|
483
|
+
else {
|
|
484
|
+
blastRadius = impact;
|
|
485
|
+
logMcpInfo('blast-radius', 'computed', {
|
|
486
|
+
editedFunctionCount: impact.editedFunctionCount,
|
|
487
|
+
transitiveCallers: impact.transitiveCallers,
|
|
488
|
+
reachableExports: impact.reachableExports,
|
|
489
|
+
latencyMs: impact.latencyMs,
|
|
490
|
+
});
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
catch (err) {
|
|
495
|
+
// Blast radius is advisory — non-fatal
|
|
496
|
+
logMcpInfo('blast-radius', 'computation failed (continuing)', {
|
|
497
|
+
error: err instanceof Error ? err.message : String(err)
|
|
498
|
+
});
|
|
499
|
+
}
|
|
500
|
+
}
|
|
308
501
|
const handoffRemaining = [];
|
|
309
502
|
const maxPerRun = mergedOptions.maxFilesPerRun;
|
|
310
503
|
if (typeof maxPerRun === 'number' && maxPerRun > 0 && files.length > maxPerRun) {
|
|
@@ -391,6 +584,32 @@ export function createAuditRunner(options = {}) {
|
|
|
391
584
|
// Run analyzers
|
|
392
585
|
const analyzerResults = {};
|
|
393
586
|
const enabledAnalyzers = getEnabledAnalyzers(mergedOptions, analyzerRegistry);
|
|
587
|
+
// ── Style index sync (Spec 10) ────────────────────────────────────
|
|
588
|
+
// Sync style declarations, tokens, and class usage before the
|
|
589
|
+
// styles analyzer runs, mirroring the function index sync pattern.
|
|
590
|
+
if (enabledAnalyzers.includes('styles')) {
|
|
591
|
+
try {
|
|
592
|
+
const styleDb = CodeIndexDB.getInstance();
|
|
593
|
+
await styleDb.initialize();
|
|
594
|
+
const styleSyncResult = await syncStyleIndex(styleDb.rawDb, files, root, { scoped: isScoped });
|
|
595
|
+
logMcpInfo('style-index', 'style index sync complete', {
|
|
596
|
+
changed: styleSyncResult.changed,
|
|
597
|
+
skipped: styleSyncResult.skipped,
|
|
598
|
+
removed: styleSyncResult.removed,
|
|
599
|
+
errors: styleSyncResult.errors,
|
|
600
|
+
});
|
|
601
|
+
}
|
|
602
|
+
catch (err) {
|
|
603
|
+
// Non-fatal: styles analyzer will run with whatever is in the index
|
|
604
|
+
logMcpInfo('style-index', 'style index sync failed (continuing)', {
|
|
605
|
+
error: err instanceof Error ? err.message : String(err)
|
|
606
|
+
});
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
// Spec 21: Shared timing accumulator for provenance resolution.
|
|
610
|
+
// Injected into data-access and schema analyzers so they can report
|
|
611
|
+
// per-file buildProvenanceContext() wall time (hook-latency measurement).
|
|
612
|
+
const provenanceTiming = { totalMs: 0 };
|
|
394
613
|
logMcpInfo('analysis', 'enabled analyzers', {
|
|
395
614
|
names: enabledAnalyzers,
|
|
396
615
|
fileCount: files.length,
|
|
@@ -421,11 +640,28 @@ export function createAuditRunner(options = {}) {
|
|
|
421
640
|
analyzer: analyzerName,
|
|
422
641
|
message: `Running ${analyzerName} analyzer...`
|
|
423
642
|
});
|
|
424
|
-
// Build analyzer config; inject full index for DRY on scoped runs
|
|
425
|
-
|
|
643
|
+
// Build analyzer config; inject full index for DRY on scoped runs.
|
|
644
|
+
// Skip pathProfiles for invariants (Spec-20 R3): no backdoor
|
|
645
|
+
// around declared laws — invariants severity is absolute.
|
|
646
|
+
const isInvariants = analyzerName === 'invariants';
|
|
647
|
+
const analyzerConfig = {
|
|
648
|
+
...(mergedOptions.analyzerConfigs?.[analyzerName] || {}),
|
|
649
|
+
};
|
|
650
|
+
if (!isInvariants) {
|
|
651
|
+
analyzerConfig.pathProfiles = mergedOptions.pathProfiles;
|
|
652
|
+
analyzerConfig.projectRoot = root;
|
|
653
|
+
analyzerConfig.severityOverrides = mergedOptions.severityOverrides;
|
|
654
|
+
}
|
|
426
655
|
if (analyzerName === 'dry' && isScoped && fullFunctionIndex) {
|
|
427
656
|
analyzerConfig.fullFunctionIndex = fullFunctionIndex;
|
|
428
657
|
}
|
|
658
|
+
// Spec 21: Pass shared provenance timing accumulator to analyzers
|
|
659
|
+
// that call buildProvenanceContext() (data-access and schema).
|
|
660
|
+
// The analyzers accumulate per-file timing into this object;
|
|
661
|
+
// we read it back after all analyzers complete.
|
|
662
|
+
if (analyzerName === 'data-access' || analyzerName === 'schema') {
|
|
663
|
+
analyzerConfig._provenanceTiming = provenanceTiming;
|
|
664
|
+
}
|
|
429
665
|
logMcpInfo('analysis', `running ${analyzerName}`, { fileCount: files.length });
|
|
430
666
|
try {
|
|
431
667
|
const result = await analyzer.analyze(files, analyzerConfig, mergedOptions, (progress) => {
|
|
@@ -474,6 +710,208 @@ export function createAuditRunner(options = {}) {
|
|
|
474
710
|
orderedAnalyzerResults[analyzerName] = analyzerResults[analyzerName];
|
|
475
711
|
}
|
|
476
712
|
}
|
|
713
|
+
// ── Spec 13 R5 Phase 1: Persist seeded DRY pairs ──────────────────
|
|
714
|
+
// Store pairs from DRY analysis in dry_pair_history for divergence tracking.
|
|
715
|
+
// Pair identity is fingerprint-based (file + nodeType + line),
|
|
716
|
+
// NOT content-hash-based — so a diverging clone stays the same pair.
|
|
717
|
+
let dryPersistRunId = null;
|
|
718
|
+
try {
|
|
719
|
+
const dryResult = orderedAnalyzerResults['dry'];
|
|
720
|
+
const dryPairs = dryResult?.dryPairs;
|
|
721
|
+
if (dryPairs && dryPairs.length > 0) {
|
|
722
|
+
const indexDb = CodeIndexDB.getInstance();
|
|
723
|
+
await indexDb.initialize();
|
|
724
|
+
dryPersistRunId = randomUUID();
|
|
725
|
+
const insertStmt = indexDb.rawDb.prepare(`
|
|
726
|
+
INSERT OR IGNORE INTO dry_pair_history
|
|
727
|
+
(pair_fingerprint, file1, symbol1, line1, content_hash1,
|
|
728
|
+
file2, symbol2, line2, content_hash2, similarity, timestamp, run_id)
|
|
729
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, datetime('now'), ?)
|
|
730
|
+
`);
|
|
731
|
+
const tx = indexDb.rawDb.transaction(() => {
|
|
732
|
+
for (const pair of dryPairs) {
|
|
733
|
+
insertStmt.run(pair.pairFingerprint, pair.file1, pair.symbol1, pair.line1, pair.contentHash1, pair.file2, pair.symbol2, pair.line2, pair.contentHash2, pair.similarity, dryPersistRunId);
|
|
734
|
+
}
|
|
735
|
+
});
|
|
736
|
+
tx();
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
catch {
|
|
740
|
+
// dry_pair_history persistence is advisory — non-fatal
|
|
741
|
+
}
|
|
742
|
+
// ── Baseline classification (Spec 18 R1) ────────────────────────────
|
|
743
|
+
const projectRoot = path.resolve(mergedOptions.projectRoot || process.cwd());
|
|
744
|
+
const baseline = loadBaseline(projectRoot);
|
|
745
|
+
let baselineMetadata;
|
|
746
|
+
if (baseline) {
|
|
747
|
+
const allViolations = Object.values(orderedAnalyzerResults).flatMap((r) => r.violations);
|
|
748
|
+
// For scoped runs, limit "fixed" to in-scope files
|
|
749
|
+
const scopedFiles = isScoped ? files : undefined;
|
|
750
|
+
const classified = matchFindings(allViolations, baseline, scopedFiles);
|
|
751
|
+
// Tag violations with their baseline status
|
|
752
|
+
for (const v of classified.new) {
|
|
753
|
+
v.new = true;
|
|
754
|
+
}
|
|
755
|
+
for (const v of classified.known) {
|
|
756
|
+
v.new = false;
|
|
757
|
+
}
|
|
758
|
+
baselineMetadata = {
|
|
759
|
+
present: true,
|
|
760
|
+
hash: hashBaseline(baseline),
|
|
761
|
+
newCount: classified.new.length,
|
|
762
|
+
fixedCount: classified.fixed.length,
|
|
763
|
+
knownCount: classified.known.length,
|
|
764
|
+
previousKnownCount: baseline.metadata.totalFindings,
|
|
765
|
+
};
|
|
766
|
+
logMcpInfo('baseline', 'baseline classification complete', {
|
|
767
|
+
new: classified.new.length,
|
|
768
|
+
fixed: classified.fixed.length,
|
|
769
|
+
known: classified.known.length,
|
|
770
|
+
});
|
|
771
|
+
}
|
|
772
|
+
// ── Spec 13 R2 — Hotspot scoring & finding reordering ──────────────
|
|
773
|
+
// Attach hotspot scores to violations and reorder within severity tiers.
|
|
774
|
+
// Falls back gracefully when no churn/hotspot data exists.
|
|
775
|
+
try {
|
|
776
|
+
const indexDb = CodeIndexDB.getInstance();
|
|
777
|
+
await indexDb.initialize();
|
|
778
|
+
// Build hotspot lookup: target -> score
|
|
779
|
+
const hotspotRows = indexDb.rawDb
|
|
780
|
+
.prepare('SELECT target, type, score FROM hotspot_scores')
|
|
781
|
+
.all();
|
|
782
|
+
if (hotspotRows.length > 0) {
|
|
783
|
+
const hotspotByTarget = new Map();
|
|
784
|
+
const hotspotByFile = new Map();
|
|
785
|
+
for (const row of hotspotRows) {
|
|
786
|
+
hotspotByTarget.set(row.target, row.score);
|
|
787
|
+
if (row.type === 'file') {
|
|
788
|
+
hotspotByFile.set(row.target, row.score);
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
// Attach hotspot to each violation and reorder within each analyzer
|
|
792
|
+
for (const analyzerName of Object.keys(orderedAnalyzerResults)) {
|
|
793
|
+
const result = orderedAnalyzerResults[analyzerName];
|
|
794
|
+
const violations = result.violations;
|
|
795
|
+
// Attach hotspot scores
|
|
796
|
+
for (const v of violations) {
|
|
797
|
+
const funcName = v.functionName;
|
|
798
|
+
const file = v.file;
|
|
799
|
+
// Try function-level hotspot first: "filePath::functionName"
|
|
800
|
+
const funcTarget = funcName ? `${file}::${funcName}` : undefined;
|
|
801
|
+
v.hotspot = (funcTarget ? hotspotByTarget.get(funcTarget) : undefined)
|
|
802
|
+
?? hotspotByFile.get(file)
|
|
803
|
+
?? 0;
|
|
804
|
+
}
|
|
805
|
+
// Reorder within severity tiers: sort by hotspot descending,
|
|
806
|
+
// preserving original order as tiebreaker.
|
|
807
|
+
const severityOrder = {
|
|
808
|
+
critical: 0,
|
|
809
|
+
warning: 1,
|
|
810
|
+
suggestion: 2,
|
|
811
|
+
info: 3,
|
|
812
|
+
};
|
|
813
|
+
const indexed = violations.map((v, i) => ({ v, i }));
|
|
814
|
+
indexed.sort((a, b) => {
|
|
815
|
+
const sevA = severityOrder[a.v.severity] ?? 99;
|
|
816
|
+
const sevB = severityOrder[b.v.severity] ?? 99;
|
|
817
|
+
if (sevA !== sevB)
|
|
818
|
+
return sevA - sevB;
|
|
819
|
+
// Within same severity: higher hotspot first
|
|
820
|
+
const hsA = a.v.hotspot ?? 0;
|
|
821
|
+
const hsB = b.v.hotspot ?? 0;
|
|
822
|
+
if (hsA !== hsB)
|
|
823
|
+
return hsB - hsA;
|
|
824
|
+
// Tiebreaker: original order
|
|
825
|
+
return a.i - b.i;
|
|
826
|
+
});
|
|
827
|
+
result.violations = indexed.map(x => x.v);
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
catch {
|
|
832
|
+
// Hotspot reordering is advisory — failure is non-fatal
|
|
833
|
+
}
|
|
834
|
+
// ── Spec 13 R5 Phase 2: Divergence tracking pass ──────────────────
|
|
835
|
+
// For every pair in dry_pair_history, compare the last 2 similarity
|
|
836
|
+
// measurements. If similarity has declined by ≥ divergenceThreshold
|
|
837
|
+
// for divergenceRuns consecutive runs, emit dry/diverging-clone.
|
|
838
|
+
//
|
|
839
|
+
// New rows are inserted by Phase 1 for pairs the DRY analyzer re-detects
|
|
840
|
+
// this run. Pairs that weren't re-detected don't get a new measurement
|
|
841
|
+
// this pass — full source re-read is deferred to the 20% case.
|
|
842
|
+
try {
|
|
843
|
+
const divergenceCfg = mergedOptions.analyzerConfigs?.dry?.divergence
|
|
844
|
+
?? mergedOptions.divergence
|
|
845
|
+
?? { divergenceThreshold: 0.05, divergenceRuns: 2, minPairSimilarity: 0.5 };
|
|
846
|
+
const threshold = divergenceCfg.divergenceThreshold ?? 0.05;
|
|
847
|
+
const requiredDeclines = divergenceCfg.divergenceRuns ?? 2;
|
|
848
|
+
if (threshold > 0) {
|
|
849
|
+
const indexDb = CodeIndexDB.getInstance();
|
|
850
|
+
await indexDb.initialize();
|
|
851
|
+
// Get all distinct pair fingerprints
|
|
852
|
+
const fingerprints = indexDb.rawDb
|
|
853
|
+
.prepare('SELECT DISTINCT pair_fingerprint FROM dry_pair_history')
|
|
854
|
+
.all();
|
|
855
|
+
if (fingerprints.length > 0) {
|
|
856
|
+
const getRows = indexDb.rawDb.prepare(`
|
|
857
|
+
SELECT similarity, timestamp
|
|
858
|
+
FROM dry_pair_history
|
|
859
|
+
WHERE pair_fingerprint = ?
|
|
860
|
+
ORDER BY timestamp ASC
|
|
861
|
+
`);
|
|
862
|
+
const divergingViolations = [];
|
|
863
|
+
for (const { pair_fingerprint: fp } of fingerprints) {
|
|
864
|
+
const rows = getRows.all(fp);
|
|
865
|
+
if (rows.length < requiredDeclines + 1)
|
|
866
|
+
continue;
|
|
867
|
+
// Check last `requiredDeclines` consecutive pairs for decline
|
|
868
|
+
let consecutiveDeclines = 0;
|
|
869
|
+
for (let i = rows.length - requiredDeclines; i < rows.length; i++) {
|
|
870
|
+
if (rows[i].similarity < rows[i - 1].similarity - threshold) {
|
|
871
|
+
consecutiveDeclines++;
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
if (consecutiveDeclines >= requiredDeclines) {
|
|
875
|
+
// Get file/line info from the last row
|
|
876
|
+
const lastRow = indexDb.rawDb.prepare(`
|
|
877
|
+
SELECT file1, file2, line1, line2 FROM dry_pair_history
|
|
878
|
+
WHERE pair_fingerprint = ?
|
|
879
|
+
ORDER BY timestamp DESC LIMIT 1
|
|
880
|
+
`).get(fp);
|
|
881
|
+
if (lastRow) {
|
|
882
|
+
const currentSim = rows[rows.length - 1].similarity;
|
|
883
|
+
const prevSim = rows[rows.length - 2].similarity;
|
|
884
|
+
const drop = Math.round((prevSim - currentSim) * 1000) / 1000;
|
|
885
|
+
divergingViolations.push({
|
|
886
|
+
file: lastRow.file1,
|
|
887
|
+
line: lastRow.line1,
|
|
888
|
+
severity: 'suggestion',
|
|
889
|
+
message: `Clone pair has diverged: similarity dropped ${drop} (from ${prevSim.toFixed(3)} to ${currentSim.toFixed(3)}) across ${requiredDeclines} consecutive runs (pair: ${fp.slice(0, 12)}…). Review ${lastRow.file1}:${lastRow.line1} and ${lastRow.file2}:${lastRow.line2} for diverged logic.`,
|
|
890
|
+
analyzer: 'dry',
|
|
891
|
+
rule: 'dry/diverging-clone',
|
|
892
|
+
});
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
if (divergingViolations.length > 0) {
|
|
897
|
+
// Append to the dry analyzer result, or create one if none exists
|
|
898
|
+
if (orderedAnalyzerResults['dry']) {
|
|
899
|
+
orderedAnalyzerResults['dry'].violations.push(...divergingViolations);
|
|
900
|
+
}
|
|
901
|
+
else {
|
|
902
|
+
orderedAnalyzerResults['dry'] = {
|
|
903
|
+
violations: divergingViolations,
|
|
904
|
+
filesProcessed: 0,
|
|
905
|
+
executionTime: 0,
|
|
906
|
+
};
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
catch {
|
|
913
|
+
// Divergence tracking is advisory — non-fatal
|
|
914
|
+
}
|
|
477
915
|
// Generate summary
|
|
478
916
|
const summary = generateSummary(orderedAnalyzerResults, files.length);
|
|
479
917
|
// Create result
|
|
@@ -488,6 +926,9 @@ export function createAuditRunner(options = {}) {
|
|
|
488
926
|
analyzersRun: enabledAnalyzers,
|
|
489
927
|
configUsed: mergedOptions,
|
|
490
928
|
scope: scopeResultType,
|
|
929
|
+
provenanceResolutionMs: provenanceTiming.totalMs,
|
|
930
|
+
...(blastRadius && { blastRadius }),
|
|
931
|
+
...(baselineMetadata && { baseline: baselineMetadata }),
|
|
491
932
|
...(collectedFunctions.length > 0 && {
|
|
492
933
|
collectedFunctions,
|
|
493
934
|
fileToFunctionsMap: Object.fromEntries(fileToFunctionsMap)
|
|
@@ -502,6 +943,25 @@ export function createAuditRunner(options = {}) {
|
|
|
502
943
|
if (handoffRemaining.length > 0) {
|
|
503
944
|
throw new AuditHandoffError(`${handoffRemaining.length} file(s) deferred to the next worker chunk`, result, handoffRemaining);
|
|
504
945
|
}
|
|
946
|
+
// Spec 11 R1 — write to findings ledger (non-fatal: ledger is advisory)
|
|
947
|
+
const ledgerRunId = (async () => {
|
|
948
|
+
try {
|
|
949
|
+
const indexDb = CodeIndexDB.getInstance();
|
|
950
|
+
await indexDb.initialize();
|
|
951
|
+
const violations = Object.values(orderedAnalyzerResults).flatMap(ar => ar.violations);
|
|
952
|
+
// Hook-contract guard: no violation may carry an empty file path or line 0.
|
|
953
|
+
// A sentinel violation with file:'' broke the Claude Code hook's JSON consumer
|
|
954
|
+
// (Spec 15 regression). This guard is permanent — every analyzer code path
|
|
955
|
+
// must produce properly anchored violations.
|
|
956
|
+
validateHookContract(violations);
|
|
957
|
+
const scopeStr = Array.isArray(scope) ? `files:${scope.length}` : (scope ?? 'all');
|
|
958
|
+
return writeAuditToLedger(indexDb.rawDb, detectRunInput(process.argv.slice(2).join(' '), options.surface ?? 'cli', scopeStr, root, TOOL_VERSION), violations, Date.now() - startTime, 0);
|
|
959
|
+
}
|
|
960
|
+
catch (_err) {
|
|
961
|
+
// ledger write is non-fatal — audit result is still valid
|
|
962
|
+
return null;
|
|
963
|
+
}
|
|
964
|
+
})();
|
|
505
965
|
return result;
|
|
506
966
|
}
|
|
507
967
|
/**
|
|
@@ -692,6 +1152,41 @@ function generateSummary(analyzerResults, filesAnalyzed) {
|
|
|
692
1152
|
topIssues
|
|
693
1153
|
};
|
|
694
1154
|
}
|
|
1155
|
+
/**
|
|
1156
|
+
* Hook-contract regression guard.
|
|
1157
|
+
*
|
|
1158
|
+
* Every violation must carry a non-empty `file` path. A sentinel violation with
|
|
1159
|
+
* `file: ''` (Spec 15 regression, commit 3419ed0) broke the Claude Code hook's
|
|
1160
|
+
* JSON consumer, which expects every violation to anchor to a real file.
|
|
1161
|
+
*
|
|
1162
|
+
* When `line` is present, it must be > 0 — a zero line means the analyzer
|
|
1163
|
+
* failed to locate the finding and shipped an unactionable anchor.
|
|
1164
|
+
*
|
|
1165
|
+
* Contract violations are logged as warnings and dropped from the pipeline
|
|
1166
|
+
* (they are never written to the ledger or surfaced to the hook).
|
|
1167
|
+
*
|
|
1168
|
+
* This guard is permanent — no analyzer code path may produce violations with
|
|
1169
|
+
* empty file paths or line 0.
|
|
1170
|
+
*/
|
|
1171
|
+
function validateHookContract(violations) {
|
|
1172
|
+
for (let i = violations.length - 1; i >= 0; i--) {
|
|
1173
|
+
const v = violations[i];
|
|
1174
|
+
const problems = [];
|
|
1175
|
+
if (!v.file || v.file.trim() === '') {
|
|
1176
|
+
problems.push('empty file path');
|
|
1177
|
+
}
|
|
1178
|
+
if (v.line !== undefined && v.line === 0) {
|
|
1179
|
+
problems.push('line=0 (unactionable anchor)');
|
|
1180
|
+
}
|
|
1181
|
+
if (problems.length > 0) {
|
|
1182
|
+
console.warn(`[hook-contract] Dropping violation from analyzer '${v.analyzer ?? 'unknown'}': ` +
|
|
1183
|
+
`${problems.join(', ')}. Rule: ${v.rule ?? 'unknown'}. ` +
|
|
1184
|
+
`Message: "${v.message.slice(0, 120)}". ` +
|
|
1185
|
+
`This is a bug in the analyzer — violations must anchor to real files with valid lines.`);
|
|
1186
|
+
violations.splice(i, 1);
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
695
1190
|
/**
|
|
696
1191
|
* Report progress
|
|
697
1192
|
*/
|