@viberaven/cli 1.6.2 → 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.
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
  `);
@@ -10437,6 +10459,18 @@ var init_databaseEvidence = __esm({
10437
10459
  });
10438
10460
 
10439
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
+ }
10440
10474
  function hasFilePath(scan, pattern) {
10441
10475
  return scan.files.some((f) => pattern.test(f.path));
10442
10476
  }
@@ -10730,7 +10764,8 @@ function auditFindingSeeds(audit) {
10730
10764
  detail: capLines(lines).join("\n"),
10731
10765
  copyPrompt: fixes.length > 0 ? `${meta.prompt}
10732
10766
  ${capLines(fixes).join("\n")}` : meta.prompt,
10733
- primaryMapCategory: meta.map
10767
+ primaryMapCategory: meta.map,
10768
+ locations: findingLocations(findings)
10734
10769
  };
10735
10770
  });
10736
10771
  }
@@ -10929,6 +10964,7 @@ ${check.evidence.map((e2) => `- ${e2}`).join("\n")}` : "";
10929
10964
  category: "SECURITY & AUTH",
10930
10965
  severity: "warning",
10931
10966
  title: "Stripe webhook does not verify the signature",
10967
+ locations: findingLocations(webhooks.unverified.map((u2) => ({ file: u2.path }))),
10932
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.`,
10933
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.',
10934
10970
  primaryMapCategory: "payments"
@@ -10965,7 +11001,7 @@ ${check.evidence.map((e2) => `- ${e2}`).join("\n")}` : "";
10965
11001
  severity: "warning",
10966
11002
  title: `Env vars used in code but missing from ${target}`,
10967
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.`,
10968
- copyPrompt: `Add placeholder entries for these variables to ${contract.templates.length === 1 ? target : contract.templates[0]} (never real values): ${shown}${drifted.length > 5 ? " and the rest" : ""}. Then confirm each one is set in the deployment environment (Vercel project settings).`,
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).`,
10969
11005
  primaryMapCategory: "deployment"
10970
11006
  });
10971
11007
  } else {
@@ -11032,9 +11068,9 @@ function scoreFrom(gaps) {
11032
11068
  }
11033
11069
  return Math.max(5, Math.min(96, score));
11034
11070
  }
11035
- function labelFor(score, criticalCount) {
11071
+ function scoreLabelFor(score, criticalCount, warningCount) {
11036
11072
  if (criticalCount > 0) return "Blocked";
11037
- if (score >= 85) return "Launch ready";
11073
+ if (warningCount === 0) return "No blockers in repo checks";
11038
11074
  if (score >= 60) return "Almost there";
11039
11075
  return "Needs work";
11040
11076
  }
@@ -11133,11 +11169,12 @@ async function buildLocalStationOutput(input) {
11133
11169
  toolSuggestions: [],
11134
11170
  mcpSuggestion: null,
11135
11171
  primaryMapCategory: seed.primaryMapCategory,
11136
- affectedMapCategories: [seed.primaryMapCategory]
11172
+ affectedMapCategories: [seed.primaryMapCategory],
11173
+ ...seed.locations?.length ? { locations: seed.locations } : {}
11137
11174
  }));
11138
11175
  return {
11139
11176
  score,
11140
- scoreLabel: labelFor(score, criticalCount),
11177
+ scoreLabel: scoreLabelFor(score, criticalCount, warningCount),
11141
11178
  summary,
11142
11179
  archetype,
11143
11180
  gaps,
@@ -11593,6 +11630,28 @@ var init_runScan = __esm({
11593
11630
  }
11594
11631
  });
11595
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
+
11596
11655
  // src/capabilities/classify.ts
11597
11656
  function gapText(gap) {
11598
11657
  return `${gap.id} ${gap.title} ${gap.detail} ${gap.primaryMapCategory}`.toLowerCase();
@@ -11780,6 +11839,37 @@ var init_capabilities = __esm({
11780
11839
  }
11781
11840
  });
11782
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
+
11783
11873
  // src/contracts/commands.ts
11784
11874
  function promptGapCommand(gapId) {
11785
11875
  return `${PUBLIC_COMMAND} prompt --gap ${gapId}`;
@@ -11807,65 +11897,18 @@ var init_commands = __esm({
11807
11897
  }
11808
11898
  });
11809
11899
 
11810
- // src/contracts/contextMap.ts
11811
- function generateContextMap(artifact) {
11812
- const criticalCount = artifact.gaps.filter((gap) => gap.severity === "critical").length;
11813
- const warningCount = artifact.gaps.filter((gap) => gap.severity === "warning").length;
11814
- return {
11815
- $schema: "https://viberaven.dev/schemas/context-map.schema.json",
11816
- schemaVersion: "v1",
11817
- generatedAt: artifact.scannedAt,
11818
- workspace: {
11819
- root: artifact.workspacePath,
11820
- packageManager: "unknown",
11821
- languages: ["typescript"],
11822
- frameworks: artifact.archetype ? [artifact.archetype] : []
11823
- },
11824
- agentContract: {
11825
- readFirst: ".viberaven/agent-tasklist.md",
11826
- machineResult: ".viberaven/gate-result.json",
11827
- contextMap: ".viberaven/context-map.json",
11828
- verifyCommand: PUBLIC_VERIFY_COMMAND,
11829
- strictCommand: PUBLIC_STRICT_COMMAND,
11830
- nextCommand: PUBLIC_NEXT_JSON_COMMAND
11831
- },
11832
- stack: {
11833
- deployment: artifact.selectedProviders?.deployment ? [artifact.selectedProviders.deployment] : [],
11834
- database: artifact.selectedProviders?.database ? [artifact.selectedProviders.database] : [],
11835
- auth: artifact.selectedProviders?.auth ? [artifact.selectedProviders.auth] : [],
11836
- payments: artifact.selectedProviders?.payments ? [artifact.selectedProviders.payments] : [],
11837
- monitoring: artifact.selectedProviders?.monitoring ? [artifact.selectedProviders.monitoring] : []
11838
- },
11839
- capabilityGraph: summarizeCapabilities(artifact.gaps),
11840
- productionGate: {
11841
- status: criticalCount > 0 ? "not_clear" : warningCount > 0 ? "warning" : "clear",
11842
- criticalCount,
11843
- warningCount,
11844
- providerDashboardChecksRequired: true
11845
- },
11846
- topGaps: artifact.gaps.slice(0, 10).map((gap) => ({
11847
- id: gap.id,
11848
- severity: gap.severity,
11849
- title: gap.title,
11850
- area: String(gap.primaryMapCategory),
11851
- promptCommand: promptGapCommand(gap.id)
11852
- }))
11853
- };
11854
- }
11855
- var init_contextMap = __esm({
11856
- "src/contracts/contextMap.ts"() {
11857
- "use strict";
11858
- init_capabilities();
11859
- init_commands();
11860
- }
11861
- });
11862
-
11863
11900
  // src/contracts/gateResult.ts
11864
11901
  function gateStatus(criticalCount, warningCount) {
11865
11902
  if (criticalCount > 0) return "not_clear";
11866
11903
  if (warningCount > 0) return "warning";
11867
11904
  return "clear";
11868
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
+ }
11869
11912
  function runIdFrom(scannedAt) {
11870
11913
  return `vr_${scannedAt.replace(/\D/g, "").slice(0, 14) || "scan"}`;
11871
11914
  }
@@ -11883,7 +11926,7 @@ function generateGateResult(artifact, options = {}) {
11883
11926
  generatedAt: artifact.scannedAt,
11884
11927
  workspace: {
11885
11928
  root: artifact.workspacePath,
11886
- packageManager: "unknown",
11929
+ packageManager: options.packageManager ?? detectPackageManager(artifact.workspacePath),
11887
11930
  languages: ["typescript"],
11888
11931
  frameworks: artifact.archetype ? [artifact.archetype] : []
11889
11932
  },
@@ -11924,101 +11967,246 @@ var init_gateResult = __esm({
11924
11967
  "src/contracts/gateResult.ts"() {
11925
11968
  "use strict";
11926
11969
  init_capabilities();
11970
+ init_packageManager();
11927
11971
  init_commands();
11928
11972
  }
11929
11973
  });
11930
11974
 
11931
- // src/contracts/gapEvidence.ts
11932
- function summarizeGap(gap) {
11933
- 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;
11934
11986
  }
11935
- function generateGapEvidenceFiles(artifact) {
11936
- return artifact.gaps.slice(0, 50).map((gap) => ({
11937
- path: `.viberaven/gaps/${gap.id}.json`,
11938
- content: {
11939
- $schema: "https://viberaven.dev/schemas/gap.schema.json",
11940
- schemaVersion: "v1",
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) => ({
11941
12017
  id: gap.id,
11942
12018
  severity: gap.severity,
11943
- capability: classifyGapCapability(gap),
11944
12019
  title: gap.title,
11945
- summary: summarizeGap(gap),
11946
- evidence: [{ kind: String(gap.primaryMapCategory), redacted: true }],
11947
- commands: {
11948
- prompt: promptGapCommand(gap.id),
11949
- healPlan: healPlanGapCommand(gap.id),
11950
- verify: PUBLIC_VERIFY_COMMAND
11951
- },
11952
- providerBoundary: {
11953
- requiresDashboardCheck: true,
11954
- reason: "Repo evidence cannot prove live provider dashboard state."
11955
- }
11956
- }
11957
- }));
12020
+ area: String(gap.primaryMapCategory),
12021
+ promptCommand: promptGapCommand(gap.id)
12022
+ }))
12023
+ };
11958
12024
  }
11959
- var init_gapEvidence = __esm({
11960
- "src/contracts/gapEvidence.ts"() {
12025
+ var init_contextMap = __esm({
12026
+ "src/contracts/contextMap.ts"() {
11961
12027
  "use strict";
12028
+ init_detectedProviders();
12029
+ init_gateResult();
11962
12030
  init_capabilities();
12031
+ init_packageManager();
11963
12032
  init_commands();
11964
12033
  }
11965
12034
  });
11966
12035
 
11967
- // src/playbooks/checkMap.ts
11968
- function mapCheckToPlaybook(check) {
11969
- if (CHECK_TO_PLAYBOOK[check.id]) {
11970
- 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
+ }
11971
12069
  }
11972
- const providerKey = check.providerKey.toLowerCase();
11973
- if (providerKey.includes("vercel") || check.area === "deployment") {
11974
- 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;
11975
12086
  }
11976
- if (providerKey.includes("stripe") || check.area === "payments") {
11977
- 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]);
11978
12129
  }
11979
- if (providerKey.includes("supabase") && check.area === "auth") {
11980
- 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
+ };
11981
12149
  }
11982
- if (providerKey.includes("supabase") || check.area === "database") {
11983
- 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
+ };
11984
12157
  }
11985
- if (check.area === "auth") {
11986
- 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
+ };
11987
12178
  }
11988
- return "vercel";
12179
+ return { ok: true, file: `${dir}/${name}` };
11989
12180
  }
11990
- var CHECK_TO_PLAYBOOK;
11991
- var init_checkMap = __esm({
11992
- "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"() {
11993
12184
  "use strict";
11994
- CHECK_TO_PLAYBOOK = {
11995
- "vercel-deployment": "vercel",
11996
- "vercel-project": "vercel",
11997
- "supabase-project": "supabase",
11998
- "supabase-env": "supabase",
11999
- "stripe-webhook": "stripe",
12000
- "stripe-product": "stripe",
12001
- "stripe-keys": "stripe"
12002
- };
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$/;
12003
12191
  }
12004
12192
  });
12005
12193
 
12006
12194
  // src/playbooks/playbooksRoot.ts
12007
12195
  function playbooksRoot() {
12008
- 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")];
12009
12197
  for (const dir of candidates) {
12010
- 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"))) {
12011
12199
  return dir;
12012
12200
  }
12013
12201
  }
12014
12202
  throw new Error("VibeRaven playbooks directory not found (expected vercel.json)");
12015
12203
  }
12016
- var import_node_fs4, import_node_path8;
12204
+ var import_node_fs6, import_node_path10;
12017
12205
  var init_playbooksRoot = __esm({
12018
12206
  "src/playbooks/playbooksRoot.ts"() {
12019
12207
  "use strict";
12020
- import_node_fs4 = require("node:fs");
12021
- import_node_path8 = require("node:path");
12208
+ import_node_fs6 = require("node:fs");
12209
+ import_node_path10 = require("node:path");
12022
12210
  }
12023
12211
  });
12024
12212
 
@@ -12125,7 +12313,7 @@ async function loadPlaybook(provider2) {
12125
12313
  `Unknown provider "${provider2}". Available: ${PLAYBOOK_PROVIDERS.join(", ")}`
12126
12314
  );
12127
12315
  }
12128
- const path = (0, import_node_path9.join)(playbooksRoot(), `${normalized}.json`);
12316
+ const path = (0, import_node_path11.join)(playbooksRoot(), `${normalized}.json`);
12129
12317
  const raw = JSON.parse(await (0, import_promises7.readFile)(path, "utf-8"));
12130
12318
  const playbook = parsePlaybook(raw);
12131
12319
  if (playbook.provider !== normalized) {
@@ -12140,59 +12328,30 @@ function loadPlaybookSync(provider2) {
12140
12328
  `Unknown provider "${provider2}". Available: ${PLAYBOOK_PROVIDERS.join(", ")}`
12141
12329
  );
12142
12330
  }
12143
- const path = (0, import_node_path9.join)(playbooksRoot(), `${normalized}.json`);
12144
- 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"));
12145
12333
  const playbook = parsePlaybook(raw);
12146
12334
  if (playbook.provider !== normalized) {
12147
12335
  throw new Error(`Playbook file ${normalized}.json has mismatched provider field`);
12148
12336
  }
12149
12337
  return playbook;
12150
12338
  }
12151
- var import_node_fs5, import_promises7, import_node_path9;
12339
+ var import_node_fs7, import_promises7, import_node_path11;
12152
12340
  var init_loadPlaybook = __esm({
12153
12341
  "src/playbooks/loadPlaybook.ts"() {
12154
12342
  "use strict";
12155
- import_node_fs5 = require("node:fs");
12343
+ import_node_fs7 = require("node:fs");
12156
12344
  import_promises7 = require("node:fs/promises");
12157
- import_node_path9 = require("node:path");
12345
+ import_node_path11 = require("node:path");
12158
12346
  init_playbooksRoot();
12159
12347
  init_types2();
12160
12348
  init_validate();
12161
12349
  }
12162
12350
  });
12163
12351
 
