viberails 0.5.2 → 0.5.4
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 +66 -37
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +66 -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;
|
|
@@ -1662,13 +1663,15 @@ function displayInitSummary(config, exemptedPackages) {
|
|
|
1662
1663
|
console.log("");
|
|
1663
1664
|
console.log(` ${chalk4.bold("Rules to apply:")}`);
|
|
1664
1665
|
console.log(` ${ok} Max file size: ${chalk4.cyan(`${config.rules.maxFileLines} lines`)}`);
|
|
1665
|
-
|
|
1666
|
-
|
|
1666
|
+
const fileNaming = root?.conventions?.fileNaming ?? config.packages.find((p) => p.conventions?.fileNaming)?.conventions?.fileNaming;
|
|
1667
|
+
if (config.rules.enforceNaming && fileNaming) {
|
|
1668
|
+
console.log(` ${ok} File naming: ${chalk4.cyan(fileNaming)}`);
|
|
1667
1669
|
} else {
|
|
1668
1670
|
console.log(` ${off} File naming: ${chalk4.dim("not enforced")}`);
|
|
1669
1671
|
}
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
+
const testPattern = root?.structure?.testPattern ?? config.packages.find((p) => p.structure?.testPattern)?.structure?.testPattern;
|
|
1673
|
+
if (config.rules.enforceMissingTests && testPattern) {
|
|
1674
|
+
console.log(` ${ok} Missing tests: ${chalk4.cyan(`enforced (${testPattern})`)}`);
|
|
1672
1675
|
} else if (config.rules.enforceMissingTests) {
|
|
1673
1676
|
console.log(` ${ok} Missing tests: ${chalk4.cyan("enforced")}`);
|
|
1674
1677
|
} else {
|
|
@@ -2466,30 +2469,36 @@ import * as path14 from "path";
|
|
|
2466
2469
|
import * as clack7 from "@clack/prompts";
|
|
2467
2470
|
import chalk8 from "chalk";
|
|
2468
2471
|
function checkCoveragePrereqs(projectRoot, scanResult) {
|
|
2469
|
-
const testRunner = scanResult.stack.testRunner;
|
|
2470
|
-
if (!testRunner) return [];
|
|
2471
|
-
const runner = testRunner.name;
|
|
2472
2472
|
const pm = scanResult.stack.packageManager.name;
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
installCommand: installed ? void 0 : `${addCmd} @vitest/coverage-v8`,
|
|
2483
|
-
reason: "Required for coverage percentage checks with vitest"
|
|
2484
|
-
}
|
|
2485
|
-
];
|
|
2473
|
+
const vitestPackages = scanResult.packages.filter((pkg) => pkg.stack.testRunner?.name === "vitest").map((pkg) => pkg.relativePath);
|
|
2474
|
+
const hasVitest = vitestPackages.length > 0 || scanResult.stack.testRunner?.name === "vitest";
|
|
2475
|
+
if (!hasVitest) return [];
|
|
2476
|
+
let installed = hasDependency(projectRoot, "@vitest/coverage-v8") || hasDependency(projectRoot, "@vitest/coverage-istanbul");
|
|
2477
|
+
if (!installed && vitestPackages.length > 0) {
|
|
2478
|
+
installed = vitestPackages.every((rel) => {
|
|
2479
|
+
const pkgDir = path14.join(projectRoot, rel);
|
|
2480
|
+
return hasDependency(pkgDir, "@vitest/coverage-v8") || hasDependency(pkgDir, "@vitest/coverage-istanbul");
|
|
2481
|
+
});
|
|
2486
2482
|
}
|
|
2487
|
-
|
|
2483
|
+
const isWorkspace = scanResult.packages.length > 1;
|
|
2484
|
+
const addCmd = pm === "yarn" ? "yarn add -D" : pm === "pnpm" && isWorkspace ? "pnpm add -D -w" : pm === "npm" ? "npm install -D" : `${pm} add -D`;
|
|
2485
|
+
const affectedPackages = vitestPackages.length > 1 ? vitestPackages : void 0;
|
|
2486
|
+
const reason = affectedPackages ? `Required for coverage in: ${affectedPackages.join(", ")}` : "Required for coverage percentage checks with vitest";
|
|
2487
|
+
return [
|
|
2488
|
+
{
|
|
2489
|
+
label: "@vitest/coverage-v8",
|
|
2490
|
+
installed,
|
|
2491
|
+
installCommand: installed ? void 0 : `${addCmd} @vitest/coverage-v8`,
|
|
2492
|
+
reason,
|
|
2493
|
+
affectedPackages
|
|
2494
|
+
}
|
|
2495
|
+
];
|
|
2488
2496
|
}
|
|
2489
2497
|
function displayMissingPrereqs(prereqs) {
|
|
2490
2498
|
const missing = prereqs.filter((p) => !p.installed);
|
|
2491
2499
|
for (const m of missing) {
|
|
2492
|
-
|
|
2500
|
+
const suffix = m.affectedPackages ? ` \u2014 needed for coverage in: ${m.affectedPackages.join(", ")}` : ` \u2014 ${m.reason}`;
|
|
2501
|
+
console.log(` ${chalk8.yellow("!")} ${m.label} not installed${suffix}`);
|
|
2493
2502
|
if (m.installCommand) {
|
|
2494
2503
|
console.log(` Install: ${chalk8.cyan(m.installCommand)}`);
|
|
2495
2504
|
}
|
|
@@ -2498,15 +2507,19 @@ function displayMissingPrereqs(prereqs) {
|
|
|
2498
2507
|
async function promptMissingPrereqs(projectRoot, prereqs) {
|
|
2499
2508
|
const missing = prereqs.filter((p) => !p.installed);
|
|
2500
2509
|
if (missing.length === 0) return { disableCoverage: false };
|
|
2501
|
-
const prereqLines = prereqs.map(
|
|
2502
|
-
(p
|
|
2503
|
-
|
|
2510
|
+
const prereqLines = prereqs.map((p) => {
|
|
2511
|
+
if (p.installed) return `\u2713 ${p.label}`;
|
|
2512
|
+
const detail = p.affectedPackages ? `needed by: ${p.affectedPackages.join(", ")}` : p.reason;
|
|
2513
|
+
return `\u2717 ${p.label} \u2014 ${detail}`;
|
|
2514
|
+
}).join("\n");
|
|
2504
2515
|
clack7.note(prereqLines, "Coverage prerequisites");
|
|
2505
2516
|
let disableCoverage = false;
|
|
2506
2517
|
for (const m of missing) {
|
|
2507
2518
|
if (!m.installCommand) continue;
|
|
2519
|
+
const pkgCount = m.affectedPackages?.length;
|
|
2520
|
+
const message = pkgCount ? `${m.label} is not installed. Required for coverage in ${pkgCount} packages using vitest.` : `${m.label} is not installed. It is required for coverage percentage checks.`;
|
|
2508
2521
|
const choice = await clack7.select({
|
|
2509
|
-
message
|
|
2522
|
+
message,
|
|
2510
2523
|
options: [
|
|
2511
2524
|
{
|
|
2512
2525
|
value: "install",
|
|
@@ -2669,7 +2682,6 @@ function addLefthookPreCommit(lefthookPath) {
|
|
|
2669
2682
|
function detectHookManager(projectRoot) {
|
|
2670
2683
|
if (fs16.existsSync(path16.join(projectRoot, "lefthook.yml"))) return "Lefthook";
|
|
2671
2684
|
if (fs16.existsSync(path16.join(projectRoot, ".husky"))) return "Husky";
|
|
2672
|
-
if (fs16.existsSync(path16.join(projectRoot, ".git"))) return "git hook";
|
|
2673
2685
|
return void 0;
|
|
2674
2686
|
}
|
|
2675
2687
|
function setupClaudeCodeHook(projectRoot) {
|
|
@@ -2723,7 +2735,7 @@ function setupClaudeMdReference(projectRoot) {
|
|
|
2723
2735
|
fs16.writeFileSync(claudeMdPath, prefix + ref);
|
|
2724
2736
|
console.log(` ${chalk9.green("\u2713")} CLAUDE.md \u2014 added @.viberails/context.md reference`);
|
|
2725
2737
|
}
|
|
2726
|
-
function setupGithubAction(projectRoot, packageManager) {
|
|
2738
|
+
function setupGithubAction(projectRoot, packageManager, options) {
|
|
2727
2739
|
const workflowDir = path16.join(projectRoot, ".github", "workflows");
|
|
2728
2740
|
const workflowPath = path16.join(workflowDir, "viberails.yml");
|
|
2729
2741
|
if (fs16.existsSync(workflowPath)) {
|
|
@@ -2759,7 +2771,16 @@ function setupGithubAction(projectRoot, packageManager) {
|
|
|
2759
2771
|
" node-version: 22",
|
|
2760
2772
|
pm !== "npm" ? ` cache: ${pm}` : "",
|
|
2761
2773
|
"",
|
|
2762
|
-
` - run: ${installCmd}
|
|
2774
|
+
` - run: ${installCmd}`
|
|
2775
|
+
);
|
|
2776
|
+
if (options?.typecheck) {
|
|
2777
|
+
lines.push(` - run: ${runPrefix} tsc --noEmit`);
|
|
2778
|
+
}
|
|
2779
|
+
if (options?.linter) {
|
|
2780
|
+
const lintCmd = options.linter === "biome" ? "biome check ." : "eslint .";
|
|
2781
|
+
lines.push(` - run: ${runPrefix} ${lintCmd}`);
|
|
2782
|
+
}
|
|
2783
|
+
lines.push(
|
|
2763
2784
|
` - run: ${runPrefix} viberails check --enforce --diff-base origin/\${{ github.event.pull_request.base.ref }}`,
|
|
2764
2785
|
""
|
|
2765
2786
|
);
|
|
@@ -2880,7 +2901,10 @@ function setupSelectedIntegrations(projectRoot, integrations, opts) {
|
|
|
2880
2901
|
created.push("CLAUDE.md \u2014 added @.viberails/context.md reference");
|
|
2881
2902
|
}
|
|
2882
2903
|
if (integrations.githubAction) {
|
|
2883
|
-
const t = setupGithubAction(projectRoot, opts.packageManager ?? "npm"
|
|
2904
|
+
const t = setupGithubAction(projectRoot, opts.packageManager ?? "npm", {
|
|
2905
|
+
linter: integrations.lintHook ? opts.linter : void 0,
|
|
2906
|
+
typecheck: integrations.typecheckHook
|
|
2907
|
+
});
|
|
2884
2908
|
if (t) created.push(`${t} \u2014 blocks PRs on violations`);
|
|
2885
2909
|
}
|
|
2886
2910
|
return created;
|
|
@@ -2948,11 +2972,15 @@ async function initNonInteractive(projectRoot, configPath) {
|
|
|
2948
2972
|
setupClaudeMdReference(projectRoot);
|
|
2949
2973
|
const rootPkg = config.packages[0];
|
|
2950
2974
|
const rootPkgPm = rootPkg?.stack?.packageManager ?? "npm";
|
|
2951
|
-
const
|
|
2975
|
+
const linter = rootPkg?.stack?.linter?.split("@")[0];
|
|
2976
|
+
const isTypeScript = rootPkg?.stack?.language === "typescript";
|
|
2977
|
+
const actionTarget = setupGithubAction(projectRoot, rootPkgPm, {
|
|
2978
|
+
linter,
|
|
2979
|
+
typecheck: isTypeScript
|
|
2980
|
+
});
|
|
2952
2981
|
const hookManager = detectHookManager(projectRoot);
|
|
2953
2982
|
const hasHookManager = hookManager === "Lefthook" || hookManager === "Husky";
|
|
2954
2983
|
const preCommitTarget = hasHookManager ? setupPreCommitHook(projectRoot) : void 0;
|
|
2955
|
-
const linter = rootPkg?.stack?.linter?.split("@")[0];
|
|
2956
2984
|
const ok = chalk11.green("\u2713");
|
|
2957
2985
|
const created = [
|
|
2958
2986
|
`${ok} ${path18.basename(configPath)}`,
|
|
@@ -3045,7 +3073,8 @@ async function initInteractive(projectRoot, configPath, options) {
|
|
|
3045
3073
|
const integrations = await promptIntegrations(projectRoot, hookManager, {
|
|
3046
3074
|
isTypeScript: rootPkgStack?.language === "typescript",
|
|
3047
3075
|
linter: rootPkgStack?.linter?.split("@")[0],
|
|
3048
|
-
packageManager: rootPkgStack?.packageManager
|
|
3076
|
+
packageManager: rootPkgStack?.packageManager,
|
|
3077
|
+
isWorkspace: config.packages.length > 1
|
|
3049
3078
|
});
|
|
3050
3079
|
const shouldWrite = await confirm3("Write configuration and set up selected integrations?");
|
|
3051
3080
|
if (!shouldWrite) {
|
|
@@ -3181,7 +3210,7 @@ ${chalk12.bold("Synced:")}`);
|
|
|
3181
3210
|
}
|
|
3182
3211
|
|
|
3183
3212
|
// src/index.ts
|
|
3184
|
-
var VERSION = "0.5.
|
|
3213
|
+
var VERSION = "0.5.4";
|
|
3185
3214
|
var program = new Command();
|
|
3186
3215
|
program.name("viberails").description("Guardrails for vibe coding").version(VERSION);
|
|
3187
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) => {
|