@eduardbar/drift 0.9.0 → 1.0.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.
- package/.github/workflows/publish-vscode.yml +76 -0
- package/AGENTS.md +30 -12
- package/CHANGELOG.md +9 -0
- package/README.md +273 -168
- package/ROADMAP.md +130 -98
- package/dist/analyzer.d.ts +4 -38
- package/dist/analyzer.js +85 -1510
- package/dist/cli.js +47 -4
- package/dist/config.js +1 -1
- package/dist/fix.d.ts +13 -0
- package/dist/fix.js +120 -0
- package/dist/git/blame.d.ts +22 -0
- package/dist/git/blame.js +227 -0
- package/dist/git/helpers.d.ts +36 -0
- package/dist/git/helpers.js +152 -0
- package/dist/git/trend.d.ts +21 -0
- package/dist/git/trend.js +80 -0
- package/dist/git.d.ts +0 -4
- package/dist/git.js +2 -2
- package/dist/report.js +620 -293
- package/dist/rules/phase0-basic.d.ts +11 -0
- package/dist/rules/phase0-basic.js +176 -0
- package/dist/rules/phase1-complexity.d.ts +31 -0
- package/dist/rules/phase1-complexity.js +277 -0
- package/dist/rules/phase2-crossfile.d.ts +27 -0
- package/dist/rules/phase2-crossfile.js +122 -0
- package/dist/rules/phase3-arch.d.ts +31 -0
- package/dist/rules/phase3-arch.js +148 -0
- package/dist/rules/phase5-ai.d.ts +8 -0
- package/dist/rules/phase5-ai.js +262 -0
- package/dist/rules/phase8-semantic.d.ts +22 -0
- package/dist/rules/phase8-semantic.js +109 -0
- package/dist/rules/shared.d.ts +7 -0
- package/dist/rules/shared.js +27 -0
- package/package.json +8 -3
- package/packages/vscode-drift/.vscodeignore +9 -0
- package/packages/vscode-drift/LICENSE +21 -0
- package/packages/vscode-drift/README.md +64 -0
- package/packages/vscode-drift/images/icon.png +0 -0
- package/packages/vscode-drift/images/icon.svg +30 -0
- package/packages/vscode-drift/package-lock.json +485 -0
- package/packages/vscode-drift/package.json +119 -0
- package/packages/vscode-drift/src/analyzer.ts +38 -0
- package/packages/vscode-drift/src/diagnostics.ts +55 -0
- package/packages/vscode-drift/src/extension.ts +111 -0
- package/packages/vscode-drift/src/statusbar.ts +47 -0
- package/packages/vscode-drift/src/treeview.ts +108 -0
- package/packages/vscode-drift/tsconfig.json +18 -0
- package/packages/vscode-drift/vscode-drift-0.1.0.vsix +0 -0
- package/packages/vscode-drift/vscode-drift-0.1.1.vsix +0 -0
- package/src/analyzer.ts +124 -1726
- package/src/cli.ts +53 -4
- package/src/config.ts +1 -1
- package/src/fix.ts +154 -0
- package/src/git/blame.ts +279 -0
- package/src/git/helpers.ts +198 -0
- package/src/git/trend.ts +116 -0
- package/src/git.ts +2 -2
- package/src/report.ts +631 -296
- package/src/rules/phase0-basic.ts +187 -0
- package/src/rules/phase1-complexity.ts +302 -0
- package/src/rules/phase2-crossfile.ts +149 -0
- package/src/rules/phase3-arch.ts +179 -0
- package/src/rules/phase5-ai.ts +292 -0
- package/src/rules/phase8-semantic.ts +132 -0
- package/src/rules/shared.ts +39 -0
- package/tests/helpers.ts +45 -0
- package/tests/rules.test.ts +1269 -0
- package/vitest.config.ts +15 -0
package/README.md
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-

|
|
2
2
|
|
|
3
3
|
# drift
|
|
4
4
|
|
|
5
|
-
Detect
|
|
6
|
-
|
|
7
|
-
_Vibe coding ships fast. drift tells you what it left behind._
|
|
5
|
+
Detect technical debt in AI-generated TypeScript code. One command. Zero config.
|
|
8
6
|
|
|
9
7
|

|
|
10
8
|

|
|
@@ -12,20 +10,73 @@ _Vibe coding ships fast. drift tells you what it left behind._
|
|
|
12
10
|

|
|
13
11
|

