@viberaven/cli 1.6.2 → 1.6.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -656,9 +656,30 @@ function normalizeGap(v) {
656
656
  toolSuggestions,
657
657
  mcpSuggestion: typeof v.mcpSuggestion === "string" ? v.mcpSuggestion : null,
658
658
  primaryMapCategory: affectedMapCategories[0],
659
- affectedMapCategories
659
+ affectedMapCategories,
660
+ ...normalizeLocations(v.locations)
660
661
  };
661
662
  }
663
+ function normalizeLocations(v) {
664
+ if (!Array.isArray(v)) return {};
665
+ const locations = [];
666
+ for (const item4 of v) {
667
+ if (!isRecord(item4) || typeof item4.file !== "string" || !item4.file.trim()) continue;
668
+ const line = typeof item4.line === "number" && Number.isInteger(item4.line) && item4.line > 0 ? item4.line : void 0;
669
+ locations.push(line === void 0 ? { file: item4.file } : { file: item4.file, line });
670
+ }
671
+ return locations.length > 0 ? { locations } : {};
672
+ }
673
+ function mergeLocations(a, b3) {
674
+ const seen = /* @__PURE__ */ new Set();
675
+ const merged = [...a ?? [], ...b3 ?? []].filter((location) => {
676
+ const key = `${location.file}:${location.line ?? ""}`;
677
+ if (seen.has(key)) return false;
678
+ seen.add(key);
679
+ return true;
680
+ });
681
+ return merged.length > 0 ? { locations: merged } : {};
682
+ }
662
683
  function rootGapKey(gap) {
663
684
  const titleKey = slugify(gap.title);
664
685
  if (titleKey) {
@@ -688,7 +709,8 @@ function mergeRootGaps(a, b3) {
688
709
  toolSuggestions: mergeToolSuggestions(a.toolSuggestions, b3.toolSuggestions),
689
710
  mcpSuggestion: a.mcpSuggestion ?? b3.mcpSuggestion,
690
711
  primaryMapCategory: a.primaryMapCategory,
691
- affectedMapCategories: a.affectedMapCategories
712
+ affectedMapCategories: a.affectedMapCategories,
713
+ ...mergeLocations(a.locations, b3.locations)
692
714
  };
693
715
  }
694
716
  function dedupeRootGaps(gaps) {
@@ -3304,11 +3326,11 @@ var init_providerTruth = __esm({
3304
3326
  // ../../src/station/promptBuilder.ts
3305
3327
  function buildAnalysisPrompt(scan, specContent, productionConnectionContext, scannerEvidenceContext, stackWiringContext, stackAutomationContext) {
3306
3328
  const sections = [];
3307
- const detectedStack = Object.entries(scan.stackSignals).filter(([, v]) => v === true).map(([k3]) => k3).join(", ");
3329
+ const detectedStack2 = Object.entries(scan.stackSignals).filter(([, v]) => v === true).map(([k3]) => k3).join(", ");
3308
3330
  sections.push(`## PROJECT CONTEXT
3309
3331
  Total files in repo: ${scan.totalFilesScanned}
3310
3332
  Files analyzed: ${scan.files.length}
3311
- Detected stack signals: ${detectedStack || "none detected"}
3333
+ Detected stack signals: ${detectedStack2 || "none detected"}
3312
3334
  All dependencies: ${scan.packageDeps.join(", ") || "none found"}
3313
3335
  Secret files found (contents not read): ${scan.secretsFound.join(", ") || "none"}
3314
3336
  `);
@@ -8462,8 +8484,9 @@ function readFunctionAttributes(tokens) {
8462
8484
  if (t.value === "security" && next?.kind === "word" && (next.value === "definer" || next.value === "invoker")) {
8463
8485
  out.definer = next.value === "definer";
8464
8486
  }
8465
- if (t.value === "returns" && next?.kind === "word") {
8466
- out.returnsTrigger = next.value === "trigger" || next.value === "event_trigger";
8487
+ if (t.value === "returns" && (next?.kind === "word" || next?.kind === "ident")) {
8488
+ const type = next.value;
8489
+ out.returnsTrigger = type === "trigger" || type === "event_trigger";
8467
8490
  }
8468
8491
  }
8469
8492
  return out;
@@ -8591,7 +8614,7 @@ function analyzeRls(replay) {
8591
8614
  }
8592
8615
  if (!table2.rls && (table2.created || table2.disabledLoc)) {
8593
8616
  const where = table2.disabledLoc ?? table2.arrivedLoc ?? table2.created;
8594
- 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).";
8617
+ const risk = "Without RLS, the Data API does not enforce row restrictions for roles and operations allowed by the table's grants. Verify those grants to determine who can read or change rows.";
8595
8618
  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}`;
8596
8619
  missingRls.push({
8597
8620
  id: "rls_disabled",
@@ -9417,7 +9440,7 @@ function buildRlsCheck(replay) {
9417
9440
  id: "supabase-rls-policy-proof",
9418
9441
  title: "Supabase RLS policy proof",
9419
9442
  status: "needs_work",
9420
- summary: `${plural(analysis.missingRls.length, "public table")} without row level security: ${names}. Anyone holding the anon key your frontend ships can read and change those rows through the Data API.`,
9443
+ summary: `${plural(analysis.missingRls.length, "public table")} without row level security: ${names}. The Data API does not enforce row restrictions for roles and operations allowed by the table's grants. Verify those grants to determine who can read or change rows.`,
9421
9444
  evidence: findingEvidence(analysis.missingRls),
9422
9445
  findings
9423
9446
  };
@@ -10437,6 +10460,18 @@ var init_databaseEvidence = __esm({
10437
10460
  });
10438
10461
 
10439
10462
  // src/localStation.ts
10463
+ function findingLocations(findings) {
10464
+ const seen = /* @__PURE__ */ new Set();
10465
+ const out = [];
10466
+ for (const finding of findings) {
10467
+ if (!finding.file) continue;
10468
+ const key = `${finding.file}:${finding.line ?? ""}`;
10469
+ if (seen.has(key)) continue;
10470
+ seen.add(key);
10471
+ out.push(finding.line ? { file: finding.file, line: finding.line } : { file: finding.file });
10472
+ }
10473
+ return out.slice(0, DETAIL_MAX_FINDINGS);
10474
+ }
10440
10475
  function hasFilePath(scan, pattern) {
10441
10476
  return scan.files.some((f) => pattern.test(f.path));
10442
10477
  }
@@ -10730,7 +10765,8 @@ function auditFindingSeeds(audit) {
10730
10765
  detail: capLines(lines).join("\n"),
10731
10766
  copyPrompt: fixes.length > 0 ? `${meta.prompt}
10732
10767
  ${capLines(fixes).join("\n")}` : meta.prompt,
10733
- primaryMapCategory: meta.map
10768
+ primaryMapCategory: meta.map,
10769
+ locations: findingLocations(findings)
10734
10770
  };
10735
10771
  });
10736
10772
  }
@@ -10929,6 +10965,7 @@ ${check.evidence.map((e2) => `- ${e2}`).join("\n")}` : "";
10929
10965
  category: "SECURITY & AUTH",
10930
10966
  severity: "warning",
10931
10967
  title: "Stripe webhook does not verify the signature",
10968
+ locations: findingLocations(webhooks.unverified.map((u2) => ({ file: u2.path }))),
10932
10969
  detail: `${webhooks.unverified.length} Stripe webhook handler${webhooks.unverified.length === 1 ? "" : "s"} without signature verification over the raw body: ${capLines(lines).join("; ")}. Anyone who finds the URL can post a fake event, for example a paid checkout.`,
10933
10970
  copyPrompt: 'In each listed handler, read the raw request body (await req.text() in Next.js route handlers and Deno, express.raw({ type: "application/json" }) in Express), read the stripe-signature header, and pass both to stripe.webhooks.constructEvent (constructEventAsync on Deno or edge runtimes) with STRIPE_WEBHOOK_SECRET before trusting event.type. Return 400 when verification throws.',
10934
10971
  primaryMapCategory: "payments"
@@ -10965,7 +11002,7 @@ ${check.evidence.map((e2) => `- ${e2}`).join("\n")}` : "";
10965
11002
  severity: "warning",
10966
11003
  title: `Env vars used in code but missing from ${target}`,
10967
11004
  detail: `Code reads ${count} that ${contract.templates.length === 1 ? target : `no env template (${contract.templates.join(", ")})`} does not document (${shown}${more}). Fresh clones, CI, and the production deploy can silently run without them.`,
10968
- copyPrompt: `Add placeholder entries for these variables to ${contract.templates.length === 1 ? target : contract.templates[0]} (never real values): ${shown}${drifted.length > 5 ? " and the rest" : ""}. Then confirm each one is set in the deployment environment (Vercel project settings).`,
11005
+ copyPrompt: `Add placeholder entries for these variables to ${contract.templates.length === 1 ? target : contract.templates[0]} (never real values): ${shown}${drifted.length > 5 ? " and the rest" : ""}. Then confirm each one is set in the deployment environment (the hosting provider's project settings).`,
10969
11006
  primaryMapCategory: "deployment"
10970
11007
  });
10971
11008
  } else {
@@ -11032,9 +11069,9 @@ function scoreFrom(gaps) {
11032
11069
  }
11033
11070
  return Math.max(5, Math.min(96, score));
11034
11071
  }
11035
- function labelFor(score, criticalCount) {
11072
+ function scoreLabelFor(score, criticalCount, warningCount) {
11036
11073
  if (criticalCount > 0) return "Blocked";
11037
- if (score >= 85) return "Launch ready";
11074
+ if (warningCount === 0) return "No blockers in repo checks";
11038
11075
  if (score >= 60) return "Almost there";
11039
11076
  return "Needs work";
11040
11077
  }
@@ -11133,11 +11170,12 @@ async function buildLocalStationOutput(input) {
11133
11170
  toolSuggestions: [],
11134
11171
  mcpSuggestion: null,
11135
11172
  primaryMapCategory: seed.primaryMapCategory,
11136
- affectedMapCategories: [seed.primaryMapCategory]
11173
+ affectedMapCategories: [seed.primaryMapCategory],
11174
+ ...seed.locations?.length ? { locations: seed.locations } : {}
11137
11175
  }));
11138
11176
  return {
11139
11177
  score,
11140
- scoreLabel: labelFor(score, criticalCount),
11178
+ scoreLabel: scoreLabelFor(score, criticalCount, warningCount),
11141
11179
  summary,
11142
11180
  archetype,
11143
11181
  gaps,
@@ -11593,6 +11631,28 @@ var init_runScan = __esm({
11593
11631
  }
11594
11632
  });
11595
11633
 
11634
+ // src/contracts/detectedProviders.ts
11635
+ function isProviderInRepo(row) {
11636
+ if ((row.roles ?? []).includes("runtime-code")) return true;
11637
+ const evidence = (row.evidence ?? []).filter((item4) => item4.kind !== "docs-mention");
11638
+ if (!evidence.some((item4) => PACKAGE_OR_CONFIG.has(item4.kind))) return false;
11639
+ return !evidence.every((item4) => item4.kind === "package-installed");
11640
+ }
11641
+ function providersInRepo(artifact) {
11642
+ const areas = artifact.providerTruth?.areas;
11643
+ if (!Array.isArray(areas)) return void 0;
11644
+ return areas.flatMap(
11645
+ (area) => (area.rows ?? []).filter(isProviderInRepo).map((row) => ({ area: String(area.area), provider: String(row.provider) }))
11646
+ );
11647
+ }
11648
+ var PACKAGE_OR_CONFIG;
11649
+ var init_detectedProviders = __esm({
11650
+ "src/contracts/detectedProviders.ts"() {
11651
+ "use strict";
11652
+ PACKAGE_OR_CONFIG = /* @__PURE__ */ new Set(["package-installed", "deployment-config", "provider-config"]);
11653
+ }
11654
+ });
11655
+
11596
11656
  // src/capabilities/classify.ts
11597
11657
  function gapText(gap) {
11598
11658
  return `${gap.id} ${gap.title} ${gap.detail} ${gap.primaryMapCategory}`.toLowerCase();
@@ -11780,6 +11840,37 @@ var init_capabilities = __esm({
11780
11840
  }
11781
11841
  });
11782
11842
 
11843
+ // src/contracts/packageManager.ts
11844
+ function detectPackageManager(workspacePath) {
11845
+ if (!workspacePath) return "unknown";
11846
+ try {
11847
+ const manifest = JSON.parse((0, import_node_fs4.readFileSync)((0, import_node_path8.join)(workspacePath, "package.json"), "utf8").replace(/^\uFEFF/, ""));
11848
+ const named = typeof manifest.packageManager === "string" ? manifest.packageManager.split("@")[0] : "";
11849
+ if (named === "npm" || named === "pnpm" || named === "yarn" || named === "bun") return named;
11850
+ } catch {
11851
+ }
11852
+ for (const [file, manager] of LOCKFILES) {
11853
+ if ((0, import_node_fs4.existsSync)((0, import_node_path8.join)(workspacePath, file))) return manager;
11854
+ }
11855
+ return "unknown";
11856
+ }
11857
+ var import_node_fs4, import_node_path8, LOCKFILES;
11858
+ var init_packageManager = __esm({
11859
+ "src/contracts/packageManager.ts"() {
11860
+ "use strict";
11861
+ import_node_fs4 = require("node:fs");
11862
+ import_node_path8 = require("node:path");
11863
+ LOCKFILES = [
11864
+ ["pnpm-lock.yaml", "pnpm"],
11865
+ ["yarn.lock", "yarn"],
11866
+ ["bun.lock", "bun"],
11867
+ ["bun.lockb", "bun"],
11868
+ ["package-lock.json", "npm"],
11869
+ ["npm-shrinkwrap.json", "npm"]
11870
+ ];
11871
+ }
11872
+ });
11873
+
11783
11874
  // src/contracts/commands.ts
11784
11875
  function promptGapCommand(gapId) {
11785
11876
  return `${PUBLIC_COMMAND} prompt --gap ${gapId}`;
@@ -11807,65 +11898,18 @@ var init_commands = __esm({
11807
11898
  }
11808
11899
  });
11809
11900
 
11810
- // src/contracts/contextMap.ts
11811
- function generateContextMap(artifact) {
11812
- const criticalCount = artifact.gaps.filter((gap) => gap.severity === "critical").length;
11813
- const warningCount = artifact.gaps.filter((gap) => gap.severity === "warning").length;
11814
- return {
11815
- $schema: "https://viberaven.dev/schemas/context-map.schema.json",
11816
- schemaVersion: "v1",
11817
- generatedAt: artifact.scannedAt,
11818
- workspace: {
11819
- root: artifact.workspacePath,
11820
- packageManager: "unknown",
11821
- languages: ["typescript"],
11822
- frameworks: artifact.archetype ? [artifact.archetype] : []
11823
- },
11824
- agentContract: {
11825
- readFirst: ".viberaven/agent-tasklist.md",
11826
- machineResult: ".viberaven/gate-result.json",
11827
- contextMap: ".viberaven/context-map.json",
11828
- verifyCommand: PUBLIC_VERIFY_COMMAND,
11829
- strictCommand: PUBLIC_STRICT_COMMAND,
11830
- nextCommand: PUBLIC_NEXT_JSON_COMMAND
11831
- },
11832
- stack: {
11833
- deployment: artifact.selectedProviders?.deployment ? [artifact.selectedProviders.deployment] : [],
11834
- database: artifact.selectedProviders?.database ? [artifact.selectedProviders.database] : [],
11835
- auth: artifact.selectedProviders?.auth ? [artifact.selectedProviders.auth] : [],
11836
- payments: artifact.selectedProviders?.payments ? [artifact.selectedProviders.payments] : [],
11837
- monitoring: artifact.selectedProviders?.monitoring ? [artifact.selectedProviders.monitoring] : []
11838
- },
11839
- capabilityGraph: summarizeCapabilities(artifact.gaps),
11840
- productionGate: {
11841
- status: criticalCount > 0 ? "not_clear" : warningCount > 0 ? "warning" : "clear",
11842
- criticalCount,
11843
- warningCount,
11844
- providerDashboardChecksRequired: true
11845
- },
11846
- topGaps: artifact.gaps.slice(0, 10).map((gap) => ({
11847
- id: gap.id,
11848
- severity: gap.severity,
11849
- title: gap.title,
11850
- area: String(gap.primaryMapCategory),
11851
- promptCommand: promptGapCommand(gap.id)
11852
- }))
11853
- };
11854
- }
11855
- var init_contextMap = __esm({
11856
- "src/contracts/contextMap.ts"() {
11857
- "use strict";
11858
- init_capabilities();
11859
- init_commands();
11860
- }
11861
- });
11862
-
11863
11901
  // src/contracts/gateResult.ts
11864
11902
  function gateStatus(criticalCount, warningCount) {
11865
11903
  if (criticalCount > 0) return "not_clear";
11866
11904
  if (warningCount > 0) return "warning";
11867
11905
  return "clear";
11868
11906
  }
11907
+ function gateStatusForGaps(gaps) {
11908
+ return gateStatus(
11909
+ gaps.filter((gap) => gap.severity === "critical").length,
11910
+ gaps.filter((gap) => gap.severity === "warning").length
11911
+ );
11912
+ }
11869
11913
  function runIdFrom(scannedAt) {
11870
11914
  return `vr_${scannedAt.replace(/\D/g, "").slice(0, 14) || "scan"}`;
11871
11915
  }
