@eduardbar/drift 1.2.0 → 1.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.
Files changed (195) hide show
  1. package/.gga +50 -0
  2. package/.github/actions/drift-review/README.md +60 -0
  3. package/.github/actions/drift-review/action.yml +131 -0
  4. package/.github/actions/drift-scan/README.md +28 -32
  5. package/.github/actions/drift-scan/action.yml +78 -14
  6. package/.github/workflows/publish-vscode.yml +3 -3
  7. package/.github/workflows/publish.yml +3 -3
  8. package/.github/workflows/review-pr.yml +94 -9
  9. package/AGENTS.md +75 -245
  10. package/CHANGELOG.md +28 -0
  11. package/README.md +308 -51
  12. package/ROADMAP.md +6 -5
  13. package/dist/analyzer.d.ts +2 -2
  14. package/dist/analyzer.js +420 -159
  15. package/dist/benchmark.d.ts +2 -0
  16. package/dist/benchmark.js +204 -0
  17. package/dist/cli.js +693 -67
  18. package/dist/config.js +16 -2
  19. package/dist/diff.js +66 -10
  20. package/dist/doctor.d.ts +5 -0
  21. package/dist/doctor.js +133 -0
  22. package/dist/format.d.ts +17 -0
  23. package/dist/format.js +45 -0
  24. package/dist/git.js +12 -0
  25. package/dist/guard-types.d.ts +57 -0
  26. package/dist/guard-types.js +2 -0
  27. package/dist/guard.d.ts +14 -0
  28. package/dist/guard.js +239 -0
  29. package/dist/index.d.ts +12 -3
  30. package/dist/index.js +6 -1
  31. package/dist/init.d.ts +15 -0
  32. package/dist/init.js +273 -0
  33. package/dist/map-cycles.d.ts +2 -0
  34. package/dist/map-cycles.js +34 -0
  35. package/dist/map-svg.d.ts +19 -0
  36. package/dist/map-svg.js +97 -0
  37. package/dist/map.js +78 -138
  38. package/dist/metrics.js +70 -55
  39. package/dist/output-metadata.d.ts +13 -0
  40. package/dist/output-metadata.js +17 -0
  41. package/dist/plugins-capabilities.d.ts +4 -0
  42. package/dist/plugins-capabilities.js +21 -0
  43. package/dist/plugins-messages.d.ts +10 -0
  44. package/dist/plugins-messages.js +16 -0
  45. package/dist/plugins-rules.d.ts +9 -0
  46. package/dist/plugins-rules.js +137 -0
  47. package/dist/plugins.d.ts +2 -1
  48. package/dist/plugins.js +80 -28
  49. package/dist/printer.js +4 -0
  50. package/dist/reporter-constants.d.ts +16 -0
  51. package/dist/reporter-constants.js +39 -0
  52. package/dist/reporter.d.ts +3 -3
  53. package/dist/reporter.js +35 -55
  54. package/dist/review.d.ts +2 -1
  55. package/dist/review.js +4 -3
  56. package/dist/rules/comments.js +2 -2
  57. package/dist/rules/complexity.js +2 -7
  58. package/dist/rules/nesting.js +3 -13
  59. package/dist/rules/phase0-basic.js +10 -10
  60. package/dist/rules/phase3-configurable.js +23 -15
  61. package/dist/rules/shared.d.ts +2 -0
  62. package/dist/rules/shared.js +27 -3
  63. package/dist/saas/constants.d.ts +15 -0
  64. package/dist/saas/constants.js +48 -0
  65. package/dist/saas/dashboard.d.ts +8 -0
  66. package/dist/saas/dashboard.js +132 -0
  67. package/dist/saas/errors.d.ts +19 -0
  68. package/dist/saas/errors.js +37 -0
  69. package/dist/saas/helpers.d.ts +21 -0
  70. package/dist/saas/helpers.js +110 -0
  71. package/dist/saas/ingest.d.ts +3 -0
  72. package/dist/saas/ingest.js +249 -0
  73. package/dist/saas/organization.d.ts +5 -0
  74. package/dist/saas/organization.js +82 -0
  75. package/dist/saas/plan-change.d.ts +10 -0
  76. package/dist/saas/plan-change.js +15 -0
  77. package/dist/saas/store.d.ts +21 -0
  78. package/dist/saas/store.js +159 -0
  79. package/dist/saas/types.d.ts +191 -0
  80. package/dist/saas/types.js +2 -0
  81. package/dist/saas.d.ts +8 -82
  82. package/dist/saas.js +7 -320
  83. package/dist/sarif.d.ts +74 -0
  84. package/dist/sarif.js +122 -0
  85. package/dist/trust-advanced.d.ts +14 -0
  86. package/dist/trust-advanced.js +65 -0
  87. package/dist/trust-kpi-fs.d.ts +3 -0
  88. package/dist/trust-kpi-fs.js +141 -0
  89. package/dist/trust-kpi-parse.d.ts +7 -0
  90. package/dist/trust-kpi-parse.js +186 -0
  91. package/dist/trust-kpi-types.d.ts +16 -0
  92. package/dist/trust-kpi-types.js +2 -0
  93. package/dist/trust-kpi.d.ts +7 -0
  94. package/dist/trust-kpi.js +185 -0
  95. package/dist/trust-policy.d.ts +32 -0
  96. package/dist/trust-policy.js +160 -0
  97. package/dist/trust-render.d.ts +9 -0
  98. package/dist/trust-render.js +54 -0
  99. package/dist/trust-scoring.d.ts +9 -0
  100. package/dist/trust-scoring.js +208 -0
  101. package/dist/trust.d.ts +37 -0
  102. package/dist/trust.js +168 -0
  103. package/dist/types/app.d.ts +30 -0
  104. package/dist/types/app.js +2 -0
  105. package/dist/types/config.d.ts +25 -0
  106. package/dist/types/config.js +2 -0
  107. package/dist/types/core.d.ts +100 -0
  108. package/dist/types/core.js +2 -0
  109. package/dist/types/diff.d.ts +55 -0
  110. package/dist/types/diff.js +2 -0
  111. package/dist/types/plugin.d.ts +41 -0
  112. package/dist/types/plugin.js +2 -0
  113. package/dist/types/trust.d.ts +120 -0
  114. package/dist/types/trust.js +2 -0
  115. package/dist/types.d.ts +8 -211
  116. package/docs/PRD.md +187 -109
  117. package/docs/plugin-contract.md +61 -0
  118. package/docs/release-notes-draft.md +40 -0
  119. package/docs/rules-catalog.md +49 -0
  120. package/docs/trust-core-release-checklist.md +87 -0
  121. package/package.json +6 -3
  122. package/packages/vscode-drift/src/code-actions.ts +1 -1
  123. package/schemas/drift-ai-output.v1.json +162 -0
  124. package/schemas/drift-report.v1.json +151 -0
  125. package/schemas/drift-trust.v1.json +131 -0
  126. package/scripts/smoke-repo.mjs +394 -0
  127. package/src/analyzer.ts +484 -155
  128. package/src/benchmark.ts +266 -0
  129. package/src/cli.ts +840 -85
  130. package/src/config.ts +19 -2
  131. package/src/diff.ts +84 -10
  132. package/src/doctor.ts +173 -0
  133. package/src/format.ts +81 -0
  134. package/src/git.ts +16 -0
  135. package/src/guard-types.ts +64 -0
  136. package/src/guard.ts +324 -0
  137. package/src/index.ts +83 -0
  138. package/src/init.ts +298 -0
  139. package/src/map-cycles.ts +38 -0
  140. package/src/map-svg.ts +124 -0
  141. package/src/map.ts +111 -142
  142. package/src/metrics.ts +78 -59
  143. package/src/output-metadata.ts +30 -0
  144. package/src/plugins-capabilities.ts +36 -0
  145. package/src/plugins-messages.ts +35 -0
  146. package/src/plugins-rules.ts +296 -0
  147. package/src/plugins.ts +148 -27
  148. package/src/printer.ts +4 -0
  149. package/src/reporter-constants.ts +46 -0
  150. package/src/reporter.ts +64 -65
  151. package/src/review.ts +6 -4
  152. package/src/rules/comments.ts +2 -2
  153. package/src/rules/complexity.ts +2 -7
  154. package/src/rules/nesting.ts +3 -13
  155. package/src/rules/phase0-basic.ts +11 -12
  156. package/src/rules/phase3-configurable.ts +39 -26
  157. package/src/rules/shared.ts +31 -3
  158. package/src/saas/constants.ts +56 -0
  159. package/src/saas/dashboard.ts +172 -0
  160. package/src/saas/errors.ts +45 -0
  161. package/src/saas/helpers.ts +140 -0
  162. package/src/saas/ingest.ts +278 -0
  163. package/src/saas/organization.ts +99 -0
  164. package/src/saas/plan-change.ts +19 -0
  165. package/src/saas/store.ts +172 -0
  166. package/src/saas/types.ts +216 -0
  167. package/src/saas.ts +49 -433
  168. package/src/sarif.ts +232 -0
  169. package/src/trust-advanced.ts +99 -0
  170. package/src/trust-kpi-fs.ts +169 -0
  171. package/src/trust-kpi-parse.ts +219 -0
  172. package/src/trust-kpi-types.ts +19 -0
  173. package/src/trust-kpi.ts +210 -0
  174. package/src/trust-policy.ts +246 -0
  175. package/src/trust-render.ts +61 -0
  176. package/src/trust-scoring.ts +231 -0
  177. package/src/trust.ts +260 -0
  178. package/src/types/app.ts +30 -0
  179. package/src/types/config.ts +27 -0
  180. package/src/types/core.ts +105 -0
  181. package/src/types/diff.ts +61 -0
  182. package/src/types/plugin.ts +46 -0
  183. package/src/types/trust.ts +134 -0
  184. package/src/types.ts +78 -238
  185. package/tests/cli-sarif.test.ts +92 -0
  186. package/tests/diff.test.ts +124 -0
  187. package/tests/format.test.ts +157 -0
  188. package/tests/new-features.test.ts +80 -1
  189. package/tests/phase1-init-doctor-guard.test.ts +199 -0
  190. package/tests/plugins.test.ts +219 -0
  191. package/tests/rules.test.ts +23 -1
  192. package/tests/saas-foundation.test.ts +358 -1
  193. package/tests/sarif.test.ts +160 -0
  194. package/tests/trust-kpi.test.ts +147 -0
  195. package/tests/trust.test.ts +602 -0
