skiller 0.4.3
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/LICENSE +21 -0
- package/README.md +989 -0
- package/dist/agents/AbstractAgent.js +92 -0
- package/dist/agents/AgentsMdAgent.js +85 -0
- package/dist/agents/AiderAgent.js +108 -0
- package/dist/agents/AmazonQCliAgent.js +103 -0
- package/dist/agents/AmpAgent.js +13 -0
- package/dist/agents/AugmentCodeAgent.js +70 -0
- package/dist/agents/ClaudeAgent.js +95 -0
- package/dist/agents/ClineAgent.js +53 -0
- package/dist/agents/CodexCliAgent.js +143 -0
- package/dist/agents/CopilotAgent.js +43 -0
- package/dist/agents/CrushAgent.js +128 -0
- package/dist/agents/CursorAgent.js +93 -0
- package/dist/agents/FirebaseAgent.js +61 -0
- package/dist/agents/FirebenderAgent.js +205 -0
- package/dist/agents/GeminiCliAgent.js +99 -0
- package/dist/agents/GooseAgent.js +58 -0
- package/dist/agents/IAgent.js +2 -0
- package/dist/agents/JulesAgent.js +14 -0
- package/dist/agents/JunieAgent.js +53 -0
- package/dist/agents/KiloCodeAgent.js +63 -0
- package/dist/agents/KiroAgent.js +50 -0
- package/dist/agents/OpenCodeAgent.js +99 -0
- package/dist/agents/OpenHandsAgent.js +56 -0
- package/dist/agents/QwenCodeAgent.js +82 -0
- package/dist/agents/RooCodeAgent.js +139 -0
- package/dist/agents/TraeAgent.js +54 -0
- package/dist/agents/WarpAgent.js +61 -0
- package/dist/agents/WindsurfAgent.js +27 -0
- package/dist/agents/ZedAgent.js +132 -0
- package/dist/agents/agent-utils.js +37 -0
- package/dist/agents/index.js +77 -0
- package/dist/cli/commands.js +136 -0
- package/dist/cli/handlers.js +221 -0
- package/dist/cli/index.js +5 -0
- package/dist/constants.js +58 -0
- package/dist/core/ConfigLoader.js +274 -0
- package/dist/core/FileSystemUtils.js +421 -0
- package/dist/core/FrontmatterParser.js +142 -0
- package/dist/core/GitignoreUtils.js +171 -0
- package/dist/core/RuleProcessor.js +60 -0
- package/dist/core/SkillsProcessor.js +528 -0
- package/dist/core/SkillsUtils.js +230 -0
- package/dist/core/UnifiedConfigLoader.js +432 -0
- package/dist/core/UnifiedConfigTypes.js +2 -0
- package/dist/core/agent-selection.js +52 -0
- package/dist/core/apply-engine.js +668 -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 +196 -0
- package/dist/mcp/capabilities.js +65 -0
- package/dist/mcp/merge.js +39 -0
- package/dist/mcp/propagateOpenCodeMcp.js +116 -0
- package/dist/mcp/propagateOpenHandsMcp.js +169 -0
- package/dist/mcp/validate.js +17 -0
- package/dist/paths/mcp.js +120 -0
- package/dist/revert.js +186 -0
- package/dist/types.js +2 -0
- package/dist/vscode/settings.js +117 -0
- package/package.json +77 -0
|
@@ -0,0 +1,143 @@
|
|
|
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.CodexCliAgent = void 0;
|
|
37
|
+
const path = __importStar(require("path"));
|
|
38
|
+
const fs_1 = require("fs");
|
|
39
|
+
const toml_1 = require("@iarna/toml");
|
|
40
|
+
const AgentsMdAgent_1 = require("./AgentsMdAgent");
|
|
41
|
+
const FileSystemUtils_1 = require("../core/FileSystemUtils");
|
|
42
|
+
const constants_1 = require("../constants");
|
|
43
|
+
/**
|
|
44
|
+
* OpenAI Codex CLI agent adapter.
|
|
45
|
+
*/
|
|
46
|
+
class CodexCliAgent {
|
|
47
|
+
constructor() {
|
|
48
|
+
this.agentsMdAgent = new AgentsMdAgent_1.AgentsMdAgent();
|
|
49
|
+
}
|
|
50
|
+
getIdentifier() {
|
|
51
|
+
return 'codex';
|
|
52
|
+
}
|
|
53
|
+
getName() {
|
|
54
|
+
return 'OpenAI Codex CLI';
|
|
55
|
+
}
|
|
56
|
+
async applyRulerConfig(concatenatedRules, projectRoot, rulerMcpJson, agentConfig, backup = true) {
|
|
57
|
+
// First perform idempotent AGENTS.md write via composed AgentsMdAgent
|
|
58
|
+
await this.agentsMdAgent.applyRulerConfig(concatenatedRules, projectRoot, null, {
|
|
59
|
+
// Preserve explicit outputPath precedence semantics if provided.
|
|
60
|
+
outputPath: agentConfig?.outputPath ||
|
|
61
|
+
agentConfig?.outputPathInstructions ||
|
|
62
|
+
undefined,
|
|
63
|
+
}, backup);
|
|
64
|
+
// Use proper path resolution from getDefaultOutputPath and agentConfig
|
|
65
|
+
const defaults = this.getDefaultOutputPath(projectRoot);
|
|
66
|
+
const mcpEnabled = agentConfig?.mcp?.enabled ?? true;
|
|
67
|
+
if (mcpEnabled && rulerMcpJson) {
|
|
68
|
+
// Apply MCP server filtering and transformation
|
|
69
|
+
const { filterMcpConfigForAgent } = await Promise.resolve().then(() => __importStar(require('../mcp/capabilities')));
|
|
70
|
+
const filteredMcpConfig = filterMcpConfigForAgent(rulerMcpJson, this);
|
|
71
|
+
if (!filteredMcpConfig) {
|
|
72
|
+
return; // No compatible servers found
|
|
73
|
+
}
|
|
74
|
+
const filteredRulerMcpJson = filteredMcpConfig;
|
|
75
|
+
// Determine the config file path using proper precedence
|
|
76
|
+
const configPath = agentConfig?.outputPathConfig ?? defaults.config;
|
|
77
|
+
// Ensure the parent directory exists
|
|
78
|
+
await fs_1.promises.mkdir(path.dirname(configPath), { recursive: true });
|
|
79
|
+
// Get the merge strategy
|
|
80
|
+
const strategy = agentConfig?.mcp?.strategy ?? 'merge';
|
|
81
|
+
// Extract MCP servers from filtered ruler config
|
|
82
|
+
const rulerServers = filteredRulerMcpJson.mcpServers || {};
|
|
83
|
+
// Read existing TOML config if it exists
|
|
84
|
+
let existingConfig = {};
|
|
85
|
+
try {
|
|
86
|
+
const existingContent = await fs_1.promises.readFile(configPath, 'utf8');
|
|
87
|
+
existingConfig = (0, toml_1.parse)(existingContent);
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
// File doesn't exist or can't be parsed, use empty config
|
|
91
|
+
}
|
|
92
|
+
// Create the updated config
|
|
93
|
+
const updatedConfig = { ...existingConfig };
|
|
94
|
+
// Initialize mcp_servers if it doesn't exist
|
|
95
|
+
if (!updatedConfig.mcp_servers) {
|
|
96
|
+
updatedConfig.mcp_servers = {};
|
|
97
|
+
}
|
|
98
|
+
if (strategy === 'overwrite') {
|
|
99
|
+
// For overwrite strategy, replace the entire mcp_servers section
|
|
100
|
+
updatedConfig.mcp_servers = {};
|
|
101
|
+
}
|
|
102
|
+
// Add the ruler servers
|
|
103
|
+
for (const [serverName, serverConfig] of Object.entries(rulerServers)) {
|
|
104
|
+
// Create a properly formatted MCP server entry
|
|
105
|
+
const mcpServer = {
|
|
106
|
+
command: serverConfig.command,
|
|
107
|
+
};
|
|
108
|
+
if (serverConfig.args) {
|
|
109
|
+
mcpServer.args = serverConfig.args;
|
|
110
|
+
}
|
|
111
|
+
// Format env as an inline table
|
|
112
|
+
if (serverConfig.env) {
|
|
113
|
+
mcpServer.env = serverConfig.env;
|
|
114
|
+
}
|
|
115
|
+
// Handle additional properties from remote server transformation
|
|
116
|
+
if (serverConfig.headers) {
|
|
117
|
+
mcpServer.headers = serverConfig.headers;
|
|
118
|
+
}
|
|
119
|
+
if (updatedConfig.mcp_servers) {
|
|
120
|
+
updatedConfig.mcp_servers[serverName] = mcpServer;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
// Convert to TOML using structured objects
|
|
124
|
+
const finalConfig = { ...updatedConfig };
|
|
125
|
+
// @iarna/toml should handle the formatting properly
|
|
126
|
+
const tomlContent = (0, toml_1.stringify)(finalConfig);
|
|
127
|
+
await (0, FileSystemUtils_1.writeGeneratedFile)(configPath, tomlContent);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
getDefaultOutputPath(projectRoot) {
|
|
131
|
+
return {
|
|
132
|
+
instructions: path.join(projectRoot, constants_1.DEFAULT_RULES_FILENAME),
|
|
133
|
+
config: path.join(projectRoot, '.codex', 'config.toml'),
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
supportsMcpStdio() {
|
|
137
|
+
return true;
|
|
138
|
+
}
|
|
139
|
+
supportsMcpRemote() {
|
|
140
|
+
return false; // Codex CLI only supports STDIO based on PR description
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
exports.CodexCliAgent = CodexCliAgent;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CopilotAgent = void 0;
|
|
4
|
+
const AgentsMdAgent_1 = require("./AgentsMdAgent");
|
|
5
|
+
/**
|
|
6
|
+
* GitHub Copilot agent adapter.
|
|
7
|
+
* Writes to AGENTS.md for both web-based GitHub Copilot and VS Code extension.
|
|
8
|
+
*/
|
|
9
|
+
class CopilotAgent {
|
|
10
|
+
constructor() {
|
|
11
|
+
this.agentsMdAgent = new AgentsMdAgent_1.AgentsMdAgent();
|
|
12
|
+
}
|
|
13
|
+
getIdentifier() {
|
|
14
|
+
return 'copilot';
|
|
15
|
+
}
|
|
16
|
+
getName() {
|
|
17
|
+
return 'GitHub Copilot';
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Returns the default output path for AGENTS.md.
|
|
21
|
+
*/
|
|
22
|
+
getDefaultOutputPath(projectRoot) {
|
|
23
|
+
return this.agentsMdAgent.getDefaultOutputPath(projectRoot);
|
|
24
|
+
}
|
|
25
|
+
async applyRulerConfig(concatenatedRules, projectRoot, rulerMcpJson, agentConfig, backup = true) {
|
|
26
|
+
// Write to AGENTS.md using the existing AgentsMdAgent infrastructure
|
|
27
|
+
await this.agentsMdAgent.applyRulerConfig(concatenatedRules, projectRoot, null, // No MCP config needed for the instructions file
|
|
28
|
+
{
|
|
29
|
+
// Preserve explicit outputPath precedence semantics if provided
|
|
30
|
+
outputPath: agentConfig?.outputPath || agentConfig?.outputPathInstructions,
|
|
31
|
+
}, backup);
|
|
32
|
+
}
|
|
33
|
+
getMcpServerKey() {
|
|
34
|
+
return 'servers';
|
|
35
|
+
}
|
|
36
|
+
supportsMcpStdio() {
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
supportsMcpRemote() {
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
exports.CopilotAgent = CopilotAgent;
|
|
@@ -0,0 +1,128 @@
|
|
|
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.CrushAgent = void 0;
|
|
37
|
+
const fs = __importStar(require("fs/promises"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
class CrushAgent {
|
|
40
|
+
getIdentifier() {
|
|
41
|
+
return 'crush';
|
|
42
|
+
}
|
|
43
|
+
getName() {
|
|
44
|
+
return 'Crush';
|
|
45
|
+
}
|
|
46
|
+
getDefaultOutputPath(projectRoot) {
|
|
47
|
+
return {
|
|
48
|
+
instructions: path.join(projectRoot, 'CRUSH.md'),
|
|
49
|
+
mcp: path.join(projectRoot, '.crush.json'),
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Transform MCP server types for Crush compatibility.
|
|
54
|
+
* Crush expects "http" for HTTP servers and "sse" for SSE servers, not "remote".
|
|
55
|
+
*/
|
|
56
|
+
transformMcpServersForCrush(mcpServers) {
|
|
57
|
+
const transformedServers = {};
|
|
58
|
+
for (const [name, serverDef] of Object.entries(mcpServers)) {
|
|
59
|
+
if (serverDef && typeof serverDef === 'object') {
|
|
60
|
+
const server = serverDef;
|
|
61
|
+
const transformedServer = { ...server };
|
|
62
|
+
// Transform type: "remote" to appropriate Crush types
|
|
63
|
+
if (server.type === 'remote' &&
|
|
64
|
+
server.url &&
|
|
65
|
+
typeof server.url === 'string') {
|
|
66
|
+
const url = server.url;
|
|
67
|
+
// Check if URL suggests SSE (contains /sse path segment)
|
|
68
|
+
if (/\/sse(\/|$)/i.test(url)) {
|
|
69
|
+
transformedServer.type = 'sse';
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
transformedServer.type = 'http';
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
transformedServers[name] = transformedServer;
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
transformedServers[name] = serverDef;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return transformedServers;
|
|
82
|
+
}
|
|
83
|
+
async applyRulerConfig(concatenatedRules, projectRoot, rulerMcpJson, agentConfig) {
|
|
84
|
+
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);
|
|
88
|
+
// Always transform from mcpServers ({ mcpServers: ... }) to { mcp: ... } for Crush
|
|
89
|
+
let finalMcpConfig = { mcp: {} };
|
|
90
|
+
try {
|
|
91
|
+
const existingMcpConfig = JSON.parse(await fs.readFile(mcpPath, 'utf-8'));
|
|
92
|
+
if (existingMcpConfig && typeof existingMcpConfig === 'object') {
|
|
93
|
+
const transformedServers = this.transformMcpServersForCrush((rulerMcpJson?.mcpServers ?? {}));
|
|
94
|
+
finalMcpConfig = {
|
|
95
|
+
...existingMcpConfig,
|
|
96
|
+
mcp: {
|
|
97
|
+
...(existingMcpConfig.mcp || {}),
|
|
98
|
+
...transformedServers,
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
else if (rulerMcpJson) {
|
|
103
|
+
const transformedServers = this.transformMcpServersForCrush((rulerMcpJson?.mcpServers ?? {}));
|
|
104
|
+
finalMcpConfig = {
|
|
105
|
+
mcp: transformedServers,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
if (rulerMcpJson) {
|
|
111
|
+
const transformedServers = this.transformMcpServersForCrush((rulerMcpJson?.mcpServers ?? {}));
|
|
112
|
+
finalMcpConfig = {
|
|
113
|
+
mcp: transformedServers,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
if (Object.keys(finalMcpConfig.mcp).length > 0) {
|
|
118
|
+
await fs.writeFile(mcpPath, JSON.stringify(finalMcpConfig, null, 2));
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
supportsMcpStdio() {
|
|
122
|
+
return true;
|
|
123
|
+
}
|
|
124
|
+
supportsMcpRemote() {
|
|
125
|
+
return true;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
exports.CrushAgent = CrushAgent;
|
|
@@ -0,0 +1,93 @@
|
|
|
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.CursorAgent = void 0;
|
|
37
|
+
const path = __importStar(require("path"));
|
|
38
|
+
const fs = __importStar(require("fs/promises"));
|
|
39
|
+
const AgentsMdAgent_1 = require("./AgentsMdAgent");
|
|
40
|
+
const SkillsUtils_1 = require("../core/SkillsUtils");
|
|
41
|
+
/**
|
|
42
|
+
* Cursor agent adapter.
|
|
43
|
+
* Leverages the standardized AGENTS.md approach supported natively by Cursor.
|
|
44
|
+
* See: https://docs.cursor.com/en/cli/using
|
|
45
|
+
*/
|
|
46
|
+
class CursorAgent extends AgentsMdAgent_1.AgentsMdAgent {
|
|
47
|
+
getIdentifier() {
|
|
48
|
+
return 'cursor';
|
|
49
|
+
}
|
|
50
|
+
getName() {
|
|
51
|
+
return 'Cursor';
|
|
52
|
+
}
|
|
53
|
+
async applyRulerConfig(concatenatedRules, projectRoot, _rulerMcpJson, agentConfig, backup = true, _ruleFiles, rulerDir, mergeStrategy) {
|
|
54
|
+
// Write AGENTS.md via base class
|
|
55
|
+
// Cursor natively reads AGENTS.md from the project root
|
|
56
|
+
await super.applyRulerConfig(concatenatedRules, projectRoot, null, {
|
|
57
|
+
outputPath: agentConfig?.outputPath,
|
|
58
|
+
}, backup);
|
|
59
|
+
// Copy .claude/rules to .cursor/rules when using cursor merge strategy
|
|
60
|
+
if (mergeStrategy === 'cursor' && rulerDir) {
|
|
61
|
+
const rulerDirName = path.basename(rulerDir);
|
|
62
|
+
if (rulerDirName === '.claude') {
|
|
63
|
+
const sourceRulesDir = path.join(rulerDir, 'rules');
|
|
64
|
+
const targetRulesDir = path.join(projectRoot, '.cursor', 'rules');
|
|
65
|
+
// Check if source rules directory exists
|
|
66
|
+
try {
|
|
67
|
+
await fs.access(sourceRulesDir);
|
|
68
|
+
// Copy the entire rules directory
|
|
69
|
+
await fs.mkdir(path.dirname(targetRulesDir), { recursive: true });
|
|
70
|
+
await fs.rm(targetRulesDir, { recursive: true, force: true });
|
|
71
|
+
await (0, SkillsUtils_1.copySkillsDirectory)(sourceRulesDir, targetRulesDir);
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
// Source rules directory doesn't exist, skip copying
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
getMcpServerKey() {
|
|
80
|
+
return 'mcpServers';
|
|
81
|
+
}
|
|
82
|
+
supportsMcpStdio() {
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
supportsMcpRemote() {
|
|
86
|
+
return true;
|
|
87
|
+
}
|
|
88
|
+
supportsNativeSkills() {
|
|
89
|
+
// Cursor has native support for rules via .cursor/rules/
|
|
90
|
+
return true;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
exports.CursorAgent = CursorAgent;
|
|
@@ -0,0 +1,61 @@
|
|
|
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.FirebaseAgent = void 0;
|
|
37
|
+
const path = __importStar(require("path"));
|
|
38
|
+
const AbstractAgent_1 = require("./AbstractAgent");
|
|
39
|
+
/**
|
|
40
|
+
* Firebase Studio agent adapter.
|
|
41
|
+
*/
|
|
42
|
+
class FirebaseAgent extends AbstractAgent_1.AbstractAgent {
|
|
43
|
+
getIdentifier() {
|
|
44
|
+
return 'firebase';
|
|
45
|
+
}
|
|
46
|
+
getName() {
|
|
47
|
+
return 'Firebase Studio';
|
|
48
|
+
}
|
|
49
|
+
getDefaultOutputPath(projectRoot) {
|
|
50
|
+
return path.join(projectRoot, '.idx', 'airules.md');
|
|
51
|
+
}
|
|
52
|
+
// Firebase Studio (IDX) supports stdio MCP servers via .idx/mcp.json
|
|
53
|
+
supportsMcpStdio() {
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
// Remote MCP over HTTP/SSE is not documented for Firebase Studio yet
|
|
57
|
+
supportsMcpRemote() {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
exports.FirebaseAgent = FirebaseAgent;
|
|
@@ -0,0 +1,205 @@
|
|
|
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.FirebenderAgent = void 0;
|
|
37
|
+
const path = __importStar(require("path"));
|
|
38
|
+
const fs = __importStar(require("fs"));
|
|
39
|
+
const FileSystemUtils_1 = require("../core/FileSystemUtils");
|
|
40
|
+
/**
|
|
41
|
+
* Firebender agent adapter.
|
|
42
|
+
*/
|
|
43
|
+
class FirebenderAgent {
|
|
44
|
+
/**
|
|
45
|
+
* Type guard function to safely check if an object is a FirebenderRule.
|
|
46
|
+
*/
|
|
47
|
+
isFirebenderRule(rule) {
|
|
48
|
+
return (typeof rule === 'object' &&
|
|
49
|
+
rule !== null &&
|
|
50
|
+
'filePathMatches' in rule &&
|
|
51
|
+
'rulesPaths' in rule &&
|
|
52
|
+
typeof rule.filePathMatches === 'string' &&
|
|
53
|
+
typeof rule.rulesPaths === 'string');
|
|
54
|
+
}
|
|
55
|
+
getIdentifier() {
|
|
56
|
+
return 'firebender';
|
|
57
|
+
}
|
|
58
|
+
getName() {
|
|
59
|
+
return 'Firebender';
|
|
60
|
+
}
|
|
61
|
+
async applyRulerConfig(concatenatedRules, projectRoot, rulerMcpJson, agentConfig, backup = true) {
|
|
62
|
+
const rulesPath = this.resolveOutputPath(projectRoot, agentConfig);
|
|
63
|
+
await (0, FileSystemUtils_1.ensureDirExists)(path.dirname(rulesPath));
|
|
64
|
+
const firebenderConfig = await this.loadExistingConfig(rulesPath);
|
|
65
|
+
const newRules = this.createRulesFromConcatenatedRules(concatenatedRules, projectRoot);
|
|
66
|
+
firebenderConfig.rules.push(...newRules);
|
|
67
|
+
this.removeDuplicateRules(firebenderConfig);
|
|
68
|
+
const mcpEnabled = agentConfig?.mcp?.enabled ?? true;
|
|
69
|
+
if (mcpEnabled && rulerMcpJson) {
|
|
70
|
+
await this.handleMcpConfiguration(firebenderConfig, rulerMcpJson, agentConfig);
|
|
71
|
+
}
|
|
72
|
+
await this.saveConfig(rulesPath, firebenderConfig, backup);
|
|
73
|
+
}
|
|
74
|
+
resolveOutputPath(projectRoot, agentConfig) {
|
|
75
|
+
const outputPaths = this.getDefaultOutputPath(projectRoot);
|
|
76
|
+
const output = agentConfig?.outputPath ??
|
|
77
|
+
agentConfig?.outputPathInstructions ??
|
|
78
|
+
outputPaths['instructions'];
|
|
79
|
+
return path.resolve(projectRoot, output);
|
|
80
|
+
}
|
|
81
|
+
async loadExistingConfig(rulesPath) {
|
|
82
|
+
try {
|
|
83
|
+
const existingContent = await fs.promises.readFile(rulesPath, 'utf8');
|
|
84
|
+
const config = JSON.parse(existingContent);
|
|
85
|
+
if (!config.rules) {
|
|
86
|
+
config.rules = [];
|
|
87
|
+
}
|
|
88
|
+
return config;
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
if (error &&
|
|
92
|
+
typeof error === 'object' &&
|
|
93
|
+
'code' in error &&
|
|
94
|
+
error.code === 'ENOENT') {
|
|
95
|
+
return { rules: [] };
|
|
96
|
+
}
|
|
97
|
+
console.warn(`Failed to read/parse existing firebender.json: ${error}`);
|
|
98
|
+
return { rules: [] };
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
createRulesFromConcatenatedRules(concatenatedRules, projectRoot) {
|
|
102
|
+
const filePaths = this.extractFilePathsFromRules(concatenatedRules, projectRoot);
|
|
103
|
+
if (filePaths.length > 0) {
|
|
104
|
+
return this.createRuleObjectsFromFilePaths(filePaths);
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
return this.createRulesFromPlainText(concatenatedRules);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
createRuleObjectsFromFilePaths(filePaths) {
|
|
111
|
+
return filePaths.map((filePath) => ({
|
|
112
|
+
filePathMatches: '**/*',
|
|
113
|
+
rulesPaths: filePath,
|
|
114
|
+
}));
|
|
115
|
+
}
|
|
116
|
+
createRulesFromPlainText(concatenatedRules) {
|
|
117
|
+
return concatenatedRules.split('\n').filter((rule) => rule.trim());
|
|
118
|
+
}
|
|
119
|
+
removeDuplicateRules(firebenderConfig) {
|
|
120
|
+
const seen = new Set();
|
|
121
|
+
firebenderConfig.rules = firebenderConfig.rules.filter((rule) => {
|
|
122
|
+
let key;
|
|
123
|
+
if (this.isFirebenderRule(rule)) {
|
|
124
|
+
const filePathMatchesPart = rule.filePathMatches;
|
|
125
|
+
const rulesPathsPart = rule.rulesPaths;
|
|
126
|
+
key = `${filePathMatchesPart}::${rulesPathsPart}`;
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
key = String(rule);
|
|
130
|
+
}
|
|
131
|
+
if (seen.has(key)) {
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
seen.add(key);
|
|
135
|
+
return true;
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
async saveConfig(rulesPath, config, backup) {
|
|
139
|
+
const updatedContent = JSON.stringify(config, null, 2);
|
|
140
|
+
if (backup) {
|
|
141
|
+
await (0, FileSystemUtils_1.backupFile)(rulesPath);
|
|
142
|
+
}
|
|
143
|
+
await (0, FileSystemUtils_1.writeGeneratedFile)(rulesPath, updatedContent);
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Handle MCP server configuration for Firebender.
|
|
147
|
+
* Merges or overwrites MCP servers in the firebender.json configuration based on strategy.
|
|
148
|
+
*/
|
|
149
|
+
async handleMcpConfiguration(firebenderConfig, rulerMcpJson, agentConfig) {
|
|
150
|
+
const strategy = agentConfig?.mcp?.strategy ?? 'merge';
|
|
151
|
+
const incomingServers = rulerMcpJson.mcpServers || {};
|
|
152
|
+
if (!firebenderConfig.mcpServers) {
|
|
153
|
+
firebenderConfig.mcpServers = {};
|
|
154
|
+
}
|
|
155
|
+
if (strategy === 'overwrite') {
|
|
156
|
+
firebenderConfig.mcpServers = { ...incomingServers };
|
|
157
|
+
}
|
|
158
|
+
else if (strategy === 'merge') {
|
|
159
|
+
const existingServers = firebenderConfig.mcpServers || {};
|
|
160
|
+
firebenderConfig.mcpServers = { ...existingServers, ...incomingServers };
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
getDefaultOutputPath(projectRoot) {
|
|
164
|
+
return {
|
|
165
|
+
instructions: path.join(projectRoot, 'firebender.json'),
|
|
166
|
+
mcp: path.join(projectRoot, 'firebender.json'),
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
getMcpServerKey() {
|
|
170
|
+
return 'mcpServers';
|
|
171
|
+
}
|
|
172
|
+
supportsMcpStdio() {
|
|
173
|
+
return true;
|
|
174
|
+
}
|
|
175
|
+
supportsMcpRemote() {
|
|
176
|
+
return true;
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Extracts file paths from concatenated rules by parsing HTML source comments.
|
|
180
|
+
* @param concatenatedRules The concatenated rules string with HTML comments
|
|
181
|
+
* @param projectRoot The project root directory
|
|
182
|
+
* @returns Array of file paths relative to project root
|
|
183
|
+
*/
|
|
184
|
+
extractFilePathsFromRules(concatenatedRules, projectRoot) {
|
|
185
|
+
const sourceCommentRegex = /<!-- Source: (.+?) -->/g;
|
|
186
|
+
const filePaths = [];
|
|
187
|
+
let match;
|
|
188
|
+
while ((match = sourceCommentRegex.exec(concatenatedRules)) !== null) {
|
|
189
|
+
const relativePath = match[1];
|
|
190
|
+
const absolutePath = path.resolve(projectRoot, relativePath);
|
|
191
|
+
const normalizedProjectRoot = path.resolve(projectRoot);
|
|
192
|
+
// Ensure the absolutePath is within the project root (cross-platform compatible)
|
|
193
|
+
// This prevents path traversal attacks while handling Windows/Unix path differences
|
|
194
|
+
const isWithinProject = absolutePath.startsWith(normalizedProjectRoot) &&
|
|
195
|
+
(absolutePath.length === normalizedProjectRoot.length ||
|
|
196
|
+
absolutePath[normalizedProjectRoot.length] === path.sep);
|
|
197
|
+
if (isWithinProject) {
|
|
198
|
+
const projectRelativePath = path.relative(projectRoot, absolutePath);
|
|
199
|
+
filePaths.push(projectRelativePath);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
return filePaths;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
exports.FirebenderAgent = FirebenderAgent;
|