@intellectronica/ruler 0.2.19 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +281 -62
- package/dist/agents/AbstractAgent.js +82 -0
- package/dist/agents/AgentsMdAgent.js +84 -0
- package/dist/agents/AiderAgent.js +29 -9
- package/dist/agents/AmpAgent.js +2 -44
- package/dist/agents/AugmentCodeAgent.js +10 -12
- package/dist/agents/ClaudeAgent.js +9 -9
- package/dist/agents/ClineAgent.js +3 -9
- package/dist/agents/CodexCliAgent.js +27 -21
- package/dist/agents/CopilotAgent.js +9 -10
- package/dist/agents/CrushAgent.js +6 -0
- package/dist/agents/CursorAgent.js +13 -5
- package/dist/agents/FirebaseAgent.js +2 -8
- package/dist/agents/GeminiCliAgent.js +31 -22
- package/dist/agents/GooseAgent.js +2 -10
- package/dist/agents/JulesAgent.js +3 -44
- package/dist/agents/JunieAgent.js +2 -9
- package/dist/agents/KiloCodeAgent.js +8 -9
- package/dist/agents/KiroAgent.js +50 -0
- package/dist/agents/OpenCodeAgent.js +50 -11
- package/dist/agents/OpenHandsAgent.js +8 -9
- package/dist/agents/QwenCodeAgent.js +83 -0
- package/dist/agents/WarpAgent.js +61 -0
- package/dist/agents/WindsurfAgent.js +16 -5
- package/dist/agents/ZedAgent.js +132 -0
- package/dist/agents/agent-utils.js +37 -0
- package/dist/agents/index.js +53 -0
- package/dist/cli/commands.js +53 -242
- package/dist/cli/handlers.js +181 -0
- package/dist/constants.js +9 -2
- package/dist/core/ConfigLoader.js +4 -1
- package/dist/core/FileSystemUtils.js +123 -4
- package/dist/core/RuleProcessor.js +15 -3
- package/dist/core/UnifiedConfigLoader.js +364 -0
- package/dist/core/UnifiedConfigTypes.js +2 -0
- package/dist/core/agent-selection.js +52 -0
- package/dist/core/apply-engine.js +416 -0
- package/dist/core/config-utils.js +30 -0
- package/dist/core/hash.js +24 -0
- package/dist/core/revert-engine.js +413 -0
- package/dist/lib.js +46 -329
- package/dist/mcp/capabilities.js +51 -0
- package/dist/mcp/propagateOpenCodeMcp.js +30 -31
- package/dist/mcp/propagateOpenHandsMcp.js +101 -17
- package/dist/paths/mcp.js +7 -3
- package/dist/revert.js +96 -481
- package/package.json +2 -1
package/dist/lib.js
CHANGED
|
@@ -1,123 +1,13 @@
|
|
|
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
|
-
})();
|
|
35
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.allAgents = void 0;
|
|
36
4
|
exports.applyAllAgentConfigs = applyAllAgentConfigs;
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
const FileSystemUtils = __importStar(require("./core/FileSystemUtils"));
|
|
40
|
-
const RuleProcessor_1 = require("./core/RuleProcessor");
|
|
41
|
-
const ConfigLoader_1 = require("./core/ConfigLoader");
|
|
42
|
-
const GitignoreUtils_1 = require("./core/GitignoreUtils");
|
|
43
|
-
const CopilotAgent_1 = require("./agents/CopilotAgent");
|
|
44
|
-
const ClaudeAgent_1 = require("./agents/ClaudeAgent");
|
|
45
|
-
const CodexCliAgent_1 = require("./agents/CodexCliAgent");
|
|
46
|
-
const CursorAgent_1 = require("./agents/CursorAgent");
|
|
47
|
-
const WindsurfAgent_1 = require("./agents/WindsurfAgent");
|
|
48
|
-
const ClineAgent_1 = require("./agents/ClineAgent");
|
|
49
|
-
const AiderAgent_1 = require("./agents/AiderAgent");
|
|
50
|
-
const FirebaseAgent_1 = require("./agents/FirebaseAgent");
|
|
51
|
-
const OpenHandsAgent_1 = require("./agents/OpenHandsAgent");
|
|
52
|
-
const GeminiCliAgent_1 = require("./agents/GeminiCliAgent");
|
|
53
|
-
const JulesAgent_1 = require("./agents/JulesAgent");
|
|
54
|
-
const JunieAgent_1 = require("./agents/JunieAgent");
|
|
55
|
-
const AugmentCodeAgent_1 = require("./agents/AugmentCodeAgent");
|
|
56
|
-
const KiloCodeAgent_1 = require("./agents/KiloCodeAgent");
|
|
57
|
-
const OpenCodeAgent_1 = require("./agents/OpenCodeAgent");
|
|
58
|
-
const CrushAgent_1 = require("./agents/CrushAgent");
|
|
59
|
-
const GooseAgent_1 = require("./agents/GooseAgent");
|
|
60
|
-
const AmpAgent_1 = require("./agents/AmpAgent");
|
|
61
|
-
const merge_1 = require("./mcp/merge");
|
|
62
|
-
const validate_1 = require("./mcp/validate");
|
|
63
|
-
const mcp_1 = require("./paths/mcp");
|
|
64
|
-
const propagateOpenHandsMcp_1 = require("./mcp/propagateOpenHandsMcp");
|
|
65
|
-
const propagateOpenCodeMcp_1 = require("./mcp/propagateOpenCodeMcp");
|
|
5
|
+
const agents_1 = require("./agents");
|
|
6
|
+
Object.defineProperty(exports, "allAgents", { enumerable: true, get: function () { return agents_1.allAgents; } });
|
|
66
7
|
const constants_1 = require("./constants");
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
function getAgentOutputPaths(agent, projectRoot, agentConfig) {
|
|
71
|
-
const paths = [];
|
|
72
|
-
const defaults = agent.getDefaultOutputPath(projectRoot);
|
|
73
|
-
if (typeof defaults === 'string') {
|
|
74
|
-
// Single output path (most agents)
|
|
75
|
-
const actualPath = agentConfig?.outputPath ?? defaults;
|
|
76
|
-
paths.push(actualPath);
|
|
77
|
-
}
|
|
78
|
-
else {
|
|
79
|
-
// Multiple output paths (e.g., AiderAgent)
|
|
80
|
-
const defaultPaths = defaults;
|
|
81
|
-
// Handle instructions path
|
|
82
|
-
if ('instructions' in defaultPaths) {
|
|
83
|
-
const instructionsPath = agentConfig?.outputPathInstructions ?? defaultPaths.instructions;
|
|
84
|
-
paths.push(instructionsPath);
|
|
85
|
-
}
|
|
86
|
-
// Handle config path
|
|
87
|
-
if ('config' in defaultPaths) {
|
|
88
|
-
const configPath = agentConfig?.outputPathConfig ?? defaultPaths.config;
|
|
89
|
-
paths.push(configPath);
|
|
90
|
-
}
|
|
91
|
-
// Handle any other paths in the default paths record
|
|
92
|
-
for (const [key, defaultPath] of Object.entries(defaultPaths)) {
|
|
93
|
-
if (key !== 'instructions' && key !== 'config') {
|
|
94
|
-
// For unknown path types, use the default since we don't have specific config overrides
|
|
95
|
-
paths.push(defaultPath);
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
return paths;
|
|
100
|
-
}
|
|
101
|
-
const agents = [
|
|
102
|
-
new CopilotAgent_1.CopilotAgent(),
|
|
103
|
-
new ClaudeAgent_1.ClaudeAgent(),
|
|
104
|
-
new CodexCliAgent_1.CodexCliAgent(),
|
|
105
|
-
new CursorAgent_1.CursorAgent(),
|
|
106
|
-
new WindsurfAgent_1.WindsurfAgent(),
|
|
107
|
-
new ClineAgent_1.ClineAgent(),
|
|
108
|
-
new AiderAgent_1.AiderAgent(),
|
|
109
|
-
new FirebaseAgent_1.FirebaseAgent(),
|
|
110
|
-
new OpenHandsAgent_1.OpenHandsAgent(),
|
|
111
|
-
new GeminiCliAgent_1.GeminiCliAgent(),
|
|
112
|
-
new JulesAgent_1.JulesAgent(),
|
|
113
|
-
new JunieAgent_1.JunieAgent(),
|
|
114
|
-
new AugmentCodeAgent_1.AugmentCodeAgent(),
|
|
115
|
-
new KiloCodeAgent_1.KiloCodeAgent(),
|
|
116
|
-
new OpenCodeAgent_1.OpenCodeAgent(),
|
|
117
|
-
new GooseAgent_1.GooseAgent(),
|
|
118
|
-
new CrushAgent_1.CrushAgent(),
|
|
119
|
-
new AmpAgent_1.AmpAgent(),
|
|
120
|
-
];
|
|
8
|
+
const apply_engine_1 = require("./core/apply-engine");
|
|
9
|
+
const config_utils_1 = require("./core/config-utils");
|
|
10
|
+
const agents = agents_1.allAgents;
|
|
121
11
|
/**
|
|
122
12
|
* Applies ruler configurations for all supported AI agents.
|
|
123
13
|
* @param projectRoot Root directory of the project
|
|
@@ -127,224 +17,51 @@ const agents = [
|
|
|
127
17
|
* @param projectRoot Root directory of the project
|
|
128
18
|
* @param includedAgents Optional list of agent name filters (case-insensitive substrings)
|
|
129
19
|
*/
|
|
130
|
-
async function applyAllAgentConfigs(projectRoot, includedAgents, configPath, cliMcpEnabled = true, cliMcpStrategy, cliGitignoreEnabled, verbose = false, dryRun = false, localOnly = false) {
|
|
131
|
-
// Load configuration
|
|
20
|
+
async function applyAllAgentConfigs(projectRoot, includedAgents, configPath, cliMcpEnabled = true, cliMcpStrategy, cliGitignoreEnabled, verbose = false, dryRun = false, localOnly = false, nested = false) {
|
|
21
|
+
// Load configuration and rules
|
|
132
22
|
(0, constants_1.logVerbose)(`Loading configuration from project root: ${projectRoot}`, verbose);
|
|
133
23
|
if (configPath) {
|
|
134
24
|
(0, constants_1.logVerbose)(`Using custom config path: ${configPath}`, verbose);
|
|
135
25
|
}
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
const rawConfigs = config.agentConfigs;
|
|
144
|
-
const mappedConfigs = {};
|
|
145
|
-
for (const [key, cfg] of Object.entries(rawConfigs)) {
|
|
146
|
-
const lowerKey = key.toLowerCase();
|
|
147
|
-
for (const agent of agents) {
|
|
148
|
-
const identifier = agent.getIdentifier();
|
|
149
|
-
// Exact match with identifier or substring match with display name for backwards compatibility
|
|
150
|
-
if (identifier === lowerKey ||
|
|
151
|
-
agent.getName().toLowerCase().includes(lowerKey)) {
|
|
152
|
-
mappedConfigs[identifier] = cfg;
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
config.agentConfigs = mappedConfigs;
|
|
157
|
-
const rulerDir = await FileSystemUtils.findRulerDir(projectRoot, !localOnly);
|
|
158
|
-
if (!rulerDir) {
|
|
159
|
-
throw (0, constants_1.createRulerError)(`.ruler directory not found`, `Searched from: ${projectRoot}`);
|
|
160
|
-
}
|
|
161
|
-
(0, constants_1.logVerbose)(`Found .ruler directory at: ${rulerDir}`, verbose);
|
|
162
|
-
const files = await FileSystemUtils.readMarkdownFiles(rulerDir);
|
|
163
|
-
(0, constants_1.logVerbose)(`Found ${files.length} markdown files in ruler configuration directory`, verbose);
|
|
164
|
-
const concatenated = (0, RuleProcessor_1.concatenateRules)(files);
|
|
165
|
-
(0, constants_1.logVerbose)(`Concatenated rules length: ${concatenated.length} characters`, verbose);
|
|
166
|
-
const mcpFile = path.join(rulerDir, 'mcp.json');
|
|
167
|
-
let rulerMcpJson = null;
|
|
168
|
-
try {
|
|
169
|
-
const raw = await fs_1.promises.readFile(mcpFile, 'utf8');
|
|
170
|
-
rulerMcpJson = JSON.parse(raw);
|
|
171
|
-
(0, validate_1.validateMcp)(rulerMcpJson);
|
|
172
|
-
(0, constants_1.logVerbose)(`Loaded MCP configuration from: ${mcpFile}`, verbose);
|
|
173
|
-
}
|
|
174
|
-
catch (err) {
|
|
175
|
-
if (err.code !== 'ENOENT') {
|
|
176
|
-
throw (0, constants_1.createRulerError)(`Failed to load MCP configuration`, `File: ${mcpFile}, Error: ${err.message}`);
|
|
177
|
-
}
|
|
178
|
-
(0, constants_1.logVerbose)(`No MCP configuration found at: ${mcpFile}`, verbose);
|
|
179
|
-
}
|
|
180
|
-
// Determine which agents to run:
|
|
181
|
-
// CLI --agents > config.default_agents > per-agent.enabled flags > default all
|
|
182
|
-
let selected = agents;
|
|
183
|
-
if (config.cliAgents && config.cliAgents.length > 0) {
|
|
184
|
-
const filters = config.cliAgents.map((n) => n.toLowerCase());
|
|
185
|
-
// Check if any of the specified agents don't exist
|
|
186
|
-
const validAgentIdentifiers = new Set(agents.map((agent) => agent.getIdentifier()));
|
|
187
|
-
const validAgentNames = new Set(agents.map((agent) => agent.getName().toLowerCase()));
|
|
188
|
-
const invalidAgents = filters.filter((filter) => !validAgentIdentifiers.has(filter) &&
|
|
189
|
-
![...validAgentNames].some((name) => name.includes(filter)));
|
|
190
|
-
if (invalidAgents.length > 0) {
|
|
191
|
-
throw (0, constants_1.createRulerError)(`Invalid agent specified: ${invalidAgents.join(', ')}`, `Valid agents are: ${[...validAgentIdentifiers].join(', ')}`);
|
|
26
|
+
let selectedAgents;
|
|
27
|
+
let generatedPaths;
|
|
28
|
+
let loadedConfig;
|
|
29
|
+
if (nested) {
|
|
30
|
+
const hierarchicalConfigs = await (0, apply_engine_1.loadNestedConfigurations)(projectRoot, configPath, localOnly);
|
|
31
|
+
if (hierarchicalConfigs.length === 0) {
|
|
32
|
+
throw new Error('No .ruler directories found');
|
|
192
33
|
}
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
![...validAgentNames].some((name) => name.includes(filter)));
|
|
204
|
-
if (invalidAgents.length > 0) {
|
|
205
|
-
throw (0, constants_1.createRulerError)(`Invalid agent specified in default_agents: ${invalidAgents.join(', ')}`, `Valid agents are: ${[...validAgentIdentifiers].join(', ')}`);
|
|
206
|
-
}
|
|
207
|
-
selected = agents.filter((agent) => {
|
|
208
|
-
const identifier = agent.getIdentifier();
|
|
209
|
-
const override = config.agentConfigs[identifier]?.enabled;
|
|
210
|
-
if (override !== undefined) {
|
|
211
|
-
return override;
|
|
212
|
-
}
|
|
213
|
-
return defaults.some((d) => identifier === d || agent.getName().toLowerCase().includes(d));
|
|
214
|
-
});
|
|
215
|
-
(0, constants_1.logVerbose)(`Selected agents via config default_agents: ${selected.map((a) => a.getName()).join(', ')}`, verbose);
|
|
34
|
+
// Use the root config for agent selection (all levels share the same agent settings)
|
|
35
|
+
const rootConfig = hierarchicalConfigs[0].config;
|
|
36
|
+
loadedConfig = rootConfig;
|
|
37
|
+
rootConfig.cliAgents = includedAgents;
|
|
38
|
+
(0, constants_1.logVerbose)(`Loaded ${hierarchicalConfigs.length} .ruler directory configurations`, verbose);
|
|
39
|
+
(0, constants_1.logVerbose)(`Root configuration has ${Object.keys(rootConfig.agentConfigs).length} agent configs`, verbose);
|
|
40
|
+
normalizeAgentConfigs(rootConfig, agents);
|
|
41
|
+
selectedAgents = (0, apply_engine_1.selectAgentsToRun)(agents, rootConfig);
|
|
42
|
+
(0, constants_1.logVerbose)(`Selected ${selectedAgents.length} agents: ${selectedAgents.map((a) => a.getName()).join(', ')}`, verbose);
|
|
43
|
+
generatedPaths = await (0, apply_engine_1.processHierarchicalConfigurations)(selectedAgents, hierarchicalConfigs, verbose, dryRun, cliMcpEnabled, cliMcpStrategy);
|
|
216
44
|
}
|
|
217
45
|
else {
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
else {
|
|
240
|
-
if (agent.getIdentifier() === 'jules' ||
|
|
241
|
-
agent.getIdentifier() === 'codex') {
|
|
242
|
-
if (agentsMdWritten) {
|
|
243
|
-
continue;
|
|
244
|
-
}
|
|
245
|
-
agentsMdWritten = true;
|
|
246
|
-
}
|
|
247
|
-
let finalAgentConfig = agentConfig;
|
|
248
|
-
if (agent.getIdentifier() === 'augmentcode' && rulerMcpJson) {
|
|
249
|
-
const resolvedStrategy = cliMcpStrategy ??
|
|
250
|
-
agentConfig?.mcp?.strategy ??
|
|
251
|
-
config.mcp?.strategy ??
|
|
252
|
-
'merge';
|
|
253
|
-
finalAgentConfig = {
|
|
254
|
-
...agentConfig,
|
|
255
|
-
mcp: {
|
|
256
|
-
...agentConfig?.mcp,
|
|
257
|
-
strategy: resolvedStrategy,
|
|
258
|
-
},
|
|
259
|
-
};
|
|
260
|
-
}
|
|
261
|
-
await agent.applyRulerConfig(concatenated, projectRoot, rulerMcpJson, finalAgentConfig);
|
|
262
|
-
}
|
|
263
|
-
const dest = await (0, mcp_1.getNativeMcpPath)(agent.getName(), projectRoot);
|
|
264
|
-
const mcpEnabledForAgent = cliMcpEnabled &&
|
|
265
|
-
(agentConfig?.mcp?.enabled ?? config.mcp?.enabled ?? true);
|
|
266
|
-
const rulerMcpFile = path.join(rulerDir, 'mcp.json');
|
|
267
|
-
if (dest && mcpEnabledForAgent) {
|
|
268
|
-
// Include MCP config file in .gitignore only if it's within the project directory
|
|
269
|
-
if (dest.startsWith(projectRoot)) {
|
|
270
|
-
const relativeDest = path.relative(projectRoot, dest);
|
|
271
|
-
generatedPaths.push(relativeDest);
|
|
272
|
-
// Also add the backup for the MCP file
|
|
273
|
-
generatedPaths.push(`${relativeDest}.bak`);
|
|
274
|
-
}
|
|
275
|
-
if (agent.getIdentifier() === 'openhands') {
|
|
276
|
-
// *** Special handling for Open Hands ***
|
|
277
|
-
if (dryRun) {
|
|
278
|
-
(0, constants_1.logVerbose)(`DRY RUN: Would apply MCP config by updating TOML file: ${dest}`, verbose);
|
|
279
|
-
}
|
|
280
|
-
else {
|
|
281
|
-
await (0, propagateOpenHandsMcp_1.propagateMcpToOpenHands)(rulerMcpFile, dest);
|
|
282
|
-
}
|
|
283
|
-
// Open Hands config file is already included above
|
|
284
|
-
}
|
|
285
|
-
else if (agent.getIdentifier() === 'augmentcode') {
|
|
286
|
-
// *** Special handling for AugmentCode ***
|
|
287
|
-
// AugmentCode handles MCP configuration internally in applyRulerConfig
|
|
288
|
-
// by updating VSCode settings.json with augment.advanced.mcpServers format
|
|
289
|
-
if (dryRun) {
|
|
290
|
-
(0, constants_1.logVerbose)(`DRY RUN: AugmentCode MCP config handled internally via VSCode settings`, verbose);
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
else if (agent.getIdentifier() === 'opencode') {
|
|
294
|
-
// *** Special handling for OpenCode ***
|
|
295
|
-
if (dryRun) {
|
|
296
|
-
(0, constants_1.logVerbose)(`DRY RUN: Would apply MCP config by updating OpenCode config file: ${dest}`, verbose);
|
|
297
|
-
}
|
|
298
|
-
else {
|
|
299
|
-
await (0, propagateOpenCodeMcp_1.propagateMcpToOpenCode)(rulerMcpFile, dest);
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
else {
|
|
303
|
-
if (rulerMcpJson) {
|
|
304
|
-
const strategy = cliMcpStrategy ??
|
|
305
|
-
agentConfig?.mcp?.strategy ??
|
|
306
|
-
config.mcp?.strategy ??
|
|
307
|
-
'merge';
|
|
308
|
-
// Determine the correct server key for the agent
|
|
309
|
-
const serverKey = agent.getMcpServerKey?.() ?? 'mcpServers';
|
|
310
|
-
(0, constants_1.logVerbose)(`Applying MCP config for ${agent.getName()} with strategy: ${strategy} and key: ${serverKey}`, verbose);
|
|
311
|
-
if (dryRun) {
|
|
312
|
-
(0, constants_1.logVerbose)(`DRY RUN: Would apply MCP config to: ${dest}`, true);
|
|
313
|
-
}
|
|
314
|
-
else {
|
|
315
|
-
const existing = await (0, mcp_1.readNativeMcp)(dest);
|
|
316
|
-
const merged = (0, merge_1.mergeMcp)(existing, rulerMcpJson, strategy, serverKey);
|
|
317
|
-
await (0, mcp_1.writeNativeMcp)(dest, merged);
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
// Handle .gitignore updates
|
|
324
|
-
// Configuration precedence: CLI > TOML > Default (enabled)
|
|
325
|
-
let gitignoreEnabled;
|
|
326
|
-
if (cliGitignoreEnabled !== undefined) {
|
|
327
|
-
gitignoreEnabled = cliGitignoreEnabled;
|
|
328
|
-
}
|
|
329
|
-
else if (config.gitignore?.enabled !== undefined) {
|
|
330
|
-
gitignoreEnabled = config.gitignore.enabled;
|
|
331
|
-
}
|
|
332
|
-
else {
|
|
333
|
-
gitignoreEnabled = true; // Default enabled
|
|
334
|
-
}
|
|
335
|
-
if (gitignoreEnabled && generatedPaths.length > 0) {
|
|
336
|
-
const uniquePaths = [...new Set(generatedPaths)];
|
|
337
|
-
// Add wildcard pattern for backup files
|
|
338
|
-
uniquePaths.push('*.bak');
|
|
339
|
-
if (uniquePaths.length > 0) {
|
|
340
|
-
const actionPrefix = dryRun ? '[ruler:dry-run]' : '[ruler]';
|
|
341
|
-
if (dryRun) {
|
|
342
|
-
console.log(`${actionPrefix} Would update .gitignore with ${uniquePaths.length} unique path(s): ${uniquePaths.join(', ')}`);
|
|
343
|
-
}
|
|
344
|
-
else {
|
|
345
|
-
await (0, GitignoreUtils_1.updateGitignore)(projectRoot, uniquePaths);
|
|
346
|
-
console.log(`${actionPrefix} Updated .gitignore with ${uniquePaths.length} unique path(s) in the Ruler block.`);
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
}
|
|
46
|
+
const singleConfig = await (0, apply_engine_1.loadSingleConfiguration)(projectRoot, configPath, localOnly);
|
|
47
|
+
loadedConfig = singleConfig.config;
|
|
48
|
+
singleConfig.config.cliAgents = includedAgents;
|
|
49
|
+
(0, constants_1.logVerbose)(`Loaded configuration with ${Object.keys(singleConfig.config.agentConfigs).length} agent configs`, verbose);
|
|
50
|
+
(0, constants_1.logVerbose)(`Found .ruler directory with ${singleConfig.concatenatedRules.length} characters of rules`, verbose);
|
|
51
|
+
normalizeAgentConfigs(singleConfig.config, agents);
|
|
52
|
+
selectedAgents = (0, apply_engine_1.selectAgentsToRun)(agents, singleConfig.config);
|
|
53
|
+
(0, constants_1.logVerbose)(`Selected ${selectedAgents.length} agents: ${selectedAgents.map((a) => a.getName()).join(', ')}`, verbose);
|
|
54
|
+
generatedPaths = await (0, apply_engine_1.processSingleConfiguration)(selectedAgents, singleConfig, projectRoot, verbose, dryRun, cliMcpEnabled, cliMcpStrategy);
|
|
55
|
+
}
|
|
56
|
+
await (0, apply_engine_1.updateGitignore)(projectRoot, generatedPaths, loadedConfig, cliGitignoreEnabled, dryRun);
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Normalizes per-agent config keys to agent identifiers for consistent lookup.
|
|
60
|
+
* Maps both exact identifier matches and substring matches with agent names.
|
|
61
|
+
* @param config The configuration object to normalize
|
|
62
|
+
* @param agents Array of available agents
|
|
63
|
+
*/
|
|
64
|
+
function normalizeAgentConfigs(config, agents) {
|
|
65
|
+
// Normalize per-agent config keys to agent identifiers (exact match or substring match)
|
|
66
|
+
config.agentConfigs = (0, config_utils_1.mapRawAgentConfigs)(config.agentConfigs, agents);
|
|
350
67
|
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getAgentMcpCapabilities = getAgentMcpCapabilities;
|
|
4
|
+
exports.agentSupportsMcp = agentSupportsMcp;
|
|
5
|
+
exports.filterMcpConfigForAgent = filterMcpConfigForAgent;
|
|
6
|
+
/**
|
|
7
|
+
* Derives MCP capabilities for an agent
|
|
8
|
+
*/
|
|
9
|
+
function getAgentMcpCapabilities(agent) {
|
|
10
|
+
return {
|
|
11
|
+
supportsStdio: agent.supportsMcpStdio?.() ?? false,
|
|
12
|
+
supportsRemote: agent.supportsMcpRemote?.() ?? false,
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Checks if an agent supports any MCP functionality
|
|
17
|
+
*/
|
|
18
|
+
function agentSupportsMcp(agent) {
|
|
19
|
+
const capabilities = getAgentMcpCapabilities(agent);
|
|
20
|
+
return capabilities.supportsStdio || capabilities.supportsRemote;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Filters MCP configuration based on agent capabilities
|
|
24
|
+
*/
|
|
25
|
+
function filterMcpConfigForAgent(mcpConfig, agent) {
|
|
26
|
+
const capabilities = getAgentMcpCapabilities(agent);
|
|
27
|
+
if (!agentSupportsMcp(agent)) {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
const servers = mcpConfig.mcpServers;
|
|
31
|
+
if (!servers) {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
const filteredServers = {};
|
|
35
|
+
for (const [serverName, serverConfig] of Object.entries(servers)) {
|
|
36
|
+
const config = serverConfig;
|
|
37
|
+
// Determine server type
|
|
38
|
+
const hasCommand = 'command' in config;
|
|
39
|
+
const hasUrl = 'url' in config;
|
|
40
|
+
const isStdio = hasCommand && !hasUrl;
|
|
41
|
+
const isRemote = hasUrl && !hasCommand;
|
|
42
|
+
// Include server if agent supports its type
|
|
43
|
+
if ((isStdio && capabilities.supportsStdio) ||
|
|
44
|
+
(isRemote && capabilities.supportsRemote)) {
|
|
45
|
+
filteredServers[serverName] = serverConfig;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return Object.keys(filteredServers).length > 0
|
|
49
|
+
? { mcpServers: filteredServers }
|
|
50
|
+
: null;
|
|
51
|
+
}
|
|
@@ -37,6 +37,15 @@ exports.propagateMcpToOpenCode = propagateMcpToOpenCode;
|
|
|
37
37
|
const fs = __importStar(require("fs/promises"));
|
|
38
38
|
const FileSystemUtils_1 = require("../core/FileSystemUtils");
|
|
39
39
|
const path = __importStar(require("path"));
|
|
40
|
+
function isLocalServer(value) {
|
|
41
|
+
const server = value;
|
|
42
|
+
return (server &&
|
|
43
|
+
(typeof server.command === 'string' || Array.isArray(server.command)));
|
|
44
|
+
}
|
|
45
|
+
function isRemoteServer(value) {
|
|
46
|
+
const server = value;
|
|
47
|
+
return server && typeof server.url === 'string';
|
|
48
|
+
}
|
|
40
49
|
/**
|
|
41
50
|
* Transform ruler MCP configuration to OpenCode's specific format
|
|
42
51
|
*/
|
|
@@ -44,34 +53,31 @@ function transformToOpenCodeFormat(rulerMcp) {
|
|
|
44
53
|
const rulerServers = rulerMcp.mcpServers || {};
|
|
45
54
|
const openCodeServers = {};
|
|
46
55
|
for (const [name, serverDef] of Object.entries(rulerServers)) {
|
|
47
|
-
const server = serverDef;
|
|
48
|
-
// Determine if this is a local or remote server
|
|
49
|
-
const isRemote = !!server.url;
|
|
50
56
|
const openCodeServer = {
|
|
51
|
-
type:
|
|
52
|
-
enabled: true,
|
|
57
|
+
type: 'local',
|
|
58
|
+
enabled: true,
|
|
53
59
|
};
|
|
54
|
-
if (
|
|
55
|
-
|
|
56
|
-
openCodeServer.url =
|
|
57
|
-
if (
|
|
58
|
-
openCodeServer.headers =
|
|
60
|
+
if (isRemoteServer(serverDef)) {
|
|
61
|
+
openCodeServer.type = 'remote';
|
|
62
|
+
openCodeServer.url = serverDef.url;
|
|
63
|
+
if (serverDef.headers) {
|
|
64
|
+
openCodeServer.headers = serverDef.headers;
|
|
59
65
|
}
|
|
60
66
|
}
|
|
61
|
-
else {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
openCodeServer.
|
|
70
|
-
}
|
|
71
|
-
if (server.env) {
|
|
72
|
-
openCodeServer.environment = server.env;
|
|
67
|
+
else if (isLocalServer(serverDef)) {
|
|
68
|
+
openCodeServer.type = 'local';
|
|
69
|
+
const command = Array.isArray(serverDef.command)
|
|
70
|
+
? serverDef.command
|
|
71
|
+
: [serverDef.command];
|
|
72
|
+
const args = serverDef.args || [];
|
|
73
|
+
openCodeServer.command = [...command, ...args];
|
|
74
|
+
if (serverDef.env) {
|
|
75
|
+
openCodeServer.environment = serverDef.env;
|
|
73
76
|
}
|
|
74
77
|
}
|
|
78
|
+
else {
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
75
81
|
openCodeServers[name] = openCodeServer;
|
|
76
82
|
}
|
|
77
83
|
return {
|
|
@@ -79,15 +85,8 @@ function transformToOpenCodeFormat(rulerMcp) {
|
|
|
79
85
|
mcp: openCodeServers,
|
|
80
86
|
};
|
|
81
87
|
}
|
|
82
|
-
async function propagateMcpToOpenCode(
|
|
83
|
-
|
|
84
|
-
try {
|
|
85
|
-
const rulerJsonContent = await fs.readFile(rulerMcpPath, 'utf8');
|
|
86
|
-
rulerMcp = JSON.parse(rulerJsonContent);
|
|
87
|
-
}
|
|
88
|
-
catch {
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
88
|
+
async function propagateMcpToOpenCode(rulerMcpData, openCodeConfigPath) {
|
|
89
|
+
const rulerMcp = rulerMcpData || {};
|
|
91
90
|
// Read existing OpenCode config if it exists
|
|
92
91
|
let existingConfig = {};
|
|
93
92
|
try {
|