viberails 0.5.3 → 0.5.5
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 +55 -37
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +55 -37
- package/dist/index.js.map +1 -1
- package/package.json +6 -6
package/dist/index.js
CHANGED
|
@@ -33,7 +33,7 @@ import * as clack5 from "@clack/prompts";
|
|
|
33
33
|
// src/utils/prompt-integrations.ts
|
|
34
34
|
import { spawnSync } from "child_process";
|
|
35
35
|
import * as clack from "@clack/prompts";
|
|
36
|
-
async function promptHookManagerInstall(projectRoot, packageManager) {
|
|
36
|
+
async function promptHookManagerInstall(projectRoot, packageManager, isWorkspace) {
|
|
37
37
|
const choice = await clack.select({
|
|
38
38
|
message: "No git hook manager detected. Install Lefthook for shareable pre-commit hooks?",
|
|
39
39
|
options: [
|
|
@@ -52,7 +52,7 @@ async function promptHookManagerInstall(projectRoot, packageManager) {
|
|
|
52
52
|
assertNotCancelled(choice);
|
|
53
53
|
if (choice !== "install") return void 0;
|
|
54
54
|
const pm = packageManager || "npm";
|
|
55
|
-
const installCmd = pm === "yarn" ? "yarn add -D lefthook" : pm === "pnpm" ?
|
|
55
|
+
const installCmd = pm === "yarn" ? "yarn add -D lefthook" : pm === "pnpm" ? `pnpm add -D${isWorkspace ? " -w" : ""} lefthook` : "npm install -D lefthook";
|
|
56
56
|
const s = clack.spinner();
|
|
57
57
|
s.start("Installing Lefthook...");
|
|
58
58
|
const result = spawnSync(installCmd, {
|
|
@@ -80,7 +80,8 @@ async function promptIntegrations(projectRoot, hookManager, tools) {
|
|
|
80
80
|
if (!resolvedHookManager) {
|
|
81
81
|
resolvedHookManager = await promptHookManagerInstall(
|
|
82
82
|
projectRoot,
|
|
83
|
-
tools?.packageManager ?? "npm"
|
|
83
|
+
tools?.packageManager ?? "npm",
|
|
84
|
+
tools?.isWorkspace
|
|
84
85
|
);
|
|
85
86
|
}
|
|
86
87
|
const isBareHook = !resolvedHookManager;
|
|
@@ -97,7 +98,7 @@ async function promptIntegrations(projectRoot, hookManager, tools) {
|
|
|
97
98
|
options.push({
|
|
98
99
|
value: "typecheck",
|
|
99
100
|
label: "Typecheck (tsc --noEmit)",
|
|
100
|
-
hint: "
|
|
101
|
+
hint: "pre-commit hook + CI check"
|
|
101
102
|
});
|
|
102
103
|
}
|
|
103
104
|
if (tools?.linter) {
|
|
@@ -105,7 +106,7 @@ async function promptIntegrations(projectRoot, hookManager, tools) {
|
|
|
105
106
|
options.push({
|
|
106
107
|
value: "lint",
|
|
107
108
|
label: `Lint check (${linterName})`,
|
|
108
|
-
hint: "
|
|
109
|
+
hint: "pre-commit hook + CI check"
|
|
109
110
|
});
|
|
110
111
|
}
|
|
111
112
|
options.push(
|
|
@@ -1420,7 +1421,8 @@ function formatPackageSummary(pkg) {
|
|
|
1420
1421
|
if (pkg.stack.styling) {
|
|
1421
1422
|
parts.push(formatItem(pkg.stack.styling, STYLING_NAMES));
|
|
1422
1423
|
}
|
|
1423
|
-
const
|
|
1424
|
+
const n = pkg.statistics.totalFiles;
|
|
1425
|
+
const files = `${n} ${n === 1 ? "file" : "files"}`;
|
|
1424
1426
|
const detail = parts.length > 0 ? `${parts.join(", ")} (${files})` : `(${files})`;
|
|
1425
1427
|
return ` ${pkg.relativePath} \u2014 ${detail}`;
|
|
1426
1428
|
}
|
|
@@ -1476,7 +1478,8 @@ function formatPackageSummaryPlain(pkg) {
|
|
|
1476
1478
|
if (pkg.stack.styling) {
|
|
1477
1479
|
parts.push(formatItem(pkg.stack.styling, STYLING_NAMES));
|
|
1478
1480
|
}
|
|
1479
|
-
const
|
|
1481
|
+
const n = pkg.statistics.totalFiles;
|
|
1482
|
+
const files = `${n} ${n === 1 ? "file" : "files"}`;
|
|
1480
1483
|
const detail = parts.length > 0 ? `${parts.join(", ")} (${files})` : `(${files})`;
|
|
1481
1484
|
return ` ${pkg.relativePath} \u2014 ${detail}`;
|
|
1482
1485
|
}
|
|
@@ -2479,7 +2482,8 @@ function checkCoveragePrereqs(projectRoot, scanResult) {
|
|
|
2479
2482
|
return hasDependency(pkgDir, "@vitest/coverage-v8") || hasDependency(pkgDir, "@vitest/coverage-istanbul");
|
|
2480
2483
|
});
|
|
2481
2484
|
}
|
|
2482
|
-
const
|
|
2485
|
+
const isWorkspace = scanResult.packages.length > 1;
|
|
2486
|
+
const addCmd = pm === "yarn" ? "yarn add -D" : pm === "pnpm" && isWorkspace ? "pnpm add -D -w" : pm === "npm" ? "npm install -D" : `${pm} add -D`;
|
|
2483
2487
|
const affectedPackages = vitestPackages.length > 1 ? vitestPackages : void 0;
|
|
2484
2488
|
const reason = affectedPackages ? `Required for coverage in: ${affectedPackages.join(", ")}` : "Required for coverage percentage checks with vitest";
|
|
2485
2489
|
return [
|
|
@@ -2680,7 +2684,6 @@ function addLefthookPreCommit(lefthookPath) {
|
|
|
2680
2684
|
function detectHookManager(projectRoot) {
|
|
2681
2685
|
if (fs16.existsSync(path16.join(projectRoot, "lefthook.yml"))) return "Lefthook";
|
|
2682
2686
|
if (fs16.existsSync(path16.join(projectRoot, ".husky"))) return "Husky";
|
|
2683
|
-
if (fs16.existsSync(path16.join(projectRoot, ".git"))) return "git hook";
|
|
2684
2687
|
return void 0;
|
|
2685
2688
|
}
|
|
2686
2689
|
function setupClaudeCodeHook(projectRoot) {
|
|
@@ -2734,7 +2737,7 @@ function setupClaudeMdReference(projectRoot) {
|
|
|
2734
2737
|
fs16.writeFileSync(claudeMdPath, prefix + ref);
|
|
2735
2738
|
console.log(` ${chalk9.green("\u2713")} CLAUDE.md \u2014 added @.viberails/context.md reference`);
|
|
2736
2739
|
}
|
|
2737
|
-
function setupGithubAction(projectRoot, packageManager) {
|
|
2740
|
+
function setupGithubAction(projectRoot, packageManager, options) {
|
|
2738
2741
|
const workflowDir = path16.join(projectRoot, ".github", "workflows");
|
|
2739
2742
|
const workflowPath = path16.join(workflowDir, "viberails.yml");
|
|
2740
2743
|
if (fs16.existsSync(workflowPath)) {
|
|
@@ -2770,7 +2773,16 @@ function setupGithubAction(projectRoot, packageManager) {
|
|
|
2770
2773
|
" node-version: 22",
|
|
2771
2774
|
pm !== "npm" ? ` cache: ${pm}` : "",
|
|
2772
2775
|
"",
|
|
2773
|
-
` - run: ${installCmd}
|
|
2776
|
+
` - run: ${installCmd}`
|
|
2777
|
+
);
|
|
2778
|
+
if (options?.typecheck) {
|
|
2779
|
+
lines.push(` - run: ${runPrefix} tsc --noEmit`);
|
|
2780
|
+
}
|
|
2781
|
+
if (options?.linter) {
|
|
2782
|
+
const lintCmd = options.linter === "biome" ? "biome check ." : "eslint .";
|
|
2783
|
+
lines.push(` - run: ${runPrefix} ${lintCmd}`);
|
|
2784
|
+
}
|
|
2785
|
+
lines.push(
|
|
2774
2786
|
` - run: ${runPrefix} viberails check --enforce --diff-base origin/\${{ github.event.pull_request.base.ref }}`,
|
|
2775
2787
|
""
|
|
2776
2788
|
);
|
|
@@ -2891,7 +2903,10 @@ function setupSelectedIntegrations(projectRoot, integrations, opts) {
|
|
|
2891
2903
|
created.push("CLAUDE.md \u2014 added @.viberails/context.md reference");
|
|
2892
2904
|
}
|
|
2893
2905
|
if (integrations.githubAction) {
|
|
2894
|
-
const t = setupGithubAction(projectRoot, opts.packageManager ?? "npm"
|
|
2906
|
+
const t = setupGithubAction(projectRoot, opts.packageManager ?? "npm", {
|
|
2907
|
+
linter: integrations.lintHook ? opts.linter : void 0,
|
|
2908
|
+
typecheck: integrations.typecheckHook
|
|
2909
|
+
});
|
|
2895
2910
|
if (t) created.push(`${t} \u2014 blocks PRs on violations`);
|
|
2896
2911
|
}
|
|
2897
2912
|
return created;
|
|
@@ -2959,11 +2974,15 @@ async function initNonInteractive(projectRoot, configPath) {
|
|
|
2959
2974
|
setupClaudeMdReference(projectRoot);
|
|
2960
2975
|
const rootPkg = config.packages[0];
|
|
2961
2976
|
const rootPkgPm = rootPkg?.stack?.packageManager ?? "npm";
|
|
2962
|
-
const
|
|
2977
|
+
const linter = rootPkg?.stack?.linter?.split("@")[0];
|
|
2978
|
+
const isTypeScript = rootPkg?.stack?.language === "typescript";
|
|
2979
|
+
const actionTarget = setupGithubAction(projectRoot, rootPkgPm, {
|
|
2980
|
+
linter,
|
|
2981
|
+
typecheck: isTypeScript
|
|
2982
|
+
});
|
|
2963
2983
|
const hookManager = detectHookManager(projectRoot);
|
|
2964
2984
|
const hasHookManager = hookManager === "Lefthook" || hookManager === "Husky";
|
|
2965
2985
|
const preCommitTarget = hasHookManager ? setupPreCommitHook(projectRoot) : void 0;
|
|
2966
|
-
const linter = rootPkg?.stack?.linter?.split("@")[0];
|
|
2967
2986
|
const ok = chalk11.green("\u2713");
|
|
2968
2987
|
const created = [
|
|
2969
2988
|
`${ok} ${path18.basename(configPath)}`,
|
|
@@ -2996,13 +3015,6 @@ async function initInteractive(projectRoot, configPath, options) {
|
|
|
2996
3015
|
const scanResult = await scan2(projectRoot);
|
|
2997
3016
|
const config = generateConfig(scanResult);
|
|
2998
3017
|
s.stop("Scan complete");
|
|
2999
|
-
const prereqResult = await promptMissingPrereqs(
|
|
3000
|
-
projectRoot,
|
|
3001
|
-
checkCoveragePrereqs(projectRoot, scanResult)
|
|
3002
|
-
);
|
|
3003
|
-
if (prereqResult.disableCoverage) {
|
|
3004
|
-
config.rules.testCoverage = 0;
|
|
3005
|
-
}
|
|
3006
3018
|
if (scanResult.statistics.totalFiles === 0) {
|
|
3007
3019
|
clack8.log.warn(
|
|
3008
3020
|
"No source files detected. Try running from the project root,\nor check that source files exist. Run viberails sync after adding files."
|
|
@@ -3043,20 +3055,29 @@ async function initInteractive(projectRoot, configPath, options) {
|
|
|
3043
3055
|
if (denyCount > 0) {
|
|
3044
3056
|
config.boundaries = inferred;
|
|
3045
3057
|
config.rules.enforceBoundaries = true;
|
|
3046
|
-
|
|
3047
|
-
|
|
3048
|
-
clack8.note(boundaryLines, "Boundary rules");
|
|
3058
|
+
const pkgCount = Object.keys(inferred.deny).length;
|
|
3059
|
+
bs.stop(`Inferred ${denyCount} boundary rules across ${pkgCount} packages`);
|
|
3049
3060
|
} else {
|
|
3050
3061
|
bs.stop("No boundary rules inferred");
|
|
3051
3062
|
}
|
|
3052
3063
|
}
|
|
3053
3064
|
}
|
|
3054
3065
|
const hookManager = detectHookManager(projectRoot);
|
|
3066
|
+
const coveragePrereqs = checkCoveragePrereqs(projectRoot, scanResult);
|
|
3067
|
+
const hasMissingPrereqs = coveragePrereqs.some((p) => !p.installed) || !hookManager;
|
|
3068
|
+
if (hasMissingPrereqs) {
|
|
3069
|
+
clack8.log.info("Some dependencies are needed for full functionality.");
|
|
3070
|
+
}
|
|
3071
|
+
const prereqResult = await promptMissingPrereqs(projectRoot, coveragePrereqs);
|
|
3072
|
+
if (prereqResult.disableCoverage) {
|
|
3073
|
+
config.rules.testCoverage = 0;
|
|
3074
|
+
}
|
|
3055
3075
|
const rootPkgStack = (config.packages.find((p) => p.path === ".") ?? config.packages[0])?.stack;
|
|
3056
3076
|
const integrations = await promptIntegrations(projectRoot, hookManager, {
|
|
3057
3077
|
isTypeScript: rootPkgStack?.language === "typescript",
|
|
3058
3078
|
linter: rootPkgStack?.linter?.split("@")[0],
|
|
3059
|
-
packageManager: rootPkgStack?.packageManager
|
|
3079
|
+
packageManager: rootPkgStack?.packageManager,
|
|
3080
|
+
isWorkspace: config.packages.length > 1
|
|
3060
3081
|
});
|
|
3061
3082
|
const shouldWrite = await confirm3("Write configuration and set up selected integrations?");
|
|
3062
3083
|
if (!shouldWrite) {
|
|
@@ -3068,17 +3089,14 @@ async function initInteractive(projectRoot, configPath, options) {
|
|
|
3068
3089
|
`);
|
|
3069
3090
|
writeGeneratedFiles(projectRoot, config, scanResult);
|
|
3070
3091
|
updateGitignore(projectRoot);
|
|
3071
|
-
const
|
|
3072
|
-
|
|
3073
|
-
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
];
|
|
3080
|
-
clack8.log.success(`Created:
|
|
3081
|
-
${createdFiles.map((f) => ` ${f}`).join("\n")}`);
|
|
3092
|
+
const ok = chalk11.green("\u2713");
|
|
3093
|
+
clack8.log.step(`${ok} ${path18.basename(configPath)}`);
|
|
3094
|
+
clack8.log.step(`${ok} .viberails/context.md`);
|
|
3095
|
+
clack8.log.step(`${ok} .viberails/scan-result.json`);
|
|
3096
|
+
setupSelectedIntegrations(projectRoot, integrations, {
|
|
3097
|
+
linter: rootPkgStack?.linter?.split("@")[0],
|
|
3098
|
+
packageManager: rootPkgStack?.packageManager
|
|
3099
|
+
});
|
|
3082
3100
|
clack8.outro(
|
|
3083
3101
|
`Done! Next: review viberails.config.json, then run viberails check
|
|
3084
3102
|
${chalk11.dim("Tip: use")} ${chalk11.cyan("viberails check --enforce")} ${chalk11.dim("in CI to block PRs on violations.")}`
|
|
@@ -3192,7 +3210,7 @@ ${chalk12.bold("Synced:")}`);
|
|
|
3192
3210
|
}
|
|
3193
3211
|
|
|
3194
3212
|
// src/index.ts
|
|
3195
|
-
var VERSION = "0.5.
|
|
3213
|
+
var VERSION = "0.5.5";
|
|
3196
3214
|
var program = new Command();
|
|
3197
3215
|
program.name("viberails").description("Guardrails for vibe coding").version(VERSION);
|
|
3198
3216
|
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) => {
|