viberails 0.5.1 → 0.5.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -361,11 +361,10 @@ function buildMenuOptions(state, packageCount) {
361
361
  hint: state.fileNamingValue
362
362
  });
363
363
  }
364
- options.push({
365
- value: "testCoverage",
366
- label: "Test coverage target",
367
- hint: state.testCoverage === 0 ? "0 (disabled)" : `${state.testCoverage}%`
368
- });
364
+ const isMonorepo = packageCount > 0;
365
+ const coverageLabel = isMonorepo ? "Default coverage target" : "Test coverage target";
366
+ const coverageHint = state.testCoverage === 0 ? "0 (disabled)" : isMonorepo ? `${state.testCoverage}% (per-package default)` : `${state.testCoverage}%`;
367
+ options.push({ value: "testCoverage", label: coverageLabel, hint: coverageHint });
369
368
  options.push({
370
369
  value: "enforceMissingTests",
371
370
  label: "Enforce missing tests",
@@ -375,16 +374,16 @@ function buildMenuOptions(state, packageCount) {
375
374
  options.push(
376
375
  {
377
376
  value: "coverageSummaryPath",
378
- label: "Coverage summary path",
377
+ label: isMonorepo ? "Default coverage summary path" : "Coverage summary path",
379
378
  hint: state.coverageSummaryPath
380
379
  },
381
380
  {
382
381
  value: "coverageCommand",
383
- label: "Coverage command",
382
+ label: isMonorepo ? "Default coverage command" : "Coverage command",
384
383
  hint: state.coverageCommand ?? "auto-detect from package.json test runner"
385
384
  }
386
385
  );
387
- if (packageCount > 0) {
386
+ if (isMonorepo) {
388
387
  options.push({
389
388
  value: "packageOverrides",
390
389
  label: "Per-package coverage overrides",
@@ -1676,6 +1675,58 @@ function displayRulesPreview(config) {
1676
1675
  );
1677
1676
  console.log("");
1678
1677
  }
1678
+ function displayInitSummary(config, exemptedPackages) {
1679
+ const root = config.packages.find((p) => p.path === ".") ?? config.packages[0];
1680
+ const isMonorepo = config.packages.length > 1;
1681
+ const ok = import_chalk4.default.green("\u2713");
1682
+ const off = import_chalk4.default.dim("\u25CB");
1683
+ console.log("");
1684
+ console.log(` ${import_chalk4.default.bold("Rules to apply:")}`);
1685
+ console.log(` ${ok} Max file size: ${import_chalk4.default.cyan(`${config.rules.maxFileLines} lines`)}`);
1686
+ const fileNaming = root?.conventions?.fileNaming ?? config.packages.find((p) => p.conventions?.fileNaming)?.conventions?.fileNaming;
1687
+ if (config.rules.enforceNaming && fileNaming) {
1688
+ console.log(` ${ok} File naming: ${import_chalk4.default.cyan(fileNaming)}`);
1689
+ } else {
1690
+ console.log(` ${off} File naming: ${import_chalk4.default.dim("not enforced")}`);
1691
+ }
1692
+ const testPattern = root?.structure?.testPattern ?? config.packages.find((p) => p.structure?.testPattern)?.structure?.testPattern;
1693
+ if (config.rules.enforceMissingTests && testPattern) {
1694
+ console.log(` ${ok} Missing tests: ${import_chalk4.default.cyan(`enforced (${testPattern})`)}`);
1695
+ } else if (config.rules.enforceMissingTests) {
1696
+ console.log(` ${ok} Missing tests: ${import_chalk4.default.cyan("enforced")}`);
1697
+ } else {
1698
+ console.log(` ${off} Missing tests: ${import_chalk4.default.dim("not enforced")}`);
1699
+ }
1700
+ if (config.rules.testCoverage > 0) {
1701
+ if (isMonorepo) {
1702
+ const withCoverage = config.packages.filter(
1703
+ (p) => (p.rules?.testCoverage ?? config.rules.testCoverage) > 0
1704
+ );
1705
+ console.log(
1706
+ ` ${ok} Coverage: ${import_chalk4.default.cyan(`${config.rules.testCoverage}%`)} default ${import_chalk4.default.dim(`(${withCoverage.length}/${config.packages.length} packages)`)}`
1707
+ );
1708
+ } else {
1709
+ console.log(` ${ok} Coverage: ${import_chalk4.default.cyan(`${config.rules.testCoverage}%`)}`);
1710
+ }
1711
+ } else {
1712
+ console.log(` ${off} Coverage: ${import_chalk4.default.dim("disabled")}`);
1713
+ }
1714
+ if (exemptedPackages.length > 0) {
1715
+ console.log(
1716
+ ` ${import_chalk4.default.dim(" exempted:")} ${import_chalk4.default.dim(exemptedPackages.join(", "))} ${import_chalk4.default.dim("(types-only)")}`
1717
+ );
1718
+ }
1719
+ if (isMonorepo) {
1720
+ console.log(
1721
+ `
1722
+ ${import_chalk4.default.dim(`${config.packages.length} packages scanned \xB7 warns on violation \xB7 use --enforce in CI`)}`
1723
+ );
1724
+ } else {
1725
+ console.log(`
1726
+ ${import_chalk4.default.dim("warns on violation \xB7 use --enforce in CI to block")}`);
1727
+ }
1728
+ console.log("");
1729
+ }
1679
1730
 
1680
1731
  // src/display-text.ts
1681
1732
  function plainConfidenceLabel(convention) {
@@ -1716,10 +1767,13 @@ function formatConventionsText(scanResult) {
1716
1767
  }
1717
1768
  function formatRulesText(config) {
1718
1769
  const root = config.packages.find((p) => p.path === ".") ?? config.packages[0];
1770
+ const isMonorepo = config.packages.length > 1;
1719
1771
  const lines = [];
1720
1772
  lines.push(`Max file size: ${config.rules.maxFileLines} lines`);
1721
1773
  if (config.rules.testCoverage > 0) {
1722
- lines.push(`Test coverage target: ${config.rules.testCoverage}%`);
1774
+ const label = isMonorepo ? "Default coverage target" : "Test coverage target";
1775
+ const suffix = isMonorepo ? " (per-package)" : "";
1776
+ lines.push(`${label}: ${config.rules.testCoverage}%${suffix}`);
1723
1777
  } else {
1724
1778
  lines.push("Test coverage target: disabled");
1725
1779
  }
@@ -2435,30 +2489,35 @@ var path14 = __toESM(require("path"), 1);
2435
2489
  var clack7 = __toESM(require("@clack/prompts"), 1);
2436
2490
  var import_chalk8 = __toESM(require("chalk"), 1);
2437
2491
  function checkCoveragePrereqs(projectRoot, scanResult) {
2438
- const testRunner = scanResult.stack.testRunner;
2439
- if (!testRunner) return [];
2440
- const runner = testRunner.name;
2441
2492
  const pm = scanResult.stack.packageManager.name;
2442
- if (runner === "vitest") {
2443
- const hasV8 = hasDependency(projectRoot, "@vitest/coverage-v8");
2444
- const hasIstanbul = hasDependency(projectRoot, "@vitest/coverage-istanbul");
2445
- const installed = hasV8 || hasIstanbul;
2446
- const addCmd = pm === "yarn" ? "yarn add -D" : pm === "npm" ? "npm install -D" : `${pm} add -D`;
2447
- return [
2448
- {
2449
- label: "@vitest/coverage-v8",
2450
- installed,
2451
- installCommand: installed ? void 0 : `${addCmd} @vitest/coverage-v8`,
2452
- reason: "Required for coverage percentage checks with vitest"
2453
- }
2454
- ];
2493
+ const vitestPackages = scanResult.packages.filter((pkg) => pkg.stack.testRunner?.name === "vitest").map((pkg) => pkg.relativePath);
2494
+ const hasVitest = vitestPackages.length > 0 || scanResult.stack.testRunner?.name === "vitest";
2495
+ if (!hasVitest) return [];
2496
+ let installed = hasDependency(projectRoot, "@vitest/coverage-v8") || hasDependency(projectRoot, "@vitest/coverage-istanbul");
2497
+ if (!installed && vitestPackages.length > 0) {
2498
+ installed = vitestPackages.every((rel) => {
2499
+ const pkgDir = path14.join(projectRoot, rel);
2500
+ return hasDependency(pkgDir, "@vitest/coverage-v8") || hasDependency(pkgDir, "@vitest/coverage-istanbul");
2501
+ });
2455
2502
  }
2456
- return [];
2503
+ const addCmd = pm === "yarn" ? "yarn add -D" : pm === "npm" ? "npm install -D" : `${pm} add -D`;
2504
+ const affectedPackages = vitestPackages.length > 1 ? vitestPackages : void 0;
2505
+ const reason = affectedPackages ? `Required for coverage in: ${affectedPackages.join(", ")}` : "Required for coverage percentage checks with vitest";
2506
+ return [
2507
+ {
2508
+ label: "@vitest/coverage-v8",
2509
+ installed,
2510
+ installCommand: installed ? void 0 : `${addCmd} @vitest/coverage-v8`,
2511
+ reason,
2512
+ affectedPackages
2513
+ }
2514
+ ];
2457
2515
  }
2458
2516
  function displayMissingPrereqs(prereqs) {
2459
2517
  const missing = prereqs.filter((p) => !p.installed);
2460
2518
  for (const m of missing) {
2461
- console.log(` ${import_chalk8.default.yellow("!")} ${m.label} not installed \u2014 ${m.reason}`);
2519
+ const suffix = m.affectedPackages ? ` \u2014 needed for coverage in: ${m.affectedPackages.join(", ")}` : ` \u2014 ${m.reason}`;
2520
+ console.log(` ${import_chalk8.default.yellow("!")} ${m.label} not installed${suffix}`);
2462
2521
  if (m.installCommand) {
2463
2522
  console.log(` Install: ${import_chalk8.default.cyan(m.installCommand)}`);
2464
2523
  }
@@ -2467,15 +2526,19 @@ function displayMissingPrereqs(prereqs) {
2467
2526
  async function promptMissingPrereqs(projectRoot, prereqs) {
2468
2527
  const missing = prereqs.filter((p) => !p.installed);
2469
2528
  if (missing.length === 0) return { disableCoverage: false };
2470
- const prereqLines = prereqs.map(
2471
- (p) => `${p.installed ? "\u2713" : "\u2717"} ${p.label}${p.installed ? "" : ` \u2014 ${p.reason}`}`
2472
- ).join("\n");
2529
+ const prereqLines = prereqs.map((p) => {
2530
+ if (p.installed) return `\u2713 ${p.label}`;
2531
+ const detail = p.affectedPackages ? `needed by: ${p.affectedPackages.join(", ")}` : p.reason;
2532
+ return `\u2717 ${p.label} \u2014 ${detail}`;
2533
+ }).join("\n");
2473
2534
  clack7.note(prereqLines, "Coverage prerequisites");
2474
2535
  let disableCoverage = false;
2475
2536
  for (const m of missing) {
2476
2537
  if (!m.installCommand) continue;
2538
+ const pkgCount = m.affectedPackages?.length;
2539
+ const message = pkgCount ? `${m.label} is not installed. Required for coverage in ${pkgCount} packages using vitest.` : `${m.label} is not installed. It is required for coverage percentage checks.`;
2477
2540
  const choice = await clack7.select({
2478
- message: `${m.label} is not installed. It is required for coverage percentage checks.`,
2541
+ message,
2479
2542
  options: [
2480
2543
  {
2481
2544
  value: "install",
@@ -2967,11 +3030,8 @@ async function initInteractive(projectRoot, configPath, options) {
2967
3030
  );
2968
3031
  }
2969
3032
  clack8.note(formatScanResultsText(scanResult), "Scan results");
2970
- const rulesLines = formatRulesText(config);
2971
3033
  const exemptedPkgs = getExemptedPackages(config);
2972
- if (exemptedPkgs.length > 0)
2973
- rulesLines.push(`Auto-exempted from coverage: ${exemptedPkgs.join(", ")} (types-only)`);
2974
- clack8.note(rulesLines.join("\n"), "Rules");
3034
+ displayInitSummary(config, exemptedPkgs);
2975
3035
  const decision = await promptInitDecision();
2976
3036
  if (decision === "customize") {
2977
3037
  const rootPkg = config.packages.find((p) => p.path === ".") ?? config.packages[0];
@@ -3153,7 +3213,7 @@ ${import_chalk12.default.bold("Synced:")}`);
3153
3213
  }
3154
3214
 
3155
3215
  // src/index.ts
3156
- var VERSION = "0.5.1";
3216
+ var VERSION = "0.5.3";
3157
3217
  var program = new import_commander.Command();
3158
3218
  program.name("viberails").description("Guardrails for vibe coding").version(VERSION);
3159
3219
  program.command("init", { isDefault: true }).description("Scan your project and set up enforcement guardrails").option("-y, --yes", "Non-interactive mode (use defaults, high-confidence only)").option("-f, --force", "Re-initialize, replacing existing config").action(async (options) => {