skiller 0.7.10 → 0.7.12
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 +71 -51
- package/dist/agents/AmpAgent.js +40 -0
- package/dist/agents/CodexCliAgent.js +6 -0
- package/dist/agents/CopilotAgent.js +40 -0
- package/dist/agents/CursorAgent.js +3 -0
- package/dist/agents/GeminiCliAgent.js +6 -0
- package/dist/agents/GooseAgent.js +6 -0
- package/dist/agents/KiloCodeAgent.js +6 -0
- package/dist/agents/OpenCodeAgent.js +6 -0
- package/dist/agents/RooCodeAgent.js +6 -0
- package/dist/cli/commands.js +7 -7
- package/dist/cli/index.js +4 -1
- package/dist/core/SkillsProcessor.js +183 -19
- package/dist/lib.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -46,6 +46,14 @@ A Claude-centric fork of [ruler](https://github.com/intellectronica/ruler) with
|
|
|
46
46
|
|
|
47
47
|
- `[backup].enabled = false` disables `.bak` files
|
|
48
48
|
|
|
49
|
+
## 8. Multi-Agent Skills Propagation
|
|
50
|
+
|
|
51
|
+
- `.claude/skills/` is the source of truth — skills are automatically copied to agent-specific directories on `skiller apply`
|
|
52
|
+
- Supported agent paths: `.codex/skills`, `.cursor/skills`, `.opencode/skill`, `.roo/skills`, `.gemini/skills`, `.agents/skills`
|
|
53
|
+
- Shared paths are deduplicated (Claude/Copilot/Kilo share `.claude/skills`, Goose/Amp share `.agents/skills`)
|
|
54
|
+
- Agent skills directories are auto-added to `.gitignore` (excluding `.claude/skills`)
|
|
55
|
+
- Validates skill structure — warns on missing `SKILL.md`
|
|
56
|
+
|
|
49
57
|
---
|
|
50
58
|
|
|
51
59
|
# Skiller: Centralise Your AI Coding Assistant Instructions
|
|
@@ -79,35 +87,35 @@ Skiller solves this by providing a **single source of truth** for all your AI ag
|
|
|
79
87
|
|
|
80
88
|
## Supported AI Agents
|
|
81
89
|
|
|
82
|
-
| Agent | Rules File(s) | MCP Configuration / Notes |
|
|
83
|
-
| ---------------- | -------------------------------------------------- | ------------------------------------------------ |
|
|
84
|
-
| AGENTS.md | `AGENTS.md` | (pseudo-agent ensuring root `AGENTS.md` exists) |
|
|
85
|
-
| GitHub Copilot | `AGENTS.md` | `.vscode/mcp.json` |
|
|
86
|
-
| Claude Code | `CLAUDE.md` (@filename references) | `.mcp.json` |
|
|
87
|
-
| OpenAI Codex CLI | `AGENTS.md` | `.codex/config.toml` |
|
|
88
|
-
| Jules | `AGENTS.md` | - |
|
|
89
|
-
| Cursor | `AGENTS.md` | `.cursor/mcp.json` |
|
|
90
|
-
| Windsurf | `AGENTS.md` | `.windsurf/mcp_config.json` |
|
|
91
|
-
| Cline | `.clinerules` | - |
|
|
92
|
-
| Crush | `CRUSH.md` | `.crush.json` |
|
|
93
|
-
| Amp | `AGENTS.md` | - |
|
|
94
|
-
| Amazon Q CLI | `.amazonq/rules/skiller_q_rules.md` | `.amazonq/mcp.json` |
|
|
95
|
-
| Aider | `AGENTS.md`, `.aider.conf.yml` | `.mcp.json` |
|
|
96
|
-
| Firebase Studio | `.idx/airules.md` | `.idx/mcp.json` |
|
|
97
|
-
| Open Hands | `.openhands/microagents/repo.md` | `config.toml` |
|
|
98
|
-
| Gemini CLI | `AGENTS.md` | `.gemini/settings.json` |
|
|
99
|
-
| Junie | `.junie/guidelines.md` | - |
|
|
100
|
-
| AugmentCode | `.augment/rules/skiller_augment_instructions.md` | - |
|
|
101
|
-
| Kilo Code | `.kilocode/rules/skiller_kilocode_instructions.md` | `.kilocode/mcp.json` |
|
|
102
|
-
| OpenCode | `AGENTS.md` | `opencode.json` |
|
|
103
|
-
| Goose | `.goosehints` | - |
|
|
104
|
-
| Qwen Code | `AGENTS.md` | `.qwen/settings.json` |
|
|
105
|
-
| RooCode | `AGENTS.md` | `.roo/mcp.json` |
|
|
106
|
-
| Zed | `AGENTS.md` | `.zed/settings.json` (project root, never $HOME) |
|
|
107
|
-
| Trae AI | `.trae/rules/project_rules.md` | - |
|
|
108
|
-
| Warp | `WARP.md` | - |
|
|
109
|
-
| Kiro | `.kiro/steering/skiller_kiro_instructions.md` | - |
|
|
110
|
-
| Firebender | `firebender.json` | `firebender.json` (rules and MCP in same file) |
|
|
90
|
+
| Agent | Rules File(s) | MCP Configuration / Notes | Skills Location |
|
|
91
|
+
| ---------------- | -------------------------------------------------- | ------------------------------------------------ | ------------------ |
|
|
92
|
+
| AGENTS.md | `AGENTS.md` | (pseudo-agent ensuring root `AGENTS.md` exists) | - |
|
|
93
|
+
| GitHub Copilot | `AGENTS.md` | `.vscode/mcp.json` | `.claude/skills/` |
|
|
94
|
+
| Claude Code | `CLAUDE.md` (@filename references) | `.mcp.json` | `.claude/skills/` |
|
|
95
|
+
| OpenAI Codex CLI | `AGENTS.md` | `.codex/config.toml` | `.codex/skills/` |
|
|
96
|
+
| Jules | `AGENTS.md` | - | - |
|
|
97
|
+
| Cursor | `AGENTS.md` | `.cursor/mcp.json` | `.cursor/skills/` |
|
|
98
|
+
| Windsurf | `AGENTS.md` | `.windsurf/mcp_config.json` | - |
|
|
99
|
+
| Cline | `.clinerules` | - | - |
|
|
100
|
+
| Crush | `CRUSH.md` | `.crush.json` | - |
|
|
101
|
+
| Amp | `AGENTS.md` | - | `.agents/skills/` |
|
|
102
|
+
| Amazon Q CLI | `.amazonq/rules/skiller_q_rules.md` | `.amazonq/mcp.json` | - |
|
|
103
|
+
| Aider | `AGENTS.md`, `.aider.conf.yml` | `.mcp.json` | - |
|
|
104
|
+
| Firebase Studio | `.idx/airules.md` | `.idx/mcp.json` | - |
|
|
105
|
+
| Open Hands | `.openhands/microagents/repo.md` | `config.toml` | - |
|
|
106
|
+
| Gemini CLI | `AGENTS.md` | `.gemini/settings.json` | `.gemini/skills/` |
|
|
107
|
+
| Junie | `.junie/guidelines.md` | - | - |
|
|
108
|
+
| AugmentCode | `.augment/rules/skiller_augment_instructions.md` | - | - |
|
|
109
|
+
| Kilo Code | `.kilocode/rules/skiller_kilocode_instructions.md` | `.kilocode/mcp.json` | `.claude/skills/` |
|
|
110
|
+
| OpenCode | `AGENTS.md` | `opencode.json` | `.opencode/skill/` |
|
|
111
|
+
| Goose | `.goosehints` | - | `.agents/skills/` |
|
|
112
|
+
| Qwen Code | `AGENTS.md` | `.qwen/settings.json` | - |
|
|
113
|
+
| RooCode | `AGENTS.md` | `.roo/mcp.json` | `.roo/skills/` |
|
|
114
|
+
| Zed | `AGENTS.md` | `.zed/settings.json` (project root, never $HOME) | - |
|
|
115
|
+
| Trae AI | `.trae/rules/project_rules.md` | - | - |
|
|
116
|
+
| Warp | `WARP.md` | - | - |
|
|
117
|
+
| Kiro | `.kiro/steering/skiller_kiro_instructions.md` | - | - |
|
|
118
|
+
| Firebender | `firebender.json` | `firebender.json` (rules and MCP in same file) | - |
|
|
111
119
|
|
|
112
120
|
## Getting Started
|
|
113
121
|
|
|
@@ -555,11 +563,21 @@ export CODEX_HOME="$(pwd)/.codex"
|
|
|
555
563
|
|
|
556
564
|
## Skills Support
|
|
557
565
|
|
|
558
|
-
Skiller can manage and propagate
|
|
566
|
+
Skiller can manage and propagate skills to supported AI agents. Skills are stored in `.claude/skills/` as the **committed source of truth** and automatically copied to agent-specific directories on `skiller apply`.
|
|
559
567
|
|
|
560
568
|
### How It Works
|
|
561
569
|
|
|
562
|
-
Skills are specialized knowledge packages that extend AI agent capabilities
|
|
570
|
+
Skills are specialized knowledge packages that extend AI agent capabilities. Skiller discovers skills in your `.claude/skills/` directory, keeps them in sync via bidirectional `.mdc`/`SKILL.md` sync, and propagates them to all agents with native skills support:
|
|
571
|
+
|
|
572
|
+
- **Claude Code, GitHub Copilot, Kilo Code**: `.claude/skills/` (shared, source of truth)
|
|
573
|
+
- **OpenAI Codex CLI**: `.codex/skills/`
|
|
574
|
+
- **Cursor**: `.cursor/skills/`
|
|
575
|
+
- **OpenCode**: `.opencode/skill/`
|
|
576
|
+
- **Goose, Amp**: `.agents/skills/` (shared)
|
|
577
|
+
- **Roo Code**: `.roo/skills/`
|
|
578
|
+
- **Gemini CLI**: `.gemini/skills/`
|
|
579
|
+
|
|
580
|
+
Shared paths are deduplicated — agents sharing the same directory only trigger one copy operation.
|
|
563
581
|
|
|
564
582
|
### Skills Directory Structure
|
|
565
583
|
|
|
@@ -586,11 +604,11 @@ Each skill can be defined in two ways:
|
|
|
586
604
|
|
|
587
605
|
Skiller provides bidirectional sync between `.mdc` files and `SKILL.md` folders:
|
|
588
606
|
|
|
589
|
-
| Scenario
|
|
590
|
-
|
|
591
|
-
| `.mdc` exists, no `SKILL.md`
|
|
592
|
-
| `SKILL.md` body is `@reference` | .mdc is source of truth (frontmatter in SKILL.md)
|
|
593
|
-
| `SKILL.md` has full content
|
|
607
|
+
| Scenario | Sync Direction |
|
|
608
|
+
| ------------------------------- | ---------------------------------------------------------- |
|
|
609
|
+
| `.mdc` exists, no `SKILL.md` | → Generate `SKILL.md` with `@reference` to .mdc |
|
|
610
|
+
| `SKILL.md` body is `@reference` | .mdc is source of truth (frontmatter in SKILL.md) |
|
|
611
|
+
| `SKILL.md` has full content | → Generate .mdc from body, update SKILL.md to `@reference` |
|
|
594
612
|
|
|
595
613
|
The `@reference` body pattern indicates that the `.mdc` file contains the skill content:
|
|
596
614
|
|
|
@@ -656,6 +674,12 @@ Skiller validates discovered skills and issues warnings for:
|
|
|
656
674
|
|
|
657
675
|
Warnings don't prevent propagation but help identify potential issues.
|
|
658
676
|
|
|
677
|
+
### `.gitignore` Integration
|
|
678
|
+
|
|
679
|
+
When skills propagation is enabled, agent skills directories are automatically added to `.gitignore` (excluding `.claude/skills/` which is the committed source of truth):
|
|
680
|
+
|
|
681
|
+
- `.codex/skills/`, `.cursor/skills/`, `.opencode/skill/`, `.agents/skills/`, `.roo/skills/`, `.gemini/skills/`
|
|
682
|
+
|
|
659
683
|
### Dry-Run Mode
|
|
660
684
|
|
|
661
685
|
Test skills propagation without making changes:
|
|
@@ -664,23 +688,12 @@ Test skills propagation without making changes:
|
|
|
664
688
|
skiller apply --dry-run
|
|
665
689
|
```
|
|
666
690
|
|
|
667
|
-
This shows which skills would be synced and
|
|
691
|
+
This shows which skills would be synced, validated, and copied to each agent directory.
|
|
668
692
|
|
|
669
693
|
### Example Workflow
|
|
670
694
|
|
|
671
695
|
```bash
|
|
672
|
-
#
|
|
673
|
-
cat > .claude/skills/my-skill.mdc << 'EOF'
|
|
674
|
-
---
|
|
675
|
-
description: My custom skill
|
|
676
|
-
---
|
|
677
|
-
|
|
678
|
-
# My Custom Skill
|
|
679
|
-
|
|
680
|
-
This skill provides specialized knowledge for...
|
|
681
|
-
EOF
|
|
682
|
-
|
|
683
|
-
# Option 2: Create a skill folder directly
|
|
696
|
+
# 1. Create a skill folder
|
|
684
697
|
mkdir -p .claude/skills/my-skill
|
|
685
698
|
cat > .claude/skills/my-skill/SKILL.md << 'EOF'
|
|
686
699
|
---
|
|
@@ -693,10 +706,17 @@ description: My custom skill
|
|
|
693
706
|
This skill provides specialized knowledge for...
|
|
694
707
|
EOF
|
|
695
708
|
|
|
696
|
-
# Apply to sync
|
|
709
|
+
# 2. Apply to sync and propagate skills
|
|
697
710
|
skiller apply
|
|
698
711
|
|
|
699
|
-
# Skills are now available to
|
|
712
|
+
# 3. Skills are now available to all compatible agents:
|
|
713
|
+
# - Claude Code, Copilot, Kilo Code: .claude/skills/my-skill/
|
|
714
|
+
# - Codex CLI: .codex/skills/my-skill/
|
|
715
|
+
# - Cursor: .cursor/skills/my-skill/
|
|
716
|
+
# - OpenCode: .opencode/skill/my-skill/
|
|
717
|
+
# - Goose, Amp: .agents/skills/my-skill/
|
|
718
|
+
# - Roo Code: .roo/skills/my-skill/
|
|
719
|
+
# - Gemini CLI: .gemini/skills/my-skill/
|
|
700
720
|
```
|
|
701
721
|
|
|
702
722
|
## `.gitignore` Integration
|
package/dist/agents/AmpAgent.js
CHANGED
|
@@ -1,6 +1,40 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
2
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
36
|
exports.AmpAgent = void 0;
|
|
37
|
+
const path = __importStar(require("path"));
|
|
4
38
|
const AgentsMdAgent_1 = require("./AgentsMdAgent");
|
|
5
39
|
class AmpAgent extends AgentsMdAgent_1.AgentsMdAgent {
|
|
6
40
|
getIdentifier() {
|
|
@@ -9,5 +43,11 @@ class AmpAgent extends AgentsMdAgent_1.AgentsMdAgent {
|
|
|
9
43
|
getName() {
|
|
10
44
|
return 'Amp';
|
|
11
45
|
}
|
|
46
|
+
supportsNativeSkills() {
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
getSkillsPath(projectRoot) {
|
|
50
|
+
return path.join(projectRoot, '.agents/skills');
|
|
51
|
+
}
|
|
12
52
|
}
|
|
13
53
|
exports.AmpAgent = AmpAgent;
|
|
@@ -139,5 +139,11 @@ class CodexCliAgent {
|
|
|
139
139
|
supportsMcpRemote() {
|
|
140
140
|
return false; // Codex CLI only supports STDIO based on PR description
|
|
141
141
|
}
|
|
142
|
+
supportsNativeSkills() {
|
|
143
|
+
return true;
|
|
144
|
+
}
|
|
145
|
+
getSkillsPath(projectRoot) {
|
|
146
|
+
return path.join(projectRoot, '.codex/skills');
|
|
147
|
+
}
|
|
142
148
|
}
|
|
143
149
|
exports.CodexCliAgent = CodexCliAgent;
|
|
@@ -1,6 +1,40 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
2
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
36
|
exports.CopilotAgent = void 0;
|
|
37
|
+
const path = __importStar(require("path"));
|
|
4
38
|
const AgentsMdAgent_1 = require("./AgentsMdAgent");
|
|
5
39
|
/**
|
|
6
40
|
* GitHub Copilot agent adapter.
|
|
@@ -39,5 +73,11 @@ class CopilotAgent {
|
|
|
39
73
|
supportsMcpRemote() {
|
|
40
74
|
return true;
|
|
41
75
|
}
|
|
76
|
+
supportsNativeSkills() {
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
getSkillsPath(projectRoot) {
|
|
80
|
+
return path.join(projectRoot, '.claude/skills');
|
|
81
|
+
}
|
|
42
82
|
}
|
|
43
83
|
exports.CopilotAgent = CopilotAgent;
|
|
@@ -89,5 +89,8 @@ class CursorAgent extends AgentsMdAgent_1.AgentsMdAgent {
|
|
|
89
89
|
// Cursor has native support for rules via .cursor/rules/
|
|
90
90
|
return true;
|
|
91
91
|
}
|
|
92
|
+
getSkillsPath(projectRoot) {
|
|
93
|
+
return path.join(projectRoot, '.cursor/skills');
|
|
94
|
+
}
|
|
92
95
|
}
|
|
93
96
|
exports.CursorAgent = CursorAgent;
|
|
@@ -95,5 +95,11 @@ class GeminiCliAgent extends AgentsMdAgent_1.AgentsMdAgent {
|
|
|
95
95
|
supportsMcpRemote() {
|
|
96
96
|
return true;
|
|
97
97
|
}
|
|
98
|
+
supportsNativeSkills() {
|
|
99
|
+
return true;
|
|
100
|
+
}
|
|
101
|
+
getSkillsPath(projectRoot) {
|
|
102
|
+
return path.join(projectRoot, '.gemini/skills');
|
|
103
|
+
}
|
|
98
104
|
}
|
|
99
105
|
exports.GeminiCliAgent = GeminiCliAgent;
|
|
@@ -54,5 +54,11 @@ class GooseAgent extends AbstractAgent_1.AbstractAgent {
|
|
|
54
54
|
// Goose doesn't support MCP configuration via local config files
|
|
55
55
|
return '';
|
|
56
56
|
}
|
|
57
|
+
supportsNativeSkills() {
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
getSkillsPath(projectRoot) {
|
|
61
|
+
return path.join(projectRoot, '.agents/skills');
|
|
62
|
+
}
|
|
57
63
|
}
|
|
58
64
|
exports.GooseAgent = GooseAgent;
|
|
@@ -59,5 +59,11 @@ class KiloCodeAgent extends AbstractAgent_1.AbstractAgent {
|
|
|
59
59
|
supportsMcpRemote() {
|
|
60
60
|
return true;
|
|
61
61
|
}
|
|
62
|
+
supportsNativeSkills() {
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
getSkillsPath(projectRoot) {
|
|
66
|
+
return path.join(projectRoot, '.claude/skills');
|
|
67
|
+
}
|
|
62
68
|
}
|
|
63
69
|
exports.KiloCodeAgent = KiloCodeAgent;
|
|
@@ -95,5 +95,11 @@ class OpenCodeAgent {
|
|
|
95
95
|
supportsMcpRemote() {
|
|
96
96
|
return true;
|
|
97
97
|
}
|
|
98
|
+
supportsNativeSkills() {
|
|
99
|
+
return true;
|
|
100
|
+
}
|
|
101
|
+
getSkillsPath(projectRoot) {
|
|
102
|
+
return path.join(projectRoot, '.opencode/skill');
|
|
103
|
+
}
|
|
98
104
|
}
|
|
99
105
|
exports.OpenCodeAgent = OpenCodeAgent;
|
|
@@ -135,5 +135,11 @@ class RooCodeAgent {
|
|
|
135
135
|
getMcpServerKey() {
|
|
136
136
|
return 'mcpServers';
|
|
137
137
|
}
|
|
138
|
+
supportsNativeSkills() {
|
|
139
|
+
return true;
|
|
140
|
+
}
|
|
141
|
+
getSkillsPath(projectRoot) {
|
|
142
|
+
return path.join(projectRoot, '.roo/skills');
|
|
143
|
+
}
|
|
138
144
|
}
|
|
139
145
|
exports.RooCodeAgent = RooCodeAgent;
|
package/dist/cli/commands.js
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
3
|
exports.run = run;
|
|
7
|
-
const yargs_1 = __importDefault(require("yargs"));
|
|
8
|
-
const helpers_1 = require("yargs/helpers");
|
|
9
4
|
const handlers_1 = require("./handlers");
|
|
10
5
|
const index_1 = require("../agents/index");
|
|
11
6
|
/**
|
|
12
7
|
* Sets up and parses CLI commands.
|
|
13
8
|
*/
|
|
14
|
-
function run() {
|
|
15
|
-
|
|
9
|
+
async function run() {
|
|
10
|
+
const dynamicImport = new Function('modulePath', 'return import(modulePath);');
|
|
11
|
+
const [{ default: yargs }, { hideBin }] = await Promise.all([
|
|
12
|
+
dynamicImport('yargs'),
|
|
13
|
+
dynamicImport('yargs/helpers'),
|
|
14
|
+
]);
|
|
15
|
+
yargs(hideBin(process.argv))
|
|
16
16
|
.scriptName('skiller')
|
|
17
17
|
.usage('$0 <command> [options]')
|
|
18
18
|
.command('apply', 'Apply skiller configurations to supported AI agents', (y) => {
|
package/dist/cli/index.js
CHANGED
|
@@ -36,6 +36,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
36
36
|
exports.isReferenceBody = isReferenceBody;
|
|
37
37
|
exports.syncMdcToSkillMd = syncMdcToSkillMd;
|
|
38
38
|
exports.discoverSkills = discoverSkills;
|
|
39
|
+
exports.copySkillsToAgent = copySkillsToAgent;
|
|
39
40
|
exports.getSkillsGitignorePaths = getSkillsGitignorePaths;
|
|
40
41
|
exports.propagateSkills = propagateSkills;
|
|
41
42
|
exports.migrateRulesToSkills = migrateRulesToSkills;
|
|
@@ -48,6 +49,92 @@ const yaml = __importStar(require("js-yaml"));
|
|
|
48
49
|
const constants_1 = require("../constants");
|
|
49
50
|
const SkillsUtils_1 = require("./SkillsUtils");
|
|
50
51
|
const FrontmatterParser_1 = require("./FrontmatterParser");
|
|
52
|
+
/**
|
|
53
|
+
* For non-Claude agents, compile a wrapper SKILL.md (body is a single @reference)
|
|
54
|
+
* into a standalone SKILL.md with the referenced file's body inlined.
|
|
55
|
+
*
|
|
56
|
+
* We intentionally keep this conservative: only expand when the body is *just*
|
|
57
|
+
* an @reference line, to avoid accidentally treating email addresses or
|
|
58
|
+
* "@mentions" inside real content as file references.
|
|
59
|
+
*/
|
|
60
|
+
async function compileSkillMdForNonClaudeAgents(skillMdContent, projectRoot, skillFolderPath) {
|
|
61
|
+
const { frontmatter, rawFrontmatter, body } = (0, FrontmatterParser_1.parseFrontmatter)(skillMdContent);
|
|
62
|
+
const refCheck = isReferenceBody(body);
|
|
63
|
+
if (!refCheck.isReference || !refCheck.referencePath) {
|
|
64
|
+
return skillMdContent;
|
|
65
|
+
}
|
|
66
|
+
const referencePath = refCheck.referencePath;
|
|
67
|
+
const absoluteRefPath = referencePath.startsWith('./') || referencePath.startsWith('../')
|
|
68
|
+
? path.resolve(skillFolderPath, referencePath)
|
|
69
|
+
: path.resolve(projectRoot, referencePath);
|
|
70
|
+
// Security: only inline references within the project root.
|
|
71
|
+
const normalizedProjectRoot = path.resolve(projectRoot);
|
|
72
|
+
const normalizedAbsoluteRefPath = path.resolve(absoluteRefPath);
|
|
73
|
+
if (!normalizedAbsoluteRefPath.startsWith(normalizedProjectRoot + path.sep)) {
|
|
74
|
+
return skillMdContent;
|
|
75
|
+
}
|
|
76
|
+
let referencedContent;
|
|
77
|
+
try {
|
|
78
|
+
referencedContent = await fs.readFile(normalizedAbsoluteRefPath, 'utf8');
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
return skillMdContent;
|
|
82
|
+
}
|
|
83
|
+
const { body: referencedBody } = (0, FrontmatterParser_1.parseFrontmatter)(referencedContent);
|
|
84
|
+
const fmData = rawFrontmatter && Object.keys(rawFrontmatter).length > 0
|
|
85
|
+
? rawFrontmatter
|
|
86
|
+
: frontmatter && Object.keys(frontmatter).length > 0
|
|
87
|
+
? frontmatter
|
|
88
|
+
: null;
|
|
89
|
+
if (fmData) {
|
|
90
|
+
return `---
|
|
91
|
+
${yaml.dump(fmData, { lineWidth: -1, noRefs: true }).trim()}
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
${referencedBody}
|
|
95
|
+
`;
|
|
96
|
+
}
|
|
97
|
+
return `${referencedBody}\n`;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Copies a single skill directory to an agent skill directory:
|
|
101
|
+
* - SKILL.md is compiled (inlines @reference wrapper content)
|
|
102
|
+
* - .mdc files are excluded (Claude-only sources)
|
|
103
|
+
* - all other files are copied as-is
|
|
104
|
+
*/
|
|
105
|
+
async function copySkillDirectoryForNonClaudeAgents(src, dest, projectRoot, skillFolderPath, depth = 0) {
|
|
106
|
+
// Security: Prevent DoS via deeply nested directories
|
|
107
|
+
if (depth >= constants_1.MAX_RECURSION_DEPTH) {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
const stat = await fs.stat(src);
|
|
111
|
+
if (stat.isDirectory()) {
|
|
112
|
+
await fs.mkdir(dest, { recursive: true });
|
|
113
|
+
const entries = await fs.readdir(src, { withFileTypes: true });
|
|
114
|
+
for (const entry of entries) {
|
|
115
|
+
// Exclude all .mdc files from agent skills directories.
|
|
116
|
+
if (entry.isFile() && entry.name.endsWith('.mdc')) {
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
const srcPath = path.join(src, entry.name);
|
|
120
|
+
const destPath = path.join(dest, entry.name);
|
|
121
|
+
await copySkillDirectoryForNonClaudeAgents(srcPath, destPath, projectRoot, skillFolderPath, depth + 1);
|
|
122
|
+
}
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
// Files
|
|
126
|
+
if (path.basename(src) === constants_1.SKILL_MD_FILENAME) {
|
|
127
|
+
const content = await fs.readFile(src, 'utf8');
|
|
128
|
+
const compiled = await compileSkillMdForNonClaudeAgents(content, projectRoot, skillFolderPath);
|
|
129
|
+
await fs.writeFile(dest, compiled, 'utf8');
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
// Extra guard: skip .mdc even if reached via recursion.
|
|
133
|
+
if (src.endsWith('.mdc')) {
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
await fs.copyFile(src, dest);
|
|
137
|
+
}
|
|
51
138
|
/**
|
|
52
139
|
* Check if SKILL.md body is just a reference (single non-empty line starting with @).
|
|
53
140
|
* This replaces the previous synced: true frontmatter detection.
|
|
@@ -237,21 +324,25 @@ ${yaml.dump(skillFrontmatter || { name: skillName }, { lineWidth: -1, noRefs: tr
|
|
|
237
324
|
else {
|
|
238
325
|
referencedPath = path.resolve(skillFolderPath, refCheck.referencePath);
|
|
239
326
|
}
|
|
240
|
-
// One-time migration: for old @.claude/rules/ references,
|
|
241
|
-
//
|
|
242
|
-
|
|
243
|
-
let actualPath = referencedPath;
|
|
327
|
+
// One-time migration: for old @.claude/rules/ references, prefer the
|
|
328
|
+
// original rules path if it exists, then fall back to migrated skills.
|
|
329
|
+
const candidatePaths = [referencedPath];
|
|
244
330
|
if (refCheck.referencePath?.includes('/rules/')) {
|
|
245
331
|
const refFileName = path.basename(refCheck.referencePath);
|
|
246
332
|
const refBaseName = path.basename(refFileName, '.mdc');
|
|
247
|
-
|
|
333
|
+
candidatePaths.push(path.join(skillsDir, refBaseName, refFileName));
|
|
248
334
|
}
|
|
249
335
|
let referencedContent = null;
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
336
|
+
let actualPath = referencedPath;
|
|
337
|
+
for (const candidatePath of candidatePaths) {
|
|
338
|
+
try {
|
|
339
|
+
referencedContent = await fs.readFile(candidatePath, 'utf8');
|
|
340
|
+
actualPath = candidatePath;
|
|
341
|
+
break;
|
|
342
|
+
}
|
|
343
|
+
catch {
|
|
344
|
+
// Try next candidate
|
|
345
|
+
}
|
|
255
346
|
}
|
|
256
347
|
if (referencedContent === null) {
|
|
257
348
|
warnings.push(`Cannot migrate ${skillName}: referenced file not found at ${actualPath}`);
|
|
@@ -364,24 +455,76 @@ async function discoverSkills(projectRoot, skillerDir) {
|
|
|
364
455
|
// Walk the skills tree
|
|
365
456
|
return await (0, SkillsUtils_1.walkSkillsTree)(skillsPath);
|
|
366
457
|
}
|
|
458
|
+
/**
|
|
459
|
+
* Copies skills from source directory to target agent's skills directory.
|
|
460
|
+
* Validates skill structure and returns copy count and warnings.
|
|
461
|
+
*/
|
|
462
|
+
async function copySkillsToAgent(sourceSkillsDir, targetSkillsDir, projectRoot, verbose, dryRun) {
|
|
463
|
+
const warnings = [];
|
|
464
|
+
let copied = 0;
|
|
465
|
+
try {
|
|
466
|
+
await fs.access(sourceSkillsDir);
|
|
467
|
+
}
|
|
468
|
+
catch {
|
|
469
|
+
// Source directory doesn't exist
|
|
470
|
+
return { copied: 0, warnings: [] };
|
|
471
|
+
}
|
|
472
|
+
// Use walkSkillsTree to discover skills
|
|
473
|
+
const skillsTree = await (0, SkillsUtils_1.walkSkillsTree)(sourceSkillsDir);
|
|
474
|
+
// Validate and copy each skill
|
|
475
|
+
for (const skill of skillsTree.skills) {
|
|
476
|
+
// skill.path is absolute, use it directly
|
|
477
|
+
const skillPath = skill.path;
|
|
478
|
+
const skillMdPath = path.join(skillPath, constants_1.SKILL_MD_FILENAME);
|
|
479
|
+
// Validate: skill must have SKILL.md
|
|
480
|
+
try {
|
|
481
|
+
await fs.access(skillMdPath);
|
|
482
|
+
}
|
|
483
|
+
catch {
|
|
484
|
+
warnings.push(`Skill '${skill.name}' missing required SKILL.md file, skipping`);
|
|
485
|
+
continue;
|
|
486
|
+
}
|
|
487
|
+
// Copy skill directory to target using relative path
|
|
488
|
+
const relativeSkillPath = path.relative(sourceSkillsDir, skill.path);
|
|
489
|
+
const targetSkillPath = path.join(targetSkillsDir, relativeSkillPath);
|
|
490
|
+
if (!dryRun) {
|
|
491
|
+
await copySkillDirectoryForNonClaudeAgents(skillPath, targetSkillPath, projectRoot, skillPath);
|
|
492
|
+
}
|
|
493
|
+
(0, constants_1.logVerboseInfo)(dryRun
|
|
494
|
+
? `DRY RUN: Would copy skill '${skill.name}' to ${targetSkillsDir}`
|
|
495
|
+
: `Copied skill '${skill.name}' to ${targetSkillsDir}`, verbose, dryRun);
|
|
496
|
+
copied++;
|
|
497
|
+
}
|
|
498
|
+
return { copied, warnings };
|
|
499
|
+
}
|
|
367
500
|
/**
|
|
368
501
|
* Gets the paths that skills will generate, for gitignore purposes.
|
|
369
|
-
*
|
|
370
|
-
* This function now returns an empty array as skills are committed.
|
|
502
|
+
* Collects paths from all agents with native skills support, excluding the source (.claude/skills).
|
|
371
503
|
*/
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
504
|
+
function getSkillsGitignorePaths(projectRoot, agents) {
|
|
505
|
+
const paths = [];
|
|
506
|
+
const sourceSkillsPath = path.join(projectRoot, constants_1.CLAUDE_SKILLS_PATH);
|
|
507
|
+
for (const agent of agents) {
|
|
508
|
+
if (agent.supportsNativeSkills?.() && agent.getSkillsPath) {
|
|
509
|
+
const skillsPath = agent.getSkillsPath(projectRoot);
|
|
510
|
+
if (skillsPath && skillsPath !== sourceSkillsPath) {
|
|
511
|
+
// Convert to relative path for gitignore
|
|
512
|
+
const relativePath = path.relative(projectRoot, skillsPath);
|
|
513
|
+
// Deduplicate paths
|
|
514
|
+
if (!paths.includes(relativePath)) {
|
|
515
|
+
paths.push(relativePath);
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
return paths;
|
|
378
521
|
}
|
|
379
522
|
/**
|
|
380
523
|
* Propagates skills for agents that need them.
|
|
381
524
|
* In the new architecture, skills are committed to .claude/skills and discovered by agents natively.
|
|
382
525
|
* This function now only discovers and validates skills.
|
|
383
526
|
*/
|
|
384
|
-
async function propagateSkills(projectRoot,
|
|
527
|
+
async function propagateSkills(projectRoot, agents, skillsEnabled, verbose, dryRun, skillerDir) {
|
|
385
528
|
if (!skillsEnabled) {
|
|
386
529
|
(0, constants_1.logVerboseInfo)('Skills support disabled', verbose, dryRun);
|
|
387
530
|
return;
|
|
@@ -422,6 +565,27 @@ async function propagateSkills(projectRoot, _agents, skillsEnabled, verbose, dry
|
|
|
422
565
|
return;
|
|
423
566
|
}
|
|
424
567
|
(0, constants_1.logVerboseInfo)(`Discovered ${skills.length} skill(s)`, verbose, dryRun);
|
|
568
|
+
// Copy skills to all agents with native skills support
|
|
569
|
+
const destinationPaths = new Set();
|
|
570
|
+
for (const agent of agents) {
|
|
571
|
+
if (agent.supportsNativeSkills?.() && agent.getSkillsPath) {
|
|
572
|
+
const targetPath = agent.getSkillsPath(projectRoot);
|
|
573
|
+
if (targetPath && targetPath !== skillsDir) {
|
|
574
|
+
// Deduplicate shared paths
|
|
575
|
+
destinationPaths.add(targetPath);
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
// Copy skills to each unique destination
|
|
580
|
+
for (const targetPath of destinationPaths) {
|
|
581
|
+
const result = await copySkillsToAgent(skillsDir, targetPath, projectRoot, verbose, dryRun);
|
|
582
|
+
if (result.copied > 0) {
|
|
583
|
+
(0, constants_1.logVerboseInfo)(`Copied ${result.copied} skill(s) to ${targetPath}`, verbose, dryRun);
|
|
584
|
+
}
|
|
585
|
+
for (const warning of result.warnings) {
|
|
586
|
+
(0, constants_1.logWarn)(warning, dryRun);
|
|
587
|
+
}
|
|
588
|
+
}
|
|
425
589
|
}
|
|
426
590
|
/**
|
|
427
591
|
* Recursively finds all folders containing SKILL.md in a directory.
|
package/dist/lib.js
CHANGED
|
@@ -147,7 +147,7 @@ async function applyAllAgentConfigs(projectRoot, includedAgents, configPath, cli
|
|
|
147
147
|
if (skillsEnabledForGitignore) {
|
|
148
148
|
// Skills enabled by default or explicitly
|
|
149
149
|
const { getSkillsGitignorePaths } = await Promise.resolve().then(() => __importStar(require('./core/SkillsProcessor')));
|
|
150
|
-
const skillsPaths =
|
|
150
|
+
const skillsPaths = getSkillsGitignorePaths(projectRoot, selectedAgents);
|
|
151
151
|
allGeneratedPaths = [...generatedPaths, ...skillsPaths];
|
|
152
152
|
}
|
|
153
153
|
await (0, apply_engine_1.updateGitignore)(projectRoot, allGeneratedPaths, loadedConfig, cliGitignoreEnabled, dryRun);
|