@viberaven/cli 1.1.3 → 1.1.5

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,
@@ -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)];
@@ -5342,7 +5339,7 @@ ${scan.files.map((file) => file.path).join("\n")}`.replace(/\\/g, "/").toLowerCa
5342
5339
  ];
5343
5340
  const passedCount = items.filter((entry) => entry.status === "passed").length;
5344
5341
  const totalCount = items.length;
5345
- const readinessPercent2 = Math.round(passedCount / Math.max(totalCount, 1) * 100);
5342
+ const readinessPercent = Math.round(passedCount / Math.max(totalCount, 1) * 100);
5346
5343
  return {
5347
5344
  key: "supabase-database",
5348
5345
  provider: "supabase",
@@ -5353,7 +5350,7 @@ ${scan.files.map((file) => file.path).join("\n")}`.replace(/\\/g, "/").toLowerCa
5353
5350
  items,
5354
5351
  passedCount,
5355
5352
  totalCount,
5356
- readinessPercent: readinessPercent2
5353
+ readinessPercent
5357
5354
  };
5358
5355
  }
5359
5356
  function visibleFiles2(scan) {
@@ -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
  }
@@ -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(check) {
8317
+ if (CHECK_TO_PLAYBOOK[check.id]) {
8318
+ return CHECK_TO_PLAYBOOK[check.id];
8319
+ }
8320
+ const providerKey = check.providerKey.toLowerCase();
8321
+ if (providerKey.includes("vercel") || check.area === "deployment") {
8322
+ return "vercel";
8323
+ }
8324
+ if (providerKey.includes("stripe") || check.area === "payments") {
8325
+ return "stripe";
8326
+ }
8327
+ if (providerKey.includes("supabase") && check.area === "auth") {
8328
+ return "auth-supabase";
8329
+ }
8330
+ if (providerKey.includes("supabase") || check.area === "database") {
8331
+ return "supabase";
8332
+ }
8333
+ if (check.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(check) {
8475
+ return check.evidenceClass === "manual-dashboard" || check.evidenceClass === "mcp-verifier" || check.evidenceSource === "provider" || check.evidenceSource === "mcp" || check.status === "needs-connection" || check.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 check of mission.checks) {
8482
+ if (!isManualProviderCheck(check)) {
8483
+ continue;
8484
+ }
8485
+ refs.push({
8486
+ areaLabel: area.label,
8487
+ providerLabel: mission.providerLabel,
8488
+ check,
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
@@ -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,563 +8556,123 @@ 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";
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}`
8593
+ };
8594
+ }
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;
8633
8604
  }
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"
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
8642
8613
  };
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;
8614
+ }
8615
+ const lockedGap = sortGapsByPriority(artifact.gaps).find(
8616
+ (gap) => !unlocked.has(gap.primaryMapCategory)
8617
+ );
8618
+ if (lockedGap) {
8619
+ const lane = lockedGap.primaryMapCategory;
8620
+ return {
8621
+ type: "upgrade",
8622
+ title: lockedGap.title,
8623
+ detail: `Free plan covers 6/12 mission map lanes. Upgrade to fix ${LOCKED_LANE_LABELS[lane] ?? lane}.`,
8624
+ lockedLane: lane,
8625
+ upgradeUrl: UPGRADE_URL
8626
+ };
8627
+ }
8628
+ const lockedManual = collectManualChecks(artifact).find((item3) => !unlocked.has(item3.mapCategory));
8629
+ if (lockedManual) {
8630
+ const lane = lockedManual.mapCategory;
8631
+ return {
8632
+ type: "upgrade",
8633
+ title: lockedManual.check.label,
8634
+ detail: `This check is in the ${LOCKED_LANE_LABELS[lane] ?? lane} lane (Pro).`,
8635
+ lockedLane: lane,
8636
+ upgradeUrl: UPGRADE_URL
8637
+ };
8638
+ }
8639
+ if (unlocked.size < 12) {
8640
+ return {
8641
+ type: "done",
8642
+ title: "No blockers in unlocked lanes",
8643
+ detail: "Run scan again after changes. Upgrade for deployment, monitoring, security, testing, and onboarding lanes.",
8644
+ upgradeUrl: UPGRADE_URL
8645
+ };
8646
+ }
8647
+ return {
8648
+ type: "done",
8649
+ title: "No blockers found",
8650
+ detail: "Re-scan after changes to confirm production readiness."
8651
+ };
8652
+ }
8653
+
8654
+ // src/report/agentSummary.ts
8655
+ var UPGRADE_URL2 = "https://viberaven.dev/pricing";
8656
+ var LOCKED_LANE_KEYS = [
8657
+ "deployment",
8658
+ "monitoring",
8659
+ "security",
8660
+ "testing",
8661
+ "landing",
8662
+ "errorHandling"
8663
+ ];
8664
+ var SEVERITY_ORDER = {
8665
+ critical: 0,
8666
+ warning: 1,
8667
+ info: 2
8668
+ };
8669
+ function sortGaps(gaps) {
8670
+ return [...gaps].sort((a, b3) => {
8671
+ const sev = SEVERITY_ORDER[a.severity] - SEVERITY_ORDER[b3.severity];
8672
+ if (sev !== 0) {
8673
+ return sev;
8664
8674
  }
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
- }
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}\``);
8705
- }
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]");
8728
- }
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)
9034
- );
9035
- if (lockedGap) {
9036
- const lane = lockedGap.primaryMapCategory;
9037
- return {
9038
- type: "upgrade",
9039
- title: lockedGap.title,
9040
- detail: `Free plan covers 6/12 mission map lanes. Upgrade to fix ${LOCKED_LANE_LABELS[lane] ?? lane}.`,
9041
- lockedLane: lane,
9042
- upgradeUrl: UPGRADE_URL
9043
- };
9044
- }
9045
- const lockedManual = collectManualChecks(artifact).find((item3) => !unlocked.has(item3.mapCategory));
9046
- if (lockedManual) {
9047
- const lane = lockedManual.mapCategory;
9048
- return {
9049
- type: "upgrade",
9050
- title: lockedManual.check.label,
9051
- detail: `This check is in the ${LOCKED_LANE_LABELS[lane] ?? lane} lane (Pro).`,
9052
- lockedLane: lane,
9053
- upgradeUrl: UPGRADE_URL
9054
- };
9055
- }
9056
- if (unlocked.size < 12) {
9057
- return {
9058
- type: "done",
9059
- title: "No blockers in unlocked lanes",
9060
- detail: "Run scan again after changes. Upgrade for deployment, monitoring, security, testing, and onboarding lanes.",
9061
- upgradeUrl: UPGRADE_URL
9062
- };
9063
- }
9064
- return {
9065
- type: "done",
9066
- title: "No blockers found",
9067
- detail: "Re-scan after changes to confirm production readiness."
9068
- };
9069
- }
9070
-
9071
- // src/report/agentSummary.ts
9072
- var UPGRADE_URL2 = "https://viberaven.dev/pricing";
9073
- var LOCKED_LANE_KEYS = [
9074
- "deployment",
9075
- "monitoring",
9076
- "security",
9077
- "testing",
9078
- "landing",
9079
- "errorHandling"
9080
- ];
9081
- var SEVERITY_ORDER = {
9082
- critical: 0,
9083
- warning: 1,
9084
- info: 2
9085
- };
9086
- function sortGaps(gaps) {
9087
- return [...gaps].sort((a, b3) => {
9088
- const sev = SEVERITY_ORDER[a.severity] - SEVERITY_ORDER[b3.severity];
9089
- if (sev !== 0) {
9090
- return sev;
9091
- }
9092
- return a.title.localeCompare(b3.title);
8675
+ return a.title.localeCompare(b3.title);
9093
8676
  });
9094
8677
  }
9095
8678
  function generateAgentSummary(artifact) {
@@ -9154,14 +8737,14 @@ 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
  }
@@ -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
  }
@@ -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.3";
11232
+ var VERSION = "1.1.5";
11458
11233
 
11459
11234
  // src/commands/guide.ts
11460
11235
  var import_picocolors3 = __toESM(require_picocolors());
@@ -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,6 +11549,8 @@ 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
@@ -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,9 +12078,9 @@ function formatAgentRulesInitSummary(output) {
12291
12078
  );
12292
12079
  return lines.join("\n");
12293
12080
  }
