@viberaven/cli 1.0.3 → 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");
@@ -8806,7 +8806,8 @@ var KNOWN_RECIPE_GAP_IDS = /* @__PURE__ */ new Set([
8806
8806
  "missing_404_page",
8807
8807
  // W3 file-patch recipes
8808
8808
  "missing_csp_header",
8809
- "missing_rate_limit"
8809
+ "missing_rate_limit",
8810
+ "eslint_restricted_imports"
8810
8811
  // NOTE: 'rls_disabled' is intentionally NOT here — it is provider-action only,
8811
8812
  // canAutoApply=false, and should be classified as 'provider-action' by buildTaskList.
8812
8813
  ]);
@@ -10641,7 +10642,7 @@ function isRecord6(value) {
10641
10642
  }
10642
10643
 
10643
10644
  // src/tui/runInteractive.ts
10644
- var import_node_path12 = require("node:path");
10645
+ var import_node_path14 = require("node:path");
10645
10646
 
10646
10647
  // ../../node_modules/@clack/core/dist/index.mjs
10647
10648
  var import_sisteransi = __toESM(require_src(), 1);
@@ -11491,8 +11492,8 @@ function renderVercelSupabaseAudit(result) {
11491
11492
  }
11492
11493
 
11493
11494
  // src/commands/initRules.ts
11494
- var import_promises8 = require("node:fs/promises");
11495
- var import_node_path11 = require("node:path");
11495
+ var import_promises10 = require("node:fs/promises");
11496
+ var import_node_path13 = require("node:path");
11496
11497
 
11497
11498
  // src/commands/agentRulesBlock.ts
11498
11499
  var VIBERAVEN_BLOCK_START = "<!-- VIBERAVEN:START -->";
