@viberaven/cli 1.0.2 → 1.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -170,8 +170,8 @@ __export(cli_exports, {
170
170
  runScanCommand: () => runScanCommand
171
171
  });
172
172
  module.exports = __toCommonJS(cli_exports);
173
- var import_promises17 = require("node:fs/promises");
174
- var import_node_path22 = require("node:path");
173
+ var import_promises19 = require("node:fs/promises");
174
+ var import_node_path25 = require("node:path");
175
175
 
176
176
  // src/config.ts
177
177
  var import_node_os = require("node:os");
@@ -585,14 +585,18 @@ function formatUsageLine(usage) {
585
585
  const periodLabel = usage.period === "monthly" ? "this month" : "lifetime";
586
586
  return `Scans: ${usage.used}/${usage.limit} (${periodLabel}, ${usage.plan}) \xB7 ${usage.remainingPrompts} remaining`;
587
587
  }
588
+ function normalizeUpgradeUrl(url) {
589
+ return url.replace("https://viberice.com/account", "https://viberaven.dev/account");
590
+ }
588
591
  function formatScanLimitMessage(upgradeUrl) {
592
+ const safeUpgradeUrl = normalizeUpgradeUrl(upgradeUrl);
589
593
  return [
590
594
  "",
591
595
  formatAgentStatus(UPGRADE_REQUIRED, "Free scan limit reached. Upgrade to Pro to continue."),
592
596
  "Your last scan artifacts are unchanged if you already ran a scan in this repo.",
593
597
  "Do not keep retrying this scan until the user upgrades or quota resets.",
594
598
  "",
595
- `Upgrade & account: ${upgradeUrl}`,
599
+ `Upgrade & account: ${safeUpgradeUrl}`,
596
600
  ""
597
601
  ].join("\n");
598
602
  }
@@ -8802,7 +8806,8 @@ var KNOWN_RECIPE_GAP_IDS = /* @__PURE__ */ new Set([
8802
8806
  "missing_404_page",
8803
8807
  // W3 file-patch recipes
8804
8808
  "missing_csp_header",
8805
- "missing_rate_limit"
8809
+ "missing_rate_limit",
8810
+ "eslint_restricted_imports"
8806
8811
  // NOTE: 'rls_disabled' is intentionally NOT here — it is provider-action only,
8807
8812
  // canAutoApply=false, and should be classified as 'provider-action' by buildTaskList.
8808
8813
  ]);
@@ -10637,7 +10642,7 @@ function isRecord6(value) {
10637
10642
  }
10638
10643
 
10639
10644
  // src/tui/runInteractive.ts
10640
- var import_node_path12 = require("node:path");
10645
+ var import_node_path14 = require("node:path");
10641
10646
 
10642
10647
  // ../../node_modules/@clack/core/dist/index.mjs
10643
10648
  var import_sisteransi = __toESM(require_src(), 1);
@@ -11224,7 +11229,7 @@ function buildAgentFixPrompt(artifact, gap) {
11224
11229
  }
11225
11230
 
11226
11231
  // src/version.ts
11227
- var VERSION = "1.0.2";
11232
+ var VERSION = "1.0.3";
11228
11233
 
11229
11234
  // src/commands/guide.ts
11230
11235
  var import_picocolors3 = __toESM(require_picocolors());
@@ -11487,8 +11492,8 @@ function renderVercelSupabaseAudit(result) {
11487
11492
  }
11488
11493
 
11489
11494
  // src/commands/initRules.ts
11490
- var import_promises8 = require("node:fs/promises");
11491
- var import_node_path11 = require("node:path");
11495
+ var import_promises10 = require("node:fs/promises");
11496
+ var import_node_path13 = require("node:path");
11492
11497
 
11493
11498
  // src/commands/agentRulesBlock.ts
11494
11499
  var VIBERAVEN_BLOCK_START = "<!-- VIBERAVEN:START -->";
@@ -11504,9 +11509,39 @@ Canonical command: \`${PUBLIC_AGENT_MODE_COMMAND}\`
11504
11509
 
11505
11510
  Use VibeRaven before launch, deployment, real users, auth, billing, database, RLS, env vars, webhooks, monitoring, or tests.
11506
11511
 
11507
- Agent loop: run VibeRaven, read Mission Map (\`.viberaven/mission-map.md\`), fix one launch gap, re-run VibeRaven.`;
11512
+ Agent loop: run VibeRaven, read Mission Map (\`.viberaven/mission-map.md\`), fix one launch gap, re-run VibeRaven.
11513
+
11514
+ Maps repo evidence to OWASP LLM Top 10 launch risks. Supports SAFECode-style deployment readiness checks.`;
11515
+ var STACK_FRAMING = `Stack context for this repo: Next.js, Supabase, Vercel, VibeRaven.
11516
+ Do NOT deploy to Vercel or ship to production users without running \`${PUBLIC_AGENT_MODE_COMMAND}\`.
11517
+ Do NOT rely on manual production checklists when VibeRaven gate artifacts exist in \`.viberaven/\`.`;
11518
+ var AGENT_NEGATIVE_CONSTRAINTS = `## Event-Triggered Production Verification
11519
+
11520
+ Trigger: modifications to \`package.json\`, \`vercel.json\`, \`supabase/migrations/**/*\`, \`.env.example\`, auth/billing/webhook paths, or deploy workflow files.
11521
+
11522
+ 1. Do NOT claim "production ready" or close a deploy checklist until \`gate.status === "clear"\` in \`.viberaven/gate-result.json\`.
11523
+ 2. Do NOT run verify after every file patch. Batch heals, then \`${PUBLIC_VERIFY_COMMAND}\` once per loop.
11524
+ 3. Do NOT \`git push\` or deploy after auth, RLS, billing, or webhook changes without \`${PUBLIC_AGENT_MODE_COMMAND}\`.
11525
+ 4. If anomalies appear, read \`.viberaven/agent-tasklist.md\` fully before writing new repo logic.
11526
+ 5. Do NOT mark production-ready because the local dev server starts successfully; INSTEAD run \`${PUBLIC_AGENT_MODE_COMMAND}\` and read \`.viberaven/gate-result.json\`.
11527
+ 6. Do NOT treat the Supabase dashboard UI as proof of RLS; INSTEAD fix repo evidence gaps from \`.viberaven/agent-tasklist.md\`.
11528
+ 7. Do NOT add or change npm dependencies without calling \`viberaven_validate_npm_package\` (MCP) or verifying the package exists on the public npm registry; INSTEAD run \`${PUBLIC_AGENT_MODE_COMMAND}\` after dependency changes.
11529
+ 8. Do NOT refactor or edit files unrelated to the current TASK-001 gap; INSTEAD make minimal, targeted changes.
11530
+ 9. Do NOT install new dependencies unless TASK-001 or the user explicitly requires them.
11531
+ 10. Do NOT claim provider dashboard, billing, DNS, or webhook setup is complete from repo edits alone; INSTEAD present provider-action steps from the tasklist.
11532
+
11533
+ Prefer \`viberaven_check_readiness\` when MCP is configured; otherwise \`${PUBLIC_AGENT_MODE_COMMAND}\`.`;
11534
+ var ANCHOR_FOOTER = `## Anchor (mandatory)
11535
+
11536
+ Before ending any deploy, auth, RLS, webhook, or dependency task, run:
11537
+ \`${PUBLIC_AGENT_MODE_COMMAND}\`
11538
+ Gate is not clear until \`gate.status === "clear"\` in \`.viberaven/gate-result.json\`.`;
11508
11539
  var AGENT_RULES_BODY = `${AGENT_RULES_PREAMBLE}
11509
11540
 
11541
+ ${STACK_FRAMING}
11542
+
11543
+ ${AGENT_NEGATIVE_CONSTRAINTS}
11544
+
11510
11545
  ## VibeRaven Production-Readiness Gate
11511
11546
 
11512
11547
  - Before running build, compile, cleanup, deployment, vercel, or supabase commands, run \`${PUBLIC_AGENT_MODE_COMMAND}\` first from the project root.
@@ -11571,7 +11606,9 @@ var BLOCK_MARKER_PAIRS = [
11571
11606
  [VIBERAVEN_LEGACY_BLOCK_START, VIBERAVEN_LEGACY_BLOCK_END]
11572
11607
  ];
11573
11608
  function buildAgentRulesBlock() {
11574
- return wrapViberavenBlock(AGENT_RULES_BODY);
11609
+ return wrapViberavenBlock(`${AGENT_RULES_BODY}
11610
+
11611
+ ${ANCHOR_FOOTER}`);
11575
11612
  }
11576
11613
  function buildAgentContextBlock() {
11577
11614
  return wrapViberavenBlock(AGENT_CONTEXT_BODY);
@@ -11582,11 +11619,31 @@ function buildMissionMapBlock() {
11582
11619
  function wrapViberavenBlock(body) {
11583
11620
  return [VIBERAVEN_BLOCK_START, body, VIBERAVEN_BLOCK_END].join("\n");
11584
11621
  }
11622
+ function stripLegacyFrontmatterBeforeViberavenBlock(content) {
11623
+ const blockStarts = [VIBERAVEN_BLOCK_START, VIBERAVEN_LEGACY_BLOCK_START];
11624
+ let blockIndex = -1;
11625
+ for (const start of blockStarts) {
11626
+ const index = content.indexOf(start);
11627
+ if (index !== -1 && (blockIndex === -1 || index < blockIndex)) {
11628
+ blockIndex = index;
11629
+ }
11630
+ }
11631
+ if (blockIndex <= 0) {
11632
+ return content;
11633
+ }
11634
+ const beforeBlock = content.slice(0, blockIndex);
11635
+ if (!beforeBlock.trimStart().startsWith("---")) {
11636
+ return content;
11637
+ }
11638
+ const strippedPrefix = beforeBlock.replace(/^---[\s\S]*?---\s*/m, "");
11639
+ return `${strippedPrefix}${content.slice(blockIndex)}`;
11640
+ }
11585
11641
  function injectAgentRulesBlock(existingContent, replacementBlock = buildAgentRulesBlock()) {
11586
- const existingMatch = findBoundedBlock(existingContent);
11642
+ const normalizedExisting = replacementBlock.trimStart().startsWith("---") ? stripLegacyFrontmatterBeforeViberavenBlock(existingContent) : existingContent;
11643
+ const existingMatch = findBoundedBlock(normalizedExisting);
11587
11644
  if (existingMatch) {
11588
11645
  const content = replaceExistingAgentRulesBlock({
11589
- existingContent,
11646
+ existingContent: normalizedExisting,
11590
11647
  existingMatch,
11591
11648
  replacementBlock
11592
11649
  });
@@ -11595,9 +11652,9 @@ function injectAgentRulesBlock(existingContent, replacementBlock = buildAgentRul
11595
11652
  changed: content !== existingContent
11596
11653
  };
11597
11654
  }
11598
- const separator = existingContent.length > 0 ? "\n\n" : "";
11655
+ const separator = normalizedExisting.length > 0 ? "\n\n" : "";
11599
11656
  return {
11600
- content: `${replacementBlock}${separator}${existingContent}`,
11657
+ content: `${replacementBlock}${separator}${normalizedExisting}`,
11601
11658
  changed: true
11602
11659
  };
11603
11660
  }
@@ -11672,11 +11729,135 @@ function escapeRegExp3(value) {
11672
11729
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
11673
11730
  }
11674
11731
 
11732
+ // src/commands/cursorRulesPack.ts
11733
+ var import_promises8 = require("node:fs/promises");
11734
+ var import_node_path11 = require("node:path");
11735
+ var CURSOR_RULES_DIR = ".cursor/rules";
11736
+ var LEGACY_CURSOR_RULE_FILE = `${CURSOR_RULES_DIR}/viberaven.mdc`;
11737
+ var DOMAIN_PATH_POINTER = "Before editing these files, read `.viberaven/agent-context.md` and `.viberaven/mission-map.md`.";
11738
+ function buildCursorRulesPack() {
11739
+ return [
11740
+ {
11741
+ filename: "viberaven-core.mdc",
11742
+ content: [
11743
+ "---",
11744
+ "description: VibeRaven production gate \u2014 canonical agent commands and gate loop",
11745
+ "alwaysApply: true",
11746
+ "---",
11747
+ "",
11748
+ `Run \`${PUBLIC_AGENT_MODE_COMMAND}\` before deploy, auth, RLS, webhooks, or dependency changes.`,
11749
+ "",
11750
+ "Read `.viberaven/agent-tasklist.md` first, then `.viberaven/gate-result.json` and `.viberaven/mission-map.md`.",
11751
+ `Fix one gap, then \`${PUBLIC_VERIFY_COMMAND}\`. Before CI or production promote: \`${PUBLIC_STRICT_COMMAND}\`.`,
11752
+ "",
11753
+ 'Gate is not clear until `gate.status === "clear"`.',
11754
+ "",
11755
+ DOMAIN_PATH_POINTER,
11756
+ "",
11757
+ "Full production rules: `AGENTS.md` / `CLAUDE.md` (installed by `npx -y viberaven init --agents all`)."
11758
+ ].join("\n")
11759
+ },
11760
+ {
11761
+ filename: "viberaven-supabase-rls.mdc",
11762
+ content: [
11763
+ "---",
11764
+ "description: Apply when editing Supabase migrations, RLS policies, or auth SQL",
11765
+ "globs:",
11766
+ " - supabase/**",
11767
+ "alwaysApply: false",
11768
+ "---",
11769
+ "",
11770
+ DOMAIN_PATH_POINTER,
11771
+ "",
11772
+ "Do NOT enable RLS on only one table while leaving related tables open; INSTEAD read `.viberaven/mission-map.md` before migration edits."
11773
+ ].join("\n")
11774
+ },
11775
+ {
11776
+ filename: "viberaven-deploy.mdc",
11777
+ content: [
11778
+ "---",
11779
+ "description: Apply before changing Vercel config or deploy CI workflows",
11780
+ "globs:",
11781
+ " - vercel.json",
11782
+ " - .github/workflows/**",
11783
+ "alwaysApply: false",
11784
+ "---",
11785
+ "",
11786
+ DOMAIN_PATH_POINTER,
11787
+ "",
11788
+ "Do NOT add production env vars only to the Vercel dashboard without updating `.env.example` in the repo."
11789
+ ].join("\n")
11790
+ },
11791
+ {
11792
+ filename: "viberaven-payments.mdc",
11793
+ content: [
11794
+ "---",
11795
+ "description: Apply when editing Stripe, Polar, or payment webhook handlers",
11796
+ "globs:",
11797
+ ' - "**/*webhook*"',
11798
+ ' - "**/*stripe*"',
11799
+ ' - "**/*polar*"',
11800
+ "alwaysApply: false",
11801
+ "---",
11802
+ "",
11803
+ DOMAIN_PATH_POINTER,
11804
+ "",
11805
+ "Do NOT ship webhook handlers without signature verification; INSTEAD run `npx -y viberaven --agent-mode` after webhook edits."
11806
+ ].join("\n")
11807
+ }
11808
+ ];
11809
+ }
11810
+ function renderCursorCoreRulePreview() {
11811
+ return buildCursorRulesPack()[0]?.content ?? "";
11812
+ }
11813
+ async function initCursorRulesPack(options) {
11814
+ const results = [];
11815
+ const pack = buildCursorRulesPack();
11816
+ for (const rule of pack) {
11817
+ const file = `${CURSOR_RULES_DIR}/${rule.filename}`;
11818
+ const path = (0, import_node_path11.join)(options.cwd, file);
11819
+ const existing = await readExistingFile(path);
11820
+ const changed = !existing.exists || existing.content !== rule.content;
11821
+ const action = !existing.exists ? "created" : changed ? "updated" : "unchanged";
11822
+ if (!options.dryRun && changed) {
11823
+ await (0, import_promises8.mkdir)((0, import_node_path11.join)(options.cwd, CURSOR_RULES_DIR), { recursive: true });
11824
+ await (0, import_promises8.writeFile)(path, rule.content, "utf-8");
11825
+ }
11826
+ results.push({ target: "cursor", file, path, action });
11827
+ }
11828
+ const legacyPath = (0, import_node_path11.join)(options.cwd, LEGACY_CURSOR_RULE_FILE);
11829
+ if (!options.dryRun && await fileExists(legacyPath)) {
11830
+ await (0, import_promises8.rm)(legacyPath, { force: true });
11831
+ }
11832
+ return results;
11833
+ }
11834
+ async function readExistingFile(path) {
11835
+ try {
11836
+ return { exists: true, content: await (0, import_promises8.readFile)(path, "utf-8") };
11837
+ } catch (error) {
11838
+ if (isFileNotFoundError(error)) {
11839
+ return { exists: false, content: "" };
11840
+ }
11841
+ throw error;
11842
+ }
11843
+ }
11844
+ async function fileExists(path) {
11845
+ try {
11846
+ await (0, import_promises8.access)(path);
11847
+ return true;
11848
+ } catch {
11849
+ return false;
11850
+ }
11851
+ }
11852
+ function isFileNotFoundError(error) {
11853
+ return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
11854
+ }
11855
+
11675
11856
  // src/commands/agentTargets.ts
