claude-skill-lint 0.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/LICENSE +21 -0
- package/README.md +383 -0
- package/bin/cli.js +10 -0
- package/dist/changed-files.d.ts +20 -0
- package/dist/changed-files.d.ts.map +1 -0
- package/dist/changed-files.js +49 -0
- package/dist/changed-files.js.map +1 -0
- package/dist/classify.d.ts +13 -0
- package/dist/classify.d.ts.map +1 -0
- package/dist/classify.js +72 -0
- package/dist/classify.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +124 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +19 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +103 -0
- package/dist/config.js.map +1 -0
- package/dist/detect-format.d.ts +18 -0
- package/dist/detect-format.d.ts.map +1 -0
- package/dist/detect-format.js +137 -0
- package/dist/detect-format.js.map +1 -0
- package/dist/extract.d.ts +19 -0
- package/dist/extract.d.ts.map +1 -0
- package/dist/extract.js +197 -0
- package/dist/extract.js.map +1 -0
- package/dist/graph.d.ts +21 -0
- package/dist/graph.d.ts.map +1 -0
- package/dist/graph.js +94 -0
- package/dist/graph.js.map +1 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +30 -0
- package/dist/index.js.map +1 -0
- package/dist/init.d.ts +17 -0
- package/dist/init.d.ts.map +1 -0
- package/dist/init.js +45 -0
- package/dist/init.js.map +1 -0
- package/dist/lint.d.ts +16 -0
- package/dist/lint.d.ts.map +1 -0
- package/dist/lint.js +152 -0
- package/dist/lint.js.map +1 -0
- package/dist/profiles.d.ts +37 -0
- package/dist/profiles.d.ts.map +1 -0
- package/dist/profiles.js +161 -0
- package/dist/profiles.js.map +1 -0
- package/dist/reporter.d.ts +21 -0
- package/dist/reporter.d.ts.map +1 -0
- package/dist/reporter.js +110 -0
- package/dist/reporter.js.map +1 -0
- package/dist/types.d.ts +61 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +6 -0
- package/dist/types.js.map +1 -0
- package/dist/validate-frontmatter.d.ts +41 -0
- package/dist/validate-frontmatter.d.ts.map +1 -0
- package/dist/validate-frontmatter.js +409 -0
- package/dist/validate-frontmatter.js.map +1 -0
- package/dist/validate-graph.d.ts +46 -0
- package/dist/validate-graph.d.ts.map +1 -0
- package/dist/validate-graph.js +651 -0
- package/dist/validate-graph.js.map +1 -0
- package/dist/validate-manifest.d.ts +22 -0
- package/dist/validate-manifest.d.ts.map +1 -0
- package/dist/validate-manifest.js +365 -0
- package/dist/validate-manifest.js.map +1 -0
- package/package.json +64 -0
- package/schemas/agent.schema.json +58 -0
- package/schemas/command.schema.json +60 -0
- package/schemas/skill.schema.json +74 -0
package/dist/cli.js
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import yargs from 'yargs';
|
|
2
|
+
import { hideBin } from 'yargs/helpers';
|
|
3
|
+
import { runLint } from './lint.js';
|
|
4
|
+
import { runGraph } from './graph.js';
|
|
5
|
+
import { runInit } from './init.js';
|
|
6
|
+
import { ConfigError } from './config.js';
|
|
7
|
+
import { ChangedFilesError } from './changed-files.js';
|
|
8
|
+
const cli = yargs(hideBin(process.argv))
|
|
9
|
+
.scriptName('claude-skill-lint')
|
|
10
|
+
.usage('$0 <command> [options]')
|
|
11
|
+
.command('lint [paths..]', 'Validate skill files for structural correctness', (yargs) => yargs
|
|
12
|
+
.positional('paths', {
|
|
13
|
+
describe: 'File or directory paths to lint',
|
|
14
|
+
type: 'string',
|
|
15
|
+
array: true,
|
|
16
|
+
})
|
|
17
|
+
.option('level', {
|
|
18
|
+
alias: 'l',
|
|
19
|
+
describe: 'Quality level to enforce (0-3)',
|
|
20
|
+
type: 'number',
|
|
21
|
+
default: 0,
|
|
22
|
+
})
|
|
23
|
+
.option('changed-only', {
|
|
24
|
+
describe: 'Only lint files changed since base ref',
|
|
25
|
+
type: 'boolean',
|
|
26
|
+
default: false,
|
|
27
|
+
})
|
|
28
|
+
.option('base', {
|
|
29
|
+
describe: 'Git base ref for --changed-only',
|
|
30
|
+
type: 'string',
|
|
31
|
+
default: 'origin/main',
|
|
32
|
+
})
|
|
33
|
+
.option('format', {
|
|
34
|
+
alias: 'f',
|
|
35
|
+
describe: 'Output format',
|
|
36
|
+
choices: ['terminal', 'json', 'github'],
|
|
37
|
+
default: 'terminal',
|
|
38
|
+
})
|
|
39
|
+
.option('strict', {
|
|
40
|
+
describe: 'Treat warnings as errors',
|
|
41
|
+
type: 'boolean',
|
|
42
|
+
default: false,
|
|
43
|
+
})
|
|
44
|
+
.option('ratchet', {
|
|
45
|
+
describe: 'Enforce anti-regression (never go below current level)',
|
|
46
|
+
type: 'boolean',
|
|
47
|
+
default: false,
|
|
48
|
+
}), async (argv) => {
|
|
49
|
+
try {
|
|
50
|
+
const exitCode = await runLint({
|
|
51
|
+
paths: argv.paths,
|
|
52
|
+
level: argv.level,
|
|
53
|
+
changedOnly: argv.changedOnly,
|
|
54
|
+
base: argv.base,
|
|
55
|
+
format: argv.format,
|
|
56
|
+
strict: argv.strict,
|
|
57
|
+
ratchet: argv.ratchet,
|
|
58
|
+
});
|
|
59
|
+
process.exit(exitCode);
|
|
60
|
+
}
|
|
61
|
+
catch (err) {
|
|
62
|
+
if (err instanceof ConfigError || err instanceof ChangedFilesError) {
|
|
63
|
+
process.stderr.write(`${err.message}\n`);
|
|
64
|
+
process.exit(2);
|
|
65
|
+
}
|
|
66
|
+
throw err;
|
|
67
|
+
}
|
|
68
|
+
})
|
|
69
|
+
.command('graph [paths..]', 'Validate cross-file references and dependency graph', (yargs) => yargs
|
|
70
|
+
.positional('paths', {
|
|
71
|
+
describe: 'File or directory paths to analyze',
|
|
72
|
+
type: 'string',
|
|
73
|
+
array: true,
|
|
74
|
+
})
|
|
75
|
+
.option('format', {
|
|
76
|
+
alias: 'f',
|
|
77
|
+
describe: 'Output format',
|
|
78
|
+
choices: ['terminal', 'json', 'github'],
|
|
79
|
+
default: 'terminal',
|
|
80
|
+
})
|
|
81
|
+
.option('strict', {
|
|
82
|
+
describe: 'Treat warnings as errors',
|
|
83
|
+
type: 'boolean',
|
|
84
|
+
default: false,
|
|
85
|
+
}), async (argv) => {
|
|
86
|
+
try {
|
|
87
|
+
const exitCode = await runGraph({
|
|
88
|
+
paths: argv.paths,
|
|
89
|
+
format: argv.format,
|
|
90
|
+
strict: argv.strict,
|
|
91
|
+
});
|
|
92
|
+
process.exit(exitCode);
|
|
93
|
+
}
|
|
94
|
+
catch (err) {
|
|
95
|
+
if (err instanceof ConfigError) {
|
|
96
|
+
process.stderr.write(`${err.message}\n`);
|
|
97
|
+
process.exit(2);
|
|
98
|
+
}
|
|
99
|
+
throw err;
|
|
100
|
+
}
|
|
101
|
+
})
|
|
102
|
+
.command('promote <path>', 'Promote a skill file to the next quality level', (yargs) => yargs.positional('path', {
|
|
103
|
+
describe: 'Path to the skill file to promote',
|
|
104
|
+
type: 'string',
|
|
105
|
+
demandOption: true,
|
|
106
|
+
}), () => {
|
|
107
|
+
// Stub handler — exits 0
|
|
108
|
+
process.exit(0);
|
|
109
|
+
})
|
|
110
|
+
.command('init', 'Create a .skill-lint.yaml configuration file', (yargs) => yargs.option('force', {
|
|
111
|
+
describe: 'Overwrite existing .skill-lint.yaml',
|
|
112
|
+
type: 'boolean',
|
|
113
|
+
default: false,
|
|
114
|
+
}), (argv) => {
|
|
115
|
+
const exitCode = runInit(process.cwd(), { force: argv.force });
|
|
116
|
+
process.exit(exitCode);
|
|
117
|
+
})
|
|
118
|
+
.demandCommand(1, 'You must specify a command')
|
|
119
|
+
.strict()
|
|
120
|
+
.help()
|
|
121
|
+
.alias('h', 'help')
|
|
122
|
+
.alias('v', 'version');
|
|
123
|
+
await cli.parseAsync();
|
|
124
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Config loader for .skill-lint.yaml files.
|
|
3
|
+
* Loads user configuration and deep-merges with defaults.
|
|
4
|
+
*/
|
|
5
|
+
import type { Config } from './types.js';
|
|
6
|
+
/** Error thrown when the config file contains invalid YAML. */
|
|
7
|
+
export declare class ConfigError extends Error {
|
|
8
|
+
constructor(message: string);
|
|
9
|
+
}
|
|
10
|
+
/** Returns a fresh copy of the default configuration. */
|
|
11
|
+
export declare function getDefaults(): Config;
|
|
12
|
+
/**
|
|
13
|
+
* Load configuration from .skill-lint.yaml in the given root directory.
|
|
14
|
+
* Returns defaults if the file is missing or empty.
|
|
15
|
+
* Throws ConfigError if the file contains invalid YAML.
|
|
16
|
+
* Unknown keys are silently ignored.
|
|
17
|
+
*/
|
|
18
|
+
export declare function loadConfig(rootDir: string): Config;
|
|
19
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAIzC,+DAA+D;AAC/D,qBAAa,WAAY,SAAQ,KAAK;gBACxB,OAAO,EAAE,MAAM;CAI5B;AAED,yDAAyD;AACzD,wBAAgB,WAAW,IAAI,MAAM,CAiBpC;AAkCD;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CA8ClD"}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Config loader for .skill-lint.yaml files.
|
|
3
|
+
* Loads user configuration and deep-merges with defaults.
|
|
4
|
+
*/
|
|
5
|
+
import { readFileSync, existsSync, statSync } from 'node:fs';
|
|
6
|
+
import { join } from 'node:path';
|
|
7
|
+
import { parse } from 'yaml';
|
|
8
|
+
const CONFIG_FILENAME = '.skill-lint.yaml';
|
|
9
|
+
/** Error thrown when the config file contains invalid YAML. */
|
|
10
|
+
export class ConfigError extends Error {
|
|
11
|
+
constructor(message) {
|
|
12
|
+
super(message);
|
|
13
|
+
this.name = 'ConfigError';
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
/** Returns a fresh copy of the default configuration. */
|
|
17
|
+
export function getDefaults() {
|
|
18
|
+
return {
|
|
19
|
+
skills_root: '.',
|
|
20
|
+
default_level: 0,
|
|
21
|
+
levels: {},
|
|
22
|
+
tools: { mcp_pattern: 'mcp__*', custom: [] },
|
|
23
|
+
models: ['opus', 'sonnet', 'haiku'],
|
|
24
|
+
limits: { max_file_size: 15360 },
|
|
25
|
+
ignore: ['**/README.md'],
|
|
26
|
+
prefixes: 'PREFIXES.md',
|
|
27
|
+
graph: {
|
|
28
|
+
warn_orphans: true,
|
|
29
|
+
warn_fanout_above: 50000,
|
|
30
|
+
detect_cycles: true,
|
|
31
|
+
detect_duplicates: true,
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Deep-merge source into target.
|
|
37
|
+
* For objects: recursively merge nested keys.
|
|
38
|
+
* For arrays: source value replaces target (no concatenation).
|
|
39
|
+
*/
|
|
40
|
+
function deepMerge(target, source) {
|
|
41
|
+
const result = { ...target };
|
|
42
|
+
for (const key of Object.keys(source)) {
|
|
43
|
+
const targetVal = target[key];
|
|
44
|
+
const sourceVal = source[key];
|
|
45
|
+
if (sourceVal !== null &&
|
|
46
|
+
typeof sourceVal === 'object' &&
|
|
47
|
+
!Array.isArray(sourceVal) &&
|
|
48
|
+
targetVal !== null &&
|
|
49
|
+
typeof targetVal === 'object' &&
|
|
50
|
+
!Array.isArray(targetVal)) {
|
|
51
|
+
result[key] = deepMerge(targetVal, sourceVal);
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
result[key] = sourceVal;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return result;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Load configuration from .skill-lint.yaml in the given root directory.
|
|
61
|
+
* Returns defaults if the file is missing or empty.
|
|
62
|
+
* Throws ConfigError if the file contains invalid YAML.
|
|
63
|
+
* Unknown keys are silently ignored.
|
|
64
|
+
*/
|
|
65
|
+
export function loadConfig(rootDir) {
|
|
66
|
+
const defaults = getDefaults();
|
|
67
|
+
const configPath = join(rootDir, CONFIG_FILENAME);
|
|
68
|
+
if (!existsSync(configPath)) {
|
|
69
|
+
return defaults;
|
|
70
|
+
}
|
|
71
|
+
const stat = statSync(configPath);
|
|
72
|
+
if (stat.size === 0) {
|
|
73
|
+
return defaults;
|
|
74
|
+
}
|
|
75
|
+
const raw = readFileSync(configPath, 'utf-8');
|
|
76
|
+
let parsed;
|
|
77
|
+
try {
|
|
78
|
+
parsed = parse(raw);
|
|
79
|
+
}
|
|
80
|
+
catch (err) {
|
|
81
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
82
|
+
throw new ConfigError(`Invalid YAML in ${CONFIG_FILENAME}: ${msg}`);
|
|
83
|
+
}
|
|
84
|
+
// parse() returns null for empty documents (e.g., only comments)
|
|
85
|
+
if (parsed == null || typeof parsed !== 'object') {
|
|
86
|
+
return defaults;
|
|
87
|
+
}
|
|
88
|
+
const merged = deepMerge(defaults, parsed);
|
|
89
|
+
// Pick only known Config keys from the merged result
|
|
90
|
+
return {
|
|
91
|
+
skills_root: merged.skills_root,
|
|
92
|
+
default_level: merged.default_level,
|
|
93
|
+
levels: merged.levels,
|
|
94
|
+
tools: merged.tools,
|
|
95
|
+
models: merged.models,
|
|
96
|
+
limits: merged.limits,
|
|
97
|
+
ignore: merged.ignore,
|
|
98
|
+
prefixes: merged.prefixes,
|
|
99
|
+
graph: merged.graph,
|
|
100
|
+
...(merged.format !== undefined ? { format: merged.format } : {}),
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-detect repository format based on filesystem signals.
|
|
3
|
+
* Determines whether a repo uses legacy-commands, plugin, multi-plugin, or project-skills format.
|
|
4
|
+
*/
|
|
5
|
+
import type { Config, RepoFormat } from './types.js';
|
|
6
|
+
/**
|
|
7
|
+
* Detect the repository format for skill file organization.
|
|
8
|
+
*
|
|
9
|
+
* Priority order (first match wins):
|
|
10
|
+
* 1. Config override (format field in .skill-lint.yaml)
|
|
11
|
+
* 2. multi-plugin: marketplace.json + plugins with plugin.json
|
|
12
|
+
* 3. plugin: marketplace.json + skills/SKILL.md files
|
|
13
|
+
* 4. project-skills: .claude/skills/{name}/SKILL.md
|
|
14
|
+
* 5. legacy-commands: commands/, agents/, or context/ directories
|
|
15
|
+
* 6. Fallback: legacy-commands with stderr warning
|
|
16
|
+
*/
|
|
17
|
+
export declare function detectFormat(rootDir: string, config: Config): RepoFormat;
|
|
18
|
+
//# sourceMappingURL=detect-format.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"detect-format.d.ts","sourceRoot":"","sources":["../src/detect-format.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAIrD;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,UAAU,CAwDxE"}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-detect repository format based on filesystem signals.
|
|
3
|
+
* Determines whether a repo uses legacy-commands, plugin, multi-plugin, or project-skills format.
|
|
4
|
+
*/
|
|
5
|
+
import { existsSync, readdirSync, readFileSync } from 'node:fs';
|
|
6
|
+
import { join } from 'node:path';
|
|
7
|
+
import { ConfigError } from './config.js';
|
|
8
|
+
const VALID_FORMATS = ['legacy-commands', 'plugin', 'multi-plugin', 'project-skills'];
|
|
9
|
+
/**
|
|
10
|
+
* Detect the repository format for skill file organization.
|
|
11
|
+
*
|
|
12
|
+
* Priority order (first match wins):
|
|
13
|
+
* 1. Config override (format field in .skill-lint.yaml)
|
|
14
|
+
* 2. multi-plugin: marketplace.json + plugins with plugin.json
|
|
15
|
+
* 3. plugin: marketplace.json + skills/SKILL.md files
|
|
16
|
+
* 4. project-skills: .claude/skills/{name}/SKILL.md
|
|
17
|
+
* 5. legacy-commands: commands/, agents/, or context/ directories
|
|
18
|
+
* 6. Fallback: legacy-commands with stderr warning
|
|
19
|
+
*/
|
|
20
|
+
export function detectFormat(rootDir, config) {
|
|
21
|
+
// AC-4 / AC-9: Config override takes precedence
|
|
22
|
+
if (config.format !== undefined) {
|
|
23
|
+
if (!VALID_FORMATS.includes(config.format)) {
|
|
24
|
+
throw new ConfigError(`Invalid format "${config.format}" in .skill-lint.yaml. Valid values: ${VALID_FORMATS.join(', ')}`);
|
|
25
|
+
}
|
|
26
|
+
return config.format;
|
|
27
|
+
}
|
|
28
|
+
// Check for .claude-plugin/marketplace.json
|
|
29
|
+
const marketplacePath = join(rootDir, '.claude-plugin', 'marketplace.json');
|
|
30
|
+
const hasMarketplace = existsSync(marketplacePath);
|
|
31
|
+
if (hasMarketplace) {
|
|
32
|
+
// AC-8: Validate marketplace.json is parseable
|
|
33
|
+
let marketplaceValid = true;
|
|
34
|
+
try {
|
|
35
|
+
const raw = readFileSync(marketplacePath, 'utf-8');
|
|
36
|
+
JSON.parse(raw);
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
process.stderr.write(`Warning: .claude-plugin/marketplace.json contains malformed JSON, skipping plugin detection\n`);
|
|
40
|
+
marketplaceValid = false;
|
|
41
|
+
}
|
|
42
|
+
if (marketplaceValid) {
|
|
43
|
+
// AC-1: Check for multi-plugin (plugins/*/.claude-plugin/plugin.json)
|
|
44
|
+
if (hasMultiPlugin(rootDir)) {
|
|
45
|
+
return 'multi-plugin';
|
|
46
|
+
}
|
|
47
|
+
// AC-2: Check for plugin format (skills/*/SKILL.md)
|
|
48
|
+
if (hasSkillFiles(rootDir)) {
|
|
49
|
+
return 'plugin';
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
// project-skills: .claude/skills/*/SKILL.md (priority 3, after plugin formats)
|
|
54
|
+
if (hasProjectSkills(rootDir)) {
|
|
55
|
+
return 'project-skills';
|
|
56
|
+
}
|
|
57
|
+
// AC-3 / AC-7: Legacy commands detection (only when plugin detection didn't match)
|
|
58
|
+
if (hasLegacyDirs(rootDir)) {
|
|
59
|
+
return 'legacy-commands';
|
|
60
|
+
}
|
|
61
|
+
// AC-5: Fallback with warning
|
|
62
|
+
process.stderr.write(`Warning: No recognized repo format signals found, defaulting to legacy-commands\n`);
|
|
63
|
+
return 'legacy-commands';
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Check if any subdirectory of plugins/ contains .claude-plugin/plugin.json.
|
|
67
|
+
*/
|
|
68
|
+
function hasMultiPlugin(rootDir) {
|
|
69
|
+
const pluginsDir = join(rootDir, 'plugins');
|
|
70
|
+
if (!existsSync(pluginsDir))
|
|
71
|
+
return false;
|
|
72
|
+
let entries;
|
|
73
|
+
try {
|
|
74
|
+
entries = readdirSync(pluginsDir, { withFileTypes: true })
|
|
75
|
+
.filter((e) => e.isDirectory())
|
|
76
|
+
.map((e) => e.name);
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
return entries.some((name) => existsSync(join(pluginsDir, name, '.claude-plugin', 'plugin.json')));
|
|
82
|
+
}
|
|
83
|
+
/** Check if any subdirectory of .claude/skills/ contains a SKILL.md file. */
|
|
84
|
+
function hasProjectSkills(rootDir) {
|
|
85
|
+
const skillsDir = join(rootDir, '.claude', 'skills');
|
|
86
|
+
if (!existsSync(skillsDir))
|
|
87
|
+
return false;
|
|
88
|
+
let entries;
|
|
89
|
+
try {
|
|
90
|
+
entries = readdirSync(skillsDir, { withFileTypes: true })
|
|
91
|
+
.filter((e) => e.isDirectory())
|
|
92
|
+
.map((e) => e.name);
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
return entries.some((name) => existsSync(join(skillsDir, name, 'SKILL.md')));
|
|
98
|
+
}
|
|
99
|
+
/** Check if any subdirectory of skills/ contains a SKILL.md file. */
|
|
100
|
+
function hasSkillFiles(rootDir) {
|
|
101
|
+
const skillsDir = join(rootDir, 'skills');
|
|
102
|
+
if (!existsSync(skillsDir))
|
|
103
|
+
return false;
|
|
104
|
+
let entries;
|
|
105
|
+
try {
|
|
106
|
+
entries = readdirSync(skillsDir, { withFileTypes: true })
|
|
107
|
+
.filter((e) => e.isDirectory())
|
|
108
|
+
.map((e) => e.name);
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
return entries.some((name) => existsSync(join(skillsDir, name, 'SKILL.md')));
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Check if legacy command directories exist at repo root,
|
|
117
|
+
* or nested one level deep inside suite directories (monorepo layout).
|
|
118
|
+
*/
|
|
119
|
+
function hasLegacyDirs(rootDir) {
|
|
120
|
+
const legacyDirs = ['commands', 'agents', 'context'];
|
|
121
|
+
// Check root-level legacy dirs
|
|
122
|
+
if (legacyDirs.some((dir) => existsSync(join(rootDir, dir)))) {
|
|
123
|
+
return true;
|
|
124
|
+
}
|
|
125
|
+
// Check for suite-monorepo pattern: {suite}/commands|agents|context/
|
|
126
|
+
let entries;
|
|
127
|
+
try {
|
|
128
|
+
entries = readdirSync(rootDir, { withFileTypes: true })
|
|
129
|
+
.filter((e) => e.isDirectory() && !e.name.startsWith('.') && e.name !== 'node_modules')
|
|
130
|
+
.map((e) => e.name);
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
return false;
|
|
134
|
+
}
|
|
135
|
+
return entries.some((suite) => legacyDirs.some((dir) => existsSync(join(rootDir, suite, dir))));
|
|
136
|
+
}
|
|
137
|
+
//# sourceMappingURL=detect-format.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"detect-format.js","sourceRoot":"","sources":["../src/detect-format.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type ExtractResult, type RepoFormat } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Extract frontmatter and synthetic metadata from a single markdown file.
|
|
4
|
+
*
|
|
5
|
+
* Returns a structured ExtractResult — never throws. Parse errors are
|
|
6
|
+
* captured in the `errors` array.
|
|
7
|
+
*/
|
|
8
|
+
export declare function extractFile(filePath: string): ExtractResult;
|
|
9
|
+
/**
|
|
10
|
+
* Extract frontmatter from all markdown files matching the given glob
|
|
11
|
+
* patterns. Returns one ExtractResult per file.
|
|
12
|
+
*
|
|
13
|
+
* When `format` is provided, overrides `patterns` with format-specific
|
|
14
|
+
* discovery globs rooted at `patterns[0]`'s parent (the skills root).
|
|
15
|
+
*
|
|
16
|
+
* Returns an empty array when no files match (AC-6).
|
|
17
|
+
*/
|
|
18
|
+
export declare function extractAll(patterns: string[], ignore?: string[], format?: RepoFormat): Promise<ExtractResult[]>;
|
|
19
|
+
//# sourceMappingURL=extract.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extract.d.ts","sourceRoot":"","sources":["../src/extract.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,KAAK,aAAa,EAAmB,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AAElF;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,aAAa,CAoE3D;AA0DD;;;;;;;;GAQG;AACH,wBAAsB,UAAU,CAC9B,QAAQ,EAAE,MAAM,EAAE,EAClB,MAAM,GAAE,MAAM,EAAO,EACrB,MAAM,CAAC,EAAE,UAAU,GAClB,OAAO,CAAC,aAAa,EAAE,CAAC,CA+C1B"}
|
package/dist/extract.js
ADDED
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import { readFileSync, existsSync } from 'node:fs';
|
|
2
|
+
import matter from 'gray-matter';
|
|
3
|
+
import { glob } from 'glob';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
import { classifyFile } from './classify.js';
|
|
6
|
+
/**
|
|
7
|
+
* Extract frontmatter and synthetic metadata from a single markdown file.
|
|
8
|
+
*
|
|
9
|
+
* Returns a structured ExtractResult — never throws. Parse errors are
|
|
10
|
+
* captured in the `errors` array.
|
|
11
|
+
*/
|
|
12
|
+
export function extractFile(filePath) {
|
|
13
|
+
const errors = [];
|
|
14
|
+
let raw;
|
|
15
|
+
let fileSize;
|
|
16
|
+
try {
|
|
17
|
+
const buf = readFileSync(filePath);
|
|
18
|
+
fileSize = buf.byteLength;
|
|
19
|
+
raw = buf.toString('utf-8');
|
|
20
|
+
}
|
|
21
|
+
catch (err) {
|
|
22
|
+
return {
|
|
23
|
+
data: {},
|
|
24
|
+
errors: [
|
|
25
|
+
{
|
|
26
|
+
message: `Failed to read file: ${err.message}`,
|
|
27
|
+
filePath,
|
|
28
|
+
},
|
|
29
|
+
],
|
|
30
|
+
filePath,
|
|
31
|
+
fileType: 'unknown',
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
let data = {};
|
|
35
|
+
let body = '';
|
|
36
|
+
let hasFrontmatter = false;
|
|
37
|
+
try {
|
|
38
|
+
const parsed = matter(raw);
|
|
39
|
+
data = parsed.data;
|
|
40
|
+
body = parsed.content;
|
|
41
|
+
// gray-matter returns an empty object when there is no frontmatter,
|
|
42
|
+
// but it also returns an empty object for `---\n---`. Detect real
|
|
43
|
+
// frontmatter by checking whether the raw input starts with the
|
|
44
|
+
// delimiter.
|
|
45
|
+
hasFrontmatter = raw.trimStart().startsWith('---');
|
|
46
|
+
}
|
|
47
|
+
catch (err) {
|
|
48
|
+
// Invalid YAML — capture the error, keep going with body-only result (AC-4).
|
|
49
|
+
errors.push({
|
|
50
|
+
message: `YAML parse error: ${err.message}`,
|
|
51
|
+
filePath,
|
|
52
|
+
});
|
|
53
|
+
// When gray-matter throws, we still have the raw content as the body.
|
|
54
|
+
body = raw;
|
|
55
|
+
hasFrontmatter = false;
|
|
56
|
+
}
|
|
57
|
+
// AC-8 (story-016): SKILL.md with no frontmatter → parse error
|
|
58
|
+
const basename = filePath.split('/').pop() ?? '';
|
|
59
|
+
if (basename === 'SKILL.md' && !hasFrontmatter && errors.length === 0) {
|
|
60
|
+
errors.push({
|
|
61
|
+
message: 'SKILL.md file has no frontmatter',
|
|
62
|
+
filePath,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
// Inject synthetic metadata (AC-3).
|
|
66
|
+
data['___has_frontmatter'] = hasFrontmatter;
|
|
67
|
+
data['___body_length'] = body.length;
|
|
68
|
+
data['___file_size'] = fileSize;
|
|
69
|
+
data['___body_text'] = body;
|
|
70
|
+
data['___file_path'] = filePath;
|
|
71
|
+
// Classify the file (AC-7: unknown when unclassifiable).
|
|
72
|
+
const fileType = classifyFile(filePath, hasFrontmatter);
|
|
73
|
+
data['___file_type'] = fileType;
|
|
74
|
+
return { data, errors, filePath, fileType };
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Build glob patterns for plugin format discovery.
|
|
78
|
+
*
|
|
79
|
+
* Plugin format: skills/{name}/**\/*.md (all markdown in skill dirs),
|
|
80
|
+
* context/{name}.md, agents/{name}.md
|
|
81
|
+
*/
|
|
82
|
+
function pluginPatterns(root) {
|
|
83
|
+
return [
|
|
84
|
+
`${root}/skills/**/*.md`,
|
|
85
|
+
`${root}/context/*.md`,
|
|
86
|
+
`${root}/agents/*.md`,
|
|
87
|
+
];
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Build glob patterns for multi-plugin format discovery.
|
|
91
|
+
*
|
|
92
|
+
* Multi-plugin: plugins/{p}/skills/**\/*.md (all markdown in skill dirs),
|
|
93
|
+
* plugins/{p}/context/{n}.md, plugins/{p}/agents/{n}.md
|
|
94
|
+
*/
|
|
95
|
+
function multiPluginPatterns(root) {
|
|
96
|
+
return [
|
|
97
|
+
`${root}/plugins/*/skills/**/*.md`,
|
|
98
|
+
`${root}/plugins/*/context/*.md`,
|
|
99
|
+
`${root}/plugins/*/agents/*.md`,
|
|
100
|
+
];
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Build glob patterns for project-skills format discovery.
|
|
104
|
+
*
|
|
105
|
+
* Project-skills: **\/.claude/skills/{name}/**\/*.md (all markdown in skill dirs)
|
|
106
|
+
* The ** prefix matches zero or more path segments, so this discovers BOTH
|
|
107
|
+
* root-level .claude/skills/ AND nested monorepo .claude/skills/ directories
|
|
108
|
+
* (e.g. packages/frontend/.claude/skills/). node_modules exclusion in the
|
|
109
|
+
* glob ignore list prevents discovery there.
|
|
110
|
+
*/
|
|
111
|
+
function projectSkillsPatterns(root) {
|
|
112
|
+
return [
|
|
113
|
+
`${root}/**/.claude/skills/**/*.md`,
|
|
114
|
+
];
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Build glob patterns for legacy directory discovery.
|
|
118
|
+
*
|
|
119
|
+
* Legacy: commands/**\/*.md, agents/**\/*.md, context/**\/*.md
|
|
120
|
+
*/
|
|
121
|
+
function legacyPatterns(root) {
|
|
122
|
+
return [
|
|
123
|
+
`${root}/commands/**/*.md`,
|
|
124
|
+
`${root}/agents/**/*.md`,
|
|
125
|
+
`${root}/context/**/*.md`,
|
|
126
|
+
];
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Extract frontmatter from all markdown files matching the given glob
|
|
130
|
+
* patterns. Returns one ExtractResult per file.
|
|
131
|
+
*
|
|
132
|
+
* When `format` is provided, overrides `patterns` with format-specific
|
|
133
|
+
* discovery globs rooted at `patterns[0]`'s parent (the skills root).
|
|
134
|
+
*
|
|
135
|
+
* Returns an empty array when no files match (AC-6).
|
|
136
|
+
*/
|
|
137
|
+
export async function extractAll(patterns, ignore = [], format) {
|
|
138
|
+
let effectivePatterns;
|
|
139
|
+
if (format === 'plugin' || format === 'multi-plugin' || format === 'project-skills') {
|
|
140
|
+
// For structured formats, the first pattern is expected to be a glob
|
|
141
|
+
// rooted at the skills root. Extract the root directory from it.
|
|
142
|
+
// Convention: patterns[0] is something like "/path/to/root/**/*.md"
|
|
143
|
+
// We take the root from patterns[0] by stripping the glob suffix.
|
|
144
|
+
const root = extractRoot(patterns);
|
|
145
|
+
if (format === 'plugin') {
|
|
146
|
+
effectivePatterns = pluginPatterns(root);
|
|
147
|
+
// AC-3b (story-032): plugin format + .claude/skills/ → also extract project-skills
|
|
148
|
+
if (existsSync(join(root, '.claude', 'skills'))) {
|
|
149
|
+
effectivePatterns.push(...projectSkillsPatterns(root));
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
else if (format === 'multi-plugin') {
|
|
153
|
+
effectivePatterns = multiPluginPatterns(root);
|
|
154
|
+
// AC-3b (story-032): multi-plugin format + .claude/skills/ → also extract project-skills
|
|
155
|
+
if (existsSync(join(root, '.claude', 'skills'))) {
|
|
156
|
+
effectivePatterns.push(...projectSkillsPatterns(root));
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
// project-skills format
|
|
161
|
+
effectivePatterns = projectSkillsPatterns(root);
|
|
162
|
+
// AC-3 (story-032): project-skills + legacy dirs → extract from BOTH
|
|
163
|
+
if (existsSync(join(root, 'commands')) ||
|
|
164
|
+
existsSync(join(root, 'agents')) ||
|
|
165
|
+
existsSync(join(root, 'context'))) {
|
|
166
|
+
effectivePatterns.push(...legacyPatterns(root));
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
effectivePatterns = patterns;
|
|
172
|
+
}
|
|
173
|
+
const allMatched = await Promise.all(effectivePatterns.map((pattern) => glob(pattern, { nodir: true, ignore: ['**/node_modules/**', ...ignore] })));
|
|
174
|
+
const files = allMatched.flat();
|
|
175
|
+
// Deduplicate in case patterns overlap.
|
|
176
|
+
const unique = [...new Set(files)];
|
|
177
|
+
return unique.map((f) => extractFile(f));
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Extract the root directory from a set of glob patterns.
|
|
181
|
+
*
|
|
182
|
+
* Takes the first pattern and strips any glob suffix (everything from
|
|
183
|
+
* the first `*` or `{` character onward), then removes trailing slashes.
|
|
184
|
+
*/
|
|
185
|
+
function extractRoot(patterns) {
|
|
186
|
+
const first = patterns[0] ?? '.';
|
|
187
|
+
// Find the first glob metacharacter
|
|
188
|
+
const globIdx = first.search(/[*?{[]/);
|
|
189
|
+
if (globIdx === -1) {
|
|
190
|
+
// No glob chars — the pattern is a literal path
|
|
191
|
+
return first;
|
|
192
|
+
}
|
|
193
|
+
// Strip from the glob char back to the last directory separator
|
|
194
|
+
const prefix = first.slice(0, globIdx);
|
|
195
|
+
return prefix.replace(/\/+$/, '') || '.';
|
|
196
|
+
}
|
|
197
|
+
//# sourceMappingURL=extract.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extract.js","sourceRoot":"","sources":["../src/extract.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,MAAM,MAAM,aAAa,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAG7C;;;;;GAKG;AACH,MAAM,UAAU,WAAW,CAAC,QAAgB;IAC1C,MAAM,MAAM,GAAiB,EAAE,CAAC;IAChC,IAAI,GAAW,CAAC;IAChB,IAAI,QAAgB,CAAC;IAErB,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC;QACnC,QAAQ,GAAG,GAAG,CAAC,UAAU,CAAC;QAC1B,GAAG,GAAG,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;IAC9B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO;YACL,IAAI,EAAE,EAAE;YACR,MAAM,EAAE;gBACN;oBACE,OAAO,EAAE,wBAAyB,GAAa,CAAC,OAAO,EAAE;oBACzD,QAAQ;iBACT;aACF;YACD,QAAQ;YACR,QAAQ,EAAE,SAAS;SACpB,CAAC;IACJ,CAAC;IAED,IAAI,IAAI,GAA4B,EAAE,CAAC;IACvC,IAAI,IAAI,GAAG,EAAE,CAAC;IACd,IAAI,cAAc,GAAG,KAAK,CAAC;IAE3B,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;QAC3B,IAAI,GAAG,MAAM,CAAC,IAA+B,CAAC;QAC9C,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC;QACtB,oEAAoE;QACpE,kEAAkE;QAClE,gEAAgE;QAChE,aAAa;QACb,cAAc,GAAG,GAAG,CAAC,SAAS,EAAE,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;IACrD,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,6EAA6E;QAC7E,MAAM,CAAC,IAAI,CAAC;YACV,OAAO,EAAE,qBAAsB,GAAa,CAAC,OAAO,EAAE;YACtD,QAAQ;SACT,CAAC,CAAC;QACH,sEAAsE;QACtE,IAAI,GAAG,GAAG,CAAC;QACX,cAAc,GAAG,KAAK,CAAC;IACzB,CAAC;IAED,+DAA+D;IAC/D,MAAM,QAAQ,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC;IACjD,IAAI,QAAQ,KAAK,UAAU,IAAI,CAAC,cAAc,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACtE,MAAM,CAAC,IAAI,CAAC;YACV,OAAO,EAAE,kCAAkC;YAC3C,QAAQ;SACT,CAAC,CAAC;IACL,CAAC;IAED,oCAAoC;IACpC,IAAI,CAAC,oBAAoB,CAAC,GAAG,cAAc,CAAC;IAC5C,IAAI,CAAC,gBAAgB,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC;IACrC,IAAI,CAAC,cAAc,CAAC,GAAG,QAAS,CAAC;IACjC,IAAI,CAAC,cAAc,CAAC,GAAG,IAAI,CAAC;IAC5B,IAAI,CAAC,cAAc,CAAC,GAAG,QAAQ,CAAC;IAEhC,yDAAyD;IACzD,MAAM,QAAQ,GAAG,YAAY,CAAC,QAAQ,EAAE,cAAc,CAAC,CAAC;IACxD,IAAI,CAAC,cAAc,CAAC,GAAG,QAAQ,CAAC;IAEhC,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC;AAC9C,CAAC;AAED;;;;;GAKG;AACH,SAAS,cAAc,CAAC,IAAY;IAClC,OAAO;QACL,GAAG,IAAI,iBAAiB;QACxB,GAAG,IAAI,eAAe;QACtB,GAAG,IAAI,cAAc;KACtB,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,SAAS,mBAAmB,CAAC,IAAY;IACvC,OAAO;QACL,GAAG,IAAI,2BAA2B;QAClC,GAAG,IAAI,yBAAyB;QAChC,GAAG,IAAI,wBAAwB;KAChC,CAAC;AACJ,CAAC;AAED;;;;;;;;GAQG;AACH,SAAS,qBAAqB,CAAC,IAAY;IACzC,OAAO;QACL,GAAG,IAAI,4BAA4B;KACpC,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,SAAS,cAAc,CAAC,IAAY;IAClC,OAAO;QACL,GAAG,IAAI,mBAAmB;QAC1B,GAAG,IAAI,iBAAiB;QACxB,GAAG,IAAI,kBAAkB;KAC1B,CAAC;AACJ,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,QAAkB,EAClB,SAAmB,EAAE,EACrB,MAAmB;IAEnB,IAAI,iBAA2B,CAAC;IAEhC,IAAI,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,cAAc,IAAI,MAAM,KAAK,gBAAgB,EAAE,CAAC;QACpF,qEAAqE;QACrE,iEAAiE;QACjE,oEAAoE;QACpE,kEAAkE;QAClE,MAAM,IAAI,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC;QAEnC,IAAI,MAAM,KAAK,QAAQ,EAAE,CAAC;YACxB,iBAAiB,GAAG,cAAc,CAAC,IAAI,CAAC,CAAC;YACzC,mFAAmF;YACnF,IAAI,UAAU,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC,EAAE,CAAC;gBAChD,iBAAiB,CAAC,IAAI,CAAC,GAAG,qBAAqB,CAAC,IAAI,CAAC,CAAC,CAAC;YACzD,CAAC;QACH,CAAC;aAAM,IAAI,MAAM,KAAK,cAAc,EAAE,CAAC;YACrC,iBAAiB,GAAG,mBAAmB,CAAC,IAAI,CAAC,CAAC;YAC9C,yFAAyF;YACzF,IAAI,UAAU,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC,EAAE,CAAC;gBAChD,iBAAiB,CAAC,IAAI,CAAC,GAAG,qBAAqB,CAAC,IAAI,CAAC,CAAC,CAAC;YACzD,CAAC;QACH,CAAC;aAAM,CAAC;YACN,wBAAwB;YACxB,iBAAiB,GAAG,qBAAqB,CAAC,IAAI,CAAC,CAAC;YAChD,qEAAqE;YACrE,IAAI,UAAU,CAAC,IAAI,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;gBAClC,UAAU,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;gBAChC,UAAU,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC,EAAE,CAAC;gBACtC,iBAAiB,CAAC,IAAI,CAAC,GAAG,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC;YAClD,CAAC;QACH,CAAC;IACH,CAAC;SAAM,CAAC;QACN,iBAAiB,GAAG,QAAQ,CAAC;IAC/B,CAAC;IAED,MAAM,UAAU,GAAG,MAAM,OAAO,CAAC,GAAG,CAClC,iBAAiB,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAChC,IAAI,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,oBAAoB,EAAE,GAAG,MAAM,CAAC,EAAE,CAAC,CAC1E,CACF,CAAC;IACF,MAAM,KAAK,GAAa,UAAU,CAAC,IAAI,EAAE,CAAC;IAE1C,wCAAwC;IACxC,MAAM,MAAM,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC;IAEnC,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC;AAC3C,CAAC;AAED;;;;;GAKG;AACH,SAAS,WAAW,CAAC,QAAkB;IACrC,MAAM,KAAK,GAAG,QAAQ,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC;IACjC,oCAAoC;IACpC,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IACvC,IAAI,OAAO,KAAK,CAAC,CAAC,EAAE,CAAC;QACnB,gDAAgD;QAChD,OAAO,KAAK,CAAC;IACf,CAAC;IACD,gEAAgE;IAChE,MAAM,MAAM,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;IACvC,OAAO,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,GAAG,CAAC;AAC3C,CAAC"}
|