@viberaven/cli 1.5.3 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -6970,7 +6970,7 @@ var init_mockSupabaseVerifier = __esm({
6970
6970
  providerObservationMet: false,
6971
6971
  fixType: "mcp-connect",
6972
6972
  severity: tables.length > 0 ? "critical" : "info",
6973
- repoSignals: tables.map((table) => `table referenced: ${table}`),
6973
+ repoSignals: tables.map((table2) => `table referenced: ${table2}`),
6974
6974
  providerSignals: ["Supabase policy list API or MCP"],
6975
6975
  requiredEvidence: ["Policies exist for tables used in application code"]
6976
6976
  }),
@@ -6986,7 +6986,7 @@ var init_mockSupabaseVerifier = __esm({
6986
6986
  providerObservationMet: false,
6987
6987
  fixType: "provider-config",
6988
6988
  severity: "warning",
6989
- repoSignals: tables.map((table) => `.from("${table}")`),
6989
+ repoSignals: tables.map((table2) => `.from("${table2}")`),
6990
6990
  providerSignals: ["Live table policy map"],
6991
6991
  requiredEvidence: ["Each referenced public table has RLS + policy in production"]
6992
6992
  })
@@ -7008,7 +7008,7 @@ var init_mockSupabaseVerifier = __esm({
7008
7008
  providerActual: "Live RLS state unknown without Supabase MCP",
7009
7009
  severity: "critical",
7010
7010
  suggestedFix: "repo-fix",
7011
- evidenceRefs: tables.map((table) => `table: ${table}`)
7011
+ evidenceRefs: tables.map((table2) => `table: ${table2}`)
7012
7012
  });
7013
7013
  }
7014
7014
  if (rlsInRepo) {
@@ -8303,7 +8303,7 @@ function replayProject(files, root) {
8303
8303
  }
8304
8304
  }
8305
8305
  const replayed = [...tables.values()];
8306
- for (const table of replayed) table.root = root;
8306
+ for (const table2 of replayed) table2.root = root;
8307
8307
  return { tables: replayed, functions: [...functions.values()], statementsSeen };
8308
8308
  }
