@vibecheckai/cli 3.1.2 → 3.1.4

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 (47) hide show
  1. package/README.md +60 -33
  2. package/bin/registry.js +319 -34
  3. package/bin/runners/CLI_REFACTOR_SUMMARY.md +229 -0
  4. package/bin/runners/REPORT_AUDIT.md +64 -0
  5. package/bin/runners/lib/entitlements-v2.js +97 -28
  6. package/bin/runners/lib/entitlements.js +3 -6
  7. package/bin/runners/lib/init-wizard.js +1 -1
  8. package/bin/runners/lib/report-engine.js +459 -280
  9. package/bin/runners/lib/report-html.js +1154 -1423
  10. package/bin/runners/lib/report-output.js +187 -0
  11. package/bin/runners/lib/report-templates.js +848 -850
  12. package/bin/runners/lib/scan-output.js +545 -0
  13. package/bin/runners/lib/server-usage.js +0 -12
  14. package/bin/runners/lib/ship-output.js +641 -0
  15. package/bin/runners/lib/status-output.js +253 -0
  16. package/bin/runners/lib/terminal-ui.js +853 -0
  17. package/bin/runners/runCheckpoint.js +502 -0
  18. package/bin/runners/runContracts.js +105 -0
  19. package/bin/runners/runExport.js +93 -0
  20. package/bin/runners/runFix.js +31 -24
  21. package/bin/runners/runInit.js +377 -112
  22. package/bin/runners/runInstall.js +1 -5
  23. package/bin/runners/runLabs.js +3 -3
  24. package/bin/runners/runPolish.js +2452 -0
  25. package/bin/runners/runProve.js +2 -2
  26. package/bin/runners/runReport.js +251 -200
  27. package/bin/runners/runRuntime.js +110 -0
  28. package/bin/runners/runScan.js +477 -379
  29. package/bin/runners/runSecurity.js +92 -0
  30. package/bin/runners/runShip.js +137 -207
  31. package/bin/runners/runStatus.js +16 -68
  32. package/bin/runners/utils.js +5 -5
  33. package/bin/vibecheck.js +25 -11
  34. package/mcp-server/index.js +150 -18
  35. package/mcp-server/package.json +2 -2
  36. package/mcp-server/premium-tools.js +13 -13
  37. package/mcp-server/tier-auth.js +292 -27
  38. package/mcp-server/vibecheck-tools.js +9 -9
  39. package/package.json +1 -1
  40. package/bin/runners/runClaimVerifier.js +0 -483
  41. package/bin/runners/runContextCompiler.js +0 -385
  42. package/bin/runners/runGate.js +0 -17
  43. package/bin/runners/runInitGha.js +0 -164
  44. package/bin/runners/runInteractive.js +0 -388
  45. package/bin/runners/runMdc.js +0 -204
  46. package/bin/runners/runMissionGenerator.js +0 -282
  47. package/bin/runners/runTruthpack.js +0 -636