12164
- // src/playbooks/manualChecks.ts
12165
- function isManualProviderCheck(check) {
12166
- return check.evidenceClass === "manual-dashboard" || check.evidenceClass === "mcp-verifier" || check.evidenceSource === "provider" || check.evidenceSource === "mcp" || check.status === "needs-connection" || check.status === "unknown";
12167
- }
12168
- function collectManualChecks(artifact) {
12169
- const refs = [];
12170
- for (const area of artifact.missionGraph.areas ?? []) {
12171
- for (const mission of area.providerMissions) {
12172
- for (const check of mission.checks) {
12173
- if (!isManualProviderCheck(check)) {
12174
- continue;
12175
- }
12176
- refs.push({
12177
- areaLabel: area.label,
12178
- providerLabel: mission.providerLabel,
12179
- check,
12180
- mapCategory: area.key
12181
- });
12182
- }
12183
- }
12184
- }
12185
- return refs;
12186
- }
12187
- var init_manualChecks = __esm({
12188
- "src/playbooks/manualChecks.ts"() {
12189
- "use strict";
12190
- }
12191
- });
12192
-
12193
- // src/tui/menu.ts
12194
- function isScanNotFoundError(error) {
12195
- return error instanceof ScanNotFoundError;
12352
+ // src/tui/menu.ts
12353
+ function isScanNotFoundError(error) {
12354
+ return error instanceof ScanNotFoundError;
12196
12355
  }
12197
12356
  function needsScanMessage(startDir) {
12198
12357
  const cwd = startDir ?? process.cwd();
@@ -12239,7 +12398,7 @@ async function loadLastArtifact(startDir) {
12239
12398
  if (!workspace) {
12240
12399
  throw new ScanNotFoundError(needsScanMessage(startDir));
12241
12400
  }
12242
- 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");
12243
12402
  try {
12244
12403
  const raw = await (0, import_promises8.readFile)(path, "utf-8");
12245
12404
  return JSON.parse(raw);
@@ -12247,12 +12406,12 @@ async function loadLastArtifact(startDir) {
12247
12406
  throw new ScanNotFoundError(needsScanMessage(startDir));
12248
12407
  }
12249
12408
  }
12250
- var import_promises8, import_node_path10, SEVERITY_RANK2, ScanNotFoundError;
12409
+ var import_promises8, import_node_path12, SEVERITY_RANK2, ScanNotFoundError;
12251
12410
  var init_menu = __esm({
12252
12411
  "src/tui/menu.ts"() {
12253
12412
  "use strict";
12254
12413
  import_promises8 = require("node:fs/promises");
12255
- import_node_path10 = require("node:path");
12414
+ import_node_path12 = require("node:path");
12256
12415
  init_config();
12257
12416
  SEVERITY_RANK2 = {
12258
12417
  critical: 0,
@@ -12268,228 +12427,6 @@ var init_menu = __esm({
12268
12427
  }
12269
12428
  });
12270
12429
 
12271
- // src/resolveNextAction.ts
12272
- function resolveNextAction(artifact) {
12273
- const repoGap = sortGapsByPriority(artifact.gaps)[0];
12274
- if (repoGap) {
12275
- return {
12276
- type: "repo-fix",
12277
- title: repoGap.title,
12278
- detail: repoGap.detail,
12279
- command: `viberaven prompt --gap ${repoGap.id}`
12280
- };
12281
- }
12282
- const manual = collectManualChecks(artifact)[0];
12283
- if (manual) {
12284
- const provider2 = mapCheckToPlaybook(manual.check);
12285
- let openUrl;
12286
- try {
12287
- const playbook = loadPlaybookSync(provider2);
12288
- openUrl = playbook.steps[0]?.openUrl;
12289
- } catch {
12290
- openUrl = void 0;
12291
- }
12292
- return {
12293
- type: "provider-guide",
12294
- title: manual.check.label,
12295
- detail: manual.check.promptHint || `Configure ${manual.providerLabel} in the provider dashboard (${manual.areaLabel}).`,
12296
- provider: provider2,
12297
- playbookStep: 1,
12298
- command: `viberaven guide ${provider2} --step 1`,
12299
- openUrl
12300
- };
12301
- }
12302
- return {
12303
- type: "done",
12304
- title: "No blockers found",
12305
- detail: "Re-scan after changes to confirm production readiness."
12306
- };
12307
- }
12308
- var init_resolveNextAction = __esm({
12309
- "src/resolveNextAction.ts"() {
12310
- "use strict";
12311
- init_checkMap();
12312
- init_loadPlaybook();
12313
- init_manualChecks();
12314
- init_menu();
12315
- }
12316
- });
12317
-
12318
- // src/report/agentSummary.ts
12319
- function sortGaps(gaps) {
12320
- return [...gaps].sort((a, b3) => {
12321
- const sev = SEVERITY_ORDER2[a.severity] - SEVERITY_ORDER2[b3.severity];
12322
- if (sev !== 0) {
12323
- return sev;
12324
- }
12325
- return a.title.localeCompare(b3.title);
12326
- });
12327
- }
12328
- function generateAgentSummary(artifact) {
12329
- const lines = [];
12330
- const topGaps = sortGaps(artifact.gaps).slice(0, 8);
12331
- lines.push("# VibeRaven agent summary", "");
12332
- lines.push(`Scanned: \`${artifact.workspacePath}\``);
12333
- lines.push(`At: ${artifact.scannedAt}`);
12334
- lines.push(
12335
- `Production core: **${artifact.productionCorePercent}%** \xB7 Model score: **${artifact.score}** (${artifact.scoreLabel})`
12336
- );
12337
- lines.push("");
12338
- const next = resolveNextAction(artifact);
12339
- lines.push("## Next action", "");
12340
- lines.push(`**${next.title}** \u2014 ${next.detail}`);
12341
- if (next.command) {
12342
- lines.push(`Command: \`${next.command}\``);
12343
- }
12344
- lines.push("");
12345
- lines.push("## Suggested stack", "");
12346
- lines.push(
12347
- "React + Tailwind + shadcn/ui + Supabase + Vercel (agent-default stack for lowest launch friction)"
12348
- );
12349
- lines.push("");
12350
- lines.push("## Summary");
12351
- lines.push(artifact.summary || "_No summary returned._");
12352
- lines.push("");
12353
- lines.push("## Mission map (repo wiring)");
12354
- lines.push("");
12355
- lines.push("| Area | Provider | Readiness | Notes |");
12356
- lines.push("|------|----------|-----------|-------|");
12357
- for (const area of artifact.missionGraph.areas ?? []) {
12358
- for (const mission of area.providerMissions) {
12359
- const failed = mission.checks.filter(
12360
- (c) => c.status === "missing" || c.status === "failed" || c.status === "needs-connection"
12361
- ).length;
12362
- const notes = failed > 0 ? `${failed} open check${failed === 1 ? "" : "s"}` : `${mission.readinessPercent}% repo checks`;
12363
- lines.push(
12364
- `| ${area.label} | ${mission.providerLabel} | ${mission.readinessPercent}% | ${notes} |`
12365
- );
12366
- }
12367
- }
12368
- lines.push("");
12369
- lines.push("## Agent-code actions");
12370
- if (topGaps.length === 0) {
12371
- lines.push("_No model gaps returned. Review mission map checks before changing code._");
12372
- } else {
12373
- topGaps.forEach((gap, index) => {
12374
- lines.push(
12375
- `${index + 1}. **${gap.title}** (\`${gap.id}\`, ${gap.severity}, map: \`${gap.primaryMapCategory}\`)`
12376
- );
12377
- lines.push(` - ${gap.detail}`);
12378
- lines.push(` - Command: \`viberaven prompt --gap ${gap.id}\``);
12379
- if (gap.copyPrompt) {
12380
- lines.push(` - Prompt: ${gap.copyPrompt}`);
12381
- }
12382
- });
12383
- }
12384
- const manualChecks = (artifact.missionGraph.areas ?? []).flatMap(
12385
- (area) => area.providerMissions.flatMap(
12386
- (mission) => mission.checks.filter(
12387
- (check) => check.evidenceClass === "manual-dashboard" || check.evidenceClass === "mcp-verifier" || check.evidenceSource === "provider" || check.evidenceSource === "mcp" || check.status === "needs-connection" || check.status === "unknown"
12388
- ).map((check) => ({ area: area.label, provider: mission.providerLabel, check }))
12389
- )
12390
- );
12391
- lines.push("");
12392
- lines.push("## Human-provider actions");
12393
- if (manualChecks.length === 0) {
12394
- lines.push("_No manual provider actions were identified in this scan._");
12395
- } else {
12396
- manualChecks.slice(0, 8).forEach((item4, index) => {
12397
- lines.push(`${index + 1}. **${item4.check.label}** (${item4.area} / ${item4.provider})`);
12398
- lines.push(
12399
- ` - ${item4.check.promptHint || "Ask the user to confirm this in the provider dashboard or through read-only MCP."}`
12400
- );
12401
- });
12402
- }
12403
- lines.push("");
12404
- lines.push("Do not claim human-provider actions as repo-code fixes.");
12405
- lines.push("");
12406
- lines.push("## Agent workflow");
12407
- lines.push("1. Read `.viberaven/agent-tasklist.md` first for the production gate.");
12408
- lines.push("2. Read `.viberaven/launch-playbook.md` for the full checklist.");
12409
- lines.push("3. Run `viberaven next --json` - one action at a time.");
12410
- lines.push("4. Repo fix: `viberaven prompt --gap <id>` then implement.");
12411
- lines.push("5. Provider: `viberaven guide <provider> --step N` and `viberaven open <provider>`.");
12412
- lines.push(`6. Run \`${PUBLIC_VERIFY_COMMAND}\` to rescan and refresh \`.viberaven/agent-tasklist.md\`.`);
12413
- lines.push("");
12414
- return `${lines.join("\n")}
12415
- `;
12416
- }
12417
- var SEVERITY_ORDER2;
12418
- var init_agentSummary = __esm({
12419
- "src/report/agentSummary.ts"() {
12420
- "use strict";
12421
- init_commands();
12422
- init_resolveNextAction();
12423
- SEVERITY_ORDER2 = {
12424
- critical: 0,
12425
- warning: 1,
12426
- info: 2
12427
- };
12428
- }
12429
- });
12430
-
12431
- // src/heal/rlsMigrationTarget.ts
12432
- function rlsEnableStatements(tables) {
12433
- return [...new Set(tables)].map((table2) => `alter table ${table2} enable row level security;`);
12434
- }
12435
- function migrationStamp(now) {
12436
- return now.toISOString().replace(/\D/g, "").slice(0, 14);
12437
- }
12438
- function rlsMigrationTarget(cwd, evidence, now = /* @__PURE__ */ new Date()) {
12439
- if (evidence.length === 0) return { ok: false, reason: "No table without row level security was found in the repo SQL." };
12440
- const statementList = rlsEnableStatements(evidence.map((entry) => entry.table)).join(" ");
12441
- const turnedOff = evidence.filter((entry) => entry.turnedOff);
12442
- if (turnedOff.length > 0) {
12443
- const where = turnedOff.map((entry) => `${entry.table} (${entry.file.replace(/\\/g, "/")})`).join(", ");
12444
- return {
12445
- ok: false,
12446
- reason: `The latest migration that touches ${where} turns row level security off on purpose, so VibeRaven will not turn it back on automatically. If the user wants it on, add these statements by hand in a new migration, with the policies the app needs: ${statementList}`
12447
- };
12448
- }
12449
- const source = evidence[0].file.replace(/\\/g, "/");
12450
- const dir = import_node_path11.posix.dirname(source);
12451
- if (import_node_path11.posix.basename(dir) !== "migrations") {
12452
- return {
12453
- ok: false,
12454
- reason: `The tables come from ${source}, which is not in a migrations folder, so VibeRaven will not guess where a migration goes. Add these statements by hand where this project keeps its schema: ${statementList}`
12455
- };
12456
- }
12457
- let existing;
12458
- try {
12459
- existing = (0, import_node_fs6.readdirSync)((0, import_node_path11.join)(cwd, dir)).filter((file) => file.endsWith(".sql")).sort();
12460
- } catch {
12461
- return { ok: false, reason: `VibeRaven could not read ${dir}. Add these statements by hand in a new migration: ${statementList}` };
12462
- }
12463
- const otherNaming = existing.find((file) => !TIMESTAMPED_MIGRATION.test(file));
12464
- if (otherNaming) {
12465
- return {
12466
- ok: false,
12467
- reason: `The migrations in ${dir} are not named <14-digit timestamp>_name.sql (for example ${otherNaming}), so a timestamped file from VibeRaven could run in the wrong order next to them. Add these statements by hand in a new migration that follows this project's naming: ${statementList}`
12468
- };
12469
- }
12470
- const name = `${migrationStamp(now)}_viberaven_enable_rls.sql`;
12471
- const last = existing[existing.length - 1];
12472
- if (last && name <= last) {
12473
- return {
12474
- ok: false,
12475
- reason: `A new migration named ${name} would not run after ${last} in ${dir}, so VibeRaven will not write it. Add these statements by hand in a migration that runs last: ${statementList}`
12476
- };
12477
- }
12478
- return { ok: true, file: `${dir}/${name}` };
12479
- }
12480
- var import_node_fs6, import_node_path11, RLS_TURNED_OFF_PHRASE, RLS_ASK_FIRST, RLS_NO_POLICIES_WARNING, TIMESTAMPED_MIGRATION;
12481
- var init_rlsMigrationTarget = __esm({
12482
- "src/heal/rlsMigrationTarget.ts"() {
12483
- "use strict";
12484
- import_node_fs6 = require("node:fs");
12485
- import_node_path11 = require("node:path");
12486
- RLS_TURNED_OFF_PHRASE = "has row level security turned off at";
12487
- RLS_ASK_FIRST = "Ask the user first: enabling RLS without policies makes browser reads return no rows until policies exist.";
12488
- RLS_NO_POLICIES_WARNING = "This migration enables row level security with no policies. Until you add policies, the anon and authenticated roles get no rows from these tables through the Data API, so browser reads return nothing and browser writes fail. `check` then reports the tables only as info (rls_no_policies), not as a blocker. Add the policies the app needs before you apply the migration to the database.";
12489
- TIMESTAMPED_MIGRATION = /^\d{14}_.+\.sql$/;
12490
- }
12491
- });
12492
-
12493
12430
  // src/buildTaskList.ts
12494
12431
  function rlsMigrationFile(gap) {
12495
12432
  if (gap.id !== "rls_disabled" || typeof gap.detail !== "string") return void 0;
@@ -12508,7 +12445,12 @@ function hasRecipe(gapId) {
12508
12445
  const lower = gapId.toLowerCase();
12509
12446
  return lower.includes("empty") && lower.includes("catch");
12510
12447
  }
12511
- 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) {
12512
12454
  const cat = gap.primaryMapCategory.toLowerCase();
12513
12455
  if (cat === "deployment") return "vercel";
12514
12456
  if (cat === "payments") return "stripe";
@@ -12521,9 +12463,9 @@ function gapToPlaybookProvider(gap) {
12521
12463
  if (id.includes("auth")) return "auth-supabase";
12522
12464
  return void 0;
12523
12465
  }
12524
- function hasPlaybook(gap) {
12466
+ function hasPlaybook(gap, inRepo) {
12525
12467
  try {
12526
- const provider2 = gapToPlaybookProvider(gap);
12468
+ const provider2 = gapToPlaybookProvider(gap, inRepo);
12527
12469
  if (!provider2) return false;
12528
12470
  return PLAYBOOK_PROVIDERS.includes(provider2);
12529
12471
  } catch {
@@ -12553,13 +12495,20 @@ function buildProviderAction(gap, provider2) {
12553
12495
  }
12554
12496
  function buildTaskList(artifact) {
12555
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;
12556
12500
  return sorted.map((gap, index) => {
12557
12501
  const id = `TASK-${String(index + 1).padStart(3, "0")}`;
12558
12502
  let fixType;
12559
12503
  const migrationFile = rlsMigrationFile(gap);
12560
- 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) {
12561
12508
  fixType = "repo-code";
12562
- } else if (hasPlaybook(gap)) {
12509
+ } else if (informational) {
12510
+ fixType = "manual-verify";
12511
+ } else if (hasPlaybook(gap, inRepo)) {
12563
12512
  fixType = "provider-action";
12564
12513
  } else {
12565
12514
  fixType = "manual-verify";
@@ -12576,6 +12525,8 @@ function buildTaskList(artifact) {
12576
12525
  if (fixType === "repo-code" && !hasRecipe(gap.id) && migrationFile) {
12577
12526
  base.requiresUserAction = true;
12578
12527
  base.file = migrationFile;
12528
+ base.userSteps = [APPLY_MIGRATION];
12529
+ if (location?.file === migrationFile && location.line !== void 0) base.line = location.line;
12579
12530
  const target = rlsMigrationTarget(artifact.workspacePath, rlsEvidence2(gap.detail));
12580
12531
  if (target.ok) {
12581
12532
  base.mcpTool = "viberaven_heal_apply";
@@ -12587,6 +12538,28 @@ function buildTaskList(artifact) {
12587
12538
  if (gap.copyPrompt) {
12588
12539
  base.exactFix = gap.copyPrompt.trim();
12589
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
+ }
12590
12563
  } else if (fixType === "repo-code") {
12591
12564
  base.mcpTool = "viberaven_heal_apply";
12592
12565
  base.mcpArgs = { gap: gap.id, yes: true };
@@ -12596,7 +12569,7 @@ function buildTaskList(artifact) {
12596
12569
  }
12597
12570
  } else if (fixType === "provider-action") {
12598
12571
  try {
12599
- const provider2 = gapToPlaybookProvider(gap);
12572
+ const provider2 = gapToPlaybookProvider(gap, inRepo);
12600
12573
  if (provider2) {
12601
12574
  const pa = buildProviderAction(gap, provider2);
12602
12575
  if (pa) {
@@ -12614,7 +12587,7 @@ function buildTaskList(artifact) {
12614
12587
  }
12615
12588
  function buildTaskListMarkdown(tasks) {
12616
12589
  if (tasks.length === 0) {
12617
- 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";
12618
12591
  }
12619
12592
  const lines = ["# VibeRaven Agent Tasklist", ""];
12620
12593
  for (const task of tasks) {
@@ -12622,7 +12595,7 @@ function buildTaskListMarkdown(tasks) {
12622
12595
  lines.push(`## ${task.id} \xB7 ${task.gapId} \xB7 ${severityLabel}`, "");
12623
12596
  lines.push(`**Fix type:** ${task.fixType} `);
12624
12597
  if (task.file) {
12625
- lines.push(`**File:** \`${task.file}\` `);
12598
+ lines.push(`**File:** \`${task.file}${task.line !== void 0 ? `:${task.line}` : ""}\` `);
12626
12599
  }
12627
12600
  if (task.action) {
12628
12601
  lines.push(`**Action:** ${task.action} `);
@@ -12630,62 +12603,387 @@ function buildTaskListMarkdown(tasks) {
12630
12603
  if (task.exactFix) {
12631
12604
  lines.push(`**Exact fix:** ${task.exactFix} `);
12632
12605
  } else {
12633
- 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. `);
12634
12607
  }
12635
12608
  lines.push(`**Verify:** \`${task.verifyCommand}\` `);
12636
12609
  if (task.mcpTool && task.mcpArgs) {
12637
12610
  lines.push(
12638
- `**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}\`)`
12639
12934
  );
12640
- }
12641
- lines.push(`**Requires user action:** ${task.requiresUserAction}`);
12642
- if (task.providerAction) {
12643
- const pa = task.providerAction;
12644
- lines.push("");
12645
- lines.push("**Provider action:**");
12646
- lines.push(`- Provider: ${pa.provider}`);
12647
- lines.push(`- Dashboard: ${pa.dashboardUrl}`);
12648
- lines.push(`- Step: ${pa.exactStep}`);
12649
- if (pa.envKeyName) lines.push(`- Env key: \`${pa.envKeyName}\``);
12650
- if (pa.doneSignal) lines.push(`- Done when: ${pa.doneSignal}`);
12651
- if (pa.mcpAlternative) lines.push(`- MCP alternative: \`${pa.mcpAlternative}\``);
12652
- }
12653
- lines.push("");
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
+ });
12654
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("");
12655
12972
  return `${lines.join("\n")}
12656
12973
  `;
12657
12974
  }
12658
- var KNOWN_RECIPE_GAP_IDS;
12659
- var init_buildTaskList = __esm({
12660
- "src/buildTaskList.ts"() {
12975
+ var SEVERITY_ORDER2;
12976
+ var init_agentSummary = __esm({
12977
+ "src/report/agentSummary.ts"() {
12661
12978
  "use strict";
12662
12979
  init_commands();
12663
- init_rlsMigrationTarget();
12664
- init_loadPlaybook();
12665
- init_types2();
12666
- init_menu();
12667
- KNOWN_RECIPE_GAP_IDS = /* @__PURE__ */ new Set([
12668
- // emptyCatch (original recipe)
12669
- "empty-catch",
12670
- "empty_catch",
12671
- "emptyCatch",
12672
- // W3 env-add recipes
12673
- "auth_secret_missing",
12674
- "node_env_not_set",
12675
- "database_url_missing",
12676
- // W3 file-create recipes
12677
- "missing_error_boundary",
12678
- "missing_health_route",
12679
- "missing_loading_state",
12680
- "missing_404_page",
12681
- // W3 file-patch recipes
12682
- "missing_csp_header",
12683
- "missing_rate_limit",
12684
- "eslint_restricted_imports"
12685
- // NOTE: 'rls_disabled' is intentionally NOT here. When the finding comes from repo SQL it is a repo action whose
12686
- // recipe writes a new migration (see rlsMigrationFile and heal/recipes/supabaseRls.ts); without SQL evidence it
12687
- // stays a provider action and heal refuses.
12688
- ]);
12980
+ init_resolveNextAction();
12981
+ init_inRepoMissions();
12982
+ SEVERITY_ORDER2 = {
12983
+ critical: 0,
12984
+ warning: 1,
12985
+ info: 2
12986
+ };
12689
12987
  }
12690
12988
  });
12691
12989
 
@@ -12695,12 +12993,21 @@ function generateLaunchPlaybook(artifact) {
12695
12993
  lines.push("# VibeRaven launch playbook", "");
12696
12994
  lines.push(`Generated from scan at ${artifact.scannedAt}. Work top to bottom.`, "");
12697
12995
  lines.push("## Repo fixes (agent code)", "");
12698
- 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]);
12699
12999
  if (repoGaps.length === 0) {
12700
13000
  lines.push("_No repo-code gaps found._", "");
12701
13001
  } else {
12702
13002
  for (const gap of repoGaps) {
12703
- 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]}`);
12704
13011
  }
12705
13012
  lines.push("");
12706
13013
  }
@@ -12712,7 +13019,7 @@ function generateLaunchPlaybook(artifact) {
12712
13019
  for (const item4 of manuals) {
12713
13020
  const provider2 = mapCheckToPlaybook(item4.check);
12714
13021
  lines.push(
12715
- `- [ ] ${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})`
12716
13023
  );
12717
13024
  }
12718
13025
  lines.push("");
@@ -12733,6 +13040,7 @@ var init_launchPlaybook = __esm({
12733
13040
  init_manualChecks();
12734
13041
  init_menu();
12735
13042
  init_commands();
13043
+ init_repoFixes();
12736
13044
  }
12737
13045
  });
12738
13046
 
@@ -13061,25 +13369,25 @@ var init_reportHtml = __esm({
13061
13369
  function getBundledReportAssetsDir() {
13062
13370
  const base = __dirname;
13063
13371
  const candidates = [
13064
- (0, import_node_path12.join)(base, "report"),
13065
- (0, import_node_path12.join)(base, "..", "assets", "report"),
13066
- (0, import_node_path12.join)(base, "..", "..", "assets", "report"),
13067
- (0, import_node_path12.join)(process.cwd(), "packages", "cli", "assets", "report"),
13068
- (0, import_node_path12.join)(process.cwd(), "assets", "report")
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")
13069
13377
  ];
13070
13378
  for (const dir of candidates) {
13071
- 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"))) {
13072
13380
  return dir;
13073
13381
  }
13074
13382
  }
13075
13383
  throw new Error("Report assets missing. Run `npm run sync-report-assets` and `npm run build` in packages/cli.");
13076
13384
  }
13077
- var import_node_fs7, import_node_path12, REPORT_ASSET_FILES;
13385
+ var import_node_fs8, import_node_path13, REPORT_ASSET_FILES;
13078
13386
  var init_reportAssets = __esm({
13079
13387
  "src/report/reportAssets.ts"() {
13080
13388
  "use strict";
13081
- import_node_fs7 = require("node:fs");
13082
- import_node_path12 = require("node:path");
13389
+ import_node_fs8 = require("node:fs");
13390
+ import_node_path13 = require("node:path");
13083
13391
  REPORT_ASSET_FILES = [
13084
13392
  "station.css",
13085
13393
  "station.js",
@@ -13267,9 +13575,10 @@ function deriveRepoCodeAction(task) {
13267
13575
  gapId: task.gapId,
13268
13576
  readiness: [...askFirst ? [askFirst] : [], task.exactFix ?? "Repo-code fix is available"],
13269
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}` },
13270
- 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,
13271
13579
  verifyCommand: verifyActionCommand("pending"),
13272
- 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] } : {},
13273
13582
  mcpTool: task.mcpTool,
13274
13583
  mcpArgs: task.mcpArgs,
13275
13584
  // Only a gap with an automatic recipe has a heal-apply fallback, and a task that needs the user's yes keeps it
@@ -13278,7 +13587,7 @@ function deriveRepoCodeAction(task) {
13278
13587
  ...task.requiresUserAction ? { requiresUserAction: true } : {}
13279
13588
  };
13280
13589
  }
13281
- function deriveVerifyAction() {
13590
+ function deriveVerifyAction(waiting) {
13282
13591
  return {
13283
13592
  id: "pending",
13284
13593
  actionKey: buildActionKey({
@@ -13290,8 +13599,9 @@ function deriveVerifyAction() {
13290
13599
  revision: 1,
13291
13600
  kind: "verify",
13292
13601
  title: "Run Final Verification",
13293
- status: "blocked",
13294
- 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"],
13295
13605
  target: { type: "command", label: "Final strict gate", command: `${PUBLIC_COMMAND} --strict` },
13296
13606
  verifyCommand: `${PUBLIC_COMMAND} --strict`,
13297
13607
  resumeInstruction: "Final verification finished. Continue VibeRaven from pending."
@@ -13299,7 +13609,7 @@ function deriveVerifyAction() {
13299
13609
  }
13300
13610
  function deriveCurrentActions(input) {
13301
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));
13302
- return [...primaryActions, deriveVerifyAction()];
13612
+ return [...primaryActions, deriveVerifyAction(primaryActions.length > 0)];
13303
13613
  }
13304
13614
  var init_derive = __esm({
13305
13615
  "src/actions/derive.ts"() {
@@ -13314,7 +13624,7 @@ function emptyActionRegistry() {
13314
13624
  return { version: 1, nextId: 1, actions: {} };
13315
13625
  }
13316
13626
  function actionRegistryPath(cwd) {
13317
- return (0, import_node_path13.join)(getProjectArtifactsDir(cwd), "action-registry.json");
13627
+ return (0, import_node_path14.join)(getProjectArtifactsDir(cwd), "action-registry.json");
13318
13628
  }
13319
13629
  async function loadActionRegistry(cwd) {
13320
13630
  try {
@@ -13386,7 +13696,8 @@ async function assignActionIds(input) {
13386
13696
  title: action.title,
13387
13697
  gapId: action.gapId,
13388
13698
  kind: action.kind,
13389
- provider: action.provider
13699
+ provider: action.provider,
13700
+ ...action.userSteps?.length ? { userSteps: [...action.userSteps] } : {}
13390
13701
  };
13391
13702
  } else {
13392
13703
  entry.status = "active";
@@ -13395,6 +13706,9 @@ async function assignActionIds(input) {
13395
13706
  entry.gapId = action.gapId;
13396
13707
  entry.kind = action.kind;
13397
13708
  entry.provider = action.provider;
13709
+ if (action.userSteps?.length) entry.userSteps = [...action.userSteps];
13710
+ else delete entry.userSteps;
13711
+ delete entry.followUpShownAt;
13398
13712
  if (entry.fingerprint !== fingerprint) {
13399
13713
  entry.revision += 1;
13400
13714
  entry.fingerprint = fingerprint;
@@ -13418,16 +13732,27 @@ async function assignActionIds(input) {
13418
13732
  await saveActionRegistry(input.cwd, registry);
13419
13733
  return { actions, registry };
13420
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
+ }
13421
13746
  async function resolveActionById(cwd, id) {
13422
13747
  const registry = await loadActionRegistry(cwd);
13423
13748
  return Object.values(registry.actions).find((entry) => entry.id === id);
13424
13749
  }
13425
- var import_promises9, import_node_path13;
13750
+ var import_promises9, import_node_path14;
13426
13751
  var init_registry2 = __esm({
13427
13752
  "src/actions/registry.ts"() {
13428
13753
  "use strict";
13429
13754
  import_promises9 = require("node:fs/promises");
13430
- import_node_path13 = require("node:path");
13755
+ import_node_path14 = require("node:path");
13431
13756
  init_config();
13432
13757
  init_canonical();
13433
13758
  }
@@ -13438,7 +13763,7 @@ function gateStatus2(artifact) {
13438
13763
  if (!Array.isArray(artifact.gaps)) {
13439
13764
  return "unknown";
13440
13765
  }
13441
- return artifact.gaps.length === 0 ? "clear" : "not_clear";
13766
+ return gateStatusForGaps(artifact.gaps);
13442
13767
  }
13443
13768
  async function writeActionArtifacts(input) {
13444
13769
  const derived = deriveCurrentActions({
@@ -13459,7 +13784,9 @@ async function writeActionArtifacts(input) {
13459
13784
  gateStatus: gateStatus2(input.artifact),
13460
13785
  actions: assigned.actions
13461
13786
  };
13462
- 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");
13463
13790
  await (0, import_promises10.writeFile)(actionsPath, `${JSON.stringify(manifest, null, 2)}
13464
13791
  `, "utf8");
13465
13792
  return {
@@ -13468,13 +13795,14 @@ async function writeActionArtifacts(input) {
13468
13795
  manifest
13469
13796
  };
13470
13797
  }
13471
- var import_promises10, import_node_path14;
13798
+ var import_promises10, import_node_path15;
13472
13799
  var init_artifacts = __esm({
13473
13800
  "src/actions/artifacts.ts"() {
13474
13801
  "use strict";
13475
13802
  import_promises10 = require("node:fs/promises");
13476
- import_node_path14 = require("node:path");
13803
+ import_node_path15 = require("node:path");
13477
13804
  init_config();
13805
+ init_gateResult();
13478
13806
  init_derive();
13479
13807
  init_registry2();
13480
13808
  }
@@ -13483,24 +13811,47 @@ var init_artifacts = __esm({
13483
13811
  // src/artifacts.ts
13484
13812
  async function copyReportAssets(reportAssetsDir) {
13485
13813
  const sourceDir = getBundledReportAssetsDir();
13486
- 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 });
13487
13815
  for (const rel of REPORT_ASSET_FILES) {
13488
- await (0, import_promises11.copyFile)((0, import_node_path15.join)(sourceDir, rel), (0, import_node_path15.join)(reportAssetsDir, rel));
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
+ }
13489
13840
  }
13490
13841
  }
13491
13842
  async function writeScanArtifacts(options) {
13492
13843
  const cwd = options.cwd ?? options.artifact.workspacePath;
13493
13844
  const dir = getProjectArtifactsDir(cwd);
13494
13845
  await (0, import_promises11.mkdir)(dir, { recursive: true });
13495
- const jsonPath = (0, import_node_path15.join)(dir, "last-scan.json");
13496
- const gateResultPath = (0, import_node_path15.join)(dir, "gate-result.json");
13497
- const contextMapPath = (0, import_node_path15.join)(dir, "context-map.json");
13498
- const gapsDir = (0, import_node_path15.join)(dir, "gaps");
13499
- const tasklistPath = (0, import_node_path15.join)(dir, "agent-tasklist.md");
13500
- const summaryPath = (0, import_node_path15.join)(dir, "agent-summary.md");
13501
- const playbookPath = (0, import_node_path15.join)(dir, "launch-playbook.md");
13502
- const reportPath = (0, import_node_path15.join)(dir, "report.html");
13503
- const reportAssetsDir = (0, import_node_path15.join)(dir, "report");
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");
13504
13855
  await (0, import_promises11.mkdir)(gapsDir, { recursive: true });
13505
13856
  const safe2 = sanitizeArtifactForDisk(options.artifact);
13506
13857
  const json = `${JSON.stringify(safe2, null, 2)}
@@ -13510,26 +13861,27 @@ async function writeScanArtifacts(options) {
13510
13861
  const html = generateReportHtml(safe2);
13511
13862
  const tasks = buildTaskList(safe2);
13512
13863
  const tasklist = buildTaskListMarkdown(tasks);
13513
- 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)}
13514
13866
  `;
13515
- const contextMap = `${JSON.stringify(generateContextMap(safe2), null, 2)}
13867
+ const contextMap = `${JSON.stringify(generateContextMap(safe2, { packageManager }), null, 2)}
13516
13868
  `;
13517
13869
  const gapEvidenceFiles = generateGapEvidenceFiles(safe2);
13518
13870
  await copyReportAssets(reportAssetsDir);
13519
13871
  await (0, import_promises11.writeFile)(gateResultPath, gateResult, "utf-8");
13520
13872
  await (0, import_promises11.writeFile)(contextMapPath, contextMap, "utf-8");
13521
13873
  for (const gap of gapEvidenceFiles) {
13522
- await (0, import_promises11.writeFile)((0, import_node_path15.join)(dir, "gaps", `${gap.content.id}.json`), `${JSON.stringify(gap.content, null, 2)}
13874
+ await (0, import_promises11.writeFile)((0, import_node_path16.join)(dir, "gaps", `${gap.content.id}.json`), `${JSON.stringify(gap.content, null, 2)}
13523
13875
  `, "utf-8");
13524
13876
  }
13525
13877
  await (0, import_promises11.writeFile)(tasklistPath, tasklist, "utf-8");
13526
13878
  let architectureMapPath;
13527
13879
  if (typeof safe2.architectureMap === "string" && safe2.architectureMap.length > 0) {
13528
- architectureMapPath = (0, import_node_path15.join)(dir, "architecture-map.md");
13880
+ architectureMapPath = (0, import_node_path16.join)(dir, "architecture-map.md");
13529
13881
  await (0, import_promises11.writeFile)(architectureMapPath, safe2.architectureMap, "utf-8");
13530
13882
  }
13531
13883
  if (safe2.architectureData) {
13532
- 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)}
13533
13885
  `, "utf-8");
13534
13886
  }
13535
13887
  await (0, import_promises11.writeFile)(jsonPath, json, "utf-8");
@@ -13542,6 +13894,7 @@ async function writeScanArtifacts(options) {
13542
13894
  tasks,
13543
13895
  paths: { reportPath, playbookPath }
13544
13896
  });
13897
+ await removeStaleGapFiles(gapsDir, new Set(gapEvidenceFiles.map((gap) => `${gap.content.id}.json`)));
13545
13898
  return {
13546
13899
  dir,
13547
13900
  jsonPath,
@@ -13558,14 +13911,15 @@ async function writeScanArtifacts(options) {
13558
13911
  ...architectureMapPath ? { architectureMapPath } : {}
13559
13912
  };
13560
13913
  }
13561
- var import_promises11, import_node_path15;
13914
+ var import_promises11, import_node_path16, GAP_SCHEMA;
13562
13915
  var init_artifacts2 = __esm({
13563
13916
  "src/artifacts.ts"() {
13564
13917
  "use strict";
13565
13918
  import_promises11 = require("node:fs/promises");
13566
- import_node_path15 = require("node:path");
13919
+ import_node_path16 = require("node:path");
13567
13920
  init_contextMap();
13568
13921
  init_gateResult();
13922
+ init_packageManager();
13569
13923
  init_gapEvidence();
13570
13924
  init_agentSummary();
13571
13925
  init_buildTaskList();
@@ -13575,6 +13929,7 @@ var init_artifacts2 = __esm({
13575
13929
  init_config();
13576
13930
  init_sanitizeArtifact();
13577
13931
  init_artifacts();
13932
+ GAP_SCHEMA = "https://viberaven.dev/schemas/gap.schema.json";
13578
13933
  }
13579
13934
  });
13580
13935
 
@@ -13672,12 +14027,12 @@ function createAppWindowOpenCommands(target, platform = process.platform, env =
13672
14027
  ];
13673
14028
  if (platform === "win32") {
13674
14029
  const candidates = [
13675
- env.LOCALAPPDATA ? (0, import_node_path16.join)(env.LOCALAPPDATA, "Google", "Chrome", "Application", "chrome.exe") : "",
13676
- env.PROGRAMFILES ? (0, import_node_path16.join)(env.PROGRAMFILES, "Google", "Chrome", "Application", "chrome.exe") : "",
13677
- env["PROGRAMFILES(X86)"] ? (0, import_node_path16.join)(env["PROGRAMFILES(X86)"], "Google", "Chrome", "Application", "chrome.exe") : "",
13678
- env.LOCALAPPDATA ? (0, import_node_path16.join)(env.LOCALAPPDATA, "Microsoft", "Edge", "Application", "msedge.exe") : "",
13679
- env.PROGRAMFILES ? (0, import_node_path16.join)(env.PROGRAMFILES, "Microsoft", "Edge", "Application", "msedge.exe") : "",
13680
- env["PROGRAMFILES(X86)"] ? (0, import_node_path16.join)(env["PROGRAMFILES(X86)"], "Microsoft", "Edge", "Application", "msedge.exe") : "",
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") : "",
13681
14036
  "chrome",
13682
14037
  "msedge"
13683
14038
  ].filter(Boolean);
@@ -13711,7 +14066,7 @@ async function openWithSystemDefault(target) {
13711
14066
  });
13712
14067
  }
13713
14068
  async function spawnDetached(command) {
13714
- 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)) {
13715
14070
  throw new Error(`Browser not found: ${command.command}`);
13716
14071
  }
13717
14072
  await new Promise((resolve10, reject) => {
@@ -13754,13 +14109,13 @@ async function openUrlInBrowser(url) {
13754
14109
  }
13755
14110
  await openWithSystemDefault(trimmed);
13756
14111
  }
13757
- var import_node_child_process4, import_node_fs8, import_node_path16;
14112
+ var import_node_child_process4, import_node_fs9, import_node_path17;
13758
14113
  var init_openBrowser = __esm({
13759
14114
  "src/openBrowser.ts"() {
13760
14115
  "use strict";
13761
14116
  import_node_child_process4 = require("node:child_process");
13762
- import_node_fs8 = require("node:fs");
13763
- import_node_path16 = require("node:path");
14117
+ import_node_fs9 = require("node:fs");
14118
+ import_node_path17 = require("node:path");
13764
14119
  }
13765
14120
  });
13766
14121
 
@@ -13817,9 +14172,9 @@ var init_clipboard = __esm({
13817
14172
  }
13818
14173
  });
13819
14174
 
13820
- // ../../../../../VibeRice/packages/cli/node_modules/picocolors/picocolors.js
14175
+ // node_modules/picocolors/picocolors.js
13821
14176
  var require_picocolors = __commonJS({
13822
- "../../../../../VibeRice/packages/cli/node_modules/picocolors/picocolors.js"(exports2, module2) {
14177
+ "node_modules/picocolors/picocolors.js"(exports2, module2) {
13823
14178
  var p2 = process || {};
13824
14179
  var argv = p2.argv || [];
13825
14180
  var env = p2.env || {};
@@ -13889,46 +14244,6 @@ var require_picocolors = __commonJS({
13889
14244
  }
13890
14245
  });
13891
14246
 
13892
- // src/report/missionSelection.ts
13893
- function normalizeProviderToken2(value) {
13894
- return value.toLowerCase().replace(/&/g, "and").replace(/[^a-z0-9]+/g, "");
13895
- }
13896
- function missionMatchesProvider(mission, provider2) {
13897
- const current = normalizeProviderToken2(provider2);
13898
- return normalizeProviderToken2(mission.provider || mission.providerLabel || mission.key) === current || normalizeProviderToken2(mission.providerLabel || mission.provider || mission.key) === current;
13899
- }
13900
- function missionEvidenceScore(mission) {
13901
- const repoVerified = mission.checks.filter((check) => check.evidenceClass === "repo-verified" || check.status === "passed").length;
13902
- const missing = mission.checks.filter(
13903
- (check) => check.evidenceClass === "missing-repo-fix" || check.status === "missing" || check.status === "failed"
13904
- ).length;
13905
- return repoVerified * 100 + (mission.readinessPercent ?? 0) - missing;
13906
- }
13907
- function preferredMissionForArea(area, selectedProvider) {
13908
- const missions = area?.providerMissions ?? [];
13909
- if (missions.length === 0) {
13910
- return void 0;
13911
- }
13912
- const selected = selectedProvider ? missions.find((mission) => missionMatchesProvider(mission, selectedProvider)) : void 0;
13913
- if (selected) {
13914
- return selected;
13915
- }
13916
- return [...missions].sort((a, b3) => missionEvidenceScore(b3) - missionEvidenceScore(a))[0] ?? missions[0];
13917
- }
13918
- function openChecksForMission(mission) {
13919
- if (!mission) {
13920
- return 0;
13921
- }
13922
- return mission.checks.filter(
13923
- (check) => check.status === "missing" || check.status === "failed" || check.status === "needs-connection"
13924
- ).length;
13925
- }
13926
- var init_missionSelection = __esm({
13927
- "src/report/missionSelection.ts"() {
13928
- "use strict";
13929
- }
13930
- });
13931
-
13932
14247
  // src/statusLabels.ts
13933
14248
  function formatAgentStatus(label, message) {
13934
14249
  return `${label}: ${message}`;
@@ -13970,7 +14285,7 @@ function gapTagColor(modelGaps, open2) {
13970
14285
  }
13971
14286
  function manualActionCheckCount(artifact) {
13972
14287
  return (artifact.missionGraph.areas ?? []).reduce((areaTotal, area) => {
13973
- return areaTotal + area.providerMissions.reduce((missionTotal, mission) => {
14288
+ return areaTotal + inRepoMissions(artifact, area).reduce((missionTotal, mission) => {
13974
14289
  return missionTotal + mission.checks.filter(
13975
14290
  (check) => check.evidenceClass === "manual-dashboard" || check.evidenceClass === "mcp-verifier" || check.evidenceSource === "provider" || check.evidenceSource === "mcp" || check.status === "needs-connection" || check.status === "unknown"
13976
14291
  ).length;
@@ -13991,8 +14306,14 @@ function printScanSummary(artifact, paths) {
13991
14306
  console.log(import_picocolors.default.dim("\u2514") + import_picocolors.default.dim("\u2500".repeat(width - 2)) + import_picocolors.default.dim("\u2518"));
13992
14307
  console.log("");
13993
14308
  for (const area of artifact.missionGraph.areas ?? []) {
13994
- const mission = preferredMissionForArea(area, artifact.selectedProviders?.[area.key] ?? "");
14309
+ if ((area.providerMissions ?? []).length === 0) {
14310
+ continue;
14311
+ }
14312
+ const mission = preferredInRepoMission(artifact, area);
13995
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}`);
13996
14317
  continue;
13997
14318
  }
13998
14319
  const open2 = openChecksForMission(mission);
@@ -14038,14 +14359,15 @@ var init_terminalSummary = __esm({
14038
14359
  "use strict";
14039
14360
  import_picocolors = __toESM(require_picocolors());
14040
14361
  init_missionSelection();
14362
+ init_inRepoMissions();
14041
14363
  init_statusLabels();
14042
14364
  init_resolveNextAction();
14043
14365
  }
14044
14366
  });
14045
14367
 
14046
- // ../../../../../VibeRice/packages/cli/node_modules/sisteransi/src/index.js
14368
+ // node_modules/sisteransi/src/index.js
14047
14369
  var require_src = __commonJS({
14048
- "../../../../../VibeRice/packages/cli/node_modules/sisteransi/src/index.js"(exports2, module2) {
14370
+ "node_modules/sisteransi/src/index.js"(exports2, module2) {
14049
14371
  "use strict";
14050
14372
  var ESC = "\x1B";
14051
14373
  var CSI = `${ESC}[`;
@@ -14099,7 +14421,7 @@ var require_src = __commonJS({
14099
14421
  }
14100
14422
  });
14101
14423
 
14102
- // ../../../../../VibeRice/packages/cli/node_modules/@clack/core/dist/index.mjs
14424
+ // node_modules/@clack/core/dist/index.mjs
14103
14425
  function DD({ onlyFirst: e2 = false } = {}) {
14104
14426
  const t = ["[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?(?:\\u0007|\\u001B\\u005C|\\u009C))", "(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]))"].join("|");
14105
14427
  return new RegExp(t, e2 ? void 0 : "g");
@@ -14211,7 +14533,7 @@ function fD({ input: e2 = import_node_process.stdin, output: u2 = import_node_pr
14211
14533
  }
14212
14534
  var import_sisteransi, import_node_process, g, import_node_readline, import_node_stream, uD, W, tD, eD, FD, sD, w, N, I, R, C, iD, rD, ED, d, oD, y, V, nD, G, _, z, K, aD, k, hD, lD, xD, B, AD, S, gD, vD, h, x, A, OD, PD, J, LD;
14213
14535
  var init_dist = __esm({
14214
- "../../../../../VibeRice/packages/cli/node_modules/@clack/core/dist/index.mjs"() {
14536
+ "node_modules/@clack/core/dist/index.mjs"() {
14215
14537
  import_sisteransi = __toESM(require_src(), 1);
14216
14538
  import_node_process = require("node:process");
14217
14539
  g = __toESM(require("node:readline"), 1);
@@ -14461,13 +14783,13 @@ var init_dist = __esm({
14461
14783
  }
14462
14784
  });
14463
14785
 
14464
- // ../../../../../VibeRice/packages/cli/node_modules/@clack/prompts/dist/index.mjs
14786
+ // node_modules/@clack/prompts/dist/index.mjs
14465
14787
  function ce() {
14466
14788
  return import_node_process2.default.platform !== "win32" ? import_node_process2.default.env.TERM !== "linux" : !!import_node_process2.default.env.CI || !!import_node_process2.default.env.WT_SESSION || !!import_node_process2.default.env.TERMINUS_SUBLIME || import_node_process2.default.env.ConEmuTask === "{cmd::Cmder}" || import_node_process2.default.env.TERM_PROGRAM === "Terminus-Sublime" || import_node_process2.default.env.TERM_PROGRAM === "vscode" || import_node_process2.default.env.TERM === "xterm-256color" || import_node_process2.default.env.TERM === "alacritty" || import_node_process2.default.env.TERMINAL_EMULATOR === "JetBrains-JediTerm";
14467
14789
  }
14468
14790
  var import_node_process2, import_picocolors2, import_sisteransi2, V2, u, le, L2, W2, C2, ue, o, d2, k2, P2, A2, T, F, $e, _2, me, de, pe, q, D, U, K2, b2, G2, ve, xe, Ie, Se, M2, J2, Y2;
14469
14791
  var init_dist2 = __esm({
14470
- "../../../../../VibeRice/packages/cli/node_modules/@clack/prompts/dist/index.mjs"() {
14792
+ "node_modules/@clack/prompts/dist/index.mjs"() {
14471
14793
  init_dist();
14472
14794
  init_dist();
14473
14795
  import_node_process2 = __toESM(require("node:process"), 1);
@@ -14700,7 +15022,7 @@ var VERSION;
14700
15022
  var init_version = __esm({
14701
15023
  "src/version.ts"() {
14702
15024
  "use strict";
14703
- VERSION = "1.6.2";
15025
+ VERSION = "1.6.3";
14704
15026
  }
14705
15027
  });
14706
15028
 
@@ -15072,17 +15394,17 @@ async function initCursorRulesPack(options) {
15072
15394
  const pack = buildCursorRulesPack();
15073
15395
  for (const rule of pack) {
15074
15396
  const file = `${CURSOR_RULES_DIR}/${rule.filename}`;
15075
- const path = (0, import_node_path17.join)(options.cwd, file);
15397
+ const path = (0, import_node_path18.join)(options.cwd, file);
15076
15398
  const existing = await readExistingFile(path);
15077
15399
  const changed = !existing.exists || existing.content !== rule.content;
15078
15400
  const action = !existing.exists ? "created" : changed ? "updated" : "unchanged";
15079
15401
  if (!options.dryRun && changed) {
15080
- 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 });
15081
15403
  await (0, import_promises12.writeFile)(path, rule.content, "utf-8");
15082
15404
  }
15083
15405
  results.push({ target: "cursor", file, path, action });
15084
15406
  }
15085
- 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);
15086
15408
  if (!options.dryRun && await fileExists(legacyPath)) {
15087
15409
  await (0, import_promises12.rm)(legacyPath, { force: true });
15088
15410
  }
@@ -15109,12 +15431,12 @@ async function fileExists(path) {
15109
15431
  function isFileNotFoundError(error) {
15110
15432
  return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
15111
15433
  }
15112
- 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;
15113
15435
  var init_cursorRulesPack = __esm({
15114
15436
  "src/commands/cursorRulesPack.ts"() {
15115
15437
  "use strict";
15116
15438
  import_promises12 = require("node:fs/promises");
15117
- import_node_path17 = require("node:path");
15439
+ import_node_path18 = require("node:path");
15118
15440
  init_commands();
15119
15441
  CURSOR_RULES_DIR = ".cursor/rules";
15120
15442
  LEGACY_CURSOR_RULE_FILE = `${CURSOR_RULES_DIR}/viberaven.mdc`;
@@ -15214,8 +15536,8 @@ var init_agentTargets = __esm({
15214
15536
 
15215
15537
  // src/commands/seedPackageJsonScripts.ts
15216
15538
  async function seedPackageJsonScripts(options) {
15217
- const packageJsonPath = (0, import_node_path18.join)(options.cwd, "package.json");
15218
- 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)) {
15219
15541
  return null;
15220
15542
  }
15221
15543
  const raw = await (0, import_promises13.readFile)(packageJsonPath, "utf-8");
@@ -15252,13 +15574,13 @@ async function seedPackageJsonScripts(options) {
15252
15574
  }
15253
15575
  return { action: "updated", added, skipped, changed: true };
15254
15576
  }
15255
- 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;
15256
15578
  var init_seedPackageJsonScripts = __esm({
15257
15579
  "src/commands/seedPackageJsonScripts.ts"() {
15258
15580
  "use strict";
15259
- import_node_fs9 = require("node:fs");
15581
+ import_node_fs10 = require("node:fs");
15260
15582
  import_promises13 = require("node:fs/promises");
15261
- import_node_path18 = require("node:path");
15583
+ import_node_path19 = require("node:path");
15262
15584
  init_commands();
15263
15585
  VIBERAVEN_PACKAGE_JSON_SCRIPTS = {
15264
15586
  "viberaven:gate": PUBLIC_AGENT_MODE_COMMAND,
@@ -15278,12 +15600,12 @@ async function initAgentRules(options) {
15278
15600
  continue;
15279
15601
  }
15280
15602
  const file = AGENT_RULE_TARGETS[target].file;
15281
- const path = (0, import_node_path19.join)(options.cwd, file);
15603
+ const path = (0, import_node_path20.join)(options.cwd, file);
15282
15604
  const existing = await readExistingFile2(path);
15283
15605
  const injected = injectAgentRulesBlock(existing.content, renderAgentRulesForTarget(target));
15284
15606
  const action = !existing.exists ? "created" : injected.changed ? "updated" : "unchanged";
15285
15607
  if (!options.dryRun && injected.changed) {
15286
- 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 });
15287
15609
  await (0, import_promises14.writeFile)(path, injected.content, "utf-8");
15288
15610
  }
15289
15611
  results.push({ target, file, path, action });
@@ -15363,12 +15685,12 @@ async function readExistingFile2(path) {
15363
15685
  function isFileNotFoundError2(error) {
15364
15686
  return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
15365
15687
  }
15366
- var import_promises14, import_node_path19;
15688
+ var import_promises14, import_node_path20;
15367
15689
  var init_initRules = __esm({
15368
15690
  "src/commands/initRules.ts"() {
15369
15691
  "use strict";
15370
15692
  import_promises14 = require("node:fs/promises");
15371
- import_node_path19 = require("node:path");
15693
+ import_node_path20 = require("node:path");
15372
15694
  init_agentRulesBlock();
15373
15695
  init_agentTargets();
15374
15696
  init_cursorRulesPack();
@@ -15631,7 +15953,7 @@ async function runInteractiveSession(startDir = process.cwd()) {
15631
15953
  Ie(`${import_picocolors4.default.bold("VibeRaven")} ${import_picocolors4.default.dim(VERSION)}`);
15632
15954
  const cwd = await resolveWorkspaceRoot(startDir);
15633
15955
  const artifactsAt = await findArtifactsWorkspace(startDir);
15634
- 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)) {
15635
15957
  M2.message(import_picocolors4.default.dim(`Using scan from: ${artifactsAt}`));
15636
15958
  } else {
15637
15959
  M2.message(import_picocolors4.default.dim(`Project folder: ${cwd}`));
@@ -15690,11 +16012,11 @@ async function runInteractiveSession(startDir = process.cwd()) {
15690
16012
  }
15691
16013
  Se(import_picocolors4.default.dim("Run viberaven anytime for the interactive menu."));
15692
16014
  }
15693
- var import_node_path20, import_picocolors4;
16015
+ var import_node_path21, import_picocolors4;
15694
16016
  var init_runInteractive = __esm({
15695
16017
  "src/tui/runInteractive.ts"() {
15696
16018
  "use strict";
15697
- import_node_path20 = require("node:path");
16019
+ import_node_path21 = require("node:path");
15698
16020
  init_dist2();
15699
16021
  import_picocolors4 = __toESM(require_picocolors());
15700
16022
  init_config();
@@ -15719,18 +16041,18 @@ var init_runInteractive = __esm({
15719
16041
  // src/commands/condense.ts
15720
16042
  async function runCondenseCommand(options) {
15721
16043
  const dir = getProjectArtifactsDir(options.cwd);
15722
- const artifact = JSON.parse(await (0, import_promises15.readFile)((0, import_node_path21.join)(dir, "last-scan.json"), "utf8"));
15723
- const contextMapPath = (0, import_node_path21.join)(dir, "context-map.json");
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");
15724
16046
  await (0, import_promises15.writeFile)(contextMapPath, `${JSON.stringify(generateContextMap(artifact), null, 2)}
15725
16047
  `, "utf8");
15726
16048
  return { contextMapPath };
15727
16049
  }
15728
- var import_promises15, import_node_path21;
16050
+ var import_promises15, import_node_path22;
15729
16051
  var init_condense = __esm({
15730
16052
  "src/commands/condense.ts"() {
15731
16053
  "use strict";
15732
16054
  import_promises15 = require("node:fs/promises");
15733
- import_node_path21 = require("node:path");
16055
+ import_node_path22 = require("node:path");
15734
16056
  init_config();
15735
16057
  init_contextMap();
15736
16058
  }
@@ -15738,9 +16060,9 @@ var init_condense = __esm({
15738
16060
 
15739
16061
  // src/heal/pathSafety.ts
15740
16062
  function assertSafeHealTarget(cwd, target) {
15741
- const root = (0, import_node_path22.resolve)(cwd);
15742
- const absolute = (0, import_node_path22.resolve)(root, target);
15743
- const rel = (0, import_node_path22.relative)(root, absolute);
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);
15744
16066
  if (rel.startsWith("..") || rel === "" || /^[A-Za-z]:/.test(rel)) {
15745
16067
  throw new Error("Heal target must stay inside the workspace");
15746
16068
  }
@@ -15750,11 +16072,11 @@ function assertSafeHealTarget(cwd, target) {
15750
16072
  }
15751
16073
  return absolute;
15752
16074
  }
15753
- var import_node_path22, BLOCKED_SEGMENTS;
16075
+ var import_node_path23, BLOCKED_SEGMENTS;
15754
16076
  var init_pathSafety = __esm({
15755
16077
  "src/heal/pathSafety.ts"() {
15756
16078
  "use strict";
15757
- import_node_path22 = require("node:path");
16079
+ import_node_path23 = require("node:path");
15758
16080
  BLOCKED_SEGMENTS = /* @__PURE__ */ new Set([".git", "node_modules", "dist", "build", ".next", ".viberaven"]);
15759
16081
  }
15760
16082
  });
@@ -15780,11 +16102,11 @@ function newHealId(now = /* @__PURE__ */ new Date()) {
15780
16102
  return `heal_${now.toISOString().replace(/\D/g, "").slice(0, 17)}_${(0, import_node_crypto.randomBytes)(3).toString("hex")}`;
15781
16103
  }
15782
16104
  async function createHealDir(cwd, makeId = () => newHealId()) {
15783
- const parent = (0, import_node_path23.join)(cwd, ".viberaven", "heal");
16105
+ const parent = (0, import_node_path24.join)(cwd, ".viberaven", "heal");
15784
16106
  await (0, import_promises16.mkdir)(parent, { recursive: true });
15785
16107
  for (let attempt = 0; attempt < 10; attempt += 1) {
15786
16108
  const id = makeId();
15787
- const dir = (0, import_node_path23.join)(parent, id);
16109
+ const dir = (0, import_node_path24.join)(parent, id);
15788
16110
  try {
15789
16111
  await (0, import_promises16.mkdir)(dir);
15790
16112
  return { id, dir };
@@ -15794,13 +16116,13 @@ async function createHealDir(cwd, makeId = () => newHealId()) {
15794
16116
  }
15795
16117
  throw new Error("Could not create a new heal directory under .viberaven/heal.");
15796
16118
  }
15797
- var import_node_crypto, import_promises16, import_node_path23;
16119
+ var import_node_crypto, import_promises16, import_node_path24;
15798
16120
  var init_healId = __esm({
15799
16121
  "src/heal/healId.ts"() {
15800
16122
  "use strict";
15801
16123
  import_node_crypto = require("node:crypto");
15802
16124
  import_promises16 = require("node:fs/promises");
15803
- import_node_path23 = require("node:path");
16125
+ import_node_path24 = require("node:path");
15804
16126
  }
15805
16127
  });
15806
16128
 
@@ -16267,7 +16589,7 @@ export const config = {
16267
16589
  // src/heal/recipes/eslintRestrictedImports.ts
16268
16590
  function detectEslintConfigFile(cwd) {
16269
16591
  for (const candidate of ESLINT_CONFIG_CANDIDATES) {
16270
- 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))) {
16271
16593
  return candidate;
16272
16594
  }
16273
16595
  }
@@ -16422,12 +16744,12 @@ function applyEslintRestrictedImportsRecipe(source, configFile) {
16422
16744
  }
16423
16745
  return applyLegacyModuleRecipe(source);
16424
16746
  }
16425
- 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;
16426
16748
  var init_eslintRestrictedImports = __esm({
16427
16749
  "src/heal/recipes/eslintRestrictedImports.ts"() {
16428
16750
  "use strict";
16429
- import_node_fs10 = require("node:fs");
16430
- import_node_path24 = require("node:path");
16751
+ import_node_fs11 = require("node:fs");
16752
+ import_node_path25 = require("node:path");
16431
16753
  init_commands();
16432
16754
  VIBERAVEN_ESLINT_MARKER = "VibeRaven heal: eslint_restricted_imports";
16433
16755
  RESTRICTED_IMPORTS_MESSAGE = `Restricted import. Run ${PUBLIC_AGENT_MODE_COMMAND} before substituting packages.`;
@@ -16478,13 +16800,13 @@ function defaultTargetFile(gapId) {
16478
16800
  return map[gapId];
16479
16801
  }
16480
16802
  async function readSourceOrEmpty(absolutePath) {
16481
- if (!(0, import_node_fs11.existsSync)(absolutePath)) return "";
16803
+ if (!(0, import_node_fs12.existsSync)(absolutePath)) return "";
16482
16804
  return (0, import_promises17.readFile)(absolutePath, "utf8");
16483
16805
  }
16484
16806
  async function detectUpstash(cwd) {
16485
16807
  try {
16486
- const pkgPath = (0, import_node_path25.join)(cwd, "package.json");
16487
- 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;
16488
16810
  const raw = await (0, import_promises17.readFile)(pkgPath, "utf8");
16489
16811
  const pkg = JSON.parse(raw);
16490
16812
  const deps = {
@@ -16500,7 +16822,7 @@ async function dispatchRecipeByGapId(gapId, cwd, explicitTarget) {
16500
16822
  const targetFile = explicitTarget ?? defaultTargetFile(gapId);
16501
16823
  if (targetFile === void 0) return null;
16502
16824
  if (gapId === "auth_secret_missing" || gapId === "node_env_not_set" || gapId === "database_url_missing") {
16503
- const absolutePath = (0, import_node_path25.join)(cwd, targetFile);
16825
+ const absolutePath = (0, import_node_path26.join)(cwd, targetFile);
16504
16826
  const source = await readSourceOrEmpty(absolutePath);
16505
16827
  let result;
16506
16828
  if (gapId === "auth_secret_missing") result = applyAuthSecretRecipe(source);
@@ -16514,25 +16836,25 @@ async function dispatchRecipeByGapId(gapId, cwd, explicitTarget) {
16514
16836
  };
16515
16837
  }
16516
16838
  if (gapId === "missing_error_boundary") {
16517
- const absolutePath = (0, import_node_path25.join)(cwd, "app/error.tsx");
16839
+ const absolutePath = (0, import_node_path26.join)(cwd, "app/error.tsx");
16518
16840
  const source = await readSourceOrEmpty(absolutePath);
16519
16841
  const result = applyErrorBoundaryRecipe(source);
16520
16842
  return { ...result, canAutoApply: true, recipeName: gapId };
16521
16843
  }
16522
16844
  if (gapId === "missing_health_route") {
16523
- 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");
16524
16846
  const source = await readSourceOrEmpty(absolutePath);
16525
16847
  const result = applyHealthRouteRecipe(source);
16526
16848
  return { ...result, canAutoApply: true, recipeName: gapId };
16527
16849
  }
16528
16850
  if (gapId === "missing_loading_state") {
16529
- const absolutePath = (0, import_node_path25.join)(cwd, "app/loading.tsx");
16851
+ const absolutePath = (0, import_node_path26.join)(cwd, "app/loading.tsx");
16530
16852
  const source = await readSourceOrEmpty(absolutePath);
16531
16853
  const result = applyLoadingStateRecipe(source);
16532
16854
  return { ...result, canAutoApply: true, recipeName: gapId };
16533
16855
  }
16534
16856
  if (gapId === "missing_404_page") {
16535
- 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");
16536
16858
  const source = await readSourceOrEmpty(absolutePath);
16537
16859
  const result = applyNotFoundRecipe(source);
16538
16860
  return { ...result, canAutoApply: true, recipeName: gapId };
@@ -16549,10 +16871,10 @@ async function dispatchRecipeByGapId(gapId, cwd, explicitTarget) {
16549
16871
  }
16550
16872
  if (gapId === "missing_csp_header") {
16551
16873
  let configFile = "next.config.js";
16552
- let absolutePath = (0, import_node_path25.join)(cwd, configFile);
16553
- 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)) {
16554
16876
  configFile = "next.config.mjs";
16555
- absolutePath = (0, import_node_path25.join)(cwd, configFile);
16877
+ absolutePath = (0, import_node_path26.join)(cwd, configFile);
16556
16878
  }
16557
16879
  const source = await readSourceOrEmpty(absolutePath);
16558
16880
  const result = applyCspHeaderRecipe(source);
@@ -16564,7 +16886,7 @@ async function dispatchRecipeByGapId(gapId, cwd, explicitTarget) {
16564
16886
  }
16565
16887
  if (gapId === "missing_rate_limit") {
16566
16888
  const hasUpstash = await detectUpstash(cwd);
16567
- const middlewarePath = (0, import_node_path25.join)(cwd, "middleware.ts");
16889
+ const middlewarePath = (0, import_node_path26.join)(cwd, "middleware.ts");
16568
16890
  const source = await readSourceOrEmpty(middlewarePath);
16569
16891
  const result = applyRateLimitRecipe(source, hasUpstash);
16570
16892
  return {
@@ -16586,7 +16908,7 @@ async function dispatchRecipeByGapId(gapId, cwd, explicitTarget) {
16586
16908
  recipeName: gapId
16587
16909
  };
16588
16910
  }
16589
- const absolutePath = (0, import_node_path25.join)(cwd, configFile);
16911
+ const absolutePath = (0, import_node_path26.join)(cwd, configFile);
16590
16912
  const source = await readSourceOrEmpty(absolutePath);
16591
16913
  const result = applyEslintRestrictedImportsRecipe(source, configFile);
16592
16914
  return {
@@ -16600,13 +16922,13 @@ async function dispatchRecipeByGapId(gapId, cwd, explicitTarget) {
16600
16922
  }
16601
16923
  return null;
16602
16924
  }
16603
- var import_node_fs11, import_promises17, import_node_path25;
16925
+ var import_node_fs12, import_promises17, import_node_path26;
16604
16926
  var init_recipes = __esm({
16605
16927
  "src/heal/recipes/index.ts"() {
16606
16928
  "use strict";
16607
- import_node_fs11 = require("node:fs");
16929
+ import_node_fs12 = require("node:fs");
16608
16930
  import_promises17 = require("node:fs/promises");
16609
- import_node_path25 = require("node:path");
16931
+ import_node_path26 = require("node:path");
16610
16932
  init_envAuthSecret();
16611
16933
  init_envNodeEnv();
16612
16934
  init_envDatabaseUrl();
@@ -16683,16 +17005,16 @@ async function applyHeal(options) {
16683
17005
  rollback: { available: false, instructions: "Recipe matched but no change was needed (already applied or file already exists)." }
16684
17006
  };
16685
17007
  }
16686
- const absoluteTarget = (0, import_node_path26.join)(options.cwd, dispatched.targetFile);
17008
+ const absoluteTarget = (0, import_node_path27.join)(options.cwd, dispatched.targetFile);
16687
17009
  assertSafeHealTarget(options.cwd, dispatched.targetFile);
16688
- 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 });
16689
17011
  const created2 = await createHealDir(options.cwd);
16690
17012
  id = created2.id;
16691
17013
  const healDir2 = created2.dir;
16692
- await (0, import_promises18.mkdir)((0, import_node_path26.join)(healDir2, "before"));
16693
- const existedBefore = (0, import_node_fs12.existsSync)(absoluteTarget);
17014
+ await (0, import_promises18.mkdir)((0, import_node_path27.join)(healDir2, "before"));
17015
+ const existedBefore = (0, import_node_fs13.existsSync)(absoluteTarget);
16694
17016
  const beforeContent = existedBefore ? await (0, import_promises18.readFile)(absoluteTarget, "utf8") : "";
16695
- await (0, import_promises18.writeFile)((0, import_node_path26.join)(healDir2, "before", "target.txt"), beforeContent, "utf8");
17017
+ await (0, import_promises18.writeFile)((0, import_node_path27.join)(healDir2, "before", "target.txt"), beforeContent, "utf8");
16696
17018
  await (0, import_promises18.writeFile)(absoluteTarget, dispatched.output, "utf8");
16697
17019
  const patch2 = [
16698
17020
  `--- ${dispatched.targetFile}`,
@@ -16703,7 +17025,7 @@ async function applyHeal(options) {
16703
17025
  dispatched.output,
16704
17026
  ""
16705
17027
  ].join("\n");
16706
- 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");
16707
17029
  const result2 = {
16708
17030
  $schema: "https://viberaven.dev/schemas/heal-result.schema.json",
16709
17031
  schemaVersion: "v1",
@@ -16715,17 +17037,17 @@ async function applyHeal(options) {
16715
17037
  recipe: dispatched.recipeName,
16716
17038
  ...dispatched.warnings?.length ? { warnings: dispatched.warnings } : {},
16717
17039
  target: dispatched.targetFile,
16718
- changedFiles: [(0, import_node_path26.relative)(options.cwd, absoluteTarget).replace(/\\/g, "/")],
17040
+ changedFiles: [(0, import_node_path27.relative)(options.cwd, absoluteTarget).replace(/\\/g, "/")],
16719
17041
  artifacts: {
16720
17042
  patch: `.viberaven/heal/${id}/patch.diff`,
16721
17043
  result: `.viberaven/heal/${id}/result.json`
16722
17044
  },
16723
17045
  rollback: {
16724
17046
  available: true,
16725
- 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)
16726
17048
  }
16727
17049
  };
16728
- 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)}
16729
17051
  `, "utf8");
16730
17052
  return result2;
16731
17053
  }
@@ -16765,8 +17087,8 @@ async function applyHeal(options) {
16765
17087
  const created = await createHealDir(options.cwd);
16766
17088
  id = created.id;
16767
17089
  const healDir = created.dir;
16768
- await (0, import_promises18.mkdir)((0, import_node_path26.join)(healDir, "before"));
16769
- await (0, import_promises18.writeFile)((0, import_node_path26.join)(healDir, "before", "target.txt"), before, "utf8");
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");
16770
17092
  await (0, import_promises18.writeFile)(absolute, recipe.output, "utf8");
16771
17093
  const patch = [
16772
17094
  `--- ${options.target}`,
@@ -16777,7 +17099,7 @@ async function applyHeal(options) {
16777
17099
  recipe.output,
16778
17100
  ""
16779
17101
  ].join("\n");
16780
- 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");
16781
17103
  const result = {
16782
17104
  $schema: "https://viberaven.dev/schemas/heal-result.schema.json",
16783
17105
  schemaVersion: "v1",
@@ -16788,7 +17110,7 @@ async function applyHeal(options) {
16788
17110
  gapId: options.gapId,
16789
17111
  recipe: "empty-catch-safe-response",
16790
17112
  target: options.target,
16791
- changedFiles: [(0, import_node_path26.relative)(options.cwd, absolute).replace(/\\/g, "/")],
17113
+ changedFiles: [(0, import_node_path27.relative)(options.cwd, absolute).replace(/\\/g, "/")],
16792
17114
  artifacts: {
16793
17115
  patch: `.viberaven/heal/${id}/patch.diff`,
16794
17116
  result: `.viberaven/heal/${id}/result.json`
@@ -16796,20 +17118,20 @@ async function applyHeal(options) {
16796
17118
  rollback: {
16797
17119
  available: true,
16798
17120
  // The empty-catch recipe only edits a file that already exists.
16799
- instructions: rollbackInstructions(id, (0, import_node_path26.relative)(options.cwd, absolute).replace(/\\/g, "/"), true)
17121
+ instructions: rollbackInstructions(id, (0, import_node_path27.relative)(options.cwd, absolute).replace(/\\/g, "/"), true)
16800
17122
  }
16801
17123
  };
16802
- 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)}
16803
17125
  `, "utf8");
16804
17126
  return result;
16805
17127
  }
16806
- var import_promises18, import_node_fs12, import_node_path26;
17128
+ var import_promises18, import_node_fs13, import_node_path27;
16807
17129
  var init_apply = __esm({
16808
17130
  "src/heal/apply.ts"() {
16809
17131
  "use strict";
16810
17132
  import_promises18 = require("node:fs/promises");
16811
- import_node_fs12 = require("node:fs");
16812
- import_node_path26 = require("node:path");
17133
+ import_node_fs13 = require("node:fs");
17134
+ import_node_path27 = require("node:path");
16813
17135
  init_pathSafety();
16814
17136
  init_emptyCatch();
16815
17137
  init_healId();
@@ -16819,7 +17141,7 @@ var init_apply = __esm({
16819
17141
 
16820
17142
  // src/heal/plan.ts
16821
17143
  async function writeHealPlan(options) {
16822
- const dir = (0, import_node_path27.join)(options.cwd, ".viberaven");
17144
+ const dir = (0, import_node_path28.join)(options.cwd, ".viberaven");
16823
17145
  await (0, import_promises19.mkdir)(dir, { recursive: true });
16824
17146
  const id = newHealId();
16825
17147
  const target = options.target ?? `gap:${options.gapId}`;
@@ -16872,18 +17194,18 @@ async function writeHealPlan(options) {
16872
17194
  artifacts: { plan: ".viberaven/heal-plan.md" },
16873
17195
  rollback: { available: false, instructions: "No source files were changed." }
16874
17196
  };
16875
- await (0, import_promises19.writeFile)((0, import_node_path27.join)(dir, "heal-plan.md"), markdown, "utf8");
16876
- await (0, import_promises19.writeFile)((0, import_node_path27.join)(dir, "heal-plan.json"), `${JSON.stringify(result, null, 2)}
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)}
16877
17199
  `, "utf8");
16878
17200
  return result;
16879
17201
  }
16880
- var import_promises19, import_node_path27;
17202
+ var import_promises19, import_node_path28;
16881
17203
  var init_plan = __esm({
16882
17204
  "src/heal/plan.ts"() {
16883
17205
  "use strict";
16884
17206
  import_promises19 = require("node:fs/promises");
16885
17207
  init_healId();
16886
- import_node_path27 = require("node:path");
17208
+ import_node_path28 = require("node:path");
16887
17209
  init_commands();
16888
17210
  init_supabaseRls2();
16889
17211
  }
@@ -16891,7 +17213,7 @@ var init_plan = __esm({
16891
17213
 
16892
17214
  // src/heal/prompt.ts
16893
17215
  async function writeHealPrompt(options) {
16894
- const dir = (0, import_node_path28.join)(options.cwd, ".viberaven");
17216
+ const dir = (0, import_node_path29.join)(options.cwd, ".viberaven");
16895
17217
  await (0, import_promises20.mkdir)(dir, { recursive: true });
16896
17218
  const id = newHealId();
16897
17219
  const target = options.target ?? `gap:${options.gapId}`;
@@ -16919,16 +17241,16 @@ async function writeHealPrompt(options) {
16919
17241
  artifacts: { prompt: ".viberaven/heal-prompt.md" },
16920
17242
  rollback: { available: false, instructions: "No source files were changed." }
16921
17243
  };
16922
- 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");
16923
17245
  return result;
16924
17246
  }
16925
- var import_promises20, import_node_path28;
17247
+ var import_promises20, import_node_path29;
16926
17248
  var init_prompt = __esm({
16927
17249
  "src/heal/prompt.ts"() {
16928
17250
  "use strict";
16929
17251
  import_promises20 = require("node:fs/promises");
16930
17252
  init_healId();
16931
- import_node_path28 = require("node:path");
17253
+ import_node_path29 = require("node:path");
16932
17254
  init_commands();
16933
17255
  }
16934
17256
  });
@@ -17031,8 +17353,8 @@ var init_heal = __esm({
17031
17353
 
17032
17354
  // src/stackRecommend.ts
17033
17355
  async function recommendStack(cwd = process.cwd()) {
17034
- const pkgPath = (0, import_node_path29.join)(cwd, "package.json");
17035
- if (!(0, import_node_fs13.existsSync)(pkgPath)) {
17356
+ const pkgPath = (0, import_node_path30.join)(cwd, "package.json");
17357
+ if (!(0, import_node_fs14.existsSync)(pkgPath)) {
17036
17358
  return DEFAULT_STACK;
17037
17359
  }
17038
17360
  const pkg = JSON.parse(await (0, import_promises22.readFile)(pkgPath, "utf-8"));
@@ -17063,13 +17385,13 @@ async function recommendStack(cwd = process.cwd()) {
17063
17385
  }
17064
17386
  return rec;
17065
17387
  }
17066
- var import_promises22, import_node_fs13, import_node_path29, DEFAULT_STACK;
17388
+ var import_promises22, import_node_fs14, import_node_path30, DEFAULT_STACK;
17067
17389
  var init_stackRecommend = __esm({
17068
17390
  "src/stackRecommend.ts"() {
17069
17391
  "use strict";
17070
17392
  import_promises22 = require("node:fs/promises");
17071
- import_node_fs13 = require("node:fs");
17072
- import_node_path29 = require("node:path");
17393
+ import_node_fs14 = require("node:fs");
17394
+ import_node_path30 = require("node:path");
17073
17395
  DEFAULT_STACK = {
17074
17396
  frontend: "react",
17075
17397
  ui: "tailwind + shadcn/ui",
@@ -17212,13 +17534,13 @@ async function readBlobs(cwd, shas) {
17212
17534
  async function exportCommit(cwd, commit) {
17213
17535
  const entries = parseTree(await git(cwd, ["ls-tree", "-r", "-z", "-l", "--full-tree", commit])).filter(wanted);
17214
17536
  const blobs = await readBlobs(cwd, [...new Set(entries.map((entry) => entry.sha))]);
17215
- const root = await (0, import_promises23.mkdtemp)((0, import_node_path30.join)((0, import_node_os.tmpdir)(), "viberaven-base-"));
17537
+ const root = await (0, import_promises23.mkdtemp)((0, import_node_path31.join)((0, import_node_os.tmpdir)(), "viberaven-base-"));
17216
17538
  for (const entry of entries) {
17217
- const target = (0, import_node_path30.resolve)(root, ...entry.path.split("/"));
17218
- if (!target.startsWith(root + import_node_path30.sep)) continue;
17539
+ const target = (0, import_node_path31.resolve)(root, ...entry.path.split("/"));
17540
+ if (!target.startsWith(root + import_node_path31.sep)) continue;
17219
17541
  const content = blobs.get(entry.sha);
17220
17542
  if (!content) continue;
17221
- 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 });
17222
17544
  await (0, import_promises23.writeFile)(target, content);
17223
17545
  }
17224
17546
  return root;
@@ -17288,14 +17610,14 @@ function renderCheckMarkdown({ head, change, version, score }) {
17288
17610
  return `${lines.join("\n")}
17289
17611
  `;
17290
17612
  }
17291
- var import_node_child_process6, import_promises23, import_node_os, import_node_path30, import_node_util4, execFileAsync4, MAX_BLOB_BYTES, SKIP_SEGMENTS, SEVERITY_RANK3, LOCATION_TEXT, SUBJECT, COMMENT_MARKER, DOT;
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;
17292
17614
  var init_checkChange = __esm({
17293
17615
  "src/commands/checkChange.ts"() {
17294
17616
  "use strict";
17295
17617
  import_node_child_process6 = require("node:child_process");
17296
17618
  import_promises23 = require("node:fs/promises");
17297
17619
  import_node_os = require("node:os");
17298
- import_node_path30 = require("node:path");
17620
+ import_node_path31 = require("node:path");
17299
17621
  import_node_util4 = require("node:util");
17300
17622
  init_runScan();
17301
17623
  init_safeGit();
@@ -17379,12 +17701,12 @@ async function hasGithubRemote(cwd) {
17379
17701
  }
17380
17702
  }
17381
17703
  async function githubWorkflowStatus(cwd) {
17382
- return { exists: (0, import_node_fs14.existsSync)((0, import_node_path31.join)(cwd, WORKFLOW_PATH)), path: DISPLAY_PATH, onGithub: await hasGithubRemote(cwd) };
17704
+ return { exists: (0, import_node_fs15.existsSync)((0, import_node_path32.join)(cwd, WORKFLOW_PATH)), path: DISPLAY_PATH, onGithub: await hasGithubRemote(cwd) };
17383
17705
  }
17384
17706
  async function writeGithubWorkflow(cwd, options = {}) {
17385
- const target = (0, import_node_path31.join)(cwd, WORKFLOW_PATH);
17386
- if ((0, import_node_fs14.existsSync)(target) && !options.force) return { written: false, status: await githubWorkflowStatus(cwd) };
17387
- await (0, import_promises24.mkdir)((0, import_node_path31.dirname)(target), { recursive: true });
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 });
17388
17710
  await (0, import_promises24.writeFile)(target, renderWorkflow(), "utf8");
17389
17711
  return { written: true, status: await githubWorkflowStatus(cwd) };
17390
17712
  }
@@ -17409,20 +17731,20 @@ async function runInitGithubCommand(options) {
17409
17731
  if (!status.onGithub) console.log("This repository has no github.com remote yet: the workflow runs once it is pushed to GitHub.");
17410
17732
  return 0;
17411
17733
  }
17412
- 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;
17413
17735
  var init_initGithub = __esm({
17414
17736
  "src/commands/initGithub.ts"() {
17415
17737
  "use strict";
17416
17738
  import_node_child_process7 = require("node:child_process");
17417
- import_node_fs14 = require("node:fs");
17739
+ import_node_fs15 = require("node:fs");
17418
17740
  import_promises24 = require("node:fs/promises");
17419
- import_node_path31 = require("node:path");
17741
+ import_node_path32 = require("node:path");
17420
17742
  import_node_util5 = require("node:util");
17421
17743
  init_safeGit();
17422
17744
  init_version();
17423
17745
  init_checkChange();
17424
17746
  execFileAsync5 = (0, import_node_util5.promisify)(import_node_child_process7.execFile);
17425
- WORKFLOW_PATH = (0, import_node_path31.join)(".github", "workflows", "viberaven.yml");
17747
+ WORKFLOW_PATH = (0, import_node_path32.join)(".github", "workflows", "viberaven.yml");
17426
17748
  DISPLAY_PATH = ".github/workflows/viberaven.yml";
17427
17749
  }
17428
17750
  });
@@ -17446,7 +17768,7 @@ function managedBlocks(content) {
17446
17768
  async function checkAgentInjection(cwd) {
17447
17769
  const checks = [];
17448
17770
  for (const item4 of REQUIRED_EXISTENCE_CHECKS) {
17449
- 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));
17450
17772
  checks.push({
17451
17773
  id: item4.id,
17452
17774
  status: exists ? "pass" : "fail",
@@ -17454,17 +17776,17 @@ async function checkAgentInjection(cwd) {
17454
17776
  });
17455
17777
  }
17456
17778
  for (const item4 of OPTIONAL_CURSOR_PACK_CHECKS) {
17457
- 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));
17458
17780
  checks.push({
17459
17781
  id: item4.id,
17460
17782
  status: exists ? "pass" : "fail",
17461
17783
  message: exists ? `${item4.file} exists` : `Missing ${item4.file} \u2014 run npx -y viberaven init --agents all`
17462
17784
  });
17463
17785
  }
17464
- 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");
17465
17787
  if (await fileExists2(legacyCursorPath)) {
17466
17788
  const legacyContent = await (0, import_promises25.readFile)(legacyCursorPath, "utf-8");
17467
- const hasCoreSplit = await fileExists2((0, import_node_path32.join)(cwd, ".cursor/rules/viberaven-core.mdc"));
17789
+ const hasCoreSplit = await fileExists2((0, import_node_path33.join)(cwd, ".cursor/rules/viberaven-core.mdc"));
17468
17790
  if (!hasCoreSplit || legacyContent.includes("alwaysApply: true")) {
17469
17791
  checks.push({
17470
17792
  id: "cursor-legacy-mdc",
@@ -17475,7 +17797,7 @@ async function checkAgentInjection(cwd) {
17475
17797
  }
17476
17798
  for (const target of CORE_AGENT_INJECTION_TARGETS) {
17477
17799
  const file = target === "cursor" ? ".cursor/rules/viberaven-core.mdc" : AGENT_RULE_TARGETS[target].file;
17478
- const path = (0, import_node_path32.join)(cwd, file);
17800
+ const path = (0, import_node_path33.join)(cwd, file);
17479
17801
  const exists = await fileExists2(path);
17480
17802
  if (!exists) {
17481
17803
  checks.push({
@@ -17534,12 +17856,12 @@ async function fileExists2(path) {
17534
17856
  return false;
17535
17857
  }
17536
17858
  }
17537
- 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;
17538
17860
  var init_doctorAgents = __esm({
17539
17861
  "src/commands/doctorAgents.ts"() {
17540
17862
  "use strict";
17541
17863
  import_promises25 = require("node:fs/promises");
17542
- import_node_path32 = require("node:path");
17864
+ import_node_path33 = require("node:path");
17543
17865
  init_commands();
17544
17866
  init_agentTargets();
17545
17867
  REQUIRED_EXISTENCE_CHECKS = [
@@ -17810,7 +18132,7 @@ function buildContextCleanupPlan(input) {
17810
18132
  const items = [];
17811
18133
  const seen = /* @__PURE__ */ new Set();
17812
18134
  for (const file of input.files) {
17813
- 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);
17814
18136
  const fullPath = normalizePath5(file.path);
17815
18137
  const category = categorizeCleanupFile(path, fullPath, file);
17816
18138
  if (!category || seen.has(path)) {
@@ -17836,8 +18158,8 @@ async function collectCleanupFiles(projectRoot) {
17836
18158
  return files;
17837
18159
  }
17838
18160
  async function writeCleanupPlan(projectRoot, plan) {
17839
- const outputDir = (0, import_node_path33.join)(projectRoot, ".viberaven");
17840
- const outputPath = (0, import_node_path33.join)(outputDir, "context-cleanup.md");
18161
+ const outputDir = (0, import_node_path34.join)(projectRoot, ".viberaven");
18162
+ const outputPath = (0, import_node_path34.join)(outputDir, "context-cleanup.md");
17841
18163
  await (0, import_promises26.mkdir)(outputDir, { recursive: true });
17842
18164
  await (0, import_promises26.writeFile)(outputPath, renderCleanupPlan(plan), "utf8");
17843
18165
  return outputPath;
@@ -17895,9 +18217,9 @@ async function walkCleanupFiles(root, dir, result, depth) {
17895
18217
  if (SKIP_DIRECTORIES.has(entry.name)) {
17896
18218
  continue;
17897
18219
  }
17898
- const absolute = (0, import_node_path33.join)(dir, entry.name);
18220
+ const absolute = (0, import_node_path34.join)(dir, entry.name);
17899
18221
  if (entry.isDirectory()) {
17900
- if (isCachePath(normalizePath5((0, import_node_path33.relative)(root, absolute)))) {
18222
+ if (isCachePath(normalizePath5((0, import_node_path34.relative)(root, absolute)))) {
17901
18223
  result.push({ path: absolute, sizeBytes: 0, kind: "directory" });
17902
18224
  continue;
17903
18225
  }
@@ -17918,12 +18240,12 @@ async function walkCleanupFiles(root, dir, result, depth) {
17918
18240
  function normalizePath5(path) {
17919
18241
  return path.replace(/\\/g, "/");
17920
18242
  }
17921
- 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;
17922
18244
  var init_cleanPlan = __esm({
17923
18245
  "src/commands/cleanPlan.ts"() {
17924
18246
  "use strict";
17925
18247
  import_promises26 = require("node:fs/promises");
17926
- import_node_path33 = require("node:path");
18248
+ import_node_path34 = require("node:path");
17927
18249
  MAX_WALK_DEPTH = 5;
17928
18250
  LARGE_LOG_BYTES = 8192;
17929
18251
  SKIP_DIRECTORIES = /* @__PURE__ */ new Set([".git", "node_modules"]);
@@ -18001,6 +18323,9 @@ function renderVerifyCommand(action) {
18001
18323
  }
18002
18324
  return ["Verify:", "```bash", redact(action.verifyCommand), "```"];
18003
18325
  }
18326
+ function renderUserSteps(steps) {
18327
+ return steps?.length ? ["Outside the repo (tell the user):", ...steps.map((step) => `- ${redact(step)}`)] : [];
18328
+ }
18004
18329
  function renderAction(action) {
18005
18330
  return [
18006
18331
  `[${action.id}] ${redact(action.title)}`,
@@ -18009,6 +18334,7 @@ function renderAction(action) {
18009
18334
  ...renderTarget(action.target),
18010
18335
  ...renderCopyPayload(action),
18011
18336
  ...renderVerifyCommand(action),
18337
+ ...renderUserSteps(action.userSteps),
18012
18338
  action.resumeInstruction ? `Resume: "${redact(action.resumeInstruction)}"` : void 0
18013
18339
  ].filter((line) => Boolean(line));
18014
18340
  }
@@ -18024,6 +18350,9 @@ function renderActionSurface(manifest, options = {}) {
18024
18350
  for (const action of visibleActions) {
18025
18351
  lines.push(...renderAction(action), "");
18026
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
+ }
18027
18356
  return `${lines.join("\n").trimEnd()}
18028
18357
  `;
18029
18358
  }
@@ -18036,7 +18365,7 @@ var init_render = __esm({
18036
18365
 
18037
18366
  // src/commands/actions.ts
18038
18367
  async function runActionsCommand(input) {
18039
- 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");
18040
18369
  let raw;
18041
18370
  try {
18042
18371
  raw = await (0, import_promises27.readFile)(actionsPath, "utf8");
@@ -18053,12 +18382,12 @@ async function runActionsCommand(input) {
18053
18382
  process.stdout.write(renderActionSurface(manifest));
18054
18383
  return 0;
18055
18384
  }
18056
- var import_promises27, import_node_path34;
18385
+ var import_promises27, import_node_path35;
18057
18386
  var init_actions = __esm({
18058
18387
  "src/commands/actions.ts"() {
18059
18388
  "use strict";
18060
18389
  import_promises27 = require("node:fs/promises");
18061
- import_node_path34 = require("node:path");
18390
+ import_node_path35 = require("node:path");
18062
18391
  init_render();
18063
18392
  init_config();
18064
18393
  }
@@ -18067,7 +18396,7 @@ var init_actions = __esm({
18067
18396
  // src/commands/verifyAction.ts
18068
18397
  async function currentActionExists(cwd, actionId) {
18069
18398
  try {
18070
- 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");
18071
18400
  const manifest = JSON.parse(raw);
18072
18401
  return manifest.actions.some((action) => action.id === actionId);
18073
18402
  } catch {
@@ -18111,12 +18440,12 @@ This only looks the action up. Run \`npx -y viberaven --verify\` to scan the rep
18111
18440
  );
18112
18441
  return 0;
18113
18442
  }
18114
- var import_promises28, import_node_path35;
18443
+ var import_promises28, import_node_path36;
18115
18444
  var init_verifyAction = __esm({
18116
18445
  "src/commands/verifyAction.ts"() {
18117
18446
  "use strict";
18118
18447
  import_promises28 = require("node:fs/promises");
18119
- import_node_path35 = require("node:path");
18448
+ import_node_path36 = require("node:path");
18120
18449
  init_registry2();
18121
18450
  init_config();
18122
18451
  }
@@ -18271,6 +18600,7 @@ ${message}
18271
18600
  return failing ? 1 : 0;
18272
18601
  }
18273
18602
  if (options.json) {
18603
+ const userFollowUps = await readUserFollowUps(paths.actionsPath);
18274
18604
  console.log(
18275
18605
  JSON.stringify(
18276
18606
  {
@@ -18293,6 +18623,8 @@ ${message}
18293
18623
  unchanged: change.unchanged.map((gap) => gap.id)
18294
18624
  }
18295
18625
  } : {},
18626
+ // Steps outside the repo for fixes already in it (MCP check and verify read this). Read only.
18627
+ ...userFollowUps.length > 0 ? { userFollowUps } : {},
18296
18628
  artifacts: paths
18297
18629
  },
18298
18630
  null,
@@ -18347,14 +18679,31 @@ ${message}
18347
18679
  console.log(
18348
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}`
18349
18681
  );
18682
+ await printUserFollowUps(paths.actionsPath);
18350
18683
  console.log(`Fix: viberaven fix \xB7 Details: ${paths.tasklistPath}`);
18351
18684
  console.log("");
18352
18685
  return critical > 0 ? 1 : 0;
18353
18686
  }
18354
- 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;
18355
18703
  var init_check = __esm({
18356
18704
  "src/commands/check.ts"() {
18357
18705
  "use strict";
18706
+ import_promises29 = require("node:fs/promises");
18358
18707
  init_runScan();
18359
18708
  init_artifacts2();
18360
18709
  init_statusLabels();
@@ -18803,8 +19152,8 @@ function isSqlError(error) {
18803
19152
  }
18804
19153
  function displayPath(projectDir, path) {
18805
19154
  for (const base of [projectDir, process.cwd()]) {
18806
- const rel = (0, import_node_path36.relative)(base, path);
18807
- if (rel && !rel.startsWith("..") && !(0, import_node_path36.isAbsolute)(rel)) return rel.split("\\").join("/");
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("/");
18808
19157
  }
18809
19158
  return path;
18810
19159
  }
@@ -19408,26 +19757,26 @@ function verdictFor(summary) {
19408
19757
  }
19409
19758
  async function listMigrations(dir) {
19410
19759
  try {
19411
- const entries = await (0, import_promises29.readdir)(dir, { withFileTypes: true });
19760
+ const entries = await (0, import_promises30.readdir)(dir, { withFileTypes: true });
19412
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);
19413
19762
  } catch {
19414
19763
  return [];
19415
19764
  }
19416
19765
  }
19417
19766
  async function runRlsTest(options) {
19418
- const projectDir = (0, import_node_path36.resolve)(options.projectDir);
19419
- const matrixPath = (0, import_node_path36.resolve)(projectDir, options.matrixPath ?? DEFAULT_MATRIX_FILE);
19420
- const migrationsDir = (0, import_node_path36.resolve)(projectDir, options.migrationsDir ?? (0, import_node_path36.join)("supabase", "migrations"));
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"));
19421
19770
  const timeouts = { ...DEFAULT_TIMEOUTS, ...options.timeouts };
19422
- if ((0, import_node_path36.basename)(matrixPath).toLowerCase().startsWith(".env")) {
19423
- throw new RlsInputError(`Refusing to read ${(0, import_node_path36.basename)(matrixPath)}: rls-test never reads .env files. Point --matrix at a viberaven.permissions.json.`);
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.`);
19424
19773
  }
19425
19774
  const filesRead = [];
19426
19775
  let matrixText;
19427
19776
  try {
19428
- const info = await (0, import_promises29.stat)(matrixPath);
19777
+ const info = await (0, import_promises30.stat)(matrixPath);
19429
19778
  if (!info.isFile()) throw new Error("not a file");
19430
- matrixText = normalise(await (0, import_promises29.readFile)(matrixPath, "utf8"));
19779
+ matrixText = normalise(await (0, import_promises30.readFile)(matrixPath, "utf8"));
19431
19780
  } catch {
19432
19781
  throw new RlsInputError(
19433
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.`
@@ -19438,8 +19787,8 @@ async function runRlsTest(options) {
19438
19787
  const names = await listMigrations(migrationsDir);
19439
19788
  const migrationFiles = [];
19440
19789
  for (const name of names) {
19441
- const sql = normalise(await (0, import_promises29.readFile)((0, import_node_path36.join)(migrationsDir, name), "utf8"));
19442
- filesRead.push(displayPath(projectDir, (0, import_node_path36.join)(migrationsDir, name)));
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)));
19443
19792
  migrationFiles.push({ name, sha256: sha256(sql), sql });
19444
19793
  }
19445
19794
  const migrationsHash = sha256(migrationFiles.map((f) => `${f.name}\0${f.sha256}`).join("\n"));
@@ -19639,15 +19988,15 @@ async function runTable(db, table2, badRoles, roleChecks) {
19639
19988
  cells
19640
19989
  };
19641
19990
  }
19642
- var import_node_child_process8, import_node_crypto3, import_promises29, import_node_net, import_node_path36, import_node_util6, NON_AUTHORITATIVE_LABEL, PGLITE_INSTALL_HINT, ENV_ALLOWLIST, PERMISSION_DENIED, RLS_REFUSAL, DEFAULT_TIMEOUTS, RlsInputError, normalise, quoteIdent2, firstLine2, plural3, NUMBERS, ACTOR_SESSION, changedRows, isRlsRefusal, isPrivilegeRefusal, SELF, PROBE_EXCLUSIONS, SEED_HINTS;
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;
19643
19992
  var init_rlsRunner = __esm({
19644
19993
  "src/rls/rlsRunner.ts"() {
19645
19994
  "use strict";
19646
19995
  import_node_child_process8 = require("node:child_process");
19647
19996
  import_node_crypto3 = require("node:crypto");
19648
- import_promises29 = require("node:fs/promises");
19997
+ import_promises30 = require("node:fs/promises");
19649
19998
  import_node_net = __toESM(require("node:net"));
19650
- import_node_path36 = require("node:path");
19999
+ import_node_path37 = require("node:path");
19651
20000
  import_node_util6 = require("node:util");
19652
20001
  init_safeGit();
19653
20002
  init_permissionsMatrix();
@@ -20475,7 +20824,7 @@ function buildMissionCards(artifact, providers) {
20475
20824
  };
20476
20825
  }
20477
20826
  function projectNameFromPath(pathValue) {
20478
- return (0, import_node_path37.basename)(pathValue.replace(/[\\/]$/, "")) || "current repo";
20827
+ return (0, import_node_path38.basename)(pathValue.replace(/[\\/]$/, "")) || "current repo";
20479
20828
  }
20480
20829
  function gapSearchText(gap) {
20481
20830
  return [
@@ -20666,7 +21015,7 @@ function mapProvider(template, artifact, projectName) {
20666
21015
  const gaps = providerGaps(artifact, template.id);
20667
21016
  const primaryGap2 = gaps[0];
20668
21017
  const state = providerState(gaps);
20669
- const launchGateClear = artifact.gaps.length === 0 && artifact.score >= 80;
21018
+ const launchGateClear = gateStatusForGaps(artifact.gaps) === "clear";
20670
21019
  const launchPath = template.items.map((item4, index) => ({
20671
21020
  ...item4,
20672
21021
  state: itemState(primaryGap2, index, item4, launchGateClear),
@@ -20875,8 +21224,9 @@ function mapArtifactToLocalUiState(artifact, presentProviderIds) {
20875
21224
  scannedAt: artifact.scannedAt
20876
21225
  },
20877
21226
  gate: {
20878
- status: artifact.gaps.length === 0 && artifact.score >= 80 ? "clear" : "not_clear",
20879
- label: artifact.gaps.length === 0 && artifact.score >= 80 ? "Ready" : "Gate not clear",
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)],
20880
21230
  score: artifact.score,
20881
21231
  productionCorePercent: artifact.productionCorePercent
20882
21232
  },
@@ -20886,25 +21236,31 @@ function mapArtifactToLocalUiState(artifact, presentProviderIds) {
20886
21236
  selectedProviderId: selectedProvider.id,
20887
21237
  currentPrompt: selectedProvider.nextFix?.prompt ?? "",
20888
21238
  artifacts: {
20889
- tasklist: (0, import_node_path37.join)(".viberaven", "agent-tasklist.md"),
20890
- contextMap: (0, import_node_path37.join)(".viberaven", "context-map.json"),
20891
- gateResult: (0, import_node_path37.join)(".viberaven", "gate-result.json"),
20892
- lastScan: (0, import_node_path37.join)(".viberaven", "last-scan.json")
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")
20893
21243
  },
20894
21244
  empty: false
20895
21245
  };
20896
21246
  }
20897
- 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;
20898
21248
  var init_artifactAdapter = __esm({
20899
21249
  "src/local-ui/artifactAdapter.ts"() {
20900
21250
  "use strict";
20901
- import_node_path37 = require("node:path");
21251
+ import_node_path38 = require("node:path");
20902
21252
  init_providerLogos();
20903
21253
  init_buildTaskList();
20904
21254
  init_prompts();
20905
21255
  init_providerLaunchPaths();
20906
21256
  init_launchRunway();
20907
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
+ };
20908
21264
  CONNECT_PROVIDER_PRIORITY = ["supabase", "vercel", "stripe"];
20909
21265
  providerDashboardUrls = {
20910
21266
  supabase: "https://supabase.com/dashboard/projects",
@@ -21201,18 +21557,18 @@ var init_planLimits = __esm({
21201
21557
  // src/hosted/credentials.ts
21202
21558
  function defaultConfigDir(env = process.env, platform = process.platform) {
21203
21559
  const override = env.VIBERAVEN_CONFIG_DIR?.trim();
21204
- if (override) return (0, import_node_path38.resolve)(override);
21560
+ if (override) return (0, import_node_path39.resolve)(override);
21205
21561
  if (platform === "win32") {
21206
- return (0, import_node_path38.join)(env.APPDATA?.trim() || (0, import_node_path38.join)((0, import_node_os2.homedir)(), "AppData", "Roaming"), "viberaven");
21562
+ return (0, import_node_path39.join)(env.APPDATA?.trim() || (0, import_node_path39.join)((0, import_node_os2.homedir)(), "AppData", "Roaming"), "viberaven");
21207
21563
  }
21208
21564
  if (platform === "darwin") {
21209
- 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");
21210
21566
  }
21211
- 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");
21212
21568
  }
21213
21569
  function isInside(child, parent) {
21214
- const rel = (0, import_node_path38.relative)((0, import_node_path38.resolve)(parent), (0, import_node_path38.resolve)(child));
21215
- return rel === "" || !rel.startsWith("..") && !(0, import_node_path38.isAbsolute)(rel);
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);
21216
21572
  }
21217
21573
  async function retryTransient(work) {
21218
21574
  for (let attempt = 1; ; attempt += 1) {
@@ -21225,15 +21581,15 @@ async function retryTransient(work) {
21225
21581
  }
21226
21582
  }
21227
21583
  }
21228
- var import_node_child_process9, import_node_crypto4, import_promises30, import_node_os2, import_node_path38, CREDENTIALS_FILE_NAME, CredentialLocationError, CredentialPermissionError, restrictWithIcacls, TRANSIENT_FS_CODES, FS_ATTEMPTS, FS_BACKOFF_MS, CredentialStore;
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;
21229
21585
  var init_credentials = __esm({
21230
21586
  "src/hosted/credentials.ts"() {
21231
21587
  "use strict";
21232
21588
  import_node_child_process9 = require("node:child_process");
21233
21589
  import_node_crypto4 = require("node:crypto");
21234
- import_promises30 = require("node:fs/promises");
21590
+ import_promises31 = require("node:fs/promises");
21235
21591
  import_node_os2 = require("node:os");
21236
- import_node_path38 = require("node:path");
21592
+ import_node_path39 = require("node:path");
21237
21593
  CREDENTIALS_FILE_NAME = "credentials.json";
21238
21594
  CredentialLocationError = class extends Error {
21239
21595
  };
@@ -21260,9 +21616,9 @@ var init_credentials = __esm({
21260
21616
  constructor(configDir, workspacePath, options = {}) {
21261
21617
  this.configDir = configDir;
21262
21618
  this.workspacePath = workspacePath;
21263
- this.path = (0, import_node_path38.join)(configDir, CREDENTIALS_FILE_NAME);
21619
+ this.path = (0, import_node_path39.join)(configDir, CREDENTIALS_FILE_NAME);
21264
21620
  this.restrict = options.restrict !== void 0 ? options.restrict : process.platform === "win32" ? restrictWithIcacls : null;
21265
- this.fs = { readFile: (path, encoding) => (0, import_promises30.readFile)(path, encoding), rename: (from, to) => (0, import_promises30.rename)(from, to), rm: (path) => (0, import_promises30.rm)(path, { force: true }), ...options.fs };
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 };
21266
21622
  }
21267
21623
  path;
21268
21624
  restrict;
@@ -21311,15 +21667,15 @@ var init_credentials = __esm({
21311
21667
  `Refusing to store the VibeRaven sign-in inside this project (${this.configDir}). Set VIBERAVEN_CONFIG_DIR to a folder outside the repo.`
21312
21668
  );
21313
21669
  }
21314
- await (0, import_promises30.mkdir)(this.configDir, { recursive: true, mode: 448 });
21670
+ await (0, import_promises31.mkdir)(this.configDir, { recursive: true, mode: 448 });
21315
21671
  const temp = `${this.path}.${(0, import_node_crypto4.randomUUID)()}.tmp`;
21316
- await (0, import_promises30.writeFile)(temp, `${JSON.stringify(body, null, 2)}
21672
+ await (0, import_promises31.writeFile)(temp, `${JSON.stringify(body, null, 2)}
21317
21673
  `, { encoding: "utf8", mode: 384 });
21318
- await (0, import_promises30.chmod)(temp, 384).catch(() => void 0);
21674
+ await (0, import_promises31.chmod)(temp, 384).catch(() => void 0);
21319
21675
  try {
21320
21676
  if (this.restrict) await this.restrict(temp);
21321
21677
  } catch (error) {
21322
- await (0, import_promises30.rm)(temp, { force: true });
21678
+ await (0, import_promises31.rm)(temp, { force: true });
21323
21679
  const code = error?.code;
21324
21680
  const toolMissing = code === "ENOENT";
21325
21681
  throw new CredentialPermissionError(
@@ -21330,17 +21686,17 @@ var init_credentials = __esm({
21330
21686
  if (guard) {
21331
21687
  const onDisk = await this.readFor(guard.apiBaseUrl);
21332
21688
  if (onDisk?.accessToken !== guard.accessToken || guard.isCurrent && !guard.isCurrent()) {
21333
- await (0, import_promises30.rm)(temp, { force: true });
21689
+ await (0, import_promises31.rm)(temp, { force: true });
21334
21690
  return false;
21335
21691
  }
21336
21692
  }
21337
21693
  try {
21338
21694
  await retryTransient(() => this.fs.rename(temp, this.path));
21339
21695
  } catch (error) {
21340
- await (0, import_promises30.rm)(temp, { force: true });
21696
+ await (0, import_promises31.rm)(temp, { force: true });
21341
21697
  throw error;
21342
21698
  }
21343
- await (0, import_promises30.chmod)(this.path, 384).catch(() => void 0);
21699
+ await (0, import_promises31.chmod)(this.path, 384).catch(() => void 0);
21344
21700
  return true;
21345
21701
  }
21346
21702
  /** Keeps the stored plan label in step with what the API last said. */
@@ -21709,7 +22065,7 @@ async function buildHostedCheckPayload(workspacePath) {
21709
22065
  throw new Error("Could not assemble the full check request.");
21710
22066
  }
21711
22067
  function finalizePayload(payload) {
21712
- 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";
21713
22069
  const redacted = redactPayload({ ...payload, workspacePath: folder });
21714
22070
  return { payload: redacted.payload, redactions: redacted.count };
21715
22071
  }
@@ -21740,13 +22096,13 @@ function splitSections(prompt) {
21740
22096
  chars: section.length
21741
22097
  }));
21742
22098
  }
21743
- var import_node_path39, PayloadCaptured;
22099
+ var import_node_path40, PayloadCaptured;
21744
22100
  var init_payload = __esm({
21745
22101
  "src/hosted/payload.ts"() {
21746
22102
  "use strict";
21747
22103
  init_fileScanner();
21748
22104
  init_orchestrator();
21749
- import_node_path39 = require("node:path");
22105
+ import_node_path40 = require("node:path");
21750
22106
  init_config();
21751
22107
  init_redact();
21752
22108
  PayloadCaptured = class extends Error {
@@ -22212,10 +22568,10 @@ async function connectProviderMcp(cwd, providerId) {
22212
22568
  if (!recipe) {
22213
22569
  return { ok: false, message: `No known MCP server for ${providerId} yet. Add one manually to .mcp.json.` };
22214
22570
  }
22215
- const configPath = (0, import_node_path40.join)(cwd, ".mcp.json");
22571
+ const configPath = (0, import_node_path41.join)(cwd, ".mcp.json");
22216
22572
  let config = {};
22217
22573
  try {
22218
- const raw = await (0, import_promises31.readFile)(configPath, "utf8");
22574
+ const raw = await (0, import_promises32.readFile)(configPath, "utf8");
22219
22575
  const parsed = JSON.parse(raw.replace(/^\uFEFF/, ""));
22220
22576
  if (!isRecord5(parsed) || parsed.mcpServers !== void 0 && !isRecord5(parsed.mcpServers)) {
22221
22577
  return { ok: false, message: "Existing .mcp.json has an unsupported structure. It was left unchanged." };
@@ -22232,7 +22588,7 @@ async function connectProviderMcp(cwd, providerId) {
22232
22588
  }
22233
22589
  servers[providerId] = recipe.entry;
22234
22590
  const next = { ...config, mcpServers: servers };
22235
- await (0, import_promises31.writeFile)(configPath, `${JSON.stringify(next, null, 2)}
22591
+ await (0, import_promises32.writeFile)(configPath, `${JSON.stringify(next, null, 2)}
22236
22592
  `, "utf8");
22237
22593
  const suffix = recipe.note ? ` ${recipe.note}` : "";
22238
22594
  return { ok: true, message: `Added ${providerId} MCP server to .mcp.json.${suffix}`, path: ".mcp.json" };
@@ -22285,7 +22641,7 @@ async function defaultScanRunner(flags, positional) {
22285
22641
  }
22286
22642
  async function readPackageJson(cwd) {
22287
22643
  try {
22288
- 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");
22289
22645
  const parsed = JSON.parse(raw.replace(/^\uFEFF/, ""));
22290
22646
  return isRecord5(parsed) ? parsed : {};
22291
22647
  } catch {
@@ -22308,7 +22664,7 @@ async function listProjectFiles(cwd, maxFiles = 1800) {
22308
22664
  if (files.length >= maxFiles) return;
22309
22665
  let entries;
22310
22666
  try {
22311
- entries = await (0, import_promises31.readdir)(dir, { withFileTypes: true });
22667
+ entries = await (0, import_promises32.readdir)(dir, { withFileTypes: true });
22312
22668
  } catch {
22313
22669
  return;
22314
22670
  }
@@ -22316,7 +22672,7 @@ async function listProjectFiles(cwd, maxFiles = 1800) {
22316
22672
  if (files.length >= maxFiles) return;
22317
22673
  const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
22318
22674
  if (entry.isDirectory()) {
22319
- 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);
22320
22676
  continue;
22321
22677
  }
22322
22678
  if (entry.isFile()) files.push(rel.toLowerCase());
@@ -22335,7 +22691,7 @@ async function readEnvKeys(cwd) {
22335
22691
  const keys = /* @__PURE__ */ new Set();
22336
22692
  for (const name of [".env.example", ".env"]) {
22337
22693
  try {
22338
- 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");
22339
22695
  for (const line of raw.split(/\r?\n/)) {
22340
22696
  const trimmed = line.trim();
22341
22697
  if (!trimmed || trimmed.startsWith("#")) continue;
@@ -22653,7 +23009,7 @@ async function codexModelOptions() {
22653
23009
  { id: "gpt-5.4-mini", label: "GPT-5.4 Mini" }
22654
23010
  ];
22655
23011
  try {
22656
- 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");
22657
23013
  const parsed = JSON.parse(raw);
22658
23014
  const list = Array.isArray(parsed) ? parsed : parsed.models ?? [];
22659
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 }));
@@ -22856,19 +23212,19 @@ function isPlaceholderProjectName(value) {
22856
23212
  async function readMcpEvidence(cwd) {
22857
23213
  const candidates = MCP_CONFIG_PATHS.map((relativePath) => ({
22858
23214
  source: relativePath.replace(/\\/g, "/"),
22859
- path: (0, import_node_path40.resolve)(cwd, relativePath)
23215
+ path: (0, import_node_path41.resolve)(cwd, relativePath)
22860
23216
  }));
22861
23217
  const home = process.env.USERPROFILE || process.env.HOME;
22862
23218
  if (home) {
22863
23219
  candidates.push({
22864
23220
  source: "~/.codex/config.toml",
22865
- path: (0, import_node_path40.resolve)(home, ".codex", "config.toml")
23221
+ path: (0, import_node_path41.resolve)(home, ".codex", "config.toml")
22866
23222
  });
22867
23223
  }
22868
23224
  const evidence = [];
22869
23225
  for (const candidate of candidates) {
22870
23226
  try {
22871
- const text = await (0, import_promises31.readFile)(candidate.path, "utf8");
23227
+ const text = await (0, import_promises32.readFile)(candidate.path, "utf8");
22872
23228
  if (/mcp/i.test(text)) evidence.push({ source: candidate.source, text });
22873
23229
  } catch {
22874
23230
  }
@@ -22959,7 +23315,7 @@ async function enrichProjectMetadata(state, cwd) {
22959
23315
  async function readState(cwd) {
22960
23316
  let state;
22961
23317
  try {
22962
- 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");
22963
23319
  const packageJson = await readPackageJson(cwd);
22964
23320
  const deps = dependencyNames(packageJson);
22965
23321
  const files = await listProjectFiles(cwd);
@@ -23226,7 +23582,7 @@ async function whereExe(command, cwd) {
23226
23582
  }
23227
23583
  function expandShimPath(value, base) {
23228
23584
  const relative8 = value.replace(/^%(?:dp0%\\+|~dp0\\*)/i, "");
23229
- 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(/[\\/]+/));
23230
23586
  }
23231
23587
  function findShimTarget(lines, pattern, accept = () => true) {
23232
23588
  for (const line of lines) {
@@ -23237,21 +23593,21 @@ function findShimTarget(lines, pattern, accept = () => true) {
23237
23593
  }
23238
23594
  async function resolveWindowsShimTarget(shimPath) {
23239
23595
  try {
23240
- const content = await (0, import_promises31.readFile)(shimPath, "utf8");
23241
- 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);
23242
23598
  const lines = content.split(/\r?\n/);
23243
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("%"));
23244
23600
  const script = findShimTarget(lines, /"%(?:dp0%\\+|~dp0\\*)([^"]+\.js)"/i);
23245
23601
  if (script) {
23246
23602
  return {
23247
23603
  command: process.execPath,
23248
- args: [(0, import_node_path40.join)(base, ...script.match[1].split(/[\\/]+/)), ...trailingArgs(script.line, script.match.index + script.match[0].length)]
23604
+ args: [(0, import_node_path41.join)(base, ...script.match[1].split(/[\\/]+/)), ...trailingArgs(script.line, script.match.index + script.match[0].length)]
23249
23605
  };
23250
23606
  }
23251
- 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");
23252
23608
  if (binary) {
23253
23609
  return {
23254
- command: (0, import_node_path40.join)(base, ...binary.match[1].split(/[\\/]+/)),
23610
+ command: (0, import_node_path41.join)(base, ...binary.match[1].split(/[\\/]+/)),
23255
23611
  args: trailingArgs(binary.line, binary.match.index + binary.match[0].length)
23256
23612
  };
23257
23613
  }
@@ -23376,8 +23732,8 @@ async function writeChatContextArtifact(cwd, request, prompt) {
23376
23732
  "",
23377
23733
  prompt
23378
23734
  ].filter((line) => typeof line === "string");
23379
- await (0, import_promises31.mkdir)((0, import_node_path40.join)(cwd, ".viberaven"), { recursive: true });
23380
- await (0, import_promises31.writeFile)((0, import_node_path40.join)(cwd, ".viberaven", "chat-context.md"), `${lines.join("\n")}
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")}
23381
23737
  `, "utf8");
23382
23738
  } catch {
23383
23739
  }
@@ -23397,13 +23753,13 @@ async function runAgentChat(cwd, request) {
23397
23753
  }
23398
23754
  const prompt = await buildAgentChatPrompt(request, cwd);
23399
23755
  await writeChatContextArtifact(cwd, request, prompt);
23400
- const outputPath = request.cliId === "codex" ? (0, import_node_path40.join)(cwd, ".viberaven", `studio-agent-${Date.now()}-${Math.random().toString(36).slice(2)}.md`) : void 0;
23401
- if (outputPath) await (0, import_promises31.mkdir)((0, import_node_path40.dirname)(outputPath), { recursive: true });
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 });
23402
23758
  const { command, args, stdin } = commandForAgentChat(request, prompt, cwd, outputPath);
23403
23759
  const result = await runAgentCommand(command, args, cwd, stdin, request.runId);
23404
23760
  if (outputPath) {
23405
23761
  try {
23406
- const finalMessage = (await (0, import_promises31.readFile)(outputPath, "utf8")).trim();
23762
+ const finalMessage = (await (0, import_promises32.readFile)(outputPath, "utf8")).trim();
23407
23763
  if (finalMessage) return { ...result, output: finalMessage };
23408
23764
  } catch {
23409
23765
  }
@@ -23415,8 +23771,8 @@ function firstGapId(state) {
23415
23771
  return state.providers.find((provider2) => provider2.nextFix?.id)?.nextFix?.id;
23416
23772
  }
23417
23773
  async function runLocalAction(cwd, intent) {
23418
- const artifactDir = (0, import_node_path40.join)(cwd, ".viberaven");
23419
- 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 });
23420
23776
  if (intent === "preview_rehearsal") {
23421
23777
  const state2 = await readState(cwd);
23422
23778
  const topProvider = state2.providers[0];
@@ -23457,7 +23813,7 @@ async function runLocalAction(cwd, intent) {
23457
23813
  proof,
23458
23814
  ""
23459
23815
  ].join("\n");
23460
- 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");
23461
23817
  return {
23462
23818
  title: "Preview rehearsal ready",
23463
23819
  summary: `${projectName}: ${gateLabel} with ${state2.providers.length} provider beat${state2.providers.length === 1 ? "" : "s"}`,
@@ -23467,7 +23823,7 @@ async function runLocalAction(cwd, intent) {
23467
23823
  if (intent === "audit_vercel_supabase") {
23468
23824
  const auditInput = await collectVercelSupabaseAuditInput(cwd);
23469
23825
  const result2 = buildVercelSupabaseAudit(auditInput);
23470
- 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)}
23471
23827
  `, "utf8");
23472
23828
  return {
23473
23829
  title: result2.status === "pass" ? "Audit passed" : "Audit needs work",
@@ -23495,7 +23851,7 @@ async function runLocalAction(cwd, intent) {
23495
23851
  const mode = intent === "heal_prompt" ? "prompt" : intent === "heal_apply" ? "apply" : "plan";
23496
23852
  const result = await runHealCommand({ cwd, mode, gapId, yes: mode === "apply", noVerify: true });
23497
23853
  if (mode === "apply") {
23498
- 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)}
23499
23855
  `, "utf8");
23500
23856
  }
23501
23857
  return {
@@ -23505,14 +23861,14 @@ async function runLocalAction(cwd, intent) {
23505
23861
  };
23506
23862
  }
23507
23863
  async function sendArtifact(cwd, rel, response, contentType = "text/plain; charset=utf-8") {
23508
- const absolute = (0, import_node_path40.resolve)(cwd, rel);
23509
- const root = (0, import_node_path40.resolve)(cwd);
23864
+ const absolute = (0, import_node_path41.resolve)(cwd, rel);
23865
+ const root = (0, import_node_path41.resolve)(cwd);
23510
23866
  if (!absolute.startsWith(root)) {
23511
23867
  sendText(response, 403, "Forbidden");
23512
23868
  return;
23513
23869
  }
23514
23870
  try {
23515
- const text = await (0, import_promises31.readFile)(absolute, "utf8");
23871
+ const text = await (0, import_promises32.readFile)(absolute, "utf8");
23516
23872
  response.writeHead(200, { "content-type": contentType });
23517
23873
  response.end(text);
23518
23874
  } catch {
@@ -23521,7 +23877,7 @@ async function sendArtifact(cwd, rel, response, contentType = "text/plain; chars
23521
23877
  }
23522
23878
  async function sendReportArtifact(cwd, response) {
23523
23879
  try {
23524
- 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");
23525
23881
  response.writeHead(200, { "content-type": "text/html; charset=utf-8" });
23526
23882
  response.end(text);
23527
23883
  return;
@@ -23648,7 +24004,7 @@ async function sendReportArtifact(cwd, response) {
23648
24004
  }
23649
24005
  async function sendExtensionIcon(cwd, response) {
23650
24006
  try {
23651
- 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"));
23652
24008
  sendBinary(response, 200, "image/png", body);
23653
24009
  } catch {
23654
24010
  sendText(response, 404, "Icon not found");
@@ -23661,15 +24017,15 @@ async function sendMangaAsset(pathname, response) {
23661
24017
  return;
23662
24018
  }
23663
24019
  const candidates = [
23664
- (0, import_node_path40.join)(__dirname, "manga", name),
24020
+ (0, import_node_path41.join)(__dirname, "manga", name),
23665
24021
  // installed + dev: <pkg>/dist/manga
23666
- (0, import_node_path40.join)(__dirname, "..", "assets", "manga", name),
24022
+ (0, import_node_path41.join)(__dirname, "..", "assets", "manga", name),
23667
24023
  // fallback: <pkg>/assets/manga (source)
23668
- (0, import_node_path40.join)(__dirname, "..", "..", "assets", "manga", name)
24024
+ (0, import_node_path41.join)(__dirname, "..", "..", "assets", "manga", name)
23669
24025
  ];
23670
24026
  for (const candidate of candidates) {
23671
24027
  try {
23672
- const body = await (0, import_promises31.readFile)(candidate);
24028
+ const body = await (0, import_promises32.readFile)(candidate);
23673
24029
  response.setHeader("cache-control", "no-cache");
23674
24030
  sendBinary(response, 200, contentTypeForAsset(candidate), body);
23675
24031
  return;
@@ -23679,7 +24035,7 @@ async function sendMangaAsset(pathname, response) {
23679
24035
  sendText(response, 404, "Asset not found");
23680
24036
  }
23681
24037
  function contentTypeForAsset(pathname) {
23682
- switch ((0, import_node_path40.extname)(pathname).toLowerCase()) {
24038
+ switch ((0, import_node_path41.extname)(pathname).toLowerCase()) {
23683
24039
  case ".svg":
23684
24040
  return "image/svg+xml; charset=utf-8";
23685
24041
  case ".png":
@@ -23696,14 +24052,14 @@ function contentTypeForAsset(pathname) {
23696
24052
  }
23697
24053
  async function sendBundledReportAsset(pathname, response) {
23698
24054
  const rel = decodeURIComponent(pathname.replace(/^\/report\//, ""));
23699
- const assetRoot = (0, import_node_path40.resolve)(getBundledReportAssetsDir());
23700
- const absolute = (0, import_node_path40.resolve)(assetRoot, rel);
24055
+ const assetRoot = (0, import_node_path41.resolve)(getBundledReportAssetsDir());
24056
+ const absolute = (0, import_node_path41.resolve)(assetRoot, rel);
23701
24057
  if (!absolute.startsWith(assetRoot)) {
23702
24058
  sendText(response, 403, "Forbidden");
23703
24059
  return;
23704
24060
  }
23705
24061
  try {
23706
- const body = await (0, import_promises31.readFile)(absolute);
24062
+ const body = await (0, import_promises32.readFile)(absolute);
23707
24063
  sendBinary(response, 200, contentTypeForAsset(absolute), body);
23708
24064
  } catch {
23709
24065
  sendText(response, 404, "Asset not found");
@@ -23725,18 +24081,18 @@ async function bundleStackClient(entryPath) {
23725
24081
  return output.text;
23726
24082
  }
23727
24083
  async function sendStackAppJs(response) {
23728
- const distPath = (0, import_node_path40.join)(__dirname, "stack", "app.js");
24084
+ const distPath = (0, import_node_path41.join)(__dirname, "stack", "app.js");
23729
24085
  try {
23730
- const body = await (0, import_promises31.readFile)(distPath);
24086
+ const body = await (0, import_promises32.readFile)(distPath);
23731
24087
  response.writeHead(200, { "content-type": "application/javascript; charset=utf-8", "cache-control": "no-store" });
23732
24088
  response.end(body);
23733
24089
  return;
23734
24090
  } catch {
23735
24091
  }
23736
- const entryPath = (0, import_node_path40.join)(__dirname, "stack", "main.ts");
24092
+ const entryPath = (0, import_node_path41.join)(__dirname, "stack", "main.ts");
23737
24093
  let mtimeMs;
23738
24094
  try {
23739
- mtimeMs = (await (0, import_promises31.stat)(entryPath)).mtimeMs;
24095
+ mtimeMs = (await (0, import_promises32.stat)(entryPath)).mtimeMs;
23740
24096
  } catch {
23741
24097
  sendText(response, 404, "Stack client entry not found: src/local-ui/stack/main.ts is missing.");
23742
24098
  return;
@@ -23755,7 +24111,7 @@ async function sendStackAppJs(response) {
23755
24111
  }
23756
24112
  async function sendStackAppCss(response) {
23757
24113
  try {
23758
- 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"));
23759
24115
  response.writeHead(200, { "content-type": "text/css; charset=utf-8", "cache-control": "no-store" });
23760
24116
  response.end(body);
23761
24117
  } catch {
@@ -23764,7 +24120,7 @@ async function sendStackAppCss(response) {
23764
24120
  }
23765
24121
  async function readProviderVerifyLedger(cwd) {
23766
24122
  try {
23767
- 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");
23768
24124
  const parsed = JSON.parse(raw.replace(/^\uFEFF/, ""));
23769
24125
  return isRecord5(parsed) ? parsed : {};
23770
24126
  } catch {
@@ -23774,8 +24130,8 @@ async function readProviderVerifyLedger(cwd) {
23774
24130
  async function writeProviderVerifyEntry(cwd, providerId, entry) {
23775
24131
  const ledger = await readProviderVerifyLedger(cwd);
23776
24132
  ledger[providerId] = entry;
23777
- await (0, import_promises31.mkdir)((0, import_node_path40.join)(cwd, ".viberaven"), { recursive: true });
23778
- await (0, import_promises31.writeFile)((0, import_node_path40.join)(cwd, ".viberaven", "provider-verify.json"), `${JSON.stringify(ledger, null, 2)}
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)}
23779
24135
  `, "utf8");
23780
24136
  }
23781
24137
  async function handleRequest(request, response, options) {
@@ -23848,13 +24204,13 @@ async function handleRequest(request, response, options) {
23848
24204
  return;
23849
24205
  }
23850
24206
  if (request.method === "GET" && url.pathname === "/api/architecture-map") {
23851
- const mapPath = (0, import_node_path40.join)(options.cwd, ".viberaven", "architecture-map.md");
23852
- const markdown = await (0, import_promises31.readFile)(mapPath, "utf8").catch(() => null);
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);
23853
24209
  if (markdown === null) {
23854
24210
  sendJsonStatus(response, 404, { error: "No architecture map yet. Run a scan first (it is written on every scan)." });
23855
24211
  return;
23856
24212
  }
23857
- 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);
23858
24214
  let data = null;
23859
24215
  if (dataRaw) {
23860
24216
  try {
@@ -23863,7 +24219,7 @@ async function handleRequest(request, response, options) {
23863
24219
  data = null;
23864
24220
  }
23865
24221
  }
23866
- 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);
23867
24223
  let gaps = [];
23868
24224
  if (contextRaw) {
23869
24225
  try {
@@ -24129,35 +24485,35 @@ async function handleRequest(request, response, options) {
24129
24485
  return;
24130
24486
  }
24131
24487
  if (request.method === "GET" && url.pathname === "/api/artifacts/tasklist") {
24132
- 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);
24133
24489
  return;
24134
24490
  }
24135
24491
  if (request.method === "GET" && url.pathname === "/api/artifacts/playbook") {
24136
- 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);
24137
24493
  return;
24138
24494
  }
24139
24495
  if (request.method === "GET" && url.pathname === "/api/artifacts/audit") {
24140
- await sendArtifact(options.cwd, (0, import_node_path40.join)(".viberaven", "audit-vercel-supabase.txt"), response);
24496
+ await sendArtifact(options.cwd, (0, import_node_path41.join)(".viberaven", "audit-vercel-supabase.txt"), response);
24141
24497
  return;
24142
24498
  }
24143
24499
  if (request.method === "GET" && url.pathname === "/api/artifacts/cleanup") {
24144
- 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);
24145
24501
  return;
24146
24502
  }
24147
24503
  if (request.method === "GET" && url.pathname === "/api/artifacts/preview") {
24148
- 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);
24149
24505
  return;
24150
24506
  }
24151
24507
  if (request.method === "GET" && url.pathname === "/api/artifacts/heal-plan") {
24152
- await sendArtifact(options.cwd, (0, import_node_path40.join)(".viberaven", "heal-plan.md"), response);
24508
+ await sendArtifact(options.cwd, (0, import_node_path41.join)(".viberaven", "heal-plan.md"), response);
24153
24509
  return;
24154
24510
  }
24155
24511
  if (request.method === "GET" && url.pathname === "/api/artifacts/heal-prompt") {
24156
- await sendArtifact(options.cwd, (0, import_node_path40.join)(".viberaven", "heal-prompt.md"), response);
24512
+ await sendArtifact(options.cwd, (0, import_node_path41.join)(".viberaven", "heal-prompt.md"), response);
24157
24513
  return;
24158
24514
  }
24159
24515
  if (request.method === "GET" && url.pathname === "/api/artifacts/heal-apply") {
24160
- await sendArtifact(options.cwd, (0, import_node_path40.join)(".viberaven", "heal-apply-result.json"), response, "application/json; charset=utf-8");
24516
+ await sendArtifact(options.cwd, (0, import_node_path41.join)(".viberaven", "heal-apply-result.json"), response, "application/json; charset=utf-8");
24161
24517
  return;
24162
24518
  }
24163
24519
  if (request.method === "GET" && url.pathname === "/api/artifacts/report") {
@@ -24183,12 +24539,12 @@ async function handleRequest(request, response, options) {
24183
24539
  }
24184
24540
  if (request.method === "GET" && url.pathname === "/vendor/anime.umd.min.js") {
24185
24541
  for (const candidate of [
24186
- (0, import_node_path40.join)(__dirname, "vendor", "anime.umd.min.js"),
24187
- (0, import_node_path40.join)(__dirname, "..", "assets", "vendor", "anime.umd.min.js"),
24188
- (0, import_node_path40.join)(__dirname, "..", "..", "assets", "vendor", "anime.umd.min.js")
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")
24189
24545
  ]) {
24190
24546
  try {
24191
- const body = await (0, import_promises31.readFile)(candidate);
24547
+ const body = await (0, import_promises32.readFile)(candidate);
24192
24548
  response.setHeader("cache-control", "public, max-age=604800, immutable");
24193
24549
  sendBinary(response, 200, "text/javascript; charset=utf-8", body);
24194
24550
  return;
@@ -24405,7 +24761,7 @@ function listen(server, startPort) {
24405
24761
  });
24406
24762
  }
24407
24763
  async function startLocalUiServer(options) {
24408
- const cwd = (0, import_node_path40.resolve)(options.cwd);
24764
+ const cwd = (0, import_node_path41.resolve)(options.cwd);
24409
24765
  const scanRunner = options.scanRunner ?? defaultScanRunner;
24410
24766
  const autoScan = { state: "idle" };
24411
24767
  const fullCheck = new FullCheckService(cwd, options.fullCheck);
@@ -24422,7 +24778,7 @@ async function startLocalUiServer(options) {
24422
24778
  });
24423
24779
  const port = await listen(server, options.port ?? 4177);
24424
24780
  guard.port = port;
24425
- 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(
24426
24782
  () => true,
24427
24783
  () => false
24428
24784
  );
@@ -24461,16 +24817,16 @@ async function waitForServerShutdown() {
24461
24817
  process.once("SIGTERM", done);
24462
24818
  });
24463
24819
  }
24464
- var import_node_http, import_node_child_process10, import_node_crypto6, import_promises31, import_node_os3, import_node_path40, CLI_AGENT_SETUP, MCP_CONFIG_PATHS, MCP_PROVIDER_NEEDLES, MCP_CONNECT_COMMANDS, MCP_SERVER_RECIPES, STUDIO_TOKEN_HEADER, PROVIDER_IDS, STUDIO_STATUS_ARGS, CLI_AUTH_PROBE, AGENT_RUN_TIMEOUT_MS, activeAgentRuns, stackBundleCache;
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;
24465
24821
  var init_server = __esm({
24466
24822
  "src/local-ui/server.ts"() {
24467
24823
  "use strict";
24468
24824
  import_node_http = require("node:http");
24469
24825
  import_node_child_process10 = require("node:child_process");
24470
24826
  import_node_crypto6 = require("node:crypto");
24471
- import_promises31 = require("node:fs/promises");
24827
+ import_promises32 = require("node:fs/promises");
24472
24828
  import_node_os3 = require("node:os");
24473
- import_node_path40 = require("node:path");
24829
+ import_node_path41 = require("node:path");
24474
24830
  init_initGithub();
24475
24831
  init_cleanPlan();
24476
24832
  init_audit();
@@ -24512,8 +24868,8 @@ var init_server = __esm({
24512
24868
  MCP_CONFIG_PATHS = [
24513
24869
  ".mcp.json",
24514
24870
  "mcp.json",
24515
- (0, import_node_path40.join)(".cursor", "mcp.json"),
24516
- (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")
24517
24873
  ];
24518
24874
  MCP_PROVIDER_NEEDLES = {
24519
24875
  supabase: ["supabase", "@supabase/mcp", "mcp-server-supabase"],
@@ -24595,6 +24951,7 @@ function buildNextActionBlock(tasks, loopState) {
24595
24951
  mcpTool: repoCodeTask.mcpTool,
24596
24952
  mcpArgs: repoCodeTask.mcpArgs,
24597
24953
  fallbackCommand: repoCodeTask.mcpArgs ? `npx -y viberaven --heal --apply --gap ${repoCodeTask.gapId} --yes` : void 0,
24954
+ ...whereAndAfter(repoCodeTask),
24598
24955
  requiresUserAction: false
24599
24956
  };
24600
24957
  }
@@ -24608,6 +24965,7 @@ function buildNextActionBlock(tasks, loopState) {
24608
24965
  mcpTool: userTask.mcpTool,
24609
24966
  mcpArgs: userTask.mcpArgs,
24610
24967
  fallbackCommand: userTask.mcpArgs ? `npx -y viberaven --heal --apply --gap ${userTask.gapId} --yes` : void 0,
24968
+ ...whereAndAfter(userTask),
24611
24969
  requiresUserAction: true
24612
24970
  };
24613
24971
  }
@@ -24623,10 +24981,17 @@ function buildNextActionBlock(tasks, loopState) {
24623
24981
  return {
24624
24982
  ...base,
24625
24983
  type: "done",
24626
- title: "All gaps resolved \u2014 production gate is clear",
24984
+ title: "No repo-code or provider tasks left; gate-result.json has the verdict",
24627
24985
  requiresUserAction: false
24628
24986
  };
24629
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
+ }
24630
24995
  function resolveStallReason(tasks) {
24631
24996
  if (tasks.length === 0) return "no-recipes";
24632
24997
  const allProviderAction = tasks.every((t) => t.fixType === "provider-action");
@@ -24684,9 +25049,9 @@ var init_nextActionBlock = __esm({
24684
25049
  function defaultMcpConfigPaths() {
24685
25050
  const home = (0, import_node_os4.homedir)();
24686
25051
  return [
24687
- (0, import_node_path41.join)(home, ".config", "claude", "claude_desktop_config.json"),
24688
- (0, import_node_path41.join)(home, ".cursor", "mcp.json"),
24689
- (0, import_node_path41.join)(home, ".gemini", "antigravity", "mcp_config.json")
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")
24690
25055
  ];
24691
25056
  }
24692
25057
  function resolveConfigPaths() {
@@ -24715,11 +25080,11 @@ function findServerEntry(servers, provider2) {
24715
25080
  function findProviderMcpConfig(provider2, configPaths) {
24716
25081
  const paths = configPaths ?? resolveConfigPaths();
24717
25082
  for (const path of paths) {
24718
- if (!(0, import_node_fs15.existsSync)(path)) {
25083
+ if (!(0, import_node_fs16.existsSync)(path)) {
24719
25084
  continue;
24720
25085
  }
24721
25086
  try {
24722
- const raw = JSON.parse((0, import_node_fs15.readFileSync)(path, "utf8"));
25087
+ const raw = JSON.parse((0, import_node_fs16.readFileSync)(path, "utf8"));
24723
25088
  const servers = parseMcpServers(raw);
24724
25089
  if (!servers) {
24725
25090
  continue;
@@ -24763,13 +25128,13 @@ async function verifyProviderGap(options) {
24763
25128
  fallbackCommand: FALLBACK_COMMAND
24764
25129
  };
24765
25130
  }
24766
- 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;
24767
25132
  var init_providerMcpBridge = __esm({
24768
25133
  "src/providerMcpBridge.ts"() {
24769
25134
  "use strict";
24770
- import_node_fs15 = require("node:fs");
25135
+ import_node_fs16 = require("node:fs");
24771
25136
  import_node_os4 = require("node:os");
24772
- import_node_path41 = require("node:path");
25137
+ import_node_path42 = require("node:path");
24773
25138
  FALLBACK_COMMAND = "npx -y viberaven audit --vercel-supabase --json";
24774
25139
  SUPPORTED_PROVIDERS = /* @__PURE__ */ new Set(["supabase", "vercel"]);
24775
25140
  }
@@ -24992,7 +25357,7 @@ async function cmdProviderVerify(flags, positional) {
24992
25357
  return result.verified ? 0 : 1;
24993
25358
  }
24994
25359
  async function cmdStatus(flags, positional) {
24995
- 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();
24996
25361
  let artifact;
24997
25362
  try {
24998
25363
  artifact = await loadLastArtifact(startDir);
@@ -25031,7 +25396,7 @@ async function cmdStatus(flags, positional) {
25031
25396
  return 0;
25032
25397
  }
25033
25398
  async function runScanCommand(flags, positional, options) {
25034
- 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());
25035
25400
  if (!options?.deferMachineOutput) {
25036
25401
  console.log(`Scanning ${workspacePath}\u2026 (local, offline)`);
25037
25402
  }
@@ -25055,15 +25420,27 @@ async function runScanCommand(flags, positional, options) {
25055
25420
  const updatedState = resetBatch(loopState, openGapCount);
25056
25421
  const tasks = buildTaskList(artifact);
25057
25422
  const block = buildNextActionBlock(tasks, updatedState);
25058
- printNextActionBlock(block);
25059
- printProviderActionBlock(tasks);
25423
+ let manifest;
25060
25424
  try {
25061
- const manifest = JSON.parse(await (0, import_promises32.readFile)(paths.actionsPath, "utf8"));
25062
- console.log(renderActionSurface(manifest, { limit: 5 }).trimEnd());
25425
+ manifest = JSON.parse(await (0, import_promises33.readFile)(paths.actionsPath, "utf8"));
25063
25426
  } catch (error) {
25064
25427
  console.warn(error instanceof Error ? error.message : String(error));
25065
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
+ }
25066
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
+ }
25067
25444
  }
25068
25445
  if (flags.open) {
25069
25446
  try {
@@ -25075,7 +25452,7 @@ async function runScanCommand(flags, positional, options) {
25075
25452
  return { exitCode: 0, artifacts: paths };
25076
25453
  }
25077
25454
  async function cmdReport(flags, positional) {
25078
- 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();
25079
25456
  try {
25080
25457
  const paths = await refreshReportFromDisk(startDir);
25081
25458
  console.log(`Report refreshed: ${paths.reportPath}`);
@@ -25097,7 +25474,7 @@ async function cmdReport(flags, positional) {
25097
25474
  }
25098
25475
  }
25099
25476
  async function cmdPrompt(flags, positional) {
25100
- 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();
25101
25478
  let artifact;
25102
25479
  try {
25103
25480
  artifact = await loadLastArtifact(startDir);
@@ -25194,7 +25571,7 @@ async function main() {
25194
25571
  const wantsJsonl = hasFlag(flags, "jsonl");
25195
25572
  const wantsStrict = hasFlag(flags, "strict");
25196
25573
  if (flags.condense) {
25197
- 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();
25198
25575
  const result = await runCondenseCommand({ cwd });
25199
25576
  console.log(`VibeRaven context map refreshed: ${result.contextMapPath}`);
25200
25577
  return 0;
@@ -25214,18 +25591,18 @@ async function main() {
25214
25591
  }
25215
25592
  if (!command && flags.verify === true && typeof flags.action === "string") {
25216
25593
  return runVerifyActionCommand({
25217
- 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(),
25218
25595
  actionId: flags.action
25219
25596
  });
25220
25597
  }
25221
25598
  if (!command && (isAgentMode || flags.verify === true || wantsJson || wantsJsonl || wantsStrict)) {
25222
25599
  const deferMachineOutput = wantsJson || wantsJsonl;
25223
- const scanResult = await runScanCommand(flags, positional, { deferMachineOutput });
25600
+ const scanResult = await runScanCommand(flags, positional, { deferMachineOutput, markFollowUpsShown: true });
25224
25601
  if ((wantsJson || wantsJsonl) && !scanResult.artifacts) {
25225
25602
  console.error("VibeRaven could not produce machine output because scan artifacts were not written.");
25226
25603
  return 3;
25227
25604
  }
25228
- 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;
25229
25606
  const strictExitCode = wantsStrict && gateResult ? exitCodeForStrictGate(gateResult, { failOnWarnings: flags.strict === "warning" }) : scanResult.exitCode;
25230
25607
  if (wantsJson && gateResult) {
25231
25608
  process.stdout.write(renderGateResultJson(gateResult));
@@ -25247,7 +25624,7 @@ async function main() {
25247
25624
  env: process.env
25248
25625
  });
25249
25626
  if (mode === "local-ui") {
25250
- 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();
25251
25628
  const handle = await startLocalUiServer({ cwd, initialPath: stackInitialPath(flags) });
25252
25629
  console.log(`VibeRaven local UI: ${handle.url}`);
25253
25630
  await waitForServerShutdown();
@@ -25258,7 +25635,7 @@ async function main() {
25258
25635
  await runInteractiveSession();
25259
25636
  return 0;
25260
25637
  }
25261
- const scanResult = await runScanCommand({ "agent-mode": true }, positional);
25638
+ const scanResult = await runScanCommand({ "agent-mode": true }, positional, { markFollowUpsShown: true });
25262
25639
  return scanResult.exitCode;
25263
25640
  }
25264
25641
  switch (command) {
@@ -25267,7 +25644,7 @@ async function main() {
25267
25644
  await runInteractiveSession();
25268
25645
  return 0;
25269
25646
  case "ui": {
25270
- 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();
25271
25648
  const envPort = Number(process.env.PORT);
25272
25649
  const handle = await startLocalUiServer({ cwd, port: Number.isFinite(envPort) && envPort > 0 ? envPort : void 0, initialPath: stackInitialPath(flags) });
25273
25650
  console.log(`VibeRaven local UI: ${handle.url}`);
@@ -25279,14 +25656,14 @@ async function main() {
25279
25656
  return cmdStatus(flags, positional);
25280
25657
  case "check":
25281
25658
  return runCheckCommand({
25282
- 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(),
25283
25660
  json: Boolean(flags.json),
25284
25661
  quiet: Boolean(flags.quiet),
25285
25662
  base: typeof flags.base === "string" ? flags.base : void 0,
25286
25663
  markdown: flags.markdown === true
25287
25664
  });
25288
25665
  case "fix": {
25289
- 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();
25290
25667
  const gapId = typeof flags.gap === "string" ? flags.gap : void 0;
25291
25668
  if (!gapId) {
25292
25669
  const artifact = await loadLastArtifact(fixCwd).catch(() => void 0);
@@ -25315,19 +25692,19 @@ async function main() {
25315
25692
  }
25316
25693
  case "actions":
25317
25694
  return runActionsCommand({
25318
- 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(),
25319
25696
  json: Boolean(flags.json)
25320
25697
  });
25321
25698
  case "preview":
25322
25699
  return runPreviewCommand({
25323
- 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(),
25324
25701
  agentMode: flags["agent-mode"] === true,
25325
25702
  json: Boolean(flags.json)
25326
25703
  });
25327
25704
  case "next":
25328
25705
  return runNextCommand({
25329
25706
  json: Boolean(flags.json),
25330
- 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()
25331
25708
  });
25332
25709
  case "guide": {
25333
25710
  const provider2 = positional[0];
@@ -25349,7 +25726,7 @@ async function main() {
25349
25726
  cwd: process.cwd()
25350
25727
  });
25351
25728
  case "scan": {
25352
- const scanResult = await runScanCommand(flags, positional);
25729
+ const scanResult = await runScanCommand(flags, positional, { markFollowUpsShown: true });
25353
25730
  return scanResult.exitCode;
25354
25731
  }
25355
25732
  case "report":
@@ -25361,7 +25738,7 @@ async function main() {
25361
25738
  case "provider-verify":
25362
25739
  return cmdProviderVerify(flags, positional);
25363
25740
  case "init": {
25364
- 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();
25365
25742
  const agents = typeof flags.agents === "string" ? flags.agents : void 0;
25366
25743
  if (flags.github === true) {
25367
25744
  return runInitGithubCommand({ cwd, dryRun: flags["dry-run"] === true, force: flags.force === true });
@@ -25378,7 +25755,7 @@ async function main() {
25378
25755
  return 1;
25379
25756
  }
25380
25757
  return runDoctorAgentsCommand({
25381
- 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()
25382
25759
  });
25383
25760
  case "validate-npm-package":
25384
25761
  return runValidateNpmPackageCommand({
@@ -25391,7 +25768,7 @@ async function main() {
25391
25768
  return 1;
25392
25769
  }
25393
25770
  return runAuditCommand({
25394
- 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(),
25395
25772
  json: Boolean(flags.json)
25396
25773
  });
25397
25774
  case "clean": {
@@ -25399,7 +25776,7 @@ async function main() {
25399
25776
  console.error("Usage: viberaven clean --plan [path]");
25400
25777
  return 1;
25401
25778
  }
25402
- 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();
25403
25780
  const plan = buildContextCleanupPlan({ projectRoot: cwd, files: await collectCleanupFiles(cwd) });
25404
25781
  const outputPath = await writeCleanupPlan(cwd, plan);
25405
25782
  console.log(`Wrote ${outputPath}
@@ -25409,9 +25786,9 @@ async function main() {
25409
25786
  }
25410
25787
  case "rls-test":
25411
25788
  return runRlsTestCommand({
25412
- cwd: positional[0] ? (0, import_node_path42.resolve)(process.cwd(), positional[0]) : process.cwd(),
25413
- matrix: typeof flags.matrix === "string" ? (0, import_node_path42.resolve)(process.cwd(), flags.matrix) : void 0,
25414
- migrations: typeof flags.migrations === "string" ? (0, import_node_path42.resolve)(process.cwd(), flags.migrations) : void 0,
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,
25415
25792
  json: Boolean(flags.json)
25416
25793
  });
25417
25794
  default:
@@ -25420,11 +25797,11 @@ async function main() {
25420
25797
  return 1;
25421
25798
  }
25422
25799
  }
25423
- var import_promises32, import_node_path42, STACK_AREAS;
25800
+ var import_promises33, import_node_path43, STACK_AREAS;
25424
25801
  var init_cli = __esm({
25425
25802
  "src/cli.ts"() {
25426
- import_promises32 = require("node:fs/promises");
25427
- import_node_path42 = require("node:path");
25803
+ import_promises33 = require("node:fs/promises");
25804
+ import_node_path43 = require("node:path");
25428
25805
  init_config();
25429
25806
  init_runScan();
25430
25807
  init_artifacts2();
@@ -25466,6 +25843,7 @@ var init_cli = __esm({
25466
25843
  init_buildTaskList();
25467
25844
  init_providerMcpBridge();
25468
25845
  init_render();
25846
+ init_registry2();
25469
25847
  STACK_AREAS = /* @__PURE__ */ new Set(["database", "auth", "payments", "deployment", "monitoring", "security"]);
25470
25848
  if (require.main === module) {
25471
25849
  main().then((code) => {