skiller 0.6.3 → 0.7.1
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/README.md +70 -115
- package/dist/constants.js +3 -3
- package/dist/core/ConfigLoader.js +5 -6
- package/dist/core/FileSystemUtils.js +15 -6
- package/dist/core/FrontmatterParser.js +11 -2
- package/dist/core/SkillsProcessor.js +288 -432
- package/dist/core/SkillsUtils.js +35 -10
- package/dist/core/UnifiedConfigLoader.js +15 -4
- package/dist/core/apply-engine.js +8 -102
- package/dist/lib.js +5 -25
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,52 +2,50 @@
|
|
|
2
2
|
|
|
3
3
|
A Claude-centric fork of [ruler](https://github.com/intellectronica/ruler) with native skills support:
|
|
4
4
|
|
|
5
|
-
## 1.
|
|
5
|
+
## 1. Skills as Source of Truth
|
|
6
|
+
|
|
7
|
+
- `.claude/skills/` is the committed source of truth for skills
|
|
8
|
+
- **Bidirectional sync** between `.mdc` and `SKILL.md`:
|
|
9
|
+
- Create `.claude/skills/foo.mdc` → auto-generates `.claude/skills/foo/SKILL.md`
|
|
10
|
+
- Create `.claude/skills/foo/SKILL.md` → auto-generates `.claude/skills/foo.mdc`
|
|
11
|
+
- Uses `synced: true` frontmatter to track sync direction
|
|
12
|
+
- Edit either file, the other stays in sync on next `skiller apply`
|
|
13
|
+
- Skill folders from `.claude/rules/` are copied to `.claude/skills/`
|
|
14
|
+
|
|
15
|
+
## 2. CLAUDE.md @filename References
|
|
6
16
|
|
|
7
17
|
- Uses `@filename` syntax instead of merging content
|
|
8
18
|
- Claude Code auto-includes referenced files
|
|
9
19
|
- Reduces CLAUDE.md size and keeps sources separate
|
|
10
20
|
- Other agents still get merged content
|
|
11
21
|
|
|
12
|
-
##
|
|
22
|
+
## 3. MDC File Support
|
|
13
23
|
|
|
14
24
|
- Supports both `.md` and `.mdc` files (Nuxt Content, Vue)
|
|
15
25
|
- All patterns auto-expand: `"components"` → `"components/**/*.{md,mdc}"`
|
|
16
26
|
|
|
17
|
-
##
|
|
27
|
+
## 4. Rules Filtering
|
|
18
28
|
|
|
19
29
|
- `include`/`exclude` glob patterns in `[rules]`
|
|
20
30
|
- Directory names auto-expand to `directory/**/*.{md,mdc}`
|
|
21
31
|
- Organize by team/feature, exclude drafts/internal docs
|
|
22
32
|
|
|
23
|
-
##
|
|
33
|
+
## 5. Claude Root Folder
|
|
24
34
|
|
|
25
35
|
- Default directory is `.claude/` (no extra flags needed)
|
|
26
36
|
- Skills already in `.claude/skills` (no copying)
|
|
27
37
|
- Single directory for all Claude Code config
|
|
28
38
|
|
|
29
|
-
##
|
|
39
|
+
## 6. Cursor-style Rules
|
|
30
40
|
|
|
31
41
|
- `merge_strategy = "cursor"` parses `.mdc` frontmatter
|
|
32
42
|
- Only includes rules with `alwaysApply: true`
|
|
33
43
|
- Strips frontmatter, keeps body only
|
|
34
44
|
|
|
35
|
-
##
|
|
45
|
+
## 7. Backup Control
|
|
36
46
|
|
|
37
47
|
- `[backup].enabled = false` disables `.bak` files
|
|
38
48
|
|
|
39
|
-
## 7. Auto-Generate Skills from Rules
|
|
40
|
-
|
|
41
|
-
- `[skills].generate_from_rules = true` creates skills from .mdc files
|
|
42
|
-
- Only generates from files with `alwaysApply: false` (or undefined)
|
|
43
|
-
- Files with `alwaysApply: true` are merged into AGENTS.md instead
|
|
44
|
-
- Automatically removes skills when `alwaysApply` changes to `true`
|
|
45
|
-
- Skills use @filename references to original .mdc (for Claude Code)
|
|
46
|
-
- MCP agents (excluding Cursor) get full content in .skillz (frontmatter stripped)
|
|
47
|
-
- Cursor uses .cursor/rules directly (no skillz MCP needed)
|
|
48
|
-
- Globs appended to description: "Applies to files matching: ..."
|
|
49
|
-
- **Folder support**: `rules/docx/docx.mdc` + `rules/docx/script.sh` → `skills/docx/SKILL.md` + `skills/docx/script.sh`
|
|
50
|
-
|
|
51
49
|
---
|
|
52
50
|
|
|
53
51
|
# Skiller: Centralise Your AI Coding Assistant Instructions
|
|
@@ -555,19 +553,13 @@ Skiller uses this configuration with the `merge` (default) or `overwrite` strate
|
|
|
555
553
|
export CODEX_HOME="$(pwd)/.codex"
|
|
556
554
|
```
|
|
557
555
|
|
|
558
|
-
## Skills Support
|
|
556
|
+
## Skills Support
|
|
559
557
|
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
Skiller can manage and propagate Claude Code-compatible skills to supported AI agents. Skills are stored in `.claude/skills/` and are automatically distributed to compatible agents when you run `skiller apply`.
|
|
558
|
+
Skiller can manage and propagate Claude Code-compatible skills to supported AI agents. Skills are stored in `.claude/skills/` as the **committed source of truth** and are automatically discovered by Claude Code.
|
|
563
559
|
|
|
564
560
|
### How It Works
|
|
565
561
|
|
|
566
|
-
Skills are specialized knowledge packages that extend AI agent capabilities with domain-specific expertise, workflows, or tool integrations. Skiller discovers skills in your `.claude/skills/` directory and
|
|
567
|
-
|
|
568
|
-
- **Claude Code**: Skills copied to `.claude/skills/` with @filename references preserved
|
|
569
|
-
- **Cursor**: Uses `.cursor/rules/` directly (copied when `merge_strategy = "cursor"`), no skillz MCP needed
|
|
570
|
-
- **Other MCP agents**: Skills copied to `.skillz/` with @filename references expanded to full content (frontmatter stripped), Skillz MCP server auto-configured via `uvx`
|
|
562
|
+
Skills are specialized knowledge packages that extend AI agent capabilities with domain-specific expertise, workflows, or tool integrations. Skiller discovers skills in your `.claude/skills/` directory and keeps them in sync.
|
|
571
563
|
|
|
572
564
|
### Skills Directory Structure
|
|
573
565
|
|
|
@@ -575,66 +567,62 @@ Skills can be organized flat or nested:
|
|
|
575
567
|
|
|
576
568
|
```
|
|
577
569
|
.claude/skills/
|
|
570
|
+
├── my-skill.mdc # Standalone skill file (auto-syncs to folder)
|
|
578
571
|
├── my-skill/
|
|
579
|
-
│
|
|
572
|
+
│ └── SKILL.md # Generated from my-skill.mdc (synced: true)
|
|
573
|
+
├── another-skill/
|
|
574
|
+
│ ├── SKILL.md # Manually created skill
|
|
580
575
|
│ ├── helper.py # Optional: additional resources (scripts)
|
|
581
576
|
│ └── reference.md # Optional: additional resources (docs)
|
|
582
|
-
└── another-skill/
|
|
583
|
-
└── SKILL.md
|
|
584
577
|
```
|
|
585
578
|
|
|
586
|
-
Each skill
|
|
579
|
+
Each skill can be defined in two ways:
|
|
580
|
+
|
|
581
|
+
1. **Standalone `.mdc` file** - Simple skills can be a single `.mdc` file at the skills root
|
|
582
|
+
2. **Skill folder with `SKILL.md`** - Complex skills with additional resources
|
|
587
583
|
|
|
588
|
-
|
|
584
|
+
### Bidirectional Sync
|
|
589
585
|
|
|
590
|
-
|
|
586
|
+
Skiller provides bidirectional sync between `.mdc` files and `SKILL.md` folders:
|
|
591
587
|
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
588
|
+
| Scenario | Sync Direction |
|
|
589
|
+
|----------|---------------|
|
|
590
|
+
| `.mdc` exists, no `SKILL.md` | → Generate `SKILL.md` with `synced: true` |
|
|
591
|
+
| `SKILL.md` has `synced: true` | .mdc → `SKILL.md` (regenerate from .mdc) |
|
|
592
|
+
| `SKILL.md` without `synced: true` | `SKILL.md` → .mdc (SKILL.md is source of truth) |
|
|
595
593
|
|
|
596
|
-
|
|
594
|
+
The `synced: true` frontmatter flag indicates that the `.mdc` file is the source of truth:
|
|
595
|
+
|
|
596
|
+
```yaml
|
|
597
|
+
---
|
|
598
|
+
name: my-skill
|
|
599
|
+
description: My custom skill
|
|
600
|
+
synced: true
|
|
601
|
+
---
|
|
602
|
+
|
|
603
|
+
# Skill content here
|
|
604
|
+
```
|
|
597
605
|
|
|
598
|
-
|
|
606
|
+
### Skill Folders from Rules
|
|
599
607
|
|
|
600
|
-
|
|
608
|
+
Skill folders (directories containing `SKILL.md`) in `.claude/rules/` are automatically copied to `.claude/skills/` during `skiller apply`:
|
|
601
609
|
|
|
602
610
|
```
|
|
603
611
|
.claude/rules/docx/
|
|
604
|
-
├──
|
|
612
|
+
├── SKILL.md # Makes this a skill folder
|
|
605
613
|
├── script.sh # Helper script
|
|
606
614
|
└── templates/ # Subdirectory
|
|
607
615
|
└── default.docx # Template file
|
|
608
616
|
|
|
609
|
-
→
|
|
617
|
+
→ Copied to:
|
|
610
618
|
|
|
611
619
|
.claude/skills/docx/
|
|
612
|
-
├── SKILL.md #
|
|
620
|
+
├── SKILL.md # Copied as-is
|
|
613
621
|
├── script.sh # Copied automatically
|
|
614
622
|
└── templates/ # Copied automatically
|
|
615
623
|
└── default.docx # Copied automatically
|
|
616
624
|
```
|
|
617
625
|
|
|
618
|
-
**Requirements for folder copying**:
|
|
619
|
-
|
|
620
|
-
- The `.mdc` file must be in a folder with the same basename (e.g., `docx/docx.mdc`)
|
|
621
|
-
- The `.mdc` file must have frontmatter with `alwaysApply: false` (or undefined)
|
|
622
|
-
- All files and subdirectories in that folder (except the `.mdc` file itself) are copied
|
|
623
|
-
|
|
624
|
-
**Example `.mdc` file with frontmatter**:
|
|
625
|
-
|
|
626
|
-
```markdown
|
|
627
|
-
---
|
|
628
|
-
description: DOCX file processing utilities
|
|
629
|
-
globs: ['**/*.docx']
|
|
630
|
-
alwaysApply: false
|
|
631
|
-
---
|
|
632
|
-
|
|
633
|
-
# DOCX Processing
|
|
634
|
-
|
|
635
|
-
Use script.sh to process DOCX files. Templates are in templates/ directory.
|
|
636
|
-
```
|
|
637
|
-
|
|
638
626
|
### Configuration
|
|
639
627
|
|
|
640
628
|
Skills support is **enabled by default** but can be controlled via:
|
|
@@ -656,46 +644,6 @@ skiller apply --no-skills
|
|
|
656
644
|
enabled = true # or false to disable
|
|
657
645
|
```
|
|
658
646
|
|
|
659
|
-
### Skillz MCP Server
|
|
660
|
-
|
|
661
|
-
For agents that support MCP but don't have native skills support (excluding Claude Code and Cursor), Skiller automatically:
|
|
662
|
-
|
|
663
|
-
1. Copies skills to `.skillz/` directory with @filename references expanded to full content
|
|
664
|
-
2. Strips frontmatter from referenced .mdc files to avoid duplication
|
|
665
|
-
3. Configures a Skillz MCP server in the agent's configuration
|
|
666
|
-
4. Uses `uvx` to launch the server with the absolute path to `.skillz`
|
|
667
|
-
|
|
668
|
-
**Note**: Cursor is excluded from Skillz MCP because it uses `.cursor/rules/` directory natively.
|
|
669
|
-
|
|
670
|
-
Example auto-generated MCP server configuration:
|
|
671
|
-
|
|
672
|
-
```toml
|
|
673
|
-
[mcp_servers.skillz]
|
|
674
|
-
command = "uvx"
|
|
675
|
-
args = ["skillz@latest", "/absolute/path/to/project/.skillz"]
|
|
676
|
-
```
|
|
677
|
-
|
|
678
|
-
### `.gitignore` Integration
|
|
679
|
-
|
|
680
|
-
When skills support is enabled and gitignore integration is active, Skiller automatically adds:
|
|
681
|
-
|
|
682
|
-
- `.claude/skills/` (when generated from `.claude/rules/` or copied from `.claude/skills/`)
|
|
683
|
-
- `.skillz/` (for MCP-based agents excluding Cursor)
|
|
684
|
-
- `.cursor/rules/` (when using `merge_strategy = "cursor"`)
|
|
685
|
-
|
|
686
|
-
to your `.gitignore` file within the managed Skiller block.
|
|
687
|
-
|
|
688
|
-
**Note**: If you manually create `.claude/skills/` without `.claude/rules/`, it won't be gitignored (assumed to be versioned).
|
|
689
|
-
|
|
690
|
-
### Requirements
|
|
691
|
-
|
|
692
|
-
- **For Claude Code**: No additional requirements
|
|
693
|
-
- **For MCP agents**: `uv` must be installed and available in your PATH
|
|
694
|
-
```bash
|
|
695
|
-
# Install uv if needed
|
|
696
|
-
curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
697
|
-
```
|
|
698
|
-
|
|
699
647
|
### Validation
|
|
700
648
|
|
|
701
649
|
Skiller validates discovered skills and issues warnings for:
|
|
@@ -713,32 +661,39 @@ Test skills propagation without making changes:
|
|
|
713
661
|
skiller apply --dry-run
|
|
714
662
|
```
|
|
715
663
|
|
|
716
|
-
This shows which skills would be
|
|
664
|
+
This shows which skills would be synced and validated.
|
|
717
665
|
|
|
718
666
|
### Example Workflow
|
|
719
667
|
|
|
720
668
|
```bash
|
|
721
|
-
# 1
|
|
722
|
-
|
|
723
|
-
|
|
669
|
+
# Option 1: Create a skill using .mdc file
|
|
670
|
+
cat > .claude/skills/my-skill.mdc << 'EOF'
|
|
671
|
+
---
|
|
672
|
+
description: My custom skill
|
|
673
|
+
---
|
|
674
|
+
|
|
724
675
|
# My Custom Skill
|
|
725
676
|
|
|
726
677
|
This skill provides specialized knowledge for...
|
|
678
|
+
EOF
|
|
679
|
+
|
|
680
|
+
# Option 2: Create a skill folder directly
|
|
681
|
+
mkdir -p .claude/skills/my-skill
|
|
682
|
+
cat > .claude/skills/my-skill/SKILL.md << 'EOF'
|
|
683
|
+
---
|
|
684
|
+
name: my-skill
|
|
685
|
+
description: My custom skill
|
|
686
|
+
---
|
|
727
687
|
|
|
728
|
-
|
|
688
|
+
# My Custom Skill
|
|
729
689
|
|
|
730
|
-
|
|
731
|
-
- Use TypeScript for all new code
|
|
732
|
-
- Write tests for all features
|
|
733
|
-
- Follow the existing code style
|
|
690
|
+
This skill provides specialized knowledge for...
|
|
734
691
|
EOF
|
|
735
692
|
|
|
736
|
-
#
|
|
693
|
+
# Apply to sync skills (runs bidirectional sync)
|
|
737
694
|
skiller apply
|
|
738
695
|
|
|
739
|
-
#
|
|
740
|
-
# - Claude Code: .claude/skills/my-skill/
|
|
741
|
-
# - Other MCP agents: .skillz/my-skill/ + Skillz MCP server configured
|
|
696
|
+
# Skills are now available to Claude Code via .claude/skills/
|
|
742
697
|
```
|
|
743
698
|
|
|
744
699
|
## `.gitignore` Integration
|
package/dist/constants.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.MAX_RECURSION_DEPTH = exports.SKILL_MD_FILENAME = exports.CLAUDE_SKILLS_PATH = exports.SKILLS_DIR = exports.DEFAULT_RULES_FILENAME = exports.ERROR_PREFIX = void 0;
|
|
4
4
|
exports.actionPrefix = actionPrefix;
|
|
5
5
|
exports.createSkillerError = createSkillerError;
|
|
6
6
|
exports.logVerbose = logVerbose;
|
|
@@ -51,6 +51,6 @@ function logVerboseInfo(message, isVerbose, dryRun = false) {
|
|
|
51
51
|
// Skills-related constants
|
|
52
52
|
exports.SKILLS_DIR = 'skills';
|
|
53
53
|
exports.CLAUDE_SKILLS_PATH = '.claude/skills';
|
|
54
|
-
exports.SKILLZ_DIR = '.skillz';
|
|
55
54
|
exports.SKILL_MD_FILENAME = 'SKILL.md';
|
|
56
|
-
|
|
55
|
+
// Security: Maximum recursion depth to prevent DoS via deeply nested directories
|
|
56
|
+
exports.MAX_RECURSION_DEPTH = 50;
|
|
@@ -78,8 +78,6 @@ const skillerConfigSchema = zod_1.z.object({
|
|
|
78
78
|
skills: zod_1.z
|
|
79
79
|
.object({
|
|
80
80
|
enabled: zod_1.z.boolean().optional(),
|
|
81
|
-
generate_from_rules: zod_1.z.boolean().optional(),
|
|
82
|
-
prune: zod_1.z.boolean().optional(),
|
|
83
81
|
})
|
|
84
82
|
.optional(),
|
|
85
83
|
rules: zod_1.z
|
|
@@ -233,11 +231,12 @@ async function loadConfig(options) {
|
|
|
233
231
|
if (typeof rawSkillsSection.enabled === 'boolean') {
|
|
234
232
|
skillsConfig.enabled = rawSkillsSection.enabled;
|
|
235
233
|
}
|
|
236
|
-
|
|
237
|
-
|
|
234
|
+
// Deprecation warnings for removed config options
|
|
235
|
+
if ('generate_from_rules' in rawSkillsSection) {
|
|
236
|
+
console.warn(`[skiller] Warning: skills.generate_from_rules is deprecated and has no effect. Skills are now edited directly in .claude/skills/`);
|
|
238
237
|
}
|
|
239
|
-
if (
|
|
240
|
-
|
|
238
|
+
if ('prune' in rawSkillsSection) {
|
|
239
|
+
console.warn(`[skiller] Warning: skills.prune is deprecated and has no effect. Skills in .claude/skills/ are never auto-deleted.`);
|
|
241
240
|
}
|
|
242
241
|
const rawRulesSection = raw.rules && typeof raw.rules === 'object' && !Array.isArray(raw.rules)
|
|
243
242
|
? raw.rules
|
|
@@ -47,6 +47,7 @@ const fs_1 = require("fs");
|
|
|
47
47
|
const path = __importStar(require("path"));
|
|
48
48
|
const os = __importStar(require("os"));
|
|
49
49
|
const FrontmatterParser_1 = require("./FrontmatterParser");
|
|
50
|
+
const constants_1 = require("../constants");
|
|
50
51
|
/**
|
|
51
52
|
* Gets the XDG config directory path, falling back to ~/.config if XDG_CONFIG_HOME is not set.
|
|
52
53
|
*/
|
|
@@ -172,12 +173,16 @@ function matchesPattern(filePath, pattern) {
|
|
|
172
173
|
async function readMarkdownFiles(skillerDir, options) {
|
|
173
174
|
const mdFiles = [];
|
|
174
175
|
// Gather all markdown files (recursive) first
|
|
175
|
-
async function walk(dir) {
|
|
176
|
+
async function walk(dir, depth = 0) {
|
|
177
|
+
// Security: Prevent DoS via deeply nested directories
|
|
178
|
+
if (depth >= constants_1.MAX_RECURSION_DEPTH) {
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
176
181
|
const entries = await fs_1.promises.readdir(dir, { withFileTypes: true });
|
|
177
182
|
for (const entry of entries) {
|
|
178
183
|
const fullPath = path.join(dir, entry.name);
|
|
179
184
|
if (entry.isDirectory()) {
|
|
180
|
-
await walk(fullPath);
|
|
185
|
+
await walk(fullPath, depth + 1);
|
|
181
186
|
}
|
|
182
187
|
else if (entry.isFile() &&
|
|
183
188
|
(entry.name.endsWith('.md') || entry.name.endsWith('.mdc'))) {
|
|
@@ -186,7 +191,7 @@ async function readMarkdownFiles(skillerDir, options) {
|
|
|
186
191
|
}
|
|
187
192
|
}
|
|
188
193
|
}
|
|
189
|
-
await walk(skillerDir);
|
|
194
|
+
await walk(skillerDir, 0);
|
|
190
195
|
// Apply include/exclude filters
|
|
191
196
|
let filteredFiles = mdFiles;
|
|
192
197
|
if (options?.include || options?.exclude) {
|
|
@@ -358,7 +363,11 @@ exports.findGlobalRulerDir = findGlobalSkillerDir;
|
|
|
358
363
|
async function findAllSkillerDirs(startPath) {
|
|
359
364
|
const skillerDirs = [];
|
|
360
365
|
// Search the entire directory tree downwards from startPath
|
|
361
|
-
async function findSkillerDirsRecursive(dir) {
|
|
366
|
+
async function findSkillerDirsRecursive(dir, depth = 0) {
|
|
367
|
+
// Security: Prevent DoS via deeply nested directories
|
|
368
|
+
if (depth >= constants_1.MAX_RECURSION_DEPTH) {
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
362
371
|
try {
|
|
363
372
|
const entries = await fs_1.promises.readdir(dir, { withFileTypes: true });
|
|
364
373
|
for (const entry of entries) {
|
|
@@ -378,7 +387,7 @@ async function findAllSkillerDirs(startPath) {
|
|
|
378
387
|
else {
|
|
379
388
|
// Recursively search subdirectories (but skip hidden directories like .git)
|
|
380
389
|
if (!entry.name.startsWith('.')) {
|
|
381
|
-
await findSkillerDirsRecursive(fullPath);
|
|
390
|
+
await findSkillerDirsRecursive(fullPath, depth + 1);
|
|
382
391
|
}
|
|
383
392
|
}
|
|
384
393
|
}
|
|
@@ -389,7 +398,7 @@ async function findAllSkillerDirs(startPath) {
|
|
|
389
398
|
}
|
|
390
399
|
}
|
|
391
400
|
// Start searching from the startPath
|
|
392
|
-
await findSkillerDirsRecursive(startPath);
|
|
401
|
+
await findSkillerDirsRecursive(startPath, 0);
|
|
393
402
|
// Sort by depth (most specific first) - deeper paths come first
|
|
394
403
|
skillerDirs.sort((a, b) => {
|
|
395
404
|
const depthA = a.split(path.sep).length;
|
|
@@ -67,7 +67,10 @@ function parseFrontmatter(content) {
|
|
|
67
67
|
const [, yamlContent, body] = match;
|
|
68
68
|
try {
|
|
69
69
|
// Try parsing YAML as-is first
|
|
70
|
-
|
|
70
|
+
// Use JSON_SCHEMA for safety - prevents YAML-specific type coercion attacks
|
|
71
|
+
const parsed = yaml.load(yamlContent, {
|
|
72
|
+
schema: yaml.JSON_SCHEMA,
|
|
73
|
+
});
|
|
71
74
|
return extractFrontmatter(parsed, body);
|
|
72
75
|
}
|
|
73
76
|
catch {
|
|
@@ -91,7 +94,9 @@ function parseFrontmatter(content) {
|
|
|
91
94
|
});
|
|
92
95
|
// Try parsing again with fixed YAML
|
|
93
96
|
if (fixedYaml !== yamlContent) {
|
|
94
|
-
const parsed = yaml.load(fixedYaml
|
|
97
|
+
const parsed = yaml.load(fixedYaml, {
|
|
98
|
+
schema: yaml.JSON_SCHEMA,
|
|
99
|
+
});
|
|
95
100
|
return extractFrontmatter(parsed, body);
|
|
96
101
|
}
|
|
97
102
|
}
|
|
@@ -134,6 +139,10 @@ function extractFrontmatter(parsed, body) {
|
|
|
134
139
|
if (typeof parsed.alwaysApply === 'boolean') {
|
|
135
140
|
frontmatter.alwaysApply = parsed.alwaysApply;
|
|
136
141
|
}
|
|
142
|
+
// Extract name (for SKILL.md)
|
|
143
|
+
if (typeof parsed.name === 'string') {
|
|
144
|
+
frontmatter.name = parsed.name;
|
|
145
|
+
}
|
|
137
146
|
}
|
|
138
147
|
return {
|
|
139
148
|
frontmatter,
|