@@ -11508,7 +11509,12 @@ Canonical command: \`${PUBLIC_AGENT_MODE_COMMAND}\`
11508
11509
 
11509
11510
  Use VibeRaven before launch, deployment, real users, auth, billing, database, RLS, env vars, webhooks, monitoring, or tests.
11510
11511
 
11511
- 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/\`.`;
11512
11518
  var AGENT_NEGATIVE_CONSTRAINTS = `## Event-Triggered Production Verification
11513
11519
 
11514
11520
  Trigger: modifications to \`package.json\`, \`vercel.json\`, \`supabase/migrations/**/*\`, \`.env.example\`, auth/billing/webhook paths, or deploy workflow files.
@@ -11517,10 +11523,23 @@ Trigger: modifications to \`package.json\`, \`vercel.json\`, \`supabase/migratio
11517
11523
  2. Do NOT run verify after every file patch. Batch heals, then \`${PUBLIC_VERIFY_COMMAND}\` once per loop.
11518
11524
  3. Do NOT \`git push\` or deploy after auth, RLS, billing, or webhook changes without \`${PUBLIC_AGENT_MODE_COMMAND}\`.
11519
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.
11520
11532
 
11521
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\`.`;
11522
11539
  var AGENT_RULES_BODY = `${AGENT_RULES_PREAMBLE}
11523
11540
 
11541
+ ${STACK_FRAMING}
11542
+
11524
11543
  ${AGENT_NEGATIVE_CONSTRAINTS}
11525
11544
 
11526
11545
  ## VibeRaven Production-Readiness Gate
@@ -11587,7 +11606,9 @@ var BLOCK_MARKER_PAIRS = [
11587
11606
  [VIBERAVEN_LEGACY_BLOCK_START, VIBERAVEN_LEGACY_BLOCK_END]
11588
11607
  ];
11589
11608
  function buildAgentRulesBlock() {
11590
- return wrapViberavenBlock(AGENT_RULES_BODY);
11609
+ return wrapViberavenBlock(`${AGENT_RULES_BODY}
11610
+
11611
+ ${ANCHOR_FOOTER}`);
11591
11612
  }
11592
11613
  function buildAgentContextBlock() {
11593
11614
  return wrapViberavenBlock(AGENT_CONTEXT_BODY);
@@ -11708,11 +11729,135 @@ function escapeRegExp3(value) {
11708
11729
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
11709
11730
  }
11710
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
+
11711
11856
  // src/commands/agentTargets.ts
11712
11857
  var AGENT_RULE_TARGETS = {
11713
11858
  codex: { file: "AGENTS.md" },
11714
11859
  claude: { file: "CLAUDE.md" },
11715
- cursor: { file: ".cursor/rules/viberaven.mdc" },
11860
+ cursor: { file: ".cursor/rules/viberaven-core.mdc" },
11716
11861
  cursorLegacy: { file: ".cursorrules", aliases: ["cursor-legacy"] },
11717
11862
  copilot: { file: ".github/copilot-instructions.md", aliases: ["github-copilot"] },
11718
11863
  gemini: { file: "GEMINI.md" },
@@ -11758,20 +11903,7 @@ function renderAgentRulesForTarget(target) {
11758
11903
  return ["@AGENTS.md", "", buildAgentRulesBlock()].join("\n");
11759
11904
  }
11760
11905
  if (target === "cursor") {
11761
- return [
11762
- "---",
11763
- "description: Applied when verifying backend or infrastructure readiness before production deployment.",
11764
- "globs:",
11765
- " - package.json",
11766
- " - vercel.json",
11767
- " - supabase/migrations/**",
11768
- " - .env.example",
11769
- " - .github/workflows/**",
11770
- "alwaysApply: true",
11771
- "---",
11772
- "",
11773
- buildAgentRulesBlock()
11774
- ].join("\n");
11906
+ return renderCursorCoreRulePreview();
11775
11907
  }
11776
11908
  if (target === "agentContext") {
11777
11909
  return ["# VibeRaven Agent Context", "", buildAgentContextBlock()].join("\n");
@@ -11803,26 +11935,88 @@ function getAgentRulesTargets(value) {
11803
11935
  return ALL_AGENT_RULE_TARGETS.filter((target) => requestedTargets.has(target));
11804
11936
  }
11805
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
+
11806
11987
  // src/commands/initRules.ts
11807
11988
  async function initAgentRules(options) {
11808
11989
  const targets = options.targets ?? [...CORE_AGENT_INJECTION_TARGETS];
11809
11990
  const results = [];
11810
11991
  for (const target of targets) {
11992
+ if (target === "cursor") {
11993
+ results.push(...await initCursorRulesPack({ cwd: options.cwd, dryRun: options.dryRun }));
11994
+ continue;
11995
+ }
11811
11996
  const file = AGENT_RULE_TARGETS[target].file;
11812
- const path = (0, import_node_path11.join)(options.cwd, file);
11813
- const existing = await readExistingFile(path);
11997
+ const path = (0, import_node_path13.join)(options.cwd, file);
11998
+ const existing = await readExistingFile2(path);
11814
11999
  const injected = injectAgentRulesBlock(existing.content, renderAgentRulesForTarget(target));
11815
12000
  const action = !existing.exists ? "created" : injected.changed ? "updated" : "unchanged";
11816
12001
  if (!options.dryRun && injected.changed) {
11817
- await (0, import_promises8.mkdir)((0, import_node_path11.dirname)(path), { recursive: true });
11818
- 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");
11819
12004
  }
11820
12005
  results.push({ target, file, path, action });
11821
12006
  }
11822
- return results;
12007
+ const packageJsonScripts = await seedPackageJsonScripts({
12008
+ cwd: options.cwd,
12009
+ dryRun: options.dryRun
12010
+ });
12011
+ return { results, packageJsonScripts };
11823
12012
  }
11824
12013
  function renderAgentRulesDryRun(targets) {
11825
- 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");
11826
12020
  const previews = targets.flatMap((target) => [
11827
12021
  `Preview: ${target} (${AGENT_RULE_TARGETS[target].file})`,
11828
12022
  "",
@@ -11830,7 +12024,8 @@ function renderAgentRulesDryRun(targets) {
11830
12024
  ]);
11831
12025
  return [`VibeRaven agent rules dry run`, "", `Target files:`, files, "", ...previews].join("\n");
11832
12026
  }
11833
- function formatAgentRulesInitSummary(results) {
12027
+ function formatAgentRulesInitSummary(output) {
12028
+ const { results, packageJsonScripts } = output;
11834
12029
  const created = results.filter((result) => result.action === "created");
11835
12030
  const updated = results.filter((result) => result.action === "updated");
11836
12031
  const skipped = results.filter((result) => result.action === "unchanged");
@@ -11856,22 +12051,32 @@ function formatAgentRulesInitSummary(results) {
11856
12051
  }
11857
12052
  lines.push("");
11858
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
+ }
11859
12064
  lines.push(
11860
12065
  `Done: ${created.length} created, ${updated.length} updated, ${skipped.length} skipped.`
11861
12066
  );
11862
12067
  return lines.join("\n");
11863
12068
  }
11864
- async function readExistingFile(path) {
12069
+ async function readExistingFile2(path) {
11865
12070
  try {
11866
- 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") };
11867
12072
  } catch (error) {
11868
- if (isFileNotFoundError(error)) {
12073
+ if (isFileNotFoundError2(error)) {
11869
12074
  return { exists: false, content: "" };
11870
12075
  }
11871
12076
  throw error;
11872
12077
  }
11873
12078
  }
11874
- function isFileNotFoundError(error) {
12079
+ function isFileNotFoundError2(error) {
11875
12080
  return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
11876
12081
  }
11877
12082
 
@@ -12083,7 +12288,7 @@ async function handleAuth() {
12083
12288
  await runDeviceLogin(apiBaseUrl);
12084
12289
  }
12085
12290
  async function handleAgentRules(cwd) {
12086
- const results = await initAgentRules({ cwd });
12291
+ const { results } = await initAgentRules({ cwd });
12087
12292
  for (const result of results) {
12088
12293
  const color = result.action === "created" ? import_picocolors4.default.green : result.action === "updated" ? import_picocolors4.default.yellow : import_picocolors4.default.dim;
12089
12294
  M2.message(color(`${result.action.toUpperCase()}: ${result.file}`));
@@ -12171,7 +12376,7 @@ async function runInteractiveSession(startDir = process.cwd()) {
12171
12376
  Ie(`${import_picocolors4.default.bold("VibeRaven")} ${import_picocolors4.default.dim(VERSION)}`);
12172
12377
  const cwd = await resolveWorkspaceRoot(startDir);
12173
12378
  const artifactsAt = await findArtifactsWorkspace(startDir);
12174
- 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)) {
12175
12380
  M2.message(import_picocolors4.default.dim(`Using scan from: ${artifactsAt}`));
12176
12381
  } else {
12177
12382
  M2.message(import_picocolors4.default.dim(`Project folder: ${cwd}`));
@@ -12238,29 +12443,29 @@ async function runInteractiveSession(startDir = process.cwd()) {
12238
12443
  }
12239
12444
 
12240
12445
  // src/commands/condense.ts
12241
- var import_promises9 = require("node:fs/promises");
12242
- var import_node_path13 = require("node:path");
12446
+ var import_promises11 = require("node:fs/promises");
12447
+ var import_node_path15 = require("node:path");
12243
12448
  async function runCondenseCommand(options) {
12244
12449
  const dir = getProjectArtifactsDir(options.cwd);
12245
- const artifact = JSON.parse(await (0, import_promises9.readFile)((0, import_node_path13.join)(dir, "last-scan.json"), "utf8"));
12246
- const contextMapPath = (0, import_node_path13.join)(dir, "context-map.json");
12247
- 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)}
12248
12453
  `, "utf8");
