@vibecheckai/cli 3.7.0 → 3.9.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 (118) hide show
  1. package/README.md +135 -63
  2. package/bin/_deprecations.js +447 -19
  3. package/bin/_router.js +1 -1
  4. package/bin/registry.js +347 -280
  5. package/bin/runners/context/generators/cursor-enhanced.js +2439 -0
  6. package/bin/runners/lib/agent-firewall/enforcement/gateway.js +1059 -0
  7. package/bin/runners/lib/agent-firewall/enforcement/index.js +98 -0
  8. package/bin/runners/lib/agent-firewall/enforcement/mode.js +318 -0
  9. package/bin/runners/lib/agent-firewall/enforcement/orchestrator.js +484 -0
  10. package/bin/runners/lib/agent-firewall/enforcement/proof-artifact.js +418 -0
  11. package/bin/runners/lib/agent-firewall/enforcement/schemas/change-event.schema.json +173 -0
  12. package/bin/runners/lib/agent-firewall/enforcement/schemas/intent.schema.json +181 -0
  13. package/bin/runners/lib/agent-firewall/enforcement/schemas/verdict.schema.json +222 -0
  14. package/bin/runners/lib/agent-firewall/enforcement/verdict-v2.js +333 -0
  15. package/bin/runners/lib/agent-firewall/index.js +200 -0
  16. package/bin/runners/lib/agent-firewall/integration/index.js +20 -0
  17. package/bin/runners/lib/agent-firewall/integration/ship-gate.js +437 -0
  18. package/bin/runners/lib/agent-firewall/intent/alignment-engine.js +634 -0
  19. package/bin/runners/lib/agent-firewall/intent/auto-detect.js +426 -0
  20. package/bin/runners/lib/agent-firewall/intent/index.js +102 -0
  21. package/bin/runners/lib/agent-firewall/intent/schema.js +352 -0
  22. package/bin/runners/lib/agent-firewall/intent/store.js +283 -0
  23. package/bin/runners/lib/agent-firewall/interception/fs-interceptor.js +502 -0
  24. package/bin/runners/lib/agent-firewall/interception/index.js +23 -0
  25. package/bin/runners/lib/agent-firewall/interceptor/base.js +7 -3
  26. package/bin/runners/lib/agent-firewall/session/collector.js +451 -0
  27. package/bin/runners/lib/agent-firewall/session/index.js +26 -0
  28. package/bin/runners/lib/artifact-envelope.js +540 -0
  29. package/bin/runners/lib/auth-shared.js +977 -0
  30. package/bin/runners/lib/checkpoint.js +941 -0
  31. package/bin/runners/lib/cleanup/engine.js +571 -0
  32. package/bin/runners/lib/cleanup/index.js +53 -0
  33. package/bin/runners/lib/cleanup/output.js +375 -0
  34. package/bin/runners/lib/cleanup/rules.js +1060 -0
  35. package/bin/runners/lib/doctor/diagnosis-receipt.js +454 -0
  36. package/bin/runners/lib/doctor/failure-signatures.js +526 -0
  37. package/bin/runners/lib/doctor/fix-script.js +336 -0
  38. package/bin/runners/lib/doctor/modules/build-tools.js +453 -0
  39. package/bin/runners/lib/doctor/modules/index.js +62 -3
  40. package/bin/runners/lib/doctor/modules/os-quirks.js +706 -0
  41. package/bin/runners/lib/doctor/modules/repo-integrity.js +485 -0
  42. package/bin/runners/lib/doctor/safe-repair.js +384 -0
  43. package/bin/runners/lib/engine/ast-cache.js +210 -210
  44. package/bin/runners/lib/engine/auth-extractor.js +211 -211
  45. package/bin/runners/lib/engine/billing-extractor.js +112 -112
  46. package/bin/runners/lib/engine/enforcement-extractor.js +100 -100
  47. package/bin/runners/lib/engine/env-extractor.js +207 -207
  48. package/bin/runners/lib/engine/express-extractor.js +208 -208
  49. package/bin/runners/lib/engine/extractors.js +849 -849
  50. package/bin/runners/lib/engine/index.js +207 -207
  51. package/bin/runners/lib/engine/repo-index.js +514 -514
  52. package/bin/runners/lib/engine/types.js +124 -124
  53. package/bin/runners/lib/engines/attack-detector.js +1192 -0
  54. package/bin/runners/lib/entitlements-v2.js +2 -2
  55. package/bin/runners/lib/missions/briefing.js +427 -0
  56. package/bin/runners/lib/missions/checkpoint.js +753 -0
  57. package/bin/runners/lib/missions/hardening.js +851 -0
  58. package/bin/runners/lib/missions/plan.js +421 -32
  59. package/bin/runners/lib/missions/safety-gates.js +645 -0
  60. package/bin/runners/lib/missions/schema.js +478 -0
  61. package/bin/runners/lib/packs/bundle.js +675 -0
  62. package/bin/runners/lib/packs/evidence-pack.js +671 -0
  63. package/bin/runners/lib/packs/pack-factory.js +837 -0
  64. package/bin/runners/lib/packs/permissions-pack.js +686 -0
  65. package/bin/runners/lib/packs/proof-graph-pack.js +779 -0
  66. package/bin/runners/lib/safelist/index.js +96 -0
  67. package/bin/runners/lib/safelist/integration.js +334 -0
  68. package/bin/runners/lib/safelist/matcher.js +696 -0
  69. package/bin/runners/lib/safelist/schema.js +948 -0
  70. package/bin/runners/lib/safelist/store.js +438 -0
  71. package/bin/runners/lib/schemas/ship-manifest.schema.json +251 -0
  72. package/bin/runners/lib/ship-gate.js +832 -0
  73. package/bin/runners/lib/ship-manifest.js +1153 -0
  74. package/bin/runners/lib/ship-output.js +1 -1
  75. package/bin/runners/lib/unified-cli-output.js +710 -383
  76. package/bin/runners/lib/upsell.js +3 -3
  77. package/bin/runners/lib/why-tree.js +650 -0
  78. package/bin/runners/runAllowlist.js +33 -4
  79. package/bin/runners/runApprove.js +240 -1122
  80. package/bin/runners/runAudit.js +692 -0
  81. package/bin/runners/runAuth.js +325 -29
  82. package/bin/runners/runCheckpoint.js +442 -494
  83. package/bin/runners/runCleanup.js +343 -0
  84. package/bin/runners/runDoctor.js +269 -19
  85. package/bin/runners/runFix.js +411 -32
  86. package/bin/runners/runForge.js +411 -0
  87. package/bin/runners/runIntent.js +906 -0
  88. package/bin/runners/runKickoff.js +878 -0
  89. package/bin/runners/runLaunch.js +2000 -0
  90. package/bin/runners/runLink.js +785 -0
  91. package/bin/runners/runMcp.js +1741 -837
  92. package/bin/runners/runPacks.js +2089 -0
  93. package/bin/runners/runPolish.js +41 -0
  94. package/bin/runners/runReality.js +178 -1
  95. package/bin/runners/runSafelist.js +1190 -0
  96. package/bin/runners/runScan.js +21 -9
  97. package/bin/runners/runShield.js +1282 -0
  98. package/bin/runners/runShip.js +395 -16
  99. package/bin/vibecheck.js +34 -6
  100. package/mcp-server/README.md +117 -158
  101. package/mcp-server/handlers/index.ts +2 -2
  102. package/mcp-server/handlers/tool-handler.ts +50 -11
  103. package/mcp-server/index.js +16 -0
  104. package/mcp-server/intent-firewall-interceptor.js +529 -0
  105. package/mcp-server/lib/executor.ts +5 -5
  106. package/mcp-server/lib/index.ts +14 -4
  107. package/mcp-server/lib/sandbox.test.ts +4 -4
  108. package/mcp-server/lib/sandbox.ts +2 -2
  109. package/mcp-server/manifest.json +473 -0
  110. package/mcp-server/package.json +1 -1
  111. package/mcp-server/registry/tool-registry.js +315 -523
  112. package/mcp-server/registry/tools.json +442 -428
  113. package/mcp-server/registry.test.ts +18 -12
  114. package/mcp-server/tier-auth.js +68 -11
  115. package/mcp-server/tools-v3.js +70 -16
  116. package/mcp-server/tsconfig.json +1 -0
  117. package/package.json +2 -1
  118. package/bin/runners/runProof.zip +0 -0