8309
8309
  function isSqlTestPath(path) {
@@ -8574,38 +8574,38 @@ function analyzeRls(replay) {
8574
8574
  const noPolicies = [];
8575
8575
  const permissive = [];
8576
8576
  const rlsEnabledEvidence = [];
8577
- for (const table of replay.tables) {
8578
- if (table.schema !== "public") continue;
8579
- if (table.rls) {
8580
- rlsEnabledEvidence.push(`${at(table.rlsLoc)}: row level security enabled on ${safe(table.display)}`);
8577
+ for (const table2 of replay.tables) {
8578
+ if (table2.schema !== "public") continue;
8579
+ if (table2.rls) {
8580
+ rlsEnabledEvidence.push(`${at(table2.rlsLoc)}: row level security enabled on ${safe(table2.display)}`);
8581
8581
  }
8582
- if (!table.rls && (table.created || table.disabledLoc)) {
8583
- const where = table.disabledLoc ?? table.arrivedLoc ?? table.created;
8582
+ if (!table2.rls && (table2.created || table2.disabledLoc)) {
8583
+ const where = table2.disabledLoc ?? table2.arrivedLoc ?? table2.created;
8584
8584
  const risk = "Anyone holding the anon key your frontend ships can read and change every row through the Data API (unless the table's grants were revoked).";
8585
- const message = table.disabledLoc ? `${table.display} has row level security turned off at ${at(table.disabledLoc)}. ${risk}` : table.arrivedLoc ? `${table.display} is moved into public at ${at(table.arrivedLoc)} without row level security, and no later migration enables it. ${risk}` : `${table.display} is created at ${at(table.created)} and no migration enables row level security on it. ${risk}`;
8585
+ const message = table2.disabledLoc ? `${table2.display} has row level security turned off at ${at(table2.disabledLoc)}. ${risk}` : table2.arrivedLoc ? `${table2.display} is moved into public at ${at(table2.arrivedLoc)} without row level security, and no later migration enables it. ${risk}` : `${table2.display} is created at ${at(table2.created)} and no migration enables row level security on it. ${risk}`;
8586
8586
  missingRls.push({
8587
8587
  id: "rls_disabled",
8588
8588
  severity: "critical",
8589
8589
  message: safe(message),
8590
8590
  file: where?.file,
8591
8591
  line: where?.line,
8592
- table: table.display,
8593
- fix: `alter table ${table.display} enable row level security;`
8592
+ table: table2.display,
8593
+ fix: `alter table ${table2.display} enable row level security;`
8594
8594
  });
8595
8595
  }
8596
- if (table.rls && table.policies.size === 0) {
8596
+ if (table2.rls && table2.policies.size === 0) {
8597
8597
  noPolicies.push({
8598
8598
  id: "rls_no_policies",
8599
8599
  severity: "info",
8600
8600
  message: safe(
8601
- `${table.display} has row level security on and no policies, so it is locked to the service role: the browser client gets no rows back and cannot write. Fine for server-only tables; add policies if the app reads it from the frontend.`
8601
+ `${table2.display} has row level security on and no policies, so it is locked to the service role: the browser client gets no rows back and cannot write. Fine for server-only tables; add policies if the app reads it from the frontend.`
8602
8602
  ),
8603
- file: table.rlsLoc?.file,
8604
- line: table.rlsLoc?.line,
8605
- table: table.display
8603
+ file: table2.rlsLoc?.file,
8604
+ line: table2.rlsLoc?.line,
8605
+ table: table2.display
8606
8606
  });
8607
8607
  }
8608
- for (const policy of table.policies.values()) {
8608
+ for (const policy of table2.policies.values()) {
8609
8609
  if (!policy.permissive) continue;
8610
8610
  const roles = policy.roles.filter((r) => r === "public" || r === "anon" || r === "authenticated");
8611
8611
  if (roles.length === 0) continue;
@@ -8618,11 +8618,11 @@ function analyzeRls(replay) {
8618
8618
  id: "rls_policy_allows_all_read",
8619
8619
  severity: "high",
8620
8620
  message: safe(
8621
- `Policy "${policy.name}" on ${table.display} (${at(policy.loc)}) lets ${who} read every row: using (true). Fine for truly public content; wrong for anything per-user.`
8621
+ `Policy "${policy.name}" on ${table2.display} (${at(policy.loc)}) lets ${who} read every row: using (true). Fine for truly public content; wrong for anything per-user.`
8622
8622
  ),
8623
8623
  file: policy.loc.file,
8624
8624
  line: policy.loc.line,
8625
- table: table.display,
8625
+ table: table2.display,
8626
8626
  policy: policy.name
8627
8627
  });
8628
8628
  continue;
@@ -8650,10 +8650,10 @@ function analyzeRls(replay) {
8650
8650
  permissive.push({
8651
8651
  id: "rls_policy_allows_all_write",
8652
8652
  severity,
8653
- message: safe(`Policy "${policy.name}" on ${table.display} (${at(policy.loc)}) lets ${who} ${verb}: ${clause}.`),
8653
+ message: safe(`Policy "${policy.name}" on ${table2.display} (${at(policy.loc)}) lets ${who} ${verb}: ${clause}.`),
8654
8654
  file: policy.loc.file,
8655
8655
  line: policy.loc.line,
8656
- table: table.display,
8656
+ table: table2.display,
8657
8657
  policy: policy.name
8658
8658
  });
8659
8659
  }
@@ -8733,35 +8733,35 @@ function analyzeGrants(replay, uses, autoExpose) {
8733
8733
  if (existing) use.ops.forEach((op) => existing.ops.add(op));
8734
8734
  else byName.set(use.table, { ...use, ops: new Set(use.ops) });
8735
8735
  }
8736
- for (const table of replay.tables) {
8737
- if (table.schema !== "public") continue;
8738
- const granted = table.grants.size > 0;
8739
- if (granted && !table.rls) {
8740
- const roles2 = [...new Set([...table.grants].map((g2) => g2.split(":")[0]))].join(" and ");
8736
+ for (const table2 of replay.tables) {
8737
+ if (table2.schema !== "public") continue;
8738
+ const granted = table2.grants.size > 0;
8739
+ if (granted && !table2.rls) {
8740
+ const roles2 = [...new Set([...table2.grants].map((g2) => g2.split(":")[0]))].join(" and ");
8741
8741
  out.push({
8742
8742
  id: "data_api_grant_without_rls",
8743
8743
  severity: "critical",
8744
8744
  message: safe(
8745
- `${table.display} is granted to ${roles2} (${at(table.grantLoc)}) but row level security is off. A grant without RLS is the dangerous combination: the Data API serves every row to that role. Enable RLS and add policies before granting.`
8745
+ `${table2.display} is granted to ${roles2} (${at(table2.grantLoc)}) but row level security is off. A grant without RLS is the dangerous combination: the Data API serves every row to that role. Enable RLS and add policies before granting.`
8746
8746
  ),
8747
- file: table.grantLoc?.file,
8748
- line: table.grantLoc?.line,
8749
- table: table.display,
8750
- fix: `alter table ${table.display} enable row level security;`
8747
+ file: table2.grantLoc?.file,
8748
+ line: table2.grantLoc?.line,
8749
+ table: table2.display,
8750
+ fix: `alter table ${table2.display} enable row level security;`
8751
8751
  });
8752
8752
  continue;
8753
8753
  }
8754
- if (!table.created || !table.rls) continue;
8754
+ if (!table2.created || !table2.rls) continue;
8755
8755
  const configOff = autoExpose && !autoExpose.value ? autoExpose : void 0;
8756
- const revokedAtCreate = table.defaultsRevoked ?? /* @__PURE__ */ new Set();
8756
+ const revokedAtCreate = table2.defaultsRevoked ?? /* @__PURE__ */ new Set();
8757
8757
  if (revokedAtCreate.size === 0 && !configOff) continue;
8758
- const use = byName.get(table.name);
8758
+ const use = byName.get(table2.name);
8759
8759
  if (!use) continue;
8760
- const lacks = (role, priv) => !table.grants.has(`${role}:${priv}`) && (configOff !== void 0 || revokedAtCreate.has(`${role}:${priv}`));
8760
+ const lacks = (role, priv) => !table2.grants.has(`${role}:${priv}`) && (configOff !== void 0 || revokedAtCreate.has(`${role}:${priv}`));
8761
8761
  const rolesFor = (priv) => {
8762
- if (!table.rls) return [...DATA_API_ROLES];
8762
+ if (!table2.rls) return [...DATA_API_ROLES];
8763
8763
  return expandRoles(
8764
- [...table.policies.values()].filter((p2) => p2.permissive && (p2.command === priv || p2.command === "all")).flatMap((p2) => p2.roles)
8764
+ [...table2.policies.values()].filter((p2) => p2.permissive && (p2.command === priv || p2.command === "all")).flatMap((p2) => p2.roles)
8765
8765
  );
8766
8766
  };
8767
8767
  const blocked = /* @__PURE__ */ new Map();
@@ -8773,17 +8773,17 @@ function analyzeGrants(replay, uses, autoExpose) {
8773
8773
  if (blocked.size === 0) continue;
8774
8774
  const privs = [...blocked.keys()];
8775
8775
  const roles = [...new Set([...blocked.values()].flat())];
8776
- const reason = configOff ? `${configOff.file}:${configOff.line} sets auto_expose_new_tables = false, so new public tables get no default grants` : `the default grants for new public tables are revoked at ${at(table.defaultsRevokedLoc)}`;
8777
- const fix = `grant ${privs.join(", ")} on table ${table.display} to ${roles.join(", ")};`;
8776
+ const reason = configOff ? `${configOff.file}:${configOff.line} sets auto_expose_new_tables = false, so new public tables get no default grants` : `the default grants for new public tables are revoked at ${at(table2.defaultsRevokedLoc)}`;
8777
+ const fix = `grant ${privs.join(", ")} on table ${table2.display} to ${roles.join(", ")};`;
8778
8778
  out.push({
8779
8779
  id: "data_api_grant_missing",
8780
8780
  severity: "warning",
8781
8781
  message: safe(
8782
- `${table.display} is used from the client (${use.file}:${use.line}) for ${privs.join(", ")}, but ${roles.join(" and ")} ${roles.length === 1 ? "has" : "have"} no ${privs.join("/")} grant on it: ${reason}, and no migration grants it back. Those calls get "permission denied for table ${table.name}". Add to a migration: ${fix}`
8782
+ `${table2.display} is used from the client (${use.file}:${use.line}) for ${privs.join(", ")}, but ${roles.join(" and ")} ${roles.length === 1 ? "has" : "have"} no ${privs.join("/")} grant on it: ${reason}, and no migration grants it back. Those calls get "permission denied for table ${table2.name}". Add to a migration: ${fix}`
8783
8783
  ),
8784
- file: table.created.file,
8785
- line: table.created.line,
8786
- table: table.display,
8784
+ file: table2.created.file,
8785
+ line: table2.created.line,
8786
+ table: table2.display,
8787
8787
  fix
8788
8788
  });
8789
8789
  }
@@ -10291,10 +10291,10 @@ async function listTrackedIgnoredFiles(workspacePath) {
10291
10291
  return { paths: [], incomplete: false };
10292
10292
  }
10293
10293
  try {
10294
- const git = await safeGitInvocation(workspacePath, ["ls-files", "-c", "-i", "--exclude-standard", "-z"]);
10295
- const { stdout } = await execFileAsync2("git", git.args, {
10294
+ const git2 = await safeGitInvocation(workspacePath, ["ls-files", "-c", "-i", "--exclude-standard", "-z"]);
10295
+ const { stdout } = await execFileAsync2("git", git2.args, {
10296
10296
  cwd: workspacePath,
10297
- env: git.env,
10297
+ env: git2.env,
10298
10298
  timeout: GIT_TIMEOUT_MS,
10299
10299
  maxBuffer: GIT_MAX_OUTPUT_BYTES,
10300
10300
  windowsHide: true
@@ -10318,14 +10318,14 @@ async function collectDatabaseEvidence(workspacePath, scan) {
10318
10318
  [...scan.fileTree.split("\n"), ...scan.secretsFound, ...walk.listed, ...trackedIgnored.paths].map((p2) => normalizedPath(p2.trim()))
10319
10319
  )
10320
10320
  ].filter((p2) => p2.length > 0 && !isOwnArtifact(p2) && !fullyLoaded.has(p2));
10321
- const wanted = [
10321
+ const wanted2 = [
10322
10322
  ...candidates.filter((p2) => EVIDENCE_MANIFEST.test(p2)),
10323
10323
  ...candidates.filter((p2) => !EVIDENCE_MANIFEST.test(p2) && EVIDENCE_CODE.test(p2))
10324
10324
  ];
10325
- if (wanted.length > EVIDENCE_MAX_FILES) incomplete = true;
10325
+ if (wanted2.length > EVIDENCE_MAX_FILES) incomplete = true;
10326
10326
  const files = [];
10327
10327
  let bytesRead = 0;
10328
- for (const path of wanted.slice(0, EVIDENCE_MAX_FILES)) {
10328
+ for (const path of wanted2.slice(0, EVIDENCE_MAX_FILES)) {
10329
10329
  if (bytesRead >= EVIDENCE_MAX_TOTAL_BYTES) {
10330
10330
  incomplete = true;
10331
10331
  break;
@@ -10669,8 +10669,8 @@ function noReleaseTagsSeed(commitCount) {
10669
10669
  }
10670
10670
  async function detectReleaseTagGap(workspacePath) {
10671
10671
  try {
10672
- const git = (args) => safeGitInvocation(workspacePath, args).then((call) => execFileAsync3("git", call.args, { cwd: workspacePath, env: call.env, windowsHide: true }));
10673
- const [tags, count] = await Promise.all([git(["tag", "--list"]), git(["rev-list", "--count", "HEAD"])]);
10672
+ const git2 = (args) => safeGitInvocation(workspacePath, args).then((call) => execFileAsync3("git", call.args, { cwd: workspacePath, env: call.env, windowsHide: true }));
10673
+ const [tags, count] = await Promise.all([git2(["tag", "--list"]), git2(["rev-list", "--count", "HEAD"])]);
10674
10674
  const tagCount = tags.stdout.split("\n").filter((line) => line.trim().length > 0).length;
10675
10675
  const commitCount = Number.parseInt(count.stdout.trim(), 10) || 0;
10676
10676
  if (tagCount === 0 && commitCount >= 20) {
@@ -12420,7 +12420,7 @@ var init_agentSummary = __esm({
12420
12420
 
12421
12421
  // src/heal/rlsMigrationTarget.ts
12422
12422
  function rlsEnableStatements(tables) {
12423
- return [...new Set(tables)].map((table) => `alter table ${table} enable row level security;`);
12423
+ return [...new Set(tables)].map((table2) => `alter table ${table2} enable row level security;`);
12424
12424
  }
12425
12425
  function migrationStamp(now) {
12426
12426
  return now.toISOString().replace(/\D/g, "").slice(0, 14);
@@ -13688,12 +13688,12 @@ function createAppWindowOpenCommands(target, platform = process.platform, env =
13688
13688
  }
13689
13689
  async function openWithSystemDefault(target) {
13690
13690
  const { command, args, shell } = createOpenCommand(target);
13691
- await new Promise((resolve9, reject) => {
13691
+ await new Promise((resolve10, reject) => {
13692
13692
  const child = (0, import_node_child_process4.spawn)(command, args, { stdio: "ignore", shell });
13693
13693
  child.on("error", reject);
13694
13694
  child.on("exit", (code) => {
13695
13695
  if (code === 0) {
13696
- resolve9();
13696
+ resolve10();
13697
13697
  } else {
13698
13698
  reject(new Error(`Could not open browser (exit ${code ?? "unknown"}). Open manually: ${target}`));
13699
13699
  }
@@ -13704,7 +13704,7 @@ async function spawnDetached(command) {
13704
13704
  if (/^[A-Za-z]:[\\/]/.test(command.command) && !(0, import_node_fs8.existsSync)(command.command)) {
13705
13705
  throw new Error(`Browser not found: ${command.command}`);
13706
13706
  }
13707
- await new Promise((resolve9, reject) => {
13707
+ await new Promise((resolve10, reject) => {
13708
13708
  const child = (0, import_node_child_process4.spawn)(command.command, command.args, {
13709
13709
  detached: true,
13710
13710
  stdio: "ignore",
@@ -13712,7 +13712,7 @@ async function spawnDetached(command) {
13712
13712
  });
13713
13713
  child.once("spawn", () => {
13714
13714
  child.unref();
13715
- resolve9();
13715
+ resolve10();
13716
13716
  });
13717
13717
  child.once("error", reject);
13718
13718
  });
@@ -13780,12 +13780,12 @@ async function copyToClipboard(text) {
13780
13780
  }
13781
13781
  }
13782
13782
  function pipeToCommand(command, text, extraArgs = []) {
13783
- return new Promise((resolve9, reject) => {
13783
+ return new Promise((resolve10, reject) => {
13784
13784
  const child = (0, import_node_child_process5.spawn)(command, extraArgs, { stdio: ["pipe", "ignore", "ignore"] });
13785
13785
  child.on("error", reject);
13786
13786
  child.on("close", (code) => {
13787
13787
  if (code === 0) {
13788
- resolve9();
13788
+ resolve10();
13789
13789
  } else {
13790
13790
  reject(new Error(`${command} exited with code ${code ?? "unknown"}`));
13791
13791
  }
@@ -14690,7 +14690,7 @@ var VERSION;
14690
14690
  var init_version = __esm({
14691
14691
  "src/version.ts"() {
14692
14692
  "use strict";
14693
- VERSION = "1.5.3";
14693
+ VERSION = "1.6.0";
14694
14694
  }
14695
14695
  });
14696
14696
 
@@ -17100,6 +17100,323 @@ var init_runInit = __esm({
17100
17100
  }
17101
17101
  });
17102
17102
 
17103
+ // src/commands/checkChange.ts
17104
+ function findingKey(finding) {
17105
+ return `${finding.id}\0${finding.title}`;
17106
+ }
17107
+ function findingSubjects(finding) {
17108
+ const line = (finding.detail.split("\n").find((part) => part.trim()) ?? "").replace(LOCATION_TEXT, " ");
17109
+ return [...new Set((line.match(SUBJECT) ?? []).map((subject) => subject.toLowerCase()))].sort();
17110
+ }
17111
+ function bySeverity(a, b3) {
17112
+ return SEVERITY_RANK3[a.severity] - SEVERITY_RANK3[b3.severity] || a.title.localeCompare(b3.title);
17113
+ }
17114
+ function compareFindings(base, head, ref, baseCommit) {
17115
+ const baseByKey = new Map(base.map((finding) => [findingKey(finding), finding]));
17116
+ const headKeys = new Set(head.map(findingKey));
17117
+ const introduced = [];
17118
+ const resolved = base.filter((finding) => !headKeys.has(findingKey(finding)));
17119
+ const unchanged = [];
17120
+ for (const finding of head) {
17121
+ const before = baseByKey.get(findingKey(finding));
17122
+ if (!before) {
17123
+ introduced.push(finding);
17124
+ continue;
17125
+ }
17126
+ const was = new Set(findingSubjects(before));
17127
+ const now = findingSubjects(finding);
17128
+ const added = now.filter((subject) => !was.has(subject));
17129
+ const removed = [...was].filter((subject) => !now.includes(subject));
17130
+ if (added.length) introduced.push({ ...finding, subjects: added });
17131
+ else unchanged.push(finding);
17132
+ if (removed.length) resolved.push({ ...before, subjects: removed });
17133
+ }
17134
+ return { base: ref, baseCommit, introduced: introduced.sort(bySeverity), resolved: resolved.sort(bySeverity), unchanged: unchanged.sort(bySeverity) };
17135
+ }
17136
+ async function git(cwd, args) {
17137
+ const call = await safeGitInvocation(cwd, args);
17138
+ const { stdout } = await execFileAsync4("git", call.args, { cwd, env: call.env, windowsHide: true, maxBuffer: 64 * 1024 * 1024 });
17139
+ return stdout;
17140
+ }
17141
+ async function resolveBaseCommit(cwd, ref) {
17142
+ let commit;
17143
+ try {
17144
+ commit = (await git(cwd, ["rev-parse", "--verify", "--end-of-options", `${ref}^{commit}`])).trim();
17145
+ } catch {
17146
+ throw new Error(
17147
+ `Could not find "${ref}" in this repository. Fetch it first (git fetch origin ${ref.replace(/^origin\//, "")}); in GitHub Actions, check out with fetch-depth: 0.`
17148
+ );
17149
+ }
17150
+ try {
17151
+ const mergeBase = (await git(cwd, ["merge-base", commit, "HEAD"])).trim();
17152
+ return mergeBase || commit;
17153
+ } catch {
17154
+ return commit;
17155
+ }
17156
+ }
17157
+ function parseTree(output) {
17158
+ const entries = [];
17159
+ for (const record of output.split("\0")) {
17160
+ if (!record) continue;
17161
+ const tab = record.indexOf(" ");
17162
+ const [mode, type, sha, size] = record.slice(0, tab).trim().split(/\s+/);
17163
+ entries.push({ mode, type, sha, size: Number.parseInt(size, 10) || 0, path: record.slice(tab + 1) });
17164
+ }
17165
+ return entries;
17166
+ }
17167
+ function wanted(entry) {
17168
+ if (entry.type !== "blob" || entry.mode !== "100644" && entry.mode !== "100755") return false;
17169
+ if (entry.size > MAX_BLOB_BYTES) return false;
17170
+ return !entry.path.split("/").some((segment) => SKIP_SEGMENTS.has(segment));
17171
+ }
17172
+ async function readBlobs(cwd, shas) {
17173
+ const blobs = /* @__PURE__ */ new Map();
17174
+ if (!shas.length) return blobs;
17175
+ const call = await safeGitInvocation(cwd, ["cat-file", "--batch"]);
17176
+ const output = await new Promise((resolveOutput, reject) => {
17177
+ const child = (0, import_node_child_process6.spawn)("git", call.args, { cwd, env: call.env, windowsHide: true });
17178
+ const chunks = [];
17179
+ let stderr = "";
17180
+ child.stdout.on("data", (chunk) => chunks.push(chunk));
17181
+ child.stderr.on("data", (chunk) => {
17182
+ stderr += chunk.toString();
17183
+ });
17184
+ child.on("error", reject);
17185
+ child.on("close", (code) => code === 0 ? resolveOutput(Buffer.concat(chunks)) : reject(new Error(stderr.trim() || `git cat-file exited with ${code}`)));
17186
+ child.stdin.end(`${shas.join("\n")}
17187
+ `);
17188
+ });
17189
+ let offset = 0;
17190
+ while (offset < output.length) {
17191
+ const newline = output.indexOf(10, offset);
17192
+ if (newline === -1) break;
17193
+ const [sha, type, sizeText] = output.subarray(offset, newline).toString().split(" ");
17194
+ offset = newline + 1;
17195
+ if (type === "missing" || sizeText === void 0) continue;
17196
+ const size = Number.parseInt(sizeText, 10);
17197
+ blobs.set(sha, output.subarray(offset, offset + size));
17198
+ offset += size + 1;
17199
+ }
17200
+ return blobs;
17201
+ }
17202
+ async function exportCommit(cwd, commit) {
17203
+ const entries = parseTree(await git(cwd, ["ls-tree", "-r", "-z", "-l", "--full-tree", commit])).filter(wanted);
17204
+ const blobs = await readBlobs(cwd, [...new Set(entries.map((entry) => entry.sha))]);
17205
+ const root = await (0, import_promises23.mkdtemp)((0, import_node_path30.join)((0, import_node_os.tmpdir)(), "viberaven-base-"));
17206
+ for (const entry of entries) {
17207
+ const target = (0, import_node_path30.resolve)(root, ...entry.path.split("/"));
17208
+ if (!target.startsWith(root + import_node_path30.sep)) continue;
17209
+ const content = blobs.get(entry.sha);
17210
+ if (!content) continue;
17211
+ await (0, import_promises23.mkdir)((0, import_node_path30.dirname)(target), { recursive: true });
17212
+ await (0, import_promises23.writeFile)(target, content);
17213
+ }
17214
+ return root;
17215
+ }
17216
+ async function checkChange(cwd, ref, head) {
17217
+ const baseCommit = await resolveBaseCommit(cwd, ref);
17218
+ const folder = await exportCommit(cwd, baseCommit);
17219
+ try {
17220
+ const result = await runProjectScan({ workspacePath: folder });
17221
+ if (!result.ok) throw new Error(`The check on ${ref} did not finish: ${result.message}`);
17222
+ return compareFindings(result.artifact.gaps, head, ref, baseCommit);
17223
+ } finally {
17224
+ await (0, import_promises23.rm)(folder, { recursive: true, force: true, maxRetries: 3, retryDelay: 200 }).catch(() => void 0);
17225
+ }
17226
+ }
17227
+ function cell(text) {
17228
+ return text.replace(/\r?\n/g, " ").replace(/\|/g, "\\|").trim();
17229
+ }
17230
+ function firstLine(detail) {
17231
+ const line = detail.split("\n").map((part) => part.trim()).find(Boolean) ?? "";
17232
+ return line.length > 180 ? `${line.slice(0, 177)}\u2026` : line;
17233
+ }
17234
+ function plural2(count, word) {
17235
+ return `${count} ${word}${count === 1 ? "" : "s"}`;
17236
+ }
17237
+ function table(findings) {
17238
+ return [
17239
+ "| | Finding | Check |",
17240
+ "|---|---|---|",
17241
+ ...findings.map((finding) => {
17242
+ const why = finding.subjects?.length ? `New in this change: ${finding.subjects.join(", ")}` : firstLine(finding.detail);
17243
+ return `| ${DOT[finding.severity]} | **${cell(finding.title)}**${why ? `<br>${cell(why)}` : ""} | \`${cell(finding.id)}\` |`;
17244
+ })
17245
+ ];
17246
+ }
17247
+ function fixedLabel(finding) {
17248
+ return `\u2705 ${cell(finding.title)}${finding.subjects?.length ? ` for ${cell(finding.subjects.join(", "))}` : ""} (\`${cell(finding.id)}\`)`;
17249
+ }
17250
+ function renderCheckMarkdown({ head, change, version, score }) {
17251
+ const lines = [COMMENT_MARKER];
17252
+ const sorted = [...head].sort(bySeverity);
17253
+ const critical = sorted.filter((finding) => finding.severity === "critical").length;
17254
+ const warnings = sorted.filter((finding) => finding.severity === "warning").length;
17255
+ if (change) {
17256
+ const newCritical = change.introduced.filter((finding) => finding.severity === "critical").length;
17257
+ const headline = newCritical ? `${plural2(newCritical, "new blocker")} in this change` : change.introduced.length ? `${plural2(change.introduced.length, "new finding")} in this change, no new blockers` : "No new findings in this change";
17258
+ lines.push(`### VibeRaven \xB7 ${headline}`, "");
17259
+ lines.push(`Compared with \`${change.base}\` (${change.baseCommit.slice(0, 7)}).${change.resolved.length ? ` This change fixes ${plural2(change.resolved.length, "finding")}.` : ""}`, "");
17260
+ if (change.introduced.length) lines.push(...table(change.introduced), "");
17261
+ if (change.resolved.length) {
17262
+ lines.push(`**Fixed by this change:** ${change.resolved.map(fixedLabel).join(" \xB7 ")}`, "");
17263
+ }
17264
+ if (change.unchanged.length) {
17265
+ lines.push(`<details><summary>Already in ${cell(change.base)} before this change (${change.unchanged.length})</summary>`, "", ...table(change.unchanged), "", "</details>", "");
17266
+ }
17267
+ const firstNew = change.introduced.find((finding) => finding.severity === "critical") ?? change.introduced[0];
17268
+ if (firstNew) lines.push(`Fix it with your coding agent: \`npx -y viberaven fix --gap ${firstNew.id}\`, or open the Studio with \`npx -y viberaven\`.`, "");
17269
+ } else {
17270
+ const headline = critical ? `${plural2(critical, "blocker")}, ${plural2(warnings, "warning")}` : `No blockers, ${plural2(warnings, "warning")}`;
17271
+ lines.push(`### VibeRaven \xB7 ${headline}`, "");
17272
+ if (sorted.length) lines.push(...table(sorted), "");
17273
+ else lines.push("No launch findings in the repo evidence.", "");
17274
+ }
17275
+ lines.push(
17276
+ `<sub>viberaven ${version}${typeof score === "number" ? ` \xB7 readiness ${score}/100` : ""} \xB7 checked where it ran, nothing was uploaded. Advice, not a gate: a clear result reads the repo, it is not a security audit.</sub>`
17277
+ );
17278
+ return `${lines.join("\n")}
17279
+ `;
17280
+ }
17281
+ var import_node_child_process6, import_promises23, import_node_os, import_node_path30, import_node_util4, execFileAsync4, MAX_BLOB_BYTES, SKIP_SEGMENTS, SEVERITY_RANK3, LOCATION_TEXT, SUBJECT, COMMENT_MARKER, DOT;
17282
+ var init_checkChange = __esm({
17283
+ "src/commands/checkChange.ts"() {
17284
+ "use strict";
17285
+ import_node_child_process6 = require("node:child_process");
17286
+ import_promises23 = require("node:fs/promises");
17287
+ import_node_os = require("node:os");
17288
+ import_node_path30 = require("node:path");
17289
+ import_node_util4 = require("node:util");
17290
+ init_runScan();
17291
+ init_safeGit();
17292
+ execFileAsync4 = (0, import_node_util4.promisify)(import_node_child_process6.execFile);
17293
+ MAX_BLOB_BYTES = 1e6;
17294
+ SKIP_SEGMENTS = /* @__PURE__ */ new Set(["node_modules", ".git", ".next", ".turbo", ".vercel", ".output", "dist", "build", "coverage"]);
17295
+ SEVERITY_RANK3 = { critical: 0, warning: 1, info: 2 };
17296
+ LOCATION_TEXT = /[\w./\\-]+\.(?:sql|tsx?|jsx?|mjs|cjs|json|toml|ya?ml|env|prisma|md)(?::\d+)?/gi;
17297
+ SUBJECT = /\b[a-z_][a-z0-9_]*\.[a-z_][a-z0-9_]*\b/gi;
17298
+ COMMENT_MARKER = "<!-- viberaven-check -->";
17299
+ DOT = { critical: "\u{1F534}", warning: "\u{1F7E1}", info: "\u26AA" };
17300
+ }
17301
+ });
17302
+
17303
+ // src/commands/initGithub.ts
17304
+ function renderWorkflow(version = VERSION) {
17305
+ return [
17306
+ "# VibeRaven checks every pull request in this repository's own GitHub Actions run.",
17307
+ "# The code is read on the runner and never uploaded. Written by `npx -y viberaven init --github`.",
17308
+ "name: VibeRaven",
17309
+ "",
17310
+ "on:",
17311
+ " pull_request:",
17312
+ "",
17313
+ "permissions:",
17314
+ " contents: read",
17315
+ " pull-requests: write",
17316
+ "",
17317
+ "jobs:",
17318
+ " check:",
17319
+ " name: Launch check",
17320
+ " runs-on: ubuntu-latest",
17321
+ " steps:",
17322
+ " - uses: actions/checkout@v4",
17323
+ " with:",
17324
+ " fetch-depth: 0",
17325
+ "",
17326
+ " - uses: actions/setup-node@v4",
17327
+ " with:",
17328
+ " node-version: 20",
17329
+ "",
17330
+ " - name: Check this change",
17331
+ " id: check",
17332
+ " env:",
17333
+ " BASE_REF: ${{ github.base_ref }}",
17334
+ " run: |",
17335
+ " code=0",
17336
+ ` npx -y viberaven@${version} check --base "origin/$BASE_REF" --markdown > viberaven-check.md || code=$?`,
17337
+ ' cat viberaven-check.md >> "$GITHUB_STEP_SUMMARY"',
17338
+ ' echo "code=$code" >> "$GITHUB_OUTPUT"',
17339
+ "",
17340
+ " # Pull requests from forks get a read-only token: they keep the job summary and skip the comment.",
17341
+ " - name: Comment on the pull request",
17342
+ " if: github.event.pull_request.head.repo.full_name == github.repository",
17343
+ " env:",
17344
+ " GH_TOKEN: ${{ github.token }}",
17345
+ " PR: ${{ github.event.pull_request.number }}",
17346
+ " run: |",
17347
+ ` id=$(gh api "repos/$GITHUB_REPOSITORY/issues/$PR/comments" --paginate --jq '.[] | select(.body | contains("${COMMENT_MARKER}")) | .id' | tail -n 1)`,
17348
+ ' if [ -n "$id" ]; then',
17349
+ ' gh api --method PATCH "repos/$GITHUB_REPOSITORY/issues/comments/$id" -F body=@viberaven-check.md > /dev/null',
17350
+ " else",
17351
+ ' gh pr comment "$PR" --body-file viberaven-check.md',
17352
+ " fi",
17353
+ "",
17354
+ " # Advice, not a gate. To block merging when a change adds a launch blocker, uncomment this step",
17355
+ ' # and mark "Launch check" as required in the branch protection settings.',
17356
+ " # - name: Fail on new blockers",
17357
+ " # if: steps.check.outputs.code == '1'",
17358
+ " # run: exit 1",
17359
+ ""
17360
+ ].join("\n");
17361
+ }
17362
+ async function hasGithubRemote(cwd) {
17363
+ try {
17364
+ const call = await safeGitInvocation(cwd, ["remote", "-v"]);
17365
+ const { stdout } = await execFileAsync5("git", call.args, { cwd, env: call.env, windowsHide: true });
17366
+ return /github\.com[:/]/i.test(stdout);
17367
+ } catch {
17368
+ return false;
17369
+ }
17370
+ }
17371
+ async function githubWorkflowStatus(cwd) {
17372
+ return { exists: (0, import_node_fs14.existsSync)((0, import_node_path31.join)(cwd, WORKFLOW_PATH)), path: DISPLAY_PATH, onGithub: await hasGithubRemote(cwd) };
17373
+ }
17374
+ async function writeGithubWorkflow(cwd, options = {}) {
17375
+ const target = (0, import_node_path31.join)(cwd, WORKFLOW_PATH);
17376
+ if ((0, import_node_fs14.existsSync)(target) && !options.force) return { written: false, status: await githubWorkflowStatus(cwd) };
17377
+ await (0, import_promises24.mkdir)((0, import_node_path31.dirname)(target), { recursive: true });
17378
+ await (0, import_promises24.writeFile)(target, renderWorkflow(), "utf8");
17379
+ return { written: true, status: await githubWorkflowStatus(cwd) };
17380
+ }
17381
+ async function runInitGithubCommand(options) {
17382
+ if (options.dryRun) {
17383
+ console.log(`Would write ${DISPLAY_PATH}:
17384
+ `);
17385
+ console.log(renderWorkflow());
17386
+ return 0;
17387
+ }
17388
+ const { written, status } = await writeGithubWorkflow(options.cwd, { force: options.force });
17389
+ if (!written) {
17390
+ console.log(`${DISPLAY_PATH} already exists. Nothing changed. Run with --force to replace it.`);
17391
+ return 0;
17392
+ }
17393
+ console.log(`Wrote ${DISPLAY_PATH}.`);
17394
+ console.log("");
17395
+ console.log("Commit it, and every pull request gets one VibeRaven comment with what the change added or fixed:");
17396
+ console.log(` git add ${DISPLAY_PATH} && git commit -m "Check pull requests with VibeRaven"`);
17397
+ console.log("");
17398
+ console.log("It runs in your own GitHub Actions, so private repositories work and the code is never uploaded.");
17399
+ if (!status.onGithub) console.log("This repository has no github.com remote yet: the workflow runs once it is pushed to GitHub.");
17400
+ return 0;
17401
+ }
17402
+ var import_node_child_process7, import_node_fs14, import_promises24, import_node_path31, import_node_util5, execFileAsync5, WORKFLOW_PATH, DISPLAY_PATH;
17403
+ var init_initGithub = __esm({
17404
+ "src/commands/initGithub.ts"() {
17405
+ "use strict";
17406
+ import_node_child_process7 = require("node:child_process");
17407
+ import_node_fs14 = require("node:fs");
17408
+ import_promises24 = require("node:fs/promises");
17409
+ import_node_path31 = require("node:path");
17410
+ import_node_util5 = require("node:util");
17411
+ init_safeGit();
17412
+ init_version();
17413
+ init_checkChange();
17414
+ execFileAsync5 = (0, import_node_util5.promisify)(import_node_child_process7.execFile);
17415
+ WORKFLOW_PATH = (0, import_node_path31.join)(".github", "workflows", "viberaven.yml");
17416
+ DISPLAY_PATH = ".github/workflows/viberaven.yml";
17417
+ }
17418
+ });
17419
+
17103
17420
  // src/commands/doctorAgents.ts
17104
17421
  function managedBlocks(content) {
17105
17422
  const blocks = content.match(/<!-- (?:VIBERAVEN:START|viberaven:agent-rules:start) -->[\s\S]*?<!-- (?:VIBERAVEN:END|viberaven:agent-rules:end) -->/g);
@@ -17108,7 +17425,7 @@ function managedBlocks(content) {
17108
17425
  async function checkAgentInjection(cwd) {
17109
17426
  const checks = [];
17110
17427
  for (const item4 of REQUIRED_EXISTENCE_CHECKS) {
17111
- const exists = await fileExists2((0, import_node_path30.join)(cwd, item4.file));
17428
+ const exists = await fileExists2((0, import_node_path32.join)(cwd, item4.file));
17112
17429
  checks.push({
17113
17430
  id: item4.id,
17114
17431
  status: exists ? "pass" : "fail",
@@ -17116,17 +17433,17 @@ async function checkAgentInjection(cwd) {
17116
17433
  });
17117
17434
  }
17118
17435
  for (const item4 of OPTIONAL_CURSOR_PACK_CHECKS) {
17119
- const exists = await fileExists2((0, import_node_path30.join)(cwd, item4.file));
17436
+ const exists = await fileExists2((0, import_node_path32.join)(cwd, item4.file));
17120
17437
  checks.push({
17121
17438
  id: item4.id,
17122
17439
  status: exists ? "pass" : "fail",
17123
17440
  message: exists ? `${item4.file} exists` : `Missing ${item4.file} \u2014 run npx -y viberaven init --agents all`
17124
17441
  });
17125
17442
  }
17126
- const legacyCursorPath = (0, import_node_path30.join)(cwd, ".cursor/rules/viberaven.mdc");
17443
+ const legacyCursorPath = (0, import_node_path32.join)(cwd, ".cursor/rules/viberaven.mdc");
17127
17444
  if (await fileExists2(legacyCursorPath)) {
17128
- const legacyContent = await (0, import_promises23.readFile)(legacyCursorPath, "utf-8");
17129
- const hasCoreSplit = await fileExists2((0, import_node_path30.join)(cwd, ".cursor/rules/viberaven-core.mdc"));
17445
+ const legacyContent = await (0, import_promises25.readFile)(legacyCursorPath, "utf-8");
17446
+ const hasCoreSplit = await fileExists2((0, import_node_path32.join)(cwd, ".cursor/rules/viberaven-core.mdc"));
17130
17447
  if (!hasCoreSplit || legacyContent.includes("alwaysApply: true")) {
17131
17448
  checks.push({
17132
17449
  id: "cursor-legacy-mdc",
@@ -17137,7 +17454,7 @@ async function checkAgentInjection(cwd) {
17137
17454
  }
17138
17455
  for (const target of CORE_AGENT_INJECTION_TARGETS) {
17139
17456
  const file = target === "cursor" ? ".cursor/rules/viberaven-core.mdc" : AGENT_RULE_TARGETS[target].file;
17140
- const path = (0, import_node_path30.join)(cwd, file);
17457
+ const path = (0, import_node_path32.join)(cwd, file);
17141
17458
  const exists = await fileExists2(path);
17142
17459
  if (!exists) {
17143
17460
  checks.push({
@@ -17147,7 +17464,7 @@ async function checkAgentInjection(cwd) {
17147
17464
  });
17148
17465
  continue;
17149
17466
  }
17150
- const content = await (0, import_promises23.readFile)(path, "utf-8");
17467
+ const content = await (0, import_promises25.readFile)(path, "utf-8");
17151
17468
  const hasCommand = content.includes(PUBLIC_CHECK_COMMAND);
17152
17469
  checks.push({
17153
17470
  id: `canonical-${target}`,
@@ -17190,18 +17507,18 @@ function formatDoctorAgentsReport(report) {
17190
17507
  }
17191
17508
  async function fileExists2(path) {
17192
17509
  try {
17193
- await (0, import_promises23.access)(path);
17510
+ await (0, import_promises25.access)(path);
17194
17511
  return true;
17195
17512
  } catch {
17196
17513
  return false;
17197
17514
  }
17198
17515
  }
17199
- var import_promises23, import_node_path30, REQUIRED_EXISTENCE_CHECKS, OPTIONAL_CURSOR_PACK_CHECKS, STALE_PATTERNS, GATE_RULE_PHRASES_1_5_2;
17516
+ var import_promises25, import_node_path32, REQUIRED_EXISTENCE_CHECKS, OPTIONAL_CURSOR_PACK_CHECKS, STALE_PATTERNS, GATE_RULE_PHRASES_1_5_2;
17200
17517
  var init_doctorAgents = __esm({
17201
17518
  "src/commands/doctorAgents.ts"() {
17202
17519
  "use strict";
17203
- import_promises23 = require("node:fs/promises");
17204
- import_node_path30 = require("node:path");
17520
+ import_promises25 = require("node:fs/promises");
17521
+ import_node_path32 = require("node:path");
17205
17522
  init_commands();
17206
17523
  init_agentTargets();
17207
17524
  REQUIRED_EXISTENCE_CHECKS = [
@@ -17472,7 +17789,7 @@ function buildContextCleanupPlan(input) {
17472
17789
  const items = [];
17473
17790
  const seen = /* @__PURE__ */ new Set();
17474
17791
  for (const file of input.files) {
17475
- const path = normalizePath5((0, import_node_path31.relative)(input.projectRoot, file.path) || file.path);
17792
+ const path = normalizePath5((0, import_node_path33.relative)(input.projectRoot, file.path) || file.path);
17476
17793
  const fullPath = normalizePath5(file.path);
17477
17794
  const category = categorizeCleanupFile(path, fullPath, file);
17478
17795
  if (!category || seen.has(path)) {
@@ -17498,10 +17815,10 @@ async function collectCleanupFiles(projectRoot) {
17498
17815
  return files;
17499
17816
  }
17500
17817
  async function writeCleanupPlan(projectRoot, plan) {
17501
- const outputDir = (0, import_node_path31.join)(projectRoot, ".viberaven");
17502
- const outputPath = (0, import_node_path31.join)(outputDir, "context-cleanup.md");
17503
- await (0, import_promises24.mkdir)(outputDir, { recursive: true });
17504
- await (0, import_promises24.writeFile)(outputPath, renderCleanupPlan(plan), "utf8");
17818
+ const outputDir = (0, import_node_path33.join)(projectRoot, ".viberaven");
17819
+ const outputPath = (0, import_node_path33.join)(outputDir, "context-cleanup.md");
17820
+ await (0, import_promises26.mkdir)(outputDir, { recursive: true });
17821
+ await (0, import_promises26.writeFile)(outputPath, renderCleanupPlan(plan), "utf8");
17505
17822
  return outputPath;
17506
17823
  }
17507
17824
  function categorizeCleanupFile(relativePath, fullPath, file) {
@@ -17549,7 +17866,7 @@ async function walkCleanupFiles(root, dir, result, depth) {
17549
17866
  }
17550
17867
  let entries;
17551
17868
  try {
17552
- entries = await (0, import_promises24.readdir)(dir, { withFileTypes: true });
17869
+ entries = await (0, import_promises26.readdir)(dir, { withFileTypes: true });
17553
17870
  } catch {
17554
17871
  return;
17555
17872
  }
@@ -17557,9 +17874,9 @@ async function walkCleanupFiles(root, dir, result, depth) {
17557
17874
  if (SKIP_DIRECTORIES.has(entry.name)) {
17558
17875
  continue;
17559
17876
  }
17560
- const absolute = (0, import_node_path31.join)(dir, entry.name);
17877
+ const absolute = (0, import_node_path33.join)(dir, entry.name);
17561
17878
  if (entry.isDirectory()) {
17562
- if (isCachePath(normalizePath5((0, import_node_path31.relative)(root, absolute)))) {
17879
+ if (isCachePath(normalizePath5((0, import_node_path33.relative)(root, absolute)))) {
17563
17880
  result.push({ path: absolute, sizeBytes: 0, kind: "directory" });
17564
17881
  continue;
17565
17882
  }
@@ -17570,7 +17887,7 @@ async function walkCleanupFiles(root, dir, result, depth) {
17570
17887
  continue;
17571
17888
  }
17572
17889
  try {
17573
- const fileStat = await (0, import_promises24.stat)(absolute);
17890
+ const fileStat = await (0, import_promises26.stat)(absolute);
17574
17891
  result.push({ path: absolute, sizeBytes: fileStat.size, kind: "file" });
17575
17892
  } catch {
17576
17893
  continue;
@@ -17580,12 +17897,12 @@ async function walkCleanupFiles(root, dir, result, depth) {
17580
17897
  function normalizePath5(path) {
17581
17898
  return path.replace(/\\/g, "/");
17582
17899
  }
17583
- var import_promises24, import_node_path31, MAX_WALK_DEPTH, LARGE_LOG_BYTES, SKIP_DIRECTORIES;
17900
+ var import_promises26, import_node_path33, MAX_WALK_DEPTH, LARGE_LOG_BYTES, SKIP_DIRECTORIES;
17584
17901
  var init_cleanPlan = __esm({
17585
17902
  "src/commands/cleanPlan.ts"() {
17586
17903
  "use strict";
17587
- import_promises24 = require("node:fs/promises");
17588
- import_node_path31 = require("node:path");
17904
+ import_promises26 = require("node:fs/promises");
17905
+ import_node_path33 = require("node:path");
17589
17906
  MAX_WALK_DEPTH = 5;
17590
17907
  LARGE_LOG_BYTES = 8192;
17591
17908
  SKIP_DIRECTORIES = /* @__PURE__ */ new Set([".git", "node_modules"]);
@@ -17698,10 +18015,10 @@ var init_render = __esm({
17698
18015
 
17699
18016
  // src/commands/actions.ts
17700
18017
  async function runActionsCommand(input) {
17701
- const actionsPath = (0, import_node_path32.join)(getProjectArtifactsDir(input.cwd), "actions.json");
18018
+ const actionsPath = (0, import_node_path34.join)(getProjectArtifactsDir(input.cwd), "actions.json");
17702
18019
  let raw;
17703
18020
  try {
17704
- raw = await (0, import_promises25.readFile)(actionsPath, "utf8");
18021
+ raw = await (0, import_promises27.readFile)(actionsPath, "utf8");
17705
18022
  } catch {
17706
18023
  process.stderr.write("No VibeRaven actions found. Run `npx -y viberaven --agent-mode` first.\n");
17707
18024
  return 1;
@@ -17715,12 +18032,12 @@ async function runActionsCommand(input) {
17715
18032
  process.stdout.write(renderActionSurface(manifest));
17716
18033
  return 0;
17717
18034
  }
17718
- var import_promises25, import_node_path32;
18035
+ var import_promises27, import_node_path34;
17719
18036
  var init_actions = __esm({
17720
18037
  "src/commands/actions.ts"() {
17721
18038
  "use strict";
17722
- import_promises25 = require("node:fs/promises");
17723
- import_node_path32 = require("node:path");
18039
+ import_promises27 = require("node:fs/promises");
18040
+ import_node_path34 = require("node:path");
17724
18041
  init_render();
17725
18042
  init_config();
17726
18043
  }
@@ -17729,7 +18046,7 @@ var init_actions = __esm({
17729
18046
  // src/commands/verifyAction.ts
17730
18047
  async function currentActionExists(cwd, actionId) {
17731
18048
  try {
17732
- const raw = await (0, import_promises26.readFile)((0, import_node_path33.join)(getProjectArtifactsDir(cwd), "actions.json"), "utf8");
18049
+ const raw = await (0, import_promises28.readFile)((0, import_node_path35.join)(getProjectArtifactsDir(cwd), "actions.json"), "utf8");
17733
18050
  const manifest = JSON.parse(raw);
17734
18051
  return manifest.actions.some((action) => action.id === actionId);
17735
18052
  } catch {
@@ -17773,12 +18090,12 @@ This only looks the action up. Run \`npx -y viberaven --verify\` to scan the rep
17773
18090
  );
17774
18091
  return 0;
17775
18092
  }
17776
- var import_promises26, import_node_path33;
18093
+ var import_promises28, import_node_path35;
17777
18094
  var init_verifyAction = __esm({
17778
18095
  "src/commands/verifyAction.ts"() {
17779
18096
  "use strict";
17780
- import_promises26 = require("node:fs/promises");
17781
- import_node_path33 = require("node:path");
18097
+ import_promises28 = require("node:fs/promises");
18098
+ import_node_path35 = require("node:path");
17782
18099
  init_registry2();
17783
18100
  init_config();
17784
18101
  }
@@ -17902,11 +18219,36 @@ var init_preview = __esm({
17902
18219
  async function runCheckCommand(options) {
17903
18220
  const result = await runProjectScan({ workspacePath: options.cwd });
17904
18221
  if (!result.ok) {
18222
+ if (options.markdown) console.log(`${COMMENT_MARKER}
18223
+ ### VibeRaven could not check this change
18224
+
18225
+ ${result.message}
18226
+ `);
17905
18227
  console.error(formatAgentStatus(ERROR, result.message));
17906
18228
  return 2;
17907
18229
  }
17908
18230
  const artifact = result.artifact;
17909
18231
  const paths = await writeScanArtifacts({ artifact, cwd: options.cwd });
18232
+ let change;
18233
+ if (options.base) {
18234
+ try {
18235
+ change = await checkChange(options.cwd, options.base, artifact.gaps);
18236
+ } catch (error) {
18237
+ const message = error instanceof Error ? error.message : String(error);
18238
+ if (options.markdown) console.log(`${COMMENT_MARKER}
18239
+ ### VibeRaven could not compare this change
18240
+
18241
+ ${message}
18242
+ `);
18243
+ console.error(formatAgentStatus(ERROR, message));
18244
+ return 2;
18245
+ }
18246
+ }
18247
+ const failing = change ? change.introduced.some((g2) => g2.severity === "critical") : artifact.gaps.some((g2) => g2.severity === "critical");
18248
+ if (options.markdown) {
18249
+ console.log(renderCheckMarkdown({ head: artifact.gaps, change, version: VERSION, score: artifact.score }));
18250
+ return failing ? 1 : 0;
18251
+ }
17910
18252
  if (options.json) {
17911
18253
  console.log(
17912
18254
  JSON.stringify(
@@ -17921,13 +18263,22 @@ async function runCheckCommand(options) {
17921
18263
  title: gap.title,
17922
18264
  detail: gap.detail
17923
18265
  })),
18266
+ ...change ? {
18267
+ change: {
18268
+ base: change.base,
18269
+ baseCommit: change.baseCommit,
18270
+ introduced: change.introduced.map((gap) => gap.id),
18271
+ resolved: change.resolved.map((gap) => gap.id),
18272
+ unchanged: change.unchanged.map((gap) => gap.id)
18273
+ }
18274
+ } : {},
17924
18275
  artifacts: paths
17925
18276
  },
17926
18277
  null,
17927
18278
  2
17928
18279
  )
17929
18280
  );
17930
- return artifact.gaps.some((g2) => g2.severity === "critical") ? 1 : 0;
18281
+ return failing ? 1 : 0;
17931
18282
  }
17932
18283
  const sorted = [...artifact.gaps].sort(
17933
18284
  (a, b3) => SEVERITY_ORDER3[a.severity] - SEVERITY_ORDER3[b3.severity]
@@ -17937,14 +18288,37 @@ async function runCheckCommand(options) {
17937
18288
  console.log("");
17938
18289
  console.log(`viberaven check \xB7 ${options.cwd}`);
17939
18290
  console.log("");
18291
+ if (change) {
18292
+ const line = (gap) => `${SEVERITY_ICON[gap.severity]} ${gap.title} (${gap.id})`;
18293
+ console.log(`Compared with ${change.base} (${change.baseCommit.slice(0, 7)})`);
18294
+ console.log("");
18295
+ console.log(change.introduced.length ? "New in this change:" : "New in this change: nothing");
18296
+ for (const gap of change.introduced) {
18297
+ console.log(` ${line(gap)}${gap.subjects?.length ? ` new: ${gap.subjects.join(", ")}` : ""}`);
18298
+ if (!options.quiet && !gap.subjects?.length && gap.detail) console.log(` ${gap.detail.split("\n")[0]}`);
18299
+ }
18300
+ if (change.resolved.length) {
18301
+ console.log("Fixed by this change:");
18302
+ for (const gap of change.resolved) console.log(` \u2705 ${gap.title}${gap.subjects?.length ? ` for ${gap.subjects.join(", ")}` : ""} (${gap.id})`);
18303
+ }
18304
+ if (change.unchanged.length) console.log(`Already there before: ${change.unchanged.length}`);
18305
+ const newCritical = change.introduced.filter((g2) => g2.severity === "critical").length;
18306
+ console.log("");
18307
+ console.log(
18308
+ newCritical > 0 ? `Verdict: \u274C ${newCritical} new blocker${newCritical === 1 ? "" : "s"} in this change \xB7 score ${artifact.score}` : `Verdict: \u2705 no new blockers in this change \xB7 score ${artifact.score}`
18309
+ );
18310
+ console.log(`Fix: viberaven fix \xB7 Details: ${paths.tasklistPath}`);
18311
+ console.log("");
18312
+ return failing ? 1 : 0;
18313
+ }
17940
18314
  if (sorted.length === 0) {
17941
18315
  console.log("\u2705 No launch blockers found in repo evidence.");
17942
18316
  } else {
17943
18317
  for (const gap of sorted) {
17944
18318
  console.log(`${SEVERITY_ICON[gap.severity]} ${gap.title} (${gap.id})`);
17945
18319
  if (!options.quiet && gap.detail) {
17946
- const firstLine2 = gap.detail.split("\n")[0];
17947
- console.log(` ${firstLine2}`);
18320
+ const firstLine3 = gap.detail.split("\n")[0];
18321
+ console.log(` ${firstLine3}`);
17948
18322
  }
17949
18323
  }
17950
18324
  }
@@ -17963,6 +18337,8 @@ var init_check = __esm({
17963
18337
  init_runScan();
17964
18338
  init_artifacts2();
17965
18339
  init_statusLabels();
18340
+ init_version();
18341
+ init_checkChange();
17966
18342
  SEVERITY_ICON = {
17967
18343
  critical: "\u{1F534}",
17968
18344
  // red circle
@@ -18006,12 +18382,12 @@ function parsePermissionsMatrix(text) {
18006
18382
  } else {
18007
18383
  const seen = /* @__PURE__ */ new Set();
18008
18384
  for (const [key, value] of Object.entries(raw.tables)) {
18009
- const table = parseTable(key, value, problems);
18010
- if (!table) continue;
18011
- const full = `${table.schema}.${table.name}`;
18385
+ const table2 = parseTable(key, value, problems);
18386
+ if (!table2) continue;
18387
+ const full = `${table2.schema}.${table2.name}`;
18012
18388
  if (seen.has(full)) problems.push(`tables[${show(key)}]: ${full} is listed twice`);
18013
18389
  seen.add(full);
18014
- tables.push(table);
18390
+ tables.push(table2);
18015
18391
  }
18016
18392
  }
18017
18393
  if (problems.length > 0) throw new MatrixError(problems);
@@ -18085,10 +18461,10 @@ function parseTable(key, value, problems) {
18085
18461
  }
18086
18462
  const parsed = {};
18087
18463
  for (const action of MATRIX_ACTIONS) {
18088
- const cell = cells[action];
18089
- if (cell === void 0) problems.push(`${actorAt}.${action}: missing; write ${quoteList(CELL_VALUES)}`);
18090
- else if (!CELL_VALUES.includes(cell)) problems.push(`${actorAt}.${action}: must be ${quoteList(CELL_VALUES)} (got ${show(cell)})`);
18091
- else parsed[action] = cell;
18464
+ const cell2 = cells[action];
18465
+ if (cell2 === void 0) problems.push(`${actorAt}.${action}: missing; write ${quoteList(CELL_VALUES)}`);
18466
+ else if (!CELL_VALUES.includes(cell2)) problems.push(`${actorAt}.${action}: must be ${quoteList(CELL_VALUES)} (got ${show(cell2)})`);
18467
+ else parsed[action] = cell2;
18092
18468
  }
18093
18469
  if (cells.reassign !== void 0) {
18094
18470
  if (!CELL_VALUES.includes(cells.reassign)) problems.push(`${actorAt}.reassign: must be ${quoteList(CELL_VALUES)} (got ${show(cells.reassign)})`);
@@ -18308,13 +18684,13 @@ async function createWorkerDatabase(options) {
18308
18684
  const call = (message, timeoutMs) => {
18309
18685
  if (dead) return Promise.reject(dead);
18310
18686
  const id = ++nextId;
18311
- return new Promise((resolve9, reject) => {
18687
+ return new Promise((resolve10, reject) => {
18312
18688
  const timer = setTimeout(() => {
18313
18689
  const error = new RlsTimeoutError(`timed out after ${Math.round(timeoutMs / 1e3)} s`);
18314
18690
  failAll(error);
18315
18691
  void worker.terminate();
18316
18692
  }, timeoutMs);
18317
- pending.set(id, { resolve: resolve9, reject, timer });
18693
+ pending.set(id, { resolve: resolve10, reject, timer });
18318
18694
  worker.postMessage({ ...message, id });
18319
18695
  });
18320
18696
  };
@@ -18399,22 +18775,22 @@ parentPort.on('message', async (m) => {
18399
18775
  // src/rls/rlsRunner.ts
18400
18776
  function sqlError(error) {
18401
18777
  const e2 = error;
18402
- return { code: typeof e2?.code === "string" ? e2.code : "unknown", message: firstLine(typeof e2?.message === "string" ? e2.message : String(error)) };
18778
+ return { code: typeof e2?.code === "string" ? e2.code : "unknown", message: firstLine2(typeof e2?.message === "string" ? e2.message : String(error)) };
18403
18779
  }
18404
18780
  function isSqlError(error) {
18405
18781
  return !(error instanceof RlsTimeoutError) && typeof error?.code === "string";
18406
18782
  }
18407
18783
  function displayPath(projectDir, path) {
18408
18784
  for (const base of [projectDir, process.cwd()]) {
18409
- const rel = (0, import_node_path34.relative)(base, path);
18410
- if (rel && !rel.startsWith("..") && !(0, import_node_path34.isAbsolute)(rel)) return rel.split("\\").join("/");
18785
+ const rel = (0, import_node_path36.relative)(base, path);
18786
+ if (rel && !rel.startsWith("..") && !(0, import_node_path36.isAbsolute)(rel)) return rel.split("\\").join("/");
18411
18787
  }
18412
18788
  return path;
18413
18789
  }
18414
18790
  async function gitCommit(projectDir, paths) {
18415
18791
  const run = async (args) => {
18416
18792
  const call = await safeGitInvocation(projectDir, args);
18417
- const { stdout } = await (0, import_node_util4.promisify)(import_node_child_process6.execFile)("git", call.args, { cwd: projectDir, env: call.env, windowsHide: true, timeout: 5e3, encoding: "utf8" });
18793
+ const { stdout } = await (0, import_node_util6.promisify)(import_node_child_process8.execFile)("git", call.args, { cwd: projectDir, env: call.env, windowsHide: true, timeout: 5e3, encoding: "utf8" });
18418
18794
  return String(stdout);
18419
18795
  };
18420
18796
  let sha;
@@ -18487,11 +18863,11 @@ function valueCandidates(column, label) {
18487
18863
  if (column.category === "A") return u2 ? [] : ["{}"];
18488
18864
  return [];
18489
18865
  }
18490
- async function describeTable(db, table) {
18491
- const display = `${table.schema}.${table.name}`;
18866
+ async function describeTable(db, table2) {
18867
+ const display = `${table2.schema}.${table2.name}`;
18492
18868
  const rel = await db.query(
18493
18869
  `select c.oid::int as oid, c.relkind::text as kind from pg_class c join pg_namespace n on n.oid = c.relnamespace where n.nspname = $1 and c.relname = $2`,
18494
- [table.schema, table.name]
18870
+ [table2.schema, table2.name]
18495
18871
  );
18496
18872
  if (rel.rows.length === 0) return { reason: `${display} does not exist after the migrations; check the table name in the matrix.` };
18497
18873
  const kind = String(rel.rows[0].kind);
@@ -18530,35 +18906,35 @@ async function describeTable(db, table) {
18530
18906
  };
18531
18907
  });
18532
18908
  const byName = new Map(columns.map((c) => [c.name, c]));
18533
- const owner = byName.get(table.ownerColumn);
18534
- if (!owner) return { reason: `${display} has no column ${table.ownerColumn} (the matrix owner column).` };
18909
+ const owner = byName.get(table2.ownerColumn);
18910
+ if (!owner) return { reason: `${display} has no column ${table2.ownerColumn} (the matrix owner column).` };
18535
18911
  if (owner.typname !== "uuid" && owner.category !== "S") {
18536
- return { reason: `${display}.${table.ownerColumn} is ${owner.type}; the owner column must hold auth.uid() (uuid or text).` };
18912
+ return { reason: `${display}.${table2.ownerColumn} is ${owner.type}; the owner column must hold auth.uid() (uuid or text).` };
18537
18913
  }
18538
18914
  const pk = columns.filter((c) => c.isPk);
18539
18915
  if (pk.length === 0) return { reason: `${display} has no primary key; rls-test targets the owner's row by primary key.` };
18540
- for (const column of Object.keys(table.seed)) {
18916
+ for (const column of Object.keys(table2.seed)) {
18541
18917
  if (!byName.has(column)) return { reason: `the matrix seeds ${display}.${column}, which does not exist.` };
18542
18918
  }
18543
- if (table.updateColumn) {
18544
- const column = byName.get(table.updateColumn);
18545
- if (!column) return { reason: `the matrix updateColumn ${display}.${table.updateColumn} does not exist.` };
18919
+ if (table2.updateColumn) {
18920
+ const column = byName.get(table2.updateColumn);
18921
+ if (!column) return { reason: `the matrix updateColumn ${display}.${table2.updateColumn} does not exist.` };
18546
18922
  if (valueCandidates(column, "u").length === 0) return { reason: `the test cannot invent a new value for ${display}.${column.name} (${column.type}); pick another updateColumn.` };
18547
18923
  }
18548
18924
  const writable = (c) => c.identity === "" && c.generated === "";
18549
- const required = columns.filter((c) => c.name !== owner.name && c.notNull && !c.hasDefault && writable(c) && !(c.name in table.seed));
18925
+ const required = columns.filter((c) => c.name !== owner.name && c.notNull && !c.hasDefault && writable(c) && !(c.name in table2.seed));
18550
18926
  for (const column of required) {
18551
18927
  if (column.isFk) return { reason: `${display}.${column.name} is a NOT NULL foreign key the test cannot invent; give it a value under "seed" in the matrix.` };
18552
18928
  if (valueCandidates(column, "a").length === 0) {
18553
18929
  return { reason: `${display}.${column.name} (${column.type}) is NOT NULL and the test cannot invent a value; give it one under "seed" in the matrix.` };
18554
18930
  }
18555
18931
  }
18556
- const seedColumns = Object.keys(table.seed);
18932
+ const seedColumns = Object.keys(table2.seed);
18557
18933
  return {
18558
18934
  shape: {
18559
- table,
18935
+ table: table2,
18560
18936
  oid,
18561
- qualified: `${quoteIdent2(table.schema)}.${quoteIdent2(table.name)}`,
18937
+ qualified: `${quoteIdent2(table2.schema)}.${quoteIdent2(table2.name)}`,
18562
18938
  display,
18563
18939
  columns,
18564
18940
  pk,
@@ -18566,7 +18942,7 @@ async function describeTable(db, table) {
18566
18942
  ownerUnique: unique3.has(owner.name),
18567
18943
  ownerInPk: pk.some((c) => c.name === owner.name),
18568
18944
  insertColumns: [owner.name, ...seedColumns, ...required.map((c) => c.name)],
18569
- rowValues: (label, ownerId) => [ownerId, ...seedColumns.map((c) => table.seed[c]), ...required.map((c) => valueCandidates(c, label)[0])]
18945
+ rowValues: (label, ownerId) => [ownerId, ...seedColumns.map((c) => table2.seed[c]), ...required.map((c) => valueCandidates(c, label)[0])]
18570
18946
  }
18571
18947
  };
18572
18948
  }
@@ -18662,10 +19038,10 @@ function writeControl(filtered, changed) {
18662
19038
  if (!changed(run)) {
18663
19039
  return {
18664
19040
  ok: false,
18665
- detail: filtered ? `as the superuser the same statement changed ${plural2(run.affected ?? 0, "row")}, expected 1` : "as the superuser the same statement did not change the owner row"
19041
+ detail: filtered ? `as the superuser the same statement changed ${plural3(run.affected ?? 0, "row")}, expected 1` : "as the superuser the same statement did not change the owner row"
18666
19042
  };
18667
19043
  }
18668
- return { ok: true, detail: `as the superuser the same statement changed ${plural2(filtered ? run.affected ?? 0 : 1, "row")}` };
19044
+ return { ok: true, detail: `as the superuser the same statement changed ${plural3(filtered ? run.affected ?? 0 : 1, "row")}` };
18669
19045
  };
18670
19046
  }
18671
19047
  async function differingValue(db, shape, target, column) {
@@ -18692,7 +19068,7 @@ async function selectPlans(c) {
18692
19068
  return [
18693
19069
  {
18694
19070
  stmt: { name: "select", sql: `select ${pkList} from ${shape.qualified} where ${pkCondition(shape, 1)}`, params: target, display: `select the owner's row from ${shape.display} by primary key`, filtered: true, select: "pk" },
18695
- controlOk: (run) => run.error ? { ok: false, detail: `as the superuser the same statement failed: ${run.error.message} (${run.error.code})` } : run.rows?.length === 1 ? { ok: true, detail: "as the superuser the same statement returned 1 row" } : { ok: false, detail: `as the superuser the same statement returned ${plural2(run.rows?.length ?? 0, "row")}, expected 1` },
19071
+ controlOk: (run) => run.error ? { ok: false, detail: `as the superuser the same statement failed: ${run.error.message} (${run.error.code})` } : run.rows?.length === 1 ? { ok: true, detail: "as the superuser the same statement returned 1 row" } : { ok: false, detail: `as the superuser the same statement returned ${plural3(run.rows?.length ?? 0, "row")}, expected 1` },
18696
19072
  judge: (run) => {
18697
19073
  const denied = privilegeDenied(run);
18698
19074
  if (denied) return denied;
@@ -18747,7 +19123,7 @@ function insertPlans(c) {
18747
19123
  prep,
18748
19124
  controlOk: (run) => {
18749
19125
  if (run.error) return { ok: false, detail: `as the superuser the same statement failed: ${run.error.message} (${run.error.code})` };
18750
- return added(run) ? { ok: true, detail: "as the superuser the same statement changed 1 row" } : { ok: false, detail: `as the superuser the same statement changed ${plural2(run.affected ?? 0, "row")}, expected 1` };
19126
+ return added(run) ? { ok: true, detail: "as the superuser the same statement changed 1 row" } : { ok: false, detail: `as the superuser the same statement changed ${plural3(run.affected ?? 0, "row")}, expected 1` };
18751
19127
  },
18752
19128
  judge: (run) => {
18753
19129
  if (isRlsRefusal(run)) return { verdict: "unknown", detail: `refused by WITH CHECK: ${run.error.message} (42501). Only the generated row was tried; other values may allow insertion` };
@@ -18759,7 +19135,7 @@ function insertPlans(c) {
18759
19135
  if ((run.affected ?? 0) === 0 && run.after.digest === run.before.digest) return { verdict: "denied", detail: "0 rows added, table unchanged" };
18760
19136
  return {
18761
19137
  verdict: "unknown",
18762
- detail: `reported ${plural2(run.affected ?? 0, "row")}, but the table went from ${run.before.count} to ${run.after.count} rows and the owner's rows from ${run.before.owned} to ${run.after.owned}`
19138
+ detail: `reported ${plural3(run.affected ?? 0, "row")}, but the table went from ${run.before.count} to ${run.after.count} rows and the owner's rows from ${run.before.owned} to ${run.after.owned}`
18763
19139
  };
18764
19140
  }
18765
19141
  }
@@ -18807,7 +19183,7 @@ async function updatePlans(c) {
18807
19183
  if (changed(run)) return { verdict: "allowed", detail: noop ? "1 row updated (rewrote its own value)" : `${changedRows(filtered ? run.affected : 1)} (${sets.map((s) => s.column).join(" and ")} rewritten)` };
18808
19184
  if (!filtered) return { verdict: "denied", detail: "the owner row is unchanged" };
18809
19185
  if ((run.affected ?? 0) === 0 && run.after.digest === run.before.digest) return { verdict: "denied", detail: "0 rows changed, the row is still there and unchanged" };
18810
- return { verdict: "unknown", detail: `reported ${plural2(run.affected ?? 0, "row")}, but the owner row ${run.after.target === null ? "is gone" : "did not change"} and the table ${run.after.digest === run.before.digest ? "did not" : "did"} change` };
19186
+ return { verdict: "unknown", detail: `reported ${plural3(run.affected ?? 0, "row")}, but the owner row ${run.after.target === null ? "is gone" : "did not change"} and the table ${run.after.digest === run.before.digest ? "did not" : "did"} change` };
18811
19187
  }
18812
19188
  });
18813
19189
  };
@@ -18859,7 +19235,7 @@ function reassignPlans(c) {
18859
19235
  if (moved(run)) return { verdict: "allowed", detail: `the owner row now belongs to the other user` };
18860
19236
  if (!filtered && run.after.target === run.before.target) return { verdict: "denied", detail: "the owner row is unchanged" };
18861
19237
  if ((run.affected ?? 0) === 0 && run.after.digest === run.before.digest) return { verdict: "denied", detail: "0 rows changed, the row is still there and unchanged" };
18862
- return { verdict: "unknown", detail: `reported ${plural2(run.affected ?? 0, "row")}, but the owner column did not move` };
19238
+ return { verdict: "unknown", detail: `reported ${plural3(run.affected ?? 0, "row")}, but the owner column did not move` };
18863
19239
  }
18864
19240
  }
18865
19241
  ];
@@ -18888,7 +19264,7 @@ function deletePlans(c) {
18888
19264
  if ((run.affected ?? 0) === 0 && run.after.digest === run.before.digest) return { verdict: "denied", detail: "0 rows changed, the row is still there and unchanged" };
18889
19265
  return {
18890
19266
  verdict: "unknown",
18891
- detail: `reported ${plural2(run.affected ?? 0, "row")}, but the owner row is ${run.after.target === null ? "gone" : "still there"} and the table ${run.after.digest === run.before.digest ? "did not" : "did"} change`
19267
+ detail: `reported ${plural3(run.affected ?? 0, "row")}, but the owner row is ${run.after.target === null ? "gone" : "still there"} and the table ${run.after.digest === run.before.digest ? "did not" : "did"} change`
18892
19268
  };
18893
19269
  }
18894
19270
  }
@@ -18959,25 +19335,25 @@ async function runCell(ctx, privilegesByRole, actor, action, expected) {
18959
19335
  const reason = pass ? `Expected ${expected}, observed ${allowed ? "allowed" : "denied"}.` : want ? `Expected ${actorText} to be allowed to ${action}, but ${observed.summary}.${action !== "select" && !r.error ? " Postgres reports rows no policy allows as 0 rows, not as an error, so the app sees no failure." : ""}` : `Expected ${actorText} to be denied ${action}, but ${observed.summary}.`;
18960
19336
  return { ...base, outcome: pass ? "Pass" : "Fail", observed, reason };
18961
19337
  }
18962
- function cellsFor(table) {
19338
+ function cellsFor(table2) {
18963
19339
  const out = [];
18964
19340
  for (const actor of TESTED_ACTORS) {
18965
- for (const action of MATRIX_ACTIONS) out.push({ actor, action, expected: table.expect[actor][action] });
18966
- const reassign = table.expect[actor].reassign;
19341
+ for (const action of MATRIX_ACTIONS) out.push({ actor, action, expected: table2.expect[actor][action] });
19342
+ const reassign = table2.expect[actor].reassign;
18967
19343
  if (reassign) out.push({ actor, action: "reassign", expected: reassign });
18968
19344
  }
18969
19345
  return out;
18970
19346
  }
18971
- function unknownCells(table, reason, summary) {
18972
- const display = `${table.schema}.${table.name}`;
18973
- const cells = cellsFor(table).map(
19347
+ function unknownCells(table2, reason, summary) {
19348
+ const display = `${table2.schema}.${table2.name}`;
19349
+ const cells = cellsFor(table2).map(
18974
19350
  ({ actor, action, expected }) => expected === "skip" ? { table: display, actor, action, expected, outcome: "N/A", observed: { summary: "not tested (skip in the matrix)" }, reason: "The matrix marks this cell skip." } : { table: display, actor, action, expected, outcome: "Unknown", observed: { summary }, reason }
18975
19351
  );
18976
- return [...cells, ...serviceRoleCells(table)];
19352
+ return [...cells, ...serviceRoleCells(table2)];
18977
19353
  }
18978
- function serviceRoleCells(table) {
18979
- if (!table.serviceRoleExcluded) return [];
18980
- const display = `${table.schema}.${table.name}`;
19354
+ function serviceRoleCells(table2) {
19355
+ if (!table2.serviceRoleExcluded) return [];
19356
+ const display = `${table2.schema}.${table2.name}`;
18981
19357
  return MATRIX_ACTIONS.map((action) => ({
18982
19358
  table: display,
18983
19359
  actor: "service_role",
@@ -18997,40 +19373,40 @@ function summarise(tables) {
18997
19373
  function verdictFor(summary) {
18998
19374
  if (summary.fail > 0) {
18999
19375
  return {
19000
- verdict: `Not ready within tested scope: ${plural2(summary.fail, "cell does", "cells do")} not match the matrix; ${plural2(summary.unknown, "unknown")}.`,
19376
+ verdict: `Not ready within tested scope: ${plural3(summary.fail, "cell does", "cells do")} not match the matrix; ${plural3(summary.unknown, "unknown")}.`,
19001
19377
  exitCode: 1
19002
19378
  };
19003
19379
  }
19004
19380
  if (summary.pass === 0) {
19005
- return { verdict: `Nothing was observed: ${plural2(summary.unknown, "unknown")}, ${summary.notApplicable} N/A.`, exitCode: 2 };
19381
+ return { verdict: `Nothing was observed: ${plural3(summary.unknown, "unknown")}, ${summary.notApplicable} N/A.`, exitCode: 2 };
19006
19382
  }
19007
19383
  if (summary.unknown > 0) {
19008
- return { verdict: `No blockers found within tested scope; ${plural2(summary.unknown, "unknown")}.`, exitCode: 2 };
19384
+ return { verdict: `No blockers found within tested scope; ${plural3(summary.unknown, "unknown")}.`, exitCode: 2 };
19009
19385
  }
19010
19386
  return { verdict: "No blockers found within tested scope; 0 unknowns. Scenario passed locally.", exitCode: 0 };
19011
19387
  }
19012
19388
  async function listMigrations(dir) {
19013
19389
  try {
19014
- const entries = await (0, import_promises27.readdir)(dir, { withFileTypes: true });
19390
+ const entries = await (0, import_promises29.readdir)(dir, { withFileTypes: true });
19015
19391
  return entries.filter((e2) => e2.isFile() && /\.sql$/i.test(e2.name) && !e2.name.startsWith(".")).map((e2) => e2.name).sort((a, b3) => a < b3 ? -1 : a > b3 ? 1 : 0);
19016
19392
  } catch {
19017
19393
  return [];
19018
19394
  }
19019
19395
  }
19020
19396
  async function runRlsTest(options) {
19021
- const projectDir = (0, import_node_path34.resolve)(options.projectDir);
19022
- const matrixPath = (0, import_node_path34.resolve)(projectDir, options.matrixPath ?? DEFAULT_MATRIX_FILE);
19023
- const migrationsDir = (0, import_node_path34.resolve)(projectDir, options.migrationsDir ?? (0, import_node_path34.join)("supabase", "migrations"));
19397
+ const projectDir = (0, import_node_path36.resolve)(options.projectDir);
19398
+ const matrixPath = (0, import_node_path36.resolve)(projectDir, options.matrixPath ?? DEFAULT_MATRIX_FILE);
19399
+ const migrationsDir = (0, import_node_path36.resolve)(projectDir, options.migrationsDir ?? (0, import_node_path36.join)("supabase", "migrations"));
19024
19400
  const timeouts = { ...DEFAULT_TIMEOUTS, ...options.timeouts };
19025
- if ((0, import_node_path34.basename)(matrixPath).toLowerCase().startsWith(".env")) {
19026
- throw new RlsInputError(`Refusing to read ${(0, import_node_path34.basename)(matrixPath)}: rls-test never reads .env files. Point --matrix at a viberaven.permissions.json.`);
19401
+ if ((0, import_node_path36.basename)(matrixPath).toLowerCase().startsWith(".env")) {
19402
+ throw new RlsInputError(`Refusing to read ${(0, import_node_path36.basename)(matrixPath)}: rls-test never reads .env files. Point --matrix at a viberaven.permissions.json.`);
19027
19403
  }
19028
19404
  const filesRead = [];
19029
19405
  let matrixText;
19030
19406
  try {
19031
- const info = await (0, import_promises27.stat)(matrixPath);
19407
+ const info = await (0, import_promises29.stat)(matrixPath);
19032
19408
  if (!info.isFile()) throw new Error("not a file");
19033
- matrixText = normalise(await (0, import_promises27.readFile)(matrixPath, "utf8"));
19409
+ matrixText = normalise(await (0, import_promises29.readFile)(matrixPath, "utf8"));
19034
19410
  } catch {
19035
19411
  throw new RlsInputError(
19036
19412
  `No permissions matrix at ${matrixPath}. Write one by hand (schema: schemas/permissions-matrix.schema.json in the @viberaven/cli package); rls-test never infers it from your policies.`
@@ -19041,8 +19417,8 @@ async function runRlsTest(options) {
19041
19417
  const names = await listMigrations(migrationsDir);
19042
19418
  const migrationFiles = [];
19043
19419
  for (const name of names) {
19044
- const sql = normalise(await (0, import_promises27.readFile)((0, import_node_path34.join)(migrationsDir, name), "utf8"));
19045
- filesRead.push(displayPath(projectDir, (0, import_node_path34.join)(migrationsDir, name)));
19420
+ const sql = normalise(await (0, import_promises29.readFile)((0, import_node_path36.join)(migrationsDir, name), "utf8"));
19421
+ filesRead.push(displayPath(projectDir, (0, import_node_path36.join)(migrationsDir, name)));
19046
19422
  migrationFiles.push({ name, sha256: sha256(sql), sql });
19047
19423
  }
19048
19424
  const migrationsHash = sha256(migrationFiles.map((f) => `${f.name}\0${f.sha256}`).join("\n"));
@@ -19094,7 +19470,7 @@ async function runRlsTest(options) {
19094
19470
  engineLoaded = true;
19095
19471
  db = await engine.create({ env: allowlistedEnv(process.env), statementTimeoutMs: timeouts.statementMs });
19096
19472
  } catch (error) {
19097
- result.engine.error = firstLine(error instanceof Error ? error.message : String(error));
19473
+ result.engine.error = firstLine2(error instanceof Error ? error.message : String(error));
19098
19474
  if (engineLoaded) return allUnknown(`PGlite could not start: ${result.engine.error}. Nothing was tested.`, "not run: PGlite startup failed");
19099
19475
  const missing = error?.code === "MODULE_NOT_FOUND" || error?.code === "ERR_MODULE_NOT_FOUND";
19100
19476
  return missing ? allUnknown(PGLITE_INSTALL_HINT, "not run: PGlite is not installed") : allUnknown(`PGlite could not load: ${result.engine.error}. Nothing was tested.`, "not run: PGlite loading failed");
@@ -19154,13 +19530,13 @@ async function runRlsTest(options) {
19154
19530
  result.roleChecks.push({ role, ok, detail: row ? `superuser ${Boolean(row.su)}, BYPASSRLS ${Boolean(row.bypass)}` : "the role does not exist after the migrations" });
19155
19531
  }
19156
19532
  const badRoles = result.roleChecks.filter((c) => !c.ok);
19157
- for (const table of matrix.tables) {
19158
- result.tables.push(await runTable(db, table, badRoles, result.roleChecks));
19533
+ for (const table2 of matrix.tables) {
19534
+ result.tables.push(await runTable(db, table2, badRoles, result.roleChecks));
19159
19535
  }
19160
19536
  return finish();
19161
19537
  } catch (error) {
19162
19538
  if (error instanceof RlsTimeoutError || !isSqlError(error)) {
19163
- const message = firstLine(error instanceof Error ? error.message : String(error));
19539
+ const message = firstLine2(error instanceof Error ? error.message : String(error));
19164
19540
  return allUnknown(
19165
19541
  error instanceof RlsTimeoutError ? `A statement did not finish within ${Math.round(timeouts.statementMs / 1e3)} s, so the PGlite worker was stopped and no cell is reported as passed.` : `The PGlite worker stopped (${message}), so no cell is reported as passed.`,
19166
19542
  "not run: the database stopped"
@@ -19186,13 +19562,13 @@ async function seedRow(db, shape, label, userId) {
19186
19562
  if (r.rows.length !== 1) throw Object.assign(new Error("the insert returned no row (a trigger or rule may drop it)"), { code: "vr000" });
19187
19563
  return { key: keyOf2(r.rows[0]), reused: false };
19188
19564
  }
19189
- async function runTable(db, table, badRoles, roleChecks) {
19190
- const display = `${table.schema}.${table.name}`;
19191
- const unknownTable = (reason, extra = {}) => ({ name: display, ...extra, unknownReason: reason, cells: unknownCells(table, reason, "not run") });
19565
+ async function runTable(db, table2, badRoles, roleChecks) {
19566
+ const display = `${table2.schema}.${table2.name}`;
19567
+ const unknownTable = (reason, extra = {}) => ({ name: display, ...extra, unknownReason: reason, cells: unknownCells(table2, reason, "not run") });
19192
19568
  if (badRoles.length > 0) {
19193
19569
  return unknownTable(`Role check failed (${badRoles.map((c) => `${c.role}: ${c.detail}`).join("; ")}), so RLS would not apply to the probes the way it does for the Data API.`);
19194
19570
  }
19195
- const described = await describeTable(db, table);
19571
+ const described = await describeTable(db, table2);
19196
19572
  if (!described.shape) return unknownTable(described.reason ?? "unknown table problem");
19197
19573
  const shape = described.shape;
19198
19574
  const rel = await db.query(
@@ -19226,15 +19602,15 @@ async function runTable(db, table, badRoles, roleChecks) {
19226
19602
  const privilegesByRole = /* @__PURE__ */ new Map();
19227
19603
  for (const role of ["anon", "authenticated"]) privilegesByRole.set(role, await privilegesFor(db, shape, role));
19228
19604
  const cells = [];
19229
- for (const { actor, action, expected } of cellsFor(table)) {
19605
+ for (const { actor, action, expected } of cellsFor(table2)) {
19230
19606
  cells.push(await runCell({ db, shape, target: ownerSeed.key }, privilegesByRole, actor, action, expected));
19231
19607
  }
19232
- cells.push(...serviceRoleCells(table));
19608
+ cells.push(...serviceRoleCells(table2));
19233
19609
  const columns = {};
19234
19610
  shape.insertColumns.forEach((c) => {
19235
19611
  columns[c] = c === shape.owner.name ? "the test user id" : "generated test value";
19236
19612
  });
19237
- for (const c of Object.keys(table.seed)) columns[c] = "from the matrix seed";
19613
+ for (const c of Object.keys(table2.seed)) columns[c] = "from the matrix seed";
19238
19614
  return {
19239
19615
  name: display,
19240
19616
  rls,
@@ -19242,16 +19618,16 @@ async function runTable(db, table, badRoles, roleChecks) {
19242
19618
  cells
19243
19619
  };
19244
19620
  }
19245
- var import_node_child_process6, import_node_crypto3, import_promises27, import_node_net, import_node_path34, import_node_util4, NON_AUTHORITATIVE_LABEL, PGLITE_INSTALL_HINT, ENV_ALLOWLIST, PERMISSION_DENIED, RLS_REFUSAL, DEFAULT_TIMEOUTS, RlsInputError, normalise, quoteIdent2, firstLine, plural2, NUMBERS, ACTOR_SESSION, changedRows, isRlsRefusal, isPrivilegeRefusal, SELF, PROBE_EXCLUSIONS, SEED_HINTS;
19621
+ var import_node_child_process8, import_node_crypto3, import_promises29, import_node_net, import_node_path36, import_node_util6, NON_AUTHORITATIVE_LABEL, PGLITE_INSTALL_HINT, ENV_ALLOWLIST, PERMISSION_DENIED, RLS_REFUSAL, DEFAULT_TIMEOUTS, RlsInputError, normalise, quoteIdent2, firstLine2, plural3, NUMBERS, ACTOR_SESSION, changedRows, isRlsRefusal, isPrivilegeRefusal, SELF, PROBE_EXCLUSIONS, SEED_HINTS;
19246
19622
  var init_rlsRunner = __esm({
19247
19623
  "src/rls/rlsRunner.ts"() {
19248
19624
  "use strict";
19249
- import_node_child_process6 = require("node:child_process");
19625
+ import_node_child_process8 = require("node:child_process");
19250
19626
  import_node_crypto3 = require("node:crypto");
19251
- import_promises27 = require("node:fs/promises");
19627
+ import_promises29 = require("node:fs/promises");
19252
19628
  import_node_net = __toESM(require("node:net"));
19253
- import_node_path34 = require("node:path");
19254
- import_node_util4 = require("node:util");
19629
+ import_node_path36 = require("node:path");
19630
+ import_node_util6 = require("node:util");
19255
19631
  init_safeGit();
19256
19632
  init_permissionsMatrix();
19257
19633
  init_supabaseImitation();
@@ -19267,15 +19643,15 @@ var init_rlsRunner = __esm({
19267
19643
  };
19268
19644
  normalise = (text) => text.replace(/\r\n/g, "\n");
19269
19645
  quoteIdent2 = (name) => `"${name.replace(/"/g, '""')}"`;
19270
- firstLine = (message) => message.split("\n")[0].trim();
19271
- plural2 = (n, one, many = `${one}s`) => `${n} ${n === 1 ? one : many}`;
19646
+ firstLine2 = (message) => message.split("\n")[0].trim();
19647
+ plural3 = (n, one, many = `${one}s`) => `${n} ${n === 1 ? one : many}`;
19272
19648
  NUMBERS = { a: ["1001"], b: ["1002"], i: ["1003"], u: ["1004", "7", "1"] };
19273
19649
  ACTOR_SESSION = {
19274
19650
  owner: { role: "authenticated", uid: OWNER_ID, claims: { sub: OWNER_ID, role: "authenticated", aud: "authenticated", email: "owner@example.test" } },
19275
19651
  other_user: { role: "authenticated", uid: OTHER_USER_ID, claims: { sub: OTHER_USER_ID, role: "authenticated", aud: "authenticated", email: "other-user@example.test" } },
19276
19652
  anon: { role: "anon", uid: null, claims: { role: "anon" } }
19277
19653
  };
19278
- changedRows = (n) => `${plural2(n ?? 0, "row")} changed`;
19654
+ changedRows = (n) => `${plural3(n ?? 0, "row")} changed`;
19279
19655
  isRlsRefusal = (run) => run.error?.code === PERMISSION_DENIED && RLS_REFUSAL.test(run.error.message);
19280
19656
  isPrivilegeRefusal = (run) => run.error?.code === PERMISSION_DENIED && !RLS_REFUSAL.test(run.error.message);
19281
19657
  SELF = Symbol("self");
@@ -19322,25 +19698,25 @@ function renderRlsText(result) {
19322
19698
  lines.push("");
19323
19699
  if (result.engine.error) lines.push(`(${result.engine.error})`);
19324
19700
  }
19325
- for (const table of result.tables) {
19701
+ for (const table2 of result.tables) {
19326
19702
  lines.push("");
19327
- const rls = table.rls ? `RLS ${table.rls.enabled ? "on" : "off"}${table.rls.forced ? ", forced" : ""}, owner ${table.rls.owner}` : "not inspected";
19328
- lines.push(`${table.name} (${rls})`);
19329
- if (table.seed) {
19330
- const reused = [table.seed.reused.owner ? "the owner" : "", table.seed.reused.otherUser ? "the other user" : ""].filter(Boolean);
19703
+ const rls = table2.rls ? `RLS ${table2.rls.enabled ? "on" : "off"}${table2.rls.forced ? ", forced" : ""}, owner ${table2.rls.owner}` : "not inspected";
19704
+ lines.push(`${table2.name} (${rls})`);
19705
+ if (table2.seed) {
19706
+ const reused = [table2.seed.reused.owner ? "the owner" : "", table2.seed.reused.otherUser ? "the other user" : ""].filter(Boolean);
19331
19707
  lines.push(
19332
- ` One row for the owner and one for another user (${table.seed.rowsBefore} rows in the table)${reused.length > 0 ? `; reused the rows the migrations created for ${reused.join(" and ")}` : ""}.`
19708
+ ` One row for the owner and one for another user (${table2.seed.rowsBefore} rows in the table)${reused.length > 0 ? `; reused the rows the migrations created for ${reused.join(" and ")}` : ""}.`
19333
19709
  );
19334
19710
  }
19335
- if (table.unknownReason) lines.push(` Unknown: ${table.unknownReason}`);
19336
- const tested = table.cells.filter((c) => c.actor !== "service_role");
19711
+ if (table2.unknownReason) lines.push(` Unknown: ${table2.unknownReason}`);
19712
+ const tested = table2.cells.filter((c) => c.actor !== "service_role");
19337
19713
  const actorWidth = 14;
19338
19714
  const actionWidth = 10;
19339
19715
  lines.push(` ${"actor".padEnd(actorWidth)}${"action".padEnd(actionWidth)}${"expected".padEnd(10)}${"result".padEnd(9)}observed`);
19340
- for (const cell of tested) {
19341
- lines.push(` ${cell.actor.padEnd(actorWidth)}${cell.action.padEnd(actionWidth)}${cell.expected.padEnd(10)}${cell.outcome.padEnd(9)}${cell.observed.summary}`);
19716
+ for (const cell2 of tested) {
19717
+ lines.push(` ${cell2.actor.padEnd(actorWidth)}${cell2.action.padEnd(actionWidth)}${cell2.expected.padEnd(10)}${cell2.outcome.padEnd(9)}${cell2.observed.summary}`);
19342
19718
  }
19343
- const service = table.cells.filter((c) => c.actor === "service_role");
19719
+ const service = table2.cells.filter((c) => c.actor === "service_role");
19344
19720
  if (service.length > 0) lines.push(` ${"service_role".padEnd(actorWidth)}${"all".padEnd(actionWidth)}${"excluded".padEnd(10)}${"N/A".padEnd(9)}bypasses RLS by design, not tested`);
19345
19721
  }
19346
19722
  const s = result.summary;
@@ -19348,8 +19724,8 @@ function renderRlsText(result) {
19348
19724
  lines.push(`Result: ${s.pass} Pass, ${s.fail} Fail, ${s.unknown} Unknown, ${s.notApplicable} N/A \xB7 ${s.pass + s.fail} of ${s.tested} tested cells observed (${s.total} cells in the matrix)`);
19349
19725
  lines.push(result.verdict);
19350
19726
  const failed = result.tables.flatMap((t) => t.cells).filter((c) => c.outcome === "Fail");
19351
- for (const cell of failed) {
19352
- lines.push(` Fail ${cell.actor} ${cell.action} on ${cell.table}: ${cell.reason}`);
19727
+ for (const cell2 of failed) {
19728
+ lines.push(` Fail ${cell2.actor} ${cell2.action} on ${cell2.table}: ${cell2.reason}`);
19353
19729
  }
19354
19730
  const unknown = result.tables.flatMap((t) => t.cells).filter((c) => c.outcome === "Unknown");
19355
19731
  const unknownReasons = [...new Set(unknown.map((c) => c.reason))];
@@ -20078,7 +20454,7 @@ function buildMissionCards(artifact, providers) {
20078
20454
  };
20079
20455
  }
20080
20456
  function projectNameFromPath(pathValue) {
20081
- return (0, import_node_path35.basename)(pathValue.replace(/[\\/]$/, "")) || "current repo";
20457
+ return (0, import_node_path37.basename)(pathValue.replace(/[\\/]$/, "")) || "current repo";
20082
20458
  }
20083
20459
  function gapSearchText(gap) {
20084
20460
  return [
@@ -20489,19 +20865,19 @@ function mapArtifactToLocalUiState(artifact, presentProviderIds) {
20489
20865
  selectedProviderId: selectedProvider.id,
20490
20866
  currentPrompt: selectedProvider.nextFix?.prompt ?? "",
20491
20867
  artifacts: {
20492
- tasklist: (0, import_node_path35.join)(".viberaven", "agent-tasklist.md"),
20493
- contextMap: (0, import_node_path35.join)(".viberaven", "context-map.json"),
20494
- gateResult: (0, import_node_path35.join)(".viberaven", "gate-result.json"),
20495
- lastScan: (0, import_node_path35.join)(".viberaven", "last-scan.json")
20868
+ tasklist: (0, import_node_path37.join)(".viberaven", "agent-tasklist.md"),
20869
+ contextMap: (0, import_node_path37.join)(".viberaven", "context-map.json"),
20870
+ gateResult: (0, import_node_path37.join)(".viberaven", "gate-result.json"),
20871
+ lastScan: (0, import_node_path37.join)(".viberaven", "last-scan.json")
20496
20872
  },
20497
20873
  empty: false
20498
20874
  };
20499
20875
  }
20500
- var import_node_path35, CONNECT_PROVIDER_PRIORITY, providerDashboardUrls, providerHints, PROVIDER_OWNED_GAP;
20876
+ var import_node_path37, CONNECT_PROVIDER_PRIORITY, providerDashboardUrls, providerHints, PROVIDER_OWNED_GAP;
20501
20877
  var init_artifactAdapter = __esm({
20502
20878
  "src/local-ui/artifactAdapter.ts"() {
20503
20879
  "use strict";
20504
- import_node_path35 = require("node:path");
20880
+ import_node_path37 = require("node:path");
20505
20881
  init_providerLogos();
20506
20882
  init_buildTaskList();
20507
20883
  init_prompts();
@@ -20804,18 +21180,18 @@ var init_planLimits = __esm({
20804
21180
  // src/hosted/credentials.ts
20805
21181
  function defaultConfigDir(env = process.env, platform = process.platform) {
20806
21182
  const override = env.VIBERAVEN_CONFIG_DIR?.trim();
20807
- if (override) return (0, import_node_path36.resolve)(override);
21183
+ if (override) return (0, import_node_path38.resolve)(override);
20808
21184
  if (platform === "win32") {
20809
- return (0, import_node_path36.join)(env.APPDATA?.trim() || (0, import_node_path36.join)((0, import_node_os.homedir)(), "AppData", "Roaming"), "viberaven");
21185
+ return (0, import_node_path38.join)(env.APPDATA?.trim() || (0, import_node_path38.join)((0, import_node_os2.homedir)(), "AppData", "Roaming"), "viberaven");
20810
21186
  }
20811
21187
  if (platform === "darwin") {
20812
- return (0, import_node_path36.join)((0, import_node_os.homedir)(), "Library", "Application Support", "viberaven");
21188
+ return (0, import_node_path38.join)((0, import_node_os2.homedir)(), "Library", "Application Support", "viberaven");
20813
21189
  }
20814
- return (0, import_node_path36.join)(env.XDG_CONFIG_HOME?.trim() || (0, import_node_path36.join)((0, import_node_os.homedir)(), ".config"), "viberaven");
21190
+ return (0, import_node_path38.join)(env.XDG_CONFIG_HOME?.trim() || (0, import_node_path38.join)((0, import_node_os2.homedir)(), ".config"), "viberaven");
20815
21191
  }
20816
21192
  function isInside(child, parent) {
20817
- const rel = (0, import_node_path36.relative)((0, import_node_path36.resolve)(parent), (0, import_node_path36.resolve)(child));
20818
- return rel === "" || !rel.startsWith("..") && !(0, import_node_path36.isAbsolute)(rel);
21193
+ const rel = (0, import_node_path38.relative)((0, import_node_path38.resolve)(parent), (0, import_node_path38.resolve)(child));
21194
+ return rel === "" || !rel.startsWith("..") && !(0, import_node_path38.isAbsolute)(rel);
20819
21195
  }
20820
21196
  async function retryTransient(work) {
20821
21197
  for (let attempt = 1; ; attempt += 1) {
@@ -20828,15 +21204,15 @@ async function retryTransient(work) {
20828
21204
  }
20829
21205
  }
20830
21206
  }
20831
- var import_node_child_process7, import_node_crypto4, import_promises28, import_node_os, import_node_path36, CREDENTIALS_FILE_NAME, CredentialLocationError, CredentialPermissionError, restrictWithIcacls, TRANSIENT_FS_CODES, FS_ATTEMPTS, FS_BACKOFF_MS, CredentialStore;
21207
+ var import_node_child_process9, import_node_crypto4, import_promises30, import_node_os2, import_node_path38, CREDENTIALS_FILE_NAME, CredentialLocationError, CredentialPermissionError, restrictWithIcacls, TRANSIENT_FS_CODES, FS_ATTEMPTS, FS_BACKOFF_MS, CredentialStore;
20832
21208
  var init_credentials = __esm({
20833
21209
  "src/hosted/credentials.ts"() {
20834
21210
  "use strict";
20835
- import_node_child_process7 = require("node:child_process");
21211
+ import_node_child_process9 = require("node:child_process");
20836
21212
  import_node_crypto4 = require("node:crypto");
20837
- import_promises28 = require("node:fs/promises");
20838
- import_node_os = require("node:os");
20839
- import_node_path36 = require("node:path");
21213
+ import_promises30 = require("node:fs/promises");
21214
+ import_node_os2 = require("node:os");
21215
+ import_node_path38 = require("node:path");
20840
21216
  CREDENTIALS_FILE_NAME = "credentials.json";
20841
21217
  CredentialLocationError = class extends Error {
20842
21218
  };
@@ -20847,10 +21223,10 @@ var init_credentials = __esm({
20847
21223
  }
20848
21224
  };
20849
21225
  restrictWithIcacls = (path) => {
20850
- const name = (0, import_node_os.userInfo)().username;
21226
+ const name = (0, import_node_os2.userInfo)().username;
20851
21227
  const user = process.env.USERDOMAIN ? `${process.env.USERDOMAIN}\\${name}` : name;
20852
21228
  return new Promise((resolveRestrict, reject) => {
20853
- (0, import_node_child_process7.execFile)("icacls", [path, "/inheritance:r", "/grant:r", `${user}:F`], { windowsHide: true, timeout: 1e4 }, (error) => {
21229
+ (0, import_node_child_process9.execFile)("icacls", [path, "/inheritance:r", "/grant:r", `${user}:F`], { windowsHide: true, timeout: 1e4 }, (error) => {
20854
21230
  if (error) reject(error);
20855
21231
  else resolveRestrict();
20856
21232
  });
@@ -20863,9 +21239,9 @@ var init_credentials = __esm({
20863
21239
  constructor(configDir, workspacePath, options = {}) {
20864
21240
  this.configDir = configDir;
20865
21241
  this.workspacePath = workspacePath;
20866
- this.path = (0, import_node_path36.join)(configDir, CREDENTIALS_FILE_NAME);
21242
+ this.path = (0, import_node_path38.join)(configDir, CREDENTIALS_FILE_NAME);
20867
21243
  this.restrict = options.restrict !== void 0 ? options.restrict : process.platform === "win32" ? restrictWithIcacls : null;
20868
- this.fs = { readFile: (path, encoding) => (0, import_promises28.readFile)(path, encoding), rename: (from, to) => (0, import_promises28.rename)(from, to), rm: (path) => (0, import_promises28.rm)(path, { force: true }), ...options.fs };
21244
+ this.fs = { readFile: (path, encoding) => (0, import_promises30.readFile)(path, encoding), rename: (from, to) => (0, import_promises30.rename)(from, to), rm: (path) => (0, import_promises30.rm)(path, { force: true }), ...options.fs };
20869
21245
  }
20870
21246
  path;
20871
21247
  restrict;
@@ -20914,15 +21290,15 @@ var init_credentials = __esm({
20914
21290
  `Refusing to store the VibeRaven sign-in inside this project (${this.configDir}). Set VIBERAVEN_CONFIG_DIR to a folder outside the repo.`
20915
21291
  );
20916
21292
  }
20917
- await (0, import_promises28.mkdir)(this.configDir, { recursive: true, mode: 448 });
21293
+ await (0, import_promises30.mkdir)(this.configDir, { recursive: true, mode: 448 });
20918
21294
  const temp = `${this.path}.${(0, import_node_crypto4.randomUUID)()}.tmp`;
20919
- await (0, import_promises28.writeFile)(temp, `${JSON.stringify(body, null, 2)}
21295
+ await (0, import_promises30.writeFile)(temp, `${JSON.stringify(body, null, 2)}
20920
21296
  `, { encoding: "utf8", mode: 384 });
20921
- await (0, import_promises28.chmod)(temp, 384).catch(() => void 0);
21297
+ await (0, import_promises30.chmod)(temp, 384).catch(() => void 0);
20922
21298
  try {
20923
21299
  if (this.restrict) await this.restrict(temp);
20924
21300
  } catch (error) {
20925
- await (0, import_promises28.rm)(temp, { force: true });
21301
+ await (0, import_promises30.rm)(temp, { force: true });
20926
21302
  const code = error?.code;
20927
21303
  const toolMissing = code === "ENOENT";
20928
21304
  throw new CredentialPermissionError(
@@ -20933,17 +21309,17 @@ var init_credentials = __esm({
20933
21309
  if (guard) {
20934
21310
  const onDisk = await this.readFor(guard.apiBaseUrl);
20935
21311
  if (onDisk?.accessToken !== guard.accessToken || guard.isCurrent && !guard.isCurrent()) {
20936
- await (0, import_promises28.rm)(temp, { force: true });
21312
+ await (0, import_promises30.rm)(temp, { force: true });
20937
21313
  return false;
20938
21314
  }
20939
21315
  }
20940
21316
  try {
20941
21317
  await retryTransient(() => this.fs.rename(temp, this.path));
20942
21318
  } catch (error) {
20943
- await (0, import_promises28.rm)(temp, { force: true });
21319
+ await (0, import_promises30.rm)(temp, { force: true });
20944
21320
  throw error;
20945
21321
  }
20946
- await (0, import_promises28.chmod)(this.path, 384).catch(() => void 0);
21322
+ await (0, import_promises30.chmod)(this.path, 384).catch(() => void 0);
20947
21323
  return true;
20948
21324
  }
20949
21325
  /** Keeps the stored plan label in step with what the API last said. */
@@ -21312,7 +21688,7 @@ async function buildHostedCheckPayload(workspacePath) {
21312
21688
  throw new Error("Could not assemble the full check request.");
21313
21689
  }
21314
21690
  function finalizePayload(payload) {
21315
- const folder = (0, import_node_path37.basename)(payload.workspacePath.split("\\").join("/").replace(/\/+$/, "")) || "project";
21691
+ const folder = (0, import_node_path39.basename)(payload.workspacePath.split("\\").join("/").replace(/\/+$/, "")) || "project";
21316
21692
  const redacted = redactPayload({ ...payload, workspacePath: folder });
21317
21693
  return { payload: redacted.payload, redactions: redacted.count };
21318
21694
  }
@@ -21343,13 +21719,13 @@ function splitSections(prompt) {
21343
21719
  chars: section.length
21344
21720
  }));
21345
21721
  }
21346
- var import_node_path37, PayloadCaptured;
21722
+ var import_node_path39, PayloadCaptured;
21347
21723
  var init_payload = __esm({
21348
21724
  "src/hosted/payload.ts"() {
21349
21725
  "use strict";
21350
21726
  init_fileScanner();
21351
21727
  init_orchestrator();
21352
- import_node_path37 = require("node:path");
21728
+ import_node_path39 = require("node:path");
21353
21729
  init_config();
21354
21730
  init_redact();
21355
21731
  PayloadCaptured = class extends Error {
@@ -21387,7 +21763,7 @@ function shapeResult(response, now = /* @__PURE__ */ new Date()) {
21387
21763
  const usage = toUsage(response.usage);
21388
21764
  const freeKeys = new Set(FREE_TRIAL_UNLOCKED_MAP_CATEGORY_KEYS);
21389
21765
  const unlocked = (key) => usage.plan === "pro" || freeKeys.has(key) && response.usage.unlockedMapCategoryKeys.includes(key);
21390
- const sorted = [...output.gaps].sort((a, b3) => SEVERITY_RANK3[a.severity] - SEVERITY_RANK3[b3.severity]);
21766
+ const sorted = [...output.gaps].sort((a, b3) => SEVERITY_RANK4[a.severity] - SEVERITY_RANK4[b3.severity]);
21391
21767
  const areas = PRODUCTION_MAP_LANES.map((lane) => {
21392
21768
  const gaps = sorted.filter((gap) => gap.primaryMapCategory === lane.extensionKey);
21393
21769
  const locked = !unlocked(lane.extensionKey);
@@ -21425,7 +21801,7 @@ function readServerMessage(body) {
21425
21801
  return body.slice(0, 300);
21426
21802
  }
21427
21803
  }
21428
- var import_node_crypto5, PRICING_URL, FREE_LIMIT_MESSAGE, PREVIEW_TTL_MS, STATUS_TIMEOUT_MS, STATUS_CACHE_MS, MAX_PREVIEWS, TRANSIENT_REASONS, SEVERITY_RANK3, FullCheckService;
21804
+ var import_node_crypto5, PRICING_URL, FREE_LIMIT_MESSAGE, PREVIEW_TTL_MS, STATUS_TIMEOUT_MS, STATUS_CACHE_MS, MAX_PREVIEWS, TRANSIENT_REASONS, SEVERITY_RANK4, FullCheckService;
21429
21805
  var init_fullCheck = __esm({
21430
21806
  "src/hosted/fullCheck.ts"() {
21431
21807
  "use strict";
@@ -21444,7 +21820,7 @@ var init_fullCheck = __esm({
21444
21820
  STATUS_CACHE_MS = 5e3;
21445
21821
  MAX_PREVIEWS = 5;
21446
21822
  TRANSIENT_REASONS = /* @__PURE__ */ new Set(["network", "timeout", "server", "rate_limited", "busy"]);
21447
- SEVERITY_RANK3 = { critical: 0, warning: 1, info: 2 };
21823
+ SEVERITY_RANK4 = { critical: 0, warning: 1, info: 2 };
21448
21824
  FullCheckService = class {
21449
21825
  constructor(workspacePath, options = {}) {
21450
21826
  this.workspacePath = workspacePath;
@@ -21815,10 +22191,10 @@ async function connectProviderMcp(cwd, providerId) {
21815
22191
  if (!recipe) {
21816
22192
  return { ok: false, message: `No known MCP server for ${providerId} yet. Add one manually to .mcp.json.` };
21817
22193
  }
21818
- const configPath = (0, import_node_path38.join)(cwd, ".mcp.json");
22194
+ const configPath = (0, import_node_path40.join)(cwd, ".mcp.json");
21819
22195
  let config = {};
21820
22196
  try {
21821
- const raw = await (0, import_promises29.readFile)(configPath, "utf8");
22197
+ const raw = await (0, import_promises31.readFile)(configPath, "utf8");
21822
22198
  const parsed = JSON.parse(raw.replace(/^\uFEFF/, ""));
21823
22199
  if (!isRecord5(parsed) || parsed.mcpServers !== void 0 && !isRecord5(parsed.mcpServers)) {
21824
22200
  return { ok: false, message: "Existing .mcp.json has an unsupported structure. It was left unchanged." };
@@ -21835,7 +22211,7 @@ async function connectProviderMcp(cwd, providerId) {
21835
22211
  }
21836
22212
  servers[providerId] = recipe.entry;
21837
22213
  const next = { ...config, mcpServers: servers };
21838
- await (0, import_promises29.writeFile)(configPath, `${JSON.stringify(next, null, 2)}
22214
+ await (0, import_promises31.writeFile)(configPath, `${JSON.stringify(next, null, 2)}
21839
22215
  `, "utf8");
21840
22216
  const suffix = recipe.note ? ` ${recipe.note}` : "";
21841
22217
  return { ok: true, message: `Added ${providerId} MCP server to .mcp.json.${suffix}`, path: ".mcp.json" };
@@ -21888,7 +22264,7 @@ async function defaultScanRunner(flags, positional) {
21888
22264
  }
21889
22265
  async function readPackageJson(cwd) {
21890
22266
  try {
21891
- const raw = await (0, import_promises29.readFile)((0, import_node_path38.join)(cwd, "package.json"), "utf8");
22267
+ const raw = await (0, import_promises31.readFile)((0, import_node_path40.join)(cwd, "package.json"), "utf8");
21892
22268
  const parsed = JSON.parse(raw.replace(/^\uFEFF/, ""));
21893
22269
  return isRecord5(parsed) ? parsed : {};
21894
22270
  } catch {
@@ -21911,7 +22287,7 @@ async function listProjectFiles(cwd, maxFiles = 1800) {
21911
22287
  if (files.length >= maxFiles) return;
21912
22288
  let entries;
21913
22289
  try {
21914
- entries = await (0, import_promises29.readdir)(dir, { withFileTypes: true });
22290
+ entries = await (0, import_promises31.readdir)(dir, { withFileTypes: true });
21915
22291
  } catch {
21916
22292
  return;
21917
22293
  }
@@ -21919,7 +22295,7 @@ async function listProjectFiles(cwd, maxFiles = 1800) {
21919
22295
  if (files.length >= maxFiles) return;
21920
22296
  const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
21921
22297
  if (entry.isDirectory()) {
21922
- if (!skipped.has(entry.name)) await walk((0, import_node_path38.join)(dir, entry.name), rel);
22298
+ if (!skipped.has(entry.name)) await walk((0, import_node_path40.join)(dir, entry.name), rel);
21923
22299
  continue;
21924
22300
  }
21925
22301
  if (entry.isFile()) files.push(rel.toLowerCase());
@@ -21938,7 +22314,7 @@ async function readEnvKeys(cwd) {
21938
22314
  const keys = /* @__PURE__ */ new Set();
21939
22315
  for (const name of [".env.example", ".env"]) {
21940
22316
  try {
21941
- const raw = await (0, import_promises29.readFile)((0, import_node_path38.join)(cwd, name), "utf8");
22317
+ const raw = await (0, import_promises31.readFile)((0, import_node_path40.join)(cwd, name), "utf8");
21942
22318
  for (const line of raw.split(/\r?\n/)) {
21943
22319
  const trimmed = line.trim();
21944
22320
  if (!trimmed || trimmed.startsWith("#")) continue;
@@ -22032,7 +22408,7 @@ function applyProviderDetection(state, deps, files) {
22032
22408
  }
22033
22409
  function runGit(cwd, args, timeoutMs = 1500) {
22034
22410
  return safeGitInvocation(cwd, args).then(
22035
- (git) => spawnGit(cwd, git.args, git.env, timeoutMs),
22411
+ (git2) => spawnGit(cwd, git2.args, git2.env, timeoutMs),
22036
22412
  (error) => {
22037
22413
  if (error instanceof SafeGitRefusal) throw error;
22038
22414
  return "";
@@ -22041,7 +22417,7 @@ function runGit(cwd, args, timeoutMs = 1500) {
22041
22417
  }
22042
22418
  function spawnGit(cwd, args, env, timeoutMs) {
22043
22419
  return new Promise((resolveGit) => {
22044
- const child = (0, import_node_child_process8.spawn)("git", args, {
22420
+ const child = (0, import_node_child_process10.spawn)("git", args, {
22045
22421
  cwd,
22046
22422
  env,
22047
22423
  windowsHide: true,
@@ -22104,7 +22480,7 @@ function parseGitShortstat(raw) {
22104
22480
  }
22105
22481
  function runGitCommand(cwd, args, timeoutMs = 5e3) {
22106
22482
  return safeGitInvocation(cwd, args).then(
22107
- (git) => spawnGitCommand(cwd, git.args, git.env, timeoutMs),
22483
+ (git2) => spawnGitCommand(cwd, git2.args, git2.env, timeoutMs),
22108
22484
  (error) => ({
22109
22485
  code: 1,
22110
22486
  stdout: "",
@@ -22114,7 +22490,7 @@ function runGitCommand(cwd, args, timeoutMs = 5e3) {
22114
22490
  }
22115
22491
  function spawnGitCommand(cwd, args, env, timeoutMs) {
22116
22492
  return new Promise((resolveCommand) => {
22117
- const child = (0, import_node_child_process8.spawn)("git", args, {
22493
+ const child = (0, import_node_child_process10.spawn)("git", args, {
22118
22494
  cwd,
22119
22495
  env,
22120
22496
  windowsHide: true,
@@ -22256,7 +22632,7 @@ async function codexModelOptions() {
22256
22632
  { id: "gpt-5.4-mini", label: "GPT-5.4 Mini" }
22257
22633
  ];
22258
22634
  try {
22259
- const raw = await (0, import_promises29.readFile)((0, import_node_path38.join)((0, import_node_os2.homedir)(), ".codex", "models_cache.json"), "utf8");
22635
+ const raw = await (0, import_promises31.readFile)((0, import_node_path40.join)((0, import_node_os3.homedir)(), ".codex", "models_cache.json"), "utf8");
22260
22636
  const parsed = JSON.parse(raw);
22261
22637
  const list = Array.isArray(parsed) ? parsed : parsed.models ?? [];
22262
22638
  const options = list.filter((model) => typeof model?.slug === "string").filter((model) => !/auto-review|reserve/i.test(model.slug)).map((model) => ({ id: model.slug, label: model.display_name?.trim() || model.slug }));
@@ -22280,7 +22656,7 @@ function cliModelDefaults(id) {
22280
22656
  }
22281
22657
  function detectCommand(command, cwd) {
22282
22658
  return new Promise((resolveDetect) => {
22283
- const child = (0, import_node_child_process8.spawn)(process.platform === "win32" ? "where.exe" : "sh", process.platform === "win32" ? [command] : ["-lc", `command -v ${command}`], {
22659
+ const child = (0, import_node_child_process10.spawn)(process.platform === "win32" ? "where.exe" : "sh", process.platform === "win32" ? [command] : ["-lc", `command -v ${command}`], {
22284
22660
  cwd,
22285
22661
  windowsHide: true,
22286
22662
  stdio: ["ignore", "pipe", "pipe"]
@@ -22307,7 +22683,7 @@ function commandMissingFromOutput(text) {
22307
22683
  return /not recognized|not found|unknown command|no such file|command not found|is not recognized as an internal or external command/.test(text.toLowerCase());
22308
22684
  }
22309
22685
  function redactProbeOutput(output) {
22310
- const home = (0, import_node_os2.homedir)();
22686
+ const home = (0, import_node_os3.homedir)();
22311
22687
  const homeVariants = [home, home.split("\\").join("\\\\"), home.split("\\").join("/")].filter((value) => value.length > 3);
22312
22688
  let scrubbed = output;
22313
22689
  for (const variant of homeVariants) scrubbed = scrubbed.split(variant).join("~");
@@ -22367,7 +22743,7 @@ function setupCommandFor(cliId, step) {
22367
22743
  function runShellCommand(command, cwd, timeoutMs = 18e4) {
22368
22744
  return new Promise((resolveRun) => {
22369
22745
  const useCmd = process.platform === "win32";
22370
- const child = (0, import_node_child_process8.spawn)(useCmd ? process.env.ComSpec || "cmd.exe" : "sh", useCmd ? ["/d", "/s", "/c", command] : ["-lc", command], {
22746
+ const child = (0, import_node_child_process10.spawn)(useCmd ? process.env.ComSpec || "cmd.exe" : "sh", useCmd ? ["/d", "/s", "/c", command] : ["-lc", command], {
22371
22747
  cwd,
22372
22748
  windowsHide: true,
22373
22749
  stdio: ["ignore", "pipe", "pipe"]
@@ -22401,7 +22777,7 @@ function openSystemTerminal(command, cwd) {
22401
22777
  const fullCommand = process.platform === "win32" ? `cd /d ${quotedCwd} && ${command}` : `cd ${quotedCwd} && ${command}`;
22402
22778
  let child;
22403
22779
  if (process.platform === "win32") {
22404
- child = (0, import_node_child_process8.spawn)("cmd.exe", ["/c", "start", "cmd.exe", "/k", fullCommand], {
22780
+ child = (0, import_node_child_process10.spawn)("cmd.exe", ["/c", "start", "cmd.exe", "/k", fullCommand], {
22405
22781
  cwd,
22406
22782
  windowsHide: true,
22407
22783
  stdio: "ignore",
@@ -22409,9 +22785,9 @@ function openSystemTerminal(command, cwd) {
22409
22785
  });
22410
22786
  } else if (process.platform === "darwin") {
22411
22787
  const script = `tell application "Terminal" to do script "${fullCommand.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
22412
- child = (0, import_node_child_process8.spawn)("osascript", ["-e", script], { cwd, windowsHide: true, stdio: "ignore", detached: true });
22788
+ child = (0, import_node_child_process10.spawn)("osascript", ["-e", script], { cwd, windowsHide: true, stdio: "ignore", detached: true });
22413
22789
  } else {
22414
- child = (0, import_node_child_process8.spawn)("x-terminal-emulator", ["-e", `bash -lc ${JSON.stringify(fullCommand)}`], {
22790
+ child = (0, import_node_child_process10.spawn)("x-terminal-emulator", ["-e", `bash -lc ${JSON.stringify(fullCommand)}`], {
22415
22791
  cwd,
22416
22792
  windowsHide: true,
22417
22793
  stdio: "ignore",
@@ -22459,19 +22835,19 @@ function isPlaceholderProjectName(value) {
22459
22835
  async function readMcpEvidence(cwd) {
22460
22836
  const candidates = MCP_CONFIG_PATHS.map((relativePath) => ({
22461
22837
  source: relativePath.replace(/\\/g, "/"),
22462
- path: (0, import_node_path38.resolve)(cwd, relativePath)
22838
+ path: (0, import_node_path40.resolve)(cwd, relativePath)
22463
22839
  }));
22464
22840
  const home = process.env.USERPROFILE || process.env.HOME;
22465
22841
  if (home) {
22466
22842
  candidates.push({
22467
22843
  source: "~/.codex/config.toml",
22468
- path: (0, import_node_path38.resolve)(home, ".codex", "config.toml")
22844
+ path: (0, import_node_path40.resolve)(home, ".codex", "config.toml")
22469
22845
  });
22470
22846
  }
22471
22847
  const evidence = [];
22472
22848
  for (const candidate of candidates) {
22473
22849
  try {
22474
- const text = await (0, import_promises29.readFile)(candidate.path, "utf8");
22850
+ const text = await (0, import_promises31.readFile)(candidate.path, "utf8");
22475
22851
  if (/mcp/i.test(text)) evidence.push({ source: candidate.source, text });
22476
22852
  } catch {
22477
22853
  }
@@ -22562,7 +22938,7 @@ async function enrichProjectMetadata(state, cwd) {
22562
22938
  async function readState(cwd) {
22563
22939
  let state;
22564
22940
  try {
22565
- const raw = await (0, import_promises29.readFile)((0, import_node_path38.join)(cwd, ".viberaven", "last-scan.json"), "utf8");
22941
+ const raw = await (0, import_promises31.readFile)((0, import_node_path40.join)(cwd, ".viberaven", "last-scan.json"), "utf8");
22566
22942
  const packageJson = await readPackageJson(cwd);
22567
22943
  const deps = dependencyNames(packageJson);
22568
22944
  const files = await listProjectFiles(cwd);
@@ -22808,7 +23184,7 @@ function quoteWindowsCmdArg(value) {
22808
23184
  }
22809
23185
  async function whereExe(command, cwd) {
22810
23186
  return new Promise((resolvePath) => {
22811
- const child = (0, import_node_child_process8.spawn)("where.exe", [command], {
23187
+ const child = (0, import_node_child_process10.spawn)("where.exe", [command], {
22812
23188
  cwd,
22813
23189
  windowsHide: true,
22814
23190
  stdio: ["ignore", "pipe", "ignore"]
@@ -22829,7 +23205,7 @@ async function whereExe(command, cwd) {
22829
23205
  }
22830
23206
  function expandShimPath(value, base) {
22831
23207
  const relative8 = value.replace(/^%(?:dp0%\\+|~dp0\\*)/i, "");
22832
- return relative8 === value ? value : (0, import_node_path38.join)(base, ...relative8.split(/[\\/]+/));
23208
+ return relative8 === value ? value : (0, import_node_path40.join)(base, ...relative8.split(/[\\/]+/));
22833
23209
  }
22834
23210
  function findShimTarget(lines, pattern, accept = () => true) {
22835
23211
  for (const line of lines) {
@@ -22840,21 +23216,21 @@ function findShimTarget(lines, pattern, accept = () => true) {
22840
23216
  }
22841
23217
  async function resolveWindowsShimTarget(shimPath) {
22842
23218
  try {
22843
- const content = await (0, import_promises29.readFile)(shimPath, "utf8");
22844
- const base = (0, import_node_path38.dirname)(shimPath);
23219
+ const content = await (0, import_promises31.readFile)(shimPath, "utf8");
23220
+ const base = (0, import_node_path40.dirname)(shimPath);
22845
23221
  const lines = content.split(/\r?\n/);
22846
23222
  const trailingArgs = (line, after) => [...line.slice(after).split("%*")[0].matchAll(/"([^"]*)"|([^\s"&|<>]+)/g)].map((token) => expandShimPath(token[1] ?? token[2], base)).filter((value) => value.length > 0 && !value.includes("%"));
22847
23223
  const script = findShimTarget(lines, /"%(?:dp0%\\+|~dp0\\*)([^"]+\.js)"/i);
22848
23224
  if (script) {
22849
23225
  return {
22850
23226
  command: process.execPath,
22851
- args: [(0, import_node_path38.join)(base, ...script.match[1].split(/[\\/]+/)), ...trailingArgs(script.line, script.match.index + script.match[0].length)]
23227
+ args: [(0, import_node_path40.join)(base, ...script.match[1].split(/[\\/]+/)), ...trailingArgs(script.line, script.match.index + script.match[0].length)]
22852
23228
  };
22853
23229
  }
22854
- const binary = findShimTarget(lines, /"%(?:dp0%\\+|~dp0\\*)([^"]+\.exe)"/i, (target) => (0, import_node_path38.basename)(target).toLowerCase() !== "node.exe");
23230
+ const binary = findShimTarget(lines, /"%(?:dp0%\\+|~dp0\\*)([^"]+\.exe)"/i, (target) => (0, import_node_path40.basename)(target).toLowerCase() !== "node.exe");
22855
23231
  if (binary) {
22856
23232
  return {
22857
- command: (0, import_node_path38.join)(base, ...binary.match[1].split(/[\\/]+/)),
23233
+ command: (0, import_node_path40.join)(base, ...binary.match[1].split(/[\\/]+/)),
22858
23234
  args: trailingArgs(binary.line, binary.match.index + binary.match[0].length)
22859
23235
  };
22860
23236
  }
@@ -22875,7 +23251,7 @@ function killProcessTree(pid, fallback) {
22875
23251
  return;
22876
23252
  }
22877
23253
  if (process.platform === "win32") {
22878
- const killer = (0, import_node_child_process8.spawn)("taskkill", ["/pid", String(pid), "/T", "/F"], { windowsHide: true, stdio: "ignore" });
23254
+ const killer = (0, import_node_child_process10.spawn)("taskkill", ["/pid", String(pid), "/T", "/F"], { windowsHide: true, stdio: "ignore" });
22879
23255
  killer.once("error", fallback);
22880
23256
  killer.once("close", (code) => {
22881
23257
  if (code !== 0) fallback();
@@ -22900,7 +23276,7 @@ async function runAgentCommand(command, args, cwd, stdin, runId) {
22900
23276
  const spawnCommand = shim?.command ?? (useCmdShim ? process.env.ComSpec || "cmd.exe" : command);
22901
23277
  const spawnArgs = shim ? [...shim.args, ...args] : useCmdShim ? ["/d", "/s", "/c", [command, ...args.map(quoteWindowsCmdArg)].join(" ")] : args;
22902
23278
  return new Promise((resolveRun) => {
22903
- const child = (0, import_node_child_process8.spawn)(spawnCommand, spawnArgs, {
23279
+ const child = (0, import_node_child_process10.spawn)(spawnCommand, spawnArgs, {
22904
23280
  cwd,
22905
23281
  windowsHide: true,
22906
23282
  // A separate process group lets Stop and timeout terminate descendants on Unix too.
@@ -22979,8 +23355,8 @@ async function writeChatContextArtifact(cwd, request, prompt) {
22979
23355
  "",
22980
23356
  prompt
22981
23357
  ].filter((line) => typeof line === "string");
22982
- await (0, import_promises29.mkdir)((0, import_node_path38.join)(cwd, ".viberaven"), { recursive: true });
22983
- await (0, import_promises29.writeFile)((0, import_node_path38.join)(cwd, ".viberaven", "chat-context.md"), `${lines.join("\n")}
23358
+ await (0, import_promises31.mkdir)((0, import_node_path40.join)(cwd, ".viberaven"), { recursive: true });
23359
+ await (0, import_promises31.writeFile)((0, import_node_path40.join)(cwd, ".viberaven", "chat-context.md"), `${lines.join("\n")}
22984
23360
  `, "utf8");
22985
23361
  } catch {
22986
23362
  }
@@ -23000,13 +23376,13 @@ async function runAgentChat(cwd, request) {
23000
23376
  }
23001
23377
  const prompt = await buildAgentChatPrompt(request, cwd);
23002
23378
  await writeChatContextArtifact(cwd, request, prompt);
23003
- const outputPath = request.cliId === "codex" ? (0, import_node_path38.join)(cwd, ".viberaven", `studio-agent-${Date.now()}-${Math.random().toString(36).slice(2)}.md`) : void 0;
23004
- if (outputPath) await (0, import_promises29.mkdir)((0, import_node_path38.dirname)(outputPath), { recursive: true });
23379
+ const outputPath = request.cliId === "codex" ? (0, import_node_path40.join)(cwd, ".viberaven", `studio-agent-${Date.now()}-${Math.random().toString(36).slice(2)}.md`) : void 0;
23380
+ if (outputPath) await (0, import_promises31.mkdir)((0, import_node_path40.dirname)(outputPath), { recursive: true });
23005
23381
  const { command, args, stdin } = commandForAgentChat(request, prompt, cwd, outputPath);
23006
23382
  const result = await runAgentCommand(command, args, cwd, stdin, request.runId);
23007
23383
  if (outputPath) {
23008
23384
  try {
23009
- const finalMessage = (await (0, import_promises29.readFile)(outputPath, "utf8")).trim();
23385
+ const finalMessage = (await (0, import_promises31.readFile)(outputPath, "utf8")).trim();
23010
23386
  if (finalMessage) return { ...result, output: finalMessage };
23011
23387
  } catch {
23012
23388
  }
@@ -23018,8 +23394,8 @@ function firstGapId(state) {
23018
23394
  return state.providers.find((provider2) => provider2.nextFix?.id)?.nextFix?.id;
23019
23395
  }
23020
23396
  async function runLocalAction(cwd, intent) {
23021
- const artifactDir = (0, import_node_path38.join)(cwd, ".viberaven");
23022
- await (0, import_promises29.mkdir)(artifactDir, { recursive: true });
23397
+ const artifactDir = (0, import_node_path40.join)(cwd, ".viberaven");
23398
+ await (0, import_promises31.mkdir)(artifactDir, { recursive: true });
23023
23399
  if (intent === "preview_rehearsal") {
23024
23400
  const state2 = await readState(cwd);
23025
23401
  const topProvider = state2.providers[0];
@@ -23060,7 +23436,7 @@ async function runLocalAction(cwd, intent) {
23060
23436
  proof,
23061
23437
  ""
23062
23438
  ].join("\n");
23063
- await (0, import_promises29.writeFile)((0, import_node_path38.join)(artifactDir, "preview-rehearsal.md"), markdown, "utf8");
23439
+ await (0, import_promises31.writeFile)((0, import_node_path40.join)(artifactDir, "preview-rehearsal.md"), markdown, "utf8");
23064
23440
  return {
23065
23441
  title: "Preview rehearsal ready",
23066
23442
  summary: `${projectName}: ${gateLabel} with ${state2.providers.length} provider beat${state2.providers.length === 1 ? "" : "s"}`,
@@ -23070,7 +23446,7 @@ async function runLocalAction(cwd, intent) {
23070
23446
  if (intent === "audit_vercel_supabase") {
23071
23447
  const auditInput = await collectVercelSupabaseAuditInput(cwd);
23072
23448
  const result2 = buildVercelSupabaseAudit(auditInput);
23073
- await (0, import_promises29.writeFile)((0, import_node_path38.join)(artifactDir, "audit-vercel-supabase.txt"), `${renderVercelSupabaseAudit(result2)}
23449
+ await (0, import_promises31.writeFile)((0, import_node_path40.join)(artifactDir, "audit-vercel-supabase.txt"), `${renderVercelSupabaseAudit(result2)}
23074
23450
  `, "utf8");
23075
23451
  return {
23076
23452
  title: result2.status === "pass" ? "Audit passed" : "Audit needs work",
@@ -23098,7 +23474,7 @@ async function runLocalAction(cwd, intent) {
23098
23474
  const mode = intent === "heal_prompt" ? "prompt" : intent === "heal_apply" ? "apply" : "plan";
23099
23475
  const result = await runHealCommand({ cwd, mode, gapId, yes: mode === "apply", noVerify: true });
23100
23476
  if (mode === "apply") {
23101
- await (0, import_promises29.writeFile)((0, import_node_path38.join)(artifactDir, "heal-apply-result.json"), `${JSON.stringify(result, null, 2)}
23477
+ await (0, import_promises31.writeFile)((0, import_node_path40.join)(artifactDir, "heal-apply-result.json"), `${JSON.stringify(result, null, 2)}
23102
23478
  `, "utf8");
23103
23479
  }
23104
23480
  return {
@@ -23108,14 +23484,14 @@ async function runLocalAction(cwd, intent) {
23108
23484
  };
23109
23485
  }
23110
23486
  async function sendArtifact(cwd, rel, response, contentType = "text/plain; charset=utf-8") {
23111
- const absolute = (0, import_node_path38.resolve)(cwd, rel);
23112
- const root = (0, import_node_path38.resolve)(cwd);
23487
+ const absolute = (0, import_node_path40.resolve)(cwd, rel);
23488
+ const root = (0, import_node_path40.resolve)(cwd);
23113
23489
  if (!absolute.startsWith(root)) {
23114
23490
  sendText(response, 403, "Forbidden");
23115
23491
  return;
23116
23492
  }
23117
23493
  try {
23118
- const text = await (0, import_promises29.readFile)(absolute, "utf8");
23494
+ const text = await (0, import_promises31.readFile)(absolute, "utf8");
23119
23495
  response.writeHead(200, { "content-type": contentType });
23120
23496
  response.end(text);
23121
23497
  } catch {
@@ -23124,7 +23500,7 @@ async function sendArtifact(cwd, rel, response, contentType = "text/plain; chars
23124
23500
  }
23125
23501
  async function sendReportArtifact(cwd, response) {
23126
23502
  try {
23127
- const text = await (0, import_promises29.readFile)((0, import_node_path38.join)(cwd, ".viberaven", "report.html"), "utf8");
23503
+ const text = await (0, import_promises31.readFile)((0, import_node_path40.join)(cwd, ".viberaven", "report.html"), "utf8");
23128
23504
  response.writeHead(200, { "content-type": "text/html; charset=utf-8" });
23129
23505
  response.end(text);
23130
23506
  return;
@@ -23251,7 +23627,7 @@ async function sendReportArtifact(cwd, response) {
23251
23627
  }
23252
23628
  async function sendExtensionIcon(cwd, response) {
23253
23629
  try {
23254
- const body = await (0, import_promises29.readFile)((0, import_node_path38.join)(cwd, "media", "extension-icon.png"));
23630
+ const body = await (0, import_promises31.readFile)((0, import_node_path40.join)(cwd, "media", "extension-icon.png"));
23255
23631
  sendBinary(response, 200, "image/png", body);
23256
23632
  } catch {
23257
23633
  sendText(response, 404, "Icon not found");
@@ -23264,15 +23640,15 @@ async function sendMangaAsset(pathname, response) {
23264
23640
  return;
23265
23641
  }
23266
23642
  const candidates = [
23267
- (0, import_node_path38.join)(__dirname, "manga", name),
23643
+ (0, import_node_path40.join)(__dirname, "manga", name),
23268
23644
  // installed + dev: <pkg>/dist/manga
23269
- (0, import_node_path38.join)(__dirname, "..", "assets", "manga", name),
23645
+ (0, import_node_path40.join)(__dirname, "..", "assets", "manga", name),
23270
23646
  // fallback: <pkg>/assets/manga (source)
23271
- (0, import_node_path38.join)(__dirname, "..", "..", "assets", "manga", name)
23647
+ (0, import_node_path40.join)(__dirname, "..", "..", "assets", "manga", name)
23272
23648
  ];
23273
23649
  for (const candidate of candidates) {
23274
23650
  try {
23275
- const body = await (0, import_promises29.readFile)(candidate);
23651
+ const body = await (0, import_promises31.readFile)(candidate);
23276
23652
  response.setHeader("cache-control", "no-cache");
23277
23653
  sendBinary(response, 200, contentTypeForAsset(candidate), body);
23278
23654
  return;
@@ -23282,7 +23658,7 @@ async function sendMangaAsset(pathname, response) {
23282
23658
  sendText(response, 404, "Asset not found");
23283
23659
  }
23284
23660
  function contentTypeForAsset(pathname) {
23285
- switch ((0, import_node_path38.extname)(pathname).toLowerCase()) {
23661
+ switch ((0, import_node_path40.extname)(pathname).toLowerCase()) {
23286
23662
  case ".svg":
23287
23663
  return "image/svg+xml; charset=utf-8";
23288
23664
  case ".png":
@@ -23299,14 +23675,14 @@ function contentTypeForAsset(pathname) {
23299
23675
  }
23300
23676
  async function sendBundledReportAsset(pathname, response) {
23301
23677
  const rel = decodeURIComponent(pathname.replace(/^\/report\//, ""));
23302
- const assetRoot = (0, import_node_path38.resolve)(getBundledReportAssetsDir());
23303
- const absolute = (0, import_node_path38.resolve)(assetRoot, rel);
23678
+ const assetRoot = (0, import_node_path40.resolve)(getBundledReportAssetsDir());
23679
+ const absolute = (0, import_node_path40.resolve)(assetRoot, rel);
23304
23680
  if (!absolute.startsWith(assetRoot)) {
23305
23681
  sendText(response, 403, "Forbidden");
23306
23682
  return;
23307
23683
  }
23308
23684
  try {
23309
- const body = await (0, import_promises29.readFile)(absolute);
23685
+ const body = await (0, import_promises31.readFile)(absolute);
23310
23686
  sendBinary(response, 200, contentTypeForAsset(absolute), body);
23311
23687
  } catch {
23312
23688
  sendText(response, 404, "Asset not found");
@@ -23328,18 +23704,18 @@ async function bundleStackClient(entryPath) {
23328
23704
  return output.text;
23329
23705
  }
23330
23706
  async function sendStackAppJs(response) {
23331
- const distPath = (0, import_node_path38.join)(__dirname, "stack", "app.js");
23707
+ const distPath = (0, import_node_path40.join)(__dirname, "stack", "app.js");
23332
23708
  try {
23333
- const body = await (0, import_promises29.readFile)(distPath);
23709
+ const body = await (0, import_promises31.readFile)(distPath);
23334
23710
  response.writeHead(200, { "content-type": "application/javascript; charset=utf-8", "cache-control": "no-store" });
23335
23711
  response.end(body);
23336
23712
  return;
23337
23713
  } catch {
23338
23714
  }
23339
- const entryPath = (0, import_node_path38.join)(__dirname, "stack", "main.ts");
23715
+ const entryPath = (0, import_node_path40.join)(__dirname, "stack", "main.ts");
23340
23716
  let mtimeMs;
23341
23717
  try {
23342
- mtimeMs = (await (0, import_promises29.stat)(entryPath)).mtimeMs;
23718
+ mtimeMs = (await (0, import_promises31.stat)(entryPath)).mtimeMs;
23343
23719
  } catch {
23344
23720
  sendText(response, 404, "Stack client entry not found: src/local-ui/stack/main.ts is missing.");
23345
23721
  return;
@@ -23358,7 +23734,7 @@ async function sendStackAppJs(response) {
23358
23734
  }
23359
23735
  async function sendStackAppCss(response) {
23360
23736
  try {
23361
- const body = await (0, import_promises29.readFile)((0, import_node_path38.join)(__dirname, "stack", "stack.css"));
23737
+ const body = await (0, import_promises31.readFile)((0, import_node_path40.join)(__dirname, "stack", "stack.css"));
23362
23738
  response.writeHead(200, { "content-type": "text/css; charset=utf-8", "cache-control": "no-store" });
23363
23739
  response.end(body);
23364
23740
  } catch {
@@ -23367,7 +23743,7 @@ async function sendStackAppCss(response) {
23367
23743
  }
23368
23744
  async function readProviderVerifyLedger(cwd) {
23369
23745
  try {
23370
- const raw = await (0, import_promises29.readFile)((0, import_node_path38.join)(cwd, ".viberaven", "provider-verify.json"), "utf8");
23746
+ const raw = await (0, import_promises31.readFile)((0, import_node_path40.join)(cwd, ".viberaven", "provider-verify.json"), "utf8");
23371
23747
  const parsed = JSON.parse(raw.replace(/^\uFEFF/, ""));
23372
23748
  return isRecord5(parsed) ? parsed : {};
23373
23749
  } catch {
@@ -23377,8 +23753,8 @@ async function readProviderVerifyLedger(cwd) {
23377
23753
  async function writeProviderVerifyEntry(cwd, providerId, entry) {
23378
23754
  const ledger = await readProviderVerifyLedger(cwd);
23379
23755
  ledger[providerId] = entry;
23380
- await (0, import_promises29.mkdir)((0, import_node_path38.join)(cwd, ".viberaven"), { recursive: true });
23381
- await (0, import_promises29.writeFile)((0, import_node_path38.join)(cwd, ".viberaven", "provider-verify.json"), `${JSON.stringify(ledger, null, 2)}
23756
+ await (0, import_promises31.mkdir)((0, import_node_path40.join)(cwd, ".viberaven"), { recursive: true });
23757
+ await (0, import_promises31.writeFile)((0, import_node_path40.join)(cwd, ".viberaven", "provider-verify.json"), `${JSON.stringify(ledger, null, 2)}
23382
23758
  `, "utf8");
23383
23759
  }
23384
23760
  async function handleRequest(request, response, options) {
@@ -23421,6 +23797,18 @@ async function handleRequest(request, response, options) {
23421
23797
  sendText(response, 404, "This VibeRaven surface has been removed. Use the Launch Canvas at /.");
23422
23798
  return;
23423
23799
  }
23800
+ if (request.method === "GET" && url.pathname === "/api/github-workflow") {
23801
+ sendJson(response, await githubWorkflowStatus(options.cwd));
23802
+ return;
23803
+ }
23804
+ if (request.method === "POST" && url.pathname === "/api/github-workflow") {
23805
+ try {
23806
+ sendJson(response, await writeGithubWorkflow(options.cwd));
23807
+ } catch (error) {
23808
+ sendJsonStatus(response, 500, { error: `Could not write the workflow: ${error instanceof Error ? error.message : String(error)}` });
23809
+ }
23810
+ return;
23811
+ }
23424
23812
  if (request.method === "GET" && url.pathname === "/api/project") {
23425
23813
  sendJson(response, await readState(options.cwd));
23426
23814
  return;
@@ -23439,13 +23827,13 @@ async function handleRequest(request, response, options) {
23439
23827
  return;
23440
23828
  }
23441
23829
  if (request.method === "GET" && url.pathname === "/api/architecture-map") {
23442
- const mapPath = (0, import_node_path38.join)(options.cwd, ".viberaven", "architecture-map.md");
23443
- const markdown = await (0, import_promises29.readFile)(mapPath, "utf8").catch(() => null);
23830
+ const mapPath = (0, import_node_path40.join)(options.cwd, ".viberaven", "architecture-map.md");
23831
+ const markdown = await (0, import_promises31.readFile)(mapPath, "utf8").catch(() => null);
23444
23832
  if (markdown === null) {
23445
23833
  sendJsonStatus(response, 404, { error: "No architecture map yet. Run a scan first (it is written on every scan)." });
23446
23834
  return;
23447
23835
  }
23448
- const dataRaw = await (0, import_promises29.readFile)((0, import_node_path38.join)(options.cwd, ".viberaven", "architecture-map.json"), "utf8").catch(() => null);
23836
+ const dataRaw = await (0, import_promises31.readFile)((0, import_node_path40.join)(options.cwd, ".viberaven", "architecture-map.json"), "utf8").catch(() => null);
23449
23837
  let data = null;
23450
23838
  if (dataRaw) {
23451
23839
  try {
@@ -23454,7 +23842,7 @@ async function handleRequest(request, response, options) {
23454
23842
  data = null;
23455
23843
  }
23456
23844
  }
23457
- const contextRaw = await (0, import_promises29.readFile)((0, import_node_path38.join)(options.cwd, ".viberaven", "context-map.json"), "utf8").catch(() => null);
23845
+ const contextRaw = await (0, import_promises31.readFile)((0, import_node_path40.join)(options.cwd, ".viberaven", "context-map.json"), "utf8").catch(() => null);
23458
23846
  let gaps = [];
23459
23847
  if (contextRaw) {
23460
23848
  try {
@@ -23720,35 +24108,35 @@ async function handleRequest(request, response, options) {
23720
24108
  return;
23721
24109
  }
23722
24110
  if (request.method === "GET" && url.pathname === "/api/artifacts/tasklist") {
23723
- await sendArtifact(options.cwd, (0, import_node_path38.join)(".viberaven", "agent-tasklist.md"), response);
24111
+ await sendArtifact(options.cwd, (0, import_node_path40.join)(".viberaven", "agent-tasklist.md"), response);
23724
24112
  return;
23725
24113
  }
23726
24114
  if (request.method === "GET" && url.pathname === "/api/artifacts/playbook") {
23727
- await sendArtifact(options.cwd, (0, import_node_path38.join)(".viberaven", "launch-playbook.md"), response);
24115
+ await sendArtifact(options.cwd, (0, import_node_path40.join)(".viberaven", "launch-playbook.md"), response);
23728
24116
  return;
23729
24117
  }
23730
24118
  if (request.method === "GET" && url.pathname === "/api/artifacts/audit") {
23731
- await sendArtifact(options.cwd, (0, import_node_path38.join)(".viberaven", "audit-vercel-supabase.txt"), response);
24119
+ await sendArtifact(options.cwd, (0, import_node_path40.join)(".viberaven", "audit-vercel-supabase.txt"), response);
23732
24120
  return;
23733
24121
  }
23734
24122
  if (request.method === "GET" && url.pathname === "/api/artifacts/cleanup") {
23735
- await sendArtifact(options.cwd, (0, import_node_path38.join)(".viberaven", "context-cleanup.md"), response);
24123
+ await sendArtifact(options.cwd, (0, import_node_path40.join)(".viberaven", "context-cleanup.md"), response);
23736
24124
  return;
23737
24125
  }
23738
24126
  if (request.method === "GET" && url.pathname === "/api/artifacts/preview") {
23739
- await sendArtifact(options.cwd, (0, import_node_path38.join)(".viberaven", "preview-rehearsal.md"), response);
24127
+ await sendArtifact(options.cwd, (0, import_node_path40.join)(".viberaven", "preview-rehearsal.md"), response);
23740
24128
  return;
23741
24129
  }
23742
24130
  if (request.method === "GET" && url.pathname === "/api/artifacts/heal-plan") {
23743
- await sendArtifact(options.cwd, (0, import_node_path38.join)(".viberaven", "heal-plan.md"), response);
24131
+ await sendArtifact(options.cwd, (0, import_node_path40.join)(".viberaven", "heal-plan.md"), response);
23744
24132
  return;
23745
24133
  }
23746
24134
  if (request.method === "GET" && url.pathname === "/api/artifacts/heal-prompt") {
23747
- await sendArtifact(options.cwd, (0, import_node_path38.join)(".viberaven", "heal-prompt.md"), response);
24135
+ await sendArtifact(options.cwd, (0, import_node_path40.join)(".viberaven", "heal-prompt.md"), response);
23748
24136
  return;
23749
24137
  }
23750
24138
  if (request.method === "GET" && url.pathname === "/api/artifacts/heal-apply") {
23751
- await sendArtifact(options.cwd, (0, import_node_path38.join)(".viberaven", "heal-apply-result.json"), response, "application/json; charset=utf-8");
24139
+ await sendArtifact(options.cwd, (0, import_node_path40.join)(".viberaven", "heal-apply-result.json"), response, "application/json; charset=utf-8");
23752
24140
  return;
23753
24141
  }
23754
24142
  if (request.method === "GET" && url.pathname === "/api/artifacts/report") {
@@ -23774,12 +24162,12 @@ async function handleRequest(request, response, options) {
23774
24162
  }
23775
24163
  if (request.method === "GET" && url.pathname === "/vendor/anime.umd.min.js") {
23776
24164
  for (const candidate of [
23777
- (0, import_node_path38.join)(__dirname, "vendor", "anime.umd.min.js"),
23778
- (0, import_node_path38.join)(__dirname, "..", "assets", "vendor", "anime.umd.min.js"),
23779
- (0, import_node_path38.join)(__dirname, "..", "..", "assets", "vendor", "anime.umd.min.js")
24165
+ (0, import_node_path40.join)(__dirname, "vendor", "anime.umd.min.js"),
24166
+ (0, import_node_path40.join)(__dirname, "..", "assets", "vendor", "anime.umd.min.js"),
24167
+ (0, import_node_path40.join)(__dirname, "..", "..", "assets", "vendor", "anime.umd.min.js")
23780
24168
  ]) {
23781
24169
  try {
23782
- const body = await (0, import_promises29.readFile)(candidate);
24170
+ const body = await (0, import_promises31.readFile)(candidate);
23783
24171
  response.setHeader("cache-control", "public, max-age=604800, immutable");
23784
24172
  sendBinary(response, 200, "text/javascript; charset=utf-8", body);
23785
24173
  return;
@@ -23996,7 +24384,7 @@ function listen(server, startPort) {
23996
24384
  });
23997
24385
  }
23998
24386
  async function startLocalUiServer(options) {
23999
- const cwd = (0, import_node_path38.resolve)(options.cwd);
24387
+ const cwd = (0, import_node_path40.resolve)(options.cwd);
24000
24388
  const scanRunner = options.scanRunner ?? defaultScanRunner;
24001
24389
  const autoScan = { state: "idle" };
24002
24390
  const fullCheck = new FullCheckService(cwd, options.fullCheck);
@@ -24013,7 +24401,7 @@ async function startLocalUiServer(options) {
24013
24401
  });
24014
24402
  const port = await listen(server, options.port ?? 4177);
24015
24403
  guard.port = port;
24016
- const hasArtifact = await (0, import_promises29.readFile)((0, import_node_path38.join)(cwd, ".viberaven", "last-scan.json"), "utf8").then(
24404
+ const hasArtifact = await (0, import_promises31.readFile)((0, import_node_path40.join)(cwd, ".viberaven", "last-scan.json"), "utf8").then(
24017
24405
  () => true,
24018
24406
  () => false
24019
24407
  );
@@ -24052,16 +24440,17 @@ async function waitForServerShutdown() {
24052
24440
  process.once("SIGTERM", done);
24053
24441
  });
24054
24442
  }
24055
- var import_node_http, import_node_child_process8, import_node_crypto6, import_promises29, import_node_os2, import_node_path38, CLI_AGENT_SETUP, MCP_CONFIG_PATHS, MCP_PROVIDER_NEEDLES, MCP_CONNECT_COMMANDS, MCP_SERVER_RECIPES, STUDIO_TOKEN_HEADER, PROVIDER_IDS, STUDIO_STATUS_ARGS, CLI_AUTH_PROBE, AGENT_RUN_TIMEOUT_MS, activeAgentRuns, stackBundleCache;
24443
+ var import_node_http, import_node_child_process10, import_node_crypto6, import_promises31, import_node_os3, import_node_path40, CLI_AGENT_SETUP, MCP_CONFIG_PATHS, MCP_PROVIDER_NEEDLES, MCP_CONNECT_COMMANDS, MCP_SERVER_RECIPES, STUDIO_TOKEN_HEADER, PROVIDER_IDS, STUDIO_STATUS_ARGS, CLI_AUTH_PROBE, AGENT_RUN_TIMEOUT_MS, activeAgentRuns, stackBundleCache;
24056
24444
  var init_server = __esm({
24057
24445
  "src/local-ui/server.ts"() {
24058
24446
  "use strict";
24059
24447
  import_node_http = require("node:http");
24060
- import_node_child_process8 = require("node:child_process");
24448
+ import_node_child_process10 = require("node:child_process");
24061
24449
  import_node_crypto6 = require("node:crypto");
24062
- import_promises29 = require("node:fs/promises");
24063
- import_node_os2 = require("node:os");
24064
- import_node_path38 = require("node:path");
24450
+ import_promises31 = require("node:fs/promises");
24451
+ import_node_os3 = require("node:os");
24452
+ import_node_path40 = require("node:path");
24453
+ init_initGithub();
24065
24454
  init_cleanPlan();
24066
24455
  init_audit();
24067
24456
  init_heal();
@@ -24102,8 +24491,8 @@ var init_server = __esm({
24102
24491
  MCP_CONFIG_PATHS = [
24103
24492
  ".mcp.json",
24104
24493
  "mcp.json",
24105
- (0, import_node_path38.join)(".cursor", "mcp.json"),
24106
- (0, import_node_path38.join)(".vscode", "mcp.json")
24494
+ (0, import_node_path40.join)(".cursor", "mcp.json"),
24495
+ (0, import_node_path40.join)(".vscode", "mcp.json")
24107
24496
  ];
24108
24497
  MCP_PROVIDER_NEEDLES = {
24109
24498
  supabase: ["supabase", "@supabase/mcp", "mcp-server-supabase"],
@@ -24272,11 +24661,11 @@ var init_nextActionBlock = __esm({
24272
24661
 
24273
24662
  // src/providerMcpBridge.ts
24274
24663
  function defaultMcpConfigPaths() {
24275
- const home = (0, import_node_os3.homedir)();
24664
+ const home = (0, import_node_os4.homedir)();
24276
24665
  return [
24277
- (0, import_node_path39.join)(home, ".config", "claude", "claude_desktop_config.json"),
24278
- (0, import_node_path39.join)(home, ".cursor", "mcp.json"),
24279
- (0, import_node_path39.join)(home, ".gemini", "antigravity", "mcp_config.json")
24666
+ (0, import_node_path41.join)(home, ".config", "claude", "claude_desktop_config.json"),
24667
+ (0, import_node_path41.join)(home, ".cursor", "mcp.json"),
24668
+ (0, import_node_path41.join)(home, ".gemini", "antigravity", "mcp_config.json")
24280
24669
  ];
24281
24670
  }
24282
24671
  function resolveConfigPaths() {
@@ -24305,11 +24694,11 @@ function findServerEntry(servers, provider2) {
24305
24694
  function findProviderMcpConfig(provider2, configPaths) {
24306
24695
  const paths = configPaths ?? resolveConfigPaths();
24307
24696
  for (const path of paths) {
24308
- if (!(0, import_node_fs14.existsSync)(path)) {
24697
+ if (!(0, import_node_fs15.existsSync)(path)) {
24309
24698
  continue;
24310
24699
  }
24311
24700
  try {
24312
- const raw = JSON.parse((0, import_node_fs14.readFileSync)(path, "utf8"));
24701
+ const raw = JSON.parse((0, import_node_fs15.readFileSync)(path, "utf8"));
24313
24702
  const servers = parseMcpServers(raw);
24314
24703
  if (!servers) {
24315
24704
  continue;
@@ -24353,13 +24742,13 @@ async function verifyProviderGap(options) {
24353
24742
  fallbackCommand: FALLBACK_COMMAND
24354
24743
  };
24355
24744
  }
24356
- var import_node_fs14, import_node_os3, import_node_path39, FALLBACK_COMMAND, SUPPORTED_PROVIDERS, configPathsOverride;
24745
+ var import_node_fs15, import_node_os4, import_node_path41, FALLBACK_COMMAND, SUPPORTED_PROVIDERS, configPathsOverride;
24357
24746
  var init_providerMcpBridge = __esm({
24358
24747
  "src/providerMcpBridge.ts"() {
24359
24748
  "use strict";
24360
- import_node_fs14 = require("node:fs");
24361
- import_node_os3 = require("node:os");
24362
- import_node_path39 = require("node:path");
24749
+ import_node_fs15 = require("node:fs");
24750
+ import_node_os4 = require("node:os");
24751
+ import_node_path41 = require("node:path");
24363
24752
  FALLBACK_COMMAND = "npx -y viberaven audit --vercel-supabase --json";
24364
24753
  SUPPORTED_PROVIDERS = /* @__PURE__ */ new Set(["supabase", "vercel"]);
24365
24754
  }
@@ -24388,6 +24777,8 @@ Usage:
24388
24777
  viberaven tui Interactive terminal menu
24389
24778
 
24390
24779
  viberaven check [--json] [--quiet] [path]
24780
+ viberaven check --base <ref> [--markdown] What this change introduced and fixed, compared with <ref>
24781
+ viberaven init --github [--dry-run] Check every pull request in GitHub Actions
24391
24782
  Local production check: one line per finding, exit 1 on blockers
24392
24783
 
24393
24784
  viberaven fix [--gap <id>] [--dry-run] [path]
@@ -24537,6 +24928,9 @@ function isBooleanFlag(command, key) {
24537
24928
  if (key === "open" && (command === "" || command === "scan" || command === "report")) return true;
24538
24929
  if (key === "verify" && command === "") return true;
24539
24930
  if (key === "quiet" && command === "check") return true;
24931
+ if (key === "markdown" && command === "check") return true;
24932
+ if (key === "github" && command === "init") return true;
24933
+ if (key === "force" && command === "init") return true;
24540
24934
  if (key === "vercel-supabase" && command === "audit") return true;
24541
24935
  if (key === "json" && command === "validate-npm-package") return true;
24542
24936
  if (key === "dry-run" && (command === "init" || command === "fix")) return true;
@@ -24570,7 +24964,7 @@ async function cmdProviderVerify(flags, positional) {
24570
24964
  return result.verified ? 0 : 1;
24571
24965
  }
24572
24966
  async function cmdStatus(flags, positional) {
24573
- const startDir = positional[0] ? (0, import_node_path40.resolve)(process.cwd(), positional[0]) : process.cwd();
24967
+ const startDir = positional[0] ? (0, import_node_path42.resolve)(process.cwd(), positional[0]) : process.cwd();
24574
24968
  let artifact;
24575
24969
  try {
24576
24970
  artifact = await loadLastArtifact(startDir);
@@ -24609,7 +25003,7 @@ async function cmdStatus(flags, positional) {
24609
25003
  return 0;
24610
25004
  }
24611
25005
  async function runScanCommand(flags, positional, options) {
24612
- const workspacePath = positional[0] ? (0, import_node_path40.resolve)(process.cwd(), positional[0]) : await resolveWorkspaceRoot(process.cwd());
25006
+ const workspacePath = positional[0] ? (0, import_node_path42.resolve)(process.cwd(), positional[0]) : await resolveWorkspaceRoot(process.cwd());
24613
25007
  if (!options?.deferMachineOutput) {
24614
25008
  console.log(`Scanning ${workspacePath}\u2026 (local, offline)`);
24615
25009
  }
@@ -24636,7 +25030,7 @@ async function runScanCommand(flags, positional, options) {
24636
25030
  printNextActionBlock(block);
24637
25031
  printProviderActionBlock(tasks);
24638
25032
  try {
24639
- const manifest = JSON.parse(await (0, import_promises30.readFile)(paths.actionsPath, "utf8"));
25033
+ const manifest = JSON.parse(await (0, import_promises32.readFile)(paths.actionsPath, "utf8"));
24640
25034
  console.log(renderActionSurface(manifest, { limit: 5 }).trimEnd());
24641
25035
  } catch (error) {
24642
25036
  console.warn(error instanceof Error ? error.message : String(error));
@@ -24653,7 +25047,7 @@ async function runScanCommand(flags, positional, options) {
24653
25047
  return { exitCode: 0, artifacts: paths };
24654
25048
  }
24655
25049
  async function cmdReport(flags, positional) {
24656
- const startDir = positional[0] ? (0, import_node_path40.resolve)(process.cwd(), positional[0]) : process.cwd();
25050
+ const startDir = positional[0] ? (0, import_node_path42.resolve)(process.cwd(), positional[0]) : process.cwd();
24657
25051
  try {
24658
25052
  const paths = await refreshReportFromDisk(startDir);
24659
25053
  console.log(`Report refreshed: ${paths.reportPath}`);
@@ -24675,7 +25069,7 @@ async function cmdReport(flags, positional) {
24675
25069
  }
24676
25070
  }
24677
25071
  async function cmdPrompt(flags, positional) {
24678
- const startDir = positional[0] ? (0, import_node_path40.resolve)(process.cwd(), positional[0]) : process.cwd();
25072
+ const startDir = positional[0] ? (0, import_node_path42.resolve)(process.cwd(), positional[0]) : process.cwd();
24679
25073
  let artifact;
24680
25074
  try {
24681
25075
  artifact = await loadLastArtifact(startDir);
@@ -24772,7 +25166,7 @@ async function main() {
24772
25166
  const wantsJsonl = hasFlag(flags, "jsonl");
24773
25167
  const wantsStrict = hasFlag(flags, "strict");
24774
25168
  if (flags.condense) {
24775
- const cwd = positional[0] ? (0, import_node_path40.resolve)(process.cwd(), positional[0]) : process.cwd();
25169
+ const cwd = positional[0] ? (0, import_node_path42.resolve)(process.cwd(), positional[0]) : process.cwd();
24776
25170
  const result = await runCondenseCommand({ cwd });
24777
25171
  console.log(`VibeRaven context map refreshed: ${result.contextMapPath}`);
24778
25172
  return 0;
@@ -24792,7 +25186,7 @@ async function main() {
24792
25186
  }
24793
25187
  if (!command && flags.verify === true && typeof flags.action === "string") {
24794
25188
  return runVerifyActionCommand({
24795
- cwd: positional[0] ? (0, import_node_path40.resolve)(process.cwd(), positional[0]) : process.cwd(),
25189
+ cwd: positional[0] ? (0, import_node_path42.resolve)(process.cwd(), positional[0]) : process.cwd(),
24796
25190
  actionId: flags.action
24797
25191
  });
24798
25192
  }
@@ -24803,7 +25197,7 @@ async function main() {
24803
25197
  console.error("VibeRaven could not produce machine output because scan artifacts were not written.");
24804
25198
  return 3;
24805
25199
  }
24806
- const gateResult = scanResult.artifacts && (wantsJson || wantsJsonl || wantsStrict) ? JSON.parse(await (0, import_promises30.readFile)(scanResult.artifacts.gateResultPath, "utf8")) : void 0;
25200
+ const gateResult = scanResult.artifacts && (wantsJson || wantsJsonl || wantsStrict) ? JSON.parse(await (0, import_promises32.readFile)(scanResult.artifacts.gateResultPath, "utf8")) : void 0;
24807
25201
  const strictExitCode = wantsStrict && gateResult ? exitCodeForStrictGate(gateResult, { failOnWarnings: flags.strict === "warning" }) : scanResult.exitCode;
24808
25202
  if (wantsJson && gateResult) {
24809
25203
  process.stdout.write(renderGateResultJson(gateResult));
@@ -24825,7 +25219,7 @@ async function main() {
24825
25219
  env: process.env
24826
25220
  });
24827
25221
  if (mode === "local-ui") {
24828
- const cwd = positional[0] ? (0, import_node_path40.resolve)(process.cwd(), positional[0]) : process.cwd();
25222
+ const cwd = positional[0] ? (0, import_node_path42.resolve)(process.cwd(), positional[0]) : process.cwd();
24829
25223
  const handle = await startLocalUiServer({ cwd });
24830
25224
  console.log(`VibeRaven local UI: ${handle.url}`);
24831
25225
  await waitForServerShutdown();
@@ -24845,7 +25239,7 @@ async function main() {
24845
25239
  await runInteractiveSession();
24846
25240
  return 0;
24847
25241
  case "ui": {
24848
- const cwd = positional[0] ? (0, import_node_path40.resolve)(process.cwd(), positional[0]) : process.cwd();
25242
+ const cwd = positional[0] ? (0, import_node_path42.resolve)(process.cwd(), positional[0]) : process.cwd();
24849
25243
  const envPort = Number(process.env.PORT);
24850
25244
  const handle = await startLocalUiServer({ cwd, port: Number.isFinite(envPort) && envPort > 0 ? envPort : void 0 });
24851
25245
  console.log(`VibeRaven local UI: ${handle.url}`);
@@ -24857,12 +25251,14 @@ async function main() {
24857
25251
  return cmdStatus(flags, positional);
24858
25252
  case "check":
24859
25253
  return runCheckCommand({
24860
- cwd: positional[0] ? (0, import_node_path40.resolve)(process.cwd(), positional[0]) : process.cwd(),
25254
+ cwd: positional[0] ? (0, import_node_path42.resolve)(process.cwd(), positional[0]) : process.cwd(),
24861
25255
  json: Boolean(flags.json),
24862
- quiet: Boolean(flags.quiet)
25256
+ quiet: Boolean(flags.quiet),
25257
+ base: typeof flags.base === "string" ? flags.base : void 0,
25258
+ markdown: flags.markdown === true
24863
25259
  });
24864
25260
  case "fix": {
24865
- const fixCwd = positional[0] ? (0, import_node_path40.resolve)(process.cwd(), positional[0]) : process.cwd();
25261
+ const fixCwd = positional[0] ? (0, import_node_path42.resolve)(process.cwd(), positional[0]) : process.cwd();
24866
25262
  const gapId = typeof flags.gap === "string" ? flags.gap : void 0;
24867
25263
  if (!gapId) {
24868
25264
  const artifact = await loadLastArtifact(fixCwd).catch(() => void 0);
@@ -24891,19 +25287,19 @@ async function main() {
24891
25287
  }
24892
25288
  case "actions":
24893
25289
  return runActionsCommand({
24894
- cwd: positional[0] ? (0, import_node_path40.resolve)(process.cwd(), positional[0]) : process.cwd(),
25290
+ cwd: positional[0] ? (0, import_node_path42.resolve)(process.cwd(), positional[0]) : process.cwd(),
24895
25291
  json: Boolean(flags.json)
24896
25292
  });
24897
25293
  case "preview":
24898
25294
  return runPreviewCommand({
24899
- cwd: positional[0] ? (0, import_node_path40.resolve)(process.cwd(), positional[0]) : process.cwd(),
25295
+ cwd: positional[0] ? (0, import_node_path42.resolve)(process.cwd(), positional[0]) : process.cwd(),
24900
25296
  agentMode: flags["agent-mode"] === true,
24901
25297
  json: Boolean(flags.json)
24902
25298
  });
24903
25299
  case "next":
24904
25300
  return runNextCommand({
24905
25301
  json: Boolean(flags.json),
24906
- cwd: positional[0] ? (0, import_node_path40.resolve)(process.cwd(), positional[0]) : process.cwd()
25302
+ cwd: positional[0] ? (0, import_node_path42.resolve)(process.cwd(), positional[0]) : process.cwd()
24907
25303
  });
24908
25304
  case "guide": {
24909
25305
  const provider2 = positional[0];
@@ -24937,8 +25333,11 @@ async function main() {
24937
25333
  case "provider-verify":
24938
25334
  return cmdProviderVerify(flags, positional);
24939
25335
  case "init": {
24940
- const cwd = positional[0] ? (0, import_node_path40.resolve)(process.cwd(), positional[0]) : process.cwd();
25336
+ const cwd = positional[0] ? (0, import_node_path42.resolve)(process.cwd(), positional[0]) : process.cwd();
24941
25337
  const agents = typeof flags.agents === "string" ? flags.agents : void 0;
25338
+ if (flags.github === true) {
25339
+ return runInitGithubCommand({ cwd, dryRun: flags["dry-run"] === true, force: flags.force === true });
25340
+ }
24942
25341
  return runInitCommand({
24943
25342
  cwd,
24944
25343
  agents,
@@ -24951,7 +25350,7 @@ async function main() {
24951
25350
  return 1;
24952
25351
  }
24953
25352
  return runDoctorAgentsCommand({
24954
- cwd: positional[0] ? (0, import_node_path40.resolve)(process.cwd(), positional[0]) : process.cwd()
25353
+ cwd: positional[0] ? (0, import_node_path42.resolve)(process.cwd(), positional[0]) : process.cwd()
24955
25354
  });
24956
25355
  case "validate-npm-package":
24957
25356
  return runValidateNpmPackageCommand({
@@ -24964,7 +25363,7 @@ async function main() {
24964
25363
  return 1;
24965
25364
  }
24966
25365
  return runAuditCommand({
24967
- cwd: positional[0] ? (0, import_node_path40.resolve)(process.cwd(), positional[0]) : process.cwd(),
25366
+ cwd: positional[0] ? (0, import_node_path42.resolve)(process.cwd(), positional[0]) : process.cwd(),
24968
25367
  json: Boolean(flags.json)
24969
25368
  });
24970
25369
  case "clean": {
@@ -24972,7 +25371,7 @@ async function main() {
24972
25371
  console.error("Usage: viberaven clean --plan [path]");
24973
25372
  return 1;
24974
25373
  }
24975
- const cwd = positional[0] ? (0, import_node_path40.resolve)(process.cwd(), positional[0]) : process.cwd();
25374
+ const cwd = positional[0] ? (0, import_node_path42.resolve)(process.cwd(), positional[0]) : process.cwd();
24976
25375
  const plan = buildContextCleanupPlan({ projectRoot: cwd, files: await collectCleanupFiles(cwd) });
24977
25376
  const outputPath = await writeCleanupPlan(cwd, plan);
24978
25377
  console.log(`Wrote ${outputPath}
@@ -24982,9 +25381,9 @@ async function main() {
24982
25381
  }
24983
25382
  case "rls-test":
24984
25383
  return runRlsTestCommand({
24985
- cwd: positional[0] ? (0, import_node_path40.resolve)(process.cwd(), positional[0]) : process.cwd(),
24986
- matrix: typeof flags.matrix === "string" ? (0, import_node_path40.resolve)(process.cwd(), flags.matrix) : void 0,
24987
- migrations: typeof flags.migrations === "string" ? (0, import_node_path40.resolve)(process.cwd(), flags.migrations) : void 0,
25384
+ cwd: positional[0] ? (0, import_node_path42.resolve)(process.cwd(), positional[0]) : process.cwd(),
25385
+ matrix: typeof flags.matrix === "string" ? (0, import_node_path42.resolve)(process.cwd(), flags.matrix) : void 0,
25386
+ migrations: typeof flags.migrations === "string" ? (0, import_node_path42.resolve)(process.cwd(), flags.migrations) : void 0,
24988
25387
  json: Boolean(flags.json)
24989
25388
  });
24990
25389
  default:
@@ -24993,11 +25392,11 @@ async function main() {
24993
25392
  return 1;
24994
25393
  }
24995
25394
  }
24996
- var import_promises30, import_node_path40, STACK_AREAS;
25395
+ var import_promises32, import_node_path42, STACK_AREAS;
24997
25396
  var init_cli = __esm({
24998
25397
  "src/cli.ts"() {
24999
- import_promises30 = require("node:fs/promises");
25000
- import_node_path40 = require("node:path");
25398
+ import_promises32 = require("node:fs/promises");
25399
+ import_node_path42 = require("node:path");
25001
25400
  init_config();
25002
25401
  init_runScan();
25003
25402
  init_artifacts2();
@@ -25021,6 +25420,7 @@ var init_cli = __esm({
25021
25420
  init_stackRecommend();
25022
25421
  init_commands();
25023
25422
  init_runInit();
25423
+ init_initGithub();
25024
25424
  init_runDoctor();
25025
25425
  init_runValidateNpmPackage();
25026
25426
  init_runAudit();