@viberaven/cli 1.1.4 → 1.1.6

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.
package/dist/cli.js CHANGED
@@ -31,9 +31,9 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
31
31
  ));
32
32
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
33
33
 
34
- // ../../../../node_modules/picocolors/picocolors.js
34
+ // ../../node_modules/picocolors/picocolors.js
35
35
  var require_picocolors = __commonJS({
36
- "../../../../node_modules/picocolors/picocolors.js"(exports2, module2) {
36
+ "../../node_modules/picocolors/picocolors.js"(exports2, module2) {
37
37
  var p2 = process || {};
38
38
  var argv = p2.argv || [];
39
39
  var env = p2.env || {};
@@ -103,9 +103,9 @@ var require_picocolors = __commonJS({
103
103
  }
104
104
  });
105
105
 
106
- // ../../../../node_modules/sisteransi/src/index.js
106
+ // ../../node_modules/sisteransi/src/index.js
107
107
  var require_src = __commonJS({
108
- "../../../../node_modules/sisteransi/src/index.js"(exports2, module2) {
108
+ "../../node_modules/sisteransi/src/index.js"(exports2, module2) {
109
109
  "use strict";
110
110
  var ESC = "\x1B";
111
111
  var CSI = `${ESC}[`;
@@ -170,8 +170,8 @@ __export(cli_exports, {
170
170
  runScanCommand: () => runScanCommand
171
171
  });
172
172
  module.exports = __toCommonJS(cli_exports);
173
- var import_promises21 = require("node:fs/promises");
174
- var import_node_path28 = require("node:path");
173
+ var import_promises19 = require("node:fs/promises");
174
+ var import_node_path25 = require("node:path");
175
175
 
176
176
  // src/config.ts
177
177
  var import_node_os = require("node:os");
@@ -528,8 +528,8 @@ var UPGRADE_REQUIRED = "UPGRADE_REQUIRED";
528
528
  var MANUAL_ACTION_REQUIRED = "MANUAL_ACTION_REQUIRED";
529
529
  var AGENT_ACTION = "AGENT_ACTION";
530
530
  var ERROR = "ERROR";
531
- function formatAgentStatus(label2, message) {
532
- return `${label2}: ${message}`;
531
+ function formatAgentStatus(label, message) {
532
+ return `${label}: ${message}`;
533
533
  }
534
534
 
535
535
  // src/account.ts
@@ -621,12 +621,12 @@ function createOpenCommand(target, platform = process.platform) {
621
621
  }
622
622
  async function openWithSystemDefault(target) {
623
623
  const { command, args, shell } = createOpenCommand(target);
624
- await new Promise((resolve6, reject) => {
624
+ await new Promise((resolve5, reject) => {
625
625
  const child = (0, import_node_child_process.spawn)(command, args, { stdio: "ignore", shell });
626
626
  child.on("error", reject);
627
627
  child.on("exit", (code) => {
628
628
  if (code === 0) {
629
- resolve6();
629
+ resolve5();
630
630
  } else {
631
631
  reject(new Error(`Could not open browser (exit ${code ?? "unknown"}). Open manually: ${target}`));
632
632
  }
@@ -661,14 +661,11 @@ function promptGapCommand(gapId) {
661
661
  function healPlanGapCommand(gapId) {
662
662
  return `${PUBLIC_COMMAND} --heal --plan --gap ${gapId}`;
663
663
  }
664
- function healApplyGapCommand(gapId) {
665
- return `${PUBLIC_COMMAND} --heal --apply --gap ${gapId}`;
666
- }
667
664
 
668
665
  // src/auth.ts
669
666
  var PUBLIC_LOGIN_COMMAND = `${PUBLIC_COMMAND} login`;
670
667
  function sleep(ms) {
671
- return new Promise((resolve6) => setTimeout(resolve6, ms));
668
+ return new Promise((resolve5) => setTimeout(resolve5, ms));
672
669
  }
673
670
  async function runDeviceLogin(apiBaseUrl) {
674
671
  const signIn = await startManagedSignIn(apiBaseUrl);
@@ -1097,7 +1094,7 @@ function createGitignoreMatcher(gitignoreContent) {
1097
1094
  const rules = gitignoreContent.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#") && !line.startsWith("!")).map(parseGitignoreRule);
1098
1095
  return (relPath) => {
1099
1096
  const normalized = relPath.replace(/\\/g, "/").replace(/^\/+/, "");
1100
- return rules.some((rule2) => ruleMatchesPath(rule2, normalized));
1097
+ return rules.some((rule) => ruleMatchesPath(rule, normalized));
1101
1098
  };
1102
1099
  }
1103
1100
  function parseGitignoreRule(raw) {
@@ -1114,22 +1111,22 @@ function parseGitignoreRule(raw) {
1114
1111
  regex: new RegExp(`^${gitignoreGlobToRegex(pattern)}$`)
1115
1112
  };
1116
1113
  }
1117
- function ruleMatchesPath(rule2, relPath) {
1118
- const candidates = rule2.anchored || rule2.hasSlash ? [relPath] : relPath.split("/");
1119
- const directMatch = candidates.some((candidate) => rule2.regex.test(candidate));
1120
- if (directMatch && !rule2.directoryOnly) {
1114
+ function ruleMatchesPath(rule, relPath) {
1115
+ const candidates = rule.anchored || rule.hasSlash ? [relPath] : relPath.split("/");
1116
+ const directMatch = candidates.some((candidate) => rule.regex.test(candidate));
1117
+ if (directMatch && !rule.directoryOnly) {
1121
1118
  return true;
1122
1119
  }
1123
- if (directMatch && rule2.directoryOnly) {
1120
+ if (directMatch && rule.directoryOnly) {
1124
1121
  return true;
1125
1122
  }
1126
- if (!rule2.directoryOnly) {
1123
+ if (!rule.directoryOnly) {
1127
1124
  return false;
1128
1125
  }
1129
- if (rule2.anchored || rule2.hasSlash) {
1130
- return relPath === rule2.pattern || relPath.startsWith(`${rule2.pattern}/`);
1126
+ if (rule.anchored || rule.hasSlash) {
1127
+ return relPath === rule.pattern || relPath.startsWith(`${rule.pattern}/`);
1131
1128
  }
1132
- return relPath.split("/").includes(rule2.pattern);
1129
+ return relPath.split("/").includes(rule.pattern);
1133
1130
  }
1134
1131
  function gitignoreGlobToRegex(pattern) {
1135
1132
  let out = "";
@@ -1249,7 +1246,7 @@ function fallbackMapCategoryForGap(category, text) {
1249
1246
  function inferMapCategories(category, title, detail, copyPrompt, explicitPrimary, explicitAffected) {
1250
1247
  const text = [category, title, detail, copyPrompt].filter(Boolean).join(" ");
1251
1248
  const fallback = fallbackMapCategoryForGap(category, text);
1252
- const matched = MAP_CATEGORY_RULES.filter((rule2) => rule2.match.test(text)).map((rule2) => rule2.key);
1249
+ const matched = MAP_CATEGORY_RULES.filter((rule) => rule.match.test(text)).map((rule) => rule.key);
1253
1250
  const explicit = isProductionMapCategoryKey(explicitPrimary) ? [explicitPrimary] : [];
1254
1251
  const affected = Array.isArray(explicitAffected) ? explicitAffected.filter(isProductionMapCategoryKey) : [];
1255
1252
  const primarySeed = matched[0] ? [matched[0], fallback] : [fallback];
@@ -1294,12 +1291,12 @@ function normalizeGap(v) {
1294
1291
  affectedMapCategories
1295
1292
  };
1296
1293
  }
1297
- function rootGapKey(gap2) {
1298
- const titleKey = slugify(gap2.title);
1294
+ function rootGapKey(gap) {
1295
+ const titleKey = slugify(gap.title);
1299
1296
  if (titleKey) {
1300
1297
  return titleKey;
1301
1298
  }
1302
- return slugify([gap2.category, gap2.copyPrompt].join(" ")) || gap2.id;
1299
+ return slugify([gap.category, gap.copyPrompt].join(" ")) || gap.id;
1303
1300
  }
1304
1301
  function mergeToolSuggestions(a, b3) {
1305
1302
  const seen = /* @__PURE__ */ new Set();
@@ -1329,17 +1326,17 @@ function mergeRootGaps(a, b3) {
1329
1326
  function dedupeRootGaps(gaps) {
1330
1327
  const byKey = /* @__PURE__ */ new Map();
1331
1328
  const order = [];
1332
- for (const gap2 of gaps) {
1333
- const key = rootGapKey(gap2);
1329
+ for (const gap of gaps) {
1330
+ const key = rootGapKey(gap);
1334
1331
  const existing = byKey.get(key);
1335
1332
  if (!existing) {
1336
- byKey.set(key, gap2);
1333
+ byKey.set(key, gap);
1337
1334
  order.push(key);
1338
1335
  continue;
1339
1336
  }
1340
- byKey.set(key, mergeRootGaps(existing, gap2));
1337
+ byKey.set(key, mergeRootGaps(existing, gap));
1341
1338
  }
1342
- return order.map((key) => byKey.get(key)).filter((gap2) => Boolean(gap2));
1339
+ return order.map((key) => byKey.get(key)).filter((gap) => Boolean(gap));
1343
1340
  }
1344
1341
  function normalizeChecklist(v) {
1345
1342
  const defaults = {
@@ -1483,15 +1480,15 @@ function buildLaunchValidationReport(input) {
1483
1480
  promptTemplate: conflict.recommendedAction.promptTemplate
1484
1481
  }))
1485
1482
  );
1486
- const gapIssues = (input.gaps ?? []).filter((gap2) => gap2.severity !== "info").map((gap2) => {
1487
- const area = gapArea(gap2);
1483
+ const gapIssues = (input.gaps ?? []).filter((gap) => gap.severity !== "info").map((gap) => {
1484
+ const area = gapArea(gap);
1488
1485
  return {
1489
- id: `gap-${gap2.id}`,
1486
+ id: `gap-${gap.id}`,
1490
1487
  area,
1491
- severity: gap2.severity,
1492
- title: gap2.title,
1493
- detail: gap2.detail,
1494
- promptTemplate: gap2.severity === "critical" && isLaunchCriticalArea(area) ? "launch-blocker" : "repo-fix"
1488
+ severity: gap.severity,
1489
+ title: gap.title,
1490
+ detail: gap.detail,
1491
+ promptTemplate: gap.severity === "critical" && isLaunchCriticalArea(area) ? "launch-blocker" : "repo-fix"
1495
1492
  };
1496
1493
  });
1497
1494
  const providerCoverageWarnings = providerCoverageIssues(input.providerTruth);
@@ -1606,8 +1603,8 @@ function isActionableProviderCheckRow(row) {
1606
1603
  function hasCompletedManualConfirmation(row) {
1607
1604
  return row.manualProof.status === "manual-confirmed" || row.roles.some((role) => role === "manual-confirmed");
1608
1605
  }
1609
- function gapArea(gap2) {
1610
- return VERIFICATION_AREAS.includes(gap2.primaryMapCategory) ? gap2.primaryMapCategory : "appFlow";
1606
+ function gapArea(gap) {
1607
+ return VERIFICATION_AREAS.includes(gap.primaryMapCategory) ? gap.primaryMapCategory : "appFlow";
1611
1608
  }
1612
1609
  function isLaunchCriticalArea(area) {
1613
1610
  return LAUNCH_CRITICAL_AREAS.includes(area);
@@ -2076,11 +2073,11 @@ function buildProviderRegistrySnapshot(now = /* @__PURE__ */ new Date()) {
2076
2073
  providers
2077
2074
  };
2078
2075
  }
2079
- function provider(providerKey, label2, aliases, areas, productionAreas, iconKey, extras = {}) {
2076
+ function provider(providerKey, label, aliases, areas, productionAreas, iconKey, extras = {}) {
2080
2077
  const sifgTemplateIds = areas.flatMap((area) => sifgTemplatesForRegistryProviderArea(providerKey, area)).map((template) => template.id);
2081
2078
  return {
2082
2079
  provider: providerKey,
2083
- label: label2,
2080
+ label,
2084
2081
  aliases,
2085
2082
  areas,
2086
2083
  productionAreas,
@@ -2218,20 +2215,20 @@ function mapEvidenceSourceToLegacyEvidenceClass(source, status) {
2218
2215
  }
2219
2216
  return "mcp-verifier";
2220
2217
  }
2221
- function isProviderLayerCheck(check) {
2222
- return check.evidenceSource === "provider" || check.evidenceSource === "mcp";
2218
+ function isProviderLayerCheck(check2) {
2219
+ return check2.evidenceSource === "provider" || check2.evidenceSource === "mcp";
2223
2220
  }
2224
- function isRepoLayerCheck(check) {
2225
- return check.evidenceSource === "repo";
2221
+ function isRepoLayerCheck(check2) {
2222
+ return check2.evidenceSource === "repo";
2226
2223
  }
2227
2224
  function computeLayerReadinessPercent(checks, layer) {
2228
2225
  const filtered = checks.filter(
2229
- (check) => layer === "repo" ? isRepoLayerCheck(check) : isProviderLayerCheck(check)
2226
+ (check2) => layer === "repo" ? isRepoLayerCheck(check2) : isProviderLayerCheck(check2)
2230
2227
  );
2231
2228
  if (filtered.length === 0) {
2232
2229
  return 100;
2233
2230
  }
2234
- const verified = filtered.filter((check) => check.status === "verified").length;
2231
+ const verified = filtered.filter((check2) => check2.status === "verified").length;
2235
2232
  return Math.round(verified / filtered.length * 100);
2236
2233
  }
2237
2234
  function aggregateReadinessPercents(providerResults) {
@@ -2263,7 +2260,7 @@ function mergeVerificationIntoMissionGraph(graph, layer) {
2263
2260
  if (!mission) {
2264
2261
  continue;
2265
2262
  }
2266
- if (mission.checks.some((check) => check.verificationCheckId === layerCheck.id || check.id === missionRowId(layerCheck.id))) {
2263
+ if (mission.checks.some((check2) => check2.verificationCheckId === layerCheck.id || check2.id === missionRowId(layerCheck.id))) {
2267
2264
  continue;
2268
2265
  }
2269
2266
  mission.checks.push(verificationCheckToMissionCheck(layerCheck, mission));
@@ -2354,7 +2351,7 @@ function readinessPercentForRepoChecks(checks) {
2354
2351
  if (repoChecks.length === 0) {
2355
2352
  return 100;
2356
2353
  }
2357
- const verified = repoChecks.filter((check) => isVerifiedMissionCheck(check)).length;
2354
+ const verified = repoChecks.filter((check2) => isVerifiedMissionCheck(check2)).length;
2358
2355
  return Math.round(verified / repoChecks.length * 100);
2359
2356
  }
2360
2357
  function readinessPercentForProviderChecks(checks) {
@@ -2362,29 +2359,29 @@ function readinessPercentForProviderChecks(checks) {
2362
2359
  if (providerChecks.length === 0) {
2363
2360
  return 100;
2364
2361
  }
2365
- const verified = providerChecks.filter((check) => isVerifiedMissionCheck(check)).length;
2362
+ const verified = providerChecks.filter((check2) => isVerifiedMissionCheck(check2)).length;
2366
2363
  return Math.round(verified / providerChecks.length * 100);
2367
2364
  }
2368
- function isRepoLayerMissionCheck(check) {
2369
- if (check.evidenceSource === "provider" || check.evidenceSource === "mcp" || check.evidenceSource === "manual") {
2365
+ function isRepoLayerMissionCheck(check2) {
2366
+ if (check2.evidenceSource === "provider" || check2.evidenceSource === "mcp" || check2.evidenceSource === "manual") {
2370
2367
  return false;
2371
2368
  }
2372
- if (check.evidenceSource === "repo") {
2369
+ if (check2.evidenceSource === "repo") {
2373
2370
  return true;
2374
2371
  }
2375
- return check.evidenceClass === "repo-verified" || check.evidenceClass === "missing-repo-fix";
2372
+ return check2.evidenceClass === "repo-verified" || check2.evidenceClass === "missing-repo-fix";
2376
2373
  }
2377
- function isProviderLayerMissionCheck(check) {
2378
- if (check.evidenceSource === "provider" || check.evidenceSource === "mcp") {
2374
+ function isProviderLayerMissionCheck(check2) {
2375
+ if (check2.evidenceSource === "provider" || check2.evidenceSource === "mcp") {
2379
2376
  return true;
2380
2377
  }
2381
- return check.evidenceClass === "mcp-verifier";
2378
+ return check2.evidenceClass === "mcp-verifier";
2382
2379
  }
2383
- function isVerifiedMissionCheck(check) {
2384
- if (check.verificationStatus) {
2385
- return check.verificationStatus === "verified";
2380
+ function isVerifiedMissionCheck(check2) {
2381
+ if (check2.verificationStatus) {
2382
+ return check2.verificationStatus === "verified";
2386
2383
  }
2387
- return check.status === "passed" || check.status === "user-confirmed";
2384
+ return check2.status === "passed" || check2.status === "user-confirmed";
2388
2385
  }
2389
2386
  function rebuildAreas(providerMissions) {
2390
2387
  const byArea = /* @__PURE__ */ new Map();
@@ -2413,7 +2410,7 @@ function rebuildAreas(providerMissions) {
2413
2410
  const repoReadinessPercent = percentVerified(repoChecks);
2414
2411
  const providerReadinessPercent = percentVerified(providerChecks);
2415
2412
  const criticalCount = missions.flatMap((m2) => m2.checks).filter(
2416
- (check) => isProviderLayerMissionCheck(check) && (check.verificationStatus === "missing" || check.status === "missing" || check.status === "failed")
2413
+ (check2) => isProviderLayerMissionCheck(check2) && (check2.verificationStatus === "missing" || check2.status === "missing" || check2.status === "failed")
2417
2414
  ).length;
2418
2415
  return {
2419
2416
  key,
@@ -2430,7 +2427,7 @@ function percentVerified(checks) {
2430
2427
  if (checks.length === 0) {
2431
2428
  return 100;
2432
2429
  }
2433
- const verified = checks.filter((check) => isVerifiedMissionCheck(check)).length;
2430
+ const verified = checks.filter((check2) => isVerifiedMissionCheck(check2)).length;
2434
2431
  return Math.round(verified / checks.length * 100);
2435
2432
  }
2436
2433
 
@@ -2495,7 +2492,7 @@ function toProviderMission(summary) {
2495
2492
  promptSubject: summary.promptSubject,
2496
2493
  readinessPercent: summary.readinessPercent,
2497
2494
  repoReadinessPercent: summary.readinessPercent,
2498
- providerReadinessPercent: checks.some((check) => check.evidenceClass === "mcp-verifier") ? 0 : 100,
2495
+ providerReadinessPercent: checks.some((check2) => check2.evidenceClass === "mcp-verifier") ? 0 : 100,
2499
2496
  checks
2500
2497
  };
2501
2498
  }
@@ -2540,7 +2537,7 @@ function overlaySifgLeaks(providerMissions, graph) {
2540
2537
  const providerCheckIds = new Map(
2541
2538
  providerMissions.map((mission) => [
2542
2539
  mission.key,
2543
- new Set(mission.checks.map((check) => check.id))
2540
+ new Set(mission.checks.map((check2) => check2.id))
2544
2541
  ])
2545
2542
  );
2546
2543
  for (const leak of graph.leaks) {
@@ -2559,9 +2556,9 @@ function overlaySifgLeaks(providerMissions, graph) {
2559
2556
  continue;
2560
2557
  }
2561
2558
  const usedCheckIds = providerCheckIds.get(mission.key) ?? /* @__PURE__ */ new Set();
2562
- const check = toSifgMissionCheck(mission, pipeline, pipelineId, leaks, usedCheckIds);
2563
- mission.checks.push(check);
2564
- usedCheckIds.add(check.id);
2559
+ const check2 = toSifgMissionCheck(mission, pipeline, pipelineId, leaks, usedCheckIds);
2560
+ mission.checks.push(check2);
2561
+ usedCheckIds.add(check2.id);
2565
2562
  providerCheckIds.set(mission.key, usedCheckIds);
2566
2563
  }
2567
2564
  for (const mission of providerMissions) {
@@ -2612,11 +2609,11 @@ function stableSifgSuffix(id) {
2612
2609
  }
2613
2610
  function readinessPercentForChecks(checks) {
2614
2611
  const actionableChecks = checks.filter(isActionableCheck);
2615
- const passed = actionableChecks.filter((check) => check.status === "passed").length;
2612
+ const passed = actionableChecks.filter((check2) => check2.status === "passed").length;
2616
2613
  return Math.round(passed / Math.max(actionableChecks.length, 1) * 100);
2617
2614
  }
2618
- function isActionableCheck(check) {
2619
- return check.evidenceClass !== "manual-dashboard" && check.evidenceClass !== "mcp-verifier";
2615
+ function isActionableCheck(check2) {
2616
+ return check2.evidenceClass !== "manual-dashboard" && check2.evidenceClass !== "mcp-verifier";
2620
2617
  }
2621
2618
  function buildAreas(providerMissions) {
2622
2619
  const byArea = /* @__PURE__ */ new Map();
@@ -2627,15 +2624,15 @@ function buildAreas(providerMissions) {
2627
2624
  }
2628
2625
  return [...byArea.entries()].map(([key, missions]) => {
2629
2626
  const actionableChecks = missions.flatMap((mission) => mission.checks).filter(isActionableCheck);
2630
- const passed = actionableChecks.filter((check) => check.status === "passed").length;
2631
- const missing = actionableChecks.filter((check) => check.status === "missing" || check.status === "failed").length;
2627
+ const passed = actionableChecks.filter((check2) => check2.status === "passed").length;
2628
+ const missing = actionableChecks.filter((check2) => check2.status === "missing" || check2.status === "failed").length;
2632
2629
  return {
2633
2630
  key,
2634
2631
  label: AREA_LABELS[key],
2635
2632
  readinessPercent: Math.round(passed / Math.max(actionableChecks.length, 1) * 100),
2636
2633
  repoReadinessPercent: Math.round(passed / Math.max(actionableChecks.length, 1) * 100),
2637
2634
  providerReadinessPercent: missions.some(
2638
- (mission) => mission.checks.some((check) => check.evidenceClass === "mcp-verifier")
2635
+ (mission) => mission.checks.some((check2) => check2.evidenceClass === "mcp-verifier")
2639
2636
  ) ? 0 : 100,
2640
2637
  criticalCount: missing,
2641
2638
  providerMissions: missions
@@ -2938,14 +2935,14 @@ function detectProductionConnectionEvidence(scan) {
2938
2935
  content: file.isSecret || typeof file.content !== "string" ? "" : file.content,
2939
2936
  lowerContent: file.isSecret || typeof file.content !== "string" ? "" : file.content.toLowerCase()
2940
2937
  }));
2941
- const secretPathBlob = scan.secretsFound.map((path3) => normalizePath(path3)).join("\n");
2938
+ const secretPathBlob = scan.secretsFound.map((path) => normalizePath(path)).join("\n");
2942
2939
  const pathBlob = `${scan.fileTree}
2943
2940
  ${files.map((file) => file.path).join("\n")}`.toLowerCase();
2944
2941
  const contentBlob = files.map((file) => file.lowerContent).join("\n").slice(0, 12e4);
2945
2942
  const secretsHygieneBlob = `${pathBlob}
2946
2943
  ${secretPathBlob}`;
2947
- for (const rule2 of PROVIDER_RULES) {
2948
- detectProvider(evidence, rule2, deps, files, pathBlob);
2944
+ for (const rule of PROVIDER_RULES) {
2945
+ detectProvider(evidence, rule, deps, files, pathBlob);
2949
2946
  }
2950
2947
  detectSecretsHygiene(evidence, secretsHygieneBlob);
2951
2948
  for (const item3 of Object.values(evidence)) {
@@ -2999,55 +2996,55 @@ function buildProductionConnectionContext(choices, evidence) {
2999
2996
  }
3000
2997
  return lines.length > 0 ? lines.join("\n") : "production connections: no selected or detected providers";
3001
2998
  }
3002
- function detectProvider(evidence, rule2, deps, files, pathBlob) {
3003
- if (evidence[rule2.area]) {
2999
+ function detectProvider(evidence, rule, deps, files, pathBlob) {
3000
+ if (evidence[rule.area]) {
3004
3001
  return;
3005
3002
  }
3006
- const signals = collectSignals(rule2, deps, files, pathBlob);
3003
+ const signals = collectSignals(rule, deps, files, pathBlob);
3007
3004
  if (signals.length === 0) {
3008
3005
  return;
3009
3006
  }
3010
- evidence[rule2.area] = {
3011
- area: rule2.area,
3012
- provider: rule2.provider,
3007
+ evidence[rule.area] = {
3008
+ area: rule.area,
3009
+ provider: rule.provider,
3013
3010
  status: ["detected"],
3014
3011
  signals
3015
3012
  };
3016
3013
  }
3017
- function collectSignals(rule2, deps, files, pathBlob) {
3014
+ function collectSignals(rule, deps, files, pathBlob) {
3018
3015
  const signals = [];
3019
3016
  for (const dep of deps) {
3020
- if ((rule2.packages ?? []).some((pattern) => testRegex(pattern, dep))) {
3017
+ if ((rule.packages ?? []).some((pattern) => testRegex(pattern, dep))) {
3021
3018
  addSignal(signals, `package: ${dep}`);
3022
3019
  }
3023
3020
  }
3024
3021
  const upperContents = files.map((file) => file.content).join("\n").toUpperCase();
3025
- for (const envName of rule2.env ?? []) {
3022
+ for (const envName of rule.env ?? []) {
3026
3023
  if (upperContents.includes(envName.toUpperCase())) {
3027
3024
  addSignal(signals, `env: ${envName}`);
3028
3025
  }
3029
3026
  }
3030
- const pathLines = pathBlob.split(/\r?\n/).map((path3) => path3.trim()).filter(Boolean);
3031
- for (const path3 of pathLines) {
3032
- if ((rule2.paths ?? []).some((pattern) => testRegex(pattern, path3))) {
3033
- addSignal(signals, `${pathSignalPrefix(path3)}: ${path3}`);
3027
+ const pathLines = pathBlob.split(/\r?\n/).map((path) => path.trim()).filter(Boolean);
3028
+ for (const path of pathLines) {
3029
+ if ((rule.paths ?? []).some((pattern) => testRegex(pattern, path))) {
3030
+ addSignal(signals, `${pathSignalPrefix(path)}: ${path}`);
3034
3031
  }
3035
3032
  }
3036
3033
  for (const file of files) {
3037
- for (const importName of rule2.imports ?? []) {
3034
+ for (const importName of rule.imports ?? []) {
3038
3035
  if (containsImport(file.lowerContent, importName)) {
3039
3036
  addSignal(signals, `import: ${importName}`);
3040
3037
  }
3041
3038
  }
3042
- for (const item3 of rule2.content ?? []) {
3039
+ for (const item3 of rule.content ?? []) {
3043
3040
  if (testRegex(item3.pattern, file.content)) {
3044
3041
  addSignal(signals, item3.signal);
3045
3042
  }
3046
3043
  }
3047
3044
  if (isDocsPath(file.path)) {
3048
- for (const docsTerm of rule2.docs ?? []) {
3045
+ for (const docsTerm of rule.docs ?? []) {
3049
3046
  if (file.lowerContent.includes(docsTerm.toLowerCase())) {
3050
- addSignal(signals, `docs: ${file.displayPath} mentions ${rule2.label}`);
3047
+ addSignal(signals, `docs: ${file.displayPath} mentions ${rule.label}`);
3051
3048
  break;
3052
3049
  }
3053
3050
  }
@@ -3059,14 +3056,14 @@ function containsImport(content, importName) {
3059
3056
  const escaped = importName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&").toLowerCase();
3060
3057
  return new RegExp(`(?:from\\s+['"]${escaped}['"]|import\\s*\\(\\s*['"]${escaped}['"]|require\\s*\\(\\s*['"]${escaped}['"])`).test(content);
3061
3058
  }
3062
- function isDocsPath(path3) {
3063
- return /(^|\/)(readme|product|spec)\.md$/.test(path3) || /(^|\/)docs\/.*\.md$/.test(path3);
3059
+ function isDocsPath(path) {
3060
+ return /(^|\/)(readme|product|spec)\.md$/.test(path) || /(^|\/)docs\/.*\.md$/.test(path);
3064
3061
  }
3065
- function pathSignalPrefix(path3) {
3066
- if (/webhook|checkout|billing|api\/|route\.[jt]s$/.test(path3)) {
3062
+ function pathSignalPrefix(path) {
3063
+ if (/webhook|checkout|billing|api\/|route\.[jt]s$/.test(path)) {
3067
3064
  return "route";
3068
3065
  }
3069
- if (/config|\.json$|\.toml$|\.ya?ml$/.test(path3)) {
3066
+ if (/config|\.json$|\.toml$|\.ya?ml$/.test(path)) {
3070
3067
  return "config";
3071
3068
  }
3072
3069
  return "file";
@@ -3077,19 +3074,19 @@ function addSignal(signals, signal) {
3077
3074
  }
3078
3075
  }
3079
3076
  function freezeProviderRules(rules) {
3080
- for (const rule2 of rules) {
3081
- Object.freeze(rule2.packages ?? []);
3082
- Object.freeze(rule2.env ?? []);
3083
- Object.freeze(rule2.paths ?? []);
3084
- Object.freeze(rule2.imports ?? []);
3085
- Object.freeze(rule2.docs ?? []);
3086
- if (rule2.content) {
3087
- for (const item3 of rule2.content) {
3077
+ for (const rule of rules) {
3078
+ Object.freeze(rule.packages ?? []);
3079
+ Object.freeze(rule.env ?? []);
3080
+ Object.freeze(rule.paths ?? []);
3081
+ Object.freeze(rule.imports ?? []);
3082
+ Object.freeze(rule.docs ?? []);
3083
+ if (rule.content) {
3084
+ for (const item3 of rule.content) {
3088
3085
  Object.freeze(item3);
3089
3086
  }
3090
- Object.freeze(rule2.content);
3087
+ Object.freeze(rule.content);
3091
3088
  }
3092
- Object.freeze(rule2);
3089
+ Object.freeze(rule);
3093
3090
  }
3094
3091
  return Object.freeze(rules);
3095
3092
  }
@@ -3217,8 +3214,8 @@ function normalizeSelectedAt(value) {
3217
3214
  }
3218
3215
  return null;
3219
3216
  }
3220
- function normalizePath(path3) {
3221
- return path3.replace(/\\/g, "/").toLowerCase();
3217
+ function normalizePath(path) {
3218
+ return path.replace(/\\/g, "/").toLowerCase();
3222
3219
  }
3223
3220
  function isObject(value) {
3224
3221
  return typeof value === "object" && value !== null;
@@ -3246,8 +3243,8 @@ var WEAK_PATH_SEGMENTS = /* @__PURE__ */ new Set([
3246
3243
  "demo"
3247
3244
  ]);
3248
3245
  var TEST_FILE_PATTERN = /\.(test|spec)\.[jt]sx?$/;
3249
- function classifyProviderEvidencePath(path3) {
3250
- const normalized = normalizePath2(path3);
3246
+ function classifyProviderEvidencePath(path) {
3247
+ const normalized = normalizePath2(path);
3251
3248
  const segments = normalized.split("/").filter(Boolean);
3252
3249
  if (isDocsLikePath(normalized) || segments.some((segment) => WEAK_PATH_SEGMENTS.has(segment)) || TEST_FILE_PATTERN.test(normalized)) {
3253
3250
  return "weak";
@@ -3262,14 +3259,14 @@ function buildProviderTruthSnapshot(input) {
3262
3259
  const rowsByArea = {};
3263
3260
  void input.mcpVerifierState;
3264
3261
  void input.verificationLayer;
3265
- for (const rule2 of PROVIDER_RULES) {
3266
- const evidence = collectProviderTruthEvidence(rule2, deps, files, pathLines);
3267
- const selected = choices.choices[rule2.area]?.provider === rule2.provider;
3262
+ for (const rule of PROVIDER_RULES) {
3263
+ const evidence = collectProviderTruthEvidence(rule, deps, files, pathLines);
3264
+ const selected = choices.choices[rule.area]?.provider === rule.provider;
3268
3265
  if (evidence.length === 0 && !selected) {
3269
3266
  continue;
3270
3267
  }
3271
- const row = buildRow(rule2, evidence, selected);
3272
- rowsByArea[rule2.area] = [...rowsByArea[rule2.area] ?? [], row];
3268
+ const row = buildRow(rule, evidence, selected);
3269
+ rowsByArea[rule.area] = [...rowsByArea[rule.area] ?? [], row];
3273
3270
  }
3274
3271
  for (const [area, choice] of Object.entries(choices.choices)) {
3275
3272
  if (!choice) {
@@ -3301,32 +3298,32 @@ function buildProviderTruthSnapshot(input) {
3301
3298
  summary
3302
3299
  };
3303
3300
  }
3304
- function collectProviderTruthEvidence(rule2, deps, files, pathLines) {
3301
+ function collectProviderTruthEvidence(rule, deps, files, pathLines) {
3305
3302
  const evidence = [];
3306
3303
  for (const dep of deps) {
3307
- if ((rule2.packages ?? []).some((pattern) => testRegex2(pattern, dep))) {
3308
- addEvidence(evidence, rule2, {
3304
+ if ((rule.packages ?? []).some((pattern) => testRegex2(pattern, dep))) {
3305
+ addEvidence(evidence, rule, {
3309
3306
  kind: "package-installed",
3310
3307
  strength: "medium",
3311
- label: `${rule2.label} package installed`,
3308
+ label: `${rule.label} package installed`,
3312
3309
  detail: dep,
3313
3310
  points: 20,
3314
3311
  isRuntimeEvidence: false
3315
3312
  });
3316
3313
  }
3317
3314
  }
3318
- for (const path3 of pathLines) {
3319
- if (!(rule2.paths ?? []).some((pattern) => testRegex2(pattern, path3))) {
3315
+ for (const path of pathLines) {
3316
+ if (!(rule.paths ?? []).some((pattern) => testRegex2(pattern, path))) {
3320
3317
  continue;
3321
3318
  }
3322
- const pathClass = classifyProviderEvidencePath(path3);
3323
- const kind = weakPathKind(path3, "active-runtime-route") ?? routeKind(path3);
3324
- addEvidence(evidence, rule2, {
3319
+ const pathClass = classifyProviderEvidencePath(path);
3320
+ const kind = weakPathKind(path, "active-runtime-route") ?? routeKind(path);
3321
+ addEvidence(evidence, rule, {
3325
3322
  kind,
3326
3323
  strength: pathClass === "runtime" ? "strong" : "weak",
3327
- label: `${rule2.label} ${pathClass === "runtime" ? "runtime route or path" : "weak path reference"}`,
3328
- file: path3,
3329
- points: pathClass === "runtime" ? 35 : weakEvidencePoints(path3),
3324
+ label: `${rule.label} ${pathClass === "runtime" ? "runtime route or path" : "weak path reference"}`,
3325
+ file: path,
3326
+ points: pathClass === "runtime" ? 35 : weakEvidencePoints(path),
3330
3327
  isRuntimeEvidence: pathClass === "runtime"
3331
3328
  });
3332
3329
  }
@@ -3334,45 +3331,45 @@ function collectProviderTruthEvidence(rule2, deps, files, pathLines) {
3334
3331
  const pathClass = classifyProviderEvidencePath(file.path);
3335
3332
  const sourceContent = pathClass === "runtime" ? file.executableContent : file.content;
3336
3333
  const sourceLowerContent = pathClass === "runtime" ? file.lowerExecutableContent : file.lowerContent;
3337
- for (const envName of rule2.env ?? []) {
3334
+ for (const envName of rule.env ?? []) {
3338
3335
  if (!sourceContent.toUpperCase().includes(envName.toUpperCase())) {
3339
3336
  continue;
3340
3337
  }
3341
- addEvidence(evidence, rule2, {
3338
+ addEvidence(evidence, rule, {
3342
3339
  kind: pathClass === "runtime" ? "runtime-env-usage" : "env-name-only",
3343
3340
  strength: pathClass === "runtime" ? "medium" : "weak",
3344
- label: `${rule2.label} env name ${pathClass === "runtime" ? "used in runtime source" : "mentioned outside runtime source"}`,
3341
+ label: `${rule.label} env name ${pathClass === "runtime" ? "used in runtime source" : "mentioned outside runtime source"}`,
3345
3342
  file: file.displayPath,
3346
3343
  detail: envName,
3347
3344
  points: pathClass === "runtime" ? 20 : weakEvidencePoints(file.path),
3348
3345
  isRuntimeEvidence: pathClass === "runtime"
3349
3346
  });
3350
3347
  }
3351
- for (const importName of rule2.imports ?? []) {
3348
+ for (const importName of rule.imports ?? []) {
3352
3349
  if (!containsImport2(sourceLowerContent, importName)) {
3353
3350
  continue;
3354
3351
  }
3355
3352
  const weakKind = weakPathKind(file.path, "sdk-import-source");
3356
- addEvidence(evidence, rule2, {
3353
+ addEvidence(evidence, rule, {
3357
3354
  kind: weakKind ?? "sdk-import-source",
3358
3355
  strength: pathClass === "runtime" ? "strong" : "weak",
3359
- label: `${rule2.label} SDK import ${pathClass === "runtime" ? "in runtime source" : "outside runtime source"}`,
3356
+ label: `${rule.label} SDK import ${pathClass === "runtime" ? "in runtime source" : "outside runtime source"}`,
3360
3357
  file: file.displayPath,
3361
3358
  detail: importName,
3362
3359
  points: pathClass === "runtime" ? 35 : weakEvidencePoints(file.path),
3363
3360
  isRuntimeEvidence: pathClass === "runtime"
3364
3361
  });
3365
3362
  }
3366
- for (const item3 of rule2.content ?? []) {
3363
+ for (const item3 of rule.content ?? []) {
3367
3364
  if (!testRegex2(item3.pattern, sourceContent)) {
3368
3365
  continue;
3369
3366
  }
3370
3367
  const strongKind = contentSignalKind(item3.signal);
3371
3368
  const weakKind = weakPathKind(file.path, strongKind);
3372
- addEvidence(evidence, rule2, {
3369
+ addEvidence(evidence, rule, {
3373
3370
  kind: weakKind ?? strongKind,
3374
3371
  strength: pathClass === "runtime" ? "strong" : "weak",
3375
- label: `${rule2.label} ${pathClass === "runtime" ? "runtime content signal" : "weak content reference"}`,
3372
+ label: `${rule.label} ${pathClass === "runtime" ? "runtime content signal" : "weak content reference"}`,
3376
3373
  file: file.displayPath,
3377
3374
  detail: item3.signal,
3378
3375
  points: pathClass === "runtime" ? 35 : weakEvidencePoints(file.path),
@@ -3380,14 +3377,14 @@ function collectProviderTruthEvidence(rule2, deps, files, pathLines) {
3380
3377
  });
3381
3378
  }
3382
3379
  if (isDocsLikePath(file.path)) {
3383
- for (const docsTerm of rule2.docs ?? []) {
3380
+ for (const docsTerm of rule.docs ?? []) {
3384
3381
  if (!file.lowerContent.includes(docsTerm.toLowerCase())) {
3385
3382
  continue;
3386
3383
  }
3387
- addEvidence(evidence, rule2, {
3384
+ addEvidence(evidence, rule, {
3388
3385
  kind: "docs-mention",
3389
3386
  strength: "weak",
3390
- label: `${rule2.label} mentioned in docs`,
3387
+ label: `${rule.label} mentioned in docs`,
3391
3388
  file: file.displayPath,
3392
3389
  detail: docsTerm,
3393
3390
  points: 4,
@@ -3399,16 +3396,16 @@ function collectProviderTruthEvidence(rule2, deps, files, pathLines) {
3399
3396
  }
3400
3397
  return evidence.sort(compareEvidence);
3401
3398
  }
3402
- function buildRow(rule2, evidence, selected) {
3399
+ function buildRow(rule, evidence, selected) {
3403
3400
  const score = evidence.reduce((total, item3) => total + item3.points, 0);
3404
3401
  const roles = rolesForEvidence(evidence, selected);
3405
- applyMcpSupportRoles(rule2.provider, roles);
3402
+ applyMcpSupportRoles(rule.provider, roles);
3406
3403
  const confidence = confidenceForEvidence(evidence, roles);
3407
- const mcpProof = mcpProofForProvider(rule2.provider);
3404
+ const mcpProof = mcpProofForProvider(rule.provider);
3408
3405
  return {
3409
- area: rule2.area,
3410
- provider: rule2.provider,
3411
- providerLabel: providerLabel(rule2.provider),
3406
+ area: rule.area,
3407
+ provider: rule.provider,
3408
+ providerLabel: providerLabel(rule.provider),
3412
3409
  roles,
3413
3410
  confidence,
3414
3411
  score,
@@ -3665,10 +3662,10 @@ function statusBadgesForRoles(roles, confidence) {
3665
3662
  badges.push(confidence.toUpperCase());
3666
3663
  return badges;
3667
3664
  }
3668
- function addEvidence(evidence, rule2, item3) {
3665
+ function addEvidence(evidence, rule, item3) {
3669
3666
  const id = [
3670
- rule2.area,
3671
- rule2.provider,
3667
+ rule.area,
3668
+ rule.provider,
3672
3669
  item3.kind,
3673
3670
  item3.file ?? "",
3674
3671
  item3.detail ?? item3.label
@@ -3683,29 +3680,29 @@ function addEvidence(evidence, rule2, item3) {
3683
3680
  isManualProof: false
3684
3681
  });
3685
3682
  }
3686
- function weakPathKind(path3, fallback) {
3687
- if (classifyProviderEvidencePath(path3) === "runtime") {
3683
+ function weakPathKind(path, fallback) {
3684
+ if (classifyProviderEvidencePath(path) === "runtime") {
3688
3685
  return null;
3689
3686
  }
3690
- if (isDocsLikePath(path3)) {
3687
+ if (isDocsLikePath(path)) {
3691
3688
  return "docs-mention";
3692
3689
  }
3693
- if (/(^|\/)(__tests__|tests)(\/|$)|\.(test|spec)\.[jt]sx?$/.test(normalizePath2(path3))) {
3690
+ if (/(^|\/)(__tests__|tests)(\/|$)|\.(test|spec)\.[jt]sx?$/.test(normalizePath2(path))) {
3694
3691
  return "test-reference";
3695
3692
  }
3696
- if (/(^|\/)(tmp|out|outputs|videos|marketing|examples|demo)(\/|$)/.test(normalizePath2(path3))) {
3693
+ if (/(^|\/)(tmp|out|outputs|videos|marketing|examples|demo)(\/|$)/.test(normalizePath2(path))) {
3697
3694
  return "tmp-demo-example";
3698
3695
  }
3699
3696
  return fallback;
3700
3697
  }
3701
- function routeKind(path3) {
3702
- if (/webhook/.test(path3)) {
3698
+ function routeKind(path) {
3699
+ if (/webhook/.test(path)) {
3703
3700
  return "webhook-handler";
3704
3701
  }
3705
- if (/checkout|billing/.test(path3)) {
3702
+ if (/checkout|billing/.test(path)) {
3706
3703
  return "checkout-handler";
3707
3704
  }
3708
- if (/config|\.json$|\.toml$|\.ya?ml$/.test(path3)) {
3705
+ if (/config|\.json$|\.toml$|\.ya?ml$/.test(path)) {
3709
3706
  return "deployment-config";
3710
3707
  }
3711
3708
  return "active-runtime-route";
@@ -3722,11 +3719,11 @@ function contentSignalKind(signal) {
3722
3719
  }
3723
3720
  return "active-runtime-route";
3724
3721
  }
3725
- function weakEvidencePoints(path3) {
3726
- if (isDocsLikePath(path3)) {
3722
+ function weakEvidencePoints(path) {
3723
+ if (isDocsLikePath(path)) {
3727
3724
  return 4;
3728
3725
  }
3729
- if (/(^|\/)(__tests__|tests)(\/|$)|\.(test|spec)\.[jt]sx?$/.test(normalizePath2(path3))) {
3726
+ if (/(^|\/)(__tests__|tests)(\/|$)|\.(test|spec)\.[jt]sx?$/.test(normalizePath2(path))) {
3730
3727
  return 6;
3731
3728
  }
3732
3729
  return 8;
@@ -3745,8 +3742,8 @@ function toScannableFile(file) {
3745
3742
  }
3746
3743
  function collectPathLines(scan, files) {
3747
3744
  const paths = /* @__PURE__ */ new Set();
3748
- for (const path3 of scan.fileTree.split(/\r?\n/)) {
3749
- const normalized = normalizePath2(path3.trim());
3745
+ for (const path of scan.fileTree.split(/\r?\n/)) {
3746
+ const normalized = normalizePath2(path.trim());
3750
3747
  if (normalized) {
3751
3748
  paths.add(normalized);
3752
3749
  }
@@ -3760,8 +3757,8 @@ function containsImport2(content, importName) {
3760
3757
  const escaped = importName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&").toLowerCase();
3761
3758
  return new RegExp(`(?:from\\s+['"]${escaped}['"]|import\\s*\\(\\s*['"]${escaped}['"]|require\\s*\\(\\s*['"]${escaped}['"])`).test(content);
3762
3759
  }
3763
- function isDocsLikePath(path3) {
3764
- const normalized = normalizePath2(path3);
3760
+ function isDocsLikePath(path) {
3761
+ const normalized = normalizePath2(path);
3765
3762
  return /(^|\/)(readme|product|spec)\.mdx?$/.test(normalized) || /(^|\/)docs\/.*\.mdx?$/.test(normalized);
3766
3763
  }
3767
3764
  function stripComments(content) {
@@ -3773,8 +3770,8 @@ function compareRows(a, b3) {
3773
3770
  function compareEvidence(a, b3) {
3774
3771
  return b3.points - a.points || a.kind.localeCompare(b3.kind) || (a.file ?? "").localeCompare(b3.file ?? "");
3775
3772
  }
3776
- function normalizePath2(path3) {
3777
- return path3.replace(/\\/g, "/").toLowerCase();
3773
+ function normalizePath2(path) {
3774
+ return path.replace(/\\/g, "/").toLowerCase();
3778
3775
  }
3779
3776
  function rowPriority(row) {
3780
3777
  if (row.roles.includes("live-verified")) {
@@ -3969,8 +3966,8 @@ Return ONLY the JSON object \u2014 no markdown, no explanation.
3969
3966
  }
3970
3967
 
3971
3968
  // ../../src/station/envEvidence.ts
3972
- function normalizeEvidencePath(path3) {
3973
- return path3.replace(/\\/g, "/");
3969
+ function normalizeEvidencePath(path) {
3970
+ return path.replace(/\\/g, "/");
3974
3971
  }
3975
3972
  function uniquePaths(paths) {
3976
3973
  return Array.from(new Set(paths.map(normalizeEvidencePath)));
@@ -4038,7 +4035,7 @@ function collectEnvVarEvidence(scan, names) {
4038
4035
  present: true,
4039
4036
  mode,
4040
4037
  source: "non-secret-content",
4041
- evidence: uniquePaths(contentEvidence).map((path3) => `file: ${path3}`)
4038
+ evidence: uniquePaths(contentEvidence).map((path) => `file: ${path}`)
4042
4039
  };
4043
4040
  }
4044
4041
  if (nonEmptyAssignmentEvidence.length > 0) {
@@ -4047,7 +4044,7 @@ function collectEnvVarEvidence(scan, names) {
4047
4044
  present: true,
4048
4045
  mode: "unknown",
4049
4046
  source: "non-secret-content",
4050
- evidence: uniquePaths(nonEmptyAssignmentEvidence).map((path3) => `file: ${path3}`)
4047
+ evidence: uniquePaths(nonEmptyAssignmentEvidence).map((path) => `file: ${path}`)
4051
4048
  };
4052
4049
  }
4053
4050
  if (nameOnlyEvidence.length > 0) {
@@ -4056,7 +4053,7 @@ function collectEnvVarEvidence(scan, names) {
4056
4053
  present: true,
4057
4054
  mode: "unknown",
4058
4055
  source: "variable-name-only",
4059
- evidence: uniquePaths(nameOnlyEvidence).map((path3) => `file: ${path3}`)
4056
+ evidence: uniquePaths(nameOnlyEvidence).map((path) => `file: ${path}`)
4060
4057
  };
4061
4058
  }
4062
4059
  if (secretEvidence.length > 0) {
@@ -4065,7 +4062,7 @@ function collectEnvVarEvidence(scan, names) {
4065
4062
  present: false,
4066
4063
  mode: "unknown",
4067
4064
  source: "secret-file-path",
4068
- evidence: secretEvidence.map((path3) => `secret file: ${path3}`)
4065
+ evidence: secretEvidence.map((path) => `secret file: ${path}`)
4069
4066
  };
4070
4067
  }
4071
4068
  return {
@@ -4160,10 +4157,10 @@ ${pathBlob}`),
4160
4157
  function visibleFiles(scan) {
4161
4158
  return scan.files.filter((file) => !file.isSecret && typeof file.content === "string");
4162
4159
  }
4163
- function foundOrMissing(id, label2, found, evidence) {
4160
+ function foundOrMissing(id, label, found, evidence) {
4164
4161
  return {
4165
4162
  id,
4166
- label: label2,
4163
+ label,
4167
4164
  status: found ? "found" : "missing",
4168
4165
  evidence: unique(evidence).slice(0, 6)
4169
4166
  };
@@ -4200,7 +4197,7 @@ function evidenceFor(files, pattern) {
4200
4197
  return files.filter((file) => pattern.test(file.content)).map((file) => `file: ${normalizePath3(file.path)}`).slice(0, 6);
4201
4198
  }
4202
4199
  function pathEvidence(scan, pattern) {
4203
- return scan.files.map((file) => normalizePath3(file.path)).filter((path3) => pattern.test(path3)).map((path3) => `file: ${path3}`).slice(0, 6);
4200
+ return scan.files.map((file) => normalizePath3(file.path)).filter((path) => pattern.test(path)).map((path) => `file: ${path}`).slice(0, 6);
4204
4201
  }
4205
4202
  function isClientReachableFile(file) {
4206
4203
  const content = file.content;
@@ -4216,22 +4213,22 @@ function isClientReachableFile(file) {
4216
4213
  function hasUseClientDirective(content) {
4217
4214
  return /^(?:\s|;|\/\/[^\n]*(?:\n|$)|\/\*[\s\S]*?\*\/)*['"]use client['"]/.test(content);
4218
4215
  }
4219
- function isBrowserOnlyPath(path3) {
4220
- const normalized = normalizePath3(path3).toLowerCase();
4221
- if (isServerOnlyPath(path3)) {
4216
+ function isBrowserOnlyPath(path) {
4217
+ const normalized = normalizePath3(path).toLowerCase();
4218
+ if (isServerOnlyPath(path)) {
4222
4219
  return false;
4223
4220
  }
4224
4221
  return /(^|\/)(components|hooks|contexts|providers)\//.test(normalized) || /\.(jsx|tsx)$/.test(normalized);
4225
4222
  }
4226
- function isServerOnlyPath(path3) {
4227
- const normalized = normalizePath3(path3).toLowerCase();
4223
+ function isServerOnlyPath(path) {
4224
+ const normalized = normalizePath3(path).toLowerCase();
4228
4225
  return /\/api\/|app\/api\/|pages\/api\/|route\.[jt]s$|\.server\.[jt]sx?$|\/server\//.test(normalized);
4229
4226
  }
4230
- function isEnvOrDocPath(path3) {
4231
- return ENV_OR_DOC_PATH.test(normalizePath3(path3).toLowerCase());
4227
+ function isEnvOrDocPath(path) {
4228
+ return ENV_OR_DOC_PATH.test(normalizePath3(path).toLowerCase());
4232
4229
  }
4233
- function normalizePath3(path3) {
4234
- return path3.replace(/\\/g, "/");
4230
+ function normalizePath3(path) {
4231
+ return path.replace(/\\/g, "/");
4235
4232
  }
4236
4233
  function unique(values) {
4237
4234
  return [...new Set(values)];
@@ -5364,10 +5361,10 @@ function visibleFiles2(scan) {
5364
5361
  lowerContent: file.content.toLowerCase()
5365
5362
  }));
5366
5363
  }
5367
- function item(id, label2, passed, evidence, promptHint) {
5364
+ function item(id, label, passed, evidence, promptHint) {
5368
5365
  return {
5369
5366
  id,
5370
- label: label2,
5367
+ label,
5371
5368
  status: passed ? "passed" : "missing",
5372
5369
  evidence,
5373
5370
  promptHint
@@ -5397,7 +5394,7 @@ function hasSupabaseClient(files, pathBlob) {
5397
5394
  }
5398
5395
  function clientEvidence(files, pathBlob) {
5399
5396
  const evidence = [];
5400
- const pathMatch = pathBlob.split(/\r?\n/).find((path3) => /(^|\/)(lib|utils|src\/lib|src\/utils)\/supabase\.[jt]s\b/i.test(path3));
5397
+ const pathMatch = pathBlob.split(/\r?\n/).find((path) => /(^|\/)(lib|utils|src\/lib|src\/utils)\/supabase\.[jt]s\b/i.test(path));
5401
5398
  if (pathMatch) {
5402
5399
  evidence.push(`file: ${pathMatch}`);
5403
5400
  }
@@ -5414,11 +5411,11 @@ function hasSchemaOrMigration(files, pathBlob) {
5414
5411
  return /(^|\n|\/)supabase\/migrations\/[^/\n]+\.sql\b/i.test(pathBlob) || /(^|\n|\/)(migrations?|schema)\/[^/\n]+\.(sql|ts|js)\b/i.test(pathBlob) || files.some((file) => /create\s+table|alter\s+table/i.test(file.content));
5415
5412
  }
5416
5413
  function schemaEvidence(files, pathBlob) {
5417
- const path3 = pathBlob.split(/\r?\n/).find(
5414
+ const path = pathBlob.split(/\r?\n/).find(
5418
5415
  (entry) => /(^|\/)supabase\/migrations\/[^/]+\.sql\b/i.test(entry) || /(^|\/)(migrations?|schema)\/[^/]+\.(sql|ts|js)\b/i.test(entry)
5419
5416
  );
5420
- if (path3) {
5421
- return [`schema: ${path3}`];
5417
+ if (path) {
5418
+ return [`schema: ${path}`];
5422
5419
  }
5423
5420
  const file = files.find((entry) => /create\s+table|alter\s+table/i.test(entry.content));
5424
5421
  return file ? [`schema: ${file.path}`] : [];
@@ -5427,9 +5424,9 @@ function hasRlsEvidence(files, pathBlob) {
5427
5424
  return /\/policies\/|_rls\.sql|\brls\b/i.test(pathBlob) || files.some((file) => /enable\s+row\s+level\s+security|create\s+policy|alter\s+table[\s\S]{0,200}enable\s+row\s+level/i.test(file.content));
5428
5425
  }
5429
5426
  function rlsEvidence(files, pathBlob) {
5430
- const path3 = pathBlob.split(/\r?\n/).find((entry) => /\/policies\/|_rls\.sql|\brls\b/i.test(entry));
5431
- if (path3) {
5432
- return [`rls: ${path3}`];
5427
+ const path = pathBlob.split(/\r?\n/).find((entry) => /\/policies\/|_rls\.sql|\brls\b/i.test(entry));
5428
+ if (path) {
5429
+ return [`rls: ${path}`];
5433
5430
  }
5434
5431
  const file = files.find((entry) => /enable\s+row\s+level\s+security|create\s+policy|alter\s+table[\s\S]{0,200}enable\s+row\s+level/i.test(entry.content));
5435
5432
  return file ? [`rls: ${file.path}`] : [];
@@ -5438,11 +5435,11 @@ function hasGeneratedTypes(files, pathBlob) {
5438
5435
  return /database\.types\.[jt]s\b|supabase.*types\.[jt]s\b|types\/database\.[jt]s\b/i.test(pathBlob) || files.some((file) => /export\s+type\s+database\b|export\s+interface\s+database\b/i.test(file.content));
5439
5436
  }
5440
5437
  function generatedTypeEvidence(files, pathBlob) {
5441
- const path3 = pathBlob.split(/\r?\n/).find(
5438
+ const path = pathBlob.split(/\r?\n/).find(
5442
5439
  (entry) => /database\.types\.[jt]s\b|supabase.*types\.[jt]s\b|types\/database\.[jt]s\b/i.test(entry)
5443
5440
  );
5444
- if (path3) {
5445
- return [`types: ${path3}`];
5441
+ if (path) {
5442
+ return [`types: ${path}`];
5446
5443
  }
5447
5444
  const file = files.find((entry) => /export\s+type\s+database\b|export\s+interface\s+database\b/i.test(entry.content));
5448
5445
  return file ? [`types: ${file.path}`] : [];
@@ -5459,8 +5456,8 @@ function serviceRoleSafetyItem(files) {
5459
5456
  promptHint: "Move service-role usage to server-only code and use public anon keys in frontend clients."
5460
5457
  };
5461
5458
  }
5462
- function isClientExecutedPath(path3) {
5463
- return /\.(tsx|jsx)$/.test(path3) || /(^|\/)(components|pages|app|client|frontend|web)\//.test(path3) || /\.client\.[jt]sx?$/.test(path3);
5459
+ function isClientExecutedPath(path) {
5460
+ return /\.(tsx|jsx)$/.test(path) || /(^|\/)(components|pages|app|client|frontend|web)\//.test(path) || /\.client\.[jt]sx?$/.test(path);
5464
5461
  }
5465
5462
 
5466
5463
  // ../../src/station/stackWiring.ts
@@ -6285,7 +6282,7 @@ function analyzeSecretsHygiene(ctx) {
6285
6282
  promptSubject: "secrets hygiene",
6286
6283
  items: [
6287
6284
  item2("env-example-found", "Env example or docs found", Boolean(ctx.scan.stackSignals.hasEnvExample) || /\.env\.example|env\.example|environment variables/i.test(ctx.pathBlob + "\n" + ctx.contentBlob), pathEvidence2(ctx, /\.env\.example|env\.example/i), "Add an env example or setup docs with variable names only."),
6288
- item2("secret-files-ignored", "Secret files detected as private", Array.isArray(ctx.scan.secretsFound) && ctx.scan.secretsFound.length > 0, ctx.scan.secretsFound.slice(0, 4).map((path3) => `secret file: ${path3}`), "Keep real .env files private and out of copied prompts or docs."),
6285
+ item2("secret-files-ignored", "Secret files detected as private", Array.isArray(ctx.scan.secretsFound) && ctx.scan.secretsFound.length > 0, ctx.scan.secretsFound.slice(0, 4).map((path) => `secret file: ${path}`), "Keep real .env files private and out of copied prompts or docs."),
6289
6286
  item2("frontend-secrets-clean", "No obvious frontend secret exposure found", unsafePublicSecret.length === 0, unsafePublicSecret.slice(0, 4).map((file) => `unsafe reference: ${file.path}`), "Move secret values and private keys out of frontend/client-executed files."),
6290
6287
  item2("gitignore-env-found", "Env files ignored by git", /\.gitignore/i.test(ctx.pathBlob) && /\.env/i.test(ctx.contentBlob), pathEvidence2(ctx, /\.gitignore/i), "Ensure .gitignore excludes real .env files while keeping .env.example committed."),
6291
6288
  manualItem("production-secret-rotation-checked", "Production secret rotation checked", "Confirm production secrets can be rotated and revoked in provider dashboards.")
@@ -6361,29 +6358,29 @@ function summarize(summary) {
6361
6358
  readinessPercent: Math.round(passedCount / Math.max(totalCount, 1) * 100)
6362
6359
  };
6363
6360
  }
6364
- function item2(id, label2, passed, evidence, promptHint) {
6361
+ function item2(id, label, passed, evidence, promptHint) {
6365
6362
  return {
6366
6363
  id,
6367
- label: label2,
6364
+ label,
6368
6365
  status: passed ? "passed" : "missing",
6369
6366
  evidence,
6370
6367
  promptHint
6371
6368
  };
6372
6369
  }
6373
- function manualItem(id, label2, promptHint) {
6370
+ function manualItem(id, label, promptHint) {
6374
6371
  return {
6375
6372
  id,
6376
- label: label2,
6373
+ label,
6377
6374
  status: "manual",
6378
6375
  evidence: [],
6379
6376
  promptHint
6380
6377
  };
6381
6378
  }
6382
- function secretSafetyItem(ctx, id, label2, pattern, promptHint) {
6379
+ function secretSafetyItem(ctx, id, label, pattern, promptHint) {
6383
6380
  const exposed = ctx.files.filter((file) => isClientExecutedPath2(file.normalizedPath) && pattern.test(file.content));
6384
6381
  return {
6385
6382
  id,
6386
- label: label2,
6383
+ label,
6387
6384
  status: exposed.length > 0 ? "missing" : "passed",
6388
6385
  evidence: exposed.slice(0, 4).map((file) => `unsafe reference: ${file.path}`),
6389
6386
  promptHint
@@ -6408,13 +6405,13 @@ function envEvidence2(ctx, patterns) {
6408
6405
  return evidence.slice(0, 4);
6409
6406
  }
6410
6407
  function pathEvidence2(ctx, pattern) {
6411
- return ctx.pathBlob.split(/\r?\n/).filter((path3) => pattern.test(path3)).slice(0, 4).map((path3) => `file: ${path3}`);
6408
+ return ctx.pathBlob.split(/\r?\n/).filter((path) => pattern.test(path)).slice(0, 4).map((path) => `file: ${path}`);
6412
6409
  }
6413
6410
  function fileEvidence(ctx, pattern) {
6414
6411
  return ctx.files.filter((file) => pattern.test(file.path) || pattern.test(file.content)).slice(0, 4).map((file) => `file: ${file.path}`);
6415
6412
  }
6416
- function isClientExecutedPath2(path3) {
6417
- return /\.(tsx|jsx)$/.test(path3) || /(^|\/)(components|pages|app|client|frontend|web)\//.test(path3) || /\.client\.[jt]sx?$/.test(path3);
6413
+ function isClientExecutedPath2(path) {
6414
+ return /\.(tsx|jsx)$/.test(path) || /(^|\/)(components|pages|app|client|frontend|web)\//.test(path) || /\.client\.[jt]sx?$/.test(path);
6418
6415
  }
6419
6416
 
6420
6417
  // ../../src/station/verification.ts
@@ -6543,27 +6540,27 @@ function emptyArea(area) {
6543
6540
  manual: []
6544
6541
  };
6545
6542
  }
6546
- function normalizePath4(path3) {
6547
- return path3.replace(/\\/g, "/").replace(/^[\s\u2500\u2502\u2514\u251c>*+-]+/u, "").trim().toLowerCase();
6543
+ function normalizePath4(path) {
6544
+ return path.replace(/\\/g, "/").replace(/^[\s\u2500\u2502\u2514\u251c>*+-]+/u, "").trim().toLowerCase();
6548
6545
  }
6549
- function addFound(area, label2, source, detail) {
6550
- addItem(area, "found", label2, source, detail);
6546
+ function addFound(area, label, source, detail) {
6547
+ addItem(area, "found", label, source, detail);
6551
6548
  }
6552
- function addMissing(area, label2, source, detail) {
6553
- addItem(area, "missing", label2, source, detail);
6549
+ function addMissing(area, label, source, detail) {
6550
+ addItem(area, "missing", label, source, detail);
6554
6551
  }
6555
- function addManual(area, label2, source, detail) {
6556
- addItem(area, "manual", label2, source, detail);
6552
+ function addManual(area, label, source, detail) {
6553
+ addItem(area, "manual", label, source, detail);
6557
6554
  }
6558
- function addItem(area, status, label2, source, detail) {
6559
- const item3 = compactItem(label2, status, source, detail);
6555
+ function addItem(area, status, label, source, detail) {
6556
+ const item3 = compactItem(label, status, source, detail);
6560
6557
  const bucket = area[status];
6561
6558
  if (!bucket.some((existing) => existing.label === item3.label)) {
6562
6559
  bucket.push(item3);
6563
6560
  }
6564
6561
  }
6565
- function compactItem(label2, status, source, detail) {
6566
- return detail ? { label: label2, status, source, detail } : { label: label2, status, source };
6562
+ function compactItem(label, status, source, detail) {
6563
+ return detail ? { label, status, source, detail } : { label, status, source };
6567
6564
  }
6568
6565
  function hasDep(ctx, patterns) {
6569
6566
  return patterns.some((pattern) => {
@@ -6572,7 +6569,7 @@ function hasDep(ctx, patterns) {
6572
6569
  });
6573
6570
  }
6574
6571
  function hasPath(ctx, patterns) {
6575
- return [...ctx.paths].some((path3) => !ctx.secretPaths.has(path3) && patterns.some((pattern) => pattern.test(path3)));
6572
+ return [...ctx.paths].some((path) => !ctx.secretPaths.has(path) && patterns.some((pattern) => pattern.test(path)));
6576
6573
  }
6577
6574
  function hasContent(ctx, patterns) {
6578
6575
  return patterns.some((pattern) => pattern.test(ctx.contents));
@@ -7141,7 +7138,7 @@ var mockGitHubVerifier = {
7141
7138
  providerObservationMet: false,
7142
7139
  fixType: "mcp-connect",
7143
7140
  severity: "warning",
7144
- repoSignals: workflows.map((path3) => `workflow: ${path3}`),
7141
+ repoSignals: workflows.map((path) => `workflow: ${path}`),
7145
7142
  providerSignals: ["GitHub Actions API or MCP"],
7146
7143
  requiredEvidence: ["Latest workflow run status on default branch"]
7147
7144
  }),
@@ -7196,7 +7193,7 @@ var mockGitHubVerifier = {
7196
7193
  providerActual: "Not verified (mock \u2014 connect GitHub MCP read-only)",
7197
7194
  severity: "warning",
7198
7195
  suggestedFix: "mcp-connect",
7199
- evidenceRefs: workflows.map((path3) => `workflow: ${path3}`)
7196
+ evidenceRefs: workflows.map((path) => `workflow: ${path}`)
7200
7197
  }
7201
7198
  ];
7202
7199
  }
@@ -7589,9 +7586,9 @@ function runVerifier(verifier, ctx) {
7589
7586
  }
7590
7587
  const connectionState = verifier.connectStatus(ctx);
7591
7588
  const rawChecks = verifier.runChecks(ctx);
7592
- const checks = rawChecks.map((check) => ({
7593
- ...check,
7594
- status: coerceProviderVerificationStatus(check.status, connectionState)
7589
+ const checks = rawChecks.map((check2) => ({
7590
+ ...check2,
7591
+ status: coerceProviderVerificationStatus(check2.status, connectionState)
7595
7592
  }));
7596
7593
  const diffs = verifier.buildDiffs(ctx);
7597
7594
  return {
@@ -7998,12 +7995,12 @@ var import_promises5 = require("node:fs/promises");
7998
7995
  var import_node_path8 = require("node:path");
7999
7996
 
8000
7997
  // src/capabilities/classify.ts
8001
- function gapText(gap2) {
8002
- return `${gap2.id} ${gap2.title} ${gap2.detail} ${gap2.primaryMapCategory}`.toLowerCase();
7998
+ function gapText(gap) {
7999
+ return `${gap.id} ${gap.title} ${gap.detail} ${gap.primaryMapCategory}`.toLowerCase();
8003
8000
  }
8004
8001
  function statusForGaps(gaps) {
8005
- if (gaps.some((gap2) => gap2.severity === "critical")) return "critical";
8006
- if (gaps.some((gap2) => gap2.severity === "warning")) return "warning";
8002
+ if (gaps.some((gap) => gap.severity === "critical")) return "critical";
8003
+ if (gaps.some((gap) => gap.severity === "warning")) return "warning";
8007
8004
  if (gaps.length > 0) return "warning";
8008
8005
  return "unknown";
8009
8006
  }
@@ -8023,12 +8020,12 @@ function capabilityFromArea(area) {
8023
8020
  // src/capabilities/database.ts
8024
8021
  var databasePack = {
8025
8022
  key: "database",
8026
- classify(gap2) {
8027
- const text = gapText(gap2);
8023
+ classify(gap) {
8024
+ const text = gapText(gap);
8028
8025
  return /database|supabase|rls|migration|postgres|pooler|query/.test(text);
8029
8026
  },
8030
- riskTags(gap2) {
8031
- const text = gapText(gap2);
8027
+ riskTags(gap) {
8028
+ const text = gapText(gap);
8032
8029
  return [
8033
8030
  text.includes("rls") ? "rls" : "",
8034
8031
  text.includes("migration") ? "migration" : "",
@@ -8040,12 +8037,12 @@ var databasePack = {
8040
8037
  // src/capabilities/payments.ts
8041
8038
  var paymentsPack = {
8042
8039
  key: "payments",
8043
- classify(gap2) {
8044
- const text = gapText(gap2);
8040
+ classify(gap) {
8041
+ const text = gapText(gap);
8045
8042
  return /payment|stripe|checkout|billing|entitlement|subscription|refund|cancel/.test(text);
8046
8043
  },
8047
- riskTags(gap2) {
8048
- const text = gapText(gap2);
8044
+ riskTags(gap) {
8045
+ const text = gapText(gap);
8049
8046
  return [
8050
8047
  text.includes("entitlement") ? "entitlement-source" : "",
8051
8048
  text.includes("checkout") ? "checkout-flow" : "",
@@ -8057,12 +8054,12 @@ var paymentsPack = {
8057
8054
  // src/capabilities/scaling.ts
8058
8055
  var scalingPack = {
8059
8056
  key: "scaling",
8060
- classify(gap2) {
8061
- const text = gapText(gap2);
8057
+ classify(gap) {
8058
+ const text = gapText(gap);
8062
8059
  return /serverless|vercel|pooler|rate limit|rate-limit|cache|queue|cron|worker|runtime|connection/.test(text);
8063
8060
  },
8064
- riskTags(gap2) {
8065
- const text = gapText(gap2);
8061
+ riskTags(gap) {
8062
+ const text = gapText(gap);
8066
8063
  return [
8067
8064
  text.includes("serverless") || text.includes("vercel") ? "serverless" : "",
8068
8065
  text.includes("pooler") || text.includes("connection") ? "db-connection" : "",
@@ -8074,12 +8071,12 @@ var scalingPack = {
8074
8071
  // src/capabilities/security.ts
8075
8072
  var securityPack = {
8076
8073
  key: "security",
8077
- classify(gap2) {
8078
- const text = gapText(gap2);
8074
+ classify(gap) {
8075
+ const text = gapText(gap);
8079
8076
  return /secret|service role|token|api key|auth|authorization|browser-exposed|cors|csrf|session/.test(text);
8080
8077
  },
8081
- riskTags(gap2) {
8082
- const text = gapText(gap2);
8078
+ riskTags(gap) {
8079
+ const text = gapText(gap);
8083
8080
  return [
8084
8081
  text.includes("secret") || text.includes("api key") || text.includes("service role") ? "secret-boundary" : "",
8085
8082
  text.includes("auth") || text.includes("authorization") ? "auth-boundary" : "",
@@ -8091,12 +8088,12 @@ var securityPack = {
8091
8088
  // src/capabilities/webhooks.ts
8092
8089
  var webhooksPack = {
8093
8090
  key: "webhooks",
8094
- classify(gap2) {
8095
- const text = gapText(gap2);
8091
+ classify(gap) {
8092
+ const text = gapText(gap);
8096
8093
  return /webhook|signature|idempotency|retry|replay|dead-letter/.test(text);
8097
8094
  },
8098
- riskTags(gap2) {
8099
- const text = gapText(gap2);
8095
+ riskTags(gap) {
8096
+ const text = gapText(gap);
8100
8097
  return [
8101
8098
  text.includes("signature") ? "signature" : "",
8102
8099
  text.includes("idempotency") ? "idempotency" : "",
@@ -8107,10 +8104,10 @@ var webhooksPack = {
8107
8104
 
8108
8105
  // src/capabilities/index.ts
8109
8106
  var PACKS = [scalingPack, securityPack, webhooksPack, paymentsPack, databasePack];
8110
- function classifyGapCapability(gap2) {
8111
- const direct = PACKS.find((pack) => pack.classify(gap2));
8107
+ function classifyGapCapability(gap) {
8108
+ const direct = PACKS.find((pack) => pack.classify(gap));
8112
8109
  if (direct) return direct.key;
8113
- return capabilityFromArea(String(gap2.primaryMapCategory)) ?? "security";
8110
+ return capabilityFromArea(String(gap.primaryMapCategory)) ?? "security";
8114
8111
  }
8115
8112
  function summarizeCapabilities(gaps) {
8116
8113
  const result = Object.fromEntries(
@@ -8120,13 +8117,13 @@ function summarizeCapabilities(gaps) {
8120
8117
  ])
8121
8118
  );
8122
8119
  for (const pack of PACKS) {
8123
- const matching = gaps.filter((gap2) => classifyGapCapability(gap2) === pack.key);
8120
+ const matching = gaps.filter((gap) => classifyGapCapability(gap) === pack.key);
8124
8121
  result[pack.key] = {
8125
8122
  key: pack.key,
8126
8123
  status: statusForGaps(matching),
8127
- topGapIds: matching.slice(0, 5).map((gap2) => gap2.id),
8124
+ topGapIds: matching.slice(0, 5).map((gap) => gap.id),
8128
8125
  evidenceCount: matching.length,
8129
- riskTags: unique2(matching.flatMap((gap2) => pack.riskTags(gap2)))
8126
+ riskTags: unique2(matching.flatMap((gap) => pack.riskTags(gap)))
8130
8127
  };
8131
8128
  }
8132
8129
  return result;
@@ -8134,8 +8131,8 @@ function summarizeCapabilities(gaps) {
8134
8131
 
8135
8132
  // src/contracts/contextMap.ts
8136
8133
  function generateContextMap(artifact) {
8137
- const criticalCount = artifact.gaps.filter((gap2) => gap2.severity === "critical").length;
8138
- const warningCount = artifact.gaps.filter((gap2) => gap2.severity === "warning").length;
8134
+ const criticalCount = artifact.gaps.filter((gap) => gap.severity === "critical").length;
8135
+ const warningCount = artifact.gaps.filter((gap) => gap.severity === "warning").length;
8139
8136
  return {
8140
8137
  $schema: "https://viberaven.dev/schemas/context-map.schema.json",
8141
8138
  schemaVersion: "v1",
@@ -8168,12 +8165,12 @@ function generateContextMap(artifact) {
8168
8165
  warningCount,
8169
8166
  providerDashboardChecksRequired: true
8170
8167
  },
8171
- topGaps: artifact.gaps.slice(0, 10).map((gap2) => ({
8172
- id: gap2.id,
8173
- severity: gap2.severity,
8174
- title: gap2.title,
8175
- area: String(gap2.primaryMapCategory),
8176
- promptCommand: promptGapCommand(gap2.id)
8168
+ topGaps: artifact.gaps.slice(0, 10).map((gap) => ({
8169
+ id: gap.id,
8170
+ severity: gap.severity,
8171
+ title: gap.title,
8172
+ area: String(gap.primaryMapCategory),
8173
+ promptCommand: promptGapCommand(gap.id)
8177
8174
  }))
8178
8175
  };
8179
8176
  }
@@ -8188,10 +8185,10 @@ function runIdFrom(scannedAt) {
8188
8185
  return `vr_${scannedAt.replace(/\D/g, "").slice(0, 14) || "scan"}`;
8189
8186
  }
8190
8187
  function generateGateResult(artifact, options = {}) {
8191
- const criticalCount = artifact.gaps.filter((gap2) => gap2.severity === "critical").length;
8192
- const warningCount = artifact.gaps.filter((gap2) => gap2.severity === "warning").length;
8188
+ const criticalCount = artifact.gaps.filter((gap) => gap.severity === "critical").length;
8189
+ const warningCount = artifact.gaps.filter((gap) => gap.severity === "warning").length;
8193
8190
  const capabilities = summarizeCapabilities(artifact.gaps);
8194
- const topGapIds = artifact.gaps.slice(0, 10).map((gap2) => gap2.id);
8191
+ const topGapIds = artifact.gaps.slice(0, 10).map((gap) => gap.id);
8195
8192
  const firstGapId = topGapIds[0];
8196
8193
  return {
8197
8194
  $schema: "https://viberaven.dev/schemas/gate-result.schema.json",
@@ -8240,24 +8237,24 @@ function generateGateResult(artifact, options = {}) {
8240
8237
  }
8241
8238
 
8242
8239
  // src/contracts/gapEvidence.ts
8243
- function summarizeGap(gap2) {
8244
- return `${gap2.title}. See the tasklist and original scan for redacted detail.`;
8240
+ function summarizeGap(gap) {
8241
+ return `${gap.title}. See the tasklist and original scan for redacted detail.`;
8245
8242
  }
8246
8243
  function generateGapEvidenceFiles(artifact) {
8247
- return artifact.gaps.slice(0, 50).map((gap2) => ({
8248
- path: `.viberaven/gaps/${gap2.id}.json`,
8244
+ return artifact.gaps.slice(0, 50).map((gap) => ({
8245
+ path: `.viberaven/gaps/${gap.id}.json`,
8249
8246
  content: {
8250
8247
  $schema: "https://viberaven.dev/schemas/gap.schema.json",
8251
8248
  schemaVersion: "v1",
8252
- id: gap2.id,
8253
- severity: gap2.severity,
8254
- capability: classifyGapCapability(gap2),
8255
- title: gap2.title,
8256
- summary: summarizeGap(gap2),
8257
- evidence: [{ kind: String(gap2.primaryMapCategory), redacted: true }],
8249
+ id: gap.id,
8250
+ severity: gap.severity,
8251
+ capability: classifyGapCapability(gap),
8252
+ title: gap.title,
8253
+ summary: summarizeGap(gap),
8254
+ evidence: [{ kind: String(gap.primaryMapCategory), redacted: true }],
8258
8255
  commands: {
8259
- prompt: promptGapCommand(gap2.id),
8260
- healPlan: healPlanGapCommand(gap2.id),
8256
+ prompt: promptGapCommand(gap.id),
8257
+ healPlan: healPlanGapCommand(gap.id),
8261
8258
  verify: PUBLIC_VERIFY_COMMAND
8262
8259
  },
8263
8260
  providerBoundary: {
@@ -8306,6 +8303,39 @@ var PRODUCTION_MAP_CATEGORY_KEYS_ALL = PRODUCTION_MAP_LANES.map(
8306
8303
  (lane) => lane.extensionKey
8307
8304
  );
8308
8305
 
8306
+ // src/playbooks/checkMap.ts
8307
+ var CHECK_TO_PLAYBOOK = {
8308
+ "vercel-deployment": "vercel",
8309
+ "vercel-project": "vercel",
8310
+ "supabase-project": "supabase",
8311
+ "supabase-env": "supabase",
8312
+ "stripe-webhook": "stripe",
8313
+ "stripe-product": "stripe",
8314
+ "stripe-keys": "stripe"
8315
+ };
8316
+ function mapCheckToPlaybook(check2) {
8317
+ if (CHECK_TO_PLAYBOOK[check2.id]) {
8318
+ return CHECK_TO_PLAYBOOK[check2.id];
8319
+ }
8320
+ const providerKey = check2.providerKey.toLowerCase();
8321
+ if (providerKey.includes("vercel") || check2.area === "deployment") {
8322
+ return "vercel";
8323
+ }
8324
+ if (providerKey.includes("stripe") || check2.area === "payments") {
8325
+ return "stripe";
8326
+ }
8327
+ if (providerKey.includes("supabase") && check2.area === "auth") {
8328
+ return "auth-supabase";
8329
+ }
8330
+ if (providerKey.includes("supabase") || check2.area === "database") {
8331
+ return "supabase";
8332
+ }
8333
+ if (check2.area === "auth") {
8334
+ return "auth-supabase";
8335
+ }
8336
+ return "vercel";
8337
+ }
8338
+
8309
8339
  // src/playbooks/loadPlaybook.ts
8310
8340
  var import_node_fs5 = require("node:fs");
8311
8341
  var import_promises3 = require("node:fs/promises");
@@ -8416,8 +8446,8 @@ async function loadPlaybook(provider2) {
8416
8446
  `Unknown provider "${provider2}". Available: ${PLAYBOOK_PROVIDERS.join(", ")}`
8417
8447
  );
8418
8448
  }
8419
- const path3 = (0, import_node_path5.join)(playbooksRoot(), `${normalized}.json`);
8420
- const raw = JSON.parse(await (0, import_promises3.readFile)(path3, "utf-8"));
8449
+ const path = (0, import_node_path5.join)(playbooksRoot(), `${normalized}.json`);
8450
+ const raw = JSON.parse(await (0, import_promises3.readFile)(path, "utf-8"));
8421
8451
  const playbook = parsePlaybook(raw);
8422
8452
  if (playbook.provider !== normalized) {
8423
8453
  throw new Error(`Playbook file ${normalized}.json has mismatched provider field`);
@@ -8431,8 +8461,8 @@ function loadPlaybookSync(provider2) {
8431
8461
  `Unknown provider "${provider2}". Available: ${PLAYBOOK_PROVIDERS.join(", ")}`
8432
8462
  );
8433
8463
  }
8434
- const path3 = (0, import_node_path5.join)(playbooksRoot(), `${normalized}.json`);
8435
- const raw = JSON.parse((0, import_node_fs5.readFileSync)(path3, "utf-8"));
8464
+ const path = (0, import_node_path5.join)(playbooksRoot(), `${normalized}.json`);
8465
+ const raw = JSON.parse((0, import_node_fs5.readFileSync)(path, "utf-8"));
8436
8466
  const playbook = parsePlaybook(raw);
8437
8467
  if (playbook.provider !== normalized) {
8438
8468
  throw new Error(`Playbook file ${normalized}.json has mismatched provider field`);
@@ -8440,35 +8470,28 @@ function loadPlaybookSync(provider2) {
8440
8470
  return playbook;
8441
8471
  }
8442
8472
 
8443
- // src/providers/envKeys.ts
8444
- var PROVIDER_DEFAULT_KEYS = {
8445
- stripe: ["STRIPE_SECRET_KEY", "STRIPE_WEBHOOK_SECRET"],
8446
- supabase: [
8447
- "NEXT_PUBLIC_SUPABASE_URL",
8448
- "NEXT_PUBLIC_SUPABASE_ANON_KEY",
8449
- "SUPABASE_SERVICE_ROLE_KEY"
8450
- ],
8451
- vercel: []
8452
- };
8453
- var GAP_KEYS = {
8454
- stripe_webhook_signature_missing: ["STRIPE_WEBHOOK_SECRET"],
8455
- missing_prod_env_stripe_webhook_secret: ["STRIPE_WEBHOOK_SECRET"],
8456
- rls_disabled: ["SUPABASE_SERVICE_ROLE_KEY"]
8457
- };
8458
- function normalizeProvider2(provider2) {
8459
- const normalized = provider2.trim().toLowerCase().replace(/\s+/g, "-");
8460
- if (normalized === "auth-supabase" || normalized === "supabase-auth") {
8461
- return "supabase";
8462
- }
8463
- return normalized;
8473
+ // src/playbooks/manualChecks.ts
8474
+ function isManualProviderCheck(check2) {
8475
+ return check2.evidenceClass === "manual-dashboard" || check2.evidenceClass === "mcp-verifier" || check2.evidenceSource === "provider" || check2.evidenceSource === "mcp" || check2.status === "needs-connection" || check2.status === "unknown";
8464
8476
  }
8465
- function envKeysForGap(gapId, provider2) {
8466
- const gapKeys = GAP_KEYS[gapId];
8467
- if (gapKeys) {
8468
- return [...gapKeys];
8477
+ function collectManualChecks(artifact) {
8478
+ const refs = [];
8479
+ for (const area of artifact.missionGraph.areas ?? []) {
8480
+ for (const mission of area.providerMissions) {
8481
+ for (const check2 of mission.checks) {
8482
+ if (!isManualProviderCheck(check2)) {
8483
+ continue;
8484
+ }
8485
+ refs.push({
8486
+ areaLabel: area.label,
8487
+ providerLabel: mission.providerLabel,
8488
+ check: check2,
8489
+ mapCategory: area.key
8490
+ });
8491
+ }
8492
+ }
8469
8493
  }
8470
- const providerKeys = PROVIDER_DEFAULT_KEYS[normalizeProvider2(provider2)];
8471
- return providerKeys ? [...providerKeys] : [];
8494
+ return refs;
8472
8495
  }
8473
8496
 
8474
8497
  // src/tui/menu.ts
@@ -8493,7 +8516,7 @@ function needsScanMessage(startDir) {
8493
8516
  return [
8494
8517
  "No CLI scan found for this folder.",
8495
8518
  `Looking from: ${cwd}`,
8496
- 'Choose "Make this app production ready", or run the menu from your repo root (where .viberaven/ lives).',
8519
+ 'Choose "Scan project", or run the menu from your repo root (where .viberaven/ lives).',
8497
8520
  "VS Code extension scans stay inside the editor \u2014 run a CLI scan once to create .viberaven/ on disk."
8498
8521
  ].join("\n");
8499
8522
  }
@@ -8522,10 +8545,10 @@ function formatTopGapsList(artifact, limit = 10) {
8522
8545
  if (sorted.length === 0) {
8523
8546
  return "No gaps found \u2014 production core looks solid.";
8524
8547
  }
8525
- return sorted.slice(0, limit).map((gap2, index) => {
8526
- const severity = gap2.severity.toUpperCase().padEnd(8);
8527
- const area = gap2.primaryMapCategory.padEnd(12);
8528
- return `${index + 1}. [${severity}] ${area} ${gap2.title}`;
8548
+ return sorted.slice(0, limit).map((gap, index) => {
8549
+ const severity = gap.severity.toUpperCase().padEnd(8);
8550
+ const area = gap.primaryMapCategory.padEnd(12);
8551
+ return `${index + 1}. [${severity}] ${area} ${gap.title}`;
8529
8552
  }).join("\n");
8530
8553
  }
8531
8554
  async function loadLastArtifact(startDir) {
@@ -8533,504 +8556,64 @@ async function loadLastArtifact(startDir) {
8533
8556
  if (!workspace) {
8534
8557
  throw new ScanNotFoundError(needsScanMessage(startDir));
8535
8558
  }
8536
- const path3 = (0, import_node_path6.join)(getProjectArtifactsDir(workspace), "last-scan.json");
8559
+ const path = (0, import_node_path6.join)(getProjectArtifactsDir(workspace), "last-scan.json");
8537
8560
  try {
8538
- const raw = await (0, import_promises4.readFile)(path3, "utf-8");
8561
+ const raw = await (0, import_promises4.readFile)(path, "utf-8");
8539
8562
  return JSON.parse(raw);
8540
8563
  } catch {
8541
8564
  throw new ScanNotFoundError(needsScanMessage(startDir));
8542
8565
  }
8543
8566
  }
8544
8567
 
8545
- // src/buildTaskList.ts
8546
- var KNOWN_RECIPE_GAP_IDS = /* @__PURE__ */ new Set([
8547
- // emptyCatch (original recipe)
8548
- "empty-catch",
8549
- "empty_catch",
8550
- "emptyCatch",
8551
- // W3 env-add recipes
8552
- "auth_secret_missing",
8553
- "node_env_not_set",
8554
- "database_url_missing",
8555
- // W3 file-create recipes
8556
- "missing_error_boundary",
8557
- "missing_health_route",
8558
- "missing_loading_state",
8559
- "missing_404_page",
8560
- // W3 file-patch recipes
8561
- "missing_csp_header",
8562
- "missing_rate_limit",
8563
- "eslint_restricted_imports"
8564
- // NOTE: 'rls_disabled' is intentionally NOT here — it is provider-action only,
8565
- // canAutoApply=false, and should be classified as 'provider-action' by buildTaskList.
8566
- ]);
8567
- function hasRecipe(gapId) {
8568
- if (KNOWN_RECIPE_GAP_IDS.has(gapId)) return true;
8569
- const lower = gapId.toLowerCase();
8570
- return lower.includes("empty") && lower.includes("catch");
8571
- }
8572
- function gapToPlaybookProvider(gap2) {
8573
- const cat = gap2.primaryMapCategory.toLowerCase();
8574
- if (cat === "deployment") return "vercel";
8575
- if (cat === "payments") return "stripe";
8576
- if (cat === "auth") return "auth-supabase";
8577
- if (cat === "database") return "supabase";
8578
- const id = gap2.id.toLowerCase();
8579
- if (id.includes("vercel") || id.includes("deploy")) return "vercel";
8580
- if (id.includes("stripe") || id.includes("payment")) return "stripe";
8581
- if (id.includes("supabase") || id.includes("database") || id.includes("db")) return "supabase";
8582
- if (id.includes("auth")) return "auth-supabase";
8583
- return void 0;
8584
- }
8585
- function hasPlaybook(gap2) {
8586
- try {
8587
- const provider2 = gapToPlaybookProvider(gap2);
8588
- if (!provider2) return false;
8589
- return PLAYBOOK_PROVIDERS.includes(provider2);
8590
- } catch {
8591
- return false;
8592
- }
8593
- }
8594
- function buildProviderAction(gap2, provider2) {
8595
- try {
8596
- const playbook = loadPlaybookSync(provider2);
8597
- const step = playbook.steps[0];
8598
- if (!step) return void 0;
8599
- return {
8600
- provider: provider2,
8601
- dashboardUrl: step.openUrl ?? `https://${provider2}.com`,
8602
- // PlaybookStep uses `instruction` — map to exactStep
8603
- exactStep: step.instruction,
8604
- envKeyName: envKeysForGap(gap2.id, provider2)[0],
8605
- envKeyExample: void 0,
8606
- // Use step title as the done signal
8607
- doneSignal: `${step.title} step completed`,
8608
- verifyCommand: PUBLIC_VERIFY_COMMAND
8609
- };
8610
- } catch {
8611
- return void 0;
8612
- }
8613
- }
8568
+ // src/resolveNextAction.ts
8569
+ var UPGRADE_URL = "https://viberaven.dev/pricing";
8570
+ var LOCKED_LANE_LABELS = {
8571
+ deployment: "Deployment",
8572
+ monitoring: "Monitoring / Analytics",
8573
+ security: "Security",
8574
+ testing: "Testing",
8575
+ landing: "Onboarding",
8576
+ errorHandling: "Error handling / observability"
8577
+ };
8614
8578
  function unlockedKeys(artifact) {
8615
8579
  const keys = artifact.usage?.unlockedMapCategoryKeys ?? FREE_TRIAL_UNLOCKED_MAP_CATEGORY_KEYS;
8616
8580
  return new Set(keys);
8617
8581
  }
8618
- function buildTaskList(artifact) {
8582
+ function resolveNextAction(artifact) {
8619
8583
  const unlocked = unlockedKeys(artifact);
8620
- const sorted = sortGapsByPriority(artifact.gaps);
8621
- return sorted.map((gap2, index) => {
8622
- const id = `TASK-${String(index + 1).padStart(3, "0")}`;
8623
- const isLocked = !unlocked.has(gap2.primaryMapCategory);
8624
- let fixType;
8625
- if (isLocked) {
8626
- fixType = "upgrade-required";
8627
- } else if (hasRecipe(gap2.id)) {
8628
- fixType = "repo-code";
8629
- } else if (hasPlaybook(gap2)) {
8630
- fixType = "provider-action";
8631
- } else {
8632
- fixType = "manual-verify";
8633
- }
8634
- const base = {
8635
- id,
8636
- gapId: gap2.id,
8637
- severity: gap2.severity,
8638
- fixType,
8639
- title: gap2.title,
8640
- verifyCommand: PUBLIC_VERIFY_COMMAND,
8641
- requiresUserAction: fixType !== "repo-code"
8584
+ const repoGap = sortGapsByPriority(artifact.gaps).find(
8585
+ (gap) => unlocked.has(gap.primaryMapCategory)
8586
+ );
8587
+ if (repoGap) {
8588
+ return {
8589
+ type: "repo-fix",
8590
+ title: repoGap.title,
8591
+ detail: repoGap.detail,
8592
+ command: `viberaven prompt --gap ${repoGap.id}`
8642
8593
  };
8643
- if (fixType === "repo-code") {
8644
- base.mcpTool = "viberaven_heal_apply";
8645
- base.mcpArgs = { gap: gap2.id, yes: true };
8646
- base.requiresUserAction = false;
8647
- if (gap2.copyPrompt) {
8648
- base.exactFix = gap2.copyPrompt.trim();
8649
- }
8650
- } else if (fixType === "provider-action") {
8651
- try {
8652
- const provider2 = gapToPlaybookProvider(gap2);
8653
- if (provider2) {
8654
- const pa = buildProviderAction(gap2, provider2);
8655
- if (pa) {
8656
- base.providerAction = pa;
8657
- base.action = pa.exactStep;
8658
- }
8659
- }
8660
- } catch {
8661
- base.fixType = "manual-verify";
8662
- }
8663
- base.requiresUserAction = true;
8664
- }
8665
- return base;
8666
- });
8667
- }
8668
- function buildTaskListMarkdown(tasks) {
8669
- if (tasks.length === 0) {
8670
- return "# VibeRaven Agent Tasklist\n\n_No gaps found \u2014 production core looks solid._\n";
8671
8594
  }
8672
- const lines = ["# VibeRaven Agent Tasklist", ""];
8673
- for (const task of tasks) {
8674
- const severityLabel = task.severity.toUpperCase();
8675
- lines.push(`## ${task.id} \xB7 ${task.gapId} \xB7 ${severityLabel}`, "");
8676
- lines.push(`**Fix type:** ${task.fixType} `);
8677
- if (task.file) {
8678
- lines.push(`**File:** \`${task.file}\` `);
8679
- }
8680
- if (task.action) {
8681
- lines.push(`**Action:** ${task.action} `);
8682
- }
8683
- if (task.exactFix) {
8684
- lines.push(`**Exact fix:** ${task.exactFix} `);
8685
- } else {
8686
- lines.push(`**Exact fix:** No automated recipe \u2014 see scanner hint. `);
8687
- }
8688
- lines.push(`**Verify:** \`${task.verifyCommand}\` `);
8689
- if (task.mcpTool && task.mcpArgs) {
8690
- lines.push(
8691
- `**MCP:** \`${task.mcpTool} ${JSON.stringify(task.mcpArgs)}\` `
8692
- );
8693
- }
8694
- lines.push(`**Requires user action:** ${task.requiresUserAction}`);
8695
- if (task.providerAction) {
8696
- const pa = task.providerAction;
8697
- lines.push("");
8698
- lines.push("**Provider action:**");
8699
- lines.push(`- Provider: ${pa.provider}`);
8700
- lines.push(`- Dashboard: ${pa.dashboardUrl}`);
8701
- lines.push(`- Step: ${pa.exactStep}`);
8702
- if (pa.envKeyName) lines.push(`- Env key: \`${pa.envKeyName}\``);
8703
- if (pa.doneSignal) lines.push(`- Done when: ${pa.doneSignal}`);
8704
- if (pa.mcpAlternative) lines.push(`- MCP alternative: \`${pa.mcpAlternative}\``);
8595
+ const manual = collectManualChecks(artifact).find((item3) => unlocked.has(item3.mapCategory));
8596
+ if (manual) {
8597
+ const provider2 = mapCheckToPlaybook(manual.check);
8598
+ let openUrl;
8599
+ try {
8600
+ const playbook = loadPlaybookSync(provider2);
8601
+ openUrl = playbook.steps[0]?.openUrl;
8602
+ } catch {
8603
+ openUrl = void 0;
8705
8604
  }
8706
- lines.push("");
8707
- }
8708
- return `${lines.join("\n")}
8709
- `;
8710
- }
8711
-
8712
- // src/sanitizeArtifact.ts
8713
- var INLINE_SECRET_PATTERNS = [
8714
- /\b(sk_(?:live|test)_[A-Za-z0-9]{12,}|sk-proj-[A-Za-z0-9_-]{16,}|sk-[A-Za-z0-9_-]{20,})\b/g,
8715
- /\b(whsec_[A-Za-z0-9]{12,}|rk_(?:live|test)_[A-Za-z0-9]{12,})\b/g,
8716
- /\bghp_[A-Za-z0-9]{36,}\b/g,
8717
- /\bgithub_pat_[A-Za-z0-9_]{50,}\b/g,
8718
- /\bxox[bp]-[A-Za-z0-9-]{20,}\b/g,
8719
- /\bxapp-[A-Za-z0-9-]{20,}\b/g,
8720
- /\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\b/g,
8721
- /-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z ]*PRIVATE KEY-----/g
8722
- ];
8723
- var SENSITIVE_ENV_KEYS = /(?:^|_)(?:ACCESS_TOKEN|AUTHORIZATION|API_KEY|SECRET|SECRET_KEY|SERVICE_ROLE_KEY|TOKEN|PASSWORD|PRIVATE_KEY|CREDENTIALS?)(?:$|_)/i;
8724
- function redactString(value) {
8725
- let out = value;
8726
- for (const pattern of INLINE_SECRET_PATTERNS) {
8727
- out = out.replace(pattern, pattern.source.includes("PRIVATE KEY") ? "[REDACTED_PRIVATE_KEY]" : "[REDACTED_SECRET]");
8605
+ return {
8606
+ type: "provider-guide",
8607
+ title: manual.check.label,
8608
+ detail: manual.check.promptHint || `Configure ${manual.providerLabel} in the provider dashboard (${manual.areaLabel}).`,
8609
+ provider: provider2,
8610
+ playbookStep: 1,
8611
+ command: `viberaven guide ${provider2} --step 1`,
8612
+ openUrl
8613
+ };
8728
8614
  }
8729
- out = out.replace(/\bAuthorization\s*:\s*([A-Za-z][A-Za-z0-9._-]*)\s+[^\s;,]+/gi, "Authorization: $1 [REDACTED]");
8730
- return out.replace(
8731
- /\b([A-Za-z0-9_]*(?:ACCESS_TOKEN|AUTHORIZATION|API_KEY|SECRET|SECRET_KEY|SERVICE_ROLE_KEY|TOKEN|PASSWORD|PRIVATE_KEY|CREDENTIALS?)[A-Za-z0-9_]*)\s*=\s*["']?[^"'\s;,]+["']?/gi,
8732
- "$1=[REDACTED]"
8733
- );
8734
- }
8735
- function redactUnknown(value) {
8736
- if (typeof value === "string") {
8737
- return redactString(value);
8738
- }
8739
- if (Array.isArray(value)) {
8740
- return value.map(redactUnknown);
8741
- }
8742
- if (value && typeof value === "object") {
8743
- const record = value;
8744
- const next = {};
8745
- for (const [key, entry] of Object.entries(record)) {
8746
- if (SENSITIVE_ENV_KEYS.test(key)) {
8747
- next[key] = "[REDACTED]";
8748
- } else {
8749
- next[key] = redactUnknown(entry);
8750
- }
8751
- }
8752
- return next;
8753
- }
8754
- return value;
8755
- }
8756
- function sanitizeArtifactForDisk(artifact) {
8757
- return redactUnknown(artifact);
8758
- }
8759
-
8760
- // src/risk/riskMapping.ts
8761
- var OWASP = {
8762
- promptInjection: "LLM01:2025 Prompt Injection",
8763
- sensitiveDisclosure: "LLM06:2025 Sensitive Information Disclosure",
8764
- improperOutputHandling: "LLM05:2025 Improper Output Handling",
8765
- excessiveAgency: "LLM08:2025 Excessive Agency",
8766
- unboundedConsumption: "LLM10:2025 Unbounded Consumption"
8767
- };
8768
- var SAFECODE = {
8769
- dataProtection: "Data protection and least-data exposure",
8770
- callbackValidation: "Callback validation and message integrity",
8771
- leastPrivilege: "Least privilege and access control",
8772
- rateLimiting: "Rate limiting and quota enforcement",
8773
- outputValidation: "Output validation and encoding",
8774
- promptBoundary: "Prompt boundary validation"
8775
- };
8776
- function tokenize(text) {
8777
- return text.toLowerCase().split(/[^a-z0-9]+/).filter(Boolean);
8778
- }
8779
- function hasKeyword(tokens, keyword) {
8780
- const keywordTokens = tokenize(keyword);
8781
- if (keywordTokens.length === 0) {
8782
- return false;
8783
- }
8784
- if (keywordTokens.length === 1) {
8785
- return tokens.includes(keywordTokens[0]);
8786
- }
8787
- const textPhrase = ` ${tokens.join(" ")} `;
8788
- const keywordPhrase = keywordTokens.join(" ");
8789
- return textPhrase.includes(` ${keywordPhrase} `);
8790
- }
8791
- function hasAny(tokens, keywords) {
8792
- return keywords.some((keyword) => hasKeyword(tokens, keyword));
8793
- }
8794
- function addUnique(values, value) {
8795
- if (!values.includes(value)) {
8796
- values.push(value);
8797
- }
8798
- }
8799
- function mapGapToRisk(gap2) {
8800
- const haystack = `${gap2.id} ${gap2.title} ${gap2.primaryMapCategory}`.toLowerCase();
8801
- const tokens = tokenize(haystack);
8802
- const owaspLlm = [];
8803
- const safecode = [];
8804
- if (hasAny(tokens, ["prompt injection", "prompt_injection", "jailbreak"])) {
8805
- addUnique(owaspLlm, OWASP.promptInjection);
8806
- addUnique(safecode, SAFECODE.promptBoundary);
8807
- }
8808
- if (hasAny(tokens, [
8809
- "rls",
8810
- "row level security",
8811
- "data exposure",
8812
- "sensitive information",
8813
- "sensitive info",
8814
- "secret exposure"
8815
- ])) {
8816
- addUnique(owaspLlm, OWASP.sensitiveDisclosure);
8817
- addUnique(safecode, SAFECODE.dataProtection);
8818
- }
8819
- if (hasAny(tokens, ["webhook", "callback"]) && hasAny(tokens, ["signature", "signing", "integrity", "payment", "stripe"])) {
8820
- addUnique(owaspLlm, OWASP.improperOutputHandling);
8821
- addUnique(safecode, SAFECODE.callbackValidation);
8822
- }
8823
- if (hasAny(tokens, ["auth", "access", "permission", "role", "privilege"])) {
8824
- addUnique(owaspLlm, OWASP.excessiveAgency);
8825
- addUnique(safecode, SAFECODE.leastPrivilege);
8826
- }
8827
- if (hasAny(tokens, ["rate limit", "rate_limit", "quota", "usage limit", "throttle"])) {
8828
- addUnique(owaspLlm, OWASP.unboundedConsumption);
8829
- addUnique(safecode, SAFECODE.rateLimiting);
8830
- }
8831
- if (hasAny(tokens, ["output handling", "output validation", "sanitize", "escaping", "encoding"])) {
8832
- addUnique(owaspLlm, OWASP.improperOutputHandling);
8833
- addUnique(safecode, SAFECODE.outputValidation);
8834
- }
8835
- return { owaspLlm, safecode };
8836
- }
8837
-
8838
- // src/contracts/prp.ts
8839
- function decisionFromGateStatus(status) {
8840
- if (status === "clear") return "CLEAR";
8841
- if (status === "warning") return "WARNING";
8842
- return "BLOCKED";
8843
- }
8844
- function actionContractFor(task) {
8845
- if (task.fixType === "repo-code") {
8846
- return {
8847
- owner: "coding_agent",
8848
- actionClass: "local_repo_write",
8849
- command: healApplyGapCommand(task.gapId)
8850
- };
8851
- }
8852
- if (task.fixType === "provider-action") {
8853
- return {
8854
- owner: "human",
8855
- actionClass: "manual_provider_step",
8856
- command: promptGapCommand(task.gapId)
8857
- };
8858
- }
8859
- if (task.fixType === "upgrade-required") {
8860
- return {
8861
- owner: "human",
8862
- actionClass: "upgrade_required",
8863
- command: promptGapCommand(task.gapId)
8864
- };
8865
- }
8866
- if (task.fixType === "manual-verify") {
8867
- return {
8868
- owner: "human",
8869
- actionClass: "local_repo_read",
8870
- command: promptGapCommand(task.gapId)
8871
- };
8872
- }
8873
- return {
8874
- owner: "coding_agent",
8875
- actionClass: "local_repo_read",
8876
- command: promptGapCommand(task.gapId)
8877
- };
8878
- }
8879
- function nextActionFromTask(task) {
8880
- return {
8881
- id: task.id,
8882
- gapId: task.gapId,
8883
- severity: task.severity,
8884
- title: task.title,
8885
- fixType: task.fixType,
8886
- requiresUserAction: task.requiresUserAction,
8887
- verifyCommand: task.verifyCommand,
8888
- ...actionContractFor(task)
8889
- };
8890
- }
8891
- function generateProductionProtocol(artifact, options = {}) {
8892
- const safeArtifact = sanitizeArtifactForDisk(artifact);
8893
- const contextMap = generateContextMap(safeArtifact);
8894
- const tasks = buildTaskList(safeArtifact);
8895
- return {
8896
- $schema: "https://viberaven.dev/schemas/prp.schema.json",
8897
- schemaVersion: "v1",
8898
- generatedAt: safeArtifact.scannedAt,
8899
- mode: options.mode ?? "live",
8900
- decision: decisionFromGateStatus(contextMap.productionGate.status),
8901
- detectedStack: {
8902
- archetype: safeArtifact.archetype ?? null,
8903
- deployment: contextMap.stack.deployment,
8904
- database: contextMap.stack.database,
8905
- auth: contextMap.stack.auth,
8906
- payments: contextMap.stack.payments,
8907
- monitoring: contextMap.stack.monitoring
8908
- },
8909
- findings: safeArtifact.gaps.map((gap2) => ({
8910
- id: gap2.id,
8911
- severity: gap2.severity,
8912
- title: gap2.title,
8913
- area: String(gap2.primaryMapCategory),
8914
- riskMapping: mapGapToRisk(gap2)
8915
- })),
8916
- nextActions: tasks.map(nextActionFromTask),
8917
- connectedTools: options.connectedTools ?? {},
8918
- providerActionsRequired: tasks.some((task) => task.fixType === "provider-action"),
8919
- verifyCommand: PUBLIC_VERIFY_COMMAND,
8920
- agentInstructions: [
8921
- "Read .viberaven/agent-tasklist.md before making launch fixes.",
8922
- `Apply repo-code nextActions first, then run ${PUBLIC_VERIFY_COMMAND} once per batch.`,
8923
- "For human-owned actions, use the command to open the focused provider prompt and do not request secrets."
8924
- ]
8925
- };
8926
- }
8927
-
8928
- // src/playbooks/checkMap.ts
8929
- var CHECK_TO_PLAYBOOK = {
8930
- "vercel-deployment": "vercel",
8931
- "vercel-project": "vercel",
8932
- "supabase-project": "supabase",
8933
- "supabase-env": "supabase",
8934
- "stripe-webhook": "stripe",
8935
- "stripe-product": "stripe",
8936
- "stripe-keys": "stripe"
8937
- };
8938
- function mapCheckToPlaybook(check) {
8939
- if (CHECK_TO_PLAYBOOK[check.id]) {
8940
- return CHECK_TO_PLAYBOOK[check.id];
8941
- }
8942
- const providerKey = check.providerKey.toLowerCase();
8943
- if (providerKey.includes("vercel") || check.area === "deployment") {
8944
- return "vercel";
8945
- }
8946
- if (providerKey.includes("stripe") || check.area === "payments") {
8947
- return "stripe";
8948
- }
8949
- if (providerKey.includes("supabase") && check.area === "auth") {
8950
- return "auth-supabase";
8951
- }
8952
- if (providerKey.includes("supabase") || check.area === "database") {
8953
- return "supabase";
8954
- }
8955
- if (check.area === "auth") {
8956
- return "auth-supabase";
8957
- }
8958
- return "vercel";
8959
- }
8960
-
8961
- // src/playbooks/manualChecks.ts
8962
- function isManualProviderCheck(check) {
8963
- return check.evidenceClass === "manual-dashboard" || check.evidenceClass === "mcp-verifier" || check.evidenceSource === "provider" || check.evidenceSource === "mcp" || check.status === "needs-connection" || check.status === "unknown";
8964
- }
8965
- function collectManualChecks(artifact) {
8966
- const refs = [];
8967
- for (const area of artifact.missionGraph.areas ?? []) {
8968
- for (const mission of area.providerMissions) {
8969
- for (const check of mission.checks) {
8970
- if (!isManualProviderCheck(check)) {
8971
- continue;
8972
- }
8973
- refs.push({
8974
- areaLabel: area.label,
8975
- providerLabel: mission.providerLabel,
8976
- check,
8977
- mapCategory: area.key
8978
- });
8979
- }
8980
- }
8981
- }
8982
- return refs;
8983
- }
8984
-
8985
- // src/resolveNextAction.ts
8986
- var UPGRADE_URL = "https://viberaven.dev/pricing";
8987
- var LOCKED_LANE_LABELS = {
8988
- deployment: "Deployment",
8989
- monitoring: "Monitoring / Analytics",
8990
- security: "Security",
8991
- testing: "Testing",
8992
- landing: "Onboarding",
8993
- errorHandling: "Error handling / observability"
8994
- };
8995
- function unlockedKeys2(artifact) {
8996
- const keys = artifact.usage?.unlockedMapCategoryKeys ?? FREE_TRIAL_UNLOCKED_MAP_CATEGORY_KEYS;
8997
- return new Set(keys);
8998
- }
8999
- function resolveNextAction(artifact) {
9000
- const unlocked = unlockedKeys2(artifact);
9001
- const repoGap = sortGapsByPriority(artifact.gaps).find(
9002
- (gap2) => unlocked.has(gap2.primaryMapCategory)
9003
- );
9004
- if (repoGap) {
9005
- return {
9006
- type: "repo-fix",
9007
- title: repoGap.title,
9008
- detail: repoGap.detail,
9009
- command: `viberaven prompt --gap ${repoGap.id}`
9010
- };
9011
- }
9012
- const manual = collectManualChecks(artifact).find((item3) => unlocked.has(item3.mapCategory));
9013
- if (manual) {
9014
- const provider2 = mapCheckToPlaybook(manual.check);
9015
- let openUrl;
9016
- try {
9017
- const playbook = loadPlaybookSync(provider2);
9018
- openUrl = playbook.steps[0]?.openUrl;
9019
- } catch {
9020
- openUrl = void 0;
9021
- }
9022
- return {
9023
- type: "provider-guide",
9024
- title: manual.check.label,
9025
- detail: manual.check.promptHint || `Configure ${manual.providerLabel} in the provider dashboard (${manual.areaLabel}).`,
9026
- provider: provider2,
9027
- playbookStep: 1,
9028
- command: `viberaven guide ${provider2} --step 1`,
9029
- openUrl
9030
- };
9031
- }
9032
- const lockedGap = sortGapsByPriority(artifact.gaps).find(
9033
- (gap2) => !unlocked.has(gap2.primaryMapCategory)
8615
+ const lockedGap = sortGapsByPriority(artifact.gaps).find(
8616
+ (gap) => !unlocked.has(gap.primaryMapCategory)
9034
8617
  );
9035
8618
  if (lockedGap) {
9036
8619
  const lane = lockedGap.primaryMapCategory;
@@ -9154,22 +8737,22 @@ function generateAgentSummary(artifact) {
9154
8737
  if (topGaps.length === 0) {
9155
8738
  lines.push("_No model gaps returned. Review mission map checks before changing code._");
9156
8739
  } else {
9157
- topGaps.forEach((gap2, index) => {
8740
+ topGaps.forEach((gap, index) => {
9158
8741
  lines.push(
9159
- `${index + 1}. **${gap2.title}** (\`${gap2.id}\`, ${gap2.severity}, map: \`${gap2.primaryMapCategory}\`)`
8742
+ `${index + 1}. **${gap.title}** (\`${gap.id}\`, ${gap.severity}, map: \`${gap.primaryMapCategory}\`)`
9160
8743
  );
9161
- lines.push(` - ${gap2.detail}`);
9162
- lines.push(` - Command: \`viberaven prompt --gap ${gap2.id}\``);
9163
- if (gap2.copyPrompt) {
9164
- lines.push(` - Prompt: ${gap2.copyPrompt}`);
8744
+ lines.push(` - ${gap.detail}`);
8745
+ lines.push(` - Command: \`viberaven prompt --gap ${gap.id}\``);
8746
+ if (gap.copyPrompt) {
8747
+ lines.push(` - Prompt: ${gap.copyPrompt}`);
9165
8748
  }
9166
8749
  });
9167
8750
  }
9168
8751
  const manualChecks = (artifact.missionGraph.areas ?? []).flatMap(
9169
8752
  (area) => area.providerMissions.flatMap(
9170
8753
  (mission) => mission.checks.filter(
9171
- (check) => check.evidenceClass === "manual-dashboard" || check.evidenceClass === "mcp-verifier" || check.evidenceSource === "provider" || check.evidenceSource === "mcp" || check.status === "needs-connection" || check.status === "unknown"
9172
- ).map((check) => ({ area: area.label, provider: mission.providerLabel, check }))
8754
+ (check2) => check2.evidenceClass === "manual-dashboard" || check2.evidenceClass === "mcp-verifier" || check2.evidenceSource === "provider" || check2.evidenceSource === "mcp" || check2.status === "needs-connection" || check2.status === "unknown"
8755
+ ).map((check2) => ({ area: area.label, provider: mission.providerLabel, check: check2 }))
9173
8756
  )
9174
8757
  );
9175
8758
  lines.push("");
@@ -9206,6 +8789,174 @@ function generateAgentSummary(artifact) {
9206
8789
  `;
9207
8790
  }
9208
8791
 
8792
+ // src/buildTaskList.ts
8793
+ var KNOWN_RECIPE_GAP_IDS = /* @__PURE__ */ new Set([
8794
+ // emptyCatch (original recipe)
8795
+ "empty-catch",
8796
+ "empty_catch",
8797
+ "emptyCatch",
8798
+ // W3 env-add recipes
8799
+ "auth_secret_missing",
8800
+ "node_env_not_set",
8801
+ "database_url_missing",
8802
+ // W3 file-create recipes
8803
+ "missing_error_boundary",
8804
+ "missing_health_route",
8805
+ "missing_loading_state",
8806
+ "missing_404_page",
8807
+ // W3 file-patch recipes
8808
+ "missing_csp_header",
8809
+ "missing_rate_limit",
8810
+ "eslint_restricted_imports"
8811
+ // NOTE: 'rls_disabled' is intentionally NOT here — it is provider-action only,
8812
+ // canAutoApply=false, and should be classified as 'provider-action' by buildTaskList.
8813
+ ]);
8814
+ function hasRecipe(gapId) {
8815
+ if (KNOWN_RECIPE_GAP_IDS.has(gapId)) return true;
8816
+ const lower = gapId.toLowerCase();
8817
+ return lower.includes("empty") && lower.includes("catch");
8818
+ }
8819
+ function gapToPlaybookProvider(gap) {
8820
+ const cat = gap.primaryMapCategory.toLowerCase();
8821
+ if (cat === "deployment") return "vercel";
8822
+ if (cat === "payments") return "stripe";
8823
+ if (cat === "auth") return "auth-supabase";
8824
+ if (cat === "database") return "supabase";
8825
+ const id = gap.id.toLowerCase();
8826
+ if (id.includes("vercel") || id.includes("deploy")) return "vercel";
8827
+ if (id.includes("stripe") || id.includes("payment")) return "stripe";
8828
+ if (id.includes("supabase") || id.includes("database") || id.includes("db")) return "supabase";
8829
+ if (id.includes("auth")) return "auth-supabase";
8830
+ return void 0;
8831
+ }
8832
+ function hasPlaybook(gap) {
8833
+ try {
8834
+ const provider2 = gapToPlaybookProvider(gap);
8835
+ if (!provider2) return false;
8836
+ return PLAYBOOK_PROVIDERS.includes(provider2);
8837
+ } catch {
8838
+ return false;
8839
+ }
8840
+ }
8841
+ function buildProviderAction(gap, provider2) {
8842
+ try {
8843
+ const playbook = loadPlaybookSync(provider2);
8844
+ const step = playbook.steps[0];
8845
+ if (!step) return void 0;
8846
+ return {
8847
+ provider: provider2,
8848
+ dashboardUrl: step.openUrl ?? `https://${provider2}.com`,
8849
+ // PlaybookStep uses `instruction` — map to exactStep
8850
+ exactStep: step.instruction,
8851
+ // No envKeyName/envKeyExample in current PlaybookStep schema — leave undefined
8852
+ envKeyName: void 0,
8853
+ envKeyExample: void 0,
8854
+ // Use step title as the done signal
8855
+ doneSignal: `${step.title} step completed`,
8856
+ verifyCommand: PUBLIC_VERIFY_COMMAND
8857
+ };
8858
+ } catch {
8859
+ return void 0;
8860
+ }
8861
+ }
8862
+ function unlockedKeys2(artifact) {
8863
+ const keys = artifact.usage?.unlockedMapCategoryKeys ?? FREE_TRIAL_UNLOCKED_MAP_CATEGORY_KEYS;
8864
+ return new Set(keys);
8865
+ }
8866
+ function buildTaskList(artifact) {
8867
+ const unlocked = unlockedKeys2(artifact);
8868
+ const sorted = sortGapsByPriority(artifact.gaps);
8869
+ return sorted.map((gap, index) => {
8870
+ const id = `TASK-${String(index + 1).padStart(3, "0")}`;
8871
+ const isLocked = !unlocked.has(gap.primaryMapCategory);
8872
+ let fixType;
8873
+ if (isLocked) {
8874
+ fixType = "upgrade-required";
8875
+ } else if (hasRecipe(gap.id)) {
8876
+ fixType = "repo-code";
8877
+ } else if (hasPlaybook(gap)) {
8878
+ fixType = "provider-action";
8879
+ } else {
8880
+ fixType = "manual-verify";
8881
+ }
8882
+ const base = {
8883
+ id,
8884
+ gapId: gap.id,
8885
+ severity: gap.severity,
8886
+ fixType,
8887
+ title: gap.title,
8888
+ verifyCommand: PUBLIC_VERIFY_COMMAND,
8889
+ requiresUserAction: fixType !== "repo-code"
8890
+ };
8891
+ if (fixType === "repo-code") {
8892
+ base.mcpTool = "viberaven_heal_apply";
8893
+ base.mcpArgs = { gap: gap.id, yes: true };
8894
+ base.requiresUserAction = false;
8895
+ if (gap.copyPrompt) {
8896
+ base.exactFix = gap.copyPrompt.trim();
8897
+ }
8898
+ } else if (fixType === "provider-action") {
8899
+ try {
8900
+ const provider2 = gapToPlaybookProvider(gap);
8901
+ if (provider2) {
8902
+ const pa = buildProviderAction(gap, provider2);
8903
+ if (pa) {
8904
+ base.providerAction = pa;
8905
+ base.action = pa.exactStep;
8906
+ }
8907
+ }
8908
+ } catch {
8909
+ base.fixType = "manual-verify";
8910
+ }
8911
+ base.requiresUserAction = true;
8912
+ }
8913
+ return base;
8914
+ });
8915
+ }
8916
+ function buildTaskListMarkdown(tasks) {
8917
+ if (tasks.length === 0) {
8918
+ return "# VibeRaven Agent Tasklist\n\n_No gaps found \u2014 production core looks solid._\n";
8919
+ }
8920
+ const lines = ["# VibeRaven Agent Tasklist", ""];
8921
+ for (const task of tasks) {
8922
+ const severityLabel = task.severity.toUpperCase();
8923
+ lines.push(`## ${task.id} \xB7 ${task.gapId} \xB7 ${severityLabel}`, "");
8924
+ lines.push(`**Fix type:** ${task.fixType} `);
8925
+ if (task.file) {
8926
+ lines.push(`**File:** \`${task.file}\` `);
8927
+ }
8928
+ if (task.action) {
8929
+ lines.push(`**Action:** ${task.action} `);
8930
+ }
8931
+ if (task.exactFix) {
8932
+ lines.push(`**Exact fix:** ${task.exactFix} `);
8933
+ } else {
8934
+ lines.push(`**Exact fix:** No automated recipe \u2014 see scanner hint. `);
8935
+ }
8936
+ lines.push(`**Verify:** \`${task.verifyCommand}\` `);
8937
+ if (task.mcpTool && task.mcpArgs) {
8938
+ lines.push(
8939
+ `**MCP:** \`${task.mcpTool} ${JSON.stringify(task.mcpArgs)}\` `
8940
+ );
8941
+ }
8942
+ lines.push(`**Requires user action:** ${task.requiresUserAction}`);
8943
+ if (task.providerAction) {
8944
+ const pa = task.providerAction;
8945
+ lines.push("");
8946
+ lines.push("**Provider action:**");
8947
+ lines.push(`- Provider: ${pa.provider}`);
8948
+ lines.push(`- Dashboard: ${pa.dashboardUrl}`);
8949
+ lines.push(`- Step: ${pa.exactStep}`);
8950
+ if (pa.envKeyName) lines.push(`- Env key: \`${pa.envKeyName}\``);
8951
+ if (pa.doneSignal) lines.push(`- Done when: ${pa.doneSignal}`);
8952
+ if (pa.mcpAlternative) lines.push(`- MCP alternative: \`${pa.mcpAlternative}\``);
8953
+ }
8954
+ lines.push("");
8955
+ }
8956
+ return `${lines.join("\n")}
8957
+ `;
8958
+ }
8959
+
9209
8960
  // src/report/launchPlaybook.ts
9210
8961
  var UPGRADE_URL3 = "https://viberaven.dev/pricing";
9211
8962
  function unlockedSet(artifact) {
@@ -9219,13 +8970,13 @@ function generateLaunchPlaybook(artifact) {
9219
8970
  lines.push(`Generated from scan at ${artifact.scannedAt}. Work top to bottom.`, "");
9220
8971
  lines.push("## Repo fixes (agent code)", "");
9221
8972
  const repoGaps = sortGapsByPriority(artifact.gaps).filter(
9222
- (gap2) => unlocked.has(gap2.primaryMapCategory)
8973
+ (gap) => unlocked.has(gap.primaryMapCategory)
9223
8974
  );
9224
8975
  if (repoGaps.length === 0) {
9225
8976
  lines.push("_No repo-code gaps in unlocked lanes._", "");
9226
8977
  } else {
9227
- for (const gap2 of repoGaps) {
9228
- lines.push(`- [ ] ${gap2.title} \u2014 \`viberaven prompt --gap ${gap2.id}\``);
8978
+ for (const gap of repoGaps) {
8979
+ lines.push(`- [ ] ${gap.title} \u2014 \`viberaven prompt --gap ${gap.id}\``);
9229
8980
  }
9230
8981
  lines.push("");
9231
8982
  }
@@ -9245,14 +8996,14 @@ function generateLaunchPlaybook(artifact) {
9245
8996
  if (!isPro) {
9246
8997
  lines.push("## [Pro] Deployment, monitoring, and full map", "");
9247
8998
  const proGaps = sortGapsByPriority(artifact.gaps).filter(
9248
- (gap2) => !unlocked.has(gap2.primaryMapCategory)
8999
+ (gap) => !unlocked.has(gap.primaryMapCategory)
9249
9000
  );
9250
9001
  const proManuals = collectManualChecks(artifact).filter((item3) => !unlocked.has(item3.mapCategory));
9251
9002
  if (proGaps.length === 0 && proManuals.length === 0) {
9252
9003
  lines.push("_No Pro-only blockers detected._", "");
9253
9004
  } else {
9254
- for (const gap2 of proGaps.slice(0, 6)) {
9255
- lines.push(`- [ ] ${gap2.title} [Pro] \u2014 upgrade at ${UPGRADE_URL3}`);
9005
+ for (const gap of proGaps.slice(0, 6)) {
9006
+ lines.push(`- [ ] ${gap.title} [Pro] \u2014 upgrade at ${UPGRADE_URL3}`);
9256
9007
  }
9257
9008
  for (const item3 of proManuals.slice(0, 6)) {
9258
9009
  lines.push(`- [ ] ${item3.check.label} [Pro] \u2014 upgrade at ${UPGRADE_URL3}`);
@@ -9377,21 +9128,6 @@ function getStationHtml(nonce, cssUri, jsUri, options) {
9377
9128
  </div>
9378
9129
  </header>
9379
9130
 
9380
- <section class="studio-launch-hero" aria-label="VibeRaven readiness">
9381
- <div class="studio-launch-hero__main">
9382
- <p class="studio-launch-hero__wordmark">VibeRaven</p>
9383
- <div class="studio-launch-hero__decision" id="studio-launch-decision">\u25C7 BLOCKED</div>
9384
- <div class="studio-launch-hero__gauge" aria-hidden="true">
9385
- <span id="studio-launch-gauge-fill" class="studio-launch-hero__gauge-fill" style="width:25%"></span>
9386
- </div>
9387
- </div>
9388
- <div class="studio-launch-hero__next">
9389
- <span class="studio-launch-hero__next-label">Next action</span>
9390
- <strong id="studio-launch-next-action">Run a scan to map production gaps.</strong>
9391
- <button type="button" class="studio-launch-hero__verify" data-station-action="rescan">Run verify</button>
9392
- </div>
9393
- </section>
9394
-
9395
9131
  <div class="studio-workspace">
9396
9132
  <nav class="studio-nav-rail" aria-label="Studio areas">
9397
9133
  <span class="studio-nav-rail__item studio-nav-rail__item--active" aria-label="Architecture">ARCH</span>
@@ -9623,6 +9359,54 @@ var REPORT_ASSET_FILES = [
9623
9359
  "assets/provider-logrocket.svg"
9624
9360
  ];
9625
9361
 
9362
+ // src/sanitizeArtifact.ts
9363
+ var INLINE_SECRET_PATTERNS = [
9364
+ /\b(sk_(?:live|test)_[A-Za-z0-9]{12,}|sk-proj-[A-Za-z0-9_-]{16,}|sk-[A-Za-z0-9_-]{20,})\b/g,
9365
+ /\b(whsec_[A-Za-z0-9]{12,}|rk_(?:live|test)_[A-Za-z0-9]{12,})\b/g,
9366
+ /\bghp_[A-Za-z0-9]{36,}\b/g,
9367
+ /\bgithub_pat_[A-Za-z0-9_]{50,}\b/g,
9368
+ /\bxox[bp]-[A-Za-z0-9-]{20,}\b/g,
9369
+ /\bxapp-[A-Za-z0-9-]{20,}\b/g,
9370
+ /\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\b/g,
9371
+ /-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z ]*PRIVATE KEY-----/g
9372
+ ];
9373
+ var SENSITIVE_ENV_KEYS = /(?:^|_)(?:ACCESS_TOKEN|AUTHORIZATION|API_KEY|SECRET|SECRET_KEY|SERVICE_ROLE_KEY|TOKEN|PASSWORD|PRIVATE_KEY|CREDENTIALS?)(?:$|_)/i;
9374
+ function redactString(value) {
9375
+ let out = value;
9376
+ for (const pattern of INLINE_SECRET_PATTERNS) {
9377
+ out = out.replace(pattern, pattern.source.includes("PRIVATE KEY") ? "[REDACTED_PRIVATE_KEY]" : "[REDACTED_SECRET]");
9378
+ }
9379
+ out = out.replace(/\bAuthorization\s*:\s*([A-Za-z][A-Za-z0-9._-]*)\s+[^\s;,]+/gi, "Authorization: $1 [REDACTED]");
9380
+ return out.replace(
9381
+ /\b([A-Za-z0-9_]*(?:ACCESS_TOKEN|AUTHORIZATION|API_KEY|SECRET|SECRET_KEY|SERVICE_ROLE_KEY|TOKEN|PASSWORD|PRIVATE_KEY|CREDENTIALS?)[A-Za-z0-9_]*)\s*=\s*["']?[^"'\s;,]+["']?/gi,
9382
+ "$1=[REDACTED]"
9383
+ );
9384
+ }
9385
+ function redactUnknown(value) {
9386
+ if (typeof value === "string") {
9387
+ return redactString(value);
9388
+ }
9389
+ if (Array.isArray(value)) {
9390
+ return value.map(redactUnknown);
9391
+ }
9392
+ if (value && typeof value === "object") {
9393
+ const record = value;
9394
+ const next = {};
9395
+ for (const [key, entry] of Object.entries(record)) {
9396
+ if (SENSITIVE_ENV_KEYS.test(key)) {
9397
+ next[key] = "[REDACTED]";
9398
+ } else {
9399
+ next[key] = redactUnknown(entry);
9400
+ }
9401
+ }
9402
+ return next;
9403
+ }
9404
+ return value;
9405
+ }
9406
+ function sanitizeArtifactForDisk(artifact) {
9407
+ return redactUnknown(artifact);
9408
+ }
9409
+
9626
9410
  // src/artifacts.ts
9627
9411
  async function copyReportAssets(reportAssetsDir) {
9628
9412
  const sourceDir = getBundledReportAssetsDir();
@@ -9638,7 +9422,6 @@ async function writeScanArtifacts(options) {
9638
9422
  const jsonPath = (0, import_node_path8.join)(dir, "last-scan.json");
9639
9423
  const gateResultPath = (0, import_node_path8.join)(dir, "gate-result.json");
9640
9424
  const contextMapPath = (0, import_node_path8.join)(dir, "context-map.json");
9641
- const prpPath = (0, import_node_path8.join)(dir, "prp.json");
9642
9425
  const gapsDir = (0, import_node_path8.join)(dir, "gaps");
9643
9426
  const tasklistPath = (0, import_node_path8.join)(dir, "agent-tasklist.md");
9644
9427
  const summaryPath = (0, import_node_path8.join)(dir, "agent-summary.md");
@@ -9657,20 +9440,13 @@ async function writeScanArtifacts(options) {
9657
9440
  const gateResult = `${JSON.stringify(generateGateResult(safe), null, 2)}
9658
9441
  `;
9659
9442
  const contextMap = `${JSON.stringify(generateContextMap(safe), null, 2)}
9660
- `;
9661
- const prp = `${JSON.stringify(
9662
- generateProductionProtocol(safe, { connectedTools: options.connectedTools, mode: options.prpMode }),
9663
- null,
9664
- 2
9665
- )}
9666
9443
  `;
9667
9444
  const gapEvidenceFiles = generateGapEvidenceFiles(safe);
9668
9445
  await copyReportAssets(reportAssetsDir);
9669
9446
  await (0, import_promises5.writeFile)(gateResultPath, gateResult, "utf-8");
9670
9447
  await (0, import_promises5.writeFile)(contextMapPath, contextMap, "utf-8");
9671
- await (0, import_promises5.writeFile)(prpPath, prp, "utf-8");
9672
- for (const gap2 of gapEvidenceFiles) {
9673
- await (0, import_promises5.writeFile)((0, import_node_path8.join)(dir, "gaps", `${gap2.content.id}.json`), `${JSON.stringify(gap2.content, null, 2)}
9448
+ for (const gap of gapEvidenceFiles) {
9449
+ await (0, import_promises5.writeFile)((0, import_node_path8.join)(dir, "gaps", `${gap.content.id}.json`), `${JSON.stringify(gap.content, null, 2)}
9674
9450
  `, "utf-8");
9675
9451
  }
9676
9452
  await (0, import_promises5.writeFile)(tasklistPath, tasklist, "utf-8");
@@ -9683,7 +9459,6 @@ async function writeScanArtifacts(options) {
9683
9459
  jsonPath,
9684
9460
  gateResultPath,
9685
9461
  contextMapPath,
9686
- prpPath,
9687
9462
  gapsDir,
9688
9463
  tasklistPath,
9689
9464
  summaryPath,
@@ -9766,12 +9541,12 @@ async function copyToClipboard(text) {
9766
9541
  }
9767
9542
  }
9768
9543
  function pipeToCommand(command, text, extraArgs = []) {
9769
- return new Promise((resolve6, reject) => {
9544
+ return new Promise((resolve5, reject) => {
9770
9545
  const child = (0, import_node_child_process2.spawn)(command, extraArgs, { stdio: ["pipe", "ignore", "ignore"] });
9771
9546
  child.on("error", reject);
9772
9547
  child.on("close", (code) => {
9773
9548
  if (code === 0) {
9774
- resolve6();
9549
+ resolve5();
9775
9550
  } else {
9776
9551
  reject(new Error(`${command} exited with code ${code ?? "unknown"}`));
9777
9552
  }
@@ -9798,9 +9573,9 @@ function missionMatchesProvider(mission, provider2) {
9798
9573
  return normalizeProviderToken2(mission.provider || mission.providerLabel || mission.key) === current || normalizeProviderToken2(mission.providerLabel || mission.provider || mission.key) === current;
9799
9574
  }
9800
9575
  function missionEvidenceScore(mission) {
9801
- const repoVerified = mission.checks.filter((check) => check.evidenceClass === "repo-verified" || check.status === "passed").length;
9576
+ const repoVerified = mission.checks.filter((check2) => check2.evidenceClass === "repo-verified" || check2.status === "passed").length;
9802
9577
  const missing = mission.checks.filter(
9803
- (check) => check.evidenceClass === "missing-repo-fix" || check.status === "missing" || check.status === "failed"
9578
+ (check2) => check2.evidenceClass === "missing-repo-fix" || check2.status === "missing" || check2.status === "failed"
9804
9579
  ).length;
9805
9580
  return repoVerified * 100 + (mission.readinessPercent ?? 0) - missing;
9806
9581
  }
@@ -9820,7 +9595,7 @@ function openChecksForMission(mission) {
9820
9595
  return 0;
9821
9596
  }
9822
9597
  return mission.checks.filter(
9823
- (check) => check.status === "missing" || check.status === "failed" || check.status === "needs-connection"
9598
+ (check2) => check2.status === "missing" || check2.status === "failed" || check2.status === "needs-connection"
9824
9599
  ).length;
9825
9600
  }
9826
9601
 
@@ -9852,7 +9627,7 @@ function manualActionCheckCount(artifact) {
9852
9627
  return (artifact.missionGraph.areas ?? []).reduce((areaTotal, area) => {
9853
9628
  return areaTotal + area.providerMissions.reduce((missionTotal, mission) => {
9854
9629
  return missionTotal + mission.checks.filter(
9855
- (check) => check.evidenceClass === "manual-dashboard" || check.evidenceClass === "mcp-verifier" || check.evidenceSource === "provider" || check.evidenceSource === "mcp" || check.status === "needs-connection" || check.status === "unknown"
9630
+ (check2) => check2.evidenceClass === "manual-dashboard" || check2.evidenceClass === "mcp-verifier" || check2.evidenceSource === "provider" || check2.evidenceSource === "mcp" || check2.status === "needs-connection" || check2.status === "unknown"
9856
9631
  ).length;
9857
9632
  }, 0);
9858
9633
  }, 0);
@@ -9879,10 +9654,10 @@ function printScanSummary(artifact, paths) {
9879
9654
  const modelGaps = artifact.gaps.filter((g2) => g2.primaryMapCategory === area.key).length;
9880
9655
  const tag = modelGaps > 0 ? ` GAP ${modelGaps}` : open > 0 ? ` ${open} fix` : "";
9881
9656
  const tagColored = tag ? gapTagColor(modelGaps, open)(tag) : "";
9882
- const label2 = area.label.padEnd(18);
9657
+ const label = area.label.padEnd(18);
9883
9658
  const provider2 = mission.providerLabel.padEnd(14);
9884
9659
  const readiness = readinessColor(mission.readinessPercent)(`${mission.readinessPercent}%`);
9885
- console.log(` ${import_picocolors.default.dim(label2)} ${provider2} ${readiness}${tagColored}`);
9660
+ console.log(` ${import_picocolors.default.dim(label)} ${provider2} ${readiness}${tagColored}`);
9886
9661
  }
9887
9662
  console.log("");
9888
9663
  console.log(import_picocolors.default.bold("Artifacts:"));
@@ -9904,7 +9679,7 @@ function printScanSummary(artifact, paths) {
9904
9679
  if (next.upgradeUrl) {
9905
9680
  console.log(`Upgrade: ${next.upgradeUrl}`);
9906
9681
  }
9907
- console.log(formatAgentStatus(AGENT_ACTION, "Read .viberaven/agent-tasklist.md, run the next command above, then rescan."));
9682
+ console.log(formatAgentStatus(AGENT_ACTION, "Keep operating: read .viberaven/agent-tasklist.md, apply the next safe repo fix, then continue the VibeRaven loop."));
9908
9683
  console.log("");
9909
9684
  if (artifact.usage) {
9910
9685
  const lanes = artifact.usage.unlockedMapCategoryKeys.length;
@@ -10009,8 +9784,8 @@ function validateSafeFixJobInput(kind, rawInput) {
10009
9784
  }
10010
9785
  };
10011
9786
  }
10012
- function validateSafeFixRelativePath(path3) {
10013
- const trimmed = path3.trim();
9787
+ function validateSafeFixRelativePath(path) {
9788
+ const trimmed = path.trim();
10014
9789
  if (!trimmed) {
10015
9790
  return { ok: false, reason: "Safe fix path must not be empty." };
10016
9791
  }
@@ -10052,11 +9827,11 @@ var SECRET_VALUE_PATTERNS = [
10052
9827
  /\bwhsec_[A-Za-z0-9]{12,}\b/,
10053
9828
  /\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\b/
10054
9829
  ];
10055
- function isAllowedSafeFixPath(path3) {
10056
- if (path3 === ".env.example") {
9830
+ function isAllowedSafeFixPath(path) {
9831
+ if (path === ".env.example") {
10057
9832
  return true;
10058
9833
  }
10059
- const segments = path3.split("/");
9834
+ const segments = path.split("/");
10060
9835
  if (segments.length !== 2) {
10061
9836
  return false;
10062
9837
  }
@@ -10167,10 +9942,10 @@ function sleepForRunnerPoll(ms, signal) {
10167
9942
  if (signal?.aborted) {
10168
9943
  return Promise.reject(createRunnerWatchAbortError());
10169
9944
  }
10170
- return new Promise((resolve6, reject) => {
9945
+ return new Promise((resolve5, reject) => {
10171
9946
  const timer = setTimeout(() => {
10172
9947
  cleanup();
10173
- resolve6();
9948
+ resolve5();
10174
9949
  }, ms);
10175
9950
  const onAbort = () => {
10176
9951
  cleanup();
@@ -10362,8 +10137,8 @@ async function resolveSafeFixTarget(workspaceRoot, relativePath) {
10362
10137
  }
10363
10138
  return { ok: true, path: target };
10364
10139
  }
10365
- async function realpathNearestExisting(path3, root) {
10366
- let candidate = path3;
10140
+ async function realpathNearestExisting(path, root) {
10141
+ let candidate = path;
10367
10142
  while (isInsideRoot(root, candidate)) {
10368
10143
  try {
10369
10144
  await (0, import_promises6.lstat)(candidate);
@@ -10494,8 +10269,8 @@ function buildStationScanProof(kind, artifact) {
10494
10269
  `gaps: ${artifact.gaps.length}`,
10495
10270
  `summary: ${artifact.summary || "No summary returned."}`
10496
10271
  ];
10497
- for (const gap2 of artifact.gaps.slice(0, 5)) {
10498
- evidence.push(`gap: ${gap2.title} (${gap2.severity})`);
10272
+ for (const gap of artifact.gaps.slice(0, 5)) {
10273
+ evidence.push(`gap: ${gap.title} (${gap.severity})`);
10499
10274
  }
10500
10275
  for (const area of (artifact.missionGraph.areas ?? []).slice(0, 6)) {
10501
10276
  for (const mission of area.providerMissions.slice(0, 1)) {
@@ -10561,13 +10336,13 @@ function summarizeSafeNoopJob(job) {
10561
10336
  proofItems: [proofItemForJob(job, "runner_summary", "Runner summary", summary, [])]
10562
10337
  };
10563
10338
  }
10564
- function proofItemForJob(job, kind, label2, summary, evidence, redactionSecrets = []) {
10339
+ function proofItemForJob(job, kind, label, summary, evidence, redactionSecrets = []) {
10565
10340
  return {
10566
10341
  deploySessionId: job.deploySessionId,
10567
10342
  runnerSessionId: job.runnerSessionId,
10568
10343
  jobId: job.id,
10569
10344
  kind,
10570
- label: label2,
10345
+ label,
10571
10346
  summary: redactRunnerProofText(summary, redactionSecrets),
10572
10347
  evidence: evidence.map((value) => redactRunnerProofText(value, redactionSecrets)),
10573
10348
  redacted: true
@@ -10590,10 +10365,10 @@ function packageScriptArgs(command, scriptName) {
10590
10365
  }
10591
10366
  return ["run", scriptName];
10592
10367
  }
10593
- function summarizeCommandOutput(label2, result, redactionSecrets = []) {
10368
+ function summarizeCommandOutput(label, result, redactionSecrets = []) {
10594
10369
  const lines = `${result.stdout}
10595
10370
  ${result.stderr ?? ""}`.split(/\r?\n/).map((line) => redactRunnerProofText(line.trim(), redactionSecrets)).filter(Boolean).slice(0, 8);
10596
- return [`${label2} ${result.ok ? "succeeded" : "failed"}`, ...lines];
10371
+ return [`${label} ${result.ok ? "succeeded" : "failed"}`, ...lines];
10597
10372
  }
10598
10373
  function redactRunnerProofText(value, additionalSecrets = []) {
10599
10374
  let out = redactAdditionalSecrets(value, additionalSecrets);
@@ -10742,9 +10517,9 @@ function formatRepoMatch(repoMatch) {
10742
10517
  }
10743
10518
  }
10744
10519
  async function runCommand(command, args, cwd) {
10745
- return new Promise((resolve6) => {
10520
+ return new Promise((resolve5) => {
10746
10521
  (0, import_node_child_process3.execFile)(command, args, { cwd, windowsHide: true }, (error, stdout, stderr) => {
10747
- resolve6({
10522
+ resolve5({
10748
10523
  ok: !error,
10749
10524
  stdout: String(stdout ?? ""),
10750
10525
  stderr: String(stderr ?? "")
@@ -10869,7 +10644,7 @@ function isRecord6(value) {
10869
10644
  // src/tui/runInteractive.ts
10870
10645
  var import_node_path14 = require("node:path");
10871
10646
 
10872
- // ../../../../node_modules/@clack/core/dist/index.mjs
10647
+ // ../../node_modules/@clack/core/dist/index.mjs
10873
10648
  var import_sisteransi = __toESM(require_src(), 1);
10874
10649
  var import_node_process = require("node:process");
10875
10650
  var g = __toESM(require("node:readline"), 1);
@@ -11227,7 +11002,7 @@ var LD = class extends x {
11227
11002
  }
11228
11003
  };
11229
11004
 
11230
- // ../../../../node_modules/@clack/prompts/dist/index.mjs
11005
+ // ../../node_modules/@clack/prompts/dist/index.mjs
11231
11006
  var import_node_process2 = __toESM(require("node:process"), 1);
11232
11007
  var import_picocolors2 = __toESM(require_picocolors(), 1);
11233
11008
  var import_sisteransi2 = __toESM(require_src(), 1);
@@ -11406,12 +11181,12 @@ var import_picocolors4 = __toESM(require_picocolors());
11406
11181
  function compact(value) {
11407
11182
  return String(value ?? "").replace(/\s+/g, " ").trim();
11408
11183
  }
11409
- function originalHint(gap2) {
11410
- const hint = compact(gap2.copyPrompt);
11184
+ function originalHint(gap) {
11185
+ const hint = compact(gap.copyPrompt);
11411
11186
  return hint ? hint : "No scanner hint was returned for this gap.";
11412
11187
  }
11413
- function buildAgentFixPrompt(artifact, gap2) {
11414
- const affected = [gap2.primaryMapCategory, ...gap2.affectedMapCategories ?? []].filter(Boolean).filter((value, index, all) => all.indexOf(value) === index).join(", ");
11188
+ function buildAgentFixPrompt(artifact, gap) {
11189
+ const affected = [gap.primaryMapCategory, ...gap.affectedMapCategories ?? []].filter(Boolean).filter((value, index, all) => all.indexOf(value) === index).join(", ");
11415
11190
  return [
11416
11191
  "You are an AI coding agent using VibeRaven as the production-readiness map.",
11417
11192
  "",
@@ -11419,11 +11194,11 @@ function buildAgentFixPrompt(artifact, gap2) {
11419
11194
  "",
11420
11195
  `Project: ${artifact.workspacePath}`,
11421
11196
  `Current VibeRaven state: production core ${artifact.productionCorePercent}% | score ${artifact.score} (${artifact.scoreLabel})`,
11422
- `Gap: ${gap2.title}`,
11423
- `Gap id: ${gap2.id}`,
11424
- `Severity: ${gap2.severity}`,
11425
- `Production area: ${gap2.primaryMapCategory}${affected ? ` | affected: ${affected}` : ""}`,
11426
- `Scanner detail: ${compact(gap2.detail)}`,
11197
+ `Gap: ${gap.title}`,
11198
+ `Gap id: ${gap.id}`,
11199
+ `Severity: ${gap.severity}`,
11200
+ `Production area: ${gap.primaryMapCategory}${affected ? ` | affected: ${affected}` : ""}`,
11201
+ `Scanner detail: ${compact(gap.detail)}`,
11427
11202
  "",
11428
11203
  "Required workflow:",
11429
11204
  "1. Read `.viberaven/agent-summary.md` and `.viberaven/launch-playbook.md` before changing code.",
@@ -11449,12 +11224,12 @@ function buildAgentFixPrompt(artifact, gap2) {
11449
11224
  "- Report what changed, what verification passed, and which VibeRaven gap remains next.",
11450
11225
  "",
11451
11226
  "Original scanner hint:",
11452
- originalHint(gap2)
11227
+ originalHint(gap)
11453
11228
  ].join("\n");
11454
11229
  }
11455
11230
 
11456
11231
  // src/version.ts
11457
- var VERSION = "1.1.4";
11232
+ var VERSION = "1.1.6";
11458
11233
 
11459
11234
  // src/commands/guide.ts
11460
11235
  var import_picocolors3 = __toESM(require_picocolors());
@@ -11615,7 +11390,7 @@ function buildVercelSupabaseAudit(input) {
11615
11390
  buildPoolerCheck(input.files),
11616
11391
  buildServiceRoleCheck(input.files)
11617
11392
  ];
11618
- const status = checks.every((check) => check.status === "pass") ? "pass" : "needs_work";
11393
+ const status = checks.every((check2) => check2.status === "pass") ? "pass" : "needs_work";
11619
11394
  return {
11620
11395
  status,
11621
11396
  summary: status === "pass" ? "Repo evidence passes the local Vercel/Supabase audit checks." : "Repo evidence needs work before claiming Vercel/Supabase production readiness.",
@@ -11702,13 +11477,13 @@ function renderVercelSupabaseAudit(result) {
11702
11477
  "Boundary: this audit uses local repo evidence only. Provider dashboard settings still need manual verification or read-only provider MCP confirmation.",
11703
11478
  ""
11704
11479
  ];
11705
- for (const check of result.checks) {
11706
- lines.push(`${check.status === "pass" ? "PASS" : "NEEDS_WORK"} ${check.id}`);
11707
- lines.push(` ${check.summary}`);
11708
- for (const evidence of check.evidence.slice(0, 5)) {
11480
+ for (const check2 of result.checks) {
11481
+ lines.push(`${check2.status === "pass" ? "PASS" : "NEEDS_WORK"} ${check2.id}`);
11482
+ lines.push(` ${check2.summary}`);
11483
+ for (const evidence of check2.evidence.slice(0, 5)) {
11709
11484
  lines.push(` - ${evidence}`);
11710
11485
  }
11711
- if (check.evidence.length === 0) {
11486
+ if (check2.evidence.length === 0) {
11712
11487
  lines.push(" - No local evidence found.");
11713
11488
  }
11714
11489
  lines.push("");
@@ -11740,6 +11515,15 @@ Maps repo evidence to OWASP LLM Top 10 launch risks. Supports SAFECode-style dep
11740
11515
  var STACK_FRAMING = `Stack context for this repo: Next.js, Supabase, Vercel, VibeRaven.
11741
11516
  Do NOT deploy to Vercel or ship to production users without running \`${PUBLIC_AGENT_MODE_COMMAND}\`.
11742
11517
  Do NOT rely on manual production checklists when VibeRaven gate artifacts exist in \`.viberaven/\`.`;
11518
+ var PRODUCTION_PROTOCOL = `## VibeRaven Production Protocol
11519
+
11520
+ Before making deploy, auth, database, payment, API, or scaling changes:
11521
+
11522
+ 1. Read \`.viberaven/prp.json\`.
11523
+ 2. Follow \`.viberaven/mission-map.md\`.
11524
+ 3. Do not deploy if \`decision.status\` is \`blocked\`.
11525
+ 4. Fix \`nextActions\` in order.
11526
+ 5. Re-run \`${PUBLIC_AGENT_MODE_COMMAND}\`.`;
11743
11527
  var AGENT_NEGATIVE_CONSTRAINTS = `## Event-Triggered Production Verification
11744
11528
 
11745
11529
  Trigger: modifications to \`package.json\`, \`vercel.json\`, \`supabase/migrations/**/*\`, \`.env.example\`, auth/billing/webhook paths, or deploy workflow files.
@@ -11765,12 +11549,14 @@ var AGENT_RULES_BODY = `${AGENT_RULES_PREAMBLE}
11765
11549
 
11766
11550
  ${STACK_FRAMING}
11767
11551
 
11552
+ ${PRODUCTION_PROTOCOL}
11553
+
11768
11554
  ${AGENT_NEGATIVE_CONSTRAINTS}
11769
11555
 
11770
11556
  ## VibeRaven Production-Readiness Gate
11771
11557
 
11772
11558
  - Before running build, compile, cleanup, deployment, vercel, or supabase commands, run \`${PUBLIC_AGENT_MODE_COMMAND}\` first from the project root.
11773
- - Treat VibeRaven as the agent production layer for AI-built apps, especially apps using Vercel, Supabase, auth, database, payments, webhooks, monitoring, rate limits, or environment variables.
11559
+ - Treat VibeRaven as the Agent Context + Production Gate for AI-built apps, especially apps using Vercel, Supabase, auth, database, payments, webhooks, monitoring, rate limits, or environment variables.
11774
11560
  - Read \`.viberaven/agent-tasklist.md\` first. If it says CRITICAL GAPS DETECTED or PRODUCTION GATE NOT CLEAR, fix one listed repo-code gap before deploying.
11775
11561
  - Read \`.viberaven/gate-result.json\` for the machine verdict.
11776
11562
  - Read \`.viberaven/context-map.json\` for compact agent context.
@@ -11814,10 +11600,11 @@ var AGENT_CONTEXT_BODY = `${AGENT_RULES_PREAMBLE}
11814
11600
 
11815
11601
  After \`--agent-mode\`, read these artifacts in order:
11816
11602
 
11817
- 1. \`.viberaven/mission-map.md\`
11818
- 2. \`.viberaven/agent-tasklist.md\`
11819
- 3. \`.viberaven/gate-result.json\`
11820
- 4. \`.viberaven/context-map.json\``;
11603
+ 1. \`.viberaven/prp.json\`
11604
+ 2. \`.viberaven/mission-map.md\`
11605
+ 3. \`.viberaven/agent-tasklist.md\`
11606
+ 4. \`.viberaven/gate-result.json\`
11607
+ 5. \`.viberaven/context-map.json\``;
11821
11608
  var MISSION_MAP_BODY = `${AGENT_RULES_PREAMBLE}
11822
11609
 
11823
11610
  ## Mission Map loop
@@ -12038,17 +11825,17 @@ function renderCursorCoreRulePreview() {
12038
11825
  async function initCursorRulesPack(options) {
12039
11826
  const results = [];
12040
11827
  const pack = buildCursorRulesPack();
12041
- for (const rule2 of pack) {
12042
- const file = `${CURSOR_RULES_DIR}/${rule2.filename}`;
12043
- const path3 = (0, import_node_path11.join)(options.cwd, file);
12044
- const existing = await readExistingFile(path3);
12045
- const changed = !existing.exists || existing.content !== rule2.content;
11828
+ for (const rule of pack) {
11829
+ const file = `${CURSOR_RULES_DIR}/${rule.filename}`;
11830
+ const path = (0, import_node_path11.join)(options.cwd, file);
11831
+ const existing = await readExistingFile(path);
11832
+ const changed = !existing.exists || existing.content !== rule.content;
12046
11833
  const action = !existing.exists ? "created" : changed ? "updated" : "unchanged";
12047
11834
  if (!options.dryRun && changed) {
12048
11835
  await (0, import_promises8.mkdir)((0, import_node_path11.join)(options.cwd, CURSOR_RULES_DIR), { recursive: true });
12049
- await (0, import_promises8.writeFile)(path3, rule2.content, "utf-8");
11836
+ await (0, import_promises8.writeFile)(path, rule.content, "utf-8");
12050
11837
  }
12051
- results.push({ target: "cursor", file, path: path3, action });
11838
+ results.push({ target: "cursor", file, path, action });
12052
11839
  }
12053
11840
  const legacyPath = (0, import_node_path11.join)(options.cwd, LEGACY_CURSOR_RULE_FILE);
12054
11841
  if (!options.dryRun && await fileExists(legacyPath)) {
@@ -12056,9 +11843,9 @@ async function initCursorRulesPack(options) {
12056
11843
  }
12057
11844
  return results;
12058
11845
  }
12059
- async function readExistingFile(path3) {
11846
+ async function readExistingFile(path) {
12060
11847
  try {
12061
- return { exists: true, content: await (0, import_promises8.readFile)(path3, "utf-8") };
11848
+ return { exists: true, content: await (0, import_promises8.readFile)(path, "utf-8") };
12062
11849
  } catch (error) {
12063
11850
  if (isFileNotFoundError(error)) {
12064
11851
  return { exists: false, content: "" };
@@ -12066,9 +11853,9 @@ async function readExistingFile(path3) {
12066
11853
  throw error;
12067
11854
  }
12068
11855
  }
12069
- async function fileExists(path3) {
11856
+ async function fileExists(path) {
12070
11857
  try {
12071
- await (0, import_promises8.access)(path3);
11858
+ await (0, import_promises8.access)(path);
12072
11859
  return true;
12073
11860
  } catch {
12074
11861
  return false;
@@ -12219,15 +12006,15 @@ async function initAgentRules(options) {
12219
12006
  continue;
12220
12007
  }
12221
12008
  const file = AGENT_RULE_TARGETS[target].file;
12222
- const path3 = (0, import_node_path13.join)(options.cwd, file);
12223
- const existing = await readExistingFile2(path3);
12009
+ const path = (0, import_node_path13.join)(options.cwd, file);
12010
+ const existing = await readExistingFile2(path);
12224
12011
  const injected = injectAgentRulesBlock(existing.content, renderAgentRulesForTarget(target));
12225
12012
  const action = !existing.exists ? "created" : injected.changed ? "updated" : "unchanged";
12226
12013
  if (!options.dryRun && injected.changed) {
12227
- await (0, import_promises10.mkdir)((0, import_node_path13.dirname)(path3), { recursive: true });
12228
- await (0, import_promises10.writeFile)(path3, injected.content, "utf-8");
12014
+ await (0, import_promises10.mkdir)((0, import_node_path13.dirname)(path), { recursive: true });
12015
+ await (0, import_promises10.writeFile)(path, injected.content, "utf-8");
12229
12016
  }
12230
- results.push({ target, file, path: path3, action });
12017
+ results.push({ target, file, path, action });
12231
12018
  }
12232
12019
  const packageJsonScripts = await seedPackageJsonScripts({
12233
12020
  cwd: options.cwd,
@@ -12238,7 +12025,7 @@ async function initAgentRules(options) {
12238
12025
  function renderAgentRulesDryRun(targets) {
12239
12026
  const files = targets.flatMap((target) => {
12240
12027
  if (target === "cursor") {
12241
- return buildCursorRulesPack().map((rule2) => `- cursor: .cursor/rules/${rule2.filename}`);
12028
+ return buildCursorRulesPack().map((rule) => `- cursor: .cursor/rules/${rule.filename}`);
12242
12029
  }
12243
12030
  return [`- ${target}: ${AGENT_RULE_TARGETS[target].file}`];
12244
12031
  }).join("\n");
@@ -12291,40 +12078,9 @@ function formatAgentRulesInitSummary(output) {
12291
12078
  );
12292
12079
  return lines.join("\n");
12293
12080
  }
12294
- async function maybeAutoInstallAgentRules(options) {
12295
- const existing = await Promise.all([
12296
- fileExists2((0, import_node_path13.join)(options.cwd, AGENT_RULE_TARGETS.codex.file)),
12297
- fileExists2((0, import_node_path13.join)(options.cwd, AGENT_RULE_TARGETS.claude.file)),
12298
- fileExists2((0, import_node_path13.join)(options.cwd, AGENT_RULE_TARGETS.cursor.file))
12299
- ]);
12300
- if (existing.some(Boolean)) {
12301
- return { status: "ready" };
12302
- }
12303
- try {
12304
- const output = await initAgentRules({
12305
- cwd: options.cwd,
12306
- targets: ["codex", "claude", "cursor", "agentContext", "missionMap"]
12307
- });
12308
- return { status: "installed", output };
12309
- } catch (error) {
12310
- return { status: "skipped", reason: error instanceof Error ? error.message : String(error) };
12311
- }
12312
- }
12313
- function formatAgentRulesBootstrapSummary(result) {
12314
- if (result.status === "ready") {
12315
- return "Agent setup: Codex/Claude/Cursor instructions already connected.";
12316
- }
12317
- if (result.status === "skipped") {
12318
- return `Agent setup: skipped (${result.reason})`;
12319
- }
12320
- const created = result.output.results.filter((item3) => item3.action === "created").map((item3) => item3.file);
12321
- const updated = result.output.results.filter((item3) => item3.action === "updated").map((item3) => item3.file);
12322
- const changed = [...created, ...updated];
12323
- return changed.length > 0 ? `Agent setup: connected ${changed.join(", ")}.` : "Agent setup: Codex/Claude/Cursor instructions already connected.";
12324
- }
12325
- async function readExistingFile2(path3) {
12081
+ async function readExistingFile2(path) {
12326
12082
  try {
12327
- return { exists: true, content: await (0, import_promises10.readFile)(path3, "utf-8") };
12083
+ return { exists: true, content: await (0, import_promises10.readFile)(path, "utf-8") };
12328
12084
  } catch (error) {
12329
12085
  if (isFileNotFoundError2(error)) {
12330
12086
  return { exists: false, content: "" };
@@ -12332,14 +12088,6 @@ async function readExistingFile2(path3) {
12332
12088
  throw error;
12333
12089
  }
12334
12090
  }
12335
- async function fileExists2(path3) {
12336
- try {
12337
- await (0, import_promises10.access)(path3);
12338
- return true;
12339
- } catch {
12340
- return false;
12341
- }
12342
- }
12343
12091
  function isFileNotFoundError2(error) {
12344
12092
  return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
12345
12093
  }
@@ -12500,15 +12248,15 @@ async function handleViewGaps(cwd) {
12500
12248
  async function handlePrompt(cwd) {
12501
12249
  try {
12502
12250
  const artifact = await loadLastArtifact(cwd);
12503
- const gap2 = pickGap(artifact);
12504
- if (!gap2) {
12251
+ const gap = pickGap(artifact);
12252
+ if (!gap) {
12505
12253
  M2.warn("No gaps to fix. Run a scan or pick a different project.");
12506
12254
  return;
12507
12255
  }
12508
- const prompt = buildAgentFixPrompt(artifact, gap2);
12256
+ const prompt = buildAgentFixPrompt(artifact, gap);
12509
12257
  try {
12510
12258
  await copyToClipboard(prompt);
12511
- M2.success(import_picocolors4.default.green(`Copied top prompt to clipboard \u2014 ${gap2.title}`));
12259
+ M2.success(import_picocolors4.default.green(`Copied top prompt to clipboard \u2014 ${gap.title}`));
12512
12260
  } catch (error) {
12513
12261
  M2.warn(error instanceof Error ? error.message : String(error));
12514
12262
  console.log("");
@@ -12619,15 +12367,19 @@ async function handleOpenDashboard(cwd) {
12619
12367
  }
12620
12368
  function buildMenuOptions(isSignedIn) {
12621
12369
  return [
12622
- { value: "scan", label: "Make this app production ready", hint: "Scan, write tasks, start the next fix" },
12623
- { value: "next", label: "Continue next step", hint: "One action from the last run" },
12624
- { value: "open-dashboard", label: "Open provider step", hint: "Supabase, Stripe, Vercel, auth" },
12625
- { value: "guide", label: "Provider setup guide", hint: "Simple dashboard steps" },
12626
- { value: "agent-rules", label: "Connect this AI agent", hint: "Codex, Claude, Cursor rules" },
12370
+ { value: "next", label: "What's next?", hint: "One action from last scan" },
12371
+ { value: "scan", label: "Scan project", hint: "Map launch readiness" },
12372
+ { value: "open-report", label: "Open report in browser", hint: "Rebuilds UI from last scan" },
12373
+ { value: "guide", label: "Provider guide", hint: "Vercel, Supabase, Stripe steps" },
12374
+ { value: "open-dashboard", label: "Open dashboard", hint: "Browser link for next step" },
12375
+ { value: "gaps", label: "View top gaps", hint: "From last scan" },
12376
+ { value: "prompt", label: "Copy top prompt", hint: "Agent-ready fix prompt" },
12377
+ { value: "agent-rules", label: "Install agent rules", hint: "AGENTS.md, CLAUDE.md, Cursor" },
12378
+ { value: "audit", label: "Vercel/Supabase audit", hint: "RLS, service-role, serverless pooler evidence" },
12627
12379
  {
12628
12380
  value: "auth",
12629
12381
  label: isSignedIn ? "Sign out" : "Sign in",
12630
- hint: isSignedIn ? "Clear local credentials" : "Use the managed VibeRaven scan"
12382
+ hint: isSignedIn ? "Clear local credentials" : "Device login flow"
12631
12383
  },
12632
12384
  { value: "exit", label: "Exit" }
12633
12385
  ];
@@ -12643,7 +12395,7 @@ async function runInteractiveSession(startDir = process.cwd()) {
12643
12395
  if (!artifactsAt) {
12644
12396
  M2.message(
12645
12397
  import_picocolors4.default.dim(
12646
- 'No .viberaven/ here yet. Choose "Make this app production ready" to start.'
12398
+ "No .viberaven/ here yet. Extension scans are separate \u2014 choose Scan project to write CLI artifacts."
12647
12399
  )
12648
12400
  );
12649
12401
  }
@@ -13886,7 +13638,7 @@ var STALE_PATTERNS = [
13886
13638
  async function checkAgentInjection(cwd) {
13887
13639
  const checks = [];
13888
13640
  for (const item3 of REQUIRED_EXISTENCE_CHECKS) {
13889
- const exists = await fileExists3((0, import_node_path23.join)(cwd, item3.file));
13641
+ const exists = await fileExists2((0, import_node_path23.join)(cwd, item3.file));
13890
13642
  checks.push({
13891
13643
  id: item3.id,
13892
13644
  status: exists ? "pass" : "fail",
@@ -13894,7 +13646,7 @@ async function checkAgentInjection(cwd) {
13894
13646
  });
13895
13647
  }
13896
13648
  for (const item3 of OPTIONAL_CURSOR_PACK_CHECKS) {
13897
- const exists = await fileExists3((0, import_node_path23.join)(cwd, item3.file));
13649
+ const exists = await fileExists2((0, import_node_path23.join)(cwd, item3.file));
13898
13650
  checks.push({
13899
13651
  id: item3.id,
13900
13652
  status: exists ? "pass" : "fail",
@@ -13902,9 +13654,9 @@ async function checkAgentInjection(cwd) {
13902
13654
  });
13903
13655
  }
13904
13656
  const legacyCursorPath = (0, import_node_path23.join)(cwd, ".cursor/rules/viberaven.mdc");
13905
- if (await fileExists3(legacyCursorPath)) {
13657
+ if (await fileExists2(legacyCursorPath)) {
13906
13658
  const legacyContent = await (0, import_promises18.readFile)(legacyCursorPath, "utf-8");
13907
- const hasCoreSplit = await fileExists3((0, import_node_path23.join)(cwd, ".cursor/rules/viberaven-core.mdc"));
13659
+ const hasCoreSplit = await fileExists2((0, import_node_path23.join)(cwd, ".cursor/rules/viberaven-core.mdc"));
13908
13660
  if (!hasCoreSplit || legacyContent.includes("alwaysApply: true")) {
13909
13661
  checks.push({
13910
13662
  id: "cursor-legacy-mdc",
@@ -13915,8 +13667,8 @@ async function checkAgentInjection(cwd) {
13915
13667
  }
13916
13668
  for (const target of CORE_AGENT_INJECTION_TARGETS) {
13917
13669
  const file = target === "cursor" ? ".cursor/rules/viberaven-core.mdc" : AGENT_RULE_TARGETS[target].file;
13918
- const path3 = (0, import_node_path23.join)(cwd, file);
13919
- const exists = await fileExists3(path3);
13670
+ const path = (0, import_node_path23.join)(cwd, file);
13671
+ const exists = await fileExists2(path);
13920
13672
  if (!exists) {
13921
13673
  checks.push({
13922
13674
  id: `canonical-${target}`,
@@ -13925,7 +13677,7 @@ async function checkAgentInjection(cwd) {
13925
13677
  });
13926
13678
  continue;
13927
13679
  }
13928
- const content = await (0, import_promises18.readFile)(path3, "utf-8");
13680
+ const content = await (0, import_promises18.readFile)(path, "utf-8");
13929
13681
  const hasCommand = content.includes(PUBLIC_AGENT_MODE_COMMAND);
13930
13682
  checks.push({
13931
13683
  id: `canonical-${target}`,
@@ -13943,23 +13695,23 @@ async function checkAgentInjection(cwd) {
13943
13695
  }
13944
13696
  }
13945
13697
  return {
13946
- ok: checks.every((check) => check.status === "pass"),
13698
+ ok: checks.every((check2) => check2.status === "pass"),
13947
13699
  checks
13948
13700
  };
13949
13701
  }
13950
13702
  function formatDoctorAgentsReport(report) {
13951
13703
  const lines = ["VibeRaven agent injection doctor", ""];
13952
- for (const check of report.checks) {
13953
- const icon = check.status === "pass" ? "\u2713" : "\u2717";
13954
- lines.push(`${icon} ${check.message}`);
13704
+ for (const check2 of report.checks) {
13705
+ const icon = check2.status === "pass" ? "\u2713" : "\u2717";
13706
+ lines.push(`${icon} ${check2.message}`);
13955
13707
  }
13956
13708
  lines.push("");
13957
13709
  lines.push(report.ok ? "All agent injection checks passed." : "Agent injection checks failed.");
13958
13710
  return lines.join("\n");
13959
13711
  }
13960
- async function fileExists3(path3) {
13712
+ async function fileExists2(path) {
13961
13713
  try {
13962
- await (0, import_promises18.access)(path3);
13714
+ await (0, import_promises18.access)(path);
13963
13715
  return true;
13964
13716
  } catch {
13965
13717
  return false;
@@ -14172,216 +13924,8 @@ async function runAuditCommand(input) {
14172
13924
  return result.status === "pass" ? 0 : 1;
14173
13925
  }
14174
13926
 
14175
- // src/commands/badge.ts
14176
- var import_promises19 = require("node:fs/promises");
14177
- var import_node_path24 = require("node:path");
14178
-
14179
- // src/brand/palette.ts
13927
+ // src/commands/preview.ts
14180
13928
  var import_picocolors5 = __toESM(require_picocolors());
14181
- var STATUS_GLYPH = {
14182
- clear: "\u2713",
14183
- warning: "\u25C8",
14184
- blocked: "\u25C6",
14185
- fixable: "\u25B8",
14186
- manual: "\u25C7",
14187
- done: "\u2713",
14188
- pending: "\xB7"
14189
- };
14190
- var DECISION_GLYPH = {
14191
- CLEAR: STATUS_GLYPH.clear,
14192
- WARNING: STATUS_GLYPH.warning,
14193
- BLOCKED: STATUS_GLYPH.blocked
14194
- };
14195
- function identity(text) {
14196
- return text;
14197
- }
14198
- function colorsFor(mode) {
14199
- return import_picocolors5.default.createColors(mode === "rich");
14200
- }
14201
- function decisionColor(decision, mode) {
14202
- const colors = colorsFor(mode);
14203
- if (decision === "CLEAR") return colors.green;
14204
- if (decision === "WARNING") return colors.yellow;
14205
- return colors.red;
14206
- }
14207
- function decisionStyle(decision, mode) {
14208
- const colorize = mode === "rich" ? decisionColor(decision, mode) : identity;
14209
- return {
14210
- glyph: DECISION_GLYPH[decision],
14211
- label: colorize(decision),
14212
- colorize
14213
- };
14214
- }
14215
- function richOrPlain(text, mode, colorize) {
14216
- return mode === "rich" ? colorize(text) : text;
14217
- }
14218
- var accent = {
14219
- brand(text, mode) {
14220
- return richOrPlain(text, mode, colorsFor(mode).cyan);
14221
- },
14222
- dim(text, mode) {
14223
- return richOrPlain(text, mode, colorsFor(mode).dim);
14224
- },
14225
- bold(text, mode) {
14226
- return richOrPlain(text, mode, colorsFor(mode).bold);
14227
- }
14228
- };
14229
-
14230
- // src/brand/ui.ts
14231
- var import_picocolors6 = __toESM(require_picocolors());
14232
- var ANSI_PATTERN = /\x1b\[[0-9;]*m/g;
14233
- function colorsFor2(mode) {
14234
- return import_picocolors6.default.createColors(mode === "rich");
14235
- }
14236
- function stripAnsi(value) {
14237
- return value.replace(ANSI_PATTERN, "");
14238
- }
14239
- function visibleWidth(value) {
14240
- return stripAnsi(value).length;
14241
- }
14242
- function padVisible(value, width) {
14243
- return `${value}${" ".repeat(Math.max(0, width - visibleWidth(value)))}`;
14244
- }
14245
- function safeWidth(width) {
14246
- if (!Number.isFinite(width)) return 1;
14247
- return Math.max(1, Math.floor(width));
14248
- }
14249
- function clampPercent(percent) {
14250
- if (Number.isNaN(percent)) return 0;
14251
- return Math.max(0, Math.min(100, percent));
14252
- }
14253
- function colorizeGaugeFill(text, percent, mode) {
14254
- const colors = colorsFor2(mode);
14255
- if (mode !== "rich") return text;
14256
- if (percent >= 80) return colors.green(text);
14257
- if (percent >= 50) return colors.yellow(text);
14258
- return colors.red(text);
14259
- }
14260
- function rule(width, options) {
14261
- const line = "\u2500".repeat(safeWidth(width));
14262
- return accent.dim(line, options.mode);
14263
- }
14264
- function box(lines, options) {
14265
- const content = lines.length > 0 ? lines : [""];
14266
- const innerWidth = Math.max(1, ...content.map(visibleWidth));
14267
- const horizontal = "\u2500".repeat(innerWidth + 2);
14268
- const colors = colorsFor2(options.mode);
14269
- const border = (value) => options.mode === "rich" ? colors.cyan(value) : value;
14270
- const body = content.map((line) => border("\u2502") + ` ${padVisible(line, innerWidth)} ` + border("\u2502"));
14271
- return [border(`\u250C${horizontal}\u2510`), ...body, border(`\u2514${horizontal}\u2518`)].join("\n");
14272
- }
14273
- function gauge(percent, options) {
14274
- const width = safeWidth(options.width);
14275
- const clamped = clampPercent(percent);
14276
- const label2 = `${Math.round(clamped)}%`;
14277
- const filledWidth = Math.round(clamped / 100 * width);
14278
- const emptyWidth = width - filledWidth;
14279
- const filled = colorizeGaugeFill("\u2588".repeat(filledWidth), clamped, options.mode);
14280
- const empty = accent.dim("\u2591".repeat(emptyWidth), options.mode);
14281
- return `[${filled}${empty}] ${label2}`;
14282
- }
14283
- function banner(decision, options) {
14284
- const style = decisionStyle(decision, options.mode);
14285
- return `${style.glyph} ${style.label}`;
14286
- }
14287
-
14288
- // src/brand/wordmark.ts
14289
- var DEFAULT_TAGLINE = "production operator for AI-built apps";
14290
- function wordmark(options) {
14291
- const tagline = options.tagline ?? DEFAULT_TAGLINE;
14292
- const brand = accent.brand("VibeRaven", options.mode);
14293
- const mutedTagline = accent.dim(tagline, options.mode);
14294
- if (options.variant === "compact") {
14295
- return `${brand} - ${mutedTagline}`;
14296
- }
14297
- return [accent.bold(brand, options.mode), mutedTagline].join("\n");
14298
- }
14299
-
14300
- // src/badge/renderBadge.ts
14301
- var COLORS = {
14302
- CLEAR: "#2ea44f",
14303
- WARNING: "#dbab09",
14304
- BLOCKED: "#d1242f"
14305
- };
14306
- function label(prp) {
14307
- return prp.decision.toLowerCase();
14308
- }
14309
- function renderBadgeSvg(prp) {
14310
- const text = label(prp);
14311
- const color = COLORS[prp.decision];
14312
- const width = 132 + text.length * 7;
14313
- return `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="20" role="img" aria-label="VibeRaven: ${text}">
14314
- <rect width="${width}" height="20" fill="#24292f"/>
14315
- <rect x="86" width="${width - 86}" height="20" fill="${color}"/>
14316
- <g fill="#fff" font-family="Verdana,Geneva,sans-serif" font-size="11">
14317
- <text x="8" y="14">VibeRaven</text>
14318
- <text x="94" y="14">${text}</text>
14319
- </g>
14320
- </svg>`;
14321
- }
14322
- function renderBadgeMarkdown(prp) {
14323
- const text = label(prp);
14324
- const date = prp.generatedAt.slice(0, 10);
14325
- return `[![VibeRaven launch gate: ${text}](.viberaven/badge.svg)](https://viberaven.dev/production-protocol.md?utm_source=badge) \`gate: ${text} - ${date}\``;
14326
- }
14327
- function renderBadgeCard(prp, mode) {
14328
- const text = label(prp);
14329
- const date = prp.generatedAt.slice(0, 10);
14330
- const style = decisionStyle(prp.decision, mode);
14331
- return box(
14332
- [
14333
- wordmark({ variant: "compact", mode, tagline: "launch gate proof" }),
14334
- `${accent.dim("gate", mode)}: ${style.colorize(text)}`,
14335
- `${accent.dim("date", mode)}: ${date}`,
14336
- accent.brand("viberaven.dev", mode)
14337
- ],
14338
- { mode }
14339
- );
14340
- }
14341
-
14342
- // src/brand/renderMode.ts
14343
- function resolveRenderMode(input) {
14344
- if (input.env.VIBERAVEN_FORCE_RICH === "1") return "rich";
14345
- if (input.env.VIBERAVEN_PLAIN === "1") return "plain";
14346
- if (input.env.NO_COLOR !== void 0 && input.env.NO_COLOR !== "") return "plain";
14347
- if (input.surface === "agent-mode") return "plain";
14348
- return input.isTTY ? "rich" : "plain";
14349
- }
14350
- function currentRenderMode(surface) {
14351
- return resolveRenderMode({
14352
- surface,
14353
- isTTY: Boolean(process.stdout.isTTY),
14354
- env: process.env
14355
- });
14356
- }
14357
-
14358
- // src/commands/badge.ts
14359
- async function runBadgeCommand(options) {
14360
- const log = options.log ?? ((message) => console.log(message));
14361
- const artifactDir = (0, import_node_path24.join)(options.cwd, ".viberaven");
14362
- const prpPath = (0, import_node_path24.join)(artifactDir, "prp.json");
14363
- let prp;
14364
- try {
14365
- prp = JSON.parse(await (0, import_promises19.readFile)(prpPath, "utf8"));
14366
- } catch {
14367
- log("No .viberaven/prp.json found. Run: npx -y viberaven --agent-mode");
14368
- return 1;
14369
- }
14370
- if (options.svg) {
14371
- const outputPath = (0, import_node_path24.join)(artifactDir, "badge.svg");
14372
- await (0, import_promises19.mkdir)(artifactDir, { recursive: true });
14373
- await (0, import_promises19.writeFile)(outputPath, renderBadgeSvg(prp), "utf8");
14374
- log(`Wrote ${outputPath}`);
14375
- return 0;
14376
- }
14377
- if (options.card) {
14378
- log(renderBadgeCard(prp, currentRenderMode("human-command")));
14379
- return 0;
14380
- }
14381
- log("Add this to your README:");
14382
- log(renderBadgeMarkdown(prp));
14383
- return 0;
14384
- }
14385
13929
 
14386
13930
  // src/output/nextActionBlock.ts
14387
13931
  function buildNextActionBlock(tasks, loopState, plan) {
@@ -14397,7 +13941,11 @@ function buildNextActionBlock(tasks, loopState, plan) {
14397
13941
  return {
14398
13942
  ...base,
14399
13943
  type: "stalled",
14400
- title: "Loop stalled \u2014 no gap reduction after 2+ consecutive scans",
13944
+ title: "Loop stalled - no gap reduction after 2+ consecutive scans",
13945
+ agentInstruction: "Stop the scan loop. Explain the blocker plainly, then either guide the provider/user action or report that no safe repo-code recipe is available.",
13946
+ executionPlan: [
13947
+ { kind: "stop", note: "Do not keep spending scans while the loop is stalled." }
13948
+ ],
14401
13949
  requiresUserAction: true,
14402
13950
  stallReason
14403
13951
  };
@@ -14406,42 +13954,67 @@ function buildNextActionBlock(tasks, loopState, plan) {
14406
13954
  return {
14407
13955
  ...base,
14408
13956
  type: "verify",
14409
- title: "Batch complete \u2014 run verify to rescan before next batch",
13957
+ title: "Batch complete - run verify to rescan before next batch",
13958
+ agentInstruction: "Run verify now, read the refreshed VibeRaven artifacts, then continue with the next safe repo fix or provider step. Do not stop at a summary.",
13959
+ executionPlan: [
13960
+ { kind: "verify", command: "viberaven_verify" },
13961
+ { kind: "continue-loop", note: "After verify, continue until the gate is clear or a provider/user blocker remains." }
13962
+ ],
14410
13963
  mcpTool: "viberaven_verify",
14411
13964
  mcpArgs: {},
14412
13965
  requiresUserAction: false
14413
13966
  };
14414
13967
  }
14415
- const repoCodeTask = tasks.find((t) => t.fixType === "repo-code" && !t.requiresUserAction);
13968
+ const repoCodeTask = tasks.find((task) => task.fixType === "repo-code" && !task.requiresUserAction);
14416
13969
  if (repoCodeTask) {
13970
+ const fallbackCommand = repoCodeTask.mcpArgs ? `npx -y viberaven --heal --apply --gap ${repoCodeTask.gapId} --yes` : void 0;
14417
13971
  return {
14418
13972
  ...base,
14419
13973
  type: "repo-code",
14420
13974
  gapId: repoCodeTask.gapId,
14421
13975
  title: repoCodeTask.title,
13976
+ agentInstruction: "Apply this repo-code fix now using the MCP tool or fallback command, then continue the local VibeRaven loop. Do not stop at a summary.",
13977
+ executionPlan: [
13978
+ {
13979
+ kind: "apply-repo-fix",
13980
+ command: repoCodeTask.mcpTool ?? fallbackCommand,
13981
+ note: "Patch only this gap and preserve unrelated user changes."
13982
+ },
13983
+ { kind: "continue-loop", note: "Apply remaining safe repo fixes until the batch is full, then verify once." }
13984
+ ],
14422
13985
  mcpTool: repoCodeTask.mcpTool,
14423
13986
  mcpArgs: repoCodeTask.mcpArgs,
14424
- fallbackCommand: repoCodeTask.mcpArgs ? `npx -y viberaven --heal --apply --gap ${repoCodeTask.gapId} --yes` : void 0,
13987
+ fallbackCommand,
14425
13988
  requiresUserAction: false
14426
13989
  };
14427
13990
  }
14428
- const providerTask = tasks.find((t) => t.fixType === "provider-action");
13991
+ const providerTask = tasks.find((task) => task.fixType === "provider-action");
14429
13992
  if (providerTask) {
14430
13993
  return {
14431
13994
  ...base,
14432
13995
  type: "provider-action",
14433
13996
  gapId: providerTask.gapId,
14434
13997
  title: providerTask.title,
13998
+ agentInstruction: "Open or present the provider step now. If browser/tool access is available, open the dashboard link for the user; otherwise show the exact step and copy values. Do not keep telling the user to verify until the provider step is done.",
13999
+ executionPlan: [
14000
+ { kind: "open-provider-step", note: "Use VIBERAVEN_PROVIDER_ACTION for the exact dashboard step and URL." },
14001
+ { kind: "wait-for-user", note: "Wait for confirmation or read-only MCP evidence before verifying." }
14002
+ ],
14435
14003
  requiresUserAction: true
14436
14004
  };
14437
14005
  }
14438
- const upgradeTask = tasks.find((t) => t.fixType === "upgrade-required");
14006
+ const upgradeTask = tasks.find((task) => task.fixType === "upgrade-required");
14439
14007
  if (upgradeTask) {
14440
14008
  return {
14441
14009
  ...base,
14442
14010
  type: "upgrade-required",
14443
14011
  gapId: upgradeTask.gapId,
14444
14012
  title: upgradeTask.title,
14013
+ agentInstruction: "Explain that this production lane needs an upgrade or connected provider evidence. Continue with any remaining unlocked repo-code fixes if present; otherwise stop cleanly.",
14014
+ executionPlan: [
14015
+ { kind: "upgrade", command: "https://viberaven.dev/pricing" },
14016
+ { kind: "stop", note: "Do not pretend locked lanes or provider state were fixed locally." }
14017
+ ],
14445
14018
  requiresUserAction: true,
14446
14019
  upgradeUrl: "https://viberaven.dev/pricing"
14447
14020
  };
@@ -14449,15 +14022,20 @@ function buildNextActionBlock(tasks, loopState, plan) {
14449
14022
  return {
14450
14023
  ...base,
14451
14024
  type: "done",
14452
- title: "All gaps resolved \u2014 production gate is clear",
14025
+ title: "All gaps resolved - production gate is clear",
14026
+ agentInstruction: "The repo-code gate is clear. Run strict before deploy and still verify provider dashboard state through MCP or the provider dashboard.",
14027
+ executionPlan: [
14028
+ { kind: "verify", command: "npx -y viberaven --strict" },
14029
+ { kind: "stop", note: "Do not deploy until provider dashboard state is verified where applicable." }
14030
+ ],
14453
14031
  requiresUserAction: false
14454
14032
  };
14455
14033
  }
14456
14034
  function resolveStallReason(tasks) {
14457
14035
  if (tasks.length === 0) return "no-recipes";
14458
- const allUpgradeOrEmpty = tasks.every((t) => t.fixType === "upgrade-required");
14036
+ const allUpgradeOrEmpty = tasks.every((task) => task.fixType === "upgrade-required");
14459
14037
  if (allUpgradeOrEmpty) return "no-recipes";
14460
- const allProviderAction = tasks.every((t) => t.fixType === "provider-action");
14038
+ const allProviderAction = tasks.every((task) => task.fixType === "provider-action");
14461
14039
  if (allProviderAction) return "provider-action-required";
14462
14040
  return "unknown";
14463
14041
  }
@@ -14477,33 +14055,162 @@ function buildProviderActionBlock(task) {
14477
14055
  dashboardUrl: pa.dashboardUrl,
14478
14056
  exactStep: pa.exactStep,
14479
14057
  envKeyName: pa.envKeyName ?? null,
14058
+ envKeyExample: pa.envKeyExample ?? null,
14480
14059
  doneSignal: pa.doneSignal,
14481
14060
  verifyCommand: task.verifyCommand,
14482
14061
  mcpAlternative: task.mcpTool ?? pa.mcpAlternative ?? null
14483
14062
  }
14484
14063
  };
14485
14064
  }
14486
- function printProviderActionBlock(tasks) {
14487
- const task = tasks.find(
14488
- (t) => t.fixType === "provider-action" && t.requiresUserAction === true
14489
- );
14490
- if (!task) return;
14491
- const block = buildProviderActionBlock(task);
14492
- if (!block) return;
14493
- console.log(PROVIDER_ACTION_START);
14494
- console.log(JSON.stringify(block, null, 2));
14495
- console.log(PROVIDER_ACTION_END);
14065
+ function printProviderActionBlock(tasks) {
14066
+ const task = tasks.find(
14067
+ (entry) => entry.fixType === "provider-action" && entry.requiresUserAction === true
14068
+ );
14069
+ if (!task) return;
14070
+ const block = buildProviderActionBlock(task);
14071
+ if (!block) return;
14072
+ console.log(PROVIDER_ACTION_START);
14073
+ console.log(JSON.stringify(block, null, 2));
14074
+ console.log(PROVIDER_ACTION_END);
14075
+ }
14076
+ function printNextActionBlock(block) {
14077
+ console.log(NEXT_ACTION_START);
14078
+ console.log(JSON.stringify(block, null, 2));
14079
+ console.log(NEXT_ACTION_END);
14080
+ }
14081
+
14082
+ // src/commands/preview.ts
14083
+ function previewArtifact(cwd) {
14084
+ return {
14085
+ version: 1,
14086
+ scannedAt: (/* @__PURE__ */ new Date()).toISOString(),
14087
+ workspacePath: cwd,
14088
+ score: 42,
14089
+ scoreLabel: "Blocked",
14090
+ summary: "Vercel, Supabase, and Stripe are detected, but launch-critical provider setup is not proven yet.",
14091
+ archetype: "ai-built-saas",
14092
+ plan: "free",
14093
+ productionCorePercent: 40,
14094
+ usage: {
14095
+ plan: "free",
14096
+ remainingPrompts: 0,
14097
+ used: 0,
14098
+ limit: 0,
14099
+ period: "lifetime",
14100
+ unlockedMapCategoryKeys: ["appFlow", "frontend", "backend", "auth", "database", "payments"]
14101
+ },
14102
+ gaps: [
14103
+ {
14104
+ id: "auth_secret_missing",
14105
+ category: "SECURITY & AUTH",
14106
+ severity: "critical",
14107
+ title: "Create production auth secret",
14108
+ detail: "The app needs a production session secret before real users sign in.",
14109
+ copyPrompt: "Generate and document NEXTAUTH_SECRET or the equivalent auth secret for this stack.",
14110
+ toolSuggestions: [],
14111
+ mcpSuggestion: null,
14112
+ primaryMapCategory: "auth",
14113
+ affectedMapCategories: []
14114
+ },
14115
+ {
14116
+ id: "rls_disabled",
14117
+ category: "DATABASE & DATA",
14118
+ severity: "critical",
14119
+ title: "Verify Supabase RLS before launch",
14120
+ detail: "Database access must be protected by Row Level Security or equivalent server-side controls.",
14121
+ copyPrompt: "Open Supabase and verify RLS policies for user-owned tables.",
14122
+ toolSuggestions: [],
14123
+ mcpSuggestion: null,
14124
+ primaryMapCategory: "database",
14125
+ affectedMapCategories: []
14126
+ },
14127
+ {
14128
+ id: "stripe_webhook_secret_missing",
14129
+ category: "PAYMENTS",
14130
+ severity: "critical",
14131
+ title: "Connect Stripe webhook secret",
14132
+ detail: "Stripe webhook signature verification needs STRIPE_WEBHOOK_SECRET before live payments.",
14133
+ copyPrompt: "Create a Stripe webhook endpoint and add STRIPE_WEBHOOK_SECRET to the deployment environment.",
14134
+ toolSuggestions: [],
14135
+ mcpSuggestion: null,
14136
+ primaryMapCategory: "payments",
14137
+ affectedMapCategories: []
14138
+ }
14139
+ ],
14140
+ missionGraph: {
14141
+ areas: [],
14142
+ byArea: {},
14143
+ byProvider: {},
14144
+ repositoryEvidence: { env: [], security: [] }
14145
+ },
14146
+ stackWiring: { items: [], byKey: {} },
14147
+ providerRegistry: {
14148
+ version: 1,
14149
+ source: "bundled",
14150
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
14151
+ status: "fresh",
14152
+ staleAfterDays: 14,
14153
+ providers: []
14154
+ }
14155
+ };
14156
+ }
14157
+ function check(done, text) {
14158
+ const mark = done ? import_picocolors5.default.green("[x]") : import_picocolors5.default.yellow("[ ]");
14159
+ return ` ${mark} ${text}`;
14496
14160
  }
14497
- function printNextActionBlock(block) {
14498
- console.log(NEXT_ACTION_START);
14499
- console.log(JSON.stringify(block, null, 2));
14500
- console.log(NEXT_ACTION_END);
14161
+ async function runPreviewCommand(options) {
14162
+ const artifact = previewArtifact(options.cwd);
14163
+ const tasks = buildTaskList(artifact);
14164
+ const next = buildNextActionBlock(tasks, {
14165
+ batchApplied: 1,
14166
+ appliedGapIdsSinceScan: ["auth_secret_missing"],
14167
+ lastGapCount: artifact.gaps.length,
14168
+ stalledScans: 0
14169
+ }, "free");
14170
+ if (options.json) {
14171
+ console.log(JSON.stringify({ artifact, tasks, next }, null, 2));
14172
+ return 0;
14173
+ }
14174
+ console.log(import_picocolors5.default.dim(`VibeRaven ${VERSION} preview - local rehearsal, no login or API spend.`));
14175
+ console.log(import_picocolors5.default.bold("VibeRaven"));
14176
+ console.log("Taking this app from demo to production.");
14177
+ console.log("");
14178
+ console.log(`${import_picocolors5.default.bold("Status:")} ${import_picocolors5.default.red("BLOCKED")} | vercel + supabase + stripe | 3 launch gaps`);
14179
+ console.log("");
14180
+ console.log(import_picocolors5.default.bold("Progress"));
14181
+ console.log(check(true, "Understand the stack and launch gaps"));
14182
+ console.log(check(true, "Patch the first repo-owned secret gap"));
14183
+ console.log(check(false, "Open Supabase and verify RLS policies"));
14184
+ console.log(check(false, "Connect Stripe webhook signing secret"));
14185
+ console.log(check(false, "Verify again before launch"));
14186
+ console.log("");
14187
+ console.log(import_picocolors5.default.bold("I can do now"));
14188
+ console.log(" 1. Add missing env names and agent rules.");
14189
+ console.log(" 2. Prepare focused repo fixes the coding agent can apply.");
14190
+ console.log(" 3. Keep the launch loop moving until provider proof is needed.");
14191
+ console.log("");
14192
+ console.log(import_picocolors5.default.bold("I need you for"));
14193
+ console.log(" 1. Supabase: open the project and confirm RLS policies.");
14194
+ console.log(" 2. Stripe: create or open the webhook endpoint.");
14195
+ console.log(" 3. Deployment: paste provider secrets into Vercel or your host.");
14196
+ console.log("");
14197
+ console.log(import_picocolors5.default.bold("Open"));
14198
+ console.log(` supabase: ${import_picocolors5.default.cyan("https://supabase.com/dashboard")}`);
14199
+ console.log(` stripe: ${import_picocolors5.default.cyan("https://dashboard.stripe.com/")}`);
14200
+ console.log("");
14201
+ console.log(`${import_picocolors5.default.bold("Copy env names:")} NEXTAUTH_SECRET, STRIPE_WEBHOOK_SECRET`);
14202
+ console.log(`${import_picocolors5.default.bold("Next:")} I will guide the blocked provider setup, then verify when you confirm it is done.`);
14203
+ if (options.agentMode) {
14204
+ console.log("");
14205
+ console.log(import_picocolors5.default.dim("Agent note: render the checklist above as the live timeline; update [ ] to [x] as work completes."));
14206
+ }
14207
+ return 0;
14501
14208
  }
14502
14209
 
14503
14210
  // src/providerMcpBridge.ts
14504
14211
  var import_node_fs12 = require("node:fs");
14505
14212
  var import_node_os2 = require("node:os");
14506
- var import_node_path25 = require("node:path");
14213
+ var import_node_path24 = require("node:path");
14507
14214
  var UPGRADE_URL4 = "https://viberaven.dev/pricing";
14508
14215
  var FALLBACK_COMMAND = "npx -y viberaven audit --vercel-supabase --json";
14509
14216
  var SUPPORTED_PROVIDERS = /* @__PURE__ */ new Set(["supabase", "vercel"]);
@@ -14511,9 +14218,9 @@ var configPathsOverride;
14511
14218
  function defaultMcpConfigPaths() {
14512
14219
  const home = (0, import_node_os2.homedir)();
14513
14220
  return [
14514
- (0, import_node_path25.join)(home, ".config", "claude", "claude_desktop_config.json"),
14515
- (0, import_node_path25.join)(home, ".cursor", "mcp.json"),
14516
- (0, import_node_path25.join)(home, ".gemini", "antigravity", "mcp_config.json")
14221
+ (0, import_node_path24.join)(home, ".config", "claude", "claude_desktop_config.json"),
14222
+ (0, import_node_path24.join)(home, ".cursor", "mcp.json"),
14223
+ (0, import_node_path24.join)(home, ".gemini", "antigravity", "mcp_config.json")
14517
14224
  ];
14518
14225
  }
14519
14226
  function resolveConfigPaths() {
@@ -14541,12 +14248,12 @@ function findServerEntry(servers, provider2) {
14541
14248
  }
14542
14249
  function findProviderMcpConfig(provider2, configPaths) {
14543
14250
  const paths = configPaths ?? resolveConfigPaths();
14544
- for (const path3 of paths) {
14545
- if (!(0, import_node_fs12.existsSync)(path3)) {
14251
+ for (const path of paths) {
14252
+ if (!(0, import_node_fs12.existsSync)(path)) {
14546
14253
  continue;
14547
14254
  }
14548
14255
  try {
14549
- const raw = JSON.parse((0, import_node_fs12.readFileSync)(path3, "utf8"));
14256
+ const raw = JSON.parse((0, import_node_fs12.readFileSync)(path, "utf8"));
14550
14257
  const servers = parseMcpServers(raw);
14551
14258
  if (!servers) {
14552
14259
  continue;
@@ -14560,7 +14267,7 @@ function findProviderMcpConfig(provider2, configPaths) {
14560
14267
  command: typeof server.command === "string" ? server.command : void 0,
14561
14268
  args: Array.isArray(server.args) ? server.args.filter((arg) => typeof arg === "string") : void 0,
14562
14269
  url: typeof server.url === "string" ? server.url : void 0,
14563
- source: path3
14270
+ source: path
14564
14271
  };
14565
14272
  } catch {
14566
14273
  continue;
@@ -14568,19 +14275,6 @@ function findProviderMcpConfig(provider2, configPaths) {
14568
14275
  }
14569
14276
  return void 0;
14570
14277
  }
14571
- function hasUsableProviderMcpConfig(config) {
14572
- return Boolean(config.command?.trim() || config.url?.trim());
14573
- }
14574
- function findUsableProviderMcpConfig(provider2, configPaths) {
14575
- const paths = configPaths ?? resolveConfigPaths();
14576
- for (const path3 of paths) {
14577
- const config = findProviderMcpConfig(provider2, [path3]);
14578
- if (config && hasUsableProviderMcpConfig(config)) {
14579
- return config;
14580
- }
14581
- }
14582
- return void 0;
14583
- }
14584
14278
  async function verifyProviderGap(options) {
14585
14279
  if (options.plan !== "pro") {
14586
14280
  return {
@@ -14611,581 +14305,6 @@ async function verifyProviderGap(options) {
14611
14305
  };
14612
14306
  }
14613
14307
 
14614
- // src/connectedTools.ts
14615
- var DETECTED_PROVIDERS = ["supabase", "vercel", "stripe", "github"];
14616
- var INSTALL_HINTS = {
14617
- supabase: "claude mcp add --transport http supabase https://mcp.supabase.com/",
14618
- vercel: "claude mcp add --transport http vercel https://mcp.vercel.com/",
14619
- stripe: "claude mcp add --transport http stripe https://mcp.stripe.com/",
14620
- github: "claude mcp add --transport http github https://api.githubcopilot.com/mcp/"
14621
- };
14622
- var READONLY_PROVIDERS = /* @__PURE__ */ new Set(["vercel"]);
14623
- function isDetectedProvider(provider2) {
14624
- return DETECTED_PROVIDERS.includes(provider2);
14625
- }
14626
- function installHintFor(provider2) {
14627
- const normalizedProvider = provider2.toLowerCase().trim();
14628
- if (isDetectedProvider(normalizedProvider)) {
14629
- return INSTALL_HINTS[normalizedProvider];
14630
- }
14631
- return `Add the ${provider2} MCP server to your agent config.`;
14632
- }
14633
- function detectConnectedTools() {
14634
- const tools = {};
14635
- for (const provider2 of DETECTED_PROVIDERS) {
14636
- const config = findUsableProviderMcpConfig(provider2);
14637
- if (!config) {
14638
- tools[`${provider2}Mcp`] = "missing";
14639
- continue;
14640
- }
14641
- tools[`${provider2}Mcp`] = READONLY_PROVIDERS.has(provider2) ? "available_readonly" : "available";
14642
- }
14643
- tools.browser = "available";
14644
- return tools;
14645
- }
14646
-
14647
- // src/output/actionPanelBlock.ts
14648
- var ACTION_PANEL_START = "VIBERAVEN_ACTION_PANEL_START";
14649
- var ACTION_PANEL_END = "VIBERAVEN_ACTION_PANEL_END";
14650
- function stackLine(prp) {
14651
- const parts = Array.from(
14652
- /* @__PURE__ */ new Set([
14653
- ...prp.detectedStack.deployment,
14654
- ...prp.detectedStack.database,
14655
- ...prp.detectedStack.auth,
14656
- ...prp.detectedStack.payments,
14657
- ...prp.detectedStack.monitoring
14658
- ])
14659
- );
14660
- return parts.length > 0 ? parts.join(" + ") : prp.detectedStack.archetype ?? "unknown";
14661
- }
14662
- function envVarNames(tasks) {
14663
- return Array.from(
14664
- new Set(
14665
- tasks.map((task) => task.providerAction?.envKeyName).filter((name) => Boolean(name))
14666
- )
14667
- );
14668
- }
14669
- function humanStep(task) {
14670
- if (task.fixType === "provider-action") {
14671
- return task.providerAction?.exactStep ?? task.title;
14672
- }
14673
- if (task.fixType === "upgrade-required") {
14674
- return `Upgrade required: ${task.action ?? task.exactFix ?? task.title}`;
14675
- }
14676
- if (task.fixType === "manual-verify") {
14677
- return `Manual verification required: ${task.action ?? task.exactFix ?? task.title}`;
14678
- }
14679
- return task.title;
14680
- }
14681
- function repoTaskTitle(task) {
14682
- return task.action ?? task.exactFix ?? task.title;
14683
- }
14684
- function parseDecision(value) {
14685
- if (value === "CLEAR" || value === "WARNING" || value === "BLOCKED") return value;
14686
- return "BLOCKED";
14687
- }
14688
- function providerFromConnectedToolKey(tool) {
14689
- if (!tool.endsWith("Mcp")) return void 0;
14690
- return tool.slice(0, -3).toLowerCase();
14691
- }
14692
- function normalizeProvider3(provider2) {
14693
- const lower = provider2.trim().toLowerCase();
14694
- const tokens = lower.split(/[^a-z0-9]+/).filter(Boolean);
14695
- for (const knownProvider of ["supabase", "stripe", "vercel", "github"]) {
14696
- if (lower === knownProvider || tokens.includes(knownProvider)) {
14697
- return knownProvider;
14698
- }
14699
- }
14700
- return lower;
14701
- }
14702
- function toolSetupFor(prp) {
14703
- const relevantProviders = new Set([
14704
- ...prp.detectedStack.auth,
14705
- ...prp.detectedStack.database,
14706
- ...prp.detectedStack.deployment,
14707
- ...prp.detectedStack.payments
14708
- ].map(normalizeProvider3));
14709
- return Object.entries(prp.connectedTools).flatMap(([tool, state]) => {
14710
- const provider2 = providerFromConnectedToolKey(tool);
14711
- if (!provider2 || state !== "missing" || !relevantProviders.has(provider2)) {
14712
- return [];
14713
- }
14714
- return [provider2];
14715
- }).map((provider2) => ({
14716
- provider: provider2,
14717
- command: installHintFor(provider2),
14718
- reason: "Connect this provider MCP so the agent can verify live provider state when available."
14719
- }));
14720
- }
14721
- function checklistFor(repoTasks, humanTasks) {
14722
- return [
14723
- { label: "Understand the stack and launch gaps", done: true, kind: "scan" },
14724
- ...repoTasks.slice(0, 3).map((task) => ({
14725
- label: repoTaskTitle(task),
14726
- done: false,
14727
- kind: "repo-code"
14728
- })),
14729
- ...humanTasks.slice(0, 3).map((task) => ({
14730
- label: humanStep(task),
14731
- done: false,
14732
- kind: "provider"
14733
- })),
14734
- { label: "Verify again before launch", done: false, kind: "verify" }
14735
- ];
14736
- }
14737
- function mermaidFor(prp, tasks) {
14738
- const status = prp.decision === "CLEAR" ? "Clear" : prp.decision === "WARNING" ? "Review" : "Blocked";
14739
- const hasProvider = tasks.some((task) => task.fixType === "provider-action");
14740
- const hasRepo = tasks.some((task) => task.fixType === "repo-code" && !task.requiresUserAction);
14741
- const lines = [
14742
- "flowchart LR",
14743
- ` app["AI-built app"] --> stack["${stackLine(prp)}"]`,
14744
- ` stack --> gate["VibeRaven: ${status}"]`
14745
- ];
14746
- if (hasRepo) lines.push(' gate --> repo["Repo fixes"]');
14747
- if (hasProvider) lines.push(' gate --> provider["Provider setup"]');
14748
- lines.push(' gate --> verify["Verify before launch"]');
14749
- return lines.join("\n");
14750
- }
14751
- function buildActionPanelBlock(prp, tasks) {
14752
- const repoTasks = tasks.filter((task) => task.fixType === "repo-code" && !task.requiresUserAction);
14753
- const humanTasks = tasks.filter((task) => task.fixType !== "repo-code" || task.requiresUserAction);
14754
- const nextRepoTask = repoTasks[0];
14755
- const toolSetup = toolSetupFor(prp);
14756
- const statusLine = [
14757
- prp.decision,
14758
- stackLine(prp),
14759
- `${tasks.length} launch gap${tasks.length === 1 ? "" : "s"}`
14760
- ].join(" | ");
14761
- return {
14762
- VIBERAVEN_ACTION_PANEL: {
14763
- surface: "native-agent-chat",
14764
- title: "VibeRaven",
14765
- statusLine,
14766
- primaryCommand: nextRepoTask ? `npx -y viberaven --heal --apply --gap ${nextRepoTask.gapId} --yes` : "npx -y viberaven next --json",
14767
- nextCommand: "npx -y viberaven next --json",
14768
- verifyCommand: prp.verifyCommand,
14769
- checklist: checklistFor(repoTasks, humanTasks),
14770
- mermaidDiagram: mermaidFor(prp, tasks),
14771
- agentCanDo: repoTasks.length > 0 ? repoTasks.map(repoTaskTitle) : ["No safe repo-code fix is available yet. I will guide the provider steps first."],
14772
- userMustDo: humanTasks.map((task) => ({
14773
- provider: task.providerAction?.provider,
14774
- dashboardUrl: task.providerAction?.dashboardUrl,
14775
- step: humanStep(task),
14776
- doneWhen: task.providerAction?.doneSignal
14777
- })),
14778
- toolSetup,
14779
- envVarNames: envVarNames(tasks),
14780
- agentInstruction: "Render this as a short VibeRaven action message in the native chat. Use the checklist as the live execution timeline and update [ ] to [x] as tasks complete. Present toolSetup as optional connection help, not as a blocker for safe repo fixes. Do not describe the output with internal product labels. Keep the user inside Codex, Claude Code, Cursor, or the terminal. Do not open report.html unless the user explicitly asks for the legacy report."
14781
- }
14782
- };
14783
- }
14784
- function nextHumanStep(panel) {
14785
- const hasRepoFix = panel.agentCanDo.some((item3) => !item3.startsWith("No safe repo-code fix"));
14786
- if (hasRepoFix) {
14787
- return "Next: I will apply the first safe repo fix, then verify the launch state.";
14788
- }
14789
- if (panel.userMustDo.length > 0) {
14790
- return "Next: I will guide the blocked provider setup, then verify when you confirm it is done.";
14791
- }
14792
- return "Next: I will verify the launch state before deploy.";
14793
- }
14794
- function buildActionPanelCard(block, mode = "plain") {
14795
- const panel = block.VIBERAVEN_ACTION_PANEL;
14796
- const agentCanDo = panel.agentCanDo.slice(0, 3);
14797
- const userMustDo = panel.userMustDo.slice(0, 3);
14798
- const lines = [];
14799
- const statusParts = panel.statusLine.split(/\s*\|\s*/);
14800
- const decision = parseDecision(statusParts[0] ?? "BLOCKED");
14801
- const rest = statusParts.slice(1).join(" | ");
14802
- lines.push(accent.bold("VibeRaven", mode));
14803
- lines.push("Taking this app from demo to production.");
14804
- lines.push("");
14805
- lines.push(`Status: ${banner(decision, { mode })}${rest ? ` | ${rest}` : ""}`);
14806
- lines.push("");
14807
- lines.push("Progress:");
14808
- panel.checklist.slice(0, 6).forEach((item3) => {
14809
- lines.push(` - [${item3.done ? "x" : " "}] ${item3.label}`);
14810
- });
14811
- lines.push("");
14812
- lines.push("I can do now:");
14813
- agentCanDo.forEach((item3, index) => {
14814
- lines.push(` ${index + 1}. ${item3}`);
14815
- });
14816
- lines.push("");
14817
- lines.push("I need you for:");
14818
- if (userMustDo.length === 0) {
14819
- lines.push(" (none)");
14820
- } else {
14821
- userMustDo.forEach((item3, index) => {
14822
- const provider2 = item3.provider ? `${item3.provider}: ` : "";
14823
- lines.push(` ${index + 1}. ${provider2}${item3.step}`);
14824
- });
14825
- }
14826
- const links = userMustDo.filter((item3) => item3.dashboardUrl);
14827
- if (links.length > 0) {
14828
- lines.push("");
14829
- lines.push("Open:");
14830
- links.forEach((item3) => {
14831
- const label2 = item3.provider ?? "provider";
14832
- lines.push(` ${label2}: ${item3.dashboardUrl}`);
14833
- });
14834
- }
14835
- if (panel.envVarNames.length > 0) {
14836
- lines.push("");
14837
- lines.push(`Copy env names: ${panel.envVarNames.join(", ")}`);
14838
- }
14839
- const toolSetup = panel.toolSetup.slice(0, 3);
14840
- if (toolSetup.length > 0) {
14841
- lines.push("");
14842
- lines.push("Connect tools:");
14843
- toolSetup.forEach((item3) => {
14844
- lines.push(` ${item3.provider}: ${item3.command}`);
14845
- });
14846
- }
14847
- lines.push("");
14848
- lines.push(nextHumanStep(panel));
14849
- lines.push("Verify: I will re-check after the repo fix or provider step.");
14850
- return lines.join("\n");
14851
- }
14852
- function printActionPanelBlock(prp, tasks, mode = "plain") {
14853
- const block = buildActionPanelBlock(prp, tasks);
14854
- console.log(buildActionPanelCard(block, mode));
14855
- console.log(ACTION_PANEL_START);
14856
- console.log(JSON.stringify(block, null, 2));
14857
- console.log(ACTION_PANEL_END);
14858
- }
14859
-
14860
- // src/output/celebration.ts
14861
- function renderClearCelebration(prp, mode) {
14862
- if (prp.decision !== "CLEAR") return "";
14863
- const verifiedDate = prp.generatedAt.slice(0, 10);
14864
- const style = decisionStyle("CLEAR", mode);
14865
- const lines = [
14866
- `${style.glyph} ${style.label} production gate is clear`,
14867
- accent.dim(`Verified ${verifiedDate}`, mode),
14868
- "",
14869
- `Share your launch proof: ${accent.bold("npx -y viberaven badge", mode)}`
14870
- ];
14871
- return [wordmark({ variant: "compact", mode }), box(lines, { mode }), rule(40, { mode })].join("\n");
14872
- }
14873
-
14874
- // src/output/loopProgress.ts
14875
- function safeCount(value) {
14876
- if (!Number.isFinite(value)) return 0;
14877
- return Math.max(0, Math.floor(value));
14878
- }
14879
- function renderLoopProgress(input, mode) {
14880
- if (mode === "plain" && input.silentInPlain) return "";
14881
- const total = safeCount(input.total);
14882
- const applied = total > 0 ? Math.min(safeCount(input.applied), total) : safeCount(input.applied);
14883
- const percent = total === 0 ? 100 : applied / total * 100;
14884
- const label2 = `${STATUS_GLYPH.fixable} ${input.label}`;
14885
- return `${accent.bold(label2, mode)} ${gauge(percent, { width: 12, mode })} ${applied}/${total}`;
14886
- }
14887
-
14888
- // src/demo/runDemo.ts
14889
- var import_node_fs13 = require("node:fs");
14890
- var import_node_path27 = __toESM(require("node:path"));
14891
-
14892
- // src/demo/localRules.ts
14893
- var import_promises20 = require("node:fs/promises");
14894
- var import_node_path26 = __toESM(require("node:path"));
14895
- var DEMO_SCANNED_AT = "2026-06-14T00:00:00.000Z";
14896
- async function readKnownFile(root, relativePath) {
14897
- try {
14898
- return await (0, import_promises20.readFile)(import_node_path26.default.join(root, relativePath), "utf8");
14899
- } catch (error) {
14900
- const code = error.code;
14901
- if (code === "ENOENT") return "";
14902
- throw error;
14903
- }
14904
- }
14905
- function dependencyNames(packageJson) {
14906
- if (!packageJson.trim()) return /* @__PURE__ */ new Set();
14907
- const parsed = JSON.parse(packageJson);
14908
- return /* @__PURE__ */ new Set([
14909
- ...Object.keys(parsed.dependencies ?? {}),
14910
- ...Object.keys(parsed.devDependencies ?? {})
14911
- ]);
14912
- }
14913
- function stripTypeScriptComments(source) {
14914
- return source.replace(/\/\*[\s\S]*?\*\//g, "").split("\n").map((line) => {
14915
- let quote = null;
14916
- let escaped = false;
14917
- for (let index = 0; index < line.length - 1; index += 1) {
14918
- const char = line[index];
14919
- const next = line[index + 1];
14920
- if (quote) {
14921
- if (escaped) {
14922
- escaped = false;
14923
- } else if (char === "\\") {
14924
- escaped = true;
14925
- } else if (char === quote) {
14926
- quote = null;
14927
- }
14928
- continue;
14929
- }
14930
- if (char === '"' || char === "'" || char === "`") {
14931
- quote = char;
14932
- continue;
14933
- }
14934
- if (char === "/" && next === "/") {
14935
- return line.slice(0, index);
14936
- }
14937
- }
14938
- return line;
14939
- }).join("\n");
14940
- }
14941
- function stripSqlComments(source) {
14942
- return source.replace(/\/\*[\s\S]*?\*\//g, "").replace(/--.*$/gm, "");
14943
- }
14944
- function hasStripeWebhookSignatureVerification(source) {
14945
- const executable = stripTypeScriptComments(source);
14946
- return /webhooks\s*\.\s*constructEvent\s*\(/.test(executable) && /Stripe-Signature/.test(executable);
14947
- }
14948
- function hasEnabledRowLevelSecurity(source) {
14949
- return /enable\s+row\s+level\s+security/i.test(stripSqlComments(source));
14950
- }
14951
- function hasEnvAssignment(source, name) {
14952
- const pattern = new RegExp(`^\\s*${name}\\s*=`, "m");
14953
- return pattern.test(source);
14954
- }
14955
- function gap(input) {
14956
- return {
14957
- ...input,
14958
- toolSuggestions: [],
14959
- mcpSuggestion: null,
14960
- affectedMapCategories: input.affectedMapCategories ?? []
14961
- };
14962
- }
14963
- function emptyMissionGraph() {
14964
- return {
14965
- areas: [],
14966
- byArea: {},
14967
- byProvider: {},
14968
- repositoryEvidence: {
14969
- env: [],
14970
- security: []
14971
- }
14972
- };
14973
- }
14974
- async function scanDemoFixture(root) {
14975
- const [packageJson, stripeWebhook, migration, envExample] = await Promise.all([
14976
- readKnownFile(root, "package.json"),
14977
- readKnownFile(root, "app/api/stripe/webhook/route.ts"),
14978
- readKnownFile(root, "supabase/migrations/0001_init.sql"),
14979
- readKnownFile(root, ".env.example")
14980
- ]);
14981
- const dependencies = dependencyNames(packageJson);
14982
- const hasNext = dependencies.has("next");
14983
- const hasSupabase = dependencies.has("@supabase/supabase-js");
14984
- const hasStripe = dependencies.has("stripe");
14985
- const selectedProviders = {};
14986
- if (hasNext) selectedProviders.deployment = "vercel";
14987
- if (hasSupabase) {
14988
- selectedProviders.database = "supabase";
14989
- selectedProviders.auth = "supabase";
14990
- }
14991
- if (hasStripe) selectedProviders.payments = "stripe";
14992
- const gaps = [];
14993
- if (hasStripe && !hasStripeWebhookSignatureVerification(stripeWebhook)) {
14994
- gaps.push(
14995
- gap({
14996
- id: "stripe_webhook_signature_missing",
14997
- category: "SECURITY & AUTH",
14998
- severity: "critical",
14999
- title: "Verify Stripe webhook signatures",
15000
- detail: "The demo Stripe webhook handler does not verify the Stripe-Signature header with stripe.webhooks.constructEvent.",
15001
- copyPrompt: "Update the Stripe webhook route to read the raw request body, read the Stripe-Signature header, and verify events with stripe.webhooks.constructEvent using STRIPE_WEBHOOK_SECRET.",
15002
- primaryMapCategory: "payments",
15003
- affectedMapCategories: ["security"]
15004
- })
15005
- );
15006
- }
15007
- if (hasSupabase && !hasEnabledRowLevelSecurity(migration)) {
15008
- gaps.push(
15009
- gap({
15010
- id: "rls_disabled",
15011
- category: "DATABASE & DATA",
15012
- severity: "critical",
15013
- title: "Enable Supabase row level security",
15014
- detail: "The demo migration creates application data without enabling row level security.",
15015
- copyPrompt: "Add alter table statements that enable row level security and add least-privilege policies for the Supabase tables in the demo migration.",
15016
- primaryMapCategory: "database",
15017
- affectedMapCategories: ["auth", "security"]
15018
- })
15019
- );
15020
- }
15021
- if (hasStripe && !hasEnvAssignment(envExample, "STRIPE_WEBHOOK_SECRET")) {
15022
- gaps.push(
15023
- gap({
15024
- id: "missing_prod_env_stripe_webhook_secret",
15025
- category: "DEPLOYMENT",
15026
- severity: "warning",
15027
- title: "Document STRIPE_WEBHOOK_SECRET",
15028
- detail: "The demo .env.example does not include a STRIPE_WEBHOOK_SECRET assignment.",
15029
- copyPrompt: "Add STRIPE_WEBHOOK_SECRET to .env.example as an empty variable name so deploy setup documents the required Stripe webhook secret without exposing a value.",
15030
- primaryMapCategory: "deployment",
15031
- affectedMapCategories: ["payments"]
15032
- })
15033
- );
15034
- }
15035
- const hasCriticalGaps = gaps.some((item3) => item3.severity === "critical");
15036
- return {
15037
- version: 1,
15038
- scannedAt: DEMO_SCANNED_AT,
15039
- workspacePath: root,
15040
- score: hasCriticalGaps ? 55 : 90,
15041
- scoreLabel: hasCriticalGaps ? "Needs work" : "Looks solid",
15042
- summary: `Demo scan: ${gaps.length} launch gap(s) detected by local rules.`,
15043
- archetype: hasNext && hasSupabase && hasStripe ? "nextjs-supabase-stripe" : "demo-saas",
15044
- gaps,
15045
- missionGraph: emptyMissionGraph(),
15046
- stackWiring: { items: [], byKey: {} },
15047
- providerRegistry: {
15048
- version: 1,
15049
- source: "bundled",
15050
- generatedAt: DEMO_SCANNED_AT,
15051
- staleAfterDays: 30,
15052
- status: "fresh",
15053
- providers: []
15054
- },
15055
- verificationSummary: { byArea: {} },
15056
- productionCorePercent: hasCriticalGaps ? 40 : 95,
15057
- selectedProviders
15058
- };
15059
- }
15060
-
15061
- // src/demo/runDemo.ts
15062
- var import_picocolors7 = __toESM(require_picocolors());
15063
- function bundledFixtureRoot() {
15064
- const candidates = [
15065
- import_node_path27.default.join(__dirname, "..", "fixtures", "demo-saas"),
15066
- import_node_path27.default.join(__dirname, "..", "..", "fixtures", "demo-saas")
15067
- ];
15068
- return candidates.find((candidate) => (0, import_node_fs13.existsSync)(import_node_path27.default.join(candidate, "package.json"))) ?? candidates[0];
15069
- }
15070
- function canUseInteractiveTryMenu(options) {
15071
- return isPreviewLabel(options.label) && !options.agentMode && !options.openReport && process.stdin.isTTY === true && process.stdout.isTTY === true && process.env.CI !== "true";
15072
- }
15073
- function isPreviewLabel(label2) {
15074
- return label2 === "preview" || label2 === "try";
15075
- }
15076
- function printTryMenuSummary(input) {
15077
- const providerStack = Array.from(new Set(Object.values(input.artifact.selectedProviders ?? {}))).filter(Boolean).join(" + ");
15078
- console.log("");
15079
- console.log("VibeRaven Preview");
15080
- console.log("Local production rehearsal. No login or API spend.");
15081
- console.log("");
15082
- console.log(`Preview stack: ${input.artifact.archetype}${providerStack ? ` (${providerStack})` : ""}`);
15083
- console.log(
15084
- `Decision: BLOCKED | Production core: ${input.artifact.productionCorePercent}% | Gaps: ${input.artifact.gaps.length}`
15085
- );
15086
- console.log("");
15087
- console.log("Menu:");
15088
- console.log(" 1. Show Codex/Claude native agent UI");
15089
- console.log(" npx -y viberaven preview --agent-mode");
15090
- console.log(" 2. Run VibeRaven on your real app");
15091
- console.log(" npx -y viberaven --agent-mode");
15092
- console.log(" 3. Open legacy HTML report only if you explicitly want it");
15093
- console.log(" npx -y viberaven preview --open");
15094
- console.log("");
15095
- if (input.opened) {
15096
- console.log("Opened legacy HTML report in your browser.");
15097
- }
15098
- console.log("");
15099
- }
15100
- async function runInteractiveTryMenu(input) {
15101
- Ie(`${import_picocolors7.default.bold("VibeRaven Preview")} ${import_picocolors7.default.dim("local production rehearsal")}`);
15102
- M2.message(
15103
- `Preview stack: ${input.artifact.archetype} | Production core ${input.artifact.productionCorePercent}% | ${input.artifact.gaps.length} gaps`
15104
- );
15105
- const action = await ve({
15106
- message: "What do you want to see?",
15107
- options: [
15108
- { value: "agent-output", label: "Show Codex/Claude native UI", hint: "Checklist, provider links, and next step" },
15109
- { value: "real-app", label: "Run on my real app", hint: "Copy/use the agent command" },
15110
- { value: "open-report", label: "Open legacy HTML report", hint: "Optional old browser report" },
15111
- { value: "exit", label: "Exit" }
15112
- ]
15113
- });
15114
- if (pD(action) || action === "exit") {
15115
- Se(import_picocolors7.default.dim("Run npx -y viberaven preview anytime."));
15116
- return;
15117
- }
15118
- if (action === "open-report") {
15119
- await openPathInBrowser(input.reportPath);
15120
- Se(`Opened ${input.reportPath}`);
15121
- return;
15122
- }
15123
- if (action === "agent-output") {
15124
- const prp = generateProductionProtocol(input.artifact, {
15125
- mode: "demo",
15126
- connectedTools: input.connectedTools
15127
- });
15128
- const tasks = buildTaskList(input.artifact);
15129
- printActionPanelBlock(prp, tasks, currentRenderMode("demo"));
15130
- Se(import_picocolors7.default.dim("This is the output Codex/Claude/Cursor should reason over."));
15131
- return;
15132
- }
15133
- console.log("");
15134
- console.log("Run this inside Codex, Claude Code, Cursor, Windsurf, or Gemini CLI:");
15135
- console.log(" npx -y viberaven --agent-mode");
15136
- console.log("");
15137
- Se(import_picocolors7.default.dim("The native agent chat stays the main UX. Use report.html only as a legacy fallback."));
15138
- }
15139
- async function runDemo(options) {
15140
- const label2 = options.label ?? "demo";
15141
- const connectedTools = detectConnectedTools();
15142
- const fixtureRoot = options.fixtureRoot ?? bundledFixtureRoot();
15143
- const scannedArtifact = await scanDemoFixture(fixtureRoot);
15144
- const artifact = {
15145
- ...scannedArtifact,
15146
- workspacePath: options.cwd
15147
- };
15148
- const paths = await writeScanArtifacts({
15149
- artifact,
15150
- cwd: options.cwd,
15151
- connectedTools,
15152
- prpMode: "demo"
15153
- });
15154
- let reportOpened = false;
15155
- if (options.openReport) {
15156
- try {
15157
- await openPathInBrowser(paths.reportPath);
15158
- reportOpened = true;
15159
- } catch (error) {
15160
- console.warn(error instanceof Error ? error.message : String(error));
15161
- }
15162
- }
15163
- if (options.agentMode) {
15164
- console.log(
15165
- label2 === "demo" ? "VibeRaven demo mode - no login, no API spend. Provider verification is simulated." : isPreviewLabel(label2) ? "VibeRaven Preview - local production rehearsal. Provider verification is simulated." : "VibeRaven showcase run - no login, no API spend. Provider verification is simulated."
15166
- );
15167
- const prp = generateProductionProtocol(artifact, { mode: "demo", connectedTools });
15168
- const mode = currentRenderMode("demo");
15169
- const tasks = buildTaskList(artifact);
15170
- const celebration = renderClearCelebration(prp, mode);
15171
- printActionPanelBlock(prp, tasks, mode);
15172
- if (celebration) {
15173
- console.log(celebration);
15174
- }
15175
- } else if (isPreviewLabel(label2)) {
15176
- if (canUseInteractiveTryMenu(options)) {
15177
- await runInteractiveTryMenu({ artifact, reportPath: paths.reportPath, connectedTools });
15178
- } else {
15179
- printTryMenuSummary({ artifact, opened: reportOpened });
15180
- }
15181
- } else {
15182
- console.log(
15183
- label2 === "demo" ? "VibeRaven demo complete. See .viberaven/prp.json" : "VibeRaven showcase run complete. See .viberaven/prp.json"
15184
- );
15185
- }
15186
- return 0;
15187
- }
15188
-
15189
14308
  // src/cli.ts
15190
14309
  function printHelp() {
15191
14310
  console.log(`viberaven ${VERSION} \u2014 launch readiness for AI-built apps
@@ -15204,6 +14323,9 @@ Usage:
15204
14323
 
15205
14324
  viberaven status
15206
14325
 
14326
+ viberaven preview [--agent-mode] [--json]
14327
+ Local production rehearsal for videos and onboarding; no login or API spend
14328
+
15207
14329
  viberaven connect --session <id> --token <token> [--once] [--api-url <url>]
15208
14330
  Handshake, save runner session, then watch for jobs (Ctrl+C to stop)
15209
14331
 
@@ -15213,22 +14335,7 @@ Usage:
15213
14335
  viberaven scan [--open] [--json] [--api-url <url>] [path]
15214
14336
 
15215
14337
  viberaven --agent-mode [--json|--jsonl] [path]
15216
- Agent-first production flow for Codex, Claude Code, Cursor, or terminal agents
15217
-
15218
- viberaven --demo [path]
15219
- No-login demo scan over the bundled fixture; writes demo artifacts locally
15220
-
15221
- viberaven --agent-mode --demo [path]
15222
- Demo production flow; no login, no managed API spend
15223
-
15224
- viberaven preview [--agent-mode] [path]
15225
- Local production flow preview with native chat/terminal UX
15226
-
15227
- viberaven try [--agent-mode] [path]
15228
- Alias for preview
15229
-
15230
- viberaven --showcase --agent-mode [path]
15231
- Legacy alias for the local preview run
14338
+ Agent-first scan; writes tasklist, gate-result, context-map, and per-gap JSON
15232
14339
 
15233
14340
  viberaven --strict[=warning] [path]
15234
14341
  Fail when production gate is not clear; warning mode also fails on warnings
@@ -15237,7 +14344,7 @@ Usage:
15237
14344
  Refresh .viberaven/context-map.json from the last scan
15238
14345
 
15239
14346
  viberaven report [--open] [path]
15240
- Legacy: rebuild report.html from last scan (no new API scan)
14347
+ Rebuild report.html from last scan (no new API scan)
15241
14348
 
15242
14349
  viberaven prompt [--gap <id>] [--provider <key>] [--area <key>] [--no-copy]
15243
14350
 
@@ -15268,9 +14375,6 @@ Usage:
15268
14375
  viberaven audit --vercel-supabase [--json] [path]
15269
14376
  Local Vercel/Supabase repo evidence audit (RLS, pooler, secrets)
15270
14377
 
15271
- viberaven badge [--svg|--card] [path]
15272
- Print a README badge, terminal card, or write .viberaven/badge.svg from .viberaven/prp.json
15273
-
15274
14378
 
15275
14379
 
15276
14380
  Agent workflow (Claude Code / Codex):
@@ -15330,63 +14434,16 @@ function parseArgs(argv) {
15330
14434
  continue;
15331
14435
  }
15332
14436
  if (!command) {
15333
- if (isRootModePositional(flags, arg)) {
15334
- positional.push(arg);
15335
- } else {
15336
- command = arg;
15337
- }
14437
+ command = arg;
15338
14438
  } else {
15339
14439
  positional.push(arg);
15340
14440
  }
15341
14441
  }
15342
14442
  return { command, flags, positional };
15343
14443
  }
15344
- var KNOWN_COMMANDS = /* @__PURE__ */ new Set([
15345
- "audit",
15346
- "badge",
15347
- "connect",
15348
- "doctor",
15349
- "guide",
15350
- "init",
15351
- "interactive",
15352
- "login",
15353
- "logout",
15354
- "next",
15355
- "open",
15356
- "prompt",
15357
- "preview",
15358
- "provider-verify",
15359
- "report",
15360
- "scan",
15361
- "stack",
15362
- "status",
15363
- "tui",
15364
- "try",
15365
- "validate-npm-package",
15366
- "version",
15367
- "watch"
15368
- ]);
15369
- function isRootModePositional(flags, token) {
15370
- if (KNOWN_COMMANDS.has(token)) {
15371
- return false;
15372
- }
15373
- return [
15374
- "agent-mode",
15375
- "demo",
15376
- "showcase",
15377
- "json",
15378
- "jsonl",
15379
- "strict",
15380
- "condense",
15381
- "verify",
15382
- "heal"
15383
- ].some((key) => flags[key] !== void 0);
15384
- }
15385
14444
  function isBooleanFlag(command, key) {
15386
14445
  if ([
15387
14446
  "agent-mode",
15388
- "demo",
15389
- "showcase",
15390
14447
  "json",
15391
14448
  "jsonl",
15392
14449
  "condense",
@@ -15396,17 +14453,14 @@ function isBooleanFlag(command, key) {
15396
14453
  "apply",
15397
14454
  "yes",
15398
14455
  "no-verify",
15399
- "force-scan",
15400
- "ui"
14456
+ "force-scan"
15401
14457
  ].includes(key)) {
15402
14458
  return true;
15403
14459
  }
15404
14460
  if (key === "strict") return true;
15405
- if (key === "open" && (command === "" || command === "scan" || command === "report" || command === "preview" || command === "try")) return true;
14461
+ if (key === "open" && (command === "" || command === "scan" || command === "report")) return true;
15406
14462
  if (key === "verify" && command === "") return true;
15407
14463
  if (key === "vercel-supabase" && command === "audit") return true;
15408
- if (key === "svg" && command === "badge") return true;
15409
- if (key === "card" && command === "badge") return true;
15410
14464
  if (key === "json" && command === "validate-npm-package") return true;
15411
14465
  if (key === "dry-run" && command === "init") return true;
15412
14466
  if (key === "agents" && command === "doctor") return true;
@@ -15418,9 +14472,6 @@ function shouldConsumeLeadingHyphenValue(command, key, value) {
15418
14472
  function hasFlag(flags, key) {
15419
14473
  return flags[key] === true || typeof flags[key] === "string";
15420
14474
  }
15421
- function resolveCliPath(input) {
15422
- return input ? (0, import_node_path28.resolve)(process.cwd(), input) : process.cwd();
15423
- }
15424
14475
  async function guardEarlyVerifyScan(input) {
15425
14476
  if (input.flags["force-scan"] === true) {
15426
14477
  return void 0;
@@ -15429,7 +14480,7 @@ async function guardEarlyVerifyScan(input) {
15429
14480
  if (!verifyLike) {
15430
14481
  return void 0;
15431
14482
  }
15432
- const workspacePath = input.positional[0] ? (0, import_node_path28.join)(process.cwd(), input.positional[0]) : await resolveWorkspaceRoot(process.cwd());
14483
+ const workspacePath = input.positional[0] ? (0, import_node_path25.join)(process.cwd(), input.positional[0]) : await resolveWorkspaceRoot(process.cwd());
15433
14484
  const loopState = await loadLoopState(workspacePath);
15434
14485
  if (loopState.batchApplied <= 0) {
15435
14486
  return void 0;
@@ -15453,19 +14504,7 @@ async function guardEarlyVerifyScan(input) {
15453
14504
  return void 0;
15454
14505
  }
15455
14506
  const nextTask = remainingRepoCodeTasks[0];
15456
- const progress = renderLoopProgress(
15457
- {
15458
- applied: loopState.batchApplied,
15459
- total: batchSize,
15460
- label: "Healing repo gaps",
15461
- silentInPlain: true
15462
- },
15463
- currentRenderMode("human-command")
15464
- );
15465
14507
  console.error("SCAN_DEFERRED: Local heal batch is not full yet, so VibeRaven is protecting scan quota.");
15466
- if (progress) {
15467
- console.error(progress);
15468
- }
15469
14508
  console.error(`Batch progress: ${loopState.batchApplied}/${batchSize} local heals applied since the last scan.`);
15470
14509
  console.error(`Next local heal: npx -y viberaven --heal --apply --gap ${nextTask.gapId} --yes`);
15471
14510
  console.error("Run verify again after the batch is full, or add --force-scan if the user explicitly wants to spend a scan now.");
@@ -15490,8 +14529,8 @@ async function cmdLogout() {
15490
14529
  }
15491
14530
  async function cmdProviderVerify(flags, positional) {
15492
14531
  const provider2 = typeof flags.provider === "string" ? flags.provider : positional[0];
15493
- const check = typeof flags.check === "string" ? flags.check : positional[1];
15494
- if (!provider2 || !check) {
14532
+ const check2 = typeof flags.check === "string" ? flags.check : positional[1];
14533
+ if (!provider2 || !check2) {
15495
14534
  console.error("Usage: viberaven provider-verify --provider <supabase|vercel> --check <id> [--plan free|pro]");
15496
14535
  return 1;
15497
14536
  }
@@ -15504,7 +14543,7 @@ async function cmdProviderVerify(flags, positional) {
15504
14543
  }
15505
14544
  const result = await verifyProviderGap({
15506
14545
  provider: provider2,
15507
- check,
14546
+ check: check2,
15508
14547
  cwd: process.cwd(),
15509
14548
  plan
15510
14549
  });
@@ -15517,7 +14556,7 @@ async function cmdStatus(flags, positional) {
15517
14556
  console.log("Not signed in. Run: viberaven login");
15518
14557
  return 1;
15519
14558
  }
15520
- const startDir = positional[0] ? (0, import_node_path28.join)(process.cwd(), positional[0]) : process.cwd();
14559
+ const startDir = positional[0] ? (0, import_node_path25.join)(process.cwd(), positional[0]) : process.cwd();
15521
14560
  let artifact;
15522
14561
  try {
15523
14562
  artifact = await loadLastArtifact(startDir);
@@ -15671,7 +14710,7 @@ async function cmdWatch(flags) {
15671
14710
  }
15672
14711
  }
15673
14712
  async function runScanCommand(flags, positional, options) {
15674
- const workspacePath = positional[0] ? (0, import_node_path28.join)(process.cwd(), positional[0]) : await resolveWorkspaceRoot(process.cwd());
14713
+ const workspacePath = positional[0] ? (0, import_node_path25.join)(process.cwd(), positional[0]) : await resolveWorkspaceRoot(process.cwd());
15675
14714
  const apiBaseUrl = resolveApiBaseUrl(typeof flags["api-url"] === "string" ? flags["api-url"] : void 0);
15676
14715
  let accessToken;
15677
14716
  try {
@@ -15717,16 +14756,15 @@ async function runScanCommand(flags, positional, options) {
15717
14756
  return { exitCode: 1 };
15718
14757
  }
15719
14758
  const artifact = await enrichArtifactWithAccount(result.artifact, apiBaseUrl, accessToken);
15720
- const connectedTools = detectConnectedTools();
15721
- const paths = await writeScanArtifacts({ artifact, cwd: workspacePath, connectedTools });
14759
+ const paths = await writeScanArtifacts({ artifact, cwd: workspacePath });
15722
14760
  if (flags.json && !options?.deferMachineOutput) {
15723
14761
  console.log(formatScanJsonStdout(artifact));
15724
14762
  return { exitCode: 0, artifacts: paths };
15725
14763
  }
15726
- if (!options?.deferMachineOutput && !flags["agent-mode"]) {
14764
+ if (!options?.deferMachineOutput) {
15727
14765
  printScanSummary(artifact, paths);
15728
14766
  }
15729
- if (artifact.usage && !options?.deferMachineOutput && !flags["agent-mode"]) {
14767
+ if (artifact.usage && !options?.deferMachineOutput) {
15730
14768
  console.log(formatUsageLine(artifact.usage));
15731
14769
  }
15732
14770
  if (flags["agent-mode"] && !options?.deferMachineOutput) {
@@ -15734,16 +14772,6 @@ async function runScanCommand(flags, positional, options) {
15734
14772
  const openGapCount = artifact.gaps.length;
15735
14773
  const updatedState = resetBatch(loopState, openGapCount);
15736
14774
  const tasks = buildTaskList(artifact);
15737
- const prp = generateProductionProtocol(artifact, { connectedTools });
15738
- const mode = currentRenderMode("agent-mode");
15739
- const bootstrap = await maybeAutoInstallAgentRules({ cwd: workspacePath });
15740
- console.log(formatAgentRulesBootstrapSummary(bootstrap));
15741
- console.log("");
15742
- printActionPanelBlock(prp, tasks, mode);
15743
- const celebration = renderClearCelebration(prp, mode);
15744
- if (celebration) {
15745
- console.log(celebration);
15746
- }
15747
14775
  const plan = artifact.plan ?? "free";
15748
14776
  const block = buildNextActionBlock(tasks, updatedState, plan);
15749
14777
  printNextActionBlock(block);
@@ -15760,7 +14788,7 @@ async function runScanCommand(flags, positional, options) {
15760
14788
  return { exitCode: 0, artifacts: paths };
15761
14789
  }
15762
14790
  async function cmdReport(flags, positional) {
15763
- const startDir = positional[0] ? (0, import_node_path28.join)(process.cwd(), positional[0]) : process.cwd();
14791
+ const startDir = positional[0] ? (0, import_node_path25.join)(process.cwd(), positional[0]) : process.cwd();
15764
14792
  try {
15765
14793
  const paths = await refreshReportFromDisk(startDir);
15766
14794
  console.log(`Report refreshed: ${paths.reportPath}`);
@@ -15782,7 +14810,7 @@ async function cmdReport(flags, positional) {
15782
14810
  }
15783
14811
  }
15784
14812
  async function cmdPrompt(flags, positional) {
15785
- const startDir = positional[0] ? (0, import_node_path28.join)(process.cwd(), positional[0]) : process.cwd();
14813
+ const startDir = positional[0] ? (0, import_node_path25.join)(process.cwd(), positional[0]) : process.cwd();
15786
14814
  let artifact;
15787
14815
  try {
15788
14816
  artifact = await loadLastArtifact(startDir);
@@ -15790,26 +14818,26 @@ async function cmdPrompt(flags, positional) {
15790
14818
  console.error(error instanceof Error ? error.message : "No scan found. Run: viberaven scan");
15791
14819
  return 1;
15792
14820
  }
15793
- const gap2 = pickGap(artifact, {
14821
+ const gap = pickGap(artifact, {
15794
14822
  gapId: typeof flags.gap === "string" ? flags.gap : void 0,
15795
14823
  provider: typeof flags.provider === "string" ? flags.provider : void 0,
15796
14824
  area: typeof flags.area === "string" ? flags.area : void 0
15797
14825
  });
15798
- if (!gap2) {
14826
+ if (!gap) {
15799
14827
  console.error("No matching gap. Run `viberaven scan` or pass --gap <id>.");
15800
14828
  return 1;
15801
14829
  }
15802
14830
  const skipCopy = flags["no-copy"] === true;
15803
14831
  if (!skipCopy) {
15804
14832
  try {
15805
- await copyToClipboard(gap2.copyPrompt);
15806
- console.log(`Copied to clipboard: ${gap2.title}`);
14833
+ await copyToClipboard(gap.copyPrompt);
14834
+ console.log(`Copied to clipboard: ${gap.title}`);
15807
14835
  return 0;
15808
14836
  } catch (error) {
15809
14837
  console.warn(error instanceof Error ? error.message : String(error));
15810
14838
  }
15811
14839
  }
15812
- console.log(gap2.copyPrompt);
14840
+ console.log(gap.copyPrompt);
15813
14841
  return 0;
15814
14842
  }
15815
14843
  var STACK_AREAS = /* @__PURE__ */ new Set(["database", "auth", "payments", "deployment", "monitoring", "security"]);
@@ -15880,7 +14908,7 @@ async function main() {
15880
14908
  const wantsJsonl = hasFlag(flags, "jsonl");
15881
14909
  const wantsStrict = hasFlag(flags, "strict");
15882
14910
  if (flags.condense) {
15883
- const cwd = positional[0] ? (0, import_node_path28.join)(process.cwd(), positional[0]) : process.cwd();
14911
+ const cwd = positional[0] ? (0, import_node_path25.join)(process.cwd(), positional[0]) : process.cwd();
15884
14912
  const result = await runCondenseCommand({ cwd });
15885
14913
  console.log(`VibeRaven context map refreshed: ${result.contextMapPath}`);
15886
14914
  return 0;
@@ -15898,19 +14926,6 @@ async function main() {
15898
14926
  console.log(JSON.stringify(result, null, 2));
15899
14927
  return result.status.startsWith("refused") || result.status === "failed" ? 1 : 0;
15900
14928
  }
15901
- if (command === "preview" || command === "try") {
15902
- const cwd = resolveCliPath(positional[0]);
15903
- return runDemo({
15904
- cwd,
15905
- agentMode: isAgentMode,
15906
- label: command === "preview" ? "preview" : "try",
15907
- openReport: flags.open === true || flags.ui === true
15908
- });
15909
- }
15910
- if (hasFlag(flags, "demo") || hasFlag(flags, "showcase")) {
15911
- const cwd = resolveCliPath(positional[0]);
15912
- return runDemo({ cwd, agentMode: isAgentMode, label: hasFlag(flags, "showcase") ? "showcase" : "demo" });
15913
- }
15914
14929
  if (!command && (isAgentMode || flags.verify === true || wantsJson || wantsJsonl || wantsStrict)) {
15915
14930
  const guardedExitCode = await guardEarlyVerifyScan({ flags, positional, wantsStrict });
15916
14931
  if (guardedExitCode !== void 0) {
@@ -15922,7 +14937,7 @@ async function main() {
15922
14937
  console.error("VibeRaven could not produce machine output because scan artifacts were not written.");
15923
14938
  return 3;
15924
14939
  }
15925
- const gateResult = scanResult.artifacts && (wantsJson || wantsJsonl || wantsStrict) ? JSON.parse(await (0, import_promises21.readFile)(scanResult.artifacts.gateResultPath, "utf8")) : void 0;
14940
+ const gateResult = scanResult.artifacts && (wantsJson || wantsJsonl || wantsStrict) ? JSON.parse(await (0, import_promises19.readFile)(scanResult.artifacts.gateResultPath, "utf8")) : void 0;
15926
14941
  const strictExitCode = wantsStrict && gateResult ? exitCodeForStrictGate(gateResult, { failOnWarnings: flags.strict === "warning" }) : scanResult.exitCode;
15927
14942
  if (wantsJson && gateResult) {
15928
14943
  process.stdout.write(renderGateResultJson(gateResult));
@@ -15954,10 +14969,16 @@ async function main() {
15954
14969
  return 0;
15955
14970
  case "status":
15956
14971
  return cmdStatus(flags, positional);
14972
+ case "preview":
14973
+ return runPreviewCommand({
14974
+ cwd: positional[0] ? (0, import_node_path25.join)(process.cwd(), positional[0]) : process.cwd(),
14975
+ agentMode: flags["agent-mode"] === true,
14976
+ json: Boolean(flags.json)
14977
+ });
15957
14978
  case "next":
15958
14979
  return runNextCommand({
15959
14980
  json: Boolean(flags.json),
15960
- cwd: positional[0] ? (0, import_node_path28.join)(process.cwd(), positional[0]) : process.cwd()
14981
+ cwd: positional[0] ? (0, import_node_path25.join)(process.cwd(), positional[0]) : process.cwd()
15961
14982
  });
15962
14983
  case "guide": {
15963
14984
  const provider2 = positional[0];
@@ -15995,7 +15016,7 @@ async function main() {
15995
15016
  case "provider-verify":
15996
15017
  return cmdProviderVerify(flags, positional);
15997
15018
  case "init": {
15998
- const cwd = positional[0] ? (0, import_node_path28.join)(process.cwd(), positional[0]) : process.cwd();
15019
+ const cwd = positional[0] ? (0, import_node_path25.join)(process.cwd(), positional[0]) : process.cwd();
15999
15020
  const agents = typeof flags.agents === "string" ? flags.agents : void 0;
16000
15021
  return runInitCommand({
16001
15022
  cwd,
@@ -16009,7 +15030,7 @@ async function main() {
16009
15030
  return 1;
16010
15031
  }
16011
15032
  return runDoctorAgentsCommand({
16012
- cwd: positional[0] ? (0, import_node_path28.join)(process.cwd(), positional[0]) : process.cwd()
15033
+ cwd: positional[0] ? (0, import_node_path25.join)(process.cwd(), positional[0]) : process.cwd()
16013
15034
  });
16014
15035
  case "validate-npm-package":
16015
15036
  return runValidateNpmPackageCommand({
@@ -16022,15 +15043,9 @@ async function main() {
16022
15043
  return 1;
16023
15044
  }
16024
15045
  return runAuditCommand({
16025
- cwd: positional[0] ? (0, import_node_path28.join)(process.cwd(), positional[0]) : process.cwd(),
15046
+ cwd: positional[0] ? (0, import_node_path25.join)(process.cwd(), positional[0]) : process.cwd(),
16026
15047
  json: Boolean(flags.json)
16027
15048
  });
16028
- case "badge":
16029
- return runBadgeCommand({
16030
- cwd: positional[0] ? (0, import_node_path28.join)(process.cwd(), positional[0]) : process.cwd(),
16031
- svg: flags.svg === true,
16032
- card: flags.card === true
16033
- });
16034
15049
  default:
16035
15050
  console.error(`Unknown command: ${command}`);
16036
15051
  printHelp();