@viberaven/cli 1.0.4 → 1.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -167,6 +167,7 @@ __export(cli_exports, {
167
167
  parseArgs: () => parseArgs,
168
168
  printHelp: () => printHelp,
169
169
  resolveDefaultEntrypointMode: () => resolveDefaultEntrypointMode,
170
+ resolveLaunchPermissionMode: () => resolveLaunchPermissionMode,
170
171
  runScanCommand: () => runScanCommand
171
172
  });
172
173
  module.exports = __toCommonJS(cli_exports);
@@ -7992,7 +7993,7 @@ async function runProjectScan(options) {
7992
7993
 
7993
7994
  // src/artifacts.ts
7994
7995
  var import_promises5 = require("node:fs/promises");
7995
- var import_node_path8 = require("node:path");
7996
+ var import_node_path9 = require("node:path");
7996
7997
 
7997
7998
  // src/capabilities/classify.ts
7998
7999
  function gapText(gap) {
@@ -8853,7 +8854,11 @@ function buildProviderAction(gap, provider2) {
8853
8854
  envKeyExample: void 0,
8854
8855
  // Use step title as the done signal
8855
8856
  doneSignal: `${step.title} step completed`,
8856
- verifyCommand: PUBLIC_VERIFY_COMMAND
8857
+ verifyCommand: PUBLIC_VERIFY_COMMAND,
8858
+ actionClass: "provider_write",
8859
+ approvalRequired: true,
8860
+ manualFallback: `Open ${step.openUrl ?? `https://${provider2}.com`} and complete: ${step.instruction}`,
8861
+ copyValues: []
8857
8862
  };
8858
8863
  } catch {
8859
8864
  return void 0;
@@ -9407,35 +9412,458 @@ function sanitizeArtifactForDisk(artifact) {
9407
9412
  return redactUnknown(artifact);
9408
9413
  }
9409
9414
 
9415
+ // src/providerMcpBridge.ts
9416
+ var import_node_fs7 = require("node:fs");
9417
+ var import_node_os2 = require("node:os");
9418
+ var import_node_path8 = require("node:path");
9419
+ var UPGRADE_URL4 = "https://viberaven.dev/pricing";
9420
+ var FALLBACK_COMMAND = "npx -y viberaven audit --vercel-supabase --json";
9421
+ var SUPPORTED_PROVIDERS = /* @__PURE__ */ new Set(["supabase", "vercel"]);
9422
+ var configPathsOverride;
9423
+ function defaultMcpConfigPaths() {
9424
+ const home = (0, import_node_os2.homedir)();
9425
+ return [
9426
+ (0, import_node_path8.join)(home, ".config", "claude", "claude_desktop_config.json"),
9427
+ (0, import_node_path8.join)(home, ".cursor", "mcp.json"),
9428
+ (0, import_node_path8.join)(home, ".gemini", "antigravity", "mcp_config.json")
9429
+ ];
9430
+ }
9431
+ function resolveConfigPaths() {
9432
+ return configPathsOverride ?? defaultMcpConfigPaths();
9433
+ }
9434
+ function parseMcpServers(raw) {
9435
+ if (!raw || typeof raw !== "object") {
9436
+ return void 0;
9437
+ }
9438
+ const obj = raw;
9439
+ if (obj.mcpServers && typeof obj.mcpServers === "object") {
9440
+ return obj.mcpServers;
9441
+ }
9442
+ if (obj.servers && typeof obj.servers === "object") {
9443
+ return obj.servers;
9444
+ }
9445
+ return void 0;
9446
+ }
9447
+ function findServerEntry(servers, provider2) {
9448
+ if (servers[provider2]) {
9449
+ return servers[provider2];
9450
+ }
9451
+ const key = Object.keys(servers).find((candidate) => candidate.toLowerCase() === provider2);
9452
+ return key ? servers[key] : void 0;
9453
+ }
9454
+ function findProviderMcpConfig(provider2, configPaths) {
9455
+ const paths = configPaths ?? resolveConfigPaths();
9456
+ for (const path of paths) {
9457
+ if (!(0, import_node_fs7.existsSync)(path)) {
9458
+ continue;
9459
+ }
9460
+ try {
9461
+ const raw = JSON.parse((0, import_node_fs7.readFileSync)(path, "utf8"));
9462
+ const servers = parseMcpServers(raw);
9463
+ if (!servers) {
9464
+ continue;
9465
+ }
9466
+ const entry = findServerEntry(servers, provider2);
9467
+ if (!entry || typeof entry !== "object") {
9468
+ continue;
9469
+ }
9470
+ const server = entry;
9471
+ return {
9472
+ command: typeof server.command === "string" ? server.command : void 0,
9473
+ args: Array.isArray(server.args) ? server.args.filter((arg) => typeof arg === "string") : void 0,
9474
+ url: typeof server.url === "string" ? server.url : void 0,
9475
+ source: path
9476
+ };
9477
+ } catch {
9478
+ continue;
9479
+ }
9480
+ }
9481
+ return void 0;
9482
+ }
9483
+ async function verifyProviderGap(options) {
9484
+ if (options.plan !== "pro") {
9485
+ return {
9486
+ verified: false,
9487
+ reason: "pro-required",
9488
+ upgradeUrl: UPGRADE_URL4
9489
+ };
9490
+ }
9491
+ const provider2 = options.provider.toLowerCase().trim();
9492
+ if (!SUPPORTED_PROVIDERS.has(provider2)) {
9493
+ return {
9494
+ verified: false,
9495
+ reason: "unsupported-provider"
9496
+ };
9497
+ }
9498
+ const mcpConfig = findProviderMcpConfig(provider2);
9499
+ if (!mcpConfig) {
9500
+ return {
9501
+ verified: false,
9502
+ mcpUnavailable: true,
9503
+ fallbackCommand: FALLBACK_COMMAND
9504
+ };
9505
+ }
9506
+ return {
9507
+ verified: false,
9508
+ mcpUnavailable: true,
9509
+ fallbackCommand: FALLBACK_COMMAND
9510
+ };
9511
+ }
9512
+ function defaultProviderDashboardUrl(provider2) {
9513
+ const normalized = provider2.toLowerCase();
9514
+ if (normalized === "vercel") return "https://vercel.com/dashboard";
9515
+ if (normalized === "supabase") return "https://supabase.com/dashboard/projects";
9516
+ if (normalized === "stripe") return "https://dashboard.stripe.com/webhooks";
9517
+ if (normalized === "posthog") return "https://us.posthog.com/project/settings";
9518
+ return void 0;
9519
+ }
9520
+
9521
+ // src/launch/providerAutomation.ts
9522
+ function buildProviderAutomationOptions(options) {
9523
+ const provider2 = options.provider.toLowerCase();
9524
+ const result = [];
9525
+ if (options.mcpConfig) {
9526
+ result.push({
9527
+ kind: "mcp",
9528
+ provider: provider2,
9529
+ available: true,
9530
+ label: `${provider2} MCP configured`,
9531
+ source: options.mcpConfig.source,
9532
+ openUrl: options.mcpConfig.url
9533
+ });
9534
+ }
9535
+ if (options.cli?.available) {
9536
+ result.push({
9537
+ kind: "cli",
9538
+ provider: provider2,
9539
+ available: true,
9540
+ label: options.cli.authenticated ? `${options.cli.command} CLI authenticated` : `${options.cli.command} CLI needs login`,
9541
+ command: options.cli.authenticated ? options.cli.command : `${options.cli.command} login`,
9542
+ reason: options.cli.detail
9543
+ });
9544
+ }
9545
+ const dashboardUrl = defaultProviderDashboardUrl(provider2);
9546
+ if (dashboardUrl) {
9547
+ result.push({
9548
+ kind: "dashboard",
9549
+ provider: provider2,
9550
+ available: true,
9551
+ label: `Open ${provider2} dashboard`,
9552
+ openUrl: dashboardUrl
9553
+ });
9554
+ }
9555
+ result.push({
9556
+ kind: "manual",
9557
+ provider: provider2,
9558
+ available: true,
9559
+ label: `Show manual ${provider2} setup steps`,
9560
+ reason: "Manual fallback is always available"
9561
+ });
9562
+ return result;
9563
+ }
9564
+
9565
+ // src/launch/providerActions.ts
9566
+ var VERIFY_COMMAND = "npx -y viberaven --verify";
9567
+ function buildLaunchProviderActions(options) {
9568
+ const actions = [];
9569
+ const providers = new Set(options.recipe.providers);
9570
+ if (providers.has("vercel")) {
9571
+ actions.push({
9572
+ provider: "vercel",
9573
+ actionClass: "preview_deploy",
9574
+ title: "Create a Vercel preview URL",
9575
+ detail: "Run a preview deploy so the vibe coder can see the app live before production promotion.",
9576
+ openUrl: "https://vercel.com/new",
9577
+ command: "vercel deploy",
9578
+ copyValues: [],
9579
+ verifyCommand: VERIFY_COMMAND,
9580
+ approvalRequired: true,
9581
+ manualFallback: "Open Vercel, import or link this project, then create a preview deployment.",
9582
+ automation: buildProviderAutomationOptions({ provider: "vercel" })
9583
+ });
9584
+ }
9585
+ if (providers.has("supabase")) {
9586
+ actions.push({
9587
+ provider: "supabase",
9588
+ actionClass: "provider_write",
9589
+ title: "Connect Supabase project and env values",
9590
+ detail: "Connect the production Supabase project, then verify env wiring, migrations, auth, and RLS evidence.",
9591
+ openUrl: defaultProviderDashboardUrl("supabase"),
9592
+ copyValues: [
9593
+ { label: "Public URL env name", value: "NEXT_PUBLIC_SUPABASE_URL", secret: false },
9594
+ { label: "Anon key env name", value: "NEXT_PUBLIC_SUPABASE_ANON_KEY", secret: false }
9595
+ ],
9596
+ verifyCommand: VERIFY_COMMAND,
9597
+ approvalRequired: true,
9598
+ manualFallback: "Open Supabase project settings, copy the project URL and anon key, then add them to Vercel preview and production env vars.",
9599
+ automation: buildProviderAutomationOptions({ provider: "supabase" })
9600
+ });
9601
+ }
9602
+ if (providers.has("stripe")) {
9603
+ const baseUrl = options.productionDomain ?? "https://<production-domain>";
9604
+ actions.push({
9605
+ provider: "stripe",
9606
+ actionClass: "provider_write",
9607
+ title: "Finish Stripe products and webhook setup",
9608
+ detail: "Configure Stripe products/prices and add the production webhook endpoint with required billing events.",
9609
+ openUrl: defaultProviderDashboardUrl("stripe"),
9610
+ copyValues: [
9611
+ { label: "Webhook endpoint", value: `${baseUrl}/api/stripe/webhook`, secret: false },
9612
+ { label: "Required event", value: "checkout.session.completed", secret: false },
9613
+ { label: "Required event", value: "customer.subscription.updated", secret: false },
9614
+ { label: "Required event", value: "customer.subscription.deleted", secret: false },
9615
+ { label: "Required event", value: "invoice.payment_failed", secret: false },
9616
+ { label: "Webhook secret env name", value: "STRIPE_WEBHOOK_SECRET", secret: false }
9617
+ ],
9618
+ verifyCommand: VERIFY_COMMAND,
9619
+ approvalRequired: true,
9620
+ manualFallback: "Open Stripe Webhooks, add the endpoint URL, select the listed events, then store the signing secret as STRIPE_WEBHOOK_SECRET.",
9621
+ automation: buildProviderAutomationOptions({ provider: "stripe" })
9622
+ });
9623
+ }
9624
+ return actions;
9625
+ }
9626
+
9627
+ // src/launch/recipes.ts
9628
+ function textIncludes(text, token) {
9629
+ return text.toLowerCase().includes(token.toLowerCase());
9630
+ }
9631
+ function selectedProviderSet(artifact) {
9632
+ return new Set(Object.values(artifact.selectedProviders ?? {}).map((value) => value.toLowerCase()));
9633
+ }
9634
+ function hasProvider(artifact, provider2) {
9635
+ const selected = selectedProviderSet(artifact);
9636
+ return selected.has(provider2);
9637
+ }
9638
+ function isNextLike(artifact) {
9639
+ const text = [artifact.archetype, artifact.summary].join(" ");
9640
+ return textIncludes(text, "next") || textIncludes(text, "nextjs") || textIncludes(text, "next.js");
9641
+ }
9642
+ function detectLaunchRecipe(artifact) {
9643
+ const next = isNextLike(artifact);
9644
+ const vercel = hasProvider(artifact, "vercel");
9645
+ const supabase = hasProvider(artifact, "supabase");
9646
+ const stripe = hasProvider(artifact, "stripe");
9647
+ if (next && supabase && stripe && vercel) {
9648
+ return {
9649
+ id: "nextjs-supabase-stripe-vercel",
9650
+ label: "Next.js + Supabase + Stripe + Vercel SaaS",
9651
+ confidence: "high",
9652
+ goal: "preview-first",
9653
+ providers: ["vercel", "supabase", "stripe"],
9654
+ reasons: ["Next.js/Vercel evidence detected", "Supabase evidence detected", "Stripe billing evidence detected"]
9655
+ };
9656
+ }
9657
+ if (next && supabase && vercel) {
9658
+ return {
9659
+ id: "nextjs-supabase-vercel",
9660
+ label: "Next.js + Supabase + Vercel app",
9661
+ confidence: "high",
9662
+ goal: "preview-first",
9663
+ providers: ["vercel", "supabase"],
9664
+ reasons: ["Next.js/Vercel evidence detected", "Supabase evidence detected"]
9665
+ };
9666
+ }
9667
+ if (next && vercel) {
9668
+ return {
9669
+ id: "nextjs-vercel",
9670
+ label: "Next.js + Vercel app",
9671
+ confidence: "medium",
9672
+ goal: "preview-first",
9673
+ providers: ["vercel"],
9674
+ reasons: ["Next.js/Vercel evidence detected"]
9675
+ };
9676
+ }
9677
+ return {
9678
+ id: "generic-preview-first",
9679
+ label: "Generic preview-first launch",
9680
+ confidence: "low",
9681
+ goal: "preview-first",
9682
+ providers: vercel ? ["vercel"] : [],
9683
+ reasons: ["Provider evidence is incomplete, so VibeRaven will use a conservative preview-first path"]
9684
+ };
9685
+ }
9686
+
9687
+ // src/launch/buildLaunchPlan.ts
9688
+ function isSafeRepoTask(task) {
9689
+ return task.fixType === "repo-code" && task.requiresUserAction === false;
9690
+ }
9691
+ function providerActionFromTask(task) {
9692
+ if (task.fixType !== "provider-action" || !task.providerAction) {
9693
+ return void 0;
9694
+ }
9695
+ const providerAction = task.providerAction;
9696
+ return {
9697
+ provider: providerAction.provider,
9698
+ actionClass: providerAction.actionClass,
9699
+ title: task.title,
9700
+ detail: providerAction.exactStep,
9701
+ openUrl: providerAction.dashboardUrl,
9702
+ copyValues: providerAction.copyValues,
9703
+ verifyCommand: providerAction.verifyCommand,
9704
+ approvalRequired: providerAction.approvalRequired,
9705
+ manualFallback: providerAction.manualFallback,
9706
+ automation: buildProviderAutomationOptions({ provider: providerAction.provider })
9707
+ };
9708
+ }
9709
+ function mergeTaskProviderActions(recipeActions, tasks) {
9710
+ const actions = [...recipeActions];
9711
+ for (const task of tasks) {
9712
+ const taskAction = providerActionFromTask(task);
9713
+ if (!taskAction) continue;
9714
+ const alreadyCovered = actions.some(
9715
+ (action) => action.provider === taskAction.provider && action.actionClass === taskAction.actionClass
9716
+ );
9717
+ if (!alreadyCovered) {
9718
+ actions.push(taskAction);
9719
+ }
9720
+ }
9721
+ return actions;
9722
+ }
9723
+ function selectNextAction(options) {
9724
+ const safeRepoTask = options.safeRepoTasks[0];
9725
+ if (safeRepoTask) {
9726
+ return {
9727
+ type: "safe_repo_gap",
9728
+ title: safeRepoTask.title,
9729
+ detail: safeRepoTask.exactFix ?? "Apply the supported VibeRaven safe repo fix, then verify after the batch.",
9730
+ actionClass: "local_repo_write",
9731
+ gapId: safeRepoTask.gapId,
9732
+ approvalRequired: false,
9733
+ manualFallback: "Use VIBERAVEN_NEXT_ACTION or viberaven_heal_apply for the supported safe repo fix, then run verify after the batch."
9734
+ };
9735
+ }
9736
+ const preview = options.providerActions.find((action) => action.actionClass === "preview_deploy");
9737
+ if (preview) {
9738
+ return {
9739
+ type: "preview_deploy",
9740
+ title: preview.title,
9741
+ detail: preview.detail,
9742
+ actionClass: "preview_deploy",
9743
+ provider: preview.provider,
9744
+ approvalRequired: preview.approvalRequired,
9745
+ openUrl: preview.openUrl,
9746
+ command: preview.command,
9747
+ manualFallback: preview.manualFallback
9748
+ };
9749
+ }
9750
+ const provider2 = options.providerActions[0];
9751
+ if (provider2) {
9752
+ return {
9753
+ type: "provider_setup",
9754
+ title: provider2.title,
9755
+ detail: provider2.detail,
9756
+ actionClass: provider2.actionClass,
9757
+ provider: provider2.provider,
9758
+ approvalRequired: provider2.approvalRequired,
9759
+ openUrl: provider2.openUrl,
9760
+ command: provider2.command,
9761
+ manualFallback: provider2.manualFallback
9762
+ };
9763
+ }
9764
+ return {
9765
+ type: "done",
9766
+ title: "Launch path is clear",
9767
+ detail: "No launch action is currently required. Run verify before claiming production readiness.",
9768
+ actionClass: "local_repo_read",
9769
+ approvalRequired: false
9770
+ };
9771
+ }
9772
+ function buildLaunchPlan(options) {
9773
+ const permissionMode = options.permissionMode ?? "ask";
9774
+ const recipe = detectLaunchRecipe(options.artifact);
9775
+ const tasks = options.tasks ?? buildTaskList(options.artifact);
9776
+ const safeRepoTasks = tasks.filter(isSafeRepoTask);
9777
+ const providerActions = mergeTaskProviderActions(buildLaunchProviderActions({ recipe }), tasks);
9778
+ const criticalGaps = options.artifact.gaps.filter((gap) => gap.severity === "critical");
9779
+ return {
9780
+ version: 1,
9781
+ generatedAt: (options.now ?? /* @__PURE__ */ new Date()).toISOString(),
9782
+ permissionMode,
9783
+ recipe,
9784
+ usageLine: options.artifact.usageLine,
9785
+ safeRepoTasks,
9786
+ providerActions,
9787
+ nextAction: selectNextAction({ safeRepoTasks, providerActions }),
9788
+ previewFirst: true,
9789
+ productionGate: {
9790
+ status: criticalGaps.length > 0 ? "blocked" : "unknown",
9791
+ reasons: criticalGaps.length > 0 ? ["Critical launch gaps remain"] : ["Preview deploy should be verified before production promotion"]
9792
+ }
9793
+ };
9794
+ }
9795
+
9410
9796
  // src/artifacts.ts
9411
9797
  async function copyReportAssets(reportAssetsDir) {
9412
9798
  const sourceDir = getBundledReportAssetsDir();
9413
- await (0, import_promises5.mkdir)((0, import_node_path8.join)(reportAssetsDir, "assets"), { recursive: true });
9799
+ await (0, import_promises5.mkdir)((0, import_node_path9.join)(reportAssetsDir, "assets"), { recursive: true });
9414
9800
  for (const rel of REPORT_ASSET_FILES) {
9415
- await (0, import_promises5.copyFile)((0, import_node_path8.join)(sourceDir, rel), (0, import_node_path8.join)(reportAssetsDir, rel));
9801
+ await (0, import_promises5.copyFile)((0, import_node_path9.join)(sourceDir, rel), (0, import_node_path9.join)(reportAssetsDir, rel));
9416
9802
  }
9417
9803
  }
9804
+ function renderLaunchTasklist(plan) {
9805
+ const lines = [
9806
+ "# VibeRaven Launch Autopilot",
9807
+ "",
9808
+ `Recipe: ${plan.recipe.label}`,
9809
+ `Goal: preview URL first, production gate second`,
9810
+ `Mode: ${plan.permissionMode}`,
9811
+ "",
9812
+ `Safe repo tasks: ${plan.safeRepoTasks.length}`,
9813
+ `Provider actions: ${plan.providerActions.length}`,
9814
+ "",
9815
+ "## Best Next Move",
9816
+ "",
9817
+ `**Type:** ${plan.nextAction.type}`,
9818
+ `**Title:** ${plan.nextAction.title}`,
9819
+ `**Detail:** ${plan.nextAction.detail}`,
9820
+ `**Approval required:** ${plan.nextAction.approvalRequired}`
9821
+ ];
9822
+ if (plan.nextAction.openUrl) lines.push(`**Open:** ${plan.nextAction.openUrl}`);
9823
+ if (plan.nextAction.command) lines.push(`**Command:** \`${plan.nextAction.command}\``);
9824
+ if (plan.nextAction.manualFallback) lines.push(`**Manual fallback:** ${plan.nextAction.manualFallback}`);
9825
+ lines.push("", "## Production Gate", "");
9826
+ lines.push(`Status: ${plan.productionGate.status}`);
9827
+ for (const reason of plan.productionGate.reasons) {
9828
+ lines.push(`- ${reason}`);
9829
+ }
9830
+ return `${lines.join("\n")}
9831
+ `;
9832
+ }
9418
9833
  async function writeScanArtifacts(options) {
9419
9834
  const cwd = options.cwd ?? options.artifact.workspacePath;
9420
9835
  const dir = getProjectArtifactsDir(cwd);
9421
9836
  await (0, import_promises5.mkdir)(dir, { recursive: true });
9422
- const jsonPath = (0, import_node_path8.join)(dir, "last-scan.json");
9423
- const gateResultPath = (0, import_node_path8.join)(dir, "gate-result.json");
9424
- const contextMapPath = (0, import_node_path8.join)(dir, "context-map.json");
9425
- const gapsDir = (0, import_node_path8.join)(dir, "gaps");
9426
- const tasklistPath = (0, import_node_path8.join)(dir, "agent-tasklist.md");
9427
- const summaryPath = (0, import_node_path8.join)(dir, "agent-summary.md");
9428
- const playbookPath = (0, import_node_path8.join)(dir, "launch-playbook.md");
9429
- const reportPath = (0, import_node_path8.join)(dir, "report.html");
9430
- const reportAssetsDir = (0, import_node_path8.join)(dir, "report");
9837
+ const jsonPath = (0, import_node_path9.join)(dir, "last-scan.json");
9838
+ const gateResultPath = (0, import_node_path9.join)(dir, "gate-result.json");
9839
+ const contextMapPath = (0, import_node_path9.join)(dir, "context-map.json");
9840
+ const gapsDir = (0, import_node_path9.join)(dir, "gaps");
9841
+ const tasklistPath = (0, import_node_path9.join)(dir, "agent-tasklist.md");
9842
+ const summaryPath = (0, import_node_path9.join)(dir, "agent-summary.md");
9843
+ const playbookPath = (0, import_node_path9.join)(dir, "launch-playbook.md");
9844
+ const launchPlanPath = (0, import_node_path9.join)(dir, "launch-plan.json");
9845
+ const launchTasklistPath = (0, import_node_path9.join)(dir, "launch-tasklist.md");
9846
+ const providerActionsPath = (0, import_node_path9.join)(dir, "provider-actions.json");
9847
+ const reportPath = (0, import_node_path9.join)(dir, "report.html");
9848
+ const reportAssetsDir = (0, import_node_path9.join)(dir, "report");
9431
9849
  await (0, import_promises5.mkdir)(gapsDir, { recursive: true });
9432
9850
  const safe = sanitizeArtifactForDisk(options.artifact);
9851
+ const tasks = buildTaskList(safe);
9852
+ const launchPlan = buildLaunchPlan({
9853
+ artifact: safe,
9854
+ tasks,
9855
+ permissionMode: options.launchPermissionMode
9856
+ });
9857
+ const launchPlanJson = `${JSON.stringify(launchPlan, null, 2)}
9858
+ `;
9859
+ const launchTasklist = renderLaunchTasklist(launchPlan);
9860
+ const providerActionsJson = `${JSON.stringify({ providerActions: launchPlan.providerActions }, null, 2)}
9861
+ `;
9433
9862
  const json = `${JSON.stringify(safe, null, 2)}
9434
9863
  `;
9435
9864
  const summary = generateAgentSummary(safe);
9436
9865
  const playbook = generateLaunchPlaybook(safe);
9437
9866
  const html = generateReportHtml(safe);
9438
- const tasks = buildTaskList(safe);
9439
9867
  const tasklist = buildTaskListMarkdown(tasks);
9440
9868
  const gateResult = `${JSON.stringify(generateGateResult(safe), null, 2)}
9441
9869
  `;
@@ -9446,9 +9874,12 @@ async function writeScanArtifacts(options) {
9446
9874
  await (0, import_promises5.writeFile)(gateResultPath, gateResult, "utf-8");
9447
9875
  await (0, import_promises5.writeFile)(contextMapPath, contextMap, "utf-8");
9448
9876
  for (const gap of gapEvidenceFiles) {
9449
- await (0, import_promises5.writeFile)((0, import_node_path8.join)(dir, "gaps", `${gap.content.id}.json`), `${JSON.stringify(gap.content, null, 2)}
9877
+ await (0, import_promises5.writeFile)((0, import_node_path9.join)(dir, "gaps", `${gap.content.id}.json`), `${JSON.stringify(gap.content, null, 2)}
9450
9878
  `, "utf-8");
9451
9879
  }
9880
+ await (0, import_promises5.writeFile)(launchPlanPath, launchPlanJson, "utf-8");
9881
+ await (0, import_promises5.writeFile)(launchTasklistPath, launchTasklist, "utf-8");
9882
+ await (0, import_promises5.writeFile)(providerActionsPath, providerActionsJson, "utf-8");
9452
9883
  await (0, import_promises5.writeFile)(tasklistPath, tasklist, "utf-8");
9453
9884
  await (0, import_promises5.writeFile)(jsonPath, json, "utf-8");
9454
9885
  await (0, import_promises5.writeFile)(summaryPath, summary, "utf-8");
@@ -9463,8 +9894,13 @@ async function writeScanArtifacts(options) {
9463
9894
  tasklistPath,
9464
9895
  summaryPath,
9465
9896
  playbookPath,
9897
+ launchPlanPath,
9898
+ launchTasklistPath,
9899
+ providerActionsPath,
9466
9900
  reportPath,
9467
- reportAssetsDir
9901
+ reportAssetsDir,
9902
+ tasks,
9903
+ launchPlan
9468
9904
  };
9469
9905
  }
9470
9906
 
@@ -9705,7 +10141,7 @@ function printScanSummary(artifact, paths) {
9705
10141
 
9706
10142
  // src/runnerConnect.ts
9707
10143
  var import_promises6 = require("node:fs/promises");
9708
- var import_node_path9 = require("node:path");
10144
+ var import_node_path10 = require("node:path");
9709
10145
  var import_node_child_process3 = require("node:child_process");
9710
10146
  var import_node_crypto = require("node:crypto");
9711
10147
 
@@ -10053,7 +10489,7 @@ async function createSafeFixFile(job, input, targetPath) {
10053
10489
  } catch {
10054
10490
  }
10055
10491
  try {
10056
- await (0, import_promises6.mkdir)((0, import_node_path9.dirname)(targetPath), { recursive: true });
10492
+ await (0, import_promises6.mkdir)((0, import_node_path10.dirname)(targetPath), { recursive: true });
10057
10493
  await writeNewFileAtomically(targetPath, input.content);
10058
10494
  } catch {
10059
10495
  return safeFixNeedsUser(job, "SAFE_FIX_WRITE_FAILED", `Could not create ${input.path}.`);
@@ -10121,12 +10557,12 @@ function safeFixNeedsUser(job, code, message) {
10121
10557
  };
10122
10558
  }
10123
10559
  async function resolveSafeFixTarget(workspaceRoot, relativePath) {
10124
- const root = await (0, import_promises6.realpath)(workspaceRoot).catch(() => (0, import_node_path9.resolve)(workspaceRoot));
10125
- const target = (0, import_node_path9.resolve)(root, relativePath);
10560
+ const root = await (0, import_promises6.realpath)(workspaceRoot).catch(() => (0, import_node_path10.resolve)(workspaceRoot));
10561
+ const target = (0, import_node_path10.resolve)(root, relativePath);
10126
10562
  if (!isInsideRoot(root, target)) {
10127
10563
  return { ok: false, reason: "Safe fix target escaped the workspace root." };
10128
10564
  }
10129
- const parent = (0, import_node_path9.dirname)(target);
10565
+ const parent = (0, import_node_path10.dirname)(target);
10130
10566
  const parentReal = await realpathNearestExisting(parent, root);
10131
10567
  if (!isInsideRoot(root, parentReal)) {
10132
10568
  return { ok: false, reason: "Safe fix parent path escaped the workspace root." };
@@ -10144,7 +10580,7 @@ async function realpathNearestExisting(path, root) {
10144
10580
  await (0, import_promises6.lstat)(candidate);
10145
10581
  return (0, import_promises6.realpath)(candidate);
10146
10582
  } catch {
10147
- const next = (0, import_node_path9.dirname)(candidate);
10583
+ const next = (0, import_node_path10.dirname)(candidate);
10148
10584
  if (next === candidate) {
10149
10585
  break;
10150
10586
  }
@@ -10154,8 +10590,8 @@ async function realpathNearestExisting(path, root) {
10154
10590
  return root;
10155
10591
  }
10156
10592
  function isInsideRoot(root, candidate) {
10157
- const rel = (0, import_node_path9.relative)(root, candidate);
10158
- return rel === "" || !rel.startsWith("..") && !(0, import_node_path9.isAbsolute)(rel);
10593
+ const rel = (0, import_node_path10.relative)(root, candidate);
10594
+ return rel === "" || !rel.startsWith("..") && !(0, import_node_path10.isAbsolute)(rel);
10159
10595
  }
10160
10596
  async function writeNewFileAtomically(targetPath, content) {
10161
10597
  const tempPath = tempPathFor(targetPath);
@@ -10176,7 +10612,7 @@ async function replaceFileAtomically(targetPath, content) {
10176
10612
  }
10177
10613
  }
10178
10614
  function tempPathFor(targetPath) {
10179
- return (0, import_node_path9.join)((0, import_node_path9.dirname)(targetPath), `.${(0, import_node_path9.basename)(targetPath)}.${(0, import_node_crypto.randomUUID)()}.tmp`);
10615
+ return (0, import_node_path10.join)((0, import_node_path10.dirname)(targetPath), `.${(0, import_node_path10.basename)(targetPath)}.${(0, import_node_crypto.randomUUID)()}.tmp`);
10180
10616
  }
10181
10617
  async function executePackageScriptJob(job, scriptName, options) {
10182
10618
  const packageJson = await readPackageJson(options.workspaceRoot);
@@ -10350,7 +10786,7 @@ function proofItemForJob(job, kind, label, summary, evidence, redactionSecrets =
10350
10786
  }
10351
10787
  async function readPackageJson(workspaceRoot) {
10352
10788
  try {
10353
- const raw = await (0, import_promises6.readFile)((0, import_node_path9.join)(workspaceRoot, "package.json"), "utf-8");
10789
+ const raw = await (0, import_promises6.readFile)((0, import_node_path10.join)(workspaceRoot, "package.json"), "utf-8");
10354
10790
  const parsed = JSON.parse(raw);
10355
10791
  if (isRecord6(parsed) && isRecord6(parsed.scripts)) {
10356
10792
  return { scripts: Object.fromEntries(Object.entries(parsed.scripts).filter(([, value]) => typeof value === "string")) };
@@ -10433,7 +10869,7 @@ async function collectLocalRepoMetadata(workspaceRoot, commandRunner = runComman
10433
10869
  const headSha = headResult.ok ? normalizeSha(headResult.stdout) : null;
10434
10870
  const dirty = statusResult.ok ? statusResult.stdout.trim().length > 0 : void 0;
10435
10871
  return {
10436
- rootName: (0, import_node_path9.basename)(workspaceRoot) || "workspace",
10872
+ rootName: (0, import_node_path10.basename)(workspaceRoot) || "workspace",
10437
10873
  remotes: remoteResult.ok ? parseGitRemotes(remoteResult.stdout) : [],
10438
10874
  branch,
10439
10875
  headSha,
@@ -10451,7 +10887,7 @@ async function detectPackageManager(workspaceRoot) {
10451
10887
  ];
10452
10888
  for (const [manager, file] of checks) {
10453
10889
  try {
10454
- await (0, import_promises6.access)((0, import_node_path9.join)(workspaceRoot, file));
10890
+ await (0, import_promises6.access)((0, import_node_path10.join)(workspaceRoot, file));
10455
10891
  return manager;
10456
10892
  } catch {
10457
10893
  }
@@ -10642,7 +11078,7 @@ function isRecord6(value) {
10642
11078
  }
10643
11079
 
10644
11080
  // src/tui/runInteractive.ts
10645
- var import_node_path14 = require("node:path");
11081
+ var import_node_path15 = require("node:path");
10646
11082
 
10647
11083
  // ../../node_modules/@clack/core/dist/index.mjs
10648
11084
  var import_sisteransi = __toESM(require_src(), 1);
@@ -11229,7 +11665,7 @@ function buildAgentFixPrompt(artifact, gap) {
11229
11665
  }
11230
11666
 
11231
11667
  // src/version.ts
11232
- var VERSION = "1.0.3";
11668
+ var VERSION = "1.0.6";
11233
11669
 
11234
11670
  // src/commands/guide.ts
11235
11671
  var import_picocolors3 = __toESM(require_picocolors());
@@ -11299,7 +11735,7 @@ async function runGuideCommand(options) {
11299
11735
 
11300
11736
  // src/commands/audit.ts
11301
11737
  var import_promises7 = require("node:fs/promises");
11302
- var import_node_path10 = require("node:path");
11738
+ var import_node_path11 = require("node:path");
11303
11739
  var ENV_FILES = [
11304
11740
  ".env",
11305
11741
  ".env.local",
@@ -11399,7 +11835,7 @@ function buildVercelSupabaseAudit(input) {
11399
11835
  }
11400
11836
  async function readIfExists(projectRoot, relativePath) {
11401
11837
  try {
11402
- const absolutePath = (0, import_node_path10.join)(projectRoot, relativePath);
11838
+ const absolutePath = (0, import_node_path11.join)(projectRoot, relativePath);
11403
11839
  const fileStat = await (0, import_promises7.stat)(absolutePath);
11404
11840
  if (!fileStat.isFile()) {
11405
11841
  return void 0;
@@ -11413,7 +11849,7 @@ async function readIfExists(projectRoot, relativePath) {
11413
11849
  }
11414
11850
  }
11415
11851
  async function collectSqlFiles(projectRoot, root) {
11416
- const base = (0, import_node_path10.join)(projectRoot, root);
11852
+ const base = (0, import_node_path11.join)(projectRoot, root);
11417
11853
  try {
11418
11854
  const rootStat = await (0, import_promises7.stat)(base);
11419
11855
  if (!rootStat.isDirectory()) {
@@ -11433,17 +11869,17 @@ async function collectSqlFiles(projectRoot, root) {
11433
11869
  for (const entry of entries) {
11434
11870
  if (entry.isDirectory()) {
11435
11871
  if (!SKIP_DIRS.has(entry.name)) {
11436
- await visit((0, import_node_path10.join)(dir, entry.name));
11872
+ await visit((0, import_node_path11.join)(dir, entry.name));
11437
11873
  }
11438
11874
  continue;
11439
11875
  }
11440
11876
  if (!entry.isFile() || !entry.name.toLowerCase().endsWith(".sql")) {
11441
11877
  continue;
11442
11878
  }
11443
- const absolutePath = (0, import_node_path10.join)(dir, entry.name);
11879
+ const absolutePath = (0, import_node_path11.join)(dir, entry.name);
11444
11880
  try {
11445
11881
  files.push({
11446
- path: (0, import_node_path10.relative)(projectRoot, absolutePath).replace(/\\/g, "/"),
11882
+ path: (0, import_node_path11.relative)(projectRoot, absolutePath).replace(/\\/g, "/"),
11447
11883
  content: await (0, import_promises7.readFile)(absolutePath, "utf8")
11448
11884
  });
11449
11885
  } catch {
@@ -11493,7 +11929,7 @@ function renderVercelSupabaseAudit(result) {
11493
11929
 
11494
11930
  // src/commands/initRules.ts
11495
11931
  var import_promises10 = require("node:fs/promises");
11496
- var import_node_path13 = require("node:path");
11932
+ var import_node_path14 = require("node:path");
11497
11933
 
11498
11934
  // src/commands/agentRulesBlock.ts
11499
11935
  var VIBERAVEN_BLOCK_START = "<!-- VIBERAVEN:START -->";
@@ -11731,7 +12167,7 @@ function escapeRegExp3(value) {
11731
12167
 
11732
12168
  // src/commands/cursorRulesPack.ts
11733
12169
  var import_promises8 = require("node:fs/promises");
11734
- var import_node_path11 = require("node:path");
12170
+ var import_node_path12 = require("node:path");
11735
12171
  var CURSOR_RULES_DIR = ".cursor/rules";
11736
12172
  var LEGACY_CURSOR_RULE_FILE = `${CURSOR_RULES_DIR}/viberaven.mdc`;
11737
12173
  var DOMAIN_PATH_POINTER = "Before editing these files, read `.viberaven/agent-context.md` and `.viberaven/mission-map.md`.";
@@ -11815,17 +12251,17 @@ async function initCursorRulesPack(options) {
11815
12251
  const pack = buildCursorRulesPack();
11816
12252
  for (const rule of pack) {
11817
12253
  const file = `${CURSOR_RULES_DIR}/${rule.filename}`;
11818
- const path = (0, import_node_path11.join)(options.cwd, file);
12254
+ const path = (0, import_node_path12.join)(options.cwd, file);
11819
12255
  const existing = await readExistingFile(path);
11820
12256
  const changed = !existing.exists || existing.content !== rule.content;
11821
12257
  const action = !existing.exists ? "created" : changed ? "updated" : "unchanged";
11822
12258
  if (!options.dryRun && changed) {
11823
- await (0, import_promises8.mkdir)((0, import_node_path11.join)(options.cwd, CURSOR_RULES_DIR), { recursive: true });
12259
+ await (0, import_promises8.mkdir)((0, import_node_path12.join)(options.cwd, CURSOR_RULES_DIR), { recursive: true });
11824
12260
  await (0, import_promises8.writeFile)(path, rule.content, "utf-8");
11825
12261
  }
11826
12262
  results.push({ target: "cursor", file, path, action });
11827
12263
  }
11828
- const legacyPath = (0, import_node_path11.join)(options.cwd, LEGACY_CURSOR_RULE_FILE);
12264
+ const legacyPath = (0, import_node_path12.join)(options.cwd, LEGACY_CURSOR_RULE_FILE);
11829
12265
  if (!options.dryRun && await fileExists(legacyPath)) {
11830
12266
  await (0, import_promises8.rm)(legacyPath, { force: true });
11831
12267
  }
@@ -11936,17 +12372,17 @@ function getAgentRulesTargets(value) {
11936
12372
  }
11937
12373
 
11938
12374
  // src/commands/seedPackageJsonScripts.ts
11939
- var import_node_fs7 = require("node:fs");
12375
+ var import_node_fs8 = require("node:fs");
11940
12376
  var import_promises9 = require("node:fs/promises");
11941
- var import_node_path12 = require("node:path");
12377
+ var import_node_path13 = require("node:path");
11942
12378
  var VIBERAVEN_PACKAGE_JSON_SCRIPTS = {
11943
12379
  "viberaven:gate": PUBLIC_AGENT_MODE_COMMAND,
11944
12380
  "viberaven:verify": PUBLIC_VERIFY_COMMAND,
11945
12381
  "viberaven:strict": PUBLIC_STRICT_COMMAND
11946
12382
  };
11947
12383
  async function seedPackageJsonScripts(options) {
11948
- const packageJsonPath = (0, import_node_path12.join)(options.cwd, "package.json");
11949
- if (!(0, import_node_fs7.existsSync)(packageJsonPath)) {
12384
+ const packageJsonPath = (0, import_node_path13.join)(options.cwd, "package.json");
12385
+ if (!(0, import_node_fs8.existsSync)(packageJsonPath)) {
11950
12386
  return null;
11951
12387
  }
11952
12388
  const raw = await (0, import_promises9.readFile)(packageJsonPath, "utf-8");
@@ -11994,12 +12430,12 @@ async function initAgentRules(options) {
11994
12430
  continue;
11995
12431
  }
11996
12432
  const file = AGENT_RULE_TARGETS[target].file;
11997
- const path = (0, import_node_path13.join)(options.cwd, file);
12433
+ const path = (0, import_node_path14.join)(options.cwd, file);
11998
12434
  const existing = await readExistingFile2(path);
11999
12435
  const injected = injectAgentRulesBlock(existing.content, renderAgentRulesForTarget(target));
12000
12436
  const action = !existing.exists ? "created" : injected.changed ? "updated" : "unchanged";
12001
12437
  if (!options.dryRun && injected.changed) {
12002
- await (0, import_promises10.mkdir)((0, import_node_path13.dirname)(path), { recursive: true });
12438
+ await (0, import_promises10.mkdir)((0, import_node_path14.dirname)(path), { recursive: true });
12003
12439
  await (0, import_promises10.writeFile)(path, injected.content, "utf-8");
12004
12440
  }
12005
12441
  results.push({ target, file, path, action });
@@ -12376,7 +12812,7 @@ async function runInteractiveSession(startDir = process.cwd()) {
12376
12812
  Ie(`${import_picocolors4.default.bold("VibeRaven")} ${import_picocolors4.default.dim(VERSION)}`);
12377
12813
  const cwd = await resolveWorkspaceRoot(startDir);
12378
12814
  const artifactsAt = await findArtifactsWorkspace(startDir);
12379
- if (artifactsAt && (0, import_node_path14.resolve)(artifactsAt) !== (0, import_node_path14.resolve)(startDir)) {
12815
+ if (artifactsAt && (0, import_node_path15.resolve)(artifactsAt) !== (0, import_node_path15.resolve)(startDir)) {
12380
12816
  M2.message(import_picocolors4.default.dim(`Using scan from: ${artifactsAt}`));
12381
12817
  } else {
12382
12818
  M2.message(import_picocolors4.default.dim(`Project folder: ${cwd}`));
@@ -12444,11 +12880,11 @@ async function runInteractiveSession(startDir = process.cwd()) {
12444
12880
 
12445
12881
  // src/commands/condense.ts
12446
12882
  var import_promises11 = require("node:fs/promises");
12447
- var import_node_path15 = require("node:path");
12883
+ var import_node_path16 = require("node:path");
12448
12884
  async function runCondenseCommand(options) {
12449
12885
  const dir = getProjectArtifactsDir(options.cwd);
12450
- const artifact = JSON.parse(await (0, import_promises11.readFile)((0, import_node_path15.join)(dir, "last-scan.json"), "utf8"));
12451
- const contextMapPath = (0, import_node_path15.join)(dir, "context-map.json");
12886
+ const artifact = JSON.parse(await (0, import_promises11.readFile)((0, import_node_path16.join)(dir, "last-scan.json"), "utf8"));
12887
+ const contextMapPath = (0, import_node_path16.join)(dir, "context-map.json");
12452
12888
  await (0, import_promises11.writeFile)(contextMapPath, `${JSON.stringify(generateContextMap(artifact), null, 2)}
12453
12889
  `, "utf8");
12454
12890
  return { contextMapPath };
@@ -12456,16 +12892,16 @@ async function runCondenseCommand(options) {
12456
12892
 
12457
12893
  // src/heal/apply.ts
12458
12894
  var import_promises13 = require("node:fs/promises");
12459
- var import_node_fs10 = require("node:fs");
12460
- var import_node_path19 = require("node:path");
12895
+ var import_node_fs11 = require("node:fs");
12896
+ var import_node_path20 = require("node:path");
12461
12897
 
12462
12898
  // src/heal/pathSafety.ts
12463
- var import_node_path16 = require("node:path");
12899
+ var import_node_path17 = require("node:path");
12464
12900
  var BLOCKED_SEGMENTS = /* @__PURE__ */ new Set([".git", "node_modules", "dist", "build", ".next", ".viberaven"]);
12465
12901
  function assertSafeHealTarget(cwd, target) {
12466
- const root = (0, import_node_path16.resolve)(cwd);
12467
- const absolute = (0, import_node_path16.resolve)(root, target);
12468
- const rel = (0, import_node_path16.relative)(root, absolute);
12902
+ const root = (0, import_node_path17.resolve)(cwd);
12903
+ const absolute = (0, import_node_path17.resolve)(root, target);
12904
+ const rel = (0, import_node_path17.relative)(root, absolute);
12469
12905
  if (rel.startsWith("..") || rel === "" || /^[A-Za-z]:/.test(rel)) {
12470
12906
  throw new Error("Heal target must stay inside the workspace");
12471
12907
  }
@@ -12488,9 +12924,9 @@ function applyEmptyCatchRecipe(source) {
12488
12924
  }
12489
12925
 
12490
12926
  // src/heal/recipes/index.ts
12491
- var import_node_fs9 = require("node:fs");
12927
+ var import_node_fs10 = require("node:fs");
12492
12928
  var import_promises12 = require("node:fs/promises");
12493
- var import_node_path18 = require("node:path");
12929
+ var import_node_path19 = require("node:path");
12494
12930
 
12495
12931
  // src/heal/recipes/envAuthSecret.ts
12496
12932
  function applyAuthSecretRecipe(source) {
@@ -12871,8 +13307,8 @@ function applyRateLimitRecipe(source, hasUpstash) {
12871
13307
  }
12872
13308
 
12873
13309
  // src/heal/recipes/eslintRestrictedImports.ts
12874
- var import_node_fs8 = require("node:fs");
12875
- var import_node_path17 = require("node:path");
13310
+ var import_node_fs9 = require("node:fs");
13311
+ var import_node_path18 = require("node:path");
12876
13312
  var VIBERAVEN_ESLINT_MARKER = "VibeRaven heal: eslint_restricted_imports";
12877
13313
  var RESTRICTED_IMPORTS_MESSAGE = `Restricted import. Run ${PUBLIC_AGENT_MODE_COMMAND} before substituting packages.`;
12878
13314
  var RESTRICTED_PATHS = [
@@ -12902,7 +13338,7 @@ var ESLINT_CONFIG_CANDIDATES = [
12902
13338
  ];
12903
13339
  function detectEslintConfigFile(cwd) {
12904
13340
  for (const candidate of ESLINT_CONFIG_CANDIDATES) {
12905
- if ((0, import_node_fs8.existsSync)((0, import_node_path17.join)(cwd, candidate))) {
13341
+ if ((0, import_node_fs9.existsSync)((0, import_node_path18.join)(cwd, candidate))) {
12906
13342
  return candidate;
12907
13343
  }
12908
13344
  }
@@ -13077,13 +13513,13 @@ function defaultTargetFile(gapId) {
13077
13513
  return map[gapId];
13078
13514
  }
13079
13515
  async function readSourceOrEmpty(absolutePath) {
13080
- if (!(0, import_node_fs9.existsSync)(absolutePath)) return "";
13516
+ if (!(0, import_node_fs10.existsSync)(absolutePath)) return "";
13081
13517
  return (0, import_promises12.readFile)(absolutePath, "utf8");
13082
13518
  }
13083
13519
  async function detectUpstash(cwd) {
13084
13520
  try {
13085
- const pkgPath = (0, import_node_path18.join)(cwd, "package.json");
13086
- if (!(0, import_node_fs9.existsSync)(pkgPath)) return false;
13521
+ const pkgPath = (0, import_node_path19.join)(cwd, "package.json");
13522
+ if (!(0, import_node_fs10.existsSync)(pkgPath)) return false;
13087
13523
  const raw = await (0, import_promises12.readFile)(pkgPath, "utf8");
13088
13524
  const pkg = JSON.parse(raw);
13089
13525
  const deps = {
@@ -13099,7 +13535,7 @@ async function dispatchRecipeByGapId(gapId, cwd, explicitTarget) {
13099
13535
  const targetFile = explicitTarget ?? defaultTargetFile(gapId);
13100
13536
  if (targetFile === void 0) return null;
13101
13537
  if (gapId === "auth_secret_missing" || gapId === "node_env_not_set" || gapId === "database_url_missing") {
13102
- const absolutePath = (0, import_node_path18.join)(cwd, targetFile);
13538
+ const absolutePath = (0, import_node_path19.join)(cwd, targetFile);
13103
13539
  const source = await readSourceOrEmpty(absolutePath);
13104
13540
  let result;
13105
13541
  if (gapId === "auth_secret_missing") result = applyAuthSecretRecipe(source);
@@ -13113,25 +13549,25 @@ async function dispatchRecipeByGapId(gapId, cwd, explicitTarget) {
13113
13549
  };
13114
13550
  }
13115
13551
  if (gapId === "missing_error_boundary") {
13116
- const absolutePath = (0, import_node_path18.join)(cwd, "app/error.tsx");
13552
+ const absolutePath = (0, import_node_path19.join)(cwd, "app/error.tsx");
13117
13553
  const source = await readSourceOrEmpty(absolutePath);
13118
13554
  const result = applyErrorBoundaryRecipe(source);
13119
13555
  return { ...result, canAutoApply: true, recipeName: gapId };
13120
13556
  }
13121
13557
  if (gapId === "missing_health_route") {
13122
- const absolutePath = (0, import_node_path18.join)(cwd, "app/api/health/route.ts");
13558
+ const absolutePath = (0, import_node_path19.join)(cwd, "app/api/health/route.ts");
13123
13559
  const source = await readSourceOrEmpty(absolutePath);
13124
13560
  const result = applyHealthRouteRecipe(source);
13125
13561
  return { ...result, canAutoApply: true, recipeName: gapId };
13126
13562
  }
13127
13563
  if (gapId === "missing_loading_state") {
13128
- const absolutePath = (0, import_node_path18.join)(cwd, "app/loading.tsx");
13564
+ const absolutePath = (0, import_node_path19.join)(cwd, "app/loading.tsx");
13129
13565
  const source = await readSourceOrEmpty(absolutePath);
13130
13566
  const result = applyLoadingStateRecipe(source);
13131
13567
  return { ...result, canAutoApply: true, recipeName: gapId };
13132
13568
  }
13133
13569
  if (gapId === "missing_404_page") {
13134
- const absolutePath = (0, import_node_path18.join)(cwd, "app/not-found.tsx");
13570
+ const absolutePath = (0, import_node_path19.join)(cwd, "app/not-found.tsx");
13135
13571
  const source = await readSourceOrEmpty(absolutePath);
13136
13572
  const result = applyNotFoundRecipe(source);
13137
13573
  return { ...result, canAutoApply: true, recipeName: gapId };
@@ -13149,10 +13585,10 @@ async function dispatchRecipeByGapId(gapId, cwd, explicitTarget) {
13149
13585
  }
13150
13586
  if (gapId === "missing_csp_header") {
13151
13587
  let configFile = "next.config.js";
13152
- let absolutePath = (0, import_node_path18.join)(cwd, configFile);
13153
- if (!(0, import_node_fs9.existsSync)(absolutePath)) {
13588
+ let absolutePath = (0, import_node_path19.join)(cwd, configFile);
13589
+ if (!(0, import_node_fs10.existsSync)(absolutePath)) {
13154
13590
  configFile = "next.config.mjs";
13155
- absolutePath = (0, import_node_path18.join)(cwd, configFile);
13591
+ absolutePath = (0, import_node_path19.join)(cwd, configFile);
13156
13592
  }
13157
13593
  const source = await readSourceOrEmpty(absolutePath);
13158
13594
  const result = applyCspHeaderRecipe(source);
@@ -13164,7 +13600,7 @@ async function dispatchRecipeByGapId(gapId, cwd, explicitTarget) {
13164
13600
  }
13165
13601
  if (gapId === "missing_rate_limit") {
13166
13602
  const hasUpstash = await detectUpstash(cwd);
13167
- const middlewarePath = (0, import_node_path18.join)(cwd, "middleware.ts");
13603
+ const middlewarePath = (0, import_node_path19.join)(cwd, "middleware.ts");
13168
13604
  const source = await readSourceOrEmpty(middlewarePath);
13169
13605
  const result = applyRateLimitRecipe(source, hasUpstash);
13170
13606
  return {
@@ -13186,7 +13622,7 @@ async function dispatchRecipeByGapId(gapId, cwd, explicitTarget) {
13186
13622
  recipeName: gapId
13187
13623
  };
13188
13624
  }
13189
- const absolutePath = (0, import_node_path18.join)(cwd, configFile);
13625
+ const absolutePath = (0, import_node_path19.join)(cwd, configFile);
13190
13626
  const source = await readSourceOrEmpty(absolutePath);
13191
13627
  const result = applyEslintRestrictedImportsRecipe(source, configFile);
13192
13628
  return {
@@ -13261,13 +13697,13 @@ async function applyHeal(options) {
13261
13697
  rollback: { available: false, instructions: "Recipe matched but no change was needed (already applied or file already exists)." }
13262
13698
  };
13263
13699
  }
13264
- const absoluteTarget = (0, import_node_path19.join)(options.cwd, dispatched.targetFile);
13700
+ const absoluteTarget = (0, import_node_path20.join)(options.cwd, dispatched.targetFile);
13265
13701
  assertSafeHealTarget(options.cwd, dispatched.targetFile);
13266
- await (0, import_promises13.mkdir)((0, import_node_path19.dirname)(absoluteTarget), { recursive: true });
13267
- const healDir2 = (0, import_node_path19.join)(options.cwd, ".viberaven", "heal", id);
13268
- await (0, import_promises13.mkdir)((0, import_node_path19.join)(healDir2, "before"), { recursive: true });
13269
- const beforeContent = (0, import_node_fs10.existsSync)(absoluteTarget) ? await (0, import_promises13.readFile)(absoluteTarget, "utf8") : "";
13270
- await (0, import_promises13.writeFile)((0, import_node_path19.join)(healDir2, "before", "target.txt"), beforeContent, "utf8");
13702
+ await (0, import_promises13.mkdir)((0, import_node_path20.dirname)(absoluteTarget), { recursive: true });
13703
+ const healDir2 = (0, import_node_path20.join)(options.cwd, ".viberaven", "heal", id);
13704
+ await (0, import_promises13.mkdir)((0, import_node_path20.join)(healDir2, "before"), { recursive: true });
13705
+ const beforeContent = (0, import_node_fs11.existsSync)(absoluteTarget) ? await (0, import_promises13.readFile)(absoluteTarget, "utf8") : "";
13706
+ await (0, import_promises13.writeFile)((0, import_node_path20.join)(healDir2, "before", "target.txt"), beforeContent, "utf8");
13271
13707
  await (0, import_promises13.writeFile)(absoluteTarget, dispatched.output, "utf8");
13272
13708
  const patch2 = [
13273
13709
  `--- ${dispatched.targetFile}`,
@@ -13278,7 +13714,7 @@ async function applyHeal(options) {
13278
13714
  dispatched.output,
13279
13715
  ""
13280
13716
  ].join("\n");
13281
- await (0, import_promises13.writeFile)((0, import_node_path19.join)(healDir2, "patch.diff"), patch2, "utf8");
13717
+ await (0, import_promises13.writeFile)((0, import_node_path20.join)(healDir2, "patch.diff"), patch2, "utf8");
13282
13718
  const result2 = {
13283
13719
  $schema: "https://viberaven.dev/schemas/heal-result.schema.json",
13284
13720
  schemaVersion: "v1",
@@ -13289,7 +13725,7 @@ async function applyHeal(options) {
13289
13725
  gapId: options.gapId,
13290
13726
  recipe: dispatched.recipeName,
13291
13727
  target: dispatched.targetFile,
13292
- changedFiles: [(0, import_node_path19.relative)(options.cwd, absoluteTarget).replace(/\\/g, "/")],
13728
+ changedFiles: [(0, import_node_path20.relative)(options.cwd, absoluteTarget).replace(/\\/g, "/")],
13293
13729
  artifacts: {
13294
13730
  patch: `.viberaven/heal/${id}/patch.diff`,
13295
13731
  result: `.viberaven/heal/${id}/result.json`
@@ -13299,7 +13735,7 @@ async function applyHeal(options) {
13299
13735
  instructions: "Restore .viberaven/heal/<healId>/before/target.txt to the target file or apply the reverse patch."
13300
13736
  }
13301
13737
  };
13302
- await (0, import_promises13.writeFile)((0, import_node_path19.join)(healDir2, "result.json"), `${JSON.stringify(result2, null, 2)}
13738
+ await (0, import_promises13.writeFile)((0, import_node_path20.join)(healDir2, "result.json"), `${JSON.stringify(result2, null, 2)}
13303
13739
  `, "utf8");
13304
13740
  return result2;
13305
13741
  }
@@ -13336,9 +13772,9 @@ async function applyHeal(options) {
13336
13772
  rollback: { available: false, instructions: "No supported heal recipe matched this file." }
13337
13773
  };
13338
13774
  }
13339
- const healDir = (0, import_node_path19.join)(options.cwd, ".viberaven", "heal", id);
13340
- await (0, import_promises13.mkdir)((0, import_node_path19.join)(healDir, "before"), { recursive: true });
13341
- await (0, import_promises13.writeFile)((0, import_node_path19.join)(healDir, "before", "target.txt"), before, "utf8");
13775
+ const healDir = (0, import_node_path20.join)(options.cwd, ".viberaven", "heal", id);
13776
+ await (0, import_promises13.mkdir)((0, import_node_path20.join)(healDir, "before"), { recursive: true });
13777
+ await (0, import_promises13.writeFile)((0, import_node_path20.join)(healDir, "before", "target.txt"), before, "utf8");
13342
13778
  await (0, import_promises13.writeFile)(absolute, recipe.output, "utf8");
13343
13779
  const patch = [
13344
13780
  `--- ${options.target}`,
@@ -13349,7 +13785,7 @@ async function applyHeal(options) {
13349
13785
  recipe.output,
13350
13786
  ""
13351
13787
  ].join("\n");
13352
- await (0, import_promises13.writeFile)((0, import_node_path19.join)(healDir, "patch.diff"), patch, "utf8");
13788
+ await (0, import_promises13.writeFile)((0, import_node_path20.join)(healDir, "patch.diff"), patch, "utf8");
13353
13789
  const result = {
13354
13790
  $schema: "https://viberaven.dev/schemas/heal-result.schema.json",
13355
13791
  schemaVersion: "v1",
@@ -13360,7 +13796,7 @@ async function applyHeal(options) {
13360
13796
  gapId: options.gapId,
13361
13797
  recipe: "empty-catch-safe-response",
13362
13798
  target: options.target,
13363
- changedFiles: [(0, import_node_path19.relative)(options.cwd, absolute).replace(/\\/g, "/")],
13799
+ changedFiles: [(0, import_node_path20.relative)(options.cwd, absolute).replace(/\\/g, "/")],
13364
13800
  artifacts: {
13365
13801
  patch: `.viberaven/heal/${id}/patch.diff`,
13366
13802
  result: `.viberaven/heal/${id}/result.json`
@@ -13370,19 +13806,19 @@ async function applyHeal(options) {
13370
13806
  instructions: "Restore .viberaven/heal/<healId>/before/target.txt to the target file or apply the reverse patch."
13371
13807
  }
13372
13808
  };
13373
- await (0, import_promises13.writeFile)((0, import_node_path19.join)(healDir, "result.json"), `${JSON.stringify(result, null, 2)}
13809
+ await (0, import_promises13.writeFile)((0, import_node_path20.join)(healDir, "result.json"), `${JSON.stringify(result, null, 2)}
13374
13810
  `, "utf8");
13375
13811
  return result;
13376
13812
  }
13377
13813
 
13378
13814
  // src/heal/plan.ts
13379
13815
  var import_promises14 = require("node:fs/promises");
13380
- var import_node_path20 = require("node:path");
13816
+ var import_node_path21 = require("node:path");
13381
13817
  function healId2() {
13382
13818
  return `heal_${(/* @__PURE__ */ new Date()).toISOString().replace(/\D/g, "").slice(0, 14)}`;
13383
13819
  }
13384
13820
  async function writeHealPlan(options) {
13385
- const dir = (0, import_node_path20.join)(options.cwd, ".viberaven");
13821
+ const dir = (0, import_node_path21.join)(options.cwd, ".viberaven");
13386
13822
  await (0, import_promises14.mkdir)(dir, { recursive: true });
13387
13823
  const id = healId2();
13388
13824
  const target = options.target ?? `gap:${options.gapId}`;
@@ -13410,20 +13846,20 @@ async function writeHealPlan(options) {
13410
13846
  artifacts: { plan: ".viberaven/heal-plan.md" },
13411
13847
  rollback: { available: false, instructions: "No source files were changed." }
13412
13848
  };
13413
- await (0, import_promises14.writeFile)((0, import_node_path20.join)(dir, "heal-plan.md"), markdown, "utf8");
13414
- await (0, import_promises14.writeFile)((0, import_node_path20.join)(dir, "heal-plan.json"), `${JSON.stringify(result, null, 2)}
13849
+ await (0, import_promises14.writeFile)((0, import_node_path21.join)(dir, "heal-plan.md"), markdown, "utf8");
13850
+ await (0, import_promises14.writeFile)((0, import_node_path21.join)(dir, "heal-plan.json"), `${JSON.stringify(result, null, 2)}
13415
13851
  `, "utf8");
13416
13852
  return result;
13417
13853
  }
13418
13854
 
13419
13855
  // src/heal/prompt.ts
13420
13856
  var import_promises15 = require("node:fs/promises");
13421
- var import_node_path21 = require("node:path");
13857
+ var import_node_path22 = require("node:path");
13422
13858
  function healId3() {
13423
13859
  return `heal_${(/* @__PURE__ */ new Date()).toISOString().replace(/\D/g, "").slice(0, 14)}`;
13424
13860
  }
13425
13861
  async function writeHealPrompt(options) {
13426
- const dir = (0, import_node_path21.join)(options.cwd, ".viberaven");
13862
+ const dir = (0, import_node_path22.join)(options.cwd, ".viberaven");
13427
13863
  await (0, import_promises15.mkdir)(dir, { recursive: true });
13428
13864
  const id = healId3();
13429
13865
  const target = options.target ?? `gap:${options.gapId}`;
@@ -13451,7 +13887,7 @@ async function writeHealPrompt(options) {
13451
13887
  artifacts: { prompt: ".viberaven/heal-prompt.md" },
13452
13888
  rollback: { available: false, instructions: "No source files were changed." }
13453
13889
  };
13454
- await (0, import_promises15.writeFile)((0, import_node_path21.join)(dir, "heal-prompt.md"), prompt, "utf8");
13890
+ await (0, import_promises15.writeFile)((0, import_node_path22.join)(dir, "heal-prompt.md"), prompt, "utf8");
13455
13891
  return result;
13456
13892
  }
13457
13893
 
@@ -13537,8 +13973,8 @@ async function runHealCommand(options) {
13537
13973
 
13538
13974
  // src/stackRecommend.ts
13539
13975
  var import_promises17 = require("node:fs/promises");
13540
- var import_node_fs11 = require("node:fs");
13541
- var import_node_path22 = require("node:path");
13976
+ var import_node_fs12 = require("node:fs");
13977
+ var import_node_path23 = require("node:path");
13542
13978
  var DEFAULT_STACK = {
13543
13979
  frontend: "react",
13544
13980
  ui: "tailwind + shadcn/ui",
@@ -13549,8 +13985,8 @@ var DEFAULT_STACK = {
13549
13985
  reason: "Agent-default stack for lowest launch friction when repo signals are ambiguous"
13550
13986
  };
13551
13987
  async function recommendStack(cwd = process.cwd()) {
13552
- const pkgPath = (0, import_node_path22.join)(cwd, "package.json");
13553
- if (!(0, import_node_fs11.existsSync)(pkgPath)) {
13988
+ const pkgPath = (0, import_node_path23.join)(cwd, "package.json");
13989
+ if (!(0, import_node_fs12.existsSync)(pkgPath)) {
13554
13990
  return DEFAULT_STACK;
13555
13991
  }
13556
13992
  const pkg = JSON.parse(await (0, import_promises17.readFile)(pkgPath, "utf-8"));
@@ -13606,7 +14042,7 @@ async function runInitCommand(options) {
13606
14042
 
13607
14043
  // src/commands/doctorAgents.ts
13608
14044
  var import_promises18 = require("node:fs/promises");
13609
- var import_node_path23 = require("node:path");
14045
+ var import_node_path24 = require("node:path");
13610
14046
  var REQUIRED_EXISTENCE_CHECKS = [
13611
14047
  { id: "agents-md", file: "AGENTS.md" },
13612
14048
  { id: "claude-md", file: "CLAUDE.md" },
@@ -13626,7 +14062,7 @@ var STALE_PATTERNS = [
13626
14062
  async function checkAgentInjection(cwd) {
13627
14063
  const checks = [];
13628
14064
  for (const item3 of REQUIRED_EXISTENCE_CHECKS) {
13629
- const exists = await fileExists2((0, import_node_path23.join)(cwd, item3.file));
14065
+ const exists = await fileExists2((0, import_node_path24.join)(cwd, item3.file));
13630
14066
  checks.push({
13631
14067
  id: item3.id,
13632
14068
  status: exists ? "pass" : "fail",
@@ -13634,17 +14070,17 @@ async function checkAgentInjection(cwd) {
13634
14070
  });
13635
14071
  }
13636
14072
  for (const item3 of OPTIONAL_CURSOR_PACK_CHECKS) {
13637
- const exists = await fileExists2((0, import_node_path23.join)(cwd, item3.file));
14073
+ const exists = await fileExists2((0, import_node_path24.join)(cwd, item3.file));
13638
14074
  checks.push({
13639
14075
  id: item3.id,
13640
14076
  status: exists ? "pass" : "fail",
13641
14077
  message: exists ? `${item3.file} exists` : `Missing ${item3.file} \u2014 run npx -y viberaven init --agents all`
13642
14078
  });
13643
14079
  }
13644
- const legacyCursorPath = (0, import_node_path23.join)(cwd, ".cursor/rules/viberaven.mdc");
14080
+ const legacyCursorPath = (0, import_node_path24.join)(cwd, ".cursor/rules/viberaven.mdc");
13645
14081
  if (await fileExists2(legacyCursorPath)) {
13646
14082
  const legacyContent = await (0, import_promises18.readFile)(legacyCursorPath, "utf-8");
13647
- const hasCoreSplit = await fileExists2((0, import_node_path23.join)(cwd, ".cursor/rules/viberaven-core.mdc"));
14083
+ const hasCoreSplit = await fileExists2((0, import_node_path24.join)(cwd, ".cursor/rules/viberaven-core.mdc"));
13648
14084
  if (!hasCoreSplit || legacyContent.includes("alwaysApply: true")) {
13649
14085
  checks.push({
13650
14086
  id: "cursor-legacy-mdc",
@@ -13655,7 +14091,7 @@ async function checkAgentInjection(cwd) {
13655
14091
  }
13656
14092
  for (const target of CORE_AGENT_INJECTION_TARGETS) {
13657
14093
  const file = target === "cursor" ? ".cursor/rules/viberaven-core.mdc" : AGENT_RULE_TARGETS[target].file;
13658
- const path = (0, import_node_path23.join)(cwd, file);
14094
+ const path = (0, import_node_path24.join)(cwd, file);
13659
14095
  const exists = await fileExists2(path);
13660
14096
  if (!exists) {
13661
14097
  checks.push({
@@ -13898,6 +14334,20 @@ async function runValidateNpmPackageCommand(options) {
13898
14334
  return hasBlocking ? 2 : 0;
13899
14335
  }
13900
14336
 
14337
+ // src/commands/runAudit.ts
14338
+ async function runAuditCommand(input) {
14339
+ const auditInput = await collectVercelSupabaseAuditInput(input.cwd);
14340
+ const result = buildVercelSupabaseAudit(auditInput);
14341
+ if (input.json) {
14342
+ process.stdout.write(`${JSON.stringify(result, null, 2)}
14343
+ `);
14344
+ } else {
14345
+ process.stdout.write(`${renderVercelSupabaseAudit(result)}
14346
+ `);
14347
+ }
14348
+ return result.status === "pass" ? 0 : 1;
14349
+ }
14350
+
13901
14351
  // src/output/nextActionBlock.ts
13902
14352
  function buildNextActionBlock(tasks, loopState, plan) {
13903
14353
  const batchSize = plan === "pro" ? 10 : 3;
@@ -13995,7 +14445,11 @@ function buildProviderActionBlock(task) {
13995
14445
  envKeyExample: pa.envKeyExample ?? null,
13996
14446
  doneSignal: pa.doneSignal,
13997
14447
  verifyCommand: task.verifyCommand,
13998
- mcpAlternative: task.mcpTool ?? pa.mcpAlternative ?? null
14448
+ mcpAlternative: task.mcpTool ?? pa.mcpAlternative ?? null,
14449
+ actionClass: pa.actionClass,
14450
+ approvalRequired: pa.approvalRequired,
14451
+ manualFallback: pa.manualFallback,
14452
+ copyValues: pa.copyValues
13999
14453
  }
14000
14454
  };
14001
14455
  }
@@ -14016,104 +14470,54 @@ function printNextActionBlock(block) {
14016
14470
  console.log(NEXT_ACTION_END);
14017
14471
  }
14018
14472
 
14019
- // src/providerMcpBridge.ts
14020
- var import_node_fs12 = require("node:fs");
14021
- var import_node_os2 = require("node:os");
14022
- var import_node_path24 = require("node:path");
14023
- var UPGRADE_URL4 = "https://viberaven.dev/pricing";
14024
- var FALLBACK_COMMAND = "npx -y viberaven audit --vercel-supabase --json";
14025
- var SUPPORTED_PROVIDERS = /* @__PURE__ */ new Set(["supabase", "vercel"]);
14026
- var configPathsOverride;
14027
- function defaultMcpConfigPaths() {
14028
- const home = (0, import_node_os2.homedir)();
14029
- return [
14030
- (0, import_node_path24.join)(home, ".config", "claude", "claude_desktop_config.json"),
14031
- (0, import_node_path24.join)(home, ".cursor", "mcp.json"),
14032
- (0, import_node_path24.join)(home, ".gemini", "antigravity", "mcp_config.json")
14473
+ // src/output/launchActionBlock.ts
14474
+ var LAUNCH_ACTION_START = "VIBERAVEN_LAUNCH_ACTION_START";
14475
+ var LAUNCH_ACTION_END = "VIBERAVEN_LAUNCH_ACTION_END";
14476
+ function renderLaunchSummary(plan) {
14477
+ const lines = [
14478
+ "",
14479
+ "VibeRaven Launch Autopilot",
14480
+ "",
14481
+ `Recipe: ${plan.recipe.label}`,
14482
+ "Goal: preview URL first, production gate second",
14483
+ `Mode: ${plan.permissionMode}`
14033
14484
  ];
14034
- }
14035
- function resolveConfigPaths() {
14036
- return configPathsOverride ?? defaultMcpConfigPaths();
14037
- }
14038
- function parseMcpServers(raw) {
14039
- if (!raw || typeof raw !== "object") {
14040
- return void 0;
14485
+ if (plan.usageLine) {
14486
+ lines.push(`Scan usage: ${plan.usageLine}`);
14041
14487
  }
14042
- const obj = raw;
14043
- if (obj.mcpServers && typeof obj.mcpServers === "object") {
14044
- return obj.mcpServers;
14045
- }
14046
- if (obj.servers && typeof obj.servers === "object") {
14047
- return obj.servers;
14488
+ lines.push(
14489
+ "",
14490
+ `Auto-close queue: ${plan.safeRepoTasks.length} safe repo task${plan.safeRepoTasks.length === 1 ? "" : "s"}`,
14491
+ `Provider actions: ${plan.providerActions.length}`,
14492
+ "",
14493
+ "Best next move:",
14494
+ plan.nextAction.title,
14495
+ plan.nextAction.detail
14496
+ );
14497
+ if (plan.nextAction.openUrl) {
14498
+ lines.push(`Open: ${plan.nextAction.openUrl}`);
14048
14499
  }
14049
- return void 0;
14050
- }
14051
- function findServerEntry(servers, provider2) {
14052
- if (servers[provider2]) {
14053
- return servers[provider2];
14500
+ if (plan.nextAction.command) {
14501
+ lines.push(`Command: ${plan.nextAction.command}`);
14054
14502
  }
14055
- const key = Object.keys(servers).find((candidate) => candidate.toLowerCase() === provider2);
14056
- return key ? servers[key] : void 0;
14503
+ lines.push(`Approval required: ${plan.nextAction.approvalRequired ? "yes" : "no"}`);
14504
+ return lines.join("\n");
14057
14505
  }
14058
- function findProviderMcpConfig(provider2, configPaths) {
14059
- const paths = configPaths ?? resolveConfigPaths();
14060
- for (const path of paths) {
14061
- if (!(0, import_node_fs12.existsSync)(path)) {
14062
- continue;
14063
- }
14064
- try {
14065
- const raw = JSON.parse((0, import_node_fs12.readFileSync)(path, "utf8"));
14066
- const servers = parseMcpServers(raw);
14067
- if (!servers) {
14068
- continue;
14069
- }
14070
- const entry = findServerEntry(servers, provider2);
14071
- if (!entry || typeof entry !== "object") {
14072
- continue;
14506
+ function printLaunchActionBlock(plan) {
14507
+ console.log(LAUNCH_ACTION_START);
14508
+ console.log(
14509
+ JSON.stringify(
14510
+ {
14511
+ VIBERAVEN_LAUNCH_ACTION: plan.nextAction
14073
14512
  }
14074
- const server = entry;
14075
- return {
14076
- command: typeof server.command === "string" ? server.command : void 0,
14077
- args: Array.isArray(server.args) ? server.args.filter((arg) => typeof arg === "string") : void 0,
14078
- url: typeof server.url === "string" ? server.url : void 0,
14079
- source: path
14080
- };
14081
- } catch {
14082
- continue;
14083
- }
14084
- }
14085
- return void 0;
14086
- }
14087
- async function verifyProviderGap(options) {
14088
- if (options.plan !== "pro") {
14089
- return {
14090
- verified: false,
14091
- reason: "pro-required",
14092
- upgradeUrl: UPGRADE_URL4
14093
- };
14094
- }
14095
- const provider2 = options.provider.toLowerCase().trim();
14096
- if (!SUPPORTED_PROVIDERS.has(provider2)) {
14097
- return {
14098
- verified: false,
14099
- reason: "unsupported-provider"
14100
- };
14101
- }
14102
- const mcpConfig = findProviderMcpConfig(provider2);
14103
- if (!mcpConfig) {
14104
- return {
14105
- verified: false,
14106
- mcpUnavailable: true,
14107
- fallbackCommand: FALLBACK_COMMAND
14108
- };
14109
- }
14110
- return {
14111
- verified: false,
14112
- mcpUnavailable: true,
14113
- fallbackCommand: FALLBACK_COMMAND
14114
- };
14513
+ )
14514
+ );
14515
+ console.log(LAUNCH_ACTION_END);
14115
14516
  }
14116
14517
 
14518
+ // src/launch/types.ts
14519
+ var LAUNCH_PERMISSION_MODES = ["manual", "ask", "safe", "full"];
14520
+
14117
14521
  // src/cli.ts
14118
14522
  function printHelp() {
14119
14523
  console.log(`viberaven ${VERSION} \u2014 launch readiness for AI-built apps
@@ -14142,6 +14546,7 @@ Usage:
14142
14546
 
14143
14547
  viberaven --agent-mode [--json|--jsonl] [path]
14144
14548
  Agent-first scan; writes tasklist, gate-result, context-map, and per-gap JSON
14549
+ Optional: --launch-mode manual|ask|safe|full (default: ask)
14145
14550
 
14146
14551
  viberaven --strict[=warning] [path]
14147
14552
  Fail when production gate is not clear; warning mode also fails on warnings
@@ -14178,6 +14583,9 @@ Usage:
14178
14583
  viberaven doctor --agents [path]
14179
14584
  Verify agent instruction files and canonical commands
14180
14585
 
14586
+ viberaven audit --vercel-supabase [--json] [path]
14587
+ Local Vercel/Supabase repo evidence audit (RLS, pooler, secrets)
14588
+
14181
14589
 
14182
14590
 
14183
14591
  Agent workflow (Claude Code / Codex):
@@ -14275,6 +14683,13 @@ function shouldConsumeLeadingHyphenValue(command, key, value) {
14275
14683
  function hasFlag(flags, key) {
14276
14684
  return flags[key] === true || typeof flags[key] === "string";
14277
14685
  }
14686
+ function resolveLaunchPermissionMode(flags) {
14687
+ const value = flags["launch-mode"];
14688
+ if (typeof value !== "string") {
14689
+ return "ask";
14690
+ }
14691
+ return LAUNCH_PERMISSION_MODES.includes(value) ? value : "ask";
14692
+ }
14278
14693
  async function guardEarlyVerifyScan(input) {
14279
14694
  if (input.flags["force-scan"] === true) {
14280
14695
  return void 0;
@@ -14559,7 +14974,11 @@ async function runScanCommand(flags, positional, options) {
14559
14974
  return { exitCode: 1 };
14560
14975
  }
14561
14976
  const artifact = await enrichArtifactWithAccount(result.artifact, apiBaseUrl, accessToken);
14562
- const paths = await writeScanArtifacts({ artifact, cwd: workspacePath });
14977
+ const paths = await writeScanArtifacts({
14978
+ artifact,
14979
+ cwd: workspacePath,
14980
+ launchPermissionMode: resolveLaunchPermissionMode(flags)
14981
+ });
14563
14982
  if (flags.json && !options?.deferMachineOutput) {
14564
14983
  console.log(formatScanJsonStdout(artifact));
14565
14984
  return { exitCode: 0, artifacts: paths };
@@ -14574,11 +14993,14 @@ async function runScanCommand(flags, positional, options) {
14574
14993
  const loopState = await loadLoopState(workspacePath);
14575
14994
  const openGapCount = artifact.gaps.length;
14576
14995
  const updatedState = resetBatch(loopState, openGapCount);
14577
- const tasks = buildTaskList(artifact);
14996
+ const tasks = paths.tasks;
14578
14997
  const plan = artifact.plan ?? "free";
14579
14998
  const block = buildNextActionBlock(tasks, updatedState, plan);
14580
14999
  printNextActionBlock(block);
14581
15000
  printProviderActionBlock(tasks);
15001
+ const launchPlan = paths.launchPlan;
15002
+ console.log(renderLaunchSummary(launchPlan));
15003
+ printLaunchActionBlock(launchPlan);
14582
15004
  await saveLoopState(workspacePath, updatedState);
14583
15005
  }
14584
15006
  if (flags.open) {
@@ -14834,6 +15256,15 @@ async function main() {
14834
15256
  names: positional,
14835
15257
  json: Boolean(flags.json)
14836
15258
  });
15259
+ case "audit":
15260
+ if (flags["vercel-supabase"] !== true) {
15261
+ console.error("Usage: viberaven audit --vercel-supabase [--json] [path]");
15262
+ return 1;
15263
+ }
15264
+ return runAuditCommand({
15265
+ cwd: positional[0] ? (0, import_node_path25.join)(process.cwd(), positional[0]) : process.cwd(),
15266
+ json: Boolean(flags.json)
15267
+ });
14837
15268
  default:
14838
15269
  console.error(`Unknown command: ${command}`);
14839
15270
  printHelp();
@@ -14857,6 +15288,7 @@ if (require.main === module) {
14857
15288
  parseArgs,
14858
15289
  printHelp,
14859
15290
  resolveDefaultEntrypointMode,
15291
+ resolveLaunchPermissionMode,
14860
15292
  runScanCommand
14861
15293
  });
14862
15294
  //# sourceMappingURL=cli.js.map