12294
- async function readExistingFile2(path3) {
12081
+ async function readExistingFile2(path) {
12295
12082
  try {
12296
- 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") };
12297
12084
  } catch (error) {
12298
12085
  if (isFileNotFoundError2(error)) {
12299
12086
  return { exists: false, content: "" };
@@ -12461,15 +12248,15 @@ async function handleViewGaps(cwd) {
12461
12248
  async function handlePrompt(cwd) {
12462
12249
  try {
12463
12250
  const artifact = await loadLastArtifact(cwd);
12464
- const gap2 = pickGap(artifact);
12465
- if (!gap2) {
12251
+ const gap = pickGap(artifact);
12252
+ if (!gap) {
12466
12253
  M2.warn("No gaps to fix. Run a scan or pick a different project.");
12467
12254
  return;
12468
12255
  }
12469
- const prompt = buildAgentFixPrompt(artifact, gap2);
12256
+ const prompt = buildAgentFixPrompt(artifact, gap);
12470
12257
  try {
12471
12258
  await copyToClipboard(prompt);
12472
- 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}`));
12473
12260
  } catch (error) {
12474
12261
  M2.warn(error instanceof Error ? error.message : String(error));
12475
12262
  console.log("");
@@ -13880,8 +13667,8 @@ async function checkAgentInjection(cwd) {
13880
13667
  }
13881
13668
  for (const target of CORE_AGENT_INJECTION_TARGETS) {
13882
13669
  const file = target === "cursor" ? ".cursor/rules/viberaven-core.mdc" : AGENT_RULE_TARGETS[target].file;
13883
- const path3 = (0, import_node_path23.join)(cwd, file);
13884
- const exists = await fileExists2(path3);
13670
+ const path = (0, import_node_path23.join)(cwd, file);
13671
+ const exists = await fileExists2(path);
13885
13672
  if (!exists) {
13886
13673
  checks.push({
13887
13674
  id: `canonical-${target}`,
@@ -13890,7 +13677,7 @@ async function checkAgentInjection(cwd) {
13890
13677
  });
13891
13678
  continue;
13892
13679
  }
13893
- const content = await (0, import_promises18.readFile)(path3, "utf-8");
13680
+ const content = await (0, import_promises18.readFile)(path, "utf-8");
13894
13681
  const hasCommand = content.includes(PUBLIC_AGENT_MODE_COMMAND);
13895
13682
  checks.push({
13896
13683
  id: `canonical-${target}`,
@@ -13922,9 +13709,9 @@ function formatDoctorAgentsReport(report) {
13922
13709
  lines.push(report.ok ? "All agent injection checks passed." : "Agent injection checks failed.");
13923
13710
  return lines.join("\n");
13924
13711
  }
13925
- async function fileExists2(path3) {
13712
+ async function fileExists2(path) {
13926
13713
  try {
13927
- await (0, import_promises18.access)(path3);
13714
+ await (0, import_promises18.access)(path);
13928
13715
  return true;
13929
13716
  } catch {
13930
13717
  return false;
@@ -14114,241 +13901,27 @@ async function runValidateNpmPackageCommand(options) {
14114
13901
  for (const result of results) {
14115
13902
  console.log(`${result.name}: ${result.verdict}`);
14116
13903
  for (const reason of result.reasons) {
14117
- console.log(` - ${reason}`);
14118
- }
14119
- console.log(` followUp: ${result.followUpCommand}`);
14120
- }
14121
- }
14122
- const hasBlocking = results.some((result) => result.verdict !== "ok");
14123
- return hasBlocking ? 2 : 0;
14124
- }
14125
-
14126
- // src/commands/runAudit.ts
14127
- async function runAuditCommand(input) {
14128
- const auditInput = await collectVercelSupabaseAuditInput(input.cwd);
14129
- const result = buildVercelSupabaseAudit(auditInput);
14130
- if (input.json) {
14131
- process.stdout.write(`${JSON.stringify(result, null, 2)}
14132
- `);
14133
- } else {
14134
- process.stdout.write(`${renderVercelSupabaseAudit(result)}
14135
- `);
14136
- }
14137
- return result.status === "pass" ? 0 : 1;
14138
- }
14139
-
14140
- // src/commands/badge.ts
14141
- var import_promises19 = require("node:fs/promises");
14142
- var import_node_path24 = require("node:path");
14143
-
14144
- // src/brand/palette.ts
14145
- var import_picocolors5 = __toESM(require_picocolors());
14146
- var STATUS_GLYPH = {
14147
- clear: "\u2713",
14148
- warning: "\u25C8",
14149
- blocked: "\u25C6",
14150
- fixable: "\u25B8",
14151
- manual: "\u25C7",
14152
- done: "\u2713",
14153
- pending: "\xB7"
14154
- };
14155
- var DECISION_GLYPH = {
14156
- CLEAR: STATUS_GLYPH.clear,
14157
- WARNING: STATUS_GLYPH.warning,
14158
- BLOCKED: STATUS_GLYPH.blocked
14159
- };
14160
- function identity(text) {
14161
- return text;
14162
- }
14163
- function colorsFor(mode) {
14164
- return import_picocolors5.default.createColors(mode === "rich");
14165
- }
14166
- function decisionColor(decision, mode) {
14167
- const colors = colorsFor(mode);
14168
- if (decision === "CLEAR") return colors.green;
14169
- if (decision === "WARNING") return colors.yellow;
14170
- return colors.red;
14171
- }
14172
- function decisionStyle(decision, mode) {
14173
- const colorize = mode === "rich" ? decisionColor(decision, mode) : identity;
14174
- return {
14175
- glyph: DECISION_GLYPH[decision],
14176
- label: colorize(decision),
14177
- colorize
14178
- };
14179
- }
14180
- function richOrPlain(text, mode, colorize) {
14181
- return mode === "rich" ? colorize(text) : text;
14182
- }
14183
- var accent = {
14184
- brand(text, mode) {
14185
- return richOrPlain(text, mode, colorsFor(mode).cyan);
14186
- },
14187
- dim(text, mode) {
14188
- return richOrPlain(text, mode, colorsFor(mode).dim);
14189
- },
14190
- bold(text, mode) {
14191
- return richOrPlain(text, mode, colorsFor(mode).bold);
14192
- }
14193
- };
14194
-
14195
- // src/brand/ui.ts
14196
- var import_picocolors6 = __toESM(require_picocolors());
14197
- var ANSI_PATTERN = /\x1b\[[0-9;]*m/g;
14198
- function colorsFor2(mode) {
14199
- return import_picocolors6.default.createColors(mode === "rich");
14200
- }
14201
- function stripAnsi(value) {
14202
- return value.replace(ANSI_PATTERN, "");
14203
- }
14204
- function visibleWidth(value) {
14205
- return stripAnsi(value).length;
14206
- }
14207
- function padVisible(value, width) {
14208
- return `${value}${" ".repeat(Math.max(0, width - visibleWidth(value)))}`;
14209
- }
14210
- function safeWidth(width) {
14211
- if (!Number.isFinite(width)) return 1;
14212
- return Math.max(1, Math.floor(width));
14213
- }
14214
- function clampPercent(percent) {
14215
- if (Number.isNaN(percent)) return 0;
14216
- return Math.max(0, Math.min(100, percent));
14217
- }
14218
- function colorizeGaugeFill(text, percent, mode) {
14219
- const colors = colorsFor2(mode);
14220
- if (mode !== "rich") return text;
14221
- if (percent >= 80) return colors.green(text);
14222
- if (percent >= 50) return colors.yellow(text);
14223
- return colors.red(text);
14224
- }
14225
- function rule(width, options) {
14226
- const line = "\u2500".repeat(safeWidth(width));
14227
- return accent.dim(line, options.mode);
14228
- }
14229
- function box(lines, options) {
14230
- const content = lines.length > 0 ? lines : [""];
14231
- const innerWidth = Math.max(1, ...content.map(visibleWidth));
14232
- const horizontal = "\u2500".repeat(innerWidth + 2);
14233
- const colors = colorsFor2(options.mode);
14234
- const border = (value) => options.mode === "rich" ? colors.cyan(value) : value;
14235
- const body = content.map((line) => border("\u2502") + ` ${padVisible(line, innerWidth)} ` + border("\u2502"));
14236
- return [border(`\u250C${horizontal}\u2510`), ...body, border(`\u2514${horizontal}\u2518`)].join("\n");
14237
- }
14238
- function gauge(percent, options) {
14239
- const width = safeWidth(options.width);
14240
- const clamped = clampPercent(percent);
14241
- const label2 = `${Math.round(clamped)}%`;
14242
- const filledWidth = Math.round(clamped / 100 * width);
14243
- const emptyWidth = width - filledWidth;
14244
- const filled = colorizeGaugeFill("\u2588".repeat(filledWidth), clamped, options.mode);
14245
- const empty = accent.dim("\u2591".repeat(emptyWidth), options.mode);
14246
- return `[${filled}${empty}] ${label2}`;
14247
- }
14248
- function banner(decision, options) {
14249
- const style = decisionStyle(decision, options.mode);
14250
- return `${style.glyph} ${style.label}`;
14251
- }
14252
- function kv(key, value, options) {
14253
- return `${accent.dim(key, options.mode)}: ${value}`;
14254
- }
14255
-
14256
- // src/brand/wordmark.ts
14257
- var DEFAULT_TAGLINE = "production operator for AI-built apps";
14258
- function wordmark(options) {
14259
- const tagline = options.tagline ?? DEFAULT_TAGLINE;
14260
- const brand = accent.brand("VibeRaven", options.mode);
14261
- const mutedTagline = accent.dim(tagline, options.mode);
14262
- if (options.variant === "compact") {
14263
- return `${brand} - ${mutedTagline}`;
14264
- }
14265
- return [accent.bold(brand, options.mode), mutedTagline].join("\n");
14266
- }
14267
-
14268
- // src/badge/renderBadge.ts
14269
- var COLORS = {
14270
- CLEAR: "#2ea44f",
14271
- WARNING: "#dbab09",
14272
- BLOCKED: "#d1242f"
14273
- };
14274
- function label(prp) {
14275
- return prp.decision.toLowerCase();
14276
- }
14277
- function renderBadgeSvg(prp) {
14278
- const text = label(prp);
14279
- const color = COLORS[prp.decision];
14280
- const width = 132 + text.length * 7;
14281
- return `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="20" role="img" aria-label="VibeRaven: ${text}">
14282
- <rect width="${width}" height="20" fill="#24292f"/>
14283
- <rect x="86" width="${width - 86}" height="20" fill="${color}"/>
14284
- <g fill="#fff" font-family="Verdana,Geneva,sans-serif" font-size="11">
14285
- <text x="8" y="14">VibeRaven</text>
14286
- <text x="94" y="14">${text}</text>
14287
- </g>
14288
- </svg>`;
14289
- }
14290
- function renderBadgeMarkdown(prp) {
14291
- const text = label(prp);
14292
- const date = prp.generatedAt.slice(0, 10);
14293
- return `[![VibeRaven launch gate: ${text}](.viberaven/badge.svg)](https://viberaven.dev/production-protocol.md?utm_source=badge) \`gate: ${text} - ${date}\``;
14294
- }
14295
- function renderBadgeCard(prp, mode) {
14296
- const text = label(prp);
14297
- const date = prp.generatedAt.slice(0, 10);
14298
- const style = decisionStyle(prp.decision, mode);
14299
- return box(
14300
- [
14301
- wordmark({ variant: "compact", mode, tagline: "launch gate proof" }),
14302
- `${accent.dim("gate", mode)}: ${style.colorize(text)}`,
14303
- `${accent.dim("date", mode)}: ${date}`,
14304
- accent.brand("viberaven.dev", mode)
14305
- ],
14306
- { mode }
14307
- );
14308
- }
14309
-
14310
- // src/brand/renderMode.ts
14311
- function resolveRenderMode(input) {
14312
- if (input.env.VIBERAVEN_FORCE_RICH === "1") return "rich";
14313
- if (input.env.VIBERAVEN_PLAIN === "1") return "plain";
14314
- if (input.env.NO_COLOR !== void 0 && input.env.NO_COLOR !== "") return "plain";
14315
- if (input.surface === "agent-mode") return "plain";
14316
- return input.isTTY ? "rich" : "plain";
14317
- }
14318
- function currentRenderMode(surface) {
14319
- return resolveRenderMode({
14320
- surface,
14321
- isTTY: Boolean(process.stdout.isTTY),
14322
- env: process.env
14323
- });
13904
+ console.log(` - ${reason}`);
13905
+ }
13906
+ console.log(` followUp: ${result.followUpCommand}`);
13907
+ }
13908
+ }
13909
+ const hasBlocking = results.some((result) => result.verdict !== "ok");
13910
+ return hasBlocking ? 2 : 0;
14324
13911
  }
14325
13912
 
14326
- // src/commands/badge.ts
14327
- async function runBadgeCommand(options) {
14328
- const log = options.log ?? ((message) => console.log(message));
14329
- const artifactDir = (0, import_node_path24.join)(options.cwd, ".viberaven");
14330
- const prpPath = (0, import_node_path24.join)(artifactDir, "prp.json");
14331
- let prp;
14332
- try {
14333
- prp = JSON.parse(await (0, import_promises19.readFile)(prpPath, "utf8"));
14334
- } catch {
14335
- log("No .viberaven/prp.json found. Run: npx -y viberaven --agent-mode");
14336
- return 1;
14337
- }
14338
- if (options.svg) {
14339
- const outputPath = (0, import_node_path24.join)(artifactDir, "badge.svg");
14340
- await (0, import_promises19.mkdir)(artifactDir, { recursive: true });
14341
- await (0, import_promises19.writeFile)(outputPath, renderBadgeSvg(prp), "utf8");
14342
- log(`Wrote ${outputPath}`);
14343
- return 0;
14344
- }
14345
- if (options.card) {
14346
- log(renderBadgeCard(prp, currentRenderMode("human-command")));
14347
- return 0;
13913
+ // src/commands/runAudit.ts
13914
+ async function runAuditCommand(input) {
13915
+ const auditInput = await collectVercelSupabaseAuditInput(input.cwd);
13916
+ const result = buildVercelSupabaseAudit(auditInput);
13917
+ if (input.json) {
13918
+ process.stdout.write(`${JSON.stringify(result, null, 2)}
13919
+ `);
13920
+ } else {
13921
+ process.stdout.write(`${renderVercelSupabaseAudit(result)}
13922
+ `);
14348
13923
  }
14349
- log("Add this to your README:");
14350
- log(renderBadgeMarkdown(prp));
14351
- return 0;
13924
+ return result.status === "pass" ? 0 : 1;
14352
13925
  }
14353
13926
 
14354
13927
  // src/output/nextActionBlock.ts
@@ -14365,7 +13938,11 @@ function buildNextActionBlock(tasks, loopState, plan) {
14365
13938
  return {
14366
13939
  ...base,
14367
13940
  type: "stalled",
14368
- title: "Loop stalled \u2014 no gap reduction after 2+ consecutive scans",
13941
+ title: "Loop stalled - no gap reduction after 2+ consecutive scans",
13942
+ 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.",
13943
+ executionPlan: [
13944
+ { kind: "stop", note: "Do not keep spending scans while the loop is stalled." }
13945
+ ],
14369
13946
  requiresUserAction: true,
14370
13947
  stallReason
14371
13948
  };
@@ -14374,42 +13951,67 @@ function buildNextActionBlock(tasks, loopState, plan) {
14374
13951
  return {
14375
13952
  ...base,
14376
13953
  type: "verify",
14377
- title: "Batch complete \u2014 run verify to rescan before next batch",
13954
+ title: "Batch complete - run verify to rescan before next batch",
13955
+ 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.",
13956
+ executionPlan: [
13957
+ { kind: "verify", command: "viberaven_verify" },
13958
+ { kind: "continue-loop", note: "After verify, continue until the gate is clear or a provider/user blocker remains." }
13959
+ ],
14378
13960
  mcpTool: "viberaven_verify",
14379
13961
  mcpArgs: {},
14380
13962
  requiresUserAction: false
14381
13963
  };
14382
13964
  }
14383
- const repoCodeTask = tasks.find((t) => t.fixType === "repo-code" && !t.requiresUserAction);
13965
+ const repoCodeTask = tasks.find((task) => task.fixType === "repo-code" && !task.requiresUserAction);
14384
13966
  if (repoCodeTask) {
13967
+ const fallbackCommand = repoCodeTask.mcpArgs ? `npx -y viberaven --heal --apply --gap ${repoCodeTask.gapId} --yes` : void 0;
14385
13968
  return {
14386
13969
  ...base,
14387
13970
  type: "repo-code",
14388
13971
  gapId: repoCodeTask.gapId,
14389
13972
  title: repoCodeTask.title,
13973
+ 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.",
13974
+ executionPlan: [
13975
+ {
13976
+ kind: "apply-repo-fix",
13977
+ command: repoCodeTask.mcpTool ?? fallbackCommand,
13978
+ note: "Patch only this gap and preserve unrelated user changes."
13979
+ },
13980
+ { kind: "continue-loop", note: "Apply remaining safe repo fixes until the batch is full, then verify once." }
13981
+ ],
14390
13982
  mcpTool: repoCodeTask.mcpTool,
14391
13983
  mcpArgs: repoCodeTask.mcpArgs,
14392
- fallbackCommand: repoCodeTask.mcpArgs ? `npx -y viberaven --heal --apply --gap ${repoCodeTask.gapId} --yes` : void 0,
13984
+ fallbackCommand,
14393
13985
  requiresUserAction: false
14394
13986
  };
14395
13987
  }
14396
- const providerTask = tasks.find((t) => t.fixType === "provider-action");
13988
+ const providerTask = tasks.find((task) => task.fixType === "provider-action");
14397
13989
  if (providerTask) {
14398
13990
  return {
14399
13991
  ...base,
14400
13992
  type: "provider-action",
14401
13993
  gapId: providerTask.gapId,
14402
13994
  title: providerTask.title,
13995
+ 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.",
13996
+ executionPlan: [
13997
+ { kind: "open-provider-step", note: "Use VIBERAVEN_PROVIDER_ACTION for the exact dashboard step and URL." },
13998
+ { kind: "wait-for-user", note: "Wait for confirmation or read-only MCP evidence before verifying." }
13999
+ ],
14403
14000
  requiresUserAction: true
14404
14001
  };
14405
14002
  }
14406
- const upgradeTask = tasks.find((t) => t.fixType === "upgrade-required");
14003
+ const upgradeTask = tasks.find((task) => task.fixType === "upgrade-required");
14407
14004
  if (upgradeTask) {
14408
14005
  return {
14409
14006
  ...base,
14410
14007
  type: "upgrade-required",
14411
14008
  gapId: upgradeTask.gapId,
14412
14009
  title: upgradeTask.title,
14010
+ 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.",
14011
+ executionPlan: [
14012
+ { kind: "upgrade", command: "https://viberaven.dev/pricing" },
14013
+ { kind: "stop", note: "Do not pretend locked lanes or provider state were fixed locally." }
14014
+ ],
14413
14015
  requiresUserAction: true,
14414
14016
  upgradeUrl: "https://viberaven.dev/pricing"
14415
14017
  };
@@ -14417,15 +14019,20 @@ function buildNextActionBlock(tasks, loopState, plan) {
14417
14019
  return {
14418
14020
  ...base,
14419
14021
  type: "done",
14420
- title: "All gaps resolved \u2014 production gate is clear",
14022
+ title: "All gaps resolved - production gate is clear",
14023
+ agentInstruction: "The repo-code gate is clear. Run strict before deploy and still verify provider dashboard state through MCP or the provider dashboard.",
14024
+ executionPlan: [
14025
+ { kind: "verify", command: "npx -y viberaven --strict" },
14026
+ { kind: "stop", note: "Do not deploy until provider dashboard state is verified where applicable." }
14027
+ ],
14421
14028
  requiresUserAction: false
14422
14029
  };
14423
14030
  }
14424
14031
  function resolveStallReason(tasks) {
14425
14032
  if (tasks.length === 0) return "no-recipes";
14426
- const allUpgradeOrEmpty = tasks.every((t) => t.fixType === "upgrade-required");
14033
+ const allUpgradeOrEmpty = tasks.every((task) => task.fixType === "upgrade-required");
14427
14034
  if (allUpgradeOrEmpty) return "no-recipes";
14428
- const allProviderAction = tasks.every((t) => t.fixType === "provider-action");
14035
+ const allProviderAction = tasks.every((task) => task.fixType === "provider-action");
14429
14036
  if (allProviderAction) return "provider-action-required";
14430
14037
  return "unknown";
14431
14038
  }
@@ -14445,6 +14052,7 @@ function buildProviderActionBlock(task) {
14445
14052
  dashboardUrl: pa.dashboardUrl,
14446
14053
  exactStep: pa.exactStep,
14447
14054
  envKeyName: pa.envKeyName ?? null,
14055
+ envKeyExample: pa.envKeyExample ?? null,
14448
14056
  doneSignal: pa.doneSignal,
14449
14057
  verifyCommand: task.verifyCommand,
14450
14058
  mcpAlternative: task.mcpTool ?? pa.mcpAlternative ?? null
@@ -14453,7 +14061,7 @@ function buildProviderActionBlock(task) {
14453
14061
  }
14454
14062
  function printProviderActionBlock(tasks) {
14455
14063
  const task = tasks.find(
14456
- (t) => t.fixType === "provider-action" && t.requiresUserAction === true
14064
+ (entry) => entry.fixType === "provider-action" && entry.requiresUserAction === true
14457
14065
  );
14458
14066
  if (!task) return;
14459
14067
  const block = buildProviderActionBlock(task);
@@ -14468,251 +14076,10 @@ function printNextActionBlock(block) {
14468
14076
  console.log(NEXT_ACTION_END);
14469
14077
  }
14470
14078
 
14471
- // src/output/actionPanelBlock.ts
14472
- var ACTION_PANEL_START = "VIBERAVEN_ACTION_PANEL_START";
14473
- var ACTION_PANEL_END = "VIBERAVEN_ACTION_PANEL_END";
14474
- function buildActionPanelBlock(reportPath) {
14475
- return {
14476
- VIBERAVEN_ACTION_PANEL: {
14477
- surface: "local-report",
14478
- title: "Visual action panel",
14479
- reportPath,
14480
- openCommand: "npx -y viberaven report --open",
14481
- nextCommand: "npx -y viberaven next --json",
14482
- agentInstruction: "Use the native chat for reasoning. Open this local report only when the user needs buttons, provider links, copy actions, or visual progress."
14483
- }
14484
- };
14485
- }
14486
- function printActionPanelBlock(reportPath) {
14487
- const block = buildActionPanelBlock(reportPath);
14488
- console.log(ACTION_PANEL_START);
14489
- console.log(JSON.stringify(block, null, 2));
14490
- console.log(ACTION_PANEL_END);
14491
- }
14492
-
14493
- // src/output/operatorBlock.ts
14494
- var OPERATOR_START = "VIBERAVEN_OPERATOR_START";
14495
- var OPERATOR_END = "VIBERAVEN_OPERATOR_END";
14496
- function stackLine(prp) {
14497
- const parts = Array.from(
14498
- /* @__PURE__ */ new Set([
14499
- ...prp.detectedStack.deployment,
14500
- ...prp.detectedStack.database,
14501
- ...prp.detectedStack.auth,
14502
- ...prp.detectedStack.payments,
14503
- ...prp.detectedStack.monitoring
14504
- ])
14505
- );
14506
- if (parts.length > 0) {
14507
- return parts.join(" + ");
14508
- }
14509
- return prp.detectedStack.archetype ?? "unknown";
14510
- }
14511
- function uniqueEnvNames(tasks) {
14512
- return Array.from(
14513
- new Set(
14514
- tasks.map((task) => task.providerAction?.envKeyName).filter((name) => Boolean(name))
14515
- )
14516
- );
14517
- }
14518
- function humanTaskText(task) {
14519
- if (task.fixType === "provider-action") {
14520
- return task.providerAction?.exactStep ?? task.title;
14521
- }
14522
- if (task.fixType === "upgrade-required") {
14523
- return `Upgrade required: ${task.action ?? task.exactFix ?? task.title}`;
14524
- }
14525
- if (task.fixType === "manual-verify") {
14526
- return `Manual verification required: ${task.action ?? task.exactFix ?? task.title}`;
14527
- }
14528
- return task.title;
14529
- }
14530
- function riskSuffixFor(task, prp) {
14531
- const topRisk = prp.findings.find((finding) => finding.id === task.gapId)?.riskMapping?.owaspLlm?.[0];
14532
- return topRisk ? ` (risk: ${topRisk})` : "";
14533
- }
14534
- function readinessPercent(prp) {
14535
- if (prp.decision === "CLEAR") return 100;
14536
- if (prp.decision === "WARNING") return 65;
14537
- return 25;
14538
- }
14539
- function buildPlainOperatorBlock(prp, tasks) {
14540
- const repoTasks = tasks.filter((task) => task.fixType === "repo-code");
14541
- const humanTasks = tasks.filter((task) => task.fixType !== "repo-code");
14542
- const providerTasks = tasks.filter((task) => task.fixType === "provider-action");
14543
- const nextRepoTask = repoTasks[0];
14544
- const envNames = uniqueEnvNames(providerTasks);
14545
- const lines = [];
14546
- lines.push(`Decision: ${prp.decision}`);
14547
- lines.push(`Detected stack: ${stackLine(prp)}`);
14548
- lines.push("");
14549
- lines.push("What I can fix:");
14550
- if (repoTasks.length === 0) {
14551
- lines.push(" (none - no automated repo-code recipes apply)");
14552
- } else {
14553
- repoTasks.forEach((task, index) => {
14554
- lines.push(` ${index + 1}. ${task.title}${riskSuffixFor(task, prp)}`);
14555
- });
14556
- }
14557
- lines.push("");
14558
- lines.push("What you must do:");
14559
- if (humanTasks.length === 0) {
14560
- lines.push(" (none - no human-owned steps required)");
14561
- } else {
14562
- humanTasks.forEach((task, index) => {
14563
- lines.push(` ${index + 1}. ${humanTaskText(task)}${riskSuffixFor(task, prp)}`);
14564
- });
14565
- }
14566
- lines.push("");
14567
- lines.push("Next repo-code fix:");
14568
- lines.push(
14569
- nextRepoTask ? ` ${nextRepoTask.title}${riskSuffixFor(nextRepoTask, prp)} -> npx -y viberaven --heal --apply --gap ${nextRepoTask.gapId} --yes` : " (none)"
14570
- );
14571
- lines.push("");
14572
- lines.push("Provider actions:");
14573
- if (providerTasks.length === 0) {
14574
- lines.push(" (none)");
14575
- } else {
14576
- providerTasks.forEach((task) => {
14577
- const providerAction = task.providerAction;
14578
- if (!providerAction) {
14579
- lines.push(` - ${task.title}${riskSuffixFor(task, prp)}`);
14580
- return;
14581
- }
14582
- lines.push(` - ${providerAction.provider}: ${providerAction.dashboardUrl}`);
14583
- lines.push(` Step: ${providerAction.exactStep}${riskSuffixFor(task, prp)}`);
14584
- lines.push(` Done when: ${providerAction.doneSignal}`);
14585
- });
14586
- }
14587
- lines.push("");
14588
- lines.push("Copy these env var names:");
14589
- lines.push(envNames.length > 0 ? ` ${envNames.join(", ")}` : " (none)");
14590
- lines.push("");
14591
- lines.push(`Verify command: ${prp.verifyCommand}`);
14592
- lines.push("");
14593
- lines.push(`Do not deploy until: decision is CLEAR (current: ${prp.decision}). Run the verify command after fixes.`);
14594
- if (prp.decision === "CLEAR") {
14595
- lines.push("Share your launch proof: npx -y viberaven badge");
14596
- }
14597
- return lines.join("\n");
14598
- }
14599
- function buildRichOperatorBlock(prp, tasks) {
14600
- const repoTasks = tasks.filter((task) => task.fixType === "repo-code");
14601
- const humanTasks = tasks.filter((task) => task.fixType !== "repo-code");
14602
- const providerTasks = tasks.filter((task) => task.fixType === "provider-action");
14603
- const nextRepoTask = repoTasks[0];
14604
- const envNames = uniqueEnvNames(providerTasks);
14605
- const mode = "rich";
14606
- const lines = [];
14607
- lines.push(wordmark({ variant: "compact", mode }));
14608
- lines.push(rule(64, { mode }));
14609
- lines.push(
14610
- `${accent.bold("Decision", mode)}: ${banner(prp.decision, { mode })} ${gauge(readinessPercent(prp), {
14611
- width: 18,
14612
- mode
14613
- })}`
14614
- );
14615
- lines.push(kv("Detected stack", stackLine(prp), { mode }));
14616
- lines.push("");
14617
- lines.push(`${accent.bold("What I can fix", mode)}:`);
14618
- if (repoTasks.length === 0) {
14619
- lines.push(` ${accent.dim("(none - no automated repo-code recipes apply)", mode)}`);
14620
- } else {
14621
- repoTasks.forEach((task, index) => {
14622
- lines.push(` ${index + 1}. ${task.title}${riskSuffixFor(task, prp)}`);
14623
- });
14624
- }
14625
- lines.push("");
14626
- lines.push(`${accent.bold("What you must do", mode)}:`);
14627
- if (humanTasks.length === 0) {
14628
- lines.push(` ${accent.dim("(none - no human-owned steps required)", mode)}`);
14629
- } else {
14630
- humanTasks.forEach((task, index) => {
14631
- lines.push(` ${index + 1}. ${humanTaskText(task)}${riskSuffixFor(task, prp)}`);
14632
- });
14633
- }
14634
- lines.push("");
14635
- lines.push(`${accent.bold("Next repo-code fix", mode)}:`);
14636
- lines.push(
14637
- nextRepoTask ? ` ${nextRepoTask.title}${riskSuffixFor(nextRepoTask, prp)} -> npx -y viberaven --heal --apply --gap ${nextRepoTask.gapId} --yes` : ` ${accent.dim("(none)", mode)}`
14638
- );
14639
- lines.push("");
14640
- lines.push(`${accent.bold("Provider actions", mode)}:`);
14641
- if (providerTasks.length === 0) {
14642
- lines.push(` ${accent.dim("(none)", mode)}`);
14643
- } else {
14644
- providerTasks.forEach((task) => {
14645
- const providerAction = task.providerAction;
14646
- if (!providerAction) {
14647
- lines.push(` - ${task.title}${riskSuffixFor(task, prp)}`);
14648
- return;
14649
- }
14650
- lines.push(` - ${accent.brand(providerAction.provider, mode)}: ${providerAction.dashboardUrl}`);
14651
- lines.push(` ${kv("Step", `${providerAction.exactStep}${riskSuffixFor(task, prp)}`, { mode })}`);
14652
- lines.push(` ${kv("Done when", providerAction.doneSignal, { mode })}`);
14653
- });
14654
- }
14655
- lines.push("");
14656
- lines.push(`${accent.bold("Copy these env var names", mode)}:`);
14657
- lines.push(envNames.length > 0 ? ` ${envNames.join(", ")}` : ` ${accent.dim("(none)", mode)}`);
14658
- lines.push("");
14659
- lines.push(kv("Verify command", prp.verifyCommand, { mode }));
14660
- lines.push("");
14661
- lines.push(
14662
- `${accent.bold("Do not deploy until", mode)}: decision is CLEAR (current: ${banner(
14663
- prp.decision,
14664
- { mode }
14665
- )}). Run the verify command after fixes.`
14666
- );
14667
- if (prp.decision === "CLEAR") {
14668
- lines.push(kv("Share your launch proof", "npx -y viberaven badge", { mode }));
14669
- }
14670
- return lines.join("\n");
14671
- }
14672
- function buildOperatorBlock(prp, tasks, mode = "plain") {
14673
- return mode === "rich" ? buildRichOperatorBlock(prp, tasks) : buildPlainOperatorBlock(prp, tasks);
14674
- }
14675
- function printOperatorBlock(prp, tasks, mode = "plain") {
14676
- if (mode === "rich") {
14677
- console.log(buildOperatorBlock(prp, tasks, "rich"));
14678
- }
14679
- console.log(OPERATOR_START);
14680
- console.log(buildOperatorBlock(prp, tasks, "plain"));
14681
- console.log(OPERATOR_END);
14682
- }
14683
-
14684
- // src/output/celebration.ts
14685
- function renderClearCelebration(prp, mode) {
14686
- if (prp.decision !== "CLEAR") return "";
14687
- const verifiedDate = prp.generatedAt.slice(0, 10);
14688
- const style = decisionStyle("CLEAR", mode);
14689
- const lines = [
14690
- `${style.glyph} ${style.label} production gate is clear`,
14691
- accent.dim(`Verified ${verifiedDate}`, mode),
14692
- "",
14693
- `Share your launch proof: ${accent.bold("npx -y viberaven badge", mode)}`
14694
- ];
14695
- return [wordmark({ variant: "compact", mode }), box(lines, { mode }), rule(40, { mode })].join("\n");
14696
- }
14697
-
14698
- // src/output/loopProgress.ts
14699
- function safeCount(value) {
14700
- if (!Number.isFinite(value)) return 0;
14701
- return Math.max(0, Math.floor(value));
14702
- }
14703
- function renderLoopProgress(input, mode) {
14704
- if (mode === "plain" && input.silentInPlain) return "";
14705
- const total = safeCount(input.total);
14706
- const applied = total > 0 ? Math.min(safeCount(input.applied), total) : safeCount(input.applied);
14707
- const percent = total === 0 ? 100 : applied / total * 100;
14708
- const label2 = `${STATUS_GLYPH.fixable} ${input.label}`;
14709
- return `${accent.bold(label2, mode)} ${gauge(percent, { width: 12, mode })} ${applied}/${total}`;
14710
- }
14711
-
14712
14079
  // src/providerMcpBridge.ts
14713
14080
  var import_node_fs12 = require("node:fs");
14714
14081
  var import_node_os2 = require("node:os");
14715
- var import_node_path25 = require("node:path");
14082
+ var import_node_path24 = require("node:path");
14716
14083
  var UPGRADE_URL4 = "https://viberaven.dev/pricing";
14717
14084
  var FALLBACK_COMMAND = "npx -y viberaven audit --vercel-supabase --json";
14718
14085
  var SUPPORTED_PROVIDERS = /* @__PURE__ */ new Set(["supabase", "vercel"]);
@@ -14720,9 +14087,9 @@ var configPathsOverride;
14720
14087
  function defaultMcpConfigPaths() {
14721
14088
  const home = (0, import_node_os2.homedir)();
14722
14089
  return [
14723
- (0, import_node_path25.join)(home, ".config", "claude", "claude_desktop_config.json"),
14724
- (0, import_node_path25.join)(home, ".cursor", "mcp.json"),
14725
- (0, import_node_path25.join)(home, ".gemini", "antigravity", "mcp_config.json")
14090
+ (0, import_node_path24.join)(home, ".config", "claude", "claude_desktop_config.json"),
14091
+ (0, import_node_path24.join)(home, ".cursor", "mcp.json"),
14092
+ (0, import_node_path24.join)(home, ".gemini", "antigravity", "mcp_config.json")
14726
14093
  ];
14727
14094
  }
14728
14095
  function resolveConfigPaths() {
@@ -14750,12 +14117,12 @@ function findServerEntry(servers, provider2) {
14750
14117
  }
14751
14118
  function findProviderMcpConfig(provider2, configPaths) {
14752
14119
  const paths = configPaths ?? resolveConfigPaths();
14753
- for (const path3 of paths) {
14754
- if (!(0, import_node_fs12.existsSync)(path3)) {
14120
+ for (const path of paths) {
14121
+ if (!(0, import_node_fs12.existsSync)(path)) {
14755
14122
  continue;
14756
14123
  }
14757
14124
  try {
14758
- const raw = JSON.parse((0, import_node_fs12.readFileSync)(path3, "utf8"));
14125
+ const raw = JSON.parse((0, import_node_fs12.readFileSync)(path, "utf8"));
14759
14126
  const servers = parseMcpServers(raw);
14760
14127
  if (!servers) {
14761
14128
  continue;
@@ -14769,7 +14136,7 @@ function findProviderMcpConfig(provider2, configPaths) {
14769
14136
  command: typeof server.command === "string" ? server.command : void 0,
14770
14137
  args: Array.isArray(server.args) ? server.args.filter((arg) => typeof arg === "string") : void 0,
14771
14138
  url: typeof server.url === "string" ? server.url : void 0,
14772
- source: path3
14139
+ source: path
14773
14140
  };
14774
14141
  } catch {
14775
14142
  continue;
@@ -14777,19 +14144,6 @@ function findProviderMcpConfig(provider2, configPaths) {
14777
14144
  }
14778
14145
  return void 0;
14779
14146
  }
14780
- function hasUsableProviderMcpConfig(config) {
14781
- return Boolean(config.command?.trim() || config.url?.trim());
14782
- }
14783
- function findUsableProviderMcpConfig(provider2, configPaths) {
14784
- const paths = configPaths ?? resolveConfigPaths();
14785
- for (const path3 of paths) {
14786
- const config = findProviderMcpConfig(provider2, [path3]);
14787
- if (config && hasUsableProviderMcpConfig(config)) {
14788
- return config;
14789
- }
14790
- }
14791
- return void 0;
14792
- }
14793
14147
  async function verifyProviderGap(options) {
14794
14148
  if (options.plan !== "pro") {
14795
14149
  return {
@@ -14820,338 +14174,6 @@ async function verifyProviderGap(options) {
14820
14174
  };
14821
14175
  }
14822
14176
 
14823
- // src/connectedTools.ts
14824
- var DETECTED_PROVIDERS = ["supabase", "vercel", "stripe", "github"];
14825
- var INSTALL_HINTS = {
14826
- supabase: "claude mcp add --transport http supabase https://mcp.supabase.com/",
14827
- vercel: "claude mcp add --transport http vercel https://mcp.vercel.com/",
14828
- stripe: "claude mcp add --transport http stripe https://mcp.stripe.com/",
14829
- github: "claude mcp add --transport http github https://api.githubcopilot.com/mcp/"
14830
- };
14831
- var READONLY_PROVIDERS = /* @__PURE__ */ new Set(["vercel"]);
14832
- function isDetectedProvider(provider2) {
14833
- return DETECTED_PROVIDERS.includes(provider2);
14834
- }
14835
- function installHintFor(provider2) {
14836
- const normalizedProvider = provider2.toLowerCase().trim();
14837
- if (isDetectedProvider(normalizedProvider)) {
14838
- return INSTALL_HINTS[normalizedProvider];
14839
- }
14840
- return `Add the ${provider2} MCP server to your agent config.`;
14841
- }
14842
- function detectConnectedTools() {
14843
- const tools = {};
14844
- for (const provider2 of DETECTED_PROVIDERS) {
14845
- const config = findUsableProviderMcpConfig(provider2);
14846
- if (!config) {
14847
- tools[`${provider2}Mcp`] = "missing";
14848
- continue;
14849
- }
14850
- tools[`${provider2}Mcp`] = READONLY_PROVIDERS.has(provider2) ? "available_readonly" : "available";
14851
- }
14852
- tools.browser = "available";
14853
- return tools;
14854
- }
14855
-
14856
- // src/demo/runDemo.ts
14857
- var import_node_fs13 = require("node:fs");
14858
- var import_node_path27 = __toESM(require("node:path"));
14859
-
14860
- // src/demo/localRules.ts
14861
- var import_promises20 = require("node:fs/promises");
14862
- var import_node_path26 = __toESM(require("node:path"));
14863
- var DEMO_SCANNED_AT = "2026-06-14T00:00:00.000Z";
14864
- async function readKnownFile(root, relativePath) {
14865
- try {
14866
- return await (0, import_promises20.readFile)(import_node_path26.default.join(root, relativePath), "utf8");
14867
- } catch (error) {
14868
- const code = error.code;
14869
- if (code === "ENOENT") return "";
14870
- throw error;
14871
- }
14872
- }
14873
- function dependencyNames(packageJson) {
14874
- if (!packageJson.trim()) return /* @__PURE__ */ new Set();
14875
- const parsed = JSON.parse(packageJson);
14876
- return /* @__PURE__ */ new Set([
14877
- ...Object.keys(parsed.dependencies ?? {}),
14878
- ...Object.keys(parsed.devDependencies ?? {})
14879
- ]);
14880
- }
14881
- function stripTypeScriptComments(source) {
14882
- return source.replace(/\/\*[\s\S]*?\*\//g, "").split("\n").map((line) => {
14883
- let quote = null;
14884
- let escaped = false;
14885
- for (let index = 0; index < line.length - 1; index += 1) {
14886
- const char = line[index];
14887
- const next = line[index + 1];
14888
- if (quote) {
14889
- if (escaped) {
14890
- escaped = false;
14891
- } else if (char === "\\") {
14892
- escaped = true;
14893
- } else if (char === quote) {
14894
- quote = null;
14895
- }
14896
- continue;
14897
- }
14898
- if (char === '"' || char === "'" || char === "`") {
14899
- quote = char;
14900
- continue;
14901
- }
14902
- if (char === "/" && next === "/") {
14903
- return line.slice(0, index);
14904
- }
14905
- }
14906
- return line;
14907
- }).join("\n");
14908
- }
14909
- function stripSqlComments(source) {
14910
- return source.replace(/\/\*[\s\S]*?\*\//g, "").replace(/--.*$/gm, "");
14911
- }
14912
- function hasStripeWebhookSignatureVerification(source) {
14913
- const executable = stripTypeScriptComments(source);
14914
- return /webhooks\s*\.\s*constructEvent\s*\(/.test(executable) && /Stripe-Signature/.test(executable);
14915
- }
14916
- function hasEnabledRowLevelSecurity(source) {
14917
- return /enable\s+row\s+level\s+security/i.test(stripSqlComments(source));
14918
- }
14919
- function hasEnvAssignment(source, name) {
14920
- const pattern = new RegExp(`^\\s*${name}\\s*=`, "m");
14921
- return pattern.test(source);
14922
- }
14923
- function gap(input) {
14924
- return {
14925
- ...input,
14926
- toolSuggestions: [],
14927
- mcpSuggestion: null,
14928
- affectedMapCategories: input.affectedMapCategories ?? []
14929
- };
14930
- }
14931
- function emptyMissionGraph() {
14932
- return {
14933
- areas: [],
14934
- byArea: {},
14935
- byProvider: {},
14936
- repositoryEvidence: {
14937
- env: [],
14938
- security: []
14939
- }
14940
- };
14941
- }
14942
- async function scanDemoFixture(root) {
14943
- const [packageJson, stripeWebhook, migration, envExample] = await Promise.all([
14944
- readKnownFile(root, "package.json"),
14945
- readKnownFile(root, "app/api/stripe/webhook/route.ts"),
14946
- readKnownFile(root, "supabase/migrations/0001_init.sql"),
14947
- readKnownFile(root, ".env.example")
14948
- ]);
14949
- const dependencies = dependencyNames(packageJson);
14950
- const hasNext = dependencies.has("next");
14951
- const hasSupabase = dependencies.has("@supabase/supabase-js");
14952
- const hasStripe = dependencies.has("stripe");
14953
- const selectedProviders = {};
14954
- if (hasNext) selectedProviders.deployment = "vercel";
14955
- if (hasSupabase) {
14956
- selectedProviders.database = "supabase";
14957
- selectedProviders.auth = "supabase";
14958
- }
14959
- if (hasStripe) selectedProviders.payments = "stripe";
14960
- const gaps = [];
14961
- if (hasStripe && !hasStripeWebhookSignatureVerification(stripeWebhook)) {
14962
- gaps.push(
14963
- gap({
14964
- id: "stripe_webhook_signature_missing",
14965
- category: "SECURITY & AUTH",
14966
- severity: "critical",
14967
- title: "Verify Stripe webhook signatures",
14968
- detail: "The demo Stripe webhook handler does not verify the Stripe-Signature header with stripe.webhooks.constructEvent.",
14969
- 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.",
14970
- primaryMapCategory: "payments",
14971
- affectedMapCategories: ["security"]
14972
- })
14973
- );
14974
- }
14975
- if (hasSupabase && !hasEnabledRowLevelSecurity(migration)) {
14976
- gaps.push(
14977
- gap({
14978
- id: "rls_disabled",
14979
- category: "DATABASE & DATA",
14980
- severity: "critical",
14981
- title: "Enable Supabase row level security",
14982
- detail: "The demo migration creates application data without enabling row level security.",
14983
- copyPrompt: "Add alter table statements that enable row level security and add least-privilege policies for the Supabase tables in the demo migration.",
14984
- primaryMapCategory: "database",
14985
- affectedMapCategories: ["auth", "security"]
14986
- })
14987
- );
14988
- }
14989
- if (hasStripe && !hasEnvAssignment(envExample, "STRIPE_WEBHOOK_SECRET")) {
14990
- gaps.push(
14991
- gap({
14992
- id: "missing_prod_env_stripe_webhook_secret",
14993
- category: "DEPLOYMENT",
14994
- severity: "warning",
14995
- title: "Document STRIPE_WEBHOOK_SECRET",
14996
- detail: "The demo .env.example does not include a STRIPE_WEBHOOK_SECRET assignment.",
14997
- 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.",
14998
- primaryMapCategory: "deployment",
14999
- affectedMapCategories: ["payments"]
15000
- })
15001
- );
15002
- }
15003
- const hasCriticalGaps = gaps.some((item3) => item3.severity === "critical");
15004
- return {
15005
- version: 1,
15006
- scannedAt: DEMO_SCANNED_AT,
15007
- workspacePath: root,
15008
- score: hasCriticalGaps ? 55 : 90,
15009
- scoreLabel: hasCriticalGaps ? "Needs work" : "Looks solid",
15010
- summary: `Demo scan: ${gaps.length} launch gap(s) detected by local rules.`,
15011
- archetype: hasNext && hasSupabase && hasStripe ? "nextjs-supabase-stripe" : "demo-saas",
15012
- gaps,
15013
- missionGraph: emptyMissionGraph(),
15014
- stackWiring: { items: [], byKey: {} },
15015
- providerRegistry: {
15016
- version: 1,
15017
- source: "bundled",
15018
- generatedAt: DEMO_SCANNED_AT,
15019
- staleAfterDays: 30,
15020
- status: "fresh",
15021
- providers: []
15022
- },
15023
- verificationSummary: { byArea: {} },
15024
- productionCorePercent: hasCriticalGaps ? 40 : 95,
15025
- selectedProviders
15026
- };
15027
- }
15028
-
15029
- // src/demo/runDemo.ts
15030
- var import_picocolors7 = __toESM(require_picocolors());
15031
- function bundledFixtureRoot() {
15032
- const candidates = [
15033
- import_node_path27.default.join(__dirname, "..", "fixtures", "demo-saas"),
15034
- import_node_path27.default.join(__dirname, "..", "..", "fixtures", "demo-saas")
15035
- ];
15036
- return candidates.find((candidate) => (0, import_node_fs13.existsSync)(import_node_path27.default.join(candidate, "package.json"))) ?? candidates[0];
15037
- }
15038
- function canUseInteractiveTryMenu(options) {
15039
- return options.label === "try" && !options.agentMode && !options.openReport && process.stdin.isTTY === true && process.stdout.isTTY === true && process.env.CI !== "true";
15040
- }
15041
- function printTryMenuSummary(input) {
15042
- const providerStack = Array.from(new Set(Object.values(input.artifact.selectedProviders ?? {}))).filter(Boolean).join(" + ");
15043
- console.log("");
15044
- console.log("VibeRaven Try");
15045
- console.log("No login. No OpenAI key. Local fixture only.");
15046
- console.log("");
15047
- console.log(`Demo stack: ${input.artifact.archetype}${providerStack ? ` (${providerStack})` : ""}`);
15048
- console.log(
15049
- `Decision: BLOCKED | Production core: ${input.artifact.productionCorePercent}% | Gaps: ${input.artifact.gaps.length}`
15050
- );
15051
- console.log("");
15052
- console.log("Menu:");
15053
- console.log(" 1. Open visual report");
15054
- console.log(" npx -y viberaven try --open");
15055
- console.log(" 2. Show Codex/Claude agent output");
15056
- console.log(" npx -y viberaven try --agent-mode");
15057
- console.log(" 3. Run VibeRaven on your real app");
15058
- console.log(" npx -y viberaven --agent-mode");
15059
- console.log("");
15060
- console.log(`Local UI: ${input.reportPath}`);
15061
- if (input.opened) {
15062
- console.log("Opened visual report in your browser.");
15063
- }
15064
- console.log("");
15065
- }
15066
- async function runInteractiveTryMenu(input) {
15067
- Ie(`${import_picocolors7.default.bold("VibeRaven Try")} ${import_picocolors7.default.dim("local, no login, no API spend")}`);
15068
- M2.message(
15069
- `Demo stack: ${input.artifact.archetype} | Production core ${input.artifact.productionCorePercent}% | ${input.artifact.gaps.length} gaps`
15070
- );
15071
- const action = await ve({
15072
- message: "What do you want to see?",
15073
- options: [
15074
- { value: "open-report", label: "Open visual report", hint: "Browser action panel" },
15075
- { value: "agent-output", label: "Show Codex/Claude output", hint: "Native chat transcript" },
15076
- { value: "real-app", label: "Run on my real app", hint: "Copy/use the agent command" },
15077
- { value: "exit", label: "Exit" }
15078
- ]
15079
- });
15080
- if (pD(action) || action === "exit") {
15081
- Se(import_picocolors7.default.dim("Run npx -y viberaven try anytime."));
15082
- return;
15083
- }
15084
- if (action === "open-report") {
15085
- await openPathInBrowser(input.reportPath);
15086
- Se(`Opened ${input.reportPath}`);
15087
- return;
15088
- }
15089
- if (action === "agent-output") {
15090
- const prp = generateProductionProtocol(input.artifact, {
15091
- mode: "demo",
15092
- connectedTools: input.connectedTools
15093
- });
15094
- printOperatorBlock(prp, buildTaskList(input.artifact), currentRenderMode("demo"));
15095
- printActionPanelBlock(input.reportPath);
15096
- Se(import_picocolors7.default.dim("This is the output Codex/Claude/Cursor should reason over."));
15097
- return;
15098
- }
15099
- console.log("");
15100
- console.log("Run this inside Codex, Claude Code, Cursor, Windsurf, or Gemini CLI:");
15101
- console.log(" npx -y viberaven --agent-mode");
15102
- console.log("");
15103
- Se(import_picocolors7.default.dim("The native agent chat stays the main UX; the report is the visual action panel."));
15104
- }
15105
- async function runDemo(options) {
15106
- const label2 = options.label ?? "demo";
15107
- const connectedTools = detectConnectedTools();
15108
- const fixtureRoot = options.fixtureRoot ?? bundledFixtureRoot();
15109
- const scannedArtifact = await scanDemoFixture(fixtureRoot);
15110
- const artifact = {
15111
- ...scannedArtifact,
15112
- workspacePath: options.cwd
15113
- };
15114
- const paths = await writeScanArtifacts({
15115
- artifact,
15116
- cwd: options.cwd,
15117
- connectedTools,
15118
- prpMode: "demo"
15119
- });
15120
- let reportOpened = false;
15121
- if (options.openReport) {
15122
- try {
15123
- await openPathInBrowser(paths.reportPath);
15124
- reportOpened = true;
15125
- } catch (error) {
15126
- console.warn(error instanceof Error ? error.message : String(error));
15127
- }
15128
- }
15129
- if (options.agentMode) {
15130
- console.log(
15131
- label2 === "demo" ? "VibeRaven demo mode - no login, no API spend. Provider verification is simulated." : `VibeRaven ${label2 === "try" ? "Try" : "showcase run"} - no login, no API spend. Provider verification is simulated.`
15132
- );
15133
- const prp = generateProductionProtocol(artifact, { mode: "demo", connectedTools });
15134
- const mode = currentRenderMode("demo");
15135
- printOperatorBlock(prp, buildTaskList(artifact), mode);
15136
- const celebration = renderClearCelebration(prp, mode);
15137
- if (celebration) {
15138
- console.log(celebration);
15139
- }
15140
- printActionPanelBlock(paths.reportPath);
15141
- } else if (label2 === "try") {
15142
- if (canUseInteractiveTryMenu(options)) {
15143
- await runInteractiveTryMenu({ artifact, reportPath: paths.reportPath, connectedTools });
15144
- } else {
15145
- printTryMenuSummary({ artifact, reportPath: paths.reportPath, opened: reportOpened });
15146
- }
15147
- } else {
15148
- console.log(
15149
- label2 === "demo" ? "VibeRaven demo complete. See .viberaven/prp.json" : "VibeRaven showcase run complete. See .viberaven/prp.json"
15150
- );
15151
- }
15152
- return 0;
15153
- }
15154
-
15155
14177
  // src/cli.ts
15156
14178
  function printHelp() {
15157
14179
  console.log(`viberaven ${VERSION} \u2014 launch readiness for AI-built apps
@@ -15181,18 +14203,6 @@ Usage:
15181
14203
  viberaven --agent-mode [--json|--jsonl] [path]
15182
14204
  Agent-first scan; writes tasklist, gate-result, context-map, and per-gap JSON
15183
14205
 
15184
- viberaven --demo [path]
15185
- No-login demo scan over the bundled fixture; writes demo artifacts locally
15186
-
15187
- viberaven --agent-mode --demo [path]
15188
- Demo scan with operator output; no login, no managed API spend
15189
-
15190
- viberaven try [--open|--ui] [path]
15191
- Free no-login local try run with a simple menu and visual report
15192
-
15193
- viberaven --showcase --agent-mode [path]
15194
- Alias for the no-login local try run
15195
-
15196
14206
  viberaven --strict[=warning] [path]
15197
14207
  Fail when production gate is not clear; warning mode also fails on warnings
15198
14208
 
@@ -15231,9 +14241,6 @@ Usage:
15231
14241
  viberaven audit --vercel-supabase [--json] [path]
15232
14242
  Local Vercel/Supabase repo evidence audit (RLS, pooler, secrets)
15233
14243
 
15234
- viberaven badge [--svg|--card] [path]
15235
- Print a README badge, terminal card, or write .viberaven/badge.svg from .viberaven/prp.json
15236
-
15237
14244
 
15238
14245
 
15239
14246
  Agent workflow (Claude Code / Codex):
@@ -15293,62 +14300,16 @@ function parseArgs(argv) {
15293
14300
  continue;
15294
14301
  }
15295
14302
  if (!command) {
15296
- if (isRootModePositional(flags, arg)) {
15297
- positional.push(arg);
15298
- } else {
15299
- command = arg;
15300
- }
14303
+ command = arg;
15301
14304
  } else {
15302
14305
  positional.push(arg);
15303
14306
  }
15304
14307
  }
15305
14308
  return { command, flags, positional };
15306
14309
  }
15307
- var KNOWN_COMMANDS = /* @__PURE__ */ new Set([
15308
- "audit",
15309
- "badge",
15310
- "connect",
15311
- "doctor",
15312
- "guide",
15313
- "init",
15314
- "interactive",
15315
- "login",
15316
- "logout",
15317
- "next",
15318
- "open",
15319
- "prompt",
15320
- "provider-verify",
15321
- "report",
15322
- "scan",
15323
- "stack",
15324
- "status",
15325
- "tui",
15326
- "try",
15327
- "validate-npm-package",
15328
- "version",
15329
- "watch"
15330
- ]);
15331
- function isRootModePositional(flags, token) {
15332
- if (KNOWN_COMMANDS.has(token)) {
15333
- return false;
15334
- }
15335
- return [
15336
- "agent-mode",
15337
- "demo",
15338
- "showcase",
15339
- "json",
15340
- "jsonl",
15341
- "strict",
15342
- "condense",
15343
- "verify",
15344
- "heal"
15345
- ].some((key) => flags[key] !== void 0);
15346
- }
15347
14310
  function isBooleanFlag(command, key) {
15348
14311
  if ([
15349
14312
  "agent-mode",
15350
- "demo",
15351
- "showcase",
15352
14313
  "json",
15353
14314
  "jsonl",
15354
14315
  "condense",
@@ -15358,17 +14319,14 @@ function isBooleanFlag(command, key) {
15358
14319
  "apply",
15359
14320
  "yes",
15360
14321
  "no-verify",
15361
- "force-scan",
15362
- "ui"
14322
+ "force-scan"
15363
14323
  ].includes(key)) {
15364
14324
  return true;
15365
14325
  }
15366
14326
  if (key === "strict") return true;
15367
- if (key === "open" && (command === "" || command === "scan" || command === "report" || command === "try")) return true;
14327
+ if (key === "open" && (command === "" || command === "scan" || command === "report")) return true;
15368
14328
  if (key === "verify" && command === "") return true;
15369
14329
  if (key === "vercel-supabase" && command === "audit") return true;
15370
- if (key === "svg" && command === "badge") return true;
15371
- if (key === "card" && command === "badge") return true;
15372
14330
  if (key === "json" && command === "validate-npm-package") return true;
15373
14331
  if (key === "dry-run" && command === "init") return true;
15374
14332
  if (key === "agents" && command === "doctor") return true;
@@ -15380,9 +14338,6 @@ function shouldConsumeLeadingHyphenValue(command, key, value) {
15380
14338
  function hasFlag(flags, key) {
15381
14339
  return flags[key] === true || typeof flags[key] === "string";
15382
14340
  }
15383
- function resolveCliPath(input) {
15384
- return input ? (0, import_node_path28.resolve)(process.cwd(), input) : process.cwd();
15385
- }
15386
14341
  async function guardEarlyVerifyScan(input) {
15387
14342
  if (input.flags["force-scan"] === true) {
15388
14343
  return void 0;
@@ -15391,7 +14346,7 @@ async function guardEarlyVerifyScan(input) {
15391
14346
  if (!verifyLike) {
15392
14347
  return void 0;
15393
14348
  }
15394
- const workspacePath = input.positional[0] ? (0, import_node_path28.join)(process.cwd(), input.positional[0]) : await resolveWorkspaceRoot(process.cwd());
14349
+ const workspacePath = input.positional[0] ? (0, import_node_path25.join)(process.cwd(), input.positional[0]) : await resolveWorkspaceRoot(process.cwd());
15395
14350
  const loopState = await loadLoopState(workspacePath);
15396
14351
  if (loopState.batchApplied <= 0) {
15397
14352
  return void 0;
@@ -15415,19 +14370,7 @@ async function guardEarlyVerifyScan(input) {
15415
14370
  return void 0;
15416
14371
  }
15417
14372
  const nextTask = remainingRepoCodeTasks[0];
15418
- const progress = renderLoopProgress(
15419
- {
15420
- applied: loopState.batchApplied,
15421
- total: batchSize,
15422
- label: "Healing repo gaps",
15423
- silentInPlain: true
15424
- },
15425
- currentRenderMode("human-command")
15426
- );
15427
14373
  console.error("SCAN_DEFERRED: Local heal batch is not full yet, so VibeRaven is protecting scan quota.");
15428
- if (progress) {
15429
- console.error(progress);
15430
- }
15431
14374
  console.error(`Batch progress: ${loopState.batchApplied}/${batchSize} local heals applied since the last scan.`);
15432
14375
  console.error(`Next local heal: npx -y viberaven --heal --apply --gap ${nextTask.gapId} --yes`);
15433
14376
  console.error("Run verify again after the batch is full, or add --force-scan if the user explicitly wants to spend a scan now.");
@@ -15442,36 +14385,6 @@ function resolveDefaultEntrypointMode(options) {
15442
14385
  function formatScanJsonStdout(artifact) {
15443
14386
  return JSON.stringify(sanitizeArtifactForDisk(artifact), null, 2);
15444
14387
  }
15445
- function providerFromConnectedToolKey(tool) {
15446
- if (!tool.endsWith("Mcp")) return void 0;
15447
- return tool.slice(0, -3).toLowerCase();
15448
- }
15449
- function normalizeProviderForConnectedToolHint(provider2) {
15450
- const lower = provider2.trim().toLowerCase();
15451
- const tokens = lower.split(/[^a-z0-9]+/).filter(Boolean);
15452
- for (const knownProvider of ["supabase", "stripe", "vercel", "github"]) {
15453
- if (lower === knownProvider || tokens.includes(knownProvider)) {
15454
- return knownProvider;
15455
- }
15456
- }
15457
- return lower;
15458
- }
15459
- function printMissingConnectedToolHints(prp) {
15460
- const relevantProviders = new Set([
15461
- ...prp.detectedStack.auth,
15462
- ...prp.detectedStack.database,
15463
- ...prp.detectedStack.deployment,
15464
- ...prp.detectedStack.payments
15465
- ].map(normalizeProviderForConnectedToolHint));
15466
- for (const [tool, state] of Object.entries(prp.connectedTools)) {
15467
- const provider2 = providerFromConnectedToolKey(tool);
15468
- if (!provider2 || state !== "missing" || !relevantProviders.has(provider2)) {
15469
- continue;
15470
- }
15471
- console.log(`${provider2} MCP is not connected. For stronger verification, add it:`);
15472
- console.log(` ${installHintFor(provider2)}`);
15473
- }
15474
- }
15475
14388
  async function cmdLogin(flags) {
15476
14389
  const apiBaseUrl = resolveApiBaseUrl(typeof flags["api-url"] === "string" ? flags["api-url"] : void 0);
15477
14390
  await runDeviceLogin(apiBaseUrl);
@@ -15509,7 +14422,7 @@ async function cmdStatus(flags, positional) {
15509
14422
  console.log("Not signed in. Run: viberaven login");
15510
14423
  return 1;
15511
14424
  }
15512
- const startDir = positional[0] ? (0, import_node_path28.join)(process.cwd(), positional[0]) : process.cwd();
14425
+ const startDir = positional[0] ? (0, import_node_path25.join)(process.cwd(), positional[0]) : process.cwd();
15513
14426
  let artifact;
15514
14427
  try {
15515
14428
  artifact = await loadLastArtifact(startDir);
@@ -15663,7 +14576,7 @@ async function cmdWatch(flags) {
15663
14576
  }
15664
14577
  }
15665
14578
  async function runScanCommand(flags, positional, options) {
15666
- const workspacePath = positional[0] ? (0, import_node_path28.join)(process.cwd(), positional[0]) : await resolveWorkspaceRoot(process.cwd());
14579
+ const workspacePath = positional[0] ? (0, import_node_path25.join)(process.cwd(), positional[0]) : await resolveWorkspaceRoot(process.cwd());
15667
14580
  const apiBaseUrl = resolveApiBaseUrl(typeof flags["api-url"] === "string" ? flags["api-url"] : void 0);
15668
14581
  let accessToken;
15669
14582
  try {
@@ -15709,8 +14622,7 @@ async function runScanCommand(flags, positional, options) {
15709
14622
  return { exitCode: 1 };
15710
14623
  }
15711
14624
  const artifact = await enrichArtifactWithAccount(result.artifact, apiBaseUrl, accessToken);
15712
- const connectedTools = detectConnectedTools();
15713
- const paths = await writeScanArtifacts({ artifact, cwd: workspacePath, connectedTools });
14625
+ const paths = await writeScanArtifacts({ artifact, cwd: workspacePath });
15714
14626
  if (flags.json && !options?.deferMachineOutput) {
15715
14627
  console.log(formatScanJsonStdout(artifact));
15716
14628
  return { exitCode: 0, artifacts: paths };
@@ -15726,15 +14638,6 @@ async function runScanCommand(flags, positional, options) {
15726
14638
  const openGapCount = artifact.gaps.length;
15727
14639
  const updatedState = resetBatch(loopState, openGapCount);
15728
14640
  const tasks = buildTaskList(artifact);
15729
- const prp = generateProductionProtocol(artifact, { connectedTools });
15730
- const mode = currentRenderMode("agent-mode");
15731
- printOperatorBlock(prp, tasks, mode);
15732
- const celebration = renderClearCelebration(prp, mode);
15733
- if (celebration) {
15734
- console.log(celebration);
15735
- }
15736
- printActionPanelBlock(paths.reportPath);
15737
- printMissingConnectedToolHints(prp);
15738
14641
  const plan = artifact.plan ?? "free";
15739
14642
  const block = buildNextActionBlock(tasks, updatedState, plan);
15740
14643
  printNextActionBlock(block);
@@ -15751,7 +14654,7 @@ async function runScanCommand(flags, positional, options) {
15751
14654
  return { exitCode: 0, artifacts: paths };
15752
14655
  }
15753
14656
  async function cmdReport(flags, positional) {
15754
- const startDir = positional[0] ? (0, import_node_path28.join)(process.cwd(), positional[0]) : process.cwd();
14657
+ const startDir = positional[0] ? (0, import_node_path25.join)(process.cwd(), positional[0]) : process.cwd();
15755
14658
  try {
15756
14659
  const paths = await refreshReportFromDisk(startDir);
15757
14660
  console.log(`Report refreshed: ${paths.reportPath}`);
@@ -15773,7 +14676,7 @@ async function cmdReport(flags, positional) {
15773
14676
  }
15774
14677
  }
15775
14678
  async function cmdPrompt(flags, positional) {
15776
- const startDir = positional[0] ? (0, import_node_path28.join)(process.cwd(), positional[0]) : process.cwd();
14679
+ const startDir = positional[0] ? (0, import_node_path25.join)(process.cwd(), positional[0]) : process.cwd();
15777
14680
  let artifact;
15778
14681
  try {
15779
14682
  artifact = await loadLastArtifact(startDir);
@@ -15781,26 +14684,26 @@ async function cmdPrompt(flags, positional) {
15781
14684
  console.error(error instanceof Error ? error.message : "No scan found. Run: viberaven scan");
15782
14685
  return 1;
15783
14686
  }
15784
- const gap2 = pickGap(artifact, {
14687
+ const gap = pickGap(artifact, {
15785
14688
  gapId: typeof flags.gap === "string" ? flags.gap : void 0,
15786
14689
  provider: typeof flags.provider === "string" ? flags.provider : void 0,
15787
14690
  area: typeof flags.area === "string" ? flags.area : void 0
15788
14691
  });
15789
- if (!gap2) {
14692
+ if (!gap) {
15790
14693
  console.error("No matching gap. Run `viberaven scan` or pass --gap <id>.");
15791
14694
  return 1;
15792
14695
  }
15793
14696
  const skipCopy = flags["no-copy"] === true;
15794
14697
  if (!skipCopy) {
15795
14698
  try {
15796
- await copyToClipboard(gap2.copyPrompt);
15797
- console.log(`Copied to clipboard: ${gap2.title}`);
14699
+ await copyToClipboard(gap.copyPrompt);
14700
+ console.log(`Copied to clipboard: ${gap.title}`);
15798
14701
  return 0;
15799
14702
  } catch (error) {
15800
14703
  console.warn(error instanceof Error ? error.message : String(error));
15801
14704
  }
15802
14705
  }
15803
- console.log(gap2.copyPrompt);
14706
+ console.log(gap.copyPrompt);
15804
14707
  return 0;
15805
14708
  }
15806
14709
  var STACK_AREAS = /* @__PURE__ */ new Set(["database", "auth", "payments", "deployment", "monitoring", "security"]);
@@ -15871,7 +14774,7 @@ async function main() {
15871
14774
  const wantsJsonl = hasFlag(flags, "jsonl");
15872
14775
  const wantsStrict = hasFlag(flags, "strict");
15873
14776
  if (flags.condense) {
15874
- const cwd = positional[0] ? (0, import_node_path28.join)(process.cwd(), positional[0]) : process.cwd();
14777
+ const cwd = positional[0] ? (0, import_node_path25.join)(process.cwd(), positional[0]) : process.cwd();
15875
14778
  const result = await runCondenseCommand({ cwd });
15876
14779
  console.log(`VibeRaven context map refreshed: ${result.contextMapPath}`);
15877
14780
  return 0;
@@ -15889,19 +14792,6 @@ async function main() {
15889
14792
  console.log(JSON.stringify(result, null, 2));
15890
14793
  return result.status.startsWith("refused") || result.status === "failed" ? 1 : 0;
15891
14794
  }
15892
- if (command === "try") {
15893
- const cwd = resolveCliPath(positional[0]);
15894
- return runDemo({
15895
- cwd,
15896
- agentMode: isAgentMode,
15897
- label: "try",
15898
- openReport: flags.open === true || flags.ui === true
15899
- });
15900
- }
15901
- if (hasFlag(flags, "demo") || hasFlag(flags, "showcase")) {
15902
- const cwd = resolveCliPath(positional[0]);
15903
- return runDemo({ cwd, agentMode: isAgentMode, label: hasFlag(flags, "showcase") ? "showcase" : "demo" });
15904
- }
15905
14795
  if (!command && (isAgentMode || flags.verify === true || wantsJson || wantsJsonl || wantsStrict)) {
15906
14796
  const guardedExitCode = await guardEarlyVerifyScan({ flags, positional, wantsStrict });
15907
14797
  if (guardedExitCode !== void 0) {
@@ -15913,7 +14803,7 @@ async function main() {
15913
14803
  console.error("VibeRaven could not produce machine output because scan artifacts were not written.");
15914
14804
  return 3;
15915
14805
  }
15916
- const gateResult = scanResult.artifacts && (wantsJson || wantsJsonl || wantsStrict) ? JSON.parse(await (0, import_promises21.readFile)(scanResult.artifacts.gateResultPath, "utf8")) : void 0;
14806
+ const gateResult = scanResult.artifacts && (wantsJson || wantsJsonl || wantsStrict) ? JSON.parse(await (0, import_promises19.readFile)(scanResult.artifacts.gateResultPath, "utf8")) : void 0;
15917
14807
  const strictExitCode = wantsStrict && gateResult ? exitCodeForStrictGate(gateResult, { failOnWarnings: flags.strict === "warning" }) : scanResult.exitCode;
15918
14808
  if (wantsJson && gateResult) {
15919
14809
  process.stdout.write(renderGateResultJson(gateResult));
@@ -15948,7 +14838,7 @@ async function main() {
15948
14838
  case "next":
15949
14839
  return runNextCommand({
15950
14840
  json: Boolean(flags.json),
15951
- cwd: positional[0] ? (0, import_node_path28.join)(process.cwd(), positional[0]) : process.cwd()
14841
+ cwd: positional[0] ? (0, import_node_path25.join)(process.cwd(), positional[0]) : process.cwd()
15952
14842
  });
15953
14843
  case "guide": {
15954
14844
  const provider2 = positional[0];
@@ -15986,7 +14876,7 @@ async function main() {
15986
14876
  case "provider-verify":
15987
14877
  return cmdProviderVerify(flags, positional);
15988
14878
  case "init": {
15989
- const cwd = positional[0] ? (0, import_node_path28.join)(process.cwd(), positional[0]) : process.cwd();
14879
+ const cwd = positional[0] ? (0, import_node_path25.join)(process.cwd(), positional[0]) : process.cwd();
15990
14880
  const agents = typeof flags.agents === "string" ? flags.agents : void 0;
15991
14881
  return runInitCommand({
15992
14882
  cwd,
@@ -16000,7 +14890,7 @@ async function main() {
16000
14890
  return 1;
16001
14891
  }
16002
14892
  return runDoctorAgentsCommand({
16003
- cwd: positional[0] ? (0, import_node_path28.join)(process.cwd(), positional[0]) : process.cwd()
14893
+ cwd: positional[0] ? (0, import_node_path25.join)(process.cwd(), positional[0]) : process.cwd()
16004
14894
  });
16005
14895
  case "validate-npm-package":
16006
14896
  return runValidateNpmPackageCommand({
@@ -16013,15 +14903,9 @@ async function main() {
16013
14903
  return 1;
16014
14904
  }
16015
14905
  return runAuditCommand({
16016
- cwd: positional[0] ? (0, import_node_path28.join)(process.cwd(), positional[0]) : process.cwd(),
14906
+ cwd: positional[0] ? (0, import_node_path25.join)(process.cwd(), positional[0]) : process.cwd(),
16017
14907
  json: Boolean(flags.json)
16018
14908
  });
16019
- case "badge":
16020
- return runBadgeCommand({
16021
- cwd: positional[0] ? (0, import_node_path28.join)(process.cwd(), positional[0]) : process.cwd(),
16022
- svg: flags.svg === true,
16023
- card: flags.card === true
16024
- });
16025
14909
  default:
16026
14910
  console.error(`Unknown command: ${command}`);
16027
14911
  printHelp();