@santi020k/eslint-config-basic 3.2.0 → 3.3.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.
- package/CHANGELOG.md +28 -0
- package/dist/agent-skill-generator.d.ts +7 -18
- package/dist/agent-skill-generator.js +54 -18
- package/dist/agent-skill-generator.js.map +1 -0
- package/dist/cli-advanced.d.ts +7 -0
- package/dist/cli-advanced.js +74 -15
- package/dist/cli-advanced.js.map +1 -0
- package/dist/cli-migration.js +22 -20
- package/dist/cli-migration.js.map +1 -0
- package/dist/cli-package-manager.d.ts +10 -0
- package/dist/cli-package-manager.js +135 -0
- package/dist/cli-package-manager.js.map +1 -0
- package/dist/cli-preset.d.ts +74 -2
- package/dist/cli-preset.js +253 -6
- package/dist/cli-preset.js.map +1 -0
- package/dist/cli-workflows.js +1 -0
- package/dist/cli-workflows.js.map +1 -0
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +461 -176
- package/dist/cli.js.map +1 -0
- package/dist/define-config-metadata.js +2 -4
- package/dist/define-config-metadata.js.map +1 -0
- package/dist/frameworks.d.ts +1 -0
- package/dist/frameworks.js +2 -1
- package/dist/frameworks.js.map +1 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +83 -22
- package/dist/index.js.map +1 -0
- package/dist/integrations.js +1 -0
- package/dist/integrations.js.map +1 -0
- package/dist/optional-package-errors.js +1 -0
- package/dist/optional-package-errors.js.map +1 -0
- package/dist/recommended.js +1 -0
- package/dist/recommended.js.map +1 -0
- package/dist/resolvers.js +1 -0
- package/dist/resolvers.js.map +1 -0
- package/dist/tailwind.d.ts +3 -1
- package/dist/tailwind.js +115 -13
- package/dist/tailwind.js.map +1 -0
- package/package.json +21 -21
- package/IMPROVEMENTS.md +0 -270
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,33 @@
|
|
|
1
1
|
# @santi020k/eslint-config-basic
|
|
2
2
|
|
|
3
|
+
## 3.3.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#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
|
|
8
|
+
autofix preview, and a TypeScript compiler preflight that prioritizes root
|
|
9
|
+
module-resolution failures. Enhance complexity explanations with live
|
|
10
|
+
rule-specific diagnostics and source locations.
|
|
11
|
+
|
|
12
|
+
Add structured per-project Doctor activation reports covering installed,
|
|
13
|
+
detected, and enabled frameworks and feature packs, TypeScript mode, tsconfig,
|
|
14
|
+
ignores, Tailwind entry-point policy, and reasons optional packages remain
|
|
15
|
+
inactive. Improve compatibility, install, and Doctor repair flows for pnpm
|
|
16
|
+
catalogs, npm aliases, ESM-only manifests, package-manager-specific installs,
|
|
17
|
+
Node engine ranges, recommended re-exports, and preset-implied category packs.
|
|
18
|
+
|
|
19
|
+
Improve automatic project scoping for declaration-only TypeScript packages,
|
|
20
|
+
shared Astro sources, generated Next.js declarations, CLI entry points, and
|
|
21
|
+
package-local Tailwind CSS graphs, including Tailwind v4 custom utilities.
|
|
22
|
+
|
|
23
|
+
### Patch Changes
|
|
24
|
+
|
|
25
|
+
- [#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,
|
|
26
|
+
and Bun by ensuring the eagerly loaded TypeScript parser has its runtime.
|
|
27
|
+
- Updated dependencies [[`66cee8e`](https://github.com/santi020k/eslint-config-basic/commit/66cee8e41ac22b14e8afa91f1f77587f08bc414f), [`66cee8e`](https://github.com/santi020k/eslint-config-basic/commit/66cee8e41ac22b14e8afa91f1f77587f08bc414f)]:
|
|
28
|
+
- @santi020k/eslint-config-typescript@3.1.2
|
|
29
|
+
- @santi020k/eslint-config-core@3.1.1
|
|
30
|
+
|
|
3
31
|
## 3.2.0
|
|
4
32
|
|
|
5
33
|
### 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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
671
|
-
if (
|
|
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":"AACA,SAAS,YAAY,WAAW,cAAc,qBAAqB;AACnE,SAAS,MAAM,eAAe;AAC9B,SAAS,qBAAqB;AAE9B,SAAS,4BAA4B;AAE9B,MAAM,0BAA0B;AAAA,EACrC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAmGO,MAAM,gBAA+B;AAAA,EAC1C;AAAA,IACE,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,cAAc;AAAA,IACd,WAAW;AAAA,IACX,aAAa;AAAA,EACf;AAAA,EACA;AAAA,IACE,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,cAAc;AAAA,IACd,WAAW;AAAA,IACX,aAAa;AAAA,EACf;AAAA,EACA;AAAA,IACE,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,cAAc;AAAA,IACd,WAAW;AAAA,IACX,aAAa;AAAA,EACf;AAAA,EACA;AAAA,IACE,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,cAAc;AAAA,IACd,WAAW;AAAA,IACX,aAAa;AAAA,EACf;AAAA,EACA;AAAA,IACE,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,cAAc;AAAA,IACd,WAAW;AAAA,IACX,aAAa;AAAA,EACf;AAAA,EACA;AAAA,IACE,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,cAAc;AAAA,IACd,WAAW;AAAA,IACX,aAAa;AAAA,EACf;AAAA,EACA;AAAA,IACE,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,cAAc;AAAA,IACd,WAAW;AAAA,IACX,aAAa;AAAA,EACf;AAAA,EACA;AAAA,IACE,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,cAAc;AAAA,IACd,WAAW;AAAA,IACX,aAAa;AAAA,EACf;AAAA,EACA;AAAA,IACE,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,cAAc;AAAA,IACd,WAAW;AAAA,IACX,aAAa;AAAA,EACf;AAAA,EACA;AAAA,IACE,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,cAAc;AAAA,IACd,WAAW;AAAA,IACX,aAAa;AAAA,EACf;AAAA,EACA;AAAA,IACE,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,cAAc;AAAA,IACd,WAAW;AAAA,IACX,aAAa;AAAA,EACf;AACF;AAgBA,MAAM,cAAsF;AAAA;AAAA,EAE1F,CAAC,6BAA6B,cAAc,YAAY;AAAA,EACxD,CAAC,sBAA4B,cAAc,YAAY;AAAA;AAAA,EAGvD,CAAC,wBAAyB,cAAc,OAAO;AAAA,EAC/C,CAAC,uBAAyB,cAAc,SAAS;AAAA,EACjD,CAAC,sBAAyB,cAAc,KAAK;AAAA,EAC7C,CAAC,yBAAyB,cAAc,QAAQ;AAAA,EAChD,CAAC,wBAAyB,cAAc,OAAO;AAAA,EAC/C,CAAC,0BAA0B,cAAc,SAAS;AAAA,EAClD,CAAC,uBAAyB,cAAc,QAAQ;AAAA,EAChD,CAAC,uBAAyB,cAAc,MAAM;AAAA,EAC9C,CAAC,uBAAyB,cAAc,MAAM;AAAA,EAC9C,CAAC,uBAAyB,cAAc,MAAM;AAAA,EAC9C,CAAC,+BAA+B,cAAc,cAAc;AAAA,EAC5D,CAAC,wBAAyB,cAAc,SAAS;AAAA,EACjD,CAAC,uBAAyB,cAAc,MAAM;AAAA,EAC9C,CAAC,sBAAyB,cAAc,KAAK;AAAA,EAC7C,CAAC,iCAAiC,cAAc,gBAAgB;AAAA,EAChE,CAAC,yBAAyB,cAAc,QAAQ;AAAA,EAChD,CAAC,yBAAyB,cAAc,QAAQ;AAAA,EAChD,CAAC,uBAAyB,cAAc,MAAM;AAAA;AAAA,EAG9C,CAAC,2CAA2C,cAAc,cAAc;AAAA,EACxE,CAAC,iBAAiB,cAAc,cAAc;AAAA;AAAA,EAG9C,CAAC,uBAAoC,WAAW,QAAQ;AAAA,EACxD,CAAC,qBAAmC,WAAW,MAAM;AAAA,EACrD,CAAC,uCAAuC,WAAW,UAAU;AAAA,EAC7D,CAAC,2BAAoC,WAAW,YAAY;AAAA,EAC5D,CAAC,wBAAoC,WAAW,SAAS;AAAA,EACzD,CAAC,gCAAoC,WAAW,iBAAiB;AAAA;AAAA,EAGjE,CAAC,0BAA2C,SAAS,UAAU;AAAA,EAC/D,CAAC,uBAA2C,SAAS,QAAQ;AAAA,EAC7D,CAAC,oCAA2C,SAAS,OAAO;AAAA,EAC5D,CAAC,wBAA2C,SAAS,SAAS;AAAA,EAC9D,CAAC,sCAA2C,SAAS,SAAS;AAAA,EAC9D,CAAC,qCAA2C,SAAS,QAAQ;AAAA,EAC7D,CAAC,6CAA6C,SAAS,gBAAgB;AAAA,EACvE,CAAC,iCAA2C,SAAS,IAAI;AAAA,EACzD,CAAC,sBAA2C,SAAS,MAAM;AAAA;AAAA,EAG3D,CAAC,qCAA4C,aAAa,QAAQ;AAAA,EAClE,CAAC,kCAA4C,aAAa,KAAK;AAAA,EAC/D,CAAC,qCAA4C,aAAa,QAAQ;AAAA,EAClE,CAAC,4CAA4C,aAAa,eAAe;AAAA,EACzE,CAAC,wCAA4C,aAAa,WAAW;AAAA,EACrE,CAAC,yCAA4C,aAAa,YAAY;AAAA,EACtE,CAAC,uBAA4C,aAAa,cAAc;AAAA,EACxE,CAAC,wBAA4C,aAAa,SAAS;AAAA,EACnE,CAAC,wBAA4C,aAAa,SAAS;AAAA,EACnE,CAAC,yBAA4C,aAAa,WAAW;AAAA,EACrE,CAAC,6CAA8C,aAAa,gBAAgB;AAAA,EAC5E,CAAC,8CAA8C,aAAa,iBAAiB;AAAA,EAC7E,CAAC,sCAA4C,aAAa,SAAS;AAAA,EACnE,CAAC,2CAA4C,aAAa,cAAc;AAAA,EACxE,CAAC,sCAA4C,aAAa,aAAa;AAAA,EACvE,CAAC,qCAA4C,aAAa,QAAQ;AAAA,EAClE,CAAC,sCAA4C,aAAa,SAAS;AAAA,EACnE,CAAC,wCAA4C,aAAa,UAAU;AAAA,EACpE,CAAC,wCAA4C,aAAa,WAAW;AAAA,EACrE,CAAC,kCAA4C,aAAa,KAAK;AAAA,EAC/D,CAAC,wBAA4C,aAAa,OAAO;AAAA;AAAA,EAGjE,CAAC,qBAA4C,WAAW,KAAK;AAAA,EAC7D,CAAC,sBAA4C,WAAW,MAAM;AAAA,EAC9D,CAAC,2CAA4C,WAAW,cAAc;AAAA,EACtE,CAAC,yBAA4C,WAAW,SAAS;AAAA,EACjE,CAAC,sBAA4C,WAAW,MAAM;AAAA,EAC9D,CAAC,uBAA4C,WAAW,OAAO;AAAA,EAC/D,CAAC,yBAA4C,WAAW,UAAU;AAAA,EAClE,CAAC,sBAA4C,WAAW,KAAK;AAAA,EAC7D,CAAC,sBAA4C,WAAW,MAAM;AAAA;AAAA,EAG9D,CAAC,yBAA8C,cAAc,SAAS;AAAA,EACtE,CAAC,yBAA8C,cAAc,SAAS;AAAA,EACtE,CAAC,uCAA8C,cAAc,UAAU;AAAA,EACvE,CAAC,4CAA8C,cAAc,eAAe;AAAA,EAC5E,CAAC,uBAA8C,cAAc,QAAQ;AAAA,EACrE,CAAC,gCAA8C,cAAc,gBAAgB;AAAA,EAC7E,CAAC,oCAA8C,cAAc,sBAAsB;AAAA,EACnF,CAAC,oCAA8C,cAAc,OAAO;AAAA,EACpE,CAAC,gDAAgD,cAAc,mBAAmB;AAAA,EAClF,CAAC,wBAA8C,cAAc,gBAAgB;AAAA,EAC7E,CAAC,2BAA8C,cAAc,WAAW;AAAA,EACxE,CAAC,wBAA8C,cAAc,QAAQ;AAAA,EACrE,CAAC,sBAA8C,cAAc,SAAS;AAAA,EACtE,CAAC,4CAA8C,cAAc,eAAe;AAAA,EAC5E,CAAC,uBAA8C,cAAc,QAAQ;AACvE;AAUA,MAAM,4BAA4B,oBAAI,IAAoB;AAAA,EACxD,CAAC,WAAW,SAAS;AAAA,EACrB,CAAC,SAAS,OAAO;AAAA,EACjB,CAAC,QAAQ,MAAM;AAAA,EACf,CAAC,QAAQ,MAAM;AAAA,EACf,CAAC,OAAO,KAAK;AAAA,EACb,CAAC,QAAQ,QAAQ;AAAA,EACjB,CAAC,QAAQ,SAAS;AAAA,EAClB,CAAC,QAAQ,MAAM;AAAA,EACf,CAAC,UAAU,QAAQ;AAAA,EACnB,CAAC,QAAQ,MAAM;AAAA,EACf,CAAC,SAAS,OAAO;AAAA,EACjB,CAAC,gBAAgB,cAAc;AAAA,EAC/B,CAAC,UAAU,QAAQ;AAAA,EACnB,CAAC,SAAS,SAAS;AAAA,EACnB,CAAC,UAAU,QAAQ;AAAA,EACnB,CAAC,kBAAkB,gBAAgB;AAAA,EACnC,CAAC,QAAQ,MAAM;AAAA,EACf,CAAC,OAAO,KAAK;AACf,CAAC;AAED,MAAM,iBAAiB,oBAAI,IAAoB;AAAA,EAC7C,CAAC,kBAAkB,gBAAgB;AAAA,EACnC,CAAC,UAAU,gBAAgB;AAAA,EAC3B,CAAC,UAAU,QAAQ;AAAA,EACnB,CAAC,OAAO,KAAK;AAAA,EACb,CAAC,WAAW,SAAS;AAAA,EACrB,CAAC,aAAa,WAAW;AAAA,EACzB,CAAC,UAAU,QAAQ;AAAA,EACnB,CAAC,WAAW,SAAS;AAAA,EACrB,CAAC,QAAQ,MAAM;AAAA,EACf,CAAC,WAAW,SAAS;AAAA,EACrB,CAAC,QAAQ,MAAM;AAAA,EACf,CAAC,SAAS,OAAO;AAAA,EACjB,CAAC,SAAS,OAAO;AAAA,EACjB,CAAC,YAAY,UAAU;AAAA,EACvB,CAAC,OAAO,KAAK;AAAA,EACb,CAAC,QAAQ,SAAS;AAAA,EAClB,CAAC,UAAU,QAAQ;AAAA,EACnB,CAAC,iBAAiB,eAAe;AAAA,EACjC,CAAC,cAAc,YAAY;AAAA,EAC3B,CAAC,QAAQ,MAAM;AAAA,EACf,CAAC,YAAY,UAAU;AAAA,EACvB,CAAC,UAAU,QAAQ;AAAA,EACnB,CAAC,YAAY,UAAU;AAAA,EACvB,CAAC,WAAW,SAAS;AAAA,EACrB,CAAC,WAAW,SAAS;AAAA,EACrB,CAAC,aAAa,WAAW;AAAA,EACzB,CAAC,WAAW,SAAS;AAAA,EACrB,CAAC,YAAY,cAAc;AAAA,EAC3B,CAAC,kBAAkB,gBAAgB;AAAA,EACnC,CAAC,mBAAmB,iBAAiB;AAAA,EACrC,CAAC,mBAAmB,iBAAiB;AAAA,EACrC,CAAC,QAAQ,MAAM;AAAA,EACf,CAAC,WAAW,SAAS;AAAA,EACrB,CAAC,UAAU,QAAQ;AAAA,EACnB,CAAC,QAAQ,MAAM;AACjB,CAAC;AAED,MAAM,iBAAiB,CAAC,UAA0B,eAAe,IAAI,KAAK,KAAK;AAE/E,MAAM,wBAAwB,CAAC,UAA4B,OAAO,KAAK,KAAK,EACzE,OAAO,SAAO,IAAI,SAAS,GAAG,CAAC,EAC/B,IAAI,SAAO,IAAI,MAAM,GAAG,IAAI,QAAQ,GAAG,CAAC,CAAC;AAM5C,MAAM,gBAAgB,CAAC,YAAiC;AACtD,QAAM,SAAmB,CAAC;AAE1B,aAAW,SAAS,SAAS;AAC3B,QAAI,CAAC,SAAS,OAAO,UAAU,SAAU;AAEzC,UAAM,MAAM;AAGZ,QAAI,OAAO,IAAI,SAAS,UAAU;AAChC,aAAO,KAAK,IAAI,IAAI;AAAA,IACtB;AAGA,QAAI,IAAI,WAAW,OAAO,IAAI,YAAY,UAAU;AAClD,aAAO,KAAK,GAAG,OAAO,KAAK,IAAI,OAAO,CAAC;AAAA,IACzC;AAGA,QAAI,IAAI,SAAS,OAAO,IAAI,UAAU,UAAU;AAC9C,aAAO,KAAK,GAAG,sBAAsB,IAAI,KAAK,CAAC;AAAA,IACjD;AAAA,EACF;AAEA,SAAO;AACT;AAEA,MAAM,wBAAwB,CAAC,UAA6B;AAC1D,MAAI,CAAC,SAAS,OAAO,UAAU,SAAU,QAAO,CAAC;AAEjD,QAAM,EAAE,OAAO,QAAQ,IAAI;AAC3B,QAAM,aAAa,MAAM,QAAQ,KAAK,IAAK,QAAsB,CAAC;AAClE,QAAM,eAAe,MAAM,QAAQ,OAAO,IAAK,UAAwB,CAAC;AAExE,SAAO;AAAA,IACL,GAAG;AAAA,IACH,GAAG;AAAA,EACL,EAAE,KAAK,OAAO,iBAAiB,EAC5B,OAAO,CAAC,UAA2B,OAAO,UAAU,YAAY,CAAC,MAAM,WAAW,GAAG,CAAC;AAC3F;AAEA,MAAM,0BAA0B,CAAC,YAA4B,QAAQ,QAAQ,SAAS,EAAE;AAExF,MAAM,mBAAmB,CAAC,SAAiB,gBAAiC;AAC1E,QAAM,wBAAwB,YAAY,QAAQ,SAAS,EAAE,EAAE,QAAQ,OAAO,EAAE;AAChF,QAAM,oBAAoB,wBAAwB,OAAO;AAEzD,SAAO,sBAAsB,yBAAyB,kBAAkB,WAAW,GAAG,qBAAqB,GAAG;AAChH;AAEA,MAAM,wBAAwB,CAC5B,SACA,aACA,iBACc,QAAQ,OAAO,WAAS;AACtC,QAAM,WAAW,sBAAsB,KAAK;AAE5C,MAAI,SAAS,WAAW,EAAG,QAAO;AAElC,QAAM,qBAAqB,aACxB,OAAO,UAAQ,SAAS,GAAG,EAC3B,OAAO,UAAQ,SAAS,KAAK,aAAW,iBAAiB,SAAS,IAAI,CAAC,CAAC;AAE3E,MAAI,mBAAmB,WAAW,EAAG,QAAO;AAE5C,SAAO,gBAAgB,OAAO,mBAAmB,KAAK,UACpD,gBAAgB,QAAQ,YAAY,WAAW,GAAG,IAAI,GAAG,CAC1D;AACH,CAAC;AAED,MAAM,iBAAiB,CAAC,YAAiC,CAAC,GAAG,IAAI,IAAI,QAAQ,QAAQ,WAAS;AAC5F,MAAI,CAAC,SAAS,OAAO,UAAU,SAAU,QAAO,CAAC;AAEjD,QAAM,UAAW,MAA6B;AAE9C,SAAO,MAAM,QAAQ,OAAO,IAAI,QAAQ,OAAO,CAAC,UAA2B,OAAO,UAAU,QAAQ,IAAI,CAAC;AAC3G,CAAC,CAAC,CAAC;AAEH,MAAM,gBAAgB,CAAC,UAAgC,UAAkB,UAAwB;AAC/F,MAAI,aAAa,cAAc;AAC7B,aAAS,aAAa;AAEtB;AAAA,EACF;AAEA,UAAQ,UAAU;AAAA,IAChB,KAAK;AACH,eAAS,WAAW,KAAK,KAAK;AAE9B;AAAA,IAEF,KAAK;AACH,eAAS,QAAQ,KAAK,KAAK;AAE3B;AAAA,IAEF,KAAK;AACH,eAAS,WAAW,KAAK,KAAK;AAE9B;AAAA,IAEF,KAAK;AACH,eAAS,UAAU,KAAK,KAAK;AAE7B;AAAA,IAEF,KAAK;AACH,eAAS,QAAQ,KAAK,KAAK;AAE3B;AAAA,IAEF,KAAK;AACH,eAAS,MAAM,KAAK,KAAK;AAEzB;AAAA,EACJ;AACF;AAMA,MAAM,kBAAkB,CACtB,SACA,aACA,eACyB;AACzB,QAAM,SAAS,cAAc,OAAO;AAEpC,QAAM,WAAiC;AAAA,IACrC;AAAA,IACA,YAAY,CAAC;AAAA,IACb,SAAS,CAAC;AAAA,IACV,YAAY,CAAC;AAAA,IACb,SAAS,eAAe,OAAO;AAAA,IAC/B,WAAW,CAAC;AAAA,IACZ;AAAA,IACA,QAAQ;AAAA,IACR,SAAS,CAAC;AAAA,IACV,OAAO,CAAC;AAAA,IACR,YAAY;AAAA,EACd;AAEA,QAAM,OAAO,oBAAI,IAAY;AAE7B,aAAW,CAAC,SAAS,UAAU,KAAK,KAAK,aAAa;AACpD,QAAI,KAAK,IAAI,KAAK,EAAG;AAErB,UAAM,UAAU,OAAO,KAAK,OAAK,EAAE,SAAS,OAAO,CAAC;AAEpD,QAAI,CAAC,QAAS;AAEd,SAAK,IAAI,KAAK;AAEd,kBAAc,UAAU,UAAU,KAAK;AAAA,EACzC;AAEA,SAAO;AACT;AAMA,MAAM,mBAAmB,CAAC,QAA+B;AACvD,aAAW,QAAQ,yBAAyB;AAC1C,UAAM,IAAI,KAAK,KAAK,IAAI;AAExB,QAAI,WAAW,CAAC,EAAG,QAAO;AAAA,EAC5B;AAEA,SAAO;AACT;AAMA,MAAM,oBAAoB,CAAC,QAAwB;AAEjD,MAAI,aAAa;AAEjB,MAAI,WAAW,KAAK,KAAK,gBAAgB,CAAC,KAAK,WAAW,KAAK,KAAK,qBAAqB,CAAC,GAAG;AAC3F,iBAAa;AAAA,EACf,WAAW,WAAW,KAAK,KAAK,WAAW,CAAC,GAAG;AAC7C,iBAAa;AAAA,EACf,WAAW,WAAW,KAAK,KAAK,WAAW,CAAC,KAAK,WAAW,KAAK,KAAK,UAAU,CAAC,GAAG;AAClF,iBAAa;AAAA,EACf;AAGA,QAAM,UAAU,KAAK,KAAK,cAAc;AAExC,MAAI;AACF,QAAI,WAAW,OAAO,GAAG;AACvB,YAAM,MAAM,KAAK,MAAM,aAAa,SAAS,OAAO,CAAC;AAIrD,UAAI,IAAI,SAAS,KAAM,QAAO,GAAG,UAAU;AAAA,IAC7C;AAAA,EACF,QAAQ;AAAA,EAER;AAEA,SAAO;AACT;AAaO,MAAM,sBAAsB,OACjC,KACA,YACyC;AACzC,QAAM,aAAa,iBAAiB,GAAG;AAEvC,MAAI,CAAC,WAAY,QAAO;AAExB,QAAM,cAAc,kBAAkB,GAAG;AAEzC,MAAI;AAEF,UAAM,MAAM,MAAM,OAAO,cAAc,QAAQ,UAAU,CAAC,EAAE;AAC5D,UAAM,MAAO,IAAI,WAAW;AAE5B,QAAI,UAAmB;AAEvB,QAAI,MAAM,QAAQ,GAAG,GAAG;AACtB,gBAAU;AAAA,IACZ,WAAW,eAAe,SAAS;AACjC,gBAAU,MAAM;AAAA,IAClB;AAEA,QAAI,CAAC,MAAM,QAAQ,OAAO,EAAG,QAAO;AAEpC,UAAM,kBAAkB,UACtB,sBAAsB,SAAS,QAAQ,aAAa,QAAQ,YAAY,IACxE;AAEF,WAAO,gBAAgB,iBAAiB,aAAa,UAAU;AAAA,EACjE,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAQA,MAAM,wBAAwB,CAAC,QAAsC;AACnE,QAAM,OAAO,qBAAqB,GAAG;AACrC,QAAM,cAAc,kBAAkB,GAAG;AACzC,QAAM,cAAc,KAAK,sBAAsB,CAAC,GAAG,IAAI,OAAK,0BAA0B,IAAI,CAAC,KAAK,CAAC;AAEjG,SAAO;AAAA,IACL,YAAY;AAAA,IACZ,aAAa,KAAK,cAAc,CAAC,GAAG,IAAI,cAAc;AAAA,IACtD,UAAU,KAAK,WAAW,CAAC,GAAG,IAAI,cAAc;AAAA,IAChD;AAAA,IACA,SAAS,KAAK,WAAW,CAAC;AAAA,IAC1B,YAAY,KAAK,aAAa,CAAC,GAAG,IAAI,cAAc;AAAA,IACpD;AAAA,IACA,QAAQ;AAAA,IACR,UAAU,KAAK,WAAW,CAAC,GAAG,IAAI,cAAc;AAAA,IAChD,QAAQ,KAAK,SAAS,CAAC,GAAG,IAAI,cAAc;AAAA,IAC5C,YAAY,QAAQ,KAAK,UAAU;AAAA,EACrC;AACF;AAIA,MAAM,oBAAoB,CAAC,aAA6C;AACtE,QAAM,EAAE,YAAY,SAAS,YAAY,WAAW,SAAS,OAAO,WAAW,IAAI;AACnF,QAAM,QAAkB,CAAC;AAEzB,QAAM,KAAK,qBAAqB,aAAa,YAAY,UAAU,EAAE;AAErE,MAAI,WAAW,SAAS,EAAG,OAAM,KAAK,qBAAqB,WAAW,KAAK,IAAI,CAAC,EAAE;AAElF,MAAI,QAAQ,SAAS,EAAG,OAAM,KAAK,kBAAkB,QAAQ,KAAK,IAAI,CAAC,EAAE;AAEzE,MAAI,MAAM,SAAS,EAAG,OAAM,KAAK,gBAAgB,MAAM,KAAK,IAAI,CAAC,EAAE;AAEnE,MAAI,UAAU,SAAS,EAAG,OAAM,KAAK,oBAAoB,UAAU,KAAK,IAAI,CAAC,EAAE;AAE/E,MAAI,QAAQ,SAAS,EAAG,OAAM,KAAK,kBAAkB,QAAQ,KAAK,IAAI,CAAC,EAAE;AAEzE,MAAI,WAAW,SAAS,EAAG,OAAM,KAAK,qBAAqB,WAAW,KAAK,IAAI,CAAC,EAAE;AAElF,SAAO;AACT;AAEA,MAAM,yBAAyB,CAAC,eAAmC;AACjE,QAAM,WAAqB,CAAC;AAE5B,MAAI,WAAW,KAAK,OAAK,MAAM,WAAW,MAAM,SAAS,GAAG;AAC1D,aAAS,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAOjB;AAAA,EACC;AAEA,MAAI,WAAW,SAAS,SAAS,GAAG;AAClC,aAAS,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAMjB;AAAA,EACC;AAEA,MAAI,WAAW,SAAS,KAAK,GAAG;AAC9B,aAAS,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA,CAKjB;AAAA,EACC;AAEA,MAAI,WAAW,SAAS,QAAQ,GAAG;AACjC,aAAS,KAAK;AAAA;AAAA;AAAA;AAAA,CAIjB;AAAA,EACC;AAEA,MAAI,WAAW,SAAS,OAAO,GAAG;AAChC,aAAS,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA,CAKjB;AAAA,EACC;AAEA,MAAI,WAAW,SAAS,SAAS,GAAG;AAClC,aAAS,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA,CAKjB;AAAA,EACC;AAEA,SAAO;AACT;AAEA,MAAM,iBAAiB,CAAC,MAAc,WAA0C;AAC9E,MAAI,WAAW,eAAe;AAC5B,WAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQT,IAAI;AAAA,EACJ;AAEA,MAAI,WAAW,UAAU;AACvB,WAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAeT,IAAI;AAAA,EACJ;AAEA,MAAI,WAAW,QAAQ;AACrB,WAAO;AAAA;AAAA;AAAA;AAAA,EAIT,IAAI;AAAA,EACJ;AAEA,SAAO;AACT;AAWO,MAAM,uBAAuB,CAClC,UACA,WACW;AACX,QAAM,EAAE,YAAY,WAAW,aAAa,SAAS,OAAO,WAAW,IAAI;AAE3E,QAAM,eAAe,kBAAkB,QAAQ;AAG/C,QAAM,YAAY,aAChB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IASA;AAGF,QAAM,oBAAoB,uBAAuB,UAAU;AAE3D,QAAM,kBAA4B,CAAC;AAEnC,MAAI,QAAQ,KAAK,OAAK,MAAM,YAAY,MAAM,MAAM,GAAG;AACrD,oBAAgB,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAMxB;AAAA,EACC;AAEA,MAAI,QAAQ,SAAS,YAAY,GAAG;AAClC,oBAAgB,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA,CAKxB;AAAA,EACC;AAGA,QAAM,eAAyB,CAAC;AAEhC,MAAI,MAAM,SAAS,UAAU,GAAG;AAC9B,iBAAa,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA,CAKrB;AAAA,EACC;AAEA,MAAI,MAAM,SAAS,cAAc,KAAK,UAAU,SAAS,cAAc,GAAG;AACxE,iBAAa,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA,CAKrB;AAAA,EACC;AAGA,QAAM,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA,iBAKE,WAAW;AAAA;AAAA;AAAA;AAAA,EAI1B,aAAa,KAAK,IAAI,CAAC;AAAA;AAAA;AAAA,EAGvB,SAAS,GAAG,kBAAkB,KAAK,EAAE,CAAC,GAAG,gBAAgB,KAAK,EAAE,CAAC,GAAG,aAAa,KAAK,EAAE,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUzF,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAWX,SAAO,eAAe,MAAM,MAAM;AACpC;AAIA,MAAM,iBAAiB,CAAC,UAAkB,SAAiB,UAA4B;AACrF,MAAI,WAAW,QAAQ,KAAK,CAAC,MAAO,QAAO;AAE3C,YAAU,KAAK,UAAU,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;AAEnD,gBAAc,UAAU,SAAS,OAAO;AAExC,SAAO;AACT;AAIA,MAAM,4BAA4B;AAClC,MAAM,iBAAiB;AACvB,MAAM,wBAAwB;AAC9B,MAAM,sBAAwB;AAE9B,MAAM,sBAAsB,CAAC,SAC3B,GAAG,qBAAqB;AAAA,EAAK,IAAI;AAAA,EAAK,mBAAmB;AAc3D,MAAM,2BAA2B,CAC/B,UACA,MACA,OACA,OACA,kBAAkB,UACO;AACzB,QAAM,UAAU,oBAAoB,IAAI;AAExC,MAAI,CAAC,WAAW,QAAQ,GAAG;AACzB,QAAI,CAAC,mBAAmB,MAAO,QAAO;AAEtC,kBAAc,UAAU;AAAA;AAAA,EAA2B,OAAO;AAAA,GAAM,OAAO;AAEvE,WAAO;AAAA,EACT;AAEA,QAAM,WAAW,aAAa,UAAU,OAAO;AAE/C,MAAI,OAAO;AACT,WAAO,SAAS,SAAS,OAAO,IAAI,YAAY;AAAA,EAClD;AAEA,MAAI,SAAS,SAAS,qBAAqB,GAAG;AAC5C,QAAI,CAAC,MAAO,QAAO;AAEnB,UAAM,UAAU,SAAS;AAAA,MACvB,IAAI,OAAO,GAAG,qBAAqB,aAAa,mBAAmB,IAAI,GAAG;AAAA,MAAG;AAAA,IAC/E;AAEA,kBAAc,UAAU,SAAS,OAAO;AAExC,WAAO;AAAA,EACT;AAEA,gBAAc,UAAU,GAAG,QAAQ;AAAA,EAAK,OAAO;AAAA,GAAM,OAAO;AAE5D,SAAO;AACT;AAIA,MAAM,sBAAsB,CAAC,UAAgC;AAAA,EAC3D,OAAO,KAAK,SAAS;AAAA,EACrB,gBAAgB,KAAK,kBAAkB;AAAA,EACvC,KAAK,KAAK,OAAO,QAAQ,IAAI;AAAA,EAC7B,OAAO,KAAK,SAAS;AAAA,EACrB,eAAe,KAAK,iBAAiB;AACvC;AAEA,MAAM,wBAAwB,CAAC,QAAqD;AAClF,MAAI,WAAW,KAAK,KAAK,gBAAgB,CAAC,KAAK,WAAW,KAAK,KAAK,qBAAqB,CAAC,GAAG;AAC3F,WAAO,EAAE,MAAM,CAAC,QAAQ,UAAU,OAAO,GAAG,SAAS,OAAO;AAAA,EAC9D;AAEA,MAAI,WAAW,KAAK,KAAK,WAAW,CAAC,GAAG;AACtC,WAAO,EAAE,MAAM,CAAC,QAAQ,UAAU,OAAO,GAAG,SAAS,OAAO;AAAA,EAC9D;AAEA,MAAI,WAAW,KAAK,KAAK,WAAW,CAAC,KAAK,WAAW,KAAK,KAAK,UAAU,CAAC,GAAG;AAC3E,WAAO,EAAE,MAAM,CAAC,UAAU,OAAO,GAAG,SAAS,OAAO;AAAA,EACtD;AAEA,SAAO,EAAE,MAAM,CAAC,UAAU,OAAO,GAAG,SAAS,MAAM;AACrD;AAEA,MAAM,wBAAwB,CAC5B,KACA,OACA,UACyB;AACzB,QAAM,aAAa,KAAK,KAAK,WAAW;AACxC,QAAM,SAAS,sBAAsB,GAAG;AACxC,MAAI,SAA2E,CAAC;AAEhF,MAAI,WAAW,UAAU,GAAG;AAC1B,QAAI;AACF,eAAS,KAAK,MAAM,aAAa,YAAY,MAAM,CAAC;AAAA,IACtD,QAAQ;AACN,YAAM,IAAI,MAAM,wEAAwE;AAAA,IAC1F;AAAA,EACF;AAEA,QAAM,UAAU,OAAO,YAAY;AACnC,QAAM,YAAY,KAAK,UAAU,OAAO,MAAM,KAAK,UAAU,MAAM;AAEnE,MAAI,MAAO,QAAO,YAAY,YAAY;AAE1C,MAAI,WAAW,CAAC,MAAO,QAAO;AAE9B,SAAO,aAAa;AAAA,IAClB,GAAG,OAAO;AAAA,IACV,QAAQ;AAAA,EACV;AAEA,gBAAc,YAAY,GAAG,KAAK,UAAU,QAAQ,MAAM,CAAC,CAAC;AAAA,CAAI;AAEhE,SAAO;AACT;AAkBA,MAAM,qBAAqB,CACzB,QACA,KACA,UACA,OACA,UACmE;AACnE,QAAM,cAAc,KAAK,KAAK,OAAO,YAAY;AAEjD,MAAI,CAAC,WAAW,WAAW,EAAG,QAAO;AAErC,QAAM,SAAS,OAAO,gBAAgB,MAAM,cAAc,KAAK,aAAa,OAAO,WAAW;AAC9F,QAAM,WAAW,KAAK,QAAQ,OAAO,SAAS;AAC9C,QAAM,UAAU,qBAAqB,UAAU,OAAO,MAAM;AAE5D,MAAI,OAAO;AACT,UAAM,WAAW,WAAW,QAAQ,KAAK,aAAa,UAAU,OAAO,MAAM;AAE7E,WAAO,EAAE,UAAU,QAAQ,WAAW,YAAY,QAAQ;AAAA,EAC5D;AAEA,SAAO;AAAA,IACL;AAAA,IACA,QAAQ,eAAe,UAAU,SAAS,KAAK,IAAI,YAAY;AAAA,EACjE;AACF;AAEO,MAAM,sBAAsB,OACjC,OAA6B,CAAC,MACG;AACjC,QAAM,EAAE,OAAO,gBAAgB,KAAK,OAAO,cAAc,IAAI,oBAAoB,IAAI;AACrF,QAAM,UAAoB,CAAC;AAC3B,QAAM,UAAoB,CAAC;AAC3B,QAAM,QAAkB,CAAC;AAEzB,QAAM,mBAAmB,MAAM,oBAAoB,GAAG;AACtD,QAAM,WAAW,oBAAoB,sBAAsB,GAAG;AAC9D,QAAM,YAAY,qBAAqB,UAAU,OAAO;AAExD,QAAM,sBAAsB,CAAC,UAAkB,WAAuC;AACpF,YAAQ,QAAQ;AAAA,MACd,KAAK;AACH,gBAAQ,KAAK,QAAQ;AAErB;AAAA,MAEF,KAAK;AACH,cAAM,KAAK,QAAQ;AAEnB;AAAA,MAEF,KAAK;AACH,gBAAQ,KAAK,QAAQ;AAErB;AAAA,IACJ;AAAA,EACF;AAGA,QAAM,cAAc,KAAK,KAAK,yBAAyB;AAEvD,sBAAoB,aAAa,yBAAyB,aAAa,WAAW,OAAO,KAAK,CAAC;AAG/F,QAAM,eAAe,KAAK,KAAK,cAAc;AAE7C,sBAAoB,cAAc,yBAAyB,cAAc,WAAW,OAAO,OAAO,cAAc,CAAC;AAEjH,MAAI,eAAe;AACjB,UAAM,gBAAgB,KAAK,KAAK,WAAW;AAE3C,wBAAoB,eAAe,sBAAsB,KAAK,OAAO,KAAK,CAAC;AAAA,EAC7E;AAGA,aAAW,UAAU,eAAe;AAClC,UAAM,YAAY,mBAAmB,QAAQ,KAAK,UAAU,OAAO,KAAK;AAExE,QAAI,UAAW,qBAAoB,UAAU,UAAU,UAAU,MAAM;AAAA,EACzE;AAEA,SAAO,EAAE,SAAS,OAAO,QAAQ;AACnC;AAgBA,MAAM,4BAA4B,CAAC,WAAqC;AAAA,EACtE,OAAO,MAAM,SAAS;AAAA,EACtB,gBAAgB,MAAM,kBAAkB;AAAA,EACxC,eAAe,MAAM,iBAAiB;AACxC;AAEA,MAAM,6BAA6B,CACjC,OACA,QACA,QACS;AACT,MAAI,OAAO;AACT,eAAW,QAAQ,OAAO,SAAS;AACjC,cAAQ,IAAI,sBAAiB,KAAK,QAAQ,MAAM,KAAK,EAAE,CAAC,EAAE;AAAA,IAC5D;AAEA,eAAW,QAAQ,OAAO,OAAO;AAC/B,cAAQ,IAAI,4BAAuB,KAAK,QAAQ,MAAM,KAAK,EAAE,CAAC,EAAE;AAAA,IAClE;AAEA,QAAI,OAAO,MAAM,SAAS,GAAG;AAC3B,cAAQ,IAAI;AAAA,gBAAS,OAAO,MAAM,MAAM,oFAAoF;AAE5H,cAAQ,WAAW;AAAA,IACrB,OAAO;AACL,cAAQ,IAAI,mDAA4C;AAAA,IAC1D;AAEA;AAAA,EACF;AAEA,aAAW,QAAQ,OAAO,SAAS;AACjC,YAAQ,IAAI,mBAAc,KAAK,QAAQ,MAAM,KAAK,EAAE,CAAC,EAAE;AAAA,EACzD;AAEA,aAAW,QAAQ,OAAO,SAAS;AACjC,YAAQ,IAAI,2CAAiC,KAAK,QAAQ,MAAM,KAAK,EAAE,CAAC,EAAE;AAAA,EAC5E;AAEA,MAAI,OAAO,QAAQ,SAAS,GAAG;AAC7B,YAAQ,IAAI;AAAA,sBAAkB,OAAO,QAAQ,MAAM,iBAAiB;AAEpE,YAAQ,IAAI,0EAA2E;AAAA,EACzF;AAEA,MAAI,OAAO,QAAQ,SAAS,GAAG;AAC7B,YAAQ,IAAI,sEAA+D;AAAA,EAC7E;AACF;AAEO,MAAM,sBAAsB,OACjC,MAAc,QAAQ,IAAI,GAC1B,QAAQ,OACR,QAAkC,CAAC,MACjB;AAClB,QAAM,EAAE,OAAO,gBAAgB,cAAc,IAAI,0BAA0B,KAAK;AAEhF,UAAQ,IAAI,QAAQ,+CAAwC,4CAAqC;AAEjG,QAAM,aAAa,iBAAiB,GAAG;AAEvC,MAAI,YAAY;AACd,YAAQ,IAAI,kCAA2B,WAAW,QAAQ,MAAM,KAAK,EAAE,CAAC,EAAE;AAAA,EAC5E,OAAO;AACL,YAAQ,IAAI,wFAAyE;AAAA,EACvF;AAEA,QAAM,SAAS,MAAM,oBAAoB,EAAE,OAAO,gBAAgB,KAAK,OAAO,cAAc,CAAC;AAE7F,MAAI,OAAO,QAAQ,WAAW,KAAK,OAAO,QAAQ,WAAW,KAAK,OAAO,MAAM,WAAW,GAAG;AAC3F,YAAQ,IAAI,8JAAoJ;AAEhK,YAAQ,IAAI,uFAAuF;AAEnG;AAAA,EACF;AAEA,6BAA2B,OAAO,QAAQ,GAAG;AAC/C;","names":[]}
|
package/dist/cli-advanced.d.ts
CHANGED
|
@@ -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;
|
package/dist/cli-advanced.js
CHANGED
|
@@ -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
|
-
|
|
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 = (
|
|
155
|
+
const readResolvedManifest = (basePath, packageName) => {
|
|
130
156
|
try {
|
|
131
|
-
|
|
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
|
|
149
|
-
|
|
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
|
|
187
|
+
const rootManifestPath = join(cwd, "package.json");
|
|
188
|
+
const rootManifest = readJson(rootManifestPath);
|
|
155
189
|
const declared = getDependencyVersions(rootManifest);
|
|
156
|
-
const
|
|
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
|
-
|
|
193
|
+
consumerNodeRange,
|
|
194
|
+
eslint: readResolvedManifest(rootManifestPath, "eslint")?.manifest.version ?? null,
|
|
160
195
|
node: process.versions.node,
|
|
161
|
-
typescript: readResolvedManifest(
|
|
196
|
+
typescript: readResolvedManifest(rootManifestPath, "typescript")?.manifest.version ?? null
|
|
162
197
|
};
|
|
163
198
|
const packages = configPackages.map((name) => {
|
|
164
|
-
const
|
|
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
|