@viberaven/cli 1.6.1 → 1.6.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/cli.js +1200 -812
  2. package/package.json +17 -6
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
  `);
@@ -8563,6 +8585,16 @@ function safe(text) {
8563
8585
  function at(loc) {
8564
8586
  return loc ? `${loc.file}:${loc.line}` : "an unknown location";
8565
8587
  }
8588
+ function policyNameNote(name) {
8589
+ if (/\bservice[ _-]?role\b/i.test(name)) {
8590
+ return ` The name mentions the service role, but the service role bypasses row level security and never needs a policy, so this one only opens the table to everyone else.`;
8591
+ }
8592
+ const promise = name.match(/\b(?:own|owner|owners|their|admins?|teachers?|creators?|authors?|members?|self)\b/i);
8593
+ if (promise) {
8594
+ return ` The name says "${name}", but nothing in the policy checks who the caller is.`;
8595
+ }
8596
+ return "";
8597
+ }
8566
8598
  function audience(roles) {
8567
8599
  const set = new Set(roles);
8568
8600
  if (set.has("public") || set.has("anon") && set.has("authenticated")) return "anyone, signed in or not,";
@@ -8618,7 +8650,7 @@ function analyzeRls(replay) {
8618
8650
  id: "rls_policy_allows_all_read",
8619
8651
  severity: "high",
8620
8652
  message: safe(
8621
- `Policy "${policy.name}" on ${table2.display} (${at(policy.loc)}) lets ${who} read every row: using (true). Fine for truly public content; wrong for anything per-user.`
8653
+ `Policy "${policy.name}" on ${table2.display} (${at(policy.loc)}) lets ${who} read every row: using (true). Fine for truly public content; wrong for anything per-user.${policyNameNote(policy.name)}`
8622
8654
  ),
8623
8655
  file: policy.loc.file,
8624
8656
  line: policy.loc.line,
@@ -8650,7 +8682,7 @@ function analyzeRls(replay) {
8650
8682
  permissive.push({
8651
8683
  id: "rls_policy_allows_all_write",
8652
8684
  severity,
8653
- message: safe(`Policy "${policy.name}" on ${table2.display} (${at(policy.loc)}) lets ${who} ${verb}: ${clause}.`),
8685
+ message: safe(`Policy "${policy.name}" on ${table2.display} (${at(policy.loc)}) lets ${who} ${verb}: ${clause}.${severity === "critical" ? policyNameNote(policy.name) : ""}`),
8654
8686
  file: policy.loc.file,
8655
8687
  line: policy.loc.line,
8656
8688
  table: table2.display,
@@ -10427,6 +10459,18 @@ var init_databaseEvidence = __esm({
10427
10459
  });
10428
10460
 
10429
10461
  // src/localStation.ts
10462
+ function findingLocations(findings) {
10463
+ const seen = /* @__PURE__ */ new Set();
10464
+ const out = [];
10465
+ for (const finding of findings) {
10466
+ if (!finding.file) continue;
10467
+ const key = `${finding.file}:${finding.line ?? ""}`;
10468
+ if (seen.has(key)) continue;
10469
+ seen.add(key);
10470
+ out.push(finding.line ? { file: finding.file, line: finding.line } : { file: finding.file });
10471
+ }
10472
+ return out.slice(0, DETAIL_MAX_FINDINGS);
10473
+ }
10430
10474
  function hasFilePath(scan, pattern) {
10431
10475
  return scan.files.some((f) => pattern.test(f.path));
10432
10476
  }
@@ -10720,7 +10764,8 @@ function auditFindingSeeds(audit) {
10720
10764
  detail: capLines(lines).join("\n"),
10721
10765
  copyPrompt: fixes.length > 0 ? `${meta.prompt}
10722
10766
  ${capLines(fixes).join("\n")}` : meta.prompt,
10723
- primaryMapCategory: meta.map
10767
+ primaryMapCategory: meta.map,
10768
+ locations: findingLocations(findings)
10724
10769
  };
10725
10770
  });
10726
10771
  }
@@ -10919,6 +10964,7 @@ ${check.evidence.map((e2) => `- ${e2}`).join("\n")}` : "";
10919
10964
  category: "SECURITY & AUTH",
10920
10965
  severity: "warning",
10921
10966
  title: "Stripe webhook does not verify the signature",
10967
+ locations: findingLocations(webhooks.unverified.map((u2) => ({ file: u2.path }))),
10922
10968
  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.`,
10923
10969
  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.',
10924
10970
  primaryMapCategory: "payments"
@@ -10955,7 +11001,7 @@ ${check.evidence.map((e2) => `- ${e2}`).join("\n")}` : "";
10955
11001
  severity: "warning",
10956
11002
  title: `Env vars used in code but missing from ${target}`,
10957
11003
  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.`,
10958
- 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).`,
11004
+ 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).`,
10959
11005
  primaryMapCategory: "deployment"
10960
11006
  });
10961
11007
  } else {
@@ -11022,9 +11068,9 @@ function scoreFrom(gaps) {
11022
11068
  }
11023
11069
  return Math.max(5, Math.min(96, score));
11024
11070
  }
11025
- function labelFor(score, criticalCount) {
11071
+ function scoreLabelFor(score, criticalCount, warningCount) {
11026
11072
  if (criticalCount > 0) return "Blocked";
11027
- if (score >= 85) return "Launch ready";
11073
+ if (warningCount === 0) return "No blockers in repo checks";
11028
11074
  if (score >= 60) return "Almost there";
11029
11075
  return "Needs work";
11030
11076
  }
@@ -11123,11 +11169,12 @@ async function buildLocalStationOutput(input) {
11123
11169
  toolSuggestions: [],
11124
11170
  mcpSuggestion: null,
11125
11171
  primaryMapCategory: seed.primaryMapCategory,
11126
- affectedMapCategories: [seed.primaryMapCategory]
11172
+ affectedMapCategories: [seed.primaryMapCategory],
11173
+ ...seed.locations?.length ? { locations: seed.locations } : {}
11127
11174
  }));
11128
11175
  return {
11129
11176
  score,
11130
- scoreLabel: labelFor(score, criticalCount),
11177
+ scoreLabel: scoreLabelFor(score, criticalCount, warningCount),
11131
11178
  summary,
11132
11179
  archetype,
11133
11180
  gaps,
@@ -11583,6 +11630,28 @@ var init_runScan = __esm({
11583
11630
  }
11584
11631
  });
11585
11632
 
11633
+ // src/contracts/detectedProviders.ts
11634
+ function isProviderInRepo(row) {
11635
+ if ((row.roles ?? []).includes("runtime-code")) return true;
11636
+ const evidence = (row.evidence ?? []).filter((item4) => item4.kind !== "docs-mention");
11637
+ if (!evidence.some((item4) => PACKAGE_OR_CONFIG.has(item4.kind))) return false;
11638
+ return !evidence.every((item4) => item4.kind === "package-installed");
11639
+ }
11640
+ function providersInRepo(artifact) {
11641
+ const areas = artifact.providerTruth?.areas;
11642
+ if (!Array.isArray(areas)) return void 0;
11643
+ return areas.flatMap(
11644
+ (area) => (area.rows ?? []).filter(isProviderInRepo).map((row) => ({ area: String(area.area), provider: String(row.provider) }))
11645
+ );
11646
+ }
11647
+ var PACKAGE_OR_CONFIG;
11648
+ var init_detectedProviders = __esm({
11649
+ "src/contracts/detectedProviders.ts"() {
11650
+ "use strict";
11651
+ PACKAGE_OR_CONFIG = /* @__PURE__ */ new Set(["package-installed", "deployment-config", "provider-config"]);
11652
+ }
11653
+ });
11654
+
11586
11655
  // src/capabilities/classify.ts
11587
11656
  function gapText(gap) {
11588
11657
  return `${gap.id} ${gap.title} ${gap.detail} ${gap.primaryMapCategory}`.toLowerCase();
@@ -11770,6 +11839,37 @@ var init_capabilities = __esm({
11770
11839
  }
11771
11840
  });
11772
11841
 
11842
+ // src/contracts/packageManager.ts
11843
+ function detectPackageManager(workspacePath) {
11844
+ if (!workspacePath) return "unknown";
11845
+ try {
11846
+ const manifest = JSON.parse((0, import_node_fs4.readFileSync)((0, import_node_path8.join)(workspacePath, "package.json"), "utf8").replace(/^\uFEFF/, ""));
11847
+ const named = typeof manifest.packageManager === "string" ? manifest.packageManager.split("@")[0] : "";
11848
+ if (named === "npm" || named === "pnpm" || named === "yarn" || named === "bun") return named;
11849
+ } catch {
11850
+ }
11851
+ for (const [file, manager] of LOCKFILES) {
11852
+ if ((0, import_node_fs4.existsSync)((0, import_node_path8.join)(workspacePath, file))) return manager;
11853
+ }
11854
+ return "unknown";
11855
+ }
11856
+ var import_node_fs4, import_node_path8, LOCKFILES;
11857
+ var init_packageManager = __esm({
11858
+ "src/contracts/packageManager.ts"() {
11859
+ "use strict";
11860
+ import_node_fs4 = require("node:fs");
11861
+ import_node_path8 = require("node:path");
11862
+ LOCKFILES = [
11863
+ ["pnpm-lock.yaml", "pnpm"],
11864
+ ["yarn.lock", "yarn"],
11865
+ ["bun.lock", "bun"],
11866
+ ["bun.lockb", "bun"],
11867
+ ["package-lock.json", "npm"],
11868
+ ["npm-shrinkwrap.json", "npm"]
11869
+ ];
11870
+ }
11871
+ });
11872
+
11773
11873
  // src/contracts/commands.ts
11774
11874
  function promptGapCommand(gapId) {
11775
11875
  return `${PUBLIC_COMMAND} prompt --gap ${gapId}`;
@@ -11797,65 +11897,18 @@ var init_commands = __esm({
11797
11897
  }
11798
11898
  });
11799
11899
 
11800
- // src/contracts/contextMap.ts
11801
- function generateContextMap(artifact) {
11802
- const criticalCount = artifact.gaps.filter((gap) => gap.severity === "critical").length;
11803
- const warningCount = artifact.gaps.filter((gap) => gap.severity === "warning").length;
11804
- return {
11805
- $schema: "https://viberaven.dev/schemas/context-map.schema.json",
11806
- schemaVersion: "v1",
11807
- generatedAt: artifact.scannedAt,
11808
- workspace: {
11809
- root: artifact.workspacePath,
11810
- packageManager: "unknown",
11811
- languages: ["typescript"],
11812
- frameworks: artifact.archetype ? [artifact.archetype] : []
11813
- },
11814
- agentContract: {
11815
- readFirst: ".viberaven/agent-tasklist.md",
11816
- machineResult: ".viberaven/gate-result.json",
11817
- contextMap: ".viberaven/context-map.json",
11818
- verifyCommand: PUBLIC_VERIFY_COMMAND,
11819
- strictCommand: PUBLIC_STRICT_COMMAND,
11820
- nextCommand: PUBLIC_NEXT_JSON_COMMAND
11821
- },
11822
- stack: {
11823
- deployment: artifact.selectedProviders?.deployment ? [artifact.selectedProviders.deployment] : [],
11824
- database: artifact.selectedProviders?.database ? [artifact.selectedProviders.database] : [],
11825
- auth: artifact.selectedProviders?.auth ? [artifact.selectedProviders.auth] : [],
11826
- payments: artifact.selectedProviders?.payments ? [artifact.selectedProviders.payments] : [],
11827
- monitoring: artifact.selectedProviders?.monitoring ? [artifact.selectedProviders.monitoring] : []
11828
- },
11829
- capabilityGraph: summarizeCapabilities(artifact.gaps),
11830
- productionGate: {
11831
- status: criticalCount > 0 ? "not_clear" : warningCount > 0 ? "warning" : "clear",
11832
- criticalCount,
11833
- warningCount,
11834
- providerDashboardChecksRequired: true
11835
- },
11836
- topGaps: artifact.gaps.slice(0, 10).map((gap) => ({
11837
- id: gap.id,
11838
- severity: gap.severity,
11839
- title: gap.title,
11840
- area: String(gap.primaryMapCategory),
11841
- promptCommand: promptGapCommand(gap.id)
11842
- }))
11843
- };
11844
- }
11845
- var init_contextMap = __esm({
11846
- "src/contracts/contextMap.ts"() {
11847
- "use strict";
11848
- init_capabilities();
11849
- init_commands();
11850
- }
11851
- });
11852
-
11853
11900
  // src/contracts/gateResult.ts
11854
11901
  function gateStatus(criticalCount, warningCount) {
11855
11902
  if (criticalCount > 0) return "not_clear";
11856
11903
  if (warningCount > 0) return "warning";
11857
11904
  return "clear";
11858
11905
  }
11906
+ function gateStatusForGaps(gaps) {
11907
+ return gateStatus(
11908
+ gaps.filter((gap) => gap.severity === "critical").length,
11909
+ gaps.filter((gap) => gap.severity === "warning").length
11910
+ );
11911
+ }
11859
11912
  function runIdFrom(scannedAt) {
11860
11913
  return `vr_${scannedAt.replace(/\D/g, "").slice(0, 14) || "scan"}`;
11861
11914
  }
@@ -11873,7 +11926,7 @@ function generateGateResult(artifact, options = {}) {
11873
11926
  generatedAt: artifact.scannedAt,
11874
11927
  workspace: {
11875
11928
  root: artifact.workspacePath,
11876
- packageManager: "unknown",
11929
+ packageManager: options.packageManager ?? detectPackageManager(artifact.workspacePath),
11877
11930
  languages: ["typescript"],
11878
11931
  frameworks: artifact.archetype ? [artifact.archetype] : []
11879
11932
  },
@@ -11914,101 +11967,246 @@ var init_gateResult = __esm({
11914
11967
  "src/contracts/gateResult.ts"() {
11915
11968
  "use strict";
11916
11969
  init_capabilities();
11970
+ init_packageManager();
11917
11971
  init_commands();
11918
11972
  }
11919
11973
  });
11920
11974
 
11921
- // src/contracts/gapEvidence.ts
11922
- function summarizeGap(gap) {
11923
- return `${gap.title}. See the tasklist and original scan for redacted detail.`;
11975
+ // src/contracts/contextMap.ts
11976
+ function detectedStack(artifact) {
11977
+ const stack = { deployment: [], database: [], auth: [], payments: [], monitoring: [] };
11978
+ const add = (area, provider2) => {
11979
+ if (!(area in stack) || typeof provider2 !== "string" || !provider2) return;
11980
+ const list = stack[area];
11981
+ if (!list.includes(provider2)) list.push(provider2);
11982
+ };
11983
+ for (const entry of providersInRepo(artifact) ?? []) add(entry.area, entry.provider);
11984
+ for (const [area, provider2] of Object.entries(artifact.selectedProviders ?? {})) add(area, provider2);
11985
+ return stack;
11924
11986
  }
11925
- function generateGapEvidenceFiles(artifact) {
11926
- return artifact.gaps.slice(0, 50).map((gap) => ({
11927
- path: `.viberaven/gaps/${gap.id}.json`,
11928
- content: {
11929
- $schema: "https://viberaven.dev/schemas/gap.schema.json",
11930
- schemaVersion: "v1",
11987
+ function generateContextMap(artifact, options = {}) {
11988
+ const criticalCount = artifact.gaps.filter((gap) => gap.severity === "critical").length;
11989
+ const warningCount = artifact.gaps.filter((gap) => gap.severity === "warning").length;
11990
+ return {
11991
+ $schema: "https://viberaven.dev/schemas/context-map.schema.json",
11992
+ schemaVersion: "v1",
11993
+ generatedAt: artifact.scannedAt,
11994
+ workspace: {
11995
+ root: artifact.workspacePath,
11996
+ packageManager: options.packageManager ?? detectPackageManager(artifact.workspacePath),
11997
+ languages: ["typescript"],
11998
+ frameworks: artifact.archetype ? [artifact.archetype] : []
11999
+ },
12000
+ agentContract: {
12001
+ readFirst: ".viberaven/agent-tasklist.md",
12002
+ machineResult: ".viberaven/gate-result.json",
12003
+ contextMap: ".viberaven/context-map.json",
12004
+ verifyCommand: PUBLIC_VERIFY_COMMAND,
12005
+ strictCommand: PUBLIC_STRICT_COMMAND,
12006
+ nextCommand: PUBLIC_NEXT_JSON_COMMAND
12007
+ },
12008
+ stack: detectedStack(artifact),
12009
+ capabilityGraph: summarizeCapabilities(artifact.gaps),
12010
+ productionGate: {
12011
+ status: gateStatusForGaps(artifact.gaps),
12012
+ criticalCount,
12013
+ warningCount,
12014
+ providerDashboardChecksRequired: true
12015
+ },
12016
+ topGaps: artifact.gaps.slice(0, 10).map((gap) => ({
11931
12017
  id: gap.id,
11932
12018
  severity: gap.severity,
11933
- capability: classifyGapCapability(gap),
11934
12019
  title: gap.title,
11935
- summary: summarizeGap(gap),
11936
- evidence: [{ kind: String(gap.primaryMapCategory), redacted: true }],
11937
- commands: {
11938
- prompt: promptGapCommand(gap.id),
11939
- healPlan: healPlanGapCommand(gap.id),
11940
- verify: PUBLIC_VERIFY_COMMAND
11941
- },
11942
- providerBoundary: {
11943
- requiresDashboardCheck: true,
11944
- reason: "Repo evidence cannot prove live provider dashboard state."
11945
- }
11946
- }
11947
- }));
12020
+ area: String(gap.primaryMapCategory),
12021
+ promptCommand: promptGapCommand(gap.id)
12022
+ }))
12023
+ };
11948
12024
  }
11949
- var init_gapEvidence = __esm({
11950
- "src/contracts/gapEvidence.ts"() {
12025
+ var init_contextMap = __esm({
12026
+ "src/contracts/contextMap.ts"() {
11951
12027
  "use strict";
12028
+ init_detectedProviders();
12029
+ init_gateResult();
11952
12030
  init_capabilities();
12031
+ init_packageManager();
11953
12032
  init_commands();
11954
12033
  }
11955
12034
  });
11956
12035
 
11957
- // src/playbooks/checkMap.ts
11958
- function mapCheckToPlaybook(check) {
11959
- if (CHECK_TO_PLAYBOOK[check.id]) {
11960
- return CHECK_TO_PLAYBOOK[check.id];
12036
+ // src/contracts/gapLocations.ts
12037
+ function locationsForGap(gap) {
12038
+ return gap.locations && gap.locations.length > 0 ? gap.locations : gapLocations(gap.detail);
12039
+ }
12040
+ function withoutUrlsAndAddresses(text) {
12041
+ return text.split(/(\s+)/).map((word) => /:\/\/|@|=/.test(word) ? " " : word).join("");
12042
+ }
12043
+ function gapLocations(detail) {
12044
+ if (typeof detail !== "string") return [];
12045
+ const seen = /* @__PURE__ */ new Set();
12046
+ const out = [];
12047
+ for (const rawLine of detail.split(/\r?\n/)) {
12048
+ const text = withoutUrlsAndAddresses(rawLine.split(" Fix:")[0]);
12049
+ for (const match of text.matchAll(LOCATION)) {
12050
+ const folder = match[1].replace(/\\/g, "/");
12051
+ const name = match[2];
12052
+ const file = `${folder}${name}`;
12053
+ const line = match[3] ? Number(match[3]) : void 0;
12054
+ if (line !== void 0 && !(line > 0)) continue;
12055
+ const envFile = name.startsWith(".env");
12056
+ if (!envFile && (VERSION_LIKE.test(name) || VERSION_LIKE.test(name.replace(/\.[A-Za-z0-9]+$/, "")))) continue;
12057
+ const codeFile = CODE_EXTENSION.test(name) && !(folder === "" && PROSE_NAME.test(name));
12058
+ const configFile = CONFIG_EXTENSION.test(name);
12059
+ if (line !== void 0) {
12060
+ if (!envFile && !codeFile && !configFile) continue;
12061
+ } else if (!envFile && !codeFile && !(configFile && folder !== "")) {
12062
+ continue;
12063
+ }
12064
+ const key = `${file}:${line ?? ""}`;
12065
+ if (seen.has(key)) continue;
12066
+ seen.add(key);
12067
+ out.push(line === void 0 ? { file } : { file, line });
12068
+ }
11961
12069
  }
11962
- const providerKey = check.providerKey.toLowerCase();
11963
- if (providerKey.includes("vercel") || check.area === "deployment") {
11964
- return "vercel";
12070
+ return out;
12071
+ }
12072
+ var SEGMENT, NAME, LOCATION, CODE_EXTENSION, CONFIG_EXTENSION, PROSE_NAME, VERSION_LIKE;
12073
+ var init_gapLocations = __esm({
12074
+ "src/contracts/gapLocations.ts"() {
12075
+ "use strict";
12076
+ SEGMENT = String.raw`(?:\([\w.-]+\)|\[[\w.-]+\]|[\w-][\w.-]*)`;
12077
+ NAME = String.raw`(\.env(?:\.[\w-]+)*|(?:\[[\w.-]+\]|[\w-][\w.-]*?)\.[A-Za-z0-9]+)`;
12078
+ LOCATION = new RegExp(
12079
+ String.raw`(?<![\w./\\:-])((?:[A-Za-z]:[\\/])?(?:${SEGMENT}[\\/])*)${NAME}(?::(\d+))?(?!\.?[\w\\/])`,
12080
+ "g"
12081
+ );
12082
+ CODE_EXTENSION = /\.(?:sql|[cm]?[jt]sx?|py|rb|php|go|rs|java|kt|cs|swift|ex|exs|vue|svelte|astro)$/i;
12083
+ CONFIG_EXTENSION = /\.(?:json|toml|ya?ml)$/i;
12084
+ PROSE_NAME = /^(?:next|node|nuxt|vue|express|nest|react|three|chart|solid|ember|backbone|angular|alpine|d3)\.js$/i;
12085
+ VERSION_LIKE = /^v?\d+(?:\.\d+)+$/i;
11965
12086
  }
11966
- if (providerKey.includes("stripe") || check.area === "payments") {
11967
- return "stripe";
12087
+ });
12088
+
12089
+ // src/contracts/repoFixes.ts
12090
+ 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;
12091
+ var init_repoFixes = __esm({
12092
+ "src/contracts/repoFixes.ts"() {
12093
+ "use strict";
12094
+ APPLY_MIGRATION = "Apply the new migration to the live database the way this project applies migrations.";
12095
+ ROTATE_KEY = "Rotate the service role key in the Supabase dashboard if this key was ever deployed or committed with a real value.";
12096
+ REPO_FIX_GAPS = {
12097
+ rls_policy_allows_all_write: [APPLY_MIGRATION],
12098
+ rls_policy_allows_all_read: [APPLY_MIGRATION],
12099
+ data_api_grant_without_rls: [APPLY_MIGRATION],
12100
+ data_api_grant_missing: [APPLY_MIGRATION],
12101
+ security_definer_function_executable: [APPLY_MIGRATION],
12102
+ service_role_key_in_client_env: [ROTATE_KEY],
12103
+ service_role_key_in_client_code: [ROTATE_KEY],
12104
+ env_var_drift: ["Set each of these variables in the environment where the app is deployed."],
12105
+ unverified_stripe_webhook: [
12106
+ "Set STRIPE_WEBHOOK_SECRET where the handler runs to the signing secret of this endpoint in the Stripe dashboard, then deploy the handler."
12107
+ ]
12108
+ };
12109
+ INFORMATIONAL_GAPS = {
12110
+ 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."
12111
+ };
12112
+ REPO_ONLY_RECIPE_GAPS = [
12113
+ // emptyCatch (original recipe)
12114
+ "empty-catch",
12115
+ "empty_catch",
12116
+ "emptyCatch",
12117
+ // file-create recipes
12118
+ "missing_error_boundary",
12119
+ "missing_health_route",
12120
+ "missing_loading_state",
12121
+ "missing_404_page",
12122
+ // file-patch recipes
12123
+ "missing_csp_header",
12124
+ "eslint_restricted_imports"
12125
+ ];
12126
+ RECIPE_GAPS_WITH_OUTSIDE_STEP = ["auth_secret_missing", "node_env_not_set", "database_url_missing", "missing_rate_limit"];
12127
+ KNOWN_RECIPE_GAP_IDS = /* @__PURE__ */ new Set([...REPO_ONLY_RECIPE_GAPS, ...RECIPE_GAPS_WITH_OUTSIDE_STEP]);
12128
+ REPO_ONLY_GAPS = /* @__PURE__ */ new Set([...Object.keys(INFORMATIONAL_GAPS), ...REPO_ONLY_RECIPE_GAPS]);
11968
12129
  }
11969
- if (providerKey.includes("supabase") && check.area === "auth") {
11970
- return "auth-supabase";
12130
+ });
12131
+
12132
+ // src/heal/rlsMigrationTarget.ts
12133
+ function rlsEnableStatements(tables) {
12134
+ return [...new Set(tables)].map((table2) => `alter table ${table2} enable row level security;`);
12135
+ }
12136
+ function migrationStamp(now) {
12137
+ return now.toISOString().replace(/\D/g, "").slice(0, 14);
12138
+ }
12139
+ function rlsMigrationTarget(cwd, evidence, now = /* @__PURE__ */ new Date()) {
12140
+ if (evidence.length === 0) return { ok: false, reason: "No table without row level security was found in the repo SQL." };
12141
+ const statementList = rlsEnableStatements(evidence.map((entry) => entry.table)).join(" ");
12142
+ const turnedOff = evidence.filter((entry) => entry.turnedOff);
12143
+ if (turnedOff.length > 0) {
12144
+ const where = turnedOff.map((entry) => `${entry.table} (${entry.file.replace(/\\/g, "/")})`).join(", ");
12145
+ return {
12146
+ ok: false,
12147
+ 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}`
12148
+ };
11971
12149
  }
