debtlens 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (112) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/LICENSE +21 -0
  3. package/README.md +244 -0
  4. package/dist/cli/index.d.ts +2 -0
  5. package/dist/cli/index.js +153 -0
  6. package/dist/cli/index.js.map +1 -0
  7. package/dist/cli/init.d.ts +10 -0
  8. package/dist/cli/init.js +18 -0
  9. package/dist/cli/init.js.map +1 -0
  10. package/dist/cli/parseList.d.ts +2 -0
  11. package/dist/cli/parseList.js +29 -0
  12. package/dist/cli/parseList.js.map +1 -0
  13. package/dist/config/defaults.d.ts +2 -0
  14. package/dist/config/defaults.js +40 -0
  15. package/dist/config/defaults.js.map +1 -0
  16. package/dist/config/loadConfig.d.ts +2 -0
  17. package/dist/config/loadConfig.js +23 -0
  18. package/dist/config/loadConfig.js.map +1 -0
  19. package/dist/config/mergeConfig.d.ts +2 -0
  20. package/dist/config/mergeConfig.js +26 -0
  21. package/dist/config/mergeConfig.js.map +1 -0
  22. package/dist/config/schema.d.ts +7 -0
  23. package/dist/config/schema.js +63 -0
  24. package/dist/config/schema.js.map +1 -0
  25. package/dist/config/template.d.ts +10 -0
  26. package/dist/config/template.js +32 -0
  27. package/dist/config/template.js.map +1 -0
  28. package/dist/core/baseline.d.ts +23 -0
  29. package/dist/core/baseline.js +118 -0
  30. package/dist/core/baseline.js.map +1 -0
  31. package/dist/core/scan.d.ts +2 -0
  32. package/dist/core/scan.js +129 -0
  33. package/dist/core/scan.js.map +1 -0
  34. package/dist/core/severity.d.ts +7 -0
  35. package/dist/core/severity.js +26 -0
  36. package/dist/core/severity.js.map +1 -0
  37. package/dist/core/types.d.ts +96 -0
  38. package/dist/core/types.js +2 -0
  39. package/dist/core/types.js.map +1 -0
  40. package/dist/detectors/deadAbstraction.d.ts +2 -0
  41. package/dist/detectors/deadAbstraction.js +115 -0
  42. package/dist/detectors/deadAbstraction.js.map +1 -0
  43. package/dist/detectors/duplicateLogic.d.ts +2 -0
  44. package/dist/detectors/duplicateLogic.js +81 -0
  45. package/dist/detectors/duplicateLogic.js.map +1 -0
  46. package/dist/detectors/effectComplexity.d.ts +2 -0
  47. package/dist/detectors/effectComplexity.js +64 -0
  48. package/dist/detectors/effectComplexity.js.map +1 -0
  49. package/dist/detectors/index.d.ts +3 -0
  50. package/dist/detectors/index.js +20 -0
  51. package/dist/detectors/index.js.map +1 -0
  52. package/dist/detectors/largeComponent.d.ts +2 -0
  53. package/dist/detectors/largeComponent.js +46 -0
  54. package/dist/detectors/largeComponent.js.map +1 -0
  55. package/dist/detectors/namingDrift.d.ts +10 -0
  56. package/dist/detectors/namingDrift.js +82 -0
  57. package/dist/detectors/namingDrift.js.map +1 -0
  58. package/dist/detectors/propDrilling.d.ts +2 -0
  59. package/dist/detectors/propDrilling.js +97 -0
  60. package/dist/detectors/propDrilling.js.map +1 -0
  61. package/dist/detectors/stateSprawl.d.ts +2 -0
  62. package/dist/detectors/stateSprawl.js +47 -0
  63. package/dist/detectors/stateSprawl.js.map +1 -0
  64. package/dist/detectors/todoComment.d.ts +2 -0
  65. package/dist/detectors/todoComment.js +45 -0
  66. package/dist/detectors/todoComment.js.map +1 -0
  67. package/dist/reporters/index.d.ts +4 -0
  68. package/dist/reporters/index.js +14 -0
  69. package/dist/reporters/index.js.map +1 -0
  70. package/dist/reporters/jsonReporter.d.ts +2 -0
  71. package/dist/reporters/jsonReporter.js +4 -0
  72. package/dist/reporters/jsonReporter.js.map +1 -0
  73. package/dist/reporters/markdownReporter.d.ts +2 -0
  74. package/dist/reporters/markdownReporter.js +52 -0
  75. package/dist/reporters/markdownReporter.js.map +1 -0
  76. package/dist/reporters/sarifReporter.d.ts +7 -0
  77. package/dist/reporters/sarifReporter.js +77 -0
  78. package/dist/reporters/sarifReporter.js.map +1 -0
  79. package/dist/reporters/terminalReporter.d.ts +4 -0
  80. package/dist/reporters/terminalReporter.js +39 -0
  81. package/dist/reporters/terminalReporter.js.map +1 -0
  82. package/dist/utils/ast.d.ts +23 -0
  83. package/dist/utils/ast.js +132 -0
  84. package/dist/utils/ast.js.map +1 -0
  85. package/dist/utils/color.d.ts +8 -0
  86. package/dist/utils/color.js +27 -0
  87. package/dist/utils/color.js.map +1 -0
  88. package/dist/utils/createIssue.d.ts +13 -0
  89. package/dist/utils/createIssue.js +28 -0
  90. package/dist/utils/createIssue.js.map +1 -0
  91. package/dist/utils/git.d.ts +15 -0
  92. package/dist/utils/git.js +68 -0
  93. package/dist/utils/git.js.map +1 -0
  94. package/dist/utils/hostComponents.d.ts +12 -0
  95. package/dist/utils/hostComponents.js +57 -0
  96. package/dist/utils/hostComponents.js.map +1 -0
  97. package/dist/utils/identifiers.d.ts +3 -0
  98. package/dist/utils/identifiers.js +20 -0
  99. package/dist/utils/identifiers.js.map +1 -0
  100. package/dist/utils/lines.d.ts +8 -0
  101. package/dist/utils/lines.js +19 -0
  102. package/dist/utils/lines.js.map +1 -0
  103. package/dist/utils/similarity.d.ts +8 -0
  104. package/dist/utils/similarity.js +63 -0
  105. package/dist/utils/similarity.js.map +1 -0
  106. package/docs/architecture.md +68 -0
  107. package/docs/example-report.md +141 -0
  108. package/docs/good-first-issues.md +63 -0
  109. package/docs/rules.md +139 -0
  110. package/docs/showcase-expensify-app.md +119 -0
  111. package/package.json +60 -0
  112. package/schema/debtlens.config.schema.json +116 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,29 @@
