viberails 0.2.0 → 0.2.1

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
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/index.ts
4
- import chalk7 from "chalk";
4
+ import chalk10 from "chalk";
5
5
  import { Command } from "commander";
6
6
 
7
7
  // src/commands/boundaries.ts
@@ -499,12 +499,56 @@ ${violations.length} ${word} found.`);
499
499
  }
500
500
 
501
501
  // src/commands/fix.ts
502
- import { execSync as execSync2 } from "child_process";
503
502
  import * as fs9 from "fs";
504
503
  import * as path10 from "path";
505
- import { createInterface as createInterface2 } from "readline";
506
504
  import { loadConfig as loadConfig3 } from "@viberails/config";
505
+ import chalk4 from "chalk";
506
+
507
+ // src/commands/fix-helpers.ts
508
+ import { execSync as execSync2 } from "child_process";
509
+ import { createInterface as createInterface2 } from "readline";
507
510
  import chalk3 from "chalk";
511
+ function printPlan(renames, stubs) {
512
+ if (renames.length > 0) {
513
+ console.log(chalk3.bold("\nFile renames:"));
514
+ for (const r of renames) {
515
+ console.log(` ${chalk3.red(r.oldPath)} \u2192 ${chalk3.green(r.newPath)}`);
516
+ }
517
+ }
518
+ if (stubs.length > 0) {
519
+ console.log(chalk3.bold("\nTest stubs to create:"));
520
+ for (const s of stubs) {
521
+ console.log(` ${chalk3.green("+")} ${s.path}`);
522
+ }
523
+ }
524
+ }
525
+ function checkGitDirty(projectRoot) {
526
+ try {
527
+ const output = execSync2("git status --porcelain", {
528
+ cwd: projectRoot,
529
+ encoding: "utf-8"
530
+ });
531
+ return output.trim().length > 0;
532
+ } catch {
533
+ return false;
534
+ }
535
+ }
536
+ function getConventionValue(convention) {
537
+ if (typeof convention === "string") return convention;
538
+ if (convention && typeof convention === "object" && "value" in convention) {
539
+ return convention.value;
540
+ }
541
+ return void 0;
542
+ }
543
+ function promptConfirm(question) {
544
+ const rl = createInterface2({ input: process.stdin, output: process.stdout });
545
+ return new Promise((resolve4) => {
546
+ rl.question(`${question} (y/N) `, (answer) => {
547
+ rl.close();
548
+ resolve4(answer.toLowerCase() === "y" || answer.toLowerCase() === "yes");
549
+ });
550
+ });
551
+ }
508
552
 
509
553
  // src/commands/fix-imports.ts
510
554
  import * as path7 from "path";
@@ -724,13 +768,13 @@ async function fixCommand(options, cwd) {
724
768
  const startDir = cwd ?? process.cwd();
725
769
  const projectRoot = findProjectRoot(startDir);
726
770
  if (!projectRoot) {
727
- console.error(`${chalk3.red("Error:")} No package.json found. Are you in a JS/TS project?`);
771
+ console.error(`${chalk4.red("Error:")} No package.json found. Are you in a JS/TS project?`);
728
772
  return 1;
729
773
  }
730
774
  const configPath = path10.join(projectRoot, CONFIG_FILE3);
731
775
  if (!fs9.existsSync(configPath)) {
732
776
  console.error(
733
- `${chalk3.red("Error:")} No viberails.config.json found. Run \`viberails init\` first.`
777
+ `${chalk4.red("Error:")} No viberails.config.json found. Run \`viberails init\` first.`
734
778
  );
735
779
  return 1;
736
780
  }
@@ -739,7 +783,7 @@ async function fixCommand(options, cwd) {
739
783
  const isDirty = checkGitDirty(projectRoot);
740
784
  if (isDirty) {
741
785
  console.log(
742
- chalk3.yellow("Warning: You have uncommitted changes. Consider committing first.")
786
+ chalk4.yellow("Warning: You have uncommitted changes. Consider committing first.")
743
787
  );
744
788
  }
745
789
  }
@@ -769,12 +813,12 @@ async function fixCommand(options, cwd) {
769
813
  }
770
814
  }
771
815
  if (dedupedRenames.length === 0 && testStubs.length === 0) {
772
- console.log(`${chalk3.green("\u2713")} No fixable violations found.`);
816
+ console.log(`${chalk4.green("\u2713")} No fixable violations found.`);
773
817
  return 0;
774
818
  }
775
819
  printPlan(dedupedRenames, testStubs);
776
820
  if (options.dryRun) {
777
- console.log(chalk3.dim("\nDry run \u2014 no changes applied."));
821
+ console.log(chalk4.dim("\nDry run \u2014 no changes applied."));
778
822
  return 0;
779
823
  }