11972
- if (providerKey.includes("supabase") || check.area === "database") {
11973
- return "supabase";
12150
+ const source = evidence[0].file.replace(/\\/g, "/");
12151
+ const dir = import_node_path9.posix.dirname(source);
12152
+ if (import_node_path9.posix.basename(dir) !== "migrations") {
12153
+ return {
12154
+ ok: false,
12155
+ 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}`
12156
+ };
11974
12157
  }
11975
- if (check.area === "auth") {
11976
- return "auth-supabase";
12158
+ let existing;
12159
+ try {
12160
+ existing = (0, import_node_fs5.readdirSync)((0, import_node_path9.join)(cwd, dir)).filter((file) => file.endsWith(".sql")).sort();
12161
+ } catch {
12162
+ return { ok: false, reason: `VibeRaven could not read ${dir}. Add these statements by hand in a new migration: ${statementList}` };
12163
+ }
12164
+ const otherNaming = existing.find((file) => !TIMESTAMPED_MIGRATION.test(file));
12165
+ if (otherNaming) {
12166
+ return {
12167
+ ok: false,
12168
+ 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}`
12169
+ };
12170
+ }
12171
+ const name = `${migrationStamp(now)}_viberaven_enable_rls.sql`;
12172
+ const last = existing[existing.length - 1];
12173
+ if (last && name <= last) {
12174
+ return {
12175
+ ok: false,
12176
+ 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}`
12177
+ };
11977
12178
  }
11978
- return "vercel";
12179
+ return { ok: true, file: `${dir}/${name}` };
11979
12180
  }
11980
- var CHECK_TO_PLAYBOOK;
11981
- var init_checkMap = __esm({
11982
- "src/playbooks/checkMap.ts"() {
12181
+ var import_node_fs5, import_node_path9, RLS_TURNED_OFF_PHRASE, RLS_ASK_FIRST, RLS_NO_POLICIES_WARNING, TIMESTAMPED_MIGRATION;
12182
+ var init_rlsMigrationTarget = __esm({
12183
+ "src/heal/rlsMigrationTarget.ts"() {
11983
12184
  "use strict";
11984
- CHECK_TO_PLAYBOOK = {
11985
- "vercel-deployment": "vercel",
11986
- "vercel-project": "vercel",
11987
- "supabase-project": "supabase",
11988
- "supabase-env": "supabase",
11989
- "stripe-webhook": "stripe",
11990
- "stripe-product": "stripe",
11991
- "stripe-keys": "stripe"
11992
- };
12185
+ import_node_fs5 = require("node:fs");
12186
+ import_node_path9 = require("node:path");
12187
+ RLS_TURNED_OFF_PHRASE = "has row level security turned off at";
12188
+ RLS_ASK_FIRST = "Ask the user first: enabling RLS without policies makes browser reads return no rows until policies exist.";
12189
+ 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.";
12190
+ TIMESTAMPED_MIGRATION = /^\d{14}_.+\.sql$/;
11993
12191
  }
11994
12192
  });
11995
12193
 
11996
12194
  // src/playbooks/playbooksRoot.ts
11997
12195
  function playbooksRoot() {
11998
- const candidates = [(0, import_node_path8.join)(__dirname, "playbooks"), (0, import_node_path8.join)(__dirname, "..", "..", "playbooks")];
12196
+ const candidates = [(0, import_node_path10.join)(__dirname, "playbooks"), (0, import_node_path10.join)(__dirname, "..", "..", "playbooks")];
11999
12197
  for (const dir of candidates) {
12000
- if ((0, import_node_fs4.existsSync)((0, import_node_path8.join)(dir, "vercel.json"))) {
12198
+ if ((0, import_node_fs6.existsSync)((0, import_node_path10.join)(dir, "vercel.json"))) {
12001
12199
  return dir;
12002
12200
  }
12003
12201
  }
12004
12202
  throw new Error("VibeRaven playbooks directory not found (expected vercel.json)");
12005
12203
  }
12006
- var import_node_fs4, import_node_path8;
12204
+ var import_node_fs6, import_node_path10;
12007
12205
  var init_playbooksRoot = __esm({
12008
12206
  "src/playbooks/playbooksRoot.ts"() {
12009
12207
  "use strict";
12010
- import_node_fs4 = require("node:fs");
12011
- import_node_path8 = require("node:path");
12208
+ import_node_fs6 = require("node:fs");
12209
+ import_node_path10 = require("node:path");
12012
12210
  }
12013
12211
  });
12014
12212
 
@@ -12115,7 +12313,7 @@ async function loadPlaybook(provider2) {
12115
12313
  `Unknown provider "${provider2}". Available: ${PLAYBOOK_PROVIDERS.join(", ")}`
12116
12314
  );
12117
12315
  }
12118
- const path = (0, import_node_path9.join)(playbooksRoot(), `${normalized}.json`);
12316
+ const path = (0, import_node_path11.join)(playbooksRoot(), `${normalized}.json`);
12119
12317
  const raw = JSON.parse(await (0, import_promises7.readFile)(path, "utf-8"));
12120
12318
  const playbook = parsePlaybook(raw);
12121
12319
  if (playbook.provider !== normalized) {
@@ -12130,59 +12328,30 @@ function loadPlaybookSync(provider2) {
12130
12328
  `Unknown provider "${provider2}". Available: ${PLAYBOOK_PROVIDERS.join(", ")}`
12131
12329
  );
12132
12330
  }
12133
- const path = (0, import_node_path9.join)(playbooksRoot(), `${normalized}.json`);
12134
- const raw = JSON.parse((0, import_node_fs5.readFileSync)(path, "utf-8"));
12331
+ const path = (0, import_node_path11.join)(playbooksRoot(), `${normalized}.json`);
12332
+ const raw = JSON.parse((0, import_node_fs7.readFileSync)(path, "utf-8"));
12135
12333
  const playbook = parsePlaybook(raw);
12136
12334
  if (playbook.provider !== normalized) {
12137
12335
  throw new Error(`Playbook file ${normalized}.json has mismatched provider field`);
12138
12336
  }
12139
12337
  return playbook;
12140
12338
  }
12141
- var import_node_fs5, import_promises7, import_node_path9;
12339
+ var import_node_fs7, import_promises7, import_node_path11;
12142
12340
  var init_loadPlaybook = __esm({
12143
12341
  "src/playbooks/loadPlaybook.ts"() {
12144
12342
  "use strict";
12145
- import_node_fs5 = require("node:fs");
12343
+ import_node_fs7 = require("node:fs");
12146
12344
  import_promises7 = require("node:fs/promises");
12147
- import_node_path9 = require("node:path");
12345
+ import_node_path11 = require("node:path");
12148
12346
  init_playbooksRoot();
12149
12347
  init_types2();
12150
12348
  init_validate();
12151
12349
  }
12152
12350
  });
12153
12351
 
12154
- // src/playbooks/manualChecks.ts
12155
- function isManualProviderCheck(check) {
12156
- return check.evidenceClass === "manual-dashboard" || check.evidenceClass === "mcp-verifier" || check.evidenceSource === "provider" || check.evidenceSource === "mcp" || check.status === "needs-connection" || check.status === "unknown";
12157
- }
12158
- function collectManualChecks(artifact) {
12159
- const refs = [];
12160
- for (const area of artifact.missionGraph.areas ?? []) {
12161
- for (const mission of area.providerMissions) {
12162
- for (const check of mission.checks) {
12163
- if (!isManualProviderCheck(check)) {
12164
- continue;
12165
- }
12166
- refs.push({
12167
- areaLabel: area.label,
12168
- providerLabel: mission.providerLabel,
12169
- check,
12170
- mapCategory: area.key
12171
- });
12172
- }
12173
- }
12174
- }
12175
- return refs;
12176
- }
12177
- var init_manualChecks = __esm({
12178
- "src/playbooks/manualChecks.ts"() {
12179
- "use strict";
12180
- }
12181
- });
12182
-
12183
- // src/tui/menu.ts
12184
- function isScanNotFoundError(error) {
12185
- return error instanceof ScanNotFoundError;
12352
+ // src/tui/menu.ts
12353
+ function isScanNotFoundError(error) {
12354
+ return error instanceof ScanNotFoundError;
12186
12355
  }
12187
12356
  function needsScanMessage(startDir) {
12188
12357
  const cwd = startDir ?? process.cwd();
@@ -12229,7 +12398,7 @@ async function loadLastArtifact(startDir) {
12229
12398
  if (!workspace) {
12230
12399
  throw new ScanNotFoundError(needsScanMessage(startDir));
12231
12400
  }
12232
- const path = (0, import_node_path10.join)(getProjectArtifactsDir(workspace), "last-scan.json");
12401
+ const path = (0, import_node_path12.join)(getProjectArtifactsDir(workspace), "last-scan.json");
12233
12402
  try {
12234
12403
  const raw = await (0, import_promises8.readFile)(path, "utf-8");
12235
12404
  return JSON.parse(raw);
@@ -12237,12 +12406,12 @@ async function loadLastArtifact(startDir) {
12237
12406
  throw new ScanNotFoundError(needsScanMessage(startDir));
12238
12407
  }
12239
12408
  }
12240
- var import_promises8, import_node_path10, SEVERITY_RANK2, ScanNotFoundError;
12409
+ var import_promises8, import_node_path12, SEVERITY_RANK2, ScanNotFoundError;
12241
12410
  var init_menu = __esm({
12242
12411
  "src/tui/menu.ts"() {
12243
12412
  "use strict";
12244
12413
  import_promises8 = require("node:fs/promises");
12245
- import_node_path10 = require("node:path");
12414
+ import_node_path12 = require("node:path");
12246
12415
  init_config();
12247
12416
  SEVERITY_RANK2 = {
12248
12417
  critical: 0,
@@ -12258,228 +12427,6 @@ var init_menu = __esm({
12258
12427
  }
12259
12428
  });
12260
12429
 
12261
- // src/resolveNextAction.ts
12262
- function resolveNextAction(artifact) {
12263
- const repoGap = sortGapsByPriority(artifact.gaps)[0];
12264
- if (repoGap) {
12265
- return {
12266
- type: "repo-fix",
12267
- title: repoGap.title,
12268
- detail: repoGap.detail,
12269
- command: `viberaven prompt --gap ${repoGap.id}`
12270
- };
12271
- }
12272
- const manual = collectManualChecks(artifact)[0];
12273
- if (manual) {
12274
- const provider2 = mapCheckToPlaybook(manual.check);
12275
- let openUrl;
12276
- try {
12277
- const playbook = loadPlaybookSync(provider2);
12278
- openUrl = playbook.steps[0]?.openUrl;
12279
- } catch {
12280
- openUrl = void 0;
12281
- }
12282
- return {
12283
- type: "provider-guide",
12284
- title: manual.check.label,
12285
- detail: manual.check.promptHint || `Configure ${manual.providerLabel} in the provider dashboard (${manual.areaLabel}).`,
12286
- provider: provider2,
12287
- playbookStep: 1,
12288
- command: `viberaven guide ${provider2} --step 1`,
12289
- openUrl
12290
- };
12291
- }
12292
- return {
12293
- type: "done",
12294
- title: "No blockers found",
12295
- detail: "Re-scan after changes to confirm production readiness."
12296
- };
12297
- }
12298
- var init_resolveNextAction = __esm({
12299
- "src/resolveNextAction.ts"() {
12300
- "use strict";
12301
- init_checkMap();
12302
- init_loadPlaybook();
12303
- init_manualChecks();
12304
- init_menu();
12305
- }
12306
- });
12307
-
12308
- // src/report/agentSummary.ts
12309
- function sortGaps(gaps) {
12310
- return [...gaps].sort((a, b3) => {
12311
- const sev = SEVERITY_ORDER2[a.severity] - SEVERITY_ORDER2[b3.severity];
12312
- if (sev !== 0) {
12313
- return sev;
12314
- }
12315
- return a.title.localeCompare(b3.title);
12316
- });
12317
- }
12318
- function generateAgentSummary(artifact) {
12319
- const lines = [];
12320
- const topGaps = sortGaps(artifact.gaps).slice(0, 8);
12321
- lines.push("# VibeRaven agent summary", "");
12322
- lines.push(`Scanned: \`${artifact.workspacePath}\``);
12323
- lines.push(`At: ${artifact.scannedAt}`);
12324
- lines.push(
12325
- `Production core: **${artifact.productionCorePercent}%** \xB7 Model score: **${artifact.score}** (${artifact.scoreLabel})`
12326
- );
12327
- lines.push("");
12328
- const next = resolveNextAction(artifact);
12329
- lines.push("## Next action", "");
12330
- lines.push(`**${next.title}** \u2014 ${next.detail}`);
12331
- if (next.command) {
12332
- lines.push(`Command: \`${next.command}\``);
12333
- }
12334
- lines.push("");
12335
- lines.push("## Suggested stack", "");
12336
- lines.push(
12337
- "React + Tailwind + shadcn/ui + Supabase + Vercel (agent-default stack for lowest launch friction)"
12338
- );
12339
- lines.push("");
12340
- lines.push("## Summary");
12341
- lines.push(artifact.summary || "_No summary returned._");
12342
- lines.push("");
12343
- lines.push("## Mission map (repo wiring)");
12344
- lines.push("");
12345
- lines.push("| Area | Provider | Readiness | Notes |");
12346
- lines.push("|------|----------|-----------|-------|");
12347
- for (const area of artifact.missionGraph.areas ?? []) {
12348
- for (const mission of area.providerMissions) {
12349
- const failed = mission.checks.filter(
12350
- (c) => c.status === "missing" || c.status === "failed" || c.status === "needs-connection"
12351
- ).length;
12352
- const notes = failed > 0 ? `${failed} open check${failed === 1 ? "" : "s"}` : `${mission.readinessPercent}% repo checks`;
12353
- lines.push(
12354
- `| ${area.label} | ${mission.providerLabel} | ${mission.readinessPercent}% | ${notes} |`
12355
- );
12356
- }
12357
- }
12358
- lines.push("");
12359
- lines.push("## Agent-code actions");
12360
- if (topGaps.length === 0) {
12361
- lines.push("_No model gaps returned. Review mission map checks before changing code._");
12362
- } else {
12363
- topGaps.forEach((gap, index) => {
12364
- lines.push(
12365
- `${index + 1}. **${gap.title}** (\`${gap.id}\`, ${gap.severity}, map: \`${gap.primaryMapCategory}\`)`
12366
- );
12367
- lines.push(` - ${gap.detail}`);
12368
- lines.push(` - Command: \`viberaven prompt --gap ${gap.id}\``);
12369
- if (gap.copyPrompt) {
12370
- lines.push(` - Prompt: ${gap.copyPrompt}`);
12371
- }
12372
- });
12373
- }
12374
- const manualChecks = (artifact.missionGraph.areas ?? []).flatMap(
12375
- (area) => area.providerMissions.flatMap(
12376
- (mission) => mission.checks.filter(
12377
- (check) => check.evidenceClass === "manual-dashboard" || check.evidenceClass === "mcp-verifier" || check.evidenceSource === "provider" || check.evidenceSource === "mcp" || check.status === "needs-connection" || check.status === "unknown"
12378
- ).map((check) => ({ area: area.label, provider: mission.providerLabel, check }))
12379
- )
12380
- );
12381
- lines.push("");
12382
- lines.push("## Human-provider actions");
12383
- if (manualChecks.length === 0) {
12384
- lines.push("_No manual provider actions were identified in this scan._");
12385
- } else {
12386
- manualChecks.slice(0, 8).forEach((item4, index) => {
12387
- lines.push(`${index + 1}. **${item4.check.label}** (${item4.area} / ${item4.provider})`);
12388
- lines.push(
12389
- ` - ${item4.check.promptHint || "Ask the user to confirm this in the provider dashboard or through read-only MCP."}`
12390
- );
12391
- });
12392
- }
12393
- lines.push("");
12394
- lines.push("Do not claim human-provider actions as repo-code fixes.");
12395
- lines.push("");
12396
- lines.push("## Agent workflow");
12397
- lines.push("1. Read `.viberaven/agent-tasklist.md` first for the production gate.");
12398
- lines.push("2. Read `.viberaven/launch-playbook.md` for the full checklist.");
12399
- lines.push("3. Run `viberaven next --json` - one action at a time.");
12400
- lines.push("4. Repo fix: `viberaven prompt --gap <id>` then implement.");
12401
- lines.push("5. Provider: `viberaven guide <provider> --step N` and `viberaven open <provider>`.");
12402
- lines.push(`6. Run \`${PUBLIC_VERIFY_COMMAND}\` to rescan and refresh \`.viberaven/agent-tasklist.md\`.`);
12403
- lines.push("");
12404
- return `${lines.join("\n")}
12405
- `;
12406
- }
12407
- var SEVERITY_ORDER2;
12408
- var init_agentSummary = __esm({
12409
- "src/report/agentSummary.ts"() {
12410
- "use strict";
12411
- init_commands();
12412
- init_resolveNextAction();
12413
- SEVERITY_ORDER2 = {
12414
- critical: 0,
12415
- warning: 1,
12416
- info: 2
12417
- };
12418
- }
12419
- });
12420
-
12421
- // src/heal/rlsMigrationTarget.ts
12422
- function rlsEnableStatements(tables) {
12423
- return [...new Set(tables)].map((table2) => `alter table ${table2} enable row level security;`);
12424
- }
12425
- function migrationStamp(now) {
12426
- return now.toISOString().replace(/\D/g, "").slice(0, 14);
12427
- }
12428
- function rlsMigrationTarget(cwd, evidence, now = /* @__PURE__ */ new Date()) {
12429
- if (evidence.length === 0) return { ok: false, reason: "No table without row level security was found in the repo SQL." };
12430
- const statementList = rlsEnableStatements(evidence.map((entry) => entry.table)).join(" ");
12431
- const turnedOff = evidence.filter((entry) => entry.turnedOff);
12432
- if (turnedOff.length > 0) {
12433
- const where = turnedOff.map((entry) => `${entry.table} (${entry.file.replace(/\\/g, "/")})`).join(", ");
12434
- return {
12435
- ok: false,
12436
- reason: `The latest migration that touches ${where} turns row level security off on purpose, so VibeRaven will not turn it back on automatically. If the user wants it on, add these statements by hand in a new migration, with the policies the app needs: ${statementList}`
12437
- };
12438
- }
12439
- const source = evidence[0].file.replace(/\\/g, "/");
12440
- const dir = import_node_path11.posix.dirname(source);
12441
- if (import_node_path11.posix.basename(dir) !== "migrations") {
12442
- return {
12443
- ok: false,
12444
- reason: `The tables come from ${source}, which is not in a migrations folder, so VibeRaven will not guess where a migration goes. Add these statements by hand where this project keeps its schema: ${statementList}`
12445
- };
12446
- }
12447
- let existing;
12448
- try {
12449
- existing = (0, import_node_fs6.readdirSync)((0, import_node_path11.join)(cwd, dir)).filter((file) => file.endsWith(".sql")).sort();
12450
- } catch {
12451
- return { ok: false, reason: `VibeRaven could not read ${dir}. Add these statements by hand in a new migration: ${statementList}` };
12452
- }
12453
- const otherNaming = existing.find((file) => !TIMESTAMPED_MIGRATION.test(file));
12454
- if (otherNaming) {
12455
- return {
12456
- ok: false,
12457
- reason: `The migrations in ${dir} are not named <14-digit timestamp>_name.sql (for example ${otherNaming}), so a timestamped file from VibeRaven could run in the wrong order next to them. Add these statements by hand in a new migration that follows this project's naming: ${statementList}`
12458
- };
12459
- }
12460
- const name = `${migrationStamp(now)}_viberaven_enable_rls.sql`;
12461
- const last = existing[existing.length - 1];
12462
- if (last && name <= last) {
12463
- return {
12464
- ok: false,
12465
- reason: `A new migration named ${name} would not run after ${last} in ${dir}, so VibeRaven will not write it. Add these statements by hand in a migration that runs last: ${statementList}`
12466
- };
12467
- }
12468
- return { ok: true, file: `${dir}/${name}` };
12469
- }
12470
- var import_node_fs6, import_node_path11, RLS_TURNED_OFF_PHRASE, RLS_ASK_FIRST, RLS_NO_POLICIES_WARNING, TIMESTAMPED_MIGRATION;
12471
- var init_rlsMigrationTarget = __esm({
12472
- "src/heal/rlsMigrationTarget.ts"() {
12473
- "use strict";
12474
- import_node_fs6 = require("node:fs");
12475
- import_node_path11 = require("node:path");
12476
- RLS_TURNED_OFF_PHRASE = "has row level security turned off at";
12477
- RLS_ASK_FIRST = "Ask the user first: enabling RLS without policies makes browser reads return no rows until policies exist.";
12478
- RLS_NO_POLICIES_WARNING = "This migration enables row level security with no policies. Until you add policies, the anon and authenticated roles get no rows from these tables through the Data API, so browser reads return nothing and browser writes fail. `check` then reports the tables only as info (rls_no_policies), not as a blocker. Add the policies the app needs before you apply the migration to the database.";
12479
- TIMESTAMPED_MIGRATION = /^\d{14}_.+\.sql$/;
12480
- }
12481
- });
12482
-
12483
12430
  // src/buildTaskList.ts
