@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.
Files changed (47) hide show
  1. package/README.md +281 -62
  2. package/dist/agents/AbstractAgent.js +82 -0
  3. package/dist/agents/AgentsMdAgent.js +84 -0
  4. package/dist/agents/AiderAgent.js +29 -9
  5. package/dist/agents/AmpAgent.js +2 -44
  6. package/dist/agents/AugmentCodeAgent.js +10 -12
  7. package/dist/agents/ClaudeAgent.js +9 -9
  8. package/dist/agents/ClineAgent.js +3 -9
  9. package/dist/agents/CodexCliAgent.js +27 -21
  10. package/dist/agents/CopilotAgent.js +9 -10
  11. package/dist/agents/CrushAgent.js +6 -0
  12. package/dist/agents/CursorAgent.js +13 -5
  13. package/dist/agents/FirebaseAgent.js +2 -8
  14. package/dist/agents/GeminiCliAgent.js +31 -22
  15. package/dist/agents/GooseAgent.js +2 -10
  16. package/dist/agents/JulesAgent.js +3 -44
  17. package/dist/agents/JunieAgent.js +2 -9
  18. package/dist/agents/KiloCodeAgent.js +8 -9
  19. package/dist/agents/KiroAgent.js +50 -0
  20. package/dist/agents/OpenCodeAgent.js +50 -11
  21. package/dist/agents/OpenHandsAgent.js +8 -9
  22. package/dist/agents/QwenCodeAgent.js +83 -0
  23. package/dist/agents/WarpAgent.js +61 -0
  24. package/dist/agents/WindsurfAgent.js +16 -5
  25. package/dist/agents/ZedAgent.js +132 -0
  26. package/dist/agents/agent-utils.js +37 -0
  27. package/dist/agents/index.js +53 -0
  28. package/dist/cli/commands.js +53 -242
  29. package/dist/cli/handlers.js +181 -0
  30. package/dist/constants.js +9 -2
  31. package/dist/core/ConfigLoader.js +4 -1
  32. package/dist/core/FileSystemUtils.js +123 -4
  33. package/dist/core/RuleProcessor.js +15 -3
  34. package/dist/core/UnifiedConfigLoader.js +364 -0
  35. package/dist/core/UnifiedConfigTypes.js +2 -0
  36. package/dist/core/agent-selection.js +52 -0
  37. package/dist/core/apply-engine.js +416 -0
  38. package/dist/core/config-utils.js +30 -0
  39. package/dist/core/hash.js +24 -0
  40. package/dist/core/revert-engine.js +413 -0
  41. package/dist/lib.js +46 -329
  42. package/dist/mcp/capabilities.js +51 -0
  43. package/dist/mcp/propagateOpenCodeMcp.js +30 -31
  44. package/dist/mcp/propagateOpenHandsMcp.js +101 -17
  45. package/dist/paths/mcp.js +7 -3
  46. package/dist/revert.js +96 -481
  47. package/package.json +2 -1
