@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
package/README.md CHANGED
@@ -1,8 +1,8 @@
1
- ![drift technical debt detector for AI-generated code](./assets/og.png)
1
+ ![drift - AI Code Audit CLI for merge trust](./assets/og.png)
2
2
 
3
3
  # drift
4
4
 
5
- Detect technical debt in AI-generated TypeScript code. One command. Zero config.
5
+ AI Code Audit CLI for AI-assisted PRs. Drift turns static signals into merge trust decisions before you merge.
6
6
 
7
7
  ![npm](https://img.shields.io/npm/v/@eduardbar/drift?color=6366f1&label=npm)
8
8
  ![license](https://img.shields.io/badge/license-MIT-green.svg)
@@ -20,7 +20,7 @@ AI coding tools ship code fast. They also leave behind consistent, predictable s
20
20
 
21
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
22
 
23
- drift gives you a 0–100 score per file and project so you know what to look at before it reaches production.
23
+ drift gives you debt signals (`scan`, `review`) and a trust decision layer (`trust`) so teams can merge with confidence instead of guesswork.
24
24
 
25
25
  **How drift compares to existing tools:**
26
26
 
@@ -50,12 +50,62 @@ npm install --save-dev @eduardbar/drift
50
50
  ## Product Docs
51
51
 
52
52
  - Product requirements and roadmap: [`docs/PRD.md`](./docs/PRD.md)
53
+ - Trust core release checklist: [`docs/trust-core-release-checklist.md`](./docs/trust-core-release-checklist.md)
54
+ - Rules catalog (source-of-truth snapshot): [`docs/rules-catalog.md`](./docs/rules-catalog.md)
53
55
  - Contributor/agent workflow guide: [`docs/AGENTS.md`](./docs/AGENTS.md)
54
56
 
55
57
  ---
56
58
 
57
59
  ## Commands
58
60
 
61
+ ### `drift init`
62
+
63
+ Scaffold first-run setup for drift in an existing repository.
64
+
65
+ ```bash
66
+ drift init
67
+ drift init --preset node-backend
68
+ drift init --preset react-app --ci --baseline
69
+ ```
70
+
71
+ | Flag | Description |
72
+ |------|-------------|
73
+ | `--preset <type>` | Generate `drift.config.ts` using one of: `node-backend`, `react-app`, `hexagonal`, `monorepo` |
74
+ | `--ci` | Generate `.github/workflows/drift-review.yml` |
75
+ | `--baseline` | Generate `drift-baseline.json` from the current scan |
76
+
77
+ `drift init` is non-destructive for pre-existing targets: drift skips generation when an output file already exists and prints a warning.
78
+
79
+ ---
80
+
81
+ ### `drift doctor`
82
+
83
+ Run environment diagnostics for Node/runtime and project readiness.
84
+
85
+ ```bash
86
+ drift doctor
87
+ drift doctor --json
88
+ ```
89
+
90
+ | Flag | Description |
91
+ |------|-------------|
92
+ | `--json` | Output structured doctor report JSON |
93
+
94
+ Checks include Node major version support (`>=18`), `package.json`, ESM mode, `tsconfig.json`, source file count, optional `--low-memory` recommendation, and `drift.config.*` detection.
95
+
96
+ ---
97
+
98
+ ### Repository smoke (local source CLI)
99
+
100
+ Run a non-destructive end-to-end smoke suite against any repository path using local source commands (`node --import tsx ./src/cli.ts ...`).
101
+
102
+ ```bash
103
+ npm run smoke:repo -- ../target-repo --base origin/main
104
+ npm run smoke:repo -- ../target-repo --dry-run
105
+ ```
106
+
107
+ The script writes JSON + Markdown summaries and command logs under `.drift-smoke/<repo>-<timestamp>/`.
108
+
59
109
  ### `drift scan [path]`
60
110
 
61
111
  Scan a directory and print a scored report to stdout.
@@ -68,6 +118,7 @@ drift scan ./src --json
68
118
  drift scan ./src --ai
69
119
  drift scan ./src --fix
70
120
  drift scan ./src --min-score 50
121
+ drift scan ./src --low-memory --max-file-size-kb 1024
71
122
  ```
72
123
 
73
124
  **Options:**
@@ -75,10 +126,18 @@ drift scan ./src --min-score 50
75
126
  | Flag | Description |
76
127
  |------|-------------|
77
128
  | `--output <file>` | Write Markdown report to a file instead of stdout |
129
+ | `--format <type>` | Output format: `console\|json\|markdown\|ai\|sarif` |
78
130
  | `--json` | Output raw `DriftReport` JSON |
79
131
  | `--ai` | Output structured JSON optimized for LLM consumption (Claude, GPT, etc.) |
80
132
  | `--fix` | Print inline fix suggestions for each detected issue |
81
- | `--min-score <n>` | Exit with code 1 if the overall score meets or exceeds this threshold |
133
+ | `--min-score <n>` | Exit with code 1 if the overall score strictly exceeds this threshold |
134
+ | `--low-memory` | Analyze files in bounded chunks to reduce peak RAM |
135
+ | `--chunk-size <n>` | Files per chunk in low-memory mode (default: 40) |
136
+ | `--max-files <n>` | Soft cap on analyzed files; extra files are skipped with diagnostics |
137
+ | `--max-file-size-kb <n>` | Skip oversized files with diagnostics to avoid OOM |
138
+ | `--with-semantic-duplication` | Keep semantic duplication rule enabled in low-memory mode |
139
+
140
+ `--min-score` currently fails when `totalScore > n` (strictly greater than), matching CLI behavior.
82
141
 
83
142
  **Example output:**
84
143
 
@@ -106,6 +165,33 @@ drift scan ./src --min-score 50
106
165
 
107
166
  ---
108
167
 
168
+ ### `drift guard [path]`
169
+
170
+ Evaluate drift regression guardrails against a git diff (`--base`) or a baseline file/object.
171
+
172
+ ```bash
173
+ drift guard ./src --base origin/main
174
+ drift guard ./src --baseline drift-baseline.json
175
+ drift guard ./src --base origin/main --budget 3 --by-severity error=0,warning=2,info=5
176
+ drift guard ./src --base origin/main --json
177
+ ```
178
+
179
+ | Flag | Description |
180
+ |------|-------------|
181
+ | `--base <ref>` | Diff mode: compare current state vs git ref |
182
+ | `--baseline <file>` | Baseline mode: compare against baseline JSON (default file name: `drift-baseline.json`) |
183
+ | `--budget <n>` | Maximum allowed score delta |
184
+ | `--by-severity <spec>` | Severity thresholds as CSV `key=value` pairs (`error`, `warning`, `info`) |
185
+ | `--json` | Output full `GuardResult` JSON |
186
+ | `--low-memory` / `--chunk-size <n>` / `--max-files <n>` / `--max-file-size-kb <n>` / `--with-semantic-duplication` | Analysis resource controls shared with scan/diff/report/badge/ci/trust |
187
+
188
+ Behavior:
189
+ - With `--base`, guard enforces no-regression checks for score and total issues, then applies optional budget/severity thresholds.
190
+ - Without `--base`, guard requires a baseline (inline or file) and compares only against available baseline anchors.
191
+ - Exit code is `1` when any guard check fails.
192
+
193
+ ---
194
+
109
195
  ### `drift diff [ref]`
110
196
 
111
197
  Compare the current project state against any git ref. Defaults to `HEAD~1`.
@@ -115,6 +201,7 @@ drift diff # HEAD vs HEAD~1
115
201
  drift diff HEAD~3 # HEAD vs 3 commits ago
116
202
  drift diff main # HEAD vs branch main
117
203
  drift diff abc1234 # HEAD vs a specific commit
204
+ drift diff --format sarif # Output SARIF for code scanning
118
205
  drift diff --json # Output raw JSON diff
119
206
  ```
120
207
 
@@ -122,6 +209,7 @@ drift diff --json # Output raw JSON diff
122
209
 
123
210
  | Flag | Description |
124
211
  |------|-------------|
212
+ | `--format <type>` | Output format: `console\|json\|sarif` |
125
213
  | `--json` | Output raw JSON diff |
126
214
 
127
215
  Shows score delta, issues introduced, and issues resolved since the given ref.
@@ -136,16 +224,105 @@ Review drift against a git base ref and output a PR-ready markdown comment.
136
224
  drift review --base origin/main
137
225
  drift review --base main --comment
138
226
  drift review --base HEAD~3 --json
227
+ drift review --base origin/main --format sarif
139
228
  drift review --base origin/main --fail-on 5
140
229
  ```
141
230
 
142
231
  | Flag | Description |
143
232
  |------|-------------|
144
233
  | `--base <ref>` | Git base ref to compare against (default: `origin/main`) |
234
+ | `--format <type>` | Output format: `console\|json\|markdown\|sarif` |
145
235
  | `--json` | Output structured review JSON |
146
236
  | `--comment` | Print only the markdown body for PR comments |
147
237
  | `--fail-on <n>` | Exit code 1 when score delta is greater than or equal to `n` |
148
238
 
239
+ `drift review` is best used as supplementary diff context alongside `drift trust` in pull-request workflows.
240
+
241
+ ---
242
+
243
+ ### `drift trust [path]`
244
+
245
+ Compute merge trust baseline for local checks and CI merge gates.
246
+
247
+ ```bash
248
+ drift trust
249
+ drift trust ./src
250
+ drift trust ./src --json
251
+ drift trust ./src --base origin/main
252
+ drift trust ./src --base origin/main --markdown
253
+ drift trust ./src --format sarif
254
+ drift trust ./src --markdown --output trust.md
255
+ drift trust ./src --min-trust 45
256
+ drift trust ./src --max-risk HIGH
257
+ drift trust ./src --branch main
258
+ drift trust ./src --branch release/v1.4.0 --policy-pack strict --explain-policy
259
+ drift trust ./src --advanced-trust
260
+ drift trust ./src --advanced-trust --previous-trust ./artifacts/prev-trust.json
261
+ drift trust ./src --advanced-trust --history-file ./drift-history.json --markdown
262
+ ```
263
+
264
+ | Flag | Description |
265
+ |------|-------------|
266
+ | `--base <ref>` | Compare against a git base ref and include deterministic diff-aware penalties/bonuses |
267
+ | `--format <type>` | Output format: `console\|json\|markdown\|sarif` |
268
+ | `--json` | Output structured trust JSON (`trust_score`, `merge_risk`, `top_reasons`, `fix_priorities`, optional `diff_context`) |
269
+ | `--markdown` | Output PR-ready markdown trust summary |
270
+ | `--output <file>` | Write selected trust output format to file |
271
+ | `--min-trust <n>` | Exit code 1 when trust score is below `n` |
272
+ | `--max-risk <level>` | Exit code 1 when computed merge risk exceeds `LOW`, `MEDIUM`, `HIGH`, or `CRITICAL` |
273
+ | `--branch <name>` | Branch used for `drift.config` trust policy matching (falls back to CI env vars) |
274
+ | `--policy-pack <name>` | Optional trust policy pack from `trustGate.policyPacks` in `drift.config.*` |
275
+ | `--explain-policy` | Print base/pack/branch/override resolution and effective gate policy to stderr |
276
+ | `--advanced-trust` | Enable optional advanced trust layer (historical comparison + team guidance metadata) |
277
+ | `--previous-trust <file>` | Compare current trust against a prior trust JSON file in advanced mode |
278
+ | `--history-file <file>` | Use a specific `drift-history.json` file for advanced historical fallback |
279
+
280
+ When `trustGate` policy is configured in `drift.config.*`, branch-based thresholds are applied automatically. CLI flags still override policy values.
281
+
282
+ ---
283
+
284
+ ### `drift trust-gate <trust-json-file>`
285
+
286
+ Evaluate trust gate thresholds from a previously generated trust JSON file. This is ideal for CI workflows that already produced `drift-trust.json` and want a single source of truth for gate logic.
287
+
288
+ ```bash
289
+ drift trust-gate drift-trust.json --min-trust 45 --max-risk HIGH
290
+ drift trust-gate drift-trust.json --branch release/v1.2.0
291
+ drift trust-gate drift-trust.json --branch main --policy-pack balanced --explain-policy
292
+ ```
293
+
294
+ | Flag | Description |
295
+ |------|-------------|
296
+ | `--min-trust <n>` | Exit code 1 when trust score in JSON is below `n` |
297
+ | `--max-risk <level>` | Exit code 1 when merge risk in JSON exceeds `LOW`, `MEDIUM`, `HIGH`, or `CRITICAL` |
298
+ | `--branch <name>` | Branch used for `drift.config` trust policy matching (falls back to CI env vars) |
299
+ | `--policy-pack <name>` | Optional trust policy pack from `trustGate.policyPacks` in `drift.config.*` |
300
+ | `--explain-policy` | Print base/pack/branch/override resolution and effective gate policy to stderr |
301
+
302
+ ---
303
+
304
+ ### `drift kpi <path>`
305
+
306
+ Aggregate trust KPI evidence from local artifacts (directory or glob). Prints a compact console summary to stderr and structured KPI JSON to stdout.
307
+
308
+ ```bash
309
+ drift kpi ./artifacts/trust
310
+ drift kpi "./artifacts/**/drift-trust-*.json"
311
+ drift kpi ./artifacts/trust --no-summary
312
+ ```
313
+
314
+ | Flag | Description |
315
+ |------|-------------|
316
+ | `--no-summary` | Disable stderr console summary and output JSON only |
317
+
318
+ Computed KPI fields include:
319
+ - PRs evaluated count
320
+ - Merge risk distribution (`LOW`, `MEDIUM`, `HIGH`, `CRITICAL`)
321
+ - Trust score average/median/min/max
322
+ - High-risk ratio (`HIGH` + `CRITICAL`)
323
+ - Diff trend aggregates when `diff_context` exists (`scoreDelta`, new/resolved issues, status distribution)
324
+ - Diagnostics for malformed/missing/invalid JSON artifacts
325
+
149
326
  ---
150
327
 
151
328
  ### `drift map [path]`
@@ -216,6 +393,7 @@ Emit GitHub Actions annotations and a step summary. Designed to run inside a CI
216
393
  ```bash
217
394
  drift ci # scan current directory
218
395
  drift ci ./src
396
+ drift ci ./src --format sarif
219
397
  drift ci ./src --min-score 60
220
398
  ```
221
399
 
@@ -223,7 +401,8 @@ drift ci ./src --min-score 60
223
401
 
224
402
  | Flag | Description |
225
403
  |------|-------------|
226
- | `--min-score <n>` | Exit with code 1 if the overall score meets or exceeds this threshold |
404
+ | `--format <type>` | Output format: `console\|json\|sarif` |
405
+ | `--min-score <n>` | Exit with code 1 if the overall score strictly exceeds this threshold |
227
406
 
228
407
  Outputs `::error` and `::warning` annotations visible in the PR diff. Writes a markdown summary to `$GITHUB_STEP_SUMMARY`.
229
408
 
@@ -290,11 +469,18 @@ drift fix ./src --dry-run # alias of --preview
290
469
 
291
470
  ### `drift cloud`
292
471
 
293
- Local SaaS foundations backed by `.drift-cloud/store.json`.
472
+ Local multi-tenant foundations backed by `.drift-cloud/store.json`.
294
473
 
295
474
  ```bash
296
- drift cloud ingest ./src --workspace acme --user u-123 --repo webapp
475
+ drift cloud ingest ./src --org acme --workspace core --user u-123 --role owner --plan sponsor --repo webapp
297
476
  drift cloud summary
477
+ drift cloud summary --org acme --workspace core
478
+ drift cloud summary --org acme --workspace core --actor u-owner
479
+ drift cloud ingest ./src --org acme --workspace core --user u-member --actor u-owner
480
+ drift cloud ingest ./src --org acme --workspace core --user u-member --actor u-member # strict actor mode ready
481
+ drift cloud plan-set --org acme --plan team --actor u-owner --reason "annual upgrade"
482
+ drift cloud plan-changes --org acme --actor u-owner
483
+ drift cloud usage --org acme --actor u-owner
298
484
  drift cloud summary --json
299
485
  drift cloud dashboard --output drift-cloud-dashboard.html
300
486
  ```
@@ -303,46 +489,53 @@ drift cloud dashboard --output drift-cloud-dashboard.html
303
489
 
304
490
  | Command | Description |
305
491
  |---------|-------------|
306
- | `drift cloud ingest [path] --workspace <id> --user <id> [--repo <name>] [--store <file>]` | Scans the path and stores one SaaS snapshot |
307
- | `drift cloud summary [--json] [--store <file>]` | Shows users/workspaces/repos usage and runs per month |
492
+ | `drift cloud ingest [path] --org <id> --workspace <id> --user <id> [--role <owner\|member\|viewer>] [--plan <free\|sponsor\|team\|business>] [--repo <name>] [--actor <user>] [--store <file>]` | Scans the path and stores one tenant-scoped snapshot (`--actor` optional by default, required only when `saas.strictActorEnforcement=true`) |
493
+ | `drift cloud summary [--json] [--org <id>] [--workspace <id>] [--actor <user>] [--store <file>]` | Shows users/workspaces/repos usage and runs per month; in strict mode, scoped reads (`--org`/`--workspace`) require `--actor` |
494
+ | `drift cloud plan-set --org <id> --plan <free\|sponsor\|team\|business> --actor <user> [--reason <text>] [--store <file>]` | Updates organization plan and writes an audited plan-change event (owner-gated by actor) |
495
+ | `drift cloud plan-changes --org <id> --actor <user> [--json] [--store <file>]` | Lists audited plan lifecycle events for the organization |
496
+ | `drift cloud usage --org <id> --actor <user> [--month <yyyy-mm>] [--json] [--store <file>]` | Shows organization usage plus effective limits for current plan |
308
497
  | `drift cloud dashboard [--output <file>] [--store <file>]` | Generates an HTML dashboard with trends and hotspots |
309
498
 
310
- `drift cloud` ships with a free-until-7,500 strategy and configurable guardrails for the free phase: max runs per workspace per month, max repos per workspace, and retention window.
499
+ `drift cloud` ships with tenant identity boundaries (`organizationId` + `workspaceId`), role primitives (`owner`/`member`/`viewer`), and plan placeholders (`free`/`sponsor`/`team`/`business`). It is an infrastructure foundation, not a full auth/billing platform.
500
+
501
+ By default, local guardrails enforce one plan-based limit: max workspaces per organization (`free:20`, `sponsor:50`, `team:200`, `business:1000`), plus existing free-phase limits (runs per workspace, repos per workspace, retention window).
502
+
503
+ To require actor identity on scoped cloud operations, enable strict actor mode in `drift.config.*`:
504
+
505
+ ```ts
506
+ export default {
507
+ saas: {
508
+ strictActorEnforcement: true,
509
+ },
510
+ }
511
+ ```
311
512
 
312
513
  ---
313
514
 
314
515
  ## Rules
315
516
 
316
- 26 rules across three severity levels. All run automatically unless marked as requiring configuration.
517
+ drift currently defines **35 rule IDs** in `RULE_WEIGHTS` (`src/analyzer.ts`): core detections, configurable architecture checks, AI/meta aggregation, plugin diagnostics, and analysis guardrail diagnostics.
518
+
519
+ For the complete up-to-date catalog (id, severity, weight, phase/origin, note), see [`docs/rules-catalog.md`](./docs/rules-catalog.md).
520
+
521
+ Highlights:
317
522
 
318
523
  | Rule | Severity | Weight | What it detects |
319
524
  |------|----------|--------|-----------------|
320
- | `large-file` | error | 20 | Files exceeding 300 lines — AI generates monolithic files instead of splitting responsibility |
321
- | `large-function` | error | 15 | Functions exceeding 50 lines AI avoids decomposing logic into smaller units |
322
- | `duplicate-function-name` | error | 18 | Function names that appear more than once (case-insensitive) — AI regenerates helpers instead of reusing them |
323
- | `high-complexity` | error | 15 | Cyclomatic complexity above 10 AI produces correct code, not necessarily simple code |
324
- | `circular-dependency` | error | 14 | Circular import chains between modules AI doesn't reason about module topology |
325
- | `layer-violation` | error | 16 | Imports that cross architectural layers in the wrong direction (e.g., domain importing from infra) — requires `drift.config.ts` |
326
- | `debug-leftover` | warning | 10 | `console.log`, `console.warn`, `console.error`, and `TODO` / `FIXME` / `HACK` comments — AI leaves scaffolding in place |
327
- | `dead-code` | warning | 8 | Named imports that are never used in the file — AI imports broadly |
328
- | `any-abuse` | warning | 8 | Explicit `any` type annotations — AI defaults to `any` when type inference is unclear |
329
- | `catch-swallow` | warning | 10 | Empty `catch` blocks — AI makes code not throw without handling the error |
330
- | `comment-contradiction` | warning | 12 | Comments that restate what the surrounding code already expresses — AI over-documents the obvious |
331
- | `deep-nesting` | warning | 12 | Control flow nested more than 3 levels deep — results in code that is difficult to follow |
332
- | `too-many-params` | warning | 8 | Functions with more than 4 parameters — AI avoids grouping related arguments into objects |
333
- | `high-coupling` | warning | 10 | Files importing from more than 10 distinct modules — AI imports broadly without encapsulation |
334
- | `promise-style-mix` | warning | 7 | `async/await` and `.then()` / `.catch()` used together in the same file — AI combines styles inconsistently |
335
- | `unused-export` | warning | 8 | Named exports that are never imported anywhere in the project — cross-file dead code ESLint cannot detect |
336
- | `dead-file` | warning | 10 | Files never imported by any other file in the project — invisible dead code |
337
- | `unused-dependency` | warning | 6 | Packages listed in `package.json` with no corresponding import in source files |
338
- | `cross-boundary-import` | warning | 10 | Imports that cross module boundaries outside the allowed list — requires `drift.config.ts` |
339
- | `hardcoded-config` | warning | 10 | Hardcoded URLs, IP addresses, secrets, or connection strings — AI skips environment variable abstraction |
340
- | `inconsistent-error-handling` | warning | 8 | Mixed `try/catch` and `.catch()` patterns in the same file — AI combines approaches without a consistent strategy |
341
- | `unnecessary-abstraction` | warning | 7 | Wrapper functions or helpers that add no logic over what they wrap — AI over-engineers simple calls |
342
- | `naming-inconsistency` | warning | 6 | Mixed `camelCase` and `snake_case` in the same module — AI forgets project conventions mid-generation |
343
- | `semantic-duplication` | warning | 12 | Functions with structurally identical logic despite different names — detected via AST fingerprinting, not text comparison |
344
- | `no-return-type` | info | 5 | Functions missing an explicit return type annotation |
345
- | `magic-number` | info | 3 | Numeric literals used directly in logic without a named constant |
525
+ | `large-file` | error | 20 | Files exceeding 300 lines |
526
+ | `duplicate-function-name` | error | 18 | Same function name repeated across a file |
527
+ | `high-complexity` | error | 15 | Cyclomatic complexity above threshold |
528
+ | `layer-violation` | error | 16 | Invalid layer import direction (requires `layers` config) |
529
+ | `cross-boundary-import` | warning | 10 | Invalid module boundary import (requires `modules`/legacy boundaries config) |
530
+ | `controller-no-db` | warning | 11 | Controller touching DB directly (configurable architecture rule) |
531
+ | `service-no-http` | warning | 11 | Service layer coupled to HTTP concerns (configurable architecture rule) |
532
+ | `max-function-lines` | warning | 9 | Function line cap enforced by `architectureRules.maxFunctionLines` |
533
+ | `semantic-duplication` | warning | 12 | AST-level semantic function duplication |
534
+ | `ai-code-smell` | warning | 12 | Aggregated AI-smell signal from multiple heuristics in a file |
535
+ | `plugin-error` | warning | 4 | Plugin load/contract/runtime failure surfaced as issue |
536
+ | `plugin-warning` | info | 0 | Non-fatal plugin validation warning |
537
+ | `analysis-skip-max-files` | info | 0 | File skipped by `maxFiles` guardrail |
538
+ | `analysis-skip-file-size` | info | 0 | File skipped by `maxFileSizeKb` guardrail |
346
539
 
347
540
  ---
348
541
 
@@ -362,39 +555,63 @@ drift cloud dashboard --output drift-cloud-dashboard.html
362
555
 
363
556
  ## Configuration
364
557
 
365
- drift runs with zero configuration. Architectural rules (`layer-violation`, `cross-boundary-import`) require a `drift.config.ts` (or `.js` / `.json`) at your project root:
558
+ drift runs with zero configuration. Architectural rules (`layer-violation`, `cross-boundary-import`) and configurable architecture checks use `drift.config.ts` (or `.js` / `.json`) at project root:
366
559
 
367
560
  ```typescript
368
561
  import type { DriftConfig } from '@eduardbar/drift'
369
562
 
370
563
  export default {
371
564
  plugins: ['drift-plugin-example'],
565
+ performance: {
566
+ lowMemory: true,
567
+ chunkSize: 40,
568
+ maxFiles: 8000,
569
+ maxFileSizeKb: 1024,
570
+ includeSemanticDuplication: false,
571
+ },
372
572
  architectureRules: {
373
573
  controllerNoDb: true,
374
574
  serviceNoHttp: true,
375
575
  maxFunctionLines: 80,
376
576
  },
577
+ trustGate: {
578
+ minTrust: 45,
579
+ maxRisk: 'HIGH',
580
+ presets: [
581
+ { branch: 'main', minTrust: 70, maxRisk: 'MEDIUM' },
582
+ { branch: 'release/*', minTrust: 80, maxRisk: 'LOW' },
583
+ { branch: 'legacy/*', enabled: false },
584
+ ],
585
+ },
377
586
  layers: [
378
587
  { name: 'domain', patterns: ['src/domain/**'], canImportFrom: [] },
379
588
  { name: 'app', patterns: ['src/app/**'], canImportFrom: ['domain'] },
380
589
  { name: 'infra', patterns: ['src/infra/**'], canImportFrom: ['domain', 'app'] },
381
590
  ],
382
- boundaries: [
591
+ modules: [
383
592
  { name: 'auth', root: 'src/modules/auth', allowedExternalImports: ['src/shared'] },
384
593
  { name: 'billing', root: 'src/modules/billing', allowedExternalImports: ['src/shared'] },
385
594
  ],
386
- exclude: [
387
- 'src/generated/**',
388
- '**/*.spec.ts',
389
- ],
390
- rules: {
391
- 'large-file': { threshold: 400 }, // override default 300
392
- 'magic-number': 'off', // disable a rule
393
- },
394
595
  } satisfies DriftConfig
395
596
  ```
396
597
 
397
- Without a config file, `layer-violation` and `cross-boundary-import` are silently skipped. All other rules run with their defaults.
598
+ For backwards compatibility, `moduleBoundaries` and `boundaries` are normalized to `modules`.
599
+
600
+ Without a config file, `layer-violation`, `cross-boundary-import`, and configurable architecture checks (`controller-no-db`, `service-no-http`, `max-function-lines`) are skipped. All other rules run with defaults.
601
+
602
+ ### Memory guardrails (recommended for large repos)
603
+
604
+ If your repository is large or your machine has limited RAM, start with:
605
+
606
+ ```bash
607
+ drift scan . --low-memory --max-file-size-kb 1024 --max-files 8000
608
+ ```
609
+
610
+ Practical tuning:
611
+ - Lower `--chunk-size` to reduce peak memory further (slower but safer).
612
+ - Keep `includeSemanticDuplication: false` in low-memory mode for the lowest memory footprint.
613
+ - Set `maxFileSizeKb` to skip generated/vendor files that can explode AST memory usage.
614
+ - Set `maxFiles` to protect CI runners from worst-case monorepo scans.
398
615
 
399
616
  ---
400
617
 
@@ -419,7 +636,7 @@ jobs:
419
636
  run: npx @eduardbar/drift scan ./src --min-score 60
420
637
  ```
421
638
 
422
- Exit code is `1` if the score meets or exceeds `--min-score`. Exit code `0` otherwise.
639
+ Exit code is `1` if the score is strictly greater than `--min-score`. Exit code `0` otherwise.
423
640
 
424
641
  ### Annotations and step summary with `drift ci`
425
642
 
@@ -445,10 +662,50 @@ jobs:
445
662
  ### Auto PR comment with `drift review`
446
663
 
447
664
  The repository includes `.github/workflows/review-pr.yml`, which:
448
- - generates a PR-ready markdown comment from `drift review --comment`
665
+ - generates a PR-ready markdown comment with `drift trust --markdown` first and `drift review --comment` as supplementary context
449
666
  - updates a single sticky comment (`<!-- drift-review -->`) on non-fork PRs
450
667
  - falls back to `$GITHUB_STEP_SUMMARY` for fork PRs
451
- - enforces a score delta threshold with `--fail-on`
668
+ - enforces a trust baseline gate with `drift trust-gate drift-trust.json --min-trust 45 --max-risk HIGH`
669
+ - uploads `drift trust --json` as `drift-trust-json-pr-<PR_NUMBER>-run-<RUN_ATTEMPT>` for manual KPI tracking
670
+ - publishes a compact trust KPI summary in `$GITHUB_STEP_SUMMARY` (score, merge risk, new/resolved issues when diff context is available)
671
+ - generates `drift.sarif` via `drift scan --format sarif`, uploads it to GitHub Code Scanning on non-fork PRs, and stores the SARIF file as workflow artifact for traceability
672
+
673
+ Quick local SARIF export:
674
+
675
+ ```bash
676
+ drift scan ./src --format sarif > drift.sarif
677
+ ```
678
+
679
+ Example GitHub Code Scanning upload with SARIF:
680
+
681
+ ```yaml
682
+ - name: Generate drift SARIF
683
+ run: npx @eduardbar/drift scan ./src --format sarif > drift.sarif
684
+
685
+ - name: Upload SARIF to Code Scanning
686
+ uses: github/codeql-action/upload-sarif@v3
687
+ with:
688
+ sarif_file: drift.sarif
689
+ ```
690
+
691
+ Default gate behavior in this repo:
692
+ - fail when trust is below 45
693
+ - fail when merge risk is above `HIGH` (that means `CRITICAL` is blocked)
694
+
695
+ ### GitHub Action contract (v2)
696
+
697
+ This repo also ships reusable local composite actions:
698
+
699
+ - `./.github/actions/drift-scan`: score-focused scan gate using `npx @eduardbar/drift@<version>` (no global install).
700
+ - `./.github/actions/drift-review`: trust/review/gate flow for PRs, aligned with `.github/workflows/review-pr.yml`.
701
+
702
+ `drift-scan` exposes machine-friendly outputs for CI composition:
703
+ - `score`, `grade`, `errors`, `warnings`, `infos`, `total-issues`, `files-affected`, `top-rules`
704
+
705
+ `drift-review` exposes:
706
+ - `trust-score`, `merge-risk`, `new-issues`, `resolved-issues`, `trust-json`, `trust-markdown`, `review-markdown`
707
+
708
+ See `.github/actions/drift-scan/README.md` and `.github/actions/drift-review/README.md` for usage details.
452
709
 
453
710
  ---
454
711
 
package/ROADMAP.md CHANGED
@@ -21,19 +21,20 @@ drift's goal is to be the tool that sits between ESLint and SonarQube — lightw
21
21
  **What drift is not:**
22
22
  - Not an ESLint replacement for style or correctness rules
23
23
  - Not a SonarQube replacement for security scanning
24
- - Not a cloud product, not a SaaS, not freemium
24
+ - Not a cloud-only product or mandatory hosted backend
25
25
 
26
26
  **What drift is:**
27
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
28
+ - An AI code audit CLI that helps teams decide merge trust on AI-assisted PRs
29
+ - A CI gate that blocks risky merges using trust thresholds
29
30
  - 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
+ - Core CLI is free and MIT; premium governance/sponsor tiers are a product direction for teams
31
32
 
32
33
  ---
33
34
 
34
35
  ## Principles that don't change
35
36
 
36
- - **Always free for the developer.** MIT. Forever. No paid tier, no cloud lock-in.
37
+ - **Core stays free for developers.** MIT for the OSS CLI, no backend lock-in for the core workflow.
37
38
  - **Zero config to start.** One command, one number. Config is optional and additive.
38
39
  - **Fast.** Results in under 3 seconds on any normal project.
39
40
  - **One actionable number, not 400 warnings nobody reads.**
@@ -153,7 +154,7 @@ A score of 45 means nothing without context. A score that went from 80 to 45 ove
153
154
 
154
155
  - **26 rules active** across 9 detection categories
155
156
  - **Self-scan score: 14/100 (LOW)**
156
- - Published on npm as `@eduardbar/drift` — MIT, always free
157
+ - Published on npm as `@eduardbar/drift` — MIT core CLI, optional premium direction documented in PRD
157
158
  - Cross-platform: Windows / Linux / macOS via `npx`
158
159
 
159
160
  ---
@@ -1,4 +1,4 @@
1
- import type { DriftIssue, FileReport, DriftConfig, LoadedPlugin } from './types.js';
1
+ import type { DriftIssue, FileReport, DriftConfig, DriftAnalysisOptions, LoadedPlugin } from './types.js';
2
2
  export { TrendAnalyzer } from './git/trend.js';
3
3
  export { BlameAnalyzer } from './git/blame.js';
4
4
  export declare const RULE_WEIGHTS: Record<string, {
@@ -10,5 +10,5 @@ export declare function analyzeFile(file: import('ts-morph').SourceFile, options
10
10
  loadedPlugins?: LoadedPlugin[];
11
11
  projectRoot?: string;
12
12
  }): FileReport;
13
- export declare function analyzeProject(targetPath: string, config?: DriftConfig): FileReport[];
13
+ export declare function analyzeProject(targetPath: string, config?: DriftConfig, options?: DriftAnalysisOptions): FileReport[];
14
14
  //# sourceMappingURL=analyzer.d.ts.map