code-auditor-mcp 3.1.0 → 3.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +665 -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 -4
- 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 -5
- package/dist/analyzers/cross-language/CrossLanguageSOLIDAnalyzer.js.map +1 -1
- package/dist/analyzers/cross-language/DependencyGraphBuilder.d.ts.map +1 -1
- package/dist/analyzers/cross-language/DependencyGraphBuilder.js +0 -3
- package/dist/analyzers/cross-language/DependencyGraphBuilder.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 -5
- 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 -103
- 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 +483 -3
- 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 +261 -0
- package/dist/churn/churnExtractor.js.map +1 -0
- package/dist/cli.js +1215 -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 +661 -5
- package/dist/codeIndexDB.js.map +1 -1
- package/dist/config/configLoader.d.ts.map +1 -1
- package/dist/config/configLoader.js +95 -1
- 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 +59 -24
- 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/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
|
@@ -4,13 +4,17 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { statSync, readFileSync } from 'fs';
|
|
6
6
|
import path from 'path';
|
|
7
|
+
import { fileURLToPath } from 'url';
|
|
7
8
|
import { execSync } from 'child_process';
|
|
9
|
+
import { randomUUID } from 'node:crypto';
|
|
8
10
|
import { AuditAbortedError, AuditHandoffError, } from './types.js';
|
|
9
11
|
import { discoverFiles } from './utils/fileDiscovery.js';
|
|
10
12
|
import { loadConfig } from './config/configLoader.js';
|
|
11
13
|
import { generateReport } from './reporting/reportGenerator.js';
|
|
12
14
|
import { extractFunctionsFromFile } from './functionScanner.js';
|
|
13
15
|
import { isMcpDebugEnabled, logMcpDebug, logMcpInfo } from './mcpDiagnostics.js';
|
|
16
|
+
import { loadBaseline, matchFindings, hashBaseline } from './baseline.js';
|
|
17
|
+
import { computeImpact, LATENCY_BUDGET_MS } from './graph/blastRadius.js';
|
|
14
18
|
// Import universal analyzers
|
|
15
19
|
import { initializeLanguages } from './languages/index.js';
|
|
16
20
|
import { UniversalSOLIDAnalyzer } from './analyzers/universal/UniversalSOLIDAnalyzer.js';
|
|
@@ -18,12 +22,24 @@ import { UniversalDRYAnalyzer } from './analyzers/universal/UniversalDRYAnalyzer
|
|
|
18
22
|
import { UniversalDataAccessAnalyzer } from './analyzers/universal/UniversalDataAccessAnalyzer.js';
|
|
19
23
|
import { UniversalDocumentationAnalyzer } from './analyzers/universal/UniversalDocumentationAnalyzer.js';
|
|
20
24
|
import { UniversalSchemaAnalyzer } from './analyzers/universal/UniversalSchemaAnalyzer.js';
|
|
25
|
+
import { UniversalStylesAnalyzer } from './analyzers/universal/UniversalStylesAnalyzer.js';
|
|
26
|
+
import { UniversalConventionsAnalyzer } from './analyzers/universal/UniversalConventionsAnalyzer.js';
|
|
27
|
+
import { CrossDomainAnalyzer } from './analyzers/crossDomain/CrossDomainAnalyzer.js';
|
|
28
|
+
import { initializeOrmAdapters } from './analyzers/orm/index.js';
|
|
29
|
+
import { syncStyleIndex } from './styles/styleIndexer.js';
|
|
21
30
|
import { reactAnalyzer } from './analyzers/reactAnalyzer.js';
|
|
22
31
|
import { invariantsAnalyzer } from './analyzers/invariantsAnalyzer.js';
|
|
23
32
|
import { hasRules } from './invariants/ruleEngine.js';
|
|
24
33
|
import { CodeIndexDB } from './codeIndexDB.js';
|
|
34
|
+
import { writeAuditToLedger, detectRunInput } from './ledger.js';
|
|
35
|
+
// Package version — read once at module load
|
|
36
|
+
const __auditRunnerDirname = path.dirname(fileURLToPath(import.meta.url));
|
|
37
|
+
const _pkg = JSON.parse(readFileSync(path.join(__auditRunnerDirname, '..', 'package.json'), 'utf-8'));
|
|
38
|
+
const TOOL_VERSION = String(_pkg.version || '0.0.0');
|
|
25
39
|
// Initialize the canonical language system once
|
|
26
40
|
initializeLanguages();
|
|
41
|
+
// Initialize ORM adapters for cross-domain schema extraction (Spec 15 R2)
|
|
42
|
+
initializeOrmAdapters();
|
|
27
43
|
/**
|
|
28
44
|
* Shared progress callback adapter: translates universal analyzer's (number) into
|
|
29
45
|
* the object-based callback that auditRunner passes downstream.
|
|
@@ -75,6 +91,13 @@ const DEFAULT_ANALYZERS = {
|
|
|
75
91
|
universalConfig.checkInterfaceSegregation = config.checkInterfaceSegregation;
|
|
76
92
|
if (config.checkLiskovSubstitution !== undefined)
|
|
77
93
|
universalConfig.checkLiskovSubstitution = config.checkLiskovSubstitution;
|
|
94
|
+
// Forward base-class properties (Spec-20 path profiles + severity overrides)
|
|
95
|
+
if (config.severityOverrides !== undefined)
|
|
96
|
+
universalConfig.severityOverrides = config.severityOverrides;
|
|
97
|
+
if (config.pathProfiles !== undefined)
|
|
98
|
+
universalConfig.pathProfiles = config.pathProfiles;
|
|
99
|
+
if (config.projectRoot !== undefined)
|
|
100
|
+
universalConfig.projectRoot = config.projectRoot;
|
|
78
101
|
const result = await analyzer.analyze(files, universalConfig, {
|
|
79
102
|
progressCallback: createProgressAdapter('solid', progressCallback),
|
|
80
103
|
...options,
|
|
@@ -127,6 +150,16 @@ const DEFAULT_ANALYZERS = {
|
|
|
127
150
|
// R4.3: directAccess — "flag" (default) or "allow"
|
|
128
151
|
if (config.directAccess !== undefined)
|
|
129
152
|
universalConfig.directAccess = config.directAccess;
|
|
153
|
+
// Forward base-class properties (Spec-20 path profiles + severity overrides)
|
|
154
|
+
if (config.severityOverrides !== undefined)
|
|
155
|
+
universalConfig.severityOverrides = config.severityOverrides;
|
|
156
|
+
if (config.pathProfiles !== undefined)
|
|
157
|
+
universalConfig.pathProfiles = config.pathProfiles;
|
|
158
|
+
if (config.projectRoot !== undefined)
|
|
159
|
+
universalConfig.projectRoot = config.projectRoot;
|
|
160
|
+
// Spec 21: Forward shared provenance timing accumulator
|
|
161
|
+
if (config._provenanceTiming !== undefined)
|
|
162
|
+
universalConfig._provenanceTiming = config._provenanceTiming;
|
|
130
163
|
return analyzer.analyze(files, universalConfig, {
|
|
131
164
|
progressCallback: createProgressAdapter('data-access', progressCallback),
|
|
132
165
|
...options,
|
|
@@ -157,6 +190,13 @@ const DEFAULT_ANALYZERS = {
|
|
|
157
190
|
fileHeaders: config.fileHeaders ?? config.requireFileDocs ?? false, // R1.5
|
|
158
191
|
headerSkipGlobs: config.headerSkipGlobs, // R1.5 (undefined → analyzer default)
|
|
159
192
|
};
|
|
193
|
+
// Forward base-class properties (Spec-20 path profiles + severity overrides)
|
|
194
|
+
if (config.severityOverrides !== undefined)
|
|
195
|
+
universalConfig.severityOverrides = config.severityOverrides;
|
|
196
|
+
if (config.pathProfiles !== undefined)
|
|
197
|
+
universalConfig.pathProfiles = config.pathProfiles;
|
|
198
|
+
if (config.projectRoot !== undefined)
|
|
199
|
+
universalConfig.projectRoot = config.projectRoot;
|
|
160
200
|
return analyzer.analyze(files, universalConfig, {
|
|
161
201
|
progressCallback: createProgressAdapter('documentation', progressCallback),
|
|
162
202
|
...options,
|
|
@@ -190,7 +230,6 @@ const DEFAULT_ANALYZERS = {
|
|
|
190
230
|
// If database isn't available, continue without schemas
|
|
191
231
|
}
|
|
192
232
|
const universalConfig = {
|
|
193
|
-
enableTableUsageTracking: config.enableTableUsageTracking ?? true,
|
|
194
233
|
checkMissingReferences: config.checkMissingReferences ?? true,
|
|
195
234
|
checkNamingConventions: config.checkNamingConventions ?? true,
|
|
196
235
|
detectUnusedTables: config.detectUnusedTables ?? false,
|
|
@@ -205,6 +244,16 @@ const DEFAULT_ANALYZERS = {
|
|
|
205
244
|
dbBindingNames: config.dbBindingNames ?? ['env.DB'], // R2.2
|
|
206
245
|
fileGateGlobs: config.fileGateGlobs, // R2.2 (let analyzer use defaults)
|
|
207
246
|
};
|
|
247
|
+
// Forward base-class properties (Spec-20 path profiles + severity overrides)
|
|
248
|
+
if (config.severityOverrides !== undefined)
|
|
249
|
+
universalConfig.severityOverrides = config.severityOverrides;
|
|
250
|
+
if (config.pathProfiles !== undefined)
|
|
251
|
+
universalConfig.pathProfiles = config.pathProfiles;
|
|
252
|
+
if (config.projectRoot !== undefined)
|
|
253
|
+
universalConfig.projectRoot = config.projectRoot;
|
|
254
|
+
// Spec 21: Forward shared provenance timing accumulator
|
|
255
|
+
if (config._provenanceTiming !== undefined)
|
|
256
|
+
universalConfig._provenanceTiming = config._provenanceTiming;
|
|
208
257
|
const result = await analyzer.analyze(files, universalConfig);
|
|
209
258
|
return {
|
|
210
259
|
...result,
|
|
@@ -217,6 +266,98 @@ const DEFAULT_ANALYZERS = {
|
|
|
217
266
|
};
|
|
218
267
|
},
|
|
219
268
|
},
|
|
269
|
+
'styles': {
|
|
270
|
+
name: 'styles',
|
|
271
|
+
description: 'Detects style fragmentation, value drift, token bypass, dead classes, off-scale values, mechanism fragmentation, and z-index sprawl',
|
|
272
|
+
category: 'style',
|
|
273
|
+
analyze: async (files, config, options, progressCallback) => {
|
|
274
|
+
const analyzer = new UniversalStylesAnalyzer();
|
|
275
|
+
const universalConfig = {};
|
|
276
|
+
if (config.minCorpus !== undefined)
|
|
277
|
+
universalConfig.minCorpus = config.minCorpus;
|
|
278
|
+
if (config.colorDeltaE !== undefined)
|
|
279
|
+
universalConfig.colorDeltaE = config.colorDeltaE;
|
|
280
|
+
if (config.outlierMaxShare !== undefined)
|
|
281
|
+
universalConfig.outlierMaxShare = config.outlierMaxShare;
|
|
282
|
+
if (config.modeMinCount !== undefined)
|
|
283
|
+
universalConfig.modeMinCount = config.modeMinCount;
|
|
284
|
+
if (config.scaleProperties !== undefined)
|
|
285
|
+
universalConfig.scaleProperties = config.scaleProperties;
|
|
286
|
+
if (config.zIndexMaxDistinct !== undefined)
|
|
287
|
+
universalConfig.zIndexMaxDistinct = config.zIndexMaxDistinct;
|
|
288
|
+
if (config.mechanismFragmentationMinMechanisms !== undefined)
|
|
289
|
+
universalConfig.mechanismFragmentationMinMechanisms = config.mechanismFragmentationMinMechanisms;
|
|
290
|
+
if (config.declarationSetMinDeclarations !== undefined)
|
|
291
|
+
universalConfig.declarationSetMinDeclarations = config.declarationSetMinDeclarations;
|
|
292
|
+
if (config.declarationSetSimilarityThreshold !== undefined)
|
|
293
|
+
universalConfig.declarationSetSimilarityThreshold = config.declarationSetSimilarityThreshold;
|
|
294
|
+
// Forward base-class properties (Spec-20 path profiles + severity overrides)
|
|
295
|
+
if (config.severityOverrides !== undefined)
|
|
296
|
+
universalConfig.severityOverrides = config.severityOverrides;
|
|
297
|
+
if (config.pathProfiles !== undefined)
|
|
298
|
+
universalConfig.pathProfiles = config.pathProfiles;
|
|
299
|
+
if (config.projectRoot !== undefined)
|
|
300
|
+
universalConfig.projectRoot = config.projectRoot;
|
|
301
|
+
return analyzer.analyze(files, universalConfig, {
|
|
302
|
+
progressCallback: createProgressAdapter('styles', progressCallback),
|
|
303
|
+
...options,
|
|
304
|
+
});
|
|
305
|
+
},
|
|
306
|
+
},
|
|
307
|
+
'conventions': {
|
|
308
|
+
name: 'conventions',
|
|
309
|
+
description: 'Mines codebase conventions and flags deviations at suggestion severity',
|
|
310
|
+
category: 'style',
|
|
311
|
+
analyze: async (files, config, options, progressCallback) => {
|
|
312
|
+
const analyzer = new UniversalConventionsAnalyzer();
|
|
313
|
+
const universalConfig = {};
|
|
314
|
+
if (config.minCorpus !== undefined)
|
|
315
|
+
universalConfig.minCorpus = config.minCorpus;
|
|
316
|
+
if (config.pairConfidence !== undefined)
|
|
317
|
+
universalConfig.pairConfidence = config.pairConfidence;
|
|
318
|
+
if (config.modeShare !== undefined)
|
|
319
|
+
universalConfig.modeShare = config.modeShare;
|
|
320
|
+
if (config.maxConventionsPerDomain !== undefined)
|
|
321
|
+
universalConfig.maxConventionsPerDomain = config.maxConventionsPerDomain;
|
|
322
|
+
// Forward base-class properties (Spec-20 path profiles + severity overrides)
|
|
323
|
+
if (config.severityOverrides !== undefined)
|
|
324
|
+
universalConfig.severityOverrides = config.severityOverrides;
|
|
325
|
+
if (config.pathProfiles !== undefined)
|
|
326
|
+
universalConfig.pathProfiles = config.pathProfiles;
|
|
327
|
+
if (config.projectRoot !== undefined)
|
|
328
|
+
universalConfig.projectRoot = config.projectRoot;
|
|
329
|
+
return analyzer.analyze(files, universalConfig, {
|
|
330
|
+
progressCallback: createProgressAdapter('conventions', progressCallback),
|
|
331
|
+
...options,
|
|
332
|
+
});
|
|
333
|
+
},
|
|
334
|
+
},
|
|
335
|
+
'cross-domain': {
|
|
336
|
+
name: 'cross-domain',
|
|
337
|
+
description: 'Detects cross-domain issues (schema lifecycle, validation bypass, coverage gaps)',
|
|
338
|
+
category: 'architecture',
|
|
339
|
+
analyze: async (files, config, options, progressCallback) => {
|
|
340
|
+
const analyzer = new CrossDomainAnalyzer();
|
|
341
|
+
const universalConfig = {};
|
|
342
|
+
if (config.schemaLifecycle !== undefined)
|
|
343
|
+
universalConfig.schemaLifecycle = config.schemaLifecycle;
|
|
344
|
+
if (config.validatorBypass !== undefined)
|
|
345
|
+
universalConfig.validatorBypass = config.validatorBypass;
|
|
346
|
+
if (config.coverage !== undefined)
|
|
347
|
+
universalConfig.coverage = config.coverage;
|
|
348
|
+
// Forward base-class properties (Spec-20 path profiles + severity overrides)
|
|
349
|
+
if (config.severityOverrides !== undefined)
|
|
350
|
+
universalConfig.severityOverrides = config.severityOverrides;
|
|
351
|
+
if (config.pathProfiles !== undefined)
|
|
352
|
+
universalConfig.pathProfiles = config.pathProfiles;
|
|
353
|
+
if (config.projectRoot !== undefined)
|
|
354
|
+
universalConfig.projectRoot = config.projectRoot;
|
|
355
|
+
return analyzer.analyze(files, universalConfig, {
|
|
356
|
+
progressCallback: createProgressAdapter('cross-domain', progressCallback),
|
|
357
|
+
...options,
|
|
358
|
+
});
|
|
359
|
+
},
|
|
360
|
+
},
|
|
220
361
|
};
|
|
221
362
|
/**
|
|
222
363
|
* Create an audit runner with the given options
|
|
@@ -305,6 +446,44 @@ export function createAuditRunner(options = {}) {
|
|
|
305
446
|
});
|
|
306
447
|
}
|
|
307
448
|
}
|
|
449
|
+
// ── Blast-radius impact (Spec 14 R6) ──────────────────────────────
|
|
450
|
+
// Compute transitive-caller impact for changed functions.
|
|
451
|
+
// If latency exceeds the 100ms budget, skip and emit a warning;
|
|
452
|
+
// the feature is disabled-by-default when over budget.
|
|
453
|
+
let blastRadius;
|
|
454
|
+
if (isScoped && changedFunctions && changedFunctions.length > 0) {
|
|
455
|
+
try {
|
|
456
|
+
const db = CodeIndexDB.getInstance();
|
|
457
|
+
const rawDb = db.rawDb;
|
|
458
|
+
const functionIds = [];
|
|
459
|
+
for (const fn of changedFunctions) {
|
|
460
|
+
const row = rawDb.prepare('SELECT id FROM functions WHERE name = ? AND file_path = ?').get(fn.name, fn.filePath);
|
|
461
|
+
if (row)
|
|
462
|
+
functionIds.push(row.id);
|
|
463
|
+
}
|
|
464
|
+
if (functionIds.length > 0) {
|
|
465
|
+
const impact = computeImpact(rawDb, functionIds);
|
|
466
|
+
if (impact.latencyMs > LATENCY_BUDGET_MS) {
|
|
467
|
+
logMcpInfo('blast-radius', `latency ${impact.latencyMs}ms exceeds ${LATENCY_BUDGET_MS}ms budget — blast radius disabled for this run`, {});
|
|
468
|
+
}
|
|
469
|
+
else {
|
|
470
|
+
blastRadius = impact;
|
|
471
|
+
logMcpInfo('blast-radius', 'computed', {
|
|
472
|
+
editedFunctionCount: impact.editedFunctionCount,
|
|
473
|
+
transitiveCallers: impact.transitiveCallers,
|
|
474
|
+
reachableExports: impact.reachableExports,
|
|
475
|
+
latencyMs: impact.latencyMs,
|
|
476
|
+
});
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
catch (err) {
|
|
481
|
+
// Blast radius is advisory — non-fatal
|
|
482
|
+
logMcpInfo('blast-radius', 'computation failed (continuing)', {
|
|
483
|
+
error: err instanceof Error ? err.message : String(err)
|
|
484
|
+
});
|
|
485
|
+
}
|
|
486
|
+
}
|
|
308
487
|
const handoffRemaining = [];
|
|
309
488
|
const maxPerRun = mergedOptions.maxFilesPerRun;
|
|
310
489
|
if (typeof maxPerRun === 'number' && maxPerRun > 0 && files.length > maxPerRun) {
|
|
@@ -391,6 +570,31 @@ export function createAuditRunner(options = {}) {
|
|
|
391
570
|
// Run analyzers
|
|
392
571
|
const analyzerResults = {};
|
|
393
572
|
const enabledAnalyzers = getEnabledAnalyzers(mergedOptions, analyzerRegistry);
|
|
573
|
+
// ── Style index sync (Spec 10) ────────────────────────────────────
|
|
574
|
+
// Sync style declarations, tokens, and class usage before the
|
|
575
|
+
// styles analyzer runs, mirroring the function index sync pattern.
|
|
576
|
+
if (enabledAnalyzers.includes('styles')) {
|
|
577
|
+
try {
|
|
578
|
+
const styleDb = CodeIndexDB.getInstance();
|
|
579
|
+
const styleSyncResult = await syncStyleIndex(styleDb.rawDb, files, root, { scoped: isScoped });
|
|
580
|
+
logMcpInfo('style-index', 'style index sync complete', {
|
|
581
|
+
changed: styleSyncResult.changed,
|
|
582
|
+
skipped: styleSyncResult.skipped,
|
|
583
|
+
removed: styleSyncResult.removed,
|
|
584
|
+
errors: styleSyncResult.errors,
|
|
585
|
+
});
|
|
586
|
+
}
|
|
587
|
+
catch (err) {
|
|
588
|
+
// Non-fatal: styles analyzer will run with whatever is in the index
|
|
589
|
+
logMcpInfo('style-index', 'style index sync failed (continuing)', {
|
|
590
|
+
error: err instanceof Error ? err.message : String(err)
|
|
591
|
+
});
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
// Spec 21: Shared timing accumulator for provenance resolution.
|
|
595
|
+
// Injected into data-access and schema analyzers so they can report
|
|
596
|
+
// per-file buildProvenanceContext() wall time (hook-latency measurement).
|
|
597
|
+
const provenanceTiming = { totalMs: 0 };
|
|
394
598
|
logMcpInfo('analysis', 'enabled analyzers', {
|
|
395
599
|
names: enabledAnalyzers,
|
|
396
600
|
fileCount: files.length,
|
|
@@ -421,11 +625,28 @@ export function createAuditRunner(options = {}) {
|
|
|
421
625
|
analyzer: analyzerName,
|
|
422
626
|
message: `Running ${analyzerName} analyzer...`
|
|
423
627
|
});
|
|
424
|
-
// Build analyzer config; inject full index for DRY on scoped runs
|
|
425
|
-
|
|
628
|
+
// Build analyzer config; inject full index for DRY on scoped runs.
|
|
629
|
+
// Skip pathProfiles for invariants (Spec-20 R3): no backdoor
|
|
630
|
+
// around declared laws — invariants severity is absolute.
|
|
631
|
+
const isInvariants = analyzerName === 'invariants';
|
|
632
|
+
const analyzerConfig = {
|
|
633
|
+
...(mergedOptions.analyzerConfigs?.[analyzerName] || {}),
|
|
634
|
+
};
|
|
635
|
+
if (!isInvariants) {
|
|
636
|
+
analyzerConfig.pathProfiles = mergedOptions.pathProfiles;
|
|
637
|
+
analyzerConfig.projectRoot = root;
|
|
638
|
+
analyzerConfig.severityOverrides = mergedOptions.severityOverrides;
|
|
639
|
+
}
|
|
426
640
|
if (analyzerName === 'dry' && isScoped && fullFunctionIndex) {
|
|
427
641
|
analyzerConfig.fullFunctionIndex = fullFunctionIndex;
|
|
428
642
|
}
|
|
643
|
+
// Spec 21: Pass shared provenance timing accumulator to analyzers
|
|
644
|
+
// that call buildProvenanceContext() (data-access and schema).
|
|
645
|
+
// The analyzers accumulate per-file timing into this object;
|
|
646
|
+
// we read it back after all analyzers complete.
|
|
647
|
+
if (analyzerName === 'data-access' || analyzerName === 'schema') {
|
|
648
|
+
analyzerConfig._provenanceTiming = provenanceTiming;
|
|
649
|
+
}
|
|
429
650
|
logMcpInfo('analysis', `running ${analyzerName}`, { fileCount: files.length });
|
|
430
651
|
try {
|
|
431
652
|
const result = await analyzer.analyze(files, analyzerConfig, mergedOptions, (progress) => {
|
|
@@ -474,6 +695,208 @@ export function createAuditRunner(options = {}) {
|
|
|
474
695
|
orderedAnalyzerResults[analyzerName] = analyzerResults[analyzerName];
|
|
475
696
|
}
|
|
476
697
|
}
|
|
698
|
+
// ── Spec 13 R5 Phase 1: Persist seeded DRY pairs ──────────────────
|
|
699
|
+
// Store pairs from DRY analysis in dry_pair_history for divergence tracking.
|
|
700
|
+
// Pair identity is fingerprint-based (file + nodeType + line),
|
|
701
|
+
// NOT content-hash-based — so a diverging clone stays the same pair.
|
|
702
|
+
let dryPersistRunId = null;
|
|
703
|
+
try {
|
|
704
|
+
const dryResult = orderedAnalyzerResults['dry'];
|
|
705
|
+
const dryPairs = dryResult?.dryPairs;
|
|
706
|
+
if (dryPairs && dryPairs.length > 0) {
|
|
707
|
+
const indexDb = CodeIndexDB.getInstance();
|
|
708
|
+
await indexDb.initialize();
|
|
709
|
+
dryPersistRunId = randomUUID();
|
|
710
|
+
const insertStmt = indexDb.rawDb.prepare(`
|
|
711
|
+
INSERT OR IGNORE INTO dry_pair_history
|
|
712
|
+
(pair_fingerprint, file1, symbol1, line1, content_hash1,
|
|
713
|
+
file2, symbol2, line2, content_hash2, similarity, timestamp, run_id)
|
|
714
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, datetime('now'), ?)
|
|
715
|
+
`);
|
|
716
|
+
const tx = indexDb.rawDb.transaction(() => {
|
|
717
|
+
for (const pair of dryPairs) {
|
|
718
|
+
insertStmt.run(pair.pairFingerprint, pair.file1, pair.symbol1, pair.line1, pair.contentHash1, pair.file2, pair.symbol2, pair.line2, pair.contentHash2, pair.similarity, dryPersistRunId);
|
|
719
|
+
}
|
|
720
|
+
});
|
|
721
|
+
tx();
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
catch {
|
|
725
|
+
// dry_pair_history persistence is advisory — non-fatal
|
|
726
|
+
}
|
|
727
|
+
// ── Baseline classification (Spec 18 R1) ────────────────────────────
|
|
728
|
+
const projectRoot = path.resolve(mergedOptions.projectRoot || process.cwd());
|
|
729
|
+
const baseline = loadBaseline(projectRoot);
|
|
730
|
+
let baselineMetadata;
|
|
731
|
+
if (baseline) {
|
|
732
|
+
const allViolations = Object.values(orderedAnalyzerResults).flatMap((r) => r.violations);
|
|
733
|
+
// For scoped runs, limit "fixed" to in-scope files
|
|
734
|
+
const scopedFiles = isScoped ? files : undefined;
|
|
735
|
+
const classified = matchFindings(allViolations, baseline, scopedFiles);
|
|
736
|
+
// Tag violations with their baseline status
|
|
737
|
+
for (const v of classified.new) {
|
|
738
|
+
v.new = true;
|
|
739
|
+
}
|
|
740
|
+
for (const v of classified.known) {
|
|
741
|
+
v.new = false;
|
|
742
|
+
}
|
|
743
|
+
baselineMetadata = {
|
|
744
|
+
present: true,
|
|
745
|
+
hash: hashBaseline(baseline),
|
|
746
|
+
newCount: classified.new.length,
|
|
747
|
+
fixedCount: classified.fixed.length,
|
|
748
|
+
knownCount: classified.known.length,
|
|
749
|
+
previousKnownCount: baseline.metadata.totalFindings,
|
|
750
|
+
};
|
|
751
|
+
logMcpInfo('baseline', 'baseline classification complete', {
|
|
752
|
+
new: classified.new.length,
|
|
753
|
+
fixed: classified.fixed.length,
|
|
754
|
+
known: classified.known.length,
|
|
755
|
+
});
|
|
756
|
+
}
|
|
757
|
+
// ── Spec 13 R2 — Hotspot scoring & finding reordering ──────────────
|
|
758
|
+
// Attach hotspot scores to violations and reorder within severity tiers.
|
|
759
|
+
// Falls back gracefully when no churn/hotspot data exists.
|
|
760
|
+
try {
|
|
761
|
+
const indexDb = CodeIndexDB.getInstance();
|
|
762
|
+
await indexDb.initialize();
|
|
763
|
+
// Build hotspot lookup: target -> score
|
|
764
|
+
const hotspotRows = indexDb.rawDb
|
|
765
|
+
.prepare('SELECT target, type, score FROM hotspot_scores')
|
|
766
|
+
.all();
|
|
767
|
+
if (hotspotRows.length > 0) {
|
|
768
|
+
const hotspotByTarget = new Map();
|
|
769
|
+
const hotspotByFile = new Map();
|
|
770
|
+
for (const row of hotspotRows) {
|
|
771
|
+
hotspotByTarget.set(row.target, row.score);
|
|
772
|
+
if (row.type === 'file') {
|
|
773
|
+
hotspotByFile.set(row.target, row.score);
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
// Attach hotspot to each violation and reorder within each analyzer
|
|
777
|
+
for (const analyzerName of Object.keys(orderedAnalyzerResults)) {
|
|
778
|
+
const result = orderedAnalyzerResults[analyzerName];
|
|
779
|
+
const violations = result.violations;
|
|
780
|
+
// Attach hotspot scores
|
|
781
|
+
for (const v of violations) {
|
|
782
|
+
const funcName = v.functionName;
|
|
783
|
+
const file = v.file;
|
|
784
|
+
// Try function-level hotspot first: "filePath::functionName"
|
|
785
|
+
const funcTarget = funcName ? `${file}::${funcName}` : undefined;
|
|
786
|
+
v.hotspot = (funcTarget ? hotspotByTarget.get(funcTarget) : undefined)
|
|
787
|
+
?? hotspotByFile.get(file)
|
|
788
|
+
?? 0;
|
|
789
|
+
}
|
|
790
|
+
// Reorder within severity tiers: sort by hotspot descending,
|
|
791
|
+
// preserving original order as tiebreaker.
|
|
792
|
+
const severityOrder = {
|
|
793
|
+
critical: 0,
|
|
794
|
+
warning: 1,
|
|
795
|
+
suggestion: 2,
|
|
796
|
+
info: 3,
|
|
797
|
+
};
|
|
798
|
+
const indexed = violations.map((v, i) => ({ v, i }));
|
|
799
|
+
indexed.sort((a, b) => {
|
|
800
|
+
const sevA = severityOrder[a.v.severity] ?? 99;
|
|
801
|
+
const sevB = severityOrder[b.v.severity] ?? 99;
|
|
802
|
+
if (sevA !== sevB)
|
|
803
|
+
return sevA - sevB;
|
|
804
|
+
// Within same severity: higher hotspot first
|
|
805
|
+
const hsA = a.v.hotspot ?? 0;
|
|
806
|
+
const hsB = b.v.hotspot ?? 0;
|
|
807
|
+
if (hsA !== hsB)
|
|
808
|
+
return hsB - hsA;
|
|
809
|
+
// Tiebreaker: original order
|
|
810
|
+
return a.i - b.i;
|
|
811
|
+
});
|
|
812
|
+
result.violations = indexed.map(x => x.v);
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
catch {
|
|
817
|
+
// Hotspot reordering is advisory — failure is non-fatal
|
|
818
|
+
}
|
|
819
|
+
// ── Spec 13 R5 Phase 2: Divergence tracking pass ──────────────────
|
|
820
|
+
// For every pair in dry_pair_history, compare the last 2 similarity
|
|
821
|
+
// measurements. If similarity has declined by ≥ divergenceThreshold
|
|
822
|
+
// for divergenceRuns consecutive runs, emit dry/diverging-clone.
|
|
823
|
+
//
|
|
824
|
+
// New rows are inserted by Phase 1 for pairs the DRY analyzer re-detects
|
|
825
|
+
// this run. Pairs that weren't re-detected don't get a new measurement
|
|
826
|
+
// this pass — full source re-read is deferred to the 20% case.
|
|
827
|
+
try {
|
|
828
|
+
const divergenceCfg = mergedOptions.analyzerConfigs?.dry?.divergence
|
|
829
|
+
?? mergedOptions.divergence
|
|
830
|
+
?? { divergenceThreshold: 0.05, divergenceRuns: 2, minPairSimilarity: 0.5 };
|
|
831
|
+
const threshold = divergenceCfg.divergenceThreshold ?? 0.05;
|
|
832
|
+
const requiredDeclines = divergenceCfg.divergenceRuns ?? 2;
|
|
833
|
+
if (threshold > 0) {
|
|
834
|
+
const indexDb = CodeIndexDB.getInstance();
|
|
835
|
+
await indexDb.initialize();
|
|
836
|
+
// Get all distinct pair fingerprints
|
|
837
|
+
const fingerprints = indexDb.rawDb
|
|
838
|
+
.prepare('SELECT DISTINCT pair_fingerprint FROM dry_pair_history')
|
|
839
|
+
.all();
|
|
840
|
+
if (fingerprints.length > 0) {
|
|
841
|
+
const getRows = indexDb.rawDb.prepare(`
|
|
842
|
+
SELECT similarity, timestamp
|
|
843
|
+
FROM dry_pair_history
|
|
844
|
+
WHERE pair_fingerprint = ?
|
|
845
|
+
ORDER BY timestamp ASC
|
|
846
|
+
`);
|
|
847
|
+
const divergingViolations = [];
|
|
848
|
+
for (const { pair_fingerprint: fp } of fingerprints) {
|
|
849
|
+
const rows = getRows.all(fp);
|
|
850
|
+
if (rows.length < requiredDeclines + 1)
|
|
851
|
+
continue;
|
|
852
|
+
// Check last `requiredDeclines` consecutive pairs for decline
|
|
853
|
+
let consecutiveDeclines = 0;
|
|
854
|
+
for (let i = rows.length - requiredDeclines; i < rows.length; i++) {
|
|
855
|
+
if (rows[i].similarity < rows[i - 1].similarity - threshold) {
|
|
856
|
+
consecutiveDeclines++;
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
if (consecutiveDeclines >= requiredDeclines) {
|
|
860
|
+
// Get file/line info from the last row
|
|
861
|
+
const lastRow = indexDb.rawDb.prepare(`
|
|
862
|
+
SELECT file1, file2, line1, line2 FROM dry_pair_history
|
|
863
|
+
WHERE pair_fingerprint = ?
|
|
864
|
+
ORDER BY timestamp DESC LIMIT 1
|
|
865
|
+
`).get(fp);
|
|
866
|
+
if (lastRow) {
|
|
867
|
+
const currentSim = rows[rows.length - 1].similarity;
|
|
868
|
+
const prevSim = rows[rows.length - 2].similarity;
|
|
869
|
+
const drop = Math.round((prevSim - currentSim) * 1000) / 1000;
|
|
870
|
+
divergingViolations.push({
|
|
871
|
+
file: lastRow.file1,
|
|
872
|
+
line: lastRow.line1,
|
|
873
|
+
severity: 'suggestion',
|
|
874
|
+
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.`,
|
|
875
|
+
analyzer: 'dry',
|
|
876
|
+
rule: 'dry/diverging-clone',
|
|
877
|
+
});
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
if (divergingViolations.length > 0) {
|
|
882
|
+
// Append to the dry analyzer result, or create one if none exists
|
|
883
|
+
if (orderedAnalyzerResults['dry']) {
|
|
884
|
+
orderedAnalyzerResults['dry'].violations.push(...divergingViolations);
|
|
885
|
+
}
|
|
886
|
+
else {
|
|
887
|
+
orderedAnalyzerResults['dry'] = {
|
|
888
|
+
violations: divergingViolations,
|
|
889
|
+
filesProcessed: 0,
|
|
890
|
+
executionTime: 0,
|
|
891
|
+
};
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
catch {
|
|
898
|
+
// Divergence tracking is advisory — non-fatal
|
|
899
|
+
}
|
|
477
900
|
// Generate summary
|
|
478
901
|
const summary = generateSummary(orderedAnalyzerResults, files.length);
|
|
479
902
|
// Create result
|
|
@@ -488,6 +911,9 @@ export function createAuditRunner(options = {}) {
|
|
|
488
911
|
analyzersRun: enabledAnalyzers,
|
|
489
912
|
configUsed: mergedOptions,
|
|
490
913
|
scope: scopeResultType,
|
|
914
|
+
provenanceResolutionMs: provenanceTiming.totalMs,
|
|
915
|
+
...(blastRadius && { blastRadius }),
|
|
916
|
+
...(baselineMetadata && { baseline: baselineMetadata }),
|
|
491
917
|
...(collectedFunctions.length > 0 && {
|
|
492
918
|
collectedFunctions,
|
|
493
919
|
fileToFunctionsMap: Object.fromEntries(fileToFunctionsMap)
|
|
@@ -502,6 +928,25 @@ export function createAuditRunner(options = {}) {
|
|
|
502
928
|
if (handoffRemaining.length > 0) {
|
|
503
929
|
throw new AuditHandoffError(`${handoffRemaining.length} file(s) deferred to the next worker chunk`, result, handoffRemaining);
|
|
504
930
|
}
|
|
931
|
+
// Spec 11 R1 — write to findings ledger (non-fatal: ledger is advisory)
|
|
932
|
+
const ledgerRunId = (async () => {
|
|
933
|
+
try {
|
|
934
|
+
const indexDb = CodeIndexDB.getInstance();
|
|
935
|
+
await indexDb.initialize();
|
|
936
|
+
const violations = Object.values(orderedAnalyzerResults).flatMap(ar => ar.violations);
|
|
937
|
+
// Hook-contract guard: no violation may carry an empty file path or line 0.
|
|
938
|
+
// A sentinel violation with file:'' broke the Claude Code hook's JSON consumer
|
|
939
|
+
// (Spec 15 regression). This guard is permanent — every analyzer code path
|
|
940
|
+
// must produce properly anchored violations.
|
|
941
|
+
validateHookContract(violations);
|
|
942
|
+
const scopeStr = Array.isArray(scope) ? `files:${scope.length}` : (scope ?? 'all');
|
|
943
|
+
return writeAuditToLedger(indexDb.rawDb, detectRunInput(process.argv.slice(2).join(' '), options.surface ?? 'cli', scopeStr, root, TOOL_VERSION), violations, Date.now() - startTime, 0);
|
|
944
|
+
}
|
|
945
|
+
catch (_err) {
|
|
946
|
+
// ledger write is non-fatal — audit result is still valid
|
|
947
|
+
return null;
|
|
948
|
+
}
|
|
949
|
+
})();
|
|
505
950
|
return result;
|
|
506
951
|
}
|
|
507
952
|
/**
|
|
@@ -692,6 +1137,41 @@ function generateSummary(analyzerResults, filesAnalyzed) {
|
|
|
692
1137
|
topIssues
|
|
693
1138
|
};
|
|
694
1139
|
}
|
|
1140
|
+
/**
|
|
1141
|
+
* Hook-contract regression guard.
|
|
1142
|
+
*
|
|
1143
|
+
* Every violation must carry a non-empty `file` path. A sentinel violation with
|
|
1144
|
+
* `file: ''` (Spec 15 regression, commit 3419ed0) broke the Claude Code hook's
|
|
1145
|
+
* JSON consumer, which expects every violation to anchor to a real file.
|
|
1146
|
+
*
|
|
1147
|
+
* When `line` is present, it must be > 0 — a zero line means the analyzer
|
|
1148
|
+
* failed to locate the finding and shipped an unactionable anchor.
|
|
1149
|
+
*
|
|
1150
|
+
* Contract violations are logged as warnings and dropped from the pipeline
|
|
1151
|
+
* (they are never written to the ledger or surfaced to the hook).
|
|
1152
|
+
*
|
|
1153
|
+
* This guard is permanent — no analyzer code path may produce violations with
|
|
1154
|
+
* empty file paths or line 0.
|
|
1155
|
+
*/
|
|
1156
|
+
function validateHookContract(violations) {
|
|
1157
|
+
for (let i = violations.length - 1; i >= 0; i--) {
|
|
1158
|
+
const v = violations[i];
|
|
1159
|
+
const problems = [];
|
|
1160
|
+
if (!v.file || v.file.trim() === '') {
|
|
1161
|
+
problems.push('empty file path');
|
|
1162
|
+
}
|
|
1163
|
+
if (v.line !== undefined && v.line === 0) {
|
|
1164
|
+
problems.push('line=0 (unactionable anchor)');
|
|
1165
|
+
}
|
|
1166
|
+
if (problems.length > 0) {
|
|
1167
|
+
console.warn(`[hook-contract] Dropping violation from analyzer '${v.analyzer ?? 'unknown'}': ` +
|
|
1168
|
+
`${problems.join(', ')}. Rule: ${v.rule ?? 'unknown'}. ` +
|
|
1169
|
+
`Message: "${v.message.slice(0, 120)}". ` +
|
|
1170
|
+
`This is a bug in the analyzer — violations must anchor to real files with valid lines.`);
|
|
1171
|
+
violations.splice(i, 1);
|
|
1172
|
+
}
|
|
1173
|
+
}
|
|
1174
|
+
}
|
|
695
1175
|
/**
|
|
696
1176
|
* Report progress
|
|
697
1177
|
*/
|