1
+ # Changelog
2
+
3
+ All notable changes to DebtLens are documented here. This project adheres to
4
+ [Semantic Versioning](https://semver.org/).
5
+
6
+ ## [0.1.0] - 2026-06-01
7
+
8
+ First public release.
9
+
10
+ ### Added
11
+ - `debtlens scan` with 8 maintainability rules: `large-component`, `state-sprawl`,
12
+ `effect-complexity`, `duplicate-logic`, `dead-abstraction`, `prop-drilling`,
13
+ `todo-comment`, `naming-drift`.
14
+ - Output formats: terminal, JSON, Markdown, and **SARIF 2.1.0** (for GitHub code scanning).
15
+ - `debtlens init` to scaffold a `debtlens.config.json`.
16
+ - **Baseline mode** (`--write-baseline` / `--baseline`) with line-shift-stable
17
+ fingerprints, so teams can adopt on legacy code and only see newly introduced debt.
18
+ - **`--changed [ref]`** mode to scan only files changed vs HEAD or a base ref (PR scans).
19
+ - Configurable naming-drift **vocabulary** (concept id → terms), merged with a built-in pack.
20
+ - Structural **AST fingerprint** pre-filter for `duplicate-logic` (precision + speed).
21
+ - JSON config with a generated **JSON Schema** (`schema/debtlens.config.schema.json`).
22
+ - A composite **GitHub Action** (`action.yml`).
23
+ - Test suite (`node:test` via `tsx`) and CI on a Node 20 + 22 matrix.
24
+
25
+ ### Notes
26
+ - DebtLens reports **heuristic signals, not proof of defects**, and makes no claim about
27
+ how code was authored.
28
+ - See [`docs/showcase-expensify-app.md`](docs/showcase-expensify-app.md) for a curated
29
+ run against a large production React Native codebase.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 DebtLens contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,244 @@
1
+ # DebtLens
2
+
3
+ [![CI](https://github.com/ColumbusLabs/debtlens/actions/workflows/ci.yml/badge.svg)](https://github.com/ColumbusLabs/debtlens/actions/workflows/ci.yml)
4
+ [![npm version](https://img.shields.io/npm/v/debtlens.svg)](https://www.npmjs.com/package/debtlens)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](./LICENSE)
6
+ [![Node](https://img.shields.io/badge/node-%3E%3D20-brightgreen.svg)](https://nodejs.org)
7
+
8
+ DebtLens is a static-analysis CLI for finding maintainability debt common in fast-moving AI-assisted TypeScript, React, React Native, Expo, and Next.js codebases.
9
+
10
+ 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.
11
+
12
+ ```bash
13
+ npx debtlens scan
14
+ npx debtlens scan src --format markdown
15
+ npx debtlens scan --min-severity medium --fail-on high
16
+ npx debtlens scan --rules duplicates,state,effects
17
+ ```
18
+
19
+ ## Example output
20
+
21
+ ```text
22
+ $ debtlens scan examples/react --min-severity medium
23
+
24
+ DebtLens Report
25
+ Scanned 3 files with 8 rules in 38ms.
26
+ Issues: 4 | high 2 | medium 2 | low 0 | info 0
27
+
28
+ HIGH (2)
29
+ Prop drilling [prop-drilling]
30
+ src/Dashboard.tsx:13
31
+ Dashboard forwards 7 props across 3 child components.
32
+ confidence 73%
33
+ - ReleaseHero: movie, userId, region, theme, onSelect, onSave
34
+ - ReleaseGrid: movie, userId, region, theme, onSelect, onSave, onShare
35
+ suggestion: Consider colocating the data owner closer to consumers, using a
36
+ composition slot, or extracting a focused context for stable cross-cutting values.
37
+
38
+ Duplicate logic [duplicate-logic]
39
+ src/duplicateOne.ts:1
40
+ normalizeMovieRelease is 100% structurally similar to normalizeGameRelease.
41
+ confidence 100%
42
+ - src/duplicateOne.ts:1-18 (18 lines)
43
+ - src/duplicateTwo.ts:1-18 (18 lines)
44
+ ```
45
+
46
+ See [`docs/showcase-expensify-app.md`](./docs/showcase-expensify-app.md) for a curated run against a large production React Native codebase.
47
+
48
+ ## Why this matters
49
+
50
+ 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.
51
+
52
+ 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.
53
+
54
+ 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.
55
+
56
+ ## Current rule set
57
+
58
+ | Rule | What it catches | Default severity |
59
+ | --- | --- | --- |
60
+ | `large-component` | React-style components with too many lines, hooks, or branch points | Medium |
61
+ | `state-sprawl` | Components/hooks with many local stateful hooks | Medium |
62
+ | `effect-complexity` | Long or overloaded `useEffect` blocks | Medium |
63
+ | `duplicate-logic` | Near-duplicate functions/components using normalized AST/text similarity | Medium |
64
+ | `dead-abstraction` | Thin wrappers that add little behavior | Low |
65
+ | `prop-drilling` | Components that forward many props to children | Medium |
66
+ | `todo-comment` | TODO/FIXME/HACK/temporary implementation comments | Low |
67
+ | `naming-drift` | Files with multiple competing names for the same domain concept | Info |
68
+
69
+ ## Install
70
+
71
+ ```bash
72
+ npm install --save-dev debtlens
73
+ ```
74
+
75
+ or run without installing:
76
+
77
+ ```bash
78
+ npx debtlens scan
79
+ ```
80
+
81
+ ## Usage
82
+
83
+ ```bash
84
+ debtlens init # write a starter debtlens.config.json (use --force to overwrite)
85
+ debtlens scan [target]
86
+ ```
87
+
88
+ Options:
89
+
90
+ ```txt
91
+ -i, --include <patterns> comma-separated glob patterns to include
92
+ -x, --exclude <patterns> comma-separated glob patterns to exclude
93
+ --min-severity <severity> info, low, medium, or high
94
+ --rules <rules> comma-separated rule ids
95
+ --threshold <thresholds> comma-separated key=value threshold overrides
96
+ --max-files <count> maximum files to scan
97
+ --format <format> terminal, json, markdown, or sarif
98
+ -o, --output <path> write the report to a file
99
+ --fail-on <severity> exit 1 when an issue meets this severity
100
+ --baseline <path> report only issues absent from this baseline file
101
+ --write-baseline [path] write current issues to a baseline file and exit
102
+ --changed [ref] scan only files changed vs HEAD (or vs <ref> if given)
103
+ --config <path> path to debtlens.config.json
104
+ --cwd <path> working directory
105
+ --no-color disable terminal color
106
+ ```
107
+
108
+ Examples:
109
+
110
+ ```bash
111
+ # Scan the current project
112
+ debtlens scan
113
+
114
+ # Scan only app source files
115
+ debtlens scan . --include "app/**/*.{ts,tsx},src/**/*.{ts,tsx}"
116
+
117
+ # Create a Markdown report for a pull request artifact
118
+ debtlens scan --format markdown --output debtlens-report.md
119
+
120
+ # CI gate: allow low/medium debt but fail high-confidence high-severity debt
121
+ debtlens scan --min-severity medium --fail-on high
122
+
123
+ # Tune component-size threshold
124
+ debtlens scan --threshold "large-component.maxLines=320,state-sprawl.maxStatefulHooks=8"
125
+
126
+ # Adopt on a legacy repo: record existing debt, then only report newly introduced debt
127
+ debtlens scan --write-baseline
128
+ debtlens scan --baseline debtlens-baseline.json --fail-on high
129
+
130
+ # Pull-request scan: only the files this branch changed vs main
131
+ debtlens scan --changed origin/main --fail-on high
132
+ ```
133
+
134
+ 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.
135
+
136
+ ## Configuration
137
+
138
+ Create `debtlens.config.json`:
139
+
140
+ ```json
141
+ {
142
+ "include": ["src/**/*.{ts,tsx,js,jsx}"],
143
+ "exclude": ["node_modules/**", "dist/**", "build/**", ".next/**"],
144
+ "minSeverity": "low",
145
+ "rules": [
146
+ "large-component",
147
+ "state-sprawl",
148
+ "effect-complexity",
149
+ "duplicate-logic",
150
+ "dead-abstraction",
151
+ "prop-drilling",
152
+ "todo-comment",
153
+ "naming-drift"
154
+ ],
155
+ "thresholds": {
156
+ "large-component.maxLines": 250,
157
+ "state-sprawl.maxStatefulHooks": 6,
158
+ "effect-complexity.maxLines": 30,
159
+ "duplicate-logic.minSimilarity": 0.86
160
+ }
161
+ }
162
+ ```
163
+
164
+ ### Custom naming vocabulary
165
+
166
+ `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.
167
+
168
+ ```json
169
+ {
170
+ "vocabulary": {
171
+ "commerce-entity": ["product", "sku", "item", "listing"],
172
+ "auth-user": ["user", "account", "member", "profile"]
173
+ }
174
+ }
175
+ ```
176
+
177
+ ## Output formats
178
+
179
+ Terminal output is designed for local development. JSON is designed for integrations. Markdown is designed for PR comments, release notes, and maintainer handoffs. SARIF (2.1.0) is designed for GitHub code scanning and other security/quality dashboards.
180
+
181
+ ```bash
182
+ debtlens scan --format json
183
+ debtlens scan --format markdown --output reports/debtlens.md
184
+ debtlens scan --format sarif --output debtlens.sarif
185
+ ```
186
+
187
+ ## GitHub Action
188
+
189
+ Run DebtLens on pull requests and surface findings as code-scanning annotations:
190
+
191
+ ```yaml
192
+ name: DebtLens
193
+ on: pull_request
194
+
195
+ permissions:
196
+ contents: read
197
+ security-events: write # required to upload SARIF
198
+
199
+ jobs:
200
+ debtlens:
201
+ runs-on: ubuntu-latest
202
+ steps:
203
+ - uses: actions/checkout@v4
204
+ with:
205
+ fetch-depth: 0 # needed for --changed to diff against the base branch
206
+ - uses: ColumbusLabs/debtlens@v0
207
+ with:
208
+ changed: origin/${{ github.base_ref }}
209
+ format: sarif
210
+ output: debtlens.sarif
211
+ fail-on: high
212
+ - uses: github/codeql-action/upload-sarif@v3
213
+ if: always()
214
+ with:
215
+ sarif_file: debtlens.sarif
216
+ ```
217
+
218
+ Inputs: `target`, `min-severity`, `rules`, `fail-on`, `format`, `output`, `changed`, `baseline`, `config`, `working-directory`. Each maps to the matching `scan` flag. With `fail-on`, a qualifying issue fails the job (gating the merge); `if: always()` still uploads the SARIF so annotations appear even on a failing run.
219
+
220
+ ## Development
221
+
222
+ ```bash
223
+ npm install
224
+ npm run typecheck
225
+ npm test # node:test suite (run via tsx)
226
+ npm run test:all # typecheck + tests
227
+ npm run build
228
+ npm run dev
229
+ node dist/cli/index.js scan examples/react --min-severity info
230
+ ```
231
+
232
+ ## Project status
233
+
234
+ DebtLens is currently a v0.1 proof-of-concept. The architecture is intentionally simple so maintainers can add rules quickly. The next milestones are documented in [`ROADMAP.md`](./ROADMAP.md), and starter contribution ideas are tracked in [`docs/good-first-issues.md`](./docs/good-first-issues.md).
235
+
236
+ ## Positioning
237
+
238
+ DebtLens should be pitched as open-source maintainer infrastructure:
239
+
240
+ > AI-assisted development increases code throughput, but also increases review burden and architectural drift. DebtLens gives maintainers an explainable, open-source way to find maintainability debt before it ships.
241
+
242
+ ## License
243
+
244
+ MIT
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,153 @@
1
+ #!/usr/bin/env node
2
+ import { mkdirSync, writeFileSync } from "node:fs";
3
+ import { dirname, resolve } from "node:path";
4
+ import { Command } from "commander";
5
+ import { loadConfig } from "../config/loadConfig.js";
6
+ import { mergeConfig } from "../config/mergeConfig.js";
7
+ import { DEFAULT_BASELINE_FILENAME, applyBaseline, createBaseline, loadBaseline, writeBaseline } from "../core/baseline.js";
8
+ import { scan } from "../core/scan.js";
9
+ import { getChangedFiles } from "../utils/git.js";
10
+ import { parseSeverity, severityRank } from "../core/severity.js";
11
+ import { detectorIds } from "../detectors/index.js";
12
+ import { renderReport } from "../reporters/index.js";
13
+ import { runInit } from "./init.js";
14
+ import { parseCommaList, parseThresholds } from "./parseList.js";
15
+ const program = new Command();
16
+ program
17
+ .name("debtlens")
18
+ .description("Find maintainability debt common in fast-moving AI-assisted TypeScript and React codebases.")
19
+ .version("0.1.0");
20
+ program.command("scan")
21
+ .description("Scan a project, directory, or file for maintainability debt.")
22
+ .argument("[target]", "directory or file to scan", ".")
23
+ .option("-i, --include <patterns>", "comma-separated glob patterns to include")
24
+ .option("-x, --exclude <patterns>", "comma-separated glob patterns to exclude")
25
+ .option("--min-severity <severity>", "info, low, medium, or high", "low")
26
+ .option("--rules <rules>", `comma-separated rule ids. Available: ${detectorIds.join(", ")}`)
27
+ .option("--threshold <thresholds>", "comma-separated key=value threshold overrides")
28
+ .option("--max-files <count>", "maximum files to scan", parseInteger)
29
+ .option("--format <format>", "terminal, json, markdown, or sarif", "terminal")
30
+ .option("-o, --output <path>", "write the report to a file instead of stdout")
31
+ .option("--fail-on <severity>", "exit with code 1 when any issue meets this severity")
32
+ .option("--baseline <path>", "report only issues absent from this baseline file")
33
+ .option("--write-baseline [path]", "write current issues to a baseline file and exit")
34
+ .option("--changed [ref]", "scan only files changed vs HEAD (or vs <ref> if given)")
35
+ .option("--config <path>", "path to debtlens.config.json")
36
+ .option("--cwd <path>", "working directory", process.cwd())
37
+ .option("--no-color", "disable ANSI color in terminal output")
38
+ .action(async (target, rawOptions) => {
39
+ try {
40
+ const format = parseFormat(String(rawOptions.format ?? "terminal"));
41
+ const cwd = resolve(String(rawOptions.cwd ?? process.cwd()));
42
+ const fileConfig = loadConfig(cwd, rawOptions.config ? String(rawOptions.config) : undefined);
43
+ const minSeverity = parseSeverity(String(rawOptions.minSeverity ?? "low"), "low");
44
+ const failOn = rawOptions.failOn ? parseSeverity(String(rawOptions.failOn), "high") : undefined;
45
+ let changedFiles;
46
+ if (rawOptions.changed) {
47
+ const base = rawOptions.changed === true ? undefined : String(rawOptions.changed);
48
+ const changed = getChangedFiles(cwd, base);
49
+ if (changed === null) {
50
+ process.stderr.write("DebtLens: --changed ignored (not a git repository).\n");
51
+ }
52
+ else {
53
+ changedFiles = changed.files;
54
+ }
55
+ }
56
+ const options = mergeConfig(target, fileConfig, {
57
+ cwd,
58
+ include: parseCommaList(rawOptions.include),
59
+ exclude: parseCommaList(rawOptions.exclude),
60
+ rules: parseRuleList(rawOptions.rules),
61
+ thresholds: parseThresholds(rawOptions.threshold),
62
+ minSeverity,
63
+ maxFiles: rawOptions.maxFiles,
64
+ changedFiles,
65
+ });
66
+ if (rawOptions.writeBaseline && rawOptions.baseline) {
67
+ throw new Error("Use either --write-baseline or --baseline, not both.");
68
+ }
69
+ const result = await scan(options);
70
+ if (rawOptions.writeBaseline) {
71
+ const baselinePath = rawOptions.writeBaseline === true
72
+ ? DEFAULT_BASELINE_FILENAME
73
+ : String(rawOptions.writeBaseline);
74
+ const written = writeBaseline(cwd, baselinePath, createBaseline(result.issues));
75
+ process.stdout.write(`Wrote baseline with ${result.issues.length} issues to ${written}\n`);
76
+ return;
77
+ }
78
+ const reported = rawOptions.baseline
79
+ ? applyBaseline(result, loadBaseline(cwd, String(rawOptions.baseline)))
80
+ : result;
81
+ const report = renderReport(reported, format, { color: rawOptions.color !== false && format === "terminal" && process.stdout.isTTY });
82
+ if (rawOptions.output) {
83
+ const outputPath = resolve(cwd, String(rawOptions.output));
84
+ mkdirSync(dirname(outputPath), { recursive: true });
85
+ writeFileSync(outputPath, report, "utf8");
86
+ }
87
+ else {
88
+ process.stdout.write(report);
89
+ }
90
+ if (failOn && reported.issues.some((issue) => severityRank[issue.severity] >= severityRank[failOn])) {
91
+ process.exitCode = 1;
92
+ }
93
+ }
94
+ catch (error) {
95
+ const message = error instanceof Error ? error.message : String(error);
96
+ process.stderr.write(`DebtLens failed: ${message}\n`);
97
+ process.exitCode = 1;
98
+ }
99
+ });
100
+ program.command("init")
101
+ .description("Create a starter debtlens.config.json in the current directory.")
102
+ .option("--force", "overwrite an existing config file")
103
+ .option("--cwd <path>", "working directory", process.cwd())
104
+ .action((rawOptions) => {
105
+ try {
106
+ const cwd = resolve(String(rawOptions.cwd ?? process.cwd()));
107
+ const result = runInit(cwd, rawOptions.force === true);
108
+ process.stdout.write(`${result.overwritten ? "Overwrote" : "Created"} ${result.path}\n`);
109
+ }
110
+ catch (error) {
111
+ const message = error instanceof Error ? error.message : String(error);
112
+ process.stderr.write(`DebtLens failed: ${message}\n`);
113
+ process.exitCode = 1;
114
+ }
115
+ });
116
+ if (process.argv.length <= 2) {
117
+ program.help();
118
+ }
119
+ await program.parseAsync(process.argv);
120
+ function parseRuleList(value) {
121
+ const parsed = parseCommaList(value);
122
+ if (!parsed)
123
+ return undefined;
124
+ const aliases = {
125
+ components: "large-component",
126
+ component: "large-component",
127
+ state: "state-sprawl",
128
+ effects: "effect-complexity",
129
+ effect: "effect-complexity",
130
+ duplicates: "duplicate-logic",
131
+ duplicate: "duplicate-logic",
132
+ abstractions: "dead-abstraction",
133
+ abstraction: "dead-abstraction",
134
+ props: "prop-drilling",
135
+ comments: "todo-comment",
136
+ todos: "todo-comment",
137
+ naming: "naming-drift",
138
+ };
139
+ return parsed.map((rule) => aliases[rule] ?? rule);
140
+ }
141
+ function parseInteger(value) {
142
+ const parsed = Number(value);
143
+ if (!Number.isInteger(parsed) || parsed <= 0) {
144
+ throw new Error(`Expected a positive integer, received "${value}".`);
145
+ }
146
+ return parsed;
147
+ }
148
+ function parseFormat(value) {
149
+ if (value === "terminal" || value === "json" || value === "markdown" || value === "sarif")
150
+ return value;
151
+ throw new Error(`Invalid format "${value}". Expected terminal, json, markdown, or sarif.`);
152
+ }
153
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ export declare const CONFIG_FILENAME = "debtlens.config.json";
2
+ export interface InitResult {
3
+ path: string;
4
+ overwritten: boolean;
5
+ }
6
+ /**
7
+ * Write a starter `debtlens.config.json` into `cwd`. Refuses to clobber an existing
8
+ * config unless `force` is set.
9
+ */
10
+ export declare function runInit(cwd: string, force?: boolean): InitResult;
@@ -0,0 +1,18 @@
1
+ import { existsSync, writeFileSync } from "node:fs";
2
+ import { resolve } from "node:path";
3
+ import { renderConfigFile } from "../config/template.js";
4
+ export const CONFIG_FILENAME = "debtlens.config.json";
5
+ /**
6
+ * Write a starter `debtlens.config.json` into `cwd`. Refuses to clobber an existing
7
+ * config unless `force` is set.
8
+ */
9
+ export function runInit(cwd, force = false) {
10
+ const path = resolve(cwd, CONFIG_FILENAME);
11
+ const exists = existsSync(path);
12
+ if (exists && !force) {
13
+ throw new Error(`${CONFIG_FILENAME} already exists. Pass --force to overwrite it.`);
14
+ }
15
+ writeFileSync(path, renderConfigFile(), "utf8");
16
+ return { path, overwritten: exists };
17
+ }
18
+ //# sourceMappingURL=init.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/cli/init.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACpD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAEzD,MAAM,CAAC,MAAM,eAAe,GAAG,sBAAsB,CAAC;AAOtD;;;GAGG;AACH,MAAM,UAAU,OAAO,CAAC,GAAW,EAAE,KAAK,GAAG,KAAK;IAChD,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,EAAE,eAAe,CAAC,CAAC;IAC3C,MAAM,MAAM,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;IAEhC,IAAI,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC;QACrB,MAAM,IAAI,KAAK,CAAC,GAAG,eAAe,gDAAgD,CAAC,CAAC;IACtF,CAAC;IAED,aAAa,CAAC,IAAI,EAAE,gBAAgB,EAAE,EAAE,MAAM,CAAC,CAAC;IAChD,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,CAAC;AACvC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function parseCommaList(value: string | string[] | undefined): string[] | undefined;
2
+ export declare function parseThresholds(value: string | undefined): Record<string, number> | undefined;
@@ -0,0 +1,29 @@
1
+ export function parseCommaList(value) {
2
+ if (!value)
3
+ return undefined;
4
+ const values = Array.isArray(value) ? value : [value];
5
+ const parsed = values
6
+ .flatMap((item) => item.split(","))
7
+ .map((item) => item.trim())
8
+ .filter(Boolean);
9
+ return parsed.length > 0 ? parsed : undefined;
10
+ }
11
+ export function parseThresholds(value) {
12
+ if (!value)
13
+ return undefined;
14
+ const entries = value.split(",").map((entry) => entry.trim()).filter(Boolean);
15
+ const result = {};
16
+ for (const entry of entries) {
17
+ const [key, rawValue] = entry.split("=").map((part) => part.trim());
18
+ if (!key || !rawValue) {
19
+ throw new Error(`Invalid threshold "${entry}". Use key=value, for example large-component.maxLines=300.`);
20
+ }
21
+ const numberValue = Number(rawValue);
22
+ if (!Number.isFinite(numberValue)) {
23
+ throw new Error(`Invalid numeric threshold value in "${entry}".`);
24
+ }
25
+ result[key] = numberValue;
26
+ }
27
+ return result;
28
+ }
29
+ //# sourceMappingURL=parseList.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parseList.js","sourceRoot":"","sources":["../../src/cli/parseList.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,cAAc,CAAC,KAAoC;IACjE,IAAI,CAAC,KAAK;QAAE,OAAO,SAAS,CAAC;IAC7B,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;IACtD,MAAM,MAAM,GAAG,MAAM;SAClB,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;SAClC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;SAC1B,MAAM,CAAC,OAAO,CAAC,CAAC;IACnB,OAAO,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;AAChD,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,KAAyB;IACvD,IAAI,CAAC,KAAK;QAAE,OAAO,SAAS,CAAC;IAC7B,MAAM,OAAO,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAC9E,MAAM,MAAM,GAA2B,EAAE,CAAC;IAE1C,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,MAAM,CAAC,GAAG,EAAE,QAAQ,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;QACpE,IAAI,CAAC,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC;YACtB,MAAM,IAAI,KAAK,CAAC,sBAAsB,KAAK,6DAA6D,CAAC,CAAC;QAC5G,CAAC;QACD,MAAM,WAAW,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC;QACrC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;YAClC,MAAM,IAAI,KAAK,CAAC,uCAAuC,KAAK,IAAI,CAAC,CAAC;QACpE,CAAC;QACD,MAAM,CAAC,GAAG,CAAC,GAAG,WAAW,CAAC;IAC5B,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { DebtLensConfig } from "../core/types.js";
2
+ export declare const defaultConfig: Required<DebtLensConfig>;
@@ -0,0 +1,40 @@
1
+ export const defaultConfig = {
2
+ include: ["**/*.{ts,tsx,js,jsx}"],
3
+ exclude: [
4
+ "node_modules/**",
5
+ "dist/**",
6
+ "build/**",
7
+ "coverage/**",
8
+ ".next/**",
9
+ ".expo/**",
10
+ ".turbo/**",
11
+ "ios/**",
12
+ "android/**",
13
+ "**/*.d.ts",
14
+ "**/*.min.js",
15
+ "**/*.test.{ts,tsx,js,jsx}",
16
+ "**/*.spec.{ts,tsx,js,jsx}",
17
+ "**/__tests__/**",
18
+ "**/__mocks__/**"
19
+ ],
20
+ minSeverity: "low",
21
+ rules: [],
22
+ thresholds: {
23
+ "large-component.maxLines": 250,
24
+ "large-component.maxBranches": 16,
25
+ "large-component.maxHooks": 10,
26
+ "state-sprawl.maxStatefulHooks": 6,
27
+ "effect-complexity.maxLines": 30,
28
+ "effect-complexity.maxDependencies": 8,
29
+ "duplicate-logic.minSimilarity": 0.86,
30
+ "duplicate-logic.minLines": 8,
31
+ "duplicate-logic.maxSnippets": 450,
32
+ "dead-abstraction.maxWrapperLines": 8,
33
+ "prop-drilling.maxForwardedProps": 4,
34
+ "naming-drift.minVariants": 4,
35
+ "duplicate-logic.minStructuralSimilarity": 0.6
36
+ },
37
+ maxFiles: 2000,
38
+ vocabulary: {},
39
+ };
40
+ //# sourceMappingURL=defaults.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"defaults.js","sourceRoot":"","sources":["../../src/config/defaults.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,aAAa,GAA6B;IACrD,OAAO,EAAE,CAAC,sBAAsB,CAAC;IACjC,OAAO,EAAE;QACP,iBAAiB;QACjB,SAAS;QACT,UAAU;QACV,aAAa;QACb,UAAU;QACV,UAAU;QACV,WAAW;QACX,QAAQ;QACR,YAAY;QACZ,WAAW;QACX,aAAa;QACb,2BAA2B;QAC3B,2BAA2B;QAC3B,iBAAiB;QACjB,iBAAiB;KAClB;IACD,WAAW,EAAE,KAAK;IAClB,KAAK,EAAE,EAAE;IACT,UAAU,EAAE;QACV,0BAA0B,EAAE,GAAG;QAC/B,6BAA6B,EAAE,EAAE;QACjC,0BAA0B,EAAE,EAAE;QAC9B,+BAA+B,EAAE,CAAC;QAClC,4BAA4B,EAAE,EAAE;QAChC,mCAAmC,EAAE,CAAC;QACtC,+BAA+B,EAAE,IAAI;QACrC,0BAA0B,EAAE,CAAC;QAC7B,6BAA6B,EAAE,GAAG;QAClC,kCAAkC,EAAE,CAAC;QACrC,iCAAiC,EAAE,CAAC;QACpC,0BAA0B,EAAE,CAAC;QAC7B,yCAAyC,EAAE,GAAG;KAC/C;IACD,QAAQ,EAAE,IAAI;IACd,UAAU,EAAE,EAAE;CACf,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { DebtLensConfig } from "../core/types.js";
2
+ export declare function loadConfig(cwd: string, explicitPath?: string): DebtLensConfig;
@@ -0,0 +1,23 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { resolve } from "node:path";
3
+ const configNames = [
4
+ "debtlens.config.json",
5
+ ".debtlensrc.json"
6
+ ];
7
+ export function loadConfig(cwd, explicitPath) {
8
+ const configPath = explicitPath
9
+ ? resolve(cwd, explicitPath)
10
+ : configNames.map((name) => resolve(cwd, name)).find((candidate) => existsSync(candidate));
11
+ if (!configPath || !existsSync(configPath)) {
12
+ return {};
13
+ }
14
+ try {
15
+ const raw = readFileSync(configPath, "utf8");
16
+ return JSON.parse(raw);
17
+ }
18
+ catch (error) {
19
+ const message = error instanceof Error ? error.message : String(error);
20
+ throw new Error(`Could not read DebtLens config at ${configPath}: ${message}`);
21
+ }
22
+ }
23
+ //# sourceMappingURL=loadConfig.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loadConfig.js","sourceRoot":"","sources":["../../src/config/loadConfig.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGpC,MAAM,WAAW,GAAG;IAClB,sBAAsB;IACtB,kBAAkB;CACnB,CAAC;AAEF,MAAM,UAAU,UAAU,CAAC,GAAW,EAAE,YAAqB;IAC3D,MAAM,UAAU,GAAG,YAAY;QAC7B,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,YAAY,CAAC;QAC5B,CAAC,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC;IAE7F,IAAI,CAAC,UAAU,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC3C,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,YAAY,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;QAC7C,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAmB,CAAC;IAC3C,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACvE,MAAM,IAAI,KAAK,CAAC,qCAAqC,UAAU,KAAK,OAAO,EAAE,CAAC,CAAC;IACjF,CAAC;AACH,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { CliOptions, DebtLensConfig, ScanOptions } from "../core/types.js";
2
+ export declare function mergeConfig(target: string, fileConfig: DebtLensConfig, cliOptions: CliOptions): ScanOptions;
@@ -0,0 +1,26 @@
1
+ import { resolve } from "node:path";
2
+ import { defaultConfig } from "./defaults.js";
3
+ export function mergeConfig(target, fileConfig, cliOptions) {
4
+ const cwd = resolve(cliOptions.cwd ?? process.cwd());
5
+ return {
6
+ cwd,
7
+ target: resolve(cwd, target),
8
+ include: cliOptions.include?.length ? cliOptions.include : fileConfig.include ?? defaultConfig.include,
9
+ exclude: [
10
+ ...defaultConfig.exclude,
11
+ ...(fileConfig.exclude ?? []),
12
+ ...(cliOptions.exclude ?? []),
13
+ ],
14
+ minSeverity: cliOptions.minSeverity ?? fileConfig.minSeverity ?? defaultConfig.minSeverity,
15
+ rules: cliOptions.rules?.length ? cliOptions.rules : fileConfig.rules,
16
+ thresholds: {
17
+ ...defaultConfig.thresholds,
18
+ ...(fileConfig.thresholds ?? {}),
19
+ ...(cliOptions.thresholds ?? {}),
20
+ },
21
+ maxFiles: cliOptions.maxFiles ?? fileConfig.maxFiles ?? defaultConfig.maxFiles,
22
+ vocabulary: { ...defaultConfig.vocabulary, ...(fileConfig.vocabulary ?? {}) },
23
+ changedFiles: cliOptions.changedFiles,
24
+ };
25
+ }
26
+ //# sourceMappingURL=mergeConfig.js.map