rulesync 0.37.0 → 0.38.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/dist/{chunk-TKNVMYAC.mjs → chunk-BEJD7I74.js} +1 -1
- package/dist/{chunk-PBOKMNYU.mjs → chunk-HOVR7QDA.js} +1 -1
- package/dist/{chunk-2BYTQ4LL.mjs → chunk-LQDXGEGC.js} +1 -1
- package/dist/{chunk-3GVVX3G5.mjs → chunk-SPMMXF7R.js} +1 -1
- package/dist/{chunk-KZATM2CQ.mjs → chunk-VZMUQVX4.js} +1 -1
- package/dist/{chunk-2CDVII2R.mjs → chunk-XERXHVPP.js} +1 -1
- package/dist/{claude-2NLZ2CDE.mjs → claude-ZBX7NASK.js} +2 -2
- package/dist/{cline-HUXPTQP7.mjs → cline-XP43H6CB.js} +2 -2
- package/dist/{copilot-376H5OXX.mjs → copilot-4O7P53MT.js} +2 -2
- package/dist/{cursor-XWLBQYWY.mjs → cursor-HSO7S4D3.js} +2 -2
- package/dist/{geminicli-EREPFSDP.mjs → geminicli-UZWXPT2J.js} +2 -2
- package/dist/{index.mjs → index.cjs} +507 -152
- package/dist/index.js +153 -506
- package/dist/{roo-YS23AEWJ.mjs → roo-JSRLCK7Z.js} +2 -2
- package/package.json +7 -18
- /package/dist/{chunk-QQN5GTOV.mjs → chunk-AWKMUY5R.js} +0 -0
- /package/dist/{index.d.mts → index.d.cts} +0 -0
package/dist/index.js
CHANGED
|
@@ -1,376 +1,30 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
-
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
-
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
-
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
-
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
-
mod
|
|
27
|
-
));
|
|
28
|
-
|
|
29
|
-
// src/schemas/mcp.ts
|
|
30
|
-
var import_zod, ToolTargetSchema, WildcardTargetSchema, SpecificTargetsSchema, RulesyncTargetsSchema, McpTransportTypeSchema, McpServerBaseSchema, RulesyncMcpServerSchema;
|
|
31
|
-
var init_mcp = __esm({
|
|
32
|
-
"src/schemas/mcp.ts"() {
|
|
33
|
-
"use strict";
|
|
34
|
-
import_zod = require("zod");
|
|
35
|
-
ToolTargetSchema = import_zod.z.enum([
|
|
36
|
-
"copilot",
|
|
37
|
-
"cursor",
|
|
38
|
-
"cline",
|
|
39
|
-
"claudecode",
|
|
40
|
-
"claude",
|
|
41
|
-
"roo",
|
|
42
|
-
"geminicli"
|
|
43
|
-
]);
|
|
44
|
-
WildcardTargetSchema = import_zod.z.tuple([import_zod.z.literal("*")]);
|
|
45
|
-
SpecificTargetsSchema = import_zod.z.array(ToolTargetSchema);
|
|
46
|
-
RulesyncTargetsSchema = import_zod.z.union([SpecificTargetsSchema, WildcardTargetSchema]);
|
|
47
|
-
McpTransportTypeSchema = import_zod.z.enum(["stdio", "sse", "http"]);
|
|
48
|
-
McpServerBaseSchema = import_zod.z.object({
|
|
49
|
-
command: import_zod.z.string().optional(),
|
|
50
|
-
args: import_zod.z.array(import_zod.z.string()).optional(),
|
|
51
|
-
url: import_zod.z.string().optional(),
|
|
52
|
-
httpUrl: import_zod.z.string().optional(),
|
|
53
|
-
env: import_zod.z.record(import_zod.z.string()).optional(),
|
|
54
|
-
disabled: import_zod.z.boolean().optional(),
|
|
55
|
-
networkTimeout: import_zod.z.number().optional(),
|
|
56
|
-
timeout: import_zod.z.number().optional(),
|
|
57
|
-
trust: import_zod.z.boolean().optional(),
|
|
58
|
-
cwd: import_zod.z.string().optional(),
|
|
59
|
-
transport: McpTransportTypeSchema.optional(),
|
|
60
|
-
type: import_zod.z.enum(["sse", "streamable-http"]).optional(),
|
|
61
|
-
alwaysAllow: import_zod.z.array(import_zod.z.string()).optional(),
|
|
62
|
-
tools: import_zod.z.array(import_zod.z.string()).optional()
|
|
63
|
-
});
|
|
64
|
-
RulesyncMcpServerSchema = McpServerBaseSchema.extend({
|
|
65
|
-
rulesyncTargets: RulesyncTargetsSchema.optional()
|
|
66
|
-
});
|
|
67
|
-
}
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
// src/utils/mcp-helpers.ts
|
|
71
|
-
function shouldIncludeServer(server, targetTool) {
|
|
72
|
-
if (!server.rulesyncTargets || server.rulesyncTargets.length === 0) {
|
|
73
|
-
return true;
|
|
74
|
-
}
|
|
75
|
-
const parsedTargets = RulesyncTargetsSchema.parse(server.rulesyncTargets);
|
|
76
|
-
if (parsedTargets.length === 1 && parsedTargets[0] === "*") {
|
|
77
|
-
return true;
|
|
78
|
-
}
|
|
79
|
-
const validatedTool = ToolTargetSchema.parse(targetTool);
|
|
80
|
-
for (const target of parsedTargets) {
|
|
81
|
-
if (target === validatedTool) {
|
|
82
|
-
return true;
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
return false;
|
|
86
|
-
}
|
|
87
|
-
var init_mcp_helpers = __esm({
|
|
88
|
-
"src/utils/mcp-helpers.ts"() {
|
|
89
|
-
"use strict";
|
|
90
|
-
init_mcp();
|
|
91
|
-
}
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
// src/generators/mcp/claude.ts
|
|
95
|
-
function generateClaudeMcp(config, _target) {
|
|
96
|
-
const claudeSettings = {
|
|
97
|
-
mcpServers: {}
|
|
98
|
-
};
|
|
99
|
-
const shouldInclude = (server) => {
|
|
100
|
-
return shouldIncludeServer(server, "claude");
|
|
101
|
-
};
|
|
102
|
-
for (const [serverName, server] of Object.entries(config.mcpServers)) {
|
|
103
|
-
if (!shouldInclude(server)) continue;
|
|
104
|
-
const claudeServer = {};
|
|
105
|
-
if (server.command) {
|
|
106
|
-
claudeServer.command = server.command;
|
|
107
|
-
if (server.args) claudeServer.args = server.args;
|
|
108
|
-
} else if (server.url || server.httpUrl) {
|
|
109
|
-
const url = server.httpUrl || server.url;
|
|
110
|
-
if (url) {
|
|
111
|
-
claudeServer.url = url;
|
|
112
|
-
}
|
|
113
|
-
if (server.httpUrl) {
|
|
114
|
-
claudeServer.transport = "http";
|
|
115
|
-
} else if (server.transport === "sse") {
|
|
116
|
-
claudeServer.transport = "sse";
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
if (server.env) {
|
|
120
|
-
claudeServer.env = server.env;
|
|
121
|
-
}
|
|
122
|
-
claudeSettings.mcpServers[serverName] = claudeServer;
|
|
123
|
-
}
|
|
124
|
-
return JSON.stringify(claudeSettings, null, 2);
|
|
125
|
-
}
|
|
126
|
-
var init_claude = __esm({
|
|
127
|
-
"src/generators/mcp/claude.ts"() {
|
|
128
|
-
"use strict";
|
|
129
|
-
init_mcp_helpers();
|
|
130
|
-
}
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
// src/generators/mcp/cline.ts
|
|
134
|
-
function generateClineMcp(config, _target) {
|
|
135
|
-
const clineConfig = {
|
|
136
|
-
mcpServers: {}
|
|
137
|
-
};
|
|
138
|
-
const shouldInclude = (server) => {
|
|
139
|
-
return shouldIncludeServer(server, "cline");
|
|
140
|
-
};
|
|
141
|
-
for (const [serverName, server] of Object.entries(config.mcpServers)) {
|
|
142
|
-
if (!shouldInclude(server)) continue;
|
|
143
|
-
const clineServer = {};
|
|
144
|
-
if (server.command) {
|
|
145
|
-
clineServer.command = server.command;
|
|
146
|
-
if (server.args) clineServer.args = server.args;
|
|
147
|
-
} else if (server.url) {
|
|
148
|
-
clineServer.url = server.url;
|
|
149
|
-
}
|
|
150
|
-
if (server.env) {
|
|
151
|
-
clineServer.env = server.env;
|
|
152
|
-
}
|
|
153
|
-
if (server.disabled !== void 0) {
|
|
154
|
-
clineServer.disabled = server.disabled;
|
|
155
|
-
}
|
|
156
|
-
if (server.alwaysAllow) {
|
|
157
|
-
clineServer.alwaysAllow = server.alwaysAllow;
|
|
158
|
-
}
|
|
159
|
-
if (server.networkTimeout !== void 0) {
|
|
160
|
-
clineServer.networkTimeout = server.networkTimeout;
|
|
161
|
-
}
|
|
162
|
-
clineConfig.mcpServers[serverName] = clineServer;
|
|
163
|
-
}
|
|
164
|
-
return JSON.stringify(clineConfig, null, 2);
|
|
165
|
-
}
|
|
166
|
-
var init_cline = __esm({
|
|
167
|
-
"src/generators/mcp/cline.ts"() {
|
|
168
|
-
"use strict";
|
|
169
|
-
init_mcp_helpers();
|
|
170
|
-
}
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
// src/generators/mcp/copilot.ts
|
|
174
|
-
function generateCopilotMcp(config, target) {
|
|
175
|
-
const servers = {};
|
|
176
|
-
const inputs = [];
|
|
177
|
-
const inputMap = /* @__PURE__ */ new Map();
|
|
178
|
-
for (const [serverName, server] of Object.entries(config.mcpServers)) {
|
|
179
|
-
if (!shouldIncludeServer(server, "copilot")) continue;
|
|
180
|
-
const copilotServer = {};
|
|
181
|
-
if (server.command) {
|
|
182
|
-
copilotServer.command = server.command;
|
|
183
|
-
if (server.args) copilotServer.args = server.args;
|
|
184
|
-
} else if (server.url || server.httpUrl) {
|
|
185
|
-
const url = server.httpUrl || server.url;
|
|
186
|
-
if (url) {
|
|
187
|
-
copilotServer.url = url;
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
if (server.env) {
|
|
191
|
-
copilotServer.env = {};
|
|
192
|
-
for (const [key, value] of Object.entries(server.env)) {
|
|
193
|
-
if (target === "editor" && value.includes("SECRET")) {
|
|
194
|
-
const inputId = `${serverName}_${key}`;
|
|
195
|
-
inputMap.set(inputId, value);
|
|
196
|
-
copilotServer.env[key] = `\${input:${inputId}}`;
|
|
197
|
-
inputs.push({
|
|
198
|
-
id: inputId,
|
|
199
|
-
type: "password",
|
|
200
|
-
description: `${key} for ${serverName}`
|
|
201
|
-
});
|
|
202
|
-
} else {
|
|
203
|
-
copilotServer.env[key] = value;
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
if (server.tools) {
|
|
208
|
-
copilotServer.tools = server.tools;
|
|
209
|
-
} else if (server.alwaysAllow) {
|
|
210
|
-
copilotServer.tools = server.alwaysAllow;
|
|
211
|
-
}
|
|
212
|
-
servers[serverName] = copilotServer;
|
|
213
|
-
}
|
|
214
|
-
if (target === "codingAgent") {
|
|
215
|
-
const config2 = { mcpServers: servers };
|
|
216
|
-
return JSON.stringify(config2, null, 2);
|
|
217
|
-
} else {
|
|
218
|
-
const config2 = { servers };
|
|
219
|
-
if (inputs.length > 0) {
|
|
220
|
-
config2.inputs = inputs;
|
|
221
|
-
}
|
|
222
|
-
return JSON.stringify(config2, null, 2);
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
var init_copilot = __esm({
|
|
226
|
-
"src/generators/mcp/copilot.ts"() {
|
|
227
|
-
"use strict";
|
|
228
|
-
init_mcp_helpers();
|
|
229
|
-
}
|
|
230
|
-
});
|
|
231
|
-
|
|
232
|
-
// src/generators/mcp/cursor.ts
|
|
233
|
-
function generateCursorMcp(config, _target) {
|
|
234
|
-
const cursorConfig = {
|
|
235
|
-
mcpServers: {}
|
|
236
|
-
};
|
|
237
|
-
for (const [serverName, server] of Object.entries(config.mcpServers)) {
|
|
238
|
-
if (!shouldIncludeServer(server, "cursor")) continue;
|
|
239
|
-
const cursorServer = {};
|
|
240
|
-
if (server.command) {
|
|
241
|
-
cursorServer.command = server.command;
|
|
242
|
-
if (server.args) cursorServer.args = server.args;
|
|
243
|
-
} else if (server.url || server.httpUrl) {
|
|
244
|
-
const url = server.httpUrl || server.url;
|
|
245
|
-
if (url) {
|
|
246
|
-
cursorServer.url = url;
|
|
247
|
-
}
|
|
248
|
-
if (server.httpUrl || server.transport === "http") {
|
|
249
|
-
cursorServer.type = "streamable-http";
|
|
250
|
-
} else if (server.transport === "sse" || server.type === "sse") {
|
|
251
|
-
cursorServer.type = "sse";
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
if (server.env) {
|
|
255
|
-
cursorServer.env = server.env;
|
|
256
|
-
}
|
|
257
|
-
if (server.cwd) {
|
|
258
|
-
cursorServer.cwd = server.cwd;
|
|
259
|
-
}
|
|
260
|
-
cursorConfig.mcpServers[serverName] = cursorServer;
|
|
261
|
-
}
|
|
262
|
-
return JSON.stringify(cursorConfig, null, 2);
|
|
263
|
-
}
|
|
264
|
-
var init_cursor = __esm({
|
|
265
|
-
"src/generators/mcp/cursor.ts"() {
|
|
266
|
-
"use strict";
|
|
267
|
-
init_mcp_helpers();
|
|
268
|
-
}
|
|
269
|
-
});
|
|
270
|
-
|
|
271
|
-
// src/generators/mcp/geminicli.ts
|
|
272
|
-
function generateGeminiCliMcp(config, _target) {
|
|
273
|
-
const geminiSettings = {
|
|
274
|
-
mcpServers: {}
|
|
275
|
-
};
|
|
276
|
-
for (const [serverName, server] of Object.entries(config.mcpServers)) {
|
|
277
|
-
if (!shouldIncludeServer(server, "geminicli")) continue;
|
|
278
|
-
const geminiServer = {};
|
|
279
|
-
if (server.command) {
|
|
280
|
-
geminiServer.command = server.command;
|
|
281
|
-
if (server.args) geminiServer.args = server.args;
|
|
282
|
-
} else if (server.url || server.httpUrl) {
|
|
283
|
-
if (server.httpUrl) {
|
|
284
|
-
geminiServer.httpUrl = server.httpUrl;
|
|
285
|
-
} else if (server.url) {
|
|
286
|
-
geminiServer.url = server.url;
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
if (server.env) {
|
|
290
|
-
geminiServer.env = {};
|
|
291
|
-
for (const [key, value] of Object.entries(server.env)) {
|
|
292
|
-
if (value.startsWith("${") && value.endsWith("}")) {
|
|
293
|
-
geminiServer.env[key] = value;
|
|
294
|
-
} else {
|
|
295
|
-
geminiServer.env[key] = `\${${value}}`;
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
if (server.timeout !== void 0) {
|
|
300
|
-
geminiServer.timeout = server.timeout;
|
|
301
|
-
}
|
|
302
|
-
if (server.trust !== void 0) {
|
|
303
|
-
geminiServer.trust = server.trust;
|
|
304
|
-
}
|
|
305
|
-
geminiSettings.mcpServers[serverName] = geminiServer;
|
|
306
|
-
}
|
|
307
|
-
return JSON.stringify(geminiSettings, null, 2);
|
|
308
|
-
}
|
|
309
|
-
var init_geminicli = __esm({
|
|
310
|
-
"src/generators/mcp/geminicli.ts"() {
|
|
311
|
-
"use strict";
|
|
312
|
-
init_mcp_helpers();
|
|
313
|
-
}
|
|
314
|
-
});
|
|
315
|
-
|
|
316
|
-
// src/generators/mcp/roo.ts
|
|
317
|
-
function generateRooMcp(config, _target) {
|
|
318
|
-
const rooConfig = {
|
|
319
|
-
mcpServers: {}
|
|
320
|
-
};
|
|
321
|
-
for (const [serverName, server] of Object.entries(config.mcpServers)) {
|
|
322
|
-
if (!shouldIncludeServer(server, "roo")) continue;
|
|
323
|
-
const rooServer = {};
|
|
324
|
-
if (server.command) {
|
|
325
|
-
rooServer.command = server.command;
|
|
326
|
-
if (server.args) rooServer.args = server.args;
|
|
327
|
-
} else if (server.url || server.httpUrl) {
|
|
328
|
-
const url = server.httpUrl || server.url;
|
|
329
|
-
if (url) {
|
|
330
|
-
rooServer.url = url;
|
|
331
|
-
}
|
|
332
|
-
if (server.httpUrl || server.transport === "http") {
|
|
333
|
-
rooServer.type = "streamable-http";
|
|
334
|
-
} else if (server.transport === "sse" || server.type === "sse") {
|
|
335
|
-
rooServer.type = "sse";
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
if (server.env) {
|
|
339
|
-
rooServer.env = {};
|
|
340
|
-
for (const [key, value] of Object.entries(server.env)) {
|
|
341
|
-
if (value.startsWith("${env:") && value.endsWith("}")) {
|
|
342
|
-
rooServer.env[key] = value;
|
|
343
|
-
} else {
|
|
344
|
-
rooServer.env[key] = `\${env:${value}}`;
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
if (server.disabled !== void 0) {
|
|
349
|
-
rooServer.disabled = server.disabled;
|
|
350
|
-
}
|
|
351
|
-
if (server.alwaysAllow) {
|
|
352
|
-
rooServer.alwaysAllow = server.alwaysAllow;
|
|
353
|
-
}
|
|
354
|
-
if (server.networkTimeout !== void 0) {
|
|
355
|
-
rooServer.networkTimeout = Math.max(3e4, Math.min(3e5, server.networkTimeout));
|
|
356
|
-
}
|
|
357
|
-
rooConfig.mcpServers[serverName] = rooServer;
|
|
358
|
-
}
|
|
359
|
-
return JSON.stringify(rooConfig, null, 2);
|
|
360
|
-
}
|
|
361
|
-
var init_roo = __esm({
|
|
362
|
-
"src/generators/mcp/roo.ts"() {
|
|
363
|
-
"use strict";
|
|
364
|
-
init_mcp_helpers();
|
|
365
|
-
}
|
|
366
|
-
});
|
|
2
|
+
import {
|
|
3
|
+
generateClaudeMcp
|
|
4
|
+
} from "./chunk-LQDXGEGC.js";
|
|
5
|
+
import {
|
|
6
|
+
generateClineMcp
|
|
7
|
+
} from "./chunk-XERXHVPP.js";
|
|
8
|
+
import {
|
|
9
|
+
generateCopilotMcp
|
|
10
|
+
} from "./chunk-BEJD7I74.js";
|
|
11
|
+
import {
|
|
12
|
+
generateCursorMcp
|
|
13
|
+
} from "./chunk-VZMUQVX4.js";
|
|
14
|
+
import {
|
|
15
|
+
generateGeminiCliMcp
|
|
16
|
+
} from "./chunk-HOVR7QDA.js";
|
|
17
|
+
import {
|
|
18
|
+
generateRooMcp
|
|
19
|
+
} from "./chunk-SPMMXF7R.js";
|
|
20
|
+
import "./chunk-AWKMUY5R.js";
|
|
367
21
|
|
|
368
22
|
// src/cli/index.ts
|
|
369
|
-
|
|
23
|
+
import { Command } from "commander";
|
|
370
24
|
|
|
371
25
|
// src/cli/commands/add.ts
|
|
372
|
-
|
|
373
|
-
|
|
26
|
+
import { mkdir, writeFile } from "fs/promises";
|
|
27
|
+
import path from "path";
|
|
374
28
|
|
|
375
29
|
// src/utils/config.ts
|
|
376
30
|
function getDefaultConfig() {
|
|
@@ -418,10 +72,10 @@ async function addCommand(filename) {
|
|
|
418
72
|
const config = getDefaultConfig();
|
|
419
73
|
const sanitizedFilename = sanitizeFilename(filename);
|
|
420
74
|
const rulesDir = config.aiRulesDir;
|
|
421
|
-
const filePath =
|
|
422
|
-
await
|
|
75
|
+
const filePath = path.join(rulesDir, `${sanitizedFilename}.md`);
|
|
76
|
+
await mkdir(rulesDir, { recursive: true });
|
|
423
77
|
const template = generateRuleTemplate(sanitizedFilename);
|
|
424
|
-
await
|
|
78
|
+
await writeFile(filePath, template, "utf8");
|
|
425
79
|
console.log(`\u2705 Created rule file: ${filePath}`);
|
|
426
80
|
console.log(`\u{1F4DD} Edit the file to customize your rules.`);
|
|
427
81
|
} catch (error) {
|
|
@@ -433,32 +87,32 @@ async function addCommand(filename) {
|
|
|
433
87
|
}
|
|
434
88
|
|
|
435
89
|
// src/generators/rules/claudecode.ts
|
|
436
|
-
|
|
90
|
+
import { join as join3 } from "path";
|
|
437
91
|
|
|
438
92
|
// src/utils/file.ts
|
|
439
|
-
|
|
440
|
-
|
|
93
|
+
import { readdir, rm } from "fs/promises";
|
|
94
|
+
import { join as join2 } from "path";
|
|
441
95
|
|
|
442
96
|
// src/utils/file-ops.ts
|
|
443
|
-
|
|
444
|
-
|
|
97
|
+
import { mkdir as mkdir2, readFile, stat, writeFile as writeFile2 } from "fs/promises";
|
|
98
|
+
import { dirname } from "path";
|
|
445
99
|
async function ensureDir(dirPath) {
|
|
446
100
|
try {
|
|
447
|
-
await
|
|
101
|
+
await stat(dirPath);
|
|
448
102
|
} catch {
|
|
449
|
-
await (
|
|
103
|
+
await mkdir2(dirPath, { recursive: true });
|
|
450
104
|
}
|
|
451
105
|
}
|
|
452
106
|
async function readFileContent(filepath) {
|
|
453
|
-
return
|
|
107
|
+
return readFile(filepath, "utf-8");
|
|
454
108
|
}
|
|
455
109
|
async function writeFileContent(filepath, content) {
|
|
456
|
-
await ensureDir(
|
|
457
|
-
await (
|
|
110
|
+
await ensureDir(dirname(filepath));
|
|
111
|
+
await writeFile2(filepath, content, "utf-8");
|
|
458
112
|
}
|
|
459
113
|
async function fileExists(filepath) {
|
|
460
114
|
try {
|
|
461
|
-
await
|
|
115
|
+
await stat(filepath);
|
|
462
116
|
return true;
|
|
463
117
|
} catch {
|
|
464
118
|
return false;
|
|
@@ -466,14 +120,14 @@ async function fileExists(filepath) {
|
|
|
466
120
|
}
|
|
467
121
|
|
|
468
122
|
// src/utils/ignore.ts
|
|
469
|
-
|
|
470
|
-
|
|
123
|
+
import { join } from "path";
|
|
124
|
+
import micromatch from "micromatch";
|
|
471
125
|
var cachedIgnorePatterns = null;
|
|
472
126
|
async function loadIgnorePatterns(baseDir = process.cwd()) {
|
|
473
127
|
if (cachedIgnorePatterns) {
|
|
474
128
|
return cachedIgnorePatterns;
|
|
475
129
|
}
|
|
476
|
-
const ignorePath =
|
|
130
|
+
const ignorePath = join(baseDir, ".rulesyncignore");
|
|
477
131
|
if (!await fileExists(ignorePath)) {
|
|
478
132
|
cachedIgnorePatterns = { patterns: [] };
|
|
479
133
|
return cachedIgnorePatterns;
|
|
@@ -498,12 +152,12 @@ function isFileIgnored(filepath, ignorePatterns) {
|
|
|
498
152
|
}
|
|
499
153
|
const negationPatterns = ignorePatterns.filter((p) => p.startsWith("!"));
|
|
500
154
|
const positivePatterns = ignorePatterns.filter((p) => !p.startsWith("!"));
|
|
501
|
-
const isIgnored = positivePatterns.length > 0 &&
|
|
155
|
+
const isIgnored = positivePatterns.length > 0 && micromatch.isMatch(filepath, positivePatterns, {
|
|
502
156
|
dot: true
|
|
503
157
|
});
|
|
504
158
|
if (isIgnored && negationPatterns.length > 0) {
|
|
505
159
|
const negationPatternsWithoutPrefix = negationPatterns.map((p) => p.substring(1));
|
|
506
|
-
return !
|
|
160
|
+
return !micromatch.isMatch(filepath, negationPatternsWithoutPrefix, {
|
|
507
161
|
dot: true
|
|
508
162
|
});
|
|
509
163
|
}
|
|
@@ -519,8 +173,8 @@ function filterIgnoredFiles(files, ignorePatterns) {
|
|
|
519
173
|
// src/utils/file.ts
|
|
520
174
|
async function findFiles(dir, extension = ".md", ignorePatterns) {
|
|
521
175
|
try {
|
|
522
|
-
const files = await
|
|
523
|
-
const filtered = files.filter((file) => file.endsWith(extension)).map((file) => (
|
|
176
|
+
const files = await readdir(dir);
|
|
177
|
+
const filtered = files.filter((file) => file.endsWith(extension)).map((file) => join2(dir, file));
|
|
524
178
|
if (ignorePatterns && ignorePatterns.length > 0) {
|
|
525
179
|
return filterIgnoredFiles(filtered, ignorePatterns);
|
|
526
180
|
}
|
|
@@ -537,7 +191,7 @@ async function removeDirectory(dirPath) {
|
|
|
537
191
|
}
|
|
538
192
|
try {
|
|
539
193
|
if (await fileExists(dirPath)) {
|
|
540
|
-
await
|
|
194
|
+
await rm(dirPath, { recursive: true, force: true });
|
|
541
195
|
}
|
|
542
196
|
} catch (error) {
|
|
543
197
|
console.warn(`Failed to remove directory ${dirPath}:`, error);
|
|
@@ -546,7 +200,7 @@ async function removeDirectory(dirPath) {
|
|
|
546
200
|
async function removeFile(filepath) {
|
|
547
201
|
try {
|
|
548
202
|
if (await fileExists(filepath)) {
|
|
549
|
-
await
|
|
203
|
+
await rm(filepath);
|
|
550
204
|
}
|
|
551
205
|
} catch (error) {
|
|
552
206
|
console.warn(`Failed to remove file ${filepath}:`, error);
|
|
@@ -569,23 +223,23 @@ async function generateClaudecodeConfig(rules, config, baseDir) {
|
|
|
569
223
|
const rootRules = rules.filter((r) => r.frontmatter.root === true);
|
|
570
224
|
const detailRules = rules.filter((r) => r.frontmatter.root === false);
|
|
571
225
|
const claudeMdContent = generateClaudeMarkdown(rootRules, detailRules);
|
|
572
|
-
const claudeOutputDir = baseDir ? (
|
|
226
|
+
const claudeOutputDir = baseDir ? join3(baseDir, config.outputPaths.claudecode) : config.outputPaths.claudecode;
|
|
573
227
|
outputs.push({
|
|
574
228
|
tool: "claudecode",
|
|
575
|
-
filepath: (
|
|
229
|
+
filepath: join3(claudeOutputDir, "CLAUDE.md"),
|
|
576
230
|
content: claudeMdContent
|
|
577
231
|
});
|
|
578
232
|
for (const rule of detailRules) {
|
|
579
233
|
const memoryContent = generateMemoryFile(rule);
|
|
580
234
|
outputs.push({
|
|
581
235
|
tool: "claudecode",
|
|
582
|
-
filepath: (
|
|
236
|
+
filepath: join3(claudeOutputDir, ".claude", "memories", `${rule.filename}.md`),
|
|
583
237
|
content: memoryContent
|
|
584
238
|
});
|
|
585
239
|
}
|
|
586
240
|
const ignorePatterns = await loadIgnorePatterns(baseDir);
|
|
587
241
|
if (ignorePatterns.patterns.length > 0) {
|
|
588
|
-
const settingsPath = baseDir ? (
|
|
242
|
+
const settingsPath = baseDir ? join3(baseDir, ".claude", "settings.json") : join3(".claude", "settings.json");
|
|
589
243
|
await updateClaudeSettings(settingsPath, ignorePatterns.patterns);
|
|
590
244
|
}
|
|
591
245
|
return outputs;
|
|
@@ -655,13 +309,13 @@ async function updateClaudeSettings(settingsPath, ignorePatterns) {
|
|
|
655
309
|
}
|
|
656
310
|
|
|
657
311
|
// src/generators/rules/cline.ts
|
|
658
|
-
|
|
312
|
+
import { join as join4 } from "path";
|
|
659
313
|
async function generateClineConfig(rules, config, baseDir) {
|
|
660
314
|
const outputs = [];
|
|
661
315
|
for (const rule of rules) {
|
|
662
316
|
const content = generateClineMarkdown(rule);
|
|
663
|
-
const outputDir = baseDir ? (
|
|
664
|
-
const filepath = (
|
|
317
|
+
const outputDir = baseDir ? join4(baseDir, config.outputPaths.cline) : config.outputPaths.cline;
|
|
318
|
+
const filepath = join4(outputDir, `${rule.filename}.md`);
|
|
665
319
|
outputs.push({
|
|
666
320
|
tool: "cline",
|
|
667
321
|
filepath,
|
|
@@ -670,7 +324,7 @@ async function generateClineConfig(rules, config, baseDir) {
|
|
|
670
324
|
}
|
|
671
325
|
const ignorePatterns = await loadIgnorePatterns(baseDir);
|
|
672
326
|
if (ignorePatterns.patterns.length > 0) {
|
|
673
|
-
const clineIgnorePath = baseDir ? (
|
|
327
|
+
const clineIgnorePath = baseDir ? join4(baseDir, ".clineignore") : ".clineignore";
|
|
674
328
|
const clineIgnoreContent = generateClineIgnore(ignorePatterns.patterns);
|
|
675
329
|
outputs.push({
|
|
676
330
|
tool: "cline",
|
|
@@ -694,14 +348,14 @@ function generateClineIgnore(patterns) {
|
|
|
694
348
|
}
|
|
695
349
|
|
|
696
350
|
// src/generators/rules/copilot.ts
|
|
697
|
-
|
|
351
|
+
import { join as join5 } from "path";
|
|
698
352
|
async function generateCopilotConfig(rules, config, baseDir) {
|
|
699
353
|
const outputs = [];
|
|
700
354
|
for (const rule of rules) {
|
|
701
355
|
const content = generateCopilotMarkdown(rule);
|
|
702
356
|
const baseFilename = rule.filename.replace(/\.md$/, "");
|
|
703
|
-
const outputDir = baseDir ? (
|
|
704
|
-
const filepath = (
|
|
357
|
+
const outputDir = baseDir ? join5(baseDir, config.outputPaths.copilot) : config.outputPaths.copilot;
|
|
358
|
+
const filepath = join5(outputDir, `${baseFilename}.instructions.md`);
|
|
705
359
|
outputs.push({
|
|
706
360
|
tool: "copilot",
|
|
707
361
|
filepath,
|
|
@@ -710,7 +364,7 @@ async function generateCopilotConfig(rules, config, baseDir) {
|
|
|
710
364
|
}
|
|
711
365
|
const ignorePatterns = await loadIgnorePatterns(baseDir);
|
|
712
366
|
if (ignorePatterns.patterns.length > 0) {
|
|
713
|
-
const copilotIgnorePath = baseDir ? (
|
|
367
|
+
const copilotIgnorePath = baseDir ? join5(baseDir, ".copilotignore") : ".copilotignore";
|
|
714
368
|
const copilotIgnoreContent = generateCopilotIgnore(ignorePatterns.patterns);
|
|
715
369
|
outputs.push({
|
|
716
370
|
tool: "copilot",
|
|
@@ -746,13 +400,13 @@ function generateCopilotIgnore(patterns) {
|
|
|
746
400
|
}
|
|
747
401
|
|
|
748
402
|
// src/generators/rules/cursor.ts
|
|
749
|
-
|
|
403
|
+
import { join as join6 } from "path";
|
|
750
404
|
async function generateCursorConfig(rules, config, baseDir) {
|
|
751
405
|
const outputs = [];
|
|
752
406
|
for (const rule of rules) {
|
|
753
407
|
const content = generateCursorMarkdown(rule);
|
|
754
|
-
const outputDir = baseDir ? (
|
|
755
|
-
const filepath = (
|
|
408
|
+
const outputDir = baseDir ? join6(baseDir, config.outputPaths.cursor) : config.outputPaths.cursor;
|
|
409
|
+
const filepath = join6(outputDir, `${rule.filename}.mdc`);
|
|
756
410
|
outputs.push({
|
|
757
411
|
tool: "cursor",
|
|
758
412
|
filepath,
|
|
@@ -761,7 +415,7 @@ async function generateCursorConfig(rules, config, baseDir) {
|
|
|
761
415
|
}
|
|
762
416
|
const ignorePatterns = await loadIgnorePatterns(baseDir);
|
|
763
417
|
if (ignorePatterns.patterns.length > 0) {
|
|
764
|
-
const cursorIgnorePath = baseDir ? (
|
|
418
|
+
const cursorIgnorePath = baseDir ? join6(baseDir, ".cursorignore") : ".cursorignore";
|
|
765
419
|
const cursorIgnoreContent = generateCursorIgnore(ignorePatterns.patterns);
|
|
766
420
|
outputs.push({
|
|
767
421
|
tool: "cursor",
|
|
@@ -802,15 +456,15 @@ function generateCursorIgnore(patterns) {
|
|
|
802
456
|
}
|
|
803
457
|
|
|
804
458
|
// src/generators/rules/geminicli.ts
|
|
805
|
-
|
|
459
|
+
import { join as join7 } from "path";
|
|
806
460
|
async function generateGeminiConfig(rules, config, baseDir) {
|
|
807
461
|
const outputs = [];
|
|
808
462
|
const rootRule = rules.find((rule) => rule.frontmatter.root === true);
|
|
809
463
|
const memoryRules = rules.filter((rule) => rule.frontmatter.root === false);
|
|
810
464
|
for (const rule of memoryRules) {
|
|
811
465
|
const content = generateGeminiMemoryMarkdown(rule);
|
|
812
|
-
const outputDir = baseDir ? (
|
|
813
|
-
const filepath = (
|
|
466
|
+
const outputDir = baseDir ? join7(baseDir, config.outputPaths.geminicli) : config.outputPaths.geminicli;
|
|
467
|
+
const filepath = join7(outputDir, `${rule.filename}.md`);
|
|
814
468
|
outputs.push({
|
|
815
469
|
tool: "geminicli",
|
|
816
470
|
filepath,
|
|
@@ -818,7 +472,7 @@ async function generateGeminiConfig(rules, config, baseDir) {
|
|
|
818
472
|
});
|
|
819
473
|
}
|
|
820
474
|
const rootContent = generateGeminiRootMarkdown(rootRule, memoryRules, baseDir);
|
|
821
|
-
const rootFilepath = baseDir ? (
|
|
475
|
+
const rootFilepath = baseDir ? join7(baseDir, "GEMINI.md") : "GEMINI.md";
|
|
822
476
|
outputs.push({
|
|
823
477
|
tool: "geminicli",
|
|
824
478
|
filepath: rootFilepath,
|
|
@@ -826,7 +480,7 @@ async function generateGeminiConfig(rules, config, baseDir) {
|
|
|
826
480
|
});
|
|
827
481
|
const ignorePatterns = await loadIgnorePatterns(baseDir);
|
|
828
482
|
if (ignorePatterns.patterns.length > 0) {
|
|
829
|
-
const aiexcludePath = baseDir ? (
|
|
483
|
+
const aiexcludePath = baseDir ? join7(baseDir, ".aiexclude") : ".aiexclude";
|
|
830
484
|
const aiexcludeContent = generateAiexclude(ignorePatterns.patterns);
|
|
831
485
|
outputs.push({
|
|
832
486
|
tool: "geminicli",
|
|
@@ -874,13 +528,13 @@ function generateAiexclude(patterns) {
|
|
|
874
528
|
}
|
|
875
529
|
|
|
876
530
|
// src/generators/rules/roo.ts
|
|
877
|
-
|
|
531
|
+
import { join as join8 } from "path";
|
|
878
532
|
async function generateRooConfig(rules, config, baseDir) {
|
|
879
533
|
const outputs = [];
|
|
880
534
|
for (const rule of rules) {
|
|
881
535
|
const content = generateRooMarkdown(rule);
|
|
882
|
-
const outputDir = baseDir ? (
|
|
883
|
-
const filepath = (
|
|
536
|
+
const outputDir = baseDir ? join8(baseDir, config.outputPaths.roo) : config.outputPaths.roo;
|
|
537
|
+
const filepath = join8(outputDir, `${rule.filename}.md`);
|
|
884
538
|
outputs.push({
|
|
885
539
|
tool: "roo",
|
|
886
540
|
filepath,
|
|
@@ -889,7 +543,7 @@ async function generateRooConfig(rules, config, baseDir) {
|
|
|
889
543
|
}
|
|
890
544
|
const ignorePatterns = await loadIgnorePatterns(baseDir);
|
|
891
545
|
if (ignorePatterns.patterns.length > 0) {
|
|
892
|
-
const rooIgnorePath = baseDir ? (
|
|
546
|
+
const rooIgnorePath = baseDir ? join8(baseDir, ".rooignore") : ".rooignore";
|
|
893
547
|
const rooIgnoreContent = generateRooIgnore(ignorePatterns.patterns);
|
|
894
548
|
outputs.push({
|
|
895
549
|
tool: "roo",
|
|
@@ -962,8 +616,8 @@ async function generateForTool(tool, rules, config, baseDir) {
|
|
|
962
616
|
}
|
|
963
617
|
|
|
964
618
|
// src/core/parser.ts
|
|
965
|
-
|
|
966
|
-
|
|
619
|
+
import { basename } from "path";
|
|
620
|
+
import matter from "gray-matter";
|
|
967
621
|
async function parseRulesFromDirectory(aiRulesDir) {
|
|
968
622
|
const ignorePatterns = await loadIgnorePatterns();
|
|
969
623
|
const ruleFiles = await findFiles(aiRulesDir, ".md", ignorePatterns.patterns);
|
|
@@ -996,10 +650,10 @@ ${errors.join("\n")}`);
|
|
|
996
650
|
}
|
|
997
651
|
async function parseRuleFile(filepath) {
|
|
998
652
|
const content = await readFileContent(filepath);
|
|
999
|
-
const parsed = (
|
|
653
|
+
const parsed = matter(content);
|
|
1000
654
|
validateFrontmatter(parsed.data, filepath);
|
|
1001
655
|
const frontmatter = parsed.data;
|
|
1002
|
-
const filename =
|
|
656
|
+
const filename = basename(filepath, ".md");
|
|
1003
657
|
return {
|
|
1004
658
|
frontmatter,
|
|
1005
659
|
content: parsed.content,
|
|
@@ -1128,27 +782,19 @@ async function validateRule(rule) {
|
|
|
1128
782
|
}
|
|
1129
783
|
|
|
1130
784
|
// src/core/mcp-generator.ts
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
// src/generators/mcp/index.ts
|
|
1135
|
-
init_claude();
|
|
1136
|
-
init_cline();
|
|
1137
|
-
init_copilot();
|
|
1138
|
-
init_cursor();
|
|
1139
|
-
init_geminicli();
|
|
1140
|
-
init_roo();
|
|
785
|
+
import os from "os";
|
|
786
|
+
import path3 from "path";
|
|
1141
787
|
|
|
1142
788
|
// src/core/mcp-parser.ts
|
|
1143
|
-
|
|
1144
|
-
|
|
789
|
+
import fs from "fs";
|
|
790
|
+
import path2 from "path";
|
|
1145
791
|
function parseMcpConfig(projectRoot) {
|
|
1146
|
-
const mcpPath =
|
|
1147
|
-
if (!
|
|
792
|
+
const mcpPath = path2.join(projectRoot, ".rulesync", ".mcp.json");
|
|
793
|
+
if (!fs.existsSync(mcpPath)) {
|
|
1148
794
|
return null;
|
|
1149
795
|
}
|
|
1150
796
|
try {
|
|
1151
|
-
const content =
|
|
797
|
+
const content = fs.readFileSync(mcpPath, "utf-8");
|
|
1152
798
|
const rawConfig = JSON.parse(content);
|
|
1153
799
|
if (rawConfig.servers && !rawConfig.mcpServers) {
|
|
1154
800
|
rawConfig.mcpServers = rawConfig.servers;
|
|
@@ -1179,32 +825,32 @@ async function generateMcpConfigs(projectRoot, baseDir) {
|
|
|
1179
825
|
const generators = [
|
|
1180
826
|
{
|
|
1181
827
|
tool: "claude-project",
|
|
1182
|
-
path:
|
|
828
|
+
path: path3.join(targetRoot, ".mcp.json"),
|
|
1183
829
|
generate: () => generateClaudeMcp(config, "project")
|
|
1184
830
|
},
|
|
1185
831
|
{
|
|
1186
832
|
tool: "copilot-editor",
|
|
1187
|
-
path:
|
|
833
|
+
path: path3.join(targetRoot, ".vscode", "mcp.json"),
|
|
1188
834
|
generate: () => generateCopilotMcp(config, "editor")
|
|
1189
835
|
},
|
|
1190
836
|
{
|
|
1191
837
|
tool: "cursor-project",
|
|
1192
|
-
path:
|
|
838
|
+
path: path3.join(targetRoot, ".cursor", "mcp.json"),
|
|
1193
839
|
generate: () => generateCursorMcp(config, "project")
|
|
1194
840
|
},
|
|
1195
841
|
{
|
|
1196
842
|
tool: "cline-project",
|
|
1197
|
-
path:
|
|
843
|
+
path: path3.join(targetRoot, ".cline", "mcp.json"),
|
|
1198
844
|
generate: () => generateClineMcp(config, "project")
|
|
1199
845
|
},
|
|
1200
846
|
{
|
|
1201
847
|
tool: "gemini-project",
|
|
1202
|
-
path:
|
|
848
|
+
path: path3.join(targetRoot, ".gemini", "settings.json"),
|
|
1203
849
|
generate: () => generateGeminiCliMcp(config, "project")
|
|
1204
850
|
},
|
|
1205
851
|
{
|
|
1206
852
|
tool: "roo-project",
|
|
1207
|
-
path:
|
|
853
|
+
path: path3.join(targetRoot, ".roo", "mcp.json"),
|
|
1208
854
|
generate: () => generateRooMcp(config, "project")
|
|
1209
855
|
}
|
|
1210
856
|
];
|
|
@@ -1212,17 +858,17 @@ async function generateMcpConfigs(projectRoot, baseDir) {
|
|
|
1212
858
|
generators.push(
|
|
1213
859
|
{
|
|
1214
860
|
tool: "claude-global",
|
|
1215
|
-
path:
|
|
861
|
+
path: path3.join(os.homedir(), ".claude", "settings.json"),
|
|
1216
862
|
generate: () => generateClaudeMcp(config, "global")
|
|
1217
863
|
},
|
|
1218
864
|
{
|
|
1219
865
|
tool: "cursor-global",
|
|
1220
|
-
path:
|
|
866
|
+
path: path3.join(os.homedir(), ".cursor", "mcp.json"),
|
|
1221
867
|
generate: () => generateCursorMcp(config, "global")
|
|
1222
868
|
},
|
|
1223
869
|
{
|
|
1224
870
|
tool: "gemini-global",
|
|
1225
|
-
path:
|
|
871
|
+
path: path3.join(os.homedir(), ".gemini", "settings.json"),
|
|
1226
872
|
generate: () => generateGeminiCliMcp(config, "global")
|
|
1227
873
|
}
|
|
1228
874
|
);
|
|
@@ -1380,10 +1026,10 @@ Generating configurations for base directory: ${baseDir}`);
|
|
|
1380
1026
|
}
|
|
1381
1027
|
|
|
1382
1028
|
// src/cli/commands/gitignore.ts
|
|
1383
|
-
|
|
1384
|
-
|
|
1029
|
+
import { existsSync, readFileSync, writeFileSync } from "fs";
|
|
1030
|
+
import { join as join9 } from "path";
|
|
1385
1031
|
var gitignoreCommand = async () => {
|
|
1386
|
-
const gitignorePath = (
|
|
1032
|
+
const gitignorePath = join9(process.cwd(), ".gitignore");
|
|
1387
1033
|
const rulesFilesToIgnore = [
|
|
1388
1034
|
"# Generated by rulesync - AI tool configuration files",
|
|
1389
1035
|
"**/.github/copilot-instructions.md",
|
|
@@ -1401,6 +1047,7 @@ var gitignoreCommand = async () => {
|
|
|
1401
1047
|
"**/.gemini/memories/",
|
|
1402
1048
|
"**/.aiexclude",
|
|
1403
1049
|
"**/.mcp.json",
|
|
1050
|
+
"!.rulesync/.mcp.json",
|
|
1404
1051
|
"**/.cursor/mcp.json",
|
|
1405
1052
|
"**/.cline/mcp.json",
|
|
1406
1053
|
"**/.vscode/mcp.json",
|
|
@@ -1408,8 +1055,8 @@ var gitignoreCommand = async () => {
|
|
|
1408
1055
|
"**/.roo/mcp.json"
|
|
1409
1056
|
];
|
|
1410
1057
|
let gitignoreContent = "";
|
|
1411
|
-
if (
|
|
1412
|
-
gitignoreContent =
|
|
1058
|
+
if (existsSync(gitignorePath)) {
|
|
1059
|
+
gitignoreContent = readFileSync(gitignorePath, "utf-8");
|
|
1413
1060
|
}
|
|
1414
1061
|
const linesToAdd = [];
|
|
1415
1062
|
for (const rule of rulesFilesToIgnore) {
|
|
@@ -1426,7 +1073,7 @@ var gitignoreCommand = async () => {
|
|
|
1426
1073
|
${linesToAdd.join("\n")}
|
|
1427
1074
|
` : `${linesToAdd.join("\n")}
|
|
1428
1075
|
`;
|
|
1429
|
-
|
|
1076
|
+
writeFileSync(gitignorePath, newContent);
|
|
1430
1077
|
console.log(`\u2705 .gitignore\u306B${linesToAdd.length}\u500B\u306E\u30EB\u30FC\u30EB\u3092\u8FFD\u52A0\u3057\u307E\u3057\u305F:`);
|
|
1431
1078
|
for (const line of linesToAdd) {
|
|
1432
1079
|
if (!line.startsWith("#")) {
|
|
@@ -1436,17 +1083,17 @@ ${linesToAdd.join("\n")}
|
|
|
1436
1083
|
};
|
|
1437
1084
|
|
|
1438
1085
|
// src/core/importer.ts
|
|
1439
|
-
|
|
1440
|
-
|
|
1086
|
+
import { join as join16 } from "path";
|
|
1087
|
+
import matter4 from "gray-matter";
|
|
1441
1088
|
|
|
1442
1089
|
// src/parsers/claudecode.ts
|
|
1443
|
-
|
|
1090
|
+
import { basename as basename2, join as join10 } from "path";
|
|
1444
1091
|
async function parseClaudeConfiguration(baseDir = process.cwd()) {
|
|
1445
1092
|
const errors = [];
|
|
1446
1093
|
const rules = [];
|
|
1447
1094
|
let ignorePatterns;
|
|
1448
1095
|
let mcpServers;
|
|
1449
|
-
const claudeFilePath = (
|
|
1096
|
+
const claudeFilePath = join10(baseDir, "CLAUDE.md");
|
|
1450
1097
|
if (!await fileExists(claudeFilePath)) {
|
|
1451
1098
|
errors.push("CLAUDE.md file not found");
|
|
1452
1099
|
return { rules, errors };
|
|
@@ -1457,12 +1104,12 @@ async function parseClaudeConfiguration(baseDir = process.cwd()) {
|
|
|
1457
1104
|
if (mainRule) {
|
|
1458
1105
|
rules.push(mainRule);
|
|
1459
1106
|
}
|
|
1460
|
-
const memoryDir = (
|
|
1107
|
+
const memoryDir = join10(baseDir, ".claude", "memories");
|
|
1461
1108
|
if (await fileExists(memoryDir)) {
|
|
1462
1109
|
const memoryRules = await parseClaudeMemoryFiles(memoryDir);
|
|
1463
1110
|
rules.push(...memoryRules);
|
|
1464
1111
|
}
|
|
1465
|
-
const settingsPath = (
|
|
1112
|
+
const settingsPath = join10(baseDir, ".claude", "settings.json");
|
|
1466
1113
|
if (await fileExists(settingsPath)) {
|
|
1467
1114
|
const settingsResult = await parseClaudeSettings(settingsPath);
|
|
1468
1115
|
if (settingsResult.ignorePatterns) {
|
|
@@ -1519,10 +1166,10 @@ async function parseClaudeMemoryFiles(memoryDir) {
|
|
|
1519
1166
|
const files = await readdir2(memoryDir);
|
|
1520
1167
|
for (const file of files) {
|
|
1521
1168
|
if (file.endsWith(".md")) {
|
|
1522
|
-
const filePath = (
|
|
1169
|
+
const filePath = join10(memoryDir, file);
|
|
1523
1170
|
const content = await readFileContent(filePath);
|
|
1524
1171
|
if (content.trim()) {
|
|
1525
|
-
const filename = (
|
|
1172
|
+
const filename = basename2(file, ".md");
|
|
1526
1173
|
const frontmatter = {
|
|
1527
1174
|
root: false,
|
|
1528
1175
|
targets: ["claudecode"],
|
|
@@ -1581,11 +1228,11 @@ async function parseClaudeSettings(settingsPath) {
|
|
|
1581
1228
|
}
|
|
1582
1229
|
|
|
1583
1230
|
// src/parsers/cline.ts
|
|
1584
|
-
|
|
1231
|
+
import { join as join11 } from "path";
|
|
1585
1232
|
async function parseClineConfiguration(baseDir = process.cwd()) {
|
|
1586
1233
|
const errors = [];
|
|
1587
1234
|
const rules = [];
|
|
1588
|
-
const clineFilePath = (
|
|
1235
|
+
const clineFilePath = join11(baseDir, ".cline", "instructions.md");
|
|
1589
1236
|
if (await fileExists(clineFilePath)) {
|
|
1590
1237
|
try {
|
|
1591
1238
|
const content = await readFileContent(clineFilePath);
|
|
@@ -1608,14 +1255,14 @@ async function parseClineConfiguration(baseDir = process.cwd()) {
|
|
|
1608
1255
|
errors.push(`Failed to parse .cline/instructions.md: ${errorMessage}`);
|
|
1609
1256
|
}
|
|
1610
1257
|
}
|
|
1611
|
-
const clinerulesDirPath = (
|
|
1258
|
+
const clinerulesDirPath = join11(baseDir, ".clinerules");
|
|
1612
1259
|
if (await fileExists(clinerulesDirPath)) {
|
|
1613
1260
|
try {
|
|
1614
1261
|
const { readdir: readdir2 } = await import("fs/promises");
|
|
1615
1262
|
const files = await readdir2(clinerulesDirPath);
|
|
1616
1263
|
for (const file of files) {
|
|
1617
1264
|
if (file.endsWith(".md")) {
|
|
1618
|
-
const filePath = (
|
|
1265
|
+
const filePath = join11(clinerulesDirPath, file);
|
|
1619
1266
|
try {
|
|
1620
1267
|
const content = await readFileContent(filePath);
|
|
1621
1268
|
if (content.trim()) {
|
|
@@ -1651,16 +1298,16 @@ async function parseClineConfiguration(baseDir = process.cwd()) {
|
|
|
1651
1298
|
}
|
|
1652
1299
|
|
|
1653
1300
|
// src/parsers/copilot.ts
|
|
1654
|
-
|
|
1655
|
-
|
|
1301
|
+
import { basename as basename3, join as join12 } from "path";
|
|
1302
|
+
import matter2 from "gray-matter";
|
|
1656
1303
|
async function parseCopilotConfiguration(baseDir = process.cwd()) {
|
|
1657
1304
|
const errors = [];
|
|
1658
1305
|
const rules = [];
|
|
1659
|
-
const copilotFilePath = (
|
|
1306
|
+
const copilotFilePath = join12(baseDir, ".github", "copilot-instructions.md");
|
|
1660
1307
|
if (await fileExists(copilotFilePath)) {
|
|
1661
1308
|
try {
|
|
1662
1309
|
const rawContent = await readFileContent(copilotFilePath);
|
|
1663
|
-
const parsed = (
|
|
1310
|
+
const parsed = matter2(rawContent);
|
|
1664
1311
|
const content = parsed.content.trim();
|
|
1665
1312
|
if (content) {
|
|
1666
1313
|
const frontmatter = {
|
|
@@ -1681,19 +1328,19 @@ async function parseCopilotConfiguration(baseDir = process.cwd()) {
|
|
|
1681
1328
|
errors.push(`Failed to parse copilot-instructions.md: ${errorMessage}`);
|
|
1682
1329
|
}
|
|
1683
1330
|
}
|
|
1684
|
-
const instructionsDir = (
|
|
1331
|
+
const instructionsDir = join12(baseDir, ".github", "instructions");
|
|
1685
1332
|
if (await fileExists(instructionsDir)) {
|
|
1686
1333
|
try {
|
|
1687
1334
|
const { readdir: readdir2 } = await import("fs/promises");
|
|
1688
1335
|
const files = await readdir2(instructionsDir);
|
|
1689
1336
|
for (const file of files) {
|
|
1690
1337
|
if (file.endsWith(".instructions.md")) {
|
|
1691
|
-
const filePath = (
|
|
1338
|
+
const filePath = join12(instructionsDir, file);
|
|
1692
1339
|
const rawContent = await readFileContent(filePath);
|
|
1693
|
-
const parsed = (
|
|
1340
|
+
const parsed = matter2(rawContent);
|
|
1694
1341
|
const content = parsed.content.trim();
|
|
1695
1342
|
if (content) {
|
|
1696
|
-
const filename = (
|
|
1343
|
+
const filename = basename3(file, ".instructions.md");
|
|
1697
1344
|
const frontmatter = {
|
|
1698
1345
|
root: false,
|
|
1699
1346
|
targets: ["copilot"],
|
|
@@ -1723,19 +1370,19 @@ async function parseCopilotConfiguration(baseDir = process.cwd()) {
|
|
|
1723
1370
|
}
|
|
1724
1371
|
|
|
1725
1372
|
// src/parsers/cursor.ts
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1373
|
+
import { basename as basename4, join as join13 } from "path";
|
|
1374
|
+
import matter3 from "gray-matter";
|
|
1375
|
+
import { DEFAULT_SCHEMA, FAILSAFE_SCHEMA, load } from "js-yaml";
|
|
1729
1376
|
var customMatterOptions = {
|
|
1730
1377
|
engines: {
|
|
1731
1378
|
yaml: {
|
|
1732
1379
|
parse: (str) => {
|
|
1733
1380
|
try {
|
|
1734
1381
|
const preprocessed = str.replace(/^(\s*globs:\s*)\*\s*$/gm, '$1"*"');
|
|
1735
|
-
return
|
|
1382
|
+
return load(preprocessed, { schema: DEFAULT_SCHEMA });
|
|
1736
1383
|
} catch (error) {
|
|
1737
1384
|
try {
|
|
1738
|
-
return
|
|
1385
|
+
return load(str, { schema: FAILSAFE_SCHEMA });
|
|
1739
1386
|
} catch {
|
|
1740
1387
|
throw error;
|
|
1741
1388
|
}
|
|
@@ -1749,11 +1396,11 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
|
|
|
1749
1396
|
const rules = [];
|
|
1750
1397
|
let ignorePatterns;
|
|
1751
1398
|
let mcpServers;
|
|
1752
|
-
const cursorFilePath = (
|
|
1399
|
+
const cursorFilePath = join13(baseDir, ".cursorrules");
|
|
1753
1400
|
if (await fileExists(cursorFilePath)) {
|
|
1754
1401
|
try {
|
|
1755
1402
|
const rawContent = await readFileContent(cursorFilePath);
|
|
1756
|
-
const parsed = (
|
|
1403
|
+
const parsed = matter3(rawContent, customMatterOptions);
|
|
1757
1404
|
const content = parsed.content.trim();
|
|
1758
1405
|
if (content) {
|
|
1759
1406
|
const frontmatter = {
|
|
@@ -1774,20 +1421,20 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
|
|
|
1774
1421
|
errors.push(`Failed to parse .cursorrules file: ${errorMessage}`);
|
|
1775
1422
|
}
|
|
1776
1423
|
}
|
|
1777
|
-
const cursorRulesDir = (
|
|
1424
|
+
const cursorRulesDir = join13(baseDir, ".cursor", "rules");
|
|
1778
1425
|
if (await fileExists(cursorRulesDir)) {
|
|
1779
1426
|
try {
|
|
1780
1427
|
const { readdir: readdir2 } = await import("fs/promises");
|
|
1781
1428
|
const files = await readdir2(cursorRulesDir);
|
|
1782
1429
|
for (const file of files) {
|
|
1783
1430
|
if (file.endsWith(".mdc")) {
|
|
1784
|
-
const filePath = (
|
|
1431
|
+
const filePath = join13(cursorRulesDir, file);
|
|
1785
1432
|
try {
|
|
1786
1433
|
const rawContent = await readFileContent(filePath);
|
|
1787
|
-
const parsed = (
|
|
1434
|
+
const parsed = matter3(rawContent, customMatterOptions);
|
|
1788
1435
|
const content = parsed.content.trim();
|
|
1789
1436
|
if (content) {
|
|
1790
|
-
const filename = (
|
|
1437
|
+
const filename = basename4(file, ".mdc");
|
|
1791
1438
|
const frontmatter = {
|
|
1792
1439
|
root: false,
|
|
1793
1440
|
targets: ["cursor"],
|
|
@@ -1815,7 +1462,7 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
|
|
|
1815
1462
|
if (rules.length === 0) {
|
|
1816
1463
|
errors.push("No Cursor configuration files found (.cursorrules or .cursor/rules/*.mdc)");
|
|
1817
1464
|
}
|
|
1818
|
-
const cursorIgnorePath = (
|
|
1465
|
+
const cursorIgnorePath = join13(baseDir, ".cursorignore");
|
|
1819
1466
|
if (await fileExists(cursorIgnorePath)) {
|
|
1820
1467
|
try {
|
|
1821
1468
|
const content = await readFileContent(cursorIgnorePath);
|
|
@@ -1828,7 +1475,7 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
|
|
|
1828
1475
|
errors.push(`Failed to parse .cursorignore: ${errorMessage}`);
|
|
1829
1476
|
}
|
|
1830
1477
|
}
|
|
1831
|
-
const cursorMcpPath = (
|
|
1478
|
+
const cursorMcpPath = join13(baseDir, ".cursor", "mcp.json");
|
|
1832
1479
|
if (await fileExists(cursorMcpPath)) {
|
|
1833
1480
|
try {
|
|
1834
1481
|
const content = await readFileContent(cursorMcpPath);
|
|
@@ -1850,13 +1497,13 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
|
|
|
1850
1497
|
}
|
|
1851
1498
|
|
|
1852
1499
|
// src/parsers/geminicli.ts
|
|
1853
|
-
|
|
1500
|
+
import { basename as basename5, join as join14 } from "path";
|
|
1854
1501
|
async function parseGeminiConfiguration(baseDir = process.cwd()) {
|
|
1855
1502
|
const errors = [];
|
|
1856
1503
|
const rules = [];
|
|
1857
1504
|
let ignorePatterns;
|
|
1858
1505
|
let mcpServers;
|
|
1859
|
-
const geminiFilePath = (
|
|
1506
|
+
const geminiFilePath = join14(baseDir, "GEMINI.md");
|
|
1860
1507
|
if (!await fileExists(geminiFilePath)) {
|
|
1861
1508
|
errors.push("GEMINI.md file not found");
|
|
1862
1509
|
return { rules, errors };
|
|
@@ -1867,12 +1514,12 @@ async function parseGeminiConfiguration(baseDir = process.cwd()) {
|
|
|
1867
1514
|
if (mainRule) {
|
|
1868
1515
|
rules.push(mainRule);
|
|
1869
1516
|
}
|
|
1870
|
-
const memoryDir = (
|
|
1517
|
+
const memoryDir = join14(baseDir, ".gemini", "memories");
|
|
1871
1518
|
if (await fileExists(memoryDir)) {
|
|
1872
1519
|
const memoryRules = await parseGeminiMemoryFiles(memoryDir);
|
|
1873
1520
|
rules.push(...memoryRules);
|
|
1874
1521
|
}
|
|
1875
|
-
const settingsPath = (
|
|
1522
|
+
const settingsPath = join14(baseDir, ".gemini", "settings.json");
|
|
1876
1523
|
if (await fileExists(settingsPath)) {
|
|
1877
1524
|
const settingsResult = await parseGeminiSettings(settingsPath);
|
|
1878
1525
|
if (settingsResult.ignorePatterns) {
|
|
@@ -1883,7 +1530,7 @@ async function parseGeminiConfiguration(baseDir = process.cwd()) {
|
|
|
1883
1530
|
}
|
|
1884
1531
|
errors.push(...settingsResult.errors);
|
|
1885
1532
|
}
|
|
1886
|
-
const aiexcludePath = (
|
|
1533
|
+
const aiexcludePath = join14(baseDir, ".aiexclude");
|
|
1887
1534
|
if (await fileExists(aiexcludePath)) {
|
|
1888
1535
|
const aiexcludePatterns = await parseAiexclude(aiexcludePath);
|
|
1889
1536
|
if (aiexcludePatterns.length > 0) {
|
|
@@ -1936,10 +1583,10 @@ async function parseGeminiMemoryFiles(memoryDir) {
|
|
|
1936
1583
|
const files = await readdir2(memoryDir);
|
|
1937
1584
|
for (const file of files) {
|
|
1938
1585
|
if (file.endsWith(".md")) {
|
|
1939
|
-
const filePath = (
|
|
1586
|
+
const filePath = join14(memoryDir, file);
|
|
1940
1587
|
const content = await readFileContent(filePath);
|
|
1941
1588
|
if (content.trim()) {
|
|
1942
|
-
const filename = (
|
|
1589
|
+
const filename = basename5(file, ".md");
|
|
1943
1590
|
const frontmatter = {
|
|
1944
1591
|
root: false,
|
|
1945
1592
|
targets: ["geminicli"],
|
|
@@ -1988,11 +1635,11 @@ async function parseAiexclude(aiexcludePath) {
|
|
|
1988
1635
|
}
|
|
1989
1636
|
|
|
1990
1637
|
// src/parsers/roo.ts
|
|
1991
|
-
|
|
1638
|
+
import { join as join15 } from "path";
|
|
1992
1639
|
async function parseRooConfiguration(baseDir = process.cwd()) {
|
|
1993
1640
|
const errors = [];
|
|
1994
1641
|
const rules = [];
|
|
1995
|
-
const rooFilePath = (
|
|
1642
|
+
const rooFilePath = join15(baseDir, ".roo", "instructions.md");
|
|
1996
1643
|
if (await fileExists(rooFilePath)) {
|
|
1997
1644
|
try {
|
|
1998
1645
|
const content = await readFileContent(rooFilePath);
|
|
@@ -2015,14 +1662,14 @@ async function parseRooConfiguration(baseDir = process.cwd()) {
|
|
|
2015
1662
|
errors.push(`Failed to parse .roo/instructions.md: ${errorMessage}`);
|
|
2016
1663
|
}
|
|
2017
1664
|
}
|
|
2018
|
-
const rooRulesDir = (
|
|
1665
|
+
const rooRulesDir = join15(baseDir, ".roo", "rules");
|
|
2019
1666
|
if (await fileExists(rooRulesDir)) {
|
|
2020
1667
|
try {
|
|
2021
1668
|
const { readdir: readdir2 } = await import("fs/promises");
|
|
2022
1669
|
const files = await readdir2(rooRulesDir);
|
|
2023
1670
|
for (const file of files) {
|
|
2024
1671
|
if (file.endsWith(".md")) {
|
|
2025
|
-
const filePath = (
|
|
1672
|
+
const filePath = join15(rooRulesDir, file);
|
|
2026
1673
|
try {
|
|
2027
1674
|
const content = await readFileContent(filePath);
|
|
2028
1675
|
if (content.trim()) {
|
|
@@ -2123,7 +1770,7 @@ async function importConfiguration(options) {
|
|
|
2123
1770
|
if (rules.length === 0 && !ignorePatterns && !mcpServers) {
|
|
2124
1771
|
return { success: false, rulesCreated: 0, errors };
|
|
2125
1772
|
}
|
|
2126
|
-
const rulesDirPath = (
|
|
1773
|
+
const rulesDirPath = join16(baseDir, rulesDir);
|
|
2127
1774
|
try {
|
|
2128
1775
|
const { mkdir: mkdir3 } = await import("fs/promises");
|
|
2129
1776
|
await mkdir3(rulesDirPath, { recursive: true });
|
|
@@ -2137,7 +1784,7 @@ async function importConfiguration(options) {
|
|
|
2137
1784
|
try {
|
|
2138
1785
|
const baseFilename = `${tool}__${rule.filename}`;
|
|
2139
1786
|
const filename = await generateUniqueFilename(rulesDirPath, baseFilename);
|
|
2140
|
-
const filePath = (
|
|
1787
|
+
const filePath = join16(rulesDirPath, `${filename}.md`);
|
|
2141
1788
|
const content = generateRuleFileContent(rule);
|
|
2142
1789
|
await writeFileContent(filePath, content);
|
|
2143
1790
|
rulesCreated++;
|
|
@@ -2152,7 +1799,7 @@ async function importConfiguration(options) {
|
|
|
2152
1799
|
let ignoreFileCreated = false;
|
|
2153
1800
|
if (ignorePatterns && ignorePatterns.length > 0) {
|
|
2154
1801
|
try {
|
|
2155
|
-
const rulesyncignorePath = (
|
|
1802
|
+
const rulesyncignorePath = join16(baseDir, ".rulesyncignore");
|
|
2156
1803
|
const ignoreContent = `${ignorePatterns.join("\n")}
|
|
2157
1804
|
`;
|
|
2158
1805
|
await writeFileContent(rulesyncignorePath, ignoreContent);
|
|
@@ -2168,7 +1815,7 @@ async function importConfiguration(options) {
|
|
|
2168
1815
|
let mcpFileCreated = false;
|
|
2169
1816
|
if (mcpServers && Object.keys(mcpServers).length > 0) {
|
|
2170
1817
|
try {
|
|
2171
|
-
const mcpPath = (
|
|
1818
|
+
const mcpPath = join16(baseDir, rulesDir, ".mcp.json");
|
|
2172
1819
|
const mcpContent = `${JSON.stringify({ mcpServers }, null, 2)}
|
|
2173
1820
|
`;
|
|
2174
1821
|
await writeFileContent(mcpPath, mcpContent);
|
|
@@ -2190,13 +1837,13 @@ async function importConfiguration(options) {
|
|
|
2190
1837
|
};
|
|
2191
1838
|
}
|
|
2192
1839
|
function generateRuleFileContent(rule) {
|
|
2193
|
-
const frontmatter =
|
|
1840
|
+
const frontmatter = matter4.stringify("", rule.frontmatter);
|
|
2194
1841
|
return frontmatter + rule.content;
|
|
2195
1842
|
}
|
|
2196
1843
|
async function generateUniqueFilename(rulesDir, baseFilename) {
|
|
2197
1844
|
let filename = baseFilename;
|
|
2198
1845
|
let counter = 1;
|
|
2199
|
-
while (await fileExists((
|
|
1846
|
+
while (await fileExists(join16(rulesDir, `${filename}.md`))) {
|
|
2200
1847
|
filename = `${baseFilename}-${counter}`;
|
|
2201
1848
|
counter++;
|
|
2202
1849
|
}
|
|
@@ -2261,7 +1908,7 @@ async function importCommand(options = {}) {
|
|
|
2261
1908
|
}
|
|
2262
1909
|
|
|
2263
1910
|
// src/cli/commands/init.ts
|
|
2264
|
-
|
|
1911
|
+
import { join as join17 } from "path";
|
|
2265
1912
|
async function initCommand() {
|
|
2266
1913
|
const aiRulesDir = ".rulesync";
|
|
2267
1914
|
console.log("Initializing rulesync...");
|
|
@@ -2391,7 +2038,7 @@ globs: ["src/api/**/*.ts", "src/services/**/*.ts", "src/models/**/*.ts"]
|
|
|
2391
2038
|
}
|
|
2392
2039
|
];
|
|
2393
2040
|
for (const file of sampleFiles) {
|
|
2394
|
-
const filepath = (
|
|
2041
|
+
const filepath = join17(aiRulesDir, file.filename);
|
|
2395
2042
|
if (!await fileExists(filepath)) {
|
|
2396
2043
|
await writeFileContent(filepath, file.content);
|
|
2397
2044
|
console.log(`Created ${filepath}`);
|
|
@@ -2493,13 +2140,13 @@ async function validateCommand() {
|
|
|
2493
2140
|
}
|
|
2494
2141
|
|
|
2495
2142
|
// src/cli/commands/watch.ts
|
|
2496
|
-
|
|
2143
|
+
import { watch } from "chokidar";
|
|
2497
2144
|
async function watchCommand() {
|
|
2498
2145
|
const config = getDefaultConfig();
|
|
2499
2146
|
console.log("\u{1F440} Watching for changes in .rulesync directory...");
|
|
2500
2147
|
console.log("Press Ctrl+C to stop watching");
|
|
2501
2148
|
await generateCommand({ verbose: false });
|
|
2502
|
-
const watcher =
|
|
2149
|
+
const watcher = watch(`${config.aiRulesDir}/**/*.md`, {
|
|
2503
2150
|
ignoreInitial: true,
|
|
2504
2151
|
persistent: true
|
|
2505
2152
|
});
|
|
@@ -2533,8 +2180,8 @@ async function watchCommand() {
|
|
|
2533
2180
|
}
|
|
2534
2181
|
|
|
2535
2182
|
// src/cli/index.ts
|
|
2536
|
-
var program = new
|
|
2537
|
-
program.name("rulesync").description("Unified AI rules management CLI tool").version("0.
|
|
2183
|
+
var program = new Command();
|
|
2184
|
+
program.name("rulesync").description("Unified AI rules management CLI tool").version("0.38.0");
|
|
2538
2185
|
program.command("init").description("Initialize rulesync in current directory").action(initCommand);
|
|
2539
2186
|
program.command("add <filename>").description("Add a new rule file").action(addCommand);
|
|
2540
2187
|
program.command("gitignore").description("Add generated files to .gitignore").action(gitignoreCommand);
|