uilint 0.2.23 → 0.2.27

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.
@@ -1,13 +1,15 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  GENSTYLEGUIDE_COMMAND_MD,
4
- detectPackageManager,
5
- loadSkill
6
- } from "./chunk-PBEKMDUH.js";
4
+ loadSkill,
5
+ toInstallSpecifier
6
+ } from "./chunk-TWUDB36F.js";
7
+ import {
8
+ detectPackageManager
9
+ } from "./chunk-P4I4RKBY.js";
7
10
 
8
11
  // src/commands/install/plan.ts
9
12
  import { join as join2 } from "path";
10
- import { createRequire as createRequire2 } from "module";
11
13
 
12
14
  // src/utils/rule-loader.ts
13
15
  import { readFileSync, existsSync, readdirSync } from "fs";
@@ -82,21 +84,53 @@ function getUilintEslintDistDir() {
82
84
  'Could not find uilint-eslint "dist/" directory. This is a bug in uilint installation.'
83
85
  );
84
86
  }
87
+ var EXPORTED_UTILITIES = [
88
+ "create-rule",
89
+ "cache",
90
+ "styleguide-loader",
91
+ "import-graph",
92
+ "component-parser",
93
+ "export-resolver",
94
+ "coverage-aggregator",
95
+ "dependency-graph",
96
+ "file-categorizer",
97
+ "jsx-coverage-analyzer"
98
+ ];
99
+ var EXPORT_RENAMES = {
100
+ "import-graph:clearCache": "clearImportGraphCache"
101
+ };
102
+ function transformImportSpecifier(specifier, utilFile) {
103
+ const trimmed = specifier.trim();
104
+ const aliasMatch = trimmed.match(/^(\w+)\s+as\s+(\w+)$/);
105
+ if (aliasMatch) {
106
+ const [, localName, alias] = aliasMatch;
107
+ const renameKey2 = `${utilFile}:${localName}`;
108
+ const externalName2 = EXPORT_RENAMES[renameKey2];
109
+ if (externalName2) {
110
+ if (alias === externalName2) {
111
+ return externalName2;
112
+ }
113
+ return `${externalName2} as ${alias}`;
114
+ }
115
+ return trimmed;
116
+ }
117
+ const renameKey = `${utilFile}:${trimmed}`;
118
+ const externalName = EXPORT_RENAMES[renameKey];
119
+ if (externalName) {
120
+ return externalName;
121
+ }
122
+ return trimmed;
123
+ }
85
124
  function transformRuleContent(content) {
86
125
  let transformed = content;
87
- const utilsFromPackage = [
88
- "create-rule",
89
- "cache",
90
- "styleguide-loader",
91
- "import-graph",
92
- "component-parser",
93
- "export-resolver"
94
- ];
95
126
  transformed = transformed.replace(
96
127
  /import\s+{([^}]+)}\s+from\s+["']\.\.\/utils\/([^"']+)\.js["'];?/g,
97
128
  (match, imports, utilFile) => {
98
- if (utilsFromPackage.includes(utilFile)) {
99
- return `import {${imports}} from "uilint-eslint";`;
129
+ if (EXPORTED_UTILITIES.includes(utilFile)) {
130
+ const specifiers = imports.split(",").map(
131
+ (s) => transformImportSpecifier(s, utilFile)
132
+ );
133
+ return `import { ${specifiers.join(", ")} } from "uilint-eslint";`;
100
134
  }
101
135
  return match;
102
136
  }
@@ -104,7 +138,7 @@ function transformRuleContent(content) {
104
138
  transformed = transformed.replace(
105
139
  /import\s+(\w+)\s+from\s+["']\.\.\/utils\/([^"']+)\.js["'];?/g,
106
140
  (match, importName, utilFile) => {
107
- if (utilsFromPackage.includes(utilFile)) {
141
+ if (EXPORTED_UTILITIES.includes(utilFile)) {
108
142
  return `import { ${importName} } from "uilint-eslint";`;
109
143
  }
110
144
  return match;
@@ -161,27 +195,6 @@ function loadSelectedRules(ruleIds, options = { typescript: true }) {
161
195
  }
162
196
 
163
197
  // src/commands/install/plan.ts
164
- var require3 = createRequire2(import.meta.url);
165
- function getSelfDependencyVersionRange(pkgName) {
166
- try {
167
- const pkgJson = require3("uilint/package.json");
168
- const deps = pkgJson?.dependencies;
169
- const optDeps = pkgJson?.optionalDependencies;
170
- const peerDeps = pkgJson?.peerDependencies;
171
- const v = deps?.[pkgName] ?? optDeps?.[pkgName] ?? peerDeps?.[pkgName];
172
- return typeof v === "string" ? v : null;
173
- } catch {
174
- return null;
175
- }
176
- }
177
- function toInstallSpecifier(pkgName) {
178
- const range = getSelfDependencyVersionRange(pkgName);
179
- if (!range) return pkgName;
180
- if (range.startsWith("workspace:")) return pkgName;
181
- if (range.startsWith("file:")) return pkgName;
182
- if (range.startsWith("link:")) return pkgName;
183
- return `${pkgName}@${range}`;
184
- }
185
198
  function createPlan(state, choices, options = {}) {
186
199
  const actions = [];
187
200
  const dependencies = [];
@@ -250,7 +263,19 @@ function createPlan(state, choices, options = {}) {
250
263
  dependencies.push({
251
264
  packagePath: projectPath,
252
265
  packageManager: detectPackageManager(projectPath),
253
- packages: ["uilint-react", "uilint-core", "jsx-loc-plugin"]
266
+ packages: [
267
+ toInstallSpecifier("uilint-react", {
268
+ preferWorkspaceProtocol: state.packageManager === "pnpm",
269
+ workspaceRoot: state.workspaceRoot,
270
+ targetProjectPath: projectPath
271
+ }),
272
+ toInstallSpecifier("uilint-core", {
273
+ preferWorkspaceProtocol: state.packageManager === "pnpm",
274
+ workspaceRoot: state.workspaceRoot,
275
+ targetProjectPath: projectPath
276
+ }),
277
+ "jsx-loc-plugin"
278
+ ]
254
279
  });
255
280
  actions.push({
256
281
  type: "inject_react",
@@ -269,7 +294,19 @@ function createPlan(state, choices, options = {}) {
269
294
  dependencies.push({
270
295
  packagePath: projectPath,
271
296
  packageManager: detectPackageManager(projectPath),
272
- packages: ["uilint-react", "uilint-core", "jsx-loc-plugin"]
297
+ packages: [
298
+ toInstallSpecifier("uilint-react", {
299
+ preferWorkspaceProtocol: state.packageManager === "pnpm",
300
+ workspaceRoot: state.workspaceRoot,
301
+ targetProjectPath: projectPath
302
+ }),
303
+ toInstallSpecifier("uilint-core", {
304
+ preferWorkspaceProtocol: state.packageManager === "pnpm",
305
+ workspaceRoot: state.workspaceRoot,
306
+ targetProjectPath: projectPath
307
+ }),
308
+ "jsx-loc-plugin"
309
+ ]
273
310
  });
274
311
  actions.push({
275
312
  type: "inject_react",
@@ -312,10 +349,28 @@ function createPlan(state, choices, options = {}) {
312
349
  });
313
350
  }
314
351
  }
352
+ const packagesToInstall = [
353
+ toInstallSpecifier("uilint-eslint", {
354
+ preferWorkspaceProtocol: state.packageManager === "pnpm",
355
+ workspaceRoot: state.workspaceRoot,
356
+ targetProjectPath: pkgPath
357
+ }),
358
+ "typescript-eslint"
359
+ ];
360
+ const hasCoverageRule = selectedRules.some(
361
+ (r) => r.id === "require-test-coverage"
362
+ );
363
+ if (hasCoverageRule) {
364
+ packagesToInstall.push("@vitest/coverage-v8");
365
+ actions.push({
366
+ type: "inject_vitest_coverage",
367
+ projectPath: pkgPath
368
+ });
369
+ }
315
370
  dependencies.push({
316
371
  packagePath: pkgPath,
317
372
  packageManager: detectPackageManager(pkgPath),
318
- packages: [toInstallSpecifier("uilint-eslint"), "typescript-eslint"]
373
+ packages: packagesToInstall
319
374
  });
320
375
  if (pkgInfo?.eslintConfigPath) {
321
376
  actions.push({
@@ -345,4 +400,4 @@ export {
345
400
  createPlan,
346
401
  getMissingRules
347
402
  };
348
- //# sourceMappingURL=plan-SIXVCXCK.js.map
403
+ //# sourceMappingURL=plan-5WHKVACB.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/install/plan.ts","../src/utils/rule-loader.ts"],"sourcesContent":["/**\n * Plan phase - pure function generating InstallPlan from state + choices\n *\n * This function has NO I/O whatsoever. It takes the analyzed ProjectState,\n * user choices, and options, then returns an InstallPlan describing exactly\n * what actions to take.\n */\n\nimport { join } from \"path\";\nimport type { RuleMetadata } from \"uilint-eslint\";\nimport type {\n ProjectState,\n UserChoices,\n InstallPlan,\n InstallAction,\n DependencyInstall,\n PlanOptions,\n} from \"./types.js\";\nimport { GENSTYLEGUIDE_COMMAND_MD } from \"./constants.js\";\nimport { toInstallSpecifier } from \"./versioning.js\";\nimport { loadSkill } from \"../../utils/skill-loader.js\";\nimport { loadSelectedRules } from \"../../utils/rule-loader.js\";\nimport { detectPackageManager } from \"../../utils/package-manager.js\";\n\n/**\n * Create the install plan from project state and user choices\n *\n * @param state - The analyzed project state\n * @param choices - User's installation choices\n * @param options - Planning options (force, etc.)\n * @returns InstallPlan with all actions and dependencies\n */\nexport function createPlan(\n state: ProjectState,\n choices: UserChoices,\n options: PlanOptions = {}\n): InstallPlan {\n const actions: InstallAction[] = [];\n const dependencies: DependencyInstall[] = [];\n\n const { force = false } = options;\n const { items } = choices;\n\n // Ensure .cursor directory exists if needed\n const needsCursorDir =\n items.includes(\"genstyleguide\") || items.includes(\"skill\");\n\n if (needsCursorDir && !state.cursorDir.exists) {\n actions.push({\n type: \"create_directory\",\n path: state.cursorDir.path,\n });\n }\n\n // =========================================================================\n // Genstyleguide Command\n // =========================================================================\n if (items.includes(\"genstyleguide\")) {\n const commandsDir = join(state.cursorDir.path, \"commands\");\n\n actions.push({\n type: \"create_directory\",\n path: commandsDir,\n });\n\n actions.push({\n type: \"create_file\",\n path: join(commandsDir, \"genstyleguide.md\"),\n content: GENSTYLEGUIDE_COMMAND_MD,\n });\n }\n\n // =========================================================================\n // Agent Skill Installation\n // =========================================================================\n if (items.includes(\"skill\")) {\n const skillsDir = join(state.cursorDir.path, \"skills\");\n\n // Create skills directory\n actions.push({\n type: \"create_directory\",\n path: skillsDir,\n });\n\n // Load and install the ui-consistency-enforcer skill\n try {\n const skill = loadSkill(\"ui-consistency-enforcer\");\n const skillDir = join(skillsDir, skill.name);\n\n // Create skill directory\n actions.push({\n type: \"create_directory\",\n path: skillDir,\n });\n\n // Create all skill files\n for (const file of skill.files) {\n const filePath = join(skillDir, file.relativePath);\n\n // Ensure subdirectories exist (e.g., references/)\n const fileDir = join(\n skillDir,\n file.relativePath.split(\"/\").slice(0, -1).join(\"/\")\n );\n if (fileDir !== skillDir && file.relativePath.includes(\"/\")) {\n actions.push({\n type: \"create_directory\",\n path: fileDir,\n });\n }\n\n actions.push({\n type: \"create_file\",\n path: filePath,\n content: file.content,\n });\n }\n } catch {\n // Skill not found - skip silently (shouldn't happen in normal install)\n }\n }\n\n // =========================================================================\n // Next.js Overlay Installation\n // =========================================================================\n if (items.includes(\"next\") && choices.next) {\n const { projectPath, detection, targetFile, createProviders } =\n choices.next;\n\n // Install Next.js routes\n actions.push({\n type: \"install_next_routes\",\n projectPath,\n appRoot: detection.appRoot,\n });\n\n // Install React overlay dependencies using the package manager for this specific target\n dependencies.push({\n packagePath: projectPath,\n packageManager: detectPackageManager(projectPath),\n packages: [\n toInstallSpecifier(\"uilint-react\", {\n preferWorkspaceProtocol: state.packageManager === \"pnpm\",\n workspaceRoot: state.workspaceRoot,\n targetProjectPath: projectPath,\n }),\n toInstallSpecifier(\"uilint-core\", {\n preferWorkspaceProtocol: state.packageManager === \"pnpm\",\n workspaceRoot: state.workspaceRoot,\n targetProjectPath: projectPath,\n }),\n \"jsx-loc-plugin\",\n ],\n });\n\n // Inject <uilint-devtools /> web component into React\n // Use targetFile or createProviders if specified by the user\n actions.push({\n type: \"inject_react\",\n projectPath,\n appRoot: detection.appRoot,\n targetFile,\n createProviders,\n });\n\n // Inject jsx-loc-plugin into next.config\n actions.push({\n type: \"inject_next_config\",\n projectPath,\n });\n }\n\n // =========================================================================\n // Vite Overlay Installation\n // =========================================================================\n if (items.includes(\"vite\") && choices.vite) {\n const { projectPath, detection } = choices.vite;\n\n // Install React overlay dependencies using the package manager for this specific target\n dependencies.push({\n packagePath: projectPath,\n packageManager: detectPackageManager(projectPath),\n packages: [\n toInstallSpecifier(\"uilint-react\", {\n preferWorkspaceProtocol: state.packageManager === \"pnpm\",\n workspaceRoot: state.workspaceRoot,\n targetProjectPath: projectPath,\n }),\n toInstallSpecifier(\"uilint-core\", {\n preferWorkspaceProtocol: state.packageManager === \"pnpm\",\n workspaceRoot: state.workspaceRoot,\n targetProjectPath: projectPath,\n }),\n \"jsx-loc-plugin\",\n ],\n });\n\n // Inject <uilint-devtools /> web component into React entry\n actions.push({\n type: \"inject_react\",\n projectPath,\n appRoot: detection.entryRoot,\n mode: \"vite\",\n });\n\n // Inject jsx-loc-plugin into vite.config\n actions.push({\n type: \"inject_vite_config\",\n projectPath,\n });\n }\n\n // =========================================================================\n // ESLint Plugin Installation\n // =========================================================================\n if (items.includes(\"eslint\") && choices.eslint) {\n const { packagePaths, selectedRules } = choices.eslint;\n\n for (const pkgPath of packagePaths) {\n const pkgInfo = state.packages.find((p) => p.path === pkgPath);\n\n // Create .uilint/rules directory alongside the target app (not at workspace root)\n const rulesDir = join(pkgPath, \".uilint\", \"rules\");\n actions.push({\n type: \"create_directory\",\n path: rulesDir,\n });\n\n // Load and copy rule files into this target package\n // Use TypeScript rule files if the ESLint config is TypeScript (.ts)\n // This ensures the imports match the actual rule files being copied\n const isTypeScriptConfig =\n pkgInfo?.eslintConfigPath?.endsWith(\".ts\") ?? false;\n const ruleFiles = loadSelectedRules(\n selectedRules.map((r) => r.id),\n {\n typescript: isTypeScriptConfig,\n }\n );\n for (const ruleFile of ruleFiles) {\n // Copy implementation file\n actions.push({\n type: \"create_file\",\n path: join(rulesDir, ruleFile.implementation.relativePath),\n content: ruleFile.implementation.content,\n });\n\n // Copy test file if it exists (only for TypeScript configs)\n if (ruleFile.test && isTypeScriptConfig) {\n actions.push({\n type: \"create_file\",\n path: join(rulesDir, ruleFile.test.relativePath),\n content: ruleFile.test.content,\n });\n }\n }\n\n // Install dependencies using the package manager for this specific target\n const packagesToInstall = [\n toInstallSpecifier(\"uilint-eslint\", {\n preferWorkspaceProtocol: state.packageManager === \"pnpm\",\n workspaceRoot: state.workspaceRoot,\n targetProjectPath: pkgPath,\n }),\n \"typescript-eslint\",\n ];\n\n // If require-test-coverage rule is selected, add coverage package and config\n const hasCoverageRule = selectedRules.some(\n (r) => r.id === \"require-test-coverage\"\n );\n if (hasCoverageRule) {\n packagesToInstall.push(\"@vitest/coverage-v8\");\n\n // Add action to inject coverage config into vitest.config.ts\n actions.push({\n type: \"inject_vitest_coverage\",\n projectPath: pkgPath,\n });\n }\n\n dependencies.push({\n packagePath: pkgPath,\n packageManager: detectPackageManager(pkgPath),\n packages: packagesToInstall,\n });\n\n // Inject ESLint rules (will reference local .uilint/rules/ files)\n if (pkgInfo?.eslintConfigPath) {\n actions.push({\n type: \"inject_eslint\",\n packagePath: pkgPath,\n configPath: pkgInfo.eslintConfigPath,\n rules: selectedRules,\n hasExistingRules: pkgInfo.hasUilintRules,\n });\n }\n }\n\n // Add .uilint/.cache to .gitignore at workspace root\n const gitignorePath = join(state.workspaceRoot, \".gitignore\");\n actions.push({\n type: \"append_to_file\",\n path: gitignorePath,\n content: \"\\n# UILint cache\\n.uilint/.cache\\n\",\n ifNotContains: \".uilint/.cache\",\n });\n }\n\n return { actions, dependencies };\n}\n\n/**\n * Get the list of rules that are missing from a package's ESLint config\n */\nexport function getMissingRules(\n configuredRuleIds: string[],\n selectedRules: RuleMetadata[]\n): RuleMetadata[] {\n const configuredSet = new Set(configuredRuleIds);\n return selectedRules.filter((rule) => !configuredSet.has(rule.id));\n}\n","/**\n * Rule Loader Utility\n *\n * Loads ESLint rule source files from the uilint-eslint package for installation\n * into user projects. Rules are copied to .uilint/rules/ in the target project.\n */\n\nimport { readFileSync, existsSync, readdirSync } from \"fs\";\nimport { join, dirname } from \"path\";\nimport { fileURLToPath } from \"url\";\nimport { createRequire } from \"module\";\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = dirname(__filename);\nconst require = createRequire(import.meta.url);\n\nfunction findNodeModulesPackageRoot(\n pkgName: string,\n startDir: string\n): string | null {\n let dir = startDir;\n while (true) {\n const candidate = join(dir, \"node_modules\", pkgName);\n if (existsSync(join(candidate, \"package.json\"))) return candidate;\n const parent = dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n return null;\n}\n\nfunction getUilintEslintPackageRoot(): string {\n // Prefer a filesystem-based lookup first. This avoids Node resolution edge\n // cases with package.json \"exports\" (especially ESM-only packages), and works\n // well for monorepos + pnpm where node_modules contains symlinks.\n //\n // Search upwards from process.cwd() (the user's project) and from this file\n // location (for test/dev environments).\n const fromCwd = findNodeModulesPackageRoot(\"uilint-eslint\", process.cwd());\n if (fromCwd) return fromCwd;\n\n const fromHere = findNodeModulesPackageRoot(\"uilint-eslint\", __dirname);\n if (fromHere) return fromHere;\n\n // Last resort: try resolver-based lookup.\n try {\n const entry = require.resolve(\"uilint-eslint\"); // typically .../dist/index.js\n const entryDir = dirname(entry); // typically .../dist\n return dirname(entryDir); // package root\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n throw new Error(\n `Unable to locate uilint-eslint in node_modules (searched upwards from cwd and uilint's install path).\\n` +\n `Resolver error: ${msg}\\n` +\n `Fix: ensure uilint-eslint is installed in the target project (or workspace) and try again.`\n );\n }\n}\n\n/**\n * Represents a file for a rule (implementation or test)\n */\nexport interface RuleFile {\n /** Relative path within the rules directory (e.g., \"no-arbitrary-tailwind.ts\") */\n relativePath: string;\n /** File content */\n content: string;\n}\n\n/**\n * Represents a complete rule ready for installation\n */\nexport interface RuleFiles {\n /** Rule identifier (e.g., \"no-arbitrary-tailwind\") */\n ruleId: string;\n /** Implementation file */\n implementation: RuleFile;\n /** Test file (if exists) */\n test?: RuleFile;\n}\n\n/**\n * Get the path to the uilint-eslint package source directory\n */\nfunction getUilintEslintSrcDir(): string {\n // In development: packages/uilint-eslint/src/\n // In production (installed): node_modules/uilint-eslint/src/\n\n // Try workspace/dev path first (repo layout)\n const devPath = join(\n __dirname,\n \"..\",\n \"..\",\n \"..\",\n \"..\",\n \"uilint-eslint\",\n \"src\"\n );\n if (existsSync(devPath)) return devPath;\n\n // Try from installed package root (works with \"exports\")\n const pkgRoot = getUilintEslintPackageRoot();\n const srcPath = join(pkgRoot, \"src\");\n if (existsSync(srcPath)) return srcPath;\n\n throw new Error(\n 'Could not find uilint-eslint \"src/\" directory. If you are using a published install of uilint-eslint, ensure it includes source files, or run a JS-only rules install.'\n );\n}\n\n/**\n * Get the path to the uilint-eslint package dist directory\n */\nfunction getUilintEslintDistDir(): string {\n // In development: packages/uilint-eslint/dist/\n // In production (installed): node_modules/uilint-eslint/dist/\n\n // Try workspace/dev path first (repo layout)\n const devPath = join(\n __dirname,\n \"..\",\n \"..\",\n \"..\",\n \"..\",\n \"uilint-eslint\",\n \"dist\"\n );\n if (existsSync(devPath)) return devPath;\n\n // Try from installed package root (works with \"exports\")\n const pkgRoot = getUilintEslintPackageRoot();\n const distPath = join(pkgRoot, \"dist\");\n if (existsSync(distPath)) return distPath;\n\n throw new Error(\n 'Could not find uilint-eslint \"dist/\" directory. This is a bug in uilint installation.'\n );\n}\n\n/**\n * All utilities that are exported from uilint-eslint and can be imported.\n * When adding a new utility to uilint-eslint that rules may import,\n * add it here AND export it from uilint-eslint/src/index.ts.\n *\n * Rules can also declare their dependencies via the `internalDependencies`\n * field in defineRuleMeta() - this serves as documentation and can be\n * used for validation.\n */\nconst EXPORTED_UTILITIES = [\n \"create-rule\",\n \"cache\",\n \"styleguide-loader\",\n \"import-graph\",\n \"component-parser\",\n \"export-resolver\",\n \"coverage-aggregator\",\n \"dependency-graph\",\n \"file-categorizer\",\n \"jsx-coverage-analyzer\",\n];\n\n/**\n * Maps internal export names to their uilint-eslint export names.\n * Some exports are renamed when re-exported from the main package\n * to avoid naming conflicts.\n *\n * Format: \"utilFile:internalName\" -> \"externalName\"\n */\nconst EXPORT_RENAMES: Record<string, string> = {\n \"import-graph:clearCache\": \"clearImportGraphCache\",\n};\n\n/**\n * Transform an import specifier, applying any necessary renames.\n * Handles both simple imports (foo) and aliased imports (foo as bar).\n */\nfunction transformImportSpecifier(specifier: string, utilFile: string): string {\n const trimmed = specifier.trim();\n\n // Check for aliased import: \"localName as alias\"\n const aliasMatch = trimmed.match(/^(\\w+)\\s+as\\s+(\\w+)$/);\n if (aliasMatch) {\n const [, localName, alias] = aliasMatch;\n const renameKey = `${utilFile}:${localName}`;\n const externalName = EXPORT_RENAMES[renameKey];\n if (externalName) {\n // If the alias matches the external name, simplify to just the name\n if (alias === externalName) {\n return externalName;\n }\n // Otherwise, use the external name with the original alias\n return `${externalName} as ${alias}`;\n }\n // No rename needed, keep original\n return trimmed;\n }\n\n // Simple import: check if it needs renaming\n const renameKey = `${utilFile}:${trimmed}`;\n const externalName = EXPORT_RENAMES[renameKey];\n if (externalName) {\n return externalName;\n }\n\n return trimmed;\n}\n\n/**\n * Transform rule content to fix imports for copied location\n * Changes imports from \"../utils/...\" to \"uilint-eslint\"\n */\nfunction transformRuleContent(content: string): string {\n let transformed = content;\n\n // Replace all relative utility imports with uilint-eslint imports\n // Pattern: import { ... } from \"../utils/create-rule.js\" or similar\n // This handles any combination of imports like { createRule, defineRuleMeta }\n transformed = transformed.replace(\n /import\\s+{([^}]+)}\\s+from\\s+[\"']\\.\\.\\/utils\\/([^\"']+)\\.js[\"'];?/g,\n (match, imports, utilFile) => {\n if (EXPORTED_UTILITIES.includes(utilFile)) {\n // Transform each import specifier\n const specifiers = imports.split(\",\").map((s: string) =>\n transformImportSpecifier(s, utilFile)\n );\n return `import { ${specifiers.join(\", \")} } from \"uilint-eslint\";`;\n }\n return match; // Keep original if not a known utility\n }\n );\n\n // Also handle default imports: import createRule from \"../utils/create-rule.js\"\n transformed = transformed.replace(\n /import\\s+(\\w+)\\s+from\\s+[\"']\\.\\.\\/utils\\/([^\"']+)\\.js[\"'];?/g,\n (match, importName, utilFile) => {\n if (EXPORTED_UTILITIES.includes(utilFile)) {\n return `import { ${importName} } from \"uilint-eslint\";`;\n }\n return match;\n }\n );\n\n return transformed;\n}\n\n/**\n * Load a specific rule by ID\n */\nexport function loadRule(\n ruleId: string,\n options: { typescript: boolean } = { typescript: true }\n): RuleFiles {\n const { typescript } = options;\n const extension = typescript ? \".ts\" : \".js\";\n\n if (typescript) {\n // Load TypeScript source files\n const rulesDir = join(getUilintEslintSrcDir(), \"rules\");\n const implPath = join(rulesDir, `${ruleId}.ts`);\n const testPath = join(rulesDir, `${ruleId}.test.ts`);\n\n if (!existsSync(implPath)) {\n throw new Error(`Rule \"${ruleId}\" not found at ${implPath}`);\n }\n\n const rawContent = readFileSync(implPath, \"utf-8\");\n const transformedContent = transformRuleContent(rawContent);\n\n const implementation: RuleFile = {\n relativePath: `${ruleId}.ts`,\n content: transformedContent,\n };\n\n const test: RuleFile | undefined = existsSync(testPath)\n ? {\n relativePath: `${ruleId}.test.ts`,\n content: transformRuleContent(readFileSync(testPath, \"utf-8\")),\n }\n : undefined;\n\n return {\n ruleId,\n implementation,\n test,\n };\n } else {\n // Load compiled JavaScript files\n const rulesDir = join(getUilintEslintDistDir(), \"rules\");\n const implPath = join(rulesDir, `${ruleId}.js`);\n\n if (!existsSync(implPath)) {\n throw new Error(\n `Rule \"${ruleId}\" not found at ${implPath}. ` +\n `For JavaScript-only projects, uilint-eslint must be built to include compiled rule files in dist/rules/. ` +\n `If you're developing uilint-eslint, run 'pnpm build' in packages/uilint-eslint. ` +\n `If you're using a published package, ensure it includes the dist/ directory.`\n );\n }\n\n // Compiled JS files don't need transformation - they already use uilint-eslint imports\n const content = readFileSync(implPath, \"utf-8\");\n\n const implementation: RuleFile = {\n relativePath: `${ruleId}.js`,\n content,\n };\n\n // Test files are not compiled, so we don't copy them for JS projects\n return {\n ruleId,\n implementation,\n };\n }\n}\n\n/**\n * Load multiple rules by their IDs\n */\nexport function loadSelectedRules(\n ruleIds: string[],\n options: { typescript: boolean } = { typescript: true }\n): RuleFiles[] {\n return ruleIds.map((id) => loadRule(id, options));\n}\n\n/**\n * Get the list of available rule IDs from the registry\n */\nexport function getAvailableRuleIds(): string[] {\n try {\n // Import the rule registry from uilint-eslint\n const { ruleRegistry } = require(\"uilint-eslint\");\n return ruleRegistry.map((rule: { id: string }) => rule.id);\n } catch {\n // Fallback: try to read from filesystem\n const rulesDir = join(getUilintEslintSrcDir(), \"rules\");\n if (!existsSync(rulesDir)) {\n return [];\n }\n\n // This is a fallback - ideally we'd use the registry\n // But if we can't import it, we can at least try to list files\n const files = readdirSync(rulesDir);\n return files\n .filter((f: string) => f.endsWith(\".ts\") && !f.endsWith(\".test.ts\"))\n .map((f: string) => f.replace(\".ts\", \"\"));\n }\n}\n"],"mappings":"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","names":["join","require","renameKey","externalName","join"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "uilint",
3
- "version": "0.2.23",
3
+ "version": "0.2.27",
4
4
  "description": "CLI for UILint - AI-powered UI consistency checking",
5
5
  "author": "Peter Suggate",
6
6
  "repository": {
@@ -42,12 +42,14 @@
42
42
  "dotenv": "^17.2.3",
43
43
  "ink": "^6.6.0",
44
44
  "magicast": "^0.5.1",
45
+ "ora": "^9.0.0",
45
46
  "picocolors": "^1.1.1",
46
47
  "react": "^19.2.3",
47
48
  "typescript": "^5.9.3",
48
49
  "ws": "^8.19.0",
49
- "uilint-core": "0.2.23",
50
- "uilint-eslint": "0.2.23"
50
+ "uilint-core": "0.2.27",
51
+ "uilint-eslint": "0.2.27",
52
+ "uilint-duplicates": "0.2.27"
51
53
  },
52
54
  "optionalDependencies": {
53
55
  "@langfuse/client": "^4.5.1",
@@ -56,11 +58,14 @@
56
58
  "@opentelemetry/sdk-node": "^0.209.0"
57
59
  },
58
60
  "devDependencies": {
61
+ "@inkjs/ui": "^2.0.0",
59
62
  "@types/node": "^25.0.8",
60
63
  "@types/react": "^19.2.8",
61
64
  "@types/ws": "^8.18.1",
65
+ "ink-testing-library": "^4.0.0",
62
66
  "tsup": "^8.5.1",
63
- "vitest": "^4.0.17"
67
+ "vitest": "^4.0.17",
68
+ "uilint-react": "0.2.27"
64
69
  },
65
70
  "keywords": [
66
71
  "cli",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/utils/prompts.ts","../src/utils/next-detect.ts","../src/utils/eslint-config-inject.ts"],"sourcesContent":["/**\n * Shared clack/prompts utilities for UILint CLI\n * Provides branded intro/outro, spinners, and common UI patterns\n */\n\nimport * as p from \"@clack/prompts\";\nimport pc from \"picocolors\";\nimport { readFileSync } from \"fs\";\nimport { dirname, join } from \"path\";\nimport { fileURLToPath } from \"url\";\n\n/**\n * Get the CLI version from package.json\n */\nfunction getCLIVersion(): string {\n try {\n const __dirname = dirname(fileURLToPath(import.meta.url));\n const pkgPath = join(__dirname, \"..\", \"..\", \"package.json\");\n const pkg = JSON.parse(readFileSync(pkgPath, \"utf-8\")) as {\n version?: string;\n };\n return pkg.version || \"0.0.0\";\n } catch {\n return \"0.0.0\";\n }\n}\n\n/**\n * Branded UILint intro with logo and version\n */\nexport function intro(title?: string): void {\n const version = getCLIVersion();\n const header = pc.bold(pc.cyan(\"◆ UILint\")) + pc.dim(` v${version}`);\n \n console.log();\n p.intro(title ? `${header} ${pc.dim(\"·\")} ${title}` : header);\n}\n\n/**\n * Styled outro with next steps\n */\nexport function outro(message: string): void {\n p.outro(pc.green(message));\n}\n\n/**\n * Cancel message when user exits\n */\nexport function cancel(message = \"Operation cancelled.\"): void {\n p.cancel(pc.yellow(message));\n process.exit(0);\n}\n\n/**\n * Check if user cancelled a prompt\n */\nexport function isCancel(value: unknown): value is symbol {\n return p.isCancel(value);\n}\n\n/**\n * Handle cancel check - exits if cancelled\n */\nexport function handleCancel<T>(value: T | symbol): T {\n if (p.isCancel(value)) {\n cancel();\n process.exit(0);\n }\n return value as T;\n}\n\n/**\n * Spinner wrapper with automatic error handling\n */\nexport async function withSpinner<T>(\n message: string,\n fn: (() => Promise<T>) | ((spinner: ReturnType<typeof p.spinner>) => Promise<T>)\n): Promise<T> {\n const s = p.spinner();\n s.start(message);\n try {\n const result =\n fn.length >= 1\n ? await (fn as (spinner: ReturnType<typeof p.spinner>) => Promise<T>)(s)\n : await (fn as () => Promise<T>)();\n s.stop(pc.green(\"✓ \") + message);\n return result;\n } catch (error) {\n s.stop(pc.red(\"✗ \") + message);\n throw error;\n }\n}\n\n/**\n * Spinner that can be updated\n */\nexport function createSpinner() {\n return p.spinner();\n}\n\n/**\n * Display a note box\n */\nexport function note(message: string, title?: string): void {\n p.note(message, title);\n}\n\n/**\n * Display a log message\n */\nexport function log(message: string): void {\n p.log.message(message);\n}\n\n/**\n * Display an info message\n */\nexport function logInfo(message: string): void {\n p.log.info(message);\n}\n\n/**\n * Display a success message\n */\nexport function logSuccess(message: string): void {\n p.log.success(message);\n}\n\n/**\n * Display a warning message\n */\nexport function logWarning(message: string): void {\n p.log.warn(message);\n}\n\n/**\n * Display an error message\n */\nexport function logError(message: string): void {\n p.log.error(message);\n}\n\n/**\n * Display a step message\n */\nexport function logStep(message: string): void {\n p.log.step(message);\n}\n\n/**\n * Select prompt wrapper\n */\nexport async function select<T extends string>(options: {\n message: string;\n options: Array<{ value: T; label: string; hint?: string }>;\n initialValue?: T;\n}): Promise<T> {\n const result = await p.select({\n message: options.message,\n options: options.options as { value: T; label: string; hint?: string }[],\n initialValue: options.initialValue,\n } as Parameters<typeof p.select>[0]);\n return handleCancel(result) as T;\n}\n\n/**\n * Confirm prompt wrapper\n */\nexport async function confirm(options: {\n message: string;\n initialValue?: boolean;\n}): Promise<boolean> {\n const result = await p.confirm({\n message: options.message,\n initialValue: options.initialValue ?? true,\n });\n return handleCancel(result);\n}\n\n/**\n * Text input prompt wrapper\n */\nexport async function text(options: {\n message: string;\n placeholder?: string;\n defaultValue?: string;\n validate?: (value: string) => string | Error | undefined;\n}): Promise<string> {\n const result = await p.text(options);\n return handleCancel(result);\n}\n\n/**\n * Multiselect prompt wrapper\n */\nexport async function multiselect<T extends string>(options: {\n message: string;\n options: Array<{ value: T; label: string; hint?: string }>;\n required?: boolean;\n initialValues?: T[];\n}): Promise<T[]> {\n const result = await p.multiselect({\n message: options.message,\n options: options.options as { value: T; label: string; hint?: string }[],\n required: options.required,\n initialValues: options.initialValues,\n } as Parameters<typeof p.multiselect>[0]);\n return handleCancel(result) as T[];\n}\n\n/**\n * Group of tasks displayed together\n */\nexport async function group<T extends Record<string, unknown>>(\n prompts: p.PromptGroup<T>,\n options?: p.PromptGroupOptions<T>\n): Promise<T> {\n const result = await p.group(prompts, options);\n return result;\n}\n\n// Re-export picocolors for consistent styling\nexport { pc };\n","import { existsSync, readdirSync } from \"fs\";\nimport { join } from \"path\";\n\nexport interface NextAppRouterDetection {\n /**\n * Relative path to the Next App Router root dir (either \"app\" or \"src/app\").\n */\n appRoot: string;\n /**\n * Absolute path to the App Router root dir.\n */\n appRootAbs: string;\n /**\n * Candidate entry files (relative paths) that are good injection targets.\n */\n candidates: string[];\n}\n\nfunction fileExists(projectPath: string, relPath: string): boolean {\n return existsSync(join(projectPath, relPath));\n}\n\nexport function detectNextAppRouter(\n projectPath: string\n): NextAppRouterDetection | null {\n const roots = [\"app\", join(\"src\", \"app\")];\n const candidates: string[] = [];\n\n let chosenRoot: string | null = null;\n for (const root of roots) {\n if (existsSync(join(projectPath, root))) {\n chosenRoot = root;\n break;\n }\n }\n\n if (!chosenRoot) return null;\n\n // Prioritize layout files (Next App Router canonical integration point).\n const entryCandidates = [\n join(chosenRoot, \"layout.tsx\"),\n join(chosenRoot, \"layout.jsx\"),\n join(chosenRoot, \"layout.ts\"),\n join(chosenRoot, \"layout.js\"),\n // Fallbacks (less ideal, but can work):\n join(chosenRoot, \"page.tsx\"),\n join(chosenRoot, \"page.jsx\"),\n ];\n\n for (const rel of entryCandidates) {\n if (fileExists(projectPath, rel)) candidates.push(rel);\n }\n\n // If nothing exists, still return detection so routes can be installed.\n return {\n appRoot: chosenRoot,\n appRootAbs: join(projectPath, chosenRoot),\n candidates,\n };\n}\n\nexport interface NextAppRouterProjectMatch {\n /**\n * Absolute path to the Next project root (dir containing app/ or src/app/).\n */\n projectPath: string;\n detection: NextAppRouterDetection;\n}\n\nconst DEFAULT_IGNORE_DIRS = new Set([\n \"node_modules\",\n \".git\",\n \".next\",\n \"dist\",\n \"build\",\n \"out\",\n \".turbo\",\n \".vercel\",\n \".cursor\",\n \"coverage\",\n \".uilint\",\n]);\n\n/**\n * Best-effort monorepo discovery for Next.js App Router apps.\n *\n * Walks down from `rootDir` looking for directories that contain `app/` or\n * `src/app/`. Skips common large/irrelevant dirs.\n */\nexport function findNextAppRouterProjects(\n rootDir: string,\n options?: { maxDepth?: number; ignoreDirs?: Set<string> }\n): NextAppRouterProjectMatch[] {\n const maxDepth = options?.maxDepth ?? 4;\n const ignoreDirs = options?.ignoreDirs ?? DEFAULT_IGNORE_DIRS;\n const results: NextAppRouterProjectMatch[] = [];\n const visited = new Set<string>();\n\n function walk(dir: string, depth: number) {\n if (depth > maxDepth) return;\n if (visited.has(dir)) return;\n visited.add(dir);\n\n const detection = detectNextAppRouter(dir);\n if (detection) {\n results.push({ projectPath: dir, detection });\n // Don't descend further once we found a project root (avoid nested hits).\n return;\n }\n\n let entries: Array<{ name: string; isDirectory: boolean }> = [];\n try {\n entries = readdirSync(dir, { withFileTypes: true }).map((d) => ({\n name: d.name,\n isDirectory: d.isDirectory(),\n }));\n } catch {\n return;\n }\n\n for (const ent of entries) {\n if (!ent.isDirectory) continue;\n if (ignoreDirs.has(ent.name)) continue;\n // Skip hidden dirs by default (except `src` which matters)\n if (ent.name.startsWith(\".\") && ent.name !== \".\") continue;\n walk(join(dir, ent.name), depth + 1);\n }\n }\n\n walk(rootDir, 0);\n return results;\n}\n","/**\n * Inject uilint-eslint rules into ESLint config\n *\n * Modifies eslint.config.{ts,mjs,js,cjs} to add uilint import and selected rules\n */\n\nimport { existsSync, readFileSync, writeFileSync } from \"fs\";\nimport { join, relative, dirname } from \"path\";\nimport type { RuleMetadata } from \"uilint-eslint\";\nimport { parseExpression, parseModule, generateCode } from \"magicast\";\nimport { findWorkspaceRoot } from \"uilint-core/node\";\n\nexport interface InstallEslintPluginOptions {\n projectPath: string;\n selectedRules: RuleMetadata[];\n force?: boolean;\n confirmAddMissingRules?: (\n relPath: string,\n missingRules: RuleMetadata[]\n ) => Promise<boolean>;\n}\n\nconst CONFIG_EXTENSIONS = [\".ts\", \".mjs\", \".js\", \".cjs\"];\n\n/**\n * Find the eslint.config file in a project\n */\nexport function findEslintConfigFile(projectPath: string): string | null {\n for (const ext of CONFIG_EXTENSIONS) {\n const configPath = join(projectPath, `eslint.config${ext}`);\n if (existsSync(configPath)) {\n return configPath;\n }\n }\n return null;\n}\n\n/**\n * Get the relative config filename for display\n */\nexport function getEslintConfigFilename(configPath: string): string {\n const parts = configPath.split(\"/\");\n return parts[parts.length - 1] || \"eslint.config.mjs\";\n}\n\n/**\n * Check if the source already has uilint rules configured\n */\nfunction hasUilintRules(source: string): boolean {\n return source.includes('\"uilint/') || source.includes(\"'uilint/\");\n}\n\n/**\n * Check if the source already has uilint imported\n */\nfunction hasUilintImport(source: string): boolean {\n return (\n source.includes('from \"uilint-eslint\"') ||\n source.includes(\"from 'uilint-eslint'\") ||\n source.includes('require(\"uilint-eslint\")') ||\n source.includes(\"require('uilint-eslint')\")\n );\n}\n\ntype UilintEslintConfigInfo = {\n /** Set of configured `uilint/*` rule IDs (without the `uilint/` prefix). */\n configuredRuleIds: Set<string>;\n /** Whether config appears to configure uilint rules. */\n configured: boolean;\n};\n\nfunction walkAst(node: any, visit: (n: any) => void): void {\n if (!node || typeof node !== \"object\") return;\n visit(node);\n for (const key of Object.keys(node)) {\n const v = (node as any)[key];\n if (!v) continue;\n if (Array.isArray(v)) {\n for (const item of v) walkAst(item, visit);\n } else if (typeof v === \"object\" && v.type) {\n walkAst(v, visit);\n }\n }\n}\n\nfunction isIdentifier(node: any, name?: string): boolean {\n return (\n !!node &&\n node.type === \"Identifier\" &&\n (name ? node.name === name : typeof node.name === \"string\")\n );\n}\n\nfunction isStringLiteral(node: any): node is { type: string; value: string } {\n return (\n !!node &&\n (node.type === \"StringLiteral\" || node.type === \"Literal\") &&\n typeof node.value === \"string\"\n );\n}\n\nfunction getObjectPropertyValue(obj: any, keyName: string): any | null {\n if (!obj || obj.type !== \"ObjectExpression\") return null;\n for (const prop of obj.properties ?? []) {\n if (!prop) continue;\n if (prop.type === \"ObjectProperty\" || prop.type === \"Property\") {\n const key = prop.key;\n const keyMatch =\n (key?.type === \"Identifier\" && key.name === keyName) ||\n (isStringLiteral(key) && key.value === keyName);\n if (keyMatch) return prop.value;\n }\n }\n return null;\n}\n\nfunction hasSpreadProperties(obj: any): boolean {\n if (!obj || obj.type !== \"ObjectExpression\") return false;\n return (obj.properties ?? []).some(\n (p: any) => p && (p.type === \"SpreadElement\" || p.type === \"SpreadProperty\")\n );\n}\n\nconst IGNORED_AST_KEYS = new Set([\n \"loc\",\n \"start\",\n \"end\",\n \"extra\",\n \"leadingComments\",\n \"trailingComments\",\n \"innerComments\",\n]);\n\nfunction normalizeAstForCompare(node: any): any {\n if (node === null) return null;\n if (node === undefined) return undefined;\n if (typeof node !== \"object\") return node;\n if (Array.isArray(node)) return node.map(normalizeAstForCompare);\n\n const out: Record<string, any> = {};\n const keys = Object.keys(node)\n .filter((k) => !IGNORED_AST_KEYS.has(k))\n .sort();\n for (const k of keys) {\n // Avoid proxy-ish or non-serializable fields if present.\n if (k.startsWith(\"$\")) continue;\n out[k] = normalizeAstForCompare(node[k]);\n }\n return out;\n}\n\nfunction astEquivalent(a: any, b: any): boolean {\n try {\n return (\n JSON.stringify(normalizeAstForCompare(a)) ===\n JSON.stringify(normalizeAstForCompare(b))\n );\n } catch {\n return false;\n }\n}\n\nfunction collectUilintRuleIdsFromRulesObject(rulesObj: any): Set<string> {\n const ids = new Set<string>();\n if (!rulesObj || rulesObj.type !== \"ObjectExpression\") return ids;\n for (const prop of rulesObj.properties ?? []) {\n if (!prop) continue;\n if (prop.type !== \"ObjectProperty\" && prop.type !== \"Property\") continue;\n const key = prop.key;\n if (!isStringLiteral(key)) continue;\n const val = key.value;\n if (typeof val !== \"string\") continue;\n if (val.startsWith(\"uilint/\")) {\n ids.add(val.slice(\"uilint/\".length));\n }\n }\n return ids;\n}\n\nfunction findExportedConfigArrayExpression(mod: any): {\n kind: \"esm\" | \"cjs\";\n arrayExpr: any;\n program: any;\n} | null {\n function unwrapExpression(expr: any): any {\n let e = expr;\n // Best-effort unwrap for TS/parenthesized wrappers. (These can appear if the\n // config is authored in TS/JS with type assertions or parentheses.)\n while (e) {\n if (e.type === \"TSAsExpression\" || e.type === \"TSNonNullExpression\") {\n e = e.expression;\n continue;\n }\n if (e.type === \"TSSatisfiesExpression\") {\n e = e.expression;\n continue;\n }\n if (e.type === \"ParenthesizedExpression\") {\n e = e.expression;\n continue;\n }\n break;\n }\n return e;\n }\n\n function resolveTopLevelIdentifierToArrayExpr(\n program: any,\n name: string\n ): any | null {\n if (!program || program.type !== \"Program\") return null;\n for (const stmt of program.body ?? []) {\n if (stmt?.type !== \"VariableDeclaration\") continue;\n for (const decl of stmt.declarations ?? []) {\n const id = decl?.id;\n if (!isIdentifier(id, name)) continue;\n const init = unwrapExpression(decl?.init);\n if (!init) return null;\n if (init.type === \"ArrayExpression\") return init;\n if (\n init.type === \"CallExpression\" &&\n isIdentifier(init.callee, \"defineConfig\") &&\n unwrapExpression(init.arguments?.[0])?.type === \"ArrayExpression\"\n ) {\n return unwrapExpression(init.arguments?.[0]);\n }\n return null;\n }\n }\n return null;\n }\n\n // Prefer reading directly from the program AST so we can handle:\n // - export default [ ... ]\n // - export default defineConfig([ ... ])\n // - export default eslintConfig; (where eslintConfig is a top-level array)\n const program = mod?.$ast;\n if (program && program.type === \"Program\") {\n for (const stmt of program.body ?? []) {\n if (!stmt || stmt.type !== \"ExportDefaultDeclaration\") continue;\n const decl = unwrapExpression(stmt.declaration);\n if (!decl) break;\n\n if (decl.type === \"ArrayExpression\") {\n return { kind: \"esm\", arrayExpr: decl, program };\n }\n if (\n decl.type === \"CallExpression\" &&\n isIdentifier(decl.callee, \"defineConfig\") &&\n unwrapExpression(decl.arguments?.[0])?.type === \"ArrayExpression\"\n ) {\n return {\n kind: \"esm\",\n arrayExpr: unwrapExpression(decl.arguments?.[0]),\n program,\n };\n }\n if (decl.type === \"Identifier\" && typeof decl.name === \"string\") {\n const resolved = resolveTopLevelIdentifierToArrayExpr(\n program,\n decl.name\n );\n if (resolved) return { kind: \"esm\", arrayExpr: resolved, program };\n }\n break;\n }\n }\n\n // CommonJS: module.exports = [ ... ] OR module.exports = defineConfig([ ... ])\n if (!program || program.type !== \"Program\") return null;\n\n for (const stmt of program.body ?? []) {\n if (!stmt || stmt.type !== \"ExpressionStatement\") continue;\n const expr = stmt.expression;\n if (!expr || expr.type !== \"AssignmentExpression\") continue;\n const left = expr.left;\n const right = expr.right;\n const isModuleExports =\n left?.type === \"MemberExpression\" &&\n isIdentifier(left.object, \"module\") &&\n isIdentifier(left.property, \"exports\");\n if (!isModuleExports) continue;\n\n if (right?.type === \"ArrayExpression\") {\n return { kind: \"cjs\", arrayExpr: right, program };\n }\n if (\n right?.type === \"CallExpression\" &&\n isIdentifier(right.callee, \"defineConfig\") &&\n right.arguments?.[0]?.type === \"ArrayExpression\"\n ) {\n return { kind: \"cjs\", arrayExpr: right.arguments[0], program };\n }\n if (right?.type === \"Identifier\" && typeof right.name === \"string\") {\n const resolved = resolveTopLevelIdentifierToArrayExpr(\n program,\n right.name\n );\n if (resolved) return { kind: \"cjs\", arrayExpr: resolved, program };\n }\n }\n\n return null;\n}\n\nfunction collectConfiguredUilintRuleIdsFromConfigArray(\n arrayExpr: any\n): Set<string> {\n const ids = new Set<string>();\n if (!arrayExpr || arrayExpr.type !== \"ArrayExpression\") return ids;\n for (const el of arrayExpr.elements ?? []) {\n if (!el || el.type !== \"ObjectExpression\") continue;\n const rules = getObjectPropertyValue(el, \"rules\");\n for (const id of collectUilintRuleIdsFromRulesObject(rules)) ids.add(id);\n }\n return ids;\n}\n\nfunction findExistingUilintRulesObject(arrayExpr: any): {\n configObj: any | null;\n rulesObj: any | null;\n safeToMutate: boolean;\n} {\n if (!arrayExpr || arrayExpr.type !== \"ArrayExpression\") {\n return { configObj: null, rulesObj: null, safeToMutate: false };\n }\n\n for (const el of arrayExpr.elements ?? []) {\n if (!el || el.type !== \"ObjectExpression\") continue;\n\n const plugins = getObjectPropertyValue(el, \"plugins\");\n const rules = getObjectPropertyValue(el, \"rules\");\n\n const hasUilintPlugin =\n plugins?.type === \"ObjectExpression\" &&\n getObjectPropertyValue(plugins, \"uilint\") !== null;\n\n const uilintIds = collectUilintRuleIdsFromRulesObject(rules);\n const hasUilintRules = uilintIds.size > 0;\n\n if (!hasUilintPlugin && !hasUilintRules) continue;\n\n const safe =\n rules?.type === \"ObjectExpression\" && !hasSpreadProperties(rules);\n return { configObj: el, rulesObj: rules, safeToMutate: safe };\n }\n\n return { configObj: null, rulesObj: null, safeToMutate: false };\n}\n\nfunction collectTopLevelBindings(program: any): Set<string> {\n const names = new Set<string>();\n if (!program || program.type !== \"Program\") return names;\n\n for (const stmt of program.body ?? []) {\n if (stmt?.type === \"VariableDeclaration\") {\n for (const decl of stmt.declarations ?? []) {\n const id = decl?.id;\n if (id?.type === \"Identifier\" && typeof id.name === \"string\") {\n names.add(id.name);\n }\n }\n } else if (stmt?.type === \"FunctionDeclaration\") {\n if (stmt.id?.type === \"Identifier\" && typeof stmt.id.name === \"string\") {\n names.add(stmt.id.name);\n }\n }\n }\n return names;\n}\n\nfunction chooseUniqueIdentifier(base: string, used: Set<string>): string {\n if (!used.has(base)) return base;\n let i = 2;\n while (used.has(`${base}${i}`)) i++;\n return `${base}${i}`;\n}\n\n/**\n * Add imports for local rules from .uilint/rules/\n */\nfunction addLocalRuleImportsAst(\n mod: any,\n selectedRules: RuleMetadata[],\n configPath: string,\n rulesRoot: string,\n fileExtension: string = \".js\"\n): { importNames: Map<string, string>; changed: boolean } {\n const importNames = new Map<string, string>();\n let changed = false;\n\n // Calculate relative path from config file to .uilint/rules/\n const configDir = dirname(configPath);\n const rulesDir = join(rulesRoot, \".uilint\", \"rules\");\n const relativeRulesPath = relative(configDir, rulesDir).replace(/\\\\/g, \"/\");\n\n // Ensure it starts with ./ or ../ (note: `.foo` is NOT a valid relative import)\n const normalizedRulesPath =\n relativeRulesPath.startsWith(\"./\") || relativeRulesPath.startsWith(\"../\")\n ? relativeRulesPath\n : `./${relativeRulesPath}`;\n\n const used = collectTopLevelBindings(mod.$ast);\n\n for (const rule of selectedRules) {\n // Generate a safe import name (e.g., noArbitraryTailwindRule)\n const importName = chooseUniqueIdentifier(\n `${rule.id\n .replace(/-([a-z])/g, (_: string, c: string) => c.toUpperCase())\n .replace(/^./, (c: string) => c.toUpperCase())}Rule`,\n used\n );\n importNames.set(rule.id, importName);\n used.add(importName);\n\n // Add import statement\n const rulePath = `${normalizedRulesPath}/${rule.id}${fileExtension}`;\n mod.imports.$add({\n imported: \"default\",\n local: importName,\n from: rulePath,\n });\n changed = true;\n }\n\n return { importNames, changed };\n}\n\n/**\n * Add require statements for local rules (CommonJS)\n */\nfunction addLocalRuleRequiresAst(\n program: any,\n selectedRules: RuleMetadata[],\n configPath: string,\n rulesRoot: string,\n fileExtension: string = \".js\"\n): { importNames: Map<string, string>; changed: boolean } {\n const importNames = new Map<string, string>();\n let changed = false;\n\n if (!program || program.type !== \"Program\") {\n return { importNames, changed };\n }\n\n // Calculate relative path from config file to .uilint/rules/\n const configDir = dirname(configPath);\n const rulesDir = join(rulesRoot, \".uilint\", \"rules\");\n const relativeRulesPath = relative(configDir, rulesDir).replace(/\\\\/g, \"/\");\n\n // Ensure it starts with ./ or ../ (note: `.foo` is NOT a valid relative require)\n const normalizedRulesPath =\n relativeRulesPath.startsWith(\"./\") || relativeRulesPath.startsWith(\"../\")\n ? relativeRulesPath\n : `./${relativeRulesPath}`;\n\n const used = collectTopLevelBindings(program);\n\n for (const rule of selectedRules) {\n // Generate a safe import name\n const importName = chooseUniqueIdentifier(\n `${rule.id\n .replace(/-([a-z])/g, (_: string, c: string) => c.toUpperCase())\n .replace(/^./, (c: string) => c.toUpperCase())}Rule`,\n used\n );\n importNames.set(rule.id, importName);\n used.add(importName);\n\n // Add require statement\n const rulePath = `${normalizedRulesPath}/${rule.id}${fileExtension}`;\n const stmtMod = parseModule(\n `const ${importName} = require(\"${rulePath}\");`\n );\n const stmt = (stmtMod.$ast as any).body?.[0];\n if (stmt) {\n // Place after a leading \"use strict\" if present.\n let insertAt = 0;\n const first = program.body?.[0];\n if (\n first?.type === \"ExpressionStatement\" &&\n first.expression?.type === \"StringLiteral\" &&\n first.expression.value === \"use strict\"\n ) {\n insertAt = 1;\n }\n program.body.splice(insertAt, 0, stmt);\n changed = true;\n }\n }\n\n return { importNames, changed };\n}\n\nfunction appendUilintConfigBlockToArray(\n arrayExpr: any,\n selectedRules: RuleMetadata[],\n ruleImportNames: Map<string, string>\n): void {\n // Build plugin object with local rule imports\n const pluginRulesCode = Array.from(ruleImportNames.entries())\n .map(([ruleId, importName]) => ` \"${ruleId}\": ${importName},`)\n .join(\"\\n\");\n\n const rulesPropsCode = selectedRules\n .map((r) => {\n const ruleKey = `uilint/${r.id}`;\n const valueCode =\n r.defaultOptions && r.defaultOptions.length > 0\n ? `[\"${r.defaultSeverity}\", ...${JSON.stringify(\n r.defaultOptions,\n null,\n 2\n )}]`\n : `\"${r.defaultSeverity}\"`;\n return ` \"${ruleKey}\": ${valueCode},`;\n })\n .join(\"\\n\");\n\n const blockCode = `{\n files: [\n \"src/**/*.{js,jsx,ts,tsx}\",\n \"app/**/*.{js,jsx,ts,tsx}\",\n \"pages/**/*.{js,jsx,ts,tsx}\",\n ],\n plugins: {\n uilint: {\n rules: {\n${pluginRulesCode}\n },\n },\n },\n rules: {\n${rulesPropsCode}\n },\n }`;\n\n const objExpr = (parseExpression(blockCode) as any).$ast;\n arrayExpr.elements.push(objExpr);\n}\n\nfunction getUilintEslintConfigInfoFromSourceAst(source: string):\n | {\n info: UilintEslintConfigInfo;\n mod: any;\n arrayExpr: any;\n kind: \"esm\" | \"cjs\";\n }\n | { error: string } {\n try {\n const mod = parseModule(source);\n const found = findExportedConfigArrayExpression(mod);\n if (!found) {\n return {\n error:\n \"Could not locate an exported ESLint flat config array (expected `export default [...]`, `export default defineConfig([...])`, `module.exports = [...]`, or `module.exports = defineConfig([...])`).\",\n };\n }\n\n const configuredRuleIds = collectConfiguredUilintRuleIdsFromConfigArray(\n found.arrayExpr\n );\n const existingUilint = findExistingUilintRulesObject(found.arrayExpr);\n const configured =\n configuredRuleIds.size > 0 || existingUilint.configObj !== null;\n\n return {\n info: { configuredRuleIds, configured },\n mod,\n arrayExpr: found.arrayExpr,\n kind: found.kind,\n };\n } catch {\n return {\n error:\n \"Unable to parse ESLint config as JavaScript. Please update it manually or simplify the config so it can be safely auto-modified.\",\n };\n }\n}\n\nexport function getUilintEslintConfigInfoFromSource(\n source: string\n): UilintEslintConfigInfo {\n const ast = getUilintEslintConfigInfoFromSourceAst(source);\n if (\"error\" in ast) {\n // Fallback (best-effort) to string heuristics for scan-only scenarios.\n const configuredRuleIds = extractConfiguredUilintRuleIds(source);\n return {\n configuredRuleIds,\n configured: configuredRuleIds.size > 0,\n };\n }\n return ast.info;\n}\n\nfunction findEsmExportedConfigArrayStartIndex(source: string): number | null {\n // Supported:\n // - export default [ ... ]\n // - export default defineConfig([ ... ])\n const patterns: RegExp[] = [\n /export\\s+default\\s+\\[/,\n /export\\s+default\\s+defineConfig\\s*\\(\\s*\\[/,\n ];\n\n for (const re of patterns) {\n const m = source.match(re);\n if (!m || m.index === undefined) continue;\n return m.index + m[0].length;\n }\n\n return null;\n}\n\nfunction findCommonJsExportedConfigArrayStartIndex(\n source: string\n): number | null {\n // Supported:\n // - module.exports = [ ... ]\n // - module.exports = defineConfig([ ... ]) (best-effort)\n const patterns: RegExp[] = [\n /module\\.exports\\s*=\\s*\\[/,\n /module\\.exports\\s*=\\s*defineConfig\\s*\\(\\s*\\[/,\n ];\n\n for (const re of patterns) {\n const m = source.match(re);\n if (!m || m.index === undefined) continue;\n return m.index + m[0].length;\n }\n\n return null;\n}\n\n/**\n * Extract configured uilint rule IDs from source.\n * Matches keys like: \"uilint/no-arbitrary-tailwind\": \"error\"\n */\nfunction extractConfiguredUilintRuleIds(source: string): Set<string> {\n const ids = new Set<string>();\n const re = /[\"']uilint\\/([^\"']+)[\"']\\s*:/g;\n for (const m of source.matchAll(re)) {\n if (m[1]) ids.add(m[1]);\n }\n return ids;\n}\n\nfunction getMissingSelectedRules(\n selectedRules: RuleMetadata[],\n configuredIds: Set<string>\n): RuleMetadata[] {\n return selectedRules.filter((r) => !configuredIds.has(r.id));\n}\n\n/**\n * Get rules that exist but need updating (different options or severity)\n */\nfunction buildDesiredRuleValueExpression(rule: RuleMetadata): string {\n if (rule.defaultOptions && rule.defaultOptions.length > 0) {\n // Match the shape we generate elsewhere: [\"severity\", ...[options...]]\n return `[\"${rule.defaultSeverity}\", ...${JSON.stringify(\n rule.defaultOptions,\n null,\n 2\n )}]`;\n }\n return `\"${rule.defaultSeverity}\"`;\n}\n\nfunction collectUilintRuleValueNodesFromConfigArray(\n arrayExpr: any\n): Map<string, any> {\n const out = new Map<string, any>();\n if (!arrayExpr || arrayExpr.type !== \"ArrayExpression\") return out;\n\n for (const el of arrayExpr.elements ?? []) {\n if (!el || el.type !== \"ObjectExpression\") continue;\n const rules = getObjectPropertyValue(el, \"rules\");\n if (!rules || rules.type !== \"ObjectExpression\") continue;\n\n for (const prop of rules.properties ?? []) {\n if (!prop) continue;\n if (prop.type !== \"ObjectProperty\" && prop.type !== \"Property\") continue;\n const key = prop.key;\n if (!isStringLiteral(key)) continue;\n const k = key.value;\n if (typeof k !== \"string\" || !k.startsWith(\"uilint/\")) continue;\n const id = k.slice(\"uilint/\".length);\n // First occurrence wins; that's enough for detecting up-to-date configs.\n if (!out.has(id)) out.set(id, prop.value);\n }\n }\n\n return out;\n}\n\nfunction getRulesNeedingUpdate(\n selectedRules: RuleMetadata[],\n configuredIds: Set<string>,\n arrayExpr: any\n): RuleMetadata[] {\n // Only consider rules that are already configured, and only update if the\n // existing severity/options differ from what we would generate.\n const existingVals = collectUilintRuleValueNodesFromConfigArray(arrayExpr);\n\n return selectedRules.filter((r) => {\n if (!configuredIds.has(r.id)) return false;\n const existing = existingVals.get(r.id);\n if (!existing) return true;\n\n const desiredExpr = buildDesiredRuleValueExpression(r);\n const desiredAst = (parseExpression(desiredExpr) as any).$ast;\n return !astEquivalent(existing, desiredAst);\n });\n}\n\n/**\n * Generate a single rule config string\n */\nfunction generateSingleRuleConfig(rule: RuleMetadata): string {\n const ruleKey = `\"uilint/${rule.id}\"`;\n\n if (rule.defaultOptions && rule.defaultOptions.length > 0) {\n // Rule with options\n const optionsStr = JSON.stringify(rule.defaultOptions, null, 6)\n .split(\"\\n\")\n .join(\"\\n \");\n return ` ${ruleKey}: [\"${rule.defaultSeverity}\", ...${optionsStr}],`;\n } else {\n // Simple rule\n return ` ${ruleKey}: \"${rule.defaultSeverity}\",`;\n }\n}\n\n/**\n * Add the uilint import to the source if not present\n */\nfunction ensureUilintImport(source: string, isCommonJS: boolean): string {\n if (hasUilintImport(source)) {\n return source;\n }\n\n const importLine = isCommonJS\n ? `const uilint = require(\"uilint-eslint\");\\n`\n : `import uilint from \"uilint-eslint\";\\n`;\n\n // Find the last import/require statement and insert after it\n const header = source.slice(0, Math.min(source.length, 5000));\n const importRegex = isCommonJS\n ? /^(?:const|var|let)\\s+.*?=\\s*require\\([^)]+\\);?\\s*$/gm\n : /^import[\\s\\S]*?;\\s*$/gm;\n\n let lastImportEnd = -1;\n for (const m of header.matchAll(importRegex)) {\n lastImportEnd = (m.index ?? 0) + m[0].length;\n }\n\n if (lastImportEnd !== -1) {\n return (\n source.slice(0, lastImportEnd) +\n \"\\n\" +\n importLine +\n source.slice(lastImportEnd)\n );\n }\n\n // No imports found, add at the beginning\n return importLine + source;\n}\n\n/**\n * Generate the rules config object from selected rules\n */\nfunction generateRulesConfig(selectedRules: RuleMetadata[]): string {\n const lines: string[] = [];\n\n for (const rule of selectedRules) {\n const ruleKey = `\"uilint/${rule.id}\"`;\n\n if (rule.defaultOptions && rule.defaultOptions.length > 0) {\n // Rule with options\n const optionsStr = JSON.stringify(rule.defaultOptions, null, 6)\n .split(\"\\n\")\n .join(\"\\n \");\n lines.push(\n ` ${ruleKey}: [\"${rule.defaultSeverity}\", ...${optionsStr}],`\n );\n } else {\n // Simple rule\n lines.push(` ${ruleKey}: \"${rule.defaultSeverity}\",`);\n }\n }\n\n return lines.join(\"\\n\");\n}\n\nfunction detectIndent(source: string, index: number): string {\n const lineStart = source.lastIndexOf(\"\\n\", index);\n const start = lineStart === -1 ? 0 : lineStart + 1;\n const line = source.slice(start, index);\n const m = line.match(/^\\s*/);\n return m?.[0] ?? \"\";\n}\n\n/**\n * Insert missing uilint rule keys into an existing `rules: { ... }` object\n * that already contains at least one \"uilint/\" key.\n *\n * This is intentionally a best-effort string transform (no JS AST dependency).\n */\nfunction insertMissingRulesIntoExistingRulesObject(\n source: string,\n missingRules: RuleMetadata[]\n): string {\n if (missingRules.length === 0) return source;\n\n // Anchor on an existing uilint rule key, then look backwards for the\n // nearest `rules:` preceding it.\n const uilintKeyMatch = source.match(/[\"']uilint\\/[^\"']+[\"']\\s*:/);\n if (!uilintKeyMatch || uilintKeyMatch.index === undefined) return source;\n\n const uilintKeyIndex = uilintKeyMatch.index;\n const searchStart = Math.max(0, uilintKeyIndex - 4000);\n const before = source.slice(searchStart, uilintKeyIndex);\n const rulesKwIndexRel = before.lastIndexOf(\"rules\");\n if (rulesKwIndexRel === -1) return source;\n\n const rulesKwIndex = searchStart + rulesKwIndexRel;\n const braceOpenIndex = source.indexOf(\"{\", rulesKwIndex);\n if (braceOpenIndex === -1 || braceOpenIndex > uilintKeyIndex) return source;\n\n // Find the matching closing brace for the rules object.\n let depth = 0;\n let braceCloseIndex = -1;\n for (let i = braceOpenIndex; i < source.length; i++) {\n const ch = source[i];\n if (ch === \"{\") depth++;\n else if (ch === \"}\") {\n depth--;\n if (depth === 0) {\n braceCloseIndex = i;\n break;\n }\n }\n }\n if (braceCloseIndex === -1) return source;\n\n const rulesIndent = detectIndent(source, braceOpenIndex);\n const entryIndent = rulesIndent + \" \";\n const entryTextRaw = generateRulesConfig(missingRules);\n const entryText = entryTextRaw\n .split(\"\\n\")\n .map((l) => (l.trim().length === 0 ? l : entryIndent + l.trimStart()))\n .join(\"\\n\");\n\n const insertion =\n (source.slice(braceOpenIndex + 1, braceCloseIndex).trim().length === 0\n ? \"\\n\"\n : \"\\n\") +\n entryText +\n \"\\n\" +\n rulesIndent;\n\n return (\n source.slice(0, braceCloseIndex) + insertion + source.slice(braceCloseIndex)\n );\n}\n\n/**\n * Find the end of a rule value in the source code\n * Handles: \"error\", [\"error\"], [\"error\", {...}], [\"error\", ...[{...}]]\n */\nfunction findRuleValueEnd(source: string, startIndex: number): number {\n let pos = startIndex;\n let depth = 0;\n let inString = false;\n let stringChar = \"\";\n let foundArray = false;\n\n while (pos < source.length) {\n const ch = source[pos];\n const prevCh = pos > 0 ? source[pos - 1] : \"\";\n\n // Handle string literals\n if (!inString && (ch === '\"' || ch === \"'\")) {\n inString = true;\n stringChar = ch;\n } else if (inString && ch === stringChar && prevCh !== \"\\\\\") {\n inString = false;\n } else if (!inString) {\n // Track brackets/braces\n if (ch === \"[\") {\n depth++;\n foundArray = true;\n } else if (ch === \"]\") {\n depth--;\n if (depth === 0 && foundArray) {\n // Found the end of the array\n pos++;\n // Skip whitespace and include trailing comma if present\n while (pos < source.length && /\\s/.test(source[pos])) {\n pos++;\n }\n if (pos < source.length && source[pos] === \",\") {\n pos++;\n }\n return pos;\n }\n } else if (ch === \"{\" || ch === \"(\") {\n depth++;\n } else if (ch === \"}\" || ch === \")\") {\n depth--;\n } else if (!foundArray && depth === 0) {\n // Simple string value - ends at comma or closing brace\n if (ch === \",\" || ch === \"}\") {\n return pos + (ch === \",\" ? 1 : 0);\n }\n }\n }\n\n pos++;\n }\n\n return pos;\n}\n\n/**\n * Update existing uilint rule configurations with new options/severity\n *\n * This finds existing rule entries and replaces them with updated configurations.\n * Uses a more robust approach to handle multi-line rules with spread syntax.\n */\nfunction updateExistingRulesWithNewOptions(\n source: string,\n rulesToUpdate: RuleMetadata[]\n): string {\n if (rulesToUpdate.length === 0) return source;\n\n let updated = source;\n\n // Process rules in reverse order to avoid index shifting issues\n for (let i = rulesToUpdate.length - 1; i >= 0; i--) {\n const rule = rulesToUpdate[i]!;\n const ruleKeyPattern = new RegExp(\n `[\"']uilint/${rule.id.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\")}[\"']\\\\s*:`,\n \"g\"\n );\n\n // Find all occurrences (should only be one, but be safe)\n const matches: Array<{ index: number; length: number }> = [];\n let match;\n while ((match = ruleKeyPattern.exec(updated)) !== null) {\n if (match.index !== undefined) {\n matches.push({ index: match.index, length: match[0].length });\n }\n }\n\n // Process matches in reverse order\n for (let j = matches.length - 1; j >= 0; j--) {\n const keyMatch = matches[j]!;\n const keyStart = keyMatch.index;\n const keyEnd = keyStart + keyMatch.length;\n\n // Find the value start (after colon and whitespace)\n let valueStart = keyEnd;\n while (valueStart < updated.length && /\\s/.test(updated[valueStart])) {\n valueStart++;\n }\n\n // Find the value end\n const valueEnd = findRuleValueEnd(updated, valueStart);\n\n // Generate new rule config\n const newRuleConfig = generateSingleRuleConfig(rule);\n\n // Find the indentation of the rule key line\n const indent = detectIndent(updated, keyStart);\n\n // Replace the old rule with the new one\n const before = updated.slice(0, keyStart);\n const after = updated.slice(valueEnd);\n\n updated = before + newRuleConfig + \"\\n\" + indent + after;\n }\n }\n\n return updated;\n}\n\n/**\n * Inject uilint rules into the export default array\n */\nfunction injectUilintRules(\n source: string,\n selectedRules: RuleMetadata[]\n): { source: string; injected: boolean } {\n if (hasUilintRules(source)) {\n // Already has uilint rules - don't inject again\n return { source, injected: false };\n }\n\n const rulesConfig = generateRulesConfig(selectedRules);\n\n const configBlock = ` {\n files: [\n \"src/**/*.{js,jsx,ts,tsx}\",\n \"app/**/*.{js,jsx,ts,tsx}\",\n \"pages/**/*.{js,jsx,ts,tsx}\",\n ],\n plugins: { uilint: uilint },\n rules: {\n${rulesConfig}\n },\n },`;\n\n const arrayStart = findEsmExportedConfigArrayStartIndex(source);\n if (arrayStart === null) {\n return { source, injected: false };\n }\n\n const afterExport = source.slice(arrayStart);\n\n // Insert at the beginning of the array (after opening bracket)\n // Add a newline if the array doesn't start on a new line\n const needsNewline = !afterExport.trimStart().startsWith(\"\\n\");\n const insertion = needsNewline\n ? \"\\n\" + configBlock + \"\\n\"\n : configBlock + \"\\n\";\n\n return {\n source: source.slice(0, arrayStart) + insertion + source.slice(arrayStart),\n injected: true,\n };\n}\n\n/**\n * Inject uilint rules into the CommonJS export\n */\nfunction injectUilintRulesCommonJS(\n source: string,\n selectedRules: RuleMetadata[]\n): { source: string; injected: boolean } {\n if (hasUilintRules(source)) {\n return { source, injected: false };\n }\n\n const rulesConfig = generateRulesConfig(selectedRules);\n\n const configBlock = ` {\n files: [\n \"src/**/*.{js,jsx,ts,tsx}\",\n \"app/**/*.{js,jsx,ts,tsx}\",\n \"pages/**/*.{js,jsx,ts,tsx}\",\n ],\n plugins: { uilint: uilint },\n rules: {\n${rulesConfig}\n },\n },`;\n\n const arrayStart = findCommonJsExportedConfigArrayStartIndex(source);\n if (arrayStart === null) {\n return { source, injected: false };\n }\n\n const afterExport = source.slice(arrayStart);\n const needsNewline = !afterExport.trimStart().startsWith(\"\\n\");\n const insertion = needsNewline\n ? \"\\n\" + configBlock + \"\\n\"\n : configBlock + \"\\n\";\n\n return {\n source: source.slice(0, arrayStart) + insertion + source.slice(arrayStart),\n injected: true,\n };\n}\n\n/**\n * Install uilint-eslint into eslint config\n */\nexport async function installEslintPlugin(\n opts: InstallEslintPluginOptions\n): Promise<{\n configFile: string | null;\n modified: boolean;\n missingRuleIds: string[];\n configured: boolean;\n error?: string;\n}> {\n const configPath = findEslintConfigFile(opts.projectPath);\n\n if (!configPath) {\n return {\n configFile: null,\n modified: false,\n missingRuleIds: [],\n configured: false,\n };\n }\n\n const configFilename = getEslintConfigFilename(configPath);\n const original = readFileSync(configPath, \"utf-8\");\n const isCommonJS = configPath.endsWith(\".cjs\");\n\n const ast = getUilintEslintConfigInfoFromSourceAst(original);\n if (\"error\" in ast) {\n return {\n configFile: configFilename,\n modified: false,\n missingRuleIds: [],\n configured: false,\n error: ast.error,\n };\n }\n\n const { info, mod, arrayExpr, kind } = ast;\n const configuredIds = info.configuredRuleIds;\n\n const missingRules = getMissingSelectedRules(\n opts.selectedRules,\n configuredIds\n );\n const rulesToUpdate = getRulesNeedingUpdate(\n opts.selectedRules,\n configuredIds,\n arrayExpr\n );\n\n // Decide what rules to apply, respecting prompts.\n let rulesToApply: RuleMetadata[] = [];\n if (!info.configured) {\n rulesToApply = opts.selectedRules;\n } else {\n // When already configured, we only apply updates + missing rules.\n rulesToApply = [...missingRules, ...rulesToUpdate];\n if (missingRules.length > 0 && !opts.force) {\n const ok = await opts.confirmAddMissingRules?.(\n configFilename,\n missingRules\n );\n if (!ok) {\n return {\n configFile: configFilename,\n modified: false,\n missingRuleIds: missingRules.map((r) => r.id),\n configured: true,\n };\n }\n }\n }\n\n if (rulesToApply.length === 0) {\n return {\n configFile: configFilename,\n modified: false,\n missingRuleIds: missingRules.map((r) => r.id),\n configured: info.configured,\n };\n }\n\n let modifiedAst = false;\n\n // Check if .uilint/rules/ directory exists alongside this target package/app.\n // (Also allow a fallback to workspace root for backwards compatibility.)\n const localRulesDir = join(opts.projectPath, \".uilint\", \"rules\");\n const workspaceRoot = findWorkspaceRoot(opts.projectPath);\n const workspaceRulesDir = join(workspaceRoot, \".uilint\", \"rules\");\n\n const rulesRoot = existsSync(localRulesDir)\n ? opts.projectPath\n : workspaceRoot;\n\n // Always use local rules (they should have been copied by the plan phase)\n // For TypeScript configs, omit the extension (TypeScript will resolve .ts files)\n // For JavaScript configs, use .js extension\n // Note: We don't use .ts extension directly because it requires allowImportingTsExtensions\n const isTypeScriptConfig = configPath.endsWith(\".ts\");\n let fileExtension = isTypeScriptConfig ? \"\" : \".js\";\n\n let ruleImportNames: Map<string, string> | undefined;\n\n // Add imports for local rules\n if (kind === \"esm\") {\n const result = addLocalRuleImportsAst(\n mod,\n rulesToApply,\n configPath,\n rulesRoot,\n fileExtension\n );\n ruleImportNames = result.importNames;\n if (result.changed) modifiedAst = true;\n } else {\n const result = addLocalRuleRequiresAst(\n mod.$ast,\n rulesToApply,\n configPath,\n rulesRoot,\n fileExtension\n );\n ruleImportNames = result.importNames;\n if (result.changed) modifiedAst = true;\n }\n\n // Add config block with local rules\n if (ruleImportNames && ruleImportNames.size > 0) {\n appendUilintConfigBlockToArray(arrayExpr, rulesToApply, ruleImportNames);\n modifiedAst = true;\n }\n\n // Ensure uilint-eslint import for utilities (createRule, etc.)\n if (!info.configured) {\n if (kind === \"esm\") {\n // Import createRule utility from uilint-eslint\n mod.imports.$add({\n imported: \"createRule\",\n local: \"createRule\",\n from: \"uilint-eslint\",\n });\n modifiedAst = true;\n } else {\n // CommonJS: add require for createRule\n const stmtMod = parseModule(\n `const { createRule } = require(\"uilint-eslint\");`\n );\n const stmt = (stmtMod.$ast as any).body?.[0];\n if (stmt) {\n let insertAt = 0;\n const first = mod.$ast.body?.[0];\n if (\n first?.type === \"ExpressionStatement\" &&\n first.expression?.type === \"StringLiteral\" &&\n first.expression.value === \"use strict\"\n ) {\n insertAt = 1;\n }\n mod.$ast.body.splice(insertAt, 0, stmt);\n modifiedAst = true;\n }\n }\n }\n\n const updated = modifiedAst ? generateCode(mod).code : original;\n\n if (updated !== original) {\n writeFileSync(configPath, updated, \"utf-8\");\n return {\n configFile: configFilename,\n modified: true,\n missingRuleIds: missingRules.map((r) => r.id),\n configured: getUilintEslintConfigInfoFromSource(updated).configured,\n };\n }\n\n return {\n configFile: configFilename,\n modified: false,\n missingRuleIds: missingRules.map((r) => r.id),\n configured: getUilintEslintConfigInfoFromSource(updated).configured,\n };\n}\n\nexport interface UninstallEslintPluginOptions {\n projectPath: string;\n}\n\nexport interface UninstallEslintPluginResult {\n success: boolean;\n error?: string;\n modifiedFiles?: string[];\n}\n\n/**\n * Remove uilint-eslint rules from ESLint config\n *\n * This is a best-effort uninstall that:\n * 1. Removes uilint rule imports\n * 2. Removes uilint config blocks from the flat config array\n * 3. Removes uilint-eslint package import\n */\nexport async function uninstallEslintPlugin(\n options: UninstallEslintPluginOptions\n): Promise<UninstallEslintPluginResult> {\n const { projectPath } = options;\n\n const configPath = findEslintConfigFile(projectPath);\n if (!configPath) {\n return {\n success: true, // Nothing to uninstall\n modifiedFiles: [],\n };\n }\n\n try {\n const original = readFileSync(configPath, \"utf-8\");\n\n // Simple regex-based removal for now\n // Remove uilint rule imports (import { ... } from \"./uilint-rules/...\" or \"./.uilint/rules/...\")\n let updated = original.replace(\n /^import\\s+\\{[^}]*\\}\\s+from\\s+[\"'][^\"']*\\.uilint\\/rules[^\"']*[\"'];?\\s*$/gm,\n \"\"\n );\n\n // Remove default imports from .uilint/rules (e.g., import RuleName from \"./.uilint/rules/rule-name\")\n updated = updated.replace(\n /^import\\s+\\w+\\s+from\\s+[\"'][^\"']*\\.uilint\\/rules[^\"']*[\"'];?\\s*$/gm,\n \"\"\n );\n\n // Remove uilint-eslint import\n updated = updated.replace(\n /^import\\s+\\{[^}]*\\}\\s+from\\s+[\"']uilint-eslint[\"'];?\\s*$/gm,\n \"\"\n );\n\n // Remove createRule require\n updated = updated.replace(\n /^const\\s+\\{[^}]*createRule[^}]*\\}\\s*=\\s*require\\s*\\(\\s*[\"']uilint-eslint[\"']\\s*\\)\\s*;?\\s*$/gm,\n \"\"\n );\n\n // Remove uilint rules from rules objects (e.g., \"uilint/rule-name\": \"error\")\n updated = updated.replace(\n /[\"']uilint\\/[^\"']+[\"']\\s*:\\s*[\"'][^\"']+[\"']\\s*,?\\s*/g,\n \"\"\n );\n\n // Remove uilint rules array form (e.g., \"uilint/rule-name\": [\"error\", {}])\n updated = updated.replace(\n /[\"']uilint\\/[^\"']+[\"']\\s*:\\s*\\[[^\\]]*\\]\\s*,?\\s*/g,\n \"\"\n );\n\n // Remove entire uilint config block from flat config array\n // This matches: { plugins: { uilint: { rules: {...} } }, rules: {...} }\n updated = updated.replace(\n /\\{\\s*plugins:\\s*\\{\\s*uilint:\\s*\\{[^}]*\\}[^}]*\\}[^}]*rules:\\s*\\{[^}]*\\}[^}]*\\}\\s*,?\\s*/gs,\n \"\"\n );\n\n // Clean up empty lines\n updated = updated.replace(/\\n{3,}/g, \"\\n\\n\");\n\n if (updated !== original) {\n writeFileSync(configPath, updated, \"utf-8\");\n return {\n success: true,\n modifiedFiles: [configPath],\n };\n }\n\n return {\n success: true,\n modifiedFiles: [],\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : String(error),\n };\n }\n}\n\nexport interface UpdateRuleConfigResult {\n success: boolean;\n error?: string;\n}\n\nexport interface RuleConfigFromFile {\n severity: \"error\" | \"warn\" | \"off\";\n options?: Record<string, unknown>;\n}\n\n/**\n * Extract severity from an AST node representing a rule value.\n * Handles both formats:\n * - Simple string: \"error\"\n * - Array: [\"error\", { ...options }]\n */\nfunction extractSeverityFromValueNode(\n valueNode: any\n): \"error\" | \"warn\" | \"off\" | null {\n if (!valueNode) return null;\n\n // Simple string format: \"error\"\n if (isStringLiteral(valueNode)) {\n const val = valueNode.value;\n if (val === \"error\" || val === \"warn\" || val === \"off\") {\n return val;\n }\n return null;\n }\n\n // Array format: [\"error\", { ...options }]\n if (valueNode.type === \"ArrayExpression\") {\n const firstEl = valueNode.elements?.[0];\n if (firstEl && isStringLiteral(firstEl)) {\n const val = firstEl.value;\n if (val === \"error\" || val === \"warn\" || val === \"off\") {\n return val;\n }\n }\n }\n\n return null;\n}\n\n/**\n * Extract options object from an AST node representing a rule value in array format.\n * Returns undefined if not in array format or no options present.\n */\nfunction extractOptionsFromValueNode(\n valueNode: any\n): Record<string, unknown> | undefined {\n if (!valueNode || valueNode.type !== \"ArrayExpression\") return undefined;\n\n const elements = valueNode.elements ?? [];\n if (elements.length < 2) return undefined;\n\n const optionsNode = elements[1];\n if (!optionsNode || optionsNode.type !== \"ObjectExpression\") return undefined;\n\n // Convert AST object to plain JS object\n try {\n const obj: Record<string, unknown> = {};\n for (const prop of optionsNode.properties ?? []) {\n if (!prop || (prop.type !== \"ObjectProperty\" && prop.type !== \"Property\"))\n continue;\n\n const key = prop.key;\n let keyName: string | null = null;\n if (key?.type === \"Identifier\") {\n keyName = key.name;\n } else if (isStringLiteral(key)) {\n keyName = key.value;\n }\n if (!keyName) continue;\n\n const val = prop.value;\n if (isStringLiteral(val)) {\n obj[keyName] = val.value;\n } else if (\n val?.type === \"NumericLiteral\" ||\n (val?.type === \"Literal\" && typeof val.value === \"number\")\n ) {\n obj[keyName] = val.value;\n } else if (\n val?.type === \"BooleanLiteral\" ||\n (val?.type === \"Literal\" && typeof val.value === \"boolean\")\n ) {\n obj[keyName] = val.value;\n } else if (val?.type === \"ArrayExpression\") {\n // Simple array of literals\n const arr: unknown[] = [];\n for (const el of val.elements ?? []) {\n if (isStringLiteral(el)) arr.push(el.value);\n else if (\n el?.type === \"NumericLiteral\" ||\n (el?.type === \"Literal\" && typeof el.value === \"number\")\n )\n arr.push(el.value);\n else if (\n el?.type === \"BooleanLiteral\" ||\n (el?.type === \"Literal\" && typeof el.value === \"boolean\")\n )\n arr.push(el.value);\n }\n obj[keyName] = arr;\n }\n }\n return Object.keys(obj).length > 0 ? obj : undefined;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Read current rule configurations from an ESLint config file.\n * Uses magicast to parse the config and extract uilint rule severities and options.\n *\n * @param configPath Path to the ESLint config file\n * @returns Map of ruleId (without \"uilint/\" prefix) to { severity, options }\n */\nexport function readRuleConfigsFromConfig(\n configPath: string\n): Map<string, RuleConfigFromFile> {\n const configs = new Map<string, RuleConfigFromFile>();\n\n try {\n const source = readFileSync(configPath, \"utf-8\");\n const mod = parseModule(source);\n const found = findExportedConfigArrayExpression(mod);\n\n if (!found) {\n return configs;\n }\n\n const { arrayExpr } = found;\n const valueNodes = collectUilintRuleValueNodesFromConfigArray(arrayExpr);\n\n for (const [ruleId, valueNode] of valueNodes) {\n const severity = extractSeverityFromValueNode(valueNode);\n if (severity) {\n const options = extractOptionsFromValueNode(valueNode);\n configs.set(ruleId, { severity, options });\n }\n }\n } catch (error) {\n console.error(\"[eslint-config-inject] Failed to read rule configs:\", error);\n }\n\n return configs;\n}\n\n/**\n * Find a rule property node in the config array by rule ID.\n * Returns the property node and its parent rules object.\n */\nfunction findRulePropertyInConfigArray(\n arrayExpr: any,\n ruleId: string\n): { prop: any; rulesObj: any } | null {\n const fullRuleKey = `uilint/${ruleId}`;\n\n if (!arrayExpr || arrayExpr.type !== \"ArrayExpression\") return null;\n\n for (const el of arrayExpr.elements ?? []) {\n if (!el || el.type !== \"ObjectExpression\") continue;\n const rules = getObjectPropertyValue(el, \"rules\");\n if (!rules || rules.type !== \"ObjectExpression\") continue;\n\n for (const prop of rules.properties ?? []) {\n if (!prop) continue;\n if (prop.type !== \"ObjectProperty\" && prop.type !== \"Property\") continue;\n const key = prop.key;\n if (!isStringLiteral(key)) continue;\n if (key.value === fullRuleKey) {\n return { prop, rulesObj: rules };\n }\n }\n }\n\n return null;\n}\n\n/**\n * Update the severity of a single uilint rule in the ESLint config file.\n * Uses magicast for proper AST-based editing.\n *\n * Handles both formats:\n * - Simple: \"uilint/rule-name\": \"error\"\n * - With options: \"uilint/rule-name\": [\"error\", { ... }]\n *\n * Only updates existing rules - returns error if rule not found.\n */\nexport function updateRuleSeverityInConfig(\n configPath: string,\n ruleId: string,\n severity: \"error\" | \"warn\" | \"off\"\n): UpdateRuleConfigResult {\n try {\n const source = readFileSync(configPath, \"utf-8\");\n const mod = parseModule(source);\n const found = findExportedConfigArrayExpression(mod);\n\n if (!found) {\n return {\n success: false,\n error: \"Could not parse ESLint config array\",\n };\n }\n\n const { arrayExpr } = found;\n const ruleInfo = findRulePropertyInConfigArray(arrayExpr, ruleId);\n\n if (!ruleInfo) {\n return {\n success: false,\n error: `Rule \"uilint/${ruleId}\" not found in config. Use 'uilint install' to add new rules.`,\n };\n }\n\n const { prop } = ruleInfo;\n const valueNode = prop.value;\n\n // Update the severity based on the format\n if (isStringLiteral(valueNode)) {\n // Simple string format - replace the value\n valueNode.value = severity;\n } else if (valueNode?.type === \"ArrayExpression\") {\n // Array format - update the first element\n const firstEl = valueNode.elements?.[0];\n if (firstEl && isStringLiteral(firstEl)) {\n firstEl.value = severity;\n } else {\n // Create a new string literal for severity\n const severityNode = (parseExpression(`\"${severity}\"`) as any).$ast;\n if (valueNode.elements && valueNode.elements.length > 0) {\n valueNode.elements[0] = severityNode;\n } else {\n valueNode.elements = [severityNode];\n }\n }\n } else {\n return {\n success: false,\n error: `Rule \"uilint/${ruleId}\" has unexpected format`,\n };\n }\n\n const updated = generateCode(mod).code;\n writeFileSync(configPath, updated, \"utf-8\");\n return { success: true };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : String(error),\n };\n }\n}\n\n/**\n * Update the severity AND options of a single uilint rule in the ESLint config file.\n * Uses magicast for proper AST-based editing.\n *\n * Converts the rule to array format: [\"severity\", { ...options }]\n *\n * Only updates existing rules - returns error if rule not found.\n */\nexport function updateRuleConfigInConfig(\n configPath: string,\n ruleId: string,\n severity: \"error\" | \"warn\" | \"off\",\n options: Record<string, unknown>\n): UpdateRuleConfigResult {\n try {\n const source = readFileSync(configPath, \"utf-8\");\n const mod = parseModule(source);\n const found = findExportedConfigArrayExpression(mod);\n\n if (!found) {\n return {\n success: false,\n error: \"Could not parse ESLint config array\",\n };\n }\n\n const { arrayExpr } = found;\n const ruleInfo = findRulePropertyInConfigArray(arrayExpr, ruleId);\n\n if (!ruleInfo) {\n return {\n success: false,\n error: `Rule \"uilint/${ruleId}\" not found in config. Use 'uilint install' to add new rules.`,\n };\n }\n\n const { prop } = ruleInfo;\n\n // Build the new value as array format: [\"severity\", { ...options }]\n const optionsJson = JSON.stringify(options);\n const newValueExpr = `[\"${severity}\", ${optionsJson}]`;\n const newValueNode = (parseExpression(newValueExpr) as any).$ast;\n\n // Replace the value\n prop.value = newValueNode;\n\n const updated = generateCode(mod).code;\n writeFileSync(configPath, updated, \"utf-8\");\n return { success: true };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : String(error),\n };\n }\n}\n"],"mappings":"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","names":["intro","outro","cancel","cancel","note","log","select","confirm","multiselect","join","existsSync","readFileSync","join","dirname","p","program","dirname","join","readFileSync","join","existsSync"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/utils/package-manager.ts","../src/commands/install/constants.ts","../src/utils/skill-loader.ts"],"sourcesContent":["import { existsSync } from \"fs\";\nimport { spawn } from \"child_process\";\nimport { dirname, join } from \"path\";\n\nexport type PackageManager = \"pnpm\" | \"yarn\" | \"npm\" | \"bun\";\n\n/**\n * Detect which package manager a project uses by looking for lockfiles.\n * Walks up the directory tree to support monorepos.\n */\nexport function detectPackageManager(projectPath: string): PackageManager {\n // Monorepo-friendly detection: walk up to find the lockfile/workspace marker.\n let dir = projectPath;\n for (;;) {\n // pnpm\n if (existsSync(join(dir, \"pnpm-lock.yaml\"))) return \"pnpm\";\n if (existsSync(join(dir, \"pnpm-workspace.yaml\"))) return \"pnpm\";\n\n // yarn\n if (existsSync(join(dir, \"yarn.lock\"))) return \"yarn\";\n\n // bun\n if (existsSync(join(dir, \"bun.lockb\"))) return \"bun\";\n if (existsSync(join(dir, \"bun.lock\"))) return \"bun\";\n\n // npm\n if (existsSync(join(dir, \"package-lock.json\"))) return \"npm\";\n\n const parent = dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n\n // Default: npm (best-effort)\n return \"npm\";\n}\n\nfunction spawnAsync(\n command: string,\n args: string[],\n cwd: string\n): Promise<void> {\n return new Promise((resolve, reject) => {\n const child = spawn(command, args, {\n cwd,\n stdio: \"inherit\",\n shell: process.platform === \"win32\",\n });\n\n child.on(\"error\", reject);\n child.on(\"close\", (code) => {\n if (code === 0) resolve();\n else\n reject(new Error(`${command} ${args.join(\" \")} exited with ${code}`));\n });\n });\n}\n\nexport async function installDependencies(\n pm: PackageManager,\n projectPath: string,\n packages: string[]\n): Promise<void> {\n if (!packages.length) return;\n\n switch (pm) {\n case \"pnpm\":\n await spawnAsync(\"pnpm\", [\"add\", ...packages], projectPath);\n return;\n case \"yarn\":\n await spawnAsync(\"yarn\", [\"add\", ...packages], projectPath);\n return;\n case \"bun\":\n await spawnAsync(\"bun\", [\"add\", ...packages], projectPath);\n return;\n case \"npm\":\n default:\n await spawnAsync(\"npm\", [\"install\", \"--save\", ...packages], projectPath);\n return;\n }\n}\n","/**\n * Constants for the install command\n *\n * Contains file content templates for commands.\n */\n\n// ============================================================================\n// Cursor Commands\n// ============================================================================\n\nexport const GENSTYLEGUIDE_COMMAND_MD = `# React Style Guide Generator\n\nAnalyze the React UI codebase to produce a **prescriptive, semantic** style guide. Focus on consistency, intent, and relationships—not specific values.\n\n## Philosophy\n\n1. **Identify the intended architecture** from the best patterns in use\n2. **Prescribe semantic rules** — about consistency and relationships, not pixels\n3. **Stay general** — \"primary buttons should be visually consistent\" not \"buttons use px-4\"\n4. **Focus on intent** — what should FEEL the same, not what values to use\n\n## Analysis Steps\n\n### 1. Detect the Stack\n- Framework: Next.js (App Router? Pages?), Vite, CRA\n- Component system: shadcn, MUI, Chakra, Radix, custom\n- Styling: Tailwind, CSS Modules, styled-components\n- Forms: react-hook-form, Formik, native\n- State: React context, Zustand, Redux, Jotai\n\n### 2. Identify Best Patterns\nExamine the **best-written** components. Look at:\n- \\`components/ui/*\\` — the design system\n- Recently modified files — current standards\n- Shared layouts — structural patterns\n\n### 3. Infer Visual Hierarchy & Intent\nUnderstand the design language:\n- What distinguishes primary vs secondary actions?\n- How is visual hierarchy established?\n- What creates consistency across similar elements?\n\n## Output Format\n\nGenerate at \\`<nextjs app root>/.uilint/styleguide.md\\`:\n\\`\\`\\`yaml\n# Stack\nframework: \nstyling: \ncomponents: \ncomponent_path: \nforms: \n\n# Component Usage (MUST use these)\nuse:\n buttons: \n inputs: \n modals: \n cards: \n feedback: \n icons: \n links: \n\n# Semantic Rules (consistency & relationships)\nsemantics:\n hierarchy:\n - <e.g., \"primary actions must be visually distinct from secondary\">\n - <e.g., \"destructive actions should be visually cautionary\">\n - <e.g., \"page titles should be visually heavier than section titles\">\n consistency:\n - <e.g., \"all primary buttons should share the same visual weight\">\n - <e.g., \"form inputs should have uniform height and padding\">\n - <e.g., \"card padding should be consistent across the app\">\n - <e.g., \"interactive elements should have consistent hover/focus states\">\n spacing:\n - <e.g., \"use the spacing scale — no arbitrary values\">\n - <e.g., \"related elements should be closer than unrelated\">\n - <e.g., \"section spacing should be larger than element spacing\">\n layout:\n - <e.g., \"use gap for sibling spacing, not margin\">\n - <e.g., \"containers should have consistent max-width and padding\">\n\n# Patterns (structural, not values)\npatterns:\n forms: <e.g., \"FormField + Controller + zod schema\">\n conditionals: <e.g., \"cn() for class merging\">\n loading: <e.g., \"Skeleton for content, Spinner for actions\">\n errors: <e.g., \"ErrorBoundary at route, inline for forms\">\n responsive: <e.g., \"mobile-first, standard breakpoints only\">\n\n# Component Authoring\nauthoring:\n - <e.g., \"forwardRef for interactive components\">\n - <e.g., \"variants via CVA or component props, not className overrides\">\n - <e.g., \"extract when used 2+ times\">\n - <e.g., \"'use client' only when needed\">\n\n# Forbidden\nforbidden:\n - <e.g., \"inline style={{}}\">\n - <e.g., \"raw HTML elements when component exists\">\n - <e.g., \"arbitrary values — use scale\">\n - <e.g., \"className overrides that break visual consistency\">\n - <e.g., \"one-off spacing that doesn't match siblings\">\n\n# Legacy (if migration in progress)\nlegacy:\n - <e.g., \"old: CSS modules → new: Tailwind\">\n - <e.g., \"old: Formik → new: react-hook-form\">\n\n# Conventions\nconventions:\n - \n - \n - \n\\`\\`\\`\n\n## Rules\n\n- **Semantic over specific**: \"consistent padding\" not \"p-4\"\n- **Relationships over absolutes**: \"heavier than\" not \"font-bold\"\n- **Intent over implementation**: \"visually distinct\" not \"blue background\"\n- **Prescriptive**: Define target state, not current state\n- **Terse**: No prose. Fragments and short phrases only.\n- **Actionable**: Every rule should be human-verifiable\n- **Omit if N/A**: Skip sections that don't apply\n- **Max 5 items** per section — highest impact only\n`;\n\n","/**\n * Skill Loader Utility\n *\n * Loads Agent Skill files from the bundled skills directory for installation\n * into user projects. Skills follow the Agent Skills specification\n * (agentskills.io).\n */\n\nimport { readFileSync, readdirSync, statSync, existsSync } from \"fs\";\nimport { join, dirname, relative } from \"path\";\nimport { fileURLToPath } from \"url\";\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = dirname(__filename);\n\n/**\n * Represents a file in a skill directory\n */\nexport interface SkillFile {\n /** Relative path within the skill directory */\n relativePath: string;\n /** File content */\n content: string;\n}\n\n/**\n * Represents a complete skill ready for installation\n */\nexport interface Skill {\n /** Skill name (directory name) */\n name: string;\n /** All files in the skill */\n files: SkillFile[];\n}\n\n/**\n * Get the path to the bundled skills directory\n */\nfunction getSkillsDir(): string {\n // In development: packages/uilint/skills/\n // In production (installed): node_modules/uilint/dist/ -> ../skills/\n const devPath = join(__dirname, \"..\", \"..\", \"skills\");\n const prodPath = join(__dirname, \"..\", \"skills\");\n\n if (existsSync(devPath)) {\n return devPath;\n }\n if (existsSync(prodPath)) {\n return prodPath;\n }\n\n throw new Error(\n \"Could not find skills directory. This is a bug in uilint installation.\"\n );\n}\n\n/**\n * Recursively collect all files in a directory\n */\nfunction collectFiles(dir: string, baseDir: string): SkillFile[] {\n const files: SkillFile[] = [];\n const entries = readdirSync(dir);\n\n for (const entry of entries) {\n const fullPath = join(dir, entry);\n const stat = statSync(fullPath);\n\n if (stat.isDirectory()) {\n files.push(...collectFiles(fullPath, baseDir));\n } else if (stat.isFile()) {\n const relativePath = relative(baseDir, fullPath);\n const content = readFileSync(fullPath, \"utf-8\");\n files.push({ relativePath, content });\n }\n }\n\n return files;\n}\n\n/**\n * Load a specific skill by name\n */\nexport function loadSkill(name: string): Skill {\n const skillsDir = getSkillsDir();\n const skillDir = join(skillsDir, name);\n\n if (!existsSync(skillDir)) {\n throw new Error(`Skill \"${name}\" not found in ${skillsDir}`);\n }\n\n const skillMdPath = join(skillDir, \"SKILL.md\");\n if (!existsSync(skillMdPath)) {\n throw new Error(`Skill \"${name}\" is missing SKILL.md`);\n }\n\n const files = collectFiles(skillDir, skillDir);\n\n return { name, files };\n}\n\n/**\n * Load all available skills\n */\nexport function loadAllSkills(): Skill[] {\n const skillsDir = getSkillsDir();\n const entries = readdirSync(skillsDir);\n const skills: Skill[] = [];\n\n for (const entry of entries) {\n const skillDir = join(skillsDir, entry);\n const stat = statSync(skillDir);\n\n if (stat.isDirectory()) {\n const skillMdPath = join(skillDir, \"SKILL.md\");\n if (existsSync(skillMdPath)) {\n skills.push(loadSkill(entry));\n }\n }\n }\n\n return skills;\n}\n\n/**\n * Get the list of available skill names\n */\nexport function getAvailableSkillNames(): string[] {\n const skillsDir = getSkillsDir();\n const entries = readdirSync(skillsDir);\n const names: string[] = [];\n\n for (const entry of entries) {\n const skillDir = join(skillsDir, entry);\n const stat = statSync(skillDir);\n\n if (stat.isDirectory()) {\n const skillMdPath = join(skillDir, \"SKILL.md\");\n if (existsSync(skillMdPath)) {\n names.push(entry);\n }\n }\n }\n\n return names;\n}\n"],"mappings":"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","names":["existsSync","join","dirname"]}