code-auditor-mcp 4.1.1 → 5.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +338 -0
- package/README.md +4 -4
- package/configs/hhra-compat.json +0 -8
- package/dist/__tests__/fixtures/spec-52/item-03-exec-in-loop.d.ts +1 -1
- package/dist/__tests__/fixtures/spec-52/item-03-exec-in-loop.d.ts.map +1 -1
- package/dist/__tests__/fixtures/spec-52/item-03-exec-in-loop.js +7 -1
- package/dist/__tests__/fixtures/spec-52/item-03-exec-in-loop.js.map +1 -1
- package/dist/__tests__/fixtures/spec-52/item-04-storage-sql-exec-in-loop.d.ts +2 -8
- package/dist/__tests__/fixtures/spec-52/item-04-storage-sql-exec-in-loop.d.ts.map +1 -1
- package/dist/__tests__/fixtures/spec-52/item-04-storage-sql-exec-in-loop.js +4 -4
- package/dist/__tests__/fixtures/spec-52/item-04-storage-sql-exec-in-loop.js.map +1 -1
- package/dist/__tests__/fixtures/spec-52/item-06-chained-prepare-run-in-loop.d.ts +1 -1
- package/dist/__tests__/fixtures/spec-52/item-06-chained-prepare-run-in-loop.d.ts.map +1 -1
- package/dist/__tests__/fixtures/spec-52/item-06-chained-prepare-run-in-loop.js.map +1 -1
- package/dist/__tests__/fixtures/spec-52/item-07-chained-prepare-bind-first-in-loop.d.ts +2 -2
- package/dist/__tests__/fixtures/spec-52/item-07-chained-prepare-bind-first-in-loop.d.ts.map +1 -1
- package/dist/__tests__/fixtures/spec-52/item-07-chained-prepare-bind-first-in-loop.js.map +1 -1
- package/dist/__tests__/fixtures/spec-52/item-10-promise-all-map-of-eager-all-in-loop.d.ts +1 -1
- package/dist/__tests__/fixtures/spec-52/item-10-promise-all-map-of-eager-all-in-loop.d.ts.map +1 -1
- package/dist/__tests__/fixtures/spec-52/item-10-promise-all-map-of-eager-all-in-loop.js.map +1 -1
- package/dist/analyzers/applicability.d.ts +42 -0
- package/dist/analyzers/applicability.d.ts.map +1 -1
- package/dist/analyzers/applicability.js +74 -0
- package/dist/analyzers/applicability.js.map +1 -1
- package/dist/analyzers/cross-language/APIContractAnalyzer.js +18 -24
- package/dist/analyzers/cross-language/APIContractAnalyzer.js.map +1 -1
- package/dist/analyzers/cross-language/DependencyGraphBuilder.d.ts +30 -23
- package/dist/analyzers/cross-language/DependencyGraphBuilder.d.ts.map +1 -1
- package/dist/analyzers/cross-language/DependencyGraphBuilder.js +113 -76
- package/dist/analyzers/cross-language/DependencyGraphBuilder.js.map +1 -1
- package/dist/analyzers/cross-language/SchemaValidator.d.ts +2 -0
- package/dist/analyzers/cross-language/SchemaValidator.d.ts.map +1 -1
- package/dist/analyzers/cross-language/SchemaValidator.js +8 -3
- package/dist/analyzers/cross-language/SchemaValidator.js.map +1 -1
- package/dist/analyzers/crossDomain/CrossDomainAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/crossDomain/CrossDomainAnalyzer.js +210 -148
- package/dist/analyzers/crossDomain/CrossDomainAnalyzer.js.map +1 -1
- package/dist/analyzers/documentationAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/documentationAnalyzer.js +23 -28
- package/dist/analyzers/documentationAnalyzer.js.map +1 -1
- package/dist/analyzers/handleIdentification.d.ts +356 -0
- package/dist/analyzers/handleIdentification.d.ts.map +1 -0
- package/dist/analyzers/handleIdentification.js +424 -0
- package/dist/analyzers/handleIdentification.js.map +1 -0
- package/dist/analyzers/manifestStaleness.d.ts +64 -0
- package/dist/analyzers/manifestStaleness.d.ts.map +1 -0
- package/dist/analyzers/manifestStaleness.js +115 -0
- package/dist/analyzers/manifestStaleness.js.map +1 -0
- package/dist/analyzers/orgFilterTiers.d.ts +73 -1
- package/dist/analyzers/orgFilterTiers.d.ts.map +1 -1
- package/dist/analyzers/orgFilterTiers.js +166 -1
- package/dist/analyzers/orgFilterTiers.js.map +1 -1
- package/dist/analyzers/provenance.d.ts +251 -57
- package/dist/analyzers/provenance.d.ts.map +1 -1
- package/dist/analyzers/provenance.js +1109 -429
- package/dist/analyzers/provenance.js.map +1 -1
- package/dist/analyzers/reactAnalyzer.d.ts +2 -0
- package/dist/analyzers/reactAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/reactAnalyzer.js +2 -0
- package/dist/analyzers/reactAnalyzer.js.map +1 -1
- package/dist/analyzers/receiverResolution.d.ts +183 -0
- package/dist/analyzers/receiverResolution.d.ts.map +1 -0
- package/dist/analyzers/receiverResolution.js +540 -0
- package/dist/analyzers/receiverResolution.js.map +1 -0
- package/dist/analyzers/receiverRoot.d.ts +328 -0
- package/dist/analyzers/receiverRoot.d.ts.map +1 -0
- package/dist/analyzers/receiverRoot.js +990 -0
- package/dist/analyzers/receiverRoot.js.map +1 -0
- package/dist/analyzers/ruleRegistry.d.ts +12 -68
- package/dist/analyzers/ruleRegistry.d.ts.map +1 -1
- package/dist/analyzers/ruleRegistry.js +32 -342
- package/dist/analyzers/ruleRegistry.js.map +1 -1
- package/dist/analyzers/ruleTiming.d.ts +10 -0
- package/dist/analyzers/ruleTiming.d.ts.map +1 -1
- package/dist/analyzers/ruleTiming.js +20 -0
- package/dist/analyzers/ruleTiming.js.map +1 -1
- package/dist/analyzers/sqlLiteral.d.ts +76 -0
- package/dist/analyzers/sqlLiteral.d.ts.map +1 -0
- package/dist/analyzers/sqlLiteral.js +419 -0
- package/dist/analyzers/sqlLiteral.js.map +1 -0
- package/dist/analyzers/tsEcosystem.d.ts +115 -0
- package/dist/analyzers/tsEcosystem.d.ts.map +1 -0
- package/dist/analyzers/tsEcosystem.js +231 -0
- package/dist/analyzers/tsEcosystem.js.map +1 -0
- package/dist/analyzers/tsExpressionDescriptor.d.ts +156 -0
- package/dist/analyzers/tsExpressionDescriptor.d.ts.map +1 -0
- package/dist/analyzers/tsExpressionDescriptor.js +374 -0
- package/dist/analyzers/tsExpressionDescriptor.js.map +1 -0
- package/dist/analyzers/universal/UniversalConventionsAnalyzer.d.ts +6 -0
- package/dist/analyzers/universal/UniversalConventionsAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalConventionsAnalyzer.js +107 -44
- package/dist/analyzers/universal/UniversalConventionsAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/UniversalDRYAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalDRYAnalyzer.js +23 -35
- package/dist/analyzers/universal/UniversalDRYAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/UniversalDataAccessAnalyzer.d.ts +287 -49
- package/dist/analyzers/universal/UniversalDataAccessAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalDataAccessAnalyzer.js +2130 -512
- 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 +2 -18
- package/dist/analyzers/universal/UniversalDocumentationAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/UniversalSOLIDAnalyzer.d.ts +0 -33
- package/dist/analyzers/universal/UniversalSOLIDAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalSOLIDAnalyzer.js +20 -143
- package/dist/analyzers/universal/UniversalSOLIDAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/UniversalSchemaAnalyzer.d.ts +4 -3
- package/dist/analyzers/universal/UniversalSchemaAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalSchemaAnalyzer.js +36 -13
- package/dist/analyzers/universal/UniversalSchemaAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/UniversalSecretsAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalSecretsAnalyzer.js +6 -0
- package/dist/analyzers/universal/UniversalSecretsAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/UniversalSecurityAnalyzer.d.ts +15 -0
- package/dist/analyzers/universal/UniversalSecurityAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalSecurityAnalyzer.js +25 -0
- package/dist/analyzers/universal/UniversalSecurityAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/UniversalStylesAnalyzer.d.ts +21 -32
- package/dist/analyzers/universal/UniversalStylesAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalStylesAnalyzer.js +166 -196
- package/dist/analyzers/universal/UniversalStylesAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/functionConcerns.d.ts +3 -0
- package/dist/analyzers/universal/functionConcerns.d.ts.map +1 -1
- package/dist/analyzers/universal/functionConcerns.js +4 -1
- package/dist/analyzers/universal/functionConcerns.js.map +1 -1
- package/dist/analyzers/universal/schema/codeAnalysis.d.ts +310 -69
- package/dist/analyzers/universal/schema/codeAnalysis.d.ts.map +1 -1
- package/dist/analyzers/universal/schema/codeAnalysis.js +709 -383
- package/dist/analyzers/universal/schema/codeAnalysis.js.map +1 -1
- package/dist/analyzers/universal/schema/config.d.ts +3 -14
- package/dist/analyzers/universal/schema/config.d.ts.map +1 -1
- package/dist/analyzers/universal/schema/config.js +3 -16
- package/dist/analyzers/universal/schema/config.js.map +1 -1
- package/dist/analyzers/universal/schema/discovery.d.ts +22 -1
- package/dist/analyzers/universal/schema/discovery.d.ts.map +1 -1
- package/dist/analyzers/universal/schema/discovery.js +36 -20
- package/dist/analyzers/universal/schema/discovery.js.map +1 -1
- package/dist/analyzers/universal/schema/jsonSchema.d.ts.map +1 -1
- package/dist/analyzers/universal/schema/jsonSchema.js +13 -3
- package/dist/analyzers/universal/schema/jsonSchema.js.map +1 -1
- package/dist/analyzers/universal/schema/migrations.d.ts +187 -27
- package/dist/analyzers/universal/schema/migrations.d.ts.map +1 -1
- package/dist/analyzers/universal/schema/migrations.js +443 -186
- package/dist/analyzers/universal/schema/migrations.js.map +1 -1
- package/dist/analyzers/universal/schema/types.d.ts +17 -3
- package/dist/analyzers/universal/schema/types.d.ts.map +1 -1
- package/dist/analyzers/universal/schema/violations.js +1 -1
- package/dist/analyzers/universal/schema/violations.js.map +1 -1
- package/dist/analyzers/universal/styleScale.d.ts +52 -0
- package/dist/analyzers/universal/styleScale.d.ts.map +1 -0
- package/dist/analyzers/universal/styleScale.js +95 -0
- package/dist/analyzers/universal/styleScale.js.map +1 -0
- package/dist/applyDataDirEnv.js +1 -1
- package/dist/applyDataDirEnv.js.map +1 -1
- package/dist/auditRouter.d.ts +26 -30
- package/dist/auditRouter.d.ts.map +1 -1
- package/dist/auditRouter.js +373 -143
- package/dist/auditRouter.js.map +1 -1
- package/dist/auditRunner.d.ts +64 -1
- package/dist/auditRunner.d.ts.map +1 -1
- package/dist/auditRunner.js +1538 -1033
- package/dist/auditRunner.js.map +1 -1
- package/dist/baseline.d.ts +30 -3
- package/dist/baseline.d.ts.map +1 -1
- package/dist/baseline.js +70 -9
- package/dist/baseline.js.map +1 -1
- package/dist/cli.js +969 -821
- package/dist/cli.js.map +1 -1
- package/dist/codeIndex/analyzerConfig.d.ts +60 -0
- package/dist/codeIndex/analyzerConfig.d.ts.map +1 -0
- package/dist/codeIndex/analyzerConfig.js +98 -0
- package/dist/codeIndex/analyzerConfig.js.map +1 -0
- package/dist/codeIndex/auditResults.d.ts +39 -0
- package/dist/codeIndex/auditResults.d.ts.map +1 -0
- package/dist/codeIndex/auditResults.js +99 -0
- package/dist/codeIndex/auditResults.js.map +1 -0
- package/dist/codeIndex/codeMap.d.ts +59 -0
- package/dist/codeIndex/codeMap.d.ts.map +1 -0
- package/dist/codeIndex/codeMap.js +72 -0
- package/dist/codeIndex/codeMap.js.map +1 -0
- package/dist/codeIndex/conventions.d.ts +33 -0
- package/dist/codeIndex/conventions.d.ts.map +1 -0
- package/dist/codeIndex/conventions.js +84 -0
- package/dist/codeIndex/conventions.js.map +1 -0
- package/dist/codeIndex/coverage.d.ts +78 -0
- package/dist/codeIndex/coverage.d.ts.map +1 -0
- package/dist/codeIndex/coverage.js +164 -0
- package/dist/codeIndex/coverage.js.map +1 -0
- package/dist/codeIndex/dependencyGraph.d.ts +97 -0
- package/dist/codeIndex/dependencyGraph.d.ts.map +1 -0
- package/dist/codeIndex/dependencyGraph.js +241 -0
- package/dist/codeIndex/dependencyGraph.js.map +1 -0
- package/dist/codeIndex/functionIndex.d.ts +111 -0
- package/dist/codeIndex/functionIndex.d.ts.map +1 -0
- package/dist/codeIndex/functionIndex.js +306 -0
- package/dist/codeIndex/functionIndex.js.map +1 -0
- package/dist/codeIndex/inferredReceivers.d.ts +32 -0
- package/dist/codeIndex/inferredReceivers.d.ts.map +1 -0
- package/dist/codeIndex/inferredReceivers.js +41 -0
- package/dist/codeIndex/inferredReceivers.js.map +1 -0
- package/dist/codeIndex/meta.d.ts +30 -0
- package/dist/codeIndex/meta.d.ts.map +1 -0
- package/dist/codeIndex/meta.js +37 -0
- package/dist/codeIndex/meta.js.map +1 -0
- package/dist/codeIndex/migrations.d.ts +96 -0
- package/dist/codeIndex/migrations.d.ts.map +1 -0
- package/dist/codeIndex/migrations.js +1302 -0
- package/dist/codeIndex/migrations.js.map +1 -0
- package/dist/codeIndex/projectTasks.d.ts +54 -0
- package/dist/codeIndex/projectTasks.d.ts.map +1 -0
- package/dist/codeIndex/projectTasks.js +81 -0
- package/dist/codeIndex/projectTasks.js.map +1 -0
- package/dist/codeIndex/provenance.d.ts +47 -0
- package/dist/codeIndex/provenance.d.ts.map +1 -0
- package/dist/codeIndex/provenance.js +48 -0
- package/dist/codeIndex/provenance.js.map +1 -0
- package/dist/codeIndex/rawSql.d.ts +54 -0
- package/dist/codeIndex/rawSql.d.ts.map +1 -0
- package/dist/codeIndex/rawSql.js +71 -0
- package/dist/codeIndex/rawSql.js.map +1 -0
- package/dist/codeIndex/schema.d.ts +99 -0
- package/dist/codeIndex/schema.d.ts.map +1 -0
- package/dist/codeIndex/schema.js +174 -0
- package/dist/codeIndex/schema.js.map +1 -0
- package/dist/codeIndex/search.d.ts +40 -0
- package/dist/codeIndex/search.d.ts.map +1 -0
- package/dist/codeIndex/search.js +316 -0
- package/dist/codeIndex/search.js.map +1 -0
- package/dist/codeIndex/shared.d.ts +27 -0
- package/dist/codeIndex/shared.d.ts.map +1 -0
- package/dist/codeIndex/shared.js +46 -0
- package/dist/codeIndex/shared.js.map +1 -0
- package/dist/codeIndex/sqliteCollection.d.ts +81 -0
- package/dist/codeIndex/sqliteCollection.d.ts.map +1 -0
- package/dist/codeIndex/sqliteCollection.js +250 -0
- package/dist/codeIndex/sqliteCollection.js.map +1 -0
- package/dist/codeIndex/whitelist.d.ts +59 -0
- package/dist/codeIndex/whitelist.d.ts.map +1 -0
- package/dist/codeIndex/whitelist.js +188 -0
- package/dist/codeIndex/whitelist.js.map +1 -0
- package/dist/codeIndexDB.d.ts +132 -271
- package/dist/codeIndexDB.d.ts.map +1 -1
- package/dist/codeIndexDB.js +365 -2729
- package/dist/codeIndexDB.js.map +1 -1
- package/dist/codeIndexService.d.ts +87 -12
- package/dist/codeIndexService.d.ts.map +1 -1
- package/dist/codeIndexService.js +110 -34
- package/dist/codeIndexService.js.map +1 -1
- package/dist/componentScanner.d.ts +35 -0
- package/dist/componentScanner.d.ts.map +1 -1
- package/dist/componentScanner.js +114 -80
- package/dist/componentScanner.js.map +1 -1
- package/dist/config/configGeneratePath.d.ts +4 -0
- package/dist/config/configGeneratePath.d.ts.map +1 -1
- package/dist/config/configGeneratePath.js +4 -0
- package/dist/config/configGeneratePath.js.map +1 -1
- package/dist/config/configLoader.d.ts +15 -0
- package/dist/config/configLoader.d.ts.map +1 -1
- package/dist/config/configLoader.js +15 -16
- package/dist/config/configLoader.js.map +1 -1
- package/dist/config/defaults.d.ts +11 -7
- package/dist/config/defaults.d.ts.map +1 -1
- package/dist/config/defaults.js +16 -14
- package/dist/config/defaults.js.map +1 -1
- package/dist/config/effectiveConfig.d.ts +17 -2
- package/dist/config/effectiveConfig.d.ts.map +1 -1
- package/dist/config/effectiveConfig.js +32 -25
- package/dist/config/effectiveConfig.js.map +1 -1
- package/dist/config/lintConfigReader.d.ts +6 -0
- package/dist/config/lintConfigReader.d.ts.map +1 -1
- package/dist/config/lintConfigReader.js +6 -0
- package/dist/config/lintConfigReader.js.map +1 -1
- package/dist/config/staticObjectExtract.d.ts +4 -0
- package/dist/config/staticObjectExtract.d.ts.map +1 -1
- package/dist/config/staticObjectExtract.js +7 -3
- package/dist/config/staticObjectExtract.js.map +1 -1
- package/dist/config/thresholdRationales.d.ts +2 -0
- package/dist/config/thresholdRationales.d.ts.map +1 -1
- package/dist/config/thresholdRationales.js +5 -2
- package/dist/config/thresholdRationales.js.map +1 -1
- package/dist/constants.d.ts +1 -1
- package/dist/conventions/conventionMiner.d.ts +105 -2
- package/dist/conventions/conventionMiner.d.ts.map +1 -1
- package/dist/conventions/conventionMiner.js +334 -259
- package/dist/conventions/conventionMiner.js.map +1 -1
- package/dist/coverage/coverageService.js +4 -4
- package/dist/coverage/coverageService.js.map +1 -1
- package/dist/daemon/args.d.ts +3 -0
- package/dist/daemon/args.d.ts.map +1 -1
- package/dist/daemon/args.js +3 -0
- package/dist/daemon/args.js.map +1 -1
- package/dist/daemon/core.d.ts +33 -27
- package/dist/daemon/core.d.ts.map +1 -1
- package/dist/daemon/core.js +198 -162
- package/dist/daemon/core.js.map +1 -1
- package/dist/daemon/lspServer.d.ts +8 -0
- package/dist/daemon/lspServer.d.ts.map +1 -1
- package/dist/daemon/lspServer.js +8 -0
- package/dist/daemon/lspServer.js.map +1 -1
- package/dist/daemon/main.d.ts +2 -0
- package/dist/daemon/main.d.ts.map +1 -1
- package/dist/daemon/main.js +2 -0
- package/dist/daemon/main.js.map +1 -1
- package/dist/daemon/resolve.d.ts +18 -3
- package/dist/daemon/resolve.d.ts.map +1 -1
- package/dist/daemon/resolve.js +25 -4
- package/dist/daemon/resolve.js.map +1 -1
- package/dist/daemon/socketClient.d.ts +10 -1
- package/dist/daemon/socketClient.d.ts.map +1 -1
- package/dist/daemon/socketClient.js +10 -1
- package/dist/daemon/socketClient.js.map +1 -1
- package/dist/daemon/socketServer.d.ts +13 -0
- package/dist/daemon/socketServer.d.ts.map +1 -1
- package/dist/daemon/socketServer.js +13 -0
- package/dist/daemon/socketServer.js.map +1 -1
- package/dist/dataPaths.d.ts +17 -8
- package/dist/dataPaths.d.ts.map +1 -1
- package/dist/dataPaths.js +16 -44
- package/dist/dataPaths.js.map +1 -1
- package/dist/dismissals.d.ts +22 -1
- package/dist/dismissals.d.ts.map +1 -1
- package/dist/dismissals.js +22 -1
- package/dist/dismissals.js.map +1 -1
- package/dist/enforcement/gate.d.ts +1 -0
- package/dist/enforcement/gate.d.ts.map +1 -1
- package/dist/enforcement/gate.js +1 -0
- package/dist/enforcement/gate.js.map +1 -1
- package/dist/fingerprint.d.ts +6 -0
- package/dist/fingerprint.d.ts.map +1 -1
- package/dist/fingerprint.js +46 -1
- package/dist/fingerprint.js.map +1 -1
- package/dist/functionScanner.d.ts +30 -1
- package/dist/functionScanner.d.ts.map +1 -1
- package/dist/functionScanner.js +258 -256
- package/dist/functionScanner.js.map +1 -1
- package/dist/generators/AWSQConfigGenerator.d.ts +23 -0
- package/dist/generators/AWSQConfigGenerator.d.ts.map +1 -1
- package/dist/generators/AWSQConfigGenerator.js +23 -0
- package/dist/generators/AWSQConfigGenerator.js.map +1 -1
- package/dist/generators/BaseConfigGenerator.d.ts +7 -0
- package/dist/generators/BaseConfigGenerator.d.ts.map +1 -1
- package/dist/generators/BaseConfigGenerator.js +7 -0
- package/dist/generators/BaseConfigGenerator.js.map +1 -1
- package/dist/generators/ClaudeConfigGenerator.d.ts +23 -0
- package/dist/generators/ClaudeConfigGenerator.d.ts.map +1 -1
- package/dist/generators/ClaudeConfigGenerator.js +23 -0
- package/dist/generators/ClaudeConfigGenerator.js.map +1 -1
- package/dist/generators/ClineConfigGenerator.d.ts +23 -0
- package/dist/generators/ClineConfigGenerator.d.ts.map +1 -1
- package/dist/generators/ClineConfigGenerator.js +23 -0
- package/dist/generators/ClineConfigGenerator.js.map +1 -1
- package/dist/generators/CodeiumConfigGenerator.d.ts +23 -0
- package/dist/generators/CodeiumConfigGenerator.d.ts.map +1 -1
- package/dist/generators/CodeiumConfigGenerator.js +23 -0
- package/dist/generators/CodeiumConfigGenerator.js.map +1 -1
- package/dist/generators/CodexConfigGenerator.d.ts +23 -0
- package/dist/generators/CodexConfigGenerator.d.ts.map +1 -1
- package/dist/generators/CodexConfigGenerator.js +23 -0
- package/dist/generators/CodexConfigGenerator.js.map +1 -1
- package/dist/generators/ConfigGeneratorFactory.d.ts +9 -0
- package/dist/generators/ConfigGeneratorFactory.d.ts.map +1 -1
- package/dist/generators/ConfigGeneratorFactory.js +9 -0
- package/dist/generators/ConfigGeneratorFactory.js.map +1 -1
- package/dist/generators/ContinueConfigGenerator.d.ts +23 -0
- package/dist/generators/ContinueConfigGenerator.d.ts.map +1 -1
- package/dist/generators/ContinueConfigGenerator.js +23 -0
- package/dist/generators/ContinueConfigGenerator.js.map +1 -1
- package/dist/generators/CopilotConfigGenerator.d.ts +23 -0
- package/dist/generators/CopilotConfigGenerator.d.ts.map +1 -1
- package/dist/generators/CopilotConfigGenerator.js +23 -0
- package/dist/generators/CopilotConfigGenerator.js.map +1 -1
- package/dist/generators/CursorConfigGenerator.d.ts +23 -0
- package/dist/generators/CursorConfigGenerator.d.ts.map +1 -1
- package/dist/generators/CursorConfigGenerator.js +23 -0
- package/dist/generators/CursorConfigGenerator.js.map +1 -1
- package/dist/generators/GeminiConfigGenerator.d.ts +23 -0
- package/dist/generators/GeminiConfigGenerator.d.ts.map +1 -1
- package/dist/generators/GeminiConfigGenerator.js +23 -0
- package/dist/generators/GeminiConfigGenerator.js.map +1 -1
- package/dist/generators/VSCodeConfigGenerator.d.ts +23 -0
- package/dist/generators/VSCodeConfigGenerator.d.ts.map +1 -1
- package/dist/generators/VSCodeConfigGenerator.js +23 -0
- package/dist/generators/VSCodeConfigGenerator.js.map +1 -1
- package/dist/generators/ZCodeConfigGenerator.d.ts +23 -0
- package/dist/generators/ZCodeConfigGenerator.d.ts.map +1 -1
- package/dist/generators/ZCodeConfigGenerator.js +23 -0
- package/dist/generators/ZCodeConfigGenerator.js.map +1 -1
- package/dist/graph/blastRadius.d.ts.map +1 -1
- package/dist/graph/blastRadius.js +12 -7
- package/dist/graph/blastRadius.js.map +1 -1
- package/dist/graph/callGraph.d.ts +31 -0
- package/dist/graph/callGraph.d.ts.map +1 -1
- package/dist/graph/callGraph.js +62 -32
- package/dist/graph/callGraph.js.map +1 -1
- package/dist/graph/importClassification.d.ts +28 -0
- package/dist/graph/importClassification.d.ts.map +1 -1
- package/dist/graph/importClassification.js +57 -0
- package/dist/graph/importClassification.js.map +1 -1
- package/dist/graph/importGraph.d.ts +38 -0
- package/dist/graph/importGraph.d.ts.map +1 -1
- package/dist/graph/importGraph.js +38 -0
- package/dist/graph/importGraph.js.map +1 -1
- package/dist/graph/outputFormatter.d.ts +12 -4
- package/dist/graph/outputFormatter.d.ts.map +1 -1
- package/dist/graph/outputFormatter.js +12 -62
- package/dist/graph/outputFormatter.js.map +1 -1
- package/dist/hooks/codex.d.ts +8 -1
- package/dist/hooks/codex.d.ts.map +1 -1
- package/dist/hooks/codex.js +7 -0
- package/dist/hooks/codex.js.map +1 -1
- package/dist/hooks/core.d.ts +6 -0
- package/dist/hooks/core.d.ts.map +1 -1
- package/dist/hooks/core.js +6 -1
- package/dist/hooks/core.js.map +1 -1
- package/dist/hooks/cursor.d.ts +11 -1
- package/dist/hooks/cursor.d.ts.map +1 -1
- package/dist/hooks/cursor.js +10 -0
- package/dist/hooks/cursor.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/installConfig.d.ts +23 -5
- package/dist/installConfig.d.ts.map +1 -1
- package/dist/installConfig.js +23 -5
- package/dist/installConfig.js.map +1 -1
- package/dist/installer.d.ts +6 -0
- package/dist/installer.d.ts.map +1 -1
- package/dist/installer.js +6 -0
- package/dist/installer.js.map +1 -1
- package/dist/invariants/ruleEngine.d.ts +3 -0
- package/dist/invariants/ruleEngine.d.ts.map +1 -1
- package/dist/invariants/ruleEngine.js +156 -119
- package/dist/invariants/ruleEngine.js.map +1 -1
- package/dist/invariants/ruleValidator.d.ts +3 -0
- package/dist/invariants/ruleValidator.d.ts.map +1 -1
- package/dist/invariants/ruleValidator.js +185 -178
- package/dist/invariants/ruleValidator.js.map +1 -1
- package/dist/languages/LanguageOrchestrator.d.ts +4 -1
- package/dist/languages/LanguageOrchestrator.d.ts.map +1 -1
- package/dist/languages/LanguageOrchestrator.js +8 -19
- package/dist/languages/LanguageOrchestrator.js.map +1 -1
- package/dist/languages/RuntimeManager.d.ts +8 -0
- package/dist/languages/RuntimeManager.d.ts.map +1 -1
- package/dist/languages/RuntimeManager.js +55 -5
- package/dist/languages/RuntimeManager.js.map +1 -1
- package/dist/languages/UniversalAnalyzer.js +1 -1
- package/dist/languages/UniversalAnalyzer.js.map +1 -1
- package/dist/languages/adapterBridge.d.ts +4 -0
- package/dist/languages/adapterBridge.d.ts.map +1 -1
- package/dist/languages/adapterBridge.js +37 -3
- package/dist/languages/adapterBridge.js.map +1 -1
- package/dist/languages/go/GoAdapter.d.ts.map +1 -1
- package/dist/languages/go/GoAdapter.js +11 -14
- package/dist/languages/go/GoAdapter.js.map +1 -1
- package/dist/languages/go/analyzer-darwin-amd64 +0 -0
- package/dist/languages/go/analyzer-darwin-arm64 +0 -0
- package/dist/languages/go/analyzer-linux-amd64 +0 -0
- package/dist/languages/go/analyzer-linux-arm64 +0 -0
- package/dist/languages/go/analyzer-src/analyzer.go +2 -0
- package/dist/languages/go/analyzer-src/dataaccess.go +388 -0
- package/dist/languages/go/analyzer-src/types.go +8 -0
- package/dist/languages/go/analyzer-windows-amd64.exe +0 -0
- package/dist/languages/go/database-packages.json +8 -0
- package/dist/languages/go/goResolution.d.ts +229 -0
- package/dist/languages/go/goResolution.d.ts.map +1 -0
- package/dist/languages/go/goResolution.js +799 -0
- package/dist/languages/go/goResolution.js.map +1 -0
- package/dist/languages/go/stdlib-packages.json +353 -0
- package/dist/languages/go/tenantInputs.d.ts +37 -0
- package/dist/languages/go/tenantInputs.d.ts.map +1 -0
- package/dist/languages/go/tenantInputs.js +121 -0
- package/dist/languages/go/tenantInputs.js.map +1 -0
- package/dist/languages/index.d.ts +1 -0
- package/dist/languages/index.d.ts.map +1 -1
- package/dist/languages/index.js +5 -0
- package/dist/languages/index.js.map +1 -1
- package/dist/languages/json/JsonAdapter.d.ts +177 -0
- package/dist/languages/json/JsonAdapter.d.ts.map +1 -0
- package/dist/languages/json/JsonAdapter.js +531 -0
- package/dist/languages/json/JsonAdapter.js.map +1 -0
- package/dist/languages/locationIndex.d.ts +41 -0
- package/dist/languages/locationIndex.d.ts.map +1 -0
- package/dist/languages/locationIndex.js +42 -0
- package/dist/languages/locationIndex.js.map +1 -0
- package/dist/languages/sql/dialectDetection.d.ts +57 -0
- package/dist/languages/sql/dialectDetection.d.ts.map +1 -0
- package/dist/languages/sql/dialectDetection.js +135 -0
- package/dist/languages/sql/dialectDetection.js.map +1 -0
- package/dist/languages/sql/sqlAst.d.ts +494 -0
- package/dist/languages/sql/sqlAst.d.ts.map +1 -0
- package/dist/languages/sql/sqlAst.js +1817 -0
- package/dist/languages/sql/sqlAst.js.map +1 -0
- package/dist/languages/testConventions.d.ts +12 -1
- package/dist/languages/testConventions.d.ts.map +1 -1
- package/dist/languages/testConventions.js +13 -1
- package/dist/languages/testConventions.js.map +1 -1
- package/dist/languages/tree-sitter/TreeSitterCssAdapter.js +1 -1
- package/dist/languages/tree-sitter/TreeSitterCssAdapter.js.map +1 -1
- package/dist/languages/tree-sitter/converter.js +2 -2
- package/dist/languages/tree-sitter/converter.js.map +1 -1
- package/dist/languages/tree-sitter/parser.d.ts +3 -4
- package/dist/languages/tree-sitter/parser.d.ts.map +1 -1
- package/dist/languages/tree-sitter/parser.js +10 -4
- package/dist/languages/tree-sitter/parser.js.map +1 -1
- package/dist/languages/tree-sitter/rawNode.d.ts +3 -0
- package/dist/languages/tree-sitter/rawNode.d.ts.map +1 -1
- package/dist/languages/tree-sitter/rawNode.js +3 -0
- package/dist/languages/tree-sitter/rawNode.js.map +1 -1
- package/dist/languages/types.d.ts +7 -0
- package/dist/languages/types.d.ts.map +1 -1
- package/dist/languages/typescript/TreeSitterTypeScriptAdapter.d.ts +36 -0
- package/dist/languages/typescript/TreeSitterTypeScriptAdapter.d.ts.map +1 -1
- package/dist/languages/typescript/TreeSitterTypeScriptAdapter.js +168 -45
- package/dist/languages/typescript/TreeSitterTypeScriptAdapter.js.map +1 -1
- package/dist/languages/typescript/database-packages.json +19 -0
- package/dist/ledger.d.ts +118 -2
- package/dist/ledger.d.ts.map +1 -1
- package/dist/ledger.js +167 -45
- package/dist/ledger.js.map +1 -1
- package/dist/legacyIndexNotice.d.ts +32 -0
- package/dist/legacyIndexNotice.d.ts.map +1 -0
- package/dist/legacyIndexNotice.js +89 -0
- package/dist/legacyIndexNotice.js.map +1 -0
- package/dist/mcp-tools/discoveryQueries.d.ts +55 -0
- package/dist/mcp-tools/discoveryQueries.d.ts.map +1 -0
- package/dist/mcp-tools/discoveryQueries.js +157 -0
- package/dist/mcp-tools/discoveryQueries.js.map +1 -0
- package/dist/mcp-tools/projectTasks.d.ts +9 -123
- package/dist/mcp-tools/projectTasks.d.ts.map +1 -1
- package/dist/mcp-tools/projectTasks.js +180 -170
- package/dist/mcp-tools/projectTasks.js.map +1 -1
- package/dist/mcp-tools/whitelistTools.d.ts +20 -0
- package/dist/mcp-tools/whitelistTools.d.ts.map +1 -1
- package/dist/mcp-tools/whitelistTools.js +20 -0
- package/dist/mcp-tools/whitelistTools.js.map +1 -1
- package/dist/mcp-tools/workflowGuide.d.ts +9 -0
- package/dist/mcp-tools/workflowGuide.d.ts.map +1 -1
- package/dist/mcp-tools/workflowGuide.js +13 -5
- package/dist/mcp-tools/workflowGuide.js.map +1 -1
- package/dist/mcp-tools-shared.d.ts +78 -0
- package/dist/mcp-tools-shared.d.ts.map +1 -1
- package/dist/mcp-tools-shared.js +203 -340
- package/dist/mcp-tools-shared.js.map +1 -1
- package/dist/mcp-ui-simple.d.ts +2 -0
- package/dist/mcp-ui-simple.d.ts.map +1 -1
- package/dist/mcp-ui-simple.js +12 -5
- package/dist/mcp-ui-simple.js.map +1 -1
- package/dist/mcp.d.ts +6 -0
- package/dist/mcp.d.ts.map +1 -1
- package/dist/mcp.js +827 -850
- package/dist/mcp.js.map +1 -1
- package/dist/mcpAuditJobs.d.ts +29 -33
- package/dist/mcpAuditJobs.d.ts.map +1 -1
- package/dist/mcpAuditJobs.js +102 -791
- package/dist/mcpAuditJobs.js.map +1 -1
- package/dist/mcpAutoIndex.d.ts +5 -0
- package/dist/mcpAutoIndex.d.ts.map +1 -1
- package/dist/mcpAutoIndex.js +7 -2
- package/dist/mcpAutoIndex.js.map +1 -1
- package/dist/mcpDiagnostics.d.ts +7 -0
- package/dist/mcpDiagnostics.d.ts.map +1 -1
- package/dist/mcpDiagnostics.js +34 -9
- package/dist/mcpDiagnostics.js.map +1 -1
- package/dist/mcpToolErrors.d.ts +16 -0
- package/dist/mcpToolErrors.d.ts.map +1 -1
- package/dist/mcpToolErrors.js +16 -0
- package/dist/mcpToolErrors.js.map +1 -1
- package/dist/nextFile.d.ts +6 -0
- package/dist/nextFile.d.ts.map +1 -1
- package/dist/nextFile.js +6 -0
- package/dist/nextFile.js.map +1 -1
- package/dist/nextFileIncremental.d.ts +44 -4
- package/dist/nextFileIncremental.d.ts.map +1 -1
- package/dist/nextFileIncremental.js +47 -7
- package/dist/nextFileIncremental.js.map +1 -1
- package/dist/phase/batchFunctions.d.ts +38 -0
- package/dist/phase/batchFunctions.d.ts.map +1 -0
- package/dist/phase/batchFunctions.js +64 -0
- package/dist/phase/batchFunctions.js.map +1 -0
- package/dist/phase/checks.d.ts +39 -0
- package/dist/phase/checks.d.ts.map +1 -0
- package/dist/phase/checks.js +48 -0
- package/dist/phase/checks.js.map +1 -0
- package/dist/phase/codeBlocks.d.ts +25 -0
- package/dist/phase/codeBlocks.d.ts.map +1 -0
- package/dist/phase/codeBlocks.js +268 -0
- package/dist/phase/codeBlocks.js.map +1 -0
- package/dist/phase/colorMath.d.ts +41 -0
- package/dist/phase/colorMath.d.ts.map +1 -0
- package/dist/phase/colorMath.js +109 -0
- package/dist/phase/colorMath.js.map +1 -0
- package/dist/phase/config.d.ts +29 -0
- package/dist/phase/config.d.ts.map +1 -0
- package/dist/phase/config.js +54 -0
- package/dist/phase/config.js.map +1 -0
- package/dist/phase/consumed.d.ts +47 -0
- package/dist/phase/consumed.d.ts.map +1 -0
- package/dist/phase/consumed.js +22 -0
- package/dist/phase/consumed.js.map +1 -0
- package/dist/phase/conventionMining.d.ts +59 -0
- package/dist/phase/conventionMining.d.ts.map +1 -0
- package/dist/phase/conventionMining.js +152 -0
- package/dist/phase/conventionMining.js.map +1 -0
- package/dist/phase/coverage.d.ts +65 -0
- package/dist/phase/coverage.d.ts.map +1 -0
- package/dist/phase/coverage.js +121 -0
- package/dist/phase/coverage.js.map +1 -0
- package/dist/phase/dag.d.ts +49 -0
- package/dist/phase/dag.d.ts.map +1 -0
- package/dist/phase/dag.js +120 -0
- package/dist/phase/dag.js.map +1 -0
- package/dist/phase/dataAccessCalls.d.ts +33 -0
- package/dist/phase/dataAccessCalls.d.ts.map +1 -0
- package/dist/phase/dataAccessCalls.js +42 -0
- package/dist/phase/dataAccessCalls.js.map +1 -0
- package/dist/phase/dataAccessCallsCandidates.d.ts +25 -0
- package/dist/phase/dataAccessCallsCandidates.d.ts.map +1 -0
- package/dist/phase/dataAccessCallsCandidates.js +27 -0
- package/dist/phase/dataAccessCallsCandidates.js.map +1 -0
- package/dist/phase/dynamicSql.d.ts +18 -0
- package/dist/phase/dynamicSql.d.ts.map +1 -0
- package/dist/phase/dynamicSql.js +20 -0
- package/dist/phase/dynamicSql.js.map +1 -0
- package/dist/phase/exportForm.d.ts +23 -0
- package/dist/phase/exportForm.d.ts.map +1 -0
- package/dist/phase/exportForm.js +29 -0
- package/dist/phase/exportForm.js.map +1 -0
- package/dist/phase/exportSymbols.d.ts +21 -0
- package/dist/phase/exportSymbols.d.ts.map +1 -0
- package/dist/phase/exportSymbols.js +28 -0
- package/dist/phase/exportSymbols.js.map +1 -0
- package/dist/phase/fileHeader.d.ts +15 -0
- package/dist/phase/fileHeader.d.ts.map +1 -0
- package/dist/phase/fileHeader.js +33 -0
- package/dist/phase/fileHeader.js.map +1 -0
- package/dist/phase/fileImports.d.ts +39 -0
- package/dist/phase/fileImports.d.ts.map +1 -0
- package/dist/phase/fileImports.js +138 -0
- package/dist/phase/fileImports.js.map +1 -0
- package/dist/phase/fileSymbols.d.ts +25 -0
- package/dist/phase/fileSymbols.d.ts.map +1 -0
- package/dist/phase/fileSymbols.js +302 -0
- package/dist/phase/fileSymbols.js.map +1 -0
- package/dist/phase/functionBodies.d.ts +28 -0
- package/dist/phase/functionBodies.d.ts.map +1 -0
- package/dist/phase/functionBodies.js +67 -0
- package/dist/phase/functionBodies.js.map +1 -0
- package/dist/phase/functionIndex.d.ts +28 -0
- package/dist/phase/functionIndex.d.ts.map +1 -0
- package/dist/phase/functionIndex.js +152 -0
- package/dist/phase/functionIndex.js.map +1 -0
- package/dist/phase/goFunctionAnalysis.d.ts +47 -0
- package/dist/phase/goFunctionAnalysis.d.ts.map +1 -0
- package/dist/phase/goFunctionAnalysis.js +291 -0
- package/dist/phase/goFunctionAnalysis.js.map +1 -0
- package/dist/phase/goFunctions.d.ts +38 -0
- package/dist/phase/goFunctions.d.ts.map +1 -0
- package/dist/phase/goFunctions.js +154 -0
- package/dist/phase/goFunctions.js.map +1 -0
- package/dist/phase/goImports.d.ts +29 -0
- package/dist/phase/goImports.d.ts.map +1 -0
- package/dist/phase/goImports.js +68 -0
- package/dist/phase/goImports.js.map +1 -0
- package/dist/phase/goPackageBindings.d.ts +22 -0
- package/dist/phase/goPackageBindings.d.ts.map +1 -0
- package/dist/phase/goPackageBindings.js +30 -0
- package/dist/phase/goPackageBindings.js.map +1 -0
- package/dist/phase/goSwitches.d.ts +28 -0
- package/dist/phase/goSwitches.d.ts.map +1 -0
- package/dist/phase/goSwitches.js +52 -0
- package/dist/phase/goSwitches.js.map +1 -0
- package/dist/phase/importForm.d.ts +20 -0
- package/dist/phase/importForm.d.ts.map +1 -0
- package/dist/phase/importForm.js +27 -0
- package/dist/phase/importForm.js.map +1 -0
- package/dist/phase/importSpecifiers.d.ts +20 -0
- package/dist/phase/importSpecifiers.d.ts.map +1 -0
- package/dist/phase/importSpecifiers.js +30 -0
- package/dist/phase/importSpecifiers.js.map +1 -0
- package/dist/phase/imports.d.ts +18 -0
- package/dist/phase/imports.d.ts.map +1 -0
- package/dist/phase/imports.js +25 -0
- package/dist/phase/imports.js.map +1 -0
- package/dist/phase/jsonDocument.d.ts +26 -0
- package/dist/phase/jsonDocument.d.ts.map +1 -0
- package/dist/phase/jsonDocument.js +36 -0
- package/dist/phase/jsonDocument.js.map +1 -0
- package/dist/phase/localBinding.d.ts +33 -0
- package/dist/phase/localBinding.d.ts.map +1 -0
- package/dist/phase/localBinding.js +208 -0
- package/dist/phase/localBinding.js.map +1 -0
- package/dist/phase/loopQueries.d.ts +31 -0
- package/dist/phase/loopQueries.d.ts.map +1 -0
- package/dist/phase/loopQueries.js +44 -0
- package/dist/phase/loopQueries.js.map +1 -0
- package/dist/phase/loopQueryCandidates.d.ts +26 -0
- package/dist/phase/loopQueryCandidates.d.ts.map +1 -0
- package/dist/phase/loopQueryCandidates.js +28 -0
- package/dist/phase/loopQueryCandidates.js.map +1 -0
- package/dist/phase/oracles.d.ts +216 -0
- package/dist/phase/oracles.d.ts.map +1 -0
- package/dist/phase/oracles.js +484 -0
- package/dist/phase/oracles.js.map +1 -0
- package/dist/phase/phaseModel.d.ts +190 -0
- package/dist/phase/phaseModel.d.ts.map +1 -0
- package/dist/phase/phaseModel.js +468 -0
- package/dist/phase/phaseModel.js.map +1 -0
- package/dist/phase/producers.d.ts +450 -0
- package/dist/phase/producers.d.ts.map +1 -0
- package/dist/phase/producers.js +1027 -0
- package/dist/phase/producers.js.map +1 -0
- package/dist/phase/querySiteCandidates.d.ts +27 -0
- package/dist/phase/querySiteCandidates.d.ts.map +1 -0
- package/dist/phase/querySiteCandidates.js +66 -0
- package/dist/phase/querySiteCandidates.js.map +1 -0
- package/dist/phase/querySites.d.ts +30 -0
- package/dist/phase/querySites.d.ts.map +1 -0
- package/dist/phase/querySites.js +93 -0
- package/dist/phase/querySites.js.map +1 -0
- package/dist/phase/reachability.d.ts +61 -0
- package/dist/phase/reachability.d.ts.map +1 -0
- package/dist/phase/reachability.js +104 -0
- package/dist/phase/reachability.js.map +1 -0
- package/dist/phase/reactComponents.d.ts +33 -0
- package/dist/phase/reactComponents.d.ts.map +1 -0
- package/dist/phase/reactComponents.js +47 -0
- package/dist/phase/reactComponents.js.map +1 -0
- package/dist/phase/receiverActivity.d.ts +35 -0
- package/dist/phase/receiverActivity.d.ts.map +1 -0
- package/dist/phase/receiverActivity.js +66 -0
- package/dist/phase/receiverActivity.js.map +1 -0
- package/dist/phase/receiverConsumers.d.ts +200 -0
- package/dist/phase/receiverConsumers.d.ts.map +1 -0
- package/dist/phase/receiverConsumers.js +1025 -0
- package/dist/phase/receiverConsumers.js.map +1 -0
- package/dist/phase/receiverProvenance.d.ts +97 -0
- package/dist/phase/receiverProvenance.d.ts.map +1 -0
- package/dist/phase/receiverProvenance.js +337 -0
- package/dist/phase/receiverProvenance.js.map +1 -0
- package/dist/phase/routing.d.ts +47 -0
- package/dist/phase/routing.d.ts.map +1 -0
- package/dist/phase/routing.js +62 -0
- package/dist/phase/routing.js.map +1 -0
- package/dist/phase/rules/conventions.d.ts +61 -0
- package/dist/phase/rules/conventions.d.ts.map +1 -0
- package/dist/phase/rules/conventions.js +403 -0
- package/dist/phase/rules/conventions.js.map +1 -0
- package/dist/phase/rules/crossDomain.d.ts +102 -0
- package/dist/phase/rules/crossDomain.d.ts.map +1 -0
- package/dist/phase/rules/crossDomain.js +918 -0
- package/dist/phase/rules/crossDomain.js.map +1 -0
- package/dist/phase/rules/dataAccess.d.ts +62 -0
- package/dist/phase/rules/dataAccess.d.ts.map +1 -0
- package/dist/phase/rules/dataAccess.js +480 -0
- package/dist/phase/rules/dataAccess.js.map +1 -0
- package/dist/phase/rules/dependencyGraph.d.ts +44 -0
- package/dist/phase/rules/dependencyGraph.d.ts.map +1 -0
- package/dist/phase/rules/dependencyGraph.js +260 -0
- package/dist/phase/rules/dependencyGraph.js.map +1 -0
- package/dist/phase/rules/documentation.d.ts +45 -0
- package/dist/phase/rules/documentation.d.ts.map +1 -0
- package/dist/phase/rules/documentation.js +340 -0
- package/dist/phase/rules/documentation.js.map +1 -0
- package/dist/phase/rules/dry.d.ts +109 -0
- package/dist/phase/rules/dry.d.ts.map +1 -0
- package/dist/phase/rules/dry.js +803 -0
- package/dist/phase/rules/dry.js.map +1 -0
- package/dist/phase/rules/fileDocumentation.d.ts +36 -0
- package/dist/phase/rules/fileDocumentation.d.ts.map +1 -0
- package/dist/phase/rules/fileDocumentation.js +134 -0
- package/dist/phase/rules/fileDocumentation.js.map +1 -0
- package/dist/phase/rules/functionBodies.d.ts +33 -0
- package/dist/phase/rules/functionBodies.d.ts.map +1 -0
- package/dist/phase/rules/functionBodies.js +71 -0
- package/dist/phase/rules/functionBodies.js.map +1 -0
- package/dist/phase/rules/goRules.d.ts +49 -0
- package/dist/phase/rules/goRules.d.ts.map +1 -0
- package/dist/phase/rules/goRules.js +209 -0
- package/dist/phase/rules/goRules.js.map +1 -0
- package/dist/phase/rules/querySites.d.ts +29 -0
- package/dist/phase/rules/querySites.d.ts.map +1 -0
- package/dist/phase/rules/querySites.js +94 -0
- package/dist/phase/rules/querySites.js.map +1 -0
- package/dist/phase/rules/react.d.ts +38 -0
- package/dist/phase/rules/react.d.ts.map +1 -0
- package/dist/phase/rules/react.js +202 -0
- package/dist/phase/rules/react.js.map +1 -0
- package/dist/phase/rules/registry.d.ts +170 -0
- package/dist/phase/rules/registry.d.ts.map +1 -0
- package/dist/phase/rules/registry.js +103 -0
- package/dist/phase/rules/registry.js.map +1 -0
- package/dist/phase/rules/schema.d.ts +79 -0
- package/dist/phase/rules/schema.d.ts.map +1 -0
- package/dist/phase/rules/schema.js +273 -0
- package/dist/phase/rules/schema.js.map +1 -0
- package/dist/phase/rules/schemaJson.d.ts +32 -0
- package/dist/phase/rules/schemaJson.d.ts.map +1 -0
- package/dist/phase/rules/schemaJson.js +81 -0
- package/dist/phase/rules/schemaJson.js.map +1 -0
- package/dist/phase/rules/schemaValidator.d.ts +38 -0
- package/dist/phase/rules/schemaValidator.d.ts.map +1 -0
- package/dist/phase/rules/schemaValidator.js +107 -0
- package/dist/phase/rules/schemaValidator.js.map +1 -0
- package/dist/phase/rules/secrets.d.ts +34 -0
- package/dist/phase/rules/secrets.d.ts.map +1 -0
- package/dist/phase/rules/secrets.js +208 -0
- package/dist/phase/rules/secrets.js.map +1 -0
- package/dist/phase/rules/security.d.ts +30 -0
- package/dist/phase/rules/security.d.ts.map +1 -0
- package/dist/phase/rules/security.js +85 -0
- package/dist/phase/rules/security.js.map +1 -0
- package/dist/phase/rules/securityDefects.d.ts +37 -0
- package/dist/phase/rules/securityDefects.d.ts.map +1 -0
- package/dist/phase/rules/securityDefects.js +167 -0
- package/dist/phase/rules/securityDefects.js.map +1 -0
- package/dist/phase/rules/solid.d.ts +61 -0
- package/dist/phase/rules/solid.d.ts.map +1 -0
- package/dist/phase/rules/solid.js +562 -0
- package/dist/phase/rules/solid.js.map +1 -0
- package/dist/phase/rules/styles.d.ts +77 -0
- package/dist/phase/rules/styles.d.ts.map +1 -0
- package/dist/phase/rules/styles.js +868 -0
- package/dist/phase/rules/styles.js.map +1 -0
- package/dist/phase/rules/unreferencedModule.d.ts +26 -0
- package/dist/phase/rules/unreferencedModule.d.ts.map +1 -0
- package/dist/phase/rules/unreferencedModule.js +61 -0
- package/dist/phase/rules/unreferencedModule.js.map +1 -0
- package/dist/phase/runner.d.ts +549 -0
- package/dist/phase/runner.d.ts.map +1 -0
- package/dist/phase/runner.js +1098 -0
- package/dist/phase/runner.js.map +1 -0
- package/dist/phase/schemaCode.d.ts +34 -0
- package/dist/phase/schemaCode.d.ts.map +1 -0
- package/dist/phase/schemaCode.js +53 -0
- package/dist/phase/schemaCode.js.map +1 -0
- package/dist/phase/schemaObjects.d.ts +37 -0
- package/dist/phase/schemaObjects.d.ts.map +1 -0
- package/dist/phase/schemaObjects.js +139 -0
- package/dist/phase/schemaObjects.js.map +1 -0
- package/dist/phase/schemaUsage.d.ts +30 -0
- package/dist/phase/schemaUsage.d.ts.map +1 -0
- package/dist/phase/schemaUsage.js +99 -0
- package/dist/phase/schemaUsage.js.map +1 -0
- package/dist/phase/schemaUsageCandidates.d.ts +32 -0
- package/dist/phase/schemaUsageCandidates.d.ts.map +1 -0
- package/dist/phase/schemaUsageCandidates.js +165 -0
- package/dist/phase/schemaUsageCandidates.js.map +1 -0
- package/dist/phase/schemaValidations.d.ts +34 -0
- package/dist/phase/schemaValidations.d.ts.map +1 -0
- package/dist/phase/schemaValidations.js +53 -0
- package/dist/phase/schemaValidations.js.map +1 -0
- package/dist/phase/secretCandidates.d.ts +28 -0
- package/dist/phase/secretCandidates.d.ts.map +1 -0
- package/dist/phase/secretCandidates.js +132 -0
- package/dist/phase/secretCandidates.js.map +1 -0
- package/dist/phase/securityCandidates.d.ts +29 -0
- package/dist/phase/securityCandidates.d.ts.map +1 -0
- package/dist/phase/securityCandidates.js +469 -0
- package/dist/phase/securityCandidates.js.map +1 -0
- package/dist/phase/seeded-defects.d.ts +18 -0
- package/dist/phase/seeded-defects.d.ts.map +1 -0
- package/dist/phase/seeded-defects.js +61 -0
- package/dist/phase/seeded-defects.js.map +1 -0
- package/dist/phase/stringLiterals.d.ts +24 -0
- package/dist/phase/stringLiterals.d.ts.map +1 -0
- package/dist/phase/stringLiterals.js +35 -0
- package/dist/phase/stringLiterals.js.map +1 -0
- package/dist/phase/stylesCss.d.ts +25 -0
- package/dist/phase/stylesCss.d.ts.map +1 -0
- package/dist/phase/stylesCss.js +34 -0
- package/dist/phase/stylesCss.js.map +1 -0
- package/dist/phase/stylesMarkup.d.ts +31 -0
- package/dist/phase/stylesMarkup.d.ts.map +1 -0
- package/dist/phase/stylesMarkup.js +48 -0
- package/dist/phase/stylesMarkup.js.map +1 -0
- package/dist/phase/stylesSource.d.ts +36 -0
- package/dist/phase/stylesSource.d.ts.map +1 -0
- package/dist/phase/stylesSource.js +50 -0
- package/dist/phase/stylesSource.js.map +1 -0
- package/dist/phase/typeDeclarations.d.ts +33 -0
- package/dist/phase/typeDeclarations.d.ts.map +1 -0
- package/dist/phase/typeDeclarations.js +76 -0
- package/dist/phase/typeDeclarations.js.map +1 -0
- package/dist/phase/types.d.ts +2191 -0
- package/dist/phase/types.d.ts.map +1 -0
- package/dist/phase/types.js +27 -0
- package/dist/phase/types.js.map +1 -0
- package/dist/phase/withinFileProvenance.d.ts +50 -0
- package/dist/phase/withinFileProvenance.d.ts.map +1 -0
- package/dist/phase/withinFileProvenance.js +112 -0
- package/dist/phase/withinFileProvenance.js.map +1 -0
- package/dist/pipeline.d.ts +37 -30
- package/dist/pipeline.d.ts.map +1 -1
- package/dist/pipeline.js +131 -326
- package/dist/pipeline.js.map +1 -1
- package/dist/pipelineAdapters.d.ts +104 -21
- package/dist/pipelineAdapters.d.ts.map +1 -1
- package/dist/pipelineAdapters.js +1144 -984
- package/dist/pipelineAdapters.js.map +1 -1
- package/dist/pipelineTypes.d.ts +21 -0
- package/dist/pipelineTypes.d.ts.map +1 -1
- package/dist/pipelineTypes.js +21 -10
- package/dist/pipelineTypes.js.map +1 -1
- package/dist/presets/presets.d.ts +9 -2
- package/dist/presets/presets.d.ts.map +1 -1
- package/dist/presets/presets.js +15 -26
- package/dist/presets/presets.js.map +1 -1
- package/dist/reporting/csvReportGenerator.d.ts +14 -3
- package/dist/reporting/csvReportGenerator.d.ts.map +1 -1
- package/dist/reporting/csvReportGenerator.js +14 -3
- package/dist/reporting/csvReportGenerator.js.map +1 -1
- package/dist/reporting/htmlReportGenerator.d.ts +5 -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 +9 -2
- package/dist/reporting/jsonReportGenerator.d.ts.map +1 -1
- package/dist/reporting/jsonReportGenerator.js +19 -5
- package/dist/reporting/jsonReportGenerator.js.map +1 -1
- package/dist/reporting/reportGenerator.d.ts +9 -2
- package/dist/reporting/reportGenerator.d.ts.map +1 -1
- package/dist/reporting/reportGenerator.js +9 -2
- package/dist/reporting/reportGenerator.js.map +1 -1
- package/dist/reporting/sarifReportGenerator.d.ts +14 -0
- package/dist/reporting/sarifReportGenerator.d.ts.map +1 -1
- package/dist/reporting/sarifReportGenerator.js +14 -0
- package/dist/reporting/sarifReportGenerator.js.map +1 -1
- package/dist/reporting/sizeDistribution.d.ts +19 -2
- package/dist/reporting/sizeDistribution.d.ts.map +1 -1
- package/dist/reporting/sizeDistribution.js +23 -5
- package/dist/reporting/sizeDistribution.js.map +1 -1
- package/dist/ruleAliases.d.ts +6 -8
- package/dist/ruleAliases.d.ts.map +1 -1
- package/dist/ruleAliases.js +29 -4
- package/dist/ruleAliases.js.map +1 -1
- package/dist/search/QueryParser.d.ts +1 -0
- package/dist/search/QueryParser.d.ts.map +1 -1
- package/dist/search/QueryParser.js +32 -91
- package/dist/search/QueryParser.js.map +1 -1
- package/dist/services/CodeMapGenerator.d.ts +19 -20
- package/dist/services/CodeMapGenerator.d.ts.map +1 -1
- package/dist/services/CodeMapGenerator.js +214 -194
- package/dist/services/CodeMapGenerator.js.map +1 -1
- package/dist/services/ProjectTaskRepository.d.ts +65 -3
- package/dist/services/ProjectTaskRepository.d.ts.map +1 -1
- package/dist/services/ProjectTaskRepository.js +94 -25
- package/dist/services/ProjectTaskRepository.js.map +1 -1
- package/dist/services/auditJobService.d.ts +17 -1
- package/dist/services/auditJobService.d.ts.map +1 -1
- package/dist/services/auditJobService.js +16 -0
- package/dist/services/auditJobService.js.map +1 -1
- package/dist/services/fileAccounting.d.ts +43 -5
- package/dist/services/fileAccounting.d.ts.map +1 -1
- package/dist/services/fileAccounting.js +48 -9
- package/dist/services/fileAccounting.js.map +1 -1
- package/dist/services/projectTaskHelpers.d.ts +45 -0
- package/dist/services/projectTaskHelpers.d.ts.map +1 -1
- package/dist/services/projectTaskHelpers.js +45 -0
- package/dist/services/projectTaskHelpers.js.map +1 -1
- package/dist/services/whitelistService.d.ts +40 -5
- package/dist/services/whitelistService.d.ts.map +1 -1
- package/dist/services/whitelistService.js +87 -46
- package/dist/services/whitelistService.js.map +1 -1
- package/dist/sqlite/betterSqlite3.d.ts +35 -1
- package/dist/sqlite/betterSqlite3.d.ts.map +1 -1
- package/dist/sqlite/betterSqlite3.js +35 -1
- package/dist/sqlite/betterSqlite3.js.map +1 -1
- package/dist/sqlite/driver.d.ts +23 -0
- package/dist/sqlite/driver.d.ts.map +1 -1
- package/dist/sqlite/driver.js +23 -0
- package/dist/sqlite/driver.js.map +1 -1
- package/dist/sqlite/nodeSqlite.d.ts +35 -1
- package/dist/sqlite/nodeSqlite.d.ts.map +1 -1
- package/dist/sqlite/nodeSqlite.js +35 -1
- package/dist/sqlite/nodeSqlite.js.map +1 -1
- package/dist/structuralSignature.d.ts +9 -0
- package/dist/structuralSignature.d.ts.map +1 -1
- package/dist/structuralSignature.js +9 -0
- package/dist/structuralSignature.js.map +1 -1
- package/dist/styles/cssAstExtractor.d.ts +15 -0
- package/dist/styles/cssAstExtractor.d.ts.map +1 -1
- package/dist/styles/cssAstExtractor.js +15 -0
- package/dist/styles/cssAstExtractor.js.map +1 -1
- package/dist/styles/normalizer.d.ts +6 -0
- package/dist/styles/normalizer.d.ts.map +1 -1
- package/dist/styles/normalizer.js +6 -0
- package/dist/styles/normalizer.js.map +1 -1
- package/dist/styles/styleExtractor.d.ts +29 -2
- package/dist/styles/styleExtractor.d.ts.map +1 -1
- package/dist/styles/styleExtractor.js +67 -48
- package/dist/styles/styleExtractor.js.map +1 -1
- package/dist/styles/styleIndexer.d.ts +8 -0
- package/dist/styles/styleIndexer.d.ts.map +1 -1
- package/dist/styles/styleIndexer.js +135 -138
- package/dist/styles/styleIndexer.js.map +1 -1
- package/dist/styles/tailwindConfigLoader.d.ts +12 -0
- package/dist/styles/tailwindConfigLoader.d.ts.map +1 -1
- package/dist/styles/tailwindConfigLoader.js +12 -0
- package/dist/styles/tailwindConfigLoader.js.map +1 -1
- package/dist/styles/tailwindExpander.d.ts +3 -0
- package/dist/styles/tailwindExpander.d.ts.map +1 -1
- package/dist/styles/tailwindExpander.js +8 -4
- package/dist/styles/tailwindExpander.js.map +1 -1
- package/dist/styles/tailwindProbe.d.ts +9 -0
- package/dist/styles/tailwindProbe.d.ts.map +1 -1
- package/dist/styles/tailwindProbe.js +181 -166
- package/dist/styles/tailwindProbe.js.map +1 -1
- package/dist/styles/tailwindUtilityExpander.d.ts +13 -0
- package/dist/styles/tailwindUtilityExpander.d.ts.map +1 -1
- package/dist/styles/tailwindUtilityExpander.js +13 -0
- package/dist/styles/tailwindUtilityExpander.js.map +1 -1
- package/dist/symbols.d.ts +13 -11
- package/dist/symbols.d.ts.map +1 -1
- package/dist/symbols.js +14 -21
- package/dist/symbols.js.map +1 -1
- package/dist/telemetry.d.ts +31 -3
- package/dist/telemetry.d.ts.map +1 -1
- package/dist/telemetry.js +31 -3
- package/dist/telemetry.js.map +1 -1
- package/dist/tool-registry.d.ts +37 -1
- package/dist/tool-registry.d.ts.map +1 -1
- package/dist/tool-registry.js +37 -1
- package/dist/tool-registry.js.map +1 -1
- package/dist/types.d.ts +101 -71
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +7 -16
- package/dist/types.js.map +1 -1
- package/dist/ui/InteractivePrompts.d.ts +12 -0
- package/dist/ui/InteractivePrompts.d.ts.map +1 -1
- package/dist/ui/InteractivePrompts.js +12 -0
- package/dist/ui/InteractivePrompts.js.map +1 -1
- package/dist/utils/astParser.d.ts +15 -0
- package/dist/utils/astParser.d.ts.map +1 -1
- package/dist/utils/astParser.js +15 -0
- package/dist/utils/astParser.js.map +1 -1
- package/dist/utils/astUtils.d.ts +60 -0
- package/dist/utils/astUtils.d.ts.map +1 -1
- package/dist/utils/astUtils.js +350 -362
- package/dist/utils/astUtils.js.map +1 -1
- package/dist/utils/contentHash.d.ts +22 -0
- package/dist/utils/contentHash.d.ts.map +1 -0
- package/dist/utils/contentHash.js +26 -0
- package/dist/utils/contentHash.js.map +1 -0
- package/dist/utils/dependencyExtractor.d.ts +28 -0
- package/dist/utils/dependencyExtractor.d.ts.map +1 -1
- package/dist/utils/dependencyExtractor.js +28 -1
- package/dist/utils/dependencyExtractor.js.map +1 -1
- package/dist/utils/errorMessage.d.ts +10 -0
- package/dist/utils/errorMessage.d.ts.map +1 -0
- package/dist/utils/errorMessage.js +12 -0
- package/dist/utils/errorMessage.js.map +1 -0
- package/dist/utils/fileDiscovery.d.ts +61 -2
- package/dist/utils/fileDiscovery.d.ts.map +1 -1
- package/dist/utils/fileDiscovery.js +131 -23
- package/dist/utils/fileDiscovery.js.map +1 -1
- package/dist/utils/reactDetection.d.ts +34 -5
- package/dist/utils/reactDetection.d.ts.map +1 -1
- package/dist/utils/reactDetection.js +73 -52
- package/dist/utils/reactDetection.js.map +1 -1
- package/dist/version.generated.d.ts +1 -1
- package/dist/version.generated.js +1 -1
- package/dist/workers/auditWorker.js +7 -3
- package/dist/workers/auditWorker.js.map +1 -1
- package/examples/.auditrc.example.json +0 -8
- package/examples/.auditrc.json +0 -11
- package/examples/.code-auditor.json +0 -2
- package/examples/nextjs.auditrc.json +0 -8
- package/examples/node-api.auditrc.json +0 -7
- package/examples/react.auditrc.json +0 -6
- package/examples/tolerant-solid.auditrc.json +0 -5
- package/package.json +11 -5
- package/plugin/.claude-plugin/manifest.json +16 -0
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/hooks/hooks.json +6 -2
- package/plugin/scripts/hook-audit.sh +41 -14
- package/plugin/scripts/hook-common.sh +50 -5
- package/plugin/scripts/hook-drift-check.sh +72 -0
- package/plugin/scripts/hook-self-audit.sh +17 -12
- package/plugin/skills/code-auditor/SKILL.md +1 -1
- package/server.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -7,7 +7,6 @@ import './native-bootstrap.js';
|
|
|
7
7
|
import { Command } from 'commander';
|
|
8
8
|
import chalk from 'chalk';
|
|
9
9
|
import { createAuditRunner } from './auditRunner.js';
|
|
10
|
-
import { runAuditDispatch } from './auditRouter.js';
|
|
11
10
|
import { promises as fs } from 'fs';
|
|
12
11
|
import { createInterface } from 'readline';
|
|
13
12
|
import { dirname, isAbsolute, join, relative, resolve } from 'path';
|
|
@@ -23,7 +22,6 @@ import { fingerprint, buildFingerprintInput } from './fingerprint.js';
|
|
|
23
22
|
import { buildDismissalEntry, upsertDismissal } from './dismissals.js';
|
|
24
23
|
import { resolveTelemetryConfig, signatureForFinding, buildTelemetryPayload, formatTelemetryPreview, sendTelemetry, languageHint } from './telemetry.js';
|
|
25
24
|
import { getInstallId } from './installConfig.js';
|
|
26
|
-
import { ALL_ANALYZERS } from './analyzers/ruleRegistry.js';
|
|
27
25
|
import { computeGatingDecision } from './enforcement/gate.js';
|
|
28
26
|
import { BLOCKING_SEVERITIES, SEVERITIES } from './types.js';
|
|
29
27
|
import { rankFilesByPriority, orderFindingsWithinFile } from './nextFile.js';
|
|
@@ -36,6 +34,18 @@ const program = new Command();
|
|
|
36
34
|
// `--fail-on advisory` must fail loudly and name the new tier, not emit a generic
|
|
37
35
|
// "must be one of" list that leaves the user guessing what `advisory` became.
|
|
38
36
|
const LEGACY_SEVERITY_NAMES = { advisory: 'high' };
|
|
37
|
+
// CLI option descriptions, hoisted so the 30+ subcommands' repeated
|
|
38
|
+
// option() / -p,--path descriptions share one source.
|
|
39
|
+
const OPTION_OUTPUT_AS_JSON = 'Output as JSON';
|
|
40
|
+
const OPTION_PROJECT_PATH = 'Project path';
|
|
41
|
+
const OPTION_PROJECT_ROOT_PATH = 'Project root path';
|
|
42
|
+
const OPTION_PROJECT_ROOT = 'Project root';
|
|
43
|
+
const OPTION_CONFIGURATION_NAME = 'Configuration name';
|
|
44
|
+
const UNKNOWN_ERROR = 'Unknown error';
|
|
45
|
+
// "file:line" suffix for a finding row; empty when the finding has no line.
|
|
46
|
+
function lineSuffix(line) {
|
|
47
|
+
return line ? `:${line}` : '';
|
|
48
|
+
}
|
|
39
49
|
// Spec 44 R4 — file accounting summary line + `--explain-skipped` breakdown.
|
|
40
50
|
// The full per-reason file lists live in the JSON report (`metadata.fileAccounting`);
|
|
41
51
|
// the CLI preview is bounded to the first ~20 files per reason.
|
|
@@ -88,12 +98,447 @@ program
|
|
|
88
98
|
.name('code-auditor')
|
|
89
99
|
.description('TypeScript/JavaScript code quality auditor with AI tool integration')
|
|
90
100
|
.version(`${PACKAGE_VERSION} (sqlite: ${describeSqliteBackend()})`);
|
|
101
|
+
/**
|
|
102
|
+
* Collapse a receiver-resolution reason to a single line for the cannot-fire
|
|
103
|
+
* panel. Reasons embed the receiver text (which can span the SQL of a chained
|
|
104
|
+
* `db.prepare("...").bind(...)`), so newlines must be folded and the result
|
|
105
|
+
* truncated to stay legible in the per-file rollup.
|
|
106
|
+
*/
|
|
107
|
+
function sanitizeReason(reason) {
|
|
108
|
+
if (!reason)
|
|
109
|
+
return '';
|
|
110
|
+
const collapsed = reason.replace(/\s+/g, ' ').trim();
|
|
111
|
+
return collapsed.length > 220 ? `${collapsed.slice(0, 217)}…` : collapsed;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Print the pre-delta diagnostic panels: coverage, skipped analyzers, ignored
|
|
115
|
+
* config keys, coverage gaps, test coverage, size distributions, and dead
|
|
116
|
+
* clusters. Pure reporting over `result.metadata`.
|
|
117
|
+
*/
|
|
118
|
+
function printAuditDiagnostics(result) {
|
|
119
|
+
// ── Coverage panel leads the report (Spec 47 R2) ─────────────
|
|
120
|
+
const coverage = result.metadata?.coverage;
|
|
121
|
+
// Spec 69 §10 R4 — the site-level cannot-fire count (unproven DB-access
|
|
122
|
+
// receiver sites) sits beside the rule-level states, so one line reports
|
|
123
|
+
// both the per-rule verdict and the per-call-site gap the affected rules
|
|
124
|
+
// may have under-reported on.
|
|
125
|
+
const unprovenSiteCount = (result.metadata?.diagnostics ?? [])
|
|
126
|
+
.filter((d) => d.kind === 'cannot-fire' && d.analyzerName === 'schema')
|
|
127
|
+
.length;
|
|
128
|
+
if (coverage && coverage.length > 0) {
|
|
129
|
+
const covFired = coverage.filter(c => c.state === 'fired').length;
|
|
130
|
+
const covClean = coverage.filter(c => c.state === 'clean').length;
|
|
131
|
+
const covIncomplete = coverage.filter(c => c.state === 'incomplete').length;
|
|
132
|
+
const covNotApplicable = coverage.filter(c => c.state === 'notApplicable').length;
|
|
133
|
+
const covCannotFire = coverage.filter(c => c.state === 'cannot-fire').length;
|
|
134
|
+
console.log(chalk.gray(`── Coverage panel ── ${covFired} fired · ${covClean} clean · ` +
|
|
135
|
+
`${covIncomplete} incomplete · ${covNotApplicable} not-applicable · ` +
|
|
136
|
+
`${covCannotFire} cannot-fire · ${unprovenSiteCount} unproven sites`));
|
|
137
|
+
}
|
|
138
|
+
// ── Skipped analyzers (Spec 61) ──────────────────────────────
|
|
139
|
+
const skippedAnalyzers = (result.metadata?.diagnostics ?? []).filter((d) => d.kind === 'go-toolchain-missing' ||
|
|
140
|
+
d.kind === 'go-analyzer-wrong-arch' ||
|
|
141
|
+
d.kind === 'go-analyzer-build-failed');
|
|
142
|
+
if (skippedAnalyzers.length > 0) {
|
|
143
|
+
console.log(chalk.yellow(`── Skipped analyzers ── ${skippedAnalyzers.length}`));
|
|
144
|
+
for (const d of skippedAnalyzers) {
|
|
145
|
+
const name = d.analyzerName || 'go';
|
|
146
|
+
console.log(chalk.yellow(` ${name} [${d.kind}] — ${d.message}`));
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
// ── Ignored config keys (Spec 61 R1.4) ─────────────────────────
|
|
150
|
+
const ignoredConfigKeys = (result.metadata?.diagnostics ?? []).filter((d) => d.kind === 'config-key-rejected');
|
|
151
|
+
if (ignoredConfigKeys.length > 0) {
|
|
152
|
+
console.log(chalk.yellow(`── Ignored config keys ── ${ignoredConfigKeys.length}`));
|
|
153
|
+
for (const d of ignoredConfigKeys) {
|
|
154
|
+
console.log(chalk.yellow(` ${d.message}`));
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
// ── Cannot-fire sites (Spec 69 §10) ───────────────────────────
|
|
158
|
+
// The cannot-fire verdict surface is the release bar: every DB-access call
|
|
159
|
+
// site whose receiver could not be proven a handle must be *visible with its
|
|
160
|
+
// reason*, never capped or folded into a silent clean. Grouped by file so a
|
|
161
|
+
// reader can see exactly which files the table-reference and data-access
|
|
162
|
+
// rules may have under-reported on.
|
|
163
|
+
const allDiagnostics = result.metadata?.diagnostics ?? [];
|
|
164
|
+
const cannotFireSites = allDiagnostics.filter((d) => d.kind === 'cannot-fire');
|
|
165
|
+
if (cannotFireSites.length > 0) {
|
|
166
|
+
const byFile = new Map();
|
|
167
|
+
for (const d of cannotFireSites) {
|
|
168
|
+
const key = d.file ?? '(unknown)';
|
|
169
|
+
const entry = byFile.get(key) ?? { lines: [], reasons: new Set() };
|
|
170
|
+
if (typeof d.line === 'number')
|
|
171
|
+
entry.lines.push(d.line);
|
|
172
|
+
const rawReason = typeof d.details?.reason === 'string' ? d.details.reason : d.message;
|
|
173
|
+
const reason = sanitizeReason(rawReason);
|
|
174
|
+
if (reason)
|
|
175
|
+
entry.reasons.add(reason);
|
|
176
|
+
byFile.set(key, entry);
|
|
177
|
+
}
|
|
178
|
+
console.log(chalk.yellow(`── Cannot-fire sites ── ${cannotFireSites.length} unproven DB-access call sites across ${byFile.size} files`));
|
|
179
|
+
console.log(chalk.yellow(` Table-reference and data-access rules may report clean on access they could not observe.`));
|
|
180
|
+
for (const [file, entry] of [...byFile.entries()].sort((a, b) => a[0].localeCompare(b[0]))) {
|
|
181
|
+
const reasons = [...entry.reasons];
|
|
182
|
+
const shown = reasons.slice(0, 2).join(' │ ');
|
|
183
|
+
const more = reasons.length > 2 ? ` (+${reasons.length - 2} more reasons)` : '';
|
|
184
|
+
const loc = entry.lines.length === 1 ? ` (line ${entry.lines[0]})` : '';
|
|
185
|
+
console.log(chalk.yellow(` ${file} — ${entry.lines.length} site(s)${loc}: ${shown}${more}`));
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
// ── Coverage gaps (Spec 58 follow-up) ─────────────────────────
|
|
189
|
+
const otherCoverageGaps = allDiagnostics.filter((d) => d.kind === 'unresolved-query' || d.kind === 'unresolved-dynamic-import' || d.kind === 'undefined-class-not-found');
|
|
190
|
+
if (otherCoverageGaps.length > 0) {
|
|
191
|
+
const byKind = {};
|
|
192
|
+
for (const d of otherCoverageGaps)
|
|
193
|
+
byKind[d.kind] = (byKind[d.kind] ?? 0) + 1;
|
|
194
|
+
const totals = Object.entries(byKind)
|
|
195
|
+
.map(([kind, n]) => `${n} ${kind}`)
|
|
196
|
+
.join(' · ');
|
|
197
|
+
console.log(chalk.gray(`── Coverage gaps ── ${totals}`));
|
|
198
|
+
for (const d of otherCoverageGaps.slice(0, 20)) {
|
|
199
|
+
const loc = d.file ? `${d.file}${typeof d.line === 'number' ? `:${d.line}` : ''}` : '(unknown)';
|
|
200
|
+
console.log(chalk.gray(` ${loc} [${d.kind}] — ${d.message}`));
|
|
201
|
+
}
|
|
202
|
+
if (otherCoverageGaps.length > 20) {
|
|
203
|
+
console.log(chalk.gray(` … and ${otherCoverageGaps.length - 20} more`));
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
// ── Spec 60 R1 — test coverage (reporting, not detection) ─────
|
|
207
|
+
const testCoverage = result.metadata?.testCoverage;
|
|
208
|
+
if (testCoverage) {
|
|
209
|
+
const pct = testCoverage.total > 0
|
|
210
|
+
? ((testCoverage.tested / testCoverage.total) * 100).toFixed(1)
|
|
211
|
+
: '0.0';
|
|
212
|
+
let line = `tested ${testCoverage.tested}/${testCoverage.total} (${pct}%)` +
|
|
213
|
+
` · untested-live ${testCoverage.untestedLive}` +
|
|
214
|
+
` · untested-dead ${testCoverage.untestedDead}`;
|
|
215
|
+
if (testCoverage.deadDrop > 0) {
|
|
216
|
+
line += ` (was ${testCoverage.deadPreException} pre-exception; −${testCoverage.deadDrop} entry points exempted)`;
|
|
217
|
+
}
|
|
218
|
+
console.log(chalk.gray(`── Test coverage ── ${line}`));
|
|
219
|
+
}
|
|
220
|
+
// ── Spec 60 R2 — size distributions (median / p95 / max) ──────
|
|
221
|
+
const sizeDistributions = result.metadata?.sizeDistributions;
|
|
222
|
+
if (sizeDistributions && sizeDistributions.length > 0) {
|
|
223
|
+
console.log(chalk.gray(`── Size distributions ──`));
|
|
224
|
+
for (const d of sizeDistributions) {
|
|
225
|
+
const fmt = (n) => Number.isInteger(n) ? String(n) : n.toFixed(1);
|
|
226
|
+
const tail = d.maxEntry
|
|
227
|
+
? ` (${d.maxEntry.name}${d.maxEntry.fileType ? ` · ${d.maxEntry.fileType}` : ''} · ${d.maxEntry.entityType})`
|
|
228
|
+
: '';
|
|
229
|
+
const pop = d.population ? `${d.population}, n=${d.count}` : `n=${d.count}`;
|
|
230
|
+
console.log(chalk.gray(` ${d.measure} [${pop}]: median ${fmt(d.median)} · p95 ${fmt(d.p95)} · max ${fmt(d.max)}${tail}`));
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
// ── Spec 60 R3 — dead-and-duplicated clusters ─────────────────
|
|
234
|
+
const deadClusters = result.metadata?.deadClusters;
|
|
235
|
+
if (deadClusters && deadClusters.length > 0) {
|
|
236
|
+
console.log(chalk.gray(`── Dead-and-duplicated ── ${deadClusters.length} clusters`));
|
|
237
|
+
for (const c of deadClusters.slice(0, 10)) {
|
|
238
|
+
console.log(chalk.gray(` ${c.count} × ${c.basename}`));
|
|
239
|
+
}
|
|
240
|
+
if (deadClusters.length > 10) {
|
|
241
|
+
console.log(chalk.gray(` … and ${deadClusters.length - 10} more clusters`));
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Print the findings summary: the delta view when a baseline exists, or the
|
|
247
|
+
* absolute summary otherwise. Recomputes the dismissed/gating suffixes so the
|
|
248
|
+
* summary is self-contained.
|
|
249
|
+
*/
|
|
250
|
+
function printFindingSummary(result, violations, baseline, options) {
|
|
251
|
+
// Spec 57 — dismissed count is reported alongside the total, never
|
|
252
|
+
// subtracted from it ("43 findings, 3 dismissed").
|
|
253
|
+
const dismissedCount = result.summary.dismissed ?? 0;
|
|
254
|
+
const dismissedSuffix = dismissedCount > 0 ? `, ${dismissedCount} dismissed` : '';
|
|
255
|
+
const gateExcludedCount = violations.filter((v) => v.gateExcluded).length;
|
|
256
|
+
const gatingCount = (severity) => violations.filter((v) => v.severity === severity && !v.gateExcluded && !v.dismissed).length;
|
|
257
|
+
const findingsSuffix = `${dismissedSuffix}${gateExcludedCount > 0 ? ` (${gateExcludedCount.toLocaleString()} excluded from gate)` : ''}`;
|
|
258
|
+
const summaryLine = (label, total, severity) => gateExcludedCount > 0
|
|
259
|
+
? `${label}: ${total} (${gatingCount(severity)} gating)`
|
|
260
|
+
: `${label}: ${total}`;
|
|
261
|
+
// ── Delta output (Spec 18 R2) ─────────────────────────────────
|
|
262
|
+
if (baseline && !options.full) {
|
|
263
|
+
const newViolations = violations.filter((v) => v.new === true);
|
|
264
|
+
const knownCount = baseline.knownCount ?? 0;
|
|
265
|
+
const fixedCount = baseline.fixedCount ?? 0;
|
|
266
|
+
const previousKnown = baseline.previousKnownCount ?? 0;
|
|
267
|
+
const currentDebt = newViolations.length + knownCount;
|
|
268
|
+
const debtDelta = currentDebt - previousKnown;
|
|
269
|
+
const trendIcon = debtDelta > 0 ? '↑' : debtDelta < 0 ? '↓' : '→';
|
|
270
|
+
const trendLabel = debtDelta > 0
|
|
271
|
+
? `(debt increased since last baseline)`
|
|
272
|
+
: debtDelta < 0
|
|
273
|
+
? `(debt decreased since last baseline)`
|
|
274
|
+
: '(unchanged)';
|
|
275
|
+
console.log(`\n📊 Delta: +${newViolations.length} new · −${fixedCount} fixed · ${knownCount} known ${trendIcon} ${trendLabel}`);
|
|
276
|
+
if (newViolations.length > 0) {
|
|
277
|
+
console.log(chalk.gray(`\n── New readings (${newViolations.length}) ──────────────────────────`));
|
|
278
|
+
for (const v of newViolations) {
|
|
279
|
+
const icon = v.severity === 'critical' ? '🔴' :
|
|
280
|
+
v.severity === 'severe' ? '🟠' : '🟡';
|
|
281
|
+
console.log(`${icon} ${chalk.bold(v.file)}${lineSuffix(v.line)} [${v.severity}] ${v.message}`);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
else {
|
|
285
|
+
console.log(chalk.green('\n✓ No new readings since last baseline.'));
|
|
286
|
+
if (knownCount > 0) {
|
|
287
|
+
console.log(chalk.gray(` ${knownCount} known reading(s) are still open — recorded, not resolved.`));
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
// Debt by analyzer
|
|
291
|
+
console.log(chalk.gray(`\n── Debt by Analyzer ──────────────────────────`));
|
|
292
|
+
const analyzerCounts = {};
|
|
293
|
+
for (const v of violations) {
|
|
294
|
+
const a = v.analyzer || 'unknown';
|
|
295
|
+
if (!analyzerCounts[a])
|
|
296
|
+
analyzerCounts[a] = { known: 0, new: 0 };
|
|
297
|
+
if (v.new === false)
|
|
298
|
+
analyzerCounts[a].known++;
|
|
299
|
+
else if (v.new === true)
|
|
300
|
+
analyzerCounts[a].new++;
|
|
301
|
+
}
|
|
302
|
+
for (const [analyzer, counts] of Object.entries(analyzerCounts).sort()) {
|
|
303
|
+
const newPart = counts.new > 0 ? ` (+${counts.new})` : '';
|
|
304
|
+
console.log(`${analyzer}: ${counts.known.toLocaleString()} known${newPart}`);
|
|
305
|
+
}
|
|
306
|
+
// Top files
|
|
307
|
+
console.log(chalk.gray(`\n── Top Files ─────────────────────────────────`));
|
|
308
|
+
const fileCounts = new Map();
|
|
309
|
+
for (const v of violations) {
|
|
310
|
+
const f = v.file || '';
|
|
311
|
+
fileCounts.set(f, (fileCounts.get(f) || 0) + 1);
|
|
312
|
+
}
|
|
313
|
+
const topFiles = [...fileCounts.entries()]
|
|
314
|
+
.sort((a, b) => b[1] - a[1])
|
|
315
|
+
.slice(0, 5);
|
|
316
|
+
for (const [file, count] of topFiles) {
|
|
317
|
+
console.log(`${file} — ${count} reading${count !== 1 ? 's' : ''}`);
|
|
318
|
+
}
|
|
319
|
+
console.log(chalk.gray(`\n💡 Run ${chalk.cyan('code-audit --full')} to see all ${currentDebt.toLocaleString()} readings.`));
|
|
320
|
+
}
|
|
321
|
+
else if (!baseline) {
|
|
322
|
+
// No baseline: current behavior + hint
|
|
323
|
+
console.log(`\nFound ${result.summary.totalViolations} findings${findingsSuffix}`);
|
|
324
|
+
console.log(summaryLine('Critical', result.summary.criticalIssues, 'critical'));
|
|
325
|
+
console.log(summaryLine('Severe', result.summary.severe, 'severe'));
|
|
326
|
+
console.log(summaryLine('High', result.summary.high, 'high'));
|
|
327
|
+
console.log(chalk.gray(`\nEvery reading is a defect — severity is urgency, the order to act.`));
|
|
328
|
+
console.log(chalk.gray(`\n💡 Run ${chalk.cyan('code-audit baseline')} to adopt the ratchet and track changes over time.`));
|
|
329
|
+
}
|
|
330
|
+
else {
|
|
331
|
+
// --full with baseline: full itemized inventory (current behavior)
|
|
332
|
+
console.log(`\nFound ${result.summary.totalViolations} findings${findingsSuffix}`);
|
|
333
|
+
console.log(summaryLine('Critical', result.summary.criticalIssues, 'critical'));
|
|
334
|
+
console.log(summaryLine('Severe', result.summary.severe, 'severe'));
|
|
335
|
+
console.log(summaryLine('High', result.summary.high, 'high'));
|
|
336
|
+
console.log(chalk.gray(`\nEvery reading is a defect — severity is urgency, the order to act.`));
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
/**
|
|
340
|
+
* Print unparsed-file failures and skipped-extension notice.
|
|
341
|
+
*/
|
|
342
|
+
function printUnparsedAndSkipped(result) {
|
|
343
|
+
// Spec 32 — unparsed files are never silent.
|
|
344
|
+
const unparsedFiles = result.metadata?.unparsedFiles ?? [];
|
|
345
|
+
if (unparsedFiles.length > 0) {
|
|
346
|
+
console.error(chalk.red(`\n⚠️ ${unparsedFiles.length} file${unparsedFiles.length !== 1 ? 's' : ''} failed to parse:`));
|
|
347
|
+
for (const u of unparsedFiles.slice(0, 20)) {
|
|
348
|
+
console.error(` ${u.filePath} — ${u.reason}`);
|
|
349
|
+
}
|
|
350
|
+
if (unparsedFiles.length > 20) {
|
|
351
|
+
console.error(` … and ${unparsedFiles.length - 20} more`);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
// Spec 43 R5 follow-up — extensions present on disk that discovery skipped.
|
|
355
|
+
const skippedExtensions = result.metadata?.skippedExtensions ?? [];
|
|
356
|
+
if (skippedExtensions.length > 0) {
|
|
357
|
+
console.log(chalk.gray(`\n── Not analyzed (skipped extensions) ────────`));
|
|
358
|
+
const parts = skippedExtensions.map(s => `${s.ext} (${s.count})`);
|
|
359
|
+
console.log(` ${parts.join(', ')}`);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* Print per-analyzer pipeline activity and the detailed coverage breakdown.
|
|
364
|
+
*/
|
|
365
|
+
function printPipelineStages(result, options) {
|
|
366
|
+
if (options.json)
|
|
367
|
+
return;
|
|
368
|
+
const analyzerFiles = [];
|
|
369
|
+
for (const [name, ar] of Object.entries(result.analyzerResults)) {
|
|
370
|
+
const vCount = ar.violations.length;
|
|
371
|
+
if (isVisitorStatus(ar.status)) {
|
|
372
|
+
analyzerFiles.push(`${name} [visitor-ran]: ${getFilesProcessed(ar.status)} files, ${vCount} violations`);
|
|
373
|
+
}
|
|
374
|
+
else if (isReducerStatus(ar.status)) {
|
|
375
|
+
analyzerFiles.push(`${name} [reducer-ran]: ${getFactsConsumed(ar.status)} facts, ${vCount} violations`);
|
|
376
|
+
}
|
|
377
|
+
else {
|
|
378
|
+
analyzerFiles.push(`${name} [notRun]: ${vCount} violations`);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
if (analyzerFiles.length > 0) {
|
|
382
|
+
console.log(chalk.gray(`\n── Pipeline Stages ──────────────────────────`));
|
|
383
|
+
console.log(analyzerFiles.join('\n'));
|
|
384
|
+
}
|
|
385
|
+
// ── Coverage (Spec 27) ──────────────────────────────────────────
|
|
386
|
+
const coverage = result.metadata?.coverage;
|
|
387
|
+
if (coverage && coverage.length > 0) {
|
|
388
|
+
const fired = coverage.filter(c => c.state === 'fired');
|
|
389
|
+
const clean = coverage.filter(c => c.state === 'clean');
|
|
390
|
+
const incomplete = coverage.filter(c => c.state === 'incomplete');
|
|
391
|
+
const notApplicable = coverage.filter(c => c.state === 'notApplicable');
|
|
392
|
+
const cannotFire = coverage.filter(c => c.state === 'cannot-fire');
|
|
393
|
+
const firedCount = fired.reduce((s, c) => s + c.count, 0);
|
|
394
|
+
console.log(chalk.gray(`\n── Coverage ─────────────────────────────────`));
|
|
395
|
+
console.log(` ${fired.length} fired (${firedCount.toLocaleString()} violations), ` +
|
|
396
|
+
`${clean.length} clean, ` +
|
|
397
|
+
`${incomplete.length} incomplete, ` +
|
|
398
|
+
`${notApplicable.length} notApplicable, ` +
|
|
399
|
+
`${cannotFire.length} cannot-fire ` +
|
|
400
|
+
`(${coverage.length} rules registered)`);
|
|
401
|
+
if (cannotFire.length > 0) {
|
|
402
|
+
console.log(chalk.yellow(` ── Cannot Fire (broken in the tool) ──`));
|
|
403
|
+
for (const c of cannotFire) {
|
|
404
|
+
console.log(` ${c.ruleId}: ${c.reason ?? 'unknown'}`);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
if (notApplicable.length > 0) {
|
|
408
|
+
console.log(chalk.gray(` ── Not Applicable ──`));
|
|
409
|
+
for (const c of notApplicable) {
|
|
410
|
+
console.log(` ${c.ruleId}: ${c.reason ?? 'unknown'}`);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
if (clean.length > 0) {
|
|
414
|
+
console.log(chalk.gray(` ── Clean ──`));
|
|
415
|
+
for (const c of clean) {
|
|
416
|
+
console.log(` ${c.ruleId} (0)`);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
if (incomplete.length > 0) {
|
|
420
|
+
console.log(chalk.gray(` ── Incomplete (missing facts for some files) ──`));
|
|
421
|
+
for (const c of incomplete) {
|
|
422
|
+
console.log(` ${c.ruleId} (0)`);
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
/**
|
|
428
|
+
* Generate and write the formatted report when --format is specified.
|
|
429
|
+
*/
|
|
430
|
+
async function writeReport(result, options) {
|
|
431
|
+
if (!options.format)
|
|
432
|
+
return;
|
|
433
|
+
const validFormats = ['html', 'json', 'csv', 'sarif'];
|
|
434
|
+
if (!validFormats.includes(options.format)) {
|
|
435
|
+
console.error(chalk.red(`Unknown format: "${options.format}". Must be one of: ${validFormats.join(', ')}`));
|
|
436
|
+
process.exit(1);
|
|
437
|
+
}
|
|
438
|
+
const { generateReport } = await import('./reporting/reportGenerator.js');
|
|
439
|
+
const { readVersionControlProvenance } = await import('./reporting/sarifReportGenerator.js');
|
|
440
|
+
const rootDir = resolve(options.path || process.cwd());
|
|
441
|
+
const config = options.format === 'sarif'
|
|
442
|
+
? { rootDir, ...readVersionControlProvenance(rootDir) }
|
|
443
|
+
: { rootDir };
|
|
444
|
+
const report = generateReport(result, options.format, config);
|
|
445
|
+
const ext = options.format === 'sarif' ? 'sarif' : options.format;
|
|
446
|
+
if (options.output) {
|
|
447
|
+
const outputDir = resolve(options.output);
|
|
448
|
+
const reportPath = join(outputDir, `audit-report.${ext}`);
|
|
449
|
+
let exists = false;
|
|
450
|
+
try {
|
|
451
|
+
await fs.access(reportPath);
|
|
452
|
+
exists = true;
|
|
453
|
+
}
|
|
454
|
+
catch { /* ok */ }
|
|
455
|
+
if (exists && !options.overwrite) {
|
|
456
|
+
console.error(chalk.red(`Refusing to overwrite existing report ${reportPath} — pass --overwrite to replace it.`));
|
|
457
|
+
process.exit(1);
|
|
458
|
+
}
|
|
459
|
+
await fs.mkdir(dirname(reportPath), { recursive: true });
|
|
460
|
+
await fs.writeFile(reportPath, report, 'utf-8');
|
|
461
|
+
console.log(chalk.green(`\nReport written to ${reportPath}`));
|
|
462
|
+
}
|
|
463
|
+
else {
|
|
464
|
+
// No output path — default to stdout.
|
|
465
|
+
process.stdout.write(report + '\n');
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
/**
|
|
469
|
+
* Apply the exit-code gates: fail-on-regression, zero-files, parse-failure,
|
|
470
|
+
* and --fail-on severity thresholds.
|
|
471
|
+
*/
|
|
472
|
+
function applyFailOnGates(result, violations, baseline, options, failOnSeverity) {
|
|
473
|
+
// --fail-on-regression: compare total high debt to baseline snapshot
|
|
474
|
+
if (baseline && options.failOnRegression) {
|
|
475
|
+
const currentDebt = violations.filter((v) => v.new || v.new === false).length;
|
|
476
|
+
const snapshotDebt = baseline.previousKnownCount ?? 0;
|
|
477
|
+
if (currentDebt > snapshotDebt) {
|
|
478
|
+
console.error(chalk.red(`Debt regression: ${currentDebt - snapshotDebt} readings added without re-baselining.`));
|
|
479
|
+
process.exit(2);
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
// Pipeline-failure gate — the audit pipeline itself threw (schema drift,
|
|
483
|
+
// DB write failure, …). One error, reported once by the runner; this exits
|
|
484
|
+
// non-zero (broken tool) so a swallowed pipeline throw can never read as a
|
|
485
|
+
// clean pass.
|
|
486
|
+
{
|
|
487
|
+
const diagnostics = result.metadata?.diagnostics ?? [];
|
|
488
|
+
const pipelineErrors = diagnostics.filter((d) => d.kind === 'pipeline-error');
|
|
489
|
+
if (pipelineErrors.length > 0) {
|
|
490
|
+
console.error(`Pipeline failure: ${pipelineErrors[0].message}`);
|
|
491
|
+
process.exit(1);
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
// Zero-files gate — any enabled analyzer matching zero source files is a
|
|
495
|
+
// dark-analyzer failure; fail the run so the bug can't hide.
|
|
496
|
+
{
|
|
497
|
+
const diagnostics = result.metadata?.diagnostics ?? [];
|
|
498
|
+
const zeroFileWarnings = diagnostics.filter((d) => d.kind === 'zero-files');
|
|
499
|
+
if (zeroFileWarnings.length > 0) {
|
|
500
|
+
const names = zeroFileWarnings.map((d) => d.analyzerName ?? d.analyzer).join(', ');
|
|
501
|
+
console.error(`Zero-files failure: ${zeroFileWarnings.length} analyzer(s) matched zero source files (${names})`);
|
|
502
|
+
process.exit(2);
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
// Spec 32 — a run where any file failed to parse is incomplete and must
|
|
506
|
+
// not exit 0.
|
|
507
|
+
{
|
|
508
|
+
const unparsedFiles = result.metadata?.unparsedFiles ?? [];
|
|
509
|
+
if (unparsedFiles.length > 0) {
|
|
510
|
+
console.error(`Parse-failure: ${unparsedFiles.length} file(s) could not be parsed; analysis is incomplete.`);
|
|
511
|
+
process.exit(2);
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
// --fail-on: evaluate new + invariant findings only (unless --include-baseline)
|
|
515
|
+
if (failOnSeverity) {
|
|
516
|
+
const evaluableViolations = (baseline && !options.includeBaseline)
|
|
517
|
+
? violations.filter((v) => v.new || v.analyzer === 'invariants')
|
|
518
|
+
: violations;
|
|
519
|
+
const severityOrder = SEVERITIES;
|
|
520
|
+
const failIndex = severityOrder.indexOf(failOnSeverity);
|
|
521
|
+
const hasAtOrAbove = evaluableViolations.some((v) => {
|
|
522
|
+
// Spec 57 — a dismissed finding never blocks the gate.
|
|
523
|
+
if (v.dismissed)
|
|
524
|
+
return false;
|
|
525
|
+
// Spec 36 R4 — a finding from a path-profile-excluded file never blocks.
|
|
526
|
+
if (v.gateExcluded)
|
|
527
|
+
return false;
|
|
528
|
+
const vIndex = severityOrder.indexOf(v.severity);
|
|
529
|
+
return vIndex >= 0 && vIndex <= failIndex;
|
|
530
|
+
});
|
|
531
|
+
if (hasAtOrAbove) {
|
|
532
|
+
process.exit(2);
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
}
|
|
91
536
|
// Legacy audit command (default behavior)
|
|
92
537
|
program
|
|
93
538
|
.command('audit', { isDefault: true })
|
|
94
539
|
.description('Run code quality audit (default command)')
|
|
95
540
|
.option('-p, --path <path>', 'Path to audit', process.cwd())
|
|
96
|
-
.option('-c, --config <config>',
|
|
541
|
+
.option('-c, --config <config>', OPTION_CONFIGURATION_NAME)
|
|
97
542
|
.option('-o, --output <dir>', 'Output directory for reports')
|
|
98
543
|
.option('-f, --format <format>', 'Report format: html, json, csv, or sarif')
|
|
99
544
|
.option('--overwrite', 'Overwrite an existing report file at --output (default: refuse)')
|
|
@@ -103,9 +548,6 @@ program
|
|
|
103
548
|
.option('--fail-on-regression', 'Exit code 2 when total high debt exceeds the baseline snapshot')
|
|
104
549
|
.option('--preset <id>', 'Apply a shareable preset (repeatable)', (v, prev) => prev.concat([v]), [])
|
|
105
550
|
.option('--detach', 'Run the audit in a detached background process and print the job ID')
|
|
106
|
-
.option('--partition-strategy <strategy>', 'Partition strategy for detached runs: none, auto, or top-level')
|
|
107
|
-
.option('--max-partitions <n>', 'Maximum number of partition shards (detached runs)')
|
|
108
|
-
.option('--shard-timeout-ms <ms>', 'Per-shard timeout in milliseconds (detached runs)')
|
|
109
551
|
.option('--explain-skipped', 'Print a per-reason breakdown of files dropped from analysis')
|
|
110
552
|
.action(async (options) => {
|
|
111
553
|
console.log(chalk.blue('🔍 Code Quality Audit Tool'));
|
|
@@ -138,302 +580,20 @@ program
|
|
|
138
580
|
}
|
|
139
581
|
}
|
|
140
582
|
}
|
|
141
|
-
const result = await
|
|
583
|
+
const result = await createAuditRunner({
|
|
142
584
|
projectRoot: options.path,
|
|
143
585
|
configName: options.config,
|
|
144
586
|
outputDirectory: options.output,
|
|
145
587
|
presets: presetIds
|
|
146
|
-
});
|
|
588
|
+
}).run();
|
|
147
589
|
const violations = Object.values(result.analyzerResults).flatMap((r) => r.violations || []);
|
|
148
590
|
const baseline = result.metadata?.baseline;
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
const dismissedCount = result.summary.dismissed ?? 0;
|
|
152
|
-
const dismissedSuffix = dismissedCount > 0 ? `, ${dismissedCount} dismissed` : '';
|
|
153
|
-
// Spec 36 R4 — a path-profile-excluded finding reports at its real
|
|
154
|
-
// severity but never gates. When any finding is gate-excluded, surface
|
|
155
|
-
// the gating count next to the total so a clean exit doesn't read as
|
|
156
|
-
// "0 findings" beside a non-zero critical count (e.g. knex: 38 critical,
|
|
157
|
-
// all test-dir, exit 0). The excluded total still reads in full — the
|
|
158
|
-
// distinction is gating vs. reporting, never subtraction.
|
|
159
|
-
const gateExcludedCount = violations.filter((v) => v.gateExcluded).length;
|
|
160
|
-
const gatingCount = (severity) => violations.filter((v) => v.severity === severity && !v.gateExcluded && !v.dismissed).length;
|
|
161
|
-
const findingsSuffix = `${dismissedSuffix}${gateExcludedCount > 0 ? ` (${gateExcludedCount.toLocaleString()} excluded from gate)` : ''}`;
|
|
162
|
-
const summaryLine = (label, total, severity) => gateExcludedCount > 0
|
|
163
|
-
? `${label}: ${total} (${gatingCount(severity)} gating)`
|
|
164
|
-
: `${label}: ${total}`;
|
|
165
|
-
// ── Coverage panel leads the report (Spec 47 R2) ─────────────
|
|
166
|
-
// A diagnostic report opens with what was measured before it lists any
|
|
167
|
-
// readings, so a zero-reading report can't be mistaken for a clean tree.
|
|
168
|
-
const coverage = result.metadata?.coverage;
|
|
169
|
-
if (coverage && coverage.length > 0) {
|
|
170
|
-
const covFired = coverage.filter(c => c.state === 'fired').length;
|
|
171
|
-
const covClean = coverage.filter(c => c.state === 'clean').length;
|
|
172
|
-
const covUnassessed = coverage.filter(c => c.state === 'unassessed').length;
|
|
173
|
-
const covNotApplicable = coverage.filter(c => c.state === 'notApplicable').length;
|
|
174
|
-
const covCannotFire = coverage.filter(c => c.state === 'cannot-fire').length;
|
|
175
|
-
console.log(chalk.gray(`── Coverage panel ── ${covFired} fired · ${covClean} clean · ` +
|
|
176
|
-
`${covUnassessed} unassessed · ${covNotApplicable} not-applicable · ` +
|
|
177
|
-
`${covCannotFire} cannot-fire`));
|
|
178
|
-
}
|
|
179
|
-
// ── Skipped analyzers (Spec 61) ──────────────────────────────
|
|
180
|
-
// A language present on disk but never analyzed is a stated gap, not a
|
|
181
|
-
// silent zero. The Go path surfaces a named runtime failure (missing
|
|
182
|
-
// toolchain, wrong-arch prebuilt binary, failed rebuild) as a
|
|
183
|
-
// metadata.diagnostics entry; before this section those kinds were
|
|
184
|
-
// invisible outside --json, so a polyglot repo with .go files and no Go
|
|
185
|
-
// toolchain printed zero Go findings and exited clean — the silent-zero
|
|
186
|
-
// failure mode. These lead above coverage gaps: a skipped analyzer is a
|
|
187
|
-
// whole language unmeasured, more severe than a per-file visibility gap.
|
|
188
|
-
const skippedAnalyzers = (result.metadata?.diagnostics ?? []).filter((d) => d.kind === 'go-toolchain-missing' ||
|
|
189
|
-
d.kind === 'go-analyzer-wrong-arch' ||
|
|
190
|
-
d.kind === 'go-analyzer-build-failed');
|
|
191
|
-
if (skippedAnalyzers.length > 0) {
|
|
192
|
-
console.log(chalk.yellow(`── Skipped analyzers ── ${skippedAnalyzers.length}`));
|
|
193
|
-
for (const d of skippedAnalyzers) {
|
|
194
|
-
const name = d.analyzerName || 'go';
|
|
195
|
-
console.log(chalk.yellow(` ${name} [${d.kind}] — ${d.message}`));
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
// ── Ignored config keys (Spec 61 R1.4) ─────────────────────────
|
|
199
|
-
// Keys dropped by sanitizeProjectFileConfig: undeclared keys (like
|
|
200
|
-
// `scope`, which previously reached execSync via mergeConfig's raw-key
|
|
201
|
-
// iteration) and path keys resolving outside the project root. Surfaced
|
|
202
|
-
// so a silently-ignored config never reads as "loaded fine".
|
|
203
|
-
const ignoredConfigKeys = (result.metadata?.diagnostics ?? []).filter((d) => d.kind === 'config-key-rejected');
|
|
204
|
-
if (ignoredConfigKeys.length > 0) {
|
|
205
|
-
console.log(chalk.yellow(`── Ignored config keys ── ${ignoredConfigKeys.length}`));
|
|
206
|
-
for (const d of ignoredConfigKeys) {
|
|
207
|
-
console.log(chalk.yellow(` ${d.message}`));
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
// ── Coverage gaps (Spec 58 follow-up) ─────────────────────────
|
|
211
|
-
// Coverage diagnostics are the analyzer saying "my visibility ends here",
|
|
212
|
-
// not "the code is wrong". They lead with counts, then per-occurrence
|
|
213
|
-
// file:line — visible and counted, never blocking (they never reach the
|
|
214
|
-
// gate). Rendered alongside the coverage panel that leads the report.
|
|
215
|
-
const coverageDiagnostics = (result.metadata?.diagnostics ?? []).filter((d) => d.kind === 'unresolved-query' || d.kind === 'unresolved-dynamic-import' || d.kind === 'undefined-class-not-found' || d.kind === 'cannot-fire');
|
|
216
|
-
if (coverageDiagnostics.length > 0) {
|
|
217
|
-
const byKind = {};
|
|
218
|
-
for (const d of coverageDiagnostics)
|
|
219
|
-
byKind[d.kind] = (byKind[d.kind] ?? 0) + 1;
|
|
220
|
-
const totals = Object.entries(byKind)
|
|
221
|
-
.map(([kind, n]) => `${n} ${kind}`)
|
|
222
|
-
.join(' · ');
|
|
223
|
-
console.log(chalk.gray(`── Coverage gaps ── ${totals}`));
|
|
224
|
-
for (const d of coverageDiagnostics.slice(0, 20)) {
|
|
225
|
-
const loc = d.file ? `${d.file}${typeof d.line === 'number' ? `:${d.line}` : ''}` : '(unknown)';
|
|
226
|
-
console.log(chalk.gray(` ${loc} [${d.kind}] — ${d.message}`));
|
|
227
|
-
}
|
|
228
|
-
if (coverageDiagnostics.length > 20) {
|
|
229
|
-
console.log(chalk.gray(` … and ${coverageDiagnostics.length - 20} more`));
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
// ── Spec 60 R1 — test coverage (reporting, not detection) ─────
|
|
233
|
-
// Classifies every non-test module as tested / untested-live /
|
|
234
|
-
// untested-dead off the file-level import edges. The dead count is
|
|
235
|
-
// post-entry-point-exception, with the drop attributed.
|
|
236
|
-
const testCoverage = result.metadata?.testCoverage;
|
|
237
|
-
if (testCoverage) {
|
|
238
|
-
const pct = testCoverage.total > 0
|
|
239
|
-
? ((testCoverage.tested / testCoverage.total) * 100).toFixed(1)
|
|
240
|
-
: '0.0';
|
|
241
|
-
let line = `tested ${testCoverage.tested}/${testCoverage.total} (${pct}%)` +
|
|
242
|
-
` · untested-live ${testCoverage.untestedLive}` +
|
|
243
|
-
` · untested-dead ${testCoverage.untestedDead}`;
|
|
244
|
-
if (testCoverage.deadDrop > 0) {
|
|
245
|
-
line += ` (was ${testCoverage.deadPreException} pre-exception; −${testCoverage.deadDrop} entry points exempted)`;
|
|
246
|
-
}
|
|
247
|
-
console.log(chalk.gray(`── Test coverage ── ${line}`));
|
|
248
|
-
}
|
|
249
|
-
// ── Spec 60 R2 — size distributions (median / p95 / max) ──────
|
|
250
|
-
const sizeDistributions = result.metadata?.sizeDistributions;
|
|
251
|
-
if (sizeDistributions && sizeDistributions.length > 0) {
|
|
252
|
-
console.log(chalk.gray(`── Size distributions ──`));
|
|
253
|
-
for (const d of sizeDistributions) {
|
|
254
|
-
const fmt = (n) => Number.isInteger(n) ? String(n) : n.toFixed(1);
|
|
255
|
-
const tail = d.maxEntry
|
|
256
|
-
? ` (${d.maxEntry.name}${d.maxEntry.fileType ? ` · ${d.maxEntry.fileType}` : ''} · ${d.maxEntry.entityType})`
|
|
257
|
-
: '';
|
|
258
|
-
const pop = d.population ? `${d.population}, n=${d.count}` : `n=${d.count}`;
|
|
259
|
-
console.log(chalk.gray(` ${d.measure} [${pop}]: median ${fmt(d.median)} · p95 ${fmt(d.p95)} · max ${fmt(d.max)}${tail}`));
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
// ── Spec 60 R3 — dead-and-duplicated clusters ─────────────────
|
|
263
|
-
const deadClusters = result.metadata?.deadClusters;
|
|
264
|
-
if (deadClusters && deadClusters.length > 0) {
|
|
265
|
-
console.log(chalk.gray(`── Dead-and-duplicated ── ${deadClusters.length} clusters`));
|
|
266
|
-
for (const c of deadClusters.slice(0, 10)) {
|
|
267
|
-
console.log(chalk.gray(` ${c.count} × ${c.basename}`));
|
|
268
|
-
}
|
|
269
|
-
if (deadClusters.length > 10) {
|
|
270
|
-
console.log(chalk.gray(` … and ${deadClusters.length - 10} more clusters`));
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
// ── Delta output (Spec 18 R2) ─────────────────────────────────
|
|
274
|
-
if (baseline && !options.full) {
|
|
275
|
-
const newViolations = violations.filter((v) => v.new === true);
|
|
276
|
-
const knownCount = baseline.knownCount ?? 0;
|
|
277
|
-
const fixedCount = baseline.fixedCount ?? 0;
|
|
278
|
-
const previousKnown = baseline.previousKnownCount ?? 0;
|
|
279
|
-
const currentDebt = newViolations.length + knownCount;
|
|
280
|
-
const debtDelta = currentDebt - previousKnown;
|
|
281
|
-
const trendIcon = debtDelta > 0 ? '↑' : debtDelta < 0 ? '↓' : '→';
|
|
282
|
-
const trendLabel = debtDelta > 0
|
|
283
|
-
? `(debt increased since last baseline)`
|
|
284
|
-
: debtDelta < 0
|
|
285
|
-
? `(debt decreased since last baseline)`
|
|
286
|
-
: '(unchanged)';
|
|
287
|
-
console.log(`\n📊 Delta: +${newViolations.length} new · −${fixedCount} fixed · ${knownCount} known ${trendIcon} ${trendLabel}`);
|
|
288
|
-
if (newViolations.length > 0) {
|
|
289
|
-
console.log(chalk.gray(`\n── New readings (${newViolations.length}) ──────────────────────────`));
|
|
290
|
-
for (const v of newViolations) {
|
|
291
|
-
const icon = v.severity === 'critical' ? '🔴' :
|
|
292
|
-
v.severity === 'severe' ? '🟠' : '🟡';
|
|
293
|
-
console.log(`${icon} ${chalk.bold(v.file)}${v.line ? `:${v.line}` : ''} [${v.severity}] ${v.message}`);
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
else {
|
|
297
|
-
console.log(chalk.green('\n✓ No new readings since last baseline.'));
|
|
298
|
-
if (knownCount > 0) {
|
|
299
|
-
console.log(chalk.gray(` ${knownCount} known reading(s) are still open — recorded, not resolved.`));
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
// Debt by analyzer
|
|
303
|
-
console.log(chalk.gray(`\n── Debt by Analyzer ──────────────────────────`));
|
|
304
|
-
const analyzerCounts = {};
|
|
305
|
-
for (const v of violations) {
|
|
306
|
-
const a = v.analyzer || 'unknown';
|
|
307
|
-
if (!analyzerCounts[a])
|
|
308
|
-
analyzerCounts[a] = { known: 0, new: 0 };
|
|
309
|
-
if (v.new === false)
|
|
310
|
-
analyzerCounts[a].known++;
|
|
311
|
-
else if (v.new === true)
|
|
312
|
-
analyzerCounts[a].new++;
|
|
313
|
-
}
|
|
314
|
-
for (const [analyzer, counts] of Object.entries(analyzerCounts).sort()) {
|
|
315
|
-
const newPart = counts.new > 0 ? ` (+${counts.new})` : '';
|
|
316
|
-
console.log(`${analyzer}: ${counts.known.toLocaleString()} known${newPart}`);
|
|
317
|
-
}
|
|
318
|
-
// Top files
|
|
319
|
-
console.log(chalk.gray(`\n── Top Files ─────────────────────────────────`));
|
|
320
|
-
const fileCounts = new Map();
|
|
321
|
-
for (const v of violations) {
|
|
322
|
-
const f = v.file || '';
|
|
323
|
-
fileCounts.set(f, (fileCounts.get(f) || 0) + 1);
|
|
324
|
-
}
|
|
325
|
-
const topFiles = [...fileCounts.entries()]
|
|
326
|
-
.sort((a, b) => b[1] - a[1])
|
|
327
|
-
.slice(0, 5);
|
|
328
|
-
for (const [file, count] of topFiles) {
|
|
329
|
-
console.log(`${file} — ${count} reading${count !== 1 ? 's' : ''}`);
|
|
330
|
-
}
|
|
331
|
-
console.log(chalk.gray(`\n💡 Run ${chalk.cyan('code-audit --full')} to see all ${currentDebt.toLocaleString()} readings.`));
|
|
332
|
-
}
|
|
333
|
-
else if (!baseline) {
|
|
334
|
-
// No baseline: current behavior + hint
|
|
335
|
-
console.log(`\nFound ${result.summary.totalViolations} findings${findingsSuffix}`);
|
|
336
|
-
console.log(summaryLine('Critical', result.summary.criticalIssues, 'critical'));
|
|
337
|
-
console.log(summaryLine('Severe', result.summary.severe, 'severe'));
|
|
338
|
-
console.log(summaryLine('High', result.summary.high, 'high'));
|
|
339
|
-
console.log(chalk.gray(`\nEvery reading is a defect — severity is urgency, the order to act.`));
|
|
340
|
-
console.log(chalk.gray(`\n💡 Run ${chalk.cyan('code-audit baseline')} to adopt the ratchet and track changes over time.`));
|
|
341
|
-
}
|
|
342
|
-
else {
|
|
343
|
-
// --full with baseline: full itemized inventory (current behavior)
|
|
344
|
-
console.log(`\nFound ${result.summary.totalViolations} findings${findingsSuffix}`);
|
|
345
|
-
console.log(summaryLine('Critical', result.summary.criticalIssues, 'critical'));
|
|
346
|
-
console.log(summaryLine('Severe', result.summary.severe, 'severe'));
|
|
347
|
-
console.log(summaryLine('High', result.summary.high, 'high'));
|
|
348
|
-
console.log(chalk.gray(`\nEvery reading is a defect — severity is urgency, the order to act.`));
|
|
349
|
-
}
|
|
591
|
+
printAuditDiagnostics(result);
|
|
592
|
+
printFindingSummary(result, violations, baseline, options);
|
|
350
593
|
// Spec 44 R4 — file accounting: analyzed/dropped totals + optional breakdown.
|
|
351
594
|
printFileAccounting(result, !!options.explainSkipped);
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
const unparsedFiles = result.metadata?.unparsedFiles ?? [];
|
|
355
|
-
if (unparsedFiles.length > 0) {
|
|
356
|
-
console.error(chalk.red(`\n⚠️ ${unparsedFiles.length} file${unparsedFiles.length !== 1 ? 's' : ''} failed to parse:`));
|
|
357
|
-
for (const u of unparsedFiles.slice(0, 20)) {
|
|
358
|
-
console.error(` ${u.filePath} — ${u.reason}`);
|
|
359
|
-
}
|
|
360
|
-
if (unparsedFiles.length > 20) {
|
|
361
|
-
console.error(` … and ${unparsedFiles.length - 20} more`);
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
// Spec 43 R5 follow-up — extensions present on disk that discovery skipped.
|
|
365
|
-
// Informational: surfaces "what isn't being analyzed here" so an unlisted
|
|
366
|
-
// extension (e.g. `.mdx`) never vanishes silently at discovery. Not an
|
|
367
|
-
// error — a skipped extension is not necessarily a defect (`.md`/`.svg`
|
|
368
|
-
// are legitimately not source), unlike `unparsedFiles` above.
|
|
369
|
-
const skippedExtensions = result.metadata?.skippedExtensions ?? [];
|
|
370
|
-
if (skippedExtensions.length > 0) {
|
|
371
|
-
console.log(chalk.gray(`\n── Not analyzed (skipped extensions) ────────`));
|
|
372
|
-
const parts = skippedExtensions.map(s => `${s.ext} (${s.count})`);
|
|
373
|
-
console.log(` ${parts.join(', ')}`);
|
|
374
|
-
}
|
|
375
|
-
// Per-analyzer activity (read from result data, not serialized summary)
|
|
376
|
-
// — surfaces zero-scan failures that would otherwise be invisible.
|
|
377
|
-
if (!options.json) {
|
|
378
|
-
const analyzerFiles = [];
|
|
379
|
-
for (const [name, ar] of Object.entries(result.analyzerResults)) {
|
|
380
|
-
const vCount = ar.violations.length;
|
|
381
|
-
if (isVisitorStatus(ar.status)) {
|
|
382
|
-
analyzerFiles.push(`${name} [visitor-ran]: ${getFilesProcessed(ar.status)} files, ${vCount} violations`);
|
|
383
|
-
}
|
|
384
|
-
else if (isReducerStatus(ar.status)) {
|
|
385
|
-
analyzerFiles.push(`${name} [reducer-ran]: ${getFactsConsumed(ar.status)} facts, ${vCount} violations`);
|
|
386
|
-
}
|
|
387
|
-
else {
|
|
388
|
-
analyzerFiles.push(`${name} [notRun]: ${vCount} violations`);
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
if (analyzerFiles.length > 0) {
|
|
392
|
-
console.log(chalk.gray(`\n── Pipeline Stages ──────────────────────────`));
|
|
393
|
-
console.log(analyzerFiles.join('\n'));
|
|
394
|
-
}
|
|
395
|
-
// ── Coverage (Spec 27) ──────────────────────────────────────────
|
|
396
|
-
const coverage = result.metadata?.coverage;
|
|
397
|
-
if (coverage && coverage.length > 0) {
|
|
398
|
-
const fired = coverage.filter(c => c.state === 'fired');
|
|
399
|
-
const clean = coverage.filter(c => c.state === 'clean');
|
|
400
|
-
const unassessed = coverage.filter(c => c.state === 'unassessed');
|
|
401
|
-
const notApplicable = coverage.filter(c => c.state === 'notApplicable');
|
|
402
|
-
const cannotFire = coverage.filter(c => c.state === 'cannot-fire');
|
|
403
|
-
const firedCount = fired.reduce((s, c) => s + c.count, 0);
|
|
404
|
-
console.log(chalk.gray(`\n── Coverage ─────────────────────────────────`));
|
|
405
|
-
console.log(` ${fired.length} fired (${firedCount.toLocaleString()} violations), ` +
|
|
406
|
-
`${clean.length} clean, ` +
|
|
407
|
-
`${unassessed.length} unassessed, ` +
|
|
408
|
-
`${notApplicable.length} notApplicable, ` +
|
|
409
|
-
`${cannotFire.length} cannot-fire ` +
|
|
410
|
-
`(${coverage.length} rules registered)`);
|
|
411
|
-
if (cannotFire.length > 0) {
|
|
412
|
-
console.log(chalk.yellow(` ── Cannot Fire (broken in the tool) ──`));
|
|
413
|
-
for (const c of cannotFire) {
|
|
414
|
-
console.log(` ${c.ruleId}: ${c.reason ?? 'unknown'}`);
|
|
415
|
-
}
|
|
416
|
-
}
|
|
417
|
-
if (notApplicable.length > 0) {
|
|
418
|
-
console.log(chalk.gray(` ── Not Applicable ──`));
|
|
419
|
-
for (const c of notApplicable) {
|
|
420
|
-
console.log(` ${c.ruleId}: ${c.reason ?? 'unknown'}`);
|
|
421
|
-
}
|
|
422
|
-
}
|
|
423
|
-
if (clean.length > 0) {
|
|
424
|
-
console.log(chalk.gray(` ── Clean ──`));
|
|
425
|
-
for (const c of clean) {
|
|
426
|
-
console.log(` ${c.ruleId} (0)`);
|
|
427
|
-
}
|
|
428
|
-
}
|
|
429
|
-
if (unassessed.length > 0) {
|
|
430
|
-
console.log(chalk.gray(` ── Unassessed (zero readings, applicability unknown) ──`));
|
|
431
|
-
for (const c of unassessed) {
|
|
432
|
-
console.log(` ${c.ruleId} (0)`);
|
|
433
|
-
}
|
|
434
|
-
}
|
|
435
|
-
}
|
|
436
|
-
}
|
|
595
|
+
printUnparsedAndSkipped(result);
|
|
596
|
+
printPipelineStages(result, options);
|
|
437
597
|
// Spec-20 R4: built-in profile visibility — silent behavior changes
|
|
438
598
|
// are never acceptable. Notify when scripts-and-tests excludes findings
|
|
439
599
|
// from the blocking gate.
|
|
@@ -443,103 +603,8 @@ program
|
|
|
443
603
|
console.log(chalk.blue(`\nℹ️ ${builtinCapped.toLocaleString()} readings excluded from the blocking gate by built-in profile "scripts-and-tests" (scripts/tests/fixtures).`));
|
|
444
604
|
console.log(chalk.gray(` Set ${chalk.cyan('"builtin": false')} in .codeauditor.json to disable.`));
|
|
445
605
|
}
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
const validFormats = ['html', 'json', 'csv', 'sarif'];
|
|
449
|
-
if (!validFormats.includes(options.format)) {
|
|
450
|
-
console.error(chalk.red(`Unknown format: "${options.format}". Must be one of: ${validFormats.join(', ')}`));
|
|
451
|
-
process.exit(1);
|
|
452
|
-
}
|
|
453
|
-
const { generateReport } = await import('./reporting/reportGenerator.js');
|
|
454
|
-
const { readVersionControlProvenance } = await import('./reporting/sarifReportGenerator.js');
|
|
455
|
-
const rootDir = resolve(options.path || process.cwd());
|
|
456
|
-
const config = options.format === 'sarif'
|
|
457
|
-
? { rootDir, ...readVersionControlProvenance(rootDir) }
|
|
458
|
-
: { rootDir };
|
|
459
|
-
const report = generateReport(result, options.format, config);
|
|
460
|
-
const ext = options.format === 'sarif' ? 'sarif' : options.format;
|
|
461
|
-
if (options.output) {
|
|
462
|
-
// Explicit output path — write a file. Refuse to clobber an existing
|
|
463
|
-
// report unless --overwrite is passed. A report write is destructive;
|
|
464
|
-
// a silent overwrite of `audit-report.json` in a consuming repo is the
|
|
465
|
-
// defect this guard removes.
|
|
466
|
-
const outputDir = resolve(options.output);
|
|
467
|
-
const reportPath = join(outputDir, `audit-report.${ext}`);
|
|
468
|
-
let exists = false;
|
|
469
|
-
try {
|
|
470
|
-
await fs.access(reportPath);
|
|
471
|
-
exists = true;
|
|
472
|
-
}
|
|
473
|
-
catch { /* ok */ }
|
|
474
|
-
if (exists && !options.overwrite) {
|
|
475
|
-
console.error(chalk.red(`Refusing to overwrite existing report ${reportPath} — pass --overwrite to replace it.`));
|
|
476
|
-
process.exit(1);
|
|
477
|
-
}
|
|
478
|
-
await fs.mkdir(dirname(reportPath), { recursive: true });
|
|
479
|
-
await fs.writeFile(reportPath, report, 'utf-8');
|
|
480
|
-
console.log(chalk.green(`\nReport written to ${reportPath}`));
|
|
481
|
-
}
|
|
482
|
-
else {
|
|
483
|
-
// No output path — default to stdout. Never write a report into the
|
|
484
|
-
// audited project without an explicit --output on the command line.
|
|
485
|
-
process.stdout.write(report + '\n');
|
|
486
|
-
}
|
|
487
|
-
}
|
|
488
|
-
// ── Fail-on logic (Spec 18 R3) ───────────────────────────────
|
|
489
|
-
// --fail-on-regression: compare total high debt to baseline snapshot
|
|
490
|
-
if (baseline && options.failOnRegression) {
|
|
491
|
-
const currentDebt = violations.filter((v) => v.new || v.new === false).length;
|
|
492
|
-
const snapshotDebt = baseline.previousKnownCount ?? 0;
|
|
493
|
-
if (currentDebt > snapshotDebt) {
|
|
494
|
-
console.error(chalk.red(`Debt regression: ${currentDebt - snapshotDebt} readings added without re-baselining.`));
|
|
495
|
-
process.exit(2);
|
|
496
|
-
}
|
|
497
|
-
}
|
|
498
|
-
// Zero-files gate — any enabled analyzer matching zero source files is a
|
|
499
|
-
// dark-analyzer failure; fail the run so the bug can't hide.
|
|
500
|
-
{
|
|
501
|
-
const diagnostics = result.metadata?.diagnostics ?? [];
|
|
502
|
-
const zeroFileWarnings = diagnostics.filter((d) => d.kind === 'zero-files');
|
|
503
|
-
if (zeroFileWarnings.length > 0) {
|
|
504
|
-
const names = zeroFileWarnings.map((d) => d.analyzerName ?? d.analyzer).join(', ');
|
|
505
|
-
console.error(`Zero-files failure: ${zeroFileWarnings.length} analyzer(s) matched zero source files (${names})`);
|
|
506
|
-
process.exit(2);
|
|
507
|
-
}
|
|
508
|
-
}
|
|
509
|
-
// Spec 32 — a run where any file failed to parse is incomplete and must
|
|
510
|
-
// not exit 0. This mirrors the zero-files gate: a silent dark-analyzer
|
|
511
|
-
// failure at file granularity is a hard failure.
|
|
512
|
-
{
|
|
513
|
-
const unparsedFiles = result.metadata?.unparsedFiles ?? [];
|
|
514
|
-
if (unparsedFiles.length > 0) {
|
|
515
|
-
console.error(`Parse-failure: ${unparsedFiles.length} file(s) could not be parsed; analysis is incomplete.`);
|
|
516
|
-
process.exit(2);
|
|
517
|
-
}
|
|
518
|
-
}
|
|
519
|
-
// --fail-on: evaluate new + invariant findings only (unless --include-baseline)
|
|
520
|
-
if (failOnSeverity) {
|
|
521
|
-
const evaluableViolations = (baseline && !options.includeBaseline)
|
|
522
|
-
? violations.filter((v) => v.new || v.analyzer === 'invariants')
|
|
523
|
-
: violations;
|
|
524
|
-
const severityOrder = SEVERITIES;
|
|
525
|
-
const failIndex = severityOrder.indexOf(failOnSeverity);
|
|
526
|
-
const hasAtOrAbove = evaluableViolations.some((v) => {
|
|
527
|
-
// Spec 57 — a dismissed finding never blocks the gate.
|
|
528
|
-
if (v.dismissed)
|
|
529
|
-
return false;
|
|
530
|
-
// Spec 36 R4 — a finding from a path-profile-excluded file (e.g. a
|
|
531
|
-
// test/script under "scripts-and-tests") never blocks the gate,
|
|
532
|
-
// whatever its severity. The file is doing its job; a hardcoded
|
|
533
|
-
// connection string in a stress-test script is not a defect to block on.
|
|
534
|
-
if (v.gateExcluded)
|
|
535
|
-
return false;
|
|
536
|
-
const vIndex = severityOrder.indexOf(v.severity);
|
|
537
|
-
return vIndex >= 0 && vIndex <= failIndex;
|
|
538
|
-
});
|
|
539
|
-
if (hasAtOrAbove) {
|
|
540
|
-
process.exit(2);
|
|
541
|
-
}
|
|
542
|
-
}
|
|
606
|
+
await writeReport(result, options);
|
|
607
|
+
applyFailOnGates(result, violations, baseline, options, failOnSeverity);
|
|
543
608
|
}
|
|
544
609
|
catch (error) {
|
|
545
610
|
console.error(chalk.red('Error:'), error);
|
|
@@ -556,7 +621,7 @@ program
|
|
|
556
621
|
.option('--fail-on-zero-files', 'Exit code 2 when any enabled analyzer matches zero source files', true)
|
|
557
622
|
.option('--stdin', 'Read file paths from stdin (one per line)')
|
|
558
623
|
.option('--explain-skipped', 'Print a per-reason breakdown of files dropped from analysis')
|
|
559
|
-
.option('-p, --path <projectPath>',
|
|
624
|
+
.option('-p, --path <projectPath>', OPTION_PROJECT_ROOT_PATH, process.cwd())
|
|
560
625
|
.action(async (paths, options) => {
|
|
561
626
|
try {
|
|
562
627
|
const fileSet = new Set();
|
|
@@ -592,11 +657,17 @@ program
|
|
|
592
657
|
// the gate never misses a finding. SARIF needs full run metadata, so it
|
|
593
658
|
// always runs in-process.
|
|
594
659
|
const projectRoot = resolve(options.path || process.cwd());
|
|
660
|
+
const hookMode = process.env.CODE_AUDITOR_HOOK === '1' || process.env.CODE_AUDITOR_HOOK === 'true';
|
|
595
661
|
let result;
|
|
596
662
|
if (resolvedPaths.length > 0 && options.format !== 'sarif') {
|
|
597
|
-
const { resolveDaemon, readDaemonDiagnostics } = await import('./daemon/resolve.js');
|
|
663
|
+
const { resolveDaemon, readDaemonDiagnostics, startDaemonDetached } = await import('./daemon/resolve.js');
|
|
598
664
|
const daemon = await resolveDaemon(projectRoot);
|
|
599
665
|
if (daemon.mode === 'not-ready') {
|
|
666
|
+
// Hook mode (Spec 68 §hook-contract): never block the edit on an audit the
|
|
667
|
+
// daemon can't serve yet — it is still indexing, and the finding arrives on
|
|
668
|
+
// a later edit. Say nothing, exit clean. A non-hook caller gets the status.
|
|
669
|
+
if (hookMode)
|
|
670
|
+
process.exit(0);
|
|
600
671
|
reportDaemonIndexing(daemon.state, !!options.json);
|
|
601
672
|
return;
|
|
602
673
|
}
|
|
@@ -605,14 +676,27 @@ program
|
|
|
605
676
|
if (diag && diag.status === 'ready' && diag.staleFiles.length === 0) {
|
|
606
677
|
result = await buildChangedResultFromDiagnostics(diag.diagnostics, projectRoot);
|
|
607
678
|
}
|
|
679
|
+
else if (hookMode) {
|
|
680
|
+
// A just-edited file whose re-audit hasn't landed is not trustworthy from
|
|
681
|
+
// cache. Report nothing; the finding arrives on the next edit.
|
|
682
|
+
process.exit(0);
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
else if (hookMode) {
|
|
686
|
+
// No daemon: enqueue by starting one detached, then exit. The hook reads
|
|
687
|
+
// results from the daemon's cache on later edits — it never computes them.
|
|
688
|
+
startDaemonDetached(projectRoot);
|
|
689
|
+
process.exit(0);
|
|
608
690
|
}
|
|
609
691
|
}
|
|
610
692
|
if (!result) {
|
|
693
|
+
// Hook mode never falls back to an in-process audit (Spec 68 §hook-contract).
|
|
694
|
+
if (hookMode)
|
|
695
|
+
process.exit(0);
|
|
611
696
|
await initParsers();
|
|
612
697
|
const runner = createAuditRunner({
|
|
613
698
|
projectRoot: options.path,
|
|
614
699
|
scope,
|
|
615
|
-
analyzerConcurrency: 4
|
|
616
700
|
});
|
|
617
701
|
result = await runner.run();
|
|
618
702
|
}
|
|
@@ -621,7 +705,7 @@ program
|
|
|
621
705
|
// Spec 57 — dismissed count is reported alongside the total, never
|
|
622
706
|
// subtracted from it. The hook surface (`changed`) is agent-facing, so it
|
|
623
707
|
// must show the same "N findings, M dismissed" figure as the full report.
|
|
624
|
-
const dismissedCount = result.summary
|
|
708
|
+
const dismissedCount = result.summary?.dismissed ?? 0;
|
|
625
709
|
// Blocking gate decision (Spec 45 R1/R4, Spec 54 R3), computed once so the
|
|
626
710
|
// agent-facing before/after count (Spec 45 A2) and the exit code agree on
|
|
627
711
|
// the same number. Every registered rule participates (R1); enforcement is
|
|
@@ -629,98 +713,21 @@ program
|
|
|
629
713
|
// {critical, severe, high} — every finding is a defect and every finding
|
|
630
714
|
// blocks; there is no configurable gate.
|
|
631
715
|
const { blocking, resolutionGaps } = computeGatingDecision(violations, BLOCKING_SEVERITIES);
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
const { generateSARIFReport, readVersionControlProvenance } = await import('./reporting/sarifReportGenerator.js');
|
|
635
|
-
const projectDir = resolve(options.path || process.cwd());
|
|
636
|
-
const sarifOutput = generateSARIFReport(result, { rootDir: projectDir, ...readVersionControlProvenance(projectDir) });
|
|
637
|
-
process.stdout.write(sarifOutput + '\n');
|
|
638
|
-
}
|
|
639
|
-
else if (options.json) {
|
|
640
|
-
const projectDir = resolve(options.path || process.cwd());
|
|
641
|
-
const jsonViolations = violations.map((v) => {
|
|
642
|
-
// Compute relative path if file resolves inside the project
|
|
643
|
-
let filePath = v.file || '';
|
|
644
|
-
if (filePath.startsWith('/') || filePath.startsWith('\\\\')) {
|
|
645
|
-
const rel = relative(projectDir, filePath);
|
|
646
|
-
// Only use relative path if it doesn't escape the project
|
|
647
|
-
if (!rel.startsWith('..') && !isAbsolute(rel)) {
|
|
648
|
-
filePath = rel;
|
|
649
|
-
}
|
|
650
|
-
}
|
|
651
|
-
// Find column: prefer explicit column, then start.column, then default to 1
|
|
652
|
-
const col = v.column ?? v.start?.column ?? 1;
|
|
653
|
-
return {
|
|
654
|
-
analyzer: v.analyzer || '',
|
|
655
|
-
rule: v.rule,
|
|
656
|
-
severity: v.severity,
|
|
657
|
-
message: v.message,
|
|
658
|
-
file: filePath,
|
|
659
|
-
line: v.line ?? v.start?.line,
|
|
660
|
-
column: col,
|
|
661
|
-
endLine: v.end?.line,
|
|
662
|
-
endColumn: v.end?.column,
|
|
663
|
-
enclosingSymbol: v.symbol || v.enclosingFunction || '',
|
|
664
|
-
suggestion: v.suggestion || '',
|
|
665
|
-
details: v.details || '',
|
|
666
|
-
...(v.new !== undefined && { new: v.new }),
|
|
667
|
-
...(v.dismissed !== undefined && { dismissed: v.dismissed }),
|
|
668
|
-
fingerprint: fingerprint(buildFingerprintInput(v))
|
|
669
|
-
};
|
|
670
|
-
});
|
|
671
|
-
// Coverage diagnostics ride alongside violations so an agent sees "the
|
|
672
|
-
// analyzer couldn't resolve this" without a metadata flag — and without
|
|
673
|
-
// it ever gating (diagnostics never reach computeGatingDecision).
|
|
674
|
-
const jsonDiagnostics = (result.metadata?.diagnostics ?? []).map((d) => ({
|
|
675
|
-
analyzer: d.analyzerName ?? d.analyzer ?? '',
|
|
676
|
-
kind: d.kind,
|
|
677
|
-
message: d.message,
|
|
678
|
-
...(d.file ? { file: d.file } : {}),
|
|
679
|
-
...(typeof d.line === 'number' ? { line: d.line } : {}),
|
|
680
|
-
...(d.details ? { details: d.details } : {}),
|
|
681
|
-
}));
|
|
682
|
-
const jsonOutput = { violations: jsonViolations, diagnostics: jsonDiagnostics };
|
|
683
|
-
process.stdout.write(JSON.stringify(jsonOutput, null, 2) + '\n');
|
|
684
|
-
}
|
|
685
|
-
else if (!options.quiet || violations.length > 0) {
|
|
686
|
-
// Console output — Spec 45 A2: agent-facing output emits counts (per
|
|
687
|
-
// analyzer/rule/severity, plus the before/after gate figure) alongside
|
|
688
|
-
// findings. The hook and the `changed` command are the agent's surface.
|
|
689
|
-
console.log(chalk.blue('🔍 Diff-Scoped Code Audit'));
|
|
690
|
-
console.log(chalk.gray('══════════════════════════════════════════════════'));
|
|
691
|
-
printFileAccounting(result, !!options.explainSkipped);
|
|
692
|
-
if (violations.length > 0) {
|
|
693
|
-
console.log('');
|
|
694
|
-
printCountSummary(violations);
|
|
695
|
-
console.log(chalk.bold(`gate before/after: ${violations.length} → ${blocking.length} blocking`));
|
|
696
|
-
if (dismissedCount > 0) {
|
|
697
|
-
console.log(chalk.dim(` ${dismissedCount} dismissed — still counted above, excluded from the gate`));
|
|
698
|
-
}
|
|
699
|
-
console.log(chalk.gray('── Violations ────────────────────────────────────'));
|
|
700
|
-
for (const v of violations) {
|
|
701
|
-
const icon = v.severity === 'critical' ? '🔴' :
|
|
702
|
-
v.severity === 'severe' ? '🟠' : '🟡';
|
|
703
|
-
const statusTag = v.new === false
|
|
704
|
-
? chalk.dim(' [known — still open]')
|
|
705
|
-
: '';
|
|
706
|
-
const dismissedTag = v.dismissed
|
|
707
|
-
? chalk.dim(' [dismissed]')
|
|
708
|
-
: '';
|
|
709
|
-
console.log(`${icon} ${chalk.bold(v.file)}${v.line ? `:${v.line}` : ''} [${v.severity}] ${v.message}${statusTag}${dismissedTag}`);
|
|
710
|
-
}
|
|
711
|
-
}
|
|
712
|
-
else {
|
|
713
|
-
console.log(chalk.green('\n✓ No readings.'));
|
|
714
|
-
}
|
|
715
|
-
}
|
|
716
|
-
// Spec 38 R2/R3 — per-rule timing + gate wall-clock, opt-in by env var.
|
|
716
|
+
await emitChangedOutput(result, violations, blocking, dismissedCount, options);
|
|
717
|
+
// Spec 38 R2/R3 — per-rule timing + gate cpu-time, opt-in by env var.
|
|
717
718
|
// Emitted to stderr so it never corrupts --json stdout. Slowest rule first.
|
|
719
|
+
// The gate metric is CPU time (auditCpuMs), not wall clock: the budget is
|
|
720
|
+
// about a slow *rule*, and CPU time isolates that from a slow machine.
|
|
718
721
|
if (process.env.CODE_AUDIT_RULE_TIMING === '1') {
|
|
719
722
|
const ruleTiming = result.metadata?.ruleTiming;
|
|
720
|
-
const gateMs = result.metadata?.
|
|
723
|
+
const gateMs = result.metadata?.auditCpuMs;
|
|
724
|
+
const gateWallMs = result.metadata?.auditDuration;
|
|
721
725
|
const lines = [];
|
|
722
726
|
lines.push('');
|
|
723
|
-
|
|
727
|
+
// §16 criterion 16 — report both wall-clock (what the hook's user feels)
|
|
728
|
+
// and CPU (what the rule actually burned); the budget asserts on CPU.
|
|
729
|
+
lines.push(`⏱ gate wall-clock: ${(gateWallMs ?? 0).toFixed(1)} ms`);
|
|
730
|
+
lines.push(`⏱ gate cpu-time: ${(gateMs ?? 0).toFixed(1)} ms (budget 400 ms)`);
|
|
724
731
|
lines.push('── per-rule timing (slowest first) ──');
|
|
725
732
|
if (ruleTiming && ruleTiming.length > 0) {
|
|
726
733
|
for (const t of ruleTiming) {
|
|
@@ -753,6 +760,17 @@ program
|
|
|
753
760
|
process.exit(2);
|
|
754
761
|
}
|
|
755
762
|
}
|
|
763
|
+
// Pipeline-failure gate — the audit pipeline itself threw. One error,
|
|
764
|
+
// reported once by the runner; exit non-zero (broken tool) so a swallowed
|
|
765
|
+
// pipeline throw can never read as a clean pass.
|
|
766
|
+
{
|
|
767
|
+
const diagnostics = result.metadata?.diagnostics ?? [];
|
|
768
|
+
const pipelineErrors = diagnostics.filter((d) => d.kind === 'pipeline-error');
|
|
769
|
+
if (pipelineErrors.length > 0) {
|
|
770
|
+
console.error(`Pipeline failure: ${pipelineErrors[0].message}`);
|
|
771
|
+
process.exit(1);
|
|
772
|
+
}
|
|
773
|
+
}
|
|
756
774
|
// Exit code based on --no-fail-on-zero-files (default-on in v3.4.8)
|
|
757
775
|
// Any enabled analyzer matching zero source files is a failure — a >50%
|
|
758
776
|
// threshold lets the exact bug (3/9 analyzers dark) pass silently.
|
|
@@ -771,6 +789,96 @@ program
|
|
|
771
789
|
process.exit(1);
|
|
772
790
|
}
|
|
773
791
|
});
|
|
792
|
+
/**
|
|
793
|
+
* Emit the changed-audit output (SARIF, JSON, or console) for the hook surface.
|
|
794
|
+
* The SARIF branch dynamically imports its generator, so this is async.
|
|
795
|
+
*/
|
|
796
|
+
async function emitChangedOutput(result, violations, blocking, dismissedCount, options) {
|
|
797
|
+
// JSON output
|
|
798
|
+
if (options.format === 'sarif') {
|
|
799
|
+
const { generateSARIFReport, readVersionControlProvenance } = await import('./reporting/sarifReportGenerator.js');
|
|
800
|
+
const projectDir = resolve(options.path || process.cwd());
|
|
801
|
+
const sarifOutput = generateSARIFReport(result, { rootDir: projectDir, ...readVersionControlProvenance(projectDir) });
|
|
802
|
+
process.stdout.write(sarifOutput + '\n');
|
|
803
|
+
}
|
|
804
|
+
else if (options.json) {
|
|
805
|
+
const projectDir = resolve(options.path || process.cwd());
|
|
806
|
+
const jsonViolations = violations.map((v) => {
|
|
807
|
+
// Compute relative path if file resolves inside the project
|
|
808
|
+
let filePath = v.file || '';
|
|
809
|
+
if (filePath.startsWith('/') || filePath.startsWith('\\\\')) {
|
|
810
|
+
const rel = relative(projectDir, filePath);
|
|
811
|
+
// Only use relative path if it doesn't escape the project
|
|
812
|
+
if (!rel.startsWith('..') && !isAbsolute(rel)) {
|
|
813
|
+
filePath = rel;
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
// Find column: prefer explicit column, then start.column, then default to 1
|
|
817
|
+
const col = v.column ?? v.start?.column ?? 1;
|
|
818
|
+
return {
|
|
819
|
+
analyzer: v.analyzer || '',
|
|
820
|
+
rule: v.rule,
|
|
821
|
+
severity: v.severity,
|
|
822
|
+
message: v.message,
|
|
823
|
+
file: filePath,
|
|
824
|
+
line: v.line ?? v.start?.line,
|
|
825
|
+
column: col,
|
|
826
|
+
endLine: v.end?.line,
|
|
827
|
+
endColumn: v.end?.column,
|
|
828
|
+
enclosingSymbol: v.symbol || v.enclosingFunction || '',
|
|
829
|
+
suggestion: v.suggestion || '',
|
|
830
|
+
details: v.details || '',
|
|
831
|
+
...(v.new !== undefined && { new: v.new }),
|
|
832
|
+
...(v.dismissed !== undefined && { dismissed: v.dismissed }),
|
|
833
|
+
fingerprint: fingerprint(buildFingerprintInput(v))
|
|
834
|
+
};
|
|
835
|
+
});
|
|
836
|
+
// Coverage diagnostics ride alongside violations so an agent sees "the
|
|
837
|
+
// analyzer couldn't resolve this" without a metadata flag — and without
|
|
838
|
+
// it ever gating (diagnostics never reach computeGatingDecision).
|
|
839
|
+
const jsonDiagnostics = (result.metadata?.diagnostics ?? []).map((d) => ({
|
|
840
|
+
analyzer: d.analyzerName ?? d.analyzer ?? '',
|
|
841
|
+
kind: d.kind,
|
|
842
|
+
message: d.message,
|
|
843
|
+
...(d.file ? { file: d.file } : {}),
|
|
844
|
+
...(typeof d.line === 'number' ? { line: d.line } : {}),
|
|
845
|
+
...(d.details ? { details: d.details } : {}),
|
|
846
|
+
}));
|
|
847
|
+
const jsonOutput = { violations: jsonViolations, diagnostics: jsonDiagnostics };
|
|
848
|
+
process.stdout.write(JSON.stringify(jsonOutput, null, 2) + '\n');
|
|
849
|
+
}
|
|
850
|
+
else if (!options.quiet || violations.length > 0) {
|
|
851
|
+
// Console output — Spec 45 A2: agent-facing output emits counts (per
|
|
852
|
+
// analyzer/rule/severity, plus the before/after gate figure) alongside
|
|
853
|
+
// findings. The hook and the `changed` command are the agent's surface.
|
|
854
|
+
console.log(chalk.blue('🔍 Diff-Scoped Code Audit'));
|
|
855
|
+
console.log(chalk.gray('══════════════════════════════════════════════════'));
|
|
856
|
+
printFileAccounting(result, !!options.explainSkipped);
|
|
857
|
+
if (violations.length > 0) {
|
|
858
|
+
console.log('');
|
|
859
|
+
printCountSummary(violations);
|
|
860
|
+
console.log(chalk.bold(`gate before/after: ${violations.length} → ${blocking.length} blocking`));
|
|
861
|
+
if (dismissedCount > 0) {
|
|
862
|
+
console.log(chalk.dim(` ${dismissedCount} dismissed — still counted above, excluded from the gate`));
|
|
863
|
+
}
|
|
864
|
+
console.log(chalk.gray('── Violations ────────────────────────────────────'));
|
|
865
|
+
for (const v of violations) {
|
|
866
|
+
const icon = v.severity === 'critical' ? '🔴' :
|
|
867
|
+
v.severity === 'severe' ? '🟠' : '🟡';
|
|
868
|
+
const statusTag = v.new === false
|
|
869
|
+
? chalk.dim(' [known — still open]')
|
|
870
|
+
: '';
|
|
871
|
+
const dismissedTag = v.dismissed
|
|
872
|
+
? chalk.dim(' [dismissed]')
|
|
873
|
+
: '';
|
|
874
|
+
console.log(`${icon} ${chalk.bold(v.file)}${lineSuffix(v.line)} [${v.severity}] ${v.message}${statusTag}${dismissedTag}`);
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
else {
|
|
878
|
+
console.log(chalk.green('\n✓ No readings.'));
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
}
|
|
774
882
|
// Spec 45 A2 — counts are emitted where useful (per analyzer, per rule, per
|
|
775
883
|
// severity), on agent-facing surfaces as well as human ones. This reverts Spec
|
|
776
884
|
// 36 R3's "no bare counts": an agent now sees the shape of what fired instead
|
|
@@ -810,11 +918,61 @@ function printCountSummary(violations) {
|
|
|
810
918
|
*/
|
|
811
919
|
async function buildChangedResultFromDiagnostics(violations, projectRoot) {
|
|
812
920
|
const analyzerResults = {};
|
|
921
|
+
const byAnalyzer = {};
|
|
922
|
+
let totalViolations = 0;
|
|
923
|
+
let criticalIssues = 0;
|
|
924
|
+
let severe = 0;
|
|
925
|
+
let high = 0;
|
|
926
|
+
let dismissed = 0;
|
|
927
|
+
const violationsByCategory = {};
|
|
813
928
|
for (const v of violations) {
|
|
814
929
|
const key = v.analyzer || 'unknown';
|
|
815
930
|
(analyzerResults[key] = analyzerResults[key] || { violations: [] }).violations.push(v);
|
|
931
|
+
totalViolations++;
|
|
932
|
+
switch (v.severity) {
|
|
933
|
+
case 'critical':
|
|
934
|
+
criticalIssues++;
|
|
935
|
+
break;
|
|
936
|
+
case 'severe':
|
|
937
|
+
severe++;
|
|
938
|
+
break;
|
|
939
|
+
case 'high':
|
|
940
|
+
high++;
|
|
941
|
+
break;
|
|
942
|
+
}
|
|
943
|
+
const rule = v.rule || 'unknown';
|
|
944
|
+
violationsByCategory[rule] = (violationsByCategory[rule] ?? 0) + 1;
|
|
945
|
+
const bucket = byAnalyzer[key] ?? (byAnalyzer[key] = { violations: 0 });
|
|
946
|
+
bucket.violations++;
|
|
947
|
+
if (v.dismissed)
|
|
948
|
+
dismissed++;
|
|
816
949
|
}
|
|
817
|
-
|
|
950
|
+
const topIssues = Object.entries(violationsByCategory)
|
|
951
|
+
.sort(([, a], [, b]) => b - a)
|
|
952
|
+
.slice(0, 5)
|
|
953
|
+
.map(([type, count]) => ({ type, count }));
|
|
954
|
+
// A *real* summary derived from the diagnostics the daemon served — not a
|
|
955
|
+
// zero-filled placeholder (a stub satisfying the type, with a `dismissed: 0`
|
|
956
|
+
// that was never measured). Every field here is computed from the actual
|
|
957
|
+
// findings: severity/category/byAnalyzer rollups, plus `dismissed` from each
|
|
958
|
+
// diagnostic's own `dismissed` flag. `totalFiles` and the per-analyzer
|
|
959
|
+
// `filesProcessed`/`fatalErrors`/`unprovenSites` are not derivable from a flat
|
|
960
|
+
// per-file diagnostic list, so they are omitted; the guarded read
|
|
961
|
+
// (`result.summary?.dismissed ?? 0`) tolerates their absence.
|
|
962
|
+
return {
|
|
963
|
+
analyzerResults,
|
|
964
|
+
summary: {
|
|
965
|
+
totalViolations,
|
|
966
|
+
criticalIssues,
|
|
967
|
+
severe,
|
|
968
|
+
high,
|
|
969
|
+
violationsByCategory,
|
|
970
|
+
topIssues,
|
|
971
|
+
byAnalyzer,
|
|
972
|
+
dismissed,
|
|
973
|
+
},
|
|
974
|
+
metadata: { configUsed: undefined, diagnostics: [] },
|
|
975
|
+
};
|
|
818
976
|
}
|
|
819
977
|
// Self-audit gate (Spec 33 Item 15 + Spec 44 remediation). Runs the full
|
|
820
978
|
// analyzer pipeline over the tool's own production source and asserts zero
|
|
@@ -850,7 +1008,7 @@ program
|
|
|
850
1008
|
.description('Audit the tool\'s own analyzers/ + languages/ source and fail on blocking readings')
|
|
851
1009
|
.option('--json', 'Output violations as machine-readable JSON to stdout')
|
|
852
1010
|
.option('--stdin', 'Read file paths from stdin (one per line)')
|
|
853
|
-
.option('-p, --path <projectPath>',
|
|
1011
|
+
.option('-p, --path <projectPath>', OPTION_PROJECT_ROOT_PATH, process.cwd())
|
|
854
1012
|
.option('--fail-on <severity>', 'Blocking severity floor: critical, severe, or high', 'high')
|
|
855
1013
|
.action(async (paths, options) => {
|
|
856
1014
|
try {
|
|
@@ -889,7 +1047,6 @@ program
|
|
|
889
1047
|
const runner = createAuditRunner({
|
|
890
1048
|
projectRoot: options.path,
|
|
891
1049
|
scope,
|
|
892
|
-
analyzerConcurrency: 4
|
|
893
1050
|
});
|
|
894
1051
|
const result = await runner.run();
|
|
895
1052
|
const violations = Object.values(result.analyzerResults).flatMap((r) => r.violations || []);
|
|
@@ -934,7 +1091,7 @@ program
|
|
|
934
1091
|
for (const v of blocking) {
|
|
935
1092
|
const icon = v.severity === 'critical' ? '🔴' :
|
|
936
1093
|
v.severity === 'severe' ? '🟠' : '🟡';
|
|
937
|
-
console.log(`${icon} ${v.file}${v.line
|
|
1094
|
+
console.log(`${icon} ${v.file}${lineSuffix(v.line)} [${v.severity}] ${v.message}`);
|
|
938
1095
|
}
|
|
939
1096
|
}
|
|
940
1097
|
else {
|
|
@@ -960,8 +1117,8 @@ program
|
|
|
960
1117
|
program
|
|
961
1118
|
.command('next-file')
|
|
962
1119
|
.description('Audit and return the highest-priority file with readings (file-by-file refactor loop)')
|
|
963
|
-
.option('-p, --path <projectPath>',
|
|
964
|
-
.option('-c, --config <config>',
|
|
1120
|
+
.option('-p, --path <projectPath>', OPTION_PROJECT_ROOT_PATH, process.cwd())
|
|
1121
|
+
.option('-c, --config <config>', OPTION_CONFIGURATION_NAME)
|
|
965
1122
|
.option('--json', 'Output a single JSON object (or {done:true}) to stdout')
|
|
966
1123
|
.action(async (options) => {
|
|
967
1124
|
try {
|
|
@@ -1059,7 +1216,7 @@ function printNextFile(violations, summary, projectDir, json) {
|
|
|
1059
1216
|
console.log(chalk.gray('\n── Readings ────────────────────────────────────────'));
|
|
1060
1217
|
for (const v of ordered) {
|
|
1061
1218
|
const icon = v.severity === 'critical' ? '🔴' : v.severity === 'severe' ? '🟠' : '🟡';
|
|
1062
|
-
console.log(`${icon} ${chalk.bold(relativize(v.file || ''))}${v.line
|
|
1219
|
+
console.log(`${icon} ${chalk.bold(relativize(v.file || ''))}${lineSuffix(v.line)} [${v.severity}] ${v.rule} — ${v.message}`);
|
|
1063
1220
|
}
|
|
1064
1221
|
}
|
|
1065
1222
|
}
|
|
@@ -1067,8 +1224,8 @@ function printNextFile(violations, summary, projectDir, json) {
|
|
|
1067
1224
|
program
|
|
1068
1225
|
.command('baseline')
|
|
1069
1226
|
.description('Snapshot current advisory readings as the baseline (excludes invariants)')
|
|
1070
|
-
.option('-p, --path <path>',
|
|
1071
|
-
.option('--json',
|
|
1227
|
+
.option('-p, --path <path>', OPTION_PROJECT_PATH, process.cwd())
|
|
1228
|
+
.option('--json', OPTION_OUTPUT_AS_JSON)
|
|
1072
1229
|
.action(async (options) => {
|
|
1073
1230
|
try {
|
|
1074
1231
|
await initParsers();
|
|
@@ -1134,9 +1291,9 @@ program
|
|
|
1134
1291
|
.command('dismiss <fingerprint>')
|
|
1135
1292
|
.description('Dismiss one finding by fingerprint — a written reason is required')
|
|
1136
1293
|
.requiredOption('--reason <reason>', 'Why this finding is dismissed (required — a dismissal without a reason is a config error)')
|
|
1137
|
-
.option('-p, --path <path>',
|
|
1294
|
+
.option('-p, --path <path>', OPTION_PROJECT_PATH, process.cwd())
|
|
1138
1295
|
.option('--telemetry-endpoint <url>', 'Feedback service endpoint override (defaults to the opt-in endpoint recorded by the telemetry tool)')
|
|
1139
|
-
.option('--json',
|
|
1296
|
+
.option('--json', OPTION_OUTPUT_AS_JSON)
|
|
1140
1297
|
.action(async (fingerprintArg, options) => {
|
|
1141
1298
|
try {
|
|
1142
1299
|
const reason = (options.reason ?? '').trim();
|
|
@@ -1248,7 +1405,7 @@ indexCmd
|
|
|
1248
1405
|
.command('sync')
|
|
1249
1406
|
.description('Synchronize index from source files')
|
|
1250
1407
|
.option('--path <path>', 'Path to a specific file or directory', process.cwd())
|
|
1251
|
-
.option('--json',
|
|
1408
|
+
.option('--json', OPTION_OUTPUT_AS_JSON)
|
|
1252
1409
|
.action(async (options) => {
|
|
1253
1410
|
try {
|
|
1254
1411
|
await initParsers();
|
|
@@ -1289,64 +1446,65 @@ indexCmd
|
|
|
1289
1446
|
process.exit(1);
|
|
1290
1447
|
}
|
|
1291
1448
|
});
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1449
|
+
/** Register a simple `index` subcommand that runs one mutation on the
|
|
1450
|
+
* initialized index and prints the outcome (JSON or console). `cleanup` and
|
|
1451
|
+
* `reset` share the get-instance/initialize/json-vs-console/try-catch
|
|
1452
|
+
* scaffolding; only the mutation and its result prose differ, so they live
|
|
1453
|
+
* here once and each command is a thin `defineIndexMutationCommand(...)` call. */
|
|
1454
|
+
function defineIndexMutationCommand(name, description, work) {
|
|
1455
|
+
indexCmd
|
|
1456
|
+
.command(name)
|
|
1457
|
+
.description(description)
|
|
1458
|
+
.option('--json', OPTION_OUTPUT_AS_JSON)
|
|
1459
|
+
.action(async (options) => {
|
|
1460
|
+
try {
|
|
1461
|
+
const db = CodeIndexDB.getInstance();
|
|
1462
|
+
await db.initialize();
|
|
1463
|
+
const { extra, greenLine, grayLine } = await work(db);
|
|
1464
|
+
if (options.json) {
|
|
1465
|
+
process.stdout.write(JSON.stringify({ mode: name, success: true, ...extra }) + '\n');
|
|
1466
|
+
}
|
|
1467
|
+
else {
|
|
1468
|
+
console.log(chalk.green(greenLine));
|
|
1469
|
+
console.log(chalk.gray(grayLine));
|
|
1470
|
+
}
|
|
1303
1471
|
}
|
|
1304
|
-
|
|
1305
|
-
console.
|
|
1306
|
-
|
|
1472
|
+
catch (error) {
|
|
1473
|
+
console.error(chalk.red('Error:'), error);
|
|
1474
|
+
process.exit(1);
|
|
1307
1475
|
}
|
|
1308
|
-
}
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1476
|
+
});
|
|
1477
|
+
}
|
|
1478
|
+
defineIndexMutationCommand('cleanup', 'Remove index entries for deleted files', async (db) => {
|
|
1479
|
+
const result = await db.bulkCleanup();
|
|
1480
|
+
return {
|
|
1481
|
+
extra: result,
|
|
1482
|
+
greenLine: '✓ Cleanup complete',
|
|
1483
|
+
grayLine: `Removed ${result.removedCount} entries from ${result.removedFiles.length} deleted files`,
|
|
1484
|
+
};
|
|
1313
1485
|
});
|
|
1314
|
-
|
|
1315
|
-
.
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
await db.initialize();
|
|
1322
|
-
await db.clearIndex();
|
|
1323
|
-
if (options.json) {
|
|
1324
|
-
process.stdout.write(JSON.stringify({ mode: 'reset', success: true, message: 'All analysis data cleared' }) + '\n');
|
|
1325
|
-
}
|
|
1326
|
-
else {
|
|
1327
|
-
console.log(chalk.green('✓ Index reset complete'));
|
|
1328
|
-
console.log(chalk.gray('All analysis-derived data cleared (tasks, config, and whitelist preserved)'));
|
|
1329
|
-
}
|
|
1330
|
-
}
|
|
1331
|
-
catch (error) {
|
|
1332
|
-
console.error(chalk.red('Error:'), error);
|
|
1333
|
-
process.exit(1);
|
|
1334
|
-
}
|
|
1486
|
+
defineIndexMutationCommand('reset', 'Clear all analysis data (preserves tasks, config, whitelist)', async (db) => {
|
|
1487
|
+
await db.clearIndex();
|
|
1488
|
+
return {
|
|
1489
|
+
extra: { message: 'All analysis data cleared' },
|
|
1490
|
+
greenLine: '✓ Index reset complete',
|
|
1491
|
+
grayLine: 'All analysis-derived data cleared (tasks, config, and whitelist preserved)',
|
|
1492
|
+
};
|
|
1335
1493
|
});
|
|
1336
1494
|
// ── Index status command (Spec 14 R1) ──────────────────────────
|
|
1337
1495
|
indexCmd
|
|
1338
1496
|
.command('status')
|
|
1339
1497
|
.description('Show index health and graph statistics')
|
|
1340
|
-
.option('--json',
|
|
1498
|
+
.option('--json', OPTION_OUTPUT_AS_JSON)
|
|
1341
1499
|
.action(async (options) => {
|
|
1342
1500
|
try {
|
|
1343
1501
|
const db = CodeIndexDB.getInstance();
|
|
1344
1502
|
await db.initialize();
|
|
1345
|
-
const stats = await db.getStats();
|
|
1503
|
+
const stats = await db.functionIndex.getStats();
|
|
1346
1504
|
// Graph stats — non-fatal if graph cache is empty
|
|
1347
1505
|
let graphStats = null;
|
|
1348
1506
|
try {
|
|
1349
|
-
graphStats = db.getGraphStats();
|
|
1507
|
+
graphStats = db.graph.getGraphStats();
|
|
1350
1508
|
}
|
|
1351
1509
|
catch {
|
|
1352
1510
|
// No graph data yet — ok
|
|
@@ -1417,7 +1575,7 @@ configCmd
|
|
|
1417
1575
|
.command('rules-list')
|
|
1418
1576
|
.description('List all configured invariant rules from .codeauditor.json')
|
|
1419
1577
|
.option('--config-path <path>', 'Path to .codeauditor.json')
|
|
1420
|
-
.option('--json',
|
|
1578
|
+
.option('--json', OPTION_OUTPUT_AS_JSON)
|
|
1421
1579
|
.action(async (options) => {
|
|
1422
1580
|
try {
|
|
1423
1581
|
const pathModule = await import('path');
|
|
@@ -1427,7 +1585,9 @@ configCmd
|
|
|
1427
1585
|
try {
|
|
1428
1586
|
const raw = readFileSync(configPath, 'utf-8');
|
|
1429
1587
|
const config = JSON.parse(raw);
|
|
1430
|
-
|
|
1588
|
+
// `rules` is an array by schema; tolerate the `{}` shorthand some fixtures
|
|
1589
|
+
// and hand-edited configs use for "no rules" — `.map` on `{}` would throw.
|
|
1590
|
+
const rules = Array.isArray(config?.rules) ? config.rules : [];
|
|
1431
1591
|
if (options.json) {
|
|
1432
1592
|
process.stdout.write(JSON.stringify({
|
|
1433
1593
|
rules: rules.map((r) => ({ id: r.id, kind: r.kind, severity: r.severity, message: r.message || null })),
|
|
@@ -1465,7 +1625,7 @@ configCmd
|
|
|
1465
1625
|
.command('rules-check')
|
|
1466
1626
|
.description('Validate the current .codeauditor.json rules')
|
|
1467
1627
|
.option('--config-path <path>', 'Path to .codeauditor.json')
|
|
1468
|
-
.option('--json',
|
|
1628
|
+
.option('--json', OPTION_OUTPUT_AS_JSON)
|
|
1469
1629
|
.action(async (options) => {
|
|
1470
1630
|
try {
|
|
1471
1631
|
const pathModule = await import('path');
|
|
@@ -1513,9 +1673,9 @@ configCmd
|
|
|
1513
1673
|
configCmd
|
|
1514
1674
|
.command('profiles')
|
|
1515
1675
|
.description('Show active path profiles and resolve per-file matching')
|
|
1516
|
-
.option('-p, --path <path>',
|
|
1676
|
+
.option('-p, --path <path>', OPTION_PROJECT_PATH, process.cwd())
|
|
1517
1677
|
.option('--file <file>', 'Resolve profiles for a specific file')
|
|
1518
|
-
.option('--json',
|
|
1678
|
+
.option('--json', OPTION_OUTPUT_AS_JSON)
|
|
1519
1679
|
.action(async (options) => {
|
|
1520
1680
|
try {
|
|
1521
1681
|
const fs = await import('fs/promises');
|
|
@@ -1613,8 +1773,8 @@ configCmd
|
|
|
1613
1773
|
configCmd
|
|
1614
1774
|
.command('detection')
|
|
1615
1775
|
.description('Show DB-provenanced and inferred receiver identifiers with per-entry evidence')
|
|
1616
|
-
.option('-p, --path <path>',
|
|
1617
|
-
.option('--json',
|
|
1776
|
+
.option('-p, --path <path>', OPTION_PROJECT_PATH, process.cwd())
|
|
1777
|
+
.option('--json', OPTION_OUTPUT_AS_JSON)
|
|
1618
1778
|
.option('--mode <mode>', 'Detection mode filter (hybrid, provenance, names)')
|
|
1619
1779
|
.action(async (options) => {
|
|
1620
1780
|
try {
|
|
@@ -1666,9 +1826,7 @@ configCmd
|
|
|
1666
1826
|
// Build provenance context using schema analyzer config (most complete)
|
|
1667
1827
|
const ctx = buildProvenanceContext(ast, adapter, sourceCode, {
|
|
1668
1828
|
mode: detectionMode,
|
|
1669
|
-
dbReceiverNames: schemaCfg.dbReceiverNames ?? daCfg.dbReceiverNames,
|
|
1670
1829
|
dbBindingNames: schemaCfg.dbBindingNames ?? daCfg.dbBindingNames,
|
|
1671
|
-
dbCallMethods: schemaCfg.dbCallMethods ?? daCfg.dbCallMethods,
|
|
1672
1830
|
});
|
|
1673
1831
|
// Collect DB-provenanced
|
|
1674
1832
|
for (const [id, evidence] of ctx.dbProvenanced) {
|
|
@@ -1721,70 +1879,84 @@ configCmd
|
|
|
1721
1879
|
entry.validatorProvenanced.push(v);
|
|
1722
1880
|
}
|
|
1723
1881
|
for (const [file, data] of byFile) {
|
|
1724
|
-
db.storeFileProvenance(file, data);
|
|
1882
|
+
db.provenance.storeFileProvenance(file, data);
|
|
1725
1883
|
}
|
|
1726
1884
|
// Store inferred receivers
|
|
1727
|
-
db.storeInferredReceivers(inferred);
|
|
1885
|
+
db.inferredReceivers.storeInferredReceivers(inferred);
|
|
1728
1886
|
}
|
|
1729
1887
|
catch {
|
|
1730
1888
|
// Index not available — skip persistence
|
|
1731
1889
|
}
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
}
|
|
1742
|
-
else {
|
|
1743
|
-
console.log(chalk.bold(`Detection mode: ${chalk.cyan(detectionMode)}`));
|
|
1744
|
-
console.log(chalk.gray(`Scanned ${files.slice(0, 500).length} files`));
|
|
1745
|
-
console.log();
|
|
1746
|
-
if (provenanced.length > 0) {
|
|
1747
|
-
console.log(chalk.bold('DB-Provenanced Identifiers:'));
|
|
1748
|
-
for (const p of provenanced) {
|
|
1749
|
-
const reasonColor = p.reason === 'fallback'
|
|
1750
|
-
? chalk.yellow
|
|
1751
|
-
: p.reason === 'package'
|
|
1752
|
-
? chalk.green
|
|
1753
|
-
: chalk.cyan;
|
|
1754
|
-
console.log(` ${chalk.white(p.identifier)} ${chalk.gray(`(${p.file})`)}`);
|
|
1755
|
-
console.log(` ${reasonColor(`reason: ${p.reason}`)} — ${chalk.gray(p.source)}`);
|
|
1756
|
-
if (p.chain) {
|
|
1757
|
-
console.log(` ${chalk.gray(`chain: ${p.chain.join(' → ')}`)}`);
|
|
1758
|
-
}
|
|
1759
|
-
}
|
|
1760
|
-
console.log();
|
|
1761
|
-
}
|
|
1762
|
-
if (inferred.length > 0) {
|
|
1763
|
-
console.log(chalk.bold('Inferred Receivers:'));
|
|
1764
|
-
for (const i of inferred) {
|
|
1765
|
-
console.log(` ${chalk.white(i.identifier)} ${chalk.gray(`(${i.file})`)}`);
|
|
1766
|
-
console.log(` ${chalk.magenta(i.reason)}`);
|
|
1767
|
-
}
|
|
1768
|
-
console.log();
|
|
1769
|
-
}
|
|
1770
|
-
if (validatorIdentifiers.length > 0) {
|
|
1771
|
-
console.log(chalk.bold('Validator-Provenanced Identifiers:'));
|
|
1772
|
-
for (const v of validatorIdentifiers) {
|
|
1773
|
-
console.log(` ${chalk.white(v.identifier)} ${chalk.gray(`(${v.file})`)}`);
|
|
1774
|
-
console.log(` ${chalk.green(`reason: ${v.reason}`)} — ${chalk.gray(v.source)}`);
|
|
1775
|
-
}
|
|
1776
|
-
console.log();
|
|
1777
|
-
}
|
|
1778
|
-
if (provenanced.length === 0 && inferred.length === 0 && validatorIdentifiers.length === 0) {
|
|
1779
|
-
console.log(chalk.gray('No DB-provenanced, inferred, or validator-provenanced identifiers found.'));
|
|
1780
|
-
}
|
|
1781
|
-
}
|
|
1890
|
+
printDetectionResults({
|
|
1891
|
+
json: options.json,
|
|
1892
|
+
mode: detectionMode,
|
|
1893
|
+
projectRoot,
|
|
1894
|
+
filesScanned: files.slice(0, 500).length,
|
|
1895
|
+
dbProvenanced: provenanced,
|
|
1896
|
+
validatorProvenanced: validatorIdentifiers,
|
|
1897
|
+
inferred,
|
|
1898
|
+
});
|
|
1782
1899
|
}
|
|
1783
1900
|
catch (error) {
|
|
1784
1901
|
console.error(chalk.red('Error:'), error);
|
|
1785
1902
|
process.exit(1);
|
|
1786
1903
|
}
|
|
1787
1904
|
});
|
|
1905
|
+
// Renders the `config detection` results — pulled out of the `.action` handler so
|
|
1906
|
+
// the scan/persist logic stays short and the two output shapes (JSON vs human) are
|
|
1907
|
+
// isolated from the provenance collection.
|
|
1908
|
+
function printDetectionResults(params) {
|
|
1909
|
+
const { json, mode, projectRoot, filesScanned, dbProvenanced, validatorProvenanced, inferred } = params;
|
|
1910
|
+
if (json) {
|
|
1911
|
+
process.stdout.write(JSON.stringify({
|
|
1912
|
+
mode,
|
|
1913
|
+
projectRoot,
|
|
1914
|
+
filesScanned,
|
|
1915
|
+
dbProvenanced,
|
|
1916
|
+
validatorProvenanced,
|
|
1917
|
+
inferred,
|
|
1918
|
+
}, null, 2) + '\n');
|
|
1919
|
+
return;
|
|
1920
|
+
}
|
|
1921
|
+
console.log(chalk.bold(`Detection mode: ${chalk.cyan(mode)}`));
|
|
1922
|
+
console.log(chalk.gray(`Scanned ${filesScanned} files`));
|
|
1923
|
+
console.log();
|
|
1924
|
+
if (dbProvenanced.length > 0) {
|
|
1925
|
+
console.log(chalk.bold('DB-Provenanced Identifiers:'));
|
|
1926
|
+
for (const p of dbProvenanced) {
|
|
1927
|
+
const reasonColor = p.reason === 'fallback'
|
|
1928
|
+
? chalk.yellow
|
|
1929
|
+
: p.reason === 'package'
|
|
1930
|
+
? chalk.green
|
|
1931
|
+
: chalk.cyan;
|
|
1932
|
+
console.log(` ${chalk.white(p.identifier)} ${chalk.gray(`(${p.file})`)}`);
|
|
1933
|
+
console.log(` ${reasonColor(`reason: ${p.reason}`)} — ${chalk.gray(p.source)}`);
|
|
1934
|
+
if (p.chain) {
|
|
1935
|
+
console.log(` ${chalk.gray(`chain: ${p.chain.join(' → ')}`)}`);
|
|
1936
|
+
}
|
|
1937
|
+
}
|
|
1938
|
+
console.log();
|
|
1939
|
+
}
|
|
1940
|
+
if (inferred.length > 0) {
|
|
1941
|
+
console.log(chalk.bold('Inferred Receivers:'));
|
|
1942
|
+
for (const i of inferred) {
|
|
1943
|
+
console.log(` ${chalk.white(i.identifier)} ${chalk.gray(`(${i.file})`)}`);
|
|
1944
|
+
console.log(` ${chalk.magenta(i.reason)}`);
|
|
1945
|
+
}
|
|
1946
|
+
console.log();
|
|
1947
|
+
}
|
|
1948
|
+
if (validatorProvenanced.length > 0) {
|
|
1949
|
+
console.log(chalk.bold('Validator-Provenanced Identifiers:'));
|
|
1950
|
+
for (const v of validatorProvenanced) {
|
|
1951
|
+
console.log(` ${chalk.white(v.identifier)} ${chalk.gray(`(${v.file})`)}`);
|
|
1952
|
+
console.log(` ${chalk.green(`reason: ${v.reason}`)} — ${chalk.gray(v.source)}`);
|
|
1953
|
+
}
|
|
1954
|
+
console.log();
|
|
1955
|
+
}
|
|
1956
|
+
if (dbProvenanced.length === 0 && inferred.length === 0 && validatorProvenanced.length === 0) {
|
|
1957
|
+
console.log(chalk.gray('No DB-provenanced, inferred, or validator-provenanced identifiers found.'));
|
|
1958
|
+
}
|
|
1959
|
+
}
|
|
1788
1960
|
// Search command
|
|
1789
1961
|
program
|
|
1790
1962
|
.command('search <query>')
|
|
@@ -1852,7 +2024,7 @@ tasksCmd
|
|
|
1852
2024
|
.option('--priority <priority>', 'Filter by priority (low, medium, high)')
|
|
1853
2025
|
.option('--label <label>', 'Filter by label')
|
|
1854
2026
|
.option('--limit <limit>', 'Result limit', '50')
|
|
1855
|
-
.option('--json',
|
|
2027
|
+
.option('--json', OPTION_OUTPUT_AS_JSON)
|
|
1856
2028
|
.action(async (options) => {
|
|
1857
2029
|
try {
|
|
1858
2030
|
const args = { action: 'list' };
|
|
@@ -1887,7 +2059,7 @@ tasksCmd
|
|
|
1887
2059
|
}
|
|
1888
2060
|
}
|
|
1889
2061
|
else {
|
|
1890
|
-
console.error(chalk.red(result.error ||
|
|
2062
|
+
console.error(chalk.red(result.error || UNKNOWN_ERROR));
|
|
1891
2063
|
process.exit(1);
|
|
1892
2064
|
}
|
|
1893
2065
|
}
|
|
@@ -1904,7 +2076,7 @@ tasksCmd
|
|
|
1904
2076
|
.option('--priority <priority>', 'Priority (low, medium, high)')
|
|
1905
2077
|
.option('--status <status>', 'Status (open, in_progress)')
|
|
1906
2078
|
.option('--labels <labels>', 'Comma-separated labels')
|
|
1907
|
-
.option('--json',
|
|
2079
|
+
.option('--json', OPTION_OUTPUT_AS_JSON)
|
|
1908
2080
|
.action(async (options) => {
|
|
1909
2081
|
try {
|
|
1910
2082
|
const args = {
|
|
@@ -1929,7 +2101,7 @@ tasksCmd
|
|
|
1929
2101
|
console.log(chalk.green(`✓ Task created: ${task.title || task.taskId || 'unknown'}`));
|
|
1930
2102
|
}
|
|
1931
2103
|
else {
|
|
1932
|
-
console.error(chalk.red(result.error ||
|
|
2104
|
+
console.error(chalk.red(result.error || UNKNOWN_ERROR));
|
|
1933
2105
|
process.exit(1);
|
|
1934
2106
|
}
|
|
1935
2107
|
}
|
|
@@ -1941,7 +2113,7 @@ tasksCmd
|
|
|
1941
2113
|
tasksCmd
|
|
1942
2114
|
.command('get <taskId>')
|
|
1943
2115
|
.description('Get a task by ID')
|
|
1944
|
-
.option('--json',
|
|
2116
|
+
.option('--json', OPTION_OUTPUT_AS_JSON)
|
|
1945
2117
|
.action(async (taskId, options) => {
|
|
1946
2118
|
try {
|
|
1947
2119
|
const { handleProjectTasks } = await import('./mcp-tools/projectTasks.js');
|
|
@@ -1967,7 +2139,7 @@ tasksCmd
|
|
|
1967
2139
|
console.log(chalk.dim(`\nfingerprint: ${t.fingerprint}`));
|
|
1968
2140
|
}
|
|
1969
2141
|
else {
|
|
1970
|
-
console.error(chalk.red(result.error ||
|
|
2142
|
+
console.error(chalk.red(result.error || UNKNOWN_ERROR));
|
|
1971
2143
|
process.exit(1);
|
|
1972
2144
|
}
|
|
1973
2145
|
}
|
|
@@ -1984,7 +2156,7 @@ tasksCmd
|
|
|
1984
2156
|
.option('--status <status>', 'New status (open, in_progress, completed)')
|
|
1985
2157
|
.option('--priority <priority>', 'New priority (low, medium, high)')
|
|
1986
2158
|
.option('--labels <labels>', 'Comma-separated labels')
|
|
1987
|
-
.option('--json',
|
|
2159
|
+
.option('--json', OPTION_OUTPUT_AS_JSON)
|
|
1988
2160
|
.action(async (taskId, options) => {
|
|
1989
2161
|
try {
|
|
1990
2162
|
const patch = {};
|
|
@@ -2011,7 +2183,7 @@ tasksCmd
|
|
|
2011
2183
|
console.log(chalk.green(`✓ Task updated`));
|
|
2012
2184
|
}
|
|
2013
2185
|
else {
|
|
2014
|
-
console.error(chalk.red(result.error ||
|
|
2186
|
+
console.error(chalk.red(result.error || UNKNOWN_ERROR));
|
|
2015
2187
|
process.exit(1);
|
|
2016
2188
|
}
|
|
2017
2189
|
}
|
|
@@ -2020,60 +2192,45 @@ tasksCmd
|
|
|
2020
2192
|
process.exit(1);
|
|
2021
2193
|
}
|
|
2022
2194
|
});
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2195
|
+
/** Run a single-taskId tasks subcommand and print its result. */
|
|
2196
|
+
function makeTaskIdAction(action, successLabel) {
|
|
2197
|
+
return async (taskId, options) => {
|
|
2198
|
+
try {
|
|
2199
|
+
const { handleProjectTasks } = await import('./mcp-tools/projectTasks.js');
|
|
2200
|
+
const result = await handleProjectTasks({ action, taskId });
|
|
2201
|
+
if (options.json) {
|
|
2202
|
+
process.stdout.write(JSON.stringify(result, null, 2) + '\n');
|
|
2203
|
+
}
|
|
2204
|
+
else if (result.success) {
|
|
2205
|
+
console.log(chalk.green(`✓ ${successLabel}`));
|
|
2206
|
+
}
|
|
2207
|
+
else {
|
|
2208
|
+
console.error(chalk.red(result.error || UNKNOWN_ERROR));
|
|
2209
|
+
process.exit(1);
|
|
2210
|
+
}
|
|
2036
2211
|
}
|
|
2037
|
-
|
|
2038
|
-
console.error(chalk.red(
|
|
2212
|
+
catch (error) {
|
|
2213
|
+
console.error(chalk.red('Error:'), error);
|
|
2039
2214
|
process.exit(1);
|
|
2040
2215
|
}
|
|
2041
|
-
}
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2216
|
+
};
|
|
2217
|
+
}
|
|
2218
|
+
tasksCmd
|
|
2219
|
+
.command('complete <taskId>')
|
|
2220
|
+
.description('Mark a task as completed')
|
|
2221
|
+
.option('--json', OPTION_OUTPUT_AS_JSON)
|
|
2222
|
+
.action(makeTaskIdAction('complete_task', 'Task completed'));
|
|
2047
2223
|
tasksCmd
|
|
2048
2224
|
.command('delete <taskId>')
|
|
2049
2225
|
.description('Delete a task')
|
|
2050
|
-
.option('--json',
|
|
2051
|
-
.action(
|
|
2052
|
-
try {
|
|
2053
|
-
const { handleProjectTasks } = await import('./mcp-tools/projectTasks.js');
|
|
2054
|
-
const result = await handleProjectTasks({ action: 'delete', taskId });
|
|
2055
|
-
if (options.json) {
|
|
2056
|
-
process.stdout.write(JSON.stringify(result, null, 2) + '\n');
|
|
2057
|
-
}
|
|
2058
|
-
else if (result.success) {
|
|
2059
|
-
console.log(chalk.green(`✓ Task deleted`));
|
|
2060
|
-
}
|
|
2061
|
-
else {
|
|
2062
|
-
console.error(chalk.red(result.error || 'Unknown error'));
|
|
2063
|
-
process.exit(1);
|
|
2064
|
-
}
|
|
2065
|
-
}
|
|
2066
|
-
catch (error) {
|
|
2067
|
-
console.error(chalk.red('Error:'), error);
|
|
2068
|
-
process.exit(1);
|
|
2069
|
-
}
|
|
2070
|
-
});
|
|
2226
|
+
.option('--json', OPTION_OUTPUT_AS_JSON)
|
|
2227
|
+
.action(makeTaskIdAction('delete', 'Task deleted'));
|
|
2071
2228
|
tasksCmd
|
|
2072
2229
|
.command('from-audit')
|
|
2073
2230
|
.description('Populate tasks from audit violations')
|
|
2074
2231
|
.option('--auditJobId <id>', 'Specific audit result ID')
|
|
2075
2232
|
.option('--severities <severities>', 'Comma-separated severities (critical,severe,high)', 'critical,severe,high')
|
|
2076
|
-
.option('--json',
|
|
2233
|
+
.option('--json', OPTION_OUTPUT_AS_JSON)
|
|
2077
2234
|
.action(async (options) => {
|
|
2078
2235
|
try {
|
|
2079
2236
|
const args = { action: 'from_audit' };
|
|
@@ -2100,7 +2257,7 @@ tasksCmd
|
|
|
2100
2257
|
console.log(chalk.yellow(result.error));
|
|
2101
2258
|
}
|
|
2102
2259
|
else {
|
|
2103
|
-
console.error(chalk.red(result.error ||
|
|
2260
|
+
console.error(chalk.red(result.error || UNKNOWN_ERROR));
|
|
2104
2261
|
process.exit(1);
|
|
2105
2262
|
}
|
|
2106
2263
|
}
|
|
@@ -2116,7 +2273,7 @@ program
|
|
|
2116
2273
|
.description('Rank files and functions by hotspot score (churn × complexity)')
|
|
2117
2274
|
.option('--limit <n>', 'Max entries to show')
|
|
2118
2275
|
.option('--path <dir>', 'Target directory (triggers spot churn extraction if index is empty)')
|
|
2119
|
-
.option('--json',
|
|
2276
|
+
.option('--json', OPTION_OUTPUT_AS_JSON)
|
|
2120
2277
|
.action(async (options) => {
|
|
2121
2278
|
try {
|
|
2122
2279
|
const db = CodeIndexDB.getInstance();
|
|
@@ -2191,8 +2348,8 @@ program
|
|
|
2191
2348
|
.command('risk')
|
|
2192
2349
|
.description('Rank functions by architectural risk (PageRank × betweenness × complexity × untested)')
|
|
2193
2350
|
.option('--limit <n>', 'Max entries to show', '20')
|
|
2194
|
-
.option('--path <dir>',
|
|
2195
|
-
.option('--json',
|
|
2351
|
+
.option('--path <dir>', OPTION_PROJECT_PATH, process.cwd())
|
|
2352
|
+
.option('--json', OPTION_OUTPUT_AS_JSON)
|
|
2196
2353
|
.option('--format <format>', 'Output format: dot (call-graph neighborhood of top functions)')
|
|
2197
2354
|
.action(async (options) => {
|
|
2198
2355
|
try {
|
|
@@ -2207,10 +2364,13 @@ program
|
|
|
2207
2364
|
if (options.format === 'dot') {
|
|
2208
2365
|
// DOT output: call-graph neighborhood of top-N risk functions
|
|
2209
2366
|
const { callGraphToDot } = await import('./graph/outputFormatter.js');
|
|
2210
|
-
// Collect IDs for top risk functions
|
|
2367
|
+
// Collect IDs for top risk functions — one query for the whole top-N
|
|
2368
|
+
// set, not one query per function (loop-query).
|
|
2211
2369
|
const topIds = new Set();
|
|
2212
|
-
|
|
2213
|
-
const
|
|
2370
|
+
if (top.length > 0) {
|
|
2371
|
+
const orClause = top.map(() => '(name = ? AND file_path = ?)').join(' OR ');
|
|
2372
|
+
const params = top.flatMap((e) => [e.functionName, e.filePath]);
|
|
2373
|
+
const rows = rawDb.prepare(`SELECT id FROM functions WHERE ${orClause}`).all(...params);
|
|
2214
2374
|
for (const r of rows)
|
|
2215
2375
|
topIds.add(r.id);
|
|
2216
2376
|
}
|
|
@@ -2267,7 +2427,7 @@ ledgerCmd
|
|
|
2267
2427
|
.command('list')
|
|
2268
2428
|
.description('List recent audit runs')
|
|
2269
2429
|
.option('--limit <n>', 'Max runs to show', '20')
|
|
2270
|
-
.option('--json',
|
|
2430
|
+
.option('--json', OPTION_OUTPUT_AS_JSON)
|
|
2271
2431
|
.action(async (options) => {
|
|
2272
2432
|
try {
|
|
2273
2433
|
const { listRuns } = await import('./ledger.js');
|
|
@@ -2316,7 +2476,7 @@ ledgerCmd
|
|
|
2316
2476
|
ledgerCmd
|
|
2317
2477
|
.command('stats')
|
|
2318
2478
|
.description('Show aggregated ledger statistics')
|
|
2319
|
-
.option('--json',
|
|
2479
|
+
.option('--json', OPTION_OUTPUT_AS_JSON)
|
|
2320
2480
|
.action(async (options) => {
|
|
2321
2481
|
try {
|
|
2322
2482
|
const { getLedgerStats } = await import('./ledger.js');
|
|
@@ -2349,7 +2509,7 @@ ledgerCmd
|
|
|
2349
2509
|
.command('trends')
|
|
2350
2510
|
.description('Compare consecutive full-audit runs and report new vs fixed readings per rule')
|
|
2351
2511
|
.option('--since <runId>', 'Only consider runs after this run ID')
|
|
2352
|
-
.option('--json',
|
|
2512
|
+
.option('--json', OPTION_OUTPUT_AS_JSON)
|
|
2353
2513
|
.action(async (options) => {
|
|
2354
2514
|
try {
|
|
2355
2515
|
const { getTrends } = await import('./ledger.js');
|
|
@@ -2412,9 +2572,6 @@ ledgerCmd
|
|
|
2412
2572
|
// `audit --detach` forks workers/auditJobRunner; `status`/`result`/`jobs` read
|
|
2413
2573
|
// the persisted ledger. One job model (the ledger run), one write path — these
|
|
2414
2574
|
// are thin wrappers over ledger.ts + mcpAuditJobs.ts, not a second store.
|
|
2415
|
-
/** Full analyzer set — derives from createAuditRunner's registry so `--detach`
|
|
2416
|
-
* produces the same per-rule coverage as a synchronous `audit`. */
|
|
2417
|
-
const DETACHED_DEFAULT_ANALYZERS = [...ALL_ANALYZERS];
|
|
2418
2575
|
/** Lease TTL used by read-path reclaim, mirroring mcpAuditJobs.jobLeaseTtlMs. */
|
|
2419
2576
|
const CLI_JOB_LEASE_TTL_MS = Number(process.env.CODE_AUDITOR_JOB_LEASE_TTL_MS) || 30_000;
|
|
2420
2577
|
function spec41StatusBadge(status) {
|
|
@@ -2496,12 +2653,8 @@ async function runDetachedAudit(options) {
|
|
|
2496
2653
|
const logFd = openSync(logPath, 'a');
|
|
2497
2654
|
const argsJson = JSON.stringify({
|
|
2498
2655
|
path: auditPath,
|
|
2499
|
-
...(options.partitionStrategy && { partitionStrategy: options.partitionStrategy }),
|
|
2500
|
-
...(options.maxPartitions && { maxPartitions: Number(options.maxPartitions) }),
|
|
2501
|
-
...(options.shardTimeoutMs && { shardTimeoutMs: Number(options.shardTimeoutMs) }),
|
|
2502
2656
|
});
|
|
2503
2657
|
const defaultsJson = JSON.stringify({
|
|
2504
|
-
defaultAnalyzers: DETACHED_DEFAULT_ANALYZERS,
|
|
2505
2658
|
defaultMinSeverity: 'high',
|
|
2506
2659
|
defaultGenerateCodeMap: false,
|
|
2507
2660
|
});
|
|
@@ -2521,7 +2674,7 @@ program
|
|
|
2521
2674
|
.command('status <jobId>')
|
|
2522
2675
|
.description('Show a detached audit job status, progress, and staleness')
|
|
2523
2676
|
.option('-p, --path <path>', 'Project root containing the job (default: cwd)', process.cwd())
|
|
2524
|
-
.option('--json',
|
|
2677
|
+
.option('--json', OPTION_OUTPUT_AS_JSON)
|
|
2525
2678
|
.action(async (jobId, options) => {
|
|
2526
2679
|
try {
|
|
2527
2680
|
const { getLedgerRun, reclaimStaleRunning, computeStaleness } = await import('./ledger.js');
|
|
@@ -2584,12 +2737,12 @@ program
|
|
|
2584
2737
|
.option('--analyzer <analyzer>', 'Filter by analyzer')
|
|
2585
2738
|
.option('--file <file>', 'Filter by file path (substring match)')
|
|
2586
2739
|
.option('--severity <severity>', 'Filter by severity (critical|severe|high)')
|
|
2587
|
-
.option('--state [state]', 'Query coverage by state (fired|clean|notApplicable|cannot-fire|
|
|
2740
|
+
.option('--state [state]', 'Query coverage by state (fired|clean|notApplicable|cannot-fire|incomplete); omit value for all')
|
|
2588
2741
|
.option('--count', 'Group findings by analyzer/rule with counts')
|
|
2589
2742
|
.option('--limit <n>', 'Max findings to return (0 = unbounded)', '50')
|
|
2590
2743
|
.option('--offset <n>', 'Findings offset', '0')
|
|
2591
2744
|
.option('--stale', 'Force full content re-hash for staleness')
|
|
2592
|
-
.option('--json',
|
|
2745
|
+
.option('--json', OPTION_OUTPUT_AS_JSON)
|
|
2593
2746
|
.action(async (jobId, options) => {
|
|
2594
2747
|
try {
|
|
2595
2748
|
const { getLedgerRun, queryLedgerFindings, queryLedgerCoverage, computeStaleness } = await import('./ledger.js');
|
|
@@ -2682,7 +2835,7 @@ program
|
|
|
2682
2835
|
.option('--prune', 'Delete all but the newest runs for this project')
|
|
2683
2836
|
.option('--keep <n>', 'Runs to keep when --prune is set', '10')
|
|
2684
2837
|
.option('--limit <n>', 'Runs to display (staleness computed for these)', '20')
|
|
2685
|
-
.option('--json',
|
|
2838
|
+
.option('--json', OPTION_OUTPUT_AS_JSON)
|
|
2686
2839
|
.action(async (options) => {
|
|
2687
2840
|
try {
|
|
2688
2841
|
const { listLedgerRuns, reclaimStaleRunning, pruneLedgerRuns, computeStaleness } = await import('./ledger.js');
|
|
@@ -2738,7 +2891,7 @@ conventionsCmd
|
|
|
2738
2891
|
.command('list')
|
|
2739
2892
|
.description('List mined conventions from the code index')
|
|
2740
2893
|
.option('--domain <domain>', 'Filter by domain: usage-pair, import-form, error-handling, export-shape, naming')
|
|
2741
|
-
.option('--json',
|
|
2894
|
+
.option('--json', OPTION_OUTPUT_AS_JSON)
|
|
2742
2895
|
.action(async (options) => {
|
|
2743
2896
|
try {
|
|
2744
2897
|
const db = CodeIndexDB.getInstance();
|
|
@@ -2802,7 +2955,7 @@ conventionsCmd
|
|
|
2802
2955
|
.command('propose')
|
|
2803
2956
|
.description('Emit ready-to-paste .codeauditor.json rules from mined conventions')
|
|
2804
2957
|
.option('--domain <domain>', 'Filter by domain: naming or import-form (other domains do not map to rule kinds)')
|
|
2805
|
-
.option('--json',
|
|
2958
|
+
.option('--json', OPTION_OUTPUT_AS_JSON)
|
|
2806
2959
|
.action(async (options) => {
|
|
2807
2960
|
try {
|
|
2808
2961
|
const db = CodeIndexDB.getInstance();
|
|
@@ -2912,8 +3065,8 @@ program
|
|
|
2912
3065
|
program
|
|
2913
3066
|
.command('architecture')
|
|
2914
3067
|
.description('Analyze import graph: community detection, directory purity, Martin instability metrics')
|
|
2915
|
-
.option('--path <dir>',
|
|
2916
|
-
.option('--json',
|
|
3068
|
+
.option('--path <dir>', OPTION_PROJECT_PATH, process.cwd())
|
|
3069
|
+
.option('--json', OPTION_OUTPUT_AS_JSON)
|
|
2917
3070
|
.option('--format <format>', 'Output format: dot or mermaid (import graph colored by community)')
|
|
2918
3071
|
.action(async (options) => {
|
|
2919
3072
|
try {
|
|
@@ -3111,7 +3264,7 @@ coverageCmd
|
|
|
3111
3264
|
coverageCmd
|
|
3112
3265
|
.command('by-risk', { isDefault: true })
|
|
3113
3266
|
.description('Show coverage rate by risk decile')
|
|
3114
|
-
.option('--json',
|
|
3267
|
+
.option('--json', OPTION_OUTPUT_AS_JSON)
|
|
3115
3268
|
.action(async (options) => {
|
|
3116
3269
|
try {
|
|
3117
3270
|
const { generateCoverageReport } = await import('./coverage/coverageService.js');
|
|
@@ -3160,8 +3313,8 @@ coverageCmd
|
|
|
3160
3313
|
program
|
|
3161
3314
|
.command('print-config <file>')
|
|
3162
3315
|
.description('Print the effective config for a file, naming the source of every value')
|
|
3163
|
-
.option('-p, --path <path>',
|
|
3164
|
-
.option('--json',
|
|
3316
|
+
.option('-p, --path <path>', OPTION_PROJECT_PATH, process.cwd())
|
|
3317
|
+
.option('--json', OPTION_OUTPUT_AS_JSON)
|
|
3165
3318
|
.option('--preset <id>', 'Apply a shareable preset (repeatable)', (v, prev) => prev.concat([v]), [])
|
|
3166
3319
|
.action(async (file, options) => {
|
|
3167
3320
|
try {
|
|
@@ -3192,7 +3345,6 @@ program
|
|
|
3192
3345
|
}
|
|
3193
3346
|
const analyzerConfigs = (config.analyzerConfigs ?? {});
|
|
3194
3347
|
const pathProfiles = (config.pathProfiles ?? []);
|
|
3195
|
-
const enabledAnalyzers = (config.enabledAnalyzers ?? []);
|
|
3196
3348
|
// Resolve --preset ids to ordered preset objects (Spec 38 R4). Unknown
|
|
3197
3349
|
// ids are a hard error, not a silent skip.
|
|
3198
3350
|
const presetIds = Array.isArray(options.preset) ? options.preset : (options.preset ? [options.preset] : []);
|
|
@@ -3212,7 +3364,6 @@ program
|
|
|
3212
3364
|
projectRoot,
|
|
3213
3365
|
analyzerConfigs,
|
|
3214
3366
|
pathProfiles,
|
|
3215
|
-
enabledAnalyzers,
|
|
3216
3367
|
presets,
|
|
3217
3368
|
lintConfig,
|
|
3218
3369
|
});
|
|
@@ -3325,7 +3476,7 @@ program
|
|
|
3325
3476
|
for (const v of sorted) {
|
|
3326
3477
|
const icon = v.severity === 'critical' ? '🔴' :
|
|
3327
3478
|
v.severity === 'severe' ? '🟠' : '🟡';
|
|
3328
|
-
console.log(`${icon} ${chalk.bold(v.file)}${v.line
|
|
3479
|
+
console.log(`${icon} ${chalk.bold(v.file)}${lineSuffix(v.line)} [${v.rule || v.severity}] ${v.message}`);
|
|
3329
3480
|
}
|
|
3330
3481
|
}
|
|
3331
3482
|
catch (error) {
|
|
@@ -3390,8 +3541,8 @@ const daemonCmd = program
|
|
|
3390
3541
|
daemonCmd
|
|
3391
3542
|
.command('start')
|
|
3392
3543
|
.description('Start the daemon for this project (detached by default)')
|
|
3393
|
-
.option('-p, --path <path>',
|
|
3394
|
-
.option('-c, --config <config>',
|
|
3544
|
+
.option('-p, --path <path>', OPTION_PROJECT_ROOT, process.cwd())
|
|
3545
|
+
.option('-c, --config <config>', OPTION_CONFIGURATION_NAME)
|
|
3395
3546
|
.option('--foreground', 'Run in the foreground (blocking; useful for debugging)')
|
|
3396
3547
|
.action(async (options) => {
|
|
3397
3548
|
try {
|
|
@@ -3422,7 +3573,7 @@ daemonCmd
|
|
|
3422
3573
|
daemonCmd
|
|
3423
3574
|
.command('stop')
|
|
3424
3575
|
.description('Ask the daemon to shut down')
|
|
3425
|
-
.option('-p, --path <path>',
|
|
3576
|
+
.option('-p, --path <path>', OPTION_PROJECT_ROOT, process.cwd())
|
|
3426
3577
|
.action(async (options) => {
|
|
3427
3578
|
try {
|
|
3428
3579
|
const { resolveDaemonSocketPath } = await import('./dataPaths.js');
|
|
@@ -3444,8 +3595,8 @@ daemonCmd
|
|
|
3444
3595
|
daemonCmd
|
|
3445
3596
|
.command('status')
|
|
3446
3597
|
.description('Show daemon status')
|
|
3447
|
-
.option('-p, --path <path>',
|
|
3448
|
-
.option('--json',
|
|
3598
|
+
.option('-p, --path <path>', OPTION_PROJECT_ROOT, process.cwd())
|
|
3599
|
+
.option('--json', OPTION_OUTPUT_AS_JSON)
|
|
3449
3600
|
.action(async (options) => {
|
|
3450
3601
|
try {
|
|
3451
3602
|
const { resolveDaemon } = await import('./daemon/resolve.js');
|
|
@@ -3638,6 +3789,136 @@ async function generateConfigurations(options) {
|
|
|
3638
3789
|
console.log(chalk.gray(' 2. Run ') + chalk.cyan('code-audit') + chalk.gray(' to enforce your rules'));
|
|
3639
3790
|
console.log(chalk.gray(' 3. Use ') + chalk.cyan('code-audit changed') + chalk.gray(' in your agent hook (gating rules block on new readings)'));
|
|
3640
3791
|
}
|
|
3792
|
+
async function promptKindSpecificFields(kind, rule) {
|
|
3793
|
+
switch (kind) {
|
|
3794
|
+
case 'import-ban': {
|
|
3795
|
+
const { module } = await inquirer.prompt({
|
|
3796
|
+
module: {
|
|
3797
|
+
type: 'input',
|
|
3798
|
+
message: 'Banned module specifier (e.g. "lodash" or "@old-lib/*"):',
|
|
3799
|
+
validate: (input) => input.trim() ? true : 'Module specifier is required',
|
|
3800
|
+
},
|
|
3801
|
+
});
|
|
3802
|
+
rule.module = module;
|
|
3803
|
+
const { addExcept } = await inquirer.prompt({
|
|
3804
|
+
addExcept: {
|
|
3805
|
+
type: 'confirm',
|
|
3806
|
+
message: 'Add exception paths (files allowed to import it)?',
|
|
3807
|
+
default: false,
|
|
3808
|
+
},
|
|
3809
|
+
});
|
|
3810
|
+
if (addExcept) {
|
|
3811
|
+
const { except } = await inquirer.prompt({
|
|
3812
|
+
except: {
|
|
3813
|
+
type: 'input',
|
|
3814
|
+
message: 'Exception globs (comma-separated, e.g. "src/migration/**"):',
|
|
3815
|
+
},
|
|
3816
|
+
});
|
|
3817
|
+
const exceptList = except.split(',').map((s) => s.trim()).filter(Boolean);
|
|
3818
|
+
if (exceptList.length > 0)
|
|
3819
|
+
rule.except = exceptList;
|
|
3820
|
+
}
|
|
3821
|
+
break;
|
|
3822
|
+
}
|
|
3823
|
+
case 'call-constraint': {
|
|
3824
|
+
const { callee } = await inquirer.prompt({
|
|
3825
|
+
callee: {
|
|
3826
|
+
type: 'input',
|
|
3827
|
+
message: 'Callee (function name, optionally path-qualified as "path/glob#name"):',
|
|
3828
|
+
validate: (input) => input.trim() ? true : 'Callee is required',
|
|
3829
|
+
},
|
|
3830
|
+
});
|
|
3831
|
+
rule.callee = callee;
|
|
3832
|
+
const { mode } = await inquirer.prompt({
|
|
3833
|
+
mode: {
|
|
3834
|
+
type: 'list',
|
|
3835
|
+
message: 'Restriction mode:',
|
|
3836
|
+
choices: [
|
|
3837
|
+
{ name: 'Allow only specific callers (allowFrom)', value: 'allow' },
|
|
3838
|
+
{ name: 'Deny specific callers (denyFrom)', value: 'deny' },
|
|
3839
|
+
],
|
|
3840
|
+
},
|
|
3841
|
+
});
|
|
3842
|
+
const { paths } = await inquirer.prompt({
|
|
3843
|
+
paths: {
|
|
3844
|
+
type: 'input',
|
|
3845
|
+
message: `Path globs (comma-separated) for ${mode === 'allow' ? 'allowFrom' : 'denyFrom'}:`,
|
|
3846
|
+
validate: (input) => input.trim() ? true : 'At least one path glob is required',
|
|
3847
|
+
},
|
|
3848
|
+
});
|
|
3849
|
+
const pathList = paths.split(',').map((s) => s.trim()).filter(Boolean);
|
|
3850
|
+
if (mode === 'allow') {
|
|
3851
|
+
rule.allowFrom = pathList;
|
|
3852
|
+
}
|
|
3853
|
+
else {
|
|
3854
|
+
rule.denyFrom = pathList;
|
|
3855
|
+
}
|
|
3856
|
+
break;
|
|
3857
|
+
}
|
|
3858
|
+
case 'module-boundary': {
|
|
3859
|
+
const { from, to } = await inquirer.prompt({
|
|
3860
|
+
from: {
|
|
3861
|
+
type: 'input',
|
|
3862
|
+
message: 'From (path glob for files that must not import):',
|
|
3863
|
+
validate: (input) => input.trim() ? true : '"from" path glob is required',
|
|
3864
|
+
},
|
|
3865
|
+
to: {
|
|
3866
|
+
type: 'input',
|
|
3867
|
+
message: 'To (path glob for files that must not be imported):',
|
|
3868
|
+
validate: (input) => input.trim() ? true : '"to" path glob is required',
|
|
3869
|
+
},
|
|
3870
|
+
});
|
|
3871
|
+
rule.from = from;
|
|
3872
|
+
rule.to = to;
|
|
3873
|
+
break;
|
|
3874
|
+
}
|
|
3875
|
+
case 'naming': {
|
|
3876
|
+
const { path, exports: exportPattern } = await inquirer.prompt({
|
|
3877
|
+
path: {
|
|
3878
|
+
type: 'input',
|
|
3879
|
+
message: 'Path (glob for files this rule applies to):',
|
|
3880
|
+
validate: (input) => input.trim() ? true : 'Path glob is required',
|
|
3881
|
+
},
|
|
3882
|
+
exports: {
|
|
3883
|
+
type: 'input',
|
|
3884
|
+
message: 'Exports regex (exported symbols must match, e.g. "^use[A-Z]"):',
|
|
3885
|
+
validate: (input) => input.trim() ? true : 'Exports regex is required',
|
|
3886
|
+
},
|
|
3887
|
+
});
|
|
3888
|
+
rule.path = path;
|
|
3889
|
+
rule.exports = exportPattern;
|
|
3890
|
+
break;
|
|
3891
|
+
}
|
|
3892
|
+
case 'ast-pattern': {
|
|
3893
|
+
const { pattern } = await inquirer.prompt({
|
|
3894
|
+
pattern: {
|
|
3895
|
+
type: 'input',
|
|
3896
|
+
message: 'AST pattern (ast-grep syntax, e.g. "new Function($$$)"):',
|
|
3897
|
+
validate: (input) => input.trim() ? true : 'Pattern is required',
|
|
3898
|
+
},
|
|
3899
|
+
});
|
|
3900
|
+
rule.pattern = pattern;
|
|
3901
|
+
const { addLanguage } = await inquirer.prompt({
|
|
3902
|
+
addLanguage: {
|
|
3903
|
+
type: 'confirm',
|
|
3904
|
+
message: 'Restrict to a specific language? (default: typescript)',
|
|
3905
|
+
default: false,
|
|
3906
|
+
},
|
|
3907
|
+
});
|
|
3908
|
+
if (addLanguage) {
|
|
3909
|
+
const { language } = await inquirer.prompt({
|
|
3910
|
+
language: {
|
|
3911
|
+
type: 'list',
|
|
3912
|
+
message: 'Language:',
|
|
3913
|
+
choices: ['typescript', 'javascript', 'go'],
|
|
3914
|
+
},
|
|
3915
|
+
});
|
|
3916
|
+
rule.language = language;
|
|
3917
|
+
}
|
|
3918
|
+
break;
|
|
3919
|
+
}
|
|
3920
|
+
}
|
|
3921
|
+
}
|
|
3641
3922
|
/**
|
|
3642
3923
|
* Interactive rule builder — walks the user through adding rules one at a time.
|
|
3643
3924
|
*/
|
|
@@ -3696,134 +3977,7 @@ async function buildRulesInteractively() {
|
|
|
3696
3977
|
message: common.message,
|
|
3697
3978
|
};
|
|
3698
3979
|
// Kind-specific fields
|
|
3699
|
-
|
|
3700
|
-
case 'import-ban': {
|
|
3701
|
-
const { module } = await inquirer.prompt({
|
|
3702
|
-
module: {
|
|
3703
|
-
type: 'input',
|
|
3704
|
-
message: 'Banned module specifier (e.g. "lodash" or "@old-lib/*"):',
|
|
3705
|
-
validate: (input) => input.trim() ? true : 'Module specifier is required',
|
|
3706
|
-
},
|
|
3707
|
-
});
|
|
3708
|
-
rule.module = module;
|
|
3709
|
-
const { addExcept } = await inquirer.prompt({
|
|
3710
|
-
addExcept: {
|
|
3711
|
-
type: 'confirm',
|
|
3712
|
-
message: 'Add exception paths (files allowed to import it)?',
|
|
3713
|
-
default: false,
|
|
3714
|
-
},
|
|
3715
|
-
});
|
|
3716
|
-
if (addExcept) {
|
|
3717
|
-
const { except } = await inquirer.prompt({
|
|
3718
|
-
except: {
|
|
3719
|
-
type: 'input',
|
|
3720
|
-
message: 'Exception globs (comma-separated, e.g. "src/migration/**"):',
|
|
3721
|
-
},
|
|
3722
|
-
});
|
|
3723
|
-
const exceptList = except.split(',').map((s) => s.trim()).filter(Boolean);
|
|
3724
|
-
if (exceptList.length > 0)
|
|
3725
|
-
rule.except = exceptList;
|
|
3726
|
-
}
|
|
3727
|
-
break;
|
|
3728
|
-
}
|
|
3729
|
-
case 'call-constraint': {
|
|
3730
|
-
const { callee } = await inquirer.prompt({
|
|
3731
|
-
callee: {
|
|
3732
|
-
type: 'input',
|
|
3733
|
-
message: 'Callee (function name, optionally path-qualified as "path/glob#name"):',
|
|
3734
|
-
validate: (input) => input.trim() ? true : 'Callee is required',
|
|
3735
|
-
},
|
|
3736
|
-
});
|
|
3737
|
-
rule.callee = callee;
|
|
3738
|
-
const { mode } = await inquirer.prompt({
|
|
3739
|
-
mode: {
|
|
3740
|
-
type: 'list',
|
|
3741
|
-
message: 'Restriction mode:',
|
|
3742
|
-
choices: [
|
|
3743
|
-
{ name: 'Allow only specific callers (allowFrom)', value: 'allow' },
|
|
3744
|
-
{ name: 'Deny specific callers (denyFrom)', value: 'deny' },
|
|
3745
|
-
],
|
|
3746
|
-
},
|
|
3747
|
-
});
|
|
3748
|
-
const { paths } = await inquirer.prompt({
|
|
3749
|
-
paths: {
|
|
3750
|
-
type: 'input',
|
|
3751
|
-
message: `Path globs (comma-separated) for ${mode === 'allow' ? 'allowFrom' : 'denyFrom'}:`,
|
|
3752
|
-
validate: (input) => input.trim() ? true : 'At least one path glob is required',
|
|
3753
|
-
},
|
|
3754
|
-
});
|
|
3755
|
-
const pathList = paths.split(',').map((s) => s.trim()).filter(Boolean);
|
|
3756
|
-
if (mode === 'allow') {
|
|
3757
|
-
rule.allowFrom = pathList;
|
|
3758
|
-
}
|
|
3759
|
-
else {
|
|
3760
|
-
rule.denyFrom = pathList;
|
|
3761
|
-
}
|
|
3762
|
-
break;
|
|
3763
|
-
}
|
|
3764
|
-
case 'module-boundary': {
|
|
3765
|
-
const { from, to } = await inquirer.prompt({
|
|
3766
|
-
from: {
|
|
3767
|
-
type: 'input',
|
|
3768
|
-
message: 'From (path glob for files that must not import):',
|
|
3769
|
-
validate: (input) => input.trim() ? true : '"from" path glob is required',
|
|
3770
|
-
},
|
|
3771
|
-
to: {
|
|
3772
|
-
type: 'input',
|
|
3773
|
-
message: 'To (path glob for files that must not be imported):',
|
|
3774
|
-
validate: (input) => input.trim() ? true : '"to" path glob is required',
|
|
3775
|
-
},
|
|
3776
|
-
});
|
|
3777
|
-
rule.from = from;
|
|
3778
|
-
rule.to = to;
|
|
3779
|
-
break;
|
|
3780
|
-
}
|
|
3781
|
-
case 'naming': {
|
|
3782
|
-
const { path, exports: exportPattern } = await inquirer.prompt({
|
|
3783
|
-
path: {
|
|
3784
|
-
type: 'input',
|
|
3785
|
-
message: 'Path (glob for files this rule applies to):',
|
|
3786
|
-
validate: (input) => input.trim() ? true : 'Path glob is required',
|
|
3787
|
-
},
|
|
3788
|
-
exports: {
|
|
3789
|
-
type: 'input',
|
|
3790
|
-
message: 'Exports regex (exported symbols must match, e.g. "^use[A-Z]"):',
|
|
3791
|
-
validate: (input) => input.trim() ? true : 'Exports regex is required',
|
|
3792
|
-
},
|
|
3793
|
-
});
|
|
3794
|
-
rule.path = path;
|
|
3795
|
-
rule.exports = exportPattern;
|
|
3796
|
-
break;
|
|
3797
|
-
}
|
|
3798
|
-
case 'ast-pattern': {
|
|
3799
|
-
const { pattern } = await inquirer.prompt({
|
|
3800
|
-
pattern: {
|
|
3801
|
-
type: 'input',
|
|
3802
|
-
message: 'AST pattern (ast-grep syntax, e.g. "new Function($$$)"):',
|
|
3803
|
-
validate: (input) => input.trim() ? true : 'Pattern is required',
|
|
3804
|
-
},
|
|
3805
|
-
});
|
|
3806
|
-
rule.pattern = pattern;
|
|
3807
|
-
const { addLanguage } = await inquirer.prompt({
|
|
3808
|
-
addLanguage: {
|
|
3809
|
-
type: 'confirm',
|
|
3810
|
-
message: 'Restrict to a specific language? (default: typescript)',
|
|
3811
|
-
default: false,
|
|
3812
|
-
},
|
|
3813
|
-
});
|
|
3814
|
-
if (addLanguage) {
|
|
3815
|
-
const { language } = await inquirer.prompt({
|
|
3816
|
-
language: {
|
|
3817
|
-
type: 'list',
|
|
3818
|
-
message: 'Language:',
|
|
3819
|
-
choices: ['typescript', 'javascript', 'go'],
|
|
3820
|
-
},
|
|
3821
|
-
});
|
|
3822
|
-
rule.language = language;
|
|
3823
|
-
}
|
|
3824
|
-
break;
|
|
3825
|
-
}
|
|
3826
|
-
}
|
|
3980
|
+
await promptKindSpecificFields(kind, rule);
|
|
3827
3981
|
rules.push(rule);
|
|
3828
3982
|
console.log(chalk.green(` ✓ Added rule "${rule.id}" [${rule.kind}]`));
|
|
3829
3983
|
const { cont } = await inquirer.prompt({
|
|
@@ -3956,7 +4110,7 @@ async function runSearch(query, options) {
|
|
|
3956
4110
|
await db.initialize();
|
|
3957
4111
|
// --definition: look up a specific symbol
|
|
3958
4112
|
if (options.definition) {
|
|
3959
|
-
const func = await db.findDefinition(query);
|
|
4113
|
+
const func = await db.functionIndex.findDefinition(query);
|
|
3960
4114
|
if (!func) {
|
|
3961
4115
|
console.log(chalk.yellow(`No definition found for "${query}"`));
|
|
3962
4116
|
return;
|
|
@@ -3968,9 +4122,6 @@ async function runSearch(query, options) {
|
|
|
3968
4122
|
console.log(chalk.blue(`\nDefinition: ${func.name}`));
|
|
3969
4123
|
console.log(chalk.gray('──────────────────────────────────────────'));
|
|
3970
4124
|
console.log(`${chalk.bold('File:')} ${func.filePath}${func.lineNumber ? `:${func.lineNumber}` : ''}`);
|
|
3971
|
-
if (func.signature) {
|
|
3972
|
-
console.log(`${chalk.bold('Signature:')} ${func.signature}`);
|
|
3973
|
-
}
|
|
3974
4125
|
if (func.purpose) {
|
|
3975
4126
|
console.log(`${chalk.bold('Purpose:')} ${func.purpose}`);
|
|
3976
4127
|
}
|
|
@@ -4002,7 +4153,7 @@ async function runSearch(query, options) {
|
|
|
4002
4153
|
}
|
|
4003
4154
|
searchOptions.filters.language = options.language;
|
|
4004
4155
|
}
|
|
4005
|
-
const result = await db.searchFunctions(searchOptions);
|
|
4156
|
+
const result = await db.search.searchFunctions(searchOptions);
|
|
4006
4157
|
if (options.json) {
|
|
4007
4158
|
process.stdout.write(JSON.stringify({
|
|
4008
4159
|
query,
|
|
@@ -4025,9 +4176,6 @@ async function runSearch(query, options) {
|
|
|
4025
4176
|
const lang = func.language ? chalk.dim(` [${func.language}]`) : '';
|
|
4026
4177
|
const lineInfo = func.lineNumber ? chalk.dim(`:${func.lineNumber}`) : '';
|
|
4027
4178
|
console.log(`${chalk.bold(func.name)}${lang} — ${chalk.green(func.filePath)}${lineInfo}`);
|
|
4028
|
-
if (func.signature) {
|
|
4029
|
-
console.log(chalk.dim(` ${func.signature}`));
|
|
4030
|
-
}
|
|
4031
4179
|
if (func.purpose) {
|
|
4032
4180
|
console.log(chalk.dim(` ${func.purpose}`));
|
|
4033
4181
|
}
|