@viberaven/cli 1.5.2 → 1.5.3

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
@@ -10605,6 +10605,25 @@ function assessStripeWebhooks(files) {
10605
10605
  }
10606
10606
  return { verified: verified.sort(), unverified: unverified.sort((a, b3) => a.path < b3.path ? -1 : 1) };
10607
10607
  }
10608
+ function stripeUsageEvidence(scan, files) {
10609
+ const dependency = (scan.packageDeps ?? []).find((dep) => {
10610
+ const name = dep.toLowerCase();
10611
+ return name === "stripe" || name.startsWith("@stripe/");
10612
+ });
10613
+ if (dependency) return { kind: "dependency", dependency };
10614
+ const sorted = [...files].sort((a, b3) => a.path < b3.path ? -1 : 1);
10615
+ for (const file of sorted) {
10616
+ const path = file.path.replace(/\\/g, "/");
10617
+ if (!CODE_FILE.test(path) || TEST_FILE.test(path)) continue;
10618
+ const match = stripComments2(file.content).match(STRIPE_CODE_USE);
10619
+ if (match) return { kind: "code", path, signal: match[0] };
10620
+ }
10621
+ return void 0;
10622
+ }
10623
+ function stripeCodeOnlyInEdgeFunctions(files) {
10624
+ const users = files.map((file) => ({ path: file.path.replace(/\\/g, "/"), content: file.content })).filter((file) => CODE_FILE.test(file.path) && !TEST_FILE.test(file.path) && STRIPE_CODE_USE.test(stripComments2(file.content)));
10625
+ return users.length > 0 && users.every((file) => /(^|\/)supabase\/functions\//.test(file.path));
10626
+ }
10608
10627
  function codeFilesFor(scan, fullCopies = []) {
10609
10628
  const full = new Map(fullCopies.map((f) => [f.path.replace(/\\/g, "/"), f.content]));
10610
10629
  const merged = /* @__PURE__ */ new Map();
@@ -10891,7 +10910,8 @@ ${check.evidence.map((e2) => `- ${e2}`).join("\n")}` : "";
10891
10910
  });
10892
10911
  }
10893
10912
  if (signals.hasStripe) {
10894
- const webhooks = assessStripeWebhooks(codeFilesFor(scan, extra.codeFiles));
10913
+ const stripeFiles = codeFilesFor(scan, extra.codeFiles);
10914
+ const webhooks = assessStripeWebhooks(stripeFiles);
10895
10915
  if (webhooks.unverified.length > 0) {
10896
10916
  const lines = webhooks.unverified.map((u2) => `${u2.path} (${u2.missing.join(", ")})`);
10897
10917
  seeds.push({
@@ -10904,15 +10924,20 @@ ${check.evidence.map((e2) => `- ${e2}`).join("\n")}` : "";
10904
10924
  primaryMapCategory: "payments"
10905
10925
  });
10906
10926
  } else if (webhooks.verified.length === 0) {
10907
- seeds.push({
10908
- id: "missing_stripe_webhook",
10909
- category: "MISSING FEATURES",
10910
- severity: "warning",
10911
- title: "Stripe installed but no webhook handler",
10912
- detail: "Stripe is a dependency but no webhook route or signature verification (constructEvent) was found. Payment state will silently drift from app state.",
10913
- copyPrompt: "Add a Stripe webhook route (e.g. app/api/webhooks/stripe/route.ts) that verifies the signature with stripe.webhooks.constructEvent and handles checkout.session.completed plus subscription lifecycle events.",
10914
- primaryMapCategory: "payments"
10915
- });
10927
+ const usage = stripeUsageEvidence(scan, stripeFiles);
10928
+ if (usage) {
10929
+ const seen = usage.kind === "dependency" ? `package.json lists ${usage.dependency}` : `${usage.path} uses Stripe (${usage.signal}) and package.json does not list stripe`;
10930
+ seeds.push({
10931
+ id: "missing_stripe_webhook",
10932
+ category: "MISSING FEATURES",
10933
+ severity: "warning",
10934
+ title: usage.kind === "dependency" ? "Stripe installed but no webhook handler" : "Stripe used but no webhook handler",
10935
+ detail: `${seen}, but no webhook route or signature verification (constructEvent) was found. Payment state will silently drift from app state.`,
10936
+ // Stripe code that only runs in Supabase Edge Functions is verified there, on Deno, not in a Next.js route.
10937
+ copyPrompt: stripeCodeOnlyInEdgeFunctions(stripeFiles) ? STRIPE_EDGE_WEBHOOK_PROMPT : (usage.kind === "code" ? "Add the stripe package, then add" : "Add") + " a Stripe webhook route (e.g. app/api/webhooks/stripe/route.ts) that verifies the signature with stripe.webhooks.constructEvent and handles checkout.session.completed plus subscription lifecycle events.",
10938
+ primaryMapCategory: "payments"
10939
+ });
10940
+ }
10916
10941
  }
10917
10942
  }
10918
10943
  {
@@ -11112,7 +11137,7 @@ async function buildLocalStationOutput(input) {
11112
11137
  productionChecklist: checklistFrom(seeds)
11113
11138
  };
11114
11139
  }