|
|
14
12
|
|
|
15
|
-
[Installation](
|
|
13
|
+
[Why](#why) · [Installation](#installation) · [Commands](#commands) · [Rules](#rules) · [Score](#score) · [Configuration](#configuration) · [CI Integration](#ci-integration) · [drift-ignore](#drift-ignore) · [Contributing](#contributing)
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Why
|
|
18
|
+
|
|
19
|
+
AI coding tools ship code fast. They also leave behind consistent, predictable structural patterns that accumulate silently: files that grow to 600 lines, catch blocks that swallow errors, exports that nothing imports, functions duplicated across three modules because the model regenerated instead of reusing.
|
|
20
|
+
|
|
21
|
+
GitClear's 2024 analysis of 211M lines of code found a **39.9% drop in refactoring activity** and an **8x increase in duplicated code blocks** since AI tools became mainstream. A senior engineer on r/vibecoding put it plainly: _"The code looks reviewed. It isn't. Nobody's reading 400-line files the AI dumped in one shot."_
|
|
22
|
+
|
|
23
|
+
drift gives you a 0–100 score per file and project so you know what to look at before it reaches production.
|
|
24
|
+
|
|
25
|
+
**How drift compares to existing tools:**
|
|
26
|
+
|
|
27
|
+
| Tool | What it does | What it misses |
|
|
28
|
+
|------|--------------|----------------|
|
|
29
|
+
| ESLint | Correctness and style within a single file | Structural patterns, cross-file dead code, architecture violations |
|
|
30
|
+
| SonarQube | Enterprise-grade static analysis | Costs money, requires infrastructure, overwhelming for small teams |
|
|
31
|
+
| drift | Structural debt + AI-specific patterns + cross-file analysis + 0–100 score | Not a linter — does not replace ESLint |
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## Installation
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
# Run without installing
|
|
39
|
+
npx @eduardbar/drift scan .
|
|
40
|
+
|
|
41
|
+
# Install globally
|
|
42
|
+
npm install -g @eduardbar/drift
|
|
43
|
+
|
|
44
|
+
# Install as a dev dependency
|
|
45
|
+
npm install --save-dev @eduardbar/drift
|
|
46
|
+
```
|
|
16
47
|
|
|
17
48
|
---
|
|
18
49
|
|
|
19
|
-
##
|
|
50
|
+
## Commands
|
|
20
51
|
|
|
21
|
-
|
|
52
|
+
### `drift scan [path]`
|
|
22
53
|
|
|
23
|
-
|
|
54
|
+
Scan a directory and print a scored report to stdout.
|
|
24
55
|
|
|
25
56
|
```bash
|
|
26
|
-
|
|
57
|
+
drift scan .
|
|
58
|
+
drift scan ./src
|
|
59
|
+
drift scan ./src --output report.md
|
|
60
|
+
drift scan ./src --json
|
|
61
|
+
drift scan ./src --ai
|
|
62
|
+
drift scan ./src --fix
|
|
63
|
+
drift scan ./src --min-score 50
|
|
64
|
+
```
|
|
27
65
|
|
|
28
|
-
|
|
66
|
+
**Options:**
|
|
67
|
+
|
|
68
|
+
| Flag | Description |
|
|
69
|
+
|------|-------------|
|
|
70
|
+
| `--output <file>` | Write Markdown report to a file instead of stdout |
|
|
71
|
+
| `--json` | Output raw `DriftReport` JSON |
|
|
72
|
+
| `--ai` | Output structured JSON optimized for LLM consumption (Claude, GPT, etc.) |
|
|
73
|
+
| `--fix` | Print inline fix suggestions for each detected issue |
|
|
74
|
+
| `--min-score <n>` | Exit with code 1 if the overall score meets or exceeds this threshold |
|
|
75
|
+
|
|
76
|
+
**Example output:**
|
|
77
|
+
|
|
78
|
+
```
|
|
79
|
+
drift — technical debt detector
|
|
29
80
|
──────────────────────────────────────────────────
|
|
30
81
|
|
|
31
82
|
Score █████████████░░░░░░░ 67/100 HIGH
|
|
@@ -48,156 +99,181 @@ $ npx @eduardbar/drift scan ./src
|
|
|
48
99
|
|
|
49
100
|
---
|
|
50
101
|
|
|
51
|
-
|
|
102
|
+
### `drift diff [ref]`
|
|
103
|
+
|
|
104
|
+
Compare the current project state against any git ref. Defaults to `HEAD~1`.
|
|
52
105
|
|
|
53
106
|
```bash
|
|
54
|
-
#
|
|
55
|
-
|
|
107
|
+
drift diff # HEAD vs HEAD~1
|
|
108
|
+
drift diff HEAD~3 # HEAD vs 3 commits ago
|
|
109
|
+
drift diff main # HEAD vs branch main
|
|
110
|
+
drift diff abc1234 # HEAD vs a specific commit
|
|
111
|
+
drift diff --json # Output raw JSON diff
|
|
112
|
+
```
|
|
56
113
|
|
|
57
|
-
|
|
58
|
-
npm install -g @eduardbar/drift
|
|
59
|
-
drift scan ./src
|
|
114
|
+
**Options:**
|
|
60
115
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
116
|
+
| Flag | Description |
|
|
117
|
+
|------|-------------|
|
|
118
|
+
| `--json` | Output raw JSON diff |
|
|
119
|
+
|
|
120
|
+
Shows score delta, issues introduced, and issues resolved since the given ref.
|
|
64
121
|
|
|
65
122
|
---
|
|
66
123
|
|
|
67
|
-
|
|
124
|
+
### `drift report [path]`
|
|
125
|
+
|
|
126
|
+
Generate a self-contained HTML report. No server required — open in any browser.
|
|
68
127
|
|
|
69
128
|
```bash
|
|
70
|
-
#
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
npx @eduardbar/drift scan ./src --output report.md
|
|
74
|
-
npx @eduardbar/drift scan ./src --json
|
|
75
|
-
npx @eduardbar/drift scan ./src --ai
|
|
76
|
-
npx @eduardbar/drift scan ./src --fix
|
|
77
|
-
npx @eduardbar/drift scan ./src --min-score 50
|
|
78
|
-
|
|
79
|
-
# Install globally if you want the short 'drift' command
|
|
80
|
-
npm install -g @eduardbar/drift
|
|
81
|
-
drift scan .
|
|
129
|
+
drift report # scan current directory
|
|
130
|
+
drift report ./src # scan specific path
|
|
131
|
+
drift report ./src --output my-report.html
|
|
82
132
|
```
|
|
83
133
|
|
|
84
|
-
|
|
134
|
+
**Options:**
|
|
85
135
|
|
|
86
136
|
| Flag | Description |
|
|
87
137
|
|------|-------------|
|
|
88
|
-
| `--output <file>` |
|
|
89
|
-
| `--json` | Output raw JSON instead of console output |
|
|
90
|
-
| `--ai` | Output AI-optimized JSON for LLM consumption (Claude, GPT, etc.) |
|
|
91
|
-
| `--fix` | Show fix suggestions for each detected issue |
|
|
92
|
-
| `--min-score <n>` | Exit with code 1 if overall score exceeds threshold |
|
|
138
|
+
| `--output <file>` | Output path for the HTML file (default: `drift-report.html`) |
|
|
93
139
|
|
|
94
|
-
|
|
140
|
+
All styles and data are embedded inline in the output file.
|
|
95
141
|
|
|
96
|
-
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
### `drift badge [path]`
|
|
145
|
+
|
|
146
|
+
Generate a `badge.svg` with the current score, compatible with shields.io style.
|
|
97
147
|
|
|
98
148
|
```bash
|
|
99
|
-
drift
|
|
100
|
-
drift
|
|
101
|
-
drift
|
|
102
|
-
drift diff abc1234 # HEAD vs specific commit
|
|
103
|
-
drift diff --json # Output raw JSON diff
|
|
149
|
+
drift badge # writes badge.svg to current directory
|
|
150
|
+
drift badge ./src
|
|
151
|
+
drift badge ./src --output ./assets/drift-badge.svg
|
|
104
152
|
```
|
|
105
153
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
### `drift report [path]`
|
|
154
|
+
**Options:**
|
|
109
155
|
|
|
110
|
-
|
|
156
|
+
| Flag | Description |
|
|
157
|
+
|------|-------------|
|
|
158
|
+
| `--output <file>` | Output path for the SVG file (default: `badge.svg`) |
|
|
111
159
|
|
|
112
|
-
|
|
113
|
-
drift report # scan current directory
|
|
114
|
-
drift report ./src # scan specific path
|
|
115
|
-
```
|
|
160
|
+
Add the badge to your README — see [README Badge](#readme-badge).
|
|
116
161
|
|
|
117
|
-
|
|
162
|
+
---
|
|
118
163
|
|
|
119
|
-
### `drift
|
|
164
|
+
### `drift ci [path]`
|
|
120
165
|
|
|
121
|
-
|
|
166
|
+
Emit GitHub Actions annotations and a step summary. Designed to run inside a CI workflow.
|
|
122
167
|
|
|
123
168
|
```bash
|
|
124
|
-
drift
|
|
125
|
-
drift
|
|
169
|
+
drift ci # scan current directory
|
|
170
|
+
drift ci ./src
|
|
171
|
+
drift ci ./src --min-score 60
|
|
126
172
|
```
|
|
127
173
|
|
|
128
|
-
|
|
174
|
+
**Options:**
|
|
129
175
|
|
|
130
|
-
|
|
176
|
+
| Flag | Description |
|
|
177
|
+
|------|-------------|
|
|
178
|
+
| `--min-score <n>` | Exit with code 1 if the overall score meets or exceeds this threshold |
|
|
179
|
+
|
|
180
|
+
Outputs `::error` and `::warning` annotations visible in the PR diff. Writes a markdown summary to `$GITHUB_STEP_SUMMARY`.
|
|
181
|
+
|
|
182
|
+
---
|
|
183
|
+
|
|
184
|
+
### `drift trend [period]`
|
|
131
185
|
|
|
132
|
-
|
|
186
|
+
Show score evolution over time. `period` accepts: `week`, `month`, `quarter`, `year`.
|
|
133
187
|
|
|
134
188
|
```bash
|
|
135
|
-
drift
|
|
136
|
-
drift
|
|
137
|
-
drift
|
|
189
|
+
drift trend week
|
|
190
|
+
drift trend month
|
|
191
|
+
drift trend quarter --since 2025-01-01
|
|
192
|
+
drift trend year --until 2025-12-31
|
|
138
193
|
```
|
|
139
194
|
|
|
140
|
-
|
|
195
|
+
**Options:**
|
|
141
196
|
|
|
142
|
-
|
|
197
|
+
| Flag | Description |
|
|
198
|
+
|------|-------------|
|
|
199
|
+
| `--since <date>` | Start date for the trend window (ISO 8601) |
|
|
200
|
+
| `--until <date>` | End date for the trend window (ISO 8601) |
|
|
201
|
+
|
|
202
|
+
---
|
|
143
203
|
|
|
144
|
-
|
|
204
|
+
### `drift blame [target]`
|
|
205
|
+
|
|
206
|
+
Identify which files, rules, or contributors are responsible for the most debt. `target` accepts: `file`, `rule`, `overall`.
|
|
145
207
|
|
|
146
208
|
```bash
|
|
147
|
-
|
|
209
|
+
drift blame file # top files by score
|
|
210
|
+
drift blame rule # top rules by frequency
|
|
211
|
+
drift blame overall
|
|
212
|
+
drift blame file --top 10
|
|
148
213
|
```
|
|
149
214
|
|
|
150
|
-
|
|
151
|
-
- Priority-ordered issues (by severity and effort)
|
|
152
|
-
- Fix suggestions for each issue
|
|
153
|
-
- Recommended action for quick wins
|
|
215
|
+
**Options:**
|
|
154
216
|
|
|
155
|
-
|
|
217
|
+
| Flag | Description |
|
|
218
|
+
|------|-------------|
|
|
219
|
+
| `--top <n>` | Limit output to top N results (default: 5) |
|
|
156
220
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
221
|
+
---
|
|
222
|
+
|
|
223
|
+
## Rules
|
|
224
|
+
|
|
225
|
+
26 rules across three severity levels. All run automatically unless marked as requiring configuration.
|
|
226
|
+
|
|
227
|
+
| Rule | Severity | Weight | What it detects |
|
|
228
|
+
|------|----------|--------|-----------------|
|
|
229
|
+
| `large-file` | error | 20 | Files exceeding 300 lines — AI generates monolithic files instead of splitting responsibility |
|
|
230
|
+
| `large-function` | error | 15 | Functions exceeding 50 lines — AI avoids decomposing logic into smaller units |
|
|
231
|
+
| `duplicate-function-name` | error | 18 | Function names that appear more than once (case-insensitive) — AI regenerates helpers instead of reusing them |
|
|
232
|
+
| `high-complexity` | error | 15 | Cyclomatic complexity above 10 — AI produces correct code, not necessarily simple code |
|
|
233
|
+
| `circular-dependency` | error | 14 | Circular import chains between modules — AI doesn't reason about module topology |
|
|
234
|
+
| `layer-violation` | error | 16 | Imports that cross architectural layers in the wrong direction (e.g., domain importing from infra) — requires `drift.config.ts` |
|
|
235
|
+
| `debug-leftover` | warning | 10 | `console.log`, `console.warn`, `console.error`, and `TODO` / `FIXME` / `HACK` comments — AI leaves scaffolding in place |
|
|
236
|
+
| `dead-code` | warning | 8 | Named imports that are never used in the file — AI imports broadly |
|
|
237
|
+
| `any-abuse` | warning | 8 | Explicit `any` type annotations — AI defaults to `any` when type inference is unclear |
|
|
238
|
+
| `catch-swallow` | warning | 10 | Empty `catch` blocks — AI makes code not throw without handling the error |
|
|
239
|
+
| `comment-contradiction` | warning | 12 | Comments that restate what the surrounding code already expresses — AI over-documents the obvious |
|
|
240
|
+
| `deep-nesting` | warning | 12 | Control flow nested more than 3 levels deep — results in code that is difficult to follow |
|
|
241
|
+
| `too-many-params` | warning | 8 | Functions with more than 4 parameters — AI avoids grouping related arguments into objects |
|
|
242
|
+
| `high-coupling` | warning | 10 | Files importing from more than 10 distinct modules — AI imports broadly without encapsulation |
|
|
243
|
+
| `promise-style-mix` | warning | 7 | `async/await` and `.then()` / `.catch()` used together in the same file — AI combines styles inconsistently |
|
|
244
|
+
| `unused-export` | warning | 8 | Named exports that are never imported anywhere in the project — cross-file dead code ESLint cannot detect |
|
|
245
|
+
| `dead-file` | warning | 10 | Files never imported by any other file in the project — invisible dead code |
|
|
246
|
+
| `unused-dependency` | warning | 6 | Packages listed in `package.json` with no corresponding import in source files |
|
|
247
|
+
| `cross-boundary-import` | warning | 10 | Imports that cross module boundaries outside the allowed list — requires `drift.config.ts` |
|
|
248
|
+
| `hardcoded-config` | warning | 10 | Hardcoded URLs, IP addresses, secrets, or connection strings — AI skips environment variable abstraction |
|
|
249
|
+
| `inconsistent-error-handling` | warning | 8 | Mixed `try/catch` and `.catch()` patterns in the same file — AI combines approaches without a consistent strategy |
|
|
250
|
+
| `unnecessary-abstraction` | warning | 7 | Wrapper functions or helpers that add no logic over what they wrap — AI over-engineers simple calls |
|
|
251
|
+
| `naming-inconsistency` | warning | 6 | Mixed `camelCase` and `snake_case` in the same module — AI forgets project conventions mid-generation |
|
|
252
|
+
| `semantic-duplication` | warning | 12 | Functions with structurally identical logic despite different names — detected via AST fingerprinting, not text comparison |
|
|
253
|
+
| `no-return-type` | info | 5 | Functions missing an explicit return type annotation |
|
|
254
|
+
| `magic-number` | info | 3 | Numeric literals used directly in logic without a named constant |
|
|
160
255
|
|
|
161
256
|
---
|
|
162
257
|
|
|
163
|
-
##
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
|
168
|
-
|
|
169
|
-
|
|
|
170
|
-
|
|
|
171
|
-
|
|
|
172
|
-
|
|
|
173
|
-
|
|
|
174
|
-
| `any-abuse` | warning | Explicit `any` type — AI defaults to `any` when it can't infer |
|
|
175
|
-
| `catch-swallow` | warning | Empty catch blocks — AI makes code "not throw" |
|
|
176
|
-
| `comment-contradiction` | warning | Comments that restate what the code already says — AI documents the obvious |
|
|
177
|
-
| `deep-nesting` | warning | Nesting depth > 3 — if inside for inside if inside try = unreadable |
|
|
178
|
-
| `too-many-params` | warning | Functions with more than 4 parameters — AI avoids options objects |
|
|
179
|
-
| `high-coupling` | warning | Files importing from more than 10 modules — AI imports broadly |
|
|
180
|
-
| `promise-style-mix` | warning | `async/await` and `.then()` mixed in the same file |
|
|
181
|
-
| `unused-export` | warning | Named exports never imported anywhere in the project — cross-file dead code |
|
|
182
|
-
| `dead-file` | warning | Files never imported by any other file — invisible dead code |
|
|
183
|
-
| `unused-dependency` | warning | Packages in `package.json` never imported in source code |
|
|
184
|
-
| `no-return-type` | info | Missing explicit return types on functions |
|
|
185
|
-
| `magic-number` | info | Numeric literals used directly in logic — extract to named constants |
|
|
186
|
-
| `layer-violation` | error | Layer imports a layer it's not allowed to (requires `drift.config.ts`) |
|
|
187
|
-
| `cross-boundary-import` | warning | Module imports from another module outside allowed boundaries (requires `drift.config.ts`) |
|
|
188
|
-
| `over-commented` | info | Functions where comments exceed 40% of lines — AI over-documents the obvious |
|
|
189
|
-
| `hardcoded-config` | warning | Hardcoded URLs, IPs, or connection strings — AI skips environment variables |
|
|
190
|
-
| `inconsistent-error-handling` | warning | Mixed `try/catch` and `.catch()` in the same file — AI combines styles randomly |
|
|
191
|
-
| `unnecessary-abstraction` | warning | Single-method interfaces or abstract classes with no reuse — AI over-engineers |
|
|
192
|
-
| `naming-inconsistency` | warning | Mixed camelCase and snake_case in the same scope — AI forgets project conventions |
|
|
258
|
+
## Score
|
|
259
|
+
|
|
260
|
+
**Calculation:** For each file, drift sums the weights of all detected issues, capped at 100. The project score is the average across all scanned files.
|
|
261
|
+
|
|
262
|
+
| Score | Grade | Meaning |
|
|
263
|
+
|-------|-------|---------|
|
|
264
|
+
| 0 | CLEAN | No issues found |
|
|
265
|
+
| 1–19 | LOW | Minor issues — safe to ship |
|
|
266
|
+
| 20–44 | MODERATE | Worth a review before merging |
|
|
267
|
+
| 45–69 | HIGH | Significant structural debt detected |
|
|
268
|
+
| 70–100 | CRITICAL | Review before this goes anywhere near production |
|
|
193
269
|
|
|
194
270
|
---
|
|
195
271
|
|
|
196
|
-
##
|
|
272
|
+
## Configuration
|
|
197
273
|
|
|
198
|
-
Architectural rules (`layer-violation`, `cross-boundary-import`) require a `drift.config.ts` at your project root:
|
|
274
|
+
drift runs with zero configuration. Architectural rules (`layer-violation`, `cross-boundary-import`) require a `drift.config.ts` (or `.js` / `.json`) at your project root:
|
|
199
275
|
|
|
200
|
-
```
|
|
276
|
+
```typescript
|
|
201
277
|
import type { DriftConfig } from '@eduardbar/drift'
|
|
202
278
|
|
|
203
279
|
export default {
|
|
@@ -206,117 +282,146 @@ export default {
|
|
|
206
282
|
{ name: 'app', patterns: ['src/app/**'], canImportFrom: ['domain'] },
|
|
207
283
|
{ name: 'infra', patterns: ['src/infra/**'], canImportFrom: ['domain', 'app'] },
|
|
208
284
|
],
|
|
209
|
-
|
|
285
|
+
boundaries: [
|
|
210
286
|
{ name: 'auth', root: 'src/modules/auth', allowedExternalImports: ['src/shared'] },
|
|
211
287
|
{ name: 'billing', root: 'src/modules/billing', allowedExternalImports: ['src/shared'] },
|
|
212
288
|
],
|
|
289
|
+
exclude: [
|
|
290
|
+
'src/generated/**',
|
|
291
|
+
'**/*.spec.ts',
|
|
292
|
+
],
|
|
293
|
+
rules: {
|
|
294
|
+
'large-file': { threshold: 400 }, // override default 300
|
|
295
|
+
'magic-number': 'off', // disable a rule
|
|
296
|
+
},
|
|
213
297
|
} satisfies DriftConfig
|
|
214
298
|
```
|
|
215
299
|
|
|
216
|
-
Without a config file,
|
|
300
|
+
Without a config file, `layer-violation` and `cross-boundary-import` are silently skipped. All other rules run with their defaults.
|
|
217
301
|
|
|
218
302
|
---
|
|
219
303
|
|
|
220
|
-
##
|
|
304
|
+
## CI Integration
|
|
221
305
|
|
|
222
|
-
|
|
306
|
+
### Basic gate with `scan`
|
|
223
307
|
|
|
224
308
|
```yaml
|
|
225
|
-
|
|
226
|
-
|
|
309
|
+
name: Drift
|
|
310
|
+
|
|
311
|
+
on: [pull_request]
|
|
312
|
+
|
|
313
|
+
jobs:
|
|
314
|
+
drift:
|
|
315
|
+
runs-on: ubuntu-latest
|
|
316
|
+
steps:
|
|
317
|
+
- uses: actions/checkout@v4
|
|
318
|
+
- uses: actions/setup-node@v4
|
|
319
|
+
with:
|
|
320
|
+
node-version: 20
|
|
321
|
+
- name: Check debt score
|
|
322
|
+
run: npx @eduardbar/drift scan ./src --min-score 60
|
|
227
323
|
```
|
|
228
324
|
|
|
229
|
-
|
|
230
|
-
- Emit inline annotations on the exact lines with issues (visible in the PR diff)
|
|
231
|
-
- Write a summary to the GitHub Actions step summary
|
|
232
|
-
- Exit with code 1 if the score exceeds the threshold
|
|
325
|
+
Exit code is `1` if the score meets or exceeds `--min-score`. Exit code `0` otherwise.
|
|
233
326
|
|
|
234
|
-
|
|
327
|
+
### Annotations and step summary with `drift ci`
|
|
235
328
|
|
|
236
329
|
```yaml
|
|
237
|
-
|
|
238
|
-
|
|
330
|
+
name: Drift
|
|
331
|
+
|
|
332
|
+
on: [pull_request]
|
|
333
|
+
|
|
334
|
+
jobs:
|
|
335
|
+
drift:
|
|
336
|
+
runs-on: ubuntu-latest
|
|
337
|
+
steps:
|
|
338
|
+
- uses: actions/checkout@v4
|
|
339
|
+
- uses: actions/setup-node@v4
|
|
340
|
+
with:
|
|
341
|
+
node-version: 20
|
|
342
|
+
- name: Run drift
|
|
343
|
+
run: npx @eduardbar/drift ci ./src --min-score 60
|
|
239
344
|
```
|
|
240
345
|
|
|
241
|
-
|
|
346
|
+
`drift ci` emits `::error` and `::warning` annotations that appear inline in the PR diff and writes a formatted summary to `$GITHUB_STEP_SUMMARY`. Use this when you want visibility beyond a pass/fail exit code.
|
|
242
347
|
|
|
243
348
|
---
|
|
244
349
|
|
|
245
|
-
##
|
|
350
|
+
## drift-ignore
|
|
246
351
|
|
|
247
|
-
|
|
248
|
-
|-------|-------|---------|
|
|
249
|
-
| 0 | CLEAN | No issues found |
|
|
250
|
-
| 1–19 | LOW | Minor issues, safe to ship |
|
|
251
|
-
| 20–44 | MODERATE | Worth a review before merging |
|
|
252
|
-
| 45–69 | HIGH | Significant structural debt detected |
|
|
253
|
-
| 70–100 | CRITICAL | Review before this goes anywhere near production |
|
|
352
|
+
### Suppress a single issue
|
|
254
353
|
|
|
255
|
-
|
|
354
|
+
Add `// drift-ignore` at the end of the flagged line or on the line immediately above it:
|
|
256
355
|
|
|
257
|
-
|
|
356
|
+
```typescript
|
|
357
|
+
console.log(debugPayload) // drift-ignore
|
|
358
|
+
```
|
|
258
359
|
|
|
360
|
+
```typescript
|
|
361
|
+
// drift-ignore
|
|
362
|
+
const result: any = parse(input)
|
|
259
363
|
```
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
364
|
+
|
|
365
|
+
### Suppress an entire file
|
|
366
|
+
|
|
367
|
+
Add `// drift-ignore-file` anywhere in the first 10 lines of the file:
|
|
368
|
+
|
|
369
|
+
```typescript
|
|
370
|
+
// drift-ignore-file
|
|
371
|
+
// This file contains intentional console output — not debug leftovers.
|
|
267
372
|
```
|
|
268
373
|
|
|
374
|
+
When `drift-ignore-file` is present, `analyzeFile()` returns an empty report with score 0 for that file. Use this for files like loggers or CLI printers where `console.*` calls are intentional.
|
|
375
|
+
|
|
269
376
|
---
|
|
270
377
|
|
|
271
|
-
##
|
|
378
|
+
## README Badge
|
|
272
379
|
|
|
273
|
-
|
|
380
|
+
Generate a badge from your project score and add it to your README:
|
|
274
381
|
|
|
275
382
|
```bash
|
|
276
|
-
|
|
277
|
-
cd drift
|
|
278
|
-
npm install
|
|
279
|
-
npm run build
|
|
280
|
-
node dist/cli.js scan ./src
|
|
383
|
+
drift badge . --output ./assets/drift-badge.svg
|
|
281
384
|
```
|
|
282
385
|
|
|
283
|
-
|
|
386
|
+
Then reference it in your README:
|
|
284
387
|
|
|
285
|
-
```
|
|
286
|
-
|
|
388
|
+
```markdown
|
|
389
|
+

|
|
287
390
|
```
|
|
288
391
|
|
|
392
|
+
The badge uses shields.io-compatible styling and color-codes automatically by grade: green for LOW, yellow for MODERATE, orange for HIGH, red for CRITICAL.
|
|
393
|
+
|
|
289
394
|
---
|
|
290
395
|
|
|
291
|
-
##
|
|
396
|
+
## Contributing
|
|
292
397
|
|
|
293
|
-
|
|
398
|
+
Open an issue before starting significant work. Check [existing issues](https://github.com/eduardbar/drift/issues) first — use the bug report or feature request templates.
|
|
294
399
|
|
|
295
|
-
**
|
|
400
|
+
**To add a new detection rule:**
|
|
296
401
|
|
|
297
|
-
1.
|
|
298
|
-
2. Add
|
|
299
|
-
3. Implement
|
|
300
|
-
4. Add a `fix_suggestion`
|
|
402
|
+
1. Create a branch: `git checkout -b feat/rule-name`
|
|
403
|
+
2. Add `"rule-name": <weight>` to `RULE_WEIGHTS` in `src/analyzer.ts`
|
|
404
|
+
3. Implement AST detection logic using ts-morph in `analyzeFile()`
|
|
405
|
+
4. Add a `fix_suggestion` entry in `src/printer.ts`
|
|
301
406
|
5. Update the rules table in `README.md` and `AGENTS.md`
|
|
302
|
-
6. Open a PR
|
|
303
|
-
|
|
304
|
-
Before opening an issue, check [existing issues](https://github.com/eduardbar/drift/issues). Use the [bug report](./.github/ISSUE_TEMPLATE/bug_report.md) or [feature request](./.github/ISSUE_TEMPLATE/feature_request.md) templates.
|
|
407
|
+
6. Open a PR using the template in `.github/PULL_REQUEST_TEMPLATE.md`
|
|
305
408
|
|
|
306
|
-
|
|
409
|
+
See [CODE_OF_CONDUCT.md](./CODE_OF_CONDUCT.md) before participating.
|
|
307
410
|
|
|
308
411
|
---
|
|
309
412
|
|
|
310
|
-
##
|
|
413
|
+
## Stack
|
|
414
|
+
|
|
415
|
+
| Package | Role |
|
|
416
|
+
|---------|------|
|
|
417
|
+
| [`ts-morph`](https://github.com/dsherret/ts-morph) | AST traversal and TypeScript analysis |
|
|
418
|
+
| [`commander`](https://github.com/tj/commander.js) | CLI commands and flags |
|
|
419
|
+
| [`kleur`](https://github.com/lukeed/kleur) | Terminal colors (zero dependencies) |
|
|
311
420
|
|
|
312
|
-
|
|
421
|
+
**Runtime:** Node.js 18+ · TypeScript 5.x · ES Modules · Supports TypeScript (`.ts`, `.tsx`) and JavaScript (`.js`, `.jsx`) files
|
|
313
422
|
|
|
314
423
|
---
|
|
315
424
|
|
|
316
|
-
##
|
|
425
|
+
## License
|
|
317
426
|
|
|
318
427
|
MIT © [eduardbar](https://github.com/eduardbar)
|
|
319
|
-
|
|
320
|
-
---
|
|
321
|
-
|
|
322
|
-
_Built with mate by a developer who got tired of reviewing the same AI-generated patterns every week._
|