@santi020k/eslint-config-basic 3.2.0 → 3.4.0

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.
Files changed (42) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/dist/agent-skill-generator.d.ts +7 -18
  3. package/dist/agent-skill-generator.js +54 -18
  4. package/dist/agent-skill-generator.js.map +1 -0
  5. package/dist/cli-advanced.d.ts +7 -0
  6. package/dist/cli-advanced.js +74 -15
  7. package/dist/cli-advanced.js.map +1 -0
  8. package/dist/cli-migration.js +22 -20
  9. package/dist/cli-migration.js.map +1 -0
  10. package/dist/cli-package-manager.d.ts +10 -0
  11. package/dist/cli-package-manager.js +135 -0
  12. package/dist/cli-package-manager.js.map +1 -0
  13. package/dist/cli-preset.d.ts +74 -2
  14. package/dist/cli-preset.js +253 -6
  15. package/dist/cli-preset.js.map +1 -0
  16. package/dist/cli-workflows.js +1 -0
  17. package/dist/cli-workflows.js.map +1 -0
  18. package/dist/cli.d.ts +1 -1
  19. package/dist/cli.js +461 -176
  20. package/dist/cli.js.map +1 -0
  21. package/dist/define-config-metadata.js +2 -4
  22. package/dist/define-config-metadata.js.map +1 -0
  23. package/dist/frameworks.d.ts +1 -0
  24. package/dist/frameworks.js +2 -1
  25. package/dist/frameworks.js.map +1 -0
  26. package/dist/index.d.ts +13 -2
  27. package/dist/index.js +83 -22
  28. package/dist/index.js.map +1 -0
  29. package/dist/integrations.js +1 -0
  30. package/dist/integrations.js.map +1 -0
  31. package/dist/optional-package-errors.js +1 -0
  32. package/dist/optional-package-errors.js.map +1 -0
  33. package/dist/recommended.d.ts +6 -3
  34. package/dist/recommended.js +3 -1
  35. package/dist/recommended.js.map +1 -0
  36. package/dist/resolvers.js +1 -0
  37. package/dist/resolvers.js.map +1 -0
  38. package/dist/tailwind.d.ts +3 -1
  39. package/dist/tailwind.js +115 -13
  40. package/dist/tailwind.js.map +1 -0
  41. package/package.json +25 -25
  42. package/IMPROVEMENTS.md +0 -270
package/CHANGELOG.md CHANGED
@@ -1,5 +1,42 @@
1
1
  # @santi020k/eslint-config-basic
2
2
 
