@intellectronica/ruler 0.3.41 → 0.3.43

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 (105) hide show
  1. package/README.md +135 -36
  2. package/dist/agents/AbstractAgent.d.ts +53 -0
  3. package/dist/agents/AbstractAgent.js +3 -2
  4. package/dist/agents/AgentsMdAgent.d.ts +14 -0
  5. package/dist/agents/AgentsMdAgent.js +3 -2
  6. package/dist/agents/AiderAgent.d.ts +14 -0
  7. package/dist/agents/AiderAgent.js +7 -4
  8. package/dist/agents/AmazonQCliAgent.d.ts +13 -0
  9. package/dist/agents/AmazonQCliAgent.js +6 -4
  10. package/dist/agents/AmpAgent.d.ts +6 -0
  11. package/dist/agents/AntigravityAgent.d.ts +10 -0
  12. package/dist/agents/AugmentCodeAgent.d.ts +13 -0
  13. package/dist/agents/AugmentCodeAgent.js +3 -2
  14. package/dist/agents/ClaudeAgent.d.ts +13 -0
  15. package/dist/agents/ClineAgent.d.ts +9 -0
  16. package/dist/agents/CodexCliAgent.d.ts +31 -0
  17. package/dist/agents/CodexCliAgent.js +1 -1
  18. package/dist/agents/CopilotAgent.d.ts +20 -0
  19. package/dist/agents/CrushAgent.d.ts +14 -0
  20. package/dist/agents/CrushAgent.js +18 -6
  21. package/dist/agents/CursorAgent.d.ts +17 -0
  22. package/dist/agents/FactoryDroidAgent.d.ts +13 -0
  23. package/dist/agents/FirebaseAgent.d.ts +11 -0
  24. package/dist/agents/FirebenderAgent.d.ts +36 -0
  25. package/dist/agents/FirebenderAgent.js +5 -4
  26. package/dist/agents/GeminiCliAgent.d.ts +12 -0
  27. package/dist/agents/GeminiCliAgent.js +13 -7
  28. package/dist/agents/GooseAgent.d.ts +12 -0
  29. package/dist/agents/IAgent.d.ts +74 -0
  30. package/dist/agents/JetBrainsAiAssistantAgent.d.ts +10 -0
  31. package/dist/agents/JulesAgent.d.ts +5 -0
  32. package/dist/agents/JunieAgent.d.ts +12 -0
  33. package/dist/agents/KiloCodeAgent.d.ts +14 -0
  34. package/dist/agents/KiroAgent.d.ts +8 -0
  35. package/dist/agents/MistralVibeAgent.d.ts +31 -0
  36. package/dist/agents/MistralVibeAgent.js +14 -3
  37. package/dist/agents/OpenCodeAgent.d.ts +11 -0
  38. package/dist/agents/OpenCodeAgent.js +24 -12
  39. package/dist/agents/OpenHandsAgent.d.ts +8 -0
  40. package/dist/agents/PiAgent.d.ts +9 -0
  41. package/dist/agents/QwenCodeAgent.d.ts +11 -0
  42. package/dist/agents/QwenCodeAgent.js +11 -5
  43. package/dist/agents/RooCodeAgent.d.ts +16 -0
  44. package/dist/agents/RooCodeAgent.js +3 -2
  45. package/dist/agents/TraeAgent.d.ts +10 -0
  46. package/dist/agents/WarpAgent.d.ts +12 -0
  47. package/dist/agents/WindsurfAgent.d.ts +13 -0
  48. package/dist/agents/ZedAgent.d.ts +21 -0
  49. package/dist/agents/ZedAgent.js +8 -5
  50. package/dist/agents/agent-utils.d.ts +5 -0
  51. package/dist/agents/agent-utils.js +8 -5
  52. package/dist/agents/index.d.ts +9 -0
  53. package/dist/cli/commands.d.ts +4 -0
  54. package/dist/cli/commands.js +1 -2
  55. package/dist/cli/handlers.d.ts +41 -0
  56. package/dist/cli/handlers.js +75 -59
  57. package/dist/cli/index.d.ts +2 -0
  58. package/dist/constants.d.ts +35 -0
  59. package/dist/constants.js +1 -1
  60. package/dist/core/ConfigLoader.d.ts +59 -0
  61. package/dist/core/ConfigLoader.js +178 -44
  62. package/dist/core/FileSystemUtils.d.ts +53 -0
  63. package/dist/core/FileSystemUtils.js +157 -20
  64. package/dist/core/GitignoreUtils.d.ts +25 -0
  65. package/dist/core/GitignoreUtils.js +94 -32
  66. package/dist/core/RuleProcessor.d.ts +8 -0
  67. package/dist/core/SkillsProcessor.d.ts +127 -0
  68. package/dist/core/SkillsProcessor.js +118 -223
  69. package/dist/core/SkillsUtils.d.ts +26 -0
  70. package/dist/core/SubagentsProcessor.d.ts +38 -0
  71. package/dist/core/SubagentsProcessor.js +8 -5
  72. package/dist/core/SubagentsUtils.d.ts +34 -0
  73. package/dist/core/UnifiedConfigLoader.d.ts +10 -0
  74. package/dist/core/UnifiedConfigLoader.js +115 -33
  75. package/dist/core/UnifiedConfigTypes.d.ts +97 -0
  76. package/dist/core/agent-selection.d.ts +12 -0
  77. package/dist/core/agent-selection.js +17 -7
  78. package/dist/core/apply-engine.d.ts +70 -0
  79. package/dist/core/apply-engine.js +88 -58
  80. package/dist/core/config-utils.d.ts +14 -0
  81. package/dist/core/config-utils.js +9 -3
  82. package/dist/core/hash.d.ts +2 -0
  83. package/dist/core/path-utils.d.ts +1 -0
  84. package/dist/core/path-utils.js +42 -0
  85. package/dist/core/revert-engine.d.ts +37 -0
  86. package/dist/core/revert-engine.js +142 -34
  87. package/dist/lib.d.ts +13 -0
  88. package/dist/lib.js +24 -8
  89. package/dist/mcp/capabilities.d.ts +20 -0
  90. package/dist/mcp/merge.d.ts +10 -0
  91. package/dist/mcp/merge.js +36 -16
  92. package/dist/mcp/propagateOpenCodeMcp.d.ts +2 -0
  93. package/dist/mcp/propagateOpenCodeMcp.js +30 -11
  94. package/dist/mcp/propagateOpenHandsMcp.d.ts +2 -0
  95. package/dist/mcp/propagateOpenHandsMcp.js +48 -21
  96. package/dist/mcp/validate.d.ts +7 -0
  97. package/dist/mcp/validate.js +6 -1
  98. package/dist/paths/mcp.d.ts +8 -0
  99. package/dist/paths/mcp.js +44 -8
  100. package/dist/revert.d.ts +6 -0
  101. package/dist/revert.js +58 -46
  102. package/dist/types.d.ts +87 -0
  103. package/dist/vscode/settings.d.ts +40 -0
  104. package/dist/vscode/settings.js +3 -3
  105. package/package.json +8 -5