@@ -0,0 +1,84 @@
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
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.AgentsMdAgent = void 0;
37
+ const path = __importStar(require("path"));
38
+ const fs_1 = require("fs");
39
+ const AbstractAgent_1 = require("./AbstractAgent");
40
+ const FileSystemUtils_1 = require("../core/FileSystemUtils");
41
+ /**
42
+ * Pseudo-agent that ensures the concatenated rules are written to root-level `AGENTS.md`.
43
+ * Does not participate in MCP propagation. Idempotent: only writes (and creates a backup)
44
+ * when content differs from existing file.
45
+ */
46
+ class AgentsMdAgent extends AbstractAgent_1.AbstractAgent {
47
+ getIdentifier() {
48
+ return 'agentsmd';
49
+ }
50
+ getName() {
51
+ return 'AgentsMd';
52
+ }
53
+ getDefaultOutputPath(projectRoot) {
54
+ return path.join(projectRoot, 'AGENTS.md');
55
+ }
56
+ async applyRulerConfig(concatenatedRules, projectRoot, rulerMcpJson, // eslint-disable-line @typescript-eslint/no-unused-vars
57
+ agentConfig) {
58
+ const output = agentConfig?.outputPath ?? this.getDefaultOutputPath(projectRoot);
59
+ const absolutePath = path.resolve(projectRoot, output);
60
+ await (0, FileSystemUtils_1.ensureDirExists)(path.dirname(absolutePath));
61
+ // Add marker comment to the content to identify it as generated
62
+ const contentWithMarker = `<!-- Generated by Ruler -->\n${concatenatedRules}`;
63
+ // Read existing content if present and skip write if identical
64
+ let existing = null;
65
+ try {
66
+ existing = await fs_1.promises.readFile(absolutePath, 'utf8');
67
+ }
68
+ catch {
69
+ existing = null;
70
+ }
71
+ if (existing !== null && existing === contentWithMarker) {
72
+ // No change; skip backup/write for idempotency
73
+ return;
74
+ }
75
+ // Backup (only if file existed) then write new content
76
+ await (0, FileSystemUtils_1.backupFile)(absolutePath);
77
+ await (0, FileSystemUtils_1.writeGeneratedFile)(absolutePath, contentWithMarker);
78
+ }
79
+ getMcpServerKey() {
80
+ // No MCP configuration for this pseudo-agent
81
+ return '';
82
+ }
83
+ }
84
+ exports.AgentsMdAgent = AgentsMdAgent;
@@ -35,25 +35,32 @@ var __importStar = (this && this.__importStar) || (function () {
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.AiderAgent = void 0;
37
37
  const path = __importStar(require("path"));
38
+ const AgentsMdAgent_1 = require("./AgentsMdAgent");
38
39
  const FileSystemUtils_1 = require("../core/FileSystemUtils");
39
40
  const fs = __importStar(require("fs/promises"));
40
41
  const yaml = __importStar(require("js-yaml"));
41
42
  /**
42
- * Aider agent adapter (stub implementation).
43
+ * Aider agent adapter that uses AGENTS.md for instructions and .aider.conf.yml for configuration.
43
44
  */
44
45
  class AiderAgent {
46
+ constructor() {
47
+ this.agentsMdAgent = new AgentsMdAgent_1.AgentsMdAgent();
48
+ }
45
49
  getIdentifier() {
46
50
  return 'aider';
47
51
  }
48
52
  getName() {
49
53
  return 'Aider';
50
54
  }
51
- async applyRulerConfig(concatenatedRules, projectRoot, rulerMcpJson, // eslint-disable-line @typescript-eslint/no-unused-vars
52
- agentConfig) {
53
- const mdPath = agentConfig?.outputPathInstructions ??
54
- this.getDefaultOutputPath(projectRoot).instructions;
55
- await (0, FileSystemUtils_1.backupFile)(mdPath);
56
- await (0, FileSystemUtils_1.writeGeneratedFile)(mdPath, concatenatedRules);
55
+ async applyRulerConfig(concatenatedRules, projectRoot, rulerMcpJson, agentConfig) {
56
+ // First perform idempotent AGENTS.md write via composed AgentsMdAgent
57
+ await this.agentsMdAgent.applyRulerConfig(concatenatedRules, projectRoot, null, {
58
+ // Preserve explicit outputPath precedence semantics if provided.
59
+ outputPath: agentConfig?.outputPath ||
60
+ agentConfig?.outputPathInstructions ||
61
+ undefined,
62
+ });
63
+ // Now handle .aider.conf.yml configuration
57
64
  const cfgPath = agentConfig?.outputPathConfig ??
58
65
  this.getDefaultOutputPath(projectRoot).config;
59
66
  let doc = {};
@@ -69,7 +76,11 @@ class AiderAgent {
69
76
  if (!Array.isArray(doc.read)) {
70
77
  doc.read = [];
71
78
  }
72
- const name = path.basename(mdPath);
79
+ // Determine the actual agents file path (AGENTS.md by default, or custom path)
80
+ const agentsPath = agentConfig?.outputPath ||
81
+ agentConfig?.outputPathInstructions ||
82
+ this.getDefaultOutputPath(projectRoot).instructions;
83
+ const name = path.basename(agentsPath);
73
84
  if (!doc.read.includes(name)) {
74
85
  doc.read.push(name);
75
86
  }
@@ -78,9 +89,18 @@ class AiderAgent {
78
89
  }
79
90
  getDefaultOutputPath(projectRoot) {
80
91
  return {
81
- instructions: path.join(projectRoot, 'ruler_aider_instructions.md'),
92
+ instructions: path.join(projectRoot, 'AGENTS.md'),
82
93
  config: path.join(projectRoot, '.aider.conf.yml'),
83
94
  };
84
95
  }
96
+ getMcpServerKey() {
97
+ return this.agentsMdAgent.getMcpServerKey();
98
+ }
99
+ supportsMcpStdio() {
100
+ return true;
101
+ }
102
+ supportsMcpRemote() {
103
+ return true;
104
+ }
85
105
  }
86
106
  exports.AiderAgent = AiderAgent;
@@ -1,55 +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 });
36
3
  exports.AmpAgent = void 0;
37
- const FileSystemUtils_1 = require("../core/FileSystemUtils");
38
- const path = __importStar(require("path"));
39
- class AmpAgent {
4
+ const AgentsMdAgent_1 = require("./AgentsMdAgent");
5
+ class AmpAgent extends AgentsMdAgent_1.AgentsMdAgent {
40
6
  getIdentifier() {
41
7
  return 'amp';
42
8
  }
43
9
  getName() {
44
10
  return 'Amp';
45
11
  }
46
- async applyRulerConfig(concatenatedRules, projectRoot, rulerMcpJson, agentConfig) {
47
- const outputPath = agentConfig?.outputPath ?? this.getDefaultOutputPath(projectRoot);
48
- const absolutePath = path.resolve(projectRoot, outputPath);
49
- await (0, FileSystemUtils_1.writeGeneratedFile)(absolutePath, concatenatedRules);
50
- }
51
- getDefaultOutputPath(projectRoot) {
52
- return path.join(projectRoot, 'AGENT.md');
53
- }
54
12
  }
55
13
  exports.AmpAgent = AmpAgent;
@@ -36,7 +36,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.AugmentCodeAgent = void 0;
37
37
  const path = __importStar(require("path"));
38
38
  const FileSystemUtils_1 = require("../core/FileSystemUtils");
39
- const settings_1 = require("../vscode/settings");
40
39
  /**
41
40
  * AugmentCode agent adapter.
42
41
  * Generates ruler_augment_instructions.md configuration file and updates VSCode settings.json with MCP server configuration.
@@ -48,24 +47,23 @@ class AugmentCodeAgent {
48
47
  getName() {
49
48
  return 'AugmentCode';
50
49
  }
51
- async applyRulerConfig(concatenatedRules, projectRoot, rulerMcpJson, agentConfig) {
50
+ async applyRulerConfig(concatenatedRules, projectRoot, rulerMcpJson, // eslint-disable-line @typescript-eslint/no-unused-vars
51
+ agentConfig) {
52
52
  const output = agentConfig?.outputPath ?? this.getDefaultOutputPath(projectRoot);
53
53
  await (0, FileSystemUtils_1.backupFile)(output);
54
54
  await (0, FileSystemUtils_1.writeGeneratedFile)(output, concatenatedRules);
55
- if (rulerMcpJson) {
56
- const settingsPath = (0, settings_1.getVSCodeSettingsPath)(projectRoot);
57
- await (0, FileSystemUtils_1.backupFile)(settingsPath);
58
- const existingSettings = await (0, settings_1.readVSCodeSettings)(settingsPath);
59
- const augmentServers = (0, settings_1.transformRulerToAugmentMcp)(rulerMcpJson);
60
- const mergedSettings = (0, settings_1.mergeAugmentMcpServers)(existingSettings, augmentServers, agentConfig?.mcp?.strategy ?? 'merge');
61
- await (0, settings_1.writeVSCodeSettings)(settingsPath, mergedSettings);
62
- }
55
+ // AugmentCode does not support MCP servers
56
+ // MCP configuration is ignored for this agent
63
57
  }
64
58
  getDefaultOutputPath(projectRoot) {
65
59
  return path.join(projectRoot, '.augment', 'rules', 'ruler_augment_instructions.md');
66
60
  }
67
- getMcpServerKey() {
68
- return 'mcpServers';
61
+ // AugmentCode does not support MCP servers
62
+ supportsMcpStdio() {
63
+ return false;
64
+ }
65
+ supportsMcpRemote() {
66
+ return false;
69
67
  }
70
68
  }
71
69
  exports.AugmentCodeAgent = AugmentCodeAgent;
@@ -35,25 +35,25 @@ var __importStar = (this && this.__importStar) || (function () {
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.ClaudeAgent = void 0;
37
37
  const path = __importStar(require("path"));
38
- const FileSystemUtils_1 = require("../core/FileSystemUtils");
38
+ const AbstractAgent_1 = require("./AbstractAgent");
39
39
  /**
40
- * Claude Code agent adapter (stub implementation).
40
+ * Claude Code agent adapter.
41
41
  */
42
- class ClaudeAgent {
42
+ class ClaudeAgent extends AbstractAgent_1.AbstractAgent {
43
43
  getIdentifier() {
44
44
  return 'claude';
45
45
  }
46
46
  getName() {
47
47
  return 'Claude Code';
48
48
  }
49
- async applyRulerConfig(concatenatedRules, projectRoot, rulerMcpJson, // eslint-disable-line @typescript-eslint/no-unused-vars
50
- agentConfig) {
51
- const output = agentConfig?.outputPath ?? this.getDefaultOutputPath(projectRoot);
52
- await (0, FileSystemUtils_1.backupFile)(output);
53
- await (0, FileSystemUtils_1.writeGeneratedFile)(output, concatenatedRules);
54
- }
55
49
  getDefaultOutputPath(projectRoot) {
56
50
  return path.join(projectRoot, 'CLAUDE.md');
57
51
  }
52
+ supportsMcpStdio() {
53
+ return true;
54
+ }
55
+ supportsMcpRemote() {
56
+ return true;
57
+ }
58
58
  }
59
59
  exports.ClaudeAgent = ClaudeAgent;
@@ -35,23 +35,17 @@ var __importStar = (this && this.__importStar) || (function () {
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.ClineAgent = void 0;
37
37
  const path = __importStar(require("path"));
38
- const FileSystemUtils_1 = require("../core/FileSystemUtils");
38
+ const AbstractAgent_1 = require("./AbstractAgent");
39
39
  /**
40
- * Cline agent adapter (stub implementation).
40
+ * Cline agent adapter.
41
41
  */
42
- class ClineAgent {
42
+ class ClineAgent extends AbstractAgent_1.AbstractAgent {
43
43
  getIdentifier() {
44
44
  return 'cline';
45
45
  }
46
46
  getName() {
47
47
  return 'Cline';
48
48
  }
49
- async applyRulerConfig(concatenatedRules, projectRoot, rulerMcpJson, // eslint-disable-line @typescript-eslint/no-unused-vars
50
- agentConfig) {
51
- const output = agentConfig?.outputPath ?? this.getDefaultOutputPath(projectRoot);
52
- await (0, FileSystemUtils_1.backupFile)(output);
53
- await (0, FileSystemUtils_1.writeGeneratedFile)(output, concatenatedRules);
54
- }
55
49
  getDefaultOutputPath(projectRoot) {
56
50
  return path.join(projectRoot, '.clinerules');
57
51
  }
@@ -35,15 +35,16 @@ var __importStar = (this && this.__importStar) || (function () {
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.CodexCliAgent = void 0;
37
37
  const path = __importStar(require("path"));
38
- /* eslint-disable @typescript-eslint/no-explicit-any */
39
38
  const fs_1 = require("fs");
40
39
  const toml = __importStar(require("toml"));
41
40
  const toml_1 = require("@iarna/toml");
41
+ const AgentsMdAgent_1 = require("./AgentsMdAgent");
42
42
  const FileSystemUtils_1 = require("../core/FileSystemUtils");
43
+ const constants_1 = require("../constants");
43
44
  /**
44
45
  * OpenAI Codex CLI agent adapter.
45
46
  */
46
- class CodexCliAgent {
47
+ class CodexCliAgent extends AgentsMdAgent_1.AgentsMdAgent {
47
48
  getIdentifier() {
48
49
  return 'codex';
49
50
  }
@@ -51,16 +52,18 @@ class CodexCliAgent {
51
52
  return 'OpenAI Codex CLI';
52
53
  }
53
54
  async applyRulerConfig(concatenatedRules, projectRoot, rulerMcpJson, agentConfig) {
54
- // Get default paths
55
- const defaults = this.getDefaultOutputPath(projectRoot);
56
- // Determine the instructions file path
57
- const instructionsPath = agentConfig?.outputPath ??
58
- agentConfig?.outputPathInstructions ??
59
- defaults.instructions;
60
- // Write the instructions file
61
- await (0, FileSystemUtils_1.backupFile)(instructionsPath);
62
- await (0, FileSystemUtils_1.writeGeneratedFile)(instructionsPath, concatenatedRules);
63
- // Handle MCP configuration if enabled
55
+ // First perform idempotent AGENTS.md write via base class (instructions file).
56
+ await super.applyRulerConfig(concatenatedRules, projectRoot, null, {
57
+ // Preserve explicit outputPath precedence semantics if provided.
58
+ outputPath: agentConfig?.outputPath ||
59
+ agentConfig?.outputPathInstructions ||
60
+ undefined,
61
+ });
62
+ // Resolve config path helper (mirrors previous logic)
63
+ const defaults = {
64
+ instructions: path.join(projectRoot, constants_1.DEFAULT_RULES_FILENAME),
65
+ config: path.join(projectRoot, '.codex', 'config.toml'),
66
+ };
64
67
  const mcpEnabled = agentConfig?.mcp?.enabled ?? true;
65
68
  if (mcpEnabled && rulerMcpJson) {
66
69
  // Determine the config file path
@@ -95,13 +98,17 @@ class CodexCliAgent {
95
98
  // Create a properly formatted MCP server entry
96
99
  const mcpServer = {
97
100
  command: serverConfig.command,
98
- args: serverConfig.args,
99
101
  };
102
+ if (serverConfig.args) {
103
+ mcpServer.args = serverConfig.args;
104
+ }
100
105
  // Format env as an inline table
101
106
  if (serverConfig.env) {
102
107
  mcpServer.env = serverConfig.env;
103
108
  }
104
- updatedConfig.mcp_servers[serverName] = mcpServer;
109
+ if (updatedConfig.mcp_servers) {
110
+ updatedConfig.mcp_servers[serverName] = mcpServer;
111
+ }
105
112
  }
106
113
  // Convert to TOML with special handling for env to ensure it's an inline table
107
114
  let tomlContent = '';
@@ -114,8 +121,7 @@ class CodexCliAgent {
114
121
  // Now handle mcp_servers with special formatting for env
115
122
  if (updatedConfig.mcp_servers &&
116
123
  Object.keys(updatedConfig.mcp_servers).length > 0) {
117
- for (const [serverName, serverConfigRaw] of Object.entries(updatedConfig.mcp_servers)) {
118
- const serverConfig = serverConfigRaw;
124
+ for (const [serverName, serverConfig] of Object.entries(updatedConfig.mcp_servers)) {
119
125
  tomlContent += `\n[mcp_servers.${serverName}]\n`;
120
126
  // Add command
121
127
  if (serverConfig.command) {
@@ -146,11 +152,11 @@ class CodexCliAgent {
146
152
  await (0, FileSystemUtils_1.writeGeneratedFile)(configPath, tomlContent);
147
153
  }
148
154
  }
149
- getDefaultOutputPath(projectRoot) {
150
- return {
151
- instructions: path.join(projectRoot, 'AGENTS.md'),
152
- config: path.join(projectRoot, '.codex', 'config.toml'),
153
- };
155
+ supportsMcpStdio() {
156
+ return true;
157
+ }
158
+ supportsMcpRemote() {
159
+ return false; // Codex CLI only supports STDIO based on PR description
154
160
  }
155
161
  }
156
162
  exports.CodexCliAgent = CodexCliAgent;
@@ -35,29 +35,28 @@ var __importStar = (this && this.__importStar) || (function () {
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.CopilotAgent = void 0;
37
37
  const path = __importStar(require("path"));
38
- const FileSystemUtils_1 = require("../core/FileSystemUtils");
38
+ const AbstractAgent_1 = require("./AbstractAgent");
39
39
  /**
40
- * GitHub Copilot agent adapter (stub implementation).
40
+ * GitHub Copilot agent adapter.
41
41
  */
42
- class CopilotAgent {
42
+ class CopilotAgent extends AbstractAgent_1.AbstractAgent {
43
43
  getIdentifier() {
44
44
  return 'copilot';
45
45
  }
46
46
  getName() {
47
47
  return 'GitHub Copilot';
48
48
  }
49
- async applyRulerConfig(concatenatedRules, projectRoot, rulerMcpJson, // eslint-disable-line @typescript-eslint/no-unused-vars
50
- agentConfig) {
51
- const output = agentConfig?.outputPath ?? this.getDefaultOutputPath(projectRoot);
52
- await (0, FileSystemUtils_1.ensureDirExists)(path.dirname(output));
53
- await (0, FileSystemUtils_1.backupFile)(output);
54
- await (0, FileSystemUtils_1.writeGeneratedFile)(output, concatenatedRules);
55
- }
56
49
  getDefaultOutputPath(projectRoot) {
57
50
  return path.join(projectRoot, '.github', 'copilot-instructions.md');
58
51
  }
59
52
  getMcpServerKey() {
60
53
  return 'servers';
61
54
  }
55
+ supportsMcpStdio() {
56
+ return true;
57
+ }
58
+ supportsMcpRemote() {
59
+ return true;
60
+ }
62
61
  }
63
62
  exports.CopilotAgent = CopilotAgent;
@@ -84,5 +84,11 @@ class CrushAgent {
84
84
  await fs.writeFile(mcpPath, JSON.stringify(finalMcpConfig, null, 2));
85
85
  }
86
86
  }
87
+ supportsMcpStdio() {
88
+ return true;
89
+ }
90
+ supportsMcpRemote() {
91
+ return true;
92
+ }
87
93
  }
88
94
  exports.CrushAgent = CrushAgent;
@@ -35,11 +35,12 @@ var __importStar = (this && this.__importStar) || (function () {
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.CursorAgent = void 0;
37
37
  const path = __importStar(require("path"));
38
+ const AbstractAgent_1 = require("./AbstractAgent");
38
39
  const FileSystemUtils_1 = require("../core/FileSystemUtils");
39
40
  /**
40
- * Cursor agent adapter (stub implementation).
41
+ * Cursor agent adapter.
41
42
  */
42
- class CursorAgent {
43
+ class CursorAgent extends AbstractAgent_1.AbstractAgent {
43
44
  getIdentifier() {
44
45
  return 'cursor';
45
46
  }
@@ -49,16 +50,23 @@ class CursorAgent {
49
50
  async applyRulerConfig(concatenatedRules, projectRoot, rulerMcpJson, // eslint-disable-line @typescript-eslint/no-unused-vars
50
51
  agentConfig) {
51
52
  const output = agentConfig?.outputPath ?? this.getDefaultOutputPath(projectRoot);
53
+ const absolutePath = path.resolve(projectRoot, output);
52
54
  // Cursor expects a YAML front-matter block with an `alwaysApply` flag.
53
55
  // See: https://docs.cursor.com/context/rules#rule-anatomy
54
56
  const frontMatter = ['---', 'alwaysApply: true', '---', ''].join('\n');
55
57
  const content = `${frontMatter}${concatenatedRules.trimStart()}`;
56
- await (0, FileSystemUtils_1.ensureDirExists)(path.dirname(output));
57
- await (0, FileSystemUtils_1.backupFile)(output);
58
- await (0, FileSystemUtils_1.writeGeneratedFile)(output, content);
58
+ await (0, FileSystemUtils_1.ensureDirExists)(path.dirname(absolutePath));
59
+ await (0, FileSystemUtils_1.backupFile)(absolutePath);
60
+ await (0, FileSystemUtils_1.writeGeneratedFile)(absolutePath, content);
59
61
  }
60
62
  getDefaultOutputPath(projectRoot) {
61
63
  return path.join(projectRoot, '.cursor', 'rules', 'ruler_cursor_instructions.mdc');
62
64
  }
65
+ supportsMcpStdio() {
66
+ return true;
67
+ }
68
+ supportsMcpRemote() {
69
+ return true;
70
+ }
63
71
  }
64
72
  exports.CursorAgent = CursorAgent;
@@ -35,23 +35,17 @@ var __importStar = (this && this.__importStar) || (function () {
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.FirebaseAgent = void 0;
37
37
  const path = __importStar(require("path"));
38
- const FileSystemUtils_1 = require("../core/FileSystemUtils");
38
+ const AbstractAgent_1 = require("./AbstractAgent");
39
39
  /**
40
40
  * Firebase Studio agent adapter.
41
41
  */
42
- class FirebaseAgent {
42
+ class FirebaseAgent extends AbstractAgent_1.AbstractAgent {
43
43
  getIdentifier() {
44
44
  return 'firebase';
45
45
  }
46
46
  getName() {
47
47
  return 'Firebase Studio';
48
48
  }
49
- async applyRulerConfig(concatenatedRules, projectRoot, rulerMcpJson, // eslint-disable-line @typescript-eslint/no-unused-vars
50
- agentConfig) {
51
- const output = agentConfig?.outputPath ?? this.getDefaultOutputPath(projectRoot);
52
- await (0, FileSystemUtils_1.backupFile)(output);
53
- await (0, FileSystemUtils_1.writeGeneratedFile)(output, concatenatedRules);
54
- }
55
49
  getDefaultOutputPath(projectRoot) {
56
50
  return path.join(projectRoot, '.idx', 'airules.md');
57
51
  }
@@ -36,39 +36,48 @@ Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.GeminiCliAgent = void 0;
37
37
  const path = __importStar(require("path"));
38
38
  const fs_1 = require("fs");
39
- const merge_1 = require("../mcp/merge");
40
- class GeminiCliAgent {
39
+ const AgentsMdAgent_1 = require("./AgentsMdAgent");
40
+ class GeminiCliAgent extends AgentsMdAgent_1.AgentsMdAgent {
41
41
  getIdentifier() {
42
42
  return 'gemini-cli';
43
43
  }
44
44
  getName() {
45
45
  return 'Gemini CLI';
46
46
  }
47
- async applyRulerConfig(concatenatedRules, projectRoot, rulerMcpJson, agentConfig) {
48
- const outputPath = this.getDefaultOutputPath(projectRoot);
49
- await fs_1.promises.writeFile(outputPath, concatenatedRules);
50
- if (rulerMcpJson) {
51
- const settingsPath = path.join(projectRoot, '.gemini', 'settings.json');
52
- let existingSettings = {};
53
- try {
54
- const existingSettingsRaw = await fs_1.promises.readFile(settingsPath, 'utf8');
55
- existingSettings = JSON.parse(existingSettingsRaw);
56
- }
57
- catch (error) {
58
- if (error.code !== 'ENOENT') {
59
- throw error;
60
- }
47
+ async applyRulerConfig(concatenatedRules, projectRoot, rulerMcpJson, // eslint-disable-line @typescript-eslint/no-unused-vars
48
+ agentConfig) {
49
+ // First, perform idempotent write of AGENTS.md via base class
50
+ await super.applyRulerConfig(concatenatedRules, projectRoot, null, {
51
+ outputPath: agentConfig?.outputPath,
52
+ });
53
+ // Ensure .gemini/settings.json has contextFileName set to AGENTS.md
54
+ const settingsPath = path.join(projectRoot, '.gemini', 'settings.json');
55
+ let existingSettings = {};
56
+ try {
57
+ const raw = await fs_1.promises.readFile(settingsPath, 'utf8');
58
+ existingSettings = JSON.parse(raw);
59
+ }
60
+ catch (err) {
61
+ if (err.code !== 'ENOENT') {
62
+ throw err;
61
63
  }
62
- const merged = (0, merge_1.mergeMcp)(existingSettings, rulerMcpJson, agentConfig?.mcp?.strategy ?? 'merge', this.getMcpServerKey());
63
- await fs_1.promises.mkdir(path.dirname(settingsPath), { recursive: true });
64
- await fs_1.promises.writeFile(settingsPath, JSON.stringify(merged, null, 2));
65
64
  }
65
+ const updated = {
66
+ ...existingSettings,
67
+ contextFileName: 'AGENTS.md',
68
+ };
69
+ await fs_1.promises.mkdir(path.dirname(settingsPath), { recursive: true });
70
+ await fs_1.promises.writeFile(settingsPath, JSON.stringify(updated, null, 2));
66
71
  }
67
- getDefaultOutputPath(projectRoot) {
68
- return path.join(projectRoot, 'GEMINI.md');
69
- }
72
+ // Ensure MCP merging uses the correct key for Gemini (.gemini/settings.json)
70
73
  getMcpServerKey() {
71
74
  return 'mcpServers';
72
75
  }
76
+ supportsMcpStdio() {
77
+ return true;
78
+ }
79
+ supportsMcpRemote() {
80
+ return true;
81
+ }
73
82
  }
74
83
  exports.GeminiCliAgent = GeminiCliAgent;
@@ -35,26 +35,18 @@ var __importStar = (this && this.__importStar) || (function () {
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.GooseAgent = void 0;
37
37
  const path = __importStar(require("path"));
38
- const FileSystemUtils_1 = require("../core/FileSystemUtils");
38
+ const AbstractAgent_1 = require("./AbstractAgent");
39
39
  /**
40
40
  * Goose agent adapter for Block's Goose AI assistant.
41
41
  * Propagates rules to .goosehints file.
42
42
  */
43
- class GooseAgent {
43
+ class GooseAgent extends AbstractAgent_1.AbstractAgent {
44
44
  getIdentifier() {
45
45
  return 'goose';
46
46
  }
47
47
  getName() {
48
48
  return 'Goose';
49
49
  }
50
- async applyRulerConfig(concatenatedRules, projectRoot, rulerMcpJson, agentConfig) {
51
- // Get the output path for .goosehints
52
- const hintsPath = agentConfig?.outputPathInstructions ??
53
- this.getDefaultOutputPath(projectRoot);
54
- // Write rules to .goosehints
55
- await (0, FileSystemUtils_1.backupFile)(hintsPath);
56
- await (0, FileSystemUtils_1.writeGeneratedFile)(hintsPath, concatenatedRules);
57
- }
58
50
  getDefaultOutputPath(projectRoot) {
59
51
  return path.join(projectRoot, '.goosehints');
60
52
  }