code-auditor-mcp 4.1.1 → 5.0.2
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 +381 -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 +427 -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 +348 -0
- package/dist/analyzers/receiverRoot.d.ts.map +1 -0
- package/dist/analyzers/receiverRoot.js +1032 -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 +241 -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 +66 -1
- package/dist/auditRunner.d.ts.map +1 -1
- package/dist/auditRunner.js +1544 -1034
- 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 +983 -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 +22 -4
- 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/graph/resolutionEnvironment.d.ts +61 -0
- package/dist/graph/resolutionEnvironment.d.ts.map +1 -0
- package/dist/graph/resolutionEnvironment.js +57 -0
- package/dist/graph/resolutionEnvironment.js.map +1 -0
- 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 +491 -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 +1030 -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 +207 -0
- package/dist/phase/receiverConsumers.d.ts.map +1 -0
- package/dist/phase/receiverConsumers.js +1063 -0
- package/dist/phase/receiverConsumers.js.map +1 -0
- package/dist/phase/receiverProvenance.d.ts +120 -0
- package/dist/phase/receiverProvenance.d.ts.map +1 -0
- package/dist/phase/receiverProvenance.js +377 -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 +2199 -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 +114 -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 +18 -9
- package/plugin/.claude-plugin/manifest.json +16 -0
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/hooks/hooks.json +7 -3
- package/plugin/scripts/hook-audit.sh +46 -16
- package/plugin/scripts/hook-common.sh +81 -12
- package/plugin/scripts/hook-drift-check.sh +76 -0
- package/plugin/scripts/hook-self-audit.sh +22 -14
- package/plugin/scripts/hook-warm.sh +5 -6
- package/plugin/skills/code-auditor/SKILL.md +1 -1
- package/server.json +10 -4
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,461 @@ 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
|
+
const heldOutByProfile = result.summary?.heldOut ?? 0;
|
|
129
|
+
if (coverage && coverage.length > 0) {
|
|
130
|
+
const covFired = coverage.filter(c => c.state === 'fired').length;
|
|
131
|
+
const covClean = coverage.filter(c => c.state === 'clean').length;
|
|
132
|
+
const covIncomplete = coverage.filter(c => c.state === 'incomplete').length;
|
|
133
|
+
const covNotApplicable = coverage.filter(c => c.state === 'notApplicable').length;
|
|
134
|
+
const covCannotFire = coverage.filter(c => c.state === 'cannot-fire').length;
|
|
135
|
+
// Spec 69 §10 R4 follow-up — "clean" is not a printable verdict while any
|
|
136
|
+
// site is unproven (cannot-fire) or held out by a path profile: a rule that
|
|
137
|
+
// reports "clean" may have under-reported on access it could not observe.
|
|
138
|
+
const cleanSuppressed = unprovenSiteCount > 0 || heldOutByProfile > 0;
|
|
139
|
+
const cleanPart = cleanSuppressed ? '' : `${covClean} clean · `;
|
|
140
|
+
console.log(chalk.gray(`── Coverage panel ── ${covFired} fired · ${cleanPart}` +
|
|
141
|
+
`${covIncomplete} incomplete · ${covNotApplicable} not-applicable · ` +
|
|
142
|
+
`${covCannotFire} cannot-fire · ${unprovenSiteCount} unproven sites · ` +
|
|
143
|
+
`${heldOutByProfile} held out by profile`));
|
|
144
|
+
}
|
|
145
|
+
// ── Skipped analyzers (Spec 61) ──────────────────────────────
|
|
146
|
+
const skippedAnalyzers = (result.metadata?.diagnostics ?? []).filter((d) => d.kind === 'go-toolchain-missing' ||
|
|
147
|
+
d.kind === 'go-analyzer-wrong-arch' ||
|
|
148
|
+
d.kind === 'go-analyzer-build-failed');
|
|
149
|
+
if (skippedAnalyzers.length > 0) {
|
|
150
|
+
console.log(chalk.yellow(`── Skipped analyzers ── ${skippedAnalyzers.length}`));
|
|
151
|
+
for (const d of skippedAnalyzers) {
|
|
152
|
+
const name = d.analyzerName || 'go';
|
|
153
|
+
console.log(chalk.yellow(` ${name} [${d.kind}] — ${d.message}`));
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
// ── Ignored config keys (Spec 61 R1.4) ─────────────────────────
|
|
157
|
+
const ignoredConfigKeys = (result.metadata?.diagnostics ?? []).filter((d) => d.kind === 'config-key-rejected');
|
|
158
|
+
if (ignoredConfigKeys.length > 0) {
|
|
159
|
+
console.log(chalk.yellow(`── Ignored config keys ── ${ignoredConfigKeys.length}`));
|
|
160
|
+
for (const d of ignoredConfigKeys) {
|
|
161
|
+
console.log(chalk.yellow(` ${d.message}`));
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
// ── Cannot-fire sites (Spec 69 §10) ───────────────────────────
|
|
165
|
+
// The cannot-fire verdict surface is the release bar: every DB-access call
|
|
166
|
+
// site whose receiver could not be proven a handle must be *visible with its
|
|
167
|
+
// reason*, never capped or folded into a silent clean. Grouped by file so a
|
|
168
|
+
// reader can see exactly which files the table-reference and data-access
|
|
169
|
+
// rules may have under-reported on.
|
|
170
|
+
const allDiagnostics = result.metadata?.diagnostics ?? [];
|
|
171
|
+
const cannotFireSites = allDiagnostics.filter((d) => d.kind === 'cannot-fire');
|
|
172
|
+
if (cannotFireSites.length > 0) {
|
|
173
|
+
const byFile = new Map();
|
|
174
|
+
for (const d of cannotFireSites) {
|
|
175
|
+
const key = d.file ?? '(unknown)';
|
|
176
|
+
const entry = byFile.get(key) ?? { lines: [], reasons: new Set() };
|
|
177
|
+
if (typeof d.line === 'number')
|
|
178
|
+
entry.lines.push(d.line);
|
|
179
|
+
const rawReason = typeof d.details?.reason === 'string' ? d.details.reason : d.message;
|
|
180
|
+
const reason = sanitizeReason(rawReason);
|
|
181
|
+
if (reason)
|
|
182
|
+
entry.reasons.add(reason);
|
|
183
|
+
byFile.set(key, entry);
|
|
184
|
+
}
|
|
185
|
+
console.log(chalk.yellow(`── Cannot-fire sites ── ${cannotFireSites.length} unproven DB-access call sites across ${byFile.size} files`));
|
|
186
|
+
console.log(chalk.yellow(` Table-reference and data-access rules may report clean on access they could not observe.`));
|
|
187
|
+
for (const [file, entry] of [...byFile.entries()].sort((a, b) => a[0].localeCompare(b[0]))) {
|
|
188
|
+
const reasons = [...entry.reasons];
|
|
189
|
+
const shown = reasons.slice(0, 2).join(' │ ');
|
|
190
|
+
const more = reasons.length > 2 ? ` (+${reasons.length - 2} more reasons)` : '';
|
|
191
|
+
const loc = entry.lines.length === 1 ? ` (line ${entry.lines[0]})` : '';
|
|
192
|
+
console.log(chalk.yellow(` ${file} — ${entry.lines.length} site(s)${loc}: ${shown}${more}`));
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
// ── Coverage gaps (Spec 58 follow-up) ─────────────────────────
|
|
196
|
+
const otherCoverageGaps = allDiagnostics.filter((d) => d.kind === 'unresolved-query' || d.kind === 'unresolved-dynamic-import' || d.kind === 'undefined-class-not-found');
|
|
197
|
+
if (otherCoverageGaps.length > 0) {
|
|
198
|
+
const byKind = {};
|
|
199
|
+
for (const d of otherCoverageGaps)
|
|
200
|
+
byKind[d.kind] = (byKind[d.kind] ?? 0) + 1;
|
|
201
|
+
const totals = Object.entries(byKind)
|
|
202
|
+
.map(([kind, n]) => `${n} ${kind}`)
|
|
203
|
+
.join(' · ');
|
|
204
|
+
console.log(chalk.gray(`── Coverage gaps ── ${totals}`));
|
|
205
|
+
for (const d of otherCoverageGaps.slice(0, 20)) {
|
|
206
|
+
const loc = d.file ? `${d.file}${typeof d.line === 'number' ? `:${d.line}` : ''}` : '(unknown)';
|
|
207
|
+
console.log(chalk.gray(` ${loc} [${d.kind}] — ${d.message}`));
|
|
208
|
+
}
|
|
209
|
+
if (otherCoverageGaps.length > 20) {
|
|
210
|
+
console.log(chalk.gray(` … and ${otherCoverageGaps.length - 20} more`));
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
// ── Spec 60 R1 — test coverage (reporting, not detection) ─────
|
|
214
|
+
const testCoverage = result.metadata?.testCoverage;
|
|
215
|
+
if (testCoverage) {
|
|
216
|
+
const pct = testCoverage.total > 0
|
|
217
|
+
? ((testCoverage.tested / testCoverage.total) * 100).toFixed(1)
|
|
218
|
+
: '0.0';
|
|
219
|
+
let line = `tested ${testCoverage.tested}/${testCoverage.total} (${pct}%)` +
|
|
220
|
+
` · untested-live ${testCoverage.untestedLive}` +
|
|
221
|
+
` · untested-dead ${testCoverage.untestedDead}`;
|
|
222
|
+
if (testCoverage.deadDrop > 0) {
|
|
223
|
+
line += ` (was ${testCoverage.deadPreException} pre-exception; −${testCoverage.deadDrop} entry points exempted)`;
|
|
224
|
+
}
|
|
225
|
+
console.log(chalk.gray(`── Test coverage ── ${line}`));
|
|
226
|
+
}
|
|
227
|
+
// ── Spec 60 R2 — size distributions (median / p95 / max) ──────
|
|
228
|
+
const sizeDistributions = result.metadata?.sizeDistributions;
|
|
229
|
+
if (sizeDistributions && sizeDistributions.length > 0) {
|
|
230
|
+
console.log(chalk.gray(`── Size distributions ──`));
|
|
231
|
+
for (const d of sizeDistributions) {
|
|
232
|
+
const fmt = (n) => Number.isInteger(n) ? String(n) : n.toFixed(1);
|
|
233
|
+
const tail = d.maxEntry
|
|
234
|
+
? ` (${d.maxEntry.name}${d.maxEntry.fileType ? ` · ${d.maxEntry.fileType}` : ''} · ${d.maxEntry.entityType})`
|
|
235
|
+
: '';
|
|
236
|
+
const pop = d.population ? `${d.population}, n=${d.count}` : `n=${d.count}`;
|
|
237
|
+
console.log(chalk.gray(` ${d.measure} [${pop}]: median ${fmt(d.median)} · p95 ${fmt(d.p95)} · max ${fmt(d.max)}${tail}`));
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
// ── Spec 60 R3 — dead-and-duplicated clusters ─────────────────
|
|
241
|
+
const deadClusters = result.metadata?.deadClusters;
|
|
242
|
+
if (deadClusters && deadClusters.length > 0) {
|
|
243
|
+
console.log(chalk.gray(`── Dead-and-duplicated ── ${deadClusters.length} clusters`));
|
|
244
|
+
for (const c of deadClusters.slice(0, 10)) {
|
|
245
|
+
console.log(chalk.gray(` ${c.count} × ${c.basename}`));
|
|
246
|
+
}
|
|
247
|
+
if (deadClusters.length > 10) {
|
|
248
|
+
console.log(chalk.gray(` … and ${deadClusters.length - 10} more clusters`));
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Print the findings summary: the delta view when a baseline exists, or the
|
|
254
|
+
* absolute summary otherwise. Recomputes the dismissed/gating suffixes so the
|
|
255
|
+
* summary is self-contained.
|
|
256
|
+
*/
|
|
257
|
+
function printFindingSummary(result, violations, baseline, options) {
|
|
258
|
+
// Spec 57 — dismissed count is reported alongside the total, never
|
|
259
|
+
// subtracted from it ("43 findings, 3 dismissed").
|
|
260
|
+
const dismissedCount = result.summary.dismissed ?? 0;
|
|
261
|
+
const dismissedSuffix = dismissedCount > 0 ? `, ${dismissedCount} dismissed` : '';
|
|
262
|
+
const gateExcludedCount = violations.filter((v) => v.gateExcluded).length;
|
|
263
|
+
const gatingCount = (severity) => violations.filter((v) => v.severity === severity && !v.gateExcluded && !v.dismissed).length;
|
|
264
|
+
// Spec 69 §10 R4 follow-up — the headline states findings, unproven sites,
|
|
265
|
+
// and held-out readings together. Held-out readings are named "held out by
|
|
266
|
+
// profile" (they still count in the total but do not block the gate).
|
|
267
|
+
const heldOutByProfile = result.summary?.heldOut ?? gateExcludedCount;
|
|
268
|
+
const unprovenSites = result.summary?.unprovenSites ?? 0;
|
|
269
|
+
const findingsSuffix = `${dismissedSuffix}` +
|
|
270
|
+
`${unprovenSites > 0 ? ` · ${unprovenSites.toLocaleString()} unproven sites` : ''}` +
|
|
271
|
+
`${heldOutByProfile > 0 ? ` · ${heldOutByProfile.toLocaleString()} held out by profile` : ''}`;
|
|
272
|
+
const summaryLine = (label, total, severity) => gateExcludedCount > 0
|
|
273
|
+
? `${label}: ${total} (${gatingCount(severity)} gating)`
|
|
274
|
+
: `${label}: ${total}`;
|
|
275
|
+
// ── Delta output (Spec 18 R2) ─────────────────────────────────
|
|
276
|
+
if (baseline && !options.full) {
|
|
277
|
+
const newViolations = violations.filter((v) => v.new === true);
|
|
278
|
+
const knownCount = baseline.knownCount ?? 0;
|
|
279
|
+
const fixedCount = baseline.fixedCount ?? 0;
|
|
280
|
+
const previousKnown = baseline.previousKnownCount ?? 0;
|
|
281
|
+
const currentDebt = newViolations.length + knownCount;
|
|
282
|
+
const debtDelta = currentDebt - previousKnown;
|
|
283
|
+
const trendIcon = debtDelta > 0 ? '↑' : debtDelta < 0 ? '↓' : '→';
|
|
284
|
+
const trendLabel = debtDelta > 0
|
|
285
|
+
? `(debt increased since last baseline)`
|
|
286
|
+
: debtDelta < 0
|
|
287
|
+
? `(debt decreased since last baseline)`
|
|
288
|
+
: '(unchanged)';
|
|
289
|
+
console.log(`\n📊 Delta: +${newViolations.length} new · −${fixedCount} fixed · ${knownCount} known ${trendIcon} ${trendLabel}`);
|
|
290
|
+
if (newViolations.length > 0) {
|
|
291
|
+
console.log(chalk.gray(`\n── New readings (${newViolations.length}) ──────────────────────────`));
|
|
292
|
+
for (const v of newViolations) {
|
|
293
|
+
const icon = v.severity === 'critical' ? '🔴' :
|
|
294
|
+
v.severity === 'severe' ? '🟠' : '🟡';
|
|
295
|
+
console.log(`${icon} ${chalk.bold(v.file)}${lineSuffix(v.line)} [${v.severity}] ${v.message}`);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
else {
|
|
299
|
+
console.log(chalk.green('\n✓ No new readings since last baseline.'));
|
|
300
|
+
if (knownCount > 0) {
|
|
301
|
+
console.log(chalk.gray(` ${knownCount} known reading(s) are still open — recorded, not resolved.`));
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
// Debt by analyzer
|
|
305
|
+
console.log(chalk.gray(`\n── Debt by Analyzer ──────────────────────────`));
|
|
306
|
+
const analyzerCounts = {};
|
|
307
|
+
for (const v of violations) {
|
|
308
|
+
const a = v.analyzer || 'unknown';
|
|
309
|
+
if (!analyzerCounts[a])
|
|
310
|
+
analyzerCounts[a] = { known: 0, new: 0 };
|
|
311
|
+
if (v.new === false)
|
|
312
|
+
analyzerCounts[a].known++;
|
|
313
|
+
else if (v.new === true)
|
|
314
|
+
analyzerCounts[a].new++;
|
|
315
|
+
}
|
|
316
|
+
for (const [analyzer, counts] of Object.entries(analyzerCounts).sort()) {
|
|
317
|
+
const newPart = counts.new > 0 ? ` (+${counts.new})` : '';
|
|
318
|
+
console.log(`${analyzer}: ${counts.known.toLocaleString()} known${newPart}`);
|
|
319
|
+
}
|
|
320
|
+
// Top files
|
|
321
|
+
console.log(chalk.gray(`\n── Top Files ─────────────────────────────────`));
|
|
322
|
+
const fileCounts = new Map();
|
|
323
|
+
for (const v of violations) {
|
|
324
|
+
const f = v.file || '';
|
|
325
|
+
fileCounts.set(f, (fileCounts.get(f) || 0) + 1);
|
|
326
|
+
}
|
|
327
|
+
const topFiles = [...fileCounts.entries()]
|
|
328
|
+
.sort((a, b) => b[1] - a[1])
|
|
329
|
+
.slice(0, 5);
|
|
330
|
+
for (const [file, count] of topFiles) {
|
|
331
|
+
console.log(`${file} — ${count} reading${count !== 1 ? 's' : ''}`);
|
|
332
|
+
}
|
|
333
|
+
console.log(chalk.gray(`\n💡 Run ${chalk.cyan('code-audit --full')} to see all ${currentDebt.toLocaleString()} readings.`));
|
|
334
|
+
}
|
|
335
|
+
else if (!baseline) {
|
|
336
|
+
// No baseline: current behavior + hint
|
|
337
|
+
console.log(`\nFound ${result.summary.totalViolations} findings${findingsSuffix}`);
|
|
338
|
+
console.log(summaryLine('Critical', result.summary.criticalIssues, 'critical'));
|
|
339
|
+
console.log(summaryLine('Severe', result.summary.severe, 'severe'));
|
|
340
|
+
console.log(summaryLine('High', result.summary.high, 'high'));
|
|
341
|
+
console.log(chalk.gray(`\nEvery reading is a defect — severity is urgency, the order to act.`));
|
|
342
|
+
console.log(chalk.gray(`\n💡 Run ${chalk.cyan('code-audit baseline')} to adopt the ratchet and track changes over time.`));
|
|
343
|
+
}
|
|
344
|
+
else {
|
|
345
|
+
// --full with baseline: full itemized inventory (current behavior)
|
|
346
|
+
console.log(`\nFound ${result.summary.totalViolations} findings${findingsSuffix}`);
|
|
347
|
+
console.log(summaryLine('Critical', result.summary.criticalIssues, 'critical'));
|
|
348
|
+
console.log(summaryLine('Severe', result.summary.severe, 'severe'));
|
|
349
|
+
console.log(summaryLine('High', result.summary.high, 'high'));
|
|
350
|
+
console.log(chalk.gray(`\nEvery reading is a defect — severity is urgency, the order to act.`));
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
/**
|
|
354
|
+
* Print unparsed-file failures and skipped-extension notice.
|
|
355
|
+
*/
|
|
356
|
+
function printUnparsedAndSkipped(result) {
|
|
357
|
+
// Spec 32 — unparsed files are never silent.
|
|
358
|
+
const unparsedFiles = result.metadata?.unparsedFiles ?? [];
|
|
359
|
+
if (unparsedFiles.length > 0) {
|
|
360
|
+
console.error(chalk.red(`\n⚠️ ${unparsedFiles.length} file${unparsedFiles.length !== 1 ? 's' : ''} failed to parse:`));
|
|
361
|
+
for (const u of unparsedFiles.slice(0, 20)) {
|
|
362
|
+
console.error(` ${u.filePath} — ${u.reason}`);
|
|
363
|
+
}
|
|
364
|
+
if (unparsedFiles.length > 20) {
|
|
365
|
+
console.error(` … and ${unparsedFiles.length - 20} more`);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
// Spec 43 R5 follow-up — extensions present on disk that discovery skipped.
|
|
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
|
+
}
|
|
376
|
+
/**
|
|
377
|
+
* Print per-analyzer pipeline activity and the detailed coverage breakdown.
|
|
378
|
+
*/
|
|
379
|
+
function printPipelineStages(result, options) {
|
|
380
|
+
if (options.json)
|
|
381
|
+
return;
|
|
382
|
+
const analyzerFiles = [];
|
|
383
|
+
for (const [name, ar] of Object.entries(result.analyzerResults)) {
|
|
384
|
+
const vCount = ar.violations.length;
|
|
385
|
+
if (isVisitorStatus(ar.status)) {
|
|
386
|
+
analyzerFiles.push(`${name} [visitor-ran]: ${getFilesProcessed(ar.status)} files, ${vCount} violations`);
|
|
387
|
+
}
|
|
388
|
+
else if (isReducerStatus(ar.status)) {
|
|
389
|
+
analyzerFiles.push(`${name} [reducer-ran]: ${getFactsConsumed(ar.status)} facts, ${vCount} violations`);
|
|
390
|
+
}
|
|
391
|
+
else {
|
|
392
|
+
analyzerFiles.push(`${name} [notRun]: ${vCount} violations`);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
if (analyzerFiles.length > 0) {
|
|
396
|
+
console.log(chalk.gray(`\n── Pipeline Stages ──────────────────────────`));
|
|
397
|
+
console.log(analyzerFiles.join('\n'));
|
|
398
|
+
}
|
|
399
|
+
// ── Coverage (Spec 27) ──────────────────────────────────────────
|
|
400
|
+
const coverage = result.metadata?.coverage;
|
|
401
|
+
if (coverage && coverage.length > 0) {
|
|
402
|
+
const fired = coverage.filter(c => c.state === 'fired');
|
|
403
|
+
const clean = coverage.filter(c => c.state === 'clean');
|
|
404
|
+
const incomplete = coverage.filter(c => c.state === 'incomplete');
|
|
405
|
+
const notApplicable = coverage.filter(c => c.state === 'notApplicable');
|
|
406
|
+
const cannotFire = coverage.filter(c => c.state === 'cannot-fire');
|
|
407
|
+
const firedCount = fired.reduce((s, c) => s + c.count, 0);
|
|
408
|
+
console.log(chalk.gray(`\n── Coverage ─────────────────────────────────`));
|
|
409
|
+
console.log(` ${fired.length} fired (${firedCount.toLocaleString()} violations), ` +
|
|
410
|
+
`${clean.length} clean, ` +
|
|
411
|
+
`${incomplete.length} incomplete, ` +
|
|
412
|
+
`${notApplicable.length} notApplicable, ` +
|
|
413
|
+
`${cannotFire.length} cannot-fire ` +
|
|
414
|
+
`(${coverage.length} rules registered)`);
|
|
415
|
+
if (cannotFire.length > 0) {
|
|
416
|
+
console.log(chalk.yellow(` ── Cannot Fire (broken in the tool) ──`));
|
|
417
|
+
for (const c of cannotFire) {
|
|
418
|
+
console.log(` ${c.ruleId}: ${c.reason ?? 'unknown'}`);
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
if (notApplicable.length > 0) {
|
|
422
|
+
console.log(chalk.gray(` ── Not Applicable ──`));
|
|
423
|
+
for (const c of notApplicable) {
|
|
424
|
+
console.log(` ${c.ruleId}: ${c.reason ?? 'unknown'}`);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
if (clean.length > 0) {
|
|
428
|
+
console.log(chalk.gray(` ── Clean ──`));
|
|
429
|
+
for (const c of clean) {
|
|
430
|
+
console.log(` ${c.ruleId} (0)`);
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
if (incomplete.length > 0) {
|
|
434
|
+
console.log(chalk.gray(` ── Incomplete (missing facts for some files) ──`));
|
|
435
|
+
for (const c of incomplete) {
|
|
436
|
+
console.log(` ${c.ruleId} (0)`);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
/**
|
|
442
|
+
* Generate and write the formatted report when --format is specified.
|
|
443
|
+
*/
|
|
444
|
+
async function writeReport(result, options) {
|
|
445
|
+
if (!options.format)
|
|
446
|
+
return;
|
|
447
|
+
const validFormats = ['html', 'json', 'csv', 'sarif'];
|
|
448
|
+
if (!validFormats.includes(options.format)) {
|
|
449
|
+
console.error(chalk.red(`Unknown format: "${options.format}". Must be one of: ${validFormats.join(', ')}`));
|
|
450
|
+
process.exit(1);
|
|
451
|
+
}
|
|
452
|
+
const { generateReport } = await import('./reporting/reportGenerator.js');
|
|
453
|
+
const { readVersionControlProvenance } = await import('./reporting/sarifReportGenerator.js');
|
|
454
|
+
const rootDir = resolve(options.path || process.cwd());
|
|
455
|
+
const config = options.format === 'sarif'
|
|
456
|
+
? { rootDir, ...readVersionControlProvenance(rootDir) }
|
|
457
|
+
: { rootDir };
|
|
458
|
+
const report = generateReport(result, options.format, config);
|
|
459
|
+
const ext = options.format === 'sarif' ? 'sarif' : options.format;
|
|
460
|
+
if (options.output) {
|
|
461
|
+
const outputDir = resolve(options.output);
|
|
462
|
+
const reportPath = join(outputDir, `audit-report.${ext}`);
|
|
463
|
+
let exists = false;
|
|
464
|
+
try {
|
|
465
|
+
await fs.access(reportPath);
|
|
466
|
+
exists = true;
|
|
467
|
+
}
|
|
468
|
+
catch { /* ok */ }
|
|
469
|
+
if (exists && !options.overwrite) {
|
|
470
|
+
console.error(chalk.red(`Refusing to overwrite existing report ${reportPath} — pass --overwrite to replace it.`));
|
|
471
|
+
process.exit(1);
|
|
472
|
+
}
|
|
473
|
+
await fs.mkdir(dirname(reportPath), { recursive: true });
|
|
474
|
+
await fs.writeFile(reportPath, report, 'utf-8');
|
|
475
|
+
console.log(chalk.green(`\nReport written to ${reportPath}`));
|
|
476
|
+
}
|
|
477
|
+
else {
|
|
478
|
+
// No output path — default to stdout.
|
|
479
|
+
process.stdout.write(report + '\n');
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
/**
|
|
483
|
+
* Apply the exit-code gates: fail-on-regression, zero-files, parse-failure,
|
|
484
|
+
* and --fail-on severity thresholds.
|
|
485
|
+
*/
|
|
486
|
+
function applyFailOnGates(result, violations, baseline, options, failOnSeverity) {
|
|
487
|
+
// --fail-on-regression: compare total high debt to baseline snapshot
|
|
488
|
+
if (baseline && options.failOnRegression) {
|
|
489
|
+
const currentDebt = violations.filter((v) => v.new || v.new === false).length;
|
|
490
|
+
const snapshotDebt = baseline.previousKnownCount ?? 0;
|
|
491
|
+
if (currentDebt > snapshotDebt) {
|
|
492
|
+
console.error(chalk.red(`Debt regression: ${currentDebt - snapshotDebt} readings added without re-baselining.`));
|
|
493
|
+
process.exit(2);
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
// Pipeline-failure gate — the audit pipeline itself threw (schema drift,
|
|
497
|
+
// DB write failure, …). One error, reported once by the runner; this exits
|
|
498
|
+
// non-zero (broken tool) so a swallowed pipeline throw can never read as a
|
|
499
|
+
// clean pass.
|
|
500
|
+
{
|
|
501
|
+
const diagnostics = result.metadata?.diagnostics ?? [];
|
|
502
|
+
const pipelineErrors = diagnostics.filter((d) => d.kind === 'pipeline-error');
|
|
503
|
+
if (pipelineErrors.length > 0) {
|
|
504
|
+
console.error(`Pipeline failure: ${pipelineErrors[0].message}`);
|
|
505
|
+
process.exit(1);
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
// Zero-files gate — any enabled analyzer matching zero source files is a
|
|
509
|
+
// dark-analyzer failure; fail the run so the bug can't hide.
|
|
510
|
+
{
|
|
511
|
+
const diagnostics = result.metadata?.diagnostics ?? [];
|
|
512
|
+
const zeroFileWarnings = diagnostics.filter((d) => d.kind === 'zero-files');
|
|
513
|
+
if (zeroFileWarnings.length > 0) {
|
|
514
|
+
const names = zeroFileWarnings.map((d) => d.analyzerName ?? d.analyzer).join(', ');
|
|
515
|
+
console.error(`Zero-files failure: ${zeroFileWarnings.length} analyzer(s) matched zero source files (${names})`);
|
|
516
|
+
process.exit(2);
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
// Spec 32 — a run where any file failed to parse is incomplete and must
|
|
520
|
+
// not exit 0.
|
|
521
|
+
{
|
|
522
|
+
const unparsedFiles = result.metadata?.unparsedFiles ?? [];
|
|
523
|
+
if (unparsedFiles.length > 0) {
|
|
524
|
+
console.error(`Parse-failure: ${unparsedFiles.length} file(s) could not be parsed; analysis is incomplete.`);
|
|
525
|
+
process.exit(2);
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
// --fail-on: evaluate new + invariant findings only (unless --include-baseline)
|
|
529
|
+
if (failOnSeverity) {
|
|
530
|
+
const evaluableViolations = (baseline && !options.includeBaseline)
|
|
531
|
+
? violations.filter((v) => v.new || v.analyzer === 'invariants')
|
|
532
|
+
: violations;
|
|
533
|
+
const severityOrder = SEVERITIES;
|
|
534
|
+
const failIndex = severityOrder.indexOf(failOnSeverity);
|
|
535
|
+
const hasAtOrAbove = evaluableViolations.some((v) => {
|
|
536
|
+
// Spec 57 — a dismissed finding never blocks the gate.
|
|
537
|
+
if (v.dismissed)
|
|
538
|
+
return false;
|
|
539
|
+
// Spec 36 R4 — a finding from a path-profile-excluded file never blocks.
|
|
540
|
+
if (v.gateExcluded)
|
|
541
|
+
return false;
|
|
542
|
+
const vIndex = severityOrder.indexOf(v.severity);
|
|
543
|
+
return vIndex >= 0 && vIndex <= failIndex;
|
|
544
|
+
});
|
|
545
|
+
if (hasAtOrAbove) {
|
|
546
|
+
process.exit(2);
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
}
|
|
91
550
|
// Legacy audit command (default behavior)
|
|
92
551
|
program
|
|
93
552
|
.command('audit', { isDefault: true })
|
|
94
553
|
.description('Run code quality audit (default command)')
|
|
95
554
|
.option('-p, --path <path>', 'Path to audit', process.cwd())
|
|
96
|
-
.option('-c, --config <config>',
|
|
555
|
+
.option('-c, --config <config>', OPTION_CONFIGURATION_NAME)
|
|
97
556
|
.option('-o, --output <dir>', 'Output directory for reports')
|
|
98
557
|
.option('-f, --format <format>', 'Report format: html, json, csv, or sarif')
|
|
99
558
|
.option('--overwrite', 'Overwrite an existing report file at --output (default: refuse)')
|
|
@@ -103,9 +562,6 @@ program
|
|
|
103
562
|
.option('--fail-on-regression', 'Exit code 2 when total high debt exceeds the baseline snapshot')
|
|
104
563
|
.option('--preset <id>', 'Apply a shareable preset (repeatable)', (v, prev) => prev.concat([v]), [])
|
|
105
564
|
.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
565
|
.option('--explain-skipped', 'Print a per-reason breakdown of files dropped from analysis')
|
|
110
566
|
.action(async (options) => {
|
|
111
567
|
console.log(chalk.blue('🔍 Code Quality Audit Tool'));
|
|
@@ -138,302 +594,20 @@ program
|
|
|
138
594
|
}
|
|
139
595
|
}
|
|
140
596
|
}
|
|
141
|
-
const result = await
|
|
597
|
+
const result = await createAuditRunner({
|
|
142
598
|
projectRoot: options.path,
|
|
143
599
|
configName: options.config,
|
|
144
600
|
outputDirectory: options.output,
|
|
145
601
|
presets: presetIds
|
|
146
|
-
});
|
|
602
|
+
}).run();
|
|
147
603
|
const violations = Object.values(result.analyzerResults).flatMap((r) => r.violations || []);
|
|
148
604
|
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
|
-
}
|
|
605
|
+
printAuditDiagnostics(result);
|
|
606
|
+
printFindingSummary(result, violations, baseline, options);
|
|
350
607
|
// Spec 44 R4 — file accounting: analyzed/dropped totals + optional breakdown.
|
|
351
608
|
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
|
-
}
|
|
609
|
+
printUnparsedAndSkipped(result);
|
|
610
|
+
printPipelineStages(result, options);
|
|
437
611
|
// Spec-20 R4: built-in profile visibility — silent behavior changes
|
|
438
612
|
// are never acceptable. Notify when scripts-and-tests excludes findings
|
|
439
613
|
// from the blocking gate.
|
|
@@ -443,103 +617,8 @@ program
|
|
|
443
617
|
console.log(chalk.blue(`\nℹ️ ${builtinCapped.toLocaleString()} readings excluded from the blocking gate by built-in profile "scripts-and-tests" (scripts/tests/fixtures).`));
|
|
444
618
|
console.log(chalk.gray(` Set ${chalk.cyan('"builtin": false')} in .codeauditor.json to disable.`));
|
|
445
619
|
}
|
|
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
|
-
}
|
|
620
|
+
await writeReport(result, options);
|
|
621
|
+
applyFailOnGates(result, violations, baseline, options, failOnSeverity);
|
|
543
622
|
}
|
|
544
623
|
catch (error) {
|
|
545
624
|
console.error(chalk.red('Error:'), error);
|
|
@@ -556,7 +635,7 @@ program
|
|
|
556
635
|
.option('--fail-on-zero-files', 'Exit code 2 when any enabled analyzer matches zero source files', true)
|
|
557
636
|
.option('--stdin', 'Read file paths from stdin (one per line)')
|
|
558
637
|
.option('--explain-skipped', 'Print a per-reason breakdown of files dropped from analysis')
|
|
559
|
-
.option('-p, --path <projectPath>',
|
|
638
|
+
.option('-p, --path <projectPath>', OPTION_PROJECT_ROOT_PATH, process.cwd())
|
|
560
639
|
.action(async (paths, options) => {
|
|
561
640
|
try {
|
|
562
641
|
const fileSet = new Set();
|
|
@@ -592,11 +671,17 @@ program
|
|
|
592
671
|
// the gate never misses a finding. SARIF needs full run metadata, so it
|
|
593
672
|
// always runs in-process.
|
|
594
673
|
const projectRoot = resolve(options.path || process.cwd());
|
|
674
|
+
const hookMode = process.env.CODE_AUDITOR_HOOK === '1' || process.env.CODE_AUDITOR_HOOK === 'true';
|
|
595
675
|
let result;
|
|
596
676
|
if (resolvedPaths.length > 0 && options.format !== 'sarif') {
|
|
597
|
-
const { resolveDaemon, readDaemonDiagnostics } = await import('./daemon/resolve.js');
|
|
677
|
+
const { resolveDaemon, readDaemonDiagnostics, startDaemonDetached } = await import('./daemon/resolve.js');
|
|
598
678
|
const daemon = await resolveDaemon(projectRoot);
|
|
599
679
|
if (daemon.mode === 'not-ready') {
|
|
680
|
+
// Hook mode (Spec 68 §hook-contract): never block the edit on an audit the
|
|
681
|
+
// daemon can't serve yet — it is still indexing, and the finding arrives on
|
|
682
|
+
// a later edit. Say nothing, exit clean. A non-hook caller gets the status.
|
|
683
|
+
if (hookMode)
|
|
684
|
+
process.exit(0);
|
|
600
685
|
reportDaemonIndexing(daemon.state, !!options.json);
|
|
601
686
|
return;
|
|
602
687
|
}
|
|
@@ -605,14 +690,27 @@ program
|
|
|
605
690
|
if (diag && diag.status === 'ready' && diag.staleFiles.length === 0) {
|
|
606
691
|
result = await buildChangedResultFromDiagnostics(diag.diagnostics, projectRoot);
|
|
607
692
|
}
|
|
693
|
+
else if (hookMode) {
|
|
694
|
+
// A just-edited file whose re-audit hasn't landed is not trustworthy from
|
|
695
|
+
// cache. Report nothing; the finding arrives on the next edit.
|
|
696
|
+
process.exit(0);
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
else if (hookMode) {
|
|
700
|
+
// No daemon: enqueue by starting one detached, then exit. The hook reads
|
|
701
|
+
// results from the daemon's cache on later edits — it never computes them.
|
|
702
|
+
startDaemonDetached(projectRoot);
|
|
703
|
+
process.exit(0);
|
|
608
704
|
}
|
|
609
705
|
}
|
|
610
706
|
if (!result) {
|
|
707
|
+
// Hook mode never falls back to an in-process audit (Spec 68 §hook-contract).
|
|
708
|
+
if (hookMode)
|
|
709
|
+
process.exit(0);
|
|
611
710
|
await initParsers();
|
|
612
711
|
const runner = createAuditRunner({
|
|
613
712
|
projectRoot: options.path,
|
|
614
713
|
scope,
|
|
615
|
-
analyzerConcurrency: 4
|
|
616
714
|
});
|
|
617
715
|
result = await runner.run();
|
|
618
716
|
}
|
|
@@ -621,7 +719,7 @@ program
|
|
|
621
719
|
// Spec 57 — dismissed count is reported alongside the total, never
|
|
622
720
|
// subtracted from it. The hook surface (`changed`) is agent-facing, so it
|
|
623
721
|
// must show the same "N findings, M dismissed" figure as the full report.
|
|
624
|
-
const dismissedCount = result.summary
|
|
722
|
+
const dismissedCount = result.summary?.dismissed ?? 0;
|
|
625
723
|
// Blocking gate decision (Spec 45 R1/R4, Spec 54 R3), computed once so the
|
|
626
724
|
// agent-facing before/after count (Spec 45 A2) and the exit code agree on
|
|
627
725
|
// the same number. Every registered rule participates (R1); enforcement is
|
|
@@ -629,98 +727,21 @@ program
|
|
|
629
727
|
// {critical, severe, high} — every finding is a defect and every finding
|
|
630
728
|
// blocks; there is no configurable gate.
|
|
631
729
|
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.
|
|
730
|
+
await emitChangedOutput(result, violations, blocking, dismissedCount, options);
|
|
731
|
+
// Spec 38 R2/R3 — per-rule timing + gate cpu-time, opt-in by env var.
|
|
717
732
|
// Emitted to stderr so it never corrupts --json stdout. Slowest rule first.
|
|
733
|
+
// The gate metric is CPU time (auditCpuMs), not wall clock: the budget is
|
|
734
|
+
// about a slow *rule*, and CPU time isolates that from a slow machine.
|
|
718
735
|
if (process.env.CODE_AUDIT_RULE_TIMING === '1') {
|
|
719
736
|
const ruleTiming = result.metadata?.ruleTiming;
|
|
720
|
-
const gateMs = result.metadata?.
|
|
737
|
+
const gateMs = result.metadata?.auditCpuMs;
|
|
738
|
+
const gateWallMs = result.metadata?.auditDuration;
|
|
721
739
|
const lines = [];
|
|
722
740
|
lines.push('');
|
|
723
|
-
|
|
741
|
+
// §16 criterion 16 — report both wall-clock (what the hook's user feels)
|
|
742
|
+
// and CPU (what the rule actually burned); the budget asserts on CPU.
|
|
743
|
+
lines.push(`⏱ gate wall-clock: ${(gateWallMs ?? 0).toFixed(1)} ms`);
|
|
744
|
+
lines.push(`⏱ gate cpu-time: ${(gateMs ?? 0).toFixed(1)} ms (budget 400 ms)`);
|
|
724
745
|
lines.push('── per-rule timing (slowest first) ──');
|
|
725
746
|
if (ruleTiming && ruleTiming.length > 0) {
|
|
726
747
|
for (const t of ruleTiming) {
|
|
@@ -753,6 +774,17 @@ program
|
|
|
753
774
|
process.exit(2);
|
|
754
775
|
}
|
|
755
776
|
}
|
|
777
|
+
// Pipeline-failure gate — the audit pipeline itself threw. One error,
|
|
778
|
+
// reported once by the runner; exit non-zero (broken tool) so a swallowed
|
|
779
|
+
// pipeline throw can never read as a clean pass.
|
|
780
|
+
{
|
|
781
|
+
const diagnostics = result.metadata?.diagnostics ?? [];
|
|
782
|
+
const pipelineErrors = diagnostics.filter((d) => d.kind === 'pipeline-error');
|
|
783
|
+
if (pipelineErrors.length > 0) {
|
|
784
|
+
console.error(`Pipeline failure: ${pipelineErrors[0].message}`);
|
|
785
|
+
process.exit(1);
|
|
786
|
+
}
|
|
787
|
+
}
|
|
756
788
|
// Exit code based on --no-fail-on-zero-files (default-on in v3.4.8)
|
|
757
789
|
// Any enabled analyzer matching zero source files is a failure — a >50%
|
|
758
790
|
// threshold lets the exact bug (3/9 analyzers dark) pass silently.
|
|
@@ -771,6 +803,96 @@ program
|
|
|
771
803
|
process.exit(1);
|
|
772
804
|
}
|
|
773
805
|
});
|
|
806
|
+
/**
|
|
807
|
+
* Emit the changed-audit output (SARIF, JSON, or console) for the hook surface.
|
|
808
|
+
* The SARIF branch dynamically imports its generator, so this is async.
|
|
809
|
+
*/
|
|
810
|
+
async function emitChangedOutput(result, violations, blocking, dismissedCount, options) {
|
|
811
|
+
// JSON output
|
|
812
|
+
if (options.format === 'sarif') {
|
|
813
|
+
const { generateSARIFReport, readVersionControlProvenance } = await import('./reporting/sarifReportGenerator.js');
|
|
814
|
+
const projectDir = resolve(options.path || process.cwd());
|
|
815
|
+
const sarifOutput = generateSARIFReport(result, { rootDir: projectDir, ...readVersionControlProvenance(projectDir) });
|
|
816
|
+
process.stdout.write(sarifOutput + '\n');
|
|
817
|
+
}
|
|
818
|
+
else if (options.json) {
|
|
819
|
+
const projectDir = resolve(options.path || process.cwd());
|
|
820
|
+
const jsonViolations = violations.map((v) => {
|
|
821
|
+
// Compute relative path if file resolves inside the project
|
|
822
|
+
let filePath = v.file || '';
|
|
823
|
+
if (filePath.startsWith('/') || filePath.startsWith('\\\\')) {
|
|
824
|
+
const rel = relative(projectDir, filePath);
|
|
825
|
+
// Only use relative path if it doesn't escape the project
|
|
826
|
+
if (!rel.startsWith('..') && !isAbsolute(rel)) {
|
|
827
|
+
filePath = rel;
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
// Find column: prefer explicit column, then start.column, then default to 1
|
|
831
|
+
const col = v.column ?? v.start?.column ?? 1;
|
|
832
|
+
return {
|
|
833
|
+
analyzer: v.analyzer || '',
|
|
834
|
+
rule: v.rule,
|
|
835
|
+
severity: v.severity,
|
|
836
|
+
message: v.message,
|
|
837
|
+
file: filePath,
|
|
838
|
+
line: v.line ?? v.start?.line,
|
|
839
|
+
column: col,
|
|
840
|
+
endLine: v.end?.line,
|
|
841
|
+
endColumn: v.end?.column,
|
|
842
|
+
enclosingSymbol: v.symbol || v.enclosingFunction || '',
|
|
843
|
+
suggestion: v.suggestion || '',
|
|
844
|
+
details: v.details || '',
|
|
845
|
+
...(v.new !== undefined && { new: v.new }),
|
|
846
|
+
...(v.dismissed !== undefined && { dismissed: v.dismissed }),
|
|
847
|
+
fingerprint: fingerprint(buildFingerprintInput(v))
|
|
848
|
+
};
|
|
849
|
+
});
|
|
850
|
+
// Coverage diagnostics ride alongside violations so an agent sees "the
|
|
851
|
+
// analyzer couldn't resolve this" without a metadata flag — and without
|
|
852
|
+
// it ever gating (diagnostics never reach computeGatingDecision).
|
|
853
|
+
const jsonDiagnostics = (result.metadata?.diagnostics ?? []).map((d) => ({
|
|
854
|
+
analyzer: d.analyzerName ?? d.analyzer ?? '',
|
|
855
|
+
kind: d.kind,
|
|
856
|
+
message: d.message,
|
|
857
|
+
...(d.file ? { file: d.file } : {}),
|
|
858
|
+
...(typeof d.line === 'number' ? { line: d.line } : {}),
|
|
859
|
+
...(d.details ? { details: d.details } : {}),
|
|
860
|
+
}));
|
|
861
|
+
const jsonOutput = { violations: jsonViolations, diagnostics: jsonDiagnostics };
|
|
862
|
+
process.stdout.write(JSON.stringify(jsonOutput, null, 2) + '\n');
|
|
863
|
+
}
|
|
864
|
+
else if (!options.quiet || violations.length > 0) {
|
|
865
|
+
// Console output — Spec 45 A2: agent-facing output emits counts (per
|
|
866
|
+
// analyzer/rule/severity, plus the before/after gate figure) alongside
|
|
867
|
+
// findings. The hook and the `changed` command are the agent's surface.
|
|
868
|
+
console.log(chalk.blue('🔍 Diff-Scoped Code Audit'));
|
|
869
|
+
console.log(chalk.gray('══════════════════════════════════════════════════'));
|
|
870
|
+
printFileAccounting(result, !!options.explainSkipped);
|
|
871
|
+
if (violations.length > 0) {
|
|
872
|
+
console.log('');
|
|
873
|
+
printCountSummary(violations);
|
|
874
|
+
console.log(chalk.bold(`gate before/after: ${violations.length} → ${blocking.length} blocking`));
|
|
875
|
+
if (dismissedCount > 0) {
|
|
876
|
+
console.log(chalk.dim(` ${dismissedCount} dismissed — still counted above, excluded from the gate`));
|
|
877
|
+
}
|
|
878
|
+
console.log(chalk.gray('── Violations ────────────────────────────────────'));
|
|
879
|
+
for (const v of violations) {
|
|
880
|
+
const icon = v.severity === 'critical' ? '🔴' :
|
|
881
|
+
v.severity === 'severe' ? '🟠' : '🟡';
|
|
882
|
+
const statusTag = v.new === false
|
|
883
|
+
? chalk.dim(' [known — still open]')
|
|
884
|
+
: '';
|
|
885
|
+
const dismissedTag = v.dismissed
|
|
886
|
+
? chalk.dim(' [dismissed]')
|
|
887
|
+
: '';
|
|
888
|
+
console.log(`${icon} ${chalk.bold(v.file)}${lineSuffix(v.line)} [${v.severity}] ${v.message}${statusTag}${dismissedTag}`);
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
else {
|
|
892
|
+
console.log(chalk.green('\n✓ No readings.'));
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
}
|
|
774
896
|
// Spec 45 A2 — counts are emitted where useful (per analyzer, per rule, per
|
|
775
897
|
// severity), on agent-facing surfaces as well as human ones. This reverts Spec
|
|
776
898
|
// 36 R3's "no bare counts": an agent now sees the shape of what fired instead
|
|
@@ -810,11 +932,61 @@ function printCountSummary(violations) {
|
|
|
810
932
|
*/
|
|
811
933
|
async function buildChangedResultFromDiagnostics(violations, projectRoot) {
|
|
812
934
|
const analyzerResults = {};
|
|
935
|
+
const byAnalyzer = {};
|
|
936
|
+
let totalViolations = 0;
|
|
937
|
+
let criticalIssues = 0;
|
|
938
|
+
let severe = 0;
|
|
939
|
+
let high = 0;
|
|
940
|
+
let dismissed = 0;
|
|
941
|
+
const violationsByCategory = {};
|
|
813
942
|
for (const v of violations) {
|
|
814
943
|
const key = v.analyzer || 'unknown';
|
|
815
944
|
(analyzerResults[key] = analyzerResults[key] || { violations: [] }).violations.push(v);
|
|
945
|
+
totalViolations++;
|
|
946
|
+
switch (v.severity) {
|
|
947
|
+
case 'critical':
|
|
948
|
+
criticalIssues++;
|
|
949
|
+
break;
|
|
950
|
+
case 'severe':
|
|
951
|
+
severe++;
|
|
952
|
+
break;
|
|
953
|
+
case 'high':
|
|
954
|
+
high++;
|
|
955
|
+
break;
|
|
956
|
+
}
|
|
957
|
+
const rule = v.rule || 'unknown';
|
|
958
|
+
violationsByCategory[rule] = (violationsByCategory[rule] ?? 0) + 1;
|
|
959
|
+
const bucket = byAnalyzer[key] ?? (byAnalyzer[key] = { violations: 0 });
|
|
960
|
+
bucket.violations++;
|
|
961
|
+
if (v.dismissed)
|
|
962
|
+
dismissed++;
|
|
816
963
|
}
|
|
817
|
-
|
|
964
|
+
const topIssues = Object.entries(violationsByCategory)
|
|
965
|
+
.sort(([, a], [, b]) => b - a)
|
|
966
|
+
.slice(0, 5)
|
|
967
|
+
.map(([type, count]) => ({ type, count }));
|
|
968
|
+
// A *real* summary derived from the diagnostics the daemon served — not a
|
|
969
|
+
// zero-filled placeholder (a stub satisfying the type, with a `dismissed: 0`
|
|
970
|
+
// that was never measured). Every field here is computed from the actual
|
|
971
|
+
// findings: severity/category/byAnalyzer rollups, plus `dismissed` from each
|
|
972
|
+
// diagnostic's own `dismissed` flag. `totalFiles` and the per-analyzer
|
|
973
|
+
// `filesProcessed`/`fatalErrors`/`unprovenSites` are not derivable from a flat
|
|
974
|
+
// per-file diagnostic list, so they are omitted; the guarded read
|
|
975
|
+
// (`result.summary?.dismissed ?? 0`) tolerates their absence.
|
|
976
|
+
return {
|
|
977
|
+
analyzerResults,
|
|
978
|
+
summary: {
|
|
979
|
+
totalViolations,
|
|
980
|
+
criticalIssues,
|
|
981
|
+
severe,
|
|
982
|
+
high,
|
|
983
|
+
violationsByCategory,
|
|
984
|
+
topIssues,
|
|
985
|
+
byAnalyzer,
|
|
986
|
+
dismissed,
|
|
987
|
+
},
|
|
988
|
+
metadata: { configUsed: undefined, diagnostics: [] },
|
|
989
|
+
};
|
|
818
990
|
}
|
|
819
991
|
// Self-audit gate (Spec 33 Item 15 + Spec 44 remediation). Runs the full
|
|
820
992
|
// analyzer pipeline over the tool's own production source and asserts zero
|
|
@@ -850,7 +1022,7 @@ program
|
|
|
850
1022
|
.description('Audit the tool\'s own analyzers/ + languages/ source and fail on blocking readings')
|
|
851
1023
|
.option('--json', 'Output violations as machine-readable JSON to stdout')
|
|
852
1024
|
.option('--stdin', 'Read file paths from stdin (one per line)')
|
|
853
|
-
.option('-p, --path <projectPath>',
|
|
1025
|
+
.option('-p, --path <projectPath>', OPTION_PROJECT_ROOT_PATH, process.cwd())
|
|
854
1026
|
.option('--fail-on <severity>', 'Blocking severity floor: critical, severe, or high', 'high')
|
|
855
1027
|
.action(async (paths, options) => {
|
|
856
1028
|
try {
|
|
@@ -889,7 +1061,6 @@ program
|
|
|
889
1061
|
const runner = createAuditRunner({
|
|
890
1062
|
projectRoot: options.path,
|
|
891
1063
|
scope,
|
|
892
|
-
analyzerConcurrency: 4
|
|
893
1064
|
});
|
|
894
1065
|
const result = await runner.run();
|
|
895
1066
|
const violations = Object.values(result.analyzerResults).flatMap((r) => r.violations || []);
|
|
@@ -934,7 +1105,7 @@ program
|
|
|
934
1105
|
for (const v of blocking) {
|
|
935
1106
|
const icon = v.severity === 'critical' ? '🔴' :
|
|
936
1107
|
v.severity === 'severe' ? '🟠' : '🟡';
|
|
937
|
-
console.log(`${icon} ${v.file}${v.line
|
|
1108
|
+
console.log(`${icon} ${v.file}${lineSuffix(v.line)} [${v.severity}] ${v.message}`);
|
|
938
1109
|
}
|
|
939
1110
|
}
|
|
940
1111
|
else {
|
|
@@ -960,8 +1131,8 @@ program
|
|
|
960
1131
|
program
|
|
961
1132
|
.command('next-file')
|
|
962
1133
|
.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>',
|
|
1134
|
+
.option('-p, --path <projectPath>', OPTION_PROJECT_ROOT_PATH, process.cwd())
|
|
1135
|
+
.option('-c, --config <config>', OPTION_CONFIGURATION_NAME)
|
|
965
1136
|
.option('--json', 'Output a single JSON object (or {done:true}) to stdout')
|
|
966
1137
|
.action(async (options) => {
|
|
967
1138
|
try {
|
|
@@ -1059,7 +1230,7 @@ function printNextFile(violations, summary, projectDir, json) {
|
|
|
1059
1230
|
console.log(chalk.gray('\n── Readings ────────────────────────────────────────'));
|
|
1060
1231
|
for (const v of ordered) {
|
|
1061
1232
|
const icon = v.severity === 'critical' ? '🔴' : v.severity === 'severe' ? '🟠' : '🟡';
|
|
1062
|
-
console.log(`${icon} ${chalk.bold(relativize(v.file || ''))}${v.line
|
|
1233
|
+
console.log(`${icon} ${chalk.bold(relativize(v.file || ''))}${lineSuffix(v.line)} [${v.severity}] ${v.rule} — ${v.message}`);
|
|
1063
1234
|
}
|
|
1064
1235
|
}
|
|
1065
1236
|
}
|
|
@@ -1067,8 +1238,8 @@ function printNextFile(violations, summary, projectDir, json) {
|
|
|
1067
1238
|
program
|
|
1068
1239
|
.command('baseline')
|
|
1069
1240
|
.description('Snapshot current advisory readings as the baseline (excludes invariants)')
|
|
1070
|
-
.option('-p, --path <path>',
|
|
1071
|
-
.option('--json',
|
|
1241
|
+
.option('-p, --path <path>', OPTION_PROJECT_PATH, process.cwd())
|
|
1242
|
+
.option('--json', OPTION_OUTPUT_AS_JSON)
|
|
1072
1243
|
.action(async (options) => {
|
|
1073
1244
|
try {
|
|
1074
1245
|
await initParsers();
|
|
@@ -1134,9 +1305,9 @@ program
|
|
|
1134
1305
|
.command('dismiss <fingerprint>')
|
|
1135
1306
|
.description('Dismiss one finding by fingerprint — a written reason is required')
|
|
1136
1307
|
.requiredOption('--reason <reason>', 'Why this finding is dismissed (required — a dismissal without a reason is a config error)')
|
|
1137
|
-
.option('-p, --path <path>',
|
|
1308
|
+
.option('-p, --path <path>', OPTION_PROJECT_PATH, process.cwd())
|
|
1138
1309
|
.option('--telemetry-endpoint <url>', 'Feedback service endpoint override (defaults to the opt-in endpoint recorded by the telemetry tool)')
|
|
1139
|
-
.option('--json',
|
|
1310
|
+
.option('--json', OPTION_OUTPUT_AS_JSON)
|
|
1140
1311
|
.action(async (fingerprintArg, options) => {
|
|
1141
1312
|
try {
|
|
1142
1313
|
const reason = (options.reason ?? '').trim();
|
|
@@ -1248,7 +1419,7 @@ indexCmd
|
|
|
1248
1419
|
.command('sync')
|
|
1249
1420
|
.description('Synchronize index from source files')
|
|
1250
1421
|
.option('--path <path>', 'Path to a specific file or directory', process.cwd())
|
|
1251
|
-
.option('--json',
|
|
1422
|
+
.option('--json', OPTION_OUTPUT_AS_JSON)
|
|
1252
1423
|
.action(async (options) => {
|
|
1253
1424
|
try {
|
|
1254
1425
|
await initParsers();
|
|
@@ -1289,64 +1460,65 @@ indexCmd
|
|
|
1289
1460
|
process.exit(1);
|
|
1290
1461
|
}
|
|
1291
1462
|
});
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1463
|
+
/** Register a simple `index` subcommand that runs one mutation on the
|
|
1464
|
+
* initialized index and prints the outcome (JSON or console). `cleanup` and
|
|
1465
|
+
* `reset` share the get-instance/initialize/json-vs-console/try-catch
|
|
1466
|
+
* scaffolding; only the mutation and its result prose differ, so they live
|
|
1467
|
+
* here once and each command is a thin `defineIndexMutationCommand(...)` call. */
|
|
1468
|
+
function defineIndexMutationCommand(name, description, work) {
|
|
1469
|
+
indexCmd
|
|
1470
|
+
.command(name)
|
|
1471
|
+
.description(description)
|
|
1472
|
+
.option('--json', OPTION_OUTPUT_AS_JSON)
|
|
1473
|
+
.action(async (options) => {
|
|
1474
|
+
try {
|
|
1475
|
+
const db = CodeIndexDB.getInstance();
|
|
1476
|
+
await db.initialize();
|
|
1477
|
+
const { extra, greenLine, grayLine } = await work(db);
|
|
1478
|
+
if (options.json) {
|
|
1479
|
+
process.stdout.write(JSON.stringify({ mode: name, success: true, ...extra }) + '\n');
|
|
1480
|
+
}
|
|
1481
|
+
else {
|
|
1482
|
+
console.log(chalk.green(greenLine));
|
|
1483
|
+
console.log(chalk.gray(grayLine));
|
|
1484
|
+
}
|
|
1303
1485
|
}
|
|
1304
|
-
|
|
1305
|
-
console.
|
|
1306
|
-
|
|
1486
|
+
catch (error) {
|
|
1487
|
+
console.error(chalk.red('Error:'), error);
|
|
1488
|
+
process.exit(1);
|
|
1307
1489
|
}
|
|
1308
|
-
}
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1490
|
+
});
|
|
1491
|
+
}
|
|
1492
|
+
defineIndexMutationCommand('cleanup', 'Remove index entries for deleted files', async (db) => {
|
|
1493
|
+
const result = await db.bulkCleanup();
|
|
1494
|
+
return {
|
|
1495
|
+
extra: result,
|
|
1496
|
+
greenLine: '✓ Cleanup complete',
|
|
1497
|
+
grayLine: `Removed ${result.removedCount} entries from ${result.removedFiles.length} deleted files`,
|
|
1498
|
+
};
|
|
1313
1499
|
});
|
|
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
|
-
}
|
|
1500
|
+
defineIndexMutationCommand('reset', 'Clear all analysis data (preserves tasks, config, whitelist)', async (db) => {
|
|
1501
|
+
await db.clearIndex();
|
|
1502
|
+
return {
|
|
1503
|
+
extra: { message: 'All analysis data cleared' },
|
|
1504
|
+
greenLine: '✓ Index reset complete',
|
|
1505
|
+
grayLine: 'All analysis-derived data cleared (tasks, config, and whitelist preserved)',
|
|
1506
|
+
};
|
|
1335
1507
|
});
|
|
1336
1508
|
// ── Index status command (Spec 14 R1) ──────────────────────────
|
|
1337
1509
|
indexCmd
|
|
1338
1510
|
.command('status')
|
|
1339
1511
|
.description('Show index health and graph statistics')
|
|
1340
|
-
.option('--json',
|
|
1512
|
+
.option('--json', OPTION_OUTPUT_AS_JSON)
|
|
1341
1513
|
.action(async (options) => {
|
|
1342
1514
|
try {
|
|
1343
1515
|
const db = CodeIndexDB.getInstance();
|
|
1344
1516
|
await db.initialize();
|
|
1345
|
-
const stats = await db.getStats();
|
|
1517
|
+
const stats = await db.functionIndex.getStats();
|
|
1346
1518
|
// Graph stats — non-fatal if graph cache is empty
|
|
1347
1519
|
let graphStats = null;
|
|
1348
1520
|
try {
|
|
1349
|
-
graphStats = db.getGraphStats();
|
|
1521
|
+
graphStats = db.graph.getGraphStats();
|
|
1350
1522
|
}
|
|
1351
1523
|
catch {
|
|
1352
1524
|
// No graph data yet — ok
|
|
@@ -1417,7 +1589,7 @@ configCmd
|
|
|
1417
1589
|
.command('rules-list')
|
|
1418
1590
|
.description('List all configured invariant rules from .codeauditor.json')
|
|
1419
1591
|
.option('--config-path <path>', 'Path to .codeauditor.json')
|
|
1420
|
-
.option('--json',
|
|
1592
|
+
.option('--json', OPTION_OUTPUT_AS_JSON)
|
|
1421
1593
|
.action(async (options) => {
|
|
1422
1594
|
try {
|
|
1423
1595
|
const pathModule = await import('path');
|
|
@@ -1427,7 +1599,9 @@ configCmd
|
|
|
1427
1599
|
try {
|
|
1428
1600
|
const raw = readFileSync(configPath, 'utf-8');
|
|
1429
1601
|
const config = JSON.parse(raw);
|
|
1430
|
-
|
|
1602
|
+
// `rules` is an array by schema; tolerate the `{}` shorthand some fixtures
|
|
1603
|
+
// and hand-edited configs use for "no rules" — `.map` on `{}` would throw.
|
|
1604
|
+
const rules = Array.isArray(config?.rules) ? config.rules : [];
|
|
1431
1605
|
if (options.json) {
|
|
1432
1606
|
process.stdout.write(JSON.stringify({
|
|
1433
1607
|
rules: rules.map((r) => ({ id: r.id, kind: r.kind, severity: r.severity, message: r.message || null })),
|
|
@@ -1465,7 +1639,7 @@ configCmd
|
|
|
1465
1639
|
.command('rules-check')
|
|
1466
1640
|
.description('Validate the current .codeauditor.json rules')
|
|
1467
1641
|
.option('--config-path <path>', 'Path to .codeauditor.json')
|
|
1468
|
-
.option('--json',
|
|
1642
|
+
.option('--json', OPTION_OUTPUT_AS_JSON)
|
|
1469
1643
|
.action(async (options) => {
|
|
1470
1644
|
try {
|
|
1471
1645
|
const pathModule = await import('path');
|
|
@@ -1513,9 +1687,9 @@ configCmd
|
|
|
1513
1687
|
configCmd
|
|
1514
1688
|
.command('profiles')
|
|
1515
1689
|
.description('Show active path profiles and resolve per-file matching')
|
|
1516
|
-
.option('-p, --path <path>',
|
|
1690
|
+
.option('-p, --path <path>', OPTION_PROJECT_PATH, process.cwd())
|
|
1517
1691
|
.option('--file <file>', 'Resolve profiles for a specific file')
|
|
1518
|
-
.option('--json',
|
|
1692
|
+
.option('--json', OPTION_OUTPUT_AS_JSON)
|
|
1519
1693
|
.action(async (options) => {
|
|
1520
1694
|
try {
|
|
1521
1695
|
const fs = await import('fs/promises');
|
|
@@ -1613,8 +1787,8 @@ configCmd
|
|
|
1613
1787
|
configCmd
|
|
1614
1788
|
.command('detection')
|
|
1615
1789
|
.description('Show DB-provenanced and inferred receiver identifiers with per-entry evidence')
|
|
1616
|
-
.option('-p, --path <path>',
|
|
1617
|
-
.option('--json',
|
|
1790
|
+
.option('-p, --path <path>', OPTION_PROJECT_PATH, process.cwd())
|
|
1791
|
+
.option('--json', OPTION_OUTPUT_AS_JSON)
|
|
1618
1792
|
.option('--mode <mode>', 'Detection mode filter (hybrid, provenance, names)')
|
|
1619
1793
|
.action(async (options) => {
|
|
1620
1794
|
try {
|
|
@@ -1666,9 +1840,7 @@ configCmd
|
|
|
1666
1840
|
// Build provenance context using schema analyzer config (most complete)
|
|
1667
1841
|
const ctx = buildProvenanceContext(ast, adapter, sourceCode, {
|
|
1668
1842
|
mode: detectionMode,
|
|
1669
|
-
dbReceiverNames: schemaCfg.dbReceiverNames ?? daCfg.dbReceiverNames,
|
|
1670
1843
|
dbBindingNames: schemaCfg.dbBindingNames ?? daCfg.dbBindingNames,
|
|
1671
|
-
dbCallMethods: schemaCfg.dbCallMethods ?? daCfg.dbCallMethods,
|
|
1672
1844
|
});
|
|
1673
1845
|
// Collect DB-provenanced
|
|
1674
1846
|
for (const [id, evidence] of ctx.dbProvenanced) {
|
|
@@ -1721,70 +1893,84 @@ configCmd
|
|
|
1721
1893
|
entry.validatorProvenanced.push(v);
|
|
1722
1894
|
}
|
|
1723
1895
|
for (const [file, data] of byFile) {
|
|
1724
|
-
db.storeFileProvenance(file, data);
|
|
1896
|
+
db.provenance.storeFileProvenance(file, data);
|
|
1725
1897
|
}
|
|
1726
1898
|
// Store inferred receivers
|
|
1727
|
-
db.storeInferredReceivers(inferred);
|
|
1899
|
+
db.inferredReceivers.storeInferredReceivers(inferred);
|
|
1728
1900
|
}
|
|
1729
1901
|
catch {
|
|
1730
1902
|
// Index not available — skip persistence
|
|
1731
1903
|
}
|
|
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
|
-
}
|
|
1904
|
+
printDetectionResults({
|
|
1905
|
+
json: options.json,
|
|
1906
|
+
mode: detectionMode,
|
|
1907
|
+
projectRoot,
|
|
1908
|
+
filesScanned: files.slice(0, 500).length,
|
|
1909
|
+
dbProvenanced: provenanced,
|
|
1910
|
+
validatorProvenanced: validatorIdentifiers,
|
|
1911
|
+
inferred,
|
|
1912
|
+
});
|
|
1782
1913
|
}
|
|
1783
1914
|
catch (error) {
|
|
1784
1915
|
console.error(chalk.red('Error:'), error);
|
|
1785
1916
|
process.exit(1);
|
|
1786
1917
|
}
|
|
1787
1918
|
});
|
|
1919
|
+
// Renders the `config detection` results — pulled out of the `.action` handler so
|
|
1920
|
+
// the scan/persist logic stays short and the two output shapes (JSON vs human) are
|
|
1921
|
+
// isolated from the provenance collection.
|
|
1922
|
+
function printDetectionResults(params) {
|
|
1923
|
+
const { json, mode, projectRoot, filesScanned, dbProvenanced, validatorProvenanced, inferred } = params;
|
|
1924
|
+
if (json) {
|
|
1925
|
+
process.stdout.write(JSON.stringify({
|
|
1926
|
+
mode,
|
|
1927
|
+
projectRoot,
|
|
1928
|
+
filesScanned,
|
|
1929
|
+
dbProvenanced,
|
|
1930
|
+
validatorProvenanced,
|
|
1931
|
+
inferred,
|
|
1932
|
+
}, null, 2) + '\n');
|
|
1933
|
+
return;
|
|
1934
|
+
}
|
|
1935
|
+
console.log(chalk.bold(`Detection mode: ${chalk.cyan(mode)}`));
|
|
1936
|
+
console.log(chalk.gray(`Scanned ${filesScanned} files`));
|
|
1937
|
+
console.log();
|
|
1938
|
+
if (dbProvenanced.length > 0) {
|
|
1939
|
+
console.log(chalk.bold('DB-Provenanced Identifiers:'));
|
|
1940
|
+
for (const p of dbProvenanced) {
|
|
1941
|
+
const reasonColor = p.reason === 'fallback'
|
|
1942
|
+
? chalk.yellow
|
|
1943
|
+
: p.reason === 'package'
|
|
1944
|
+
? chalk.green
|
|
1945
|
+
: chalk.cyan;
|
|
1946
|
+
console.log(` ${chalk.white(p.identifier)} ${chalk.gray(`(${p.file})`)}`);
|
|
1947
|
+
console.log(` ${reasonColor(`reason: ${p.reason}`)} — ${chalk.gray(p.source)}`);
|
|
1948
|
+
if (p.chain) {
|
|
1949
|
+
console.log(` ${chalk.gray(`chain: ${p.chain.join(' → ')}`)}`);
|
|
1950
|
+
}
|
|
1951
|
+
}
|
|
1952
|
+
console.log();
|
|
1953
|
+
}
|
|
1954
|
+
if (inferred.length > 0) {
|
|
1955
|
+
console.log(chalk.bold('Inferred Receivers:'));
|
|
1956
|
+
for (const i of inferred) {
|
|
1957
|
+
console.log(` ${chalk.white(i.identifier)} ${chalk.gray(`(${i.file})`)}`);
|
|
1958
|
+
console.log(` ${chalk.magenta(i.reason)}`);
|
|
1959
|
+
}
|
|
1960
|
+
console.log();
|
|
1961
|
+
}
|
|
1962
|
+
if (validatorProvenanced.length > 0) {
|
|
1963
|
+
console.log(chalk.bold('Validator-Provenanced Identifiers:'));
|
|
1964
|
+
for (const v of validatorProvenanced) {
|
|
1965
|
+
console.log(` ${chalk.white(v.identifier)} ${chalk.gray(`(${v.file})`)}`);
|
|
1966
|
+
console.log(` ${chalk.green(`reason: ${v.reason}`)} — ${chalk.gray(v.source)}`);
|
|
1967
|
+
}
|
|
1968
|
+
console.log();
|
|
1969
|
+
}
|
|
1970
|
+
if (dbProvenanced.length === 0 && inferred.length === 0 && validatorProvenanced.length === 0) {
|
|
1971
|
+
console.log(chalk.gray('No DB-provenanced, inferred, or validator-provenanced identifiers found.'));
|
|
1972
|
+
}
|
|
1973
|
+
}
|
|
1788
1974
|
// Search command
|
|
1789
1975
|
program
|
|
1790
1976
|
.command('search <query>')
|
|
@@ -1852,7 +2038,7 @@ tasksCmd
|
|
|
1852
2038
|
.option('--priority <priority>', 'Filter by priority (low, medium, high)')
|
|
1853
2039
|
.option('--label <label>', 'Filter by label')
|
|
1854
2040
|
.option('--limit <limit>', 'Result limit', '50')
|
|
1855
|
-
.option('--json',
|
|
2041
|
+
.option('--json', OPTION_OUTPUT_AS_JSON)
|
|
1856
2042
|
.action(async (options) => {
|
|
1857
2043
|
try {
|
|
1858
2044
|
const args = { action: 'list' };
|
|
@@ -1887,7 +2073,7 @@ tasksCmd
|
|
|
1887
2073
|
}
|
|
1888
2074
|
}
|
|
1889
2075
|
else {
|
|
1890
|
-
console.error(chalk.red(result.error ||
|
|
2076
|
+
console.error(chalk.red(result.error || UNKNOWN_ERROR));
|
|
1891
2077
|
process.exit(1);
|
|
1892
2078
|
}
|
|
1893
2079
|
}
|
|
@@ -1904,7 +2090,7 @@ tasksCmd
|
|
|
1904
2090
|
.option('--priority <priority>', 'Priority (low, medium, high)')
|
|
1905
2091
|
.option('--status <status>', 'Status (open, in_progress)')
|
|
1906
2092
|
.option('--labels <labels>', 'Comma-separated labels')
|
|
1907
|
-
.option('--json',
|
|
2093
|
+
.option('--json', OPTION_OUTPUT_AS_JSON)
|
|
1908
2094
|
.action(async (options) => {
|
|
1909
2095
|
try {
|
|
1910
2096
|
const args = {
|
|
@@ -1929,7 +2115,7 @@ tasksCmd
|
|
|
1929
2115
|
console.log(chalk.green(`✓ Task created: ${task.title || task.taskId || 'unknown'}`));
|
|
1930
2116
|
}
|
|
1931
2117
|
else {
|
|
1932
|
-
console.error(chalk.red(result.error ||
|
|
2118
|
+
console.error(chalk.red(result.error || UNKNOWN_ERROR));
|
|
1933
2119
|
process.exit(1);
|
|
1934
2120
|
}
|
|
1935
2121
|
}
|
|
@@ -1941,7 +2127,7 @@ tasksCmd
|
|
|
1941
2127
|
tasksCmd
|
|
1942
2128
|
.command('get <taskId>')
|
|
1943
2129
|
.description('Get a task by ID')
|
|
1944
|
-
.option('--json',
|
|
2130
|
+
.option('--json', OPTION_OUTPUT_AS_JSON)
|
|
1945
2131
|
.action(async (taskId, options) => {
|
|
1946
2132
|
try {
|
|
1947
2133
|
const { handleProjectTasks } = await import('./mcp-tools/projectTasks.js');
|
|
@@ -1967,7 +2153,7 @@ tasksCmd
|
|
|
1967
2153
|
console.log(chalk.dim(`\nfingerprint: ${t.fingerprint}`));
|
|
1968
2154
|
}
|
|
1969
2155
|
else {
|
|
1970
|
-
console.error(chalk.red(result.error ||
|
|
2156
|
+
console.error(chalk.red(result.error || UNKNOWN_ERROR));
|
|
1971
2157
|
process.exit(1);
|
|
1972
2158
|
}
|
|
1973
2159
|
}
|
|
@@ -1984,7 +2170,7 @@ tasksCmd
|
|
|
1984
2170
|
.option('--status <status>', 'New status (open, in_progress, completed)')
|
|
1985
2171
|
.option('--priority <priority>', 'New priority (low, medium, high)')
|
|
1986
2172
|
.option('--labels <labels>', 'Comma-separated labels')
|
|
1987
|
-
.option('--json',
|
|
2173
|
+
.option('--json', OPTION_OUTPUT_AS_JSON)
|
|
1988
2174
|
.action(async (taskId, options) => {
|
|
1989
2175
|
try {
|
|
1990
2176
|
const patch = {};
|
|
@@ -2011,7 +2197,7 @@ tasksCmd
|
|
|
2011
2197
|
console.log(chalk.green(`✓ Task updated`));
|
|
2012
2198
|
}
|
|
2013
2199
|
else {
|
|
2014
|
-
console.error(chalk.red(result.error ||
|
|
2200
|
+
console.error(chalk.red(result.error || UNKNOWN_ERROR));
|
|
2015
2201
|
process.exit(1);
|
|
2016
2202
|
}
|
|
2017
2203
|
}
|
|
@@ -2020,60 +2206,45 @@ tasksCmd
|
|
|
2020
2206
|
process.exit(1);
|
|
2021
2207
|
}
|
|
2022
2208
|
});
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2209
|
+
/** Run a single-taskId tasks subcommand and print its result. */
|
|
2210
|
+
function makeTaskIdAction(action, successLabel) {
|
|
2211
|
+
return async (taskId, options) => {
|
|
2212
|
+
try {
|
|
2213
|
+
const { handleProjectTasks } = await import('./mcp-tools/projectTasks.js');
|
|
2214
|
+
const result = await handleProjectTasks({ action, taskId });
|
|
2215
|
+
if (options.json) {
|
|
2216
|
+
process.stdout.write(JSON.stringify(result, null, 2) + '\n');
|
|
2217
|
+
}
|
|
2218
|
+
else if (result.success) {
|
|
2219
|
+
console.log(chalk.green(`✓ ${successLabel}`));
|
|
2220
|
+
}
|
|
2221
|
+
else {
|
|
2222
|
+
console.error(chalk.red(result.error || UNKNOWN_ERROR));
|
|
2223
|
+
process.exit(1);
|
|
2224
|
+
}
|
|
2036
2225
|
}
|
|
2037
|
-
|
|
2038
|
-
console.error(chalk.red(
|
|
2226
|
+
catch (error) {
|
|
2227
|
+
console.error(chalk.red('Error:'), error);
|
|
2039
2228
|
process.exit(1);
|
|
2040
2229
|
}
|
|
2041
|
-
}
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2230
|
+
};
|
|
2231
|
+
}
|
|
2232
|
+
tasksCmd
|
|
2233
|
+
.command('complete <taskId>')
|
|
2234
|
+
.description('Mark a task as completed')
|
|
2235
|
+
.option('--json', OPTION_OUTPUT_AS_JSON)
|
|
2236
|
+
.action(makeTaskIdAction('complete_task', 'Task completed'));
|
|
2047
2237
|
tasksCmd
|
|
2048
2238
|
.command('delete <taskId>')
|
|
2049
2239
|
.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
|
-
});
|
|
2240
|
+
.option('--json', OPTION_OUTPUT_AS_JSON)
|
|
2241
|
+
.action(makeTaskIdAction('delete', 'Task deleted'));
|
|
2071
2242
|
tasksCmd
|
|
2072
2243
|
.command('from-audit')
|
|
2073
2244
|
.description('Populate tasks from audit violations')
|
|
2074
2245
|
.option('--auditJobId <id>', 'Specific audit result ID')
|
|
2075
2246
|
.option('--severities <severities>', 'Comma-separated severities (critical,severe,high)', 'critical,severe,high')
|
|
2076
|
-
.option('--json',
|
|
2247
|
+
.option('--json', OPTION_OUTPUT_AS_JSON)
|
|
2077
2248
|
.action(async (options) => {
|
|
2078
2249
|
try {
|
|
2079
2250
|
const args = { action: 'from_audit' };
|
|
@@ -2100,7 +2271,7 @@ tasksCmd
|
|
|
2100
2271
|
console.log(chalk.yellow(result.error));
|
|
2101
2272
|
}
|
|
2102
2273
|
else {
|
|
2103
|
-
console.error(chalk.red(result.error ||
|
|
2274
|
+
console.error(chalk.red(result.error || UNKNOWN_ERROR));
|
|
2104
2275
|
process.exit(1);
|
|
2105
2276
|
}
|
|
2106
2277
|
}
|
|
@@ -2116,7 +2287,7 @@ program
|
|
|
2116
2287
|
.description('Rank files and functions by hotspot score (churn × complexity)')
|
|
2117
2288
|
.option('--limit <n>', 'Max entries to show')
|
|
2118
2289
|
.option('--path <dir>', 'Target directory (triggers spot churn extraction if index is empty)')
|
|
2119
|
-
.option('--json',
|
|
2290
|
+
.option('--json', OPTION_OUTPUT_AS_JSON)
|
|
2120
2291
|
.action(async (options) => {
|
|
2121
2292
|
try {
|
|
2122
2293
|
const db = CodeIndexDB.getInstance();
|
|
@@ -2191,8 +2362,8 @@ program
|
|
|
2191
2362
|
.command('risk')
|
|
2192
2363
|
.description('Rank functions by architectural risk (PageRank × betweenness × complexity × untested)')
|
|
2193
2364
|
.option('--limit <n>', 'Max entries to show', '20')
|
|
2194
|
-
.option('--path <dir>',
|
|
2195
|
-
.option('--json',
|
|
2365
|
+
.option('--path <dir>', OPTION_PROJECT_PATH, process.cwd())
|
|
2366
|
+
.option('--json', OPTION_OUTPUT_AS_JSON)
|
|
2196
2367
|
.option('--format <format>', 'Output format: dot (call-graph neighborhood of top functions)')
|
|
2197
2368
|
.action(async (options) => {
|
|
2198
2369
|
try {
|
|
@@ -2207,10 +2378,13 @@ program
|
|
|
2207
2378
|
if (options.format === 'dot') {
|
|
2208
2379
|
// DOT output: call-graph neighborhood of top-N risk functions
|
|
2209
2380
|
const { callGraphToDot } = await import('./graph/outputFormatter.js');
|
|
2210
|
-
// Collect IDs for top risk functions
|
|
2381
|
+
// Collect IDs for top risk functions — one query for the whole top-N
|
|
2382
|
+
// set, not one query per function (loop-query).
|
|
2211
2383
|
const topIds = new Set();
|
|
2212
|
-
|
|
2213
|
-
const
|
|
2384
|
+
if (top.length > 0) {
|
|
2385
|
+
const orClause = top.map(() => '(name = ? AND file_path = ?)').join(' OR ');
|
|
2386
|
+
const params = top.flatMap((e) => [e.functionName, e.filePath]);
|
|
2387
|
+
const rows = rawDb.prepare(`SELECT id FROM functions WHERE ${orClause}`).all(...params);
|
|
2214
2388
|
for (const r of rows)
|
|
2215
2389
|
topIds.add(r.id);
|
|
2216
2390
|
}
|
|
@@ -2267,7 +2441,7 @@ ledgerCmd
|
|
|
2267
2441
|
.command('list')
|
|
2268
2442
|
.description('List recent audit runs')
|
|
2269
2443
|
.option('--limit <n>', 'Max runs to show', '20')
|
|
2270
|
-
.option('--json',
|
|
2444
|
+
.option('--json', OPTION_OUTPUT_AS_JSON)
|
|
2271
2445
|
.action(async (options) => {
|
|
2272
2446
|
try {
|
|
2273
2447
|
const { listRuns } = await import('./ledger.js');
|
|
@@ -2316,7 +2490,7 @@ ledgerCmd
|
|
|
2316
2490
|
ledgerCmd
|
|
2317
2491
|
.command('stats')
|
|
2318
2492
|
.description('Show aggregated ledger statistics')
|
|
2319
|
-
.option('--json',
|
|
2493
|
+
.option('--json', OPTION_OUTPUT_AS_JSON)
|
|
2320
2494
|
.action(async (options) => {
|
|
2321
2495
|
try {
|
|
2322
2496
|
const { getLedgerStats } = await import('./ledger.js');
|
|
@@ -2349,7 +2523,7 @@ ledgerCmd
|
|
|
2349
2523
|
.command('trends')
|
|
2350
2524
|
.description('Compare consecutive full-audit runs and report new vs fixed readings per rule')
|
|
2351
2525
|
.option('--since <runId>', 'Only consider runs after this run ID')
|
|
2352
|
-
.option('--json',
|
|
2526
|
+
.option('--json', OPTION_OUTPUT_AS_JSON)
|
|
2353
2527
|
.action(async (options) => {
|
|
2354
2528
|
try {
|
|
2355
2529
|
const { getTrends } = await import('./ledger.js');
|
|
@@ -2412,9 +2586,6 @@ ledgerCmd
|
|
|
2412
2586
|
// `audit --detach` forks workers/auditJobRunner; `status`/`result`/`jobs` read
|
|
2413
2587
|
// the persisted ledger. One job model (the ledger run), one write path — these
|
|
2414
2588
|
// 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
2589
|
/** Lease TTL used by read-path reclaim, mirroring mcpAuditJobs.jobLeaseTtlMs. */
|
|
2419
2590
|
const CLI_JOB_LEASE_TTL_MS = Number(process.env.CODE_AUDITOR_JOB_LEASE_TTL_MS) || 30_000;
|
|
2420
2591
|
function spec41StatusBadge(status) {
|
|
@@ -2496,12 +2667,8 @@ async function runDetachedAudit(options) {
|
|
|
2496
2667
|
const logFd = openSync(logPath, 'a');
|
|
2497
2668
|
const argsJson = JSON.stringify({
|
|
2498
2669
|
path: auditPath,
|
|
2499
|
-
...(options.partitionStrategy && { partitionStrategy: options.partitionStrategy }),
|
|
2500
|
-
...(options.maxPartitions && { maxPartitions: Number(options.maxPartitions) }),
|
|
2501
|
-
...(options.shardTimeoutMs && { shardTimeoutMs: Number(options.shardTimeoutMs) }),
|
|
2502
2670
|
});
|
|
2503
2671
|
const defaultsJson = JSON.stringify({
|
|
2504
|
-
defaultAnalyzers: DETACHED_DEFAULT_ANALYZERS,
|
|
2505
2672
|
defaultMinSeverity: 'high',
|
|
2506
2673
|
defaultGenerateCodeMap: false,
|
|
2507
2674
|
});
|
|
@@ -2521,7 +2688,7 @@ program
|
|
|
2521
2688
|
.command('status <jobId>')
|
|
2522
2689
|
.description('Show a detached audit job status, progress, and staleness')
|
|
2523
2690
|
.option('-p, --path <path>', 'Project root containing the job (default: cwd)', process.cwd())
|
|
2524
|
-
.option('--json',
|
|
2691
|
+
.option('--json', OPTION_OUTPUT_AS_JSON)
|
|
2525
2692
|
.action(async (jobId, options) => {
|
|
2526
2693
|
try {
|
|
2527
2694
|
const { getLedgerRun, reclaimStaleRunning, computeStaleness } = await import('./ledger.js');
|
|
@@ -2584,12 +2751,12 @@ program
|
|
|
2584
2751
|
.option('--analyzer <analyzer>', 'Filter by analyzer')
|
|
2585
2752
|
.option('--file <file>', 'Filter by file path (substring match)')
|
|
2586
2753
|
.option('--severity <severity>', 'Filter by severity (critical|severe|high)')
|
|
2587
|
-
.option('--state [state]', 'Query coverage by state (fired|clean|notApplicable|cannot-fire|
|
|
2754
|
+
.option('--state [state]', 'Query coverage by state (fired|clean|notApplicable|cannot-fire|incomplete); omit value for all')
|
|
2588
2755
|
.option('--count', 'Group findings by analyzer/rule with counts')
|
|
2589
2756
|
.option('--limit <n>', 'Max findings to return (0 = unbounded)', '50')
|
|
2590
2757
|
.option('--offset <n>', 'Findings offset', '0')
|
|
2591
2758
|
.option('--stale', 'Force full content re-hash for staleness')
|
|
2592
|
-
.option('--json',
|
|
2759
|
+
.option('--json', OPTION_OUTPUT_AS_JSON)
|
|
2593
2760
|
.action(async (jobId, options) => {
|
|
2594
2761
|
try {
|
|
2595
2762
|
const { getLedgerRun, queryLedgerFindings, queryLedgerCoverage, computeStaleness } = await import('./ledger.js');
|
|
@@ -2682,7 +2849,7 @@ program
|
|
|
2682
2849
|
.option('--prune', 'Delete all but the newest runs for this project')
|
|
2683
2850
|
.option('--keep <n>', 'Runs to keep when --prune is set', '10')
|
|
2684
2851
|
.option('--limit <n>', 'Runs to display (staleness computed for these)', '20')
|
|
2685
|
-
.option('--json',
|
|
2852
|
+
.option('--json', OPTION_OUTPUT_AS_JSON)
|
|
2686
2853
|
.action(async (options) => {
|
|
2687
2854
|
try {
|
|
2688
2855
|
const { listLedgerRuns, reclaimStaleRunning, pruneLedgerRuns, computeStaleness } = await import('./ledger.js');
|
|
@@ -2738,7 +2905,7 @@ conventionsCmd
|
|
|
2738
2905
|
.command('list')
|
|
2739
2906
|
.description('List mined conventions from the code index')
|
|
2740
2907
|
.option('--domain <domain>', 'Filter by domain: usage-pair, import-form, error-handling, export-shape, naming')
|
|
2741
|
-
.option('--json',
|
|
2908
|
+
.option('--json', OPTION_OUTPUT_AS_JSON)
|
|
2742
2909
|
.action(async (options) => {
|
|
2743
2910
|
try {
|
|
2744
2911
|
const db = CodeIndexDB.getInstance();
|
|
@@ -2802,7 +2969,7 @@ conventionsCmd
|
|
|
2802
2969
|
.command('propose')
|
|
2803
2970
|
.description('Emit ready-to-paste .codeauditor.json rules from mined conventions')
|
|
2804
2971
|
.option('--domain <domain>', 'Filter by domain: naming or import-form (other domains do not map to rule kinds)')
|
|
2805
|
-
.option('--json',
|
|
2972
|
+
.option('--json', OPTION_OUTPUT_AS_JSON)
|
|
2806
2973
|
.action(async (options) => {
|
|
2807
2974
|
try {
|
|
2808
2975
|
const db = CodeIndexDB.getInstance();
|
|
@@ -2912,8 +3079,8 @@ program
|
|
|
2912
3079
|
program
|
|
2913
3080
|
.command('architecture')
|
|
2914
3081
|
.description('Analyze import graph: community detection, directory purity, Martin instability metrics')
|
|
2915
|
-
.option('--path <dir>',
|
|
2916
|
-
.option('--json',
|
|
3082
|
+
.option('--path <dir>', OPTION_PROJECT_PATH, process.cwd())
|
|
3083
|
+
.option('--json', OPTION_OUTPUT_AS_JSON)
|
|
2917
3084
|
.option('--format <format>', 'Output format: dot or mermaid (import graph colored by community)')
|
|
2918
3085
|
.action(async (options) => {
|
|
2919
3086
|
try {
|
|
@@ -3111,7 +3278,7 @@ coverageCmd
|
|
|
3111
3278
|
coverageCmd
|
|
3112
3279
|
.command('by-risk', { isDefault: true })
|
|
3113
3280
|
.description('Show coverage rate by risk decile')
|
|
3114
|
-
.option('--json',
|
|
3281
|
+
.option('--json', OPTION_OUTPUT_AS_JSON)
|
|
3115
3282
|
.action(async (options) => {
|
|
3116
3283
|
try {
|
|
3117
3284
|
const { generateCoverageReport } = await import('./coverage/coverageService.js');
|
|
@@ -3160,8 +3327,8 @@ coverageCmd
|
|
|
3160
3327
|
program
|
|
3161
3328
|
.command('print-config <file>')
|
|
3162
3329
|
.description('Print the effective config for a file, naming the source of every value')
|
|
3163
|
-
.option('-p, --path <path>',
|
|
3164
|
-
.option('--json',
|
|
3330
|
+
.option('-p, --path <path>', OPTION_PROJECT_PATH, process.cwd())
|
|
3331
|
+
.option('--json', OPTION_OUTPUT_AS_JSON)
|
|
3165
3332
|
.option('--preset <id>', 'Apply a shareable preset (repeatable)', (v, prev) => prev.concat([v]), [])
|
|
3166
3333
|
.action(async (file, options) => {
|
|
3167
3334
|
try {
|
|
@@ -3192,7 +3359,6 @@ program
|
|
|
3192
3359
|
}
|
|
3193
3360
|
const analyzerConfigs = (config.analyzerConfigs ?? {});
|
|
3194
3361
|
const pathProfiles = (config.pathProfiles ?? []);
|
|
3195
|
-
const enabledAnalyzers = (config.enabledAnalyzers ?? []);
|
|
3196
3362
|
// Resolve --preset ids to ordered preset objects (Spec 38 R4). Unknown
|
|
3197
3363
|
// ids are a hard error, not a silent skip.
|
|
3198
3364
|
const presetIds = Array.isArray(options.preset) ? options.preset : (options.preset ? [options.preset] : []);
|
|
@@ -3212,7 +3378,6 @@ program
|
|
|
3212
3378
|
projectRoot,
|
|
3213
3379
|
analyzerConfigs,
|
|
3214
3380
|
pathProfiles,
|
|
3215
|
-
enabledAnalyzers,
|
|
3216
3381
|
presets,
|
|
3217
3382
|
lintConfig,
|
|
3218
3383
|
});
|
|
@@ -3325,7 +3490,7 @@ program
|
|
|
3325
3490
|
for (const v of sorted) {
|
|
3326
3491
|
const icon = v.severity === 'critical' ? '🔴' :
|
|
3327
3492
|
v.severity === 'severe' ? '🟠' : '🟡';
|
|
3328
|
-
console.log(`${icon} ${chalk.bold(v.file)}${v.line
|
|
3493
|
+
console.log(`${icon} ${chalk.bold(v.file)}${lineSuffix(v.line)} [${v.rule || v.severity}] ${v.message}`);
|
|
3329
3494
|
}
|
|
3330
3495
|
}
|
|
3331
3496
|
catch (error) {
|
|
@@ -3390,8 +3555,8 @@ const daemonCmd = program
|
|
|
3390
3555
|
daemonCmd
|
|
3391
3556
|
.command('start')
|
|
3392
3557
|
.description('Start the daemon for this project (detached by default)')
|
|
3393
|
-
.option('-p, --path <path>',
|
|
3394
|
-
.option('-c, --config <config>',
|
|
3558
|
+
.option('-p, --path <path>', OPTION_PROJECT_ROOT, process.cwd())
|
|
3559
|
+
.option('-c, --config <config>', OPTION_CONFIGURATION_NAME)
|
|
3395
3560
|
.option('--foreground', 'Run in the foreground (blocking; useful for debugging)')
|
|
3396
3561
|
.action(async (options) => {
|
|
3397
3562
|
try {
|
|
@@ -3422,7 +3587,7 @@ daemonCmd
|
|
|
3422
3587
|
daemonCmd
|
|
3423
3588
|
.command('stop')
|
|
3424
3589
|
.description('Ask the daemon to shut down')
|
|
3425
|
-
.option('-p, --path <path>',
|
|
3590
|
+
.option('-p, --path <path>', OPTION_PROJECT_ROOT, process.cwd())
|
|
3426
3591
|
.action(async (options) => {
|
|
3427
3592
|
try {
|
|
3428
3593
|
const { resolveDaemonSocketPath } = await import('./dataPaths.js');
|
|
@@ -3444,8 +3609,8 @@ daemonCmd
|
|
|
3444
3609
|
daemonCmd
|
|
3445
3610
|
.command('status')
|
|
3446
3611
|
.description('Show daemon status')
|
|
3447
|
-
.option('-p, --path <path>',
|
|
3448
|
-
.option('--json',
|
|
3612
|
+
.option('-p, --path <path>', OPTION_PROJECT_ROOT, process.cwd())
|
|
3613
|
+
.option('--json', OPTION_OUTPUT_AS_JSON)
|
|
3449
3614
|
.action(async (options) => {
|
|
3450
3615
|
try {
|
|
3451
3616
|
const { resolveDaemon } = await import('./daemon/resolve.js');
|
|
@@ -3638,6 +3803,136 @@ async function generateConfigurations(options) {
|
|
|
3638
3803
|
console.log(chalk.gray(' 2. Run ') + chalk.cyan('code-audit') + chalk.gray(' to enforce your rules'));
|
|
3639
3804
|
console.log(chalk.gray(' 3. Use ') + chalk.cyan('code-audit changed') + chalk.gray(' in your agent hook (gating rules block on new readings)'));
|
|
3640
3805
|
}
|
|
3806
|
+
async function promptKindSpecificFields(kind, rule) {
|
|
3807
|
+
switch (kind) {
|
|
3808
|
+
case 'import-ban': {
|
|
3809
|
+
const { module } = await inquirer.prompt({
|
|
3810
|
+
module: {
|
|
3811
|
+
type: 'input',
|
|
3812
|
+
message: 'Banned module specifier (e.g. "lodash" or "@old-lib/*"):',
|
|
3813
|
+
validate: (input) => input.trim() ? true : 'Module specifier is required',
|
|
3814
|
+
},
|
|
3815
|
+
});
|
|
3816
|
+
rule.module = module;
|
|
3817
|
+
const { addExcept } = await inquirer.prompt({
|
|
3818
|
+
addExcept: {
|
|
3819
|
+
type: 'confirm',
|
|
3820
|
+
message: 'Add exception paths (files allowed to import it)?',
|
|
3821
|
+
default: false,
|
|
3822
|
+
},
|
|
3823
|
+
});
|
|
3824
|
+
if (addExcept) {
|
|
3825
|
+
const { except } = await inquirer.prompt({
|
|
3826
|
+
except: {
|
|
3827
|
+
type: 'input',
|
|
3828
|
+
message: 'Exception globs (comma-separated, e.g. "src/migration/**"):',
|
|
3829
|
+
},
|
|
3830
|
+
});
|
|
3831
|
+
const exceptList = except.split(',').map((s) => s.trim()).filter(Boolean);
|
|
3832
|
+
if (exceptList.length > 0)
|
|
3833
|
+
rule.except = exceptList;
|
|
3834
|
+
}
|
|
3835
|
+
break;
|
|
3836
|
+
}
|
|
3837
|
+
case 'call-constraint': {
|
|
3838
|
+
const { callee } = await inquirer.prompt({
|
|
3839
|
+
callee: {
|
|
3840
|
+
type: 'input',
|
|
3841
|
+
message: 'Callee (function name, optionally path-qualified as "path/glob#name"):',
|
|
3842
|
+
validate: (input) => input.trim() ? true : 'Callee is required',
|
|
3843
|
+
},
|
|
3844
|
+
});
|
|
3845
|
+
rule.callee = callee;
|
|
3846
|
+
const { mode } = await inquirer.prompt({
|
|
3847
|
+
mode: {
|
|
3848
|
+
type: 'list',
|
|
3849
|
+
message: 'Restriction mode:',
|
|
3850
|
+
choices: [
|
|
3851
|
+
{ name: 'Allow only specific callers (allowFrom)', value: 'allow' },
|
|
3852
|
+
{ name: 'Deny specific callers (denyFrom)', value: 'deny' },
|
|
3853
|
+
],
|
|
3854
|
+
},
|
|
3855
|
+
});
|
|
3856
|
+
const { paths } = await inquirer.prompt({
|
|
3857
|
+
paths: {
|
|
3858
|
+
type: 'input',
|
|
3859
|
+
message: `Path globs (comma-separated) for ${mode === 'allow' ? 'allowFrom' : 'denyFrom'}:`,
|
|
3860
|
+
validate: (input) => input.trim() ? true : 'At least one path glob is required',
|
|
3861
|
+
},
|
|
3862
|
+
});
|
|
3863
|
+
const pathList = paths.split(',').map((s) => s.trim()).filter(Boolean);
|
|
3864
|
+
if (mode === 'allow') {
|
|
3865
|
+
rule.allowFrom = pathList;
|
|
3866
|
+
}
|
|
3867
|
+
else {
|
|
3868
|
+
rule.denyFrom = pathList;
|
|
3869
|
+
}
|
|
3870
|
+
break;
|
|
3871
|
+
}
|
|
3872
|
+
case 'module-boundary': {
|
|
3873
|
+
const { from, to } = await inquirer.prompt({
|
|
3874
|
+
from: {
|
|
3875
|
+
type: 'input',
|
|
3876
|
+
message: 'From (path glob for files that must not import):',
|
|
3877
|
+
validate: (input) => input.trim() ? true : '"from" path glob is required',
|
|
3878
|
+
},
|
|
3879
|
+
to: {
|
|
3880
|
+
type: 'input',
|
|
3881
|
+
message: 'To (path glob for files that must not be imported):',
|
|
3882
|
+
validate: (input) => input.trim() ? true : '"to" path glob is required',
|
|
3883
|
+
},
|
|
3884
|
+
});
|
|
3885
|
+
rule.from = from;
|
|
3886
|
+
rule.to = to;
|
|
3887
|
+
break;
|
|
3888
|
+
}
|
|
3889
|
+
case 'naming': {
|
|
3890
|
+
const { path, exports: exportPattern } = await inquirer.prompt({
|
|
3891
|
+
path: {
|
|
3892
|
+
type: 'input',
|
|
3893
|
+
message: 'Path (glob for files this rule applies to):',
|
|
3894
|
+
validate: (input) => input.trim() ? true : 'Path glob is required',
|
|
3895
|
+
},
|
|
3896
|
+
exports: {
|
|
3897
|
+
type: 'input',
|
|
3898
|
+
message: 'Exports regex (exported symbols must match, e.g. "^use[A-Z]"):',
|
|
3899
|
+
validate: (input) => input.trim() ? true : 'Exports regex is required',
|
|
3900
|
+
},
|
|
3901
|
+
});
|
|
3902
|
+
rule.path = path;
|
|
3903
|
+
rule.exports = exportPattern;
|
|
3904
|
+
break;
|
|
3905
|
+
}
|
|
3906
|
+
case 'ast-pattern': {
|
|
3907
|
+
const { pattern } = await inquirer.prompt({
|
|
3908
|
+
pattern: {
|
|
3909
|
+
type: 'input',
|
|
3910
|
+
message: 'AST pattern (ast-grep syntax, e.g. "new Function($$$)"):',
|
|
3911
|
+
validate: (input) => input.trim() ? true : 'Pattern is required',
|
|
3912
|
+
},
|
|
3913
|
+
});
|
|
3914
|
+
rule.pattern = pattern;
|
|
3915
|
+
const { addLanguage } = await inquirer.prompt({
|
|
3916
|
+
addLanguage: {
|
|
3917
|
+
type: 'confirm',
|
|
3918
|
+
message: 'Restrict to a specific language? (default: typescript)',
|
|
3919
|
+
default: false,
|
|
3920
|
+
},
|
|
3921
|
+
});
|
|
3922
|
+
if (addLanguage) {
|
|
3923
|
+
const { language } = await inquirer.prompt({
|
|
3924
|
+
language: {
|
|
3925
|
+
type: 'list',
|
|
3926
|
+
message: 'Language:',
|
|
3927
|
+
choices: ['typescript', 'javascript', 'go'],
|
|
3928
|
+
},
|
|
3929
|
+
});
|
|
3930
|
+
rule.language = language;
|
|
3931
|
+
}
|
|
3932
|
+
break;
|
|
3933
|
+
}
|
|
3934
|
+
}
|
|
3935
|
+
}
|
|
3641
3936
|
/**
|
|
3642
3937
|
* Interactive rule builder — walks the user through adding rules one at a time.
|
|
3643
3938
|
*/
|
|
@@ -3696,134 +3991,7 @@ async function buildRulesInteractively() {
|
|
|
3696
3991
|
message: common.message,
|
|
3697
3992
|
};
|
|
3698
3993
|
// 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
|
-
}
|
|
3994
|
+
await promptKindSpecificFields(kind, rule);
|
|
3827
3995
|
rules.push(rule);
|
|
3828
3996
|
console.log(chalk.green(` ✓ Added rule "${rule.id}" [${rule.kind}]`));
|
|
3829
3997
|
const { cont } = await inquirer.prompt({
|
|
@@ -3956,7 +4124,7 @@ async function runSearch(query, options) {
|
|
|
3956
4124
|
await db.initialize();
|
|
3957
4125
|
// --definition: look up a specific symbol
|
|
3958
4126
|
if (options.definition) {
|
|
3959
|
-
const func = await db.findDefinition(query);
|
|
4127
|
+
const func = await db.functionIndex.findDefinition(query);
|
|
3960
4128
|
if (!func) {
|
|
3961
4129
|
console.log(chalk.yellow(`No definition found for "${query}"`));
|
|
3962
4130
|
return;
|
|
@@ -3968,9 +4136,6 @@ async function runSearch(query, options) {
|
|
|
3968
4136
|
console.log(chalk.blue(`\nDefinition: ${func.name}`));
|
|
3969
4137
|
console.log(chalk.gray('──────────────────────────────────────────'));
|
|
3970
4138
|
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
4139
|
if (func.purpose) {
|
|
3975
4140
|
console.log(`${chalk.bold('Purpose:')} ${func.purpose}`);
|
|
3976
4141
|
}
|
|
@@ -4002,7 +4167,7 @@ async function runSearch(query, options) {
|
|
|
4002
4167
|
}
|
|
4003
4168
|
searchOptions.filters.language = options.language;
|
|
4004
4169
|
}
|
|
4005
|
-
const result = await db.searchFunctions(searchOptions);
|
|
4170
|
+
const result = await db.search.searchFunctions(searchOptions);
|
|
4006
4171
|
if (options.json) {
|
|
4007
4172
|
process.stdout.write(JSON.stringify({
|
|
4008
4173
|
query,
|
|
@@ -4025,9 +4190,6 @@ async function runSearch(query, options) {
|
|
|
4025
4190
|
const lang = func.language ? chalk.dim(` [${func.language}]`) : '';
|
|
4026
4191
|
const lineInfo = func.lineNumber ? chalk.dim(`:${func.lineNumber}`) : '';
|
|
4027
4192
|
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
4193
|
if (func.purpose) {
|
|
4032
4194
|
console.log(chalk.dim(` ${func.purpose}`));
|
|
4033
4195
|
}
|