@viberaven/cli 1.1.0 → 1.1.1

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
@@ -170,8 +170,8 @@ __export(cli_exports, {
170
170
  runScanCommand: () => runScanCommand
171
171
  });
172
172
  module.exports = __toCommonJS(cli_exports);
173
- var import_promises19 = require("node:fs/promises");
174
- var import_node_path26 = require("node:path");
173
+ var import_promises21 = require("node:fs/promises");
174
+ var import_node_path28 = 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(label, message) {
532
- return `${label}: ${message}`;
531
+ function formatAgentStatus(label2, message) {
532
+ return `${label2}: ${message}`;
533
533
  }
534
534
 
535
535
  // src/account.ts
@@ -661,6 +661,9 @@ 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
+ }
664
667
 
665
668
  // src/auth.ts
666
669
  var PUBLIC_LOGIN_COMMAND = `${PUBLIC_COMMAND} login`;
@@ -1094,7 +1097,7 @@ function createGitignoreMatcher(gitignoreContent) {
1094
1097
  const rules = gitignoreContent.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#") && !line.startsWith("!")).map(parseGitignoreRule);
1095
1098
  return (relPath) => {
1096
1099
  const normalized = relPath.replace(/\\/g, "/").replace(/^\/+/, "");
1097
- return rules.some((rule) => ruleMatchesPath(rule, normalized));
1100
+ return rules.some((rule2) => ruleMatchesPath(rule2, normalized));
1098
1101
  };
1099
1102
  }
1100
1103
  function parseGitignoreRule(raw) {
@@ -1111,22 +1114,22 @@ function parseGitignoreRule(raw) {
1111
1114
  regex: new RegExp(`^${gitignoreGlobToRegex(pattern)}$`)
1112
1115
  };
1113
1116
  }
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) {
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) {
1118
1121
  return true;
1119
1122
  }
