code-auditor-mcp 3.1.1 → 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 -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 +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 +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/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/codeIndexDB.js
CHANGED
|
@@ -8,6 +8,11 @@ import { promises as fs } from 'fs';
|
|
|
8
8
|
import path from 'path';
|
|
9
9
|
import { discoverFiles, ALL_EXTENSIONS } from './utils/fileDiscovery.js';
|
|
10
10
|
import { ProjectTaskRepository } from './services/ProjectTaskRepository.js';
|
|
11
|
+
import { mineConventions, computeMinerInputHash } from './conventions/conventionMiner.js';
|
|
12
|
+
import { extractChurn } from './churn/churnExtractor.js';
|
|
13
|
+
import { computeHotspots } from './hotspots/hotspotScorer.js';
|
|
14
|
+
import { populateCallGraphCache, getGraphStats as getGs } from './graph/callGraph.js';
|
|
15
|
+
import { populateImportGraphCache } from './graph/importGraph.js';
|
|
11
16
|
import { QueryParser, compileToSQL } from './search/QueryParser.js';
|
|
12
17
|
import { getPersistedStorageRoot, resolvePersistedIndexPath } from './dataPaths.js';
|
|
13
18
|
import { ContextualError, getErrnoCode } from './mcpToolErrors.js';
|
|
@@ -203,6 +208,10 @@ export class CodeIndexDB {
|
|
|
203
208
|
static instance;
|
|
204
209
|
/** Subclasses (e.g. EnhancedCodeIndexDB) need access for extra tables without `as any`. */
|
|
205
210
|
db;
|
|
211
|
+
/** Public access to raw SQLite handle — used by ledger writes from external surfaces. */
|
|
212
|
+
get rawDb() {
|
|
213
|
+
return this.db;
|
|
214
|
+
}
|
|
206
215
|
dbPath;
|
|
207
216
|
isInitialized = false;
|
|
208
217
|
initializePromise = null;
|
|
@@ -215,23 +224,44 @@ export class CodeIndexDB {
|
|
|
215
224
|
schemaAdapter;
|
|
216
225
|
schemaUsageAdapter;
|
|
217
226
|
tasksAdapter;
|
|
227
|
+
conventionsAdapter;
|
|
228
|
+
fileChurnAdapter;
|
|
229
|
+
functionChurnAdapter;
|
|
230
|
+
pairHistoryAdapter;
|
|
231
|
+
hotspotScoresAdapter;
|
|
232
|
+
graphCacheAdapter;
|
|
218
233
|
taskRepository = null;
|
|
219
234
|
stmts = new Map();
|
|
220
235
|
// ── Schema version ──────────────────────────────────────────────────
|
|
221
|
-
static SCHEMA_VERSION =
|
|
236
|
+
static SCHEMA_VERSION = 7;
|
|
222
237
|
constructor(dbPath = ':memory:') {
|
|
223
238
|
this.dbPath = dbPath === ':memory:' ? dbPath : path.resolve(dbPath);
|
|
224
239
|
}
|
|
225
240
|
// ── Singleton ───────────────────────────────────────────────────────
|
|
226
241
|
static getInstance(dbPath) {
|
|
227
242
|
if (!CodeIndexDB.instance) {
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
:
|
|
243
|
+
let resolved;
|
|
244
|
+
if (dbPath !== undefined && dbPath !== '') {
|
|
245
|
+
resolved = dbPath === ':memory:' ? ':memory:' : path.resolve(dbPath);
|
|
246
|
+
}
|
|
247
|
+
else {
|
|
248
|
+
resolved = resolvePersistedIndexPath();
|
|
249
|
+
}
|
|
231
250
|
CodeIndexDB.instance = new CodeIndexDB(resolved);
|
|
232
251
|
}
|
|
233
252
|
return CodeIndexDB.instance;
|
|
234
253
|
}
|
|
254
|
+
/** Reset the singleton — used by tests that need a fresh in-memory instance. */
|
|
255
|
+
static resetInstance() {
|
|
256
|
+
if (CodeIndexDB.instance) {
|
|
257
|
+
try {
|
|
258
|
+
CodeIndexDB.instance.db.close();
|
|
259
|
+
}
|
|
260
|
+
catch { /* ignore */ }
|
|
261
|
+
CodeIndexDB.instance.isInitialized = false;
|
|
262
|
+
}
|
|
263
|
+
CodeIndexDB.instance = null;
|
|
264
|
+
}
|
|
235
265
|
// ── Lifecycle ───────────────────────────────────────────────────────
|
|
236
266
|
async initialize() {
|
|
237
267
|
if (this.isInitialized)
|
|
@@ -305,6 +335,12 @@ export class CodeIndexDB {
|
|
|
305
335
|
this.schemaAdapter = new SqliteCollectionAdapter(this.db, 'schema_definitions');
|
|
306
336
|
this.schemaUsageAdapter = new SqliteCollectionAdapter(this.db, 'schema_usage');
|
|
307
337
|
this.tasksAdapter = new SqliteCollectionAdapter(this.db, 'project_tasks');
|
|
338
|
+
this.conventionsAdapter = new SqliteCollectionAdapter(this.db, 'conventions');
|
|
339
|
+
this.fileChurnAdapter = new SqliteCollectionAdapter(this.db, 'file_churn');
|
|
340
|
+
this.functionChurnAdapter = new SqliteCollectionAdapter(this.db, 'function_churn');
|
|
341
|
+
this.pairHistoryAdapter = new SqliteCollectionAdapter(this.db, 'dry_pair_history');
|
|
342
|
+
this.hotspotScoresAdapter = new SqliteCollectionAdapter(this.db, 'hotspot_scores');
|
|
343
|
+
this.graphCacheAdapter = new SqliteCollectionAdapter(this.db, 'graph_cache');
|
|
308
344
|
// Init defaults
|
|
309
345
|
const whitelistCount = this.db.prepare('SELECT COUNT(*) as cnt FROM whitelist').get().cnt;
|
|
310
346
|
if (whitelistCount === 0) {
|
|
@@ -338,6 +374,183 @@ export class CodeIndexDB {
|
|
|
338
374
|
CREATE UNIQUE INDEX IF NOT EXISTS idx_functions_name_file_line ON functions(name, file_path, line_number);
|
|
339
375
|
`);
|
|
340
376
|
}
|
|
377
|
+
// Migration 2 → 3: Style intelligence tables (Spec 10)
|
|
378
|
+
if (currentVersion < 3) {
|
|
379
|
+
this.db.exec(`
|
|
380
|
+
CREATE TABLE IF NOT EXISTS style_declarations (
|
|
381
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
382
|
+
property TEXT NOT NULL,
|
|
383
|
+
raw_value TEXT NOT NULL,
|
|
384
|
+
normalized_value TEXT,
|
|
385
|
+
mechanism TEXT NOT NULL,
|
|
386
|
+
file_path TEXT NOT NULL,
|
|
387
|
+
line INTEGER NOT NULL,
|
|
388
|
+
context TEXT,
|
|
389
|
+
variant_context TEXT,
|
|
390
|
+
token_ref TEXT,
|
|
391
|
+
content_hash TEXT,
|
|
392
|
+
created_at TEXT DEFAULT (datetime('now'))
|
|
393
|
+
);
|
|
394
|
+
CREATE INDEX IF NOT EXISTS idx_style_decls_property ON style_declarations(property);
|
|
395
|
+
CREATE INDEX IF NOT EXISTS idx_style_decls_mechanism ON style_declarations(mechanism);
|
|
396
|
+
CREATE INDEX IF NOT EXISTS idx_style_decls_file_path ON style_declarations(file_path);
|
|
397
|
+
CREATE INDEX IF NOT EXISTS idx_style_decls_token_ref ON style_declarations(token_ref);
|
|
398
|
+
CREATE INDEX IF NOT EXISTS idx_style_decls_content_hash ON style_declarations(content_hash);
|
|
399
|
+
|
|
400
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS style_declarations_fts USING fts5(
|
|
401
|
+
property, raw_value, normalized_value,
|
|
402
|
+
content='style_declarations', content_rowid='id',
|
|
403
|
+
tokenize='porter unicode61'
|
|
404
|
+
);
|
|
405
|
+
|
|
406
|
+
CREATE TABLE IF NOT EXISTS style_tokens (
|
|
407
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
408
|
+
name TEXT NOT NULL,
|
|
409
|
+
value TEXT NOT NULL,
|
|
410
|
+
file_path TEXT NOT NULL,
|
|
411
|
+
mechanism TEXT NOT NULL,
|
|
412
|
+
created_at TEXT DEFAULT (datetime('now'))
|
|
413
|
+
);
|
|
414
|
+
CREATE INDEX IF NOT EXISTS idx_style_tokens_name ON style_tokens(name);
|
|
415
|
+
|
|
416
|
+
CREATE TABLE IF NOT EXISTS style_class_usage (
|
|
417
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
418
|
+
class_name TEXT NOT NULL,
|
|
419
|
+
file_path TEXT NOT NULL,
|
|
420
|
+
line INTEGER NOT NULL,
|
|
421
|
+
mechanism TEXT NOT NULL,
|
|
422
|
+
unresolvable INTEGER DEFAULT 0,
|
|
423
|
+
created_at TEXT DEFAULT (datetime('now'))
|
|
424
|
+
);
|
|
425
|
+
CREATE INDEX IF NOT EXISTS idx_style_class_usage_name ON style_class_usage(class_name);
|
|
426
|
+
CREATE INDEX IF NOT EXISTS idx_style_class_usage_file ON style_class_usage(file_path);
|
|
427
|
+
CREATE INDEX IF NOT EXISTS idx_style_class_usage_unres ON style_class_usage(unresolvable);
|
|
428
|
+
`);
|
|
429
|
+
}
|
|
430
|
+
// Migration 3 → 4: Convention mining tables (Spec 12)
|
|
431
|
+
if (currentVersion < 4) {
|
|
432
|
+
this.db.exec(`
|
|
433
|
+
CREATE TABLE IF NOT EXISTS conventions (
|
|
434
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
435
|
+
domain TEXT NOT NULL,
|
|
436
|
+
rule_id TEXT NOT NULL,
|
|
437
|
+
antecedent TEXT,
|
|
438
|
+
consequent TEXT,
|
|
439
|
+
pattern TEXT,
|
|
440
|
+
directory TEXT,
|
|
441
|
+
file_path TEXT,
|
|
442
|
+
line INTEGER,
|
|
443
|
+
support INTEGER DEFAULT 0,
|
|
444
|
+
total_cases INTEGER DEFAULT 0,
|
|
445
|
+
confidence REAL DEFAULT 0,
|
|
446
|
+
exemplar_file TEXT,
|
|
447
|
+
exemplar_line INTEGER,
|
|
448
|
+
hash TEXT,
|
|
449
|
+
created_at TEXT DEFAULT (datetime('now'))
|
|
450
|
+
);
|
|
451
|
+
CREATE INDEX IF NOT EXISTS idx_conv_domain ON conventions(domain);
|
|
452
|
+
CREATE INDEX IF NOT EXISTS idx_conv_rule_id ON conventions(rule_id);
|
|
453
|
+
CREATE INDEX IF NOT EXISTS idx_conv_directory ON conventions(directory);
|
|
454
|
+
CREATE INDEX IF NOT EXISTS idx_conv_hash ON conventions(hash);
|
|
455
|
+
`);
|
|
456
|
+
}
|
|
457
|
+
// Migration 4 → 5: Hotspots & temporal analysis (Spec 13)
|
|
458
|
+
if (currentVersion < 5) {
|
|
459
|
+
this.db.exec(`
|
|
460
|
+
CREATE TABLE IF NOT EXISTS file_churn (
|
|
461
|
+
file_path TEXT PRIMARY KEY,
|
|
462
|
+
commit_count INTEGER NOT NULL DEFAULT 0,
|
|
463
|
+
lines_added INTEGER NOT NULL DEFAULT 0,
|
|
464
|
+
lines_deleted INTEGER NOT NULL DEFAULT 0,
|
|
465
|
+
distinct_authors INTEGER NOT NULL DEFAULT 0,
|
|
466
|
+
dominant_author TEXT,
|
|
467
|
+
dominant_author_share REAL DEFAULT 0,
|
|
468
|
+
last_touched TEXT
|
|
469
|
+
);
|
|
470
|
+
CREATE INDEX IF NOT EXISTS idx_file_churn_cc ON file_churn(commit_count);
|
|
471
|
+
|
|
472
|
+
CREATE TABLE IF NOT EXISTS function_churn (
|
|
473
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
474
|
+
function_id INTEGER REFERENCES functions(id) ON DELETE CASCADE,
|
|
475
|
+
function_name TEXT NOT NULL,
|
|
476
|
+
file_path TEXT NOT NULL,
|
|
477
|
+
commit_count INTEGER NOT NULL DEFAULT 0,
|
|
478
|
+
distinct_authors INTEGER NOT NULL DEFAULT 0,
|
|
479
|
+
dominant_author TEXT,
|
|
480
|
+
dominant_author_share REAL DEFAULT 0,
|
|
481
|
+
renamed INTEGER DEFAULT 0,
|
|
482
|
+
confidence REAL DEFAULT 1.0
|
|
483
|
+
);
|
|
484
|
+
CREATE INDEX IF NOT EXISTS idx_func_churn_fid ON function_churn(function_id);
|
|
485
|
+
|
|
486
|
+
CREATE TABLE IF NOT EXISTS hotspot_scores (
|
|
487
|
+
target TEXT PRIMARY KEY,
|
|
488
|
+
type TEXT NOT NULL,
|
|
489
|
+
score REAL NOT NULL DEFAULT 0,
|
|
490
|
+
churn_pct REAL NOT NULL DEFAULT 0,
|
|
491
|
+
complexity_pct REAL NOT NULL DEFAULT 0,
|
|
492
|
+
commit_count INTEGER NOT NULL DEFAULT 0,
|
|
493
|
+
distinct_authors INTEGER NOT NULL DEFAULT 0,
|
|
494
|
+
dominant_author TEXT,
|
|
495
|
+
dominant_author_share REAL DEFAULT 0,
|
|
496
|
+
bus_factor_risk INTEGER DEFAULT 0,
|
|
497
|
+
complexity INTEGER NOT NULL DEFAULT 0,
|
|
498
|
+
updated_at TEXT DEFAULT (datetime('now'))
|
|
499
|
+
);
|
|
500
|
+
CREATE INDEX IF NOT EXISTS idx_hotspot_scores_score ON hotspot_scores(score DESC);
|
|
501
|
+
|
|
502
|
+
CREATE TABLE IF NOT EXISTS dry_pair_history (
|
|
503
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
504
|
+
pair_fingerprint TEXT NOT NULL,
|
|
505
|
+
file1 TEXT NOT NULL,
|
|
506
|
+
symbol1 TEXT,
|
|
507
|
+
line1 INTEGER NOT NULL,
|
|
508
|
+
content_hash1 TEXT NOT NULL,
|
|
509
|
+
file2 TEXT NOT NULL,
|
|
510
|
+
symbol2 TEXT,
|
|
511
|
+
line2 INTEGER NOT NULL,
|
|
512
|
+
content_hash2 TEXT NOT NULL,
|
|
513
|
+
similarity REAL NOT NULL,
|
|
514
|
+
timestamp TEXT DEFAULT (datetime('now')),
|
|
515
|
+
run_id TEXT NOT NULL
|
|
516
|
+
);
|
|
517
|
+
CREATE INDEX IF NOT EXISTS idx_dph_fingerprint ON dry_pair_history(pair_fingerprint);
|
|
518
|
+
CREATE INDEX IF NOT EXISTS idx_dph_run ON dry_pair_history(run_id);
|
|
519
|
+
`);
|
|
520
|
+
}
|
|
521
|
+
// Migration 5 → 6: Graph cache for call/import graph construction (Spec 14)
|
|
522
|
+
if (currentVersion < 6) {
|
|
523
|
+
this.db.exec(`
|
|
524
|
+
CREATE TABLE IF NOT EXISTS graph_cache (
|
|
525
|
+
graph_type TEXT NOT NULL,
|
|
526
|
+
node_key TEXT NOT NULL,
|
|
527
|
+
neighbor_key TEXT NOT NULL,
|
|
528
|
+
weight REAL NOT NULL,
|
|
529
|
+
PRIMARY KEY (graph_type, node_key, neighbor_key)
|
|
530
|
+
);
|
|
531
|
+
CREATE INDEX IF NOT EXISTS idx_gc_type_node ON graph_cache(graph_type, node_key);
|
|
532
|
+
CREATE INDEX IF NOT EXISTS idx_gc_type_neighbor ON graph_cache(graph_type, neighbor_key);
|
|
533
|
+
`);
|
|
534
|
+
}
|
|
535
|
+
// Migration 6 → 7: Coverage data for cross-domain analysis (Spec 15)
|
|
536
|
+
if (currentVersion < 7) {
|
|
537
|
+
this.db.exec(`
|
|
538
|
+
CREATE TABLE IF NOT EXISTS coverage_data (
|
|
539
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
540
|
+
function_name TEXT NOT NULL,
|
|
541
|
+
file_path TEXT NOT NULL,
|
|
542
|
+
line_number INTEGER,
|
|
543
|
+
basis TEXT NOT NULL DEFAULT 'static-reach',
|
|
544
|
+
covered INTEGER NOT NULL DEFAULT 0,
|
|
545
|
+
source TEXT,
|
|
546
|
+
imported_at TEXT DEFAULT (datetime('now')),
|
|
547
|
+
UNIQUE(function_name, file_path, line_number)
|
|
548
|
+
);
|
|
549
|
+
CREATE INDEX IF NOT EXISTS idx_cov_basis ON coverage_data(basis);
|
|
550
|
+
CREATE INDEX IF NOT EXISTS idx_cov_covered ON coverage_data(covered);
|
|
551
|
+
CREATE INDEX IF NOT EXISTS idx_cov_function ON coverage_data(function_name);
|
|
552
|
+
`);
|
|
553
|
+
}
|
|
341
554
|
}
|
|
342
555
|
// ── SQLite schema ───────────────────────────────────────────────────
|
|
343
556
|
createSchema() {
|
|
@@ -508,6 +721,21 @@ export class CodeIndexDB {
|
|
|
508
721
|
CREATE INDEX IF NOT EXISTS idx_schema_usage_file ON schema_usage(file_path);
|
|
509
722
|
CREATE INDEX IF NOT EXISTS idx_schema_usage_function ON schema_usage(function_name);
|
|
510
723
|
|
|
724
|
+
CREATE TABLE IF NOT EXISTS coverage_data (
|
|
725
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
726
|
+
function_name TEXT NOT NULL,
|
|
727
|
+
file_path TEXT NOT NULL,
|
|
728
|
+
line_number INTEGER,
|
|
729
|
+
basis TEXT NOT NULL DEFAULT 'static-reach',
|
|
730
|
+
covered INTEGER NOT NULL DEFAULT 0,
|
|
731
|
+
source TEXT,
|
|
732
|
+
imported_at TEXT DEFAULT (datetime('now')),
|
|
733
|
+
UNIQUE(function_name, file_path, line_number)
|
|
734
|
+
);
|
|
735
|
+
CREATE INDEX IF NOT EXISTS idx_cov_basis ON coverage_data(basis);
|
|
736
|
+
CREATE INDEX IF NOT EXISTS idx_cov_covered ON coverage_data(covered);
|
|
737
|
+
CREATE INDEX IF NOT EXISTS idx_cov_function ON coverage_data(function_name);
|
|
738
|
+
|
|
511
739
|
CREATE TABLE IF NOT EXISTS project_tasks (
|
|
512
740
|
taskId TEXT PRIMARY KEY,
|
|
513
741
|
projectPath TEXT NOT NULL,
|
|
@@ -534,6 +762,174 @@ export class CodeIndexDB {
|
|
|
534
762
|
CREATE INDEX IF NOT EXISTS idx_project_tasks_source ON project_tasks(source);
|
|
535
763
|
CREATE INDEX IF NOT EXISTS idx_project_tasks_fingerprint ON project_tasks(fingerprint);
|
|
536
764
|
CREATE INDEX IF NOT EXISTS idx_project_tasks_sort ON project_tasks(sortOrder);
|
|
765
|
+
|
|
766
|
+
-- Spec 11 R1 — Findings Ledger: append-only audit history
|
|
767
|
+
CREATE TABLE IF NOT EXISTS findings_ledger_runs (
|
|
768
|
+
run_id TEXT PRIMARY KEY,
|
|
769
|
+
timestamp TEXT NOT NULL,
|
|
770
|
+
git_sha TEXT,
|
|
771
|
+
git_dirty INTEGER NOT NULL DEFAULT 0,
|
|
772
|
+
tool_version TEXT NOT NULL,
|
|
773
|
+
command TEXT NOT NULL,
|
|
774
|
+
surface TEXT NOT NULL,
|
|
775
|
+
scope TEXT NOT NULL,
|
|
776
|
+
target TEXT NOT NULL,
|
|
777
|
+
duration_ms INTEGER NOT NULL DEFAULT 0,
|
|
778
|
+
exit_status INTEGER NOT NULL DEFAULT 0,
|
|
779
|
+
metadata_json TEXT DEFAULT '{}'
|
|
780
|
+
);
|
|
781
|
+
CREATE TABLE IF NOT EXISTS findings_ledger_findings (
|
|
782
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
783
|
+
run_id TEXT NOT NULL REFERENCES findings_ledger_runs(run_id) ON DELETE CASCADE,
|
|
784
|
+
analyzer TEXT NOT NULL,
|
|
785
|
+
rule TEXT NOT NULL,
|
|
786
|
+
severity TEXT NOT NULL,
|
|
787
|
+
message TEXT NOT NULL,
|
|
788
|
+
file TEXT NOT NULL,
|
|
789
|
+
line INTEGER,
|
|
790
|
+
symbol TEXT DEFAULT '',
|
|
791
|
+
fingerprint TEXT NOT NULL
|
|
792
|
+
);
|
|
793
|
+
CREATE INDEX IF NOT EXISTS idx_ledger_runs_surface ON findings_ledger_runs(surface);
|
|
794
|
+
CREATE INDEX IF NOT EXISTS idx_ledger_runs_timestamp ON findings_ledger_runs(timestamp);
|
|
795
|
+
CREATE INDEX IF NOT EXISTS idx_ledger_findings_run ON findings_ledger_findings(run_id);
|
|
796
|
+
CREATE INDEX IF NOT EXISTS idx_ledger_findings_fp ON findings_ledger_findings(fingerprint);
|
|
797
|
+
CREATE INDEX IF NOT EXISTS idx_ledger_findings_rule ON findings_ledger_findings(analyzer, rule);
|
|
798
|
+
|
|
799
|
+
-- Spec 10: Style intelligence tables
|
|
800
|
+
CREATE TABLE IF NOT EXISTS style_declarations (
|
|
801
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
802
|
+
property TEXT NOT NULL,
|
|
803
|
+
raw_value TEXT NOT NULL,
|
|
804
|
+
normalized_value TEXT,
|
|
805
|
+
mechanism TEXT NOT NULL,
|
|
806
|
+
file_path TEXT NOT NULL,
|
|
807
|
+
line INTEGER NOT NULL,
|
|
808
|
+
context TEXT,
|
|
809
|
+
variant_context TEXT,
|
|
810
|
+
token_ref TEXT,
|
|
811
|
+
content_hash TEXT,
|
|
812
|
+
created_at TEXT DEFAULT (datetime('now'))
|
|
813
|
+
);
|
|
814
|
+
CREATE INDEX IF NOT EXISTS idx_style_decls_property ON style_declarations(property);
|
|
815
|
+
CREATE INDEX IF NOT EXISTS idx_style_decls_mechanism ON style_declarations(mechanism);
|
|
816
|
+
CREATE INDEX IF NOT EXISTS idx_style_decls_file_path ON style_declarations(file_path);
|
|
817
|
+
CREATE INDEX IF NOT EXISTS idx_style_decls_token_ref ON style_declarations(token_ref);
|
|
818
|
+
CREATE INDEX IF NOT EXISTS idx_style_decls_content_hash ON style_declarations(content_hash);
|
|
819
|
+
|
|
820
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS style_declarations_fts USING fts5(
|
|
821
|
+
property, raw_value, normalized_value,
|
|
822
|
+
content='style_declarations', content_rowid='id',
|
|
823
|
+
tokenize='porter unicode61'
|
|
824
|
+
);
|
|
825
|
+
|
|
826
|
+
CREATE TABLE IF NOT EXISTS style_tokens (
|
|
827
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
828
|
+
name TEXT NOT NULL,
|
|
829
|
+
value TEXT NOT NULL,
|
|
830
|
+
file_path TEXT NOT NULL,
|
|
831
|
+
mechanism TEXT NOT NULL,
|
|
832
|
+
created_at TEXT DEFAULT (datetime('now'))
|
|
833
|
+
);
|
|
834
|
+
CREATE INDEX IF NOT EXISTS idx_style_tokens_name ON style_tokens(name);
|
|
835
|
+
|
|
836
|
+
CREATE TABLE IF NOT EXISTS style_class_usage (
|
|
837
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
838
|
+
class_name TEXT NOT NULL,
|
|
839
|
+
file_path TEXT NOT NULL,
|
|
840
|
+
line INTEGER NOT NULL,
|
|
841
|
+
mechanism TEXT NOT NULL,
|
|
842
|
+
unresolvable INTEGER DEFAULT 0,
|
|
843
|
+
created_at TEXT DEFAULT (datetime('now'))
|
|
844
|
+
);
|
|
845
|
+
CREATE INDEX IF NOT EXISTS idx_style_class_usage_name ON style_class_usage(class_name);
|
|
846
|
+
CREATE INDEX IF NOT EXISTS idx_style_class_usage_file ON style_class_usage(file_path);
|
|
847
|
+
CREATE INDEX IF NOT EXISTS idx_style_class_usage_unres ON style_class_usage(unresolvable);
|
|
848
|
+
|
|
849
|
+
-- Spec 12: Convention mining
|
|
850
|
+
CREATE TABLE IF NOT EXISTS conventions (
|
|
851
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
852
|
+
domain TEXT NOT NULL,
|
|
853
|
+
rule_id TEXT NOT NULL,
|
|
854
|
+
antecedent TEXT,
|
|
855
|
+
consequent TEXT,
|
|
856
|
+
pattern TEXT,
|
|
857
|
+
directory TEXT,
|
|
858
|
+
file_path TEXT,
|
|
859
|
+
line INTEGER,
|
|
860
|
+
support INTEGER DEFAULT 0,
|
|
861
|
+
total_cases INTEGER DEFAULT 0,
|
|
862
|
+
confidence REAL DEFAULT 0,
|
|
863
|
+
exemplar_file TEXT,
|
|
864
|
+
exemplar_line INTEGER,
|
|
865
|
+
hash TEXT,
|
|
866
|
+
created_at TEXT DEFAULT (datetime('now'))
|
|
867
|
+
);
|
|
868
|
+
CREATE INDEX IF NOT EXISTS idx_conv_domain ON conventions(domain);
|
|
869
|
+
CREATE INDEX IF NOT EXISTS idx_conv_rule_id ON conventions(rule_id);
|
|
870
|
+
CREATE INDEX IF NOT EXISTS idx_conv_directory ON conventions(directory);
|
|
871
|
+
CREATE INDEX IF NOT EXISTS idx_conv_hash ON conventions(hash);
|
|
872
|
+
|
|
873
|
+
-- Spec 13: Hotspots & temporal analysis
|
|
874
|
+
CREATE TABLE IF NOT EXISTS file_churn (
|
|
875
|
+
file_path TEXT PRIMARY KEY,
|
|
876
|
+
commit_count INTEGER NOT NULL DEFAULT 0,
|
|
877
|
+
lines_added INTEGER NOT NULL DEFAULT 0,
|
|
878
|
+
lines_deleted INTEGER NOT NULL DEFAULT 0,
|
|
879
|
+
distinct_authors INTEGER NOT NULL DEFAULT 0,
|
|
880
|
+
dominant_author TEXT,
|
|
881
|
+
dominant_author_share REAL DEFAULT 0,
|
|
882
|
+
last_touched TEXT
|
|
883
|
+
);
|
|
884
|
+
CREATE INDEX IF NOT EXISTS idx_file_churn_cc ON file_churn(commit_count);
|
|
885
|
+
|
|
886
|
+
CREATE TABLE IF NOT EXISTS function_churn (
|
|
887
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
888
|
+
function_id INTEGER REFERENCES functions(id) ON DELETE CASCADE,
|
|
889
|
+
function_name TEXT NOT NULL,
|
|
890
|
+
file_path TEXT NOT NULL,
|
|
891
|
+
commit_count INTEGER NOT NULL DEFAULT 0,
|
|
892
|
+
distinct_authors INTEGER NOT NULL DEFAULT 0,
|
|
893
|
+
dominant_author TEXT,
|
|
894
|
+
dominant_author_share REAL DEFAULT 0,
|
|
895
|
+
renamed INTEGER DEFAULT 0,
|
|
896
|
+
confidence REAL DEFAULT 1.0
|
|
897
|
+
);
|
|
898
|
+
CREATE INDEX IF NOT EXISTS idx_func_churn_fid ON function_churn(function_id);
|
|
899
|
+
|
|
900
|
+
CREATE TABLE IF NOT EXISTS hotspot_scores (
|
|
901
|
+
target TEXT PRIMARY KEY,
|
|
902
|
+
type TEXT NOT NULL,
|
|
903
|
+
score REAL NOT NULL DEFAULT 0,
|
|
904
|
+
churn_pct REAL NOT NULL DEFAULT 0,
|
|
905
|
+
complexity_pct REAL NOT NULL DEFAULT 0,
|
|
906
|
+
commit_count INTEGER NOT NULL DEFAULT 0,
|
|
907
|
+
distinct_authors INTEGER NOT NULL DEFAULT 0,
|
|
908
|
+
dominant_author TEXT,
|
|
909
|
+
dominant_author_share REAL DEFAULT 0,
|
|
910
|
+
bus_factor_risk INTEGER DEFAULT 0,
|
|
911
|
+
complexity INTEGER NOT NULL DEFAULT 0,
|
|
912
|
+
updated_at TEXT DEFAULT (datetime('now'))
|
|
913
|
+
);
|
|
914
|
+
CREATE INDEX IF NOT EXISTS idx_hotspot_scores_score ON hotspot_scores(score DESC);
|
|
915
|
+
|
|
916
|
+
CREATE TABLE IF NOT EXISTS dry_pair_history (
|
|
917
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
918
|
+
pair_fingerprint TEXT NOT NULL,
|
|
919
|
+
file1 TEXT NOT NULL,
|
|
920
|
+
symbol1 TEXT,
|
|
921
|
+
line1 INTEGER NOT NULL,
|
|
922
|
+
content_hash1 TEXT NOT NULL,
|
|
923
|
+
file2 TEXT NOT NULL,
|
|
924
|
+
symbol2 TEXT,
|
|
925
|
+
line2 INTEGER NOT NULL,
|
|
926
|
+
content_hash2 TEXT NOT NULL,
|
|
927
|
+
similarity REAL NOT NULL,
|
|
928
|
+
timestamp TEXT DEFAULT (datetime('now')),
|
|
929
|
+
run_id TEXT NOT NULL
|
|
930
|
+
);
|
|
931
|
+
CREATE INDEX IF NOT EXISTS idx_dph_fingerprint ON dry_pair_history(pair_fingerprint);
|
|
932
|
+
CREATE INDEX IF NOT EXISTS idx_dph_run ON dry_pair_history(run_id);
|
|
537
933
|
`);
|
|
538
934
|
// Run schema migrations
|
|
539
935
|
this.runMigrations();
|
|
@@ -1222,6 +1618,11 @@ export class CodeIndexDB {
|
|
|
1222
1618
|
lastUpdated: new Date()
|
|
1223
1619
|
};
|
|
1224
1620
|
}
|
|
1621
|
+
/** Get graph statistics — delegates to graph/callGraph.ts (Spec 14 R1). */
|
|
1622
|
+
getGraphStats() {
|
|
1623
|
+
this.ensureInitialized();
|
|
1624
|
+
return getGs(this.db);
|
|
1625
|
+
}
|
|
1225
1626
|
// ── Lifecycle: clear & close ────────────────────────────────────────
|
|
1226
1627
|
async clearIndex() {
|
|
1227
1628
|
this.ensureInitialized();
|
|
@@ -1232,7 +1633,13 @@ export class CodeIndexDB {
|
|
|
1232
1633
|
this.db.prepare('DELETE FROM code_maps').run();
|
|
1233
1634
|
this.db.prepare('DELETE FROM schema_definitions').run();
|
|
1234
1635
|
this.db.prepare('DELETE FROM schema_usage').run();
|
|
1235
|
-
|
|
1636
|
+
this.db.prepare('DELETE FROM conventions').run();
|
|
1637
|
+
this.db.prepare('DELETE FROM file_churn').run();
|
|
1638
|
+
this.db.prepare('DELETE FROM function_churn').run();
|
|
1639
|
+
this.db.prepare('DELETE FROM hotspot_scores').run();
|
|
1640
|
+
this.db.prepare('DELETE FROM dry_pair_history').run();
|
|
1641
|
+
this.db.prepare('DELETE FROM graph_cache').run();
|
|
1642
|
+
// Preserve: project_tasks, analyzer_configs, whitelist, findings_ledger_runs, findings_ledger_findings
|
|
1236
1643
|
})();
|
|
1237
1644
|
}
|
|
1238
1645
|
async close() {
|
|
@@ -1342,6 +1749,72 @@ export class CodeIndexDB {
|
|
|
1342
1749
|
}
|
|
1343
1750
|
}
|
|
1344
1751
|
}
|
|
1752
|
+
// Spec 13 — Extract git churn data after index is built and stale entries
|
|
1753
|
+
// are cleaned up. Runs before conventions mining so hotspot data is
|
|
1754
|
+
// available for reordering. Degrades gracefully when no repo exists.
|
|
1755
|
+
if (projectRoot) {
|
|
1756
|
+
try {
|
|
1757
|
+
extractChurn(this.db, projectRoot, { churnWindowMonths: 12 });
|
|
1758
|
+
// Compute hotspot scores from churn data
|
|
1759
|
+
computeHotspots(this.db);
|
|
1760
|
+
}
|
|
1761
|
+
catch {
|
|
1762
|
+
// Churn extraction failure is non-fatal — continue without hotspot data
|
|
1763
|
+
}
|
|
1764
|
+
}
|
|
1765
|
+
// Spec 12 — Mine codebase conventions after sync when the functions table
|
|
1766
|
+
// has changed. Content-hash-based skip: stored hash avoids re-mining.
|
|
1767
|
+
const miningConfig = {
|
|
1768
|
+
minCorpus: 20,
|
|
1769
|
+
pairConfidence: 0.9,
|
|
1770
|
+
modeShare: 0.8,
|
|
1771
|
+
maxConventionsPerDomain: 200,
|
|
1772
|
+
};
|
|
1773
|
+
const newHash = computeMinerInputHash(this.db, miningConfig);
|
|
1774
|
+
const oldHashRow = this.db.prepare("SELECT value FROM meta WHERE key = 'conventions_hash'").get();
|
|
1775
|
+
if (!oldHashRow || oldHashRow.value !== newHash) {
|
|
1776
|
+
const conventions = mineConventions(this.db, miningConfig, projectRoot);
|
|
1777
|
+
// Upsert: clear existing conventions, insert new ones
|
|
1778
|
+
const deleteStmt = this.db.prepare('DELETE FROM conventions');
|
|
1779
|
+
const insertStmt = this.db.prepare(`INSERT INTO conventions
|
|
1780
|
+
(domain, rule_id, antecedent, consequent, pattern, directory,
|
|
1781
|
+
file_path, line, support, total_cases, confidence,
|
|
1782
|
+
exemplar_file, exemplar_line, hash)
|
|
1783
|
+
VALUES (@domain, @rule_id, @antecedent, @consequent, @pattern,
|
|
1784
|
+
@directory, @file_path, @line, @support, @total_cases,
|
|
1785
|
+
@confidence, @exemplar_file, @exemplar_line, @hash)`);
|
|
1786
|
+
const upsertAll = this.db.transaction(() => {
|
|
1787
|
+
deleteStmt.run();
|
|
1788
|
+
for (const c of conventions) {
|
|
1789
|
+
insertStmt.run({
|
|
1790
|
+
domain: c.domain,
|
|
1791
|
+
rule_id: c.rule_id,
|
|
1792
|
+
antecedent: c.antecedent ?? null,
|
|
1793
|
+
consequent: c.consequent ?? null,
|
|
1794
|
+
pattern: c.pattern ?? null,
|
|
1795
|
+
directory: c.directory ?? null,
|
|
1796
|
+
file_path: c.file_path ?? null,
|
|
1797
|
+
line: c.line ?? null,
|
|
1798
|
+
support: c.support,
|
|
1799
|
+
total_cases: c.total_cases,
|
|
1800
|
+
confidence: c.confidence,
|
|
1801
|
+
exemplar_file: c.exemplar_file ?? null,
|
|
1802
|
+
exemplar_line: c.exemplar_line ?? null,
|
|
1803
|
+
hash: c.hash ?? null,
|
|
1804
|
+
});
|
|
1805
|
+
}
|
|
1806
|
+
});
|
|
1807
|
+
upsertAll();
|
|
1808
|
+
this.db.prepare("INSERT OR REPLACE INTO meta (key, value) VALUES ('conventions_hash', ?)").run(newHash);
|
|
1809
|
+
}
|
|
1810
|
+
// Spec 14 — Populate graph caches after index is built
|
|
1811
|
+
try {
|
|
1812
|
+
populateCallGraphCache(this.db);
|
|
1813
|
+
populateImportGraphCache(this.db);
|
|
1814
|
+
}
|
|
1815
|
+
catch {
|
|
1816
|
+
// Graph cache population failure is non-fatal — continue without graph data
|
|
1817
|
+
}
|
|
1345
1818
|
return {
|
|
1346
1819
|
syncedFiles,
|
|
1347
1820
|
addedFunctions: totalAdded,
|
|
@@ -1957,6 +2430,15 @@ export class CodeIndexDB {
|
|
|
1957
2430
|
this.db.prepare('INSERT INTO schema_usage (schema_id, table_name, file_path, function_name, usage_type, line, "column", raw_query, parameters, recorded_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)').run(schemaId ?? 'default', usage.tableName, usage.filePath, usage.functionName, usage.usageType, usage.line ?? null, usage.column ?? null, usage.rawQuery ?? null, JSON.stringify(usage.parameters ?? []), new Date().toISOString());
|
|
1958
2431
|
}
|
|
1959
2432
|
}
|
|
2433
|
+
/**
|
|
2434
|
+
* Clear all schema_usage entries for a given file path.
|
|
2435
|
+
* Used for idempotent per-file writes during indexing — stale entries
|
|
2436
|
+
* from a previous scan are removed before fresh references are inserted.
|
|
2437
|
+
*/
|
|
2438
|
+
clearSchemaUsageForFile(filePath) {
|
|
2439
|
+
this.ensureInitialized();
|
|
2440
|
+
this.db.prepare('DELETE FROM schema_usage WHERE file_path = ?').run(filePath);
|
|
2441
|
+
}
|
|
1960
2442
|
async getSchemaUsage(options = {}) {
|
|
1961
2443
|
this.ensureInitialized();
|
|
1962
2444
|
const clauses = [];
|
|
@@ -2075,6 +2557,180 @@ export class CodeIndexDB {
|
|
|
2075
2557
|
async deleteProjectTask(taskId, mode) {
|
|
2076
2558
|
return this.getTaskRepository().delete(taskId, mode);
|
|
2077
2559
|
}
|
|
2560
|
+
// ── Meta key-value store ─────────────────────────────────────────────
|
|
2561
|
+
/** Upsert a key-value pair in the meta table. */
|
|
2562
|
+
setMeta(key, value) {
|
|
2563
|
+
this.ensureInitialized();
|
|
2564
|
+
this.db.prepare(`INSERT INTO meta (key, value) VALUES (?, ?)
|
|
2565
|
+
ON CONFLICT(key) DO UPDATE SET value = excluded.value`).run(key, value);
|
|
2566
|
+
}
|
|
2567
|
+
/** Retrieve a value from the meta table, or null if absent. */
|
|
2568
|
+
getMeta(key) {
|
|
2569
|
+
this.ensureInitialized();
|
|
2570
|
+
const row = this.db.prepare('SELECT value FROM meta WHERE key = ?').get(key);
|
|
2571
|
+
return row?.value ?? null;
|
|
2572
|
+
}
|
|
2573
|
+
// ── Provenance storage (Spec-21 R2 cross-file) ─────────────────────
|
|
2574
|
+
/** Store per-file provenance context in the meta table. */
|
|
2575
|
+
storeFileProvenance(filePath, provenanceData) {
|
|
2576
|
+
const key = `provenance:${filePath}`;
|
|
2577
|
+
this.setMeta(key, JSON.stringify(provenanceData));
|
|
2578
|
+
}
|
|
2579
|
+
/** Retrieve per-file provenance context, or null if not stored. */
|
|
2580
|
+
getFileProvenance(filePath) {
|
|
2581
|
+
const raw = this.getMeta(`provenance:${filePath}`);
|
|
2582
|
+
if (!raw)
|
|
2583
|
+
return null;
|
|
2584
|
+
try {
|
|
2585
|
+
return JSON.parse(raw);
|
|
2586
|
+
}
|
|
2587
|
+
catch {
|
|
2588
|
+
return null;
|
|
2589
|
+
}
|
|
2590
|
+
}
|
|
2591
|
+
/** Store the inferred receiver set as a meta record. */
|
|
2592
|
+
storeInferredReceivers(inferred) {
|
|
2593
|
+
this.setMeta('inferred_receivers', JSON.stringify(inferred));
|
|
2594
|
+
}
|
|
2595
|
+
/** Retrieve the inferred receiver set, or null if not stored. */
|
|
2596
|
+
getInferredReceivers() {
|
|
2597
|
+
const raw = this.getMeta('inferred_receivers');
|
|
2598
|
+
if (!raw)
|
|
2599
|
+
return null;
|
|
2600
|
+
try {
|
|
2601
|
+
return JSON.parse(raw);
|
|
2602
|
+
}
|
|
2603
|
+
catch {
|
|
2604
|
+
return null;
|
|
2605
|
+
}
|
|
2606
|
+
}
|
|
2607
|
+
// ── Coverage data (Spec 15 R4) ──────────────────────────────────────
|
|
2608
|
+
/** Import coverage entries, replacing any existing data for the given basis. */
|
|
2609
|
+
importCoverageData(entries) {
|
|
2610
|
+
this.ensureInitialized();
|
|
2611
|
+
// Group by basis so we only clear one basis at a time
|
|
2612
|
+
const bases = new Set(entries.map(e => e.basis));
|
|
2613
|
+
const clearStmt = this.db.prepare('DELETE FROM coverage_data WHERE basis = ?');
|
|
2614
|
+
for (const basis of bases) {
|
|
2615
|
+
clearStmt.run(basis);
|
|
2616
|
+
}
|
|
2617
|
+
const insert = this.db.prepare(`INSERT OR REPLACE INTO coverage_data (function_name, file_path, line_number, basis, covered, source, imported_at)
|
|
2618
|
+
VALUES (?, ?, ?, ?, ?, ?, datetime('now'))`);
|
|
2619
|
+
const tx = this.db.transaction((items) => {
|
|
2620
|
+
for (const e of items) {
|
|
2621
|
+
insert.run(e.functionName, e.filePath, e.lineNumber, e.basis, e.covered ? 1 : 0, e.source ?? null);
|
|
2622
|
+
}
|
|
2623
|
+
});
|
|
2624
|
+
tx(entries);
|
|
2625
|
+
}
|
|
2626
|
+
/** Get all coverage entries for a given basis. */
|
|
2627
|
+
getCoverageByBasis(basis) {
|
|
2628
|
+
this.ensureInitialized();
|
|
2629
|
+
const rows = this.db.prepare('SELECT * FROM coverage_data WHERE basis = ?').all(basis);
|
|
2630
|
+
return rows.map((r) => ({
|
|
2631
|
+
functionName: r.function_name,
|
|
2632
|
+
filePath: r.file_path,
|
|
2633
|
+
lineNumber: r.line_number,
|
|
2634
|
+
basis: r.basis,
|
|
2635
|
+
covered: r.covered === 1,
|
|
2636
|
+
source: r.source,
|
|
2637
|
+
importedAt: r.imported_at,
|
|
2638
|
+
}));
|
|
2639
|
+
}
|
|
2640
|
+
/** Clear coverage data, optionally scoped to a single basis. */
|
|
2641
|
+
clearCoverageData(basis) {
|
|
2642
|
+
this.ensureInitialized();
|
|
2643
|
+
if (basis) {
|
|
2644
|
+
this.db.prepare('DELETE FROM coverage_data WHERE basis = ?').run(basis);
|
|
2645
|
+
}
|
|
2646
|
+
else {
|
|
2647
|
+
this.db.prepare('DELETE FROM coverage_data').run();
|
|
2648
|
+
}
|
|
2649
|
+
}
|
|
2650
|
+
/** Check if measured coverage data is stale (older than the last full sync). */
|
|
2651
|
+
isCoverageStale() {
|
|
2652
|
+
this.ensureInitialized();
|
|
2653
|
+
const lastSync = this.getMeta('last_full_sync_timestamp');
|
|
2654
|
+
if (!lastSync)
|
|
2655
|
+
return false;
|
|
2656
|
+
const staleRow = this.db.prepare(`SELECT COUNT(*) as cnt FROM coverage_data
|
|
2657
|
+
WHERE basis = 'measured' AND imported_at < ?`).get(lastSync);
|
|
2658
|
+
return (staleRow?.cnt ?? 0) > 0;
|
|
2659
|
+
}
|
|
2660
|
+
/** Get coverage rate by risk decile for the coverage report. */
|
|
2661
|
+
getCoverageByRiskDecile(decileCount = 10) {
|
|
2662
|
+
this.ensureInitialized();
|
|
2663
|
+
// Rank functions by risk score from hotspot_scores, join with coverage_data
|
|
2664
|
+
const rows = this.db.prepare(`
|
|
2665
|
+
WITH ranked AS (
|
|
2666
|
+
SELECT
|
|
2667
|
+
f.name,
|
|
2668
|
+
f.file_path,
|
|
2669
|
+
f.line_number,
|
|
2670
|
+
COALESCE(hs.score, 0.0) as risk_score,
|
|
2671
|
+
NTILE(?) OVER (ORDER BY COALESCE(hs.score, 0.0) DESC) as decile
|
|
2672
|
+
FROM functions f
|
|
2673
|
+
LEFT JOIN hotspot_scores hs ON hs.target = (f.file_path || ':' || f.name)
|
|
2674
|
+
AND hs.type = 'function'
|
|
2675
|
+
WHERE f.is_exported = 1
|
|
2676
|
+
),
|
|
2677
|
+
coverage AS (
|
|
2678
|
+
SELECT DISTINCT function_name, file_path FROM coverage_data
|
|
2679
|
+
WHERE covered = 1
|
|
2680
|
+
)
|
|
2681
|
+
SELECT
|
|
2682
|
+
r.decile,
|
|
2683
|
+
COUNT(*) as total,
|
|
2684
|
+
SUM(CASE WHEN c.function_name IS NOT NULL THEN 1 ELSE 0 END) as covered
|
|
2685
|
+
FROM ranked r
|
|
2686
|
+
LEFT JOIN coverage c ON c.function_name = r.name AND c.file_path = r.file_path
|
|
2687
|
+
GROUP BY r.decile
|
|
2688
|
+
ORDER BY r.decile
|
|
2689
|
+
`).all(decileCount);
|
|
2690
|
+
return rows.map((r) => ({
|
|
2691
|
+
decile: r.decile,
|
|
2692
|
+
covered: r.covered,
|
|
2693
|
+
total: r.total,
|
|
2694
|
+
rate: r.total > 0 ? r.covered / r.total : 0,
|
|
2695
|
+
}));
|
|
2696
|
+
}
|
|
2697
|
+
/** Get untested functions in the top risk decile. */
|
|
2698
|
+
getUntestedTopDecile(topDecile = 0.1) {
|
|
2699
|
+
this.ensureInitialized();
|
|
2700
|
+
const rows = this.db.prepare(`
|
|
2701
|
+
WITH ranked AS (
|
|
2702
|
+
SELECT
|
|
2703
|
+
f.name,
|
|
2704
|
+
f.file_path,
|
|
2705
|
+
COALESCE(hs.score, 0.0) as risk_score,
|
|
2706
|
+
PERCENT_RANK() OVER (ORDER BY COALESCE(hs.score, 0.0) DESC) as pct
|
|
2707
|
+
FROM functions f
|
|
2708
|
+
LEFT JOIN hotspot_scores hs ON hs.target = (f.file_path || ':' || f.name)
|
|
2709
|
+
AND hs.type = 'function'
|
|
2710
|
+
WHERE f.is_exported = 1
|
|
2711
|
+
),
|
|
2712
|
+
best_coverage AS (
|
|
2713
|
+
SELECT DISTINCT function_name, file_path, basis FROM coverage_data
|
|
2714
|
+
WHERE covered = 1
|
|
2715
|
+
)
|
|
2716
|
+
SELECT
|
|
2717
|
+
r.name,
|
|
2718
|
+
r.file_path,
|
|
2719
|
+
r.risk_score,
|
|
2720
|
+
COALESCE(bc.basis, 'static-reach') as basis
|
|
2721
|
+
FROM ranked r
|
|
2722
|
+
LEFT JOIN best_coverage bc ON bc.function_name = r.name AND bc.file_path = r.file_path
|
|
2723
|
+
WHERE r.pct <= ?
|
|
2724
|
+
AND bc.basis IS NULL
|
|
2725
|
+
ORDER BY r.risk_score DESC
|
|
2726
|
+
`).all(topDecile);
|
|
2727
|
+
return rows.map((r) => ({
|
|
2728
|
+
functionName: r.name,
|
|
2729
|
+
filePath: r.file_path,
|
|
2730
|
+
riskScore: r.risk_score,
|
|
2731
|
+
basis: r.basis,
|
|
2732
|
+
}));
|
|
2733
|
+
}
|
|
2078
2734
|
}
|
|
2079
2735
|
// ── Helpers ────────────────────────────────────────────────────────────
|
|
2080
2736
|
function tryParseJson(val) {
|