@viberails/config 0.2.1 → 0.2.2

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
@@ -49,7 +49,20 @@ var DEFAULT_RULES = {
49
49
  enforceNaming: true,
50
50
  enforceBoundaries: false
51
51
  };
52
- var DEFAULT_IGNORE = ["**/*.d.ts", "dist/**", "node_modules/**"];
52
+ var DEFAULT_IGNORE = [
53
+ "**/*.d.ts",
54
+ "dist/**",
55
+ "node_modules/**",
56
+ "build/**",
57
+ ".next/**",
58
+ ".expo/**",
59
+ ".output/**",
60
+ ".svelte-kit/**",
61
+ ".turbo/**",
62
+ "coverage/**",
63
+ "public/**",
64
+ ".viberails/**"
65
+ ];
53
66
 
54
67
  // src/generate-config.ts
55
68
  var path = __toESM(require("path"), 1);
@@ -655,7 +668,7 @@ var configSchema = {
655
668
  };
656
669
 
657
670
  // src/index.ts
658
- var VERSION = "0.1.0";
671
+ var VERSION = "0.2.2";
659
672
  // Annotate the CommonJS export names for ESM import in node:
660
673
  0 && (module.exports = {
661
674
  DEFAULT_IGNORE,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/defaults.ts","../src/generate-config.ts","../src/generate-overrides.ts","../src/load-config.ts","../src/merge-config.ts","../src/schema.ts"],"sourcesContent":["export const VERSION = '0.1.0';\n\nexport { DEFAULT_IGNORE, DEFAULT_RULES } from './defaults.js';\nexport { generateConfig } from './generate-config.js';\nexport { loadConfig, loadConfigSafe } from './load-config.js';\nexport { mergeConfig } from './merge-config.js';\nexport { configSchema } from './schema.js';\n","import type { ConfigRules } from '@viberails/types';\n\n/**\n * Default rule thresholds and toggles for a new viberails config.\n * These values are intentionally conservative to build trust on first run.\n */\nexport const DEFAULT_RULES: ConfigRules = {\n maxFileLines: 300,\n maxFunctionLines: 50,\n requireTests: true,\n enforceNaming: true,\n enforceBoundaries: false,\n};\n\n/**\n * Default glob patterns for files and directories to ignore.\n */\nexport const DEFAULT_IGNORE: string[] = ['**/*.d.ts', 'dist/**', 'node_modules/**'];\n","import * as path from 'node:path';\nimport type {\n ConfigConventions,\n ConfigStack,\n ConfigStructure,\n ConventionValue,\n DetectedConvention,\n DirectoryRole,\n ScanResult,\n StackItem,\n ViberailsConfig,\n} from '@viberails/types';\nimport { DEFAULT_IGNORE, DEFAULT_RULES } from './defaults.js';\nimport { generatePackageOverrides } from './generate-overrides.js';\n\n/**\n * Format a StackItem as a config string: `\"name@version\"` or `\"name\"`.\n */\nexport function formatStackItem(item: StackItem): string {\n return item.version ? `${item.name}@${item.version}` : item.name;\n}\n\n/**\n * Map DetectedStack → ConfigStack by formatting each StackItem.\n */\nfunction mapStack(scanResult: ScanResult): ConfigStack {\n const { stack } = scanResult;\n const config: ConfigStack = {\n language: formatStackItem(stack.language),\n packageManager: formatStackItem(stack.packageManager),\n };\n\n if (stack.framework) config.framework = formatStackItem(stack.framework);\n if (stack.styling) config.styling = formatStackItem(stack.styling);\n if (stack.backend) config.backend = formatStackItem(stack.backend);\n if (stack.linter) config.linter = formatStackItem(stack.linter);\n if (stack.formatter) config.formatter = formatStackItem(stack.formatter);\n if (stack.testRunner) config.testRunner = formatStackItem(stack.testRunner);\n\n return config;\n}\n\n/** Directory roles that map to ConfigStructure fields. */\nconst ROLE_TO_FIELD: Partial<Record<DirectoryRole, keyof ConfigStructure>> = {\n pages: 'pages',\n components: 'components',\n hooks: 'hooks',\n utils: 'utils',\n types: 'types',\n tests: 'tests',\n};\n\n/**\n * Map DetectedStructure → ConfigStructure by finding the first directory\n * for each known role.\n */\nfunction mapStructure(scanResult: ScanResult): ConfigStructure {\n const { structure } = scanResult;\n const config: ConfigStructure = {};\n\n if (structure.srcDir) {\n config.srcDir = structure.srcDir;\n }\n\n for (const dir of structure.directories) {\n const field = ROLE_TO_FIELD[dir.role];\n if (field && config[field] === undefined) {\n (config as Record<string, string>)[field] = dir.path;\n }\n }\n\n if (structure.testPattern) {\n config.testPattern = structure.testPattern.value;\n }\n\n return config;\n}\n\n/**\n * Convert a DetectedConvention to a ConventionValue with metadata.\n * Returns undefined for low-confidence conventions (they are omitted).\n */\nexport function mapConvention(convention: DetectedConvention): ConventionValue | undefined {\n if (convention.confidence === 'low') {\n return undefined;\n }\n\n return {\n value: convention.value,\n _confidence: convention.confidence,\n _consistency: convention.consistency,\n };\n}\n\n/** Convention keys from ScanResult that map to ConfigConventions fields. */\nexport const CONVENTION_KEYS: (keyof ConfigConventions)[] = [\n 'fileNaming',\n 'componentNaming',\n 'hookNaming',\n 'importAlias',\n];\n\n/**\n * Map scanner conventions → ConfigConventions, omitting low-confidence entries.\n */\nfunction mapConventions(scanResult: ScanResult): ConfigConventions {\n const config: ConfigConventions = {};\n\n for (const key of CONVENTION_KEYS) {\n const detected = scanResult.conventions[key];\n if (detected) {\n const value = mapConvention(detected);\n if (value !== undefined) {\n config[key] = value;\n }\n }\n }\n\n return config;\n}\n\n/**\n * Generate a ViberailsConfig from scan results.\n *\n * Maps the scanner's DetectedStack, DetectedStructure, and conventions\n * into the config format with smart defaults. Low-confidence conventions\n * are omitted. The project name is derived from the root directory basename.\n *\n * @param scanResult - The output of scanning a project\n * @returns A complete ViberailsConfig ready to be written as JSON\n */\nexport function generateConfig(scanResult: ScanResult): ViberailsConfig {\n const config: ViberailsConfig = {\n $schema: 'https://viberails.sh/schema/v1.json',\n version: 1,\n name: path.basename(scanResult.root),\n enforcement: 'warn',\n stack: mapStack(scanResult),\n structure: mapStructure(scanResult),\n conventions: mapConventions(scanResult),\n rules: { ...DEFAULT_RULES },\n ignore: [...DEFAULT_IGNORE],\n };\n\n if (scanResult.workspace) {\n config.workspace = {\n packages: scanResult.workspace.packages.map((p) => p.relativePath),\n isMonorepo: true,\n };\n config.boundaries = [];\n }\n\n const packageOverrides = generatePackageOverrides(scanResult, config);\n if (packageOverrides) {\n config.packages = packageOverrides;\n }\n\n return config;\n}\n","import type {\n ConfigConventions,\n ConfigStack,\n DetectedConvention,\n PackageConfigOverrides,\n ScanResult,\n ViberailsConfig,\n} from '@viberails/types';\nimport { CONVENTION_KEYS, formatStackItem, mapConvention } from './generate-config.js';\n\n/**\n * Compare a package's conventions against the global config conventions.\n * Returns only the differing conventions, or undefined if all match.\n */\nexport function conventionsDiffer(\n pkgConventions: Record<string, DetectedConvention>,\n globalConventions: ConfigConventions,\n): Partial<ConfigConventions> | undefined {\n const overrides: Partial<ConfigConventions> = {};\n let hasDiff = false;\n\n for (const key of CONVENTION_KEYS) {\n const detected = pkgConventions[key];\n if (!detected) continue;\n\n const mapped = mapConvention(detected);\n if (mapped === undefined) continue;\n\n const globalValue = globalConventions[key];\n const globalStr = typeof globalValue === 'string' ? globalValue : globalValue?.value;\n if (detected.value !== globalStr) {\n overrides[key] = mapped;\n hasDiff = true;\n }\n }\n\n return hasDiff ? overrides : undefined;\n}\n\n/**\n * Generate per-package config overrides for packages whose stack or\n * conventions differ from the aggregate global config.\n */\nexport function generatePackageOverrides(\n scanResult: ScanResult,\n globalConfig: ViberailsConfig,\n): PackageConfigOverrides[] | undefined {\n if (!scanResult.packages || scanResult.packages.length <= 1) return undefined;\n\n const overrides: PackageConfigOverrides[] = [];\n\n for (const pkg of scanResult.packages) {\n const override: PackageConfigOverrides = {\n name: pkg.name,\n path: pkg.relativePath,\n };\n let hasDiff = false;\n\n // Compare stack fields — only include overrides when the package has a\n // value that differs from the global, not when the package simply lacks the field\n const stackOverride: Partial<ConfigStack> = {};\n let hasStackDiff = false;\n\n const optionalStackFields = [\n 'framework',\n 'styling',\n 'backend',\n 'linter',\n 'formatter',\n 'testRunner',\n ] as const;\n for (const field of optionalStackFields) {\n const pkgItem = pkg.stack[field];\n if (!pkgItem) continue;\n const pkgValue = formatStackItem(pkgItem);\n if (pkgValue !== globalConfig.stack[field]) {\n stackOverride[field] = pkgValue;\n hasStackDiff = true;\n }\n }\n\n if (hasStackDiff) {\n override.stack = stackOverride;\n hasDiff = true;\n }\n\n // Compare conventions\n const conventionOverrides = conventionsDiffer(pkg.conventions, globalConfig.conventions);\n if (conventionOverrides) {\n override.conventions = conventionOverrides;\n hasDiff = true;\n }\n\n if (hasDiff) {\n overrides.push(override);\n }\n }\n\n return overrides.length > 0 ? overrides : undefined;\n}\n","import * as fs from 'node:fs/promises';\nimport type { ViberailsConfig } from '@viberails/types';\n\n/**\n * Validate that a parsed object has the required ViberailsConfig fields\n * and that their types are correct.\n * Throws a descriptive error if validation fails.\n */\nfunction validateConfig(parsed: Record<string, unknown>, configPath: string): void {\n const errors: string[] = [];\n\n // Required top-level fields\n const required = ['version', 'name', 'stack', 'rules'] as const;\n const missing = required.filter((field) => parsed[field] === undefined);\n if (missing.length > 0) {\n throw new Error(\n `Invalid viberails config at ${configPath}: missing required field(s): ${missing.join(', ')}`,\n );\n }\n\n // Type checks\n if (typeof parsed.version !== 'number') errors.push('\"version\" must be a number');\n if (typeof parsed.name !== 'string') errors.push('\"name\" must be a string');\n if (\n parsed.enforcement !== undefined &&\n parsed.enforcement !== 'warn' &&\n parsed.enforcement !== 'enforce'\n ) {\n errors.push('\"enforcement\" must be \"warn\" or \"enforce\"');\n }\n\n // Stack validation\n if (typeof parsed.stack !== 'object' || parsed.stack === null) {\n errors.push('\"stack\" must be an object');\n } else {\n const stack = parsed.stack as Record<string, unknown>;\n if (typeof stack.language !== 'string') errors.push('\"stack.language\" must be a string');\n if (typeof stack.packageManager !== 'string')\n errors.push('\"stack.packageManager\" must be a string');\n }\n\n // Rules validation\n if (typeof parsed.rules !== 'object' || parsed.rules === null) {\n errors.push('\"rules\" must be an object');\n } else {\n const rules = parsed.rules as Record<string, unknown>;\n if (typeof rules.maxFileLines !== 'number')\n errors.push('\"rules.maxFileLines\" must be a number');\n if (typeof rules.maxFunctionLines !== 'number')\n errors.push('\"rules.maxFunctionLines\" must be a number');\n if (typeof rules.requireTests !== 'boolean')\n errors.push('\"rules.requireTests\" must be a boolean');\n if (typeof rules.enforceNaming !== 'boolean')\n errors.push('\"rules.enforceNaming\" must be a boolean');\n if (typeof rules.enforceBoundaries !== 'boolean')\n errors.push('\"rules.enforceBoundaries\" must be a boolean');\n }\n\n // Ignore validation\n if (parsed.ignore !== undefined && !Array.isArray(parsed.ignore)) {\n errors.push('\"ignore\" must be an array');\n }\n\n if (errors.length > 0) {\n throw new Error(`Invalid viberails config at ${configPath}: ${errors.join('; ')}`);\n }\n}\n\n/**\n * Load and parse a viberails config file.\n *\n * Reads the JSON file at the given path, validates that it contains\n * the required fields (version, name, stack, rules), and returns\n * the parsed config.\n *\n * @param configPath - Absolute or relative path to viberails.config.json\n * @returns The parsed ViberailsConfig\n * @throws If the file doesn't exist, contains invalid JSON, or is missing required fields\n */\nexport async function loadConfig(configPath: string): Promise<ViberailsConfig> {\n let raw: string;\n try {\n raw = await fs.readFile(configPath, 'utf-8');\n } catch (err) {\n const code = (err as NodeJS.ErrnoException).code;\n if (code === 'ENOENT') {\n throw new Error(`Config file not found: ${configPath}. Run \"npx viberails\" to generate one.`);\n }\n throw new Error(`Failed to read config file at ${configPath}: ${(err as Error).message}`);\n }\n\n let parsed: Record<string, unknown>;\n try {\n parsed = JSON.parse(raw) as Record<string, unknown>;\n } catch {\n throw new Error(`Invalid JSON in config file at ${configPath}. Check for syntax errors.`);\n }\n\n validateConfig(parsed, configPath);\n\n // Safe to cast: validateConfig has verified all required fields and types\n return parsed as unknown as ViberailsConfig;\n}\n\n/**\n * Safely load a viberails config file, returning null on any error.\n *\n * Used by the CLI for \"does config already exist?\" checks where\n * failure is an expected case, not an error.\n *\n * @param configPath - Absolute or relative path to viberails.config.json\n * @returns The parsed ViberailsConfig, or null if loading fails\n */\nexport async function loadConfigSafe(configPath: string): Promise<ViberailsConfig | null> {\n try {\n return await loadConfig(configPath);\n } catch {\n return null;\n }\n}\n","import type {\n ConfigConventions,\n ConfigStack,\n ConfigStructure,\n ConventionValue,\n PackageConfigOverrides,\n ScanResult,\n ViberailsConfig,\n} from '@viberails/types';\nimport { CONVENTION_KEYS, generateConfig } from './generate-config.js';\n\n/**\n * Merge stack: keep existing values, fill in undefined fields from fresh scan.\n */\nfunction mergeStack(existing: ConfigStack, fresh: ConfigStack): ConfigStack {\n return {\n language: existing.language,\n packageManager: existing.packageManager,\n framework: existing.framework ?? fresh.framework,\n styling: existing.styling ?? fresh.styling,\n backend: existing.backend ?? fresh.backend,\n linter: existing.linter ?? fresh.linter,\n formatter: existing.formatter ?? fresh.formatter,\n testRunner: existing.testRunner ?? fresh.testRunner,\n };\n}\n\n/**\n * Merge structure: keep existing values, fill in undefined fields from fresh scan.\n */\nfunction mergeStructure(existing: ConfigStructure, fresh: ConfigStructure): ConfigStructure {\n return {\n srcDir: existing.srcDir ?? fresh.srcDir,\n pages: existing.pages ?? fresh.pages,\n components: existing.components ?? fresh.components,\n hooks: existing.hooks ?? fresh.hooks,\n utils: existing.utils ?? fresh.utils,\n types: existing.types ?? fresh.types,\n tests: existing.tests ?? fresh.tests,\n testPattern: existing.testPattern ?? fresh.testPattern,\n };\n}\n\n/**\n * Check if a convention key exists in the existing config\n * (either as a string or as an object with a value).\n */\nfunction hasConvention(conventions: ConfigConventions, key: keyof ConfigConventions): boolean {\n return conventions[key] !== undefined;\n}\n\n/**\n * Mark a ConventionValue as newly detected by adding `_detected: true`.\n * Only applies to object-form values (not plain strings).\n */\nfunction markAsDetected(value: ConventionValue): ConventionValue {\n if (typeof value === 'string') {\n return { value, _confidence: 'high', _consistency: 100, _detected: true };\n }\n return { ...value, _detected: true };\n}\n\n/**\n * Merge conventions: keep all existing values, add new detections with `_detected: true`.\n */\nfunction mergeConventions(\n existing: ConfigConventions,\n fresh: ConfigConventions,\n): ConfigConventions {\n const merged: ConfigConventions = { ...existing };\n\n for (const key of CONVENTION_KEYS) {\n if (!hasConvention(existing, key) && fresh[key] !== undefined) {\n merged[key] = markAsDetected(fresh[key]!);\n }\n }\n\n return merged;\n}\n\n/**\n * Merge a new scan result into an existing config for `viberails sync`.\n *\n * Preserves all developer-confirmed values from the existing config.\n * Adds newly detected conventions with a `_detected: true` annotation\n * so the developer can review them. Never removes rules or values\n * the developer has set.\n *\n * @param existing - The current ViberailsConfig (from viberails.config.json)\n * @param scanResult - Fresh scan results from re-scanning the project\n * @returns A merged config that preserves existing values and adds new detections\n */\nexport function mergeConfig(existing: ViberailsConfig, scanResult: ScanResult): ViberailsConfig {\n const fresh = generateConfig(scanResult);\n\n const merged: ViberailsConfig = {\n $schema: existing.$schema ?? fresh.$schema,\n version: existing.version,\n name: existing.name,\n enforcement: existing.enforcement,\n stack: mergeStack(existing.stack, fresh.stack),\n structure: mergeStructure(existing.structure, fresh.structure),\n conventions: mergeConventions(existing.conventions, fresh.conventions),\n rules: { ...existing.rules },\n ignore: [...existing.ignore],\n };\n\n // Workspace: always take fresh scan (structure can change)\n if (fresh.workspace) {\n merged.workspace = fresh.workspace;\n }\n\n // Boundaries: preserve existing rules (user may have adjusted)\n if (existing.boundaries) {\n merged.boundaries = [...existing.boundaries];\n } else if (fresh.boundaries) {\n merged.boundaries = [...fresh.boundaries];\n }\n\n // Packages: preserve existing overrides, add new ones\n if (existing.packages || fresh.packages) {\n merged.packages = mergePackageOverrides(existing.packages, fresh.packages);\n }\n\n return merged;\n}\n\n/**\n * Merge per-package overrides: keep existing user-edited overrides,\n * add new packages from fresh scan.\n */\nfunction mergePackageOverrides(\n existing?: PackageConfigOverrides[],\n fresh?: PackageConfigOverrides[],\n): PackageConfigOverrides[] | undefined {\n if (!fresh || fresh.length === 0) return existing;\n if (!existing || existing.length === 0) return fresh;\n\n const existingByPath = new Map(existing.map((p) => [p.path, p]));\n const merged: PackageConfigOverrides[] = [...existing];\n\n for (const freshPkg of fresh) {\n if (!existingByPath.has(freshPkg.path)) {\n merged.push(freshPkg);\n }\n }\n\n return merged.length > 0 ? merged : undefined;\n}\n","/**\n * JSON Schema (draft-07) definition for viberails.config.json.\n *\n * This schema will eventually be hosted at https://viberails.sh/schema/v1.json.\n * For now it is exported as a TypeScript object that can be serialized to JSON.\n */\nexport const configSchema = {\n $schema: 'http://json-schema.org/draft-07/schema#',\n $id: 'https://viberails.sh/schema/v1.json',\n title: 'viberails configuration',\n description: 'Configuration file for viberails — guardrails for vibe coding.',\n type: 'object',\n required: ['version', 'name', 'stack', 'rules'],\n properties: {\n $schema: {\n type: 'string',\n description: 'JSON Schema URL for editor validation.',\n },\n version: {\n type: 'number',\n const: 1,\n description: 'Config format version. Always 1 for V1.0.',\n },\n name: {\n type: 'string',\n description: 'Project name, typically from package.json.',\n },\n enforcement: {\n type: 'string',\n enum: ['warn', 'enforce'],\n default: 'warn',\n description: 'Whether conventions are warned about or enforced as errors.',\n },\n stack: {\n type: 'object',\n required: ['language', 'packageManager'],\n properties: {\n framework: {\n type: 'string',\n description: 'Primary framework identifier (e.g. \"nextjs@15\", \"remix@2\").',\n },\n language: {\n type: 'string',\n description: 'Primary language (e.g. \"typescript\", \"javascript\").',\n },\n styling: {\n type: 'string',\n description: 'Styling solution (e.g. \"tailwindcss@4\", \"css-modules\").',\n },\n backend: {\n type: 'string',\n description: 'Backend framework (e.g. \"express@5\", \"fastify\").',\n },\n packageManager: {\n type: 'string',\n description: 'Package manager (e.g. \"pnpm\", \"npm\", \"yarn\").',\n },\n linter: {\n type: 'string',\n description: 'Linter (e.g. \"eslint@9\", \"biome\").',\n },\n formatter: {\n type: 'string',\n description: 'Formatter (e.g. \"prettier\", \"biome\").',\n },\n testRunner: {\n type: 'string',\n description: 'Test runner (e.g. \"vitest\", \"jest\").',\n },\n },\n additionalProperties: false,\n description: 'Detected or configured technology stack.',\n },\n structure: {\n type: 'object',\n properties: {\n srcDir: {\n type: 'string',\n description: 'Source directory (e.g. \"src\"), or omit for flat structure.',\n },\n pages: {\n type: 'string',\n description: 'Pages or routes directory (e.g. \"src/app\").',\n },\n components: {\n type: 'string',\n description: 'Components directory (e.g. \"src/components\").',\n },\n hooks: {\n type: 'string',\n description: 'Hooks directory (e.g. \"src/hooks\").',\n },\n utils: {\n type: 'string',\n description: 'Utilities directory (e.g. \"src/utils\", \"src/lib\").',\n },\n types: {\n type: 'string',\n description: 'Type definitions directory (e.g. \"src/types\").',\n },\n tests: {\n type: 'string',\n description: 'Tests directory (e.g. \"tests\", \"__tests__\").',\n },\n testPattern: {\n type: 'string',\n description: 'Test file naming pattern (e.g. \"*.test.ts\", \"*.spec.ts\").',\n },\n },\n additionalProperties: false,\n description: 'Detected or configured directory structure.',\n },\n conventions: {\n type: 'object',\n properties: {\n fileNaming: { $ref: '#/definitions/conventionValue' },\n componentNaming: { $ref: '#/definitions/conventionValue' },\n hookNaming: { $ref: '#/definitions/conventionValue' },\n importAlias: { $ref: '#/definitions/conventionValue' },\n },\n additionalProperties: false,\n description: 'Detected or configured coding conventions.',\n },\n rules: {\n type: 'object',\n required: [\n 'maxFileLines',\n 'maxFunctionLines',\n 'requireTests',\n 'enforceNaming',\n 'enforceBoundaries',\n ],\n properties: {\n maxFileLines: {\n type: 'number',\n default: 300,\n description: 'Maximum number of lines allowed per file.',\n },\n maxFunctionLines: {\n type: 'number',\n default: 50,\n description: 'Maximum number of lines allowed per function.',\n },\n requireTests: {\n type: 'boolean',\n default: true,\n description: 'Whether to require test files for source modules.',\n },\n enforceNaming: {\n type: 'boolean',\n default: true,\n description: 'Whether to enforce detected file naming conventions.',\n },\n enforceBoundaries: {\n type: 'boolean',\n default: false,\n description: 'Whether to enforce module boundary rules.',\n },\n },\n additionalProperties: false,\n description: 'Rule thresholds and toggles for enforcement.',\n },\n ignore: {\n type: 'array',\n items: { type: 'string' },\n description: 'Glob patterns for files and directories to ignore.',\n },\n boundaries: {\n type: 'array',\n items: {\n type: 'object',\n required: ['from', 'to', 'allow'],\n properties: {\n from: {\n type: 'string',\n description: 'Source package or directory pattern.',\n },\n to: {\n type: 'string',\n description: 'Target package or directory pattern.',\n },\n allow: {\n type: 'boolean',\n description: 'Whether this import direction is allowed or disallowed.',\n },\n reason: {\n type: 'string',\n description: 'Human-readable explanation of why this boundary exists.',\n },\n },\n additionalProperties: false,\n },\n description: 'Module boundary rules for import enforcement.',\n },\n workspace: {\n type: 'object',\n required: ['packages', 'isMonorepo'],\n properties: {\n packages: {\n type: 'array',\n items: { type: 'string' },\n description: 'Relative paths to workspace packages.',\n },\n isMonorepo: {\n type: 'boolean',\n description: 'Whether this project is a monorepo with multiple packages.',\n },\n },\n additionalProperties: false,\n description: 'Workspace configuration for monorepo projects.',\n },\n packages: {\n type: 'array',\n items: {\n type: 'object',\n required: ['name', 'path'],\n properties: {\n name: { type: 'string', description: 'Package name from package.json.' },\n path: { type: 'string', description: 'Relative path to the package.' },\n stack: {\n type: 'object',\n properties: {\n framework: { type: 'string' },\n language: { type: 'string' },\n styling: { type: 'string' },\n backend: { type: 'string' },\n packageManager: { type: 'string' },\n linter: { type: 'string' },\n formatter: { type: 'string' },\n testRunner: { type: 'string' },\n },\n additionalProperties: false,\n },\n conventions: { $ref: '#/properties/conventions' },\n rules: {\n type: 'object',\n properties: {\n maxFileLines: { type: 'number' },\n maxFunctionLines: { type: 'number' },\n requireTests: { type: 'boolean' },\n enforceNaming: { type: 'boolean' },\n enforceBoundaries: { type: 'boolean' },\n },\n additionalProperties: false,\n },\n ignore: { type: 'array', items: { type: 'string' } },\n },\n additionalProperties: false,\n },\n description: 'Per-package overrides for monorepo projects.',\n },\n },\n additionalProperties: false,\n definitions: {\n conventionValue: {\n description:\n 'A convention value — either a plain string (user-confirmed) or an object with scanner metadata.',\n oneOf: [\n { type: 'string' },\n {\n type: 'object',\n required: ['value', '_confidence', '_consistency'],\n properties: {\n value: {\n type: 'string',\n description: 'The convention value.',\n },\n _confidence: {\n type: 'string',\n enum: ['high', 'medium', 'low'],\n description: 'Scanner confidence level.',\n },\n _consistency: {\n type: 'number',\n minimum: 0,\n maximum: 100,\n description: 'Scanner consistency percentage.',\n },\n _detected: {\n type: 'boolean',\n description: 'Set by mergeConfig when a convention is newly detected during sync.',\n },\n },\n additionalProperties: false,\n },\n ],\n },\n },\n} as const;\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/index.ts","../src/defaults.ts","../src/generate-config.ts","../src/generate-overrides.ts","../src/load-config.ts","../src/merge-config.ts","../src/schema.ts"],"sourcesContent":["declare const __PACKAGE_VERSION__: string;\nexport const VERSION: string = __PACKAGE_VERSION__;\n\nexport { DEFAULT_IGNORE, DEFAULT_RULES } from './defaults.js';\nexport { generateConfig } from './generate-config.js';\nexport { loadConfig, loadConfigSafe } from './load-config.js';\nexport { mergeConfig } from './merge-config.js';\nexport { configSchema } from './schema.js';\n","import type { ConfigRules } from '@viberails/types';\n\n/**\n * Default rule thresholds and toggles for a new viberails config.\n * These values are intentionally conservative to build trust on first run.\n */\nexport const DEFAULT_RULES: ConfigRules = {\n maxFileLines: 300,\n maxFunctionLines: 50,\n requireTests: true,\n enforceNaming: true,\n enforceBoundaries: false,\n};\n\n/**\n * Default glob patterns for files and directories to ignore.\n */\nexport const DEFAULT_IGNORE: string[] = [\n '**/*.d.ts',\n 'dist/**',\n 'node_modules/**',\n 'build/**',\n '.next/**',\n '.expo/**',\n '.output/**',\n '.svelte-kit/**',\n '.turbo/**',\n 'coverage/**',\n 'public/**',\n '.viberails/**',\n];\n","import * as path from 'node:path';\nimport type {\n ConfigConventions,\n ConfigStack,\n ConfigStructure,\n ConventionValue,\n DetectedConvention,\n DirectoryRole,\n ScanResult,\n StackItem,\n ViberailsConfig,\n} from '@viberails/types';\nimport { DEFAULT_IGNORE, DEFAULT_RULES } from './defaults.js';\nimport { generatePackageOverrides } from './generate-overrides.js';\n\n/**\n * Format a StackItem as a config string: `\"name@version\"` or `\"name\"`.\n */\nexport function formatStackItem(item: StackItem): string {\n return item.version ? `${item.name}@${item.version}` : item.name;\n}\n\n/**\n * Map DetectedStack → ConfigStack by formatting each StackItem.\n */\nfunction mapStack(scanResult: ScanResult): ConfigStack {\n const { stack } = scanResult;\n const config: ConfigStack = {\n language: formatStackItem(stack.language),\n packageManager: formatStackItem(stack.packageManager),\n };\n\n if (stack.framework) config.framework = formatStackItem(stack.framework);\n if (stack.styling) config.styling = formatStackItem(stack.styling);\n if (stack.backend) config.backend = formatStackItem(stack.backend);\n if (stack.linter) config.linter = formatStackItem(stack.linter);\n if (stack.formatter) config.formatter = formatStackItem(stack.formatter);\n if (stack.testRunner) config.testRunner = formatStackItem(stack.testRunner);\n\n return config;\n}\n\n/** Directory roles that map to ConfigStructure fields. */\nconst ROLE_TO_FIELD: Partial<Record<DirectoryRole, keyof ConfigStructure>> = {\n pages: 'pages',\n components: 'components',\n hooks: 'hooks',\n utils: 'utils',\n types: 'types',\n tests: 'tests',\n};\n\n/**\n * Map DetectedStructure → ConfigStructure by finding the first directory\n * for each known role.\n */\nfunction mapStructure(scanResult: ScanResult): ConfigStructure {\n const { structure } = scanResult;\n const config: ConfigStructure = {};\n\n if (structure.srcDir) {\n config.srcDir = structure.srcDir;\n }\n\n for (const dir of structure.directories) {\n const field = ROLE_TO_FIELD[dir.role];\n if (field && config[field] === undefined) {\n (config as Record<string, string>)[field] = dir.path;\n }\n }\n\n if (structure.testPattern) {\n config.testPattern = structure.testPattern.value;\n }\n\n return config;\n}\n\n/**\n * Convert a DetectedConvention to a ConventionValue with metadata.\n * Returns undefined for low-confidence conventions (they are omitted).\n */\nexport function mapConvention(convention: DetectedConvention): ConventionValue | undefined {\n if (convention.confidence === 'low') {\n return undefined;\n }\n\n return {\n value: convention.value,\n _confidence: convention.confidence,\n _consistency: convention.consistency,\n };\n}\n\n/** Convention keys from ScanResult that map to ConfigConventions fields. */\nexport const CONVENTION_KEYS: (keyof ConfigConventions)[] = [\n 'fileNaming',\n 'componentNaming',\n 'hookNaming',\n 'importAlias',\n];\n\n/**\n * Map scanner conventions → ConfigConventions, omitting low-confidence entries.\n */\nfunction mapConventions(scanResult: ScanResult): ConfigConventions {\n const config: ConfigConventions = {};\n\n for (const key of CONVENTION_KEYS) {\n const detected = scanResult.conventions[key];\n if (detected) {\n const value = mapConvention(detected);\n if (value !== undefined) {\n config[key] = value;\n }\n }\n }\n\n return config;\n}\n\n/**\n * Generate a ViberailsConfig from scan results.\n *\n * Maps the scanner's DetectedStack, DetectedStructure, and conventions\n * into the config format with smart defaults. Low-confidence conventions\n * are omitted. The project name is derived from the root directory basename.\n *\n * @param scanResult - The output of scanning a project\n * @returns A complete ViberailsConfig ready to be written as JSON\n */\nexport function generateConfig(scanResult: ScanResult): ViberailsConfig {\n const config: ViberailsConfig = {\n $schema: 'https://viberails.sh/schema/v1.json',\n version: 1,\n name: path.basename(scanResult.root),\n enforcement: 'warn',\n stack: mapStack(scanResult),\n structure: mapStructure(scanResult),\n conventions: mapConventions(scanResult),\n rules: { ...DEFAULT_RULES },\n ignore: [...DEFAULT_IGNORE],\n };\n\n if (scanResult.workspace) {\n config.workspace = {\n packages: scanResult.workspace.packages.map((p) => p.relativePath),\n isMonorepo: true,\n };\n config.boundaries = [];\n }\n\n const packageOverrides = generatePackageOverrides(scanResult, config);\n if (packageOverrides) {\n config.packages = packageOverrides;\n }\n\n return config;\n}\n","import type {\n ConfigConventions,\n ConfigStack,\n DetectedConvention,\n PackageConfigOverrides,\n ScanResult,\n ViberailsConfig,\n} from '@viberails/types';\nimport { CONVENTION_KEYS, formatStackItem, mapConvention } from './generate-config.js';\n\n/**\n * Compare a package's conventions against the global config conventions.\n * Returns only the differing conventions, or undefined if all match.\n */\nexport function conventionsDiffer(\n pkgConventions: Record<string, DetectedConvention>,\n globalConventions: ConfigConventions,\n): Partial<ConfigConventions> | undefined {\n const overrides: Partial<ConfigConventions> = {};\n let hasDiff = false;\n\n for (const key of CONVENTION_KEYS) {\n const detected = pkgConventions[key];\n if (!detected) continue;\n\n const mapped = mapConvention(detected);\n if (mapped === undefined) continue;\n\n const globalValue = globalConventions[key];\n const globalStr = typeof globalValue === 'string' ? globalValue : globalValue?.value;\n if (detected.value !== globalStr) {\n overrides[key] = mapped;\n hasDiff = true;\n }\n }\n\n return hasDiff ? overrides : undefined;\n}\n\n/**\n * Generate per-package config overrides for packages whose stack or\n * conventions differ from the aggregate global config.\n */\nexport function generatePackageOverrides(\n scanResult: ScanResult,\n globalConfig: ViberailsConfig,\n): PackageConfigOverrides[] | undefined {\n if (!scanResult.packages || scanResult.packages.length <= 1) return undefined;\n\n const overrides: PackageConfigOverrides[] = [];\n\n for (const pkg of scanResult.packages) {\n const override: PackageConfigOverrides = {\n name: pkg.name,\n path: pkg.relativePath,\n };\n let hasDiff = false;\n\n // Compare stack fields — only include overrides when the package has a\n // value that differs from the global, not when the package simply lacks the field\n const stackOverride: Partial<ConfigStack> = {};\n let hasStackDiff = false;\n\n const optionalStackFields = [\n 'framework',\n 'styling',\n 'backend',\n 'linter',\n 'formatter',\n 'testRunner',\n ] as const;\n for (const field of optionalStackFields) {\n const pkgItem = pkg.stack[field];\n if (!pkgItem) continue;\n const pkgValue = formatStackItem(pkgItem);\n if (pkgValue !== globalConfig.stack[field]) {\n stackOverride[field] = pkgValue;\n hasStackDiff = true;\n }\n }\n\n if (hasStackDiff) {\n override.stack = stackOverride;\n hasDiff = true;\n }\n\n // Compare conventions\n const conventionOverrides = conventionsDiffer(pkg.conventions, globalConfig.conventions);\n if (conventionOverrides) {\n override.conventions = conventionOverrides;\n hasDiff = true;\n }\n\n if (hasDiff) {\n overrides.push(override);\n }\n }\n\n return overrides.length > 0 ? overrides : undefined;\n}\n","import * as fs from 'node:fs/promises';\nimport type { ViberailsConfig } from '@viberails/types';\n\n/**\n * Validate that a parsed object has the required ViberailsConfig fields\n * and that their types are correct.\n * Throws a descriptive error if validation fails.\n */\nfunction validateConfig(parsed: Record<string, unknown>, configPath: string): void {\n const errors: string[] = [];\n\n // Required top-level fields\n const required = ['version', 'name', 'stack', 'rules'] as const;\n const missing = required.filter((field) => parsed[field] === undefined);\n if (missing.length > 0) {\n throw new Error(\n `Invalid viberails config at ${configPath}: missing required field(s): ${missing.join(', ')}`,\n );\n }\n\n // Type checks\n if (typeof parsed.version !== 'number') errors.push('\"version\" must be a number');\n if (typeof parsed.name !== 'string') errors.push('\"name\" must be a string');\n if (\n parsed.enforcement !== undefined &&\n parsed.enforcement !== 'warn' &&\n parsed.enforcement !== 'enforce'\n ) {\n errors.push('\"enforcement\" must be \"warn\" or \"enforce\"');\n }\n\n // Stack validation\n if (typeof parsed.stack !== 'object' || parsed.stack === null) {\n errors.push('\"stack\" must be an object');\n } else {\n const stack = parsed.stack as Record<string, unknown>;\n if (typeof stack.language !== 'string') errors.push('\"stack.language\" must be a string');\n if (typeof stack.packageManager !== 'string')\n errors.push('\"stack.packageManager\" must be a string');\n }\n\n // Rules validation\n if (typeof parsed.rules !== 'object' || parsed.rules === null) {\n errors.push('\"rules\" must be an object');\n } else {\n const rules = parsed.rules as Record<string, unknown>;\n if (typeof rules.maxFileLines !== 'number')\n errors.push('\"rules.maxFileLines\" must be a number');\n if (typeof rules.maxFunctionLines !== 'number')\n errors.push('\"rules.maxFunctionLines\" must be a number');\n if (typeof rules.requireTests !== 'boolean')\n errors.push('\"rules.requireTests\" must be a boolean');\n if (typeof rules.enforceNaming !== 'boolean')\n errors.push('\"rules.enforceNaming\" must be a boolean');\n if (typeof rules.enforceBoundaries !== 'boolean')\n errors.push('\"rules.enforceBoundaries\" must be a boolean');\n }\n\n // Ignore validation\n if (parsed.ignore !== undefined && !Array.isArray(parsed.ignore)) {\n errors.push('\"ignore\" must be an array');\n }\n\n if (errors.length > 0) {\n throw new Error(`Invalid viberails config at ${configPath}: ${errors.join('; ')}`);\n }\n}\n\n/**\n * Load and parse a viberails config file.\n *\n * Reads the JSON file at the given path, validates that it contains\n * the required fields (version, name, stack, rules), and returns\n * the parsed config.\n *\n * @param configPath - Absolute or relative path to viberails.config.json\n * @returns The parsed ViberailsConfig\n * @throws If the file doesn't exist, contains invalid JSON, or is missing required fields\n */\nexport async function loadConfig(configPath: string): Promise<ViberailsConfig> {\n let raw: string;\n try {\n raw = await fs.readFile(configPath, 'utf-8');\n } catch (err) {\n const code = (err as NodeJS.ErrnoException).code;\n if (code === 'ENOENT') {\n throw new Error(`Config file not found: ${configPath}. Run \"npx viberails\" to generate one.`);\n }\n throw new Error(`Failed to read config file at ${configPath}: ${(err as Error).message}`);\n }\n\n let parsed: Record<string, unknown>;\n try {\n parsed = JSON.parse(raw) as Record<string, unknown>;\n } catch {\n throw new Error(`Invalid JSON in config file at ${configPath}. Check for syntax errors.`);\n }\n\n validateConfig(parsed, configPath);\n\n // Safe to cast: validateConfig has verified all required fields and types\n return parsed as unknown as ViberailsConfig;\n}\n\n/**\n * Safely load a viberails config file, returning null on any error.\n *\n * Used by the CLI for \"does config already exist?\" checks where\n * failure is an expected case, not an error.\n *\n * @param configPath - Absolute or relative path to viberails.config.json\n * @returns The parsed ViberailsConfig, or null if loading fails\n */\nexport async function loadConfigSafe(configPath: string): Promise<ViberailsConfig | null> {\n try {\n return await loadConfig(configPath);\n } catch {\n return null;\n }\n}\n","import type {\n ConfigConventions,\n ConfigStack,\n ConfigStructure,\n ConventionValue,\n PackageConfigOverrides,\n ScanResult,\n ViberailsConfig,\n} from '@viberails/types';\nimport { CONVENTION_KEYS, generateConfig } from './generate-config.js';\n\n/**\n * Merge stack: keep existing values, fill in undefined fields from fresh scan.\n */\nfunction mergeStack(existing: ConfigStack, fresh: ConfigStack): ConfigStack {\n return {\n language: existing.language,\n packageManager: existing.packageManager,\n framework: existing.framework ?? fresh.framework,\n styling: existing.styling ?? fresh.styling,\n backend: existing.backend ?? fresh.backend,\n linter: existing.linter ?? fresh.linter,\n formatter: existing.formatter ?? fresh.formatter,\n testRunner: existing.testRunner ?? fresh.testRunner,\n };\n}\n\n/**\n * Merge structure: keep existing values, fill in undefined fields from fresh scan.\n */\nfunction mergeStructure(existing: ConfigStructure, fresh: ConfigStructure): ConfigStructure {\n return {\n srcDir: existing.srcDir ?? fresh.srcDir,\n pages: existing.pages ?? fresh.pages,\n components: existing.components ?? fresh.components,\n hooks: existing.hooks ?? fresh.hooks,\n utils: existing.utils ?? fresh.utils,\n types: existing.types ?? fresh.types,\n tests: existing.tests ?? fresh.tests,\n testPattern: existing.testPattern ?? fresh.testPattern,\n };\n}\n\n/**\n * Check if a convention key exists in the existing config\n * (either as a string or as an object with a value).\n */\nfunction hasConvention(conventions: ConfigConventions, key: keyof ConfigConventions): boolean {\n return conventions[key] !== undefined;\n}\n\n/**\n * Mark a ConventionValue as newly detected by adding `_detected: true`.\n * Only applies to object-form values (not plain strings).\n */\nfunction markAsDetected(value: ConventionValue): ConventionValue {\n if (typeof value === 'string') {\n return { value, _confidence: 'high', _consistency: 100, _detected: true };\n }\n return { ...value, _detected: true };\n}\n\n/**\n * Merge conventions: keep all existing values, add new detections with `_detected: true`.\n */\nfunction mergeConventions(\n existing: ConfigConventions,\n fresh: ConfigConventions,\n): ConfigConventions {\n const merged: ConfigConventions = { ...existing };\n\n for (const key of CONVENTION_KEYS) {\n if (!hasConvention(existing, key) && fresh[key] !== undefined) {\n merged[key] = markAsDetected(fresh[key]!);\n }\n }\n\n return merged;\n}\n\n/**\n * Merge a new scan result into an existing config for `viberails sync`.\n *\n * Preserves all developer-confirmed values from the existing config.\n * Adds newly detected conventions with a `_detected: true` annotation\n * so the developer can review them. Never removes rules or values\n * the developer has set.\n *\n * @param existing - The current ViberailsConfig (from viberails.config.json)\n * @param scanResult - Fresh scan results from re-scanning the project\n * @returns A merged config that preserves existing values and adds new detections\n */\nexport function mergeConfig(existing: ViberailsConfig, scanResult: ScanResult): ViberailsConfig {\n const fresh = generateConfig(scanResult);\n\n const merged: ViberailsConfig = {\n $schema: existing.$schema ?? fresh.$schema,\n version: existing.version,\n name: existing.name,\n enforcement: existing.enforcement,\n stack: mergeStack(existing.stack, fresh.stack),\n structure: mergeStructure(existing.structure, fresh.structure),\n conventions: mergeConventions(existing.conventions, fresh.conventions),\n rules: { ...existing.rules },\n ignore: [...existing.ignore],\n };\n\n // Workspace: always take fresh scan (structure can change)\n if (fresh.workspace) {\n merged.workspace = fresh.workspace;\n }\n\n // Boundaries: preserve existing rules (user may have adjusted)\n if (existing.boundaries) {\n merged.boundaries = [...existing.boundaries];\n } else if (fresh.boundaries) {\n merged.boundaries = [...fresh.boundaries];\n }\n\n // Packages: preserve existing overrides, add new ones\n if (existing.packages || fresh.packages) {\n merged.packages = mergePackageOverrides(existing.packages, fresh.packages);\n }\n\n return merged;\n}\n\n/**\n * Merge per-package overrides: keep existing user-edited overrides,\n * add new packages from fresh scan.\n */\nfunction mergePackageOverrides(\n existing?: PackageConfigOverrides[],\n fresh?: PackageConfigOverrides[],\n): PackageConfigOverrides[] | undefined {\n if (!fresh || fresh.length === 0) return existing;\n if (!existing || existing.length === 0) return fresh;\n\n const existingByPath = new Map(existing.map((p) => [p.path, p]));\n const merged: PackageConfigOverrides[] = [...existing];\n\n for (const freshPkg of fresh) {\n if (!existingByPath.has(freshPkg.path)) {\n merged.push(freshPkg);\n }\n }\n\n return merged.length > 0 ? merged : undefined;\n}\n","/**\n * JSON Schema (draft-07) definition for viberails.config.json.\n *\n * This schema will eventually be hosted at https://viberails.sh/schema/v1.json.\n * For now it is exported as a TypeScript object that can be serialized to JSON.\n */\nexport const configSchema = {\n $schema: 'http://json-schema.org/draft-07/schema#',\n $id: 'https://viberails.sh/schema/v1.json',\n title: 'viberails configuration',\n description: 'Configuration file for viberails — guardrails for vibe coding.',\n type: 'object',\n required: ['version', 'name', 'stack', 'rules'],\n properties: {\n $schema: {\n type: 'string',\n description: 'JSON Schema URL for editor validation.',\n },\n version: {\n type: 'number',\n const: 1,\n description: 'Config format version. Always 1 for V1.0.',\n },\n name: {\n type: 'string',\n description: 'Project name, typically from package.json.',\n },\n enforcement: {\n type: 'string',\n enum: ['warn', 'enforce'],\n default: 'warn',\n description: 'Whether conventions are warned about or enforced as errors.',\n },\n stack: {\n type: 'object',\n required: ['language', 'packageManager'],\n properties: {\n framework: {\n type: 'string',\n description: 'Primary framework identifier (e.g. \"nextjs@15\", \"remix@2\").',\n },\n language: {\n type: 'string',\n description: 'Primary language (e.g. \"typescript\", \"javascript\").',\n },\n styling: {\n type: 'string',\n description: 'Styling solution (e.g. \"tailwindcss@4\", \"css-modules\").',\n },\n backend: {\n type: 'string',\n description: 'Backend framework (e.g. \"express@5\", \"fastify\").',\n },\n packageManager: {\n type: 'string',\n description: 'Package manager (e.g. \"pnpm\", \"npm\", \"yarn\").',\n },\n linter: {\n type: 'string',\n description: 'Linter (e.g. \"eslint@9\", \"biome\").',\n },\n formatter: {\n type: 'string',\n description: 'Formatter (e.g. \"prettier\", \"biome\").',\n },\n testRunner: {\n type: 'string',\n description: 'Test runner (e.g. \"vitest\", \"jest\").',\n },\n },\n additionalProperties: false,\n description: 'Detected or configured technology stack.',\n },\n structure: {\n type: 'object',\n properties: {\n srcDir: {\n type: 'string',\n description: 'Source directory (e.g. \"src\"), or omit for flat structure.',\n },\n pages: {\n type: 'string',\n description: 'Pages or routes directory (e.g. \"src/app\").',\n },\n components: {\n type: 'string',\n description: 'Components directory (e.g. \"src/components\").',\n },\n hooks: {\n type: 'string',\n description: 'Hooks directory (e.g. \"src/hooks\").',\n },\n utils: {\n type: 'string',\n description: 'Utilities directory (e.g. \"src/utils\", \"src/lib\").',\n },\n types: {\n type: 'string',\n description: 'Type definitions directory (e.g. \"src/types\").',\n },\n tests: {\n type: 'string',\n description: 'Tests directory (e.g. \"tests\", \"__tests__\").',\n },\n testPattern: {\n type: 'string',\n description: 'Test file naming pattern (e.g. \"*.test.ts\", \"*.spec.ts\").',\n },\n },\n additionalProperties: false,\n description: 'Detected or configured directory structure.',\n },\n conventions: {\n type: 'object',\n properties: {\n fileNaming: { $ref: '#/definitions/conventionValue' },\n componentNaming: { $ref: '#/definitions/conventionValue' },\n hookNaming: { $ref: '#/definitions/conventionValue' },\n importAlias: { $ref: '#/definitions/conventionValue' },\n },\n additionalProperties: false,\n description: 'Detected or configured coding conventions.',\n },\n rules: {\n type: 'object',\n required: [\n 'maxFileLines',\n 'maxFunctionLines',\n 'requireTests',\n 'enforceNaming',\n 'enforceBoundaries',\n ],\n properties: {\n maxFileLines: {\n type: 'number',\n default: 300,\n description: 'Maximum number of lines allowed per file.',\n },\n maxFunctionLines: {\n type: 'number',\n default: 50,\n description: 'Maximum number of lines allowed per function.',\n },\n requireTests: {\n type: 'boolean',\n default: true,\n description: 'Whether to require test files for source modules.',\n },\n enforceNaming: {\n type: 'boolean',\n default: true,\n description: 'Whether to enforce detected file naming conventions.',\n },\n enforceBoundaries: {\n type: 'boolean',\n default: false,\n description: 'Whether to enforce module boundary rules.',\n },\n },\n additionalProperties: false,\n description: 'Rule thresholds and toggles for enforcement.',\n },\n ignore: {\n type: 'array',\n items: { type: 'string' },\n description: 'Glob patterns for files and directories to ignore.',\n },\n boundaries: {\n type: 'array',\n items: {\n type: 'object',\n required: ['from', 'to', 'allow'],\n properties: {\n from: {\n type: 'string',\n description: 'Source package or directory pattern.',\n },\n to: {\n type: 'string',\n description: 'Target package or directory pattern.',\n },\n allow: {\n type: 'boolean',\n description: 'Whether this import direction is allowed or disallowed.',\n },\n reason: {\n type: 'string',\n description: 'Human-readable explanation of why this boundary exists.',\n },\n },\n additionalProperties: false,\n },\n description: 'Module boundary rules for import enforcement.',\n },\n workspace: {\n type: 'object',\n required: ['packages', 'isMonorepo'],\n properties: {\n packages: {\n type: 'array',\n items: { type: 'string' },\n description: 'Relative paths to workspace packages.',\n },\n isMonorepo: {\n type: 'boolean',\n description: 'Whether this project is a monorepo with multiple packages.',\n },\n },\n additionalProperties: false,\n description: 'Workspace configuration for monorepo projects.',\n },\n packages: {\n type: 'array',\n items: {\n type: 'object',\n required: ['name', 'path'],\n properties: {\n name: { type: 'string', description: 'Package name from package.json.' },\n path: { type: 'string', description: 'Relative path to the package.' },\n stack: {\n type: 'object',\n properties: {\n framework: { type: 'string' },\n language: { type: 'string' },\n styling: { type: 'string' },\n backend: { type: 'string' },\n packageManager: { type: 'string' },\n linter: { type: 'string' },\n formatter: { type: 'string' },\n testRunner: { type: 'string' },\n },\n additionalProperties: false,\n },\n conventions: { $ref: '#/properties/conventions' },\n rules: {\n type: 'object',\n properties: {\n maxFileLines: { type: 'number' },\n maxFunctionLines: { type: 'number' },\n requireTests: { type: 'boolean' },\n enforceNaming: { type: 'boolean' },\n enforceBoundaries: { type: 'boolean' },\n },\n additionalProperties: false,\n },\n ignore: { type: 'array', items: { type: 'string' } },\n },\n additionalProperties: false,\n },\n description: 'Per-package overrides for monorepo projects.',\n },\n },\n additionalProperties: false,\n definitions: {\n conventionValue: {\n description:\n 'A convention value — either a plain string (user-confirmed) or an object with scanner metadata.',\n oneOf: [\n { type: 'string' },\n {\n type: 'object',\n required: ['value', '_confidence', '_consistency'],\n properties: {\n value: {\n type: 'string',\n description: 'The convention value.',\n },\n _confidence: {\n type: 'string',\n enum: ['high', 'medium', 'low'],\n description: 'Scanner confidence level.',\n },\n _consistency: {\n type: 'number',\n minimum: 0,\n maximum: 100,\n description: 'Scanner consistency percentage.',\n },\n _detected: {\n type: 'boolean',\n description: 'Set by mergeConfig when a convention is newly detected during sync.',\n },\n },\n additionalProperties: false,\n },\n ],\n },\n },\n} as const;\n"],"mappings":"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","names":[]}
package/dist/index.d.cts CHANGED
@@ -392,6 +392,6 @@ declare const configSchema: {
392
392
  };
393
393
  };
394
394
 
395
- declare const VERSION = "0.1.0";
395
+ declare const VERSION: string;
396
396
 
397
397
  export { DEFAULT_IGNORE, DEFAULT_RULES, VERSION, configSchema, generateConfig, loadConfig, loadConfigSafe, mergeConfig };
package/dist/index.d.ts CHANGED
@@ -392,6 +392,6 @@ declare const configSchema: {
392
392
  };
393
393
  };
394
394
 
395
- declare const VERSION = "0.1.0";
395
+ declare const VERSION: string;
396
396
 
397
397
  export { DEFAULT_IGNORE, DEFAULT_RULES, VERSION, configSchema, generateConfig, loadConfig, loadConfigSafe, mergeConfig };
package/dist/index.js CHANGED
@@ -6,7 +6,20 @@ var DEFAULT_RULES = {
6
6
  enforceNaming: true,
7
7
  enforceBoundaries: false
8
8
  };
9
- var DEFAULT_IGNORE = ["**/*.d.ts", "dist/**", "node_modules/**"];
9
+ var DEFAULT_IGNORE = [
10
+ "**/*.d.ts",
11
+ "dist/**",
12
+ "node_modules/**",
13
+ "build/**",
14
+ ".next/**",
15
+ ".expo/**",
16
+ ".output/**",
17
+ ".svelte-kit/**",
18
+ ".turbo/**",
19
+ "coverage/**",
20
+ "public/**",
21
+ ".viberails/**"
22
+ ];
10
23
 
11
24
  // src/generate-config.ts
12
25
  import * as path from "path";
@@ -612,7 +625,7 @@ var configSchema = {
612
625
  };
613
626
 
614
627
  // src/index.ts
615
- var VERSION = "0.1.0";
628
+ var VERSION = "0.2.2";
616
629
  export {
617
630
  DEFAULT_IGNORE,
618
631
  DEFAULT_RULES,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/defaults.ts","../src/generate-config.ts","../src/generate-overrides.ts","../src/load-config.ts","../src/merge-config.ts","../src/schema.ts","../src/index.ts"],"sourcesContent":["import type { ConfigRules } from '@viberails/types';\n\n/**\n * Default rule thresholds and toggles for a new viberails config.\n * These values are intentionally conservative to build trust on first run.\n */\nexport const DEFAULT_RULES: ConfigRules = {\n maxFileLines: 300,\n maxFunctionLines: 50,\n requireTests: true,\n enforceNaming: true,\n enforceBoundaries: false,\n};\n\n/**\n * Default glob patterns for files and directories to ignore.\n */\nexport const DEFAULT_IGNORE: string[] = ['**/*.d.ts', 'dist/**', 'node_modules/**'];\n","import * as path from 'node:path';\nimport type {\n ConfigConventions,\n ConfigStack,\n ConfigStructure,\n ConventionValue,\n DetectedConvention,\n DirectoryRole,\n ScanResult,\n StackItem,\n ViberailsConfig,\n} from '@viberails/types';\nimport { DEFAULT_IGNORE, DEFAULT_RULES } from './defaults.js';\nimport { generatePackageOverrides } from './generate-overrides.js';\n\n/**\n * Format a StackItem as a config string: `\"name@version\"` or `\"name\"`.\n */\nexport function formatStackItem(item: StackItem): string {\n return item.version ? `${item.name}@${item.version}` : item.name;\n}\n\n/**\n * Map DetectedStack → ConfigStack by formatting each StackItem.\n */\nfunction mapStack(scanResult: ScanResult): ConfigStack {\n const { stack } = scanResult;\n const config: ConfigStack = {\n language: formatStackItem(stack.language),\n packageManager: formatStackItem(stack.packageManager),\n };\n\n if (stack.framework) config.framework = formatStackItem(stack.framework);\n if (stack.styling) config.styling = formatStackItem(stack.styling);\n if (stack.backend) config.backend = formatStackItem(stack.backend);\n if (stack.linter) config.linter = formatStackItem(stack.linter);\n if (stack.formatter) config.formatter = formatStackItem(stack.formatter);\n if (stack.testRunner) config.testRunner = formatStackItem(stack.testRunner);\n\n return config;\n}\n\n/** Directory roles that map to ConfigStructure fields. */\nconst ROLE_TO_FIELD: Partial<Record<DirectoryRole, keyof ConfigStructure>> = {\n pages: 'pages',\n components: 'components',\n hooks: 'hooks',\n utils: 'utils',\n types: 'types',\n tests: 'tests',\n};\n\n/**\n * Map DetectedStructure → ConfigStructure by finding the first directory\n * for each known role.\n */\nfunction mapStructure(scanResult: ScanResult): ConfigStructure {\n const { structure } = scanResult;\n const config: ConfigStructure = {};\n\n if (structure.srcDir) {\n config.srcDir = structure.srcDir;\n }\n\n for (const dir of structure.directories) {\n const field = ROLE_TO_FIELD[dir.role];\n if (field && config[field] === undefined) {\n (config as Record<string, string>)[field] = dir.path;\n }\n }\n\n if (structure.testPattern) {\n config.testPattern = structure.testPattern.value;\n }\n\n return config;\n}\n\n/**\n * Convert a DetectedConvention to a ConventionValue with metadata.\n * Returns undefined for low-confidence conventions (they are omitted).\n */\nexport function mapConvention(convention: DetectedConvention): ConventionValue | undefined {\n if (convention.confidence === 'low') {\n return undefined;\n }\n\n return {\n value: convention.value,\n _confidence: convention.confidence,\n _consistency: convention.consistency,\n };\n}\n\n/** Convention keys from ScanResult that map to ConfigConventions fields. */\nexport const CONVENTION_KEYS: (keyof ConfigConventions)[] = [\n 'fileNaming',\n 'componentNaming',\n 'hookNaming',\n 'importAlias',\n];\n\n/**\n * Map scanner conventions → ConfigConventions, omitting low-confidence entries.\n */\nfunction mapConventions(scanResult: ScanResult): ConfigConventions {\n const config: ConfigConventions = {};\n\n for (const key of CONVENTION_KEYS) {\n const detected = scanResult.conventions[key];\n if (detected) {\n const value = mapConvention(detected);\n if (value !== undefined) {\n config[key] = value;\n }\n }\n }\n\n return config;\n}\n\n/**\n * Generate a ViberailsConfig from scan results.\n *\n * Maps the scanner's DetectedStack, DetectedStructure, and conventions\n * into the config format with smart defaults. Low-confidence conventions\n * are omitted. The project name is derived from the root directory basename.\n *\n * @param scanResult - The output of scanning a project\n * @returns A complete ViberailsConfig ready to be written as JSON\n */\nexport function generateConfig(scanResult: ScanResult): ViberailsConfig {\n const config: ViberailsConfig = {\n $schema: 'https://viberails.sh/schema/v1.json',\n version: 1,\n name: path.basename(scanResult.root),\n enforcement: 'warn',\n stack: mapStack(scanResult),\n structure: mapStructure(scanResult),\n conventions: mapConventions(scanResult),\n rules: { ...DEFAULT_RULES },\n ignore: [...DEFAULT_IGNORE],\n };\n\n if (scanResult.workspace) {\n config.workspace = {\n packages: scanResult.workspace.packages.map((p) => p.relativePath),\n isMonorepo: true,\n };\n config.boundaries = [];\n }\n\n const packageOverrides = generatePackageOverrides(scanResult, config);\n if (packageOverrides) {\n config.packages = packageOverrides;\n }\n\n return config;\n}\n","import type {\n ConfigConventions,\n ConfigStack,\n DetectedConvention,\n PackageConfigOverrides,\n ScanResult,\n ViberailsConfig,\n} from '@viberails/types';\nimport { CONVENTION_KEYS, formatStackItem, mapConvention } from './generate-config.js';\n\n/**\n * Compare a package's conventions against the global config conventions.\n * Returns only the differing conventions, or undefined if all match.\n */\nexport function conventionsDiffer(\n pkgConventions: Record<string, DetectedConvention>,\n globalConventions: ConfigConventions,\n): Partial<ConfigConventions> | undefined {\n const overrides: Partial<ConfigConventions> = {};\n let hasDiff = false;\n\n for (const key of CONVENTION_KEYS) {\n const detected = pkgConventions[key];\n if (!detected) continue;\n\n const mapped = mapConvention(detected);\n if (mapped === undefined) continue;\n\n const globalValue = globalConventions[key];\n const globalStr = typeof globalValue === 'string' ? globalValue : globalValue?.value;\n if (detected.value !== globalStr) {\n overrides[key] = mapped;\n hasDiff = true;\n }\n }\n\n return hasDiff ? overrides : undefined;\n}\n\n/**\n * Generate per-package config overrides for packages whose stack or\n * conventions differ from the aggregate global config.\n */\nexport function generatePackageOverrides(\n scanResult: ScanResult,\n globalConfig: ViberailsConfig,\n): PackageConfigOverrides[] | undefined {\n if (!scanResult.packages || scanResult.packages.length <= 1) return undefined;\n\n const overrides: PackageConfigOverrides[] = [];\n\n for (const pkg of scanResult.packages) {\n const override: PackageConfigOverrides = {\n name: pkg.name,\n path: pkg.relativePath,\n };\n let hasDiff = false;\n\n // Compare stack fields — only include overrides when the package has a\n // value that differs from the global, not when the package simply lacks the field\n const stackOverride: Partial<ConfigStack> = {};\n let hasStackDiff = false;\n\n const optionalStackFields = [\n 'framework',\n 'styling',\n 'backend',\n 'linter',\n 'formatter',\n 'testRunner',\n ] as const;\n for (const field of optionalStackFields) {\n const pkgItem = pkg.stack[field];\n if (!pkgItem) continue;\n const pkgValue = formatStackItem(pkgItem);\n if (pkgValue !== globalConfig.stack[field]) {\n stackOverride[field] = pkgValue;\n hasStackDiff = true;\n }\n }\n\n if (hasStackDiff) {\n override.stack = stackOverride;\n hasDiff = true;\n }\n\n // Compare conventions\n const conventionOverrides = conventionsDiffer(pkg.conventions, globalConfig.conventions);\n if (conventionOverrides) {\n override.conventions = conventionOverrides;\n hasDiff = true;\n }\n\n if (hasDiff) {\n overrides.push(override);\n }\n }\n\n return overrides.length > 0 ? overrides : undefined;\n}\n","import * as fs from 'node:fs/promises';\nimport type { ViberailsConfig } from '@viberails/types';\n\n/**\n * Validate that a parsed object has the required ViberailsConfig fields\n * and that their types are correct.\n * Throws a descriptive error if validation fails.\n */\nfunction validateConfig(parsed: Record<string, unknown>, configPath: string): void {\n const errors: string[] = [];\n\n // Required top-level fields\n const required = ['version', 'name', 'stack', 'rules'] as const;\n const missing = required.filter((field) => parsed[field] === undefined);\n if (missing.length > 0) {\n throw new Error(\n `Invalid viberails config at ${configPath}: missing required field(s): ${missing.join(', ')}`,\n );\n }\n\n // Type checks\n if (typeof parsed.version !== 'number') errors.push('\"version\" must be a number');\n if (typeof parsed.name !== 'string') errors.push('\"name\" must be a string');\n if (\n parsed.enforcement !== undefined &&\n parsed.enforcement !== 'warn' &&\n parsed.enforcement !== 'enforce'\n ) {\n errors.push('\"enforcement\" must be \"warn\" or \"enforce\"');\n }\n\n // Stack validation\n if (typeof parsed.stack !== 'object' || parsed.stack === null) {\n errors.push('\"stack\" must be an object');\n } else {\n const stack = parsed.stack as Record<string, unknown>;\n if (typeof stack.language !== 'string') errors.push('\"stack.language\" must be a string');\n if (typeof stack.packageManager !== 'string')\n errors.push('\"stack.packageManager\" must be a string');\n }\n\n // Rules validation\n if (typeof parsed.rules !== 'object' || parsed.rules === null) {\n errors.push('\"rules\" must be an object');\n } else {\n const rules = parsed.rules as Record<string, unknown>;\n if (typeof rules.maxFileLines !== 'number')\n errors.push('\"rules.maxFileLines\" must be a number');\n if (typeof rules.maxFunctionLines !== 'number')\n errors.push('\"rules.maxFunctionLines\" must be a number');\n if (typeof rules.requireTests !== 'boolean')\n errors.push('\"rules.requireTests\" must be a boolean');\n if (typeof rules.enforceNaming !== 'boolean')\n errors.push('\"rules.enforceNaming\" must be a boolean');\n if (typeof rules.enforceBoundaries !== 'boolean')\n errors.push('\"rules.enforceBoundaries\" must be a boolean');\n }\n\n // Ignore validation\n if (parsed.ignore !== undefined && !Array.isArray(parsed.ignore)) {\n errors.push('\"ignore\" must be an array');\n }\n\n if (errors.length > 0) {\n throw new Error(`Invalid viberails config at ${configPath}: ${errors.join('; ')}`);\n }\n}\n\n/**\n * Load and parse a viberails config file.\n *\n * Reads the JSON file at the given path, validates that it contains\n * the required fields (version, name, stack, rules), and returns\n * the parsed config.\n *\n * @param configPath - Absolute or relative path to viberails.config.json\n * @returns The parsed ViberailsConfig\n * @throws If the file doesn't exist, contains invalid JSON, or is missing required fields\n */\nexport async function loadConfig(configPath: string): Promise<ViberailsConfig> {\n let raw: string;\n try {\n raw = await fs.readFile(configPath, 'utf-8');\n } catch (err) {\n const code = (err as NodeJS.ErrnoException).code;\n if (code === 'ENOENT') {\n throw new Error(`Config file not found: ${configPath}. Run \"npx viberails\" to generate one.`);\n }\n throw new Error(`Failed to read config file at ${configPath}: ${(err as Error).message}`);\n }\n\n let parsed: Record<string, unknown>;\n try {\n parsed = JSON.parse(raw) as Record<string, unknown>;\n } catch {\n throw new Error(`Invalid JSON in config file at ${configPath}. Check for syntax errors.`);\n }\n\n validateConfig(parsed, configPath);\n\n // Safe to cast: validateConfig has verified all required fields and types\n return parsed as unknown as ViberailsConfig;\n}\n\n/**\n * Safely load a viberails config file, returning null on any error.\n *\n * Used by the CLI for \"does config already exist?\" checks where\n * failure is an expected case, not an error.\n *\n * @param configPath - Absolute or relative path to viberails.config.json\n * @returns The parsed ViberailsConfig, or null if loading fails\n */\nexport async function loadConfigSafe(configPath: string): Promise<ViberailsConfig | null> {\n try {\n return await loadConfig(configPath);\n } catch {\n return null;\n }\n}\n","import type {\n ConfigConventions,\n ConfigStack,\n ConfigStructure,\n ConventionValue,\n PackageConfigOverrides,\n ScanResult,\n ViberailsConfig,\n} from '@viberails/types';\nimport { CONVENTION_KEYS, generateConfig } from './generate-config.js';\n\n/**\n * Merge stack: keep existing values, fill in undefined fields from fresh scan.\n */\nfunction mergeStack(existing: ConfigStack, fresh: ConfigStack): ConfigStack {\n return {\n language: existing.language,\n packageManager: existing.packageManager,\n framework: existing.framework ?? fresh.framework,\n styling: existing.styling ?? fresh.styling,\n backend: existing.backend ?? fresh.backend,\n linter: existing.linter ?? fresh.linter,\n formatter: existing.formatter ?? fresh.formatter,\n testRunner: existing.testRunner ?? fresh.testRunner,\n };\n}\n\n/**\n * Merge structure: keep existing values, fill in undefined fields from fresh scan.\n */\nfunction mergeStructure(existing: ConfigStructure, fresh: ConfigStructure): ConfigStructure {\n return {\n srcDir: existing.srcDir ?? fresh.srcDir,\n pages: existing.pages ?? fresh.pages,\n components: existing.components ?? fresh.components,\n hooks: existing.hooks ?? fresh.hooks,\n utils: existing.utils ?? fresh.utils,\n types: existing.types ?? fresh.types,\n tests: existing.tests ?? fresh.tests,\n testPattern: existing.testPattern ?? fresh.testPattern,\n };\n}\n\n/**\n * Check if a convention key exists in the existing config\n * (either as a string or as an object with a value).\n */\nfunction hasConvention(conventions: ConfigConventions, key: keyof ConfigConventions): boolean {\n return conventions[key] !== undefined;\n}\n\n/**\n * Mark a ConventionValue as newly detected by adding `_detected: true`.\n * Only applies to object-form values (not plain strings).\n */\nfunction markAsDetected(value: ConventionValue): ConventionValue {\n if (typeof value === 'string') {\n return { value, _confidence: 'high', _consistency: 100, _detected: true };\n }\n return { ...value, _detected: true };\n}\n\n/**\n * Merge conventions: keep all existing values, add new detections with `_detected: true`.\n */\nfunction mergeConventions(\n existing: ConfigConventions,\n fresh: ConfigConventions,\n): ConfigConventions {\n const merged: ConfigConventions = { ...existing };\n\n for (const key of CONVENTION_KEYS) {\n if (!hasConvention(existing, key) && fresh[key] !== undefined) {\n merged[key] = markAsDetected(fresh[key]!);\n }\n }\n\n return merged;\n}\n\n/**\n * Merge a new scan result into an existing config for `viberails sync`.\n *\n * Preserves all developer-confirmed values from the existing config.\n * Adds newly detected conventions with a `_detected: true` annotation\n * so the developer can review them. Never removes rules or values\n * the developer has set.\n *\n * @param existing - The current ViberailsConfig (from viberails.config.json)\n * @param scanResult - Fresh scan results from re-scanning the project\n * @returns A merged config that preserves existing values and adds new detections\n */\nexport function mergeConfig(existing: ViberailsConfig, scanResult: ScanResult): ViberailsConfig {\n const fresh = generateConfig(scanResult);\n\n const merged: ViberailsConfig = {\n $schema: existing.$schema ?? fresh.$schema,\n version: existing.version,\n name: existing.name,\n enforcement: existing.enforcement,\n stack: mergeStack(existing.stack, fresh.stack),\n structure: mergeStructure(existing.structure, fresh.structure),\n conventions: mergeConventions(existing.conventions, fresh.conventions),\n rules: { ...existing.rules },\n ignore: [...existing.ignore],\n };\n\n // Workspace: always take fresh scan (structure can change)\n if (fresh.workspace) {\n merged.workspace = fresh.workspace;\n }\n\n // Boundaries: preserve existing rules (user may have adjusted)\n if (existing.boundaries) {\n merged.boundaries = [...existing.boundaries];\n } else if (fresh.boundaries) {\n merged.boundaries = [...fresh.boundaries];\n }\n\n // Packages: preserve existing overrides, add new ones\n if (existing.packages || fresh.packages) {\n merged.packages = mergePackageOverrides(existing.packages, fresh.packages);\n }\n\n return merged;\n}\n\n/**\n * Merge per-package overrides: keep existing user-edited overrides,\n * add new packages from fresh scan.\n */\nfunction mergePackageOverrides(\n existing?: PackageConfigOverrides[],\n fresh?: PackageConfigOverrides[],\n): PackageConfigOverrides[] | undefined {\n if (!fresh || fresh.length === 0) return existing;\n if (!existing || existing.length === 0) return fresh;\n\n const existingByPath = new Map(existing.map((p) => [p.path, p]));\n const merged: PackageConfigOverrides[] = [...existing];\n\n for (const freshPkg of fresh) {\n if (!existingByPath.has(freshPkg.path)) {\n merged.push(freshPkg);\n }\n }\n\n return merged.length > 0 ? merged : undefined;\n}\n","/**\n * JSON Schema (draft-07) definition for viberails.config.json.\n *\n * This schema will eventually be hosted at https://viberails.sh/schema/v1.json.\n * For now it is exported as a TypeScript object that can be serialized to JSON.\n */\nexport const configSchema = {\n $schema: 'http://json-schema.org/draft-07/schema#',\n $id: 'https://viberails.sh/schema/v1.json',\n title: 'viberails configuration',\n description: 'Configuration file for viberails — guardrails for vibe coding.',\n type: 'object',\n required: ['version', 'name', 'stack', 'rules'],\n properties: {\n $schema: {\n type: 'string',\n description: 'JSON Schema URL for editor validation.',\n },\n version: {\n type: 'number',\n const: 1,\n description: 'Config format version. Always 1 for V1.0.',\n },\n name: {\n type: 'string',\n description: 'Project name, typically from package.json.',\n },\n enforcement: {\n type: 'string',\n enum: ['warn', 'enforce'],\n default: 'warn',\n description: 'Whether conventions are warned about or enforced as errors.',\n },\n stack: {\n type: 'object',\n required: ['language', 'packageManager'],\n properties: {\n framework: {\n type: 'string',\n description: 'Primary framework identifier (e.g. \"nextjs@15\", \"remix@2\").',\n },\n language: {\n type: 'string',\n description: 'Primary language (e.g. \"typescript\", \"javascript\").',\n },\n styling: {\n type: 'string',\n description: 'Styling solution (e.g. \"tailwindcss@4\", \"css-modules\").',\n },\n backend: {\n type: 'string',\n description: 'Backend framework (e.g. \"express@5\", \"fastify\").',\n },\n packageManager: {\n type: 'string',\n description: 'Package manager (e.g. \"pnpm\", \"npm\", \"yarn\").',\n },\n linter: {\n type: 'string',\n description: 'Linter (e.g. \"eslint@9\", \"biome\").',\n },\n formatter: {\n type: 'string',\n description: 'Formatter (e.g. \"prettier\", \"biome\").',\n },\n testRunner: {\n type: 'string',\n description: 'Test runner (e.g. \"vitest\", \"jest\").',\n },\n },\n additionalProperties: false,\n description: 'Detected or configured technology stack.',\n },\n structure: {\n type: 'object',\n properties: {\n srcDir: {\n type: 'string',\n description: 'Source directory (e.g. \"src\"), or omit for flat structure.',\n },\n pages: {\n type: 'string',\n description: 'Pages or routes directory (e.g. \"src/app\").',\n },\n components: {\n type: 'string',\n description: 'Components directory (e.g. \"src/components\").',\n },\n hooks: {\n type: 'string',\n description: 'Hooks directory (e.g. \"src/hooks\").',\n },\n utils: {\n type: 'string',\n description: 'Utilities directory (e.g. \"src/utils\", \"src/lib\").',\n },\n types: {\n type: 'string',\n description: 'Type definitions directory (e.g. \"src/types\").',\n },\n tests: {\n type: 'string',\n description: 'Tests directory (e.g. \"tests\", \"__tests__\").',\n },\n testPattern: {\n type: 'string',\n description: 'Test file naming pattern (e.g. \"*.test.ts\", \"*.spec.ts\").',\n },\n },\n additionalProperties: false,\n description: 'Detected or configured directory structure.',\n },\n conventions: {\n type: 'object',\n properties: {\n fileNaming: { $ref: '#/definitions/conventionValue' },\n componentNaming: { $ref: '#/definitions/conventionValue' },\n hookNaming: { $ref: '#/definitions/conventionValue' },\n importAlias: { $ref: '#/definitions/conventionValue' },\n },\n additionalProperties: false,\n description: 'Detected or configured coding conventions.',\n },\n rules: {\n type: 'object',\n required: [\n 'maxFileLines',\n 'maxFunctionLines',\n 'requireTests',\n 'enforceNaming',\n 'enforceBoundaries',\n ],\n properties: {\n maxFileLines: {\n type: 'number',\n default: 300,\n description: 'Maximum number of lines allowed per file.',\n },\n maxFunctionLines: {\n type: 'number',\n default: 50,\n description: 'Maximum number of lines allowed per function.',\n },\n requireTests: {\n type: 'boolean',\n default: true,\n description: 'Whether to require test files for source modules.',\n },\n enforceNaming: {\n type: 'boolean',\n default: true,\n description: 'Whether to enforce detected file naming conventions.',\n },\n enforceBoundaries: {\n type: 'boolean',\n default: false,\n description: 'Whether to enforce module boundary rules.',\n },\n },\n additionalProperties: false,\n description: 'Rule thresholds and toggles for enforcement.',\n },\n ignore: {\n type: 'array',\n items: { type: 'string' },\n description: 'Glob patterns for files and directories to ignore.',\n },\n boundaries: {\n type: 'array',\n items: {\n type: 'object',\n required: ['from', 'to', 'allow'],\n properties: {\n from: {\n type: 'string',\n description: 'Source package or directory pattern.',\n },\n to: {\n type: 'string',\n description: 'Target package or directory pattern.',\n },\n allow: {\n type: 'boolean',\n description: 'Whether this import direction is allowed or disallowed.',\n },\n reason: {\n type: 'string',\n description: 'Human-readable explanation of why this boundary exists.',\n },\n },\n additionalProperties: false,\n },\n description: 'Module boundary rules for import enforcement.',\n },\n workspace: {\n type: 'object',\n required: ['packages', 'isMonorepo'],\n properties: {\n packages: {\n type: 'array',\n items: { type: 'string' },\n description: 'Relative paths to workspace packages.',\n },\n isMonorepo: {\n type: 'boolean',\n description: 'Whether this project is a monorepo with multiple packages.',\n },\n },\n additionalProperties: false,\n description: 'Workspace configuration for monorepo projects.',\n },\n packages: {\n type: 'array',\n items: {\n type: 'object',\n required: ['name', 'path'],\n properties: {\n name: { type: 'string', description: 'Package name from package.json.' },\n path: { type: 'string', description: 'Relative path to the package.' },\n stack: {\n type: 'object',\n properties: {\n framework: { type: 'string' },\n language: { type: 'string' },\n styling: { type: 'string' },\n backend: { type: 'string' },\n packageManager: { type: 'string' },\n linter: { type: 'string' },\n formatter: { type: 'string' },\n testRunner: { type: 'string' },\n },\n additionalProperties: false,\n },\n conventions: { $ref: '#/properties/conventions' },\n rules: {\n type: 'object',\n properties: {\n maxFileLines: { type: 'number' },\n maxFunctionLines: { type: 'number' },\n requireTests: { type: 'boolean' },\n enforceNaming: { type: 'boolean' },\n enforceBoundaries: { type: 'boolean' },\n },\n additionalProperties: false,\n },\n ignore: { type: 'array', items: { type: 'string' } },\n },\n additionalProperties: false,\n },\n description: 'Per-package overrides for monorepo projects.',\n },\n },\n additionalProperties: false,\n definitions: {\n conventionValue: {\n description:\n 'A convention value — either a plain string (user-confirmed) or an object with scanner metadata.',\n oneOf: [\n { type: 'string' },\n {\n type: 'object',\n required: ['value', '_confidence', '_consistency'],\n properties: {\n value: {\n type: 'string',\n description: 'The convention value.',\n },\n _confidence: {\n type: 'string',\n enum: ['high', 'medium', 'low'],\n description: 'Scanner confidence level.',\n },\n _consistency: {\n type: 'number',\n minimum: 0,\n maximum: 100,\n description: 'Scanner consistency percentage.',\n },\n _detected: {\n type: 'boolean',\n description: 'Set by mergeConfig when a convention is newly detected during sync.',\n },\n },\n additionalProperties: false,\n },\n ],\n },\n },\n} as const;\n","export const VERSION = '0.1.0';\n\nexport { DEFAULT_IGNORE, DEFAULT_RULES } from './defaults.js';\nexport { generateConfig } from './generate-config.js';\nexport { loadConfig, loadConfigSafe } from './load-config.js';\nexport { mergeConfig } from './merge-config.js';\nexport { configSchema } from './schema.js';\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/defaults.ts","../src/generate-config.ts","../src/generate-overrides.ts","../src/load-config.ts","../src/merge-config.ts","../src/schema.ts","../src/index.ts"],"sourcesContent":["import type { ConfigRules } from '@viberails/types';\n\n/**\n * Default rule thresholds and toggles for a new viberails config.\n * These values are intentionally conservative to build trust on first run.\n */\nexport const DEFAULT_RULES: ConfigRules = {\n maxFileLines: 300,\n maxFunctionLines: 50,\n requireTests: true,\n enforceNaming: true,\n enforceBoundaries: false,\n};\n\n/**\n * Default glob patterns for files and directories to ignore.\n */\nexport const DEFAULT_IGNORE: string[] = [\n '**/*.d.ts',\n 'dist/**',\n 'node_modules/**',\n 'build/**',\n '.next/**',\n '.expo/**',\n '.output/**',\n '.svelte-kit/**',\n '.turbo/**',\n 'coverage/**',\n 'public/**',\n '.viberails/**',\n];\n","import * as path from 'node:path';\nimport type {\n ConfigConventions,\n ConfigStack,\n ConfigStructure,\n ConventionValue,\n DetectedConvention,\n DirectoryRole,\n ScanResult,\n StackItem,\n ViberailsConfig,\n} from '@viberails/types';\nimport { DEFAULT_IGNORE, DEFAULT_RULES } from './defaults.js';\nimport { generatePackageOverrides } from './generate-overrides.js';\n\n/**\n * Format a StackItem as a config string: `\"name@version\"` or `\"name\"`.\n */\nexport function formatStackItem(item: StackItem): string {\n return item.version ? `${item.name}@${item.version}` : item.name;\n}\n\n/**\n * Map DetectedStack → ConfigStack by formatting each StackItem.\n */\nfunction mapStack(scanResult: ScanResult): ConfigStack {\n const { stack } = scanResult;\n const config: ConfigStack = {\n language: formatStackItem(stack.language),\n packageManager: formatStackItem(stack.packageManager),\n };\n\n if (stack.framework) config.framework = formatStackItem(stack.framework);\n if (stack.styling) config.styling = formatStackItem(stack.styling);\n if (stack.backend) config.backend = formatStackItem(stack.backend);\n if (stack.linter) config.linter = formatStackItem(stack.linter);\n if (stack.formatter) config.formatter = formatStackItem(stack.formatter);\n if (stack.testRunner) config.testRunner = formatStackItem(stack.testRunner);\n\n return config;\n}\n\n/** Directory roles that map to ConfigStructure fields. */\nconst ROLE_TO_FIELD: Partial<Record<DirectoryRole, keyof ConfigStructure>> = {\n pages: 'pages',\n components: 'components',\n hooks: 'hooks',\n utils: 'utils',\n types: 'types',\n tests: 'tests',\n};\n\n/**\n * Map DetectedStructure → ConfigStructure by finding the first directory\n * for each known role.\n */\nfunction mapStructure(scanResult: ScanResult): ConfigStructure {\n const { structure } = scanResult;\n const config: ConfigStructure = {};\n\n if (structure.srcDir) {\n config.srcDir = structure.srcDir;\n }\n\n for (const dir of structure.directories) {\n const field = ROLE_TO_FIELD[dir.role];\n if (field && config[field] === undefined) {\n (config as Record<string, string>)[field] = dir.path;\n }\n }\n\n if (structure.testPattern) {\n config.testPattern = structure.testPattern.value;\n }\n\n return config;\n}\n\n/**\n * Convert a DetectedConvention to a ConventionValue with metadata.\n * Returns undefined for low-confidence conventions (they are omitted).\n */\nexport function mapConvention(convention: DetectedConvention): ConventionValue | undefined {\n if (convention.confidence === 'low') {\n return undefined;\n }\n\n return {\n value: convention.value,\n _confidence: convention.confidence,\n _consistency: convention.consistency,\n };\n}\n\n/** Convention keys from ScanResult that map to ConfigConventions fields. */\nexport const CONVENTION_KEYS: (keyof ConfigConventions)[] = [\n 'fileNaming',\n 'componentNaming',\n 'hookNaming',\n 'importAlias',\n];\n\n/**\n * Map scanner conventions → ConfigConventions, omitting low-confidence entries.\n */\nfunction mapConventions(scanResult: ScanResult): ConfigConventions {\n const config: ConfigConventions = {};\n\n for (const key of CONVENTION_KEYS) {\n const detected = scanResult.conventions[key];\n if (detected) {\n const value = mapConvention(detected);\n if (value !== undefined) {\n config[key] = value;\n }\n }\n }\n\n return config;\n}\n\n/**\n * Generate a ViberailsConfig from scan results.\n *\n * Maps the scanner's DetectedStack, DetectedStructure, and conventions\n * into the config format with smart defaults. Low-confidence conventions\n * are omitted. The project name is derived from the root directory basename.\n *\n * @param scanResult - The output of scanning a project\n * @returns A complete ViberailsConfig ready to be written as JSON\n */\nexport function generateConfig(scanResult: ScanResult): ViberailsConfig {\n const config: ViberailsConfig = {\n $schema: 'https://viberails.sh/schema/v1.json',\n version: 1,\n name: path.basename(scanResult.root),\n enforcement: 'warn',\n stack: mapStack(scanResult),\n structure: mapStructure(scanResult),\n conventions: mapConventions(scanResult),\n rules: { ...DEFAULT_RULES },\n ignore: [...DEFAULT_IGNORE],\n };\n\n if (scanResult.workspace) {\n config.workspace = {\n packages: scanResult.workspace.packages.map((p) => p.relativePath),\n isMonorepo: true,\n };\n config.boundaries = [];\n }\n\n const packageOverrides = generatePackageOverrides(scanResult, config);\n if (packageOverrides) {\n config.packages = packageOverrides;\n }\n\n return config;\n}\n","import type {\n ConfigConventions,\n ConfigStack,\n DetectedConvention,\n PackageConfigOverrides,\n ScanResult,\n ViberailsConfig,\n} from '@viberails/types';\nimport { CONVENTION_KEYS, formatStackItem, mapConvention } from './generate-config.js';\n\n/**\n * Compare a package's conventions against the global config conventions.\n * Returns only the differing conventions, or undefined if all match.\n */\nexport function conventionsDiffer(\n pkgConventions: Record<string, DetectedConvention>,\n globalConventions: ConfigConventions,\n): Partial<ConfigConventions> | undefined {\n const overrides: Partial<ConfigConventions> = {};\n let hasDiff = false;\n\n for (const key of CONVENTION_KEYS) {\n const detected = pkgConventions[key];\n if (!detected) continue;\n\n const mapped = mapConvention(detected);\n if (mapped === undefined) continue;\n\n const globalValue = globalConventions[key];\n const globalStr = typeof globalValue === 'string' ? globalValue : globalValue?.value;\n if (detected.value !== globalStr) {\n overrides[key] = mapped;\n hasDiff = true;\n }\n }\n\n return hasDiff ? overrides : undefined;\n}\n\n/**\n * Generate per-package config overrides for packages whose stack or\n * conventions differ from the aggregate global config.\n */\nexport function generatePackageOverrides(\n scanResult: ScanResult,\n globalConfig: ViberailsConfig,\n): PackageConfigOverrides[] | undefined {\n if (!scanResult.packages || scanResult.packages.length <= 1) return undefined;\n\n const overrides: PackageConfigOverrides[] = [];\n\n for (const pkg of scanResult.packages) {\n const override: PackageConfigOverrides = {\n name: pkg.name,\n path: pkg.relativePath,\n };\n let hasDiff = false;\n\n // Compare stack fields — only include overrides when the package has a\n // value that differs from the global, not when the package simply lacks the field\n const stackOverride: Partial<ConfigStack> = {};\n let hasStackDiff = false;\n\n const optionalStackFields = [\n 'framework',\n 'styling',\n 'backend',\n 'linter',\n 'formatter',\n 'testRunner',\n ] as const;\n for (const field of optionalStackFields) {\n const pkgItem = pkg.stack[field];\n if (!pkgItem) continue;\n const pkgValue = formatStackItem(pkgItem);\n if (pkgValue !== globalConfig.stack[field]) {\n stackOverride[field] = pkgValue;\n hasStackDiff = true;\n }\n }\n\n if (hasStackDiff) {\n override.stack = stackOverride;\n hasDiff = true;\n }\n\n // Compare conventions\n const conventionOverrides = conventionsDiffer(pkg.conventions, globalConfig.conventions);\n if (conventionOverrides) {\n override.conventions = conventionOverrides;\n hasDiff = true;\n }\n\n if (hasDiff) {\n overrides.push(override);\n }\n }\n\n return overrides.length > 0 ? overrides : undefined;\n}\n","import * as fs from 'node:fs/promises';\nimport type { ViberailsConfig } from '@viberails/types';\n\n/**\n * Validate that a parsed object has the required ViberailsConfig fields\n * and that their types are correct.\n * Throws a descriptive error if validation fails.\n */\nfunction validateConfig(parsed: Record<string, unknown>, configPath: string): void {\n const errors: string[] = [];\n\n // Required top-level fields\n const required = ['version', 'name', 'stack', 'rules'] as const;\n const missing = required.filter((field) => parsed[field] === undefined);\n if (missing.length > 0) {\n throw new Error(\n `Invalid viberails config at ${configPath}: missing required field(s): ${missing.join(', ')}`,\n );\n }\n\n // Type checks\n if (typeof parsed.version !== 'number') errors.push('\"version\" must be a number');\n if (typeof parsed.name !== 'string') errors.push('\"name\" must be a string');\n if (\n parsed.enforcement !== undefined &&\n parsed.enforcement !== 'warn' &&\n parsed.enforcement !== 'enforce'\n ) {\n errors.push('\"enforcement\" must be \"warn\" or \"enforce\"');\n }\n\n // Stack validation\n if (typeof parsed.stack !== 'object' || parsed.stack === null) {\n errors.push('\"stack\" must be an object');\n } else {\n const stack = parsed.stack as Record<string, unknown>;\n if (typeof stack.language !== 'string') errors.push('\"stack.language\" must be a string');\n if (typeof stack.packageManager !== 'string')\n errors.push('\"stack.packageManager\" must be a string');\n }\n\n // Rules validation\n if (typeof parsed.rules !== 'object' || parsed.rules === null) {\n errors.push('\"rules\" must be an object');\n } else {\n const rules = parsed.rules as Record<string, unknown>;\n if (typeof rules.maxFileLines !== 'number')\n errors.push('\"rules.maxFileLines\" must be a number');\n if (typeof rules.maxFunctionLines !== 'number')\n errors.push('\"rules.maxFunctionLines\" must be a number');\n if (typeof rules.requireTests !== 'boolean')\n errors.push('\"rules.requireTests\" must be a boolean');\n if (typeof rules.enforceNaming !== 'boolean')\n errors.push('\"rules.enforceNaming\" must be a boolean');\n if (typeof rules.enforceBoundaries !== 'boolean')\n errors.push('\"rules.enforceBoundaries\" must be a boolean');\n }\n\n // Ignore validation\n if (parsed.ignore !== undefined && !Array.isArray(parsed.ignore)) {\n errors.push('\"ignore\" must be an array');\n }\n\n if (errors.length > 0) {\n throw new Error(`Invalid viberails config at ${configPath}: ${errors.join('; ')}`);\n }\n}\n\n/**\n * Load and parse a viberails config file.\n *\n * Reads the JSON file at the given path, validates that it contains\n * the required fields (version, name, stack, rules), and returns\n * the parsed config.\n *\n * @param configPath - Absolute or relative path to viberails.config.json\n * @returns The parsed ViberailsConfig\n * @throws If the file doesn't exist, contains invalid JSON, or is missing required fields\n */\nexport async function loadConfig(configPath: string): Promise<ViberailsConfig> {\n let raw: string;\n try {\n raw = await fs.readFile(configPath, 'utf-8');\n } catch (err) {\n const code = (err as NodeJS.ErrnoException).code;\n if (code === 'ENOENT') {\n throw new Error(`Config file not found: ${configPath}. Run \"npx viberails\" to generate one.`);\n }\n throw new Error(`Failed to read config file at ${configPath}: ${(err as Error).message}`);\n }\n\n let parsed: Record<string, unknown>;\n try {\n parsed = JSON.parse(raw) as Record<string, unknown>;\n } catch {\n throw new Error(`Invalid JSON in config file at ${configPath}. Check for syntax errors.`);\n }\n\n validateConfig(parsed, configPath);\n\n // Safe to cast: validateConfig has verified all required fields and types\n return parsed as unknown as ViberailsConfig;\n}\n\n/**\n * Safely load a viberails config file, returning null on any error.\n *\n * Used by the CLI for \"does config already exist?\" checks where\n * failure is an expected case, not an error.\n *\n * @param configPath - Absolute or relative path to viberails.config.json\n * @returns The parsed ViberailsConfig, or null if loading fails\n */\nexport async function loadConfigSafe(configPath: string): Promise<ViberailsConfig | null> {\n try {\n return await loadConfig(configPath);\n } catch {\n return null;\n }\n}\n","import type {\n ConfigConventions,\n ConfigStack,\n ConfigStructure,\n ConventionValue,\n PackageConfigOverrides,\n ScanResult,\n ViberailsConfig,\n} from '@viberails/types';\nimport { CONVENTION_KEYS, generateConfig } from './generate-config.js';\n\n/**\n * Merge stack: keep existing values, fill in undefined fields from fresh scan.\n */\nfunction mergeStack(existing: ConfigStack, fresh: ConfigStack): ConfigStack {\n return {\n language: existing.language,\n packageManager: existing.packageManager,\n framework: existing.framework ?? fresh.framework,\n styling: existing.styling ?? fresh.styling,\n backend: existing.backend ?? fresh.backend,\n linter: existing.linter ?? fresh.linter,\n formatter: existing.formatter ?? fresh.formatter,\n testRunner: existing.testRunner ?? fresh.testRunner,\n };\n}\n\n/**\n * Merge structure: keep existing values, fill in undefined fields from fresh scan.\n */\nfunction mergeStructure(existing: ConfigStructure, fresh: ConfigStructure): ConfigStructure {\n return {\n srcDir: existing.srcDir ?? fresh.srcDir,\n pages: existing.pages ?? fresh.pages,\n components: existing.components ?? fresh.components,\n hooks: existing.hooks ?? fresh.hooks,\n utils: existing.utils ?? fresh.utils,\n types: existing.types ?? fresh.types,\n tests: existing.tests ?? fresh.tests,\n testPattern: existing.testPattern ?? fresh.testPattern,\n };\n}\n\n/**\n * Check if a convention key exists in the existing config\n * (either as a string or as an object with a value).\n */\nfunction hasConvention(conventions: ConfigConventions, key: keyof ConfigConventions): boolean {\n return conventions[key] !== undefined;\n}\n\n/**\n * Mark a ConventionValue as newly detected by adding `_detected: true`.\n * Only applies to object-form values (not plain strings).\n */\nfunction markAsDetected(value: ConventionValue): ConventionValue {\n if (typeof value === 'string') {\n return { value, _confidence: 'high', _consistency: 100, _detected: true };\n }\n return { ...value, _detected: true };\n}\n\n/**\n * Merge conventions: keep all existing values, add new detections with `_detected: true`.\n */\nfunction mergeConventions(\n existing: ConfigConventions,\n fresh: ConfigConventions,\n): ConfigConventions {\n const merged: ConfigConventions = { ...existing };\n\n for (const key of CONVENTION_KEYS) {\n if (!hasConvention(existing, key) && fresh[key] !== undefined) {\n merged[key] = markAsDetected(fresh[key]!);\n }\n }\n\n return merged;\n}\n\n/**\n * Merge a new scan result into an existing config for `viberails sync`.\n *\n * Preserves all developer-confirmed values from the existing config.\n * Adds newly detected conventions with a `_detected: true` annotation\n * so the developer can review them. Never removes rules or values\n * the developer has set.\n *\n * @param existing - The current ViberailsConfig (from viberails.config.json)\n * @param scanResult - Fresh scan results from re-scanning the project\n * @returns A merged config that preserves existing values and adds new detections\n */\nexport function mergeConfig(existing: ViberailsConfig, scanResult: ScanResult): ViberailsConfig {\n const fresh = generateConfig(scanResult);\n\n const merged: ViberailsConfig = {\n $schema: existing.$schema ?? fresh.$schema,\n version: existing.version,\n name: existing.name,\n enforcement: existing.enforcement,\n stack: mergeStack(existing.stack, fresh.stack),\n structure: mergeStructure(existing.structure, fresh.structure),\n conventions: mergeConventions(existing.conventions, fresh.conventions),\n rules: { ...existing.rules },\n ignore: [...existing.ignore],\n };\n\n // Workspace: always take fresh scan (structure can change)\n if (fresh.workspace) {\n merged.workspace = fresh.workspace;\n }\n\n // Boundaries: preserve existing rules (user may have adjusted)\n if (existing.boundaries) {\n merged.boundaries = [...existing.boundaries];\n } else if (fresh.boundaries) {\n merged.boundaries = [...fresh.boundaries];\n }\n\n // Packages: preserve existing overrides, add new ones\n if (existing.packages || fresh.packages) {\n merged.packages = mergePackageOverrides(existing.packages, fresh.packages);\n }\n\n return merged;\n}\n\n/**\n * Merge per-package overrides: keep existing user-edited overrides,\n * add new packages from fresh scan.\n */\nfunction mergePackageOverrides(\n existing?: PackageConfigOverrides[],\n fresh?: PackageConfigOverrides[],\n): PackageConfigOverrides[] | undefined {\n if (!fresh || fresh.length === 0) return existing;\n if (!existing || existing.length === 0) return fresh;\n\n const existingByPath = new Map(existing.map((p) => [p.path, p]));\n const merged: PackageConfigOverrides[] = [...existing];\n\n for (const freshPkg of fresh) {\n if (!existingByPath.has(freshPkg.path)) {\n merged.push(freshPkg);\n }\n }\n\n return merged.length > 0 ? merged : undefined;\n}\n","/**\n * JSON Schema (draft-07) definition for viberails.config.json.\n *\n * This schema will eventually be hosted at https://viberails.sh/schema/v1.json.\n * For now it is exported as a TypeScript object that can be serialized to JSON.\n */\nexport const configSchema = {\n $schema: 'http://json-schema.org/draft-07/schema#',\n $id: 'https://viberails.sh/schema/v1.json',\n title: 'viberails configuration',\n description: 'Configuration file for viberails — guardrails for vibe coding.',\n type: 'object',\n required: ['version', 'name', 'stack', 'rules'],\n properties: {\n $schema: {\n type: 'string',\n description: 'JSON Schema URL for editor validation.',\n },\n version: {\n type: 'number',\n const: 1,\n description: 'Config format version. Always 1 for V1.0.',\n },\n name: {\n type: 'string',\n description: 'Project name, typically from package.json.',\n },\n enforcement: {\n type: 'string',\n enum: ['warn', 'enforce'],\n default: 'warn',\n description: 'Whether conventions are warned about or enforced as errors.',\n },\n stack: {\n type: 'object',\n required: ['language', 'packageManager'],\n properties: {\n framework: {\n type: 'string',\n description: 'Primary framework identifier (e.g. \"nextjs@15\", \"remix@2\").',\n },\n language: {\n type: 'string',\n description: 'Primary language (e.g. \"typescript\", \"javascript\").',\n },\n styling: {\n type: 'string',\n description: 'Styling solution (e.g. \"tailwindcss@4\", \"css-modules\").',\n },\n backend: {\n type: 'string',\n description: 'Backend framework (e.g. \"express@5\", \"fastify\").',\n },\n packageManager: {\n type: 'string',\n description: 'Package manager (e.g. \"pnpm\", \"npm\", \"yarn\").',\n },\n linter: {\n type: 'string',\n description: 'Linter (e.g. \"eslint@9\", \"biome\").',\n },\n formatter: {\n type: 'string',\n description: 'Formatter (e.g. \"prettier\", \"biome\").',\n },\n testRunner: {\n type: 'string',\n description: 'Test runner (e.g. \"vitest\", \"jest\").',\n },\n },\n additionalProperties: false,\n description: 'Detected or configured technology stack.',\n },\n structure: {\n type: 'object',\n properties: {\n srcDir: {\n type: 'string',\n description: 'Source directory (e.g. \"src\"), or omit for flat structure.',\n },\n pages: {\n type: 'string',\n description: 'Pages or routes directory (e.g. \"src/app\").',\n },\n components: {\n type: 'string',\n description: 'Components directory (e.g. \"src/components\").',\n },\n hooks: {\n type: 'string',\n description: 'Hooks directory (e.g. \"src/hooks\").',\n },\n utils: {\n type: 'string',\n description: 'Utilities directory (e.g. \"src/utils\", \"src/lib\").',\n },\n types: {\n type: 'string',\n description: 'Type definitions directory (e.g. \"src/types\").',\n },\n tests: {\n type: 'string',\n description: 'Tests directory (e.g. \"tests\", \"__tests__\").',\n },\n testPattern: {\n type: 'string',\n description: 'Test file naming pattern (e.g. \"*.test.ts\", \"*.spec.ts\").',\n },\n },\n additionalProperties: false,\n description: 'Detected or configured directory structure.',\n },\n conventions: {\n type: 'object',\n properties: {\n fileNaming: { $ref: '#/definitions/conventionValue' },\n componentNaming: { $ref: '#/definitions/conventionValue' },\n hookNaming: { $ref: '#/definitions/conventionValue' },\n importAlias: { $ref: '#/definitions/conventionValue' },\n },\n additionalProperties: false,\n description: 'Detected or configured coding conventions.',\n },\n rules: {\n type: 'object',\n required: [\n 'maxFileLines',\n 'maxFunctionLines',\n 'requireTests',\n 'enforceNaming',\n 'enforceBoundaries',\n ],\n properties: {\n maxFileLines: {\n type: 'number',\n default: 300,\n description: 'Maximum number of lines allowed per file.',\n },\n maxFunctionLines: {\n type: 'number',\n default: 50,\n description: 'Maximum number of lines allowed per function.',\n },\n requireTests: {\n type: 'boolean',\n default: true,\n description: 'Whether to require test files for source modules.',\n },\n enforceNaming: {\n type: 'boolean',\n default: true,\n description: 'Whether to enforce detected file naming conventions.',\n },\n enforceBoundaries: {\n type: 'boolean',\n default: false,\n description: 'Whether to enforce module boundary rules.',\n },\n },\n additionalProperties: false,\n description: 'Rule thresholds and toggles for enforcement.',\n },\n ignore: {\n type: 'array',\n items: { type: 'string' },\n description: 'Glob patterns for files and directories to ignore.',\n },\n boundaries: {\n type: 'array',\n items: {\n type: 'object',\n required: ['from', 'to', 'allow'],\n properties: {\n from: {\n type: 'string',\n description: 'Source package or directory pattern.',\n },\n to: {\n type: 'string',\n description: 'Target package or directory pattern.',\n },\n allow: {\n type: 'boolean',\n description: 'Whether this import direction is allowed or disallowed.',\n },\n reason: {\n type: 'string',\n description: 'Human-readable explanation of why this boundary exists.',\n },\n },\n additionalProperties: false,\n },\n description: 'Module boundary rules for import enforcement.',\n },\n workspace: {\n type: 'object',\n required: ['packages', 'isMonorepo'],\n properties: {\n packages: {\n type: 'array',\n items: { type: 'string' },\n description: 'Relative paths to workspace packages.',\n },\n isMonorepo: {\n type: 'boolean',\n description: 'Whether this project is a monorepo with multiple packages.',\n },\n },\n additionalProperties: false,\n description: 'Workspace configuration for monorepo projects.',\n },\n packages: {\n type: 'array',\n items: {\n type: 'object',\n required: ['name', 'path'],\n properties: {\n name: { type: 'string', description: 'Package name from package.json.' },\n path: { type: 'string', description: 'Relative path to the package.' },\n stack: {\n type: 'object',\n properties: {\n framework: { type: 'string' },\n language: { type: 'string' },\n styling: { type: 'string' },\n backend: { type: 'string' },\n packageManager: { type: 'string' },\n linter: { type: 'string' },\n formatter: { type: 'string' },\n testRunner: { type: 'string' },\n },\n additionalProperties: false,\n },\n conventions: { $ref: '#/properties/conventions' },\n rules: {\n type: 'object',\n properties: {\n maxFileLines: { type: 'number' },\n maxFunctionLines: { type: 'number' },\n requireTests: { type: 'boolean' },\n enforceNaming: { type: 'boolean' },\n enforceBoundaries: { type: 'boolean' },\n },\n additionalProperties: false,\n },\n ignore: { type: 'array', items: { type: 'string' } },\n },\n additionalProperties: false,\n },\n description: 'Per-package overrides for monorepo projects.',\n },\n },\n additionalProperties: false,\n definitions: {\n conventionValue: {\n description:\n 'A convention value — either a plain string (user-confirmed) or an object with scanner metadata.',\n oneOf: [\n { type: 'string' },\n {\n type: 'object',\n required: ['value', '_confidence', '_consistency'],\n properties: {\n value: {\n type: 'string',\n description: 'The convention value.',\n },\n _confidence: {\n type: 'string',\n enum: ['high', 'medium', 'low'],\n description: 'Scanner confidence level.',\n },\n _consistency: {\n type: 'number',\n minimum: 0,\n maximum: 100,\n description: 'Scanner consistency percentage.',\n },\n _detected: {\n type: 'boolean',\n description: 'Set by mergeConfig when a convention is newly detected during sync.',\n },\n },\n additionalProperties: false,\n },\n ],\n },\n },\n} as const;\n","declare const __PACKAGE_VERSION__: string;\nexport const VERSION: string = __PACKAGE_VERSION__;\n\nexport { DEFAULT_IGNORE, DEFAULT_RULES } from './defaults.js';\nexport { generateConfig } from './generate-config.js';\nexport { loadConfig, loadConfigSafe } from './load-config.js';\nexport { mergeConfig } from './merge-config.js';\nexport { configSchema } from './schema.js';\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@viberails/config",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "description": "Config generation and loading for viberails",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -26,7 +26,7 @@
26
26
  },
27
27
  "dependencies": {
28
28
  "ajv": "^8.18.0",
29
- "@viberails/types": "0.2.1"
29
+ "@viberails/types": "0.2.2"
30
30
  },
31
31
  "devDependencies": {
32
32
  "@types/node": "^25.3.5"