11676
11857
  var AGENT_RULE_TARGETS = {
11677
11858
  codex: { file: "AGENTS.md" },
11678
11859
  claude: { file: "CLAUDE.md" },
11679
- cursor: { file: ".cursor/rules/viberaven.mdc" },
11860
+ cursor: { file: ".cursor/rules/viberaven-core.mdc" },
11680
11861
  cursorLegacy: { file: ".cursorrules", aliases: ["cursor-legacy"] },
11681
11862
  copilot: { file: ".github/copilot-instructions.md", aliases: ["github-copilot"] },
11682
11863
  gemini: { file: "GEMINI.md" },
@@ -11722,15 +11903,7 @@ function renderAgentRulesForTarget(target) {
11722
11903
  return ["@AGENTS.md", "", buildAgentRulesBlock()].join("\n");
11723
11904
  }
11724
11905
  if (target === "cursor") {
11725
- return [
11726
- "---",
11727
- "description: VibeRaven production-readiness gate",
11728
- "globs:",
11729
- "alwaysApply: true",
11730
- "---",
11731
- "",
11732
- buildAgentRulesBlock()
11733
- ].join("\n");
11906
+ return renderCursorCoreRulePreview();
11734
11907
  }
11735
11908
  if (target === "agentContext") {
11736
11909
  return ["# VibeRaven Agent Context", "", buildAgentContextBlock()].join("\n");
@@ -11762,26 +11935,88 @@ function getAgentRulesTargets(value) {
11762
11935
  return ALL_AGENT_RULE_TARGETS.filter((target) => requestedTargets.has(target));
11763
11936
  }
11764
11937
 
11938
+ // src/commands/seedPackageJsonScripts.ts
11939
+ var import_node_fs7 = require("node:fs");
11940
+ var import_promises9 = require("node:fs/promises");
11941
+ var import_node_path12 = require("node:path");
11942
+ var VIBERAVEN_PACKAGE_JSON_SCRIPTS = {
11943
+ "viberaven:gate": PUBLIC_AGENT_MODE_COMMAND,
11944
+ "viberaven:verify": PUBLIC_VERIFY_COMMAND,
11945
+ "viberaven:strict": PUBLIC_STRICT_COMMAND
11946
+ };
11947
+ async function seedPackageJsonScripts(options) {
11948
+ const packageJsonPath = (0, import_node_path12.join)(options.cwd, "package.json");
11949
+ if (!(0, import_node_fs7.existsSync)(packageJsonPath)) {
11950
+ return null;
11951
+ }
11952
+ const raw = await (0, import_promises9.readFile)(packageJsonPath, "utf-8");
11953
+ let pkg;
11954
+ try {
11955
+ pkg = JSON.parse(raw);
11956
+ } catch {
11957
+ return {
11958
+ action: "skipped",
11959
+ added: [],
11960
+ skipped: [],
11961
+ changed: false
11962
+ };
11963
+ }
11964
+ const scripts = typeof pkg.scripts === "object" && pkg.scripts !== null && !Array.isArray(pkg.scripts) ? { ...pkg.scripts } : {};
11965
+ const added = [];
11966
+ const skipped = [];
11967
+ for (const [key, value] of Object.entries(VIBERAVEN_PACKAGE_JSON_SCRIPTS)) {
11968
+ if (key in scripts) {
11969
+ skipped.push(key);
11970
+ continue;
11971
+ }
11972
+ scripts[key] = value;
11973
+ added.push(key);
11974
+ }
11975
+ if (added.length === 0) {
11976
+ return { action: "unchanged", added, skipped, changed: false };
11977
+ }
11978
+ if (!options.dryRun) {
11979
+ pkg.scripts = scripts;
11980
+ const output = `${JSON.stringify(pkg, null, 2)}
11981
+ `;
11982
+ await (0, import_promises9.writeFile)(packageJsonPath, output, "utf-8");
11983
+ }
11984
+ return { action: "updated", added, skipped, changed: true };
11985
+ }
11986
+
11765
11987
  // src/commands/initRules.ts
11766
11988
  async function initAgentRules(options) {
11767
11989
  const targets = options.targets ?? [...CORE_AGENT_INJECTION_TARGETS];
11768
11990
  const results = [];
11769
11991
  for (const target of targets) {
11992
+ if (target === "cursor") {
11993
+ results.push(...await initCursorRulesPack({ cwd: options.cwd, dryRun: options.dryRun }));
11994
+ continue;
11995
+ }
11770
11996
  const file = AGENT_RULE_TARGETS[target].file;
11771
- const path = (0, import_node_path11.join)(options.cwd, file);
11772
- const existing = await readExistingFile(path);
11997
+ const path = (0, import_node_path13.join)(options.cwd, file);
11998
+ const existing = await readExistingFile2(path);
11773
11999
  const injected = injectAgentRulesBlock(existing.content, renderAgentRulesForTarget(target));
11774
12000
  const action = !existing.exists ? "created" : injected.changed ? "updated" : "unchanged";
11775
12001
  if (!options.dryRun && injected.changed) {
11776
- await (0, import_promises8.mkdir)((0, import_node_path11.dirname)(path), { recursive: true });
11777
- await (0, import_promises8.writeFile)(path, injected.content, "utf-8");
12002
+ await (0, import_promises10.mkdir)((0, import_node_path13.dirname)(path), { recursive: true });
12003
+ await (0, import_promises10.writeFile)(path, injected.content, "utf-8");
11778
12004
  }
11779
12005
  results.push({ target, file, path, action });
11780
12006
  }
11781
- return results;
12007
+ const packageJsonScripts = await seedPackageJsonScripts({
12008
+ cwd: options.cwd,
12009
+ dryRun: options.dryRun
12010
+ });
12011
+ return { results, packageJsonScripts };
11782
12012
  }
11783
12013
  function renderAgentRulesDryRun(targets) {
11784
- const files = targets.map((target) => `- ${target}: ${AGENT_RULE_TARGETS[target].file}`).join("\n");
12014
+ const files = targets.flatMap((target) => {
12015
+ if (target === "cursor") {
12016
+ return buildCursorRulesPack().map((rule) => `- cursor: .cursor/rules/${rule.filename}`);
12017
+ }
12018
+ return [`- ${target}: ${AGENT_RULE_TARGETS[target].file}`];
12019
+ }).join("\n");
11785
12020
  const previews = targets.flatMap((target) => [
11786
12021
  `Preview: ${target} (${AGENT_RULE_TARGETS[target].file})`,
11787
12022
  "",
@@ -11789,7 +12024,8 @@ function renderAgentRulesDryRun(targets) {
11789
12024
  ]);
11790
12025
  return [`VibeRaven agent rules dry run`, "", `Target files:`, files, "", ...previews].join("\n");
11791
12026
  }
11792
- function formatAgentRulesInitSummary(results) {
12027
+ function formatAgentRulesInitSummary(output) {
12028
+ const { results, packageJsonScripts } = output;
11793
12029
  const created = results.filter((result) => result.action === "created");
11794
12030
  const updated = results.filter((result) => result.action === "updated");
11795
12031
  const skipped = results.filter((result) => result.action === "unchanged");
@@ -11815,22 +12051,32 @@ function formatAgentRulesInitSummary(results) {
11815
12051
  }
11816
12052
  lines.push("");
11817
12053
  }
12054
+ if (packageJsonScripts?.changed) {
12055
+ lines.push("package.json scripts added:");
12056
+ for (const script of packageJsonScripts.added) {
12057
+ lines.push(` + ${script}`);
12058
+ }
12059
+ lines.push("");
12060
+ } else if (packageJsonScripts?.action === "unchanged") {
12061
+ lines.push("package.json scripts: unchanged (already present)");
12062
+ lines.push("");
12063
+ }
11818
12064
  lines.push(
11819
12065
  `Done: ${created.length} created, ${updated.length} updated, ${skipped.length} skipped.`
11820
12066
  );
11821
12067
  return lines.join("\n");
11822
12068
  }
11823
- async function readExistingFile(path) {
12069
+ async function readExistingFile2(path) {
11824
12070
  try {
11825
- return { exists: true, content: await (0, import_promises8.readFile)(path, "utf-8") };
12071
+ return { exists: true, content: await (0, import_promises10.readFile)(path, "utf-8") };
11826
12072
  } catch (error) {
11827
- if (isFileNotFoundError(error)) {
12073
+ if (isFileNotFoundError2(error)) {
11828
12074
  return { exists: false, content: "" };
11829
12075
  }
11830
12076
  throw error;
11831
12077
  }
11832
12078
  }
11833
- function isFileNotFoundError(error) {
12079
+ function isFileNotFoundError2(error) {
11834
12080
  return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
11835
12081
  }
11836
12082
 
@@ -12042,7 +12288,7 @@ async function handleAuth() {
12042
12288
  await runDeviceLogin(apiBaseUrl);
12043
12289
  }
12044
12290
  async function handleAgentRules(cwd) {
12045
- const results = await initAgentRules({ cwd });
12291
+ const { results } = await initAgentRules({ cwd });
12046
12292
  for (const result of results) {
12047
12293
  const color = result.action === "created" ? import_picocolors4.default.green : result.action === "updated" ? import_picocolors4.default.yellow : import_picocolors4.default.dim;
12048
12294
  M2.message(color(`${result.action.toUpperCase()}: ${result.file}`));
@@ -12130,7 +12376,7 @@ async function runInteractiveSession(startDir = process.cwd()) {
12130
12376
  Ie(`${import_picocolors4.default.bold("VibeRaven")} ${import_picocolors4.default.dim(VERSION)}`);
12131
12377
  const cwd = await resolveWorkspaceRoot(startDir);
12132
12378
  const artifactsAt = await findArtifactsWorkspace(startDir);
12133
- if (artifactsAt && (0, import_node_path12.resolve)(artifactsAt) !== (0, import_node_path12.resolve)(startDir)) {
12379
+ if (artifactsAt && (0, import_node_path14.resolve)(artifactsAt) !== (0, import_node_path14.resolve)(startDir)) {
12134
12380
  M2.message(import_picocolors4.default.dim(`Using scan from: ${artifactsAt}`));
12135
12381
  } else {
12136
12382
  M2.message(import_picocolors4.default.dim(`Project folder: ${cwd}`));
@@ -12197,29 +12443,29 @@ async function runInteractiveSession(startDir = process.cwd()) {
12197
12443
  }
12198
12444
 
12199
12445
  // src/commands/condense.ts
12200
- var import_promises9 = require("node:fs/promises");
12201
- var import_node_path13 = require("node:path");
12446
+ var import_promises11 = require("node:fs/promises");
12447
+ var import_node_path15 = require("node:path");
12202
12448
  async function runCondenseCommand(options) {
12203
12449
  const dir = getProjectArtifactsDir(options.cwd);
12204
- const artifact = JSON.parse(await (0, import_promises9.readFile)((0, import_node_path13.join)(dir, "last-scan.json"), "utf8"));
12205
- const contextMapPath = (0, import_node_path13.join)(dir, "context-map.json");
12206
- await (0, import_promises9.writeFile)(contextMapPath, `${JSON.stringify(generateContextMap(artifact), null, 2)}
12450
+ const artifact = JSON.parse(await (0, import_promises11.readFile)((0, import_node_path15.join)(dir, "last-scan.json"), "utf8"));
12451
+ const contextMapPath = (0, import_node_path15.join)(dir, "context-map.json");
12452
+ await (0, import_promises11.writeFile)(contextMapPath, `${JSON.stringify(generateContextMap(artifact), null, 2)}
12207
12453
  `, "utf8");
12208
12454
  return { contextMapPath };
12209
12455
  }
12210
12456
 
12211
12457
  // src/heal/apply.ts
12212
- var import_promises11 = require("node:fs/promises");
12213
- var import_node_fs8 = require("node:fs");
12214
- var import_node_path16 = require("node:path");
12458
+ var import_promises13 = require("node:fs/promises");
12459
+ var import_node_fs10 = require("node:fs");
12460
+ var import_node_path19 = require("node:path");
12215
12461
 
12216
12462
  // src/heal/pathSafety.ts
12217
- var import_node_path14 = require("node:path");
12463
+ var import_node_path16 = require("node:path");
12218
12464
  var BLOCKED_SEGMENTS = /* @__PURE__ */ new Set([".git", "node_modules", "dist", "build", ".next", ".viberaven"]);
12219
12465
  function assertSafeHealTarget(cwd, target) {
12220
- const root = (0, import_node_path14.resolve)(cwd);
12221
- const absolute = (0, import_node_path14.resolve)(root, target);
12222
- const rel = (0, import_node_path14.relative)(root, absolute);
12466
+ const root = (0, import_node_path16.resolve)(cwd);
12467
+ const absolute = (0, import_node_path16.resolve)(root, target);
12468
+ const rel = (0, import_node_path16.relative)(root, absolute);
12223
12469
  if (rel.startsWith("..") || rel === "" || /^[A-Za-z]:/.test(rel)) {
12224
12470
  throw new Error("Heal target must stay inside the workspace");
12225
12471
  }
@@ -12242,9 +12488,9 @@ function applyEmptyCatchRecipe(source) {
12242
12488
  }
12243
12489
 
12244
12490
  // src/heal/recipes/index.ts
12245
- var import_node_fs7 = require("node:fs");
12246
- var import_promises10 = require("node:fs/promises");
12247
- var import_node_path15 = require("node:path");
12491
+ var import_node_fs9 = require("node:fs");
12492
+ var import_promises12 = require("node:fs/promises");
12493
+ var import_node_path18 = require("node:path");
12248
12494
 
12249
12495
  // src/heal/recipes/envAuthSecret.ts
12250
12496
  function applyAuthSecretRecipe(source) {
@@ -12624,6 +12870,194 @@ function applyRateLimitRecipe(source, hasUpstash) {
12624
12870
  };
12625
12871
  }
12626
12872
 
12873
+ // src/heal/recipes/eslintRestrictedImports.ts
12874
+ var import_node_fs8 = require("node:fs");
12875
+ var import_node_path17 = require("node:path");
12876
+ var VIBERAVEN_ESLINT_MARKER = "VibeRaven heal: eslint_restricted_imports";
12877
+ var RESTRICTED_IMPORTS_MESSAGE = `Restricted import. Run ${PUBLIC_AGENT_MODE_COMMAND} before substituting packages.`;
12878
+ var RESTRICTED_PATHS = [
12879
+ {
12880
+ name: "@supabase/auth-helpers-nextjs",
12881
+ message: RESTRICTED_IMPORTS_MESSAGE
12882
+ },
12883
+ {
12884
+ name: "@supabase/auth-helpers-react",
12885
+ message: RESTRICTED_IMPORTS_MESSAGE
12886
+ }
12887
+ ];
12888
+ var RESTRICTED_PATTERNS = [
12889
+ {
12890
+ group: ["@supabase/auth-helpers-*"],
12891
+ message: RESTRICTED_IMPORTS_MESSAGE
12892
+ }
12893
+ ];
12894
+ var ESLINT_CONFIG_CANDIDATES = [
12895
+ "eslint.config.js",
12896
+ "eslint.config.mjs",
12897
+ "eslint.config.ts",
12898
+ "eslint.config.cjs",
12899
+ ".eslintrc.json",
12900
+ ".eslintrc.js",
12901
+ ".eslintrc.cjs"
12902
+ ];
12903
+ function detectEslintConfigFile(cwd) {
12904
+ for (const candidate of ESLINT_CONFIG_CANDIDATES) {
12905
+ if ((0, import_node_fs8.existsSync)((0, import_node_path17.join)(cwd, candidate))) {
12906
+ return candidate;
12907
+ }
12908
+ }
12909
+ return null;
12910
+ }
12911
+ function alreadyApplied(source) {
12912
+ return source.includes(VIBERAVEN_ESLINT_MARKER) || source.includes(RESTRICTED_IMPORTS_MESSAGE) || /no-restricted-imports[\s\S]*@supabase\/auth-helpers-nextjs/.test(source);
12913
+ }
12914
+ function buildFlatConfigBlock() {
12915
+ const pathsJson = JSON.stringify(RESTRICTED_PATHS, null, 2).replace(/\n/g, "\n ");
12916
+ const patternsJson = JSON.stringify(RESTRICTED_PATTERNS, null, 2).replace(/\n/g, "\n ");
12917
+ return `// ${VIBERAVEN_ESLINT_MARKER}
12918
+ {
12919
+ rules: {
12920
+ 'no-restricted-imports': ['error', {
12921
+ paths: ${pathsJson},
12922
+ patterns: ${patternsJson},
12923
+ }],
12924
+ },
12925
+ },`;
12926
+ }
12927
+ function applyFlatConfigRecipe(source) {
12928
+ if (alreadyApplied(source)) {
12929
+ return { changed: false, output: source, canAutoApply: true };
12930
+ }
12931
+ const arrayExportMatch = /export\s+default\s+(\[[\s\S]*\])\s*;?\s*$/.exec(source);
12932
+ if (arrayExportMatch) {
12933
+ const closingBracket = source.lastIndexOf("]");
12934
+ if (closingBracket === -1) {
12935
+ return {
12936
+ changed: false,
12937
+ output: source,
12938
+ canAutoApply: false,
12939
+ reason: "cannot-parse-flat-config-array"
12940
+ };
12941
+ }
12942
+ const output = `${source.slice(0, closingBracket)}
12943
+ ${buildFlatConfigBlock()}
12944
+ ${source.slice(closingBracket)}`;
12945
+ return { changed: true, output, canAutoApply: true };
12946
+ }
12947
+ const defineConfigMatch = /defineConfig\(\s*(\[[\s\S]*\])\s*\)\s*;?\s*$/.exec(source);
12948
+ if (defineConfigMatch) {
12949
+ const closingBracket = source.lastIndexOf("]");
12950
+ if (closingBracket === -1) {
12951
+ return {
12952
+ changed: false,
12953
+ output: source,
12954
+ canAutoApply: false,
12955
+ reason: "cannot-parse-define-config-array"
12956
+ };
12957
+ }
12958
+ const output = `${source.slice(0, closingBracket)}
12959
+ ${buildFlatConfigBlock()}
12960
+ ${source.slice(closingBracket)}`;
12961
+ return { changed: true, output, canAutoApply: true };
12962
+ }
12963
+ return {
12964
+ changed: false,
12965
+ output: source,
12966
+ canAutoApply: false,
12967
+ reason: "unsupported-flat-config-shape"
12968
+ };
12969
+ }
12970
+ function applyLegacyModuleRecipe(source) {
12971
+ if (alreadyApplied(source)) {
12972
+ return { changed: false, output: source, canAutoApply: true };
12973
+ }
12974
+ const ruleValue = JSON.stringify(
12975
+ [
12976
+ "error",
12977
+ {
12978
+ paths: RESTRICTED_PATHS,
12979
+ patterns: RESTRICTED_PATTERNS
12980
+ }
12981
+ ],
12982
+ null,
12983
+ 2
12984
+ ).replace(/\n/g, "\n ");
12985
+ if (/module\.exports\s*=\s*\{/.test(source)) {
12986
+ if (/\brules\s*:\s*\{/.test(source)) {
12987
+ const output2 = source.replace(
12988
+ /(\brules\s*:\s*\{)/,
12989
+ `$1
12990
+ // ${VIBERAVEN_ESLINT_MARKER}
12991
+ 'no-restricted-imports': ${ruleValue},`
12992
+ );
12993
+ return { changed: true, output: output2, canAutoApply: true };
12994
+ }
12995
+ const output = source.replace(
12996
+ /(module\.exports\s*=\s*\{)/,
12997
+ `$1
12998
+ // ${VIBERAVEN_ESLINT_MARKER}
12999
+ rules: {
13000
+ 'no-restricted-imports': ${ruleValue},
13001
+ },`
13002
+ );
13003
+ return { changed: true, output, canAutoApply: true };
13004
+ }
13005
+ return {
13006
+ changed: false,
13007
+ output: source,
13008
+ canAutoApply: false,
13009
+ reason: "unsupported-legacy-eslint-module"
13010
+ };
13011
+ }
13012
+ function applyJsonRecipe(source) {
13013
+ let config;
13014
+ try {
13015
+ config = JSON.parse(source);
13016
+ } catch {
13017
+ return {
13018
+ changed: false,
13019
+ output: source,
13020
+ canAutoApply: false,
13021
+ reason: "invalid-eslint-json"
13022
+ };
13023
+ }
13024
+ if (alreadyApplied(source)) {
13025
+ return { changed: false, output: source, canAutoApply: true };
13026
+ }
13027
+ const rules = typeof config.rules === "object" && config.rules !== null && !Array.isArray(config.rules) ? { ...config.rules } : {};
13028
+ if ("no-restricted-imports" in rules) {
13029
+ return {
13030
+ changed: false,
13031
+ output: source,
13032
+ canAutoApply: false,
13033
+ reason: "no-restricted-imports-already-defined"
13034
+ };
13035
+ }
13036
+ rules["no-restricted-imports"] = [
13037
+ "error",
13038
+ {
13039
+ paths: RESTRICTED_PATHS,
13040
+ patterns: RESTRICTED_PATTERNS
13041
+ }
13042
+ ];
13043
+ config.rules = rules;
13044
+ return {
13045
+ changed: true,
13046
+ output: `${JSON.stringify(config, null, 2)}
13047
+ `,
13048
+ canAutoApply: true
13049
+ };
13050
+ }
13051
+ function applyEslintRestrictedImportsRecipe(source, configFile) {
13052
+ if (configFile.endsWith(".json")) {
13053
+ return applyJsonRecipe(source);
13054
+ }
13055
+ if (configFile.startsWith("eslint.config.")) {
13056
+ return applyFlatConfigRecipe(source);
13057
+ }
13058
+ return applyLegacyModuleRecipe(source);
13059
+ }
13060
+
12627
13061
  // src/heal/recipes/index.ts
12628
13062
  function defaultTargetFile(gapId) {
12629
13063
  const map = {
@@ -12637,19 +13071,20 @@ function defaultTargetFile(gapId) {
12637
13071
  rls_disabled: "",
12638
13072
  // no file — provider-action
12639
13073
  missing_csp_header: "next.config.js",
12640
- missing_rate_limit: "middleware.ts"
13074
+ missing_rate_limit: "middleware.ts",
13075
+ eslint_restricted_imports: ""
12641
13076
  };
12642
13077
  return map[gapId];
12643
13078
  }
12644
13079
  async function readSourceOrEmpty(absolutePath) {
12645
- if (!(0, import_node_fs7.existsSync)(absolutePath)) return "";
12646
- return (0, import_promises10.readFile)(absolutePath, "utf8");
13080
+ if (!(0, import_node_fs9.existsSync)(absolutePath)) return "";
13081
+ return (0, import_promises12.readFile)(absolutePath, "utf8");
12647
13082
  }
12648
13083
  async function detectUpstash(cwd) {
12649
13084
  try {
12650
- const pkgPath = (0, import_node_path15.join)(cwd, "package.json");
12651
- if (!(0, import_node_fs7.existsSync)(pkgPath)) return false;
12652
- const raw = await (0, import_promises10.readFile)(pkgPath, "utf8");
13085
+ const pkgPath = (0, import_node_path18.join)(cwd, "package.json");
13086
+ if (!(0, import_node_fs9.existsSync)(pkgPath)) return false;
13087
+ const raw = await (0, import_promises12.readFile)(pkgPath, "utf8");
12653
13088
  const pkg = JSON.parse(raw);
12654
13089
  const deps = {
12655
13090
  ...pkg["dependencies"] ?? {},
@@ -12664,7 +13099,7 @@ async function dispatchRecipeByGapId(gapId, cwd, explicitTarget) {
12664
13099
  const targetFile = explicitTarget ?? defaultTargetFile(gapId);
12665
13100
  if (targetFile === void 0) return null;
12666
13101
  if (gapId === "auth_secret_missing" || gapId === "node_env_not_set" || gapId === "database_url_missing") {
12667
- const absolutePath = (0, import_node_path15.join)(cwd, targetFile);
13102
+ const absolutePath = (0, import_node_path18.join)(cwd, targetFile);
12668
13103
  const source = await readSourceOrEmpty(absolutePath);
12669
13104
  let result;
12670
13105
  if (gapId === "auth_secret_missing") result = applyAuthSecretRecipe(source);
@@ -12678,25 +13113,25 @@ async function dispatchRecipeByGapId(gapId, cwd, explicitTarget) {
12678
13113
  };
12679
13114
  }
12680
13115
  if (gapId === "missing_error_boundary") {
12681
- const absolutePath = (0, import_node_path15.join)(cwd, "app/error.tsx");
13116
+ const absolutePath = (0, import_node_path18.join)(cwd, "app/error.tsx");
12682
13117
  const source = await readSourceOrEmpty(absolutePath);
12683
13118
  const result = applyErrorBoundaryRecipe(source);
12684
13119
  return { ...result, canAutoApply: true, recipeName: gapId };
12685
13120
  }
12686
13121
  if (gapId === "missing_health_route") {
12687
- const absolutePath = (0, import_node_path15.join)(cwd, "app/api/health/route.ts");
13122
+ const absolutePath = (0, import_node_path18.join)(cwd, "app/api/health/route.ts");
12688
13123
  const source = await readSourceOrEmpty(absolutePath);
12689
13124
  const result = applyHealthRouteRecipe(source);
12690
13125
  return { ...result, canAutoApply: true, recipeName: gapId };
12691
13126
  }
12692
13127
  if (gapId === "missing_loading_state") {
12693
- const absolutePath = (0, import_node_path15.join)(cwd, "app/loading.tsx");
13128
+ const absolutePath = (0, import_node_path18.join)(cwd, "app/loading.tsx");
12694
13129
  const source = await readSourceOrEmpty(absolutePath);
12695
13130
  const result = applyLoadingStateRecipe(source);
12696
13131
  return { ...result, canAutoApply: true, recipeName: gapId };
12697
13132
  }
12698
13133
  if (gapId === "missing_404_page") {
12699
- const absolutePath = (0, import_node_path15.join)(cwd, "app/not-found.tsx");
13134
+ const absolutePath = (0, import_node_path18.join)(cwd, "app/not-found.tsx");
12700
13135
  const source = await readSourceOrEmpty(absolutePath);
12701
13136
  const result = applyNotFoundRecipe(source);
12702
13137
  return { ...result, canAutoApply: true, recipeName: gapId };
@@ -12714,10 +13149,10 @@ async function dispatchRecipeByGapId(gapId, cwd, explicitTarget) {
12714
13149
  }
12715
13150
  if (gapId === "missing_csp_header") {
12716
13151
  let configFile = "next.config.js";
12717
- let absolutePath = (0, import_node_path15.join)(cwd, configFile);
12718
- if (!(0, import_node_fs7.existsSync)(absolutePath)) {
13152
+ let absolutePath = (0, import_node_path18.join)(cwd, configFile);
13153
+ if (!(0, import_node_fs9.existsSync)(absolutePath)) {
12719
13154
  configFile = "next.config.mjs";
12720
- absolutePath = (0, import_node_path15.join)(cwd, configFile);
13155
+ absolutePath = (0, import_node_path18.join)(cwd, configFile);
12721
13156
  }
12722
13157
  const source = await readSourceOrEmpty(absolutePath);
12723
13158
  const result = applyCspHeaderRecipe(source);
@@ -12729,7 +13164,7 @@ async function dispatchRecipeByGapId(gapId, cwd, explicitTarget) {
12729
13164
  }
12730
13165
  if (gapId === "missing_rate_limit") {
12731
13166
  const hasUpstash = await detectUpstash(cwd);
12732
- const middlewarePath = (0, import_node_path15.join)(cwd, "middleware.ts");
13167
+ const middlewarePath = (0, import_node_path18.join)(cwd, "middleware.ts");
12733
13168
  const source = await readSourceOrEmpty(middlewarePath);
12734
13169
  const result = applyRateLimitRecipe(source, hasUpstash);
12735
13170
  return {
@@ -12739,6 +13174,30 @@ async function dispatchRecipeByGapId(gapId, cwd, explicitTarget) {
12739
13174
  recipeName: gapId
12740
13175
  };
12741
13176
  }
13177
+ if (gapId === "eslint_restricted_imports") {
13178
+ const configFile = detectEslintConfigFile(cwd);
13179
+ if (!configFile) {
13180
+ return {
13181
+ changed: false,
13182
+ output: "",
13183
+ targetFile: "",
13184
+ canAutoApply: false,
13185
+ reason: "no-eslint-config",
13186
+ recipeName: gapId
13187
+ };
13188
+ }
13189
+ const absolutePath = (0, import_node_path18.join)(cwd, configFile);
13190
+ const source = await readSourceOrEmpty(absolutePath);
13191
+ const result = applyEslintRestrictedImportsRecipe(source, configFile);
13192
+ return {
13193
+ changed: result.changed,
13194
+ output: result.output,
13195
+ targetFile: configFile,
13196
+ canAutoApply: result.canAutoApply,
13197
+ reason: result.reason,
13198
+ recipeName: gapId
13199
+ };
13200
+ }
12742
13201
  return null;
12743
13202
  }
12744
13203
 
@@ -12802,14 +13261,14 @@ async function applyHeal(options) {
12802
13261
  rollback: { available: false, instructions: "Recipe matched but no change was needed (already applied or file already exists)." }
12803
13262
  };
12804
13263
  }
12805
- const absoluteTarget = (0, import_node_path16.join)(options.cwd, dispatched.targetFile);
13264
+ const absoluteTarget = (0, import_node_path19.join)(options.cwd, dispatched.targetFile);
12806
13265
  assertSafeHealTarget(options.cwd, dispatched.targetFile);
12807
- await (0, import_promises11.mkdir)((0, import_node_path16.dirname)(absoluteTarget), { recursive: true });
12808
- const healDir2 = (0, import_node_path16.join)(options.cwd, ".viberaven", "heal", id);
12809
- await (0, import_promises11.mkdir)((0, import_node_path16.join)(healDir2, "before"), { recursive: true });
12810
- const beforeContent = (0, import_node_fs8.existsSync)(absoluteTarget) ? await (0, import_promises11.readFile)(absoluteTarget, "utf8") : "";
12811
- await (0, import_promises11.writeFile)((0, import_node_path16.join)(healDir2, "before", "target.txt"), beforeContent, "utf8");
12812
- await (0, import_promises11.writeFile)(absoluteTarget, dispatched.output, "utf8");
13266
+ await (0, import_promises13.mkdir)((0, import_node_path19.dirname)(absoluteTarget), { recursive: true });
13267
+ const healDir2 = (0, import_node_path19.join)(options.cwd, ".viberaven", "heal", id);
13268
+ await (0, import_promises13.mkdir)((0, import_node_path19.join)(healDir2, "before"), { recursive: true });
13269
+ const beforeContent = (0, import_node_fs10.existsSync)(absoluteTarget) ? await (0, import_promises13.readFile)(absoluteTarget, "utf8") : "";
13270
+ await (0, import_promises13.writeFile)((0, import_node_path19.join)(healDir2, "before", "target.txt"), beforeContent, "utf8");
13271
+ await (0, import_promises13.writeFile)(absoluteTarget, dispatched.output, "utf8");
12813
13272
  const patch2 = [
12814
13273
  `--- ${dispatched.targetFile}`,
12815
13274
  `+++ ${dispatched.targetFile}`,
@@ -12819,7 +13278,7 @@ async function applyHeal(options) {
12819
13278
  dispatched.output,
12820
13279
  ""
12821
13280
  ].join("\n");
12822
- await (0, import_promises11.writeFile)((0, import_node_path16.join)(healDir2, "patch.diff"), patch2, "utf8");
13281
+ await (0, import_promises13.writeFile)((0, import_node_path19.join)(healDir2, "patch.diff"), patch2, "utf8");
12823
13282
  const result2 = {
12824
13283
  $schema: "https://viberaven.dev/schemas/heal-result.schema.json",
12825
13284
  schemaVersion: "v1",
@@ -12830,7 +13289,7 @@ async function applyHeal(options) {
12830
13289
  gapId: options.gapId,
12831
13290
  recipe: dispatched.recipeName,
12832
13291
  target: dispatched.targetFile,
12833
- changedFiles: [(0, import_node_path16.relative)(options.cwd, absoluteTarget).replace(/\\/g, "/")],
13292
+ changedFiles: [(0, import_node_path19.relative)(options.cwd, absoluteTarget).replace(/\\/g, "/")],
12834
13293
  artifacts: {
12835
13294
  patch: `.viberaven/heal/${id}/patch.diff`,
12836
13295
  result: `.viberaven/heal/${id}/result.json`
@@ -12840,7 +13299,7 @@ async function applyHeal(options) {
12840
13299
  instructions: "Restore .viberaven/heal/<healId>/before/target.txt to the target file or apply the reverse patch."
12841
13300
  }
12842
13301
  };
12843
- await (0, import_promises11.writeFile)((0, import_node_path16.join)(healDir2, "result.json"), `${JSON.stringify(result2, null, 2)}
13302
+ await (0, import_promises13.writeFile)((0, import_node_path19.join)(healDir2, "result.json"), `${JSON.stringify(result2, null, 2)}
12844
13303
  `, "utf8");
12845
13304
  return result2;
12846
13305
  }
@@ -12860,7 +13319,7 @@ async function applyHeal(options) {
12860
13319
  };
12861
13320
  }
12862
13321
  const absolute = assertSafeHealTarget(options.cwd, options.target);
12863
- const before = await (0, import_promises11.readFile)(absolute, "utf8");
13322
+ const before = await (0, import_promises13.readFile)(absolute, "utf8");
12864
13323
  const recipe = applyEmptyCatchRecipe(before);
12865
13324
  if (!recipe.changed) {
12866
13325
  return {
@@ -12877,10 +13336,10 @@ async function applyHeal(options) {
12877
13336
  rollback: { available: false, instructions: "No supported heal recipe matched this file." }
12878
13337
  };
12879
13338
  }
12880
- const healDir = (0, import_node_path16.join)(options.cwd, ".viberaven", "heal", id);
12881
- await (0, import_promises11.mkdir)((0, import_node_path16.join)(healDir, "before"), { recursive: true });
12882
- await (0, import_promises11.writeFile)((0, import_node_path16.join)(healDir, "before", "target.txt"), before, "utf8");
12883
- await (0, import_promises11.writeFile)(absolute, recipe.output, "utf8");
13339
+ const healDir = (0, import_node_path19.join)(options.cwd, ".viberaven", "heal", id);
13340
+ await (0, import_promises13.mkdir)((0, import_node_path19.join)(healDir, "before"), { recursive: true });
13341
+ await (0, import_promises13.writeFile)((0, import_node_path19.join)(healDir, "before", "target.txt"), before, "utf8");
13342
+ await (0, import_promises13.writeFile)(absolute, recipe.output, "utf8");
12884
13343
  const patch = [
12885
13344
  `--- ${options.target}`,
12886
13345
  `+++ ${options.target}`,
@@ -12890,7 +13349,7 @@ async function applyHeal(options) {
12890
13349
  recipe.output,
12891
13350
  ""
12892
13351
  ].join("\n");
12893
- await (0, import_promises11.writeFile)((0, import_node_path16.join)(healDir, "patch.diff"), patch, "utf8");
13352
+ await (0, import_promises13.writeFile)((0, import_node_path19.join)(healDir, "patch.diff"), patch, "utf8");
12894
13353
  const result = {
12895
13354
  $schema: "https://viberaven.dev/schemas/heal-result.schema.json",
12896
13355
  schemaVersion: "v1",
@@ -12901,7 +13360,7 @@ async function applyHeal(options) {
12901
13360
  gapId: options.gapId,
12902
13361
  recipe: "empty-catch-safe-response",
12903
13362
  target: options.target,
12904
- changedFiles: [(0, import_node_path16.relative)(options.cwd, absolute).replace(/\\/g, "/")],
13363
+ changedFiles: [(0, import_node_path19.relative)(options.cwd, absolute).replace(/\\/g, "/")],
12905
13364
  artifacts: {
12906
13365
  patch: `.viberaven/heal/${id}/patch.diff`,
12907
13366
  result: `.viberaven/heal/${id}/result.json`
@@ -12911,20 +13370,20 @@ async function applyHeal(options) {
12911
13370
  instructions: "Restore .viberaven/heal/<healId>/before/target.txt to the target file or apply the reverse patch."
12912
13371
  }
12913
13372
  };
12914
- await (0, import_promises11.writeFile)((0, import_node_path16.join)(healDir, "result.json"), `${JSON.stringify(result, null, 2)}
13373
+ await (0, import_promises13.writeFile)((0, import_node_path19.join)(healDir, "result.json"), `${JSON.stringify(result, null, 2)}
12915
13374
  `, "utf8");
12916
13375
  return result;
12917
13376
  }
12918
13377
 
12919
13378
  // src/heal/plan.ts
12920
- var import_promises12 = require("node:fs/promises");
12921
- var import_node_path17 = require("node:path");
13379
+ var import_promises14 = require("node:fs/promises");
13380
+ var import_node_path20 = require("node:path");
12922
13381
  function healId2() {
12923
13382
  return `heal_${(/* @__PURE__ */ new Date()).toISOString().replace(/\D/g, "").slice(0, 14)}`;
12924
13383
  }
12925
13384
  async function writeHealPlan(options) {
12926
- const dir = (0, import_node_path17.join)(options.cwd, ".viberaven");
12927
- await (0, import_promises12.mkdir)(dir, { recursive: true });
13385
+ const dir = (0, import_node_path20.join)(options.cwd, ".viberaven");
13386
+ await (0, import_promises14.mkdir)(dir, { recursive: true });
12928
13387
  const id = healId2();
12929
13388
  const target = options.target ?? `gap:${options.gapId}`;
12930
13389
  const markdown = [
@@ -12951,21 +13410,21 @@ async function writeHealPlan(options) {
12951
13410
  artifacts: { plan: ".viberaven/heal-plan.md" },
12952
13411
  rollback: { available: false, instructions: "No source files were changed." }
12953
13412
  };
12954
- await (0, import_promises12.writeFile)((0, import_node_path17.join)(dir, "heal-plan.md"), markdown, "utf8");
12955
- await (0, import_promises12.writeFile)((0, import_node_path17.join)(dir, "heal-plan.json"), `${JSON.stringify(result, null, 2)}
13413
+ await (0, import_promises14.writeFile)((0, import_node_path20.join)(dir, "heal-plan.md"), markdown, "utf8");
13414
+ await (0, import_promises14.writeFile)((0, import_node_path20.join)(dir, "heal-plan.json"), `${JSON.stringify(result, null, 2)}
12956
13415
  `, "utf8");
12957
13416
  return result;
12958
13417
  }
12959
13418
 
12960
13419
  // src/heal/prompt.ts
12961
- var import_promises13 = require("node:fs/promises");
12962
- var import_node_path18 = require("node:path");
13420
+ var import_promises15 = require("node:fs/promises");
13421
+ var import_node_path21 = require("node:path");
12963
13422
  function healId3() {
12964
13423
  return `heal_${(/* @__PURE__ */ new Date()).toISOString().replace(/\D/g, "").slice(0, 14)}`;
12965
13424
  }
12966
13425
  async function writeHealPrompt(options) {
12967
- const dir = (0, import_node_path18.join)(options.cwd, ".viberaven");
12968
- await (0, import_promises13.mkdir)(dir, { recursive: true });
13426
+ const dir = (0, import_node_path21.join)(options.cwd, ".viberaven");
13427
+ await (0, import_promises15.mkdir)(dir, { recursive: true });
12969
13428
  const id = healId3();
12970
13429
  const target = options.target ?? `gap:${options.gapId}`;
12971
13430
  const prompt = [
@@ -12992,12 +13451,12 @@ async function writeHealPrompt(options) {
12992
13451
  artifacts: { prompt: ".viberaven/heal-prompt.md" },
12993
13452
  rollback: { available: false, instructions: "No source files were changed." }
12994
13453
  };
12995
- await (0, import_promises13.writeFile)((0, import_node_path18.join)(dir, "heal-prompt.md"), prompt, "utf8");
13454
+ await (0, import_promises15.writeFile)((0, import_node_path21.join)(dir, "heal-prompt.md"), prompt, "utf8");
12996
13455
  return result;
12997
13456
  }
12998
13457
 
12999
13458
  // src/loopState.ts
13000
- var import_promises14 = require("fs/promises");
13459
+ var import_promises16 = require("fs/promises");
13001
13460
  var import_path = require("path");
13002
13461
  var DEFAULT_LOOP_STATE = {
13003
13462
  batchApplied: 0,
@@ -13010,7 +13469,7 @@ function loopStatePath(workspaceRoot) {
13010
13469
  }
13011
13470
  async function loadLoopState(workspaceRoot) {
13012
13471
  try {
13013
- const raw = await (0, import_promises14.readFile)(loopStatePath(workspaceRoot), "utf8");
13472
+ const raw = await (0, import_promises16.readFile)(loopStatePath(workspaceRoot), "utf8");
13014
13473
  const parsed = JSON.parse(raw);
13015
13474
  if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed) && typeof parsed.batchApplied === "number" && typeof parsed.lastGapCount === "number" && typeof parsed.stalledScans === "number") {
13016
13475
  const p2 = parsed;
@@ -13030,8 +13489,8 @@ async function loadLoopState(workspaceRoot) {
13030
13489
  async function saveLoopState(workspaceRoot, state) {
13031
13490
  try {
13032
13491
  const dir = (0, import_path.join)(workspaceRoot, ".viberaven");
13033
- await (0, import_promises14.mkdir)(dir, { recursive: true });
13034
- await (0, import_promises14.writeFile)(loopStatePath(workspaceRoot), JSON.stringify(state, null, 2) + "\n", "utf8");
13492
+ await (0, import_promises16.mkdir)(dir, { recursive: true });
13493
+ await (0, import_promises16.writeFile)(loopStatePath(workspaceRoot), JSON.stringify(state, null, 2) + "\n", "utf8");
13035
13494
  } catch (err) {
13036
13495
  console.warn("[VibeRaven] Could not save loop-state.json:", err instanceof Error ? err.message : String(err));
13037
13496
  }
@@ -13077,9 +13536,9 @@ async function runHealCommand(options) {
13077
13536
  }
13078
13537
 
13079
13538
  // src/stackRecommend.ts
13080
- var import_promises15 = require("node:fs/promises");
13081
- var import_node_fs9 = require("node:fs");
13082
- var import_node_path19 = require("node:path");
13539
+ var import_promises17 = require("node:fs/promises");
13540
+ var import_node_fs11 = require("node:fs");
13541
+ var import_node_path22 = require("node:path");
13083
13542
  var DEFAULT_STACK = {
13084
13543
  frontend: "react",
13085
13544
  ui: "tailwind + shadcn/ui",
@@ -13090,11 +13549,11 @@ var DEFAULT_STACK = {
13090
13549
  reason: "Agent-default stack for lowest launch friction when repo signals are ambiguous"
13091
13550
  };
13092
13551
  async function recommendStack(cwd = process.cwd()) {
13093
- const pkgPath = (0, import_node_path19.join)(cwd, "package.json");
13094
- if (!(0, import_node_fs9.existsSync)(pkgPath)) {
13552
+ const pkgPath = (0, import_node_path22.join)(cwd, "package.json");
13553
+ if (!(0, import_node_fs11.existsSync)(pkgPath)) {
13095
13554
  return DEFAULT_STACK;
13096
13555
  }
13097
- const pkg = JSON.parse(await (0, import_promises15.readFile)(pkgPath, "utf-8"));
13556
+ const pkg = JSON.parse(await (0, import_promises17.readFile)(pkgPath, "utf-8"));
13098
13557
  const deps = { ...pkg.dependencies, ...pkg.devDependencies };
13099
13558
  const names = Object.keys(deps).join(" ").toLowerCase();
13100
13559
  const rec = {
@@ -13136,24 +13595,29 @@ async function runInitCommand(options) {
13136
13595
  console.log(renderAgentRulesDryRun(targets));
13137
13596
  return 0;
13138
13597
  }
13139
- const results = await initAgentRules({
13598
+ const output = await initAgentRules({
13140
13599
  cwd: options.cwd,
13141
13600
  targets,
13142
13601
  dryRun: false
13143
13602
  });
13144
- console.log(formatAgentRulesInitSummary(results));
13603
+ console.log(formatAgentRulesInitSummary(output));
13145
13604
  return 0;
13146
13605
  }
13147
13606
 
13148
13607
  // src/commands/doctorAgents.ts
13149
- var import_promises16 = require("node:fs/promises");
13150
- var import_node_path20 = require("node:path");
13608
+ var import_promises18 = require("node:fs/promises");
13609
+ var import_node_path23 = require("node:path");
13151
13610
  var REQUIRED_EXISTENCE_CHECKS = [
13152
13611
  { id: "agents-md", file: "AGENTS.md" },
13153
13612
  { id: "claude-md", file: "CLAUDE.md" },
13154
- { id: "cursor-rule", file: ".cursor/rules/viberaven.mdc" },
13613
+ { id: "cursor-core-rule", file: ".cursor/rules/viberaven-core.mdc" },
13155
13614
  { id: "copilot-instructions", file: ".github/copilot-instructions.md" }
13156
13615
  ];
13616
+ var OPTIONAL_CURSOR_PACK_CHECKS = [
13617
+ { id: "cursor-supabase-rule", file: ".cursor/rules/viberaven-supabase-rls.mdc" },
13618
+ { id: "cursor-deploy-rule", file: ".cursor/rules/viberaven-deploy.mdc" },
13619
+ { id: "cursor-payments-rule", file: ".cursor/rules/viberaven-payments.mdc" }
13620
+ ];
13157
13621
  var STALE_PATTERNS = [
13158
13622
  { id: "stale-beta-scan", pattern: "@beta scan" },
13159
13623
  { id: "stale-viberaven-station", pattern: "viberaven-station" },
@@ -13162,17 +13626,37 @@ var STALE_PATTERNS = [
13162
13626
  async function checkAgentInjection(cwd) {
13163
13627
  const checks = [];
13164
13628
  for (const item3 of REQUIRED_EXISTENCE_CHECKS) {
13165
- const exists = await fileExists((0, import_node_path20.join)(cwd, item3.file));
13629
+ const exists = await fileExists2((0, import_node_path23.join)(cwd, item3.file));
13166
13630
  checks.push({
13167
13631
  id: item3.id,
13168
13632
  status: exists ? "pass" : "fail",
13169
13633
  message: exists ? `${item3.file} exists` : `Missing ${item3.file}`
13170
13634
  });
13171
13635
  }
13636
+ for (const item3 of OPTIONAL_CURSOR_PACK_CHECKS) {
13637
+ const exists = await fileExists2((0, import_node_path23.join)(cwd, item3.file));
13638
+ checks.push({
13639
+ id: item3.id,
13640
+ status: exists ? "pass" : "fail",
13641
+ message: exists ? `${item3.file} exists` : `Missing ${item3.file} \u2014 run npx -y viberaven init --agents all`
13642
+ });
13643
+ }
13644
+ const legacyCursorPath = (0, import_node_path23.join)(cwd, ".cursor/rules/viberaven.mdc");
13645
+ if (await fileExists2(legacyCursorPath)) {
13646
+ const legacyContent = await (0, import_promises18.readFile)(legacyCursorPath, "utf-8");
13647
+ const hasCoreSplit = await fileExists2((0, import_node_path23.join)(cwd, ".cursor/rules/viberaven-core.mdc"));
13648
+ if (!hasCoreSplit || legacyContent.includes("alwaysApply: true")) {
13649
+ checks.push({
13650
+ id: "cursor-legacy-mdc",
13651
+ status: "fail",
13652
+ message: "Legacy .cursor/rules/viberaven.mdc detected \u2014 run npx -y viberaven init --agents all to migrate to the split Cursor pack"
13653
+ });
13654
+ }
13655
+ }
13172
13656
  for (const target of CORE_AGENT_INJECTION_TARGETS) {
13173
- const file = AGENT_RULE_TARGETS[target].file;
13174
- const path = (0, import_node_path20.join)(cwd, file);
13175
- const exists = await fileExists(path);
13657
+ const file = target === "cursor" ? ".cursor/rules/viberaven-core.mdc" : AGENT_RULE_TARGETS[target].file;
13658
+ const path = (0, import_node_path23.join)(cwd, file);
13659
+ const exists = await fileExists2(path);
13176
13660
  if (!exists) {
13177
13661
  checks.push({
13178
13662
  id: `canonical-${target}`,
@@ -13181,7 +13665,7 @@ async function checkAgentInjection(cwd) {
13181
13665
  });
13182
13666
  continue;
13183
13667
  }
13184
- const content = await (0, import_promises16.readFile)(path, "utf-8");
13668
+ const content = await (0, import_promises18.readFile)(path, "utf-8");
13185
13669
  const hasCommand = content.includes(PUBLIC_AGENT_MODE_COMMAND);
13186
13670
  checks.push({
13187
13671
  id: `canonical-${target}`,
@@ -13213,9 +13697,9 @@ function formatDoctorAgentsReport(report) {
13213
13697
  lines.push(report.ok ? "All agent injection checks passed." : "Agent injection checks failed.");
13214
13698
  return lines.join("\n");
13215
13699
  }
13216
- async function fileExists(path) {
13700
+ async function fileExists2(path) {
13217
13701
  try {
13218
- await (0, import_promises16.access)(path);
13702
+ await (0, import_promises18.access)(path);
13219
13703
  return true;
13220
13704
  } catch {
13221
13705
  return false;
@@ -13229,6 +13713,191 @@ async function runDoctorAgentsCommand(options) {
13229
13713
  return report.ok ? 0 : 1;
13230
13714
  }
13231
13715
 
13716
+ // src/npm/popularPackages.ts
13717
+ var POPULAR_NPM_PACKAGES = [
13718
+ "next",
13719
+ "react",
13720
+ "react-dom",
13721
+ "vue",
13722
+ "@vue/cli",
13723
+ "express",
13724
+ "lodash",
13725
+ "axios",
13726
+ "stripe",
13727
+ "@stripe/stripe-js",
13728
+ "supabase",
13729
+ "@supabase/supabase-js",
13730
+ "@prisma/client",
13731
+ "prisma",
13732
+ "typescript",
13733
+ "eslint",
13734
+ "prettier",
13735
+ "tailwindcss",
13736
+ "vite",
13737
+ "zod",
13738
+ "dotenv",
13739
+ "jsonwebtoken",
13740
+ "bcrypt",
13741
+ "mongoose",
13742
+ "pg",
13743
+ "redis",
13744
+ "ioredis",
13745
+ "winston",
13746
+ "@sentry/node",
13747
+ "@sentry/nextjs",
13748
+ "firebase",
13749
+ "@clerk/nextjs",
13750
+ "openai",
13751
+ "@anthropic-ai/sdk",
13752
+ "vercel",
13753
+ "@vercel/analytics"
13754
+ ];
13755
+
13756
+ // src/npm/validateNpmPackage.ts
13757
+ var REGISTRY_BASE = "https://registry.npmjs.org";
13758
+ var NEW_PACKAGE_DAYS = 14;
13759
+ var TYPOSQUAT_MAX_DISTANCE = 2;
13760
+ var LOW_PUBLISH_COUNT = 3;
13761
+ function levenshteinDistance(a, b3) {
13762
+ if (a === b3) return 0;
13763
+ if (a.length === 0) return b3.length;
13764
+ if (b3.length === 0) return a.length;
13765
+ const matrix = Array.from(
13766
+ { length: a.length + 1 },
13767
+ () => Array.from({ length: b3.length + 1 }, () => 0)
13768
+ );
13769
+ for (let i = 0; i <= a.length; i += 1) matrix[i][0] = i;
13770
+ for (let j2 = 0; j2 <= b3.length; j2 += 1) matrix[0][j2] = j2;
13771
+ for (let i = 1; i <= a.length; i += 1) {
13772
+ for (let j2 = 1; j2 <= b3.length; j2 += 1) {
13773
+ const cost = a[i - 1] === b3[j2 - 1] ? 0 : 1;
13774
+ matrix[i][j2] = Math.min(
13775
+ matrix[i - 1][j2] + 1,
13776
+ matrix[i][j2 - 1] + 1,
13777
+ matrix[i - 1][j2 - 1] + cost
13778
+ );
13779
+ }
13780
+ }
13781
+ return matrix[a.length][b3.length];
13782
+ }
13783
+ function daysSince(isoDate, now = Date.now()) {
13784
+ const created = Date.parse(isoDate);
13785
+ if (!Number.isFinite(created)) return Number.POSITIVE_INFINITY;
13786
+ return (now - created) / (1e3 * 60 * 60 * 24);
13787
+ }
13788
+ function closestPopularPackage(name) {
13789
+ let best = null;
13790
+ for (const popular of POPULAR_NPM_PACKAGES) {
13791
+ const distance = levenshteinDistance(name, popular);
13792
+ if (distance <= TYPOSQUAT_MAX_DISTANCE && (!best || distance < best.distance)) {
13793
+ best = { pkg: popular, distance };
13794
+ }
13795
+ }
13796
+ return best;
13797
+ }
13798
+ function buildBase(name) {
13799
+ const normalized = name.trim().toLowerCase();
13800
+ return {
13801
+ name: normalized,
13802
+ registryUrl: `${REGISTRY_BASE}/${encodeURIComponent(normalized)}`,
13803
+ followUpCommand: PUBLIC_AGENT_MODE_COMMAND
13804
+ };
13805
+ }
13806
+ async function validateNpmPackage(name, options) {
13807
+ const fetchFn = options?.fetch ?? fetch;
13808
+ const now = options?.now ?? Date.now();
13809
+ const base = buildBase(name);
13810
+ if (!base.name) {
13811
+ return {
13812
+ ...base,
13813
+ verdict: "not_found",
13814
+ reasons: ["Package name is empty."]
13815
+ };
13816
+ }
13817
+ let response;
13818
+ try {
13819
+ response = await fetchFn(base.registryUrl);
13820
+ } catch {
13821
+ return {
13822
+ ...base,
13823
+ verdict: "not_found",
13824
+ reasons: ["Could not reach the public npm registry."]
13825
+ };
13826
+ }
13827
+ if (response.status === 404) {
13828
+ return {
13829
+ ...base,
13830
+ verdict: "not_found",
13831
+ reasons: [`Package "${base.name}" was not found on the public npm registry.`]
13832
+ };
13833
+ }
13834
+ if (!response.ok) {
13835
+ return {
13836
+ ...base,
13837
+ verdict: "suspicious",
13838
+ reasons: [`Unexpected npm registry response: HTTP ${response.status}.`]
13839
+ };
13840
+ }
13841
+ const data = await response.json();
13842
+ const reasons = [];
13843
+ let suspicious = false;
13844
+ const created = data.time?.created;
13845
+ if (created) {
13846
+ const ageDays = daysSince(created, now);
13847
+ const closest = closestPopularPackage(base.name);
13848
+ if (ageDays < NEW_PACKAGE_DAYS && closest && closest.pkg !== base.name) {
13849
+ suspicious = true;
13850
+ reasons.push(
13851
+ `Package was published ${Math.max(0, Math.floor(ageDays))} day(s) ago and its name is within ${TYPOSQUAT_MAX_DISTANCE} character edits of popular package "${closest.pkg}".`
13852
+ );
13853
+ }
13854
+ }
13855
+ const description = (data.description ?? "").trim();
13856
+ const maintainerCount = Array.isArray(data.maintainers) ? data.maintainers.length : 0;
13857
+ const publishCount = data.versions ? Object.keys(data.versions).length : 0;
13858
+ if (!description && maintainerCount === 0 && publishCount <= LOW_PUBLISH_COUNT) {
13859
+ suspicious = true;
13860
+ reasons.push(
13861
+ "Package has an empty description, no listed maintainers, and very few published versions."
13862
+ );
13863
+ }
13864
+ if (suspicious) {
13865
+ return { ...base, verdict: "suspicious", reasons };
13866
+ }
13867
+ return {
13868
+ ...base,
13869
+ verdict: "ok",
13870
+ reasons: ["Package exists on the public npm registry with no v1 suspicious signals."]
13871
+ };
13872
+ }
13873
+ async function validateNpmPackages(names, options) {
13874
+ return Promise.all(names.map((name) => validateNpmPackage(name, options)));
13875
+ }
13876
+
13877
+ // src/commands/runValidateNpmPackage.ts
13878
+ async function runValidateNpmPackageCommand(options) {
13879
+ const names = options.names.map((name) => name.trim()).filter(Boolean);
13880
+ if (names.length === 0) {
13881
+ console.error("Usage: viberaven validate-npm-package [--json] <package> [package...]");
13882
+ return 1;
13883
+ }
13884
+ const results = names.length === 1 ? [await validateNpmPackage(names[0])] : await validateNpmPackages(names);
13885
+ const payload = names.length === 1 ? results[0] : { results };
13886
+ if (options.json) {
13887
+ console.log(JSON.stringify(payload, null, 2));
13888
+ } else {
13889
+ for (const result of results) {
13890
+ console.log(`${result.name}: ${result.verdict}`);
13891
+ for (const reason of result.reasons) {
13892
+ console.log(` - ${reason}`);
13893
+ }
13894
+ console.log(` followUp: ${result.followUpCommand}`);
13895
+ }
13896
+ }
13897
+ const hasBlocking = results.some((result) => result.verdict !== "ok");
13898
+ return hasBlocking ? 2 : 0;
13899
+ }
13900
+
13232
13901
  // src/output/nextActionBlock.ts
13233
13902
  function buildNextActionBlock(tasks, loopState, plan) {
13234
13903
  const batchSize = plan === "pro" ? 10 : 3;
@@ -13348,9 +14017,9 @@ function printNextActionBlock(block) {
13348
14017
  }
13349
14018
 
13350
14019
  // src/providerMcpBridge.ts
13351
- var import_node_fs10 = require("node:fs");
14020
+ var import_node_fs12 = require("node:fs");
13352
14021
  var import_node_os2 = require("node:os");
13353
- var import_node_path21 = require("node:path");
14022
+ var import_node_path24 = require("node:path");
13354
14023
  var UPGRADE_URL4 = "https://viberaven.dev/pricing";
13355
14024
  var FALLBACK_COMMAND = "npx -y viberaven audit --vercel-supabase --json";
13356
14025
  var SUPPORTED_PROVIDERS = /* @__PURE__ */ new Set(["supabase", "vercel"]);
@@ -13358,9 +14027,9 @@ var configPathsOverride;
13358
14027
  function defaultMcpConfigPaths() {
13359
14028
  const home = (0, import_node_os2.homedir)();
13360
14029
  return [
13361
- (0, import_node_path21.join)(home, ".config", "claude", "claude_desktop_config.json"),
13362
- (0, import_node_path21.join)(home, ".cursor", "mcp.json"),
13363
- (0, import_node_path21.join)(home, ".gemini", "antigravity", "mcp_config.json")
14030
+ (0, import_node_path24.join)(home, ".config", "claude", "claude_desktop_config.json"),
14031
+ (0, import_node_path24.join)(home, ".cursor", "mcp.json"),
14032
+ (0, import_node_path24.join)(home, ".gemini", "antigravity", "mcp_config.json")
13364
14033
  ];
13365
14034
  }
13366
14035
  function resolveConfigPaths() {
@@ -13389,11 +14058,11 @@ function findServerEntry(servers, provider2) {
13389
14058
  function findProviderMcpConfig(provider2, configPaths) {
13390
14059
  const paths = configPaths ?? resolveConfigPaths();
13391
14060
  for (const path of paths) {
13392
- if (!(0, import_node_fs10.existsSync)(path)) {
14061
+ if (!(0, import_node_fs12.existsSync)(path)) {
13393
14062
  continue;
13394
14063
  }
13395
14064
  try {
13396
- const raw = JSON.parse((0, import_node_fs10.readFileSync)(path, "utf8"));
14065
+ const raw = JSON.parse((0, import_node_fs12.readFileSync)(path, "utf8"));
13397
14066
  const servers = parseMcpServers(raw);
13398
14067
  if (!servers) {
13399
14068
  continue;
@@ -13595,6 +14264,7 @@ function isBooleanFlag(command, key) {
13595
14264
  if (key === "open" && (command === "" || command === "scan" || command === "report")) return true;
13596
14265
  if (key === "verify" && command === "") return true;
13597
14266
  if (key === "vercel-supabase" && command === "audit") return true;
14267
+ if (key === "json" && command === "validate-npm-package") return true;
13598
14268
  if (key === "dry-run" && command === "init") return true;
13599
14269
  if (key === "agents" && command === "doctor") return true;
13600
14270
  return false;
@@ -13613,7 +14283,7 @@ async function guardEarlyVerifyScan(input) {
13613
14283
  if (!verifyLike) {
13614
14284
  return void 0;
13615
14285
  }
13616
- const workspacePath = input.positional[0] ? (0, import_node_path22.join)(process.cwd(), input.positional[0]) : await resolveWorkspaceRoot(process.cwd());
14286
+ const workspacePath = input.positional[0] ? (0, import_node_path25.join)(process.cwd(), input.positional[0]) : await resolveWorkspaceRoot(process.cwd());
13617
14287
  const loopState = await loadLoopState(workspacePath);
13618
14288
  if (loopState.batchApplied <= 0) {
13619
14289
  return void 0;
@@ -13689,7 +14359,7 @@ async function cmdStatus(flags, positional) {
13689
14359
  console.log("Not signed in. Run: viberaven login");
13690
14360
  return 1;
13691
14361
  }
13692
- const startDir = positional[0] ? (0, import_node_path22.join)(process.cwd(), positional[0]) : process.cwd();
14362
+ const startDir = positional[0] ? (0, import_node_path25.join)(process.cwd(), positional[0]) : process.cwd();
13693
14363
  let artifact;
13694
14364
  try {
13695
14365
  artifact = await loadLastArtifact(startDir);
@@ -13843,7 +14513,7 @@ async function cmdWatch(flags) {
13843
14513
  }
13844
14514
  }
13845
14515
  async function runScanCommand(flags, positional, options) {
13846
- const workspacePath = positional[0] ? (0, import_node_path22.join)(process.cwd(), positional[0]) : await resolveWorkspaceRoot(process.cwd());
14516
+ const workspacePath = positional[0] ? (0, import_node_path25.join)(process.cwd(), positional[0]) : await resolveWorkspaceRoot(process.cwd());
13847
14517
  const apiBaseUrl = resolveApiBaseUrl(typeof flags["api-url"] === "string" ? flags["api-url"] : void 0);
13848
14518
  let accessToken;
13849
14519
  try {
@@ -13921,7 +14591,7 @@ async function runScanCommand(flags, positional, options) {
13921
14591
  return { exitCode: 0, artifacts: paths };
13922
14592
  }
13923
14593
  async function cmdReport(flags, positional) {
13924
- const startDir = positional[0] ? (0, import_node_path22.join)(process.cwd(), positional[0]) : process.cwd();
14594
+ const startDir = positional[0] ? (0, import_node_path25.join)(process.cwd(), positional[0]) : process.cwd();
13925
14595
  try {
13926
14596
  const paths = await refreshReportFromDisk(startDir);
13927
14597
  console.log(`Report refreshed: ${paths.reportPath}`);
@@ -13943,7 +14613,7 @@ async function cmdReport(flags, positional) {
13943
14613
  }
13944
14614
  }
13945
14615
  async function cmdPrompt(flags, positional) {
13946
- const startDir = positional[0] ? (0, import_node_path22.join)(process.cwd(), positional[0]) : process.cwd();
14616
+ const startDir = positional[0] ? (0, import_node_path25.join)(process.cwd(), positional[0]) : process.cwd();
13947
14617
  let artifact;
13948
14618
  try {
13949
14619
  artifact = await loadLastArtifact(startDir);
@@ -14041,7 +14711,7 @@ async function main() {
14041
14711
  const wantsJsonl = hasFlag(flags, "jsonl");
14042
14712
  const wantsStrict = hasFlag(flags, "strict");
14043
14713
  if (flags.condense) {
14044
- const cwd = positional[0] ? (0, import_node_path22.join)(process.cwd(), positional[0]) : process.cwd();
14714
+ const cwd = positional[0] ? (0, import_node_path25.join)(process.cwd(), positional[0]) : process.cwd();
14045
14715
  const result = await runCondenseCommand({ cwd });
14046
14716
  console.log(`VibeRaven context map refreshed: ${result.contextMapPath}`);
14047
14717
  return 0;
@@ -14070,7 +14740,7 @@ async function main() {
14070
14740
  console.error("VibeRaven could not produce machine output because scan artifacts were not written.");
14071
14741
  return 3;
14072
14742
  }
14073
- const gateResult = scanResult.artifacts && (wantsJson || wantsJsonl || wantsStrict) ? JSON.parse(await (0, import_promises17.readFile)(scanResult.artifacts.gateResultPath, "utf8")) : void 0;
14743
+ const gateResult = scanResult.artifacts && (wantsJson || wantsJsonl || wantsStrict) ? JSON.parse(await (0, import_promises19.readFile)(scanResult.artifacts.gateResultPath, "utf8")) : void 0;
14074
14744
  const strictExitCode = wantsStrict && gateResult ? exitCodeForStrictGate(gateResult, { failOnWarnings: flags.strict === "warning" }) : scanResult.exitCode;
14075
14745
  if (wantsJson && gateResult) {
14076
14746
  process.stdout.write(renderGateResultJson(gateResult));
@@ -14105,7 +14775,7 @@ async function main() {
14105
14775
  case "next":
14106
14776
  return runNextCommand({
14107
14777
  json: Boolean(flags.json),
14108
- cwd: positional[0] ? (0, import_node_path22.join)(process.cwd(), positional[0]) : process.cwd()
14778
+ cwd: positional[0] ? (0, import_node_path25.join)(process.cwd(), positional[0]) : process.cwd()
14109
14779
  });
14110
14780
  case "guide": {
14111
14781
  const provider2 = positional[0];
@@ -14143,7 +14813,7 @@ async function main() {
14143
14813
  case "provider-verify":
14144
14814
  return cmdProviderVerify(flags, positional);
14145
14815
  case "init": {
14146
- const cwd = positional[0] ? (0, import_node_path22.join)(process.cwd(), positional[0]) : process.cwd();
14816
+ const cwd = positional[0] ? (0, import_node_path25.join)(process.cwd(), positional[0]) : process.cwd();
14147
14817
  const agents = typeof flags.agents === "string" ? flags.agents : void 0;
14148
14818
  return runInitCommand({
14149
14819
  cwd,
@@ -14157,7 +14827,12 @@ async function main() {
14157
14827
  return 1;
14158
14828
  }
14159
14829
  return runDoctorAgentsCommand({
14160
- cwd: positional[0] ? (0, import_node_path22.join)(process.cwd(), positional[0]) : process.cwd()
14830
+ cwd: positional[0] ? (0, import_node_path25.join)(process.cwd(), positional[0]) : process.cwd()
14831
+ });
14832
+ case "validate-npm-package":
14833
+ return runValidateNpmPackageCommand({
14834
+ names: positional,
14835
+ json: Boolean(flags.json)
14161
14836
  });
14162
14837
  default:
14163
14838
  console.error(`Unknown command: ${command}`);