1120
- if (directMatch && rule.directoryOnly) {
1123
+ if (directMatch && rule2.directoryOnly) {
1121
1124
  return true;
1122
1125
  }
1123
- if (!rule.directoryOnly) {
1126
+ if (!rule2.directoryOnly) {
1124
1127
  return false;
1125
1128
  }
1126
- if (rule.anchored || rule.hasSlash) {
1127
- return relPath === rule.pattern || relPath.startsWith(`${rule.pattern}/`);
1129
+ if (rule2.anchored || rule2.hasSlash) {
1130
+ return relPath === rule2.pattern || relPath.startsWith(`${rule2.pattern}/`);
1128
1131
  }
1129
- return relPath.split("/").includes(rule.pattern);
1132
+ return relPath.split("/").includes(rule2.pattern);
1130
1133
  }
1131
1134
  function gitignoreGlobToRegex(pattern) {
1132
1135
  let out = "";
@@ -1246,7 +1249,7 @@ function fallbackMapCategoryForGap(category, text) {
1246
1249
  function inferMapCategories(category, title, detail, copyPrompt, explicitPrimary, explicitAffected) {
1247
1250
  const text = [category, title, detail, copyPrompt].filter(Boolean).join(" ");
1248
1251
  const fallback = fallbackMapCategoryForGap(category, text);
1249
- const matched = MAP_CATEGORY_RULES.filter((rule) => rule.match.test(text)).map((rule) => rule.key);
1252
+ const matched = MAP_CATEGORY_RULES.filter((rule2) => rule2.match.test(text)).map((rule2) => rule2.key);
1250
1253
  const explicit = isProductionMapCategoryKey(explicitPrimary) ? [explicitPrimary] : [];
1251
1254
  const affected = Array.isArray(explicitAffected) ? explicitAffected.filter(isProductionMapCategoryKey) : [];
1252
1255
  const primarySeed = matched[0] ? [matched[0], fallback] : [fallback];
@@ -1291,12 +1294,12 @@ function normalizeGap(v) {
1291
1294
  affectedMapCategories
1292
1295
  };
1293
1296
  }
1294
- function rootGapKey(gap) {
1295
- const titleKey = slugify(gap.title);
1297
+ function rootGapKey(gap2) {
1298
+ const titleKey = slugify(gap2.title);
1296
1299
  if (titleKey) {
1297
1300
  return titleKey;
1298
1301
  }
1299
- return slugify([gap.category, gap.copyPrompt].join(" ")) || gap.id;
1302
+ return slugify([gap2.category, gap2.copyPrompt].join(" ")) || gap2.id;
1300
1303
  }
1301
1304
  function mergeToolSuggestions(a, b3) {
1302
1305
  const seen = /* @__PURE__ */ new Set();
@@ -1326,17 +1329,17 @@ function mergeRootGaps(a, b3) {
1326
1329
  function dedupeRootGaps(gaps) {
1327
1330
  const byKey = /* @__PURE__ */ new Map();
1328
1331
  const order = [];
1329
- for (const gap of gaps) {
1330
- const key = rootGapKey(gap);
1332
+ for (const gap2 of gaps) {
1333
+ const key = rootGapKey(gap2);
1331
1334
  const existing = byKey.get(key);
1332
1335
  if (!existing) {
1333
- byKey.set(key, gap);
1336
+ byKey.set(key, gap2);
1334
1337
  order.push(key);
1335
1338
  continue;
1336
1339
  }
1337
- byKey.set(key, mergeRootGaps(existing, gap));
1340
+ byKey.set(key, mergeRootGaps(existing, gap2));
1338
1341
  }
1339
- return order.map((key) => byKey.get(key)).filter((gap) => Boolean(gap));
1342
+ return order.map((key) => byKey.get(key)).filter((gap2) => Boolean(gap2));
1340
1343
  }
1341
1344
  function normalizeChecklist(v) {
1342
1345
  const defaults = {
@@ -1480,15 +1483,15 @@ function buildLaunchValidationReport(input) {
1480
1483
  promptTemplate: conflict.recommendedAction.promptTemplate
1481
1484
  }))
1482
1485
  );
1483
- const gapIssues = (input.gaps ?? []).filter((gap) => gap.severity !== "info").map((gap) => {
1484
- const area = gapArea(gap);
1486
+ const gapIssues = (input.gaps ?? []).filter((gap2) => gap2.severity !== "info").map((gap2) => {
1487
+ const area = gapArea(gap2);
1485
1488
  return {
1486
- id: `gap-${gap.id}`,
1489
+ id: `gap-${gap2.id}`,
1487
1490
  area,
1488
- severity: gap.severity,
1489
- title: gap.title,
1490
- detail: gap.detail,
1491
- promptTemplate: gap.severity === "critical" && isLaunchCriticalArea(area) ? "launch-blocker" : "repo-fix"
1491
+ severity: gap2.severity,
1492
+ title: gap2.title,
1493
+ detail: gap2.detail,
1494
+ promptTemplate: gap2.severity === "critical" && isLaunchCriticalArea(area) ? "launch-blocker" : "repo-fix"
1492
1495
  };
1493
1496
  });
1494
1497
  const providerCoverageWarnings = providerCoverageIssues(input.providerTruth);
@@ -1603,8 +1606,8 @@ function isActionableProviderCheckRow(row) {
1603
1606
  function hasCompletedManualConfirmation(row) {
1604
1607
  return row.manualProof.status === "manual-confirmed" || row.roles.some((role) => role === "manual-confirmed");
1605
1608
  }
1606
- function gapArea(gap) {
1607
- return VERIFICATION_AREAS.includes(gap.primaryMapCategory) ? gap.primaryMapCategory : "appFlow";
1609
+ function gapArea(gap2) {
1610
+ return VERIFICATION_AREAS.includes(gap2.primaryMapCategory) ? gap2.primaryMapCategory : "appFlow";
1608
1611
  }
1609
1612
  function isLaunchCriticalArea(area) {
1610
1613
  return LAUNCH_CRITICAL_AREAS.includes(area);
@@ -2073,11 +2076,11 @@ function buildProviderRegistrySnapshot(now = /* @__PURE__ */ new Date()) {
2073
2076
  providers
2074
2077
  };
2075
2078
  }
2076
- function provider(providerKey, label, aliases, areas, productionAreas, iconKey, extras = {}) {
2079
+ function provider(providerKey, label2, aliases, areas, productionAreas, iconKey, extras = {}) {
2077
2080
  const sifgTemplateIds = areas.flatMap((area) => sifgTemplatesForRegistryProviderArea(providerKey, area)).map((template) => template.id);
2078
2081
  return {
2079
2082
  provider: providerKey,
2080
- label,
2083
+ label: label2,
2081
2084
  aliases,
2082
2085
  areas,
2083
2086
  productionAreas,
@@ -2935,14 +2938,14 @@ function detectProductionConnectionEvidence(scan) {
2935
2938
  content: file.isSecret || typeof file.content !== "string" ? "" : file.content,
2936
2939
  lowerContent: file.isSecret || typeof file.content !== "string" ? "" : file.content.toLowerCase()
2937
2940
  }));
2938
- const secretPathBlob = scan.secretsFound.map((path) => normalizePath(path)).join("\n");
2941
+ const secretPathBlob = scan.secretsFound.map((path3) => normalizePath(path3)).join("\n");
2939
2942
  const pathBlob = `${scan.fileTree}
2940
2943
  ${files.map((file) => file.path).join("\n")}`.toLowerCase();
2941
2944
  const contentBlob = files.map((file) => file.lowerContent).join("\n").slice(0, 12e4);
2942
2945
  const secretsHygieneBlob = `${pathBlob}
2943
2946
  ${secretPathBlob}`;
2944
- for (const rule of PROVIDER_RULES) {
2945
- detectProvider(evidence, rule, deps, files, pathBlob);
2947
+ for (const rule2 of PROVIDER_RULES) {
2948
+ detectProvider(evidence, rule2, deps, files, pathBlob);
2946
2949
  }
2947
2950
  detectSecretsHygiene(evidence, secretsHygieneBlob);
2948
2951
  for (const item3 of Object.values(evidence)) {
@@ -2996,55 +2999,55 @@ function buildProductionConnectionContext(choices, evidence) {
2996
2999
  }
2997
3000
  return lines.length > 0 ? lines.join("\n") : "production connections: no selected or detected providers";
2998
3001
  }
2999
- function detectProvider(evidence, rule, deps, files, pathBlob) {
3000
- if (evidence[rule.area]) {
3002
+ function detectProvider(evidence, rule2, deps, files, pathBlob) {
3003
+ if (evidence[rule2.area]) {
3001
3004
  return;
3002
3005
  }
3003
- const signals = collectSignals(rule, deps, files, pathBlob);
3006
+ const signals = collectSignals(rule2, deps, files, pathBlob);
3004
3007
  if (signals.length === 0) {
3005
3008
  return;
3006
3009
  }
3007
- evidence[rule.area] = {
3008
- area: rule.area,
3009
- provider: rule.provider,
3010
+ evidence[rule2.area] = {
3011
+ area: rule2.area,
3012
+ provider: rule2.provider,
3010
3013
  status: ["detected"],
3011
3014
  signals
3012
3015
  };
3013
3016
  }
3014
- function collectSignals(rule, deps, files, pathBlob) {
3017
+ function collectSignals(rule2, deps, files, pathBlob) {
3015
3018
  const signals = [];
3016
3019
  for (const dep of deps) {
3017
- if ((rule.packages ?? []).some((pattern) => testRegex(pattern, dep))) {
3020
+ if ((rule2.packages ?? []).some((pattern) => testRegex(pattern, dep))) {
3018
3021
  addSignal(signals, `package: ${dep}`);
3019
3022
  }
3020
3023
  }
3021
3024
  const upperContents = files.map((file) => file.content).join("\n").toUpperCase();
3022
- for (const envName of rule.env ?? []) {
3025
+ for (const envName of rule2.env ?? []) {
3023
3026
  if (upperContents.includes(envName.toUpperCase())) {
3024
3027
  addSignal(signals, `env: ${envName}`);
3025
3028
  }
3026
3029
  }
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}`);
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}`);
3031
3034
  }
3032
3035
  }
3033
3036
  for (const file of files) {
3034
- for (const importName of rule.imports ?? []) {
3037
+ for (const importName of rule2.imports ?? []) {
3035
3038
  if (containsImport(file.lowerContent, importName)) {
3036
3039
  addSignal(signals, `import: ${importName}`);
3037
3040
  }
3038
3041
  }
3039
- for (const item3 of rule.content ?? []) {
3042
+ for (const item3 of rule2.content ?? []) {
3040
3043
  if (testRegex(item3.pattern, file.content)) {
3041
3044
  addSignal(signals, item3.signal);
3042
3045
  }
3043
3046
  }
3044
3047
  if (isDocsPath(file.path)) {
3045
- for (const docsTerm of rule.docs ?? []) {
3048
+ for (const docsTerm of rule2.docs ?? []) {
3046
3049
  if (file.lowerContent.includes(docsTerm.toLowerCase())) {
3047
- addSignal(signals, `docs: ${file.displayPath} mentions ${rule.label}`);
3050
+ addSignal(signals, `docs: ${file.displayPath} mentions ${rule2.label}`);
3048
3051
  break;
3049
3052
  }
3050
3053
  }
@@ -3056,14 +3059,14 @@ function containsImport(content, importName) {
3056
3059
  const escaped = importName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&").toLowerCase();
3057
3060
  return new RegExp(`(?:from\\s+['"]${escaped}['"]|import\\s*\\(\\s*['"]${escaped}['"]|require\\s*\\(\\s*['"]${escaped}['"])`).test(content);
3058
3061
  }
3059
- function isDocsPath(path) {
3060
- return /(^|\/)(readme|product|spec)\.md$/.test(path) || /(^|\/)docs\/.*\.md$/.test(path);
3062
+ function isDocsPath(path3) {
3063
+ return /(^|\/)(readme|product|spec)\.md$/.test(path3) || /(^|\/)docs\/.*\.md$/.test(path3);
3061
3064
  }
3062
- function pathSignalPrefix(path) {
3063
- if (/webhook|checkout|billing|api\/|route\.[jt]s$/.test(path)) {
3065
+ function pathSignalPrefix(path3) {
3066
+ if (/webhook|checkout|billing|api\/|route\.[jt]s$/.test(path3)) {
3064
3067
  return "route";
3065
3068
  }
3066
- if (/config|\.json$|\.toml$|\.ya?ml$/.test(path)) {
3069
+ if (/config|\.json$|\.toml$|\.ya?ml$/.test(path3)) {
3067
3070
  return "config";
3068
3071
  }
3069
3072
  return "file";
@@ -3074,19 +3077,19 @@ function addSignal(signals, signal) {
3074
3077
  }
3075
3078
  }
3076
3079
  function freezeProviderRules(rules) {
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) {
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) {
3085
3088
  Object.freeze(item3);
3086
3089
  }
3087
- Object.freeze(rule.content);
3090
+ Object.freeze(rule2.content);
3088
3091
  }
3089
- Object.freeze(rule);
3092
+ Object.freeze(rule2);
3090
3093
  }
3091
3094
  return Object.freeze(rules);
3092
3095
  }
@@ -3214,8 +3217,8 @@ function normalizeSelectedAt(value) {
3214
3217
  }
3215
3218
  return null;
3216
3219
  }
3217
- function normalizePath(path) {
3218
- return path.replace(/\\/g, "/").toLowerCase();
3220
+ function normalizePath(path3) {
3221
+ return path3.replace(/\\/g, "/").toLowerCase();
3219
3222
  }
3220
3223
  function isObject(value) {
3221
3224
  return typeof value === "object" && value !== null;
@@ -3243,8 +3246,8 @@ var WEAK_PATH_SEGMENTS = /* @__PURE__ */ new Set([
3243
3246
  "demo"
3244
3247
  ]);
3245
3248
  var TEST_FILE_PATTERN = /\.(test|spec)\.[jt]sx?$/;
3246
- function classifyProviderEvidencePath(path) {
3247
- const normalized = normalizePath2(path);
3249
+ function classifyProviderEvidencePath(path3) {
3250
+ const normalized = normalizePath2(path3);
3248
3251
  const segments = normalized.split("/").filter(Boolean);
3249
3252
  if (isDocsLikePath(normalized) || segments.some((segment) => WEAK_PATH_SEGMENTS.has(segment)) || TEST_FILE_PATTERN.test(normalized)) {
3250
3253
  return "weak";
@@ -3259,14 +3262,14 @@ function buildProviderTruthSnapshot(input) {
3259
3262
  const rowsByArea = {};
3260
3263
  void input.mcpVerifierState;
3261
3264
  void input.verificationLayer;
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;
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;
3265
3268
  if (evidence.length === 0 && !selected) {
3266
3269
  continue;
3267
3270
  }
3268
- const row = buildRow(rule, evidence, selected);
3269
- rowsByArea[rule.area] = [...rowsByArea[rule.area] ?? [], row];
3271
+ const row = buildRow(rule2, evidence, selected);
3272
+ rowsByArea[rule2.area] = [...rowsByArea[rule2.area] ?? [], row];
3270
3273
  }
3271
3274
  for (const [area, choice] of Object.entries(choices.choices)) {
3272
3275
  if (!choice) {
@@ -3298,32 +3301,32 @@ function buildProviderTruthSnapshot(input) {
3298
3301
  summary
3299
3302
  };
3300
3303
  }
3301
- function collectProviderTruthEvidence(rule, deps, files, pathLines) {
3304
+ function collectProviderTruthEvidence(rule2, deps, files, pathLines) {
3302
3305
  const evidence = [];
3303
3306
  for (const dep of deps) {
3304
- if ((rule.packages ?? []).some((pattern) => testRegex2(pattern, dep))) {
3305
- addEvidence(evidence, rule, {
3307
+ if ((rule2.packages ?? []).some((pattern) => testRegex2(pattern, dep))) {
3308
+ addEvidence(evidence, rule2, {
3306
3309
  kind: "package-installed",
3307
3310
  strength: "medium",
3308
- label: `${rule.label} package installed`,
3311
+ label: `${rule2.label} package installed`,
3309
3312
  detail: dep,
3310
3313
  points: 20,
3311
3314
  isRuntimeEvidence: false
3312
3315
  });
3313
3316
  }
3314
3317
  }
3315
- for (const path of pathLines) {
3316
- if (!(rule.paths ?? []).some((pattern) => testRegex2(pattern, path))) {
3318
+ for (const path3 of pathLines) {
3319
+ if (!(rule2.paths ?? []).some((pattern) => testRegex2(pattern, path3))) {
3317
3320
  continue;
3318
3321
  }
3319
- const pathClass = classifyProviderEvidencePath(path);
3320
- const kind = weakPathKind(path, "active-runtime-route") ?? routeKind(path);
3321
- addEvidence(evidence, rule, {
3322
+ const pathClass = classifyProviderEvidencePath(path3);
3323
+ const kind = weakPathKind(path3, "active-runtime-route") ?? routeKind(path3);
3324
+ addEvidence(evidence, rule2, {
3322
3325
  kind,
3323
3326
  strength: pathClass === "runtime" ? "strong" : "weak",
3324
- label: `${rule.label} ${pathClass === "runtime" ? "runtime route or path" : "weak path reference"}`,
3325
- file: path,
3326
- points: pathClass === "runtime" ? 35 : weakEvidencePoints(path),
3327
+ label: `${rule2.label} ${pathClass === "runtime" ? "runtime route or path" : "weak path reference"}`,
3328
+ file: path3,
3329
+ points: pathClass === "runtime" ? 35 : weakEvidencePoints(path3),
3327
3330
  isRuntimeEvidence: pathClass === "runtime"
3328
3331
  });
3329
3332
  }
@@ -3331,45 +3334,45 @@ function collectProviderTruthEvidence(rule, deps, files, pathLines) {
3331
3334
  const pathClass = classifyProviderEvidencePath(file.path);
3332
3335
  const sourceContent = pathClass === "runtime" ? file.executableContent : file.content;
3333
3336
  const sourceLowerContent = pathClass === "runtime" ? file.lowerExecutableContent : file.lowerContent;
3334
- for (const envName of rule.env ?? []) {
3337
+ for (const envName of rule2.env ?? []) {
3335
3338
  if (!sourceContent.toUpperCase().includes(envName.toUpperCase())) {
3336
3339
  continue;
3337
3340
  }
3338
- addEvidence(evidence, rule, {
3341
+ addEvidence(evidence, rule2, {
3339
3342
  kind: pathClass === "runtime" ? "runtime-env-usage" : "env-name-only",
3340
3343
  strength: pathClass === "runtime" ? "medium" : "weak",
3341
- label: `${rule.label} env name ${pathClass === "runtime" ? "used in runtime source" : "mentioned outside runtime source"}`,
3344
+ label: `${rule2.label} env name ${pathClass === "runtime" ? "used in runtime source" : "mentioned outside runtime source"}`,
3342
3345
  file: file.displayPath,
3343
3346
  detail: envName,
3344
3347
  points: pathClass === "runtime" ? 20 : weakEvidencePoints(file.path),
3345
3348
  isRuntimeEvidence: pathClass === "runtime"
3346
3349
  });
3347
3350
  }
3348
- for (const importName of rule.imports ?? []) {
3351
+ for (const importName of rule2.imports ?? []) {
3349
3352
  if (!containsImport2(sourceLowerContent, importName)) {
3350
3353
  continue;
3351
3354
  }
3352
3355
  const weakKind = weakPathKind(file.path, "sdk-import-source");
3353
- addEvidence(evidence, rule, {
3356
+ addEvidence(evidence, rule2, {
3354
3357
  kind: weakKind ?? "sdk-import-source",
3355
3358
  strength: pathClass === "runtime" ? "strong" : "weak",
3356
- label: `${rule.label} SDK import ${pathClass === "runtime" ? "in runtime source" : "outside runtime source"}`,
3359
+ label: `${rule2.label} SDK import ${pathClass === "runtime" ? "in runtime source" : "outside runtime source"}`,
3357
3360
  file: file.displayPath,
3358
3361
  detail: importName,
3359
3362
  points: pathClass === "runtime" ? 35 : weakEvidencePoints(file.path),
3360
3363
  isRuntimeEvidence: pathClass === "runtime"
3361
3364
  });
3362
3365
  }
3363
- for (const item3 of rule.content ?? []) {
3366
+ for (const item3 of rule2.content ?? []) {
3364
3367
  if (!testRegex2(item3.pattern, sourceContent)) {
3365
3368
  continue;
3366
3369
  }
3367
3370
  const strongKind = contentSignalKind(item3.signal);
3368
3371
  const weakKind = weakPathKind(file.path, strongKind);
3369
- addEvidence(evidence, rule, {
3372
+ addEvidence(evidence, rule2, {
3370
3373
  kind: weakKind ?? strongKind,
3371
3374
  strength: pathClass === "runtime" ? "strong" : "weak",
3372
- label: `${rule.label} ${pathClass === "runtime" ? "runtime content signal" : "weak content reference"}`,
3375
+ label: `${rule2.label} ${pathClass === "runtime" ? "runtime content signal" : "weak content reference"}`,
3373
3376
  file: file.displayPath,
3374
3377
  detail: item3.signal,
3375
3378
  points: pathClass === "runtime" ? 35 : weakEvidencePoints(file.path),
@@ -3377,14 +3380,14 @@ function collectProviderTruthEvidence(rule, deps, files, pathLines) {
3377
3380
  });
3378
3381
  }
3379
3382
  if (isDocsLikePath(file.path)) {
3380
- for (const docsTerm of rule.docs ?? []) {
3383
+ for (const docsTerm of rule2.docs ?? []) {
3381
3384
  if (!file.lowerContent.includes(docsTerm.toLowerCase())) {
3382
3385
  continue;
3383
3386
  }
3384
- addEvidence(evidence, rule, {
3387
+ addEvidence(evidence, rule2, {
3385
3388
  kind: "docs-mention",
3386
3389
  strength: "weak",
3387
- label: `${rule.label} mentioned in docs`,
3390
+ label: `${rule2.label} mentioned in docs`,
3388
3391
  file: file.displayPath,
3389
3392
  detail: docsTerm,
3390
3393
  points: 4,
@@ -3396,16 +3399,16 @@ function collectProviderTruthEvidence(rule, deps, files, pathLines) {
3396
3399
  }
3397
3400
  return evidence.sort(compareEvidence);
3398
3401
  }
3399
- function buildRow(rule, evidence, selected) {
3402
+ function buildRow(rule2, evidence, selected) {
3400
3403
  const score = evidence.reduce((total, item3) => total + item3.points, 0);
3401
3404
  const roles = rolesForEvidence(evidence, selected);
3402
- applyMcpSupportRoles(rule.provider, roles);
3405
+ applyMcpSupportRoles(rule2.provider, roles);
3403
3406
  const confidence = confidenceForEvidence(evidence, roles);
3404
- const mcpProof = mcpProofForProvider(rule.provider);
3407
+ const mcpProof = mcpProofForProvider(rule2.provider);
3405
3408
  return {
3406
- area: rule.area,
3407
- provider: rule.provider,
3408
- providerLabel: providerLabel(rule.provider),
3409
+ area: rule2.area,
3410
+ provider: rule2.provider,
3411
+ providerLabel: providerLabel(rule2.provider),
3409
3412
  roles,
3410
3413
  confidence,
3411
3414
  score,
@@ -3662,10 +3665,10 @@ function statusBadgesForRoles(roles, confidence) {
3662
3665
  badges.push(confidence.toUpperCase());
3663
3666
  return badges;
3664
3667
  }
3665
- function addEvidence(evidence, rule, item3) {
3668
+ function addEvidence(evidence, rule2, item3) {
3666
3669
  const id = [
3667
- rule.area,
3668
- rule.provider,
3670
+ rule2.area,
3671
+ rule2.provider,
3669
3672
  item3.kind,
3670
3673
  item3.file ?? "",
3671
3674
  item3.detail ?? item3.label
@@ -3680,29 +3683,29 @@ function addEvidence(evidence, rule, item3) {
3680
3683
  isManualProof: false
3681
3684
  });
3682
3685
  }
3683
- function weakPathKind(path, fallback) {
3684
- if (classifyProviderEvidencePath(path) === "runtime") {
3686
+ function weakPathKind(path3, fallback) {
3687
+ if (classifyProviderEvidencePath(path3) === "runtime") {
3685
3688
  return null;
3686
3689
  }
3687
- if (isDocsLikePath(path)) {
3690
+ if (isDocsLikePath(path3)) {
3688
3691
  return "docs-mention";
3689
3692
  }
3690
- if (/(^|\/)(__tests__|tests)(\/|$)|\.(test|spec)\.[jt]sx?$/.test(normalizePath2(path))) {
3693
+ if (/(^|\/)(__tests__|tests)(\/|$)|\.(test|spec)\.[jt]sx?$/.test(normalizePath2(path3))) {
3691
3694
  return "test-reference";
3692
3695
  }
3693
- if (/(^|\/)(tmp|out|outputs|videos|marketing|examples|demo)(\/|$)/.test(normalizePath2(path))) {
3696
+ if (/(^|\/)(tmp|out|outputs|videos|marketing|examples|demo)(\/|$)/.test(normalizePath2(path3))) {
3694
3697
  return "tmp-demo-example";
3695
3698
  }
3696
3699
  return fallback;
3697
3700
  }
3698
- function routeKind(path) {
3699
- if (/webhook/.test(path)) {
3701
+ function routeKind(path3) {
3702
+ if (/webhook/.test(path3)) {
3700
3703
  return "webhook-handler";
3701
3704
  }
3702
- if (/checkout|billing/.test(path)) {
3705
+ if (/checkout|billing/.test(path3)) {
3703
3706
  return "checkout-handler";
3704
3707
  }
3705
- if (/config|\.json$|\.toml$|\.ya?ml$/.test(path)) {
3708
+ if (/config|\.json$|\.toml$|\.ya?ml$/.test(path3)) {
3706
3709
  return "deployment-config";
3707
3710
  }
3708
3711
  return "active-runtime-route";
@@ -3719,11 +3722,11 @@ function contentSignalKind(signal) {
3719
3722
  }
3720
3723
  return "active-runtime-route";
3721
3724
  }
3722
- function weakEvidencePoints(path) {
3723
- if (isDocsLikePath(path)) {
3725
+ function weakEvidencePoints(path3) {
3726
+ if (isDocsLikePath(path3)) {
3724
3727
  return 4;
3725
3728
  }
3726
- if (/(^|\/)(__tests__|tests)(\/|$)|\.(test|spec)\.[jt]sx?$/.test(normalizePath2(path))) {
3729
+ if (/(^|\/)(__tests__|tests)(\/|$)|\.(test|spec)\.[jt]sx?$/.test(normalizePath2(path3))) {
3727
3730
  return 6;
3728
3731
  }
3729
3732
  return 8;
@@ -3742,8 +3745,8 @@ function toScannableFile(file) {
3742
3745
  }
3743
3746
  function collectPathLines(scan, files) {
3744
3747
  const paths = /* @__PURE__ */ new Set();
3745
- for (const path of scan.fileTree.split(/\r?\n/)) {
3746
- const normalized = normalizePath2(path.trim());
3748
+ for (const path3 of scan.fileTree.split(/\r?\n/)) {
3749
+ const normalized = normalizePath2(path3.trim());
3747
3750
  if (normalized) {
3748
3751
  paths.add(normalized);
3749
3752
  }
@@ -3757,8 +3760,8 @@ function containsImport2(content, importName) {
3757
3760
  const escaped = importName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&").toLowerCase();
3758
3761
  return new RegExp(`(?:from\\s+['"]${escaped}['"]|import\\s*\\(\\s*['"]${escaped}['"]|require\\s*\\(\\s*['"]${escaped}['"])`).test(content);
3759
3762
  }
3760
- function isDocsLikePath(path) {
3761
- const normalized = normalizePath2(path);
3763
+ function isDocsLikePath(path3) {
3764
+ const normalized = normalizePath2(path3);
3762
3765
  return /(^|\/)(readme|product|spec)\.mdx?$/.test(normalized) || /(^|\/)docs\/.*\.mdx?$/.test(normalized);
3763
3766
  }
3764
3767
  function stripComments(content) {
@@ -3770,8 +3773,8 @@ function compareRows(a, b3) {
3770
3773
  function compareEvidence(a, b3) {
3771
3774
  return b3.points - a.points || a.kind.localeCompare(b3.kind) || (a.file ?? "").localeCompare(b3.file ?? "");
3772
3775
  }
3773
- function normalizePath2(path) {
3774
- return path.replace(/\\/g, "/").toLowerCase();
3776
+ function normalizePath2(path3) {
3777
+ return path3.replace(/\\/g, "/").toLowerCase();
3775
3778
  }
3776
3779
  function rowPriority(row) {
3777
3780
  if (row.roles.includes("live-verified")) {
@@ -3966,8 +3969,8 @@ Return ONLY the JSON object \u2014 no markdown, no explanation.
3966
3969
  }
3967
3970
 
3968
3971
  // ../../src/station/envEvidence.ts
3969
- function normalizeEvidencePath(path) {
3970
- return path.replace(/\\/g, "/");
3972
+ function normalizeEvidencePath(path3) {
3973
+ return path3.replace(/\\/g, "/");
3971
3974
  }
3972
3975
  function uniquePaths(paths) {
3973
3976
  return Array.from(new Set(paths.map(normalizeEvidencePath)));
@@ -4035,7 +4038,7 @@ function collectEnvVarEvidence(scan, names) {
4035
4038
  present: true,
4036
4039
  mode,
4037
4040
  source: "non-secret-content",
4038
- evidence: uniquePaths(contentEvidence).map((path) => `file: ${path}`)
4041
+ evidence: uniquePaths(contentEvidence).map((path3) => `file: ${path3}`)
4039
4042
  };
4040
4043
  }
4041
4044
  if (nonEmptyAssignmentEvidence.length > 0) {
@@ -4044,7 +4047,7 @@ function collectEnvVarEvidence(scan, names) {
4044
4047
  present: true,
4045
4048
  mode: "unknown",
4046
4049
  source: "non-secret-content",
4047
- evidence: uniquePaths(nonEmptyAssignmentEvidence).map((path) => `file: ${path}`)
4050
+ evidence: uniquePaths(nonEmptyAssignmentEvidence).map((path3) => `file: ${path3}`)
4048
4051
  };
4049
4052
  }
4050
4053
  if (nameOnlyEvidence.length > 0) {
@@ -4053,7 +4056,7 @@ function collectEnvVarEvidence(scan, names) {
4053
4056
  present: true,
4054
4057
  mode: "unknown",
4055
4058
  source: "variable-name-only",
4056
- evidence: uniquePaths(nameOnlyEvidence).map((path) => `file: ${path}`)
4059
+ evidence: uniquePaths(nameOnlyEvidence).map((path3) => `file: ${path3}`)
4057
4060
  };
4058
4061
  }
4059
4062
  if (secretEvidence.length > 0) {
@@ -4062,7 +4065,7 @@ function collectEnvVarEvidence(scan, names) {
4062
4065
  present: false,
4063
4066
  mode: "unknown",
4064
4067
  source: "secret-file-path",
4065
- evidence: secretEvidence.map((path) => `secret file: ${path}`)
4068
+ evidence: secretEvidence.map((path3) => `secret file: ${path3}`)
4066
4069
  };
4067
4070
  }
4068
4071
  return {
@@ -4157,10 +4160,10 @@ ${pathBlob}`),
4157
4160
  function visibleFiles(scan) {
4158
4161
  return scan.files.filter((file) => !file.isSecret && typeof file.content === "string");
4159
4162
  }
4160
- function foundOrMissing(id, label, found, evidence) {
4163
+ function foundOrMissing(id, label2, found, evidence) {
4161
4164
  return {
4162
4165
  id,
4163
- label,
4166
+ label: label2,
4164
4167
  status: found ? "found" : "missing",
4165
4168
  evidence: unique(evidence).slice(0, 6)
4166
4169
  };
@@ -4197,7 +4200,7 @@ function evidenceFor(files, pattern) {
4197
4200
  return files.filter((file) => pattern.test(file.content)).map((file) => `file: ${normalizePath3(file.path)}`).slice(0, 6);
4198
4201
  }
4199
4202
  function pathEvidence(scan, pattern) {
4200
- return scan.files.map((file) => normalizePath3(file.path)).filter((path) => pattern.test(path)).map((path) => `file: ${path}`).slice(0, 6);
4203
+ return scan.files.map((file) => normalizePath3(file.path)).filter((path3) => pattern.test(path3)).map((path3) => `file: ${path3}`).slice(0, 6);
4201
4204
  }
4202
4205
  function isClientReachableFile(file) {
4203
4206
  const content = file.content;
@@ -4213,22 +4216,22 @@ function isClientReachableFile(file) {
4213
4216
  function hasUseClientDirective(content) {
4214
4217
  return /^(?:\s|;|\/\/[^\n]*(?:\n|$)|\/\*[\s\S]*?\*\/)*['"]use client['"]/.test(content);
4215
4218
  }
4216
- function isBrowserOnlyPath(path) {
4217
- const normalized = normalizePath3(path).toLowerCase();
4218
- if (isServerOnlyPath(path)) {
4219
+ function isBrowserOnlyPath(path3) {
4220
+ const normalized = normalizePath3(path3).toLowerCase();
4221
+ if (isServerOnlyPath(path3)) {
4219
4222
  return false;
4220
4223
  }
4221
4224
  return /(^|\/)(components|hooks|contexts|providers)\//.test(normalized) || /\.(jsx|tsx)$/.test(normalized);
4222
4225
  }
4223
- function isServerOnlyPath(path) {
4224
- const normalized = normalizePath3(path).toLowerCase();
4226
+ function isServerOnlyPath(path3) {
4227
+ const normalized = normalizePath3(path3).toLowerCase();
4225
4228
  return /\/api\/|app\/api\/|pages\/api\/|route\.[jt]s$|\.server\.[jt]sx?$|\/server\//.test(normalized);
4226
4229
  }
4227
- function isEnvOrDocPath(path) {
4228
- return ENV_OR_DOC_PATH.test(normalizePath3(path).toLowerCase());
4230
+ function isEnvOrDocPath(path3) {
4231
+ return ENV_OR_DOC_PATH.test(normalizePath3(path3).toLowerCase());
4229
4232
  }
4230
- function normalizePath3(path) {
4231
- return path.replace(/\\/g, "/");
4233
+ function normalizePath3(path3) {
4234
+ return path3.replace(/\\/g, "/");
4232
4235
  }
4233
4236
  function unique(values) {
4234
4237
  return [...new Set(values)];
@@ -5339,7 +5342,7 @@ ${scan.files.map((file) => file.path).join("\n")}`.replace(/\\/g, "/").toLowerCa
5339
5342
  ];
5340
5343
  const passedCount = items.filter((entry) => entry.status === "passed").length;
5341
5344
  const totalCount = items.length;
5342
- const readinessPercent = Math.round(passedCount / Math.max(totalCount, 1) * 100);
5345
+ const readinessPercent2 = Math.round(passedCount / Math.max(totalCount, 1) * 100);
5343
5346
  return {
5344
5347
  key: "supabase-database",
5345
5348
  provider: "supabase",
@@ -5350,7 +5353,7 @@ ${scan.files.map((file) => file.path).join("\n")}`.replace(/\\/g, "/").toLowerCa
5350
5353
  items,
5351
5354
  passedCount,
5352
5355
  totalCount,
5353
- readinessPercent
5356
+ readinessPercent: readinessPercent2
5354
5357
  };
5355
5358
  }
5356
5359
  function visibleFiles2(scan) {
@@ -5361,10 +5364,10 @@ function visibleFiles2(scan) {
5361
5364
  lowerContent: file.content.toLowerCase()
5362
5365
  }));
5363
5366
  }
5364
- function item(id, label, passed, evidence, promptHint) {
5367
+ function item(id, label2, passed, evidence, promptHint) {
5365
5368
  return {
5366
5369
  id,
5367
- label,
5370
+ label: label2,
5368
5371
  status: passed ? "passed" : "missing",
5369
5372
  evidence,
5370
5373
  promptHint
@@ -5394,7 +5397,7 @@ function hasSupabaseClient(files, pathBlob) {
5394
5397
  }
5395
5398
  function clientEvidence(files, pathBlob) {
5396
5399
  const evidence = [];
5397
- const pathMatch = pathBlob.split(/\r?\n/).find((path) => /(^|\/)(lib|utils|src\/lib|src\/utils)\/supabase\.[jt]s\b/i.test(path));
5400
+ const pathMatch = pathBlob.split(/\r?\n/).find((path3) => /(^|\/)(lib|utils|src\/lib|src\/utils)\/supabase\.[jt]s\b/i.test(path3));
5398
5401
  if (pathMatch) {
5399
5402
  evidence.push(`file: ${pathMatch}`);
5400
5403
  }
@@ -5411,11 +5414,11 @@ function hasSchemaOrMigration(files, pathBlob) {
5411
5414
  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));
5412
5415
  }
5413
5416
  function schemaEvidence(files, pathBlob) {
5414
- const path = pathBlob.split(/\r?\n/).find(
5417
+ const path3 = pathBlob.split(/\r?\n/).find(
5415
5418
  (entry) => /(^|\/)supabase\/migrations\/[^/]+\.sql\b/i.test(entry) || /(^|\/)(migrations?|schema)\/[^/]+\.(sql|ts|js)\b/i.test(entry)
5416
5419
  );
5417
- if (path) {
5418
- return [`schema: ${path}`];
5420
+ if (path3) {
5421
+ return [`schema: ${path3}`];
5419
5422
  }
5420
5423
  const file = files.find((entry) => /create\s+table|alter\s+table/i.test(entry.content));
5421
5424
  return file ? [`schema: ${file.path}`] : [];
@@ -5424,9 +5427,9 @@ function hasRlsEvidence(files, pathBlob) {
5424
5427
  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));
5425
5428
  }
5426
5429
  function rlsEvidence(files, pathBlob) {
5427
- const path = pathBlob.split(/\r?\n/).find((entry) => /\/policies\/|_rls\.sql|\brls\b/i.test(entry));
5428
- if (path) {
5429
- return [`rls: ${path}`];
5430
+ const path3 = pathBlob.split(/\r?\n/).find((entry) => /\/policies\/|_rls\.sql|\brls\b/i.test(entry));
5431
+ if (path3) {
5432
+ return [`rls: ${path3}`];
5430
5433
  }
5431
5434
  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));
5432
5435
  return file ? [`rls: ${file.path}`] : [];
@@ -5435,11 +5438,11 @@ function hasGeneratedTypes(files, pathBlob) {
5435
5438
  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));
5436
5439
  }
5437
5440
  function generatedTypeEvidence(files, pathBlob) {
5438
- const path = pathBlob.split(/\r?\n/).find(
5441
+ const path3 = pathBlob.split(/\r?\n/).find(
5439
5442
  (entry) => /database\.types\.[jt]s\b|supabase.*types\.[jt]s\b|types\/database\.[jt]s\b/i.test(entry)
5440
5443
  );
5441
- if (path) {
5442
- return [`types: ${path}`];
5444
+ if (path3) {
5445
+ return [`types: ${path3}`];
5443
5446
  }
5444
5447
  const file = files.find((entry) => /export\s+type\s+database\b|export\s+interface\s+database\b/i.test(entry.content));
5445
5448
  return file ? [`types: ${file.path}`] : [];
@@ -5456,8 +5459,8 @@ function serviceRoleSafetyItem(files) {
5456
5459
  promptHint: "Move service-role usage to server-only code and use public anon keys in frontend clients."
5457
5460
  };
5458
5461
  }
5459
- function isClientExecutedPath(path) {
5460
- return /\.(tsx|jsx)$/.test(path) || /(^|\/)(components|pages|app|client|frontend|web)\//.test(path) || /\.client\.[jt]sx?$/.test(path);
5462
+ function isClientExecutedPath(path3) {
5463
+ return /\.(tsx|jsx)$/.test(path3) || /(^|\/)(components|pages|app|client|frontend|web)\//.test(path3) || /\.client\.[jt]sx?$/.test(path3);
5461
5464
  }
5462
5465
 
5463
5466
  // ../../src/station/stackWiring.ts
@@ -6282,7 +6285,7 @@ function analyzeSecretsHygiene(ctx) {
6282
6285
  promptSubject: "secrets hygiene",
6283
6286
  items: [
6284
6287
  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."),
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."),
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."),
6286
6289
  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."),
6287
6290
  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."),
6288
6291
  manualItem("production-secret-rotation-checked", "Production secret rotation checked", "Confirm production secrets can be rotated and revoked in provider dashboards.")
@@ -6358,29 +6361,29 @@ function summarize(summary) {
6358
6361
  readinessPercent: Math.round(passedCount / Math.max(totalCount, 1) * 100)
6359
6362
  };
6360
6363
  }
6361
- function item2(id, label, passed, evidence, promptHint) {
6364
+ function item2(id, label2, passed, evidence, promptHint) {
6362
6365
  return {
6363
6366
  id,
6364
- label,
6367
+ label: label2,
6365
6368
  status: passed ? "passed" : "missing",
6366
6369
  evidence,
6367
6370
  promptHint
6368
6371
  };
6369
6372
  }
6370
- function manualItem(id, label, promptHint) {
6373
+ function manualItem(id, label2, promptHint) {
6371
6374
  return {
6372
6375
  id,
6373
- label,
6376
+ label: label2,
6374
6377
  status: "manual",
6375
6378
  evidence: [],
6376
6379
  promptHint
6377
6380
  };
6378
6381
  }
6379
- function secretSafetyItem(ctx, id, label, pattern, promptHint) {
6382
+ function secretSafetyItem(ctx, id, label2, pattern, promptHint) {
6380
6383
  const exposed = ctx.files.filter((file) => isClientExecutedPath2(file.normalizedPath) && pattern.test(file.content));
6381
6384
  return {
6382
6385
  id,
6383
- label,
6386
+ label: label2,
6384
6387
  status: exposed.length > 0 ? "missing" : "passed",
6385
6388
  evidence: exposed.slice(0, 4).map((file) => `unsafe reference: ${file.path}`),
6386
6389
  promptHint
@@ -6405,13 +6408,13 @@ function envEvidence2(ctx, patterns) {
6405
6408
  return evidence.slice(0, 4);
6406
6409
  }
6407
6410
  function pathEvidence2(ctx, pattern) {
6408
- return ctx.pathBlob.split(/\r?\n/).filter((path) => pattern.test(path)).slice(0, 4).map((path) => `file: ${path}`);
6411
+ return ctx.pathBlob.split(/\r?\n/).filter((path3) => pattern.test(path3)).slice(0, 4).map((path3) => `file: ${path3}`);
6409
6412
  }
6410
6413
  function fileEvidence(ctx, pattern) {
6411
6414
  return ctx.files.filter((file) => pattern.test(file.path) || pattern.test(file.content)).slice(0, 4).map((file) => `file: ${file.path}`);
6412
6415
  }
6413
- function isClientExecutedPath2(path) {
6414
- return /\.(tsx|jsx)$/.test(path) || /(^|\/)(components|pages|app|client|frontend|web)\//.test(path) || /\.client\.[jt]sx?$/.test(path);
6416
+ function isClientExecutedPath2(path3) {
6417
+ return /\.(tsx|jsx)$/.test(path3) || /(^|\/)(components|pages|app|client|frontend|web)\//.test(path3) || /\.client\.[jt]sx?$/.test(path3);
6415
6418
  }
6416
6419
 
6417
6420
  // ../../src/station/verification.ts
@@ -6540,27 +6543,27 @@ function emptyArea(area) {
6540
6543
  manual: []
6541
6544
  };
6542
6545
  }
6543
- function normalizePath4(path) {
6544
- return path.replace(/\\/g, "/").replace(/^[\s\u2500\u2502\u2514\u251c>*+-]+/u, "").trim().toLowerCase();
6546
+ function normalizePath4(path3) {
6547
+ return path3.replace(/\\/g, "/").replace(/^[\s\u2500\u2502\u2514\u251c>*+-]+/u, "").trim().toLowerCase();
6545
6548
  }
6546
- function addFound(area, label, source, detail) {
6547
- addItem(area, "found", label, source, detail);
6549
+ function addFound(area, label2, source, detail) {
6550
+ addItem(area, "found", label2, source, detail);
6548
6551
  }
6549
- function addMissing(area, label, source, detail) {
6550
- addItem(area, "missing", label, source, detail);
6552
+ function addMissing(area, label2, source, detail) {
6553
+ addItem(area, "missing", label2, source, detail);
6551
6554
  }
6552
- function addManual(area, label, source, detail) {
6553
- addItem(area, "manual", label, source, detail);
6555
+ function addManual(area, label2, source, detail) {
6556
+ addItem(area, "manual", label2, source, detail);
6554
6557
  }
6555
- function addItem(area, status, label, source, detail) {
6556
- const item3 = compactItem(label, status, source, detail);
6558
+ function addItem(area, status, label2, source, detail) {
6559
+ const item3 = compactItem(label2, status, source, detail);
6557
6560
  const bucket = area[status];
6558
6561
  if (!bucket.some((existing) => existing.label === item3.label)) {
6559
6562
  bucket.push(item3);
6560
6563
  }
6561
6564
  }
6562
- function compactItem(label, status, source, detail) {
6563
- return detail ? { label, status, source, detail } : { label, status, source };
6565
+ function compactItem(label2, status, source, detail) {
6566
+ return detail ? { label: label2, status, source, detail } : { label: label2, status, source };
6564
6567
  }
6565
6568
  function hasDep(ctx, patterns) {
6566
6569
  return patterns.some((pattern) => {
@@ -6569,7 +6572,7 @@ function hasDep(ctx, patterns) {
6569
6572
  });
6570
6573
  }
6571
6574
  function hasPath(ctx, patterns) {
6572
- return [...ctx.paths].some((path) => !ctx.secretPaths.has(path) && patterns.some((pattern) => pattern.test(path)));
6575
+ return [...ctx.paths].some((path3) => !ctx.secretPaths.has(path3) && patterns.some((pattern) => pattern.test(path3)));
6573
6576
  }
6574
6577
  function hasContent(ctx, patterns) {
6575
6578
  return patterns.some((pattern) => pattern.test(ctx.contents));
@@ -7138,7 +7141,7 @@ var mockGitHubVerifier = {
7138
7141
  providerObservationMet: false,
7139
7142
  fixType: "mcp-connect",
7140
7143
  severity: "warning",
7141
- repoSignals: workflows.map((path) => `workflow: ${path}`),
7144
+ repoSignals: workflows.map((path3) => `workflow: ${path3}`),
7142
7145
  providerSignals: ["GitHub Actions API or MCP"],
7143
7146
  requiredEvidence: ["Latest workflow run status on default branch"]
7144
7147
  }),
@@ -7193,7 +7196,7 @@ var mockGitHubVerifier = {
7193
7196
  providerActual: "Not verified (mock \u2014 connect GitHub MCP read-only)",
7194
7197
  severity: "warning",
7195
7198
  suggestedFix: "mcp-connect",
7196
- evidenceRefs: workflows.map((path) => `workflow: ${path}`)
7199
+ evidenceRefs: workflows.map((path3) => `workflow: ${path3}`)
7197
7200
  }
7198
7201
  ];
7199
7202
  }
@@ -7992,15 +7995,15 @@ async function runProjectScan(options) {
7992
7995
 
7993
7996
  // src/artifacts.ts
7994
7997
  var import_promises5 = require("node:fs/promises");
7995
- var import_node_path9 = require("node:path");
7998
+ var import_node_path8 = require("node:path");
7996
7999
 
7997
8000
  // src/capabilities/classify.ts
7998
- function gapText(gap) {
7999
- return `${gap.id} ${gap.title} ${gap.detail} ${gap.primaryMapCategory}`.toLowerCase();
8001
+ function gapText(gap2) {
8002
+ return `${gap2.id} ${gap2.title} ${gap2.detail} ${gap2.primaryMapCategory}`.toLowerCase();
8000
8003
  }
8001
8004
  function statusForGaps(gaps) {
8002
- if (gaps.some((gap) => gap.severity === "critical")) return "critical";
8003
- if (gaps.some((gap) => gap.severity === "warning")) return "warning";
8005
+ if (gaps.some((gap2) => gap2.severity === "critical")) return "critical";
8006
+ if (gaps.some((gap2) => gap2.severity === "warning")) return "warning";
8004
8007
  if (gaps.length > 0) return "warning";
8005
8008
  return "unknown";
8006
8009
  }
@@ -8020,12 +8023,12 @@ function capabilityFromArea(area) {
8020
8023
  // src/capabilities/database.ts
8021
8024
  var databasePack = {
8022
8025
  key: "database",
8023
- classify(gap) {
8024
- const text = gapText(gap);
8026
+ classify(gap2) {
8027
+ const text = gapText(gap2);
8025
8028
  return /database|supabase|rls|migration|postgres|pooler|query/.test(text);
8026
8029
  },
8027
- riskTags(gap) {
8028
- const text = gapText(gap);
8030
+ riskTags(gap2) {
8031
+ const text = gapText(gap2);
8029
8032
  return [
8030
8033
  text.includes("rls") ? "rls" : "",
8031
8034
  text.includes("migration") ? "migration" : "",
@@ -8037,12 +8040,12 @@ var databasePack = {
8037
8040
  // src/capabilities/payments.ts
8038
8041
  var paymentsPack = {
8039
8042
  key: "payments",
8040
- classify(gap) {
8041
- const text = gapText(gap);
8043
+ classify(gap2) {
8044
+ const text = gapText(gap2);
8042
8045
  return /payment|stripe|checkout|billing|entitlement|subscription|refund|cancel/.test(text);
8043
8046
  },
8044
- riskTags(gap) {
8045
- const text = gapText(gap);
8047
+ riskTags(gap2) {
8048
+ const text = gapText(gap2);
8046
8049
  return [
8047
8050
  text.includes("entitlement") ? "entitlement-source" : "",
8048
8051
  text.includes("checkout") ? "checkout-flow" : "",
@@ -8054,12 +8057,12 @@ var paymentsPack = {
8054
8057
  // src/capabilities/scaling.ts
8055
8058
  var scalingPack = {
8056
8059
  key: "scaling",
8057
- classify(gap) {
8058
- const text = gapText(gap);
8060
+ classify(gap2) {
8061
+ const text = gapText(gap2);
8059
8062
  return /serverless|vercel|pooler|rate limit|rate-limit|cache|queue|cron|worker|runtime|connection/.test(text);
8060
8063
  },
8061
- riskTags(gap) {
8062
- const text = gapText(gap);
8064
+ riskTags(gap2) {
8065
+ const text = gapText(gap2);
8063
8066
  return [
8064
8067
  text.includes("serverless") || text.includes("vercel") ? "serverless" : "",
8065
8068
  text.includes("pooler") || text.includes("connection") ? "db-connection" : "",
@@ -8071,12 +8074,12 @@ var scalingPack = {
8071
8074
  // src/capabilities/security.ts
8072
8075
  var securityPack = {
8073
8076
  key: "security",
8074
- classify(gap) {
8075
- const text = gapText(gap);
8077
+ classify(gap2) {
8078
+ const text = gapText(gap2);
8076
8079
  return /secret|service role|token|api key|auth|authorization|browser-exposed|cors|csrf|session/.test(text);
8077
8080
  },
8078
- riskTags(gap) {
8079
- const text = gapText(gap);
8081
+ riskTags(gap2) {
8082
+ const text = gapText(gap2);
8080
8083
  return [
8081
8084
  text.includes("secret") || text.includes("api key") || text.includes("service role") ? "secret-boundary" : "",
8082
8085
  text.includes("auth") || text.includes("authorization") ? "auth-boundary" : "",
@@ -8088,12 +8091,12 @@ var securityPack = {
8088
8091
  // src/capabilities/webhooks.ts
8089
8092
  var webhooksPack = {
8090
8093
  key: "webhooks",
8091
- classify(gap) {
8092
- const text = gapText(gap);
8094
+ classify(gap2) {
8095
+ const text = gapText(gap2);
8093
8096
  return /webhook|signature|idempotency|retry|replay|dead-letter/.test(text);
8094
8097
  },
8095
- riskTags(gap) {
8096
- const text = gapText(gap);
8098
+ riskTags(gap2) {
8099
+ const text = gapText(gap2);
8097
8100
  return [
8098
8101
  text.includes("signature") ? "signature" : "",
8099
8102
  text.includes("idempotency") ? "idempotency" : "",
@@ -8104,10 +8107,10 @@ var webhooksPack = {
8104
8107
 
8105
8108
  // src/capabilities/index.ts
8106
8109
  var PACKS = [scalingPack, securityPack, webhooksPack, paymentsPack, databasePack];
8107
- function classifyGapCapability(gap) {
8108
- const direct = PACKS.find((pack) => pack.classify(gap));
8110
+ function classifyGapCapability(gap2) {
8111
+ const direct = PACKS.find((pack) => pack.classify(gap2));
8109
8112
  if (direct) return direct.key;
8110
- return capabilityFromArea(String(gap.primaryMapCategory)) ?? "security";
8113
+ return capabilityFromArea(String(gap2.primaryMapCategory)) ?? "security";
8111
8114
  }
8112
8115
  function summarizeCapabilities(gaps) {
8113
8116
  const result = Object.fromEntries(
@@ -8117,13 +8120,13 @@ function summarizeCapabilities(gaps) {
8117
8120
  ])
8118
8121
  );
8119
8122
  for (const pack of PACKS) {
8120
- const matching = gaps.filter((gap) => classifyGapCapability(gap) === pack.key);
8123
+ const matching = gaps.filter((gap2) => classifyGapCapability(gap2) === pack.key);
8121
8124
  result[pack.key] = {
8122
8125
  key: pack.key,
8123
8126
  status: statusForGaps(matching),
8124
- topGapIds: matching.slice(0, 5).map((gap) => gap.id),
8127
+ topGapIds: matching.slice(0, 5).map((gap2) => gap2.id),
8125
8128
  evidenceCount: matching.length,
8126
- riskTags: unique2(matching.flatMap((gap) => pack.riskTags(gap)))
8129
+ riskTags: unique2(matching.flatMap((gap2) => pack.riskTags(gap2)))
8127
8130
  };
8128
8131
  }
8129
8132
  return result;
@@ -8131,8 +8134,8 @@ function summarizeCapabilities(gaps) {
8131
8134
 
8132
8135
  // src/contracts/contextMap.ts
8133
8136
  function generateContextMap(artifact) {
8134
- const criticalCount = artifact.gaps.filter((gap) => gap.severity === "critical").length;
8135
- const warningCount = artifact.gaps.filter((gap) => gap.severity === "warning").length;
8137
+ const criticalCount = artifact.gaps.filter((gap2) => gap2.severity === "critical").length;
8138
+ const warningCount = artifact.gaps.filter((gap2) => gap2.severity === "warning").length;
8136
8139
  return {
8137
8140
  $schema: "https://viberaven.dev/schemas/context-map.schema.json",
8138
8141
  schemaVersion: "v1",
@@ -8165,12 +8168,12 @@ function generateContextMap(artifact) {
8165
8168
  warningCount,
8166
8169
  providerDashboardChecksRequired: true
8167
8170
  },
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)
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)
8174
8177
  }))
8175
8178
  };
8176
8179
  }
@@ -8185,10 +8188,10 @@ function runIdFrom(scannedAt) {
8185
8188
  return `vr_${scannedAt.replace(/\D/g, "").slice(0, 14) || "scan"}`;
8186
8189
  }
8187
8190
  function generateGateResult(artifact, options = {}) {
8188
- const criticalCount = artifact.gaps.filter((gap) => gap.severity === "critical").length;
8189
- const warningCount = artifact.gaps.filter((gap) => gap.severity === "warning").length;
8191
+ const criticalCount = artifact.gaps.filter((gap2) => gap2.severity === "critical").length;
8192
+ const warningCount = artifact.gaps.filter((gap2) => gap2.severity === "warning").length;
8190
8193
  const capabilities = summarizeCapabilities(artifact.gaps);
8191
- const topGapIds = artifact.gaps.slice(0, 10).map((gap) => gap.id);
8194
+ const topGapIds = artifact.gaps.slice(0, 10).map((gap2) => gap2.id);
8192
8195
  const firstGapId = topGapIds[0];
8193
8196
  return {
8194
8197
  $schema: "https://viberaven.dev/schemas/gate-result.schema.json",
@@ -8237,24 +8240,24 @@ function generateGateResult(artifact, options = {}) {
8237
8240
  }
8238
8241
 
8239
8242
  // src/contracts/gapEvidence.ts
8240
- function summarizeGap(gap) {
8241
- return `${gap.title}. See the tasklist and original scan for redacted detail.`;
8243
+ function summarizeGap(gap2) {
8244
+ return `${gap2.title}. See the tasklist and original scan for redacted detail.`;
8242
8245
  }
8243
8246
  function generateGapEvidenceFiles(artifact) {
8244
- return artifact.gaps.slice(0, 50).map((gap) => ({
8245
- path: `.viberaven/gaps/${gap.id}.json`,
8247
+ return artifact.gaps.slice(0, 50).map((gap2) => ({
8248
+ path: `.viberaven/gaps/${gap2.id}.json`,
8246
8249
  content: {
8247
8250
  $schema: "https://viberaven.dev/schemas/gap.schema.json",
8248
8251
  schemaVersion: "v1",
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 }],
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 }],
8255
8258
  commands: {
8256
- prompt: promptGapCommand(gap.id),
8257
- healPlan: healPlanGapCommand(gap.id),
8259
+ prompt: promptGapCommand(gap2.id),
8260
+ healPlan: healPlanGapCommand(gap2.id),
8258
8261
  verify: PUBLIC_VERIFY_COMMAND
8259
8262
  },
8260
8263
  providerBoundary: {
@@ -8303,39 +8306,6 @@ var PRODUCTION_MAP_CATEGORY_KEYS_ALL = PRODUCTION_MAP_LANES.map(
8303
8306
  (lane) => lane.extensionKey
8304
8307
  );
8305
8308
 
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
-
8339
8309
  // src/playbooks/loadPlaybook.ts
8340
8310
  var import_node_fs5 = require("node:fs");
8341
8311
  var import_promises3 = require("node:fs/promises");
@@ -8446,8 +8416,8 @@ async function loadPlaybook(provider2) {
8446
8416
  `Unknown provider "${provider2}". Available: ${PLAYBOOK_PROVIDERS.join(", ")}`
8447
8417
  );
8448
8418
  }
8449
- const path = (0, import_node_path5.join)(playbooksRoot(), `${normalized}.json`);
8450
- const raw = JSON.parse(await (0, import_promises3.readFile)(path, "utf-8"));
8419
+ const path3 = (0, import_node_path5.join)(playbooksRoot(), `${normalized}.json`);
8420
+ const raw = JSON.parse(await (0, import_promises3.readFile)(path3, "utf-8"));
8451
8421
  const playbook = parsePlaybook(raw);
8452
8422
  if (playbook.provider !== normalized) {
8453
8423
  throw new Error(`Playbook file ${normalized}.json has mismatched provider field`);
@@ -8461,8 +8431,8 @@ function loadPlaybookSync(provider2) {
8461
8431
  `Unknown provider "${provider2}". Available: ${PLAYBOOK_PROVIDERS.join(", ")}`
8462
8432
  );
8463
8433
  }
8464
- const path = (0, import_node_path5.join)(playbooksRoot(), `${normalized}.json`);
8465
- const raw = JSON.parse((0, import_node_fs5.readFileSync)(path, "utf-8"));
8434
+ const path3 = (0, import_node_path5.join)(playbooksRoot(), `${normalized}.json`);
8435
+ const raw = JSON.parse((0, import_node_fs5.readFileSync)(path3, "utf-8"));
8466
8436
  const playbook = parsePlaybook(raw);
8467
8437
  if (playbook.provider !== normalized) {
8468
8438
  throw new Error(`Playbook file ${normalized}.json has mismatched provider field`);
@@ -8470,28 +8440,35 @@ function loadPlaybookSync(provider2) {
8470
8440
  return playbook;
8471
8441
  }
8472
8442
 
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";
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;
8476
8464
  }
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
- }
8465
+ function envKeysForGap(gapId, provider2) {
8466
+ const gapKeys = GAP_KEYS[gapId];
8467
+ if (gapKeys) {
8468
+ return [...gapKeys];
8493
8469
  }
8494
- return refs;
8470
+ const providerKeys = PROVIDER_DEFAULT_KEYS[normalizeProvider2(provider2)];
8471
+ return providerKeys ? [...providerKeys] : [];
8495
8472
  }
8496
8473
 
8497
8474
  // src/tui/menu.ts
@@ -8545,10 +8522,10 @@ function formatTopGapsList(artifact, limit = 10) {
8545
8522
  if (sorted.length === 0) {
8546
8523
  return "No gaps found \u2014 production core looks solid.";
8547
8524
  }
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}`;
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}`;
8552
8529
  }).join("\n");
8553
8530
  }
8554
8531
  async function loadLastArtifact(startDir) {
@@ -8556,405 +8533,673 @@ async function loadLastArtifact(startDir) {
8556
8533
  if (!workspace) {
8557
8534
  throw new ScanNotFoundError(needsScanMessage(startDir));
8558
8535
  }
8559
- const path = (0, import_node_path6.join)(getProjectArtifactsDir(workspace), "last-scan.json");
8536
+ const path3 = (0, import_node_path6.join)(getProjectArtifactsDir(workspace), "last-scan.json");
8560
8537
  try {
8561
- const raw = await (0, import_promises4.readFile)(path, "utf-8");
8538
+ const raw = await (0, import_promises4.readFile)(path3, "utf-8");
8562
8539
  return JSON.parse(raw);
8563
8540
  } catch {
8564
8541
  throw new ScanNotFoundError(needsScanMessage(startDir));
8565
8542
  }
8566
8543
  }
8567
8544
 
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
- };
8578
- function unlockedKeys(artifact) {
8579
- const keys = artifact.usage?.unlockedMapCategoryKeys ?? FREE_TRIAL_UNLOCKED_MAP_CATEGORY_KEYS;
8580
- return new Set(keys);
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");
8581
8571
  }
8582
- function resolveNextAction(artifact) {
8583
- const unlocked = unlockedKeys(artifact);
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
- };
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;
8594
8592
  }
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;
8604
- }
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;
8605
8599
  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
8600
  provider: provider2,
8610
- playbookStep: 1,
8611
- command: `viberaven guide ${provider2} --step 1`,
8612
- openUrl
8613
- };
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
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
8645
8609
  };
8610
+ } catch {
8611
+ return void 0;
8646
8612
  }
8647
- return {
8648
- type: "done",
8649
- title: "No blockers found",
8650
- detail: "Re-scan after changes to confirm production readiness."
8651
- };
8652
8613
  }
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;
8614
+ function unlockedKeys(artifact) {
8615
+ const keys = artifact.usage?.unlockedMapCategoryKeys ?? FREE_TRIAL_UNLOCKED_MAP_CATEGORY_KEYS;
8616
+ return new Set(keys);
8617
+ }
8618
+ function buildTaskList(artifact) {
8619
+ 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";
8674
8633
  }
8675
- return a.title.localeCompare(b3.title);
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"
8642
+ };
8643
+ if (fixType === "repo-code") {
8644
+ base.mcpTool = "viberaven_heal_apply";
8645
+ base.mcpArgs = { gap: gap2.id, yes: true };
8646
+ base.requiresUserAction = false;
8647
+ if (gap2.copyPrompt) {
8648
+ base.exactFix = gap2.copyPrompt.trim();
8649
+ }
8650
+ } else if (fixType === "provider-action") {
8651
+ try {
8652
+ const provider2 = gapToPlaybookProvider(gap2);
8653
+ if (provider2) {
8654
+ const pa = buildProviderAction(gap2, provider2);
8655
+ if (pa) {
8656
+ base.providerAction = pa;
8657
+ base.action = pa.exactStep;
8658
+ }
8659
+ }
8660
+ } catch {
8661
+ base.fixType = "manual-verify";
8662
+ }
8663
+ base.requiresUserAction = true;
8664
+ }
8665
+ return base;
8676
8666
  });
8677
8667
  }
8678
- function generateAgentSummary(artifact) {
8679
- const lines = [];
8680
- const topGaps = sortGaps(artifact.gaps).slice(0, 8);
8681
- lines.push("# VibeRaven agent summary", "");
8682
- lines.push(`Scanned: \`${artifact.workspacePath}\``);
8683
- lines.push(`At: ${artifact.scannedAt}`);
8684
- lines.push(
8685
- `Production core: **${artifact.productionCorePercent}%** \xB7 Model score: **${artifact.score}** (${artifact.scoreLabel})`
8686
- );
8687
- lines.push("");
8688
- const next = resolveNextAction(artifact);
8689
- lines.push("## Next action", "");
8690
- lines.push(`**${next.title}** \u2014 ${next.detail}`);
8691
- if (next.command) {
8692
- lines.push(`Command: \`${next.command}\``);
8693
- }
8694
- if (next.upgradeUrl) {
8695
- lines.push(`Upgrade: ${next.upgradeUrl}`);
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";
8696
8671
  }
8697
- lines.push("");
8698
- const unlockedCount = artifact.usage?.unlockedMapCategoryKeys.length ?? 6;
8699
- if (unlockedCount < PRODUCTION_MAP_CATEGORY_KEYS_ALL.length) {
8700
- lines.push("## Locked lanes (Pro)", "");
8701
- lines.push(
8702
- "Free plan unlocks 6/12 mission map lanes. Pro unlocks deployment, monitoring, security, testing, onboarding, and error handling."
8703
- );
8704
- lines.push("");
8705
- for (const key of LOCKED_LANE_KEYS) {
8706
- lines.push(`- ${key}`);
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}\` `);
8707
8679
  }
8708
- lines.push("");
8709
- lines.push(`Upgrade: ${UPGRADE_URL2}`);
8710
- lines.push("");
8711
- }
8712
- lines.push("## Suggested stack", "");
8713
- lines.push(
8714
- "React + Tailwind + shadcn/ui + Supabase + Vercel (agent-default stack for lowest launch friction)"
8715
- );
8716
- lines.push("");
8717
- lines.push("## Summary");
8718
- lines.push(artifact.summary || "_No summary returned._");
8719
- lines.push("");
8720
- lines.push("## Mission map (repo wiring)");
8721
- lines.push("");
8722
- lines.push("| Area | Provider | Readiness | Notes |");
8723
- lines.push("|------|----------|-----------|-------|");
8724
- for (const area of artifact.missionGraph.areas ?? []) {
8725
- for (const mission of area.providerMissions) {
8726
- const failed = mission.checks.filter(
8727
- (c) => c.status === "missing" || c.status === "failed" || c.status === "needs-connection"
8728
- ).length;
8729
- const notes = failed > 0 ? `${failed} open check${failed === 1 ? "" : "s"}` : `${mission.readinessPercent}% repo checks`;
8730
- lines.push(
8731
- `| ${area.label} | ${mission.providerLabel} | ${mission.readinessPercent}% | ${notes} |`
8732
- );
8680
+ if (task.action) {
8681
+ lines.push(`**Action:** ${task.action} `);
8733
8682
  }
8734
- }
8735
- lines.push("");
8736
- lines.push("## Agent-code actions");
8737
- if (topGaps.length === 0) {
8738
- lines.push("_No model gaps returned. Review mission map checks before changing code._");
8739
- } else {
8740
- topGaps.forEach((gap, index) => {
8741
- lines.push(
8742
- `${index + 1}. **${gap.title}** (\`${gap.id}\`, ${gap.severity}, map: \`${gap.primaryMapCategory}\`)`
8743
- );
8744
- lines.push(` - ${gap.detail}`);
8745
- lines.push(` - Command: \`viberaven prompt --gap ${gap.id}\``);
8746
- if (gap.copyPrompt) {
8747
- lines.push(` - Prompt: ${gap.copyPrompt}`);
8748
- }
8749
- });
8750
- }
8751
- const manualChecks = (artifact.missionGraph.areas ?? []).flatMap(
8752
- (area) => area.providerMissions.flatMap(
8753
- (mission) => mission.checks.filter(
8754
- (check) => check.evidenceClass === "manual-dashboard" || check.evidenceClass === "mcp-verifier" || check.evidenceSource === "provider" || check.evidenceSource === "mcp" || check.status === "needs-connection" || check.status === "unknown"
8755
- ).map((check) => ({ area: area.label, provider: mission.providerLabel, check }))
8756
- )
8757
- );
8758
- lines.push("");
8759
- lines.push("## Human-provider actions");
8760
- if (manualChecks.length === 0) {
8761
- lines.push("_No manual provider actions were identified in this scan._");
8762
- } else {
8763
- manualChecks.slice(0, 8).forEach((item3, index) => {
8764
- lines.push(`${index + 1}. **${item3.check.label}** (${item3.area} / ${item3.provider})`);
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) {
8765
8690
  lines.push(
8766
- ` - ${item3.check.promptHint || "Ask the user to confirm this in the provider dashboard or through read-only MCP."}`
8691
+ `**MCP:** \`${task.mcpTool} ${JSON.stringify(task.mcpArgs)}\` `
8767
8692
  );
8768
- });
8769
- }
8770
- lines.push("");
8771
- lines.push("Do not claim human-provider actions as repo-code fixes.");
8772
- lines.push("");
8773
- lines.push("## Agent workflow");
8774
- lines.push("1. Read `.viberaven/agent-tasklist.md` first for the production gate.");
8775
- lines.push("2. Read `.viberaven/launch-playbook.md` for the full checklist.");
8776
- lines.push("3. Run `viberaven next --json` - one action at a time.");
8777
- lines.push("4. Repo fix: `viberaven prompt --gap <id>` then implement.");
8778
- lines.push("5. Provider: `viberaven guide <provider> --step N` and `viberaven open <provider>`.");
8779
- lines.push(`6. Run \`${PUBLIC_VERIFY_COMMAND}\` to rescan and refresh \`.viberaven/agent-tasklist.md\`.`);
8780
- lines.push("");
8781
- if (artifact.usage) {
8782
- lines.push("## Account usage");
8783
- lines.push(
8784
- `- Plan: ${artifact.usage.plan} \xB7 Scans used: ${artifact.usage.used}/${artifact.usage.limit} (${artifact.usage.period})`
8785
- );
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
+ }
8786
8706
  lines.push("");
8787
8707
  }
8788
8708
  return `${lines.join("\n")}
8789
8709
  `;
8790
8710
  }
8791
8711
 
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");
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
+ );
8818
8734
  }
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;
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;
8831
8755
  }
8832
- function hasPlaybook(gap) {
8833
- try {
8834
- const provider2 = gapToPlaybookProvider(gap);
8835
- if (!provider2) return false;
8836
- return PLAYBOOK_PROVIDERS.includes(provider2);
8837
- } catch {
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) {
8838
8782
  return false;
8839
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 };
8840
8836
  }
8841
- function buildProviderAction(gap, provider2) {
8842
- try {
8843
- const playbook = loadPlaybookSync(provider2);
8844
- const step = playbook.steps[0];
8845
- if (!step) return void 0;
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
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,
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",
8857
8855
  actionClass: "manual_provider_step",
8858
- approvalRequired: true,
8859
- manualFallback: `Open ${step.openUrl ?? `https://${provider2}.com`} and complete: ${step.instruction}`,
8860
- copyValues: []
8856
+ command: promptGapCommand(task.gapId)
8861
8857
  };
8862
- } catch {
8863
- return void 0;
8864
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";
8865
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
+ };
8866
8995
  function unlockedKeys2(artifact) {
8867
8996
  const keys = artifact.usage?.unlockedMapCategoryKeys ?? FREE_TRIAL_UNLOCKED_MAP_CATEGORY_KEYS;
8868
8997
  return new Set(keys);
8869
8998
  }
8870
- function buildTaskList(artifact) {
8999
+ function resolveNextAction(artifact) {
8871
9000
  const unlocked = unlockedKeys2(artifact);
8872
- const sorted = sortGapsByPriority(artifact.gaps);
8873
- return sorted.map((gap, index) => {
8874
- const id = `TASK-${String(index + 1).padStart(3, "0")}`;
8875
- const isLocked = !unlocked.has(gap.primaryMapCategory);
8876
- let fixType;
8877
- if (isLocked) {
8878
- fixType = "upgrade-required";
8879
- } else if (hasRecipe(gap.id)) {
8880
- fixType = "repo-code";
8881
- } else if (hasPlaybook(gap)) {
8882
- fixType = "provider-action";
8883
- } else {
8884
- fixType = "manual-verify";
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;
8885
9021
  }
8886
- const base = {
8887
- id,
8888
- gapId: gap.id,
8889
- severity: gap.severity,
8890
- fixType,
8891
- title: gap.title,
8892
- verifyCommand: PUBLIC_VERIFY_COMMAND,
8893
- requiresUserAction: fixType !== "repo-code"
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
8894
9030
  };
8895
- if (fixType === "repo-code") {
8896
- base.mcpTool = "viberaven_heal_apply";
8897
- base.mcpArgs = { gap: gap.id, yes: true };
8898
- base.requiresUserAction = false;
8899
- if (gap.copyPrompt) {
8900
- base.exactFix = gap.copyPrompt.trim();
8901
- }
8902
- } else if (fixType === "provider-action") {
8903
- try {
8904
- const provider2 = gapToPlaybookProvider(gap);
8905
- if (provider2) {
8906
- const pa = buildProviderAction(gap, provider2);
8907
- if (pa) {
8908
- base.providerAction = pa;
8909
- base.action = pa.exactStep;
8910
- }
8911
- }
8912
- } catch {
8913
- base.fixType = "manual-verify";
8914
- }
8915
- base.requiresUserAction = true;
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;
8916
9091
  }
8917
- return base;
9092
+ return a.title.localeCompare(b3.title);
8918
9093
  });
8919
9094
  }
8920
- function buildTaskListMarkdown(tasks) {
8921
- if (tasks.length === 0) {
8922
- return "# VibeRaven Agent Tasklist\n\n_No gaps found \u2014 production core looks solid._\n";
9095
+ function generateAgentSummary(artifact) {
9096
+ const lines = [];
9097
+ const topGaps = sortGaps(artifact.gaps).slice(0, 8);
9098
+ lines.push("# VibeRaven agent summary", "");
9099
+ lines.push(`Scanned: \`${artifact.workspacePath}\``);
9100
+ lines.push(`At: ${artifact.scannedAt}`);
9101
+ lines.push(
9102
+ `Production core: **${artifact.productionCorePercent}%** \xB7 Model score: **${artifact.score}** (${artifact.scoreLabel})`
9103
+ );
9104
+ lines.push("");
9105
+ const next = resolveNextAction(artifact);
9106
+ lines.push("## Next action", "");
9107
+ lines.push(`**${next.title}** \u2014 ${next.detail}`);
9108
+ if (next.command) {
9109
+ lines.push(`Command: \`${next.command}\``);
8923
9110
  }
8924
- const lines = ["# VibeRaven Agent Tasklist", ""];
8925
- for (const task of tasks) {
8926
- const severityLabel = task.severity.toUpperCase();
8927
- lines.push(`## ${task.id} \xB7 ${task.gapId} \xB7 ${severityLabel}`, "");
8928
- lines.push(`**Fix type:** ${task.fixType} `);
8929
- if (task.file) {
8930
- lines.push(`**File:** \`${task.file}\` `);
8931
- }
8932
- if (task.action) {
8933
- lines.push(`**Action:** ${task.action} `);
8934
- }
8935
- if (task.exactFix) {
8936
- lines.push(`**Exact fix:** ${task.exactFix} `);
8937
- } else {
8938
- lines.push(`**Exact fix:** No automated recipe \u2014 see scanner hint. `);
9111
+ if (next.upgradeUrl) {
9112
+ lines.push(`Upgrade: ${next.upgradeUrl}`);
9113
+ }
9114
+ lines.push("");
9115
+ const unlockedCount = artifact.usage?.unlockedMapCategoryKeys.length ?? 6;
9116
+ if (unlockedCount < PRODUCTION_MAP_CATEGORY_KEYS_ALL.length) {
9117
+ lines.push("## Locked lanes (Pro)", "");
9118
+ lines.push(
9119
+ "Free plan unlocks 6/12 mission map lanes. Pro unlocks deployment, monitoring, security, testing, onboarding, and error handling."
9120
+ );
9121
+ lines.push("");
9122
+ for (const key of LOCKED_LANE_KEYS) {
9123
+ lines.push(`- ${key}`);
8939
9124
  }
8940
- lines.push(`**Verify:** \`${task.verifyCommand}\` `);
8941
- if (task.mcpTool && task.mcpArgs) {
9125
+ lines.push("");
9126
+ lines.push(`Upgrade: ${UPGRADE_URL2}`);
9127
+ lines.push("");
9128
+ }
9129
+ lines.push("## Suggested stack", "");
9130
+ lines.push(
9131
+ "React + Tailwind + shadcn/ui + Supabase + Vercel (agent-default stack for lowest launch friction)"
9132
+ );
9133
+ lines.push("");
9134
+ lines.push("## Summary");
9135
+ lines.push(artifact.summary || "_No summary returned._");
9136
+ lines.push("");
9137
+ lines.push("## Mission map (repo wiring)");
9138
+ lines.push("");
9139
+ lines.push("| Area | Provider | Readiness | Notes |");
9140
+ lines.push("|------|----------|-----------|-------|");
9141
+ for (const area of artifact.missionGraph.areas ?? []) {
9142
+ for (const mission of area.providerMissions) {
9143
+ const failed = mission.checks.filter(
9144
+ (c) => c.status === "missing" || c.status === "failed" || c.status === "needs-connection"
9145
+ ).length;
9146
+ const notes = failed > 0 ? `${failed} open check${failed === 1 ? "" : "s"}` : `${mission.readinessPercent}% repo checks`;
8942
9147
  lines.push(
8943
- `**MCP:** \`${task.mcpTool} ${JSON.stringify(task.mcpArgs)}\` `
9148
+ `| ${area.label} | ${mission.providerLabel} | ${mission.readinessPercent}% | ${notes} |`
8944
9149
  );
8945
9150
  }
8946
- lines.push(`**Requires user action:** ${task.requiresUserAction}`);
8947
- if (task.providerAction) {
8948
- const pa = task.providerAction;
8949
- lines.push("");
8950
- lines.push("**Provider action:**");
8951
- lines.push(`- Provider: ${pa.provider}`);
8952
- lines.push(`- Dashboard: ${pa.dashboardUrl}`);
8953
- lines.push(`- Step: ${pa.exactStep}`);
8954
- if (pa.envKeyName) lines.push(`- Env key: \`${pa.envKeyName}\``);
8955
- if (pa.doneSignal) lines.push(`- Done when: ${pa.doneSignal}`);
8956
- if (pa.mcpAlternative) lines.push(`- MCP alternative: \`${pa.mcpAlternative}\``);
8957
- }
9151
+ }
9152
+ lines.push("");
9153
+ lines.push("## Agent-code actions");
9154
+ if (topGaps.length === 0) {
9155
+ lines.push("_No model gaps returned. Review mission map checks before changing code._");
9156
+ } else {
9157
+ topGaps.forEach((gap2, index) => {
9158
+ lines.push(
9159
+ `${index + 1}. **${gap2.title}** (\`${gap2.id}\`, ${gap2.severity}, map: \`${gap2.primaryMapCategory}\`)`
9160
+ );
9161
+ lines.push(` - ${gap2.detail}`);
9162
+ lines.push(` - Command: \`viberaven prompt --gap ${gap2.id}\``);
9163
+ if (gap2.copyPrompt) {
9164
+ lines.push(` - Prompt: ${gap2.copyPrompt}`);
9165
+ }
9166
+ });
9167
+ }
9168
+ const manualChecks = (artifact.missionGraph.areas ?? []).flatMap(
9169
+ (area) => area.providerMissions.flatMap(
9170
+ (mission) => mission.checks.filter(
9171
+ (check) => check.evidenceClass === "manual-dashboard" || check.evidenceClass === "mcp-verifier" || check.evidenceSource === "provider" || check.evidenceSource === "mcp" || check.status === "needs-connection" || check.status === "unknown"
9172
+ ).map((check) => ({ area: area.label, provider: mission.providerLabel, check }))
9173
+ )
9174
+ );
9175
+ lines.push("");
9176
+ lines.push("## Human-provider actions");
9177
+ if (manualChecks.length === 0) {
9178
+ lines.push("_No manual provider actions were identified in this scan._");
9179
+ } else {
9180
+ manualChecks.slice(0, 8).forEach((item3, index) => {
9181
+ lines.push(`${index + 1}. **${item3.check.label}** (${item3.area} / ${item3.provider})`);
9182
+ lines.push(
9183
+ ` - ${item3.check.promptHint || "Ask the user to confirm this in the provider dashboard or through read-only MCP."}`
9184
+ );
9185
+ });
9186
+ }
9187
+ lines.push("");
9188
+ lines.push("Do not claim human-provider actions as repo-code fixes.");
9189
+ lines.push("");
9190
+ lines.push("## Agent workflow");
9191
+ lines.push("1. Read `.viberaven/agent-tasklist.md` first for the production gate.");
9192
+ lines.push("2. Read `.viberaven/launch-playbook.md` for the full checklist.");
9193
+ lines.push("3. Run `viberaven next --json` - one action at a time.");
9194
+ lines.push("4. Repo fix: `viberaven prompt --gap <id>` then implement.");
9195
+ lines.push("5. Provider: `viberaven guide <provider> --step N` and `viberaven open <provider>`.");
9196
+ lines.push(`6. Run \`${PUBLIC_VERIFY_COMMAND}\` to rescan and refresh \`.viberaven/agent-tasklist.md\`.`);
9197
+ lines.push("");
9198
+ if (artifact.usage) {
9199
+ lines.push("## Account usage");
9200
+ lines.push(
9201
+ `- Plan: ${artifact.usage.plan} \xB7 Scans used: ${artifact.usage.used}/${artifact.usage.limit} (${artifact.usage.period})`
9202
+ );
8958
9203
  lines.push("");
8959
9204
  }
8960
9205
  return `${lines.join("\n")}
@@ -8974,13 +9219,13 @@ function generateLaunchPlaybook(artifact) {
8974
9219
  lines.push(`Generated from scan at ${artifact.scannedAt}. Work top to bottom.`, "");
8975
9220
  lines.push("## Repo fixes (agent code)", "");
8976
9221
  const repoGaps = sortGapsByPriority(artifact.gaps).filter(
8977
- (gap) => unlocked.has(gap.primaryMapCategory)
9222
+ (gap2) => unlocked.has(gap2.primaryMapCategory)
8978
9223
  );
8979
9224
  if (repoGaps.length === 0) {
8980
9225
  lines.push("_No repo-code gaps in unlocked lanes._", "");
8981
9226
  } else {
8982
- for (const gap of repoGaps) {
8983
- lines.push(`- [ ] ${gap.title} \u2014 \`viberaven prompt --gap ${gap.id}\``);
9227
+ for (const gap2 of repoGaps) {
9228
+ lines.push(`- [ ] ${gap2.title} \u2014 \`viberaven prompt --gap ${gap2.id}\``);
8984
9229
  }
8985
9230
  lines.push("");
8986
9231
  }
@@ -9000,14 +9245,14 @@ function generateLaunchPlaybook(artifact) {
9000
9245
  if (!isPro) {
9001
9246
  lines.push("## [Pro] Deployment, monitoring, and full map", "");
9002
9247
  const proGaps = sortGapsByPriority(artifact.gaps).filter(
9003
- (gap) => !unlocked.has(gap.primaryMapCategory)
9248
+ (gap2) => !unlocked.has(gap2.primaryMapCategory)
9004
9249
  );
9005
9250
  const proManuals = collectManualChecks(artifact).filter((item3) => !unlocked.has(item3.mapCategory));
9006
9251
  if (proGaps.length === 0 && proManuals.length === 0) {
9007
9252
  lines.push("_No Pro-only blockers detected._", "");
9008
9253
  } else {
9009
- for (const gap of proGaps.slice(0, 6)) {
9010
- lines.push(`- [ ] ${gap.title} [Pro] \u2014 upgrade at ${UPGRADE_URL3}`);
9254
+ for (const gap2 of proGaps.slice(0, 6)) {
9255
+ lines.push(`- [ ] ${gap2.title} [Pro] \u2014 upgrade at ${UPGRADE_URL3}`);
9011
9256
  }
9012
9257
  for (const item3 of proManuals.slice(0, 6)) {
9013
9258
  lines.push(`- [ ] ${item3.check.label} [Pro] \u2014 upgrade at ${UPGRADE_URL3}`);
@@ -9132,6 +9377,21 @@ function getStationHtml(nonce, cssUri, jsUri, options) {
9132
9377
  </div>
9133
9378
  </header>
9134
9379
 
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
+
9135
9395
  <div class="studio-workspace">
9136
9396
  <nav class="studio-nav-rail" aria-label="Studio areas">
9137
9397
  <span class="studio-nav-rail__item studio-nav-rail__item--active" aria-label="Architecture">ARCH</span>
@@ -9363,227 +9623,28 @@ var REPORT_ASSET_FILES = [
9363
9623
  "assets/provider-logrocket.svg"
9364
9624
  ];
9365
9625
 
9366
- // src/sanitizeArtifact.ts
9367
- var INLINE_SECRET_PATTERNS = [
9368
- /\b(sk_(?:live|test)_[A-Za-z0-9]{12,}|sk-proj-[A-Za-z0-9_-]{16,}|sk-[A-Za-z0-9_-]{20,})\b/g,
9369
- /\b(whsec_[A-Za-z0-9]{12,}|rk_(?:live|test)_[A-Za-z0-9]{12,})\b/g,
9370
- /\bghp_[A-Za-z0-9]{36,}\b/g,
9371
- /\bgithub_pat_[A-Za-z0-9_]{50,}\b/g,
9372
- /\bxox[bp]-[A-Za-z0-9-]{20,}\b/g,
9373
- /\bxapp-[A-Za-z0-9-]{20,}\b/g,
9374
- /\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\b/g,
9375
- /-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z ]*PRIVATE KEY-----/g
9376
- ];
9377
- var SENSITIVE_ENV_KEYS = /(?:^|_)(?:ACCESS_TOKEN|AUTHORIZATION|API_KEY|SECRET|SECRET_KEY|SERVICE_ROLE_KEY|TOKEN|PASSWORD|PRIVATE_KEY|CREDENTIALS?)(?:$|_)/i;
9378
- function redactString(value) {
9379
- let out = value;
9380
- for (const pattern of INLINE_SECRET_PATTERNS) {
9381
- out = out.replace(pattern, pattern.source.includes("PRIVATE KEY") ? "[REDACTED_PRIVATE_KEY]" : "[REDACTED_SECRET]");
9382
- }
9383
- out = out.replace(/\bAuthorization\s*:\s*([A-Za-z][A-Za-z0-9._-]*)\s+[^\s;,]+/gi, "Authorization: $1 [REDACTED]");
9384
- return out.replace(
9385
- /\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,
9386
- "$1=[REDACTED]"
9387
- );
9388
- }
9389
- function redactUnknown(value) {
9390
- if (typeof value === "string") {
9391
- return redactString(value);
9392
- }
9393
- if (Array.isArray(value)) {
9394
- return value.map(redactUnknown);
9395
- }
9396
- if (value && typeof value === "object") {
9397
- const record = value;
9398
- const next = {};
9399
- for (const [key, entry] of Object.entries(record)) {
9400
- if (SENSITIVE_ENV_KEYS.test(key)) {
9401
- next[key] = "[REDACTED]";
9402
- } else {
9403
- next[key] = redactUnknown(entry);
9404
- }
9405
- }
9406
- return next;
9407
- }
9408
- return value;
9409
- }
9410
- function sanitizeArtifactForDisk(artifact) {
9411
- return redactUnknown(artifact);
9412
- }
9413
-
9414
- // src/version.ts
9415
- var VERSION = "1.1.0";
9416
-
9417
- // src/prp/buildPrp.ts
9418
- var import_node_path8 = require("node:path");
9419
-
9420
- // src/prp/types.ts
9421
- var PRP_SCHEMA_URL = "https://schemas.viberaven.dev/prp/v0.1/schema.json";
9422
- var PRP_PROTOCOL = "viberaven-production-protocol";
9423
- var PRP_PROTOCOL_VERSION = "0.1.0";
9424
-
9425
- // src/prp/buildPrp.ts
9426
- var ONE_DAY_MS = 24 * 60 * 60 * 1e3;
9427
- var REDACTED = "<redacted>";
9428
- var SENSITIVE_JSON_KEY_PATTERN = /secret|token|api[_-]?key|apikey|password|private[_-]?key|database[_-]?url|connection[_-]?string|service[_-]?role|role[_-]?key/i;
9429
- function buildProductionProtocolReport(options) {
9430
- const profile = options.profile ?? "launch";
9431
- const generatedAt = options.artifact.scannedAt;
9432
- const expiresAt = new Date(new Date(generatedAt).getTime() + ONE_DAY_MS).toISOString();
9433
- const evidence = buildEvidence(options.artifact);
9434
- const nextActions = buildNextActions(options.tasks);
9435
- const findings = buildFindings(options.artifact, nextActions);
9436
- return {
9437
- $schema: PRP_SCHEMA_URL,
9438
- protocol: PRP_PROTOCOL,
9439
- protocolVersion: PRP_PROTOCOL_VERSION,
9440
- producer: {
9441
- name: "viberaven",
9442
- version: options.producerVersion
9443
- },
9444
- subject: {
9445
- projectName: sanitizePrpText((0, import_node_path8.basename)(options.artifact.workspacePath) || "workspace"),
9446
- framework: sanitizePrpText(options.artifact.archetype || "unknown"),
9447
- packageManager: "unknown",
9448
- deploymentTarget: sanitizePrpText(options.artifact.selectedProviders?.deployment ?? "unknown"),
9449
- environment: "production"
9450
- },
9451
- profile,
9452
- decision: {
9453
- status: resolveDecisionStatus(options.artifact),
9454
- generatedAt,
9455
- expiresAt,
9456
- summary: summarizeDecision(options.artifact)
9457
- },
9458
- findings,
9459
- evidence,
9460
- nextActions,
9461
- agentInstructions: {
9462
- mustRead: [".viberaven/prp.json", ".viberaven/mission-map.md", ".viberaven/context-map.json"],
9463
- doNotDeployUntil: [
9464
- "decision.status is not blocked",
9465
- "critical nextActions are resolved or explicitly accepted"
9466
- ]
9467
- }
9468
- };
9469
- }
9470
- function resolveDecisionStatus(artifact) {
9471
- if (artifact.gaps.some((gap) => gap.severity === "critical")) return "blocked";
9472
- if (artifact.gaps.some((gap) => gap.severity === "warning")) return "warning";
9473
- return "clear";
9474
- }
9475
- function summarizeDecision(artifact) {
9476
- const critical = artifact.gaps.filter((gap) => gap.severity === "critical").length;
9477
- const warning = artifact.gaps.filter((gap) => gap.severity === "warning").length;
9478
- if (critical > 0) return `${critical} production blocker${critical === 1 ? "" : "s"} found`;
9479
- if (warning > 0) return `${warning} production warning${warning === 1 ? "" : "s"} found`;
9480
- return "No production blockers found";
9481
- }
9482
- function buildEvidence(artifact) {
9483
- return artifact.gaps.map((gap) => {
9484
- const gapId = safePrpId(gap.id);
9485
- return {
9486
- id: `evidence.${gapId}.repo`,
9487
- kind: "repo",
9488
- status: evidenceStatusForGapSeverity(gap.severity),
9489
- source: sanitizePrpText(String(gap.file ?? gap.primaryMapCategory ?? "repo")),
9490
- summary: sanitizePrpText(gap.detail || gap.title || gap.id)
9491
- };
9492
- });
9493
- }
9494
- function evidenceStatusForGapSeverity(severity) {
9495
- if (severity === "critical" || severity === "warning") return "missing";
9496
- return "inconclusive";
9497
- }
9498
- function buildFindings(artifact, nextActions) {
9499
- return artifact.gaps.map((gap) => {
9500
- const gapId = safePrpId(gap.id);
9501
- return {
9502
- id: gapId,
9503
- severity: gap.severity,
9504
- category: sanitizePrpText(String(gap.primaryMapCategory ?? "unknown")),
9505
- summary: sanitizePrpText(gap.title || gap.id),
9506
- evidenceRefs: [`evidence.${gapId}.repo`],
9507
- nextActionRefs: nextActions.some((action) => action.gapId === sanitizePrpText(gap.id)) ? [`action.${gapId}`] : []
9508
- };
9509
- });
9510
- }
9511
- function buildNextActions(tasks) {
9512
- return tasks.map((task) => ({
9513
- id: `action.${safePrpId(task.gapId)}`,
9514
- title: sanitizePrpText(task.title),
9515
- actionClass: actionClassForTask(task),
9516
- requiresUserAction: task.requiresUserAction,
9517
- approvalRequired: task.requiresUserAction,
9518
- verifyCommand: sanitizePrpText(task.verifyCommand || PUBLIC_VERIFY_COMMAND),
9519
- gapId: sanitizePrpText(task.gapId),
9520
- provider: sanitizeOptionalPrpText(task.providerAction?.provider),
9521
- dashboardUrl: sanitizeOptionalPrpText(task.providerAction?.dashboardUrl),
9522
- manualFallback: sanitizeOptionalPrpText(task.providerAction?.exactStep ?? task.action),
9523
- mcpTool: sanitizeOptionalPrpText(task.mcpTool),
9524
- mcpArgs: sanitizePrpJsonObject(task.mcpArgs)
9525
- }));
9526
- }
9527
- function actionClassForTask(task) {
9528
- if (task.fixType === "repo-code") return "local_repo_write";
9529
- if (task.fixType === "provider-action") return "manual_provider_step";
9530
- if (task.fixType === "manual-verify") return "local_repo_read";
9531
- return "manual_provider_step";
9532
- }
9533
- function safeId(value) {
9534
- return value.trim().toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "") || "unknown";
9535
- }
9536
- function safePrpId(value) {
9537
- return safeId(sanitizePrpText(value));
9538
- }
9539
- function sanitizeOptionalPrpText(value) {
9540
- return value === void 0 ? void 0 : sanitizePrpText(value);
9541
- }
9542
- function sanitizePrpJsonObject(value) {
9543
- return value === void 0 ? void 0 : sanitizePrpJsonValue(value);
9544
- }
9545
- function sanitizePrpJsonValue(value) {
9546
- if (typeof value === "string") return sanitizePrpText(value);
9547
- if (Array.isArray(value)) return value.map((item3) => sanitizePrpJsonValue(item3));
9548
- if (value && typeof value === "object") {
9549
- return Object.fromEntries(
9550
- Object.entries(value).map(([key, item3]) => [
9551
- sanitizePrpText(key),
9552
- SENSITIVE_JSON_KEY_PATTERN.test(key) ? REDACTED : sanitizePrpJsonValue(item3)
9553
- ])
9554
- );
9555
- }
9556
- return value;
9557
- }
9558
- function sanitizePrpText(value) {
9559
- return value.replace(/-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z ]*PRIVATE KEY-----/g, REDACTED).replace(/\b[a-z][a-z0-9+.-]*:\/\/[^\s/@]+:[^\s/@]+@[^\s,;)]+/gi, REDACTED).replace(/\b(?:postgres(?:ql)?|mongodb(?:\+srv)?|redis(?:s)?|mysql|mariadb):\/\/[^\s/@:]+:[^\s/@]+@[^\s,;)]+/gi, REDACTED).replace(/\b(?:redis(?:s)?):\/\/:[^\s/@]+@[^\s,;)]+/gi, REDACTED).replace(
9560
- /\b([A-Za-z_][A-Za-z0-9_]*(?:SECRET|TOKEN|PASSWORD|PRIVATE[_-]?KEY|API[_-]?KEY|APIKEY|SERVICE[_-]?ROLE|DATABASE[_-]?URL|CONNECTION[_-]?STRING)[A-Za-z0-9_]*)\s*=\s*(?:"[^"]*"|'[^']*'|[^\s,;]+)/gi,
9561
- `$1=${REDACTED}`
9562
- ).replace(/\bBearer\s+[A-Za-z0-9._~+/=-]{8,}/gi, `Bearer ${REDACTED}`).replace(/(^|[^A-Za-z0-9])whsec_[A-Za-z0-9_]+/g, `$1${REDACTED}`).replace(/(^|[^A-Za-z0-9])sk_(live|test)_[A-Za-z0-9_]+/g, `$1${REDACTED}`).replace(/(^|[^A-Za-z0-9])pk_(live|test)_[A-Za-z0-9_]+/g, `$1${REDACTED}`).replace(/(^|[^A-Za-z0-9])sk-(?:proj-)?[A-Za-z0-9_-]{8,}/g, `$1${REDACTED}`).replace(/(^|[^A-Za-z0-9])gh[oprsu]_[A-Za-z0-9_]{16,}/g, `$1${REDACTED}`).replace(/(^|[^A-Za-z0-9])github_pat_[A-Za-z0-9_]{16,}/g, `$1${REDACTED}`).replace(/(^|[^A-Za-z0-9])(sb|supabase|vercel|stripe)_[A-Za-z0-9_]*\d[A-Za-z0-9_]{15,}/gi, `$1${REDACTED}`).replace(/(^|[^A-Za-z0-9_-])[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}(?=$|[^A-Za-z0-9_-])/g, `$1${REDACTED}`).replace(/\b[A-Za-z0-9+/]{32,}={0,2}\b/g, REDACTED);
9563
- }
9564
-
9565
9626
  // src/artifacts.ts
9566
9627
  async function copyReportAssets(reportAssetsDir) {
9567
9628
  const sourceDir = getBundledReportAssetsDir();
9568
- await (0, import_promises5.mkdir)((0, import_node_path9.join)(reportAssetsDir, "assets"), { recursive: true });
9629
+ await (0, import_promises5.mkdir)((0, import_node_path8.join)(reportAssetsDir, "assets"), { recursive: true });
9569
9630
  for (const rel of REPORT_ASSET_FILES) {
9570
- await (0, import_promises5.copyFile)((0, import_node_path9.join)(sourceDir, rel), (0, import_node_path9.join)(reportAssetsDir, rel));
9631
+ await (0, import_promises5.copyFile)((0, import_node_path8.join)(sourceDir, rel), (0, import_node_path8.join)(reportAssetsDir, rel));
9571
9632
  }
9572
9633
  }
9573
9634
  async function writeScanArtifacts(options) {
9574
9635
  const cwd = options.cwd ?? options.artifact.workspacePath;
9575
9636
  const dir = getProjectArtifactsDir(cwd);
9576
9637
  await (0, import_promises5.mkdir)(dir, { recursive: true });
9577
- const jsonPath = (0, import_node_path9.join)(dir, "last-scan.json");
9578
- const gateResultPath = (0, import_node_path9.join)(dir, "gate-result.json");
9579
- const contextMapPath = (0, import_node_path9.join)(dir, "context-map.json");
9580
- const gapsDir = (0, import_node_path9.join)(dir, "gaps");
9581
- const prpPath = (0, import_node_path9.join)(dir, "prp.json");
9582
- const tasklistPath = (0, import_node_path9.join)(dir, "agent-tasklist.md");
9583
- const summaryPath = (0, import_node_path9.join)(dir, "agent-summary.md");
9584
- const playbookPath = (0, import_node_path9.join)(dir, "launch-playbook.md");
9585
- const reportPath = (0, import_node_path9.join)(dir, "report.html");
9586
- const reportAssetsDir = (0, import_node_path9.join)(dir, "report");
9638
+ const jsonPath = (0, import_node_path8.join)(dir, "last-scan.json");
9639
+ const gateResultPath = (0, import_node_path8.join)(dir, "gate-result.json");
9640
+ const contextMapPath = (0, import_node_path8.join)(dir, "context-map.json");
9641
+ const prpPath = (0, import_node_path8.join)(dir, "prp.json");
9642
+ const gapsDir = (0, import_node_path8.join)(dir, "gaps");
9643
+ const tasklistPath = (0, import_node_path8.join)(dir, "agent-tasklist.md");
9644
+ const summaryPath = (0, import_node_path8.join)(dir, "agent-summary.md");
9645
+ const playbookPath = (0, import_node_path8.join)(dir, "launch-playbook.md");
9646
+ const reportPath = (0, import_node_path8.join)(dir, "report.html");
9647
+ const reportAssetsDir = (0, import_node_path8.join)(dir, "report");
9587
9648
  await (0, import_promises5.mkdir)(gapsDir, { recursive: true });
9588
9649
  const safe = sanitizeArtifactForDisk(options.artifact);
9589
9650
  const json = `${JSON.stringify(safe, null, 2)}
@@ -9593,25 +9654,25 @@ async function writeScanArtifacts(options) {
9593
9654
  const html = generateReportHtml(safe);
9594
9655
  const tasks = buildTaskList(safe);
9595
9656
  const tasklist = buildTaskListMarkdown(tasks);
9596
- const prp = `${JSON.stringify(buildProductionProtocolReport({
9597
- artifact: safe,
9598
- tasks,
9599
- producerVersion: VERSION
9600
- }), null, 2)}
9601
- `;
9602
9657
  const gateResult = `${JSON.stringify(generateGateResult(safe), null, 2)}
9603
9658
  `;
9604
9659
  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
+ )}
9605
9666
  `;
9606
9667
  const gapEvidenceFiles = generateGapEvidenceFiles(safe);
9607
9668
  await copyReportAssets(reportAssetsDir);
9608
9669
  await (0, import_promises5.writeFile)(gateResultPath, gateResult, "utf-8");
9609
9670
  await (0, import_promises5.writeFile)(contextMapPath, contextMap, "utf-8");
9610
- for (const gap of gapEvidenceFiles) {
9611
- await (0, import_promises5.writeFile)((0, import_node_path9.join)(dir, "gaps", `${gap.content.id}.json`), `${JSON.stringify(gap.content, null, 2)}
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)}
9612
9674
  `, "utf-8");
9613
9675
  }
9614
- await (0, import_promises5.writeFile)(prpPath, prp, "utf-8");
9615
9676
  await (0, import_promises5.writeFile)(tasklistPath, tasklist, "utf-8");
9616
9677
  await (0, import_promises5.writeFile)(jsonPath, json, "utf-8");
9617
9678
  await (0, import_promises5.writeFile)(summaryPath, summary, "utf-8");
@@ -9622,8 +9683,8 @@ async function writeScanArtifacts(options) {
9622
9683
  jsonPath,
9623
9684
  gateResultPath,
9624
9685
  contextMapPath,
9625
- gapsDir,
9626
9686
  prpPath,
9687
+ gapsDir,
9627
9688
  tasklistPath,
9628
9689
  summaryPath,
9629
9690
  playbookPath,
@@ -9659,28 +9720,6 @@ function renderJsonlEvents(result) {
9659
9720
  `;
9660
9721
  }
9661
9722
 
9662
- // src/output/prpSummary.ts
9663
- function renderProductionProtocolSummary(prp) {
9664
- const nextAction = prp.nextActions[0];
9665
- const lines = [
9666
- "",
9667
- "VibeRaven Production Protocol",
9668
- "",
9669
- `Profile: ${prp.profile}`,
9670
- `Decision: ${prp.decision.status.toUpperCase()}`,
9671
- "Canonical artifact: .viberaven/prp.json",
9672
- ""
9673
- ];
9674
- if (nextAction) {
9675
- lines.push("Next action:", nextAction.title, "");
9676
- lines.push("Why:", prp.decision.summary);
9677
- } else {
9678
- lines.push("Next action:", "No production blockers found.", "");
9679
- lines.push("Why:", prp.decision.summary);
9680
- }
9681
- return lines.join("\n");
9682
- }
9683
-
9684
9723
  // src/commands/strictGate.ts
9685
9724
  function exitCodeForStrictGate(result, options = {}) {
9686
9725
  if (result.gate.status === "error") return 2;
@@ -9840,10 +9879,10 @@ function printScanSummary(artifact, paths) {
9840
9879
  const modelGaps = artifact.gaps.filter((g2) => g2.primaryMapCategory === area.key).length;
9841
9880
  const tag = modelGaps > 0 ? ` GAP ${modelGaps}` : open > 0 ? ` ${open} fix` : "";
9842
9881
  const tagColored = tag ? gapTagColor(modelGaps, open)(tag) : "";
9843
- const label = area.label.padEnd(18);
9882
+ const label2 = area.label.padEnd(18);
9844
9883
  const provider2 = mission.providerLabel.padEnd(14);
9845
9884
  const readiness = readinessColor(mission.readinessPercent)(`${mission.readinessPercent}%`);
9846
- console.log(` ${import_picocolors.default.dim(label)} ${provider2} ${readiness}${tagColored}`);
9885
+ console.log(` ${import_picocolors.default.dim(label2)} ${provider2} ${readiness}${tagColored}`);
9847
9886
  }
9848
9887
  console.log("");
9849
9888
  console.log(import_picocolors.default.bold("Artifacts:"));
@@ -9891,7 +9930,7 @@ function printScanSummary(artifact, paths) {
9891
9930
 
9892
9931
  // src/runnerConnect.ts
9893
9932
  var import_promises6 = require("node:fs/promises");
9894
- var import_node_path10 = require("node:path");
9933
+ var import_node_path9 = require("node:path");
9895
9934
  var import_node_child_process3 = require("node:child_process");
9896
9935
  var import_node_crypto = require("node:crypto");
9897
9936
 
@@ -9970,8 +10009,8 @@ function validateSafeFixJobInput(kind, rawInput) {
9970
10009
  }
9971
10010
  };
9972
10011
  }
9973
- function validateSafeFixRelativePath(path) {
9974
- const trimmed = path.trim();
10012
+ function validateSafeFixRelativePath(path3) {
10013
+ const trimmed = path3.trim();
9975
10014
  if (!trimmed) {
9976
10015
  return { ok: false, reason: "Safe fix path must not be empty." };
9977
10016
  }
@@ -9986,11 +10025,11 @@ function validateSafeFixRelativePath(path) {
9986
10025
  if (segments.some((segment) => segment === ".git" || segment === "node_modules")) {
9987
10026
  return { ok: false, reason: "Safe fix path targets a blocked directory." };
9988
10027
  }
9989
- const basename4 = segments.at(-1)?.toLowerCase() ?? "";
9990
- if (basename4 !== ".env.example" && (basename4 === ".env" || basename4.startsWith(".env."))) {
10028
+ const basename3 = segments.at(-1)?.toLowerCase() ?? "";
10029
+ if (basename3 !== ".env.example" && (basename3 === ".env" || basename3.startsWith(".env."))) {
9991
10030
  return { ok: false, reason: "Safe fix path targets an environment secret file." };
9992
10031
  }
9993
- if (isSecretLikeFilename(basename4)) {
10032
+ if (isSecretLikeFilename(basename3)) {
9994
10033
  return { ok: false, reason: "Safe fix path targets a secret-like file." };
9995
10034
  }
9996
10035
  if (!isAllowedSafeFixPath(normalized)) {
@@ -10013,11 +10052,11 @@ var SECRET_VALUE_PATTERNS = [
10013
10052
  /\bwhsec_[A-Za-z0-9]{12,}\b/,
10014
10053
  /\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\b/
10015
10054
  ];
10016
- function isAllowedSafeFixPath(path) {
10017
- if (path === ".env.example") {
10055
+ function isAllowedSafeFixPath(path3) {
10056
+ if (path3 === ".env.example") {
10018
10057
  return true;
10019
10058
  }
10020
- const segments = path.split("/");
10059
+ const segments = path3.split("/");
10021
10060
  if (segments.length !== 2) {
10022
10061
  return false;
10023
10062
  }
@@ -10239,7 +10278,7 @@ async function createSafeFixFile(job, input, targetPath) {
10239
10278
  } catch {
10240
10279
  }
10241
10280
  try {
10242
- await (0, import_promises6.mkdir)((0, import_node_path10.dirname)(targetPath), { recursive: true });
10281
+ await (0, import_promises6.mkdir)((0, import_node_path9.dirname)(targetPath), { recursive: true });
10243
10282
  await writeNewFileAtomically(targetPath, input.content);
10244
10283
  } catch {
10245
10284
  return safeFixNeedsUser(job, "SAFE_FIX_WRITE_FAILED", `Could not create ${input.path}.`);
@@ -10307,12 +10346,12 @@ function safeFixNeedsUser(job, code, message) {
10307
10346
  };
10308
10347
  }
10309
10348
  async function resolveSafeFixTarget(workspaceRoot, relativePath) {
10310
- const root = await (0, import_promises6.realpath)(workspaceRoot).catch(() => (0, import_node_path10.resolve)(workspaceRoot));
10311
- const target = (0, import_node_path10.resolve)(root, relativePath);
10349
+ const root = await (0, import_promises6.realpath)(workspaceRoot).catch(() => (0, import_node_path9.resolve)(workspaceRoot));
10350
+ const target = (0, import_node_path9.resolve)(root, relativePath);
10312
10351
  if (!isInsideRoot(root, target)) {
10313
10352
  return { ok: false, reason: "Safe fix target escaped the workspace root." };
10314
10353
  }
10315
- const parent = (0, import_node_path10.dirname)(target);
10354
+ const parent = (0, import_node_path9.dirname)(target);
10316
10355
  const parentReal = await realpathNearestExisting(parent, root);
10317
10356
  if (!isInsideRoot(root, parentReal)) {
10318
10357
  return { ok: false, reason: "Safe fix parent path escaped the workspace root." };
@@ -10323,14 +10362,14 @@ async function resolveSafeFixTarget(workspaceRoot, relativePath) {
10323
10362
  }
10324
10363
  return { ok: true, path: target };
10325
10364
  }
10326
- async function realpathNearestExisting(path, root) {
10327
- let candidate = path;
10365
+ async function realpathNearestExisting(path3, root) {
10366
+ let candidate = path3;
10328
10367
  while (isInsideRoot(root, candidate)) {
10329
10368
  try {
10330
10369
  await (0, import_promises6.lstat)(candidate);
10331
10370
  return (0, import_promises6.realpath)(candidate);
10332
10371
  } catch {
10333
- const next = (0, import_node_path10.dirname)(candidate);
10372
+ const next = (0, import_node_path9.dirname)(candidate);
10334
10373
  if (next === candidate) {
10335
10374
  break;
10336
10375
  }
@@ -10340,8 +10379,8 @@ async function realpathNearestExisting(path, root) {
10340
10379
  return root;
10341
10380
  }
10342
10381
  function isInsideRoot(root, candidate) {
10343
- const rel = (0, import_node_path10.relative)(root, candidate);
10344
- return rel === "" || !rel.startsWith("..") && !(0, import_node_path10.isAbsolute)(rel);
10382
+ const rel = (0, import_node_path9.relative)(root, candidate);
10383
+ return rel === "" || !rel.startsWith("..") && !(0, import_node_path9.isAbsolute)(rel);
10345
10384
  }
10346
10385
  async function writeNewFileAtomically(targetPath, content) {
10347
10386
  const tempPath = tempPathFor(targetPath);
@@ -10362,7 +10401,7 @@ async function replaceFileAtomically(targetPath, content) {
10362
10401
  }
10363
10402
  }
10364
10403
  function tempPathFor(targetPath) {
10365
- return (0, import_node_path10.join)((0, import_node_path10.dirname)(targetPath), `.${(0, import_node_path10.basename)(targetPath)}.${(0, import_node_crypto.randomUUID)()}.tmp`);
10404
+ return (0, import_node_path9.join)((0, import_node_path9.dirname)(targetPath), `.${(0, import_node_path9.basename)(targetPath)}.${(0, import_node_crypto.randomUUID)()}.tmp`);
10366
10405
  }
10367
10406
  async function executePackageScriptJob(job, scriptName, options) {
10368
10407
  const packageJson = await readPackageJson(options.workspaceRoot);
@@ -10455,8 +10494,8 @@ function buildStationScanProof(kind, artifact) {
10455
10494
  `gaps: ${artifact.gaps.length}`,
10456
10495
  `summary: ${artifact.summary || "No summary returned."}`
10457
10496
  ];
10458
- for (const gap of artifact.gaps.slice(0, 5)) {
10459
- evidence.push(`gap: ${gap.title} (${gap.severity})`);
10497
+ for (const gap2 of artifact.gaps.slice(0, 5)) {
10498
+ evidence.push(`gap: ${gap2.title} (${gap2.severity})`);
10460
10499
  }
10461
10500
  for (const area of (artifact.missionGraph.areas ?? []).slice(0, 6)) {
10462
10501
  for (const mission of area.providerMissions.slice(0, 1)) {
@@ -10522,13 +10561,13 @@ function summarizeSafeNoopJob(job) {
10522
10561
  proofItems: [proofItemForJob(job, "runner_summary", "Runner summary", summary, [])]
10523
10562
  };
10524
10563
  }
10525
- function proofItemForJob(job, kind, label, summary, evidence, redactionSecrets = []) {
10564
+ function proofItemForJob(job, kind, label2, summary, evidence, redactionSecrets = []) {
10526
10565
  return {
10527
10566
  deploySessionId: job.deploySessionId,
10528
10567
  runnerSessionId: job.runnerSessionId,
10529
10568
  jobId: job.id,
10530
10569
  kind,
10531
- label,
10570
+ label: label2,
10532
10571
  summary: redactRunnerProofText(summary, redactionSecrets),
10533
10572
  evidence: evidence.map((value) => redactRunnerProofText(value, redactionSecrets)),
10534
10573
  redacted: true
@@ -10536,7 +10575,7 @@ function proofItemForJob(job, kind, label, summary, evidence, redactionSecrets =
10536
10575
  }
10537
10576
  async function readPackageJson(workspaceRoot) {
10538
10577
  try {
10539
- const raw = await (0, import_promises6.readFile)((0, import_node_path10.join)(workspaceRoot, "package.json"), "utf-8");
10578
+ const raw = await (0, import_promises6.readFile)((0, import_node_path9.join)(workspaceRoot, "package.json"), "utf-8");
10540
10579
  const parsed = JSON.parse(raw);
10541
10580
  if (isRecord6(parsed) && isRecord6(parsed.scripts)) {
10542
10581
  return { scripts: Object.fromEntries(Object.entries(parsed.scripts).filter(([, value]) => typeof value === "string")) };
@@ -10551,10 +10590,10 @@ function packageScriptArgs(command, scriptName) {
10551
10590
  }
10552
10591
  return ["run", scriptName];
10553
10592
  }
10554
- function summarizeCommandOutput(label, result, redactionSecrets = []) {
10593
+ function summarizeCommandOutput(label2, result, redactionSecrets = []) {
10555
10594
  const lines = `${result.stdout}
10556
10595
  ${result.stderr ?? ""}`.split(/\r?\n/).map((line) => redactRunnerProofText(line.trim(), redactionSecrets)).filter(Boolean).slice(0, 8);
10557
- return [`${label} ${result.ok ? "succeeded" : "failed"}`, ...lines];
10596
+ return [`${label2} ${result.ok ? "succeeded" : "failed"}`, ...lines];
10558
10597
  }
10559
10598
  function redactRunnerProofText(value, additionalSecrets = []) {
10560
10599
  let out = redactAdditionalSecrets(value, additionalSecrets);
@@ -10619,7 +10658,7 @@ async function collectLocalRepoMetadata(workspaceRoot, commandRunner = runComman
10619
10658
  const headSha = headResult.ok ? normalizeSha(headResult.stdout) : null;
10620
10659
  const dirty = statusResult.ok ? statusResult.stdout.trim().length > 0 : void 0;
10621
10660
  return {
10622
- rootName: (0, import_node_path10.basename)(workspaceRoot) || "workspace",
10661
+ rootName: (0, import_node_path9.basename)(workspaceRoot) || "workspace",
10623
10662
  remotes: remoteResult.ok ? parseGitRemotes(remoteResult.stdout) : [],
10624
10663
  branch,
10625
10664
  headSha,
@@ -10637,7 +10676,7 @@ async function detectPackageManager(workspaceRoot) {
10637
10676
  ];
10638
10677
  for (const [manager, file] of checks) {
10639
10678
  try {
10640
- await (0, import_promises6.access)((0, import_node_path10.join)(workspaceRoot, file));
10679
+ await (0, import_promises6.access)((0, import_node_path9.join)(workspaceRoot, file));
10641
10680
  return manager;
10642
10681
  } catch {
10643
10682
  }
@@ -10828,7 +10867,7 @@ function isRecord6(value) {
10828
10867
  }
10829
10868
 
10830
10869
  // src/tui/runInteractive.ts
10831
- var import_node_path15 = require("node:path");
10870
+ var import_node_path14 = require("node:path");
10832
10871
 
10833
10872
  // ../../../../node_modules/@clack/core/dist/index.mjs
10834
10873
  var import_sisteransi = __toESM(require_src(), 1);
@@ -11367,12 +11406,12 @@ var import_picocolors4 = __toESM(require_picocolors());
11367
11406
  function compact(value) {
11368
11407
  return String(value ?? "").replace(/\s+/g, " ").trim();
11369
11408
  }
11370
- function originalHint(gap) {
11371
- const hint = compact(gap.copyPrompt);
11409
+ function originalHint(gap2) {
11410
+ const hint = compact(gap2.copyPrompt);
11372
11411
  return hint ? hint : "No scanner hint was returned for this gap.";
11373
11412
  }
11374
- function buildAgentFixPrompt(artifact, gap) {
11375
- const affected = [gap.primaryMapCategory, ...gap.affectedMapCategories ?? []].filter(Boolean).filter((value, index, all) => all.indexOf(value) === index).join(", ");
11413
+ function buildAgentFixPrompt(artifact, gap2) {
11414
+ const affected = [gap2.primaryMapCategory, ...gap2.affectedMapCategories ?? []].filter(Boolean).filter((value, index, all) => all.indexOf(value) === index).join(", ");
11376
11415
  return [
11377
11416
  "You are an AI coding agent using VibeRaven as the production-readiness map.",
11378
11417
  "",
@@ -11380,11 +11419,11 @@ function buildAgentFixPrompt(artifact, gap) {
11380
11419
  "",
11381
11420
  `Project: ${artifact.workspacePath}`,
11382
11421
  `Current VibeRaven state: production core ${artifact.productionCorePercent}% | score ${artifact.score} (${artifact.scoreLabel})`,
11383
- `Gap: ${gap.title}`,
11384
- `Gap id: ${gap.id}`,
11385
- `Severity: ${gap.severity}`,
11386
- `Production area: ${gap.primaryMapCategory}${affected ? ` | affected: ${affected}` : ""}`,
11387
- `Scanner detail: ${compact(gap.detail)}`,
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)}`,
11388
11427
  "",
11389
11428
  "Required workflow:",
11390
11429
  "1. Read `.viberaven/agent-summary.md` and `.viberaven/launch-playbook.md` before changing code.",
@@ -11410,10 +11449,13 @@ function buildAgentFixPrompt(artifact, gap) {
11410
11449
  "- Report what changed, what verification passed, and which VibeRaven gap remains next.",
11411
11450
  "",
11412
11451
  "Original scanner hint:",
11413
- originalHint(gap)
11452
+ originalHint(gap2)
11414
11453
  ].join("\n");
11415
11454
  }
11416
11455
 
11456
+ // src/version.ts
11457
+ var VERSION = "1.1.1";
11458
+
11417
11459
  // src/commands/guide.ts
11418
11460
  var import_picocolors3 = __toESM(require_picocolors());
11419
11461
  function formatPasteTarget(step) {
@@ -11482,7 +11524,7 @@ async function runGuideCommand(options) {
11482
11524
 
11483
11525
  // src/commands/audit.ts
11484
11526
  var import_promises7 = require("node:fs/promises");
11485
- var import_node_path11 = require("node:path");
11527
+ var import_node_path10 = require("node:path");
11486
11528
  var ENV_FILES = [
11487
11529
  ".env",
11488
11530
  ".env.local",
@@ -11582,7 +11624,7 @@ function buildVercelSupabaseAudit(input) {
11582
11624
  }
11583
11625
  async function readIfExists(projectRoot, relativePath) {
11584
11626
  try {
11585
- const absolutePath = (0, import_node_path11.join)(projectRoot, relativePath);
11627
+ const absolutePath = (0, import_node_path10.join)(projectRoot, relativePath);
11586
11628
  const fileStat = await (0, import_promises7.stat)(absolutePath);
11587
11629
  if (!fileStat.isFile()) {
11588
11630
  return void 0;
@@ -11596,7 +11638,7 @@ async function readIfExists(projectRoot, relativePath) {
11596
11638
  }
11597
11639
  }
11598
11640
  async function collectSqlFiles(projectRoot, root) {
11599
- const base = (0, import_node_path11.join)(projectRoot, root);
11641
+ const base = (0, import_node_path10.join)(projectRoot, root);
11600
11642
  try {
11601
11643
  const rootStat = await (0, import_promises7.stat)(base);
11602
11644
  if (!rootStat.isDirectory()) {
@@ -11616,17 +11658,17 @@ async function collectSqlFiles(projectRoot, root) {
11616
11658
  for (const entry of entries) {
11617
11659
  if (entry.isDirectory()) {
11618
11660
  if (!SKIP_DIRS.has(entry.name)) {
11619
- await visit((0, import_node_path11.join)(dir, entry.name));
11661
+ await visit((0, import_node_path10.join)(dir, entry.name));
11620
11662
  }
11621
11663
  continue;
11622
11664
  }
11623
11665
  if (!entry.isFile() || !entry.name.toLowerCase().endsWith(".sql")) {
11624
11666
  continue;
11625
11667
  }
11626
- const absolutePath = (0, import_node_path11.join)(dir, entry.name);
11668
+ const absolutePath = (0, import_node_path10.join)(dir, entry.name);
11627
11669
  try {
11628
11670
  files.push({
11629
- path: (0, import_node_path11.relative)(projectRoot, absolutePath).replace(/\\/g, "/"),
11671
+ path: (0, import_node_path10.relative)(projectRoot, absolutePath).replace(/\\/g, "/"),
11630
11672
  content: await (0, import_promises7.readFile)(absolutePath, "utf8")
11631
11673
  });
11632
11674
  } catch {
@@ -11676,7 +11718,7 @@ function renderVercelSupabaseAudit(result) {
11676
11718
 
11677
11719
  // src/commands/initRules.ts
11678
11720
  var import_promises10 = require("node:fs/promises");
11679
- var import_node_path14 = require("node:path");
11721
+ var import_node_path13 = require("node:path");
11680
11722
 
11681
11723
  // src/commands/agentRulesBlock.ts
11682
11724
  var VIBERAVEN_BLOCK_START = "<!-- VIBERAVEN:START -->";
@@ -11698,15 +11740,6 @@ Maps repo evidence to OWASP LLM Top 10 launch risks. Supports SAFECode-style dep
11698
11740
  var STACK_FRAMING = `Stack context for this repo: Next.js, Supabase, Vercel, VibeRaven.
11699
11741
  Do NOT deploy to Vercel or ship to production users without running \`${PUBLIC_AGENT_MODE_COMMAND}\`.
11700
11742
  Do NOT rely on manual production checklists when VibeRaven gate artifacts exist in \`.viberaven/\`.`;
11701
- var PRODUCTION_PROTOCOL_RULES = `## VibeRaven Production Protocol
11702
-
11703
- Before making deploy, auth, database, payment, API, or scaling changes:
11704
-
11705
- 1. Read \`.viberaven/prp.json\`.
11706
- 2. Follow \`.viberaven/mission-map.md\`.
11707
- 3. Do not deploy if \`decision.status\` is \`blocked\`.
11708
- 4. Fix \`nextActions\` in order.
11709
- 5. Re-run \`${PUBLIC_AGENT_MODE_COMMAND}\`.`;
11710
11743
  var AGENT_NEGATIVE_CONSTRAINTS = `## Event-Triggered Production Verification
11711
11744
 
11712
11745
  Trigger: modifications to \`package.json\`, \`vercel.json\`, \`supabase/migrations/**/*\`, \`.env.example\`, auth/billing/webhook paths, or deploy workflow files.
@@ -11732,8 +11765,6 @@ var AGENT_RULES_BODY = `${AGENT_RULES_PREAMBLE}
11732
11765
 
11733
11766
  ${STACK_FRAMING}
11734
11767
 
11735
- ${PRODUCTION_PROTOCOL_RULES}
11736
-
11737
11768
  ${AGENT_NEGATIVE_CONSTRAINTS}
11738
11769
 
11739
11770
  ## VibeRaven Production-Readiness Gate
@@ -11783,11 +11814,10 @@ var AGENT_CONTEXT_BODY = `${AGENT_RULES_PREAMBLE}
11783
11814
 
11784
11815
  After \`--agent-mode\`, read these artifacts in order:
11785
11816
 
11786
- 1. \`.viberaven/prp.json\`
11787
- 2. \`.viberaven/mission-map.md\`
11788
- 3. \`.viberaven/agent-tasklist.md\`
11789
- 4. \`.viberaven/gate-result.json\`
11790
- 5. \`.viberaven/context-map.json\``;
11817
+ 1. \`.viberaven/mission-map.md\`
11818
+ 2. \`.viberaven/agent-tasklist.md\`
11819
+ 3. \`.viberaven/gate-result.json\`
11820
+ 4. \`.viberaven/context-map.json\``;
11791
11821
  var MISSION_MAP_BODY = `${AGENT_RULES_PREAMBLE}
11792
11822
 
11793
11823
  ## Mission Map loop
@@ -11926,7 +11956,7 @@ function escapeRegExp3(value) {
11926
11956
 
11927
11957
  // src/commands/cursorRulesPack.ts
11928
11958
  var import_promises8 = require("node:fs/promises");
11929
- var import_node_path12 = require("node:path");
11959
+ var import_node_path11 = require("node:path");
11930
11960
  var CURSOR_RULES_DIR = ".cursor/rules";
11931
11961
  var LEGACY_CURSOR_RULE_FILE = `${CURSOR_RULES_DIR}/viberaven.mdc`;
11932
11962
  var DOMAIN_PATH_POINTER = "Before editing these files, read `.viberaven/agent-context.md` and `.viberaven/mission-map.md`.";
@@ -12008,27 +12038,27 @@ function renderCursorCoreRulePreview() {
12008
12038
  async function initCursorRulesPack(options) {
12009
12039
  const results = [];
12010
12040
  const pack = buildCursorRulesPack();
12011
- for (const rule of pack) {
12012
- const file = `${CURSOR_RULES_DIR}/${rule.filename}`;
12013
- const path = (0, import_node_path12.join)(options.cwd, file);
12014
- const existing = await readExistingFile(path);
12015
- const changed = !existing.exists || existing.content !== rule.content;
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;
12016
12046
  const action = !existing.exists ? "created" : changed ? "updated" : "unchanged";
12017
12047
  if (!options.dryRun && changed) {
12018
- await (0, import_promises8.mkdir)((0, import_node_path12.join)(options.cwd, CURSOR_RULES_DIR), { recursive: true });
12019
- await (0, import_promises8.writeFile)(path, rule.content, "utf-8");
12048
+ 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");
12020
12050
  }
12021
- results.push({ target: "cursor", file, path, action });
12051
+ results.push({ target: "cursor", file, path: path3, action });
12022
12052
  }
12023
- const legacyPath = (0, import_node_path12.join)(options.cwd, LEGACY_CURSOR_RULE_FILE);
12053
+ const legacyPath = (0, import_node_path11.join)(options.cwd, LEGACY_CURSOR_RULE_FILE);
12024
12054
  if (!options.dryRun && await fileExists(legacyPath)) {
12025
12055
  await (0, import_promises8.rm)(legacyPath, { force: true });
12026
12056
  }
12027
12057
  return results;
12028
12058
  }
12029
- async function readExistingFile(path) {
12059
+ async function readExistingFile(path3) {
12030
12060
  try {
12031
- return { exists: true, content: await (0, import_promises8.readFile)(path, "utf-8") };
12061
+ return { exists: true, content: await (0, import_promises8.readFile)(path3, "utf-8") };
12032
12062
  } catch (error) {
12033
12063
  if (isFileNotFoundError(error)) {
12034
12064
  return { exists: false, content: "" };
@@ -12036,9 +12066,9 @@ async function readExistingFile(path) {
12036
12066
  throw error;
12037
12067
  }
12038
12068
  }
12039
- async function fileExists(path) {
12069
+ async function fileExists(path3) {
12040
12070
  try {
12041
- await (0, import_promises8.access)(path);
12071
+ await (0, import_promises8.access)(path3);
12042
12072
  return true;
12043
12073
  } catch {
12044
12074
  return false;
@@ -12133,14 +12163,14 @@ function getAgentRulesTargets(value) {
12133
12163
  // src/commands/seedPackageJsonScripts.ts
12134
12164
  var import_node_fs7 = require("node:fs");
12135
12165
  var import_promises9 = require("node:fs/promises");
12136
- var import_node_path13 = require("node:path");
12166
+ var import_node_path12 = require("node:path");
12137
12167
  var VIBERAVEN_PACKAGE_JSON_SCRIPTS = {
12138
12168
  "viberaven:gate": PUBLIC_AGENT_MODE_COMMAND,
12139
12169
  "viberaven:verify": PUBLIC_VERIFY_COMMAND,
12140
12170
  "viberaven:strict": PUBLIC_STRICT_COMMAND
12141
12171
  };
12142
12172
  async function seedPackageJsonScripts(options) {
12143
- const packageJsonPath = (0, import_node_path13.join)(options.cwd, "package.json");
12173
+ const packageJsonPath = (0, import_node_path12.join)(options.cwd, "package.json");
12144
12174
  if (!(0, import_node_fs7.existsSync)(packageJsonPath)) {
12145
12175
  return null;
12146
12176
  }
@@ -12189,15 +12219,15 @@ async function initAgentRules(options) {
12189
12219
  continue;
12190
12220
  }
12191
12221
  const file = AGENT_RULE_TARGETS[target].file;
12192
- const path = (0, import_node_path14.join)(options.cwd, file);
12193
- const existing = await readExistingFile2(path);
12222
+ const path3 = (0, import_node_path13.join)(options.cwd, file);
12223
+ const existing = await readExistingFile2(path3);
12194
12224
  const injected = injectAgentRulesBlock(existing.content, renderAgentRulesForTarget(target));
12195
12225
  const action = !existing.exists ? "created" : injected.changed ? "updated" : "unchanged";
12196
12226
  if (!options.dryRun && injected.changed) {
12197
- await (0, import_promises10.mkdir)((0, import_node_path14.dirname)(path), { recursive: true });
12198
- await (0, import_promises10.writeFile)(path, injected.content, "utf-8");
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");
12199
12229
  }
12200
- results.push({ target, file, path, action });
12230
+ results.push({ target, file, path: path3, action });
12201
12231
  }
12202
12232
  const packageJsonScripts = await seedPackageJsonScripts({
12203
12233
  cwd: options.cwd,
@@ -12208,7 +12238,7 @@ async function initAgentRules(options) {
12208
12238
  function renderAgentRulesDryRun(targets) {
12209
12239
  const files = targets.flatMap((target) => {
12210
12240
  if (target === "cursor") {
12211
- return buildCursorRulesPack().map((rule) => `- cursor: .cursor/rules/${rule.filename}`);
12241
+ return buildCursorRulesPack().map((rule2) => `- cursor: .cursor/rules/${rule2.filename}`);
12212
12242
  }
12213
12243
  return [`- ${target}: ${AGENT_RULE_TARGETS[target].file}`];
12214
12244
  }).join("\n");
@@ -12261,9 +12291,9 @@ function formatAgentRulesInitSummary(output) {
12261
12291
  );
12262
12292
  return lines.join("\n");
12263
12293
  }
12264
- async function readExistingFile2(path) {
12294
+ async function readExistingFile2(path3) {
12265
12295
  try {
12266
- return { exists: true, content: await (0, import_promises10.readFile)(path, "utf-8") };
12296
+ return { exists: true, content: await (0, import_promises10.readFile)(path3, "utf-8") };
12267
12297
  } catch (error) {
12268
12298
  if (isFileNotFoundError2(error)) {
12269
12299
  return { exists: false, content: "" };
@@ -12431,15 +12461,15 @@ async function handleViewGaps(cwd) {
12431
12461
  async function handlePrompt(cwd) {
12432
12462
  try {
12433
12463
  const artifact = await loadLastArtifact(cwd);
12434
- const gap = pickGap(artifact);
12435
- if (!gap) {
12464
+ const gap2 = pickGap(artifact);
12465
+ if (!gap2) {
12436
12466
  M2.warn("No gaps to fix. Run a scan or pick a different project.");
12437
12467
  return;
12438
12468
  }
12439
- const prompt = buildAgentFixPrompt(artifact, gap);
12469
+ const prompt = buildAgentFixPrompt(artifact, gap2);
12440
12470
  try {
12441
12471
  await copyToClipboard(prompt);
12442
- M2.success(import_picocolors4.default.green(`Copied top prompt to clipboard \u2014 ${gap.title}`));
12472
+ M2.success(import_picocolors4.default.green(`Copied top prompt to clipboard \u2014 ${gap2.title}`));
12443
12473
  } catch (error) {
12444
12474
  M2.warn(error instanceof Error ? error.message : String(error));
12445
12475
  console.log("");
@@ -12571,7 +12601,7 @@ async function runInteractiveSession(startDir = process.cwd()) {
12571
12601
  Ie(`${import_picocolors4.default.bold("VibeRaven")} ${import_picocolors4.default.dim(VERSION)}`);
12572
12602
  const cwd = await resolveWorkspaceRoot(startDir);
12573
12603
  const artifactsAt = await findArtifactsWorkspace(startDir);
12574
- if (artifactsAt && (0, import_node_path15.resolve)(artifactsAt) !== (0, import_node_path15.resolve)(startDir)) {
12604
+ if (artifactsAt && (0, import_node_path14.resolve)(artifactsAt) !== (0, import_node_path14.resolve)(startDir)) {
12575
12605
  M2.message(import_picocolors4.default.dim(`Using scan from: ${artifactsAt}`));
12576
12606
  } else {
12577
12607
  M2.message(import_picocolors4.default.dim(`Project folder: ${cwd}`));
@@ -12639,11 +12669,11 @@ async function runInteractiveSession(startDir = process.cwd()) {
12639
12669
 
12640
12670
  // src/commands/condense.ts
12641
12671
  var import_promises11 = require("node:fs/promises");
12642
- var import_node_path16 = require("node:path");
12672
+ var import_node_path15 = require("node:path");
12643
12673
  async function runCondenseCommand(options) {
12644
12674
  const dir = getProjectArtifactsDir(options.cwd);
12645
- const artifact = JSON.parse(await (0, import_promises11.readFile)((0, import_node_path16.join)(dir, "last-scan.json"), "utf8"));
12646
- const contextMapPath = (0, import_node_path16.join)(dir, "context-map.json");
12675
+ const artifact = JSON.parse(await (0, import_promises11.readFile)((0, import_node_path15.join)(dir, "last-scan.json"), "utf8"));
12676
+ const contextMapPath = (0, import_node_path15.join)(dir, "context-map.json");
12647
12677
  await (0, import_promises11.writeFile)(contextMapPath, `${JSON.stringify(generateContextMap(artifact), null, 2)}
12648
12678
  `, "utf8");
12649
12679
  return { contextMapPath };
@@ -12652,15 +12682,15 @@ async function runCondenseCommand(options) {
12652
12682
  // src/heal/apply.ts
12653
12683
  var import_promises13 = require("node:fs/promises");
12654
12684
  var import_node_fs10 = require("node:fs");
12655
- var import_node_path20 = require("node:path");
12685
+ var import_node_path19 = require("node:path");
12656
12686
 
12657
12687
  // src/heal/pathSafety.ts
12658
- var import_node_path17 = require("node:path");
12688
+ var import_node_path16 = require("node:path");
12659
12689
  var BLOCKED_SEGMENTS = /* @__PURE__ */ new Set([".git", "node_modules", "dist", "build", ".next", ".viberaven"]);
12660
12690
  function assertSafeHealTarget(cwd, target) {
12661
- const root = (0, import_node_path17.resolve)(cwd);
12662
- const absolute = (0, import_node_path17.resolve)(root, target);
12663
- const rel = (0, import_node_path17.relative)(root, absolute);
12691
+ const root = (0, import_node_path16.resolve)(cwd);
12692
+ const absolute = (0, import_node_path16.resolve)(root, target);
12693
+ const rel = (0, import_node_path16.relative)(root, absolute);
12664
12694
  if (rel.startsWith("..") || rel === "" || /^[A-Za-z]:/.test(rel)) {
12665
12695
  throw new Error("Heal target must stay inside the workspace");
12666
12696
  }
@@ -12685,7 +12715,7 @@ function applyEmptyCatchRecipe(source) {
12685
12715
  // src/heal/recipes/index.ts
12686
12716
  var import_node_fs9 = require("node:fs");
12687
12717
  var import_promises12 = require("node:fs/promises");
12688
- var import_node_path19 = require("node:path");
12718
+ var import_node_path18 = require("node:path");
12689
12719
 
12690
12720
  // src/heal/recipes/envAuthSecret.ts
12691
12721
  function applyAuthSecretRecipe(source) {
@@ -13067,7 +13097,7 @@ function applyRateLimitRecipe(source, hasUpstash) {
13067
13097
 
13068
13098
  // src/heal/recipes/eslintRestrictedImports.ts
13069
13099
  var import_node_fs8 = require("node:fs");
13070
- var import_node_path18 = require("node:path");
13100
+ var import_node_path17 = require("node:path");
13071
13101
  var VIBERAVEN_ESLINT_MARKER = "VibeRaven heal: eslint_restricted_imports";
13072
13102
  var RESTRICTED_IMPORTS_MESSAGE = `Restricted import. Run ${PUBLIC_AGENT_MODE_COMMAND} before substituting packages.`;
13073
13103
  var RESTRICTED_PATHS = [
@@ -13097,7 +13127,7 @@ var ESLINT_CONFIG_CANDIDATES = [
13097
13127
  ];
13098
13128
  function detectEslintConfigFile(cwd) {
13099
13129
  for (const candidate of ESLINT_CONFIG_CANDIDATES) {
13100
- if ((0, import_node_fs8.existsSync)((0, import_node_path18.join)(cwd, candidate))) {
13130
+ if ((0, import_node_fs8.existsSync)((0, import_node_path17.join)(cwd, candidate))) {
13101
13131
  return candidate;
13102
13132
  }
13103
13133
  }
@@ -13277,7 +13307,7 @@ async function readSourceOrEmpty(absolutePath) {
13277
13307
  }
13278
13308
  async function detectUpstash(cwd) {
13279
13309
  try {
13280
- const pkgPath = (0, import_node_path19.join)(cwd, "package.json");
13310
+ const pkgPath = (0, import_node_path18.join)(cwd, "package.json");
13281
13311
  if (!(0, import_node_fs9.existsSync)(pkgPath)) return false;
13282
13312
  const raw = await (0, import_promises12.readFile)(pkgPath, "utf8");
13283
13313
  const pkg = JSON.parse(raw);
@@ -13294,7 +13324,7 @@ async function dispatchRecipeByGapId(gapId, cwd, explicitTarget) {
13294
13324
  const targetFile = explicitTarget ?? defaultTargetFile(gapId);
13295
13325
  if (targetFile === void 0) return null;
13296
13326
  if (gapId === "auth_secret_missing" || gapId === "node_env_not_set" || gapId === "database_url_missing") {
13297
- const absolutePath = (0, import_node_path19.join)(cwd, targetFile);
13327
+ const absolutePath = (0, import_node_path18.join)(cwd, targetFile);
13298
13328
  const source = await readSourceOrEmpty(absolutePath);
13299
13329
  let result;
13300
13330
  if (gapId === "auth_secret_missing") result = applyAuthSecretRecipe(source);
@@ -13308,25 +13338,25 @@ async function dispatchRecipeByGapId(gapId, cwd, explicitTarget) {
13308
13338
  };
13309
13339
  }
13310
13340
  if (gapId === "missing_error_boundary") {
13311
- const absolutePath = (0, import_node_path19.join)(cwd, "app/error.tsx");
13341
+ const absolutePath = (0, import_node_path18.join)(cwd, "app/error.tsx");
13312
13342
  const source = await readSourceOrEmpty(absolutePath);
13313
13343
  const result = applyErrorBoundaryRecipe(source);
13314
13344
  return { ...result, canAutoApply: true, recipeName: gapId };
13315
13345
  }
13316
13346
  if (gapId === "missing_health_route") {
13317
- const absolutePath = (0, import_node_path19.join)(cwd, "app/api/health/route.ts");
13347
+ const absolutePath = (0, import_node_path18.join)(cwd, "app/api/health/route.ts");
13318
13348
  const source = await readSourceOrEmpty(absolutePath);
13319
13349
  const result = applyHealthRouteRecipe(source);
13320
13350
  return { ...result, canAutoApply: true, recipeName: gapId };
13321
13351
  }
13322
13352
  if (gapId === "missing_loading_state") {
13323
- const absolutePath = (0, import_node_path19.join)(cwd, "app/loading.tsx");
13353
+ const absolutePath = (0, import_node_path18.join)(cwd, "app/loading.tsx");
13324
13354
  const source = await readSourceOrEmpty(absolutePath);
13325
13355
  const result = applyLoadingStateRecipe(source);
13326
13356
  return { ...result, canAutoApply: true, recipeName: gapId };
13327
13357
  }
13328
13358
  if (gapId === "missing_404_page") {
13329
- const absolutePath = (0, import_node_path19.join)(cwd, "app/not-found.tsx");
13359
+ const absolutePath = (0, import_node_path18.join)(cwd, "app/not-found.tsx");
13330
13360
  const source = await readSourceOrEmpty(absolutePath);
13331
13361
  const result = applyNotFoundRecipe(source);
13332
13362
  return { ...result, canAutoApply: true, recipeName: gapId };
@@ -13344,10 +13374,10 @@ async function dispatchRecipeByGapId(gapId, cwd, explicitTarget) {
13344
13374
  }
13345
13375
  if (gapId === "missing_csp_header") {
13346
13376
  let configFile = "next.config.js";
13347
- let absolutePath = (0, import_node_path19.join)(cwd, configFile);
13377
+ let absolutePath = (0, import_node_path18.join)(cwd, configFile);
13348
13378
  if (!(0, import_node_fs9.existsSync)(absolutePath)) {
13349
13379
  configFile = "next.config.mjs";
13350
- absolutePath = (0, import_node_path19.join)(cwd, configFile);
13380
+ absolutePath = (0, import_node_path18.join)(cwd, configFile);
13351
13381
  }
13352
13382
  const source = await readSourceOrEmpty(absolutePath);
13353
13383
  const result = applyCspHeaderRecipe(source);
@@ -13359,7 +13389,7 @@ async function dispatchRecipeByGapId(gapId, cwd, explicitTarget) {
13359
13389
  }
13360
13390
  if (gapId === "missing_rate_limit") {
13361
13391
  const hasUpstash = await detectUpstash(cwd);
13362
- const middlewarePath = (0, import_node_path19.join)(cwd, "middleware.ts");
13392
+ const middlewarePath = (0, import_node_path18.join)(cwd, "middleware.ts");
13363
13393
  const source = await readSourceOrEmpty(middlewarePath);
13364
13394
  const result = applyRateLimitRecipe(source, hasUpstash);
13365
13395
  return {
@@ -13381,7 +13411,7 @@ async function dispatchRecipeByGapId(gapId, cwd, explicitTarget) {
13381
13411
  recipeName: gapId
13382
13412
  };
13383
13413
  }
13384
- const absolutePath = (0, import_node_path19.join)(cwd, configFile);
13414
+ const absolutePath = (0, import_node_path18.join)(cwd, configFile);
13385
13415
  const source = await readSourceOrEmpty(absolutePath);
13386
13416
  const result = applyEslintRestrictedImportsRecipe(source, configFile);
13387
13417
  return {
@@ -13456,13 +13486,13 @@ async function applyHeal(options) {
13456
13486
  rollback: { available: false, instructions: "Recipe matched but no change was needed (already applied or file already exists)." }
13457
13487
  };
13458
13488
  }
13459
- const absoluteTarget = (0, import_node_path20.join)(options.cwd, dispatched.targetFile);
13489
+ const absoluteTarget = (0, import_node_path19.join)(options.cwd, dispatched.targetFile);
13460
13490
  assertSafeHealTarget(options.cwd, dispatched.targetFile);
13461
- await (0, import_promises13.mkdir)((0, import_node_path20.dirname)(absoluteTarget), { recursive: true });
13462
- const healDir2 = (0, import_node_path20.join)(options.cwd, ".viberaven", "heal", id);
13463
- await (0, import_promises13.mkdir)((0, import_node_path20.join)(healDir2, "before"), { recursive: true });
13491
+ await (0, import_promises13.mkdir)((0, import_node_path19.dirname)(absoluteTarget), { recursive: true });
13492
+ const healDir2 = (0, import_node_path19.join)(options.cwd, ".viberaven", "heal", id);
13493
+ await (0, import_promises13.mkdir)((0, import_node_path19.join)(healDir2, "before"), { recursive: true });
13464
13494
  const beforeContent = (0, import_node_fs10.existsSync)(absoluteTarget) ? await (0, import_promises13.readFile)(absoluteTarget, "utf8") : "";
13465
- await (0, import_promises13.writeFile)((0, import_node_path20.join)(healDir2, "before", "target.txt"), beforeContent, "utf8");
13495
+ await (0, import_promises13.writeFile)((0, import_node_path19.join)(healDir2, "before", "target.txt"), beforeContent, "utf8");
13466
13496
  await (0, import_promises13.writeFile)(absoluteTarget, dispatched.output, "utf8");
13467
13497
  const patch2 = [
13468
13498
  `--- ${dispatched.targetFile}`,
@@ -13473,7 +13503,7 @@ async function applyHeal(options) {
13473
13503
  dispatched.output,
13474
13504
  ""
13475
13505
  ].join("\n");
13476
- await (0, import_promises13.writeFile)((0, import_node_path20.join)(healDir2, "patch.diff"), patch2, "utf8");
13506
+ await (0, import_promises13.writeFile)((0, import_node_path19.join)(healDir2, "patch.diff"), patch2, "utf8");
13477
13507
  const result2 = {
13478
13508
  $schema: "https://viberaven.dev/schemas/heal-result.schema.json",
13479
13509
  schemaVersion: "v1",
@@ -13484,7 +13514,7 @@ async function applyHeal(options) {
13484
13514
  gapId: options.gapId,
13485
13515
  recipe: dispatched.recipeName,
13486
13516
  target: dispatched.targetFile,
13487
- changedFiles: [(0, import_node_path20.relative)(options.cwd, absoluteTarget).replace(/\\/g, "/")],
13517
+ changedFiles: [(0, import_node_path19.relative)(options.cwd, absoluteTarget).replace(/\\/g, "/")],
13488
13518
  artifacts: {
13489
13519
  patch: `.viberaven/heal/${id}/patch.diff`,
13490
13520
  result: `.viberaven/heal/${id}/result.json`
@@ -13494,7 +13524,7 @@ async function applyHeal(options) {
13494
13524
  instructions: "Restore .viberaven/heal/<healId>/before/target.txt to the target file or apply the reverse patch."
13495
13525
  }
13496
13526
  };
13497
- await (0, import_promises13.writeFile)((0, import_node_path20.join)(healDir2, "result.json"), `${JSON.stringify(result2, null, 2)}
13527
+ await (0, import_promises13.writeFile)((0, import_node_path19.join)(healDir2, "result.json"), `${JSON.stringify(result2, null, 2)}
13498
13528
  `, "utf8");
13499
13529
  return result2;
13500
13530
  }
@@ -13531,9 +13561,9 @@ async function applyHeal(options) {
13531
13561
  rollback: { available: false, instructions: "No supported heal recipe matched this file." }
13532
13562
  };
13533
13563
  }
13534
- const healDir = (0, import_node_path20.join)(options.cwd, ".viberaven", "heal", id);
13535
- await (0, import_promises13.mkdir)((0, import_node_path20.join)(healDir, "before"), { recursive: true });
13536
- await (0, import_promises13.writeFile)((0, import_node_path20.join)(healDir, "before", "target.txt"), before, "utf8");
13564
+ const healDir = (0, import_node_path19.join)(options.cwd, ".viberaven", "heal", id);
13565
+ await (0, import_promises13.mkdir)((0, import_node_path19.join)(healDir, "before"), { recursive: true });
13566
+ await (0, import_promises13.writeFile)((0, import_node_path19.join)(healDir, "before", "target.txt"), before, "utf8");
13537
13567
  await (0, import_promises13.writeFile)(absolute, recipe.output, "utf8");
13538
13568
  const patch = [
13539
13569
  `--- ${options.target}`,
@@ -13544,7 +13574,7 @@ async function applyHeal(options) {
13544
13574
  recipe.output,
13545
13575
  ""
13546
13576
  ].join("\n");
13547
- await (0, import_promises13.writeFile)((0, import_node_path20.join)(healDir, "patch.diff"), patch, "utf8");
13577
+ await (0, import_promises13.writeFile)((0, import_node_path19.join)(healDir, "patch.diff"), patch, "utf8");
13548
13578
  const result = {
13549
13579
  $schema: "https://viberaven.dev/schemas/heal-result.schema.json",
13550
13580
  schemaVersion: "v1",
@@ -13555,7 +13585,7 @@ async function applyHeal(options) {
13555
13585
  gapId: options.gapId,
13556
13586
  recipe: "empty-catch-safe-response",
13557
13587
  target: options.target,
13558
- changedFiles: [(0, import_node_path20.relative)(options.cwd, absolute).replace(/\\/g, "/")],
13588
+ changedFiles: [(0, import_node_path19.relative)(options.cwd, absolute).replace(/\\/g, "/")],
13559
13589
  artifacts: {
13560
13590
  patch: `.viberaven/heal/${id}/patch.diff`,
13561
13591
  result: `.viberaven/heal/${id}/result.json`
@@ -13565,19 +13595,19 @@ async function applyHeal(options) {
13565
13595
  instructions: "Restore .viberaven/heal/<healId>/before/target.txt to the target file or apply the reverse patch."
13566
13596
  }
13567
13597
  };
13568
- await (0, import_promises13.writeFile)((0, import_node_path20.join)(healDir, "result.json"), `${JSON.stringify(result, null, 2)}
13598
+ await (0, import_promises13.writeFile)((0, import_node_path19.join)(healDir, "result.json"), `${JSON.stringify(result, null, 2)}
13569
13599
  `, "utf8");
13570
13600
  return result;
13571
13601
  }
13572
13602
 
13573
13603
  // src/heal/plan.ts
13574
13604
  var import_promises14 = require("node:fs/promises");
13575
- var import_node_path21 = require("node:path");
13605
+ var import_node_path20 = require("node:path");
13576
13606
  function healId2() {
13577
13607
  return `heal_${(/* @__PURE__ */ new Date()).toISOString().replace(/\D/g, "").slice(0, 14)}`;
13578
13608
  }
13579
13609
  async function writeHealPlan(options) {
13580
- const dir = (0, import_node_path21.join)(options.cwd, ".viberaven");
13610
+ const dir = (0, import_node_path20.join)(options.cwd, ".viberaven");
13581
13611
  await (0, import_promises14.mkdir)(dir, { recursive: true });
13582
13612
  const id = healId2();
13583
13613
  const target = options.target ?? `gap:${options.gapId}`;
@@ -13605,20 +13635,20 @@ async function writeHealPlan(options) {
13605
13635
  artifacts: { plan: ".viberaven/heal-plan.md" },
13606
13636
  rollback: { available: false, instructions: "No source files were changed." }
13607
13637
  };
13608
- await (0, import_promises14.writeFile)((0, import_node_path21.join)(dir, "heal-plan.md"), markdown, "utf8");
13609
- await (0, import_promises14.writeFile)((0, import_node_path21.join)(dir, "heal-plan.json"), `${JSON.stringify(result, null, 2)}
13638
+ await (0, import_promises14.writeFile)((0, import_node_path20.join)(dir, "heal-plan.md"), markdown, "utf8");
13639
+ await (0, import_promises14.writeFile)((0, import_node_path20.join)(dir, "heal-plan.json"), `${JSON.stringify(result, null, 2)}
13610
13640
  `, "utf8");
13611
13641
  return result;
13612
13642
  }
13613
13643
 
13614
13644
  // src/heal/prompt.ts
13615
13645
  var import_promises15 = require("node:fs/promises");
13616
- var import_node_path22 = require("node:path");
13646
+ var import_node_path21 = require("node:path");
13617
13647
  function healId3() {
13618
13648
  return `heal_${(/* @__PURE__ */ new Date()).toISOString().replace(/\D/g, "").slice(0, 14)}`;
13619
13649
  }
13620
13650
  async function writeHealPrompt(options) {
13621
- const dir = (0, import_node_path22.join)(options.cwd, ".viberaven");
13651
+ const dir = (0, import_node_path21.join)(options.cwd, ".viberaven");
13622
13652
  await (0, import_promises15.mkdir)(dir, { recursive: true });
13623
13653
  const id = healId3();
13624
13654
  const target = options.target ?? `gap:${options.gapId}`;
@@ -13646,7 +13676,7 @@ async function writeHealPrompt(options) {
13646
13676
  artifacts: { prompt: ".viberaven/heal-prompt.md" },
13647
13677
  rollback: { available: false, instructions: "No source files were changed." }
13648
13678
  };
13649
- await (0, import_promises15.writeFile)((0, import_node_path22.join)(dir, "heal-prompt.md"), prompt, "utf8");
13679
+ await (0, import_promises15.writeFile)((0, import_node_path21.join)(dir, "heal-prompt.md"), prompt, "utf8");
13650
13680
  return result;
13651
13681
  }
13652
13682
 
@@ -13733,7 +13763,7 @@ async function runHealCommand(options) {
13733
13763
  // src/stackRecommend.ts
13734
13764
  var import_promises17 = require("node:fs/promises");
13735
13765
  var import_node_fs11 = require("node:fs");
13736
- var import_node_path23 = require("node:path");
13766
+ var import_node_path22 = require("node:path");
13737
13767
  var DEFAULT_STACK = {
13738
13768
  frontend: "react",
13739
13769
  ui: "tailwind + shadcn/ui",
@@ -13744,7 +13774,7 @@ var DEFAULT_STACK = {
13744
13774
  reason: "Agent-default stack for lowest launch friction when repo signals are ambiguous"
13745
13775
  };
13746
13776
  async function recommendStack(cwd = process.cwd()) {
13747
- const pkgPath = (0, import_node_path23.join)(cwd, "package.json");
13777
+ const pkgPath = (0, import_node_path22.join)(cwd, "package.json");
13748
13778
  if (!(0, import_node_fs11.existsSync)(pkgPath)) {
13749
13779
  return DEFAULT_STACK;
13750
13780
  }
@@ -13801,7 +13831,7 @@ async function runInitCommand(options) {
13801
13831
 
13802
13832
  // src/commands/doctorAgents.ts
13803
13833
  var import_promises18 = require("node:fs/promises");
13804
- var import_node_path24 = require("node:path");
13834
+ var import_node_path23 = require("node:path");
13805
13835
  var REQUIRED_EXISTENCE_CHECKS = [
13806
13836
  { id: "agents-md", file: "AGENTS.md" },
13807
13837
  { id: "claude-md", file: "CLAUDE.md" },
@@ -13821,7 +13851,7 @@ var STALE_PATTERNS = [
13821
13851
  async function checkAgentInjection(cwd) {
13822
13852
  const checks = [];
13823
13853
  for (const item3 of REQUIRED_EXISTENCE_CHECKS) {
13824
- const exists = await fileExists2((0, import_node_path24.join)(cwd, item3.file));
13854
+ const exists = await fileExists2((0, import_node_path23.join)(cwd, item3.file));
13825
13855
  checks.push({
13826
13856
  id: item3.id,
13827
13857
  status: exists ? "pass" : "fail",
@@ -13829,17 +13859,17 @@ async function checkAgentInjection(cwd) {
13829
13859
  });
13830
13860
  }
13831
13861
  for (const item3 of OPTIONAL_CURSOR_PACK_CHECKS) {
13832
- const exists = await fileExists2((0, import_node_path24.join)(cwd, item3.file));
13862
+ const exists = await fileExists2((0, import_node_path23.join)(cwd, item3.file));
13833
13863
  checks.push({
13834
13864
  id: item3.id,
13835
13865
  status: exists ? "pass" : "fail",
13836
13866
  message: exists ? `${item3.file} exists` : `Missing ${item3.file} \u2014 run npx -y viberaven init --agents all`
13837
13867
  });
13838
13868
  }
13839
- const legacyCursorPath = (0, import_node_path24.join)(cwd, ".cursor/rules/viberaven.mdc");
13869
+ const legacyCursorPath = (0, import_node_path23.join)(cwd, ".cursor/rules/viberaven.mdc");
13840
13870
  if (await fileExists2(legacyCursorPath)) {
13841
13871
  const legacyContent = await (0, import_promises18.readFile)(legacyCursorPath, "utf-8");
13842
- const hasCoreSplit = await fileExists2((0, import_node_path24.join)(cwd, ".cursor/rules/viberaven-core.mdc"));
13872
+ const hasCoreSplit = await fileExists2((0, import_node_path23.join)(cwd, ".cursor/rules/viberaven-core.mdc"));
13843
13873
  if (!hasCoreSplit || legacyContent.includes("alwaysApply: true")) {
13844
13874
  checks.push({
13845
13875
  id: "cursor-legacy-mdc",
@@ -13850,8 +13880,8 @@ async function checkAgentInjection(cwd) {
13850
13880
  }
13851
13881
  for (const target of CORE_AGENT_INJECTION_TARGETS) {
13852
13882
  const file = target === "cursor" ? ".cursor/rules/viberaven-core.mdc" : AGENT_RULE_TARGETS[target].file;
13853
- const path = (0, import_node_path24.join)(cwd, file);
13854
- const exists = await fileExists2(path);
13883
+ const path3 = (0, import_node_path23.join)(cwd, file);
13884
+ const exists = await fileExists2(path3);
13855
13885
  if (!exists) {
13856
13886
  checks.push({
13857
13887
  id: `canonical-${target}`,
@@ -13860,7 +13890,7 @@ async function checkAgentInjection(cwd) {
13860
13890
  });
13861
13891
  continue;
13862
13892
  }
13863
- const content = await (0, import_promises18.readFile)(path, "utf-8");
13893
+ const content = await (0, import_promises18.readFile)(path3, "utf-8");
13864
13894
  const hasCommand = content.includes(PUBLIC_AGENT_MODE_COMMAND);
13865
13895
  checks.push({
13866
13896
  id: `canonical-${target}`,
@@ -13892,9 +13922,9 @@ function formatDoctorAgentsReport(report) {
13892
13922
  lines.push(report.ok ? "All agent injection checks passed." : "Agent injection checks failed.");
13893
13923
  return lines.join("\n");
13894
13924
  }
13895
- async function fileExists2(path) {
13925
+ async function fileExists2(path3) {
13896
13926
  try {
13897
- await (0, import_promises18.access)(path);
13927
+ await (0, import_promises18.access)(path3);
13898
13928
  return true;
13899
13929
  } catch {
13900
13930
  return false;
@@ -14107,6 +14137,220 @@ async function runAuditCommand(input) {
14107
14137
  return result.status === "pass" ? 0 : 1;
14108
14138
  }
14109
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
+ });
14324
+ }
14325
+
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;
14348
+ }
14349
+ log("Add this to your README:");
14350
+ log(renderBadgeMarkdown(prp));
14351
+ return 0;
14352
+ }
14353
+
14110
14354
  // src/output/nextActionBlock.ts
14111
14355
  function buildNextActionBlock(tasks, loopState, plan) {
14112
14356
  const batchSize = plan === "pro" ? 10 : 3;
@@ -14201,14 +14445,9 @@ function buildProviderActionBlock(task) {
14201
14445
  dashboardUrl: pa.dashboardUrl,
14202
14446
  exactStep: pa.exactStep,
14203
14447
  envKeyName: pa.envKeyName ?? null,
14204
- envKeyExample: pa.envKeyExample ?? null,
14205
14448
  doneSignal: pa.doneSignal,
14206
14449
  verifyCommand: task.verifyCommand,
14207
- mcpAlternative: task.mcpTool ?? pa.mcpAlternative ?? null,
14208
- actionClass: pa.actionClass ?? "manual_provider_step",
14209
- approvalRequired: pa.approvalRequired ?? true,
14210
- manualFallback: pa.manualFallback ?? pa.exactStep,
14211
- copyValues: pa.copyValues ?? []
14450
+ mcpAlternative: task.mcpTool ?? pa.mcpAlternative ?? null
14212
14451
  }
14213
14452
  };
14214
14453
  }
@@ -14229,6 +14468,225 @@ function printNextActionBlock(block) {
14229
14468
  console.log(NEXT_ACTION_END);
14230
14469
  }
14231
14470
 
14471
+ // src/output/operatorBlock.ts
14472
+ var OPERATOR_START = "VIBERAVEN_OPERATOR_START";
14473
+ var OPERATOR_END = "VIBERAVEN_OPERATOR_END";
14474
+ function stackLine(prp) {
14475
+ const parts = Array.from(
14476
+ /* @__PURE__ */ new Set([
14477
+ ...prp.detectedStack.deployment,
14478
+ ...prp.detectedStack.database,
14479
+ ...prp.detectedStack.auth,
14480
+ ...prp.detectedStack.payments,
14481
+ ...prp.detectedStack.monitoring
14482
+ ])
14483
+ );
14484
+ if (parts.length > 0) {
14485
+ return parts.join(" + ");
14486
+ }
14487
+ return prp.detectedStack.archetype ?? "unknown";
14488
+ }
14489
+ function uniqueEnvNames(tasks) {
14490
+ return Array.from(
14491
+ new Set(
14492
+ tasks.map((task) => task.providerAction?.envKeyName).filter((name) => Boolean(name))
14493
+ )
14494
+ );
14495
+ }
14496
+ function humanTaskText(task) {
14497
+ if (task.fixType === "provider-action") {
14498
+ return task.providerAction?.exactStep ?? task.title;
14499
+ }
14500
+ if (task.fixType === "upgrade-required") {
14501
+ return `Upgrade required: ${task.action ?? task.exactFix ?? task.title}`;
14502
+ }
14503
+ if (task.fixType === "manual-verify") {
14504
+ return `Manual verification required: ${task.action ?? task.exactFix ?? task.title}`;
14505
+ }
14506
+ return task.title;
14507
+ }
14508
+ function riskSuffixFor(task, prp) {
14509
+ const topRisk = prp.findings.find((finding) => finding.id === task.gapId)?.riskMapping?.owaspLlm?.[0];
14510
+ return topRisk ? ` (risk: ${topRisk})` : "";
14511
+ }
14512
+ function readinessPercent(prp) {
14513
+ if (prp.decision === "CLEAR") return 100;
14514
+ if (prp.decision === "WARNING") return 65;
14515
+ return 25;
14516
+ }
14517
+ function buildPlainOperatorBlock(prp, tasks) {
14518
+ const repoTasks = tasks.filter((task) => task.fixType === "repo-code");
14519
+ const humanTasks = tasks.filter((task) => task.fixType !== "repo-code");
14520
+ const providerTasks = tasks.filter((task) => task.fixType === "provider-action");
14521
+ const nextRepoTask = repoTasks[0];
14522
+ const envNames = uniqueEnvNames(providerTasks);
14523
+ const lines = [];
14524
+ lines.push(`Decision: ${prp.decision}`);
14525
+ lines.push(`Detected stack: ${stackLine(prp)}`);
14526
+ lines.push("");
14527
+ lines.push("What I can fix:");
14528
+ if (repoTasks.length === 0) {
14529
+ lines.push(" (none - no automated repo-code recipes apply)");
14530
+ } else {
14531
+ repoTasks.forEach((task, index) => {
14532
+ lines.push(` ${index + 1}. ${task.title}${riskSuffixFor(task, prp)}`);
14533
+ });
14534
+ }
14535
+ lines.push("");
14536
+ lines.push("What you must do:");
14537
+ if (humanTasks.length === 0) {
14538
+ lines.push(" (none - no human-owned steps required)");
14539
+ } else {
14540
+ humanTasks.forEach((task, index) => {
14541
+ lines.push(` ${index + 1}. ${humanTaskText(task)}${riskSuffixFor(task, prp)}`);
14542
+ });
14543
+ }
14544
+ lines.push("");
14545
+ lines.push("Next repo-code fix:");
14546
+ lines.push(
14547
+ nextRepoTask ? ` ${nextRepoTask.title}${riskSuffixFor(nextRepoTask, prp)} -> npx -y viberaven --heal --apply --gap ${nextRepoTask.gapId} --yes` : " (none)"
14548
+ );
14549
+ lines.push("");
14550
+ lines.push("Provider actions:");
14551
+ if (providerTasks.length === 0) {
14552
+ lines.push(" (none)");
14553
+ } else {
14554
+ providerTasks.forEach((task) => {
14555
+ const providerAction = task.providerAction;
14556
+ if (!providerAction) {
14557
+ lines.push(` - ${task.title}${riskSuffixFor(task, prp)}`);
14558
+ return;
14559
+ }
14560
+ lines.push(` - ${providerAction.provider}: ${providerAction.dashboardUrl}`);
14561
+ lines.push(` Step: ${providerAction.exactStep}${riskSuffixFor(task, prp)}`);
14562
+ lines.push(` Done when: ${providerAction.doneSignal}`);
14563
+ });
14564
+ }
14565
+ lines.push("");
14566
+ lines.push("Copy these env var names:");
14567
+ lines.push(envNames.length > 0 ? ` ${envNames.join(", ")}` : " (none)");
14568
+ lines.push("");
14569
+ lines.push(`Verify command: ${prp.verifyCommand}`);
14570
+ lines.push("");
14571
+ lines.push(`Do not deploy until: decision is CLEAR (current: ${prp.decision}). Run the verify command after fixes.`);
14572
+ if (prp.decision === "CLEAR") {
14573
+ lines.push("Share your launch proof: npx -y viberaven badge");
14574
+ }
14575
+ return lines.join("\n");
14576
+ }
14577
+ function buildRichOperatorBlock(prp, tasks) {
14578
+ const repoTasks = tasks.filter((task) => task.fixType === "repo-code");
14579
+ const humanTasks = tasks.filter((task) => task.fixType !== "repo-code");
14580
+ const providerTasks = tasks.filter((task) => task.fixType === "provider-action");
14581
+ const nextRepoTask = repoTasks[0];
14582
+ const envNames = uniqueEnvNames(providerTasks);
14583
+ const mode = "rich";
14584
+ const lines = [];
14585
+ lines.push(wordmark({ variant: "compact", mode }));
14586
+ lines.push(rule(64, { mode }));
14587
+ lines.push(
14588
+ `${accent.bold("Decision", mode)}: ${banner(prp.decision, { mode })} ${gauge(readinessPercent(prp), {
14589
+ width: 18,
14590
+ mode
14591
+ })}`
14592
+ );
14593
+ lines.push(kv("Detected stack", stackLine(prp), { mode }));
14594
+ lines.push("");
14595
+ lines.push(`${accent.bold("What I can fix", mode)}:`);
14596
+ if (repoTasks.length === 0) {
14597
+ lines.push(` ${accent.dim("(none - no automated repo-code recipes apply)", mode)}`);
14598
+ } else {
14599
+ repoTasks.forEach((task, index) => {
14600
+ lines.push(` ${index + 1}. ${task.title}${riskSuffixFor(task, prp)}`);
14601
+ });
14602
+ }
14603
+ lines.push("");
14604
+ lines.push(`${accent.bold("What you must do", mode)}:`);
14605
+ if (humanTasks.length === 0) {
14606
+ lines.push(` ${accent.dim("(none - no human-owned steps required)", mode)}`);
14607
+ } else {
14608
+ humanTasks.forEach((task, index) => {
14609
+ lines.push(` ${index + 1}. ${humanTaskText(task)}${riskSuffixFor(task, prp)}`);
14610
+ });
14611
+ }
14612
+ lines.push("");
14613
+ lines.push(`${accent.bold("Next repo-code fix", mode)}:`);
14614
+ lines.push(
14615
+ nextRepoTask ? ` ${nextRepoTask.title}${riskSuffixFor(nextRepoTask, prp)} -> npx -y viberaven --heal --apply --gap ${nextRepoTask.gapId} --yes` : ` ${accent.dim("(none)", mode)}`
14616
+ );
14617
+ lines.push("");
14618
+ lines.push(`${accent.bold("Provider actions", mode)}:`);
14619
+ if (providerTasks.length === 0) {
14620
+ lines.push(` ${accent.dim("(none)", mode)}`);
14621
+ } else {
14622
+ providerTasks.forEach((task) => {
14623
+ const providerAction = task.providerAction;
14624
+ if (!providerAction) {
14625
+ lines.push(` - ${task.title}${riskSuffixFor(task, prp)}`);
14626
+ return;
14627
+ }
14628
+ lines.push(` - ${accent.brand(providerAction.provider, mode)}: ${providerAction.dashboardUrl}`);
14629
+ lines.push(` ${kv("Step", `${providerAction.exactStep}${riskSuffixFor(task, prp)}`, { mode })}`);
14630
+ lines.push(` ${kv("Done when", providerAction.doneSignal, { mode })}`);
14631
+ });
14632
+ }
14633
+ lines.push("");
14634
+ lines.push(`${accent.bold("Copy these env var names", mode)}:`);
14635
+ lines.push(envNames.length > 0 ? ` ${envNames.join(", ")}` : ` ${accent.dim("(none)", mode)}`);
14636
+ lines.push("");
14637
+ lines.push(kv("Verify command", prp.verifyCommand, { mode }));
14638
+ lines.push("");
14639
+ lines.push(
14640
+ `${accent.bold("Do not deploy until", mode)}: decision is CLEAR (current: ${banner(
14641
+ prp.decision,
14642
+ { mode }
14643
+ )}). Run the verify command after fixes.`
14644
+ );
14645
+ if (prp.decision === "CLEAR") {
14646
+ lines.push(kv("Share your launch proof", "npx -y viberaven badge", { mode }));
14647
+ }
14648
+ return lines.join("\n");
14649
+ }
14650
+ function buildOperatorBlock(prp, tasks, mode = "plain") {
14651
+ return mode === "rich" ? buildRichOperatorBlock(prp, tasks) : buildPlainOperatorBlock(prp, tasks);
14652
+ }
14653
+ function printOperatorBlock(prp, tasks, mode = "plain") {
14654
+ if (mode === "rich") {
14655
+ console.log(buildOperatorBlock(prp, tasks, "rich"));
14656
+ }
14657
+ console.log(OPERATOR_START);
14658
+ console.log(buildOperatorBlock(prp, tasks, "plain"));
14659
+ console.log(OPERATOR_END);
14660
+ }
14661
+
14662
+ // src/output/celebration.ts
14663
+ function renderClearCelebration(prp, mode) {
14664
+ if (prp.decision !== "CLEAR") return "";
14665
+ const verifiedDate = prp.generatedAt.slice(0, 10);
14666
+ const style = decisionStyle("CLEAR", mode);
14667
+ const lines = [
14668
+ `${style.glyph} ${style.label} production gate is clear`,
14669
+ accent.dim(`Verified ${verifiedDate}`, mode),
14670
+ "",
14671
+ `Share your launch proof: ${accent.bold("npx -y viberaven badge", mode)}`
14672
+ ];
14673
+ return [wordmark({ variant: "compact", mode }), box(lines, { mode }), rule(40, { mode })].join("\n");
14674
+ }
14675
+
14676
+ // src/output/loopProgress.ts
14677
+ function safeCount(value) {
14678
+ if (!Number.isFinite(value)) return 0;
14679
+ return Math.max(0, Math.floor(value));
14680
+ }
14681
+ function renderLoopProgress(input, mode) {
14682
+ if (mode === "plain" && input.silentInPlain) return "";
14683
+ const total = safeCount(input.total);
14684
+ const applied = total > 0 ? Math.min(safeCount(input.applied), total) : safeCount(input.applied);
14685
+ const percent = total === 0 ? 100 : applied / total * 100;
14686
+ const label2 = `${STATUS_GLYPH.fixable} ${input.label}`;
14687
+ return `${accent.bold(label2, mode)} ${gauge(percent, { width: 12, mode })} ${applied}/${total}`;
14688
+ }
14689
+
14232
14690
  // src/providerMcpBridge.ts
14233
14691
  var import_node_fs12 = require("node:fs");
14234
14692
  var import_node_os2 = require("node:os");
@@ -14270,12 +14728,12 @@ function findServerEntry(servers, provider2) {
14270
14728
  }
14271
14729
  function findProviderMcpConfig(provider2, configPaths) {
14272
14730
  const paths = configPaths ?? resolveConfigPaths();
14273
- for (const path of paths) {
14274
- if (!(0, import_node_fs12.existsSync)(path)) {
14731
+ for (const path3 of paths) {
14732
+ if (!(0, import_node_fs12.existsSync)(path3)) {
14275
14733
  continue;
14276
14734
  }
14277
14735
  try {
14278
- const raw = JSON.parse((0, import_node_fs12.readFileSync)(path, "utf8"));
14736
+ const raw = JSON.parse((0, import_node_fs12.readFileSync)(path3, "utf8"));
14279
14737
  const servers = parseMcpServers(raw);
14280
14738
  if (!servers) {
14281
14739
  continue;
@@ -14289,7 +14747,7 @@ function findProviderMcpConfig(provider2, configPaths) {
14289
14747
  command: typeof server.command === "string" ? server.command : void 0,
14290
14748
  args: Array.isArray(server.args) ? server.args.filter((arg) => typeof arg === "string") : void 0,
14291
14749
  url: typeof server.url === "string" ? server.url : void 0,
14292
- source: path
14750
+ source: path3
14293
14751
  };
14294
14752
  } catch {
14295
14753
  continue;
@@ -14297,6 +14755,19 @@ function findProviderMcpConfig(provider2, configPaths) {
14297
14755
  }
14298
14756
  return void 0;
14299
14757
  }
14758
+ function hasUsableProviderMcpConfig(config) {
14759
+ return Boolean(config.command?.trim() || config.url?.trim());
14760
+ }
14761
+ function findUsableProviderMcpConfig(provider2, configPaths) {
14762
+ const paths = configPaths ?? resolveConfigPaths();
14763
+ for (const path3 of paths) {
14764
+ const config = findProviderMcpConfig(provider2, [path3]);
14765
+ if (config && hasUsableProviderMcpConfig(config)) {
14766
+ return config;
14767
+ }
14768
+ }
14769
+ return void 0;
14770
+ }
14300
14771
  async function verifyProviderGap(options) {
14301
14772
  if (options.plan !== "pro") {
14302
14773
  return {
@@ -14327,6 +14798,254 @@ async function verifyProviderGap(options) {
14327
14798
  };
14328
14799
  }
14329
14800
 
14801
+ // src/connectedTools.ts
14802
+ var DETECTED_PROVIDERS = ["supabase", "vercel", "stripe", "github"];
14803
+ var INSTALL_HINTS = {
14804
+ supabase: "claude mcp add --transport http supabase https://mcp.supabase.com/",
14805
+ vercel: "claude mcp add --transport http vercel https://mcp.vercel.com/",
14806
+ stripe: "claude mcp add --transport http stripe https://mcp.stripe.com/",
14807
+ github: "claude mcp add --transport http github https://api.githubcopilot.com/mcp/"
14808
+ };
14809
+ var READONLY_PROVIDERS = /* @__PURE__ */ new Set(["vercel"]);
14810
+ function isDetectedProvider(provider2) {
14811
+ return DETECTED_PROVIDERS.includes(provider2);
14812
+ }
14813
+ function installHintFor(provider2) {
14814
+ const normalizedProvider = provider2.toLowerCase().trim();
14815
+ if (isDetectedProvider(normalizedProvider)) {
14816
+ return INSTALL_HINTS[normalizedProvider];
14817
+ }
14818
+ return `Add the ${provider2} MCP server to your agent config.`;
14819
+ }
14820
+ function detectConnectedTools() {
14821
+ const tools = {};
14822
+ for (const provider2 of DETECTED_PROVIDERS) {
14823
+ const config = findUsableProviderMcpConfig(provider2);
14824
+ if (!config) {
14825
+ tools[`${provider2}Mcp`] = "missing";
14826
+ continue;
14827
+ }
14828
+ tools[`${provider2}Mcp`] = READONLY_PROVIDERS.has(provider2) ? "available_readonly" : "available";
14829
+ }
14830
+ tools.browser = "available";
14831
+ return tools;
14832
+ }
14833
+
14834
+ // src/demo/runDemo.ts
14835
+ var import_node_fs13 = require("node:fs");
14836
+ var import_node_path27 = __toESM(require("node:path"));
14837
+
14838
+ // src/demo/localRules.ts
14839
+ var import_promises20 = require("node:fs/promises");
14840
+ var import_node_path26 = __toESM(require("node:path"));
14841
+ var DEMO_SCANNED_AT = "2026-06-14T00:00:00.000Z";
14842
+ async function readKnownFile(root, relativePath) {
14843
+ try {
14844
+ return await (0, import_promises20.readFile)(import_node_path26.default.join(root, relativePath), "utf8");
14845
+ } catch (error) {
14846
+ const code = error.code;
14847
+ if (code === "ENOENT") return "";
14848
+ throw error;
14849
+ }
14850
+ }
14851
+ function dependencyNames(packageJson) {
14852
+ if (!packageJson.trim()) return /* @__PURE__ */ new Set();
14853
+ const parsed = JSON.parse(packageJson);
14854
+ return /* @__PURE__ */ new Set([
14855
+ ...Object.keys(parsed.dependencies ?? {}),
14856
+ ...Object.keys(parsed.devDependencies ?? {})
14857
+ ]);
14858
+ }
14859
+ function stripTypeScriptComments(source) {
14860
+ return source.replace(/\/\*[\s\S]*?\*\//g, "").split("\n").map((line) => {
14861
+ let quote = null;
14862
+ let escaped = false;
14863
+ for (let index = 0; index < line.length - 1; index += 1) {
14864
+ const char = line[index];
14865
+ const next = line[index + 1];
14866
+ if (quote) {
14867
+ if (escaped) {
14868
+ escaped = false;
14869
+ } else if (char === "\\") {
14870
+ escaped = true;
14871
+ } else if (char === quote) {
14872
+ quote = null;
14873
+ }
14874
+ continue;
14875
+ }
14876
+ if (char === '"' || char === "'" || char === "`") {
14877
+ quote = char;
14878
+ continue;
14879
+ }
14880
+ if (char === "/" && next === "/") {
14881
+ return line.slice(0, index);
14882
+ }
14883
+ }
14884
+ return line;
14885
+ }).join("\n");
14886
+ }
14887
+ function stripSqlComments(source) {
14888
+ return source.replace(/\/\*[\s\S]*?\*\//g, "").replace(/--.*$/gm, "");
14889
+ }
14890
+ function hasStripeWebhookSignatureVerification(source) {
14891
+ const executable = stripTypeScriptComments(source);
14892
+ return /webhooks\s*\.\s*constructEvent\s*\(/.test(executable) && /Stripe-Signature/.test(executable);
14893
+ }
14894
+ function hasEnabledRowLevelSecurity(source) {
14895
+ return /enable\s+row\s+level\s+security/i.test(stripSqlComments(source));
14896
+ }
14897
+ function hasEnvAssignment(source, name) {
14898
+ const pattern = new RegExp(`^\\s*${name}\\s*=`, "m");
14899
+ return pattern.test(source);
14900
+ }
14901
+ function gap(input) {
14902
+ return {
14903
+ ...input,
14904
+ toolSuggestions: [],
14905
+ mcpSuggestion: null,
14906
+ affectedMapCategories: input.affectedMapCategories ?? []
14907
+ };
14908
+ }
14909
+ function emptyMissionGraph() {
14910
+ return {
14911
+ areas: [],
14912
+ byArea: {},
14913
+ byProvider: {},
14914
+ repositoryEvidence: {
14915
+ env: [],
14916
+ security: []
14917
+ }
14918
+ };
14919
+ }
14920
+ async function scanDemoFixture(root) {
14921
+ const [packageJson, stripeWebhook, migration, envExample] = await Promise.all([
14922
+ readKnownFile(root, "package.json"),
14923
+ readKnownFile(root, "app/api/stripe/webhook/route.ts"),
14924
+ readKnownFile(root, "supabase/migrations/0001_init.sql"),
14925
+ readKnownFile(root, ".env.example")
14926
+ ]);
14927
+ const dependencies = dependencyNames(packageJson);
14928
+ const hasNext = dependencies.has("next");
14929
+ const hasSupabase = dependencies.has("@supabase/supabase-js");
14930
+ const hasStripe = dependencies.has("stripe");
14931
+ const selectedProviders = {};
14932
+ if (hasNext) selectedProviders.deployment = "vercel";
14933
+ if (hasSupabase) {
14934
+ selectedProviders.database = "supabase";
14935
+ selectedProviders.auth = "supabase";
14936
+ }
14937
+ if (hasStripe) selectedProviders.payments = "stripe";
14938
+ const gaps = [];
14939
+ if (hasStripe && !hasStripeWebhookSignatureVerification(stripeWebhook)) {
14940
+ gaps.push(
14941
+ gap({
14942
+ id: "stripe_webhook_signature_missing",
14943
+ category: "SECURITY & AUTH",
14944
+ severity: "critical",
14945
+ title: "Verify Stripe webhook signatures",
14946
+ detail: "The demo Stripe webhook handler does not verify the Stripe-Signature header with stripe.webhooks.constructEvent.",
14947
+ 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.",
14948
+ primaryMapCategory: "payments",
14949
+ affectedMapCategories: ["security"]
14950
+ })
14951
+ );
14952
+ }
14953
+ if (hasSupabase && !hasEnabledRowLevelSecurity(migration)) {
14954
+ gaps.push(
14955
+ gap({
14956
+ id: "rls_disabled",
14957
+ category: "DATABASE & DATA",
14958
+ severity: "critical",
14959
+ title: "Enable Supabase row level security",
14960
+ detail: "The demo migration creates application data without enabling row level security.",
14961
+ copyPrompt: "Add alter table statements that enable row level security and add least-privilege policies for the Supabase tables in the demo migration.",
14962
+ primaryMapCategory: "database",
14963
+ affectedMapCategories: ["auth", "security"]
14964
+ })
14965
+ );
14966
+ }
14967
+ if (hasStripe && !hasEnvAssignment(envExample, "STRIPE_WEBHOOK_SECRET")) {
14968
+ gaps.push(
14969
+ gap({
14970
+ id: "missing_prod_env_stripe_webhook_secret",
14971
+ category: "DEPLOYMENT",
14972
+ severity: "warning",
14973
+ title: "Document STRIPE_WEBHOOK_SECRET",
14974
+ detail: "The demo .env.example does not include a STRIPE_WEBHOOK_SECRET assignment.",
14975
+ 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.",
14976
+ primaryMapCategory: "deployment",
14977
+ affectedMapCategories: ["payments"]
14978
+ })
14979
+ );
14980
+ }
14981
+ const hasCriticalGaps = gaps.some((item3) => item3.severity === "critical");
14982
+ return {
14983
+ version: 1,
14984
+ scannedAt: DEMO_SCANNED_AT,
14985
+ workspacePath: root,
14986
+ score: hasCriticalGaps ? 55 : 90,
14987
+ scoreLabel: hasCriticalGaps ? "Needs work" : "Looks solid",
14988
+ summary: `Demo scan: ${gaps.length} launch gap(s) detected by local rules.`,
14989
+ archetype: hasNext && hasSupabase && hasStripe ? "nextjs-supabase-stripe" : "demo-saas",
14990
+ gaps,
14991
+ missionGraph: emptyMissionGraph(),
14992
+ stackWiring: { items: [], byKey: {} },
14993
+ providerRegistry: {
14994
+ version: 1,
14995
+ source: "bundled",
14996
+ generatedAt: DEMO_SCANNED_AT,
14997
+ staleAfterDays: 30,
14998
+ status: "fresh",
14999
+ providers: []
15000
+ },
15001
+ verificationSummary: { byArea: {} },
15002
+ productionCorePercent: hasCriticalGaps ? 40 : 95,
15003
+ selectedProviders
15004
+ };
15005
+ }
15006
+
15007
+ // src/demo/runDemo.ts
15008
+ function bundledFixtureRoot() {
15009
+ const candidates = [
15010
+ import_node_path27.default.join(__dirname, "..", "fixtures", "demo-saas"),
15011
+ import_node_path27.default.join(__dirname, "..", "..", "fixtures", "demo-saas")
15012
+ ];
15013
+ return candidates.find((candidate) => (0, import_node_fs13.existsSync)(import_node_path27.default.join(candidate, "package.json"))) ?? candidates[0];
15014
+ }
15015
+ async function runDemo(options) {
15016
+ const label2 = options.label ?? "demo";
15017
+ const connectedTools = detectConnectedTools();
15018
+ const fixtureRoot = options.fixtureRoot ?? bundledFixtureRoot();
15019
+ const scannedArtifact = await scanDemoFixture(fixtureRoot);
15020
+ const artifact = {
15021
+ ...scannedArtifact,
15022
+ workspacePath: options.cwd
15023
+ };
15024
+ await writeScanArtifacts({
15025
+ artifact,
15026
+ cwd: options.cwd,
15027
+ connectedTools,
15028
+ prpMode: "demo"
15029
+ });
15030
+ if (options.agentMode) {
15031
+ console.log(
15032
+ 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.`
15033
+ );
15034
+ const prp = generateProductionProtocol(artifact, { mode: "demo", connectedTools });
15035
+ const mode = currentRenderMode("demo");
15036
+ printOperatorBlock(prp, buildTaskList(artifact), mode);
15037
+ const celebration = renderClearCelebration(prp, mode);
15038
+ if (celebration) {
15039
+ console.log(celebration);
15040
+ }
15041
+ } else {
15042
+ console.log(
15043
+ label2 === "demo" ? "VibeRaven demo complete. See .viberaven/prp.json" : `VibeRaven ${label2 === "try" ? "Try" : "showcase run"} complete. See .viberaven/prp.json`
15044
+ );
15045
+ }
15046
+ return 0;
15047
+ }
15048
+
14330
15049
  // src/cli.ts
14331
15050
  function printHelp() {
14332
15051
  console.log(`viberaven ${VERSION} \u2014 launch readiness for AI-built apps
@@ -14356,6 +15075,18 @@ Usage:
14356
15075
  viberaven --agent-mode [--json|--jsonl] [path]
14357
15076
  Agent-first scan; writes tasklist, gate-result, context-map, and per-gap JSON
14358
15077
 
15078
+ viberaven --demo [path]
15079
+ No-login demo scan over the bundled fixture; writes demo artifacts locally
15080
+
15081
+ viberaven --agent-mode --demo [path]
15082
+ Demo scan with operator output; no login, no managed API spend
15083
+
15084
+ viberaven try [path]
15085
+ Free no-login local try run over a bundled fixture; no OpenAI key or managed API spend
15086
+
15087
+ viberaven --showcase --agent-mode [path]
15088
+ Alias for the no-login local try run
15089
+
14359
15090
  viberaven --strict[=warning] [path]
14360
15091
  Fail when production gate is not clear; warning mode also fails on warnings
14361
15092
 
@@ -14394,6 +15125,9 @@ Usage:
14394
15125
  viberaven audit --vercel-supabase [--json] [path]
14395
15126
  Local Vercel/Supabase repo evidence audit (RLS, pooler, secrets)
14396
15127
 
15128
+ viberaven badge [--svg|--card] [path]
15129
+ Print a README badge, terminal card, or write .viberaven/badge.svg from .viberaven/prp.json
15130
+
14397
15131
 
14398
15132
 
14399
15133
  Agent workflow (Claude Code / Codex):
@@ -14453,16 +15187,62 @@ function parseArgs(argv) {
14453
15187
  continue;
14454
15188
  }
14455
15189
  if (!command) {
14456
- command = arg;
15190
+ if (isRootModePositional(flags, arg)) {
15191
+ positional.push(arg);
15192
+ } else {
15193
+ command = arg;
15194
+ }
14457
15195
  } else {
14458
15196
  positional.push(arg);
14459
15197
  }
14460
15198
  }
14461
15199
  return { command, flags, positional };
14462
15200
  }
15201
+ var KNOWN_COMMANDS = /* @__PURE__ */ new Set([
15202
+ "audit",
15203
+ "badge",
15204
+ "connect",
15205
+ "doctor",
15206
+ "guide",
15207
+ "init",
15208
+ "interactive",
15209
+ "login",
15210
+ "logout",
15211
+ "next",
15212
+ "open",
15213
+ "prompt",
15214
+ "provider-verify",
15215
+ "report",
15216
+ "scan",
15217
+ "stack",
15218
+ "status",
15219
+ "tui",
15220
+ "try",
15221
+ "validate-npm-package",
15222
+ "version",
15223
+ "watch"
15224
+ ]);
15225
+ function isRootModePositional(flags, token) {
15226
+ if (KNOWN_COMMANDS.has(token)) {
15227
+ return false;
15228
+ }
15229
+ return [
15230
+ "agent-mode",
15231
+ "demo",
15232
+ "showcase",
15233
+ "json",
15234
+ "jsonl",
15235
+ "strict",
15236
+ "condense",
15237
+ "verify",
15238
+ "heal"
15239
+ ].some((key) => flags[key] !== void 0);
15240
+ }
14463
15241
  function isBooleanFlag(command, key) {
14464
15242
  if ([
14465
15243
  "agent-mode",
15244
+ "demo",
15245
+ "showcase",
14466
15246
  "json",
14467
15247
  "jsonl",
14468
15248
  "condense",
@@ -14480,6 +15260,8 @@ function isBooleanFlag(command, key) {
14480
15260
  if (key === "open" && (command === "" || command === "scan" || command === "report")) return true;
14481
15261
  if (key === "verify" && command === "") return true;
14482
15262
  if (key === "vercel-supabase" && command === "audit") return true;
15263
+ if (key === "svg" && command === "badge") return true;
15264
+ if (key === "card" && command === "badge") return true;
14483
15265
  if (key === "json" && command === "validate-npm-package") return true;
14484
15266
  if (key === "dry-run" && command === "init") return true;
14485
15267
  if (key === "agents" && command === "doctor") return true;
@@ -14499,7 +15281,7 @@ async function guardEarlyVerifyScan(input) {
14499
15281
  if (!verifyLike) {
14500
15282
  return void 0;
14501
15283
  }
14502
- const workspacePath = input.positional[0] ? (0, import_node_path26.join)(process.cwd(), input.positional[0]) : await resolveWorkspaceRoot(process.cwd());
15284
+ const workspacePath = input.positional[0] ? (0, import_node_path28.join)(process.cwd(), input.positional[0]) : await resolveWorkspaceRoot(process.cwd());
14503
15285
  const loopState = await loadLoopState(workspacePath);
14504
15286
  if (loopState.batchApplied <= 0) {
14505
15287
  return void 0;
@@ -14523,7 +15305,19 @@ async function guardEarlyVerifyScan(input) {
14523
15305
  return void 0;
14524
15306
  }
14525
15307
  const nextTask = remainingRepoCodeTasks[0];
15308
+ const progress = renderLoopProgress(
15309
+ {
15310
+ applied: loopState.batchApplied,
15311
+ total: batchSize,
15312
+ label: "Healing repo gaps",
15313
+ silentInPlain: true
15314
+ },
15315
+ currentRenderMode("human-command")
15316
+ );
14526
15317
  console.error("SCAN_DEFERRED: Local heal batch is not full yet, so VibeRaven is protecting scan quota.");
15318
+ if (progress) {
15319
+ console.error(progress);
15320
+ }
14527
15321
  console.error(`Batch progress: ${loopState.batchApplied}/${batchSize} local heals applied since the last scan.`);
14528
15322
  console.error(`Next local heal: npx -y viberaven --heal --apply --gap ${nextTask.gapId} --yes`);
14529
15323
  console.error("Run verify again after the batch is full, or add --force-scan if the user explicitly wants to spend a scan now.");
@@ -14538,6 +15332,36 @@ function resolveDefaultEntrypointMode(options) {
14538
15332
  function formatScanJsonStdout(artifact) {
14539
15333
  return JSON.stringify(sanitizeArtifactForDisk(artifact), null, 2);
14540
15334
  }
15335
+ function providerFromConnectedToolKey(tool) {
15336
+ if (!tool.endsWith("Mcp")) return void 0;
15337
+ return tool.slice(0, -3).toLowerCase();
15338
+ }
15339
+ function normalizeProviderForConnectedToolHint(provider2) {
15340
+ const lower = provider2.trim().toLowerCase();
15341
+ const tokens = lower.split(/[^a-z0-9]+/).filter(Boolean);
15342
+ for (const knownProvider of ["supabase", "stripe", "vercel", "github"]) {
15343
+ if (lower === knownProvider || tokens.includes(knownProvider)) {
15344
+ return knownProvider;
15345
+ }
15346
+ }
15347
+ return lower;
15348
+ }
15349
+ function printMissingConnectedToolHints(prp) {
15350
+ const relevantProviders = new Set([
15351
+ ...prp.detectedStack.auth,
15352
+ ...prp.detectedStack.database,
15353
+ ...prp.detectedStack.deployment,
15354
+ ...prp.detectedStack.payments
15355
+ ].map(normalizeProviderForConnectedToolHint));
15356
+ for (const [tool, state] of Object.entries(prp.connectedTools)) {
15357
+ const provider2 = providerFromConnectedToolKey(tool);
15358
+ if (!provider2 || state !== "missing" || !relevantProviders.has(provider2)) {
15359
+ continue;
15360
+ }
15361
+ console.log(`${provider2} MCP is not connected. For stronger verification, add it:`);
15362
+ console.log(` ${installHintFor(provider2)}`);
15363
+ }
15364
+ }
14541
15365
  async function cmdLogin(flags) {
14542
15366
  const apiBaseUrl = resolveApiBaseUrl(typeof flags["api-url"] === "string" ? flags["api-url"] : void 0);
14543
15367
  await runDeviceLogin(apiBaseUrl);
@@ -14575,7 +15399,7 @@ async function cmdStatus(flags, positional) {
14575
15399
  console.log("Not signed in. Run: viberaven login");
14576
15400
  return 1;
14577
15401
  }
14578
- const startDir = positional[0] ? (0, import_node_path26.join)(process.cwd(), positional[0]) : process.cwd();
15402
+ const startDir = positional[0] ? (0, import_node_path28.join)(process.cwd(), positional[0]) : process.cwd();
14579
15403
  let artifact;
14580
15404
  try {
14581
15405
  artifact = await loadLastArtifact(startDir);
@@ -14729,7 +15553,7 @@ async function cmdWatch(flags) {
14729
15553
  }
14730
15554
  }
14731
15555
  async function runScanCommand(flags, positional, options) {
14732
- const workspacePath = positional[0] ? (0, import_node_path26.join)(process.cwd(), positional[0]) : await resolveWorkspaceRoot(process.cwd());
15556
+ const workspacePath = positional[0] ? (0, import_node_path28.join)(process.cwd(), positional[0]) : await resolveWorkspaceRoot(process.cwd());
14733
15557
  const apiBaseUrl = resolveApiBaseUrl(typeof flags["api-url"] === "string" ? flags["api-url"] : void 0);
14734
15558
  let accessToken;
14735
15559
  try {
@@ -14775,17 +15599,14 @@ async function runScanCommand(flags, positional, options) {
14775
15599
  return { exitCode: 1 };
14776
15600
  }
14777
15601
  const artifact = await enrichArtifactWithAccount(result.artifact, apiBaseUrl, accessToken);
14778
- const paths = await writeScanArtifacts({ artifact, cwd: workspacePath });
15602
+ const connectedTools = detectConnectedTools();
15603
+ const paths = await writeScanArtifacts({ artifact, cwd: workspacePath, connectedTools });
14779
15604
  if (flags.json && !options?.deferMachineOutput) {
14780
15605
  console.log(formatScanJsonStdout(artifact));
14781
15606
  return { exitCode: 0, artifacts: paths };
14782
15607
  }
14783
15608
  if (!options?.deferMachineOutput) {
14784
15609
  printScanSummary(artifact, paths);
14785
- if (flags["agent-mode"]) {
14786
- const prp = JSON.parse(await (0, import_promises19.readFile)(paths.prpPath, "utf8"));
14787
- console.log(renderProductionProtocolSummary(prp));
14788
- }
14789
15610
  }
14790
15611
  if (artifact.usage && !options?.deferMachineOutput) {
14791
15612
  console.log(formatUsageLine(artifact.usage));
@@ -14795,6 +15616,14 @@ async function runScanCommand(flags, positional, options) {
14795
15616
  const openGapCount = artifact.gaps.length;
14796
15617
  const updatedState = resetBatch(loopState, openGapCount);
14797
15618
  const tasks = buildTaskList(artifact);
15619
+ const prp = generateProductionProtocol(artifact, { connectedTools });
15620
+ const mode = currentRenderMode("agent-mode");
15621
+ printOperatorBlock(prp, tasks, mode);
15622
+ const celebration = renderClearCelebration(prp, mode);
15623
+ if (celebration) {
15624
+ console.log(celebration);
15625
+ }
15626
+ printMissingConnectedToolHints(prp);
14798
15627
  const plan = artifact.plan ?? "free";
14799
15628
  const block = buildNextActionBlock(tasks, updatedState, plan);
14800
15629
  printNextActionBlock(block);
@@ -14811,7 +15640,7 @@ async function runScanCommand(flags, positional, options) {
14811
15640
  return { exitCode: 0, artifacts: paths };
14812
15641
  }
14813
15642
  async function cmdReport(flags, positional) {
14814
- const startDir = positional[0] ? (0, import_node_path26.join)(process.cwd(), positional[0]) : process.cwd();
15643
+ const startDir = positional[0] ? (0, import_node_path28.join)(process.cwd(), positional[0]) : process.cwd();
14815
15644
  try {
14816
15645
  const paths = await refreshReportFromDisk(startDir);
14817
15646
  console.log(`Report refreshed: ${paths.reportPath}`);
@@ -14833,7 +15662,7 @@ async function cmdReport(flags, positional) {
14833
15662
  }
14834
15663
  }
14835
15664
  async function cmdPrompt(flags, positional) {
14836
- const startDir = positional[0] ? (0, import_node_path26.join)(process.cwd(), positional[0]) : process.cwd();
15665
+ const startDir = positional[0] ? (0, import_node_path28.join)(process.cwd(), positional[0]) : process.cwd();
14837
15666
  let artifact;
14838
15667
  try {
14839
15668
  artifact = await loadLastArtifact(startDir);
@@ -14841,26 +15670,26 @@ async function cmdPrompt(flags, positional) {
14841
15670
  console.error(error instanceof Error ? error.message : "No scan found. Run: viberaven scan");
14842
15671
  return 1;
14843
15672
  }
14844
- const gap = pickGap(artifact, {
15673
+ const gap2 = pickGap(artifact, {
14845
15674
  gapId: typeof flags.gap === "string" ? flags.gap : void 0,
14846
15675
  provider: typeof flags.provider === "string" ? flags.provider : void 0,
14847
15676
  area: typeof flags.area === "string" ? flags.area : void 0
14848
15677
  });
14849
- if (!gap) {
15678
+ if (!gap2) {
14850
15679
  console.error("No matching gap. Run `viberaven scan` or pass --gap <id>.");
14851
15680
  return 1;
14852
15681
  }
14853
15682
  const skipCopy = flags["no-copy"] === true;
14854
15683
  if (!skipCopy) {
14855
15684
  try {
14856
- await copyToClipboard(gap.copyPrompt);
14857
- console.log(`Copied to clipboard: ${gap.title}`);
15685
+ await copyToClipboard(gap2.copyPrompt);
15686
+ console.log(`Copied to clipboard: ${gap2.title}`);
14858
15687
  return 0;
14859
15688
  } catch (error) {
14860
15689
  console.warn(error instanceof Error ? error.message : String(error));
14861
15690
  }
14862
15691
  }
14863
- console.log(gap.copyPrompt);
15692
+ console.log(gap2.copyPrompt);
14864
15693
  return 0;
14865
15694
  }
14866
15695
  var STACK_AREAS = /* @__PURE__ */ new Set(["database", "auth", "payments", "deployment", "monitoring", "security"]);
@@ -14931,7 +15760,7 @@ async function main() {
14931
15760
  const wantsJsonl = hasFlag(flags, "jsonl");
14932
15761
  const wantsStrict = hasFlag(flags, "strict");
14933
15762
  if (flags.condense) {
14934
- const cwd = positional[0] ? (0, import_node_path26.join)(process.cwd(), positional[0]) : process.cwd();
15763
+ const cwd = positional[0] ? (0, import_node_path28.join)(process.cwd(), positional[0]) : process.cwd();
14935
15764
  const result = await runCondenseCommand({ cwd });
14936
15765
  console.log(`VibeRaven context map refreshed: ${result.contextMapPath}`);
14937
15766
  return 0;
@@ -14949,6 +15778,14 @@ async function main() {
14949
15778
  console.log(JSON.stringify(result, null, 2));
14950
15779
  return result.status.startsWith("refused") || result.status === "failed" ? 1 : 0;
14951
15780
  }
15781
+ if (command === "try") {
15782
+ const cwd = positional[0] ? (0, import_node_path28.join)(process.cwd(), positional[0]) : process.cwd();
15783
+ return runDemo({ cwd, agentMode: true, label: "try" });
15784
+ }
15785
+ if (hasFlag(flags, "demo") || hasFlag(flags, "showcase")) {
15786
+ const cwd = positional[0] ? (0, import_node_path28.join)(process.cwd(), positional[0]) : process.cwd();
15787
+ return runDemo({ cwd, agentMode: isAgentMode, label: hasFlag(flags, "showcase") ? "showcase" : "demo" });
15788
+ }
14952
15789
  if (!command && (isAgentMode || flags.verify === true || wantsJson || wantsJsonl || wantsStrict)) {
14953
15790
  const guardedExitCode = await guardEarlyVerifyScan({ flags, positional, wantsStrict });
14954
15791
  if (guardedExitCode !== void 0) {
@@ -14960,7 +15797,7 @@ async function main() {
14960
15797
  console.error("VibeRaven could not produce machine output because scan artifacts were not written.");
14961
15798
  return 3;
14962
15799
  }
14963
- const gateResult = scanResult.artifacts && (wantsJson || wantsJsonl || wantsStrict) ? JSON.parse(await (0, import_promises19.readFile)(scanResult.artifacts.gateResultPath, "utf8")) : void 0;
15800
+ const gateResult = scanResult.artifacts && (wantsJson || wantsJsonl || wantsStrict) ? JSON.parse(await (0, import_promises21.readFile)(scanResult.artifacts.gateResultPath, "utf8")) : void 0;
14964
15801
  const strictExitCode = wantsStrict && gateResult ? exitCodeForStrictGate(gateResult, { failOnWarnings: flags.strict === "warning" }) : scanResult.exitCode;
14965
15802
  if (wantsJson && gateResult) {
14966
15803
  process.stdout.write(renderGateResultJson(gateResult));
@@ -14995,7 +15832,7 @@ async function main() {
14995
15832
  case "next":
14996
15833
  return runNextCommand({
14997
15834
  json: Boolean(flags.json),
14998
- cwd: positional[0] ? (0, import_node_path26.join)(process.cwd(), positional[0]) : process.cwd()
15835
+ cwd: positional[0] ? (0, import_node_path28.join)(process.cwd(), positional[0]) : process.cwd()
14999
15836
  });
15000
15837
  case "guide": {
15001
15838
  const provider2 = positional[0];
@@ -15033,7 +15870,7 @@ async function main() {
15033
15870
  case "provider-verify":
15034
15871
  return cmdProviderVerify(flags, positional);
15035
15872
  case "init": {
15036
- const cwd = positional[0] ? (0, import_node_path26.join)(process.cwd(), positional[0]) : process.cwd();
15873
+ const cwd = positional[0] ? (0, import_node_path28.join)(process.cwd(), positional[0]) : process.cwd();
15037
15874
  const agents = typeof flags.agents === "string" ? flags.agents : void 0;
15038
15875
  return runInitCommand({
15039
15876
  cwd,
@@ -15047,7 +15884,7 @@ async function main() {
15047
15884
  return 1;
15048
15885
  }
15049
15886
  return runDoctorAgentsCommand({
15050
- cwd: positional[0] ? (0, import_node_path26.join)(process.cwd(), positional[0]) : process.cwd()
15887
+ cwd: positional[0] ? (0, import_node_path28.join)(process.cwd(), positional[0]) : process.cwd()
15051
15888
  });
15052
15889
  case "validate-npm-package":
15053
15890
  return runValidateNpmPackageCommand({
@@ -15060,9 +15897,15 @@ async function main() {
15060
15897
  return 1;
15061
15898
  }
15062
15899
  return runAuditCommand({
15063
- cwd: positional[0] ? (0, import_node_path26.join)(process.cwd(), positional[0]) : process.cwd(),
15900
+ cwd: positional[0] ? (0, import_node_path28.join)(process.cwd(), positional[0]) : process.cwd(),
15064
15901
  json: Boolean(flags.json)
15065
15902
  });
15903
+ case "badge":
15904
+ return runBadgeCommand({
15905
+ cwd: positional[0] ? (0, import_node_path28.join)(process.cwd(), positional[0]) : process.cwd(),
15906
+ svg: flags.svg === true,
15907
+ card: flags.card === true
15908
+ });
15066
15909
  default:
15067
15910
  console.error(`Unknown command: ${command}`);
15068
15911
  printHelp();