rulesync 0.34.0 → 0.36.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.ja.md +95 -6
- package/README.md +95 -6
- package/dist/chunk-6PQ4APY4.mjs +70 -0
- package/dist/chunk-QHXMJZTJ.mjs +62 -0
- package/dist/chunk-QVPQ2X4L.mjs +77 -0
- package/dist/chunk-SBYRCTWS.mjs +64 -0
- package/dist/chunk-UNTCJDMQ.mjs +73 -0
- package/dist/chunk-YGXGGUBG.mjs +80 -0
- package/dist/claude-O4SRX6VC.mjs +8 -0
- package/dist/cline-H5JF2OPT.mjs +8 -0
- package/dist/copilot-GCIYHK4Q.mjs +8 -0
- package/dist/cursor-N75OH6WS.mjs +8 -0
- package/dist/geminicli-AGOQ32ZE.mjs +8 -0
- package/dist/index.js +1232 -218
- package/dist/index.mjs +929 -209
- package/dist/roo-V5YVC222.mjs +8 -0
- package/package.json +14 -6
package/dist/index.js
CHANGED
|
@@ -6,6 +6,9 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
|
6
6
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
7
|
var __getProtoOf = Object.getPrototypeOf;
|
|
8
8
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __esm = (fn, res) => function __init() {
|
|
10
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
11
|
+
};
|
|
9
12
|
var __copyProps = (to, from, except, desc) => {
|
|
10
13
|
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
14
|
for (let key of __getOwnPropNames(from))
|
|
@@ -23,6 +26,307 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
23
26
|
mod
|
|
24
27
|
));
|
|
25
28
|
|
|
29
|
+
// src/generators/mcp/claude.ts
|
|
30
|
+
function generateClaudeMcp(config, _target) {
|
|
31
|
+
const claudeSettings = {
|
|
32
|
+
mcpServers: {}
|
|
33
|
+
};
|
|
34
|
+
const shouldInclude = (server) => {
|
|
35
|
+
if (!server.rulesyncTargets || server.rulesyncTargets.length === 0) {
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
if (server.rulesyncTargets.length === 1 && server.rulesyncTargets[0] === "*") {
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
return server.rulesyncTargets.includes("claude");
|
|
42
|
+
};
|
|
43
|
+
for (const [serverName, server] of Object.entries(config.mcpServers)) {
|
|
44
|
+
if (!shouldInclude(server)) continue;
|
|
45
|
+
const claudeServer = {};
|
|
46
|
+
if (server.command) {
|
|
47
|
+
claudeServer.command = server.command;
|
|
48
|
+
if (server.args) claudeServer.args = server.args;
|
|
49
|
+
} else if (server.url || server.httpUrl) {
|
|
50
|
+
const url = server.httpUrl || server.url;
|
|
51
|
+
if (url) {
|
|
52
|
+
claudeServer.url = url;
|
|
53
|
+
}
|
|
54
|
+
if (server.httpUrl) {
|
|
55
|
+
claudeServer.transport = "http";
|
|
56
|
+
} else if (server.transport === "sse") {
|
|
57
|
+
claudeServer.transport = "sse";
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (server.env) {
|
|
61
|
+
claudeServer.env = server.env;
|
|
62
|
+
}
|
|
63
|
+
claudeSettings.mcpServers[serverName] = claudeServer;
|
|
64
|
+
}
|
|
65
|
+
return JSON.stringify(claudeSettings, null, 2);
|
|
66
|
+
}
|
|
67
|
+
var init_claude = __esm({
|
|
68
|
+
"src/generators/mcp/claude.ts"() {
|
|
69
|
+
"use strict";
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
// src/generators/mcp/cline.ts
|
|
74
|
+
function generateClineMcp(config, _target) {
|
|
75
|
+
const clineConfig = {
|
|
76
|
+
mcpServers: {}
|
|
77
|
+
};
|
|
78
|
+
const shouldInclude = (server) => {
|
|
79
|
+
const targets = server.rulesyncTargets;
|
|
80
|
+
if (!targets || targets.length === 0) return true;
|
|
81
|
+
if (targets.length === 1 && targets[0] === "*") return true;
|
|
82
|
+
return targets.includes("cline");
|
|
83
|
+
};
|
|
84
|
+
for (const [serverName, server] of Object.entries(config.mcpServers)) {
|
|
85
|
+
if (!shouldInclude(server)) continue;
|
|
86
|
+
const clineServer = {};
|
|
87
|
+
if (server.command) {
|
|
88
|
+
clineServer.command = server.command;
|
|
89
|
+
if (server.args) clineServer.args = server.args;
|
|
90
|
+
} else if (server.url) {
|
|
91
|
+
clineServer.url = server.url;
|
|
92
|
+
}
|
|
93
|
+
if (server.env) {
|
|
94
|
+
clineServer.env = server.env;
|
|
95
|
+
}
|
|
96
|
+
if (server.disabled !== void 0) {
|
|
97
|
+
clineServer.disabled = server.disabled;
|
|
98
|
+
}
|
|
99
|
+
if (server.alwaysAllow) {
|
|
100
|
+
clineServer.alwaysAllow = server.alwaysAllow;
|
|
101
|
+
}
|
|
102
|
+
if (server.networkTimeout !== void 0) {
|
|
103
|
+
clineServer.networkTimeout = server.networkTimeout;
|
|
104
|
+
}
|
|
105
|
+
clineConfig.mcpServers[serverName] = clineServer;
|
|
106
|
+
}
|
|
107
|
+
return JSON.stringify(clineConfig, null, 2);
|
|
108
|
+
}
|
|
109
|
+
var init_cline = __esm({
|
|
110
|
+
"src/generators/mcp/cline.ts"() {
|
|
111
|
+
"use strict";
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
// src/generators/mcp/copilot.ts
|
|
116
|
+
function generateCopilotMcp(config, target) {
|
|
117
|
+
const shouldInclude = (server) => {
|
|
118
|
+
const targets = server.rulesyncTargets;
|
|
119
|
+
if (!targets || targets.length === 0) return true;
|
|
120
|
+
if (targets.length === 1 && targets[0] === "*") return true;
|
|
121
|
+
return targets.includes("copilot");
|
|
122
|
+
};
|
|
123
|
+
const servers = {};
|
|
124
|
+
const inputs = [];
|
|
125
|
+
const inputMap = /* @__PURE__ */ new Map();
|
|
126
|
+
for (const [serverName, server] of Object.entries(config.mcpServers)) {
|
|
127
|
+
if (!shouldInclude(server)) continue;
|
|
128
|
+
const copilotServer = {};
|
|
129
|
+
if (server.command) {
|
|
130
|
+
copilotServer.command = server.command;
|
|
131
|
+
if (server.args) copilotServer.args = server.args;
|
|
132
|
+
} else if (server.url || server.httpUrl) {
|
|
133
|
+
const url = server.httpUrl || server.url;
|
|
134
|
+
if (url) {
|
|
135
|
+
copilotServer.url = url;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
if (server.env) {
|
|
139
|
+
copilotServer.env = {};
|
|
140
|
+
for (const [key, value] of Object.entries(server.env)) {
|
|
141
|
+
if (target === "editor" && value.includes("SECRET")) {
|
|
142
|
+
const inputId = `${serverName}_${key}`;
|
|
143
|
+
inputMap.set(inputId, value);
|
|
144
|
+
copilotServer.env[key] = `\${input:${inputId}}`;
|
|
145
|
+
inputs.push({
|
|
146
|
+
id: inputId,
|
|
147
|
+
type: "password",
|
|
148
|
+
description: `${key} for ${serverName}`
|
|
149
|
+
});
|
|
150
|
+
} else {
|
|
151
|
+
copilotServer.env[key] = value;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
if (server.tools) {
|
|
156
|
+
copilotServer.tools = server.tools;
|
|
157
|
+
} else if (server.alwaysAllow) {
|
|
158
|
+
copilotServer.tools = server.alwaysAllow;
|
|
159
|
+
}
|
|
160
|
+
servers[serverName] = copilotServer;
|
|
161
|
+
}
|
|
162
|
+
if (target === "codingAgent") {
|
|
163
|
+
const config2 = { mcpServers: servers };
|
|
164
|
+
return JSON.stringify(config2, null, 2);
|
|
165
|
+
} else {
|
|
166
|
+
const config2 = { servers };
|
|
167
|
+
if (inputs.length > 0) {
|
|
168
|
+
config2.inputs = inputs;
|
|
169
|
+
}
|
|
170
|
+
return JSON.stringify(config2, null, 2);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
var init_copilot = __esm({
|
|
174
|
+
"src/generators/mcp/copilot.ts"() {
|
|
175
|
+
"use strict";
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
// src/generators/mcp/cursor.ts
|
|
180
|
+
function generateCursorMcp(config, _target) {
|
|
181
|
+
const cursorConfig = {
|
|
182
|
+
mcpServers: {}
|
|
183
|
+
};
|
|
184
|
+
const shouldInclude = (server) => {
|
|
185
|
+
const targets = server.rulesyncTargets;
|
|
186
|
+
if (!targets || targets.length === 0) return true;
|
|
187
|
+
if (targets.length === 1 && targets[0] === "*") return true;
|
|
188
|
+
return targets.includes("cursor");
|
|
189
|
+
};
|
|
190
|
+
for (const [serverName, server] of Object.entries(config.mcpServers)) {
|
|
191
|
+
if (!shouldInclude(server)) continue;
|
|
192
|
+
const cursorServer = {};
|
|
193
|
+
if (server.command) {
|
|
194
|
+
cursorServer.command = server.command;
|
|
195
|
+
if (server.args) cursorServer.args = server.args;
|
|
196
|
+
} else if (server.url || server.httpUrl) {
|
|
197
|
+
const url = server.httpUrl || server.url;
|
|
198
|
+
if (url) {
|
|
199
|
+
cursorServer.url = url;
|
|
200
|
+
}
|
|
201
|
+
if (server.httpUrl || server.transport === "http") {
|
|
202
|
+
cursorServer.type = "streamable-http";
|
|
203
|
+
} else if (server.transport === "sse" || server.type === "sse") {
|
|
204
|
+
cursorServer.type = "sse";
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
if (server.env) {
|
|
208
|
+
cursorServer.env = server.env;
|
|
209
|
+
}
|
|
210
|
+
if (server.cwd) {
|
|
211
|
+
cursorServer.cwd = server.cwd;
|
|
212
|
+
}
|
|
213
|
+
cursorConfig.mcpServers[serverName] = cursorServer;
|
|
214
|
+
}
|
|
215
|
+
return JSON.stringify(cursorConfig, null, 2);
|
|
216
|
+
}
|
|
217
|
+
var init_cursor = __esm({
|
|
218
|
+
"src/generators/mcp/cursor.ts"() {
|
|
219
|
+
"use strict";
|
|
220
|
+
}
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
// src/generators/mcp/geminicli.ts
|
|
224
|
+
function generateGeminiCliMcp(config, _target) {
|
|
225
|
+
const geminiSettings = {
|
|
226
|
+
mcpServers: {}
|
|
227
|
+
};
|
|
228
|
+
const shouldInclude = (server) => {
|
|
229
|
+
const targets = server.rulesyncTargets;
|
|
230
|
+
if (!targets || targets.length === 0) return true;
|
|
231
|
+
if (targets.length === 1 && targets[0] === "*") return true;
|
|
232
|
+
return targets.includes("geminicli");
|
|
233
|
+
};
|
|
234
|
+
for (const [serverName, server] of Object.entries(config.mcpServers)) {
|
|
235
|
+
if (!shouldInclude(server)) continue;
|
|
236
|
+
const geminiServer = {};
|
|
237
|
+
if (server.command) {
|
|
238
|
+
geminiServer.command = server.command;
|
|
239
|
+
if (server.args) geminiServer.args = server.args;
|
|
240
|
+
} else if (server.url || server.httpUrl) {
|
|
241
|
+
if (server.httpUrl) {
|
|
242
|
+
geminiServer.httpUrl = server.httpUrl;
|
|
243
|
+
} else if (server.url) {
|
|
244
|
+
geminiServer.url = server.url;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
if (server.env) {
|
|
248
|
+
geminiServer.env = {};
|
|
249
|
+
for (const [key, value] of Object.entries(server.env)) {
|
|
250
|
+
if (value.startsWith("${") && value.endsWith("}")) {
|
|
251
|
+
geminiServer.env[key] = value;
|
|
252
|
+
} else {
|
|
253
|
+
geminiServer.env[key] = `\${${value}}`;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
if (server.timeout !== void 0) {
|
|
258
|
+
geminiServer.timeout = server.timeout;
|
|
259
|
+
}
|
|
260
|
+
if (server.trust !== void 0) {
|
|
261
|
+
geminiServer.trust = server.trust;
|
|
262
|
+
}
|
|
263
|
+
geminiSettings.mcpServers[serverName] = geminiServer;
|
|
264
|
+
}
|
|
265
|
+
return JSON.stringify(geminiSettings, null, 2);
|
|
266
|
+
}
|
|
267
|
+
var init_geminicli = __esm({
|
|
268
|
+
"src/generators/mcp/geminicli.ts"() {
|
|
269
|
+
"use strict";
|
|
270
|
+
}
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
// src/generators/mcp/roo.ts
|
|
274
|
+
function generateRooMcp(config, _target) {
|
|
275
|
+
const rooConfig = {
|
|
276
|
+
mcpServers: {}
|
|
277
|
+
};
|
|
278
|
+
const shouldInclude = (server) => {
|
|
279
|
+
const targets = server.rulesyncTargets;
|
|
280
|
+
if (!targets || targets.length === 0) return true;
|
|
281
|
+
if (targets.length === 1 && targets[0] === "*") return true;
|
|
282
|
+
return targets.includes("roo");
|
|
283
|
+
};
|
|
284
|
+
for (const [serverName, server] of Object.entries(config.mcpServers)) {
|
|
285
|
+
if (!shouldInclude(server)) continue;
|
|
286
|
+
const rooServer = {};
|
|
287
|
+
if (server.command) {
|
|
288
|
+
rooServer.command = server.command;
|
|
289
|
+
if (server.args) rooServer.args = server.args;
|
|
290
|
+
} else if (server.url || server.httpUrl) {
|
|
291
|
+
const url = server.httpUrl || server.url;
|
|
292
|
+
if (url) {
|
|
293
|
+
rooServer.url = url;
|
|
294
|
+
}
|
|
295
|
+
if (server.httpUrl || server.transport === "http") {
|
|
296
|
+
rooServer.type = "streamable-http";
|
|
297
|
+
} else if (server.transport === "sse" || server.type === "sse") {
|
|
298
|
+
rooServer.type = "sse";
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
if (server.env) {
|
|
302
|
+
rooServer.env = {};
|
|
303
|
+
for (const [key, value] of Object.entries(server.env)) {
|
|
304
|
+
if (value.startsWith("${env:") && value.endsWith("}")) {
|
|
305
|
+
rooServer.env[key] = value;
|
|
306
|
+
} else {
|
|
307
|
+
rooServer.env[key] = `\${env:${value}}`;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
if (server.disabled !== void 0) {
|
|
312
|
+
rooServer.disabled = server.disabled;
|
|
313
|
+
}
|
|
314
|
+
if (server.alwaysAllow) {
|
|
315
|
+
rooServer.alwaysAllow = server.alwaysAllow;
|
|
316
|
+
}
|
|
317
|
+
if (server.networkTimeout !== void 0) {
|
|
318
|
+
rooServer.networkTimeout = Math.max(3e4, Math.min(3e5, server.networkTimeout));
|
|
319
|
+
}
|
|
320
|
+
rooConfig.mcpServers[serverName] = rooServer;
|
|
321
|
+
}
|
|
322
|
+
return JSON.stringify(rooConfig, null, 2);
|
|
323
|
+
}
|
|
324
|
+
var init_roo = __esm({
|
|
325
|
+
"src/generators/mcp/roo.ts"() {
|
|
326
|
+
"use strict";
|
|
327
|
+
}
|
|
328
|
+
});
|
|
329
|
+
|
|
26
330
|
// src/cli/index.ts
|
|
27
331
|
var import_commander = require("commander");
|
|
28
332
|
|
|
@@ -39,11 +343,12 @@ function getDefaultConfig() {
|
|
|
39
343
|
cursor: ".cursor/rules",
|
|
40
344
|
cline: ".clinerules",
|
|
41
345
|
claudecode: ".",
|
|
346
|
+
claude: ".",
|
|
42
347
|
roo: ".roo/rules",
|
|
43
348
|
geminicli: ".gemini/memories"
|
|
44
349
|
},
|
|
45
350
|
watchEnabled: false,
|
|
46
|
-
defaultTargets: ["copilot", "cursor", "cline", "claudecode", "roo", "geminicli"]
|
|
351
|
+
defaultTargets: ["copilot", "cursor", "cline", "claudecode", "claude", "roo", "geminicli"]
|
|
47
352
|
};
|
|
48
353
|
}
|
|
49
354
|
function resolveTargets(targets, config) {
|
|
@@ -89,27 +394,158 @@ async function addCommand(filename) {
|
|
|
89
394
|
}
|
|
90
395
|
}
|
|
91
396
|
|
|
92
|
-
// src/generators/claudecode.ts
|
|
397
|
+
// src/generators/rules/claudecode.ts
|
|
398
|
+
var import_node_path4 = require("path");
|
|
399
|
+
|
|
400
|
+
// src/utils/file.ts
|
|
401
|
+
var import_promises2 = require("fs/promises");
|
|
402
|
+
var import_node_path3 = require("path");
|
|
403
|
+
|
|
404
|
+
// src/utils/ignore.ts
|
|
93
405
|
var import_node_path2 = require("path");
|
|
406
|
+
var import_micromatch = __toESM(require("micromatch"));
|
|
407
|
+
var cachedIgnorePatterns = null;
|
|
408
|
+
async function loadIgnorePatterns(baseDir = process.cwd()) {
|
|
409
|
+
if (cachedIgnorePatterns) {
|
|
410
|
+
return cachedIgnorePatterns;
|
|
411
|
+
}
|
|
412
|
+
const ignorePath = (0, import_node_path2.join)(baseDir, ".rulesyncignore");
|
|
413
|
+
if (!await fileExists(ignorePath)) {
|
|
414
|
+
cachedIgnorePatterns = { patterns: [] };
|
|
415
|
+
return cachedIgnorePatterns;
|
|
416
|
+
}
|
|
417
|
+
try {
|
|
418
|
+
const content = await readFileContent(ignorePath);
|
|
419
|
+
const patterns = parseIgnoreFile(content);
|
|
420
|
+
cachedIgnorePatterns = { patterns };
|
|
421
|
+
return cachedIgnorePatterns;
|
|
422
|
+
} catch (error) {
|
|
423
|
+
console.warn(`Failed to read .rulesyncignore: ${error}`);
|
|
424
|
+
cachedIgnorePatterns = { patterns: [] };
|
|
425
|
+
return cachedIgnorePatterns;
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
function parseIgnoreFile(content) {
|
|
429
|
+
return content.split("\n").map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
|
|
430
|
+
}
|
|
431
|
+
function isFileIgnored(filepath, ignorePatterns) {
|
|
432
|
+
if (ignorePatterns.length === 0) {
|
|
433
|
+
return false;
|
|
434
|
+
}
|
|
435
|
+
const negationPatterns = ignorePatterns.filter((p) => p.startsWith("!"));
|
|
436
|
+
const positivePatterns = ignorePatterns.filter((p) => !p.startsWith("!"));
|
|
437
|
+
const isIgnored = positivePatterns.length > 0 && import_micromatch.default.isMatch(filepath, positivePatterns, {
|
|
438
|
+
dot: true
|
|
439
|
+
});
|
|
440
|
+
if (isIgnored && negationPatterns.length > 0) {
|
|
441
|
+
const negationPatternsWithoutPrefix = negationPatterns.map((p) => p.substring(1));
|
|
442
|
+
return !import_micromatch.default.isMatch(filepath, negationPatternsWithoutPrefix, {
|
|
443
|
+
dot: true
|
|
444
|
+
});
|
|
445
|
+
}
|
|
446
|
+
return isIgnored;
|
|
447
|
+
}
|
|
448
|
+
function filterIgnoredFiles(files, ignorePatterns) {
|
|
449
|
+
if (ignorePatterns.length === 0) {
|
|
450
|
+
return files;
|
|
451
|
+
}
|
|
452
|
+
return files.filter((file) => !isFileIgnored(file, ignorePatterns));
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
// src/utils/file.ts
|
|
456
|
+
async function ensureDir(dirPath) {
|
|
457
|
+
try {
|
|
458
|
+
await (0, import_promises2.stat)(dirPath);
|
|
459
|
+
} catch {
|
|
460
|
+
await (0, import_promises2.mkdir)(dirPath, { recursive: true });
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
async function readFileContent(filepath) {
|
|
464
|
+
return (0, import_promises2.readFile)(filepath, "utf-8");
|
|
465
|
+
}
|
|
466
|
+
async function writeFileContent(filepath, content) {
|
|
467
|
+
await ensureDir((0, import_node_path3.dirname)(filepath));
|
|
468
|
+
await (0, import_promises2.writeFile)(filepath, content, "utf-8");
|
|
469
|
+
}
|
|
470
|
+
async function findFiles(dir, extension = ".md", ignorePatterns) {
|
|
471
|
+
try {
|
|
472
|
+
const files = await (0, import_promises2.readdir)(dir);
|
|
473
|
+
const filtered = files.filter((file) => file.endsWith(extension)).map((file) => (0, import_node_path3.join)(dir, file));
|
|
474
|
+
if (ignorePatterns && ignorePatterns.length > 0) {
|
|
475
|
+
return filterIgnoredFiles(filtered, ignorePatterns);
|
|
476
|
+
}
|
|
477
|
+
return filtered;
|
|
478
|
+
} catch {
|
|
479
|
+
return [];
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
async function fileExists(filepath) {
|
|
483
|
+
try {
|
|
484
|
+
await (0, import_promises2.stat)(filepath);
|
|
485
|
+
return true;
|
|
486
|
+
} catch {
|
|
487
|
+
return false;
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
async function removeDirectory(dirPath) {
|
|
491
|
+
const dangerousPaths = [".", "/", "~", "src", "node_modules"];
|
|
492
|
+
if (dangerousPaths.includes(dirPath) || dirPath === "") {
|
|
493
|
+
console.warn(`Skipping deletion of dangerous path: ${dirPath}`);
|
|
494
|
+
return;
|
|
495
|
+
}
|
|
496
|
+
try {
|
|
497
|
+
if (await fileExists(dirPath)) {
|
|
498
|
+
await (0, import_promises2.rm)(dirPath, { recursive: true, force: true });
|
|
499
|
+
}
|
|
500
|
+
} catch (error) {
|
|
501
|
+
console.warn(`Failed to remove directory ${dirPath}:`, error);
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
async function removeFile(filepath) {
|
|
505
|
+
try {
|
|
506
|
+
if (await fileExists(filepath)) {
|
|
507
|
+
await (0, import_promises2.rm)(filepath);
|
|
508
|
+
}
|
|
509
|
+
} catch (error) {
|
|
510
|
+
console.warn(`Failed to remove file ${filepath}:`, error);
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
async function removeClaudeGeneratedFiles() {
|
|
514
|
+
const filesToRemove = ["CLAUDE.md", ".claude/memories"];
|
|
515
|
+
for (const fileOrDir of filesToRemove) {
|
|
516
|
+
if (fileOrDir.endsWith("/memories")) {
|
|
517
|
+
await removeDirectory(fileOrDir);
|
|
518
|
+
} else {
|
|
519
|
+
await removeFile(fileOrDir);
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
// src/generators/rules/claudecode.ts
|
|
94
525
|
async function generateClaudecodeConfig(rules, config, baseDir) {
|
|
95
526
|
const outputs = [];
|
|
96
527
|
const rootRules = rules.filter((r) => r.frontmatter.root === true);
|
|
97
528
|
const detailRules = rules.filter((r) => r.frontmatter.root === false);
|
|
98
529
|
const claudeMdContent = generateClaudeMarkdown(rootRules, detailRules);
|
|
99
|
-
const claudeOutputDir = baseDir ? (0,
|
|
530
|
+
const claudeOutputDir = baseDir ? (0, import_node_path4.join)(baseDir, config.outputPaths.claudecode) : config.outputPaths.claudecode;
|
|
100
531
|
outputs.push({
|
|
101
532
|
tool: "claudecode",
|
|
102
|
-
filepath: (0,
|
|
533
|
+
filepath: (0, import_node_path4.join)(claudeOutputDir, "CLAUDE.md"),
|
|
103
534
|
content: claudeMdContent
|
|
104
535
|
});
|
|
105
536
|
for (const rule of detailRules) {
|
|
106
537
|
const memoryContent = generateMemoryFile(rule);
|
|
107
538
|
outputs.push({
|
|
108
539
|
tool: "claudecode",
|
|
109
|
-
filepath: (0,
|
|
540
|
+
filepath: (0, import_node_path4.join)(claudeOutputDir, ".claude", "memories", `${rule.filename}.md`),
|
|
110
541
|
content: memoryContent
|
|
111
542
|
});
|
|
112
543
|
}
|
|
544
|
+
const ignorePatterns = await loadIgnorePatterns(baseDir);
|
|
545
|
+
if (ignorePatterns.patterns.length > 0) {
|
|
546
|
+
const settingsPath = baseDir ? (0, import_node_path4.join)(baseDir, ".claude", "settings.json") : (0, import_node_path4.join)(".claude", "settings.json");
|
|
547
|
+
await updateClaudeSettings(settingsPath, ignorePatterns.patterns);
|
|
548
|
+
}
|
|
113
549
|
return outputs;
|
|
114
550
|
}
|
|
115
551
|
function generateClaudeMarkdown(rootRules, detailRules) {
|
|
@@ -138,42 +574,101 @@ function generateClaudeMarkdown(rootRules, detailRules) {
|
|
|
138
574
|
function generateMemoryFile(rule) {
|
|
139
575
|
return rule.content.trim();
|
|
140
576
|
}
|
|
577
|
+
async function updateClaudeSettings(settingsPath, ignorePatterns) {
|
|
578
|
+
let settings = {};
|
|
579
|
+
if (await fileExists(settingsPath)) {
|
|
580
|
+
try {
|
|
581
|
+
const content = await readFileContent(settingsPath);
|
|
582
|
+
settings = JSON.parse(content);
|
|
583
|
+
} catch (_error) {
|
|
584
|
+
console.warn(`Failed to parse existing ${settingsPath}, creating new settings`);
|
|
585
|
+
settings = {};
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
if (!settings.permissions) {
|
|
589
|
+
settings.permissions = {};
|
|
590
|
+
}
|
|
591
|
+
if (!Array.isArray(settings.permissions.deny)) {
|
|
592
|
+
settings.permissions.deny = [];
|
|
593
|
+
}
|
|
594
|
+
const readDenyRules = ignorePatterns.map((pattern) => `Read(${pattern})`);
|
|
595
|
+
settings.permissions.deny = settings.permissions.deny.filter((rule) => {
|
|
596
|
+
if (!rule.startsWith("Read(")) return true;
|
|
597
|
+
const match = rule.match(/^Read\((.*)\)$/);
|
|
598
|
+
if (!match) return true;
|
|
599
|
+
return !ignorePatterns.includes(match[1] ?? "");
|
|
600
|
+
});
|
|
601
|
+
settings.permissions.deny.push(...readDenyRules);
|
|
602
|
+
settings.permissions.deny = [...new Set(settings.permissions.deny)];
|
|
603
|
+
const jsonContent = JSON.stringify(settings, null, 2);
|
|
604
|
+
await writeFileContent(settingsPath, jsonContent);
|
|
605
|
+
console.log(`\u2705 Updated Claude Code settings: ${settingsPath}`);
|
|
606
|
+
}
|
|
141
607
|
|
|
142
|
-
// src/generators/cline.ts
|
|
143
|
-
var
|
|
608
|
+
// src/generators/rules/cline.ts
|
|
609
|
+
var import_node_path5 = require("path");
|
|
144
610
|
async function generateClineConfig(rules, config, baseDir) {
|
|
145
611
|
const outputs = [];
|
|
146
612
|
for (const rule of rules) {
|
|
147
613
|
const content = generateClineMarkdown(rule);
|
|
148
|
-
const outputDir = baseDir ? (0,
|
|
149
|
-
const filepath = (0,
|
|
614
|
+
const outputDir = baseDir ? (0, import_node_path5.join)(baseDir, config.outputPaths.cline) : config.outputPaths.cline;
|
|
615
|
+
const filepath = (0, import_node_path5.join)(outputDir, `${rule.filename}.md`);
|
|
150
616
|
outputs.push({
|
|
151
617
|
tool: "cline",
|
|
152
618
|
filepath,
|
|
153
619
|
content
|
|
154
620
|
});
|
|
155
621
|
}
|
|
622
|
+
const ignorePatterns = await loadIgnorePatterns(baseDir);
|
|
623
|
+
if (ignorePatterns.patterns.length > 0) {
|
|
624
|
+
const clineIgnorePath = baseDir ? (0, import_node_path5.join)(baseDir, ".clineignore") : ".clineignore";
|
|
625
|
+
const clineIgnoreContent = generateClineIgnore(ignorePatterns.patterns);
|
|
626
|
+
outputs.push({
|
|
627
|
+
tool: "cline",
|
|
628
|
+
filepath: clineIgnorePath,
|
|
629
|
+
content: clineIgnoreContent
|
|
630
|
+
});
|
|
631
|
+
}
|
|
156
632
|
return outputs;
|
|
157
633
|
}
|
|
158
634
|
function generateClineMarkdown(rule) {
|
|
159
635
|
return rule.content.trim();
|
|
160
636
|
}
|
|
637
|
+
function generateClineIgnore(patterns) {
|
|
638
|
+
const lines = [
|
|
639
|
+
"# Generated by rulesync from .rulesyncignore",
|
|
640
|
+
"# This file is automatically generated. Do not edit manually.",
|
|
641
|
+
"",
|
|
642
|
+
...patterns
|
|
643
|
+
];
|
|
644
|
+
return lines.join("\n");
|
|
645
|
+
}
|
|
161
646
|
|
|
162
|
-
// src/generators/copilot.ts
|
|
163
|
-
var
|
|
647
|
+
// src/generators/rules/copilot.ts
|
|
648
|
+
var import_node_path6 = require("path");
|
|
164
649
|
async function generateCopilotConfig(rules, config, baseDir) {
|
|
165
650
|
const outputs = [];
|
|
166
651
|
for (const rule of rules) {
|
|
167
652
|
const content = generateCopilotMarkdown(rule);
|
|
168
653
|
const baseFilename = rule.filename.replace(/\.md$/, "");
|
|
169
|
-
const outputDir = baseDir ? (0,
|
|
170
|
-
const filepath = (0,
|
|
654
|
+
const outputDir = baseDir ? (0, import_node_path6.join)(baseDir, config.outputPaths.copilot) : config.outputPaths.copilot;
|
|
655
|
+
const filepath = (0, import_node_path6.join)(outputDir, `${baseFilename}.instructions.md`);
|
|
171
656
|
outputs.push({
|
|
172
657
|
tool: "copilot",
|
|
173
658
|
filepath,
|
|
174
659
|
content
|
|
175
660
|
});
|
|
176
661
|
}
|
|
662
|
+
const ignorePatterns = await loadIgnorePatterns(baseDir);
|
|
663
|
+
if (ignorePatterns.patterns.length > 0) {
|
|
664
|
+
const copilotIgnorePath = baseDir ? (0, import_node_path6.join)(baseDir, ".copilotignore") : ".copilotignore";
|
|
665
|
+
const copilotIgnoreContent = generateCopilotIgnore(ignorePatterns.patterns);
|
|
666
|
+
outputs.push({
|
|
667
|
+
tool: "copilot",
|
|
668
|
+
filepath: copilotIgnorePath,
|
|
669
|
+
content: copilotIgnoreContent
|
|
670
|
+
});
|
|
671
|
+
}
|
|
177
672
|
return outputs;
|
|
178
673
|
}
|
|
179
674
|
function generateCopilotMarkdown(rule) {
|
|
@@ -189,21 +684,42 @@ function generateCopilotMarkdown(rule) {
|
|
|
189
684
|
lines.push(rule.content);
|
|
190
685
|
return lines.join("\n");
|
|
191
686
|
}
|
|
687
|
+
function generateCopilotIgnore(patterns) {
|
|
688
|
+
const lines = [
|
|
689
|
+
"# Generated by rulesync from .rulesyncignore",
|
|
690
|
+
"# This file is automatically generated. Do not edit manually.",
|
|
691
|
+
"# Note: .copilotignore is not officially supported by GitHub Copilot.",
|
|
692
|
+
"# This file is for use with community tools like copilotignore-vscode extension.",
|
|
693
|
+
"",
|
|
694
|
+
...patterns
|
|
695
|
+
];
|
|
696
|
+
return lines.join("\n");
|
|
697
|
+
}
|
|
192
698
|
|
|
193
|
-
// src/generators/cursor.ts
|
|
194
|
-
var
|
|
699
|
+
// src/generators/rules/cursor.ts
|
|
700
|
+
var import_node_path7 = require("path");
|
|
195
701
|
async function generateCursorConfig(rules, config, baseDir) {
|
|
196
702
|
const outputs = [];
|
|
197
703
|
for (const rule of rules) {
|
|
198
704
|
const content = generateCursorMarkdown(rule);
|
|
199
|
-
const outputDir = baseDir ? (0,
|
|
200
|
-
const filepath = (0,
|
|
705
|
+
const outputDir = baseDir ? (0, import_node_path7.join)(baseDir, config.outputPaths.cursor) : config.outputPaths.cursor;
|
|
706
|
+
const filepath = (0, import_node_path7.join)(outputDir, `${rule.filename}.mdc`);
|
|
201
707
|
outputs.push({
|
|
202
708
|
tool: "cursor",
|
|
203
709
|
filepath,
|
|
204
710
|
content
|
|
205
711
|
});
|
|
206
712
|
}
|
|
713
|
+
const ignorePatterns = await loadIgnorePatterns(baseDir);
|
|
714
|
+
if (ignorePatterns.patterns.length > 0) {
|
|
715
|
+
const cursorIgnorePath = baseDir ? (0, import_node_path7.join)(baseDir, ".cursorignore") : ".cursorignore";
|
|
716
|
+
const cursorIgnoreContent = generateCursorIgnore(ignorePatterns.patterns);
|
|
717
|
+
outputs.push({
|
|
718
|
+
tool: "cursor",
|
|
719
|
+
filepath: cursorIgnorePath,
|
|
720
|
+
content: cursorIgnoreContent
|
|
721
|
+
});
|
|
722
|
+
}
|
|
207
723
|
return outputs;
|
|
208
724
|
}
|
|
209
725
|
function generateCursorMarkdown(rule) {
|
|
@@ -226,17 +742,26 @@ function generateCursorMarkdown(rule) {
|
|
|
226
742
|
lines.push(rule.content);
|
|
227
743
|
return lines.join("\n");
|
|
228
744
|
}
|
|
745
|
+
function generateCursorIgnore(patterns) {
|
|
746
|
+
const lines = [
|
|
747
|
+
"# Generated by rulesync from .rulesyncignore",
|
|
748
|
+
"# This file is automatically generated. Do not edit manually.",
|
|
749
|
+
"",
|
|
750
|
+
...patterns
|
|
751
|
+
];
|
|
752
|
+
return lines.join("\n");
|
|
753
|
+
}
|
|
229
754
|
|
|
230
|
-
// src/generators/geminicli.ts
|
|
231
|
-
var
|
|
755
|
+
// src/generators/rules/geminicli.ts
|
|
756
|
+
var import_node_path8 = require("path");
|
|
232
757
|
async function generateGeminiConfig(rules, config, baseDir) {
|
|
233
758
|
const outputs = [];
|
|
234
759
|
const rootRule = rules.find((rule) => rule.frontmatter.root === true);
|
|
235
760
|
const memoryRules = rules.filter((rule) => rule.frontmatter.root === false);
|
|
236
761
|
for (const rule of memoryRules) {
|
|
237
762
|
const content = generateGeminiMemoryMarkdown(rule);
|
|
238
|
-
const outputDir = baseDir ? (0,
|
|
239
|
-
const filepath = (0,
|
|
763
|
+
const outputDir = baseDir ? (0, import_node_path8.join)(baseDir, config.outputPaths.geminicli) : config.outputPaths.geminicli;
|
|
764
|
+
const filepath = (0, import_node_path8.join)(outputDir, `${rule.filename}.md`);
|
|
240
765
|
outputs.push({
|
|
241
766
|
tool: "geminicli",
|
|
242
767
|
filepath,
|
|
@@ -244,12 +769,22 @@ async function generateGeminiConfig(rules, config, baseDir) {
|
|
|
244
769
|
});
|
|
245
770
|
}
|
|
246
771
|
const rootContent = generateGeminiRootMarkdown(rootRule, memoryRules, baseDir);
|
|
247
|
-
const rootFilepath = baseDir ? (0,
|
|
772
|
+
const rootFilepath = baseDir ? (0, import_node_path8.join)(baseDir, "GEMINI.md") : "GEMINI.md";
|
|
248
773
|
outputs.push({
|
|
249
774
|
tool: "geminicli",
|
|
250
775
|
filepath: rootFilepath,
|
|
251
776
|
content: rootContent
|
|
252
777
|
});
|
|
778
|
+
const ignorePatterns = await loadIgnorePatterns(baseDir);
|
|
779
|
+
if (ignorePatterns.patterns.length > 0) {
|
|
780
|
+
const aiexcludePath = baseDir ? (0, import_node_path8.join)(baseDir, ".aiexclude") : ".aiexclude";
|
|
781
|
+
const aiexcludeContent = generateAiexclude(ignorePatterns.patterns);
|
|
782
|
+
outputs.push({
|
|
783
|
+
tool: "geminicli",
|
|
784
|
+
filepath: aiexcludePath,
|
|
785
|
+
content: aiexcludeContent
|
|
786
|
+
});
|
|
787
|
+
}
|
|
253
788
|
return outputs;
|
|
254
789
|
}
|
|
255
790
|
function generateGeminiMemoryMarkdown(rule) {
|
|
@@ -279,92 +814,53 @@ function generateGeminiRootMarkdown(rootRule, memoryRules, _baseDir) {
|
|
|
279
814
|
}
|
|
280
815
|
return lines.join("\n");
|
|
281
816
|
}
|
|
817
|
+
function generateAiexclude(patterns) {
|
|
818
|
+
const lines = [
|
|
819
|
+
"# Generated by rulesync from .rulesyncignore",
|
|
820
|
+
"# This file is automatically generated. Do not edit manually.",
|
|
821
|
+
"",
|
|
822
|
+
...patterns
|
|
823
|
+
];
|
|
824
|
+
return lines.join("\n");
|
|
825
|
+
}
|
|
282
826
|
|
|
283
|
-
// src/generators/roo.ts
|
|
284
|
-
var
|
|
827
|
+
// src/generators/rules/roo.ts
|
|
828
|
+
var import_node_path9 = require("path");
|
|
285
829
|
async function generateRooConfig(rules, config, baseDir) {
|
|
286
830
|
const outputs = [];
|
|
287
831
|
for (const rule of rules) {
|
|
288
832
|
const content = generateRooMarkdown(rule);
|
|
289
|
-
const outputDir = baseDir ? (0,
|
|
290
|
-
const filepath = (0,
|
|
833
|
+
const outputDir = baseDir ? (0, import_node_path9.join)(baseDir, config.outputPaths.roo) : config.outputPaths.roo;
|
|
834
|
+
const filepath = (0, import_node_path9.join)(outputDir, `${rule.filename}.md`);
|
|
291
835
|
outputs.push({
|
|
292
836
|
tool: "roo",
|
|
293
837
|
filepath,
|
|
294
838
|
content
|
|
295
839
|
});
|
|
296
840
|
}
|
|
841
|
+
const ignorePatterns = await loadIgnorePatterns(baseDir);
|
|
842
|
+
if (ignorePatterns.patterns.length > 0) {
|
|
843
|
+
const rooIgnorePath = baseDir ? (0, import_node_path9.join)(baseDir, ".rooignore") : ".rooignore";
|
|
844
|
+
const rooIgnoreContent = generateRooIgnore(ignorePatterns.patterns);
|
|
845
|
+
outputs.push({
|
|
846
|
+
tool: "roo",
|
|
847
|
+
filepath: rooIgnorePath,
|
|
848
|
+
content: rooIgnoreContent
|
|
849
|
+
});
|
|
850
|
+
}
|
|
297
851
|
return outputs;
|
|
298
852
|
}
|
|
299
853
|
function generateRooMarkdown(rule) {
|
|
300
854
|
return rule.content.trim();
|
|
301
855
|
}
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
await (0, import_promises2.mkdir)(dirPath, { recursive: true });
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
async function readFileContent(filepath) {
|
|
314
|
-
return (0, import_promises2.readFile)(filepath, "utf-8");
|
|
315
|
-
}
|
|
316
|
-
async function writeFileContent(filepath, content) {
|
|
317
|
-
await ensureDir((0, import_node_path8.dirname)(filepath));
|
|
318
|
-
await (0, import_promises2.writeFile)(filepath, content, "utf-8");
|
|
319
|
-
}
|
|
320
|
-
async function findFiles(dir, extension = ".md") {
|
|
321
|
-
try {
|
|
322
|
-
const files = await (0, import_promises2.readdir)(dir);
|
|
323
|
-
return files.filter((file) => file.endsWith(extension)).map((file) => (0, import_node_path8.join)(dir, file));
|
|
324
|
-
} catch {
|
|
325
|
-
return [];
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
async function fileExists(filepath) {
|
|
329
|
-
try {
|
|
330
|
-
await (0, import_promises2.stat)(filepath);
|
|
331
|
-
return true;
|
|
332
|
-
} catch {
|
|
333
|
-
return false;
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
async function removeDirectory(dirPath) {
|
|
337
|
-
const dangerousPaths = [".", "/", "~", "src", "node_modules"];
|
|
338
|
-
if (dangerousPaths.includes(dirPath) || dirPath === "") {
|
|
339
|
-
console.warn(`Skipping deletion of dangerous path: ${dirPath}`);
|
|
340
|
-
return;
|
|
341
|
-
}
|
|
342
|
-
try {
|
|
343
|
-
if (await fileExists(dirPath)) {
|
|
344
|
-
await (0, import_promises2.rm)(dirPath, { recursive: true, force: true });
|
|
345
|
-
}
|
|
346
|
-
} catch (error) {
|
|
347
|
-
console.warn(`Failed to remove directory ${dirPath}:`, error);
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
async function removeFile(filepath) {
|
|
351
|
-
try {
|
|
352
|
-
if (await fileExists(filepath)) {
|
|
353
|
-
await (0, import_promises2.rm)(filepath);
|
|
354
|
-
}
|
|
355
|
-
} catch (error) {
|
|
356
|
-
console.warn(`Failed to remove file ${filepath}:`, error);
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
async function removeClaudeGeneratedFiles() {
|
|
360
|
-
const filesToRemove = ["CLAUDE.md", ".claude/memories"];
|
|
361
|
-
for (const fileOrDir of filesToRemove) {
|
|
362
|
-
if (fileOrDir.endsWith("/memories")) {
|
|
363
|
-
await removeDirectory(fileOrDir);
|
|
364
|
-
} else {
|
|
365
|
-
await removeFile(fileOrDir);
|
|
366
|
-
}
|
|
367
|
-
}
|
|
856
|
+
function generateRooIgnore(patterns) {
|
|
857
|
+
const lines = [
|
|
858
|
+
"# Generated by rulesync from .rulesyncignore",
|
|
859
|
+
"# This file is automatically generated. Do not edit manually.",
|
|
860
|
+
"",
|
|
861
|
+
...patterns
|
|
862
|
+
];
|
|
863
|
+
return lines.join("\n");
|
|
368
864
|
}
|
|
369
865
|
|
|
370
866
|
// src/core/generator.ts
|
|
@@ -417,12 +913,16 @@ async function generateForTool(tool, rules, config, baseDir) {
|
|
|
417
913
|
}
|
|
418
914
|
|
|
419
915
|
// src/core/parser.ts
|
|
420
|
-
var
|
|
916
|
+
var import_node_path10 = require("path");
|
|
421
917
|
var import_gray_matter = __toESM(require("gray-matter"));
|
|
422
918
|
async function parseRulesFromDirectory(aiRulesDir) {
|
|
423
|
-
const
|
|
919
|
+
const ignorePatterns = await loadIgnorePatterns();
|
|
920
|
+
const ruleFiles = await findFiles(aiRulesDir, ".md", ignorePatterns.patterns);
|
|
424
921
|
const rules = [];
|
|
425
922
|
const errors = [];
|
|
923
|
+
if (ignorePatterns.patterns.length > 0) {
|
|
924
|
+
console.log(`Loaded ${ignorePatterns.patterns.length} ignore patterns from .rulesyncignore`);
|
|
925
|
+
}
|
|
426
926
|
for (const filepath of ruleFiles) {
|
|
427
927
|
try {
|
|
428
928
|
const rule = await parseRuleFile(filepath);
|
|
@@ -450,7 +950,7 @@ async function parseRuleFile(filepath) {
|
|
|
450
950
|
const parsed = (0, import_gray_matter.default)(content);
|
|
451
951
|
validateFrontmatter(parsed.data, filepath);
|
|
452
952
|
const frontmatter = parsed.data;
|
|
453
|
-
const filename = (0,
|
|
953
|
+
const filename = (0, import_node_path10.basename)(filepath, ".md");
|
|
454
954
|
return {
|
|
455
955
|
frontmatter,
|
|
456
956
|
content: parsed.content,
|
|
@@ -578,6 +1078,148 @@ async function validateRule(rule) {
|
|
|
578
1078
|
};
|
|
579
1079
|
}
|
|
580
1080
|
|
|
1081
|
+
// src/core/mcp-generator.ts
|
|
1082
|
+
var import_node_os = __toESM(require("os"));
|
|
1083
|
+
var import_node_path12 = __toESM(require("path"));
|
|
1084
|
+
|
|
1085
|
+
// src/generators/mcp/index.ts
|
|
1086
|
+
init_claude();
|
|
1087
|
+
init_cline();
|
|
1088
|
+
init_copilot();
|
|
1089
|
+
init_cursor();
|
|
1090
|
+
init_geminicli();
|
|
1091
|
+
init_roo();
|
|
1092
|
+
|
|
1093
|
+
// src/core/mcp-parser.ts
|
|
1094
|
+
var import_node_fs = __toESM(require("fs"));
|
|
1095
|
+
var import_node_path11 = __toESM(require("path"));
|
|
1096
|
+
function parseMcpConfig(projectRoot) {
|
|
1097
|
+
const mcpPath = import_node_path11.default.join(projectRoot, ".rulesync", ".mcp.json");
|
|
1098
|
+
if (!import_node_fs.default.existsSync(mcpPath)) {
|
|
1099
|
+
return null;
|
|
1100
|
+
}
|
|
1101
|
+
try {
|
|
1102
|
+
const content = import_node_fs.default.readFileSync(mcpPath, "utf-8");
|
|
1103
|
+
const rawConfig = JSON.parse(content);
|
|
1104
|
+
if (rawConfig.servers && !rawConfig.mcpServers) {
|
|
1105
|
+
rawConfig.mcpServers = rawConfig.servers;
|
|
1106
|
+
delete rawConfig.servers;
|
|
1107
|
+
}
|
|
1108
|
+
if (!rawConfig.mcpServers || typeof rawConfig.mcpServers !== "object") {
|
|
1109
|
+
throw new Error("Invalid mcp.json: 'mcpServers' field must be an object");
|
|
1110
|
+
}
|
|
1111
|
+
if (rawConfig.tools) {
|
|
1112
|
+
delete rawConfig.tools;
|
|
1113
|
+
}
|
|
1114
|
+
return { mcpServers: rawConfig.mcpServers };
|
|
1115
|
+
} catch (error) {
|
|
1116
|
+
throw new Error(
|
|
1117
|
+
`Failed to parse mcp.json: ${error instanceof Error ? error.message : String(error)}`
|
|
1118
|
+
);
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
// src/core/mcp-generator.ts
|
|
1123
|
+
async function generateMcpConfigs(projectRoot, baseDir) {
|
|
1124
|
+
const results = [];
|
|
1125
|
+
const targetRoot = baseDir || projectRoot;
|
|
1126
|
+
const config = parseMcpConfig(projectRoot);
|
|
1127
|
+
if (!config) {
|
|
1128
|
+
return results;
|
|
1129
|
+
}
|
|
1130
|
+
const generators = [
|
|
1131
|
+
{
|
|
1132
|
+
tool: "claude-project",
|
|
1133
|
+
path: import_node_path12.default.join(targetRoot, ".mcp.json"),
|
|
1134
|
+
generate: () => generateClaudeMcp(config, "project")
|
|
1135
|
+
},
|
|
1136
|
+
{
|
|
1137
|
+
tool: "copilot-editor",
|
|
1138
|
+
path: import_node_path12.default.join(targetRoot, ".vscode", "mcp.json"),
|
|
1139
|
+
generate: () => generateCopilotMcp(config, "editor")
|
|
1140
|
+
},
|
|
1141
|
+
{
|
|
1142
|
+
tool: "cursor-project",
|
|
1143
|
+
path: import_node_path12.default.join(targetRoot, ".cursor", "mcp.json"),
|
|
1144
|
+
generate: () => generateCursorMcp(config, "project")
|
|
1145
|
+
},
|
|
1146
|
+
{
|
|
1147
|
+
tool: "cline-project",
|
|
1148
|
+
path: import_node_path12.default.join(targetRoot, ".cline", "mcp.json"),
|
|
1149
|
+
generate: () => generateClineMcp(config, "project")
|
|
1150
|
+
},
|
|
1151
|
+
{
|
|
1152
|
+
tool: "gemini-project",
|
|
1153
|
+
path: import_node_path12.default.join(targetRoot, ".gemini", "settings.json"),
|
|
1154
|
+
generate: () => generateGeminiCliMcp(config, "project")
|
|
1155
|
+
},
|
|
1156
|
+
{
|
|
1157
|
+
tool: "roo-project",
|
|
1158
|
+
path: import_node_path12.default.join(targetRoot, ".roo", "mcp.json"),
|
|
1159
|
+
generate: () => generateRooMcp(config, "project")
|
|
1160
|
+
}
|
|
1161
|
+
];
|
|
1162
|
+
if (!baseDir) {
|
|
1163
|
+
generators.push(
|
|
1164
|
+
{
|
|
1165
|
+
tool: "claude-global",
|
|
1166
|
+
path: import_node_path12.default.join(import_node_os.default.homedir(), ".claude", "settings.json"),
|
|
1167
|
+
generate: () => generateClaudeMcp(config, "global")
|
|
1168
|
+
},
|
|
1169
|
+
{
|
|
1170
|
+
tool: "cursor-global",
|
|
1171
|
+
path: import_node_path12.default.join(import_node_os.default.homedir(), ".cursor", "mcp.json"),
|
|
1172
|
+
generate: () => generateCursorMcp(config, "global")
|
|
1173
|
+
},
|
|
1174
|
+
{
|
|
1175
|
+
tool: "gemini-global",
|
|
1176
|
+
path: import_node_path12.default.join(import_node_os.default.homedir(), ".gemini", "settings.json"),
|
|
1177
|
+
generate: () => generateGeminiCliMcp(config, "global")
|
|
1178
|
+
}
|
|
1179
|
+
);
|
|
1180
|
+
}
|
|
1181
|
+
for (const generator of generators) {
|
|
1182
|
+
try {
|
|
1183
|
+
const content = generator.generate();
|
|
1184
|
+
const parsed = JSON.parse(content);
|
|
1185
|
+
if (generator.tool.includes("claude") || generator.tool.includes("cline") || generator.tool.includes("cursor") || generator.tool.includes("gemini") || generator.tool.includes("roo")) {
|
|
1186
|
+
if (!parsed.mcpServers || Object.keys(parsed.mcpServers).length === 0) {
|
|
1187
|
+
results.push({
|
|
1188
|
+
tool: generator.tool,
|
|
1189
|
+
path: generator.path,
|
|
1190
|
+
status: "skipped"
|
|
1191
|
+
});
|
|
1192
|
+
continue;
|
|
1193
|
+
}
|
|
1194
|
+
} else if (generator.tool.includes("copilot")) {
|
|
1195
|
+
const key = generator.tool.includes("codingAgent") ? "mcpServers" : "servers";
|
|
1196
|
+
if (!parsed[key] || Object.keys(parsed[key]).length === 0) {
|
|
1197
|
+
results.push({
|
|
1198
|
+
tool: generator.tool,
|
|
1199
|
+
path: generator.path,
|
|
1200
|
+
status: "skipped"
|
|
1201
|
+
});
|
|
1202
|
+
continue;
|
|
1203
|
+
}
|
|
1204
|
+
}
|
|
1205
|
+
await writeFileContent(generator.path, content);
|
|
1206
|
+
results.push({
|
|
1207
|
+
tool: generator.tool,
|
|
1208
|
+
path: generator.path,
|
|
1209
|
+
status: "success"
|
|
1210
|
+
});
|
|
1211
|
+
} catch (error) {
|
|
1212
|
+
results.push({
|
|
1213
|
+
tool: generator.tool,
|
|
1214
|
+
path: generator.path,
|
|
1215
|
+
status: "error",
|
|
1216
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1217
|
+
});
|
|
1218
|
+
}
|
|
1219
|
+
}
|
|
1220
|
+
return results;
|
|
1221
|
+
}
|
|
1222
|
+
|
|
581
1223
|
// src/cli/commands/generate.ts
|
|
582
1224
|
async function generateCommand(options = {}) {
|
|
583
1225
|
const config = getDefaultConfig();
|
|
@@ -658,6 +1300,30 @@ Generating configurations for base directory: ${baseDir}`);
|
|
|
658
1300
|
}
|
|
659
1301
|
console.log(`
|
|
660
1302
|
\u{1F389} Successfully generated ${totalOutputs} configuration file(s)!`);
|
|
1303
|
+
if (options.verbose) {
|
|
1304
|
+
console.log("\nGenerating MCP configurations...");
|
|
1305
|
+
}
|
|
1306
|
+
for (const baseDir of baseDirs) {
|
|
1307
|
+
const mcpResults = await generateMcpConfigs(
|
|
1308
|
+
process.cwd(),
|
|
1309
|
+
baseDir === process.cwd() ? void 0 : baseDir
|
|
1310
|
+
);
|
|
1311
|
+
if (mcpResults.length === 0) {
|
|
1312
|
+
if (options.verbose) {
|
|
1313
|
+
console.log(`No MCP configuration found for ${baseDir}`);
|
|
1314
|
+
}
|
|
1315
|
+
continue;
|
|
1316
|
+
}
|
|
1317
|
+
for (const result of mcpResults) {
|
|
1318
|
+
if (result.status === "success") {
|
|
1319
|
+
console.log(`\u2705 Generated ${result.tool} MCP configuration: ${result.path}`);
|
|
1320
|
+
} else if (result.status === "error") {
|
|
1321
|
+
console.error(`\u274C Failed to generate ${result.tool} MCP configuration: ${result.error}`);
|
|
1322
|
+
} else if (options.verbose && result.status === "skipped") {
|
|
1323
|
+
console.log(`\u23ED\uFE0F Skipped ${result.tool} MCP configuration (no servers configured)`);
|
|
1324
|
+
}
|
|
1325
|
+
}
|
|
1326
|
+
}
|
|
661
1327
|
} catch (error) {
|
|
662
1328
|
console.error("\u274C Failed to generate configurations:", error);
|
|
663
1329
|
process.exit(1);
|
|
@@ -665,25 +1331,36 @@ Generating configurations for base directory: ${baseDir}`);
|
|
|
665
1331
|
}
|
|
666
1332
|
|
|
667
1333
|
// src/cli/commands/gitignore.ts
|
|
668
|
-
var
|
|
669
|
-
var
|
|
1334
|
+
var import_node_fs2 = require("fs");
|
|
1335
|
+
var import_node_path13 = require("path");
|
|
670
1336
|
var gitignoreCommand = async () => {
|
|
671
|
-
const gitignorePath = (0,
|
|
1337
|
+
const gitignorePath = (0, import_node_path13.join)(process.cwd(), ".gitignore");
|
|
672
1338
|
const rulesFilesToIgnore = [
|
|
673
1339
|
"# Generated by rulesync - AI tool configuration files",
|
|
674
1340
|
"**/.github/copilot-instructions.md",
|
|
675
1341
|
"**/.github/instructions/",
|
|
676
1342
|
"**/.cursor/rules/",
|
|
1343
|
+
"**/.cursorignore",
|
|
677
1344
|
"**/.clinerules/",
|
|
1345
|
+
"**/.clineignore",
|
|
678
1346
|
"**/CLAUDE.md",
|
|
679
1347
|
"**/.claude/memories/",
|
|
680
1348
|
"**/.roo/rules/",
|
|
1349
|
+
"**/.rooignore",
|
|
1350
|
+
"**/.copilotignore",
|
|
681
1351
|
"**/GEMINI.md",
|
|
682
|
-
"**/.gemini/memories/"
|
|
1352
|
+
"**/.gemini/memories/",
|
|
1353
|
+
"**/.aiexclude",
|
|
1354
|
+
"**/.mcp.json",
|
|
1355
|
+
"**/.cursor/mcp.json",
|
|
1356
|
+
"**/.cline/mcp.json",
|
|
1357
|
+
"**/.vscode/mcp.json",
|
|
1358
|
+
"**/.gemini/settings.json",
|
|
1359
|
+
"**/.roo/mcp.json"
|
|
683
1360
|
];
|
|
684
1361
|
let gitignoreContent = "";
|
|
685
|
-
if ((0,
|
|
686
|
-
gitignoreContent = (0,
|
|
1362
|
+
if ((0, import_node_fs2.existsSync)(gitignorePath)) {
|
|
1363
|
+
gitignoreContent = (0, import_node_fs2.readFileSync)(gitignorePath, "utf-8");
|
|
687
1364
|
}
|
|
688
1365
|
const linesToAdd = [];
|
|
689
1366
|
for (const rule of rulesFilesToIgnore) {
|
|
@@ -700,7 +1377,7 @@ var gitignoreCommand = async () => {
|
|
|
700
1377
|
${linesToAdd.join("\n")}
|
|
701
1378
|
` : `${linesToAdd.join("\n")}
|
|
702
1379
|
`;
|
|
703
|
-
(0,
|
|
1380
|
+
(0, import_node_fs2.writeFileSync)(gitignorePath, newContent);
|
|
704
1381
|
console.log(`\u2705 .gitignore\u306B${linesToAdd.length}\u500B\u306E\u30EB\u30FC\u30EB\u3092\u8FFD\u52A0\u3057\u307E\u3057\u305F:`);
|
|
705
1382
|
for (const line of linesToAdd) {
|
|
706
1383
|
if (!line.startsWith("#")) {
|
|
@@ -710,15 +1387,17 @@ ${linesToAdd.join("\n")}
|
|
|
710
1387
|
};
|
|
711
1388
|
|
|
712
1389
|
// src/core/importer.ts
|
|
713
|
-
var
|
|
1390
|
+
var import_node_path20 = require("path");
|
|
714
1391
|
var import_gray_matter4 = __toESM(require("gray-matter"));
|
|
715
1392
|
|
|
716
1393
|
// src/parsers/claudecode.ts
|
|
717
|
-
var
|
|
1394
|
+
var import_node_path14 = require("path");
|
|
718
1395
|
async function parseClaudeConfiguration(baseDir = process.cwd()) {
|
|
719
1396
|
const errors = [];
|
|
720
1397
|
const rules = [];
|
|
721
|
-
|
|
1398
|
+
let ignorePatterns;
|
|
1399
|
+
let mcpServers;
|
|
1400
|
+
const claudeFilePath = (0, import_node_path14.join)(baseDir, "CLAUDE.md");
|
|
722
1401
|
if (!await fileExists(claudeFilePath)) {
|
|
723
1402
|
errors.push("CLAUDE.md file not found");
|
|
724
1403
|
return { rules, errors };
|
|
@@ -729,16 +1408,32 @@ async function parseClaudeConfiguration(baseDir = process.cwd()) {
|
|
|
729
1408
|
if (mainRule) {
|
|
730
1409
|
rules.push(mainRule);
|
|
731
1410
|
}
|
|
732
|
-
const memoryDir = (0,
|
|
1411
|
+
const memoryDir = (0, import_node_path14.join)(baseDir, ".claude", "memories");
|
|
733
1412
|
if (await fileExists(memoryDir)) {
|
|
734
1413
|
const memoryRules = await parseClaudeMemoryFiles(memoryDir);
|
|
735
1414
|
rules.push(...memoryRules);
|
|
736
1415
|
}
|
|
1416
|
+
const settingsPath = (0, import_node_path14.join)(baseDir, ".claude", "settings.json");
|
|
1417
|
+
if (await fileExists(settingsPath)) {
|
|
1418
|
+
const settingsResult = await parseClaudeSettings(settingsPath);
|
|
1419
|
+
if (settingsResult.ignorePatterns) {
|
|
1420
|
+
ignorePatterns = settingsResult.ignorePatterns;
|
|
1421
|
+
}
|
|
1422
|
+
if (settingsResult.mcpServers) {
|
|
1423
|
+
mcpServers = settingsResult.mcpServers;
|
|
1424
|
+
}
|
|
1425
|
+
errors.push(...settingsResult.errors);
|
|
1426
|
+
}
|
|
737
1427
|
} catch (error) {
|
|
738
1428
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
739
1429
|
errors.push(`Failed to parse Claude configuration: ${errorMessage}`);
|
|
740
1430
|
}
|
|
741
|
-
return {
|
|
1431
|
+
return {
|
|
1432
|
+
rules,
|
|
1433
|
+
errors,
|
|
1434
|
+
...ignorePatterns && { ignorePatterns },
|
|
1435
|
+
...mcpServers && { mcpServers }
|
|
1436
|
+
};
|
|
742
1437
|
}
|
|
743
1438
|
function parseClaudeMainFile(content, filepath) {
|
|
744
1439
|
const lines = content.split("\n");
|
|
@@ -775,10 +1470,10 @@ async function parseClaudeMemoryFiles(memoryDir) {
|
|
|
775
1470
|
const files = await readdir2(memoryDir);
|
|
776
1471
|
for (const file of files) {
|
|
777
1472
|
if (file.endsWith(".md")) {
|
|
778
|
-
const filePath = (0,
|
|
1473
|
+
const filePath = (0, import_node_path14.join)(memoryDir, file);
|
|
779
1474
|
const content = await readFileContent(filePath);
|
|
780
1475
|
if (content.trim()) {
|
|
781
|
-
const filename = (0,
|
|
1476
|
+
const filename = (0, import_node_path14.basename)(file, ".md");
|
|
782
1477
|
const frontmatter = {
|
|
783
1478
|
root: false,
|
|
784
1479
|
targets: ["claudecode"],
|
|
@@ -798,47 +1493,113 @@ async function parseClaudeMemoryFiles(memoryDir) {
|
|
|
798
1493
|
}
|
|
799
1494
|
return rules;
|
|
800
1495
|
}
|
|
1496
|
+
async function parseClaudeSettings(settingsPath) {
|
|
1497
|
+
const errors = [];
|
|
1498
|
+
let ignorePatterns;
|
|
1499
|
+
let mcpServers;
|
|
1500
|
+
try {
|
|
1501
|
+
const content = await readFileContent(settingsPath);
|
|
1502
|
+
const settings = JSON.parse(content);
|
|
1503
|
+
if (settings.permissions?.deny) {
|
|
1504
|
+
const readPatterns = settings.permissions.deny.filter((rule) => rule.startsWith("Read(") && rule.endsWith(")")).map((rule) => {
|
|
1505
|
+
const match = rule.match(/^Read\((.+)\)$/);
|
|
1506
|
+
return match ? match[1] : null;
|
|
1507
|
+
}).filter((pattern) => pattern !== null);
|
|
1508
|
+
if (readPatterns.length > 0) {
|
|
1509
|
+
ignorePatterns = readPatterns;
|
|
1510
|
+
}
|
|
1511
|
+
}
|
|
1512
|
+
if (settings.mcpServers && Object.keys(settings.mcpServers).length > 0) {
|
|
1513
|
+
mcpServers = settings.mcpServers;
|
|
1514
|
+
}
|
|
1515
|
+
} catch (error) {
|
|
1516
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1517
|
+
errors.push(`Failed to parse settings.json: ${errorMessage}`);
|
|
1518
|
+
}
|
|
1519
|
+
return {
|
|
1520
|
+
errors,
|
|
1521
|
+
...ignorePatterns && { ignorePatterns },
|
|
1522
|
+
...mcpServers && { mcpServers }
|
|
1523
|
+
};
|
|
1524
|
+
}
|
|
801
1525
|
|
|
802
1526
|
// src/parsers/cline.ts
|
|
803
|
-
var
|
|
1527
|
+
var import_node_path15 = require("path");
|
|
804
1528
|
async function parseClineConfiguration(baseDir = process.cwd()) {
|
|
805
1529
|
const errors = [];
|
|
806
1530
|
const rules = [];
|
|
807
|
-
const clineFilePath = (0,
|
|
808
|
-
if (
|
|
809
|
-
|
|
810
|
-
|
|
1531
|
+
const clineFilePath = (0, import_node_path15.join)(baseDir, ".cline", "instructions.md");
|
|
1532
|
+
if (await fileExists(clineFilePath)) {
|
|
1533
|
+
try {
|
|
1534
|
+
const content = await readFileContent(clineFilePath);
|
|
1535
|
+
if (content.trim()) {
|
|
1536
|
+
const frontmatter = {
|
|
1537
|
+
root: false,
|
|
1538
|
+
targets: ["cline"],
|
|
1539
|
+
description: "Cline instructions",
|
|
1540
|
+
globs: ["**/*"]
|
|
1541
|
+
};
|
|
1542
|
+
rules.push({
|
|
1543
|
+
frontmatter,
|
|
1544
|
+
content: content.trim(),
|
|
1545
|
+
filename: "cline-instructions",
|
|
1546
|
+
filepath: clineFilePath
|
|
1547
|
+
});
|
|
1548
|
+
}
|
|
1549
|
+
} catch (error) {
|
|
1550
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1551
|
+
errors.push(`Failed to parse .cline/instructions.md: ${errorMessage}`);
|
|
1552
|
+
}
|
|
811
1553
|
}
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
const
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
1554
|
+
const clinerulesDirPath = (0, import_node_path15.join)(baseDir, ".clinerules");
|
|
1555
|
+
if (await fileExists(clinerulesDirPath)) {
|
|
1556
|
+
try {
|
|
1557
|
+
const { readdir: readdir2 } = await import("fs/promises");
|
|
1558
|
+
const files = await readdir2(clinerulesDirPath);
|
|
1559
|
+
for (const file of files) {
|
|
1560
|
+
if (file.endsWith(".md")) {
|
|
1561
|
+
const filePath = (0, import_node_path15.join)(clinerulesDirPath, file);
|
|
1562
|
+
try {
|
|
1563
|
+
const content = await readFileContent(filePath);
|
|
1564
|
+
if (content.trim()) {
|
|
1565
|
+
const filename = file.replace(".md", "");
|
|
1566
|
+
const frontmatter = {
|
|
1567
|
+
root: false,
|
|
1568
|
+
targets: ["cline"],
|
|
1569
|
+
description: `Cline rule: ${filename}`,
|
|
1570
|
+
globs: ["**/*"]
|
|
1571
|
+
};
|
|
1572
|
+
rules.push({
|
|
1573
|
+
frontmatter,
|
|
1574
|
+
content: content.trim(),
|
|
1575
|
+
filename: `cline-${filename}`,
|
|
1576
|
+
filepath: filePath
|
|
1577
|
+
});
|
|
1578
|
+
}
|
|
1579
|
+
} catch (error) {
|
|
1580
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1581
|
+
errors.push(`Failed to parse ${filePath}: ${errorMessage}`);
|
|
1582
|
+
}
|
|
1583
|
+
}
|
|
1584
|
+
}
|
|
1585
|
+
} catch (error) {
|
|
1586
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1587
|
+
errors.push(`Failed to parse .clinerules files: ${errorMessage}`);
|
|
827
1588
|
}
|
|
828
|
-
}
|
|
829
|
-
|
|
830
|
-
errors.push(
|
|
1589
|
+
}
|
|
1590
|
+
if (rules.length === 0) {
|
|
1591
|
+
errors.push("No Cline configuration files found (.cline/instructions.md or .clinerules/*.md)");
|
|
831
1592
|
}
|
|
832
1593
|
return { rules, errors };
|
|
833
1594
|
}
|
|
834
1595
|
|
|
835
1596
|
// src/parsers/copilot.ts
|
|
836
|
-
var
|
|
1597
|
+
var import_node_path16 = require("path");
|
|
837
1598
|
var import_gray_matter2 = __toESM(require("gray-matter"));
|
|
838
1599
|
async function parseCopilotConfiguration(baseDir = process.cwd()) {
|
|
839
1600
|
const errors = [];
|
|
840
1601
|
const rules = [];
|
|
841
|
-
const copilotFilePath = (0,
|
|
1602
|
+
const copilotFilePath = (0, import_node_path16.join)(baseDir, ".github", "copilot-instructions.md");
|
|
842
1603
|
if (await fileExists(copilotFilePath)) {
|
|
843
1604
|
try {
|
|
844
1605
|
const rawContent = await readFileContent(copilotFilePath);
|
|
@@ -863,19 +1624,19 @@ async function parseCopilotConfiguration(baseDir = process.cwd()) {
|
|
|
863
1624
|
errors.push(`Failed to parse copilot-instructions.md: ${errorMessage}`);
|
|
864
1625
|
}
|
|
865
1626
|
}
|
|
866
|
-
const instructionsDir = (0,
|
|
1627
|
+
const instructionsDir = (0, import_node_path16.join)(baseDir, ".github", "instructions");
|
|
867
1628
|
if (await fileExists(instructionsDir)) {
|
|
868
1629
|
try {
|
|
869
1630
|
const { readdir: readdir2 } = await import("fs/promises");
|
|
870
1631
|
const files = await readdir2(instructionsDir);
|
|
871
1632
|
for (const file of files) {
|
|
872
1633
|
if (file.endsWith(".instructions.md")) {
|
|
873
|
-
const filePath = (0,
|
|
1634
|
+
const filePath = (0, import_node_path16.join)(instructionsDir, file);
|
|
874
1635
|
const rawContent = await readFileContent(filePath);
|
|
875
1636
|
const parsed = (0, import_gray_matter2.default)(rawContent);
|
|
876
1637
|
const content = parsed.content.trim();
|
|
877
1638
|
if (content) {
|
|
878
|
-
const filename = (0,
|
|
1639
|
+
const filename = (0, import_node_path16.basename)(file, ".instructions.md");
|
|
879
1640
|
const frontmatter = {
|
|
880
1641
|
root: false,
|
|
881
1642
|
targets: ["copilot"],
|
|
@@ -905,7 +1666,7 @@ async function parseCopilotConfiguration(baseDir = process.cwd()) {
|
|
|
905
1666
|
}
|
|
906
1667
|
|
|
907
1668
|
// src/parsers/cursor.ts
|
|
908
|
-
var
|
|
1669
|
+
var import_node_path17 = require("path");
|
|
909
1670
|
var import_gray_matter3 = __toESM(require("gray-matter"));
|
|
910
1671
|
var import_js_yaml = __toESM(require("js-yaml"));
|
|
911
1672
|
var customMatterOptions = {
|
|
@@ -929,7 +1690,9 @@ var customMatterOptions = {
|
|
|
929
1690
|
async function parseCursorConfiguration(baseDir = process.cwd()) {
|
|
930
1691
|
const errors = [];
|
|
931
1692
|
const rules = [];
|
|
932
|
-
|
|
1693
|
+
let ignorePatterns;
|
|
1694
|
+
let mcpServers;
|
|
1695
|
+
const cursorFilePath = (0, import_node_path17.join)(baseDir, ".cursorrules");
|
|
933
1696
|
if (await fileExists(cursorFilePath)) {
|
|
934
1697
|
try {
|
|
935
1698
|
const rawContent = await readFileContent(cursorFilePath);
|
|
@@ -954,20 +1717,20 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
|
|
|
954
1717
|
errors.push(`Failed to parse .cursorrules file: ${errorMessage}`);
|
|
955
1718
|
}
|
|
956
1719
|
}
|
|
957
|
-
const cursorRulesDir = (0,
|
|
1720
|
+
const cursorRulesDir = (0, import_node_path17.join)(baseDir, ".cursor", "rules");
|
|
958
1721
|
if (await fileExists(cursorRulesDir)) {
|
|
959
1722
|
try {
|
|
960
1723
|
const { readdir: readdir2 } = await import("fs/promises");
|
|
961
1724
|
const files = await readdir2(cursorRulesDir);
|
|
962
1725
|
for (const file of files) {
|
|
963
1726
|
if (file.endsWith(".mdc")) {
|
|
964
|
-
const filePath = (0,
|
|
1727
|
+
const filePath = (0, import_node_path17.join)(cursorRulesDir, file);
|
|
965
1728
|
try {
|
|
966
1729
|
const rawContent = await readFileContent(filePath);
|
|
967
1730
|
const parsed = (0, import_gray_matter3.default)(rawContent, customMatterOptions);
|
|
968
1731
|
const content = parsed.content.trim();
|
|
969
1732
|
if (content) {
|
|
970
|
-
const filename = (0,
|
|
1733
|
+
const filename = (0, import_node_path17.basename)(file, ".mdc");
|
|
971
1734
|
const frontmatter = {
|
|
972
1735
|
root: false,
|
|
973
1736
|
targets: ["cursor"],
|
|
@@ -995,38 +1758,244 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
|
|
|
995
1758
|
if (rules.length === 0) {
|
|
996
1759
|
errors.push("No Cursor configuration files found (.cursorrules or .cursor/rules/*.mdc)");
|
|
997
1760
|
}
|
|
998
|
-
|
|
1761
|
+
const cursorIgnorePath = (0, import_node_path17.join)(baseDir, ".cursorignore");
|
|
1762
|
+
if (await fileExists(cursorIgnorePath)) {
|
|
1763
|
+
try {
|
|
1764
|
+
const content = await readFileContent(cursorIgnorePath);
|
|
1765
|
+
const patterns = content.split("\n").map((line) => line.trim()).filter((line) => line && !line.startsWith("#"));
|
|
1766
|
+
if (patterns.length > 0) {
|
|
1767
|
+
ignorePatterns = patterns;
|
|
1768
|
+
}
|
|
1769
|
+
} catch (error) {
|
|
1770
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1771
|
+
errors.push(`Failed to parse .cursorignore: ${errorMessage}`);
|
|
1772
|
+
}
|
|
1773
|
+
}
|
|
1774
|
+
const cursorMcpPath = (0, import_node_path17.join)(baseDir, ".cursor", "mcp.json");
|
|
1775
|
+
if (await fileExists(cursorMcpPath)) {
|
|
1776
|
+
try {
|
|
1777
|
+
const content = await readFileContent(cursorMcpPath);
|
|
1778
|
+
const mcp = JSON.parse(content);
|
|
1779
|
+
if (mcp.mcpServers && Object.keys(mcp.mcpServers).length > 0) {
|
|
1780
|
+
mcpServers = mcp.mcpServers;
|
|
1781
|
+
}
|
|
1782
|
+
} catch (error) {
|
|
1783
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1784
|
+
errors.push(`Failed to parse .cursor/mcp.json: ${errorMessage}`);
|
|
1785
|
+
}
|
|
1786
|
+
}
|
|
1787
|
+
return {
|
|
1788
|
+
rules,
|
|
1789
|
+
errors,
|
|
1790
|
+
...ignorePatterns && { ignorePatterns },
|
|
1791
|
+
...mcpServers && { mcpServers }
|
|
1792
|
+
};
|
|
999
1793
|
}
|
|
1000
1794
|
|
|
1001
|
-
// src/parsers/
|
|
1002
|
-
var
|
|
1003
|
-
async function
|
|
1795
|
+
// src/parsers/geminicli.ts
|
|
1796
|
+
var import_node_path18 = require("path");
|
|
1797
|
+
async function parseGeminiConfiguration(baseDir = process.cwd()) {
|
|
1004
1798
|
const errors = [];
|
|
1005
1799
|
const rules = [];
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1800
|
+
let ignorePatterns;
|
|
1801
|
+
let mcpServers;
|
|
1802
|
+
const geminiFilePath = (0, import_node_path18.join)(baseDir, "GEMINI.md");
|
|
1803
|
+
if (!await fileExists(geminiFilePath)) {
|
|
1804
|
+
errors.push("GEMINI.md file not found");
|
|
1009
1805
|
return { rules, errors };
|
|
1010
1806
|
}
|
|
1011
1807
|
try {
|
|
1012
|
-
const
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
rules.push(
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1808
|
+
const geminiContent = await readFileContent(geminiFilePath);
|
|
1809
|
+
const mainRule = parseGeminiMainFile(geminiContent, geminiFilePath);
|
|
1810
|
+
if (mainRule) {
|
|
1811
|
+
rules.push(mainRule);
|
|
1812
|
+
}
|
|
1813
|
+
const memoryDir = (0, import_node_path18.join)(baseDir, ".gemini", "memories");
|
|
1814
|
+
if (await fileExists(memoryDir)) {
|
|
1815
|
+
const memoryRules = await parseGeminiMemoryFiles(memoryDir);
|
|
1816
|
+
rules.push(...memoryRules);
|
|
1817
|
+
}
|
|
1818
|
+
const settingsPath = (0, import_node_path18.join)(baseDir, ".gemini", "settings.json");
|
|
1819
|
+
if (await fileExists(settingsPath)) {
|
|
1820
|
+
const settingsResult = await parseGeminiSettings(settingsPath);
|
|
1821
|
+
if (settingsResult.ignorePatterns) {
|
|
1822
|
+
ignorePatterns = settingsResult.ignorePatterns;
|
|
1823
|
+
}
|
|
1824
|
+
if (settingsResult.mcpServers) {
|
|
1825
|
+
mcpServers = settingsResult.mcpServers;
|
|
1826
|
+
}
|
|
1827
|
+
errors.push(...settingsResult.errors);
|
|
1828
|
+
}
|
|
1829
|
+
const aiexcludePath = (0, import_node_path18.join)(baseDir, ".aiexclude");
|
|
1830
|
+
if (await fileExists(aiexcludePath)) {
|
|
1831
|
+
const aiexcludePatterns = await parseAiexclude(aiexcludePath);
|
|
1832
|
+
if (aiexcludePatterns.length > 0) {
|
|
1833
|
+
ignorePatterns = ignorePatterns ? [...ignorePatterns, ...aiexcludePatterns] : aiexcludePatterns;
|
|
1834
|
+
}
|
|
1835
|
+
}
|
|
1836
|
+
} catch (error) {
|
|
1837
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1838
|
+
errors.push(`Failed to parse Gemini configuration: ${errorMessage}`);
|
|
1839
|
+
}
|
|
1840
|
+
return {
|
|
1841
|
+
rules,
|
|
1842
|
+
errors,
|
|
1843
|
+
...ignorePatterns && { ignorePatterns },
|
|
1844
|
+
...mcpServers && { mcpServers }
|
|
1845
|
+
};
|
|
1846
|
+
}
|
|
1847
|
+
function parseGeminiMainFile(content, filepath) {
|
|
1848
|
+
const lines = content.split("\n");
|
|
1849
|
+
let contentStartIndex = 0;
|
|
1850
|
+
if (lines.some((line) => line.includes("| Document | Description | File Patterns |"))) {
|
|
1851
|
+
const tableEndIndex = lines.findIndex(
|
|
1852
|
+
(line, index) => index > 0 && line.trim() === "" && lines[index - 1]?.includes("|") && !lines[index + 1]?.includes("|")
|
|
1853
|
+
);
|
|
1854
|
+
if (tableEndIndex !== -1) {
|
|
1855
|
+
contentStartIndex = tableEndIndex + 1;
|
|
1856
|
+
}
|
|
1857
|
+
}
|
|
1858
|
+
const mainContent = lines.slice(contentStartIndex).join("\n").trim();
|
|
1859
|
+
if (!mainContent) {
|
|
1860
|
+
return null;
|
|
1861
|
+
}
|
|
1862
|
+
const frontmatter = {
|
|
1863
|
+
root: false,
|
|
1864
|
+
targets: ["geminicli"],
|
|
1865
|
+
description: "Main Gemini CLI configuration",
|
|
1866
|
+
globs: ["**/*"]
|
|
1867
|
+
};
|
|
1868
|
+
return {
|
|
1869
|
+
frontmatter,
|
|
1870
|
+
content: mainContent,
|
|
1871
|
+
filename: "gemini-main",
|
|
1872
|
+
filepath
|
|
1873
|
+
};
|
|
1874
|
+
}
|
|
1875
|
+
async function parseGeminiMemoryFiles(memoryDir) {
|
|
1876
|
+
const rules = [];
|
|
1877
|
+
try {
|
|
1878
|
+
const { readdir: readdir2 } = await import("fs/promises");
|
|
1879
|
+
const files = await readdir2(memoryDir);
|
|
1880
|
+
for (const file of files) {
|
|
1881
|
+
if (file.endsWith(".md")) {
|
|
1882
|
+
const filePath = (0, import_node_path18.join)(memoryDir, file);
|
|
1883
|
+
const content = await readFileContent(filePath);
|
|
1884
|
+
if (content.trim()) {
|
|
1885
|
+
const filename = (0, import_node_path18.basename)(file, ".md");
|
|
1886
|
+
const frontmatter = {
|
|
1887
|
+
root: false,
|
|
1888
|
+
targets: ["geminicli"],
|
|
1889
|
+
description: `Memory file: ${filename}`,
|
|
1890
|
+
globs: ["**/*"]
|
|
1891
|
+
};
|
|
1892
|
+
rules.push({
|
|
1893
|
+
frontmatter,
|
|
1894
|
+
content: content.trim(),
|
|
1895
|
+
filename: `gemini-memory-${filename}`,
|
|
1896
|
+
filepath: filePath
|
|
1897
|
+
});
|
|
1898
|
+
}
|
|
1899
|
+
}
|
|
1900
|
+
}
|
|
1901
|
+
} catch (_error) {
|
|
1902
|
+
}
|
|
1903
|
+
return rules;
|
|
1904
|
+
}
|
|
1905
|
+
async function parseGeminiSettings(settingsPath) {
|
|
1906
|
+
const errors = [];
|
|
1907
|
+
let mcpServers;
|
|
1908
|
+
try {
|
|
1909
|
+
const content = await readFileContent(settingsPath);
|
|
1910
|
+
const settings = JSON.parse(content);
|
|
1911
|
+
if (settings.mcpServers && Object.keys(settings.mcpServers).length > 0) {
|
|
1912
|
+
mcpServers = settings.mcpServers;
|
|
1026
1913
|
}
|
|
1027
1914
|
} catch (error) {
|
|
1028
1915
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1029
|
-
errors.push(`Failed to parse
|
|
1916
|
+
errors.push(`Failed to parse settings.json: ${errorMessage}`);
|
|
1917
|
+
}
|
|
1918
|
+
return {
|
|
1919
|
+
errors,
|
|
1920
|
+
...mcpServers && { mcpServers }
|
|
1921
|
+
};
|
|
1922
|
+
}
|
|
1923
|
+
async function parseAiexclude(aiexcludePath) {
|
|
1924
|
+
try {
|
|
1925
|
+
const content = await readFileContent(aiexcludePath);
|
|
1926
|
+
const patterns = content.split("\n").map((line) => line.trim()).filter((line) => line && !line.startsWith("#"));
|
|
1927
|
+
return patterns;
|
|
1928
|
+
} catch (_error) {
|
|
1929
|
+
return [];
|
|
1930
|
+
}
|
|
1931
|
+
}
|
|
1932
|
+
|
|
1933
|
+
// src/parsers/roo.ts
|
|
1934
|
+
var import_node_path19 = require("path");
|
|
1935
|
+
async function parseRooConfiguration(baseDir = process.cwd()) {
|
|
1936
|
+
const errors = [];
|
|
1937
|
+
const rules = [];
|
|
1938
|
+
const rooFilePath = (0, import_node_path19.join)(baseDir, ".roo", "instructions.md");
|
|
1939
|
+
if (await fileExists(rooFilePath)) {
|
|
1940
|
+
try {
|
|
1941
|
+
const content = await readFileContent(rooFilePath);
|
|
1942
|
+
if (content.trim()) {
|
|
1943
|
+
const frontmatter = {
|
|
1944
|
+
root: false,
|
|
1945
|
+
targets: ["roo"],
|
|
1946
|
+
description: "Roo Code instructions",
|
|
1947
|
+
globs: ["**/*"]
|
|
1948
|
+
};
|
|
1949
|
+
rules.push({
|
|
1950
|
+
frontmatter,
|
|
1951
|
+
content: content.trim(),
|
|
1952
|
+
filename: "roo-instructions",
|
|
1953
|
+
filepath: rooFilePath
|
|
1954
|
+
});
|
|
1955
|
+
}
|
|
1956
|
+
} catch (error) {
|
|
1957
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1958
|
+
errors.push(`Failed to parse .roo/instructions.md: ${errorMessage}`);
|
|
1959
|
+
}
|
|
1960
|
+
}
|
|
1961
|
+
const rooRulesDir = (0, import_node_path19.join)(baseDir, ".roo", "rules");
|
|
1962
|
+
if (await fileExists(rooRulesDir)) {
|
|
1963
|
+
try {
|
|
1964
|
+
const { readdir: readdir2 } = await import("fs/promises");
|
|
1965
|
+
const files = await readdir2(rooRulesDir);
|
|
1966
|
+
for (const file of files) {
|
|
1967
|
+
if (file.endsWith(".md")) {
|
|
1968
|
+
const filePath = (0, import_node_path19.join)(rooRulesDir, file);
|
|
1969
|
+
try {
|
|
1970
|
+
const content = await readFileContent(filePath);
|
|
1971
|
+
if (content.trim()) {
|
|
1972
|
+
const filename = file.replace(".md", "");
|
|
1973
|
+
const frontmatter = {
|
|
1974
|
+
root: false,
|
|
1975
|
+
targets: ["roo"],
|
|
1976
|
+
description: `Roo rule: ${filename}`,
|
|
1977
|
+
globs: ["**/*"]
|
|
1978
|
+
};
|
|
1979
|
+
rules.push({
|
|
1980
|
+
frontmatter,
|
|
1981
|
+
content: content.trim(),
|
|
1982
|
+
filename: `roo-${filename}`,
|
|
1983
|
+
filepath: filePath
|
|
1984
|
+
});
|
|
1985
|
+
}
|
|
1986
|
+
} catch (error) {
|
|
1987
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1988
|
+
errors.push(`Failed to parse ${filePath}: ${errorMessage}`);
|
|
1989
|
+
}
|
|
1990
|
+
}
|
|
1991
|
+
}
|
|
1992
|
+
} catch (error) {
|
|
1993
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1994
|
+
errors.push(`Failed to parse .roo/rules files: ${errorMessage}`);
|
|
1995
|
+
}
|
|
1996
|
+
}
|
|
1997
|
+
if (rules.length === 0) {
|
|
1998
|
+
errors.push("No Roo Code configuration files found (.roo/instructions.md or .roo/rules/*.md)");
|
|
1030
1999
|
}
|
|
1031
2000
|
return { rules, errors };
|
|
1032
2001
|
}
|
|
@@ -1036,6 +2005,8 @@ async function importConfiguration(options) {
|
|
|
1036
2005
|
const { tool, baseDir = process.cwd(), rulesDir = ".rulesync", verbose = false } = options;
|
|
1037
2006
|
const errors = [];
|
|
1038
2007
|
let rules = [];
|
|
2008
|
+
let ignorePatterns;
|
|
2009
|
+
let mcpServers;
|
|
1039
2010
|
if (verbose) {
|
|
1040
2011
|
console.log(`Importing ${tool} configuration from ${baseDir}...`);
|
|
1041
2012
|
}
|
|
@@ -1045,12 +2016,16 @@ async function importConfiguration(options) {
|
|
|
1045
2016
|
const claudeResult = await parseClaudeConfiguration(baseDir);
|
|
1046
2017
|
rules = claudeResult.rules;
|
|
1047
2018
|
errors.push(...claudeResult.errors);
|
|
2019
|
+
ignorePatterns = claudeResult.ignorePatterns;
|
|
2020
|
+
mcpServers = claudeResult.mcpServers;
|
|
1048
2021
|
break;
|
|
1049
2022
|
}
|
|
1050
2023
|
case "cursor": {
|
|
1051
2024
|
const cursorResult = await parseCursorConfiguration(baseDir);
|
|
1052
2025
|
rules = cursorResult.rules;
|
|
1053
2026
|
errors.push(...cursorResult.errors);
|
|
2027
|
+
ignorePatterns = cursorResult.ignorePatterns;
|
|
2028
|
+
mcpServers = cursorResult.mcpServers;
|
|
1054
2029
|
break;
|
|
1055
2030
|
}
|
|
1056
2031
|
case "copilot": {
|
|
@@ -1071,6 +2046,14 @@ async function importConfiguration(options) {
|
|
|
1071
2046
|
errors.push(...rooResult.errors);
|
|
1072
2047
|
break;
|
|
1073
2048
|
}
|
|
2049
|
+
case "geminicli": {
|
|
2050
|
+
const geminiResult = await parseGeminiConfiguration(baseDir);
|
|
2051
|
+
rules = geminiResult.rules;
|
|
2052
|
+
errors.push(...geminiResult.errors);
|
|
2053
|
+
ignorePatterns = geminiResult.ignorePatterns;
|
|
2054
|
+
mcpServers = geminiResult.mcpServers;
|
|
2055
|
+
break;
|
|
2056
|
+
}
|
|
1074
2057
|
default:
|
|
1075
2058
|
errors.push(`Unsupported tool: ${tool}`);
|
|
1076
2059
|
return { success: false, rulesCreated: 0, errors };
|
|
@@ -1080,10 +2063,10 @@ async function importConfiguration(options) {
|
|
|
1080
2063
|
errors.push(`Failed to parse ${tool} configuration: ${errorMessage}`);
|
|
1081
2064
|
return { success: false, rulesCreated: 0, errors };
|
|
1082
2065
|
}
|
|
1083
|
-
if (rules.length === 0) {
|
|
2066
|
+
if (rules.length === 0 && !ignorePatterns && !mcpServers) {
|
|
1084
2067
|
return { success: false, rulesCreated: 0, errors };
|
|
1085
2068
|
}
|
|
1086
|
-
const rulesDirPath = (0,
|
|
2069
|
+
const rulesDirPath = (0, import_node_path20.join)(baseDir, rulesDir);
|
|
1087
2070
|
try {
|
|
1088
2071
|
const { mkdir: mkdir3 } = await import("fs/promises");
|
|
1089
2072
|
await mkdir3(rulesDirPath, { recursive: true });
|
|
@@ -1097,7 +2080,7 @@ async function importConfiguration(options) {
|
|
|
1097
2080
|
try {
|
|
1098
2081
|
const baseFilename = `${tool}__${rule.filename}`;
|
|
1099
2082
|
const filename = await generateUniqueFilename(rulesDirPath, baseFilename);
|
|
1100
|
-
const filePath = (0,
|
|
2083
|
+
const filePath = (0, import_node_path20.join)(rulesDirPath, `${filename}.md`);
|
|
1101
2084
|
const content = generateRuleFileContent(rule);
|
|
1102
2085
|
await writeFileContent(filePath, content);
|
|
1103
2086
|
rulesCreated++;
|
|
@@ -1109,10 +2092,44 @@ async function importConfiguration(options) {
|
|
|
1109
2092
|
errors.push(`Failed to create rule file for ${rule.filename}: ${errorMessage}`);
|
|
1110
2093
|
}
|
|
1111
2094
|
}
|
|
2095
|
+
let ignoreFileCreated = false;
|
|
2096
|
+
if (ignorePatterns && ignorePatterns.length > 0) {
|
|
2097
|
+
try {
|
|
2098
|
+
const rulesyncignorePath = (0, import_node_path20.join)(baseDir, ".rulesyncignore");
|
|
2099
|
+
const ignoreContent = `${ignorePatterns.join("\n")}
|
|
2100
|
+
`;
|
|
2101
|
+
await writeFileContent(rulesyncignorePath, ignoreContent);
|
|
2102
|
+
ignoreFileCreated = true;
|
|
2103
|
+
if (verbose) {
|
|
2104
|
+
console.log(`\u2705 Created .rulesyncignore with ${ignorePatterns.length} patterns`);
|
|
2105
|
+
}
|
|
2106
|
+
} catch (error) {
|
|
2107
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
2108
|
+
errors.push(`Failed to create .rulesyncignore: ${errorMessage}`);
|
|
2109
|
+
}
|
|
2110
|
+
}
|
|
2111
|
+
let mcpFileCreated = false;
|
|
2112
|
+
if (mcpServers && Object.keys(mcpServers).length > 0) {
|
|
2113
|
+
try {
|
|
2114
|
+
const mcpPath = (0, import_node_path20.join)(baseDir, rulesDir, ".mcp.json");
|
|
2115
|
+
const mcpContent = `${JSON.stringify({ mcpServers }, null, 2)}
|
|
2116
|
+
`;
|
|
2117
|
+
await writeFileContent(mcpPath, mcpContent);
|
|
2118
|
+
mcpFileCreated = true;
|
|
2119
|
+
if (verbose) {
|
|
2120
|
+
console.log(`\u2705 Created .mcp.json with ${Object.keys(mcpServers).length} servers`);
|
|
2121
|
+
}
|
|
2122
|
+
} catch (error) {
|
|
2123
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
2124
|
+
errors.push(`Failed to create .mcp.json: ${errorMessage}`);
|
|
2125
|
+
}
|
|
2126
|
+
}
|
|
1112
2127
|
return {
|
|
1113
|
-
success: rulesCreated > 0,
|
|
2128
|
+
success: rulesCreated > 0 || ignoreFileCreated || mcpFileCreated,
|
|
1114
2129
|
rulesCreated,
|
|
1115
|
-
errors
|
|
2130
|
+
errors,
|
|
2131
|
+
ignoreFileCreated,
|
|
2132
|
+
mcpFileCreated
|
|
1116
2133
|
};
|
|
1117
2134
|
}
|
|
1118
2135
|
function generateRuleFileContent(rule) {
|
|
@@ -1122,7 +2139,7 @@ function generateRuleFileContent(rule) {
|
|
|
1122
2139
|
async function generateUniqueFilename(rulesDir, baseFilename) {
|
|
1123
2140
|
let filename = baseFilename;
|
|
1124
2141
|
let counter = 1;
|
|
1125
|
-
while (await fileExists((0,
|
|
2142
|
+
while (await fileExists((0, import_node_path20.join)(rulesDir, `${filename}.md`))) {
|
|
1126
2143
|
filename = `${baseFilename}-${counter}`;
|
|
1127
2144
|
counter++;
|
|
1128
2145
|
}
|
|
@@ -1140,57 +2157,54 @@ async function importCommand(options = {}) {
|
|
|
1140
2157
|
if (options.geminicli) tools.push("geminicli");
|
|
1141
2158
|
if (tools.length === 0) {
|
|
1142
2159
|
console.error(
|
|
1143
|
-
"\u274C Please specify
|
|
2160
|
+
"\u274C Please specify one tool to import from (--claudecode, --cursor, --copilot, --cline, --roo, --geminicli)"
|
|
1144
2161
|
);
|
|
1145
2162
|
process.exit(1);
|
|
1146
2163
|
}
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
for (const tool of tools) {
|
|
1151
|
-
if (options.verbose) {
|
|
1152
|
-
console.log(`
|
|
1153
|
-
Importing from ${tool}...`);
|
|
1154
|
-
}
|
|
1155
|
-
try {
|
|
1156
|
-
const result = await importConfiguration({
|
|
1157
|
-
tool,
|
|
1158
|
-
verbose: options.verbose ?? false
|
|
1159
|
-
});
|
|
1160
|
-
if (result.success) {
|
|
1161
|
-
console.log(`\u2705 Imported ${result.rulesCreated} rule(s) from ${tool}`);
|
|
1162
|
-
totalRulesCreated += result.rulesCreated;
|
|
1163
|
-
} else if (result.errors.length > 0) {
|
|
1164
|
-
console.warn(`\u26A0\uFE0F Failed to import from ${tool}: ${result.errors[0]}`);
|
|
1165
|
-
if (options.verbose) {
|
|
1166
|
-
allErrors.push(...result.errors);
|
|
1167
|
-
}
|
|
1168
|
-
}
|
|
1169
|
-
} catch (error) {
|
|
1170
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1171
|
-
console.error(`\u274C Error importing from ${tool}: ${errorMessage}`);
|
|
1172
|
-
allErrors.push(`${tool}: ${errorMessage}`);
|
|
1173
|
-
}
|
|
1174
|
-
}
|
|
1175
|
-
if (totalRulesCreated > 0) {
|
|
1176
|
-
console.log(`
|
|
1177
|
-
\u{1F389} Successfully imported ${totalRulesCreated} rule(s) total!`);
|
|
1178
|
-
console.log("You can now run 'rulesync generate' to create tool-specific configurations.");
|
|
1179
|
-
} else {
|
|
1180
|
-
console.warn(
|
|
1181
|
-
"\n\u26A0\uFE0F No rules were imported. Please check that configuration files exist for the selected tools."
|
|
2164
|
+
if (tools.length > 1) {
|
|
2165
|
+
console.error(
|
|
2166
|
+
"\u274C Only one tool can be specified at a time. Please run the import command separately for each tool."
|
|
1182
2167
|
);
|
|
2168
|
+
process.exit(1);
|
|
1183
2169
|
}
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
2170
|
+
const tool = tools[0];
|
|
2171
|
+
if (!tool) {
|
|
2172
|
+
console.error("Error: No tool specified");
|
|
2173
|
+
process.exit(1);
|
|
2174
|
+
}
|
|
2175
|
+
console.log(`Importing configuration files from ${tool}...`);
|
|
2176
|
+
try {
|
|
2177
|
+
const result = await importConfiguration({
|
|
2178
|
+
tool,
|
|
2179
|
+
verbose: options.verbose ?? false
|
|
2180
|
+
});
|
|
2181
|
+
if (result.success) {
|
|
2182
|
+
console.log(`\u2705 Imported ${result.rulesCreated} rule(s) from ${tool}`);
|
|
2183
|
+
if (result.ignoreFileCreated) {
|
|
2184
|
+
console.log("\u2705 Created .rulesyncignore file from ignore patterns");
|
|
2185
|
+
}
|
|
2186
|
+
if (result.mcpFileCreated) {
|
|
2187
|
+
console.log("\u2705 Created .rulesync/.mcp.json file from MCP configuration");
|
|
2188
|
+
}
|
|
2189
|
+
console.log("You can now run 'rulesync generate' to create tool-specific configurations.");
|
|
2190
|
+
} else if (result.errors.length > 0) {
|
|
2191
|
+
console.warn(`\u26A0\uFE0F Failed to import from ${tool}: ${result.errors[0]}`);
|
|
2192
|
+
if (options.verbose && result.errors.length > 1) {
|
|
2193
|
+
console.log("\nDetailed errors:");
|
|
2194
|
+
for (const error of result.errors) {
|
|
2195
|
+
console.log(` - ${error}`);
|
|
2196
|
+
}
|
|
2197
|
+
}
|
|
1188
2198
|
}
|
|
2199
|
+
} catch (error) {
|
|
2200
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
2201
|
+
console.error(`\u274C Error importing from ${tool}: ${errorMessage}`);
|
|
2202
|
+
process.exit(1);
|
|
1189
2203
|
}
|
|
1190
2204
|
}
|
|
1191
2205
|
|
|
1192
2206
|
// src/cli/commands/init.ts
|
|
1193
|
-
var
|
|
2207
|
+
var import_node_path21 = require("path");
|
|
1194
2208
|
async function initCommand() {
|
|
1195
2209
|
const aiRulesDir = ".rulesync";
|
|
1196
2210
|
console.log("Initializing rulesync...");
|
|
@@ -1320,7 +2334,7 @@ globs: ["src/api/**/*.ts", "src/services/**/*.ts", "src/models/**/*.ts"]
|
|
|
1320
2334
|
}
|
|
1321
2335
|
];
|
|
1322
2336
|
for (const file of sampleFiles) {
|
|
1323
|
-
const filepath = (0,
|
|
2337
|
+
const filepath = (0, import_node_path21.join)(aiRulesDir, file.filename);
|
|
1324
2338
|
if (!await fileExists(filepath)) {
|
|
1325
2339
|
await writeFileContent(filepath, file.content);
|
|
1326
2340
|
console.log(`Created ${filepath}`);
|
|
@@ -1433,11 +2447,11 @@ async function watchCommand() {
|
|
|
1433
2447
|
persistent: true
|
|
1434
2448
|
});
|
|
1435
2449
|
let isGenerating = false;
|
|
1436
|
-
const handleChange = async (
|
|
2450
|
+
const handleChange = async (path4) => {
|
|
1437
2451
|
if (isGenerating) return;
|
|
1438
2452
|
isGenerating = true;
|
|
1439
2453
|
console.log(`
|
|
1440
|
-
\u{1F4DD} Detected change in ${
|
|
2454
|
+
\u{1F4DD} Detected change in ${path4}`);
|
|
1441
2455
|
try {
|
|
1442
2456
|
await generateCommand({ verbose: false });
|
|
1443
2457
|
console.log("\u2705 Regenerated configuration files");
|
|
@@ -1447,10 +2461,10 @@ async function watchCommand() {
|
|
|
1447
2461
|
isGenerating = false;
|
|
1448
2462
|
}
|
|
1449
2463
|
};
|
|
1450
|
-
watcher.on("change", handleChange).on("add", handleChange).on("unlink", (
|
|
2464
|
+
watcher.on("change", handleChange).on("add", handleChange).on("unlink", (path4) => {
|
|
1451
2465
|
console.log(`
|
|
1452
|
-
\u{1F5D1}\uFE0F Removed ${
|
|
1453
|
-
handleChange(
|
|
2466
|
+
\u{1F5D1}\uFE0F Removed ${path4}`);
|
|
2467
|
+
handleChange(path4);
|
|
1454
2468
|
}).on("error", (error) => {
|
|
1455
2469
|
console.error("\u274C Watcher error:", error);
|
|
1456
2470
|
});
|
|
@@ -1463,7 +2477,7 @@ async function watchCommand() {
|
|
|
1463
2477
|
|
|
1464
2478
|
// src/cli/index.ts
|
|
1465
2479
|
var program = new import_commander.Command();
|
|
1466
|
-
program.name("rulesync").description("Unified AI rules management CLI tool").version("0.
|
|
2480
|
+
program.name("rulesync").description("Unified AI rules management CLI tool").version("0.36.0");
|
|
1467
2481
|
program.command("init").description("Initialize rulesync in current directory").action(initCommand);
|
|
1468
2482
|
program.command("add <filename>").description("Add a new rule file").action(addCommand);
|
|
1469
2483
|
program.command("gitignore").description("Add generated files to .gitignore").action(gitignoreCommand);
|