@@ -36,6 +36,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.CrushAgent = void 0;
37
37
  const fs = __importStar(require("fs/promises"));
38
38
  const path = __importStar(require("path"));
39
+ const FileSystemUtils_1 = require("../core/FileSystemUtils");
39
40
  class CrushAgent {
40
41
  getIdentifier() {
41
42
  return 'crush';
@@ -80,13 +81,20 @@ class CrushAgent {
80
81
  }
81
82
  return transformedServers;
82
83
  }
83
- async applyRulerConfig(concatenatedRules, projectRoot, rulerMcpJson, agentConfig) {
84
+ async applyRulerConfig(concatenatedRules, projectRoot, rulerMcpJson, agentConfig, backup = true) {
84
85
  const outputPaths = this.getDefaultOutputPath(projectRoot);
85
- const instructionsPath = agentConfig?.outputPathInstructions ?? outputPaths['instructions'];
86
- const mcpPath = agentConfig?.outputPathConfig ?? outputPaths['mcp'];
87
- await fs.writeFile(instructionsPath, concatenatedRules);
86
+ const instructionsPath = path.resolve(projectRoot, agentConfig?.outputPath ??
87
+ agentConfig?.outputPathInstructions ??
88
+ outputPaths['instructions']);
89
+ const mcpPath = path.resolve(projectRoot, agentConfig?.outputPathConfig ?? outputPaths['mcp']);
90
+ await fs.mkdir(path.dirname(instructionsPath), { recursive: true });
91
+ if (backup) {
92
+ await (0, FileSystemUtils_1.backupFile)(instructionsPath, projectRoot);
93
+ }
94
+ await (0, FileSystemUtils_1.writeGeneratedFile)(instructionsPath, concatenatedRules, projectRoot);
88
95
  // Always transform from mcpServers ({ mcpServers: ... }) to { mcp: ... } for Crush
89
96
  let finalMcpConfig = { mcp: {} };
97
+ const strategy = agentConfig?.mcp?.strategy ?? 'merge';
90
98
  try {
91
99
  const existingMcpConfig = JSON.parse(await fs.readFile(mcpPath, 'utf-8'));
92
100
  if (existingMcpConfig && typeof existingMcpConfig === 'object') {
@@ -94,7 +102,7 @@ class CrushAgent {
94
102
  finalMcpConfig = {
95
103
  ...existingMcpConfig,
96
104
  mcp: {
97
- ...(existingMcpConfig.mcp || {}),
105
+ ...(strategy === 'merge' ? existingMcpConfig.mcp || {} : {}),
98
106
  ...transformedServers,
99
107
  },
100
108
  };
@@ -115,7 +123,11 @@ class CrushAgent {
115
123
  }
116
124
  }
117
125
  if (Object.keys(finalMcpConfig.mcp).length > 0) {
118
- await fs.writeFile(mcpPath, JSON.stringify(finalMcpConfig, null, 2));
126
+ await fs.mkdir(path.dirname(mcpPath), { recursive: true });
127
+ if (backup) {
128
+ await (0, FileSystemUtils_1.backupFile)(mcpPath, projectRoot);
129
+ }
130
+ await (0, FileSystemUtils_1.writeGeneratedFile)(mcpPath, JSON.stringify(finalMcpConfig, null, 2), projectRoot);
119
131
  }
120
132
  }
121
133
  supportsMcpStdio() {
@@ -0,0 +1,17 @@
1
+ import { IAgentConfig } from './IAgent';
2
+ import { AgentsMdAgent } from './AgentsMdAgent';
3
+ /**
4
+ * Cursor agent adapter.
5
+ * Leverages the standardized AGENTS.md approach supported natively by Cursor.
6
+ * See: https://docs.cursor.com/en/cli/using
7
+ */
8
+ export declare class CursorAgent extends AgentsMdAgent {
9
+ getIdentifier(): string;
10
+ getName(): string;
11
+ applyRulerConfig(concatenatedRules: string, projectRoot: string, _rulerMcpJson: Record<string, unknown> | null, agentConfig?: IAgentConfig, backup?: boolean): Promise<void>;
12
+ getMcpServerKey(): string;
13
+ supportsMcpStdio(): boolean;
14
+ supportsMcpRemote(): boolean;
15
+ supportsNativeSkills(): boolean;
16
+ supportsNativeSubagents(): boolean;
17
+ }
@@ -0,0 +1,13 @@
1
+ import { AgentsMdAgent } from './AgentsMdAgent';
2
+ /**
3
+ * Factory Droid agent adapter.
4
+ * Uses the root-level AGENTS.md for instructions.
5
+ */
6
+ export declare class FactoryDroidAgent extends AgentsMdAgent {
7
+ getIdentifier(): string;
8
+ getName(): string;
9
+ getMcpServerKey(): string;
10
+ supportsMcpStdio(): boolean;
11
+ supportsMcpRemote(): boolean;
12
+ supportsNativeSkills(): boolean;
13
+ }
@@ -0,0 +1,11 @@
1
+ import { AbstractAgent } from './AbstractAgent';
2
+ /**
3
+ * Firebase Studio agent adapter.
4
+ */
5
+ export declare class FirebaseAgent extends AbstractAgent {
6
+ getIdentifier(): string;
7
+ getName(): string;
8
+ getDefaultOutputPath(projectRoot: string): string;
9
+ supportsMcpStdio(): boolean;
10
+ supportsMcpRemote(): boolean;
11
+ }
@@ -0,0 +1,36 @@
1
+ import { IAgent, IAgentConfig } from './IAgent';
2
+ /**
3
+ * Firebender agent adapter.
4
+ */
5
+ export declare class FirebenderAgent implements IAgent {
6
+ /**
7
+ * Type guard function to safely check if an object is a FirebenderRule.
8
+ */
9
+ private isFirebenderRule;
10
+ getIdentifier(): string;
11
+ getName(): string;
12
+ applyRulerConfig(concatenatedRules: string, projectRoot: string, rulerMcpJson: Record<string, unknown> | null, agentConfig?: IAgentConfig, backup?: boolean): Promise<void>;
13
+ private resolveOutputPath;
14
+ private loadExistingConfig;
15
+ private createRulesFromConcatenatedRules;
16
+ private createRuleObjectsFromFilePaths;
17
+ private createRulesFromPlainText;
18
+ private removeDuplicateRules;
19
+ private saveConfig;
20
+ /**
21
+ * Handle MCP server configuration for Firebender.
22
+ * Merges or overwrites MCP servers in the firebender.json configuration based on strategy.
23
+ */
24
+ private handleMcpConfiguration;
25
+ getDefaultOutputPath(projectRoot: string): Record<string, string>;
26
+ getMcpServerKey(): string;
27
+ supportsMcpStdio(): boolean;
28
+ supportsMcpRemote(): boolean;
29
+ /**
30
+ * Extracts file paths from concatenated rules by parsing HTML source comments.
31
+ * @param concatenatedRules The concatenated rules string with HTML comments
32
+ * @param projectRoot The project root directory
33
+ * @returns Array of file paths relative to project root
34
+ */
35
+ private extractFilePathsFromRules;
36
+ }
@@ -60,6 +60,7 @@ class FirebenderAgent {
60
60
  }
61
61
  async applyRulerConfig(concatenatedRules, projectRoot, rulerMcpJson, agentConfig, backup = true) {
62
62
  const rulesPath = this.resolveOutputPath(projectRoot, agentConfig);
63
+ await (0, FileSystemUtils_1.assertManagedPathInsideRoot)(rulesPath, projectRoot, 'Refusing to write generated file outside project');
63
64
  await (0, FileSystemUtils_1.ensureDirExists)(path.dirname(rulesPath));
64
65
  const firebenderConfig = await this.loadExistingConfig(rulesPath);
65
66
  const newRules = this.createRulesFromConcatenatedRules(concatenatedRules, projectRoot);
@@ -69,7 +70,7 @@ class FirebenderAgent {
69
70
  if (mcpEnabled && rulerMcpJson) {
70
71
  await this.handleMcpConfiguration(firebenderConfig, rulerMcpJson, agentConfig);
71
72
  }
72
- await this.saveConfig(rulesPath, firebenderConfig, backup);
73
+ await this.saveConfig(rulesPath, firebenderConfig, backup, projectRoot);
73
74
  }
74
75
  resolveOutputPath(projectRoot, agentConfig) {
75
76
  const outputPaths = this.getDefaultOutputPath(projectRoot);
@@ -135,12 +136,12 @@ class FirebenderAgent {
135
136
  return true;
136
137
  });
137
138
  }
138
- async saveConfig(rulesPath, config, backup) {
139
+ async saveConfig(rulesPath, config, backup, projectRoot) {
139
140
  const updatedContent = JSON.stringify(config, null, 2);
140
141
  if (backup) {
141
- await (0, FileSystemUtils_1.backupFile)(rulesPath);
142
+ await (0, FileSystemUtils_1.backupFile)(rulesPath, projectRoot);
142
143
  }
143
- await (0, FileSystemUtils_1.writeGeneratedFile)(rulesPath, updatedContent);
144
+ await (0, FileSystemUtils_1.writeGeneratedFile)(rulesPath, updatedContent, projectRoot);
144
145
  }
145
146
  /**
146
147
  * Handle MCP server configuration for Firebender.
@@ -0,0 +1,12 @@
1
+ import { IAgentConfig } from './IAgent';
2
+ import { AgentsMdAgent } from './AgentsMdAgent';
3
+ export declare class GeminiCliAgent extends AgentsMdAgent {
4
+ getIdentifier(): string;
5
+ getName(): string;
6
+ private getContextFileName;
7
+ applyRulerConfig(concatenatedRules: string, projectRoot: string, rulerMcpJson: Record<string, unknown> | null, agentConfig?: IAgentConfig, backup?: boolean): Promise<void>;
8
+ getMcpServerKey(): string;
9
+ supportsMcpStdio(): boolean;
10
+ supportsMcpRemote(): boolean;
11
+ supportsNativeSkills(): boolean;
12
+ }
@@ -37,6 +37,7 @@ exports.GeminiCliAgent = void 0;
37
37
  const path = __importStar(require("path"));
38
38
  const fs_1 = require("fs");
39
39
  const AgentsMdAgent_1 = require("./AgentsMdAgent");
40
+ const FileSystemUtils_1 = require("../core/FileSystemUtils");
40
41
  class GeminiCliAgent extends AgentsMdAgent_1.AgentsMdAgent {
41
42
  getIdentifier() {
42
43
  return 'gemini-cli';
@@ -44,13 +45,19 @@ class GeminiCliAgent extends AgentsMdAgent_1.AgentsMdAgent {
44
45
  getName() {
45
46
  return 'Gemini CLI';
46
47
  }
47
- async applyRulerConfig(concatenatedRules, projectRoot, rulerMcpJson, agentConfig) {
48
+ getContextFileName(projectRoot, agentConfig) {
49
+ const outputPath = agentConfig?.outputPath ?? 'AGENTS.md';
50
+ return path
51
+ .relative(projectRoot, path.resolve(projectRoot, outputPath))
52
+ .replace(/\\/g, '/');
53
+ }
54
+ async applyRulerConfig(concatenatedRules, projectRoot, rulerMcpJson, agentConfig, backup = true) {
48
55
  // First, perform idempotent write of AGENTS.md via base class
49
56
  await super.applyRulerConfig(concatenatedRules, projectRoot, null, {
50
57
  outputPath: agentConfig?.outputPath,
51
- });
52
- // Prepare .gemini/settings.json with contextFileName and MCP configuration
53
- const settingsPath = path.join(projectRoot, '.gemini', 'settings.json');
58
+ }, backup);
59
+ // Prepare settings with contextFileName and MCP configuration
60
+ const settingsPath = path.resolve(projectRoot, agentConfig?.outputPathConfig ?? path.join('.gemini', 'settings.json'));
54
61
  let existingSettings = {};
55
62
  try {
56
63
  const raw = await fs_1.promises.readFile(settingsPath, 'utf8');
@@ -63,7 +70,7 @@ class GeminiCliAgent extends AgentsMdAgent_1.AgentsMdAgent {
63
70
  }
64
71
  const updated = {
65
72
  ...existingSettings,
66
- contextFileName: 'AGENTS.md',
73
+ contextFileName: this.getContextFileName(projectRoot, agentConfig),
67
74
  };
68
75
  // Handle MCP server configuration if provided
69
76
  const mcpEnabled = agentConfig?.mcp?.enabled ?? true;
@@ -100,8 +107,7 @@ class GeminiCliAgent extends AgentsMdAgent_1.AgentsMdAgent {
100
107
  updated[this.getMcpServerKey()] = stripTypeField(mergedServers);
101
108
  }
102
109
  }
103
- await fs_1.promises.mkdir(path.dirname(settingsPath), { recursive: true });
104
- await fs_1.promises.writeFile(settingsPath, JSON.stringify(updated, null, 2));
110
+ await (0, FileSystemUtils_1.writeGeneratedFile)(settingsPath, JSON.stringify(updated, null, 2), projectRoot);
105
111
  }
106
112
  // Ensure MCP merging uses the correct key for Gemini (.gemini/settings.json)
107
113
  getMcpServerKey() {
@@ -0,0 +1,12 @@
1
+ import { AbstractAgent } from './AbstractAgent';
2
+ /**
3
+ * Goose agent adapter for Block's Goose AI assistant.
4
+ * Propagates rules to .goosehints file.
5
+ */
6
+ export declare class GooseAgent extends AbstractAgent {
7
+ getIdentifier(): string;
8
+ getName(): string;
9
+ getDefaultOutputPath(projectRoot: string): string;
10
+ getMcpServerKey(): string;
11
+ supportsNativeSkills(): boolean;
12
+ }
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Interface defining an AI agent configuration adapter.
3
+ */
4
+ import { McpConfig } from '../types';
5
+ /**
6
+ * Configuration overrides for a specific agent.
7
+ */
8
+ export interface IAgentConfig {
9
+ /** Explicit enable/disable agent */
10
+ enabled?: boolean;
11
+ /** Override for primary output path */
12
+ outputPath?: string;
13
+ /** Override for Aider instruction file path */
14
+ outputPathInstructions?: string;
15
+ /** Override for Aider config file path */
16
+ outputPathConfig?: string;
17
+ /** MCP propagation config for this agent. */
18
+ mcp?: McpConfig;
19
+ /** Agent-scoped MCP server definitions. */
20
+ mcpServers?: Record<string, Record<string, unknown>>;
21
+ }
22
+ export interface IAgent {
23
+ /**
24
+ * Returns the lowercase identifier of the agent (e.g., "copilot", "claude", "aider").
25
+ */
26
+ getIdentifier(): string;
27
+ /**
28
+ * Returns the display name of the agent.
29
+ */
30
+ getName(): string;
31
+ /**
32
+ * Applies the concatenated ruler rules to the agent's configuration.
33
+ * @param concatenatedRules The combined rules text
34
+ * @param projectRoot The root directory of the project
35
+ */
36
+ applyRulerConfig(concatenatedRules: string, projectRoot: string, rulerMcpJson: Record<string, unknown> | null, agentConfig?: IAgentConfig, backup?: boolean): Promise<void>;
37
+ /**
38
+ * Returns the default output path(s) for this agent given the project root.
39
+ */
40
+ getDefaultOutputPath(projectRoot: string): string | Record<string, string>;
41
+ /**
42
+ * Returns the specific key to be used for the server object in MCP JSON.
43
+ * Defaults to 'mcpServers' if not implemented.
44
+ */
45
+ getMcpServerKey?(): string;
46
+ /**
47
+ * Returns whether this agent supports MCP STDIO servers.
48
+ * Defaults to false if not implemented.
49
+ */
50
+ supportsMcpStdio?(): boolean;
51
+ /**
52
+ * Returns whether this agent supports MCP remote servers.
53
+ * Defaults to false if not implemented.
54
+ */
55
+ supportsMcpRemote?(): boolean;
56
+ /**
57
+ * Returns whether this agent supports MCP server timeout configuration.
58
+ * Defaults to false if not implemented.
59
+ */
60
+ supportsMcpTimeout?(): boolean;
61
+ /**
62
+ * Returns whether this agent has native skills support (like Claude Code).
63
+ * When true, skills are copied directly to the agent's skills directory.
64
+ * Defaults to false if not implemented.
65
+ */
66
+ supportsNativeSkills?(): boolean;
67
+ /**
68
+ * Returns whether this agent has native subagent support (like Claude Code,
69
+ * Cursor, Codex CLI, GitHub Copilot). When true, subagent definitions from
70
+ * `.ruler/agents/` are propagated to the agent's native subagent location.
71
+ * Defaults to false if not implemented.
72
+ */
73
+ supportsNativeSubagents?(): boolean;
74
+ }
@@ -0,0 +1,10 @@
1
+ import { AbstractAgent } from './AbstractAgent';
2
+ /**
3
+ * JetBrains AI Assistant agent adapter.
4
+ * Writes rules to .aiassistant/rules/AGENTS.md.
5
+ */
6
+ export declare class JetBrainsAiAssistantAgent extends AbstractAgent {
7
+ getIdentifier(): string;
8
+ getName(): string;
9
+ getDefaultOutputPath(projectRoot: string): string;
10
+ }
@@ -0,0 +1,5 @@
1
+ import { AgentsMdAgent } from './AgentsMdAgent';
2
+ export declare class JulesAgent extends AgentsMdAgent {
3
+ getIdentifier(): string;
4
+ getName(): string;
5
+ }
@@ -0,0 +1,12 @@
1
+ import { AbstractAgent } from './AbstractAgent';
2
+ /**
3
+ * JetBrains Junie agent adapter.
4
+ */
5
+ export declare class JunieAgent extends AbstractAgent {
6
+ getIdentifier(): string;
7
+ getName(): string;
8
+ getDefaultOutputPath(projectRoot: string): string;
9
+ supportsMcpStdio(): boolean;
10
+ supportsMcpRemote(): boolean;
11
+ supportsNativeSkills(): boolean;
12
+ }
@@ -0,0 +1,14 @@
1
+ import { AgentsMdAgent } from './AgentsMdAgent';
2
+ /**
3
+ * Kilo Code agent adapter.
4
+ * Uses AGENTS.md for instructions and .kilocode/mcp.json for MCP configuration.
5
+ */
6
+ export declare class KiloCodeAgent extends AgentsMdAgent {
7
+ getIdentifier(): string;
8
+ getName(): string;
9
+ getDefaultOutputPath(projectRoot: string): string;
10
+ getMcpServerKey(): string;
11
+ supportsMcpStdio(): boolean;
12
+ supportsMcpRemote(): boolean;
13
+ supportsNativeSkills(): boolean;
14
+ }
@@ -0,0 +1,8 @@
1
+ import { AbstractAgent } from './AbstractAgent';
2
+ export declare class KiroAgent extends AbstractAgent {
3
+ getIdentifier(): string;
4
+ getName(): string;
5
+ getDefaultOutputPath(projectRoot: string): string;
6
+ supportsMcpStdio(): boolean;
7
+ supportsMcpRemote(): boolean;
8
+ }
@@ -0,0 +1,31 @@
1
+ import { IAgent, IAgentConfig } from './IAgent';
2
+ interface RulerMcpServer {
3
+ command?: string;
4
+ args?: string[];
5
+ url?: string;
6
+ headers?: Record<string, string>;
7
+ env?: Record<string, string>;
8
+ timeout?: number;
9
+ }
10
+ interface RulerMcp {
11
+ mcpServers?: Record<string, RulerMcpServer>;
12
+ }
13
+ /**
14
+ * Mistral Vibe CLI agent adapter.
15
+ * Propagates rules to AGENTS.md and MCP servers to .vibe/config.toml.
16
+ */
17
+ export declare class MistralVibeAgent implements IAgent {
18
+ private agentsMdAgent;
19
+ getIdentifier(): string;
20
+ getName(): string;
21
+ applyRulerConfig(concatenatedRules: string, projectRoot: string, rulerMcpJson: RulerMcp | null, agentConfig?: IAgentConfig, backup?: boolean): Promise<void>;
22
+ /**
23
+ * Determines the transport type based on server configuration.
24
+ */
25
+ private determineTransport;
26
+ getDefaultOutputPath(projectRoot: string): Record<string, string>;
27
+ supportsMcpStdio(): boolean;
28
+ supportsMcpRemote(): boolean;
29
+ supportsNativeSkills(): boolean;
30
+ }
31
+ export {};
@@ -109,12 +109,20 @@ class MistralVibeAgent {
109
109
  }
110
110
  // Read existing TOML config if it exists
111
111
  let existingConfig = {};
112
+ let configExists = false;
112
113
  try {
113
114
  const existingContent = await fs_1.promises.readFile(configPath, 'utf8');
115
+ configExists = true;
114
116
  existingConfig = (0, toml_1.parse)(existingContent);
115
117
  }
116
- catch {
117
- // File doesn't exist or can't be parsed, use empty config
118
+ catch (error) {
119
+ if (error.code === 'ENOENT') {
120
+ // Missing config starts from an empty config.
121
+ existingConfig = {};
122
+ }
123
+ else {
124
+ throw new Error(`Invalid Mistral config at ${configPath}: ${error instanceof Error ? error.message : String(error)}`);
125
+ }
118
126
  }
119
127
  // Create the updated config
120
128
  const updatedConfig = { ...existingConfig };
@@ -134,7 +142,10 @@ class MistralVibeAgent {
134
142
  // Convert to TOML and write
135
143
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
136
144
  const tomlContent = (0, toml_1.stringify)(updatedConfig);
137
- await (0, FileSystemUtils_1.writeGeneratedFile)(configPath, tomlContent);
145
+ if (configExists && backup) {
146
+ await (0, FileSystemUtils_1.backupFile)(configPath, projectRoot);
147
+ }
148
+ await (0, FileSystemUtils_1.writeGeneratedFile)(configPath, tomlContent, projectRoot);
138
149
  }
139
150
  }
140
151
  /**
@@ -0,0 +1,11 @@
1
+ import { IAgent, IAgentConfig } from './IAgent';
2
+ export declare class OpenCodeAgent implements IAgent {
3
+ getIdentifier(): string;
4
+ getName(): string;
5
+ getDefaultOutputPath(projectRoot: string): Record<string, string>;
6
+ applyRulerConfig(concatenatedRules: string, projectRoot: string, rulerMcpJson: Record<string, unknown> | null, agentConfig?: IAgentConfig, backup?: boolean): Promise<void>;
7
+ supportsMcpStdio(): boolean;
8
+ supportsMcpRemote(): boolean;
9
+ supportsMcpTimeout(): boolean;
10
+ supportsNativeSkills(): boolean;
11
+ }
@@ -36,6 +36,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.OpenCodeAgent = void 0;
37
37
  const fs = __importStar(require("fs/promises"));
38
38
  const path = __importStar(require("path"));
39
+ const FileSystemUtils_1 = require("../core/FileSystemUtils");
39
40
  class OpenCodeAgent {
40
41
  getIdentifier() {
41
42
  return 'opencode';
@@ -49,11 +50,20 @@ class OpenCodeAgent {
49
50
  mcp: path.join(projectRoot, 'opencode.json'),
50
51
  };
51
52
  }
52
- async applyRulerConfig(concatenatedRules, projectRoot, rulerMcpJson, agentConfig) {
53
+ async applyRulerConfig(concatenatedRules, projectRoot, rulerMcpJson, agentConfig, backup = true) {
53
54
  const outputPaths = this.getDefaultOutputPath(projectRoot);
54
- const instructionsPath = path.resolve(projectRoot, agentConfig?.outputPathInstructions ?? outputPaths['instructions']);
55
+ const instructionsPath = path.resolve(projectRoot, agentConfig?.outputPath ??
56
+ agentConfig?.outputPathInstructions ??
57
+ outputPaths['instructions']);
55
58
  const mcpPath = path.resolve(projectRoot, agentConfig?.outputPathConfig ?? outputPaths['mcp']);
56
- await fs.writeFile(instructionsPath, concatenatedRules);
59
+ await fs.mkdir(path.dirname(instructionsPath), { recursive: true });
60
+ if (backup) {
61
+ await (0, FileSystemUtils_1.backupFile)(instructionsPath, projectRoot);
62
+ }
63
+ await (0, FileSystemUtils_1.writeGeneratedFile)(instructionsPath, concatenatedRules, projectRoot);
64
+ if (!rulerMcpJson) {
65
+ return;
66
+ }
57
67
  // Create OpenCode config with schema and MCP configuration
58
68
  let finalMcpConfig = {
59
69
  $schema: 'https://opencode.ai/config.json',
@@ -71,23 +81,25 @@ class OpenCodeAgent {
71
81
  },
72
82
  };
73
83
  }
74
- else if (rulerMcpJson) {
84
+ else {
75
85
  finalMcpConfig = {
76
86
  $schema: 'https://opencode.ai/config.json',
77
- mcp: (rulerMcpJson?.mcpServers ?? {}),
87
+ mcp: (rulerMcpJson.mcpServers ?? {}),
78
88
  };
79
89
  }
80
90
  }
81
91
  catch {
82
- if (rulerMcpJson) {
83
- finalMcpConfig = {
84
- $schema: 'https://opencode.ai/config.json',
85
- mcp: (rulerMcpJson?.mcpServers ?? {}),
86
- };
87
- }
92
+ finalMcpConfig = {
93
+ $schema: 'https://opencode.ai/config.json',
94
+ mcp: (rulerMcpJson.mcpServers ?? {}),
95
+ };
88
96
  }
89
97
  // Always write the config file, even if MCP is empty
90
- await fs.writeFile(mcpPath, JSON.stringify(finalMcpConfig, null, 2));
98
+ await fs.mkdir(path.dirname(mcpPath), { recursive: true });
99
+ if (backup) {
100
+ await (0, FileSystemUtils_1.backupFile)(mcpPath, projectRoot);
101
+ }
102
+ await (0, FileSystemUtils_1.writeGeneratedFile)(mcpPath, JSON.stringify(finalMcpConfig, null, 2), projectRoot);
91
103
  }
92
104
  supportsMcpStdio() {
93
105
  return true;
@@ -0,0 +1,8 @@
1
+ import { AbstractAgent } from './AbstractAgent';
2
+ export declare class OpenHandsAgent extends AbstractAgent {
3
+ getIdentifier(): string;
4
+ getName(): string;
5
+ getDefaultOutputPath(projectRoot: string): string;
6
+ supportsMcpStdio(): boolean;
7
+ supportsMcpRemote(): boolean;
8
+ }
@@ -0,0 +1,9 @@
1
+ import { AgentsMdAgent } from './AgentsMdAgent';
2
+ /**
3
+ * Pi Coding Agent adapter.
4
+ */
5
+ export declare class PiAgent extends AgentsMdAgent {
6
+ getIdentifier(): string;
7
+ getName(): string;
8
+ supportsNativeSkills(): boolean;
9
+ }
@@ -0,0 +1,11 @@
1
+ import { IAgentConfig } from './IAgent';
2
+ import { AgentsMdAgent } from './AgentsMdAgent';
3
+ export declare class QwenCodeAgent extends AgentsMdAgent {
4
+ getIdentifier(): string;
5
+ getName(): string;
6
+ private getContextFileName;
7
+ applyRulerConfig(concatenatedRules: string, projectRoot: string, _rulerMcpJson: Record<string, unknown> | null, agentConfig?: IAgentConfig, backup?: boolean): Promise<void>;
8
+ getMcpServerKey(): string;
9
+ supportsMcpStdio(): boolean;
10
+ supportsMcpRemote(): boolean;
11
+ }
@@ -37,6 +37,7 @@ exports.QwenCodeAgent = void 0;
37
37
  const path = __importStar(require("path"));
38
38
  const fs_1 = require("fs");
39
39
  const AgentsMdAgent_1 = require("./AgentsMdAgent");
40
+ const FileSystemUtils_1 = require("../core/FileSystemUtils");
40
41
  class QwenCodeAgent extends AgentsMdAgent_1.AgentsMdAgent {
41
42
  getIdentifier() {
42
43
  return 'qwen';
@@ -44,11 +45,17 @@ class QwenCodeAgent extends AgentsMdAgent_1.AgentsMdAgent {
44
45
  getName() {
45
46
  return 'Qwen Code';
46
47
  }
47
- async applyRulerConfig(concatenatedRules, projectRoot, _rulerMcpJson, agentConfig) {
48
+ getContextFileName(projectRoot, agentConfig) {
49
+ const outputPath = agentConfig?.outputPath ?? 'AGENTS.md';
50
+ return path
51
+ .relative(projectRoot, path.resolve(projectRoot, outputPath))
52
+ .replace(/\\/g, '/');
53
+ }
54
+ async applyRulerConfig(concatenatedRules, projectRoot, _rulerMcpJson, agentConfig, backup = true) {
48
55
  // First, perform idempotent write of AGENTS.md via base class
49
56
  await super.applyRulerConfig(concatenatedRules, projectRoot, null, {
50
57
  outputPath: agentConfig?.outputPath,
51
- });
58
+ }, backup);
52
59
  // Ensure .qwen/settings.json has contextFileName set to AGENTS.md
53
60
  const settingsPath = path.join(projectRoot, '.qwen', 'settings.json');
54
61
  let existingSettings = {};
@@ -63,10 +70,9 @@ class QwenCodeAgent extends AgentsMdAgent_1.AgentsMdAgent {
63
70
  }
64
71
  const updated = {
65
72
  ...existingSettings,
66
- contextFileName: 'AGENTS.md',
73
+ contextFileName: this.getContextFileName(projectRoot, agentConfig),
67
74
  };
68
- await fs_1.promises.mkdir(path.dirname(settingsPath), { recursive: true });
69
- await fs_1.promises.writeFile(settingsPath, JSON.stringify(updated, null, 2));
75
+ await (0, FileSystemUtils_1.writeGeneratedFile)(settingsPath, JSON.stringify(updated, null, 2), projectRoot);
70
76
  }
71
77
  // Ensure MCP merging uses the correct key for Qwen Code (.qwen/settings.json)
72
78
  getMcpServerKey() {
@@ -0,0 +1,16 @@
1
+ import { IAgent, IAgentConfig } from './IAgent';
2
+ /**
3
+ * Agent for RooCode that writes to AGENTS.md and generates .roo/mcp.json
4
+ * with project-level MCP server configuration.
5
+ */
6
+ export declare class RooCodeAgent implements IAgent {
7
+ private agentsMdAgent;
8
+ getIdentifier(): string;
9
+ getName(): string;
10
+ getDefaultOutputPath(projectRoot: string): Record<string, string>;
11
+ applyRulerConfig(concatenatedRules: string, projectRoot: string, rulerMcpJson: Record<string, unknown> | null, agentConfig?: IAgentConfig, backup?: boolean): Promise<void>;
12
+ supportsMcpStdio(): boolean;
13
+ supportsMcpRemote(): boolean;
14
+ getMcpServerKey(): string;
15
+ supportsNativeSkills(): boolean;
16
+ }
@@ -69,6 +69,7 @@ class RooCodeAgent {
69
69
  // Now handle .roo/mcp.json configuration
70
70
  const outputPaths = this.getDefaultOutputPath(projectRoot);
71
71
  const mcpPath = path.resolve(projectRoot, agentConfig?.outputPathConfig ?? outputPaths['mcp']);
72
+ await (0, FileSystemUtils_1.assertManagedPathInsideRoot)(mcpPath, projectRoot, 'Refusing to write generated file outside project');
72
73
  await (0, FileSystemUtils_1.ensureDirExists)(path.dirname(mcpPath));
73
74
  // Create base structure with mcpServers
74
75
  let finalMcpConfig = {
@@ -122,9 +123,9 @@ class RooCodeAgent {
122
123
  }
123
124
  // Backup (only if file existed and backup is enabled) then write new content
124
125
  if (backup) {
125
- await (0, FileSystemUtils_1.backupFile)(mcpPath);
126
+ await (0, FileSystemUtils_1.backupFile)(mcpPath, projectRoot);
126
127
  }
127
- await (0, FileSystemUtils_1.writeGeneratedFile)(mcpPath, newContent);
128
+ await (0, FileSystemUtils_1.writeGeneratedFile)(mcpPath, newContent, projectRoot);
128
129
  }
129
130
  supportsMcpStdio() {
130
131
  return true;
@@ -0,0 +1,10 @@
1
+ import { AbstractAgent } from './AbstractAgent';
2
+ /**
3
+ * Trae AI agent adapter.
4
+ * Generates project_rules.md configuration file.
5
+ */
6
+ export declare class TraeAgent extends AbstractAgent {
7
+ getIdentifier(): string;
8
+ getName(): string;
9
+ getDefaultOutputPath(projectRoot: string): string;
10
+ }