@@ -1,636 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * Truth Pack Generator v1
4
- *
5
- * Generates .vibecheck/truth/truthpack.json from codebase analysis.
6
- * This is the ground truth that agents must reference.
7
- *
8
- * Usage:
9
- * vibecheck ctx - Generate truthpack.json
10
- * vibecheck ctx --snapshot - Write to snapshots/<commit>.json
11
- * vibecheck ctx --md - Also generate truthpack.md
12
- */
13
-
14
- "use strict";
15
-
16
- const fs = require('fs/promises');
17
- const path = require('path');
18
- const crypto = require('crypto');
19
- const { execSync } = require('child_process');
20
- const { RouteIndex, resolveNextRoutes, resolveFastifyRoutes } = require('./lib/route-truth.js');
21
-
22
- const VERSION = '1.0.0';
23
-
24
- // Evidence counter for unique IDs
25
- let evidenceCounter = 0;
26
-
27
- /**
28
- * Main entry point
29
- */
30
- async function runTruthpack(projectPath = process.cwd(), options = {}) {
31
- const startTime = Date.now();
32
- console.log('📦 Generating Truth Pack...\n');
33
-
34
- // Ensure .vibecheck/truth directory exists
35
- const truthDir = path.join(projectPath, '.vibecheck', 'truth');
36
- await fs.mkdir(truthDir, { recursive: true });
37
-
38
- // Reset evidence counter
39
- evidenceCounter = 0;
40
- const allEvidence = [];
41
-
42
- // Build truth pack
43
- const truthpack = {
44
- meta: await buildMeta(projectPath),
45
- project: await extractProject(projectPath),
46
- routes: await extractRoutes(projectPath, allEvidence),
47
- env: await extractEnv(projectPath, allEvidence),
48
- auth: await extractAuth(projectPath, allEvidence),
49
- billing: await extractBilling(projectPath, allEvidence),
50
- integrations: await extractIntegrations(projectPath, allEvidence),
51
- index: {
52
- evidenceRefs: allEvidence,
53
- hashes: {
54
- truthpackHash: '', // Will be computed after serialization
55
- },
56
- },
57
- };
58
-
59
- // Compute truthpack hash
60
- const truthpackJson = JSON.stringify(truthpack, null, 2);
61
- truthpack.index.hashes.truthpackHash = crypto.createHash('sha256').update(truthpackJson).digest('hex');
62
-
63
- // Write truthpack.json
64
- const truthpackPath = path.join(truthDir, 'truthpack.json');
65
- await fs.writeFile(truthpackPath, JSON.stringify(truthpack, null, 2));
66
- console.log(`✅ Wrote ${truthpackPath}`);
67
-
68
- // Write evidence.index.json
69
- const evidenceIndex = {
70
- meta: {
71
- commit: truthpack.meta.commit.sha,
72
- generatedAt: truthpack.meta.generatedAt,
73
- },
74
- evidence: allEvidence,
75
- };
76
- const evidencePath = path.join(truthDir, 'evidence.index.json');
77
- await fs.writeFile(evidencePath, JSON.stringify(evidenceIndex, null, 2));
78
- console.log(`✅ Wrote ${evidencePath}`);
79
-
80
- // Optionally write snapshot
81
- if (options.snapshot) {
82
- const snapshotsDir = path.join(truthDir, 'snapshots');
83
- await fs.mkdir(snapshotsDir, { recursive: true });
84
- const snapshotPath = path.join(snapshotsDir, `truthpack.${truthpack.meta.commit.sha.slice(0, 8)}.json`);
85
- await fs.writeFile(snapshotPath, JSON.stringify(truthpack, null, 2));
86
- console.log(`✅ Wrote snapshot: ${snapshotPath}`);
87
- }
88
-
89
- // Optionally generate markdown
90
- if (options.md !== false) {
91
- const mdPath = path.join(truthDir, 'truthpack.md');
92
- await fs.writeFile(mdPath, generateMarkdown(truthpack));
93
- console.log(`✅ Wrote ${mdPath}`);
94
- }
95
-
96
- const duration = Date.now() - startTime;
97
- console.log(`\n📦 Truth Pack complete in ${duration}ms`);
98
- console.log(` Routes: ${truthpack.routes.server.length} server, ${truthpack.routes.clientRefs.length} client refs`);
99
- console.log(` Env vars: ${truthpack.env.vars.length}`);
100
- console.log(` Evidence: ${allEvidence.length} refs`);
101
-
102
- return truthpack;
103
- }
104
-
105
- /**
106
- * Build metadata section
107
- */
108
- async function buildMeta(projectPath) {
109
- let sha = 'unknown';
110
- let branch = 'unknown';
111
-
112
- try {
113
- sha = execSync('git rev-parse HEAD', { cwd: projectPath, encoding: 'utf8' }).trim();
114
- branch = execSync('git rev-parse --abbrev-ref HEAD', { cwd: projectPath, encoding: 'utf8' }).trim();
115
- } catch {}
116
-
117
- return {
118
- version: VERSION,
119
- generatedAt: new Date().toISOString(),
120
- repoRoot: projectPath,
121
- commit: { sha, branch },
122
- tool: { name: 'vibecheck', version: VERSION },
123
- };
124
- }
125
-
126
- /**
127
- * Extract project structure
128
- */
129
- async function extractProject(projectPath) {
130
- const frameworks = [];
131
- const workspaces = [];
132
- const entrypoints = [];
133
- const scripts = {};
134
-
135
- // Detect frameworks from package.json
136
- try {
137
- const pkgPath = path.join(projectPath, 'package.json');
138
- const pkg = JSON.parse(await fs.readFile(pkgPath, 'utf8'));
139
-
140
- const deps = { ...pkg.dependencies, ...pkg.devDependencies };
141
- if (deps.next) frameworks.push('nextjs');
142
- if (deps.express) frameworks.push('express');
143
- if (deps.fastify) frameworks.push('fastify');
144
- if (deps.react) frameworks.push('react');
145
- if (deps.vue) frameworks.push('vue');
146
- if (deps['@nestjs/core']) frameworks.push('nestjs');
147
- if (deps.prisma || deps['@prisma/client']) frameworks.push('prisma');
148
-
149
- // Extract scripts
150
- if (pkg.scripts) {
151
- Object.assign(scripts, pkg.scripts);
152
- }
153
-
154
- // Check for workspaces
155
- if (pkg.workspaces) {
156
- const wsPatterns = Array.isArray(pkg.workspaces) ? pkg.workspaces : pkg.workspaces.packages || [];
157
- for (const pattern of wsPatterns) {
158
- const wsPath = pattern.replace('/*', '');
159
- try {
160
- const entries = await fs.readdir(path.join(projectPath, wsPath), { withFileTypes: true });
161
- for (const entry of entries) {
162
- if (entry.isDirectory()) {
163
- workspaces.push({
164
- name: entry.name,
165
- path: path.join(wsPath, entry.name),
166
- type: detectWorkspaceType(entry.name),
167
- });
168
- }
169
- }
170
- } catch {}
171
- }
172
- }
173
- } catch {}
174
-
175
- // Detect entrypoints
176
- const potentialEntrypoints = [
177
- { kind: 'api', paths: ['apps/api/src/index.ts', 'src/server.ts', 'server/index.ts', 'api/index.ts'] },
178
- { kind: 'web', paths: ['apps/web/app/page.tsx', 'src/pages/index.tsx', 'pages/index.tsx', 'app/page.tsx'] },
179
- { kind: 'cli', paths: ['bin/cli.js', 'src/cli.ts', 'cli/index.ts'] },
180
- ];
181
-
182
- for (const ep of potentialEntrypoints) {
183
- for (const p of ep.paths) {
184
- try {
185
- await fs.access(path.join(projectPath, p));
186
- entrypoints.push({ kind: ep.kind, path: p });
187
- break;
188
- } catch {}
189
- }
190
- }
191
-
192
- return { frameworks, workspaces, entrypoints, scripts };
193
- }
194
-
195
- /**
196
- * Extract server routes and client route references using Route Truth v1
197
- */
198
- async function extractRoutes(projectPath, allEvidence) {
199
- // Use Route Truth v1 resolvers for high-confidence extraction
200
- const routeIndex = new RouteIndex();
201
- await routeIndex.build(projectPath);
202
-
203
- const routeMap = routeIndex.getRouteMap();
204
- const serverRoutes = routeMap.server.map(route => ({
205
- method: route.method,
206
- path: route.path,
207
- handler: route.handler,
208
- middlewares: route.middlewares || [],
209
- authRequired: route.authRequired || 'unknown',
210
- confidence: route.confidence,
211
- evidence: route.evidence,
212
- }));
213
-
214
- // Add evidence to allEvidence
215
- for (const route of routeMap.server) {
216
- if (route.evidence) allEvidence.push(...route.evidence);
217
- }
218
-
219
- // Extract client route references (fetch/axios calls)
220
- const clientRefs = [];
221
- const files = await findSourceFiles(projectPath);
222
-
223
- // Client route reference patterns
224
- const clientPatterns = [
225
- /fetch\s*\(\s*['"`]([^'"`]+)['"`]/gi,
226
- /axios\.(get|post|put|patch|delete)\s*\(\s*['"`]([^'"`]+)['"`]/gi,
227
- /api\s*\.\s*(get|post|put|delete)\s*\(\s*['"`]([^'"`]+)['"`]/gi,
228
- ];
229
-
230
- for (const file of files) {
231
- try {
232
- const content = await fs.readFile(file, 'utf8');
233
- const relPath = path.relative(projectPath, file);
234
- const lines = content.split('\n');
235
-
236
- // Extract client route references only (server routes handled by Route Truth)
237
- for (const pattern of clientPatterns) {
238
- let match;
239
- pattern.lastIndex = 0;
240
- while ((match = pattern.exec(content)) !== null) {
241
- const routePath = match[2] || match[1];
242
- if (!routePath.startsWith('/api') && !routePath.startsWith('http')) continue;
243
-
244
- const lineNum = content.substring(0, match.index).split('\n').length;
245
- const snippet = lines[lineNum - 1];
246
-
247
- const evidence = createEvidence(relPath, `${lineNum}`, snippet, 'Client route reference');
248
- allEvidence.push(evidence);
249
-
250
- clientRefs.push({
251
- method: match[1]?.toUpperCase() || 'GET',
252
- path: routePath,
253
- source: `${relPath}:${lineNum}`,
254
- confidence: routePath.includes('${') ? 'low' : 'high',
255
- evidence: [evidence],
256
- });
257
- }
258
- }
259
- } catch {}
260
- }
261
-
262
- return { server: serverRoutes, clientRefs };
263
- }
264
-
265
- /**
266
- * Extract environment variables
267
- */
268
- async function extractEnv(projectPath, allEvidence) {
269
- const vars = new Map();
270
-
271
- // Check .env.example for declarations
272
- const envFiles = ['.env.example', '.env.local.example', '.env.sample', 'env.example'];
273
- for (const envFile of envFiles) {
274
- try {
275
- const content = await fs.readFile(path.join(projectPath, envFile), 'utf8');
276
- const lines = content.split('\n');
277
-
278
- for (let i = 0; i < lines.length; i++) {
279
- const match = lines[i].match(/^([A-Z][A-Z0-9_]*)=/);
280
- if (match) {
281
- const name = match[1];
282
- if (!vars.has(name)) {
283
- vars.set(name, {
284
- name,
285
- required: !lines[i].includes('optional'),
286
- references: [],
287
- defaultValueHint: lines[i].includes('=') ? lines[i].split('=')[1]?.trim() : undefined,
288
- });
289
- }
290
- }
291
- }
292
- } catch {}
293
- }
294
-
295
- // Find process.env usage in code
296
- const files = await findSourceFiles(projectPath);
297
- for (const file of files.slice(0, 100)) {
298
- try {
299
- const content = await fs.readFile(file, 'utf8');
300
- const relPath = path.relative(projectPath, file);
301
- const lines = content.split('\n');
302
-
303
- const pattern = /process\.env\.([A-Z][A-Z0-9_]*)/g;
304
- let match;
305
- while ((match = pattern.exec(content)) !== null) {
306
- const name = match[1];
307
- const lineNum = content.substring(0, match.index).split('\n').length;
308
- const snippet = lines[lineNum - 1];
309
-
310
- const evidence = createEvidence(relPath, `${lineNum}`, snippet, `Usage of ${name}`);
311
- allEvidence.push(evidence);
312
-
313
- if (!vars.has(name)) {
314
- vars.set(name, { name, required: true, references: [] });
315
- }
316
- vars.get(name).references.push(evidence);
317
- }
318
- } catch {}
319
- }
320
-
321
- return { vars: Array.from(vars.values()) };
322
- }
323
-
324
- /**
325
- * Extract auth model and protected surfaces
326
- */
327
- async function extractAuth(projectPath, allEvidence) {
328
- const signals = [];
329
- const protectedSurfaces = [];
330
- const gaps = [];
331
- let authType = 'unknown';
332
-
333
- const files = await findSourceFiles(projectPath);
334
-
335
- // Auth type detection patterns
336
- const authPatterns = {
337
- jwt: /jsonwebtoken|jwt\.verify|jwt\.sign/i,
338
- session: /express-session|cookie-session/i,
339
- nextauth: /next-auth|NextAuth/i,
340
- };
341
-
342
- // Auth middleware patterns
343
- const middlewarePatterns = [
344
- /(?:export\s+)?(?:const|function)\s+(\w*[Aa]uth\w*Middleware|\w*[Gg]uard\w*)/g,
345
- /requireAuth|isAuthenticated|verifyToken|checkAuth/gi,
346
- ];
347
-
348
- for (const file of files.slice(0, 100)) {
349
- try {
350
- const content = await fs.readFile(file, 'utf8');
351
- const relPath = path.relative(projectPath, file);
352
- const lines = content.split('\n');
353
-
354
- // Detect auth type
355
- for (const [type, pattern] of Object.entries(authPatterns)) {
356
- if (pattern.test(content)) {
357
- authType = type;
358
- const match = content.match(pattern);
359
- if (match) {
360
- const lineNum = content.substring(0, match.index).split('\n').length;
361
- const evidence = createEvidence(relPath, `${lineNum}`, lines[lineNum - 1], `${type} auth signal`);
362
- allEvidence.push(evidence);
363
- signals.push({ name: type, evidence: [evidence] });
364
- }
365
- }
366
- }
367
-
368
- // Find auth middleware/guards
369
- for (const pattern of middlewarePatterns) {
370
- let match;
371
- pattern.lastIndex = 0;
372
- while ((match = pattern.exec(content)) !== null) {
373
- const lineNum = content.substring(0, match.index).split('\n').length;
374
- const evidence = createEvidence(relPath, `${lineNum}`, lines[lineNum - 1], 'Auth middleware');
375
- allEvidence.push(evidence);
376
- signals.push({ name: match[1] || match[0], evidence: [evidence] });
377
- }
378
- }
379
-
380
- // Find protected routes
381
- if (content.includes('auth') || content.includes('protect')) {
382
- const routePattern = /\.(get|post|put|patch|delete)\s*\(\s*['"`]([^'"`]+)['"`].*(?:auth|protect|guard)/gi;
383
- let match;
384
- while ((match = routePattern.exec(content)) !== null) {
385
- const lineNum = content.substring(0, match.index).split('\n').length;
386
- const evidence = createEvidence(relPath, `${lineNum}`, lines[lineNum - 1], 'Protected route');
387
- allEvidence.push(evidence);
388
-
389
- protectedSurfaces.push({
390
- surface: match[2],
391
- kind: 'route',
392
- enforcedBy: ['middleware'],
393
- evidence: [evidence],
394
- });
395
- }
396
- }
397
- } catch {}
398
- }
399
-
400
- return {
401
- model: { type: authType, signals },
402
- protectedSurfaces,
403
- gaps,
404
- };
405
- }
406
-
407
- /**
408
- * Extract billing signals and paid surfaces
409
- */
410
- async function extractBilling(projectPath, allEvidence) {
411
- const signals = [];
412
- const paidSurfaces = [];
413
- const gaps = [];
414
- const bypassSignals = [];
415
-
416
- const files = await findSourceFiles(projectPath);
417
-
418
- // Billing signal patterns
419
- const billingPatterns = [
420
- /stripe|Stripe/g,
421
- /subscription|Subscription/g,
422
- /isPro|isEnterprise|tier|Tier/g,
423
- /checkEntitlement|hasFeature|canAccess/g,
424
- ];
425
-
426
- // Bypass patterns (ship killers)
427
- const bypassPatterns = [
428
- { pattern: /SKIP_BILLING|BYPASS_PAYMENT/g, severity: 'block' },
429
- { pattern: /isPro\s*=\s*true\s*\/\//g, severity: 'block' },
430
- { pattern: /tier\s*=\s*['"]enterprise['"]\s*\/\/\s*dev/gi, severity: 'warn' },
431
- ];
432
-
433
- for (const file of files.slice(0, 100)) {
434
- try {
435
- const content = await fs.readFile(file, 'utf8');
436
- const relPath = path.relative(projectPath, file);
437
- const lines = content.split('\n');
438
-
439
- // Find billing signals
440
- for (const pattern of billingPatterns) {
441
- let match;
442
- pattern.lastIndex = 0;
443
- while ((match = pattern.exec(content)) !== null) {
444
- const lineNum = content.substring(0, match.index).split('\n').length;
445
- const evidence = createEvidence(relPath, `${lineNum}`, lines[lineNum - 1], 'Billing signal');
446
- allEvidence.push(evidence);
447
- signals.push({ name: match[0], evidence: [evidence] });
448
- }
449
- }
450
-
451
- // Find bypass signals
452
- for (const { pattern, severity } of bypassPatterns) {
453
- let match;
454
- pattern.lastIndex = 0;
455
- while ((match = pattern.exec(content)) !== null) {
456
- const lineNum = content.substring(0, match.index).split('\n').length;
457
- const evidence = createEvidence(relPath, `${lineNum}`, lines[lineNum - 1], 'Billing bypass signal');
458
- allEvidence.push(evidence);
459
- bypassSignals.push({ pattern: match[0], severity, evidence: [evidence] });
460
- }
461
- }
462
- } catch {}
463
- }
464
-
465
- return { signals, paidSurfaces, gaps, bypassSignals };
466
- }
467
-
468
- /**
469
- * Extract integrations (Stripe, Resend, etc.)
470
- */
471
- async function extractIntegrations(projectPath, allEvidence) {
472
- const services = [];
473
- const integrationPatterns = {
474
- stripe: /new Stripe\(|stripe\.customers|stripe\.subscriptions/gi,
475
- resend: /new Resend\(|resend\.emails/gi,
476
- prisma: /new PrismaClient\(|prisma\./gi,
477
- supabase: /createClient.*supabase|supabaseClient/gi,
478
- openai: /new OpenAI\(|openai\./gi,
479
- };
480
-
481
- const files = await findSourceFiles(projectPath);
482
-
483
- for (const file of files.slice(0, 100)) {
484
- try {
485
- const content = await fs.readFile(file, 'utf8');
486
- const relPath = path.relative(projectPath, file);
487
-
488
- for (const [name, pattern] of Object.entries(integrationPatterns)) {
489
- if (pattern.test(content)) {
490
- const existing = services.find(s => s.name === name);
491
- if (existing) {
492
- existing.clientFiles.push(relPath);
493
- } else {
494
- services.push({ name, clientFiles: [relPath], evidence: [] });
495
- }
496
- }
497
- }
498
- } catch {}
499
- }
500
-
501
- return { services };
502
- }
503
-
504
- // =============================================================================
505
- // HELPERS
506
- // =============================================================================
507
-
508
- function createEvidence(file, lines, snippet, reason) {
509
- evidenceCounter++;
510
- const id = `ev_${String(evidenceCounter).padStart(4, '0')}`;
511
- const snippetHash = `sha256:${crypto.createHash('sha256').update(snippet || '').digest('hex').slice(0, 16)}`;
512
-
513
- return { id, file, lines, snippetHash, reason };
514
- }
515
-
516
- function extractMiddlewares(content, lineNum) {
517
- // Simple middleware extraction - look for function calls before the handler
518
- const lines = content.split('\n');
519
- const line = lines[lineNum - 1] || '';
520
- const middlewares = [];
521
-
522
- const mwPattern = /(\w+Middleware|\w+Guard|\w+Auth)/g;
523
- let match;
524
- while ((match = mwPattern.exec(line)) !== null) {
525
- middlewares.push(match[1]);
526
- }
527
-
528
- return middlewares;
529
- }
530
-
531
- function detectAuthRequired(content, lineNum) {
532
- const lines = content.split('\n');
533
- const context = lines.slice(Math.max(0, lineNum - 5), lineNum + 5).join('\n');
534
-
535
- if (/auth|protect|guard|require.*auth/i.test(context)) return 'yes';
536
- if (/public|no.*auth/i.test(context)) return 'no';
537
- return 'unknown';
538
- }
539
-
540
- function detectWorkspaceType(name) {
541
- if (/api|server|backend/i.test(name)) return 'service';
542
- if (/web|app|frontend|ui/i.test(name)) return 'app';
543
- if (/package|lib|shared|common/i.test(name)) return 'package';
544
- return 'unknown';
545
- }
546
-
547
- async function findSourceFiles(projectPath) {
548
- const files = [];
549
- const extensions = ['.ts', '.tsx', '.js', '.jsx'];
550
- const ignoreDirs = ['node_modules', 'dist', 'build', '.git', '.next', 'coverage', '.vibecheck'];
551
-
552
- async function walk(dir) {
553
- try {
554
- const entries = await fs.readdir(dir, { withFileTypes: true });
555
- for (const entry of entries) {
556
- const fullPath = path.join(dir, entry.name);
557
- if (entry.isDirectory()) {
558
- if (!ignoreDirs.includes(entry.name) && !entry.name.startsWith('.')) {
559
- await walk(fullPath);
560
- }
561
- } else if (entry.isFile()) {
562
- const ext = path.extname(entry.name).toLowerCase();
563
- if (extensions.includes(ext)) {
564
- files.push(fullPath);
565
- }
566
- }
567
- }
568
- } catch {}
569
- }
570
-
571
- await walk(projectPath);
572
- return files;
573
- }
574
-
575
- function generateMarkdown(truthpack) {
576
- const lines = [
577
- '# Truth Pack',
578
- '',
579
- `Generated: ${truthpack.meta.generatedAt}`,
580
- `Commit: ${truthpack.meta.commit.sha} (${truthpack.meta.commit.branch})`,
581
- '',
582
- '## Project',
583
- '',
584
- `**Frameworks:** ${truthpack.project.frameworks.join(', ') || 'none detected'}`,
585
- '',
586
- '### Workspaces',
587
- '',
588
- ...truthpack.project.workspaces.map(w => `- \`${w.path}\` (${w.type})`),
589
- '',
590
- '### Entrypoints',
591
- '',
592
- ...truthpack.project.entrypoints.map(e => `- \`${e.path}\` (${e.kind})`),
593
- '',
594
- '## Routes',
595
- '',
596
- `**Server Routes:** ${truthpack.routes.server.length}`,
597
- `**Client Refs:** ${truthpack.routes.clientRefs.length}`,
598
- '',
599
- '### Server Routes',
600
- '',
601
- '| Method | Path | Handler | Auth |',
602
- '|--------|------|---------|------|',
603
- ...truthpack.routes.server.slice(0, 50).map(r =>
604
- `| ${r.method} | \`${r.path}\` | ${r.handler} | ${r.authRequired} |`
605
- ),
606
- '',
607
- '## Environment',
608
- '',
609
- `**Variables:** ${truthpack.env.vars.length}`,
610
- '',
611
- ...truthpack.env.vars.slice(0, 30).map(v =>
612
- `- \`${v.name}\` ${v.required ? '(required)' : '(optional)'} - ${v.references.length} refs`
613
- ),
614
- '',
615
- '## Auth',
616
- '',
617
- `**Type:** ${truthpack.auth.model.type}`,
618
- `**Signals:** ${truthpack.auth.model.signals.length}`,
619
- `**Protected Surfaces:** ${truthpack.auth.protectedSurfaces.length}`,
620
- '',
621
- '## Billing',
622
- '',
623
- `**Signals:** ${truthpack.billing.signals.length}`,
624
- `**Bypass Signals:** ${truthpack.billing.bypassSignals.length}`,
625
- '',
626
- '## Index',
627
- '',
628
- `**Evidence Refs:** ${truthpack.index.evidenceRefs.length}`,
629
- `**Hash:** ${truthpack.index.hashes.truthpackHash}`,
630
- '',
631
- ];
632
-
633
- return lines.join('\n');
634
- }
635
-
636
- module.exports = { runTruthpack };