12249
12454
  return { contextMapPath };
12250
12455
  }
12251
12456
 
12252
12457
  // src/heal/apply.ts
12253
- var import_promises11 = require("node:fs/promises");
12254
- var import_node_fs8 = require("node:fs");
12255
- 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");
12256
12461
 
12257
12462
  // src/heal/pathSafety.ts
12258
- var import_node_path14 = require("node:path");
12463
+ var import_node_path16 = require("node:path");
12259
12464
  var BLOCKED_SEGMENTS = /* @__PURE__ */ new Set([".git", "node_modules", "dist", "build", ".next", ".viberaven"]);
12260
12465
  function assertSafeHealTarget(cwd, target) {
12261
- const root = (0, import_node_path14.resolve)(cwd);
12262
- const absolute = (0, import_node_path14.resolve)(root, target);
12263
- 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);
12264
12469
  if (rel.startsWith("..") || rel === "" || /^[A-Za-z]:/.test(rel)) {
12265
12470
  throw new Error("Heal target must stay inside the workspace");
12266
12471
  }
@@ -12283,9 +12488,9 @@ function applyEmptyCatchRecipe(source) {
12283
12488
  }
12284
12489
 
12285
12490
  // src/heal/recipes/index.ts
12286
- var import_node_fs7 = require("node:fs");
12287
- var import_promises10 = require("node:fs/promises");
12288
- 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");
12289
12494
 
12290
12495
  // src/heal/recipes/envAuthSecret.ts
12291
12496
  function applyAuthSecretRecipe(source) {
@@ -12665,6 +12870,194 @@ function applyRateLimitRecipe(source, hasUpstash) {
12665
12870
  };
12666
12871
  }
12667
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
+
12668
13061
  // src/heal/recipes/index.ts
