ColumbusLabs/DebtLens

GitHub: ColumbusLabs/DebtLens

DebtLens 是一款多语言代码可维护性扫描器,通过检测重复逻辑、模块臃肿和技术债等问题,帮助团队在 AI 辅助编码时代守住代码质量。

Stars: 2 | Forks: 6

# DebtLens [![CI](https://static.pigsec.cn/wp-content/uploads/repos/2026/06/27190c5a1d135942.svg)](https://github.com/ColumbusLabs/debtlens/actions/workflows/ci.yml) [![npm version](https://img.shields.io/npm/v/debtlens.svg)](https://www.npmjs.com/package/debtlens) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](./LICENSE) [![Node](https://img.shields.io/badge/node-%3E%3D20-brightgreen.svg)](https://nodejs.org) **DebtLens is a maintainability scanner for TypeScript, JavaScript, Python, Vue/Svelte SFC scripts, Kotlin, Swift, Ruby, and Jetpack Compose codebases.** The first supported rule packs target React (including React Native, Expo, and Next.js apps) plus core Python, Vue/Svelte SFC scripts, Kotlin, Swift, Ruby, Rails, and Jetpack Compose modules, but the core idea applies broadly: catch duplicated logic, bloated modules, weak boundaries, TODO debt, and naming drift before it becomes permanent. It is not an "AI code detector." It does not try to prove who wrote a line of code. Instead, it finds the patterns that tend to slip into codebases when teams move quickly with coding assistants — duplicated logic, bloated components, state sprawl, overloaded effects, thin abstractions, prop drilling, TODO debt, and naming drift. See [`docs/rule-packs.md`](./docs/rule-packs.md) for how **core rules**, **framework packs**, and **language-agnostic reporting** fit together. Start with the [`five-minute quickstart`](./docs/quickstart.md), then use the [`pack chooser`](./docs/pack-chooser.md), [`example scenarios`](./docs/examples.md), [`report gallery`](./docs/report-gallery.md), and [`false-positive calibration guide`](./docs/false-positives.md) when you move from a local scan to CI. If you are adopting DebtLens broadly, read [`docs/when-not-to-use.md`](./docs/when-not-to-use.md) first so it gates the right work. For Python, SFC, Kotlin, and multi-language pack work, see the parser notes in [`docs/language-pack-rfc.md`](./docs/language-pack-rfc.md). ## Migration The **[Unreleased]** section of [`CHANGELOG.md`](./CHANGELOG.md) tracks breaking and behavioral changes landing on `main` before the next semver tag. Notable recent items: MCP scan/doctor run in-process (no subprocess spawn), expanded MCP workflow tools for adoption, compare, suppression, and baseline diff/prune, package config arrays union-merge with the repo root, atomic scan-cache writes, and stricter ESLint migration validation in `debtlens init --from-eslint`. Review that section when upgrading from an earlier dev build or pinned Action tag. npx --yes --package=debtlens@latest debtlens scan npx --yes --package=debtlens@latest debtlens scan src --format markdown npx --yes --package=debtlens@latest debtlens scan --min-severity medium --fail-on high npx --yes --package=debtlens@latest debtlens scan --gate advisory npx --yes --package=debtlens@latest debtlens scan --gate new-code npx --yes --package=debtlens@latest debtlens scan --rules duplicates,state,effects ## Example output $ debtlens scan examples/react --min-severity medium DebtLens Report Scanned 3 files with 8 rules in 38ms. Issues: 4 | high 2 | medium 2 | low 0 | info 0 HIGH (2) Prop drilling [prop-drilling] src/Dashboard.tsx:13 Dashboard forwards 7 props across 3 child components. confidence 73% - ReleaseHero: movie, userId, region, theme, onSelect, onSave - ReleaseGrid: movie, userId, region, theme, onSelect, onSave, onShare suggestion: Consider colocating the data owner closer to consumers, using a composition slot, or extracting a focused context for stable cross-cutting values. Duplicate logic [duplicate-logic] src/duplicateOne.ts:1 normalizeMovieRelease is 100% structurally similar to normalizeGameRelease. confidence 100% - src/duplicateOne.ts:1-18 (18 lines) - src/duplicateTwo.ts:1-18 (18 lines) See [`docs/showcase-expensify-app.md`](./docs/showcase-expensify-app.md) for a curated run against a large production React Native codebase and [`docs/showcase-next-app.md`](./docs/showcase-next-app.md) for a reproducible Next.js App Router showcase template. ## Why this matters AI coding assistants make it easier to generate working code quickly. That creates a new maintainer problem: code review must catch duplicated implementations, architectural drift, unnecessary abstractions, and components that quietly absorb too many responsibilities. That review burden is especially hard for new coders who have not yet built the instinct for what maintainability debt looks like. A beginner can ship something that works and still miss warning signs: repeated logic, overloaded effects, local state scattered everywhere, thin wrappers, or names that drift across a feature. DebtLens gives maintainers and newer contributors a neutral, explainable report before debt becomes permanent. It is meant to teach what to look for, not just fail a build. ## Current rule set Built-in rules are grouped into **core** TS/JS, **react**, framework, maintainer, and language packs. Full taxonomy: [`docs/rule-packs.md`](./docs/rule-packs.md). | Rule | Pack | What it catches | Default severity | | --- | --- | --- | --- | | `duplicate-logic` | core | Near-duplicate functions/components using normalized AST/text similarity | Medium | | `test-duplication` | core | Structurally identical test cases across test files | Medium | | `large-function` | core | Non-component functions over line or branch budgets | Medium | | `import-cycle` | core | Circular relative import graphs | Medium | | `complex-control-flow` | core | Branch-heavy or deeply nested functions | Medium | | `config-drift` | core | Conflicting repeated values across JSON config files | Medium | | `dead-abstraction` | core | Thin wrappers that add little behavior | Low | | `duplicated-literal` | core | Repeated string/number literals across files | Low | | `todo-comment` | core | TODO/FIXME/HACK/temporary implementation comments | Low | | `naming-drift` | core | Files with multiple competing names for the same domain concept | Info | | `barrel-file` | core | Re-export-only barrels that obscure import graphs | Low | | `weak-test-boundary` | core | Production imports from test-only modules | Medium | | `api-surface-sprawl` | core | Files exporting too many public symbols | Medium | | `empty-catch` | core | Empty or comment-only catch blocks that silently ignore errors | Medium | | `swallowed-error` | core | Catch blocks that only log without rethrowing or returning | Medium | | `floating-promise` | core | Unawaited promise-returning calls and effect fire-and-forget | Medium | | `commented-out-code` | core | Contiguous comment lines that look like dead code | Low | | `large-component` | react | React-style components with too many lines, hooks, or branch points | Medium | | `state-sprawl` | react | Components/hooks with many local stateful hooks | Medium | | `effect-complexity` | react | Long or overloaded React effect hooks | Medium | | `hook-dependency-smell` | react | Inline object/array/function literals in hook dependency arrays | Low | | `context-provider-sprawl` | react | Components wrapping many unrelated Context providers | Medium | | `prop-drilling` | react | Components that forward many props to children | Medium | | `story-only-component` | react | Exported components whose known consumers are only Storybook stories | Low | | `rn-host-forwarding` | react-native | RN wrappers forwarding many props into host primitives | Medium | | `server-client-boundary` | next | App Router server/client boundary mistakes | High | | `route-handler-size` | next | Oversized Next route/page modules | Medium | | `data-loader-sprawl` | next | Server loaders/components with many fetches or awaits | Medium | | `handler-depth` | node | Deeply nested Express/Fastify handlers | Medium | | `route-sprawl` | node | Route modules registering too many endpoints | Medium | | `python-duplicate-logic` | python | Near-duplicate Python functions | Medium | | `python-large-function` | python | Oversized or branch-heavy Python functions | Medium | | `python-complex-control-flow` | python | Branch-heavy or deeply nested Python functions | Medium | | `python-dead-abstraction` | python | Thin Python pass-through functions | Low | | `python-todo-comment` | python | TODO/FIXME/HACK comments in Python files | Low | | `python-error-handling` | python | Empty/bare except blocks and log-only Python error handlers | Medium | | `python-route-sprawl` | python-web | Flask/Blueprint or Django URL modules registering too many routes | Medium | | `vue-todo-comment` | vue | TODO/FIXME/HACK comments inside Vue script blocks | Low | | `vue-large-script` | vue | Oversized Vue SFC scripts or script functions | Medium | | `vue-duplicate-logic` | vue | Near-duplicate Vue script functions | Medium | | `svelte-todo-comment` | svelte | TODO/FIXME/HACK comments inside Svelte script blocks | Low | | `svelte-large-script` | svelte | Oversized Svelte component scripts or script functions | Medium | | `svelte-duplicate-logic` | svelte | Near-duplicate Svelte script functions | Medium | | `kotlin-duplicate-logic` | kotlin | Near-duplicate Kotlin functions | Medium | | `kotlin-large-function` | kotlin | Oversized or branch-heavy Kotlin functions | Medium | | `kotlin-dead-abstraction` | kotlin | Thin Kotlin pass-through functions | Low | | `kotlin-todo-comment` | kotlin | TODO/FIXME/HACK comments in Kotlin files | Low | | `kotlin-empty-catch` | kotlin | Empty or comment-only Kotlin catch blocks | Medium | | `compose-large-composable` | compose | Oversized or branch-heavy Jetpack Compose functions | Medium | | `compose-state-hoisting` | compose | Composables owning too many local state holders | Medium | ## Performance benchmarks Synthetic fixtures under `tests/benchmarks/fixtures/` exercise small (5 files), medium (30), and large (100) scan sizes. npm run build npm run benchmark # all fixtures + local budget check npm run benchmark:ci # small fixture only (used in CI) npm run benchmark -- --budget small=7500 Default budgets are generous local regression caps. Override them in CI with `DEBTLENS_BENCHMARK_BUDGETS=small=5000` or repeated `--budget fixture=ms` arguments instead of editing the script for each runner class. `benchmark:ci` wraps `--small-only`, which keeps pull-request checks fast on huge repos while still failing when the small fixture exceeds the configured cap. | Fixture | Files | Budget | | --- | ---: | ---: | | small | 5 | 5s | | medium | 30 | 30s | | large | 100 | 120s | Examples: DEBTLENS_BENCHMARK_BUDGETS=small=5000 npm run benchmark:ci DEBTLENS_BENCHMARK_BUDGET_SMALL_MS=7500 npm run benchmark:ci npm run benchmark -- --budget small=7500 --budget medium=45000 Per-rule timing is available with `--profile`; CI can keep the raw timings in the canonical JSON report artifact. For large repeat scans, `--cache` writes a content-hash cache at `.debtlens/cache.json` by default and returns cached results when the selected files and scan options are unchanged. Use `--batch-size ` to yield between source-loading batches, and `--parallel` to dispatch independent detectors concurrently while preserving deterministic output ordering. ## Install npm install --save-dev debtlens or run without installing: npx --yes --package=debtlens@latest debtlens scan ## Usage debtlens init # write a starter debtlens.config.json (use --force to overwrite) debtlens init --pack core # starter config using the core rule pack preset debtlens init --policy @org/debtlens-policy # starter config from a shared policy package debtlens init --from-eslint eslint.config.json # print a migration suggestion without writing debtlens adopt # adoption report (dry run; recommends minSeverity) debtlens watch examples/react --rules todo-comment # rescan on file changes debtlens completions zsh # print shell completions debtlens mcp # stdio MCP server for Cursor/Claude-style agents debtlens packs # list built-in rule pack presets debtlens doctor # inspect resolved config and matched files without scanning debtlens rules # list built-in rule ids and descriptions debtlens explain # print rule docs, default thresholds, and false-positive guidance debtlens suppress --rule --reason "" # print a copy-paste inline suppression comment debtlens baseline diff . --baseline debtlens-baseline.json # preview baseline drift without writing debtlens baseline prune . --baseline debtlens-baseline.json # remove resolved entries from a baseline debtlens baseline update . --baseline debtlens-baseline.json # rewrite a baseline from the current scan debtlens compare previous.json current.json --format terminal # compare two ScanResult JSON reports debtlens scan [target] Options: -i, --include comma-separated glob patterns to include -x, --exclude comma-separated glob patterns to exclude --min-severity info, low, medium, or high --pack built-in rule pack preset --rules comma-separated rule ids --threshold comma-separated key=value threshold overrides --max-files maximum files to scan --format terminal, json, markdown, pr-comment, sarif, html, junit, or gitlab-codequality -o, --output write the report to a file --fail-on exit 1 when an issue meets this severity --fail-on-confidence <0-1> with --fail-on, require at least this confidence to fail --gate advisory, new-code, strict-new-code, or legacy-baseline --fail-on-regression exit 1 when issue counts increase vs --baseline or --diff-base --baseline report only issues absent from this baseline file --diff-base report only findings introduced since this git ref --write-baseline [path] write current issues to a baseline file and exit --changed [ref] scan only files changed vs HEAD (or vs if given) --staged scan only files staged in git --respect-gitignore skip files ignored by git --config path to debtlens.config.json --cwd working directory --package scan a single npm/pnpm/Nx workspace package --no-color disable terminal color -q, --quiet terminal only: suppress per-finding detail --profile print per-rule timing to stderr without changing findings --cache [path] reuse unchanged scan results from a content-hash cache --parallel run detectors concurrently after source loading --batch-size load source files in bounded batches --blame-age add introducedDaysAgo metadata to JSON issues via git blame --hotspots [limit] rank files by current findings plus recent git churn --churn-days with --hotspots, look back this many days --churn-range with --hotspots, use this git revision range --ownership attach CODEOWNERS-based ownership summaries --codeowners with --ownership, use this CODEOWNERS file --audit-suppressions include used and unused inline suppression directives --group-by terminal grouping: severity, rule, or file --sarif-compact SARIF only: emit only rules referenced by findings --sarif-category SARIF only: set runs[].automationDetails.id --junit-fail-on JUnit only: failed testcase severity threshold --markdown-heatmap [limit] Markdown only: append a debt heatmap table --pr-comment-max-findings PR comment only: cap detailed findings --pr-comment-max-bytes PR comment only: cap rendered body bytes --pr-comment-full-report-url PR comment only: link omitted findings to a full report Examples: # Scan the current project debtlens scan # Scan only app source files debtlens scan . --include "app/**/*.ts,app/**/*.tsx,src/**/*.ts,src/**/*.tsx" # Create a Markdown report for a pull request artifact debtlens scan --format markdown --output debtlens-report.md # Create a compact grouped PR comment body debtlens scan --format pr-comment --output debtlens-pr-comment.md # Keep a busy PR comment compact while linking to the full report debtlens scan --format pr-comment --pr-comment-max-findings 20 --pr-comment-max-bytes 60000 --pr-comment-full-report-url "$DEBTLENS_REPORT_URL" --output debtlens-pr-comment.md # Create HTML and JUnit reports for CI artifacts debtlens scan --format html --output reports/debtlens.html debtlens scan --format junit --junit-fail-on high --output reports/debtlens.junit.xml # Create a GitLab Code Quality report debtlens scan --format gitlab-codequality --output gl-code-quality-report.json # Package-scoped adoption report in a workspace debtlens adopt . --package web --format markdown # CI gate: allow low/medium debt but fail high-confidence high-severity debt debtlens scan --min-severity medium --fail-on high --fail-on-confidence 0.8 # Named quality-gate presets for Clean-as-You-Code adoption debtlens scan --gate advisory debtlens scan --gate new-code --diff-base origin/main debtlens scan --gate strict-new-code --diff-base origin/main debtlens scan --gate legacy-baseline --baseline debtlens-baseline.json # Tune component-size threshold debtlens scan --threshold "large-component.maxLines=320,state-sprawl.maxStatefulHooks=8" # Adopt on a legacy repo: record existing debt, then only report newly introduced debt debtlens scan --write-baseline debtlens scan --baseline debtlens-baseline.json --fail-on high debtlens scan --baseline debtlens-baseline.json --fail-on-regression # Maintain that baseline as debt is fixed debtlens baseline diff . --baseline debtlens-baseline.json debtlens baseline prune . --baseline debtlens-baseline.json --dry-run debtlens baseline prune . --baseline debtlens-baseline.json debtlens baseline update . --baseline debtlens-baseline.json # Pull-request scan: only the files this branch changed vs main debtlens scan --changed origin/main --fail-on high # Pre-commit scan: only files currently staged in git debtlens scan --staged --fail-on high # Opt in to .gitignore filtering in addition to DebtLens exclude globs debtlens scan --respect-gitignore # Debug config and file matching without running detectors debtlens doctor --pack core debtlens doctor --include "src/**/*.ts,src/**/*.tsx" --changed debtlens doctor --provenance --threshold "large-component.maxLines=320" # Local iteration: run once, then rescan after file changes until Ctrl+C debtlens watch examples/react --rules todo-comment --debounce 300 # List rule ids for config, CI, or --rules debtlens rules debtlens rules --format json # Quiet terminal output: hide per-finding detail debtlens scan --quiet ## Recommended adoption path Preview findings and get a numbered rollout plan before committing config or baseline files. The plan includes a recommended first pack, baseline/new-code gate commands, package scope when applicable, and changed/staged local workflows: debtlens adopt --cwd . --rules todo-comment # dry-run report (default) debtlens adopt --write-config --write-baseline --force The second command writes `debtlens.config.json` and `debtlens-baseline.json` (baseline write is skipped when zero issues are found). For established repositories, follow the generated plan's baseline or `--diff-base` CI commands so pull requests focus on newly introduced debt; add `--fail-on-regression` when you want count increases to fail as well. For serious open-source repositories and broad monorepos, treat adoption as a scoped rollout instead of a whole-repo gate on day one. Run `adopt` first, then start with `--changed origin/main` or a maintained source subdirectory, add `--package` for workspaces, keep generated/dependency outputs excluded, and narrow expensive or noisy rules with `--rules`, thresholds, baselines, or confidence floors before widening coverage. If the default 2,000-file cap appears, either raise it with `--max-files` for a deliberate full scan or make the target more precise with `--package`, `--include`, `--exclude`, `--changed`, or `--respect-gitignore`. Named quality-gate presets give teams a shared rollout vocabulary: | Preset | Use when | Default behavior | | --- | --- | --- | | `advisory` | First rollout, tuning, or report-only jobs | Reports findings without adding a blocking gate | | `new-code` | Pull requests after reviewers trust the signal | Gates high-severity findings introduced since `origin/main` | | `legacy-baseline` | Mature repos with accepted historical debt | Gates findings outside `debtlens-baseline.json` and count regressions | | `strict-new-code` | Teams ready to block medium+ new debt | Gates medium+ new findings with confidence >= 0.8 and count regressions | Migrate in two lanes. Clean or near-clean repositories usually start with `--gate advisory`, move to `--gate new-code --diff-base origin/main`, then tighten to `--gate strict-new-code` after false positives and ownership are settled. Legacy repositories usually create and review `debtlens-baseline.json`, run `--gate legacy-baseline`, prune the baseline as debt is fixed, and use `--gate strict-new-code` for pull requests once the team is ready to block medium-severity new debt. Use explicit `--diff-base`, `--baseline`, `--fail-on`, or `--fail-on-confidence` flags when your branch names or severity policy differ from the preset defaults. Baseline fingerprints are stable across line shifts, so moving existing code up or down does not resurface already-recorded debt — only genuinely new issues are reported. The JSON reporter exposes the same line-stable value as both `id` and `fingerprint` in ScanResult schema v1. Use `debtlens baseline diff` to review new, resolved, and changed findings against an existing baseline. `diff` and `--dry-run` are read-only and do not write files. `debtlens baseline prune` removes entries that no longer appear in the current scan, while `debtlens baseline update` rewrites the baseline to the current scan result. Legacy baselines without newer summary metadata are supported. Run maintenance with the same scan scope and options used to create the original baseline, including target, `--cwd`, `--package`, `--include`, `--exclude`, `--pack`, `--rules`, and `--threshold`, so DebtLens compares the same surface instead of treating scope changes as resolved or new debt. For safety, mutating `baseline prune` refuses explicitly scoped scans such as `--rules`, `--package`, custom include/exclude globs, non-default targets, max-file caps, or config-driven scan shaping. Use `baseline diff` to inspect scoped drift, or `baseline update` when you intentionally want to rewrite a scoped baseline. When a scan reads zero files, DebtLens prints a stderr warning with likely causes such as include/exclude globs, the target path, `--cwd`, or an empty git file set from `--changed` / `--staged`. The warning is advisory and does not change the exit code for `--fail-on`. When `duplicate-logic` reaches `duplicate-logic.maxSnippets`, DebtLens warns that duplicate comparisons were capped. JSON output includes the same advisory under `summary.warnings`. When matched files exceed `maxFiles`, DebtLens scans the first selected files, prints a stderr warning, and includes the same advisory in `summary.warnings`. `summary.filesScanned` remains the actual number of files scanned so dashboards and gates do not confuse the cap with the full matched set. ## JSON contract `debtlens scan --format json` emits `schemaVersion: 1`. The stable JSON Schema URL is `https://raw.githubusercontent.com/ColumbusLabs/DebtLens/main/schema/debtlens.scan-result.schema.json`. Every reported issue includes a line-stable `fingerprint`. Inline suppressions with reasons are exported at the root `suppressions` array so compliance and CI consumers can audit what was hidden. Pass `--audit-suppressions` to also export `suppressionDirectives`, a directive-level audit of used, unused, and not-evaluated inline suppressions with file, line, rule, reason, hidden-finding count, and recommended action. When a baseline or `--diff-base` is used, `summary.deltaFromBaseline` reports new, resolved, changed, total, and per-rule count deltas. JSON and Markdown reports also surface `summary.correlations` for files where multiple rules cluster together. Use `debtlens compare previous.json current.json --format terminal|markdown|json` to compare two ScanResult JSON files without rescanning. The compare report includes total, severity, and rule deltas; when both inputs contain issue arrays, it also reports exact new, resolved, changed, severity-regression, and top-new-file counts. Run compare with the same scan scope and options for meaningful trends. Pass `--blame-age` to enrich JSON issues with optional `introducedDaysAgo` metadata from `git blame`. This is best-effort: outside git repositories, uncommitted lines, and staged blob scans omit the field without changing detector output or exit codes. Pass `--hotspots` to add optional `summary.hotspots` data that ranks files by current findings plus git-derived churn. By default it looks back 90 days; use `--churn-days` or `--churn-range` when you need a specific history window. The lookup is opt-in so default scans do not pay for git history traversal. Churn is based on current file paths and does not follow pre-rename history. Pass `--ownership` to add optional `summary.ownership` data from CODEOWNERS. DebtLens uses the first CODEOWNERS file found in `.github/`, the repository root, or `docs/`; use `--codeowners ` for an explicit file. Reports group high-debt handoffs by owner and call out unowned high-debt files. Missing CODEOWNERS stays silent unless ownership mode is requested. ## Inline suppressions Suppress intentional findings in source with an explicit, auditable reason. Suppressions apply during the scan; baseline and `--diff-base` filtering run afterward on the remaining issues. **Next-line** — hides a finding on the line immediately below the comment: // debtlens-disable-next-line todo-comment -- tracked in PROJ-123 // TODO: remove after migration ships **File-level** — hides all findings for that rule in the file: // debtlens-disable-file naming-drift -- domain vocabulary is intentional here Rules: - A non-empty reason is required after `--`. Suppressions without a reason are ignored and emit a warning. - Unknown rule ids emit a warning and do not suppress. - Only the matching rule (and line, for next-line) is suppressed; other rules on the same line still report. Terminal output includes inline suppression counts in the filter stats line (for example, `1 inline suppressed`). JSON reports expose the same count under `summary.filterStats.suppressedByInline`. Use `--audit-suppressions` to find stale or broad directives: debtlens scan . --audit-suppressions --format markdown debtlens scan . --audit-suppressions --format json Audit output separates file-wide and next-line suppressions, marks directives as `used`, `unused`, or `not-evaluated`, and recommends whether to remove stale suppressions, narrow broad file-wide exceptions, or rerun the audit with the relevant rule enabled. `debtlens suppress` prints a ready-to-paste directive so you don't have to remember the syntax: debtlens suppress --rule todo-comment --reason "tracked in PROJ-123" # // debtlens-disable-next-line todo-comment -- tracked in PROJ-123 debtlens suppress --rule naming-drift --reason "domain vocabulary is intentional" --file # // debtlens-disable-file naming-drift -- domain vocabulary is intentional Prefer baselines for legacy debt, config tuning for false positives, and inline suppressions for rare, documented exceptions. See [`docs/rules.md`](./docs/rules.md#suppressing-findings) for guidance. ## Configuration Create `debtlens.config.json`: { "$schema": "https://raw.githubusercontent.com/ColumbusLabs/DebtLens/main/schema/debtlens.config.schema.json", "include": ["src/**/*.{ts,tsx,js,jsx}"], "exclude": ["node_modules/**", "dist/**", "build/**", "out/**", ".next/**", ".output/**", ".venv/**", "venv/**", "**/__generated__/**"], "minSeverity": "low", "respectGitignore": false, "rules": [ "large-component", "state-sprawl", "effect-complexity", "duplicate-logic", "dead-abstraction", "prop-drilling", "todo-comment", "naming-drift" ], "thresholds": { "large-component.maxLines": 250, "state-sprawl.maxStatefulHooks": 6, "effect-complexity.maxLines": 30, "duplicate-logic.minSimilarity": 0.86 }, "propDrilling": { "ignoreComponents": ["DesignSystemCard", "DesignSystemModal"] } } The stable JSON Schema URL is `https://raw.githubusercontent.com/ColumbusLabs/DebtLens/main/schema/debtlens.config.schema.json`. `debtlens init` writes this URL into new config files so editors can provide validation and autocomplete. ### Rule packs Built-in presets select a rule set without hand-picking every rule id. Language packs also declare their discovery metadata, so selecting `python`, `vue`, `svelte`, `kotlin`, `swift`, `ruby`, `rails`, `compose`, or `swiftui` adds the registered source globs without one-off CLI flags. See [`docs/rule-packs.md`](./docs/rule-packs.md). | Pack | Rules | | --- | --- | | `core` | framework-neutral maintainability rules: duplication, large functions, barrels, test boundaries, API surface, TODOs, naming drift, silent failures, floating promises, and dead commented code | | `react` | core + component, hook, provider, prop, and Storybook signals | | `react-native` | react + RN host primitive forwarding | | `next` | react + App Router boundary, route size, and data-loader checks | | `node` | core + Express/Fastify handler depth and route sprawl | | `python` | Python duplicate functions, large and branch-heavy functions, thin wrappers, TODO comments, and error-handling smells | | `python-web` | python + Flask/Blueprint and Django URL route sprawl | | `vue` | Vue SFC script TODO, large-script, and duplicate-logic signals | | `svelte` | Svelte SFC script TODO, large-script, and duplicate-logic signals | | `kotlin` | Kotlin duplicate functions, large functions, thin wrappers, TODO comments, and empty catch blocks | | `swift` | Swift duplicate functions, large functions, thin wrappers, and TODO comments | | `ruby` | Ruby duplicate methods, large methods, thin wrappers, and TODO comments | | `rails` | Ruby core rules plus Rails route and controller sprawl checks | | `compose` | Jetpack Compose oversized composables and local state-hoisting smells | | `swiftui` | SwiftUI oversized views and local state-sprawl checks | | `expo` | React Native tuning for Expo Router projects | | `ai-assisted-maintainer` | high-signal maintainability checks for assistant-heavy codebases, including silent failures and commented-out code but excluding floating-promise; no authorship claims | | `oss-maintainer` | library API surface, barrels, duplication, tests, and TODO debt | | `ai-workflow-drift` | duplicated or contradictory AI assistant instruction files; no AI-authorship claims | { "$schema": "https://raw.githubusercontent.com/ColumbusLabs/DebtLens/main/schema/debtlens.config.schema.json", "pack": "core", "include": ["src/**/*.{ts,tsx,js,jsx}"] } Explicit `rules` in config override the pack. Use `debtlens packs` to list presets. Use comma-separated packs for mixed-language scans: debtlens scan . --pack core,python,vue,svelte,kotlin,swift,ruby,rails,compose,swiftui ### Per-rule severities and confidence floors Tune noisy rules without disabling them. `ruleSeverities` replaces the severity a rule reports (changing summary counts and `--fail-on` behavior), and `ruleConfidenceFloors` hides findings from a rule below a minimum confidence: { "ruleSeverities": { "naming-drift": "info" }, "ruleConfidenceFloors": { "prop-drilling": 0.8 } } Unknown rule ids in either map emit a warning with a did-you-mean suggestion. Issues hidden by a confidence floor are counted under `summary.filterStats.filteredByConfidenceFloor`. Both maps accept plugin rule ids when plugins are configured. ### Custom naming vocabulary `naming-drift` ships with a built-in media/release vocabulary. Add your own domain concepts with `vocabulary` (concept id → competing terms). Your groups are merged with the built-ins, and a group with the same id overrides the built-in one. { "vocabulary": { "commerce-entity": ["product", "sku", "item", "listing"], "auth-user": ["user", "account", "member", "profile"] } } ### Plugins Ship custom rules as local ESM modules without forking the CLI. List them in config with the plugin API version, then select them like built-in rules: { "$schema": "https://raw.githubusercontent.com/ColumbusLabs/DebtLens/main/schema/debtlens.config.schema.json", "pluginApiVersion": 1, "plugins": ["./debtlens-rules/no-console.mjs"], "include": ["src/**/*.{ts,tsx,js,jsx}"] } Plugin authors import types from the published `debtlens/plugin` entry point: import type { Detector, DetectorContext } from "debtlens/plugin"; Besides `rules`, a plugin's default export may include `thresholds` (defaults read by `context.getThreshold`, merged after built-ins so user config and `--threshold` still override them) and `vocabulary` (naming-drift concept groups, overridden by user config groups with the same id): export default { rules: [noConsoleDetector], thresholds: { "no-console.maxCalls": 0 }, vocabulary: { logging: ["log", "logger", "console", "debug", "trace"] }, }; See the reference plugin in [`examples/plugin/`](./examples/plugin/) and the full contract in [`docs/plugin-api-rfc.md`](./docs/plugin-api-rfc.md). Plugin paths must stay within the config file's directory tree, rule ids must not collide with built-ins, and CI pipelines scanning untrusted repos can set `DEBTLENS_DISABLE_PLUGINS=1` to skip plugin loading entirely (see [`SECURITY.md`](./SECURITY.md)). ### Policy packages Shared organization policies can be scaffolded from an npm package, an installed package module path, or a local policy module: debtlens init --policy @org/debtlens-policy debtlens init --policy ./node_modules/@org/debtlens-policy/rules/index.mjs debtlens init --policy ./node_modules/@org/debtlens-policy/presets/strict.mjs debtlens init --policy ./debtlens-policy/index.mjs debtlens init --policy ./tools/debtlens-policies/strict.mjs The generated config pins `pluginApiVersion` and registers the policy module under `plugins`. Policy modules are trusted code loaded as plugins, so only enable packages or local files maintained by your organization. CI pipelines that scan untrusted pull requests can set `DEBTLENS_DISABLE_PLUGINS=1` to skip policy plugin loading while still running built-in rules and local non-plugin config. See [`docs/policy-packages.md`](./docs/policy-packages.md) for the package layout and rollout guidance. ## Output formats Terminal output is designed for local development. JSON is designed for integrations. Markdown is designed for release notes and maintainer handoffs. `pr-comment` is compact Markdown with prioritized fix targets, collapsible per-file sections, and optional caps for GitHub pull request comments. SARIF (2.1.0) is designed for GitHub code scanning and other security/quality dashboards; findings include stable SARIF `partialFingerprints`, and `--sarif-category` can set `runs[].automationDetails.id` for package or pack-separated uploads. HTML is a self-contained human report. JUnit XML is for CI systems that expect test-style failures; `--junit-fail-on` can keep lower-severity findings visible as skipped testcases while only the selected severity threshold fails the suite. When omitted, every reported finding fails to preserve existing behavior. `gitlab-codequality` emits GitLab's Code Quality JSON array with stable fingerprints, repo-relative paths, lines, descriptions, rule names, and mapped severities. debtlens scan --format json debtlens scan --audit-suppressions --format markdown debtlens scan --format markdown --markdown-heatmap 10 --output reports/debtlens.md debtlens scan --format pr-comment --output debtlens-pr-comment.md debtlens scan --format pr-comment --pr-comment-max-findings 20 --pr-comment-max-bytes 60000 --output debtlens-pr-comment.md debtlens scan --format sarif --sarif-compact --sarif-category packages/web --output debtlens.sarif debtlens scan --format html --output reports/debtlens.html debtlens scan --format junit --junit-fail-on high --output reports/debtlens.junit.xml debtlens scan --format gitlab-codequality --output gl-code-quality-report.json debtlens scan --group-by rule debtlens compare previous.json current.json --format terminal debtlens compare previous.json current.json --format markdown debtlens compare previous.json current.json --format json ## GitHub Action Run DebtLens on pull requests and surface findings as code-scanning annotations. Pin version tags such as `@v0.4.0` when you want repeatable CI; moving tags such as `@v0` track the latest compatible v0 release. Versioned releases attach a self-contained Action runtime asset so tagged Action runs can skip the source build path; source checkouts build as a fallback when the release asset or `dist/cli/index.js` is missing. name: DebtLens on: pull_request permissions: contents: read security-events: write # required to upload SARIF jobs: debtlens: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: fetch-depth: 0 # needed for --changed and optional hotspot churn - uses: ColumbusLabs/debtlens@v0.4.0 with: changed: origin/${{ github.base_ref }} format: sarif output: debtlens.sarif sarif-category: debtlens-pr gate: new-code diff-base: origin/${{ github.base_ref }} upload-json-artifact: true thresholds: large-component.maxLines=300 quiet: true - uses: github/codeql-action/upload-sarif@v3 if: always() with: sarif_file: debtlens.sarif Scan/report inputs: `target`, `min-severity`, `rules`, `pack`, `gate`, `fail-on`, `fail-on-confidence`, `fail-on-regression`, `format`, `output`, `changed`, `diff-base`, `package`, `profile`, `cache`, `cache-path`, `parallel`, `batch-size`, `blame-age`, `hotspots`, `churn-days`, `churn-range`, `ownership`, `codeowners`, `audit-suppressions`, `respect-gitignore`, `baseline`, `config`, `write-baseline`, `thresholds`, `max-files`, `working-directory`, `quiet`, `group-by`, `sarif-compact`, `sarif-category`, `junit-fail-on`, `markdown-heatmap`, `step-summary`, `annotations`, `annotations-max-count`, `comment`, `comment-delta-only`, `comment-max-findings`, `comment-max-bytes`, `comment-full-report-url`, and `comment-fail-on-error`. Action-only orchestration inputs: `previous-report`, `json-output`, `upload-json-artifact`, `json-artifact-name`, and `json-artifact-retention-days`. `write-baseline` and `baseline` are mutually exclusive. The Action passes `gate` to normal scans, but not to `write-baseline` mode, so baseline creation remains a snapshot operation. The Action runs one canonical JSON scan, renders all requested outputs from that ScanResult, can upload the JSON artifact when `upload-json-artifact` is enabled, and then replays the scan exit code so comments/artifacts still appear on gated failures. Hotspot prioritization is disabled by default. Set `hotspots: true` to attach optional, git-derived hotspot data to the normal scan and prioritize findings in high-churn files. `churn-days` and `churn-range` map to `--churn-days ` and `--churn-range ` when you need to bound that git history lookup. Churn requires enough git history; use `actions/checkout` with `fetch-depth: 0` for full history or fetch the relevant range before the scan. Hotspots do not affect default scan speed unless enabled, and the Action does not pass hotspot inputs to `write-baseline` mode. Ownership routing is disabled by default. Set `ownership: true` to attach optional CODEOWNERS-based owner summaries and unowned high-debt callouts to rendered reports and JSON. Use `codeowners` to point at a specific CODEOWNERS file. Missing CODEOWNERS is a no-op unless ownership mode is requested, and the Action does not pass ownership inputs to `write-baseline` mode. For GitHub-specific preset recipes, including advisory, new-code, legacy-baseline, and strict-new-code migrations, see [`docs/ci-github.md`](./docs/ci-github.md). Action outputs include `scan-status`, `gate-status`, `total-issues`, `high-issues`, `medium-issues`, `low-issues`, `info-issues`, `top-rule`, `top-rule-count`, `json-path`, `json-artifact-name`, `report-path`, and `report-format`. Give the Action step an `id` to use them in later steps: - id: debtlens uses: ColumbusLabs/debtlens@v0 with: changed: origin/${{ github.base_ref }} json-output: debtlens-report.json upload-json-artifact: true - run: echo "DebtLens found ${{ steps.debtlens.outputs.total-issues }} issue(s); gate ${{ steps.debtlens.outputs.gate-status }}" Set `step-summary: true` to append a Markdown rollup to the job's GitHub Actions step summary. The summary includes the gate decision, warnings, filter stats, report/artifact paths, optional trend comparison, suppression audit, and top findings: - uses: ColumbusLabs/debtlens@v0 with: changed: origin/${{ github.base_ref }} format: sarif output: debtlens.sarif step-summary: true previous-report: previous-debtlens-report.json quiet: true fail-on: high For pull requests, you can compare the PR scan against the latest successful base-branch artifact. This workflow also runs on `main` so it seeds the artifact PRs compare against; the download step is intentionally soft-fail so first runs and renamed artifacts still produce the current summary: name: DebtLens PR trend on: pull_request: push: branches: [main] permissions: actions: read contents: read jobs: debtlens: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - name: Download latest base report if: github.event_name == 'pull_request' env: GH_TOKEN: ${{ github.token }} run: | mkdir -p previous run_id="$(gh run list --workflow "$GITHUB_WORKFLOW" --branch "${{ github.base_ref }}" --status success --limit 1 --json databaseId --jq '.[0].databaseId // empty')" if [ -n "$run_id" ]; then gh run download "$run_id" --name debtlens-scan-result --dir previous || true fi - uses: ColumbusLabs/debtlens@v0 with: changed: ${{ github.event_name == 'pull_request' && format('origin/{0}', github.base_ref) || '' }} previous-report: ${{ github.event_name == 'pull_request' && 'previous/debtlens-report.json' || '' }} json-output: current/debtlens-report.json step-summary: true upload-json-artifact: true quiet: true Set `annotations: true` to emit capped GitHub workflow command annotations without SARIF/code scanning. SARIF is best for code-scanning alert history, workflow annotations are useful for lightweight inline check feedback, and PR comments are best for grouped review context. - uses: ColumbusLabs/debtlens@v0 with: changed: origin/${{ github.base_ref }} annotations: true annotations-max-count: 50 fail-on: high The JSON artifact is named `debtlens-scan-result` by default. To also write it into the workspace for a later workflow step: - uses: ColumbusLabs/debtlens@v0 with: changed: origin/${{ github.base_ref }} json-output: debtlens-report.json json-artifact-name: debt-metrics A scheduled debt trend job can restore the last canonical JSON report, produce a fresh one, upload the new artifact, and include the trend in the step summary. If the previous artifact does not exist yet, DebtLens adds a soft warning to the summary and continues: name: DebtLens trend on: schedule: - cron: "0 13 * * 1" workflow_dispatch: jobs: trend: runs-on: ubuntu-latest permissions: actions: read contents: read steps: - uses: actions/checkout@v4 - name: Download previous report env: GH_TOKEN: ${{ github.token }} run: | mkdir -p previous run_id="$(gh run list --workflow "$GITHUB_WORKFLOW" --status success --limit 1 --json databaseId --jq '.[0].databaseId // empty')" if [ -n "$run_id" ]; then gh run download "$run_id" --name debtlens-scan-result --dir previous || true fi - id: debtlens uses: ColumbusLabs/debtlens@v0 with: json-output: current/debtlens-report.json previous-report: previous/debtlens-report.json step-summary: true upload-json-artifact: true quiet: true - name: Use metric outputs run: echo "DebtLens total=${{ steps.debtlens.outputs.total-issues }} top=${{ steps.debtlens.outputs.top-rule }}" A Shields endpoint badge can be generated from the artifact by publishing a tiny JSON file derived from `summary.totalIssues`: { "schemaVersion": 1, "label": "DebtLens", "message": "12 issues", "color": "orange" } For example: jq '{schemaVersion: 1, label: "DebtLens", message: (.summary.totalIssues|tostring + " issues"), color: (if .summary.totalIssues == 0 then "brightgreen" elif .summary.bySeverity.high > 0 then "red" else "orange" end)}' debtlens-report.json > debtlens-badge.json For a stricter "0 new high debt" badge after `--baseline` or `--diff-base`, derive the message from the remaining high-severity issues in the filtered report: jq '{ schemaVersion: 1, label: "DebtLens", message: (if [.issues[] | select(.severity == "high")] | length == 0 then "0 new high debt" else (([.issues[] | select(.severity == "high")] | length | tostring) + " new high") end), color: (if [.issues[] | select(.severity == "high")] | length == 0 then "brightgreen" else "red" end) }' debtlens-report.json > debtlens-high-badge.json Publish that JSON file from any static endpoint and use the Shields endpoint badge: ![DebtLens](https://img.shields.io/endpoint?url=https://example.com/debtlens-high-badge.json) Other CI templates: [GitLab](./docs/ci-gitlab.md), [Bitbucket](./docs/ci-bitbucket.md), and [Azure Pipelines](./docs/ci-azure.md). For local hooks, see [pre-commit hooks](./docs/pre-commit.md). For monorepo rollout, see [per-package baselines](./docs/monorepo-baselines.md). For shared organization policy, see [policy packs as npm packages](./docs/policy-packages.md). For hosted GitHub integration tradeoffs, see the [GitHub App RFC](./docs/github-app-rfc.md). For agent integrations, see the [MCP server setup](./docs/mcp.md). Set `comment: true` to upsert a stable pull request comment (requires `pull-requests: write`). Comment posting is warn-only by default so forked or permission-limited pull requests can still produce artifacts and annotations; set `comment-fail-on-error: true` when a missing comment should fail the Action. permissions: contents: read pull-requests: write - uses: ColumbusLabs/debtlens@v0 with: changed: origin/${{ github.base_ref }} diff-base: origin/${{ github.base_ref }} comment: true comment-delta-only: true comment-max-findings: 20 comment-max-bytes: 60000 fail-on: high PR comment source links use the pull request head SHA when the workflow event provides it, falling back to `GITHUB_SHA` for push and other events. Use `comment-full-report-url` when capped comments should point reviewers to the complete Markdown, JSON, or HTML artifact. For very large monorepos, keep the first Action rollout intentionally narrow. Diff against the pull request base, limit to the package or changed files you own, start with the `core` pack or a small rule list, and cap file volume so new contributors get deterministic feedback before you widen coverage: - uses: ColumbusLabs/debtlens@v0 with: changed: origin/${{ github.base_ref }} package: web rules: todo-comment,duplicate-logic max-files: 500 profile: true step-summary: true If that workflow also runs the repository benchmark gate, prefer the small fixture check so the job does not spend time on synthetic medium/large fixtures: - name: Benchmark regression gate run: npm run benchmark:ci env: DEBTLENS_BENCHMARK_BUDGETS: small=5000 To post a grouped PR comment manually instead, write the `pr-comment` output and post it with `actions/github-script`: permissions: contents: read pull-requests: write steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - uses: ColumbusLabs/debtlens@v0 with: changed: origin/${{ github.base_ref }} format: pr-comment output: debtlens-pr-comment.md fail-on: high - uses: actions/github-script@v7 if: always() && github.event_name == 'pull_request' with: script: | const fs = require('node:fs'); const body = fs.readFileSync('debtlens-pr-comment.md', 'utf8'); await github.rest.issues.createComment({ owner: context.repo.owner, repo: context.repo.repo, issue_number: context.issue.number, body, }); ## Contributing Want to help make DebtLens better? Start with the [first-PR guide](./docs/contributing-first-pr.md), the [rule pack taxonomy](./docs/rule-packs.md), and [CONTRIBUTING.md](./CONTRIBUTING.md). Current starter work is tracked in [`docs/good-first-issues.md`](./docs/good-first-issues.md), which separates active newcomer tasks from the historical v0.3 roadmap batch. Propose new work in [Discussions](https://github.com/ColumbusLabs/DebtLens/discussions), via the rule request template, or the [plugin API](./docs/plugin-api-rfc.md). Contribution paths: **core TS/JS rules**, **Python rules**, **Kotlin rules**, **React pack rules**, **framework packs** (Next.js, RN, Node, Compose, Python web), **scanner/CI** (baselines, monorepos, inline suppressions), **plugins**, and **reporters**. New rule authors should follow the rule checklist in [CONTRIBUTING.md](./CONTRIBUTING.md#rule-review-bar). ## Development npm install npm run typecheck npm test # node:test suite (run via tsx) npm run test:all # typecheck + tests npm run build npm run dev node dist/cli/index.js scan examples/react --min-severity info ## Project status DebtLens is in the **v0.4** release line. Recent capabilities include code-smell detectors for silent failures and dead code, `debtlens adopt` and `debtlens doctor`, rule packs, inline suppressions with required reasons, confidence-aware `--fail-on`, monorepo `--package` scanning, GitHub Action step summaries and PR comment upsert, and `--diff-base` branch comparisons. The architecture stays intentionally simple: a language-agnostic scan and reporting layer with pluggable rule packs on top. Current shipped packs cover core TS/JS, React, React Native, Next.js, Expo, Node, Python, Python web, Vue/Svelte SFC scripts, Kotlin, Swift, Ruby/Rails, Jetpack Compose, SwiftUI, maintainer workflows, and AI workflow instruction drift. Additional packs expand from the same scan/reporting contract. See [`ROADMAP.md`](./ROADMAP.md) and [`docs/rule-packs.md`](./docs/rule-packs.md). ## License MIT
标签:CMS安全, JavaScript, MITM代理, TypeScript, 云安全监控, 代码审查, 安全插件, 技术债, 数据可视化, 自动化攻击, 逆向工具, 静态分析