octocode-cli 1.2.8 → 1.2.9
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/README.md +42 -35
- package/out/octocode-cli.js +36 -11767
- package/package.json +36 -36
- package/skills/README.md +42 -114
- package/skills/{octocode-code-engineer → octocode-engineer}/.claude/settings.local.json +2 -1
- package/skills/octocode-engineer/README.md +99 -0
- package/skills/octocode-engineer/SKILL.md +499 -0
- package/skills/octocode-engineer/build.mjs +29 -0
- package/skills/{octocode-code-engineer → octocode-engineer}/eslint.config.mjs +3 -13
- package/skills/{octocode-code-engineer → octocode-engineer}/package.json +28 -27
- package/skills/octocode-engineer/references/ast-reference.md +166 -0
- package/skills/{octocode-code-engineer → octocode-engineer}/references/cli-reference.md +80 -6
- package/skills/octocode-engineer/references/externals.md +86 -0
- package/skills/{octocode-code-engineer → octocode-engineer}/references/output-files.md +46 -6
- package/skills/octocode-engineer/references/quality-indicators.md +202 -0
- package/skills/octocode-engineer/references/tool-workflows.md +298 -0
- package/skills/octocode-engineer/references/validation-playbooks.md +99 -0
- package/skills/octocode-engineer/scripts/ast/search.js +45 -0
- package/skills/octocode-engineer/scripts/ast/tree-search.js +27 -0
- package/skills/octocode-engineer/scripts/index.js +173 -0
- package/skills/octocode-engineer/scripts/run.js +179 -0
- package/skills/octocode-engineer/src/analysis/dependencies.ts +378 -0
- package/skills/{octocode-code-engineer → octocode-engineer}/src/analysis/discovery.test.ts +57 -0
- package/skills/{octocode-code-engineer → octocode-engineer}/src/analysis/discovery.ts +43 -0
- package/skills/{octocode-code-engineer → octocode-engineer}/src/ast/search.test.ts +113 -0
- package/skills/{octocode-code-engineer → octocode-engineer}/src/ast/search.ts +64 -1
- package/skills/{octocode-code-engineer → octocode-engineer}/src/ast/tree-sitter.test.ts +118 -2
- package/skills/{octocode-code-engineer → octocode-engineer}/src/ast/tree-sitter.ts +65 -3
- package/skills/{octocode-code-engineer → octocode-engineer}/src/ast/ts-analyzer.test.ts +281 -1
- package/skills/{octocode-code-engineer → octocode-engineer}/src/ast/ts-analyzer.ts +173 -3
- package/skills/{octocode-code-engineer → octocode-engineer}/src/collectors/security.test.ts +73 -0
- package/skills/{octocode-code-engineer → octocode-engineer}/src/collectors/security.ts +62 -4
- package/skills/octocode-engineer/src/detector-gating.test.ts +59 -0
- package/skills/{octocode-code-engineer → octocode-engineer}/src/detectors/code-quality.ts +342 -0
- package/skills/{octocode-code-engineer → octocode-engineer}/src/detectors/index.ts +8 -0
- package/skills/{octocode-code-engineer → octocode-engineer}/src/index.test.ts +565 -11
- package/skills/octocode-engineer/src/index.ts +468 -0
- package/skills/octocode-engineer/src/pipeline/affected.test.ts +147 -0
- package/skills/octocode-engineer/src/pipeline/affected.ts +68 -0
- package/skills/octocode-engineer/src/pipeline/baseline.test.ts +276 -0
- package/skills/octocode-engineer/src/pipeline/baseline.ts +76 -0
- package/skills/{octocode-code-engineer → octocode-engineer}/src/pipeline/cli.test.ts +300 -53
- package/skills/{octocode-code-engineer → octocode-engineer}/src/pipeline/cli.ts +180 -36
- package/skills/octocode-engineer/src/pipeline/config-loader.test.ts +264 -0
- package/skills/octocode-engineer/src/pipeline/config-loader.ts +109 -0
- package/skills/octocode-engineer/src/pipeline/create-options.ts +55 -0
- package/skills/octocode-engineer/src/pipeline/health-score.test.ts +65 -0
- package/skills/{octocode-code-engineer → octocode-engineer}/src/pipeline/main.ts +130 -17
- package/skills/octocode-engineer/src/pipeline/progress.ts +51 -0
- package/skills/octocode-engineer/src/pipeline/reporters.test.ts +155 -0
- package/skills/octocode-engineer/src/pipeline/reporters.ts +64 -0
- package/skills/octocode-engineer/src/reporting/graph-features.test.ts +279 -0
- package/skills/{octocode-code-engineer → octocode-engineer}/src/reporting/output-contract.test.ts +6 -0
- package/skills/octocode-engineer/src/reporting/summary-md.test.ts +1066 -0
- package/skills/octocode-engineer/src/reporting/summary-md.ts +1604 -0
- package/skills/{octocode-code-engineer → octocode-engineer}/src/reporting/writer.ts +136 -13
- package/skills/octocode-engineer/src/run.ts +78 -0
- package/skills/{octocode-code-engineer → octocode-engineer}/src/sanity.test.ts +1 -1
- package/skills/octocode-engineer/src/types/analysis.ts +25 -0
- package/skills/octocode-engineer/src/types/collectors.ts +134 -0
- package/skills/{octocode-code-engineer → octocode-engineer}/src/types/constants.ts +75 -41
- package/skills/octocode-engineer/src/types/core.ts +203 -0
- package/skills/octocode-engineer/src/types/dependency.ts +215 -0
- package/skills/octocode-engineer/src/types/file-entry.ts +108 -0
- package/skills/octocode-engineer/src/types/findings.ts +105 -0
- package/skills/{octocode-code-engineer → octocode-engineer}/src/types/index.ts +60 -30
- package/skills/octocode-engineer/src/types/tree-sitter.ts +38 -0
- package/skills/{octocode-code-engineer → octocode-engineer}/tsconfig.json +1 -0
- package/skills/octocode-research/.octocode/scan/.cache/analysis-cache.json +1 -0
- package/skills/octocode-research/.octocode/scan/2026-03-22T10-32-27-073Z/architecture.json +1 -0
- package/skills/octocode-research/.octocode/scan/2026-03-22T10-32-27-073Z/ast-trees.txt +5566 -0
- package/skills/octocode-research/.octocode/scan/2026-03-22T10-32-27-073Z/code-quality.json +1 -0
- package/skills/octocode-research/.octocode/scan/2026-03-22T10-32-27-073Z/dead-code.json +1 -0
- package/skills/octocode-research/.octocode/scan/2026-03-22T10-32-27-073Z/file-inventory.json +1 -0
- package/skills/octocode-research/.octocode/scan/2026-03-22T10-32-27-073Z/findings.json +1 -0
- package/skills/octocode-research/.octocode/scan/2026-03-22T10-32-27-073Z/graph.md +189 -0
- package/skills/octocode-research/.octocode/scan/2026-03-22T10-32-27-073Z/security.json +1 -0
- package/skills/octocode-research/.octocode/scan/2026-03-22T10-32-27-073Z/summary.json +1 -0
- package/skills/octocode-research/.octocode/scan/2026-03-22T10-32-27-073Z/summary.md +265 -0
- package/skills/octocode-research/.octocode/scan/2026-03-22T10-40-10-469Z/architecture.json +1 -0
- package/skills/octocode-research/.octocode/scan/2026-03-22T10-40-10-469Z/ast-trees.txt +5555 -0
- package/skills/octocode-research/.octocode/scan/2026-03-22T10-40-10-469Z/code-quality.json +1 -0
- package/skills/octocode-research/.octocode/scan/2026-03-22T10-40-10-469Z/dead-code.json +1 -0
- package/skills/octocode-research/.octocode/scan/2026-03-22T10-40-10-469Z/file-inventory.json +1 -0
- package/skills/octocode-research/.octocode/scan/2026-03-22T10-40-10-469Z/findings.json +1 -0
- package/skills/octocode-research/.octocode/scan/2026-03-22T10-40-10-469Z/graph.md +190 -0
- package/skills/octocode-research/.octocode/scan/2026-03-22T10-40-10-469Z/security.json +1 -0
- package/skills/octocode-research/.octocode/scan/2026-03-22T10-40-10-469Z/summary.json +1 -0
- package/skills/octocode-research/.octocode/scan/2026-03-22T10-40-10-469Z/summary.md +265 -0
- package/skills/octocode-research/CHANGELOG.md +60 -0
- package/skills/octocode-research/README.md +102 -388
- package/skills/octocode-research/SKILL.md +169 -498
- package/skills/octocode-research/package.json +19 -31
- package/skills/octocode-research/references/PARALLEL_AGENT_PROTOCOL.md +19 -0
- package/skills/octocode-research/references/SESSION_MANAGEMENT.md +38 -0
- package/skills/octocode-research/scripts/server-init.js +1 -1
- package/skills/octocode-research/scripts/server.d.ts +2 -1
- package/skills/octocode-research/scripts/server.js +329 -233
- package/skills/octocode-research/src/__tests__/integration/promptsRoutes.test.ts +180 -0
- package/skills/octocode-research/src/__tests__/integration/serverHttp.test.ts +221 -0
- package/skills/octocode-research/src/__tests__/integration/serverLifecycle.test.ts +194 -0
- package/skills/octocode-research/src/__tests__/integration/toolsRoutes.test.ts +501 -0
- package/skills/octocode-research/src/__tests__/unit/readiness.test.ts +61 -0
- package/skills/octocode-research/src/__tests__/unit/resilience.test.ts +192 -0
- package/skills/octocode-research/src/__tests__/unit/responseFactory.test.ts +172 -0
- package/skills/octocode-research/src/__tests__/unit/responseParser.test.ts +288 -0
- package/skills/octocode-research/src/__tests__/unit/schemas.test.ts +509 -0
- package/skills/octocode-research/src/index.ts +4 -124
- package/skills/octocode-research/src/middleware/queryParser.ts +0 -26
- package/skills/octocode-research/src/routes/lsp.ts +58 -59
- package/skills/octocode-research/src/routes/package.ts +35 -65
- package/skills/octocode-research/src/routes/prompts.ts +3 -3
- package/skills/octocode-research/src/routes/tools.ts +8 -20
- package/skills/octocode-research/src/server-init.ts +30 -237
- package/skills/octocode-research/src/server.ts +50 -23
- package/skills/octocode-research/src/types/errorGuards.ts +9 -80
- package/skills/octocode-research/src/types/guards.ts +0 -28
- package/skills/octocode-research/src/types/mcp.ts +11 -66
- package/skills/octocode-research/src/types/responses.ts +11 -129
- package/skills/octocode-research/src/utils/circuitBreaker.ts +0 -21
- package/skills/octocode-research/src/utils/logger.ts +1 -97
- package/skills/octocode-research/src/utils/resilience.ts +2 -12
- package/skills/octocode-research/src/utils/responseFactory.ts +0 -42
- package/skills/octocode-research/src/utils/responseParser.ts +3 -25
- package/skills/octocode-research/src/utils/retry.ts +0 -63
- package/skills/octocode-research/src/utils/routeFactory.ts +1 -1
- package/skills/octocode-research/src/validation/httpPreprocess.ts +0 -3
- package/skills/octocode-research/src/validation/index.ts +0 -1
- package/skills/octocode-research/src/validation/schemas.ts +0 -63
- package/skills/octocode-research/src/validation/toolCallSchema.ts +3 -3
- package/skills/octocode-research/tsdown.config.ts +4 -0
- package/skills/octocode-research/vitest.config.ts +3 -0
- package/skills/octocode-code-engineer/.plan/VALIDATED_PLAN.md +0 -223
- package/skills/octocode-code-engineer/README.md +0 -178
- package/skills/octocode-code-engineer/SKILL.md +0 -418
- package/skills/octocode-code-engineer/minify-scripts.mjs +0 -32
- package/skills/octocode-code-engineer/references/agent-ast-reading-rfc.md +0 -95
- package/skills/octocode-code-engineer/references/architecture-techniques.md +0 -121
- package/skills/octocode-code-engineer/references/ast-search.md +0 -210
- package/skills/octocode-code-engineer/references/ast-tree-search.md +0 -151
- package/skills/octocode-code-engineer/references/concepts.md +0 -107
- package/skills/octocode-code-engineer/references/finding-categories.md +0 -128
- package/skills/octocode-code-engineer/references/improvement-roadmap.md +0 -304
- package/skills/octocode-code-engineer/references/playbooks.md +0 -204
- package/skills/octocode-code-engineer/references/present-results.md +0 -136
- package/skills/octocode-code-engineer/references/tool-workflows.md +0 -566
- package/skills/octocode-code-engineer/references/validate-investigate.md +0 -225
- package/skills/octocode-code-engineer/scripts/analysis/dependencies.js +0 -1
- package/skills/octocode-code-engineer/scripts/analysis/dependency-summary.js +0 -1
- package/skills/octocode-code-engineer/scripts/analysis/discovery.js +0 -1
- package/skills/octocode-code-engineer/scripts/analysis/graph-analytics.js +0 -1
- package/skills/octocode-code-engineer/scripts/analysis/semantic.js +0 -1
- package/skills/octocode-code-engineer/scripts/ast/helpers.js +0 -1
- package/skills/octocode-code-engineer/scripts/ast/metrics.js +0 -1
- package/skills/octocode-code-engineer/scripts/ast/search.js +0 -2
- package/skills/octocode-code-engineer/scripts/ast/tree-search.js +0 -2
- package/skills/octocode-code-engineer/scripts/ast/tree-sitter.js +0 -1
- package/skills/octocode-code-engineer/scripts/ast/ts-analyzer.js +0 -1
- package/skills/octocode-code-engineer/scripts/collectors/chains.js +0 -1
- package/skills/octocode-code-engineer/scripts/collectors/effects.js +0 -1
- package/skills/octocode-code-engineer/scripts/collectors/input-sources.js +0 -1
- package/skills/octocode-code-engineer/scripts/collectors/performance.js +0 -1
- package/skills/octocode-code-engineer/scripts/collectors/prototype-pollution.js +0 -1
- package/skills/octocode-code-engineer/scripts/collectors/security.js +0 -1
- package/skills/octocode-code-engineer/scripts/collectors/test-profile.js +0 -1
- package/skills/octocode-code-engineer/scripts/common/is-direct-run.js +0 -1
- package/skills/octocode-code-engineer/scripts/common/utils.js +0 -1
- package/skills/octocode-code-engineer/scripts/detectors/code-quality.js +0 -1
- package/skills/octocode-code-engineer/scripts/detectors/cohesion.js +0 -1
- package/skills/octocode-code-engineer/scripts/detectors/coupling.js +0 -1
- package/skills/octocode-code-engineer/scripts/detectors/cycle.js +0 -1
- package/skills/octocode-code-engineer/scripts/detectors/dead-code.js +0 -1
- package/skills/octocode-code-engineer/scripts/detectors/import-style.js +0 -1
- package/skills/octocode-code-engineer/scripts/detectors/index.js +0 -1
- package/skills/octocode-code-engineer/scripts/detectors/security.js +0 -1
- package/skills/octocode-code-engineer/scripts/detectors/semantic.js +0 -1
- package/skills/octocode-code-engineer/scripts/detectors/shared.js +0 -1
- package/skills/octocode-code-engineer/scripts/detectors/test-quality.js +0 -1
- package/skills/octocode-code-engineer/scripts/index.js +0 -1
- package/skills/octocode-code-engineer/scripts/pipeline/cache.js +0 -1
- package/skills/octocode-code-engineer/scripts/pipeline/cli.js +0 -1
- package/skills/octocode-code-engineer/scripts/pipeline/main.js +0 -2
- package/skills/octocode-code-engineer/scripts/reporting/analysis.js +0 -1
- package/skills/octocode-code-engineer/scripts/reporting/summary-md.js +0 -1
- package/skills/octocode-code-engineer/scripts/reporting/writer.js +0 -1
- package/skills/octocode-code-engineer/scripts/types/constants.js +0 -1
- package/skills/octocode-code-engineer/scripts/types/index.js +0 -1
- package/skills/octocode-code-engineer/scripts/types/interfaces.js +0 -1
- package/skills/octocode-code-engineer/src/analysis/dependencies.ts +0 -406
- package/skills/octocode-code-engineer/src/index.ts +0 -403
- package/skills/octocode-code-engineer/src/reporting/summary-md.test.ts +0 -421
- package/skills/octocode-code-engineer/src/reporting/summary-md.ts +0 -714
- package/skills/octocode-code-engineer/src/types/interfaces.ts +0 -682
- package/skills/octocode-research/src/types/toolTypes.ts +0 -33
- package/skills/octocode-research/src/utils/logEmoji.ts +0 -103
- /package/skills/{octocode-code-engineer → octocode-engineer}/.octocode/rfc/RFC-code-engineer-weakness-fixes.md +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/architecture.ts.html +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/ast-helpers.ts.html +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/ast-search.ts.html +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/base.css +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/block-navigation.js +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/cache.ts.html +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/cli.ts.html +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/clover.xml +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/collect-effects.ts.html +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/collect-input-sources.ts.html +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/collect-performance.ts.html +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/collect-prototype-pollution.ts.html +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/collect-security.ts.html +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/collect-test-profile.ts.html +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/coverage-final.json +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/dependencies.ts.html +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/dependency-summary.ts.html +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/discovery.ts.html +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/favicon.png +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/graph-analytics.ts.html +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/index.html +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/index.ts.html +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/metrics.ts.html +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/pipeline.ts.html +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/prettify.css +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/prettify.js +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/report-analysis.ts.html +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/report-writer.ts.html +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/security-detectors.ts.html +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/semantic-detectors.ts.html +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/semantic.ts.html +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/sort-arrow-sprite.png +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/sorter.js +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/summary-md.ts.html +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/test-quality-detectors.ts.html +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/tree-sitter-analyzer.ts.html +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/ts-analyzer.ts.html +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/types.ts.html +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/coverage/utils.ts.html +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/src/analysis/dependencies.test.ts +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/src/analysis/dependency-summary.test.ts +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/src/analysis/dependency-summary.ts +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/src/analysis/graph-analytics.test.ts +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/src/analysis/graph-analytics.ts +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/src/analysis/semantic.test.ts +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/src/analysis/semantic.ts +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/src/ast/helpers.test.ts +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/src/ast/helpers.ts +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/src/ast/metrics.test.ts +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/src/ast/metrics.ts +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/src/ast/tree-search.test.ts +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/src/ast/tree-search.ts +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/src/collectors/chains.ts +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/src/collectors/effects.test.ts +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/src/collectors/effects.ts +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/src/collectors/input-sources.test.ts +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/src/collectors/input-sources.ts +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/src/collectors/performance.test.ts +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/src/collectors/performance.ts +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/src/collectors/prototype-pollution.test.ts +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/src/collectors/prototype-pollution.ts +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/src/collectors/test-profile.test.ts +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/src/collectors/test-profile.ts +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/src/common/is-direct-run.test.ts +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/src/common/is-direct-run.ts +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/src/common/utils.test.ts +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/src/common/utils.ts +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/src/detectors/cohesion.ts +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/src/detectors/coupling.ts +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/src/detectors/cycle.ts +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/src/detectors/dead-code.ts +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/src/detectors/import-style.ts +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/src/detectors/index.test.ts +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/src/detectors/security.test.ts +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/src/detectors/security.ts +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/src/detectors/semantic.ts +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/src/detectors/shared.ts +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/src/detectors/test-quality.test.ts +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/src/detectors/test-quality.ts +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/src/pipeline/cache.test.ts +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/src/pipeline/cache.ts +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/src/pipeline/main.test.ts +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/src/pipeline.test.ts +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/src/reporting/analysis.test.ts +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/src/reporting/analysis.ts +0 -0
- /package/skills/{octocode-code-engineer → octocode-engineer}/vitest.config.ts +0 -0
|
@@ -1153,7 +1153,7 @@ describe('category group constants', () => {
|
|
|
1153
1153
|
});
|
|
1154
1154
|
|
|
1155
1155
|
it('code quality group has expected categories', () => {
|
|
1156
|
-
expect(CODE_QUALITY_CATEGORIES.size).toBe(
|
|
1156
|
+
expect(CODE_QUALITY_CATEGORIES.size).toBe(34);
|
|
1157
1157
|
});
|
|
1158
1158
|
|
|
1159
1159
|
it('dead code group has 11 categories', () => {
|
|
@@ -2094,13 +2094,18 @@ describe('end-to-end output validation', () => {
|
|
|
2094
2094
|
it('produces valid summary.md with all sections', async () => {
|
|
2095
2095
|
const { execSync } = await import('node:child_process');
|
|
2096
2096
|
const dir = '/tmp/cq-test-' + Date.now();
|
|
2097
|
-
const scriptPath = path.join(process.cwd(), 'scripts', '
|
|
2097
|
+
const scriptPath = path.join(process.cwd(), 'scripts', 'run.js');
|
|
2098
2098
|
const monorepoRoot = path.join(process.cwd(), '..', '..');
|
|
2099
2099
|
try {
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2100
|
+
try {
|
|
2101
|
+
execSync(
|
|
2102
|
+
`node "${scriptPath}" --root "${monorepoRoot}" --out "${dir}" --no-tree`,
|
|
2103
|
+
{ cwd: process.cwd(), encoding: 'utf8', timeout: 30000 }
|
|
2104
|
+
);
|
|
2105
|
+
} catch (execErr: unknown) {
|
|
2106
|
+
const e = execErr as { status?: number };
|
|
2107
|
+
if (e.status !== 1) throw execErr;
|
|
2108
|
+
}
|
|
2104
2109
|
|
|
2105
2110
|
expect(fs.existsSync(`${dir}/summary.md`)).toBe(true);
|
|
2106
2111
|
expect(fs.existsSync(`${dir}/summary.json`)).toBe(true);
|
|
@@ -3336,9 +3341,7 @@ describe('buildIssueCatalog detector paths', () => {
|
|
|
3336
3341
|
...DEFAULT_OPTS,
|
|
3337
3342
|
root: '/repo',
|
|
3338
3343
|
findingsLimit: 500,
|
|
3339
|
-
anyThreshold: 5,
|
|
3340
|
-
halsteadEffortThreshold: 500_000,
|
|
3341
|
-
maintainabilityIndexThreshold: 20,
|
|
3344
|
+
thresholds: { ...DEFAULT_OPTS.thresholds, anyThreshold: 5, halsteadEffortThreshold: 500_000, maintainabilityIndexThreshold: 20 },
|
|
3342
3345
|
};
|
|
3343
3346
|
|
|
3344
3347
|
it('detects dead exports via declaredExportsByFile without consumedFromModule', () => {
|
|
@@ -4394,7 +4397,7 @@ describe('buildIssueCatalog detector paths via buildIssueCatalog', () => {
|
|
|
4394
4397
|
],
|
|
4395
4398
|
},
|
|
4396
4399
|
];
|
|
4397
|
-
const optsWithFlow = { ...opts, flowDupThreshold: 3 };
|
|
4400
|
+
const optsWithFlow = { ...opts, thresholds: { ...opts.thresholds, flowDupThreshold: 3 } };
|
|
4398
4401
|
const { findings } = buildIssueCatalog(
|
|
4399
4402
|
[],
|
|
4400
4403
|
controlDuplicates,
|
|
@@ -4411,7 +4414,7 @@ describe('buildIssueCatalog detector paths via buildIssueCatalog', () => {
|
|
|
4411
4414
|
it('detectLayerViolations: layerOrder triggers when lower layer imports from upper', () => {
|
|
4412
4415
|
const state = emptyState();
|
|
4413
4416
|
addEdge(state, 'src/repository/db.ts', 'src/service/handler.ts');
|
|
4414
|
-
const optsWithLayers = { ...opts, layerOrder: ['service', 'repository'] };
|
|
4417
|
+
const optsWithLayers = { ...opts, thresholds: { ...opts.thresholds, layerOrder: ['service', 'repository'] } };
|
|
4415
4418
|
const { findings } = buildIssueCatalog(
|
|
4416
4419
|
[],
|
|
4417
4420
|
[],
|
|
@@ -4423,3 +4426,554 @@ describe('buildIssueCatalog detector paths via buildIssueCatalog', () => {
|
|
|
4423
4426
|
expect(findings.some(f => f.category === 'layer-violation')).toBe(true);
|
|
4424
4427
|
});
|
|
4425
4428
|
});
|
|
4429
|
+
|
|
4430
|
+
describe('new v2 quality detectors via buildIssueCatalog', () => {
|
|
4431
|
+
const testOpts2 = { ...DEFAULT_OPTS, findingsLimit: 500, includeTests: false };
|
|
4432
|
+
|
|
4433
|
+
function makeEntry2(
|
|
4434
|
+
file: string,
|
|
4435
|
+
overrides: Partial<FileEntry> = {}
|
|
4436
|
+
): FileEntry {
|
|
4437
|
+
return {
|
|
4438
|
+
package: 'test-pkg',
|
|
4439
|
+
file,
|
|
4440
|
+
parseEngine: 'typescript',
|
|
4441
|
+
nodeCount: 0,
|
|
4442
|
+
kindCounts: {},
|
|
4443
|
+
functions: [],
|
|
4444
|
+
flows: [],
|
|
4445
|
+
dependencyProfile: {
|
|
4446
|
+
internalDependencies: [],
|
|
4447
|
+
externalDependencies: [],
|
|
4448
|
+
unresolvedDependencies: [],
|
|
4449
|
+
declaredExports: [],
|
|
4450
|
+
importedSymbols: [],
|
|
4451
|
+
reExports: [],
|
|
4452
|
+
},
|
|
4453
|
+
...overrides,
|
|
4454
|
+
};
|
|
4455
|
+
}
|
|
4456
|
+
|
|
4457
|
+
describe('detectDeepNesting', () => {
|
|
4458
|
+
it('triggers when function has branch depth >= threshold', () => {
|
|
4459
|
+
const entry = makeEntry2('src/deep.ts', {
|
|
4460
|
+
functions: [
|
|
4461
|
+
makeFn({
|
|
4462
|
+
name: 'deepFn',
|
|
4463
|
+
file: 'src/deep.ts',
|
|
4464
|
+
maxBranchDepth: 6,
|
|
4465
|
+
maxLoopDepth: 0,
|
|
4466
|
+
statementCount: 20,
|
|
4467
|
+
}),
|
|
4468
|
+
],
|
|
4469
|
+
});
|
|
4470
|
+
const { findings } = buildIssueCatalog(
|
|
4471
|
+
[], [], [entry], minimalDepSummary(), emptyState(), testOpts2
|
|
4472
|
+
);
|
|
4473
|
+
const deep = findings.filter(f => f.category === 'deep-nesting');
|
|
4474
|
+
expect(deep.length).toBe(1);
|
|
4475
|
+
expect(deep[0].title).toContain('6');
|
|
4476
|
+
expect(deep[0].title).toContain('deepFn');
|
|
4477
|
+
});
|
|
4478
|
+
|
|
4479
|
+
it('triggers when function has loop depth >= threshold', () => {
|
|
4480
|
+
const entry = makeEntry2('src/loops.ts', {
|
|
4481
|
+
functions: [
|
|
4482
|
+
makeFn({
|
|
4483
|
+
name: 'loopFn',
|
|
4484
|
+
file: 'src/loops.ts',
|
|
4485
|
+
maxBranchDepth: 1,
|
|
4486
|
+
maxLoopDepth: 7,
|
|
4487
|
+
statementCount: 15,
|
|
4488
|
+
}),
|
|
4489
|
+
],
|
|
4490
|
+
});
|
|
4491
|
+
const { findings } = buildIssueCatalog(
|
|
4492
|
+
[], [], [entry], minimalDepSummary(), emptyState(), testOpts2
|
|
4493
|
+
);
|
|
4494
|
+
expect(findings.some(f => f.category === 'deep-nesting')).toBe(true);
|
|
4495
|
+
});
|
|
4496
|
+
|
|
4497
|
+
it('does not trigger when depth is below threshold', () => {
|
|
4498
|
+
const entry = makeEntry2('src/shallow.ts', {
|
|
4499
|
+
functions: [
|
|
4500
|
+
makeFn({ maxBranchDepth: 2, maxLoopDepth: 1, statementCount: 10 }),
|
|
4501
|
+
],
|
|
4502
|
+
});
|
|
4503
|
+
const { findings } = buildIssueCatalog(
|
|
4504
|
+
[], [], [entry], minimalDepSummary(), emptyState(), testOpts2
|
|
4505
|
+
);
|
|
4506
|
+
expect(findings.some(f => f.category === 'deep-nesting')).toBe(false);
|
|
4507
|
+
});
|
|
4508
|
+
|
|
4509
|
+
it('skips test files', () => {
|
|
4510
|
+
const entry = makeEntry2('src/__tests__/deep.test.ts', {
|
|
4511
|
+
functions: [
|
|
4512
|
+
makeFn({ maxBranchDepth: 10, maxLoopDepth: 10, statementCount: 50 }),
|
|
4513
|
+
],
|
|
4514
|
+
});
|
|
4515
|
+
const { findings } = buildIssueCatalog(
|
|
4516
|
+
[], [], [entry], minimalDepSummary(), emptyState(), testOpts2
|
|
4517
|
+
);
|
|
4518
|
+
expect(findings.some(f => f.category === 'deep-nesting')).toBe(false);
|
|
4519
|
+
});
|
|
4520
|
+
|
|
4521
|
+
it('severity scales with nesting depth', () => {
|
|
4522
|
+
const low = makeEntry2('src/low.ts', {
|
|
4523
|
+
functions: [makeFn({ name: 'fn', file: 'src/low.ts', maxBranchDepth: 5, statementCount: 10 })],
|
|
4524
|
+
});
|
|
4525
|
+
const high = makeEntry2('src/high.ts', {
|
|
4526
|
+
functions: [makeFn({ name: 'fn', file: 'src/high.ts', maxBranchDepth: 9, statementCount: 10 })],
|
|
4527
|
+
});
|
|
4528
|
+
const { findings: lowF } = buildIssueCatalog(
|
|
4529
|
+
[], [], [low], minimalDepSummary(), emptyState(), testOpts2
|
|
4530
|
+
);
|
|
4531
|
+
const { findings: highF } = buildIssueCatalog(
|
|
4532
|
+
[], [], [high], minimalDepSummary(), emptyState(), testOpts2
|
|
4533
|
+
);
|
|
4534
|
+
const lowSev = lowF.find(f => f.category === 'deep-nesting')?.severity;
|
|
4535
|
+
const highSev = highF.find(f => f.category === 'deep-nesting')?.severity;
|
|
4536
|
+
expect(lowSev).toBe('low');
|
|
4537
|
+
expect(highSev).toBe('high');
|
|
4538
|
+
});
|
|
4539
|
+
});
|
|
4540
|
+
|
|
4541
|
+
describe('detectMultipleReturnPaths', () => {
|
|
4542
|
+
it('triggers when function has returns >= threshold', () => {
|
|
4543
|
+
const entry = makeEntry2('src/multi.ts', {
|
|
4544
|
+
functions: [
|
|
4545
|
+
makeFn({
|
|
4546
|
+
name: 'multiFn',
|
|
4547
|
+
file: 'src/multi.ts',
|
|
4548
|
+
returns: 7,
|
|
4549
|
+
statementCount: 20,
|
|
4550
|
+
}),
|
|
4551
|
+
],
|
|
4552
|
+
});
|
|
4553
|
+
const { findings } = buildIssueCatalog(
|
|
4554
|
+
[], [], [entry], minimalDepSummary(), emptyState(), testOpts2
|
|
4555
|
+
);
|
|
4556
|
+
const multi = findings.filter(f => f.category === 'multiple-return-paths');
|
|
4557
|
+
expect(multi.length).toBe(1);
|
|
4558
|
+
expect(multi[0].title).toContain('7');
|
|
4559
|
+
});
|
|
4560
|
+
|
|
4561
|
+
it('does not trigger below threshold', () => {
|
|
4562
|
+
const entry = makeEntry2('src/few.ts', {
|
|
4563
|
+
functions: [makeFn({ returns: 3, statementCount: 10 })],
|
|
4564
|
+
});
|
|
4565
|
+
const { findings } = buildIssueCatalog(
|
|
4566
|
+
[], [], [entry], minimalDepSummary(), emptyState(), testOpts2
|
|
4567
|
+
);
|
|
4568
|
+
expect(findings.some(f => f.category === 'multiple-return-paths')).toBe(false);
|
|
4569
|
+
});
|
|
4570
|
+
|
|
4571
|
+
it('skips test files', () => {
|
|
4572
|
+
const entry = makeEntry2('src/x.test.ts', {
|
|
4573
|
+
functions: [makeFn({ returns: 20, statementCount: 50 })],
|
|
4574
|
+
});
|
|
4575
|
+
const { findings } = buildIssueCatalog(
|
|
4576
|
+
[], [], [entry], minimalDepSummary(), emptyState(), testOpts2
|
|
4577
|
+
);
|
|
4578
|
+
expect(findings.some(f => f.category === 'multiple-return-paths')).toBe(false);
|
|
4579
|
+
});
|
|
4580
|
+
});
|
|
4581
|
+
|
|
4582
|
+
describe('detectCatchRethrow', () => {
|
|
4583
|
+
it('triggers from pre-collected catchRethrows data', () => {
|
|
4584
|
+
const entry = makeEntry2('src/rethrow.ts', {
|
|
4585
|
+
catchRethrows: [
|
|
4586
|
+
{ file: 'src/rethrow.ts', lineStart: 5, lineEnd: 8 },
|
|
4587
|
+
],
|
|
4588
|
+
});
|
|
4589
|
+
const { findings } = buildIssueCatalog(
|
|
4590
|
+
[], [], [entry], minimalDepSummary(), emptyState(), testOpts2
|
|
4591
|
+
);
|
|
4592
|
+
const cr = findings.filter(f => f.category === 'catch-rethrow');
|
|
4593
|
+
expect(cr.length).toBe(1);
|
|
4594
|
+
expect(cr[0].severity).toBe('low');
|
|
4595
|
+
expect(cr[0].title).toContain('Catch-rethrow');
|
|
4596
|
+
});
|
|
4597
|
+
|
|
4598
|
+
it('does not trigger when no catchRethrows', () => {
|
|
4599
|
+
const entry = makeEntry2('src/clean.ts');
|
|
4600
|
+
const { findings } = buildIssueCatalog(
|
|
4601
|
+
[], [], [entry], minimalDepSummary(), emptyState(), testOpts2
|
|
4602
|
+
);
|
|
4603
|
+
expect(findings.some(f => f.category === 'catch-rethrow')).toBe(false);
|
|
4604
|
+
});
|
|
4605
|
+
|
|
4606
|
+
it('skips test files', () => {
|
|
4607
|
+
const entry = makeEntry2('src/__tests__/rethrow.test.ts', {
|
|
4608
|
+
catchRethrows: [
|
|
4609
|
+
{ file: 'src/__tests__/rethrow.test.ts', lineStart: 5, lineEnd: 8 },
|
|
4610
|
+
],
|
|
4611
|
+
});
|
|
4612
|
+
const { findings } = buildIssueCatalog(
|
|
4613
|
+
[], [], [entry], minimalDepSummary(), emptyState(), testOpts2
|
|
4614
|
+
);
|
|
4615
|
+
expect(findings.some(f => f.category === 'catch-rethrow')).toBe(false);
|
|
4616
|
+
});
|
|
4617
|
+
});
|
|
4618
|
+
|
|
4619
|
+
describe('detectMagicStrings', () => {
|
|
4620
|
+
it('triggers when string appears >= minOccurrences across files', () => {
|
|
4621
|
+
const e1 = makeEntry2('src/a.ts', {
|
|
4622
|
+
magicStrings: [
|
|
4623
|
+
{ file: 'src/a.ts', lineStart: 1, lineEnd: 1, value: 'active' },
|
|
4624
|
+
{ file: 'src/a.ts', lineStart: 5, lineEnd: 5, value: 'active' },
|
|
4625
|
+
],
|
|
4626
|
+
});
|
|
4627
|
+
const e2 = makeEntry2('src/b.ts', {
|
|
4628
|
+
magicStrings: [
|
|
4629
|
+
{ file: 'src/b.ts', lineStart: 3, lineEnd: 3, value: 'active' },
|
|
4630
|
+
],
|
|
4631
|
+
});
|
|
4632
|
+
const { findings } = buildIssueCatalog(
|
|
4633
|
+
[], [], [e1, e2], minimalDepSummary(), emptyState(), testOpts2
|
|
4634
|
+
);
|
|
4635
|
+
const ms = findings.filter(f => f.category === 'magic-string');
|
|
4636
|
+
expect(ms.length).toBe(1);
|
|
4637
|
+
expect(ms[0].title).toContain('active');
|
|
4638
|
+
expect(ms[0].title).toContain('3');
|
|
4639
|
+
});
|
|
4640
|
+
|
|
4641
|
+
it('does not trigger below minOccurrences', () => {
|
|
4642
|
+
const entry = makeEntry2('src/single.ts', {
|
|
4643
|
+
magicStrings: [
|
|
4644
|
+
{ file: 'src/single.ts', lineStart: 1, lineEnd: 1, value: 'rare' },
|
|
4645
|
+
{ file: 'src/single.ts', lineStart: 3, lineEnd: 3, value: 'rare' },
|
|
4646
|
+
],
|
|
4647
|
+
});
|
|
4648
|
+
const { findings } = buildIssueCatalog(
|
|
4649
|
+
[], [], [entry], minimalDepSummary(), emptyState(), testOpts2
|
|
4650
|
+
);
|
|
4651
|
+
expect(findings.some(f => f.category === 'magic-string')).toBe(false);
|
|
4652
|
+
});
|
|
4653
|
+
|
|
4654
|
+
it('severity scales with occurrence count', () => {
|
|
4655
|
+
const entries = Array.from({ length: 1 }, () =>
|
|
4656
|
+
makeEntry2('src/many.ts', {
|
|
4657
|
+
magicStrings: Array.from({ length: 9 }, (_, i) => ({
|
|
4658
|
+
file: 'src/many.ts',
|
|
4659
|
+
lineStart: i + 1,
|
|
4660
|
+
lineEnd: i + 1,
|
|
4661
|
+
value: 'status',
|
|
4662
|
+
})),
|
|
4663
|
+
})
|
|
4664
|
+
);
|
|
4665
|
+
const { findings } = buildIssueCatalog(
|
|
4666
|
+
[], [], entries, minimalDepSummary(), emptyState(), testOpts2
|
|
4667
|
+
);
|
|
4668
|
+
const ms = findings.filter(f => f.category === 'magic-string');
|
|
4669
|
+
expect(ms.length).toBe(1);
|
|
4670
|
+
expect(ms[0].severity).toBe('high');
|
|
4671
|
+
});
|
|
4672
|
+
});
|
|
4673
|
+
|
|
4674
|
+
describe('detectBooleanParameterCluster', () => {
|
|
4675
|
+
it('triggers from pre-collected booleanParamClusters', () => {
|
|
4676
|
+
const entry = makeEntry2('src/flags.ts', {
|
|
4677
|
+
booleanParamClusters: [
|
|
4678
|
+
{
|
|
4679
|
+
name: 'configure',
|
|
4680
|
+
booleanCount: 3,
|
|
4681
|
+
totalParams: 4,
|
|
4682
|
+
lineStart: 1,
|
|
4683
|
+
lineEnd: 5,
|
|
4684
|
+
},
|
|
4685
|
+
],
|
|
4686
|
+
});
|
|
4687
|
+
const { findings } = buildIssueCatalog(
|
|
4688
|
+
[], [], [entry], minimalDepSummary(), emptyState(), testOpts2
|
|
4689
|
+
);
|
|
4690
|
+
const bp = findings.filter(f => f.category === 'boolean-parameter-cluster');
|
|
4691
|
+
expect(bp.length).toBe(1);
|
|
4692
|
+
expect(bp[0].severity).toBe('medium');
|
|
4693
|
+
expect(bp[0].title).toContain('3');
|
|
4694
|
+
expect(bp[0].title).toContain('configure');
|
|
4695
|
+
});
|
|
4696
|
+
|
|
4697
|
+
it('does not trigger when below threshold', () => {
|
|
4698
|
+
const entry = makeEntry2('src/few.ts', {
|
|
4699
|
+
booleanParamClusters: [
|
|
4700
|
+
{ name: 'fn', booleanCount: 2, totalParams: 3, lineStart: 1, lineEnd: 3 },
|
|
4701
|
+
],
|
|
4702
|
+
});
|
|
4703
|
+
const { findings } = buildIssueCatalog(
|
|
4704
|
+
[], [], [entry], minimalDepSummary(), emptyState(), testOpts2
|
|
4705
|
+
);
|
|
4706
|
+
expect(findings.some(f => f.category === 'boolean-parameter-cluster')).toBe(false);
|
|
4707
|
+
});
|
|
4708
|
+
|
|
4709
|
+
it('skips test files', () => {
|
|
4710
|
+
const entry = makeEntry2('src/flags.test.ts', {
|
|
4711
|
+
booleanParamClusters: [
|
|
4712
|
+
{ name: 'testFn', booleanCount: 4, totalParams: 4, lineStart: 1, lineEnd: 5 },
|
|
4713
|
+
],
|
|
4714
|
+
});
|
|
4715
|
+
const { findings } = buildIssueCatalog(
|
|
4716
|
+
[], [], [entry], minimalDepSummary(), emptyState(), testOpts2
|
|
4717
|
+
);
|
|
4718
|
+
expect(findings.some(f => f.category === 'boolean-parameter-cluster')).toBe(false);
|
|
4719
|
+
});
|
|
4720
|
+
});
|
|
4721
|
+
|
|
4722
|
+
describe('detectPromiseAllUnhandled', () => {
|
|
4723
|
+
it('triggers from pre-collected promiseAllUnhandled', () => {
|
|
4724
|
+
const entry = makeEntry2('src/fetch.ts', {
|
|
4725
|
+
promiseAllUnhandled: [
|
|
4726
|
+
{ file: 'src/fetch.ts', lineStart: 10, lineEnd: 10, kind: 'Promise.all' },
|
|
4727
|
+
],
|
|
4728
|
+
});
|
|
4729
|
+
const { findings } = buildIssueCatalog(
|
|
4730
|
+
[], [], [entry], minimalDepSummary(), emptyState(), testOpts2
|
|
4731
|
+
);
|
|
4732
|
+
const pa = findings.filter(f => f.category === 'promise-all-unhandled');
|
|
4733
|
+
expect(pa.length).toBe(1);
|
|
4734
|
+
expect(pa[0].severity).toBe('medium');
|
|
4735
|
+
expect(pa[0].title).toContain('Promise.all');
|
|
4736
|
+
});
|
|
4737
|
+
|
|
4738
|
+
it('detects multiple kinds (race, any)', () => {
|
|
4739
|
+
const entry = makeEntry2('src/multi.ts', {
|
|
4740
|
+
promiseAllUnhandled: [
|
|
4741
|
+
{ file: 'src/multi.ts', lineStart: 1, lineEnd: 1, kind: 'Promise.race' },
|
|
4742
|
+
{ file: 'src/multi.ts', lineStart: 5, lineEnd: 5, kind: 'Promise.any' },
|
|
4743
|
+
],
|
|
4744
|
+
});
|
|
4745
|
+
const { findings } = buildIssueCatalog(
|
|
4746
|
+
[], [], [entry], minimalDepSummary(), emptyState(), testOpts2
|
|
4747
|
+
);
|
|
4748
|
+
const pa = findings.filter(f => f.category === 'promise-all-unhandled');
|
|
4749
|
+
expect(pa.length).toBe(2);
|
|
4750
|
+
expect(pa.some(f => f.title.includes('Promise.race'))).toBe(true);
|
|
4751
|
+
expect(pa.some(f => f.title.includes('Promise.any'))).toBe(true);
|
|
4752
|
+
});
|
|
4753
|
+
|
|
4754
|
+
it('skips test files', () => {
|
|
4755
|
+
const entry = makeEntry2('src/fetch.spec.ts', {
|
|
4756
|
+
promiseAllUnhandled: [
|
|
4757
|
+
{ file: 'src/fetch.spec.ts', lineStart: 1, lineEnd: 1, kind: 'Promise.all' },
|
|
4758
|
+
],
|
|
4759
|
+
});
|
|
4760
|
+
const { findings } = buildIssueCatalog(
|
|
4761
|
+
[], [], [entry], minimalDepSummary(), emptyState(), testOpts2
|
|
4762
|
+
);
|
|
4763
|
+
expect(findings.some(f => f.category === 'promise-all-unhandled')).toBe(false);
|
|
4764
|
+
});
|
|
4765
|
+
});
|
|
4766
|
+
|
|
4767
|
+
describe('detectExportSurfaceDensity', () => {
|
|
4768
|
+
it('triggers when export ratio >= 50%', () => {
|
|
4769
|
+
const fns = Array.from({ length: 5 }, (_, i) =>
|
|
4770
|
+
makeFn({
|
|
4771
|
+
name: `fn${i}`,
|
|
4772
|
+
file: 'src/dense.ts',
|
|
4773
|
+
statementCount: 5,
|
|
4774
|
+
})
|
|
4775
|
+
);
|
|
4776
|
+
const entry = makeEntry2('src/dense.ts', {
|
|
4777
|
+
functions: fns,
|
|
4778
|
+
dependencyProfile: {
|
|
4779
|
+
internalDependencies: [],
|
|
4780
|
+
externalDependencies: [],
|
|
4781
|
+
unresolvedDependencies: [],
|
|
4782
|
+
declaredExports: Array.from({ length: 15 }, (_, i) => ({
|
|
4783
|
+
name: `export${i}`,
|
|
4784
|
+
kind: 'function' as const,
|
|
4785
|
+
isType: false,
|
|
4786
|
+
isDefault: false,
|
|
4787
|
+
})),
|
|
4788
|
+
importedSymbols: [],
|
|
4789
|
+
reExports: [],
|
|
4790
|
+
},
|
|
4791
|
+
});
|
|
4792
|
+
const { findings } = buildIssueCatalog(
|
|
4793
|
+
[], [], [entry], minimalDepSummary(), emptyState(), testOpts2
|
|
4794
|
+
);
|
|
4795
|
+
const esd = findings.filter(f => f.category === 'export-surface-density');
|
|
4796
|
+
expect(esd.length).toBe(1);
|
|
4797
|
+
expect(esd[0].title).toContain('%');
|
|
4798
|
+
});
|
|
4799
|
+
|
|
4800
|
+
it('does not trigger when ratio < 50%', () => {
|
|
4801
|
+
const fns = Array.from({ length: 10 }, (_, i) =>
|
|
4802
|
+
makeFn({ name: `fn${i}`, file: 'src/normal.ts', statementCount: 10 })
|
|
4803
|
+
);
|
|
4804
|
+
const entry = makeEntry2('src/normal.ts', {
|
|
4805
|
+
functions: fns,
|
|
4806
|
+
dependencyProfile: {
|
|
4807
|
+
internalDependencies: [],
|
|
4808
|
+
externalDependencies: [],
|
|
4809
|
+
unresolvedDependencies: [],
|
|
4810
|
+
declaredExports: [
|
|
4811
|
+
{ name: 'main', kind: 'function' as const, isType: false, isDefault: false },
|
|
4812
|
+
],
|
|
4813
|
+
importedSymbols: [],
|
|
4814
|
+
reExports: [],
|
|
4815
|
+
},
|
|
4816
|
+
});
|
|
4817
|
+
const { findings } = buildIssueCatalog(
|
|
4818
|
+
[], [], [entry], minimalDepSummary(), emptyState(), testOpts2
|
|
4819
|
+
);
|
|
4820
|
+
expect(findings.some(f => f.category === 'export-surface-density')).toBe(false);
|
|
4821
|
+
});
|
|
4822
|
+
|
|
4823
|
+
it('does not trigger for small files (< 20 statements)', () => {
|
|
4824
|
+
const entry = makeEntry2('src/small.ts', {
|
|
4825
|
+
functions: [makeFn({ statementCount: 5, file: 'src/small.ts' })],
|
|
4826
|
+
dependencyProfile: {
|
|
4827
|
+
internalDependencies: [],
|
|
4828
|
+
externalDependencies: [],
|
|
4829
|
+
unresolvedDependencies: [],
|
|
4830
|
+
declaredExports: Array.from({ length: 5 }, (_, i) => ({
|
|
4831
|
+
name: `e${i}`,
|
|
4832
|
+
kind: 'function' as const,
|
|
4833
|
+
isType: false,
|
|
4834
|
+
isDefault: false,
|
|
4835
|
+
})),
|
|
4836
|
+
importedSymbols: [],
|
|
4837
|
+
reExports: [],
|
|
4838
|
+
},
|
|
4839
|
+
});
|
|
4840
|
+
const { findings } = buildIssueCatalog(
|
|
4841
|
+
[], [], [entry], minimalDepSummary(), emptyState(), testOpts2
|
|
4842
|
+
);
|
|
4843
|
+
expect(findings.some(f => f.category === 'export-surface-density')).toBe(false);
|
|
4844
|
+
});
|
|
4845
|
+
});
|
|
4846
|
+
|
|
4847
|
+
describe('detectChangeRisk', () => {
|
|
4848
|
+
it('triggers when multiple quality signals overlap', () => {
|
|
4849
|
+
const entry = makeEntry2('src/risky.ts', {
|
|
4850
|
+
functions: [
|
|
4851
|
+
makeFn({
|
|
4852
|
+
name: 'complexFn',
|
|
4853
|
+
file: 'src/risky.ts',
|
|
4854
|
+
complexity: 25,
|
|
4855
|
+
cognitiveComplexity: 30,
|
|
4856
|
+
maintainabilityIndex: 10,
|
|
4857
|
+
statementCount: 50,
|
|
4858
|
+
}),
|
|
4859
|
+
makeFn({
|
|
4860
|
+
name: 'anotherFn',
|
|
4861
|
+
file: 'src/risky.ts',
|
|
4862
|
+
complexity: 20,
|
|
4863
|
+
cognitiveComplexity: 25,
|
|
4864
|
+
maintainabilityIndex: 15,
|
|
4865
|
+
statementCount: 30,
|
|
4866
|
+
}),
|
|
4867
|
+
],
|
|
4868
|
+
emptyCatches: [{ file: 'src/risky.ts', lineStart: 10, lineEnd: 12 }],
|
|
4869
|
+
promiseAllUnhandled: [
|
|
4870
|
+
{ file: 'src/risky.ts', lineStart: 20, lineEnd: 20, kind: 'Promise.all' as const },
|
|
4871
|
+
],
|
|
4872
|
+
});
|
|
4873
|
+
const { findings } = buildIssueCatalog(
|
|
4874
|
+
[], [], [entry], minimalDepSummary(), emptyState(), testOpts2
|
|
4875
|
+
);
|
|
4876
|
+
const cr = findings.filter(f => f.category === 'change-risk');
|
|
4877
|
+
expect(cr.length).toBe(1);
|
|
4878
|
+
expect(['medium', 'high', 'critical']).toContain(cr[0].severity);
|
|
4879
|
+
expect(cr[0].title).toContain('Change-risk score');
|
|
4880
|
+
});
|
|
4881
|
+
|
|
4882
|
+
it('does not trigger when risk score < 4', () => {
|
|
4883
|
+
const entry = makeEntry2('src/clean.ts', {
|
|
4884
|
+
functions: [
|
|
4885
|
+
makeFn({ complexity: 3, cognitiveComplexity: 2, statementCount: 10 }),
|
|
4886
|
+
],
|
|
4887
|
+
});
|
|
4888
|
+
const { findings } = buildIssueCatalog(
|
|
4889
|
+
[], [], [entry], minimalDepSummary(), emptyState(), testOpts2
|
|
4890
|
+
);
|
|
4891
|
+
expect(findings.some(f => f.category === 'change-risk')).toBe(false);
|
|
4892
|
+
});
|
|
4893
|
+
|
|
4894
|
+
it('skips test files', () => {
|
|
4895
|
+
const entry = makeEntry2('src/__tests__/risky.test.ts', {
|
|
4896
|
+
functions: [
|
|
4897
|
+
makeFn({ complexity: 30, cognitiveComplexity: 30, maintainabilityIndex: 5, statementCount: 100 }),
|
|
4898
|
+
],
|
|
4899
|
+
emptyCatches: [{ file: 'src/__tests__/risky.test.ts', lineStart: 1, lineEnd: 2 }],
|
|
4900
|
+
});
|
|
4901
|
+
const { findings } = buildIssueCatalog(
|
|
4902
|
+
[], [], [entry], minimalDepSummary(), emptyState(), testOpts2
|
|
4903
|
+
);
|
|
4904
|
+
expect(findings.some(f => f.category === 'change-risk')).toBe(false);
|
|
4905
|
+
});
|
|
4906
|
+
|
|
4907
|
+
it('severity critical when score >= 8', () => {
|
|
4908
|
+
const entry = makeEntry2('src/terrible.ts', {
|
|
4909
|
+
functions: [
|
|
4910
|
+
makeFn({
|
|
4911
|
+
name: 'fn1',
|
|
4912
|
+
file: 'src/terrible.ts',
|
|
4913
|
+
complexity: 40,
|
|
4914
|
+
cognitiveComplexity: 50,
|
|
4915
|
+
maintainabilityIndex: 5,
|
|
4916
|
+
statementCount: 80,
|
|
4917
|
+
}),
|
|
4918
|
+
makeFn({
|
|
4919
|
+
name: 'fn2',
|
|
4920
|
+
file: 'src/terrible.ts',
|
|
4921
|
+
complexity: 35,
|
|
4922
|
+
cognitiveComplexity: 40,
|
|
4923
|
+
maintainabilityIndex: 8,
|
|
4924
|
+
statementCount: 60,
|
|
4925
|
+
}),
|
|
4926
|
+
makeFn({
|
|
4927
|
+
name: 'fn3',
|
|
4928
|
+
file: 'src/terrible.ts',
|
|
4929
|
+
complexity: 30,
|
|
4930
|
+
cognitiveComplexity: 25,
|
|
4931
|
+
maintainabilityIndex: 12,
|
|
4932
|
+
statementCount: 40,
|
|
4933
|
+
}),
|
|
4934
|
+
],
|
|
4935
|
+
emptyCatches: [{ file: 'src/terrible.ts', lineStart: 1, lineEnd: 2 }],
|
|
4936
|
+
promiseAllUnhandled: [
|
|
4937
|
+
{ file: 'src/terrible.ts', lineStart: 5, lineEnd: 5, kind: 'Promise.all' as const },
|
|
4938
|
+
],
|
|
4939
|
+
dependencyProfile: {
|
|
4940
|
+
internalDependencies: [],
|
|
4941
|
+
externalDependencies: [],
|
|
4942
|
+
unresolvedDependencies: [],
|
|
4943
|
+
declaredExports: Array.from({ length: 20 }, (_, i) => ({
|
|
4944
|
+
name: `e${i}`,
|
|
4945
|
+
kind: 'function' as const,
|
|
4946
|
+
isType: false,
|
|
4947
|
+
isDefault: false,
|
|
4948
|
+
})),
|
|
4949
|
+
importedSymbols: [],
|
|
4950
|
+
reExports: [],
|
|
4951
|
+
},
|
|
4952
|
+
});
|
|
4953
|
+
const { findings } = buildIssueCatalog(
|
|
4954
|
+
[], [], [entry], minimalDepSummary(), emptyState(), testOpts2
|
|
4955
|
+
);
|
|
4956
|
+
const cr = findings.filter(f => f.category === 'change-risk');
|
|
4957
|
+
expect(cr.length).toBe(1);
|
|
4958
|
+
expect(cr[0].severity).toBe('critical');
|
|
4959
|
+
});
|
|
4960
|
+
});
|
|
4961
|
+
|
|
4962
|
+
describe('new categories registered in PILLAR_CATEGORIES', () => {
|
|
4963
|
+
it('all new categories exist in code-quality pillar', () => {
|
|
4964
|
+
const newCategories = [
|
|
4965
|
+
'deep-nesting',
|
|
4966
|
+
'multiple-return-paths',
|
|
4967
|
+
'catch-rethrow',
|
|
4968
|
+
'magic-string',
|
|
4969
|
+
'boolean-parameter-cluster',
|
|
4970
|
+
'promise-all-unhandled',
|
|
4971
|
+
'export-surface-density',
|
|
4972
|
+
'change-risk',
|
|
4973
|
+
];
|
|
4974
|
+
for (const cat of newCategories) {
|
|
4975
|
+
expect(PILLAR_CATEGORIES['code-quality']).toContain(cat);
|
|
4976
|
+
}
|
|
4977
|
+
});
|
|
4978
|
+
});
|
|
4979
|
+
});
|