12669
13062
  function defaultTargetFile(gapId) {
12670
13063
  const map = {
@@ -12678,19 +13071,20 @@ function defaultTargetFile(gapId) {
12678
13071
  rls_disabled: "",
12679
13072
  // no file — provider-action
12680
13073
  missing_csp_header: "next.config.js",
12681
- missing_rate_limit: "middleware.ts"
13074
+ missing_rate_limit: "middleware.ts",
13075
+ eslint_restricted_imports: ""
12682
13076
  };
12683
13077
  return map[gapId];
12684
13078
  }
12685
13079
  async function readSourceOrEmpty(absolutePath) {
12686
- if (!(0, import_node_fs7.existsSync)(absolutePath)) return "";
12687
- return (0, import_promises10.readFile)(absolutePath, "utf8");
13080
+ if (!(0, import_node_fs9.existsSync)(absolutePath)) return "";
13081
+ return (0, import_promises12.readFile)(absolutePath, "utf8");
12688
13082
  }
12689
13083
  async function detectUpstash(cwd) {
12690
13084
  try {
12691
- const pkgPath = (0, import_node_path15.join)(cwd, "package.json");
12692
- if (!(0, import_node_fs7.existsSync)(pkgPath)) return false;
12693
- 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");
12694
13088
  const pkg = JSON.parse(raw);
12695
13089
  const deps = {
12696
13090
  ...pkg["dependencies"] ?? {},
@@ -12705,7 +13099,7 @@ async function dispatchRecipeByGapId(gapId, cwd, explicitTarget) {
12705
13099
  const targetFile = explicitTarget ?? defaultTargetFile(gapId);
12706
13100
  if (targetFile === void 0) return null;
12707
13101
  if (gapId === "auth_secret_missing" || gapId === "node_env_not_set" || gapId === "database_url_missing") {
12708
- const absolutePath = (0, import_node_path15.join)(cwd, targetFile);
13102
+ const absolutePath = (0, import_node_path18.join)(cwd, targetFile);
12709
13103
  const source = await readSourceOrEmpty(absolutePath);
12710
13104
  let result;
12711
13105
  if (gapId === "auth_secret_missing") result = applyAuthSecretRecipe(source);
@@ -12719,25 +13113,25 @@ async function dispatchRecipeByGapId(gapId, cwd, explicitTarget) {
12719
13113
  };
12720
13114
  }
12721
13115
  if (gapId === "missing_error_boundary") {
12722
- const absolutePath = (0, import_node_path15.join)(cwd, "app/error.tsx");
13116
+ const absolutePath = (0, import_node_path18.join)(cwd, "app/error.tsx");
12723
13117
  const source = await readSourceOrEmpty(absolutePath);
12724
13118
  const result = applyErrorBoundaryRecipe(source);
12725
13119
  return { ...result, canAutoApply: true, recipeName: gapId };
12726
13120
  }
12727
13121
  if (gapId === "missing_health_route") {
12728
- 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");
12729
13123
  const source = await readSourceOrEmpty(absolutePath);
12730
13124
  const result = applyHealthRouteRecipe(source);
12731
13125
  return { ...result, canAutoApply: true, recipeName: gapId };
12732
13126
  }
12733
13127
  if (gapId === "missing_loading_state") {
12734
- const absolutePath = (0, import_node_path15.join)(cwd, "app/loading.tsx");
13128
+ const absolutePath = (0, import_node_path18.join)(cwd, "app/loading.tsx");
12735
13129
  const source = await readSourceOrEmpty(absolutePath);
12736
13130
  const result = applyLoadingStateRecipe(source);
12737
13131
  return { ...result, canAutoApply: true, recipeName: gapId };
12738
13132
  }
12739
13133
  if (gapId === "missing_404_page") {
12740
- 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");
12741
13135
  const source = await readSourceOrEmpty(absolutePath);
12742
13136
  const result = applyNotFoundRecipe(source);
12743
13137
  return { ...result, canAutoApply: true, recipeName: gapId };
@@ -12755,10 +13149,10 @@ async function dispatchRecipeByGapId(gapId, cwd, explicitTarget) {
12755
13149
  }
12756
13150
  if (gapId === "missing_csp_header") {
12757
13151
  let configFile = "next.config.js";
12758
- let absolutePath = (0, import_node_path15.join)(cwd, configFile);
12759
- 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)) {
12760
13154
  configFile = "next.config.mjs";
12761
- absolutePath = (0, import_node_path15.join)(cwd, configFile);
13155
+ absolutePath = (0, import_node_path18.join)(cwd, configFile);
12762
13156
  }
12763
13157
  const source = await readSourceOrEmpty(absolutePath);
12764
13158
  const result = applyCspHeaderRecipe(source);
@@ -12770,7 +13164,7 @@ async function dispatchRecipeByGapId(gapId, cwd, explicitTarget) {
12770
13164
  }
12771
13165
  if (gapId === "missing_rate_limit") {
12772
13166
  const hasUpstash = await detectUpstash(cwd);
12773
- const middlewarePath = (0, import_node_path15.join)(cwd, "middleware.ts");
13167
+ const middlewarePath = (0, import_node_path18.join)(cwd, "middleware.ts");
12774
13168
  const source = await readSourceOrEmpty(middlewarePath);
12775
13169
  const result = applyRateLimitRecipe(source, hasUpstash);
12776
13170
  return {
@@ -12780,6 +13174,30 @@ async function dispatchRecipeByGapId(gapId, cwd, explicitTarget) {
12780
13174
  recipeName: gapId
12781
13175
  };
12782
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
+ }
12783
13201
  return null;
12784
13202
  }
12785
13203
 
@@ -12843,14 +13261,14 @@ async function applyHeal(options) {
12843
13261
  rollback: { available: false, instructions: "Recipe matched but no change was needed (already applied or file already exists)." }
12844
13262
  };
12845
13263
  }
12846
- const absoluteTarget = (0, import_node_path16.join)(options.cwd, dispatched.targetFile);
13264
+ const absoluteTarget = (0, import_node_path19.join)(options.cwd, dispatched.targetFile);
12847
13265
  assertSafeHealTarget(options.cwd, dispatched.targetFile);
12848
- await (0, import_promises11.mkdir)((0, import_node_path16.dirname)(absoluteTarget), { recursive: true });
12849
- const healDir2 = (0, import_node_path16.join)(options.cwd, ".viberaven", "heal", id);
12850
- await (0, import_promises11.mkdir)((0, import_node_path16.join)(healDir2, "before"), { recursive: true });
12851
- const beforeContent = (0, import_node_fs8.existsSync)(absoluteTarget) ? await (0, import_promises11.readFile)(absoluteTarget, "utf8") : "";
12852
- await (0, import_promises11.writeFile)((0, import_node_path16.join)(healDir2, "before", "target.txt"), beforeContent, "utf8");
12853
- 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");
12854
13272
  const patch2 = [
12855
13273
  `--- ${dispatched.targetFile}`,
12856
13274
  `+++ ${dispatched.targetFile}`,
@@ -12860,7 +13278,7 @@ async function applyHeal(options) {
12860
13278
  dispatched.output,
12861
13279
  ""
12862
13280
  ].join("\n");
12863
- 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");
12864
13282
  const result2 = {
12865
13283
  $schema: "https://viberaven.dev/schemas/heal-result.schema.json",
12866
13284
  schemaVersion: "v1",
@@ -12871,7 +13289,7 @@ async function applyHeal(options) {
12871
13289
  gapId: options.gapId,
12872
13290
  recipe: dispatched.recipeName,
12873
13291
  target: dispatched.targetFile,
12874
- changedFiles: [(0, import_node_path16.relative)(options.cwd, absoluteTarget).replace(/\\/g, "/")],
13292
+ changedFiles: [(0, import_node_path19.relative)(options.cwd, absoluteTarget).replace(/\\/g, "/")],
12875
13293
  artifacts: {
12876
13294
  patch: `.viberaven/heal/${id}/patch.diff`,
12877
13295
  result: `.viberaven/heal/${id}/result.json`
@@ -12881,7 +13299,7 @@ async function applyHeal(options) {
12881
13299
  instructions: "Restore .viberaven/heal/<healId>/before/target.txt to the target file or apply the reverse patch."
12882
13300
  }
12883
13301
  };
12884
- 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)}
12885
13303
  `, "utf8");
12886
13304
  return result2;
12887
13305
  }
@@ -12901,7 +13319,7 @@ async function applyHeal(options) {
12901
13319
  };
12902
13320
  }
12903
13321
  const absolute = assertSafeHealTarget(options.cwd, options.target);
12904
- const before = await (0, import_promises11.readFile)(absolute, "utf8");
13322
+ const before = await (0, import_promises13.readFile)(absolute, "utf8");
12905
13323
  const recipe = applyEmptyCatchRecipe(before);
12906
13324
  if (!recipe.changed) {
12907
13325
  return {
@@ -12918,10 +13336,10 @@ async function applyHeal(options) {
12918
13336
  rollback: { available: false, instructions: "No supported heal recipe matched this file." }
12919
13337
  };
12920
13338
  }
12921
- const healDir = (0, import_node_path16.join)(options.cwd, ".viberaven", "heal", id);
12922
- await (0, import_promises11.mkdir)((0, import_node_path16.join)(healDir, "before"), { recursive: true });
12923
- await (0, import_promises11.writeFile)((0, import_node_path16.join)(healDir, "before", "target.txt"), before, "utf8");
12924
- 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");
12925
13343
  const patch = [
12926
13344
  `--- ${options.target}`,
12927
13345
  `+++ ${options.target}`,
@@ -12931,7 +13349,7 @@ async function applyHeal(options) {
12931
13349
  recipe.output,
12932
13350
  ""
12933
13351
  ].join("\n");
12934
- 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");
12935
13353
  const result = {
12936
13354
  $schema: "https://viberaven.dev/schemas/heal-result.schema.json",
12937
13355
  schemaVersion: "v1",
@@ -12942,7 +13360,7 @@ async function applyHeal(options) {
12942
13360
  gapId: options.gapId,
12943
13361
  recipe: "empty-catch-safe-response",
12944
13362
  target: options.target,
12945
- changedFiles: [(0, import_node_path16.relative)(options.cwd, absolute).replace(/\\/g, "/")],
13363
+ changedFiles: [(0, import_node_path19.relative)(options.cwd, absolute).replace(/\\/g, "/")],
12946
13364
  artifacts: {
12947
13365
  patch: `.viberaven/heal/${id}/patch.diff`,
12948
13366
  result: `.viberaven/heal/${id}/result.json`
@@ -12952,20 +13370,20 @@ async function applyHeal(options) {
12952
13370
  instructions: "Restore .viberaven/heal/<healId>/before/target.txt to the target file or apply the reverse patch."
12953
13371
  }
12954
13372
  };
12955
- 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)}
12956
13374
  `, "utf8");
12957
13375
  return result;
12958
13376
  }
12959
13377
 
12960
13378
  // src/heal/plan.ts
12961
- var import_promises12 = require("node:fs/promises");
12962
- var import_node_path17 = require("node:path");
13379
+ var import_promises14 = require("node:fs/promises");
13380
+ var import_node_path20 = require("node:path");
12963
13381
  function healId2() {
12964
13382
  return `heal_${(/* @__PURE__ */ new Date()).toISOString().replace(/\D/g, "").slice(0, 14)}`;
12965
13383
  }
12966
13384
  async function writeHealPlan(options) {
12967
- const dir = (0, import_node_path17.join)(options.cwd, ".viberaven");
12968
- 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 });
12969
13387
  const id = healId2();
12970
13388
  const target = options.target ?? `gap:${options.gapId}`;
12971
13389
  const markdown = [
@@ -12992,21 +13410,21 @@ async function writeHealPlan(options) {
12992
13410
  artifacts: { plan: ".viberaven/heal-plan.md" },
12993
13411
  rollback: { available: false, instructions: "No source files were changed." }
12994
13412
  };
12995
- await (0, import_promises12.writeFile)((0, import_node_path17.join)(dir, "heal-plan.md"), markdown, "utf8");
12996
- 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)}
12997
13415
  `, "utf8");
12998
13416
  return result;
12999
13417
  }
13000
13418
 
13001
13419
  // src/heal/prompt.ts
13002
- var import_promises13 = require("node:fs/promises");
13003
- var import_node_path18 = require("node:path");
13420
+ var import_promises15 = require("node:fs/promises");
13421
+ var import_node_path21 = require("node:path");
13004
13422
  function healId3() {
13005
13423
  return `heal_${(/* @__PURE__ */ new Date()).toISOString().replace(/\D/g, "").slice(0, 14)}`;
13006
13424
  }
13007
13425
  async function writeHealPrompt(options) {
13008
- const dir = (0, import_node_path18.join)(options.cwd, ".viberaven");
13009
- 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 });
13010
13428
  const id = healId3();
13011
13429
  const target = options.target ?? `gap:${options.gapId}`;
13012
13430
  const prompt = [
@@ -13033,12 +13451,12 @@ async function writeHealPrompt(options) {
13033
13451
  artifacts: { prompt: ".viberaven/heal-prompt.md" },
13034
13452
  rollback: { available: false, instructions: "No source files were changed." }
13035
13453
  };
13036
- 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");
13037
13455
  return result;
13038
13456
  }
13039
13457
 
13040
13458
  // src/loopState.ts
13041
- var import_promises14 = require("fs/promises");
13459
+ var import_promises16 = require("fs/promises");
13042
13460
  var import_path = require("path");
13043
13461
  var DEFAULT_LOOP_STATE = {
13044
13462
  batchApplied: 0,
@@ -13051,7 +13469,7 @@ function loopStatePath(workspaceRoot) {
13051
13469
  }
13052
13470
  async function loadLoopState(workspaceRoot) {
13053
13471
  try {
13054
- const raw = await (0, import_promises14.readFile)(loopStatePath(workspaceRoot), "utf8");
13472
+ const raw = await (0, import_promises16.readFile)(loopStatePath(workspaceRoot), "utf8");
13055
13473
  const parsed = JSON.parse(raw);
13056
13474
  if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed) && typeof parsed.batchApplied === "number" && typeof parsed.lastGapCount === "number" && typeof parsed.stalledScans === "number") {
13057
13475
  const p2 = parsed;
@@ -13071,8 +13489,8 @@ async function loadLoopState(workspaceRoot) {
13071
13489
  async function saveLoopState(workspaceRoot, state) {
13072
13490
  try {
13073
13491
  const dir = (0, import_path.join)(workspaceRoot, ".viberaven");
13074
- await (0, import_promises14.mkdir)(dir, { recursive: true });
13075
- 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");
13076
13494
  } catch (err) {
13077
13495
  console.warn("[VibeRaven] Could not save loop-state.json:", err instanceof Error ? err.message : String(err));
13078
13496
  }
@@ -13118,9 +13536,9 @@ async function runHealCommand(options) {
13118
13536
  }
13119
13537
 
13120
13538
  // src/stackRecommend.ts
13121
- var import_promises15 = require("node:fs/promises");
13122
- var import_node_fs9 = require("node:fs");
13123
- 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");
13124
13542
  var DEFAULT_STACK = {
13125
13543
  frontend: "react",
13126
13544
  ui: "tailwind + shadcn/ui",
@@ -13131,11 +13549,11 @@ var DEFAULT_STACK = {
13131
13549
  reason: "Agent-default stack for lowest launch friction when repo signals are ambiguous"
13132
13550
  };
13133
13551
  async function recommendStack(cwd = process.cwd()) {
13134
- const pkgPath = (0, import_node_path19.join)(cwd, "package.json");
13135
- 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)) {
13136
13554
  return DEFAULT_STACK;
13137
13555
  }
13138
- 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"));
13139
13557
  const deps = { ...pkg.dependencies, ...pkg.devDependencies };
13140
13558
  const names = Object.keys(deps).join(" ").toLowerCase();
13141
13559
  const rec = {
@@ -13177,24 +13595,29 @@ async function runInitCommand(options) {
13177
13595
  console.log(renderAgentRulesDryRun(targets));
13178
13596
  return 0;
13179
13597
  }
13180
- const results = await initAgentRules({
13598
+ const output = await initAgentRules({
13181
13599
  cwd: options.cwd,
13182
13600
  targets,
13183
13601
  dryRun: false
13184
13602
  });
13185
- console.log(formatAgentRulesInitSummary(results));
13603
+ console.log(formatAgentRulesInitSummary(output));
13186
13604
  return 0;
13187
13605
  }
13188
13606
 
13189
13607
  // src/commands/doctorAgents.ts
13190
- var import_promises16 = require("node:fs/promises");
13191
- var import_node_path20 = require("node:path");
13608
+ var import_promises18 = require("node:fs/promises");
13609
+ var import_node_path23 = require("node:path");
13192
13610
  var REQUIRED_EXISTENCE_CHECKS = [
13193
13611
  { id: "agents-md", file: "AGENTS.md" },
13194
13612
  { id: "claude-md", file: "CLAUDE.md" },
13195
- { id: "cursor-rule", file: ".cursor/rules/viberaven.mdc" },
13613
+ { id: "cursor-core-rule", file: ".cursor/rules/viberaven-core.mdc" },
13196
13614
  { id: "copilot-instructions", file: ".github/copilot-instructions.md" }
13197
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
+ ];
13198
13621
  var STALE_PATTERNS = [
13199
13622
  { id: "stale-beta-scan", pattern: "@beta scan" },
13200
13623
  { id: "stale-viberaven-station", pattern: "viberaven-station" },
@@ -13203,17 +13626,37 @@ var STALE_PATTERNS = [
13203
13626
  async function checkAgentInjection(cwd) {
13204
13627
  const checks = [];
13205
13628
  for (const item3 of REQUIRED_EXISTENCE_CHECKS) {
13206
- 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));
13207
13630
  checks.push({
13208
13631
  id: item3.id,
13209
13632
  status: exists ? "pass" : "fail",
13210
13633
  message: exists ? `${item3.file} exists` : `Missing ${item3.file}`
13211
13634
  });
13212
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
+ }
13213
13656
  for (const target of CORE_AGENT_INJECTION_TARGETS) {
13214
- const file = AGENT_RULE_TARGETS[target].file;
13215
- const path = (0, import_node_path20.join)(cwd, file);
13216
- 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);
13217
13660
  if (!exists) {
13218
13661
  checks.push({
13219
13662
  id: `canonical-${target}`,
@@ -13222,7 +13665,7 @@ async function checkAgentInjection(cwd) {
13222
13665
  });
13223
13666
  continue;
13224
13667
  }
13225
- const content = await (0, import_promises16.readFile)(path, "utf-8");
13668
+ const content = await (0, import_promises18.readFile)(path, "utf-8");
13226
13669
  const hasCommand = content.includes(PUBLIC_AGENT_MODE_COMMAND);
13227
13670
  checks.push({
13228
13671
  id: `canonical-${target}`,
@@ -13254,9 +13697,9 @@ function formatDoctorAgentsReport(report) {
13254
13697
  lines.push(report.ok ? "All agent injection checks passed." : "Agent injection checks failed.");
13255
13698
  return lines.join("\n");
13256
13699
  }
13257
- async function fileExists(path) {
13700
+ async function fileExists2(path) {
13258
13701
  try {
13259
- await (0, import_promises16.access)(path);
13702
+ await (0, import_promises18.access)(path);
13260
13703
  return true;
13261
13704
  } catch {
13262
13705
  return false;
@@ -13270,6 +13713,191 @@ async function runDoctorAgentsCommand(options) {
13270
13713
  return report.ok ? 0 : 1;
13271
13714
  }
13272
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
+
13273
13901
  // src/output/nextActionBlock.ts
13274
13902
  function buildNextActionBlock(tasks, loopState, plan) {
13275
13903
  const batchSize = plan === "pro" ? 10 : 3;
@@ -13389,9 +14017,9 @@ function printNextActionBlock(block) {
13389
14017
  }
13390
14018
 
13391
14019
  // src/providerMcpBridge.ts
13392
- var import_node_fs10 = require("node:fs");
14020
+ var import_node_fs12 = require("node:fs");
13393
14021
  var import_node_os2 = require("node:os");
13394
- var import_node_path21 = require("node:path");
14022
+ var import_node_path24 = require("node:path");
13395
14023
  var UPGRADE_URL4 = "https://viberaven.dev/pricing";
13396
14024
  var FALLBACK_COMMAND = "npx -y viberaven audit --vercel-supabase --json";
13397
14025
  var SUPPORTED_PROVIDERS = /* @__PURE__ */ new Set(["supabase", "vercel"]);
@@ -13399,9 +14027,9 @@ var configPathsOverride;
13399
14027
  function defaultMcpConfigPaths() {
13400
14028
  const home = (0, import_node_os2.homedir)();
13401
14029
  return [
13402
- (0, import_node_path21.join)(home, ".config", "claude", "claude_desktop_config.json"),
13403
- (0, import_node_path21.join)(home, ".cursor", "mcp.json"),
13404
- (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")
13405
14033
  ];
13406
14034
  }
13407
14035
  function resolveConfigPaths() {
@@ -13430,11 +14058,11 @@ function findServerEntry(servers, provider2) {
13430
14058
  function findProviderMcpConfig(provider2, configPaths) {
13431
14059
  const paths = configPaths ?? resolveConfigPaths();
13432
14060
  for (const path of paths) {
13433
- if (!(0, import_node_fs10.existsSync)(path)) {
14061
+ if (!(0, import_node_fs12.existsSync)(path)) {
13434
14062
  continue;
13435
14063
  }
13436
14064
  try {
13437
- const raw = JSON.parse((0, import_node_fs10.readFileSync)(path, "utf8"));
14065
+ const raw = JSON.parse((0, import_node_fs12.readFileSync)(path, "utf8"));
13438
14066
  const servers = parseMcpServers(raw);
13439
14067
  if (!servers) {
13440
14068
  continue;
@@ -13636,6 +14264,7 @@ function isBooleanFlag(command, key) {
13636
14264
  if (key === "open" && (command === "" || command === "scan" || command === "report")) return true;
13637
14265
  if (key === "verify" && command === "") return true;
13638
14266
  if (key === "vercel-supabase" && command === "audit") return true;
14267
+ if (key === "json" && command === "validate-npm-package") return true;
13639
14268
  if (key === "dry-run" && command === "init") return true;
13640
14269
  if (key === "agents" && command === "doctor") return true;
13641
14270
  return false;
@@ -13654,7 +14283,7 @@ async function guardEarlyVerifyScan(input) {
13654
14283
  if (!verifyLike) {
13655
14284
  return void 0;
13656
14285
  }
13657
- 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());
13658
14287
  const loopState = await loadLoopState(workspacePath);
13659
14288
  if (loopState.batchApplied <= 0) {
13660
14289
  return void 0;
@@ -13730,7 +14359,7 @@ async function cmdStatus(flags, positional) {
13730
14359
  console.log("Not signed in. Run: viberaven login");
13731
14360
  return 1;
13732
14361
  }
13733
- 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();
13734
14363
  let artifact;
13735
14364
  try {
13736
14365
  artifact = await loadLastArtifact(startDir);
@@ -13884,7 +14513,7 @@ async function cmdWatch(flags) {
13884
14513
  }
13885
14514
  }
13886
14515
  async function runScanCommand(flags, positional, options) {
13887
- 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());
13888
14517
  const apiBaseUrl = resolveApiBaseUrl(typeof flags["api-url"] === "string" ? flags["api-url"] : void 0);
13889
14518
  let accessToken;
13890
14519
  try {
@@ -13962,7 +14591,7 @@ async function runScanCommand(flags, positional, options) {
13962
14591
  return { exitCode: 0, artifacts: paths };
13963
14592
  }
13964
14593
  async function cmdReport(flags, positional) {
13965
- 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();
13966
14595
  try {
13967
14596
  const paths = await refreshReportFromDisk(startDir);
13968
14597
  console.log(`Report refreshed: ${paths.reportPath}`);
@@ -13984,7 +14613,7 @@ async function cmdReport(flags, positional) {
13984
14613
  }
13985
14614
  }
13986
14615
  async function cmdPrompt(flags, positional) {
13987
- 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();
13988
14617
  let artifact;
13989
14618
  try {
13990
14619
  artifact = await loadLastArtifact(startDir);
@@ -14082,7 +14711,7 @@ async function main() {
14082
14711
  const wantsJsonl = hasFlag(flags, "jsonl");
14083
14712
  const wantsStrict = hasFlag(flags, "strict");
14084
14713
  if (flags.condense) {
14085
- 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();
14086
14715
  const result = await runCondenseCommand({ cwd });
14087
14716
  console.log(`VibeRaven context map refreshed: ${result.contextMapPath}`);
14088
14717
  return 0;
@@ -14111,7 +14740,7 @@ async function main() {
14111
14740
  console.error("VibeRaven could not produce machine output because scan artifacts were not written.");
14112
14741
  return 3;
14113
14742
  }
14114
- 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;
14115
14744
  const strictExitCode = wantsStrict && gateResult ? exitCodeForStrictGate(gateResult, { failOnWarnings: flags.strict === "warning" }) : scanResult.exitCode;
14116
14745
  if (wantsJson && gateResult) {
14117
14746
  process.stdout.write(renderGateResultJson(gateResult));
@@ -14146,7 +14775,7 @@ async function main() {
14146
14775
  case "next":
14147
14776
  return runNextCommand({
14148
14777
  json: Boolean(flags.json),
14149
- 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()
14150
14779
  });
14151
14780
  case "guide": {
14152
14781
  const provider2 = positional[0];
@@ -14184,7 +14813,7 @@ async function main() {
14184
14813
  case "provider-verify":
14185
14814
  return cmdProviderVerify(flags, positional);
14186
14815
  case "init": {
14187
- 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();
14188
14817
  const agents = typeof flags.agents === "string" ? flags.agents : void 0;
14189
14818
  return runInitCommand({
14190
14819
  cwd,
@@ -14198,7 +14827,12 @@ async function main() {
14198
14827
  return 1;
14199
14828
  }
14200
14829
  return runDoctorAgentsCommand({
14201
- 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)
14202
14836
  });
14203
14837
  default:
14204
14838
  console.error(`Unknown command: ${command}`);