@@ -11883,7 +11927,7 @@ function generateGateResult(artifact, options = {}) {
11883
11927
  generatedAt: artifact.scannedAt,
11884
11928
  workspace: {
11885
11929
  root: artifact.workspacePath,
11886
- packageManager: "unknown",
11930
+ packageManager: options.packageManager ?? detectPackageManager(artifact.workspacePath),
11887
11931
  languages: ["typescript"],
11888
11932
  frameworks: artifact.archetype ? [artifact.archetype] : []
11889
11933
  },
@@ -11924,101 +11968,246 @@ var init_gateResult = __esm({
11924
11968
  "src/contracts/gateResult.ts"() {
11925
11969
  "use strict";
11926
11970
  init_capabilities();
11971
+ init_packageManager();
11927
11972
  init_commands();
11928
11973
  }
11929
11974
  });
11930
11975
 
11931
- // src/contracts/gapEvidence.ts
11932
- function summarizeGap(gap) {
11933
- return `${gap.title}. See the tasklist and original scan for redacted detail.`;
11976
+ // src/contracts/contextMap.ts
11977
+ function detectedStack(artifact) {
11978
+ const stack = { deployment: [], database: [], auth: [], payments: [], monitoring: [] };
11979
+ const add = (area, provider2) => {
11980
+ if (!(area in stack) || typeof provider2 !== "string" || !provider2) return;
11981
+ const list = stack[area];
11982
+ if (!list.includes(provider2)) list.push(provider2);
11983
+ };
11984
+ for (const entry of providersInRepo(artifact) ?? []) add(entry.area, entry.provider);
11985
+ for (const [area, provider2] of Object.entries(artifact.selectedProviders ?? {})) add(area, provider2);
11986
+ return stack;
11934
11987
  }
11935
- function generateGapEvidenceFiles(artifact) {
11936
- return artifact.gaps.slice(0, 50).map((gap) => ({
11937
- path: `.viberaven/gaps/${gap.id}.json`,
11938
- content: {
11939
- $schema: "https://viberaven.dev/schemas/gap.schema.json",
11940
- schemaVersion: "v1",
11988
+ function generateContextMap(artifact, options = {}) {
11989
+ const criticalCount = artifact.gaps.filter((gap) => gap.severity === "critical").length;
11990
+ const warningCount = artifact.gaps.filter((gap) => gap.severity === "warning").length;
11991
+ return {
11992
+ $schema: "https://viberaven.dev/schemas/context-map.schema.json",
11993
+ schemaVersion: "v1",
11994
+ generatedAt: artifact.scannedAt,
11995
+ workspace: {
11996
+ root: artifact.workspacePath,
11997
+ packageManager: options.packageManager ?? detectPackageManager(artifact.workspacePath),
11998
+ languages: ["typescript"],
11999
+ frameworks: artifact.archetype ? [artifact.archetype] : []
12000
+ },
12001
+ agentContract: {
12002
+ readFirst: ".viberaven/agent-tasklist.md",
12003
+ machineResult: ".viberaven/gate-result.json",
12004
+ contextMap: ".viberaven/context-map.json",
12005
+ verifyCommand: PUBLIC_VERIFY_COMMAND,
12006
+ strictCommand: PUBLIC_STRICT_COMMAND,
12007
+ nextCommand: PUBLIC_NEXT_JSON_COMMAND
12008
+ },
12009
+ stack: detectedStack(artifact),
12010
+ capabilityGraph: summarizeCapabilities(artifact.gaps),
12011
+ productionGate: {
12012
+ status: gateStatusForGaps(artifact.gaps),
12013
+ criticalCount,
12014
+ warningCount,
12015
+ providerDashboardChecksRequired: true
12016
+ },
12017
+ topGaps: artifact.gaps.slice(0, 10).map((gap) => ({
11941
12018
  id: gap.id,
11942
12019
  severity: gap.severity,
11943
- capability: classifyGapCapability(gap),
11944
12020
  title: gap.title,
11945
- summary: summarizeGap(gap),
11946
- evidence: [{ kind: String(gap.primaryMapCategory), redacted: true }],
11947
- commands: {
11948
- prompt: promptGapCommand(gap.id),
11949
- healPlan: healPlanGapCommand(gap.id),
11950
- verify: PUBLIC_VERIFY_COMMAND
11951
- },
11952
- providerBoundary: {
11953
- requiresDashboardCheck: true,
11954
- reason: "Repo evidence cannot prove live provider dashboard state."
11955
- }
11956
- }
11957
- }));
12021
+ area: String(gap.primaryMapCategory),
12022
+ promptCommand: promptGapCommand(gap.id)
12023
+ }))
12024
+ };
11958
12025
  }
11959
- var init_gapEvidence = __esm({
11960
- "src/contracts/gapEvidence.ts"() {
12026
+ var init_contextMap = __esm({
12027
+ "src/contracts/contextMap.ts"() {
11961
12028
  "use strict";
12029
+ init_detectedProviders();
12030
+ init_gateResult();
11962
12031
  init_capabilities();
12032
+ init_packageManager();
11963
12033
  init_commands();
11964
12034
  }
11965
12035
  });
11966
12036
 
11967
- // src/playbooks/checkMap.ts
11968
- function mapCheckToPlaybook(check) {
11969
- if (CHECK_TO_PLAYBOOK[check.id]) {
11970
- return CHECK_TO_PLAYBOOK[check.id];
12037
+ // src/contracts/gapLocations.ts
12038
+ function locationsForGap(gap) {
12039
+ return gap.locations && gap.locations.length > 0 ? gap.locations : gapLocations(gap.detail);
12040
+ }
12041
+ function withoutUrlsAndAddresses(text) {
12042
+ return text.split(/(\s+)/).map((word) => /:\/\/|@|=/.test(word) ? " " : word).join("");
12043
+ }
12044
+ function gapLocations(detail) {
12045
+ if (typeof detail !== "string") return [];
12046
+ const seen = /* @__PURE__ */ new Set();
12047
+ const out = [];
12048
+ for (const rawLine of detail.split(/\r?\n/)) {
12049
+ const text = withoutUrlsAndAddresses(rawLine.split(" Fix:")[0]);
12050
+ for (const match of text.matchAll(LOCATION)) {
12051
+ const folder = match[1].replace(/\\/g, "/");
12052
+ const name = match[2];
12053
+ const file = `${folder}${name}`;
12054
+ const line = match[3] ? Number(match[3]) : void 0;
12055
+ if (line !== void 0 && !(line > 0)) continue;
12056
+ const envFile = name.startsWith(".env");
12057
+ if (!envFile && (VERSION_LIKE.test(name) || VERSION_LIKE.test(name.replace(/\.[A-Za-z0-9]+$/, "")))) continue;
12058
+ const codeFile = CODE_EXTENSION.test(name) && !(folder === "" && PROSE_NAME.test(name));
12059
+ const configFile = CONFIG_EXTENSION.test(name);
12060
+ if (line !== void 0) {
12061
+ if (!envFile && !codeFile && !configFile) continue;
12062
+ } else if (!envFile && !codeFile && !(configFile && folder !== "")) {
12063
+ continue;
12064
+ }
12065
+ const key = `${file}:${line ?? ""}`;
12066
+ if (seen.has(key)) continue;
12067
+ seen.add(key);
12068
+ out.push(line === void 0 ? { file } : { file, line });
12069
+ }
11971
12070
  }
11972
- const providerKey = check.providerKey.toLowerCase();
11973
- if (providerKey.includes("vercel") || check.area === "deployment") {
11974
- return "vercel";
12071
+ return out;
12072
+ }
12073
+ var SEGMENT, NAME, LOCATION, CODE_EXTENSION, CONFIG_EXTENSION, PROSE_NAME, VERSION_LIKE;
12074
+ var init_gapLocations = __esm({
12075
+ "src/contracts/gapLocations.ts"() {
12076
+ "use strict";
12077
+ SEGMENT = String.raw`(?:\([\w.-]+\)|\[[\w.-]+\]|[\w-][\w.-]*)`;
12078
+ NAME = String.raw`(\.env(?:\.[\w-]+)*|(?:\[[\w.-]+\]|[\w-][\w.-]*?)\.[A-Za-z0-9]+)`;
12079
+ LOCATION = new RegExp(
12080
+ String.raw`(?<![\w./\\:-])((?:[A-Za-z]:[\\/])?(?:${SEGMENT}[\\/])*)${NAME}(?::(\d+))?(?!\.?[\w\\/])`,
12081
+ "g"
12082
+ );
12083
+ CODE_EXTENSION = /\.(?:sql|[cm]?[jt]sx?|py|rb|php|go|rs|java|kt|cs|swift|ex|exs|vue|svelte|astro)$/i;
12084
+ CONFIG_EXTENSION = /\.(?:json|toml|ya?ml)$/i;
12085
+ PROSE_NAME = /^(?:next|node|nuxt|vue|express|nest|react|three|chart|solid|ember|backbone|angular|alpine|d3)\.js$/i;
12086
+ VERSION_LIKE = /^v?\d+(?:\.\d+)+$/i;
11975
12087
  }
11976
- if (providerKey.includes("stripe") || check.area === "payments") {
11977
- return "stripe";
12088
+ });
12089
+
12090
+ // src/contracts/repoFixes.ts
12091
+ var APPLY_MIGRATION, ROTATE_KEY, REPO_FIX_GAPS, INFORMATIONAL_GAPS, REPO_ONLY_RECIPE_GAPS, RECIPE_GAPS_WITH_OUTSIDE_STEP, KNOWN_RECIPE_GAP_IDS, REPO_ONLY_GAPS;
12092
+ var init_repoFixes = __esm({
12093
+ "src/contracts/repoFixes.ts"() {
12094
+ "use strict";
12095
+ APPLY_MIGRATION = "Apply the new migration to the live database the way this project applies migrations.";
12096
+ ROTATE_KEY = "Rotate the service role key in the Supabase dashboard if this key was ever deployed or committed with a real value.";
12097
+ REPO_FIX_GAPS = {
12098
+ rls_policy_allows_all_write: [APPLY_MIGRATION],
12099
+ rls_policy_allows_all_read: [APPLY_MIGRATION],
12100
+ data_api_grant_without_rls: [APPLY_MIGRATION],
12101
+ data_api_grant_missing: [APPLY_MIGRATION],
12102
+ security_definer_function_executable: [APPLY_MIGRATION],
12103
+ service_role_key_in_client_env: [ROTATE_KEY],
12104
+ service_role_key_in_client_code: [ROTATE_KEY],
12105
+ env_var_drift: ["Set each of these variables in the environment where the app is deployed."],
12106
+ unverified_stripe_webhook: [
12107
+ "Set STRIPE_WEBHOOK_SECRET where the handler runs to the signing secret of this endpoint in the Stripe dashboard, then deploy the handler."
12108
+ ]
12109
+ };
12110
+ INFORMATIONAL_GAPS = {
12111
+ rls_no_policies: "Informational. Nothing to change if only the server uses this table. If the frontend reads or writes it, add policies in a new migration."
12112
+ };
12113
+ REPO_ONLY_RECIPE_GAPS = [
12114
+ // emptyCatch (original recipe)
12115
+ "empty-catch",
12116
+ "empty_catch",
12117
+ "emptyCatch",
12118
+ // file-create recipes
12119
+ "missing_error_boundary",
12120
+ "missing_health_route",
12121
+ "missing_loading_state",
12122
+ "missing_404_page",
12123
+ // file-patch recipes
12124
+ "missing_csp_header",
12125
+ "eslint_restricted_imports"
12126
+ ];
12127
+ RECIPE_GAPS_WITH_OUTSIDE_STEP = ["auth_secret_missing", "node_env_not_set", "database_url_missing", "missing_rate_limit"];
12128
+ KNOWN_RECIPE_GAP_IDS = /* @__PURE__ */ new Set([...REPO_ONLY_RECIPE_GAPS, ...RECIPE_GAPS_WITH_OUTSIDE_STEP]);
12129
+ REPO_ONLY_GAPS = /* @__PURE__ */ new Set([...Object.keys(INFORMATIONAL_GAPS), ...REPO_ONLY_RECIPE_GAPS]);
11978
12130
  }
11979
- if (providerKey.includes("supabase") && check.area === "auth") {
11980
- return "auth-supabase";
12131
+ });
12132
+
12133
+ // src/heal/rlsMigrationTarget.ts
12134
+ function rlsEnableStatements(tables) {
12135
+ return [...new Set(tables)].map((table2) => `alter table ${table2} enable row level security;`);
12136
+ }
12137
+ function migrationStamp(now) {
12138
+ return now.toISOString().replace(/\D/g, "").slice(0, 14);
12139
+ }
12140
+ function rlsMigrationTarget(cwd, evidence, now = /* @__PURE__ */ new Date()) {
12141
+ if (evidence.length === 0) return { ok: false, reason: "No table without row level security was found in the repo SQL." };
12142
+ const statementList = rlsEnableStatements(evidence.map((entry) => entry.table)).join(" ");
12143
+ const turnedOff = evidence.filter((entry) => entry.turnedOff);
12144
+ if (turnedOff.length > 0) {
12145
+ const where = turnedOff.map((entry) => `${entry.table} (${entry.file.replace(/\\/g, "/")})`).join(", ");
12146
+ return {
12147
+ ok: false,
12148
+ reason: `The latest migration that touches ${where} turns row level security off on purpose, so VibeRaven will not turn it back on automatically. If the user wants it on, add these statements by hand in a new migration, with the policies the app needs: ${statementList}`
12149
+ };
11981
12150
  }
11982
- if (providerKey.includes("supabase") || check.area === "database") {
11983
- return "supabase";
12151
+ const source = evidence[0].file.replace(/\\/g, "/");
12152
+ const dir = import_node_path9.posix.dirname(source);
12153
+ if (import_node_path9.posix.basename(dir) !== "migrations") {
12154
+ return {
12155
+ ok: false,
12156
+ reason: `The tables come from ${source}, which is not in a migrations folder, so VibeRaven will not guess where a migration goes. Add these statements by hand where this project keeps its schema: ${statementList}`
12157
+ };
11984
12158
  }
11985
- if (check.area === "auth") {
11986
- return "auth-supabase";
12159
+ let existing;
12160
+ try {
12161
+ existing = (0, import_node_fs5.readdirSync)((0, import_node_path9.join)(cwd, dir)).filter((file) => file.endsWith(".sql")).sort();
12162
+ } catch {
12163
+ return { ok: false, reason: `VibeRaven could not read ${dir}. Add these statements by hand in a new migration: ${statementList}` };
12164
+ }
12165
+ const otherNaming = existing.find((file) => !TIMESTAMPED_MIGRATION.test(file));
12166
+ if (otherNaming) {
12167
+ return {
12168
+ ok: false,
12169
+ reason: `The migrations in ${dir} are not named <14-digit timestamp>_name.sql (for example ${otherNaming}), so a timestamped file from VibeRaven could run in the wrong order next to them. Add these statements by hand in a new migration that follows this project's naming: ${statementList}`
12170
+ };
12171
+ }
12172
+ const name = `${migrationStamp(now)}_viberaven_enable_rls.sql`;
12173
+ const last = existing[existing.length - 1];
12174
+ if (last && name <= last) {
12175
+ return {
12176
+ ok: false,
12177
+ reason: `A new migration named ${name} would not run after ${last} in ${dir}, so VibeRaven will not write it. Add these statements by hand in a migration that runs last: ${statementList}`
12178
+ };
11987
12179
  }
11988
- return "vercel";
12180
+ return { ok: true, file: `${dir}/${name}` };
11989
12181
  }
11990
- var CHECK_TO_PLAYBOOK;
11991
- var init_checkMap = __esm({
11992
- "src/playbooks/checkMap.ts"() {
12182
+ var import_node_fs5, import_node_path9, RLS_TURNED_OFF_PHRASE, RLS_ASK_FIRST, RLS_NO_POLICIES_WARNING, TIMESTAMPED_MIGRATION;
12183
+ var init_rlsMigrationTarget = __esm({
12184
+ "src/heal/rlsMigrationTarget.ts"() {
11993
12185
  "use strict";
11994
- CHECK_TO_PLAYBOOK = {
11995
- "vercel-deployment": "vercel",
11996
- "vercel-project": "vercel",
11997
- "supabase-project": "supabase",
11998
- "supabase-env": "supabase",
11999
- "stripe-webhook": "stripe",
12000
- "stripe-product": "stripe",
12001
- "stripe-keys": "stripe"
12002
- };
12186
+ import_node_fs5 = require("node:fs");
12187
+ import_node_path9 = require("node:path");
12188
+ RLS_TURNED_OFF_PHRASE = "has row level security turned off at";
12189
+ RLS_ASK_FIRST = "Ask the user first: enabling RLS without policies makes browser reads return no rows until policies exist.";
12190
+ RLS_NO_POLICIES_WARNING = "This migration enables row level security with no policies. Until you add policies, the anon and authenticated roles get no rows from these tables through the Data API, so browser reads return nothing and browser writes fail. `check` then reports the tables only as info (rls_no_policies), not as a blocker. Add the policies the app needs before you apply the migration to the database.";
12191
+ TIMESTAMPED_MIGRATION = /^\d{14}_.+\.sql$/;
12003
12192
  }
12004
12193
  });
12005
12194
 
12006
12195
  // src/playbooks/playbooksRoot.ts
12007
12196
  function playbooksRoot() {
12008
- const candidates = [(0, import_node_path8.join)(__dirname, "playbooks"), (0, import_node_path8.join)(__dirname, "..", "..", "playbooks")];
12197
+ const candidates = [(0, import_node_path10.join)(__dirname, "playbooks"), (0, import_node_path10.join)(__dirname, "..", "..", "playbooks")];
12009
12198
  for (const dir of candidates) {
12010
- if ((0, import_node_fs4.existsSync)((0, import_node_path8.join)(dir, "vercel.json"))) {
12199
+ if ((0, import_node_fs6.existsSync)((0, import_node_path10.join)(dir, "vercel.json"))) {
12011
12200
  return dir;
12012
12201
  }
12013
12202
  }
12014
12203
  throw new Error("VibeRaven playbooks directory not found (expected vercel.json)");
12015
12204
  }
12016
- var import_node_fs4, import_node_path8;
12205
+ var import_node_fs6, import_node_path10;
12017
12206
  var init_playbooksRoot = __esm({
12018
12207
  "src/playbooks/playbooksRoot.ts"() {
12019
12208
  "use strict";
12020
- import_node_fs4 = require("node:fs");
12021
- import_node_path8 = require("node:path");
12209
+ import_node_fs6 = require("node:fs");
12210
+ import_node_path10 = require("node:path");
12022
12211
  }
12023
12212
  });
12024
12213
 
@@ -12125,7 +12314,7 @@ async function loadPlaybook(provider2) {
12125
12314
  `Unknown provider "${provider2}". Available: ${PLAYBOOK_PROVIDERS.join(", ")}`
12126
12315
  );
12127
12316
  }
12128
- const path = (0, import_node_path9.join)(playbooksRoot(), `${normalized}.json`);
12317
+ const path = (0, import_node_path11.join)(playbooksRoot(), `${normalized}.json`);
12129
12318
  const raw = JSON.parse(await (0, import_promises7.readFile)(path, "utf-8"));
12130
12319
  const playbook = parsePlaybook(raw);
12131
12320
  if (playbook.provider !== normalized) {
@@ -12140,59 +12329,30 @@ function loadPlaybookSync(provider2) {
12140
12329
  `Unknown provider "${provider2}". Available: ${PLAYBOOK_PROVIDERS.join(", ")}`
12141
12330
  );
12142
12331
  }
12143
- const path = (0, import_node_path9.join)(playbooksRoot(), `${normalized}.json`);
12144
- const raw = JSON.parse((0, import_node_fs5.readFileSync)(path, "utf-8"));
12332
+ const path = (0, import_node_path11.join)(playbooksRoot(), `${normalized}.json`);
12333
+ const raw = JSON.parse((0, import_node_fs7.readFileSync)(path, "utf-8"));
12145
12334
  const playbook = parsePlaybook(raw);
12146
12335
  if (playbook.provider !== normalized) {
12147
12336
  throw new Error(`Playbook file ${normalized}.json has mismatched provider field`);
12148
12337
  }
12149
12338
  return playbook;
12150
12339
  }
12151
- var import_node_fs5, import_promises7, import_node_path9;
12340
+ var import_node_fs7, import_promises7, import_node_path11;
12152
12341
  var init_loadPlaybook = __esm({
12153
12342
  "src/playbooks/loadPlaybook.ts"() {
12154
12343
  "use strict";
12155
- import_node_fs5 = require("node:fs");
12344
+ import_node_fs7 = require("node:fs");
12156
12345
  import_promises7 = require("node:fs/promises");
12157
- import_node_path9 = require("node:path");
12346
+ import_node_path11 = require("node:path");
12158
12347
  init_playbooksRoot();
12159
12348
  init_types2();
12160
12349
  init_validate();
12161
12350
  }
12162
12351
  });
12163
12352
 
12164
- // src/playbooks/manualChecks.ts
12165
- function isManualProviderCheck(check) {
12166
- return check.evidenceClass === "manual-dashboard" || check.evidenceClass === "mcp-verifier" || check.evidenceSource === "provider" || check.evidenceSource === "mcp" || check.status === "needs-connection" || check.status === "unknown";
12167
- }
12168
- function collectManualChecks(artifact) {
12169
- const refs = [];
12170
- for (const area of artifact.missionGraph.areas ?? []) {
12171
- for (const mission of area.providerMissions) {
12172
- for (const check of mission.checks) {
12173
- if (!isManualProviderCheck(check)) {
12174
- continue;
12175
- }
12176
- refs.push({
12177
- areaLabel: area.label,
12178
- providerLabel: mission.providerLabel,
12179
- check,
12180
- mapCategory: area.key
12181
- });
12182
- }
12183
- }
12184
- }
12185
- return refs;
12186
- }
12187
- var init_manualChecks = __esm({
12188
- "src/playbooks/manualChecks.ts"() {
12189
- "use strict";
12190
- }
12191
- });
12192
-
12193
- // src/tui/menu.ts
12194
- function isScanNotFoundError(error) {
12195
- return error instanceof ScanNotFoundError;
12353
+ // src/tui/menu.ts
12354
+ function isScanNotFoundError(error) {
12355
+ return error instanceof ScanNotFoundError;
12196
12356
  }
12197
12357
  function needsScanMessage(startDir) {
12198
12358
  const cwd = startDir ?? process.cwd();
@@ -12239,7 +12399,7 @@ async function loadLastArtifact(startDir) {
12239
12399
  if (!workspace) {
12240
12400
  throw new ScanNotFoundError(needsScanMessage(startDir));
12241
12401
  }
12242
- const path = (0, import_node_path10.join)(getProjectArtifactsDir(workspace), "last-scan.json");
12402
+ const path = (0, import_node_path12.join)(getProjectArtifactsDir(workspace), "last-scan.json");
12243
12403
  try {
12244
12404
  const raw = await (0, import_promises8.readFile)(path, "utf-8");
12245
12405
  return JSON.parse(raw);
@@ -12247,12 +12407,12 @@ async function loadLastArtifact(startDir) {
12247
12407
  throw new ScanNotFoundError(needsScanMessage(startDir));
12248
12408
  }
12249
12409
  }
12250
- var import_promises8, import_node_path10, SEVERITY_RANK2, ScanNotFoundError;
12410
+ var import_promises8, import_node_path12, SEVERITY_RANK2, ScanNotFoundError;
12251
12411
  var init_menu = __esm({
12252
12412
  "src/tui/menu.ts"() {
12253
12413
  "use strict";
12254
12414
  import_promises8 = require("node:fs/promises");
12255
- import_node_path10 = require("node:path");
12415
+ import_node_path12 = require("node:path");
12256
12416
  init_config();
12257
12417
  SEVERITY_RANK2 = {
12258
12418
  critical: 0,
@@ -12268,228 +12428,6 @@ var init_menu = __esm({
12268
12428
  }
12269
12429
  });
12270
12430
 
12271
- // src/resolveNextAction.ts
12272
- function resolveNextAction(artifact) {
12273
- const repoGap = sortGapsByPriority(artifact.gaps)[0];
12274
- if (repoGap) {
12275
- return {
12276
- type: "repo-fix",
12277
- title: repoGap.title,
12278
- detail: repoGap.detail,
12279
- command: `viberaven prompt --gap ${repoGap.id}`
12280
- };
12281
- }
12282
- const manual = collectManualChecks(artifact)[0];
12283
- if (manual) {
12284
- const provider2 = mapCheckToPlaybook(manual.check);
12285
- let openUrl;
12286
- try {
12287
- const playbook = loadPlaybookSync(provider2);
12288
- openUrl = playbook.steps[0]?.openUrl;
12289
- } catch {
12290
- openUrl = void 0;
12291
- }
12292
- return {
12293
- type: "provider-guide",
12294
- title: manual.check.label,
12295
- detail: manual.check.promptHint || `Configure ${manual.providerLabel} in the provider dashboard (${manual.areaLabel}).`,
12296
- provider: provider2,
12297
- playbookStep: 1,
12298
- command: `viberaven guide ${provider2} --step 1`,
12299
- openUrl
12300
- };
12301
- }
12302
- return {
12303
- type: "done",
12304
- title: "No blockers found",
12305
- detail: "Re-scan after changes to confirm production readiness."
12306
- };
12307
- }
12308
- var init_resolveNextAction = __esm({
12309
- "src/resolveNextAction.ts"() {
12310
- "use strict";
12311
- init_checkMap();
12312
- init_loadPlaybook();
12313
- init_manualChecks();
12314
- init_menu();
12315
- }
12316
- });
12317
-
12318
- // src/report/agentSummary.ts
12319
- function sortGaps(gaps) {
12320
- return [...gaps].sort((a, b3) => {
12321
- const sev = SEVERITY_ORDER2[a.severity] - SEVERITY_ORDER2[b3.severity];
12322
- if (sev !== 0) {
12323
- return sev;
12324
- }
12325
- return a.title.localeCompare(b3.title);
12326
- });
12327
- }
12328
- function generateAgentSummary(artifact) {
12329
- const lines = [];
12330
- const topGaps = sortGaps(artifact.gaps).slice(0, 8);
12331
- lines.push("# VibeRaven agent summary", "");
12332
- lines.push(`Scanned: \`${artifact.workspacePath}\``);
12333
- lines.push(`At: ${artifact.scannedAt}`);
12334
- lines.push(
12335
- `Production core: **${artifact.productionCorePercent}%** \xB7 Model score: **${artifact.score}** (${artifact.scoreLabel})`
12336
- );
12337
- lines.push("");
12338
- const next = resolveNextAction(artifact);
12339
- lines.push("## Next action", "");
12340
- lines.push(`**${next.title}** \u2014 ${next.detail}`);
12341
- if (next.command) {
12342
- lines.push(`Command: \`${next.command}\``);
12343
- }
12344
- lines.push("");
12345
- lines.push("## Suggested stack", "");
12346
- lines.push(
12347
- "React + Tailwind + shadcn/ui + Supabase + Vercel (agent-default stack for lowest launch friction)"
12348
- );
12349
- lines.push("");
12350
- lines.push("## Summary");
12351
- lines.push(artifact.summary || "_No summary returned._");
12352
- lines.push("");
12353
- lines.push("## Mission map (repo wiring)");
12354
- lines.push("");
12355
- lines.push("| Area | Provider | Readiness | Notes |");
12356
- lines.push("|------|----------|-----------|-------|");
12357
- for (const area of artifact.missionGraph.areas ?? []) {
12358
- for (const mission of area.providerMissions) {
12359
- const failed = mission.checks.filter(
12360
- (c) => c.status === "missing" || c.status === "failed" || c.status === "needs-connection"
12361
- ).length;
12362
- const notes = failed > 0 ? `${failed} open check${failed === 1 ? "" : "s"}` : `${mission.readinessPercent}% repo checks`;
12363
- lines.push(
12364
- `| ${area.label} | ${mission.providerLabel} | ${mission.readinessPercent}% | ${notes} |`
12365
- );
12366
- }
12367
- }
12368
- lines.push("");
12369
- lines.push("## Agent-code actions");
12370
- if (topGaps.length === 0) {
12371
- lines.push("_No model gaps returned. Review mission map checks before changing code._");
12372
- } else {
12373
- topGaps.forEach((gap, index) => {
12374
- lines.push(
12375
- `${index + 1}. **${gap.title}** (\`${gap.id}\`, ${gap.severity}, map: \`${gap.primaryMapCategory}\`)`
12376
- );
12377
- lines.push(` - ${gap.detail}`);
12378
- lines.push(` - Command: \`viberaven prompt --gap ${gap.id}\``);
12379
- if (gap.copyPrompt) {
12380
- lines.push(` - Prompt: ${gap.copyPrompt}`);
12381
- }
12382
- });
12383
- }
12384
- const manualChecks = (artifact.missionGraph.areas ?? []).flatMap(
12385
- (area) => area.providerMissions.flatMap(
12386
- (mission) => mission.checks.filter(
12387
- (check) => check.evidenceClass === "manual-dashboard" || check.evidenceClass === "mcp-verifier" || check.evidenceSource === "provider" || check.evidenceSource === "mcp" || check.status === "needs-connection" || check.status === "unknown"
12388
- ).map((check) => ({ area: area.label, provider: mission.providerLabel, check }))
12389
- )
12390
- );
12391
- lines.push("");
12392
- lines.push("## Human-provider actions");
12393
- if (manualChecks.length === 0) {
12394
- lines.push("_No manual provider actions were identified in this scan._");
12395
- } else {
12396
- manualChecks.slice(0, 8).forEach((item4, index) => {
12397
- lines.push(`${index + 1}. **${item4.check.label}** (${item4.area} / ${item4.provider})`);
12398
- lines.push(
12399
- ` - ${item4.check.promptHint || "Ask the user to confirm this in the provider dashboard or through read-only MCP."}`
12400
- );
12401
- });
12402
- }
12403
- lines.push("");
12404
- lines.push("Do not claim human-provider actions as repo-code fixes.");
12405
- lines.push("");
12406
- lines.push("## Agent workflow");
12407
- lines.push("1. Read `.viberaven/agent-tasklist.md` first for the production gate.");
12408
- lines.push("2. Read `.viberaven/launch-playbook.md` for the full checklist.");
12409
- lines.push("3. Run `viberaven next --json` - one action at a time.");
12410
- lines.push("4. Repo fix: `viberaven prompt --gap <id>` then implement.");
12411
- lines.push("5. Provider: `viberaven guide <provider> --step N` and `viberaven open <provider>`.");
12412
- lines.push(`6. Run \`${PUBLIC_VERIFY_COMMAND}\` to rescan and refresh \`.viberaven/agent-tasklist.md\`.`);
12413
- lines.push("");
12414
- return `${lines.join("\n")}
12415
- `;
12416
- }
12417
- var SEVERITY_ORDER2;
12418
- var init_agentSummary = __esm({
12419
- "src/report/agentSummary.ts"() {
12420
- "use strict";
12421
- init_commands();
12422
- init_resolveNextAction();
12423
- SEVERITY_ORDER2 = {
12424
- critical: 0,
12425
- warning: 1,
12426
- info: 2
12427
- };
12428
- }
12429
- });
12430
-
12431
- // src/heal/rlsMigrationTarget.ts
12432
- function rlsEnableStatements(tables) {
12433
- return [...new Set(tables)].map((table2) => `alter table ${table2} enable row level security;`);
12434
- }
12435
- function migrationStamp(now) {
12436
- return now.toISOString().replace(/\D/g, "").slice(0, 14);
12437
- }
12438
- function rlsMigrationTarget(cwd, evidence, now = /* @__PURE__ */ new Date()) {
12439
- if (evidence.length === 0) return { ok: false, reason: "No table without row level security was found in the repo SQL." };
12440
- const statementList = rlsEnableStatements(evidence.map((entry) => entry.table)).join(" ");
12441
- const turnedOff = evidence.filter((entry) => entry.turnedOff);
12442
- if (turnedOff.length > 0) {
12443
- const where = turnedOff.map((entry) => `${entry.table} (${entry.file.replace(/\\/g, "/")})`).join(", ");
12444
- return {
12445
- ok: false,
12446
- reason: `The latest migration that touches ${where} turns row level security off on purpose, so VibeRaven will not turn it back on automatically. If the user wants it on, add these statements by hand in a new migration, with the policies the app needs: ${statementList}`
12447
- };
12448
- }
12449
- const source = evidence[0].file.replace(/\\/g, "/");
12450
- const dir = import_node_path11.posix.dirname(source);
12451
- if (import_node_path11.posix.basename(dir) !== "migrations") {
12452
- return {
12453
- ok: false,
12454
- reason: `The tables come from ${source}, which is not in a migrations folder, so VibeRaven will not guess where a migration goes. Add these statements by hand where this project keeps its schema: ${statementList}`
12455
- };
12456
- }
12457
- let existing;
12458
- try {
12459
- existing = (0, import_node_fs6.readdirSync)((0, import_node_path11.join)(cwd, dir)).filter((file) => file.endsWith(".sql")).sort();
12460
- } catch {
12461
- return { ok: false, reason: `VibeRaven could not read ${dir}. Add these statements by hand in a new migration: ${statementList}` };
12462
- }
12463
- const otherNaming = existing.find((file) => !TIMESTAMPED_MIGRATION.test(file));
12464
- if (otherNaming) {
12465
- return {
12466
- ok: false,
12467
- reason: `The migrations in ${dir} are not named <14-digit timestamp>_name.sql (for example ${otherNaming}), so a timestamped file from VibeRaven could run in the wrong order next to them. Add these statements by hand in a new migration that follows this project's naming: ${statementList}`
12468
- };
12469
- }
12470
- const name = `${migrationStamp(now)}_viberaven_enable_rls.sql`;
12471
- const last = existing[existing.length - 1];
12472
- if (last && name <= last) {
12473
- return {
12474
- ok: false,
12475
- reason: `A new migration named ${name} would not run after ${last} in ${dir}, so VibeRaven will not write it. Add these statements by hand in a migration that runs last: ${statementList}`
12476
- };
12477
- }
12478
- return { ok: true, file: `${dir}/${name}` };
12479
- }
12480
- var import_node_fs6, import_node_path11, RLS_TURNED_OFF_PHRASE, RLS_ASK_FIRST, RLS_NO_POLICIES_WARNING, TIMESTAMPED_MIGRATION;
12481
- var init_rlsMigrationTarget = __esm({
12482
- "src/heal/rlsMigrationTarget.ts"() {
12483
- "use strict";
12484
- import_node_fs6 = require("node:fs");
12485
- import_node_path11 = require("node:path");
12486
- RLS_TURNED_OFF_PHRASE = "has row level security turned off at";
12487
- RLS_ASK_FIRST = "Ask the user first: enabling RLS without policies makes browser reads return no rows until policies exist.";
12488
- RLS_NO_POLICIES_WARNING = "This migration enables row level security with no policies. Until you add policies, the anon and authenticated roles get no rows from these tables through the Data API, so browser reads return nothing and browser writes fail. `check` then reports the tables only as info (rls_no_policies), not as a blocker. Add the policies the app needs before you apply the migration to the database.";
12489
- TIMESTAMPED_MIGRATION = /^\d{14}_.+\.sql$/;
12490
- }
12491
- });
12492
-
12493
12431
  // src/buildTaskList.ts
12494
12432
  function rlsMigrationFile(gap) {
12495
12433
  if (gap.id !== "rls_disabled" || typeof gap.detail !== "string") return void 0;
@@ -12508,7 +12446,12 @@ function hasRecipe(gapId) {
12508
12446
  const lower = gapId.toLowerCase();
12509
12447
  return lower.includes("empty") && lower.includes("catch");
12510
12448
  }
12511
- function gapToPlaybookProvider(gap) {
12449
+ function gapToPlaybookProvider(gap, inRepo) {
12450
+ const provider2 = playbookByCategoryOrId(gap);
12451
+ if (!provider2 || !inRepo) return provider2;
12452
+ return inRepo.has(PLAYBOOK_TOKEN[provider2] ?? provider2) ? provider2 : void 0;
12453
+ }
12454
+ function playbookByCategoryOrId(gap) {
12512
12455
  const cat = gap.primaryMapCategory.toLowerCase();
12513
12456
  if (cat === "deployment") return "vercel";
12514
12457
  if (cat === "payments") return "stripe";
@@ -12521,9 +12464,9 @@ function gapToPlaybookProvider(gap) {
12521
12464
  if (id.includes("auth")) return "auth-supabase";
12522
12465
  return void 0;
12523
12466
  }
12524
- function hasPlaybook(gap) {
12467
+ function hasPlaybook(gap, inRepo) {
12525
12468
  try {
12526
- const provider2 = gapToPlaybookProvider(gap);
12469
+ const provider2 = gapToPlaybookProvider(gap, inRepo);
12527
12470
  if (!provider2) return false;
12528
12471
  return PLAYBOOK_PROVIDERS.includes(provider2);
12529
12472
  } catch {
@@ -12553,13 +12496,20 @@ function buildProviderAction(gap, provider2) {
12553
12496
  }
12554
12497
  function buildTaskList(artifact) {
12555
12498
  const sorted = sortGapsByPriority(artifact.gaps);
12499
+ const providers = providersInRepo(artifact);
12500
+ const inRepo = providers ? new Set(providers.map((entry) => entry.provider.toLowerCase())) : void 0;
12556
12501
  return sorted.map((gap, index) => {
12557
12502
  const id = `TASK-${String(index + 1).padStart(3, "0")}`;
12558
12503
  let fixType;
12559
12504
  const migrationFile = rlsMigrationFile(gap);
12560
- if (hasRecipe(gap.id) || migrationFile) {
12505
+ const location = locationsForGap(gap)[0];
12506
+ const repoFixUserSteps = REPO_FIX_GAPS[gap.id];
12507
+ const informational = INFORMATIONAL_GAPS[gap.id];
12508
+ if (hasRecipe(gap.id) || migrationFile || repoFixUserSteps) {
12561
12509
  fixType = "repo-code";
12562
- } else if (hasPlaybook(gap)) {
12510
+ } else if (informational) {
12511
+ fixType = "manual-verify";
12512
+ } else if (hasPlaybook(gap, inRepo)) {
12563
12513
  fixType = "provider-action";
12564
12514
  } else {
12565
12515
  fixType = "manual-verify";
@@ -12576,6 +12526,8 @@ function buildTaskList(artifact) {
12576
12526
  if (fixType === "repo-code" && !hasRecipe(gap.id) && migrationFile) {
12577
12527
  base.requiresUserAction = true;
12578
12528
  base.file = migrationFile;
12529
+ base.userSteps = [APPLY_MIGRATION];
12530
+ if (location?.file === migrationFile && location.line !== void 0) base.line = location.line;
12579
12531
  const target = rlsMigrationTarget(artifact.workspacePath, rlsEvidence2(gap.detail));
12580
12532
  if (target.ok) {
12581
12533
  base.mcpTool = "viberaven_heal_apply";
@@ -12587,6 +12539,28 @@ function buildTaskList(artifact) {
12587
12539
  if (gap.copyPrompt) {
12588
12540
  base.exactFix = gap.copyPrompt.trim();
12589
12541
  }
12542
+ } else if (fixType === "repo-code" && !hasRecipe(gap.id) && repoFixUserSteps) {
12543
+ base.requiresUserAction = false;
12544
+ if (location) {
12545
+ base.file = location.file;
12546
+ if (location.line !== void 0) base.line = location.line;
12547
+ } else {
12548
+ base.action = "Edit the files named in the gap detail (`npx -y viberaven prompt --gap " + gap.id + "`).";
12549
+ }
12550
+ if (gap.copyPrompt) {
12551
+ base.exactFix = gap.copyPrompt.trim();
12552
+ }
12553
+ base.userSteps = [...repoFixUserSteps];
12554
+ } else if (fixType === "manual-verify" && informational) {
12555
+ base.requiresUserAction = false;
12556
+ base.action = informational;
12557
+ if (location) {
12558
+ base.file = location.file;
12559
+ if (location.line !== void 0) base.line = location.line;
12560
+ }
12561
+ if (gap.copyPrompt) {
12562
+ base.exactFix = gap.copyPrompt.trim();
12563
+ }
12590
12564
  } else if (fixType === "repo-code") {
12591
12565
  base.mcpTool = "viberaven_heal_apply";
12592
12566
  base.mcpArgs = { gap: gap.id, yes: true };
@@ -12596,7 +12570,7 @@ function buildTaskList(artifact) {
12596
12570
  }
12597
12571
  } else if (fixType === "provider-action") {
12598
12572
  try {
12599
- const provider2 = gapToPlaybookProvider(gap);
12573
+ const provider2 = gapToPlaybookProvider(gap, inRepo);
12600
12574
  if (provider2) {
12601
12575
  const pa = buildProviderAction(gap, provider2);
12602
12576
  if (pa) {
@@ -12614,7 +12588,7 @@ function buildTaskList(artifact) {
12614
12588
  }
12615
12589
  function buildTaskListMarkdown(tasks) {
12616
12590
  if (tasks.length === 0) {
12617
- return "# VibeRaven Agent Tasklist\n\n_No gaps found \u2014 production core looks solid._\n";
12591
+ return "# VibeRaven Agent Tasklist\n\n_No gaps found in this scan._\n";
12618
12592
  }
12619
12593
  const lines = ["# VibeRaven Agent Tasklist", ""];
12620
12594
  for (const task of tasks) {
@@ -12622,7 +12596,7 @@ function buildTaskListMarkdown(tasks) {
12622
12596
  lines.push(`## ${task.id} \xB7 ${task.gapId} \xB7 ${severityLabel}`, "");
12623
12597
  lines.push(`**Fix type:** ${task.fixType} `);
12624
12598
  if (task.file) {
12625
- lines.push(`**File:** \`${task.file}\` `);
12599
+ lines.push(`**File:** \`${task.file}${task.line !== void 0 ? `:${task.line}` : ""}\` `);
12626
12600
  }
12627
12601
  if (task.action) {
12628
12602
  lines.push(`**Action:** ${task.action} `);
@@ -12630,62 +12604,387 @@ function buildTaskListMarkdown(tasks) {
12630
12604
  if (task.exactFix) {
12631
12605
  lines.push(`**Exact fix:** ${task.exactFix} `);
12632
12606
  } else {
12633
- lines.push(`**Exact fix:** No automated recipe \u2014 see scanner hint. `);
12607
+ lines.push(`**Exact fix:** No automated recipe; see the scanner hint in the gap detail. `);
12634
12608
  }
12635
12609
  lines.push(`**Verify:** \`${task.verifyCommand}\` `);
12636
12610
  if (task.mcpTool && task.mcpArgs) {
12637
12611
  lines.push(
12638
- `**MCP:** \`${task.mcpTool} ${JSON.stringify(task.mcpArgs)}\` `
12612
+ `**MCP:** \`${task.mcpTool} ${JSON.stringify(task.mcpArgs)}\` `
12613
+ );
12614
+ }
12615
+ lines.push(`**Requires user action:** ${task.requiresUserAction}`);
12616
+ if (task.userSteps && task.userSteps.length > 0) {
12617
+ lines.push("");
12618
+ lines.push("**Outside the repo (needs the user):**");
12619
+ for (const step of task.userSteps) lines.push(`- ${step}`);
12620
+ }
12621
+ if (task.providerAction) {
12622
+ const pa = task.providerAction;
12623
+ lines.push("");
12624
+ lines.push("**Provider action:**");
12625
+ lines.push(`- Provider: ${pa.provider}`);
12626
+ lines.push(`- Dashboard: ${pa.dashboardUrl}`);
12627
+ lines.push(`- Step: ${pa.exactStep}`);
12628
+ if (pa.envKeyName) lines.push(`- Env key: \`${pa.envKeyName}\``);
12629
+ if (pa.doneSignal) lines.push(`- Done when: ${pa.doneSignal}`);
12630
+ if (pa.mcpAlternative) lines.push(`- MCP alternative: \`${pa.mcpAlternative}\``);
12631
+ }
12632
+ lines.push("");
12633
+ }
12634
+ return `${lines.join("\n")}
12635
+ `;
12636
+ }
12637
+ var PLAYBOOK_TOKEN;
12638
+ var init_buildTaskList = __esm({
12639
+ "src/buildTaskList.ts"() {
12640
+ "use strict";
12641
+ init_commands();
12642
+ init_rlsMigrationTarget();
12643
+ init_gapLocations();
12644
+ init_repoFixes();
12645
+ init_detectedProviders();
12646
+ init_loadPlaybook();
12647
+ init_types2();
12648
+ init_menu();
12649
+ PLAYBOOK_TOKEN = { vercel: "vercel", stripe: "stripe", supabase: "supabase", "auth-supabase": "supabase" };
12650
+ }
12651
+ });
12652
+
12653
+ // src/contracts/gapEvidence.ts
12654
+ function summarizeGap(gap) {
12655
+ return `${gap.title}. See the tasklist and original scan for redacted detail.`;
12656
+ }
12657
+ function evidenceFor2(gap) {
12658
+ const kind = String(gap.primaryMapCategory);
12659
+ const located = locationsForGap(gap).map((location) => ({ kind, ...location, redacted: true }));
12660
+ return located.length > 0 ? located : [{ kind, redacted: true }];
12661
+ }
12662
+ function userStepsFor(gap) {
12663
+ if (REPO_FIX_GAPS[gap.id]) return { userSteps: [...REPO_FIX_GAPS[gap.id]] };
12664
+ if (rlsMigrationFile(gap)) return { userSteps: [APPLY_MIGRATION] };
12665
+ return {};
12666
+ }
12667
+ function generateGapEvidenceFiles(artifact) {
12668
+ return artifact.gaps.slice(0, 50).map((gap) => ({
12669
+ path: `.viberaven/gaps/${gap.id}.json`,
12670
+ content: {
12671
+ $schema: "https://viberaven.dev/schemas/gap.schema.json",
12672
+ schemaVersion: "v1",
12673
+ id: gap.id,
12674
+ severity: gap.severity,
12675
+ capability: classifyGapCapability(gap),
12676
+ title: gap.title,
12677
+ summary: summarizeGap(gap),
12678
+ evidence: evidenceFor2(gap),
12679
+ commands: {
12680
+ prompt: promptGapCommand(gap.id),
12681
+ healPlan: healPlanGapCommand(gap.id),
12682
+ verify: PUBLIC_VERIFY_COMMAND
12683
+ },
12684
+ ...userStepsFor(gap),
12685
+ providerBoundary: REPO_ONLY_GAPS.has(gap.id) ? { requiresDashboardCheck: false, reason: "The fix is a repo change; no provider dashboard state is involved." } : { requiresDashboardCheck: true, reason: "Repo evidence cannot prove live provider dashboard state." }
12686
+ }
12687
+ }));
12688
+ }
12689
+ var init_gapEvidence = __esm({
12690
+ "src/contracts/gapEvidence.ts"() {
12691
+ "use strict";
12692
+ init_capabilities();
12693
+ init_gapLocations();
12694
+ init_repoFixes();
12695
+ init_buildTaskList();
12696
+ init_commands();
12697
+ }
12698
+ });
12699
+
12700
+ // src/playbooks/checkMap.ts
12701
+ function mapCheckToPlaybook(check) {
12702
+ if (CHECK_TO_PLAYBOOK[check.id]) {
12703
+ return CHECK_TO_PLAYBOOK[check.id];
12704
+ }
12705
+ const providerKey = String(check.providerKey ?? "").toLowerCase();
12706
+ if (providerKey.includes("vercel")) {
12707
+ return "vercel";
12708
+ }
12709
+ if (providerKey.includes("stripe")) {
12710
+ return "stripe";
12711
+ }
12712
+ if (providerKey.includes("supabase")) {
12713
+ return check.area === "auth" ? "auth-supabase" : "supabase";
12714
+ }
12715
+ return void 0;
12716
+ }
12717
+ var CHECK_TO_PLAYBOOK;
12718
+ var init_checkMap = __esm({
12719
+ "src/playbooks/checkMap.ts"() {
12720
+ "use strict";
12721
+ CHECK_TO_PLAYBOOK = {
12722
+ "vercel-deployment": "vercel",
12723
+ "vercel-project": "vercel",
12724
+ "supabase-project": "supabase",
12725
+ "supabase-env": "supabase",
12726
+ "stripe-webhook": "stripe",
12727
+ "stripe-product": "stripe",
12728
+ "stripe-keys": "stripe"
12729
+ };
12730
+ }
12731
+ });
12732
+
12733
+ // src/report/missionSelection.ts
12734
+ function normalizeProviderToken2(value) {
12735
+ return value.toLowerCase().replace(/&/g, "and").replace(/[^a-z0-9]+/g, "");
12736
+ }
12737
+ function missionMatchesProvider(mission, provider2) {
12738
+ const current = normalizeProviderToken2(provider2);
12739
+ return normalizeProviderToken2(mission.provider || mission.providerLabel || mission.key) === current || normalizeProviderToken2(mission.providerLabel || mission.provider || mission.key) === current;
12740
+ }
12741
+ function missionEvidenceScore(mission) {
12742
+ const repoVerified = mission.checks.filter((check) => check.evidenceClass === "repo-verified" || check.status === "passed").length;
12743
+ const missing = mission.checks.filter(
12744
+ (check) => check.evidenceClass === "missing-repo-fix" || check.status === "missing" || check.status === "failed"
12745
+ ).length;
12746
+ return repoVerified * 100 + (mission.readinessPercent ?? 0) - missing;
12747
+ }
12748
+ function openChecksForMission(mission) {
12749
+ if (!mission) {
12750
+ return 0;
12751
+ }
12752
+ return mission.checks.filter(
12753
+ (check) => check.status === "missing" || check.status === "failed" || check.status === "needs-connection"
12754
+ ).length;
12755
+ }
12756
+ var init_missionSelection = __esm({
12757
+ "src/report/missionSelection.ts"() {
12758
+ "use strict";
12759
+ }
12760
+ });
12761
+
12762
+ // src/report/inRepoMissions.ts
12763
+ function detectedProviderTokens(artifact) {
12764
+ const providers = providersInRepo(artifact);
12765
+ if (!providers) return void 0;
12766
+ return new Set(providers.map((entry) => normalizeProviderToken2(entry.provider)));
12767
+ }
12768
+ function missionInRepo(mission, detected) {
12769
+ const provider2 = String(mission.provider || mission.key || "");
12770
+ if (detected.has(normalizeProviderToken2(provider2))) return true;
12771
+ if (PRACTICES.has(provider2)) return true;
12772
+ const identity = mission.checks.find((check) => IDENTITY_CHECK.test(check.id));
12773
+ return identity?.status === "passed";
12774
+ }
12775
+ function inRepoMissions(artifact, area) {
12776
+ const missions = area.providerMissions ?? [];
12777
+ const detected = detectedProviderTokens(artifact);
12778
+ if (!detected) return missions;
12779
+ const selected = artifact.selectedProviders?.[area.key] ?? "";
12780
+ return missions.filter((mission) => missionInRepo(mission, detected) || selected && missionMatchesProvider(mission, selected));
12781
+ }
12782
+ function preferredInRepoMission(artifact, area) {
12783
+ const missions = inRepoMissions(artifact, area);
12784
+ if (missions.length === 0) return void 0;
12785
+ const selected = artifact.selectedProviders?.[area.key] ?? "";
12786
+ const picked = selected ? missions.find((mission) => missionMatchesProvider(mission, selected)) : void 0;
12787
+ return picked ?? [...missions].sort((a, b3) => missionEvidenceScore(b3) - missionEvidenceScore(a))[0];
12788
+ }
12789
+ var IDENTITY_CHECK, PRACTICES, NONE_FOUND;
12790
+ var init_inRepoMissions = __esm({
12791
+ "src/report/inRepoMissions.ts"() {
12792
+ "use strict";
12793
+ init_detectedProviders();
12794
+ init_missionSelection();
12795
+ IDENTITY_CHECK = /^(?:package-installed|package-or-config-found|package-or-widget-found|api-client-found|storybook-package-found|server-runtime-found|rails-runtime-found|go-runtime-found)$/;
12796
+ PRACTICES = /* @__PURE__ */ new Set(["route-map", "product-spec", "secrets-hygiene"]);
12797
+ NONE_FOUND = "none found";
12798
+ }
12799
+ });
12800
+
12801
+ // src/playbooks/manualChecks.ts
12802
+ function isManualProviderCheck(check) {
12803
+ return check.evidenceClass === "manual-dashboard" || check.evidenceClass === "mcp-verifier" || check.evidenceSource === "provider" || check.evidenceSource === "mcp" || check.status === "needs-connection" || check.status === "unknown";
12804
+ }
12805
+ function collectManualChecks(artifact) {
12806
+ const refs = [];
12807
+ for (const area of artifact.missionGraph.areas ?? []) {
12808
+ for (const mission of inRepoMissions(artifact, area)) {
12809
+ for (const check of mission.checks) {
12810
+ if (!isManualProviderCheck(check)) {
12811
+ continue;
12812
+ }
12813
+ refs.push({
12814
+ areaLabel: area.label,
12815
+ providerLabel: mission.providerLabel,
12816
+ check,
12817
+ mapCategory: area.key
12818
+ });
12819
+ }
12820
+ }
12821
+ }
12822
+ return refs;
12823
+ }
12824
+ var init_manualChecks = __esm({
12825
+ "src/playbooks/manualChecks.ts"() {
12826
+ "use strict";
12827
+ init_inRepoMissions();
12828
+ }
12829
+ });
12830
+
12831
+ // src/resolveNextAction.ts
12832
+ function resolveNextAction(artifact) {
12833
+ const repoGap = sortGapsByPriority(artifact.gaps)[0];
12834
+ if (repoGap) {
12835
+ return {
12836
+ type: "repo-fix",
12837
+ title: repoGap.title,
12838
+ detail: repoGap.detail,
12839
+ command: `viberaven prompt --gap ${repoGap.id}`
12840
+ };
12841
+ }
12842
+ const manual = collectManualChecks(artifact)[0];
12843
+ if (manual) {
12844
+ const provider2 = mapCheckToPlaybook(manual.check);
12845
+ let openUrl;
12846
+ try {
12847
+ openUrl = provider2 ? loadPlaybookSync(provider2).steps[0]?.openUrl : void 0;
12848
+ } catch {
12849
+ openUrl = void 0;
12850
+ }
12851
+ return {
12852
+ type: "provider-guide",
12853
+ title: manual.check.label,
12854
+ detail: manual.check.promptHint || `Configure ${manual.providerLabel} in the provider dashboard (${manual.areaLabel}).`,
12855
+ ...provider2 ? { provider: provider2, playbookStep: 1, command: `viberaven guide ${provider2} --step 1` } : {},
12856
+ openUrl
12857
+ };
12858
+ }
12859
+ return {
12860
+ type: "done",
12861
+ title: "No blockers found",
12862
+ detail: "Re-scan after changes to confirm production readiness."
12863
+ };
12864
+ }
12865
+ var init_resolveNextAction = __esm({
12866
+ "src/resolveNextAction.ts"() {
12867
+ "use strict";
12868
+ init_checkMap();
12869
+ init_loadPlaybook();
12870
+ init_manualChecks();
12871
+ init_menu();
12872
+ }
12873
+ });
12874
+
12875
+ // src/report/agentSummary.ts
12876
+ function sortGaps(gaps) {
12877
+ return [...gaps].sort((a, b3) => {
12878
+ const sev = SEVERITY_ORDER2[a.severity] - SEVERITY_ORDER2[b3.severity];
12879
+ if (sev !== 0) {
12880
+ return sev;
12881
+ }
12882
+ return a.title.localeCompare(b3.title);
12883
+ });
12884
+ }
12885
+ function generateAgentSummary(artifact) {
12886
+ const lines = [];
12887
+ const topGaps = sortGaps(artifact.gaps).slice(0, 8);
12888
+ lines.push("# VibeRaven agent summary", "");
12889
+ lines.push(`Scanned: \`${artifact.workspacePath}\``);
12890
+ lines.push(`At: ${artifact.scannedAt}`);
12891
+ lines.push(
12892
+ `Production core: **${artifact.productionCorePercent}%** \xB7 Model score: **${artifact.score}** (${artifact.scoreLabel})`
12893
+ );
12894
+ lines.push("");
12895
+ const next = resolveNextAction(artifact);
12896
+ lines.push("## Next action", "");
12897
+ lines.push(`**${next.title}**: ${next.detail}`);
12898
+ if (next.command) {
12899
+ lines.push(`Command: \`${next.command}\``);
12900
+ }
12901
+ lines.push("");
12902
+ lines.push("## Stack", "");
12903
+ lines.push("The mission map below lists the providers detected in this repo. For a suggestion, run `npx -y viberaven stack recommend`.");
12904
+ lines.push("");
12905
+ lines.push("## Summary");
12906
+ lines.push(artifact.summary || "_No summary returned._");
12907
+ lines.push("");
12908
+ lines.push("## Mission map (repo wiring)");
12909
+ lines.push("");
12910
+ lines.push("| Area | Provider | Readiness | Notes |");
12911
+ lines.push("|------|----------|-----------|-------|");
12912
+ for (const area of artifact.missionGraph.areas ?? []) {
12913
+ const missions = inRepoMissions(artifact, area);
12914
+ if (missions.length === 0 && (area.providerMissions ?? []).length > 0) {
12915
+ lines.push(`| ${area.label} | ${NONE_FOUND} | - | - |`);
12916
+ }
12917
+ for (const mission of missions) {
12918
+ const failed = mission.checks.filter(
12919
+ (c) => c.status === "missing" || c.status === "failed" || c.status === "needs-connection"
12920
+ ).length;
12921
+ const notes = failed > 0 ? `${failed} open check${failed === 1 ? "" : "s"}` : `${mission.readinessPercent}% repo checks`;
12922
+ lines.push(
12923
+ `| ${area.label} | ${mission.providerLabel} | ${mission.readinessPercent}% | ${notes} |`
12924
+ );
12925
+ }
12926
+ }
12927
+ lines.push("");
12928
+ lines.push("## Agent-code actions");
12929
+ if (topGaps.length === 0) {
12930
+ lines.push("_No model gaps returned. Review mission map checks before changing code._");
12931
+ } else {
12932
+ topGaps.forEach((gap, index) => {
12933
+ lines.push(
12934
+ `${index + 1}. **${gap.title}** (\`${gap.id}\`, ${gap.severity}, map: \`${gap.primaryMapCategory}\`)`
12639
12935
  );
12640
- }
12641
- lines.push(`**Requires user action:** ${task.requiresUserAction}`);
12642
- if (task.providerAction) {
12643
- const pa = task.providerAction;
12644
- lines.push("");
12645
- lines.push("**Provider action:**");
12646
- lines.push(`- Provider: ${pa.provider}`);
12647
- lines.push(`- Dashboard: ${pa.dashboardUrl}`);
12648
- lines.push(`- Step: ${pa.exactStep}`);
12649
- if (pa.envKeyName) lines.push(`- Env key: \`${pa.envKeyName}\``);
12650
- if (pa.doneSignal) lines.push(`- Done when: ${pa.doneSignal}`);
12651
- if (pa.mcpAlternative) lines.push(`- MCP alternative: \`${pa.mcpAlternative}\``);
12652
- }
12653
- lines.push("");
12936
+ lines.push(` - ${gap.detail}`);
12937
+ lines.push(` - Command: \`viberaven prompt --gap ${gap.id}\``);
12938
+ if (gap.copyPrompt) {
12939
+ lines.push(` - Prompt: ${gap.copyPrompt}`);
12940
+ }
12941
+ });
12942
+ }
12943
+ const manualChecks = (artifact.missionGraph.areas ?? []).flatMap(
12944
+ (area) => inRepoMissions(artifact, area).flatMap(
12945
+ (mission) => mission.checks.filter(
12946
+ (check) => check.evidenceClass === "manual-dashboard" || check.evidenceClass === "mcp-verifier" || check.evidenceSource === "provider" || check.evidenceSource === "mcp" || check.status === "needs-connection" || check.status === "unknown"
12947
+ ).map((check) => ({ area: area.label, provider: mission.providerLabel, check }))
12948
+ )
12949
+ );
12950
+ lines.push("");
12951
+ lines.push("## Human-provider actions");
12952
+ if (manualChecks.length === 0) {
12953
+ lines.push("_No manual provider actions were identified in this scan._");
12954
+ } else {
12955
+ manualChecks.slice(0, 8).forEach((item4, index) => {
12956
+ lines.push(`${index + 1}. **${item4.check.label}** (${item4.area} / ${item4.provider})`);
12957
+ lines.push(
12958
+ ` - ${item4.check.promptHint || "Ask the user to confirm this in the provider dashboard or through read-only MCP."}`
12959
+ );
12960
+ });
12654
12961
  }
12962
+ lines.push("");
12963
+ lines.push("Do not claim human-provider actions as repo-code fixes.");
12964
+ lines.push("");
12965
+ lines.push("## Agent workflow");
12966
+ lines.push("1. Read `.viberaven/agent-tasklist.md` first for the production gate.");
12967
+ lines.push("2. Read `.viberaven/launch-playbook.md` for the full checklist.");
12968
+ lines.push("3. Run `viberaven next --json` - one action at a time.");
12969
+ lines.push("4. Repo fix: `viberaven prompt --gap <id>` then implement.");
12970
+ lines.push("5. Provider: `viberaven guide <provider> --step N` and `viberaven open <provider>`.");
12971
+ lines.push(`6. Run \`${PUBLIC_VERIFY_COMMAND}\` to rescan and refresh \`.viberaven/agent-tasklist.md\`.`);
12972
+ lines.push("");
12655
12973
  return `${lines.join("\n")}
12656
12974
  `;
12657
12975
  }
12658
- var KNOWN_RECIPE_GAP_IDS;
12659
- var init_buildTaskList = __esm({
12660
- "src/buildTaskList.ts"() {
12976
+ var SEVERITY_ORDER2;
12977
+ var init_agentSummary = __esm({
12978
+ "src/report/agentSummary.ts"() {
12661
12979
  "use strict";
12662
12980
  init_commands();
12663
- init_rlsMigrationTarget();
12664
- init_loadPlaybook();
12665
- init_types2();
12666
- init_menu();
12667
- KNOWN_RECIPE_GAP_IDS = /* @__PURE__ */ new Set([
12668
- // emptyCatch (original recipe)
12669
- "empty-catch",
12670
- "empty_catch",
12671
- "emptyCatch",
12672
- // W3 env-add recipes
12673
- "auth_secret_missing",
12674
- "node_env_not_set",
12675
- "database_url_missing",
12676
- // W3 file-create recipes
12677
- "missing_error_boundary",
12678
- "missing_health_route",
12679
- "missing_loading_state",
12680
- "missing_404_page",
12681
- // W3 file-patch recipes
12682
- "missing_csp_header",
12683
- "missing_rate_limit",
12684
- "eslint_restricted_imports"
12685
- // NOTE: 'rls_disabled' is intentionally NOT here. When the finding comes from repo SQL it is a repo action whose
12686
- // recipe writes a new migration (see rlsMigrationFile and heal/recipes/supabaseRls.ts); without SQL evidence it
12687
- // stays a provider action and heal refuses.
12688
- ]);
12981
+ init_resolveNextAction();
12982
+ init_inRepoMissions();
12983
+ SEVERITY_ORDER2 = {
12984
+ critical: 0,
12985
+ warning: 1,
12986
+ info: 2
12987
+ };
12689
12988
  }
12690
12989
  });
12691
12990
 
@@ -12695,12 +12994,21 @@ function generateLaunchPlaybook(artifact) {
12695
12994
  lines.push("# VibeRaven launch playbook", "");
12696
12995
  lines.push(`Generated from scan at ${artifact.scannedAt}. Work top to bottom.`, "");
12697
12996
  lines.push("## Repo fixes (agent code)", "");
12698
- const repoGaps = sortGapsByPriority(artifact.gaps);
12997
+ const sorted = sortGapsByPriority(artifact.gaps);
12998
+ const repoGaps = sorted.filter((gap) => !INFORMATIONAL_GAPS[gap.id]);
12999
+ const informational = sorted.filter((gap) => INFORMATIONAL_GAPS[gap.id]);
12699
13000
  if (repoGaps.length === 0) {
12700
13001
  lines.push("_No repo-code gaps found._", "");
12701
13002
  } else {
12702
13003
  for (const gap of repoGaps) {
12703
- lines.push(`- [ ] ${gap.title} \u2014 \`viberaven prompt --gap ${gap.id}\``);
13004
+ lines.push(`- [ ] ${gap.title}: \`viberaven prompt --gap ${gap.id}\``);
13005
+ }
13006
+ lines.push("");
13007
+ }
13008
+ if (informational.length > 0) {
13009
+ lines.push("## Informational", "");
13010
+ for (const gap of informational) {
13011
+ lines.push(`- ${gap.title} (\`${gap.id}\`): ${INFORMATIONAL_GAPS[gap.id]}`);
12704
13012
  }
12705
13013
  lines.push("");
12706
13014
  }
@@ -12712,7 +13020,7 @@ function generateLaunchPlaybook(artifact) {
12712
13020
  for (const item4 of manuals) {
12713
13021
  const provider2 = mapCheckToPlaybook(item4.check);
12714
13022
  lines.push(
12715
- `- [ ] ${item4.check.label} (${item4.areaLabel}) \u2014 \`viberaven guide ${provider2} --step 1\``
13023
+ provider2 ? `- [ ] ${item4.check.label} (${item4.areaLabel}): \`viberaven guide ${provider2} --step 1\`` : `- [ ] ${item4.check.label} (${item4.areaLabel})`
12716
13024
  );
12717
13025
  }
12718
13026
  lines.push("");
@@ -12733,6 +13041,7 @@ var init_launchPlaybook = __esm({
12733
13041
  init_manualChecks();
12734
13042
  init_menu();
12735
13043
  init_commands();
13044
+ init_repoFixes();
12736
13045
  }
12737
13046
  });
12738
13047
 
@@ -13061,25 +13370,25 @@ var init_reportHtml = __esm({
13061
13370
  function getBundledReportAssetsDir() {
13062
13371
  const base = __dirname;
13063
13372
  const candidates = [
13064
- (0, import_node_path12.join)(base, "report"),
13065
- (0, import_node_path12.join)(base, "..", "assets", "report"),
13066
- (0, import_node_path12.join)(base, "..", "..", "assets", "report"),
13067
- (0, import_node_path12.join)(process.cwd(), "packages", "cli", "assets", "report"),
13068
- (0, import_node_path12.join)(process.cwd(), "assets", "report")
13373
+ (0, import_node_path13.join)(base, "report"),
13374
+ (0, import_node_path13.join)(base, "..", "assets", "report"),
13375
+ (0, import_node_path13.join)(base, "..", "..", "assets", "report"),
13376
+ (0, import_node_path13.join)(process.cwd(), "packages", "cli", "assets", "report"),
13377
+ (0, import_node_path13.join)(process.cwd(), "assets", "report")
13069
13378
  ];
13070
13379
  for (const dir of candidates) {
13071
- if ((0, import_node_fs7.existsSync)((0, import_node_path12.join)(dir, "station.css"))) {
13380
+ if ((0, import_node_fs8.existsSync)((0, import_node_path13.join)(dir, "station.css"))) {
13072
13381
  return dir;
13073
13382
  }
13074
13383
  }
13075
13384
  throw new Error("Report assets missing. Run `npm run sync-report-assets` and `npm run build` in packages/cli.");
13076
13385
  }
13077
- var import_node_fs7, import_node_path12, REPORT_ASSET_FILES;
13386
+ var import_node_fs8, import_node_path13, REPORT_ASSET_FILES;
13078
13387
  var init_reportAssets = __esm({
13079
13388
  "src/report/reportAssets.ts"() {
13080
13389
  "use strict";
13081
- import_node_fs7 = require("node:fs");
13082
- import_node_path12 = require("node:path");
13390
+ import_node_fs8 = require("node:fs");
13391
+ import_node_path13 = require("node:path");
13083
13392
  REPORT_ASSET_FILES = [
13084
13393
  "station.css",
13085
13394
  "station.js",
@@ -13267,9 +13576,10 @@ function deriveRepoCodeAction(task) {
13267
13576
  gapId: task.gapId,
13268
13577
  readiness: [...askFirst ? [askFirst] : [], task.exactFix ?? "Repo-code fix is available"],
13269
13578
  target: task.file ? { type: "file", label: "Repo target", path: displayFile } : { type: "command", label: "Apply repo fix", command: task.mcpTool ?? `${PUBLIC_COMMAND} prompt --gap ${task.gapId}` },
13270
- fileTargets: task.file ? [{ path: displayFile, reason: task.exactFix }] : void 0,
13579
+ fileTargets: task.file ? [{ path: displayFile, ...task.line !== void 0 ? { line: task.line } : {}, reason: task.exactFix }] : void 0,
13271
13580
  verifyCommand: verifyActionCommand("pending"),
13272
- resumeInstruction: `${task.title} is complete. Continue VibeRaven from pending.`,
13581
+ resumeInstruction: `${task.title} is complete. Continue VibeRaven from pending.${task.userSteps?.length ? ` Then tell the user what is left outside the repo: ${task.userSteps.join(" ")}` : ""}`,
13582
+ ...task.userSteps?.length ? { userSteps: [...task.userSteps] } : {},
13273
13583
  mcpTool: task.mcpTool,
13274
13584
  mcpArgs: task.mcpArgs,
13275
13585
  // Only a gap with an automatic recipe has a heal-apply fallback, and a task that needs the user's yes keeps it
@@ -13278,7 +13588,7 @@ function deriveRepoCodeAction(task) {
13278
13588
  ...task.requiresUserAction ? { requiresUserAction: true } : {}
13279
13589
  };
13280
13590
  }
13281
- function deriveVerifyAction() {
13591
+ function deriveVerifyAction(waiting) {
13282
13592
  return {
13283
13593
  id: "pending",
13284
13594
  actionKey: buildActionKey({
@@ -13290,8 +13600,9 @@ function deriveVerifyAction() {
13290
13600
  revision: 1,
13291
13601
  kind: "verify",
13292
13602
  title: "Run Final Verification",
13293
- status: "blocked",
13294
- readiness: ["Run after provider and repo-code actions are complete"],
13603
+ // Blocked only while repo-code or provider actions come first; with none left it is the next step.
13604
+ status: waiting ? "blocked" : "active",
13605
+ readiness: [waiting ? "Run after provider and repo-code actions are complete" : "No repo-code or provider actions left in this scan"],
13295
13606
  target: { type: "command", label: "Final strict gate", command: `${PUBLIC_COMMAND} --strict` },
13296
13607
  verifyCommand: `${PUBLIC_COMMAND} --strict`,
13297
13608
  resumeInstruction: "Final verification finished. Continue VibeRaven from pending."
@@ -13299,7 +13610,7 @@ function deriveVerifyAction() {
13299
13610
  }
13300
13611
  function deriveCurrentActions(input) {
13301
13612
  const primaryActions = input.tasks.filter((task) => task.fixType === "provider-action" || task.fixType === "repo-code").map((task) => task.fixType === "provider-action" ? deriveProviderAction(task) : deriveRepoCodeAction(task));
13302
- return [...primaryActions, deriveVerifyAction()];
13613
+ return [...primaryActions, deriveVerifyAction(primaryActions.length > 0)];
13303
13614
  }
13304
13615
  var init_derive = __esm({
13305
13616
  "src/actions/derive.ts"() {
@@ -13314,7 +13625,7 @@ function emptyActionRegistry() {
13314
13625
  return { version: 1, nextId: 1, actions: {} };
13315
13626
  }
13316
13627
  function actionRegistryPath(cwd) {
13317
- return (0, import_node_path13.join)(getProjectArtifactsDir(cwd), "action-registry.json");
13628
+ return (0, import_node_path14.join)(getProjectArtifactsDir(cwd), "action-registry.json");
13318
13629
  }
13319
13630
  async function loadActionRegistry(cwd) {
13320
13631
  try {
@@ -13386,7 +13697,8 @@ async function assignActionIds(input) {
13386
13697
  title: action.title,
13387
13698
  gapId: action.gapId,
13388
13699
  kind: action.kind,
13389
- provider: action.provider
13700
+ provider: action.provider,
13701
+ ...action.userSteps?.length ? { userSteps: [...action.userSteps] } : {}
13390
13702
  };
13391
13703
  } else {
13392
13704
  entry.status = "active";
@@ -13395,6 +13707,9 @@ async function assignActionIds(input) {
13395
13707
  entry.gapId = action.gapId;
13396
13708
  entry.kind = action.kind;
13397
13709
  entry.provider = action.provider;
13710
+ if (action.userSteps?.length) entry.userSteps = [...action.userSteps];
13711
+ else delete entry.userSteps;
13712
+ delete entry.followUpShownAt;
13398
13713
  if (entry.fingerprint !== fingerprint) {
13399
13714
  entry.revision += 1;
13400
13715
  entry.fingerprint = fingerprint;
@@ -13418,16 +13733,27 @@ async function assignActionIds(input) {
13418
13733
  await saveActionRegistry(input.cwd, registry);
13419
13734
  return { actions, registry };
13420
13735
  }
13736
+ function pendingUserFollowUps(registry) {
13737
+ return Object.values(registry.actions).filter((entry) => entry.status === "resolved" && entry.userSteps?.length && !entry.followUpShownAt).map((entry) => ({ id: entry.id, gapId: entry.gapId, title: entry.title, userSteps: [...entry.userSteps ?? []] }));
13738
+ }
13739
+ async function markUserFollowUpsShown(cwd, ids, now = (/* @__PURE__ */ new Date()).toISOString()) {
13740
+ if (ids.length === 0) return;
13741
+ const registry = await loadActionRegistry(cwd);
13742
+ for (const entry of Object.values(registry.actions)) {
13743
+ if (ids.includes(entry.id)) entry.followUpShownAt = now;
13744
+ }
13745
+ await saveActionRegistry(cwd, registry);
13746
+ }
13421
13747
  async function resolveActionById(cwd, id) {
13422
13748
  const registry = await loadActionRegistry(cwd);
13423
13749
  return Object.values(registry.actions).find((entry) => entry.id === id);
13424
13750
  }
13425
- var import_promises9, import_node_path13;
13751
+ var import_promises9, import_node_path14;
13426
13752
  var init_registry2 = __esm({
13427
13753
  "src/actions/registry.ts"() {
13428
13754
  "use strict";
13429
13755
  import_promises9 = require("node:fs/promises");
13430
- import_node_path13 = require("node:path");
13756
+ import_node_path14 = require("node:path");
13431
13757
  init_config();
13432
13758
  init_canonical();
13433
13759
  }
@@ -13438,7 +13764,7 @@ function gateStatus2(artifact) {
13438
13764
  if (!Array.isArray(artifact.gaps)) {
13439
13765
  return "unknown";
13440
13766
  }
13441
- return artifact.gaps.length === 0 ? "clear" : "not_clear";
13767
+ return gateStatusForGaps(artifact.gaps);
13442
13768
  }
13443
13769
  async function writeActionArtifacts(input) {
13444
13770
  const derived = deriveCurrentActions({
@@ -13459,7 +13785,9 @@ async function writeActionArtifacts(input) {
13459
13785
  gateStatus: gateStatus2(input.artifact),
13460
13786
  actions: assigned.actions
13461
13787
  };
13462
- const actionsPath = (0, import_node_path14.join)(getProjectArtifactsDir(input.cwd), "actions.json");
13788
+ const followUps = pendingUserFollowUps(assigned.registry);
13789
+ if (followUps.length > 0) manifest.userFollowUps = followUps;
13790
+ const actionsPath = (0, import_node_path15.join)(getProjectArtifactsDir(input.cwd), "actions.json");
13463
13791
  await (0, import_promises10.writeFile)(actionsPath, `${JSON.stringify(manifest, null, 2)}
13464
13792
  `, "utf8");
13465
13793
  return {
@@ -13468,13 +13796,14 @@ async function writeActionArtifacts(input) {
13468
13796
  manifest
13469
13797
  };
13470
13798
  }
13471
- var import_promises10, import_node_path14;
13799
+ var import_promises10, import_node_path15;
13472
13800
  var init_artifacts = __esm({
13473
13801
  "src/actions/artifacts.ts"() {
13474
13802
  "use strict";
13475
13803
  import_promises10 = require("node:fs/promises");
13476
- import_node_path14 = require("node:path");
13804
+ import_node_path15 = require("node:path");
13477
13805
  init_config();
13806
+ init_gateResult();
13478
13807
  init_derive();
13479
13808
  init_registry2();
13480
13809
  }
@@ -13483,24 +13812,47 @@ var init_artifacts = __esm({
13483
13812
  // src/artifacts.ts
13484
13813
  async function copyReportAssets(reportAssetsDir) {
13485
13814
  const sourceDir = getBundledReportAssetsDir();
13486
- await (0, import_promises11.mkdir)((0, import_node_path15.join)(reportAssetsDir, "assets"), { recursive: true });
13815
+ await (0, import_promises11.mkdir)((0, import_node_path16.join)(reportAssetsDir, "assets"), { recursive: true });
13487
13816
  for (const rel of REPORT_ASSET_FILES) {
13488
- await (0, import_promises11.copyFile)((0, import_node_path15.join)(sourceDir, rel), (0, import_node_path15.join)(reportAssetsDir, rel));
13817
+ await (0, import_promises11.copyFile)((0, import_node_path16.join)(sourceDir, rel), (0, import_node_path16.join)(reportAssetsDir, rel));
13818
+ }
13819
+ }
13820
+ async function isGapEvidenceFile(path) {
13821
+ try {
13822
+ return JSON.parse(await (0, import_promises11.readFile)(path, "utf8")).$schema === GAP_SCHEMA;
13823
+ } catch {
13824
+ return false;
13825
+ }
13826
+ }
13827
+ async function removeStaleGapFiles(gapsDir, current) {
13828
+ let entries;
13829
+ try {
13830
+ entries = await (0, import_promises11.readdir)(gapsDir, { withFileTypes: true });
13831
+ } catch {
13832
+ return;
13833
+ }
13834
+ for (const entry of entries) {
13835
+ if (!entry.isFile() || !entry.name.endsWith(".json") || current.has(entry.name)) continue;
13836
+ const path = (0, import_node_path16.join)(gapsDir, entry.name);
13837
+ try {
13838
+ if (await isGapEvidenceFile(path)) await (0, import_promises11.rm)(path, { force: true });
13839
+ } catch {
13840
+ }
13489
13841
  }
13490
13842
  }
13491
13843
  async function writeScanArtifacts(options) {
13492
13844
  const cwd = options.cwd ?? options.artifact.workspacePath;
13493
13845
  const dir = getProjectArtifactsDir(cwd);
13494
13846
  await (0, import_promises11.mkdir)(dir, { recursive: true });
13495
- const jsonPath = (0, import_node_path15.join)(dir, "last-scan.json");
13496
- const gateResultPath = (0, import_node_path15.join)(dir, "gate-result.json");
13497
- const contextMapPath = (0, import_node_path15.join)(dir, "context-map.json");
13498
- const gapsDir = (0, import_node_path15.join)(dir, "gaps");
13499
- const tasklistPath = (0, import_node_path15.join)(dir, "agent-tasklist.md");
13500
- const summaryPath = (0, import_node_path15.join)(dir, "agent-summary.md");
13501
- const playbookPath = (0, import_node_path15.join)(dir, "launch-playbook.md");
13502
- const reportPath = (0, import_node_path15.join)(dir, "report.html");
13503
- const reportAssetsDir = (0, import_node_path15.join)(dir, "report");
13847
+ const jsonPath = (0, import_node_path16.join)(dir, "last-scan.json");
13848
+ const gateResultPath = (0, import_node_path16.join)(dir, "gate-result.json");
13849
+ const contextMapPath = (0, import_node_path16.join)(dir, "context-map.json");
13850
+ const gapsDir = (0, import_node_path16.join)(dir, "gaps");
13851
+ const tasklistPath = (0, import_node_path16.join)(dir, "agent-tasklist.md");
13852
+ const summaryPath = (0, import_node_path16.join)(dir, "agent-summary.md");
13853
+ const playbookPath = (0, import_node_path16.join)(dir, "launch-playbook.md");
13854
+ const reportPath = (0, import_node_path16.join)(dir, "report.html");
13855
+ const reportAssetsDir = (0, import_node_path16.join)(dir, "report");
13504
13856
  await (0, import_promises11.mkdir)(gapsDir, { recursive: true });
13505
13857
  const safe2 = sanitizeArtifactForDisk(options.artifact);
13506
13858
  const json = `${JSON.stringify(safe2, null, 2)}
@@ -13510,26 +13862,27 @@ async function writeScanArtifacts(options) {
13510
13862
  const html = generateReportHtml(safe2);
13511
13863
  const tasks = buildTaskList(safe2);
13512
13864
  const tasklist = buildTaskListMarkdown(tasks);
13513
- const gateResult = `${JSON.stringify(generateGateResult(safe2), null, 2)}
13865
+ const packageManager = detectPackageManager(options.artifact.workspacePath);
13866
+ const gateResult = `${JSON.stringify(generateGateResult(safe2, { packageManager }), null, 2)}
13514
13867
  `;
13515
- const contextMap = `${JSON.stringify(generateContextMap(safe2), null, 2)}
13868
+ const contextMap = `${JSON.stringify(generateContextMap(safe2, { packageManager }), null, 2)}
13516
13869
  `;
13517
13870
  const gapEvidenceFiles = generateGapEvidenceFiles(safe2);
13518
13871
  await copyReportAssets(reportAssetsDir);
13519
13872
  await (0, import_promises11.writeFile)(gateResultPath, gateResult, "utf-8");
13520
13873
  await (0, import_promises11.writeFile)(contextMapPath, contextMap, "utf-8");
13521
13874
  for (const gap of gapEvidenceFiles) {
13522
- await (0, import_promises11.writeFile)((0, import_node_path15.join)(dir, "gaps", `${gap.content.id}.json`), `${JSON.stringify(gap.content, null, 2)}
13875
+ await (0, import_promises11.writeFile)((0, import_node_path16.join)(dir, "gaps", `${gap.content.id}.json`), `${JSON.stringify(gap.content, null, 2)}
13523
13876
  `, "utf-8");
13524
13877
  }
13525
13878
  await (0, import_promises11.writeFile)(tasklistPath, tasklist, "utf-8");
13526
13879
  let architectureMapPath;
13527
13880
  if (typeof safe2.architectureMap === "string" && safe2.architectureMap.length > 0) {
13528
- architectureMapPath = (0, import_node_path15.join)(dir, "architecture-map.md");
13881
+ architectureMapPath = (0, import_node_path16.join)(dir, "architecture-map.md");
13529
13882
  await (0, import_promises11.writeFile)(architectureMapPath, safe2.architectureMap, "utf-8");
13530
13883
  }
13531
13884
  if (safe2.architectureData) {
13532
- await (0, import_promises11.writeFile)((0, import_node_path15.join)(dir, "architecture-map.json"), `${JSON.stringify(safe2.architectureData, null, 2)}
13885
+ await (0, import_promises11.writeFile)((0, import_node_path16.join)(dir, "architecture-map.json"), `${JSON.stringify(safe2.architectureData, null, 2)}
13533
13886
  `, "utf-8");
13534
13887
  }
13535
13888
  await (0, import_promises11.writeFile)(jsonPath, json, "utf-8");
@@ -13542,6 +13895,7 @@ async function writeScanArtifacts(options) {
13542
13895
  tasks,
13543
13896
  paths: { reportPath, playbookPath }
13544
13897
  });
13898
+ await removeStaleGapFiles(gapsDir, new Set(gapEvidenceFiles.map((gap) => `${gap.content.id}.json`)));
13545
13899
  return {
13546
13900
  dir,
13547
13901
  jsonPath,
@@ -13558,14 +13912,15 @@ async function writeScanArtifacts(options) {
13558
13912
  ...architectureMapPath ? { architectureMapPath } : {}
13559
13913
  };
13560
13914
  }
13561
- var import_promises11, import_node_path15;
13915
+ var import_promises11, import_node_path16, GAP_SCHEMA;
13562
13916
  var init_artifacts2 = __esm({
13563
13917
  "src/artifacts.ts"() {
13564
13918
  "use strict";
13565
13919
  import_promises11 = require("node:fs/promises");
13566
- import_node_path15 = require("node:path");
13920
+ import_node_path16 = require("node:path");
13567
13921
  init_contextMap();
13568
13922
  init_gateResult();
13923
+ init_packageManager();
13569
13924
  init_gapEvidence();
13570
13925
  init_agentSummary();
13571
13926
  init_buildTaskList();
@@ -13575,6 +13930,7 @@ var init_artifacts2 = __esm({
13575
13930
  init_config();
13576
13931
  init_sanitizeArtifact();
13577
13932
  init_artifacts();
13933
+ GAP_SCHEMA = "https://viberaven.dev/schemas/gap.schema.json";
13578
13934
  }
13579
13935
  });
13580
13936
 
@@ -13672,12 +14028,12 @@ function createAppWindowOpenCommands(target, platform = process.platform, env =
13672
14028
  ];
13673
14029
  if (platform === "win32") {
13674
14030
  const candidates = [
13675
- env.LOCALAPPDATA ? (0, import_node_path16.join)(env.LOCALAPPDATA, "Google", "Chrome", "Application", "chrome.exe") : "",
13676
- env.PROGRAMFILES ? (0, import_node_path16.join)(env.PROGRAMFILES, "Google", "Chrome", "Application", "chrome.exe") : "",
13677
- env["PROGRAMFILES(X86)"] ? (0, import_node_path16.join)(env["PROGRAMFILES(X86)"], "Google", "Chrome", "Application", "chrome.exe") : "",
13678
- env.LOCALAPPDATA ? (0, import_node_path16.join)(env.LOCALAPPDATA, "Microsoft", "Edge", "Application", "msedge.exe") : "",
13679
- env.PROGRAMFILES ? (0, import_node_path16.join)(env.PROGRAMFILES, "Microsoft", "Edge", "Application", "msedge.exe") : "",
13680
- env["PROGRAMFILES(X86)"] ? (0, import_node_path16.join)(env["PROGRAMFILES(X86)"], "Microsoft", "Edge", "Application", "msedge.exe") : "",
14031
+ env.LOCALAPPDATA ? (0, import_node_path17.join)(env.LOCALAPPDATA, "Google", "Chrome", "Application", "chrome.exe") : "",
14032
+ env.PROGRAMFILES ? (0, import_node_path17.join)(env.PROGRAMFILES, "Google", "Chrome", "Application", "chrome.exe") : "",
14033
+ env["PROGRAMFILES(X86)"] ? (0, import_node_path17.join)(env["PROGRAMFILES(X86)"], "Google", "Chrome", "Application", "chrome.exe") : "",
14034
+ env.LOCALAPPDATA ? (0, import_node_path17.join)(env.LOCALAPPDATA, "Microsoft", "Edge", "Application", "msedge.exe") : "",
14035
+ env.PROGRAMFILES ? (0, import_node_path17.join)(env.PROGRAMFILES, "Microsoft", "Edge", "Application", "msedge.exe") : "",
14036
+ env["PROGRAMFILES(X86)"] ? (0, import_node_path17.join)(env["PROGRAMFILES(X86)"], "Microsoft", "Edge", "Application", "msedge.exe") : "",
13681
14037
  "chrome",
13682
14038
  "msedge"
13683
14039
  ].filter(Boolean);
@@ -13711,7 +14067,7 @@ async function openWithSystemDefault(target) {
13711
14067
  });
13712
14068
  }
13713
14069
  async function spawnDetached(command) {
13714
- if (/^[A-Za-z]:[\\/]/.test(command.command) && !(0, import_node_fs8.existsSync)(command.command)) {
14070
+ if (/^[A-Za-z]:[\\/]/.test(command.command) && !(0, import_node_fs9.existsSync)(command.command)) {
13715
14071
  throw new Error(`Browser not found: ${command.command}`);
13716
14072
  }
13717
14073
  await new Promise((resolve10, reject) => {
@@ -13754,13 +14110,13 @@ async function openUrlInBrowser(url) {
13754
14110
  }
13755
14111
  await openWithSystemDefault(trimmed);
13756
14112
  }
13757
- var import_node_child_process4, import_node_fs8, import_node_path16;
14113
+ var import_node_child_process4, import_node_fs9, import_node_path17;
13758
14114
  var init_openBrowser = __esm({
13759
14115
  "src/openBrowser.ts"() {
13760
14116
  "use strict";
13761
14117
  import_node_child_process4 = require("node:child_process");
13762
- import_node_fs8 = require("node:fs");
13763
- import_node_path16 = require("node:path");
14118
+ import_node_fs9 = require("node:fs");
14119
+ import_node_path17 = require("node:path");
13764
14120
  }
13765
14121
  });
13766
14122
 
@@ -13817,9 +14173,9 @@ var init_clipboard = __esm({
13817
14173
  }
13818
14174
  });
13819
14175
 
13820
- // ../../../../../VibeRice/packages/cli/node_modules/picocolors/picocolors.js
14176
+ // node_modules/picocolors/picocolors.js
13821
14177
  var require_picocolors = __commonJS({
13822
- "../../../../../VibeRice/packages/cli/node_modules/picocolors/picocolors.js"(exports2, module2) {
14178
+ "node_modules/picocolors/picocolors.js"(exports2, module2) {
13823
14179
  var p2 = process || {};
13824
14180
  var argv = p2.argv || [];
13825
14181
  var env = p2.env || {};
@@ -13889,46 +14245,6 @@ var require_picocolors = __commonJS({
13889
14245
  }
13890
14246
  });
13891
14247
 
13892
- // src/report/missionSelection.ts
13893
- function normalizeProviderToken2(value) {
13894
- return value.toLowerCase().replace(/&/g, "and").replace(/[^a-z0-9]+/g, "");
13895
- }
13896
- function missionMatchesProvider(mission, provider2) {
13897
- const current = normalizeProviderToken2(provider2);
13898
- return normalizeProviderToken2(mission.provider || mission.providerLabel || mission.key) === current || normalizeProviderToken2(mission.providerLabel || mission.provider || mission.key) === current;
13899
- }
13900
- function missionEvidenceScore(mission) {
13901
- const repoVerified = mission.checks.filter((check) => check.evidenceClass === "repo-verified" || check.status === "passed").length;
13902
- const missing = mission.checks.filter(
13903
- (check) => check.evidenceClass === "missing-repo-fix" || check.status === "missing" || check.status === "failed"
13904
- ).length;
13905
- return repoVerified * 100 + (mission.readinessPercent ?? 0) - missing;
13906
- }
13907
- function preferredMissionForArea(area, selectedProvider) {
13908
- const missions = area?.providerMissions ?? [];
13909
- if (missions.length === 0) {
13910
- return void 0;
13911
- }
13912
- const selected = selectedProvider ? missions.find((mission) => missionMatchesProvider(mission, selectedProvider)) : void 0;
13913
- if (selected) {
13914
- return selected;
13915
- }
13916
- return [...missions].sort((a, b3) => missionEvidenceScore(b3) - missionEvidenceScore(a))[0] ?? missions[0];
13917
- }
13918
- function openChecksForMission(mission) {
13919
- if (!mission) {
13920
- return 0;
13921
- }
13922
- return mission.checks.filter(
13923
- (check) => check.status === "missing" || check.status === "failed" || check.status === "needs-connection"
13924
- ).length;
13925
- }
13926
- var init_missionSelection = __esm({
13927
- "src/report/missionSelection.ts"() {
13928
- "use strict";
13929
- }
13930
- });
13931
-
13932
14248
  // src/statusLabels.ts
13933
14249
  function formatAgentStatus(label, message) {
13934
14250
  return `${label}: ${message}`;
@@ -13970,7 +14286,7 @@ function gapTagColor(modelGaps, open2) {
13970
14286
  }
13971
14287
  function manualActionCheckCount(artifact) {
13972
14288
  return (artifact.missionGraph.areas ?? []).reduce((areaTotal, area) => {
13973
- return areaTotal + area.providerMissions.reduce((missionTotal, mission) => {
14289
+ return areaTotal + inRepoMissions(artifact, area).reduce((missionTotal, mission) => {
13974
14290
  return missionTotal + mission.checks.filter(
13975
14291
  (check) => check.evidenceClass === "manual-dashboard" || check.evidenceClass === "mcp-verifier" || check.evidenceSource === "provider" || check.evidenceSource === "mcp" || check.status === "needs-connection" || check.status === "unknown"
13976
14292
  ).length;
@@ -13991,8 +14307,14 @@ function printScanSummary(artifact, paths) {
13991
14307
  console.log(import_picocolors.default.dim("\u2514") + import_picocolors.default.dim("\u2500".repeat(width - 2)) + import_picocolors.default.dim("\u2518"));
13992
14308
  console.log("");
13993
14309
  for (const area of artifact.missionGraph.areas ?? []) {
13994
- const mission = preferredMissionForArea(area, artifact.selectedProviders?.[area.key] ?? "");
14310
+ if ((area.providerMissions ?? []).length === 0) {
14311
+ continue;
14312
+ }
14313
+ const mission = preferredInRepoMission(artifact, area);
13995
14314
  if (!mission) {
14315
+ const modelGaps2 = artifact.gaps.filter((g2) => g2.primaryMapCategory === area.key).length;
14316
+ const tag2 = modelGaps2 > 0 ? import_picocolors.default.red(` GAP ${modelGaps2}`) : "";
14317
+ console.log(` ${import_picocolors.default.dim(area.label.padEnd(18))} ${import_picocolors.default.dim(NONE_FOUND)}${tag2}`);
13996
14318
  continue;
13997
14319
  }
13998
14320
  const open2 = openChecksForMission(mission);
@@ -14038,14 +14360,15 @@ var init_terminalSummary = __esm({
14038
14360
  "use strict";
14039
14361
  import_picocolors = __toESM(require_picocolors());
14040
14362
  init_missionSelection();
14363
+ init_inRepoMissions();
14041
14364
  init_statusLabels();
14042
14365
  init_resolveNextAction();
14043
14366
  }
14044
14367
  });
14045
14368
 
14046
- // ../../../../../VibeRice/packages/cli/node_modules/sisteransi/src/index.js
14369
+ // node_modules/sisteransi/src/index.js
14047
14370
  var require_src = __commonJS({
14048
- "../../../../../VibeRice/packages/cli/node_modules/sisteransi/src/index.js"(exports2, module2) {
14371
+ "node_modules/sisteransi/src/index.js"(exports2, module2) {
14049
14372
  "use strict";
14050
14373
  var ESC = "\x1B";
14051
14374
  var CSI = `${ESC}[`;
@@ -14099,7 +14422,7 @@ var require_src = __commonJS({
14099
14422
  }
14100
14423
  });
14101
14424
 
14102
- // ../../../../../VibeRice/packages/cli/node_modules/@clack/core/dist/index.mjs
14425
+ // node_modules/@clack/core/dist/index.mjs
14103
14426
  function DD({ onlyFirst: e2 = false } = {}) {
14104
14427
  const t = ["[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?(?:\\u0007|\\u001B\\u005C|\\u009C))", "(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]))"].join("|");
14105
14428
  return new RegExp(t, e2 ? void 0 : "g");
@@ -14211,7 +14534,7 @@ function fD({ input: e2 = import_node_process.stdin, output: u2 = import_node_pr
14211
14534
  }
14212
14535
  var import_sisteransi, import_node_process, g, import_node_readline, import_node_stream, uD, W, tD, eD, FD, sD, w, N, I, R, C, iD, rD, ED, d, oD, y, V, nD, G, _, z, K, aD, k, hD, lD, xD, B, AD, S, gD, vD, h, x, A, OD, PD, J, LD;
14213
14536
  var init_dist = __esm({
14214
- "../../../../../VibeRice/packages/cli/node_modules/@clack/core/dist/index.mjs"() {
14537
+ "node_modules/@clack/core/dist/index.mjs"() {
14215
14538
  import_sisteransi = __toESM(require_src(), 1);
14216
14539
  import_node_process = require("node:process");
14217
14540
  g = __toESM(require("node:readline"), 1);
@@ -14461,13 +14784,13 @@ var init_dist = __esm({
14461
14784
  }
14462
14785
  });
14463
14786
 
14464
- // ../../../../../VibeRice/packages/cli/node_modules/@clack/prompts/dist/index.mjs
14787
+ // node_modules/@clack/prompts/dist/index.mjs
14465
14788
  function ce() {
14466
14789
  return import_node_process2.default.platform !== "win32" ? import_node_process2.default.env.TERM !== "linux" : !!import_node_process2.default.env.CI || !!import_node_process2.default.env.WT_SESSION || !!import_node_process2.default.env.TERMINUS_SUBLIME || import_node_process2.default.env.ConEmuTask === "{cmd::Cmder}" || import_node_process2.default.env.TERM_PROGRAM === "Terminus-Sublime" || import_node_process2.default.env.TERM_PROGRAM === "vscode" || import_node_process2.default.env.TERM === "xterm-256color" || import_node_process2.default.env.TERM === "alacritty" || import_node_process2.default.env.TERMINAL_EMULATOR === "JetBrains-JediTerm";
14467
14790
  }
14468
14791
  var import_node_process2, import_picocolors2, import_sisteransi2, V2, u, le, L2, W2, C2, ue, o, d2, k2, P2, A2, T, F, $e, _2, me, de, pe, q, D, U, K2, b2, G2, ve, xe, Ie, Se, M2, J2, Y2;
14469
14792
  var init_dist2 = __esm({
14470
- "../../../../../VibeRice/packages/cli/node_modules/@clack/prompts/dist/index.mjs"() {
14793
+ "node_modules/@clack/prompts/dist/index.mjs"() {
14471
14794
  init_dist();
14472
14795
  init_dist();
14473
14796
  import_node_process2 = __toESM(require("node:process"), 1);
@@ -14700,7 +15023,7 @@ var VERSION;
14700
15023
  var init_version = __esm({
14701
15024
  "src/version.ts"() {
14702
15025
  "use strict";
14703
- VERSION = "1.6.2";
15026
+ VERSION = "1.6.4";
14704
15027
  }
14705
15028
  });
14706
15029
 
@@ -15072,17 +15395,17 @@ async function initCursorRulesPack(options) {
15072
15395
  const pack = buildCursorRulesPack();
15073
15396
  for (const rule of pack) {
15074
15397
  const file = `${CURSOR_RULES_DIR}/${rule.filename}`;
15075
- const path = (0, import_node_path17.join)(options.cwd, file);
15398
+ const path = (0, import_node_path18.join)(options.cwd, file);
15076
15399
  const existing = await readExistingFile(path);
15077
15400
  const changed = !existing.exists || existing.content !== rule.content;
15078
15401
  const action = !existing.exists ? "created" : changed ? "updated" : "unchanged";
15079
15402
  if (!options.dryRun && changed) {
15080
- await (0, import_promises12.mkdir)((0, import_node_path17.join)(options.cwd, CURSOR_RULES_DIR), { recursive: true });
15403
+ await (0, import_promises12.mkdir)((0, import_node_path18.join)(options.cwd, CURSOR_RULES_DIR), { recursive: true });
15081
15404
  await (0, import_promises12.writeFile)(path, rule.content, "utf-8");
15082
15405
  }
15083
15406
  results.push({ target: "cursor", file, path, action });
15084
15407
  }
15085
- const legacyPath = (0, import_node_path17.join)(options.cwd, LEGACY_CURSOR_RULE_FILE);
15408
+ const legacyPath = (0, import_node_path18.join)(options.cwd, LEGACY_CURSOR_RULE_FILE);
15086
15409
  if (!options.dryRun && await fileExists(legacyPath)) {
15087
15410
  await (0, import_promises12.rm)(legacyPath, { force: true });
15088
15411
  }
@@ -15109,12 +15432,12 @@ async function fileExists(path) {
15109
15432
  function isFileNotFoundError(error) {
15110
15433
  return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
15111
15434
  }
15112
- var import_promises12, import_node_path17, CURSOR_RULES_DIR, LEGACY_CURSOR_RULE_FILE, DOMAIN_PATH_POINTER;
15435
+ var import_promises12, import_node_path18, CURSOR_RULES_DIR, LEGACY_CURSOR_RULE_FILE, DOMAIN_PATH_POINTER;
15113
15436
  var init_cursorRulesPack = __esm({
15114
15437
  "src/commands/cursorRulesPack.ts"() {
15115
15438
  "use strict";
15116
15439
  import_promises12 = require("node:fs/promises");
15117
- import_node_path17 = require("node:path");
15440
+ import_node_path18 = require("node:path");
15118
15441
  init_commands();
15119
15442
  CURSOR_RULES_DIR = ".cursor/rules";
15120
15443
  LEGACY_CURSOR_RULE_FILE = `${CURSOR_RULES_DIR}/viberaven.mdc`;
@@ -15214,8 +15537,8 @@ var init_agentTargets = __esm({
15214
15537
 
15215
15538
  // src/commands/seedPackageJsonScripts.ts
15216
15539
  async function seedPackageJsonScripts(options) {
15217
- const packageJsonPath = (0, import_node_path18.join)(options.cwd, "package.json");
15218
- if (!(0, import_node_fs9.existsSync)(packageJsonPath)) {
15540
+ const packageJsonPath = (0, import_node_path19.join)(options.cwd, "package.json");
15541
+ if (!(0, import_node_fs10.existsSync)(packageJsonPath)) {
15219
15542
  return null;
15220
15543
  }
15221
15544
  const raw = await (0, import_promises13.readFile)(packageJsonPath, "utf-8");
@@ -15252,13 +15575,13 @@ async function seedPackageJsonScripts(options) {
15252
15575
  }
15253
15576
  return { action: "updated", added, skipped, changed: true };
15254
15577
  }
15255
- var import_node_fs9, import_promises13, import_node_path18, VIBERAVEN_PACKAGE_JSON_SCRIPTS;
15578
+ var import_node_fs10, import_promises13, import_node_path19, VIBERAVEN_PACKAGE_JSON_SCRIPTS;
15256
15579
  var init_seedPackageJsonScripts = __esm({
15257
15580
  "src/commands/seedPackageJsonScripts.ts"() {
15258
15581
  "use strict";
15259
- import_node_fs9 = require("node:fs");
15582
+ import_node_fs10 = require("node:fs");
15260
15583
  import_promises13 = require("node:fs/promises");
15261
- import_node_path18 = require("node:path");
15584
+ import_node_path19 = require("node:path");
15262
15585
  init_commands();
15263
15586
  VIBERAVEN_PACKAGE_JSON_SCRIPTS = {
15264
15587
  "viberaven:gate": PUBLIC_AGENT_MODE_COMMAND,
@@ -15278,12 +15601,12 @@ async function initAgentRules(options) {
15278
15601
  continue;
15279
15602
  }
15280
15603
  const file = AGENT_RULE_TARGETS[target].file;
15281
- const path = (0, import_node_path19.join)(options.cwd, file);
15604
+ const path = (0, import_node_path20.join)(options.cwd, file);
15282
15605
  const existing = await readExistingFile2(path);
15283
15606
  const injected = injectAgentRulesBlock(existing.content, renderAgentRulesForTarget(target));
15284
15607
  const action = !existing.exists ? "created" : injected.changed ? "updated" : "unchanged";
15285
15608
  if (!options.dryRun && injected.changed) {
15286
- await (0, import_promises14.mkdir)((0, import_node_path19.dirname)(path), { recursive: true });
15609
+ await (0, import_promises14.mkdir)((0, import_node_path20.dirname)(path), { recursive: true });
15287
15610
  await (0, import_promises14.writeFile)(path, injected.content, "utf-8");
15288
15611
  }
15289
15612
  results.push({ target, file, path, action });
@@ -15363,12 +15686,12 @@ async function readExistingFile2(path) {
15363
15686
  function isFileNotFoundError2(error) {
15364
15687
  return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
15365
15688
  }
15366
- var import_promises14, import_node_path19;
15689
+ var import_promises14, import_node_path20;
15367
15690
  var init_initRules = __esm({
15368
15691
  "src/commands/initRules.ts"() {
15369
15692
  "use strict";
15370
15693
  import_promises14 = require("node:fs/promises");
15371
- import_node_path19 = require("node:path");
15694
+ import_node_path20 = require("node:path");
15372
15695
  init_agentRulesBlock();
15373
15696
  init_agentTargets();
15374
15697
  init_cursorRulesPack();
@@ -15631,7 +15954,7 @@ async function runInteractiveSession(startDir = process.cwd()) {
15631
15954
  Ie(`${import_picocolors4.default.bold("VibeRaven")} ${import_picocolors4.default.dim(VERSION)}`);
15632
15955
  const cwd = await resolveWorkspaceRoot(startDir);
15633
15956
  const artifactsAt = await findArtifactsWorkspace(startDir);
15634
- if (artifactsAt && (0, import_node_path20.resolve)(artifactsAt) !== (0, import_node_path20.resolve)(startDir)) {
15957
+ if (artifactsAt && (0, import_node_path21.resolve)(artifactsAt) !== (0, import_node_path21.resolve)(startDir)) {
15635
15958
  M2.message(import_picocolors4.default.dim(`Using scan from: ${artifactsAt}`));
15636
15959
  } else {
15637
15960
  M2.message(import_picocolors4.default.dim(`Project folder: ${cwd}`));
@@ -15690,11 +16013,11 @@ async function runInteractiveSession(startDir = process.cwd()) {
15690
16013
  }
15691
16014
  Se(import_picocolors4.default.dim("Run viberaven anytime for the interactive menu."));
15692
16015
  }
15693
- var import_node_path20, import_picocolors4;
16016
+ var import_node_path21, import_picocolors4;
15694
16017
  var init_runInteractive = __esm({
15695
16018
  "src/tui/runInteractive.ts"() {
15696
16019
  "use strict";
15697
- import_node_path20 = require("node:path");
16020
+ import_node_path21 = require("node:path");
15698
16021
  init_dist2();
15699
16022
  import_picocolors4 = __toESM(require_picocolors());
15700
16023
  init_config();
@@ -15719,18 +16042,18 @@ var init_runInteractive = __esm({
15719
16042
  // src/commands/condense.ts
15720
16043
  async function runCondenseCommand(options) {
15721
16044
  const dir = getProjectArtifactsDir(options.cwd);
15722
- const artifact = JSON.parse(await (0, import_promises15.readFile)((0, import_node_path21.join)(dir, "last-scan.json"), "utf8"));
15723
- const contextMapPath = (0, import_node_path21.join)(dir, "context-map.json");
16045
+ const artifact = JSON.parse(await (0, import_promises15.readFile)((0, import_node_path22.join)(dir, "last-scan.json"), "utf8"));
16046
+ const contextMapPath = (0, import_node_path22.join)(dir, "context-map.json");
15724
16047
  await (0, import_promises15.writeFile)(contextMapPath, `${JSON.stringify(generateContextMap(artifact), null, 2)}
15725
16048
  `, "utf8");
15726
16049
  return { contextMapPath };
15727
16050
  }
15728
- var import_promises15, import_node_path21;
16051
+ var import_promises15, import_node_path22;
15729
16052
  var init_condense = __esm({
15730
16053
  "src/commands/condense.ts"() {
15731
16054
  "use strict";
15732
16055
  import_promises15 = require("node:fs/promises");
15733
- import_node_path21 = require("node:path");
16056
+ import_node_path22 = require("node:path");
15734
16057
  init_config();
15735
16058
  init_contextMap();
15736
16059
  }
@@ -15738,9 +16061,9 @@ var init_condense = __esm({
15738
16061
 
15739
16062
  // src/heal/pathSafety.ts
15740
16063
  function assertSafeHealTarget(cwd, target) {
15741
- const root = (0, import_node_path22.resolve)(cwd);
15742
- const absolute = (0, import_node_path22.resolve)(root, target);
15743
- const rel = (0, import_node_path22.relative)(root, absolute);
16064
+ const root = (0, import_node_path23.resolve)(cwd);
16065
+ const absolute = (0, import_node_path23.resolve)(root, target);
16066
+ const rel = (0, import_node_path23.relative)(root, absolute);
15744
16067
  if (rel.startsWith("..") || rel === "" || /^[A-Za-z]:/.test(rel)) {
15745
16068
  throw new Error("Heal target must stay inside the workspace");
15746
16069
  }
@@ -15750,11 +16073,11 @@ function assertSafeHealTarget(cwd, target) {
15750
16073
  }
15751
16074
  return absolute;
15752
16075
  }
15753
- var import_node_path22, BLOCKED_SEGMENTS;
16076
+ var import_node_path23, BLOCKED_SEGMENTS;
15754
16077
  var init_pathSafety = __esm({
15755
16078
  "src/heal/pathSafety.ts"() {
15756
16079
  "use strict";
15757
- import_node_path22 = require("node:path");
16080
+ import_node_path23 = require("node:path");
15758
16081
  BLOCKED_SEGMENTS = /* @__PURE__ */ new Set([".git", "node_modules", "dist", "build", ".next", ".viberaven"]);
15759
16082
  }
15760
16083
  });
@@ -15780,11 +16103,11 @@ function newHealId(now = /* @__PURE__ */ new Date()) {
15780
16103
  return `heal_${now.toISOString().replace(/\D/g, "").slice(0, 17)}_${(0, import_node_crypto.randomBytes)(3).toString("hex")}`;
15781
16104
  }
15782
16105
  async function createHealDir(cwd, makeId = () => newHealId()) {
15783
- const parent = (0, import_node_path23.join)(cwd, ".viberaven", "heal");
16106
+ const parent = (0, import_node_path24.join)(cwd, ".viberaven", "heal");
15784
16107
  await (0, import_promises16.mkdir)(parent, { recursive: true });
15785
16108
  for (let attempt = 0; attempt < 10; attempt += 1) {
15786
16109
  const id = makeId();
15787
- const dir = (0, import_node_path23.join)(parent, id);
16110
+ const dir = (0, import_node_path24.join)(parent, id);
15788
16111
  try {
15789
16112
  await (0, import_promises16.mkdir)(dir);
15790
16113
  return { id, dir };
@@ -15794,13 +16117,13 @@ async function createHealDir(cwd, makeId = () => newHealId()) {
15794
16117
  }
15795
16118
  throw new Error("Could not create a new heal directory under .viberaven/heal.");
15796
16119
  }
15797
- var import_node_crypto, import_promises16, import_node_path23;
16120
+ var import_node_crypto, import_promises16, import_node_path24;
15798
16121
  var init_healId = __esm({
15799
16122
  "src/heal/healId.ts"() {
15800
16123
  "use strict";
15801
16124
  import_node_crypto = require("node:crypto");
15802
16125
  import_promises16 = require("node:fs/promises");
15803
- import_node_path23 = require("node:path");
16126
+ import_node_path24 = require("node:path");
15804
16127
  }
15805
16128
  });
15806
16129
 
@@ -16267,7 +16590,7 @@ export const config = {
16267
16590
  // src/heal/recipes/eslintRestrictedImports.ts
16268
16591
  function detectEslintConfigFile(cwd) {
16269
16592
  for (const candidate of ESLINT_CONFIG_CANDIDATES) {
16270
- if ((0, import_node_fs10.existsSync)((0, import_node_path24.join)(cwd, candidate))) {
16593
+ if ((0, import_node_fs11.existsSync)((0, import_node_path25.join)(cwd, candidate))) {
16271
16594
  return candidate;
16272
16595
  }
16273
16596
  }
@@ -16422,12 +16745,12 @@ function applyEslintRestrictedImportsRecipe(source, configFile) {
16422
16745
  }
16423
16746
  return applyLegacyModuleRecipe(source);
16424
16747
  }
16425
- var import_node_fs10, import_node_path24, VIBERAVEN_ESLINT_MARKER, RESTRICTED_IMPORTS_MESSAGE, RESTRICTED_PATHS, RESTRICTED_PATTERNS, ESLINT_CONFIG_CANDIDATES;
16748
+ var import_node_fs11, import_node_path25, VIBERAVEN_ESLINT_MARKER, RESTRICTED_IMPORTS_MESSAGE, RESTRICTED_PATHS, RESTRICTED_PATTERNS, ESLINT_CONFIG_CANDIDATES;
16426
16749
  var init_eslintRestrictedImports = __esm({
16427
16750
  "src/heal/recipes/eslintRestrictedImports.ts"() {
16428
16751
  "use strict";
16429
- import_node_fs10 = require("node:fs");
16430
- import_node_path24 = require("node:path");
16752
+ import_node_fs11 = require("node:fs");
16753
+ import_node_path25 = require("node:path");
16431
16754
  init_commands();
16432
16755
  VIBERAVEN_ESLINT_MARKER = "VibeRaven heal: eslint_restricted_imports";
16433
16756
  RESTRICTED_IMPORTS_MESSAGE = `Restricted import. Run ${PUBLIC_AGENT_MODE_COMMAND} before substituting packages.`;
@@ -16478,13 +16801,13 @@ function defaultTargetFile(gapId) {
16478
16801
  return map[gapId];
16479
16802
  }
16480
16803
  async function readSourceOrEmpty(absolutePath) {
16481
- if (!(0, import_node_fs11.existsSync)(absolutePath)) return "";
16804
+ if (!(0, import_node_fs12.existsSync)(absolutePath)) return "";
16482
16805
  return (0, import_promises17.readFile)(absolutePath, "utf8");
16483
16806
  }
16484
16807
  async function detectUpstash(cwd) {
16485
16808
  try {
16486
- const pkgPath = (0, import_node_path25.join)(cwd, "package.json");
16487
- if (!(0, import_node_fs11.existsSync)(pkgPath)) return false;
16809
+ const pkgPath = (0, import_node_path26.join)(cwd, "package.json");
16810
+ if (!(0, import_node_fs12.existsSync)(pkgPath)) return false;
16488
16811
  const raw = await (0, import_promises17.readFile)(pkgPath, "utf8");
16489
16812
  const pkg = JSON.parse(raw);
16490
16813
  const deps = {
@@ -16500,7 +16823,7 @@ async function dispatchRecipeByGapId(gapId, cwd, explicitTarget) {
16500
16823
  const targetFile = explicitTarget ?? defaultTargetFile(gapId);
16501
16824
  if (targetFile === void 0) return null;
16502
16825
  if (gapId === "auth_secret_missing" || gapId === "node_env_not_set" || gapId === "database_url_missing") {
16503
- const absolutePath = (0, import_node_path25.join)(cwd, targetFile);
16826
+ const absolutePath = (0, import_node_path26.join)(cwd, targetFile);
16504
16827
  const source = await readSourceOrEmpty(absolutePath);
16505
16828
  let result;
16506
16829
  if (gapId === "auth_secret_missing") result = applyAuthSecretRecipe(source);
@@ -16514,25 +16837,25 @@ async function dispatchRecipeByGapId(gapId, cwd, explicitTarget) {
16514
16837
  };
16515
16838
  }
16516
16839
  if (gapId === "missing_error_boundary") {
16517
- const absolutePath = (0, import_node_path25.join)(cwd, "app/error.tsx");
16840
+ const absolutePath = (0, import_node_path26.join)(cwd, "app/error.tsx");
16518
16841
  const source = await readSourceOrEmpty(absolutePath);
16519
16842
  const result = applyErrorBoundaryRecipe(source);
16520
16843
  return { ...result, canAutoApply: true, recipeName: gapId };
16521
16844
  }
16522
16845
  if (gapId === "missing_health_route") {
16523
- const absolutePath = (0, import_node_path25.join)(cwd, "app/api/health/route.ts");
16846
+ const absolutePath = (0, import_node_path26.join)(cwd, "app/api/health/route.ts");
16524
16847
  const source = await readSourceOrEmpty(absolutePath);
16525
16848
  const result = applyHealthRouteRecipe(source);
16526
16849
  return { ...result, canAutoApply: true, recipeName: gapId };
16527
16850
  }
16528
16851
  if (gapId === "missing_loading_state") {
16529
- const absolutePath = (0, import_node_path25.join)(cwd, "app/loading.tsx");
16852
+ const absolutePath = (0, import_node_path26.join)(cwd, "app/loading.tsx");
16530
16853
  const source = await readSourceOrEmpty(absolutePath);
16531
16854
  const result = applyLoadingStateRecipe(source);
16532
16855
  return { ...result, canAutoApply: true, recipeName: gapId };
16533
16856
  }
16534
16857
  if (gapId === "missing_404_page") {
16535
- const absolutePath = (0, import_node_path25.join)(cwd, "app/not-found.tsx");
16858
+ const absolutePath = (0, import_node_path26.join)(cwd, "app/not-found.tsx");
16536
16859
  const source = await readSourceOrEmpty(absolutePath);
16537
16860
  const result = applyNotFoundRecipe(source);
16538
16861
  return { ...result, canAutoApply: true, recipeName: gapId };
@@ -16549,10 +16872,10 @@ async function dispatchRecipeByGapId(gapId, cwd, explicitTarget) {
16549
16872
  }
16550
16873
  if (gapId === "missing_csp_header") {
16551
16874
  let configFile = "next.config.js";
16552
- let absolutePath = (0, import_node_path25.join)(cwd, configFile);
16553
- if (!(0, import_node_fs11.existsSync)(absolutePath)) {
16875
+ let absolutePath = (0, import_node_path26.join)(cwd, configFile);
16876
+ if (!(0, import_node_fs12.existsSync)(absolutePath)) {
16554
16877
  configFile = "next.config.mjs";
16555
- absolutePath = (0, import_node_path25.join)(cwd, configFile);
16878
+ absolutePath = (0, import_node_path26.join)(cwd, configFile);
16556
16879
  }
16557
16880
  const source = await readSourceOrEmpty(absolutePath);
16558
16881
  const result = applyCspHeaderRecipe(source);
@@ -16564,7 +16887,7 @@ async function dispatchRecipeByGapId(gapId, cwd, explicitTarget) {
16564
16887
  }
16565
16888
  if (gapId === "missing_rate_limit") {
16566
16889
  const hasUpstash = await detectUpstash(cwd);
16567
- const middlewarePath = (0, import_node_path25.join)(cwd, "middleware.ts");
16890
+ const middlewarePath = (0, import_node_path26.join)(cwd, "middleware.ts");
16568
16891
  const source = await readSourceOrEmpty(middlewarePath);
16569
16892
  const result = applyRateLimitRecipe(source, hasUpstash);
16570
16893
  return {
@@ -16586,7 +16909,7 @@ async function dispatchRecipeByGapId(gapId, cwd, explicitTarget) {
16586
16909
  recipeName: gapId
16587
16910
  };
16588
16911
  }
16589
- const absolutePath = (0, import_node_path25.join)(cwd, configFile);
16912
+ const absolutePath = (0, import_node_path26.join)(cwd, configFile);
16590
16913
  const source = await readSourceOrEmpty(absolutePath);
16591
16914
  const result = applyEslintRestrictedImportsRecipe(source, configFile);
16592
16915
  return {
@@ -16600,13 +16923,13 @@ async function dispatchRecipeByGapId(gapId, cwd, explicitTarget) {
16600
16923
  }
16601
16924
  return null;
16602
16925
  }
16603
- var import_node_fs11, import_promises17, import_node_path25;
16926
+ var import_node_fs12, import_promises17, import_node_path26;
16604
16927
  var init_recipes = __esm({
16605
16928
  "src/heal/recipes/index.ts"() {
16606
16929
  "use strict";
16607
- import_node_fs11 = require("node:fs");
16930
+ import_node_fs12 = require("node:fs");
16608
16931
  import_promises17 = require("node:fs/promises");
16609
- import_node_path25 = require("node:path");
16932
+ import_node_path26 = require("node:path");
16610
16933
  init_envAuthSecret();
16611
16934
  init_envNodeEnv();
16612
16935
  init_envDatabaseUrl();
@@ -16683,16 +17006,16 @@ async function applyHeal(options) {
16683
17006
  rollback: { available: false, instructions: "Recipe matched but no change was needed (already applied or file already exists)." }
16684
17007
  };
16685
17008
  }
16686
- const absoluteTarget = (0, import_node_path26.join)(options.cwd, dispatched.targetFile);
17009
+ const absoluteTarget = (0, import_node_path27.join)(options.cwd, dispatched.targetFile);
16687
17010
  assertSafeHealTarget(options.cwd, dispatched.targetFile);
16688
- await (0, import_promises18.mkdir)((0, import_node_path26.dirname)(absoluteTarget), { recursive: true });
17011
+ await (0, import_promises18.mkdir)((0, import_node_path27.dirname)(absoluteTarget), { recursive: true });
16689
17012
  const created2 = await createHealDir(options.cwd);
16690
17013
  id = created2.id;
16691
17014
  const healDir2 = created2.dir;
16692
- await (0, import_promises18.mkdir)((0, import_node_path26.join)(healDir2, "before"));
16693
- const existedBefore = (0, import_node_fs12.existsSync)(absoluteTarget);
17015
+ await (0, import_promises18.mkdir)((0, import_node_path27.join)(healDir2, "before"));
17016
+ const existedBefore = (0, import_node_fs13.existsSync)(absoluteTarget);
16694
17017
  const beforeContent = existedBefore ? await (0, import_promises18.readFile)(absoluteTarget, "utf8") : "";
16695
- await (0, import_promises18.writeFile)((0, import_node_path26.join)(healDir2, "before", "target.txt"), beforeContent, "utf8");
17018
+ await (0, import_promises18.writeFile)((0, import_node_path27.join)(healDir2, "before", "target.txt"), beforeContent, "utf8");
16696
17019
  await (0, import_promises18.writeFile)(absoluteTarget, dispatched.output, "utf8");
16697
17020
  const patch2 = [
16698
17021
  `--- ${dispatched.targetFile}`,
@@ -16703,7 +17026,7 @@ async function applyHeal(options) {
16703
17026
  dispatched.output,
16704
17027
  ""
16705
17028
  ].join("\n");
16706
- await (0, import_promises18.writeFile)((0, import_node_path26.join)(healDir2, "patch.diff"), patch2, "utf8");
17029
+ await (0, import_promises18.writeFile)((0, import_node_path27.join)(healDir2, "patch.diff"), patch2, "utf8");
16707
17030
  const result2 = {
16708
17031
  $schema: "https://viberaven.dev/schemas/heal-result.schema.json",
16709
17032
  schemaVersion: "v1",
@@ -16715,17 +17038,17 @@ async function applyHeal(options) {
16715
17038
  recipe: dispatched.recipeName,
16716
17039
  ...dispatched.warnings?.length ? { warnings: dispatched.warnings } : {},
16717
17040
  target: dispatched.targetFile,
16718
- changedFiles: [(0, import_node_path26.relative)(options.cwd, absoluteTarget).replace(/\\/g, "/")],
17041
+ changedFiles: [(0, import_node_path27.relative)(options.cwd, absoluteTarget).replace(/\\/g, "/")],
16719
17042
  artifacts: {
16720
17043
  patch: `.viberaven/heal/${id}/patch.diff`,
16721
17044
  result: `.viberaven/heal/${id}/result.json`
16722
17045
  },
16723
17046
  rollback: {
16724
17047
  available: true,
16725
- instructions: rollbackInstructions(id, (0, import_node_path26.relative)(options.cwd, absoluteTarget).replace(/\\/g, "/"), existedBefore)
17048
+ instructions: rollbackInstructions(id, (0, import_node_path27.relative)(options.cwd, absoluteTarget).replace(/\\/g, "/"), existedBefore)
16726
17049
  }
16727
17050
  };
16728
- await (0, import_promises18.writeFile)((0, import_node_path26.join)(healDir2, "result.json"), `${JSON.stringify(result2, null, 2)}
17051
+ await (0, import_promises18.writeFile)((0, import_node_path27.join)(healDir2, "result.json"), `${JSON.stringify(result2, null, 2)}
16729
17052
  `, "utf8");
16730
17053
  return result2;
16731
17054
  }
@@ -16765,8 +17088,8 @@ async function applyHeal(options) {
16765
17088
  const created = await createHealDir(options.cwd);
16766
17089
  id = created.id;
16767
17090
  const healDir = created.dir;
16768
- await (0, import_promises18.mkdir)((0, import_node_path26.join)(healDir, "before"));
16769
- await (0, import_promises18.writeFile)((0, import_node_path26.join)(healDir, "before", "target.txt"), before, "utf8");
17091
+ await (0, import_promises18.mkdir)((0, import_node_path27.join)(healDir, "before"));
17092
+ await (0, import_promises18.writeFile)((0, import_node_path27.join)(healDir, "before", "target.txt"), before, "utf8");
16770
17093
  await (0, import_promises18.writeFile)(absolute, recipe.output, "utf8");
16771
17094
  const patch = [
16772
17095
  `--- ${options.target}`,
@@ -16777,7 +17100,7 @@ async function applyHeal(options) {
16777
17100
  recipe.output,
16778
17101
  ""
16779
17102
  ].join("\n");
16780
- await (0, import_promises18.writeFile)((0, import_node_path26.join)(healDir, "patch.diff"), patch, "utf8");
17103
+ await (0, import_promises18.writeFile)((0, import_node_path27.join)(healDir, "patch.diff"), patch, "utf8");
16781
17104
  const result = {
16782
17105
  $schema: "https://viberaven.dev/schemas/heal-result.schema.json",
16783
17106
  schemaVersion: "v1",
@@ -16788,7 +17111,7 @@ async function applyHeal(options) {
16788
17111
  gapId: options.gapId,
16789
17112
  recipe: "empty-catch-safe-response",
16790
17113
  target: options.target,
16791
- changedFiles: [(0, import_node_path26.relative)(options.cwd, absolute).replace(/\\/g, "/")],
17114
+ changedFiles: [(0, import_node_path27.relative)(options.cwd, absolute).replace(/\\/g, "/")],
16792
17115
  artifacts: {
16793
17116
  patch: `.viberaven/heal/${id}/patch.diff`,
16794
17117
  result: `.viberaven/heal/${id}/result.json`
@@ -16796,20 +17119,20 @@ async function applyHeal(options) {
16796
17119
  rollback: {
16797
17120
  available: true,
16798
17121
  // The empty-catch recipe only edits a file that already exists.
16799
- instructions: rollbackInstructions(id, (0, import_node_path26.relative)(options.cwd, absolute).replace(/\\/g, "/"), true)
17122
+ instructions: rollbackInstructions(id, (0, import_node_path27.relative)(options.cwd, absolute).replace(/\\/g, "/"), true)
16800
17123
  }
16801
17124
  };
16802
- await (0, import_promises18.writeFile)((0, import_node_path26.join)(healDir, "result.json"), `${JSON.stringify(result, null, 2)}
17125
+ await (0, import_promises18.writeFile)((0, import_node_path27.join)(healDir, "result.json"), `${JSON.stringify(result, null, 2)}
16803
17126
  `, "utf8");
16804
17127
  return result;
16805
17128
  }
16806
- var import_promises18, import_node_fs12, import_node_path26;
17129
+ var import_promises18, import_node_fs13, import_node_path27;
16807
17130
  var init_apply = __esm({
16808
17131
  "src/heal/apply.ts"() {
16809
17132
  "use strict";
16810
17133
  import_promises18 = require("node:fs/promises");
16811
- import_node_fs12 = require("node:fs");
16812
- import_node_path26 = require("node:path");
17134
+ import_node_fs13 = require("node:fs");
17135
+ import_node_path27 = require("node:path");
16813
17136
  init_pathSafety();
16814
17137
  init_emptyCatch();
16815
17138
  init_healId();
@@ -16819,7 +17142,7 @@ var init_apply = __esm({
16819
17142
 
16820
17143
  // src/heal/plan.ts
16821
17144
  async function writeHealPlan(options) {
16822
- const dir = (0, import_node_path27.join)(options.cwd, ".viberaven");
17145
+ const dir = (0, import_node_path28.join)(options.cwd, ".viberaven");
16823
17146
  await (0, import_promises19.mkdir)(dir, { recursive: true });
16824
17147
  const id = newHealId();
16825
17148
  const target = options.target ?? `gap:${options.gapId}`;
@@ -16872,18 +17195,18 @@ async function writeHealPlan(options) {
16872
17195
  artifacts: { plan: ".viberaven/heal-plan.md" },
16873
17196
  rollback: { available: false, instructions: "No source files were changed." }
16874
17197
  };
16875
- await (0, import_promises19.writeFile)((0, import_node_path27.join)(dir, "heal-plan.md"), markdown, "utf8");
16876
- await (0, import_promises19.writeFile)((0, import_node_path27.join)(dir, "heal-plan.json"), `${JSON.stringify(result, null, 2)}
17198
+ await (0, import_promises19.writeFile)((0, import_node_path28.join)(dir, "heal-plan.md"), markdown, "utf8");
17199
+ await (0, import_promises19.writeFile)((0, import_node_path28.join)(dir, "heal-plan.json"), `${JSON.stringify(result, null, 2)}
16877
17200
  `, "utf8");
16878
17201
  return result;
16879
17202
  }
16880
- var import_promises19, import_node_path27;
17203
+ var import_promises19, import_node_path28;
16881
17204
  var init_plan = __esm({
16882
17205
  "src/heal/plan.ts"() {
16883
17206
  "use strict";
16884
17207
  import_promises19 = require("node:fs/promises");
16885
17208
  init_healId();
16886
- import_node_path27 = require("node:path");
17209
+ import_node_path28 = require("node:path");
16887
17210
  init_commands();
16888
17211
  init_supabaseRls2();
16889
17212
  }
@@ -16891,7 +17214,7 @@ var init_plan = __esm({
16891
17214
 
16892
17215
  // src/heal/prompt.ts
16893
17216
  async function writeHealPrompt(options) {
16894
- const dir = (0, import_node_path28.join)(options.cwd, ".viberaven");
17217
+ const dir = (0, import_node_path29.join)(options.cwd, ".viberaven");
16895
17218
  await (0, import_promises20.mkdir)(dir, { recursive: true });
16896
17219
  const id = newHealId();
16897
17220
  const target = options.target ?? `gap:${options.gapId}`;
@@ -16919,16 +17242,16 @@ async function writeHealPrompt(options) {
16919
17242
  artifacts: { prompt: ".viberaven/heal-prompt.md" },
16920
17243
  rollback: { available: false, instructions: "No source files were changed." }
16921
17244
  };
16922
- await (0, import_promises20.writeFile)((0, import_node_path28.join)(dir, "heal-prompt.md"), prompt, "utf8");
17245
+ await (0, import_promises20.writeFile)((0, import_node_path29.join)(dir, "heal-prompt.md"), prompt, "utf8");
16923
17246
  return result;
16924
17247
  }
16925
- var import_promises20, import_node_path28;
17248
+ var import_promises20, import_node_path29;
16926
17249
  var init_prompt = __esm({
16927
17250
  "src/heal/prompt.ts"() {
16928
17251
  "use strict";
16929
17252
  import_promises20 = require("node:fs/promises");
16930
17253
  init_healId();
16931
- import_node_path28 = require("node:path");
17254
+ import_node_path29 = require("node:path");
16932
17255
  init_commands();
16933
17256
  }
16934
17257
  });
@@ -17031,8 +17354,8 @@ var init_heal = __esm({
17031
17354
 
17032
17355
  // src/stackRecommend.ts
17033
17356
  async function recommendStack(cwd = process.cwd()) {
17034
- const pkgPath = (0, import_node_path29.join)(cwd, "package.json");
17035
- if (!(0, import_node_fs13.existsSync)(pkgPath)) {
17357
+ const pkgPath = (0, import_node_path30.join)(cwd, "package.json");
17358
+ if (!(0, import_node_fs14.existsSync)(pkgPath)) {
17036
17359
  return DEFAULT_STACK;
17037
17360
  }
17038
17361
  const pkg = JSON.parse(await (0, import_promises22.readFile)(pkgPath, "utf-8"));
@@ -17063,13 +17386,13 @@ async function recommendStack(cwd = process.cwd()) {
17063
17386
  }
17064
17387
  return rec;
17065
17388
  }
17066
- var import_promises22, import_node_fs13, import_node_path29, DEFAULT_STACK;
17389
+ var import_promises22, import_node_fs14, import_node_path30, DEFAULT_STACK;
17067
17390
  var init_stackRecommend = __esm({
17068
17391
  "src/stackRecommend.ts"() {
17069
17392
  "use strict";
17070
17393
  import_promises22 = require("node:fs/promises");
17071
- import_node_fs13 = require("node:fs");
17072
- import_node_path29 = require("node:path");
17394
+ import_node_fs14 = require("node:fs");
17395
+ import_node_path30 = require("node:path");
17073
17396
  DEFAULT_STACK = {
17074
17397
  frontend: "react",
17075
17398
  ui: "tailwind + shadcn/ui",
@@ -17212,13 +17535,13 @@ async function readBlobs(cwd, shas) {
17212
17535
  async function exportCommit(cwd, commit) {
17213
17536
  const entries = parseTree(await git(cwd, ["ls-tree", "-r", "-z", "-l", "--full-tree", commit])).filter(wanted);
17214
17537
  const blobs = await readBlobs(cwd, [...new Set(entries.map((entry) => entry.sha))]);
17215
- const root = await (0, import_promises23.mkdtemp)((0, import_node_path30.join)((0, import_node_os.tmpdir)(), "viberaven-base-"));
17538
+ const root = await (0, import_promises23.mkdtemp)((0, import_node_path31.join)((0, import_node_os.tmpdir)(), "viberaven-base-"));
17216
17539
  for (const entry of entries) {
17217
- const target = (0, import_node_path30.resolve)(root, ...entry.path.split("/"));
17218
- if (!target.startsWith(root + import_node_path30.sep)) continue;
17540
+ const target = (0, import_node_path31.resolve)(root, ...entry.path.split("/"));
17541
+ if (!target.startsWith(root + import_node_path31.sep)) continue;
17219
17542
  const content = blobs.get(entry.sha);
17220
17543
  if (!content) continue;
17221
- await (0, import_promises23.mkdir)((0, import_node_path30.dirname)(target), { recursive: true });
17544
+ await (0, import_promises23.mkdir)((0, import_node_path31.dirname)(target), { recursive: true });
17222
17545
  await (0, import_promises23.writeFile)(target, content);
17223
17546
  }
17224
17547
  return root;
@@ -17288,14 +17611,14 @@ function renderCheckMarkdown({ head, change, version, score }) {
17288
17611
  return `${lines.join("\n")}
17289
17612
  `;
17290
17613
  }
17291
- 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;
17614
+ var import_node_child_process6, import_promises23, import_node_os, import_node_path31, import_node_util4, execFileAsync4, MAX_BLOB_BYTES, SKIP_SEGMENTS, SEVERITY_RANK3, LOCATION_TEXT, SUBJECT, COMMENT_MARKER, DOT;
17292
17615
  var init_checkChange = __esm({
17293
17616
  "src/commands/checkChange.ts"() {
17294
17617
  "use strict";
17295
17618
  import_node_child_process6 = require("node:child_process");
17296
17619
  import_promises23 = require("node:fs/promises");
17297
17620
  import_node_os = require("node:os");
17298
- import_node_path30 = require("node:path");
17621
+ import_node_path31 = require("node:path");
17299
17622
  import_node_util4 = require("node:util");
17300
17623
  init_runScan();
17301
17624
  init_safeGit();
@@ -17379,12 +17702,12 @@ async function hasGithubRemote(cwd) {
17379
17702
  }
17380
17703
  }
17381
17704
  async function githubWorkflowStatus(cwd) {
17382
- return { exists: (0, import_node_fs14.existsSync)((0, import_node_path31.join)(cwd, WORKFLOW_PATH)), path: DISPLAY_PATH, onGithub: await hasGithubRemote(cwd) };
17705
+ return { exists: (0, import_node_fs15.existsSync)((0, import_node_path32.join)(cwd, WORKFLOW_PATH)), path: DISPLAY_PATH, onGithub: await hasGithubRemote(cwd) };
17383
17706
  }
17384
17707
  async function writeGithubWorkflow(cwd, options = {}) {
17385
- const target = (0, import_node_path31.join)(cwd, WORKFLOW_PATH);
17386
- if ((0, import_node_fs14.existsSync)(target) && !options.force) return { written: false, status: await githubWorkflowStatus(cwd) };
17387
- await (0, import_promises24.mkdir)((0, import_node_path31.dirname)(target), { recursive: true });
17708
+ const target = (0, import_node_path32.join)(cwd, WORKFLOW_PATH);
17709
+ if ((0, import_node_fs15.existsSync)(target) && !options.force) return { written: false, status: await githubWorkflowStatus(cwd) };
17710
+ await (0, import_promises24.mkdir)((0, import_node_path32.dirname)(target), { recursive: true });
17388
17711
  await (0, import_promises24.writeFile)(target, renderWorkflow(), "utf8");
17389
17712
  return { written: true, status: await githubWorkflowStatus(cwd) };
17390
17713
  }
@@ -17409,20 +17732,20 @@ async function runInitGithubCommand(options) {
17409
17732
  if (!status.onGithub) console.log("This repository has no github.com remote yet: the workflow runs once it is pushed to GitHub.");
17410
17733
  return 0;
17411
17734
  }
17412
- var import_node_child_process7, import_node_fs14, import_promises24, import_node_path31, import_node_util5, execFileAsync5, WORKFLOW_PATH, DISPLAY_PATH;
17735
+ var import_node_child_process7, import_node_fs15, import_promises24, import_node_path32, import_node_util5, execFileAsync5, WORKFLOW_PATH, DISPLAY_PATH;
17413
17736
  var init_initGithub = __esm({
17414
17737
  "src/commands/initGithub.ts"() {
17415
17738
  "use strict";
17416
17739
  import_node_child_process7 = require("node:child_process");
17417
- import_node_fs14 = require("node:fs");
17740
+ import_node_fs15 = require("node:fs");
17418
17741
  import_promises24 = require("node:fs/promises");
17419
- import_node_path31 = require("node:path");
17742
+ import_node_path32 = require("node:path");
17420
17743
  import_node_util5 = require("node:util");
17421
17744
  init_safeGit();
17422
17745
  init_version();
17423
17746
  init_checkChange();
17424
17747
  execFileAsync5 = (0, import_node_util5.promisify)(import_node_child_process7.execFile);
17425
- WORKFLOW_PATH = (0, import_node_path31.join)(".github", "workflows", "viberaven.yml");
17748
+ WORKFLOW_PATH = (0, import_node_path32.join)(".github", "workflows", "viberaven.yml");
17426
17749
  DISPLAY_PATH = ".github/workflows/viberaven.yml";
17427
17750
  }
17428
17751
  });
@@ -17446,7 +17769,7 @@ function managedBlocks(content) {
17446
17769
  async function checkAgentInjection(cwd) {
17447
17770
  const checks = [];
17448
17771
  for (const item4 of REQUIRED_EXISTENCE_CHECKS) {
17449
- const exists = await fileExists2((0, import_node_path32.join)(cwd, item4.file));
17772
+ const exists = await fileExists2((0, import_node_path33.join)(cwd, item4.file));
17450
17773
  checks.push({
17451
17774
  id: item4.id,
17452
17775
  status: exists ? "pass" : "fail",
@@ -17454,17 +17777,17 @@ async function checkAgentInjection(cwd) {
17454
17777
  });
17455
17778
  }
17456
17779
  for (const item4 of OPTIONAL_CURSOR_PACK_CHECKS) {
17457
- const exists = await fileExists2((0, import_node_path32.join)(cwd, item4.file));
17780
+ const exists = await fileExists2((0, import_node_path33.join)(cwd, item4.file));
17458
17781
  checks.push({
17459
17782
  id: item4.id,
17460
17783
  status: exists ? "pass" : "fail",
17461
17784
  message: exists ? `${item4.file} exists` : `Missing ${item4.file} \u2014 run npx -y viberaven init --agents all`
17462
17785
  });
17463
17786
  }
17464
- const legacyCursorPath = (0, import_node_path32.join)(cwd, ".cursor/rules/viberaven.mdc");
17787
+ const legacyCursorPath = (0, import_node_path33.join)(cwd, ".cursor/rules/viberaven.mdc");
17465
17788
  if (await fileExists2(legacyCursorPath)) {
17466
17789
  const legacyContent = await (0, import_promises25.readFile)(legacyCursorPath, "utf-8");
17467
- const hasCoreSplit = await fileExists2((0, import_node_path32.join)(cwd, ".cursor/rules/viberaven-core.mdc"));
17790
+ const hasCoreSplit = await fileExists2((0, import_node_path33.join)(cwd, ".cursor/rules/viberaven-core.mdc"));
17468
17791
  if (!hasCoreSplit || legacyContent.includes("alwaysApply: true")) {
17469
17792
  checks.push({
17470
17793
  id: "cursor-legacy-mdc",
@@ -17475,7 +17798,7 @@ async function checkAgentInjection(cwd) {
17475
17798
  }
17476
17799
  for (const target of CORE_AGENT_INJECTION_TARGETS) {
17477
17800
  const file = target === "cursor" ? ".cursor/rules/viberaven-core.mdc" : AGENT_RULE_TARGETS[target].file;
17478
- const path = (0, import_node_path32.join)(cwd, file);
17801
+ const path = (0, import_node_path33.join)(cwd, file);
17479
17802
  const exists = await fileExists2(path);
17480
17803
  if (!exists) {
17481
17804
  checks.push({
@@ -17534,12 +17857,12 @@ async function fileExists2(path) {
17534
17857
  return false;
17535
17858
  }
17536
17859
  }
17537
- var import_promises25, import_node_path32, REQUIRED_EXISTENCE_CHECKS, OPTIONAL_CURSOR_PACK_CHECKS, STALE_PATTERNS, GATE_RULE_PHRASES_1_5_2;
17860
+ var import_promises25, import_node_path33, REQUIRED_EXISTENCE_CHECKS, OPTIONAL_CURSOR_PACK_CHECKS, STALE_PATTERNS, GATE_RULE_PHRASES_1_5_2;
17538
17861
  var init_doctorAgents = __esm({
17539
17862
  "src/commands/doctorAgents.ts"() {
17540
17863
  "use strict";
17541
17864
  import_promises25 = require("node:fs/promises");
17542
- import_node_path32 = require("node:path");
17865
+ import_node_path33 = require("node:path");
17543
17866
  init_commands();
17544
17867
  init_agentTargets();
17545
17868
  REQUIRED_EXISTENCE_CHECKS = [
@@ -17810,7 +18133,7 @@ function buildContextCleanupPlan(input) {
17810
18133
  const items = [];
17811
18134
  const seen = /* @__PURE__ */ new Set();
17812
18135
  for (const file of input.files) {
17813
- const path = normalizePath5((0, import_node_path33.relative)(input.projectRoot, file.path) || file.path);
18136
+ const path = normalizePath5((0, import_node_path34.relative)(input.projectRoot, file.path) || file.path);
17814
18137
  const fullPath = normalizePath5(file.path);
17815
18138
  const category = categorizeCleanupFile(path, fullPath, file);
17816
18139
  if (!category || seen.has(path)) {
@@ -17836,8 +18159,8 @@ async function collectCleanupFiles(projectRoot) {
17836
18159
  return files;
17837
18160
  }
17838
18161
  async function writeCleanupPlan(projectRoot, plan) {
17839
- const outputDir = (0, import_node_path33.join)(projectRoot, ".viberaven");
17840
- const outputPath = (0, import_node_path33.join)(outputDir, "context-cleanup.md");
18162
+ const outputDir = (0, import_node_path34.join)(projectRoot, ".viberaven");
18163
+ const outputPath = (0, import_node_path34.join)(outputDir, "context-cleanup.md");
17841
18164
  await (0, import_promises26.mkdir)(outputDir, { recursive: true });
17842
18165
  await (0, import_promises26.writeFile)(outputPath, renderCleanupPlan(plan), "utf8");
17843
18166
  return outputPath;
@@ -17895,9 +18218,9 @@ async function walkCleanupFiles(root, dir, result, depth) {
17895
18218
  if (SKIP_DIRECTORIES.has(entry.name)) {
17896
18219
  continue;
17897
18220
  }
17898
- const absolute = (0, import_node_path33.join)(dir, entry.name);
18221
+ const absolute = (0, import_node_path34.join)(dir, entry.name);
17899
18222
  if (entry.isDirectory()) {
17900
- if (isCachePath(normalizePath5((0, import_node_path33.relative)(root, absolute)))) {
18223
+ if (isCachePath(normalizePath5((0, import_node_path34.relative)(root, absolute)))) {
17901
18224
  result.push({ path: absolute, sizeBytes: 0, kind: "directory" });
17902
18225
  continue;
17903
18226
  }
@@ -17918,12 +18241,12 @@ async function walkCleanupFiles(root, dir, result, depth) {
17918
18241
  function normalizePath5(path) {
17919
18242
  return path.replace(/\\/g, "/");
17920
18243
  }
17921
- var import_promises26, import_node_path33, MAX_WALK_DEPTH, LARGE_LOG_BYTES, SKIP_DIRECTORIES;
18244
+ var import_promises26, import_node_path34, MAX_WALK_DEPTH, LARGE_LOG_BYTES, SKIP_DIRECTORIES;
17922
18245
  var init_cleanPlan = __esm({
17923
18246
  "src/commands/cleanPlan.ts"() {
17924
18247
  "use strict";
17925
18248
  import_promises26 = require("node:fs/promises");
17926
- import_node_path33 = require("node:path");
18249
+ import_node_path34 = require("node:path");
17927
18250
  MAX_WALK_DEPTH = 5;
17928
18251
  LARGE_LOG_BYTES = 8192;
17929
18252
  SKIP_DIRECTORIES = /* @__PURE__ */ new Set([".git", "node_modules"]);
@@ -18001,6 +18324,9 @@ function renderVerifyCommand(action) {
18001
18324
  }
18002
18325
  return ["Verify:", "```bash", redact(action.verifyCommand), "```"];
18003
18326
  }
18327
+ function renderUserSteps(steps) {
18328
+ return steps?.length ? ["Outside the repo (tell the user):", ...steps.map((step) => `- ${redact(step)}`)] : [];
18329
+ }
18004
18330
  function renderAction(action) {
18005
18331
  return [
18006
18332
  `[${action.id}] ${redact(action.title)}`,
@@ -18009,6 +18335,7 @@ function renderAction(action) {
18009
18335
  ...renderTarget(action.target),
18010
18336
  ...renderCopyPayload(action),
18011
18337
  ...renderVerifyCommand(action),
18338
+ ...renderUserSteps(action.userSteps),
18012
18339
  action.resumeInstruction ? `Resume: "${redact(action.resumeInstruction)}"` : void 0
18013
18340
  ].filter((line) => Boolean(line));
18014
18341
  }
@@ -18024,6 +18351,9 @@ function renderActionSurface(manifest, options = {}) {
18024
18351
  for (const action of visibleActions) {
18025
18352
  lines.push(...renderAction(action), "");
18026
18353
  }
18354
+ for (const followUp of options.followUps === false ? [] : manifest.userFollowUps ?? []) {
18355
+ lines.push(`[${followUp.id}] ${redact(followUp.title ?? followUp.gapId ?? "Fixed in the repo")}`, "Status: resolved in the repo", ...renderUserSteps(followUp.userSteps), "");
18356
+ }
18027
18357
  return `${lines.join("\n").trimEnd()}
18028
18358
  `;
18029
18359
  }
@@ -18036,7 +18366,7 @@ var init_render = __esm({
18036
18366
 
18037
18367
  // src/commands/actions.ts
18038
18368
  async function runActionsCommand(input) {
18039
- const actionsPath = (0, import_node_path34.join)(getProjectArtifactsDir(input.cwd), "actions.json");
18369
+ const actionsPath = (0, import_node_path35.join)(getProjectArtifactsDir(input.cwd), "actions.json");
18040
18370
  let raw;
18041
18371
  try {
18042
18372
  raw = await (0, import_promises27.readFile)(actionsPath, "utf8");
@@ -18053,12 +18383,12 @@ async function runActionsCommand(input) {
18053
18383
  process.stdout.write(renderActionSurface(manifest));
18054
18384
  return 0;
18055
18385
  }
18056
- var import_promises27, import_node_path34;
18386
+ var import_promises27, import_node_path35;
18057
18387
  var init_actions = __esm({
18058
18388
  "src/commands/actions.ts"() {
18059
18389
  "use strict";
18060
18390
  import_promises27 = require("node:fs/promises");
18061
- import_node_path34 = require("node:path");
18391
+ import_node_path35 = require("node:path");
18062
18392
  init_render();
18063
18393
  init_config();
18064
18394
  }
@@ -18067,7 +18397,7 @@ var init_actions = __esm({
18067
18397
  // src/commands/verifyAction.ts
18068
18398
  async function currentActionExists(cwd, actionId) {
18069
18399
  try {
18070
- const raw = await (0, import_promises28.readFile)((0, import_node_path35.join)(getProjectArtifactsDir(cwd), "actions.json"), "utf8");
18400
+ const raw = await (0, import_promises28.readFile)((0, import_node_path36.join)(getProjectArtifactsDir(cwd), "actions.json"), "utf8");
18071
18401
  const manifest = JSON.parse(raw);
18072
18402
  return manifest.actions.some((action) => action.id === actionId);
18073
18403
  } catch {
@@ -18111,12 +18441,12 @@ This only looks the action up. Run \`npx -y viberaven --verify\` to scan the rep
18111
18441
  );
18112
18442
  return 0;
18113
18443
  }
18114
- var import_promises28, import_node_path35;
18444
+ var import_promises28, import_node_path36;
18115
18445
  var init_verifyAction = __esm({
18116
18446
  "src/commands/verifyAction.ts"() {
18117
18447
  "use strict";
18118
18448
  import_promises28 = require("node:fs/promises");
18119
- import_node_path35 = require("node:path");
18449
+ import_node_path36 = require("node:path");
18120
18450
  init_registry2();
18121
18451
  init_config();
18122
18452
  }
@@ -18271,6 +18601,7 @@ ${message}
18271
18601
  return failing ? 1 : 0;
18272
18602
  }
18273
18603
  if (options.json) {
18604
+ const userFollowUps = await readUserFollowUps(paths.actionsPath);
18274
18605
  console.log(
18275
18606
  JSON.stringify(
18276
18607
  {
@@ -18293,6 +18624,8 @@ ${message}
18293
18624
  unchanged: change.unchanged.map((gap) => gap.id)
18294
18625
  }
18295
18626
  } : {},
18627
+ // Steps outside the repo for fixes already in it (MCP check and verify read this). Read only.
18628
+ ...userFollowUps.length > 0 ? { userFollowUps } : {},
18296
18629
  artifacts: paths
18297
18630
  },
18298
18631
  null,
@@ -18347,14 +18680,31 @@ ${message}
18347
18680
  console.log(
18348
18681
  critical > 0 ? `Verdict: \u274C ${critical} blocker${critical === 1 ? "" : "s"}, ${warnings} warning${warnings === 1 ? "" : "s"} \xB7 score ${artifact.score}` : `Verdict: \u2705 no blockers, ${warnings} warning${warnings === 1 ? "" : "s"} \xB7 score ${artifact.score}`
18349
18682
  );
18683
+ await printUserFollowUps(paths.actionsPath);
18350
18684
  console.log(`Fix: viberaven fix \xB7 Details: ${paths.tasklistPath}`);
18351
18685
  console.log("");
18352
18686
  return critical > 0 ? 1 : 0;
18353
18687
  }
18354
- var SEVERITY_ICON, SEVERITY_ORDER3;
18688
+ async function readUserFollowUps(actionsPath) {
18689
+ try {
18690
+ return JSON.parse(await (0, import_promises29.readFile)(actionsPath, "utf8")).userFollowUps ?? [];
18691
+ } catch {
18692
+ return [];
18693
+ }
18694
+ }
18695
+ async function printUserFollowUps(actionsPath) {
18696
+ const followUps = await readUserFollowUps(actionsPath);
18697
+ if (followUps.length === 0) return;
18698
+ console.log("Outside the repo, still on you:");
18699
+ for (const followUp of followUps) {
18700
+ for (const step of followUp.userSteps) console.log(` - ${step} (${followUp.gapId ?? followUp.id})`);
18701
+ }
18702
+ }
18703
+ var import_promises29, SEVERITY_ICON, SEVERITY_ORDER3;
18355
18704
  var init_check = __esm({
18356
18705
  "src/commands/check.ts"() {
18357
18706
  "use strict";
18707
+ import_promises29 = require("node:fs/promises");
18358
18708
  init_runScan();
18359
18709
  init_artifacts2();
18360
18710
  init_statusLabels();
@@ -18803,8 +19153,8 @@ function isSqlError(error) {
18803
19153
  }
18804
19154
  function displayPath(projectDir, path) {
18805
19155
  for (const base of [projectDir, process.cwd()]) {
18806
- const rel = (0, import_node_path36.relative)(base, path);
18807
- if (rel && !rel.startsWith("..") && !(0, import_node_path36.isAbsolute)(rel)) return rel.split("\\").join("/");
19156
+ const rel = (0, import_node_path37.relative)(base, path);
19157
+ if (rel && !rel.startsWith("..") && !(0, import_node_path37.isAbsolute)(rel)) return rel.split("\\").join("/");
18808
19158
  }
18809
19159
  return path;
18810
19160
  }
@@ -19408,26 +19758,26 @@ function verdictFor(summary) {
19408
19758
  }
19409
19759
  async function listMigrations(dir) {
19410
19760
  try {
19411
- const entries = await (0, import_promises29.readdir)(dir, { withFileTypes: true });
19761
+ const entries = await (0, import_promises30.readdir)(dir, { withFileTypes: true });
19412
19762
  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);
19413
19763
  } catch {
19414
19764
  return [];
19415
19765
  }
19416
19766
  }
19417
19767
  async function runRlsTest(options) {
19418
- const projectDir = (0, import_node_path36.resolve)(options.projectDir);
19419
- const matrixPath = (0, import_node_path36.resolve)(projectDir, options.matrixPath ?? DEFAULT_MATRIX_FILE);
19420
- const migrationsDir = (0, import_node_path36.resolve)(projectDir, options.migrationsDir ?? (0, import_node_path36.join)("supabase", "migrations"));
19768
+ const projectDir = (0, import_node_path37.resolve)(options.projectDir);
19769
+ const matrixPath = (0, import_node_path37.resolve)(projectDir, options.matrixPath ?? DEFAULT_MATRIX_FILE);
19770
+ const migrationsDir = (0, import_node_path37.resolve)(projectDir, options.migrationsDir ?? (0, import_node_path37.join)("supabase", "migrations"));
19421
19771
  const timeouts = { ...DEFAULT_TIMEOUTS, ...options.timeouts };
19422
- if ((0, import_node_path36.basename)(matrixPath).toLowerCase().startsWith(".env")) {
19423
- 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.`);
19772
+ if ((0, import_node_path37.basename)(matrixPath).toLowerCase().startsWith(".env")) {
19773
+ throw new RlsInputError(`Refusing to read ${(0, import_node_path37.basename)(matrixPath)}: rls-test never reads .env files. Point --matrix at a viberaven.permissions.json.`);
19424
19774
  }
19425
19775
  const filesRead = [];
19426
19776
  let matrixText;
19427
19777
  try {
19428
- const info = await (0, import_promises29.stat)(matrixPath);
19778
+ const info = await (0, import_promises30.stat)(matrixPath);
19429
19779
  if (!info.isFile()) throw new Error("not a file");
19430
- matrixText = normalise(await (0, import_promises29.readFile)(matrixPath, "utf8"));
19780
+ matrixText = normalise(await (0, import_promises30.readFile)(matrixPath, "utf8"));
19431
19781
  } catch {
19432
19782
  throw new RlsInputError(
19433
19783
  `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.`
@@ -19438,8 +19788,8 @@ async function runRlsTest(options) {
19438
19788
  const names = await listMigrations(migrationsDir);
19439
19789
  const migrationFiles = [];
19440
19790
  for (const name of names) {
19441
- const sql = normalise(await (0, import_promises29.readFile)((0, import_node_path36.join)(migrationsDir, name), "utf8"));
19442
- filesRead.push(displayPath(projectDir, (0, import_node_path36.join)(migrationsDir, name)));
19791
+ const sql = normalise(await (0, import_promises30.readFile)((0, import_node_path37.join)(migrationsDir, name), "utf8"));
19792
+ filesRead.push(displayPath(projectDir, (0, import_node_path37.join)(migrationsDir, name)));
19443
19793
  migrationFiles.push({ name, sha256: sha256(sql), sql });
19444
19794
  }
19445
19795
  const migrationsHash = sha256(migrationFiles.map((f) => `${f.name}\0${f.sha256}`).join("\n"));
@@ -19639,15 +19989,15 @@ async function runTable(db, table2, badRoles, roleChecks) {
19639
19989
  cells
19640
19990
  };
19641
19991
  }
19642
- 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;
19992
+ var import_node_child_process8, import_node_crypto3, import_promises30, import_node_net, import_node_path37, 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;
19643
19993
  var init_rlsRunner = __esm({
19644
19994
  "src/rls/rlsRunner.ts"() {
19645
19995
  "use strict";
19646
19996
  import_node_child_process8 = require("node:child_process");
19647
19997
  import_node_crypto3 = require("node:crypto");
19648
- import_promises29 = require("node:fs/promises");
19998
+ import_promises30 = require("node:fs/promises");
19649
19999
  import_node_net = __toESM(require("node:net"));
19650
- import_node_path36 = require("node:path");
20000
+ import_node_path37 = require("node:path");
19651
20001
  import_node_util6 = require("node:util");
19652
20002
  init_safeGit();
19653
20003
  init_permissionsMatrix();
@@ -20475,7 +20825,7 @@ function buildMissionCards(artifact, providers) {
20475
20825
  };
20476
20826
  }
20477
20827
  function projectNameFromPath(pathValue) {
20478
- return (0, import_node_path37.basename)(pathValue.replace(/[\\/]$/, "")) || "current repo";
20828
+ return (0, import_node_path38.basename)(pathValue.replace(/[\\/]$/, "")) || "current repo";
20479
20829
  }
20480
20830
  function gapSearchText(gap) {
20481
20831
  return [
@@ -20666,7 +21016,7 @@ function mapProvider(template, artifact, projectName) {
20666
21016
  const gaps = providerGaps(artifact, template.id);
20667
21017
  const primaryGap2 = gaps[0];
20668
21018
  const state = providerState(gaps);
20669
- const launchGateClear = artifact.gaps.length === 0 && artifact.score >= 80;
21019
+ const launchGateClear = gateStatusForGaps(artifact.gaps) === "clear";
20670
21020
  const launchPath = template.items.map((item4, index) => ({
20671
21021
  ...item4,
20672
21022
  state: itemState(primaryGap2, index, item4, launchGateClear),
@@ -20875,8 +21225,9 @@ function mapArtifactToLocalUiState(artifact, presentProviderIds) {
20875
21225
  scannedAt: artifact.scannedAt
20876
21226
  },
20877
21227
  gate: {
20878
- status: artifact.gaps.length === 0 && artifact.score >= 80 ? "clear" : "not_clear",
20879
- label: artifact.gaps.length === 0 && artifact.score >= 80 ? "Ready" : "Gate not clear",
21228
+ // The same rule as gate-result.json, so the Studio never shows a different verdict.
21229
+ status: gateStatusForGaps(artifact.gaps),
21230
+ label: GATE_LABEL[gateStatusForGaps(artifact.gaps)],
20880
21231
  score: artifact.score,
20881
21232
  productionCorePercent: artifact.productionCorePercent
20882
21233
  },
@@ -20886,25 +21237,31 @@ function mapArtifactToLocalUiState(artifact, presentProviderIds) {
20886
21237
  selectedProviderId: selectedProvider.id,
20887
21238
  currentPrompt: selectedProvider.nextFix?.prompt ?? "",
20888
21239
  artifacts: {
20889
- tasklist: (0, import_node_path37.join)(".viberaven", "agent-tasklist.md"),
20890
- contextMap: (0, import_node_path37.join)(".viberaven", "context-map.json"),
20891
- gateResult: (0, import_node_path37.join)(".viberaven", "gate-result.json"),
20892
- lastScan: (0, import_node_path37.join)(".viberaven", "last-scan.json")
21240
+ tasklist: (0, import_node_path38.join)(".viberaven", "agent-tasklist.md"),
21241
+ contextMap: (0, import_node_path38.join)(".viberaven", "context-map.json"),
21242
+ gateResult: (0, import_node_path38.join)(".viberaven", "gate-result.json"),
21243
+ lastScan: (0, import_node_path38.join)(".viberaven", "last-scan.json")
20893
21244
  },
20894
21245
  empty: false
20895
21246
  };
20896
21247
  }
20897
- var import_node_path37, CONNECT_PROVIDER_PRIORITY, providerDashboardUrls, providerHints, PROVIDER_OWNED_GAP;
21248
+ var import_node_path38, GATE_LABEL, CONNECT_PROVIDER_PRIORITY, providerDashboardUrls, providerHints, PROVIDER_OWNED_GAP;
20898
21249
  var init_artifactAdapter = __esm({
20899
21250
  "src/local-ui/artifactAdapter.ts"() {
20900
21251
  "use strict";
20901
- import_node_path37 = require("node:path");
21252
+ import_node_path38 = require("node:path");
20902
21253
  init_providerLogos();
20903
21254
  init_buildTaskList();
20904
21255
  init_prompts();
20905
21256
  init_providerLaunchPaths();
20906
21257
  init_launchRunway();
20907
21258
  init_providerGrade();
21259
+ init_gateResult();
21260
+ GATE_LABEL = {
21261
+ clear: "No blockers in repo checks",
21262
+ warning: "Warnings to review",
21263
+ not_clear: "Gate not clear"
21264
+ };
20908
21265
  CONNECT_PROVIDER_PRIORITY = ["supabase", "vercel", "stripe"];
20909
21266
  providerDashboardUrls = {
20910
21267
  supabase: "https://supabase.com/dashboard/projects",
@@ -21201,18 +21558,18 @@ var init_planLimits = __esm({
21201
21558
  // src/hosted/credentials.ts
21202
21559
  function defaultConfigDir(env = process.env, platform = process.platform) {
21203
21560
  const override = env.VIBERAVEN_CONFIG_DIR?.trim();
21204
- if (override) return (0, import_node_path38.resolve)(override);
21561
+ if (override) return (0, import_node_path39.resolve)(override);
21205
21562
  if (platform === "win32") {
21206
- return (0, import_node_path38.join)(env.APPDATA?.trim() || (0, import_node_path38.join)((0, import_node_os2.homedir)(), "AppData", "Roaming"), "viberaven");
21563
+ return (0, import_node_path39.join)(env.APPDATA?.trim() || (0, import_node_path39.join)((0, import_node_os2.homedir)(), "AppData", "Roaming"), "viberaven");
21207
21564
  }
21208
21565
  if (platform === "darwin") {
21209
- return (0, import_node_path38.join)((0, import_node_os2.homedir)(), "Library", "Application Support", "viberaven");
21566
+ return (0, import_node_path39.join)((0, import_node_os2.homedir)(), "Library", "Application Support", "viberaven");
21210
21567
  }
21211
- return (0, import_node_path38.join)(env.XDG_CONFIG_HOME?.trim() || (0, import_node_path38.join)((0, import_node_os2.homedir)(), ".config"), "viberaven");
21568
+ return (0, import_node_path39.join)(env.XDG_CONFIG_HOME?.trim() || (0, import_node_path39.join)((0, import_node_os2.homedir)(), ".config"), "viberaven");
21212
21569
  }
21213
21570
  function isInside(child, parent) {
21214
- const rel = (0, import_node_path38.relative)((0, import_node_path38.resolve)(parent), (0, import_node_path38.resolve)(child));
21215
- return rel === "" || !rel.startsWith("..") && !(0, import_node_path38.isAbsolute)(rel);
21571
+ const rel = (0, import_node_path39.relative)((0, import_node_path39.resolve)(parent), (0, import_node_path39.resolve)(child));
21572
+ return rel === "" || !rel.startsWith("..") && !(0, import_node_path39.isAbsolute)(rel);
21216
21573
  }
21217
21574
  async function retryTransient(work) {
21218
21575
  for (let attempt = 1; ; attempt += 1) {
@@ -21225,15 +21582,15 @@ async function retryTransient(work) {
21225
21582
  }
21226
21583
  }
21227
21584
  }
21228
- 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;
21585
+ var import_node_child_process9, import_node_crypto4, import_promises31, import_node_os2, import_node_path39, CREDENTIALS_FILE_NAME, CredentialLocationError, CredentialPermissionError, restrictWithIcacls, TRANSIENT_FS_CODES, FS_ATTEMPTS, FS_BACKOFF_MS, CredentialStore;
21229
21586
  var init_credentials = __esm({
21230
21587
  "src/hosted/credentials.ts"() {
21231
21588
  "use strict";
21232
21589
  import_node_child_process9 = require("node:child_process");
21233
21590
  import_node_crypto4 = require("node:crypto");
21234
- import_promises30 = require("node:fs/promises");
21591
+ import_promises31 = require("node:fs/promises");
21235
21592
  import_node_os2 = require("node:os");
21236
- import_node_path38 = require("node:path");
21593
+ import_node_path39 = require("node:path");
21237
21594
  CREDENTIALS_FILE_NAME = "credentials.json";
21238
21595
  CredentialLocationError = class extends Error {
21239
21596
  };
@@ -21260,9 +21617,9 @@ var init_credentials = __esm({
21260
21617
  constructor(configDir, workspacePath, options = {}) {
21261
21618
  this.configDir = configDir;
21262
21619
  this.workspacePath = workspacePath;
21263
- this.path = (0, import_node_path38.join)(configDir, CREDENTIALS_FILE_NAME);
21620
+ this.path = (0, import_node_path39.join)(configDir, CREDENTIALS_FILE_NAME);
21264
21621
  this.restrict = options.restrict !== void 0 ? options.restrict : process.platform === "win32" ? restrictWithIcacls : null;
21265
- 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 };
21622
+ this.fs = { readFile: (path, encoding) => (0, import_promises31.readFile)(path, encoding), rename: (from, to) => (0, import_promises31.rename)(from, to), rm: (path) => (0, import_promises31.rm)(path, { force: true }), ...options.fs };
21266
21623
  }
21267
21624
  path;
21268
21625
  restrict;
@@ -21311,15 +21668,15 @@ var init_credentials = __esm({
21311
21668
  `Refusing to store the VibeRaven sign-in inside this project (${this.configDir}). Set VIBERAVEN_CONFIG_DIR to a folder outside the repo.`
21312
21669
  );
21313
21670
  }
21314
- await (0, import_promises30.mkdir)(this.configDir, { recursive: true, mode: 448 });
21671
+ await (0, import_promises31.mkdir)(this.configDir, { recursive: true, mode: 448 });
21315
21672
  const temp = `${this.path}.${(0, import_node_crypto4.randomUUID)()}.tmp`;
21316
- await (0, import_promises30.writeFile)(temp, `${JSON.stringify(body, null, 2)}
21673
+ await (0, import_promises31.writeFile)(temp, `${JSON.stringify(body, null, 2)}
21317
21674
  `, { encoding: "utf8", mode: 384 });
21318
- await (0, import_promises30.chmod)(temp, 384).catch(() => void 0);
21675
+ await (0, import_promises31.chmod)(temp, 384).catch(() => void 0);
21319
21676
  try {
21320
21677
  if (this.restrict) await this.restrict(temp);
21321
21678
  } catch (error) {
21322
- await (0, import_promises30.rm)(temp, { force: true });
21679
+ await (0, import_promises31.rm)(temp, { force: true });
21323
21680
  const code = error?.code;
21324
21681
  const toolMissing = code === "ENOENT";
21325
21682
  throw new CredentialPermissionError(
@@ -21330,17 +21687,17 @@ var init_credentials = __esm({
21330
21687
  if (guard) {
21331
21688
  const onDisk = await this.readFor(guard.apiBaseUrl);
21332
21689
  if (onDisk?.accessToken !== guard.accessToken || guard.isCurrent && !guard.isCurrent()) {
21333
- await (0, import_promises30.rm)(temp, { force: true });
21690
+ await (0, import_promises31.rm)(temp, { force: true });
21334
21691
  return false;
21335
21692
  }
21336
21693
  }
21337
21694
  try {
21338
21695
  await retryTransient(() => this.fs.rename(temp, this.path));
21339
21696
  } catch (error) {
21340
- await (0, import_promises30.rm)(temp, { force: true });
21697
+ await (0, import_promises31.rm)(temp, { force: true });
21341
21698
  throw error;
21342
21699
  }
21343
- await (0, import_promises30.chmod)(this.path, 384).catch(() => void 0);
21700
+ await (0, import_promises31.chmod)(this.path, 384).catch(() => void 0);
21344
21701
  return true;
21345
21702
  }
21346
21703
  /** Keeps the stored plan label in step with what the API last said. */
@@ -21709,7 +22066,7 @@ async function buildHostedCheckPayload(workspacePath) {
21709
22066
  throw new Error("Could not assemble the full check request.");
21710
22067
  }
21711
22068
  function finalizePayload(payload) {
21712
- const folder = (0, import_node_path39.basename)(payload.workspacePath.split("\\").join("/").replace(/\/+$/, "")) || "project";
22069
+ const folder = (0, import_node_path40.basename)(payload.workspacePath.split("\\").join("/").replace(/\/+$/, "")) || "project";
21713
22070
  const redacted = redactPayload({ ...payload, workspacePath: folder });
21714
22071
  return { payload: redacted.payload, redactions: redacted.count };
21715
22072
  }
@@ -21740,13 +22097,13 @@ function splitSections(prompt) {
21740
22097
  chars: section.length
21741
22098
  }));
21742
22099
  }
21743
- var import_node_path39, PayloadCaptured;
22100
+ var import_node_path40, PayloadCaptured;
21744
22101
  var init_payload = __esm({
21745
22102
  "src/hosted/payload.ts"() {
21746
22103
  "use strict";
21747
22104
  init_fileScanner();
21748
22105
  init_orchestrator();
21749
- import_node_path39 = require("node:path");
22106
+ import_node_path40 = require("node:path");
21750
22107
  init_config();
21751
22108
  init_redact();
21752
22109
  PayloadCaptured = class extends Error {
@@ -22212,10 +22569,10 @@ async function connectProviderMcp(cwd, providerId) {
22212
22569
  if (!recipe) {
22213
22570
  return { ok: false, message: `No known MCP server for ${providerId} yet. Add one manually to .mcp.json.` };
22214
22571
  }
22215
- const configPath = (0, import_node_path40.join)(cwd, ".mcp.json");
22572
+ const configPath = (0, import_node_path41.join)(cwd, ".mcp.json");
22216
22573
  let config = {};
22217
22574
  try {
22218
- const raw = await (0, import_promises31.readFile)(configPath, "utf8");
22575
+ const raw = await (0, import_promises32.readFile)(configPath, "utf8");
22219
22576
  const parsed = JSON.parse(raw.replace(/^\uFEFF/, ""));
22220
22577
  if (!isRecord5(parsed) || parsed.mcpServers !== void 0 && !isRecord5(parsed.mcpServers)) {
22221
22578
  return { ok: false, message: "Existing .mcp.json has an unsupported structure. It was left unchanged." };
@@ -22232,7 +22589,7 @@ async function connectProviderMcp(cwd, providerId) {
22232
22589
  }
22233
22590
  servers[providerId] = recipe.entry;
22234
22591
  const next = { ...config, mcpServers: servers };
22235
- await (0, import_promises31.writeFile)(configPath, `${JSON.stringify(next, null, 2)}
22592
+ await (0, import_promises32.writeFile)(configPath, `${JSON.stringify(next, null, 2)}
22236
22593
  `, "utf8");
22237
22594
  const suffix = recipe.note ? ` ${recipe.note}` : "";
22238
22595
  return { ok: true, message: `Added ${providerId} MCP server to .mcp.json.${suffix}`, path: ".mcp.json" };
@@ -22285,7 +22642,7 @@ async function defaultScanRunner(flags, positional) {
22285
22642
  }
22286
22643
  async function readPackageJson(cwd) {
22287
22644
  try {
22288
- const raw = await (0, import_promises31.readFile)((0, import_node_path40.join)(cwd, "package.json"), "utf8");
22645
+ const raw = await (0, import_promises32.readFile)((0, import_node_path41.join)(cwd, "package.json"), "utf8");
22289
22646
  const parsed = JSON.parse(raw.replace(/^\uFEFF/, ""));
22290
22647
  return isRecord5(parsed) ? parsed : {};
22291
22648
  } catch {
@@ -22308,7 +22665,7 @@ async function listProjectFiles(cwd, maxFiles = 1800) {
22308
22665
  if (files.length >= maxFiles) return;
22309
22666
  let entries;
22310
22667
  try {
22311
- entries = await (0, import_promises31.readdir)(dir, { withFileTypes: true });
22668
+ entries = await (0, import_promises32.readdir)(dir, { withFileTypes: true });
22312
22669
  } catch {
22313
22670
  return;
22314
22671
  }
@@ -22316,7 +22673,7 @@ async function listProjectFiles(cwd, maxFiles = 1800) {
22316
22673
  if (files.length >= maxFiles) return;
22317
22674
  const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
22318
22675
  if (entry.isDirectory()) {
22319
- if (!skipped.has(entry.name)) await walk((0, import_node_path40.join)(dir, entry.name), rel);
22676
+ if (!skipped.has(entry.name)) await walk((0, import_node_path41.join)(dir, entry.name), rel);
22320
22677
  continue;
22321
22678
  }
22322
22679
  if (entry.isFile()) files.push(rel.toLowerCase());
@@ -22335,7 +22692,7 @@ async function readEnvKeys(cwd) {
22335
22692
  const keys = /* @__PURE__ */ new Set();
22336
22693
  for (const name of [".env.example", ".env"]) {
22337
22694
  try {
22338
- const raw = await (0, import_promises31.readFile)((0, import_node_path40.join)(cwd, name), "utf8");
22695
+ const raw = await (0, import_promises32.readFile)((0, import_node_path41.join)(cwd, name), "utf8");
22339
22696
  for (const line of raw.split(/\r?\n/)) {
22340
22697
  const trimmed = line.trim();
22341
22698
  if (!trimmed || trimmed.startsWith("#")) continue;
@@ -22653,7 +23010,7 @@ async function codexModelOptions() {
22653
23010
  { id: "gpt-5.4-mini", label: "GPT-5.4 Mini" }
22654
23011
  ];
22655
23012
  try {
22656
- const raw = await (0, import_promises31.readFile)((0, import_node_path40.join)((0, import_node_os3.homedir)(), ".codex", "models_cache.json"), "utf8");
23013
+ const raw = await (0, import_promises32.readFile)((0, import_node_path41.join)((0, import_node_os3.homedir)(), ".codex", "models_cache.json"), "utf8");
22657
23014
  const parsed = JSON.parse(raw);
22658
23015
  const list = Array.isArray(parsed) ? parsed : parsed.models ?? [];
22659
23016
  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 }));
@@ -22856,19 +23213,19 @@ function isPlaceholderProjectName(value) {
22856
23213
  async function readMcpEvidence(cwd) {
22857
23214
  const candidates = MCP_CONFIG_PATHS.map((relativePath) => ({
22858
23215
  source: relativePath.replace(/\\/g, "/"),
22859
- path: (0, import_node_path40.resolve)(cwd, relativePath)
23216
+ path: (0, import_node_path41.resolve)(cwd, relativePath)
22860
23217
  }));
22861
23218
  const home = process.env.USERPROFILE || process.env.HOME;
22862
23219
  if (home) {
22863
23220
  candidates.push({
22864
23221
  source: "~/.codex/config.toml",
22865
- path: (0, import_node_path40.resolve)(home, ".codex", "config.toml")
23222
+ path: (0, import_node_path41.resolve)(home, ".codex", "config.toml")
22866
23223
  });
22867
23224
  }
22868
23225
  const evidence = [];
22869
23226
  for (const candidate of candidates) {
22870
23227
  try {
22871
- const text = await (0, import_promises31.readFile)(candidate.path, "utf8");
23228
+ const text = await (0, import_promises32.readFile)(candidate.path, "utf8");
22872
23229
  if (/mcp/i.test(text)) evidence.push({ source: candidate.source, text });
22873
23230
  } catch {
22874
23231
  }
@@ -22959,7 +23316,7 @@ async function enrichProjectMetadata(state, cwd) {
22959
23316
  async function readState(cwd) {
22960
23317
  let state;
22961
23318
  try {
22962
- const raw = await (0, import_promises31.readFile)((0, import_node_path40.join)(cwd, ".viberaven", "last-scan.json"), "utf8");
23319
+ const raw = await (0, import_promises32.readFile)((0, import_node_path41.join)(cwd, ".viberaven", "last-scan.json"), "utf8");
22963
23320
  const packageJson = await readPackageJson(cwd);
22964
23321
  const deps = dependencyNames(packageJson);
22965
23322
  const files = await listProjectFiles(cwd);
@@ -23226,7 +23583,7 @@ async function whereExe(command, cwd) {
23226
23583
  }
23227
23584
  function expandShimPath(value, base) {
23228
23585
  const relative8 = value.replace(/^%(?:dp0%\\+|~dp0\\*)/i, "");
23229
- return relative8 === value ? value : (0, import_node_path40.join)(base, ...relative8.split(/[\\/]+/));
23586
+ return relative8 === value ? value : (0, import_node_path41.join)(base, ...relative8.split(/[\\/]+/));
23230
23587
  }
23231
23588
  function findShimTarget(lines, pattern, accept = () => true) {
23232
23589
  for (const line of lines) {
@@ -23237,21 +23594,21 @@ function findShimTarget(lines, pattern, accept = () => true) {
23237
23594
  }
23238
23595
  async function resolveWindowsShimTarget(shimPath) {
23239
23596
  try {
23240
- const content = await (0, import_promises31.readFile)(shimPath, "utf8");
23241
- const base = (0, import_node_path40.dirname)(shimPath);
23597
+ const content = await (0, import_promises32.readFile)(shimPath, "utf8");
23598
+ const base = (0, import_node_path41.dirname)(shimPath);
23242
23599
  const lines = content.split(/\r?\n/);
23243
23600
  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("%"));
23244
23601
  const script = findShimTarget(lines, /"%(?:dp0%\\+|~dp0\\*)([^"]+\.js)"/i);
23245
23602
  if (script) {
23246
23603
  return {
23247
23604
  command: process.execPath,
23248
- args: [(0, import_node_path40.join)(base, ...script.match[1].split(/[\\/]+/)), ...trailingArgs(script.line, script.match.index + script.match[0].length)]
23605
+ args: [(0, import_node_path41.join)(base, ...script.match[1].split(/[\\/]+/)), ...trailingArgs(script.line, script.match.index + script.match[0].length)]
23249
23606
  };
23250
23607
  }
23251
- const binary = findShimTarget(lines, /"%(?:dp0%\\+|~dp0\\*)([^"]+\.exe)"/i, (target) => (0, import_node_path40.basename)(target).toLowerCase() !== "node.exe");
23608
+ const binary = findShimTarget(lines, /"%(?:dp0%\\+|~dp0\\*)([^"]+\.exe)"/i, (target) => (0, import_node_path41.basename)(target).toLowerCase() !== "node.exe");
23252
23609
  if (binary) {
23253
23610
  return {
23254
- command: (0, import_node_path40.join)(base, ...binary.match[1].split(/[\\/]+/)),
23611
+ command: (0, import_node_path41.join)(base, ...binary.match[1].split(/[\\/]+/)),
23255
23612
  args: trailingArgs(binary.line, binary.match.index + binary.match[0].length)
23256
23613
  };
23257
23614
  }
@@ -23376,8 +23733,8 @@ async function writeChatContextArtifact(cwd, request, prompt) {
23376
23733
  "",
23377
23734
  prompt
23378
23735
  ].filter((line) => typeof line === "string");
23379
- await (0, import_promises31.mkdir)((0, import_node_path40.join)(cwd, ".viberaven"), { recursive: true });
23380
- await (0, import_promises31.writeFile)((0, import_node_path40.join)(cwd, ".viberaven", "chat-context.md"), `${lines.join("\n")}
23736
+ await (0, import_promises32.mkdir)((0, import_node_path41.join)(cwd, ".viberaven"), { recursive: true });
23737
+ await (0, import_promises32.writeFile)((0, import_node_path41.join)(cwd, ".viberaven", "chat-context.md"), `${lines.join("\n")}
23381
23738
  `, "utf8");
23382
23739
  } catch {
23383
23740
  }
@@ -23397,13 +23754,13 @@ async function runAgentChat(cwd, request) {
23397
23754
  }
23398
23755
  const prompt = await buildAgentChatPrompt(request, cwd);
23399
23756
  await writeChatContextArtifact(cwd, request, prompt);
23400
- 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;
23401
- if (outputPath) await (0, import_promises31.mkdir)((0, import_node_path40.dirname)(outputPath), { recursive: true });
23757
+ const outputPath = request.cliId === "codex" ? (0, import_node_path41.join)(cwd, ".viberaven", `studio-agent-${Date.now()}-${Math.random().toString(36).slice(2)}.md`) : void 0;
23758
+ if (outputPath) await (0, import_promises32.mkdir)((0, import_node_path41.dirname)(outputPath), { recursive: true });
23402
23759
  const { command, args, stdin } = commandForAgentChat(request, prompt, cwd, outputPath);
23403
23760
  const result = await runAgentCommand(command, args, cwd, stdin, request.runId);
23404
23761
  if (outputPath) {
23405
23762
  try {
23406
- const finalMessage = (await (0, import_promises31.readFile)(outputPath, "utf8")).trim();
23763
+ const finalMessage = (await (0, import_promises32.readFile)(outputPath, "utf8")).trim();
23407
23764
  if (finalMessage) return { ...result, output: finalMessage };
23408
23765
  } catch {
23409
23766
  }
@@ -23415,8 +23772,8 @@ function firstGapId(state) {
23415
23772
  return state.providers.find((provider2) => provider2.nextFix?.id)?.nextFix?.id;
23416
23773
  }
23417
23774
  async function runLocalAction(cwd, intent) {
23418
- const artifactDir = (0, import_node_path40.join)(cwd, ".viberaven");
23419
- await (0, import_promises31.mkdir)(artifactDir, { recursive: true });
23775
+ const artifactDir = (0, import_node_path41.join)(cwd, ".viberaven");
23776
+ await (0, import_promises32.mkdir)(artifactDir, { recursive: true });
23420
23777
  if (intent === "preview_rehearsal") {
23421
23778
  const state2 = await readState(cwd);
23422
23779
  const topProvider = state2.providers[0];
@@ -23457,7 +23814,7 @@ async function runLocalAction(cwd, intent) {
23457
23814
  proof,
23458
23815
  ""
23459
23816
  ].join("\n");
23460
- await (0, import_promises31.writeFile)((0, import_node_path40.join)(artifactDir, "preview-rehearsal.md"), markdown, "utf8");
23817
+ await (0, import_promises32.writeFile)((0, import_node_path41.join)(artifactDir, "preview-rehearsal.md"), markdown, "utf8");
23461
23818
  return {
23462
23819
  title: "Preview rehearsal ready",
23463
23820
  summary: `${projectName}: ${gateLabel} with ${state2.providers.length} provider beat${state2.providers.length === 1 ? "" : "s"}`,
@@ -23467,7 +23824,7 @@ async function runLocalAction(cwd, intent) {
23467
23824
  if (intent === "audit_vercel_supabase") {
23468
23825
  const auditInput = await collectVercelSupabaseAuditInput(cwd);
23469
23826
  const result2 = buildVercelSupabaseAudit(auditInput);
23470
- await (0, import_promises31.writeFile)((0, import_node_path40.join)(artifactDir, "audit-vercel-supabase.txt"), `${renderVercelSupabaseAudit(result2)}
23827
+ await (0, import_promises32.writeFile)((0, import_node_path41.join)(artifactDir, "audit-vercel-supabase.txt"), `${renderVercelSupabaseAudit(result2)}
23471
23828
  `, "utf8");
23472
23829
  return {
23473
23830
  title: result2.status === "pass" ? "Audit passed" : "Audit needs work",
@@ -23495,7 +23852,7 @@ async function runLocalAction(cwd, intent) {
23495
23852
  const mode = intent === "heal_prompt" ? "prompt" : intent === "heal_apply" ? "apply" : "plan";
23496
23853
  const result = await runHealCommand({ cwd, mode, gapId, yes: mode === "apply", noVerify: true });
23497
23854
  if (mode === "apply") {
23498
- await (0, import_promises31.writeFile)((0, import_node_path40.join)(artifactDir, "heal-apply-result.json"), `${JSON.stringify(result, null, 2)}
23855
+ await (0, import_promises32.writeFile)((0, import_node_path41.join)(artifactDir, "heal-apply-result.json"), `${JSON.stringify(result, null, 2)}
23499
23856
  `, "utf8");
23500
23857
  }
23501
23858
  return {
@@ -23505,14 +23862,14 @@ async function runLocalAction(cwd, intent) {
23505
23862
  };
23506
23863
  }
23507
23864
  async function sendArtifact(cwd, rel, response, contentType = "text/plain; charset=utf-8") {
23508
- const absolute = (0, import_node_path40.resolve)(cwd, rel);
23509
- const root = (0, import_node_path40.resolve)(cwd);
23865
+ const absolute = (0, import_node_path41.resolve)(cwd, rel);
23866
+ const root = (0, import_node_path41.resolve)(cwd);
23510
23867
  if (!absolute.startsWith(root)) {
23511
23868
  sendText(response, 403, "Forbidden");
23512
23869
  return;
23513
23870
  }
23514
23871
  try {
23515
- const text = await (0, import_promises31.readFile)(absolute, "utf8");
23872
+ const text = await (0, import_promises32.readFile)(absolute, "utf8");
23516
23873
  response.writeHead(200, { "content-type": contentType });
23517
23874
  response.end(text);
23518
23875
  } catch {
@@ -23521,7 +23878,7 @@ async function sendArtifact(cwd, rel, response, contentType = "text/plain; chars
23521
23878
  }
23522
23879
  async function sendReportArtifact(cwd, response) {
23523
23880
  try {
23524
- const text = await (0, import_promises31.readFile)((0, import_node_path40.join)(cwd, ".viberaven", "report.html"), "utf8");
23881
+ const text = await (0, import_promises32.readFile)((0, import_node_path41.join)(cwd, ".viberaven", "report.html"), "utf8");
23525
23882
  response.writeHead(200, { "content-type": "text/html; charset=utf-8" });
23526
23883
  response.end(text);
23527
23884
  return;
@@ -23648,7 +24005,7 @@ async function sendReportArtifact(cwd, response) {
23648
24005
  }
23649
24006
  async function sendExtensionIcon(cwd, response) {
23650
24007
  try {
23651
- const body = await (0, import_promises31.readFile)((0, import_node_path40.join)(cwd, "media", "extension-icon.png"));
24008
+ const body = await (0, import_promises32.readFile)((0, import_node_path41.join)(cwd, "media", "extension-icon.png"));
23652
24009
  sendBinary(response, 200, "image/png", body);
23653
24010
  } catch {
23654
24011
  sendText(response, 404, "Icon not found");
@@ -23661,15 +24018,15 @@ async function sendMangaAsset(pathname, response) {
23661
24018
  return;
23662
24019
  }
23663
24020
  const candidates = [
23664
- (0, import_node_path40.join)(__dirname, "manga", name),
24021
+ (0, import_node_path41.join)(__dirname, "manga", name),
23665
24022
  // installed + dev: <pkg>/dist/manga
23666
- (0, import_node_path40.join)(__dirname, "..", "assets", "manga", name),
24023
+ (0, import_node_path41.join)(__dirname, "..", "assets", "manga", name),
23667
24024
  // fallback: <pkg>/assets/manga (source)
23668
- (0, import_node_path40.join)(__dirname, "..", "..", "assets", "manga", name)
24025
+ (0, import_node_path41.join)(__dirname, "..", "..", "assets", "manga", name)
23669
24026
  ];
23670
24027
  for (const candidate of candidates) {
23671
24028
  try {
23672
- const body = await (0, import_promises31.readFile)(candidate);
24029
+ const body = await (0, import_promises32.readFile)(candidate);
23673
24030
  response.setHeader("cache-control", "no-cache");
23674
24031
  sendBinary(response, 200, contentTypeForAsset(candidate), body);
23675
24032
  return;
@@ -23679,7 +24036,7 @@ async function sendMangaAsset(pathname, response) {
23679
24036
  sendText(response, 404, "Asset not found");
23680
24037
  }
23681
24038
  function contentTypeForAsset(pathname) {
23682
- switch ((0, import_node_path40.extname)(pathname).toLowerCase()) {
24039
+ switch ((0, import_node_path41.extname)(pathname).toLowerCase()) {
23683
24040
  case ".svg":
23684
24041
  return "image/svg+xml; charset=utf-8";
23685
24042
  case ".png":
@@ -23696,14 +24053,14 @@ function contentTypeForAsset(pathname) {
23696
24053
  }
23697
24054
  async function sendBundledReportAsset(pathname, response) {
23698
24055
  const rel = decodeURIComponent(pathname.replace(/^\/report\//, ""));
23699
- const assetRoot = (0, import_node_path40.resolve)(getBundledReportAssetsDir());
23700
- const absolute = (0, import_node_path40.resolve)(assetRoot, rel);
24056
+ const assetRoot = (0, import_node_path41.resolve)(getBundledReportAssetsDir());
24057
+ const absolute = (0, import_node_path41.resolve)(assetRoot, rel);
23701
24058
  if (!absolute.startsWith(assetRoot)) {
23702
24059
  sendText(response, 403, "Forbidden");
23703
24060
  return;
23704
24061
  }
23705
24062
  try {
23706
- const body = await (0, import_promises31.readFile)(absolute);
24063
+ const body = await (0, import_promises32.readFile)(absolute);
23707
24064
  sendBinary(response, 200, contentTypeForAsset(absolute), body);
23708
24065
  } catch {
23709
24066
  sendText(response, 404, "Asset not found");
@@ -23725,18 +24082,18 @@ async function bundleStackClient(entryPath) {
23725
24082
  return output.text;
23726
24083
  }
23727
24084
  async function sendStackAppJs(response) {
23728
- const distPath = (0, import_node_path40.join)(__dirname, "stack", "app.js");
24085
+ const distPath = (0, import_node_path41.join)(__dirname, "stack", "app.js");
23729
24086
  try {
23730
- const body = await (0, import_promises31.readFile)(distPath);
24087
+ const body = await (0, import_promises32.readFile)(distPath);
23731
24088
  response.writeHead(200, { "content-type": "application/javascript; charset=utf-8", "cache-control": "no-store" });
23732
24089
  response.end(body);
23733
24090
  return;
23734
24091
  } catch {
23735
24092
  }
23736
- const entryPath = (0, import_node_path40.join)(__dirname, "stack", "main.ts");
24093
+ const entryPath = (0, import_node_path41.join)(__dirname, "stack", "main.ts");
23737
24094
  let mtimeMs;
23738
24095
  try {
23739
- mtimeMs = (await (0, import_promises31.stat)(entryPath)).mtimeMs;
24096
+ mtimeMs = (await (0, import_promises32.stat)(entryPath)).mtimeMs;
23740
24097
  } catch {
23741
24098
  sendText(response, 404, "Stack client entry not found: src/local-ui/stack/main.ts is missing.");
23742
24099
  return;
@@ -23755,7 +24112,7 @@ async function sendStackAppJs(response) {
23755
24112
  }
23756
24113
  async function sendStackAppCss(response) {
23757
24114
  try {
23758
- const body = await (0, import_promises31.readFile)((0, import_node_path40.join)(__dirname, "stack", "stack.css"));
24115
+ const body = await (0, import_promises32.readFile)((0, import_node_path41.join)(__dirname, "stack", "stack.css"));
23759
24116
  response.writeHead(200, { "content-type": "text/css; charset=utf-8", "cache-control": "no-store" });
23760
24117
  response.end(body);
23761
24118
  } catch {
@@ -23764,7 +24121,7 @@ async function sendStackAppCss(response) {
23764
24121
  }
23765
24122
  async function readProviderVerifyLedger(cwd) {
23766
24123
  try {
23767
- const raw = await (0, import_promises31.readFile)((0, import_node_path40.join)(cwd, ".viberaven", "provider-verify.json"), "utf8");
24124
+ const raw = await (0, import_promises32.readFile)((0, import_node_path41.join)(cwd, ".viberaven", "provider-verify.json"), "utf8");
23768
24125
  const parsed = JSON.parse(raw.replace(/^\uFEFF/, ""));
23769
24126
  return isRecord5(parsed) ? parsed : {};
23770
24127
  } catch {
@@ -23774,8 +24131,8 @@ async function readProviderVerifyLedger(cwd) {
23774
24131
  async function writeProviderVerifyEntry(cwd, providerId, entry) {
23775
24132
  const ledger = await readProviderVerifyLedger(cwd);
23776
24133
  ledger[providerId] = entry;
23777
- await (0, import_promises31.mkdir)((0, import_node_path40.join)(cwd, ".viberaven"), { recursive: true });
23778
- await (0, import_promises31.writeFile)((0, import_node_path40.join)(cwd, ".viberaven", "provider-verify.json"), `${JSON.stringify(ledger, null, 2)}
24134
+ await (0, import_promises32.mkdir)((0, import_node_path41.join)(cwd, ".viberaven"), { recursive: true });
24135
+ await (0, import_promises32.writeFile)((0, import_node_path41.join)(cwd, ".viberaven", "provider-verify.json"), `${JSON.stringify(ledger, null, 2)}
23779
24136
  `, "utf8");
23780
24137
  }
23781
24138
  async function handleRequest(request, response, options) {
@@ -23848,13 +24205,13 @@ async function handleRequest(request, response, options) {
23848
24205
  return;
23849
24206
  }
23850
24207
  if (request.method === "GET" && url.pathname === "/api/architecture-map") {
23851
- const mapPath = (0, import_node_path40.join)(options.cwd, ".viberaven", "architecture-map.md");
23852
- const markdown = await (0, import_promises31.readFile)(mapPath, "utf8").catch(() => null);
24208
+ const mapPath = (0, import_node_path41.join)(options.cwd, ".viberaven", "architecture-map.md");
24209
+ const markdown = await (0, import_promises32.readFile)(mapPath, "utf8").catch(() => null);
23853
24210
  if (markdown === null) {
23854
24211
  sendJsonStatus(response, 404, { error: "No architecture map yet. Run a scan first (it is written on every scan)." });
23855
24212
  return;
23856
24213
  }
23857
- const dataRaw = await (0, import_promises31.readFile)((0, import_node_path40.join)(options.cwd, ".viberaven", "architecture-map.json"), "utf8").catch(() => null);
24214
+ const dataRaw = await (0, import_promises32.readFile)((0, import_node_path41.join)(options.cwd, ".viberaven", "architecture-map.json"), "utf8").catch(() => null);
23858
24215
  let data = null;
23859
24216
  if (dataRaw) {
23860
24217
  try {
@@ -23863,7 +24220,7 @@ async function handleRequest(request, response, options) {
23863
24220
  data = null;
23864
24221
  }
23865
24222
  }
23866
- const contextRaw = await (0, import_promises31.readFile)((0, import_node_path40.join)(options.cwd, ".viberaven", "context-map.json"), "utf8").catch(() => null);
24223
+ const contextRaw = await (0, import_promises32.readFile)((0, import_node_path41.join)(options.cwd, ".viberaven", "context-map.json"), "utf8").catch(() => null);
23867
24224
  let gaps = [];
23868
24225
  if (contextRaw) {
23869
24226
  try {
@@ -24129,35 +24486,35 @@ async function handleRequest(request, response, options) {
24129
24486
  return;
24130
24487
  }
24131
24488
  if (request.method === "GET" && url.pathname === "/api/artifacts/tasklist") {
24132
- await sendArtifact(options.cwd, (0, import_node_path40.join)(".viberaven", "agent-tasklist.md"), response);
24489
+ await sendArtifact(options.cwd, (0, import_node_path41.join)(".viberaven", "agent-tasklist.md"), response);
24133
24490
  return;
24134
24491
  }
24135
24492
  if (request.method === "GET" && url.pathname === "/api/artifacts/playbook") {
24136
- await sendArtifact(options.cwd, (0, import_node_path40.join)(".viberaven", "launch-playbook.md"), response);
24493
+ await sendArtifact(options.cwd, (0, import_node_path41.join)(".viberaven", "launch-playbook.md"), response);
24137
24494
  return;
24138
24495
  }
24139
24496
  if (request.method === "GET" && url.pathname === "/api/artifacts/audit") {
24140
- await sendArtifact(options.cwd, (0, import_node_path40.join)(".viberaven", "audit-vercel-supabase.txt"), response);
24497
+ await sendArtifact(options.cwd, (0, import_node_path41.join)(".viberaven", "audit-vercel-supabase.txt"), response);
24141
24498
  return;
24142
24499
  }
24143
24500
  if (request.method === "GET" && url.pathname === "/api/artifacts/cleanup") {
24144
- await sendArtifact(options.cwd, (0, import_node_path40.join)(".viberaven", "context-cleanup.md"), response);
24501
+ await sendArtifact(options.cwd, (0, import_node_path41.join)(".viberaven", "context-cleanup.md"), response);
24145
24502
  return;
24146
24503
  }
24147
24504
  if (request.method === "GET" && url.pathname === "/api/artifacts/preview") {
24148
- await sendArtifact(options.cwd, (0, import_node_path40.join)(".viberaven", "preview-rehearsal.md"), response);
24505
+ await sendArtifact(options.cwd, (0, import_node_path41.join)(".viberaven", "preview-rehearsal.md"), response);
24149
24506
  return;
24150
24507
  }
24151
24508
  if (request.method === "GET" && url.pathname === "/api/artifacts/heal-plan") {
24152
- await sendArtifact(options.cwd, (0, import_node_path40.join)(".viberaven", "heal-plan.md"), response);
24509
+ await sendArtifact(options.cwd, (0, import_node_path41.join)(".viberaven", "heal-plan.md"), response);
24153
24510
  return;
24154
24511
  }
24155
24512
  if (request.method === "GET" && url.pathname === "/api/artifacts/heal-prompt") {
24156
- await sendArtifact(options.cwd, (0, import_node_path40.join)(".viberaven", "heal-prompt.md"), response);
24513
+ await sendArtifact(options.cwd, (0, import_node_path41.join)(".viberaven", "heal-prompt.md"), response);
24157
24514
  return;
24158
24515
  }
24159
24516
  if (request.method === "GET" && url.pathname === "/api/artifacts/heal-apply") {
24160
- await sendArtifact(options.cwd, (0, import_node_path40.join)(".viberaven", "heal-apply-result.json"), response, "application/json; charset=utf-8");
24517
+ await sendArtifact(options.cwd, (0, import_node_path41.join)(".viberaven", "heal-apply-result.json"), response, "application/json; charset=utf-8");
24161
24518
  return;
24162
24519
  }
24163
24520
  if (request.method === "GET" && url.pathname === "/api/artifacts/report") {
@@ -24183,12 +24540,12 @@ async function handleRequest(request, response, options) {
24183
24540
  }
24184
24541
  if (request.method === "GET" && url.pathname === "/vendor/anime.umd.min.js") {
24185
24542
  for (const candidate of [
24186
- (0, import_node_path40.join)(__dirname, "vendor", "anime.umd.min.js"),
24187
- (0, import_node_path40.join)(__dirname, "..", "assets", "vendor", "anime.umd.min.js"),
24188
- (0, import_node_path40.join)(__dirname, "..", "..", "assets", "vendor", "anime.umd.min.js")
24543
+ (0, import_node_path41.join)(__dirname, "vendor", "anime.umd.min.js"),
24544
+ (0, import_node_path41.join)(__dirname, "..", "assets", "vendor", "anime.umd.min.js"),
24545
+ (0, import_node_path41.join)(__dirname, "..", "..", "assets", "vendor", "anime.umd.min.js")
24189
24546
  ]) {
24190
24547
  try {
24191
- const body = await (0, import_promises31.readFile)(candidate);
24548
+ const body = await (0, import_promises32.readFile)(candidate);
24192
24549
  response.setHeader("cache-control", "public, max-age=604800, immutable");
24193
24550
  sendBinary(response, 200, "text/javascript; charset=utf-8", body);
24194
24551
  return;
@@ -24405,7 +24762,7 @@ function listen(server, startPort) {
24405
24762
  });
24406
24763
  }
24407
24764
  async function startLocalUiServer(options) {
24408
- const cwd = (0, import_node_path40.resolve)(options.cwd);
24765
+ const cwd = (0, import_node_path41.resolve)(options.cwd);
24409
24766
  const scanRunner = options.scanRunner ?? defaultScanRunner;
24410
24767
  const autoScan = { state: "idle" };
24411
24768
  const fullCheck = new FullCheckService(cwd, options.fullCheck);
@@ -24422,7 +24779,7 @@ async function startLocalUiServer(options) {
24422
24779
  });
24423
24780
  const port = await listen(server, options.port ?? 4177);
24424
24781
  guard.port = port;
24425
- const hasArtifact = await (0, import_promises31.readFile)((0, import_node_path40.join)(cwd, ".viberaven", "last-scan.json"), "utf8").then(
24782
+ const hasArtifact = await (0, import_promises32.readFile)((0, import_node_path41.join)(cwd, ".viberaven", "last-scan.json"), "utf8").then(
24426
24783
  () => true,
24427
24784
  () => false
24428
24785
  );
@@ -24461,16 +24818,16 @@ async function waitForServerShutdown() {
24461
24818
  process.once("SIGTERM", done);
24462
24819
  });
24463
24820
  }
24464
- 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;
24821
+ var import_node_http, import_node_child_process10, import_node_crypto6, import_promises32, import_node_os3, import_node_path41, 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;
24465
24822
  var init_server = __esm({
24466
24823
  "src/local-ui/server.ts"() {
24467
24824
  "use strict";
24468
24825
  import_node_http = require("node:http");
24469
24826
  import_node_child_process10 = require("node:child_process");
24470
24827
  import_node_crypto6 = require("node:crypto");
24471
- import_promises31 = require("node:fs/promises");
24828
+ import_promises32 = require("node:fs/promises");
24472
24829
  import_node_os3 = require("node:os");
24473
- import_node_path40 = require("node:path");
24830
+ import_node_path41 = require("node:path");
24474
24831
  init_initGithub();
24475
24832
  init_cleanPlan();
24476
24833
  init_audit();
@@ -24512,8 +24869,8 @@ var init_server = __esm({
24512
24869
  MCP_CONFIG_PATHS = [
24513
24870
  ".mcp.json",
24514
24871
  "mcp.json",
24515
- (0, import_node_path40.join)(".cursor", "mcp.json"),
24516
- (0, import_node_path40.join)(".vscode", "mcp.json")
24872
+ (0, import_node_path41.join)(".cursor", "mcp.json"),
24873
+ (0, import_node_path41.join)(".vscode", "mcp.json")
24517
24874
  ];
24518
24875
  MCP_PROVIDER_NEEDLES = {
24519
24876
  supabase: ["supabase", "@supabase/mcp", "mcp-server-supabase"],
@@ -24595,6 +24952,7 @@ function buildNextActionBlock(tasks, loopState) {
24595
24952
  mcpTool: repoCodeTask.mcpTool,
24596
24953
  mcpArgs: repoCodeTask.mcpArgs,
24597
24954
  fallbackCommand: repoCodeTask.mcpArgs ? `npx -y viberaven --heal --apply --gap ${repoCodeTask.gapId} --yes` : void 0,
24955
+ ...whereAndAfter(repoCodeTask),
24598
24956
  requiresUserAction: false
24599
24957
  };
24600
24958
  }
@@ -24608,6 +24966,7 @@ function buildNextActionBlock(tasks, loopState) {
24608
24966
  mcpTool: userTask.mcpTool,
24609
24967
  mcpArgs: userTask.mcpArgs,
24610
24968
  fallbackCommand: userTask.mcpArgs ? `npx -y viberaven --heal --apply --gap ${userTask.gapId} --yes` : void 0,
24969
+ ...whereAndAfter(userTask),
24611
24970
  requiresUserAction: true
24612
24971
  };
24613
24972
  }
@@ -24623,10 +24982,17 @@ function buildNextActionBlock(tasks, loopState) {
24623
24982
  return {
24624
24983
  ...base,
24625
24984
  type: "done",
24626
- title: "All gaps resolved \u2014 production gate is clear",
24985
+ title: "No repo-code or provider tasks left; gate-result.json has the verdict",
24627
24986
  requiresUserAction: false
24628
24987
  };
24629
24988
  }
24989
+ function whereAndAfter(task) {
24990
+ return {
24991
+ ...task.file ? { file: task.file } : {},
24992
+ ...task.line !== void 0 ? { line: task.line } : {},
24993
+ ...task.userSteps?.length ? { userSteps: [...task.userSteps] } : {}
24994
+ };
24995
+ }
24630
24996
  function resolveStallReason(tasks) {
24631
24997
  if (tasks.length === 0) return "no-recipes";
24632
24998
  const allProviderAction = tasks.every((t) => t.fixType === "provider-action");
@@ -24684,9 +25050,9 @@ var init_nextActionBlock = __esm({
24684
25050
  function defaultMcpConfigPaths() {
24685
25051
  const home = (0, import_node_os4.homedir)();
24686
25052
  return [
24687
- (0, import_node_path41.join)(home, ".config", "claude", "claude_desktop_config.json"),
24688
- (0, import_node_path41.join)(home, ".cursor", "mcp.json"),
24689
- (0, import_node_path41.join)(home, ".gemini", "antigravity", "mcp_config.json")
25053
+ (0, import_node_path42.join)(home, ".config", "claude", "claude_desktop_config.json"),
25054
+ (0, import_node_path42.join)(home, ".cursor", "mcp.json"),
25055
+ (0, import_node_path42.join)(home, ".gemini", "antigravity", "mcp_config.json")
24690
25056
  ];
24691
25057
  }
24692
25058
  function resolveConfigPaths() {
@@ -24715,11 +25081,11 @@ function findServerEntry(servers, provider2) {
24715
25081
  function findProviderMcpConfig(provider2, configPaths) {
24716
25082
  const paths = configPaths ?? resolveConfigPaths();
24717
25083
  for (const path of paths) {
24718
- if (!(0, import_node_fs15.existsSync)(path)) {
25084
+ if (!(0, import_node_fs16.existsSync)(path)) {
24719
25085
  continue;
24720
25086
  }
24721
25087
  try {
24722
- const raw = JSON.parse((0, import_node_fs15.readFileSync)(path, "utf8"));
25088
+ const raw = JSON.parse((0, import_node_fs16.readFileSync)(path, "utf8"));
24723
25089
  const servers = parseMcpServers(raw);
24724
25090
  if (!servers) {
24725
25091
  continue;
@@ -24763,13 +25129,13 @@ async function verifyProviderGap(options) {
24763
25129
  fallbackCommand: FALLBACK_COMMAND
24764
25130
  };
24765
25131
  }
24766
- var import_node_fs15, import_node_os4, import_node_path41, FALLBACK_COMMAND, SUPPORTED_PROVIDERS, configPathsOverride;
25132
+ var import_node_fs16, import_node_os4, import_node_path42, FALLBACK_COMMAND, SUPPORTED_PROVIDERS, configPathsOverride;
24767
25133
  var init_providerMcpBridge = __esm({
24768
25134
  "src/providerMcpBridge.ts"() {
24769
25135
  "use strict";
24770
- import_node_fs15 = require("node:fs");
25136
+ import_node_fs16 = require("node:fs");
24771
25137
  import_node_os4 = require("node:os");
24772
- import_node_path41 = require("node:path");
25138
+ import_node_path42 = require("node:path");
24773
25139
  FALLBACK_COMMAND = "npx -y viberaven audit --vercel-supabase --json";
24774
25140
  SUPPORTED_PROVIDERS = /* @__PURE__ */ new Set(["supabase", "vercel"]);
24775
25141
  }
@@ -24992,7 +25358,7 @@ async function cmdProviderVerify(flags, positional) {
24992
25358
  return result.verified ? 0 : 1;
24993
25359
  }
24994
25360
  async function cmdStatus(flags, positional) {
24995
- const startDir = positional[0] ? (0, import_node_path42.resolve)(process.cwd(), positional[0]) : process.cwd();
25361
+ const startDir = positional[0] ? (0, import_node_path43.resolve)(process.cwd(), positional[0]) : process.cwd();
24996
25362
  let artifact;
24997
25363
  try {
24998
25364
  artifact = await loadLastArtifact(startDir);
@@ -25031,7 +25397,7 @@ async function cmdStatus(flags, positional) {
25031
25397
  return 0;
25032
25398
  }
25033
25399
  async function runScanCommand(flags, positional, options) {
25034
- const workspacePath = positional[0] ? (0, import_node_path42.resolve)(process.cwd(), positional[0]) : await resolveWorkspaceRoot(process.cwd());
25400
+ const workspacePath = positional[0] ? (0, import_node_path43.resolve)(process.cwd(), positional[0]) : await resolveWorkspaceRoot(process.cwd());
25035
25401
  if (!options?.deferMachineOutput) {
25036
25402
  console.log(`Scanning ${workspacePath}\u2026 (local, offline)`);
25037
25403
  }
@@ -25055,15 +25421,27 @@ async function runScanCommand(flags, positional, options) {
25055
25421
  const updatedState = resetBatch(loopState, openGapCount);
25056
25422
  const tasks = buildTaskList(artifact);
25057
25423
  const block = buildNextActionBlock(tasks, updatedState);
25058
- printNextActionBlock(block);
25059
- printProviderActionBlock(tasks);
25424
+ let manifest;
25060
25425
  try {
25061
- const manifest = JSON.parse(await (0, import_promises32.readFile)(paths.actionsPath, "utf8"));
25062
- console.log(renderActionSurface(manifest, { limit: 5 }).trimEnd());
25426
+ manifest = JSON.parse(await (0, import_promises33.readFile)(paths.actionsPath, "utf8"));
25063
25427
  } catch (error) {
25064
25428
  console.warn(error instanceof Error ? error.message : String(error));
25065
25429
  }
25430
+ const followUps = manifest?.userFollowUps ?? [];
25431
+ if (followUps.length > 0) block.userFollowUps = followUps;
25432
+ printNextActionBlock(block);
25433
+ printProviderActionBlock(tasks);
25434
+ if (manifest) {
25435
+ console.log(renderActionSurface(manifest, { limit: 5, followUps: false }).trimEnd());
25436
+ }
25066
25437
  await saveLoopState(workspacePath, updatedState);
25438
+ if (options?.markFollowUpsShown && followUps.length > 0) {
25439
+ try {
25440
+ await markUserFollowUpsShown(workspacePath, followUps.map((followUp) => followUp.id));
25441
+ } catch (error) {
25442
+ console.warn(`Could not record that the outside-the-repo steps were shown (${error instanceof Error ? error.message : String(error)}); they will be shown again.`);
25443
+ }
25444
+ }
25067
25445
  }
25068
25446
  if (flags.open) {
25069
25447
  try {
@@ -25075,7 +25453,7 @@ async function runScanCommand(flags, positional, options) {
25075
25453
  return { exitCode: 0, artifacts: paths };
25076
25454
  }
25077
25455
  async function cmdReport(flags, positional) {
25078
- const startDir = positional[0] ? (0, import_node_path42.resolve)(process.cwd(), positional[0]) : process.cwd();
25456
+ const startDir = positional[0] ? (0, import_node_path43.resolve)(process.cwd(), positional[0]) : process.cwd();
25079
25457
  try {
25080
25458
  const paths = await refreshReportFromDisk(startDir);
25081
25459
  console.log(`Report refreshed: ${paths.reportPath}`);
@@ -25097,7 +25475,7 @@ async function cmdReport(flags, positional) {
25097
25475
  }
25098
25476
  }
25099
25477
  async function cmdPrompt(flags, positional) {
25100
- const startDir = positional[0] ? (0, import_node_path42.resolve)(process.cwd(), positional[0]) : process.cwd();
25478
+ const startDir = positional[0] ? (0, import_node_path43.resolve)(process.cwd(), positional[0]) : process.cwd();
25101
25479
  let artifact;
25102
25480
  try {
25103
25481
  artifact = await loadLastArtifact(startDir);
@@ -25194,7 +25572,7 @@ async function main() {
25194
25572
  const wantsJsonl = hasFlag(flags, "jsonl");
25195
25573
  const wantsStrict = hasFlag(flags, "strict");
25196
25574
  if (flags.condense) {
25197
- const cwd = positional[0] ? (0, import_node_path42.resolve)(process.cwd(), positional[0]) : process.cwd();
25575
+ const cwd = positional[0] ? (0, import_node_path43.resolve)(process.cwd(), positional[0]) : process.cwd();
25198
25576
  const result = await runCondenseCommand({ cwd });
25199
25577
  console.log(`VibeRaven context map refreshed: ${result.contextMapPath}`);
25200
25578
  return 0;
@@ -25214,18 +25592,18 @@ async function main() {
25214
25592
  }
25215
25593
  if (!command && flags.verify === true && typeof flags.action === "string") {
25216
25594
  return runVerifyActionCommand({
25217
- cwd: positional[0] ? (0, import_node_path42.resolve)(process.cwd(), positional[0]) : process.cwd(),
25595
+ cwd: positional[0] ? (0, import_node_path43.resolve)(process.cwd(), positional[0]) : process.cwd(),
25218
25596
  actionId: flags.action
25219
25597
  });
25220
25598
  }
25221
25599
  if (!command && (isAgentMode || flags.verify === true || wantsJson || wantsJsonl || wantsStrict)) {
25222
25600
  const deferMachineOutput = wantsJson || wantsJsonl;
25223
- const scanResult = await runScanCommand(flags, positional, { deferMachineOutput });
25601
+ const scanResult = await runScanCommand(flags, positional, { deferMachineOutput, markFollowUpsShown: true });
25224
25602
  if ((wantsJson || wantsJsonl) && !scanResult.artifacts) {
25225
25603
  console.error("VibeRaven could not produce machine output because scan artifacts were not written.");
25226
25604
  return 3;
25227
25605
  }
25228
- const gateResult = scanResult.artifacts && (wantsJson || wantsJsonl || wantsStrict) ? JSON.parse(await (0, import_promises32.readFile)(scanResult.artifacts.gateResultPath, "utf8")) : void 0;
25606
+ const gateResult = scanResult.artifacts && (wantsJson || wantsJsonl || wantsStrict) ? JSON.parse(await (0, import_promises33.readFile)(scanResult.artifacts.gateResultPath, "utf8")) : void 0;
25229
25607
  const strictExitCode = wantsStrict && gateResult ? exitCodeForStrictGate(gateResult, { failOnWarnings: flags.strict === "warning" }) : scanResult.exitCode;
25230
25608
  if (wantsJson && gateResult) {
25231
25609
  process.stdout.write(renderGateResultJson(gateResult));
@@ -25247,7 +25625,7 @@ async function main() {
25247
25625
  env: process.env
25248
25626
  });
25249
25627
  if (mode === "local-ui") {
25250
- const cwd = positional[0] ? (0, import_node_path42.resolve)(process.cwd(), positional[0]) : process.cwd();
25628
+ const cwd = positional[0] ? (0, import_node_path43.resolve)(process.cwd(), positional[0]) : process.cwd();
25251
25629
  const handle = await startLocalUiServer({ cwd, initialPath: stackInitialPath(flags) });
25252
25630
  console.log(`VibeRaven local UI: ${handle.url}`);
25253
25631
  await waitForServerShutdown();
@@ -25258,7 +25636,7 @@ async function main() {
25258
25636
  await runInteractiveSession();
25259
25637
  return 0;
25260
25638
  }
25261
- const scanResult = await runScanCommand({ "agent-mode": true }, positional);
25639
+ const scanResult = await runScanCommand({ "agent-mode": true }, positional, { markFollowUpsShown: true });
25262
25640
  return scanResult.exitCode;
25263
25641
  }
25264
25642
  switch (command) {
@@ -25267,7 +25645,7 @@ async function main() {
25267
25645
  await runInteractiveSession();
25268
25646
  return 0;
25269
25647
  case "ui": {
25270
- const cwd = positional[0] ? (0, import_node_path42.resolve)(process.cwd(), positional[0]) : process.cwd();
25648
+ const cwd = positional[0] ? (0, import_node_path43.resolve)(process.cwd(), positional[0]) : process.cwd();
25271
25649
  const envPort = Number(process.env.PORT);
25272
25650
  const handle = await startLocalUiServer({ cwd, port: Number.isFinite(envPort) && envPort > 0 ? envPort : void 0, initialPath: stackInitialPath(flags) });
25273
25651
  console.log(`VibeRaven local UI: ${handle.url}`);
@@ -25279,14 +25657,14 @@ async function main() {
25279
25657
  return cmdStatus(flags, positional);
25280
25658
  case "check":
25281
25659
  return runCheckCommand({
25282
- cwd: positional[0] ? (0, import_node_path42.resolve)(process.cwd(), positional[0]) : process.cwd(),
25660
+ cwd: positional[0] ? (0, import_node_path43.resolve)(process.cwd(), positional[0]) : process.cwd(),
25283
25661
  json: Boolean(flags.json),
25284
25662
  quiet: Boolean(flags.quiet),
25285
25663
  base: typeof flags.base === "string" ? flags.base : void 0,
25286
25664
  markdown: flags.markdown === true
25287
25665
  });
25288
25666
  case "fix": {
25289
- const fixCwd = positional[0] ? (0, import_node_path42.resolve)(process.cwd(), positional[0]) : process.cwd();
25667
+ const fixCwd = positional[0] ? (0, import_node_path43.resolve)(process.cwd(), positional[0]) : process.cwd();
25290
25668
  const gapId = typeof flags.gap === "string" ? flags.gap : void 0;
25291
25669
  if (!gapId) {
25292
25670
  const artifact = await loadLastArtifact(fixCwd).catch(() => void 0);
@@ -25315,19 +25693,19 @@ async function main() {
25315
25693
  }
25316
25694
  case "actions":
25317
25695
  return runActionsCommand({
25318
- cwd: positional[0] ? (0, import_node_path42.resolve)(process.cwd(), positional[0]) : process.cwd(),
25696
+ cwd: positional[0] ? (0, import_node_path43.resolve)(process.cwd(), positional[0]) : process.cwd(),
25319
25697
  json: Boolean(flags.json)
25320
25698
  });
25321
25699
  case "preview":
25322
25700
  return runPreviewCommand({
25323
- cwd: positional[0] ? (0, import_node_path42.resolve)(process.cwd(), positional[0]) : process.cwd(),
25701
+ cwd: positional[0] ? (0, import_node_path43.resolve)(process.cwd(), positional[0]) : process.cwd(),
25324
25702
  agentMode: flags["agent-mode"] === true,
25325
25703
  json: Boolean(flags.json)
25326
25704
  });
25327
25705
  case "next":
25328
25706
  return runNextCommand({
25329
25707
  json: Boolean(flags.json),
25330
- cwd: positional[0] ? (0, import_node_path42.resolve)(process.cwd(), positional[0]) : process.cwd()
25708
+ cwd: positional[0] ? (0, import_node_path43.resolve)(process.cwd(), positional[0]) : process.cwd()
25331
25709
  });
25332
25710
  case "guide": {
25333
25711
  const provider2 = positional[0];
@@ -25349,7 +25727,7 @@ async function main() {
25349
25727
  cwd: process.cwd()
25350
25728
  });
25351
25729
  case "scan": {
25352
- const scanResult = await runScanCommand(flags, positional);
25730
+ const scanResult = await runScanCommand(flags, positional, { markFollowUpsShown: true });
25353
25731
  return scanResult.exitCode;
25354
25732
  }
25355
25733
  case "report":
@@ -25361,7 +25739,7 @@ async function main() {
25361
25739
  case "provider-verify":
25362
25740
  return cmdProviderVerify(flags, positional);
25363
25741
  case "init": {
25364
- const cwd = positional[0] ? (0, import_node_path42.resolve)(process.cwd(), positional[0]) : process.cwd();
25742
+ const cwd = positional[0] ? (0, import_node_path43.resolve)(process.cwd(), positional[0]) : process.cwd();
25365
25743
  const agents = typeof flags.agents === "string" ? flags.agents : void 0;
25366
25744
  if (flags.github === true) {
25367
25745
  return runInitGithubCommand({ cwd, dryRun: flags["dry-run"] === true, force: flags.force === true });
@@ -25378,7 +25756,7 @@ async function main() {
25378
25756
  return 1;
25379
25757
  }
25380
25758
  return runDoctorAgentsCommand({
25381
- cwd: positional[0] ? (0, import_node_path42.resolve)(process.cwd(), positional[0]) : process.cwd()
25759
+ cwd: positional[0] ? (0, import_node_path43.resolve)(process.cwd(), positional[0]) : process.cwd()
25382
25760
  });
25383
25761
  case "validate-npm-package":
25384
25762
  return runValidateNpmPackageCommand({
@@ -25391,7 +25769,7 @@ async function main() {
25391
25769
  return 1;
25392
25770
  }
25393
25771
  return runAuditCommand({
25394
- cwd: positional[0] ? (0, import_node_path42.resolve)(process.cwd(), positional[0]) : process.cwd(),
25772
+ cwd: positional[0] ? (0, import_node_path43.resolve)(process.cwd(), positional[0]) : process.cwd(),
25395
25773
  json: Boolean(flags.json)
25396
25774
  });
25397
25775
  case "clean": {
@@ -25399,7 +25777,7 @@ async function main() {
25399
25777
  console.error("Usage: viberaven clean --plan [path]");
25400
25778
  return 1;
25401
25779
  }
25402
- const cwd = positional[0] ? (0, import_node_path42.resolve)(process.cwd(), positional[0]) : process.cwd();
25780
+ const cwd = positional[0] ? (0, import_node_path43.resolve)(process.cwd(), positional[0]) : process.cwd();
25403
25781
  const plan = buildContextCleanupPlan({ projectRoot: cwd, files: await collectCleanupFiles(cwd) });
25404
25782
  const outputPath = await writeCleanupPlan(cwd, plan);
25405
25783
  console.log(`Wrote ${outputPath}
@@ -25409,9 +25787,9 @@ async function main() {
25409
25787
  }
25410
25788
  case "rls-test":
25411
25789
  return runRlsTestCommand({
25412
- cwd: positional[0] ? (0, import_node_path42.resolve)(process.cwd(), positional[0]) : process.cwd(),
25413
- matrix: typeof flags.matrix === "string" ? (0, import_node_path42.resolve)(process.cwd(), flags.matrix) : void 0,
25414
- migrations: typeof flags.migrations === "string" ? (0, import_node_path42.resolve)(process.cwd(), flags.migrations) : void 0,
25790
+ cwd: positional[0] ? (0, import_node_path43.resolve)(process.cwd(), positional[0]) : process.cwd(),
25791
+ matrix: typeof flags.matrix === "string" ? (0, import_node_path43.resolve)(process.cwd(), flags.matrix) : void 0,
25792
+ migrations: typeof flags.migrations === "string" ? (0, import_node_path43.resolve)(process.cwd(), flags.migrations) : void 0,
25415
25793
  json: Boolean(flags.json)
25416
25794
  });
25417
25795
  default:
@@ -25420,11 +25798,11 @@ async function main() {
25420
25798
  return 1;
25421
25799
  }
25422
25800
  }
25423
- var import_promises32, import_node_path42, STACK_AREAS;
25801
+ var import_promises33, import_node_path43, STACK_AREAS;
25424
25802
  var init_cli = __esm({
25425
25803
  "src/cli.ts"() {
25426
- import_promises32 = require("node:fs/promises");
25427
- import_node_path42 = require("node:path");
25804
+ import_promises33 = require("node:fs/promises");
25805
+ import_node_path43 = require("node:path");
25428
25806
  init_config();
25429
25807
  init_runScan();
25430
25808
  init_artifacts2();
@@ -25466,6 +25844,7 @@ var init_cli = __esm({
25466
25844
  init_buildTaskList();
25467
25845
  init_providerMcpBridge();
25468
25846
  init_render();
25847
+ init_registry2();
25469
25848
  STACK_AREAS = /* @__PURE__ */ new Set(["database", "auth", "payments", "deployment", "monitoring", "security"]);
25470
25849
  if (require.main === module) {
25471
25850
  main().then((code) => {