@eduardbar/drift 0.8.0 → 0.9.1

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/src/printer.ts CHANGED
@@ -118,66 +118,66 @@ function formatFixSuggestion(issue: DriftIssue): string[] {
118
118
  }
119
119
 
120
120
  export function printConsole(report: DriftReport, options?: { showFix?: boolean }): void {
121
- const sep = kleur.gray(' ' + '─'.repeat(50))
122
-
123
- console.log()
124
- console.log(kleur.bold().white(' drift') + kleur.gray(' — vibe coding debt detector'))
125
- console.log(sep)
126
- console.log()
127
-
128
- const grade = scoreToGrade(report.totalScore)
129
- const scoreColor = report.totalScore === 0
130
- ? kleur.green
131
- : report.totalScore < 45
132
- ? kleur.yellow
133
- : kleur.red
134
-
135
- const bar = scoreBar(report.totalScore)
136
- console.log(
137
- ` Score ${kleur.gray(bar)} ${scoreColor().bold(String(report.totalScore))}/100 ${grade.badge}`
138
- )
139
-
140
- const cleanFiles = report.totalFiles - report.files.length
141
- console.log(
142
- kleur.gray(
143
- ` ${report.files.length} file(s) with issues · ${report.summary.errors} errors · ${report.summary.warnings} warnings · ${report.summary.infos} info · ${cleanFiles} files clean`
144
- )
145
- )
146
- console.log()
147
-
148
- // Top issues in header
149
- const topRules = Object.entries(report.summary.byRule).sort((a, b) => b[1] - a[1]).slice(0, 3)
150
- if (topRules.length > 0) {
151
- const parts = topRules.map(([rule, count]) => `${kleur.cyan(rule)} ${kleur.gray(`×${count}`)}`)
152
- console.log(` Top issues: ${parts.join(kleur.gray(' · '))}`)
153
- console.log()
154
- }
155
-
156
- console.log(sep)
157
- console.log()
158
-
159
- if (report.files.length === 0) {
160
- console.log(kleur.green(' No drift detected. Clean codebase.'))
161
- console.log()
162
- return
163
- }
164
-
165
- for (const file of report.files) {
166
- const rel = file.path.replace(report.targetPath, '').replace(/^[\\/]/, '')
167
- console.log(
168
- kleur.bold().white(` ${rel}`) +
169
- kleur.gray(` (score ${file.score}/100)`)
170
- )
171
-
172
- for (const issue of file.issues) {
173
- const icon = severityIcon(issue.severity)
174
- const colorFn = (s: string) =>
175
- issue.severity === 'error'
176
- ? kleur.red(s)
177
- : issue.severity === 'warning'
178
- ? kleur.yellow(s)
179
- : kleur.cyan(s)
180
-
121
+ const sep = kleur.gray(' ' + '─'.repeat(50))
122
+
123
+ console.log()
124
+ console.log(kleur.bold().white(' drift') + kleur.gray(' — vibe coding debt detector'))
125
+ console.log(sep)
126
+ console.log()
127
+
128
+ const grade = scoreToGrade(report.totalScore)
129
+ const scoreColor = report.totalScore === 0
130
+ ? kleur.green
131
+ : report.totalScore < 45
132
+ ? kleur.yellow
133
+ : kleur.red
134
+
135
+ const bar = scoreBar(report.totalScore)
136
+ console.log(
137
+ ` Score ${kleur.gray(bar)} ${scoreColor().bold(String(report.totalScore))}/100 ${grade.badge}`
138
+ )
139
+
140
+ const cleanFiles = report.totalFiles - report.files.length
141
+ console.log(
142
+ kleur.gray(
143
+ ` ${report.files.length} file(s) with issues · ${report.summary.errors} errors · ${report.summary.warnings} warnings · ${report.summary.infos} info · ${cleanFiles} files clean`
144
+ )
145
+ )
146
+ console.log()
147
+
148
+ // Top issues in header
149
+ const topRules = Object.entries(report.summary.byRule).sort((a, b) => b[1] - a[1]).slice(0, 3)
150
+ if (topRules.length > 0) {
151
+ const parts = topRules.map(([rule, count]) => `${kleur.cyan(rule)} ${kleur.gray(`×${count}`)}`)
152
+ console.log(` Top issues: ${parts.join(kleur.gray(' · '))}`)
153
+ console.log()
154
+ }
155
+
156
+ console.log(sep)
157
+ console.log()
158
+
159
+ if (report.files.length === 0) {
160
+ console.log(kleur.green(' No drift detected. Clean codebase.'))
161
+ console.log()
162
+ return
163
+ }
164
+
165
+ for (const file of report.files) {
166
+ const rel = file.path.replace(report.targetPath, '').replace(/^[\\/]/, '')
167
+ console.log(
168
+ kleur.bold().white(` ${rel}`) +
169
+ kleur.gray(` (score ${file.score}/100)`)
170
+ )
171
+
172
+ for (const issue of file.issues) {
173
+ const icon = severityIcon(issue.severity)
174
+ const colorFn = (s: string) =>
175
+ issue.severity === 'error'
176
+ ? kleur.red(s)
177
+ : issue.severity === 'warning'
178
+ ? kleur.yellow(s)
179
+ : kleur.cyan(s)
180
+
181
181
  console.log(
182
182
  ` ${colorFn(icon)} ` +
183
183
  kleur.gray(`L${issue.line}`) +
package/src/report.ts CHANGED
@@ -1,7 +1,9 @@
1
1
  import { basename } from 'node:path'
2
+ import { createRequire } from 'node:module'
2
3
  import { DriftReport, DriftIssue } from './types.js'
3
4
 
4
- const VERSION = '0.6.0'
5
+ const require = createRequire(import.meta.url)
6
+ const { version: VERSION } = require('../package.json') as { version: string }
5
7
 
6
8
  function severityColor(severity: DriftIssue['severity']): string {
7
9
  switch (severity) {
package/src/types.ts CHANGED
@@ -1,18 +1,18 @@
1
- export interface DriftIssue {
2
- rule: string
3
- severity: 'error' | 'warning' | 'info'
4
- message: string
5
- line: number
6
- column: number
7
- snippet: string
8
- }
9
-
10
- export interface FileReport {
11
- path: string
12
- issues: DriftIssue[]
13
- score: number // 0–100, higher = more drift
14
- }
15
-
1
+ export interface DriftIssue {
2
+ rule: string
3
+ severity: 'error' | 'warning' | 'info'
4
+ message: string
5
+ line: number
6
+ column: number
7
+ snippet: string
8
+ }
9
+
10
+ export interface FileReport {
11
+ path: string
12
+ issues: DriftIssue[]
13
+ score: number // 0–100, higher = more drift
14
+ }
15
+
16
16
  export interface DriftReport {
17
17
  scannedAt: string
18
18
  targetPath: string
@@ -112,3 +112,47 @@ export interface DriftDiff {
112
112
  newIssuesCount: number
113
113
  resolvedIssuesCount: number
114
114
  }
115
+
116
+ /** Historical analysis data for a single commit */
117
+ export interface HistoricalAnalysis {
118
+ commitHash: string;
119
+ commitDate: Date;
120
+ author: string;
121
+ message: string;
122
+ files: FileReport[];
123
+ totalScore: number;
124
+ averageScore: number;
125
+ }
126
+
127
+ /** Trend data point for score evolution */
128
+ export interface TrendDataPoint {
129
+ date: Date;
130
+ score: number;
131
+ fileCount: number;
132
+ avgIssuesPerFile: number;
133
+ }
134
+
135
+ /** Blame attribution data */
136
+ export interface BlameAttribution {
137
+ author: string;
138
+ email: string;
139
+ commits: number;
140
+ linesChanged: number;
141
+ issuesIntroduced: number;
142
+ avgScoreImpact: number;
143
+ }
144
+
145
+ /** Extended DriftReport with historical context */
146
+ export interface DriftTrendReport extends DriftReport {
147
+ trend: TrendDataPoint[];
148
+ regression: {
149
+ slope: number;
150
+ intercept: number;
151
+ r2: number;
152
+ };
153
+ }
154
+
155
+ /** Extended DriftReport with blame data */
156
+ export interface DriftBlameReport extends DriftReport {
157
+ blame: BlameAttribution[];
158
+ }
package/src/utils.ts CHANGED
@@ -1,35 +1,35 @@
1
- import kleur from 'kleur'
2
- import type { DriftIssue } from './types.js'
3
-
4
- export interface Grade {
5
- badge: string
6
- label: string
7
- }
8
-
9
- export function scoreToGrade(score: number): Grade {
10
- if (score === 0) return { badge: kleur.green('CLEAN'), label: 'clean' }
11
- if (score < 20) return { badge: kleur.green('LOW'), label: 'low' }
12
- if (score < 45) return { badge: kleur.yellow('MODERATE'), label: 'moderate' }
13
- if (score < 70) return { badge: kleur.red('HIGH'), label: 'high' }
14
- return { badge: kleur.bold().red('CRITICAL'), label: 'critical' }
15
- }
16
-
17
- export function scoreToGradeText(score: number): Grade {
18
- if (score === 0) return { badge: '✦ CLEAN', label: 'clean' }
19
- if (score < 20) return { badge: '◎ LOW', label: 'low' }
20
- if (score < 45) return { badge: '◈ MODERATE', label: 'moderate' }
21
- if (score < 70) return { badge: '◉ HIGH', label: 'high' }
22
- return { badge: '⬡ CRITICAL', label: 'critical' }
23
- }
24
-
25
- export function severityIcon(s: DriftIssue['severity']): string {
26
- if (s === 'error') return '✖'
27
- if (s === 'warning') return '▲'
28
- return '◦'
29
- }
30
-
31
- export function scoreBar(score: number, width = 20): string {
32
- const filled = Math.round((score / 100) * width)
33
- const empty = width - filled
34
- return '█'.repeat(filled) + '░'.repeat(empty)
35
- }
1
+ import kleur from 'kleur'
2
+ import type { DriftIssue } from './types.js'
3
+
4
+ export interface Grade {
5
+ badge: string
6
+ label: string
7
+ }
8
+
9
+ export function scoreToGrade(score: number): Grade {
10
+ if (score === 0) return { badge: kleur.green('CLEAN'), label: 'clean' }
11
+ if (score < 20) return { badge: kleur.green('LOW'), label: 'low' }
12
+ if (score < 45) return { badge: kleur.yellow('MODERATE'), label: 'moderate' }
13
+ if (score < 70) return { badge: kleur.red('HIGH'), label: 'high' }
14
+ return { badge: kleur.bold().red('CRITICAL'), label: 'critical' }
15
+ }
16
+
17
+ export function scoreToGradeText(score: number): Grade {
18
+ if (score === 0) return { badge: '✦ CLEAN', label: 'clean' }
19
+ if (score < 20) return { badge: '◎ LOW', label: 'low' }
20
+ if (score < 45) return { badge: '◈ MODERATE', label: 'moderate' }
21
+ if (score < 70) return { badge: '◉ HIGH', label: 'high' }
22
+ return { badge: '⬡ CRITICAL', label: 'critical' }
23
+ }
24
+
25
+ export function severityIcon(s: DriftIssue['severity']): string {
26
+ if (s === 'error') return '✖'
27
+ if (s === 'warning') return '▲'
28
+ return '◦'
29
+ }
30
+
31
+ export function scoreBar(score: number, width = 20): string {
32
+ const filled = Math.round((score / 100) * width)
33
+ const empty = width - filled
34
+ return '█'.repeat(filled) + '░'.repeat(empty)
35
+ }