3
+ ## 3.4.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Keep TypeScript 6 declaration inference for JavaScript ESLint configs portable
8
+ under pnpm by returning a package-owned public config-array type. Annotate the
9
+ recommended entry point and verify packed consumers do not expose internal
10
+ `typescript-eslint` dependency paths or report TS2883.
11
+
12
+ ## 3.3.0
13
+
14
+ ### Minor Changes
15
+
16
+ - [#108](https://github.com/santi020k/eslint-config-basic/pull/108) [`66cee8e`](https://github.com/santi020k/eslint-config-basic/commit/66cee8e41ac22b14e8afa91f1f77587f08bc414f) Thanks [@santi020k](https://github.com/santi020k)! - Add source-aware preset adoption reports with grouped lint debt, a non-writing
17
+ autofix preview, and a TypeScript compiler preflight that prioritizes root
18
+ module-resolution failures. Enhance complexity explanations with live
19
+ rule-specific diagnostics and source locations.
20
+
21
+ Add structured per-project Doctor activation reports covering installed,
22
+ detected, and enabled frameworks and feature packs, TypeScript mode, tsconfig,
23
+ ignores, Tailwind entry-point policy, and reasons optional packages remain
24
+ inactive. Improve compatibility, install, and Doctor repair flows for pnpm
25
+ catalogs, npm aliases, ESM-only manifests, package-manager-specific installs,
26
+ Node engine ranges, recommended re-exports, and preset-implied category packs.
27
+
28
+ Improve automatic project scoping for declaration-only TypeScript packages,
29
+ shared Astro sources, generated Next.js declarations, CLI entry points, and
30
+ package-local Tailwind CSS graphs, including Tailwind v4 custom utilities.
31
+
32
+ ### Patch Changes
33
+
34
+ - [#108](https://github.com/santi020k/eslint-config-basic/pull/108) [`66cee8e`](https://github.com/santi020k/eslint-config-basic/commit/66cee8e41ac22b14e8afa91f1f77587f08bc414f) Thanks [@santi020k](https://github.com/santi020k)! - Make packed zero-config installs behave consistently across npm, pnpm, Yarn,
35
+ and Bun by ensuring the eagerly loaded TypeScript parser has its runtime.
36
+ - Updated dependencies [[`66cee8e`](https://github.com/santi020k/eslint-config-basic/commit/66cee8e41ac22b14e8afa91f1f77587f08bc414f), [`66cee8e`](https://github.com/santi020k/eslint-config-basic/commit/66cee8e41ac22b14e8afa91f1f77587f08bc414f)]:
37
+ - @santi020k/eslint-config-typescript@3.1.2
38
+ - @santi020k/eslint-config-core@3.1.1
39
+
3
40
  ## 3.2.0
4
41
 
5
42
  ### Minor Changes
@@ -21,6 +21,7 @@ interface EslintConfigFeatures {
21
21
  extensions: string[];
22
22
  formats: string[];
23
23
  frameworks: string[];
24
+ ignores: string[];
24
25
  libraries: string[];
25
26
  /** The lint command found in the project's package.json scripts, or a sensible default */
26
27
  lintCommand: string;
@@ -76,7 +77,11 @@ declare const AGENT_TARGETS: AgentTarget[];
76
77
  *
77
78
  * Returns `null` when no config file is found or it cannot be imported.
78
79
  */
79
- declare const analyzeEslintConfig: (cwd: string) => Promise<EslintConfigFeatures | null>;
80
+ interface AnalyzeEslintConfigOptions {
81
+ projectPath: string;
82
+ projectPaths: string[];
83
+ }
84
+ declare const analyzeEslintConfig: (cwd: string, options?: AnalyzeEslintConfigOptions) => Promise<EslintConfigFeatures | null>;
80
85
  /**
81
86
  * Builds the skill document body from the project's {@link EslintConfigFeatures}.
82
87
  * Four format variants are produced:
@@ -87,22 +92,6 @@ declare const analyzeEslintConfig: (cwd: string) => Promise<EslintConfigFeatures
87
92
  * - `plain` — pure Markdown, no front-matter (Claude Code, Copilot, Aider, Gemini, Cline, Roo Code)
88
93
  */
89
94
  declare const generateSkillContent: (features: EslintConfigFeatures, format: AgentTarget["format"]) => string;
90
- /**
91
- * Detects which AI agent folders exist in the project, reads the actual
92
- * `eslint.config.js` to understand what is configured, and writes a tailored
93
- * ESLint standards skill file into each found folder.
94
- *
95
- * Falls back to package.json auto-detection when the config file cannot be
96
- * dynamically imported (e.g. when running outside a built environment).
97
- *
98
- * @example
99
- * ```ts
100
- * import { generateAgentSkills } from '@santi020k/eslint-config-basic/agent'
101
- *
102
- * const result = await generateAgentSkills({ cwd: process.cwd() })
103
- * process.stdout.write(`Written to: ${result.written}\n`)
104
- * ```
105
- */
106
95
  declare const generateAgentSkills: (opts?: GenerateSkillOptions) => Promise<GenerateSkillResult>;
107
96
  interface HandleGenerateSkillFlags {
108
97
  /** Check mode — report stale files and set a non-zero exit code, write nothing */
@@ -114,4 +103,4 @@ interface HandleGenerateSkillFlags {
114
103
  }
115
104
  declare const handleGenerateSkill: (cwd?: string, force?: boolean, flags?: HandleGenerateSkillFlags) => Promise<void>;
116
105
 
117
- export { AGENT_TARGETS, type AgentTarget, ESLINT_CONFIG_FILENAMES, type EslintConfigFeatures, type GenerateSkillOptions, type GenerateSkillResult, type HandleGenerateSkillFlags, analyzeEslintConfig, generateAgentSkills, generateSkillContent, handleGenerateSkill };
106
+ export { AGENT_TARGETS, type AgentTarget, type AnalyzeEslintConfigOptions, ESLINT_CONFIG_FILENAMES, type EslintConfigFeatures, type GenerateSkillOptions, type GenerateSkillResult, type HandleGenerateSkillFlags, analyzeEslintConfig, generateAgentSkills, generateSkillContent, handleGenerateSkill };
@@ -257,6 +257,34 @@ const collectTokens = (configs) => {
257
257
  }
258
258
  return tokens;
259
259
  };
260
+ const getConfigFilePatterns = (entry) => {
261
+ if (!entry || typeof entry !== "object") return [];
262
+ const { files, ignores } = entry;
263
+ const filesArray = Array.isArray(files) ? files : [];
264
+ const ignoresArray = Array.isArray(ignores) ? ignores : [];
265
+ return [
266
+ ...filesArray,
267
+ ...ignoresArray
268
+ ].flat(Number.POSITIVE_INFINITY).filter((value) => typeof value === "string" && !value.startsWith("!"));
269
+ };
270
+ const normalizeProjectPattern = (pattern) => pattern.replace(/^\.\//, "");
271
+ const isProjectPattern = (pattern, projectPath) => {
272
+ const normalizedProjectPath = projectPath.replace(/^\.\//, "").replace(/\/$/, "");
273
+ const normalizedPattern = normalizeProjectPattern(pattern);
274
+ return normalizedPattern === normalizedProjectPath || normalizedPattern.startsWith(`${normalizedProjectPath}/`);
275
+ };
276
+ const scopeConfigsToProject = (configs, projectPath, projectPaths) => configs.filter((entry) => {
277
+ const patterns = getConfigFilePatterns(entry);
278
+ if (patterns.length === 0) return true;
279
+ const scopedProjectPaths = projectPaths.filter((path) => path !== ".").filter((path) => patterns.some((pattern) => isProjectPattern(pattern, path)));
280
+ if (scopedProjectPaths.length === 0) return true;
281
+ return projectPath !== "." && scopedProjectPaths.some((path) => projectPath === path || projectPath.startsWith(`${path}/`));
282
+ });
283
+ const collectIgnores = (configs) => [...new Set(configs.flatMap((entry) => {
284
+ if (!entry || typeof entry !== "object") return [];
285
+ const ignores = entry.ignores;
286
+ return Array.isArray(ignores) ? ignores.filter((value) => typeof value === "string") : [];
287
+ }))];
260
288
  const recordFeature = (features, category, label) => {
261
289
  if (category === "typescript") {
262
290
  features.typescript = true;
@@ -290,6 +318,7 @@ const extractFeatures = (configs, lintCommand, configFile) => {
290
318
  extensions: [],
291
319
  formats: [],
292
320
  frameworks: [],
321
+ ignores: collectIgnores(configs),
293
322
  libraries: [],
294
323
  lintCommand,
295
324
  source: "config-file",
@@ -333,7 +362,7 @@ const detectLintCommand = (cwd) => {
333
362
  }
334
363
  return "npx eslint .";
335
364
  };
336
- const analyzeEslintConfig = async (cwd) => {
365
+ const analyzeEslintConfig = async (cwd, options) => {
337
366
  const configPath = findEslintConfig(cwd);
338
367
  if (!configPath) return null;
339
368
  const lintCommand = detectLintCommand(cwd);
@@ -347,7 +376,8 @@ const analyzeEslintConfig = async (cwd) => {
347
376
  configs = await raw;
348
377
  }
349
378
  if (!Array.isArray(configs)) return null;
350
- return extractFeatures(configs, lintCommand, configPath);
379
+ const analyzedConfigs = options ? scopeConfigsToProject(configs, options.projectPath, options.projectPaths) : configs;
380
+ return extractFeatures(analyzedConfigs, lintCommand, configPath);
351
381
  } catch {
352
382
  return null;
353
383
  }
@@ -361,12 +391,13 @@ const featuresFromDetection = (cwd) => {
361
391
  extensions: (opts.extensions ?? []).map(toFeatureLabel),
362
392
  formats: (opts.formats ?? []).map(toFeatureLabel),
363
393
  frameworks,
394
+ ignores: opts.ignores ?? [],
364
395
  libraries: (opts.libraries ?? []).map(toFeatureLabel),
365
396
  lintCommand,
366
397
  source: "detection-fallback",
367
398
  testing: (opts.testing ?? []).map(toFeatureLabel),
368
399
  tools: (opts.tools ?? []).map(toFeatureLabel),
369
- typescript: opts.typescript === true
400
+ typescript: Boolean(opts.typescript)
370
401
  };
371
402
  };
372
403
  const buildSummaryLines = (features) => {
@@ -638,12 +669,28 @@ const handleEslintMcpConfig = (cwd, force, check) => {
638
669
  `);
639
670
  return "written";
640
671
  };
672
+ const processAgentTarget = (target, cwd, features, force, check) => {
673
+ const agentFolder = join(cwd, target.markerFolder);
674
+ if (!existsSync(agentFolder)) return void 0;
675
+ const subdir = target.skillSubdir === "." ? agentFolder : join(agentFolder, target.skillSubdir);
676
+ const filePath = join(subdir, target.skillFile);
677
+ const content = generateSkillContent(features, target.format);
678
+ if (check) {
679
+ const upToDate = existsSync(filePath) && readFileSync(filePath, "utf-8") === content;
680
+ return { filePath, result: upToDate ? "skipped" : "stale" };
681
+ }
682
+ return {
683
+ filePath,
684
+ result: writeSkillFile(filePath, content, force) ? "written" : "skipped"
685
+ };
686
+ };
641
687
  const generateAgentSkills = async (opts = {}) => {
642
688
  const { check, createAgentsMd, cwd, force, withEslintMcp } = resolveSkillOptions(opts);
643
689
  const written = [];
644
690
  const skipped = [];
645
691
  const stale = [];
646
- const features = await analyzeEslintConfig(cwd) ?? featuresFromDetection(cwd);
692
+ const detectedFeatures = await analyzeEslintConfig(cwd);
693
+ const features = detectedFeatures ?? featuresFromDetection(cwd);
647
694
  const plainBody = generateSkillContent(features, "plain");
648
695
  const recordGuardedResult = (filePath, result) => {
649
696
  switch (result) {
@@ -667,20 +714,8 @@ const generateAgentSkills = async (opts = {}) => {
667
714
  recordGuardedResult(eslintMcpPath, handleEslintMcpConfig(cwd, force, check));
668
715
  }
669
716
  for (const target of AGENT_TARGETS) {
670
- const agentFolder = join(cwd, target.markerFolder);
671
- if (!existsSync(agentFolder)) continue;
672
- const subdir = target.skillSubdir === "." ? agentFolder : join(agentFolder, target.skillSubdir);
673
- const filePath = join(subdir, target.skillFile);
674
- const content = generateSkillContent(features, target.format);
675
- if (check) {
676
- const upToDate = existsSync(filePath) && readFileSync(filePath, "utf-8") === content;
677
- if (upToDate) skipped.push(filePath);
678
- else stale.push(filePath);
679
- continue;
680
- }
681
- const didWrite = writeSkillFile(filePath, content, force);
682
- if (didWrite) written.push(filePath);
683
- else skipped.push(filePath);
717
+ const processed = processAgentTarget(target, cwd, features, force, check);
718
+ if (processed) recordGuardedResult(processed.filePath, processed.result);
684
719
  }
685
720
  return { skipped, stale, written };
686
721
  };
@@ -746,3 +781,4 @@ export {
746
781
  generateSkillContent,
747
782
  handleGenerateSkill
748
783
  };
784
+ //# sourceMappingURL=agent-skill-generator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/agent-skill-generator.ts"],"sourcesContent":["/* eslint-disable no-console -- this module owns the generate-skill CLI output */\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'\nimport { join, resolve } from 'node:path'\nimport { pathToFileURL } from 'node:url'\n\nimport { detectProjectOptions } from './index.js'\n\nexport const ESLINT_CONFIG_FILENAMES = [\n 'eslint.config.js',\n 'eslint.config.mjs',\n 'eslint.config.cjs',\n 'eslint.config.ts',\n 'eslint.config.mts',\n 'eslint.config.cts'\n] as const\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\nexport interface AgentTarget {\n\n /** Format variant used when generating content */\n format: 'cursor' | 'frontmatter' | 'kiro' | 'plain'\n\n /** Human-readable label for logging */\n label: string\n\n /** Folder that must exist in cwd to be considered \"present\" */\n markerFolder: string\n\n /** File name for the generated skill */\n skillFile: string\n\n /** Subdirectory inside the agent folder where the skill file is placed */\n skillSubdir: string\n}\n\n/**\n * Features extracted from the project's actual `eslint.config.js`.\n * All arrays hold display-friendly labels (e.g. `'TypeScript'`, `'React'`).\n */\nexport interface EslintConfigFeatures {\n\n /** Path to the config file that was loaded, or null when falling back to detection */\n configFile: null | string\n extensions: string[]\n formats: string[]\n frameworks: string[]\n ignores: string[]\n libraries: string[]\n\n /** The lint command found in the project's package.json scripts, or a sensible default */\n lintCommand: string\n\n /** Whether features came from the real config file or from package.json detection */\n source: 'config-file' | 'detection-fallback'\n\n testing: string[]\n\n tools: string[]\n\n typescript: boolean\n}\n\nexport interface GenerateSkillOptions {\n\n /**\n * Check mode — compare existing skill files against freshly generated\n * content without writing anything. Stale or missing files are reported\n * in {@link GenerateSkillResult.stale}.\n * @default false\n */\n check?: boolean\n\n /**\n * Create a root `AGENTS.md` when it does not exist yet, instead of only\n * updating an existing one.\n * @default false\n */\n createAgentsMd?: boolean\n\n /** Working directory — defaults to process.cwd() */\n cwd?: string\n\n /**\n * Overwrite files that already exist.\n * @default false\n */\n force?: boolean\n\n /**\n * Add the official `eslint --mcp` server to the project root `.mcp.json`.\n * Existing unrelated MCP server entries are preserved.\n * @default false\n */\n withEslintMcp?: boolean\n}\n\nexport interface GenerateSkillResult {\n skipped: string[]\n\n /** Files that are out of date (or missing) — only populated in check mode */\n stale: string[]\n\n written: string[]\n}\n\n// ─── Known agent targets ──────────────────────────────────────────────────────\n\n/**\n * All AI coding-assistant agent folders that are probed for.\n * Add new entries here as new agents emerge — the generator picks them up\n * automatically on the next run.\n */\nexport const AGENT_TARGETS: AgentTarget[] = [\n {\n format: 'frontmatter',\n label: '.agent (generic skill format)',\n markerFolder: '.agent',\n skillFile: 'SKILL.md',\n skillSubdir: 'skills/eslint-standards'\n },\n {\n format: 'frontmatter',\n label: '.agents (generic skill format)',\n markerFolder: '.agents',\n skillFile: 'SKILL.md',\n skillSubdir: 'skills/eslint-standards'\n },\n {\n format: 'plain',\n label: 'Claude Code (.claude/commands)',\n markerFolder: '.claude',\n skillFile: 'eslint.md',\n skillSubdir: 'commands'\n },\n {\n format: 'cursor',\n label: 'Cursor (.cursor/rules)',\n markerFolder: '.cursor',\n skillFile: 'eslint-standards.mdc',\n skillSubdir: 'rules'\n },\n {\n format: 'frontmatter',\n label: 'Windsurf (.windsurf/rules)',\n markerFolder: '.windsurf',\n skillFile: 'eslint-standards.md',\n skillSubdir: 'rules'\n },\n {\n format: 'plain',\n label: 'Copilot (.copilot)',\n markerFolder: '.copilot',\n skillFile: 'eslint-standards.md',\n skillSubdir: 'instructions'\n },\n {\n format: 'plain',\n label: 'Aider (.aider)',\n markerFolder: '.aider',\n skillFile: 'eslint-standards.md',\n skillSubdir: '.'\n },\n {\n format: 'plain',\n label: 'Gemini (.gemini)',\n markerFolder: '.gemini',\n skillFile: 'SKILL.md',\n skillSubdir: 'skills/eslint-standards'\n },\n {\n format: 'plain',\n label: 'Cline (.clinerules)',\n markerFolder: '.clinerules',\n skillFile: 'eslint-standards.md',\n skillSubdir: '.'\n },\n {\n format: 'plain',\n label: 'Roo Code (.roo/rules)',\n markerFolder: '.roo',\n skillFile: 'eslint-standards.md',\n skillSubdir: 'rules'\n },\n {\n format: 'kiro',\n label: 'Kiro (.kiro/steering)',\n markerFolder: '.kiro',\n skillFile: 'eslint-standards.md',\n skillSubdir: 'steering'\n }\n]\n\n// ─── Config-file analysis ─────────────────────────────────────────────────────\n\n/**\n * Maps substrings found in flat-config entry `name` fields (or plugin keys /\n * rule namespaces) to the human-readable feature label that appears in the\n * generated skill.\n *\n * Each entry is [pattern, category, label]:\n * - pattern — substring to search for in config names, plugin keys, or rule namespaces\n * - category — which feature bucket to place the label into\n * - label — display string written to the skill file\n */\ntype FeatureCategory = 'extensions' | 'formats' | 'frameworks' | 'libraries' | 'testing' | 'tools' | 'typescript'\n\nconst FEATURE_MAP: readonly [pattern: string, category: FeatureCategory, label: string][] = [\n // TypeScript\n ['eslint-config-typescript/', 'typescript', 'TypeScript'],\n ['@typescript-eslint', 'typescript', 'TypeScript'],\n\n // Frameworks\n ['eslint-config-react/', 'frameworks', 'React'],\n ['eslint-config-next/', 'frameworks', 'Next.js'],\n ['eslint-config-vue/', 'frameworks', 'Vue'],\n ['eslint-config-svelte/', 'frameworks', 'Svelte'],\n ['eslint-config-astro/', 'frameworks', 'Astro'],\n ['eslint-config-angular/', 'frameworks', 'Angular'],\n ['eslint-config-nest/', 'frameworks', 'NestJS'],\n ['eslint-config-hono/', 'frameworks', 'Hono'],\n ['eslint-config-expo/', 'frameworks', 'Expo'],\n ['eslint-config-qwik/', 'frameworks', 'Qwik'],\n ['eslint-config-react-router/', 'frameworks', 'React Router'],\n ['eslint-config-solid/', 'frameworks', 'SolidJS'],\n ['eslint-config-nuxt/', 'frameworks', 'Nuxt'],\n ['eslint-config-lit/', 'frameworks', 'Lit'],\n ['eslint-config-tanstack-start/', 'frameworks', 'TanStack Start'],\n ['eslint-config-preact/', 'frameworks', 'Preact'],\n ['eslint-config-slidev/', 'frameworks', 'Slidev'],\n ['eslint-config-vite/', 'frameworks', 'Vite'],\n\n // Extensions\n ['eslint-config-integrations/astro-doctor', 'extensions', 'Astro Doctor'],\n ['astro-doctor/', 'extensions', 'Astro Doctor'],\n\n // Testing\n ['integrations/vitest', 'testing', 'Vitest'],\n ['integrations/jest', 'testing', 'Jest'],\n ['eslint-config-integrations/jest-dom', 'testing', 'Jest DOM'],\n ['integrations/playwright', 'testing', 'Playwright'],\n ['integrations/cypress', 'testing', 'Cypress'],\n ['integrations/testing-library', 'testing', 'Testing Library'],\n\n // Tools\n ['eslint-config/prettier', 'tools', 'Prettier'],\n ['integrations/cspell', 'tools', 'CSpell'],\n ['eslint-config-integrations/jsdoc', 'tools', 'JSDoc'],\n ['integrations/swagger', 'tools', 'Swagger'],\n ['eslint-config-integrations/command', 'tools', 'Command'],\n ['eslint-config-integrations/docker', 'tools', 'Docker'],\n ['eslint-config-integrations/github-actions', 'tools', 'GitHub Actions'],\n ['eslint-config-integrations/nx', 'tools', 'Nx'],\n ['integrations/pnpm/', 'tools', 'pnpm'],\n\n // Libraries\n ['eslint-config-integrations/ai-sdk', 'libraries', 'AI SDK'],\n ['eslint-config-integrations/mcp', 'libraries', 'MCP'],\n ['eslint-config-integrations/mastra', 'libraries', 'Mastra'],\n ['eslint-config-integrations/openai-agents', 'libraries', 'OpenAI Agents'],\n ['eslint-config-integrations/langchain', 'libraries', 'LangChain'],\n ['eslint-config-integrations/llamaindex', 'libraries', 'LlamaIndex'],\n ['santi020k/tailwind/', 'libraries', 'Tailwind CSS'],\n ['integrations/i18next', 'libraries', 'i18next'],\n ['integrations/stencil', 'libraries', 'Stencil'],\n ['storybook:recommended', 'libraries', 'Storybook'],\n ['eslint-config-integrations/tanstack-query', 'libraries', 'TanStack Query'],\n ['eslint-config-integrations/tanstack-router', 'libraries', 'TanStack Router'],\n ['eslint-config-integrations/autogen', 'libraries', 'Autogen'],\n ['eslint-config-integrations/google-genai', 'libraries', 'Google GenAI'],\n ['eslint-config-integrations/drizzle', 'libraries', 'Drizzle ORM'],\n ['eslint-config-integrations/prisma', 'libraries', 'Prisma'],\n ['eslint-config-integrations/typeorm', 'libraries', 'TypeORM'],\n ['eslint-config-integrations/mikro-orm', 'libraries', 'MikroORM'],\n ['eslint-config-integrations/sequelize', 'libraries', 'Sequelize'],\n ['eslint-config-integrations/zod', 'libraries', 'Zod'],\n ['eslint-config-turbo/', 'libraries', 'Turbo'],\n\n // Formats\n ['integrations/css/', 'formats', 'CSS'],\n ['integrations/html/', 'formats', 'HTML'],\n ['eslint-config-integrations/package-json', 'formats', 'Package JSON'],\n ['integrations/graphql/', 'formats', 'GraphQL'],\n ['integrations/yaml/', 'formats', 'YAML'],\n ['integrations/jsonc/', 'formats', 'JSONC'],\n ['integrations/markdown', 'formats', 'Markdown'],\n ['eslint-config-mdx/', 'formats', 'MDX'],\n ['integrations/toml/', 'formats', 'TOML'],\n\n // Extensions\n ['eslint-config/unicorn', 'extensions', 'Unicorn'],\n ['eslint-config/sonarjs', 'extensions', 'SonarJS'],\n ['eslint-config-integrations/security', 'extensions', 'Security'],\n ['eslint-config-integrations/perfectionist', 'extensions', 'Perfectionist'],\n ['integrations/regexp', 'extensions', 'Regexp'],\n ['eslint-config/best-practices', 'extensions', 'Best Practices'],\n ['eslint-config-integrations/a11y/', 'extensions', 'Accessibility (a11y)'],\n ['eslint-config-integrations/biome', 'extensions', 'Biome'],\n ['eslint-config-integrations/import-boundaries', 'extensions', 'Import Boundaries'],\n ['integrations/compat/', 'extensions', 'Browser Compat'],\n ['integrations/de-morgan/', 'extensions', 'De Morgan'],\n ['integrations/depend/', 'extensions', 'Depend'],\n ['integrations/node/', 'extensions', 'Node.js'],\n ['eslint-config-integrations/no-only-tests', 'extensions', 'No Only Tests'],\n ['integrations/oxlint', 'extensions', 'Oxlint']\n]\n\ninterface RawFlatConfigEntry {\n files?: unknown\n ignores?: unknown\n name?: unknown\n plugins?: unknown\n rules?: unknown\n}\n\nconst DETECTED_FRAMEWORK_LABELS = new Map<string, string>([\n ['angular', 'Angular'],\n ['astro', 'Astro'],\n ['expo', 'Expo'],\n ['hono', 'Hono'],\n ['lit', 'Lit'],\n ['nest', 'NestJS'],\n ['next', 'Next.js'],\n ['nuxt', 'Nuxt'],\n ['preact', 'Preact'],\n ['qwik', 'Qwik'],\n ['react', 'React'],\n ['react-router', 'React Router'],\n ['slidev', 'Slidev'],\n ['solid', 'SolidJS'],\n ['svelte', 'Svelte'],\n ['tanstack-start', 'TanStack Start'],\n ['vite', 'Vite'],\n ['vue', 'Vue']\n])\n\nconst FEATURE_LABELS = new Map<string, string>([\n ['best-practices', 'Best Practices'],\n ['compat', 'Browser Compat'],\n ['cspell', 'CSpell'],\n ['css', 'CSS'],\n ['cypress', 'Cypress'],\n ['de-morgan', 'De Morgan'],\n ['depend', 'Depend'],\n ['graphql', 'GraphQL'],\n ['html', 'HTML'],\n ['i18next', 'i18next'],\n ['jest', 'Jest'],\n ['jsdoc', 'JSDoc'],\n ['jsonc', 'JSONC'],\n ['markdown', 'Markdown'],\n ['mdx', 'MDX'],\n ['node', 'Node.js'],\n ['oxlint', 'Oxlint'],\n ['perfectionist', 'Perfectionist'],\n ['playwright', 'Playwright'],\n ['pnpm', 'pnpm'],\n ['prettier', 'Prettier'],\n ['regexp', 'Regexp'],\n ['security', 'Security'],\n ['sonarjs', 'SonarJS'],\n ['stencil', 'Stencil'],\n ['storybook', 'Storybook'],\n ['swagger', 'Swagger'],\n ['tailwind', 'Tailwind CSS'],\n ['tanstack-query', 'TanStack Query'],\n ['tanstack-router', 'TanStack Router'],\n ['testing-library', 'Testing Library'],\n ['toml', 'TOML'],\n ['unicorn', 'Unicorn'],\n ['vitest', 'Vitest'],\n ['yaml', 'YAML']\n])\n\nconst toFeatureLabel = (value: string): string => FEATURE_LABELS.get(value) ?? value\n\nconst extractRuleNamespaces = (rules: object): string[] => Object.keys(rules)\n .filter(key => key.includes('/'))\n .map(key => key.slice(0, key.indexOf('/')))\n\n/**\n * Extracts all searchable tokens from a flat-config array:\n * config entry names, plugin keys, and rule namespace prefixes.\n */\nconst collectTokens = (configs: unknown[]): string[] => {\n const tokens: string[] = []\n\n for (const entry of configs) {\n if (!entry || typeof entry !== 'object') continue\n\n const cfg = entry as RawFlatConfigEntry\n\n // Config entry name\n if (typeof cfg.name === 'string') {\n tokens.push(cfg.name)\n }\n\n // Plugin keys (e.g. '@typescript-eslint', 'react', 'vue', …)\n if (cfg.plugins && typeof cfg.plugins === 'object') {\n tokens.push(...Object.keys(cfg.plugins))\n }\n\n // Rule namespace prefixes (e.g. '@typescript-eslint/no-…' → '@typescript-eslint')\n if (cfg.rules && typeof cfg.rules === 'object') {\n tokens.push(...extractRuleNamespaces(cfg.rules))\n }\n }\n\n return tokens\n}\n\nconst getConfigFilePatterns = (entry: unknown): string[] => {\n if (!entry || typeof entry !== 'object') return []\n\n const { files, ignores } = entry as RawFlatConfigEntry\n const filesArray = Array.isArray(files) ? (files as unknown[]) : []\n const ignoresArray = Array.isArray(ignores) ? (ignores as unknown[]) : []\n\n return [\n ...filesArray,\n ...ignoresArray\n ].flat(Number.POSITIVE_INFINITY)\n .filter((value): value is string => typeof value === 'string' && !value.startsWith('!'))\n}\n\nconst normalizeProjectPattern = (pattern: string): string => pattern.replace(/^\\.\\//, '')\n\nconst isProjectPattern = (pattern: string, projectPath: string): boolean => {\n const normalizedProjectPath = projectPath.replace(/^\\.\\//, '').replace(/\\/$/, '')\n const normalizedPattern = normalizeProjectPattern(pattern)\n\n return normalizedPattern === normalizedProjectPath || normalizedPattern.startsWith(`${normalizedProjectPath}/`)\n}\n\nconst scopeConfigsToProject = (\n configs: unknown[],\n projectPath: string,\n projectPaths: string[]\n): unknown[] => configs.filter(entry => {\n const patterns = getConfigFilePatterns(entry)\n\n if (patterns.length === 0) return true\n\n const scopedProjectPaths = projectPaths\n .filter(path => path !== '.')\n .filter(path => patterns.some(pattern => isProjectPattern(pattern, path)))\n\n if (scopedProjectPaths.length === 0) return true\n\n return projectPath !== '.' && scopedProjectPaths.some(path => (\n projectPath === path || projectPath.startsWith(`${path}/`)\n ))\n})\n\nconst collectIgnores = (configs: unknown[]): string[] => [...new Set(configs.flatMap(entry => {\n if (!entry || typeof entry !== 'object') return []\n\n const ignores = (entry as RawFlatConfigEntry).ignores\n\n return Array.isArray(ignores) ? ignores.filter((value): value is string => typeof value === 'string') : []\n}))]\n\nconst recordFeature = (features: EslintConfigFeatures, category: string, label: string): void => {\n if (category === 'typescript') {\n features.typescript = true\n\n return\n }\n\n switch (category) {\n case 'extensions':\n features.extensions.push(label)\n\n break\n\n case 'formats':\n features.formats.push(label)\n\n break\n\n case 'frameworks':\n features.frameworks.push(label)\n\n break\n\n case 'libraries':\n features.libraries.push(label)\n\n break\n\n case 'testing':\n features.testing.push(label)\n\n break\n\n case 'tools':\n features.tools.push(label)\n\n break\n }\n}\n\n/**\n * Derives {@link EslintConfigFeatures} from the raw flat-config array loaded\n * from the user's `eslint.config.js`.\n */\nconst extractFeatures = (\n configs: unknown[],\n lintCommand: string,\n configFile: string\n): EslintConfigFeatures => {\n const tokens = collectTokens(configs)\n\n const features: EslintConfigFeatures = {\n configFile,\n extensions: [],\n formats: [],\n frameworks: [],\n ignores: collectIgnores(configs),\n libraries: [],\n lintCommand,\n source: 'config-file',\n testing: [],\n tools: [],\n typescript: false\n }\n\n const seen = new Set<string>()\n\n for (const [pattern, category, label] of FEATURE_MAP) {\n if (seen.has(label)) continue\n\n const matched = tokens.some(t => t.includes(pattern))\n\n if (!matched) continue\n\n seen.add(label)\n\n recordFeature(features, category, label)\n }\n\n return features\n}\n\n/**\n * Tries each candidate config filename in order and returns the first one\n * found, or `null` when none exist.\n */\nconst findEslintConfig = (cwd: string): null | string => {\n for (const name of ESLINT_CONFIG_FILENAMES) {\n const p = join(cwd, name)\n\n if (existsSync(p)) return p\n }\n\n return null\n}\n\n/**\n * Reads the user's `package.json` to find the lint script (e.g. `pnpm run lint`).\n * Falls back to `npm run lint` when no script or lockfile can be identified.\n */\nconst detectLintCommand = (cwd: string): string => {\n // Prefer the package manager implied by lockfiles\n let pkgManager = 'npm'\n\n if (existsSync(join(cwd, 'pnpm-lock.yaml')) || existsSync(join(cwd, 'pnpm-workspace.yaml'))) {\n pkgManager = 'pnpm'\n } else if (existsSync(join(cwd, 'yarn.lock'))) {\n pkgManager = 'yarn'\n } else if (existsSync(join(cwd, 'bun.lockb')) || existsSync(join(cwd, 'bun.lock'))) {\n pkgManager = 'bun'\n }\n\n // Check that a `lint` script actually exists in package.json\n const pkgPath = join(cwd, 'package.json')\n\n try {\n if (existsSync(pkgPath)) {\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8')) as {\n scripts?: Record<string, string>\n }\n\n if (pkg.scripts?.lint) return `${pkgManager} run lint`\n }\n } catch {\n // ignore — fall through to default\n }\n\n return 'npx eslint .'\n}\n\n/**\n * Loads the user's `eslint.config.js` (or `.mjs` / `.cjs`) via dynamic\n * `import()` and derives features from the resulting flat-config array.\n *\n * Returns `null` when no config file is found or it cannot be imported.\n */\nexport interface AnalyzeEslintConfigOptions {\n projectPath: string\n projectPaths: string[]\n}\n\nexport const analyzeEslintConfig = async (\n cwd: string,\n options?: AnalyzeEslintConfigOptions\n): Promise<EslintConfigFeatures | null> => {\n const configPath = findEslintConfig(cwd)\n\n if (!configPath) return null\n\n const lintCommand = detectLintCommand(cwd)\n\n try {\n // Dynamic import — the user's config is an ES module exporting the flat array\n const mod = await import(pathToFileURL(resolve(configPath)).href) as { default?: unknown }\n const raw = (mod.default ?? mod) as unknown\n // The export can be a direct array or a Promise (async configs)\n let configs: unknown = null\n\n if (Array.isArray(raw)) {\n configs = raw\n } else if (raw instanceof Promise) {\n configs = await raw\n }\n\n if (!Array.isArray(configs)) return null\n\n const analyzedConfigs = options ?\n scopeConfigsToProject(configs, options.projectPath, options.projectPaths) :\n configs\n\n return extractFeatures(analyzedConfigs, lintCommand, configPath)\n } catch {\n return null\n }\n}\n\n// ─── Fallback: derive features from detectProjectOptions ─────────────────────\n\n/**\n * Converts the output of `detectProjectOptions()` into `EslintConfigFeatures`\n * when the config file cannot be loaded.\n */\nconst featuresFromDetection = (cwd: string): EslintConfigFeatures => {\n const opts = detectProjectOptions(cwd)\n const lintCommand = detectLintCommand(cwd)\n const frameworks = (opts.detectedFrameworks ?? []).map(f => DETECTED_FRAMEWORK_LABELS.get(f) ?? f)\n\n return {\n configFile: null,\n extensions: (opts.extensions ?? []).map(toFeatureLabel),\n formats: (opts.formats ?? []).map(toFeatureLabel),\n frameworks,\n ignores: opts.ignores ?? [],\n libraries: (opts.libraries ?? []).map(toFeatureLabel),\n lintCommand,\n source: 'detection-fallback',\n testing: (opts.testing ?? []).map(toFeatureLabel),\n tools: (opts.tools ?? []).map(toFeatureLabel),\n typescript: Boolean(opts.typescript)\n }\n}\n\n// ─── Skill content generation ─────────────────────────────────────────────────\n\nconst buildSummaryLines = (features: EslintConfigFeatures): string[] => {\n const { extensions, formats, frameworks, libraries, testing, tools, typescript } = features\n const lines: string[] = []\n\n lines.push(`- **TypeScript**: ${typescript ? 'enabled' : 'disabled'}`)\n\n if (frameworks.length > 0) lines.push(`- **Frameworks**: ${frameworks.join(', ')}`)\n\n if (testing.length > 0) lines.push(`- **Testing**: ${testing.join(', ')}`)\n\n if (tools.length > 0) lines.push(`- **Tools**: ${tools.join(', ')}`)\n\n if (libraries.length > 0) lines.push(`- **Libraries**: ${libraries.join(', ')}`)\n\n if (formats.length > 0) lines.push(`- **Formats**: ${formats.join(', ')}`)\n\n if (extensions.length > 0) lines.push(`- **Extensions**: ${extensions.join(', ')}`)\n\n return lines\n}\n\nconst buildFrameworkSections = (frameworks: string[]): string[] => {\n const sections: string[] = []\n\n if (frameworks.some(f => f === 'React' || f === 'Next.js')) {\n sections.push(`\n### React / JSX\n\n- Prefer function components over class components\n- Hooks must follow the Rules of Hooks (no conditional calls, no loops)\n- Avoid inline arrow functions in JSX props where it hurts readability\n- Use \\`key\\` props when rendering lists\n`)\n }\n\n if (frameworks.includes('Next.js')) {\n sections.push(`\n### Next.js\n\n- Follow App Router conventions (Server Components by default, \\`\"use client\"\\` when needed)\n- Avoid \\`<img>\\` — use \\`next/image\\`\n- Avoid \\`<a>\\` for internal navigation — use \\`next/link\\`\n`)\n }\n\n if (frameworks.includes('Vue')) {\n sections.push(`\n### Vue\n\n- Prefer Composition API (\\`<script setup>\\`) over Options API\n- Use single-word or multi-word component names consistently\n`)\n }\n\n if (frameworks.includes('Svelte')) {\n sections.push(`\n### Svelte\n\n- Virtual \\`*.svelte/*.ts\\` files are handled by the ESLint config — do not manually adjust \\`allowDefaultProject\\`\n`)\n }\n\n if (frameworks.includes('Astro')) {\n sections.push(`\n### Astro\n\n- Virtual \\`*.astro/*.ts\\` files are handled by the ESLint config\n- Prefer Astro components over framework components when no interactivity is needed\n`)\n }\n\n if (frameworks.includes('Angular')) {\n sections.push(`\n### Angular\n\n- Follow the Angular style guide component, directive, and service naming conventions\n- Use standalone components by default; avoid NgModule unless required\n`)\n }\n\n return sections\n}\n\nconst wrapWithFormat = (body: string, format: AgentTarget['format']): string => {\n if (format === 'frontmatter') {\n return `---\nname: eslint-standards\ndescription: >\n Code quality standards enforced by @santi020k/eslint-config-basic. Follow these\n conventions whenever writing or editing code in this project.\ntrigger: always_on\n---\n\n${body}`\n }\n\n if (format === 'cursor') {\n return `---\ndescription: >\n ESLint code standards for this project. Apply these conventions when writing\n or editing any source file.\nglobs:\n - \"**/*.ts\"\n - \"**/*.tsx\"\n - \"**/*.js\"\n - \"**/*.jsx\"\n - \"**/*.vue\"\n - \"**/*.svelte\"\n - \"**/*.astro\"\nalwaysApply: true\n---\n\n${body}`\n }\n\n if (format === 'kiro') {\n return `---\ninclusion: always\n---\n\n${body}`\n }\n\n return body\n}\n\n/**\n * Builds the skill document body from the project's {@link EslintConfigFeatures}.\n * Four format variants are produced:\n *\n * - `frontmatter` — YAML front-matter + Markdown (`.agent`, `.agents`, `.windsurf`)\n * - `cursor` — Cursor MDC front-matter + Markdown\n * - `kiro` — Kiro steering front-matter (`inclusion: always`) + Markdown\n * - `plain` — pure Markdown, no front-matter (Claude Code, Copilot, Aider, Gemini, Cline, Roo Code)\n */\nexport const generateSkillContent = (\n features: EslintConfigFeatures,\n format: AgentTarget['format']\n): string => {\n const { frameworks, libraries, lintCommand, testing, tools, typescript } = features\n // ── Summary ────────────────────────────────────────────────────────────────\n const summaryLines = buildSummaryLines(features)\n\n // ── TypeScript conventions ─────────────────────────────────────────────────\n const tsSection = typescript ?\n `\n### TypeScript\n\n- Use explicit type annotations on all exported functions and values\n- Use \\`import type\\` for type-only imports\n- Avoid \\`any\\` — prefer \\`unknown\\` with type guards\n- Prefer \\`const\\` over \\`let\\` whenever the binding is not reassigned\n- Return types must be explicit on exported functions\n` :\n ''\n\n // ── Framework hints ────────────────────────────────────────────────────────\n const frameworkSections = buildFrameworkSections(frameworks)\n // ── Testing hints ──────────────────────────────────────────────────────────\n const testingSections: string[] = []\n\n if (testing.some(t => t === 'Vitest' || t === 'Jest')) {\n testingSections.push(`\n### Tests\n\n- Name test files \\`*.test.ts\\` or \\`*.spec.ts\\`\n- Use \\`describe\\` blocks to group related cases\n- Avoid testing implementation details — test observable behaviour\n`)\n }\n\n if (testing.includes('Playwright')) {\n testingSections.push(`\n### Playwright\n\n- Place tests in a dedicated \\`e2e/\\` or \\`tests/\\` directory\n- Use \\`page.getByRole\\` / \\`page.getByText\\` over CSS selectors where possible\n`)\n }\n\n // ── Tool hints ─────────────────────────────────────────────────────────────\n const toolSections: string[] = []\n\n if (tools.includes('Prettier')) {\n toolSections.push(`\n### Formatting\n\n- Do not manually format — Prettier handles all formatting\n- Prettier runs as the **last** ESLint config; never override its rules\n`)\n }\n\n if (tools.includes('Tailwind CSS') || libraries.includes('Tailwind CSS')) {\n toolSections.push(`\n### Tailwind CSS\n\n- Use Tailwind utility classes directly in templates rather than hand-written CSS where possible\n- The \\`better-tailwindcss\\` plugin enforces ordering; do not disable class-ordering rules\n`)\n }\n\n // ── Assemble body ─────────────────────────────────────────────────────────\n const body = `# ESLint Code Standards\n\nThis project uses [\\`@santi020k/eslint-config-basic\\`](https://github.com/santi020k/eslint-config-basic) —\na composable ESLint 10+ Flat Config package.\n\n**Always run \\`${lintCommand}\\` to validate your changes before finishing any task.**\n\n## Active Configuration\n\n${summaryLines.join('\\n')}\n\n## Conventions\n${tsSection}${frameworkSections.join('')}${testingSections.join('')}${toolSections.join('')}\n### General\n\n- ESLint 10 **Flat Config** format only — no \\`.eslintrc\\` files\n- Use \\`.js\\` extensions on relative imports (ESM requirement)\n- Use \\`type\\` imports for type-only values; regular imports for runtime values\n\n## Verification\n\n\\`\\`\\`bash\n${lintCommand}\n\\`\\`\\`\n\nIf the command is unavailable, fall back to:\n\n\\`\\`\\`bash\nnpx eslint .\n\\`\\`\\`\n`\n\n // ── Format-specific wrappers ───────────────────────────────────────────────\n return wrapWithFormat(body, format)\n}\n\n// ─── File writer ──────────────────────────────────────────────────────────────\n\nconst writeSkillFile = (filePath: string, content: string, force: boolean): boolean => {\n if (existsSync(filePath) && !force) return false\n\n mkdirSync(join(filePath, '..'), { recursive: true })\n\n writeFileSync(filePath, content, 'utf-8')\n\n return true\n}\n\n// ─── Guarded-section files (Copilot instructions, AGENTS.md) ──────────────────\n\nconst COPILOT_INSTRUCTIONS_PATH = '.github/copilot-instructions.md'\nconst AGENTS_MD_PATH = 'AGENTS.md'\nconst COPILOT_SECTION_START = '<!-- eslint-standards:start -->'\nconst COPILOT_SECTION_END = '<!-- eslint-standards:end -->'\n\nconst buildGuardedSection = (body: string): string => (\n `${COPILOT_SECTION_START}\\n${body}\\n${COPILOT_SECTION_END}`\n)\n\ntype GuardedSectionResult = 'skipped' | 'stale' | 'written' | null\n\n/**\n * Appends or updates a guarded ESLint-standards section inside an existing\n * instructions file (`.github/copilot-instructions.md` or root `AGENTS.md`,\n * the open agent-instructions standard read by Codex CLI, OpenCode, Jules,\n * Amp, and many other AI coding tools).\n *\n * In check mode nothing is written; the function reports `'stale'` when the\n * section is missing or out of date and `'skipped'` when it is up to date.\n */\nconst handleGuardedSectionFile = (\n filePath: string,\n body: string,\n force: boolean,\n check: boolean,\n createIfMissing = false\n): GuardedSectionResult => {\n const section = buildGuardedSection(body)\n\n if (!existsSync(filePath)) {\n if (!createIfMissing || check) return null\n\n writeFileSync(filePath, `# Agent instructions\\n\\n${section}\\n`, 'utf-8')\n\n return 'written'\n }\n\n const existing = readFileSync(filePath, 'utf-8')\n\n if (check) {\n return existing.includes(section) ? 'skipped' : 'stale'\n }\n\n if (existing.includes(COPILOT_SECTION_START)) {\n if (!force) return 'skipped'\n\n const updated = existing.replaceAll(\n new RegExp(`${COPILOT_SECTION_START}[\\\\s\\\\S]*?${COPILOT_SECTION_END}`, 'g'), section\n )\n\n writeFileSync(filePath, updated, 'utf-8')\n\n return 'written'\n }\n\n writeFileSync(filePath, `${existing}\\n${section}\\n`, 'utf-8')\n\n return 'written'\n}\n\n// ─── Public API ───────────────────────────────────────────────────────────────\n\nconst resolveSkillOptions = (opts: GenerateSkillOptions) => ({\n check: opts.check ?? false,\n createAgentsMd: opts.createAgentsMd ?? false,\n cwd: opts.cwd ?? process.cwd(),\n force: opts.force ?? false,\n withEslintMcp: opts.withEslintMcp ?? false\n})\n\nconst createEslintMcpServer = (cwd: string): { args: string[], command: string } => {\n if (existsSync(join(cwd, 'pnpm-lock.yaml')) || existsSync(join(cwd, 'pnpm-workspace.yaml'))) {\n return { args: ['exec', 'eslint', '--mcp'], command: 'pnpm' }\n }\n\n if (existsSync(join(cwd, 'yarn.lock'))) {\n return { args: ['exec', 'eslint', '--mcp'], command: 'yarn' }\n }\n\n if (existsSync(join(cwd, 'bun.lockb')) || existsSync(join(cwd, 'bun.lock'))) {\n return { args: ['eslint', '--mcp'], command: 'bunx' }\n }\n\n return { args: ['eslint', '--mcp'], command: 'npx' }\n}\n\nconst handleEslintMcpConfig = (\n cwd: string,\n force: boolean,\n check: boolean\n): GuardedSectionResult => {\n const configPath = join(cwd, '.mcp.json')\n const server = createEslintMcpServer(cwd)\n let config: { mcpServers?: Record<string, unknown>, [key: string]: unknown } = {}\n\n if (existsSync(configPath)) {\n try {\n config = JSON.parse(readFileSync(configPath, 'utf8')) as typeof config\n } catch {\n throw new Error('.mcp.json is invalid JSON; fix it before adding the ESLint MCP server.')\n }\n }\n\n const current = config.mcpServers?.eslint\n const isCurrent = JSON.stringify(current) === JSON.stringify(server)\n\n if (check) return isCurrent ? 'skipped' : 'stale'\n\n if (current && !force) return 'skipped'\n\n config.mcpServers = {\n ...config.mcpServers,\n eslint: server\n }\n\n writeFileSync(configPath, `${JSON.stringify(config, null, 2)}\\n`)\n\n return 'written'\n}\n\n/**\n * Detects which AI agent folders exist in the project, reads the actual\n * `eslint.config.js` to understand what is configured, and writes a tailored\n * ESLint standards skill file into each found folder.\n *\n * Falls back to package.json auto-detection when the config file cannot be\n * dynamically imported (e.g. when running outside a built environment).\n *\n * @example\n * ```ts\n * import { generateAgentSkills } from '@santi020k/eslint-config-basic/agent'\n *\n * const result = await generateAgentSkills({ cwd: process.cwd() })\n * process.stdout.write(`Written to: ${result.written}\\n`)\n * ```\n */\nconst processAgentTarget = (\n target: AgentTarget,\n cwd: string,\n features: EslintConfigFeatures,\n force: boolean,\n check: boolean\n): { filePath: string, result: GuardedSectionResult } | undefined => {\n const agentFolder = join(cwd, target.markerFolder)\n\n if (!existsSync(agentFolder)) return undefined\n\n const subdir = target.skillSubdir === '.' ? agentFolder : join(agentFolder, target.skillSubdir)\n const filePath = join(subdir, target.skillFile)\n const content = generateSkillContent(features, target.format)\n\n if (check) {\n const upToDate = existsSync(filePath) && readFileSync(filePath, 'utf-8') === content\n\n return { filePath, result: upToDate ? 'skipped' : 'stale' }\n }\n\n return {\n filePath,\n result: writeSkillFile(filePath, content, force) ? 'written' : 'skipped'\n }\n}\n\nexport const generateAgentSkills = async (\n opts: GenerateSkillOptions = {}\n): Promise<GenerateSkillResult> => {\n const { check, createAgentsMd, cwd, force, withEslintMcp } = resolveSkillOptions(opts)\n const written: string[] = []\n const skipped: string[] = []\n const stale: string[] = []\n // Primary: load the real eslint.config.js; fallback: package.json detection\n const detectedFeatures = await analyzeEslintConfig(cwd)\n const features = detectedFeatures ?? featuresFromDetection(cwd)\n const plainBody = generateSkillContent(features, 'plain')\n\n const recordGuardedResult = (filePath: string, result: GuardedSectionResult): void => {\n switch (result) {\n case 'skipped':\n skipped.push(filePath)\n\n break\n\n case 'stale':\n stale.push(filePath)\n\n break\n\n case 'written':\n written.push(filePath)\n\n break\n }\n }\n\n // ── Copilot instructions (append/update guarded section) ──────────────────\n const copilotPath = join(cwd, COPILOT_INSTRUCTIONS_PATH)\n\n recordGuardedResult(copilotPath, handleGuardedSectionFile(copilotPath, plainBody, force, check))\n\n // ── AGENTS.md (append/update guarded section, Codex CLI / OpenCode / etc.) ─\n const agentsMdPath = join(cwd, AGENTS_MD_PATH)\n\n recordGuardedResult(agentsMdPath, handleGuardedSectionFile(agentsMdPath, plainBody, force, check, createAgentsMd))\n\n if (withEslintMcp) {\n const eslintMcpPath = join(cwd, '.mcp.json')\n\n recordGuardedResult(eslintMcpPath, handleEslintMcpConfig(cwd, force, check))\n }\n\n // ── Standard agent targets ─────────────────────────────────────────────────\n for (const target of AGENT_TARGETS) {\n const processed = processAgentTarget(target, cwd, features, force, check)\n\n if (processed) recordGuardedResult(processed.filePath, processed.result)\n }\n\n return { skipped, stale, written }\n}\n\n// ─── CLI handler ──────────────────────────────────────────────────────────────\n\nexport interface HandleGenerateSkillFlags {\n\n /** Check mode — report stale files and set a non-zero exit code, write nothing */\n check?: boolean\n\n /** Create a root `AGENTS.md` when it does not exist */\n createAgentsMd?: boolean\n\n /** Add the official ESLint MCP server to `.mcp.json` */\n withEslintMcp?: boolean\n}\n\nconst resolveGenerateSkillFlags = (flags: HandleGenerateSkillFlags) => ({\n check: flags.check ?? false,\n createAgentsMd: flags.createAgentsMd ?? false,\n withEslintMcp: flags.withEslintMcp ?? false\n})\n\nconst outputGenerateSkillResults = (\n check: boolean,\n result: Awaited<ReturnType<typeof generateAgentSkills>>,\n cwd: string\n): void => {\n if (check) {\n for (const file of result.skipped) {\n console.log(`✅ Up to date: ${file.replace(cwd + '/', '')}`)\n }\n\n for (const file of result.stale) {\n console.log(`❌ Stale or missing: ${file.replace(cwd + '/', '')}`)\n }\n\n if (result.stale.length > 0) {\n console.log(`\\n⚠️ ${result.stale.length} skill file(s) are out of date. Run \\`generate-skill --force\\` to regenerate them.`)\n\n process.exitCode = 1\n } else {\n console.log('\\n🎉 All agent skill files are up to date!')\n }\n\n return\n }\n\n for (const file of result.written) {\n console.log(`✅ Written: ${file.replace(cwd + '/', '')}`)\n }\n\n for (const file of result.skipped) {\n console.log(`⏭️ Skipped (already exists): ${file.replace(cwd + '/', '')}`)\n }\n\n if (result.written.length > 0) {\n console.log(`\\n🎉 Generated ${result.written.length} skill file(s)!`)\n\n console.log(' Agents will now follow your project\\'s ESLint standards automatically.')\n }\n\n if (result.skipped.length > 0) {\n console.log('\\n💡 Tip: run with --force to overwrite existing skill files.')\n }\n}\n\nexport const handleGenerateSkill = async (\n cwd: string = process.cwd(),\n force = false,\n flags: HandleGenerateSkillFlags = {}\n): Promise<void> => {\n const { check, createAgentsMd, withEslintMcp } = resolveGenerateSkillFlags(flags)\n\n console.log(check ? '🔍 Checking AI agent skill files...' : '🔍 Scanning for AI agent folders...')\n\n const configFile = findEslintConfig(cwd)\n\n if (configFile) {\n console.log(`📄 Reading config from: ${configFile.replace(cwd + '/', '')}`)\n } else {\n console.log('⚠️ No eslint.config.js found — falling back to package.json detection.')\n }\n\n const result = await generateAgentSkills({ check, createAgentsMd, cwd, force, withEslintMcp })\n\n if (result.written.length === 0 && result.skipped.length === 0 && result.stale.length === 0) {\n console.log('\\n⚠️ No agent folders found (.agent, .agents, .claude, .cursor, .windsurf, .copilot, .aider, .gemini, .clinerules, .roo, .kiro) and no AGENTS.md.')\n\n console.log(' Create one of those folders first (or re-run with --create to scaffold AGENTS.md).')\n\n return\n }\n\n outputGenerateSkillResults(check, result, cwd)\n}\n"],"mappings":"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","names":[]}
@@ -7,12 +7,19 @@ declare const handleExplainRule: (cwd: string | undefined, options: ExplainRuleO
7
7
  declare const createCompatibilityReport: (cwd?: string) => {
8
8
  compatible: boolean;
9
9
  packages: {
10
+ aggregatedBasic: {
11
+ name: string;
12
+ resolved: string | null;
13
+ resolvedPath: string | null;
14
+ } | null;
10
15
  declared: string;
11
16
  issues: string[];
12
17
  name: string;
13
18
  resolved: string | null;
19
+ resolvedPath: string | null;
14
20
  }[];
15
21
  runtime: {
22
+ consumerNodeRange: string | null;
16
23
  eslint: string | null;
17
24
  node: string;
18
25
  typescript: string | null;
@@ -1,5 +1,5 @@
1
1
  import { existsSync, readdirSync, readFileSync } from "node:fs";
2
- import { createRequire } from "node:module";
2
+ import { createRequire, findPackageJSON } from "node:module";
3
3
  import { basename, dirname, join } from "node:path";
4
4
  import { pathToFileURL } from "node:url";
5
5
  const CONFIG_FILENAMES = [
@@ -10,6 +10,8 @@ const CONFIG_FILENAMES = [
10
10
  "eslint.config.mts",
11
11
  "eslint.config.cts"
12
12
  ];
13
+ const BASIC_PACKAGE_NAME = "@santi020k/eslint-config-basic";
14
+ const FULL_PACKAGE_NAME = "@santi020k/eslint-config-full";
13
15
  const RULE_PACKAGE_HINTS = {
14
16
  "@angular-eslint": "@santi020k/eslint-config-angular",
15
17
  "@eslint-react": "@santi020k/eslint-config-react",
@@ -18,7 +20,7 @@ const RULE_PACKAGE_HINTS = {
18
20
  astro: "@santi020k/eslint-config-astro",
19
21
  cypress: "@santi020k/eslint-config-testing",
20
22
  jest: "@santi020k/eslint-config-testing",
21
- "jsonc": "@santi020k/eslint-config-formats",
23
+ jsonc: "@santi020k/eslint-config-formats",
22
24
  playwright: "@santi020k/eslint-config-testing",
23
25
  react: "@santi020k/eslint-config-react",
24
26
  "testing-library": "@santi020k/eslint-config-testing",
@@ -70,6 +72,22 @@ const getPackageHint = (rule) => {
70
72
  const scopedPrefix = rule.startsWith("@") ? rule.split("/").slice(0, 2).join("/") : prefix;
71
73
  return RULE_PACKAGE_HINTS[scopedPrefix] ?? RULE_PACKAGE_HINTS[prefix] ?? null;
72
74
  };
75
+ const getRuleDiagnostics = async (eslint, file, rule) => {
76
+ if (rule !== "complexity") return [];
77
+ try {
78
+ const results = await eslint.lintFiles([file]);
79
+ return results.flatMap((result) => result.messages.filter((message) => message.ruleId === rule).map((message) => ({
80
+ column: message.column ?? null,
81
+ endColumn: message.endColumn ?? null,
82
+ endLine: message.endLine ?? null,
83
+ file: result.filePath,
84
+ line: message.line ?? null,
85
+ message: message.message
86
+ })));
87
+ } catch {
88
+ return [];
89
+ }
90
+ };
73
91
  const handleExplainRule = async (cwd = process.cwd(), options) => {
74
92
  const configPath = findConfigPath(cwd);
75
93
  const file = options.file ?? "eslint.config.js";
@@ -77,6 +95,7 @@ const handleExplainRule = async (cwd = process.cwd(), options) => {
77
95
  const effective = await eslint.calculateConfigForFile(file);
78
96
  const effectiveValue = effective?.rules?.[options.rule];
79
97
  const entries = await loadConfigEntries(configPath);
98
+ const diagnostics = await getRuleDiagnostics(eslint, file, options.rule);
80
99
  const sources = entries.flatMap((entry, index) => {
81
100
  if (!entry.rules || !Object.hasOwn(entry.rules, options.rule)) return [];
82
101
  return [{
@@ -88,6 +107,7 @@ const handleExplainRule = async (cwd = process.cwd(), options) => {
88
107
  });
89
108
  const payload = {
90
109
  configFile: configPath ? basename(configPath) : null,
110
+ diagnostics,
91
111
  effective: effectiveValue ?? null,
92
112
  enabled: effectiveValue !== void 0 && isRuleEnabled(effectiveValue),
93
113
  file,
@@ -106,11 +126,17 @@ const handleExplainRule = async (cwd = process.cwd(), options) => {
106
126
  `- Effective value: ${JSON.stringify(payload.effective)}`,
107
127
  `- Enabled: ${payload.enabled ? "yes" : "no"}`,
108
128
  `- Likely package: ${payload.packageHint ?? "core or custom config"}`,
109
- `- Defining entries: ${payload.sources.length}`
129
+ `- Defining entries: ${payload.sources.length}`,
130
+ ...payload.diagnostics.length > 0 ? [`- Current findings: ${payload.diagnostics.length}`] : []
110
131
  ].join("\n"));
111
132
  for (const source of payload.sources) {
112
133
  console.log(` - ${source.name}: ${JSON.stringify(source.value)}`);
113
134
  }
135
+ for (const diagnostic of payload.diagnostics) {
136
+ console.log(
137
+ ` - ${diagnostic.file}:${diagnostic.line ?? "?"}:${diagnostic.column ?? "?"} ` + diagnostic.message
138
+ );
139
+ }
114
140
  }
115
141
  if (effectiveValue === void 0) process.exitCode = 1;
116
142
  };
@@ -126,13 +152,19 @@ const getDependencyVersions = (manifest) => {
126
152
  }
127
153
  return dependencies;
128
154
  };
129
- const readResolvedManifest = (projectRequire, packageName) => {
155
+ const readResolvedManifest = (basePath, packageName) => {
130
156
  try {
131
- let directory = dirname(projectRequire.resolve(packageName));
157
+ const manifestPath = findPackageJSON(packageName, pathToFileURL(basePath));
158
+ const manifest = manifestPath ? readJson(manifestPath) : null;
159
+ if (manifestPath && manifest?.name === packageName) return { manifest, path: manifestPath };
160
+ } catch {
161
+ }
162
+ try {
163
+ let directory = dirname(createRequire(basePath).resolve(packageName));
132
164
  for (let depth = 0; depth < 6; depth++) {
133
165
  const manifestPath = join(directory, "package.json");
134
166
  const manifest = readJson(manifestPath);
135
- if (manifest?.name === packageName) return manifest;
167
+ if (manifest?.name === packageName) return { manifest, path: manifestPath };
136
168
  directory = dirname(directory);
137
169
  }
138
170
  } catch {
@@ -145,24 +177,29 @@ const readWorkspaceManifest = (cwd, packageName) => {
145
177
  if (!existsSync(packagesPath)) return null;
146
178
  for (const entry of readdirSync(packagesPath, { withFileTypes: true })) {
147
179
  if (!entry.isDirectory()) continue;
148
- const manifest = readJson(join(packagesPath, entry.name, "package.json"));
149
- if (manifest?.name === packageName) return manifest;
180
+ const manifestPath = join(packagesPath, entry.name, "package.json");
181
+ const manifest = readJson(manifestPath);
182
+ if (manifest?.name === packageName) return { manifest, path: manifestPath };
150
183
  }
151
184
  return null;
152
185
  };
153
186
  const createCompatibilityReport = (cwd = process.cwd()) => {
154
- const rootManifest = readJson(join(cwd, "package.json"));
187
+ const rootManifestPath = join(cwd, "package.json");
188
+ const rootManifest = readJson(rootManifestPath);
155
189
  const declared = getDependencyVersions(rootManifest);
156
- const projectRequire = createRequire(join(cwd, "package.json"));
190
+ const consumerNodeRange = rootManifest?.engines?.node ?? null;
157
191
  const configPackages = Object.keys(declared).filter((name) => name.startsWith("@santi020k/eslint-config-")).sort();
158
192
  const runtimeVersions = {
159
- eslint: readResolvedManifest(projectRequire, "eslint")?.version ?? null,
193
+ consumerNodeRange,
194
+ eslint: readResolvedManifest(rootManifestPath, "eslint")?.manifest.version ?? null,
160
195
  node: process.versions.node,
161
- typescript: readResolvedManifest(projectRequire, "typescript")?.version ?? null
196
+ typescript: readResolvedManifest(rootManifestPath, "typescript")?.manifest.version ?? null
162
197
  };
163
198
  const packages = configPackages.map((name) => {
164
- const manifest = readResolvedManifest(projectRequire, name) ?? readWorkspaceManifest(cwd, name);
199
+ const resolvedPackage = readResolvedManifest(rootManifestPath, name) ?? readWorkspaceManifest(cwd, name);
200
+ const manifest = resolvedPackage?.manifest;
165
201
  const issues = [];
202
+ const aggregatedBasic = name === FULL_PACKAGE_NAME && resolvedPackage ? readResolvedManifest(resolvedPackage.path, BASIC_PACKAGE_NAME) : null;
166
203
  if (!manifest) {
167
204
  issues.push("declared but not installed");
168
205
  } else {
@@ -170,6 +207,15 @@ const createCompatibilityReport = (cwd = process.cwd()) => {
170
207
  if (nodeRange && !semver.satisfies(runtimeVersions.node, nodeRange)) {
171
208
  issues.push(`requires Node ${nodeRange}`);
172
209
  }
210
+ if (nodeRange && consumerNodeRange) {
211
+ if (!semver.validRange(consumerNodeRange)) {
212
+ issues.push(`consumer engines.node ${consumerNodeRange} is not a valid semver range`);
213
+ } else if (!semver.subset(consumerNodeRange, nodeRange)) {
214
+ issues.push(
215
+ `consumer engines.node ${consumerNodeRange} permits unsupported runtimes; requires ${nodeRange}`
216
+ );
217
+ }
218
+ }
173
219
  for (const peer of ["eslint", "typescript"]) {
174
220
  const range = manifest.peerDependencies?.[peer];
175
221
  const version = runtimeVersions[peer];
@@ -179,10 +225,16 @@ const createCompatibilityReport = (cwd = process.cwd()) => {
179
225
  }
180
226
  }
181
227
  return {
228
+ aggregatedBasic: name === FULL_PACKAGE_NAME ? {
229
+ name: BASIC_PACKAGE_NAME,
230
+ resolved: aggregatedBasic?.manifest.version ?? null,
231
+ resolvedPath: aggregatedBasic?.path ?? null
232
+ } : null,
182
233
  declared: declared[name],
183
234
  issues,
184
235
  name,
185
- resolved: manifest?.version ?? null
236
+ resolved: manifest?.version ?? null,
237
+ resolvedPath: resolvedPackage?.path ?? null
186
238
  };
187
239
  });
188
240
  return {
@@ -199,13 +251,19 @@ const handleCompatibility = (cwd = process.cwd(), json = false) => {
199
251
  console.log([
200
252
  `ESLint compatibility: ${report.compatible ? "compatible" : "issues found"}`,
201
253
  `- Node: ${report.runtime.node}`,
254
+ `- Consumer Node range: ${report.runtime.consumerNodeRange ?? "not declared"}`,
202
255
  `- ESLint: ${report.runtime.eslint ?? "not installed"}`,
203
256
  `- TypeScript: ${report.runtime.typescript ?? "not installed"}`
204
257
  ].join("\n"));
205
258
  for (const item of report.packages) {
206
259
  console.log(
207
- `- ${item.name}: ${item.resolved ?? "not installed"}` + (item.issues.length > 0 ? ` (${item.issues.join("; ")})` : "")
260
+ `- ${item.name}: ${item.resolved ?? "not installed"}` + (item.resolvedPath ? ` at ${item.resolvedPath}` : "") + (item.issues.length > 0 ? ` (${item.issues.join("; ")})` : "")
208
261
  );
262
+ if (item.aggregatedBasic) {
263
+ console.log(
264
+ ` - Composer ${item.aggregatedBasic.name}: ` + (item.aggregatedBasic.resolved ?? "not resolved") + (item.aggregatedBasic.resolvedPath ? ` at ${item.aggregatedBasic.resolvedPath}` : "")
265
+ );
266
+ }
209
267
  }
210
268
  }
211
269
  if (!report.compatible) process.exitCode = 1;
@@ -215,3 +273,4 @@ export {
215
273
  handleCompatibility,
216
274
  handleExplainRule
217
275
  };
276
+ //# sourceMappingURL=cli-advanced.js.map