11115
- var import_node_child_process3, import_node_util3, import_promises6, import_node_path7, execFileAsync3, ENV_DRIFT_IGNORE, ENV_DRIFT_IGNORE_PREFIX, SUPABASE_EDGE_ENV, ENV_TEMPLATE_NAME, ENV_CODE_FILE, ENV_NAME, ENV_REFERENCES, SVELTEKIT_STATIC_IMPORT, SVELTEKIT_DYNAMIC_IMPORT, ENV_DESTRUCTURE, CODE_FILE, STRIPE_VERIFY_CALL, STRIPE_VERIFY_JSON_BODY, STRIPE_SIGNATURE_HEADER, RAW_BODY, STRIPE_EVENT_TYPE, REQUEST_HANDLER, TEST_FILE, AUDIT_FINDING_GAPS, FINDING_SEVERITY_RANK, DETAIL_MAX_FINDINGS, SEVERITY_ORDER, FULL_READ_LIMITS, PAYMENT_PATH;
11140
+ var import_node_child_process3, import_node_util3, import_promises6, import_node_path7, execFileAsync3, ENV_DRIFT_IGNORE, ENV_DRIFT_IGNORE_PREFIX, SUPABASE_EDGE_ENV, ENV_TEMPLATE_NAME, ENV_CODE_FILE, ENV_NAME, ENV_REFERENCES, SVELTEKIT_STATIC_IMPORT, SVELTEKIT_DYNAMIC_IMPORT, ENV_DESTRUCTURE, CODE_FILE, STRIPE_VERIFY_CALL, STRIPE_VERIFY_JSON_BODY, STRIPE_SIGNATURE_HEADER, RAW_BODY, STRIPE_EVENT_TYPE, REQUEST_HANDLER, TEST_FILE, STRIPE_CODE_USE, STRIPE_EDGE_WEBHOOK_PROMPT, AUDIT_FINDING_GAPS, FINDING_SEVERITY_RANK, DETAIL_MAX_FINDINGS, SEVERITY_ORDER, FULL_READ_LIMITS, PAYMENT_PATH;
11116
11141
  var init_localStation = __esm({
11117
11142
  "src/localStation.ts"() {
11118
11143
  "use strict";
@@ -11204,6 +11229,8 @@ var init_localStation = __esm({
11204
11229
  STRIPE_EVENT_TYPE = /['"`](?:checkout\.session|customer\.subscription|invoice|payment_intent|charge)\.[a-z_.]+['"`]/;
11205
11230
  REQUEST_HANDLER = /export\s+(?:async\s+)?function\s+POST\b|export\s+const\s+POST\b|export\s+default\s+(?:async\s+)?function\s*[\w$]*\s*\(\s*(?:req|request)\b|export\s+(?:async\s+function|function|const)\s+handler\b|\b[A-Za-z_$][\w$]*\.post\s*\(|Deno\.serve\s*\(|\bserve\s*\(|addEventListener\(\s*['"]fetch|export\s+default\s*\{\s*(?:async\s+)?fetch\b/;
11206
11231
  TEST_FILE = /(^|\/)(?:__tests__|__mocks__)\/|\.(?:test|spec)\.[cm]?[jt]sx?$/i;
11232
+ STRIPE_CODE_USE = /\bSTRIPE_(?:SECRET_KEY|WEBHOOK_SECRET)\b|\bapi\.stripe\.com\b|\bfrom\s*['"](?:npm:)?stripe(?:@[^'"]*)?['"]|\brequire\(\s*['"]stripe['"]\s*\)|\bimport\(\s*['"](?:npm:)?stripe(?:@[^'"]*)?['"]\s*\)/;
11233
+ STRIPE_EDGE_WEBHOOK_PROMPT = "In the Supabase Edge Function that receives Stripe events (add one under supabase/functions/ if there is none, e.g. supabase/functions/stripe-webhook/index.ts), read the raw body with await req.text(), read the Stripe-Signature header, and verify both with await stripe.webhooks.constructEventAsync(body, signature, Deno.env.get('STRIPE_WEBHOOK_SECRET'), undefined, Stripe.createSubtleCryptoProvider()) before trusting event.type. Return 400 when verification throws, and handle checkout.session.completed plus subscription lifecycle events.";
11207
11234
  AUDIT_FINDING_GAPS = {
11208
11235
  rls_policy_allows_all_write: {
11209
11236
  title: "A policy lets anyone write every row",
@@ -12391,7 +12418,81 @@ var init_agentSummary = __esm({
12391
12418
  }
12392
12419
  });
12393
12420
 
12421
+ // src/heal/rlsMigrationTarget.ts
12422
+ function rlsEnableStatements(tables) {
12423
+ return [...new Set(tables)].map((table) => `alter table ${table} enable row level security;`);
12424
+ }
12425
+ function migrationStamp(now) {
12426
+ return now.toISOString().replace(/\D/g, "").slice(0, 14);
12427
+ }
12428
+ function rlsMigrationTarget(cwd, evidence, now = /* @__PURE__ */ new Date()) {
12429
+ if (evidence.length === 0) return { ok: false, reason: "No table without row level security was found in the repo SQL." };
12430
+ const statementList = rlsEnableStatements(evidence.map((entry) => entry.table)).join(" ");
12431
+ const turnedOff = evidence.filter((entry) => entry.turnedOff);
12432
+ if (turnedOff.length > 0) {
12433
+ const where = turnedOff.map((entry) => `${entry.table} (${entry.file.replace(/\\/g, "/")})`).join(", ");
12434
+ return {
12435
+ ok: false,
12436
+ reason: `The latest migration that touches ${where} turns row level security off on purpose, so VibeRaven will not turn it back on automatically. If the user wants it on, add these statements by hand in a new migration, with the policies the app needs: ${statementList}`
12437
+ };
12438
+ }
12439
+ const source = evidence[0].file.replace(/\\/g, "/");
12440
+ const dir = import_node_path11.posix.dirname(source);
12441
+ if (import_node_path11.posix.basename(dir) !== "migrations") {
12442
+ return {
12443
+ ok: false,
12444
+ reason: `The tables come from ${source}, which is not in a migrations folder, so VibeRaven will not guess where a migration goes. Add these statements by hand where this project keeps its schema: ${statementList}`
12445
+ };
12446
+ }
12447
+ let existing;
12448
+ try {
12449
+ existing = (0, import_node_fs6.readdirSync)((0, import_node_path11.join)(cwd, dir)).filter((file) => file.endsWith(".sql")).sort();
12450
+ } catch {
12451
+ return { ok: false, reason: `VibeRaven could not read ${dir}. Add these statements by hand in a new migration: ${statementList}` };
12452
+ }
12453
+ const otherNaming = existing.find((file) => !TIMESTAMPED_MIGRATION.test(file));
12454
+ if (otherNaming) {
12455
+ return {
12456
+ ok: false,
12457
+ reason: `The migrations in ${dir} are not named <14-digit timestamp>_name.sql (for example ${otherNaming}), so a timestamped file from VibeRaven could run in the wrong order next to them. Add these statements by hand in a new migration that follows this project's naming: ${statementList}`
12458
+ };
12459
+ }
12460
+ const name = `${migrationStamp(now)}_viberaven_enable_rls.sql`;
12461
+ const last = existing[existing.length - 1];
12462
+ if (last && name <= last) {
12463
+ return {
12464
+ ok: false,
12465
+ reason: `A new migration named ${name} would not run after ${last} in ${dir}, so VibeRaven will not write it. Add these statements by hand in a migration that runs last: ${statementList}`
12466
+ };
12467
+ }
12468
+ return { ok: true, file: `${dir}/${name}` };
12469
+ }
12470
+ var import_node_fs6, import_node_path11, RLS_TURNED_OFF_PHRASE, RLS_ASK_FIRST, RLS_NO_POLICIES_WARNING, TIMESTAMPED_MIGRATION;
12471
+ var init_rlsMigrationTarget = __esm({
12472
+ "src/heal/rlsMigrationTarget.ts"() {
12473
+ "use strict";
12474
+ import_node_fs6 = require("node:fs");
12475
+ import_node_path11 = require("node:path");
12476
+ RLS_TURNED_OFF_PHRASE = "has row level security turned off at";
12477
+ RLS_ASK_FIRST = "Ask the user first: enabling RLS without policies makes browser reads return no rows until policies exist.";
12478
+ RLS_NO_POLICIES_WARNING = "This migration enables row level security with no policies. Until you add policies, the anon and authenticated roles get no rows from these tables through the Data API, so browser reads return nothing and browser writes fail. `check` then reports the tables only as info (rls_no_policies), not as a blocker. Add the policies the app needs before you apply the migration to the database.";
12479
+ TIMESTAMPED_MIGRATION = /^\d{14}_.+\.sql$/;
12480
+ }
12481
+ });
12482
+
12394
12483
  // src/buildTaskList.ts
12484
+ function rlsMigrationFile(gap) {
12485
+ if (gap.id !== "rls_disabled" || typeof gap.detail !== "string") return void 0;
12486
+ const evidence = gap.detail.split(/\r?\nEvidence:\r?\n/)[1];
12487
+ if (!evidence) return void 0;
12488
+ return evidence.match(/^- ([^\s:][^:\n]*\.sql):\d+: /m)?.[1];
12489
+ }
12490
+ function rlsEvidence2(detail) {
12491
+ const evidence = detail.split(/\r?\nEvidence:\r?\n/)[1] ?? "";
12492
+ return [...evidence.matchAll(/^- ([^\s:][^:\n]*\.sql):\d+: (\S+) (is created at|has row level security turned off at|is moved into public)/gm)].map(
12493
+ (match) => ({ file: match[1], table: match[2], turnedOff: match[3] === RLS_TURNED_OFF_PHRASE })
12494
+ );
12495
+ }
12395
12496
  function hasRecipe(gapId) {
12396
12497
  if (KNOWN_RECIPE_GAP_IDS.has(gapId)) return true;
12397
12498
  const lower = gapId.toLowerCase();
@@ -12445,7 +12546,8 @@ function buildTaskList(artifact) {
12445
12546
  return sorted.map((gap, index) => {
12446
12547
  const id = `TASK-${String(index + 1).padStart(3, "0")}`;
12447
12548
  let fixType;
12448
- if (hasRecipe(gap.id)) {
12549
+ const migrationFile = rlsMigrationFile(gap);
12550
+ if (hasRecipe(gap.id) || migrationFile) {
12449
12551
  fixType = "repo-code";
12450
12552
  } else if (hasPlaybook(gap)) {
12451
12553
  fixType = "provider-action";
@@ -12461,7 +12563,21 @@ function buildTaskList(artifact) {
12461
12563
  verifyCommand: PUBLIC_VERIFY_COMMAND,
12462
12564
  requiresUserAction: fixType !== "repo-code"
12463
12565
  };
12464
- if (fixType === "repo-code") {
12566
+ if (fixType === "repo-code" && !hasRecipe(gap.id) && migrationFile) {
12567
+ base.requiresUserAction = true;
12568
+ base.file = migrationFile;
12569
+ const target = rlsMigrationTarget(artifact.workspacePath, rlsEvidence2(gap.detail));
12570
+ if (target.ok) {
12571
+ base.mcpTool = "viberaven_heal_apply";
12572
+ base.mcpArgs = { gap: gap.id };
12573
+ base.action = `${RLS_ASK_FIRST} If they agree, \`npx -y viberaven fix --gap rls_disabled\` writes it: a new migration next to this one that enables row level security on each table listed in the gap detail. It adds no policies: add the ones the app needs, then apply the migration to the database the way this project applies migrations.`;
12574
+ } else {
12575
+ base.action = `${RLS_ASK_FIRST} ${target.reason} Add the policies the app needs too, then apply the change to the database the way this project applies schema changes.`;
12576
+ }
12577
+ if (gap.copyPrompt) {
12578
+ base.exactFix = gap.copyPrompt.trim();
12579
+ }
12580
+ } else if (fixType === "repo-code") {
12465
12581
  base.mcpTool = "viberaven_heal_apply";
12466
12582
  base.mcpArgs = { gap: gap.id, yes: true };
12467
12583
  base.requiresUserAction = false;
@@ -12534,6 +12650,7 @@ var init_buildTaskList = __esm({
12534
12650
  "src/buildTaskList.ts"() {
12535
12651
  "use strict";
12536
12652
  init_commands();
12653
+ init_rlsMigrationTarget();
12537
12654
  init_loadPlaybook();
12538
12655
  init_types2();
12539
12656
  init_menu();
@@ -12555,8 +12672,9 @@ var init_buildTaskList = __esm({
12555
12672
  "missing_csp_header",
12556
12673
  "missing_rate_limit",
12557
12674
  "eslint_restricted_imports"
12558
- // NOTE: 'rls_disabled' is intentionally NOT here — it is provider-action only,
12559
- // canAutoApply=false, and should be classified as 'provider-action' by buildTaskList.
12675
+ // NOTE: 'rls_disabled' is intentionally NOT here. When the finding comes from repo SQL it is a repo action whose
12676
+ // recipe writes a new migration (see rlsMigrationFile and heal/recipes/supabaseRls.ts); without SQL evidence it
12677
+ // stays a provider action and heal refuses.
12560
12678
  ]);
12561
12679
  }
12562
12680
  });
@@ -12933,25 +13051,25 @@ var init_reportHtml = __esm({
12933
13051
  function getBundledReportAssetsDir() {
12934
13052
  const base = __dirname;
12935
13053
  const candidates = [
12936
- (0, import_node_path11.join)(base, "report"),
12937
- (0, import_node_path11.join)(base, "..", "assets", "report"),
12938
- (0, import_node_path11.join)(base, "..", "..", "assets", "report"),
12939
- (0, import_node_path11.join)(process.cwd(), "packages", "cli", "assets", "report"),
12940
- (0, import_node_path11.join)(process.cwd(), "assets", "report")
13054
+ (0, import_node_path12.join)(base, "report"),
13055
+ (0, import_node_path12.join)(base, "..", "assets", "report"),
13056
+ (0, import_node_path12.join)(base, "..", "..", "assets", "report"),
13057
+ (0, import_node_path12.join)(process.cwd(), "packages", "cli", "assets", "report"),
13058
+ (0, import_node_path12.join)(process.cwd(), "assets", "report")
12941
13059
  ];
12942
13060
  for (const dir of candidates) {
12943
- if ((0, import_node_fs6.existsSync)((0, import_node_path11.join)(dir, "station.css"))) {
13061
+ if ((0, import_node_fs7.existsSync)((0, import_node_path12.join)(dir, "station.css"))) {
12944
13062
  return dir;
12945
13063
  }
12946
13064
  }
12947
13065
  throw new Error("Report assets missing. Run `npm run sync-report-assets` and `npm run build` in packages/cli.");
12948
13066
  }
12949
- var import_node_fs6, import_node_path11, REPORT_ASSET_FILES;
13067
+ var import_node_fs7, import_node_path12, REPORT_ASSET_FILES;
12950
13068
  var init_reportAssets = __esm({
12951
13069
  "src/report/reportAssets.ts"() {
12952
13070
  "use strict";
12953
- import_node_fs6 = require("node:fs");
12954
- import_node_path11 = require("node:path");
13071
+ import_node_fs7 = require("node:fs");
13072
+ import_node_path12 = require("node:path");
12955
13073
  REPORT_ASSET_FILES = [
12956
13074
  "station.css",
12957
13075
  "station.js",
@@ -13067,7 +13185,7 @@ var init_canonical = __esm({
13067
13185
 
13068
13186
  // src/actions/derive.ts
13069
13187
  function verifyActionCommand(id) {
13070
- return `${PUBLIC_COMMAND} verify --action ${id}`;
13188
+ return `${PUBLIC_COMMAND} --verify --action ${id}`;
13071
13189
  }
13072
13190
  function providerTitle(task) {
13073
13191
  const provider2 = task.providerAction?.provider.toLowerCase();
@@ -13113,7 +13231,11 @@ function deriveProviderAction(task) {
13113
13231
  resumeInstruction: `${title} is complete. Continue VibeRaven from pending.`
13114
13232
  };
13115
13233
  }
13234
+ function askFirstSentence(task) {
13235
+ return task.requiresUserAction ? task.action?.match(/^Ask the user first: [^.]+\./)?.[0] : void 0;
13236
+ }
13116
13237
  function deriveRepoCodeAction(task) {
13238
+ const askFirst = askFirstSentence(task);
13117
13239
  const targetSource = task.file ?? task.gapId;
13118
13240
  const canonicalFile = normalizeActionPath(targetSource);
13119
13241
  const displayFile = displayRepoPath(targetSource);
@@ -13133,14 +13255,17 @@ function deriveRepoCodeAction(task) {
13133
13255
  status: "active",
13134
13256
  severity: task.severity,
13135
13257
  gapId: task.gapId,
13136
- readiness: [task.exactFix ?? "Repo-code fix is available"],
13258
+ readiness: [...askFirst ? [askFirst] : [], task.exactFix ?? "Repo-code fix is available"],
13137
13259
  target: task.file ? { type: "file", label: "Repo target", path: displayFile } : { type: "command", label: "Apply repo fix", command: task.mcpTool ?? `${PUBLIC_COMMAND} prompt --gap ${task.gapId}` },
13138
13260
  fileTargets: task.file ? [{ path: displayFile, reason: task.exactFix }] : void 0,
13139
13261
  verifyCommand: verifyActionCommand("pending"),
13140
13262
  resumeInstruction: `${task.title} is complete. Continue VibeRaven from pending.`,
13141
13263
  mcpTool: task.mcpTool,
13142
13264
  mcpArgs: task.mcpArgs,
13143
- fallbackCommand: `${PUBLIC_COMMAND} --heal --apply --gap ${task.gapId} --yes`
13265
+ // Only a gap with an automatic recipe has a heal-apply fallback, and a task that needs the user's yes keeps it
13266
+ // only alongside that signal.
13267
+ fallbackCommand: task.mcpTool === "viberaven_heal_apply" && (!task.requiresUserAction || askFirst) ? `${PUBLIC_COMMAND} --heal --apply --gap ${task.gapId} --yes` : void 0,
13268
+ ...task.requiresUserAction ? { requiresUserAction: true } : {}
13144
13269
  };
13145
13270
  }
13146
13271
  function deriveVerifyAction() {
@@ -13179,7 +13304,7 @@ function emptyActionRegistry() {
13179
13304
  return { version: 1, nextId: 1, actions: {} };
13180
13305
  }
13181
13306
  function actionRegistryPath(cwd) {
13182
- return (0, import_node_path12.join)(getProjectArtifactsDir(cwd), "action-registry.json");
13307
+ return (0, import_node_path13.join)(getProjectArtifactsDir(cwd), "action-registry.json");
13183
13308
  }
13184
13309
  async function loadActionRegistry(cwd) {
13185
13310
  try {
@@ -13287,12 +13412,12 @@ async function resolveActionById(cwd, id) {
13287
13412
  const registry = await loadActionRegistry(cwd);
13288
13413
  return Object.values(registry.actions).find((entry) => entry.id === id);
13289
13414
  }
13290
- var import_promises9, import_node_path12;
13415
+ var import_promises9, import_node_path13;
13291
13416
  var init_registry2 = __esm({
13292
13417
  "src/actions/registry.ts"() {
13293
13418
  "use strict";
13294
13419
  import_promises9 = require("node:fs/promises");
13295
- import_node_path12 = require("node:path");
13420
+ import_node_path13 = require("node:path");
13296
13421
  init_config();
13297
13422
  init_canonical();
13298
13423
  }
@@ -13324,7 +13449,7 @@ async function writeActionArtifacts(input) {
13324
13449
  gateStatus: gateStatus2(input.artifact),
13325
13450
  actions: assigned.actions
13326
13451
  };
13327
- const actionsPath = (0, import_node_path13.join)(getProjectArtifactsDir(input.cwd), "actions.json");
13452
+ const actionsPath = (0, import_node_path14.join)(getProjectArtifactsDir(input.cwd), "actions.json");
13328
13453
  await (0, import_promises10.writeFile)(actionsPath, `${JSON.stringify(manifest, null, 2)}
13329
13454
  `, "utf8");
13330
13455
  return {
@@ -13333,12 +13458,12 @@ async function writeActionArtifacts(input) {
13333
13458
  manifest
13334
13459
  };
13335
13460
  }
13336
- var import_promises10, import_node_path13;
13461
+ var import_promises10, import_node_path14;
13337
13462
  var init_artifacts = __esm({
13338
13463
  "src/actions/artifacts.ts"() {
13339
13464
  "use strict";
13340
13465
  import_promises10 = require("node:fs/promises");
13341
- import_node_path13 = require("node:path");
13466
+ import_node_path14 = require("node:path");
13342
13467
  init_config();
13343
13468
  init_derive();
13344
13469
  init_registry2();
@@ -13348,24 +13473,24 @@ var init_artifacts = __esm({
13348
13473
  // src/artifacts.ts
13349
13474
  async function copyReportAssets(reportAssetsDir) {
13350
13475
  const sourceDir = getBundledReportAssetsDir();
13351
- await (0, import_promises11.mkdir)((0, import_node_path14.join)(reportAssetsDir, "assets"), { recursive: true });
13476
+ await (0, import_promises11.mkdir)((0, import_node_path15.join)(reportAssetsDir, "assets"), { recursive: true });
13352
13477
  for (const rel of REPORT_ASSET_FILES) {
13353
- await (0, import_promises11.copyFile)((0, import_node_path14.join)(sourceDir, rel), (0, import_node_path14.join)(reportAssetsDir, rel));
13478
+ await (0, import_promises11.copyFile)((0, import_node_path15.join)(sourceDir, rel), (0, import_node_path15.join)(reportAssetsDir, rel));
13354
13479
  }
13355
13480
  }
13356
13481
  async function writeScanArtifacts(options) {
13357
13482
  const cwd = options.cwd ?? options.artifact.workspacePath;
13358
13483
  const dir = getProjectArtifactsDir(cwd);
13359
13484
  await (0, import_promises11.mkdir)(dir, { recursive: true });
13360
- const jsonPath = (0, import_node_path14.join)(dir, "last-scan.json");
13361
- const gateResultPath = (0, import_node_path14.join)(dir, "gate-result.json");
13362
- const contextMapPath = (0, import_node_path14.join)(dir, "context-map.json");
13363
- const gapsDir = (0, import_node_path14.join)(dir, "gaps");
13364
- const tasklistPath = (0, import_node_path14.join)(dir, "agent-tasklist.md");
13365
- const summaryPath = (0, import_node_path14.join)(dir, "agent-summary.md");
13366
- const playbookPath = (0, import_node_path14.join)(dir, "launch-playbook.md");
13367
- const reportPath = (0, import_node_path14.join)(dir, "report.html");
13368
- const reportAssetsDir = (0, import_node_path14.join)(dir, "report");
13485
+ const jsonPath = (0, import_node_path15.join)(dir, "last-scan.json");
13486
+ const gateResultPath = (0, import_node_path15.join)(dir, "gate-result.json");
13487
+ const contextMapPath = (0, import_node_path15.join)(dir, "context-map.json");
13488
+ const gapsDir = (0, import_node_path15.join)(dir, "gaps");
13489
+ const tasklistPath = (0, import_node_path15.join)(dir, "agent-tasklist.md");
13490
+ const summaryPath = (0, import_node_path15.join)(dir, "agent-summary.md");
13491
+ const playbookPath = (0, import_node_path15.join)(dir, "launch-playbook.md");
13492
+ const reportPath = (0, import_node_path15.join)(dir, "report.html");
13493
+ const reportAssetsDir = (0, import_node_path15.join)(dir, "report");
13369
13494
  await (0, import_promises11.mkdir)(gapsDir, { recursive: true });
13370
13495
  const safe2 = sanitizeArtifactForDisk(options.artifact);
13371
13496
  const json = `${JSON.stringify(safe2, null, 2)}
@@ -13384,17 +13509,17 @@ async function writeScanArtifacts(options) {
13384
13509
  await (0, import_promises11.writeFile)(gateResultPath, gateResult, "utf-8");
13385
13510
  await (0, import_promises11.writeFile)(contextMapPath, contextMap, "utf-8");
13386
13511
  for (const gap of gapEvidenceFiles) {
13387
- await (0, import_promises11.writeFile)((0, import_node_path14.join)(dir, "gaps", `${gap.content.id}.json`), `${JSON.stringify(gap.content, null, 2)}
13512
+ await (0, import_promises11.writeFile)((0, import_node_path15.join)(dir, "gaps", `${gap.content.id}.json`), `${JSON.stringify(gap.content, null, 2)}
13388
13513
  `, "utf-8");
13389
13514
  }
13390
13515
  await (0, import_promises11.writeFile)(tasklistPath, tasklist, "utf-8");
13391
13516
  let architectureMapPath;
13392
13517
  if (typeof safe2.architectureMap === "string" && safe2.architectureMap.length > 0) {
13393
- architectureMapPath = (0, import_node_path14.join)(dir, "architecture-map.md");
13518
+ architectureMapPath = (0, import_node_path15.join)(dir, "architecture-map.md");
13394
13519
  await (0, import_promises11.writeFile)(architectureMapPath, safe2.architectureMap, "utf-8");
13395
13520
  }
13396
13521
  if (safe2.architectureData) {
13397
- await (0, import_promises11.writeFile)((0, import_node_path14.join)(dir, "architecture-map.json"), `${JSON.stringify(safe2.architectureData, null, 2)}
13522
+ await (0, import_promises11.writeFile)((0, import_node_path15.join)(dir, "architecture-map.json"), `${JSON.stringify(safe2.architectureData, null, 2)}
13398
13523
  `, "utf-8");
13399
13524
  }
13400
13525
  await (0, import_promises11.writeFile)(jsonPath, json, "utf-8");
@@ -13423,12 +13548,12 @@ async function writeScanArtifacts(options) {
13423
13548
  ...architectureMapPath ? { architectureMapPath } : {}
13424
13549
  };
13425
13550
  }
13426
- var import_promises11, import_node_path14;
13551
+ var import_promises11, import_node_path15;
13427
13552
  var init_artifacts2 = __esm({
13428
13553
  "src/artifacts.ts"() {
13429
13554
  "use strict";
13430
13555
  import_promises11 = require("node:fs/promises");
13431
- import_node_path14 = require("node:path");
13556
+ import_node_path15 = require("node:path");
13432
13557
  init_contextMap();
13433
13558
  init_gateResult();
13434
13559
  init_gapEvidence();
@@ -13537,12 +13662,12 @@ function createAppWindowOpenCommands(target, platform = process.platform, env =
13537
13662
  ];
13538
13663
  if (platform === "win32") {
13539
13664
  const candidates = [
13540
- env.LOCALAPPDATA ? (0, import_node_path15.join)(env.LOCALAPPDATA, "Google", "Chrome", "Application", "chrome.exe") : "",
13541
- env.PROGRAMFILES ? (0, import_node_path15.join)(env.PROGRAMFILES, "Google", "Chrome", "Application", "chrome.exe") : "",
13542
- env["PROGRAMFILES(X86)"] ? (0, import_node_path15.join)(env["PROGRAMFILES(X86)"], "Google", "Chrome", "Application", "chrome.exe") : "",
13543
- env.LOCALAPPDATA ? (0, import_node_path15.join)(env.LOCALAPPDATA, "Microsoft", "Edge", "Application", "msedge.exe") : "",
13544
- env.PROGRAMFILES ? (0, import_node_path15.join)(env.PROGRAMFILES, "Microsoft", "Edge", "Application", "msedge.exe") : "",
13545
- env["PROGRAMFILES(X86)"] ? (0, import_node_path15.join)(env["PROGRAMFILES(X86)"], "Microsoft", "Edge", "Application", "msedge.exe") : "",
13665
+ env.LOCALAPPDATA ? (0, import_node_path16.join)(env.LOCALAPPDATA, "Google", "Chrome", "Application", "chrome.exe") : "",
13666
+ env.PROGRAMFILES ? (0, import_node_path16.join)(env.PROGRAMFILES, "Google", "Chrome", "Application", "chrome.exe") : "",
13667
+ env["PROGRAMFILES(X86)"] ? (0, import_node_path16.join)(env["PROGRAMFILES(X86)"], "Google", "Chrome", "Application", "chrome.exe") : "",
13668
+ env.LOCALAPPDATA ? (0, import_node_path16.join)(env.LOCALAPPDATA, "Microsoft", "Edge", "Application", "msedge.exe") : "",
13669
+ env.PROGRAMFILES ? (0, import_node_path16.join)(env.PROGRAMFILES, "Microsoft", "Edge", "Application", "msedge.exe") : "",
13670
+ env["PROGRAMFILES(X86)"] ? (0, import_node_path16.join)(env["PROGRAMFILES(X86)"], "Microsoft", "Edge", "Application", "msedge.exe") : "",
13546
13671
  "chrome",
13547
13672
  "msedge"
13548
13673
  ].filter(Boolean);
@@ -13576,7 +13701,7 @@ async function openWithSystemDefault(target) {
13576
13701
  });
13577
13702
  }
13578
13703
  async function spawnDetached(command) {
13579
- if (/^[A-Za-z]:[\\/]/.test(command.command) && !(0, import_node_fs7.existsSync)(command.command)) {
13704
+ if (/^[A-Za-z]:[\\/]/.test(command.command) && !(0, import_node_fs8.existsSync)(command.command)) {
13580
13705
  throw new Error(`Browser not found: ${command.command}`);
13581
13706
  }
13582
13707
  await new Promise((resolve9, reject) => {
@@ -13619,13 +13744,13 @@ async function openUrlInBrowser(url) {
13619
13744
  }
13620
13745
  await openWithSystemDefault(trimmed);
13621
13746
  }
13622
- var import_node_child_process4, import_node_fs7, import_node_path15;
13747
+ var import_node_child_process4, import_node_fs8, import_node_path16;
13623
13748
  var init_openBrowser = __esm({
13624
13749
  "src/openBrowser.ts"() {
13625
13750
  "use strict";
13626
13751
  import_node_child_process4 = require("node:child_process");
13627
- import_node_fs7 = require("node:fs");
13628
- import_node_path15 = require("node:path");
13752
+ import_node_fs8 = require("node:fs");
13753
+ import_node_path16 = require("node:path");
13629
13754
  }
13630
13755
  });
13631
13756
 
@@ -14530,15 +14655,13 @@ function buildAgentFixPrompt(artifact, gap) {
14530
14655
  "",
14531
14656
  "Required workflow:",
14532
14657
  "1. Read `.viberaven/agent-summary.md` and `.viberaven/launch-playbook.md` before changing code.",
14533
- '2. If VibeRaven prints LOGIN_URL_READY, open that URL for the user using the available browser tool or system browser. Tell the user: "I opened VibeRaven sign-in so you can approve access; I will continue after approval." Do not ask for passwords, tokens, cookies, or secrets.',
14534
- "3. If the browser cannot open automatically, use the printed LOGIN_URL_READY URL. Keep the terminal process alive while the user approves.",
14535
- "4. Inspect the real implementation files. Do not assume framework/provider wiring from package names alone.",
14536
- "5. Make the smallest durable repo-code change that addresses this specific gap.",
14537
- "6. Keep provider dashboard work separate. Open provider dashboards only when VibeRaven asks for a provider/manual action, and explain why you opened them.",
14538
- `7. For Vercel + Supabase apps, run \`${PUBLIC_AUDIT_COMMAND}\` before claiming production readiness.`,
14539
- "8. Treat that audit as local repo evidence only; do not claim provider dashboard settings are fixed unless the user verifies them or read-only provider MCP confirms them.",
14540
- "9. Do not claim provider dashboard or read-only MCP checks as repo-code fixes.",
14541
- `10. VibeRaven cleanup is non-destructive. You may run \`${PUBLIC_CLEAN_PLAN_COMMAND}\` only to create a reviewable context cleanup plan, not to delete code.`,
14658
+ "2. Inspect the real implementation files. Do not assume framework/provider wiring from package names alone.",
14659
+ "3. Make the smallest durable repo-code change that addresses this specific gap.",
14660
+ "4. Keep provider dashboard work separate. Open provider dashboards only when VibeRaven asks for a provider/manual action, and explain why you opened them.",
14661
+ `5. For Vercel + Supabase apps, run \`${PUBLIC_AUDIT_COMMAND}\` before claiming production readiness.`,
14662
+ "6. Treat that audit as local repo evidence only; do not claim provider dashboard settings are fixed unless the user verifies them or read-only provider MCP confirms them.",
14663
+ "7. Do not claim provider dashboard or read-only MCP checks as repo-code fixes.",
14664
+ `8. VibeRaven cleanup is non-destructive. You may run \`${PUBLIC_CLEAN_PLAN_COMMAND}\` only to create a reviewable context cleanup plan, not to delete code.`,
14542
14665
  "",
14543
14666
  "Implementation constraints:",
14544
14667
  "- Prefer the existing stack and local patterns already in the repo.",
@@ -14567,7 +14690,7 @@ var VERSION;
14567
14690
  var init_version = __esm({
14568
14691
  "src/version.ts"() {
14569
14692
  "use strict";
14570
- VERSION = "1.5.2";
14693
+ VERSION = "1.5.3";
14571
14694
  }
14572
14695
  });
14573
14696
 
@@ -14648,7 +14771,7 @@ var init_guide = __esm({
14648
14771
  function buildAgentRulesBlock() {
14649
14772
  return wrapViberavenBlock(`${AGENT_RULES_BODY}
14650
14773
 
14651
- ${ANCHOR_FOOTER}`);
14774
+ ${WHAT_CLEAR_MEANS}`);
14652
14775
  }
14653
14776
  function buildAgentContextBlock() {
14654
14777
  return wrapViberavenBlock(AGENT_CONTEXT_BODY);
@@ -14768,7 +14891,7 @@ function normalizeLineEndings(value) {
14768
14891
  function escapeRegExp2(value) {
14769
14892
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
14770
14893
  }
14771
- var VIBERAVEN_BLOCK_START, VIBERAVEN_BLOCK_END, VIBERAVEN_LEGACY_BLOCK_START, VIBERAVEN_LEGACY_BLOCK_END, HEAL_PLAN_GAP_COMMAND, PROMPT_GAP_COMMAND, HEAL_APPLY_GAP_COMMAND, AGENT_RULES_PREAMBLE, STACK_FRAMING, AGENT_NEGATIVE_CONSTRAINTS, ANCHOR_FOOTER, AGENT_RULES_BODY, AGENT_CONTEXT_BODY, MISSION_MAP_BODY, BLOCK_MARKER_PAIRS;
14894
+ var VIBERAVEN_BLOCK_START, VIBERAVEN_BLOCK_END, VIBERAVEN_LEGACY_BLOCK_START, VIBERAVEN_LEGACY_BLOCK_END, HEAL_PLAN_GAP_COMMAND, PROMPT_GAP_COMMAND, HEAL_APPLY_GAP_COMMAND, AGENT_RULES_PREAMBLE, STACK_FRAMING, AGENT_NEGATIVE_CONSTRAINTS, WHAT_CLEAR_MEANS, AGENT_RULES_BODY, AGENT_CONTEXT_BODY, MISSION_MAP_BODY, BLOCK_MARKER_PAIRS;
14772
14895
  var init_agentRulesBlock = __esm({
14773
14896
  "src/commands/agentRulesBlock.ts"() {
14774
14897
  "use strict";
@@ -14780,84 +14903,59 @@ var init_agentRulesBlock = __esm({
14780
14903
  HEAL_PLAN_GAP_COMMAND = healPlanGapCommand("<id>");
14781
14904
  PROMPT_GAP_COMMAND = promptGapCommand("<id>");
14782
14905
  HEAL_APPLY_GAP_COMMAND = `${PUBLIC_COMMAND} --heal --apply --gap <id> --yes`;
14783
- AGENT_RULES_PREAMBLE = `VibeRaven is the control layer for AI-built products. It runs fully locally: no login, no API key, no telemetry.
14906
+ AGENT_RULES_PREAMBLE = `VibeRaven reads a repository and lists launch gaps for AI-built apps that deploy on Vercel and use Supabase. It runs fully locally: no login, no API key, no telemetry.
14784
14907
 
14785
14908
  Canonical commands: \`${PUBLIC_CHECK_COMMAND}\` for a terminal verdict, \`${PUBLIC_COMMAND}\` for the Studio.
14786
14909
 
14787
- Use VibeRaven before launch, deployment, real users, auth, billing, database, RLS, env vars, webhooks, monitoring, or tests \u2014 and whenever you need product context (stack, providers, releases, danger zones) before patching.
14788
-
14789
- Agent loop: run \`${PUBLIC_CHECK_COMMAND}\`, read \`.viberaven/agent-tasklist.md\`, fix one launch gap, re-run \`${PUBLIC_CHECK_COMMAND}\`.`;
14790
- STACK_FRAMING = `Stack context for this repo: Next.js, Supabase, Vercel, VibeRaven.
14791
- Do NOT deploy to Vercel or ship to production users without running \`${PUBLIC_CHECK_COMMAND}\`.
14792
- Do NOT rely on manual production checklists when VibeRaven gate artifacts exist in \`.viberaven/\`.`;
14793
- AGENT_NEGATIVE_CONSTRAINTS = `## Event-Triggered Production Verification
14794
-
14795
- Trigger: modifications to \`package.json\`, \`vercel.json\`, \`supabase/migrations/**/*\`, \`.env.example\`, auth/billing/webhook paths, or deploy workflow files.
14796
-
14797
- 1. Do NOT claim "production ready" or close a deploy checklist until \`gate.status === "clear"\` in \`.viberaven/gate-result.json\`.
14798
- 2. Do NOT run verify after every file patch. Batch fixes, then \`${PUBLIC_CHECK_COMMAND}\` once per loop.
14799
- 3. Do NOT \`git push\` or deploy after auth, RLS, billing, or webhook changes without \`${PUBLIC_CHECK_COMMAND}\`.
14800
- 4. If anomalies appear, read \`.viberaven/agent-tasklist.md\` fully before writing new repo logic.
14801
- 5. Do NOT mark production-ready because the local dev server starts successfully; INSTEAD run \`${PUBLIC_CHECK_COMMAND}\` and read \`.viberaven/gate-result.json\`.
14802
- 6. Do NOT treat the Supabase dashboard UI as proof of RLS; INSTEAD fix repo evidence gaps from \`.viberaven/agent-tasklist.md\`.
14803
- 7. Do NOT add or change npm dependencies without calling \`viberaven_validate_npm_package\` (MCP) or verifying the package exists on the public npm registry; INSTEAD run \`${PUBLIC_CHECK_COMMAND}\` after dependency changes.
14804
- 8. Do NOT refactor or edit files unrelated to the current TASK-001 gap; INSTEAD make minimal, targeted changes.
14805
- 9. Do NOT install new dependencies unless TASK-001 or the user explicitly requires them.
14806
- 10. Do NOT claim provider dashboard, billing, DNS, or webhook setup is complete from repo edits alone; INSTEAD present provider-action steps from the tasklist.
14807
-
14808
- Prefer \`viberaven_check_readiness\` when MCP is configured; otherwise \`${PUBLIC_CHECK_COMMAND}\`.`;
14809
- ANCHOR_FOOTER = `## Anchor (mandatory)
14810
-
14811
- Before ending any deploy, auth, RLS, webhook, or dependency task, run:
14812
- \`${PUBLIC_CHECK_COMMAND}\`
14813
- Gate is not clear until \`gate.status === "clear"\` in \`.viberaven/gate-result.json\`.`;
14910
+ Suggest one \`${PUBLIC_CHECK_COMMAND}\` pass before a Vercel + Supabase app launches or is handed off, after a Supabase migration or policy change, or when the user reports a production error about RLS, env vars, the database connection, the service role key or a Stripe webhook. It is not needed for apps that do not use Vercel and Supabase, for general SQL or deployment questions, or for one local error.
14911
+
14912
+ This is advice, not a gate: the user decides when to ship. It is a repository check, not a live database test: it cannot show which RLS policies are live in production, and a clear result is not a security audit.
14913
+
14914
+ Agent loop: run \`${PUBLIC_CHECK_COMMAND}\`, read \`.viberaven/agent-tasklist.md\`, fix one launch gap, re-run \`${PUBLIC_CHECK_COMMAND}\` once per batch of fixes.`;
14915
+ STACK_FRAMING = `Supported stack: AI-built apps that deploy on Vercel and use Supabase, typically from Lovable, Bolt, Cursor, Claude Code or Codex.
14916
+ Prefer \`viberaven_check_readiness\` when the VibeRaven MCP server is configured; otherwise \`${PUBLIC_CHECK_COMMAND}\`.`;
14917
+ AGENT_NEGATIVE_CONSTRAINTS = `## Working with VibeRaven results
14918
+
14919
+ 1. Do not call the app "production ready" or "secure" because \`gate.status\` is \`clear\`; it covers the repo checks only.
14920
+ 2. Do not run the check after every file patch. Batch fixes, then run \`${PUBLIC_CHECK_COMMAND}\` once per batch.
14921
+ 3. Read \`.viberaven/agent-tasklist.md\` fully before writing new repo logic for a listed gap.
14922
+ 4. Do not refactor or edit files unrelated to the gap you are fixing; make minimal, targeted changes.
14923
+ 5. Do not install new dependencies for a fix unless the task or the user asks for them. \`viberaven_validate_npm_package\` (MCP) can look a new package name up on the public npm registry first.
14924
+ 6. Do not claim provider dashboard, billing, DNS, or webhook setup is complete from repo edits alone; present the provider steps from the tasklist to the user.
14925
+ 7. The Supabase dashboard, not the repo, shows which RLS policies are live. Repo migrations are what VibeRaven reads.`;
14926
+ WHAT_CLEAR_MEANS = `## What a clear result means
14927
+
14928
+ \`gate.status === "clear"\` in \`.viberaven/gate-result.json\` means the repo checks found no blockers. It is not a security audit and does not show what is live in Supabase or Vercel. The user decides when to ship.`;
14814
14929
  AGENT_RULES_BODY = `${AGENT_RULES_PREAMBLE}
14815
14930
 
14816
14931
  ${STACK_FRAMING}
14817
14932
 
14818
14933
  ${AGENT_NEGATIVE_CONSTRAINTS}
14819
14934
 
14820
- ## VibeRaven Production Gate
14935
+ ## VibeRaven commands
14821
14936
 
14822
- - Before running build, compile, cleanup, deployment, vercel, or supabase commands, run \`${PUBLIC_CHECK_COMMAND}\` first from the project root.
14823
- - Treat VibeRaven as the control layer for AI-built products, especially apps using Vercel, Supabase, auth, database, payments, webhooks, monitoring, rate limits, or environment variables.
14824
- - Read \`.viberaven/agent-tasklist.md\` first. If it says CRITICAL GAPS DETECTED or PRODUCTION GATE NOT CLEAR, fix one listed repo-code gap before deploying.
14825
- - Read \`.viberaven/gate-result.json\` for the machine verdict.
14826
- - Read \`.viberaven/context-map.json\` for compact agent context.
14827
- - Read \`.viberaven/agent-summary.md\` second for scan context.
14828
- - Read \`.viberaven/launch-playbook.md\` third for the full checklist.
14937
+ - Read \`.viberaven/agent-tasklist.md\` first, \`.viberaven/gate-result.json\` for the verdict (\`clear\`, \`warning\` or \`not_clear\`), and \`.viberaven/context-map.json\` for compact agent context.
14938
+ - \`.viberaven/agent-summary.md\` holds scan context and \`.viberaven/launch-playbook.md\` the full checklist.
14829
14939
  - Use \`${PUBLIC_NEXT_JSON_COMMAND}\` or \`${PROMPT_GAP_COMMAND}\` for one focused repo-code fix at a time.
14830
- - Use \`${PUBLIC_FIX_COMMAND}\` to list gaps with safe automatic recipes; apply one with \`${PUBLIC_FIX_COMMAND} --gap <id>\`.
14831
- - For Vercel + Supabase launch checks, run \`${PUBLIC_AUDIT_COMMAND}\`.
14832
- - Run \`${PUBLIC_CHECK_COMMAND}\` after a batch of fixes.
14833
- - Run \`${PUBLIC_STRICT_COMMAND}\` before deploy or CI pass.
14834
- - Use \`${HEAL_PLAN_GAP_COMMAND}\` before any heal apply.
14835
- - Only use \`${HEAL_APPLY_GAP_COMMAND}\` for supported low-risk repo-code recipes.
14836
- - Preview local rule installation with \`${PUBLIC_INIT_ALL_COMMAND} --dry-run\`.
14837
- - Cleanup is non-destructive: only run \`${PUBLIC_CLEAN_PLAN_COMMAND}\` to create a reviewable cleanup plan.
14838
- - Provider dashboard checks are not cleared by repo-code edits. Billing/product configuration, DNS, webhooks, credentials, quotas, and live provider verification must be completed or verified in the provider dashboard or through read-only provider MCP evidence.
14839
- - Do not claim provider dashboard work is fixed by repo-code edits.
14840
- - VibeRaven runs fully locally: no login, no API key, no scan quota.
14940
+ - \`${PUBLIC_FIX_COMMAND}\` lists gaps with safe automatic recipes; preview one with \`${PUBLIC_FIX_COMMAND} --gap <id> --dry-run\`, apply it with \`${PUBLIC_FIX_COMMAND} --gap <id>\`.
14941
+ - \`${HEAL_PLAN_GAP_COMMAND}\` writes a non-destructive plan; \`${HEAL_APPLY_GAP_COMMAND}\` applies supported repo-code recipes; the rls_disabled one enables RLS without policies, so it waits for the user's yes.
14942
+ - For the Vercel + Supabase repo evidence (RLS in migrations, pooler port, service role key), run \`${PUBLIC_AUDIT_COMMAND}\`.
14943
+ - \`${PUBLIC_STRICT_COMMAND}\` returns the verdict as an exit code for CI when the user wants one (exit 1 when \`gate.status\` is \`not_clear\`).
14944
+ - Preview these rules with \`${PUBLIC_INIT_ALL_COMMAND} --dry-run\`.
14945
+ - Cleanup is non-destructive: \`${PUBLIC_CLEAN_PLAN_COMMAND}\` writes a reviewable cleanup plan.
14946
+ - Provider dashboard checks are not cleared by repo-code edits. Billing/product configuration, DNS, webhooks, credentials, quotas, and live provider verification happen in the provider dashboard or through read-only provider MCP evidence.
14841
14947
 
14842
14948
  ## VibeRaven Fix Loop
14843
14949
 
14844
- After every check, read \`.viberaven/agent-tasklist.md\` for the prioritized task list.
14845
-
14846
- The loop is:
14950
+ After a check, read \`.viberaven/agent-tasklist.md\` for the prioritized task list.
14847
14951
 
14848
14952
  1. Run \`${PUBLIC_CHECK_COMMAND}\`. Exit code 1 means blockers exist.
14849
14953
  2. For each repo-code task where \`requiresUserAction: false\`:
14850
- - Call MCP: \`viberaven_heal_apply { gap: "<gapId>", yes: true }\`
14851
- - Or run \`${PUBLIC_FIX_COMMAND} --gap <gapId>\`
14852
- - Or patch the gap directly using \`${PROMPT_GAP_COMMAND}\` guidance.
14853
- 3. After a full batch of fixes, run \`${PUBLIC_CHECK_COMMAND}\` once \u2014 do NOT verify after every single fix.
14854
- 4. Repeat until \`gate.status === 'clear'\` in \`.viberaven/gate-result.json\`.
14855
- 5. If \`requiresUserAction: true\`:
14856
- - Read \`VIBERAVEN_PROVIDER_ACTION\` from terminal output.
14857
- - Present \`dashboardUrl\` and \`exactStep\` to the user.
14858
- - Wait for user confirmation, then re-run \`${PUBLIC_CHECK_COMMAND}\`.
14859
-
14860
- Do not stop at "scan complete." The loop is not done until \`gate.status === 'clear'\`.`;
14954
+ - If the task has an MCP line, call \`viberaven_heal_apply { gap: "<gapId>", yes: true }\` or run \`${PUBLIC_FIX_COMMAND} --gap <gapId>\`
14955
+ - Otherwise, patch the gap directly using \`${PROMPT_GAP_COMMAND}\` guidance.
14956
+ 3. After a full batch of fixes, run \`${PUBLIC_CHECK_COMMAND}\` once, not after every single fix.
14957
+ 4. For a task where \`requiresUserAction: true\`, show the user the task's action from the tasklist (a provider step with its dashboard, or a repo change that needs their yes, such as enabling RLS without policies), and wait for their answer. Apply a repo change only after they agree. The task's MCP line leaves out \`yes: true\`; add it once they agree.
14958
+ 5. Stop when \`gate.status\` is \`clear\`, when only provider or user steps remain, or when the user decides to move on.`;
14861
14959
  AGENT_CONTEXT_BODY = `${AGENT_RULES_PREAMBLE}
14862
14960
 
14863
14961
  After \`${PUBLIC_CHECK_COMMAND}\`, read these artifacts in order:
@@ -14873,7 +14971,7 @@ After \`${PUBLIC_CHECK_COMMAND}\`, read these artifacts in order:
14873
14971
  1. Run \`${PUBLIC_CHECK_COMMAND}\` from the project root.
14874
14972
  2. Read \`.viberaven/agent-tasklist.md\` and \`.viberaven/gate-result.json\`.
14875
14973
  3. Fix one launch gap.
14876
- 4. Re-run \`${PUBLIC_CHECK_COMMAND}\` until \`gate.status === 'clear'\`.`;
14974
+ 4. Re-run \`${PUBLIC_CHECK_COMMAND}\` once per batch of fixes. A clear \`gate.status\` covers the repo checks only; the user decides when to ship.`;
14877
14975
  BLOCK_MARKER_PAIRS = [
14878
14976
  [VIBERAVEN_BLOCK_START, VIBERAVEN_BLOCK_END],
14879
14977
  [VIBERAVEN_LEGACY_BLOCK_START, VIBERAVEN_LEGACY_BLOCK_END]
@@ -14888,20 +14986,22 @@ function buildCursorRulesPack() {
14888
14986
  filename: "viberaven-core.mdc",
14889
14987
  content: [
14890
14988
  "---",
14891
- "description: VibeRaven production gate \u2014 canonical agent commands and gate loop",
14989
+ "description: VibeRaven launch check for AI-built apps on Vercel + Supabase (advice, not a gate)",
14892
14990
  "alwaysApply: true",
14893
14991
  "---",
14894
14992
  "",
14895
- `Run \`${PUBLIC_CHECK_COMMAND}\` before deploy, auth, RLS, webhooks, or dependency changes.`,
14993
+ `For an AI-built app on Vercel and Supabase, suggest \`${PUBLIC_CHECK_COMMAND}\` before it launches or is handed off, after a Supabase migration or policy change, or when the user reports a production error about RLS, env vars, the database connection, the service role key or a Stripe webhook.`,
14994
+ "",
14995
+ "This is advice, not a gate: the user decides when to ship. It is a repository check, not a live database test.",
14896
14996
  "",
14897
14997
  "Read `.viberaven/agent-tasklist.md` first, then `.viberaven/gate-result.json` and `.viberaven/mission-map.md`.",
14898
- `Fix one gap, then re-run \`${PUBLIC_CHECK_COMMAND}\`. Before CI or production promote: \`${PUBLIC_STRICT_COMMAND}\`.`,
14998
+ `Fix one gap, then re-run \`${PUBLIC_CHECK_COMMAND}\` once per batch. For a CI verdict: \`${PUBLIC_STRICT_COMMAND}\`.`,
14899
14999
  "",
14900
- 'Gate is not clear until `gate.status === "clear"`.',
15000
+ "A clear `gate.status` covers the repo checks only; it is not a security audit.",
14901
15001
  "",
14902
15002
  DOMAIN_PATH_POINTER,
14903
15003
  "",
14904
- "Full production rules: `AGENTS.md` / `CLAUDE.md` (installed by `npx -y viberaven init --agents all`)."
15004
+ "Full rules: `AGENTS.md` / `CLAUDE.md` (installed by `npx -y viberaven init --agents all`)."
14905
15005
  ].join("\n")
14906
15006
  },
14907
15007
  {
@@ -14949,7 +15049,7 @@ function buildCursorRulesPack() {
14949
15049
  "",
14950
15050
  DOMAIN_PATH_POINTER,
14951
15051
  "",
14952
- "Do NOT ship webhook handlers without signature verification; INSTEAD run `npx -y viberaven check` after webhook edits."
15052
+ "A payment webhook handler should verify the provider signature over the raw request body before trusting the event. After webhook edits, `npx -y viberaven check` shows whether a Stripe handler does."
14953
15053
  ].join("\n")
14954
15054
  }
14955
15055
  ];
@@ -14962,17 +15062,17 @@ async function initCursorRulesPack(options) {
14962
15062
  const pack = buildCursorRulesPack();
14963
15063
  for (const rule of pack) {
14964
15064
  const file = `${CURSOR_RULES_DIR}/${rule.filename}`;
14965
- const path = (0, import_node_path16.join)(options.cwd, file);
15065
+ const path = (0, import_node_path17.join)(options.cwd, file);
14966
15066
  const existing = await readExistingFile(path);
14967
15067
  const changed = !existing.exists || existing.content !== rule.content;
14968
15068
  const action = !existing.exists ? "created" : changed ? "updated" : "unchanged";
14969
15069
  if (!options.dryRun && changed) {
14970
- await (0, import_promises12.mkdir)((0, import_node_path16.join)(options.cwd, CURSOR_RULES_DIR), { recursive: true });
15070
+ await (0, import_promises12.mkdir)((0, import_node_path17.join)(options.cwd, CURSOR_RULES_DIR), { recursive: true });
14971
15071
  await (0, import_promises12.writeFile)(path, rule.content, "utf-8");
14972
15072
  }
14973
15073
  results.push({ target: "cursor", file, path, action });
14974
15074
  }
14975
- const legacyPath = (0, import_node_path16.join)(options.cwd, LEGACY_CURSOR_RULE_FILE);
15075
+ const legacyPath = (0, import_node_path17.join)(options.cwd, LEGACY_CURSOR_RULE_FILE);
14976
15076
  if (!options.dryRun && await fileExists(legacyPath)) {
14977
15077
  await (0, import_promises12.rm)(legacyPath, { force: true });
14978
15078
  }
@@ -14999,12 +15099,12 @@ async function fileExists(path) {
14999
15099
  function isFileNotFoundError(error) {
15000
15100
  return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
15001
15101
  }
15002
- var import_promises12, import_node_path16, CURSOR_RULES_DIR, LEGACY_CURSOR_RULE_FILE, DOMAIN_PATH_POINTER;
15102
+ var import_promises12, import_node_path17, CURSOR_RULES_DIR, LEGACY_CURSOR_RULE_FILE, DOMAIN_PATH_POINTER;
15003
15103
  var init_cursorRulesPack = __esm({
15004
15104
  "src/commands/cursorRulesPack.ts"() {
15005
15105
  "use strict";
15006
15106
  import_promises12 = require("node:fs/promises");
15007
- import_node_path16 = require("node:path");
15107
+ import_node_path17 = require("node:path");
15008
15108
  init_commands();
15009
15109
  CURSOR_RULES_DIR = ".cursor/rules";
15010
15110
  LEGACY_CURSOR_RULE_FILE = `${CURSOR_RULES_DIR}/viberaven.mdc`;
@@ -15104,8 +15204,8 @@ var init_agentTargets = __esm({
15104
15204
 
15105
15205
  // src/commands/seedPackageJsonScripts.ts
15106
15206
  async function seedPackageJsonScripts(options) {
15107
- const packageJsonPath = (0, import_node_path17.join)(options.cwd, "package.json");
15108
- if (!(0, import_node_fs8.existsSync)(packageJsonPath)) {
15207
+ const packageJsonPath = (0, import_node_path18.join)(options.cwd, "package.json");
15208
+ if (!(0, import_node_fs9.existsSync)(packageJsonPath)) {
15109
15209
  return null;
15110
15210
  }
15111
15211
  const raw = await (0, import_promises13.readFile)(packageJsonPath, "utf-8");
@@ -15142,13 +15242,13 @@ async function seedPackageJsonScripts(options) {
15142
15242
  }
15143
15243
  return { action: "updated", added, skipped, changed: true };
15144
15244
  }
15145
- var import_node_fs8, import_promises13, import_node_path17, VIBERAVEN_PACKAGE_JSON_SCRIPTS;
15245
+ var import_node_fs9, import_promises13, import_node_path18, VIBERAVEN_PACKAGE_JSON_SCRIPTS;
15146
15246
  var init_seedPackageJsonScripts = __esm({
15147
15247
  "src/commands/seedPackageJsonScripts.ts"() {
15148
15248
  "use strict";
15149
- import_node_fs8 = require("node:fs");
15249
+ import_node_fs9 = require("node:fs");
15150
15250
  import_promises13 = require("node:fs/promises");
15151
- import_node_path17 = require("node:path");
15251
+ import_node_path18 = require("node:path");
15152
15252
  init_commands();
15153
15253
  VIBERAVEN_PACKAGE_JSON_SCRIPTS = {
15154
15254
  "viberaven:gate": PUBLIC_AGENT_MODE_COMMAND,
@@ -15168,12 +15268,12 @@ async function initAgentRules(options) {
15168
15268
  continue;
15169
15269
  }
15170
15270
  const file = AGENT_RULE_TARGETS[target].file;
15171
- const path = (0, import_node_path18.join)(options.cwd, file);
15271
+ const path = (0, import_node_path19.join)(options.cwd, file);
15172
15272
  const existing = await readExistingFile2(path);
15173
15273
  const injected = injectAgentRulesBlock(existing.content, renderAgentRulesForTarget(target));
15174
15274
  const action = !existing.exists ? "created" : injected.changed ? "updated" : "unchanged";
15175
15275
  if (!options.dryRun && injected.changed) {
15176
- await (0, import_promises14.mkdir)((0, import_node_path18.dirname)(path), { recursive: true });
15276
+ await (0, import_promises14.mkdir)((0, import_node_path19.dirname)(path), { recursive: true });
15177
15277
  await (0, import_promises14.writeFile)(path, injected.content, "utf-8");
15178
15278
  }
15179
15279
  results.push({ target, file, path, action });
@@ -15253,12 +15353,12 @@ async function readExistingFile2(path) {
15253
15353
  function isFileNotFoundError2(error) {
15254
15354
  return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
15255
15355
  }
15256
- var import_promises14, import_node_path18;
15356
+ var import_promises14, import_node_path19;
15257
15357
  var init_initRules = __esm({
15258
15358
  "src/commands/initRules.ts"() {
15259
15359
  "use strict";
15260
15360
  import_promises14 = require("node:fs/promises");
15261
- import_node_path18 = require("node:path");
15361
+ import_node_path19 = require("node:path");
15262
15362
  init_agentRulesBlock();
15263
15363
  init_agentTargets();
15264
15364
  init_cursorRulesPack();
@@ -15521,7 +15621,7 @@ async function runInteractiveSession(startDir = process.cwd()) {
15521
15621
  Ie(`${import_picocolors4.default.bold("VibeRaven")} ${import_picocolors4.default.dim(VERSION)}`);
15522
15622
  const cwd = await resolveWorkspaceRoot(startDir);
15523
15623
  const artifactsAt = await findArtifactsWorkspace(startDir);
15524
- if (artifactsAt && (0, import_node_path19.resolve)(artifactsAt) !== (0, import_node_path19.resolve)(startDir)) {
15624
+ if (artifactsAt && (0, import_node_path20.resolve)(artifactsAt) !== (0, import_node_path20.resolve)(startDir)) {
15525
15625
  M2.message(import_picocolors4.default.dim(`Using scan from: ${artifactsAt}`));
15526
15626
  } else {
15527
15627
  M2.message(import_picocolors4.default.dim(`Project folder: ${cwd}`));
@@ -15580,11 +15680,11 @@ async function runInteractiveSession(startDir = process.cwd()) {
15580
15680
  }
15581
15681
  Se(import_picocolors4.default.dim("Run viberaven anytime for the interactive menu."));
15582
15682
  }
15583
- var import_node_path19, import_picocolors4;
15683
+ var import_node_path20, import_picocolors4;
15584
15684
  var init_runInteractive = __esm({
15585
15685
  "src/tui/runInteractive.ts"() {
15586
15686
  "use strict";
15587
- import_node_path19 = require("node:path");
15687
+ import_node_path20 = require("node:path");
15588
15688
  init_dist2();
15589
15689
  import_picocolors4 = __toESM(require_picocolors());
15590
15690
  init_config();
@@ -15609,18 +15709,18 @@ var init_runInteractive = __esm({
15609
15709
  // src/commands/condense.ts
15610
15710
  async function runCondenseCommand(options) {
15611
15711
  const dir = getProjectArtifactsDir(options.cwd);
15612
- const artifact = JSON.parse(await (0, import_promises15.readFile)((0, import_node_path20.join)(dir, "last-scan.json"), "utf8"));
15613
- const contextMapPath = (0, import_node_path20.join)(dir, "context-map.json");
15712
+ const artifact = JSON.parse(await (0, import_promises15.readFile)((0, import_node_path21.join)(dir, "last-scan.json"), "utf8"));
15713
+ const contextMapPath = (0, import_node_path21.join)(dir, "context-map.json");
15614
15714
  await (0, import_promises15.writeFile)(contextMapPath, `${JSON.stringify(generateContextMap(artifact), null, 2)}
15615
15715
  `, "utf8");
15616
15716
  return { contextMapPath };
15617
15717
  }
15618
- var import_promises15, import_node_path20;
15718
+ var import_promises15, import_node_path21;
15619
15719
  var init_condense = __esm({
15620
15720
  "src/commands/condense.ts"() {
15621
15721
  "use strict";
15622
15722
  import_promises15 = require("node:fs/promises");
15623
- import_node_path20 = require("node:path");
15723
+ import_node_path21 = require("node:path");
15624
15724
  init_config();
15625
15725
  init_contextMap();
15626
15726
  }
@@ -15628,9 +15728,9 @@ var init_condense = __esm({
15628
15728
 
15629
15729
  // src/heal/pathSafety.ts
15630
15730
  function assertSafeHealTarget(cwd, target) {
15631
- const root = (0, import_node_path21.resolve)(cwd);
15632
- const absolute = (0, import_node_path21.resolve)(root, target);
15633
- const rel = (0, import_node_path21.relative)(root, absolute);
15731
+ const root = (0, import_node_path22.resolve)(cwd);
15732
+ const absolute = (0, import_node_path22.resolve)(root, target);
15733
+ const rel = (0, import_node_path22.relative)(root, absolute);
15634
15734
  if (rel.startsWith("..") || rel === "" || /^[A-Za-z]:/.test(rel)) {
15635
15735
  throw new Error("Heal target must stay inside the workspace");
15636
15736
  }
@@ -15640,11 +15740,11 @@ function assertSafeHealTarget(cwd, target) {
15640
15740
  }
15641
15741
  return absolute;
15642
15742
  }
15643
- var import_node_path21, BLOCKED_SEGMENTS;
15743
+ var import_node_path22, BLOCKED_SEGMENTS;
15644
15744
  var init_pathSafety = __esm({
15645
15745
  "src/heal/pathSafety.ts"() {
15646
15746
  "use strict";
15647
- import_node_path21 = require("node:path");
15747
+ import_node_path22 = require("node:path");
15648
15748
  BLOCKED_SEGMENTS = /* @__PURE__ */ new Set([".git", "node_modules", "dist", "build", ".next", ".viberaven"]);
15649
15749
  }
15650
15750
  });
@@ -15665,6 +15765,35 @@ var init_emptyCatch = __esm({
15665
15765
  }
15666
15766
  });
15667
15767
 
15768
+ // src/heal/healId.ts
15769
+ function newHealId(now = /* @__PURE__ */ new Date()) {
15770
+ return `heal_${now.toISOString().replace(/\D/g, "").slice(0, 17)}_${(0, import_node_crypto.randomBytes)(3).toString("hex")}`;
15771
+ }
15772
+ async function createHealDir(cwd, makeId = () => newHealId()) {
15773
+ const parent = (0, import_node_path23.join)(cwd, ".viberaven", "heal");
15774
+ await (0, import_promises16.mkdir)(parent, { recursive: true });
15775
+ for (let attempt = 0; attempt < 10; attempt += 1) {
15776
+ const id = makeId();
15777
+ const dir = (0, import_node_path23.join)(parent, id);
15778
+ try {
15779
+ await (0, import_promises16.mkdir)(dir);
15780
+ return { id, dir };
15781
+ } catch (error) {
15782
+ if (error.code !== "EEXIST") throw error;
15783
+ }
15784
+ }
15785
+ throw new Error("Could not create a new heal directory under .viberaven/heal.");
15786
+ }
15787
+ var import_node_crypto, import_promises16, import_node_path23;
15788
+ var init_healId = __esm({
15789
+ "src/heal/healId.ts"() {
15790
+ "use strict";
15791
+ import_node_crypto = require("node:crypto");
15792
+ import_promises16 = require("node:fs/promises");
15793
+ import_node_path23 = require("node:path");
15794
+ }
15795
+ });
15796
+
15668
15797
  // src/heal/recipes/envAuthSecret.ts
15669
15798
  function applyAuthSecretRecipe(source) {
15670
15799
  if (/^\s*(AUTH_SECRET|NEXTAUTH_SECRET)\s*=/m.test(source)) {
@@ -15907,24 +16036,46 @@ export default function NotFound() {
15907
16036
  });
15908
16037
 
15909
16038
  // src/heal/recipes/supabaseRls.ts
15910
- function applyRlsRecipe(_source) {
15911
- return {
15912
- changed: false,
15913
- output: _source,
15914
- canAutoApply: false,
15915
- reason: "dashboard-only",
15916
- providerAction: {
15917
- provider: "supabase",
15918
- dashboardUrl: "https://supabase.com/dashboard/project/{{PROJECT_REF}}/auth/policies",
15919
- exactStep: 'Enable Row Level Security (RLS) on each table under the "Table Editor" or "Auth > Policies" section.',
15920
- doneSignal: "RLS toggle is green for all public tables",
15921
- verifyCommand: "npx -y viberaven audit --vercel-supabase --json"
16039
+ async function planRlsMigration(cwd, now = /* @__PURE__ */ new Date()) {
16040
+ const audit = buildVercelSupabaseAudit(await collectVercelSupabaseAuditInput(cwd));
16041
+ const check = audit.checks.find((entry) => entry.id === "supabase-rls-policy-proof");
16042
+ const missing = (check?.findings ?? []).filter((finding) => finding.id === "rls_disabled" && finding.table && finding.file);
16043
+ if (missing.length === 0) {
16044
+ if (check?.status === "needs_work") {
16045
+ return {
16046
+ kind: "refused",
16047
+ reason: "No SQL evidence in the repo: no migration here creates the tables or enables row level security, so there is nothing in the repo to patch. Add migrations that create your tables with row level security enabled, or check RLS in the Supabase dashboard."
16048
+ };
15922
16049
  }
16050
+ return { kind: "nothing-to-do" };
16051
+ }
16052
+ const tables = [...new Set(missing.map((finding) => finding.table))];
16053
+ const evidence = missing.map((finding) => ({
16054
+ file: finding.file,
16055
+ table: finding.table,
16056
+ turnedOff: finding.message.includes(RLS_TURNED_OFF_PHRASE)
16057
+ }));
16058
+ const target = rlsMigrationTarget(cwd, evidence, now);
16059
+ if (!target.ok) return { kind: "refused", reason: target.reason };
16060
+ return {
16061
+ kind: "migration",
16062
+ file: target.file,
16063
+ sql: [RLS_MIGRATION_NOTE, "", ...rlsEnableStatements(tables), ""].join("\n"),
16064
+ tables
15923
16065
  };
15924
16066
  }
16067
+ var RLS_MIGRATION_NOTE;
15925
16068
  var init_supabaseRls2 = __esm({
15926
16069
  "src/heal/recipes/supabaseRls.ts"() {
15927
16070
  "use strict";
16071
+ init_audit();
16072
+ init_rlsMigrationTarget();
16073
+ RLS_MIGRATION_NOTE = [
16074
+ "-- Added by VibeRaven (heal rls_disabled). Enables row level security on the public tables that the migrations in",
16075
+ "-- this repo create without it. It adds no policies: until you add them, the anon and authenticated roles get no",
16076
+ "-- rows from these tables through the Data API, and only the service role can read or write them. Add the policies",
16077
+ "-- the app needs, then apply this migration the way this project applies migrations."
16078
+ ].join("\n");
15928
16079
  }
15929
16080
  });
15930
16081
 
@@ -16106,7 +16257,7 @@ export const config = {
16106
16257
  // src/heal/recipes/eslintRestrictedImports.ts
16107
16258
  function detectEslintConfigFile(cwd) {
16108
16259
  for (const candidate of ESLINT_CONFIG_CANDIDATES) {
16109
- if ((0, import_node_fs9.existsSync)((0, import_node_path22.join)(cwd, candidate))) {
16260
+ if ((0, import_node_fs10.existsSync)((0, import_node_path24.join)(cwd, candidate))) {
16110
16261
  return candidate;
16111
16262
  }
16112
16263
  }
@@ -16261,12 +16412,12 @@ function applyEslintRestrictedImportsRecipe(source, configFile) {
16261
16412
  }
16262
16413
  return applyLegacyModuleRecipe(source);
16263
16414
  }
16264
- var import_node_fs9, import_node_path22, VIBERAVEN_ESLINT_MARKER, RESTRICTED_IMPORTS_MESSAGE, RESTRICTED_PATHS, RESTRICTED_PATTERNS, ESLINT_CONFIG_CANDIDATES;
16415
+ var import_node_fs10, import_node_path24, VIBERAVEN_ESLINT_MARKER, RESTRICTED_IMPORTS_MESSAGE, RESTRICTED_PATHS, RESTRICTED_PATTERNS, ESLINT_CONFIG_CANDIDATES;
16265
16416
  var init_eslintRestrictedImports = __esm({
16266
16417
  "src/heal/recipes/eslintRestrictedImports.ts"() {
16267
16418
  "use strict";
16268
- import_node_fs9 = require("node:fs");
16269
- import_node_path22 = require("node:path");
16419
+ import_node_fs10 = require("node:fs");
16420
+ import_node_path24 = require("node:path");
16270
16421
  init_commands();
16271
16422
  VIBERAVEN_ESLINT_MARKER = "VibeRaven heal: eslint_restricted_imports";
16272
16423
  RESTRICTED_IMPORTS_MESSAGE = `Restricted import. Run ${PUBLIC_AGENT_MODE_COMMAND} before substituting packages.`;
@@ -16309,7 +16460,7 @@ function defaultTargetFile(gapId) {
16309
16460
  missing_loading_state: "app/loading.tsx",
16310
16461
  missing_404_page: "app/not-found.tsx",
16311
16462
  rls_disabled: "",
16312
- // no file — provider-action
16463
+ // a new migration, named by planRlsMigration
16313
16464
  missing_csp_header: "next.config.js",
16314
16465
  missing_rate_limit: "middleware.ts",
16315
16466
  eslint_restricted_imports: ""
@@ -16317,14 +16468,14 @@ function defaultTargetFile(gapId) {
16317
16468
  return map[gapId];
16318
16469
  }
16319
16470
  async function readSourceOrEmpty(absolutePath) {
16320
- if (!(0, import_node_fs10.existsSync)(absolutePath)) return "";
16321
- return (0, import_promises16.readFile)(absolutePath, "utf8");
16471
+ if (!(0, import_node_fs11.existsSync)(absolutePath)) return "";
16472
+ return (0, import_promises17.readFile)(absolutePath, "utf8");
16322
16473
  }
16323
16474
  async function detectUpstash(cwd) {
16324
16475
  try {
16325
- const pkgPath = (0, import_node_path23.join)(cwd, "package.json");
16326
- if (!(0, import_node_fs10.existsSync)(pkgPath)) return false;
16327
- const raw = await (0, import_promises16.readFile)(pkgPath, "utf8");
16476
+ const pkgPath = (0, import_node_path25.join)(cwd, "package.json");
16477
+ if (!(0, import_node_fs11.existsSync)(pkgPath)) return false;
16478
+ const raw = await (0, import_promises17.readFile)(pkgPath, "utf8");
16328
16479
  const pkg = JSON.parse(raw);
16329
16480
  const deps = {
16330
16481
  ...pkg["dependencies"] ?? {},
@@ -16339,7 +16490,7 @@ async function dispatchRecipeByGapId(gapId, cwd, explicitTarget) {
16339
16490
  const targetFile = explicitTarget ?? defaultTargetFile(gapId);
16340
16491
  if (targetFile === void 0) return null;
16341
16492
  if (gapId === "auth_secret_missing" || gapId === "node_env_not_set" || gapId === "database_url_missing") {
16342
- const absolutePath = (0, import_node_path23.join)(cwd, targetFile);
16493
+ const absolutePath = (0, import_node_path25.join)(cwd, targetFile);
16343
16494
  const source = await readSourceOrEmpty(absolutePath);
16344
16495
  let result;
16345
16496
  if (gapId === "auth_secret_missing") result = applyAuthSecretRecipe(source);
@@ -16353,46 +16504,45 @@ async function dispatchRecipeByGapId(gapId, cwd, explicitTarget) {
16353
16504
  };
16354
16505
  }
16355
16506
  if (gapId === "missing_error_boundary") {
16356
- const absolutePath = (0, import_node_path23.join)(cwd, "app/error.tsx");
16507
+ const absolutePath = (0, import_node_path25.join)(cwd, "app/error.tsx");
16357
16508
  const source = await readSourceOrEmpty(absolutePath);
16358
16509
  const result = applyErrorBoundaryRecipe(source);
16359
16510
  return { ...result, canAutoApply: true, recipeName: gapId };
16360
16511
  }
16361
16512
  if (gapId === "missing_health_route") {
16362
- const absolutePath = (0, import_node_path23.join)(cwd, "app/api/health/route.ts");
16513
+ const absolutePath = (0, import_node_path25.join)(cwd, "app/api/health/route.ts");
16363
16514
  const source = await readSourceOrEmpty(absolutePath);
16364
16515
  const result = applyHealthRouteRecipe(source);
16365
16516
  return { ...result, canAutoApply: true, recipeName: gapId };
16366
16517
  }
16367
16518
  if (gapId === "missing_loading_state") {
16368
- const absolutePath = (0, import_node_path23.join)(cwd, "app/loading.tsx");
16519
+ const absolutePath = (0, import_node_path25.join)(cwd, "app/loading.tsx");
16369
16520
  const source = await readSourceOrEmpty(absolutePath);
16370
16521
  const result = applyLoadingStateRecipe(source);
16371
16522
  return { ...result, canAutoApply: true, recipeName: gapId };
16372
16523
  }
16373
16524
  if (gapId === "missing_404_page") {
16374
- const absolutePath = (0, import_node_path23.join)(cwd, "app/not-found.tsx");
16525
+ const absolutePath = (0, import_node_path25.join)(cwd, "app/not-found.tsx");
16375
16526
  const source = await readSourceOrEmpty(absolutePath);
16376
16527
  const result = applyNotFoundRecipe(source);
16377
16528
  return { ...result, canAutoApply: true, recipeName: gapId };
16378
16529
  }
16379
16530
  if (gapId === "rls_disabled") {
16380
- const result = applyRlsRecipe("");
16381
- return {
16382
- changed: false,
16383
- output: "",
16384
- targetFile: "",
16385
- canAutoApply: false,
16386
- reason: result.reason,
16387
- recipeName: gapId
16388
- };
16531
+ const plan = await planRlsMigration(cwd);
16532
+ if (plan.kind === "migration") {
16533
+ return { changed: true, output: plan.sql, targetFile: plan.file, canAutoApply: true, recipeName: gapId, warnings: [RLS_NO_POLICIES_WARNING] };
16534
+ }
16535
+ if (plan.kind === "nothing-to-do") {
16536
+ return { changed: false, output: "", targetFile: "", canAutoApply: true, recipeName: gapId };
16537
+ }
16538
+ return { changed: false, output: "", targetFile: "", canAutoApply: false, reason: plan.reason, recipeName: gapId };
16389
16539
  }
16390
16540
  if (gapId === "missing_csp_header") {
16391
16541
  let configFile = "next.config.js";
16392
- let absolutePath = (0, import_node_path23.join)(cwd, configFile);
16393
- if (!(0, import_node_fs10.existsSync)(absolutePath)) {
16542
+ let absolutePath = (0, import_node_path25.join)(cwd, configFile);
16543
+ if (!(0, import_node_fs11.existsSync)(absolutePath)) {
16394
16544
  configFile = "next.config.mjs";
16395
- absolutePath = (0, import_node_path23.join)(cwd, configFile);
16545
+ absolutePath = (0, import_node_path25.join)(cwd, configFile);
16396
16546
  }
16397
16547
  const source = await readSourceOrEmpty(absolutePath);
16398
16548
  const result = applyCspHeaderRecipe(source);
@@ -16404,7 +16554,7 @@ async function dispatchRecipeByGapId(gapId, cwd, explicitTarget) {
16404
16554
  }
16405
16555
  if (gapId === "missing_rate_limit") {
16406
16556
  const hasUpstash = await detectUpstash(cwd);
16407
- const middlewarePath = (0, import_node_path23.join)(cwd, "middleware.ts");
16557
+ const middlewarePath = (0, import_node_path25.join)(cwd, "middleware.ts");
16408
16558
  const source = await readSourceOrEmpty(middlewarePath);
16409
16559
  const result = applyRateLimitRecipe(source, hasUpstash);
16410
16560
  return {
@@ -16426,7 +16576,7 @@ async function dispatchRecipeByGapId(gapId, cwd, explicitTarget) {
16426
16576
  recipeName: gapId
16427
16577
  };
16428
16578
  }
16429
- const absolutePath = (0, import_node_path23.join)(cwd, configFile);
16579
+ const absolutePath = (0, import_node_path25.join)(cwd, configFile);
16430
16580
  const source = await readSourceOrEmpty(absolutePath);
16431
16581
  const result = applyEslintRestrictedImportsRecipe(source, configFile);
16432
16582
  return {
@@ -16440,13 +16590,13 @@ async function dispatchRecipeByGapId(gapId, cwd, explicitTarget) {
16440
16590
  }
16441
16591
  return null;
16442
16592
  }
16443
- var import_node_fs10, import_promises16, import_node_path23;
16593
+ var import_node_fs11, import_promises17, import_node_path25;
16444
16594
  var init_recipes = __esm({
16445
16595
  "src/heal/recipes/index.ts"() {
16446
16596
  "use strict";
16447
- import_node_fs10 = require("node:fs");
16448
- import_promises16 = require("node:fs/promises");
16449
- import_node_path23 = require("node:path");
16597
+ import_node_fs11 = require("node:fs");
16598
+ import_promises17 = require("node:fs/promises");
16599
+ import_node_path25 = require("node:path");
16450
16600
  init_envAuthSecret();
16451
16601
  init_envNodeEnv();
16452
16602
  init_envDatabaseUrl();
@@ -16455,6 +16605,7 @@ var init_recipes = __esm({
16455
16605
  init_nextjsLoadingState();
16456
16606
  init_nextjsNotFound();
16457
16607
  init_supabaseRls2();
16608
+ init_rlsMigrationTarget();
16458
16609
  init_nextjsCspHeader();
16459
16610
  init_nextjsRateLimit();
16460
16611
  init_eslintRestrictedImports();
@@ -16462,11 +16613,12 @@ var init_recipes = __esm({
16462
16613
  });
16463
16614
 
16464
16615
  // src/heal/apply.ts
16465
- function healId() {
16466
- return `heal_${(/* @__PURE__ */ new Date()).toISOString().replace(/\D/g, "").slice(0, 14)}`;
16616
+ function rollbackInstructions(id, file, existedBefore) {
16617
+ if (!existedBefore) return `Delete ${file}: it did not exist before this heal.`;
16618
+ return `Copy .viberaven/heal/${id}/before/target.txt back over ${file}, or apply the reverse of .viberaven/heal/${id}/patch.diff.`;
16467
16619
  }
16468
16620
  async function applyHeal(options) {
16469
- const id = healId();
16621
+ let id = newHealId();
16470
16622
  if (!options.yes) {
16471
16623
  return {
16472
16624
  $schema: "https://viberaven.dev/schemas/heal-result.schema.json",
@@ -16521,14 +16673,17 @@ async function applyHeal(options) {
16521
16673
  rollback: { available: false, instructions: "Recipe matched but no change was needed (already applied or file already exists)." }
16522
16674
  };
16523
16675
  }
16524
- const absoluteTarget = (0, import_node_path24.join)(options.cwd, dispatched.targetFile);
16676
+ const absoluteTarget = (0, import_node_path26.join)(options.cwd, dispatched.targetFile);
16525
16677
  assertSafeHealTarget(options.cwd, dispatched.targetFile);
16526
- await (0, import_promises17.mkdir)((0, import_node_path24.dirname)(absoluteTarget), { recursive: true });
16527
- const healDir2 = (0, import_node_path24.join)(options.cwd, ".viberaven", "heal", id);
16528
- await (0, import_promises17.mkdir)((0, import_node_path24.join)(healDir2, "before"), { recursive: true });
16529
- const beforeContent = (0, import_node_fs11.existsSync)(absoluteTarget) ? await (0, import_promises17.readFile)(absoluteTarget, "utf8") : "";
16530
- await (0, import_promises17.writeFile)((0, import_node_path24.join)(healDir2, "before", "target.txt"), beforeContent, "utf8");
16531
- await (0, import_promises17.writeFile)(absoluteTarget, dispatched.output, "utf8");
16678
+ await (0, import_promises18.mkdir)((0, import_node_path26.dirname)(absoluteTarget), { recursive: true });
16679
+ const created2 = await createHealDir(options.cwd);
16680
+ id = created2.id;
16681
+ const healDir2 = created2.dir;
16682
+ await (0, import_promises18.mkdir)((0, import_node_path26.join)(healDir2, "before"));
16683
+ const existedBefore = (0, import_node_fs12.existsSync)(absoluteTarget);
16684
+ const beforeContent = existedBefore ? await (0, import_promises18.readFile)(absoluteTarget, "utf8") : "";
16685
+ await (0, import_promises18.writeFile)((0, import_node_path26.join)(healDir2, "before", "target.txt"), beforeContent, "utf8");
16686
+ await (0, import_promises18.writeFile)(absoluteTarget, dispatched.output, "utf8");
16532
16687
  const patch2 = [
16533
16688
  `--- ${dispatched.targetFile}`,
16534
16689
  `+++ ${dispatched.targetFile}`,
@@ -16538,7 +16693,7 @@ async function applyHeal(options) {
16538
16693
  dispatched.output,
16539
16694
  ""
16540
16695
  ].join("\n");
16541
- await (0, import_promises17.writeFile)((0, import_node_path24.join)(healDir2, "patch.diff"), patch2, "utf8");
16696
+ await (0, import_promises18.writeFile)((0, import_node_path26.join)(healDir2, "patch.diff"), patch2, "utf8");
16542
16697
  const result2 = {
16543
16698
  $schema: "https://viberaven.dev/schemas/heal-result.schema.json",
16544
16699
  schemaVersion: "v1",
@@ -16548,18 +16703,19 @@ async function applyHeal(options) {
16548
16703
  status: "applied_verify_not_run",
16549
16704
  gapId: options.gapId,
16550
16705
  recipe: dispatched.recipeName,
16706
+ ...dispatched.warnings?.length ? { warnings: dispatched.warnings } : {},
16551
16707
  target: dispatched.targetFile,
16552
- changedFiles: [(0, import_node_path24.relative)(options.cwd, absoluteTarget).replace(/\\/g, "/")],
16708
+ changedFiles: [(0, import_node_path26.relative)(options.cwd, absoluteTarget).replace(/\\/g, "/")],
16553
16709
  artifacts: {
16554
16710
  patch: `.viberaven/heal/${id}/patch.diff`,
16555
16711
  result: `.viberaven/heal/${id}/result.json`
16556
16712
  },
16557
16713
  rollback: {
16558
16714
  available: true,
16559
- instructions: "Restore .viberaven/heal/<healId>/before/target.txt to the target file or apply the reverse patch."
16715
+ instructions: rollbackInstructions(id, (0, import_node_path26.relative)(options.cwd, absoluteTarget).replace(/\\/g, "/"), existedBefore)
16560
16716
  }
16561
16717
  };
16562
- await (0, import_promises17.writeFile)((0, import_node_path24.join)(healDir2, "result.json"), `${JSON.stringify(result2, null, 2)}
16718
+ await (0, import_promises18.writeFile)((0, import_node_path26.join)(healDir2, "result.json"), `${JSON.stringify(result2, null, 2)}
16563
16719
  `, "utf8");
16564
16720
  return result2;
16565
16721
  }
@@ -16579,7 +16735,7 @@ async function applyHeal(options) {
16579
16735
  };
16580
16736
  }
16581
16737
  const absolute = assertSafeHealTarget(options.cwd, options.target);
16582
- const before = await (0, import_promises17.readFile)(absolute, "utf8");
16738
+ const before = await (0, import_promises18.readFile)(absolute, "utf8");
16583
16739
  const recipe = applyEmptyCatchRecipe(before);
16584
16740
  if (!recipe.changed) {
16585
16741
  return {
@@ -16596,10 +16752,12 @@ async function applyHeal(options) {
16596
16752
  rollback: { available: false, instructions: "No supported heal recipe matched this file." }
16597
16753
  };
16598
16754
  }
16599
- const healDir = (0, import_node_path24.join)(options.cwd, ".viberaven", "heal", id);
16600
- await (0, import_promises17.mkdir)((0, import_node_path24.join)(healDir, "before"), { recursive: true });
16601
- await (0, import_promises17.writeFile)((0, import_node_path24.join)(healDir, "before", "target.txt"), before, "utf8");
16602
- await (0, import_promises17.writeFile)(absolute, recipe.output, "utf8");
16755
+ const created = await createHealDir(options.cwd);
16756
+ id = created.id;
16757
+ const healDir = created.dir;
16758
+ await (0, import_promises18.mkdir)((0, import_node_path26.join)(healDir, "before"));
16759
+ await (0, import_promises18.writeFile)((0, import_node_path26.join)(healDir, "before", "target.txt"), before, "utf8");
16760
+ await (0, import_promises18.writeFile)(absolute, recipe.output, "utf8");
16603
16761
  const patch = [
16604
16762
  `--- ${options.target}`,
16605
16763
  `+++ ${options.target}`,
@@ -16609,7 +16767,7 @@ async function applyHeal(options) {
16609
16767
  recipe.output,
16610
16768
  ""
16611
16769
  ].join("\n");
16612
- await (0, import_promises17.writeFile)((0, import_node_path24.join)(healDir, "patch.diff"), patch, "utf8");
16770
+ await (0, import_promises18.writeFile)((0, import_node_path26.join)(healDir, "patch.diff"), patch, "utf8");
16613
16771
  const result = {
16614
16772
  $schema: "https://viberaven.dev/schemas/heal-result.schema.json",
16615
16773
  schemaVersion: "v1",
@@ -16620,42 +16778,65 @@ async function applyHeal(options) {
16620
16778
  gapId: options.gapId,
16621
16779
  recipe: "empty-catch-safe-response",
16622
16780
  target: options.target,
16623
- changedFiles: [(0, import_node_path24.relative)(options.cwd, absolute).replace(/\\/g, "/")],
16781
+ changedFiles: [(0, import_node_path26.relative)(options.cwd, absolute).replace(/\\/g, "/")],
16624
16782
  artifacts: {
16625
16783
  patch: `.viberaven/heal/${id}/patch.diff`,
16626
16784
  result: `.viberaven/heal/${id}/result.json`
16627
16785
  },
16628
16786
  rollback: {
16629
16787
  available: true,
16630
- instructions: "Restore .viberaven/heal/<healId>/before/target.txt to the target file or apply the reverse patch."
16788
+ // The empty-catch recipe only edits a file that already exists.
16789
+ instructions: rollbackInstructions(id, (0, import_node_path26.relative)(options.cwd, absolute).replace(/\\/g, "/"), true)
16631
16790
  }
16632
16791
  };
16633
- await (0, import_promises17.writeFile)((0, import_node_path24.join)(healDir, "result.json"), `${JSON.stringify(result, null, 2)}
16792
+ await (0, import_promises18.writeFile)((0, import_node_path26.join)(healDir, "result.json"), `${JSON.stringify(result, null, 2)}
16634
16793
  `, "utf8");
16635
16794
  return result;
16636
16795
  }
16637
- var import_promises17, import_node_fs11, import_node_path24;
16796
+ var import_promises18, import_node_fs12, import_node_path26;
16638
16797
  var init_apply = __esm({
16639
16798
  "src/heal/apply.ts"() {
16640
16799
  "use strict";
16641
- import_promises17 = require("node:fs/promises");
16642
- import_node_fs11 = require("node:fs");
16643
- import_node_path24 = require("node:path");
16800
+ import_promises18 = require("node:fs/promises");
16801
+ import_node_fs12 = require("node:fs");
16802
+ import_node_path26 = require("node:path");
16644
16803
  init_pathSafety();
16645
16804
  init_emptyCatch();
16805
+ init_healId();
16646
16806
  init_recipes();
16647
16807
  }
16648
16808
  });
16649
16809
 
16650
16810
  // src/heal/plan.ts
16651
- function healId2() {
16652
- return `heal_${(/* @__PURE__ */ new Date()).toISOString().replace(/\D/g, "").slice(0, 14)}`;
16653
- }
16654
16811
  async function writeHealPlan(options) {
16655
- const dir = (0, import_node_path25.join)(options.cwd, ".viberaven");
16656
- await (0, import_promises18.mkdir)(dir, { recursive: true });
16657
- const id = healId2();
16812
+ const dir = (0, import_node_path27.join)(options.cwd, ".viberaven");
16813
+ await (0, import_promises19.mkdir)(dir, { recursive: true });
16814
+ const id = newHealId();
16658
16815
  const target = options.target ?? `gap:${options.gapId}`;
16816
+ const proposal = [];
16817
+ let proposedFile;
16818
+ if (!options.target && options.gapId === "rls_disabled") {
16819
+ const rls = await planRlsMigration(options.cwd);
16820
+ if (rls.kind === "migration") {
16821
+ proposedFile = rls.file;
16822
+ proposal.push(
16823
+ "## Proposed change",
16824
+ "",
16825
+ `New migration: \`${rls.file}\``,
16826
+ "",
16827
+ "```sql",
16828
+ rls.sql.trimEnd(),
16829
+ "```",
16830
+ "",
16831
+ "It adds no policies. Apply it with `npx -y viberaven fix --gap rls_disabled`, add the policies the app needs, then apply the migration to the database the way this project applies migrations.",
16832
+ ""
16833
+ );
16834
+ } else if (rls.kind === "refused") {
16835
+ proposal.push("## No automatic recipe", "", rls.reason, "");
16836
+ } else {
16837
+ proposal.push("## Nothing to change", "", "Every public table the migrations create already has row level security.", "");
16838
+ }
16839
+ }
16659
16840
  const markdown = [
16660
16841
  "# VibeRaven Heal Plan",
16661
16842
  "",
@@ -16664,6 +16845,7 @@ async function writeHealPlan(options) {
16664
16845
  "",
16665
16846
  "This plan is non-destructive. It does not edit source files.",
16666
16847
  "",
16848
+ ...proposal,
16667
16849
  `Verify after manual fix: \`${PUBLIC_VERIFY_COMMAND}\``,
16668
16850
  ""
16669
16851
  ].join("\n");
@@ -16675,34 +16857,33 @@ async function writeHealPlan(options) {
16675
16857
  mode: "plan",
16676
16858
  status: "planned",
16677
16859
  gapId: options.gapId,
16678
- target: options.target,
16860
+ target: options.target ?? proposedFile,
16679
16861
  changedFiles: [],
16680
16862
  artifacts: { plan: ".viberaven/heal-plan.md" },
16681
16863
  rollback: { available: false, instructions: "No source files were changed." }
16682
16864
  };
16683
- await (0, import_promises18.writeFile)((0, import_node_path25.join)(dir, "heal-plan.md"), markdown, "utf8");
16684
- await (0, import_promises18.writeFile)((0, import_node_path25.join)(dir, "heal-plan.json"), `${JSON.stringify(result, null, 2)}
16865
+ await (0, import_promises19.writeFile)((0, import_node_path27.join)(dir, "heal-plan.md"), markdown, "utf8");
16866
+ await (0, import_promises19.writeFile)((0, import_node_path27.join)(dir, "heal-plan.json"), `${JSON.stringify(result, null, 2)}
16685
16867
  `, "utf8");
16686
16868
  return result;
16687
16869
  }
16688
- var import_promises18, import_node_path25;
16870
+ var import_promises19, import_node_path27;
16689
16871
  var init_plan = __esm({
16690
16872
  "src/heal/plan.ts"() {
16691
16873
  "use strict";
16692
- import_promises18 = require("node:fs/promises");
16693
- import_node_path25 = require("node:path");
16874
+ import_promises19 = require("node:fs/promises");
16875
+ init_healId();
16876
+ import_node_path27 = require("node:path");
16694
16877
  init_commands();
16878
+ init_supabaseRls2();
16695
16879
  }
16696
16880
  });
16697
16881
 
16698
16882
  // src/heal/prompt.ts
16699
- function healId3() {
16700
- return `heal_${(/* @__PURE__ */ new Date()).toISOString().replace(/\D/g, "").slice(0, 14)}`;
16701
- }
16702
16883
  async function writeHealPrompt(options) {
16703
- const dir = (0, import_node_path26.join)(options.cwd, ".viberaven");
16704
- await (0, import_promises19.mkdir)(dir, { recursive: true });
16705
- const id = healId3();
16884
+ const dir = (0, import_node_path28.join)(options.cwd, ".viberaven");
16885
+ await (0, import_promises20.mkdir)(dir, { recursive: true });
16886
+ const id = newHealId();
16706
16887
  const target = options.target ?? `gap:${options.gapId}`;
16707
16888
  const prompt = [
16708
16889
  "# VibeRaven Heal Prompt",
@@ -16728,15 +16909,16 @@ async function writeHealPrompt(options) {
16728
16909
  artifacts: { prompt: ".viberaven/heal-prompt.md" },
16729
16910
  rollback: { available: false, instructions: "No source files were changed." }
16730
16911
  };
16731
- await (0, import_promises19.writeFile)((0, import_node_path26.join)(dir, "heal-prompt.md"), prompt, "utf8");
16912
+ await (0, import_promises20.writeFile)((0, import_node_path28.join)(dir, "heal-prompt.md"), prompt, "utf8");
16732
16913
  return result;
16733
16914
  }
16734
- var import_promises19, import_node_path26;
16915
+ var import_promises20, import_node_path28;
16735
16916
  var init_prompt = __esm({
16736
16917
  "src/heal/prompt.ts"() {
16737
16918
  "use strict";
16738
- import_promises19 = require("node:fs/promises");
16739
- import_node_path26 = require("node:path");
16919
+ import_promises20 = require("node:fs/promises");
16920
+ init_healId();
16921
+ import_node_path28 = require("node:path");
16740
16922
  init_commands();
16741
16923
  }
16742
16924
  });
@@ -16747,7 +16929,7 @@ function loopStatePath(workspaceRoot) {
16747
16929
  }
16748
16930
  async function loadLoopState(workspaceRoot) {
16749
16931
  try {
16750
- const raw = await (0, import_promises20.readFile)(loopStatePath(workspaceRoot), "utf8");
16932
+ const raw = await (0, import_promises21.readFile)(loopStatePath(workspaceRoot), "utf8");
16751
16933
  const parsed = JSON.parse(raw);
16752
16934
  if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed) && typeof parsed.batchApplied === "number" && typeof parsed.lastGapCount === "number" && typeof parsed.stalledScans === "number") {
16753
16935
  const p2 = parsed;
@@ -16767,8 +16949,8 @@ async function loadLoopState(workspaceRoot) {
16767
16949
  async function saveLoopState(workspaceRoot, state) {
16768
16950
  try {
16769
16951
  const dir = (0, import_path.join)(workspaceRoot, ".viberaven");
16770
- await (0, import_promises20.mkdir)(dir, { recursive: true });
16771
- await (0, import_promises20.writeFile)(loopStatePath(workspaceRoot), JSON.stringify(state, null, 2) + "\n", "utf8");
16952
+ await (0, import_promises21.mkdir)(dir, { recursive: true });
16953
+ await (0, import_promises21.writeFile)(loopStatePath(workspaceRoot), JSON.stringify(state, null, 2) + "\n", "utf8");
16772
16954
  } catch (err) {
16773
16955
  console.warn("[VibeRaven] Could not save loop-state.json:", err instanceof Error ? err.message : String(err));
16774
16956
  }
@@ -16794,11 +16976,11 @@ function resetBatch(state, newGapCount) {
16794
16976
  appliedGapIdsSinceScan: []
16795
16977
  };
16796
16978
  }
16797
- var import_promises20, import_path, DEFAULT_LOOP_STATE;
16979
+ var import_promises21, import_path, DEFAULT_LOOP_STATE;
16798
16980
  var init_loopState = __esm({
16799
16981
  "src/loopState.ts"() {
16800
16982
  "use strict";
16801
- import_promises20 = require("fs/promises");
16983
+ import_promises21 = require("fs/promises");
16802
16984
  import_path = require("path");
16803
16985
  DEFAULT_LOOP_STATE = {
16804
16986
  batchApplied: 0,
@@ -16839,11 +17021,11 @@ var init_heal = __esm({
16839
17021
 
16840
17022
  // src/stackRecommend.ts
16841
17023
  async function recommendStack(cwd = process.cwd()) {
16842
- const pkgPath = (0, import_node_path27.join)(cwd, "package.json");
16843
- if (!(0, import_node_fs12.existsSync)(pkgPath)) {
17024
+ const pkgPath = (0, import_node_path29.join)(cwd, "package.json");
17025
+ if (!(0, import_node_fs13.existsSync)(pkgPath)) {
16844
17026
  return DEFAULT_STACK;
16845
17027
  }
16846
- const pkg = JSON.parse(await (0, import_promises21.readFile)(pkgPath, "utf-8"));
17028
+ const pkg = JSON.parse(await (0, import_promises22.readFile)(pkgPath, "utf-8"));
16847
17029
  const deps = { ...pkg.dependencies, ...pkg.devDependencies };
16848
17030
  const names = Object.keys(deps).join(" ").toLowerCase();
16849
17031
  const rec = {
@@ -16871,13 +17053,13 @@ async function recommendStack(cwd = process.cwd()) {
16871
17053
  }
16872
17054
  return rec;
16873
17055
  }
16874
- var import_promises21, import_node_fs12, import_node_path27, DEFAULT_STACK;
17056
+ var import_promises22, import_node_fs13, import_node_path29, DEFAULT_STACK;
16875
17057
  var init_stackRecommend = __esm({
16876
17058
  "src/stackRecommend.ts"() {
16877
17059
  "use strict";
16878
- import_promises21 = require("node:fs/promises");
16879
- import_node_fs12 = require("node:fs");
16880
- import_node_path27 = require("node:path");
17060
+ import_promises22 = require("node:fs/promises");
17061
+ import_node_fs13 = require("node:fs");
17062
+ import_node_path29 = require("node:path");
16881
17063
  DEFAULT_STACK = {
16882
17064
  frontend: "react",
16883
17065
  ui: "tailwind + shadcn/ui",
@@ -16919,10 +17101,14 @@ var init_runInit = __esm({
16919
17101
  });
16920
17102
 
16921
17103
  // src/commands/doctorAgents.ts
17104
+ function managedBlocks(content) {
17105
+ const blocks = content.match(/<!-- (?:VIBERAVEN:START|viberaven:agent-rules:start) -->[\s\S]*?<!-- (?:VIBERAVEN:END|viberaven:agent-rules:end) -->/g);
17106
+ return (blocks ?? []).join("\n");
17107
+ }
16922
17108
  async function checkAgentInjection(cwd) {
16923
17109
  const checks = [];
16924
17110
  for (const item4 of REQUIRED_EXISTENCE_CHECKS) {
16925
- const exists = await fileExists2((0, import_node_path28.join)(cwd, item4.file));
17111
+ const exists = await fileExists2((0, import_node_path30.join)(cwd, item4.file));
16926
17112
  checks.push({
16927
17113
  id: item4.id,
16928
17114
  status: exists ? "pass" : "fail",
@@ -16930,17 +17116,17 @@ async function checkAgentInjection(cwd) {
16930
17116
  });
16931
17117
  }
16932
17118
  for (const item4 of OPTIONAL_CURSOR_PACK_CHECKS) {
16933
- const exists = await fileExists2((0, import_node_path28.join)(cwd, item4.file));
17119
+ const exists = await fileExists2((0, import_node_path30.join)(cwd, item4.file));
16934
17120
  checks.push({
16935
17121
  id: item4.id,
16936
17122
  status: exists ? "pass" : "fail",
16937
17123
  message: exists ? `${item4.file} exists` : `Missing ${item4.file} \u2014 run npx -y viberaven init --agents all`
16938
17124
  });
16939
17125
  }
16940
- const legacyCursorPath = (0, import_node_path28.join)(cwd, ".cursor/rules/viberaven.mdc");
17126
+ const legacyCursorPath = (0, import_node_path30.join)(cwd, ".cursor/rules/viberaven.mdc");
16941
17127
  if (await fileExists2(legacyCursorPath)) {
16942
- const legacyContent = await (0, import_promises22.readFile)(legacyCursorPath, "utf-8");
16943
- const hasCoreSplit = await fileExists2((0, import_node_path28.join)(cwd, ".cursor/rules/viberaven-core.mdc"));
17128
+ const legacyContent = await (0, import_promises23.readFile)(legacyCursorPath, "utf-8");
17129
+ const hasCoreSplit = await fileExists2((0, import_node_path30.join)(cwd, ".cursor/rules/viberaven-core.mdc"));
16944
17130
  if (!hasCoreSplit || legacyContent.includes("alwaysApply: true")) {
16945
17131
  checks.push({
16946
17132
  id: "cursor-legacy-mdc",
@@ -16951,7 +17137,7 @@ async function checkAgentInjection(cwd) {
16951
17137
  }
16952
17138
  for (const target of CORE_AGENT_INJECTION_TARGETS) {
16953
17139
  const file = target === "cursor" ? ".cursor/rules/viberaven-core.mdc" : AGENT_RULE_TARGETS[target].file;
16954
- const path = (0, import_node_path28.join)(cwd, file);
17140
+ const path = (0, import_node_path30.join)(cwd, file);
16955
17141
  const exists = await fileExists2(path);
16956
17142
  if (!exists) {
16957
17143
  checks.push({
@@ -16961,7 +17147,7 @@ async function checkAgentInjection(cwd) {
16961
17147
  });
16962
17148
  continue;
16963
17149
  }
16964
- const content = await (0, import_promises22.readFile)(path, "utf-8");
17150
+ const content = await (0, import_promises23.readFile)(path, "utf-8");
16965
17151
  const hasCommand = content.includes(PUBLIC_CHECK_COMMAND);
16966
17152
  checks.push({
16967
17153
  id: `canonical-${target}`,
@@ -16977,6 +17163,15 @@ async function checkAgentInjection(cwd) {
16977
17163
  });
16978
17164
  }
16979
17165
  }
17166
+ const managed = target === "cursor" ? content : managedBlocks(content);
17167
+ const gatePhrase = GATE_RULE_PHRASES_1_5_2.find((phrase) => managed.includes(phrase));
17168
+ if (gatePhrase) {
17169
+ checks.push({
17170
+ id: `stale-gate-rules-${target}`,
17171
+ status: "fail",
17172
+ message: `${file} still has the 1.5.2 gate rules ("${gatePhrase} ..."). Run npx -y viberaven init --agents all to replace them with the current advice block.`
17173
+ });
17174
+ }
16980
17175
  }
16981
17176
  return {
16982
17177
  ok: checks.every((check) => check.status === "pass"),
@@ -16995,18 +17190,18 @@ function formatDoctorAgentsReport(report) {
16995
17190
  }
16996
17191
  async function fileExists2(path) {
16997
17192
  try {
16998
- await (0, import_promises22.access)(path);
17193
+ await (0, import_promises23.access)(path);
16999
17194
  return true;
17000
17195
  } catch {
17001
17196
  return false;
17002
17197
  }
17003
17198
  }
17004
- var import_promises22, import_node_path28, REQUIRED_EXISTENCE_CHECKS, OPTIONAL_CURSOR_PACK_CHECKS, STALE_PATTERNS;
17199
+ var import_promises23, import_node_path30, REQUIRED_EXISTENCE_CHECKS, OPTIONAL_CURSOR_PACK_CHECKS, STALE_PATTERNS, GATE_RULE_PHRASES_1_5_2;
17005
17200
  var init_doctorAgents = __esm({
17006
17201
  "src/commands/doctorAgents.ts"() {
17007
17202
  "use strict";
17008
- import_promises22 = require("node:fs/promises");
17009
- import_node_path28 = require("node:path");
17203
+ import_promises23 = require("node:fs/promises");
17204
+ import_node_path30 = require("node:path");
17010
17205
  init_commands();
17011
17206
  init_agentTargets();
17012
17207
  REQUIRED_EXISTENCE_CHECKS = [
@@ -17025,6 +17220,12 @@ var init_doctorAgents = __esm({
17025
17220
  { id: "stale-viberaven-station", pattern: "viberaven-station" },
17026
17221
  { id: "stale-viberice", pattern: "viberice" }
17027
17222
  ];
17223
+ GATE_RULE_PHRASES_1_5_2 = [
17224
+ "The loop is not done until",
17225
+ "Gate is not clear until",
17226
+ "Do NOT rely on manual production checklists",
17227
+ "Use VibeRaven before launch, deployment, real users"
17228
+ ];
17028
17229
  }
17029
17230
  });
17030
17231
 
@@ -17271,7 +17472,7 @@ function buildContextCleanupPlan(input) {
17271
17472
  const items = [];
17272
17473
  const seen = /* @__PURE__ */ new Set();
17273
17474
  for (const file of input.files) {
17274
- const path = normalizePath5((0, import_node_path29.relative)(input.projectRoot, file.path) || file.path);
17475
+ const path = normalizePath5((0, import_node_path31.relative)(input.projectRoot, file.path) || file.path);
17275
17476
  const fullPath = normalizePath5(file.path);
17276
17477
  const category = categorizeCleanupFile(path, fullPath, file);
17277
17478
  if (!category || seen.has(path)) {
@@ -17297,10 +17498,10 @@ async function collectCleanupFiles(projectRoot) {
17297
17498
  return files;
17298
17499
  }
17299
17500
  async function writeCleanupPlan(projectRoot, plan) {
17300
- const outputDir = (0, import_node_path29.join)(projectRoot, ".viberaven");
17301
- const outputPath = (0, import_node_path29.join)(outputDir, "context-cleanup.md");
17302
- await (0, import_promises23.mkdir)(outputDir, { recursive: true });
17303
- await (0, import_promises23.writeFile)(outputPath, renderCleanupPlan(plan), "utf8");
17501
+ const outputDir = (0, import_node_path31.join)(projectRoot, ".viberaven");
17502
+ const outputPath = (0, import_node_path31.join)(outputDir, "context-cleanup.md");
17503
+ await (0, import_promises24.mkdir)(outputDir, { recursive: true });
17504
+ await (0, import_promises24.writeFile)(outputPath, renderCleanupPlan(plan), "utf8");
17304
17505
  return outputPath;
17305
17506
  }
17306
17507
  function categorizeCleanupFile(relativePath, fullPath, file) {
@@ -17348,7 +17549,7 @@ async function walkCleanupFiles(root, dir, result, depth) {
17348
17549
  }
17349
17550
  let entries;
17350
17551
  try {
17351
- entries = await (0, import_promises23.readdir)(dir, { withFileTypes: true });
17552
+ entries = await (0, import_promises24.readdir)(dir, { withFileTypes: true });
17352
17553
  } catch {
17353
17554
  return;
17354
17555
  }
@@ -17356,9 +17557,9 @@ async function walkCleanupFiles(root, dir, result, depth) {
17356
17557
  if (SKIP_DIRECTORIES.has(entry.name)) {
17357
17558
  continue;
17358
17559
  }
17359
- const absolute = (0, import_node_path29.join)(dir, entry.name);
17560
+ const absolute = (0, import_node_path31.join)(dir, entry.name);
17360
17561
  if (entry.isDirectory()) {
17361
- if (isCachePath(normalizePath5((0, import_node_path29.relative)(root, absolute)))) {
17562
+ if (isCachePath(normalizePath5((0, import_node_path31.relative)(root, absolute)))) {
17362
17563
  result.push({ path: absolute, sizeBytes: 0, kind: "directory" });
17363
17564
  continue;
17364
17565
  }
@@ -17369,7 +17570,7 @@ async function walkCleanupFiles(root, dir, result, depth) {
17369
17570
  continue;
17370
17571
  }
17371
17572
  try {
17372
- const fileStat = await (0, import_promises23.stat)(absolute);
17573
+ const fileStat = await (0, import_promises24.stat)(absolute);
17373
17574
  result.push({ path: absolute, sizeBytes: fileStat.size, kind: "file" });
17374
17575
  } catch {
17375
17576
  continue;
@@ -17379,12 +17580,12 @@ async function walkCleanupFiles(root, dir, result, depth) {
17379
17580
  function normalizePath5(path) {
17380
17581
  return path.replace(/\\/g, "/");
17381
17582
  }
17382
- var import_promises23, import_node_path29, MAX_WALK_DEPTH, LARGE_LOG_BYTES, SKIP_DIRECTORIES;
17583
+ var import_promises24, import_node_path31, MAX_WALK_DEPTH, LARGE_LOG_BYTES, SKIP_DIRECTORIES;
17383
17584
  var init_cleanPlan = __esm({
17384
17585
  "src/commands/cleanPlan.ts"() {
17385
17586
  "use strict";
17386
- import_promises23 = require("node:fs/promises");
17387
- import_node_path29 = require("node:path");
17587
+ import_promises24 = require("node:fs/promises");
17588
+ import_node_path31 = require("node:path");
17388
17589
  MAX_WALK_DEPTH = 5;
17389
17590
  LARGE_LOG_BYTES = 8192;
17390
17591
  SKIP_DIRECTORIES = /* @__PURE__ */ new Set([".git", "node_modules"]);
@@ -17432,6 +17633,10 @@ function renderTarget(target) {
17432
17633
  return ["Verify:", "```bash", redact(target.command), "```"];
17433
17634
  }
17434
17635
  function renderReadiness(action) {
17636
+ if (action.requiresUserAction && action.readiness?.[0]) {
17637
+ const rest = action.readiness.slice(1, 3).map(redact).filter(Boolean);
17638
+ return [redact(action.readiness[0]), rest.length ? `Ready: ${rest.join(", ")}` : void 0].filter(Boolean).join("\n");
17639
+ }
17435
17640
  const readiness = action.readiness?.slice(0, 2).map(redact).filter(Boolean);
17436
17641
  if (!readiness || readiness.length === 0) {
17437
17642
  return void 0;
@@ -17493,10 +17698,10 @@ var init_render = __esm({
17493
17698
 
17494
17699
  // src/commands/actions.ts
17495
17700
  async function runActionsCommand(input) {
17496
- const actionsPath = (0, import_node_path30.join)(getProjectArtifactsDir(input.cwd), "actions.json");
17701
+ const actionsPath = (0, import_node_path32.join)(getProjectArtifactsDir(input.cwd), "actions.json");
17497
17702
  let raw;
17498
17703
  try {
17499
- raw = await (0, import_promises24.readFile)(actionsPath, "utf8");
17704
+ raw = await (0, import_promises25.readFile)(actionsPath, "utf8");
17500
17705
  } catch {
17501
17706
  process.stderr.write("No VibeRaven actions found. Run `npx -y viberaven --agent-mode` first.\n");
17502
17707
  return 1;
@@ -17510,12 +17715,12 @@ async function runActionsCommand(input) {
17510
17715
  process.stdout.write(renderActionSurface(manifest));
17511
17716
  return 0;
17512
17717
  }
17513
- var import_promises24, import_node_path30;
17718
+ var import_promises25, import_node_path32;
17514
17719
  var init_actions = __esm({
17515
17720
  "src/commands/actions.ts"() {
17516
17721
  "use strict";
17517
- import_promises24 = require("node:fs/promises");
17518
- import_node_path30 = require("node:path");
17722
+ import_promises25 = require("node:fs/promises");
17723
+ import_node_path32 = require("node:path");
17519
17724
  init_render();
17520
17725
  init_config();
17521
17726
  }
@@ -17524,7 +17729,7 @@ var init_actions = __esm({
17524
17729
  // src/commands/verifyAction.ts
17525
17730
  async function currentActionExists(cwd, actionId) {
17526
17731
  try {
17527
- const raw = await (0, import_promises25.readFile)((0, import_node_path31.join)(getProjectArtifactsDir(cwd), "actions.json"), "utf8");
17732
+ const raw = await (0, import_promises26.readFile)((0, import_node_path33.join)(getProjectArtifactsDir(cwd), "actions.json"), "utf8");
17528
17733
  const manifest = JSON.parse(raw);
17529
17734
  return manifest.actions.some((action) => action.id === actionId);
17530
17735
  } catch {
@@ -17563,17 +17768,17 @@ async function runVerifyActionCommand(input) {
17563
17768
  }
17564
17769
  process.stdout.write(
17565
17770
  `Verifying ${entry.id} (${entry.title ?? "untitled action"}).
17566
- Run \`npx -y viberaven --verify\` to refresh the production gate for this action.
17771
+ This only looks the action up. Run \`npx -y viberaven --verify\` to scan the repo again and see whether the gap is gone.
17567
17772
  `
17568
17773
  );
17569
17774
  return 0;
17570
17775
  }
17571
- var import_promises25, import_node_path31;
17776
+ var import_promises26, import_node_path33;
17572
17777
  var init_verifyAction = __esm({
17573
17778
  "src/commands/verifyAction.ts"() {
17574
17779
  "use strict";
17575
- import_promises25 = require("node:fs/promises");
17576
- import_node_path31 = require("node:path");
17780
+ import_promises26 = require("node:fs/promises");
17781
+ import_node_path33 = require("node:path");
17577
17782
  init_registry2();
17578
17783
  init_config();
17579
17784
  }
@@ -17614,7 +17819,7 @@ function buildPreviewManifest() {
17614
17819
  ]
17615
17820
  }
17616
17821
  ],
17617
- verifyCommand: `${PUBLIC_COMMAND} verify --action VR-A1`,
17822
+ verifyCommand: `${PUBLIC_COMMAND} --verify --action VR-A1`,
17618
17823
  resumeInstruction: "Stripe webhook is configured. Continue VibeRaven from VR-A1."
17619
17824
  },
17620
17825
  {
@@ -17640,7 +17845,7 @@ function buildPreviewManifest() {
17640
17845
  value: 'alter table public.example enable row level security;\ncreate policy "Users manage own rows" on public.example for all using (auth.uid() = user_id);'
17641
17846
  }
17642
17847
  ],
17643
- verifyCommand: `${PUBLIC_COMMAND} verify --action VR-A2`,
17848
+ verifyCommand: `${PUBLIC_COMMAND} --verify --action VR-A2`,
17644
17849
  resumeInstruction: "Supabase RLS is applied. Continue VibeRaven from VR-A2."
17645
17850
  },
17646
17851
  {
@@ -17772,7 +17977,7 @@ var init_check = __esm({
17772
17977
 
17773
17978
  // src/rls/permissionsMatrix.ts
17774
17979
  function sha256(data) {
17775
- return (0, import_node_crypto.createHash)("sha256").update(data).digest("hex");
17980
+ return (0, import_node_crypto2.createHash)("sha256").update(data).digest("hex");
17776
17981
  }
17777
17982
  function parsePermissionsMatrix(text) {
17778
17983
  let raw;
@@ -17920,11 +18125,11 @@ function parseTable(key, value, problems) {
17920
18125
  const [schema, name] = parts.length === 2 ? parts : ["public", parts[0]];
17921
18126
  return { key, schema, name, ownerColumn, expect, serviceRoleExcluded, seed, ...updateColumn ? { updateColumn } : {} };
17922
18127
  }
17923
- var import_node_crypto, MATRIX_ACTORS, TESTED_ACTORS, MATRIX_ACTIONS, OPTIONAL_ACTIONS, ALL_ACTIONS, CELL_VALUES, DEFAULT_GRANT_MODES, DEFAULT_MATRIX_FILE, MatrixError, IDENT, TOP_KEYS, TABLE_KEYS, OWNER_KEYS, isObject2, show, quoteList;
18128
+ var import_node_crypto2, MATRIX_ACTORS, TESTED_ACTORS, MATRIX_ACTIONS, OPTIONAL_ACTIONS, ALL_ACTIONS, CELL_VALUES, DEFAULT_GRANT_MODES, DEFAULT_MATRIX_FILE, MatrixError, IDENT, TOP_KEYS, TABLE_KEYS, OWNER_KEYS, isObject2, show, quoteList;
17924
18129
  var init_permissionsMatrix = __esm({
17925
18130
  "src/rls/permissionsMatrix.ts"() {
17926
18131
  "use strict";
17927
- import_node_crypto = require("node:crypto");
18132
+ import_node_crypto2 = require("node:crypto");
17928
18133
  MATRIX_ACTORS = ["owner", "other_user", "anon", "service_role"];
17929
18134
  TESTED_ACTORS = ["owner", "other_user", "anon"];
17930
18135
  MATRIX_ACTIONS = ["select", "insert", "update", "delete"];
@@ -18201,8 +18406,8 @@ function isSqlError(error) {
18201
18406
  }
18202
18407
  function displayPath(projectDir, path) {
18203
18408
  for (const base of [projectDir, process.cwd()]) {
18204
- const rel = (0, import_node_path32.relative)(base, path);
18205
- if (rel && !rel.startsWith("..") && !(0, import_node_path32.isAbsolute)(rel)) return rel.split("\\").join("/");
18409
+ const rel = (0, import_node_path34.relative)(base, path);
18410
+ if (rel && !rel.startsWith("..") && !(0, import_node_path34.isAbsolute)(rel)) return rel.split("\\").join("/");
18206
18411
  }
18207
18412
  return path;
18208
18413
  }
@@ -18256,7 +18461,7 @@ function valueCandidates(column, label) {
18256
18461
  const u2 = label === "u";
18257
18462
  switch (column.typname) {
18258
18463
  case "uuid":
18259
- return [(0, import_node_crypto2.randomUUID)()];
18464
+ return [(0, import_node_crypto3.randomUUID)()];
18260
18465
  case "json":
18261
18466
  case "jsonb":
18262
18467
  return u2 ? ['{"vr":"u"}', "{}"] : ["{}"];
@@ -18806,26 +19011,26 @@ function verdictFor(summary) {
18806
19011
  }
18807
19012
  async function listMigrations(dir) {
18808
19013
  try {
18809
- const entries = await (0, import_promises26.readdir)(dir, { withFileTypes: true });
19014
+ const entries = await (0, import_promises27.readdir)(dir, { withFileTypes: true });
18810
19015
  return entries.filter((e2) => e2.isFile() && /\.sql$/i.test(e2.name) && !e2.name.startsWith(".")).map((e2) => e2.name).sort((a, b3) => a < b3 ? -1 : a > b3 ? 1 : 0);
18811
19016
  } catch {
18812
19017
  return [];
18813
19018
  }
18814
19019
  }
18815
19020
  async function runRlsTest(options) {
18816
- const projectDir = (0, import_node_path32.resolve)(options.projectDir);
18817
- const matrixPath = (0, import_node_path32.resolve)(projectDir, options.matrixPath ?? DEFAULT_MATRIX_FILE);
18818
- const migrationsDir = (0, import_node_path32.resolve)(projectDir, options.migrationsDir ?? (0, import_node_path32.join)("supabase", "migrations"));
19021
+ const projectDir = (0, import_node_path34.resolve)(options.projectDir);
19022
+ const matrixPath = (0, import_node_path34.resolve)(projectDir, options.matrixPath ?? DEFAULT_MATRIX_FILE);
19023
+ const migrationsDir = (0, import_node_path34.resolve)(projectDir, options.migrationsDir ?? (0, import_node_path34.join)("supabase", "migrations"));
18819
19024
  const timeouts = { ...DEFAULT_TIMEOUTS, ...options.timeouts };
18820
- if ((0, import_node_path32.basename)(matrixPath).toLowerCase().startsWith(".env")) {
18821
- throw new RlsInputError(`Refusing to read ${(0, import_node_path32.basename)(matrixPath)}: rls-test never reads .env files. Point --matrix at a viberaven.permissions.json.`);
19025
+ if ((0, import_node_path34.basename)(matrixPath).toLowerCase().startsWith(".env")) {
19026
+ throw new RlsInputError(`Refusing to read ${(0, import_node_path34.basename)(matrixPath)}: rls-test never reads .env files. Point --matrix at a viberaven.permissions.json.`);
18822
19027
  }
18823
19028
  const filesRead = [];
18824
19029
  let matrixText;
18825
19030
  try {
18826
- const info = await (0, import_promises26.stat)(matrixPath);
19031
+ const info = await (0, import_promises27.stat)(matrixPath);
18827
19032
  if (!info.isFile()) throw new Error("not a file");
18828
- matrixText = normalise(await (0, import_promises26.readFile)(matrixPath, "utf8"));
19033
+ matrixText = normalise(await (0, import_promises27.readFile)(matrixPath, "utf8"));
18829
19034
  } catch {
18830
19035
  throw new RlsInputError(
18831
19036
  `No permissions matrix at ${matrixPath}. Write one by hand (schema: schemas/permissions-matrix.schema.json in the @viberaven/cli package); rls-test never infers it from your policies.`
@@ -18836,8 +19041,8 @@ async function runRlsTest(options) {
18836
19041
  const names = await listMigrations(migrationsDir);
18837
19042
  const migrationFiles = [];
18838
19043
  for (const name of names) {
18839
- const sql = normalise(await (0, import_promises26.readFile)((0, import_node_path32.join)(migrationsDir, name), "utf8"));
18840
- filesRead.push(displayPath(projectDir, (0, import_node_path32.join)(migrationsDir, name)));
19044
+ const sql = normalise(await (0, import_promises27.readFile)((0, import_node_path34.join)(migrationsDir, name), "utf8"));
19045
+ filesRead.push(displayPath(projectDir, (0, import_node_path34.join)(migrationsDir, name)));
18841
19046
  migrationFiles.push({ name, sha256: sha256(sql), sql });
18842
19047
  }
18843
19048
  const migrationsHash = sha256(migrationFiles.map((f) => `${f.name}\0${f.sha256}`).join("\n"));
@@ -19037,15 +19242,15 @@ async function runTable(db, table, badRoles, roleChecks) {
19037
19242
  cells
19038
19243
  };
19039
19244
  }
19040
- var import_node_child_process6, import_node_crypto2, import_promises26, import_node_net, import_node_path32, import_node_util4, NON_AUTHORITATIVE_LABEL, PGLITE_INSTALL_HINT, ENV_ALLOWLIST, PERMISSION_DENIED, RLS_REFUSAL, DEFAULT_TIMEOUTS, RlsInputError, normalise, quoteIdent2, firstLine, plural2, NUMBERS, ACTOR_SESSION, changedRows, isRlsRefusal, isPrivilegeRefusal, SELF, PROBE_EXCLUSIONS, SEED_HINTS;
19245
+ var import_node_child_process6, import_node_crypto3, import_promises27, import_node_net, import_node_path34, import_node_util4, NON_AUTHORITATIVE_LABEL, PGLITE_INSTALL_HINT, ENV_ALLOWLIST, PERMISSION_DENIED, RLS_REFUSAL, DEFAULT_TIMEOUTS, RlsInputError, normalise, quoteIdent2, firstLine, plural2, NUMBERS, ACTOR_SESSION, changedRows, isRlsRefusal, isPrivilegeRefusal, SELF, PROBE_EXCLUSIONS, SEED_HINTS;
19041
19246
  var init_rlsRunner = __esm({
19042
19247
  "src/rls/rlsRunner.ts"() {
19043
19248
  "use strict";
19044
19249
  import_node_child_process6 = require("node:child_process");
19045
- import_node_crypto2 = require("node:crypto");
19046
- import_promises26 = require("node:fs/promises");
19250
+ import_node_crypto3 = require("node:crypto");
19251
+ import_promises27 = require("node:fs/promises");
19047
19252
  import_node_net = __toESM(require("node:net"));
19048
- import_node_path32 = require("node:path");
19253
+ import_node_path34 = require("node:path");
19049
19254
  import_node_util4 = require("node:util");
19050
19255
  init_safeGit();
19051
19256
  init_permissionsMatrix();
@@ -19829,7 +20034,19 @@ function buildConnectProviders(providers) {
19829
20034
  dashboardUrl: providerDashboardUrls[provider2.id]
19830
20035
  }));
19831
20036
  }
20037
+ function asksFirstByGap(artifact) {
20038
+ const asks = /* @__PURE__ */ new Map();
20039
+ try {
20040
+ for (const task of buildTaskList(artifact)) {
20041
+ const sentence = task.fixType === "repo-code" && task.requiresUserAction ? task.action?.match(/^Ask the user first: [^.]+\./)?.[0] : void 0;
20042
+ if (sentence) asks.set(task.gapId, sentence);
20043
+ }
20044
+ } catch {
20045
+ }
20046
+ return asks;
20047
+ }
19832
20048
  function buildMissionCards(artifact, providers) {
20049
+ const asksFirst = asksFirstByGap(artifact);
19833
20050
  const blockers = artifact.gaps.map((gap) => ({
19834
20051
  id: gap.id,
19835
20052
  title: gap.title,
@@ -19838,13 +20055,15 @@ function buildMissionCards(artifact, providers) {
19838
20055
  category: blockerCategoryLabel(gap),
19839
20056
  tone: blockerTone(gap.severity),
19840
20057
  prompt: gap.copyPrompt || void 0,
19841
- requiresUserAction: gapRequiresUserAction(gap)
20058
+ requiresUserAction: gapRequiresUserAction(gap),
20059
+ ...asksFirst.has(gap.id) ? { asksFirst: asksFirst.get(gap.id) } : {}
19842
20060
  }));
19843
20061
  const planSteps = artifact.gaps.map((gap) => ({
19844
20062
  id: gap.id,
19845
20063
  title: gap.title,
19846
20064
  category: planStepCategoryLabel(gap),
19847
- requiresUserAction: gapRequiresUserAction(gap)
20065
+ requiresUserAction: gapRequiresUserAction(gap),
20066
+ ...asksFirst.has(gap.id) ? { asksFirst: asksFirst.get(gap.id) } : {}
19848
20067
  }));
19849
20068
  const connectProviders = buildConnectProviders(providers);
19850
20069
  const blockerCount = blockers.length;
@@ -19859,7 +20078,7 @@ function buildMissionCards(artifact, providers) {
19859
20078
  };
19860
20079
  }
19861
20080
  function projectNameFromPath(pathValue) {
19862
- return (0, import_node_path33.basename)(pathValue.replace(/[\\/]$/, "")) || "current repo";
20081
+ return (0, import_node_path35.basename)(pathValue.replace(/[\\/]$/, "")) || "current repo";
19863
20082
  }
19864
20083
  function gapSearchText(gap) {
19865
20084
  return [
@@ -19927,15 +20146,7 @@ function providerStatusText(state) {
19927
20146
  return "Missing";
19928
20147
  }
19929
20148
  }
19930
- function verifyActionForGap(gap) {
19931
- if (gap?.id) {
19932
- return {
19933
- label: "Verify",
19934
- commandPreview: `npx -y viberaven verify --action ${gap.id}`,
19935
- intent: "verify_action",
19936
- actionId: gap.id
19937
- };
19938
- }
20149
+ function verifyActionForGap(_gap) {
19939
20150
  return {
19940
20151
  label: "Verify",
19941
20152
  commandPreview: "npx -y viberaven --verify",
@@ -20278,20 +20489,21 @@ function mapArtifactToLocalUiState(artifact, presentProviderIds) {
20278
20489
  selectedProviderId: selectedProvider.id,
20279
20490
  currentPrompt: selectedProvider.nextFix?.prompt ?? "",
20280
20491
  artifacts: {
20281
- tasklist: (0, import_node_path33.join)(".viberaven", "agent-tasklist.md"),
20282
- contextMap: (0, import_node_path33.join)(".viberaven", "context-map.json"),
20283
- gateResult: (0, import_node_path33.join)(".viberaven", "gate-result.json"),
20284
- lastScan: (0, import_node_path33.join)(".viberaven", "last-scan.json")
20492
+ tasklist: (0, import_node_path35.join)(".viberaven", "agent-tasklist.md"),
20493
+ contextMap: (0, import_node_path35.join)(".viberaven", "context-map.json"),
20494
+ gateResult: (0, import_node_path35.join)(".viberaven", "gate-result.json"),
20495
+ lastScan: (0, import_node_path35.join)(".viberaven", "last-scan.json")
20285
20496
  },
20286
20497
  empty: false
20287
20498
  };
20288
20499
  }
20289
- var import_node_path33, CONNECT_PROVIDER_PRIORITY, providerDashboardUrls, providerHints, PROVIDER_OWNED_GAP;
20500
+ var import_node_path35, CONNECT_PROVIDER_PRIORITY, providerDashboardUrls, providerHints, PROVIDER_OWNED_GAP;
20290
20501
  var init_artifactAdapter = __esm({
20291
20502
  "src/local-ui/artifactAdapter.ts"() {
20292
20503
  "use strict";
20293
- import_node_path33 = require("node:path");
20504
+ import_node_path35 = require("node:path");
20294
20505
  init_providerLogos();
20506
+ init_buildTaskList();
20295
20507
  init_prompts();
20296
20508
  init_providerLaunchPaths();
20297
20509
  init_launchRunway();
@@ -20592,18 +20804,18 @@ var init_planLimits = __esm({
20592
20804
  // src/hosted/credentials.ts
20593
20805
  function defaultConfigDir(env = process.env, platform = process.platform) {
20594
20806
  const override = env.VIBERAVEN_CONFIG_DIR?.trim();
20595
- if (override) return (0, import_node_path34.resolve)(override);
20807
+ if (override) return (0, import_node_path36.resolve)(override);
20596
20808
  if (platform === "win32") {
20597
- return (0, import_node_path34.join)(env.APPDATA?.trim() || (0, import_node_path34.join)((0, import_node_os.homedir)(), "AppData", "Roaming"), "viberaven");
20809
+ return (0, import_node_path36.join)(env.APPDATA?.trim() || (0, import_node_path36.join)((0, import_node_os.homedir)(), "AppData", "Roaming"), "viberaven");
20598
20810
  }
20599
20811
  if (platform === "darwin") {
20600
- return (0, import_node_path34.join)((0, import_node_os.homedir)(), "Library", "Application Support", "viberaven");
20812
+ return (0, import_node_path36.join)((0, import_node_os.homedir)(), "Library", "Application Support", "viberaven");
20601
20813
  }
20602
- return (0, import_node_path34.join)(env.XDG_CONFIG_HOME?.trim() || (0, import_node_path34.join)((0, import_node_os.homedir)(), ".config"), "viberaven");
20814
+ return (0, import_node_path36.join)(env.XDG_CONFIG_HOME?.trim() || (0, import_node_path36.join)((0, import_node_os.homedir)(), ".config"), "viberaven");
20603
20815
  }
20604
20816
  function isInside(child, parent) {
20605
- const rel = (0, import_node_path34.relative)((0, import_node_path34.resolve)(parent), (0, import_node_path34.resolve)(child));
20606
- return rel === "" || !rel.startsWith("..") && !(0, import_node_path34.isAbsolute)(rel);
20817
+ const rel = (0, import_node_path36.relative)((0, import_node_path36.resolve)(parent), (0, import_node_path36.resolve)(child));
20818
+ return rel === "" || !rel.startsWith("..") && !(0, import_node_path36.isAbsolute)(rel);
20607
20819
  }
20608
20820
  async function retryTransient(work) {
20609
20821
  for (let attempt = 1; ; attempt += 1) {
@@ -20616,15 +20828,15 @@ async function retryTransient(work) {
20616
20828
  }
20617
20829
  }
20618
20830
  }
20619
- var import_node_child_process7, import_node_crypto3, import_promises27, import_node_os, import_node_path34, CREDENTIALS_FILE_NAME, CredentialLocationError, CredentialPermissionError, restrictWithIcacls, TRANSIENT_FS_CODES, FS_ATTEMPTS, FS_BACKOFF_MS, CredentialStore;
20831
+ var import_node_child_process7, import_node_crypto4, import_promises28, import_node_os, import_node_path36, CREDENTIALS_FILE_NAME, CredentialLocationError, CredentialPermissionError, restrictWithIcacls, TRANSIENT_FS_CODES, FS_ATTEMPTS, FS_BACKOFF_MS, CredentialStore;
20620
20832
  var init_credentials = __esm({
20621
20833
  "src/hosted/credentials.ts"() {
20622
20834
  "use strict";
20623
20835
  import_node_child_process7 = require("node:child_process");
20624
- import_node_crypto3 = require("node:crypto");
20625
- import_promises27 = require("node:fs/promises");
20836
+ import_node_crypto4 = require("node:crypto");
20837
+ import_promises28 = require("node:fs/promises");
20626
20838
  import_node_os = require("node:os");
20627
- import_node_path34 = require("node:path");
20839
+ import_node_path36 = require("node:path");
20628
20840
  CREDENTIALS_FILE_NAME = "credentials.json";
20629
20841
  CredentialLocationError = class extends Error {
20630
20842
  };
@@ -20651,9 +20863,9 @@ var init_credentials = __esm({
20651
20863
  constructor(configDir, workspacePath, options = {}) {
20652
20864
  this.configDir = configDir;
20653
20865
  this.workspacePath = workspacePath;
20654
- this.path = (0, import_node_path34.join)(configDir, CREDENTIALS_FILE_NAME);
20866
+ this.path = (0, import_node_path36.join)(configDir, CREDENTIALS_FILE_NAME);
20655
20867
  this.restrict = options.restrict !== void 0 ? options.restrict : process.platform === "win32" ? restrictWithIcacls : null;
20656
- this.fs = { readFile: (path, encoding) => (0, import_promises27.readFile)(path, encoding), rename: (from, to) => (0, import_promises27.rename)(from, to), rm: (path) => (0, import_promises27.rm)(path, { force: true }), ...options.fs };
20868
+ this.fs = { readFile: (path, encoding) => (0, import_promises28.readFile)(path, encoding), rename: (from, to) => (0, import_promises28.rename)(from, to), rm: (path) => (0, import_promises28.rm)(path, { force: true }), ...options.fs };
20657
20869
  }
20658
20870
  path;
20659
20871
  restrict;
@@ -20702,15 +20914,15 @@ var init_credentials = __esm({
20702
20914
  `Refusing to store the VibeRaven sign-in inside this project (${this.configDir}). Set VIBERAVEN_CONFIG_DIR to a folder outside the repo.`
20703
20915
  );
20704
20916
  }
20705
- await (0, import_promises27.mkdir)(this.configDir, { recursive: true, mode: 448 });
20706
- const temp = `${this.path}.${(0, import_node_crypto3.randomUUID)()}.tmp`;
20707
- await (0, import_promises27.writeFile)(temp, `${JSON.stringify(body, null, 2)}
20917
+ await (0, import_promises28.mkdir)(this.configDir, { recursive: true, mode: 448 });
20918
+ const temp = `${this.path}.${(0, import_node_crypto4.randomUUID)()}.tmp`;
20919
+ await (0, import_promises28.writeFile)(temp, `${JSON.stringify(body, null, 2)}
20708
20920
  `, { encoding: "utf8", mode: 384 });
20709
- await (0, import_promises27.chmod)(temp, 384).catch(() => void 0);
20921
+ await (0, import_promises28.chmod)(temp, 384).catch(() => void 0);
20710
20922
  try {
20711
20923
  if (this.restrict) await this.restrict(temp);
20712
20924
  } catch (error) {
20713
- await (0, import_promises27.rm)(temp, { force: true });
20925
+ await (0, import_promises28.rm)(temp, { force: true });
20714
20926
  const code = error?.code;
20715
20927
  const toolMissing = code === "ENOENT";
20716
20928
  throw new CredentialPermissionError(
@@ -20721,17 +20933,17 @@ var init_credentials = __esm({
20721
20933
  if (guard) {
20722
20934
  const onDisk = await this.readFor(guard.apiBaseUrl);
20723
20935
  if (onDisk?.accessToken !== guard.accessToken || guard.isCurrent && !guard.isCurrent()) {
20724
- await (0, import_promises27.rm)(temp, { force: true });
20936
+ await (0, import_promises28.rm)(temp, { force: true });
20725
20937
  return false;
20726
20938
  }
20727
20939
  }
20728
20940
  try {
20729
20941
  await retryTransient(() => this.fs.rename(temp, this.path));
20730
20942
  } catch (error) {
20731
- await (0, import_promises27.rm)(temp, { force: true });
20943
+ await (0, import_promises28.rm)(temp, { force: true });
20732
20944
  throw error;
20733
20945
  }
20734
- await (0, import_promises27.chmod)(this.path, 384).catch(() => void 0);
20946
+ await (0, import_promises28.chmod)(this.path, 384).catch(() => void 0);
20735
20947
  return true;
20736
20948
  }
20737
20949
  /** Keeps the stored plan label in step with what the API last said. */
@@ -21100,7 +21312,7 @@ async function buildHostedCheckPayload(workspacePath) {
21100
21312
  throw new Error("Could not assemble the full check request.");
21101
21313
  }
21102
21314
  function finalizePayload(payload) {
21103
- const folder = (0, import_node_path35.basename)(payload.workspacePath.split("\\").join("/").replace(/\/+$/, "")) || "project";
21315
+ const folder = (0, import_node_path37.basename)(payload.workspacePath.split("\\").join("/").replace(/\/+$/, "")) || "project";
21104
21316
  const redacted = redactPayload({ ...payload, workspacePath: folder });
21105
21317
  return { payload: redacted.payload, redactions: redacted.count };
21106
21318
  }
@@ -21131,13 +21343,13 @@ function splitSections(prompt) {
21131
21343
  chars: section.length
21132
21344
  }));
21133
21345
  }
21134
- var import_node_path35, PayloadCaptured;
21346
+ var import_node_path37, PayloadCaptured;
21135
21347
  var init_payload = __esm({
21136
21348
  "src/hosted/payload.ts"() {
21137
21349
  "use strict";
21138
21350
  init_fileScanner();
21139
21351
  init_orchestrator();
21140
- import_node_path35 = require("node:path");
21352
+ import_node_path37 = require("node:path");
21141
21353
  init_config();
21142
21354
  init_redact();
21143
21355
  PayloadCaptured = class extends Error {
@@ -21213,11 +21425,11 @@ function readServerMessage(body) {
21213
21425
  return body.slice(0, 300);
21214
21426
  }
21215
21427
  }
21216
- var import_node_crypto4, PRICING_URL, FREE_LIMIT_MESSAGE, PREVIEW_TTL_MS, STATUS_TIMEOUT_MS, STATUS_CACHE_MS, MAX_PREVIEWS, TRANSIENT_REASONS, SEVERITY_RANK3, FullCheckService;
21428
+ var import_node_crypto5, PRICING_URL, FREE_LIMIT_MESSAGE, PREVIEW_TTL_MS, STATUS_TIMEOUT_MS, STATUS_CACHE_MS, MAX_PREVIEWS, TRANSIENT_REASONS, SEVERITY_RANK3, FullCheckService;
21217
21429
  var init_fullCheck = __esm({
21218
21430
  "src/hosted/fullCheck.ts"() {
21219
21431
  "use strict";
21220
- import_node_crypto4 = require("node:crypto");
21432
+ import_node_crypto5 = require("node:crypto");
21221
21433
  init_engines();
21222
21434
  init_planLimits();
21223
21435
  init_productionMapLanes();
@@ -21482,8 +21694,8 @@ var init_fullCheck = __esm({
21482
21694
  const built = await this.buildPayload(this.workspacePath);
21483
21695
  const final = finalizePayload(built.payload);
21484
21696
  this.prunePreviews();
21485
- const id = (0, import_node_crypto4.randomUUID)();
21486
- this.previews.set(id, { payload: final.payload, idempotencyKey: (0, import_node_crypto4.randomUUID)(), createdAt: Date.now() });
21697
+ const id = (0, import_node_crypto5.randomUUID)();
21698
+ this.previews.set(id, { payload: final.payload, idempotencyKey: (0, import_node_crypto5.randomUUID)(), createdAt: Date.now() });
21487
21699
  return { kind: "preview", preview: describePayload(id, `${this.apiBaseUrl}/v1/station/run`, final.payload, built.scan, final.redactions) };
21488
21700
  } catch (error) {
21489
21701
  if (error instanceof HostedCallError) return this.fromCallError(error);
@@ -21603,10 +21815,10 @@ async function connectProviderMcp(cwd, providerId) {
21603
21815
  if (!recipe) {
21604
21816
  return { ok: false, message: `No known MCP server for ${providerId} yet. Add one manually to .mcp.json.` };
21605
21817
  }
21606
- const configPath = (0, import_node_path36.join)(cwd, ".mcp.json");
21818
+ const configPath = (0, import_node_path38.join)(cwd, ".mcp.json");
21607
21819
  let config = {};
21608
21820
  try {
21609
- const raw = await (0, import_promises28.readFile)(configPath, "utf8");
21821
+ const raw = await (0, import_promises29.readFile)(configPath, "utf8");
21610
21822
  const parsed = JSON.parse(raw.replace(/^\uFEFF/, ""));
21611
21823
  if (!isRecord5(parsed) || parsed.mcpServers !== void 0 && !isRecord5(parsed.mcpServers)) {
21612
21824
  return { ok: false, message: "Existing .mcp.json has an unsupported structure. It was left unchanged." };
@@ -21623,7 +21835,7 @@ async function connectProviderMcp(cwd, providerId) {
21623
21835
  }
21624
21836
  servers[providerId] = recipe.entry;
21625
21837
  const next = { ...config, mcpServers: servers };
21626
- await (0, import_promises28.writeFile)(configPath, `${JSON.stringify(next, null, 2)}
21838
+ await (0, import_promises29.writeFile)(configPath, `${JSON.stringify(next, null, 2)}
21627
21839
  `, "utf8");
21628
21840
  const suffix = recipe.note ? ` ${recipe.note}` : "";
21629
21841
  return { ok: true, message: `Added ${providerId} MCP server to .mcp.json.${suffix}`, path: ".mcp.json" };
@@ -21635,7 +21847,7 @@ function headerValue(request, name) {
21635
21847
  function tokensMatch(given, expected) {
21636
21848
  const a = Buffer.from(given, "utf8");
21637
21849
  const b3 = Buffer.from(expected, "utf8");
21638
- return a.length === b3.length && (0, import_node_crypto5.timingSafeEqual)(a, b3);
21850
+ return a.length === b3.length && (0, import_node_crypto6.timingSafeEqual)(a, b3);
21639
21851
  }
21640
21852
  function guardRequest(request, response, pathname, guard) {
21641
21853
  const allowedHosts = [`127.0.0.1:${guard.port}`, `localhost:${guard.port}`];
@@ -21676,7 +21888,7 @@ async function defaultScanRunner(flags, positional) {
21676
21888
  }
21677
21889
  async function readPackageJson(cwd) {
21678
21890
  try {
21679
- const raw = await (0, import_promises28.readFile)((0, import_node_path36.join)(cwd, "package.json"), "utf8");
21891
+ const raw = await (0, import_promises29.readFile)((0, import_node_path38.join)(cwd, "package.json"), "utf8");
21680
21892
  const parsed = JSON.parse(raw.replace(/^\uFEFF/, ""));
21681
21893
  return isRecord5(parsed) ? parsed : {};
21682
21894
  } catch {
@@ -21699,7 +21911,7 @@ async function listProjectFiles(cwd, maxFiles = 1800) {
21699
21911
  if (files.length >= maxFiles) return;
21700
21912
  let entries;
21701
21913
  try {
21702
- entries = await (0, import_promises28.readdir)(dir, { withFileTypes: true });
21914
+ entries = await (0, import_promises29.readdir)(dir, { withFileTypes: true });
21703
21915
  } catch {
21704
21916
  return;
21705
21917
  }
@@ -21707,7 +21919,7 @@ async function listProjectFiles(cwd, maxFiles = 1800) {
21707
21919
  if (files.length >= maxFiles) return;
21708
21920
  const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
21709
21921
  if (entry.isDirectory()) {
21710
- if (!skipped.has(entry.name)) await walk((0, import_node_path36.join)(dir, entry.name), rel);
21922
+ if (!skipped.has(entry.name)) await walk((0, import_node_path38.join)(dir, entry.name), rel);
21711
21923
  continue;
21712
21924
  }
21713
21925
  if (entry.isFile()) files.push(rel.toLowerCase());
@@ -21726,7 +21938,7 @@ async function readEnvKeys(cwd) {
21726
21938
  const keys = /* @__PURE__ */ new Set();
21727
21939
  for (const name of [".env.example", ".env"]) {
21728
21940
  try {
21729
- const raw = await (0, import_promises28.readFile)((0, import_node_path36.join)(cwd, name), "utf8");
21941
+ const raw = await (0, import_promises29.readFile)((0, import_node_path38.join)(cwd, name), "utf8");
21730
21942
  for (const line of raw.split(/\r?\n/)) {
21731
21943
  const trimmed = line.trim();
21732
21944
  if (!trimmed || trimmed.startsWith("#")) continue;
@@ -22044,7 +22256,7 @@ async function codexModelOptions() {
22044
22256
  { id: "gpt-5.4-mini", label: "GPT-5.4 Mini" }
22045
22257
  ];
22046
22258
  try {
22047
- const raw = await (0, import_promises28.readFile)((0, import_node_path36.join)((0, import_node_os2.homedir)(), ".codex", "models_cache.json"), "utf8");
22259
+ const raw = await (0, import_promises29.readFile)((0, import_node_path38.join)((0, import_node_os2.homedir)(), ".codex", "models_cache.json"), "utf8");
22048
22260
  const parsed = JSON.parse(raw);
22049
22261
  const list = Array.isArray(parsed) ? parsed : parsed.models ?? [];
22050
22262
  const options = list.filter((model) => typeof model?.slug === "string").filter((model) => !/auto-review|reserve/i.test(model.slug)).map((model) => ({ id: model.slug, label: model.display_name?.trim() || model.slug }));
@@ -22247,19 +22459,19 @@ function isPlaceholderProjectName(value) {
22247
22459
  async function readMcpEvidence(cwd) {
22248
22460
  const candidates = MCP_CONFIG_PATHS.map((relativePath) => ({
22249
22461
  source: relativePath.replace(/\\/g, "/"),
22250
- path: (0, import_node_path36.resolve)(cwd, relativePath)
22462
+ path: (0, import_node_path38.resolve)(cwd, relativePath)
22251
22463
  }));
22252
22464
  const home = process.env.USERPROFILE || process.env.HOME;
22253
22465
  if (home) {
22254
22466
  candidates.push({
22255
22467
  source: "~/.codex/config.toml",
22256
- path: (0, import_node_path36.resolve)(home, ".codex", "config.toml")
22468
+ path: (0, import_node_path38.resolve)(home, ".codex", "config.toml")
22257
22469
  });
22258
22470
  }
22259
22471
  const evidence = [];
22260
22472
  for (const candidate of candidates) {
22261
22473
  try {
22262
- const text = await (0, import_promises28.readFile)(candidate.path, "utf8");
22474
+ const text = await (0, import_promises29.readFile)(candidate.path, "utf8");
22263
22475
  if (/mcp/i.test(text)) evidence.push({ source: candidate.source, text });
22264
22476
  } catch {
22265
22477
  }
@@ -22350,7 +22562,7 @@ async function enrichProjectMetadata(state, cwd) {
22350
22562
  async function readState(cwd) {
22351
22563
  let state;
22352
22564
  try {
22353
- const raw = await (0, import_promises28.readFile)((0, import_node_path36.join)(cwd, ".viberaven", "last-scan.json"), "utf8");
22565
+ const raw = await (0, import_promises29.readFile)((0, import_node_path38.join)(cwd, ".viberaven", "last-scan.json"), "utf8");
22354
22566
  const packageJson = await readPackageJson(cwd);
22355
22567
  const deps = dependencyNames(packageJson);
22356
22568
  const files = await listProjectFiles(cwd);
@@ -22617,7 +22829,7 @@ async function whereExe(command, cwd) {
22617
22829
  }
22618
22830
  function expandShimPath(value, base) {
22619
22831
  const relative8 = value.replace(/^%(?:dp0%\\+|~dp0\\*)/i, "");
22620
- return relative8 === value ? value : (0, import_node_path36.join)(base, ...relative8.split(/[\\/]+/));
22832
+ return relative8 === value ? value : (0, import_node_path38.join)(base, ...relative8.split(/[\\/]+/));
22621
22833
  }
22622
22834
  function findShimTarget(lines, pattern, accept = () => true) {
22623
22835
  for (const line of lines) {
@@ -22628,21 +22840,21 @@ function findShimTarget(lines, pattern, accept = () => true) {
22628
22840
  }
22629
22841
  async function resolveWindowsShimTarget(shimPath) {
22630
22842
  try {
22631
- const content = await (0, import_promises28.readFile)(shimPath, "utf8");
22632
- const base = (0, import_node_path36.dirname)(shimPath);
22843
+ const content = await (0, import_promises29.readFile)(shimPath, "utf8");
22844
+ const base = (0, import_node_path38.dirname)(shimPath);
22633
22845
  const lines = content.split(/\r?\n/);
22634
22846
  const trailingArgs = (line, after) => [...line.slice(after).split("%*")[0].matchAll(/"([^"]*)"|([^\s"&|<>]+)/g)].map((token) => expandShimPath(token[1] ?? token[2], base)).filter((value) => value.length > 0 && !value.includes("%"));
22635
22847
  const script = findShimTarget(lines, /"%(?:dp0%\\+|~dp0\\*)([^"]+\.js)"/i);
22636
22848
  if (script) {
22637
22849
  return {
22638
22850
  command: process.execPath,
22639
- args: [(0, import_node_path36.join)(base, ...script.match[1].split(/[\\/]+/)), ...trailingArgs(script.line, script.match.index + script.match[0].length)]
22851
+ args: [(0, import_node_path38.join)(base, ...script.match[1].split(/[\\/]+/)), ...trailingArgs(script.line, script.match.index + script.match[0].length)]
22640
22852
  };
22641
22853
  }
22642
- const binary = findShimTarget(lines, /"%(?:dp0%\\+|~dp0\\*)([^"]+\.exe)"/i, (target) => (0, import_node_path36.basename)(target).toLowerCase() !== "node.exe");
22854
+ const binary = findShimTarget(lines, /"%(?:dp0%\\+|~dp0\\*)([^"]+\.exe)"/i, (target) => (0, import_node_path38.basename)(target).toLowerCase() !== "node.exe");
22643
22855
  if (binary) {
22644
22856
  return {
22645
- command: (0, import_node_path36.join)(base, ...binary.match[1].split(/[\\/]+/)),
22857
+ command: (0, import_node_path38.join)(base, ...binary.match[1].split(/[\\/]+/)),
22646
22858
  args: trailingArgs(binary.line, binary.match.index + binary.match[0].length)
22647
22859
  };
22648
22860
  }
@@ -22767,8 +22979,8 @@ async function writeChatContextArtifact(cwd, request, prompt) {
22767
22979
  "",
22768
22980
  prompt
22769
22981
  ].filter((line) => typeof line === "string");
22770
- await (0, import_promises28.mkdir)((0, import_node_path36.join)(cwd, ".viberaven"), { recursive: true });
22771
- await (0, import_promises28.writeFile)((0, import_node_path36.join)(cwd, ".viberaven", "chat-context.md"), `${lines.join("\n")}
22982
+ await (0, import_promises29.mkdir)((0, import_node_path38.join)(cwd, ".viberaven"), { recursive: true });
22983
+ await (0, import_promises29.writeFile)((0, import_node_path38.join)(cwd, ".viberaven", "chat-context.md"), `${lines.join("\n")}
22772
22984
  `, "utf8");
22773
22985
  } catch {
22774
22986
  }
@@ -22788,13 +23000,13 @@ async function runAgentChat(cwd, request) {
22788
23000
  }
22789
23001
  const prompt = await buildAgentChatPrompt(request, cwd);
22790
23002
  await writeChatContextArtifact(cwd, request, prompt);
22791
- const outputPath = request.cliId === "codex" ? (0, import_node_path36.join)(cwd, ".viberaven", `studio-agent-${Date.now()}-${Math.random().toString(36).slice(2)}.md`) : void 0;
22792
- if (outputPath) await (0, import_promises28.mkdir)((0, import_node_path36.dirname)(outputPath), { recursive: true });
23003
+ const outputPath = request.cliId === "codex" ? (0, import_node_path38.join)(cwd, ".viberaven", `studio-agent-${Date.now()}-${Math.random().toString(36).slice(2)}.md`) : void 0;
23004
+ if (outputPath) await (0, import_promises29.mkdir)((0, import_node_path38.dirname)(outputPath), { recursive: true });
22793
23005
  const { command, args, stdin } = commandForAgentChat(request, prompt, cwd, outputPath);
22794
23006
  const result = await runAgentCommand(command, args, cwd, stdin, request.runId);
22795
23007
  if (outputPath) {
22796
23008
  try {
22797
- const finalMessage = (await (0, import_promises28.readFile)(outputPath, "utf8")).trim();
23009
+ const finalMessage = (await (0, import_promises29.readFile)(outputPath, "utf8")).trim();
22798
23010
  if (finalMessage) return { ...result, output: finalMessage };
22799
23011
  } catch {
22800
23012
  }
@@ -22806,8 +23018,8 @@ function firstGapId(state) {
22806
23018
  return state.providers.find((provider2) => provider2.nextFix?.id)?.nextFix?.id;
22807
23019
  }
22808
23020
  async function runLocalAction(cwd, intent) {
22809
- const artifactDir = (0, import_node_path36.join)(cwd, ".viberaven");
22810
- await (0, import_promises28.mkdir)(artifactDir, { recursive: true });
23021
+ const artifactDir = (0, import_node_path38.join)(cwd, ".viberaven");
23022
+ await (0, import_promises29.mkdir)(artifactDir, { recursive: true });
22811
23023
  if (intent === "preview_rehearsal") {
22812
23024
  const state2 = await readState(cwd);
22813
23025
  const topProvider = state2.providers[0];
@@ -22848,7 +23060,7 @@ async function runLocalAction(cwd, intent) {
22848
23060
  proof,
22849
23061
  ""
22850
23062
  ].join("\n");
22851
- await (0, import_promises28.writeFile)((0, import_node_path36.join)(artifactDir, "preview-rehearsal.md"), markdown, "utf8");
23063
+ await (0, import_promises29.writeFile)((0, import_node_path38.join)(artifactDir, "preview-rehearsal.md"), markdown, "utf8");
22852
23064
  return {
22853
23065
  title: "Preview rehearsal ready",
22854
23066
  summary: `${projectName}: ${gateLabel} with ${state2.providers.length} provider beat${state2.providers.length === 1 ? "" : "s"}`,
@@ -22858,7 +23070,7 @@ async function runLocalAction(cwd, intent) {
22858
23070
  if (intent === "audit_vercel_supabase") {
22859
23071
  const auditInput = await collectVercelSupabaseAuditInput(cwd);
22860
23072
  const result2 = buildVercelSupabaseAudit(auditInput);
22861
- await (0, import_promises28.writeFile)((0, import_node_path36.join)(artifactDir, "audit-vercel-supabase.txt"), `${renderVercelSupabaseAudit(result2)}
23073
+ await (0, import_promises29.writeFile)((0, import_node_path38.join)(artifactDir, "audit-vercel-supabase.txt"), `${renderVercelSupabaseAudit(result2)}
22862
23074
  `, "utf8");
22863
23075
  return {
22864
23076
  title: result2.status === "pass" ? "Audit passed" : "Audit needs work",
@@ -22886,7 +23098,7 @@ async function runLocalAction(cwd, intent) {
22886
23098
  const mode = intent === "heal_prompt" ? "prompt" : intent === "heal_apply" ? "apply" : "plan";
22887
23099
  const result = await runHealCommand({ cwd, mode, gapId, yes: mode === "apply", noVerify: true });
22888
23100
  if (mode === "apply") {
22889
- await (0, import_promises28.writeFile)((0, import_node_path36.join)(artifactDir, "heal-apply-result.json"), `${JSON.stringify(result, null, 2)}
23101
+ await (0, import_promises29.writeFile)((0, import_node_path38.join)(artifactDir, "heal-apply-result.json"), `${JSON.stringify(result, null, 2)}
22890
23102
  `, "utf8");
22891
23103
  }
22892
23104
  return {
@@ -22896,14 +23108,14 @@ async function runLocalAction(cwd, intent) {
22896
23108
  };
22897
23109
  }
22898
23110
  async function sendArtifact(cwd, rel, response, contentType = "text/plain; charset=utf-8") {
22899
- const absolute = (0, import_node_path36.resolve)(cwd, rel);
22900
- const root = (0, import_node_path36.resolve)(cwd);
23111
+ const absolute = (0, import_node_path38.resolve)(cwd, rel);
23112
+ const root = (0, import_node_path38.resolve)(cwd);
22901
23113
  if (!absolute.startsWith(root)) {
22902
23114
  sendText(response, 403, "Forbidden");
22903
23115
  return;
22904
23116
  }
22905
23117
  try {
22906
- const text = await (0, import_promises28.readFile)(absolute, "utf8");
23118
+ const text = await (0, import_promises29.readFile)(absolute, "utf8");
22907
23119
  response.writeHead(200, { "content-type": contentType });
22908
23120
  response.end(text);
22909
23121
  } catch {
@@ -22912,7 +23124,7 @@ async function sendArtifact(cwd, rel, response, contentType = "text/plain; chars
22912
23124
  }
22913
23125
  async function sendReportArtifact(cwd, response) {
22914
23126
  try {
22915
- const text = await (0, import_promises28.readFile)((0, import_node_path36.join)(cwd, ".viberaven", "report.html"), "utf8");
23127
+ const text = await (0, import_promises29.readFile)((0, import_node_path38.join)(cwd, ".viberaven", "report.html"), "utf8");
22916
23128
  response.writeHead(200, { "content-type": "text/html; charset=utf-8" });
22917
23129
  response.end(text);
22918
23130
  return;
@@ -23039,7 +23251,7 @@ async function sendReportArtifact(cwd, response) {
23039
23251
  }
23040
23252
  async function sendExtensionIcon(cwd, response) {
23041
23253
  try {
23042
- const body = await (0, import_promises28.readFile)((0, import_node_path36.join)(cwd, "media", "extension-icon.png"));
23254
+ const body = await (0, import_promises29.readFile)((0, import_node_path38.join)(cwd, "media", "extension-icon.png"));
23043
23255
  sendBinary(response, 200, "image/png", body);
23044
23256
  } catch {
23045
23257
  sendText(response, 404, "Icon not found");
@@ -23052,15 +23264,15 @@ async function sendMangaAsset(pathname, response) {
23052
23264
  return;
23053
23265
  }
23054
23266
  const candidates = [
23055
- (0, import_node_path36.join)(__dirname, "manga", name),
23267
+ (0, import_node_path38.join)(__dirname, "manga", name),
23056
23268
  // installed + dev: <pkg>/dist/manga
23057
- (0, import_node_path36.join)(__dirname, "..", "assets", "manga", name),
23269
+ (0, import_node_path38.join)(__dirname, "..", "assets", "manga", name),
23058
23270
  // fallback: <pkg>/assets/manga (source)
23059
- (0, import_node_path36.join)(__dirname, "..", "..", "assets", "manga", name)
23271
+ (0, import_node_path38.join)(__dirname, "..", "..", "assets", "manga", name)
23060
23272
  ];
23061
23273
  for (const candidate of candidates) {
23062
23274
  try {
23063
- const body = await (0, import_promises28.readFile)(candidate);
23275
+ const body = await (0, import_promises29.readFile)(candidate);
23064
23276
  response.setHeader("cache-control", "no-cache");
23065
23277
  sendBinary(response, 200, contentTypeForAsset(candidate), body);
23066
23278
  return;
@@ -23070,7 +23282,7 @@ async function sendMangaAsset(pathname, response) {
23070
23282
  sendText(response, 404, "Asset not found");
23071
23283
  }
23072
23284
  function contentTypeForAsset(pathname) {
23073
- switch ((0, import_node_path36.extname)(pathname).toLowerCase()) {
23285
+ switch ((0, import_node_path38.extname)(pathname).toLowerCase()) {
23074
23286
  case ".svg":
23075
23287
  return "image/svg+xml; charset=utf-8";
23076
23288
  case ".png":
@@ -23087,14 +23299,14 @@ function contentTypeForAsset(pathname) {
23087
23299
  }
23088
23300
  async function sendBundledReportAsset(pathname, response) {
23089
23301
  const rel = decodeURIComponent(pathname.replace(/^\/report\//, ""));
23090
- const assetRoot = (0, import_node_path36.resolve)(getBundledReportAssetsDir());
23091
- const absolute = (0, import_node_path36.resolve)(assetRoot, rel);
23302
+ const assetRoot = (0, import_node_path38.resolve)(getBundledReportAssetsDir());
23303
+ const absolute = (0, import_node_path38.resolve)(assetRoot, rel);
23092
23304
  if (!absolute.startsWith(assetRoot)) {
23093
23305
  sendText(response, 403, "Forbidden");
23094
23306
  return;
23095
23307
  }
23096
23308
  try {
23097
- const body = await (0, import_promises28.readFile)(absolute);
23309
+ const body = await (0, import_promises29.readFile)(absolute);
23098
23310
  sendBinary(response, 200, contentTypeForAsset(absolute), body);
23099
23311
  } catch {
23100
23312
  sendText(response, 404, "Asset not found");
@@ -23116,18 +23328,18 @@ async function bundleStackClient(entryPath) {
23116
23328
  return output.text;
23117
23329
  }
23118
23330
  async function sendStackAppJs(response) {
23119
- const distPath = (0, import_node_path36.join)(__dirname, "stack", "app.js");
23331
+ const distPath = (0, import_node_path38.join)(__dirname, "stack", "app.js");
23120
23332
  try {
23121
- const body = await (0, import_promises28.readFile)(distPath);
23333
+ const body = await (0, import_promises29.readFile)(distPath);
23122
23334
  response.writeHead(200, { "content-type": "application/javascript; charset=utf-8", "cache-control": "no-store" });
23123
23335
  response.end(body);
23124
23336
  return;
23125
23337
  } catch {
23126
23338
  }
23127
- const entryPath = (0, import_node_path36.join)(__dirname, "stack", "main.ts");
23339
+ const entryPath = (0, import_node_path38.join)(__dirname, "stack", "main.ts");
23128
23340
  let mtimeMs;
23129
23341
  try {
23130
- mtimeMs = (await (0, import_promises28.stat)(entryPath)).mtimeMs;
23342
+ mtimeMs = (await (0, import_promises29.stat)(entryPath)).mtimeMs;
23131
23343
  } catch {
23132
23344
  sendText(response, 404, "Stack client entry not found: src/local-ui/stack/main.ts is missing.");
23133
23345
  return;
@@ -23146,7 +23358,7 @@ async function sendStackAppJs(response) {
23146
23358
  }
23147
23359
  async function sendStackAppCss(response) {
23148
23360
  try {
23149
- const body = await (0, import_promises28.readFile)((0, import_node_path36.join)(__dirname, "stack", "stack.css"));
23361
+ const body = await (0, import_promises29.readFile)((0, import_node_path38.join)(__dirname, "stack", "stack.css"));
23150
23362
  response.writeHead(200, { "content-type": "text/css; charset=utf-8", "cache-control": "no-store" });
23151
23363
  response.end(body);
23152
23364
  } catch {
@@ -23155,7 +23367,7 @@ async function sendStackAppCss(response) {
23155
23367
  }
23156
23368
  async function readProviderVerifyLedger(cwd) {
23157
23369
  try {
23158
- const raw = await (0, import_promises28.readFile)((0, import_node_path36.join)(cwd, ".viberaven", "provider-verify.json"), "utf8");
23370
+ const raw = await (0, import_promises29.readFile)((0, import_node_path38.join)(cwd, ".viberaven", "provider-verify.json"), "utf8");
23159
23371
  const parsed = JSON.parse(raw.replace(/^\uFEFF/, ""));
23160
23372
  return isRecord5(parsed) ? parsed : {};
23161
23373
  } catch {
@@ -23165,8 +23377,8 @@ async function readProviderVerifyLedger(cwd) {
23165
23377
  async function writeProviderVerifyEntry(cwd, providerId, entry) {
23166
23378
  const ledger = await readProviderVerifyLedger(cwd);
23167
23379
  ledger[providerId] = entry;
23168
- await (0, import_promises28.mkdir)((0, import_node_path36.join)(cwd, ".viberaven"), { recursive: true });
23169
- await (0, import_promises28.writeFile)((0, import_node_path36.join)(cwd, ".viberaven", "provider-verify.json"), `${JSON.stringify(ledger, null, 2)}
23380
+ await (0, import_promises29.mkdir)((0, import_node_path38.join)(cwd, ".viberaven"), { recursive: true });
23381
+ await (0, import_promises29.writeFile)((0, import_node_path38.join)(cwd, ".viberaven", "provider-verify.json"), `${JSON.stringify(ledger, null, 2)}
23170
23382
  `, "utf8");
23171
23383
  }
23172
23384
  async function handleRequest(request, response, options) {
@@ -23227,13 +23439,13 @@ async function handleRequest(request, response, options) {
23227
23439
  return;
23228
23440
  }
23229
23441
  if (request.method === "GET" && url.pathname === "/api/architecture-map") {
23230
- const mapPath = (0, import_node_path36.join)(options.cwd, ".viberaven", "architecture-map.md");
23231
- const markdown = await (0, import_promises28.readFile)(mapPath, "utf8").catch(() => null);
23442
+ const mapPath = (0, import_node_path38.join)(options.cwd, ".viberaven", "architecture-map.md");
23443
+ const markdown = await (0, import_promises29.readFile)(mapPath, "utf8").catch(() => null);
23232
23444
  if (markdown === null) {
23233
23445
  sendJsonStatus(response, 404, { error: "No architecture map yet. Run a scan first (it is written on every scan)." });
23234
23446
  return;
23235
23447
  }
23236
- const dataRaw = await (0, import_promises28.readFile)((0, import_node_path36.join)(options.cwd, ".viberaven", "architecture-map.json"), "utf8").catch(() => null);
23448
+ const dataRaw = await (0, import_promises29.readFile)((0, import_node_path38.join)(options.cwd, ".viberaven", "architecture-map.json"), "utf8").catch(() => null);
23237
23449
  let data = null;
23238
23450
  if (dataRaw) {
23239
23451
  try {
@@ -23242,7 +23454,7 @@ async function handleRequest(request, response, options) {
23242
23454
  data = null;
23243
23455
  }
23244
23456
  }
23245
- const contextRaw = await (0, import_promises28.readFile)((0, import_node_path36.join)(options.cwd, ".viberaven", "context-map.json"), "utf8").catch(() => null);
23457
+ const contextRaw = await (0, import_promises29.readFile)((0, import_node_path38.join)(options.cwd, ".viberaven", "context-map.json"), "utf8").catch(() => null);
23246
23458
  let gaps = [];
23247
23459
  if (contextRaw) {
23248
23460
  try {
@@ -23508,35 +23720,35 @@ async function handleRequest(request, response, options) {
23508
23720
  return;
23509
23721
  }
23510
23722
  if (request.method === "GET" && url.pathname === "/api/artifacts/tasklist") {
23511
- await sendArtifact(options.cwd, (0, import_node_path36.join)(".viberaven", "agent-tasklist.md"), response);
23723
+ await sendArtifact(options.cwd, (0, import_node_path38.join)(".viberaven", "agent-tasklist.md"), response);
23512
23724
  return;
23513
23725
  }
23514
23726
  if (request.method === "GET" && url.pathname === "/api/artifacts/playbook") {
23515
- await sendArtifact(options.cwd, (0, import_node_path36.join)(".viberaven", "launch-playbook.md"), response);
23727
+ await sendArtifact(options.cwd, (0, import_node_path38.join)(".viberaven", "launch-playbook.md"), response);
23516
23728
  return;
23517
23729
  }
23518
23730
  if (request.method === "GET" && url.pathname === "/api/artifacts/audit") {
23519
- await sendArtifact(options.cwd, (0, import_node_path36.join)(".viberaven", "audit-vercel-supabase.txt"), response);
23731
+ await sendArtifact(options.cwd, (0, import_node_path38.join)(".viberaven", "audit-vercel-supabase.txt"), response);
23520
23732
  return;
23521
23733
  }
23522
23734
  if (request.method === "GET" && url.pathname === "/api/artifacts/cleanup") {
23523
- await sendArtifact(options.cwd, (0, import_node_path36.join)(".viberaven", "context-cleanup.md"), response);
23735
+ await sendArtifact(options.cwd, (0, import_node_path38.join)(".viberaven", "context-cleanup.md"), response);
23524
23736
  return;
23525
23737
  }
23526
23738
  if (request.method === "GET" && url.pathname === "/api/artifacts/preview") {
23527
- await sendArtifact(options.cwd, (0, import_node_path36.join)(".viberaven", "preview-rehearsal.md"), response);
23739
+ await sendArtifact(options.cwd, (0, import_node_path38.join)(".viberaven", "preview-rehearsal.md"), response);
23528
23740
  return;
23529
23741
  }
23530
23742
  if (request.method === "GET" && url.pathname === "/api/artifacts/heal-plan") {
23531
- await sendArtifact(options.cwd, (0, import_node_path36.join)(".viberaven", "heal-plan.md"), response);
23743
+ await sendArtifact(options.cwd, (0, import_node_path38.join)(".viberaven", "heal-plan.md"), response);
23532
23744
  return;
23533
23745
  }
23534
23746
  if (request.method === "GET" && url.pathname === "/api/artifacts/heal-prompt") {
23535
- await sendArtifact(options.cwd, (0, import_node_path36.join)(".viberaven", "heal-prompt.md"), response);
23747
+ await sendArtifact(options.cwd, (0, import_node_path38.join)(".viberaven", "heal-prompt.md"), response);
23536
23748
  return;
23537
23749
  }
23538
23750
  if (request.method === "GET" && url.pathname === "/api/artifacts/heal-apply") {
23539
- await sendArtifact(options.cwd, (0, import_node_path36.join)(".viberaven", "heal-apply-result.json"), response, "application/json; charset=utf-8");
23751
+ await sendArtifact(options.cwd, (0, import_node_path38.join)(".viberaven", "heal-apply-result.json"), response, "application/json; charset=utf-8");
23540
23752
  return;
23541
23753
  }
23542
23754
  if (request.method === "GET" && url.pathname === "/api/artifacts/report") {
@@ -23562,12 +23774,12 @@ async function handleRequest(request, response, options) {
23562
23774
  }
23563
23775
  if (request.method === "GET" && url.pathname === "/vendor/anime.umd.min.js") {
23564
23776
  for (const candidate of [
23565
- (0, import_node_path36.join)(__dirname, "vendor", "anime.umd.min.js"),
23566
- (0, import_node_path36.join)(__dirname, "..", "assets", "vendor", "anime.umd.min.js"),
23567
- (0, import_node_path36.join)(__dirname, "..", "..", "assets", "vendor", "anime.umd.min.js")
23777
+ (0, import_node_path38.join)(__dirname, "vendor", "anime.umd.min.js"),
23778
+ (0, import_node_path38.join)(__dirname, "..", "assets", "vendor", "anime.umd.min.js"),
23779
+ (0, import_node_path38.join)(__dirname, "..", "..", "assets", "vendor", "anime.umd.min.js")
23568
23780
  ]) {
23569
23781
  try {
23570
- const body = await (0, import_promises28.readFile)(candidate);
23782
+ const body = await (0, import_promises29.readFile)(candidate);
23571
23783
  response.setHeader("cache-control", "public, max-age=604800, immutable");
23572
23784
  sendBinary(response, 200, "text/javascript; charset=utf-8", body);
23573
23785
  return;
@@ -23784,11 +23996,11 @@ function listen(server, startPort) {
23784
23996
  });
23785
23997
  }
23786
23998
  async function startLocalUiServer(options) {
23787
- const cwd = (0, import_node_path36.resolve)(options.cwd);
23999
+ const cwd = (0, import_node_path38.resolve)(options.cwd);
23788
24000
  const scanRunner = options.scanRunner ?? defaultScanRunner;
23789
24001
  const autoScan = { state: "idle" };
23790
24002
  const fullCheck = new FullCheckService(cwd, options.fullCheck);
23791
- const guard = { token: (0, import_node_crypto5.randomBytes)(32).toString("base64url"), port: 0 };
24003
+ const guard = { token: (0, import_node_crypto6.randomBytes)(32).toString("base64url"), port: 0 };
23792
24004
  const server = (0, import_node_http.createServer)((request, response) => {
23793
24005
  const pathname = new URL(request.url ?? "/", "http://127.0.0.1").pathname;
23794
24006
  if (!guardRequest(request, response, pathname, guard)) {
@@ -23801,7 +24013,7 @@ async function startLocalUiServer(options) {
23801
24013
  });
23802
24014
  const port = await listen(server, options.port ?? 4177);
23803
24015
  guard.port = port;
23804
- const hasArtifact = await (0, import_promises28.readFile)((0, import_node_path36.join)(cwd, ".viberaven", "last-scan.json"), "utf8").then(
24016
+ const hasArtifact = await (0, import_promises29.readFile)((0, import_node_path38.join)(cwd, ".viberaven", "last-scan.json"), "utf8").then(
23805
24017
  () => true,
23806
24018
  () => false
23807
24019
  );
@@ -23840,16 +24052,16 @@ async function waitForServerShutdown() {
23840
24052
  process.once("SIGTERM", done);
23841
24053
  });
23842
24054
  }
23843
- var import_node_http, import_node_child_process8, import_node_crypto5, import_promises28, import_node_os2, import_node_path36, CLI_AGENT_SETUP, MCP_CONFIG_PATHS, MCP_PROVIDER_NEEDLES, MCP_CONNECT_COMMANDS, MCP_SERVER_RECIPES, STUDIO_TOKEN_HEADER, PROVIDER_IDS, STUDIO_STATUS_ARGS, CLI_AUTH_PROBE, AGENT_RUN_TIMEOUT_MS, activeAgentRuns, stackBundleCache;
24055
+ var import_node_http, import_node_child_process8, import_node_crypto6, import_promises29, import_node_os2, import_node_path38, CLI_AGENT_SETUP, MCP_CONFIG_PATHS, MCP_PROVIDER_NEEDLES, MCP_CONNECT_COMMANDS, MCP_SERVER_RECIPES, STUDIO_TOKEN_HEADER, PROVIDER_IDS, STUDIO_STATUS_ARGS, CLI_AUTH_PROBE, AGENT_RUN_TIMEOUT_MS, activeAgentRuns, stackBundleCache;
23844
24056
  var init_server = __esm({
23845
24057
  "src/local-ui/server.ts"() {
23846
24058
  "use strict";
23847
24059
  import_node_http = require("node:http");
23848
24060
  import_node_child_process8 = require("node:child_process");
23849
- import_node_crypto5 = require("node:crypto");
23850
- import_promises28 = require("node:fs/promises");
24061
+ import_node_crypto6 = require("node:crypto");
24062
+ import_promises29 = require("node:fs/promises");
23851
24063
  import_node_os2 = require("node:os");
23852
- import_node_path36 = require("node:path");
24064
+ import_node_path38 = require("node:path");
23853
24065
  init_cleanPlan();
23854
24066
  init_audit();
23855
24067
  init_heal();
@@ -23890,8 +24102,8 @@ var init_server = __esm({
23890
24102
  MCP_CONFIG_PATHS = [
23891
24103
  ".mcp.json",
23892
24104
  "mcp.json",
23893
- (0, import_node_path36.join)(".cursor", "mcp.json"),
23894
- (0, import_node_path36.join)(".vscode", "mcp.json")
24105
+ (0, import_node_path38.join)(".cursor", "mcp.json"),
24106
+ (0, import_node_path38.join)(".vscode", "mcp.json")
23895
24107
  ];
23896
24108
  MCP_PROVIDER_NEEDLES = {
23897
24109
  supabase: ["supabase", "@supabase/mcp", "mcp-server-supabase"],
@@ -23976,13 +24188,25 @@ function buildNextActionBlock(tasks, loopState) {
23976
24188
  requiresUserAction: false
23977
24189
  };
23978
24190
  }
23979
- const providerTask = tasks.find((t) => t.fixType === "provider-action");
23980
- if (providerTask) {
24191
+ const userTask = tasks.find((t) => t.fixType === "provider-action" || t.fixType === "repo-code" && t.requiresUserAction);
24192
+ if (userTask?.fixType === "repo-code") {
24193
+ return {
24194
+ ...base,
24195
+ type: "repo-code",
24196
+ gapId: userTask.gapId,
24197
+ title: userTask.title,
24198
+ mcpTool: userTask.mcpTool,
24199
+ mcpArgs: userTask.mcpArgs,
24200
+ fallbackCommand: userTask.mcpArgs ? `npx -y viberaven --heal --apply --gap ${userTask.gapId} --yes` : void 0,
24201
+ requiresUserAction: true
24202
+ };
24203
+ }
24204
+ if (userTask) {
23981
24205
  return {
23982
24206
  ...base,
23983
24207
  type: "provider-action",
23984
- gapId: providerTask.gapId,
23985
- title: providerTask.title,
24208
+ gapId: userTask.gapId,
24209
+ title: userTask.title,
23986
24210
  requiresUserAction: true
23987
24211
  };
23988
24212
  }
@@ -24050,9 +24274,9 @@ var init_nextActionBlock = __esm({
24050
24274
  function defaultMcpConfigPaths() {
24051
24275
  const home = (0, import_node_os3.homedir)();
24052
24276
  return [
24053
- (0, import_node_path37.join)(home, ".config", "claude", "claude_desktop_config.json"),
24054
- (0, import_node_path37.join)(home, ".cursor", "mcp.json"),
24055
- (0, import_node_path37.join)(home, ".gemini", "antigravity", "mcp_config.json")
24277
+ (0, import_node_path39.join)(home, ".config", "claude", "claude_desktop_config.json"),
24278
+ (0, import_node_path39.join)(home, ".cursor", "mcp.json"),
24279
+ (0, import_node_path39.join)(home, ".gemini", "antigravity", "mcp_config.json")
24056
24280
  ];
24057
24281
  }
24058
24282
  function resolveConfigPaths() {
@@ -24081,11 +24305,11 @@ function findServerEntry(servers, provider2) {
24081
24305
  function findProviderMcpConfig(provider2, configPaths) {
24082
24306
  const paths = configPaths ?? resolveConfigPaths();
24083
24307
  for (const path of paths) {
24084
- if (!(0, import_node_fs13.existsSync)(path)) {
24308
+ if (!(0, import_node_fs14.existsSync)(path)) {
24085
24309
  continue;
24086
24310
  }
24087
24311
  try {
24088
- const raw = JSON.parse((0, import_node_fs13.readFileSync)(path, "utf8"));
24312
+ const raw = JSON.parse((0, import_node_fs14.readFileSync)(path, "utf8"));
24089
24313
  const servers = parseMcpServers(raw);
24090
24314
  if (!servers) {
24091
24315
  continue;
@@ -24129,13 +24353,13 @@ async function verifyProviderGap(options) {
24129
24353
  fallbackCommand: FALLBACK_COMMAND
24130
24354
  };
24131
24355
  }
24132
- var import_node_fs13, import_node_os3, import_node_path37, FALLBACK_COMMAND, SUPPORTED_PROVIDERS, configPathsOverride;
24356
+ var import_node_fs14, import_node_os3, import_node_path39, FALLBACK_COMMAND, SUPPORTED_PROVIDERS, configPathsOverride;
24133
24357
  var init_providerMcpBridge = __esm({
24134
24358
  "src/providerMcpBridge.ts"() {
24135
24359
  "use strict";
24136
- import_node_fs13 = require("node:fs");
24360
+ import_node_fs14 = require("node:fs");
24137
24361
  import_node_os3 = require("node:os");
24138
- import_node_path37 = require("node:path");
24362
+ import_node_path39 = require("node:path");
24139
24363
  FALLBACK_COMMAND = "npx -y viberaven audit --vercel-supabase --json";
24140
24364
  SUPPORTED_PROVIDERS = /* @__PURE__ */ new Set(["supabase", "vercel"]);
24141
24365
  }
@@ -24166,8 +24390,8 @@ Usage:
24166
24390
  viberaven check [--json] [--quiet] [path]
24167
24391
  Local production check: one line per finding, exit 1 on blockers
24168
24392
 
24169
- viberaven fix [--gap <id>] [path]
24170
- Apply a safe automated fix for a known gap (heal recipe)
24393
+ viberaven fix [--gap <id>] [--dry-run] [path]
24394
+ Apply a safe automated fix for a known gap (heal recipe); --dry-run previews only
24171
24395
 
24172
24396
  viberaven scan [--open] [--json] [path]
24173
24397
  Full local scan; writes .viberaven/ artifacts
@@ -24315,7 +24539,7 @@ function isBooleanFlag(command, key) {
24315
24539
  if (key === "quiet" && command === "check") return true;
24316
24540
  if (key === "vercel-supabase" && command === "audit") return true;
24317
24541
  if (key === "json" && command === "validate-npm-package") return true;
24318
- if (key === "dry-run" && command === "init") return true;
24542
+ if (key === "dry-run" && (command === "init" || command === "fix")) return true;
24319
24543
  if (key === "agents" && command === "doctor") return true;
24320
24544
  return false;
24321
24545
  }
@@ -24346,7 +24570,7 @@ async function cmdProviderVerify(flags, positional) {
24346
24570
  return result.verified ? 0 : 1;
24347
24571
  }
24348
24572
  async function cmdStatus(flags, positional) {
24349
- const startDir = positional[0] ? (0, import_node_path38.resolve)(process.cwd(), positional[0]) : process.cwd();
24573
+ const startDir = positional[0] ? (0, import_node_path40.resolve)(process.cwd(), positional[0]) : process.cwd();
24350
24574
  let artifact;
24351
24575
  try {
24352
24576
  artifact = await loadLastArtifact(startDir);
@@ -24385,7 +24609,7 @@ async function cmdStatus(flags, positional) {
24385
24609
  return 0;
24386
24610
  }
24387
24611
  async function runScanCommand(flags, positional, options) {
24388
- const workspacePath = positional[0] ? (0, import_node_path38.resolve)(process.cwd(), positional[0]) : await resolveWorkspaceRoot(process.cwd());
24612
+ const workspacePath = positional[0] ? (0, import_node_path40.resolve)(process.cwd(), positional[0]) : await resolveWorkspaceRoot(process.cwd());
24389
24613
  if (!options?.deferMachineOutput) {
24390
24614
  console.log(`Scanning ${workspacePath}\u2026 (local, offline)`);
24391
24615
  }
@@ -24412,7 +24636,7 @@ async function runScanCommand(flags, positional, options) {
24412
24636
  printNextActionBlock(block);
24413
24637
  printProviderActionBlock(tasks);
24414
24638
  try {
24415
- const manifest = JSON.parse(await (0, import_promises29.readFile)(paths.actionsPath, "utf8"));
24639
+ const manifest = JSON.parse(await (0, import_promises30.readFile)(paths.actionsPath, "utf8"));
24416
24640
  console.log(renderActionSurface(manifest, { limit: 5 }).trimEnd());
24417
24641
  } catch (error) {
24418
24642
  console.warn(error instanceof Error ? error.message : String(error));
@@ -24429,7 +24653,7 @@ async function runScanCommand(flags, positional, options) {
24429
24653
  return { exitCode: 0, artifacts: paths };
24430
24654
  }
24431
24655
  async function cmdReport(flags, positional) {
24432
- const startDir = positional[0] ? (0, import_node_path38.resolve)(process.cwd(), positional[0]) : process.cwd();
24656
+ const startDir = positional[0] ? (0, import_node_path40.resolve)(process.cwd(), positional[0]) : process.cwd();
24433
24657
  try {
24434
24658
  const paths = await refreshReportFromDisk(startDir);
24435
24659
  console.log(`Report refreshed: ${paths.reportPath}`);
@@ -24451,7 +24675,7 @@ async function cmdReport(flags, positional) {
24451
24675
  }
24452
24676
  }
24453
24677
  async function cmdPrompt(flags, positional) {
24454
- const startDir = positional[0] ? (0, import_node_path38.resolve)(process.cwd(), positional[0]) : process.cwd();
24678
+ const startDir = positional[0] ? (0, import_node_path40.resolve)(process.cwd(), positional[0]) : process.cwd();
24455
24679
  let artifact;
24456
24680
  try {
24457
24681
  artifact = await loadLastArtifact(startDir);
@@ -24548,7 +24772,7 @@ async function main() {
24548
24772
  const wantsJsonl = hasFlag(flags, "jsonl");
24549
24773
  const wantsStrict = hasFlag(flags, "strict");
24550
24774
  if (flags.condense) {
24551
- const cwd = positional[0] ? (0, import_node_path38.resolve)(process.cwd(), positional[0]) : process.cwd();
24775
+ const cwd = positional[0] ? (0, import_node_path40.resolve)(process.cwd(), positional[0]) : process.cwd();
24552
24776
  const result = await runCondenseCommand({ cwd });
24553
24777
  console.log(`VibeRaven context map refreshed: ${result.contextMapPath}`);
24554
24778
  return 0;
@@ -24568,7 +24792,7 @@ async function main() {
24568
24792
  }
24569
24793
  if (!command && flags.verify === true && typeof flags.action === "string") {
24570
24794
  return runVerifyActionCommand({
24571
- cwd: positional[0] ? (0, import_node_path38.resolve)(process.cwd(), positional[0]) : process.cwd(),
24795
+ cwd: positional[0] ? (0, import_node_path40.resolve)(process.cwd(), positional[0]) : process.cwd(),
24572
24796
  actionId: flags.action
24573
24797
  });
24574
24798
  }
@@ -24579,7 +24803,7 @@ async function main() {
24579
24803
  console.error("VibeRaven could not produce machine output because scan artifacts were not written.");
24580
24804
  return 3;
24581
24805
  }
24582
- const gateResult = scanResult.artifacts && (wantsJson || wantsJsonl || wantsStrict) ? JSON.parse(await (0, import_promises29.readFile)(scanResult.artifacts.gateResultPath, "utf8")) : void 0;
24806
+ const gateResult = scanResult.artifacts && (wantsJson || wantsJsonl || wantsStrict) ? JSON.parse(await (0, import_promises30.readFile)(scanResult.artifacts.gateResultPath, "utf8")) : void 0;
24583
24807
  const strictExitCode = wantsStrict && gateResult ? exitCodeForStrictGate(gateResult, { failOnWarnings: flags.strict === "warning" }) : scanResult.exitCode;
24584
24808
  if (wantsJson && gateResult) {
24585
24809
  process.stdout.write(renderGateResultJson(gateResult));
@@ -24601,7 +24825,7 @@ async function main() {
24601
24825
  env: process.env
24602
24826
  });
24603
24827
  if (mode === "local-ui") {
24604
- const cwd = positional[0] ? (0, import_node_path38.resolve)(process.cwd(), positional[0]) : process.cwd();
24828
+ const cwd = positional[0] ? (0, import_node_path40.resolve)(process.cwd(), positional[0]) : process.cwd();
24605
24829
  const handle = await startLocalUiServer({ cwd });
24606
24830
  console.log(`VibeRaven local UI: ${handle.url}`);
24607
24831
  await waitForServerShutdown();
@@ -24621,7 +24845,7 @@ async function main() {
24621
24845
  await runInteractiveSession();
24622
24846
  return 0;
24623
24847
  case "ui": {
24624
- const cwd = positional[0] ? (0, import_node_path38.resolve)(process.cwd(), positional[0]) : process.cwd();
24848
+ const cwd = positional[0] ? (0, import_node_path40.resolve)(process.cwd(), positional[0]) : process.cwd();
24625
24849
  const envPort = Number(process.env.PORT);
24626
24850
  const handle = await startLocalUiServer({ cwd, port: Number.isFinite(envPort) && envPort > 0 ? envPort : void 0 });
24627
24851
  console.log(`VibeRaven local UI: ${handle.url}`);
@@ -24633,31 +24857,33 @@ async function main() {
24633
24857
  return cmdStatus(flags, positional);
24634
24858
  case "check":
24635
24859
  return runCheckCommand({
24636
- cwd: positional[0] ? (0, import_node_path38.resolve)(process.cwd(), positional[0]) : process.cwd(),
24860
+ cwd: positional[0] ? (0, import_node_path40.resolve)(process.cwd(), positional[0]) : process.cwd(),
24637
24861
  json: Boolean(flags.json),
24638
24862
  quiet: Boolean(flags.quiet)
24639
24863
  });
24640
24864
  case "fix": {
24641
- const fixCwd = positional[0] ? (0, import_node_path38.resolve)(process.cwd(), positional[0]) : process.cwd();
24865
+ const fixCwd = positional[0] ? (0, import_node_path40.resolve)(process.cwd(), positional[0]) : process.cwd();
24642
24866
  const gapId = typeof flags.gap === "string" ? flags.gap : void 0;
24643
24867
  if (!gapId) {
24644
24868
  const artifact = await loadLastArtifact(fixCwd).catch(() => void 0);
24645
- const fixable = artifact ? buildTaskList(artifact).filter((t) => t.fixType === "repo-code" && !t.requiresUserAction) : [];
24869
+ const fixable = artifact ? buildTaskList(artifact).filter((t) => t.fixType === "repo-code" && t.mcpTool === "viberaven_heal_apply") : [];
24646
24870
  if (fixable.length === 0) {
24647
24871
  console.log("Nothing to fix automatically. Run `viberaven check` first, then `viberaven fix --gap <id>`.");
24648
24872
  return artifact ? 0 : 1;
24649
24873
  }
24650
24874
  console.log("Fixable gaps:");
24651
24875
  for (const task of fixable) {
24652
- console.log(` viberaven fix --gap ${task.gapId} ${task.title}`);
24876
+ const ask = task.requiresUserAction ? task.action?.match(/^Ask the user first: ([^.]+)\./)?.[1] : void 0;
24877
+ console.log(` viberaven fix --gap ${task.gapId} ${task.title}${ask ? ` (asks first: ${ask})` : ""}`);
24653
24878
  }
24654
24879
  return 0;
24655
24880
  }
24881
+ const dryRun = flags["dry-run"] === true;
24656
24882
  const result = await runHealCommand({
24657
24883
  cwd: fixCwd,
24658
- mode: "apply",
24884
+ mode: dryRun ? "plan" : "apply",
24659
24885
  gapId,
24660
- yes: true,
24886
+ yes: !dryRun,
24661
24887
  noVerify: flags["no-verify"] === true
24662
24888
  });
24663
24889
  console.log(JSON.stringify(result, null, 2));
@@ -24665,19 +24891,19 @@ async function main() {
24665
24891
  }
24666
24892
  case "actions":
24667
24893
  return runActionsCommand({
24668
- cwd: positional[0] ? (0, import_node_path38.resolve)(process.cwd(), positional[0]) : process.cwd(),
24894
+ cwd: positional[0] ? (0, import_node_path40.resolve)(process.cwd(), positional[0]) : process.cwd(),
24669
24895
  json: Boolean(flags.json)
24670
24896
  });
24671
24897
  case "preview":
24672
24898
  return runPreviewCommand({
24673
- cwd: positional[0] ? (0, import_node_path38.resolve)(process.cwd(), positional[0]) : process.cwd(),
24899
+ cwd: positional[0] ? (0, import_node_path40.resolve)(process.cwd(), positional[0]) : process.cwd(),
24674
24900
  agentMode: flags["agent-mode"] === true,
24675
24901
  json: Boolean(flags.json)
24676
24902
  });
24677
24903
  case "next":
24678
24904
  return runNextCommand({
24679
24905
  json: Boolean(flags.json),
24680
- cwd: positional[0] ? (0, import_node_path38.resolve)(process.cwd(), positional[0]) : process.cwd()
24906
+ cwd: positional[0] ? (0, import_node_path40.resolve)(process.cwd(), positional[0]) : process.cwd()
24681
24907
  });
24682
24908
  case "guide": {
24683
24909
  const provider2 = positional[0];
@@ -24711,7 +24937,7 @@ async function main() {
24711
24937
  case "provider-verify":
24712
24938
  return cmdProviderVerify(flags, positional);
24713
24939
  case "init": {
24714
- const cwd = positional[0] ? (0, import_node_path38.resolve)(process.cwd(), positional[0]) : process.cwd();
24940
+ const cwd = positional[0] ? (0, import_node_path40.resolve)(process.cwd(), positional[0]) : process.cwd();
24715
24941
  const agents = typeof flags.agents === "string" ? flags.agents : void 0;
24716
24942
  return runInitCommand({
24717
24943
  cwd,
@@ -24725,7 +24951,7 @@ async function main() {
24725
24951
  return 1;
24726
24952
  }
24727
24953
  return runDoctorAgentsCommand({
24728
- cwd: positional[0] ? (0, import_node_path38.resolve)(process.cwd(), positional[0]) : process.cwd()
24954
+ cwd: positional[0] ? (0, import_node_path40.resolve)(process.cwd(), positional[0]) : process.cwd()
24729
24955
  });
24730
24956
  case "validate-npm-package":
24731
24957
  return runValidateNpmPackageCommand({
@@ -24738,7 +24964,7 @@ async function main() {
24738
24964
  return 1;
24739
24965
  }
24740
24966
  return runAuditCommand({
24741
- cwd: positional[0] ? (0, import_node_path38.resolve)(process.cwd(), positional[0]) : process.cwd(),
24967
+ cwd: positional[0] ? (0, import_node_path40.resolve)(process.cwd(), positional[0]) : process.cwd(),
24742
24968
  json: Boolean(flags.json)
24743
24969
  });
24744
24970
  case "clean": {
@@ -24746,7 +24972,7 @@ async function main() {
24746
24972
  console.error("Usage: viberaven clean --plan [path]");
24747
24973
  return 1;
24748
24974
  }
24749
- const cwd = positional[0] ? (0, import_node_path38.resolve)(process.cwd(), positional[0]) : process.cwd();
24975
+ const cwd = positional[0] ? (0, import_node_path40.resolve)(process.cwd(), positional[0]) : process.cwd();
24750
24976
  const plan = buildContextCleanupPlan({ projectRoot: cwd, files: await collectCleanupFiles(cwd) });
24751
24977
  const outputPath = await writeCleanupPlan(cwd, plan);
24752
24978
  console.log(`Wrote ${outputPath}
@@ -24756,9 +24982,9 @@ async function main() {
24756
24982
  }
24757
24983
  case "rls-test":
24758
24984
  return runRlsTestCommand({
24759
- cwd: positional[0] ? (0, import_node_path38.resolve)(process.cwd(), positional[0]) : process.cwd(),
24760
- matrix: typeof flags.matrix === "string" ? (0, import_node_path38.resolve)(process.cwd(), flags.matrix) : void 0,
24761
- migrations: typeof flags.migrations === "string" ? (0, import_node_path38.resolve)(process.cwd(), flags.migrations) : void 0,
24985
+ cwd: positional[0] ? (0, import_node_path40.resolve)(process.cwd(), positional[0]) : process.cwd(),
24986
+ matrix: typeof flags.matrix === "string" ? (0, import_node_path40.resolve)(process.cwd(), flags.matrix) : void 0,
24987
+ migrations: typeof flags.migrations === "string" ? (0, import_node_path40.resolve)(process.cwd(), flags.migrations) : void 0,
24762
24988
  json: Boolean(flags.json)
24763
24989
  });
24764
24990
  default:
@@ -24767,11 +24993,11 @@ async function main() {
24767
24993
  return 1;
24768
24994
  }
24769
24995
  }
24770
- var import_promises29, import_node_path38, STACK_AREAS;
24996
+ var import_promises30, import_node_path40, STACK_AREAS;
24771
24997
  var init_cli = __esm({
24772
24998
  "src/cli.ts"() {
24773
- import_promises29 = require("node:fs/promises");
24774
- import_node_path38 = require("node:path");
24999
+ import_promises30 = require("node:fs/promises");
25000
+ import_node_path40 = require("node:path");
24775
25001
  init_config();
24776
25002
  init_runScan();
24777
25003
  init_artifacts2();