rulesync 0.36.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-AWKMUY5R.js +56 -0
- package/dist/{chunk-YGXGGUBG.mjs → chunk-BEJD7I74.js} +5 -7
- package/dist/{chunk-6PQ4APY4.mjs → chunk-HOVR7QDA.js} +6 -9
- package/dist/{chunk-UNTCJDMQ.mjs → chunk-LQDXGEGC.js} +7 -13
- package/dist/{chunk-QVPQ2X4L.mjs → chunk-SPMMXF7R.js} +11 -10
- package/dist/{chunk-SBYRCTWS.mjs → chunk-VZMUQVX4.js} +11 -10
- package/dist/{chunk-QHXMJZTJ.mjs → chunk-XERXHVPP.js} +6 -6
- package/dist/{claude-O4SRX6VC.mjs → claude-ZBX7NASK.js} +2 -1
- package/dist/{cline-H5JF2OPT.mjs → cline-XP43H6CB.js} +2 -1
- package/dist/{copilot-GCIYHK4Q.mjs → copilot-4O7P53MT.js} +2 -1
- package/dist/{cursor-N75OH6WS.mjs → cursor-HSO7S4D3.js} +2 -1
- package/dist/{geminicli-AGOQ32ZE.mjs → geminicli-UZWXPT2J.js} +2 -1
- package/dist/{index.mjs → index.cjs} +561 -186
- package/dist/index.js +207 -503
- package/dist/{roo-V5YVC222.mjs → roo-JSRLCK7Z.js} +2 -1
- package/package.json +15 -13
- /package/dist/{index.d.mts → index.d.cts} +0 -0
package/dist/index.js
CHANGED
|
@@ -1,338 +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/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
|
-
});
|
|
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";
|
|
329
21
|
|
|
330
22
|
// src/cli/index.ts
|
|
331
|
-
|
|
23
|
+
import { Command } from "commander";
|
|
332
24
|
|
|
333
25
|
// src/cli/commands/add.ts
|
|
334
|
-
|
|
335
|
-
|
|
26
|
+
import { mkdir, writeFile } from "fs/promises";
|
|
27
|
+
import path from "path";
|
|
336
28
|
|
|
337
29
|
// src/utils/config.ts
|
|
338
30
|
function getDefaultConfig() {
|
|
@@ -380,10 +72,10 @@ async function addCommand(filename) {
|
|
|
380
72
|
const config = getDefaultConfig();
|
|
381
73
|
const sanitizedFilename = sanitizeFilename(filename);
|
|
382
74
|
const rulesDir = config.aiRulesDir;
|
|
383
|
-
const filePath =
|
|
384
|
-
await
|
|
75
|
+
const filePath = path.join(rulesDir, `${sanitizedFilename}.md`);
|
|
76
|
+
await mkdir(rulesDir, { recursive: true });
|
|
385
77
|
const template = generateRuleTemplate(sanitizedFilename);
|
|
386
|
-
await
|
|
78
|
+
await writeFile(filePath, template, "utf8");
|
|
387
79
|
console.log(`\u2705 Created rule file: ${filePath}`);
|
|
388
80
|
console.log(`\u{1F4DD} Edit the file to customize your rules.`);
|
|
389
81
|
} catch (error) {
|
|
@@ -395,21 +87,47 @@ async function addCommand(filename) {
|
|
|
395
87
|
}
|
|
396
88
|
|
|
397
89
|
// src/generators/rules/claudecode.ts
|
|
398
|
-
|
|
90
|
+
import { join as join3 } from "path";
|
|
399
91
|
|
|
400
92
|
// src/utils/file.ts
|
|
401
|
-
|
|
402
|
-
|
|
93
|
+
import { readdir, rm } from "fs/promises";
|
|
94
|
+
import { join as join2 } from "path";
|
|
95
|
+
|
|
96
|
+
// src/utils/file-ops.ts
|
|
97
|
+
import { mkdir as mkdir2, readFile, stat, writeFile as writeFile2 } from "fs/promises";
|
|
98
|
+
import { dirname } from "path";
|
|
99
|
+
async function ensureDir(dirPath) {
|
|
100
|
+
try {
|
|
101
|
+
await stat(dirPath);
|
|
102
|
+
} catch {
|
|
103
|
+
await mkdir2(dirPath, { recursive: true });
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
async function readFileContent(filepath) {
|
|
107
|
+
return readFile(filepath, "utf-8");
|
|
108
|
+
}
|
|
109
|
+
async function writeFileContent(filepath, content) {
|
|
110
|
+
await ensureDir(dirname(filepath));
|
|
111
|
+
await writeFile2(filepath, content, "utf-8");
|
|
112
|
+
}
|
|
113
|
+
async function fileExists(filepath) {
|
|
114
|
+
try {
|
|
115
|
+
await stat(filepath);
|
|
116
|
+
return true;
|
|
117
|
+
} catch {
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
403
121
|
|
|
404
122
|
// src/utils/ignore.ts
|
|
405
|
-
|
|
406
|
-
|
|
123
|
+
import { join } from "path";
|
|
124
|
+
import micromatch from "micromatch";
|
|
407
125
|
var cachedIgnorePatterns = null;
|
|
408
126
|
async function loadIgnorePatterns(baseDir = process.cwd()) {
|
|
409
127
|
if (cachedIgnorePatterns) {
|
|
410
128
|
return cachedIgnorePatterns;
|
|
411
129
|
}
|
|
412
|
-
const ignorePath =
|
|
130
|
+
const ignorePath = join(baseDir, ".rulesyncignore");
|
|
413
131
|
if (!await fileExists(ignorePath)) {
|
|
414
132
|
cachedIgnorePatterns = { patterns: [] };
|
|
415
133
|
return cachedIgnorePatterns;
|
|
@@ -434,12 +152,12 @@ function isFileIgnored(filepath, ignorePatterns) {
|
|
|
434
152
|
}
|
|
435
153
|
const negationPatterns = ignorePatterns.filter((p) => p.startsWith("!"));
|
|
436
154
|
const positivePatterns = ignorePatterns.filter((p) => !p.startsWith("!"));
|
|
437
|
-
const isIgnored = positivePatterns.length > 0 &&
|
|
155
|
+
const isIgnored = positivePatterns.length > 0 && micromatch.isMatch(filepath, positivePatterns, {
|
|
438
156
|
dot: true
|
|
439
157
|
});
|
|
440
158
|
if (isIgnored && negationPatterns.length > 0) {
|
|
441
159
|
const negationPatternsWithoutPrefix = negationPatterns.map((p) => p.substring(1));
|
|
442
|
-
return !
|
|
160
|
+
return !micromatch.isMatch(filepath, negationPatternsWithoutPrefix, {
|
|
443
161
|
dot: true
|
|
444
162
|
});
|
|
445
163
|
}
|
|
@@ -453,24 +171,10 @@ function filterIgnoredFiles(files, ignorePatterns) {
|
|
|
453
171
|
}
|
|
454
172
|
|
|
455
173
|
// 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
174
|
async function findFiles(dir, extension = ".md", ignorePatterns) {
|
|
471
175
|
try {
|
|
472
|
-
const files = await
|
|
473
|
-
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));
|
|
474
178
|
if (ignorePatterns && ignorePatterns.length > 0) {
|
|
475
179
|
return filterIgnoredFiles(filtered, ignorePatterns);
|
|
476
180
|
}
|
|
@@ -479,14 +183,6 @@ async function findFiles(dir, extension = ".md", ignorePatterns) {
|
|
|
479
183
|
return [];
|
|
480
184
|
}
|
|
481
185
|
}
|
|
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
186
|
async function removeDirectory(dirPath) {
|
|
491
187
|
const dangerousPaths = [".", "/", "~", "src", "node_modules"];
|
|
492
188
|
if (dangerousPaths.includes(dirPath) || dirPath === "") {
|
|
@@ -495,7 +191,7 @@ async function removeDirectory(dirPath) {
|
|
|
495
191
|
}
|
|
496
192
|
try {
|
|
497
193
|
if (await fileExists(dirPath)) {
|
|
498
|
-
await
|
|
194
|
+
await rm(dirPath, { recursive: true, force: true });
|
|
499
195
|
}
|
|
500
196
|
} catch (error) {
|
|
501
197
|
console.warn(`Failed to remove directory ${dirPath}:`, error);
|
|
@@ -504,7 +200,7 @@ async function removeDirectory(dirPath) {
|
|
|
504
200
|
async function removeFile(filepath) {
|
|
505
201
|
try {
|
|
506
202
|
if (await fileExists(filepath)) {
|
|
507
|
-
await
|
|
203
|
+
await rm(filepath);
|
|
508
204
|
}
|
|
509
205
|
} catch (error) {
|
|
510
206
|
console.warn(`Failed to remove file ${filepath}:`, error);
|
|
@@ -527,23 +223,23 @@ async function generateClaudecodeConfig(rules, config, baseDir) {
|
|
|
527
223
|
const rootRules = rules.filter((r) => r.frontmatter.root === true);
|
|
528
224
|
const detailRules = rules.filter((r) => r.frontmatter.root === false);
|
|
529
225
|
const claudeMdContent = generateClaudeMarkdown(rootRules, detailRules);
|
|
530
|
-
const claudeOutputDir = baseDir ? (
|
|
226
|
+
const claudeOutputDir = baseDir ? join3(baseDir, config.outputPaths.claudecode) : config.outputPaths.claudecode;
|
|
531
227
|
outputs.push({
|
|
532
228
|
tool: "claudecode",
|
|
533
|
-
filepath: (
|
|
229
|
+
filepath: join3(claudeOutputDir, "CLAUDE.md"),
|
|
534
230
|
content: claudeMdContent
|
|
535
231
|
});
|
|
536
232
|
for (const rule of detailRules) {
|
|
537
233
|
const memoryContent = generateMemoryFile(rule);
|
|
538
234
|
outputs.push({
|
|
539
235
|
tool: "claudecode",
|
|
540
|
-
filepath: (
|
|
236
|
+
filepath: join3(claudeOutputDir, ".claude", "memories", `${rule.filename}.md`),
|
|
541
237
|
content: memoryContent
|
|
542
238
|
});
|
|
543
239
|
}
|
|
544
240
|
const ignorePatterns = await loadIgnorePatterns(baseDir);
|
|
545
241
|
if (ignorePatterns.patterns.length > 0) {
|
|
546
|
-
const settingsPath = baseDir ? (
|
|
242
|
+
const settingsPath = baseDir ? join3(baseDir, ".claude", "settings.json") : join3(".claude", "settings.json");
|
|
547
243
|
await updateClaudeSettings(settingsPath, ignorePatterns.patterns);
|
|
548
244
|
}
|
|
549
245
|
return outputs;
|
|
@@ -580,39 +276,46 @@ async function updateClaudeSettings(settingsPath, ignorePatterns) {
|
|
|
580
276
|
try {
|
|
581
277
|
const content = await readFileContent(settingsPath);
|
|
582
278
|
settings = JSON.parse(content);
|
|
583
|
-
} catch
|
|
279
|
+
} catch {
|
|
584
280
|
console.warn(`Failed to parse existing ${settingsPath}, creating new settings`);
|
|
585
281
|
settings = {};
|
|
586
282
|
}
|
|
587
283
|
}
|
|
588
|
-
if (
|
|
589
|
-
settings
|
|
284
|
+
if (typeof settings !== "object" || settings === null) {
|
|
285
|
+
settings = {};
|
|
590
286
|
}
|
|
591
|
-
|
|
592
|
-
|
|
287
|
+
const settingsObj = settings;
|
|
288
|
+
if (!settingsObj.permissions || typeof settingsObj.permissions !== "object" || settingsObj.permissions === null) {
|
|
289
|
+
settingsObj.permissions = {};
|
|
290
|
+
}
|
|
291
|
+
const permissions = settingsObj.permissions;
|
|
292
|
+
if (!Array.isArray(permissions.deny)) {
|
|
293
|
+
permissions.deny = [];
|
|
593
294
|
}
|
|
594
295
|
const readDenyRules = ignorePatterns.map((pattern) => `Read(${pattern})`);
|
|
595
|
-
|
|
296
|
+
const denyArray = permissions.deny;
|
|
297
|
+
const filteredDeny = denyArray.filter((rule) => {
|
|
298
|
+
if (typeof rule !== "string") return false;
|
|
596
299
|
if (!rule.startsWith("Read(")) return true;
|
|
597
300
|
const match = rule.match(/^Read\((.*)\)$/);
|
|
598
301
|
if (!match) return true;
|
|
599
302
|
return !ignorePatterns.includes(match[1] ?? "");
|
|
600
303
|
});
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
const jsonContent = JSON.stringify(
|
|
304
|
+
filteredDeny.push(...readDenyRules);
|
|
305
|
+
permissions.deny = [...new Set(filteredDeny)];
|
|
306
|
+
const jsonContent = JSON.stringify(settingsObj, null, 2);
|
|
604
307
|
await writeFileContent(settingsPath, jsonContent);
|
|
605
308
|
console.log(`\u2705 Updated Claude Code settings: ${settingsPath}`);
|
|
606
309
|
}
|
|
607
310
|
|
|
608
311
|
// src/generators/rules/cline.ts
|
|
609
|
-
|
|
312
|
+
import { join as join4 } from "path";
|
|
610
313
|
async function generateClineConfig(rules, config, baseDir) {
|
|
611
314
|
const outputs = [];
|
|
612
315
|
for (const rule of rules) {
|
|
613
316
|
const content = generateClineMarkdown(rule);
|
|
614
|
-
const outputDir = baseDir ? (
|
|
615
|
-
const filepath = (
|
|
317
|
+
const outputDir = baseDir ? join4(baseDir, config.outputPaths.cline) : config.outputPaths.cline;
|
|
318
|
+
const filepath = join4(outputDir, `${rule.filename}.md`);
|
|
616
319
|
outputs.push({
|
|
617
320
|
tool: "cline",
|
|
618
321
|
filepath,
|
|
@@ -621,7 +324,7 @@ async function generateClineConfig(rules, config, baseDir) {
|
|
|
621
324
|
}
|
|
622
325
|
const ignorePatterns = await loadIgnorePatterns(baseDir);
|
|
623
326
|
if (ignorePatterns.patterns.length > 0) {
|
|
624
|
-
const clineIgnorePath = baseDir ? (
|
|
327
|
+
const clineIgnorePath = baseDir ? join4(baseDir, ".clineignore") : ".clineignore";
|
|
625
328
|
const clineIgnoreContent = generateClineIgnore(ignorePatterns.patterns);
|
|
626
329
|
outputs.push({
|
|
627
330
|
tool: "cline",
|
|
@@ -645,14 +348,14 @@ function generateClineIgnore(patterns) {
|
|
|
645
348
|
}
|
|
646
349
|
|
|
647
350
|
// src/generators/rules/copilot.ts
|
|
648
|
-
|
|
351
|
+
import { join as join5 } from "path";
|
|
649
352
|
async function generateCopilotConfig(rules, config, baseDir) {
|
|
650
353
|
const outputs = [];
|
|
651
354
|
for (const rule of rules) {
|
|
652
355
|
const content = generateCopilotMarkdown(rule);
|
|
653
356
|
const baseFilename = rule.filename.replace(/\.md$/, "");
|
|
654
|
-
const outputDir = baseDir ? (
|
|
655
|
-
const filepath = (
|
|
357
|
+
const outputDir = baseDir ? join5(baseDir, config.outputPaths.copilot) : config.outputPaths.copilot;
|
|
358
|
+
const filepath = join5(outputDir, `${baseFilename}.instructions.md`);
|
|
656
359
|
outputs.push({
|
|
657
360
|
tool: "copilot",
|
|
658
361
|
filepath,
|
|
@@ -661,7 +364,7 @@ async function generateCopilotConfig(rules, config, baseDir) {
|
|
|
661
364
|
}
|
|
662
365
|
const ignorePatterns = await loadIgnorePatterns(baseDir);
|
|
663
366
|
if (ignorePatterns.patterns.length > 0) {
|
|
664
|
-
const copilotIgnorePath = baseDir ? (
|
|
367
|
+
const copilotIgnorePath = baseDir ? join5(baseDir, ".copilotignore") : ".copilotignore";
|
|
665
368
|
const copilotIgnoreContent = generateCopilotIgnore(ignorePatterns.patterns);
|
|
666
369
|
outputs.push({
|
|
667
370
|
tool: "copilot",
|
|
@@ -697,13 +400,13 @@ function generateCopilotIgnore(patterns) {
|
|
|
697
400
|
}
|
|
698
401
|
|
|
699
402
|
// src/generators/rules/cursor.ts
|
|
700
|
-
|
|
403
|
+
import { join as join6 } from "path";
|
|
701
404
|
async function generateCursorConfig(rules, config, baseDir) {
|
|
702
405
|
const outputs = [];
|
|
703
406
|
for (const rule of rules) {
|
|
704
407
|
const content = generateCursorMarkdown(rule);
|
|
705
|
-
const outputDir = baseDir ? (
|
|
706
|
-
const filepath = (
|
|
408
|
+
const outputDir = baseDir ? join6(baseDir, config.outputPaths.cursor) : config.outputPaths.cursor;
|
|
409
|
+
const filepath = join6(outputDir, `${rule.filename}.mdc`);
|
|
707
410
|
outputs.push({
|
|
708
411
|
tool: "cursor",
|
|
709
412
|
filepath,
|
|
@@ -712,7 +415,7 @@ async function generateCursorConfig(rules, config, baseDir) {
|
|
|
712
415
|
}
|
|
713
416
|
const ignorePatterns = await loadIgnorePatterns(baseDir);
|
|
714
417
|
if (ignorePatterns.patterns.length > 0) {
|
|
715
|
-
const cursorIgnorePath = baseDir ? (
|
|
418
|
+
const cursorIgnorePath = baseDir ? join6(baseDir, ".cursorignore") : ".cursorignore";
|
|
716
419
|
const cursorIgnoreContent = generateCursorIgnore(ignorePatterns.patterns);
|
|
717
420
|
outputs.push({
|
|
718
421
|
tool: "cursor",
|
|
@@ -753,15 +456,15 @@ function generateCursorIgnore(patterns) {
|
|
|
753
456
|
}
|
|
754
457
|
|
|
755
458
|
// src/generators/rules/geminicli.ts
|
|
756
|
-
|
|
459
|
+
import { join as join7 } from "path";
|
|
757
460
|
async function generateGeminiConfig(rules, config, baseDir) {
|
|
758
461
|
const outputs = [];
|
|
759
462
|
const rootRule = rules.find((rule) => rule.frontmatter.root === true);
|
|
760
463
|
const memoryRules = rules.filter((rule) => rule.frontmatter.root === false);
|
|
761
464
|
for (const rule of memoryRules) {
|
|
762
465
|
const content = generateGeminiMemoryMarkdown(rule);
|
|
763
|
-
const outputDir = baseDir ? (
|
|
764
|
-
const filepath = (
|
|
466
|
+
const outputDir = baseDir ? join7(baseDir, config.outputPaths.geminicli) : config.outputPaths.geminicli;
|
|
467
|
+
const filepath = join7(outputDir, `${rule.filename}.md`);
|
|
765
468
|
outputs.push({
|
|
766
469
|
tool: "geminicli",
|
|
767
470
|
filepath,
|
|
@@ -769,7 +472,7 @@ async function generateGeminiConfig(rules, config, baseDir) {
|
|
|
769
472
|
});
|
|
770
473
|
}
|
|
771
474
|
const rootContent = generateGeminiRootMarkdown(rootRule, memoryRules, baseDir);
|
|
772
|
-
const rootFilepath = baseDir ? (
|
|
475
|
+
const rootFilepath = baseDir ? join7(baseDir, "GEMINI.md") : "GEMINI.md";
|
|
773
476
|
outputs.push({
|
|
774
477
|
tool: "geminicli",
|
|
775
478
|
filepath: rootFilepath,
|
|
@@ -777,7 +480,7 @@ async function generateGeminiConfig(rules, config, baseDir) {
|
|
|
777
480
|
});
|
|
778
481
|
const ignorePatterns = await loadIgnorePatterns(baseDir);
|
|
779
482
|
if (ignorePatterns.patterns.length > 0) {
|
|
780
|
-
const aiexcludePath = baseDir ? (
|
|
483
|
+
const aiexcludePath = baseDir ? join7(baseDir, ".aiexclude") : ".aiexclude";
|
|
781
484
|
const aiexcludeContent = generateAiexclude(ignorePatterns.patterns);
|
|
782
485
|
outputs.push({
|
|
783
486
|
tool: "geminicli",
|
|
@@ -825,13 +528,13 @@ function generateAiexclude(patterns) {
|
|
|
825
528
|
}
|
|
826
529
|
|
|
827
530
|
// src/generators/rules/roo.ts
|
|
828
|
-
|
|
531
|
+
import { join as join8 } from "path";
|
|
829
532
|
async function generateRooConfig(rules, config, baseDir) {
|
|
830
533
|
const outputs = [];
|
|
831
534
|
for (const rule of rules) {
|
|
832
535
|
const content = generateRooMarkdown(rule);
|
|
833
|
-
const outputDir = baseDir ? (
|
|
834
|
-
const filepath = (
|
|
536
|
+
const outputDir = baseDir ? join8(baseDir, config.outputPaths.roo) : config.outputPaths.roo;
|
|
537
|
+
const filepath = join8(outputDir, `${rule.filename}.md`);
|
|
835
538
|
outputs.push({
|
|
836
539
|
tool: "roo",
|
|
837
540
|
filepath,
|
|
@@ -840,7 +543,7 @@ async function generateRooConfig(rules, config, baseDir) {
|
|
|
840
543
|
}
|
|
841
544
|
const ignorePatterns = await loadIgnorePatterns(baseDir);
|
|
842
545
|
if (ignorePatterns.patterns.length > 0) {
|
|
843
|
-
const rooIgnorePath = baseDir ? (
|
|
546
|
+
const rooIgnorePath = baseDir ? join8(baseDir, ".rooignore") : ".rooignore";
|
|
844
547
|
const rooIgnoreContent = generateRooIgnore(ignorePatterns.patterns);
|
|
845
548
|
outputs.push({
|
|
846
549
|
tool: "roo",
|
|
@@ -913,8 +616,8 @@ async function generateForTool(tool, rules, config, baseDir) {
|
|
|
913
616
|
}
|
|
914
617
|
|
|
915
618
|
// src/core/parser.ts
|
|
916
|
-
|
|
917
|
-
|
|
619
|
+
import { basename } from "path";
|
|
620
|
+
import matter from "gray-matter";
|
|
918
621
|
async function parseRulesFromDirectory(aiRulesDir) {
|
|
919
622
|
const ignorePatterns = await loadIgnorePatterns();
|
|
920
623
|
const ruleFiles = await findFiles(aiRulesDir, ".md", ignorePatterns.patterns);
|
|
@@ -947,10 +650,10 @@ ${errors.join("\n")}`);
|
|
|
947
650
|
}
|
|
948
651
|
async function parseRuleFile(filepath) {
|
|
949
652
|
const content = await readFileContent(filepath);
|
|
950
|
-
const parsed = (
|
|
653
|
+
const parsed = matter(content);
|
|
951
654
|
validateFrontmatter(parsed.data, filepath);
|
|
952
655
|
const frontmatter = parsed.data;
|
|
953
|
-
const filename =
|
|
656
|
+
const filename = basename(filepath, ".md");
|
|
954
657
|
return {
|
|
955
658
|
frontmatter,
|
|
956
659
|
content: parsed.content,
|
|
@@ -1079,27 +782,19 @@ async function validateRule(rule) {
|
|
|
1079
782
|
}
|
|
1080
783
|
|
|
1081
784
|
// src/core/mcp-generator.ts
|
|
1082
|
-
|
|
1083
|
-
|
|
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();
|
|
785
|
+
import os from "os";
|
|
786
|
+
import path3 from "path";
|
|
1092
787
|
|
|
1093
788
|
// src/core/mcp-parser.ts
|
|
1094
|
-
|
|
1095
|
-
|
|
789
|
+
import fs from "fs";
|
|
790
|
+
import path2 from "path";
|
|
1096
791
|
function parseMcpConfig(projectRoot) {
|
|
1097
|
-
const mcpPath =
|
|
1098
|
-
if (!
|
|
792
|
+
const mcpPath = path2.join(projectRoot, ".rulesync", ".mcp.json");
|
|
793
|
+
if (!fs.existsSync(mcpPath)) {
|
|
1099
794
|
return null;
|
|
1100
795
|
}
|
|
1101
796
|
try {
|
|
1102
|
-
const content =
|
|
797
|
+
const content = fs.readFileSync(mcpPath, "utf-8");
|
|
1103
798
|
const rawConfig = JSON.parse(content);
|
|
1104
799
|
if (rawConfig.servers && !rawConfig.mcpServers) {
|
|
1105
800
|
rawConfig.mcpServers = rawConfig.servers;
|
|
@@ -1130,32 +825,32 @@ async function generateMcpConfigs(projectRoot, baseDir) {
|
|
|
1130
825
|
const generators = [
|
|
1131
826
|
{
|
|
1132
827
|
tool: "claude-project",
|
|
1133
|
-
path:
|
|
828
|
+
path: path3.join(targetRoot, ".mcp.json"),
|
|
1134
829
|
generate: () => generateClaudeMcp(config, "project")
|
|
1135
830
|
},
|
|
1136
831
|
{
|
|
1137
832
|
tool: "copilot-editor",
|
|
1138
|
-
path:
|
|
833
|
+
path: path3.join(targetRoot, ".vscode", "mcp.json"),
|
|
1139
834
|
generate: () => generateCopilotMcp(config, "editor")
|
|
1140
835
|
},
|
|
1141
836
|
{
|
|
1142
837
|
tool: "cursor-project",
|
|
1143
|
-
path:
|
|
838
|
+
path: path3.join(targetRoot, ".cursor", "mcp.json"),
|
|
1144
839
|
generate: () => generateCursorMcp(config, "project")
|
|
1145
840
|
},
|
|
1146
841
|
{
|
|
1147
842
|
tool: "cline-project",
|
|
1148
|
-
path:
|
|
843
|
+
path: path3.join(targetRoot, ".cline", "mcp.json"),
|
|
1149
844
|
generate: () => generateClineMcp(config, "project")
|
|
1150
845
|
},
|
|
1151
846
|
{
|
|
1152
847
|
tool: "gemini-project",
|
|
1153
|
-
path:
|
|
848
|
+
path: path3.join(targetRoot, ".gemini", "settings.json"),
|
|
1154
849
|
generate: () => generateGeminiCliMcp(config, "project")
|
|
1155
850
|
},
|
|
1156
851
|
{
|
|
1157
852
|
tool: "roo-project",
|
|
1158
|
-
path:
|
|
853
|
+
path: path3.join(targetRoot, ".roo", "mcp.json"),
|
|
1159
854
|
generate: () => generateRooMcp(config, "project")
|
|
1160
855
|
}
|
|
1161
856
|
];
|
|
@@ -1163,17 +858,17 @@ async function generateMcpConfigs(projectRoot, baseDir) {
|
|
|
1163
858
|
generators.push(
|
|
1164
859
|
{
|
|
1165
860
|
tool: "claude-global",
|
|
1166
|
-
path:
|
|
861
|
+
path: path3.join(os.homedir(), ".claude", "settings.json"),
|
|
1167
862
|
generate: () => generateClaudeMcp(config, "global")
|
|
1168
863
|
},
|
|
1169
864
|
{
|
|
1170
865
|
tool: "cursor-global",
|
|
1171
|
-
path:
|
|
866
|
+
path: path3.join(os.homedir(), ".cursor", "mcp.json"),
|
|
1172
867
|
generate: () => generateCursorMcp(config, "global")
|
|
1173
868
|
},
|
|
1174
869
|
{
|
|
1175
870
|
tool: "gemini-global",
|
|
1176
|
-
path:
|
|
871
|
+
path: path3.join(os.homedir(), ".gemini", "settings.json"),
|
|
1177
872
|
generate: () => generateGeminiCliMcp(config, "global")
|
|
1178
873
|
}
|
|
1179
874
|
);
|
|
@@ -1331,10 +1026,10 @@ Generating configurations for base directory: ${baseDir}`);
|
|
|
1331
1026
|
}
|
|
1332
1027
|
|
|
1333
1028
|
// src/cli/commands/gitignore.ts
|
|
1334
|
-
|
|
1335
|
-
|
|
1029
|
+
import { existsSync, readFileSync, writeFileSync } from "fs";
|
|
1030
|
+
import { join as join9 } from "path";
|
|
1336
1031
|
var gitignoreCommand = async () => {
|
|
1337
|
-
const gitignorePath = (
|
|
1032
|
+
const gitignorePath = join9(process.cwd(), ".gitignore");
|
|
1338
1033
|
const rulesFilesToIgnore = [
|
|
1339
1034
|
"# Generated by rulesync - AI tool configuration files",
|
|
1340
1035
|
"**/.github/copilot-instructions.md",
|
|
@@ -1352,6 +1047,7 @@ var gitignoreCommand = async () => {
|
|
|
1352
1047
|
"**/.gemini/memories/",
|
|
1353
1048
|
"**/.aiexclude",
|
|
1354
1049
|
"**/.mcp.json",
|
|
1050
|
+
"!.rulesync/.mcp.json",
|
|
1355
1051
|
"**/.cursor/mcp.json",
|
|
1356
1052
|
"**/.cline/mcp.json",
|
|
1357
1053
|
"**/.vscode/mcp.json",
|
|
@@ -1359,8 +1055,8 @@ var gitignoreCommand = async () => {
|
|
|
1359
1055
|
"**/.roo/mcp.json"
|
|
1360
1056
|
];
|
|
1361
1057
|
let gitignoreContent = "";
|
|
1362
|
-
if (
|
|
1363
|
-
gitignoreContent =
|
|
1058
|
+
if (existsSync(gitignorePath)) {
|
|
1059
|
+
gitignoreContent = readFileSync(gitignorePath, "utf-8");
|
|
1364
1060
|
}
|
|
1365
1061
|
const linesToAdd = [];
|
|
1366
1062
|
for (const rule of rulesFilesToIgnore) {
|
|
@@ -1377,7 +1073,7 @@ var gitignoreCommand = async () => {
|
|
|
1377
1073
|
${linesToAdd.join("\n")}
|
|
1378
1074
|
` : `${linesToAdd.join("\n")}
|
|
1379
1075
|
`;
|
|
1380
|
-
|
|
1076
|
+
writeFileSync(gitignorePath, newContent);
|
|
1381
1077
|
console.log(`\u2705 .gitignore\u306B${linesToAdd.length}\u500B\u306E\u30EB\u30FC\u30EB\u3092\u8FFD\u52A0\u3057\u307E\u3057\u305F:`);
|
|
1382
1078
|
for (const line of linesToAdd) {
|
|
1383
1079
|
if (!line.startsWith("#")) {
|
|
@@ -1387,17 +1083,17 @@ ${linesToAdd.join("\n")}
|
|
|
1387
1083
|
};
|
|
1388
1084
|
|
|
1389
1085
|
// src/core/importer.ts
|
|
1390
|
-
|
|
1391
|
-
|
|
1086
|
+
import { join as join16 } from "path";
|
|
1087
|
+
import matter4 from "gray-matter";
|
|
1392
1088
|
|
|
1393
1089
|
// src/parsers/claudecode.ts
|
|
1394
|
-
|
|
1090
|
+
import { basename as basename2, join as join10 } from "path";
|
|
1395
1091
|
async function parseClaudeConfiguration(baseDir = process.cwd()) {
|
|
1396
1092
|
const errors = [];
|
|
1397
1093
|
const rules = [];
|
|
1398
1094
|
let ignorePatterns;
|
|
1399
1095
|
let mcpServers;
|
|
1400
|
-
const claudeFilePath = (
|
|
1096
|
+
const claudeFilePath = join10(baseDir, "CLAUDE.md");
|
|
1401
1097
|
if (!await fileExists(claudeFilePath)) {
|
|
1402
1098
|
errors.push("CLAUDE.md file not found");
|
|
1403
1099
|
return { rules, errors };
|
|
@@ -1408,12 +1104,12 @@ async function parseClaudeConfiguration(baseDir = process.cwd()) {
|
|
|
1408
1104
|
if (mainRule) {
|
|
1409
1105
|
rules.push(mainRule);
|
|
1410
1106
|
}
|
|
1411
|
-
const memoryDir = (
|
|
1107
|
+
const memoryDir = join10(baseDir, ".claude", "memories");
|
|
1412
1108
|
if (await fileExists(memoryDir)) {
|
|
1413
1109
|
const memoryRules = await parseClaudeMemoryFiles(memoryDir);
|
|
1414
1110
|
rules.push(...memoryRules);
|
|
1415
1111
|
}
|
|
1416
|
-
const settingsPath = (
|
|
1112
|
+
const settingsPath = join10(baseDir, ".claude", "settings.json");
|
|
1417
1113
|
if (await fileExists(settingsPath)) {
|
|
1418
1114
|
const settingsResult = await parseClaudeSettings(settingsPath);
|
|
1419
1115
|
if (settingsResult.ignorePatterns) {
|
|
@@ -1470,10 +1166,10 @@ async function parseClaudeMemoryFiles(memoryDir) {
|
|
|
1470
1166
|
const files = await readdir2(memoryDir);
|
|
1471
1167
|
for (const file of files) {
|
|
1472
1168
|
if (file.endsWith(".md")) {
|
|
1473
|
-
const filePath = (
|
|
1169
|
+
const filePath = join10(memoryDir, file);
|
|
1474
1170
|
const content = await readFileContent(filePath);
|
|
1475
1171
|
if (content.trim()) {
|
|
1476
|
-
const filename = (
|
|
1172
|
+
const filename = basename2(file, ".md");
|
|
1477
1173
|
const frontmatter = {
|
|
1478
1174
|
root: false,
|
|
1479
1175
|
targets: ["claudecode"],
|
|
@@ -1489,7 +1185,7 @@ async function parseClaudeMemoryFiles(memoryDir) {
|
|
|
1489
1185
|
}
|
|
1490
1186
|
}
|
|
1491
1187
|
}
|
|
1492
|
-
} catch
|
|
1188
|
+
} catch {
|
|
1493
1189
|
}
|
|
1494
1190
|
return rules;
|
|
1495
1191
|
}
|
|
@@ -1500,17 +1196,25 @@ async function parseClaudeSettings(settingsPath) {
|
|
|
1500
1196
|
try {
|
|
1501
1197
|
const content = await readFileContent(settingsPath);
|
|
1502
1198
|
const settings = JSON.parse(content);
|
|
1503
|
-
if (settings
|
|
1504
|
-
const
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1199
|
+
if (typeof settings === "object" && settings !== null && "permissions" in settings) {
|
|
1200
|
+
const permissions = settings.permissions;
|
|
1201
|
+
if (permissions && "deny" in permissions && Array.isArray(permissions.deny)) {
|
|
1202
|
+
const readPatterns = permissions.deny.filter(
|
|
1203
|
+
(rule) => typeof rule === "string" && rule.startsWith("Read(") && rule.endsWith(")")
|
|
1204
|
+
).map((rule) => {
|
|
1205
|
+
const match = rule.match(/^Read\((.+)\)$/);
|
|
1206
|
+
return match ? match[1] : null;
|
|
1207
|
+
}).filter((pattern) => pattern !== null);
|
|
1208
|
+
if (readPatterns.length > 0) {
|
|
1209
|
+
ignorePatterns = readPatterns;
|
|
1210
|
+
}
|
|
1510
1211
|
}
|
|
1511
1212
|
}
|
|
1512
|
-
if (settings
|
|
1513
|
-
|
|
1213
|
+
if (typeof settings === "object" && settings !== null && "mcpServers" in settings) {
|
|
1214
|
+
const servers = settings.mcpServers;
|
|
1215
|
+
if (servers && typeof servers === "object" && Object.keys(servers).length > 0) {
|
|
1216
|
+
mcpServers = servers;
|
|
1217
|
+
}
|
|
1514
1218
|
}
|
|
1515
1219
|
} catch (error) {
|
|
1516
1220
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
@@ -1524,11 +1228,11 @@ async function parseClaudeSettings(settingsPath) {
|
|
|
1524
1228
|
}
|
|
1525
1229
|
|
|
1526
1230
|
// src/parsers/cline.ts
|
|
1527
|
-
|
|
1231
|
+
import { join as join11 } from "path";
|
|
1528
1232
|
async function parseClineConfiguration(baseDir = process.cwd()) {
|
|
1529
1233
|
const errors = [];
|
|
1530
1234
|
const rules = [];
|
|
1531
|
-
const clineFilePath = (
|
|
1235
|
+
const clineFilePath = join11(baseDir, ".cline", "instructions.md");
|
|
1532
1236
|
if (await fileExists(clineFilePath)) {
|
|
1533
1237
|
try {
|
|
1534
1238
|
const content = await readFileContent(clineFilePath);
|
|
@@ -1551,14 +1255,14 @@ async function parseClineConfiguration(baseDir = process.cwd()) {
|
|
|
1551
1255
|
errors.push(`Failed to parse .cline/instructions.md: ${errorMessage}`);
|
|
1552
1256
|
}
|
|
1553
1257
|
}
|
|
1554
|
-
const clinerulesDirPath = (
|
|
1258
|
+
const clinerulesDirPath = join11(baseDir, ".clinerules");
|
|
1555
1259
|
if (await fileExists(clinerulesDirPath)) {
|
|
1556
1260
|
try {
|
|
1557
1261
|
const { readdir: readdir2 } = await import("fs/promises");
|
|
1558
1262
|
const files = await readdir2(clinerulesDirPath);
|
|
1559
1263
|
for (const file of files) {
|
|
1560
1264
|
if (file.endsWith(".md")) {
|
|
1561
|
-
const filePath = (
|
|
1265
|
+
const filePath = join11(clinerulesDirPath, file);
|
|
1562
1266
|
try {
|
|
1563
1267
|
const content = await readFileContent(filePath);
|
|
1564
1268
|
if (content.trim()) {
|
|
@@ -1594,16 +1298,16 @@ async function parseClineConfiguration(baseDir = process.cwd()) {
|
|
|
1594
1298
|
}
|
|
1595
1299
|
|
|
1596
1300
|
// src/parsers/copilot.ts
|
|
1597
|
-
|
|
1598
|
-
|
|
1301
|
+
import { basename as basename3, join as join12 } from "path";
|
|
1302
|
+
import matter2 from "gray-matter";
|
|
1599
1303
|
async function parseCopilotConfiguration(baseDir = process.cwd()) {
|
|
1600
1304
|
const errors = [];
|
|
1601
1305
|
const rules = [];
|
|
1602
|
-
const copilotFilePath = (
|
|
1306
|
+
const copilotFilePath = join12(baseDir, ".github", "copilot-instructions.md");
|
|
1603
1307
|
if (await fileExists(copilotFilePath)) {
|
|
1604
1308
|
try {
|
|
1605
1309
|
const rawContent = await readFileContent(copilotFilePath);
|
|
1606
|
-
const parsed = (
|
|
1310
|
+
const parsed = matter2(rawContent);
|
|
1607
1311
|
const content = parsed.content.trim();
|
|
1608
1312
|
if (content) {
|
|
1609
1313
|
const frontmatter = {
|
|
@@ -1624,19 +1328,19 @@ async function parseCopilotConfiguration(baseDir = process.cwd()) {
|
|
|
1624
1328
|
errors.push(`Failed to parse copilot-instructions.md: ${errorMessage}`);
|
|
1625
1329
|
}
|
|
1626
1330
|
}
|
|
1627
|
-
const instructionsDir = (
|
|
1331
|
+
const instructionsDir = join12(baseDir, ".github", "instructions");
|
|
1628
1332
|
if (await fileExists(instructionsDir)) {
|
|
1629
1333
|
try {
|
|
1630
1334
|
const { readdir: readdir2 } = await import("fs/promises");
|
|
1631
1335
|
const files = await readdir2(instructionsDir);
|
|
1632
1336
|
for (const file of files) {
|
|
1633
1337
|
if (file.endsWith(".instructions.md")) {
|
|
1634
|
-
const filePath = (
|
|
1338
|
+
const filePath = join12(instructionsDir, file);
|
|
1635
1339
|
const rawContent = await readFileContent(filePath);
|
|
1636
|
-
const parsed = (
|
|
1340
|
+
const parsed = matter2(rawContent);
|
|
1637
1341
|
const content = parsed.content.trim();
|
|
1638
1342
|
if (content) {
|
|
1639
|
-
const filename = (
|
|
1343
|
+
const filename = basename3(file, ".instructions.md");
|
|
1640
1344
|
const frontmatter = {
|
|
1641
1345
|
root: false,
|
|
1642
1346
|
targets: ["copilot"],
|
|
@@ -1666,19 +1370,19 @@ async function parseCopilotConfiguration(baseDir = process.cwd()) {
|
|
|
1666
1370
|
}
|
|
1667
1371
|
|
|
1668
1372
|
// src/parsers/cursor.ts
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
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";
|
|
1672
1376
|
var customMatterOptions = {
|
|
1673
1377
|
engines: {
|
|
1674
1378
|
yaml: {
|
|
1675
1379
|
parse: (str) => {
|
|
1676
1380
|
try {
|
|
1677
1381
|
const preprocessed = str.replace(/^(\s*globs:\s*)\*\s*$/gm, '$1"*"');
|
|
1678
|
-
return
|
|
1382
|
+
return load(preprocessed, { schema: DEFAULT_SCHEMA });
|
|
1679
1383
|
} catch (error) {
|
|
1680
1384
|
try {
|
|
1681
|
-
return
|
|
1385
|
+
return load(str, { schema: FAILSAFE_SCHEMA });
|
|
1682
1386
|
} catch {
|
|
1683
1387
|
throw error;
|
|
1684
1388
|
}
|
|
@@ -1692,11 +1396,11 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
|
|
|
1692
1396
|
const rules = [];
|
|
1693
1397
|
let ignorePatterns;
|
|
1694
1398
|
let mcpServers;
|
|
1695
|
-
const cursorFilePath = (
|
|
1399
|
+
const cursorFilePath = join13(baseDir, ".cursorrules");
|
|
1696
1400
|
if (await fileExists(cursorFilePath)) {
|
|
1697
1401
|
try {
|
|
1698
1402
|
const rawContent = await readFileContent(cursorFilePath);
|
|
1699
|
-
const parsed = (
|
|
1403
|
+
const parsed = matter3(rawContent, customMatterOptions);
|
|
1700
1404
|
const content = parsed.content.trim();
|
|
1701
1405
|
if (content) {
|
|
1702
1406
|
const frontmatter = {
|
|
@@ -1717,20 +1421,20 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
|
|
|
1717
1421
|
errors.push(`Failed to parse .cursorrules file: ${errorMessage}`);
|
|
1718
1422
|
}
|
|
1719
1423
|
}
|
|
1720
|
-
const cursorRulesDir = (
|
|
1424
|
+
const cursorRulesDir = join13(baseDir, ".cursor", "rules");
|
|
1721
1425
|
if (await fileExists(cursorRulesDir)) {
|
|
1722
1426
|
try {
|
|
1723
1427
|
const { readdir: readdir2 } = await import("fs/promises");
|
|
1724
1428
|
const files = await readdir2(cursorRulesDir);
|
|
1725
1429
|
for (const file of files) {
|
|
1726
1430
|
if (file.endsWith(".mdc")) {
|
|
1727
|
-
const filePath = (
|
|
1431
|
+
const filePath = join13(cursorRulesDir, file);
|
|
1728
1432
|
try {
|
|
1729
1433
|
const rawContent = await readFileContent(filePath);
|
|
1730
|
-
const parsed = (
|
|
1434
|
+
const parsed = matter3(rawContent, customMatterOptions);
|
|
1731
1435
|
const content = parsed.content.trim();
|
|
1732
1436
|
if (content) {
|
|
1733
|
-
const filename = (
|
|
1437
|
+
const filename = basename4(file, ".mdc");
|
|
1734
1438
|
const frontmatter = {
|
|
1735
1439
|
root: false,
|
|
1736
1440
|
targets: ["cursor"],
|
|
@@ -1758,7 +1462,7 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
|
|
|
1758
1462
|
if (rules.length === 0) {
|
|
1759
1463
|
errors.push("No Cursor configuration files found (.cursorrules or .cursor/rules/*.mdc)");
|
|
1760
1464
|
}
|
|
1761
|
-
const cursorIgnorePath = (
|
|
1465
|
+
const cursorIgnorePath = join13(baseDir, ".cursorignore");
|
|
1762
1466
|
if (await fileExists(cursorIgnorePath)) {
|
|
1763
1467
|
try {
|
|
1764
1468
|
const content = await readFileContent(cursorIgnorePath);
|
|
@@ -1771,7 +1475,7 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
|
|
|
1771
1475
|
errors.push(`Failed to parse .cursorignore: ${errorMessage}`);
|
|
1772
1476
|
}
|
|
1773
1477
|
}
|
|
1774
|
-
const cursorMcpPath = (
|
|
1478
|
+
const cursorMcpPath = join13(baseDir, ".cursor", "mcp.json");
|
|
1775
1479
|
if (await fileExists(cursorMcpPath)) {
|
|
1776
1480
|
try {
|
|
1777
1481
|
const content = await readFileContent(cursorMcpPath);
|
|
@@ -1793,13 +1497,13 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
|
|
|
1793
1497
|
}
|
|
1794
1498
|
|
|
1795
1499
|
// src/parsers/geminicli.ts
|
|
1796
|
-
|
|
1500
|
+
import { basename as basename5, join as join14 } from "path";
|
|
1797
1501
|
async function parseGeminiConfiguration(baseDir = process.cwd()) {
|
|
1798
1502
|
const errors = [];
|
|
1799
1503
|
const rules = [];
|
|
1800
1504
|
let ignorePatterns;
|
|
1801
1505
|
let mcpServers;
|
|
1802
|
-
const geminiFilePath = (
|
|
1506
|
+
const geminiFilePath = join14(baseDir, "GEMINI.md");
|
|
1803
1507
|
if (!await fileExists(geminiFilePath)) {
|
|
1804
1508
|
errors.push("GEMINI.md file not found");
|
|
1805
1509
|
return { rules, errors };
|
|
@@ -1810,12 +1514,12 @@ async function parseGeminiConfiguration(baseDir = process.cwd()) {
|
|
|
1810
1514
|
if (mainRule) {
|
|
1811
1515
|
rules.push(mainRule);
|
|
1812
1516
|
}
|
|
1813
|
-
const memoryDir = (
|
|
1517
|
+
const memoryDir = join14(baseDir, ".gemini", "memories");
|
|
1814
1518
|
if (await fileExists(memoryDir)) {
|
|
1815
1519
|
const memoryRules = await parseGeminiMemoryFiles(memoryDir);
|
|
1816
1520
|
rules.push(...memoryRules);
|
|
1817
1521
|
}
|
|
1818
|
-
const settingsPath = (
|
|
1522
|
+
const settingsPath = join14(baseDir, ".gemini", "settings.json");
|
|
1819
1523
|
if (await fileExists(settingsPath)) {
|
|
1820
1524
|
const settingsResult = await parseGeminiSettings(settingsPath);
|
|
1821
1525
|
if (settingsResult.ignorePatterns) {
|
|
@@ -1826,7 +1530,7 @@ async function parseGeminiConfiguration(baseDir = process.cwd()) {
|
|
|
1826
1530
|
}
|
|
1827
1531
|
errors.push(...settingsResult.errors);
|
|
1828
1532
|
}
|
|
1829
|
-
const aiexcludePath = (
|
|
1533
|
+
const aiexcludePath = join14(baseDir, ".aiexclude");
|
|
1830
1534
|
if (await fileExists(aiexcludePath)) {
|
|
1831
1535
|
const aiexcludePatterns = await parseAiexclude(aiexcludePath);
|
|
1832
1536
|
if (aiexcludePatterns.length > 0) {
|
|
@@ -1879,10 +1583,10 @@ async function parseGeminiMemoryFiles(memoryDir) {
|
|
|
1879
1583
|
const files = await readdir2(memoryDir);
|
|
1880
1584
|
for (const file of files) {
|
|
1881
1585
|
if (file.endsWith(".md")) {
|
|
1882
|
-
const filePath = (
|
|
1586
|
+
const filePath = join14(memoryDir, file);
|
|
1883
1587
|
const content = await readFileContent(filePath);
|
|
1884
1588
|
if (content.trim()) {
|
|
1885
|
-
const filename = (
|
|
1589
|
+
const filename = basename5(file, ".md");
|
|
1886
1590
|
const frontmatter = {
|
|
1887
1591
|
root: false,
|
|
1888
1592
|
targets: ["geminicli"],
|
|
@@ -1898,7 +1602,7 @@ async function parseGeminiMemoryFiles(memoryDir) {
|
|
|
1898
1602
|
}
|
|
1899
1603
|
}
|
|
1900
1604
|
}
|
|
1901
|
-
} catch
|
|
1605
|
+
} catch {
|
|
1902
1606
|
}
|
|
1903
1607
|
return rules;
|
|
1904
1608
|
}
|
|
@@ -1925,17 +1629,17 @@ async function parseAiexclude(aiexcludePath) {
|
|
|
1925
1629
|
const content = await readFileContent(aiexcludePath);
|
|
1926
1630
|
const patterns = content.split("\n").map((line) => line.trim()).filter((line) => line && !line.startsWith("#"));
|
|
1927
1631
|
return patterns;
|
|
1928
|
-
} catch
|
|
1632
|
+
} catch {
|
|
1929
1633
|
return [];
|
|
1930
1634
|
}
|
|
1931
1635
|
}
|
|
1932
1636
|
|
|
1933
1637
|
// src/parsers/roo.ts
|
|
1934
|
-
|
|
1638
|
+
import { join as join15 } from "path";
|
|
1935
1639
|
async function parseRooConfiguration(baseDir = process.cwd()) {
|
|
1936
1640
|
const errors = [];
|
|
1937
1641
|
const rules = [];
|
|
1938
|
-
const rooFilePath = (
|
|
1642
|
+
const rooFilePath = join15(baseDir, ".roo", "instructions.md");
|
|
1939
1643
|
if (await fileExists(rooFilePath)) {
|
|
1940
1644
|
try {
|
|
1941
1645
|
const content = await readFileContent(rooFilePath);
|
|
@@ -1958,14 +1662,14 @@ async function parseRooConfiguration(baseDir = process.cwd()) {
|
|
|
1958
1662
|
errors.push(`Failed to parse .roo/instructions.md: ${errorMessage}`);
|
|
1959
1663
|
}
|
|
1960
1664
|
}
|
|
1961
|
-
const rooRulesDir = (
|
|
1665
|
+
const rooRulesDir = join15(baseDir, ".roo", "rules");
|
|
1962
1666
|
if (await fileExists(rooRulesDir)) {
|
|
1963
1667
|
try {
|
|
1964
1668
|
const { readdir: readdir2 } = await import("fs/promises");
|
|
1965
1669
|
const files = await readdir2(rooRulesDir);
|
|
1966
1670
|
for (const file of files) {
|
|
1967
1671
|
if (file.endsWith(".md")) {
|
|
1968
|
-
const filePath = (
|
|
1672
|
+
const filePath = join15(rooRulesDir, file);
|
|
1969
1673
|
try {
|
|
1970
1674
|
const content = await readFileContent(filePath);
|
|
1971
1675
|
if (content.trim()) {
|
|
@@ -2066,7 +1770,7 @@ async function importConfiguration(options) {
|
|
|
2066
1770
|
if (rules.length === 0 && !ignorePatterns && !mcpServers) {
|
|
2067
1771
|
return { success: false, rulesCreated: 0, errors };
|
|
2068
1772
|
}
|
|
2069
|
-
const rulesDirPath = (
|
|
1773
|
+
const rulesDirPath = join16(baseDir, rulesDir);
|
|
2070
1774
|
try {
|
|
2071
1775
|
const { mkdir: mkdir3 } = await import("fs/promises");
|
|
2072
1776
|
await mkdir3(rulesDirPath, { recursive: true });
|
|
@@ -2080,7 +1784,7 @@ async function importConfiguration(options) {
|
|
|
2080
1784
|
try {
|
|
2081
1785
|
const baseFilename = `${tool}__${rule.filename}`;
|
|
2082
1786
|
const filename = await generateUniqueFilename(rulesDirPath, baseFilename);
|
|
2083
|
-
const filePath = (
|
|
1787
|
+
const filePath = join16(rulesDirPath, `${filename}.md`);
|
|
2084
1788
|
const content = generateRuleFileContent(rule);
|
|
2085
1789
|
await writeFileContent(filePath, content);
|
|
2086
1790
|
rulesCreated++;
|
|
@@ -2095,7 +1799,7 @@ async function importConfiguration(options) {
|
|
|
2095
1799
|
let ignoreFileCreated = false;
|
|
2096
1800
|
if (ignorePatterns && ignorePatterns.length > 0) {
|
|
2097
1801
|
try {
|
|
2098
|
-
const rulesyncignorePath = (
|
|
1802
|
+
const rulesyncignorePath = join16(baseDir, ".rulesyncignore");
|
|
2099
1803
|
const ignoreContent = `${ignorePatterns.join("\n")}
|
|
2100
1804
|
`;
|
|
2101
1805
|
await writeFileContent(rulesyncignorePath, ignoreContent);
|
|
@@ -2111,7 +1815,7 @@ async function importConfiguration(options) {
|
|
|
2111
1815
|
let mcpFileCreated = false;
|
|
2112
1816
|
if (mcpServers && Object.keys(mcpServers).length > 0) {
|
|
2113
1817
|
try {
|
|
2114
|
-
const mcpPath = (
|
|
1818
|
+
const mcpPath = join16(baseDir, rulesDir, ".mcp.json");
|
|
2115
1819
|
const mcpContent = `${JSON.stringify({ mcpServers }, null, 2)}
|
|
2116
1820
|
`;
|
|
2117
1821
|
await writeFileContent(mcpPath, mcpContent);
|
|
@@ -2133,13 +1837,13 @@ async function importConfiguration(options) {
|
|
|
2133
1837
|
};
|
|
2134
1838
|
}
|
|
2135
1839
|
function generateRuleFileContent(rule) {
|
|
2136
|
-
const frontmatter =
|
|
1840
|
+
const frontmatter = matter4.stringify("", rule.frontmatter);
|
|
2137
1841
|
return frontmatter + rule.content;
|
|
2138
1842
|
}
|
|
2139
1843
|
async function generateUniqueFilename(rulesDir, baseFilename) {
|
|
2140
1844
|
let filename = baseFilename;
|
|
2141
1845
|
let counter = 1;
|
|
2142
|
-
while (await fileExists((
|
|
1846
|
+
while (await fileExists(join16(rulesDir, `${filename}.md`))) {
|
|
2143
1847
|
filename = `${baseFilename}-${counter}`;
|
|
2144
1848
|
counter++;
|
|
2145
1849
|
}
|
|
@@ -2204,7 +1908,7 @@ async function importCommand(options = {}) {
|
|
|
2204
1908
|
}
|
|
2205
1909
|
|
|
2206
1910
|
// src/cli/commands/init.ts
|
|
2207
|
-
|
|
1911
|
+
import { join as join17 } from "path";
|
|
2208
1912
|
async function initCommand() {
|
|
2209
1913
|
const aiRulesDir = ".rulesync";
|
|
2210
1914
|
console.log("Initializing rulesync...");
|
|
@@ -2334,7 +2038,7 @@ globs: ["src/api/**/*.ts", "src/services/**/*.ts", "src/models/**/*.ts"]
|
|
|
2334
2038
|
}
|
|
2335
2039
|
];
|
|
2336
2040
|
for (const file of sampleFiles) {
|
|
2337
|
-
const filepath = (
|
|
2041
|
+
const filepath = join17(aiRulesDir, file.filename);
|
|
2338
2042
|
if (!await fileExists(filepath)) {
|
|
2339
2043
|
await writeFileContent(filepath, file.content);
|
|
2340
2044
|
console.log(`Created ${filepath}`);
|
|
@@ -2436,13 +2140,13 @@ async function validateCommand() {
|
|
|
2436
2140
|
}
|
|
2437
2141
|
|
|
2438
2142
|
// src/cli/commands/watch.ts
|
|
2439
|
-
|
|
2143
|
+
import { watch } from "chokidar";
|
|
2440
2144
|
async function watchCommand() {
|
|
2441
2145
|
const config = getDefaultConfig();
|
|
2442
2146
|
console.log("\u{1F440} Watching for changes in .rulesync directory...");
|
|
2443
2147
|
console.log("Press Ctrl+C to stop watching");
|
|
2444
2148
|
await generateCommand({ verbose: false });
|
|
2445
|
-
const watcher =
|
|
2149
|
+
const watcher = watch(`${config.aiRulesDir}/**/*.md`, {
|
|
2446
2150
|
ignoreInitial: true,
|
|
2447
2151
|
persistent: true
|
|
2448
2152
|
});
|
|
@@ -2476,8 +2180,8 @@ async function watchCommand() {
|
|
|
2476
2180
|
}
|
|
2477
2181
|
|
|
2478
2182
|
// src/cli/index.ts
|
|
2479
|
-
var program = new
|
|
2480
|
-
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");
|
|
2481
2185
|
program.command("init").description("Initialize rulesync in current directory").action(initCommand);
|
|
2482
2186
|
program.command("add <filename>").description("Add a new rule file").action(addCommand);
|
|
2483
2187
|
program.command("gitignore").description("Add generated files to .gitignore").action(gitignoreCommand);
|