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.cjs +302 -228
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +300 -226
- package/dist/index.js.map +1 -1
- package/package.json +6 -6
package/dist/index.cjs
CHANGED
|
@@ -34,7 +34,7 @@ __export(index_exports, {
|
|
|
34
34
|
VERSION: () => VERSION
|
|
35
35
|
});
|
|
36
36
|
module.exports = __toCommonJS(index_exports);
|
|
37
|
-
var
|
|
37
|
+
var import_chalk10 = __toESM(require("chalk"), 1);
|
|
38
38
|
var import_commander = require("commander");
|
|
39
39
|
|
|
40
40
|
// src/commands/boundaries.ts
|
|
@@ -532,12 +532,56 @@ ${violations.length} ${word} found.`);
|
|
|
532
532
|
}
|
|
533
533
|
|
|
534
534
|
// src/commands/fix.ts
|
|
535
|
-
var import_node_child_process2 = require("child_process");
|
|
536
535
|
var fs9 = __toESM(require("fs"), 1);
|
|
537
536
|
var path10 = __toESM(require("path"), 1);
|
|
538
|
-
var import_node_readline = require("readline");
|
|
539
537
|
var import_config3 = require("@viberails/config");
|
|
538
|
+
var import_chalk4 = __toESM(require("chalk"), 1);
|
|
539
|
+
|
|
540
|
+
// src/commands/fix-helpers.ts
|
|
541
|
+
var import_node_child_process2 = require("child_process");
|
|
542
|
+
var import_node_readline = require("readline");
|
|
540
543
|
var import_chalk3 = __toESM(require("chalk"), 1);
|
|
544
|
+
function printPlan(renames, stubs) {
|
|
545
|
+
if (renames.length > 0) {
|
|
546
|
+
console.log(import_chalk3.default.bold("\nFile renames:"));
|
|
547
|
+
for (const r of renames) {
|
|
548
|
+
console.log(` ${import_chalk3.default.red(r.oldPath)} \u2192 ${import_chalk3.default.green(r.newPath)}`);
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
if (stubs.length > 0) {
|
|
552
|
+
console.log(import_chalk3.default.bold("\nTest stubs to create:"));
|
|
553
|
+
for (const s of stubs) {
|
|
554
|
+
console.log(` ${import_chalk3.default.green("+")} ${s.path}`);
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
function checkGitDirty(projectRoot) {
|
|
559
|
+
try {
|
|
560
|
+
const output = (0, import_node_child_process2.execSync)("git status --porcelain", {
|
|
561
|
+
cwd: projectRoot,
|
|
562
|
+
encoding: "utf-8"
|
|
563
|
+
});
|
|
564
|
+
return output.trim().length > 0;
|
|
565
|
+
} catch {
|
|
566
|
+
return false;
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
function getConventionValue(convention) {
|
|
570
|
+
if (typeof convention === "string") return convention;
|
|
571
|
+
if (convention && typeof convention === "object" && "value" in convention) {
|
|
572
|
+
return convention.value;
|
|
573
|
+
}
|
|
574
|
+
return void 0;
|
|
575
|
+
}
|
|
576
|
+
function promptConfirm(question) {
|
|
577
|
+
const rl = (0, import_node_readline.createInterface)({ input: process.stdin, output: process.stdout });
|
|
578
|
+
return new Promise((resolve4) => {
|
|
579
|
+
rl.question(`${question} (y/N) `, (answer) => {
|
|
580
|
+
rl.close();
|
|
581
|
+
resolve4(answer.toLowerCase() === "y" || answer.toLowerCase() === "yes");
|
|
582
|
+
});
|
|
583
|
+
});
|
|
584
|
+
}
|
|
541
585
|
|
|
542
586
|
// src/commands/fix-imports.ts
|
|
543
587
|
var path7 = __toESM(require("path"), 1);
|
|
@@ -757,13 +801,13 @@ async function fixCommand(options, cwd) {
|
|
|
757
801
|
const startDir = cwd ?? process.cwd();
|
|
758
802
|
const projectRoot = findProjectRoot(startDir);
|
|
759
803
|
if (!projectRoot) {
|
|
760
|
-
console.error(`${
|
|
804
|
+
console.error(`${import_chalk4.default.red("Error:")} No package.json found. Are you in a JS/TS project?`);
|
|
761
805
|
return 1;
|
|
762
806
|
}
|
|
763
807
|
const configPath = path10.join(projectRoot, CONFIG_FILE3);
|
|
764
808
|
if (!fs9.existsSync(configPath)) {
|
|
765
809
|
console.error(
|
|
766
|
-
`${
|
|
810
|
+
`${import_chalk4.default.red("Error:")} No viberails.config.json found. Run \`viberails init\` first.`
|
|
767
811
|
);
|
|
768
812
|
return 1;
|
|
769
813
|
}
|
|
@@ -772,7 +816,7 @@ async function fixCommand(options, cwd) {
|
|
|
772
816
|
const isDirty = checkGitDirty(projectRoot);
|
|
773
817
|
if (isDirty) {
|
|
774
818
|
console.log(
|
|
775
|
-
|
|
819
|
+
import_chalk4.default.yellow("Warning: You have uncommitted changes. Consider committing first.")
|
|
776
820
|
);
|
|
777
821
|
}
|
|
778
822
|
}
|
|
@@ -802,12 +846,12 @@ async function fixCommand(options, cwd) {
|
|
|
802
846
|
}
|
|
803
847
|
}
|
|
804
848
|
if (dedupedRenames.length === 0 && testStubs.length === 0) {
|
|
805
|
-
console.log(`${
|
|
849
|
+
console.log(`${import_chalk4.default.green("\u2713")} No fixable violations found.`);
|
|
806
850
|
return 0;
|
|
807
851
|
}
|
|
808
852
|
printPlan(dedupedRenames, testStubs);
|
|
809
853
|
if (options.dryRun) {
|
|
810
|
-
console.log(
|
|
854
|
+
console.log(import_chalk4.default.dim("\nDry run \u2014 no changes applied."));
|
|
811
855
|
return 0;
|
|
812
856
|
}
|
|
813
857
|
if (!options.yes) {
|
|
@@ -838,94 +882,89 @@ async function fixCommand(options, cwd) {
|
|
|
838
882
|
}
|
|
839
883
|
console.log("");
|
|
840
884
|
if (renameCount > 0) {
|
|
841
|
-
console.log(`${
|
|
885
|
+
console.log(`${import_chalk4.default.green("\u2713")} Renamed ${renameCount} file${renameCount > 1 ? "s" : ""}`);
|
|
842
886
|
}
|
|
843
887
|
if (importUpdateCount > 0) {
|
|
844
888
|
console.log(
|
|
845
|
-
`${
|
|
889
|
+
`${import_chalk4.default.green("\u2713")} Updated ${importUpdateCount} import${importUpdateCount > 1 ? "s" : ""}`
|
|
846
890
|
);
|
|
847
891
|
}
|
|
848
892
|
if (stubCount > 0) {
|
|
849
|
-
console.log(`${
|
|
893
|
+
console.log(`${import_chalk4.default.green("\u2713")} Generated ${stubCount} test stub${stubCount > 1 ? "s" : ""}`);
|
|
850
894
|
}
|
|
851
895
|
return 0;
|
|
852
896
|
}
|
|
853
|
-
function printPlan(renames, stubs) {
|
|
854
|
-
if (renames.length > 0) {
|
|
855
|
-
console.log(import_chalk3.default.bold("\nFile renames:"));
|
|
856
|
-
for (const r of renames) {
|
|
857
|
-
console.log(` ${import_chalk3.default.red(r.oldPath)} \u2192 ${import_chalk3.default.green(r.newPath)}`);
|
|
858
|
-
}
|
|
859
|
-
}
|
|
860
|
-
if (stubs.length > 0) {
|
|
861
|
-
console.log(import_chalk3.default.bold("\nTest stubs to create:"));
|
|
862
|
-
for (const s of stubs) {
|
|
863
|
-
console.log(` ${import_chalk3.default.green("+")} ${s.path}`);
|
|
864
|
-
}
|
|
865
|
-
}
|
|
866
|
-
}
|
|
867
|
-
function checkGitDirty(projectRoot) {
|
|
868
|
-
try {
|
|
869
|
-
const output = (0, import_node_child_process2.execSync)("git status --porcelain", {
|
|
870
|
-
cwd: projectRoot,
|
|
871
|
-
encoding: "utf-8"
|
|
872
|
-
});
|
|
873
|
-
return output.trim().length > 0;
|
|
874
|
-
} catch {
|
|
875
|
-
return false;
|
|
876
|
-
}
|
|
877
|
-
}
|
|
878
|
-
function getConventionValue(convention) {
|
|
879
|
-
if (typeof convention === "string") return convention;
|
|
880
|
-
if (convention && typeof convention === "object" && "value" in convention) {
|
|
881
|
-
return convention.value;
|
|
882
|
-
}
|
|
883
|
-
return void 0;
|
|
884
|
-
}
|
|
885
|
-
function promptConfirm(question) {
|
|
886
|
-
const rl = (0, import_node_readline.createInterface)({ input: process.stdin, output: process.stdout });
|
|
887
|
-
return new Promise((resolve4) => {
|
|
888
|
-
rl.question(`${question} (y/N) `, (answer) => {
|
|
889
|
-
rl.close();
|
|
890
|
-
resolve4(answer.toLowerCase() === "y" || answer.toLowerCase() === "yes");
|
|
891
|
-
});
|
|
892
|
-
});
|
|
893
|
-
}
|
|
894
897
|
|
|
895
898
|
// src/commands/init.ts
|
|
896
|
-
var
|
|
897
|
-
var
|
|
899
|
+
var fs12 = __toESM(require("fs"), 1);
|
|
900
|
+
var path13 = __toESM(require("path"), 1);
|
|
898
901
|
var import_config4 = require("@viberails/config");
|
|
899
902
|
var import_scanner = require("@viberails/scanner");
|
|
900
|
-
var
|
|
903
|
+
var import_chalk8 = __toESM(require("chalk"), 1);
|
|
901
904
|
|
|
902
905
|
// src/display.ts
|
|
906
|
+
var import_types3 = require("@viberails/types");
|
|
907
|
+
var import_chalk6 = __toESM(require("chalk"), 1);
|
|
908
|
+
|
|
909
|
+
// src/display-helpers.ts
|
|
903
910
|
var import_types = require("@viberails/types");
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
911
|
+
function groupByRole(directories) {
|
|
912
|
+
const map = /* @__PURE__ */ new Map();
|
|
913
|
+
for (const dir of directories) {
|
|
914
|
+
if (dir.role === "unknown") continue;
|
|
915
|
+
const existing = map.get(dir.role);
|
|
916
|
+
if (existing) {
|
|
917
|
+
existing.dirs.push(dir);
|
|
918
|
+
} else {
|
|
919
|
+
map.set(dir.role, { dirs: [dir] });
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
const groups = [];
|
|
923
|
+
for (const [role, { dirs }] of map) {
|
|
924
|
+
const label = import_types.ROLE_DESCRIPTIONS[role] ?? role;
|
|
925
|
+
const totalFiles = dirs.reduce((sum, d) => sum + d.fileCount, 0);
|
|
926
|
+
groups.push({
|
|
927
|
+
role,
|
|
928
|
+
label,
|
|
929
|
+
dirCount: dirs.length,
|
|
930
|
+
totalFiles,
|
|
931
|
+
singlePath: dirs.length === 1 ? dirs[0].path : void 0
|
|
932
|
+
});
|
|
933
|
+
}
|
|
934
|
+
return groups;
|
|
914
935
|
}
|
|
915
|
-
function
|
|
916
|
-
const
|
|
917
|
-
if (
|
|
918
|
-
|
|
936
|
+
function formatSummary(stats, packageCount) {
|
|
937
|
+
const parts = [];
|
|
938
|
+
if (packageCount && packageCount > 1) {
|
|
939
|
+
parts.push(`${packageCount} packages`);
|
|
919
940
|
}
|
|
920
|
-
|
|
941
|
+
parts.push(`${stats.totalFiles.toLocaleString()} source files`);
|
|
942
|
+
parts.push(`${stats.totalLines.toLocaleString()} lines`);
|
|
943
|
+
parts.push(`avg ${Math.round(stats.averageFileLines)} lines/file`);
|
|
944
|
+
return parts.join(" \xB7 ");
|
|
945
|
+
}
|
|
946
|
+
function formatExtensions(filesByExtension, maxEntries = 4) {
|
|
947
|
+
return Object.entries(filesByExtension).sort(([, a], [, b]) => b - a).slice(0, maxEntries).map(([ext, count]) => `${ext} ${count}`).join(" \xB7 ");
|
|
948
|
+
}
|
|
949
|
+
function formatRoleGroup(group) {
|
|
950
|
+
const files = group.totalFiles === 1 ? "1 file" : `${group.totalFiles} files`;
|
|
951
|
+
if (group.singlePath) {
|
|
952
|
+
return `${group.label} \u2014 ${group.singlePath} (${files})`;
|
|
953
|
+
}
|
|
954
|
+
const dirs = group.dirCount === 1 ? "1 dir" : `${group.dirCount} dirs`;
|
|
955
|
+
return `${group.label} \u2014 ${dirs} (${files})`;
|
|
921
956
|
}
|
|
957
|
+
|
|
958
|
+
// src/display-monorepo.ts
|
|
959
|
+
var import_types2 = require("@viberails/types");
|
|
960
|
+
var import_chalk5 = __toESM(require("chalk"), 1);
|
|
922
961
|
function formatPackageSummary(pkg) {
|
|
923
962
|
const parts = [];
|
|
924
963
|
if (pkg.stack.framework) {
|
|
925
|
-
parts.push(formatItem(pkg.stack.framework,
|
|
964
|
+
parts.push(formatItem(pkg.stack.framework, import_types2.FRAMEWORK_NAMES));
|
|
926
965
|
}
|
|
927
966
|
if (pkg.stack.styling) {
|
|
928
|
-
parts.push(formatItem(pkg.stack.styling,
|
|
967
|
+
parts.push(formatItem(pkg.stack.styling, import_types2.STYLING_NAMES));
|
|
929
968
|
}
|
|
930
969
|
const files = `${pkg.statistics.totalFiles} files`;
|
|
931
970
|
const detail = parts.length > 0 ? `${parts.join(", ")} (${files})` : `(${files})`;
|
|
@@ -934,16 +973,19 @@ function formatPackageSummary(pkg) {
|
|
|
934
973
|
function displayMonorepoResults(scanResult) {
|
|
935
974
|
const { stack, packages } = scanResult;
|
|
936
975
|
console.log(`
|
|
937
|
-
${
|
|
938
|
-
console.log(` ${
|
|
976
|
+
${import_chalk5.default.bold(`Detected: (monorepo, ${packages.length} packages)`)}`);
|
|
977
|
+
console.log(` ${import_chalk5.default.green("\u2713")} ${formatItem(stack.language)}`);
|
|
939
978
|
if (stack.packageManager) {
|
|
940
|
-
console.log(` ${
|
|
979
|
+
console.log(` ${import_chalk5.default.green("\u2713")} ${formatItem(stack.packageManager)}`);
|
|
941
980
|
}
|
|
942
981
|
if (stack.linter) {
|
|
943
|
-
console.log(` ${
|
|
982
|
+
console.log(` ${import_chalk5.default.green("\u2713")} ${formatItem(stack.linter)}`);
|
|
983
|
+
}
|
|
984
|
+
if (stack.formatter) {
|
|
985
|
+
console.log(` ${import_chalk5.default.green("\u2713")} ${formatItem(stack.formatter)}`);
|
|
944
986
|
}
|
|
945
987
|
if (stack.testRunner) {
|
|
946
|
-
console.log(` ${
|
|
988
|
+
console.log(` ${import_chalk5.default.green("\u2713")} ${formatItem(stack.testRunner)}`);
|
|
947
989
|
}
|
|
948
990
|
console.log("");
|
|
949
991
|
for (const pkg of packages) {
|
|
@@ -954,96 +996,123 @@ ${import_chalk4.default.bold(`Detected: (monorepo, ${packages.length} packages)`
|
|
|
954
996
|
);
|
|
955
997
|
if (packagesWithDirs.length > 0) {
|
|
956
998
|
console.log(`
|
|
957
|
-
${
|
|
999
|
+
${import_chalk5.default.bold("Structure:")}`);
|
|
958
1000
|
for (const pkg of packagesWithDirs) {
|
|
959
|
-
const
|
|
960
|
-
if (
|
|
1001
|
+
const groups = groupByRole(pkg.structure.directories);
|
|
1002
|
+
if (groups.length === 0) continue;
|
|
961
1003
|
console.log(` ${pkg.relativePath}:`);
|
|
962
|
-
for (const
|
|
963
|
-
|
|
964
|
-
const files = dir.fileCount === 1 ? "1 file" : `${dir.fileCount} files`;
|
|
965
|
-
console.log(` ${import_chalk4.default.green("\u2713")} ${dir.path} \u2014 ${label} (${files})`);
|
|
1004
|
+
for (const group of groups) {
|
|
1005
|
+
console.log(` ${import_chalk5.default.green("\u2713")} ${formatRoleGroup(group)}`);
|
|
966
1006
|
}
|
|
967
1007
|
}
|
|
968
1008
|
}
|
|
1009
|
+
displayConventions(scanResult);
|
|
1010
|
+
displaySummarySection(scanResult);
|
|
1011
|
+
console.log("");
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
// src/display.ts
|
|
1015
|
+
var CONVENTION_LABELS = {
|
|
1016
|
+
fileNaming: "File naming",
|
|
1017
|
+
componentNaming: "Component naming",
|
|
1018
|
+
hookNaming: "Hook naming",
|
|
1019
|
+
importAlias: "Import alias"
|
|
1020
|
+
};
|
|
1021
|
+
function formatItem(item, nameMap) {
|
|
1022
|
+
const name = nameMap?.[item.name] ?? item.name;
|
|
1023
|
+
return item.version ? `${name} ${item.version}` : name;
|
|
1024
|
+
}
|
|
1025
|
+
function confidenceLabel(convention) {
|
|
1026
|
+
const pct = Math.round(convention.consistency);
|
|
1027
|
+
if (convention.confidence === "high") {
|
|
1028
|
+
return `${pct}% \u2014 high confidence, will enforce`;
|
|
1029
|
+
}
|
|
1030
|
+
return `${pct}% \u2014 medium confidence, suggested only`;
|
|
1031
|
+
}
|
|
1032
|
+
function displayConventions(scanResult) {
|
|
969
1033
|
const conventionEntries = Object.entries(scanResult.conventions);
|
|
970
|
-
if (conventionEntries.length
|
|
971
|
-
|
|
972
|
-
${
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
1034
|
+
if (conventionEntries.length === 0) return;
|
|
1035
|
+
console.log(`
|
|
1036
|
+
${import_chalk6.default.bold("Conventions:")}`);
|
|
1037
|
+
for (const [key, convention] of conventionEntries) {
|
|
1038
|
+
if (convention.confidence === "low") continue;
|
|
1039
|
+
const label = CONVENTION_LABELS[key] ?? key;
|
|
1040
|
+
if (scanResult.packages.length > 1) {
|
|
1041
|
+
const pkgValues = scanResult.packages.filter((pkg) => pkg.conventions[key] && pkg.conventions[key].confidence !== "low").map((pkg) => ({ relativePath: pkg.relativePath, convention: pkg.conventions[key] }));
|
|
977
1042
|
const allSame = pkgValues.every((pv) => pv.convention.value === convention.value);
|
|
978
1043
|
if (allSame || pkgValues.length <= 1) {
|
|
979
|
-
const ind = convention.confidence === "high" ?
|
|
980
|
-
const detail =
|
|
1044
|
+
const ind = convention.confidence === "high" ? import_chalk6.default.green("\u2713") : import_chalk6.default.yellow("~");
|
|
1045
|
+
const detail = import_chalk6.default.dim(`(${confidenceLabel(convention)})`);
|
|
981
1046
|
console.log(` ${ind} ${label}: ${convention.value} ${detail}`);
|
|
982
1047
|
} else {
|
|
983
|
-
console.log(` ${
|
|
1048
|
+
console.log(` ${import_chalk6.default.yellow("~")} ${label}: varies by package`);
|
|
984
1049
|
for (const pv of pkgValues) {
|
|
985
1050
|
const pct = Math.round(pv.convention.consistency);
|
|
986
1051
|
console.log(` ${pv.relativePath}: ${pv.convention.value} (${pct}%)`);
|
|
987
1052
|
}
|
|
988
1053
|
}
|
|
1054
|
+
} else {
|
|
1055
|
+
const ind = convention.confidence === "high" ? import_chalk6.default.green("\u2713") : import_chalk6.default.yellow("~");
|
|
1056
|
+
const detail = import_chalk6.default.dim(`(${confidenceLabel(convention)})`);
|
|
1057
|
+
console.log(` ${ind} ${label}: ${convention.value} ${detail}`);
|
|
989
1058
|
}
|
|
990
1059
|
}
|
|
991
|
-
|
|
1060
|
+
}
|
|
1061
|
+
function displaySummarySection(scanResult) {
|
|
1062
|
+
const pkgCount = scanResult.packages.length > 1 ? scanResult.packages.length : void 0;
|
|
1063
|
+
console.log(`
|
|
1064
|
+
${import_chalk6.default.bold("Summary:")}`);
|
|
1065
|
+
console.log(` ${formatSummary(scanResult.statistics, pkgCount)}`);
|
|
1066
|
+
const ext = formatExtensions(scanResult.statistics.filesByExtension);
|
|
1067
|
+
if (ext) {
|
|
1068
|
+
console.log(` ${ext}`);
|
|
1069
|
+
}
|
|
992
1070
|
}
|
|
993
1071
|
function displayScanResults(scanResult) {
|
|
994
1072
|
if (scanResult.packages.length > 1) {
|
|
995
1073
|
displayMonorepoResults(scanResult);
|
|
996
1074
|
return;
|
|
997
1075
|
}
|
|
998
|
-
const { stack
|
|
1076
|
+
const { stack } = scanResult;
|
|
999
1077
|
console.log(`
|
|
1000
|
-
${
|
|
1078
|
+
${import_chalk6.default.bold("Detected:")}`);
|
|
1001
1079
|
if (stack.framework) {
|
|
1002
|
-
console.log(` ${
|
|
1080
|
+
console.log(` ${import_chalk6.default.green("\u2713")} ${formatItem(stack.framework, import_types3.FRAMEWORK_NAMES)}`);
|
|
1003
1081
|
}
|
|
1004
|
-
console.log(` ${
|
|
1082
|
+
console.log(` ${import_chalk6.default.green("\u2713")} ${formatItem(stack.language)}`);
|
|
1005
1083
|
if (stack.styling) {
|
|
1006
|
-
console.log(` ${
|
|
1084
|
+
console.log(` ${import_chalk6.default.green("\u2713")} ${formatItem(stack.styling, import_types3.STYLING_NAMES)}`);
|
|
1007
1085
|
}
|
|
1008
1086
|
if (stack.backend) {
|
|
1009
|
-
console.log(` ${
|
|
1087
|
+
console.log(` ${import_chalk6.default.green("\u2713")} ${formatItem(stack.backend, import_types3.FRAMEWORK_NAMES)}`);
|
|
1010
1088
|
}
|
|
1011
1089
|
if (stack.linter) {
|
|
1012
|
-
console.log(` ${
|
|
1090
|
+
console.log(` ${import_chalk6.default.green("\u2713")} ${formatItem(stack.linter)}`);
|
|
1091
|
+
}
|
|
1092
|
+
if (stack.formatter) {
|
|
1093
|
+
console.log(` ${import_chalk6.default.green("\u2713")} ${formatItem(stack.formatter)}`);
|
|
1013
1094
|
}
|
|
1014
1095
|
if (stack.testRunner) {
|
|
1015
|
-
console.log(` ${
|
|
1096
|
+
console.log(` ${import_chalk6.default.green("\u2713")} ${formatItem(stack.testRunner)}`);
|
|
1016
1097
|
}
|
|
1017
1098
|
if (stack.packageManager) {
|
|
1018
|
-
console.log(` ${
|
|
1099
|
+
console.log(` ${import_chalk6.default.green("\u2713")} ${formatItem(stack.packageManager)}`);
|
|
1019
1100
|
}
|
|
1020
1101
|
if (stack.libraries.length > 0) {
|
|
1021
1102
|
for (const lib of stack.libraries) {
|
|
1022
|
-
console.log(` ${
|
|
1103
|
+
console.log(` ${import_chalk6.default.green("\u2713")} ${formatItem(lib, import_types3.LIBRARY_NAMES)}`);
|
|
1023
1104
|
}
|
|
1024
1105
|
}
|
|
1025
|
-
const
|
|
1026
|
-
if (
|
|
1106
|
+
const groups = groupByRole(scanResult.structure.directories);
|
|
1107
|
+
if (groups.length > 0) {
|
|
1027
1108
|
console.log(`
|
|
1028
|
-
${
|
|
1029
|
-
for (const
|
|
1030
|
-
|
|
1031
|
-
const files = dir.fileCount === 1 ? "1 file" : `${dir.fileCount} files`;
|
|
1032
|
-
console.log(` ${import_chalk4.default.green("\u2713")} ${dir.path} \u2014 ${label} (${files})`);
|
|
1033
|
-
}
|
|
1034
|
-
}
|
|
1035
|
-
const conventionEntries = Object.entries(conventions);
|
|
1036
|
-
if (conventionEntries.length > 0) {
|
|
1037
|
-
console.log(`
|
|
1038
|
-
${import_chalk4.default.bold("Conventions:")}`);
|
|
1039
|
-
for (const [key, convention] of conventionEntries) {
|
|
1040
|
-
if (convention.confidence === "low") continue;
|
|
1041
|
-
const label = CONVENTION_LABELS[key] ?? key;
|
|
1042
|
-
const ind = convention.confidence === "high" ? import_chalk4.default.green("\u2713") : import_chalk4.default.yellow("~");
|
|
1043
|
-
const detail = import_chalk4.default.dim(`(${confidenceLabel(convention)})`);
|
|
1044
|
-
console.log(` ${ind} ${label}: ${convention.value} ${detail}`);
|
|
1109
|
+
${import_chalk6.default.bold("Structure:")}`);
|
|
1110
|
+
for (const group of groups) {
|
|
1111
|
+
console.log(` ${import_chalk6.default.green("\u2713")} ${formatRoleGroup(group)}`);
|
|
1045
1112
|
}
|
|
1046
1113
|
}
|
|
1114
|
+
displayConventions(scanResult);
|
|
1115
|
+
displaySummarySection(scanResult);
|
|
1047
1116
|
console.log("");
|
|
1048
1117
|
}
|
|
1049
1118
|
|
|
@@ -1073,6 +1142,79 @@ function writeGeneratedFiles(projectRoot, config, scanResult) {
|
|
|
1073
1142
|
}
|
|
1074
1143
|
}
|
|
1075
1144
|
|
|
1145
|
+
// src/commands/init-hooks.ts
|
|
1146
|
+
var fs11 = __toESM(require("fs"), 1);
|
|
1147
|
+
var path12 = __toESM(require("path"), 1);
|
|
1148
|
+
var import_chalk7 = __toESM(require("chalk"), 1);
|
|
1149
|
+
function setupPreCommitHook(projectRoot) {
|
|
1150
|
+
const lefthookPath = path12.join(projectRoot, "lefthook.yml");
|
|
1151
|
+
if (fs11.existsSync(lefthookPath)) {
|
|
1152
|
+
addLefthookPreCommit(lefthookPath);
|
|
1153
|
+
console.log(` ${import_chalk7.default.green("\u2713")} lefthook.yml \u2014 added viberails pre-commit`);
|
|
1154
|
+
return;
|
|
1155
|
+
}
|
|
1156
|
+
const huskyDir = path12.join(projectRoot, ".husky");
|
|
1157
|
+
if (fs11.existsSync(huskyDir)) {
|
|
1158
|
+
writeHuskyPreCommit(huskyDir);
|
|
1159
|
+
console.log(` ${import_chalk7.default.green("\u2713")} .husky/pre-commit \u2014 added viberails check`);
|
|
1160
|
+
return;
|
|
1161
|
+
}
|
|
1162
|
+
const gitDir = path12.join(projectRoot, ".git");
|
|
1163
|
+
if (fs11.existsSync(gitDir)) {
|
|
1164
|
+
const hooksDir = path12.join(gitDir, "hooks");
|
|
1165
|
+
if (!fs11.existsSync(hooksDir)) {
|
|
1166
|
+
fs11.mkdirSync(hooksDir, { recursive: true });
|
|
1167
|
+
}
|
|
1168
|
+
writeGitHookPreCommit(hooksDir);
|
|
1169
|
+
console.log(` ${import_chalk7.default.green("\u2713")} .git/hooks/pre-commit`);
|
|
1170
|
+
}
|
|
1171
|
+
}
|
|
1172
|
+
function writeGitHookPreCommit(hooksDir) {
|
|
1173
|
+
const hookPath = path12.join(hooksDir, "pre-commit");
|
|
1174
|
+
if (fs11.existsSync(hookPath)) {
|
|
1175
|
+
const existing = fs11.readFileSync(hookPath, "utf-8");
|
|
1176
|
+
if (existing.includes("viberails")) return;
|
|
1177
|
+
fs11.writeFileSync(
|
|
1178
|
+
hookPath,
|
|
1179
|
+
`${existing.trimEnd()}
|
|
1180
|
+
|
|
1181
|
+
# viberails check
|
|
1182
|
+
npx viberails check --staged
|
|
1183
|
+
`
|
|
1184
|
+
);
|
|
1185
|
+
return;
|
|
1186
|
+
}
|
|
1187
|
+
const script = [
|
|
1188
|
+
"#!/bin/sh",
|
|
1189
|
+
"# Generated by viberails \u2014 https://viberails.sh",
|
|
1190
|
+
"",
|
|
1191
|
+
"npx viberails check --staged",
|
|
1192
|
+
""
|
|
1193
|
+
].join("\n");
|
|
1194
|
+
fs11.writeFileSync(hookPath, script, { mode: 493 });
|
|
1195
|
+
}
|
|
1196
|
+
function addLefthookPreCommit(lefthookPath) {
|
|
1197
|
+
const content = fs11.readFileSync(lefthookPath, "utf-8");
|
|
1198
|
+
if (content.includes("viberails")) return;
|
|
1199
|
+
const addition = ["", " viberails:", " run: npx viberails check --staged"].join("\n");
|
|
1200
|
+
fs11.writeFileSync(lefthookPath, `${content.trimEnd()}
|
|
1201
|
+
${addition}
|
|
1202
|
+
`);
|
|
1203
|
+
}
|
|
1204
|
+
function writeHuskyPreCommit(huskyDir) {
|
|
1205
|
+
const hookPath = path12.join(huskyDir, "pre-commit");
|
|
1206
|
+
if (fs11.existsSync(hookPath)) {
|
|
1207
|
+
const existing = fs11.readFileSync(hookPath, "utf-8");
|
|
1208
|
+
if (!existing.includes("viberails")) {
|
|
1209
|
+
fs11.writeFileSync(hookPath, `${existing.trimEnd()}
|
|
1210
|
+
npx viberails check --staged
|
|
1211
|
+
`);
|
|
1212
|
+
}
|
|
1213
|
+
return;
|
|
1214
|
+
}
|
|
1215
|
+
fs11.writeFileSync(hookPath, "#!/bin/sh\nnpx viberails check --staged\n", { mode: 493 });
|
|
1216
|
+
}
|
|
1217
|
+
|
|
1076
1218
|
// src/commands/init.ts
|
|
1077
1219
|
var CONFIG_FILE4 = "viberails.config.json";
|
|
1078
1220
|
function filterHighConfidence(conventions) {
|
|
@@ -1095,19 +1237,19 @@ async function initCommand(options, cwd) {
|
|
|
1095
1237
|
"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"
|
|
1096
1238
|
);
|
|
1097
1239
|
}
|
|
1098
|
-
const configPath =
|
|
1099
|
-
if (
|
|
1240
|
+
const configPath = path13.join(projectRoot, CONFIG_FILE4);
|
|
1241
|
+
if (fs12.existsSync(configPath)) {
|
|
1100
1242
|
console.log(
|
|
1101
|
-
|
|
1243
|
+
import_chalk8.default.yellow("!") + " viberails is already initialized in this project.\n Run " + import_chalk8.default.cyan("viberails sync") + " to update the generated files."
|
|
1102
1244
|
);
|
|
1103
1245
|
return;
|
|
1104
1246
|
}
|
|
1105
|
-
console.log(
|
|
1247
|
+
console.log(import_chalk8.default.dim("Scanning project..."));
|
|
1106
1248
|
const scanResult = await (0, import_scanner.scan)(projectRoot);
|
|
1107
1249
|
displayScanResults(scanResult);
|
|
1108
1250
|
if (scanResult.statistics.totalFiles === 0) {
|
|
1109
1251
|
console.log(
|
|
1110
|
-
|
|
1252
|
+
import_chalk8.default.yellow("!") + " No source files detected. viberails will generate context with minimal content.\n Run " + import_chalk8.default.cyan("viberails sync") + " after adding source files.\n"
|
|
1111
1253
|
);
|
|
1112
1254
|
}
|
|
1113
1255
|
if (!options.yes) {
|
|
@@ -1127,7 +1269,7 @@ async function initCommand(options, cwd) {
|
|
|
1127
1269
|
shouldInfer = await confirm("Infer boundary rules from import patterns?");
|
|
1128
1270
|
}
|
|
1129
1271
|
if (shouldInfer) {
|
|
1130
|
-
console.log(
|
|
1272
|
+
console.log(import_chalk8.default.dim("Building import graph..."));
|
|
1131
1273
|
const { buildImportGraph, inferBoundaries } = await import("@viberails/graph");
|
|
1132
1274
|
const packages = resolveWorkspacePackages(projectRoot, config.workspace);
|
|
1133
1275
|
const graph = await buildImportGraph(projectRoot, { packages, ignore: config.ignore });
|
|
@@ -1135,115 +1277,47 @@ async function initCommand(options, cwd) {
|
|
|
1135
1277
|
if (inferred.length > 0) {
|
|
1136
1278
|
config.boundaries = inferred;
|
|
1137
1279
|
config.rules.enforceBoundaries = true;
|
|
1138
|
-
console.log(` ${
|
|
1280
|
+
console.log(` ${import_chalk8.default.green("\u2713")} Inferred ${inferred.length} boundary rules`);
|
|
1139
1281
|
}
|
|
1140
1282
|
}
|
|
1141
1283
|
}
|
|
1142
|
-
|
|
1284
|
+
fs12.writeFileSync(configPath, `${JSON.stringify(config, null, 2)}
|
|
1143
1285
|
`);
|
|
1144
1286
|
writeGeneratedFiles(projectRoot, config, scanResult);
|
|
1145
1287
|
updateGitignore(projectRoot);
|
|
1146
1288
|
setupPreCommitHook(projectRoot);
|
|
1147
1289
|
console.log(`
|
|
1148
|
-
${
|
|
1149
|
-
console.log(` ${
|
|
1150
|
-
console.log(` ${
|
|
1151
|
-
console.log(` ${
|
|
1290
|
+
${import_chalk8.default.bold("Created:")}`);
|
|
1291
|
+
console.log(` ${import_chalk8.default.green("\u2713")} ${CONFIG_FILE4}`);
|
|
1292
|
+
console.log(` ${import_chalk8.default.green("\u2713")} .viberails/context.md`);
|
|
1293
|
+
console.log(` ${import_chalk8.default.green("\u2713")} .viberails/scan-result.json`);
|
|
1152
1294
|
console.log(`
|
|
1153
|
-
${
|
|
1154
|
-
console.log(` 1. Review ${
|
|
1295
|
+
${import_chalk8.default.bold("Next steps:")}`);
|
|
1296
|
+
console.log(` 1. Review ${import_chalk8.default.cyan("viberails.config.json")} and adjust rules`);
|
|
1155
1297
|
console.log(
|
|
1156
|
-
` 2. Commit ${
|
|
1298
|
+
` 2. Commit ${import_chalk8.default.cyan("viberails.config.json")} and ${import_chalk8.default.cyan(".viberails/context.md")}`
|
|
1157
1299
|
);
|
|
1158
|
-
console.log(` 3. Run ${
|
|
1300
|
+
console.log(` 3. Run ${import_chalk8.default.cyan("viberails check")} to verify your project passes`);
|
|
1159
1301
|
}
|
|
1160
1302
|
function updateGitignore(projectRoot) {
|
|
1161
|
-
const gitignorePath =
|
|
1303
|
+
const gitignorePath = path13.join(projectRoot, ".gitignore");
|
|
1162
1304
|
let content = "";
|
|
1163
|
-
if (
|
|
1164
|
-
content =
|
|
1305
|
+
if (fs12.existsSync(gitignorePath)) {
|
|
1306
|
+
content = fs12.readFileSync(gitignorePath, "utf-8");
|
|
1165
1307
|
}
|
|
1166
1308
|
if (!content.includes(".viberails/scan-result.json")) {
|
|
1167
1309
|
const block = "\n# viberails\n.viberails/scan-result.json\n";
|
|
1168
|
-
|
|
1310
|
+
fs12.writeFileSync(gitignorePath, `${content.trimEnd()}
|
|
1169
1311
|
${block}`);
|
|
1170
1312
|
}
|
|
1171
1313
|
}
|
|
1172
|
-
function setupPreCommitHook(projectRoot) {
|
|
1173
|
-
const lefthookPath = path12.join(projectRoot, "lefthook.yml");
|
|
1174
|
-
if (fs11.existsSync(lefthookPath)) {
|
|
1175
|
-
addLefthookPreCommit(lefthookPath);
|
|
1176
|
-
console.log(` ${import_chalk5.default.green("\u2713")} lefthook.yml \u2014 added viberails pre-commit`);
|
|
1177
|
-
return;
|
|
1178
|
-
}
|
|
1179
|
-
const huskyDir = path12.join(projectRoot, ".husky");
|
|
1180
|
-
if (fs11.existsSync(huskyDir)) {
|
|
1181
|
-
writeHuskyPreCommit(huskyDir);
|
|
1182
|
-
console.log(` ${import_chalk5.default.green("\u2713")} .husky/pre-commit \u2014 added viberails check`);
|
|
1183
|
-
return;
|
|
1184
|
-
}
|
|
1185
|
-
const gitDir = path12.join(projectRoot, ".git");
|
|
1186
|
-
if (fs11.existsSync(gitDir)) {
|
|
1187
|
-
const hooksDir = path12.join(gitDir, "hooks");
|
|
1188
|
-
if (!fs11.existsSync(hooksDir)) {
|
|
1189
|
-
fs11.mkdirSync(hooksDir, { recursive: true });
|
|
1190
|
-
}
|
|
1191
|
-
writeGitHookPreCommit(hooksDir);
|
|
1192
|
-
console.log(` ${import_chalk5.default.green("\u2713")} .git/hooks/pre-commit`);
|
|
1193
|
-
}
|
|
1194
|
-
}
|
|
1195
|
-
function writeGitHookPreCommit(hooksDir) {
|
|
1196
|
-
const hookPath = path12.join(hooksDir, "pre-commit");
|
|
1197
|
-
if (fs11.existsSync(hookPath)) {
|
|
1198
|
-
const existing = fs11.readFileSync(hookPath, "utf-8");
|
|
1199
|
-
if (existing.includes("viberails")) return;
|
|
1200
|
-
fs11.writeFileSync(
|
|
1201
|
-
hookPath,
|
|
1202
|
-
`${existing.trimEnd()}
|
|
1203
|
-
|
|
1204
|
-
# viberails check
|
|
1205
|
-
npx viberails check --staged
|
|
1206
|
-
`
|
|
1207
|
-
);
|
|
1208
|
-
return;
|
|
1209
|
-
}
|
|
1210
|
-
const script = [
|
|
1211
|
-
"#!/bin/sh",
|
|
1212
|
-
"# Generated by viberails \u2014 https://viberails.sh",
|
|
1213
|
-
"",
|
|
1214
|
-
"npx viberails check --staged",
|
|
1215
|
-
""
|
|
1216
|
-
].join("\n");
|
|
1217
|
-
fs11.writeFileSync(hookPath, script, { mode: 493 });
|
|
1218
|
-
}
|
|
1219
|
-
function addLefthookPreCommit(lefthookPath) {
|
|
1220
|
-
const content = fs11.readFileSync(lefthookPath, "utf-8");
|
|
1221
|
-
if (content.includes("viberails")) return;
|
|
1222
|
-
const addition = ["", " viberails:", " run: npx viberails check --staged"].join("\n");
|
|
1223
|
-
fs11.writeFileSync(lefthookPath, `${content.trimEnd()}
|
|
1224
|
-
${addition}
|
|
1225
|
-
`);
|
|
1226
|
-
}
|
|
1227
|
-
function writeHuskyPreCommit(huskyDir) {
|
|
1228
|
-
const hookPath = path12.join(huskyDir, "pre-commit");
|
|
1229
|
-
if (fs11.existsSync(hookPath)) {
|
|
1230
|
-
const existing = fs11.readFileSync(hookPath, "utf-8");
|
|
1231
|
-
if (!existing.includes("viberails")) {
|
|
1232
|
-
fs11.writeFileSync(hookPath, `${existing.trimEnd()}
|
|
1233
|
-
npx viberails check --staged
|
|
1234
|
-
`);
|
|
1235
|
-
}
|
|
1236
|
-
return;
|
|
1237
|
-
}
|
|
1238
|
-
fs11.writeFileSync(hookPath, "#!/bin/sh\nnpx viberails check --staged\n", { mode: 493 });
|
|
1239
|
-
}
|
|
1240
1314
|
|
|
1241
1315
|
// src/commands/sync.ts
|
|
1242
|
-
var
|
|
1243
|
-
var
|
|
1316
|
+
var fs13 = __toESM(require("fs"), 1);
|
|
1317
|
+
var path14 = __toESM(require("path"), 1);
|
|
1244
1318
|
var import_config5 = require("@viberails/config");
|
|
1245
1319
|
var import_scanner2 = require("@viberails/scanner");
|
|
1246
|
-
var
|
|
1320
|
+
var import_chalk9 = __toESM(require("chalk"), 1);
|
|
1247
1321
|
var CONFIG_FILE5 = "viberails.config.json";
|
|
1248
1322
|
async function syncCommand(cwd) {
|
|
1249
1323
|
const startDir = cwd ?? process.cwd();
|
|
@@ -1253,23 +1327,23 @@ async function syncCommand(cwd) {
|
|
|
1253
1327
|
"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"
|
|
1254
1328
|
);
|
|
1255
1329
|
}
|
|
1256
|
-
const configPath =
|
|
1330
|
+
const configPath = path14.join(projectRoot, CONFIG_FILE5);
|
|
1257
1331
|
const existing = await (0, import_config5.loadConfig)(configPath);
|
|
1258
|
-
console.log(
|
|
1332
|
+
console.log(import_chalk9.default.dim("Scanning project..."));
|
|
1259
1333
|
const scanResult = await (0, import_scanner2.scan)(projectRoot);
|
|
1260
1334
|
const merged = (0, import_config5.mergeConfig)(existing, scanResult);
|
|
1261
|
-
|
|
1335
|
+
fs13.writeFileSync(configPath, `${JSON.stringify(merged, null, 2)}
|
|
1262
1336
|
`);
|
|
1263
1337
|
writeGeneratedFiles(projectRoot, merged, scanResult);
|
|
1264
1338
|
console.log(`
|
|
1265
|
-
${
|
|
1266
|
-
console.log(` ${
|
|
1267
|
-
console.log(` ${
|
|
1268
|
-
console.log(` ${
|
|
1339
|
+
${import_chalk9.default.bold("Synced:")}`);
|
|
1340
|
+
console.log(` ${import_chalk9.default.green("\u2713")} ${CONFIG_FILE5} \u2014 updated`);
|
|
1341
|
+
console.log(` ${import_chalk9.default.green("\u2713")} .viberails/context.md \u2014 regenerated`);
|
|
1342
|
+
console.log(` ${import_chalk9.default.green("\u2713")} .viberails/scan-result.json \u2014 updated`);
|
|
1269
1343
|
}
|
|
1270
1344
|
|
|
1271
1345
|
// src/index.ts
|
|
1272
|
-
var VERSION = "0.2.
|
|
1346
|
+
var VERSION = "0.2.1";
|
|
1273
1347
|
var program = new import_commander.Command();
|
|
1274
1348
|
program.name("viberails").description("Guardrails for vibe coding").version(VERSION);
|
|
1275
1349
|
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) => {
|
|
@@ -1277,7 +1351,7 @@ program.command("init", { isDefault: true }).description("Scan your project and
|
|
|
1277
1351
|
await initCommand(options);
|
|
1278
1352
|
} catch (err) {
|
|
1279
1353
|
const message = err instanceof Error ? err.message : String(err);
|
|
1280
|
-
console.error(`${
|
|
1354
|
+
console.error(`${import_chalk10.default.red("Error:")} ${message}`);
|
|
1281
1355
|
process.exit(1);
|
|
1282
1356
|
}
|
|
1283
1357
|
});
|
|
@@ -1286,7 +1360,7 @@ program.command("sync").description("Re-scan and update generated files").action
|
|
|
1286
1360
|
await syncCommand();
|
|
1287
1361
|
} catch (err) {
|
|
1288
1362
|
const message = err instanceof Error ? err.message : String(err);
|
|
1289
|
-
console.error(`${
|
|
1363
|
+
console.error(`${import_chalk10.default.red("Error:")} ${message}`);
|
|
1290
1364
|
process.exit(1);
|
|
1291
1365
|
}
|
|
1292
1366
|
});
|
|
@@ -1299,7 +1373,7 @@ program.command("check").description("Check files against enforced rules").optio
|
|
|
1299
1373
|
process.exit(exitCode);
|
|
1300
1374
|
} catch (err) {
|
|
1301
1375
|
const message = err instanceof Error ? err.message : String(err);
|
|
1302
|
-
console.error(`${
|
|
1376
|
+
console.error(`${import_chalk10.default.red("Error:")} ${message}`);
|
|
1303
1377
|
process.exit(1);
|
|
1304
1378
|
}
|
|
1305
1379
|
});
|
|
@@ -1309,7 +1383,7 @@ program.command("fix").description("Auto-fix file naming violations and generate
|
|
|
1309
1383
|
process.exit(exitCode);
|
|
1310
1384
|
} catch (err) {
|
|
1311
1385
|
const message = err instanceof Error ? err.message : String(err);
|
|
1312
|
-
console.error(`${
|
|
1386
|
+
console.error(`${import_chalk10.default.red("Error:")} ${message}`);
|
|
1313
1387
|
process.exit(1);
|
|
1314
1388
|
}
|
|
1315
1389
|
});
|
|
@@ -1318,7 +1392,7 @@ program.command("boundaries").description("Display, infer, or inspect import bou
|
|
|
1318
1392
|
await boundariesCommand(options);
|
|
1319
1393
|
} catch (err) {
|
|
1320
1394
|
const message = err instanceof Error ? err.message : String(err);
|
|
1321
|
-
console.error(`${
|
|
1395
|
+
console.error(`${import_chalk10.default.red("Error:")} ${message}`);
|
|
1322
1396
|
process.exit(1);
|
|
1323
1397
|
}
|
|
1324
1398
|
});
|