@viberaven/cli 1.2.3 → 1.2.4

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.
Files changed (69) hide show
  1. package/README.md +73 -33
  2. package/SECURITY.md +53 -53
  3. package/assets/report/assets/provider-authjs.svg +5 -5
  4. package/assets/report/assets/provider-aws.svg +5 -5
  5. package/assets/report/assets/provider-logrocket.svg +4 -4
  6. package/assets/report/assets/viberaven-favicon.png +0 -0
  7. package/assets/report/station.js +2 -2
  8. package/dist/cli.js +559 -550
  9. package/dist/cli.js.map +4 -4
  10. package/dist/report/assets/provider-authjs.svg +5 -5
  11. package/dist/report/assets/provider-aws.svg +5 -5
  12. package/dist/report/assets/provider-logrocket.svg +4 -4
  13. package/dist/report/assets/viberaven-favicon.png +0 -0
  14. package/dist/report/station.js +2 -2
  15. package/package.json +75 -75
  16. package/assets/report/assets/provider-auth.png +0 -0
  17. package/assets/report/assets/provider-authjs.png +0 -0
  18. package/assets/report/assets/provider-clerk-mark.svg +0 -1
  19. package/assets/report/assets/provider-clerk.png +0 -0
  20. package/assets/report/assets/provider-database.png +0 -0
  21. package/assets/report/assets/provider-env.png +0 -0
  22. package/assets/report/assets/provider-github-mark.svg +0 -1
  23. package/assets/report/assets/provider-github.png +0 -0
  24. package/assets/report/assets/provider-posthog-mark.svg +0 -1
  25. package/assets/report/assets/provider-posthog.png +0 -0
  26. package/assets/report/assets/provider-resend-mark.svg +0 -1
  27. package/assets/report/assets/provider-resend.png +0 -0
  28. package/assets/report/assets/provider-sentry-mark.svg +0 -1
  29. package/assets/report/assets/provider-sentry.png +0 -0
  30. package/assets/report/assets/provider-stripe-mark.svg +0 -1
  31. package/assets/report/assets/provider-stripe.png +0 -0
  32. package/assets/report/assets/provider-supabase-mark.svg +0 -1
  33. package/assets/report/assets/provider-supabase.png +0 -0
  34. package/assets/report/assets/provider-upstash-mark.svg +0 -1
  35. package/assets/report/assets/provider-upstash.png +0 -0
  36. package/assets/report/assets/provider-vercel-mark.svg +0 -1
  37. package/assets/report/assets/provider-vercel.png +0 -0
  38. package/assets/report/assets/viberaven-agent-icon.png +0 -0
  39. package/assets/report/assets/viberaven-mascot-alert.png +0 -0
  40. package/assets/report/assets/viberaven-mascot-fixing.png +0 -0
  41. package/assets/report/assets/viberaven-mascot-verified.png +0 -0
  42. package/dist/report/assets/provider-auth.png +0 -0
  43. package/dist/report/assets/provider-authjs.png +0 -0
  44. package/dist/report/assets/provider-clerk-mark.svg +0 -1
  45. package/dist/report/assets/provider-clerk.png +0 -0
  46. package/dist/report/assets/provider-database.png +0 -0
  47. package/dist/report/assets/provider-env.png +0 -0
  48. package/dist/report/assets/provider-github-mark.svg +0 -1
  49. package/dist/report/assets/provider-github.png +0 -0
  50. package/dist/report/assets/provider-posthog-mark.svg +0 -1
  51. package/dist/report/assets/provider-posthog.png +0 -0
  52. package/dist/report/assets/provider-resend-mark.svg +0 -1
  53. package/dist/report/assets/provider-resend.png +0 -0
  54. package/dist/report/assets/provider-sentry-mark.svg +0 -1
  55. package/dist/report/assets/provider-sentry.png +0 -0
  56. package/dist/report/assets/provider-stripe-mark.svg +0 -1
  57. package/dist/report/assets/provider-stripe.png +0 -0
  58. package/dist/report/assets/provider-supabase-mark.svg +0 -1
  59. package/dist/report/assets/provider-supabase.png +0 -0
  60. package/dist/report/assets/provider-upstash-mark.svg +0 -1
  61. package/dist/report/assets/provider-upstash.png +0 -0
  62. package/dist/report/assets/provider-vercel-mark.svg +0 -1
  63. package/dist/report/assets/provider-vercel.png +0 -0
  64. package/dist/report/assets/viberaven-agent-icon.png +0 -0
  65. package/dist/report/assets/viberaven-mascot-alert.png +0 -0
  66. package/dist/report/assets/viberaven-mascot-fixing.png +0 -0
  67. package/dist/report/assets/viberaven-mascot-verified.png +0 -0
  68. /package/assets/report/assets/{viberaven-mascot-idle.png → viberaven-mascot.png} +0 -0
  69. /package/dist/report/assets/{viberaven-mascot-idle.png → viberaven-mascot.png} +0 -0
package/dist/cli.js CHANGED
@@ -394,6 +394,8 @@ var init_backendClient = __esm({
394
394
  this.status = status;
395
395
  this.upgradeUrl = upgradeUrl;
396
396
  }
397
+ status;
398
+ upgradeUrl;
397
399
  };
398
400
  }
399
401
  });
@@ -543,12 +545,12 @@ function createAppWindowOpenCommands(target, platform = process.platform, env =
543
545
  }
