@vibecheckai/cli 3.1.6 → 3.2.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 (56) hide show
  1. package/README.md +27 -32
  2. package/bin/registry.js +208 -343
  3. package/bin/runners/context/generators/mcp.js +18 -0
  4. package/bin/runners/context/index.js +72 -4
  5. package/bin/runners/context/proof-context.js +293 -1
  6. package/bin/runners/context/security-scanner.js +311 -73
  7. package/bin/runners/lib/analyzers.js +607 -20
  8. package/bin/runners/lib/detectors-v2.js +172 -15
  9. package/bin/runners/lib/entitlements-v2.js +48 -1
  10. package/bin/runners/lib/evidence-pack.js +678 -0
  11. package/bin/runners/lib/html-proof-report.js +913 -0
  12. package/bin/runners/lib/missions/plan.js +231 -41
  13. package/bin/runners/lib/missions/templates.js +125 -0
  14. package/bin/runners/lib/scan-output.js +492 -253
  15. package/bin/runners/lib/ship-output.js +901 -641
  16. package/bin/runners/runCheckpoint.js +44 -3
  17. package/bin/runners/runContext.d.ts +4 -0
  18. package/bin/runners/runContext.js +2 -3
  19. package/bin/runners/runDoctor.js +11 -4
  20. package/bin/runners/runFix.js +51 -341
  21. package/bin/runners/runInit.js +37 -20
  22. package/bin/runners/runPolish.d.ts +4 -0
  23. package/bin/runners/runPolish.js +608 -29
  24. package/bin/runners/runProve.js +210 -25
  25. package/bin/runners/runReality.js +861 -107
  26. package/bin/runners/runScan.js +238 -4
  27. package/bin/runners/runShip.js +19 -3
  28. package/bin/runners/runWatch.js +25 -5
  29. package/bin/vibecheck.js +35 -47
  30. package/mcp-server/consolidated-tools.js +408 -42
  31. package/mcp-server/index.js +152 -15
  32. package/mcp-server/package.json +1 -1
  33. package/mcp-server/proof-tools.js +571 -0
  34. package/mcp-server/tier-auth.js +22 -19
  35. package/mcp-server/tools-v3.js +744 -0
  36. package/mcp-server/truth-firewall-tools.js +190 -4
  37. package/package.json +3 -1
  38. package/bin/runners/runBadge.js +0 -916
  39. package/bin/runners/runContracts.js +0 -105
  40. package/bin/runners/runCtx.js +0 -680
  41. package/bin/runners/runCtxDiff.js +0 -301
  42. package/bin/runners/runCtxGuard.js +0 -176
  43. package/bin/runners/runCtxSync.js +0 -116
  44. package/bin/runners/runExport.js +0 -93
  45. package/bin/runners/runGraph.js +0 -454
  46. package/bin/runners/runInstall.js +0 -273
  47. package/bin/runners/runLabs.js +0 -341
  48. package/bin/runners/runLaunch.js +0 -181
  49. package/bin/runners/runPR.js +0 -255
  50. package/bin/runners/runPermissions.js +0 -310
  51. package/bin/runners/runPreflight.js +0 -580
  52. package/bin/runners/runReplay.js +0 -499
  53. package/bin/runners/runSecurity.js +0 -92
  54. package/bin/runners/runShare.js +0 -212
  55. package/bin/runners/runStatus.js +0 -102
  56. package/bin/runners/runVerify.js +0 -272