12484
12431
  function rlsMigrationFile(gap) {
12485
12432
  if (gap.id !== "rls_disabled" || typeof gap.detail !== "string") return void 0;
@@ -12498,7 +12445,12 @@ function hasRecipe(gapId) {
12498
12445
  const lower = gapId.toLowerCase();
12499
12446
  return lower.includes("empty") && lower.includes("catch");
12500
12447
  }
12501
- function gapToPlaybookProvider(gap) {
12448
+ function gapToPlaybookProvider(gap, inRepo) {
12449
+ const provider2 = playbookByCategoryOrId(gap);
12450
+ if (!provider2 || !inRepo) return provider2;
12451
+ return inRepo.has(PLAYBOOK_TOKEN[provider2] ?? provider2) ? provider2 : void 0;
12452
+ }
12453
+ function playbookByCategoryOrId(gap) {
12502
12454
  const cat = gap.primaryMapCategory.toLowerCase();
12503
12455
  if (cat === "deployment") return "vercel";
12504
12456
  if (cat === "payments") return "stripe";
@@ -12511,9 +12463,9 @@ function gapToPlaybookProvider(gap) {
12511
12463
  if (id.includes("auth")) return "auth-supabase";
12512
12464
  return void 0;
12513
12465
  }
12514
- function hasPlaybook(gap) {
12466
+ function hasPlaybook(gap, inRepo) {
12515
12467
  try {
12516
- const provider2 = gapToPlaybookProvider(gap);
12468
+ const provider2 = gapToPlaybookProvider(gap, inRepo);
12517
12469
  if (!provider2) return false;
12518
12470
  return PLAYBOOK_PROVIDERS.includes(provider2);
12519
12471
  } catch {
@@ -12543,13 +12495,20 @@ function buildProviderAction(gap, provider2) {
12543
12495
  }
12544
12496
  function buildTaskList(artifact) {
12545
12497
  const sorted = sortGapsByPriority(artifact.gaps);
12498
+ const providers = providersInRepo(artifact);
12499
+ const inRepo = providers ? new Set(providers.map((entry) => entry.provider.toLowerCase())) : void 0;
12546
12500
  return sorted.map((gap, index) => {
12547
12501
  const id = `TASK-${String(index + 1).padStart(3, "0")}`;
12548
12502
  let fixType;
12549
12503
  const migrationFile = rlsMigrationFile(gap);
12550
- if (hasRecipe(gap.id) || migrationFile) {
12504
+ const location = locationsForGap(gap)[0];
12505
+ const repoFixUserSteps = REPO_FIX_GAPS[gap.id];
12506
+ const informational = INFORMATIONAL_GAPS[gap.id];
12507
+ if (hasRecipe(gap.id) || migrationFile || repoFixUserSteps) {
12551
12508
  fixType = "repo-code";
12552
- } else if (hasPlaybook(gap)) {
12509
+ } else if (informational) {
12510
+ fixType = "manual-verify";
12511
+ } else if (hasPlaybook(gap, inRepo)) {
12553
12512
  fixType = "provider-action";
12554
12513
  } else {
12555
12514
  fixType = "manual-verify";
@@ -12566,6 +12525,8 @@ function buildTaskList(artifact) {
12566
12525
  if (fixType === "repo-code" && !hasRecipe(gap.id) && migrationFile) {
12567
12526
  base.requiresUserAction = true;
12568
12527
  base.file = migrationFile;
12528
+ base.userSteps = [APPLY_MIGRATION];
12529
+ if (location?.file === migrationFile && location.line !== void 0) base.line = location.line;
12569
12530
  const target = rlsMigrationTarget(artifact.workspacePath, rlsEvidence2(gap.detail));
12570
12531
  if (target.ok) {
12571
12532
  base.mcpTool = "viberaven_heal_apply";
@@ -12577,6 +12538,28 @@ function buildTaskList(artifact) {
12577
12538
  if (gap.copyPrompt) {
12578
12539
  base.exactFix = gap.copyPrompt.trim();
12579
12540
  }
12541
+ } else if (fixType === "repo-code" && !hasRecipe(gap.id) && repoFixUserSteps) {
12542
+ base.requiresUserAction = false;
12543
+ if (location) {
12544
+ base.file = location.file;
12545
+ if (location.line !== void 0) base.line = location.line;
12546
+ } else {
12547
+ base.action = "Edit the files named in the gap detail (`npx -y viberaven prompt --gap " + gap.id + "`).";
12548
+ }
12549
+ if (gap.copyPrompt) {
12550
+ base.exactFix = gap.copyPrompt.trim();
12551
+ }
12552
+ base.userSteps = [...repoFixUserSteps];
12553
+ } else if (fixType === "manual-verify" && informational) {
12554
+ base.requiresUserAction = false;
12555
+ base.action = informational;
12556
+ if (location) {
12557
+ base.file = location.file;
12558
+ if (location.line !== void 0) base.line = location.line;
12559
+ }
12560
+ if (gap.copyPrompt) {
12561
+ base.exactFix = gap.copyPrompt.trim();
12562
+ }
12580
12563
  } else if (fixType === "repo-code") {
12581
12564
  base.mcpTool = "viberaven_heal_apply";
12582
12565
  base.mcpArgs = { gap: gap.id, yes: true };
@@ -12586,7 +12569,7 @@ function buildTaskList(artifact) {
12586
12569
  }
12587
12570
  } else if (fixType === "provider-action") {
12588
12571
  try {
12589
- const provider2 = gapToPlaybookProvider(gap);
12572
+ const provider2 = gapToPlaybookProvider(gap, inRepo);
12590
12573
  if (provider2) {
12591
12574
  const pa = buildProviderAction(gap, provider2);
12592
12575
  if (pa) {
@@ -12604,7 +12587,7 @@ function buildTaskList(artifact) {
12604
12587
  }
12605
12588
  function buildTaskListMarkdown(tasks) {
12606
12589
  if (tasks.length === 0) {
12607
- return "# VibeRaven Agent Tasklist\n\n_No gaps found \u2014 production core looks solid._\n";
12590
+ return "# VibeRaven Agent Tasklist\n\n_No gaps found in this scan._\n";
12608
12591
  }
12609
12592
  const lines = ["# VibeRaven Agent Tasklist", ""];
12610
12593
  for (const task of tasks) {
@@ -12612,7 +12595,7 @@ function buildTaskListMarkdown(tasks) {
12612
12595
  lines.push(`## ${task.id} \xB7 ${task.gapId} \xB7 ${severityLabel}`, "");
12613
12596
  lines.push(`**Fix type:** ${task.fixType} `);
12614
12597
  if (task.file) {
12615
- lines.push(`**File:** \`${task.file}\` `);
12598
+ lines.push(`**File:** \`${task.file}${task.line !== void 0 ? `:${task.line}` : ""}\` `);
12616
12599
  }
12617
12600
  if (task.action) {
12618
12601
  lines.push(`**Action:** ${task.action} `);
@@ -12620,62 +12603,387 @@ function buildTaskListMarkdown(tasks) {
12620
12603
  if (task.exactFix) {
12621
12604
  lines.push(`**Exact fix:** ${task.exactFix} `);
12622
12605
  } else {
12623
- lines.push(`**Exact fix:** No automated recipe \u2014 see scanner hint. `);
12606
+ lines.push(`**Exact fix:** No automated recipe; see the scanner hint in the gap detail. `);
12624
12607
  }
12625
12608
  lines.push(`**Verify:** \`${task.verifyCommand}\` `);
12626
12609
  if (task.mcpTool && task.mcpArgs) {
12627
12610
  lines.push(
12628
- `**MCP:** \`${task.mcpTool} ${JSON.stringify(task.mcpArgs)}\` `
12611
+ `**MCP:** \`${task.mcpTool} ${JSON.stringify(task.mcpArgs)}\` `
12612
+ );
12613
+ }
12614
+ lines.push(`**Requires user action:** ${task.requiresUserAction}`);
12615
+ if (task.userSteps && task.userSteps.length > 0) {
12616
+ lines.push("");
12617
+ lines.push("**Outside the repo (needs the user):**");
12618
+ for (const step of task.userSteps) lines.push(`- ${step}`);
12619
+ }
12620
+ if (task.providerAction) {
12621
+ const pa = task.providerAction;
12622
+ lines.push("");
12623
+ lines.push("**Provider action:**");
12624
+ lines.push(`- Provider: ${pa.provider}`);
12625
+ lines.push(`- Dashboard: ${pa.dashboardUrl}`);
12626
+ lines.push(`- Step: ${pa.exactStep}`);
12627
+ if (pa.envKeyName) lines.push(`- Env key: \`${pa.envKeyName}\``);
12628
+ if (pa.doneSignal) lines.push(`- Done when: ${pa.doneSignal}`);
12629
+ if (pa.mcpAlternative) lines.push(`- MCP alternative: \`${pa.mcpAlternative}\``);
12630
+ }
12631
+ lines.push("");
12632
+ }
12633
+ return `${lines.join("\n")}
12634
+ `;
12635
+ }
12636
+ var PLAYBOOK_TOKEN;
12637
+ var init_buildTaskList = __esm({
12638
+ "src/buildTaskList.ts"() {
12639
+ "use strict";
12640
+ init_commands();
12641
+ init_rlsMigrationTarget();
12642
+ init_gapLocations();
12643
+ init_repoFixes();
12644
+ init_detectedProviders();
12645
+ init_loadPlaybook();
12646
+ init_types2();
12647
+ init_menu();
12648
+ PLAYBOOK_TOKEN = { vercel: "vercel", stripe: "stripe", supabase: "supabase", "auth-supabase": "supabase" };
12649
+ }
12650
+ });
12651
+
12652
+ // src/contracts/gapEvidence.ts
12653
+ function summarizeGap(gap) {
12654
+ return `${gap.title}. See the tasklist and original scan for redacted detail.`;
12655
+ }
12656
+ function evidenceFor2(gap) {
12657
+ const kind = String(gap.primaryMapCategory);
12658
+ const located = locationsForGap(gap).map((location) => ({ kind, ...location, redacted: true }));
12659
+ return located.length > 0 ? located : [{ kind, redacted: true }];
12660
+ }
12661
+ function userStepsFor(gap) {
12662
+ if (REPO_FIX_GAPS[gap.id]) return { userSteps: [...REPO_FIX_GAPS[gap.id]] };
12663
+ if (rlsMigrationFile(gap)) return { userSteps: [APPLY_MIGRATION] };
12664
+ return {};
12665
+ }
12666
+ function generateGapEvidenceFiles(artifact) {
12667
+ return artifact.gaps.slice(0, 50).map((gap) => ({
12668
+ path: `.viberaven/gaps/${gap.id}.json`,
12669
+ content: {
12670
+ $schema: "https://viberaven.dev/schemas/gap.schema.json",
12671
+ schemaVersion: "v1",
12672
+ id: gap.id,
12673
+ severity: gap.severity,
12674
+ capability: classifyGapCapability(gap),
12675
+ title: gap.title,
12676
+ summary: summarizeGap(gap),
12677
+ evidence: evidenceFor2(gap),
12678
+ commands: {
12679
+ prompt: promptGapCommand(gap.id),
12680
+ healPlan: healPlanGapCommand(gap.id),
12681
+ verify: PUBLIC_VERIFY_COMMAND
12682
+ },
12683
+ ...userStepsFor(gap),
12684
+ 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." }
12685
+ }
12686
+ }));
12687
+ }
12688
+ var init_gapEvidence = __esm({
12689
+ "src/contracts/gapEvidence.ts"() {
12690
+ "use strict";
12691
+ init_capabilities();
12692
+ init_gapLocations();
12693
+ init_repoFixes();
12694
+ init_buildTaskList();
12695
+ init_commands();
12696
+ }
12697
+ });
12698
+
12699
+ // src/playbooks/checkMap.ts
12700
+ function mapCheckToPlaybook(check) {
12701
+ if (CHECK_TO_PLAYBOOK[check.id]) {
12702
+ return CHECK_TO_PLAYBOOK[check.id];
12703
+ }
12704
+ const providerKey = String(check.providerKey ?? "").toLowerCase();
12705
+ if (providerKey.includes("vercel")) {
12706
+ return "vercel";
12707
+ }
12708
+ if (providerKey.includes("stripe")) {
12709
+ return "stripe";
12710
+ }
12711
+ if (providerKey.includes("supabase")) {
12712
+ return check.area === "auth" ? "auth-supabase" : "supabase";
12713
+ }
12714
+ return void 0;
12715
+ }
12716
+ var CHECK_TO_PLAYBOOK;
12717
+ var init_checkMap = __esm({
12718
+ "src/playbooks/checkMap.ts"() {
12719
+ "use strict";
12720
+ CHECK_TO_PLAYBOOK = {
12721
+ "vercel-deployment": "vercel",
12722
+ "vercel-project": "vercel",
12723
+ "supabase-project": "supabase",
12724
+ "supabase-env": "supabase",
12725
+ "stripe-webhook": "stripe",
12726
+ "stripe-product": "stripe",
12727
+ "stripe-keys": "stripe"
12728
+ };
12729
+ }
12730
+ });
12731
+
12732
+ // src/report/missionSelection.ts
12733
+ function normalizeProviderToken2(value) {
12734
+ return value.toLowerCase().replace(/&/g, "and").replace(/[^a-z0-9]+/g, "");
12735
+ }
12736
+ function missionMatchesProvider(mission, provider2) {
12737
+ const current = normalizeProviderToken2(provider2);
12738
+ return normalizeProviderToken2(mission.provider || mission.providerLabel || mission.key) === current || normalizeProviderToken2(mission.providerLabel || mission.provider || mission.key) === current;
12739
+ }
12740
+ function missionEvidenceScore(mission) {
12741
+ const repoVerified = mission.checks.filter((check) => check.evidenceClass === "repo-verified" || check.status === "passed").length;
12742
+ const missing = mission.checks.filter(
12743
+ (check) => check.evidenceClass === "missing-repo-fix" || check.status === "missing" || check.status === "failed"
12744
+ ).length;
12745
+ return repoVerified * 100 + (mission.readinessPercent ?? 0) - missing;
12746
+ }
12747
+ function openChecksForMission(mission) {
12748
+ if (!mission) {
12749
+ return 0;
12750
+ }
12751
+ return mission.checks.filter(
12752
+ (check) => check.status === "missing" || check.status === "failed" || check.status === "needs-connection"
12753
+ ).length;
12754
+ }
12755
+ var init_missionSelection = __esm({
12756
+ "src/report/missionSelection.ts"() {
12757
+ "use strict";
12758
+ }
12759
+ });
12760
+
12761
+ // src/report/inRepoMissions.ts
12762
+ function detectedProviderTokens(artifact) {
12763
+ const providers = providersInRepo(artifact);
12764
+ if (!providers) return void 0;
12765
+ return new Set(providers.map((entry) => normalizeProviderToken2(entry.provider)));
12766
+ }
12767
+ function missionInRepo(mission, detected) {
12768
+ const provider2 = String(mission.provider || mission.key || "");
12769
+ if (detected.has(normalizeProviderToken2(provider2))) return true;
12770
+ if (PRACTICES.has(provider2)) return true;
12771
+ const identity = mission.checks.find((check) => IDENTITY_CHECK.test(check.id));
12772
+ return identity?.status === "passed";
12773
+ }
12774
+ function inRepoMissions(artifact, area) {
12775
+ const missions = area.providerMissions ?? [];
12776
+ const detected = detectedProviderTokens(artifact);
12777
+ if (!detected) return missions;
12778
+ const selected = artifact.selectedProviders?.[area.key] ?? "";
12779
+ return missions.filter((mission) => missionInRepo(mission, detected) || selected && missionMatchesProvider(mission, selected));
12780
+ }
12781
+ function preferredInRepoMission(artifact, area) {
12782
+ const missions = inRepoMissions(artifact, area);
12783
+ if (missions.length === 0) return void 0;
12784
+ const selected = artifact.selectedProviders?.[area.key] ?? "";
12785
+ const picked = selected ? missions.find((mission) => missionMatchesProvider(mission, selected)) : void 0;
12786
+ return picked ?? [...missions].sort((a, b3) => missionEvidenceScore(b3) - missionEvidenceScore(a))[0];
12787
+ }
12788
+ var IDENTITY_CHECK, PRACTICES, NONE_FOUND;
12789
+ var init_inRepoMissions = __esm({
12790
+ "src/report/inRepoMissions.ts"() {
12791
+ "use strict";
12792
+ init_detectedProviders();
12793
+ init_missionSelection();
12794
+ 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)$/;
12795
+ PRACTICES = /* @__PURE__ */ new Set(["route-map", "product-spec", "secrets-hygiene"]);
12796
+ NONE_FOUND = "none found";
12797
+ }
12798
+ });
12799
+
12800
+ // src/playbooks/manualChecks.ts
12801
+ function isManualProviderCheck(check) {
12802
+ return check.evidenceClass === "manual-dashboard" || check.evidenceClass === "mcp-verifier" || check.evidenceSource === "provider" || check.evidenceSource === "mcp" || check.status === "needs-connection" || check.status === "unknown";
12803
+ }
12804
+ function collectManualChecks(artifact) {
12805
+ const refs = [];
12806
+ for (const area of artifact.missionGraph.areas ?? []) {
12807
+ for (const mission of inRepoMissions(artifact, area)) {
12808
+ for (const check of mission.checks) {
12809
+ if (!isManualProviderCheck(check)) {
12810
+ continue;
12811
+ }
12812
+ refs.push({
12813
+ areaLabel: area.label,
12814
+ providerLabel: mission.providerLabel,
12815
+ check,
12816
+ mapCategory: area.key
12817
+ });
12818
+ }
12819
+ }
12820
+ }
12821
+ return refs;
12822
+ }
12823
+ var init_manualChecks = __esm({
12824
+ "src/playbooks/manualChecks.ts"() {
12825
+ "use strict";
12826
+ init_inRepoMissions();
12827
+ }
12828
+ });
12829
+
12830
+ // src/resolveNextAction.ts
12831
+ function resolveNextAction(artifact) {
12832
+ const repoGap = sortGapsByPriority(artifact.gaps)[0];
12833
+ if (repoGap) {
12834
+ return {
12835
+ type: "repo-fix",
12836
+ title: repoGap.title,
12837
+ detail: repoGap.detail,
12838
+ command: `viberaven prompt --gap ${repoGap.id}`
12839
+ };
12840
+ }
12841
+ const manual = collectManualChecks(artifact)[0];
12842
+ if (manual) {
12843
+ const provider2 = mapCheckToPlaybook(manual.check);
12844
+ let openUrl;
12845
+ try {
12846
+ openUrl = provider2 ? loadPlaybookSync(provider2).steps[0]?.openUrl : void 0;
12847
+ } catch {
12848
+ openUrl = void 0;
12849
+ }
12850
+ return {
12851
+ type: "provider-guide",
12852
+ title: manual.check.label,
12853
+ detail: manual.check.promptHint || `Configure ${manual.providerLabel} in the provider dashboard (${manual.areaLabel}).`,
12854
+ ...provider2 ? { provider: provider2, playbookStep: 1, command: `viberaven guide ${provider2} --step 1` } : {},
12855
+ openUrl
12856
+ };
12857
+ }
12858
+ return {
12859
+ type: "done",
12860
+ title: "No blockers found",
12861
+ detail: "Re-scan after changes to confirm production readiness."
12862
+ };
12863
+ }
12864
+ var init_resolveNextAction = __esm({
12865
+ "src/resolveNextAction.ts"() {
12866
+ "use strict";
12867
+ init_checkMap();
12868
+ init_loadPlaybook();
12869
+ init_manualChecks();
12870
+ init_menu();
12871
+ }
12872
+ });
12873
+
12874
+ // src/report/agentSummary.ts
12875
+ function sortGaps(gaps) {
12876
+ return [...gaps].sort((a, b3) => {
12877
+ const sev = SEVERITY_ORDER2[a.severity] - SEVERITY_ORDER2[b3.severity];
12878
+ if (sev !== 0) {
12879
+ return sev;
12880
+ }
12881
+ return a.title.localeCompare(b3.title);
12882
+ });
12883
+ }
12884
+ function generateAgentSummary(artifact) {
12885
+ const lines = [];
12886
+ const topGaps = sortGaps(artifact.gaps).slice(0, 8);
12887
+ lines.push("# VibeRaven agent summary", "");
12888
+ lines.push(`Scanned: \`${artifact.workspacePath}\``);
12889
+ lines.push(`At: ${artifact.scannedAt}`);
12890
+ lines.push(
12891
+ `Production core: **${artifact.productionCorePercent}%** \xB7 Model score: **${artifact.score}** (${artifact.scoreLabel})`
12892
+ );
12893
+ lines.push("");
12894
+ const next = resolveNextAction(artifact);
12895
+ lines.push("## Next action", "");
12896
+ lines.push(`**${next.title}**: ${next.detail}`);
12897
+ if (next.command) {
12898
+ lines.push(`Command: \`${next.command}\``);
12899
+ }
12900
+ lines.push("");
12901
+ lines.push("## Stack", "");
12902
+ lines.push("The mission map below lists the providers detected in this repo. For a suggestion, run `npx -y viberaven stack recommend`.");
12903
+ lines.push("");
12904
+ lines.push("## Summary");
12905
+ lines.push(artifact.summary || "_No summary returned._");
12906
+ lines.push("");
12907
+ lines.push("## Mission map (repo wiring)");
12908
+ lines.push("");
12909
+ lines.push("| Area | Provider | Readiness | Notes |");
12910
+ lines.push("|------|----------|-----------|-------|");
12911
+ for (const area of artifact.missionGraph.areas ?? []) {
12912
+ const missions = inRepoMissions(artifact, area);
12913
+ if (missions.length === 0 && (area.providerMissions ?? []).length > 0) {
12914
+ lines.push(`| ${area.label} | ${NONE_FOUND} | - | - |`);
12915
+ }
12916
+ for (const mission of missions) {
12917
+ const failed = mission.checks.filter(
12918
+ (c) => c.status === "missing" || c.status === "failed" || c.status === "needs-connection"
12919
+ ).length;
12920
+ const notes = failed > 0 ? `${failed} open check${failed === 1 ? "" : "s"}` : `${mission.readinessPercent}% repo checks`;
12921
+ lines.push(
12922
+ `| ${area.label} | ${mission.providerLabel} | ${mission.readinessPercent}% | ${notes} |`
12923
+ );
12924
+ }
12925
+ }
12926
+ lines.push("");
12927
+ lines.push("## Agent-code actions");
12928
+ if (topGaps.length === 0) {
12929
+ lines.push("_No model gaps returned. Review mission map checks before changing code._");
12930
+ } else {
12931
+ topGaps.forEach((gap, index) => {
12932
+ lines.push(
12933
+ `${index + 1}. **${gap.title}** (\`${gap.id}\`, ${gap.severity}, map: \`${gap.primaryMapCategory}\`)`
12629
12934
  );
12630
- }
12631
- lines.push(`**Requires user action:** ${task.requiresUserAction}`);
12632
- if (task.providerAction) {
12633
- const pa = task.providerAction;
12634
- lines.push("");
12635
- lines.push("**Provider action:**");
12636
- lines.push(`- Provider: ${pa.provider}`);
12637
- lines.push(`- Dashboard: ${pa.dashboardUrl}`);
12638
- lines.push(`- Step: ${pa.exactStep}`);
12639
- if (pa.envKeyName) lines.push(`- Env key: \`${pa.envKeyName}\``);
12640
- if (pa.doneSignal) lines.push(`- Done when: ${pa.doneSignal}`);
12641
- if (pa.mcpAlternative) lines.push(`- MCP alternative: \`${pa.mcpAlternative}\``);
12642
- }
12643
- lines.push("");
12935
+ lines.push(` - ${gap.detail}`);
12936
+ lines.push(` - Command: \`viberaven prompt --gap ${gap.id}\``);
12937
+ if (gap.copyPrompt) {
12938
+ lines.push(` - Prompt: ${gap.copyPrompt}`);
12939
+ }
12940
+ });
12941
+ }
12942
+ const manualChecks = (artifact.missionGraph.areas ?? []).flatMap(
12943
+ (area) => inRepoMissions(artifact, area).flatMap(
12944
+ (mission) => mission.checks.filter(
12945
+ (check) => check.evidenceClass === "manual-dashboard" || check.evidenceClass === "mcp-verifier" || check.evidenceSource === "provider" || check.evidenceSource === "mcp" || check.status === "needs-connection" || check.status === "unknown"
12946
+ ).map((check) => ({ area: area.label, provider: mission.providerLabel, check }))
12947
+ )
12948
+ );
12949
+ lines.push("");
12950
+ lines.push("## Human-provider actions");
12951
+ if (manualChecks.length === 0) {
12952
+ lines.push("_No manual provider actions were identified in this scan._");
12953
+ } else {
12954
+ manualChecks.slice(0, 8).forEach((item4, index) => {
12955
+ lines.push(`${index + 1}. **${item4.check.label}** (${item4.area} / ${item4.provider})`);
12956
+ lines.push(
12957
+ ` - ${item4.check.promptHint || "Ask the user to confirm this in the provider dashboard or through read-only MCP."}`
12958
+ );
12959
+ });
12644
12960
  }
12961
+ lines.push("");
12962
+ lines.push("Do not claim human-provider actions as repo-code fixes.");
12963
+ lines.push("");
12964
+ lines.push("## Agent workflow");
12965
+ lines.push("1. Read `.viberaven/agent-tasklist.md` first for the production gate.");
12966
+ lines.push("2. Read `.viberaven/launch-playbook.md` for the full checklist.");
12967
+ lines.push("3. Run `viberaven next --json` - one action at a time.");
12968
+ lines.push("4. Repo fix: `viberaven prompt --gap <id>` then implement.");
12969
+ lines.push("5. Provider: `viberaven guide <provider> --step N` and `viberaven open <provider>`.");
12970
+ lines.push(`6. Run \`${PUBLIC_VERIFY_COMMAND}\` to rescan and refresh \`.viberaven/agent-tasklist.md\`.`);
12971
+ lines.push("");
12645
12972
  return `${lines.join("\n")}
12646
12973
  `;
12647
12974
  }
12648
- var KNOWN_RECIPE_GAP_IDS;
12649
- var init_buildTaskList = __esm({
12650
- "src/buildTaskList.ts"() {
12975
+ var SEVERITY_ORDER2;
12976
+ var init_agentSummary = __esm({
12977
+ "src/report/agentSummary.ts"() {
12651
12978
  "use strict";
12652
12979
  init_commands();
12653
- init_rlsMigrationTarget();
12654
- init_loadPlaybook();
12655
- init_types2();
12656
- init_menu();
12657
- KNOWN_RECIPE_GAP_IDS = /* @__PURE__ */ new Set([
12658
- // emptyCatch (original recipe)
12659
- "empty-catch",
12660
- "empty_catch",
12661
- "emptyCatch",
12662
- // W3 env-add recipes
12663
- "auth_secret_missing",
12664
- "node_env_not_set",
12665
- "database_url_missing",
12666
- // W3 file-create recipes
12667
- "missing_error_boundary",
12668
- "missing_health_route",
12669
- "missing_loading_state",
12670
- "missing_404_page",
12671
- // W3 file-patch recipes
12672
- "missing_csp_header",
12673
- "missing_rate_limit",
12674
- "eslint_restricted_imports"
12675
- // NOTE: 'rls_disabled' is intentionally NOT here. When the finding comes from repo SQL it is a repo action whose
12676
- // recipe writes a new migration (see rlsMigrationFile and heal/recipes/supabaseRls.ts); without SQL evidence it
12677
- // stays a provider action and heal refuses.
12678
- ]);
12980
+ init_resolveNextAction();
12981
+ init_inRepoMissions();
12982
+ SEVERITY_ORDER2 = {
12983
+ critical: 0,
12984
+ warning: 1,
12985
+ info: 2
12986
+ };
12679
12987
  }
12680
12988
  });
12681
12989
 
@@ -12685,12 +12993,21 @@ function generateLaunchPlaybook(artifact) {
12685
12993
  lines.push("# VibeRaven launch playbook", "");
12686
12994
  lines.push(`Generated from scan at ${artifact.scannedAt}. Work top to bottom.`, "");
12687
12995
  lines.push("## Repo fixes (agent code)", "");
12688
- const repoGaps = sortGapsByPriority(artifact.gaps);
12996
+ const sorted = sortGapsByPriority(artifact.gaps);
12997
+ const repoGaps = sorted.filter((gap) => !INFORMATIONAL_GAPS[gap.id]);
12998
+ const informational = sorted.filter((gap) => INFORMATIONAL_GAPS[gap.id]);
12689
12999
  if (repoGaps.length === 0) {
12690
13000
  lines.push("_No repo-code gaps found._", "");
12691
13001
  } else {
12692
13002
  for (const gap of repoGaps) {
12693
- lines.push(`- [ ] ${gap.title} \u2014 \`viberaven prompt --gap ${gap.id}\``);
13003
+ lines.push(`- [ ] ${gap.title}: \`viberaven prompt --gap ${gap.id}\``);
13004
+ }
13005
+ lines.push("");
13006
+ }
13007
+ if (informational.length > 0) {
13008
+ lines.push("## Informational", "");
13009
+ for (const gap of informational) {
13010
+ lines.push(`- ${gap.title} (\`${gap.id}\`): ${INFORMATIONAL_GAPS[gap.id]}`);
12694
13011
  }
12695
13012
  lines.push("");
12696
13013
  }
@@ -12702,7 +13019,7 @@ function generateLaunchPlaybook(artifact) {
12702
13019
  for (const item4 of manuals) {
12703
13020
  const provider2 = mapCheckToPlaybook(item4.check);
12704
13021
  lines.push(
12705
- `- [ ] ${item4.check.label} (${item4.areaLabel}) \u2014 \`viberaven guide ${provider2} --step 1\``
13022
+ provider2 ? `- [ ] ${item4.check.label} (${item4.areaLabel}): \`viberaven guide ${provider2} --step 1\`` : `- [ ] ${item4.check.label} (${item4.areaLabel})`
12706
13023
  );
12707
13024
  }
12708
13025
  lines.push("");
@@ -12723,6 +13040,7 @@ var init_launchPlaybook = __esm({
12723
13040
  init_manualChecks();
12724
13041
  init_menu();
12725
13042
  init_commands();
13043
+ init_repoFixes();
12726
13044
  }
12727
13045
  });
12728
13046
 
@@ -13051,25 +13369,25 @@ var init_reportHtml = __esm({
13051
13369
  function getBundledReportAssetsDir() {
13052
13370
  const base = __dirname;
13053
13371
  const candidates = [
13054
- (0, import_node_path12.join)(base, "report"),
13055
- (0, import_node_path12.join)(base, "..", "assets", "report"),
13056
- (0, import_node_path12.join)(base, "..", "..", "assets", "report"),
13057
- (0, import_node_path12.join)(process.cwd(), "packages", "cli", "assets", "report"),
13058
- (0, import_node_path12.join)(process.cwd(), "assets", "report")
13372
+ (0, import_node_path13.join)(base, "report"),
13373
+ (0, import_node_path13.join)(base, "..", "assets", "report"),
13374
+ (0, import_node_path13.join)(base, "..", "..", "assets", "report"),
13375
+ (0, import_node_path13.join)(process.cwd(), "packages", "cli", "assets", "report"),
13376
+ (0, import_node_path13.join)(process.cwd(), "assets", "report")
13059
13377
  ];
13060
13378
  for (const dir of candidates) {
13061
- if ((0, import_node_fs7.existsSync)((0, import_node_path12.join)(dir, "station.css"))) {
13379
+ if ((0, import_node_fs8.existsSync)((0, import_node_path13.join)(dir, "station.css"))) {
13062
13380
  return dir;
13063
13381
  }
13064
13382
  }
13065
13383
  throw new Error("Report assets missing. Run `npm run sync-report-assets` and `npm run build` in packages/cli.");
13066
13384
  }
13067
- var import_node_fs7, import_node_path12, REPORT_ASSET_FILES;
13385
+ var import_node_fs8, import_node_path13, REPORT_ASSET_FILES;
13068
13386
  var init_reportAssets = __esm({
13069
13387
  "src/report/reportAssets.ts"() {
13070
13388
  "use strict";
13071
- import_node_fs7 = require("node:fs");
13072
- import_node_path12 = require("node:path");
13389
+ import_node_fs8 = require("node:fs");
13390
+ import_node_path13 = require("node:path");
13073
13391
  REPORT_ASSET_FILES = [
13074
13392
  "station.css",
13075
13393
  "station.js",
@@ -13257,9 +13575,10 @@ function deriveRepoCodeAction(task) {
13257
13575
  gapId: task.gapId,
13258
13576
  readiness: [...askFirst ? [askFirst] : [], task.exactFix ?? "Repo-code fix is available"],
13259
13577
  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}` },
13260
- fileTargets: task.file ? [{ path: displayFile, reason: task.exactFix }] : void 0,
13578
+ fileTargets: task.file ? [{ path: displayFile, ...task.line !== void 0 ? { line: task.line } : {}, reason: task.exactFix }] : void 0,
13261
13579
  verifyCommand: verifyActionCommand("pending"),
13262
- resumeInstruction: `${task.title} is complete. Continue VibeRaven from pending.`,
13580
+ 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(" ")}` : ""}`,
13581
+ ...task.userSteps?.length ? { userSteps: [...task.userSteps] } : {},
13263
13582
  mcpTool: task.mcpTool,
13264
13583
  mcpArgs: task.mcpArgs,
13265
13584
  // Only a gap with an automatic recipe has a heal-apply fallback, and a task that needs the user's yes keeps it
@@ -13268,7 +13587,7 @@ function deriveRepoCodeAction(task) {
13268
13587
  ...task.requiresUserAction ? { requiresUserAction: true } : {}
13269
13588
  };
13270
13589
  }
13271
- function deriveVerifyAction() {
13590
+ function deriveVerifyAction(waiting) {
13272
13591
  return {
13273
13592
  id: "pending",
13274
13593
  actionKey: buildActionKey({
@@ -13280,8 +13599,9 @@ function deriveVerifyAction() {
13280
13599
  revision: 1,
13281
13600
  kind: "verify",
13282
13601
  title: "Run Final Verification",
13283
- status: "blocked",
13284
- readiness: ["Run after provider and repo-code actions are complete"],
13602
+ // Blocked only while repo-code or provider actions come first; with none left it is the next step.
13603
+ status: waiting ? "blocked" : "active",
13604
+ readiness: [waiting ? "Run after provider and repo-code actions are complete" : "No repo-code or provider actions left in this scan"],
13285
13605
  target: { type: "command", label: "Final strict gate", command: `${PUBLIC_COMMAND} --strict` },
13286
13606
  verifyCommand: `${PUBLIC_COMMAND} --strict`,
13287
13607
  resumeInstruction: "Final verification finished. Continue VibeRaven from pending."
@@ -13289,7 +13609,7 @@ function deriveVerifyAction() {
13289
13609
  }
13290
13610
  function deriveCurrentActions(input) {
13291
13611
  const primaryActions = input.tasks.filter((task) => task.fixType === "provider-action" || task.fixType === "repo-code").map((task) => task.fixType === "provider-action" ? deriveProviderAction(task) : deriveRepoCodeAction(task));
13292
- return [...primaryActions, deriveVerifyAction()];
13612
+ return [...primaryActions, deriveVerifyAction(primaryActions.length > 0)];
13293
13613
  }
13294
13614
  var init_derive = __esm({
13295
13615
  "src/actions/derive.ts"() {
@@ -13304,7 +13624,7 @@ function emptyActionRegistry() {
13304
13624
  return { version: 1, nextId: 1, actions: {} };
13305
13625
  }
13306
13626
  function actionRegistryPath(cwd) {
13307
- return (0, import_node_path13.join)(getProjectArtifactsDir(cwd), "action-registry.json");
13627
+ return (0, import_node_path14.join)(getProjectArtifactsDir(cwd), "action-registry.json");
13308
13628
  }
13309
13629
  async function loadActionRegistry(cwd) {
13310
13630
  try {
@@ -13376,7 +13696,8 @@ async function assignActionIds(input) {
13376
13696
  title: action.title,
13377
13697
  gapId: action.gapId,
13378
13698
  kind: action.kind,
13379
- provider: action.provider
13699
+ provider: action.provider,
13700
+ ...action.userSteps?.length ? { userSteps: [...action.userSteps] } : {}
13380
13701
  };
13381
13702
  } else {
13382
13703
  entry.status = "active";
@@ -13385,6 +13706,9 @@ async function assignActionIds(input) {
13385
13706
  entry.gapId = action.gapId;
13386
13707
  entry.kind = action.kind;
13387
13708
  entry.provider = action.provider;
13709
+ if (action.userSteps?.length) entry.userSteps = [...action.userSteps];
13710
+ else delete entry.userSteps;
13711
+ delete entry.followUpShownAt;
13388
13712
  if (entry.fingerprint !== fingerprint) {
13389
13713
  entry.revision += 1;
13390
13714
  entry.fingerprint = fingerprint;
@@ -13408,16 +13732,27 @@ async function assignActionIds(input) {
13408
13732
  await saveActionRegistry(input.cwd, registry);
13409
13733
  return { actions, registry };
13410
13734
  }
13735
+ function pendingUserFollowUps(registry) {
13736
+ 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 ?? []] }));
13737
+ }
13738
+ async function markUserFollowUpsShown(cwd, ids, now = (/* @__PURE__ */ new Date()).toISOString()) {
13739
+ if (ids.length === 0) return;
13740
+ const registry = await loadActionRegistry(cwd);
13741
+ for (const entry of Object.values(registry.actions)) {
13742
+ if (ids.includes(entry.id)) entry.followUpShownAt = now;
13743
+ }
13744
+ await saveActionRegistry(cwd, registry);
13745
+ }
13411
13746
  async function resolveActionById(cwd, id) {
13412
13747
  const registry = await loadActionRegistry(cwd);
13413
13748
  return Object.values(registry.actions).find((entry) => entry.id === id);
13414
13749
  }
13415
- var import_promises9, import_node_path13;
13750
+ var import_promises9, import_node_path14;
13416
13751
  var init_registry2 = __esm({
13417
13752
  "src/actions/registry.ts"() {
13418
13753
  "use strict";
13419
13754
  import_promises9 = require("node:fs/promises");
13420
- import_node_path13 = require("node:path");
13755
+ import_node_path14 = require("node:path");
13421
13756
  init_config();
13422
13757
  init_canonical();
13423
13758
  }
@@ -13428,7 +13763,7 @@ function gateStatus2(artifact) {
13428
13763
  if (!Array.isArray(artifact.gaps)) {
13429
13764
  return "unknown";
13430
13765
  }
13431
- return artifact.gaps.length === 0 ? "clear" : "not_clear";
13766
+ return gateStatusForGaps(artifact.gaps);
13432
13767
  }
13433
13768
  async function writeActionArtifacts(input) {
13434
13769
  const derived = deriveCurrentActions({
@@ -13449,7 +13784,9 @@ async function writeActionArtifacts(input) {
13449
13784
  gateStatus: gateStatus2(input.artifact),
13450
13785
  actions: assigned.actions
13451
13786
  };
13452
- const actionsPath = (0, import_node_path14.join)(getProjectArtifactsDir(input.cwd), "actions.json");
13787
+ const followUps = pendingUserFollowUps(assigned.registry);
13788
+ if (followUps.length > 0) manifest.userFollowUps = followUps;
13789
+ const actionsPath = (0, import_node_path15.join)(getProjectArtifactsDir(input.cwd), "actions.json");
13453
13790
  await (0, import_promises10.writeFile)(actionsPath, `${JSON.stringify(manifest, null, 2)}
13454
13791
  `, "utf8");
13455
13792
  return {
@@ -13458,13 +13795,14 @@ async function writeActionArtifacts(input) {
13458
13795
  manifest
13459
13796
  };
13460
13797
  }
13461
- var import_promises10, import_node_path14;
13798
+ var import_promises10, import_node_path15;
13462
13799
  var init_artifacts = __esm({
13463
13800
  "src/actions/artifacts.ts"() {
13464
13801
  "use strict";
13465
13802
  import_promises10 = require("node:fs/promises");
13466
- import_node_path14 = require("node:path");
13803
+ import_node_path15 = require("node:path");
13467
13804
  init_config();
13805
+ init_gateResult();
13468
13806
  init_derive();
13469
13807
  init_registry2();
13470
13808
  }
@@ -13473,24 +13811,47 @@ var init_artifacts = __esm({
13473
13811
  // src/artifacts.ts
13474
13812
  async function copyReportAssets(reportAssetsDir) {
13475
13813
  const sourceDir = getBundledReportAssetsDir();
13476
- await (0, import_promises11.mkdir)((0, import_node_path15.join)(reportAssetsDir, "assets"), { recursive: true });
13814
+ await (0, import_promises11.mkdir)((0, import_node_path16.join)(reportAssetsDir, "assets"), { recursive: true });
13477
13815
  for (const rel of REPORT_ASSET_FILES) {
13478
- await (0, import_promises11.copyFile)((0, import_node_path15.join)(sourceDir, rel), (0, import_node_path15.join)(reportAssetsDir, rel));
13816
+ await (0, import_promises11.copyFile)((0, import_node_path16.join)(sourceDir, rel), (0, import_node_path16.join)(reportAssetsDir, rel));
13817
+ }
13818
+ }
13819
+ async function isGapEvidenceFile(path) {
13820
+ try {
13821
+ return JSON.parse(await (0, import_promises11.readFile)(path, "utf8")).$schema === GAP_SCHEMA;
13822
+ } catch {
13823
+ return false;
13824
+ }
13825
+ }
13826
+ async function removeStaleGapFiles(gapsDir, current) {
13827
+ let entries;
13828
+ try {
13829
+ entries = await (0, import_promises11.readdir)(gapsDir, { withFileTypes: true });
13830
+ } catch {
13831
+ return;
13832
+ }
13833
+ for (const entry of entries) {
13834
+ if (!entry.isFile() || !entry.name.endsWith(".json") || current.has(entry.name)) continue;
13835
+ const path = (0, import_node_path16.join)(gapsDir, entry.name);
13836
+ try {
13837
+ if (await isGapEvidenceFile(path)) await (0, import_promises11.rm)(path, { force: true });
13838
+ } catch {
13839
+ }
13479
13840
  }
13480
13841
  }
13481
13842
  async function writeScanArtifacts(options) {
13482
13843
  const cwd = options.cwd ?? options.artifact.workspacePath;
13483
13844
  const dir = getProjectArtifactsDir(cwd);
13484
13845
  await (0, import_promises11.mkdir)(dir, { recursive: true });
13485
- const jsonPath = (0, import_node_path15.join)(dir, "last-scan.json");
13486
- const gateResultPath = (0, import_node_path15.join)(dir, "gate-result.json");
13487
- const contextMapPath = (0, import_node_path15.join)(dir, "context-map.json");
13488
- const gapsDir = (0, import_node_path15.join)(dir, "gaps");
13489
- const tasklistPath = (0, import_node_path15.join)(dir, "agent-tasklist.md");
13490
- const summaryPath = (0, import_node_path15.join)(dir, "agent-summary.md");
13491
- const playbookPath = (0, import_node_path15.join)(dir, "launch-playbook.md");
13492
- const reportPath = (0, import_node_path15.join)(dir, "report.html");
13493
- const reportAssetsDir = (0, import_node_path15.join)(dir, "report");
13846
+ const jsonPath = (0, import_node_path16.join)(dir, "last-scan.json");
13847
+ const gateResultPath = (0, import_node_path16.join)(dir, "gate-result.json");
13848
+ const contextMapPath = (0, import_node_path16.join)(dir, "context-map.json");
13849
+ const gapsDir = (0, import_node_path16.join)(dir, "gaps");
13850
+ const tasklistPath = (0, import_node_path16.join)(dir, "agent-tasklist.md");
13851
+ const summaryPath = (0, import_node_path16.join)(dir, "agent-summary.md");
13852
+ const playbookPath = (0, import_node_path16.join)(dir, "launch-playbook.md");
13853
+ const reportPath = (0, import_node_path16.join)(dir, "report.html");
13854
+ const reportAssetsDir = (0, import_node_path16.join)(dir, "report");
13494
13855
  await (0, import_promises11.mkdir)(gapsDir, { recursive: true });
13495
13856
  const safe2 = sanitizeArtifactForDisk(options.artifact);
13496
13857
  const json = `${JSON.stringify(safe2, null, 2)}
@@ -13500,26 +13861,27 @@ async function writeScanArtifacts(options) {
13500
13861
  const html = generateReportHtml(safe2);
13501
13862
  const tasks = buildTaskList(safe2);
13502
13863
  const tasklist = buildTaskListMarkdown(tasks);
13503
- const gateResult = `${JSON.stringify(generateGateResult(safe2), null, 2)}
13864
+ const packageManager = detectPackageManager(options.artifact.workspacePath);
13865
+ const gateResult = `${JSON.stringify(generateGateResult(safe2, { packageManager }), null, 2)}
13504
13866
  `;
13505
- const contextMap = `${JSON.stringify(generateContextMap(safe2), null, 2)}
13867
+ const contextMap = `${JSON.stringify(generateContextMap(safe2, { packageManager }), null, 2)}
13506
13868
  `;
13507
13869
  const gapEvidenceFiles = generateGapEvidenceFiles(safe2);
13508
13870
  await copyReportAssets(reportAssetsDir);
13509
13871
  await (0, import_promises11.writeFile)(gateResultPath, gateResult, "utf-8");
13510
13872
  await (0, import_promises11.writeFile)(contextMapPath, contextMap, "utf-8");
13511
13873
  for (const gap of gapEvidenceFiles) {
13512
- await (0, import_promises11.writeFile)((0, import_node_path15.join)(dir, "gaps", `${gap.content.id}.json`), `${JSON.stringify(gap.content, null, 2)}
13874
+ await (0, import_promises11.writeFile)((0, import_node_path16.join)(dir, "gaps", `${gap.content.id}.json`), `${JSON.stringify(gap.content, null, 2)}
13513
13875
  `, "utf-8");
13514
13876
  }
13515
13877
  await (0, import_promises11.writeFile)(tasklistPath, tasklist, "utf-8");
13516
13878
  let architectureMapPath;
13517
13879
  if (typeof safe2.architectureMap === "string" && safe2.architectureMap.length > 0) {
13518
- architectureMapPath = (0, import_node_path15.join)(dir, "architecture-map.md");
13880
+ architectureMapPath = (0, import_node_path16.join)(dir, "architecture-map.md");
13519
13881
  await (0, import_promises11.writeFile)(architectureMapPath, safe2.architectureMap, "utf-8");
13520
13882
  }
13521
13883
  if (safe2.architectureData) {
13522
- await (0, import_promises11.writeFile)((0, import_node_path15.join)(dir, "architecture-map.json"), `${JSON.stringify(safe2.architectureData, null, 2)}
13884
+ await (0, import_promises11.writeFile)((0, import_node_path16.join)(dir, "architecture-map.json"), `${JSON.stringify(safe2.architectureData, null, 2)}
13523
13885
  `, "utf-8");
13524
13886
  }
13525
13887
  await (0, import_promises11.writeFile)(jsonPath, json, "utf-8");
@@ -13532,6 +13894,7 @@ async function writeScanArtifacts(options) {
13532
13894
  tasks,
13533
13895
  paths: { reportPath, playbookPath }
13534
13896
  });
13897
+ await removeStaleGapFiles(gapsDir, new Set(gapEvidenceFiles.map((gap) => `${gap.content.id}.json`)));
13535
13898
  return {
13536
13899
  dir,
13537
13900
  jsonPath,
@@ -13548,14 +13911,15 @@ async function writeScanArtifacts(options) {
13548
13911
  ...architectureMapPath ? { architectureMapPath } : {}
13549
13912
  };
13550
13913
  }
13551
- var import_promises11, import_node_path15;
13914
+ var import_promises11, import_node_path16, GAP_SCHEMA;
13552
13915
  var init_artifacts2 = __esm({
13553
13916
  "src/artifacts.ts"() {
13554
13917
  "use strict";
13555
13918
  import_promises11 = require("node:fs/promises");
13556
- import_node_path15 = require("node:path");
13919
+ import_node_path16 = require("node:path");
13557
13920
  init_contextMap();
13558
13921
  init_gateResult();
13922
+ init_packageManager();
13559
13923
  init_gapEvidence();
13560
13924
  init_agentSummary();
13561
13925
  init_buildTaskList();
@@ -13565,6 +13929,7 @@ var init_artifacts2 = __esm({
13565
13929
  init_config();
13566
13930
  init_sanitizeArtifact();
13567
13931
  init_artifacts();
13932
+ GAP_SCHEMA = "https://viberaven.dev/schemas/gap.schema.json";
13568
13933
  }
13569
13934
  });
13570
13935
 
@@ -13662,12 +14027,12 @@ function createAppWindowOpenCommands(target, platform = process.platform, env =
13662
14027
  ];
13663
14028
  if (platform === "win32") {
13664
14029
  const candidates = [
13665
- env.LOCALAPPDATA ? (0, import_node_path16.join)(env.LOCALAPPDATA, "Google", "Chrome", "Application", "chrome.exe") : "",
13666
- env.PROGRAMFILES ? (0, import_node_path16.join)(env.PROGRAMFILES, "Google", "Chrome", "Application", "chrome.exe") : "",
13667
- env["PROGRAMFILES(X86)"] ? (0, import_node_path16.join)(env["PROGRAMFILES(X86)"], "Google", "Chrome", "Application", "chrome.exe") : "",
13668
- env.LOCALAPPDATA ? (0, import_node_path16.join)(env.LOCALAPPDATA, "Microsoft", "Edge", "Application", "msedge.exe") : "",
13669
- env.PROGRAMFILES ? (0, import_node_path16.join)(env.PROGRAMFILES, "Microsoft", "Edge", "Application", "msedge.exe") : "",
13670
- env["PROGRAMFILES(X86)"] ? (0, import_node_path16.join)(env["PROGRAMFILES(X86)"], "Microsoft", "Edge", "Application", "msedge.exe") : "",
14030
+ env.LOCALAPPDATA ? (0, import_node_path17.join)(env.LOCALAPPDATA, "Google", "Chrome", "Application", "chrome.exe") : "",
14031
+ env.PROGRAMFILES ? (0, import_node_path17.join)(env.PROGRAMFILES, "Google", "Chrome", "Application", "chrome.exe") : "",
14032
+ env["PROGRAMFILES(X86)"] ? (0, import_node_path17.join)(env["PROGRAMFILES(X86)"], "Google", "Chrome", "Application", "chrome.exe") : "",
14033
+ env.LOCALAPPDATA ? (0, import_node_path17.join)(env.LOCALAPPDATA, "Microsoft", "Edge", "Application", "msedge.exe") : "",
14034
+ env.PROGRAMFILES ? (0, import_node_path17.join)(env.PROGRAMFILES, "Microsoft", "Edge", "Application", "msedge.exe") : "",
14035
+ env["PROGRAMFILES(X86)"] ? (0, import_node_path17.join)(env["PROGRAMFILES(X86)"], "Microsoft", "Edge", "Application", "msedge.exe") : "",
13671
14036
  "chrome",
13672
14037
  "msedge"
13673
14038
  ].filter(Boolean);
@@ -13701,7 +14066,7 @@ async function openWithSystemDefault(target) {
13701
14066
  });
13702
14067
  }
13703
14068
  async function spawnDetached(command) {
13704
- if (/^[A-Za-z]:[\\/]/.test(command.command) && !(0, import_node_fs8.existsSync)(command.command)) {
14069
+ if (/^[A-Za-z]:[\\/]/.test(command.command) && !(0, import_node_fs9.existsSync)(command.command)) {
13705
14070
  throw new Error(`Browser not found: ${command.command}`);
13706
14071
  }
13707
14072
  await new Promise((resolve10, reject) => {
@@ -13744,13 +14109,13 @@ async function openUrlInBrowser(url) {
13744
14109
  }
13745
14110
  await openWithSystemDefault(trimmed);
13746
14111
  }
13747
- var import_node_child_process4, import_node_fs8, import_node_path16;
14112
+ var import_node_child_process4, import_node_fs9, import_node_path17;
13748
14113
  var init_openBrowser = __esm({
13749
14114
  "src/openBrowser.ts"() {
13750
14115
  "use strict";
13751
14116
  import_node_child_process4 = require("node:child_process");
13752
- import_node_fs8 = require("node:fs");
13753
- import_node_path16 = require("node:path");
14117
+ import_node_fs9 = require("node:fs");
14118
+ import_node_path17 = require("node:path");
13754
14119
  }
13755
14120
  });
13756
14121
 
@@ -13879,46 +14244,6 @@ var require_picocolors = __commonJS({
13879
14244
  }
13880
14245
  });
13881
14246
 
13882
- // src/report/missionSelection.ts
13883
- function normalizeProviderToken2(value) {
13884
- return value.toLowerCase().replace(/&/g, "and").replace(/[^a-z0-9]+/g, "");
13885
- }
13886
- function missionMatchesProvider(mission, provider2) {
13887
- const current = normalizeProviderToken2(provider2);
13888
- return normalizeProviderToken2(mission.provider || mission.providerLabel || mission.key) === current || normalizeProviderToken2(mission.providerLabel || mission.provider || mission.key) === current;
13889
- }
13890
- function missionEvidenceScore(mission) {
13891
- const repoVerified = mission.checks.filter((check) => check.evidenceClass === "repo-verified" || check.status === "passed").length;
13892
- const missing = mission.checks.filter(
13893
- (check) => check.evidenceClass === "missing-repo-fix" || check.status === "missing" || check.status === "failed"
13894
- ).length;
13895
- return repoVerified * 100 + (mission.readinessPercent ?? 0) - missing;
13896
- }
13897
- function preferredMissionForArea(area, selectedProvider) {
13898
- const missions = area?.providerMissions ?? [];
13899
- if (missions.length === 0) {
13900
- return void 0;
13901
- }
13902
- const selected = selectedProvider ? missions.find((mission) => missionMatchesProvider(mission, selectedProvider)) : void 0;
13903
- if (selected) {
13904
- return selected;
13905
- }
13906
- return [...missions].sort((a, b3) => missionEvidenceScore(b3) - missionEvidenceScore(a))[0] ?? missions[0];
13907
- }
13908
- function openChecksForMission(mission) {
13909
- if (!mission) {
13910
- return 0;
13911
- }
13912
- return mission.checks.filter(
13913
- (check) => check.status === "missing" || check.status === "failed" || check.status === "needs-connection"
13914
- ).length;
13915
- }
13916
- var init_missionSelection = __esm({
13917
- "src/report/missionSelection.ts"() {
13918
- "use strict";
13919
- }
13920
- });
13921
-
13922
14247
  // src/statusLabels.ts
13923
14248
  function formatAgentStatus(label, message) {
13924
14249
  return `${label}: ${message}`;
@@ -13960,7 +14285,7 @@ function gapTagColor(modelGaps, open2) {
13960
14285
  }
13961
14286
  function manualActionCheckCount(artifact) {
13962
14287
  return (artifact.missionGraph.areas ?? []).reduce((areaTotal, area) => {
13963
- return areaTotal + area.providerMissions.reduce((missionTotal, mission) => {
14288
+ return areaTotal + inRepoMissions(artifact, area).reduce((missionTotal, mission) => {
13964
14289
  return missionTotal + mission.checks.filter(
13965
14290
  (check) => check.evidenceClass === "manual-dashboard" || check.evidenceClass === "mcp-verifier" || check.evidenceSource === "provider" || check.evidenceSource === "mcp" || check.status === "needs-connection" || check.status === "unknown"
13966
14291
  ).length;
@@ -13981,8 +14306,14 @@ function printScanSummary(artifact, paths) {
13981
14306
  console.log(import_picocolors.default.dim("\u2514") + import_picocolors.default.dim("\u2500".repeat(width - 2)) + import_picocolors.default.dim("\u2518"));
13982
14307
  console.log("");
13983
14308
  for (const area of artifact.missionGraph.areas ?? []) {
13984
- const mission = preferredMissionForArea(area, artifact.selectedProviders?.[area.key] ?? "");
14309
+ if ((area.providerMissions ?? []).length === 0) {
14310
+ continue;
14311
+ }
14312
+ const mission = preferredInRepoMission(artifact, area);
13985
14313
  if (!mission) {
14314
+ const modelGaps2 = artifact.gaps.filter((g2) => g2.primaryMapCategory === area.key).length;
14315
+ const tag2 = modelGaps2 > 0 ? import_picocolors.default.red(` GAP ${modelGaps2}`) : "";
14316
+ console.log(` ${import_picocolors.default.dim(area.label.padEnd(18))} ${import_picocolors.default.dim(NONE_FOUND)}${tag2}`);
13986
14317
  continue;
13987
14318
  }
13988
14319
  const open2 = openChecksForMission(mission);
@@ -14028,6 +14359,7 @@ var init_terminalSummary = __esm({
14028
14359
  "use strict";
14029
14360
  import_picocolors = __toESM(require_picocolors());
14030
14361
  init_missionSelection();
14362
+ init_inRepoMissions();
14031
14363
  init_statusLabels();
14032
14364
  init_resolveNextAction();
14033
14365
  }
@@ -14690,7 +15022,7 @@ var VERSION;
14690
15022
  var init_version = __esm({
14691
15023
  "src/version.ts"() {
14692
15024
  "use strict";
14693
- VERSION = "1.6.1";
15025
+ VERSION = "1.6.3";
14694
15026
  }
14695
15027
  });
14696
15028
 
@@ -15062,17 +15394,17 @@ async function initCursorRulesPack(options) {
15062
15394
  const pack = buildCursorRulesPack();
15063
15395
  for (const rule of pack) {
15064
15396
  const file = `${CURSOR_RULES_DIR}/${rule.filename}`;
15065
- const path = (0, import_node_path17.join)(options.cwd, file);
15397
+ const path = (0, import_node_path18.join)(options.cwd, file);
15066
15398
  const existing = await readExistingFile(path);
15067
15399
  const changed = !existing.exists || existing.content !== rule.content;
15068
15400
  const action = !existing.exists ? "created" : changed ? "updated" : "unchanged";
15069
15401
  if (!options.dryRun && changed) {
15070
- await (0, import_promises12.mkdir)((0, import_node_path17.join)(options.cwd, CURSOR_RULES_DIR), { recursive: true });
15402
+ await (0, import_promises12.mkdir)((0, import_node_path18.join)(options.cwd, CURSOR_RULES_DIR), { recursive: true });
15071
15403
  await (0, import_promises12.writeFile)(path, rule.content, "utf-8");
15072
15404
  }
15073
15405
  results.push({ target: "cursor", file, path, action });
15074
15406
  }
15075
- const legacyPath = (0, import_node_path17.join)(options.cwd, LEGACY_CURSOR_RULE_FILE);
15407
+ const legacyPath = (0, import_node_path18.join)(options.cwd, LEGACY_CURSOR_RULE_FILE);
15076
15408
  if (!options.dryRun && await fileExists(legacyPath)) {
15077
15409
  await (0, import_promises12.rm)(legacyPath, { force: true });
15078
15410
  }
@@ -15099,12 +15431,12 @@ async function fileExists(path) {
15099
15431
  function isFileNotFoundError(error) {
15100
15432
  return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
15101
15433
  }
15102
- var import_promises12, import_node_path17, CURSOR_RULES_DIR, LEGACY_CURSOR_RULE_FILE, DOMAIN_PATH_POINTER;
15434
+ var import_promises12, import_node_path18, CURSOR_RULES_DIR, LEGACY_CURSOR_RULE_FILE, DOMAIN_PATH_POINTER;
15103
15435
  var init_cursorRulesPack = __esm({
15104
15436
  "src/commands/cursorRulesPack.ts"() {
15105
15437
  "use strict";
15106
15438
  import_promises12 = require("node:fs/promises");
15107
- import_node_path17 = require("node:path");
15439
+ import_node_path18 = require("node:path");
15108
15440
  init_commands();
15109
15441
  CURSOR_RULES_DIR = ".cursor/rules";
15110
15442
  LEGACY_CURSOR_RULE_FILE = `${CURSOR_RULES_DIR}/viberaven.mdc`;
@@ -15204,8 +15536,8 @@ var init_agentTargets = __esm({
15204
15536
 
15205
15537
  // src/commands/seedPackageJsonScripts.ts
15206
15538
  async function seedPackageJsonScripts(options) {
15207
- const packageJsonPath = (0, import_node_path18.join)(options.cwd, "package.json");
15208
- if (!(0, import_node_fs9.existsSync)(packageJsonPath)) {
15539
+ const packageJsonPath = (0, import_node_path19.join)(options.cwd, "package.json");
15540
+ if (!(0, import_node_fs10.existsSync)(packageJsonPath)) {
15209
15541
  return null;
15210
15542
  }
15211
15543
  const raw = await (0, import_promises13.readFile)(packageJsonPath, "utf-8");
@@ -15242,13 +15574,13 @@ async function seedPackageJsonScripts(options) {
15242
15574
  }
15243
15575
  return { action: "updated", added, skipped, changed: true };
15244
15576
  }
15245
- var import_node_fs9, import_promises13, import_node_path18, VIBERAVEN_PACKAGE_JSON_SCRIPTS;
15577
+ var import_node_fs10, import_promises13, import_node_path19, VIBERAVEN_PACKAGE_JSON_SCRIPTS;
15246
15578
  var init_seedPackageJsonScripts = __esm({
15247
15579
  "src/commands/seedPackageJsonScripts.ts"() {
15248
15580
  "use strict";
15249
- import_node_fs9 = require("node:fs");
15581
+ import_node_fs10 = require("node:fs");
15250
15582
  import_promises13 = require("node:fs/promises");
15251
- import_node_path18 = require("node:path");
15583
+ import_node_path19 = require("node:path");
15252
15584
  init_commands();
15253
15585
  VIBERAVEN_PACKAGE_JSON_SCRIPTS = {
15254
15586
  "viberaven:gate": PUBLIC_AGENT_MODE_COMMAND,
@@ -15268,12 +15600,12 @@ async function initAgentRules(options) {
15268
15600
  continue;
15269
15601
  }
15270
15602
  const file = AGENT_RULE_TARGETS[target].file;
15271
- const path = (0, import_node_path19.join)(options.cwd, file);
15603
+ const path = (0, import_node_path20.join)(options.cwd, file);
15272
15604
  const existing = await readExistingFile2(path);
15273
15605
  const injected = injectAgentRulesBlock(existing.content, renderAgentRulesForTarget(target));
15274
15606
  const action = !existing.exists ? "created" : injected.changed ? "updated" : "unchanged";
15275
15607
  if (!options.dryRun && injected.changed) {
15276
- await (0, import_promises14.mkdir)((0, import_node_path19.dirname)(path), { recursive: true });
15608
+ await (0, import_promises14.mkdir)((0, import_node_path20.dirname)(path), { recursive: true });
15277
15609
  await (0, import_promises14.writeFile)(path, injected.content, "utf-8");
15278
15610
  }
15279
15611
  results.push({ target, file, path, action });
@@ -15353,12 +15685,12 @@ async function readExistingFile2(path) {
15353
15685
  function isFileNotFoundError2(error) {
15354
15686
  return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
15355
15687
  }
15356
- var import_promises14, import_node_path19;
15688
+ var import_promises14, import_node_path20;
15357
15689
  var init_initRules = __esm({
15358
15690
  "src/commands/initRules.ts"() {
15359
15691
  "use strict";
15360
15692
  import_promises14 = require("node:fs/promises");
15361
- import_node_path19 = require("node:path");
15693
+ import_node_path20 = require("node:path");
15362
15694
  init_agentRulesBlock();
15363
15695
  init_agentTargets();
15364
15696
  init_cursorRulesPack();
@@ -15621,7 +15953,7 @@ async function runInteractiveSession(startDir = process.cwd()) {
15621
15953
  Ie(`${import_picocolors4.default.bold("VibeRaven")} ${import_picocolors4.default.dim(VERSION)}`);
15622
15954
  const cwd = await resolveWorkspaceRoot(startDir);
15623
15955
  const artifactsAt = await findArtifactsWorkspace(startDir);
15624
- if (artifactsAt && (0, import_node_path20.resolve)(artifactsAt) !== (0, import_node_path20.resolve)(startDir)) {
15956
+ if (artifactsAt && (0, import_node_path21.resolve)(artifactsAt) !== (0, import_node_path21.resolve)(startDir)) {
15625
15957
  M2.message(import_picocolors4.default.dim(`Using scan from: ${artifactsAt}`));
15626
15958
  } else {
15627
15959
  M2.message(import_picocolors4.default.dim(`Project folder: ${cwd}`));
@@ -15680,11 +16012,11 @@ async function runInteractiveSession(startDir = process.cwd()) {
15680
16012
  }
15681
16013
  Se(import_picocolors4.default.dim("Run viberaven anytime for the interactive menu."));
15682
16014
  }
15683
- var import_node_path20, import_picocolors4;
16015
+ var import_node_path21, import_picocolors4;
15684
16016
  var init_runInteractive = __esm({
15685
16017
  "src/tui/runInteractive.ts"() {
15686
16018
  "use strict";
15687
- import_node_path20 = require("node:path");
16019
+ import_node_path21 = require("node:path");
15688
16020
  init_dist2();
15689
16021
  import_picocolors4 = __toESM(require_picocolors());
15690
16022
  init_config();
@@ -15709,18 +16041,18 @@ var init_runInteractive = __esm({
15709
16041
  // src/commands/condense.ts
15710
16042
  async function runCondenseCommand(options) {
15711
16043
  const dir = getProjectArtifactsDir(options.cwd);
15712
- const artifact = JSON.parse(await (0, import_promises15.readFile)((0, import_node_path21.join)(dir, "last-scan.json"), "utf8"));
15713
- const contextMapPath = (0, import_node_path21.join)(dir, "context-map.json");
16044
+ const artifact = JSON.parse(await (0, import_promises15.readFile)((0, import_node_path22.join)(dir, "last-scan.json"), "utf8"));
16045
+ const contextMapPath = (0, import_node_path22.join)(dir, "context-map.json");
15714
16046
  await (0, import_promises15.writeFile)(contextMapPath, `${JSON.stringify(generateContextMap(artifact), null, 2)}
15715
16047
  `, "utf8");
15716
16048
  return { contextMapPath };
15717
16049
  }
15718
- var import_promises15, import_node_path21;
16050
+ var import_promises15, import_node_path22;
15719
16051
  var init_condense = __esm({
15720
16052
  "src/commands/condense.ts"() {
15721
16053
  "use strict";
15722
16054
  import_promises15 = require("node:fs/promises");
15723
- import_node_path21 = require("node:path");
16055
+ import_node_path22 = require("node:path");
15724
16056
  init_config();
15725
16057
  init_contextMap();
15726
16058
  }
@@ -15728,9 +16060,9 @@ var init_condense = __esm({
15728
16060
 
15729
16061
  // src/heal/pathSafety.ts
15730
16062
  function assertSafeHealTarget(cwd, target) {
15731
- const root = (0, import_node_path22.resolve)(cwd);
15732
- const absolute = (0, import_node_path22.resolve)(root, target);
15733
- const rel = (0, import_node_path22.relative)(root, absolute);
16063
+ const root = (0, import_node_path23.resolve)(cwd);
16064
+ const absolute = (0, import_node_path23.resolve)(root, target);
16065
+ const rel = (0, import_node_path23.relative)(root, absolute);
15734
16066
  if (rel.startsWith("..") || rel === "" || /^[A-Za-z]:/.test(rel)) {
15735
16067
  throw new Error("Heal target must stay inside the workspace");
15736
16068
  }
@@ -15740,11 +16072,11 @@ function assertSafeHealTarget(cwd, target) {
15740
16072
  }
15741
16073
  return absolute;
15742
16074
  }
15743
- var import_node_path22, BLOCKED_SEGMENTS;
16075
+ var import_node_path23, BLOCKED_SEGMENTS;
15744
16076
  var init_pathSafety = __esm({
15745
16077
  "src/heal/pathSafety.ts"() {
15746
16078
  "use strict";
15747
- import_node_path22 = require("node:path");
16079
+ import_node_path23 = require("node:path");
15748
16080
  BLOCKED_SEGMENTS = /* @__PURE__ */ new Set([".git", "node_modules", "dist", "build", ".next", ".viberaven"]);
15749
16081
  }
15750
16082
  });
@@ -15770,11 +16102,11 @@ function newHealId(now = /* @__PURE__ */ new Date()) {
15770
16102
  return `heal_${now.toISOString().replace(/\D/g, "").slice(0, 17)}_${(0, import_node_crypto.randomBytes)(3).toString("hex")}`;
15771
16103
  }
15772
16104
  async function createHealDir(cwd, makeId = () => newHealId()) {
15773
- const parent = (0, import_node_path23.join)(cwd, ".viberaven", "heal");
16105
+ const parent = (0, import_node_path24.join)(cwd, ".viberaven", "heal");
15774
16106
  await (0, import_promises16.mkdir)(parent, { recursive: true });
15775
16107
  for (let attempt = 0; attempt < 10; attempt += 1) {
15776
16108
  const id = makeId();
15777
- const dir = (0, import_node_path23.join)(parent, id);
16109
+ const dir = (0, import_node_path24.join)(parent, id);
15778
16110
  try {
15779
16111
  await (0, import_promises16.mkdir)(dir);
15780
16112
  return { id, dir };
@@ -15784,13 +16116,13 @@ async function createHealDir(cwd, makeId = () => newHealId()) {
15784
16116
  }
15785
16117
  throw new Error("Could not create a new heal directory under .viberaven/heal.");
15786
16118
  }
15787
- var import_node_crypto, import_promises16, import_node_path23;
16119
+ var import_node_crypto, import_promises16, import_node_path24;
15788
16120
  var init_healId = __esm({
15789
16121
  "src/heal/healId.ts"() {
15790
16122
  "use strict";
15791
16123
  import_node_crypto = require("node:crypto");
15792
16124
  import_promises16 = require("node:fs/promises");
15793
- import_node_path23 = require("node:path");
16125
+ import_node_path24 = require("node:path");
15794
16126
  }
15795
16127
  });
15796
16128
 
@@ -16257,7 +16589,7 @@ export const config = {
16257
16589
  // src/heal/recipes/eslintRestrictedImports.ts
16258
16590
  function detectEslintConfigFile(cwd) {
16259
16591
  for (const candidate of ESLINT_CONFIG_CANDIDATES) {
16260
- if ((0, import_node_fs10.existsSync)((0, import_node_path24.join)(cwd, candidate))) {
16592
+ if ((0, import_node_fs11.existsSync)((0, import_node_path25.join)(cwd, candidate))) {
16261
16593
  return candidate;
16262
16594
  }
16263
16595
  }
@@ -16412,12 +16744,12 @@ function applyEslintRestrictedImportsRecipe(source, configFile) {
16412
16744
  }
16413
16745
  return applyLegacyModuleRecipe(source);
16414
16746
  }
16415
- var import_node_fs10, import_node_path24, VIBERAVEN_ESLINT_MARKER, RESTRICTED_IMPORTS_MESSAGE, RESTRICTED_PATHS, RESTRICTED_PATTERNS, ESLINT_CONFIG_CANDIDATES;
16747
+ var import_node_fs11, import_node_path25, VIBERAVEN_ESLINT_MARKER, RESTRICTED_IMPORTS_MESSAGE, RESTRICTED_PATHS, RESTRICTED_PATTERNS, ESLINT_CONFIG_CANDIDATES;
16416
16748
  var init_eslintRestrictedImports = __esm({
16417
16749
  "src/heal/recipes/eslintRestrictedImports.ts"() {
16418
16750
  "use strict";
16419
- import_node_fs10 = require("node:fs");
16420
- import_node_path24 = require("node:path");
16751
+ import_node_fs11 = require("node:fs");
16752
+ import_node_path25 = require("node:path");
16421
16753
  init_commands();
16422
16754
  VIBERAVEN_ESLINT_MARKER = "VibeRaven heal: eslint_restricted_imports";
16423
16755
  RESTRICTED_IMPORTS_MESSAGE = `Restricted import. Run ${PUBLIC_AGENT_MODE_COMMAND} before substituting packages.`;
@@ -16468,13 +16800,13 @@ function defaultTargetFile(gapId) {
16468
16800
  return map[gapId];
16469
16801
  }
16470
16802
  async function readSourceOrEmpty(absolutePath) {
16471
- if (!(0, import_node_fs11.existsSync)(absolutePath)) return "";
16803
+ if (!(0, import_node_fs12.existsSync)(absolutePath)) return "";
16472
16804
  return (0, import_promises17.readFile)(absolutePath, "utf8");
16473
16805
  }
16474
16806
  async function detectUpstash(cwd) {
16475
16807
  try {
16476
- const pkgPath = (0, import_node_path25.join)(cwd, "package.json");
16477
- if (!(0, import_node_fs11.existsSync)(pkgPath)) return false;
16808
+ const pkgPath = (0, import_node_path26.join)(cwd, "package.json");
16809
+ if (!(0, import_node_fs12.existsSync)(pkgPath)) return false;
16478
16810
  const raw = await (0, import_promises17.readFile)(pkgPath, "utf8");
16479
16811
  const pkg = JSON.parse(raw);
16480
16812
  const deps = {
@@ -16490,7 +16822,7 @@ async function dispatchRecipeByGapId(gapId, cwd, explicitTarget) {
16490
16822
  const targetFile = explicitTarget ?? defaultTargetFile(gapId);
16491
16823
  if (targetFile === void 0) return null;
16492
16824
  if (gapId === "auth_secret_missing" || gapId === "node_env_not_set" || gapId === "database_url_missing") {
16493
- const absolutePath = (0, import_node_path25.join)(cwd, targetFile);
16825
+ const absolutePath = (0, import_node_path26.join)(cwd, targetFile);
16494
16826
  const source = await readSourceOrEmpty(absolutePath);
16495
16827
  let result;
16496
16828
  if (gapId === "auth_secret_missing") result = applyAuthSecretRecipe(source);
@@ -16504,25 +16836,25 @@ async function dispatchRecipeByGapId(gapId, cwd, explicitTarget) {
16504
16836
  };
16505
16837
  }
16506
16838
  if (gapId === "missing_error_boundary") {
16507
- const absolutePath = (0, import_node_path25.join)(cwd, "app/error.tsx");
16839
+ const absolutePath = (0, import_node_path26.join)(cwd, "app/error.tsx");
16508
16840
  const source = await readSourceOrEmpty(absolutePath);
16509
16841
  const result = applyErrorBoundaryRecipe(source);
16510
16842
  return { ...result, canAutoApply: true, recipeName: gapId };
16511
16843
  }
16512
16844
  if (gapId === "missing_health_route") {
16513
- const absolutePath = (0, import_node_path25.join)(cwd, "app/api/health/route.ts");
16845
+ const absolutePath = (0, import_node_path26.join)(cwd, "app/api/health/route.ts");
16514
16846
  const source = await readSourceOrEmpty(absolutePath);
16515
16847
  const result = applyHealthRouteRecipe(source);
16516
16848
  return { ...result, canAutoApply: true, recipeName: gapId };
16517
16849
  }
16518
16850
  if (gapId === "missing_loading_state") {
16519
- const absolutePath = (0, import_node_path25.join)(cwd, "app/loading.tsx");
16851
+ const absolutePath = (0, import_node_path26.join)(cwd, "app/loading.tsx");
16520
16852
  const source = await readSourceOrEmpty(absolutePath);
16521
16853
  const result = applyLoadingStateRecipe(source);
16522
16854
  return { ...result, canAutoApply: true, recipeName: gapId };
16523
16855
  }
16524
16856
  if (gapId === "missing_404_page") {
16525
- const absolutePath = (0, import_node_path25.join)(cwd, "app/not-found.tsx");
16857
+ const absolutePath = (0, import_node_path26.join)(cwd, "app/not-found.tsx");
16526
16858
  const source = await readSourceOrEmpty(absolutePath);
16527
16859
  const result = applyNotFoundRecipe(source);
16528
16860
  return { ...result, canAutoApply: true, recipeName: gapId };
@@ -16539,10 +16871,10 @@ async function dispatchRecipeByGapId(gapId, cwd, explicitTarget) {
16539
16871
  }
16540
16872
  if (gapId === "missing_csp_header") {
16541
16873
  let configFile = "next.config.js";
16542
- let absolutePath = (0, import_node_path25.join)(cwd, configFile);
16543
- if (!(0, import_node_fs11.existsSync)(absolutePath)) {
16874
+ let absolutePath = (0, import_node_path26.join)(cwd, configFile);
16875
+ if (!(0, import_node_fs12.existsSync)(absolutePath)) {
16544
16876
  configFile = "next.config.mjs";
16545
- absolutePath = (0, import_node_path25.join)(cwd, configFile);
16877
+ absolutePath = (0, import_node_path26.join)(cwd, configFile);
16546
16878
  }
16547
16879
  const source = await readSourceOrEmpty(absolutePath);
16548
16880
  const result = applyCspHeaderRecipe(source);
@@ -16554,7 +16886,7 @@ async function dispatchRecipeByGapId(gapId, cwd, explicitTarget) {
16554
16886
  }
16555
16887
  if (gapId === "missing_rate_limit") {
16556
16888
  const hasUpstash = await detectUpstash(cwd);
16557
- const middlewarePath = (0, import_node_path25.join)(cwd, "middleware.ts");
16889
+ const middlewarePath = (0, import_node_path26.join)(cwd, "middleware.ts");
16558
16890
  const source = await readSourceOrEmpty(middlewarePath);
16559
16891
  const result = applyRateLimitRecipe(source, hasUpstash);
16560
16892
  return {
@@ -16576,7 +16908,7 @@ async function dispatchRecipeByGapId(gapId, cwd, explicitTarget) {
16576
16908
  recipeName: gapId
16577
16909
  };
16578
16910
  }
16579
- const absolutePath = (0, import_node_path25.join)(cwd, configFile);
16911
+ const absolutePath = (0, import_node_path26.join)(cwd, configFile);
16580
16912
  const source = await readSourceOrEmpty(absolutePath);
16581
16913
  const result = applyEslintRestrictedImportsRecipe(source, configFile);
16582
16914
  return {
@@ -16590,13 +16922,13 @@ async function dispatchRecipeByGapId(gapId, cwd, explicitTarget) {
16590
16922
  }
16591
16923
  return null;
16592
16924
  }
16593
- var import_node_fs11, import_promises17, import_node_path25;
16925
+ var import_node_fs12, import_promises17, import_node_path26;
16594
16926
  var init_recipes = __esm({
16595
16927
  "src/heal/recipes/index.ts"() {
16596
16928
  "use strict";
16597
- import_node_fs11 = require("node:fs");
16929
+ import_node_fs12 = require("node:fs");
16598
16930
  import_promises17 = require("node:fs/promises");
16599
- import_node_path25 = require("node:path");
16931
+ import_node_path26 = require("node:path");
16600
16932
  init_envAuthSecret();
16601
16933
  init_envNodeEnv();
16602
16934
  init_envDatabaseUrl();
@@ -16673,16 +17005,16 @@ async function applyHeal(options) {
16673
17005
  rollback: { available: false, instructions: "Recipe matched but no change was needed (already applied or file already exists)." }
16674
17006
  };
16675
17007
  }
16676
- const absoluteTarget = (0, import_node_path26.join)(options.cwd, dispatched.targetFile);
17008
+ const absoluteTarget = (0, import_node_path27.join)(options.cwd, dispatched.targetFile);
16677
17009
  assertSafeHealTarget(options.cwd, dispatched.targetFile);
16678
- await (0, import_promises18.mkdir)((0, import_node_path26.dirname)(absoluteTarget), { recursive: true });
17010
+ await (0, import_promises18.mkdir)((0, import_node_path27.dirname)(absoluteTarget), { recursive: true });
16679
17011
  const created2 = await createHealDir(options.cwd);
16680
17012
  id = created2.id;
16681
17013
  const healDir2 = created2.dir;
16682
- await (0, import_promises18.mkdir)((0, import_node_path26.join)(healDir2, "before"));
16683
- const existedBefore = (0, import_node_fs12.existsSync)(absoluteTarget);
17014
+ await (0, import_promises18.mkdir)((0, import_node_path27.join)(healDir2, "before"));
17015
+ const existedBefore = (0, import_node_fs13.existsSync)(absoluteTarget);
16684
17016
  const beforeContent = existedBefore ? await (0, import_promises18.readFile)(absoluteTarget, "utf8") : "";
16685
- await (0, import_promises18.writeFile)((0, import_node_path26.join)(healDir2, "before", "target.txt"), beforeContent, "utf8");
17017
+ await (0, import_promises18.writeFile)((0, import_node_path27.join)(healDir2, "before", "target.txt"), beforeContent, "utf8");
16686
17018
  await (0, import_promises18.writeFile)(absoluteTarget, dispatched.output, "utf8");
16687
17019
  const patch2 = [
16688
17020
  `--- ${dispatched.targetFile}`,
@@ -16693,7 +17025,7 @@ async function applyHeal(options) {
16693
17025
  dispatched.output,
16694
17026
  ""
16695
17027
  ].join("\n");
16696
- await (0, import_promises18.writeFile)((0, import_node_path26.join)(healDir2, "patch.diff"), patch2, "utf8");
17028
+ await (0, import_promises18.writeFile)((0, import_node_path27.join)(healDir2, "patch.diff"), patch2, "utf8");
16697
17029
  const result2 = {
16698
17030
  $schema: "https://viberaven.dev/schemas/heal-result.schema.json",
16699
17031
  schemaVersion: "v1",
@@ -16705,17 +17037,17 @@ async function applyHeal(options) {
16705
17037
  recipe: dispatched.recipeName,
16706
17038
  ...dispatched.warnings?.length ? { warnings: dispatched.warnings } : {},
16707
17039
  target: dispatched.targetFile,
16708
- changedFiles: [(0, import_node_path26.relative)(options.cwd, absoluteTarget).replace(/\\/g, "/")],
17040
+ changedFiles: [(0, import_node_path27.relative)(options.cwd, absoluteTarget).replace(/\\/g, "/")],
16709
17041
  artifacts: {
16710
17042
  patch: `.viberaven/heal/${id}/patch.diff`,
16711
17043
  result: `.viberaven/heal/${id}/result.json`
16712
17044
  },
16713
17045
  rollback: {
16714
17046
  available: true,
16715
- instructions: rollbackInstructions(id, (0, import_node_path26.relative)(options.cwd, absoluteTarget).replace(/\\/g, "/"), existedBefore)
17047
+ instructions: rollbackInstructions(id, (0, import_node_path27.relative)(options.cwd, absoluteTarget).replace(/\\/g, "/"), existedBefore)
16716
17048
  }
16717
17049
  };
16718
- await (0, import_promises18.writeFile)((0, import_node_path26.join)(healDir2, "result.json"), `${JSON.stringify(result2, null, 2)}
17050
+ await (0, import_promises18.writeFile)((0, import_node_path27.join)(healDir2, "result.json"), `${JSON.stringify(result2, null, 2)}
16719
17051
  `, "utf8");
16720
17052
  return result2;
16721
17053
  }
@@ -16755,8 +17087,8 @@ async function applyHeal(options) {
16755
17087
  const created = await createHealDir(options.cwd);
16756
17088
  id = created.id;
16757
17089
  const healDir = created.dir;
16758
- await (0, import_promises18.mkdir)((0, import_node_path26.join)(healDir, "before"));
16759
- await (0, import_promises18.writeFile)((0, import_node_path26.join)(healDir, "before", "target.txt"), before, "utf8");
17090
+ await (0, import_promises18.mkdir)((0, import_node_path27.join)(healDir, "before"));
17091
+ await (0, import_promises18.writeFile)((0, import_node_path27.join)(healDir, "before", "target.txt"), before, "utf8");
16760
17092
  await (0, import_promises18.writeFile)(absolute, recipe.output, "utf8");
16761
17093
  const patch = [
16762
17094
  `--- ${options.target}`,
@@ -16767,7 +17099,7 @@ async function applyHeal(options) {
16767
17099
  recipe.output,
16768
17100
  ""
16769
17101
  ].join("\n");
16770
- await (0, import_promises18.writeFile)((0, import_node_path26.join)(healDir, "patch.diff"), patch, "utf8");
17102
+ await (0, import_promises18.writeFile)((0, import_node_path27.join)(healDir, "patch.diff"), patch, "utf8");
16771
17103
  const result = {
16772
17104
  $schema: "https://viberaven.dev/schemas/heal-result.schema.json",
16773
17105
  schemaVersion: "v1",
@@ -16778,7 +17110,7 @@ async function applyHeal(options) {
16778
17110
  gapId: options.gapId,
16779
17111
  recipe: "empty-catch-safe-response",
16780
17112
  target: options.target,
16781
- changedFiles: [(0, import_node_path26.relative)(options.cwd, absolute).replace(/\\/g, "/")],
17113
+ changedFiles: [(0, import_node_path27.relative)(options.cwd, absolute).replace(/\\/g, "/")],
16782
17114
  artifacts: {
16783
17115
  patch: `.viberaven/heal/${id}/patch.diff`,
16784
17116
  result: `.viberaven/heal/${id}/result.json`
@@ -16786,20 +17118,20 @@ async function applyHeal(options) {
16786
17118
  rollback: {
16787
17119
  available: true,
16788
17120
  // The empty-catch recipe only edits a file that already exists.
16789
- instructions: rollbackInstructions(id, (0, import_node_path26.relative)(options.cwd, absolute).replace(/\\/g, "/"), true)
17121
+ instructions: rollbackInstructions(id, (0, import_node_path27.relative)(options.cwd, absolute).replace(/\\/g, "/"), true)
16790
17122
  }
16791
17123
  };
16792
- await (0, import_promises18.writeFile)((0, import_node_path26.join)(healDir, "result.json"), `${JSON.stringify(result, null, 2)}
17124
+ await (0, import_promises18.writeFile)((0, import_node_path27.join)(healDir, "result.json"), `${JSON.stringify(result, null, 2)}
16793
17125
  `, "utf8");
16794
17126
  return result;
16795
17127
  }
16796
- var import_promises18, import_node_fs12, import_node_path26;
17128
+ var import_promises18, import_node_fs13, import_node_path27;
16797
17129
  var init_apply = __esm({
16798
17130
  "src/heal/apply.ts"() {
16799
17131
  "use strict";
16800
17132
  import_promises18 = require("node:fs/promises");
16801
- import_node_fs12 = require("node:fs");
16802
- import_node_path26 = require("node:path");
17133
+ import_node_fs13 = require("node:fs");
17134
+ import_node_path27 = require("node:path");
16803
17135
  init_pathSafety();
16804
17136
  init_emptyCatch();
16805
17137
  init_healId();
@@ -16809,7 +17141,7 @@ var init_apply = __esm({
16809
17141
 
16810
17142
  // src/heal/plan.ts
16811
17143
  async function writeHealPlan(options) {
16812
- const dir = (0, import_node_path27.join)(options.cwd, ".viberaven");
17144
+ const dir = (0, import_node_path28.join)(options.cwd, ".viberaven");
16813
17145
  await (0, import_promises19.mkdir)(dir, { recursive: true });
16814
17146
  const id = newHealId();
16815
17147
  const target = options.target ?? `gap:${options.gapId}`;
@@ -16862,18 +17194,18 @@ async function writeHealPlan(options) {
16862
17194
  artifacts: { plan: ".viberaven/heal-plan.md" },
16863
17195
  rollback: { available: false, instructions: "No source files were changed." }
16864
17196
  };
16865
- await (0, import_promises19.writeFile)((0, import_node_path27.join)(dir, "heal-plan.md"), markdown, "utf8");
16866
- await (0, import_promises19.writeFile)((0, import_node_path27.join)(dir, "heal-plan.json"), `${JSON.stringify(result, null, 2)}
17197
+ await (0, import_promises19.writeFile)((0, import_node_path28.join)(dir, "heal-plan.md"), markdown, "utf8");
17198
+ await (0, import_promises19.writeFile)((0, import_node_path28.join)(dir, "heal-plan.json"), `${JSON.stringify(result, null, 2)}
16867
17199
  `, "utf8");
16868
17200
  return result;
16869
17201
  }
16870
- var import_promises19, import_node_path27;
17202
+ var import_promises19, import_node_path28;
16871
17203
  var init_plan = __esm({
16872
17204
  "src/heal/plan.ts"() {
16873
17205
  "use strict";
16874
17206
  import_promises19 = require("node:fs/promises");
16875
17207
  init_healId();
16876
- import_node_path27 = require("node:path");
17208
+ import_node_path28 = require("node:path");
16877
17209
  init_commands();
16878
17210
  init_supabaseRls2();
16879
17211
  }
@@ -16881,7 +17213,7 @@ var init_plan = __esm({
16881
17213
 
16882
17214
  // src/heal/prompt.ts
16883
17215
  async function writeHealPrompt(options) {
16884
- const dir = (0, import_node_path28.join)(options.cwd, ".viberaven");
17216
+ const dir = (0, import_node_path29.join)(options.cwd, ".viberaven");
16885
17217
  await (0, import_promises20.mkdir)(dir, { recursive: true });
16886
17218
  const id = newHealId();
16887
17219
  const target = options.target ?? `gap:${options.gapId}`;
@@ -16909,16 +17241,16 @@ async function writeHealPrompt(options) {
16909
17241
  artifacts: { prompt: ".viberaven/heal-prompt.md" },
16910
17242
  rollback: { available: false, instructions: "No source files were changed." }
16911
17243
  };
16912
- await (0, import_promises20.writeFile)((0, import_node_path28.join)(dir, "heal-prompt.md"), prompt, "utf8");
17244
+ await (0, import_promises20.writeFile)((0, import_node_path29.join)(dir, "heal-prompt.md"), prompt, "utf8");
16913
17245
  return result;
16914
17246
  }
16915
- var import_promises20, import_node_path28;
17247
+ var import_promises20, import_node_path29;
16916
17248
  var init_prompt = __esm({
16917
17249
  "src/heal/prompt.ts"() {
16918
17250
  "use strict";
16919
17251
  import_promises20 = require("node:fs/promises");
16920
17252
  init_healId();
16921
- import_node_path28 = require("node:path");
17253
+ import_node_path29 = require("node:path");
16922
17254
  init_commands();
16923
17255
  }
16924
17256
  });
@@ -17021,8 +17353,8 @@ var init_heal = __esm({
17021
17353
 
17022
17354
  // src/stackRecommend.ts
17023
17355
  async function recommendStack(cwd = process.cwd()) {
17024
- const pkgPath = (0, import_node_path29.join)(cwd, "package.json");
17025
- if (!(0, import_node_fs13.existsSync)(pkgPath)) {
17356
+ const pkgPath = (0, import_node_path30.join)(cwd, "package.json");
17357
+ if (!(0, import_node_fs14.existsSync)(pkgPath)) {
17026
17358
  return DEFAULT_STACK;
17027
17359
  }
17028
17360
  const pkg = JSON.parse(await (0, import_promises22.readFile)(pkgPath, "utf-8"));
@@ -17053,13 +17385,13 @@ async function recommendStack(cwd = process.cwd()) {
17053
17385
  }
17054
17386
  return rec;
17055
17387
  }
17056
- var import_promises22, import_node_fs13, import_node_path29, DEFAULT_STACK;
17388
+ var import_promises22, import_node_fs14, import_node_path30, DEFAULT_STACK;
17057
17389
  var init_stackRecommend = __esm({
17058
17390
  "src/stackRecommend.ts"() {
17059
17391
  "use strict";
17060
17392
  import_promises22 = require("node:fs/promises");
17061
- import_node_fs13 = require("node:fs");
17062
- import_node_path29 = require("node:path");
17393
+ import_node_fs14 = require("node:fs");
17394
+ import_node_path30 = require("node:path");
17063
17395
  DEFAULT_STACK = {
17064
17396
  frontend: "react",
17065
17397
  ui: "tailwind + shadcn/ui",
@@ -17202,13 +17534,13 @@ async function readBlobs(cwd, shas) {
17202
17534
  async function exportCommit(cwd, commit) {
17203
17535
  const entries = parseTree(await git(cwd, ["ls-tree", "-r", "-z", "-l", "--full-tree", commit])).filter(wanted);
17204
17536
  const blobs = await readBlobs(cwd, [...new Set(entries.map((entry) => entry.sha))]);
17205
- const root = await (0, import_promises23.mkdtemp)((0, import_node_path30.join)((0, import_node_os.tmpdir)(), "viberaven-base-"));
17537
+ const root = await (0, import_promises23.mkdtemp)((0, import_node_path31.join)((0, import_node_os.tmpdir)(), "viberaven-base-"));
17206
17538
  for (const entry of entries) {
17207
- const target = (0, import_node_path30.resolve)(root, ...entry.path.split("/"));
17208
- if (!target.startsWith(root + import_node_path30.sep)) continue;
17539
+ const target = (0, import_node_path31.resolve)(root, ...entry.path.split("/"));
17540
+ if (!target.startsWith(root + import_node_path31.sep)) continue;
17209
17541
  const content = blobs.get(entry.sha);
17210
17542
  if (!content) continue;
17211
- await (0, import_promises23.mkdir)((0, import_node_path30.dirname)(target), { recursive: true });
17543
+ await (0, import_promises23.mkdir)((0, import_node_path31.dirname)(target), { recursive: true });
17212
17544
  await (0, import_promises23.writeFile)(target, content);
17213
17545
  }
17214
17546
  return root;
@@ -17278,14 +17610,14 @@ function renderCheckMarkdown({ head, change, version, score }) {
17278
17610
  return `${lines.join("\n")}
17279
17611
  `;
17280
17612
  }
17281
- var import_node_child_process6, import_promises23, import_node_os, import_node_path30, import_node_util4, execFileAsync4, MAX_BLOB_BYTES, SKIP_SEGMENTS, SEVERITY_RANK3, LOCATION_TEXT, SUBJECT, COMMENT_MARKER, DOT;
17613
+ 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;
17282
17614
  var init_checkChange = __esm({
17283
17615
  "src/commands/checkChange.ts"() {
17284
17616
  "use strict";
17285
17617
  import_node_child_process6 = require("node:child_process");
17286
17618
  import_promises23 = require("node:fs/promises");
17287
17619
  import_node_os = require("node:os");
17288
- import_node_path30 = require("node:path");
17620
+ import_node_path31 = require("node:path");
17289
17621
  import_node_util4 = require("node:util");
17290
17622
  init_runScan();
17291
17623
  init_safeGit();
@@ -17369,12 +17701,12 @@ async function hasGithubRemote(cwd) {
17369
17701
  }
17370
17702
  }
17371
17703
  async function githubWorkflowStatus(cwd) {
17372
- return { exists: (0, import_node_fs14.existsSync)((0, import_node_path31.join)(cwd, WORKFLOW_PATH)), path: DISPLAY_PATH, onGithub: await hasGithubRemote(cwd) };
17704
+ return { exists: (0, import_node_fs15.existsSync)((0, import_node_path32.join)(cwd, WORKFLOW_PATH)), path: DISPLAY_PATH, onGithub: await hasGithubRemote(cwd) };
17373
17705
  }
17374
17706
  async function writeGithubWorkflow(cwd, options = {}) {
17375
- const target = (0, import_node_path31.join)(cwd, WORKFLOW_PATH);
17376
- if ((0, import_node_fs14.existsSync)(target) && !options.force) return { written: false, status: await githubWorkflowStatus(cwd) };
17377
- await (0, import_promises24.mkdir)((0, import_node_path31.dirname)(target), { recursive: true });
17707
+ const target = (0, import_node_path32.join)(cwd, WORKFLOW_PATH);
17708
+ if ((0, import_node_fs15.existsSync)(target) && !options.force) return { written: false, status: await githubWorkflowStatus(cwd) };
17709
+ await (0, import_promises24.mkdir)((0, import_node_path32.dirname)(target), { recursive: true });
17378
17710
  await (0, import_promises24.writeFile)(target, renderWorkflow(), "utf8");
17379
17711
  return { written: true, status: await githubWorkflowStatus(cwd) };
17380
17712
  }
@@ -17399,20 +17731,20 @@ async function runInitGithubCommand(options) {
17399
17731
  if (!status.onGithub) console.log("This repository has no github.com remote yet: the workflow runs once it is pushed to GitHub.");
17400
17732
  return 0;
17401
17733
  }
17402
- var import_node_child_process7, import_node_fs14, import_promises24, import_node_path31, import_node_util5, execFileAsync5, WORKFLOW_PATH, DISPLAY_PATH;
17734
+ var import_node_child_process7, import_node_fs15, import_promises24, import_node_path32, import_node_util5, execFileAsync5, WORKFLOW_PATH, DISPLAY_PATH;
17403
17735
  var init_initGithub = __esm({
17404
17736
  "src/commands/initGithub.ts"() {
17405
17737
  "use strict";
17406
17738
  import_node_child_process7 = require("node:child_process");
17407
- import_node_fs14 = require("node:fs");
17739
+ import_node_fs15 = require("node:fs");
17408
17740
  import_promises24 = require("node:fs/promises");
17409
- import_node_path31 = require("node:path");
17741
+ import_node_path32 = require("node:path");
17410
17742
  import_node_util5 = require("node:util");
17411
17743
  init_safeGit();
17412
17744
  init_version();
17413
17745
  init_checkChange();
17414
17746
  execFileAsync5 = (0, import_node_util5.promisify)(import_node_child_process7.execFile);
17415
- WORKFLOW_PATH = (0, import_node_path31.join)(".github", "workflows", "viberaven.yml");
17747
+ WORKFLOW_PATH = (0, import_node_path32.join)(".github", "workflows", "viberaven.yml");
17416
17748
  DISPLAY_PATH = ".github/workflows/viberaven.yml";
17417
17749
  }
17418
17750
  });
@@ -17436,7 +17768,7 @@ function managedBlocks(content) {
17436
17768
  async function checkAgentInjection(cwd) {
17437
17769
  const checks = [];
17438
17770
  for (const item4 of REQUIRED_EXISTENCE_CHECKS) {
17439
- const exists = await fileExists2((0, import_node_path32.join)(cwd, item4.file));
17771
+ const exists = await fileExists2((0, import_node_path33.join)(cwd, item4.file));
17440
17772
  checks.push({
17441
17773
  id: item4.id,
17442
17774
  status: exists ? "pass" : "fail",
@@ -17444,17 +17776,17 @@ async function checkAgentInjection(cwd) {
17444
17776
  });
17445
17777
  }
17446
17778
  for (const item4 of OPTIONAL_CURSOR_PACK_CHECKS) {
17447
- const exists = await fileExists2((0, import_node_path32.join)(cwd, item4.file));
17779
+ const exists = await fileExists2((0, import_node_path33.join)(cwd, item4.file));
17448
17780
  checks.push({
17449
17781
  id: item4.id,
17450
17782
  status: exists ? "pass" : "fail",
17451
17783
  message: exists ? `${item4.file} exists` : `Missing ${item4.file} \u2014 run npx -y viberaven init --agents all`
17452
17784
  });
17453
17785
  }
17454
- const legacyCursorPath = (0, import_node_path32.join)(cwd, ".cursor/rules/viberaven.mdc");
17786
+ const legacyCursorPath = (0, import_node_path33.join)(cwd, ".cursor/rules/viberaven.mdc");
17455
17787
  if (await fileExists2(legacyCursorPath)) {
17456
17788
  const legacyContent = await (0, import_promises25.readFile)(legacyCursorPath, "utf-8");
17457
- const hasCoreSplit = await fileExists2((0, import_node_path32.join)(cwd, ".cursor/rules/viberaven-core.mdc"));
17789
+ const hasCoreSplit = await fileExists2((0, import_node_path33.join)(cwd, ".cursor/rules/viberaven-core.mdc"));
17458
17790
  if (!hasCoreSplit || legacyContent.includes("alwaysApply: true")) {
17459
17791
  checks.push({
17460
17792
  id: "cursor-legacy-mdc",
@@ -17465,7 +17797,7 @@ async function checkAgentInjection(cwd) {
17465
17797
  }
17466
17798
  for (const target of CORE_AGENT_INJECTION_TARGETS) {
17467
17799
  const file = target === "cursor" ? ".cursor/rules/viberaven-core.mdc" : AGENT_RULE_TARGETS[target].file;
17468
- const path = (0, import_node_path32.join)(cwd, file);
17800
+ const path = (0, import_node_path33.join)(cwd, file);
17469
17801
  const exists = await fileExists2(path);
17470
17802
  if (!exists) {
17471
17803
  checks.push({
@@ -17524,12 +17856,12 @@ async function fileExists2(path) {
17524
17856
  return false;
17525
17857
  }
17526
17858
  }
17527
- var import_promises25, import_node_path32, REQUIRED_EXISTENCE_CHECKS, OPTIONAL_CURSOR_PACK_CHECKS, STALE_PATTERNS, GATE_RULE_PHRASES_1_5_2;
17859
+ var import_promises25, import_node_path33, REQUIRED_EXISTENCE_CHECKS, OPTIONAL_CURSOR_PACK_CHECKS, STALE_PATTERNS, GATE_RULE_PHRASES_1_5_2;
17528
17860
  var init_doctorAgents = __esm({
17529
17861
  "src/commands/doctorAgents.ts"() {
17530
17862
  "use strict";
17531
17863
  import_promises25 = require("node:fs/promises");
17532
- import_node_path32 = require("node:path");
17864
+ import_node_path33 = require("node:path");
17533
17865
  init_commands();
17534
17866
  init_agentTargets();
17535
17867
  REQUIRED_EXISTENCE_CHECKS = [
@@ -17800,7 +18132,7 @@ function buildContextCleanupPlan(input) {
17800
18132
  const items = [];
17801
18133
  const seen = /* @__PURE__ */ new Set();
17802
18134
  for (const file of input.files) {
17803
- const path = normalizePath5((0, import_node_path33.relative)(input.projectRoot, file.path) || file.path);
18135
+ const path = normalizePath5((0, import_node_path34.relative)(input.projectRoot, file.path) || file.path);
17804
18136
  const fullPath = normalizePath5(file.path);
17805
18137
  const category = categorizeCleanupFile(path, fullPath, file);
17806
18138
  if (!category || seen.has(path)) {
@@ -17826,8 +18158,8 @@ async function collectCleanupFiles(projectRoot) {
17826
18158
  return files;
17827
18159
  }
17828
18160
  async function writeCleanupPlan(projectRoot, plan) {
17829
- const outputDir = (0, import_node_path33.join)(projectRoot, ".viberaven");
17830
- const outputPath = (0, import_node_path33.join)(outputDir, "context-cleanup.md");
18161
+ const outputDir = (0, import_node_path34.join)(projectRoot, ".viberaven");
18162
+ const outputPath = (0, import_node_path34.join)(outputDir, "context-cleanup.md");
17831
18163
  await (0, import_promises26.mkdir)(outputDir, { recursive: true });
17832
18164
  await (0, import_promises26.writeFile)(outputPath, renderCleanupPlan(plan), "utf8");
17833
18165
  return outputPath;
@@ -17885,9 +18217,9 @@ async function walkCleanupFiles(root, dir, result, depth) {
17885
18217
  if (SKIP_DIRECTORIES.has(entry.name)) {
17886
18218
  continue;
17887
18219
  }
17888
- const absolute = (0, import_node_path33.join)(dir, entry.name);
18220
+ const absolute = (0, import_node_path34.join)(dir, entry.name);
17889
18221
  if (entry.isDirectory()) {
17890
- if (isCachePath(normalizePath5((0, import_node_path33.relative)(root, absolute)))) {
18222
+ if (isCachePath(normalizePath5((0, import_node_path34.relative)(root, absolute)))) {
17891
18223
  result.push({ path: absolute, sizeBytes: 0, kind: "directory" });
17892
18224
  continue;
17893
18225
  }
@@ -17908,12 +18240,12 @@ async function walkCleanupFiles(root, dir, result, depth) {
17908
18240
  function normalizePath5(path) {
17909
18241
  return path.replace(/\\/g, "/");
17910
18242
  }
17911
- var import_promises26, import_node_path33, MAX_WALK_DEPTH, LARGE_LOG_BYTES, SKIP_DIRECTORIES;
18243
+ var import_promises26, import_node_path34, MAX_WALK_DEPTH, LARGE_LOG_BYTES, SKIP_DIRECTORIES;
17912
18244
  var init_cleanPlan = __esm({
17913
18245
  "src/commands/cleanPlan.ts"() {
17914
18246
  "use strict";
17915
18247
  import_promises26 = require("node:fs/promises");
17916
- import_node_path33 = require("node:path");
18248
+ import_node_path34 = require("node:path");
17917
18249
  MAX_WALK_DEPTH = 5;
17918
18250
  LARGE_LOG_BYTES = 8192;
17919
18251
  SKIP_DIRECTORIES = /* @__PURE__ */ new Set([".git", "node_modules"]);
@@ -17991,6 +18323,9 @@ function renderVerifyCommand(action) {
17991
18323
  }
17992
18324
  return ["Verify:", "```bash", redact(action.verifyCommand), "```"];
17993
18325
  }
18326
+ function renderUserSteps(steps) {
18327
+ return steps?.length ? ["Outside the repo (tell the user):", ...steps.map((step) => `- ${redact(step)}`)] : [];
18328
+ }
17994
18329
  function renderAction(action) {
17995
18330
  return [
17996
18331
  `[${action.id}] ${redact(action.title)}`,
@@ -17999,6 +18334,7 @@ function renderAction(action) {
17999
18334
  ...renderTarget(action.target),
18000
18335
  ...renderCopyPayload(action),
18001
18336
  ...renderVerifyCommand(action),
18337
+ ...renderUserSteps(action.userSteps),
18002
18338
  action.resumeInstruction ? `Resume: "${redact(action.resumeInstruction)}"` : void 0
18003
18339
  ].filter((line) => Boolean(line));
18004
18340
  }
@@ -18014,6 +18350,9 @@ function renderActionSurface(manifest, options = {}) {
18014
18350
  for (const action of visibleActions) {
18015
18351
  lines.push(...renderAction(action), "");
18016
18352
  }
18353
+ for (const followUp of options.followUps === false ? [] : manifest.userFollowUps ?? []) {
18354
+ lines.push(`[${followUp.id}] ${redact(followUp.title ?? followUp.gapId ?? "Fixed in the repo")}`, "Status: resolved in the repo", ...renderUserSteps(followUp.userSteps), "");
18355
+ }
18017
18356
  return `${lines.join("\n").trimEnd()}
18018
18357
  `;
18019
18358
  }
@@ -18026,7 +18365,7 @@ var init_render = __esm({
18026
18365
 
18027
18366
  // src/commands/actions.ts
18028
18367
  async function runActionsCommand(input) {
18029
- const actionsPath = (0, import_node_path34.join)(getProjectArtifactsDir(input.cwd), "actions.json");
18368
+ const actionsPath = (0, import_node_path35.join)(getProjectArtifactsDir(input.cwd), "actions.json");
18030
18369
  let raw;
18031
18370
  try {
18032
18371
  raw = await (0, import_promises27.readFile)(actionsPath, "utf8");
@@ -18043,12 +18382,12 @@ async function runActionsCommand(input) {
18043
18382
  process.stdout.write(renderActionSurface(manifest));
18044
18383
  return 0;
18045
18384
  }
18046
- var import_promises27, import_node_path34;
18385
+ var import_promises27, import_node_path35;
18047
18386
  var init_actions = __esm({
18048
18387
  "src/commands/actions.ts"() {
18049
18388
  "use strict";
18050
18389
  import_promises27 = require("node:fs/promises");
18051
- import_node_path34 = require("node:path");
18390
+ import_node_path35 = require("node:path");
18052
18391
  init_render();
18053
18392
  init_config();
18054
18393
  }
@@ -18057,7 +18396,7 @@ var init_actions = __esm({
18057
18396
  // src/commands/verifyAction.ts
18058
18397
  async function currentActionExists(cwd, actionId) {
18059
18398
  try {
18060
- const raw = await (0, import_promises28.readFile)((0, import_node_path35.join)(getProjectArtifactsDir(cwd), "actions.json"), "utf8");
18399
+ const raw = await (0, import_promises28.readFile)((0, import_node_path36.join)(getProjectArtifactsDir(cwd), "actions.json"), "utf8");
18061
18400
  const manifest = JSON.parse(raw);
18062
18401
  return manifest.actions.some((action) => action.id === actionId);
18063
18402
  } catch {
@@ -18101,12 +18440,12 @@ This only looks the action up. Run \`npx -y viberaven --verify\` to scan the rep
18101
18440
  );
18102
18441
  return 0;
18103
18442
  }
18104
- var import_promises28, import_node_path35;
18443
+ var import_promises28, import_node_path36;
18105
18444
  var init_verifyAction = __esm({
18106
18445
  "src/commands/verifyAction.ts"() {
18107
18446
  "use strict";
18108
18447
  import_promises28 = require("node:fs/promises");
18109
- import_node_path35 = require("node:path");
18448
+ import_node_path36 = require("node:path");
18110
18449
  init_registry2();
18111
18450
  init_config();
18112
18451
  }
@@ -18261,6 +18600,7 @@ ${message}
18261
18600
  return failing ? 1 : 0;
18262
18601
  }
18263
18602
  if (options.json) {
18603
+ const userFollowUps = await readUserFollowUps(paths.actionsPath);
18264
18604
  console.log(
18265
18605
  JSON.stringify(
18266
18606
  {
@@ -18283,6 +18623,8 @@ ${message}
18283
18623
  unchanged: change.unchanged.map((gap) => gap.id)
18284
18624
  }
18285
18625
  } : {},
18626
+ // Steps outside the repo for fixes already in it (MCP check and verify read this). Read only.
18627
+ ...userFollowUps.length > 0 ? { userFollowUps } : {},
18286
18628
  artifacts: paths
18287
18629
  },
18288
18630
  null,
@@ -18337,14 +18679,31 @@ ${message}
18337
18679
  console.log(
18338
18680
  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}`
18339
18681
  );
18682
+ await printUserFollowUps(paths.actionsPath);
18340
18683
  console.log(`Fix: viberaven fix \xB7 Details: ${paths.tasklistPath}`);
18341
18684
  console.log("");
18342
18685
  return critical > 0 ? 1 : 0;
18343
18686
  }
18344
- var SEVERITY_ICON, SEVERITY_ORDER3;
18687
+ async function readUserFollowUps(actionsPath) {
18688
+ try {
18689
+ return JSON.parse(await (0, import_promises29.readFile)(actionsPath, "utf8")).userFollowUps ?? [];
18690
+ } catch {
18691
+ return [];
18692
+ }
18693
+ }
18694
+ async function printUserFollowUps(actionsPath) {
18695
+ const followUps = await readUserFollowUps(actionsPath);
18696
+ if (followUps.length === 0) return;
18697
+ console.log("Outside the repo, still on you:");
18698
+ for (const followUp of followUps) {
18699
+ for (const step of followUp.userSteps) console.log(` - ${step} (${followUp.gapId ?? followUp.id})`);
18700
+ }
18701
+ }
18702
+ var import_promises29, SEVERITY_ICON, SEVERITY_ORDER3;
18345
18703
  var init_check = __esm({
18346
18704
  "src/commands/check.ts"() {
18347
18705
  "use strict";
18706
+ import_promises29 = require("node:fs/promises");
18348
18707
  init_runScan();
18349
18708
  init_artifacts2();
18350
18709
  init_statusLabels();
@@ -18793,8 +19152,8 @@ function isSqlError(error) {
18793
19152
  }
18794
19153
  function displayPath(projectDir, path) {
18795
19154
  for (const base of [projectDir, process.cwd()]) {
18796
- const rel = (0, import_node_path36.relative)(base, path);
18797
- if (rel && !rel.startsWith("..") && !(0, import_node_path36.isAbsolute)(rel)) return rel.split("\\").join("/");
19155
+ const rel = (0, import_node_path37.relative)(base, path);
19156
+ if (rel && !rel.startsWith("..") && !(0, import_node_path37.isAbsolute)(rel)) return rel.split("\\").join("/");
18798
19157
  }
18799
19158
  return path;
18800
19159
  }
@@ -19398,26 +19757,26 @@ function verdictFor(summary) {
19398
19757
  }
19399
19758
  async function listMigrations(dir) {
19400
19759
  try {
19401
- const entries = await (0, import_promises29.readdir)(dir, { withFileTypes: true });
19760
+ const entries = await (0, import_promises30.readdir)(dir, { withFileTypes: true });
19402
19761
  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);
19403
19762
  } catch {
19404
19763
  return [];
19405
19764
  }
19406
19765
  }
19407
19766
  async function runRlsTest(options) {
19408
- const projectDir = (0, import_node_path36.resolve)(options.projectDir);
19409
- const matrixPath = (0, import_node_path36.resolve)(projectDir, options.matrixPath ?? DEFAULT_MATRIX_FILE);
19410
- const migrationsDir = (0, import_node_path36.resolve)(projectDir, options.migrationsDir ?? (0, import_node_path36.join)("supabase", "migrations"));
19767
+ const projectDir = (0, import_node_path37.resolve)(options.projectDir);
19768
+ const matrixPath = (0, import_node_path37.resolve)(projectDir, options.matrixPath ?? DEFAULT_MATRIX_FILE);
19769
+ const migrationsDir = (0, import_node_path37.resolve)(projectDir, options.migrationsDir ?? (0, import_node_path37.join)("supabase", "migrations"));
19411
19770
  const timeouts = { ...DEFAULT_TIMEOUTS, ...options.timeouts };
19412
- if ((0, import_node_path36.basename)(matrixPath).toLowerCase().startsWith(".env")) {
19413
- 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.`);
19771
+ if ((0, import_node_path37.basename)(matrixPath).toLowerCase().startsWith(".env")) {
19772
+ 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.`);
19414
19773
  }
19415
19774
  const filesRead = [];
19416
19775
  let matrixText;
19417
19776
  try {
19418
- const info = await (0, import_promises29.stat)(matrixPath);
19777
+ const info = await (0, import_promises30.stat)(matrixPath);
19419
19778
  if (!info.isFile()) throw new Error("not a file");
19420
- matrixText = normalise(await (0, import_promises29.readFile)(matrixPath, "utf8"));
19779
+ matrixText = normalise(await (0, import_promises30.readFile)(matrixPath, "utf8"));
19421
19780
  } catch {
19422
19781
  throw new RlsInputError(
19423
19782
  `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.`
@@ -19428,8 +19787,8 @@ async function runRlsTest(options) {
19428
19787
  const names = await listMigrations(migrationsDir);
19429
19788
  const migrationFiles = [];
19430
19789
  for (const name of names) {
19431
- const sql = normalise(await (0, import_promises29.readFile)((0, import_node_path36.join)(migrationsDir, name), "utf8"));
19432
- filesRead.push(displayPath(projectDir, (0, import_node_path36.join)(migrationsDir, name)));
19790
+ const sql = normalise(await (0, import_promises30.readFile)((0, import_node_path37.join)(migrationsDir, name), "utf8"));
19791
+ filesRead.push(displayPath(projectDir, (0, import_node_path37.join)(migrationsDir, name)));
19433
19792
  migrationFiles.push({ name, sha256: sha256(sql), sql });
19434
19793
  }
19435
19794
  const migrationsHash = sha256(migrationFiles.map((f) => `${f.name}\0${f.sha256}`).join("\n"));
@@ -19629,15 +19988,15 @@ async function runTable(db, table2, badRoles, roleChecks) {
19629
19988
  cells
19630
19989
  };
19631
19990
  }
19632
- 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;
19991
+ 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;
19633
19992
  var init_rlsRunner = __esm({
19634
19993
  "src/rls/rlsRunner.ts"() {
19635
19994
  "use strict";
19636
19995
  import_node_child_process8 = require("node:child_process");
19637
19996
  import_node_crypto3 = require("node:crypto");
19638
- import_promises29 = require("node:fs/promises");
19997
+ import_promises30 = require("node:fs/promises");
19639
19998
  import_node_net = __toESM(require("node:net"));
19640
- import_node_path36 = require("node:path");
19999
+ import_node_path37 = require("node:path");
19641
20000
  import_node_util6 = require("node:util");
19642
20001
  init_safeGit();
19643
20002
  init_permissionsMatrix();
@@ -20465,7 +20824,7 @@ function buildMissionCards(artifact, providers) {
20465
20824
  };
20466
20825
  }
20467
20826
  function projectNameFromPath(pathValue) {
20468
- return (0, import_node_path37.basename)(pathValue.replace(/[\\/]$/, "")) || "current repo";
20827
+ return (0, import_node_path38.basename)(pathValue.replace(/[\\/]$/, "")) || "current repo";
20469
20828
  }
20470
20829
  function gapSearchText(gap) {
20471
20830
  return [
@@ -20656,7 +21015,7 @@ function mapProvider(template, artifact, projectName) {
20656
21015
  const gaps = providerGaps(artifact, template.id);
20657
21016
  const primaryGap2 = gaps[0];
20658
21017
  const state = providerState(gaps);
20659
- const launchGateClear = artifact.gaps.length === 0 && artifact.score >= 80;
21018
+ const launchGateClear = gateStatusForGaps(artifact.gaps) === "clear";
20660
21019
  const launchPath = template.items.map((item4, index) => ({
20661
21020
  ...item4,
20662
21021
  state: itemState(primaryGap2, index, item4, launchGateClear),
@@ -20865,8 +21224,9 @@ function mapArtifactToLocalUiState(artifact, presentProviderIds) {
20865
21224
  scannedAt: artifact.scannedAt
20866
21225
  },
20867
21226
  gate: {
20868
- status: artifact.gaps.length === 0 && artifact.score >= 80 ? "clear" : "not_clear",
20869
- label: artifact.gaps.length === 0 && artifact.score >= 80 ? "Ready" : "Gate not clear",
21227
+ // The same rule as gate-result.json, so the Studio never shows a different verdict.
21228
+ status: gateStatusForGaps(artifact.gaps),
21229
+ label: GATE_LABEL[gateStatusForGaps(artifact.gaps)],
20870
21230
  score: artifact.score,
20871
21231
  productionCorePercent: artifact.productionCorePercent
20872
21232
  },
@@ -20876,25 +21236,31 @@ function mapArtifactToLocalUiState(artifact, presentProviderIds) {
20876
21236
  selectedProviderId: selectedProvider.id,
20877
21237
  currentPrompt: selectedProvider.nextFix?.prompt ?? "",
20878
21238
  artifacts: {
20879
- tasklist: (0, import_node_path37.join)(".viberaven", "agent-tasklist.md"),
20880
- contextMap: (0, import_node_path37.join)(".viberaven", "context-map.json"),
20881
- gateResult: (0, import_node_path37.join)(".viberaven", "gate-result.json"),
20882
- lastScan: (0, import_node_path37.join)(".viberaven", "last-scan.json")
21239
+ tasklist: (0, import_node_path38.join)(".viberaven", "agent-tasklist.md"),
21240
+ contextMap: (0, import_node_path38.join)(".viberaven", "context-map.json"),
21241
+ gateResult: (0, import_node_path38.join)(".viberaven", "gate-result.json"),
21242
+ lastScan: (0, import_node_path38.join)(".viberaven", "last-scan.json")
20883
21243
  },
20884
21244
  empty: false
20885
21245
  };
20886
21246
  }
20887
- var import_node_path37, CONNECT_PROVIDER_PRIORITY, providerDashboardUrls, providerHints, PROVIDER_OWNED_GAP;
21247
+ var import_node_path38, GATE_LABEL, CONNECT_PROVIDER_PRIORITY, providerDashboardUrls, providerHints, PROVIDER_OWNED_GAP;
20888
21248
  var init_artifactAdapter = __esm({
20889
21249
  "src/local-ui/artifactAdapter.ts"() {
20890
21250
  "use strict";
20891
- import_node_path37 = require("node:path");
21251
+ import_node_path38 = require("node:path");
20892
21252
  init_providerLogos();
20893
21253
  init_buildTaskList();
20894
21254
  init_prompts();
20895
21255
  init_providerLaunchPaths();
20896
21256
  init_launchRunway();
20897
21257
  init_providerGrade();
21258
+ init_gateResult();
21259
+ GATE_LABEL = {
21260
+ clear: "No blockers in repo checks",
21261
+ warning: "Warnings to review",
21262
+ not_clear: "Gate not clear"
21263
+ };
20898
21264
  CONNECT_PROVIDER_PRIORITY = ["supabase", "vercel", "stripe"];
20899
21265
  providerDashboardUrls = {
20900
21266
  supabase: "https://supabase.com/dashboard/projects",
@@ -21191,18 +21557,18 @@ var init_planLimits = __esm({
21191
21557
  // src/hosted/credentials.ts
21192
21558
  function defaultConfigDir(env = process.env, platform = process.platform) {
21193
21559
  const override = env.VIBERAVEN_CONFIG_DIR?.trim();
21194
- if (override) return (0, import_node_path38.resolve)(override);
21560
+ if (override) return (0, import_node_path39.resolve)(override);
21195
21561
  if (platform === "win32") {
21196
- return (0, import_node_path38.join)(env.APPDATA?.trim() || (0, import_node_path38.join)((0, import_node_os2.homedir)(), "AppData", "Roaming"), "viberaven");
21562
+ return (0, import_node_path39.join)(env.APPDATA?.trim() || (0, import_node_path39.join)((0, import_node_os2.homedir)(), "AppData", "Roaming"), "viberaven");
21197
21563
  }
21198
21564
  if (platform === "darwin") {
21199
- return (0, import_node_path38.join)((0, import_node_os2.homedir)(), "Library", "Application Support", "viberaven");
21565
+ return (0, import_node_path39.join)((0, import_node_os2.homedir)(), "Library", "Application Support", "viberaven");
21200
21566
  }
21201
- return (0, import_node_path38.join)(env.XDG_CONFIG_HOME?.trim() || (0, import_node_path38.join)((0, import_node_os2.homedir)(), ".config"), "viberaven");
21567
+ return (0, import_node_path39.join)(env.XDG_CONFIG_HOME?.trim() || (0, import_node_path39.join)((0, import_node_os2.homedir)(), ".config"), "viberaven");
21202
21568
  }
21203
21569
  function isInside(child, parent) {
21204
- const rel = (0, import_node_path38.relative)((0, import_node_path38.resolve)(parent), (0, import_node_path38.resolve)(child));
21205
- return rel === "" || !rel.startsWith("..") && !(0, import_node_path38.isAbsolute)(rel);
21570
+ const rel = (0, import_node_path39.relative)((0, import_node_path39.resolve)(parent), (0, import_node_path39.resolve)(child));
21571
+ return rel === "" || !rel.startsWith("..") && !(0, import_node_path39.isAbsolute)(rel);
21206
21572
  }
21207
21573
  async function retryTransient(work) {
21208
21574
  for (let attempt = 1; ; attempt += 1) {
@@ -21215,15 +21581,15 @@ async function retryTransient(work) {
21215
21581
  }
21216
21582
  }
21217
21583
  }
21218
- 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;
21584
+ 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;
21219
21585
  var init_credentials = __esm({
21220
21586
  "src/hosted/credentials.ts"() {
21221
21587
  "use strict";
21222
21588
  import_node_child_process9 = require("node:child_process");
21223
21589
  import_node_crypto4 = require("node:crypto");
21224
- import_promises30 = require("node:fs/promises");
21590
+ import_promises31 = require("node:fs/promises");
21225
21591
  import_node_os2 = require("node:os");
21226
- import_node_path38 = require("node:path");
21592
+ import_node_path39 = require("node:path");
21227
21593
  CREDENTIALS_FILE_NAME = "credentials.json";
21228
21594
  CredentialLocationError = class extends Error {
21229
21595
  };
@@ -21250,9 +21616,9 @@ var init_credentials = __esm({
21250
21616
  constructor(configDir, workspacePath, options = {}) {
21251
21617
  this.configDir = configDir;
21252
21618
  this.workspacePath = workspacePath;
21253
- this.path = (0, import_node_path38.join)(configDir, CREDENTIALS_FILE_NAME);
21619
+ this.path = (0, import_node_path39.join)(configDir, CREDENTIALS_FILE_NAME);
21254
21620
  this.restrict = options.restrict !== void 0 ? options.restrict : process.platform === "win32" ? restrictWithIcacls : null;
21255
- 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 };
21621
+ 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 };
21256
21622
  }
21257
21623
  path;
21258
21624
  restrict;
@@ -21301,15 +21667,15 @@ var init_credentials = __esm({
21301
21667
  `Refusing to store the VibeRaven sign-in inside this project (${this.configDir}). Set VIBERAVEN_CONFIG_DIR to a folder outside the repo.`
21302
21668
  );
21303
21669
  }
21304
- await (0, import_promises30.mkdir)(this.configDir, { recursive: true, mode: 448 });
21670
+ await (0, import_promises31.mkdir)(this.configDir, { recursive: true, mode: 448 });
21305
21671
  const temp = `${this.path}.${(0, import_node_crypto4.randomUUID)()}.tmp`;
21306
- await (0, import_promises30.writeFile)(temp, `${JSON.stringify(body, null, 2)}
21672
+ await (0, import_promises31.writeFile)(temp, `${JSON.stringify(body, null, 2)}
21307
21673
  `, { encoding: "utf8", mode: 384 });
21308
- await (0, import_promises30.chmod)(temp, 384).catch(() => void 0);
21674
+ await (0, import_promises31.chmod)(temp, 384).catch(() => void 0);
21309
21675
  try {
21310
21676
  if (this.restrict) await this.restrict(temp);
21311
21677
  } catch (error) {
21312
- await (0, import_promises30.rm)(temp, { force: true });
21678
+ await (0, import_promises31.rm)(temp, { force: true });
21313
21679
  const code = error?.code;
21314
21680
  const toolMissing = code === "ENOENT";
21315
21681
  throw new CredentialPermissionError(
@@ -21320,17 +21686,17 @@ var init_credentials = __esm({
21320
21686
  if (guard) {
21321
21687
  const onDisk = await this.readFor(guard.apiBaseUrl);
21322
21688
  if (onDisk?.accessToken !== guard.accessToken || guard.isCurrent && !guard.isCurrent()) {
21323
- await (0, import_promises30.rm)(temp, { force: true });
21689
+ await (0, import_promises31.rm)(temp, { force: true });
21324
21690
  return false;
21325
21691
  }
21326
21692
  }
21327
21693
  try {
21328
21694
  await retryTransient(() => this.fs.rename(temp, this.path));
21329
21695
  } catch (error) {
21330
- await (0, import_promises30.rm)(temp, { force: true });
21696
+ await (0, import_promises31.rm)(temp, { force: true });
21331
21697
  throw error;
21332
21698
  }
21333
- await (0, import_promises30.chmod)(this.path, 384).catch(() => void 0);
21699
+ await (0, import_promises31.chmod)(this.path, 384).catch(() => void 0);
21334
21700
  return true;
21335
21701
  }
21336
21702
  /** Keeps the stored plan label in step with what the API last said. */
@@ -21699,7 +22065,7 @@ async function buildHostedCheckPayload(workspacePath) {
21699
22065
  throw new Error("Could not assemble the full check request.");
21700
22066
  }
21701
22067
  function finalizePayload(payload) {
21702
- const folder = (0, import_node_path39.basename)(payload.workspacePath.split("\\").join("/").replace(/\/+$/, "")) || "project";
22068
+ const folder = (0, import_node_path40.basename)(payload.workspacePath.split("\\").join("/").replace(/\/+$/, "")) || "project";
21703
22069
  const redacted = redactPayload({ ...payload, workspacePath: folder });
21704
22070
  return { payload: redacted.payload, redactions: redacted.count };
21705
22071
  }
@@ -21730,13 +22096,13 @@ function splitSections(prompt) {
21730
22096
  chars: section.length
21731
22097
  }));
21732
22098
  }
21733
- var import_node_path39, PayloadCaptured;
22099
+ var import_node_path40, PayloadCaptured;
21734
22100
  var init_payload = __esm({
21735
22101
  "src/hosted/payload.ts"() {
21736
22102
  "use strict";
21737
22103
  init_fileScanner();
21738
22104
  init_orchestrator();
21739
- import_node_path39 = require("node:path");
22105
+ import_node_path40 = require("node:path");
21740
22106
  init_config();
21741
22107
  init_redact();
21742
22108
  PayloadCaptured = class extends Error {
@@ -22202,10 +22568,10 @@ async function connectProviderMcp(cwd, providerId) {
22202
22568
  if (!recipe) {
22203
22569
  return { ok: false, message: `No known MCP server for ${providerId} yet. Add one manually to .mcp.json.` };
22204
22570
  }
22205
- const configPath = (0, import_node_path40.join)(cwd, ".mcp.json");
22571
+ const configPath = (0, import_node_path41.join)(cwd, ".mcp.json");
22206
22572
  let config = {};
22207
22573
  try {
22208
- const raw = await (0, import_promises31.readFile)(configPath, "utf8");
22574
+ const raw = await (0, import_promises32.readFile)(configPath, "utf8");
22209
22575
  const parsed = JSON.parse(raw.replace(/^\uFEFF/, ""));
22210
22576
  if (!isRecord5(parsed) || parsed.mcpServers !== void 0 && !isRecord5(parsed.mcpServers)) {
22211
22577
  return { ok: false, message: "Existing .mcp.json has an unsupported structure. It was left unchanged." };
@@ -22222,7 +22588,7 @@ async function connectProviderMcp(cwd, providerId) {
22222
22588
  }
22223
22589
  servers[providerId] = recipe.entry;
22224
22590
  const next = { ...config, mcpServers: servers };
22225
- await (0, import_promises31.writeFile)(configPath, `${JSON.stringify(next, null, 2)}
22591
+ await (0, import_promises32.writeFile)(configPath, `${JSON.stringify(next, null, 2)}
22226
22592
  `, "utf8");
22227
22593
  const suffix = recipe.note ? ` ${recipe.note}` : "";
22228
22594
  return { ok: true, message: `Added ${providerId} MCP server to .mcp.json.${suffix}`, path: ".mcp.json" };
@@ -22275,7 +22641,7 @@ async function defaultScanRunner(flags, positional) {
22275
22641
  }
22276
22642
  async function readPackageJson(cwd) {
22277
22643
  try {
22278
- const raw = await (0, import_promises31.readFile)((0, import_node_path40.join)(cwd, "package.json"), "utf8");
22644
+ const raw = await (0, import_promises32.readFile)((0, import_node_path41.join)(cwd, "package.json"), "utf8");
22279
22645
  const parsed = JSON.parse(raw.replace(/^\uFEFF/, ""));
22280
22646
  return isRecord5(parsed) ? parsed : {};
22281
22647
  } catch {
@@ -22298,7 +22664,7 @@ async function listProjectFiles(cwd, maxFiles = 1800) {
22298
22664
  if (files.length >= maxFiles) return;
22299
22665
  let entries;
22300
22666
  try {
22301
- entries = await (0, import_promises31.readdir)(dir, { withFileTypes: true });
22667
+ entries = await (0, import_promises32.readdir)(dir, { withFileTypes: true });
22302
22668
  } catch {
22303
22669
  return;
22304
22670
  }
@@ -22306,7 +22672,7 @@ async function listProjectFiles(cwd, maxFiles = 1800) {
22306
22672
  if (files.length >= maxFiles) return;
22307
22673
  const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
22308
22674
  if (entry.isDirectory()) {
22309
- if (!skipped.has(entry.name)) await walk((0, import_node_path40.join)(dir, entry.name), rel);
22675
+ if (!skipped.has(entry.name)) await walk((0, import_node_path41.join)(dir, entry.name), rel);
22310
22676
  continue;
22311
22677
  }
22312
22678
  if (entry.isFile()) files.push(rel.toLowerCase());
@@ -22325,7 +22691,7 @@ async function readEnvKeys(cwd) {
22325
22691
  const keys = /* @__PURE__ */ new Set();
22326
22692
  for (const name of [".env.example", ".env"]) {
22327
22693
  try {
22328
- const raw = await (0, import_promises31.readFile)((0, import_node_path40.join)(cwd, name), "utf8");
22694
+ const raw = await (0, import_promises32.readFile)((0, import_node_path41.join)(cwd, name), "utf8");
22329
22695
  for (const line of raw.split(/\r?\n/)) {
22330
22696
  const trimmed = line.trim();
22331
22697
  if (!trimmed || trimmed.startsWith("#")) continue;
@@ -22643,7 +23009,7 @@ async function codexModelOptions() {
22643
23009
  { id: "gpt-5.4-mini", label: "GPT-5.4 Mini" }
22644
23010
  ];
22645
23011
  try {
22646
- const raw = await (0, import_promises31.readFile)((0, import_node_path40.join)((0, import_node_os3.homedir)(), ".codex", "models_cache.json"), "utf8");
23012
+ const raw = await (0, import_promises32.readFile)((0, import_node_path41.join)((0, import_node_os3.homedir)(), ".codex", "models_cache.json"), "utf8");
22647
23013
  const parsed = JSON.parse(raw);
22648
23014
  const list = Array.isArray(parsed) ? parsed : parsed.models ?? [];
22649
23015
  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 }));
@@ -22846,19 +23212,19 @@ function isPlaceholderProjectName(value) {
22846
23212
  async function readMcpEvidence(cwd) {
22847
23213
  const candidates = MCP_CONFIG_PATHS.map((relativePath) => ({
22848
23214
  source: relativePath.replace(/\\/g, "/"),
22849
- path: (0, import_node_path40.resolve)(cwd, relativePath)
23215
+ path: (0, import_node_path41.resolve)(cwd, relativePath)
22850
23216
  }));
22851
23217
  const home = process.env.USERPROFILE || process.env.HOME;
22852
23218
  if (home) {
22853
23219
  candidates.push({
22854
23220
  source: "~/.codex/config.toml",
22855
- path: (0, import_node_path40.resolve)(home, ".codex", "config.toml")
23221
+ path: (0, import_node_path41.resolve)(home, ".codex", "config.toml")
22856
23222
  });
22857
23223
  }
22858
23224
  const evidence = [];
22859
23225
  for (const candidate of candidates) {
22860
23226
  try {
22861
- const text = await (0, import_promises31.readFile)(candidate.path, "utf8");
23227
+ const text = await (0, import_promises32.readFile)(candidate.path, "utf8");
22862
23228
  if (/mcp/i.test(text)) evidence.push({ source: candidate.source, text });
22863
23229
  } catch {
22864
23230
  }
@@ -22949,7 +23315,7 @@ async function enrichProjectMetadata(state, cwd) {
22949
23315
  async function readState(cwd) {
22950
23316
  let state;
22951
23317
  try {
22952
- const raw = await (0, import_promises31.readFile)((0, import_node_path40.join)(cwd, ".viberaven", "last-scan.json"), "utf8");
23318
+ const raw = await (0, import_promises32.readFile)((0, import_node_path41.join)(cwd, ".viberaven", "last-scan.json"), "utf8");
22953
23319
  const packageJson = await readPackageJson(cwd);
22954
23320
  const deps = dependencyNames(packageJson);
22955
23321
  const files = await listProjectFiles(cwd);
@@ -23216,7 +23582,7 @@ async function whereExe(command, cwd) {
23216
23582
  }
23217
23583
  function expandShimPath(value, base) {
23218
23584
  const relative8 = value.replace(/^%(?:dp0%\\+|~dp0\\*)/i, "");
23219
- return relative8 === value ? value : (0, import_node_path40.join)(base, ...relative8.split(/[\\/]+/));
23585
+ return relative8 === value ? value : (0, import_node_path41.join)(base, ...relative8.split(/[\\/]+/));
23220
23586
  }
23221
23587
  function findShimTarget(lines, pattern, accept = () => true) {
23222
23588
  for (const line of lines) {
@@ -23227,21 +23593,21 @@ function findShimTarget(lines, pattern, accept = () => true) {
23227
23593
  }
23228
23594
  async function resolveWindowsShimTarget(shimPath) {
23229
23595
  try {
23230
- const content = await (0, import_promises31.readFile)(shimPath, "utf8");
23231
- const base = (0, import_node_path40.dirname)(shimPath);
23596
+ const content = await (0, import_promises32.readFile)(shimPath, "utf8");
23597
+ const base = (0, import_node_path41.dirname)(shimPath);
23232
23598
  const lines = content.split(/\r?\n/);
23233
23599
  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("%"));
23234
23600
  const script = findShimTarget(lines, /"%(?:dp0%\\+|~dp0\\*)([^"]+\.js)"/i);
23235
23601
  if (script) {
23236
23602
  return {
23237
23603
  command: process.execPath,
23238
- args: [(0, import_node_path40.join)(base, ...script.match[1].split(/[\\/]+/)), ...trailingArgs(script.line, script.match.index + script.match[0].length)]
23604
+ args: [(0, import_node_path41.join)(base, ...script.match[1].split(/[\\/]+/)), ...trailingArgs(script.line, script.match.index + script.match[0].length)]
23239
23605
  };
23240
23606
  }
23241
- const binary = findShimTarget(lines, /"%(?:dp0%\\+|~dp0\\*)([^"]+\.exe)"/i, (target) => (0, import_node_path40.basename)(target).toLowerCase() !== "node.exe");
23607
+ const binary = findShimTarget(lines, /"%(?:dp0%\\+|~dp0\\*)([^"]+\.exe)"/i, (target) => (0, import_node_path41.basename)(target).toLowerCase() !== "node.exe");
23242
23608
  if (binary) {
23243
23609
  return {
23244
- command: (0, import_node_path40.join)(base, ...binary.match[1].split(/[\\/]+/)),
23610
+ command: (0, import_node_path41.join)(base, ...binary.match[1].split(/[\\/]+/)),
23245
23611
  args: trailingArgs(binary.line, binary.match.index + binary.match[0].length)
23246
23612
  };
23247
23613
  }
@@ -23366,8 +23732,8 @@ async function writeChatContextArtifact(cwd, request, prompt) {
23366
23732
  "",
23367
23733
  prompt
23368
23734
  ].filter((line) => typeof line === "string");
23369
- await (0, import_promises31.mkdir)((0, import_node_path40.join)(cwd, ".viberaven"), { recursive: true });
23370
- await (0, import_promises31.writeFile)((0, import_node_path40.join)(cwd, ".viberaven", "chat-context.md"), `${lines.join("\n")}
23735
+ await (0, import_promises32.mkdir)((0, import_node_path41.join)(cwd, ".viberaven"), { recursive: true });
23736
+ await (0, import_promises32.writeFile)((0, import_node_path41.join)(cwd, ".viberaven", "chat-context.md"), `${lines.join("\n")}
23371
23737
  `, "utf8");
23372
23738
  } catch {
23373
23739
  }
@@ -23387,13 +23753,13 @@ async function runAgentChat(cwd, request) {
23387
23753
  }
23388
23754
  const prompt = await buildAgentChatPrompt(request, cwd);
23389
23755
  await writeChatContextArtifact(cwd, request, prompt);
23390
- 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;
23391
- if (outputPath) await (0, import_promises31.mkdir)((0, import_node_path40.dirname)(outputPath), { recursive: true });
23756
+ 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;
23757
+ if (outputPath) await (0, import_promises32.mkdir)((0, import_node_path41.dirname)(outputPath), { recursive: true });
23392
23758
  const { command, args, stdin } = commandForAgentChat(request, prompt, cwd, outputPath);
23393
23759
  const result = await runAgentCommand(command, args, cwd, stdin, request.runId);
23394
23760
  if (outputPath) {
23395
23761
  try {
23396
- const finalMessage = (await (0, import_promises31.readFile)(outputPath, "utf8")).trim();
23762
+ const finalMessage = (await (0, import_promises32.readFile)(outputPath, "utf8")).trim();
23397
23763
  if (finalMessage) return { ...result, output: finalMessage };
23398
23764
  } catch {
23399
23765
  }
@@ -23405,8 +23771,8 @@ function firstGapId(state) {
23405
23771
  return state.providers.find((provider2) => provider2.nextFix?.id)?.nextFix?.id;
23406
23772
  }
23407
23773
  async function runLocalAction(cwd, intent) {
23408
- const artifactDir = (0, import_node_path40.join)(cwd, ".viberaven");
23409
- await (0, import_promises31.mkdir)(artifactDir, { recursive: true });
23774
+ const artifactDir = (0, import_node_path41.join)(cwd, ".viberaven");
23775
+ await (0, import_promises32.mkdir)(artifactDir, { recursive: true });
23410
23776
  if (intent === "preview_rehearsal") {
23411
23777
  const state2 = await readState(cwd);
23412
23778
  const topProvider = state2.providers[0];
@@ -23447,7 +23813,7 @@ async function runLocalAction(cwd, intent) {
23447
23813
  proof,
23448
23814
  ""
23449
23815
  ].join("\n");
23450
- await (0, import_promises31.writeFile)((0, import_node_path40.join)(artifactDir, "preview-rehearsal.md"), markdown, "utf8");
23816
+ await (0, import_promises32.writeFile)((0, import_node_path41.join)(artifactDir, "preview-rehearsal.md"), markdown, "utf8");
23451
23817
  return {
23452
23818
  title: "Preview rehearsal ready",
23453
23819
  summary: `${projectName}: ${gateLabel} with ${state2.providers.length} provider beat${state2.providers.length === 1 ? "" : "s"}`,
@@ -23457,7 +23823,7 @@ async function runLocalAction(cwd, intent) {
23457
23823
  if (intent === "audit_vercel_supabase") {
23458
23824
  const auditInput = await collectVercelSupabaseAuditInput(cwd);
23459
23825
  const result2 = buildVercelSupabaseAudit(auditInput);
23460
- await (0, import_promises31.writeFile)((0, import_node_path40.join)(artifactDir, "audit-vercel-supabase.txt"), `${renderVercelSupabaseAudit(result2)}
23826
+ await (0, import_promises32.writeFile)((0, import_node_path41.join)(artifactDir, "audit-vercel-supabase.txt"), `${renderVercelSupabaseAudit(result2)}
23461
23827
  `, "utf8");
23462
23828
  return {
23463
23829
  title: result2.status === "pass" ? "Audit passed" : "Audit needs work",
@@ -23485,7 +23851,7 @@ async function runLocalAction(cwd, intent) {
23485
23851
  const mode = intent === "heal_prompt" ? "prompt" : intent === "heal_apply" ? "apply" : "plan";
23486
23852
  const result = await runHealCommand({ cwd, mode, gapId, yes: mode === "apply", noVerify: true });
23487
23853
  if (mode === "apply") {
23488
- await (0, import_promises31.writeFile)((0, import_node_path40.join)(artifactDir, "heal-apply-result.json"), `${JSON.stringify(result, null, 2)}
23854
+ await (0, import_promises32.writeFile)((0, import_node_path41.join)(artifactDir, "heal-apply-result.json"), `${JSON.stringify(result, null, 2)}
23489
23855
  `, "utf8");
23490
23856
  }
23491
23857
  return {
@@ -23495,14 +23861,14 @@ async function runLocalAction(cwd, intent) {
23495
23861
  };
23496
23862
  }
23497
23863
  async function sendArtifact(cwd, rel, response, contentType = "text/plain; charset=utf-8") {
23498
- const absolute = (0, import_node_path40.resolve)(cwd, rel);
23499
- const root = (0, import_node_path40.resolve)(cwd);
23864
+ const absolute = (0, import_node_path41.resolve)(cwd, rel);
23865
+ const root = (0, import_node_path41.resolve)(cwd);
23500
23866
  if (!absolute.startsWith(root)) {
23501
23867
  sendText(response, 403, "Forbidden");
23502
23868
  return;
23503
23869
  }
23504
23870
  try {
23505
- const text = await (0, import_promises31.readFile)(absolute, "utf8");
23871
+ const text = await (0, import_promises32.readFile)(absolute, "utf8");
23506
23872
  response.writeHead(200, { "content-type": contentType });
23507
23873
  response.end(text);
23508
23874
  } catch {
@@ -23511,7 +23877,7 @@ async function sendArtifact(cwd, rel, response, contentType = "text/plain; chars
23511
23877
  }
23512
23878
  async function sendReportArtifact(cwd, response) {
23513
23879
  try {
23514
- const text = await (0, import_promises31.readFile)((0, import_node_path40.join)(cwd, ".viberaven", "report.html"), "utf8");
23880
+ const text = await (0, import_promises32.readFile)((0, import_node_path41.join)(cwd, ".viberaven", "report.html"), "utf8");
23515
23881
  response.writeHead(200, { "content-type": "text/html; charset=utf-8" });
23516
23882
  response.end(text);
23517
23883
  return;
@@ -23638,7 +24004,7 @@ async function sendReportArtifact(cwd, response) {
23638
24004
  }
23639
24005
  async function sendExtensionIcon(cwd, response) {
23640
24006
  try {
23641
- const body = await (0, import_promises31.readFile)((0, import_node_path40.join)(cwd, "media", "extension-icon.png"));
24007
+ const body = await (0, import_promises32.readFile)((0, import_node_path41.join)(cwd, "media", "extension-icon.png"));
23642
24008
  sendBinary(response, 200, "image/png", body);
23643
24009
  } catch {
23644
24010
  sendText(response, 404, "Icon not found");
@@ -23651,15 +24017,15 @@ async function sendMangaAsset(pathname, response) {
23651
24017
  return;
23652
24018
  }
23653
24019
  const candidates = [
23654
- (0, import_node_path40.join)(__dirname, "manga", name),
24020
+ (0, import_node_path41.join)(__dirname, "manga", name),
23655
24021
  // installed + dev: <pkg>/dist/manga
23656
- (0, import_node_path40.join)(__dirname, "..", "assets", "manga", name),
24022
+ (0, import_node_path41.join)(__dirname, "..", "assets", "manga", name),
23657
24023
  // fallback: <pkg>/assets/manga (source)
23658
- (0, import_node_path40.join)(__dirname, "..", "..", "assets", "manga", name)
24024
+ (0, import_node_path41.join)(__dirname, "..", "..", "assets", "manga", name)
23659
24025
  ];
23660
24026
  for (const candidate of candidates) {
23661
24027
  try {
23662
- const body = await (0, import_promises31.readFile)(candidate);
24028
+ const body = await (0, import_promises32.readFile)(candidate);
23663
24029
  response.setHeader("cache-control", "no-cache");
23664
24030
  sendBinary(response, 200, contentTypeForAsset(candidate), body);
23665
24031
  return;
@@ -23669,7 +24035,7 @@ async function sendMangaAsset(pathname, response) {
23669
24035
  sendText(response, 404, "Asset not found");
23670
24036
  }
23671
24037
  function contentTypeForAsset(pathname) {
23672
- switch ((0, import_node_path40.extname)(pathname).toLowerCase()) {
24038
+ switch ((0, import_node_path41.extname)(pathname).toLowerCase()) {
23673
24039
  case ".svg":
23674
24040
  return "image/svg+xml; charset=utf-8";
23675
24041
  case ".png":
@@ -23686,14 +24052,14 @@ function contentTypeForAsset(pathname) {
23686
24052
  }
23687
24053
  async function sendBundledReportAsset(pathname, response) {
23688
24054
  const rel = decodeURIComponent(pathname.replace(/^\/report\//, ""));
23689
- const assetRoot = (0, import_node_path40.resolve)(getBundledReportAssetsDir());
23690
- const absolute = (0, import_node_path40.resolve)(assetRoot, rel);
24055
+ const assetRoot = (0, import_node_path41.resolve)(getBundledReportAssetsDir());
24056
+ const absolute = (0, import_node_path41.resolve)(assetRoot, rel);
23691
24057
  if (!absolute.startsWith(assetRoot)) {
23692
24058
  sendText(response, 403, "Forbidden");
23693
24059
  return;
23694
24060
  }
23695
24061
  try {
23696
- const body = await (0, import_promises31.readFile)(absolute);
24062
+ const body = await (0, import_promises32.readFile)(absolute);
23697
24063
  sendBinary(response, 200, contentTypeForAsset(absolute), body);
23698
24064
  } catch {
23699
24065
  sendText(response, 404, "Asset not found");
@@ -23715,18 +24081,18 @@ async function bundleStackClient(entryPath) {
23715
24081
  return output.text;
23716
24082
  }
23717
24083
  async function sendStackAppJs(response) {
23718
- const distPath = (0, import_node_path40.join)(__dirname, "stack", "app.js");
24084
+ const distPath = (0, import_node_path41.join)(__dirname, "stack", "app.js");
23719
24085
  try {
23720
- const body = await (0, import_promises31.readFile)(distPath);
24086
+ const body = await (0, import_promises32.readFile)(distPath);
23721
24087
  response.writeHead(200, { "content-type": "application/javascript; charset=utf-8", "cache-control": "no-store" });
23722
24088
  response.end(body);
23723
24089
  return;
23724
24090
  } catch {
23725
24091
  }
23726
- const entryPath = (0, import_node_path40.join)(__dirname, "stack", "main.ts");
24092
+ const entryPath = (0, import_node_path41.join)(__dirname, "stack", "main.ts");
23727
24093
  let mtimeMs;
23728
24094
  try {
23729
- mtimeMs = (await (0, import_promises31.stat)(entryPath)).mtimeMs;
24095
+ mtimeMs = (await (0, import_promises32.stat)(entryPath)).mtimeMs;
23730
24096
  } catch {
23731
24097
  sendText(response, 404, "Stack client entry not found: src/local-ui/stack/main.ts is missing.");
23732
24098
  return;
@@ -23745,7 +24111,7 @@ async function sendStackAppJs(response) {
23745
24111
  }
23746
24112
  async function sendStackAppCss(response) {
23747
24113
  try {
23748
- const body = await (0, import_promises31.readFile)((0, import_node_path40.join)(__dirname, "stack", "stack.css"));
24114
+ const body = await (0, import_promises32.readFile)((0, import_node_path41.join)(__dirname, "stack", "stack.css"));
23749
24115
  response.writeHead(200, { "content-type": "text/css; charset=utf-8", "cache-control": "no-store" });
23750
24116
  response.end(body);
23751
24117
  } catch {
@@ -23754,7 +24120,7 @@ async function sendStackAppCss(response) {
23754
24120
  }
23755
24121
  async function readProviderVerifyLedger(cwd) {
23756
24122
  try {
23757
- const raw = await (0, import_promises31.readFile)((0, import_node_path40.join)(cwd, ".viberaven", "provider-verify.json"), "utf8");
24123
+ const raw = await (0, import_promises32.readFile)((0, import_node_path41.join)(cwd, ".viberaven", "provider-verify.json"), "utf8");
23758
24124
  const parsed = JSON.parse(raw.replace(/^\uFEFF/, ""));
23759
24125
  return isRecord5(parsed) ? parsed : {};
23760
24126
  } catch {
@@ -23764,8 +24130,8 @@ async function readProviderVerifyLedger(cwd) {
23764
24130
  async function writeProviderVerifyEntry(cwd, providerId, entry) {
23765
24131
  const ledger = await readProviderVerifyLedger(cwd);
23766
24132
  ledger[providerId] = entry;
23767
- await (0, import_promises31.mkdir)((0, import_node_path40.join)(cwd, ".viberaven"), { recursive: true });
23768
- await (0, import_promises31.writeFile)((0, import_node_path40.join)(cwd, ".viberaven", "provider-verify.json"), `${JSON.stringify(ledger, null, 2)}
24133
+ await (0, import_promises32.mkdir)((0, import_node_path41.join)(cwd, ".viberaven"), { recursive: true });
24134
+ await (0, import_promises32.writeFile)((0, import_node_path41.join)(cwd, ".viberaven", "provider-verify.json"), `${JSON.stringify(ledger, null, 2)}
23769
24135
  `, "utf8");
23770
24136
  }
23771
24137
  async function handleRequest(request, response, options) {
@@ -23838,13 +24204,13 @@ async function handleRequest(request, response, options) {
23838
24204
  return;
23839
24205
  }
23840
24206
  if (request.method === "GET" && url.pathname === "/api/architecture-map") {
23841
- const mapPath = (0, import_node_path40.join)(options.cwd, ".viberaven", "architecture-map.md");
23842
- const markdown = await (0, import_promises31.readFile)(mapPath, "utf8").catch(() => null);
24207
+ const mapPath = (0, import_node_path41.join)(options.cwd, ".viberaven", "architecture-map.md");
24208
+ const markdown = await (0, import_promises32.readFile)(mapPath, "utf8").catch(() => null);
23843
24209
  if (markdown === null) {
23844
24210
  sendJsonStatus(response, 404, { error: "No architecture map yet. Run a scan first (it is written on every scan)." });
23845
24211
  return;
23846
24212
  }
23847
- const dataRaw = await (0, import_promises31.readFile)((0, import_node_path40.join)(options.cwd, ".viberaven", "architecture-map.json"), "utf8").catch(() => null);
24213
+ const dataRaw = await (0, import_promises32.readFile)((0, import_node_path41.join)(options.cwd, ".viberaven", "architecture-map.json"), "utf8").catch(() => null);
23848
24214
  let data = null;
23849
24215
  if (dataRaw) {
23850
24216
  try {
@@ -23853,7 +24219,7 @@ async function handleRequest(request, response, options) {
23853
24219
  data = null;
23854
24220
  }
23855
24221
  }
23856
- const contextRaw = await (0, import_promises31.readFile)((0, import_node_path40.join)(options.cwd, ".viberaven", "context-map.json"), "utf8").catch(() => null);
24222
+ const contextRaw = await (0, import_promises32.readFile)((0, import_node_path41.join)(options.cwd, ".viberaven", "context-map.json"), "utf8").catch(() => null);
23857
24223
  let gaps = [];
23858
24224
  if (contextRaw) {
23859
24225
  try {
@@ -24119,35 +24485,35 @@ async function handleRequest(request, response, options) {
24119
24485
  return;
24120
24486
  }
24121
24487
  if (request.method === "GET" && url.pathname === "/api/artifacts/tasklist") {
24122
- await sendArtifact(options.cwd, (0, import_node_path40.join)(".viberaven", "agent-tasklist.md"), response);
24488
+ await sendArtifact(options.cwd, (0, import_node_path41.join)(".viberaven", "agent-tasklist.md"), response);
24123
24489
  return;
24124
24490
  }
24125
24491
  if (request.method === "GET" && url.pathname === "/api/artifacts/playbook") {
24126
- await sendArtifact(options.cwd, (0, import_node_path40.join)(".viberaven", "launch-playbook.md"), response);
24492
+ await sendArtifact(options.cwd, (0, import_node_path41.join)(".viberaven", "launch-playbook.md"), response);
24127
24493
  return;
24128
24494
  }
24129
24495
  if (request.method === "GET" && url.pathname === "/api/artifacts/audit") {
24130
- await sendArtifact(options.cwd, (0, import_node_path40.join)(".viberaven", "audit-vercel-supabase.txt"), response);
24496
+ await sendArtifact(options.cwd, (0, import_node_path41.join)(".viberaven", "audit-vercel-supabase.txt"), response);
24131
24497
  return;
24132
24498
  }
24133
24499
  if (request.method === "GET" && url.pathname === "/api/artifacts/cleanup") {
24134
- await sendArtifact(options.cwd, (0, import_node_path40.join)(".viberaven", "context-cleanup.md"), response);
24500
+ await sendArtifact(options.cwd, (0, import_node_path41.join)(".viberaven", "context-cleanup.md"), response);
24135
24501
  return;
24136
24502
  }
24137
24503
  if (request.method === "GET" && url.pathname === "/api/artifacts/preview") {
24138
- await sendArtifact(options.cwd, (0, import_node_path40.join)(".viberaven", "preview-rehearsal.md"), response);
24504
+ await sendArtifact(options.cwd, (0, import_node_path41.join)(".viberaven", "preview-rehearsal.md"), response);
24139
24505
  return;
24140
24506
  }
24141
24507
  if (request.method === "GET" && url.pathname === "/api/artifacts/heal-plan") {
24142
- await sendArtifact(options.cwd, (0, import_node_path40.join)(".viberaven", "heal-plan.md"), response);
24508
+ await sendArtifact(options.cwd, (0, import_node_path41.join)(".viberaven", "heal-plan.md"), response);
24143
24509
  return;
24144
24510
  }
24145
24511
  if (request.method === "GET" && url.pathname === "/api/artifacts/heal-prompt") {
24146
- await sendArtifact(options.cwd, (0, import_node_path40.join)(".viberaven", "heal-prompt.md"), response);
24512
+ await sendArtifact(options.cwd, (0, import_node_path41.join)(".viberaven", "heal-prompt.md"), response);
24147
24513
  return;
24148
24514
  }
24149
24515
  if (request.method === "GET" && url.pathname === "/api/artifacts/heal-apply") {
24150
- await sendArtifact(options.cwd, (0, import_node_path40.join)(".viberaven", "heal-apply-result.json"), response, "application/json; charset=utf-8");
24516
+ await sendArtifact(options.cwd, (0, import_node_path41.join)(".viberaven", "heal-apply-result.json"), response, "application/json; charset=utf-8");
24151
24517
  return;
24152
24518
  }
24153
24519
  if (request.method === "GET" && url.pathname === "/api/artifacts/report") {
@@ -24173,12 +24539,12 @@ async function handleRequest(request, response, options) {
24173
24539
  }
24174
24540
  if (request.method === "GET" && url.pathname === "/vendor/anime.umd.min.js") {
24175
24541
  for (const candidate of [
24176
- (0, import_node_path40.join)(__dirname, "vendor", "anime.umd.min.js"),
24177
- (0, import_node_path40.join)(__dirname, "..", "assets", "vendor", "anime.umd.min.js"),
24178
- (0, import_node_path40.join)(__dirname, "..", "..", "assets", "vendor", "anime.umd.min.js")
24542
+ (0, import_node_path41.join)(__dirname, "vendor", "anime.umd.min.js"),
24543
+ (0, import_node_path41.join)(__dirname, "..", "assets", "vendor", "anime.umd.min.js"),
24544
+ (0, import_node_path41.join)(__dirname, "..", "..", "assets", "vendor", "anime.umd.min.js")
24179
24545
  ]) {
24180
24546
  try {
24181
- const body = await (0, import_promises31.readFile)(candidate);
24547
+ const body = await (0, import_promises32.readFile)(candidate);
24182
24548
  response.setHeader("cache-control", "public, max-age=604800, immutable");
24183
24549
  sendBinary(response, 200, "text/javascript; charset=utf-8", body);
24184
24550
  return;
@@ -24395,7 +24761,7 @@ function listen(server, startPort) {
24395
24761
  });
24396
24762
  }
24397
24763
  async function startLocalUiServer(options) {
24398
- const cwd = (0, import_node_path40.resolve)(options.cwd);
24764
+ const cwd = (0, import_node_path41.resolve)(options.cwd);
24399
24765
  const scanRunner = options.scanRunner ?? defaultScanRunner;
24400
24766
  const autoScan = { state: "idle" };
24401
24767
  const fullCheck = new FullCheckService(cwd, options.fullCheck);
@@ -24412,7 +24778,7 @@ async function startLocalUiServer(options) {
24412
24778
  });
24413
24779
  const port = await listen(server, options.port ?? 4177);
24414
24780
  guard.port = port;
24415
- const hasArtifact = await (0, import_promises31.readFile)((0, import_node_path40.join)(cwd, ".viberaven", "last-scan.json"), "utf8").then(
24781
+ const hasArtifact = await (0, import_promises32.readFile)((0, import_node_path41.join)(cwd, ".viberaven", "last-scan.json"), "utf8").then(
24416
24782
  () => true,
24417
24783
  () => false
24418
24784
  );
@@ -24451,16 +24817,16 @@ async function waitForServerShutdown() {
24451
24817
  process.once("SIGTERM", done);
24452
24818
  });
24453
24819
  }
24454
- 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;
24820
+ 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;
24455
24821
  var init_server = __esm({
24456
24822
  "src/local-ui/server.ts"() {
24457
24823
  "use strict";
24458
24824
  import_node_http = require("node:http");
24459
24825
  import_node_child_process10 = require("node:child_process");
24460
24826
  import_node_crypto6 = require("node:crypto");
24461
- import_promises31 = require("node:fs/promises");
24827
+ import_promises32 = require("node:fs/promises");
24462
24828
  import_node_os3 = require("node:os");
24463
- import_node_path40 = require("node:path");
24829
+ import_node_path41 = require("node:path");
24464
24830
  init_initGithub();
24465
24831
  init_cleanPlan();
24466
24832
  init_audit();
@@ -24502,8 +24868,8 @@ var init_server = __esm({
24502
24868
  MCP_CONFIG_PATHS = [
24503
24869
  ".mcp.json",
24504
24870
  "mcp.json",
24505
- (0, import_node_path40.join)(".cursor", "mcp.json"),
24506
- (0, import_node_path40.join)(".vscode", "mcp.json")
24871
+ (0, import_node_path41.join)(".cursor", "mcp.json"),
24872
+ (0, import_node_path41.join)(".vscode", "mcp.json")
24507
24873
  ];
24508
24874
  MCP_PROVIDER_NEEDLES = {
24509
24875
  supabase: ["supabase", "@supabase/mcp", "mcp-server-supabase"],
@@ -24585,6 +24951,7 @@ function buildNextActionBlock(tasks, loopState) {
24585
24951
  mcpTool: repoCodeTask.mcpTool,
24586
24952
  mcpArgs: repoCodeTask.mcpArgs,
24587
24953
  fallbackCommand: repoCodeTask.mcpArgs ? `npx -y viberaven --heal --apply --gap ${repoCodeTask.gapId} --yes` : void 0,
24954
+ ...whereAndAfter(repoCodeTask),
24588
24955
  requiresUserAction: false
24589
24956
  };
24590
24957
  }
@@ -24598,6 +24965,7 @@ function buildNextActionBlock(tasks, loopState) {
24598
24965
  mcpTool: userTask.mcpTool,
24599
24966
  mcpArgs: userTask.mcpArgs,
24600
24967
  fallbackCommand: userTask.mcpArgs ? `npx -y viberaven --heal --apply --gap ${userTask.gapId} --yes` : void 0,
24968
+ ...whereAndAfter(userTask),
24601
24969
  requiresUserAction: true
24602
24970
  };
24603
24971
  }
@@ -24613,10 +24981,17 @@ function buildNextActionBlock(tasks, loopState) {
24613
24981
  return {
24614
24982
  ...base,
24615
24983
  type: "done",
24616
- title: "All gaps resolved \u2014 production gate is clear",
24984
+ title: "No repo-code or provider tasks left; gate-result.json has the verdict",
24617
24985
  requiresUserAction: false
24618
24986
  };
24619
24987
  }
24988
+ function whereAndAfter(task) {
24989
+ return {
24990
+ ...task.file ? { file: task.file } : {},
24991
+ ...task.line !== void 0 ? { line: task.line } : {},
24992
+ ...task.userSteps?.length ? { userSteps: [...task.userSteps] } : {}
24993
+ };
24994
+ }
24620
24995
  function resolveStallReason(tasks) {
24621
24996
  if (tasks.length === 0) return "no-recipes";
24622
24997
  const allProviderAction = tasks.every((t) => t.fixType === "provider-action");
@@ -24674,9 +25049,9 @@ var init_nextActionBlock = __esm({
24674
25049
  function defaultMcpConfigPaths() {
24675
25050
  const home = (0, import_node_os4.homedir)();
24676
25051
  return [
24677
- (0, import_node_path41.join)(home, ".config", "claude", "claude_desktop_config.json"),
24678
- (0, import_node_path41.join)(home, ".cursor", "mcp.json"),
24679
- (0, import_node_path41.join)(home, ".gemini", "antigravity", "mcp_config.json")
25052
+ (0, import_node_path42.join)(home, ".config", "claude", "claude_desktop_config.json"),
25053
+ (0, import_node_path42.join)(home, ".cursor", "mcp.json"),
25054
+ (0, import_node_path42.join)(home, ".gemini", "antigravity", "mcp_config.json")
24680
25055
  ];
24681
25056
  }
24682
25057
  function resolveConfigPaths() {
@@ -24705,11 +25080,11 @@ function findServerEntry(servers, provider2) {
24705
25080
  function findProviderMcpConfig(provider2, configPaths) {
24706
25081
  const paths = configPaths ?? resolveConfigPaths();
24707
25082
  for (const path of paths) {
24708
- if (!(0, import_node_fs15.existsSync)(path)) {
25083
+ if (!(0, import_node_fs16.existsSync)(path)) {
24709
25084
  continue;
24710
25085
  }
24711
25086
  try {
24712
- const raw = JSON.parse((0, import_node_fs15.readFileSync)(path, "utf8"));
25087
+ const raw = JSON.parse((0, import_node_fs16.readFileSync)(path, "utf8"));
24713
25088
  const servers = parseMcpServers(raw);
24714
25089
  if (!servers) {
24715
25090
  continue;
@@ -24753,13 +25128,13 @@ async function verifyProviderGap(options) {
24753
25128
  fallbackCommand: FALLBACK_COMMAND
24754
25129
  };
24755
25130
  }
24756
- var import_node_fs15, import_node_os4, import_node_path41, FALLBACK_COMMAND, SUPPORTED_PROVIDERS, configPathsOverride;
25131
+ var import_node_fs16, import_node_os4, import_node_path42, FALLBACK_COMMAND, SUPPORTED_PROVIDERS, configPathsOverride;
24757
25132
  var init_providerMcpBridge = __esm({
24758
25133
  "src/providerMcpBridge.ts"() {
24759
25134
  "use strict";
24760
- import_node_fs15 = require("node:fs");
25135
+ import_node_fs16 = require("node:fs");
24761
25136
  import_node_os4 = require("node:os");
24762
- import_node_path41 = require("node:path");
25137
+ import_node_path42 = require("node:path");
24763
25138
  FALLBACK_COMMAND = "npx -y viberaven audit --vercel-supabase --json";
24764
25139
  SUPPORTED_PROVIDERS = /* @__PURE__ */ new Set(["supabase", "vercel"]);
24765
25140
  }
@@ -24982,7 +25357,7 @@ async function cmdProviderVerify(flags, positional) {
24982
25357
  return result.verified ? 0 : 1;
24983
25358
  }
24984
25359
  async function cmdStatus(flags, positional) {
24985
- const startDir = positional[0] ? (0, import_node_path42.resolve)(process.cwd(), positional[0]) : process.cwd();
25360
+ const startDir = positional[0] ? (0, import_node_path43.resolve)(process.cwd(), positional[0]) : process.cwd();
24986
25361
  let artifact;
24987
25362
  try {
24988
25363
  artifact = await loadLastArtifact(startDir);
@@ -25021,7 +25396,7 @@ async function cmdStatus(flags, positional) {
25021
25396
  return 0;
25022
25397
  }
25023
25398
  async function runScanCommand(flags, positional, options) {
25024
- const workspacePath = positional[0] ? (0, import_node_path42.resolve)(process.cwd(), positional[0]) : await resolveWorkspaceRoot(process.cwd());
25399
+ const workspacePath = positional[0] ? (0, import_node_path43.resolve)(process.cwd(), positional[0]) : await resolveWorkspaceRoot(process.cwd());
25025
25400
  if (!options?.deferMachineOutput) {
25026
25401
  console.log(`Scanning ${workspacePath}\u2026 (local, offline)`);
25027
25402
  }
@@ -25045,15 +25420,27 @@ async function runScanCommand(flags, positional, options) {
25045
25420
  const updatedState = resetBatch(loopState, openGapCount);
25046
25421
  const tasks = buildTaskList(artifact);
25047
25422
  const block = buildNextActionBlock(tasks, updatedState);
25048
- printNextActionBlock(block);
25049
- printProviderActionBlock(tasks);
25423
+ let manifest;
25050
25424
  try {
25051
- const manifest = JSON.parse(await (0, import_promises32.readFile)(paths.actionsPath, "utf8"));
25052
- console.log(renderActionSurface(manifest, { limit: 5 }).trimEnd());
25425
+ manifest = JSON.parse(await (0, import_promises33.readFile)(paths.actionsPath, "utf8"));
25053
25426
  } catch (error) {
25054
25427
  console.warn(error instanceof Error ? error.message : String(error));
25055
25428
  }
25429
+ const followUps = manifest?.userFollowUps ?? [];
25430
+ if (followUps.length > 0) block.userFollowUps = followUps;
25431
+ printNextActionBlock(block);
25432
+ printProviderActionBlock(tasks);
25433
+ if (manifest) {
25434
+ console.log(renderActionSurface(manifest, { limit: 5, followUps: false }).trimEnd());
25435
+ }
25056
25436
  await saveLoopState(workspacePath, updatedState);
25437
+ if (options?.markFollowUpsShown && followUps.length > 0) {
25438
+ try {
25439
+ await markUserFollowUpsShown(workspacePath, followUps.map((followUp) => followUp.id));
25440
+ } catch (error) {
25441
+ 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.`);
25442
+ }
25443
+ }
25057
25444
  }
25058
25445
  if (flags.open) {
25059
25446
  try {
@@ -25065,7 +25452,7 @@ async function runScanCommand(flags, positional, options) {
25065
25452
  return { exitCode: 0, artifacts: paths };
25066
25453
  }
25067
25454
  async function cmdReport(flags, positional) {
25068
- const startDir = positional[0] ? (0, import_node_path42.resolve)(process.cwd(), positional[0]) : process.cwd();
25455
+ const startDir = positional[0] ? (0, import_node_path43.resolve)(process.cwd(), positional[0]) : process.cwd();
25069
25456
  try {
25070
25457
  const paths = await refreshReportFromDisk(startDir);
25071
25458
  console.log(`Report refreshed: ${paths.reportPath}`);
@@ -25087,7 +25474,7 @@ async function cmdReport(flags, positional) {
25087
25474
  }
25088
25475
  }
25089
25476
  async function cmdPrompt(flags, positional) {
25090
- const startDir = positional[0] ? (0, import_node_path42.resolve)(process.cwd(), positional[0]) : process.cwd();
25477
+ const startDir = positional[0] ? (0, import_node_path43.resolve)(process.cwd(), positional[0]) : process.cwd();
25091
25478
  let artifact;
25092
25479
  try {
25093
25480
  artifact = await loadLastArtifact(startDir);
@@ -25184,7 +25571,7 @@ async function main() {
25184
25571
  const wantsJsonl = hasFlag(flags, "jsonl");
25185
25572
  const wantsStrict = hasFlag(flags, "strict");
25186
25573
  if (flags.condense) {
25187
- const cwd = positional[0] ? (0, import_node_path42.resolve)(process.cwd(), positional[0]) : process.cwd();
25574
+ const cwd = positional[0] ? (0, import_node_path43.resolve)(process.cwd(), positional[0]) : process.cwd();
25188
25575
  const result = await runCondenseCommand({ cwd });
25189
25576
  console.log(`VibeRaven context map refreshed: ${result.contextMapPath}`);
25190
25577
  return 0;
@@ -25204,18 +25591,18 @@ async function main() {
25204
25591
  }
25205
25592
  if (!command && flags.verify === true && typeof flags.action === "string") {
25206
25593
  return runVerifyActionCommand({
25207
- cwd: positional[0] ? (0, import_node_path42.resolve)(process.cwd(), positional[0]) : process.cwd(),
25594
+ cwd: positional[0] ? (0, import_node_path43.resolve)(process.cwd(), positional[0]) : process.cwd(),
25208
25595
  actionId: flags.action
25209
25596
  });
25210
25597
  }
25211
25598
  if (!command && (isAgentMode || flags.verify === true || wantsJson || wantsJsonl || wantsStrict)) {
25212
25599
  const deferMachineOutput = wantsJson || wantsJsonl;
25213
- const scanResult = await runScanCommand(flags, positional, { deferMachineOutput });
25600
+ const scanResult = await runScanCommand(flags, positional, { deferMachineOutput, markFollowUpsShown: true });
25214
25601
  if ((wantsJson || wantsJsonl) && !scanResult.artifacts) {
25215
25602
  console.error("VibeRaven could not produce machine output because scan artifacts were not written.");
25216
25603
  return 3;
25217
25604
  }
25218
- const gateResult = scanResult.artifacts && (wantsJson || wantsJsonl || wantsStrict) ? JSON.parse(await (0, import_promises32.readFile)(scanResult.artifacts.gateResultPath, "utf8")) : void 0;
25605
+ const gateResult = scanResult.artifacts && (wantsJson || wantsJsonl || wantsStrict) ? JSON.parse(await (0, import_promises33.readFile)(scanResult.artifacts.gateResultPath, "utf8")) : void 0;
25219
25606
  const strictExitCode = wantsStrict && gateResult ? exitCodeForStrictGate(gateResult, { failOnWarnings: flags.strict === "warning" }) : scanResult.exitCode;
25220
25607
  if (wantsJson && gateResult) {
25221
25608
  process.stdout.write(renderGateResultJson(gateResult));
@@ -25237,7 +25624,7 @@ async function main() {
25237
25624
  env: process.env
25238
25625
  });
25239
25626
  if (mode === "local-ui") {
25240
- const cwd = positional[0] ? (0, import_node_path42.resolve)(process.cwd(), positional[0]) : process.cwd();
25627
+ const cwd = positional[0] ? (0, import_node_path43.resolve)(process.cwd(), positional[0]) : process.cwd();
25241
25628
  const handle = await startLocalUiServer({ cwd, initialPath: stackInitialPath(flags) });
25242
25629
  console.log(`VibeRaven local UI: ${handle.url}`);
25243
25630
  await waitForServerShutdown();
@@ -25248,7 +25635,7 @@ async function main() {
25248
25635
  await runInteractiveSession();
25249
25636
  return 0;
25250
25637
  }
25251
- const scanResult = await runScanCommand({ "agent-mode": true }, positional);
25638
+ const scanResult = await runScanCommand({ "agent-mode": true }, positional, { markFollowUpsShown: true });
25252
25639
  return scanResult.exitCode;
25253
25640
  }
25254
25641
  switch (command) {
@@ -25257,7 +25644,7 @@ async function main() {
25257
25644
  await runInteractiveSession();
25258
25645
  return 0;
25259
25646
  case "ui": {
25260
- const cwd = positional[0] ? (0, import_node_path42.resolve)(process.cwd(), positional[0]) : process.cwd();
25647
+ const cwd = positional[0] ? (0, import_node_path43.resolve)(process.cwd(), positional[0]) : process.cwd();
25261
25648
  const envPort = Number(process.env.PORT);
25262
25649
  const handle = await startLocalUiServer({ cwd, port: Number.isFinite(envPort) && envPort > 0 ? envPort : void 0, initialPath: stackInitialPath(flags) });
25263
25650
  console.log(`VibeRaven local UI: ${handle.url}`);
@@ -25269,14 +25656,14 @@ async function main() {
25269
25656
  return cmdStatus(flags, positional);
25270
25657
  case "check":
25271
25658
  return runCheckCommand({
25272
- cwd: positional[0] ? (0, import_node_path42.resolve)(process.cwd(), positional[0]) : process.cwd(),
25659
+ cwd: positional[0] ? (0, import_node_path43.resolve)(process.cwd(), positional[0]) : process.cwd(),
25273
25660
  json: Boolean(flags.json),
25274
25661
  quiet: Boolean(flags.quiet),
25275
25662
  base: typeof flags.base === "string" ? flags.base : void 0,
25276
25663
  markdown: flags.markdown === true
25277
25664
  });
25278
25665
  case "fix": {
25279
- const fixCwd = positional[0] ? (0, import_node_path42.resolve)(process.cwd(), positional[0]) : process.cwd();
25666
+ const fixCwd = positional[0] ? (0, import_node_path43.resolve)(process.cwd(), positional[0]) : process.cwd();
25280
25667
  const gapId = typeof flags.gap === "string" ? flags.gap : void 0;
25281
25668
  if (!gapId) {
25282
25669
  const artifact = await loadLastArtifact(fixCwd).catch(() => void 0);
@@ -25305,19 +25692,19 @@ async function main() {
25305
25692
  }
25306
25693
  case "actions":
25307
25694
  return runActionsCommand({
25308
- cwd: positional[0] ? (0, import_node_path42.resolve)(process.cwd(), positional[0]) : process.cwd(),
25695
+ cwd: positional[0] ? (0, import_node_path43.resolve)(process.cwd(), positional[0]) : process.cwd(),
25309
25696
  json: Boolean(flags.json)
25310
25697
  });
25311
25698
  case "preview":
25312
25699
  return runPreviewCommand({
25313
- cwd: positional[0] ? (0, import_node_path42.resolve)(process.cwd(), positional[0]) : process.cwd(),
25700
+ cwd: positional[0] ? (0, import_node_path43.resolve)(process.cwd(), positional[0]) : process.cwd(),
25314
25701
  agentMode: flags["agent-mode"] === true,
25315
25702
  json: Boolean(flags.json)
25316
25703
  });
25317
25704
  case "next":
25318
25705
  return runNextCommand({
25319
25706
  json: Boolean(flags.json),
25320
- cwd: positional[0] ? (0, import_node_path42.resolve)(process.cwd(), positional[0]) : process.cwd()
25707
+ cwd: positional[0] ? (0, import_node_path43.resolve)(process.cwd(), positional[0]) : process.cwd()
25321
25708
  });
25322
25709
  case "guide": {
25323
25710
  const provider2 = positional[0];
@@ -25339,7 +25726,7 @@ async function main() {
25339
25726
  cwd: process.cwd()
25340
25727
  });
25341
25728
  case "scan": {
25342
- const scanResult = await runScanCommand(flags, positional);
25729
+ const scanResult = await runScanCommand(flags, positional, { markFollowUpsShown: true });
25343
25730
  return scanResult.exitCode;
25344
25731
  }
25345
25732
  case "report":
@@ -25351,7 +25738,7 @@ async function main() {
25351
25738
  case "provider-verify":
25352
25739
  return cmdProviderVerify(flags, positional);
25353
25740
  case "init": {
25354
- const cwd = positional[0] ? (0, import_node_path42.resolve)(process.cwd(), positional[0]) : process.cwd();
25741
+ const cwd = positional[0] ? (0, import_node_path43.resolve)(process.cwd(), positional[0]) : process.cwd();
25355
25742
  const agents = typeof flags.agents === "string" ? flags.agents : void 0;
25356
25743
  if (flags.github === true) {
25357
25744
  return runInitGithubCommand({ cwd, dryRun: flags["dry-run"] === true, force: flags.force === true });
@@ -25368,7 +25755,7 @@ async function main() {
25368
25755
  return 1;
25369
25756
  }
25370
25757
  return runDoctorAgentsCommand({
25371
- cwd: positional[0] ? (0, import_node_path42.resolve)(process.cwd(), positional[0]) : process.cwd()
25758
+ cwd: positional[0] ? (0, import_node_path43.resolve)(process.cwd(), positional[0]) : process.cwd()
25372
25759
  });
25373
25760
  case "validate-npm-package":
25374
25761
  return runValidateNpmPackageCommand({
@@ -25381,7 +25768,7 @@ async function main() {
25381
25768
  return 1;
25382
25769
  }
25383
25770
  return runAuditCommand({
25384
- cwd: positional[0] ? (0, import_node_path42.resolve)(process.cwd(), positional[0]) : process.cwd(),
25771
+ cwd: positional[0] ? (0, import_node_path43.resolve)(process.cwd(), positional[0]) : process.cwd(),
25385
25772
  json: Boolean(flags.json)
25386
25773
  });
25387
25774
  case "clean": {
@@ -25389,7 +25776,7 @@ async function main() {
25389
25776
  console.error("Usage: viberaven clean --plan [path]");
25390
25777
  return 1;
25391
25778
  }
25392
- const cwd = positional[0] ? (0, import_node_path42.resolve)(process.cwd(), positional[0]) : process.cwd();
25779
+ const cwd = positional[0] ? (0, import_node_path43.resolve)(process.cwd(), positional[0]) : process.cwd();
25393
25780
  const plan = buildContextCleanupPlan({ projectRoot: cwd, files: await collectCleanupFiles(cwd) });
25394
25781
  const outputPath = await writeCleanupPlan(cwd, plan);
25395
25782
  console.log(`Wrote ${outputPath}
@@ -25399,9 +25786,9 @@ async function main() {
25399
25786
  }
25400
25787
  case "rls-test":
25401
25788
  return runRlsTestCommand({
25402
- cwd: positional[0] ? (0, import_node_path42.resolve)(process.cwd(), positional[0]) : process.cwd(),
25403
- matrix: typeof flags.matrix === "string" ? (0, import_node_path42.resolve)(process.cwd(), flags.matrix) : void 0,
25404
- migrations: typeof flags.migrations === "string" ? (0, import_node_path42.resolve)(process.cwd(), flags.migrations) : void 0,
25789
+ cwd: positional[0] ? (0, import_node_path43.resolve)(process.cwd(), positional[0]) : process.cwd(),
25790
+ matrix: typeof flags.matrix === "string" ? (0, import_node_path43.resolve)(process.cwd(), flags.matrix) : void 0,
25791
+ migrations: typeof flags.migrations === "string" ? (0, import_node_path43.resolve)(process.cwd(), flags.migrations) : void 0,
25405
25792
  json: Boolean(flags.json)
25406
25793
  });
25407
25794
  default:
@@ -25410,11 +25797,11 @@ async function main() {
25410
25797
  return 1;
25411
25798
  }
25412
25799
  }
25413
- var import_promises32, import_node_path42, STACK_AREAS;
25800
+ var import_promises33, import_node_path43, STACK_AREAS;
25414
25801
  var init_cli = __esm({
25415
25802
  "src/cli.ts"() {
25416
- import_promises32 = require("node:fs/promises");
25417
- import_node_path42 = require("node:path");
25803
+ import_promises33 = require("node:fs/promises");
25804
+ import_node_path43 = require("node:path");
25418
25805
  init_config();
25419
25806
  init_runScan();
25420
25807
  init_artifacts2();
@@ -25456,6 +25843,7 @@ var init_cli = __esm({
25456
25843
  init_buildTaskList();
25457
25844
  init_providerMcpBridge();
25458
25845
  init_render();
25846
+ init_registry2();
25459
25847
  STACK_AREAS = /* @__PURE__ */ new Set(["database", "auth", "payments", "deployment", "monitoring", "security"]);
25460
25848
  if (require.main === module) {
25461
25849
  main().then((code) => {