@@ -0,0 +1,571 @@
1
+ /**
2
+ * vibecheck polish --cleanup Engine
3
+ *
4
+ * ═══════════════════════════════════════════════════════════════════════════════
5
+ * CLEANUP ENGINE - Safe Code Transformations
6
+ * ═══════════════════════════════════════════════════════════════════════════════
7
+ *
8
+ * Applies cleanup rules to files with:
9
+ * - Full diff preview before changes
10
+ * - Automatic backup for rollback
11
+ * - Risk assessment for each transformation
12
+ * - Support for regex and function-based pattern detection
13
+ * - File type filtering and exclusion patterns
14
+ */
15
+
16
+ "use strict";
17
+
18
+ const fs = require("fs");
19
+ const path = require("path");
20
+ const {
21
+ SAFE_RULES,
22
+ MODERATE_RULES,
23
+ AGGRESSIVE_RULES,
24
+ CATEGORIES,
25
+ RISK_LEVELS,
26
+ } = require("./rules");
27
+
28
+ // ═══════════════════════════════════════════════════════════════════════════════
29
+ // CONFIGURATION
30
+ // ═══════════════════════════════════════════════════════════════════════════════
31
+
32
+ /**
33
+ * File extensions to scan by default
34
+ */
35
+ const SCANNABLE_EXTENSIONS = new Set([
36
+ ".js", ".jsx", ".ts", ".tsx", ".mjs", ".cjs",
37
+ ".vue", ".svelte", ".astro",
38
+ ]);
39
+
40
+ /**
41
+ * Directories to always skip
42
+ */
43
+ const SKIP_DIRS = new Set([
44
+ "node_modules", ".git", "dist", "build", ".next", ".nuxt",
45
+ "coverage", ".vibecheck", ".cache", "__pycache__", ".turbo",
46
+ "out", ".output", ".vercel", ".netlify", ".amplify",
47
+ ]);
48
+
49
+ /**
50
+ * Files to always skip (patterns)
51
+ */
52
+ const SKIP_FILES = [
53
+ /\.min\.js$/,
54
+ /\.bundle\.js$/,
55
+ /\.d\.ts$/,
56
+ /\.map$/,
57
+ /package-lock\.json$/,
58
+ /yarn\.lock$/,
59
+ /pnpm-lock\.yaml$/,
60
+ /\.lock$/,
61
+ ];
62
+
63
+ /**
64
+ * Maximum file size to scan (in bytes) - skip very large files
65
+ */
66
+ const MAX_FILE_SIZE = 1024 * 1024; // 1MB
67
+
68
+ // ═══════════════════════════════════════════════════════════════════════════════
69
+ // GLOB MATCHING
70
+ // ═══════════════════════════════════════════════════════════════════════════════
71
+
72
+ /**
73
+ * Simple glob pattern matching
74
+ * Supports: *, **, ?, [abc], [!abc]
75
+ */
76
+ function matchGlob(pattern, filePath) {
77
+ // Normalize paths
78
+ const normalizedPath = filePath.replace(/\\/g, "/");
79
+ const normalizedPattern = pattern.replace(/\\/g, "/");
80
+
81
+ // Convert glob to regex
82
+ let regex = normalizedPattern
83
+ .replace(/\./g, "\\.")
84
+ .replace(/\*\*/g, "{{GLOBSTAR}}")
85
+ .replace(/\*/g, "[^/]*")
86
+ .replace(/\?/g, "[^/]")
87
+ .replace(/{{GLOBSTAR}}/g, ".*");
88
+
89
+ // If pattern doesn't start with **, match anywhere
90
+ if (!normalizedPattern.startsWith("**/") && !normalizedPattern.startsWith("/")) {
91
+ regex = `(?:^|/)${regex}`;
92
+ }
93
+
94
+ return new RegExp(regex + "$", "i").test(normalizedPath);
95
+ }
96
+
97
+ /**
98
+ * Check if file matches any exclusion pattern
99
+ */
100
+ function isExcluded(filePath, excludePatterns = []) {
101
+ if (!excludePatterns || excludePatterns.length === 0) return false;
102
+
103
+ for (const pattern of excludePatterns) {
104
+ if (matchGlob(pattern, filePath)) {
105
+ return true;
106
+ }
107
+ }
108
+
109
+ return false;
110
+ }
111
+
112
+ // ═══════════════════════════════════════════════════════════════════════════════
113
+ // FILE SCANNING
114
+ // ═══════════════════════════════════════════════════════════════════════════════
115
+
116
+ /**
117
+ * Check if a file should be skipped
118
+ */
119
+ function shouldSkipFile(filePath, fileName) {
120
+ // Check extension
121
+ const ext = path.extname(fileName).toLowerCase();
122
+ if (!SCANNABLE_EXTENSIONS.has(ext)) return true;
123
+
124
+ // Check skip patterns
125
+ for (const pattern of SKIP_FILES) {
126
+ if (pattern.test(fileName)) return true;
127
+ }
128
+
129
+ return false;
130
+ }
131
+
132
+ /**
133
+ * Recursively find all scannable files with robust error handling
134
+ */
135
+ async function findFiles(dir, opts = {}) {
136
+ const {
137
+ maxDepth = 10,
138
+ maxFiles = 5000,
139
+ exclude = [],
140
+ } = opts;
141
+
142
+ const results = [];
143
+ const errors = [];
144
+
145
+ async function walk(currentDir, depth) {
146
+ if (depth >= maxDepth) return;
147
+ if (results.length >= maxFiles) return;
148
+
149
+ let entries;
150
+ try {
151
+ entries = await fs.promises.readdir(currentDir, { withFileTypes: true });
152
+ } catch (err) {
153
+ if (err.code !== "ENOENT" && err.code !== "EACCES" && err.code !== "EPERM") {
154
+ errors.push({ path: currentDir, error: err.message });
155
+ }
156
+ return;
157
+ }
158
+
159
+ for (const entry of entries) {
160
+ if (results.length >= maxFiles) break;
161
+
162
+ // Skip hidden files/dirs (except .cursorrules, etc.)
163
+ if (entry.name.startsWith(".") && !entry.name.startsWith(".cursor")) continue;
164
+
165
+ const fullPath = path.join(currentDir, entry.name);
166
+
167
+ // Check exclusion patterns
168
+ if (isExcluded(fullPath, exclude)) continue;
169
+
170
+ if (entry.isDirectory()) {
171
+ if (SKIP_DIRS.has(entry.name)) continue;
172
+ await walk(fullPath, depth + 1);
173
+ } else if (entry.isFile()) {
174
+ if (shouldSkipFile(fullPath, entry.name)) continue;
175
+
176
+ // Check file size
177
+ try {
178
+ const stat = await fs.promises.stat(fullPath);
179
+ if (stat.size > MAX_FILE_SIZE) continue;
180
+ } catch {
181
+ continue;
182
+ }
183
+
184
+ results.push(fullPath);
185
+ }
186
+ }
187
+ }
188
+
189
+ await walk(dir, 0);
190
+
191
+ return {
192
+ files: results,
193
+ errors,
194
+ truncated: results.length >= maxFiles,
195
+ };
196
+ }
197
+
198
+ // ═══════════════════════════════════════════════════════════════════════════════
199
+ // FINDING DETECTION
200
+ // ═══════════════════════════════════════════════════════════════════════════════
201
+
202
+ /**
203
+ * Analyze a single file for cleanup findings
204
+ */
205
+ function analyzeFile(filePath, content, rules) {
206
+ const findings = [];
207
+ const lines = content.split("\n");
208
+
209
+ for (const [ruleId, rule] of Object.entries(rules)) {
210
+ if (!rule.pattern) continue;
211
+
212
+ // Handle array of patterns
213
+ const patterns = Array.isArray(rule.patterns) ? rule.patterns : [rule.pattern];
214
+
215
+ for (const pattern of patterns) {
216
+ // Reset lastIndex for global patterns
217
+ if (pattern.global) {
218
+ pattern.lastIndex = 0;
219
+ }
220
+
221
+ let match;
222
+ while ((match = pattern.exec(content)) !== null) {
223
+ // Calculate line number
224
+ const beforeMatch = content.substring(0, match.index);
225
+ const lineNumber = beforeMatch.split("\n").length;
226
+ const lineContent = lines[lineNumber - 1] || "";
227
+
228
+ findings.push({
229
+ ruleId,
230
+ rule,
231
+ file: filePath,
232
+ line: lineNumber,
233
+ column: match.index - beforeMatch.lastIndexOf("\n") - 1,
234
+ match: match[0],
235
+ lineContent: lineContent.trim(),
236
+ startIndex: match.index,
237
+ endIndex: match.index + match[0].length,
238
+ });
239
+
240
+ // Prevent infinite loop for non-global patterns
241
+ if (!pattern.global) break;
242
+ }
243
+ }
244
+ }
245
+
246
+ return findings;
247
+ }
248
+
249
+ /**
250
+ * Analyze entire project
251
+ */
252
+ async function analyzeProject(projectPath, opts = {}) {
253
+ const {
254
+ aggressive = false,
255
+ includeModerate = true,
256
+ maxFiles = 1000,
257
+ } = opts;
258
+
259
+ // Build rule set based on options
260
+ let rules = { ...SAFE_RULES };
261
+ if (includeModerate) {
262
+ rules = { ...rules, ...MODERATE_RULES };
263
+ }
264
+ if (aggressive) {
265
+ rules = { ...rules, ...AGGRESSIVE_RULES };
266
+ }
267
+
268
+ const files = await findFiles(projectPath);
269
+ const limitedFiles = files.slice(0, maxFiles);
270
+
271
+ const allFindings = [];
272
+ const fileResults = [];
273
+
274
+ for (const filePath of limitedFiles) {
275
+ try {
276
+ const content = await fs.promises.readFile(filePath, "utf8");
277
+ const findings = analyzeFile(filePath, content, rules);
278
+
279
+ if (findings.length > 0) {
280
+ fileResults.push({
281
+ file: filePath,
282
+ relativePath: path.relative(projectPath, filePath),
283
+ findingCount: findings.length,
284
+ findings,
285
+ });
286
+ allFindings.push(...findings);
287
+ }
288
+ } catch (err) {
289
+ // Skip files that can't be read
290
+ }
291
+ }
292
+
293
+ // Group by risk level
294
+ const byRisk = {
295
+ safe: allFindings.filter(f => f.rule.riskLevel === "safe"),
296
+ moderate: allFindings.filter(f => f.rule.riskLevel === "moderate"),
297
+ aggressive: allFindings.filter(f => f.rule.riskLevel === "aggressive"),
298
+ };
299
+
300
+ // Group by category
301
+ const byCategory = {};
302
+ for (const finding of allFindings) {
303
+ const cat = finding.rule.category;
304
+ if (!byCategory[cat]) byCategory[cat] = [];
305
+ byCategory[cat].push(finding);
306
+ }
307
+
308
+ return {
309
+ totalFiles: limitedFiles.length,
310
+ filesWithFindings: fileResults.length,
311
+ totalFindings: allFindings.length,
312
+ byRisk,
313
+ byCategory,
314
+ fileResults,
315
+ findings: allFindings,
316
+ truncated: files.length > maxFiles,
317
+ };
318
+ }
319
+
320
+ // ═══════════════════════════════════════════════════════════════════════════════
321
+ // TRANSFORMATION ENGINE
322
+ // ═══════════════════════════════════════════════════════════════════════════════
323
+
324
+ /**
325
+ * Calculate the diff for a single file
326
+ */
327
+ function calculateFileDiff(content, findings) {
328
+ // Sort findings by position (reverse order for safe replacement)
329
+ const sortedFindings = [...findings].sort((a, b) => b.startIndex - a.startIndex);
330
+
331
+ let newContent = content;
332
+ const changes = [];
333
+
334
+ for (const finding of sortedFindings) {
335
+ if (!finding.rule.autoFixable) continue;
336
+ if (finding.rule.fix === null || finding.rule.fix === undefined) continue;
337
+
338
+ const before = newContent.substring(
339
+ Math.max(0, finding.startIndex - 20),
340
+ finding.startIndex
341
+ );
342
+ const after = newContent.substring(
343
+ finding.endIndex,
344
+ Math.min(newContent.length, finding.endIndex + 20)
345
+ );
346
+
347
+ changes.push({
348
+ ruleId: finding.ruleId,
349
+ line: finding.line,
350
+ removed: finding.match,
351
+ added: finding.rule.fix,
352
+ context: { before, after },
353
+ });
354
+
355
+ // Apply the fix
356
+ newContent =
357
+ newContent.substring(0, finding.startIndex) +
358
+ finding.rule.fix +
359
+ newContent.substring(finding.endIndex);
360
+ }
361
+
362
+ return {
363
+ originalContent: content,
364
+ newContent,
365
+ changes,
366
+ changed: newContent !== content,
367
+ };
368
+ }
369
+
370
+ /**
371
+ * Calculate diffs for all files
372
+ */
373
+ async function calculateDiffs(projectPath, analysisResult, opts = {}) {
374
+ const { dryRun = true } = opts;
375
+
376
+ const diffs = [];
377
+
378
+ for (const fileResult of analysisResult.fileResults) {
379
+ try {
380
+ const content = await fs.promises.readFile(fileResult.file, "utf8");
381
+ const diff = calculateFileDiff(content, fileResult.findings);
382
+
383
+ if (diff.changed) {
384
+ diffs.push({
385
+ file: fileResult.file,
386
+ relativePath: fileResult.relativePath,
387
+ ...diff,
388
+ });
389
+ }
390
+ } catch (err) {
391
+ // Skip files that can't be processed
392
+ }
393
+ }
394
+
395
+ return {
396
+ filesChanged: diffs.length,
397
+ totalChanges: diffs.reduce((sum, d) => sum + d.changes.length, 0),
398
+ diffs,
399
+ };
400
+ }
401
+
402
+ /**
403
+ * Apply transformations to files
404
+ */
405
+ async function applyTransformations(projectPath, diffs, backupDir) {
406
+ const applied = [];
407
+ const failed = [];
408
+
409
+ // Create backup manifest
410
+ const backupManifest = {
411
+ timestamp: new Date().toISOString(),
412
+ projectPath,
413
+ files: [],
414
+ };
415
+
416
+ // Backup and transform each file
417
+ for (const diff of diffs) {
418
+ try {
419
+ // Backup original
420
+ const backupPath = path.join(backupDir, diff.relativePath);
421
+ await fs.promises.mkdir(path.dirname(backupPath), { recursive: true });
422
+ await fs.promises.writeFile(backupPath, diff.originalContent, "utf8");
423
+
424
+ backupManifest.files.push({
425
+ original: diff.file,
426
+ backup: backupPath,
427
+ relativePath: diff.relativePath,
428
+ });
429
+
430
+ // Apply transformation
431
+ await fs.promises.writeFile(diff.file, diff.newContent, "utf8");
432
+
433
+ applied.push({
434
+ file: diff.file,
435
+ relativePath: diff.relativePath,
436
+ changes: diff.changes.length,
437
+ });
438
+ } catch (err) {
439
+ failed.push({
440
+ file: diff.file,
441
+ error: err.message,
442
+ });
443
+ }
444
+ }
445
+
446
+ // Save backup manifest
447
+ await fs.promises.writeFile(
448
+ path.join(backupDir, "_manifest.json"),
449
+ JSON.stringify(backupManifest, null, 2),
450
+ "utf8"
451
+ );
452
+
453
+ return {
454
+ applied,
455
+ failed,
456
+ backupDir,
457
+ backupManifest,
458
+ };
459
+ }
460
+
461
+ /**
462
+ * Rollback transformations using backup
463
+ */
464
+ async function rollbackTransformations(backupDir) {
465
+ const manifestPath = path.join(backupDir, "_manifest.json");
466
+
467
+ if (!fs.existsSync(manifestPath)) {
468
+ return { success: false, error: "Backup manifest not found" };
469
+ }
470
+
471
+ const manifest = JSON.parse(await fs.promises.readFile(manifestPath, "utf8"));
472
+ const restored = [];
473
+ const failed = [];
474
+
475
+ for (const file of manifest.files) {
476
+ try {
477
+ const backupContent = await fs.promises.readFile(file.backup, "utf8");
478
+ await fs.promises.writeFile(file.original, backupContent, "utf8");
479
+ restored.push(file.original);
480
+ } catch (err) {
481
+ failed.push({ file: file.original, error: err.message });
482
+ }
483
+ }
484
+
485
+ return {
486
+ success: failed.length === 0,
487
+ restored,
488
+ failed,
489
+ };
490
+ }
491
+
492
+ // ═══════════════════════════════════════════════════════════════════════════════
493
+ // RISK ASSESSMENT
494
+ // ═══════════════════════════════════════════════════════════════════════════════
495
+
496
+ /**
497
+ * Generate risk assessment for transformations
498
+ */
499
+ function assessRisk(analysisResult, diffsResult) {
500
+ const risks = [];
501
+
502
+ // Count by risk level
503
+ const safeCount = analysisResult.byRisk.safe.length;
504
+ const moderateCount = analysisResult.byRisk.moderate.length;
505
+ const aggressiveCount = analysisResult.byRisk.aggressive.length;
506
+
507
+ // Safe transformations
508
+ if (safeCount > 0) {
509
+ risks.push({
510
+ level: "safe",
511
+ count: safeCount,
512
+ description: "Safe transformations (cannot change behavior)",
513
+ recommendation: "Apply automatically",
514
+ });
515
+ }
516
+
517
+ // Moderate transformations
518
+ if (moderateCount > 0) {
519
+ risks.push({
520
+ level: "moderate",
521
+ count: moderateCount,
522
+ description: "Moderate transformations (generally safe, worth reviewing)",
523
+ recommendation: "Review diff before applying",
524
+ });
525
+ }
526
+
527
+ // Aggressive transformations
528
+ if (aggressiveCount > 0) {
529
+ risks.push({
530
+ level: "aggressive",
531
+ count: aggressiveCount,
532
+ description: "Aggressive transformations (could change behavior)",
533
+ recommendation: "Manual review required, use --aggressive --yes-i-am-sure",
534
+ });
535
+ }
536
+
537
+ // Overall risk score (0-100)
538
+ const totalFindings = safeCount + moderateCount + aggressiveCount;
539
+ const riskScore = totalFindings > 0
540
+ ? Math.round((moderateCount * 25 + aggressiveCount * 75) / totalFindings)
541
+ : 0;
542
+
543
+ return {
544
+ score: riskScore,
545
+ level: riskScore < 25 ? "low" : riskScore < 50 ? "moderate" : "high",
546
+ risks,
547
+ summary: {
548
+ safe: safeCount,
549
+ moderate: moderateCount,
550
+ aggressive: aggressiveCount,
551
+ total: totalFindings,
552
+ },
553
+ };
554
+ }
555
+
556
+ // ═══════════════════════════════════════════════════════════════════════════════
557
+ // EXPORTS
558
+ // ═══════════════════════════════════════════════════════════════════════════════
559
+
560
+ module.exports = {
561
+ findFiles,
562
+ analyzeFile,
563
+ analyzeProject,
564
+ calculateFileDiff,
565
+ calculateDiffs,
566
+ applyTransformations,
567
+ rollbackTransformations,
568
+ assessRisk,
569
+ SCANNABLE_EXTENSIONS,
570
+ SKIP_DIRS,
571
+ };
@@ -0,0 +1,53 @@
1
+ /**
2
+ * vibecheck polish --cleanup Module Index
3
+ *
4
+ * ═══════════════════════════════════════════════════════════════════════════════
5
+ * PRODUCTION-GRADE CLEANUP WITHOUT CHAOS
6
+ * ═══════════════════════════════════════════════════════════════════════════════
7
+ */
8
+
9
+ "use strict";
10
+
11
+ const rules = require("./rules");
12
+ const engine = require("./engine");
13
+ const output = require("./output");
14
+
15
+ module.exports = {
16
+ // Rules
17
+ SAFE_RULES: rules.SAFE_RULES,
18
+ MODERATE_RULES: rules.MODERATE_RULES,
19
+ AGGRESSIVE_RULES: rules.AGGRESSIVE_RULES,
20
+ CATEGORIES: rules.CATEGORIES,
21
+ RISK_LEVELS: rules.RISK_LEVELS,
22
+ getAllRules: rules.getAllRules,
23
+ getSafeRules: rules.getSafeRules,
24
+ getModerateRules: rules.getModerateRules,
25
+ getAggressiveRules: rules.getAggressiveRules,
26
+ getRuleById: rules.getRuleById,
27
+ getRulesByCategory: rules.getRulesByCategory,
28
+ getRulesByRiskLevel: rules.getRulesByRiskLevel,
29
+
30
+ // Engine
31
+ findFiles: engine.findFiles,
32
+ analyzeFile: engine.analyzeFile,
33
+ analyzeProject: engine.analyzeProject,
34
+ calculateFileDiff: engine.calculateFileDiff,
35
+ calculateDiffs: engine.calculateDiffs,
36
+ applyTransformations: engine.applyTransformations,
37
+ rollbackTransformations: engine.rollbackTransformations,
38
+ assessRisk: engine.assessRisk,
39
+
40
+ // Output
41
+ printBanner: output.printBanner,
42
+ printAnalysisSummary: output.printAnalysisSummary,
43
+ printRiskAssessment: output.printRiskAssessment,
44
+ printFindingsByCategory: output.printFindingsByCategory,
45
+ printDiffPreview: output.printDiffPreview,
46
+ printApplyResults: output.printApplyResults,
47
+ printRollbackInfo: output.printRollbackInfo,
48
+ printAggressiveWarning: output.printAggressiveWarning,
49
+ printNextSteps: output.printNextSteps,
50
+ formatJsonOutput: output.formatJsonOutput,
51
+ colors: output.c,
52
+ icons: output.icons,
53
+ };