@@ -1,499 +0,0 @@
1
- /**
2
- * Vibecheck Replay CLI
3
- *
4
- * Commands:
5
- * vibecheck replay record <url> [options] Record a user session
6
- * vibecheck replay play <capsule> Replay a recorded session
7
- * vibecheck replay list List available replay capsules
8
- * vibecheck replay show <id> Show details of a replay capsule
9
- * vibecheck replay delete <id> Delete a replay capsule
10
- * vibecheck replay export <id> <file> Export a replay capsule
11
- * vibecheck replay import <file> Import a replay capsule
12
- */
13
-
14
- const { Command } = require('commander');
15
- const path = require('path');
16
-
17
- // Lazy-load heavy dependencies
18
- let chalk, fs, chromium, createReplayEngine, version;
19
-
20
- // Global options
21
- let verbose = false;
22
- let outputDir = path.join(process.cwd(), '.vibecheck', 'replays');
23
-
24
- // Initialize replay engine
25
- let replayEngine;
26
-
27
- /**
28
- * Load heavy dependencies on demand
29
- */
30
- function loadDeps() {
31
- if (!chalk) {
32
- chalk = require('chalk');
33
- fs = require('fs').promises;
34
- chromium = require('playwright').chromium;
35
- createReplayEngine = require('./lib/replay').createReplayEngine;
36
- try {
37
- version = require('../../package.json').version;
38
- } catch {
39
- version = '0.0.0';
40
- }
41
- }
42
- }
43
-
44
- /**
45
- * Initialize the replay engine
46
- */
47
- async function initEngine() {
48
- loadDeps();
49
- if (!replayEngine) {
50
- replayEngine = await createReplayEngine({
51
- basePath: process.cwd(),
52
- verbose
53
- });
54
- }
55
- return replayEngine;
56
- }
57
-
58
- /**
59
- * Record a user session
60
- */
61
- async function recordSession(url, options) {
62
- loadDeps();
63
- console.log(chalk.blue(`\n🚀 Starting recording session for ${url}\n`));
64
-
65
- const browser = await chromium.launch({
66
- headless: !options.headed,
67
- devtools: options.devtools
68
- });
69
-
70
- try {
71
- const context = await browser.newContext({
72
- viewport: options.viewport || { width: 1280, height: 800 },
73
- recordVideo: options.video ? { dir: path.join(outputDir, 'videos') } : undefined
74
- });
75
-
76
- const page = await context.newPage();
77
-
78
- // Initialize recorder
79
- const engine = await initEngine();
80
- const recorder = engine.createPlaywrightRecorder(page, {
81
- name: options.name || `Session at ${new Date().toLocaleString()}`,
82
- description: options.description || `Recorded from ${url}`,
83
- tags: options.tags ? options.tags.split(',') : []
84
- });
85
-
86
- // Set up event listeners
87
- recorder.on('start', () => {
88
- console.log(chalk.green('\n🔴 Recording started. Press Ctrl+C to stop.\n'));
89
- });
90
-
91
- recorder.on('step', (step) => {
92
- if (verbose) {
93
- console.log(chalk.gray(` [${step.type}] ${JSON.stringify(step).slice(0, 100)}...`));
94
- }
95
- });
96
-
97
- // Navigate to the URL
98
- await page.goto(url, { waitUntil: 'networkidle' });
99
-
100
- // Wait for user to stop recording
101
- console.log(chalk.blue('\n🛑 Press Ctrl+C when done recording...'));
102
-
103
- // Handle process termination
104
- const stopRecording = async () => {
105
- console.log('\n\nStopping recording...');
106
- const result = await recorder.save(outputDir);
107
- console.log(chalk.green(`\n✅ Recording saved as capsule: ${result.id}`));
108
- console.log(chalk.gray(`Path: ${result.path}`));
109
-
110
- await browser.close();
111
- process.exit(0);
112
- };
113
-
114
- process.on('SIGINT', stopRecording);
115
-
116
- // If auto-stop is enabled, wait for the specified duration
117
- if (options.duration) {
118
- console.log(`Will auto-stop after ${options.duration} seconds...`);
119
- setTimeout(stopRecording, options.duration * 1000);
120
- }
121
-
122
- } catch (error) {
123
- console.error(chalk.red('\n❌ Error during recording:'), error);
124
- await browser.close();
125
- process.exit(1);
126
- }
127
- }
128
-
129
- /**
130
- * Replay a recorded session
131
- */
132
- async function replaySession(capsuleId, options) {
133
- loadDeps();
134
- console.log(chalk.blue(`\n▶️ Replaying capsule: ${capsuleId}\n`));
135
-
136
- const browser = await chromium.launch({
137
- headless: !options.headed,
138
- devtools: options.devtools
139
- });
140
-
141
- try {
142
- const engine = await initEngine();
143
- const capsule = await engine.getCapsule(capsuleId);
144
-
145
- if (!capsule) {
146
- console.error(chalk.red(`\n❌ Capsule not found: ${capsuleId}`));
147
- process.exit(1);
148
- }
149
-
150
- console.log(chalk.blue(`📼 ${capsule.metadata.name || 'Untitled Session'}`));
151
- if (capsule.metadata.description) {
152
- console.log(chalk.gray(` ${capsule.metadata.description}`));
153
- }
154
- console.log(chalk.gray(` ${capsule.metadata.steps} steps, recorded ${new Date(capsule.metadata.createdAt).toLocaleString()}\n`));
155
-
156
- const player = engine.createPlayer({
157
- speed: options.speed,
158
- headless: !options.headed,
159
- waitForNetworkIdle: true,
160
- stopOnFailure: options.stopOnFailure
161
- });
162
-
163
- await player.loadCapsule(capsule);
164
- await player.setupPlaywright(browser);
165
-
166
- // Set up event listeners
167
- player.on('stepStart', ({ step, total, type }) => {
168
- process.stdout.write(chalk.blue(`\r[${step}/${total}] ${type}...`));
169
- });
170
-
171
- player.on('stepPass', ({ step, type, duration }) => {
172
- process.stdout.write(chalk.green(`\r[${step}] ${type} ✓ (${duration.toFixed(0)}ms)\n`));
173
- });
174
-
175
- player.on('stepFail', ({ step, type, error, duration }) => {
176
- process.stdout.write(chalk.red(`\r[${step}] ${type} ✗ (${duration.toFixed(0)}ms)\n`));
177
- if (verbose) {
178
- console.error(chalk.red(` Error: ${error.message}`));
179
- if (error.stack) {
180
- console.error(chalk.gray(error.stack.split('\n').slice(1).join('\n')));
181
- }
182
- }
183
- });
184
-
185
- player.on('complete', (result) => {
186
- console.log('\n');
187
- console.log(chalk.bold('🏁 Replay Complete'));
188
- console.log(chalk.gray('━'.repeat(40)));
189
- console.log(` Steps: ${result.passed + result.failed + result.skipped} total`);
190
- console.log(` ${chalk.green(`✓ ${result.passed} passed`)}`);
191
- if (result.failed > 0) {
192
- console.log(` ${chalk.red(`✗ ${result.failed} failed`)}`);
193
- }
194
- if (result.skipped > 0) {
195
- console.log(` ${chalk.yellow(`↷ ${result.skipped} skipped`)}`);
196
- }
197
- console.log(`\n Duration: ${(result.duration / 1000).toFixed(2)}s`);
198
- console.log(chalk.gray('━'.repeat(40) + '\n'));
199
-
200
- if (result.failed > 0) {
201
- console.log(chalk.red('❌ Some steps failed during replay'));
202
- process.exit(1);
203
- } else {
204
- console.log(chalk.green('✅ Replay completed successfully'));
205
- process.exit(0);
206
- }
207
- });
208
-
209
- // Start playback
210
- await player.play();
211
-
212
- } catch (error) {
213
- console.error(chalk.red('\n❌ Error during replay:'), error);
214
- await browser.close();
215
- process.exit(1);
216
- }
217
- }
218
-
219
- /**
220
- * List available replay capsules
221
- */
222
- async function listCapsules() {
223
- try {
224
- const engine = await initEngine();
225
- const capsules = await engine.listCapsules();
226
-
227
- if (capsules.length === 0) {
228
- console.log(chalk.yellow('\nNo replay capsules found.\n'));
229
- return;
230
- }
231
-
232
- console.log(chalk.blue('\n📼 Available Replay Capsules\n'));
233
-
234
- capsules.forEach((capsule, index) => {
235
- const { id, name, timestamp, metadata } = capsule;
236
- const date = new Date(timestamp).toLocaleString();
237
- const steps = metadata.steps || '?';
238
-
239
- console.log(chalk.bold(`${index + 1}. ${name || 'Untitled Session'}`));
240
- console.log(` ID: ${chalk.gray(id)}`);
241
- console.log(` Steps: ${chalk.cyan(steps)}`);
242
- console.log(` Recorded: ${chalk.gray(date)}`);
243
-
244
- if (metadata.tags && metadata.tags.length > 0) {
245
- console.log(` Tags: ${metadata.tags.map(t => chalk.cyan(`#${t}`)).join(' ')}`);
246
- }
247
-
248
- if (metadata.description) {
249
- console.log(` ${chalk.gray(metadata.description)}`);
250
- }
251
-
252
- console.log();
253
- });
254
-
255
- } catch (error) {
256
- console.error(chalk.red('\n❌ Error listing capsules:'), error);
257
- process.exit(1);
258
- }
259
- }
260
-
261
- /**
262
- * Show details of a specific capsule
263
- */
264
- async function showCapsule(capsuleId) {
265
- try {
266
- const engine = await initEngine();
267
- const capsule = await engine.getCapsule(capsuleId);
268
-
269
- if (!capsule) {
270
- console.error(chalk.red(`\n❌ Capsule not found: ${capsuleId}`));
271
- process.exit(1);
272
- }
273
-
274
- const { metadata, steps } = capsule;
275
-
276
- console.log(chalk.blue('\n📼 Replay Capsule Details\n'));
277
- console.log(chalk.bold(metadata.name || 'Untitled Session'));
278
- console.log(chalk.gray('━'.repeat(60)));
279
-
280
- // Basic info
281
- console.log(chalk.bold('\n📋 Metadata'));
282
- console.log(` ID: ${chalk.gray(metadata.id)}`);
283
- console.log(` Recorded: ${chalk.gray(new Date(metadata.createdAt).toLocaleString())}`);
284
- console.log(` Duration: ${chalk.cyan((metadata.duration / 1000).toFixed(2))}s`);
285
- console.log(` Steps: ${chalk.cyan(steps.length)}`);
286
-
287
- if (metadata.tags && metadata.tags.length > 0) {
288
- console.log(` Tags: ${metadata.tags.map(t => chalk.cyan(`#${t}`)).join(' ')}`);
289
- }
290
-
291
- if (metadata.url) {
292
- console.log(` URL: ${chalk.cyan(metadata.url)}`);
293
- }
294
-
295
- if (metadata.userAgent) {
296
- console.log(` User Agent: ${chalk.gray(metadata.userAgent)}`);
297
- }
298
-
299
- if (metadata.viewport) {
300
- console.log(` Viewport: ${metadata.viewport.width}x${metadata.viewport.height}`);
301
- }
302
-
303
- // Description
304
- if (metadata.description) {
305
- console.log(`\n${chalk.bold('📝 Description')}\n${metadata.description}`);
306
- }
307
-
308
- // Steps summary
309
- console.log(`\n${chalk.bold('🔍 Steps')}`);
310
-
311
- const stepTypes = {};
312
- steps.forEach(step => {
313
- stepTypes[step.type] = (stepTypes[step.type] || 0) + 1;
314
- });
315
-
316
- console.log(' ' + Object.entries(stepTypes)
317
- .map(([type, count]) => `${chalk.cyan(type)}: ${count}`)
318
- .join(', '));
319
-
320
- // Sample of steps
321
- console.log(`\n${chalk.bold('🔎 Sample Steps')}`);
322
- const sampleSize = Math.min(5, steps.length);
323
- steps.slice(0, sampleSize).forEach((step, i) => {
324
- const time = (step.timestamp / 1000).toFixed(2).padStart(6, ' ');
325
- const type = step.type.padEnd(15, ' ');
326
- let details = '';
327
-
328
- switch (step.type) {
329
- case 'navigation':
330
- details = step.url;
331
- break;
332
- case 'click':
333
- details = step.selector || `x:${step.x}, y:${step.y}`;
334
- break;
335
- case 'input':
336
- details = `${step.selector} = "${step.value}"`;
337
- break;
338
- default:
339
- details = JSON.stringify(step).slice(0, 50) + '...';
340
- }
341
-
342
- console.log(` ${chalk.gray(time + 's')} ${chalk.cyan(type)} ${details}`);
343
- });
344
-
345
- if (steps.length > sampleSize) {
346
- console.log(` ... and ${steps.length - sampleSize} more steps`);
347
- }
348
-
349
- console.log();
350
-
351
- } catch (error) {
352
- console.error(chalk.red('\n❌ Error showing capsule:'), error);
353
- process.exit(1);
354
- }
355
- }
356
-
357
- /**
358
- * Delete a replay capsule
359
- */
360
- async function deleteCapsule(capsuleId) {
361
- try {
362
- const engine = await initEngine();
363
- await engine.deleteCapsule(capsuleId);
364
- console.log(chalk.green(`\n✅ Deleted capsule: ${capsuleId}\n`));
365
- } catch (error) {
366
- console.error(chalk.red('\n❌ Error deleting capsule:'), error);
367
- process.exit(1);
368
- }
369
- }
370
-
371
- /**
372
- * Export a replay capsule to a file
373
- */
374
- async function exportCapsule(capsuleId, outputFile) {
375
- try {
376
- const engine = await initEngine();
377
- const outputPath = await engine.exportCapsule(capsuleId, outputFile);
378
- console.log(chalk.green(`\n✅ Exported capsule to: ${outputPath}\n`));
379
- } catch (error) {
380
- console.error(chalk.red('\n❌ Error exporting capsule:'), error);
381
- process.exit(1);
382
- }
383
- }
384
-
385
- /**
386
- * Import a replay capsule from a file
387
- */
388
- async function importCapsule(filePath) {
389
- try {
390
- const engine = await initEngine();
391
- const result = await engine.importCapsule(filePath);
392
- console.log(chalk.green(`\n✅ Imported capsule: ${result.id}`));
393
- console.log(chalk.gray(`Path: ${result.path}\n`));
394
- } catch (error) {
395
- console.error(chalk.red('\n❌ Error importing capsule:'), error);
396
- process.exit(1);
397
- }
398
- }
399
-
400
- /**
401
- * Main entry point for replay command
402
- * Only parses args when explicitly called
403
- */
404
- async function runReplay(args = []) {
405
- loadDeps();
406
-
407
- const program = new Command();
408
-
409
- program
410
- .name('vibecheck replay')
411
- .description('Record and replay user interactions for testing and debugging')
412
- .version(version || '0.0.0')
413
- .option('-v, --verbose', 'Enable verbose output', false)
414
- .option('--output-dir <dir>', 'Directory to save replay files', outputDir)
415
- .hook('preAction', (thisCommand) => {
416
- verbose = thisCommand.opts().verbose;
417
- outputDir = thisCommand.opts().outputDir || outputDir;
418
- });
419
-
420
- // Record command
421
- program
422
- .command('record <url>')
423
- .description('Record a user session')
424
- .option('-n, --name <name>', 'Name for this recording')
425
- .option('-d, --description <description>', 'Description of the recording')
426
- .option('--tags <tags>', 'Comma-separated list of tags')
427
- .option('--duration <seconds>', 'Auto-stop after specified seconds')
428
- .option('--headed', 'Run browser in headed mode', false)
429
- .option('--devtools', 'Open devtools', false)
430
- .option('--video', 'Record video', false)
431
- .action((url, options) => {
432
- recordSession(url, options).catch(console.error);
433
- });
434
-
435
- // Play command
436
- program
437
- .command('play <capsule>')
438
- .description('Replay a recorded session')
439
- .option('--speed <speed>', 'Playback speed (1.0 = normal, 2.0 = 2x, etc.)', parseFloat, 1.0)
440
- .option('--headed', 'Run browser in headed mode', false)
441
- .option('--devtools', 'Open devtools', false)
442
- .option('--stop-on-failure', 'Stop on first failure', false)
443
- .action((capsule, options) => {
444
- replaySession(capsule, options).catch(console.error);
445
- });
446
-
447
- // List command
448
- program
449
- .command('list')
450
- .description('List available replay capsules')
451
- .action(() => {
452
- listCapsules().catch(console.error);
453
- });
454
-
455
- // Show command
456
- program
457
- .command('show <id>')
458
- .description('Show details of a replay capsule')
459
- .action((id) => {
460
- showCapsule(id).catch(console.error);
461
- });
462
-
463
- // Delete command
464
- program
465
- .command('delete <id>')
466
- .description('Delete a replay capsule')
467
- .action((id) => {
468
- deleteCapsule(id).catch(console.error);
469
- });
470
-
471
- // Export command
472
- program
473
- .command('export <id> <file>')
474
- .description('Export a replay capsule to a file')
475
- .action((id, file) => {
476
- exportCapsule(id, file).catch(console.error);
477
- });
478
-
479
- // Import command
480
- program
481
- .command('import <file>')
482
- .description('Import a replay capsule from a file')
483
- .action((file) => {
484
- importCapsule(file).catch(console.error);
485
- });
486
-
487
- // Parse the provided args (prepend fake node/script path for commander)
488
- const argv = ['node', 'vibecheck-replay', ...args];
489
-
490
- if (args.length === 0) {
491
- program.help();
492
- return 0;
493
- }
494
-
495
- await program.parseAsync(argv);
496
- return 0;
497
- }
498
-
499
- module.exports = { runReplay };
@@ -1,92 +0,0 @@
1
- /**
2
- * vibecheck security - AuthZ + IDOR + Sensitive Security Proofs
3
- *
4
- * Subcommands:
5
- * security model = learn (extract auth model)
6
- * security matrix = build AuthZ matrix
7
- * security idor = detect IDOR candidates
8
- * security prove --url ... = runtime verification
9
- *
10
- * Replaces: permissions
11
- */
12
-
13
- "use strict";
14
-
15
- const c = {
16
- reset: '\x1b[0m',
17
- bold: '\x1b[1m',
18
- dim: '\x1b[2m',
19
- cyan: '\x1b[36m',
20
- yellow: '\x1b[33m',
21
- red: '\x1b[31m',
22
- };
23
-
24
- function printHelp() {
25
- console.log(`
26
- ${c.cyan}${c.bold}🔒 vibecheck security${c.reset} - Authorization & Security Verification
27
-
28
- AuthZ matrix & IDOR detection for sensitive security proofs.
29
-
30
- ${c.bold}SUBCOMMANDS${c.reset}
31
- ${c.cyan}model${c.reset} ${c.dim}Extract auth model from codebase (replaces 'permissions --learn')${c.reset}
32
- ${c.cyan}matrix${c.reset} ${c.dim}Build AuthZ matrix (replaces 'permissions --matrix')${c.reset}
33
- ${c.cyan}idor${c.reset} ${c.dim}Detect IDOR candidates (replaces 'permissions --idor')${c.reset}
34
- ${c.cyan}prove${c.reset} --url <url> ${c.dim}Runtime verification (replaces 'permissions --prove')${c.reset}
35
-
36
- ${c.bold}EXAMPLES${c.reset}
37
- vibecheck security model
38
- vibecheck security matrix
39
- vibecheck security idor
40
- vibecheck security prove --url http://localhost:3000
41
-
42
- ${c.dim}Note: Old command still works as alias:
43
- vibecheck permissions → vibecheck security model${c.reset}
44
- `);
45
- }
46
-
47
- async function runSecurity(args) {
48
- if (!args || args.length === 0 || args[0] === "--help" || args[0] === "-h") {
49
- printHelp();
50
- return 0;
51
- }
52
-
53
- const subcommand = args[0];
54
- const subArgs = args.slice(1);
55
-
56
- // Map subcommands to permissions flags
57
- let permissionsArgs = [];
58
-
59
- switch (subcommand) {
60
- case "model":
61
- permissionsArgs = ["--learn", ...subArgs];
62
- break;
63
-
64
- case "matrix":
65
- permissionsArgs = ["--matrix", ...subArgs];
66
- break;
67
-
68
- case "idor":
69
- permissionsArgs = ["--idor", ...subArgs];
70
- break;
71
-
72
- case "prove":
73
- permissionsArgs = ["--prove", ...subArgs];
74
- break;
75
-
76
- default:
77
- console.error(`${c.red}Unknown subcommand:${c.reset} ${subcommand}`);
78
- console.log(`\n${c.dim}Run 'vibecheck security --help' for available subcommands.${c.reset}\n`);
79
- return 1;
80
- }
81
-
82
- // Delegate to runPermissions
83
- try {
84
- const { runPermissions } = require("./runPermissions");
85
- return await runPermissions(permissionsArgs);
86
- } catch (e) {
87
- console.error(`${c.red}Error:${c.reset} Security command unavailable: ${e.message}`);
88
- return 1;
89
- }
90
- }
91
-
92
- module.exports = { runSecurity };