viberails 0.6.7 → 0.6.8
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 +162 -155
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +162 -155
- package/dist/index.js.map +1 -1
- package/package.json +6 -6
package/dist/index.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
|
-
import
|
|
4
|
+
import chalk17 from "chalk";
|
|
5
5
|
import { Command } from "commander";
|
|
6
6
|
|
|
7
7
|
// src/commands/boundaries.ts
|
|
8
8
|
import * as fs3 from "fs";
|
|
9
9
|
import * as path3 from "path";
|
|
10
10
|
import { compactConfig, loadConfig } from "@viberails/config";
|
|
11
|
-
import
|
|
11
|
+
import chalk2 from "chalk";
|
|
12
12
|
|
|
13
13
|
// src/utils/find-project-root.ts
|
|
14
14
|
import * as fs from "fs";
|
|
@@ -57,6 +57,7 @@ var HINT_AUTO_DETECT = "auto-detect";
|
|
|
57
57
|
|
|
58
58
|
// src/utils/prompt-submenus.ts
|
|
59
59
|
import * as clack from "@clack/prompts";
|
|
60
|
+
import chalk from "chalk";
|
|
60
61
|
var FILE_NAMING_OPTIONS = [
|
|
61
62
|
{ value: "kebab-case", label: "kebab-case" },
|
|
62
63
|
{ value: "camelCase", label: "camelCase" },
|
|
@@ -120,7 +121,7 @@ async function promptNamingMenu(state) {
|
|
|
120
121
|
{
|
|
121
122
|
value: "enforceNaming",
|
|
122
123
|
label: "Enforce file naming",
|
|
123
|
-
hint: state.enforceNaming ? "yes" : "no"
|
|
124
|
+
hint: state.enforceNaming ? chalk.green("yes") : chalk.dim("no")
|
|
124
125
|
}
|
|
125
126
|
];
|
|
126
127
|
if (state.enforceNaming) {
|
|
@@ -238,7 +239,7 @@ async function promptTestingMenu(state) {
|
|
|
238
239
|
{
|
|
239
240
|
value: "enforceMissingTests",
|
|
240
241
|
label: "Enforce missing tests",
|
|
241
|
-
hint: state.enforceMissingTests ? "yes" : "no"
|
|
242
|
+
hint: state.enforceMissingTests ? chalk.green("yes") : chalk.dim("no")
|
|
242
243
|
},
|
|
243
244
|
{
|
|
244
245
|
value: "testCoverage",
|
|
@@ -734,48 +735,48 @@ async function boundariesCommand(options, cwd) {
|
|
|
734
735
|
}
|
|
735
736
|
function displayRules(config) {
|
|
736
737
|
if (!config.boundaries || Object.keys(config.boundaries.deny).length === 0) {
|
|
737
|
-
console.log(
|
|
738
|
-
console.log(`Run ${
|
|
738
|
+
console.log(chalk2.yellow("No boundary rules configured."));
|
|
739
|
+
console.log(`Run ${chalk2.cyan("viberails boundaries --infer")} to generate rules.`);
|
|
739
740
|
return;
|
|
740
741
|
}
|
|
741
742
|
const { deny } = config.boundaries;
|
|
742
743
|
const sources = Object.keys(deny).filter((k) => deny[k].length > 0);
|
|
743
744
|
const totalRules = sources.reduce((sum, k) => sum + deny[k].length, 0);
|
|
744
745
|
console.log(`
|
|
745
|
-
${
|
|
746
|
+
${chalk2.bold(`Boundary rules (${totalRules} deny rules):`)}
|
|
746
747
|
`);
|
|
747
748
|
for (const source of sources) {
|
|
748
749
|
for (const target of deny[source]) {
|
|
749
|
-
console.log(` ${
|
|
750
|
+
console.log(` ${chalk2.red("\u2717")} ${source} \u2192 ${target}`);
|
|
750
751
|
}
|
|
751
752
|
}
|
|
752
753
|
console.log(
|
|
753
754
|
`
|
|
754
|
-
Enforcement: ${config.rules.enforceBoundaries ?
|
|
755
|
+
Enforcement: ${config.rules.enforceBoundaries ? chalk2.green("on") : chalk2.yellow("off")}`
|
|
755
756
|
);
|
|
756
757
|
}
|
|
757
758
|
async function inferAndDisplay(projectRoot, config, configPath) {
|
|
758
|
-
console.log(
|
|
759
|
+
console.log(chalk2.dim("Analyzing imports..."));
|
|
759
760
|
const { buildImportGraph, inferBoundaries } = await import("@viberails/graph");
|
|
760
761
|
const packages = config.packages.length > 1 ? resolveWorkspacePackages(projectRoot, config.packages) : void 0;
|
|
761
762
|
const graph = await buildImportGraph(projectRoot, {
|
|
762
763
|
packages,
|
|
763
764
|
ignore: config.ignore
|
|
764
765
|
});
|
|
765
|
-
console.log(
|
|
766
|
+
console.log(chalk2.dim(`${graph.nodes.length} files, ${graph.edges.length} edges`));
|
|
766
767
|
const inferred = inferBoundaries(graph);
|
|
767
768
|
const sources = Object.keys(inferred.deny).filter((k) => inferred.deny[k].length > 0);
|
|
768
769
|
const totalRules = sources.reduce((sum, k) => sum + inferred.deny[k].length, 0);
|
|
769
770
|
if (totalRules === 0) {
|
|
770
|
-
console.log(
|
|
771
|
+
console.log(chalk2.yellow("No boundary rules could be inferred."));
|
|
771
772
|
return;
|
|
772
773
|
}
|
|
773
774
|
console.log(`
|
|
774
|
-
${
|
|
775
|
+
${chalk2.bold("Inferred boundary rules:")}
|
|
775
776
|
`);
|
|
776
777
|
for (const source of sources) {
|
|
777
778
|
for (const target of inferred.deny[source]) {
|
|
778
|
-
console.log(` ${
|
|
779
|
+
console.log(` ${chalk2.red("\u2717")} ${source} \u2192 ${target}`);
|
|
779
780
|
}
|
|
780
781
|
}
|
|
781
782
|
console.log(`
|
|
@@ -787,11 +788,11 @@ ${chalk.bold("Inferred boundary rules:")}
|
|
|
787
788
|
config.rules.enforceBoundaries = true;
|
|
788
789
|
fs3.writeFileSync(configPath, `${JSON.stringify(compactConfig(config), null, 2)}
|
|
789
790
|
`);
|
|
790
|
-
console.log(`${
|
|
791
|
+
console.log(`${chalk2.green("\u2713")} Saved ${totalRules} rules`);
|
|
791
792
|
}
|
|
792
793
|
}
|
|
793
794
|
async function showGraph(projectRoot, config) {
|
|
794
|
-
console.log(
|
|
795
|
+
console.log(chalk2.dim("Building import graph..."));
|
|
795
796
|
const { buildImportGraph } = await import("@viberails/graph");
|
|
796
797
|
const packages = config.packages.length > 1 ? resolveWorkspacePackages(projectRoot, config.packages) : void 0;
|
|
797
798
|
const graph = await buildImportGraph(projectRoot, {
|
|
@@ -799,20 +800,20 @@ async function showGraph(projectRoot, config) {
|
|
|
799
800
|
ignore: config.ignore
|
|
800
801
|
});
|
|
801
802
|
console.log(`
|
|
802
|
-
${
|
|
803
|
+
${chalk2.bold("Import dependency graph:")}
|
|
803
804
|
`);
|
|
804
805
|
console.log(` ${graph.nodes.length} files, ${graph.edges.length} imports
|
|
805
806
|
`);
|
|
806
807
|
if (graph.packages.length > 0) {
|
|
807
808
|
for (const pkg of graph.packages) {
|
|
808
809
|
const deps = pkg.internalDeps.length > 0 ? `
|
|
809
|
-
${pkg.internalDeps.map((d) => ` \u2192 ${d}`).join("\n")}` :
|
|
810
|
+
${pkg.internalDeps.map((d) => ` \u2192 ${d}`).join("\n")}` : chalk2.dim(" (no internal deps)");
|
|
810
811
|
console.log(` ${pkg.name}${deps}`);
|
|
811
812
|
}
|
|
812
813
|
}
|
|
813
814
|
if (graph.cycles.length > 0) {
|
|
814
815
|
console.log(`
|
|
815
|
-
${
|
|
816
|
+
${chalk2.yellow("Cycles detected:")}`);
|
|
816
817
|
for (const cycle of graph.cycles) {
|
|
817
818
|
const paths = cycle.map((f) => path3.relative(projectRoot, f));
|
|
818
819
|
console.log(` ${paths.join(" \u2192 ")}`);
|
|
@@ -824,7 +825,7 @@ ${chalk.yellow("Cycles detected:")}`);
|
|
|
824
825
|
import * as fs7 from "fs";
|
|
825
826
|
import * as path7 from "path";
|
|
826
827
|
import { loadConfig as loadConfig2 } from "@viberails/config";
|
|
827
|
-
import
|
|
828
|
+
import chalk4 from "chalk";
|
|
828
829
|
|
|
829
830
|
// src/commands/check-config.ts
|
|
830
831
|
import { BUILTIN_IGNORE } from "@viberails/config";
|
|
@@ -1216,7 +1217,7 @@ function collectSourceFiles(dir, projectRoot) {
|
|
|
1216
1217
|
}
|
|
1217
1218
|
|
|
1218
1219
|
// src/commands/check-print.ts
|
|
1219
|
-
import
|
|
1220
|
+
import chalk3 from "chalk";
|
|
1220
1221
|
function printGroupedViolations(violations, limit) {
|
|
1221
1222
|
const groups = /* @__PURE__ */ new Map();
|
|
1222
1223
|
for (const v of violations) {
|
|
@@ -1244,12 +1245,12 @@ function printGroupedViolations(violations, limit) {
|
|
|
1244
1245
|
const toShow = group.slice(0, remaining);
|
|
1245
1246
|
const hidden = group.length - toShow.length;
|
|
1246
1247
|
for (const v of toShow) {
|
|
1247
|
-
const icon = v.severity === "error" ?
|
|
1248
|
-
console.log(`${icon} ${
|
|
1248
|
+
const icon = v.severity === "error" ? chalk3.red("\u2717") : chalk3.yellow("!");
|
|
1249
|
+
console.log(`${icon} ${chalk3.dim(v.rule)} ${v.file}: ${v.message}`);
|
|
1249
1250
|
}
|
|
1250
1251
|
totalShown += toShow.length;
|
|
1251
1252
|
if (hidden > 0) {
|
|
1252
|
-
console.log(
|
|
1253
|
+
console.log(chalk3.dim(` ... and ${hidden} more ${rule} violations`));
|
|
1253
1254
|
}
|
|
1254
1255
|
}
|
|
1255
1256
|
}
|
|
@@ -1339,13 +1340,13 @@ async function checkCommand(options, cwd) {
|
|
|
1339
1340
|
const startDir = cwd ?? process.cwd();
|
|
1340
1341
|
const projectRoot = findProjectRoot(startDir);
|
|
1341
1342
|
if (!projectRoot) {
|
|
1342
|
-
console.error(`${
|
|
1343
|
+
console.error(`${chalk4.red("Error:")} No package.json found. Are you in a JS/TS project?`);
|
|
1343
1344
|
return 1;
|
|
1344
1345
|
}
|
|
1345
1346
|
const configPath = path7.join(projectRoot, CONFIG_FILE2);
|
|
1346
1347
|
if (!fs7.existsSync(configPath)) {
|
|
1347
1348
|
console.error(
|
|
1348
|
-
`${
|
|
1349
|
+
`${chalk4.red("Error:")} No viberails.config.json found. Run \`viberails init\` first.`
|
|
1349
1350
|
);
|
|
1350
1351
|
return 1;
|
|
1351
1352
|
}
|
|
@@ -1359,7 +1360,7 @@ async function checkCommand(options, cwd) {
|
|
|
1359
1360
|
} else if (options.diffBase) {
|
|
1360
1361
|
const diff = getDiffFiles(projectRoot, options.diffBase);
|
|
1361
1362
|
if (diff.error && options.enforce) {
|
|
1362
|
-
console.error(`${
|
|
1363
|
+
console.error(`${chalk4.red("Error:")} ${diff.error}`);
|
|
1363
1364
|
return 1;
|
|
1364
1365
|
}
|
|
1365
1366
|
filesToCheck = diff.all.filter((f) => SOURCE_EXTS.has(path7.extname(f)));
|
|
@@ -1374,13 +1375,13 @@ async function checkCommand(options, cwd) {
|
|
|
1374
1375
|
if (options.format === "json") {
|
|
1375
1376
|
console.log(JSON.stringify({ violations: [], checkedFiles: 0 }));
|
|
1376
1377
|
} else {
|
|
1377
|
-
console.log(`${
|
|
1378
|
+
console.log(`${chalk4.green("\u2713")} No files to check.`);
|
|
1378
1379
|
}
|
|
1379
1380
|
return 0;
|
|
1380
1381
|
}
|
|
1381
1382
|
const violations = [];
|
|
1382
1383
|
const severity = options.enforce ? "error" : "warn";
|
|
1383
|
-
const log9 = options.format !== "json" && !options.hook && !options.quiet ? (msg) => process.stderr.write(
|
|
1384
|
+
const log9 = options.format !== "json" && !options.hook && !options.quiet ? (msg) => process.stderr.write(chalk4.dim(msg)) : () => {
|
|
1384
1385
|
};
|
|
1385
1386
|
log9(" Checking files...");
|
|
1386
1387
|
for (const file of filesToCheck) {
|
|
@@ -1475,7 +1476,7 @@ async function checkCommand(options, cwd) {
|
|
|
1475
1476
|
return options.enforce && violations.length > 0 ? 1 : 0;
|
|
1476
1477
|
}
|
|
1477
1478
|
if (violations.length === 0) {
|
|
1478
|
-
console.log(`${
|
|
1479
|
+
console.log(`${chalk4.green("\u2713")} ${filesToCheck.length} files checked \u2014 no violations`);
|
|
1479
1480
|
return 0;
|
|
1480
1481
|
}
|
|
1481
1482
|
if (!options.quiet) {
|
|
@@ -1483,7 +1484,7 @@ async function checkCommand(options, cwd) {
|
|
|
1483
1484
|
}
|
|
1484
1485
|
printSummary(violations);
|
|
1485
1486
|
if (options.enforce) {
|
|
1486
|
-
console.log(
|
|
1487
|
+
console.log(chalk4.red("Fix violations before committing."));
|
|
1487
1488
|
return 1;
|
|
1488
1489
|
}
|
|
1489
1490
|
return 0;
|
|
@@ -1541,7 +1542,7 @@ import * as path9 from "path";
|
|
|
1541
1542
|
import * as clack6 from "@clack/prompts";
|
|
1542
1543
|
import { compactConfig as compactConfig2, loadConfig as loadConfig3, mergeConfig } from "@viberails/config";
|
|
1543
1544
|
import { scan } from "@viberails/scanner";
|
|
1544
|
-
import
|
|
1545
|
+
import chalk7 from "chalk";
|
|
1545
1546
|
|
|
1546
1547
|
// src/display-text.ts
|
|
1547
1548
|
import {
|
|
@@ -1560,7 +1561,7 @@ import {
|
|
|
1560
1561
|
ORM_NAMES,
|
|
1561
1562
|
STYLING_NAMES as STYLING_NAMES2
|
|
1562
1563
|
} from "@viberails/types";
|
|
1563
|
-
import
|
|
1564
|
+
import chalk6 from "chalk";
|
|
1564
1565
|
|
|
1565
1566
|
// src/display-helpers.ts
|
|
1566
1567
|
import { ROLE_DESCRIPTIONS } from "@viberails/types";
|
|
@@ -1613,7 +1614,7 @@ function formatRoleGroup(group) {
|
|
|
1613
1614
|
|
|
1614
1615
|
// src/display-monorepo.ts
|
|
1615
1616
|
import { FRAMEWORK_NAMES, STYLING_NAMES } from "@viberails/types";
|
|
1616
|
-
import
|
|
1617
|
+
import chalk5 from "chalk";
|
|
1617
1618
|
function formatPackageSummary(pkg) {
|
|
1618
1619
|
const parts = [];
|
|
1619
1620
|
if (pkg.stack.framework) {
|
|
@@ -1630,23 +1631,23 @@ function formatPackageSummary(pkg) {
|
|
|
1630
1631
|
function displayMonorepoResults(scanResult) {
|
|
1631
1632
|
const { stack, packages } = scanResult;
|
|
1632
1633
|
console.log(`
|
|
1633
|
-
${
|
|
1634
|
-
console.log(` ${
|
|
1634
|
+
${chalk5.bold(`Detected: (monorepo, ${packages.length} packages)`)}`);
|
|
1635
|
+
console.log(` ${chalk5.green("\u2713")} ${formatItem(stack.language)}`);
|
|
1635
1636
|
if (stack.packageManager) {
|
|
1636
|
-
console.log(` ${
|
|
1637
|
+
console.log(` ${chalk5.green("\u2713")} ${formatItem(stack.packageManager)}`);
|
|
1637
1638
|
}
|
|
1638
1639
|
if (stack.linter && stack.formatter && stack.linter.name === stack.formatter.name) {
|
|
1639
|
-
console.log(` ${
|
|
1640
|
+
console.log(` ${chalk5.green("\u2713")} ${formatItem(stack.linter)} (lint + format)`);
|
|
1640
1641
|
} else {
|
|
1641
1642
|
if (stack.linter) {
|
|
1642
|
-
console.log(` ${
|
|
1643
|
+
console.log(` ${chalk5.green("\u2713")} ${formatItem(stack.linter)}`);
|
|
1643
1644
|
}
|
|
1644
1645
|
if (stack.formatter) {
|
|
1645
|
-
console.log(` ${
|
|
1646
|
+
console.log(` ${chalk5.green("\u2713")} ${formatItem(stack.formatter)}`);
|
|
1646
1647
|
}
|
|
1647
1648
|
}
|
|
1648
1649
|
if (stack.testRunner) {
|
|
1649
|
-
console.log(` ${
|
|
1650
|
+
console.log(` ${chalk5.green("\u2713")} ${formatItem(stack.testRunner)}`);
|
|
1650
1651
|
}
|
|
1651
1652
|
console.log("");
|
|
1652
1653
|
for (const pkg of packages) {
|
|
@@ -1657,13 +1658,13 @@ ${chalk4.bold(`Detected: (monorepo, ${packages.length} packages)`)}`);
|
|
|
1657
1658
|
);
|
|
1658
1659
|
if (packagesWithDirs.length > 0) {
|
|
1659
1660
|
console.log(`
|
|
1660
|
-
${
|
|
1661
|
+
${chalk5.bold("Structure:")}`);
|
|
1661
1662
|
for (const pkg of packagesWithDirs) {
|
|
1662
1663
|
const groups = groupByRole(pkg.structure.directories);
|
|
1663
1664
|
if (groups.length === 0) continue;
|
|
1664
1665
|
console.log(` ${pkg.relativePath}:`);
|
|
1665
1666
|
for (const group of groups) {
|
|
1666
|
-
console.log(` ${
|
|
1667
|
+
console.log(` ${chalk5.green("\u2713")} ${formatRoleGroup(group)}`);
|
|
1667
1668
|
}
|
|
1668
1669
|
}
|
|
1669
1670
|
}
|
|
@@ -1744,7 +1745,7 @@ function displayConventions(scanResult) {
|
|
|
1744
1745
|
const conventionEntries = Object.entries(scanResult.conventions);
|
|
1745
1746
|
if (conventionEntries.length === 0) return;
|
|
1746
1747
|
console.log(`
|
|
1747
|
-
${
|
|
1748
|
+
${chalk6.bold("Conventions:")}`);
|
|
1748
1749
|
for (const [key, convention] of conventionEntries) {
|
|
1749
1750
|
if (convention.confidence === "low") continue;
|
|
1750
1751
|
const label = CONVENTION_LABELS[key] ?? key;
|
|
@@ -1752,19 +1753,19 @@ ${chalk5.bold("Conventions:")}`);
|
|
|
1752
1753
|
const pkgValues = scanResult.packages.filter((pkg) => pkg.conventions[key] && pkg.conventions[key].confidence !== "low").map((pkg) => ({ relativePath: pkg.relativePath, convention: pkg.conventions[key] }));
|
|
1753
1754
|
const allSame = pkgValues.every((pv) => pv.convention.value === convention.value);
|
|
1754
1755
|
if (allSame || pkgValues.length <= 1) {
|
|
1755
|
-
const ind = convention.confidence === "high" ?
|
|
1756
|
-
const detail =
|
|
1756
|
+
const ind = convention.confidence === "high" ? chalk6.green("\u2713") : chalk6.yellow("~");
|
|
1757
|
+
const detail = chalk6.dim(`(${confidenceLabel(convention)})`);
|
|
1757
1758
|
console.log(` ${ind} ${label}: ${convention.value} ${detail}`);
|
|
1758
1759
|
} else {
|
|
1759
|
-
console.log(` ${
|
|
1760
|
+
console.log(` ${chalk6.yellow("~")} ${label}: varies by package`);
|
|
1760
1761
|
for (const pv of pkgValues) {
|
|
1761
1762
|
const pct = Math.round(pv.convention.consistency);
|
|
1762
1763
|
console.log(` ${pv.relativePath}: ${pv.convention.value} (${pct}%)`);
|
|
1763
1764
|
}
|
|
1764
1765
|
}
|
|
1765
1766
|
} else {
|
|
1766
|
-
const ind = convention.confidence === "high" ?
|
|
1767
|
-
const detail =
|
|
1767
|
+
const ind = convention.confidence === "high" ? chalk6.green("\u2713") : chalk6.yellow("~");
|
|
1768
|
+
const detail = chalk6.dim(`(${confidenceLabel(convention)})`);
|
|
1768
1769
|
console.log(` ${ind} ${label}: ${convention.value} ${detail}`);
|
|
1769
1770
|
}
|
|
1770
1771
|
}
|
|
@@ -1772,7 +1773,7 @@ ${chalk5.bold("Conventions:")}`);
|
|
|
1772
1773
|
function displaySummarySection(scanResult) {
|
|
1773
1774
|
const pkgCount = scanResult.packages.length > 1 ? scanResult.packages.length : void 0;
|
|
1774
1775
|
console.log(`
|
|
1775
|
-
${
|
|
1776
|
+
${chalk6.bold("Summary:")}`);
|
|
1776
1777
|
console.log(` ${formatSummary(scanResult.statistics, pkgCount)}`);
|
|
1777
1778
|
const ext = formatExtensions(scanResult.statistics.filesByExtension);
|
|
1778
1779
|
if (ext) {
|
|
@@ -1786,47 +1787,47 @@ function displayScanResults(scanResult) {
|
|
|
1786
1787
|
}
|
|
1787
1788
|
const { stack } = scanResult;
|
|
1788
1789
|
console.log(`
|
|
1789
|
-
${
|
|
1790
|
+
${chalk6.bold("Detected:")}`);
|
|
1790
1791
|
if (stack.framework) {
|
|
1791
|
-
console.log(` ${
|
|
1792
|
+
console.log(` ${chalk6.green("\u2713")} ${formatItem(stack.framework, FRAMEWORK_NAMES2)}`);
|
|
1792
1793
|
}
|
|
1793
|
-
console.log(` ${
|
|
1794
|
+
console.log(` ${chalk6.green("\u2713")} ${formatItem(stack.language)}`);
|
|
1794
1795
|
if (stack.styling) {
|
|
1795
|
-
console.log(` ${
|
|
1796
|
+
console.log(` ${chalk6.green("\u2713")} ${formatItem(stack.styling, STYLING_NAMES2)}`);
|
|
1796
1797
|
}
|
|
1797
1798
|
if (stack.backend) {
|
|
1798
|
-
console.log(` ${
|
|
1799
|
+
console.log(` ${chalk6.green("\u2713")} ${formatItem(stack.backend, FRAMEWORK_NAMES2)}`);
|
|
1799
1800
|
}
|
|
1800
1801
|
if (stack.orm) {
|
|
1801
|
-
console.log(` ${
|
|
1802
|
+
console.log(` ${chalk6.green("\u2713")} ${formatItem(stack.orm, ORM_NAMES)}`);
|
|
1802
1803
|
}
|
|
1803
1804
|
if (stack.linter && stack.formatter && stack.linter.name === stack.formatter.name) {
|
|
1804
|
-
console.log(` ${
|
|
1805
|
+
console.log(` ${chalk6.green("\u2713")} ${formatItem(stack.linter)} (lint + format)`);
|
|
1805
1806
|
} else {
|
|
1806
1807
|
if (stack.linter) {
|
|
1807
|
-
console.log(` ${
|
|
1808
|
+
console.log(` ${chalk6.green("\u2713")} ${formatItem(stack.linter)}`);
|
|
1808
1809
|
}
|
|
1809
1810
|
if (stack.formatter) {
|
|
1810
|
-
console.log(` ${
|
|
1811
|
+
console.log(` ${chalk6.green("\u2713")} ${formatItem(stack.formatter)}`);
|
|
1811
1812
|
}
|
|
1812
1813
|
}
|
|
1813
1814
|
if (stack.testRunner) {
|
|
1814
|
-
console.log(` ${
|
|
1815
|
+
console.log(` ${chalk6.green("\u2713")} ${formatItem(stack.testRunner)}`);
|
|
1815
1816
|
}
|
|
1816
1817
|
if (stack.packageManager) {
|
|
1817
|
-
console.log(` ${
|
|
1818
|
+
console.log(` ${chalk6.green("\u2713")} ${formatItem(stack.packageManager)}`);
|
|
1818
1819
|
}
|
|
1819
1820
|
if (stack.libraries.length > 0) {
|
|
1820
1821
|
for (const lib of stack.libraries) {
|
|
1821
|
-
console.log(` ${
|
|
1822
|
+
console.log(` ${chalk6.green("\u2713")} ${formatItem(lib, LIBRARY_NAMES)}`);
|
|
1822
1823
|
}
|
|
1823
1824
|
}
|
|
1824
1825
|
const groups = groupByRole(scanResult.structure.directories);
|
|
1825
1826
|
if (groups.length > 0) {
|
|
1826
1827
|
console.log(`
|
|
1827
|
-
${
|
|
1828
|
+
${chalk6.bold("Structure:")}`);
|
|
1828
1829
|
for (const group of groups) {
|
|
1829
|
-
console.log(` ${
|
|
1830
|
+
console.log(` ${chalk6.green("\u2713")} ${formatRoleGroup(group)}`);
|
|
1830
1831
|
}
|
|
1831
1832
|
}
|
|
1832
1833
|
displayConventions(scanResult);
|
|
@@ -1836,25 +1837,25 @@ ${chalk5.bold("Structure:")}`);
|
|
|
1836
1837
|
function displayRulesPreview(config) {
|
|
1837
1838
|
const root = config.packages.find((p) => p.path === ".") ?? config.packages[0];
|
|
1838
1839
|
console.log(
|
|
1839
|
-
`${
|
|
1840
|
+
`${chalk6.bold("Rules:")} ${chalk6.dim("(warns on violation; use --enforce in CI to block)")}`
|
|
1840
1841
|
);
|
|
1841
|
-
console.log(` ${
|
|
1842
|
+
console.log(` ${chalk6.dim("\u2022")} Max file size: ${config.rules.maxFileLines} lines`);
|
|
1842
1843
|
if (config.rules.testCoverage > 0 && root?.structure?.testPattern) {
|
|
1843
1844
|
console.log(
|
|
1844
|
-
` ${
|
|
1845
|
+
` ${chalk6.dim("\u2022")} Test coverage target: ${config.rules.testCoverage}% (${root.structure.testPattern})`
|
|
1845
1846
|
);
|
|
1846
1847
|
} else if (config.rules.testCoverage > 0) {
|
|
1847
|
-
console.log(` ${
|
|
1848
|
+
console.log(` ${chalk6.dim("\u2022")} Test coverage target: ${config.rules.testCoverage}%`);
|
|
1848
1849
|
} else {
|
|
1849
|
-
console.log(` ${
|
|
1850
|
+
console.log(` ${chalk6.dim("\u2022")} Test coverage target: disabled`);
|
|
1850
1851
|
}
|
|
1851
1852
|
if (config.rules.enforceNaming && root?.conventions?.fileNaming) {
|
|
1852
|
-
console.log(` ${
|
|
1853
|
+
console.log(` ${chalk6.dim("\u2022")} Enforce file naming: ${root.conventions.fileNaming}`);
|
|
1853
1854
|
} else {
|
|
1854
|
-
console.log(` ${
|
|
1855
|
+
console.log(` ${chalk6.dim("\u2022")} Enforce file naming: no`);
|
|
1855
1856
|
}
|
|
1856
1857
|
console.log(
|
|
1857
|
-
` ${
|
|
1858
|
+
` ${chalk6.dim("\u2022")} Enforce boundaries: ${config.rules.enforceBoundaries ? "yes" : "no"}`
|
|
1858
1859
|
);
|
|
1859
1860
|
console.log("");
|
|
1860
1861
|
}
|
|
@@ -2179,7 +2180,7 @@ async function configCommand(options, cwd) {
|
|
|
2179
2180
|
}
|
|
2180
2181
|
const configPath = path9.join(projectRoot, CONFIG_FILE3);
|
|
2181
2182
|
if (!fs10.existsSync(configPath)) {
|
|
2182
|
-
console.log(`${
|
|
2183
|
+
console.log(`${chalk7.yellow("!")} No config found. Run ${chalk7.cyan("viberails")} first.`);
|
|
2183
2184
|
return;
|
|
2184
2185
|
}
|
|
2185
2186
|
if (!options.suppressIntro) {
|
|
@@ -2270,22 +2271,22 @@ async function rescanAndMerge(projectRoot, config) {
|
|
|
2270
2271
|
import * as fs13 from "fs";
|
|
2271
2272
|
import * as path13 from "path";
|
|
2272
2273
|
import { loadConfig as loadConfig4 } from "@viberails/config";
|
|
2273
|
-
import
|
|
2274
|
+
import chalk9 from "chalk";
|
|
2274
2275
|
|
|
2275
2276
|
// src/commands/fix-helpers.ts
|
|
2276
2277
|
import { execSync as execSync2 } from "child_process";
|
|
2277
|
-
import
|
|
2278
|
+
import chalk8 from "chalk";
|
|
2278
2279
|
function printPlan(renames, stubs) {
|
|
2279
2280
|
if (renames.length > 0) {
|
|
2280
|
-
console.log(
|
|
2281
|
+
console.log(chalk8.bold("\nFile renames:"));
|
|
2281
2282
|
for (const r of renames) {
|
|
2282
|
-
console.log(` ${
|
|
2283
|
+
console.log(` ${chalk8.red(r.oldPath)} \u2192 ${chalk8.green(r.newPath)}`);
|
|
2283
2284
|
}
|
|
2284
2285
|
}
|
|
2285
2286
|
if (stubs.length > 0) {
|
|
2286
|
-
console.log(
|
|
2287
|
+
console.log(chalk8.bold("\nTest stubs to create:"));
|
|
2287
2288
|
for (const s of stubs) {
|
|
2288
|
-
console.log(` ${
|
|
2289
|
+
console.log(` ${chalk8.green("+")} ${s.path}`);
|
|
2289
2290
|
}
|
|
2290
2291
|
}
|
|
2291
2292
|
}
|
|
@@ -2613,13 +2614,13 @@ async function fixCommand(options, cwd) {
|
|
|
2613
2614
|
const startDir = cwd ?? process.cwd();
|
|
2614
2615
|
const projectRoot = findProjectRoot(startDir);
|
|
2615
2616
|
if (!projectRoot) {
|
|
2616
|
-
console.error(`${
|
|
2617
|
+
console.error(`${chalk9.red("Error:")} No package.json found. Are you in a JS/TS project?`);
|
|
2617
2618
|
return 1;
|
|
2618
2619
|
}
|
|
2619
2620
|
const configPath = path13.join(projectRoot, CONFIG_FILE4);
|
|
2620
2621
|
if (!fs13.existsSync(configPath)) {
|
|
2621
2622
|
console.error(
|
|
2622
|
-
`${
|
|
2623
|
+
`${chalk9.red("Error:")} No viberails.config.json found. Run \`viberails init\` first.`
|
|
2623
2624
|
);
|
|
2624
2625
|
return 1;
|
|
2625
2626
|
}
|
|
@@ -2628,7 +2629,7 @@ async function fixCommand(options, cwd) {
|
|
|
2628
2629
|
const isDirty = checkGitDirty(projectRoot);
|
|
2629
2630
|
if (isDirty) {
|
|
2630
2631
|
console.log(
|
|
2631
|
-
|
|
2632
|
+
chalk9.yellow("Warning: You have uncommitted changes. Consider committing first.")
|
|
2632
2633
|
);
|
|
2633
2634
|
}
|
|
2634
2635
|
}
|
|
@@ -2675,41 +2676,41 @@ async function fixCommand(options, cwd) {
|
|
|
2675
2676
|
return blockedOldBareNames.has(bare);
|
|
2676
2677
|
});
|
|
2677
2678
|
if (safeRenames.length === 0 && testStubs.length === 0 && skippedRenames.length === 0) {
|
|
2678
|
-
console.log(`${
|
|
2679
|
+
console.log(`${chalk9.green("\u2713")} No fixable violations found.`);
|
|
2679
2680
|
return 0;
|
|
2680
2681
|
}
|
|
2681
2682
|
printPlan(safeRenames, testStubs);
|
|
2682
2683
|
if (skippedRenames.length > 0) {
|
|
2683
2684
|
console.log("");
|
|
2684
2685
|
console.log(
|
|
2685
|
-
|
|
2686
|
+
chalk9.yellow(
|
|
2686
2687
|
`Skipping ${skippedRenames.length} rename${skippedRenames.length > 1 ? "s" : ""} \u2014 aliased imports would break:`
|
|
2687
2688
|
)
|
|
2688
2689
|
);
|
|
2689
2690
|
for (const r of skippedRenames.slice(0, 5)) {
|
|
2690
|
-
console.log(
|
|
2691
|
+
console.log(chalk9.dim(` ${r.oldPath} \u2192 ${r.newPath}`));
|
|
2691
2692
|
}
|
|
2692
2693
|
if (skippedRenames.length > 5) {
|
|
2693
|
-
console.log(
|
|
2694
|
+
console.log(chalk9.dim(` ... and ${skippedRenames.length - 5} more`));
|
|
2694
2695
|
}
|
|
2695
2696
|
console.log("");
|
|
2696
|
-
console.log(
|
|
2697
|
+
console.log(chalk9.yellow("Affected aliased imports:"));
|
|
2697
2698
|
for (const alias of aliasImports.slice(0, 5)) {
|
|
2698
2699
|
const relFile = path13.relative(projectRoot, alias.file);
|
|
2699
|
-
console.log(
|
|
2700
|
+
console.log(chalk9.dim(` ${relFile}:${alias.line} \u2014 ${alias.specifier}`));
|
|
2700
2701
|
}
|
|
2701
2702
|
if (aliasImports.length > 5) {
|
|
2702
|
-
console.log(
|
|
2703
|
+
console.log(chalk9.dim(` ... and ${aliasImports.length - 5} more`));
|
|
2703
2704
|
}
|
|
2704
|
-
console.log(
|
|
2705
|
+
console.log(chalk9.dim(" Update these imports to relative paths first, then re-run fix."));
|
|
2705
2706
|
}
|
|
2706
2707
|
if (safeRenames.length === 0 && testStubs.length === 0) {
|
|
2707
2708
|
console.log(`
|
|
2708
|
-
${
|
|
2709
|
+
${chalk9.yellow("!")} No safe fixes to apply. Resolve aliased imports first.`);
|
|
2709
2710
|
return 0;
|
|
2710
2711
|
}
|
|
2711
2712
|
if (options.dryRun) {
|
|
2712
|
-
console.log(
|
|
2713
|
+
console.log(chalk9.dim("\nDry run \u2014 no changes applied."));
|
|
2713
2714
|
return 0;
|
|
2714
2715
|
}
|
|
2715
2716
|
if (!options.yes) {
|
|
@@ -2740,15 +2741,15 @@ ${chalk8.yellow("!")} No safe fixes to apply. Resolve aliased imports first.`);
|
|
|
2740
2741
|
}
|
|
2741
2742
|
console.log("");
|
|
2742
2743
|
if (renameCount > 0) {
|
|
2743
|
-
console.log(`${
|
|
2744
|
+
console.log(`${chalk9.green("\u2713")} Renamed ${renameCount} file${renameCount > 1 ? "s" : ""}`);
|
|
2744
2745
|
}
|
|
2745
2746
|
if (importUpdateCount > 0) {
|
|
2746
2747
|
console.log(
|
|
2747
|
-
`${
|
|
2748
|
+
`${chalk9.green("\u2713")} Updated ${importUpdateCount} import${importUpdateCount > 1 ? "s" : ""}`
|
|
2748
2749
|
);
|
|
2749
2750
|
}
|
|
2750
2751
|
if (stubCount > 0) {
|
|
2751
|
-
console.log(`${
|
|
2752
|
+
console.log(`${chalk9.green("\u2713")} Generated ${stubCount} test stub${stubCount > 1 ? "s" : ""}`);
|
|
2752
2753
|
}
|
|
2753
2754
|
return 0;
|
|
2754
2755
|
}
|
|
@@ -2759,13 +2760,13 @@ import * as path21 from "path";
|
|
|
2759
2760
|
import * as clack13 from "@clack/prompts";
|
|
2760
2761
|
import { compactConfig as compactConfig4, generateConfig as generateConfig2 } from "@viberails/config";
|
|
2761
2762
|
import { scan as scan3 } from "@viberails/scanner";
|
|
2762
|
-
import
|
|
2763
|
+
import chalk15 from "chalk";
|
|
2763
2764
|
|
|
2764
2765
|
// src/utils/check-prerequisites.ts
|
|
2765
2766
|
import * as fs14 from "fs";
|
|
2766
2767
|
import * as path14 from "path";
|
|
2767
2768
|
import * as clack7 from "@clack/prompts";
|
|
2768
|
-
import
|
|
2769
|
+
import chalk10 from "chalk";
|
|
2769
2770
|
|
|
2770
2771
|
// src/utils/spawn-async.ts
|
|
2771
2772
|
import { spawn } from "child_process";
|
|
@@ -2820,9 +2821,9 @@ function displayMissingPrereqs(prereqs) {
|
|
|
2820
2821
|
const missing = prereqs.filter((p) => !p.installed);
|
|
2821
2822
|
for (const m of missing) {
|
|
2822
2823
|
const suffix = m.affectedPackages ? ` \u2014 needed for coverage in: ${m.affectedPackages.join(", ")}` : ` \u2014 ${m.reason}`;
|
|
2823
|
-
console.log(` ${
|
|
2824
|
+
console.log(` ${chalk10.yellow("!")} ${m.label} not installed${suffix}`);
|
|
2824
2825
|
if (m.installCommand) {
|
|
2825
|
-
console.log(` Install: ${
|
|
2826
|
+
console.log(` Install: ${chalk10.cyan(m.installCommand)}`);
|
|
2826
2827
|
}
|
|
2827
2828
|
}
|
|
2828
2829
|
}
|
|
@@ -3147,7 +3148,7 @@ async function handleIntegrations(state, opts) {
|
|
|
3147
3148
|
}
|
|
3148
3149
|
|
|
3149
3150
|
// src/utils/prompt-main-menu-hints.ts
|
|
3150
|
-
import
|
|
3151
|
+
import chalk11 from "chalk";
|
|
3151
3152
|
function fileLimitsHint(config) {
|
|
3152
3153
|
const max = config.rules.maxFileLines;
|
|
3153
3154
|
const test = config.rules.maxTestFileLines;
|
|
@@ -3192,24 +3193,29 @@ function coverageHint(config, hasTestRunner) {
|
|
|
3192
3193
|
}
|
|
3193
3194
|
function advancedNamingHint(config) {
|
|
3194
3195
|
const rootPkg = getRootPackage(config.packages);
|
|
3196
|
+
if (!config.rules.enforceNaming) return "not enforced";
|
|
3195
3197
|
const parts = [];
|
|
3196
|
-
|
|
3197
|
-
|
|
3198
|
-
|
|
3199
|
-
|
|
3200
|
-
|
|
3198
|
+
const naming = rootPkg.conventions?.fileNaming;
|
|
3199
|
+
if (naming) parts.push(chalk11.green(naming));
|
|
3200
|
+
const comp = rootPkg.conventions?.componentNaming;
|
|
3201
|
+
parts.push(comp ? chalk11.green(`${comp} components`) : chalk11.dim("components"));
|
|
3202
|
+
const hook = rootPkg.conventions?.hookNaming;
|
|
3203
|
+
parts.push(hook ? chalk11.green(`${hook} hooks`) : chalk11.dim("hooks"));
|
|
3204
|
+
const alias = rootPkg.conventions?.importAlias;
|
|
3205
|
+
parts.push(alias ? chalk11.green(alias) : chalk11.dim("alias"));
|
|
3206
|
+
return parts.join(chalk11.dim(", "));
|
|
3201
3207
|
}
|
|
3202
3208
|
function integrationsHint(state) {
|
|
3203
3209
|
if (!state.visited.integrations || !state.integrations)
|
|
3204
3210
|
return "not configured \u2014 select to set up";
|
|
3205
3211
|
const items = [];
|
|
3206
|
-
if (state.integrations.preCommitHook) items.push("pre-commit");
|
|
3207
|
-
if (state.integrations.typecheckHook) items.push("typecheck");
|
|
3208
|
-
if (state.integrations.lintHook) items.push("lint");
|
|
3209
|
-
if (state.integrations.claudeCodeHook) items.push("Claude");
|
|
3210
|
-
if (state.integrations.claudeMdRef) items.push("CLAUDE.md");
|
|
3211
|
-
if (state.integrations.githubAction) items.push("CI");
|
|
3212
|
-
return items.length > 0 ? items.join(" \xB7 ") : "none selected";
|
|
3212
|
+
if (state.integrations.preCommitHook) items.push(chalk11.green("pre-commit"));
|
|
3213
|
+
if (state.integrations.typecheckHook) items.push(chalk11.green("typecheck"));
|
|
3214
|
+
if (state.integrations.lintHook) items.push(chalk11.green("lint"));
|
|
3215
|
+
if (state.integrations.claudeCodeHook) items.push(chalk11.green("Claude"));
|
|
3216
|
+
if (state.integrations.claudeMdRef) items.push(chalk11.green("CLAUDE.md"));
|
|
3217
|
+
if (state.integrations.githubAction) items.push(chalk11.green("CI"));
|
|
3218
|
+
return items.length > 0 ? items.join(chalk11.dim(" \xB7 ")) : "none selected";
|
|
3213
3219
|
}
|
|
3214
3220
|
function packageOverridesHint(config) {
|
|
3215
3221
|
const rootNaming = getRootPackage(config.packages).conventions?.fileNaming;
|
|
@@ -3228,8 +3234,9 @@ function boundariesHint(config, state) {
|
|
|
3228
3234
|
return `${ruleCount} rules across ${pkgCount} packages`;
|
|
3229
3235
|
}
|
|
3230
3236
|
function advancedNamingStatus(config) {
|
|
3237
|
+
if (!config.rules.enforceNaming) return "disabled";
|
|
3231
3238
|
const rootPkg = getRootPackage(config.packages);
|
|
3232
|
-
const hasAny = !!rootPkg.conventions?.componentNaming || !!rootPkg.conventions?.hookNaming || !!rootPkg.conventions?.importAlias;
|
|
3239
|
+
const hasAny = !!rootPkg.conventions?.fileNaming || !!rootPkg.conventions?.componentNaming || !!rootPkg.conventions?.hookNaming || !!rootPkg.conventions?.importAlias;
|
|
3233
3240
|
return hasAny ? "ok" : "unconfigured";
|
|
3234
3241
|
}
|
|
3235
3242
|
function packageOverridesStatus(config) {
|
|
@@ -3241,10 +3248,10 @@ function packageOverridesStatus(config) {
|
|
|
3241
3248
|
return customized ? "ok" : "unconfigured";
|
|
3242
3249
|
}
|
|
3243
3250
|
function statusIcon(status) {
|
|
3244
|
-
if (status === "ok") return
|
|
3245
|
-
if (status === "needs-input") return
|
|
3246
|
-
if (status === "unconfigured") return
|
|
3247
|
-
return
|
|
3251
|
+
if (status === "ok") return chalk11.green("\u2713");
|
|
3252
|
+
if (status === "needs-input") return chalk11.yellow("?");
|
|
3253
|
+
if (status === "unconfigured") return chalk11.dim("-");
|
|
3254
|
+
return chalk11.yellow("~");
|
|
3248
3255
|
}
|
|
3249
3256
|
function buildMainMenuOptions(config, scanResult, state) {
|
|
3250
3257
|
const namingStatus = fileNamingStatus(config);
|
|
@@ -3295,7 +3302,7 @@ function buildMainMenuOptions(config, scanResult, state) {
|
|
|
3295
3302
|
options.push(
|
|
3296
3303
|
{ value: "integrations", label: `${iIcon} Integrations`, hint: integrationsHint(state) },
|
|
3297
3304
|
{ value: "reset", label: " Reset all to defaults" },
|
|
3298
|
-
{ value: "review", label: " Review scan details" },
|
|
3305
|
+
{ value: "review", label: " Review scan details", hint: "detected stack & conventions" },
|
|
3299
3306
|
{ value: "done", label: " Done \u2014 write config" }
|
|
3300
3307
|
);
|
|
3301
3308
|
return options;
|
|
@@ -3375,7 +3382,7 @@ function updateGitignore(projectRoot) {
|
|
|
3375
3382
|
// src/commands/init-hooks.ts
|
|
3376
3383
|
import * as fs18 from "fs";
|
|
3377
3384
|
import * as path18 from "path";
|
|
3378
|
-
import
|
|
3385
|
+
import chalk12 from "chalk";
|
|
3379
3386
|
import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
|
|
3380
3387
|
|
|
3381
3388
|
// src/commands/resolve-typecheck.ts
|
|
@@ -3420,13 +3427,13 @@ function setupPreCommitHook(projectRoot) {
|
|
|
3420
3427
|
const lefthookPath = path18.join(projectRoot, "lefthook.yml");
|
|
3421
3428
|
if (fs18.existsSync(lefthookPath)) {
|
|
3422
3429
|
addLefthookPreCommit(lefthookPath);
|
|
3423
|
-
console.log(` ${
|
|
3430
|
+
console.log(` ${chalk12.green("\u2713")} lefthook.yml \u2014 added viberails pre-commit`);
|
|
3424
3431
|
return "lefthook.yml";
|
|
3425
3432
|
}
|
|
3426
3433
|
const huskyDir = path18.join(projectRoot, ".husky");
|
|
3427
3434
|
if (fs18.existsSync(huskyDir)) {
|
|
3428
3435
|
writeHuskyPreCommit(huskyDir);
|
|
3429
|
-
console.log(` ${
|
|
3436
|
+
console.log(` ${chalk12.green("\u2713")} .husky/pre-commit \u2014 added viberails check`);
|
|
3430
3437
|
return ".husky/pre-commit";
|
|
3431
3438
|
}
|
|
3432
3439
|
const gitDir = path18.join(projectRoot, ".git");
|
|
@@ -3436,7 +3443,7 @@ function setupPreCommitHook(projectRoot) {
|
|
|
3436
3443
|
fs18.mkdirSync(hooksDir, { recursive: true });
|
|
3437
3444
|
}
|
|
3438
3445
|
writeGitHookPreCommit(hooksDir);
|
|
3439
|
-
console.log(` ${
|
|
3446
|
+
console.log(` ${chalk12.green("\u2713")} .git/hooks/pre-commit`);
|
|
3440
3447
|
return ".git/hooks/pre-commit";
|
|
3441
3448
|
}
|
|
3442
3449
|
return void 0;
|
|
@@ -3497,9 +3504,9 @@ function setupClaudeCodeHook(projectRoot) {
|
|
|
3497
3504
|
settings = JSON.parse(fs18.readFileSync(settingsPath, "utf-8"));
|
|
3498
3505
|
} catch {
|
|
3499
3506
|
console.warn(
|
|
3500
|
-
` ${
|
|
3507
|
+
` ${chalk12.yellow("!")} .claude/settings.json contains invalid JSON \u2014 skipping hook setup`
|
|
3501
3508
|
);
|
|
3502
|
-
console.warn(` Fix the JSON manually, then re-run ${
|
|
3509
|
+
console.warn(` Fix the JSON manually, then re-run ${chalk12.cyan("viberails init --force")}`);
|
|
3503
3510
|
return;
|
|
3504
3511
|
}
|
|
3505
3512
|
}
|
|
@@ -3522,7 +3529,7 @@ function setupClaudeCodeHook(projectRoot) {
|
|
|
3522
3529
|
settings.hooks = hooks;
|
|
3523
3530
|
fs18.writeFileSync(settingsPath, `${JSON.stringify(settings, null, 2)}
|
|
3524
3531
|
`);
|
|
3525
|
-
console.log(` ${
|
|
3532
|
+
console.log(` ${chalk12.green("\u2713")} .claude/settings.json \u2014 added viberails PostToolUse hook`);
|
|
3526
3533
|
}
|
|
3527
3534
|
function setupClaudeMdReference(projectRoot) {
|
|
3528
3535
|
const claudeMdPath = path18.join(projectRoot, "CLAUDE.md");
|
|
@@ -3534,7 +3541,7 @@ function setupClaudeMdReference(projectRoot) {
|
|
|
3534
3541
|
const ref = "\n@.viberails/context.md\n";
|
|
3535
3542
|
const prefix = content.length === 0 ? "" : content.trimEnd();
|
|
3536
3543
|
fs18.writeFileSync(claudeMdPath, prefix + ref);
|
|
3537
|
-
console.log(` ${
|
|
3544
|
+
console.log(` ${chalk12.green("\u2713")} CLAUDE.md \u2014 added @.viberails/context.md reference`);
|
|
3538
3545
|
}
|
|
3539
3546
|
function setupGithubAction(projectRoot, packageManager, options) {
|
|
3540
3547
|
const workflowDir = path18.join(projectRoot, ".github", "workflows");
|
|
@@ -3620,7 +3627,7 @@ ${cmd}
|
|
|
3620
3627
|
// src/commands/init-hooks-extra.ts
|
|
3621
3628
|
import * as fs19 from "fs";
|
|
3622
3629
|
import * as path19 from "path";
|
|
3623
|
-
import
|
|
3630
|
+
import chalk13 from "chalk";
|
|
3624
3631
|
import { parse as parseYaml2, stringify as stringifyYaml2 } from "yaml";
|
|
3625
3632
|
function addPreCommitStep(projectRoot, name, command, marker, lefthookExtra) {
|
|
3626
3633
|
const lefthookPath = path19.join(projectRoot, "lefthook.yml");
|
|
@@ -3680,12 +3687,12 @@ ${command}
|
|
|
3680
3687
|
function setupTypecheckHook(projectRoot, packageManager) {
|
|
3681
3688
|
const resolved = resolveTypecheckCommand(projectRoot, packageManager);
|
|
3682
3689
|
if (!resolved.command) {
|
|
3683
|
-
console.log(` ${
|
|
3690
|
+
console.log(` ${chalk13.yellow("!")} Skipped typecheck hook: ${resolved.reason}`);
|
|
3684
3691
|
return void 0;
|
|
3685
3692
|
}
|
|
3686
3693
|
const target = addPreCommitStep(projectRoot, "typecheck", resolved.command, "typecheck");
|
|
3687
3694
|
if (target) {
|
|
3688
|
-
console.log(` ${
|
|
3695
|
+
console.log(` ${chalk13.green("\u2713")} ${target} \u2014 added typecheck (${resolved.label})`);
|
|
3689
3696
|
}
|
|
3690
3697
|
return target;
|
|
3691
3698
|
}
|
|
@@ -3706,7 +3713,7 @@ function setupLintHook(projectRoot, linter) {
|
|
|
3706
3713
|
}
|
|
3707
3714
|
const target = addPreCommitStep(projectRoot, "lint", command, linter, lefthookExtra);
|
|
3708
3715
|
if (target) {
|
|
3709
|
-
console.log(` ${
|
|
3716
|
+
console.log(` ${chalk13.green("\u2713")} ${target} \u2014 added ${linterName} lint check`);
|
|
3710
3717
|
}
|
|
3711
3718
|
return target;
|
|
3712
3719
|
}
|
|
@@ -3715,7 +3722,7 @@ function setupSelectedIntegrations(projectRoot, integrations, opts) {
|
|
|
3715
3722
|
if (integrations.preCommitHook) {
|
|
3716
3723
|
const t = setupPreCommitHook(projectRoot);
|
|
3717
3724
|
if (t && opts.lefthookExpected && !t.includes("lefthook")) {
|
|
3718
|
-
console.log(` ${
|
|
3725
|
+
console.log(` ${chalk13.yellow("!")} Lefthook install failed \u2014 fell back to ${t}`);
|
|
3719
3726
|
}
|
|
3720
3727
|
created.push(t ? `${t} \u2014 added viberails pre-commit` : "pre-commit hook skipped");
|
|
3721
3728
|
}
|
|
@@ -3751,7 +3758,7 @@ import * as path20 from "path";
|
|
|
3751
3758
|
import * as clack12 from "@clack/prompts";
|
|
3752
3759
|
import { compactConfig as compactConfig3, generateConfig } from "@viberails/config";
|
|
3753
3760
|
import { scan as scan2 } from "@viberails/scanner";
|
|
3754
|
-
import
|
|
3761
|
+
import chalk14 from "chalk";
|
|
3755
3762
|
|
|
3756
3763
|
// src/utils/filter-confidence.ts
|
|
3757
3764
|
function filterHighConfidence(conventions, meta) {
|
|
@@ -3787,7 +3794,7 @@ async function initNonInteractive(projectRoot, configPath) {
|
|
|
3787
3794
|
const exempted = getExemptedPackages(config);
|
|
3788
3795
|
if (exempted.length > 0) {
|
|
3789
3796
|
console.log(
|
|
3790
|
-
` ${
|
|
3797
|
+
` ${chalk14.dim("Auto-exempted from coverage:")} ${exempted.join(", ")} ${chalk14.dim("(types-only)")}`
|
|
3791
3798
|
);
|
|
3792
3799
|
}
|
|
3793
3800
|
if (config.packages.length > 1) {
|
|
@@ -3824,14 +3831,14 @@ async function initNonInteractive(projectRoot, configPath) {
|
|
|
3824
3831
|
const hookManager = detectHookManager(projectRoot);
|
|
3825
3832
|
const hasHookManager = hookManager === "Lefthook" || hookManager === "Husky";
|
|
3826
3833
|
const preCommitTarget = hasHookManager ? setupPreCommitHook(projectRoot) : void 0;
|
|
3827
|
-
const ok =
|
|
3834
|
+
const ok = chalk14.green("\u2713");
|
|
3828
3835
|
const created = [
|
|
3829
3836
|
`${ok} ${path20.basename(configPath)}`,
|
|
3830
3837
|
`${ok} .viberails/context.md`,
|
|
3831
3838
|
`${ok} .viberails/scan-result.json`,
|
|
3832
3839
|
`${ok} .claude/settings.json \u2014 added viberails hook`,
|
|
3833
3840
|
`${ok} CLAUDE.md \u2014 added @.viberails/context.md reference`,
|
|
3834
|
-
preCommitTarget ? `${ok} ${preCommitTarget}` : `${
|
|
3841
|
+
preCommitTarget ? `${ok} ${preCommitTarget}` : `${chalk14.yellow("!")} pre-commit hook skipped (install lefthook or husky)`,
|
|
3835
3842
|
actionTarget ? `${ok} ${actionTarget} \u2014 blocks PRs on violations` : ""
|
|
3836
3843
|
].filter(Boolean);
|
|
3837
3844
|
if (hasHookManager && isTypeScript) setupTypecheckHook(projectRoot, rootPkgPm);
|
|
@@ -3856,8 +3863,8 @@ async function initCommand(options, cwd) {
|
|
|
3856
3863
|
return initInteractive(projectRoot, configPath, options);
|
|
3857
3864
|
}
|
|
3858
3865
|
console.log(
|
|
3859
|
-
`${
|
|
3860
|
-
Run ${
|
|
3866
|
+
`${chalk15.yellow("!")} viberails is already initialized.
|
|
3867
|
+
Run ${chalk15.cyan("viberails")} to review or edit the existing setup, ${chalk15.cyan("viberails sync")} to update generated files, or ${chalk15.cyan("viberails init --force")} to replace it.`
|
|
3861
3868
|
);
|
|
3862
3869
|
return;
|
|
3863
3870
|
}
|
|
@@ -3934,7 +3941,7 @@ async function initInteractive(projectRoot, configPath, options) {
|
|
|
3934
3941
|
writeGeneratedFiles(projectRoot, config, scanResult);
|
|
3935
3942
|
updateGitignore(projectRoot);
|
|
3936
3943
|
ws.stop("Configuration written");
|
|
3937
|
-
const ok =
|
|
3944
|
+
const ok = chalk15.green("\u2713");
|
|
3938
3945
|
clack13.log.step(`${ok} ${path21.basename(configPath)}`);
|
|
3939
3946
|
clack13.log.step(`${ok} .viberails/context.md`);
|
|
3940
3947
|
clack13.log.step(`${ok} .viberails/scan-result.json`);
|
|
@@ -3948,7 +3955,7 @@ async function initInteractive(projectRoot, configPath, options) {
|
|
|
3948
3955
|
}
|
|
3949
3956
|
clack13.outro(
|
|
3950
3957
|
`Done! Next: review viberails.config.json, then run viberails check
|
|
3951
|
-
${
|
|
3958
|
+
${chalk15.dim("Tip: use")} ${chalk15.cyan("viberails check --enforce")} ${chalk15.dim("in CI to block PRs on violations.")}`
|
|
3952
3959
|
);
|
|
3953
3960
|
}
|
|
3954
3961
|
|
|
@@ -3958,7 +3965,7 @@ import * as path22 from "path";
|
|
|
3958
3965
|
import * as clack14 from "@clack/prompts";
|
|
3959
3966
|
import { compactConfig as compactConfig5, loadConfig as loadConfig5, mergeConfig as mergeConfig2 } from "@viberails/config";
|
|
3960
3967
|
import { scan as scan4 } from "@viberails/scanner";
|
|
3961
|
-
import
|
|
3968
|
+
import chalk16 from "chalk";
|
|
3962
3969
|
var CONFIG_FILE6 = "viberails.config.json";
|
|
3963
3970
|
var SCAN_RESULT_FILE2 = ".viberails/scan-result.json";
|
|
3964
3971
|
function loadPreviousStats(projectRoot) {
|
|
@@ -3999,13 +4006,13 @@ async function syncCommand(options, cwd) {
|
|
|
3999
4006
|
const statsDelta = previousStats ? formatStatsDelta(previousStats, scanResult.statistics) : void 0;
|
|
4000
4007
|
if (changes.length > 0 || statsDelta) {
|
|
4001
4008
|
console.log(`
|
|
4002
|
-
${
|
|
4009
|
+
${chalk16.bold("Changes:")}`);
|
|
4003
4010
|
for (const change of changes) {
|
|
4004
|
-
const icon = change.type === "removed" ?
|
|
4011
|
+
const icon = change.type === "removed" ? chalk16.red("-") : chalk16.green("+");
|
|
4005
4012
|
console.log(` ${icon} ${change.description}`);
|
|
4006
4013
|
}
|
|
4007
4014
|
if (statsDelta) {
|
|
4008
|
-
console.log(` ${
|
|
4015
|
+
console.log(` ${chalk16.dim(statsDelta)}`);
|
|
4009
4016
|
}
|
|
4010
4017
|
}
|
|
4011
4018
|
if (options?.interactive) {
|
|
@@ -4054,18 +4061,18 @@ ${chalk15.bold("Changes:")}`);
|
|
|
4054
4061
|
`);
|
|
4055
4062
|
writeGeneratedFiles(projectRoot, merged, scanResult);
|
|
4056
4063
|
console.log(`
|
|
4057
|
-
${
|
|
4064
|
+
${chalk16.bold("Synced:")}`);
|
|
4058
4065
|
if (configChanged) {
|
|
4059
|
-
console.log(` ${
|
|
4066
|
+
console.log(` ${chalk16.yellow("!")} ${CONFIG_FILE6} \u2014 updated (review changes)`);
|
|
4060
4067
|
} else {
|
|
4061
|
-
console.log(` ${
|
|
4068
|
+
console.log(` ${chalk16.green("\u2713")} ${CONFIG_FILE6} \u2014 unchanged`);
|
|
4062
4069
|
}
|
|
4063
|
-
console.log(` ${
|
|
4064
|
-
console.log(` ${
|
|
4070
|
+
console.log(` ${chalk16.green("\u2713")} .viberails/context.md \u2014 regenerated`);
|
|
4071
|
+
console.log(` ${chalk16.green("\u2713")} .viberails/scan-result.json \u2014 updated`);
|
|
4065
4072
|
}
|
|
4066
4073
|
|
|
4067
4074
|
// src/index.ts
|
|
4068
|
-
var VERSION = "0.6.
|
|
4075
|
+
var VERSION = "0.6.8";
|
|
4069
4076
|
var program = new Command();
|
|
4070
4077
|
program.name("viberails").description("Guardrails for vibe coding").version(VERSION);
|
|
4071
4078
|
program.command("init", { isDefault: true }).description("Scan your project and set up enforcement guardrails").option("-y, --yes", "Non-interactive mode (use defaults, high-confidence only)").option("-f, --force", "Re-initialize, replacing existing config").action(async (options) => {
|
|
@@ -4073,7 +4080,7 @@ program.command("init", { isDefault: true }).description("Scan your project and
|
|
|
4073
4080
|
await initCommand(options);
|
|
4074
4081
|
} catch (err) {
|
|
4075
4082
|
const message = err instanceof Error ? err.message : String(err);
|
|
4076
|
-
console.error(`${
|
|
4083
|
+
console.error(`${chalk17.red("Error:")} ${message}`);
|
|
4077
4084
|
process.exit(1);
|
|
4078
4085
|
}
|
|
4079
4086
|
});
|
|
@@ -4082,7 +4089,7 @@ program.command("sync").description("Re-scan and update generated files").option
|
|
|
4082
4089
|
await syncCommand(options);
|
|
4083
4090
|
} catch (err) {
|
|
4084
4091
|
const message = err instanceof Error ? err.message : String(err);
|
|
4085
|
-
console.error(`${
|
|
4092
|
+
console.error(`${chalk17.red("Error:")} ${message}`);
|
|
4086
4093
|
process.exit(1);
|
|
4087
4094
|
}
|
|
4088
4095
|
});
|
|
@@ -4091,7 +4098,7 @@ program.command("config").description("Interactively edit existing config rules"
|
|
|
4091
4098
|
await configCommand(options);
|
|
4092
4099
|
} catch (err) {
|
|
4093
4100
|
const message = err instanceof Error ? err.message : String(err);
|
|
4094
|
-
console.error(`${
|
|
4101
|
+
console.error(`${chalk17.red("Error:")} ${message}`);
|
|
4095
4102
|
process.exit(1);
|
|
4096
4103
|
}
|
|
4097
4104
|
});
|
|
@@ -4112,7 +4119,7 @@ program.command("check").description("Check files against enforced rules").optio
|
|
|
4112
4119
|
process.exit(exitCode);
|
|
4113
4120
|
} catch (err) {
|
|
4114
4121
|
const message = err instanceof Error ? err.message : String(err);
|
|
4115
|
-
console.error(`${
|
|
4122
|
+
console.error(`${chalk17.red("Error:")} ${message}`);
|
|
4116
4123
|
process.exit(1);
|
|
4117
4124
|
}
|
|
4118
4125
|
}
|
|
@@ -4123,7 +4130,7 @@ program.command("fix").description("Auto-fix file naming violations and generate
|
|
|
4123
4130
|
process.exit(exitCode);
|
|
4124
4131
|
} catch (err) {
|
|
4125
4132
|
const message = err instanceof Error ? err.message : String(err);
|
|
4126
|
-
console.error(`${
|
|
4133
|
+
console.error(`${chalk17.red("Error:")} ${message}`);
|
|
4127
4134
|
process.exit(1);
|
|
4128
4135
|
}
|
|
4129
4136
|
});
|
|
@@ -4132,7 +4139,7 @@ program.command("boundaries").description("Display, infer, or inspect import bou
|
|
|
4132
4139
|
await boundariesCommand(options);
|
|
4133
4140
|
} catch (err) {
|
|
4134
4141
|
const message = err instanceof Error ? err.message : String(err);
|
|
4135
|
-
console.error(`${
|
|
4142
|
+
console.error(`${chalk17.red("Error:")} ${message}`);
|
|
4136
4143
|
process.exit(1);
|
|
4137
4144
|
}
|
|
4138
4145
|
});
|