viberails 0.6.11 → 0.6.12

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 CHANGED
@@ -99,8 +99,8 @@ var FILE_NAMING_OPTIONS = [
99
99
  { value: "snake_case", label: "snake_case" }
100
100
  ];
101
101
  var COMPONENT_NAMING_OPTIONS = [
102
- { value: "PascalCase", label: "PascalCase", hint: "MyComponent.tsx" },
103
- { value: "camelCase", label: "camelCase", hint: "myComponent.tsx" }
102
+ { value: "PascalCase", label: "PascalCase", hint: "e.g. MyComponent, UserProfile" },
103
+ { value: "camelCase", label: "camelCase", hint: "e.g. myComponent, userProfile" }
104
104
  ];
105
105
  var HOOK_NAMING_OPTIONS = [
106
106
  { value: "useXxx", label: "useXxx", hint: "useAuth, useFormData" },
@@ -3008,7 +3008,169 @@ var clack11 = __toESM(require("@clack/prompts"), 1);
3008
3008
 
3009
3009
  // src/utils/prompt-main-menu-handlers.ts
3010
3010
  var clack10 = __toESM(require("@clack/prompts"), 1);
3011
+ var import_chalk13 = __toESM(require("chalk"), 1);
3012
+
3013
+ // src/utils/prompt-main-menu-hints.ts
3011
3014
  var import_chalk12 = __toESM(require("chalk"), 1);
3015
+ function fileLimitsHint(config) {
3016
+ const max = config.rules.maxFileLines;
3017
+ const test = config.rules.maxTestFileLines;
3018
+ return test > 0 ? `${max} lines, tests ${test}` : `${max} lines`;
3019
+ }
3020
+ function getEffectiveFileNaming(config) {
3021
+ const rootPkg = getRootPackage(config.packages);
3022
+ if (rootPkg.conventions?.fileNaming) {
3023
+ return { naming: rootPkg.conventions.fileNaming, source: "root" };
3024
+ }
3025
+ if (config.packages.length > 1) {
3026
+ const namingValues = config.packages.map((p) => p.conventions?.fileNaming).filter((n) => !!n);
3027
+ if (namingValues.length > 0 && new Set(namingValues).size === 1) {
3028
+ return { naming: namingValues[0], source: "consensus" };
3029
+ }
3030
+ }
3031
+ return void 0;
3032
+ }
3033
+ function fileNamingHint(config, scanResult) {
3034
+ if (!config.rules.enforceNaming) return "not enforced";
3035
+ const effective = getEffectiveFileNaming(config);
3036
+ if (effective) {
3037
+ const detected = scanResult.packages.some(
3038
+ (p) => p.conventions.fileNaming?.value === effective.naming && p.conventions.fileNaming.confidence !== "low"
3039
+ );
3040
+ return detected ? `${effective.naming} (detected)` : effective.naming;
3041
+ }
3042
+ return "not set \u2014 select to configure";
3043
+ }
3044
+ function fileNamingStatus(config) {
3045
+ if (!config.rules.enforceNaming) return "unconfigured";
3046
+ return getEffectiveFileNaming(config) ? "ok" : "needs-input";
3047
+ }
3048
+ function missingTestsHint(config) {
3049
+ if (!config.rules.enforceMissingTests) return "not enforced";
3050
+ const rootPkg = getRootPackage(config.packages);
3051
+ const pattern = rootPkg.structure?.testPattern;
3052
+ return pattern ? `enforced (${pattern})` : "enforced";
3053
+ }
3054
+ function coverageHint(config, hasTestRunner) {
3055
+ if (config.rules.testCoverage === 0) return "disabled";
3056
+ if (!hasTestRunner)
3057
+ return `${config.rules.testCoverage}% target (inactive \u2014 no test runner)`;
3058
+ const isMonorepo = config.packages.length > 1;
3059
+ if (isMonorepo) {
3060
+ const withCov = config.packages.filter(
3061
+ (p) => (p.rules?.testCoverage ?? config.rules.testCoverage) > 0
3062
+ );
3063
+ const exempt = config.packages.length - withCov.length;
3064
+ return exempt > 0 ? `${config.rules.testCoverage}% (${withCov.length}/${config.packages.length} packages, ${exempt} exempt)` : `${config.rules.testCoverage}%`;
3065
+ }
3066
+ return `${config.rules.testCoverage}%`;
3067
+ }
3068
+ function aiContextHint(config) {
3069
+ const rootPkg = getRootPackage(config.packages);
3070
+ const count = [
3071
+ rootPkg.conventions?.componentNaming,
3072
+ rootPkg.conventions?.hookNaming,
3073
+ rootPkg.conventions?.importAlias
3074
+ ].filter(Boolean).length;
3075
+ if (count === 3) return "all set";
3076
+ if (count > 0) return `${count} of 3 conventions`;
3077
+ return "none set \u2014 optional AI guidelines";
3078
+ }
3079
+ function aiContextStatus(config) {
3080
+ const rootPkg = getRootPackage(config.packages);
3081
+ const count = [
3082
+ rootPkg.conventions?.componentNaming,
3083
+ rootPkg.conventions?.hookNaming,
3084
+ rootPkg.conventions?.importAlias
3085
+ ].filter(Boolean).length;
3086
+ if (count === 3) return "ok";
3087
+ if (count > 0) return "partial";
3088
+ return "unconfigured";
3089
+ }
3090
+ function packageOverridesHint(config) {
3091
+ const rootNaming = getRootPackage(config.packages).conventions?.fileNaming;
3092
+ const editable = config.packages.filter((p) => p.path !== ".");
3093
+ const customized = editable.filter(
3094
+ (p) => p.rules || p.coverage || p.conventions?.fileNaming !== void 0 && p.conventions.fileNaming !== rootNaming
3095
+ ).length;
3096
+ return customized > 0 ? `${editable.length} packages (${customized} customized)` : `${editable.length} packages`;
3097
+ }
3098
+ function boundariesHint(config, state) {
3099
+ if (!state.visited.boundaries || !config.rules.enforceBoundaries) return "not enabled";
3100
+ const deny = config.boundaries?.deny;
3101
+ if (!deny) return "enabled";
3102
+ const ruleCount = Object.values(deny).reduce((s, a) => s + a.length, 0);
3103
+ const pkgCount = Object.keys(deny).length;
3104
+ return `${ruleCount} rules across ${pkgCount} packages`;
3105
+ }
3106
+ function packageOverridesStatus(config) {
3107
+ const rootNaming = getRootPackage(config.packages).conventions?.fileNaming;
3108
+ const editable = config.packages.filter((p) => p.path !== ".");
3109
+ const customized = editable.some(
3110
+ (p) => p.rules || p.coverage || p.conventions?.fileNaming !== void 0 && p.conventions.fileNaming !== rootNaming
3111
+ );
3112
+ return customized ? "ok" : "unconfigured";
3113
+ }
3114
+ function statusIcon(status) {
3115
+ if (status === "ok") return import_chalk12.default.green("\u2713");
3116
+ if (status === "needs-input") return import_chalk12.default.yellow("?");
3117
+ if (status === "unconfigured") return import_chalk12.default.dim("-");
3118
+ return import_chalk12.default.yellow("~");
3119
+ }
3120
+ function buildMainMenuOptions(config, scanResult, state) {
3121
+ const namingStatus = fileNamingStatus(config);
3122
+ const coverageStatus = config.rules.testCoverage === 0 ? "unconfigured" : !state.hasTestRunner ? "partial" : "ok";
3123
+ const missingTestsStatus = config.rules.enforceMissingTests ? "ok" : "unconfigured";
3124
+ const options = [
3125
+ {
3126
+ value: "fileLimits",
3127
+ label: `${statusIcon("ok")} Max file size`,
3128
+ hint: fileLimitsHint(config)
3129
+ },
3130
+ {
3131
+ value: "fileNaming",
3132
+ label: `${statusIcon(namingStatus)} File naming`,
3133
+ hint: fileNamingHint(config, scanResult)
3134
+ },
3135
+ {
3136
+ value: "missingTests",
3137
+ label: `${statusIcon(missingTestsStatus)} Missing tests`,
3138
+ hint: missingTestsHint(config)
3139
+ },
3140
+ {
3141
+ value: "coverage",
3142
+ label: `${statusIcon(coverageStatus)} Coverage`,
3143
+ hint: coverageHint(config, state.hasTestRunner)
3144
+ },
3145
+ {
3146
+ value: "aiContext",
3147
+ label: `${statusIcon(aiContextStatus(config))} AI context`,
3148
+ hint: aiContextHint(config)
3149
+ }
3150
+ ];
3151
+ if (config.packages.length > 1) {
3152
+ const bIcon = statusIcon(
3153
+ state.visited.boundaries && config.rules.enforceBoundaries ? "ok" : "unconfigured"
3154
+ );
3155
+ const poIcon = statusIcon(packageOverridesStatus(config));
3156
+ options.push(
3157
+ {
3158
+ value: "packageOverrides",
3159
+ label: `${poIcon} Per-package overrides`,
3160
+ hint: packageOverridesHint(config)
3161
+ },
3162
+ { value: "boundaries", label: `${bIcon} Boundaries`, hint: boundariesHint(config, state) }
3163
+ );
3164
+ }
3165
+ options.push(
3166
+ { value: "reset", label: " Reset all to defaults" },
3167
+ { value: "review", label: " Review scan details", hint: "detected stack & conventions" },
3168
+ { value: "done", label: " Done \u2014 write config" }
3169
+ );
3170
+ return options;
3171
+ }
3172
+
3173
+ // src/utils/prompt-main-menu-handlers.ts
3012
3174
  async function handleFileNaming(config, scanResult) {
3013
3175
  const isMonorepo = config.packages.length > 1;
3014
3176
  if (isMonorepo) {
@@ -3032,10 +3194,11 @@ async function handleFileNaming(config, scanResult) {
3032
3194
  return { value: opt.value, label: opt.label };
3033
3195
  });
3034
3196
  const rootPkg = getRootPackage(config.packages);
3197
+ const effective = getEffectiveFileNaming(config);
3035
3198
  const selected = await clack10.select({
3036
3199
  message: isMonorepo ? "Default file naming convention" : "File naming convention",
3037
3200
  options: [...namingOptions, { value: SENTINEL_SKIP, label: "Don't enforce" }],
3038
- initialValue: rootPkg.conventions?.fileNaming ?? SENTINEL_SKIP
3201
+ initialValue: effective?.naming ?? SENTINEL_SKIP
3039
3202
  });
3040
3203
  if (isCancelled(selected)) return;
3041
3204
  if (selected === SENTINEL_SKIP) {
@@ -3152,8 +3315,8 @@ async function handleAiContext(config) {
3152
3315
  const rootPkg = getRootPackage(config.packages);
3153
3316
  rootPkg.conventions = rootPkg.conventions ?? {};
3154
3317
  while (true) {
3155
- const ok = import_chalk12.default.green("\u2713");
3156
- const unset = import_chalk12.default.dim("-");
3318
+ const ok = import_chalk13.default.green("\u2713");
3319
+ const unset = import_chalk13.default.dim("-");
3157
3320
  const options = [
3158
3321
  {
3159
3322
  value: "componentNaming",
@@ -3235,155 +3398,6 @@ async function handleAiContext(config) {
3235
3398
  }
3236
3399
  }
3237
3400
 
3238
- // src/utils/prompt-main-menu-hints.ts
3239
- var import_chalk13 = __toESM(require("chalk"), 1);
3240
- function fileLimitsHint(config) {
3241
- const max = config.rules.maxFileLines;
3242
- const test = config.rules.maxTestFileLines;
3243
- return test > 0 ? `${max} lines, tests ${test}` : `${max} lines`;
3244
- }
3245
- function fileNamingHint(config, scanResult) {
3246
- const rootPkg = getRootPackage(config.packages);
3247
- const naming = rootPkg.conventions?.fileNaming;
3248
- if (!config.rules.enforceNaming) return "not enforced";
3249
- if (naming) {
3250
- const detected = scanResult.packages.some(
3251
- (p) => p.conventions.fileNaming?.value === naming && p.conventions.fileNaming.confidence === "high"
3252
- );
3253
- return detected ? `${naming} (detected)` : naming;
3254
- }
3255
- return "not set \u2014 select to configure";
3256
- }
3257
- function fileNamingStatus(config) {
3258
- if (!config.rules.enforceNaming) return "unconfigured";
3259
- const rootPkg = getRootPackage(config.packages);
3260
- return rootPkg.conventions?.fileNaming ? "ok" : "needs-input";
3261
- }
3262
- function missingTestsHint(config) {
3263
- if (!config.rules.enforceMissingTests) return "not enforced";
3264
- const rootPkg = getRootPackage(config.packages);
3265
- const pattern = rootPkg.structure?.testPattern;
3266
- return pattern ? `enforced (${pattern})` : "enforced";
3267
- }
3268
- function coverageHint(config, hasTestRunner) {
3269
- if (config.rules.testCoverage === 0) return "disabled";
3270
- if (!hasTestRunner)
3271
- return `${config.rules.testCoverage}% target (inactive \u2014 no test runner)`;
3272
- const isMonorepo = config.packages.length > 1;
3273
- if (isMonorepo) {
3274
- const withCov = config.packages.filter(
3275
- (p) => (p.rules?.testCoverage ?? config.rules.testCoverage) > 0
3276
- );
3277
- const exempt = config.packages.length - withCov.length;
3278
- return exempt > 0 ? `${config.rules.testCoverage}% (${withCov.length}/${config.packages.length} packages, ${exempt} exempt)` : `${config.rules.testCoverage}%`;
3279
- }
3280
- return `${config.rules.testCoverage}%`;
3281
- }
3282
- function aiContextHint(config) {
3283
- const rootPkg = getRootPackage(config.packages);
3284
- const count = [
3285
- rootPkg.conventions?.componentNaming,
3286
- rootPkg.conventions?.hookNaming,
3287
- rootPkg.conventions?.importAlias
3288
- ].filter(Boolean).length;
3289
- if (count === 3) return "all set";
3290
- if (count > 0) return `${count} of 3 conventions`;
3291
- return "none set \u2014 optional AI guidelines";
3292
- }
3293
- function aiContextStatus(config) {
3294
- const rootPkg = getRootPackage(config.packages);
3295
- const count = [
3296
- rootPkg.conventions?.componentNaming,
3297
- rootPkg.conventions?.hookNaming,
3298
- rootPkg.conventions?.importAlias
3299
- ].filter(Boolean).length;
3300
- if (count === 3) return "ok";
3301
- if (count > 0) return "partial";
3302
- return "unconfigured";
3303
- }
3304
- function packageOverridesHint(config) {
3305
- const rootNaming = getRootPackage(config.packages).conventions?.fileNaming;
3306
- const editable = config.packages.filter((p) => p.path !== ".");
3307
- const customized = editable.filter(
3308
- (p) => p.rules || p.coverage || p.conventions?.fileNaming !== void 0 && p.conventions.fileNaming !== rootNaming
3309
- ).length;
3310
- return customized > 0 ? `${editable.length} packages (${customized} customized)` : `${editable.length} packages`;
3311
- }
3312
- function boundariesHint(config, state) {
3313
- if (!state.visited.boundaries || !config.rules.enforceBoundaries) return "not enabled";
3314
- const deny = config.boundaries?.deny;
3315
- if (!deny) return "enabled";
3316
- const ruleCount = Object.values(deny).reduce((s, a) => s + a.length, 0);
3317
- const pkgCount = Object.keys(deny).length;
3318
- return `${ruleCount} rules across ${pkgCount} packages`;
3319
- }
3320
- function packageOverridesStatus(config) {
3321
- const rootNaming = getRootPackage(config.packages).conventions?.fileNaming;
3322
- const editable = config.packages.filter((p) => p.path !== ".");
3323
- const customized = editable.some(
3324
- (p) => p.rules || p.coverage || p.conventions?.fileNaming !== void 0 && p.conventions.fileNaming !== rootNaming
3325
- );
3326
- return customized ? "ok" : "unconfigured";
3327
- }
3328
- function statusIcon(status) {
3329
- if (status === "ok") return import_chalk13.default.green("\u2713");
3330
- if (status === "needs-input") return import_chalk13.default.yellow("?");
3331
- if (status === "unconfigured") return import_chalk13.default.dim("-");
3332
- return import_chalk13.default.yellow("~");
3333
- }
3334
- function buildMainMenuOptions(config, scanResult, state) {
3335
- const namingStatus = fileNamingStatus(config);
3336
- const coverageStatus = config.rules.testCoverage === 0 ? "unconfigured" : !state.hasTestRunner ? "partial" : "ok";
3337
- const missingTestsStatus = config.rules.enforceMissingTests ? "ok" : "unconfigured";
3338
- const options = [
3339
- {
3340
- value: "fileLimits",
3341
- label: `${statusIcon("ok")} Max file size`,
3342
- hint: fileLimitsHint(config)
3343
- },
3344
- {
3345
- value: "fileNaming",
3346
- label: `${statusIcon(namingStatus)} File naming`,
3347
- hint: fileNamingHint(config, scanResult)
3348
- },
3349
- {
3350
- value: "missingTests",
3351
- label: `${statusIcon(missingTestsStatus)} Missing tests`,
3352
- hint: missingTestsHint(config)
3353
- },
3354
- {
3355
- value: "coverage",
3356
- label: `${statusIcon(coverageStatus)} Coverage`,
3357
- hint: coverageHint(config, state.hasTestRunner)
3358
- },
3359
- {
3360
- value: "aiContext",
3361
- label: `${statusIcon(aiContextStatus(config))} AI context`,
3362
- hint: aiContextHint(config)
3363
- }
3364
- ];
3365
- if (config.packages.length > 1) {
3366
- const bIcon = statusIcon(
3367
- state.visited.boundaries && config.rules.enforceBoundaries ? "ok" : "unconfigured"
3368
- );
3369
- const poIcon = statusIcon(packageOverridesStatus(config));
3370
- options.push(
3371
- {
3372
- value: "packageOverrides",
3373
- label: `${poIcon} Per-package overrides`,
3374
- hint: packageOverridesHint(config)
3375
- },
3376
- { value: "boundaries", label: `${bIcon} Boundaries`, hint: boundariesHint(config, state) }
3377
- );
3378
- }
3379
- options.push(
3380
- { value: "reset", label: " Reset all to defaults" },
3381
- { value: "review", label: " Review scan details", hint: "detected stack & conventions" },
3382
- { value: "done", label: " Done \u2014 write config" }
3383
- );
3384
- return options;
3385
- }
3386
-
3387
3401
  // src/utils/prompt-main-menu.ts
3388
3402
  async function promptMainMenu(config, scanResult, opts) {
3389
3403
  const originalConfig = structuredClone(config);
@@ -4325,7 +4339,7 @@ ${import_chalk19.default.bold("Synced:")}`);
4325
4339
  }
4326
4340
 
4327
4341
  // src/index.ts
4328
- var VERSION = "0.6.11";
4342
+ var VERSION = "0.6.12";
4329
4343
  var program = new import_commander.Command();
4330
4344
  program.name("viberails").description("Guardrails for vibe coding").version(VERSION);
4331
4345
  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) => {