780
824
  if (!options.yes) {
@@ -805,87 +849,82 @@ async function fixCommand(options, cwd) {
805
849
  }
806
850
  console.log("");
807
851
  if (renameCount > 0) {
808
- console.log(`${chalk3.green("\u2713")} Renamed ${renameCount} file${renameCount > 1 ? "s" : ""}`);
852
+ console.log(`${chalk4.green("\u2713")} Renamed ${renameCount} file${renameCount > 1 ? "s" : ""}`);
809
853
  }
810
854
  if (importUpdateCount > 0) {
811
855
  console.log(
812
- `${chalk3.green("\u2713")} Updated ${importUpdateCount} import${importUpdateCount > 1 ? "s" : ""}`
856
+ `${chalk4.green("\u2713")} Updated ${importUpdateCount} import${importUpdateCount > 1 ? "s" : ""}`
813
857
  );
814
858
  }
815
859
  if (stubCount > 0) {
816
- console.log(`${chalk3.green("\u2713")} Generated ${stubCount} test stub${stubCount > 1 ? "s" : ""}`);
860
+ console.log(`${chalk4.green("\u2713")} Generated ${stubCount} test stub${stubCount > 1 ? "s" : ""}`);
817
861
  }
818
862
  return 0;
819
863
  }
820
- function printPlan(renames, stubs) {
821
- if (renames.length > 0) {
822
- console.log(chalk3.bold("\nFile renames:"));
823
- for (const r of renames) {
824
- console.log(` ${chalk3.red(r.oldPath)} \u2192 ${chalk3.green(r.newPath)}`);
825
- }
826
- }
827
- if (stubs.length > 0) {
828
- console.log(chalk3.bold("\nTest stubs to create:"));
829
- for (const s of stubs) {
830
- console.log(` ${chalk3.green("+")} ${s.path}`);
864
+
865
+ // src/commands/init.ts
866
+ import * as fs12 from "fs";
867
+ import * as path13 from "path";
868
+ import { generateConfig } from "@viberails/config";
869
+ import { scan } from "@viberails/scanner";
870
+ import chalk8 from "chalk";
871
+
872
+ // src/display.ts
873
+ import { FRAMEWORK_NAMES as FRAMEWORK_NAMES2, LIBRARY_NAMES, STYLING_NAMES as STYLING_NAMES2 } from "@viberails/types";
874
+ import chalk6 from "chalk";
875
+
876
+ // src/display-helpers.ts
877
+ import { ROLE_DESCRIPTIONS } from "@viberails/types";
878
+ function groupByRole(directories) {
879
+ const map = /* @__PURE__ */ new Map();
880
+ for (const dir of directories) {
881
+ if (dir.role === "unknown") continue;
882
+ const existing = map.get(dir.role);
883
+ if (existing) {
884
+ existing.dirs.push(dir);
885
+ } else {
886
+ map.set(dir.role, { dirs: [dir] });
831
887
  }
832
888
  }
833
- }
834
- function checkGitDirty(projectRoot) {
835
- try {
836
- const output = execSync2("git status --porcelain", {
837
- cwd: projectRoot,
838
- encoding: "utf-8"
889
+ const groups = [];
890
+ for (const [role, { dirs }] of map) {
891
+ const label = ROLE_DESCRIPTIONS[role] ?? role;
892
+ const totalFiles = dirs.reduce((sum, d) => sum + d.fileCount, 0);
893
+ groups.push({
894
+ role,
895
+ label,
896
+ dirCount: dirs.length,
897
+ totalFiles,
898
+ singlePath: dirs.length === 1 ? dirs[0].path : void 0
839
899
  });
840
- return output.trim().length > 0;
841
- } catch {
842
- return false;
843
900
  }
901
+ return groups;
844
902
  }
845
- function getConventionValue(convention) {
846
- if (typeof convention === "string") return convention;
847
- if (convention && typeof convention === "object" && "value" in convention) {
848
- return convention.value;
903
+ function formatSummary(stats, packageCount) {
904
+ const parts = [];
905
+ if (packageCount && packageCount > 1) {
906
+ parts.push(`${packageCount} packages`);
849
907
  }
850
- return void 0;
851
- }
852
- function promptConfirm(question) {
853
- const rl = createInterface2({ input: process.stdin, output: process.stdout });
854
- return new Promise((resolve4) => {
855
- rl.question(`${question} (y/N) `, (answer) => {
856
- rl.close();
857
- resolve4(answer.toLowerCase() === "y" || answer.toLowerCase() === "yes");
858
- });
859
- });
908
+ parts.push(`${stats.totalFiles.toLocaleString()} source files`);
909
+ parts.push(`${stats.totalLines.toLocaleString()} lines`);
910
+ parts.push(`avg ${Math.round(stats.averageFileLines)} lines/file`);
911
+ return parts.join(" \xB7 ");
860
912
  }
861
-
862
- // src/commands/init.ts
863
- import * as fs11 from "fs";
864
- import * as path12 from "path";
865
- import { generateConfig } from "@viberails/config";
866
- import { scan } from "@viberails/scanner";
867
- import chalk5 from "chalk";
868
-
869
- // src/display.ts
870
- import { FRAMEWORK_NAMES, LIBRARY_NAMES, ROLE_DESCRIPTIONS, STYLING_NAMES } from "@viberails/types";
871
- import chalk4 from "chalk";
872
- var CONVENTION_LABELS = {
873
- fileNaming: "File naming",
874
- componentNaming: "Component naming",
875
- hookNaming: "Hook naming",
876
- importAlias: "Import alias"
877
- };
878
- function formatItem(item, nameMap) {
879
- const name = nameMap?.[item.name] ?? item.name;
880
- return item.version ? `${name} ${item.version}` : name;
913
+ function formatExtensions(filesByExtension, maxEntries = 4) {
914
+ return Object.entries(filesByExtension).sort(([, a], [, b]) => b - a).slice(0, maxEntries).map(([ext, count]) => `${ext} ${count}`).join(" \xB7 ");
881
915
  }
882
- function confidenceLabel(convention) {
883
- const pct = Math.round(convention.consistency);
884
- if (convention.confidence === "high") {
885
- return `${pct}% \u2014 high confidence, will enforce`;
916
+ function formatRoleGroup(group) {
917
+ const files = group.totalFiles === 1 ? "1 file" : `${group.totalFiles} files`;
918
+ if (group.singlePath) {
919
+ return `${group.label} \u2014 ${group.singlePath} (${files})`;
886
920
  }
887
- return `${pct}% \u2014 medium confidence, suggested only`;
921
+ const dirs = group.dirCount === 1 ? "1 dir" : `${group.dirCount} dirs`;
922
+ return `${group.label} \u2014 ${dirs} (${files})`;
888
923
  }
924
+
925
+ // src/display-monorepo.ts
926
+ import { FRAMEWORK_NAMES, STYLING_NAMES } from "@viberails/types";
927
+ import chalk5 from "chalk";
889
928
  function formatPackageSummary(pkg) {
890
929
  const parts = [];
891
930
  if (pkg.stack.framework) {
@@ -901,16 +940,19 @@ function formatPackageSummary(pkg) {
901
940
  function displayMonorepoResults(scanResult) {
902
941
  const { stack, packages } = scanResult;
903
942
  console.log(`
904
- ${chalk4.bold(`Detected: (monorepo, ${packages.length} packages)`)}`);
905
- console.log(` ${chalk4.green("\u2713")} ${formatItem(stack.language)}`);
943
+ ${chalk5.bold(`Detected: (monorepo, ${packages.length} packages)`)}`);
944
+ console.log(` ${chalk5.green("\u2713")} ${formatItem(stack.language)}`);
906
945
  if (stack.packageManager) {
907
- console.log(` ${chalk4.green("\u2713")} ${formatItem(stack.packageManager)}`);
946
+ console.log(` ${chalk5.green("\u2713")} ${formatItem(stack.packageManager)}`);
908
947
  }
909
948
  if (stack.linter) {
910
- console.log(` ${chalk4.green("\u2713")} ${formatItem(stack.linter)}`);
949
+ console.log(` ${chalk5.green("\u2713")} ${formatItem(stack.linter)}`);
950
+ }
951
+ if (stack.formatter) {
952
+ console.log(` ${chalk5.green("\u2713")} ${formatItem(stack.formatter)}`);
911
953
  }
912
954
  if (stack.testRunner) {
913
- console.log(` ${chalk4.green("\u2713")} ${formatItem(stack.testRunner)}`);
955
+ console.log(` ${chalk5.green("\u2713")} ${formatItem(stack.testRunner)}`);
914
956
  }
915
957
  console.log("");
916
958
  for (const pkg of packages) {
@@ -921,96 +963,123 @@ ${chalk4.bold(`Detected: (monorepo, ${packages.length} packages)`)}`);
921
963
  );
922
964
  if (packagesWithDirs.length > 0) {
923
965
  console.log(`
924
- ${chalk4.bold("Structure:")}`);
966
+ ${chalk5.bold("Structure:")}`);
925
967
  for (const pkg of packagesWithDirs) {
926
- const meaningfulDirs = pkg.structure.directories.filter((d) => d.role !== "unknown");
927
- if (meaningfulDirs.length === 0) continue;
968
+ const groups = groupByRole(pkg.structure.directories);
969
+ if (groups.length === 0) continue;
928
970
  console.log(` ${pkg.relativePath}:`);
929
- for (const dir of meaningfulDirs) {
930
- const label = ROLE_DESCRIPTIONS[dir.role] ?? dir.role;
931
- const files = dir.fileCount === 1 ? "1 file" : `${dir.fileCount} files`;
932
- console.log(` ${chalk4.green("\u2713")} ${dir.path} \u2014 ${label} (${files})`);
971
+ for (const group of groups) {
972
+ console.log(` ${chalk5.green("\u2713")} ${formatRoleGroup(group)}`);
933
973
  }
934
974
  }
935
975
  }
976
+ displayConventions(scanResult);
977
+ displaySummarySection(scanResult);
978
+ console.log("");
979
+ }
980
+
981
+ // src/display.ts
982
+ var CONVENTION_LABELS = {
983
+ fileNaming: "File naming",
984
+ componentNaming: "Component naming",
985
+ hookNaming: "Hook naming",
986
+ importAlias: "Import alias"
987
+ };
988
+ function formatItem(item, nameMap) {
989
+ const name = nameMap?.[item.name] ?? item.name;
990
+ return item.version ? `${name} ${item.version}` : name;
991
+ }
992
+ function confidenceLabel(convention) {
993
+ const pct = Math.round(convention.consistency);
994
+ if (convention.confidence === "high") {
995
+ return `${pct}% \u2014 high confidence, will enforce`;
996
+ }
997
+ return `${pct}% \u2014 medium confidence, suggested only`;
998
+ }
999
+ function displayConventions(scanResult) {
936
1000
  const conventionEntries = Object.entries(scanResult.conventions);
937
- if (conventionEntries.length > 0) {
938
- console.log(`
939
- ${chalk4.bold("Conventions:")}`);
940
- for (const [key, convention] of conventionEntries) {
941
- if (convention.confidence === "low") continue;
942
- const label = CONVENTION_LABELS[key] ?? key;
943
- const pkgValues = packages.filter((pkg) => pkg.conventions[key] && pkg.conventions[key].confidence !== "low").map((pkg) => ({ relativePath: pkg.relativePath, convention: pkg.conventions[key] }));
1001
+ if (conventionEntries.length === 0) return;
1002
+ console.log(`
1003
+ ${chalk6.bold("Conventions:")}`);
1004
+ for (const [key, convention] of conventionEntries) {
1005
+ if (convention.confidence === "low") continue;
1006
+ const label = CONVENTION_LABELS[key] ?? key;
1007
+ if (scanResult.packages.length > 1) {
1008
+ const pkgValues = scanResult.packages.filter((pkg) => pkg.conventions[key] && pkg.conventions[key].confidence !== "low").map((pkg) => ({ relativePath: pkg.relativePath, convention: pkg.conventions[key] }));
944
1009
  const allSame = pkgValues.every((pv) => pv.convention.value === convention.value);
945
1010
  if (allSame || pkgValues.length <= 1) {
946
- const ind = convention.confidence === "high" ? chalk4.green("\u2713") : chalk4.yellow("~");
947
- const detail = chalk4.dim(`(${confidenceLabel(convention)})`);
1011
+ const ind = convention.confidence === "high" ? chalk6.green("\u2713") : chalk6.yellow("~");
1012
+ const detail = chalk6.dim(`(${confidenceLabel(convention)})`);
948
1013
  console.log(` ${ind} ${label}: ${convention.value} ${detail}`);
949
1014
  } else {
950
- console.log(` ${chalk4.yellow("~")} ${label}: varies by package`);
1015
+ console.log(` ${chalk6.yellow("~")} ${label}: varies by package`);
951
1016
  for (const pv of pkgValues) {
952
1017
  const pct = Math.round(pv.convention.consistency);
953
1018
  console.log(` ${pv.relativePath}: ${pv.convention.value} (${pct}%)`);
954
1019
  }
955
1020
  }
1021
+ } else {
1022
+ const ind = convention.confidence === "high" ? chalk6.green("\u2713") : chalk6.yellow("~");
1023
+ const detail = chalk6.dim(`(${confidenceLabel(convention)})`);
1024
+ console.log(` ${ind} ${label}: ${convention.value} ${detail}`);
956
1025
  }
957
1026
  }
958
- console.log("");
1027
+ }
1028
+ function displaySummarySection(scanResult) {
1029
+ const pkgCount = scanResult.packages.length > 1 ? scanResult.packages.length : void 0;
1030
+ console.log(`
1031
+ ${chalk6.bold("Summary:")}`);
1032
+ console.log(` ${formatSummary(scanResult.statistics, pkgCount)}`);
1033
+ const ext = formatExtensions(scanResult.statistics.filesByExtension);
1034
+ if (ext) {
1035
+ console.log(` ${ext}`);
1036
+ }
959
1037
  }
960
1038
  function displayScanResults(scanResult) {
961
1039
  if (scanResult.packages.length > 1) {
962
1040
  displayMonorepoResults(scanResult);
963
1041
  return;
964
1042
  }
965
- const { stack, conventions } = scanResult;
1043
+ const { stack } = scanResult;
966
1044
  console.log(`
967
- ${chalk4.bold("Detected:")}`);
1045
+ ${chalk6.bold("Detected:")}`);
968
1046
  if (stack.framework) {
969
- console.log(` ${chalk4.green("\u2713")} ${formatItem(stack.framework, FRAMEWORK_NAMES)}`);
1047
+ console.log(` ${chalk6.green("\u2713")} ${formatItem(stack.framework, FRAMEWORK_NAMES2)}`);
970
1048
  }
971
- console.log(` ${chalk4.green("\u2713")} ${formatItem(stack.language)}`);
1049
+ console.log(` ${chalk6.green("\u2713")} ${formatItem(stack.language)}`);
972
1050
  if (stack.styling) {
973
- console.log(` ${chalk4.green("\u2713")} ${formatItem(stack.styling, STYLING_NAMES)}`);
1051
+ console.log(` ${chalk6.green("\u2713")} ${formatItem(stack.styling, STYLING_NAMES2)}`);
974
1052
  }
975
1053
  if (stack.backend) {
976
- console.log(` ${chalk4.green("\u2713")} ${formatItem(stack.backend, FRAMEWORK_NAMES)}`);
1054
+ console.log(` ${chalk6.green("\u2713")} ${formatItem(stack.backend, FRAMEWORK_NAMES2)}`);
977
1055
  }
978
1056
  if (stack.linter) {
979
- console.log(` ${chalk4.green("\u2713")} ${formatItem(stack.linter)}`);
1057
+ console.log(` ${chalk6.green("\u2713")} ${formatItem(stack.linter)}`);
1058
+ }
1059
+ if (stack.formatter) {
1060
+ console.log(` ${chalk6.green("\u2713")} ${formatItem(stack.formatter)}`);
980
1061
  }
981
1062
  if (stack.testRunner) {
982
- console.log(` ${chalk4.green("\u2713")} ${formatItem(stack.testRunner)}`);
1063
+ console.log(` ${chalk6.green("\u2713")} ${formatItem(stack.testRunner)}`);
983
1064
  }
984
1065
  if (stack.packageManager) {
985
- console.log(` ${chalk4.green("\u2713")} ${formatItem(stack.packageManager)}`);
1066
+ console.log(` ${chalk6.green("\u2713")} ${formatItem(stack.packageManager)}`);
986
1067
  }
987
1068
  if (stack.libraries.length > 0) {
988
1069
  for (const lib of stack.libraries) {
989
- console.log(` ${chalk4.green("\u2713")} ${formatItem(lib, LIBRARY_NAMES)}`);
990
- }
991
- }
992
- const meaningfulDirs = scanResult.structure.directories.filter((d) => d.role !== "unknown");
993
- if (meaningfulDirs.length > 0) {
994
- console.log(`
995
- ${chalk4.bold("Structure:")}`);
996
- for (const dir of meaningfulDirs) {
997
- const label = ROLE_DESCRIPTIONS[dir.role] ?? dir.role;
998
- const files = dir.fileCount === 1 ? "1 file" : `${dir.fileCount} files`;
999
- console.log(` ${chalk4.green("\u2713")} ${dir.path} \u2014 ${label} (${files})`);
1070
+ console.log(` ${chalk6.green("\u2713")} ${formatItem(lib, LIBRARY_NAMES)}`);
1000
1071
  }
1001
1072
  }
1002
- const conventionEntries = Object.entries(conventions);
1003
- if (conventionEntries.length > 0) {
1073
+ const groups = groupByRole(scanResult.structure.directories);
1074
+ if (groups.length > 0) {
1004
1075
  console.log(`
1005
- ${chalk4.bold("Conventions:")}`);
1006
- for (const [key, convention] of conventionEntries) {
1007
- if (convention.confidence === "low") continue;
1008
- const label = CONVENTION_LABELS[key] ?? key;
1009
- const ind = convention.confidence === "high" ? chalk4.green("\u2713") : chalk4.yellow("~");
1010
- const detail = chalk4.dim(`(${confidenceLabel(convention)})`);
1011
- console.log(` ${ind} ${label}: ${convention.value} ${detail}`);
1076
+ ${chalk6.bold("Structure:")}`);
1077
+ for (const group of groups) {
1078
+ console.log(` ${chalk6.green("\u2713")} ${formatRoleGroup(group)}`);
1012
1079
  }
1013
1080
  }
1081
+ displayConventions(scanResult);
1082
+ displaySummarySection(scanResult);
1014
1083
  console.log("");
1015
1084
  }
1016
1085
 
@@ -1040,6 +1109,79 @@ function writeGeneratedFiles(projectRoot, config, scanResult) {
1040
1109
  }
1041
1110
  }
1042
1111
 
1112
+ // src/commands/init-hooks.ts
1113
+ import * as fs11 from "fs";
1114
+ import * as path12 from "path";
1115
+ import chalk7 from "chalk";
1116
+ function setupPreCommitHook(projectRoot) {
1117
+ const lefthookPath = path12.join(projectRoot, "lefthook.yml");
1118
+ if (fs11.existsSync(lefthookPath)) {
1119
+ addLefthookPreCommit(lefthookPath);
1120
+ console.log(` ${chalk7.green("\u2713")} lefthook.yml \u2014 added viberails pre-commit`);
1121
+ return;
1122
+ }
1123
+ const huskyDir = path12.join(projectRoot, ".husky");
1124
+ if (fs11.existsSync(huskyDir)) {
1125
+ writeHuskyPreCommit(huskyDir);
1126
+ console.log(` ${chalk7.green("\u2713")} .husky/pre-commit \u2014 added viberails check`);
1127
+ return;
1128
+ }
1129
+ const gitDir = path12.join(projectRoot, ".git");
1130
+ if (fs11.existsSync(gitDir)) {
1131
+ const hooksDir = path12.join(gitDir, "hooks");
1132
+ if (!fs11.existsSync(hooksDir)) {
1133
+ fs11.mkdirSync(hooksDir, { recursive: true });
1134
+ }
1135
+ writeGitHookPreCommit(hooksDir);
1136
+ console.log(` ${chalk7.green("\u2713")} .git/hooks/pre-commit`);
1137
+ }
1138
+ }
1139
+ function writeGitHookPreCommit(hooksDir) {
1140
+ const hookPath = path12.join(hooksDir, "pre-commit");
1141
+ if (fs11.existsSync(hookPath)) {
1142
+ const existing = fs11.readFileSync(hookPath, "utf-8");
1143
+ if (existing.includes("viberails")) return;
1144
+ fs11.writeFileSync(
1145
+ hookPath,
1146
+ `${existing.trimEnd()}
1147
+
1148
+ # viberails check
1149
+ npx viberails check --staged
1150
+ `
1151
+ );
1152
+ return;
1153
+ }
1154
+ const script = [
1155
+ "#!/bin/sh",
1156
+ "# Generated by viberails \u2014 https://viberails.sh",
1157
+ "",
1158
+ "npx viberails check --staged",
1159
+ ""
1160
+ ].join("\n");
1161
+ fs11.writeFileSync(hookPath, script, { mode: 493 });
1162
+ }
1163
+ function addLefthookPreCommit(lefthookPath) {
1164
+ const content = fs11.readFileSync(lefthookPath, "utf-8");
1165
+ if (content.includes("viberails")) return;
1166
+ const addition = ["", " viberails:", " run: npx viberails check --staged"].join("\n");
1167
+ fs11.writeFileSync(lefthookPath, `${content.trimEnd()}
1168
+ ${addition}
1169
+ `);
1170
+ }
1171
+ function writeHuskyPreCommit(huskyDir) {
1172
+ const hookPath = path12.join(huskyDir, "pre-commit");
1173
+ if (fs11.existsSync(hookPath)) {
1174
+ const existing = fs11.readFileSync(hookPath, "utf-8");
1175
+ if (!existing.includes("viberails")) {
1176
+ fs11.writeFileSync(hookPath, `${existing.trimEnd()}
1177
+ npx viberails check --staged
1178
+ `);
1179
+ }
1180
+ return;
1181
+ }
1182
+ fs11.writeFileSync(hookPath, "#!/bin/sh\nnpx viberails check --staged\n", { mode: 493 });
1183
+ }
1184
+
1043
1185
  // src/commands/init.ts
1044
1186
  var CONFIG_FILE4 = "viberails.config.json";
1045
1187
  function filterHighConfidence(conventions) {
@@ -1062,19 +1204,19 @@ async function initCommand(options, cwd) {
1062
1204
  "No package.json found in this directory or any parent.\n\nMake sure you are inside a JavaScript or TypeScript project, then run:\n npx viberails"
1063
1205
  );
1064
1206
  }
1065
- const configPath = path12.join(projectRoot, CONFIG_FILE4);
1066
- if (fs11.existsSync(configPath)) {
1207
+ const configPath = path13.join(projectRoot, CONFIG_FILE4);
1208
+ if (fs12.existsSync(configPath)) {
1067
1209
  console.log(
1068
- chalk5.yellow("!") + " viberails is already initialized in this project.\n Run " + chalk5.cyan("viberails sync") + " to update the generated files."
1210
+ chalk8.yellow("!") + " viberails is already initialized in this project.\n Run " + chalk8.cyan("viberails sync") + " to update the generated files."
1069
1211
  );
1070
1212
  return;
1071
1213
  }
1072
- console.log(chalk5.dim("Scanning project..."));
1214
+ console.log(chalk8.dim("Scanning project..."));
1073
1215
  const scanResult = await scan(projectRoot);
1074
1216
  displayScanResults(scanResult);
1075
1217
  if (scanResult.statistics.totalFiles === 0) {
1076
1218
  console.log(
1077
- chalk5.yellow("!") + " No source files detected. viberails will generate context with minimal content.\n Run " + chalk5.cyan("viberails sync") + " after adding source files.\n"
1219
+ chalk8.yellow("!") + " No source files detected. viberails will generate context with minimal content.\n Run " + chalk8.cyan("viberails sync") + " after adding source files.\n"
1078
1220
  );
1079
1221
  }
1080
1222
  if (!options.yes) {
@@ -1094,7 +1236,7 @@ async function initCommand(options, cwd) {
1094
1236
  shouldInfer = await confirm("Infer boundary rules from import patterns?");
1095
1237
  }
1096
1238
  if (shouldInfer) {
1097
- console.log(chalk5.dim("Building import graph..."));
1239
+ console.log(chalk8.dim("Building import graph..."));
1098
1240
  const { buildImportGraph, inferBoundaries } = await import("@viberails/graph");
1099
1241
  const packages = resolveWorkspacePackages(projectRoot, config.workspace);
1100
1242
  const graph = await buildImportGraph(projectRoot, { packages, ignore: config.ignore });
@@ -1102,115 +1244,47 @@ async function initCommand(options, cwd) {
1102
1244
  if (inferred.length > 0) {
1103
1245
  config.boundaries = inferred;
1104
1246
  config.rules.enforceBoundaries = true;
1105
- console.log(` ${chalk5.green("\u2713")} Inferred ${inferred.length} boundary rules`);
1247
+ console.log(` ${chalk8.green("\u2713")} Inferred ${inferred.length} boundary rules`);
1106
1248
  }
1107
1249
  }
1108
1250
  }
1109
- fs11.writeFileSync(configPath, `${JSON.stringify(config, null, 2)}
1251
+ fs12.writeFileSync(configPath, `${JSON.stringify(config, null, 2)}
1110
1252
  `);
1111
1253
  writeGeneratedFiles(projectRoot, config, scanResult);
1112
1254
  updateGitignore(projectRoot);
1113
1255
  setupPreCommitHook(projectRoot);
1114
1256
  console.log(`
1115
- ${chalk5.bold("Created:")}`);
1116
- console.log(` ${chalk5.green("\u2713")} ${CONFIG_FILE4}`);
1117
- console.log(` ${chalk5.green("\u2713")} .viberails/context.md`);
1118
- console.log(` ${chalk5.green("\u2713")} .viberails/scan-result.json`);
1257
+ ${chalk8.bold("Created:")}`);
1258
+ console.log(` ${chalk8.green("\u2713")} ${CONFIG_FILE4}`);
1259
+ console.log(` ${chalk8.green("\u2713")} .viberails/context.md`);
1260
+ console.log(` ${chalk8.green("\u2713")} .viberails/scan-result.json`);
1119
1261
  console.log(`
1120
- ${chalk5.bold("Next steps:")}`);
1121
- console.log(` 1. Review ${chalk5.cyan("viberails.config.json")} and adjust rules`);
1262
+ ${chalk8.bold("Next steps:")}`);
1263
+ console.log(` 1. Review ${chalk8.cyan("viberails.config.json")} and adjust rules`);
1122
1264
  console.log(
1123
- ` 2. Commit ${chalk5.cyan("viberails.config.json")} and ${chalk5.cyan(".viberails/context.md")}`
1265
+ ` 2. Commit ${chalk8.cyan("viberails.config.json")} and ${chalk8.cyan(".viberails/context.md")}`
1124
1266
  );
1125
- console.log(` 3. Run ${chalk5.cyan("viberails check")} to verify your project passes`);
1267
+ console.log(` 3. Run ${chalk8.cyan("viberails check")} to verify your project passes`);
1126
1268
  }
1127
1269
  function updateGitignore(projectRoot) {
1128
- const gitignorePath = path12.join(projectRoot, ".gitignore");
1270
+ const gitignorePath = path13.join(projectRoot, ".gitignore");
1129
1271
  let content = "";
1130
- if (fs11.existsSync(gitignorePath)) {
1131
- content = fs11.readFileSync(gitignorePath, "utf-8");
1272
+ if (fs12.existsSync(gitignorePath)) {
1273
+ content = fs12.readFileSync(gitignorePath, "utf-8");
1132
1274
  }
1133
1275
  if (!content.includes(".viberails/scan-result.json")) {
1134
1276
  const block = "\n# viberails\n.viberails/scan-result.json\n";
1135
- fs11.writeFileSync(gitignorePath, `${content.trimEnd()}
1277
+ fs12.writeFileSync(gitignorePath, `${content.trimEnd()}
1136
1278
  ${block}`);
1137
1279
  }
1138
1280
  }
1139
- function setupPreCommitHook(projectRoot) {
1140
- const lefthookPath = path12.join(projectRoot, "lefthook.yml");
1141
- if (fs11.existsSync(lefthookPath)) {
1142
- addLefthookPreCommit(lefthookPath);
1143
- console.log(` ${chalk5.green("\u2713")} lefthook.yml \u2014 added viberails pre-commit`);
1144
- return;
1145
- }
1146
- const huskyDir = path12.join(projectRoot, ".husky");
1147
- if (fs11.existsSync(huskyDir)) {
1148
- writeHuskyPreCommit(huskyDir);
1149
- console.log(` ${chalk5.green("\u2713")} .husky/pre-commit \u2014 added viberails check`);
1150
- return;
1151
- }
1152
- const gitDir = path12.join(projectRoot, ".git");
1153
- if (fs11.existsSync(gitDir)) {
1154
- const hooksDir = path12.join(gitDir, "hooks");
1155
- if (!fs11.existsSync(hooksDir)) {
1156
- fs11.mkdirSync(hooksDir, { recursive: true });
1157
- }
1158
- writeGitHookPreCommit(hooksDir);
1159
- console.log(` ${chalk5.green("\u2713")} .git/hooks/pre-commit`);
1160
- }
1161
- }
1162
- function writeGitHookPreCommit(hooksDir) {
1163
- const hookPath = path12.join(hooksDir, "pre-commit");
1164
- if (fs11.existsSync(hookPath)) {
1165
- const existing = fs11.readFileSync(hookPath, "utf-8");
1166
- if (existing.includes("viberails")) return;
1167
- fs11.writeFileSync(
1168
- hookPath,
1169
- `${existing.trimEnd()}
1170
-
1171
- # viberails check
1172
- npx viberails check --staged
1173
- `
1174
- );
1175
- return;
1176
- }
1177
- const script = [
1178
- "#!/bin/sh",
1179
- "# Generated by viberails \u2014 https://viberails.sh",
1180
- "",
1181
- "npx viberails check --staged",
1182
- ""
1183
- ].join("\n");
1184
- fs11.writeFileSync(hookPath, script, { mode: 493 });
1185
- }
1186
- function addLefthookPreCommit(lefthookPath) {
1187
- const content = fs11.readFileSync(lefthookPath, "utf-8");
1188
- if (content.includes("viberails")) return;
1189
- const addition = ["", " viberails:", " run: npx viberails check --staged"].join("\n");
1190
- fs11.writeFileSync(lefthookPath, `${content.trimEnd()}
1191
- ${addition}
1192
- `);
1193
- }
1194
- function writeHuskyPreCommit(huskyDir) {
1195
- const hookPath = path12.join(huskyDir, "pre-commit");
1196
- if (fs11.existsSync(hookPath)) {
1197
- const existing = fs11.readFileSync(hookPath, "utf-8");
1198
- if (!existing.includes("viberails")) {
1199
- fs11.writeFileSync(hookPath, `${existing.trimEnd()}
1200
- npx viberails check --staged
1201
- `);
1202
- }
1203
- return;
1204
- }
1205
- fs11.writeFileSync(hookPath, "#!/bin/sh\nnpx viberails check --staged\n", { mode: 493 });
1206
- }
1207
1281
 
1208
1282
  // src/commands/sync.ts
1209
- import * as fs12 from "fs";
1210
- import * as path13 from "path";
1283
+ import * as fs13 from "fs";
1284
+ import * as path14 from "path";
1211
1285
  import { loadConfig as loadConfig4, mergeConfig } from "@viberails/config";
1212
1286
  import { scan as scan2 } from "@viberails/scanner";
1213
- import chalk6 from "chalk";
1287
+ import chalk9 from "chalk";
1214
1288
  var CONFIG_FILE5 = "viberails.config.json";
1215
1289
  async function syncCommand(cwd) {
1216
1290
  const startDir = cwd ?? process.cwd();
@@ -1220,23 +1294,23 @@ async function syncCommand(cwd) {
1220
1294
  "No package.json found in this directory or any parent.\n\nMake sure you are inside a JavaScript or TypeScript project, then run:\n npx viberails"
1221
1295
  );
1222
1296
  }
1223
- const configPath = path13.join(projectRoot, CONFIG_FILE5);
1297
+ const configPath = path14.join(projectRoot, CONFIG_FILE5);
1224
1298
  const existing = await loadConfig4(configPath);
1225
- console.log(chalk6.dim("Scanning project..."));
1299
+ console.log(chalk9.dim("Scanning project..."));
1226
1300
  const scanResult = await scan2(projectRoot);
1227
1301
  const merged = mergeConfig(existing, scanResult);
1228
- fs12.writeFileSync(configPath, `${JSON.stringify(merged, null, 2)}
1302
+ fs13.writeFileSync(configPath, `${JSON.stringify(merged, null, 2)}
1229
1303
  `);
1230
1304
  writeGeneratedFiles(projectRoot, merged, scanResult);
1231
1305
  console.log(`
1232
- ${chalk6.bold("Synced:")}`);
1233
- console.log(` ${chalk6.green("\u2713")} ${CONFIG_FILE5} \u2014 updated`);
1234
- console.log(` ${chalk6.green("\u2713")} .viberails/context.md \u2014 regenerated`);
1235
- console.log(` ${chalk6.green("\u2713")} .viberails/scan-result.json \u2014 updated`);
1306
+ ${chalk9.bold("Synced:")}`);
1307
+ console.log(` ${chalk9.green("\u2713")} ${CONFIG_FILE5} \u2014 updated`);
1308
+ console.log(` ${chalk9.green("\u2713")} .viberails/context.md \u2014 regenerated`);
1309
+ console.log(` ${chalk9.green("\u2713")} .viberails/scan-result.json \u2014 updated`);
1236
1310
  }
1237
1311
 
1238
1312
  // src/index.ts
1239
- var VERSION = "0.2.0";
1313
+ var VERSION = "0.2.1";
1240
1314
  var program = new Command();
1241
1315
  program.name("viberails").description("Guardrails for vibe coding").version(VERSION);
1242
1316
  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)").action(async (options) => {
@@ -1244,7 +1318,7 @@ program.command("init", { isDefault: true }).description("Scan your project and
1244
1318
  await initCommand(options);
1245
1319
  } catch (err) {
1246
1320
  const message = err instanceof Error ? err.message : String(err);
1247
- console.error(`${chalk7.red("Error:")} ${message}`);
1321
+ console.error(`${chalk10.red("Error:")} ${message}`);
1248
1322
  process.exit(1);
1249
1323
  }
1250
1324
  });
@@ -1253,7 +1327,7 @@ program.command("sync").description("Re-scan and update generated files").action
1253
1327
  await syncCommand();
1254
1328
  } catch (err) {
1255
1329
  const message = err instanceof Error ? err.message : String(err);
1256
- console.error(`${chalk7.red("Error:")} ${message}`);
1330
+ console.error(`${chalk10.red("Error:")} ${message}`);
1257
1331
  process.exit(1);
1258
1332
  }
1259
1333
  });
@@ -1266,7 +1340,7 @@ program.command("check").description("Check files against enforced rules").optio
1266
1340
  process.exit(exitCode);
1267
1341
  } catch (err) {
1268
1342
  const message = err instanceof Error ? err.message : String(err);
1269
- console.error(`${chalk7.red("Error:")} ${message}`);
1343
+ console.error(`${chalk10.red("Error:")} ${message}`);
1270
1344
  process.exit(1);
1271
1345
  }
1272
1346
  });
@@ -1276,7 +1350,7 @@ program.command("fix").description("Auto-fix file naming violations and generate
1276
1350
  process.exit(exitCode);
1277
1351
  } catch (err) {
1278
1352
  const message = err instanceof Error ? err.message : String(err);
1279
- console.error(`${chalk7.red("Error:")} ${message}`);
1353
+ console.error(`${chalk10.red("Error:")} ${message}`);
1280
1354
  process.exit(1);
1281
1355
  }
1282
1356
  });
@@ -1285,7 +1359,7 @@ program.command("boundaries").description("Display, infer, or inspect import bou
1285
1359
  await boundariesCommand(options);
1286
1360
  } catch (err) {
1287
1361
  const message = err instanceof Error ? err.message : String(err);
1288
- console.error(`${chalk7.red("Error:")} ${message}`);
1362
+ console.error(`${chalk10.red("Error:")} ${message}`);
1289
1363
  process.exit(1);
1290
1364
  }
1291
1365
  });