@intellectronica/ruler 0.3.21 → 0.3.23
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 +33 -15
- package/dist/agents/AmpAgent.js +3 -0
- package/dist/agents/CursorAgent.js +3 -0
- package/dist/agents/GeminiCliAgent.js +3 -0
- package/dist/agents/KiloCodeAgent.js +3 -0
- package/dist/agents/PiAgent.js +19 -0
- package/dist/agents/RooCodeAgent.js +3 -0
- package/dist/agents/index.js +2 -0
- package/dist/cli/handlers.js +1 -1
- package/dist/constants.js +5 -1
- package/dist/core/SkillsProcessor.js +279 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -60,6 +60,7 @@ Ruler solves this by providing a **single source of truth** for all your AI agen
|
|
|
60
60
|
| GitHub Copilot | `AGENTS.md` | `.vscode/mcp.json` |
|
|
61
61
|
| Claude Code | `CLAUDE.md` | `.mcp.json` |
|
|
62
62
|
| OpenAI Codex CLI | `AGENTS.md` | `.codex/config.toml` |
|
|
63
|
+
| Pi Coding Agent | `AGENTS.md` | - |
|
|
63
64
|
| Jules | `AGENTS.md` | - |
|
|
64
65
|
| Cursor | `AGENTS.md` | `.cursor/mcp.json` |
|
|
65
66
|
| Windsurf | `AGENTS.md` | `.windsurf/mcp_config.json` |
|
|
@@ -317,15 +318,15 @@ ruler revert [options]
|
|
|
317
318
|
|
|
318
319
|
### Options
|
|
319
320
|
|
|
320
|
-
| Option | Description
|
|
321
|
-
| ------------------------------ |
|
|
322
|
-
| `--project-root <path>` | Path to your project's root (default: current directory)
|
|
323
|
-
| `--agents <agent1,agent2,...>` | Comma-separated list of agent names to revert (agentsmd, aider, amazonqcli, amp, antigravity, augmentcode, claude, cline, codex, copilot, crush, cursor, firebase, firebender, gemini-cli, goose, jules, junie, kilocode, kiro, mistral, opencode, openhands, qwen, roo, trae, warp, windsurf, zed) |
|
|
324
|
-
| `--config <path>` | Path to a custom `ruler.toml` configuration file
|
|
325
|
-
| `--keep-backups` | Keep backup files (.bak) after restoration (default: false)
|
|
326
|
-
| `--dry-run` | Preview changes without actually reverting files
|
|
327
|
-
| `--verbose` / `-v` | Display detailed output during execution
|
|
328
|
-
| `--local-only` | Only search for local .ruler directories, ignore global config
|
|
321
|
+
| Option | Description |
|
|
322
|
+
| ------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
323
|
+
| `--project-root <path>` | Path to your project's root (default: current directory) |
|
|
324
|
+
| `--agents <agent1,agent2,...>` | Comma-separated list of agent names to revert (agentsmd, aider, amazonqcli, amp, antigravity, augmentcode, claude, cline, codex, copilot, crush, cursor, firebase, firebender, gemini-cli, goose, jules, junie, kilocode, kiro, mistral, opencode, openhands, pi, qwen, roo, trae, warp, windsurf, zed) |
|
|
325
|
+
| `--config <path>` | Path to a custom `ruler.toml` configuration file |
|
|
326
|
+
| `--keep-backups` | Keep backup files (.bak) after restoration (default: false) |
|
|
327
|
+
| `--dry-run` | Preview changes without actually reverting files |
|
|
328
|
+
| `--verbose` / `-v` | Display detailed output during execution |
|
|
329
|
+
| `--local-only` | Only search for local .ruler directories, ignore global config |
|
|
329
330
|
|
|
330
331
|
### Common Examples
|
|
331
332
|
|
|
@@ -569,9 +570,16 @@ Skills are specialized knowledge packages that extend AI agent capabilities with
|
|
|
569
570
|
- **Agents with native skills support**: Skills are copied directly to each agent's native skills directory:
|
|
570
571
|
- **Claude Code**: `.claude/skills/`
|
|
571
572
|
- **GitHub Copilot**: `.claude/skills/` (shared with Claude Code)
|
|
573
|
+
- **Kilo Code**: `.claude/skills/` (shared with Claude Code)
|
|
572
574
|
- **OpenAI Codex CLI**: `.codex/skills/`
|
|
573
575
|
- **OpenCode**: `.opencode/skill/`
|
|
576
|
+
- **Pi Coding Agent**: `.pi/skills/`
|
|
574
577
|
- **Goose**: `.agents/skills/`
|
|
578
|
+
- **Amp**: `.agents/skills/` (shared with Goose)
|
|
579
|
+
- **Mistral Vibe**: `.vibe/skills/`
|
|
580
|
+
- **Roo Code**: `.roo/skills/`
|
|
581
|
+
- **Gemini CLI**: `.gemini/skills/`
|
|
582
|
+
- **Cursor**: `.cursor/skills/`
|
|
575
583
|
- **Other MCP-compatible agents**: Skills are copied to `.skillz/` and a Skillz MCP server is automatically configured via `uvx`
|
|
576
584
|
|
|
577
585
|
### Skills Directory Structure
|
|
@@ -627,7 +635,7 @@ For agents that support MCP but don't have native skills support, Ruler automati
|
|
|
627
635
|
2. Configures a Skillz MCP server in the agent's configuration
|
|
628
636
|
3. Uses `uvx` to launch the server with the absolute path to `.skillz`
|
|
629
637
|
|
|
630
|
-
Agents using native skills support (Claude Code, GitHub Copilot, OpenAI Codex CLI, OpenCode, and
|
|
638
|
+
Agents using native skills support (Claude Code, GitHub Copilot, Kilo Code, OpenAI Codex CLI, OpenCode, Pi Coding Agent, Goose, Amp, Mistral Vibe, Roo Code, Gemini CLI, and Cursor) **do not** use the Skillz MCP server and instead use their own native skills directories.
|
|
631
639
|
|
|
632
640
|
Example auto-generated MCP server configuration:
|
|
633
641
|
|
|
@@ -641,17 +649,22 @@ args = ["skillz@latest", "/absolute/path/to/project/.skillz"]
|
|
|
641
649
|
|
|
642
650
|
When skills support is enabled and gitignore integration is active, Ruler automatically adds:
|
|
643
651
|
|
|
644
|
-
- `.claude/skills/` (for Claude Code and
|
|
652
|
+
- `.claude/skills/` (for Claude Code, GitHub Copilot, and Kilo Code)
|
|
645
653
|
- `.codex/skills/` (for OpenAI Codex CLI)
|
|
646
654
|
- `.opencode/skill/` (for OpenCode)
|
|
647
|
-
- `.
|
|
655
|
+
- `.pi/skills/` (for Pi Coding Agent)
|
|
656
|
+
- `.agents/skills/` (for Goose and Amp)
|
|
657
|
+
- `.vibe/skills/` (for Mistral Vibe)
|
|
658
|
+
- `.roo/skills/` (for Roo Code)
|
|
659
|
+
- `.gemini/skills/` (for Gemini CLI)
|
|
660
|
+
- `.cursor/skills/` (for Cursor)
|
|
648
661
|
- `.skillz/` (for other MCP-based agents)
|
|
649
662
|
|
|
650
663
|
to your `.gitignore` file within the managed Ruler block.
|
|
651
664
|
|
|
652
665
|
### Requirements
|
|
653
666
|
|
|
654
|
-
- **For agents with native skills support** (Claude Code, GitHub Copilot, OpenAI Codex CLI, OpenCode, Goose): No additional requirements
|
|
667
|
+
- **For agents with native skills support** (Claude Code, GitHub Copilot, Kilo Code, OpenAI Codex CLI, OpenCode, Pi Coding Agent, Goose, Amp, Mistral Vibe, Roo Code, Gemini CLI, Cursor): No additional requirements
|
|
655
668
|
- **For other MCP agents**: `uv` must be installed and available in your PATH
|
|
656
669
|
```bash
|
|
657
670
|
# Install uv if needed
|
|
@@ -699,10 +712,15 @@ EOF
|
|
|
699
712
|
ruler apply
|
|
700
713
|
|
|
701
714
|
# 3. Skills are now available to compatible agents:
|
|
702
|
-
# - Claude Code &
|
|
715
|
+
# - Claude Code, GitHub Copilot & Kilo Code: .claude/skills/my-skill/
|
|
703
716
|
# - OpenAI Codex CLI: .codex/skills/my-skill/
|
|
704
717
|
# - OpenCode: .opencode/skill/my-skill/
|
|
705
|
-
# -
|
|
718
|
+
# - Pi Coding Agent: .pi/skills/my-skill/
|
|
719
|
+
# - Goose & Amp: .agents/skills/my-skill/
|
|
720
|
+
# - Mistral Vibe: .vibe/skills/my-skill/
|
|
721
|
+
# - Roo Code: .roo/skills/my-skill/
|
|
722
|
+
# - Gemini CLI: .gemini/skills/my-skill/
|
|
723
|
+
# - Cursor: .cursor/skills/my-skill/
|
|
706
724
|
# - Other MCP agents: .skillz/my-skill/ + Skillz MCP server configured
|
|
707
725
|
```
|
|
708
726
|
|
package/dist/agents/AmpAgent.js
CHANGED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PiAgent = void 0;
|
|
4
|
+
const AgentsMdAgent_1 = require("./AgentsMdAgent");
|
|
5
|
+
/**
|
|
6
|
+
* Pi Coding Agent adapter.
|
|
7
|
+
*/
|
|
8
|
+
class PiAgent extends AgentsMdAgent_1.AgentsMdAgent {
|
|
9
|
+
getIdentifier() {
|
|
10
|
+
return 'pi';
|
|
11
|
+
}
|
|
12
|
+
getName() {
|
|
13
|
+
return 'Pi Coding Agent';
|
|
14
|
+
}
|
|
15
|
+
supportsNativeSkills() {
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
exports.PiAgent = PiAgent;
|
package/dist/agents/index.js
CHANGED
|
@@ -33,6 +33,7 @@ const AmazonQCliAgent_1 = require("./AmazonQCliAgent");
|
|
|
33
33
|
const FirebenderAgent_1 = require("./FirebenderAgent");
|
|
34
34
|
const AntigravityAgent_1 = require("./AntigravityAgent");
|
|
35
35
|
const MistralVibeAgent_1 = require("./MistralVibeAgent");
|
|
36
|
+
const PiAgent_1 = require("./PiAgent");
|
|
36
37
|
exports.allAgents = [
|
|
37
38
|
new CopilotAgent_1.CopilotAgent(),
|
|
38
39
|
new ClaudeAgent_1.ClaudeAgent(),
|
|
@@ -63,6 +64,7 @@ exports.allAgents = [
|
|
|
63
64
|
new FirebenderAgent_1.FirebenderAgent(),
|
|
64
65
|
new AntigravityAgent_1.AntigravityAgent(),
|
|
65
66
|
new MistralVibeAgent_1.MistralVibeAgent(),
|
|
67
|
+
new PiAgent_1.PiAgent(),
|
|
66
68
|
];
|
|
67
69
|
/**
|
|
68
70
|
* Generates a comma-separated list of agent identifiers for CLI help text.
|
package/dist/cli/handlers.js
CHANGED
|
@@ -152,7 +152,7 @@ async function initHandler(argv) {
|
|
|
152
152
|
|
|
153
153
|
# --- Agent Specific Configurations ---
|
|
154
154
|
# You can enable/disable agents and override their default output paths here.
|
|
155
|
-
# Use lowercase agent identifiers:
|
|
155
|
+
# Use lowercase agent identifiers: aider, amp, claude, cline, codex, copilot, cursor, kilocode, pi, windsurf
|
|
156
156
|
|
|
157
157
|
# [agents.copilot]
|
|
158
158
|
# enabled = true
|
package/dist/constants.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.SKILLZ_MCP_SERVER_NAME = exports.SKILL_MD_FILENAME = exports.SKILLZ_DIR = exports.VIBE_SKILLS_PATH = exports.GOOSE_SKILLS_PATH = exports.OPENCODE_SKILLS_PATH = exports.CODEX_SKILLS_PATH = exports.CLAUDE_SKILLS_PATH = exports.RULER_SKILLS_PATH = exports.SKILLS_DIR = exports.DEFAULT_RULES_FILENAME = exports.ERROR_PREFIX = void 0;
|
|
3
|
+
exports.SKILLZ_MCP_SERVER_NAME = exports.SKILL_MD_FILENAME = exports.SKILLZ_DIR = exports.CURSOR_SKILLS_PATH = exports.GEMINI_SKILLS_PATH = exports.ROO_SKILLS_PATH = exports.VIBE_SKILLS_PATH = exports.GOOSE_SKILLS_PATH = exports.PI_SKILLS_PATH = exports.OPENCODE_SKILLS_PATH = exports.CODEX_SKILLS_PATH = exports.CLAUDE_SKILLS_PATH = exports.RULER_SKILLS_PATH = exports.SKILLS_DIR = exports.DEFAULT_RULES_FILENAME = exports.ERROR_PREFIX = void 0;
|
|
4
4
|
exports.actionPrefix = actionPrefix;
|
|
5
5
|
exports.createRulerError = createRulerError;
|
|
6
6
|
exports.logVerbose = logVerbose;
|
|
@@ -55,8 +55,12 @@ exports.RULER_SKILLS_PATH = '.ruler/skills';
|
|
|
55
55
|
exports.CLAUDE_SKILLS_PATH = '.claude/skills';
|
|
56
56
|
exports.CODEX_SKILLS_PATH = '.codex/skills';
|
|
57
57
|
exports.OPENCODE_SKILLS_PATH = '.opencode/skill';
|
|
58
|
+
exports.PI_SKILLS_PATH = '.pi/skills';
|
|
58
59
|
exports.GOOSE_SKILLS_PATH = '.agents/skills';
|
|
59
60
|
exports.VIBE_SKILLS_PATH = '.vibe/skills';
|
|
61
|
+
exports.ROO_SKILLS_PATH = '.roo/skills';
|
|
62
|
+
exports.GEMINI_SKILLS_PATH = '.gemini/skills';
|
|
63
|
+
exports.CURSOR_SKILLS_PATH = '.cursor/skills';
|
|
60
64
|
exports.SKILLZ_DIR = '.skillz';
|
|
61
65
|
exports.SKILL_MD_FILENAME = 'SKILL.md';
|
|
62
66
|
exports.SKILLZ_MCP_SERVER_NAME = 'skillz';
|
|
@@ -39,8 +39,12 @@ exports.propagateSkills = propagateSkills;
|
|
|
39
39
|
exports.propagateSkillsForClaude = propagateSkillsForClaude;
|
|
40
40
|
exports.propagateSkillsForCodex = propagateSkillsForCodex;
|
|
41
41
|
exports.propagateSkillsForOpenCode = propagateSkillsForOpenCode;
|
|
42
|
+
exports.propagateSkillsForPi = propagateSkillsForPi;
|
|
42
43
|
exports.propagateSkillsForGoose = propagateSkillsForGoose;
|
|
43
44
|
exports.propagateSkillsForVibe = propagateSkillsForVibe;
|
|
45
|
+
exports.propagateSkillsForRoo = propagateSkillsForRoo;
|
|
46
|
+
exports.propagateSkillsForGemini = propagateSkillsForGemini;
|
|
47
|
+
exports.propagateSkillsForCursor = propagateSkillsForCursor;
|
|
44
48
|
exports.propagateSkillsForSkillz = propagateSkillsForSkillz;
|
|
45
49
|
exports.buildSkillzMcpConfig = buildSkillzMcpConfig;
|
|
46
50
|
const path = __importStar(require("path"));
|
|
@@ -78,13 +82,17 @@ async function getSkillsGitignorePaths(projectRoot) {
|
|
|
78
82
|
return [];
|
|
79
83
|
}
|
|
80
84
|
// Import here to avoid circular dependency
|
|
81
|
-
const { CLAUDE_SKILLS_PATH, CODEX_SKILLS_PATH, OPENCODE_SKILLS_PATH, GOOSE_SKILLS_PATH, VIBE_SKILLS_PATH, SKILLZ_DIR, } = await Promise.resolve().then(() => __importStar(require('../constants')));
|
|
85
|
+
const { CLAUDE_SKILLS_PATH, CODEX_SKILLS_PATH, OPENCODE_SKILLS_PATH, PI_SKILLS_PATH, GOOSE_SKILLS_PATH, VIBE_SKILLS_PATH, ROO_SKILLS_PATH, GEMINI_SKILLS_PATH, CURSOR_SKILLS_PATH, SKILLZ_DIR, } = await Promise.resolve().then(() => __importStar(require('../constants')));
|
|
82
86
|
return [
|
|
83
87
|
path.join(projectRoot, CLAUDE_SKILLS_PATH),
|
|
84
88
|
path.join(projectRoot, CODEX_SKILLS_PATH),
|
|
85
89
|
path.join(projectRoot, OPENCODE_SKILLS_PATH),
|
|
90
|
+
path.join(projectRoot, PI_SKILLS_PATH),
|
|
86
91
|
path.join(projectRoot, GOOSE_SKILLS_PATH),
|
|
87
92
|
path.join(projectRoot, VIBE_SKILLS_PATH),
|
|
93
|
+
path.join(projectRoot, ROO_SKILLS_PATH),
|
|
94
|
+
path.join(projectRoot, GEMINI_SKILLS_PATH),
|
|
95
|
+
path.join(projectRoot, CURSOR_SKILLS_PATH),
|
|
88
96
|
path.join(projectRoot, SKILLZ_DIR),
|
|
89
97
|
];
|
|
90
98
|
}
|
|
@@ -115,8 +123,12 @@ async function cleanupSkillsDirectories(projectRoot, dryRun, verbose) {
|
|
|
115
123
|
const claudeSkillsPath = path.join(projectRoot, constants_1.CLAUDE_SKILLS_PATH);
|
|
116
124
|
const codexSkillsPath = path.join(projectRoot, constants_1.CODEX_SKILLS_PATH);
|
|
117
125
|
const opencodeSkillsPath = path.join(projectRoot, constants_1.OPENCODE_SKILLS_PATH);
|
|
126
|
+
const piSkillsPath = path.join(projectRoot, constants_1.PI_SKILLS_PATH);
|
|
118
127
|
const gooseSkillsPath = path.join(projectRoot, constants_1.GOOSE_SKILLS_PATH);
|
|
119
128
|
const vibeSkillsPath = path.join(projectRoot, constants_1.VIBE_SKILLS_PATH);
|
|
129
|
+
const rooSkillsPath = path.join(projectRoot, constants_1.ROO_SKILLS_PATH);
|
|
130
|
+
const geminiSkillsPath = path.join(projectRoot, constants_1.GEMINI_SKILLS_PATH);
|
|
131
|
+
const cursorSkillsPath = path.join(projectRoot, constants_1.CURSOR_SKILLS_PATH);
|
|
120
132
|
const skillzPath = path.join(projectRoot, constants_1.SKILLZ_DIR);
|
|
121
133
|
// Clean up .claude/skills
|
|
122
134
|
try {
|
|
@@ -160,6 +172,20 @@ async function cleanupSkillsDirectories(projectRoot, dryRun, verbose) {
|
|
|
160
172
|
catch {
|
|
161
173
|
// Directory doesn't exist, nothing to clean
|
|
162
174
|
}
|
|
175
|
+
// Clean up .pi/skills
|
|
176
|
+
try {
|
|
177
|
+
await fs.access(piSkillsPath);
|
|
178
|
+
if (dryRun) {
|
|
179
|
+
(0, constants_1.logVerboseInfo)(`DRY RUN: Would remove ${constants_1.PI_SKILLS_PATH}`, verbose, dryRun);
|
|
180
|
+
}
|
|
181
|
+
else {
|
|
182
|
+
await fs.rm(piSkillsPath, { recursive: true, force: true });
|
|
183
|
+
(0, constants_1.logVerboseInfo)(`Removed ${constants_1.PI_SKILLS_PATH} (skills disabled)`, verbose, dryRun);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
catch {
|
|
187
|
+
// Directory doesn't exist, nothing to clean
|
|
188
|
+
}
|
|
163
189
|
// Clean up .agents/skills
|
|
164
190
|
try {
|
|
165
191
|
await fs.access(gooseSkillsPath);
|
|
@@ -188,6 +214,48 @@ async function cleanupSkillsDirectories(projectRoot, dryRun, verbose) {
|
|
|
188
214
|
catch {
|
|
189
215
|
// Directory doesn't exist, nothing to clean
|
|
190
216
|
}
|
|
217
|
+
// Clean up .roo/skills
|
|
218
|
+
try {
|
|
219
|
+
await fs.access(rooSkillsPath);
|
|
220
|
+
if (dryRun) {
|
|
221
|
+
(0, constants_1.logVerboseInfo)(`DRY RUN: Would remove ${constants_1.ROO_SKILLS_PATH}`, verbose, dryRun);
|
|
222
|
+
}
|
|
223
|
+
else {
|
|
224
|
+
await fs.rm(rooSkillsPath, { recursive: true, force: true });
|
|
225
|
+
(0, constants_1.logVerboseInfo)(`Removed ${constants_1.ROO_SKILLS_PATH} (skills disabled)`, verbose, dryRun);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
catch {
|
|
229
|
+
// Directory doesn't exist, nothing to clean
|
|
230
|
+
}
|
|
231
|
+
// Clean up .gemini/skills
|
|
232
|
+
try {
|
|
233
|
+
await fs.access(geminiSkillsPath);
|
|
234
|
+
if (dryRun) {
|
|
235
|
+
(0, constants_1.logVerboseInfo)(`DRY RUN: Would remove ${constants_1.GEMINI_SKILLS_PATH}`, verbose, dryRun);
|
|
236
|
+
}
|
|
237
|
+
else {
|
|
238
|
+
await fs.rm(geminiSkillsPath, { recursive: true, force: true });
|
|
239
|
+
(0, constants_1.logVerboseInfo)(`Removed ${constants_1.GEMINI_SKILLS_PATH} (skills disabled)`, verbose, dryRun);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
catch {
|
|
243
|
+
// Directory doesn't exist, nothing to clean
|
|
244
|
+
}
|
|
245
|
+
// Clean up .cursor/skills
|
|
246
|
+
try {
|
|
247
|
+
await fs.access(cursorSkillsPath);
|
|
248
|
+
if (dryRun) {
|
|
249
|
+
(0, constants_1.logVerboseInfo)(`DRY RUN: Would remove ${constants_1.CURSOR_SKILLS_PATH}`, verbose, dryRun);
|
|
250
|
+
}
|
|
251
|
+
else {
|
|
252
|
+
await fs.rm(cursorSkillsPath, { recursive: true, force: true });
|
|
253
|
+
(0, constants_1.logVerboseInfo)(`Removed ${constants_1.CURSOR_SKILLS_PATH} (skills disabled)`, verbose, dryRun);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
catch {
|
|
257
|
+
// Directory doesn't exist, nothing to clean
|
|
258
|
+
}
|
|
191
259
|
// Clean up .skillz
|
|
192
260
|
try {
|
|
193
261
|
await fs.access(skillzPath);
|
|
@@ -246,16 +314,24 @@ async function propagateSkills(projectRoot, agents, skillsEnabled, verbose, dryR
|
|
|
246
314
|
}
|
|
247
315
|
// Copy to Claude skills directory if needed
|
|
248
316
|
if (hasNativeSkillsAgent) {
|
|
249
|
-
(0, constants_1.logVerboseInfo)(`Copying skills to ${constants_1.CLAUDE_SKILLS_PATH} for Claude Code and
|
|
317
|
+
(0, constants_1.logVerboseInfo)(`Copying skills to ${constants_1.CLAUDE_SKILLS_PATH} for Claude Code, GitHub Copilot, and Kilo Code`, verbose, dryRun);
|
|
250
318
|
await propagateSkillsForClaude(projectRoot, { dryRun });
|
|
251
319
|
(0, constants_1.logVerboseInfo)(`Copying skills to ${constants_1.CODEX_SKILLS_PATH} for OpenAI Codex CLI`, verbose, dryRun);
|
|
252
320
|
await propagateSkillsForCodex(projectRoot, { dryRun });
|
|
253
321
|
(0, constants_1.logVerboseInfo)(`Copying skills to ${constants_1.OPENCODE_SKILLS_PATH} for OpenCode`, verbose, dryRun);
|
|
254
322
|
await propagateSkillsForOpenCode(projectRoot, { dryRun });
|
|
255
|
-
(0, constants_1.logVerboseInfo)(`Copying skills to ${constants_1.
|
|
323
|
+
(0, constants_1.logVerboseInfo)(`Copying skills to ${constants_1.PI_SKILLS_PATH} for Pi Coding Agent`, verbose, dryRun);
|
|
324
|
+
await propagateSkillsForPi(projectRoot, { dryRun });
|
|
325
|
+
(0, constants_1.logVerboseInfo)(`Copying skills to ${constants_1.GOOSE_SKILLS_PATH} for Goose and Amp`, verbose, dryRun);
|
|
256
326
|
await propagateSkillsForGoose(projectRoot, { dryRun });
|
|
257
327
|
(0, constants_1.logVerboseInfo)(`Copying skills to ${constants_1.VIBE_SKILLS_PATH} for Mistral Vibe`, verbose, dryRun);
|
|
258
328
|
await propagateSkillsForVibe(projectRoot, { dryRun });
|
|
329
|
+
(0, constants_1.logVerboseInfo)(`Copying skills to ${constants_1.ROO_SKILLS_PATH} for Roo Code`, verbose, dryRun);
|
|
330
|
+
await propagateSkillsForRoo(projectRoot, { dryRun });
|
|
331
|
+
(0, constants_1.logVerboseInfo)(`Copying skills to ${constants_1.GEMINI_SKILLS_PATH} for Gemini CLI`, verbose, dryRun);
|
|
332
|
+
await propagateSkillsForGemini(projectRoot, { dryRun });
|
|
333
|
+
(0, constants_1.logVerboseInfo)(`Copying skills to ${constants_1.CURSOR_SKILLS_PATH} for Cursor`, verbose, dryRun);
|
|
334
|
+
await propagateSkillsForCursor(projectRoot, { dryRun });
|
|
259
335
|
}
|
|
260
336
|
// Copy to .skillz directory if needed
|
|
261
337
|
if (hasMcpAgent) {
|
|
@@ -413,6 +489,56 @@ async function propagateSkillsForOpenCode(projectRoot, options) {
|
|
|
413
489
|
}
|
|
414
490
|
return [];
|
|
415
491
|
}
|
|
492
|
+
/**
|
|
493
|
+
* Propagates skills for Pi Coding Agent by copying .ruler/skills to .pi/skills.
|
|
494
|
+
* Uses atomic replace to ensure safe overwriting of existing skills.
|
|
495
|
+
* Returns dry-run steps if dryRun is true, otherwise returns empty array.
|
|
496
|
+
*/
|
|
497
|
+
async function propagateSkillsForPi(projectRoot, options) {
|
|
498
|
+
const skillsDir = path.join(projectRoot, constants_1.RULER_SKILLS_PATH);
|
|
499
|
+
const piSkillsPath = path.join(projectRoot, constants_1.PI_SKILLS_PATH);
|
|
500
|
+
const piDir = path.dirname(piSkillsPath);
|
|
501
|
+
// Check if source skills directory exists
|
|
502
|
+
try {
|
|
503
|
+
await fs.access(skillsDir);
|
|
504
|
+
}
|
|
505
|
+
catch {
|
|
506
|
+
// No skills directory - return empty
|
|
507
|
+
return [];
|
|
508
|
+
}
|
|
509
|
+
if (options.dryRun) {
|
|
510
|
+
return [`Copy skills from ${constants_1.RULER_SKILLS_PATH} to ${constants_1.PI_SKILLS_PATH}`];
|
|
511
|
+
}
|
|
512
|
+
// Ensure .pi directory exists
|
|
513
|
+
await fs.mkdir(piDir, { recursive: true });
|
|
514
|
+
// Use atomic replace: copy to temp, then rename
|
|
515
|
+
const tempDir = path.join(piDir, `skills.tmp-${Date.now()}`);
|
|
516
|
+
try {
|
|
517
|
+
// Copy to temp directory
|
|
518
|
+
await (0, SkillsUtils_1.copySkillsDirectory)(skillsDir, tempDir);
|
|
519
|
+
// Atomically replace the target
|
|
520
|
+
// First, remove existing target if it exists
|
|
521
|
+
try {
|
|
522
|
+
await fs.rm(piSkillsPath, { recursive: true, force: true });
|
|
523
|
+
}
|
|
524
|
+
catch {
|
|
525
|
+
// Target didn't exist, that's fine
|
|
526
|
+
}
|
|
527
|
+
// Rename temp to target
|
|
528
|
+
await fs.rename(tempDir, piSkillsPath);
|
|
529
|
+
}
|
|
530
|
+
catch (error) {
|
|
531
|
+
// Clean up temp directory on error
|
|
532
|
+
try {
|
|
533
|
+
await fs.rm(tempDir, { recursive: true, force: true });
|
|
534
|
+
}
|
|
535
|
+
catch {
|
|
536
|
+
// Ignore cleanup errors
|
|
537
|
+
}
|
|
538
|
+
throw error;
|
|
539
|
+
}
|
|
540
|
+
return [];
|
|
541
|
+
}
|
|
416
542
|
/**
|
|
417
543
|
* Propagates skills for Goose by copying .ruler/skills to .agents/skills.
|
|
418
544
|
* Uses atomic replace to ensure safe overwriting of existing skills.
|
|
@@ -513,6 +639,156 @@ async function propagateSkillsForVibe(projectRoot, options) {
|
|
|
513
639
|
}
|
|
514
640
|
return [];
|
|
515
641
|
}
|
|
642
|
+
/**
|
|
643
|
+
* Propagates skills for Roo Code by copying .ruler/skills to .roo/skills.
|
|
644
|
+
* Uses atomic replace to ensure safe overwriting of existing skills.
|
|
645
|
+
* Returns dry-run steps if dryRun is true, otherwise returns empty array.
|
|
646
|
+
*/
|
|
647
|
+
async function propagateSkillsForRoo(projectRoot, options) {
|
|
648
|
+
const skillsDir = path.join(projectRoot, constants_1.RULER_SKILLS_PATH);
|
|
649
|
+
const rooSkillsPath = path.join(projectRoot, constants_1.ROO_SKILLS_PATH);
|
|
650
|
+
const rooDir = path.dirname(rooSkillsPath);
|
|
651
|
+
// Check if source skills directory exists
|
|
652
|
+
try {
|
|
653
|
+
await fs.access(skillsDir);
|
|
654
|
+
}
|
|
655
|
+
catch {
|
|
656
|
+
// No skills directory - return empty
|
|
657
|
+
return [];
|
|
658
|
+
}
|
|
659
|
+
if (options.dryRun) {
|
|
660
|
+
return [`Copy skills from ${constants_1.RULER_SKILLS_PATH} to ${constants_1.ROO_SKILLS_PATH}`];
|
|
661
|
+
}
|
|
662
|
+
// Ensure .roo directory exists
|
|
663
|
+
await fs.mkdir(rooDir, { recursive: true });
|
|
664
|
+
// Use atomic replace: copy to temp, then rename
|
|
665
|
+
const tempDir = path.join(rooDir, `skills.tmp-${Date.now()}`);
|
|
666
|
+
try {
|
|
667
|
+
// Copy to temp directory
|
|
668
|
+
await (0, SkillsUtils_1.copySkillsDirectory)(skillsDir, tempDir);
|
|
669
|
+
// Atomically replace the target
|
|
670
|
+
// First, remove existing target if it exists
|
|
671
|
+
try {
|
|
672
|
+
await fs.rm(rooSkillsPath, { recursive: true, force: true });
|
|
673
|
+
}
|
|
674
|
+
catch {
|
|
675
|
+
// Target didn't exist, that's fine
|
|
676
|
+
}
|
|
677
|
+
// Rename temp to target
|
|
678
|
+
await fs.rename(tempDir, rooSkillsPath);
|
|
679
|
+
}
|
|
680
|
+
catch (error) {
|
|
681
|
+
// Clean up temp directory on error
|
|
682
|
+
try {
|
|
683
|
+
await fs.rm(tempDir, { recursive: true, force: true });
|
|
684
|
+
}
|
|
685
|
+
catch {
|
|
686
|
+
// Ignore cleanup errors
|
|
687
|
+
}
|
|
688
|
+
throw error;
|
|
689
|
+
}
|
|
690
|
+
return [];
|
|
691
|
+
}
|
|
692
|
+
/**
|
|
693
|
+
* Propagates skills for Gemini CLI by copying .ruler/skills to .gemini/skills.
|
|
694
|
+
* Uses atomic replace to ensure safe overwriting of existing skills.
|
|
695
|
+
* Returns dry-run steps if dryRun is true, otherwise returns empty array.
|
|
696
|
+
*/
|
|
697
|
+
async function propagateSkillsForGemini(projectRoot, options) {
|
|
698
|
+
const skillsDir = path.join(projectRoot, constants_1.RULER_SKILLS_PATH);
|
|
699
|
+
const geminiSkillsPath = path.join(projectRoot, constants_1.GEMINI_SKILLS_PATH);
|
|
700
|
+
const geminiDir = path.dirname(geminiSkillsPath);
|
|
701
|
+
// Check if source skills directory exists
|
|
702
|
+
try {
|
|
703
|
+
await fs.access(skillsDir);
|
|
704
|
+
}
|
|
705
|
+
catch {
|
|
706
|
+
// No skills directory - return empty
|
|
707
|
+
return [];
|
|
708
|
+
}
|
|
709
|
+
if (options.dryRun) {
|
|
710
|
+
return [`Copy skills from ${constants_1.RULER_SKILLS_PATH} to ${constants_1.GEMINI_SKILLS_PATH}`];
|
|
711
|
+
}
|
|
712
|
+
// Ensure .gemini directory exists
|
|
713
|
+
await fs.mkdir(geminiDir, { recursive: true });
|
|
714
|
+
// Use atomic replace: copy to temp, then rename
|
|
715
|
+
const tempDir = path.join(geminiDir, `skills.tmp-${Date.now()}`);
|
|
716
|
+
try {
|
|
717
|
+
// Copy to temp directory
|
|
718
|
+
await (0, SkillsUtils_1.copySkillsDirectory)(skillsDir, tempDir);
|
|
719
|
+
// Atomically replace the target
|
|
720
|
+
// First, remove existing target if it exists
|
|
721
|
+
try {
|
|
722
|
+
await fs.rm(geminiSkillsPath, { recursive: true, force: true });
|
|
723
|
+
}
|
|
724
|
+
catch {
|
|
725
|
+
// Target didn't exist, that's fine
|
|
726
|
+
}
|
|
727
|
+
// Rename temp to target
|
|
728
|
+
await fs.rename(tempDir, geminiSkillsPath);
|
|
729
|
+
}
|
|
730
|
+
catch (error) {
|
|
731
|
+
// Clean up temp directory on error
|
|
732
|
+
try {
|
|
733
|
+
await fs.rm(tempDir, { recursive: true, force: true });
|
|
734
|
+
}
|
|
735
|
+
catch {
|
|
736
|
+
// Ignore cleanup errors
|
|
737
|
+
}
|
|
738
|
+
throw error;
|
|
739
|
+
}
|
|
740
|
+
return [];
|
|
741
|
+
}
|
|
742
|
+
/**
|
|
743
|
+
* Propagates skills for Cursor by copying .ruler/skills to .cursor/skills.
|
|
744
|
+
* Uses atomic replace to ensure safe overwriting of existing skills.
|
|
745
|
+
* Returns dry-run steps if dryRun is true, otherwise returns empty array.
|
|
746
|
+
*/
|
|
747
|
+
async function propagateSkillsForCursor(projectRoot, options) {
|
|
748
|
+
const skillsDir = path.join(projectRoot, constants_1.RULER_SKILLS_PATH);
|
|
749
|
+
const cursorSkillsPath = path.join(projectRoot, constants_1.CURSOR_SKILLS_PATH);
|
|
750
|
+
const cursorDir = path.dirname(cursorSkillsPath);
|
|
751
|
+
// Check if source skills directory exists
|
|
752
|
+
try {
|
|
753
|
+
await fs.access(skillsDir);
|
|
754
|
+
}
|
|
755
|
+
catch {
|
|
756
|
+
// No skills directory - return empty
|
|
757
|
+
return [];
|
|
758
|
+
}
|
|
759
|
+
if (options.dryRun) {
|
|
760
|
+
return [`Copy skills from ${constants_1.RULER_SKILLS_PATH} to ${constants_1.CURSOR_SKILLS_PATH}`];
|
|
761
|
+
}
|
|
762
|
+
// Ensure .cursor directory exists
|
|
763
|
+
await fs.mkdir(cursorDir, { recursive: true });
|
|
764
|
+
// Use atomic replace: copy to temp, then rename
|
|
765
|
+
const tempDir = path.join(cursorDir, `skills.tmp-${Date.now()}`);
|
|
766
|
+
try {
|
|
767
|
+
// Copy to temp directory
|
|
768
|
+
await (0, SkillsUtils_1.copySkillsDirectory)(skillsDir, tempDir);
|
|
769
|
+
// Atomically replace the target
|
|
770
|
+
// First, remove existing target if it exists
|
|
771
|
+
try {
|
|
772
|
+
await fs.rm(cursorSkillsPath, { recursive: true, force: true });
|
|
773
|
+
}
|
|
774
|
+
catch {
|
|
775
|
+
// Target didn't exist, that's fine
|
|
776
|
+
}
|
|
777
|
+
// Rename temp to target
|
|
778
|
+
await fs.rename(tempDir, cursorSkillsPath);
|
|
779
|
+
}
|
|
780
|
+
catch (error) {
|
|
781
|
+
// Clean up temp directory on error
|
|
782
|
+
try {
|
|
783
|
+
await fs.rm(tempDir, { recursive: true, force: true });
|
|
784
|
+
}
|
|
785
|
+
catch {
|
|
786
|
+
// Ignore cleanup errors
|
|
787
|
+
}
|
|
788
|
+
throw error;
|
|
789
|
+
}
|
|
790
|
+
return [];
|
|
791
|
+
}
|
|
516
792
|
/**
|
|
517
793
|
* Propagates skills for MCP agents by copying .ruler/skills to .skillz.
|
|
518
794
|
* Uses atomic replace to ensure safe overwriting of existing skills.
|