@@ -0,0 +1,61 @@
1
+ import type { DriftIssue, DriftReport, FileReport } from './core.js'
2
+
3
+ export interface FileDiff {
4
+ path: string
5
+ scoreBefore: number
6
+ scoreAfter: number
7
+ scoreDelta: number
8
+ newIssues: DriftIssue[]
9
+ resolvedIssues: DriftIssue[]
10
+ }
11
+
12
+ export interface DriftDiff {
13
+ baseRef: string
14
+ projectPath: string
15
+ scannedAt: string
16
+ files: FileDiff[]
17
+ totalScoreBefore: number
18
+ totalScoreAfter: number
19
+ totalDelta: number
20
+ newIssuesCount: number
21
+ resolvedIssuesCount: number
22
+ }
23
+
24
+ export interface HistoricalAnalysis {
25
+ commitHash: string
26
+ commitDate: Date
27
+ author: string
28
+ message: string
29
+ files: FileReport[]
30
+ totalScore: number
31
+ averageScore: number
32
+ }
33
+
34
+ export interface TrendDataPoint {
35
+ date: Date
36
+ score: number
37
+ fileCount: number
38
+ avgIssuesPerFile: number
39
+ }
40
+
41
+ export interface BlameAttribution {
42
+ author: string
43
+ email: string
44
+ commits: number
45
+ linesChanged: number
46
+ issuesIntroduced: number
47
+ avgScoreImpact: number
48
+ }
49
+
50
+ export interface DriftTrendReport extends DriftReport {
51
+ trend: TrendDataPoint[]
52
+ regression: {
53
+ slope: number
54
+ intercept: number
55
+ r2: number
56
+ }
57
+ }
58
+
59
+ export interface DriftBlameReport extends DriftReport {
60
+ blame: BlameAttribution[]
61
+ }
@@ -0,0 +1,46 @@
1
+ import type { SourceFile } from 'ts-morph'
2
+ import type { DriftConfig } from './app.js'
3
+ import type { DriftIssue } from './core.js'
4
+
5
+ export interface PluginRuleContext {
6
+ projectRoot: string
7
+ filePath: string
8
+ config?: DriftConfig
9
+ }
10
+
11
+ export interface DriftPluginRule {
12
+ id?: string
13
+ name: string
14
+ severity?: DriftIssue['severity']
15
+ weight?: number
16
+ detect: (file: SourceFile, context: PluginRuleContext) => DriftIssue[]
17
+ fix?: (issue: DriftIssue, file: SourceFile, context: PluginRuleContext) => DriftIssue | void
18
+ }
19
+
20
+ export interface DriftPlugin {
21
+ name: string
22
+ apiVersion?: number
23
+ capabilities?: Record<string, string | number | boolean>
24
+ rules: DriftPluginRule[]
25
+ }
26
+
27
+ export interface LoadedPlugin {
28
+ id: string
29
+ plugin: DriftPlugin
30
+ }
31
+
32
+ export interface PluginLoadError {
33
+ pluginId: string
34
+ pluginName?: string
35
+ ruleId?: string
36
+ code?: string
37
+ message: string
38
+ }
39
+
40
+ export interface PluginLoadWarning {
41
+ pluginId: string
42
+ pluginName?: string
43
+ ruleId?: string
44
+ code?: string
45
+ message: string
46
+ }
@@ -0,0 +1,134 @@
1
+ import type { DriftIssue, DriftOutputMetadata } from './core.js'
2
+
3
+ export type MergeRiskLevel = 'LOW' | 'MEDIUM' | 'HIGH' | 'CRITICAL'
4
+
5
+ export interface TrustGatePolicyPreset {
6
+ branch: string
7
+ enabled?: boolean
8
+ minTrust?: number
9
+ maxRisk?: MergeRiskLevel
10
+ }
11
+
12
+ export interface TrustGatePolicyPack {
13
+ enabled?: boolean
14
+ minTrust?: number
15
+ maxRisk?: MergeRiskLevel
16
+ }
17
+
18
+ export interface TrustGatePolicyConfig {
19
+ enabled?: boolean
20
+ minTrust?: number
21
+ maxRisk?: MergeRiskLevel
22
+ presets?: TrustGatePolicyPreset[]
23
+ policyPacks?: Record<string, TrustGatePolicyPack>
24
+ }
25
+
26
+ export interface TrustReason {
27
+ label: string
28
+ detail: string
29
+ impact: number
30
+ }
31
+
32
+ export interface TrustFixPriority {
33
+ rank: number
34
+ rule: string
35
+ severity: DriftIssue['severity']
36
+ occurrences: number
37
+ estimated_trust_gain: number
38
+ effort: 'low' | 'medium' | 'high'
39
+ suggestion: string
40
+ confidence?: 'low' | 'medium' | 'high'
41
+ explanation?: string
42
+ systemic?: boolean
43
+ }
44
+
45
+ export interface TrustAdvancedComparison {
46
+ source: 'previous-trust-json' | 'snapshot-history'
47
+ trend: 'improving' | 'regressing' | 'stable'
48
+ summary: string
49
+ trust_delta?: number
50
+ previous_trust_score?: number
51
+ previous_merge_risk?: MergeRiskLevel
52
+ snapshot_score_delta?: number
53
+ snapshot_label?: string
54
+ snapshot_timestamp?: string
55
+ }
56
+
57
+ export interface TrustAdvancedContext {
58
+ comparison?: TrustAdvancedComparison
59
+ team_guidance: string[]
60
+ }
61
+
62
+ export interface TrustDiffContext {
63
+ baseRef: string
64
+ status: 'improved' | 'regressed' | 'neutral'
65
+ scoreDelta: number
66
+ newIssues: number
67
+ resolvedIssues: number
68
+ filesChanged: number
69
+ penalty: number
70
+ bonus: number
71
+ netImpact: number
72
+ }
73
+
74
+ export interface DriftTrustReport {
75
+ scannedAt: string
76
+ targetPath: string
77
+ trust_score: number
78
+ merge_risk: MergeRiskLevel
79
+ top_reasons: TrustReason[]
80
+ fix_priorities: TrustFixPriority[]
81
+ diff_context?: TrustDiffContext
82
+ advanced_context?: TrustAdvancedContext
83
+ }
84
+
85
+ export type DriftTrustReportJson = DriftTrustReport & DriftOutputMetadata
86
+
87
+ export interface TrustKpiDiagnostic {
88
+ level: 'warning' | 'error'
89
+ code: 'path-not-found' | 'path-not-supported' | 'read-failed' | 'parse-failed' | 'invalid-shape' | 'invalid-diff-context'
90
+ message: string
91
+ file?: string
92
+ }
93
+
94
+ export interface TrustScoreStats {
95
+ average: number | null
96
+ median: number | null
97
+ min: number | null
98
+ max: number | null
99
+ }
100
+
101
+ export interface TrustDiffTrendSummary {
102
+ available: boolean
103
+ samples: number
104
+ statusDistribution: {
105
+ improved: number
106
+ regressed: number
107
+ neutral: number
108
+ }
109
+ scoreDelta: {
110
+ average: number | null
111
+ median: number | null
112
+ }
113
+ issues: {
114
+ newTotal: number
115
+ resolvedTotal: number
116
+ netNew: number
117
+ }
118
+ }
119
+
120
+ export interface TrustKpiReport {
121
+ generatedAt: string
122
+ input: string
123
+ files: {
124
+ matched: number
125
+ parsed: number
126
+ malformed: number
127
+ }
128
+ prsEvaluated: number
129
+ mergeRiskDistribution: Record<MergeRiskLevel, number>
130
+ trustScore: TrustScoreStats
131
+ highRiskRatio: number | null
132
+ diffTrend: TrustDiffTrendSummary
133
+ diagnostics: TrustKpiDiagnostic[]
134
+ }
package/src/types.ts CHANGED
@@ -1,238 +1,78 @@
1
- import type { SourceFile } from 'ts-morph'
2
-
3
- export interface DriftIssue {
4
- rule: string
5
- severity: 'error' | 'warning' | 'info'
6
- message: string
7
- line: number
8
- column: number
9
- snippet: string
10
- }
11
-
12
- export interface FileReport {
13
- path: string
14
- issues: DriftIssue[]
15
- score: number // 0–100, higher = more drift
16
- }
17
-
18
- export interface DriftReport {
19
- scannedAt: string
20
- targetPath: string
21
- files: FileReport[]
22
- totalIssues: number
23
- totalScore: number
24
- totalFiles: number
25
- summary: {
26
- errors: number
27
- warnings: number
28
- infos: number
29
- byRule: Record<string, number>
30
- }
31
- quality: RepoQualityScore
32
- maintenanceRisk: MaintenanceRiskMetrics
33
- }
34
-
35
- export interface RepoQualityScore {
36
- overall: number
37
- dimensions: {
38
- architecture: number
39
- complexity: number
40
- 'ai-patterns': number
41
- testing: number
42
- }
43
- }
44
-
45
- export interface RiskHotspot {
46
- file: string
47
- driftScore: number
48
- complexityIssues: number
49
- hasNearbyTests: boolean
50
- changeFrequency: number
51
- risk: number
52
- reasons: string[]
53
- }
54
-
55
- export interface MaintenanceRiskMetrics {
56
- score: number
57
- level: 'low' | 'medium' | 'high' | 'critical'
58
- hotspots: RiskHotspot[]
59
- signals: {
60
- highComplexityFiles: number
61
- filesWithoutNearbyTests: number
62
- frequentChangeFiles: number
63
- }
64
- }
65
-
66
- export interface AIOutput {
67
- summary: {
68
- score: number
69
- grade: string
70
- total_issues: number
71
- files_affected: number
72
- files_clean: number
73
- ai_likelihood: number
74
- ai_code_smell_score: number
75
- }
76
- files_suspected: Array<{ path: string; ai_likelihood: number; triggers: string[] }>
77
- priority_order: AIIssue[]
78
- maintenance_risk: MaintenanceRiskMetrics
79
- quality: RepoQualityScore
80
- context_for_ai: {
81
- project_type: string
82
- scan_path: string
83
- rules_detected: string[]
84
- recommended_action: string
85
- }
86
- }
87
-
88
- export interface AIIssue {
89
- rank: number
90
- file: string
91
- line: number
92
- rule: string
93
- severity: string
94
- message: string
95
- snippet: string
96
- fix_suggestion: string
97
- effort: 'low' | 'medium' | 'high'
98
- }
99
-
100
- // ---------------------------------------------------------------------------
101
- // Configuration
102
- // ---------------------------------------------------------------------------
103
-
104
- /**
105
- * Layer definition for architectural boundary enforcement.
106
- */
107
- export interface LayerDefinition {
108
- name: string
109
- patterns: string[]
110
- canImportFrom: string[]
111
- }
112
-
113
- /**
114
- * Module boundary definition for cross-boundary enforcement.
115
- */
116
- export interface ModuleBoundary {
117
- name: string
118
- root: string
119
- allowedExternalImports?: string[]
120
- }
121
-
122
- /**
123
- * Optional project-level configuration for drift.
124
- * Place in drift.config.ts (or .js / .json) at the project root.
125
- */
126
- export interface DriftConfig {
127
- layers?: LayerDefinition[]
128
- modules?: ModuleBoundary[]
129
- plugins?: string[]
130
- architectureRules?: {
131
- controllerNoDb?: boolean
132
- serviceNoHttp?: boolean
133
- maxFunctionLines?: number
134
- }
135
- saas?: {
136
- freeUserThreshold?: number
137
- maxRunsPerWorkspacePerMonth?: number
138
- maxReposPerWorkspace?: number
139
- retentionDays?: number
140
- }
141
- }
142
-
143
- export interface PluginRuleContext {
144
- projectRoot: string
145
- filePath: string
146
- config?: DriftConfig
147
- }
148
-
149
- export interface DriftPluginRule {
150
- name: string
151
- severity?: DriftIssue['severity']
152
- weight?: number
153
- detect: (file: SourceFile, context: PluginRuleContext) => DriftIssue[]
154
- }
155
-
156
- export interface DriftPlugin {
157
- name: string
158
- rules: DriftPluginRule[]
159
- }
160
-
161
- export interface LoadedPlugin {
162
- id: string
163
- plugin: DriftPlugin
164
- }
165
-
166
- export interface PluginLoadError {
167
- pluginId: string
168
- message: string
169
- }
170
-
171
- // ---------------------------------------------------------------------------
172
- // Diff
173
- // ---------------------------------------------------------------------------
174
-
175
- export interface FileDiff {
176
- path: string // path relativo al project root
177
- scoreBefore: number
178
- scoreAfter: number
179
- scoreDelta: number // positivo = empeoró (más deuda), negativo = mejoró
180
- newIssues: DriftIssue[]
181
- resolvedIssues: DriftIssue[]
182
- }
183
-
184
- export interface DriftDiff {
185
- baseRef: string // git ref del baseline (e.g. "HEAD~1", "main")
186
- projectPath: string // path absoluto del proyecto
187
- scannedAt: string // ISO timestamp
188
- files: FileDiff[] // solo archivos con cambios (delta != 0 o issues diff != 0)
189
- totalScoreBefore: number
190
- totalScoreAfter: number
191
- totalDelta: number // positivo = más deuda, negativo = menos deuda
192
- newIssuesCount: number
193
- resolvedIssuesCount: number
194
- }
195
-
196
- /** Historical analysis data for a single commit */
197
- export interface HistoricalAnalysis {
198
- commitHash: string;
199
- commitDate: Date;
200
- author: string;
201
- message: string;
202
- files: FileReport[];
203
- totalScore: number;
204
- averageScore: number;
205
- }
206
-
207
- /** Trend data point for score evolution */
208
- export interface TrendDataPoint {
209
- date: Date;
210
- score: number;
211
- fileCount: number;
212
- avgIssuesPerFile: number;
213
- }
214
-
215
- /** Blame attribution data */
216
- export interface BlameAttribution {
217
- author: string;
218
- email: string;
219
- commits: number;
220
- linesChanged: number;
221
- issuesIntroduced: number;
222
- avgScoreImpact: number;
223
- }
224
-
225
- /** Extended DriftReport with historical context */
226
- export interface DriftTrendReport extends DriftReport {
227
- trend: TrendDataPoint[];
228
- regression: {
229
- slope: number;
230
- intercept: number;
231
- r2: number;
232
- };
233
- }
234
-
235
- /** Extended DriftReport with blame data */
236
- export interface DriftBlameReport extends DriftReport {
237
- blame: BlameAttribution[];
238
- }
1
+ // drift-ignore-file
2
+ export type {
3
+ DriftIssue,
4
+ FileReport,
5
+ RepoQualityScore,
6
+ RiskHotspot,
7
+ MaintenanceRiskMetrics,
8
+ AIIssue,
9
+ AIOutput,
10
+ AIOutputJson,
11
+ DriftReport,
12
+ DriftReportJson,
13
+ DriftOutputMetadata,
14
+ } from './types/core.js'
15
+
16
+ export type {
17
+ MergeRiskLevel,
18
+ TrustGatePolicyPreset,
19
+ TrustGatePolicyPack,
20
+ TrustGatePolicyConfig,
21
+ TrustReason,
22
+ TrustFixPriority,
23
+ TrustAdvancedComparison,
24
+ TrustAdvancedContext,
25
+ TrustDiffContext,
26
+ DriftTrustReport,
27
+ DriftTrustReportJson,
28
+ TrustKpiDiagnostic,
29
+ TrustScoreStats,
30
+ TrustDiffTrendSummary,
31
+ TrustKpiReport,
32
+ } from './types/trust.js'
33
+
34
+ export type {
35
+ LayerDefinition,
36
+ ModuleBoundary,
37
+ DriftPerformanceConfig,
38
+ DriftAnalysisOptions,
39
+ } from './types/config.js'
40
+
41
+ export type { DriftConfig } from './types/app.js'
42
+
43
+ export type {
44
+ PluginRuleContext,
45
+ DriftPluginRule,
46
+ DriftPlugin,
47
+ LoadedPlugin,
48
+ PluginLoadError,
49
+ PluginLoadWarning,
50
+ } from './types/plugin.js'
51
+
52
+ export type {
53
+ FileDiff,
54
+ DriftDiff,
55
+ HistoricalAnalysis,
56
+ TrendDataPoint,
57
+ BlameAttribution,
58
+ DriftTrendReport,
59
+ DriftBlameReport,
60
+ } from './types/diff.js'
61
+
62
+ export type {
63
+ GuardBaseline,
64
+ GuardThresholds,
65
+ GuardOptions,
66
+ GuardMetrics,
67
+ GuardCheck,
68
+ GuardEvaluation,
69
+ GuardResult,
70
+ } from './guard-types.js'
71
+
72
+ export type {
73
+ SarifLevel,
74
+ DriftSarifRule,
75
+ DriftSarifResult,
76
+ DriftSarifRun,
77
+ DriftSarifLog,
78
+ } from './sarif.js'
@@ -0,0 +1,92 @@
1
+ import { afterEach, describe, expect, it } from 'vitest'
2
+ import { spawnSync } from 'node:child_process'
3
+ import { mkdtempSync, rmSync, writeFileSync } from 'node:fs'
4
+ import { join } from 'node:path'
5
+ import { tmpdir } from 'node:os'
6
+
7
+ type CliResult = {
8
+ status: number | null
9
+ stdout: string
10
+ stderr: string
11
+ }
12
+
13
+ type SarifOutput = {
14
+ version: string
15
+ runs: Array<{
16
+ tool: {
17
+ driver: {
18
+ name: string
19
+ }
20
+ }
21
+ results?: Array<{
22
+ ruleId?: string
23
+ message?: {
24
+ text?: string
25
+ }
26
+ }>
27
+ }>
28
+ }
29
+
30
+ function runCli(args: string[]): CliResult {
31
+ const result = spawnSync(process.execPath, ['--import', 'tsx', 'src/cli.ts', ...args], {
32
+ cwd: process.cwd(),
33
+ encoding: 'utf8',
34
+ })
35
+
36
+ return {
37
+ status: result.status,
38
+ stdout: result.stdout,
39
+ stderr: result.stderr,
40
+ }
41
+ }
42
+
43
+ function expectValidSarifFrom(result: CliResult): SarifOutput {
44
+ expect(result.status).toBe(0)
45
+ expect(result.stderr).not.toContain('Error:')
46
+
47
+ const sarif = JSON.parse(result.stdout) as SarifOutput
48
+ expect(sarif.version).toBe('2.1.0')
49
+ expect(Array.isArray(sarif.runs)).toBe(true)
50
+ expect(sarif.runs.length).toBeGreaterThan(0)
51
+ expect(sarif.runs[0]?.tool.driver.name).toBe('drift')
52
+ expect(Array.isArray(sarif.runs[0]?.results)).toBe(true)
53
+
54
+ return sarif
55
+ }
56
+
57
+ describe('cli sarif output', () => {
58
+ let tmpDir = ''
59
+
60
+ afterEach(() => {
61
+ if (tmpDir) rmSync(tmpDir, { recursive: true, force: true })
62
+ tmpDir = ''
63
+ })
64
+
65
+ it('serializes scan --format sarif output as SARIF JSON', () => {
66
+ tmpDir = mkdtempSync(join(tmpdir(), 'drift-cli-sarif-scan-'))
67
+ writeFileSync(join(tmpDir, 'sample.ts'), 'console.log("debug")\n')
68
+
69
+ const result = runCli(['scan', tmpDir, '--format', 'sarif'])
70
+ const sarif = expectValidSarifFrom(result)
71
+ expect(sarif.runs[0]?.results?.some((entry) => entry.ruleId === 'debug-leftover')).toBe(true)
72
+ })
73
+
74
+ it('serializes ci --format sarif output as SARIF JSON', () => {
75
+ tmpDir = mkdtempSync(join(tmpdir(), 'drift-cli-sarif-ci-'))
76
+ writeFileSync(join(tmpDir, 'sample.ts'), 'console.log("debug")\n')
77
+
78
+ const result = runCli(['ci', tmpDir, '--format', 'sarif'])
79
+ const sarif = expectValidSarifFrom(result)
80
+ expect(sarif.runs[0]?.results?.some((entry) => entry.ruleId === 'debug-leftover')).toBe(true)
81
+ })
82
+
83
+ it('serializes trust --format sarif output as SARIF JSON without requiring git base', () => {
84
+ tmpDir = mkdtempSync(join(tmpdir(), 'drift-cli-sarif-trust-'))
85
+ writeFileSync(join(tmpDir, 'sample.ts'), 'console.log("debug")\n')
86
+
87
+ const result = runCli(['trust', tmpDir, '--format', 'sarif'])
88
+ const sarif = expectValidSarifFrom(result)
89
+ expect(sarif.runs[0]?.results?.some((entry) => entry.ruleId === 'debug-leftover')).toBe(true)
90
+ })
91
+
92
+ })