viberails 0.6.0 → 0.6.2

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.js CHANGED
@@ -3026,8 +3026,15 @@ function setupGithubAction(projectRoot, packageManager, options) {
3026
3026
  }
3027
3027
  }
3028
3028
  if (options?.linter) {
3029
- const lintCmd = options.linter === "biome" ? "biome check ." : "eslint .";
3030
- lines.push(` - run: ${runPrefix} ${lintCmd}`);
3029
+ const lintCmd = options.linter === "biome" ? "biome check" : "eslint";
3030
+ lines.push(
3031
+ ` - name: Lint changed files`,
3032
+ ` run: |`,
3033
+ ` FILES=$(git diff --name-only --diff-filter=ACMR origin/\${{ github.event.pull_request.base.ref }}...HEAD -- '*.js' '*.ts' '*.jsx' '*.tsx')`,
3034
+ ` if [ -n "$FILES" ]; then`,
3035
+ ` echo "$FILES" | xargs ${runPrefix} ${lintCmd}`,
3036
+ ` fi`
3037
+ );
3031
3038
  }
3032
3039
  lines.push(
3033
3040
  ` - run: npx viberails check --enforce --diff-base origin/\${{ github.event.pull_request.base.ref }}`,
@@ -3059,7 +3066,7 @@ import * as fs18 from "fs";
3059
3066
  import * as path18 from "path";
3060
3067
  import chalk11 from "chalk";
3061
3068
  import { parse as parseYaml2, stringify as stringifyYaml2 } from "yaml";
3062
- function addPreCommitStep(projectRoot, name, command, marker) {
3069
+ function addPreCommitStep(projectRoot, name, command, marker, lefthookExtra) {
3063
3070
  const lefthookPath = path18.join(projectRoot, "lefthook.yml");
3064
3071
  if (fs18.existsSync(lefthookPath)) {
3065
3072
  const content = fs18.readFileSync(lefthookPath, "utf-8");
@@ -3067,7 +3074,7 @@ function addPreCommitStep(projectRoot, name, command, marker) {
3067
3074
  const doc = parseYaml2(content) ?? {};
3068
3075
  if (!doc["pre-commit"]) doc["pre-commit"] = { commands: {} };
3069
3076
  if (!doc["pre-commit"].commands) doc["pre-commit"].commands = {};
3070
- doc["pre-commit"].commands[name] = { run: command };
3077
+ doc["pre-commit"].commands[name] = { run: command, ...lefthookExtra };
3071
3078
  fs18.writeFileSync(lefthookPath, stringifyYaml2(doc));
3072
3079
  return "lefthook.yml";
3073
3080
  }
@@ -3127,9 +3134,21 @@ function setupTypecheckHook(projectRoot, packageManager) {
3127
3134
  return target;
3128
3135
  }
3129
3136
  function setupLintHook(projectRoot, linter) {
3130
- const command = linter === "biome" ? "npx biome check ." : "npx eslint .";
3137
+ const isLefthook = fs18.existsSync(path18.join(projectRoot, "lefthook.yml"));
3131
3138
  const linterName = linter === "biome" ? "Biome" : "ESLint";
3132
- const target = addPreCommitStep(projectRoot, "lint", command, linter);
3139
+ let command;
3140
+ let lefthookExtra;
3141
+ if (isLefthook) {
3142
+ command = linter === "biome" ? "npx biome check {staged_files}" : "npx eslint {staged_files}";
3143
+ lefthookExtra = {
3144
+ glob: linter === "biome" ? "*.{js,ts,jsx,tsx,json,css}" : "*.{js,ts,jsx,tsx}"
3145
+ };
3146
+ } else {
3147
+ const exts = linter === "biome" ? "'*.js' '*.ts' '*.jsx' '*.tsx' '*.json' '*.css'" : "'*.js' '*.ts' '*.jsx' '*.tsx'";
3148
+ const lintCmd = linter === "biome" ? "biome check" : "eslint";
3149
+ command = `git diff --cached --name-only --diff-filter=ACMR -- ${exts} | xargs npx ${lintCmd}`;
3150
+ }
3151
+ const target = addPreCommitStep(projectRoot, "lint", command, linter, lefthookExtra);
3133
3152
  if (target) {
3134
3153
  console.log(` ${chalk11.green("\u2713")} ${target} \u2014 added ${linterName} lint check`);
3135
3154
  }
@@ -3191,9 +3210,11 @@ async function initCommand(options, cwd) {
3191
3210
  await initInteractive(projectRoot, configPath, options);
3192
3211
  }
3193
3212
  async function initNonInteractive(projectRoot, configPath) {
3194
- console.log(chalk12.dim("Scanning project..."));
3213
+ const s = clack8.spinner();
3214
+ s.start("Scanning project...");
3195
3215
  const scanResult = await scan2(projectRoot);
3196
3216
  const config = generateConfig(scanResult);
3217
+ s.stop("Scan complete");
3197
3218
  for (const pkg of config.packages) {
3198
3219
  const pkgMeta = config._meta?.packages?.[pkg.path]?.conventions;
3199
3220
  pkg.conventions = filterHighConfidence(pkg.conventions ?? {}, pkgMeta);
@@ -3208,7 +3229,8 @@ async function initNonInteractive(projectRoot, configPath) {
3208
3229
  );
3209
3230
  }
3210
3231
  if (config.packages.length > 1) {
3211
- console.log(chalk12.dim("Building import graph..."));
3232
+ const bs = clack8.spinner();
3233
+ bs.start("Building import graph...");
3212
3234
  const { buildImportGraph, inferBoundaries } = await import("@viberails/graph");
3213
3235
  const packages = resolveWorkspacePackages(projectRoot, config.packages);
3214
3236
  const graph = await buildImportGraph(projectRoot, { packages, ignore: config.ignore });
@@ -3217,7 +3239,9 @@ async function initNonInteractive(projectRoot, configPath) {
3217
3239
  if (denyCount > 0) {
3218
3240
  config.boundaries = inferred;
3219
3241
  config.rules.enforceBoundaries = true;
3220
- console.log(` Inferred ${denyCount} boundary rules`);
3242
+ bs.stop(`Inferred ${denyCount} boundary rules`);
3243
+ } else {
3244
+ bs.stop("No boundary rules inferred");
3221
3245
  }
3222
3246
  }
3223
3247
  const compacted = compactConfig3(config);
@@ -3339,19 +3363,22 @@ async function initInteractive(projectRoot, configPath, options) {
3339
3363
  clack8.outro("Aborted. No files were written.");
3340
3364
  return;
3341
3365
  }
3366
+ const ws = clack8.spinner();
3367
+ ws.start("Writing configuration and setting up integrations...");
3342
3368
  const compacted = compactConfig3(config);
3343
3369
  fs19.writeFileSync(configPath, `${JSON.stringify(compacted, null, 2)}
3344
3370
  `);
3345
3371
  writeGeneratedFiles(projectRoot, config, scanResult);
3346
3372
  updateGitignore(projectRoot);
3347
- const ok = chalk12.green("\u2713");
3348
- clack8.log.step(`${ok} ${path19.basename(configPath)}`);
3349
- clack8.log.step(`${ok} .viberails/context.md`);
3350
- clack8.log.step(`${ok} .viberails/scan-result.json`);
3351
3373
  setupSelectedIntegrations(projectRoot, integrations, {
3352
3374
  linter: rootPkgStack?.linter?.split("@")[0],
3353
3375
  packageManager: rootPkgStack?.packageManager?.split("@")[0]
3354
3376
  });
3377
+ ws.stop("Configuration written");
3378
+ const ok = chalk12.green("\u2713");
3379
+ clack8.log.step(`${ok} ${path19.basename(configPath)}`);
3380
+ clack8.log.step(`${ok} .viberails/context.md`);
3381
+ clack8.log.step(`${ok} .viberails/scan-result.json`);
3355
3382
  clack8.outro(
3356
3383
  `Done! Next: review viberails.config.json, then run viberails check
3357
3384
  ${chalk12.dim("Tip: use")} ${chalk12.cyan("viberails check --enforce")} ${chalk12.dim("in CI to block PRs on violations.")}`
@@ -3390,8 +3417,10 @@ async function syncCommand(options, cwd) {
3390
3417
  const configPath = path20.join(projectRoot, CONFIG_FILE6);
3391
3418
  const existing = await loadConfig5(configPath);
3392
3419
  const previousStats = loadPreviousStats(projectRoot);
3393
- console.log(chalk13.dim("Scanning project..."));
3420
+ const s = clack9.spinner();
3421
+ s.start("Scanning project...");
3394
3422
  const scanResult = await scan3(projectRoot);
3423
+ s.stop("Scan complete");
3395
3424
  const merged = mergeConfig2(existing, scanResult);
3396
3425
  const compacted = compactConfig4(merged);
3397
3426
  const compactedJson = JSON.stringify(compacted, null, 2);
@@ -3465,7 +3494,7 @@ ${chalk13.bold("Synced:")}`);
3465
3494
  }
3466
3495
 
3467
3496
  // src/index.ts
3468
- var VERSION = "0.6.0";
3497
+ var VERSION = "0.6.2";
3469
3498
  var program = new Command();
3470
3499
  program.name("viberails").description("Guardrails for vibe coding").version(VERSION);
3471
3500
  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) => {