@intellectronica/ruler 0.3.25 → 0.3.27
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 +49 -68
- package/dist/agents/AntigravityAgent.js +3 -0
- package/dist/agents/FactoryDroidAgent.js +29 -0
- package/dist/agents/KiloCodeAgent.js +4 -4
- package/dist/agents/index.js +2 -0
- package/dist/constants.js +3 -3
- package/dist/core/SkillsProcessor.js +102 -44
- package/dist/core/apply-engine.js +40 -77
- package/dist/lib.js +2 -2
- package/dist/paths/mcp.js +3 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -54,38 +54,38 @@ Ruler solves this by providing a **single source of truth** for all your AI agen
|
|
|
54
54
|
|
|
55
55
|
## Supported AI Agents
|
|
56
56
|
|
|
57
|
-
| Agent | Rules File(s) | MCP Configuration / Notes |
|
|
58
|
-
| ---------------- | ------------------------------------------------ | ------------------------------------------------ |
|
|
59
|
-
| AGENTS.md | `AGENTS.md` | (pseudo-agent ensuring root `AGENTS.md` exists) |
|
|
60
|
-
| GitHub Copilot | `AGENTS.md` | `.vscode/mcp.json` |
|
|
61
|
-
| Claude Code | `CLAUDE.md` | `.mcp.json` |
|
|
62
|
-
| OpenAI Codex CLI | `AGENTS.md` | `.codex/config.toml` |
|
|
63
|
-
| Pi Coding Agent | `AGENTS.md` | - |
|
|
64
|
-
| Jules | `AGENTS.md` | - |
|
|
65
|
-
| Cursor | `AGENTS.md` | `.cursor/mcp.json` |
|
|
66
|
-
| Windsurf | `AGENTS.md` | `.windsurf/mcp_config.json` |
|
|
67
|
-
| Cline | `.clinerules` | - |
|
|
68
|
-
| Crush | `CRUSH.md` | `.crush.json` |
|
|
69
|
-
| Amp | `AGENTS.md` | - |
|
|
70
|
-
| Antigravity | `.agent/rules/ruler.md` | - |
|
|
71
|
-
| Amazon Q CLI | `.amazonq/rules/ruler_q_rules.md` | `.amazonq/mcp.json` |
|
|
72
|
-
| Aider | `AGENTS.md`, `.aider.conf.yml` | `.mcp.json` |
|
|
73
|
-
| Firebase Studio | `.idx/airules.md` | `.idx/mcp.json` |
|
|
74
|
-
| Open Hands | `.openhands/microagents/repo.md` | `config.toml` |
|
|
75
|
-
| Gemini CLI | `AGENTS.md` | `.gemini/settings.json` |
|
|
76
|
-
| Junie | `.junie/guidelines.md` | - |
|
|
77
|
-
| AugmentCode | `.augment/rules/ruler_augment_instructions.md` | - |
|
|
78
|
-
| Kilo Code |
|
|
79
|
-
| OpenCode | `AGENTS.md` | `opencode.json` |
|
|
80
|
-
| Goose | `.goosehints` | - |
|
|
81
|
-
| Qwen Code | `AGENTS.md` | `.qwen/settings.json` |
|
|
82
|
-
| RooCode | `AGENTS.md` | `.roo/mcp.json` |
|
|
83
|
-
| Zed | `AGENTS.md` | `.zed/settings.json` (project root, never $HOME) |
|
|
84
|
-
| Trae AI | `.trae/rules/project_rules.md` | - |
|
|
85
|
-
| Warp | `WARP.md` | - |
|
|
86
|
-
| Kiro | `.kiro/steering/ruler_kiro_instructions.md` | `.kiro/settings/mcp.json` |
|
|
87
|
-
| Firebender | `firebender.json` | `firebender.json` (rules and MCP in same file) |
|
|
88
|
-
| Mistral Vibe | `AGENTS.md` | `.vibe/config.toml` |
|
|
57
|
+
| Agent | Rules File(s) | MCP Configuration / Notes | Skills Support / Location |
|
|
58
|
+
| ---------------- | ------------------------------------------------ | ------------------------------------------------ | ------------------------------------------------- |
|
|
59
|
+
| AGENTS.md | `AGENTS.md` | (pseudo-agent ensuring root `AGENTS.md` exists) | - |
|
|
60
|
+
| GitHub Copilot | `AGENTS.md` | `.vscode/mcp.json` | `.claude/skills/` |
|
|
61
|
+
| Claude Code | `CLAUDE.md` | `.mcp.json` | `.claude/skills/` |
|
|
62
|
+
| OpenAI Codex CLI | `AGENTS.md` | `.codex/config.toml` | `.codex/skills/` |
|
|
63
|
+
| Pi Coding Agent | `AGENTS.md` | - | `.pi/skills/` |
|
|
64
|
+
| Jules | `AGENTS.md` | - | - |
|
|
65
|
+
| Cursor | `AGENTS.md` | `.cursor/mcp.json` | `.cursor/skills/` |
|
|
66
|
+
| Windsurf | `AGENTS.md` | `.windsurf/mcp_config.json` | - |
|
|
67
|
+
| Cline | `.clinerules` | - | - |
|
|
68
|
+
| Crush | `CRUSH.md` | `.crush.json` | - |
|
|
69
|
+
| Amp | `AGENTS.md` | - | `.agents/skills/` |
|
|
70
|
+
| Antigravity | `.agent/rules/ruler.md` | - | `.agent/skills/` |
|
|
71
|
+
| Amazon Q CLI | `.amazonq/rules/ruler_q_rules.md` | `.amazonq/mcp.json` | - |
|
|
72
|
+
| Aider | `AGENTS.md`, `.aider.conf.yml` | `.mcp.json` | - |
|
|
73
|
+
| Firebase Studio | `.idx/airules.md` | `.idx/mcp.json` | - |
|
|
74
|
+
| Open Hands | `.openhands/microagents/repo.md` | `config.toml` | - |
|
|
75
|
+
| Gemini CLI | `AGENTS.md` | `.gemini/settings.json` | `.gemini/skills/` |
|
|
76
|
+
| Junie | `.junie/guidelines.md` | - | - |
|
|
77
|
+
| AugmentCode | `.augment/rules/ruler_augment_instructions.md` | - | - |
|
|
78
|
+
| Kilo Code | `AGENTS.md` | `.kilocode/mcp.json` | `.claude/skills/` |
|
|
79
|
+
| OpenCode | `AGENTS.md` | `opencode.json` | `.opencode/skill/` |
|
|
80
|
+
| Goose | `.goosehints` | - | `.agents/skills/` |
|
|
81
|
+
| Qwen Code | `AGENTS.md` | `.qwen/settings.json` | - |
|
|
82
|
+
| RooCode | `AGENTS.md` | `.roo/mcp.json` | `.roo/skills/` |
|
|
83
|
+
| Zed | `AGENTS.md` | `.zed/settings.json` (project root, never $HOME) | - |
|
|
84
|
+
| Trae AI | `.trae/rules/project_rules.md` | - | - |
|
|
85
|
+
| Warp | `WARP.md` | - | - |
|
|
86
|
+
| Kiro | `.kiro/steering/ruler_kiro_instructions.md` | `.kiro/settings/mcp.json` | - |
|
|
87
|
+
| Firebender | `firebender.json` | `firebender.json` (rules and MCP in same file) | - |
|
|
88
|
+
| Mistral Vibe | `AGENTS.md` | `.vibe/config.toml` | `.vibe/skills/` |
|
|
89
89
|
|
|
90
90
|
## Getting Started
|
|
91
91
|
|
|
@@ -318,15 +318,15 @@ ruler revert [options]
|
|
|
318
318
|
|
|
319
319
|
### Options
|
|
320
320
|
|
|
321
|
-
| Option | Description
|
|
322
|
-
| ------------------------------ |
|
|
323
|
-
| `--project-root <path>` | Path to your project's root (default: current directory)
|
|
321
|
+
| Option | Description |
|
|
322
|
+
| ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
323
|
+
| `--project-root <path>` | Path to your project's root (default: current directory) |
|
|
324
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
|
|
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 |
|
|
330
330
|
|
|
331
331
|
### Common Examples
|
|
332
332
|
|
|
@@ -449,7 +449,7 @@ enabled = false
|
|
|
449
449
|
|
|
450
450
|
[agents.kilocode]
|
|
451
451
|
enabled = true
|
|
452
|
-
output_path = ".
|
|
452
|
+
output_path = "AGENTS.md"
|
|
453
453
|
|
|
454
454
|
[agents.warp]
|
|
455
455
|
enabled = true
|
|
@@ -559,7 +559,7 @@ export CODEX_HOME="$(pwd)/.codex"
|
|
|
559
559
|
|
|
560
560
|
## Skills Support (Experimental)
|
|
561
561
|
|
|
562
|
-
**⚠️ Experimental Feature**: Skills support is currently experimental
|
|
562
|
+
**⚠️ Experimental Feature**: Skills support is currently experimental. Skills are only propagated to agents with native skills support; other agents are skipped with a warning.
|
|
563
563
|
|
|
564
564
|
Ruler can manage and propagate skills to supported AI agents. Skills are stored in `.ruler/skills/` and are automatically distributed to compatible agents when you run `ruler apply`.
|
|
565
565
|
|
|
@@ -576,11 +576,11 @@ Skills are specialized knowledge packages that extend AI agent capabilities with
|
|
|
576
576
|
- **Pi Coding Agent**: `.pi/skills/`
|
|
577
577
|
- **Goose**: `.agents/skills/`
|
|
578
578
|
- **Amp**: `.agents/skills/` (shared with Goose)
|
|
579
|
+
- **Antigravity**: `.agent/skills/`
|
|
579
580
|
- **Mistral Vibe**: `.vibe/skills/`
|
|
580
581
|
- **Roo Code**: `.roo/skills/`
|
|
581
582
|
- **Gemini CLI**: `.gemini/skills/`
|
|
582
583
|
- **Cursor**: `.cursor/skills/`
|
|
583
|
-
- **Other MCP-compatible agents**: Skills are copied to `.skillz/` and a Skillz MCP server is automatically configured via `uvx`
|
|
584
584
|
|
|
585
585
|
### Skills Directory Structure
|
|
586
586
|
|
|
@@ -627,23 +627,9 @@ ruler apply --no-skills
|
|
|
627
627
|
enabled = true # or false to disable
|
|
628
628
|
```
|
|
629
629
|
|
|
630
|
-
###
|
|
631
|
-
|
|
632
|
-
For agents that support MCP but don't have native skills support, Ruler automatically:
|
|
633
|
-
|
|
634
|
-
1. Copies skills to `.skillz/` directory
|
|
635
|
-
2. Configures a Skillz MCP server in the agent's configuration
|
|
636
|
-
3. Uses `uvx` to launch the server with the project-relative path to `.skillz`
|
|
637
|
-
|
|
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.
|
|
630
|
+
### Non-native Agents
|
|
639
631
|
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
```toml
|
|
643
|
-
[mcp_servers.skillz]
|
|
644
|
-
command = "uvx"
|
|
645
|
-
args = ["skillz@latest", ".skillz"]
|
|
646
|
-
```
|
|
632
|
+
If you run Ruler for agents that do not support native skills, Ruler logs a warning and skips skills propagation for those agents.
|
|
647
633
|
|
|
648
634
|
### `.gitignore` Integration
|
|
649
635
|
|
|
@@ -654,22 +640,17 @@ When skills support is enabled and gitignore integration is active, Ruler automa
|
|
|
654
640
|
- `.opencode/skill/` (for OpenCode)
|
|
655
641
|
- `.pi/skills/` (for Pi Coding Agent)
|
|
656
642
|
- `.agents/skills/` (for Goose and Amp)
|
|
643
|
+
- `.agent/skills/` (for Antigravity)
|
|
657
644
|
- `.vibe/skills/` (for Mistral Vibe)
|
|
658
645
|
- `.roo/skills/` (for Roo Code)
|
|
659
646
|
- `.gemini/skills/` (for Gemini CLI)
|
|
660
647
|
- `.cursor/skills/` (for Cursor)
|
|
661
|
-
- `.skillz/` (for other MCP-based agents)
|
|
662
648
|
|
|
663
649
|
to your `.gitignore` file within the managed Ruler block.
|
|
664
650
|
|
|
665
651
|
### Requirements
|
|
666
652
|
|
|
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
|
|
668
|
-
- **For other MCP agents**: `uv` must be installed and available in your PATH
|
|
669
|
-
```bash
|
|
670
|
-
# Install uv if needed
|
|
671
|
-
curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
672
|
-
```
|
|
653
|
+
- **For agents with native skills support** (Claude Code, GitHub Copilot, Kilo Code, OpenAI Codex CLI, OpenCode, Pi Coding Agent, Goose, Amp, Antigravity, Mistral Vibe, Roo Code, Gemini CLI, Cursor): No additional requirements.
|
|
673
654
|
|
|
674
655
|
### Validation
|
|
675
656
|
|
|
@@ -688,7 +669,7 @@ Test skills propagation without making changes:
|
|
|
688
669
|
ruler apply --dry-run
|
|
689
670
|
```
|
|
690
671
|
|
|
691
|
-
This shows which skills would be copied
|
|
672
|
+
This shows which skills would be copied.
|
|
692
673
|
|
|
693
674
|
### Example Workflow
|
|
694
675
|
|
|
@@ -717,11 +698,11 @@ ruler apply
|
|
|
717
698
|
# - OpenCode: .opencode/skill/my-skill/
|
|
718
699
|
# - Pi Coding Agent: .pi/skills/my-skill/
|
|
719
700
|
# - Goose & Amp: .agents/skills/my-skill/
|
|
701
|
+
# - Antigravity: .agent/skills/my-skill/
|
|
720
702
|
# - Mistral Vibe: .vibe/skills/my-skill/
|
|
721
703
|
# - Roo Code: .roo/skills/my-skill/
|
|
722
704
|
# - Gemini CLI: .gemini/skills/my-skill/
|
|
723
705
|
# - Cursor: .cursor/skills/my-skill/
|
|
724
|
-
# - Other MCP agents: .skillz/my-skill/ + Skillz MCP server configured
|
|
725
706
|
```
|
|
726
707
|
|
|
727
708
|
## `.gitignore` Integration
|
|
@@ -49,5 +49,8 @@ class AntigravityAgent extends AbstractAgent_1.AbstractAgent {
|
|
|
49
49
|
getDefaultOutputPath(projectRoot) {
|
|
50
50
|
return path.join(projectRoot, '.agent', 'rules', 'ruler.md');
|
|
51
51
|
}
|
|
52
|
+
supportsNativeSkills() {
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
52
55
|
}
|
|
53
56
|
exports.AntigravityAgent = AntigravityAgent;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FactoryDroidAgent = void 0;
|
|
4
|
+
const AgentsMdAgent_1 = require("./AgentsMdAgent");
|
|
5
|
+
/**
|
|
6
|
+
* Factory Droid agent adapter.
|
|
7
|
+
* Uses the root-level AGENTS.md for instructions.
|
|
8
|
+
*/
|
|
9
|
+
class FactoryDroidAgent extends AgentsMdAgent_1.AgentsMdAgent {
|
|
10
|
+
getIdentifier() {
|
|
11
|
+
return 'factory';
|
|
12
|
+
}
|
|
13
|
+
getName() {
|
|
14
|
+
return 'Factory Droid';
|
|
15
|
+
}
|
|
16
|
+
getMcpServerKey() {
|
|
17
|
+
return 'mcpServers';
|
|
18
|
+
}
|
|
19
|
+
supportsMcpStdio() {
|
|
20
|
+
return true;
|
|
21
|
+
}
|
|
22
|
+
supportsMcpRemote() {
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
supportsNativeSkills() {
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
exports.FactoryDroidAgent = FactoryDroidAgent;
|
|
@@ -35,12 +35,12 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.KiloCodeAgent = void 0;
|
|
37
37
|
const path = __importStar(require("path"));
|
|
38
|
-
const
|
|
38
|
+
const AgentsMdAgent_1 = require("./AgentsMdAgent");
|
|
39
39
|
/**
|
|
40
40
|
* Kilo Code agent adapter.
|
|
41
|
-
*
|
|
41
|
+
* Uses AGENTS.md for instructions and .kilocode/mcp.json for MCP configuration.
|
|
42
42
|
*/
|
|
43
|
-
class KiloCodeAgent extends
|
|
43
|
+
class KiloCodeAgent extends AgentsMdAgent_1.AgentsMdAgent {
|
|
44
44
|
getIdentifier() {
|
|
45
45
|
return 'kilocode';
|
|
46
46
|
}
|
|
@@ -48,7 +48,7 @@ class KiloCodeAgent extends AbstractAgent_1.AbstractAgent {
|
|
|
48
48
|
return 'Kilo Code';
|
|
49
49
|
}
|
|
50
50
|
getDefaultOutputPath(projectRoot) {
|
|
51
|
-
return path.join(projectRoot, '.
|
|
51
|
+
return path.join(projectRoot, 'AGENTS.md');
|
|
52
52
|
}
|
|
53
53
|
getMcpServerKey() {
|
|
54
54
|
return 'mcpServers';
|
package/dist/agents/index.js
CHANGED
|
@@ -31,6 +31,7 @@ const RooCodeAgent_1 = require("./RooCodeAgent");
|
|
|
31
31
|
const TraeAgent_1 = require("./TraeAgent");
|
|
32
32
|
const AmazonQCliAgent_1 = require("./AmazonQCliAgent");
|
|
33
33
|
const FirebenderAgent_1 = require("./FirebenderAgent");
|
|
34
|
+
const FactoryDroidAgent_1 = require("./FactoryDroidAgent");
|
|
34
35
|
const AntigravityAgent_1 = require("./AntigravityAgent");
|
|
35
36
|
const MistralVibeAgent_1 = require("./MistralVibeAgent");
|
|
36
37
|
const PiAgent_1 = require("./PiAgent");
|
|
@@ -62,6 +63,7 @@ exports.allAgents = [
|
|
|
62
63
|
new TraeAgent_1.TraeAgent(),
|
|
63
64
|
new AmazonQCliAgent_1.AmazonQCliAgent(),
|
|
64
65
|
new FirebenderAgent_1.FirebenderAgent(),
|
|
66
|
+
new FactoryDroidAgent_1.FactoryDroidAgent(),
|
|
65
67
|
new AntigravityAgent_1.AntigravityAgent(),
|
|
66
68
|
new MistralVibeAgent_1.MistralVibeAgent(),
|
|
67
69
|
new PiAgent_1.PiAgent(),
|
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.SKILL_MD_FILENAME = exports.ANTIGRAVITY_SKILLS_PATH = exports.FACTORY_SKILLS_PATH = 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;
|
|
@@ -61,6 +61,6 @@ exports.VIBE_SKILLS_PATH = '.vibe/skills';
|
|
|
61
61
|
exports.ROO_SKILLS_PATH = '.roo/skills';
|
|
62
62
|
exports.GEMINI_SKILLS_PATH = '.gemini/skills';
|
|
63
63
|
exports.CURSOR_SKILLS_PATH = '.cursor/skills';
|
|
64
|
-
exports.
|
|
64
|
+
exports.FACTORY_SKILLS_PATH = '.factory/skills';
|
|
65
|
+
exports.ANTIGRAVITY_SKILLS_PATH = '.agent/skills';
|
|
65
66
|
exports.SKILL_MD_FILENAME = 'SKILL.md';
|
|
66
|
-
exports.SKILLZ_MCP_SERVER_NAME = 'skillz';
|
|
@@ -45,8 +45,8 @@ exports.propagateSkillsForVibe = propagateSkillsForVibe;
|
|
|
45
45
|
exports.propagateSkillsForRoo = propagateSkillsForRoo;
|
|
46
46
|
exports.propagateSkillsForGemini = propagateSkillsForGemini;
|
|
47
47
|
exports.propagateSkillsForCursor = propagateSkillsForCursor;
|
|
48
|
-
exports.
|
|
49
|
-
exports.
|
|
48
|
+
exports.propagateSkillsForFactory = propagateSkillsForFactory;
|
|
49
|
+
exports.propagateSkillsForAntigravity = propagateSkillsForAntigravity;
|
|
50
50
|
const path = __importStar(require("path"));
|
|
51
51
|
const fs = __importStar(require("fs/promises"));
|
|
52
52
|
const constants_1 = require("../constants");
|
|
@@ -82,7 +82,7 @@ async function getSkillsGitignorePaths(projectRoot) {
|
|
|
82
82
|
return [];
|
|
83
83
|
}
|
|
84
84
|
// Import here to avoid circular dependency
|
|
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,
|
|
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, FACTORY_SKILLS_PATH, ANTIGRAVITY_SKILLS_PATH, } = await Promise.resolve().then(() => __importStar(require('../constants')));
|
|
86
86
|
return [
|
|
87
87
|
path.join(projectRoot, CLAUDE_SKILLS_PATH),
|
|
88
88
|
path.join(projectRoot, CODEX_SKILLS_PATH),
|
|
@@ -93,7 +93,8 @@ async function getSkillsGitignorePaths(projectRoot) {
|
|
|
93
93
|
path.join(projectRoot, ROO_SKILLS_PATH),
|
|
94
94
|
path.join(projectRoot, GEMINI_SKILLS_PATH),
|
|
95
95
|
path.join(projectRoot, CURSOR_SKILLS_PATH),
|
|
96
|
-
path.join(projectRoot,
|
|
96
|
+
path.join(projectRoot, FACTORY_SKILLS_PATH),
|
|
97
|
+
path.join(projectRoot, ANTIGRAVITY_SKILLS_PATH),
|
|
97
98
|
];
|
|
98
99
|
}
|
|
99
100
|
/**
|
|
@@ -104,16 +105,15 @@ async function getSkillsGitignorePaths(projectRoot) {
|
|
|
104
105
|
*/
|
|
105
106
|
let hasWarnedExperimental = false;
|
|
106
107
|
/**
|
|
107
|
-
* Warns once per process about experimental skills
|
|
108
|
+
* Warns once per process about experimental skills support.
|
|
108
109
|
* Uses module-level state to prevent duplicate warnings within the same process.
|
|
109
110
|
*/
|
|
110
|
-
function
|
|
111
|
+
function warnOnceExperimental(verbose, dryRun) {
|
|
111
112
|
if (hasWarnedExperimental) {
|
|
112
113
|
return;
|
|
113
114
|
}
|
|
114
115
|
hasWarnedExperimental = true;
|
|
115
116
|
(0, constants_1.logWarn)('Skills support is experimental and behavior may change in future releases.', dryRun);
|
|
116
|
-
(0, constants_1.logWarn)('Skills MCP server (Skillz) requires uv. Install: https://github.com/astral-sh/uv', dryRun);
|
|
117
117
|
}
|
|
118
118
|
/**
|
|
119
119
|
* Cleans up skills directories when skills are disabled.
|
|
@@ -129,7 +129,8 @@ async function cleanupSkillsDirectories(projectRoot, dryRun, verbose) {
|
|
|
129
129
|
const rooSkillsPath = path.join(projectRoot, constants_1.ROO_SKILLS_PATH);
|
|
130
130
|
const geminiSkillsPath = path.join(projectRoot, constants_1.GEMINI_SKILLS_PATH);
|
|
131
131
|
const cursorSkillsPath = path.join(projectRoot, constants_1.CURSOR_SKILLS_PATH);
|
|
132
|
-
const
|
|
132
|
+
const factorySkillsPath = path.join(projectRoot, constants_1.FACTORY_SKILLS_PATH);
|
|
133
|
+
const antigravitySkillsPath = path.join(projectRoot, constants_1.ANTIGRAVITY_SKILLS_PATH);
|
|
133
134
|
// Clean up .claude/skills
|
|
134
135
|
try {
|
|
135
136
|
await fs.access(claudeSkillsPath);
|
|
@@ -256,15 +257,29 @@ async function cleanupSkillsDirectories(projectRoot, dryRun, verbose) {
|
|
|
256
257
|
catch {
|
|
257
258
|
// Directory doesn't exist, nothing to clean
|
|
258
259
|
}
|
|
259
|
-
// Clean up .
|
|
260
|
+
// Clean up .factory/skills
|
|
260
261
|
try {
|
|
261
|
-
await fs.access(
|
|
262
|
+
await fs.access(factorySkillsPath);
|
|
262
263
|
if (dryRun) {
|
|
263
|
-
(0, constants_1.logVerboseInfo)(`DRY RUN: Would remove ${constants_1.
|
|
264
|
+
(0, constants_1.logVerboseInfo)(`DRY RUN: Would remove ${constants_1.FACTORY_SKILLS_PATH}`, verbose, dryRun);
|
|
264
265
|
}
|
|
265
266
|
else {
|
|
266
|
-
await fs.rm(
|
|
267
|
-
(0, constants_1.logVerboseInfo)(`Removed ${constants_1.
|
|
267
|
+
await fs.rm(factorySkillsPath, { recursive: true, force: true });
|
|
268
|
+
(0, constants_1.logVerboseInfo)(`Removed ${constants_1.FACTORY_SKILLS_PATH} (skills disabled)`, verbose, dryRun);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
catch {
|
|
272
|
+
// Directory doesn't exist, nothing to clean
|
|
273
|
+
}
|
|
274
|
+
// Clean up .agent/skills
|
|
275
|
+
try {
|
|
276
|
+
await fs.access(antigravitySkillsPath);
|
|
277
|
+
if (dryRun) {
|
|
278
|
+
(0, constants_1.logVerboseInfo)(`DRY RUN: Would remove ${constants_1.ANTIGRAVITY_SKILLS_PATH}`, verbose, dryRun);
|
|
279
|
+
}
|
|
280
|
+
else {
|
|
281
|
+
await fs.rm(antigravitySkillsPath, { recursive: true, force: true });
|
|
282
|
+
(0, constants_1.logVerboseInfo)(`Removed ${constants_1.ANTIGRAVITY_SKILLS_PATH} (skills disabled)`, verbose, dryRun);
|
|
268
283
|
}
|
|
269
284
|
}
|
|
270
285
|
catch {
|
|
@@ -301,17 +316,20 @@ async function propagateSkills(projectRoot, agents, skillsEnabled, verbose, dryR
|
|
|
301
316
|
return;
|
|
302
317
|
}
|
|
303
318
|
(0, constants_1.logVerboseInfo)(`Discovered ${skills.length} skill(s)`, verbose, dryRun);
|
|
304
|
-
// Check if any agents need skills
|
|
305
319
|
const hasNativeSkillsAgent = agents.some((a) => a.supportsNativeSkills?.());
|
|
306
|
-
const
|
|
307
|
-
if (
|
|
308
|
-
|
|
320
|
+
const nonNativeAgents = agents.filter((agent) => !agent.supportsNativeSkills?.());
|
|
321
|
+
if (nonNativeAgents.length > 0) {
|
|
322
|
+
const agentList = nonNativeAgents
|
|
323
|
+
.map((agent) => agent.getName())
|
|
324
|
+
.join(', ');
|
|
325
|
+
(0, constants_1.logWarn)(`Skills are configured, but the following agents do not support native skills and will be skipped: ${agentList}`, dryRun);
|
|
326
|
+
}
|
|
327
|
+
if (!hasNativeSkillsAgent) {
|
|
328
|
+
(0, constants_1.logVerboseInfo)('No agents support native skills, skipping skills propagation', verbose, dryRun);
|
|
309
329
|
return;
|
|
310
330
|
}
|
|
311
331
|
// Warn about experimental features
|
|
312
|
-
|
|
313
|
-
warnOnceExperimentalAndUv(verbose, dryRun);
|
|
314
|
-
}
|
|
332
|
+
warnOnceExperimental(verbose, dryRun);
|
|
315
333
|
// Copy to Claude skills directory if needed
|
|
316
334
|
if (hasNativeSkillsAgent) {
|
|
317
335
|
(0, constants_1.logVerboseInfo)(`Copying skills to ${constants_1.CLAUDE_SKILLS_PATH} for Claude Code, GitHub Copilot, and Kilo Code`, verbose, dryRun);
|
|
@@ -332,12 +350,12 @@ async function propagateSkills(projectRoot, agents, skillsEnabled, verbose, dryR
|
|
|
332
350
|
await propagateSkillsForGemini(projectRoot, { dryRun });
|
|
333
351
|
(0, constants_1.logVerboseInfo)(`Copying skills to ${constants_1.CURSOR_SKILLS_PATH} for Cursor`, verbose, dryRun);
|
|
334
352
|
await propagateSkillsForCursor(projectRoot, { dryRun });
|
|
353
|
+
(0, constants_1.logVerboseInfo)(`Copying skills to ${constants_1.FACTORY_SKILLS_PATH} for Factory Droid`, verbose, dryRun);
|
|
354
|
+
await propagateSkillsForFactory(projectRoot, { dryRun });
|
|
355
|
+
(0, constants_1.logVerboseInfo)(`Copying skills to ${constants_1.ANTIGRAVITY_SKILLS_PATH} for Antigravity`, verbose, dryRun);
|
|
356
|
+
await propagateSkillsForAntigravity(projectRoot, { dryRun });
|
|
335
357
|
}
|
|
336
|
-
//
|
|
337
|
-
if (hasMcpAgent) {
|
|
338
|
-
(0, constants_1.logVerboseInfo)(`Copying skills to ${constants_1.SKILLZ_DIR} for MCP agents`, verbose, dryRun);
|
|
339
|
-
await propagateSkillsForSkillz(projectRoot, { dryRun });
|
|
340
|
-
}
|
|
358
|
+
// No MCP-based propagation; only native skills are supported.
|
|
341
359
|
}
|
|
342
360
|
/**
|
|
343
361
|
* Propagates skills for Claude Code by copying .ruler/skills to .claude/skills.
|
|
@@ -790,13 +808,14 @@ async function propagateSkillsForCursor(projectRoot, options) {
|
|
|
790
808
|
return [];
|
|
791
809
|
}
|
|
792
810
|
/**
|
|
793
|
-
* Propagates skills for
|
|
811
|
+
* Propagates skills for Factory Droid by copying .ruler/skills to .factory/skills.
|
|
794
812
|
* Uses atomic replace to ensure safe overwriting of existing skills.
|
|
795
813
|
* Returns dry-run steps if dryRun is true, otherwise returns empty array.
|
|
796
814
|
*/
|
|
797
|
-
async function
|
|
815
|
+
async function propagateSkillsForFactory(projectRoot, options) {
|
|
798
816
|
const skillsDir = path.join(projectRoot, constants_1.RULER_SKILLS_PATH);
|
|
799
|
-
const
|
|
817
|
+
const factorySkillsPath = path.join(projectRoot, constants_1.FACTORY_SKILLS_PATH);
|
|
818
|
+
const factoryDir = path.dirname(factorySkillsPath);
|
|
800
819
|
// Check if source skills directory exists
|
|
801
820
|
try {
|
|
802
821
|
await fs.access(skillsDir);
|
|
@@ -806,26 +825,25 @@ async function propagateSkillsForSkillz(projectRoot, options) {
|
|
|
806
825
|
return [];
|
|
807
826
|
}
|
|
808
827
|
if (options.dryRun) {
|
|
809
|
-
return [
|
|
810
|
-
`Copy skills from ${constants_1.RULER_SKILLS_PATH} to ${constants_1.SKILLZ_DIR}`,
|
|
811
|
-
`Configure Skillz MCP server with path to ${constants_1.SKILLZ_DIR}`,
|
|
812
|
-
];
|
|
828
|
+
return [`Copy skills from ${constants_1.RULER_SKILLS_PATH} to ${constants_1.FACTORY_SKILLS_PATH}`];
|
|
813
829
|
}
|
|
830
|
+
// Ensure .factory directory exists
|
|
831
|
+
await fs.mkdir(factoryDir, { recursive: true });
|
|
814
832
|
// Use atomic replace: copy to temp, then rename
|
|
815
|
-
const tempDir = path.join(
|
|
833
|
+
const tempDir = path.join(factoryDir, `skills.tmp-${Date.now()}`);
|
|
816
834
|
try {
|
|
817
835
|
// Copy to temp directory
|
|
818
836
|
await (0, SkillsUtils_1.copySkillsDirectory)(skillsDir, tempDir);
|
|
819
837
|
// Atomically replace the target
|
|
820
838
|
// First, remove existing target if it exists
|
|
821
839
|
try {
|
|
822
|
-
await fs.rm(
|
|
840
|
+
await fs.rm(factorySkillsPath, { recursive: true, force: true });
|
|
823
841
|
}
|
|
824
842
|
catch {
|
|
825
843
|
// Target didn't exist, that's fine
|
|
826
844
|
}
|
|
827
845
|
// Rename temp to target
|
|
828
|
-
await fs.rename(tempDir,
|
|
846
|
+
await fs.rename(tempDir, factorySkillsPath);
|
|
829
847
|
}
|
|
830
848
|
catch (error) {
|
|
831
849
|
// Clean up temp directory on error
|
|
@@ -840,14 +858,54 @@ async function propagateSkillsForSkillz(projectRoot, options) {
|
|
|
840
858
|
return [];
|
|
841
859
|
}
|
|
842
860
|
/**
|
|
843
|
-
*
|
|
861
|
+
* Propagates skills for Antigravity by copying .ruler/skills to .agent/skills.
|
|
862
|
+
* Uses atomic replace to ensure safe overwriting of existing skills.
|
|
863
|
+
* Returns dry-run steps if dryRun is true, otherwise returns empty array.
|
|
844
864
|
*/
|
|
845
|
-
function
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
}
|
|
865
|
+
async function propagateSkillsForAntigravity(projectRoot, options) {
|
|
866
|
+
const skillsDir = path.join(projectRoot, constants_1.RULER_SKILLS_PATH);
|
|
867
|
+
const antigravitySkillsPath = path.join(projectRoot, constants_1.ANTIGRAVITY_SKILLS_PATH);
|
|
868
|
+
const antigravityDir = path.dirname(antigravitySkillsPath);
|
|
869
|
+
// Check if source skills directory exists
|
|
870
|
+
try {
|
|
871
|
+
await fs.access(skillsDir);
|
|
872
|
+
}
|
|
873
|
+
catch {
|
|
874
|
+
// No skills directory - return empty
|
|
875
|
+
return [];
|
|
876
|
+
}
|
|
877
|
+
if (options.dryRun) {
|
|
878
|
+
return [
|
|
879
|
+
`Copy skills from ${constants_1.RULER_SKILLS_PATH} to ${constants_1.ANTIGRAVITY_SKILLS_PATH}`,
|
|
880
|
+
];
|
|
881
|
+
}
|
|
882
|
+
// Ensure .agent directory exists
|
|
883
|
+
await fs.mkdir(antigravityDir, { recursive: true });
|
|
884
|
+
// Use atomic replace: copy to temp, then rename
|
|
885
|
+
const tempDir = path.join(antigravityDir, `skills.tmp-${Date.now()}`);
|
|
886
|
+
try {
|
|
887
|
+
// Copy to temp directory
|
|
888
|
+
await (0, SkillsUtils_1.copySkillsDirectory)(skillsDir, tempDir);
|
|
889
|
+
// Atomically replace the target
|
|
890
|
+
// First, remove existing target if it exists
|
|
891
|
+
try {
|
|
892
|
+
await fs.rm(antigravitySkillsPath, { recursive: true, force: true });
|
|
893
|
+
}
|
|
894
|
+
catch {
|
|
895
|
+
// Target didn't exist, that's fine
|
|
896
|
+
}
|
|
897
|
+
// Rename temp to target
|
|
898
|
+
await fs.rename(tempDir, antigravitySkillsPath);
|
|
899
|
+
}
|
|
900
|
+
catch (error) {
|
|
901
|
+
// Clean up temp directory on error
|
|
902
|
+
try {
|
|
903
|
+
await fs.rm(tempDir, { recursive: true, force: true });
|
|
904
|
+
}
|
|
905
|
+
catch {
|
|
906
|
+
// Ignore cleanup errors
|
|
907
|
+
}
|
|
908
|
+
throw error;
|
|
909
|
+
}
|
|
910
|
+
return [];
|
|
853
911
|
}
|
|
@@ -233,12 +233,12 @@ async function loadSingleConfiguration(projectRoot, configPath, localOnly) {
|
|
|
233
233
|
* @param cliMcpStrategy MCP strategy from CLI
|
|
234
234
|
* @returns Promise resolving to array of generated file paths
|
|
235
235
|
*/
|
|
236
|
-
async function processHierarchicalConfigurations(agents, configurations, verbose, dryRun, cliMcpEnabled, cliMcpStrategy, backup = true
|
|
236
|
+
async function processHierarchicalConfigurations(agents, configurations, verbose, dryRun, cliMcpEnabled, cliMcpStrategy, backup = true) {
|
|
237
237
|
const allGeneratedPaths = [];
|
|
238
238
|
for (const config of configurations) {
|
|
239
239
|
(0, constants_1.logVerboseInfo)(`Processing .ruler directory: ${config.rulerDir}`, verbose, dryRun);
|
|
240
240
|
const rulerRoot = path.dirname(config.rulerDir);
|
|
241
|
-
const paths = await applyConfigurationsToAgents(agents, config.concatenatedRules, config.rulerMcpJson, config.config, rulerRoot, verbose, dryRun, cliMcpEnabled, cliMcpStrategy, backup
|
|
241
|
+
const paths = await applyConfigurationsToAgents(agents, config.concatenatedRules, config.rulerMcpJson, config.config, rulerRoot, verbose, dryRun, cliMcpEnabled, cliMcpStrategy, backup);
|
|
242
242
|
const normalizedPaths = paths.map((p) => path.isAbsolute(p) ? p : path.join(rulerRoot, p));
|
|
243
243
|
allGeneratedPaths.push(...normalizedPaths);
|
|
244
244
|
}
|
|
@@ -256,42 +256,8 @@ async function processHierarchicalConfigurations(agents, configurations, verbose
|
|
|
256
256
|
* @param cliMcpStrategy MCP strategy from CLI
|
|
257
257
|
* @returns Promise resolving to array of generated file paths
|
|
258
258
|
*/
|
|
259
|
-
async function processSingleConfiguration(agents, configuration, projectRoot, verbose, dryRun, cliMcpEnabled, cliMcpStrategy, backup = true
|
|
260
|
-
return await applyConfigurationsToAgents(agents, configuration.concatenatedRules, configuration.rulerMcpJson, configuration.config, projectRoot, verbose, dryRun, cliMcpEnabled, cliMcpStrategy, backup
|
|
261
|
-
}
|
|
262
|
-
/**
|
|
263
|
-
* Adds Skillz MCP server to rulerMcpJson if skills exist and this agent needs it.
|
|
264
|
-
* Returns augmented MCP config or original if no changes needed.
|
|
265
|
-
*/
|
|
266
|
-
async function addSkillzMcpServerIfNeeded(rulerMcpJson, projectRoot, agent, verbose) {
|
|
267
|
-
// Check if this agent supports MCP stdio but not native skills
|
|
268
|
-
if (!agent.supportsMcpStdio?.() || agent.supportsNativeSkills?.()) {
|
|
269
|
-
return rulerMcpJson;
|
|
270
|
-
}
|
|
271
|
-
// Check if .skillz directory exists
|
|
272
|
-
try {
|
|
273
|
-
const { SKILLZ_DIR } = await Promise.resolve().then(() => __importStar(require('../constants')));
|
|
274
|
-
const skillzPath = path.join(projectRoot, SKILLZ_DIR);
|
|
275
|
-
await fs_1.promises.access(skillzPath);
|
|
276
|
-
// Skills exist, add Skillz MCP server
|
|
277
|
-
const { buildSkillzMcpConfig } = await Promise.resolve().then(() => __importStar(require('./SkillsProcessor')));
|
|
278
|
-
const skillzMcp = buildSkillzMcpConfig(projectRoot);
|
|
279
|
-
// Initialize empty config if null
|
|
280
|
-
const baseConfig = rulerMcpJson || { mcpServers: {} };
|
|
281
|
-
const mcpServers = baseConfig.mcpServers || {};
|
|
282
|
-
(0, constants_1.logVerbose)(`Adding Skillz MCP server to configuration for ${agent.getName()}`, verbose);
|
|
283
|
-
return {
|
|
284
|
-
...baseConfig,
|
|
285
|
-
mcpServers: {
|
|
286
|
-
...mcpServers,
|
|
287
|
-
...skillzMcp,
|
|
288
|
-
},
|
|
289
|
-
};
|
|
290
|
-
}
|
|
291
|
-
catch {
|
|
292
|
-
// No .skillz directory, return original config
|
|
293
|
-
return rulerMcpJson;
|
|
294
|
-
}
|
|
259
|
+
async function processSingleConfiguration(agents, configuration, projectRoot, verbose, dryRun, cliMcpEnabled, cliMcpStrategy, backup = true) {
|
|
260
|
+
return await applyConfigurationsToAgents(agents, configuration.concatenatedRules, configuration.rulerMcpJson, configuration.config, projectRoot, verbose, dryRun, cliMcpEnabled, cliMcpStrategy, backup);
|
|
295
261
|
}
|
|
296
262
|
/**
|
|
297
263
|
* Applies configurations to the selected agents (internal function).
|
|
@@ -304,16 +270,14 @@ async function addSkillzMcpServerIfNeeded(rulerMcpJson, projectRoot, agent, verb
|
|
|
304
270
|
* @param dryRun Whether to perform a dry run
|
|
305
271
|
* @returns Promise resolving to array of generated file paths
|
|
306
272
|
*/
|
|
307
|
-
async function applyConfigurationsToAgents(agents, concatenatedRules, rulerMcpJson, config, projectRoot, verbose, dryRun, cliMcpEnabled = true, cliMcpStrategy, backup = true
|
|
273
|
+
async function applyConfigurationsToAgents(agents, concatenatedRules, rulerMcpJson, config, projectRoot, verbose, dryRun, cliMcpEnabled = true, cliMcpStrategy, backup = true) {
|
|
308
274
|
const generatedPaths = [];
|
|
309
275
|
let agentsMdWritten = false;
|
|
310
276
|
for (const agent of agents) {
|
|
311
277
|
(0, constants_1.logInfo)(`Applying rules for ${agent.getName()}...`, dryRun);
|
|
312
278
|
(0, constants_1.logVerbose)(`Processing agent: ${agent.getName()}`, verbose);
|
|
313
279
|
const agentConfig = config.agentConfigs[agent.getIdentifier()];
|
|
314
|
-
const agentRulerMcpJson =
|
|
315
|
-
? await addSkillzMcpServerIfNeeded(rulerMcpJson, projectRoot, agent, verbose)
|
|
316
|
-
: rulerMcpJson;
|
|
280
|
+
const agentRulerMcpJson = rulerMcpJson;
|
|
317
281
|
// Collect output paths for .gitignore
|
|
318
282
|
const outputPaths = (0, agent_utils_1.getAgentOutputPaths)(agent, projectRoot, agentConfig);
|
|
319
283
|
(0, constants_1.logVerbose)(`Agent ${agent.getName()} output paths: ${outputPaths.join(', ')}`, verbose);
|
|
@@ -357,11 +321,11 @@ async function applyConfigurationsToAgents(agents, concatenatedRules, rulerMcpJs
|
|
|
357
321
|
}
|
|
358
322
|
}
|
|
359
323
|
// Handle MCP configuration
|
|
360
|
-
await handleMcpConfiguration(agent, agentConfig, config, agentRulerMcpJson, projectRoot, generatedPaths, verbose, dryRun, cliMcpEnabled, cliMcpStrategy, backup
|
|
324
|
+
await handleMcpConfiguration(agent, agentConfig, config, agentRulerMcpJson, projectRoot, generatedPaths, verbose, dryRun, cliMcpEnabled, cliMcpStrategy, backup);
|
|
361
325
|
}
|
|
362
326
|
return generatedPaths;
|
|
363
327
|
}
|
|
364
|
-
async function handleMcpConfiguration(agent, agentConfig, config, rulerMcpJson, projectRoot, generatedPaths, verbose, dryRun, cliMcpEnabled = true, cliMcpStrategy, backup = true
|
|
328
|
+
async function handleMcpConfiguration(agent, agentConfig, config, rulerMcpJson, projectRoot, generatedPaths, verbose, dryRun, cliMcpEnabled = true, cliMcpStrategy, backup = true) {
|
|
365
329
|
if (!(0, capabilities_1.agentSupportsMcp)(agent)) {
|
|
366
330
|
(0, constants_1.logVerbose)(`Agent ${agent.getName()} does not support MCP - skipping MCP configuration`, verbose);
|
|
367
331
|
return;
|
|
@@ -371,41 +335,9 @@ async function handleMcpConfiguration(agent, agentConfig, config, rulerMcpJson,
|
|
|
371
335
|
if (!dest || !mcpEnabledForAgent) {
|
|
372
336
|
return;
|
|
373
337
|
}
|
|
374
|
-
|
|
338
|
+
const filteredMcpJson = rulerMcpJson
|
|
375
339
|
? (0, capabilities_1.filterMcpConfigForAgent)(rulerMcpJson, agent)
|
|
376
340
|
: null;
|
|
377
|
-
// Add Skillz MCP server for agents that support stdio but not native skills
|
|
378
|
-
// Only add if skills are enabled
|
|
379
|
-
if (skillsEnabled &&
|
|
380
|
-
agent.supportsMcpStdio?.() &&
|
|
381
|
-
!agent.supportsNativeSkills?.()) {
|
|
382
|
-
// Check if .skillz directory exists
|
|
383
|
-
try {
|
|
384
|
-
const { SKILLZ_DIR } = await Promise.resolve().then(() => __importStar(require('../constants')));
|
|
385
|
-
const skillzPath = path.join(projectRoot, SKILLZ_DIR);
|
|
386
|
-
await fs_1.promises.access(skillzPath);
|
|
387
|
-
// Skills exist, add Skillz MCP server
|
|
388
|
-
const { buildSkillzMcpConfig } = await Promise.resolve().then(() => __importStar(require('./SkillsProcessor')));
|
|
389
|
-
const skillzMcp = buildSkillzMcpConfig(projectRoot);
|
|
390
|
-
// Merge Skillz server into MCP config
|
|
391
|
-
// Initialize empty config if null
|
|
392
|
-
if (!filteredMcpJson) {
|
|
393
|
-
filteredMcpJson = { mcpServers: {} };
|
|
394
|
-
}
|
|
395
|
-
const mcpServers = filteredMcpJson.mcpServers || {};
|
|
396
|
-
filteredMcpJson = {
|
|
397
|
-
...filteredMcpJson,
|
|
398
|
-
mcpServers: {
|
|
399
|
-
...mcpServers,
|
|
400
|
-
...skillzMcp,
|
|
401
|
-
},
|
|
402
|
-
};
|
|
403
|
-
(0, constants_1.logVerboseInfo)(`Added Skillz MCP server for ${agent.getName()}`, verbose, dryRun);
|
|
404
|
-
}
|
|
405
|
-
catch {
|
|
406
|
-
// No .skillz directory, skip adding Skillz server
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
341
|
if (!filteredMcpJson) {
|
|
410
342
|
(0, constants_1.logVerbose)(`No compatible MCP servers found for ${agent.getName()} - skipping MCP configuration`, verbose);
|
|
411
343
|
return;
|
|
@@ -557,6 +489,34 @@ function transformMcpForKiloCode(mcpJson) {
|
|
|
557
489
|
transformedMcp.mcpServers = transformedServers;
|
|
558
490
|
return transformedMcp;
|
|
559
491
|
}
|
|
492
|
+
/**
|
|
493
|
+
* Transform MCP server types for Factory Droid compatibility.
|
|
494
|
+
* Factory Droid expects "http" for remote HTTP servers, not "remote".
|
|
495
|
+
*/
|
|
496
|
+
function transformMcpForFactoryDroid(mcpJson) {
|
|
497
|
+
if (!mcpJson.mcpServers || typeof mcpJson.mcpServers !== 'object') {
|
|
498
|
+
return mcpJson;
|
|
499
|
+
}
|
|
500
|
+
const transformedMcp = { ...mcpJson };
|
|
501
|
+
const transformedServers = {};
|
|
502
|
+
for (const [name, serverDef] of Object.entries(mcpJson.mcpServers)) {
|
|
503
|
+
if (serverDef && typeof serverDef === 'object') {
|
|
504
|
+
const server = serverDef;
|
|
505
|
+
const transformedServer = { ...server };
|
|
506
|
+
if (server.type === 'remote' &&
|
|
507
|
+
server.url &&
|
|
508
|
+
typeof server.url === 'string') {
|
|
509
|
+
transformedServer.type = 'http';
|
|
510
|
+
}
|
|
511
|
+
transformedServers[name] = transformedServer;
|
|
512
|
+
}
|
|
513
|
+
else {
|
|
514
|
+
transformedServers[name] = serverDef;
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
transformedMcp.mcpServers = transformedServers;
|
|
518
|
+
return transformedMcp;
|
|
519
|
+
}
|
|
560
520
|
async function applyStandardMcpConfiguration(agent, filteredMcpJson, dest, agentConfig, config, cliMcpStrategy, dryRun, verbose, backup = true) {
|
|
561
521
|
const strategy = cliMcpStrategy ??
|
|
562
522
|
agentConfig?.mcp?.strategy ??
|
|
@@ -581,6 +541,9 @@ async function applyStandardMcpConfiguration(agent, filteredMcpJson, dest, agent
|
|
|
581
541
|
else if (agent.getIdentifier() === 'kilocode') {
|
|
582
542
|
mcpToMerge = transformMcpForKiloCode(filteredMcpJson);
|
|
583
543
|
}
|
|
544
|
+
else if (agent.getIdentifier() === 'factory') {
|
|
545
|
+
mcpToMerge = transformMcpForFactoryDroid(filteredMcpJson);
|
|
546
|
+
}
|
|
584
547
|
const existing = await (0, mcp_1.readNativeMcp)(dest);
|
|
585
548
|
const merged = (0, merge_1.mergeMcp)(existing, mcpToMerge, strategy, serverKey);
|
|
586
549
|
// Firebase Studio (IDX) expects no "type" fields in .idx/mcp.json server entries.
|
package/dist/lib.js
CHANGED
|
@@ -100,7 +100,7 @@ async function applyAllAgentConfigs(projectRoot, includedAgents, configPath, cli
|
|
|
100
100
|
await propagateSkills(nestedRoot, selectedAgents, skillsEnabledResolved, verbose, dryRun);
|
|
101
101
|
}
|
|
102
102
|
}
|
|
103
|
-
generatedPaths = await (0, apply_engine_1.processHierarchicalConfigurations)(selectedAgents, hierarchicalConfigs, verbose, dryRun, cliMcpEnabled, cliMcpStrategy, backup
|
|
103
|
+
generatedPaths = await (0, apply_engine_1.processHierarchicalConfigurations)(selectedAgents, hierarchicalConfigs, verbose, dryRun, cliMcpEnabled, cliMcpStrategy, backup);
|
|
104
104
|
}
|
|
105
105
|
else {
|
|
106
106
|
const singleConfig = await (0, apply_engine_1.loadSingleConfiguration)(projectRoot, configPath, localOnly);
|
|
@@ -117,7 +117,7 @@ async function applyAllAgentConfigs(projectRoot, includedAgents, configPath, cli
|
|
|
117
117
|
const { propagateSkills } = await Promise.resolve().then(() => __importStar(require('./core/SkillsProcessor')));
|
|
118
118
|
await propagateSkills(projectRoot, selectedAgents, skillsEnabledResolved, verbose, dryRun);
|
|
119
119
|
}
|
|
120
|
-
generatedPaths = await (0, apply_engine_1.processSingleConfiguration)(selectedAgents, singleConfig, projectRoot, verbose, dryRun, cliMcpEnabled, cliMcpStrategy, backup
|
|
120
|
+
generatedPaths = await (0, apply_engine_1.processSingleConfiguration)(selectedAgents, singleConfig, projectRoot, verbose, dryRun, cliMcpEnabled, cliMcpStrategy, backup);
|
|
121
121
|
}
|
|
122
122
|
// Add skills-generated paths to gitignore if skills are enabled
|
|
123
123
|
let allGeneratedPaths = generatedPaths;
|
package/dist/paths/mcp.js
CHANGED
|
@@ -86,6 +86,9 @@ async function getNativeMcpPath(adapterName, projectRoot) {
|
|
|
86
86
|
case 'Firebase Studio':
|
|
87
87
|
candidates.push(path.join(projectRoot, '.idx', 'mcp.json'));
|
|
88
88
|
break;
|
|
89
|
+
case 'Factory Droid':
|
|
90
|
+
candidates.push(path.join(projectRoot, '.factory', 'mcp.json'));
|
|
91
|
+
break;
|
|
89
92
|
case 'Zed':
|
|
90
93
|
// Only consider project-local Zed settings (avoid writing to user home directory)
|
|
91
94
|
candidates.push(path.join(projectRoot, '.zed', 'settings.json'));
|