viberails 0.2.3 → 0.3.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 +292 -140
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +293 -141
- package/dist/index.js.map +1 -1
- package/package.json +9 -7
package/dist/index.cjs
CHANGED
|
@@ -34,14 +34,14 @@ __export(index_exports, {
|
|
|
34
34
|
VERSION: () => VERSION
|
|
35
35
|
});
|
|
36
36
|
module.exports = __toCommonJS(index_exports);
|
|
37
|
-
var
|
|
37
|
+
var import_chalk11 = __toESM(require("chalk"), 1);
|
|
38
38
|
var import_commander = require("commander");
|
|
39
39
|
|
|
40
40
|
// src/commands/boundaries.ts
|
|
41
41
|
var fs3 = __toESM(require("fs"), 1);
|
|
42
42
|
var path3 = __toESM(require("path"), 1);
|
|
43
43
|
var import_config = require("@viberails/config");
|
|
44
|
-
var
|
|
44
|
+
var import_chalk2 = __toESM(require("chalk"), 1);
|
|
45
45
|
|
|
46
46
|
// src/utils/find-project-root.ts
|
|
47
47
|
var fs = __toESM(require("fs"), 1);
|
|
@@ -62,6 +62,7 @@ function findProjectRoot(startDir) {
|
|
|
62
62
|
|
|
63
63
|
// src/utils/prompt.ts
|
|
64
64
|
var readline = __toESM(require("readline"), 1);
|
|
65
|
+
var import_chalk = __toESM(require("chalk"), 1);
|
|
65
66
|
async function confirm(message) {
|
|
66
67
|
const rl = readline.createInterface({
|
|
67
68
|
input: process.stdin,
|
|
@@ -75,6 +76,29 @@ async function confirm(message) {
|
|
|
75
76
|
});
|
|
76
77
|
});
|
|
77
78
|
}
|
|
79
|
+
async function selectIntegrations(hookManager) {
|
|
80
|
+
const result = { preCommitHook: true, claudeCodeHook: true };
|
|
81
|
+
const hookLabel = hookManager ? `Pre-commit hook (detected: ${hookManager})` : "Pre-commit hook (git hook)";
|
|
82
|
+
console.log("Set up integrations:");
|
|
83
|
+
const rl = readline.createInterface({
|
|
84
|
+
input: process.stdin,
|
|
85
|
+
output: process.stdout
|
|
86
|
+
});
|
|
87
|
+
const askYn = (label, defaultYes) => new Promise((resolve4) => {
|
|
88
|
+
const hint = defaultYes ? "Y/n" : "y/N";
|
|
89
|
+
rl.question(` ${label}? (${hint}) `, (answer) => {
|
|
90
|
+
const trimmed = answer.trim().toLowerCase();
|
|
91
|
+
if (trimmed === "") resolve4(defaultYes);
|
|
92
|
+
else resolve4(trimmed === "y" || trimmed === "yes");
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
console.log(` ${import_chalk.default.dim("Runs viberails check automatically when you commit")}`);
|
|
96
|
+
result.preCommitHook = await askYn(hookLabel, true);
|
|
97
|
+
console.log(` ${import_chalk.default.dim("Checks files against your rules when Claude edits them")}`);
|
|
98
|
+
result.claudeCodeHook = await askYn("Claude Code hook", true);
|
|
99
|
+
rl.close();
|
|
100
|
+
return result;
|
|
101
|
+
}
|
|
78
102
|
|
|
79
103
|
// src/utils/resolve-workspace-packages.ts
|
|
80
104
|
var fs2 = __toESM(require("fs"), 1);
|
|
@@ -131,52 +155,52 @@ async function boundariesCommand(options, cwd) {
|
|
|
131
155
|
}
|
|
132
156
|
function displayRules(config) {
|
|
133
157
|
if (!config.boundaries || config.boundaries.length === 0) {
|
|
134
|
-
console.log(
|
|
135
|
-
console.log(`Run ${
|
|
158
|
+
console.log(import_chalk2.default.yellow("No boundary rules configured."));
|
|
159
|
+
console.log(`Run ${import_chalk2.default.cyan("viberails boundaries --infer")} to generate rules.`);
|
|
136
160
|
return;
|
|
137
161
|
}
|
|
138
162
|
const allowRules = config.boundaries.filter((r) => r.allow);
|
|
139
163
|
const denyRules = config.boundaries.filter((r) => !r.allow);
|
|
140
164
|
console.log(`
|
|
141
|
-
${
|
|
165
|
+
${import_chalk2.default.bold(`Boundary rules (${config.boundaries.length} rules):`)}
|
|
142
166
|
`);
|
|
143
167
|
for (const r of allowRules) {
|
|
144
|
-
console.log(` ${
|
|
168
|
+
console.log(` ${import_chalk2.default.green("\u2713")} ${r.from} \u2192 ${r.to}`);
|
|
145
169
|
}
|
|
146
170
|
for (const r of denyRules) {
|
|
147
|
-
const reason = r.reason ?
|
|
148
|
-
console.log(` ${
|
|
171
|
+
const reason = r.reason ? import_chalk2.default.dim(` (${r.reason})`) : "";
|
|
172
|
+
console.log(` ${import_chalk2.default.red("\u2717")} ${r.from} \u2192 ${r.to}${reason}`);
|
|
149
173
|
}
|
|
150
174
|
console.log(
|
|
151
175
|
`
|
|
152
|
-
Enforcement: ${config.rules.enforceBoundaries ?
|
|
176
|
+
Enforcement: ${config.rules.enforceBoundaries ? import_chalk2.default.green("on") : import_chalk2.default.yellow("off")}`
|
|
153
177
|
);
|
|
154
178
|
}
|
|
155
179
|
async function inferAndDisplay(projectRoot, config, configPath) {
|
|
156
|
-
console.log(
|
|
180
|
+
console.log(import_chalk2.default.dim("Analyzing imports..."));
|
|
157
181
|
const { buildImportGraph, inferBoundaries } = await import("@viberails/graph");
|
|
158
182
|
const packages = config.workspace ? resolveWorkspacePackages(projectRoot, config.workspace) : void 0;
|
|
159
183
|
const graph = await buildImportGraph(projectRoot, {
|
|
160
184
|
packages,
|
|
161
185
|
ignore: config.ignore
|
|
162
186
|
});
|
|
163
|
-
console.log(
|
|
187
|
+
console.log(import_chalk2.default.dim(`${graph.nodes.length} files, ${graph.edges.length} edges`));
|
|
164
188
|
const inferred = inferBoundaries(graph);
|
|
165
189
|
if (inferred.length === 0) {
|
|
166
|
-
console.log(
|
|
190
|
+
console.log(import_chalk2.default.yellow("No boundary rules could be inferred."));
|
|
167
191
|
return;
|
|
168
192
|
}
|
|
169
193
|
const allow = inferred.filter((r) => r.allow);
|
|
170
194
|
const deny = inferred.filter((r) => !r.allow);
|
|
171
195
|
console.log(`
|
|
172
|
-
${
|
|
196
|
+
${import_chalk2.default.bold("Inferred boundary rules:")}
|
|
173
197
|
`);
|
|
174
198
|
for (const r of allow) {
|
|
175
|
-
console.log(` ${
|
|
199
|
+
console.log(` ${import_chalk2.default.green("\u2713")} ${r.from} \u2192 ${r.to}`);
|
|
176
200
|
}
|
|
177
201
|
for (const r of deny) {
|
|
178
|
-
const reason = r.reason ?
|
|
179
|
-
console.log(` ${
|
|
202
|
+
const reason = r.reason ? import_chalk2.default.dim(` (${r.reason})`) : "";
|
|
203
|
+
console.log(` ${import_chalk2.default.red("\u2717")} ${r.from} \u2192 ${r.to}${reason}`);
|
|
180
204
|
}
|
|
181
205
|
console.log(`
|
|
182
206
|
${allow.length} allowed, ${deny.length} denied`);
|
|
@@ -186,11 +210,11 @@ ${import_chalk.default.bold("Inferred boundary rules:")}
|
|
|
186
210
|
config.rules.enforceBoundaries = true;
|
|
187
211
|
fs3.writeFileSync(configPath, `${JSON.stringify(config, null, 2)}
|
|
188
212
|
`);
|
|
189
|
-
console.log(`${
|
|
213
|
+
console.log(`${import_chalk2.default.green("\u2713")} Saved ${inferred.length} rules`);
|
|
190
214
|
}
|
|
191
215
|
}
|
|
192
216
|
async function showGraph(projectRoot, config) {
|
|
193
|
-
console.log(
|
|
217
|
+
console.log(import_chalk2.default.dim("Building import graph..."));
|
|
194
218
|
const { buildImportGraph } = await import("@viberails/graph");
|
|
195
219
|
const packages = config.workspace ? resolveWorkspacePackages(projectRoot, config.workspace) : void 0;
|
|
196
220
|
const graph = await buildImportGraph(projectRoot, {
|
|
@@ -198,20 +222,20 @@ async function showGraph(projectRoot, config) {
|
|
|
198
222
|
ignore: config.ignore
|
|
199
223
|
});
|
|
200
224
|
console.log(`
|
|
201
|
-
${
|
|
225
|
+
${import_chalk2.default.bold("Import dependency graph:")}
|
|
202
226
|
`);
|
|
203
227
|
console.log(` ${graph.nodes.length} files, ${graph.edges.length} imports
|
|
204
228
|
`);
|
|
205
229
|
if (graph.packages.length > 0) {
|
|
206
230
|
for (const pkg of graph.packages) {
|
|
207
231
|
const deps = pkg.internalDeps.length > 0 ? `
|
|
208
|
-
${pkg.internalDeps.map((d) => ` \u2192 ${d}`).join("\n")}` :
|
|
232
|
+
${pkg.internalDeps.map((d) => ` \u2192 ${d}`).join("\n")}` : import_chalk2.default.dim(" (no internal deps)");
|
|
209
233
|
console.log(` ${pkg.name}${deps}`);
|
|
210
234
|
}
|
|
211
235
|
}
|
|
212
236
|
if (graph.cycles.length > 0) {
|
|
213
237
|
console.log(`
|
|
214
|
-
${
|
|
238
|
+
${import_chalk2.default.yellow("Cycles detected:")}`);
|
|
215
239
|
for (const cycle of graph.cycles) {
|
|
216
240
|
const paths = cycle.map((f) => path3.relative(projectRoot, f));
|
|
217
241
|
console.log(` ${paths.join(" \u2192 ")}`);
|
|
@@ -223,7 +247,7 @@ ${import_chalk.default.yellow("Cycles detected:")}`);
|
|
|
223
247
|
var fs6 = __toESM(require("fs"), 1);
|
|
224
248
|
var path6 = __toESM(require("path"), 1);
|
|
225
249
|
var import_config2 = require("@viberails/config");
|
|
226
|
-
var
|
|
250
|
+
var import_chalk3 = __toESM(require("chalk"), 1);
|
|
227
251
|
|
|
228
252
|
// src/commands/check-config.ts
|
|
229
253
|
function resolveConfigForFile(relPath, config) {
|
|
@@ -256,6 +280,7 @@ function resolveIgnoreForFile(relPath, config) {
|
|
|
256
280
|
var import_node_child_process = require("child_process");
|
|
257
281
|
var fs4 = __toESM(require("fs"), 1);
|
|
258
282
|
var path4 = __toESM(require("path"), 1);
|
|
283
|
+
var import_picomatch = __toESM(require("picomatch"), 1);
|
|
259
284
|
var ALWAYS_SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
260
285
|
"node_modules",
|
|
261
286
|
".git",
|
|
@@ -291,25 +316,9 @@ var NAMING_PATTERNS = {
|
|
|
291
316
|
snake_case: /^[a-z][a-z0-9]*(_[a-z0-9]+)*$/
|
|
292
317
|
};
|
|
293
318
|
function isIgnored(relPath, ignorePatterns) {
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
if (startsGlob && endsGlob) {
|
|
298
|
-
const middle = pattern.slice(3, -3);
|
|
299
|
-
if (relPath.startsWith(`${middle}/`) || relPath.includes(`/${middle}/`) || relPath === middle) {
|
|
300
|
-
return true;
|
|
301
|
-
}
|
|
302
|
-
} else if (endsGlob) {
|
|
303
|
-
const prefix = pattern.slice(0, -3);
|
|
304
|
-
if (relPath.startsWith(`${prefix}/`) || relPath === prefix) return true;
|
|
305
|
-
} else if (startsGlob) {
|
|
306
|
-
const suffix = pattern.slice(3);
|
|
307
|
-
if (relPath.endsWith(suffix) || relPath === suffix) return true;
|
|
308
|
-
} else if (relPath === pattern || relPath.startsWith(`${pattern}/`)) {
|
|
309
|
-
return true;
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
return false;
|
|
319
|
+
if (ignorePatterns.length === 0) return false;
|
|
320
|
+
const isMatch = (0, import_picomatch.default)(ignorePatterns, { dot: true });
|
|
321
|
+
return isMatch(relPath);
|
|
313
322
|
}
|
|
314
323
|
function countFileLines(filePath) {
|
|
315
324
|
try {
|
|
@@ -476,12 +485,12 @@ function printGroupedViolations(violations, limit) {
|
|
|
476
485
|
const toShow = group.slice(0, remaining);
|
|
477
486
|
const hidden = group.length - toShow.length;
|
|
478
487
|
for (const v of toShow) {
|
|
479
|
-
const icon = v.severity === "error" ?
|
|
480
|
-
console.log(`${icon} ${
|
|
488
|
+
const icon = v.severity === "error" ? import_chalk3.default.red("\u2717") : import_chalk3.default.yellow("!");
|
|
489
|
+
console.log(`${icon} ${import_chalk3.default.dim(v.rule)} ${v.file}: ${v.message}`);
|
|
481
490
|
}
|
|
482
491
|
totalShown += toShow.length;
|
|
483
492
|
if (hidden > 0) {
|
|
484
|
-
console.log(
|
|
493
|
+
console.log(import_chalk3.default.dim(` ... and ${hidden} more ${rule} violations`));
|
|
485
494
|
}
|
|
486
495
|
}
|
|
487
496
|
}
|
|
@@ -499,13 +508,13 @@ async function checkCommand(options, cwd) {
|
|
|
499
508
|
const startDir = cwd ?? process.cwd();
|
|
500
509
|
const projectRoot = findProjectRoot(startDir);
|
|
501
510
|
if (!projectRoot) {
|
|
502
|
-
console.error(`${
|
|
511
|
+
console.error(`${import_chalk3.default.red("Error:")} No package.json found. Are you in a JS/TS project?`);
|
|
503
512
|
return 1;
|
|
504
513
|
}
|
|
505
514
|
const configPath = path6.join(projectRoot, CONFIG_FILE2);
|
|
506
515
|
if (!fs6.existsSync(configPath)) {
|
|
507
516
|
console.error(
|
|
508
|
-
`${
|
|
517
|
+
`${import_chalk3.default.red("Error:")} No viberails.config.json found. Run \`viberails init\` first.`
|
|
509
518
|
);
|
|
510
519
|
return 1;
|
|
511
520
|
}
|
|
@@ -519,7 +528,7 @@ async function checkCommand(options, cwd) {
|
|
|
519
528
|
filesToCheck = getAllSourceFiles(projectRoot, config);
|
|
520
529
|
}
|
|
521
530
|
if (filesToCheck.length === 0) {
|
|
522
|
-
console.log(`${
|
|
531
|
+
console.log(`${import_chalk3.default.green("\u2713")} No files to check.`);
|
|
523
532
|
return 0;
|
|
524
533
|
}
|
|
525
534
|
const violations = [];
|
|
@@ -581,10 +590,20 @@ async function checkCommand(options, cwd) {
|
|
|
581
590
|
});
|
|
582
591
|
}
|
|
583
592
|
const elapsed = Date.now() - startTime;
|
|
584
|
-
console.log(
|
|
593
|
+
console.log(import_chalk3.default.dim(` Boundary check: ${graph.nodes.length} files in ${elapsed}ms`));
|
|
594
|
+
}
|
|
595
|
+
if (options.format === "json") {
|
|
596
|
+
console.log(
|
|
597
|
+
JSON.stringify({
|
|
598
|
+
violations,
|
|
599
|
+
checkedFiles: filesToCheck.length,
|
|
600
|
+
enforcement: config.enforcement
|
|
601
|
+
})
|
|
602
|
+
);
|
|
603
|
+
return config.enforcement === "enforce" && violations.length > 0 ? 1 : 0;
|
|
585
604
|
}
|
|
586
605
|
if (violations.length === 0) {
|
|
587
|
-
console.log(`${
|
|
606
|
+
console.log(`${import_chalk3.default.green("\u2713")} ${filesToCheck.length} files checked \u2014 no violations`);
|
|
588
607
|
return 0;
|
|
589
608
|
}
|
|
590
609
|
if (!options.quiet) {
|
|
@@ -592,7 +611,7 @@ async function checkCommand(options, cwd) {
|
|
|
592
611
|
}
|
|
593
612
|
printSummary(violations);
|
|
594
613
|
if (config.enforcement === "enforce") {
|
|
595
|
-
console.log(
|
|
614
|
+
console.log(import_chalk3.default.red("Fix violations before committing."));
|
|
596
615
|
return 1;
|
|
597
616
|
}
|
|
598
617
|
return 0;
|
|
@@ -602,23 +621,23 @@ async function checkCommand(options, cwd) {
|
|
|
602
621
|
var fs9 = __toESM(require("fs"), 1);
|
|
603
622
|
var path10 = __toESM(require("path"), 1);
|
|
604
623
|
var import_config3 = require("@viberails/config");
|
|
605
|
-
var
|
|
624
|
+
var import_chalk5 = __toESM(require("chalk"), 1);
|
|
606
625
|
|
|
607
626
|
// src/commands/fix-helpers.ts
|
|
608
627
|
var import_node_child_process2 = require("child_process");
|
|
609
628
|
var import_node_readline = require("readline");
|
|
610
|
-
var
|
|
629
|
+
var import_chalk4 = __toESM(require("chalk"), 1);
|
|
611
630
|
function printPlan(renames, stubs) {
|
|
612
631
|
if (renames.length > 0) {
|
|
613
|
-
console.log(
|
|
632
|
+
console.log(import_chalk4.default.bold("\nFile renames:"));
|
|
614
633
|
for (const r of renames) {
|
|
615
|
-
console.log(` ${
|
|
634
|
+
console.log(` ${import_chalk4.default.red(r.oldPath)} \u2192 ${import_chalk4.default.green(r.newPath)}`);
|
|
616
635
|
}
|
|
617
636
|
}
|
|
618
637
|
if (stubs.length > 0) {
|
|
619
|
-
console.log(
|
|
638
|
+
console.log(import_chalk4.default.bold("\nTest stubs to create:"));
|
|
620
639
|
for (const s of stubs) {
|
|
621
|
-
console.log(` ${
|
|
640
|
+
console.log(` ${import_chalk4.default.green("+")} ${s.path}`);
|
|
622
641
|
}
|
|
623
642
|
}
|
|
624
643
|
}
|
|
@@ -682,7 +701,8 @@ async function updateImportsAfterRenames(renames, projectRoot) {
|
|
|
682
701
|
const extensions = ["", ".ts", ".tsx", ".js", ".jsx", "/index.ts", "/index.tsx", "/index.js"];
|
|
683
702
|
for (const sourceFile of project.getSourceFiles()) {
|
|
684
703
|
const filePath = sourceFile.getFilePath();
|
|
685
|
-
|
|
704
|
+
const segments = filePath.split(path7.sep);
|
|
705
|
+
if (segments.includes("node_modules") || segments.includes("dist")) continue;
|
|
686
706
|
const fileDir = path7.dirname(filePath);
|
|
687
707
|
for (const decl of sourceFile.getImportDeclarations()) {
|
|
688
708
|
const specifier = decl.getModuleSpecifierValue();
|
|
@@ -868,13 +888,13 @@ async function fixCommand(options, cwd) {
|
|
|
868
888
|
const startDir = cwd ?? process.cwd();
|
|
869
889
|
const projectRoot = findProjectRoot(startDir);
|
|
870
890
|
if (!projectRoot) {
|
|
871
|
-
console.error(`${
|
|
891
|
+
console.error(`${import_chalk5.default.red("Error:")} No package.json found. Are you in a JS/TS project?`);
|
|
872
892
|
return 1;
|
|
873
893
|
}
|
|
874
894
|
const configPath = path10.join(projectRoot, CONFIG_FILE3);
|
|
875
895
|
if (!fs9.existsSync(configPath)) {
|
|
876
896
|
console.error(
|
|
877
|
-
`${
|
|
897
|
+
`${import_chalk5.default.red("Error:")} No viberails.config.json found. Run \`viberails init\` first.`
|
|
878
898
|
);
|
|
879
899
|
return 1;
|
|
880
900
|
}
|
|
@@ -883,7 +903,7 @@ async function fixCommand(options, cwd) {
|
|
|
883
903
|
const isDirty = checkGitDirty(projectRoot);
|
|
884
904
|
if (isDirty) {
|
|
885
905
|
console.log(
|
|
886
|
-
|
|
906
|
+
import_chalk5.default.yellow("Warning: You have uncommitted changes. Consider committing first.")
|
|
887
907
|
);
|
|
888
908
|
}
|
|
889
909
|
}
|
|
@@ -913,12 +933,12 @@ async function fixCommand(options, cwd) {
|
|
|
913
933
|
}
|
|
914
934
|
}
|
|
915
935
|
if (dedupedRenames.length === 0 && testStubs.length === 0) {
|
|
916
|
-
console.log(`${
|
|
936
|
+
console.log(`${import_chalk5.default.green("\u2713")} No fixable violations found.`);
|
|
917
937
|
return 0;
|
|
918
938
|
}
|
|
919
939
|
printPlan(dedupedRenames, testStubs);
|
|
920
940
|
if (options.dryRun) {
|
|
921
|
-
console.log(
|
|
941
|
+
console.log(import_chalk5.default.dim("\nDry run \u2014 no changes applied."));
|
|
922
942
|
return 0;
|
|
923
943
|
}
|
|
924
944
|
if (!options.yes) {
|
|
@@ -949,15 +969,15 @@ async function fixCommand(options, cwd) {
|
|
|
949
969
|
}
|
|
950
970
|
console.log("");
|
|
951
971
|
if (renameCount > 0) {
|
|
952
|
-
console.log(`${
|
|
972
|
+
console.log(`${import_chalk5.default.green("\u2713")} Renamed ${renameCount} file${renameCount > 1 ? "s" : ""}`);
|
|
953
973
|
}
|
|
954
974
|
if (importUpdateCount > 0) {
|
|
955
975
|
console.log(
|
|
956
|
-
`${
|
|
976
|
+
`${import_chalk5.default.green("\u2713")} Updated ${importUpdateCount} import${importUpdateCount > 1 ? "s" : ""}`
|
|
957
977
|
);
|
|
958
978
|
}
|
|
959
979
|
if (stubCount > 0) {
|
|
960
|
-
console.log(`${
|
|
980
|
+
console.log(`${import_chalk5.default.green("\u2713")} Generated ${stubCount} test stub${stubCount > 1 ? "s" : ""}`);
|
|
961
981
|
}
|
|
962
982
|
return 0;
|
|
963
983
|
}
|
|
@@ -967,11 +987,11 @@ var fs12 = __toESM(require("fs"), 1);
|
|
|
967
987
|
var path13 = __toESM(require("path"), 1);
|
|
968
988
|
var import_config4 = require("@viberails/config");
|
|
969
989
|
var import_scanner = require("@viberails/scanner");
|
|
970
|
-
var
|
|
990
|
+
var import_chalk9 = __toESM(require("chalk"), 1);
|
|
971
991
|
|
|
972
992
|
// src/display.ts
|
|
973
993
|
var import_types3 = require("@viberails/types");
|
|
974
|
-
var
|
|
994
|
+
var import_chalk7 = __toESM(require("chalk"), 1);
|
|
975
995
|
|
|
976
996
|
// src/display-helpers.ts
|
|
977
997
|
var import_types = require("@viberails/types");
|
|
@@ -1024,7 +1044,7 @@ function formatRoleGroup(group) {
|
|
|
1024
1044
|
|
|
1025
1045
|
// src/display-monorepo.ts
|
|
1026
1046
|
var import_types2 = require("@viberails/types");
|
|
1027
|
-
var
|
|
1047
|
+
var import_chalk6 = __toESM(require("chalk"), 1);
|
|
1028
1048
|
function formatPackageSummary(pkg) {
|
|
1029
1049
|
const parts = [];
|
|
1030
1050
|
if (pkg.stack.framework) {
|
|
@@ -1040,19 +1060,19 @@ function formatPackageSummary(pkg) {
|
|
|
1040
1060
|
function displayMonorepoResults(scanResult) {
|
|
1041
1061
|
const { stack, packages } = scanResult;
|
|
1042
1062
|
console.log(`
|
|
1043
|
-
${
|
|
1044
|
-
console.log(` ${
|
|
1063
|
+
${import_chalk6.default.bold(`Detected: (monorepo, ${packages.length} packages)`)}`);
|
|
1064
|
+
console.log(` ${import_chalk6.default.green("\u2713")} ${formatItem(stack.language)}`);
|
|
1045
1065
|
if (stack.packageManager) {
|
|
1046
|
-
console.log(` ${
|
|
1066
|
+
console.log(` ${import_chalk6.default.green("\u2713")} ${formatItem(stack.packageManager)}`);
|
|
1047
1067
|
}
|
|
1048
1068
|
if (stack.linter) {
|
|
1049
|
-
console.log(` ${
|
|
1069
|
+
console.log(` ${import_chalk6.default.green("\u2713")} ${formatItem(stack.linter)}`);
|
|
1050
1070
|
}
|
|
1051
1071
|
if (stack.formatter) {
|
|
1052
|
-
console.log(` ${
|
|
1072
|
+
console.log(` ${import_chalk6.default.green("\u2713")} ${formatItem(stack.formatter)}`);
|
|
1053
1073
|
}
|
|
1054
1074
|
if (stack.testRunner) {
|
|
1055
|
-
console.log(` ${
|
|
1075
|
+
console.log(` ${import_chalk6.default.green("\u2713")} ${formatItem(stack.testRunner)}`);
|
|
1056
1076
|
}
|
|
1057
1077
|
console.log("");
|
|
1058
1078
|
for (const pkg of packages) {
|
|
@@ -1063,13 +1083,13 @@ ${import_chalk5.default.bold(`Detected: (monorepo, ${packages.length} packages)`
|
|
|
1063
1083
|
);
|
|
1064
1084
|
if (packagesWithDirs.length > 0) {
|
|
1065
1085
|
console.log(`
|
|
1066
|
-
${
|
|
1086
|
+
${import_chalk6.default.bold("Structure:")}`);
|
|
1067
1087
|
for (const pkg of packagesWithDirs) {
|
|
1068
1088
|
const groups = groupByRole(pkg.structure.directories);
|
|
1069
1089
|
if (groups.length === 0) continue;
|
|
1070
1090
|
console.log(` ${pkg.relativePath}:`);
|
|
1071
1091
|
for (const group of groups) {
|
|
1072
|
-
console.log(` ${
|
|
1092
|
+
console.log(` ${import_chalk6.default.green("\u2713")} ${formatRoleGroup(group)}`);
|
|
1073
1093
|
}
|
|
1074
1094
|
}
|
|
1075
1095
|
}
|
|
@@ -1100,7 +1120,7 @@ function displayConventions(scanResult) {
|
|
|
1100
1120
|
const conventionEntries = Object.entries(scanResult.conventions);
|
|
1101
1121
|
if (conventionEntries.length === 0) return;
|
|
1102
1122
|
console.log(`
|
|
1103
|
-
${
|
|
1123
|
+
${import_chalk7.default.bold("Conventions:")}`);
|
|
1104
1124
|
for (const [key, convention] of conventionEntries) {
|
|
1105
1125
|
if (convention.confidence === "low") continue;
|
|
1106
1126
|
const label = CONVENTION_LABELS[key] ?? key;
|
|
@@ -1108,19 +1128,19 @@ ${import_chalk6.default.bold("Conventions:")}`);
|
|
|
1108
1128
|
const pkgValues = scanResult.packages.filter((pkg) => pkg.conventions[key] && pkg.conventions[key].confidence !== "low").map((pkg) => ({ relativePath: pkg.relativePath, convention: pkg.conventions[key] }));
|
|
1109
1129
|
const allSame = pkgValues.every((pv) => pv.convention.value === convention.value);
|
|
1110
1130
|
if (allSame || pkgValues.length <= 1) {
|
|
1111
|
-
const ind = convention.confidence === "high" ?
|
|
1112
|
-
const detail =
|
|
1131
|
+
const ind = convention.confidence === "high" ? import_chalk7.default.green("\u2713") : import_chalk7.default.yellow("~");
|
|
1132
|
+
const detail = import_chalk7.default.dim(`(${confidenceLabel(convention)})`);
|
|
1113
1133
|
console.log(` ${ind} ${label}: ${convention.value} ${detail}`);
|
|
1114
1134
|
} else {
|
|
1115
|
-
console.log(` ${
|
|
1135
|
+
console.log(` ${import_chalk7.default.yellow("~")} ${label}: varies by package`);
|
|
1116
1136
|
for (const pv of pkgValues) {
|
|
1117
1137
|
const pct = Math.round(pv.convention.consistency);
|
|
1118
1138
|
console.log(` ${pv.relativePath}: ${pv.convention.value} (${pct}%)`);
|
|
1119
1139
|
}
|
|
1120
1140
|
}
|
|
1121
1141
|
} else {
|
|
1122
|
-
const ind = convention.confidence === "high" ?
|
|
1123
|
-
const detail =
|
|
1142
|
+
const ind = convention.confidence === "high" ? import_chalk7.default.green("\u2713") : import_chalk7.default.yellow("~");
|
|
1143
|
+
const detail = import_chalk7.default.dim(`(${confidenceLabel(convention)})`);
|
|
1124
1144
|
console.log(` ${ind} ${label}: ${convention.value} ${detail}`);
|
|
1125
1145
|
}
|
|
1126
1146
|
}
|
|
@@ -1128,7 +1148,7 @@ ${import_chalk6.default.bold("Conventions:")}`);
|
|
|
1128
1148
|
function displaySummarySection(scanResult) {
|
|
1129
1149
|
const pkgCount = scanResult.packages.length > 1 ? scanResult.packages.length : void 0;
|
|
1130
1150
|
console.log(`
|
|
1131
|
-
${
|
|
1151
|
+
${import_chalk7.default.bold("Summary:")}`);
|
|
1132
1152
|
console.log(` ${formatSummary(scanResult.statistics, pkgCount)}`);
|
|
1133
1153
|
const ext = formatExtensions(scanResult.statistics.filesByExtension);
|
|
1134
1154
|
if (ext) {
|
|
@@ -1142,46 +1162,84 @@ function displayScanResults(scanResult) {
|
|
|
1142
1162
|
}
|
|
1143
1163
|
const { stack } = scanResult;
|
|
1144
1164
|
console.log(`
|
|
1145
|
-
${
|
|
1165
|
+
${import_chalk7.default.bold("Detected:")}`);
|
|
1146
1166
|
if (stack.framework) {
|
|
1147
|
-
console.log(` ${
|
|
1167
|
+
console.log(` ${import_chalk7.default.green("\u2713")} ${formatItem(stack.framework, import_types3.FRAMEWORK_NAMES)}`);
|
|
1148
1168
|
}
|
|
1149
|
-
console.log(` ${
|
|
1169
|
+
console.log(` ${import_chalk7.default.green("\u2713")} ${formatItem(stack.language)}`);
|
|
1150
1170
|
if (stack.styling) {
|
|
1151
|
-
console.log(` ${
|
|
1171
|
+
console.log(` ${import_chalk7.default.green("\u2713")} ${formatItem(stack.styling, import_types3.STYLING_NAMES)}`);
|
|
1152
1172
|
}
|
|
1153
1173
|
if (stack.backend) {
|
|
1154
|
-
console.log(` ${
|
|
1174
|
+
console.log(` ${import_chalk7.default.green("\u2713")} ${formatItem(stack.backend, import_types3.FRAMEWORK_NAMES)}`);
|
|
1175
|
+
}
|
|
1176
|
+
if (stack.orm) {
|
|
1177
|
+
console.log(` ${import_chalk7.default.green("\u2713")} ${formatItem(stack.orm, import_types3.ORM_NAMES)}`);
|
|
1155
1178
|
}
|
|
1156
1179
|
if (stack.linter) {
|
|
1157
|
-
console.log(` ${
|
|
1180
|
+
console.log(` ${import_chalk7.default.green("\u2713")} ${formatItem(stack.linter)}`);
|
|
1158
1181
|
}
|
|
1159
1182
|
if (stack.formatter) {
|
|
1160
|
-
console.log(` ${
|
|
1183
|
+
console.log(` ${import_chalk7.default.green("\u2713")} ${formatItem(stack.formatter)}`);
|
|
1161
1184
|
}
|
|
1162
1185
|
if (stack.testRunner) {
|
|
1163
|
-
console.log(` ${
|
|
1186
|
+
console.log(` ${import_chalk7.default.green("\u2713")} ${formatItem(stack.testRunner)}`);
|
|
1164
1187
|
}
|
|
1165
1188
|
if (stack.packageManager) {
|
|
1166
|
-
console.log(` ${
|
|
1189
|
+
console.log(` ${import_chalk7.default.green("\u2713")} ${formatItem(stack.packageManager)}`);
|
|
1167
1190
|
}
|
|
1168
1191
|
if (stack.libraries.length > 0) {
|
|
1169
1192
|
for (const lib of stack.libraries) {
|
|
1170
|
-
console.log(` ${
|
|
1193
|
+
console.log(` ${import_chalk7.default.green("\u2713")} ${formatItem(lib, import_types3.LIBRARY_NAMES)}`);
|
|
1171
1194
|
}
|
|
1172
1195
|
}
|
|
1173
1196
|
const groups = groupByRole(scanResult.structure.directories);
|
|
1174
1197
|
if (groups.length > 0) {
|
|
1175
1198
|
console.log(`
|
|
1176
|
-
${
|
|
1199
|
+
${import_chalk7.default.bold("Structure:")}`);
|
|
1177
1200
|
for (const group of groups) {
|
|
1178
|
-
console.log(` ${
|
|
1201
|
+
console.log(` ${import_chalk7.default.green("\u2713")} ${formatRoleGroup(group)}`);
|
|
1179
1202
|
}
|
|
1180
1203
|
}
|
|
1181
1204
|
displayConventions(scanResult);
|
|
1182
1205
|
displaySummarySection(scanResult);
|
|
1183
1206
|
console.log("");
|
|
1184
1207
|
}
|
|
1208
|
+
function getConventionStr(cv) {
|
|
1209
|
+
return typeof cv === "string" ? cv : cv.value;
|
|
1210
|
+
}
|
|
1211
|
+
function displayRulesPreview(config) {
|
|
1212
|
+
console.log(`${import_chalk7.default.bold("Rules:")}`);
|
|
1213
|
+
console.log(` ${import_chalk7.default.dim("\u2022")} Max file size: ${config.rules.maxFileLines} lines`);
|
|
1214
|
+
if (config.rules.requireTests && config.structure.testPattern) {
|
|
1215
|
+
console.log(
|
|
1216
|
+
` ${import_chalk7.default.dim("\u2022")} Require test files: yes (${config.structure.testPattern})`
|
|
1217
|
+
);
|
|
1218
|
+
} else if (config.rules.requireTests) {
|
|
1219
|
+
console.log(` ${import_chalk7.default.dim("\u2022")} Require test files: yes`);
|
|
1220
|
+
} else {
|
|
1221
|
+
console.log(` ${import_chalk7.default.dim("\u2022")} Require test files: no`);
|
|
1222
|
+
}
|
|
1223
|
+
if (config.rules.enforceNaming && config.conventions.fileNaming) {
|
|
1224
|
+
console.log(
|
|
1225
|
+
` ${import_chalk7.default.dim("\u2022")} Enforce file naming: ${getConventionStr(config.conventions.fileNaming)}`
|
|
1226
|
+
);
|
|
1227
|
+
} else {
|
|
1228
|
+
console.log(` ${import_chalk7.default.dim("\u2022")} Enforce file naming: no`);
|
|
1229
|
+
}
|
|
1230
|
+
console.log(
|
|
1231
|
+
` ${import_chalk7.default.dim("\u2022")} Enforce boundaries: ${config.rules.enforceBoundaries ? "yes" : "no"}`
|
|
1232
|
+
);
|
|
1233
|
+
console.log("");
|
|
1234
|
+
if (config.enforcement === "enforce") {
|
|
1235
|
+
console.log(`${import_chalk7.default.bold("Enforcement mode:")} enforce (violations will block commits)`);
|
|
1236
|
+
} else {
|
|
1237
|
+
console.log(
|
|
1238
|
+
`${import_chalk7.default.bold("Enforcement mode:")} warn (violations shown but won't block commits)`
|
|
1239
|
+
);
|
|
1240
|
+
}
|
|
1241
|
+
console.log("");
|
|
1242
|
+
}
|
|
1185
1243
|
|
|
1186
1244
|
// src/utils/write-generated-files.ts
|
|
1187
1245
|
var fs10 = __toESM(require("fs"), 1);
|
|
@@ -1212,18 +1270,18 @@ function writeGeneratedFiles(projectRoot, config, scanResult) {
|
|
|
1212
1270
|
// src/commands/init-hooks.ts
|
|
1213
1271
|
var fs11 = __toESM(require("fs"), 1);
|
|
1214
1272
|
var path12 = __toESM(require("path"), 1);
|
|
1215
|
-
var
|
|
1273
|
+
var import_chalk8 = __toESM(require("chalk"), 1);
|
|
1216
1274
|
function setupPreCommitHook(projectRoot) {
|
|
1217
1275
|
const lefthookPath = path12.join(projectRoot, "lefthook.yml");
|
|
1218
1276
|
if (fs11.existsSync(lefthookPath)) {
|
|
1219
1277
|
addLefthookPreCommit(lefthookPath);
|
|
1220
|
-
console.log(` ${
|
|
1278
|
+
console.log(` ${import_chalk8.default.green("\u2713")} lefthook.yml \u2014 added viberails pre-commit`);
|
|
1221
1279
|
return;
|
|
1222
1280
|
}
|
|
1223
1281
|
const huskyDir = path12.join(projectRoot, ".husky");
|
|
1224
1282
|
if (fs11.existsSync(huskyDir)) {
|
|
1225
1283
|
writeHuskyPreCommit(huskyDir);
|
|
1226
|
-
console.log(` ${
|
|
1284
|
+
console.log(` ${import_chalk8.default.green("\u2713")} .husky/pre-commit \u2014 added viberails check`);
|
|
1227
1285
|
return;
|
|
1228
1286
|
}
|
|
1229
1287
|
const gitDir = path12.join(projectRoot, ".git");
|
|
@@ -1233,7 +1291,7 @@ function setupPreCommitHook(projectRoot) {
|
|
|
1233
1291
|
fs11.mkdirSync(hooksDir, { recursive: true });
|
|
1234
1292
|
}
|
|
1235
1293
|
writeGitHookPreCommit(hooksDir);
|
|
1236
|
-
console.log(` ${
|
|
1294
|
+
console.log(` ${import_chalk8.default.green("\u2713")} .git/hooks/pre-commit`);
|
|
1237
1295
|
}
|
|
1238
1296
|
}
|
|
1239
1297
|
function writeGitHookPreCommit(hooksDir) {
|
|
@@ -1263,10 +1321,72 @@ npx viberails check --staged
|
|
|
1263
1321
|
function addLefthookPreCommit(lefthookPath) {
|
|
1264
1322
|
const content = fs11.readFileSync(lefthookPath, "utf-8");
|
|
1265
1323
|
if (content.includes("viberails")) return;
|
|
1266
|
-
const
|
|
1267
|
-
|
|
1268
|
-
|
|
1324
|
+
const hasPreCommit = /^pre-commit:/m.test(content);
|
|
1325
|
+
if (hasPreCommit) {
|
|
1326
|
+
const commandBlock = ["", " viberails:", " run: npx viberails check --staged"].join(
|
|
1327
|
+
"\n"
|
|
1328
|
+
);
|
|
1329
|
+
const updated = `${content.trimEnd()}
|
|
1330
|
+
${commandBlock}
|
|
1331
|
+
`;
|
|
1332
|
+
fs11.writeFileSync(lefthookPath, updated);
|
|
1333
|
+
} else {
|
|
1334
|
+
const section = [
|
|
1335
|
+
"",
|
|
1336
|
+
"pre-commit:",
|
|
1337
|
+
" commands:",
|
|
1338
|
+
" viberails:",
|
|
1339
|
+
" run: npx viberails check --staged"
|
|
1340
|
+
].join("\n");
|
|
1341
|
+
fs11.writeFileSync(lefthookPath, `${content.trimEnd()}
|
|
1342
|
+
${section}
|
|
1269
1343
|
`);
|
|
1344
|
+
}
|
|
1345
|
+
}
|
|
1346
|
+
function detectHookManager(projectRoot) {
|
|
1347
|
+
if (fs11.existsSync(path12.join(projectRoot, "lefthook.yml"))) return "Lefthook";
|
|
1348
|
+
if (fs11.existsSync(path12.join(projectRoot, ".husky"))) return "Husky";
|
|
1349
|
+
if (fs11.existsSync(path12.join(projectRoot, ".git"))) return "git hook";
|
|
1350
|
+
return void 0;
|
|
1351
|
+
}
|
|
1352
|
+
function setupClaudeCodeHook(projectRoot) {
|
|
1353
|
+
const claudeDir = path12.join(projectRoot, ".claude");
|
|
1354
|
+
if (!fs11.existsSync(claudeDir)) {
|
|
1355
|
+
fs11.mkdirSync(claudeDir, { recursive: true });
|
|
1356
|
+
}
|
|
1357
|
+
const settingsPath = path12.join(claudeDir, "settings.json");
|
|
1358
|
+
let settings = {};
|
|
1359
|
+
if (fs11.existsSync(settingsPath)) {
|
|
1360
|
+
try {
|
|
1361
|
+
settings = JSON.parse(fs11.readFileSync(settingsPath, "utf-8"));
|
|
1362
|
+
} catch {
|
|
1363
|
+
console.warn(
|
|
1364
|
+
` ${import_chalk8.default.yellow("!")} .claude/settings.json contains invalid JSON \u2014 resetting to add hook`
|
|
1365
|
+
);
|
|
1366
|
+
settings = {};
|
|
1367
|
+
}
|
|
1368
|
+
}
|
|
1369
|
+
const hooks = settings.hooks ?? {};
|
|
1370
|
+
const existing = hooks.PostToolUse ?? [];
|
|
1371
|
+
if (existing.some((h) => JSON.stringify(h).includes("viberails"))) return;
|
|
1372
|
+
const extractFile = `node -e "try{process.stdout.write(JSON.parse(require('fs').readFileSync(0,'utf8')).tool_input?.file_path??'')}catch{}"`;
|
|
1373
|
+
const hookCommand = `FILE=$(${extractFile}) && [ -n "$FILE" ] && npx viberails check --files "$FILE" --format json; exit 0`;
|
|
1374
|
+
hooks.PostToolUse = [
|
|
1375
|
+
...existing,
|
|
1376
|
+
{
|
|
1377
|
+
matcher: "Edit|Write",
|
|
1378
|
+
hooks: [
|
|
1379
|
+
{
|
|
1380
|
+
type: "command",
|
|
1381
|
+
command: hookCommand
|
|
1382
|
+
}
|
|
1383
|
+
]
|
|
1384
|
+
}
|
|
1385
|
+
];
|
|
1386
|
+
settings.hooks = hooks;
|
|
1387
|
+
fs11.writeFileSync(settingsPath, `${JSON.stringify(settings, null, 2)}
|
|
1388
|
+
`);
|
|
1389
|
+
console.log(` ${import_chalk8.default.green("\u2713")} .claude/settings.json \u2014 added viberails PostToolUse hook`);
|
|
1270
1390
|
}
|
|
1271
1391
|
function writeHuskyPreCommit(huskyDir) {
|
|
1272
1392
|
const hookPath = path12.join(huskyDir, "pre-commit");
|
|
@@ -1307,36 +1427,38 @@ async function initCommand(options, cwd) {
|
|
|
1307
1427
|
const configPath = path13.join(projectRoot, CONFIG_FILE4);
|
|
1308
1428
|
if (fs12.existsSync(configPath)) {
|
|
1309
1429
|
console.log(
|
|
1310
|
-
|
|
1430
|
+
import_chalk9.default.yellow("!") + " viberails is already initialized in this project.\n Run " + import_chalk9.default.cyan("viberails sync") + " to update the generated files."
|
|
1311
1431
|
);
|
|
1312
1432
|
return;
|
|
1313
1433
|
}
|
|
1314
|
-
console.log(
|
|
1434
|
+
console.log(import_chalk9.default.dim("Scanning project..."));
|
|
1315
1435
|
const scanResult = await (0, import_scanner.scan)(projectRoot);
|
|
1436
|
+
const config = (0, import_config4.generateConfig)(scanResult);
|
|
1437
|
+
if (options.yes) {
|
|
1438
|
+
config.conventions = filterHighConfidence(config.conventions);
|
|
1439
|
+
}
|
|
1316
1440
|
displayScanResults(scanResult);
|
|
1317
1441
|
if (scanResult.statistics.totalFiles === 0) {
|
|
1318
1442
|
console.log(
|
|
1319
|
-
|
|
1443
|
+
import_chalk9.default.yellow("!") + " No source files detected. viberails will generate context with minimal content.\n Run " + import_chalk9.default.cyan("viberails sync") + " after adding source files.\n"
|
|
1320
1444
|
);
|
|
1321
1445
|
}
|
|
1446
|
+
displayRulesPreview(config);
|
|
1322
1447
|
if (!options.yes) {
|
|
1323
|
-
const accepted = await confirm("
|
|
1448
|
+
const accepted = await confirm("Proceed with these settings?");
|
|
1324
1449
|
if (!accepted) {
|
|
1325
1450
|
console.log("Aborted.");
|
|
1326
1451
|
return;
|
|
1327
1452
|
}
|
|
1328
1453
|
}
|
|
1329
|
-
const config = (0, import_config4.generateConfig)(scanResult);
|
|
1330
|
-
if (options.yes) {
|
|
1331
|
-
config.conventions = filterHighConfidence(config.conventions);
|
|
1332
|
-
}
|
|
1333
1454
|
if (config.workspace && config.workspace.packages.length > 0) {
|
|
1334
1455
|
let shouldInfer = options.yes;
|
|
1335
1456
|
if (!options.yes) {
|
|
1457
|
+
console.log(import_chalk9.default.dim(" Scans imports between packages to suggest dependency rules"));
|
|
1336
1458
|
shouldInfer = await confirm("Infer boundary rules from import patterns?");
|
|
1337
1459
|
}
|
|
1338
1460
|
if (shouldInfer) {
|
|
1339
|
-
console.log(
|
|
1461
|
+
console.log(import_chalk9.default.dim("Building import graph..."));
|
|
1340
1462
|
const { buildImportGraph, inferBoundaries } = await import("@viberails/graph");
|
|
1341
1463
|
const packages = resolveWorkspacePackages(projectRoot, config.workspace);
|
|
1342
1464
|
const graph = await buildImportGraph(projectRoot, { packages, ignore: config.ignore });
|
|
@@ -1344,27 +1466,43 @@ async function initCommand(options, cwd) {
|
|
|
1344
1466
|
if (inferred.length > 0) {
|
|
1345
1467
|
config.boundaries = inferred;
|
|
1346
1468
|
config.rules.enforceBoundaries = true;
|
|
1347
|
-
console.log(` ${
|
|
1469
|
+
console.log(` ${import_chalk9.default.green("\u2713")} Inferred ${inferred.length} boundary rules`);
|
|
1348
1470
|
}
|
|
1349
1471
|
}
|
|
1350
1472
|
}
|
|
1473
|
+
const hookManager = detectHookManager(projectRoot);
|
|
1474
|
+
let integrations = { preCommitHook: true, claudeCodeHook: true };
|
|
1475
|
+
if (!options.yes) {
|
|
1476
|
+
console.log("");
|
|
1477
|
+
integrations = await selectIntegrations(hookManager);
|
|
1478
|
+
}
|
|
1351
1479
|
fs12.writeFileSync(configPath, `${JSON.stringify(config, null, 2)}
|
|
1352
1480
|
`);
|
|
1353
1481
|
writeGeneratedFiles(projectRoot, config, scanResult);
|
|
1354
1482
|
updateGitignore(projectRoot);
|
|
1355
|
-
setupPreCommitHook(projectRoot);
|
|
1356
1483
|
console.log(`
|
|
1357
|
-
${
|
|
1358
|
-
console.log(` ${
|
|
1359
|
-
console.log(` ${
|
|
1360
|
-
console.log(` ${
|
|
1484
|
+
${import_chalk9.default.bold("Created:")}`);
|
|
1485
|
+
console.log(` ${import_chalk9.default.green("\u2713")} ${CONFIG_FILE4}`);
|
|
1486
|
+
console.log(` ${import_chalk9.default.green("\u2713")} .viberails/context.md`);
|
|
1487
|
+
console.log(` ${import_chalk9.default.green("\u2713")} .viberails/scan-result.json`);
|
|
1488
|
+
if (integrations.preCommitHook) {
|
|
1489
|
+
setupPreCommitHook(projectRoot);
|
|
1490
|
+
}
|
|
1491
|
+
if (integrations.claudeCodeHook) {
|
|
1492
|
+
setupClaudeCodeHook(projectRoot);
|
|
1493
|
+
}
|
|
1494
|
+
const filesToCommit = [
|
|
1495
|
+
`${import_chalk9.default.cyan("viberails.config.json")}`,
|
|
1496
|
+
import_chalk9.default.cyan(".viberails/context.md")
|
|
1497
|
+
];
|
|
1498
|
+
if (integrations.claudeCodeHook) {
|
|
1499
|
+
filesToCommit.push(import_chalk9.default.cyan(".claude/settings.json"));
|
|
1500
|
+
}
|
|
1361
1501
|
console.log(`
|
|
1362
|
-
${
|
|
1363
|
-
console.log(` 1. Review ${
|
|
1364
|
-
console.log(
|
|
1365
|
-
|
|
1366
|
-
);
|
|
1367
|
-
console.log(` 3. Run ${import_chalk8.default.cyan("viberails check")} to verify your project passes`);
|
|
1502
|
+
${import_chalk9.default.bold("Next steps:")}`);
|
|
1503
|
+
console.log(` 1. Review ${import_chalk9.default.cyan("viberails.config.json")} and adjust rules`);
|
|
1504
|
+
console.log(` 2. Commit ${filesToCommit.join(", ")}`);
|
|
1505
|
+
console.log(` 3. Run ${import_chalk9.default.cyan("viberails check")} to verify your project passes`);
|
|
1368
1506
|
}
|
|
1369
1507
|
function updateGitignore(projectRoot) {
|
|
1370
1508
|
const gitignorePath = path13.join(projectRoot, ".gitignore");
|
|
@@ -1374,8 +1512,9 @@ function updateGitignore(projectRoot) {
|
|
|
1374
1512
|
}
|
|
1375
1513
|
if (!content.includes(".viberails/scan-result.json")) {
|
|
1376
1514
|
const block = "\n# viberails\n.viberails/scan-result.json\n";
|
|
1377
|
-
|
|
1378
|
-
|
|
1515
|
+
const prefix = content.length === 0 ? "" : `${content.trimEnd()}
|
|
1516
|
+
`;
|
|
1517
|
+
fs12.writeFileSync(gitignorePath, `${prefix}${block}`);
|
|
1379
1518
|
}
|
|
1380
1519
|
}
|
|
1381
1520
|
|
|
@@ -1384,7 +1523,7 @@ var fs13 = __toESM(require("fs"), 1);
|
|
|
1384
1523
|
var path14 = __toESM(require("path"), 1);
|
|
1385
1524
|
var import_config5 = require("@viberails/config");
|
|
1386
1525
|
var import_scanner2 = require("@viberails/scanner");
|
|
1387
|
-
var
|
|
1526
|
+
var import_chalk10 = __toESM(require("chalk"), 1);
|
|
1388
1527
|
var CONFIG_FILE5 = "viberails.config.json";
|
|
1389
1528
|
async function syncCommand(cwd) {
|
|
1390
1529
|
const startDir = cwd ?? process.cwd();
|
|
@@ -1396,21 +1535,33 @@ async function syncCommand(cwd) {
|
|
|
1396
1535
|
}
|
|
1397
1536
|
const configPath = path14.join(projectRoot, CONFIG_FILE5);
|
|
1398
1537
|
const existing = await (0, import_config5.loadConfig)(configPath);
|
|
1399
|
-
console.log(
|
|
1538
|
+
console.log(import_chalk10.default.dim("Scanning project..."));
|
|
1400
1539
|
const scanResult = await (0, import_scanner2.scan)(projectRoot);
|
|
1401
1540
|
const merged = (0, import_config5.mergeConfig)(existing, scanResult);
|
|
1402
|
-
|
|
1541
|
+
const existingJson = JSON.stringify(existing, null, 2);
|
|
1542
|
+
const mergedJson = JSON.stringify(merged, null, 2);
|
|
1543
|
+
const configChanged = existingJson !== mergedJson;
|
|
1544
|
+
if (configChanged) {
|
|
1545
|
+
console.log(
|
|
1546
|
+
` ${import_chalk10.default.yellow("!")} Config updated \u2014 review ${import_chalk10.default.cyan(CONFIG_FILE5)} for changes`
|
|
1547
|
+
);
|
|
1548
|
+
}
|
|
1549
|
+
fs13.writeFileSync(configPath, `${mergedJson}
|
|
1403
1550
|
`);
|
|
1404
1551
|
writeGeneratedFiles(projectRoot, merged, scanResult);
|
|
1405
1552
|
console.log(`
|
|
1406
|
-
${
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1553
|
+
${import_chalk10.default.bold("Synced:")}`);
|
|
1554
|
+
if (configChanged) {
|
|
1555
|
+
console.log(` ${import_chalk10.default.yellow("!")} ${CONFIG_FILE5} \u2014 updated (review changes)`);
|
|
1556
|
+
} else {
|
|
1557
|
+
console.log(` ${import_chalk10.default.green("\u2713")} ${CONFIG_FILE5} \u2014 unchanged`);
|
|
1558
|
+
}
|
|
1559
|
+
console.log(` ${import_chalk10.default.green("\u2713")} .viberails/context.md \u2014 regenerated`);
|
|
1560
|
+
console.log(` ${import_chalk10.default.green("\u2713")} .viberails/scan-result.json \u2014 updated`);
|
|
1410
1561
|
}
|
|
1411
1562
|
|
|
1412
1563
|
// src/index.ts
|
|
1413
|
-
var VERSION = "0.
|
|
1564
|
+
var VERSION = "0.3.1";
|
|
1414
1565
|
var program = new import_commander.Command();
|
|
1415
1566
|
program.name("viberails").description("Guardrails for vibe coding").version(VERSION);
|
|
1416
1567
|
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) => {
|
|
@@ -1418,7 +1569,7 @@ program.command("init", { isDefault: true }).description("Scan your project and
|
|
|
1418
1569
|
await initCommand(options);
|
|
1419
1570
|
} catch (err) {
|
|
1420
1571
|
const message = err instanceof Error ? err.message : String(err);
|
|
1421
|
-
console.error(`${
|
|
1572
|
+
console.error(`${import_chalk11.default.red("Error:")} ${message}`);
|
|
1422
1573
|
process.exit(1);
|
|
1423
1574
|
}
|
|
1424
1575
|
});
|
|
@@ -1427,21 +1578,22 @@ program.command("sync").description("Re-scan and update generated files").action
|
|
|
1427
1578
|
await syncCommand();
|
|
1428
1579
|
} catch (err) {
|
|
1429
1580
|
const message = err instanceof Error ? err.message : String(err);
|
|
1430
|
-
console.error(`${
|
|
1581
|
+
console.error(`${import_chalk11.default.red("Error:")} ${message}`);
|
|
1431
1582
|
process.exit(1);
|
|
1432
1583
|
}
|
|
1433
1584
|
});
|
|
1434
|
-
program.command("check").description("Check files against enforced rules").option("--staged", "Check only staged files (for pre-commit hooks)").option("--files <files...>", "Check specific files").option("--no-boundaries", "Skip boundary checking").option("--quiet", "Show only summary counts, not individual violations").option("--limit <n>", "Maximum number of violations to display", Number.parseInt).action(
|
|
1585
|
+
program.command("check").description("Check files against enforced rules").option("--staged", "Check only staged files (for pre-commit hooks)").option("--files <files...>", "Check specific files").option("--no-boundaries", "Skip boundary checking").option("--quiet", "Show only summary counts, not individual violations").option("--limit <n>", "Maximum number of violations to display", Number.parseInt).option("--format <format>", "Output format: text (default) or json").action(
|
|
1435
1586
|
async (options) => {
|
|
1436
1587
|
try {
|
|
1437
1588
|
const exitCode = await checkCommand({
|
|
1438
1589
|
...options,
|
|
1439
|
-
noBoundaries: options.boundaries === false
|
|
1590
|
+
noBoundaries: options.boundaries === false,
|
|
1591
|
+
format: options.format === "json" ? "json" : "text"
|
|
1440
1592
|
});
|
|
1441
1593
|
process.exit(exitCode);
|
|
1442
1594
|
} catch (err) {
|
|
1443
1595
|
const message = err instanceof Error ? err.message : String(err);
|
|
1444
|
-
console.error(`${
|
|
1596
|
+
console.error(`${import_chalk11.default.red("Error:")} ${message}`);
|
|
1445
1597
|
process.exit(1);
|
|
1446
1598
|
}
|
|
1447
1599
|
}
|
|
@@ -1452,7 +1604,7 @@ program.command("fix").description("Auto-fix file naming violations and generate
|
|
|
1452
1604
|
process.exit(exitCode);
|
|
1453
1605
|
} catch (err) {
|
|
1454
1606
|
const message = err instanceof Error ? err.message : String(err);
|
|
1455
|
-
console.error(`${
|
|
1607
|
+
console.error(`${import_chalk11.default.red("Error:")} ${message}`);
|
|
1456
1608
|
process.exit(1);
|
|
1457
1609
|
}
|
|
1458
1610
|
});
|
|
@@ -1461,7 +1613,7 @@ program.command("boundaries").description("Display, infer, or inspect import bou
|
|
|
1461
1613
|
await boundariesCommand(options);
|
|
1462
1614
|
} catch (err) {
|
|
1463
1615
|
const message = err instanceof Error ? err.message : String(err);
|
|
1464
|
-
console.error(`${
|
|
1616
|
+
console.error(`${import_chalk11.default.red("Error:")} ${message}`);
|
|
1465
1617
|
process.exit(1);
|
|
1466
1618
|
}
|
|
1467
1619
|
});
|