544
546
  async function openWithSystemDefault(target) {
545
547
  const { command, args, shell } = createOpenCommand(target);
546
- await new Promise((resolve7, reject) => {
548
+ await new Promise((resolve6, reject) => {
547
549
  const child = (0, import_node_child_process.spawn)(command, args, { stdio: "ignore", shell });
548
550
  child.on("error", reject);
549
551
  child.on("exit", (code) => {
550
552
  if (code === 0) {
551
- resolve7();
553
+ resolve6();
552
554
  } else {
553
555
  reject(new Error(`Could not open browser (exit ${code ?? "unknown"}). Open manually: ${target}`));
554
556
  }
@@ -559,7 +561,7 @@ async function spawnDetached(command) {
559
561
  if (/^[A-Za-z]:[\\/]/.test(command.command) && !(0, import_node_fs2.existsSync)(command.command)) {
560
562
  throw new Error(`Browser not found: ${command.command}`);
561
563
  }
562
- await new Promise((resolve7, reject) => {
564
+ await new Promise((resolve6, reject) => {
563
565
  const child = (0, import_node_child_process.spawn)(command.command, command.args, {
564
566
  detached: true,
565
567
  stdio: "ignore",
@@ -567,7 +569,7 @@ async function spawnDetached(command) {
567
569
  });
568
570
  child.once("spawn", () => {
569
571
  child.unref();
570
- resolve7();
572
+ resolve6();
571
573
  });
572
574
  child.once("error", reject);
573
575
  });
@@ -636,7 +638,7 @@ var init_commands = __esm({
636
638
 
637
639
  // src/auth.ts
638
640
  function sleep(ms) {
639
- return new Promise((resolve7) => setTimeout(resolve7, ms));
641
+ return new Promise((resolve6) => setTimeout(resolve6, ms));
640
642
  }
641
643
  async function runDeviceLogin(apiBaseUrl) {
642
644
  const signIn = await startManagedSignIn(apiBaseUrl);
@@ -9790,36 +9792,11 @@ var init_reportAssets = __esm({
9790
9792
  "station.js",
9791
9793
  "report-cli.css",
9792
9794
  "assets/viberaven-logo.png",
9793
- "assets/viberaven-agent-icon.png",
9794
- "assets/viberaven-mascot-idle.png",
9795
- "assets/viberaven-mascot-fixing.png",
9796
- "assets/viberaven-mascot-verified.png",
9797
- "assets/viberaven-mascot-alert.png",
9795
+ "assets/viberaven-favicon.png",
9796
+ "assets/viberaven-mascot.png",
9798
9797
  "assets/provider-authjs.svg",
9799
9798
  "assets/provider-aws.svg",
9800
- "assets/provider-logrocket.svg",
9801
- "assets/provider-auth.png",
9802
- "assets/provider-authjs.png",
9803
- "assets/provider-clerk.png",
9804
- "assets/provider-clerk-mark.svg",
9805
- "assets/provider-database.png",
9806
- "assets/provider-env.png",
9807
- "assets/provider-github.png",
9808
- "assets/provider-github-mark.svg",
9809
- "assets/provider-posthog.png",
9810
- "assets/provider-posthog-mark.svg",
9811
- "assets/provider-resend.png",
9812
- "assets/provider-resend-mark.svg",
9813
- "assets/provider-sentry.png",
9814
- "assets/provider-sentry-mark.svg",
9815
- "assets/provider-stripe.png",
9816
- "assets/provider-stripe-mark.svg",
9817
- "assets/provider-supabase.png",
9818
- "assets/provider-supabase-mark.svg",
9819
- "assets/provider-upstash.png",
9820
- "assets/provider-upstash-mark.svg",
9821
- "assets/provider-vercel.png",
9822
- "assets/provider-vercel-mark.svg"
9799
+ "assets/provider-logrocket.svg"
9823
9800
  ];
9824
9801
  }
9825
9802
  });
@@ -10440,12 +10417,12 @@ async function copyToClipboard(text) {
10440
10417
  }
10441
10418
  }
10442
10419
  function pipeToCommand(command, text, extraArgs = []) {
10443
- return new Promise((resolve7, reject) => {
10420
+ return new Promise((resolve6, reject) => {
10444
10421
  const child = (0, import_node_child_process2.spawn)(command, extraArgs, { stdio: ["pipe", "ignore", "ignore"] });
10445
10422
  child.on("error", reject);
10446
10423
  child.on("close", (code) => {
10447
10424
  if (code === 0) {
10448
- resolve7();
10425
+ resolve6();
10449
10426
  } else {
10450
10427
  reject(new Error(`${command} exited with code ${code ?? "unknown"}`));
10451
10428
  }
@@ -10467,9 +10444,9 @@ var init_clipboard = __esm({
10467
10444
  }
10468
10445
  });
10469
10446
 
10470
- // node_modules/picocolors/picocolors.js
10447
+ // ../../node_modules/picocolors/picocolors.js
10471
10448
  var require_picocolors = __commonJS({
10472
- "node_modules/picocolors/picocolors.js"(exports2, module2) {
10449
+ "../../node_modules/picocolors/picocolors.js"(exports2, module2) {
10473
10450
  var p2 = process || {};
10474
10451
  var argv = p2.argv || [];
10475
10452
  var env = p2.env || {};
@@ -10659,7 +10636,7 @@ function printScanSummary(artifact, paths) {
10659
10636
  if (next.upgradeUrl) {
10660
10637
  console.log(`Upgrade: ${next.upgradeUrl}`);
10661
10638
  }
10662
- console.log(formatAgentStatus(AGENT_ACTION, "Keep operating: read .viberaven/agent-tasklist.md, apply the next safe repo fix, then continue the VibeRaven loop."));
10639
+ console.log(formatAgentStatus(AGENT_ACTION, "Read .viberaven/agent-tasklist.md, run the next command above, then rescan."));
10663
10640
  console.log("");
10664
10641
  if (artifact.usage) {
10665
10642
  const lanes = artifact.usage.unlockedMapCategoryKeys.length;
@@ -10923,10 +10900,10 @@ function sleepForRunnerPoll(ms, signal) {
10923
10900
  if (signal?.aborted) {
10924
10901
  return Promise.reject(createRunnerWatchAbortError());
10925
10902
  }
10926
- return new Promise((resolve7, reject) => {
10903
+ return new Promise((resolve6, reject) => {
10927
10904
  const timer = setTimeout(() => {
10928
10905
  cleanup();
10929
- resolve7();
10906
+ resolve6();
10930
10907
  }, ms);
10931
10908
  const onAbort = () => {
10932
10909
  cleanup();
@@ -11498,9 +11475,9 @@ function formatRepoMatch(repoMatch) {
11498
11475
  }
11499
11476
  }
11500
11477
  async function runCommand(command, args, cwd) {
11501
- return new Promise((resolve7) => {
11478
+ return new Promise((resolve6) => {
11502
11479
  (0, import_node_child_process3.execFile)(command, args, { cwd, windowsHide: true }, (error, stdout, stderr) => {
11503
- resolve7({
11480
+ resolve6({
11504
11481
  ok: !error,
11505
11482
  stdout: String(stdout ?? ""),
11506
11483
  stderr: String(stderr ?? "")
@@ -11645,9 +11622,9 @@ var init_runnerConnect = __esm({
11645
11622
  }
11646
11623
  });
11647
11624
 
11648
- // node_modules/sisteransi/src/index.js
11625
+ // ../../node_modules/sisteransi/src/index.js
11649
11626
  var require_src = __commonJS({
11650
- "node_modules/sisteransi/src/index.js"(exports2, module2) {
11627
+ "../../node_modules/sisteransi/src/index.js"(exports2, module2) {
11651
11628
  "use strict";
11652
11629
  var ESC = "\x1B";
11653
11630
  var CSI = `${ESC}[`;
@@ -11701,7 +11678,7 @@ var require_src = __commonJS({
11701
11678
  }
11702
11679
  });
11703
11680
 
11704
- // node_modules/@clack/core/dist/index.mjs
11681
+ // ../../node_modules/@clack/core/dist/index.mjs
11705
11682
  function DD({ onlyFirst: e2 = false } = {}) {
11706
11683
  const t = ["[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?(?:\\u0007|\\u001B\\u005C|\\u009C))", "(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]))"].join("|");
11707
11684
  return new RegExp(t, e2 ? void 0 : "g");
@@ -11813,7 +11790,7 @@ function fD({ input: e2 = import_node_process.stdin, output: u2 = import_node_pr
11813
11790
  }
11814
11791
  var import_sisteransi, import_node_process, g, import_node_readline, import_node_stream, uD, W, tD, eD, FD, sD, w, N, I, R, C, iD, rD, ED, d, oD, y, V, nD, G, _, z, K, aD, k, hD, lD, xD, B, AD, S, gD, vD, h, x, A, OD, PD, J, LD;
11815
11792
  var init_dist = __esm({
11816
- "node_modules/@clack/core/dist/index.mjs"() {
11793
+ "../../node_modules/@clack/core/dist/index.mjs"() {
11817
11794
  import_sisteransi = __toESM(require_src(), 1);
11818
11795
  import_node_process = require("node:process");
11819
11796
  g = __toESM(require("node:readline"), 1);
@@ -11939,7 +11916,7 @@ var init_dist = __esm({
11939
11916
  xD = ["up", "down", "left", "right", "space", "enter", "cancel"];
11940
11917
  B = { actions: new Set(xD), aliases: /* @__PURE__ */ new Map([["k", "up"], ["j", "down"], ["h", "left"], ["l", "right"], ["", "cancel"], ["escape", "cancel"]]) };
11941
11918
  AD = globalThis.process.platform.startsWith("win");
11942
- S = Symbol("clack:cancel");
11919
+ S = /* @__PURE__ */ Symbol("clack:cancel");
11943
11920
  gD = Object.defineProperty;
11944
11921
  vD = (e2, u2, t) => u2 in e2 ? gD(e2, u2, { enumerable: true, configurable: true, writable: true, value: t }) : e2[u2] = t;
11945
11922
  h = (e2, u2, t) => (vD(e2, typeof u2 != "symbol" ? u2 + "" : u2, t), t);
@@ -12063,13 +12040,13 @@ var init_dist = __esm({
12063
12040
  }
12064
12041
  });
12065
12042
 
12066
- // node_modules/@clack/prompts/dist/index.mjs
12043
+ // ../../node_modules/@clack/prompts/dist/index.mjs
12067
12044
  function ce() {
12068
12045
  return import_node_process2.default.platform !== "win32" ? import_node_process2.default.env.TERM !== "linux" : !!import_node_process2.default.env.CI || !!import_node_process2.default.env.WT_SESSION || !!import_node_process2.default.env.TERMINUS_SUBLIME || import_node_process2.default.env.ConEmuTask === "{cmd::Cmder}" || import_node_process2.default.env.TERM_PROGRAM === "Terminus-Sublime" || import_node_process2.default.env.TERM_PROGRAM === "vscode" || import_node_process2.default.env.TERM === "xterm-256color" || import_node_process2.default.env.TERM === "alacritty" || import_node_process2.default.env.TERMINAL_EMULATOR === "JetBrains-JediTerm";
12069
12046
  }
12070
12047
  var import_node_process2, import_picocolors2, import_sisteransi2, V2, u, le, L2, W2, C2, ue, o, d2, k2, P2, A2, T, F, $e, _2, me, de, pe, q, D, U, K2, b2, G2, ve, xe, Ie, Se, M2, J2, Y2;
12071
12048
  var init_dist2 = __esm({
12072
- "node_modules/@clack/prompts/dist/index.mjs"() {
12049
+ "../../node_modules/@clack/prompts/dist/index.mjs"() {
12073
12050
  init_dist();
12074
12051
  init_dist();
12075
12052
  import_node_process2 = __toESM(require("node:process"), 1);
@@ -12304,7 +12281,7 @@ var VERSION;
12304
12281
  var init_version = __esm({
12305
12282
  "src/version.ts"() {
12306
12283
  "use strict";
12307
- VERSION = "1.2.3";
12284
+ VERSION = "1.2.4";
12308
12285
  }
12309
12286
  });
12310
12287
 
@@ -12400,186 +12377,20 @@ function redactEvidenceLine(line) {
12400
12377
  function lineEvidence(file, pattern) {
12401
12378
  return file.content.split(/\r?\n/).map((line, index) => ({ line: line.trim(), lineNumber: index + 1 })).filter(({ line }) => pattern.test(line)).map(({ line, lineNumber }) => `${file.path}:${lineNumber}: ${redactEvidenceLine(line)}`);
12402
12379
  }
12403
- function lineNumberAt(content, index) {
12404
- return content.slice(0, index).split(/\r?\n/).length;
12405
- }
12406
- function statementEvidence(file, pattern) {
12407
- const evidence = [];
12408
- for (const match of file.content.matchAll(pattern)) {
12409
- evidence.push(
12410
- `${file.path}:${lineNumberAt(file.content, match.index ?? 0)}: ${redactEvidenceLine(
12411
- String(match[0]).replace(/\s+/g, " ").trim()
12412
- )}`
12413
- );
12414
- }
12415
- return evidence;
12416
- }
12417
- function normalizeIdent(value) {
12418
- return (value ?? "").replace(/^"|"$/g, "").toLowerCase();
12419
- }
12420
- function canonicalPublicTable(schema, table) {
12421
- const normalizedSchema = normalizeIdent(schema);
12422
- if (normalizedSchema && normalizedSchema !== "public") {
12423
- return void 0;
12424
- }
12425
- return `public.${normalizeIdent(table)}`;
12426
- }
12427
- function parseRelationRef(ref) {
12428
- const parts = ref.split(".").map((part) => normalizeIdent(part.trim()));
12429
- if (parts.length === 1) {
12430
- return `public.${parts[0]}`;
12431
- }
12432
- if (parts.length === 2 && parts[0] === "public") {
12433
- return `public.${parts[1]}`;
12434
- }
12435
- return void 0;
12436
- }
12437
- function relationEvidence(files, pattern, relationGroup = "relation") {
12438
- const evidence = [];
12439
- for (const file of files) {
12440
- for (const match of file.content.matchAll(pattern)) {
12441
- const groups = match.groups ?? {};
12442
- const relation = parseRelationRef(String(groups[relationGroup] ?? ""));
12443
- if (!relation) {
12444
- continue;
12445
- }
12446
- evidence.push({
12447
- relation,
12448
- evidence: `${file.path}:${lineNumberAt(file.content, match.index ?? 0)}: ${redactEvidenceLine(
12449
- String(match[0]).replace(/\s+/g, " ").trim()
12450
- )}`
12451
- });
12452
- }
12453
- }
12454
- return evidence;
12455
- }
12456
12380
  function sqlFiles(files) {
12457
12381
  return files.filter((file) => file.path.toLowerCase().endsWith(".sql"));
12458
12382
  }
12459
12383
  function buildRlsCheck(files) {
12460
12384
  const sql = sqlFiles(files);
12461
- const createdTables = [];
12462
- for (const file of sql) {
12463
- const pattern = /\bcreate\s+table\s+(?:if\s+not\s+exists\s+)?(?:(?<schema>"?[A-Za-z_][\w$]*"?)\.)?(?<table>"?[A-Za-z_][\w$]*"?)/gi;
12464
- for (const match of file.content.matchAll(pattern)) {
12465
- const relation = canonicalPublicTable(match.groups?.schema, String(match.groups?.table ?? ""));
12466
- if (!relation) {
12467
- continue;
12468
- }
12469
- createdTables.push({
12470
- relation,
12471
- evidence: `${file.path}:${lineNumberAt(file.content, match.index ?? 0)}: ${redactEvidenceLine(
12472
- String(match[0]).replace(/\s+/g, " ").trim()
12473
- )}`
12474
- });
12475
- }
12476
- }
12477
- const enabledTables = new Set(
12478
- relationEvidence(
12479
- sql,
12480
- /\balter\s+table\s+(?:if\s+exists\s+)?(?:only\s+)?(?<relation>(?:"?[A-Za-z_][\w$]*"?\.)?"?[A-Za-z_][\w$]*"?)\s+enable\s+row\s+level\s+security\b/gi
12481
- ).map((item4) => item4.relation)
12482
- );
12483
- const policyTables = new Set(
12484
- relationEvidence(
12485
- sql,
12486
- /\bcreate\s+policy\b[\s\S]*?\bon\s+(?<relation>(?:"?[A-Za-z_][\w$]*"?\.)?"?[A-Za-z_][\w$]*"?)[\s\S]*?;/gi
12487
- ).map((item4) => item4.relation)
12488
- );
12489
12385
  const rlsEvidence2 = sql.flatMap((file) => lineEvidence(file, /\benable\s+row\s+level\s+security\b/i));
12490
12386
  const policyEvidence = sql.flatMap((file) => lineEvidence(file, /\bcreate\s+policy\b/i));
12491
- const missingTableEvidence = createdTables.filter(({ relation }) => !enabledTables.has(relation) || !policyTables.has(relation)).map(({ relation, evidence }) => `Missing RLS/policy coverage for ${relation}; created at ${evidence}`);
12492
- const status = createdTables.length > 0 ? missingTableEvidence.length === 0 ? "pass" : "needs_work" : rlsEvidence2.length > 0 && policyEvidence.length > 0 ? "pass" : "needs_work";
12387
+ const status = rlsEvidence2.length > 0 && policyEvidence.length > 0 ? "pass" : "needs_work";
12493
12388
  return {
12494
12389
  id: "supabase-rls-policy-proof",
12495
12390
  title: "Supabase RLS policy proof",
12496
12391
  status,
12497
- summary: status === "pass" ? createdTables.length > 0 ? "Found table-level SQL evidence for row level security and matching policies on public tables." : "Found SQL evidence for row level security and at least one policy." : missingTableEvidence.length > 0 ? `Missing table-level RLS or policy evidence: ${missingTableEvidence.map((item4) => item4.match(/for ([^;]+)/)?.[1] ?? item4).join(", ")}.` : "Missing SQL evidence for both `enable row level security` and `create policy`.",
12498
- evidence: [...missingTableEvidence.slice(0, 5), ...rlsEvidence2.slice(0, 5), ...policyEvidence.slice(0, 5)]
12499
- };
12500
- }
12501
- function buildRlsPolicyQualityCheck(files) {
12502
- const sql = sqlFiles(files);
12503
- const evidence = sql.flatMap(
12504
- (file) => statementEvidence(file, /\bcreate\s+policy\b[\s\S]*?\b(?:using|with\s+check)\s*\(\s*true\s*\)[\s\S]*?;/gi)
12505
- );
12506
- const status = evidence.length === 0 ? "pass" : "needs_work";
12507
- return {
12508
- id: "supabase-rls-policy-quality",
12509
- title: "Supabase RLS policy quality",
12510
- status,
12511
- summary: status === "pass" ? "No permissive `using (true)` or `with check (true)` policy evidence found." : "Found RLS policies that allow every row. Public access may be intentional, but it must be reviewed before production.",
12512
- evidence: evidence.slice(0, 5)
12513
- };
12514
- }
12515
- function buildViewSecurityInvokerCheck(files) {
12516
- const sql = sqlFiles(files);
12517
- const secureViews = /* @__PURE__ */ new Set();
12518
- const insecureEvidence = [];
12519
- for (const file of sql) {
12520
- for (const match of file.content.matchAll(
12521
- /\bcreate\s+(?:or\s+replace\s+)?view\s+(?<relation>(?:"?[A-Za-z_][\w$]*"?\.)?"?[A-Za-z_][\w$]*"?)[\s\S]*?;/gi
12522
- )) {
12523
- const relation = parseRelationRef(String(match.groups?.relation ?? ""));
12524
- if (!relation) {
12525
- continue;
12526
- }
12527
- const statement = String(match[0]);
12528
- if (/\bsecurity_invoker\b/i.test(statement)) {
12529
- secureViews.add(relation);
12530
- continue;
12531
- }
12532
- insecureEvidence.push({
12533
- relation,
12534
- evidence: `${file.path}:${lineNumberAt(file.content, match.index ?? 0)}: ${redactEvidenceLine(
12535
- statement.replace(/\s+/g, " ").trim()
12536
- )}`
12537
- });
12538
- }
12539
- for (const match of file.content.matchAll(
12540
- /\balter\s+view\s+(?<relation>(?:"?[A-Za-z_][\w$]*"?\.)?"?[A-Za-z_][\w$]*"?)[\s\S]*?\bsecurity_invoker\s*=\s*true\b[\s\S]*?;/gi
12541
- )) {
12542
- const relation = parseRelationRef(String(match.groups?.relation ?? ""));
12543
- if (relation) {
12544
- secureViews.add(relation);
12545
- }
12546
- }
12547
- }
12548
- const evidence = insecureEvidence.filter(({ relation }) => !secureViews.has(relation)).map(({ evidence: evidence2 }) => evidence2);
12549
- const status = evidence.length === 0 ? "pass" : "needs_work";
12550
- return {
12551
- id: "supabase-view-security-invoker",
12552
- title: "Supabase view security invoker",
12553
- status,
12554
- summary: status === "pass" ? "No public view evidence without `security_invoker` found." : "Found public Supabase views without `security_invoker` evidence. Views can bypass underlying RLS by default.",
12555
- evidence: evidence.slice(0, 5)
12556
- };
12557
- }
12558
- function buildSecurityDefinerBoundaryCheck(files) {
12559
- const sql = sqlFiles(files);
12560
- const evidence = [];
12561
- for (const file of sql) {
12562
- for (const match of file.content.matchAll(
12563
- /\bcreate\s+(?:or\s+replace\s+)?function\s+(?<relation>(?:"?[A-Za-z_][\w$]*"?\.)?"?[A-Za-z_][\w$]*"?)[\s\S]*?\bsecurity\s+definer\b[\s\S]*?;/gi
12564
- )) {
12565
- const relation = parseRelationRef(String(match.groups?.relation ?? ""));
12566
- if (!relation) {
12567
- continue;
12568
- }
12569
- evidence.push(
12570
- `${file.path}:${lineNumberAt(file.content, match.index ?? 0)}: ${redactEvidenceLine(
12571
- String(match[0]).replace(/\s+/g, " ").trim()
12572
- )}`
12573
- );
12574
- }
12575
- }
12576
- const status = evidence.length === 0 ? "pass" : "needs_work";
12577
- return {
12578
- id: "supabase-security-definer-boundary",
12579
- title: "Supabase security definer boundary",
12580
- status,
12581
- summary: status === "pass" ? "No `security definer` functions in the public schema were found." : "Found `security definer` functions in the public schema. Keep privileged functions in a private, unexposed schema.",
12582
- evidence: evidence.slice(0, 5)
12392
+ summary: status === "pass" ? "Found SQL evidence for row level security and at least one policy." : "Missing SQL evidence for both `enable row level security` and `create policy`.",
12393
+ evidence: [...rlsEvidence2.slice(0, 5), ...policyEvidence.slice(0, 5)]
12583
12394
  };
12584
12395
  }
12585
12396
  function buildPoolerCheck(files) {
@@ -12623,9 +12434,6 @@ function buildServiceRoleCheck(files) {
12623
12434
  function buildVercelSupabaseAudit(input) {
12624
12435
  const checks = [
12625
12436
  buildRlsCheck(input.files),
12626
- buildRlsPolicyQualityCheck(input.files),
12627
- buildViewSecurityInvokerCheck(input.files),
12628
- buildSecurityDefinerBoundaryCheck(input.files),
12629
12437
  buildPoolerCheck(input.files),
12630
12438
  buildServiceRoleCheck(input.files)
12631
12439
  ];
@@ -12874,7 +12682,7 @@ function normalizeLineEndings(value) {
12874
12682
  function escapeRegExp3(value) {
12875
12683
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
12876
12684
  }
12877
- 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, PRODUCTION_PROTOCOL, AGENT_NEGATIVE_CONSTRAINTS, ANCHOR_FOOTER, AGENT_RULES_BODY, AGENT_CONTEXT_BODY, MISSION_MAP_BODY, BLOCK_MARKER_PAIRS;
12685
+ 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;
12878
12686
  var init_agentRulesBlock = __esm({
12879
12687
  "src/commands/agentRulesBlock.ts"() {
12880
12688
  "use strict";
@@ -12898,15 +12706,6 @@ Maps repo evidence to OWASP LLM Top 10 launch risks. Supports SAFECode-style dep
12898
12706
  STACK_FRAMING = `Stack context for this repo: Next.js, Supabase, Vercel, VibeRaven.
12899
12707
  Do NOT deploy to Vercel or ship to production users without running \`${PUBLIC_AGENT_MODE_COMMAND}\`.
12900
12708
  Do NOT rely on manual production checklists when VibeRaven gate artifacts exist in \`.viberaven/\`.`;
12901
- PRODUCTION_PROTOCOL = `## VibeRaven Production Protocol
12902
-
12903
- Before making deploy, auth, database, payment, API, or scaling changes:
12904
-
12905
- 1. Read \`.viberaven/prp.json\`.
12906
- 2. Follow \`.viberaven/mission-map.md\`.
12907
- 3. Do not deploy if \`decision.status\` is \`blocked\`.
12908
- 4. Fix \`nextActions\` in order.
12909
- 5. Re-run \`${PUBLIC_AGENT_MODE_COMMAND}\`.`;
12910
12709
  AGENT_NEGATIVE_CONSTRAINTS = `## Event-Triggered Production Verification
12911
12710
 
12912
12711
  Trigger: modifications to \`package.json\`, \`vercel.json\`, \`supabase/migrations/**/*\`, \`.env.example\`, auth/billing/webhook paths, or deploy workflow files.
@@ -12932,8 +12731,6 @@ Gate is not clear until \`gate.status === "clear"\` in \`.viberaven/gate-result.
12932
12731
 
12933
12732
  ${STACK_FRAMING}
12934
12733
 
12935
- ${PRODUCTION_PROTOCOL}
12936
-
12937
12734
  ${AGENT_NEGATIVE_CONSTRAINTS}
12938
12735
 
12939
12736
  ## VibeRaven Production-Readiness Gate
@@ -12983,11 +12780,10 @@ Do not stop at "scan complete." The loop is not done until \`gate.status === 'cl
12983
12780
 
12984
12781
  After \`--agent-mode\`, read these artifacts in order:
12985
12782
 
12986
- 1. \`.viberaven/prp.json\`
12987
- 2. \`.viberaven/mission-map.md\`
12988
- 3. \`.viberaven/agent-tasklist.md\`
12989
- 4. \`.viberaven/gate-result.json\`
12990
- 5. \`.viberaven/context-map.json\``;
12783
+ 1. \`.viberaven/mission-map.md\`
12784
+ 2. \`.viberaven/agent-tasklist.md\`
12785
+ 3. \`.viberaven/gate-result.json\`
12786
+ 4. \`.viberaven/context-map.json\``;
12991
12787
  MISSION_MAP_BODY = `${AGENT_RULES_PREAMBLE}
12992
12788
 
12993
12789
  ## Mission Map loop
@@ -15959,10 +15755,6 @@ function providerLogoHtml(providerOrLabel, labelFallback, ...moreCandidates) {
15959
15755
  }
15960
15756
  function providerRailLogoHtml(providerOrLabel, labelFallback, ...moreCandidates) {
15961
15757
  const key = resolveProviderLogoKey(providerOrLabel, labelFallback, ...moreCandidates) || normalizeProviderKey2(providerOrLabel);
15962
- const assetUrl = key ? providerAssetUrl(key) : void 0;
15963
- if (assetUrl) {
15964
- return providerIconImgHtml(assetUrl, key);
15965
- }
15966
15758
  const iconUrl = key && LOCAL_UI_BRAND_ICON_KEYS.has(key) ? PROVIDER_ICON_URLS[key] : void 0;
15967
15759
  if (iconUrl) {
15968
15760
  return providerIconImgHtml(iconUrl, key);
@@ -16098,18 +15890,6 @@ var init_providerLogos = __esm({
16098
15890
  "svelte"
16099
15891
  ]);
16100
15892
  PROVIDER_ASSET_FILES = {
16101
- auth: "provider-auth.png",
16102
- clerk: "provider-clerk.png",
16103
- database: "provider-database.png",
16104
- env: "provider-env.png",
16105
- github: "provider-github.png",
16106
- posthog: "provider-posthog.png",
16107
- resend: "provider-resend.png",
16108
- sentry: "provider-sentry.png",
16109
- stripe: "provider-stripe.png",
16110
- supabase: "provider-supabase.png",
16111
- upstash: "provider-upstash.png",
16112
- vercel: "provider-vercel.png",
16113
15893
  authjs: "provider-authjs.svg",
16114
15894
  logrocket: "provider-logrocket.svg",
16115
15895
  aws: "provider-aws.svg"
@@ -16127,16 +15907,16 @@ var init_providerLogos = __esm({
16127
15907
  turso: "https://cdn.simpleicons.org/turso/4FF8D2",
16128
15908
  "lemon-squeezy": "https://cdn.simpleicons.org/lemonsqueezy/FFC233",
16129
15909
  stripe: "https://cdn.simpleicons.org/stripe/635BFF",
16130
- github: "https://cdn.simpleicons.org/github/181717",
15910
+ github: "https://cdn.simpleicons.org/github/FFFFFF",
16131
15911
  gitlab: "https://cdn.simpleicons.org/gitlab/FC6D26",
16132
- vercel: "https://cdn.simpleicons.org/vercel/000000",
15912
+ vercel: "https://cdn.simpleicons.org/vercel/FFFFFF",
16133
15913
  netlify: "https://cdn.simpleicons.org/netlify/00C7B7",
16134
15914
  render: "https://cdn.simpleicons.org/render/46E3B7",
16135
15915
  railway: "https://cdn.simpleicons.org/railway/0B0D0E",
16136
15916
  cloudflare: "https://cdn.simpleicons.org/cloudflare/F38020",
16137
- sentry: "https://cdn.simpleicons.org/sentry/362D59",
15917
+ sentry: "https://cdn.simpleicons.org/sentry/8B5CF6",
16138
15918
  posthog: "https://cdn.simpleicons.org/posthog/F54E00",
16139
- resend: "https://cdn.simpleicons.org/resend/000000",
15919
+ resend: "https://cdn.simpleicons.org/resend/FFFFFF",
16140
15920
  upstash: "https://cdn.simpleicons.org/upstash/00E9A3",
16141
15921
  playwright: "https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/playwright/playwright-original.svg",
16142
15922
  figma: "https://cdn.simpleicons.org/figma/F24E1E",
@@ -16964,7 +16744,6 @@ const demo = {
16964
16744
  activeModal: null,
16965
16745
  activeStudioTab: 'chat',
16966
16746
  selectedCliId: 'codex',
16967
- agentPermissionMode: 'ask',
16968
16747
  cliConnected: false,
16969
16748
  cliSetupComplete: false,
16970
16749
  cliConnectTargetId: null,
@@ -16983,11 +16762,13 @@ const demo = {
16983
16762
  selectedModelId: 'gpt-5.5',
16984
16763
  selectedReasoning: 'High',
16985
16764
  selectedContext: 'Production',
16765
+ selectedAccessMode: 'approve',
16986
16766
  modelMenuOpen: false,
16987
16767
  reasoningMenuOpen: false,
16988
16768
  contextMenuOpen: false,
16769
+ accessMenuOpen: false,
16989
16770
  pickerChatIndex: 0,
16990
- chatContexts: [{ providerId: null, releaseId: null, cliId: 'codex', modelId: 'gpt-5.5', reasoning: 'High', context: 'Production', messages: [], activeTask: null }],
16771
+ chatContexts: [{ providerId: null, releaseId: null, cliId: 'codex', modelId: 'gpt-5.5', reasoning: 'High', context: 'Production', accessMode: 'approve', messages: [], activeTask: null }],
16991
16772
  activeRecentChatId: null,
16992
16773
  droppedProviderId: null,
16993
16774
  dragKind: null,
@@ -17001,23 +16782,6 @@ const demo = {
17001
16782
  releaseChangelog: { entries: [], from: '', to: '', fromLabel: '', toLabel: '', loading: false, error: '' },
17002
16783
  };
17003
16784
 
17004
- function readPermissionMode() {
17005
- try {
17006
- const raw = localStorage.getItem('viberaven:agentChatPermissionMode');
17007
- return raw === 'full-access' ? 'full-access' : 'ask';
17008
- } catch {
17009
- return 'ask';
17010
- }
17011
- }
17012
-
17013
- function savePermissionMode(mode) {
17014
- try {
17015
- localStorage.setItem('viberaven:agentChatPermissionMode', mode);
17016
- } catch {
17017
- // Ignore storage failures in preview shells.
17018
- }
17019
- }
17020
-
17021
16785
  function readCliSessionReady() {
17022
16786
  try {
17023
16787
  let raw = localStorage.getItem('viberaven-cli-ready');
@@ -17040,7 +16804,6 @@ function writeCliSessionReady(value) {
17040
16804
  }
17041
16805
 
17042
16806
  demo.cliSessionReady = readCliSessionReady();
17043
- demo.agentPermissionMode = readPermissionMode();
17044
16807
 
17045
16808
  function cliAgentInstalled(agent) {
17046
16809
  if (!agent) return false;
@@ -17123,10 +16886,10 @@ const providerMarkAssets = {
17123
16886
  };
17124
16887
 
17125
16888
  const mascotAssets = {
17126
- idle: '/report/assets/viberaven-mascot-idle.png',
17127
- fixing: '/report/assets/viberaven-mascot-fixing.png',
17128
- verified: '/report/assets/viberaven-mascot-verified.png',
17129
- alert: '/report/assets/viberaven-mascot-alert.png',
16889
+ idle: '/report/assets/viberaven-mascot.png',
16890
+ fixing: '/report/assets/viberaven-mascot.png',
16891
+ verified: '/report/assets/viberaven-mascot.png',
16892
+ alert: '/report/assets/viberaven-mascot.png',
17130
16893
  };
17131
16894
 
17132
16895
  const providerDashboardUrls = {
@@ -17335,6 +17098,52 @@ function providerHealthIcon(health) {
17335
17098
  return Icon('refresh');
17336
17099
  }
17337
17100
 
17101
+ function providerMcp(provider) {
17102
+ return provider && provider.mcp ? provider.mcp : {
17103
+ status: 'disconnected',
17104
+ label: 'Connect MCP',
17105
+ connectCommand: 'Add this provider to your local MCP config, then drag it into chat.',
17106
+ capabilities: [],
17107
+ };
17108
+ }
17109
+
17110
+ function providerMcpChip(provider) {
17111
+ const mcp = providerMcp(provider);
17112
+ const connected = mcp.status === 'connected';
17113
+ const label = connected ? 'MCP connected' : 'MCP not connected';
17114
+ return '<span class="vr-provider-mcp-chip ' + (connected ? 'is-connected' : 'is-disconnected') + '" title="' + escapeHtml(label) + '" aria-label="' + escapeHtml(label) + '">' +
17115
+ '<i></i><span>' + escapeHtml(label) + '</span>' +
17116
+ '</span>';
17117
+ }
17118
+
17119
+ function providerAssetSrc(provider) {
17120
+ if (!provider) return providerAssets.supabase;
17121
+ return providerMarkAssets[provider.id] || providerAssets[provider.id] || providerAssets.supabase;
17122
+ }
17123
+
17124
+ function ProviderToken(provider, extraClass) {
17125
+ const className = 'vr-provider-token' + (extraClass ? ' ' + extraClass : '');
17126
+ const iconHtml = provider && provider.iconHtml ? String(provider.iconHtml) : '';
17127
+ return '<span class="' + className + '">' + (iconHtml.trim() ? iconHtml : '<img src="' + providerAssetSrc(provider) + '" alt="" />') + '</span>';
17128
+ }
17129
+
17130
+ function ProviderLogo(provider, className) {
17131
+ const iconHtml = provider && provider.iconHtml ? String(provider.iconHtml) : '';
17132
+ return iconHtml.trim()
17133
+ ? '<span class="' + className + ' is-logo-html">' + iconHtml + '</span>'
17134
+ : '<img class="' + className + '" src="' + providerAssetSrc(provider) + '" alt="" />';
17135
+ }
17136
+
17137
+ function ProviderMcpPanel(provider) {
17138
+ const mcp = providerMcp(provider);
17139
+ const connected = mcp.status === 'connected';
17140
+ const capabilities = Array.isArray(mcp.capabilities) && mcp.capabilities.length ? mcp.capabilities : ['provider context', 'repo-aware chat'];
17141
+ return '<section class="vr-provider-mcp-panel ' + (connected ? 'is-connected' : 'is-disconnected') + '">' +
17142
+ '<header><span>' + Icon('agent') + '</span><div><strong>' + escapeHtml(connected ? 'MCP connected' : 'Connect MCP') + '</strong><em>' + escapeHtml(connected ? (mcp.source || 'local MCP config') : (mcp.connectCommand || 'Add this provider MCP server in your local MCP config.')) + '</em></div></header>' +
17143
+ '<div>' + capabilities.map((item) => '<small>' + escapeHtml(item) + '</small>').join('') + '</div>' +
17144
+ '</section>';
17145
+ }
17146
+
17338
17147
  function normalizeProvider(provider) {
17339
17148
  const missing = provider.state === 'not_detected';
17340
17149
  return {
@@ -17348,6 +17157,8 @@ function normalizeProvider(provider) {
17348
17157
  lastSync: currentState.project && currentState.project.scannedAt ? 'scan artifact' : 'startup',
17349
17158
  checks: providerChecks(provider),
17350
17159
  state: provider.state || 'not_detected',
17160
+ mcp: provider.mcp || null,
17161
+ iconHtml: provider.iconHtml || '',
17351
17162
  };
17352
17163
  }
17353
17164
 
@@ -17552,6 +17363,11 @@ let cliModels = {
17552
17363
 
17553
17364
  const reasoningLevels = ['Low', 'Medium', 'High', 'Extra High'];
17554
17365
  const contextLevels = ['Production', 'Provider', 'Release', 'Security'];
17366
+ const accessModes = [
17367
+ { id: 'ask', label: 'Ask approval', description: 'Confirm edits and external access' },
17368
+ { id: 'approve', label: 'Approve for me', description: 'Run safe repo actions automatically' },
17369
+ { id: 'full', label: 'Full access', description: 'Unrestricted local project access' },
17370
+ ];
17555
17371
  const topStages = [
17556
17372
  { id: 'provider', number: '1', label: 'Provider Proof', state: 'Waiting' },
17557
17373
  { id: 'agent', number: '2', label: 'Agent Fix', state: 'Ready' },
@@ -17653,6 +17469,7 @@ function buildRecentChatEntry(prompt, provider, release, chat, existing) {
17653
17469
  modelId: lane.modelId || base.modelId || demo.selectedModelId || 'gpt-5.5',
17654
17470
  reasoning: lane.reasoning || base.reasoning || demo.selectedReasoning || 'High',
17655
17471
  context: lane.context || base.context || demo.selectedContext || 'Production',
17472
+ accessMode: lane.accessMode || base.accessMode || demo.selectedAccessMode || 'approve',
17656
17473
  messages: serializeChatMessages(lane.messages),
17657
17474
  };
17658
17475
  }
@@ -17680,6 +17497,7 @@ function syncDemoFromChatContext(context) {
17680
17497
  demo.selectedModelId = context.modelId;
17681
17498
  demo.selectedReasoning = context.reasoning;
17682
17499
  demo.selectedContext = context.context;
17500
+ demo.selectedAccessMode = context.accessMode || 'approve';
17683
17501
  if (context.providerId) demo.selectedProviderId = context.providerId;
17684
17502
  if (context.releaseId) demo.selectedReleaseId = context.releaseId;
17685
17503
  syncPrimaryContext();
@@ -17694,6 +17512,7 @@ function restoreRecentChat(recent) {
17694
17512
  modelId: recent.modelId,
17695
17513
  reasoning: recent.reasoning,
17696
17514
  context: recent.context,
17515
+ accessMode: recent.accessMode,
17697
17516
  messages: recent.messages || [],
17698
17517
  draft: '',
17699
17518
  activeTask: null,
@@ -17913,6 +17732,9 @@ function contextIconChip(iconName, size) {
17913
17732
  }
17914
17733
 
17915
17734
  function BrandMark(id) {
17735
+ if (id === 'github') {
17736
+ return '<span class="vr-inline-brand-mark is-github"><svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M12 2C6.48 2 2 6.58 2 12.26c0 4.52 2.87 8.35 6.86 9.71.5.1.69-.22.69-.49 0-.24-.01-.87-.01-1.7-2.78.62-3.37-1.36-3.37-1.36-.45-1.17-1.11-1.48-1.11-1.48-.91-.64.07-.63.07-.63 1 .07 1.53 1.05 1.53 1.05.9 1.56 2.36 1.11 2.94.85.09-.67.35-1.11.63-1.37-2.22-.26-4.56-1.14-4.56-5.07 0-1.12.39-2.03 1.03-2.75-.1-.26-.45-1.3.1-2.7 0 0 .84-.28 2.75 1.05A9.2 9.2 0 0 1 12 6.84c.85 0 1.71.12 2.51.34 1.91-1.33 2.75-1.05 2.75-1.05.55 1.4.2 2.44.1 2.7.64.72 1.03 1.63 1.03 2.75 0 3.94-2.34 4.8-4.57 5.06.36.32.68.94.68 1.9 0 1.37-.01 2.47-.01 2.8 0 .27.18.6.7.49A10.03 10.03 0 0 0 22 12.26C22 6.58 17.52 2 12 2Z"/></svg></span>';
17737
+ }
17916
17738
  return '<span class="vr-inline-brand-mark"><img src="' + escapeHtml(providerMarkAssets[id] || providerAssets[id]) + '" alt="" /></span>';
17917
17739
  }
17918
17740
 
@@ -17938,6 +17760,7 @@ function defaultChatContext(seed) {
17938
17760
  modelId: source.modelId || demo.selectedModelId || normalizeModelId(models[0]),
17939
17761
  reasoning: source.reasoning || demo.selectedReasoning || 'High',
17940
17762
  context: source.context || demo.selectedContext || 'Production',
17763
+ accessMode: source.accessMode || demo.selectedAccessMode || 'approve',
17941
17764
  draft: source.draft || '',
17942
17765
  messages: Array.isArray(source.messages) ? source.messages.slice() : [],
17943
17766
  activeTask: source.activeTask || null,
@@ -18067,7 +17890,7 @@ function resolveQuickActionMission(kind, chatIndex, promptOverride) {
18067
17890
  prompt: [
18068
17891
  'Analyze production launch readiness in this project folder.',
18069
17892
  '',
18070
- 'Gate: ' + gateLabel + ' (' + (gate.status || 'unknown') + ')',
17893
+ 'Project status: ' + gateLabel + ' (' + (gate.status || 'unknown') + ')',
18071
17894
  'Main blocker: ' + mainBlocker,
18072
17895
  blockers.length ? 'Known blockers from the latest scan:\\n' + blockerText : 'No blockers loaded yet \u2014 inspect .viberaven/gate-result.json and agent-tasklist.md if present.',
18073
17896
  connectText ? '\\nProviders needing connect:\\n' + connectText : '',
@@ -18083,11 +17906,11 @@ function resolveQuickActionMission(kind, chatIndex, promptOverride) {
18083
17906
  },
18084
17907
  plan: {
18085
17908
  title: 'Building fix plan',
18086
- label: planSteps.length ? 'Production fix plan \xB7 ' + planSteps.length + ' step' + (planSteps.length === 1 ? '' : 's') : 'Build production fix plan',
17909
+ label: planSteps.length ? 'Fix plan \xB7 ' + planSteps.length + ' action' + (planSteps.length === 1 ? '' : 's') : 'Build fix plan',
18087
17910
  prompt: [
18088
- 'Build a safe, ordered production fix plan for this launch.',
17911
+ 'Build a safe, ordered fix plan for this launch.',
18089
17912
  '',
18090
- 'Gate: ' + gateLabel,
17913
+ 'Project status: ' + gateLabel,
18091
17914
  'Main blocker: ' + mainBlocker,
18092
17915
  blockers.length ? 'Blockers (' + blockers.length + '):\\n' + blockerText : 'No scan blockers loaded \u2014 inspect .viberaven artifacts first.',
18093
17916
  planSteps.length ? '\\nSuggested steps from scan:\\n' + planText : '',
@@ -18127,11 +17950,11 @@ function resolveQuickActionMission(kind, chatIndex, promptOverride) {
18127
17950
  },
18128
17951
  verify: {
18129
17952
  title: 'Running verification',
18130
- label: gateClear ? 'Re-verify launch gate' : 'Verify launch gate',
17953
+ label: gateClear ? 'Re-verify readiness' : 'Verify readiness',
18131
17954
  prompt: [
18132
17955
  'Verify production launch readiness for this project.',
18133
17956
  '',
18134
- 'Gate: ' + gateLabel + ' (' + (gate.status || 'unknown') + ')',
17957
+ 'Project status: ' + gateLabel + ' (' + (gate.status || 'unknown') + ')',
18135
17958
  'Main blocker: ' + mainBlocker,
18136
17959
  blockers.length ? 'Open blockers:\\n' + blockerText : '',
18137
17960
  '',
@@ -18142,7 +17965,7 @@ function resolveQuickActionMission(kind, chatIndex, promptOverride) {
18142
17965
  ].filter(Boolean).join('\\n'),
18143
17966
  line: 'Checking evidence, provider state, and release readiness.',
18144
17967
  steps: ['Run checks', 'Read proof', 'Mark status'],
18145
- notice: gateClear ? 'Re-verifying launch proof.' : 'Running launch gate verification.',
17968
+ notice: gateClear ? 'Re-verifying launch proof.' : 'Running production readiness verification.',
18146
17969
  },
18147
17970
  diff: {
18148
17971
  title: 'Reviewing diff',
@@ -18150,7 +17973,7 @@ function resolveQuickActionMission(kind, chatIndex, promptOverride) {
18150
17973
  prompt: [
18151
17974
  'Review the current production diff for this project.',
18152
17975
  '',
18153
- 'Gate: ' + gateLabel,
17976
+ 'Project status: ' + gateLabel,
18154
17977
  topBlocker ? 'Top blocker context: ' + topBlocker.title : '',
18155
17978
  '',
18156
17979
  'Show grouped changes, flag risky edits, and say what still needs verify before release.',
@@ -18165,7 +17988,7 @@ function resolveQuickActionMission(kind, chatIndex, promptOverride) {
18165
17988
  prompt: [
18166
17989
  'Explain recent production changes and what to verify next.',
18167
17990
  '',
18168
- 'Gate: ' + gateLabel,
17991
+ 'Project status: ' + gateLabel,
18169
17992
  mainBlocker ? 'Main blocker: ' + mainBlocker : '',
18170
17993
  blockers.length ? 'Active blockers:\\n' + blockerText : '',
18171
17994
  '',
@@ -18269,7 +18092,7 @@ function quickActionButtonSpec(index) {
18269
18092
  action: 'plan',
18270
18093
  icon: 'report',
18271
18094
  label: planSteps.length ? 'Plan \xB7 ' + planSteps.length : 'Plan',
18272
- hint: planSteps.length ? 'Build production fix plan \xB7 ' + planSteps.length + ' steps' : 'Build production fix plan',
18095
+ hint: planSteps.length ? 'Build fix plan \xB7 ' + planSteps.length + ' actions' : 'Build fix plan',
18273
18096
  primary: true,
18274
18097
  },
18275
18098
  {
@@ -18284,7 +18107,7 @@ function quickActionButtonSpec(index) {
18284
18107
  action: 'verify-now',
18285
18108
  icon: 'shield',
18286
18109
  label: gateClear ? 'Re-verify' : 'Verify',
18287
- hint: gateClear ? 'Re-verify launch gate' : 'Verify launch gate',
18110
+ hint: gateClear ? 'Re-verify readiness' : 'Verify readiness',
18288
18111
  primary: true,
18289
18112
  },
18290
18113
  {
@@ -18373,7 +18196,7 @@ function runChatTask(kind, promptOverride) {
18373
18196
  demo.notice = 'Verification finished in chat. Launch proof is green.';
18374
18197
  } else if (kind === 'fix') {
18375
18198
  demo.activeStepId = 'verify';
18376
- demo.notice = 'Fix prepared in chat. Run Verify to clear the launch gate.';
18199
+ demo.notice = 'Fix prepared in chat. Run Verify to update production readiness.';
18377
18200
  } else {
18378
18201
  demo.notice = taskMeta(kind).title + ' finished in chat.';
18379
18202
  }
@@ -18395,12 +18218,12 @@ function runChatTask(kind, promptOverride) {
18395
18218
  modelId: selectedModelLabel(chatIndex),
18396
18219
  reasoning: chat.reasoning,
18397
18220
  context: chat.context,
18221
+ accessMode: chat.accessMode || 'approve',
18398
18222
  prompt,
18399
18223
  displayPrompt: label,
18400
18224
  taskKind: kind,
18401
- permissionMode: demo.agentPermissionMode,
18402
- provider: { area: provider.area, name: provider.name },
18403
- release: release ? { label: release.label } : undefined
18225
+ provider: { id: provider.id, area: provider.area, name: provider.name },
18226
+ release: release ? { id: release.id, label: release.label } : undefined
18404
18227
  })
18405
18228
  })
18406
18229
  .then(async (response) => {
@@ -18429,7 +18252,7 @@ function sendChatInput(chatIndex) {
18429
18252
  }
18430
18253
  chat.draft = '';
18431
18254
  if (input && 'value' in input) input.value = '';
18432
- runChatTask(chat.providerId ? 'provider' : 'analyze', value);
18255
+ runChatTask(chat.releaseId ? 'diff' : chat.providerId ? 'provider' : 'analyze', value);
18433
18256
  }
18434
18257
 
18435
18258
  function resetLaunchPreview() {
@@ -18449,11 +18272,13 @@ function resetLaunchPreview() {
18449
18272
  demo.selectedModelId = 'gpt-5.5';
18450
18273
  demo.selectedReasoning = 'High';
18451
18274
  demo.selectedContext = 'Production';
18275
+ demo.selectedAccessMode = 'approve';
18452
18276
  demo.modelMenuOpen = false;
18453
18277
  demo.reasoningMenuOpen = false;
18454
18278
  demo.contextMenuOpen = false;
18279
+ demo.accessMenuOpen = false;
18455
18280
  demo.pickerChatIndex = 0;
18456
- demo.chatContexts = [defaultChatContext({ cliId: 'codex', modelId: 'gpt-5.5', reasoning: 'High', context: 'Production' })];
18281
+ demo.chatContexts = [defaultChatContext({ cliId: 'codex', modelId: 'gpt-5.5', reasoning: 'High', context: 'Production', accessMode: 'approve' })];
18457
18282
  demo.droppedProviderId = null;
18458
18283
  demo.droppedReleaseId = null;
18459
18284
  clearChatTaskTimer();
@@ -18523,10 +18348,10 @@ function RecentChatRail() {
18523
18348
 
18524
18349
  function ProviderCard(provider) {
18525
18350
  const slot = stackSlots[provider.id] || provider.area;
18526
- return '<button class="vr-provider-card ' + (provider.id === demo.selectedProviderId ? 'is-selected' : '') + '" type="button" draggable="true" data-provider="' + escapeHtml(provider.id) + '" aria-label="Open ' + escapeHtml(provider.name) + ' stack slot">' +
18527
- '<span class="vr-provider-token"><img src="' + (providerMarkAssets[provider.id] || providerAssets[provider.id]) + '" alt="" /></span>' +
18528
- '<span class="vr-provider-light"></span>' +
18351
+ return '<button class="vr-provider-card ' + (provider.id === demo.selectedProviderId ? 'is-selected' : '') + '" type="button" draggable="true" data-provider="' + escapeHtml(provider.id) + '" data-provider-tone="' + escapeHtml(provider.tone || 'neutral') + '" aria-label="Open ' + escapeHtml(provider.name) + ' stack slot">' +
18352
+ ProviderToken(provider) +
18529
18353
  '<div><small>' + escapeHtml(slot) + '</small><strong>' + escapeHtml(provider.name) + '</strong><em>' + escapeHtml(provider.status) + '</em></div>' +
18354
+ providerMcpChip(provider) +
18530
18355
  '<span class="vr-provider-proof-chip"><span>Open</span>' + Icon('chevronRight') + '</span>' +
18531
18356
  '</button>';
18532
18357
  }
@@ -18534,9 +18359,10 @@ function ProviderCard(provider) {
18534
18359
  function ProviderDetailPanel() {
18535
18360
  const provider = providers.find((item) => item.id === demo.selectedProviderId) || providers[0];
18536
18361
  return '<aside class="vr-provider-detail-panel">' +
18537
- '<div class="vr-detail-head"><span class="vr-provider-token is-large"><img src="' + (providerMarkAssets[provider.id] || providerAssets[provider.id]) + '" alt="" /></span><div><span>' + escapeHtml(provider.area) + '</span><strong>' + escapeHtml(provider.name) + '</strong></div>' + StatusBadge(provider.status, provider.tone) + '</div>' +
18362
+ '<div class="vr-detail-head">' + ProviderToken(provider, 'is-large') + '<div><span>' + escapeHtml(provider.area) + '</span><strong>' + escapeHtml(provider.name) + '</strong></div>' + StatusBadge(provider.status, provider.tone) + '</div>' +
18538
18363
  '<p>' + escapeHtml(provider.detail) + '</p>' +
18539
18364
  '<div class="vr-provider-stats"><span><b>Checks</b><em>' + escapeHtml(provider.checks) + '</em></span><span><b>Region</b><em>' + escapeHtml(provider.region) + '</em></span><span><b>Last sync</b><em>' + escapeHtml(provider.lastSync) + '</em></span></div>' +
18365
+ ProviderMcpPanel(provider) +
18540
18366
  '<a class="vr-provider-dashboard" href="' + escapeHtml(providerDashboardUrls[provider.id] || docsUrl) + '" target="_blank" rel="noreferrer">Open dashboard ' + Icon('external') + '</a>' +
18541
18367
  '</aside>';
18542
18368
  }
@@ -18557,7 +18383,7 @@ function ProviderPicker() {
18557
18383
  return '<div class="vr-provider-picker" aria-label="Provider picker">' +
18558
18384
  '<div><strong>Connector picker</strong><span>Select one provider. VibeRaven opens its proof panel and next action.</span></div>' +
18559
18385
  '<div class="vr-provider-picker-list">' + boardProviders.map((provider) =>
18560
- '<button type="button" data-provider="' + escapeHtml(provider.id) + '"><span class="vr-provider-token is-picker"><img src="' + (providerMarkAssets[provider.id] || providerAssets[provider.id]) + '" alt="" /></span><span>' + escapeHtml(provider.name) + '</span></button>'
18386
+ '<button type="button" data-provider="' + escapeHtml(provider.id) + '">' + ProviderToken(provider, 'is-picker') + '<span>' + escapeHtml(provider.name) + '</span></button>'
18561
18387
  ).join('') + '</div>' +
18562
18388
  '</div>';
18563
18389
  }
@@ -18613,11 +18439,12 @@ function ProviderModal() {
18613
18439
  const checkRows = providerModalCheckRows(raw);
18614
18440
  return ModalShell('provider',
18615
18441
  '<header class="vr-modal-provider-head">' +
18616
- '<img class="vr-modal-provider-logo" src="' + (providerMarkAssets[provider.id] || providerAssets[provider.id]) + '" alt="" />' +
18442
+ ProviderLogo(provider, 'vr-modal-provider-logo') +
18617
18443
  '<div><h2>' + escapeHtml(provider.name) + '</h2>' + StatusBadge(provider.status, provider.tone) + '<p>' + escapeHtml(provider.area) + ' provider</p></div>' +
18618
18444
  '<img class="vr-modal-mascot" src="' + providerModalMascot(health) + '" alt="" />' +
18619
18445
  '</header>' +
18620
18446
  '<section class="vr-modal-health is-' + escapeHtml(health.tone) + '"><span>' + providerHealthIcon(health) + '</span><div><strong>' + escapeHtml(health.label) + '</strong><em>' + escapeHtml(health.summary) + '</em></div><b>' + escapeHtml(health.meta) + '</b></section>' +
18447
+ ProviderMcpPanel(provider) +
18621
18448
  '<div class="vr-modal-action-grid">' +
18622
18449
  GlowButton('Open slot', { action: 'guide', tone: 'purple' }) +
18623
18450
  '<button type="button" data-action="recheck">' + Icon('refresh') + 'Re-check</button>' +
@@ -18644,7 +18471,7 @@ function AddProviderModal() {
18644
18471
  '</header>' +
18645
18472
  '<section class="vr-modal-provider-options">' + featured.map((item) => {
18646
18473
  const provider = providers.find((candidate) => candidate.id === item.icon) || providers[0];
18647
- return '<button type="button" data-provider="' + escapeHtml(provider.id) + '"><span class="vr-provider-token is-large"><img src="' + (providerMarkAssets[provider.id] || providerAssets[provider.id]) + '" alt="" /></span><div><small>' + escapeHtml(item.slot) + '</small><strong>' + escapeHtml(item.provider) + '</strong><em>' + escapeHtml(item.caption) + '</em></div><b>Connect</b></button>';
18474
+ return '<button type="button" data-provider="' + escapeHtml(provider.id) + '">' + ProviderToken(provider, 'is-large') + '<div><small>' + escapeHtml(item.slot) + '</small><strong>' + escapeHtml(item.provider) + '</strong><em>' + escapeHtml(item.caption) + '</em></div><b>Connect</b></button>';
18648
18475
  }).join('') + '</section>' +
18649
18476
  '<section class="vr-modal-connect-note">' + Icon('lock') + '<span>VibeRaven only opens setup guidance here. Real credentials stay in your local runner or provider dashboard.</span></section>'
18650
18477
  );
@@ -18846,21 +18673,18 @@ function connectCliAgent(agentId, chatIndex, options) {
18846
18673
  }
18847
18674
 
18848
18675
  function cliSetupProbeLabel() {
18849
- return demo.cliProbeStatus === 'probing' ? 'Testing\u2026' : demo.cliProbeStatus === 'success' ? 'Connected' : demo.cliProbeStatus === 'failed' ? 'Try again' : 'Test connection';
18676
+ return demo.cliProbeStatus === 'probing' ? 'Testing...' : demo.cliProbeStatus === 'success' ? 'Connected' : demo.cliProbeStatus === 'failed' ? 'Try again' : 'Test connection';
18850
18677
  }
18851
18678
 
18852
18679
  function CliSetupActions(targetId, index) {
18853
18680
  const setup = cliSetupFor(targetId);
18854
18681
  const probeLabel = cliSetupProbeLabel();
18855
- const ctaPending = demo.cliProbeStatus !== 'success' && demo.cliProbeStatus !== 'probing';
18856
- const ctaClass = 'vr-cli-action-button is-primary' + (ctaPending ? ' is-pending' : '');
18857
- return '<p class="vr-cli-setup-cta-hint">After sign-in, click <strong>Test connection</strong> \u2014 chat only works once verified.</p>' +
18858
- '<div class="vr-cli-connect-expand-actions">' +
18682
+ return '<div class="vr-cli-connect-expand-actions">' +
18859
18683
  '<button class="vr-cli-action-button" type="button" data-action="run-cli-install" data-cli-agent="' + escapeHtml(targetId) + '" data-chat-index="' + String(index) + '">' + Icon('shell') + 'Run install</button>' +
18860
18684
  '<button class="vr-cli-action-button" type="button" data-action="run-cli-signin" data-cli-agent="' + escapeHtml(targetId) + '" data-chat-index="' + String(index) + '">' + Icon('shell') + 'Run sign in</button>' +
18861
- '<button class="' + ctaClass + '" type="button" data-action="test-cli-connection" data-cli-agent="' + escapeHtml(targetId) + '" data-chat-index="' + String(index) + '">' + Icon('refresh') + escapeHtml(probeLabel) + '</button>' +
18685
+ '<button class="vr-cli-action-button is-primary" type="button" data-action="test-cli-connection" data-cli-agent="' + escapeHtml(targetId) + '" data-chat-index="' + String(index) + '">' + Icon('refresh') + escapeHtml(probeLabel) + '</button>' +
18862
18686
  '</div>' +
18863
- '<p class="vr-cli-setup-hint">Type commands in the Studio terminal below, or run: <code>' + escapeHtml(setup.installCommand) + '</code> then <code>' + escapeHtml(setup.signInCommand) + '</code></p>';
18687
+ '<p class="vr-cli-setup-hint">Run install and sign in, then use <strong>Test connection</strong> to unlock chat control. Commands: <code>' + escapeHtml(setup.installCommand) + '</code> then <code>' + escapeHtml(setup.signInCommand) + '</code></p>';
18864
18688
  }
18865
18689
 
18866
18690
  function appendTerminalLines(lines) {
@@ -18924,7 +18748,7 @@ function selectTerminalCli(agentId) {
18924
18748
  if (!demo.cliTerminalLines.length) {
18925
18749
  demo.cliTerminalLines = [
18926
18750
  'VibeRaven Studio shell',
18927
- 'Run install, run sign in, then test connection using the buttons below.',
18751
+ 'Run install, run sign in, then use Test connection to continue.',
18928
18752
  ''
18929
18753
  ];
18930
18754
  } else {
@@ -18956,8 +18780,8 @@ function StudioTerminalView() {
18956
18780
  const lines = demo.cliTerminalLines.length
18957
18781
  ? demo.cliTerminalLines
18958
18782
  : [
18959
- 'VibeRaven Studio shell',
18960
- 'Type commands at the $ prompt. Use the buttons below to install, sign in, and test.',
18783
+ 'VibeRaven Studio shell',
18784
+ 'Type commands at the $ prompt. Use Test connection after sign-in.',
18961
18785
  ''
18962
18786
  ];
18963
18787
  const title = terminalConnectTitle(agent);
@@ -19300,7 +19124,7 @@ async function runCliSignInInStudio(agentId, chatIndex) {
19300
19124
  String(payload?.output || payload?.message || 'Sign-in command finished.'),
19301
19125
  payload?.interactiveNote ? String(payload.interactiveNote) : '',
19302
19126
  '',
19303
- 'Complete any browser login, then click Test connection below to verify.'
19127
+ 'Complete any browser login, then click Test connection.'
19304
19128
  ]);
19305
19129
  demo.cliTerminalDraft = setupCommandFallback(agentId, 'sign-in');
19306
19130
  demo.notice = 'Sign-in ran in Studio terminal. Finish login if prompted, then test the connection.';
@@ -19332,7 +19156,7 @@ async function openCliOsTerminal(agentId, chatIndex) {
19332
19156
  '',
19333
19157
  String(payload?.message || 'Complete sign-in in the OS terminal window that opened.'),
19334
19158
  '',
19335
- 'When login finishes, return here and click Test connection below to verify.'
19159
+ 'When login finishes, return here and click Test connection.'
19336
19160
  ]);
19337
19161
  demo.notice = payload?.ok ? 'OS terminal opened for sign-in fallback.' : 'Could not open an OS terminal window.';
19338
19162
  } catch {
@@ -19347,11 +19171,12 @@ function ModelPicker(index) {
19347
19171
  const models = allModelOptions(index);
19348
19172
  const selectedModel = selectedModelLabel(index);
19349
19173
  const scoped = demo.pickerChatIndex === index;
19174
+ const access = accessModes.find((mode) => mode.id === chat.accessMode) || accessModes[1];
19350
19175
  return '<div class="vr-model-picker">' +
19351
19176
  '<button type="button" data-action="toggle-reasoning" data-chat-index="' + String(index) + '"><span>Reasoning</span><strong>' + escapeHtml(chat.reasoning) + '</strong><i></i></button>' +
19352
19177
  '<button type="button" data-action="toggle-models" data-chat-index="' + String(index) + '"><span>Model</span><strong>' + escapeHtml(selectedModel) + '</strong><i></i></button>' +
19353
19178
  '<button type="button" data-action="toggle-context" data-chat-index="' + String(index) + '"><span>Context</span><strong>' + escapeHtml(chat.context) + '</strong><i></i></button>' +
19354
- PermissionModeToggle(index) +
19179
+ '<button type="button" data-action="toggle-access" data-chat-index="' + String(index) + '"><span>Access</span><strong>' + escapeHtml(access.label) + '</strong><i></i></button>' +
19355
19180
  (scoped && demo.reasoningMenuOpen ? '<div class="vr-model-menu is-reasoning" role="menu"><p>Reasoning</p>' + reasoningLevels.map((level) =>
19356
19181
  '<button type="button" data-reasoning="' + escapeHtml(level) + '" data-chat-index="' + String(index) + '">' + escapeHtml(level) + (chat.reasoning === level ? '<b></b>' : '') + '</button>'
19357
19182
  ).join('') + '</div>' : '') +
@@ -19361,14 +19186,9 @@ function ModelPicker(index) {
19361
19186
  (scoped && demo.contextMenuOpen ? '<div class="vr-model-menu is-context" role="menu"><p>Context</p>' + contextLevels.map((level) =>
19362
19187
  '<button type="button" data-context="' + escapeHtml(level) + '" data-chat-index="' + String(index) + '">' + escapeHtml(level) + (chat.context === level ? '<b></b>' : '') + '</button>'
19363
19188
  ).join('') + '</div>' : '') +
19364
- '</div>';
19365
- }
19366
-
19367
- function PermissionModeToggle(index) {
19368
- const isFullAccess = demo.agentPermissionMode === 'full-access';
19369
- return '<div class="vr-permission-toggle" role="group" aria-label="Agent permission mode">' +
19370
- '<button type="button" class="vr-permission-btn' + (!isFullAccess ? ' is-active' : '') + '" data-permission-mode="ask" data-chat-index="' + String(index) + '" aria-pressed="' + String(!isFullAccess) + '" title="Ask for approval before each risky action">Ask each change</button>' +
19371
- '<button type="button" class="vr-permission-btn is-danger' + (isFullAccess ? ' is-active' : '') + '" data-permission-mode="full-access" data-chat-index="' + String(index) + '" aria-pressed="' + String(isFullAccess) + '" title="Run without per-action approval prompts \u2014 use with care">Full access</button>' +
19189
+ (scoped && demo.accessMenuOpen ? '<div class="vr-model-menu is-access" role="menu"><p>Access</p>' + accessModes.map((mode) =>
19190
+ '<button type="button" data-access-mode="' + escapeHtml(mode.id) + '" data-chat-index="' + String(index) + '"><span><strong>' + escapeHtml(mode.label) + '</strong><em>' + escapeHtml(mode.description) + '</em></span>' + (chat.accessMode === mode.id ? '<b></b>' : '') + '</button>'
19191
+ ).join('') + '</div>' : '') +
19372
19192
  '</div>';
19373
19193
  }
19374
19194
 
@@ -19388,10 +19208,10 @@ function ChatBlockersSummary(cards) {
19388
19208
  function ChatPlanCard(steps, chatIndex) {
19389
19209
  const rows = Array.isArray(steps) ? steps : [];
19390
19210
  if (!rows.length) return '';
19391
- return '<section class="vr-chat-card vr-chat-plan-card"><header><strong>Production Fix Plan</strong><b>' + String(rows.length) + ' steps</b></header><ol class="vr-chat-plan-list">' +
19211
+ return '<section class="vr-chat-card vr-chat-plan-card"><header><strong>Fix plan</strong><b>' + String(rows.length) + ' actions</b></header><ol class="vr-chat-plan-list">' +
19392
19212
  rows.map((step, index) =>
19393
19213
  '<li><button class="vr-plan-step" type="button" data-action="mission-step" data-step-id="' + escapeHtml(step.id || ('step-' + String(index))) + '" data-chat-index="' + String(chatIndex) + '">' +
19394
- '<span class="vr-plan-step-num">' + String(index + 1) + '</span>' +
19214
+ '<span class="vr-plan-step-num"></span>' +
19395
19215
  '<span class="vr-plan-step-body">' +
19396
19216
  '<span class="vr-plan-step-title">' + escapeHtml(step.title || 'Fix step') + '</span>' +
19397
19217
  '<span class="vr-plan-step-tag">' + escapeHtml(step.category || 'Quality') + '</span>' +
@@ -19428,7 +19248,7 @@ function ChatMissionCards(cards, chatIndex, options) {
19428
19248
  if (showPlan && planSteps.length) html += ChatPlanCard(planSteps, chatIndex);
19429
19249
  if (connectProviders.length) html += ChatProviderConnectCard(connectProviders, chatIndex);
19430
19250
  if (planSteps.length && !showPlan) {
19431
- html += '<button class="vr-chat-plan-row" type="button" data-action="view-fix-plan" data-chat-index="' + String(chatIndex) + '">' + Icon('report') + '<strong>View Production Fix Plan</strong><em>' + String(planSteps.length) + ' steps</em><b></b></button>';
19251
+ html += '<button class="vr-chat-plan-row" type="button" data-action="view-fix-plan" data-chat-index="' + String(chatIndex) + '">' + Icon('report') + '<strong>View fix plan</strong><em>' + String(planSteps.length) + ' actions</em><b></b></button>';
19432
19252
  }
19433
19253
  html += '</div>';
19434
19254
  return html;
@@ -19555,9 +19375,9 @@ function renderAgentListItems(items, chatIndex) {
19555
19375
 
19556
19376
  function normalizeAgentSectionKey(raw) {
19557
19377
  const key = String(raw || '').trim().toLowerCase().replace(/\\s*\\(\\d+\\)\\s*$/, '').trim();
19558
- if (/^(gate|launch gate|production gate)$/.test(key)) return 'Gate';
19378
+ if (/^(gate|production gate)$/.test(key)) return null;
19559
19379
  if (/^(blockers?|launch blockers?)$/.test(key)) return 'Blockers';
19560
- if (/^(suggested steps?|fix plan|production fix plan)$/.test(key)) return 'Suggested steps';
19380
+ if (/^(suggested steps?|fix plan)$/.test(key)) return 'Suggested steps';
19561
19381
  if (/^(summary|tl;dr|tldr)$/.test(key)) return 'Summary';
19562
19382
  if (key === 'findings') return 'Findings';
19563
19383
  if (key === 'changes made') return 'Changes made';
@@ -19590,15 +19410,7 @@ function dedupeAgentMarkdownBody(text, cards) {
19590
19410
  }
19591
19411
 
19592
19412
  function AgentGateStrip(gateText, gateState) {
19593
- const gate = gateState || currentState.gate || {};
19594
- const parsed = String(gateText || '').trim();
19595
- const status = gate.status || 'unknown';
19596
- const label = parsed || gate.label || 'Waiting for project evidence';
19597
- const tone = status === 'clear' ? 'clear' : (status === 'blocked' || status === 'not_clear' ? 'blocked' : 'unknown');
19598
- return '<div class="vr-agent-gate-strip" data-tone="' + escapeHtml(tone) + '">' +
19599
- Icon('shield') +
19600
- '<span><strong>Launch gate</strong><em>' + escapeHtml(label) + '</em></span>' +
19601
- '</div>';
19413
+ return '';
19602
19414
  }
19603
19415
 
19604
19416
  function parseAgentSections(text, options) {
@@ -19645,8 +19457,7 @@ function renderAgentSectionCard(section, chatIndex) {
19645
19457
  const name = section.name;
19646
19458
  const lower = name.toLowerCase();
19647
19459
  if (lower === 'gate') {
19648
- const gateText = section.paragraphs.concat(section.items).join(' ');
19649
- return AgentGateStrip(gateText, currentState.gate || null);
19460
+ return '';
19650
19461
  }
19651
19462
  const isNext = lower === 'next step';
19652
19463
  const isBlockers = lower === 'blockers';
@@ -19835,7 +19646,7 @@ function readOnlyContextChipsHtml(providerId, releaseId) {
19835
19646
  kind: 'provider',
19836
19647
  index: 0,
19837
19648
  id: provider.id,
19838
- iconHtml: '<img src="' + (providerMarkAssets[provider.id] || providerAssets[provider.id]) + '" alt="" />',
19649
+ iconHtml: provider.iconHtml || '<img src="' + providerAssetSrc(provider) + '" alt="" />',
19839
19650
  mutedLabel: provider.area,
19840
19651
  label: provider.name,
19841
19652
  readOnly: true,
@@ -19926,7 +19737,7 @@ function ContextDropZone(index, showAttached) {
19926
19737
  kind: 'provider',
19927
19738
  index,
19928
19739
  id: provider.id,
19929
- iconHtml: '<img src="' + (providerMarkAssets[provider.id] || providerAssets[provider.id]) + '" alt="" />',
19740
+ iconHtml: provider.iconHtml || '<img src="' + providerAssetSrc(provider) + '" alt="" />',
19930
19741
  mutedLabel: provider.area,
19931
19742
  label: provider.name,
19932
19743
  }) : '') +
@@ -20204,7 +20015,7 @@ app.addEventListener('click', async (event) => {
20204
20015
  if (demo.activeStudioTab === 'terminal' && !demo.cliTerminalLines.length) {
20205
20016
  demo.cliTerminalLines = [
20206
20017
  'VibeRaven Studio shell',
20207
- 'Type commands at the $ prompt. Use the buttons below to install, sign in, and test.',
20018
+ 'Type commands at the $ prompt. Use Test connection after sign-in.',
20208
20019
  ''
20209
20020
  ];
20210
20021
  }
@@ -20302,6 +20113,7 @@ app.addEventListener('click', async (event) => {
20302
20113
  demo.modelMenuOpen = false;
20303
20114
  demo.reasoningMenuOpen = false;
20304
20115
  demo.contextMenuOpen = false;
20116
+ demo.accessMenuOpen = false;
20305
20117
  demo.cliProbeStatus = 'idle';
20306
20118
  demo.cliProbeMessage = '';
20307
20119
  demo.pickerChatIndex = chatIndex;
@@ -20329,6 +20141,7 @@ app.addEventListener('click', async (event) => {
20329
20141
  demo.modelMenuOpen = false;
20330
20142
  demo.reasoningMenuOpen = false;
20331
20143
  demo.contextMenuOpen = false;
20144
+ demo.accessMenuOpen = false;
20332
20145
  demo.notice = (cliAgents.find((item) => item.id === chat.cliId)?.label || chat.cliId) + ' selected for this chat.';
20333
20146
  render();
20334
20147
  return;
@@ -20346,6 +20159,7 @@ app.addEventListener('click', async (event) => {
20346
20159
  demo.modelMenuOpen = false;
20347
20160
  demo.reasoningMenuOpen = false;
20348
20161
  demo.contextMenuOpen = false;
20162
+ demo.accessMenuOpen = false;
20349
20163
  demo.activeStudioTab = 'chat';
20350
20164
  demo.pickerChatIndex = chatIndex;
20351
20165
  demo.notice = selectedModelLabel(chatIndex) + ' selected for this chat.';
@@ -20364,6 +20178,7 @@ app.addEventListener('click', async (event) => {
20364
20178
  demo.reasoningMenuOpen = false;
20365
20179
  demo.modelMenuOpen = false;
20366
20180
  demo.contextMenuOpen = false;
20181
+ demo.accessMenuOpen = false;
20367
20182
  demo.activeStudioTab = 'chat';
20368
20183
  demo.pickerChatIndex = chatIndex;
20369
20184
  demo.notice = chat.reasoning + ' reasoning selected for this chat.';
@@ -20382,6 +20197,7 @@ app.addEventListener('click', async (event) => {
20382
20197
  demo.contextMenuOpen = false;
20383
20198
  demo.modelMenuOpen = false;
20384
20199
  demo.reasoningMenuOpen = false;
20200
+ demo.accessMenuOpen = false;
20385
20201
  demo.activeStudioTab = 'chat';
20386
20202
  demo.pickerChatIndex = chatIndex;
20387
20203
  demo.notice = chat.context + ' context selected for this chat.';
@@ -20390,21 +20206,23 @@ app.addEventListener('click', async (event) => {
20390
20206
  return;
20391
20207
  }
20392
20208
 
20393
- const permissionButton = event.target.closest('[data-permission-mode]');
20394
- if (permissionButton) {
20209
+ const accessButton = event.target.closest('[data-access-mode]');
20210
+ if (accessButton) {
20395
20211
  event.preventDefault();
20396
- const mode = permissionButton.getAttribute('data-permission-mode');
20397
- demo.agentPermissionMode = mode === 'full-access' ? 'full-access' : 'ask';
20398
- savePermissionMode(demo.agentPermissionMode);
20399
- const chatIndex = Number(permissionButton.getAttribute('data-chat-index') || String(demo.pickerChatIndex || 0));
20400
- demo.pickerChatIndex = chatIndex;
20212
+ const chatIndex = Number(accessButton.getAttribute('data-chat-index') || String(demo.pickerChatIndex || 0));
20213
+ const chat = chatContext(chatIndex);
20214
+ const selected = accessModes.find((mode) => mode.id === accessButton.getAttribute('data-access-mode')) || accessModes[1];
20215
+ chat.accessMode = selected.id;
20216
+ demo.selectedAccessMode = selected.id;
20217
+ demo.accessMenuOpen = false;
20218
+ demo.contextMenuOpen = false;
20401
20219
  demo.modelMenuOpen = false;
20402
20220
  demo.reasoningMenuOpen = false;
20403
- demo.contextMenuOpen = false;
20404
- demo.notice = demo.agentPermissionMode === 'full-access'
20405
- ? 'Full access mode: agent runs without per-action approval prompts.'
20406
- : 'Ask mode: agent will request approval before each risky action.';
20407
- render();
20221
+ demo.activeStudioTab = 'chat';
20222
+ demo.pickerChatIndex = chatIndex;
20223
+ demo.notice = selected.label + ' selected for this chat.';
20224
+ refreshPickerMenus();
20225
+ refreshBottomTip();
20408
20226
  return;
20409
20227
  }
20410
20228
 
@@ -20415,6 +20233,7 @@ app.addEventListener('click', async (event) => {
20415
20233
  demo.modelMenuOpen = false;
20416
20234
  demo.reasoningMenuOpen = false;
20417
20235
  demo.contextMenuOpen = false;
20236
+ demo.accessMenuOpen = false;
20418
20237
  if (stage === 'provider') {
20419
20238
  demo.activeStudioTab = 'chat';
20420
20239
  demo.providerPickerOpen = true;
@@ -20601,6 +20420,7 @@ app.addEventListener('click', async (event) => {
20601
20420
  demo.modelMenuOpen = !samePicker;
20602
20421
  demo.reasoningMenuOpen = false;
20603
20422
  demo.contextMenuOpen = false;
20423
+ demo.accessMenuOpen = false;
20604
20424
  refreshPickerMenus();
20605
20425
  return;
20606
20426
  }
@@ -20613,6 +20433,7 @@ app.addEventListener('click', async (event) => {
20613
20433
  demo.reasoningMenuOpen = !samePicker;
20614
20434
  demo.modelMenuOpen = false;
20615
20435
  demo.contextMenuOpen = false;
20436
+ demo.accessMenuOpen = false;
20616
20437
  refreshPickerMenus();
20617
20438
  return;
20618
20439
  }
@@ -20625,6 +20446,20 @@ app.addEventListener('click', async (event) => {
20625
20446
  demo.contextMenuOpen = !samePicker;
20626
20447
  demo.modelMenuOpen = false;
20627
20448
  demo.reasoningMenuOpen = false;
20449
+ demo.accessMenuOpen = false;
20450
+ refreshPickerMenus();
20451
+ return;
20452
+ }
20453
+ if (action === 'toggle-access') {
20454
+ event.preventDefault();
20455
+ const chatIndex = Number(actionButton.getAttribute('data-chat-index') || '0');
20456
+ const samePicker = demo.pickerChatIndex === chatIndex && demo.accessMenuOpen;
20457
+ demo.activeStudioTab = 'chat';
20458
+ demo.pickerChatIndex = chatIndex;
20459
+ demo.accessMenuOpen = !samePicker;
20460
+ demo.modelMenuOpen = false;
20461
+ demo.reasoningMenuOpen = false;
20462
+ demo.contextMenuOpen = false;
20628
20463
  refreshPickerMenus();
20629
20464
  return;
20630
20465
  }
@@ -20728,6 +20563,7 @@ app.addEventListener('click', async (event) => {
20728
20563
  demo.modelMenuOpen = false;
20729
20564
  demo.reasoningMenuOpen = false;
20730
20565
  demo.contextMenuOpen = false;
20566
+ demo.accessMenuOpen = false;
20731
20567
  demo.notice = 'New chat opened.';
20732
20568
  render();
20733
20569
  return;
@@ -20833,7 +20669,7 @@ app.addEventListener('click', async (event) => {
20833
20669
  const chatIndex = Number(actionButton.getAttribute('data-chat-index') || String(actionChatIndex));
20834
20670
  demo.fixPlanExpandedChatIndex = demo.fixPlanExpandedChatIndex === chatIndex ? null : chatIndex;
20835
20671
  demo.pickerChatIndex = chatIndex;
20836
- demo.notice = demo.fixPlanExpandedChatIndex === chatIndex ? 'Production fix plan expanded in chat.' : 'Production fix plan collapsed.';
20672
+ demo.notice = demo.fixPlanExpandedChatIndex === chatIndex ? 'Fix plan expanded in chat.' : 'Fix plan collapsed.';
20837
20673
  render();
20838
20674
  return;
20839
20675
  }
@@ -20973,7 +20809,7 @@ app.addEventListener('dragstart', (event) => {
20973
20809
  event,
20974
20810
  provider ? provider.name : 'Provider',
20975
20811
  provider ? provider.area : 'Drop into VibeRaven chat',
20976
- provider ? '<img src="' + escapeHtml(providerMarkAssets[provider.id] || providerAssets[provider.id]) + '" alt="" />' : ''
20812
+ provider ? (provider.iconHtml || '<img src="' + providerAssetSrc(provider) + '" alt="" />') : ''
20977
20813
  );
20978
20814
  providerButton.classList.add('is-being-dragged');
20979
20815
  demo.notice = 'Dragging provider context. Drop it into a chat composer or Split chat.';
@@ -23768,21 +23604,6 @@ body:has(.vr-app.is-dragging) {
23768
23604
  background: rgba(124, 58, 237, 0.92);
23769
23605
  color: #fff;
23770
23606
  }
23771
- .vr-cli-setup-cta-hint {
23772
- margin: 0 0 6px;
23773
- color: #94a3b8;
23774
- font-size: 11px;
23775
- font-weight: 500;
23776
- line-height: 1.35;
23777
- }
23778
- .vr-cli-setup-cta-hint strong {
23779
- color: #c4b5fd;
23780
- font-weight: 650;
23781
- }
23782
- .vr-cli-action-button.is-primary.is-pending {
23783
- border-color: rgba(168, 85, 247, 0.55);
23784
- box-shadow: 0 0 0 1px rgba(168, 85, 247, 0.25);
23785
- }
23786
23607
  .vr-cli-strip button.is-connect-target {
23787
23608
  border-color: rgba(248, 250, 252, 0.34);
23788
23609
  box-shadow: inset 0 0 0 1px rgba(248, 250, 252, 0.08);
@@ -24507,16 +24328,15 @@ body:has(.vr-app.is-dragging) {
24507
24328
  border-color: rgba(168, 85, 247, 0.38);
24508
24329
  }
24509
24330
  .vr-plan-step-num {
24510
- width: 28px;
24511
- height: 28px;
24331
+ width: 10px;
24332
+ height: 10px;
24512
24333
  flex: 0 0 auto;
24513
24334
  display: grid;
24514
24335
  place-items: center;
24515
- border: 1px solid rgba(168, 85, 247, 0.35);
24336
+ border: 1px solid rgba(168, 85, 247, 0.5);
24516
24337
  border-radius: 999px;
24517
- color: #e9d5ff;
24518
- font-size: 12px;
24519
- font-weight: 700;
24338
+ background: rgba(168, 85, 247, 0.28);
24339
+ box-shadow: 0 0 10px rgba(168, 85, 247, 0.22);
24520
24340
  }
24521
24341
  .vr-plan-step-body {
24522
24342
  flex: 1 1 0;
@@ -25128,6 +24948,10 @@ body:has(.vr-app.is-dragging) {
25128
24948
  .vr-model-menu.is-context {
25129
24949
  right: 0;
25130
24950
  }
24951
+ .vr-model-menu.is-access {
24952
+ right: 0;
24953
+ min-width: 270px;
24954
+ }
25131
24955
  .vr-studio-chat.is-split .vr-model-menu {
25132
24956
  bottom: calc(100% + 10px);
25133
24957
  min-width: min(245px, calc(100vw - 32px));
@@ -25144,6 +24968,10 @@ body:has(.vr-app.is-dragging) {
25144
24968
  left: auto;
25145
24969
  right: 0;
25146
24970
  }
24971
+ .vr-studio-chat.is-split .vr-model-menu.is-access {
24972
+ left: auto;
24973
+ right: 0;
24974
+ }
25147
24975
  .vr-model-menu p {
25148
24976
  margin: 2px 4px 6px;
25149
24977
  color: #a8a29e;
@@ -25288,7 +25116,7 @@ body:has(.vr-app.is-dragging) {
25288
25116
  }
25289
25117
  .vr-bottom-inventory .vr-provider-card {
25290
25118
  min-height: 48px;
25291
- grid-template-columns: 34px minmax(0, 1fr) auto 28px;
25119
+ grid-template-columns: 34px minmax(0, 1fr) 18px 22px;
25292
25120
  grid-template-rows: 1fr;
25293
25121
  align-items: center;
25294
25122
  gap: 8px;
@@ -25345,14 +25173,6 @@ body:has(.vr-app.is-dragging) {
25345
25173
  fill: none !important;
25346
25174
  stroke: currentColor !important;
25347
25175
  }
25348
- .vr-bottom-inventory .vr-provider-light {
25349
- right: 36px;
25350
- top: 50%;
25351
- width: 7px;
25352
- height: 7px;
25353
- transform: translateY(-50%);
25354
- box-shadow: 0 0 9px rgba(34, 197, 94, 0.42);
25355
- }
25356
25176
  .vr-bottom-inventory .vr-provider-card .vr-status-badge {
25357
25177
  grid-column: 3;
25358
25178
  min-height: 18px;
@@ -25639,8 +25459,35 @@ body:has(.vr-app.is-dragging) {
25639
25459
  filter: saturate(1.04) contrast(1.02);
25640
25460
  transition: transform 180ms cubic-bezier(0.23, 1, 0.32, 1);
25641
25461
  }
25462
+ .vr-provider-token .provider-logo__img {
25463
+ filter: saturate(1.22) contrast(1.14);
25464
+ }
25465
+ .vr-provider-token .provider-logo__img[data-provider-logo-key="github"],
25466
+ .vr-provider-token .provider-logo__img[data-provider-logo-key="vercel"],
25467
+ .vr-provider-token .provider-logo__img[data-provider-logo-key="resend"] {
25468
+ filter: drop-shadow(0 0 8px rgba(255, 255, 255, 0.18));
25469
+ }
25470
+ .vr-provider-token .provider-logo__img[data-provider-logo-key="sentry"] {
25471
+ filter: saturate(1.34) contrast(1.16) drop-shadow(0 0 9px rgba(139, 92, 246, 0.26));
25472
+ }
25473
+ .vr-provider-token svg {
25474
+ position: relative;
25475
+ z-index: 1;
25476
+ width: 26px;
25477
+ height: 26px;
25478
+ display: block;
25479
+ }
25480
+ .vr-provider-token .vr-provider-logo,
25481
+ .vr-provider-token .vr-provider-logo img {
25482
+ width: 26px;
25483
+ height: 26px;
25484
+ display: block;
25485
+ object-fit: contain;
25486
+ }
25642
25487
  .vr-provider-card:hover .vr-provider-token img,
25643
- .vr-provider-card.is-selected .vr-provider-token img {
25488
+ .vr-provider-card.is-selected .vr-provider-token img,
25489
+ .vr-provider-card:hover .vr-provider-token svg,
25490
+ .vr-provider-card.is-selected .vr-provider-token svg {
25644
25491
  transform: scale(1.08);
25645
25492
  }
25646
25493
  .vr-provider-token.is-picker {
@@ -25661,15 +25508,11 @@ body:has(.vr-app.is-dragging) {
25661
25508
  width: 34px;
25662
25509
  height: 34px;
25663
25510
  }
25664
- .vr-provider-light {
25665
- position: absolute;
25666
- right: 42px;
25667
- top: 18px;
25668
- width: 8px;
25669
- height: 8px;
25670
- border-radius: 999px;
25671
- background: var(--vr-green);
25672
- box-shadow: 0 0 8px rgba(34, 197, 94, 0.36);
25511
+ .vr-provider-token.is-large svg,
25512
+ .vr-provider-token.is-large .vr-provider-logo,
25513
+ .vr-provider-token.is-large .vr-provider-logo img {
25514
+ width: 34px;
25515
+ height: 34px;
25673
25516
  }
25674
25517
  .vr-provider-card small,
25675
25518
  .vr-provider-card strong,
@@ -25699,6 +25542,15 @@ body:has(.vr-app.is-dragging) {
25699
25542
  font-size: 11px;
25700
25543
  font-weight: 520;
25701
25544
  }
25545
+ .vr-provider-card[data-provider-tone="orange"] em {
25546
+ color: #fbbf24;
25547
+ }
25548
+ .vr-provider-card[data-provider-tone="red"] em {
25549
+ color: #fb7185;
25550
+ }
25551
+ .vr-provider-card[data-provider-tone="neutral"] em {
25552
+ color: #aeb7c6;
25553
+ }
25702
25554
  .vr-provider-proof-chip {
25703
25555
  grid-column: 1 / -1;
25704
25556
  display: inline-flex;
@@ -25730,6 +25582,130 @@ body:has(.vr-app.is-dragging) {
25730
25582
  height: 13px;
25731
25583
  stroke: currentColor;
25732
25584
  }
25585
+ .vr-provider-mcp-chip {
25586
+ grid-column: 2;
25587
+ grid-row: 2;
25588
+ justify-self: end;
25589
+ min-width: 0;
25590
+ width: 8px;
25591
+ height: 8px;
25592
+ display: inline-flex;
25593
+ align-items: center;
25594
+ justify-content: center;
25595
+ gap: 0;
25596
+ border: 0;
25597
+ border-radius: 999px;
25598
+ padding: 0;
25599
+ color: #fbbf24;
25600
+ font-size: 10px;
25601
+ font-weight: 720;
25602
+ line-height: 1;
25603
+ background: transparent;
25604
+ }
25605
+ .vr-provider-mcp-chip i {
25606
+ width: 8px;
25607
+ height: 8px;
25608
+ flex: 0 0 auto;
25609
+ border-radius: 999px;
25610
+ background: #f59e0b;
25611
+ box-shadow: 0 0 8px rgba(245, 158, 11, 0.36);
25612
+ animation: vrStatusPulse 2.7s ease-in-out infinite;
25613
+ }
25614
+ .vr-provider-mcp-chip span {
25615
+ position: absolute;
25616
+ width: 1px;
25617
+ height: 1px;
25618
+ overflow: hidden;
25619
+ clip: rect(0 0 0 0);
25620
+ }
25621
+ .vr-provider-mcp-chip.is-connected {
25622
+ border-color: transparent;
25623
+ background: transparent;
25624
+ color: #bbf7d0;
25625
+ }
25626
+ .vr-provider-mcp-chip.is-connected i {
25627
+ background: #22c55e;
25628
+ box-shadow: 0 0 8px rgba(34, 197, 94, 0.42);
25629
+ }
25630
+ @keyframes vrStatusPulse {
25631
+ 0%, 100% {
25632
+ opacity: 0.72;
25633
+ transform: scale(0.92);
25634
+ }
25635
+ 50% {
25636
+ opacity: 1;
25637
+ transform: scale(1.08);
25638
+ }
25639
+ }
25640
+ .vr-provider-mcp-panel {
25641
+ display: grid;
25642
+ gap: 10px;
25643
+ border: 1px solid rgba(148, 163, 184, 0.13);
25644
+ border-radius: 10px;
25645
+ padding: 10px;
25646
+ background: rgba(255, 255, 255, 0.035);
25647
+ }
25648
+ .vr-provider-mcp-panel header {
25649
+ display: grid;
25650
+ grid-template-columns: 30px minmax(0, 1fr);
25651
+ gap: 10px;
25652
+ align-items: center;
25653
+ }
25654
+ .vr-provider-mcp-panel header > span {
25655
+ width: 30px;
25656
+ height: 30px;
25657
+ display: inline-grid;
25658
+ place-items: center;
25659
+ border-radius: 8px;
25660
+ background: rgba(245, 158, 11, 0.11);
25661
+ color: #fbbf24;
25662
+ }
25663
+ .vr-provider-mcp-panel strong,
25664
+ .vr-provider-mcp-panel em {
25665
+ display: block;
25666
+ min-width: 0;
25667
+ }
25668
+ .vr-provider-mcp-panel strong {
25669
+ color: #f8fafc;
25670
+ font-size: 13px;
25671
+ }
25672
+ .vr-provider-mcp-panel em {
25673
+ margin-top: 2px;
25674
+ color: #98a2b3;
25675
+ font-size: 11px;
25676
+ font-style: normal;
25677
+ }
25678
+ .vr-provider-mcp-panel > div {
25679
+ display: flex;
25680
+ flex-wrap: wrap;
25681
+ gap: 6px;
25682
+ }
25683
+ .vr-provider-mcp-panel small {
25684
+ border-radius: 999px;
25685
+ border: 1px solid rgba(148, 163, 184, 0.13);
25686
+ padding: 4px 7px;
25687
+ color: #b9c2cf;
25688
+ font-size: 10px;
25689
+ font-weight: 680;
25690
+ }
25691
+ .vr-provider-mcp-panel.is-connected {
25692
+ border-color: rgba(34, 197, 94, 0.24);
25693
+ background: rgba(34, 197, 94, 0.055);
25694
+ }
25695
+ .vr-provider-mcp-panel.is-connected header > span {
25696
+ background: rgba(34, 197, 94, 0.12);
25697
+ color: #86efac;
25698
+ }
25699
+ .vr-bottom-inventory .vr-provider-mcp-chip {
25700
+ grid-column: 3;
25701
+ grid-row: 1;
25702
+ justify-self: end;
25703
+ min-width: 0;
25704
+ width: 8px;
25705
+ height: 8px;
25706
+ padding: 0;
25707
+ font-size: 0;
25708
+ }
25733
25709
  .vr-bottom-inventory .vr-provider-proof-chip {
25734
25710
  grid-column: 4;
25735
25711
  justify-self: end;
@@ -25751,13 +25727,6 @@ body:has(.vr-app.is-dragging) {
25751
25727
  height: 15px;
25752
25728
  stroke-width: 2.35;
25753
25729
  }
25754
- .vr-bottom-inventory .vr-provider-light {
25755
- right: 36px;
25756
- top: 50%;
25757
- width: 7px;
25758
- height: 7px;
25759
- transform: translateY(-50%);
25760
- }
25761
25730
  .vr-bottom-inventory .vr-provider-card:hover .vr-provider-proof-chip,
25762
25731
  .vr-bottom-inventory .vr-provider-card.is-selected .vr-provider-proof-chip {
25763
25732
  background: transparent;
@@ -26352,33 +26321,48 @@ body:has(.vr-app.is-dragging) {
26352
26321
  width: 18px;
26353
26322
  height: 18px;
26354
26323
  }
26324
+ .vr-bottom-tip-bar .vr-inline-brand-mark.is-github {
26325
+ display: inline-grid;
26326
+ place-items: center;
26327
+ color: #f8fafc;
26328
+ }
26329
+ .vr-bottom-tip-bar .vr-inline-brand-mark.is-github svg {
26330
+ width: 17px;
26331
+ height: 17px;
26332
+ display: block;
26333
+ fill: currentColor;
26334
+ }
26355
26335
  .vr-tip-mascot {
26336
+ width: 34px;
26337
+ height: 34px;
26356
26338
  align-self: stretch;
26357
26339
  position: relative;
26358
26340
  display: grid;
26359
26341
  place-items: end center;
26360
26342
  overflow: hidden;
26343
+ border-right: 1px solid rgba(148, 163, 184, 0.16);
26361
26344
  }
26362
26345
  .vr-tip-mascot::before {
26363
26346
  content: "";
26364
26347
  position: absolute;
26365
- left: 9px;
26366
- right: 9px;
26367
- bottom: 4px;
26368
- height: 12px;
26348
+ left: 8px;
26349
+ right: 8px;
26350
+ bottom: 2px;
26351
+ height: 10px;
26369
26352
  border-radius: 999px;
26370
- background: rgba(124, 58, 237, 0.3);
26371
- filter: blur(10px);
26353
+ background: rgba(124, 58, 237, 0.24);
26354
+ filter: blur(8px);
26372
26355
  }
26373
26356
  .vr-tip-mascot img {
26374
26357
  position: relative;
26358
+ z-index: 1;
26375
26359
  align-self: end;
26376
- width: 46px;
26377
- height: 42px;
26360
+ width: 32px;
26361
+ height: 32px;
26378
26362
  object-fit: contain;
26379
26363
  object-position: bottom;
26380
- transform: translateY(4px) scale(1.02);
26381
- filter: saturate(1.18) drop-shadow(0 0 22px rgba(168, 85, 247, 0.6));
26364
+ transform: translateY(3px);
26365
+ filter: saturate(1.16) drop-shadow(0 0 12px rgba(168, 85, 247, 0.46));
26382
26366
  }
26383
26367
  .vr-bottom-tip-bar span {
26384
26368
  color: #cbd5e1;
@@ -26701,6 +26685,18 @@ body:has(.vr-app.is-dragging) {
26701
26685
  box-shadow: var(--vr-glow-purple);
26702
26686
  transition: transform 200ms cubic-bezier(0.23, 1, 0.32, 1), box-shadow 200ms ease;
26703
26687
  }
26688
+ .vr-modal-provider-logo.is-logo-html {
26689
+ display: grid;
26690
+ place-items: center;
26691
+ }
26692
+ .vr-modal-provider-logo.is-logo-html img,
26693
+ .vr-modal-provider-logo.is-logo-html svg,
26694
+ .vr-modal-provider-logo.is-logo-html .vr-provider-logo {
26695
+ width: 74px;
26696
+ height: 74px;
26697
+ display: block;
26698
+ object-fit: contain;
26699
+ }
26704
26700
 
26705
26701
  .vr-modal-provider-logo:hover {
26706
26702
  transform: translateY(-3px) rotate(-2deg);
@@ -28046,46 +28042,6 @@ body:has(.vr-app.is-dragging) {
28046
28042
  display: none;
28047
28043
  }
28048
28044
  }
28049
- .vr-permission-toggle {
28050
- display: inline-flex;
28051
- align-items: center;
28052
- gap: 0;
28053
- border: 1px solid rgba(148, 163, 184, 0.18);
28054
- border-radius: 8px;
28055
- overflow: hidden;
28056
- background: rgba(39, 40, 44, 0.92);
28057
- }
28058
- .vr-permission-btn {
28059
- min-height: 28px;
28060
- padding: 0 10px;
28061
- font-size: 11px;
28062
- font-weight: 640;
28063
- color: #94a3b8;
28064
- background: transparent;
28065
- border: none;
28066
- border-radius: 0;
28067
- cursor: pointer;
28068
- transition: background 0.12s, color 0.12s;
28069
- white-space: nowrap;
28070
- }
28071
- .vr-permission-btn + .vr-permission-btn {
28072
- border-left: 1px solid rgba(148, 163, 184, 0.14);
28073
- }
28074
- .vr-permission-btn.is-active {
28075
- background: rgba(100, 116, 139, 0.28);
28076
- color: #f1f5f9;
28077
- }
28078
- .vr-permission-btn.is-danger {
28079
- color: #f59e0b;
28080
- }
28081
- .vr-permission-btn.is-danger.is-active {
28082
- background: rgba(245, 158, 11, 0.18);
28083
- color: #fbbf24;
28084
- }
28085
- .vr-permission-btn:hover:not(.is-active) {
28086
- background: rgba(148, 163, 184, 0.1);
28087
- color: #e2e8f0;
28088
- }
28089
28045
  `;
28090
28046
  }
28091
28047
  });
@@ -28102,8 +28058,9 @@ function renderLocalUiHtml(state) {
28102
28058
  <meta name="viewport" content="width=device-width, initial-scale=1" />
28103
28059
  <meta name="theme-color" content="#050816" />
28104
28060
  <title>VibeRaven</title>
28105
- <link rel="icon" type="image/png" href="/report/assets/viberaven-mascot-idle.png" />
28106
- <link rel="apple-touch-icon" href="/report/assets/viberaven-mascot-idle.png" />
28061
+ <link rel="icon" type="image/png" sizes="64x64" href="/report/assets/viberaven-favicon.png" />
28062
+ <link rel="shortcut icon" type="image/png" href="/report/assets/viberaven-favicon.png" />
28063
+ <link rel="apple-touch-icon" href="/report/assets/viberaven-mascot.png" />
28107
28064
  <style>${localUiCss}</style>
28108
28065
  </head>
28109
28066
  <body>
@@ -28366,6 +28323,19 @@ async function releaseChangelogBetween(cwd, fromRef, toRef) {
28366
28323
  });
28367
28324
  return { from: fromRef, to: toRef, fromLabel: fromRef, toLabel: toRef, entries };
28368
28325
  }
28326
+ async function releaseDiffContextBlock(cwd, releaseId) {
28327
+ const ref = safeGitRef(releaseId);
28328
+ if (!ref || ref === "workspace") return void 0;
28329
+ if (!await gitRefExists(cwd, ref)) return void 0;
28330
+ const comparison = await compareReleaseRefs(cwd, ref, "HEAD");
28331
+ const stats = comparison.stats;
28332
+ const diffPreview = comparison.diff.length > 6e3 ? comparison.diff.slice(0, 6e3) + "\n[diff truncated]" : comparison.diff;
28333
+ return [
28334
+ `Attached release diff (${comparison.fromLabel} -> ${comparison.toLabel}):`,
28335
+ `Files changed: ${stats.files}; insertions: ${stats.insertions}; deletions: ${stats.deletions}`,
28336
+ diffPreview
28337
+ ].join("\n");
28338
+ }
28369
28339
  async function rollbackReleaseRef(cwd, targetRef) {
28370
28340
  if (!await gitRefExists(cwd, targetRef)) {
28371
28341
  return { ok: false, message: `Release ref "${targetRef}" was not found in this repo.`, checkedOut: "" };
@@ -28558,7 +28528,7 @@ async function detectLocalCliAgents(cwd, options = {}) {
28558
28528
  return {
28559
28529
  ...agent,
28560
28530
  installed,
28561
- connected: installed,
28531
+ connected: agent.id === "terminal",
28562
28532
  source: simulateDisconnected ? "simulated" : installed ? "detected" : "fallback",
28563
28533
  models: cliModelDefaults(agent.id),
28564
28534
  setup: CLI_AGENT_SETUP[agent.id]
@@ -28576,6 +28546,60 @@ function isPlaceholderProjectName(value) {
28576
28546
  const normalized = value.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
28577
28547
  return normalized === "" || normalized === "project" || normalized === "npx-open-source-check" || normalized.startsWith("viberaven-local-ui");
28578
28548
  }
28549
+ async function readMcpEvidence(cwd) {
28550
+ const candidates = MCP_CONFIG_PATHS.map((relativePath) => ({
28551
+ source: relativePath.replace(/\\/g, "/"),
28552
+ path: (0, import_node_path31.resolve)(cwd, relativePath)
28553
+ }));
28554
+ const home = process.env.USERPROFILE || process.env.HOME;
28555
+ if (home) {
28556
+ candidates.push({
28557
+ source: "~/.codex/config.toml",
28558
+ path: (0, import_node_path31.resolve)(home, ".codex", "config.toml")
28559
+ });
28560
+ }
28561
+ const evidence = [];
28562
+ for (const candidate of candidates) {
28563
+ try {
28564
+ const text = await (0, import_promises24.readFile)(candidate.path, "utf8");
28565
+ if (/mcp/i.test(text)) evidence.push({ source: candidate.source, text });
28566
+ } catch {
28567
+ }
28568
+ }
28569
+ return evidence;
28570
+ }
28571
+ function mcpForProvider(provider2, evidence) {
28572
+ const needles = MCP_PROVIDER_NEEDLES[provider2.id] ?? [provider2.id];
28573
+ const match = evidence.find((item4) => {
28574
+ const lower = item4.text.toLowerCase();
28575
+ return needles.some((needle) => lower.includes(needle.toLowerCase()));
28576
+ });
28577
+ if (match) {
28578
+ return {
28579
+ status: "connected",
28580
+ label: "MCP connected",
28581
+ source: match.source,
28582
+ connectCommand: MCP_CONNECT_COMMANDS[provider2.id],
28583
+ capabilities: ["repo context", provider2.area, "provider-aware chat"]
28584
+ };
28585
+ }
28586
+ return {
28587
+ status: "disconnected",
28588
+ label: "Connect MCP",
28589
+ connectCommand: MCP_CONNECT_COMMANDS[provider2.id] ?? `Add a ${provider2.name} MCP server entry, then drag ${provider2.name} into chat.`,
28590
+ capabilities: []
28591
+ };
28592
+ }
28593
+ async function applyMcpDetection(state, cwd) {
28594
+ const evidence = await readMcpEvidence(cwd);
28595
+ return {
28596
+ ...state,
28597
+ providers: state.providers.map((provider2) => ({
28598
+ ...provider2,
28599
+ mcp: mcpForProvider(provider2, evidence)
28600
+ }))
28601
+ };
28602
+ }
28579
28603
  async function enrichProjectMetadata(state, cwd) {
28580
28604
  const packageJson = await readPackageJson2(cwd);
28581
28605
  const metadata = {
@@ -28584,7 +28608,7 @@ async function enrichProjectMetadata(state, cwd) {
28584
28608
  };
28585
28609
  const files = await listProjectFiles(cwd);
28586
28610
  const deps = dependencyNames(packageJson);
28587
- const detectedState = state.empty ? applyProviderDetection(state, deps, files) : state;
28611
+ const detectedState = await applyMcpDetection(state.empty ? applyProviderDetection(state, deps, files) : state, cwd);
28588
28612
  const releases = await detectReleases(cwd, metadata.version);
28589
28613
  const missionCards = state.empty ? buildMissionCards(
28590
28614
  {
@@ -28695,19 +28719,19 @@ function parseAgentChatRequest(value) {
28695
28719
  if (rawCliId !== "codex" && rawCliId !== "claude" && rawCliId !== "gemini") return { error: "Unsupported coding CLI" };
28696
28720
  const prompt = stringField(value.prompt, 12e3);
28697
28721
  if (!prompt) return { error: "Agent chat prompt is required" };
28698
- const provider2 = isRecord7(value.provider) ? { area: stringField(value.provider.area, 120), name: stringField(value.provider.name, 120) } : void 0;
28699
- const release = isRecord7(value.release) ? { label: stringField(value.release.label, 120) } : void 0;
28700
- const rawPermMode = stringField(value.permissionMode, 20);
28701
- const permissionMode = rawPermMode === "full-access" ? "full-access" : "ask";
28722
+ const provider2 = isRecord7(value.provider) ? { id: stringField(value.provider.id, 80), area: stringField(value.provider.area, 120), name: stringField(value.provider.name, 120) } : void 0;
28723
+ const release = isRecord7(value.release) ? { id: stringField(value.release.id, 200), label: stringField(value.release.label, 120) } : void 0;
28724
+ const requestedAccessMode = stringField(value.accessMode, 40);
28725
+ const accessMode = requestedAccessMode === "ask" || requestedAccessMode === "approve" || requestedAccessMode === "full" ? requestedAccessMode : "approve";
28702
28726
  return {
28703
28727
  cliId: rawCliId,
28704
28728
  modelId: stringField(value.modelId, 120),
28705
28729
  reasoning: stringField(value.reasoning, 80),
28706
28730
  context: stringField(value.context, 80),
28731
+ accessMode,
28707
28732
  prompt,
28708
28733
  displayPrompt: stringField(value.displayPrompt, 240),
28709
28734
  taskKind: stringField(value.taskKind, 40),
28710
- permissionMode,
28711
28735
  provider: provider2,
28712
28736
  release
28713
28737
  };
@@ -28750,22 +28774,28 @@ async function buildAgentChatPrompt(request, cwd) {
28750
28774
  const state = await readState(cwd);
28751
28775
  const executionBlock = taskKindExecutionBlock(request.taskKind);
28752
28776
  const cardsBlock = missionCardsContextBlock(state);
28777
+ const providerState2 = request.provider?.id ? state.providers.find((provider2) => provider2.id === request.provider?.id) : state.providers.find((provider2) => provider2.name === request.provider?.name);
28778
+ const mcp = providerState2?.mcp;
28779
+ const releaseDiffBlock = await releaseDiffContextBlock(cwd, request.release?.id);
28753
28780
  const lines = [
28754
28781
  "You are VibeRaven Studio, a production-readiness coding agent for an open-source local runner.",
28755
28782
  `Project folder: ${cwd}`,
28756
28783
  request.modelId ? `Selected model label: ${request.modelId}` : void 0,
28757
28784
  request.reasoning ? `Reasoning: ${request.reasoning}` : void 0,
28758
28785
  request.context ? `Context: ${request.context}` : void 0,
28786
+ `Access mode: ${request.accessMode ?? "approve"}`,
28759
28787
  request.provider?.name ? `Provider context: ${request.provider.area ?? "Provider"} / ${request.provider.name}` : void 0,
28788
+ mcp ? `Provider MCP: ${mcp.label}${mcp.source ? ` (${mcp.source})` : ""}` : void 0,
28760
28789
  request.release?.label ? `Release context: ${request.release.label}` : void 0,
28761
- state.gate?.label ? `Gate: ${state.gate.label} (${state.gate.status ?? "unknown"})` : void 0,
28790
+ releaseDiffBlock,
28791
+ state.gate?.label ? `Project status: ${state.gate.label} (${state.gate.status ?? "unknown"})` : void 0,
28762
28792
  state.missionControl?.mainBlocker ? `Main blocker: ${state.missionControl.mainBlocker}` : void 0,
28763
28793
  executionBlock,
28764
28794
  cardsBlock,
28765
28795
  "",
28766
28796
  "Safety rules:",
28767
28797
  "- Stay scoped to this project folder.",
28768
- "- Explain risky or destructive changes before making them.",
28798
+ (request.accessMode ?? "approve") === "ask" ? "- Ask before editing files, using the internet, running destructive commands, or changing provider settings." : (request.accessMode ?? "approve") === "full" ? "- Full access is enabled for this local project; still explain destructive changes before running them." : "- Edit project files when needed, but explain risky or destructive changes before making them.",
28769
28799
  "- Do not claim provider dashboard setup is complete from repo edits alone.",
28770
28800
  "- Keep the answer focused on production readiness.",
28771
28801
  "- Return only the final user-facing answer. Do not echo this prompt, session metadata, logs, or command transcripts.",
@@ -28799,24 +28829,19 @@ function insertCodexExecOptions(codexArgs, pairs) {
28799
28829
  const insertAt = execIndex >= 0 ? execIndex + 1 : codexArgs.length;
28800
28830
  codexArgs.splice(insertAt, 0, ...pairs);
28801
28831
  }
28802
- function resolveCodexSandboxArgs(permissionMode, platform) {
28803
- if (permissionMode === "full-access") {
28804
- return ["--sandbox", "danger-full-access", "--ask-for-approval", "never"];
28805
- }
28806
- if (platform === "win32") {
28807
- return ["--sandbox", "danger-full-access", "--ask-for-approval", "on-request"];
28808
- }
28809
- return ["--sandbox", "workspace-write", "--ask-for-approval", "on-request"];
28832
+ function codexAccessFlags(accessMode) {
28833
+ if (accessMode === "ask") return ["--sandbox", "workspace-write", "--ask-for-approval", "on-request"];
28834
+ if (accessMode === "full") return ["--sandbox", "danger-full-access", "--ask-for-approval", "never"];
28835
+ return ["--sandbox", "workspace-write", "--ask-for-approval", "on-failure"];
28810
28836
  }
28811
- function commandForAgentChat(request, prompt, cwd, outputPath, platform = process.platform) {
28812
- const command = platform === "win32" ? `${request.cliId}.cmd` : request.cliId;
28837
+ function commandForAgentChat(request, prompt, cwd, outputPath) {
28838
+ const command = process.platform === "win32" ? `${request.cliId}.cmd` : request.cliId;
28813
28839
  const promptArg = prompt.replace(/\s*\r?\n\s*/g, " ");
28814
28840
  const model = normalizeCliModelId(request.modelId);
28815
28841
  const effort = resolveCliReasoningEffort(request.cliId, request.reasoning);
28816
- const permissionMode = request.permissionMode ?? "ask";
28817
28842
  if (request.cliId === "gemini") {
28818
28843
  const geminiArgs = ["-p", promptArg, "--output-format", "text"];
28819
- if (permissionMode === "full-access") geminiArgs.push("--skip-trust");
28844
+ if (request.accessMode !== "ask") geminiArgs.push("--skip-trust");
28820
28845
  if (model) geminiArgs.splice(0, 0, "-m", model);
28821
28846
  return { command, args: geminiArgs };
28822
28847
  }
@@ -28824,13 +28849,12 @@ function commandForAgentChat(request, prompt, cwd, outputPath, platform = proces
28824
28849
  const claudeArgs = ["-p", promptArg];
28825
28850
  if (model) claudeArgs.unshift("--model", model);
28826
28851
  if (effort) claudeArgs.unshift("--effort", effort);
28827
- if (permissionMode === "full-access") {
28852
+ if (request.accessMode === "full") {
28828
28853
  claudeArgs.push("--dangerously-skip-permissions");
28829
28854
  }
28830
28855
  return { command, args: claudeArgs };
28831
28856
  }
28832
- const sandboxArgs = resolveCodexSandboxArgs(permissionMode, platform);
28833
- const codexArgs = ["--cd", cwd, ...sandboxArgs, "exec", "--color", "never"];
28857
+ const codexArgs = ["--cd", cwd, ...codexAccessFlags(request.accessMode), "exec", "--color", "never"];
28834
28858
  const codexExecOptions = [];
28835
28859
  if (model) codexExecOptions.push("-m", model);
28836
28860
  if (effort) codexExecOptions.push("-c", `model_reasoning_effort="${effort}"`);
@@ -28971,7 +28995,7 @@ async function runLocalAction(cwd, intent) {
28971
28995
  "# VibeRaven preview rehearsal",
28972
28996
  "",
28973
28997
  `Project: ${projectName}`,
28974
- `Gate: ${gateLabel}`,
28998
+ `Project status: ${gateLabel}`,
28975
28999
  `Blocker: ${blocker}`,
28976
29000
  "",
28977
29001
  "## Recording flow",
@@ -29571,7 +29595,7 @@ async function waitForServerShutdown() {
29571
29595
  process.once("SIGTERM", done);
29572
29596
  });
29573
29597
  }
29574
- var import_node_http, import_node_child_process4, import_promises24, import_node_path31, CLI_AGENT_SETUP, CLI_AUTH_PROBE;
29598
+ var import_node_http, import_node_child_process4, import_promises24, import_node_path31, CLI_AGENT_SETUP, MCP_CONFIG_PATHS, MCP_PROVIDER_NEEDLES, MCP_CONNECT_COMMANDS, CLI_AUTH_PROBE;
29575
29599
  var init_server = __esm({
29576
29600
  "src/local-ui/server.ts"() {
29577
29601
  "use strict";
@@ -29612,6 +29636,30 @@ var init_server = __esm({
29612
29636
  docsUrl: "https://viberaven.dev"
29613
29637
  }
29614
29638
  };
29639
+ MCP_CONFIG_PATHS = [
29640
+ ".mcp.json",
29641
+ "mcp.json",
29642
+ (0, import_node_path31.join)(".cursor", "mcp.json"),
29643
+ (0, import_node_path31.join)(".vscode", "mcp.json")
29644
+ ];
29645
+ MCP_PROVIDER_NEEDLES = {
29646
+ supabase: ["supabase", "@supabase/mcp", "mcp-server-supabase"],
29647
+ vercel: ["vercel"],
29648
+ stripe: ["stripe"],
29649
+ github: ["github"],
29650
+ sentry: ["sentry"],
29651
+ posthog: ["posthog"],
29652
+ clerk: ["clerk"],
29653
+ authjs: ["authjs", "next-auth", "nextauth"],
29654
+ resend: ["resend"],
29655
+ upstash: ["upstash"]
29656
+ };
29657
+ MCP_CONNECT_COMMANDS = {
29658
+ supabase: "Add the Supabase MCP server to your local MCP config, then drag Supabase into chat.",
29659
+ vercel: "Add a Vercel MCP server entry to your local MCP config, then drag Vercel into chat.",
29660
+ stripe: "Add a Stripe MCP server entry to your local MCP config, then drag Stripe into chat.",
29661
+ github: "Add the GitHub MCP server to your local MCP config, then drag GitHub into chat."
29662
+ };
29615
29663
  CLI_AUTH_PROBE = {
29616
29664
  codex: "codex login status",
29617
29665
  claude: "claude auth status",
@@ -29634,11 +29682,7 @@ function buildNextActionBlock(tasks, loopState, plan) {
29634
29682
  return {
29635
29683
  ...base,
29636
29684
  type: "stalled",
29637
- title: "Loop stalled - no gap reduction after 2+ consecutive scans",
29638
- agentInstruction: "Stop the scan loop. Explain the blocker plainly, then either guide the provider/user action or report that no safe repo-code recipe is available.",
29639
- executionPlan: [
29640
- { kind: "stop", note: "Do not keep spending scans while the loop is stalled." }
29641
- ],
29685
+ title: "Loop stalled \u2014 no gap reduction after 2+ consecutive scans",
29642
29686
  requiresUserAction: true,
29643
29687
  stallReason
29644
29688
  };
@@ -29647,67 +29691,42 @@ function buildNextActionBlock(tasks, loopState, plan) {
29647
29691
  return {
29648
29692
  ...base,
29649
29693
  type: "verify",
29650
- title: "Batch complete - run verify to rescan before next batch",
29651
- agentInstruction: "Run verify now, read the refreshed VibeRaven artifacts, then continue with the next safe repo fix or provider step. Do not stop at a summary.",
29652
- executionPlan: [
29653
- { kind: "verify", command: "viberaven_verify" },
29654
- { kind: "continue-loop", note: "After verify, continue until the gate is clear or a provider/user blocker remains." }
29655
- ],
29694
+ title: "Batch complete \u2014 run verify to rescan before next batch",
29656
29695
  mcpTool: "viberaven_verify",
29657
29696
  mcpArgs: {},
29658
29697
  requiresUserAction: false
29659
29698
  };
29660
29699
  }
29661
- const repoCodeTask = tasks.find((task) => task.fixType === "repo-code" && !task.requiresUserAction);
29700
+ const repoCodeTask = tasks.find((t) => t.fixType === "repo-code" && !t.requiresUserAction);
29662
29701
  if (repoCodeTask) {
29663
- const fallbackCommand = repoCodeTask.mcpArgs ? `npx -y viberaven --heal --apply --gap ${repoCodeTask.gapId} --yes` : void 0;
29664
29702
  return {
29665
29703
  ...base,
29666
29704
  type: "repo-code",
29667
29705
  gapId: repoCodeTask.gapId,
29668
29706
  title: repoCodeTask.title,
29669
- agentInstruction: "Apply this repo-code fix now using the MCP tool or fallback command, then continue the local VibeRaven loop. Do not stop at a summary.",
29670
- executionPlan: [
29671
- {
29672
- kind: "apply-repo-fix",
29673
- command: repoCodeTask.mcpTool ?? fallbackCommand,
29674
- note: "Patch only this gap and preserve unrelated user changes."
29675
- },
29676
- { kind: "continue-loop", note: "Apply remaining safe repo fixes until the batch is full, then verify once." }
29677
- ],
29678
29707
  mcpTool: repoCodeTask.mcpTool,
29679
29708
  mcpArgs: repoCodeTask.mcpArgs,
29680
- fallbackCommand,
29709
+ fallbackCommand: repoCodeTask.mcpArgs ? `npx -y viberaven --heal --apply --gap ${repoCodeTask.gapId} --yes` : void 0,
29681
29710
  requiresUserAction: false
29682
29711
  };
29683
29712
  }
29684
- const providerTask = tasks.find((task) => task.fixType === "provider-action");
29713
+ const providerTask = tasks.find((t) => t.fixType === "provider-action");
29685
29714
  if (providerTask) {
29686
29715
  return {
29687
29716
  ...base,
29688
29717
  type: "provider-action",
29689
29718
  gapId: providerTask.gapId,
29690
29719
  title: providerTask.title,
29691
- agentInstruction: "Open or present the provider step now. If browser/tool access is available, open the dashboard link for the user; otherwise show the exact step and copy values. Do not keep telling the user to verify until the provider step is done.",
29692
- executionPlan: [
29693
- { kind: "open-provider-step", note: "Use VIBERAVEN_PROVIDER_ACTION for the exact dashboard step and URL." },
29694
- { kind: "wait-for-user", note: "Wait for confirmation or read-only MCP evidence before verifying." }
29695
- ],
29696
29720
  requiresUserAction: true
29697
29721
  };
29698
29722
  }
29699
- const upgradeTask = tasks.find((task) => task.fixType === "upgrade-required");
29723
+ const upgradeTask = tasks.find((t) => t.fixType === "upgrade-required");
29700
29724
  if (upgradeTask) {
29701
29725
  return {
29702
29726
  ...base,
29703
29727
  type: "upgrade-required",
29704
29728
  gapId: upgradeTask.gapId,
29705
29729
  title: upgradeTask.title,
29706
- agentInstruction: "Explain that this production lane needs an upgrade or connected provider evidence. Continue with any remaining unlocked repo-code fixes if present; otherwise stop cleanly.",
29707
- executionPlan: [
29708
- { kind: "upgrade", command: "https://viberaven.dev/pricing" },
29709
- { kind: "stop", note: "Do not pretend locked lanes or provider state were fixed locally." }
29710
- ],
29711
29730
  requiresUserAction: true,
29712
29731
  upgradeUrl: "https://viberaven.dev/pricing"
29713
29732
  };
@@ -29715,20 +29734,15 @@ function buildNextActionBlock(tasks, loopState, plan) {
29715
29734
  return {
29716
29735
  ...base,
29717
29736
  type: "done",
29718
- title: "All gaps resolved - production gate is clear",
29719
- agentInstruction: "The repo-code gate is clear. Run strict before deploy and still verify provider dashboard state through MCP or the provider dashboard.",
29720
- executionPlan: [
29721
- { kind: "verify", command: "npx -y viberaven --strict" },
29722
- { kind: "stop", note: "Do not deploy until provider dashboard state is verified where applicable." }
29723
- ],
29737
+ title: "All gaps resolved \u2014 production gate is clear",
29724
29738
  requiresUserAction: false
29725
29739
  };
29726
29740
  }
29727
29741
  function resolveStallReason(tasks) {
29728
29742
  if (tasks.length === 0) return "no-recipes";
29729
- const allUpgradeOrEmpty = tasks.every((task) => task.fixType === "upgrade-required");
29743
+ const allUpgradeOrEmpty = tasks.every((t) => t.fixType === "upgrade-required");
29730
29744
  if (allUpgradeOrEmpty) return "no-recipes";
29731
- const allProviderAction = tasks.every((task) => task.fixType === "provider-action");
29745
+ const allProviderAction = tasks.every((t) => t.fixType === "provider-action");
29732
29746
  if (allProviderAction) return "provider-action-required";
29733
29747
  return "unknown";
29734
29748
  }
@@ -29753,7 +29767,7 @@ function buildProviderActionBlock(task) {
29753
29767
  }
29754
29768
  function printProviderActionBlock(tasks) {
29755
29769
  const task = tasks.find(
29756
- (entry) => entry.fixType === "provider-action" && entry.requiresUserAction === true
29770
+ (t) => t.fixType === "provider-action" && t.requiresUserAction === true
29757
29771
  );
29758
29772
  if (!task) return;
29759
29773
  const block = buildProviderActionBlock(task);
@@ -29902,6 +29916,7 @@ Usage:
29902
29916
  viberaven Open the local launch console for human TTY use
29903
29917
 
29904
29918
  viberaven ui [path] Open the local launch console
29919
+
29905
29920
  viberaven tui Same interactive menu
29906
29921
 
29907
29922
  viberaven login [--api-url <url>]
@@ -30010,9 +30025,6 @@ function parseArgs(argv) {
30010
30025
  if (arg.startsWith("--")) {
30011
30026
  const equalsIndex = arg.indexOf("=");
30012
30027
  const key = equalsIndex === -1 ? arg.slice(2) : arg.slice(2, equalsIndex);
30013
- if (key === "pet") {
30014
- continue;
30015
- }
30016
30028
  if (equalsIndex !== -1) {
30017
30029
  flags[key] = arg.slice(equalsIndex + 1);
30018
30030
  continue;
@@ -30108,9 +30120,6 @@ function resolveDefaultEntrypointMode(options) {
30108
30120
  if (options.env.VIBERAVEN_AGENT === "1") return "agent-scan";
30109
30121
  return "local-ui";
30110
30122
  }
30111
- function resolvePositionalCwd(positional) {
30112
- return positional[0] ? (0, import_node_path33.resolve)(process.cwd(), positional[0]) : process.cwd();
30113
- }
30114
30123
  function formatScanJsonStdout(artifact) {
30115
30124
  return JSON.stringify(sanitizeArtifactForDisk(artifact), null, 2);
30116
30125
  }
@@ -30151,7 +30160,7 @@ async function cmdStatus(flags, positional) {
30151
30160
  console.log("Not signed in. Run: viberaven login");
30152
30161
  return 1;
30153
30162
  }
30154
- const startDir = resolvePositionalCwd(positional);
30163
+ const startDir = positional[0] ? (0, import_node_path33.join)(process.cwd(), positional[0]) : process.cwd();
30155
30164
  let artifact;
30156
30165
  try {
30157
30166
  artifact = await loadLastArtifact(startDir);
@@ -30305,7 +30314,7 @@ async function cmdWatch(flags) {
30305
30314
  }
30306
30315
  }
30307
30316
  async function runScanCommand(flags, positional, options) {
30308
- const workspacePath = positional[0] ? resolvePositionalCwd(positional) : await resolveWorkspaceRoot(process.cwd());
30317
+ const workspacePath = positional[0] ? (0, import_node_path33.join)(process.cwd(), positional[0]) : await resolveWorkspaceRoot(process.cwd());
30309
30318
  const apiBaseUrl = resolveApiBaseUrl(typeof flags["api-url"] === "string" ? flags["api-url"] : void 0);
30310
30319
  let accessToken;
30311
30320
  try {
@@ -30389,7 +30398,7 @@ async function runScanCommand(flags, positional, options) {
30389
30398
  return { exitCode: 0, artifacts: paths };
30390
30399
  }
30391
30400
  async function cmdReport(flags, positional) {
30392
- const startDir = resolvePositionalCwd(positional);
30401
+ const startDir = positional[0] ? (0, import_node_path33.join)(process.cwd(), positional[0]) : process.cwd();
30393
30402
  try {
30394
30403
  const paths = await refreshReportFromDisk(startDir);
30395
30404
  console.log(`Report refreshed: ${paths.reportPath}`);
@@ -30411,7 +30420,7 @@ async function cmdReport(flags, positional) {
30411
30420
  }
30412
30421
  }
30413
30422
  async function cmdPrompt(flags, positional) {
30414
- const startDir = resolvePositionalCwd(positional);
30423
+ const startDir = positional[0] ? (0, import_node_path33.join)(process.cwd(), positional[0]) : process.cwd();
30415
30424
  let artifact;
30416
30425
  try {
30417
30426
  artifact = await loadLastArtifact(startDir);
@@ -30508,7 +30517,7 @@ async function main() {
30508
30517
  const wantsJsonl = hasFlag(flags, "jsonl");
30509
30518
  const wantsStrict = hasFlag(flags, "strict");
30510
30519
  if (flags.condense) {
30511
- const cwd = resolvePositionalCwd(positional);
30520
+ const cwd = positional[0] ? (0, import_node_path33.join)(process.cwd(), positional[0]) : process.cwd();
30512
30521
  const result = await runCondenseCommand({ cwd });
30513
30522
  console.log(`VibeRaven context map refreshed: ${result.contextMapPath}`);
30514
30523
  return 0;
@@ -30528,7 +30537,7 @@ async function main() {
30528
30537
  }
30529
30538
  if (!command && flags.verify === true && typeof flags.action === "string") {
30530
30539
  return runVerifyActionCommand({
30531
- cwd: resolvePositionalCwd(positional),
30540
+ cwd: positional[0] ? (0, import_node_path33.join)(process.cwd(), positional[0]) : process.cwd(),
30532
30541
  actionId: flags.action
30533
30542
  });
30534
30543
  }
@@ -30565,7 +30574,7 @@ async function main() {
30565
30574
  env: process.env
30566
30575
  });
30567
30576
  if (mode === "local-ui") {
30568
- const cwd = resolvePositionalCwd(positional);
30577
+ const cwd = positional[0] ? (0, import_node_path33.join)(process.cwd(), positional[0]) : process.cwd();
30569
30578
  const handle = await startLocalUiServer({ cwd });
30570
30579
  console.log(`VibeRaven local UI: ${handle.url}`);
30571
30580
  await waitForServerShutdown();
@@ -30585,7 +30594,7 @@ async function main() {
30585
30594
  await runInteractiveSession();
30586
30595
  return 0;
30587
30596
  case "ui": {
30588
- const cwd = resolvePositionalCwd(positional);
30597
+ const cwd = positional[0] ? (0, import_node_path33.join)(process.cwd(), positional[0]) : process.cwd();
30589
30598
  const handle = await startLocalUiServer({ cwd });
30590
30599
  console.log(`VibeRaven local UI: ${handle.url}`);
30591
30600
  await waitForServerShutdown();
@@ -30602,19 +30611,19 @@ async function main() {
30602
30611
  return cmdStatus(flags, positional);
30603
30612
  case "actions":
30604
30613
  return runActionsCommand({
30605
- cwd: resolvePositionalCwd(positional),
30614
+ cwd: positional[0] ? (0, import_node_path33.join)(process.cwd(), positional[0]) : process.cwd(),
30606
30615
  json: Boolean(flags.json)
30607
30616
  });
30608
30617
  case "preview":
30609
30618
  return runPreviewCommand({
30610
- cwd: resolvePositionalCwd(positional),
30619
+ cwd: positional[0] ? (0, import_node_path33.join)(process.cwd(), positional[0]) : process.cwd(),
30611
30620
  agentMode: flags["agent-mode"] === true,
30612
30621
  json: Boolean(flags.json)
30613
30622
  });
30614
30623
  case "next":
30615
30624
  return runNextCommand({
30616
30625
  json: Boolean(flags.json),
30617
- cwd: resolvePositionalCwd(positional)
30626
+ cwd: positional[0] ? (0, import_node_path33.join)(process.cwd(), positional[0]) : process.cwd()
30618
30627
  });
30619
30628
  case "guide": {
30620
30629
  const provider2 = positional[0];
@@ -30652,7 +30661,7 @@ async function main() {
30652
30661
  case "provider-verify":
30653
30662
  return cmdProviderVerify(flags, positional);
30654
30663
  case "init": {
30655
- const cwd = resolvePositionalCwd(positional);
30664
+ const cwd = positional[0] ? (0, import_node_path33.join)(process.cwd(), positional[0]) : process.cwd();
30656
30665
  const agents = typeof flags.agents === "string" ? flags.agents : void 0;
30657
30666
  return runInitCommand({
30658
30667
  cwd,
@@ -30666,7 +30675,7 @@ async function main() {
30666
30675
  return 1;
30667
30676
  }
30668
30677
  return runDoctorAgentsCommand({
30669
- cwd: resolvePositionalCwd(positional)
30678
+ cwd: positional[0] ? (0, import_node_path33.join)(process.cwd(), positional[0]) : process.cwd()
30670
30679
  });
30671
30680
  case "validate-npm-package":
30672
30681
  return runValidateNpmPackageCommand({
@@ -30679,7 +30688,7 @@ async function main() {
30679
30688
  return 1;
30680
30689
  }
30681
30690
  return runAuditCommand({
30682
- cwd: resolvePositionalCwd(positional),
30691
+ cwd: positional[0] ? (0, import_node_path33.join)(process.cwd(), positional[0]) : process.cwd(),
30683
30692
  json: Boolean(flags.json)
30684
30693
  });
30685
30694
  default: