@eduardbar/drift 0.3.0 → 0.4.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/README.md CHANGED
@@ -91,6 +91,20 @@ drift scan .
91
91
  | `--fix` | Show fix suggestions for each detected issue |
92
92
  | `--min-score <n>` | Exit with code 1 if overall score exceeds threshold |
93
93
 
94
+ ### `drift diff [ref]`
95
+
96
+ Compare the current state of your project against any git ref:
97
+
98
+ ```bash
99
+ drift diff # HEAD vs HEAD~1 (default)
100
+ drift diff HEAD~3 # HEAD vs 3 commits ago
101
+ drift diff main # HEAD vs branch main
102
+ drift diff abc1234 # HEAD vs specific commit
103
+ drift diff --json # Output raw JSON diff
104
+ ```
105
+
106
+ Shows score delta, new issues introduced, and issues resolved per file.
107
+
94
108
  ### AI Integration
95
109
 
96
110
  Use `--ai` to get structured output that LLMs can consume:
@@ -118,12 +132,49 @@ npx @eduardbar/drift scan ./src --fix
118
132
  |------|----------|-----------------|
119
133
  | `large-file` | error | Files over 300 lines — AI dumps everything into one place |
120
134
  | `large-function` | error | Functions over 50 lines — AI avoids splitting logic |
135
+ | `duplicate-function-name` | error | Near-identical function names — AI regenerates instead of reusing |
136
+ | `high-complexity` | error | Cyclomatic complexity > 10 — AI generates correct code, not simple code |
137
+ | `circular-dependency` | error | Circular import chains between modules |
121
138
  | `debug-leftover` | warning | `console.log`, `TODO`, `FIXME`, `HACK` comments |
122
139
  | `dead-code` | warning | Unused imports — AI imports more than it uses |
123
- | `duplicate-function-name` | error | Near-identical function names — AI regenerates instead of reusing |
124
140
  | `any-abuse` | warning | Explicit `any` type — AI defaults to `any` when it can't infer |
125
141
  | `catch-swallow` | warning | Empty catch blocks — AI makes code "not throw" |
142
+ | `comment-contradiction` | warning | Comments that restate what the code already says — AI documents the obvious |
143
+ | `deep-nesting` | warning | Nesting depth > 3 — if inside for inside if inside try = unreadable |
144
+ | `too-many-params` | warning | Functions with more than 4 parameters — AI avoids options objects |
145
+ | `high-coupling` | warning | Files importing from more than 10 modules — AI imports broadly |
146
+ | `promise-style-mix` | warning | `async/await` and `.then()` mixed in the same file |
147
+ | `unused-export` | warning | Named exports never imported anywhere in the project — cross-file dead code |
148
+ | `dead-file` | warning | Files never imported by any other file — invisible dead code |
149
+ | `unused-dependency` | warning | Packages in `package.json` never imported in source code |
126
150
  | `no-return-type` | info | Missing explicit return types on functions |
151
+ | `magic-number` | info | Numeric literals used directly in logic — extract to named constants |
152
+ | `layer-violation` | error | Layer imports a layer it's not allowed to (requires `drift.config.ts`) |
153
+ | `cross-boundary-import` | warning | Module imports from another module outside allowed boundaries (requires `drift.config.ts`) |
154
+
155
+ ---
156
+
157
+ ## ⚙️ Configuration (optional)
158
+
159
+ Architectural rules (`layer-violation`, `cross-boundary-import`) require a `drift.config.ts` at your project root:
160
+
161
+ ```ts
162
+ import type { DriftConfig } from '@eduardbar/drift'
163
+
164
+ export default {
165
+ layers: [
166
+ { name: 'domain', patterns: ['src/domain/**'], canImportFrom: [] },
167
+ { name: 'app', patterns: ['src/app/**'], canImportFrom: ['domain'] },
168
+ { name: 'infra', patterns: ['src/infra/**'], canImportFrom: ['domain', 'app'] },
169
+ ],
170
+ modules: [
171
+ { name: 'auth', root: 'src/modules/auth', allowedExternalImports: ['src/shared'] },
172
+ { name: 'billing', root: 'src/modules/billing', allowedExternalImports: ['src/shared'] },
173
+ ],
174
+ } satisfies DriftConfig
175
+ ```
176
+
177
+ Without a config file, these two rules are silently skipped. All other rules run automatically with no configuration needed.
127
178
 
128
179
  ---
129
180
 
@@ -157,7 +208,7 @@ Exit code `1` if score exceeds `--min-score`. Exit code `0` otherwise.
157
208
  ```
158
209
  src/
159
210
  ├── types.ts — DriftIssue, FileReport, DriftReport interfaces
160
- ├── analyzer.ts — AST analysis with ts-morph, 8 detection rules
211
+ ├── analyzer.ts — AST analysis with ts-morph, 15 detection rules
161
212
  ├── reporter.ts — buildReport() + Markdown formatter
162
213
  ├── printer.ts — Console output with color (kleur)
163
214
  ├── index.ts — Public API re-exports
@@ -188,12 +239,20 @@ npx @eduardbar/drift scan .
188
239
 
189
240
  ## 🤝 Contributing
190
241
 
191
- PRs are welcome. If you find a new AI code pattern worth detecting, open an issue with an example and we'll add a rule.
242
+ PRs are welcome. See [CONTRIBUTING.md](./CONTRIBUTING.md) for the full guide.
243
+
244
+ **Adding a new detection rule:**
245
+
246
+ 1. Fork the repo and create a branch: `git checkout -b feat/rule-name`
247
+ 2. Add the rule weight to `RULE_WEIGHTS` in `src/analyzer.ts`
248
+ 3. Implement the AST detection logic using ts-morph
249
+ 4. Add a `fix_suggestion` for the rule in `src/printer.ts`
250
+ 5. Update the rules table in `README.md` and `AGENTS.md`
251
+ 6. Open a PR — use the [PR template](./.github/PULL_REQUEST_TEMPLATE.md)
252
+
253
+ 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.
192
254
 
193
- 1. Fork the repo
194
- 2. Create a branch: `git checkout -b feat/rule-name`
195
- 3. Add your rule in `src/analyzer.ts`
196
- 4. Open a PR
255
+ Please read [CODE_OF_CONDUCT.md](./CODE_OF_CONDUCT.md) before participating.
197
256
 
198
257
  ---
199
258
 
package/ROADMAP.md ADDED
@@ -0,0 +1,213 @@
1
+ # Roadmap
2
+
3
+ ## What drift is, and what it's trying to become
4
+
5
+ ESLint tells you if your code is **correct**.
6
+ SonarQube tells you if your code is **complex**.
7
+ drift tells you if your code is **going to kill your codebase in 6 months**.
8
+
9
+ That is a different problem. And no existing tool solves it.
10
+
11
+ The gap became critical in 2024–2025. AI tools let two engineers generate the technical debt of fifty. The code compiles. The tests pass. ESLint is green. And six months later nobody understands it, refactoring has stopped, and the codebase is effectively write-only.
12
+
13
+ > *"You're staring at 847 lines of code you didn't write, don't understand, and can't debug without asking the AI to fix it seventeen times until something sticks."*
14
+ > — Reddit, r/vibecoding
15
+
16
+ > *"39.9% drop in refactoring activity with AI tools. 8x increase in duplicated code blocks."*
17
+ > — GitClear, analysis of 211M lines of code
18
+
19
+ drift's goal is to be the tool that sits between ESLint and SonarQube — lightweight enough to run on every commit, specific enough to catch what neither of them can, and simple enough that a score of 0 actually means something.
20
+
21
+ **What drift is not:**
22
+ - Not an ESLint replacement for style or correctness rules
23
+ - Not a SonarQube replacement for security scanning
24
+ - Not a cloud product, not a SaaS, not freemium
25
+
26
+ **What drift is:**
27
+ - A zero-config static analysis CLI that scores your TypeScript project's structural health
28
+ - A CI gate that blocks debt from accumulating silently
29
+ - A shared language for teams to talk about code quality: "our drift score went from 40 to 18 this sprint"
30
+ - Always free. MIT. No tiers.
31
+
32
+ ---
33
+
34
+ ## Principles that don't change
35
+
36
+ - **Always free for the developer.** MIT. Forever. No paid tier, no cloud lock-in.
37
+ - **Zero config to start.** One command, one number. Config is optional and additive.
38
+ - **Fast.** Results in under 3 seconds on any normal project.
39
+ - **One actionable number, not 400 warnings nobody reads.**
40
+ - **Cross-file analysis.** ESLint sees one file. drift sees the project.
41
+ - **Readable by humans and by LLMs.** `--ai` flag produces structured output for Claude, GPT, Gemini.
42
+
43
+ ---
44
+
45
+ ## What's already done
46
+
47
+ - AST analysis with ts-morph — 10 detection rules
48
+ - Score 0–100 per file and project
49
+ - `--json`, `--ai` (LLM-optimized output), `--fix` (inline suggestions)
50
+ - `--min-score` for CI (exit 1 if score exceeds threshold)
51
+ - `drift-ignore` per line and per file
52
+ - Windows / Linux / macOS compatible via `npx`
53
+
54
+ ---
55
+
56
+ ## What's next
57
+
58
+ Ordered by real developer pain — most common complaint first.
59
+
60
+ ---
61
+
62
+ ### 1. Complexity detection
63
+
64
+ > *"AI generates a nuclear option for a problem that needed a screwdriver."*
65
+ > — Hacker News
66
+
67
+ > *"One of my devs implemented a batching process. He presented extremely robust, high-quality code. The problem was that it was MASSIVE overkill."*
68
+ > — Hacker News, r/ExperiencedDevs
69
+
70
+ ESLint has a `complexity` rule. It measures cyclomatic complexity — the number of branches. That's not the same as cognitive complexity — how hard it is to *understand*. Biome has `noExcessiveCognitiveComplexity`. Neither gives you a score.
71
+
72
+ **Planned rules:**
73
+ - `high-complexity` — cyclomatic complexity > 10 per function
74
+ - `deep-nesting` — nesting depth > 3 levels (if inside if inside for inside try = unreadable)
75
+ - `too-many-params` — functions with more than 4 parameters (AI doesn't refactor into objects)
76
+ - `high-coupling` — files importing more than 10 distinct modules
77
+ - `promise-style-mix` — `async/await` and `.then()` mixed in the same file
78
+
79
+ **Why this matters:** Complexity is the #1 reason codebases become write-only. AI generates correct code. Not simple code.
80
+
81
+ ---
82
+
83
+ ### 2. Cross-file dead code detection
84
+
85
+ > *"After integrating Knip I removed around 3,500 lines of dead code at once."*
86
+ > — dev.to
87
+
88
+ > *"ESLint's architecture works on a file-by-file basis and was never intended to provide linting based on project-wide usage stats."*
89
+ > — typescript-eslint issue #371, marked **wontfix**
90
+
91
+ ESLint detects unused variables *inside a file*. It cannot detect unused exports, unused files, or dead modules across the project. This is a fundamental architectural limitation — not a missing rule.
92
+
93
+ **Planned features:**
94
+ - `unused-export` — exported symbol never imported anywhere in the project
95
+ - `dead-file` — file never imported by anything
96
+ - `unused-dependency` — package in `package.json` with zero imports in the codebase
97
+
98
+ **Why this matters:** Dead code is cognitive overhead. Every unused export is a trap for the next developer. ESLint will never fix this — the `wontfix` label is permanent.
99
+
100
+ ---
101
+
102
+ ### 3. Architectural boundary detection
103
+
104
+ > *"AI coding tools keep breaking architecture — so I built a guard layer."*
105
+ > — Reddit, r/javascript
106
+
107
+ This is the largest unaddressed gap in the ecosystem. ESLint can validate import paths with `no-restricted-imports`. It cannot tell you if your UI layer is importing directly from your database layer, or if your domain logic has dependencies on your HTTP framework.
108
+
109
+ **Planned rules:**
110
+ - `circular-dependency` — module A depends on B which depends on A
111
+ - `layer-violation` — import from a prohibited architectural layer
112
+ - `cross-boundary-import` — module outside its domain importing from another domain
113
+
114
+ Zero config by default — drift infers what it can from the import graph. For teams that want explicit enforcement:
115
+
116
+ ```ts
117
+ // drift.config.ts — optional
118
+ export default {
119
+ boundaries: {
120
+ layers: ['ui', 'domain', 'infrastructure'],
121
+ rules: [{ from: 'ui', allow: ['domain'] }]
122
+ }
123
+ }
124
+ ```
125
+
126
+ **Why this matters:** Architecture violations are invisible until they're catastrophic. AI generates code that works today and breaks your architecture silently.
127
+
128
+ ---
129
+
130
+ ### 4. Historical drift (`drift diff` / `drift trend`)
131
+
132
+ > *"Zero visibility on whether we're actually improving."*
133
+ > — Reddit, r/devsecops
134
+
135
+ The current score matters. The trend matters more. Is your codebase getting better or worse sprint over sprint? Nobody knows — because no tool measures it in a way that's easy to track.
136
+
137
+ **Planned commands:**
138
+ - `drift diff HEAD~N` — what got worse between two commits
139
+ - `drift trend --commits 30` — ASCII chart of score evolution over time
140
+ - `drift blame` — which commits introduced the most debt
141
+
142
+ No database. No server. Git is the source of truth.
143
+
144
+ **Why this matters:** A score of 45 means nothing without context. A score that went from 80 to 45 over 4 sprints means your team is actually improving.
145
+
146
+ ---
147
+
148
+ ### 5. AI authorship heuristics
149
+
150
+ > *"Companies will try to overcome AI-generated technical debt by throwing more AI at the problem."*
151
+ > — Hacker News
152
+
153
+ > *"When 95% of code is projected to be AI-generated by 2030 but 45% of it fails basic security tests, we're building a house of cards."*
154
+ > — Reddit, r/vibecoding
155
+
156
+ The hardest and most differentiated item on this list. Patterns that are statistically more common in AI-generated code than human-written code.
157
+
158
+ **Planned rules:**
159
+ - `over-commented` — comments that describe exactly what the code already says (AI documents the obvious)
160
+ - `unnecessary-abstraction` — class or interface used in exactly one place (AI loves creating things it doesn't use)
161
+ - `hardcoded-config` — strings that look like URLs, tokens, or environment-specific paths hardcoded in logic
162
+ - `inconsistent-error-handling` — different error handling patterns in equivalent functions across the same file
163
+
164
+ **Why this matters:** These patterns don't fail tests. ESLint doesn't catch them. They accumulate until the codebase is unmaintainable.
165
+
166
+ ---
167
+
168
+ ### 6. Static HTML report + README badge
169
+
170
+ > *"It's all disconnected — different dashboards, zero visibility."*
171
+ > — Reddit, r/devsecops
172
+
173
+ No server. No account. No cloud.
174
+
175
+ **Planned features:**
176
+ - `drift report` — single self-contained `drift-report.html`, open in any browser
177
+ - `drift badge` — `badge.svg` with the current score for your README
178
+ - `drift ci` — GitHub Actions annotations on the exact lines with issues (inline in the PR diff)
179
+
180
+ ---
181
+
182
+ ### 7. ESLint plugin
183
+
184
+ Meet developers where they already are.
185
+
186
+ - `eslint-plugin-drift` — exposes drift's rules as standard ESLint rules
187
+ - Compatible with ESLint 9 flat config
188
+ - The CLI remains canonical — the plugin is an integration path for teams already deep in ESLint
189
+
190
+ **Why this matters:** Not everyone will install a new CLI. An ESLint plugin removes all friction and puts drift's rules into a toolchain devs already trust.
191
+
192
+ ---
193
+
194
+ ### 8. Pattern inconsistency detection
195
+
196
+ > *"8x increase in duplicated code blocks with AI tools."*
197
+ > — GitClear
198
+
199
+ AI doesn't reuse — it regenerates. The same logic appears in 4 different forms across the same file.
200
+
201
+ **Planned rules:**
202
+ - `semantic-duplication` — code blocks with equivalent logic detected via AST fingerprinting (not text comparison — that's what grep does)
203
+ - `naming-inconsistency` — mixed naming conventions in the same module (camelCase + snake_case + PascalCase for the same concept)
204
+
205
+ ---
206
+
207
+ ## How to influence this roadmap
208
+
209
+ Open an issue. If you're seeing a pattern that drift doesn't catch, describe it with a code example. Every rule in drift exists because real developers kept finding the same thing in AI-generated code.
210
+
211
+ The roadmap grows from community reports — not from assumptions.
212
+
213
+ If you want to implement one of these, see [CONTRIBUTING.md](./CONTRIBUTING.md).
Binary file
@@ -0,0 +1,120 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 630" width="1200" height="630">
2
+ <defs>
3
+ <linearGradient id="bg" x1="0%" y1="0%" x2="100%" y2="100%">
4
+ <stop offset="0%" style="stop-color:#0a0a0f"/>
5
+ <stop offset="100%" style="stop-color:#0f0f1a"/>
6
+ </linearGradient>
7
+ <linearGradient id="accent" x1="0%" y1="0%" x2="100%" y2="0%">
8
+ <stop offset="0%" style="stop-color:#6366f1"/>
9
+ <stop offset="100%" style="stop-color:#8b5cf6"/>
10
+ </linearGradient>
11
+ <linearGradient id="green-accent" x1="0%" y1="0%" x2="100%" y2="0%">
12
+ <stop offset="0%" style="stop-color:#22c55e"/>
13
+ <stop offset="100%" style="stop-color:#16a34a"/>
14
+ </linearGradient>
15
+ <linearGradient id="glow" x1="0%" y1="0%" x2="100%" y2="100%">
16
+ <stop offset="0%" style="stop-color:#6366f1;stop-opacity:0.15"/>
17
+ <stop offset="100%" style="stop-color:#8b5cf6;stop-opacity:0.05"/>
18
+ </linearGradient>
19
+ <linearGradient id="glow2" x1="0%" y1="0%" x2="100%" y2="100%">
20
+ <stop offset="0%" style="stop-color:#22c55e;stop-opacity:0.10"/>
21
+ <stop offset="100%" style="stop-color:#16a34a;stop-opacity:0.03"/>
22
+ </linearGradient>
23
+ <filter id="blur-glow">
24
+ <feGaussianBlur stdDeviation="40" result="blur"/>
25
+ <feComposite in="SourceGraphic" in2="blur" operator="over"/>
26
+ </filter>
27
+ </defs>
28
+
29
+ <!-- Background -->
30
+ <rect width="1200" height="630" fill="url(#bg)"/>
31
+
32
+ <!-- Ambient glows -->
33
+ <ellipse cx="200" cy="200" rx="300" ry="250" fill="url(#glow)" filter="url(#blur-glow)" opacity="0.8"/>
34
+ <ellipse cx="1050" cy="480" rx="280" ry="220" fill="url(#glow2)" filter="url(#blur-glow)" opacity="0.6"/>
35
+
36
+ <!-- Grid lines subtle -->
37
+ <line x1="0" y1="210" x2="1200" y2="210" stroke="#ffffff" stroke-opacity="0.03" stroke-width="1"/>
38
+ <line x1="0" y1="420" x2="1200" y2="420" stroke="#ffffff" stroke-opacity="0.03" stroke-width="1"/>
39
+ <line x1="300" y1="0" x2="300" y2="630" stroke="#ffffff" stroke-opacity="0.03" stroke-width="1"/>
40
+ <line x1="900" y1="0" x2="900" y2="630" stroke="#ffffff" stroke-opacity="0.03" stroke-width="1"/>
41
+
42
+ <!-- Left accent bar -->
43
+ <rect x="72" y="155" width="4" height="310" rx="2" fill="url(#accent)"/>
44
+
45
+ <!-- Version badge -->
46
+ <rect x="102" y="155" width="105" height="32" rx="6" fill="#1e1e3a" stroke="#6366f1" stroke-opacity="0.6" stroke-width="1.5"/>
47
+ <text x="154" y="176" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif" font-size="14" font-weight="700" fill="#818cf8" text-anchor="middle">v0.3.0</text>
48
+
49
+ <!-- New badge -->
50
+ <rect x="218" y="155" width="80" height="32" rx="6" fill="#14532d" stroke="#22c55e" stroke-opacity="0.6" stroke-width="1.5"/>
51
+ <text x="258" y="176" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif" font-size="14" font-weight="700" fill="#22c55e" text-anchor="middle">NEW</text>
52
+
53
+ <!-- Main title "drift" -->
54
+ <text x="102" y="295" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', monospace" font-size="110" font-weight="700" fill="#ffffff" letter-spacing="-4">drift</text>
55
+
56
+ <!-- Subtitle -->
57
+ <text x="104" y="345" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif" font-size="22" font-weight="400" fill="#94a3b8" letter-spacing="0.5">Detect AI-generated technical debt. Zero config.</text>
58
+
59
+ <!-- Feature pills row -->
60
+ <rect x="104" y="370" width="145" height="30" rx="8" fill="#1a2a1a" stroke="#22c55e" stroke-opacity="0.5" stroke-width="1"/>
61
+ <text x="176" y="390" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif" font-size="13" font-weight="600" fill="#22c55e" text-anchor="middle">--ai LLM output</text>
62
+
63
+ <rect x="260" y="370" width="130" height="30" rx="8" fill="#1a1a2e" stroke="#6366f1" stroke-opacity="0.5" stroke-width="1"/>
64
+ <text x="325" y="390" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif" font-size="13" font-weight="600" fill="#818cf8" text-anchor="middle">--fix suggestions</text>
65
+
66
+ <rect x="401" y="370" width="145" height="30" rx="8" fill="#1a1a2e" stroke="#6366f1" stroke-opacity="0.5" stroke-width="1"/>
67
+ <text x="473" y="390" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif" font-size="13" font-weight="600" fill="#818cf8" text-anchor="middle">drift-ignore</text>
68
+
69
+ <rect x="557" y="370" width="140" height="30" rx="8" fill="#1a1a2e" stroke="#6366f1" stroke-opacity="0.5" stroke-width="1"/>
70
+ <text x="627" y="390" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif" font-size="13" font-weight="600" fill="#818cf8" text-anchor="middle">--min-score CI</text>
71
+
72
+ <!-- Terminal block right side -->
73
+ <rect x="660" y="140" width="468" height="335" rx="12" fill="#0d0d18" stroke="#ffffff" stroke-opacity="0.08" stroke-width="1"/>
74
+
75
+ <!-- Terminal header bar -->
76
+ <rect x="660" y="140" width="468" height="40" rx="12" fill="#1a1a2e"/>
77
+ <rect x="660" y="160" width="468" height="20" fill="#1a1a2e"/>
78
+
79
+ <!-- Terminal dots -->
80
+ <circle cx="690" cy="160" r="6" fill="#ff5f57"/>
81
+ <circle cx="710" cy="160" r="6" fill="#febc2e"/>
82
+ <circle cx="730" cy="160" r="6" fill="#28c840"/>
83
+
84
+ <text x="894" y="165" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif" font-size="11" fill="#475569" text-anchor="middle">drift</text>
85
+
86
+ <text x="680" y="210" font-family="'Cascadia Code', 'Fira Code', 'Consolas', monospace" font-size="12" fill="#6366f1">$</text>
87
+ <text x="695" y="210" font-family="'Cascadia Code', 'Fira Code', 'Consolas', monospace" font-size="12" fill="#e2e8f0"> drift scan ./src --ai | pbcopy</text>
88
+
89
+ <text x="680" y="238" font-family="'Cascadia Code', 'Fira Code', 'Consolas', monospace" font-size="11" fill="#475569"> {</text>
90
+ <text x="680" y="256" font-family="'Cascadia Code', 'Fira Code', 'Consolas', monospace" font-size="11" fill="#475569"> "summary": {</text>
91
+ <text x="680" y="274" font-family="'Cascadia Code', 'Fira Code', 'Consolas', monospace" font-size="11" fill="#94a3b8"> "score": </text>
92
+ <text x="775" y="274" font-family="'Cascadia Code', 'Fira Code', 'Consolas', monospace" font-size="11" font-weight="700" fill="#ef4444">52</text>
93
+ <text x="792" y="274" font-family="'Cascadia Code', 'Fira Code', 'Consolas', monospace" font-size="11" fill="#94a3b8">, "grade": </text>
94
+ <text x="863" y="274" font-family="'Cascadia Code', 'Fira Code', 'Consolas', monospace" font-size="11" fill="#eab308">"HIGH"</text>
95
+
96
+ <text x="680" y="292" font-family="'Cascadia Code', 'Fira Code', 'Consolas', monospace" font-size="11" fill="#475569"> },</text>
97
+ <text x="680" y="310" font-family="'Cascadia Code', 'Fira Code', 'Consolas', monospace" font-size="11" fill="#475569"> "priority_order": [</text>
98
+ <text x="680" y="328" font-family="'Cascadia Code', 'Fira Code', 'Consolas', monospace" font-size="11" fill="#475569"> { "rank": 1, "rule": </text>
99
+ <text x="844" y="328" font-family="'Cascadia Code', 'Fira Code', 'Consolas', monospace" font-size="11" fill="#22c55e">"debug-leftover"</text>
100
+ <text x="956" y="328" font-family="'Cascadia Code', 'Fira Code', 'Consolas', monospace" font-size="11" fill="#475569">,</text>
101
+
102
+ <text x="680" y="346" font-family="'Cascadia Code', 'Fira Code', 'Consolas', monospace" font-size="11" fill="#475569"> "effort": </text>
103
+ <text x="775" y="346" font-family="'Cascadia Code', 'Fira Code', 'Consolas', monospace" font-size="11" fill="#22c55e">"low"</text>
104
+ <text x="810" y="346" font-family="'Cascadia Code', 'Fira Code', 'Consolas', monospace" font-size="11" fill="#94a3b8">, "fix_suggestion": "..."</text>
105
+
106
+ <text x="680" y="364" font-family="'Cascadia Code', 'Fira Code', 'Consolas', monospace" font-size="11" fill="#475569"> ],</text>
107
+ <text x="680" y="382" font-family="'Cascadia Code', 'Fira Code', 'Consolas', monospace" font-size="11" fill="#475569"> "recommended_action": </text>
108
+ <text x="680" y="400" font-family="'Cascadia Code', 'Fira Code', 'Consolas', monospace" font-size="11" fill="#818cf8"> "Fix 3 low-effort issues first."</text>
109
+
110
+ <text x="680" y="418" font-family="'Cascadia Code', 'Fira Code', 'Consolas', monospace" font-size="11" fill="#475569"> }</text>
111
+ <text x="680" y="436" font-family="'Cascadia Code', 'Fira Code', 'Consolas', monospace" font-size="11" fill="#64748b"> ↳ paste into Claude/GPT as context</text>
112
+ <text x="680" y="454" font-family="'Cascadia Code', 'Fira Code', 'Consolas', monospace" font-size="11" fill="#22c55e"> ✓ prioritized by severity + effort</text>
113
+
114
+ <!-- Author -->
115
+ <text x="104" y="500" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif" font-size="15" fill="#334155">npx @eduardbar/drift scan . --ai</text>
116
+ <text x="104" y="525" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif" font-size="14" fill="#475569">github.com/eduardbar/drift · MIT · TypeScript · ts-morph</text>
117
+
118
+ <!-- Bottom accent line -->
119
+ <rect x="0" y="622" width="1200" height="8" fill="url(#accent)" opacity="0.7"/>
120
+ </svg>
Binary file
@@ -0,0 +1,94 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 675" width="1200" height="675">
2
+ <defs>
3
+ <linearGradient id="bg" x1="0%" y1="0%" x2="100%" y2="100%">
4
+ <stop offset="0%" style="stop-color:#0a0a0f"/>
5
+ <stop offset="100%" style="stop-color:#0f0f1a"/>
6
+ </linearGradient>
7
+ <linearGradient id="accent" x1="0%" y1="0%" x2="100%" y2="0%">
8
+ <stop offset="0%" style="stop-color:#6366f1"/>
9
+ <stop offset="100%" style="stop-color:#8b5cf6"/>
10
+ </linearGradient>
11
+ <linearGradient id="green-bar" x1="0%" y1="0%" x2="100%" y2="0%">
12
+ <stop offset="0%" style="stop-color:#22c55e"/>
13
+ <stop offset="100%" style="stop-color:#16a34a"/>
14
+ </linearGradient>
15
+ <linearGradient id="red-bar" x1="0%" y1="0%" x2="100%" y2="0%">
16
+ <stop offset="0%" style="stop-color:#ef4444"/>
17
+ <stop offset="100%" style="stop-color:#dc2626"/>
18
+ </linearGradient>
19
+ <linearGradient id="glow" x1="0%" y1="0%" x2="100%" y2="100%">
20
+ <stop offset="0%" style="stop-color:#6366f1;stop-opacity:0.15"/>
21
+ <stop offset="100%" style="stop-color:#8b5cf6;stop-opacity:0.05"/>
22
+ </linearGradient>
23
+ <filter id="blur-glow">
24
+ <feGaussianBlur stdDeviation="50" result="blur"/>
25
+ <feComposite in="SourceGraphic" in2="blur" operator="over"/>
26
+ </filter>
27
+ </defs>
28
+
29
+ <rect width="1200" height="675" fill="url(#bg)"/>
30
+ <ellipse cx="600" cy="200" rx="500" ry="200" fill="url(#glow)" filter="url(#blur-glow)" opacity="0.6"/>
31
+
32
+ <!-- Grid lines -->
33
+ <line x1="0" y1="225" x2="1200" y2="225" stroke="#ffffff" stroke-opacity="0.025" stroke-width="1"/>
34
+ <line x1="0" y1="450" x2="1200" y2="450" stroke="#ffffff" stroke-opacity="0.025" stroke-width="1"/>
35
+ <line x1="400" y1="0" x2="400" y2="675" stroke="#ffffff" stroke-opacity="0.025" stroke-width="1"/>
36
+ <line x1="800" y1="0" x2="800" y2="675" stroke="#ffffff" stroke-opacity="0.025" stroke-width="1"/>
37
+
38
+ <!-- Top accent bar -->
39
+ <rect x="0" y="0" width="1200" height="5" fill="url(#accent)" opacity="0.8"/>
40
+
41
+ <!-- drift title centered -->
42
+ <text x="600" y="120" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', monospace" font-size="90" font-weight="700" fill="#ffffff" letter-spacing="-3" text-anchor="middle">drift</text>
43
+
44
+ <!-- Version tag -->
45
+ <rect x="540" y="132" width="120" height="28" rx="14" fill="#1e1e3a" stroke="#6366f1" stroke-opacity="0.7" stroke-width="1.5"/>
46
+ <text x="600" y="151" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif" font-size="14" font-weight="700" fill="#818cf8" text-anchor="middle">v0.3.0</text>
47
+
48
+ <!-- Tagline -->
49
+ <text x="600" y="195" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif" font-size="20" fill="#94a3b8" text-anchor="middle">Measure AI-generated debt. One command.</text>
50
+
51
+ <!-- 3 score cards -->
52
+ <!-- Card 1: shadcn/ui CLI — Score 0 -->
53
+ <rect x="68" y="225" width="330" height="210" rx="12" fill="#0d0d18" stroke="#22c55e" stroke-opacity="0.3" stroke-width="1"/>
54
+ <text x="233" y="258" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif" font-size="13" fill="#64748b" text-anchor="middle">shadcn/ui — CLI source</text>
55
+ <text x="233" y="320" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', monospace" font-size="72" font-weight="700" fill="#22c55e" text-anchor="middle">0</text>
56
+ <text x="233" y="348" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif" font-size="12" fill="#22c55e" text-anchor="middle">CLEAN</text>
57
+ <!-- mini score bar full green -->
58
+ <rect x="100" y="368" width="266" height="6" rx="3" fill="#1e2a1e"/>
59
+ <rect x="100" y="368" width="5" height="6" rx="3" fill="url(#green-bar)"/>
60
+ <text x="233" y="395" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif" font-size="11" fill="#475569" text-anchor="middle">production-grade code</text>
61
+ <text x="233" y="413" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif" font-size="11" fill="#475569" text-anchor="middle">0 issues · 0 warnings</text>
62
+
63
+ <!-- Card 2: Vibe-coded module — Score 52 -->
64
+ <rect x="435" y="225" width="330" height="210" rx="12" fill="#0d0d18" stroke="#eab308" stroke-opacity="0.3" stroke-width="1"/>
65
+ <text x="600" y="258" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif" font-size="13" fill="#64748b" text-anchor="middle">vibe-coded module</text>
66
+ <text x="600" y="320" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', monospace" font-size="72" font-weight="700" fill="#eab308" text-anchor="middle">52</text>
67
+ <text x="600" y="348" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif" font-size="12" fill="#eab308" text-anchor="middle">HIGH</text>
68
+ <!-- mini score bar 52% -->
69
+ <rect x="467" y="368" width="266" height="6" rx="3" fill="#1e1e1e"/>
70
+ <rect x="467" y="368" width="138" height="6" rx="3" fill="#eab308"/>
71
+ <text x="600" y="395" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif" font-size="11" fill="#475569" text-anchor="middle">review before shipping</text>
72
+ <text x="600" y="413" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif" font-size="11" fill="#475569" text-anchor="middle">3 errors · 8 warnings</text>
73
+
74
+ <!-- Card 3: Full vibe dump — Score 89 -->
75
+ <rect x="802" y="225" width="330" height="210" rx="12" fill="#0d0d18" stroke="#ef4444" stroke-opacity="0.3" stroke-width="1"/>
76
+ <text x="967" y="258" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif" font-size="13" fill="#64748b" text-anchor="middle">full vibe-coding dump</text>
77
+ <text x="967" y="320" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', monospace" font-size="72" font-weight="700" fill="#ef4444" text-anchor="middle">89</text>
78
+ <text x="967" y="348" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif" font-size="12" fill="#ef4444" text-anchor="middle">CRITICAL</text>
79
+ <!-- mini score bar 89% -->
80
+ <rect x="834" y="368" width="266" height="6" rx="3" fill="#1e1e1e"/>
81
+ <rect x="834" y="368" width="237" height="6" rx="3" fill="url(#red-bar)"/>
82
+ <text x="967" y="395" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif" font-size="11" fill="#475569" text-anchor="middle">rewrite before anyone sees it</text>
83
+ <text x="967" y="413" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif" font-size="11" fill="#475569" text-anchor="middle">7 errors · 14 warnings</text>
84
+
85
+ <!-- Bottom CTA -->
86
+ <text x="600" y="480" font-family="'Cascadia Code', 'Fira Code', 'Consolas', monospace" font-size="18" fill="#6366f1" text-anchor="middle">npx @eduardbar/drift scan .</text>
87
+
88
+ <text x="600" y="520" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif" font-size="14" fill="#475569" text-anchor="middle">NEW in v0.3.0: --ai · --fix · drift-ignore · CI-ready · Windows compatible</text>
89
+
90
+ <text x="600" y="560" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif" font-size="14" fill="#334155" text-anchor="middle">github.com/eduardbar/drift · MIT Open Source</text>
91
+
92
+ <!-- Bottom accent line -->
93
+ <rect x="0" y="667" width="1200" height="8" fill="url(#accent)" opacity="0.7"/>
94
+ </svg>