rulesync 0.41.0 → 0.43.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-SXEFNT27.js → chunk-2FR4Z37J.js} +1 -1
- package/dist/{chunk-CUKKFQFO.js → chunk-B3627VQY.js} +9 -7
- package/dist/{chunk-PPX47BRK.js → chunk-RL3TE3EZ.js} +6 -6
- package/dist/{chunk-QR656A7R.js → chunk-TCK42GOL.js} +2 -2
- package/dist/chunk-THWXK5Z2.js +35 -0
- package/dist/{chunk-XN7RGMJW.js → chunk-XHRBWFGI.js} +2 -2
- package/dist/{chunk-4SFPBBIB.js → chunk-ZMAL5LIX.js} +8 -3
- package/dist/{claude-JS6ARGB3.js → claudecode-ITHKV345.js} +2 -2
- package/dist/{cline-MM3R4QQE.js → cline-PKE6TYNJ.js} +2 -2
- package/dist/{copilot-NLSI3ID7.js → copilot-5JP6D4NO.js} +2 -2
- package/dist/{cursor-AU3PRMGD.js → cursor-RHFDG6T2.js} +2 -2
- package/dist/{geminicli-LINS3RMZ.js → geminicli-B3FFO5WV.js} +2 -2
- package/dist/index.cjs +143 -182
- package/dist/index.js +111 -129
- package/dist/{roo-KUGAURJB.js → roo-UOTKEOH7.js} +2 -2
- package/package.json +1 -1
- package/dist/chunk-6YNGMPAL.js +0 -56
package/dist/index.js
CHANGED
|
@@ -1,30 +1,30 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
generateClaudeMcp
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-B3627VQY.js";
|
|
5
5
|
import {
|
|
6
6
|
generateClineMcp
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-XHRBWFGI.js";
|
|
8
8
|
import {
|
|
9
9
|
generateCopilotMcp
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-2FR4Z37J.js";
|
|
11
11
|
import {
|
|
12
12
|
generateCursorMcp
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-ZMAL5LIX.js";
|
|
14
14
|
import {
|
|
15
15
|
generateGeminiCliMcp
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-TCK42GOL.js";
|
|
17
17
|
import {
|
|
18
18
|
generateRooMcp
|
|
19
|
-
} from "./chunk-
|
|
20
|
-
import "./chunk-
|
|
19
|
+
} from "./chunk-RL3TE3EZ.js";
|
|
20
|
+
import "./chunk-THWXK5Z2.js";
|
|
21
21
|
|
|
22
22
|
// src/cli/index.ts
|
|
23
23
|
import { Command } from "commander";
|
|
24
24
|
|
|
25
25
|
// src/cli/commands/add.ts
|
|
26
26
|
import { mkdir, writeFile } from "fs/promises";
|
|
27
|
-
import path from "path";
|
|
27
|
+
import * as path from "path";
|
|
28
28
|
|
|
29
29
|
// src/utils/config.ts
|
|
30
30
|
function getDefaultConfig() {
|
|
@@ -35,12 +35,11 @@ function getDefaultConfig() {
|
|
|
35
35
|
cursor: ".cursor/rules",
|
|
36
36
|
cline: ".clinerules",
|
|
37
37
|
claudecode: ".",
|
|
38
|
-
claude: ".",
|
|
39
38
|
roo: ".roo/rules",
|
|
40
39
|
geminicli: ".gemini/memories"
|
|
41
40
|
},
|
|
42
41
|
watchEnabled: false,
|
|
43
|
-
defaultTargets: ["copilot", "cursor", "cline", "claudecode", "
|
|
42
|
+
defaultTargets: ["copilot", "cursor", "cline", "claudecode", "roo", "geminicli"]
|
|
44
43
|
};
|
|
45
44
|
}
|
|
46
45
|
function resolveTargets(targets, config) {
|
|
@@ -87,11 +86,19 @@ async function addCommand(filename) {
|
|
|
87
86
|
}
|
|
88
87
|
|
|
89
88
|
// src/generators/rules/claudecode.ts
|
|
90
|
-
import { join as
|
|
89
|
+
import { join as join4 } from "path";
|
|
90
|
+
|
|
91
|
+
// src/types/claudecode.ts
|
|
92
|
+
import { z } from "zod/v4";
|
|
93
|
+
var ClaudeSettingsSchema = z.looseObject({
|
|
94
|
+
permissions: z.looseObject({
|
|
95
|
+
deny: z.array(z.string()).default([])
|
|
96
|
+
}).default({ deny: [] })
|
|
97
|
+
});
|
|
91
98
|
|
|
92
99
|
// src/utils/file.ts
|
|
93
100
|
import { readdir, rm } from "fs/promises";
|
|
94
|
-
import { join as
|
|
101
|
+
import { join as join3 } from "path";
|
|
95
102
|
|
|
96
103
|
// src/utils/file-ops.ts
|
|
97
104
|
import { mkdir as mkdir2, readFile, stat, writeFile as writeFile2 } from "fs/promises";
|
|
@@ -120,14 +127,14 @@ async function fileExists(filepath) {
|
|
|
120
127
|
}
|
|
121
128
|
|
|
122
129
|
// src/utils/ignore.ts
|
|
123
|
-
import { join } from "path";
|
|
130
|
+
import { join as join2 } from "path";
|
|
124
131
|
import micromatch from "micromatch";
|
|
125
132
|
var cachedIgnorePatterns = null;
|
|
126
133
|
async function loadIgnorePatterns(baseDir = process.cwd()) {
|
|
127
134
|
if (cachedIgnorePatterns) {
|
|
128
135
|
return cachedIgnorePatterns;
|
|
129
136
|
}
|
|
130
|
-
const ignorePath =
|
|
137
|
+
const ignorePath = join2(baseDir, ".rulesyncignore");
|
|
131
138
|
if (!await fileExists(ignorePath)) {
|
|
132
139
|
cachedIgnorePatterns = { patterns: [] };
|
|
133
140
|
return cachedIgnorePatterns;
|
|
@@ -174,7 +181,7 @@ function filterIgnoredFiles(files, ignorePatterns) {
|
|
|
174
181
|
async function findFiles(dir, extension = ".md", ignorePatterns) {
|
|
175
182
|
try {
|
|
176
183
|
const files = await readdir(dir);
|
|
177
|
-
const filtered = files.filter((file) => file.endsWith(extension)).map((file) =>
|
|
184
|
+
const filtered = files.filter((file) => file.endsWith(extension)).map((file) => join3(dir, file));
|
|
178
185
|
if (ignorePatterns && ignorePatterns.length > 0) {
|
|
179
186
|
return filterIgnoredFiles(filtered, ignorePatterns);
|
|
180
187
|
}
|
|
@@ -223,23 +230,23 @@ async function generateClaudecodeConfig(rules, config, baseDir) {
|
|
|
223
230
|
const rootRules = rules.filter((r) => r.frontmatter.root === true);
|
|
224
231
|
const detailRules = rules.filter((r) => r.frontmatter.root === false);
|
|
225
232
|
const claudeMdContent = generateClaudeMarkdown(rootRules, detailRules);
|
|
226
|
-
const claudeOutputDir = baseDir ?
|
|
233
|
+
const claudeOutputDir = baseDir ? join4(baseDir, config.outputPaths.claudecode) : config.outputPaths.claudecode;
|
|
227
234
|
outputs.push({
|
|
228
235
|
tool: "claudecode",
|
|
229
|
-
filepath:
|
|
236
|
+
filepath: join4(claudeOutputDir, "CLAUDE.md"),
|
|
230
237
|
content: claudeMdContent
|
|
231
238
|
});
|
|
232
239
|
for (const rule of detailRules) {
|
|
233
240
|
const memoryContent = generateMemoryFile(rule);
|
|
234
241
|
outputs.push({
|
|
235
242
|
tool: "claudecode",
|
|
236
|
-
filepath:
|
|
243
|
+
filepath: join4(claudeOutputDir, ".claude", "memories", `${rule.filename}.md`),
|
|
237
244
|
content: memoryContent
|
|
238
245
|
});
|
|
239
246
|
}
|
|
240
247
|
const ignorePatterns = await loadIgnorePatterns(baseDir);
|
|
241
248
|
if (ignorePatterns.patterns.length > 0) {
|
|
242
|
-
const settingsPath = baseDir ?
|
|
249
|
+
const settingsPath = baseDir ? join4(baseDir, ".claude", "settings.json") : join4(".claude", "settings.json");
|
|
243
250
|
await updateClaudeSettings(settingsPath, ignorePatterns.patterns);
|
|
244
251
|
}
|
|
245
252
|
return outputs;
|
|
@@ -271,51 +278,46 @@ function generateMemoryFile(rule) {
|
|
|
271
278
|
return rule.content.trim();
|
|
272
279
|
}
|
|
273
280
|
async function updateClaudeSettings(settingsPath, ignorePatterns) {
|
|
274
|
-
let
|
|
281
|
+
let rawSettings = {};
|
|
275
282
|
if (await fileExists(settingsPath)) {
|
|
276
283
|
try {
|
|
277
284
|
const content = await readFileContent(settingsPath);
|
|
278
|
-
|
|
285
|
+
rawSettings = JSON.parse(content);
|
|
279
286
|
} catch {
|
|
280
287
|
console.warn(`Failed to parse existing ${settingsPath}, creating new settings`);
|
|
281
|
-
|
|
288
|
+
rawSettings = {};
|
|
282
289
|
}
|
|
283
290
|
}
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
settingsObj.permissions = {};
|
|
291
|
+
const parseResult = ClaudeSettingsSchema.safeParse(rawSettings);
|
|
292
|
+
const settings = parseResult.success ? parseResult.data : ClaudeSettingsSchema.parse({});
|
|
293
|
+
const readDenyRules = ignorePatterns.map((pattern) => `Read(${pattern})`);
|
|
294
|
+
if (!settings.permissions) {
|
|
295
|
+
settings.permissions = { deny: [] };
|
|
290
296
|
}
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
permissions.deny = [];
|
|
297
|
+
if (!Array.isArray(settings.permissions.deny)) {
|
|
298
|
+
settings.permissions.deny = [];
|
|
294
299
|
}
|
|
295
|
-
const
|
|
296
|
-
const denyArray = permissions.deny;
|
|
297
|
-
const filteredDeny = denyArray.filter((rule) => {
|
|
298
|
-
if (typeof rule !== "string") return false;
|
|
300
|
+
const filteredDeny = settings.permissions.deny.filter((rule) => {
|
|
299
301
|
if (!rule.startsWith("Read(")) return true;
|
|
300
302
|
const match = rule.match(/^Read\((.*)\)$/);
|
|
301
303
|
if (!match) return true;
|
|
302
304
|
return !ignorePatterns.includes(match[1] ?? "");
|
|
303
305
|
});
|
|
304
306
|
filteredDeny.push(...readDenyRules);
|
|
305
|
-
permissions.deny =
|
|
306
|
-
const jsonContent = JSON.stringify(
|
|
307
|
+
settings.permissions.deny = Array.from(new Set(filteredDeny));
|
|
308
|
+
const jsonContent = JSON.stringify(settings, null, 2);
|
|
307
309
|
await writeFileContent(settingsPath, jsonContent);
|
|
308
310
|
console.log(`\u2705 Updated Claude Code settings: ${settingsPath}`);
|
|
309
311
|
}
|
|
310
312
|
|
|
311
313
|
// src/generators/rules/cline.ts
|
|
312
|
-
import { join as
|
|
314
|
+
import { join as join5 } from "path";
|
|
313
315
|
async function generateClineConfig(rules, config, baseDir) {
|
|
314
316
|
const outputs = [];
|
|
315
317
|
for (const rule of rules) {
|
|
316
318
|
const content = generateClineMarkdown(rule);
|
|
317
|
-
const outputDir = baseDir ?
|
|
318
|
-
const filepath =
|
|
319
|
+
const outputDir = baseDir ? join5(baseDir, config.outputPaths.cline) : config.outputPaths.cline;
|
|
320
|
+
const filepath = join5(outputDir, `${rule.filename}.md`);
|
|
319
321
|
outputs.push({
|
|
320
322
|
tool: "cline",
|
|
321
323
|
filepath,
|
|
@@ -324,7 +326,7 @@ async function generateClineConfig(rules, config, baseDir) {
|
|
|
324
326
|
}
|
|
325
327
|
const ignorePatterns = await loadIgnorePatterns(baseDir);
|
|
326
328
|
if (ignorePatterns.patterns.length > 0) {
|
|
327
|
-
const clineIgnorePath = baseDir ?
|
|
329
|
+
const clineIgnorePath = baseDir ? join5(baseDir, ".clineignore") : ".clineignore";
|
|
328
330
|
const clineIgnoreContent = generateClineIgnore(ignorePatterns.patterns);
|
|
329
331
|
outputs.push({
|
|
330
332
|
tool: "cline",
|
|
@@ -348,14 +350,14 @@ function generateClineIgnore(patterns) {
|
|
|
348
350
|
}
|
|
349
351
|
|
|
350
352
|
// src/generators/rules/copilot.ts
|
|
351
|
-
import { join as
|
|
353
|
+
import { join as join6 } from "path";
|
|
352
354
|
async function generateCopilotConfig(rules, config, baseDir) {
|
|
353
355
|
const outputs = [];
|
|
354
356
|
for (const rule of rules) {
|
|
355
357
|
const content = generateCopilotMarkdown(rule);
|
|
356
358
|
const baseFilename = rule.filename.replace(/\.md$/, "");
|
|
357
|
-
const outputDir = baseDir ?
|
|
358
|
-
const filepath =
|
|
359
|
+
const outputDir = baseDir ? join6(baseDir, config.outputPaths.copilot) : config.outputPaths.copilot;
|
|
360
|
+
const filepath = join6(outputDir, `${baseFilename}.instructions.md`);
|
|
359
361
|
outputs.push({
|
|
360
362
|
tool: "copilot",
|
|
361
363
|
filepath,
|
|
@@ -364,7 +366,7 @@ async function generateCopilotConfig(rules, config, baseDir) {
|
|
|
364
366
|
}
|
|
365
367
|
const ignorePatterns = await loadIgnorePatterns(baseDir);
|
|
366
368
|
if (ignorePatterns.patterns.length > 0) {
|
|
367
|
-
const copilotIgnorePath = baseDir ?
|
|
369
|
+
const copilotIgnorePath = baseDir ? join6(baseDir, ".copilotignore") : ".copilotignore";
|
|
368
370
|
const copilotIgnoreContent = generateCopilotIgnore(ignorePatterns.patterns);
|
|
369
371
|
outputs.push({
|
|
370
372
|
tool: "copilot",
|
|
@@ -400,13 +402,13 @@ function generateCopilotIgnore(patterns) {
|
|
|
400
402
|
}
|
|
401
403
|
|
|
402
404
|
// src/generators/rules/cursor.ts
|
|
403
|
-
import { join as
|
|
405
|
+
import { join as join7 } from "path";
|
|
404
406
|
async function generateCursorConfig(rules, config, baseDir) {
|
|
405
407
|
const outputs = [];
|
|
406
408
|
for (const rule of rules) {
|
|
407
409
|
const content = generateCursorMarkdown(rule);
|
|
408
|
-
const outputDir = baseDir ?
|
|
409
|
-
const filepath =
|
|
410
|
+
const outputDir = baseDir ? join7(baseDir, config.outputPaths.cursor) : config.outputPaths.cursor;
|
|
411
|
+
const filepath = join7(outputDir, `${rule.filename}.mdc`);
|
|
410
412
|
outputs.push({
|
|
411
413
|
tool: "cursor",
|
|
412
414
|
filepath,
|
|
@@ -415,7 +417,7 @@ async function generateCursorConfig(rules, config, baseDir) {
|
|
|
415
417
|
}
|
|
416
418
|
const ignorePatterns = await loadIgnorePatterns(baseDir);
|
|
417
419
|
if (ignorePatterns.patterns.length > 0) {
|
|
418
|
-
const cursorIgnorePath = baseDir ?
|
|
420
|
+
const cursorIgnorePath = baseDir ? join7(baseDir, ".cursorignore") : ".cursorignore";
|
|
419
421
|
const cursorIgnoreContent = generateCursorIgnore(ignorePatterns.patterns);
|
|
420
422
|
outputs.push({
|
|
421
423
|
tool: "cursor",
|
|
@@ -488,15 +490,15 @@ function generateCursorIgnore(patterns) {
|
|
|
488
490
|
}
|
|
489
491
|
|
|
490
492
|
// src/generators/rules/geminicli.ts
|
|
491
|
-
import { join as
|
|
493
|
+
import { join as join8 } from "path";
|
|
492
494
|
async function generateGeminiConfig(rules, config, baseDir) {
|
|
493
495
|
const outputs = [];
|
|
494
496
|
const rootRule = rules.find((rule) => rule.frontmatter.root === true);
|
|
495
497
|
const memoryRules = rules.filter((rule) => rule.frontmatter.root === false);
|
|
496
498
|
for (const rule of memoryRules) {
|
|
497
499
|
const content = generateGeminiMemoryMarkdown(rule);
|
|
498
|
-
const outputDir = baseDir ?
|
|
499
|
-
const filepath =
|
|
500
|
+
const outputDir = baseDir ? join8(baseDir, config.outputPaths.geminicli) : config.outputPaths.geminicli;
|
|
501
|
+
const filepath = join8(outputDir, `${rule.filename}.md`);
|
|
500
502
|
outputs.push({
|
|
501
503
|
tool: "geminicli",
|
|
502
504
|
filepath,
|
|
@@ -504,7 +506,7 @@ async function generateGeminiConfig(rules, config, baseDir) {
|
|
|
504
506
|
});
|
|
505
507
|
}
|
|
506
508
|
const rootContent = generateGeminiRootMarkdown(rootRule, memoryRules, baseDir);
|
|
507
|
-
const rootFilepath = baseDir ?
|
|
509
|
+
const rootFilepath = baseDir ? join8(baseDir, "GEMINI.md") : "GEMINI.md";
|
|
508
510
|
outputs.push({
|
|
509
511
|
tool: "geminicli",
|
|
510
512
|
filepath: rootFilepath,
|
|
@@ -512,7 +514,7 @@ async function generateGeminiConfig(rules, config, baseDir) {
|
|
|
512
514
|
});
|
|
513
515
|
const ignorePatterns = await loadIgnorePatterns(baseDir);
|
|
514
516
|
if (ignorePatterns.patterns.length > 0) {
|
|
515
|
-
const aiexcludePath = baseDir ?
|
|
517
|
+
const aiexcludePath = baseDir ? join8(baseDir, ".aiexclude") : ".aiexclude";
|
|
516
518
|
const aiexcludeContent = generateAiexclude(ignorePatterns.patterns);
|
|
517
519
|
outputs.push({
|
|
518
520
|
tool: "geminicli",
|
|
@@ -560,13 +562,13 @@ function generateAiexclude(patterns) {
|
|
|
560
562
|
}
|
|
561
563
|
|
|
562
564
|
// src/generators/rules/roo.ts
|
|
563
|
-
import { join as
|
|
565
|
+
import { join as join9 } from "path";
|
|
564
566
|
async function generateRooConfig(rules, config, baseDir) {
|
|
565
567
|
const outputs = [];
|
|
566
568
|
for (const rule of rules) {
|
|
567
569
|
const content = generateRooMarkdown(rule);
|
|
568
|
-
const outputDir = baseDir ?
|
|
569
|
-
const filepath =
|
|
570
|
+
const outputDir = baseDir ? join9(baseDir, config.outputPaths.roo) : config.outputPaths.roo;
|
|
571
|
+
const filepath = join9(outputDir, `${rule.filename}.md`);
|
|
570
572
|
outputs.push({
|
|
571
573
|
tool: "roo",
|
|
572
574
|
filepath,
|
|
@@ -575,7 +577,7 @@ async function generateRooConfig(rules, config, baseDir) {
|
|
|
575
577
|
}
|
|
576
578
|
const ignorePatterns = await loadIgnorePatterns(baseDir);
|
|
577
579
|
if (ignorePatterns.patterns.length > 0) {
|
|
578
|
-
const rooIgnorePath = baseDir ?
|
|
580
|
+
const rooIgnorePath = baseDir ? join9(baseDir, ".rooignore") : ".rooignore";
|
|
579
581
|
const rooIgnoreContent = generateRooIgnore(ignorePatterns.patterns);
|
|
580
582
|
outputs.push({
|
|
581
583
|
tool: "roo",
|
|
@@ -814,12 +816,11 @@ async function validateRule(rule) {
|
|
|
814
816
|
}
|
|
815
817
|
|
|
816
818
|
// src/core/mcp-generator.ts
|
|
817
|
-
import
|
|
818
|
-
import path3 from "path";
|
|
819
|
+
import * as path3 from "path";
|
|
819
820
|
|
|
820
821
|
// src/core/mcp-parser.ts
|
|
821
|
-
import fs from "fs";
|
|
822
|
-
import path2 from "path";
|
|
822
|
+
import * as fs from "fs";
|
|
823
|
+
import * as path2 from "path";
|
|
823
824
|
function parseMcpConfig(projectRoot) {
|
|
824
825
|
const mcpPath = path2.join(projectRoot, ".rulesync", ".mcp.json");
|
|
825
826
|
if (!fs.existsSync(mcpPath)) {
|
|
@@ -858,7 +859,7 @@ async function generateMcpConfigs(projectRoot, baseDir) {
|
|
|
858
859
|
{
|
|
859
860
|
tool: "claude-project",
|
|
860
861
|
path: path3.join(targetRoot, ".mcp.json"),
|
|
861
|
-
generate: () => generateClaudeMcp(config
|
|
862
|
+
generate: () => generateClaudeMcp(config)
|
|
862
863
|
},
|
|
863
864
|
{
|
|
864
865
|
tool: "copilot-editor",
|
|
@@ -868,43 +869,24 @@ async function generateMcpConfigs(projectRoot, baseDir) {
|
|
|
868
869
|
{
|
|
869
870
|
tool: "cursor-project",
|
|
870
871
|
path: path3.join(targetRoot, ".cursor", "mcp.json"),
|
|
871
|
-
generate: () => generateCursorMcp(config
|
|
872
|
+
generate: () => generateCursorMcp(config)
|
|
872
873
|
},
|
|
873
874
|
{
|
|
874
875
|
tool: "cline-project",
|
|
875
876
|
path: path3.join(targetRoot, ".cline", "mcp.json"),
|
|
876
|
-
generate: () => generateClineMcp(config
|
|
877
|
+
generate: () => generateClineMcp(config)
|
|
877
878
|
},
|
|
878
879
|
{
|
|
879
880
|
tool: "gemini-project",
|
|
880
881
|
path: path3.join(targetRoot, ".gemini", "settings.json"),
|
|
881
|
-
generate: () => generateGeminiCliMcp(config
|
|
882
|
+
generate: () => generateGeminiCliMcp(config)
|
|
882
883
|
},
|
|
883
884
|
{
|
|
884
885
|
tool: "roo-project",
|
|
885
886
|
path: path3.join(targetRoot, ".roo", "mcp.json"),
|
|
886
|
-
generate: () => generateRooMcp(config
|
|
887
|
+
generate: () => generateRooMcp(config)
|
|
887
888
|
}
|
|
888
889
|
];
|
|
889
|
-
if (!baseDir) {
|
|
890
|
-
generators.push(
|
|
891
|
-
{
|
|
892
|
-
tool: "claude-global",
|
|
893
|
-
path: path3.join(os.homedir(), ".claude", "settings.json"),
|
|
894
|
-
generate: () => generateClaudeMcp(config, "global")
|
|
895
|
-
},
|
|
896
|
-
{
|
|
897
|
-
tool: "cursor-global",
|
|
898
|
-
path: path3.join(os.homedir(), ".cursor", "mcp.json"),
|
|
899
|
-
generate: () => generateCursorMcp(config, "global")
|
|
900
|
-
},
|
|
901
|
-
{
|
|
902
|
-
tool: "gemini-global",
|
|
903
|
-
path: path3.join(os.homedir(), ".gemini", "settings.json"),
|
|
904
|
-
generate: () => generateGeminiCliMcp(config, "global")
|
|
905
|
-
}
|
|
906
|
-
);
|
|
907
|
-
}
|
|
908
890
|
for (const generator of generators) {
|
|
909
891
|
try {
|
|
910
892
|
const content = generator.generate();
|
|
@@ -1058,10 +1040,10 @@ Generating configurations for base directory: ${baseDir}`);
|
|
|
1058
1040
|
}
|
|
1059
1041
|
|
|
1060
1042
|
// src/cli/commands/gitignore.ts
|
|
1061
|
-
import { existsSync, readFileSync, writeFileSync } from "fs";
|
|
1062
|
-
import { join as
|
|
1043
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync } from "fs";
|
|
1044
|
+
import { join as join12 } from "path";
|
|
1063
1045
|
var gitignoreCommand = async () => {
|
|
1064
|
-
const gitignorePath =
|
|
1046
|
+
const gitignorePath = join12(process.cwd(), ".gitignore");
|
|
1065
1047
|
const rulesFilesToIgnore = [
|
|
1066
1048
|
"# Generated by rulesync - AI tool configuration files",
|
|
1067
1049
|
"**/.github/copilot-instructions.md",
|
|
@@ -1087,8 +1069,8 @@ var gitignoreCommand = async () => {
|
|
|
1087
1069
|
"**/.roo/mcp.json"
|
|
1088
1070
|
];
|
|
1089
1071
|
let gitignoreContent = "";
|
|
1090
|
-
if (
|
|
1091
|
-
gitignoreContent =
|
|
1072
|
+
if (existsSync2(gitignorePath)) {
|
|
1073
|
+
gitignoreContent = readFileSync2(gitignorePath, "utf-8");
|
|
1092
1074
|
}
|
|
1093
1075
|
const linesToAdd = [];
|
|
1094
1076
|
for (const rule of rulesFilesToIgnore) {
|
|
@@ -1115,17 +1097,17 @@ ${linesToAdd.join("\n")}
|
|
|
1115
1097
|
};
|
|
1116
1098
|
|
|
1117
1099
|
// src/core/importer.ts
|
|
1118
|
-
import { join as
|
|
1100
|
+
import { join as join19 } from "path";
|
|
1119
1101
|
import matter4 from "gray-matter";
|
|
1120
1102
|
|
|
1121
1103
|
// src/parsers/claudecode.ts
|
|
1122
|
-
import { basename as basename2, join as
|
|
1104
|
+
import { basename as basename2, join as join13 } from "path";
|
|
1123
1105
|
async function parseClaudeConfiguration(baseDir = process.cwd()) {
|
|
1124
1106
|
const errors = [];
|
|
1125
1107
|
const rules = [];
|
|
1126
1108
|
let ignorePatterns;
|
|
1127
1109
|
let mcpServers;
|
|
1128
|
-
const claudeFilePath =
|
|
1110
|
+
const claudeFilePath = join13(baseDir, "CLAUDE.md");
|
|
1129
1111
|
if (!await fileExists(claudeFilePath)) {
|
|
1130
1112
|
errors.push("CLAUDE.md file not found");
|
|
1131
1113
|
return { rules, errors };
|
|
@@ -1136,12 +1118,12 @@ async function parseClaudeConfiguration(baseDir = process.cwd()) {
|
|
|
1136
1118
|
if (mainRule) {
|
|
1137
1119
|
rules.push(mainRule);
|
|
1138
1120
|
}
|
|
1139
|
-
const memoryDir =
|
|
1121
|
+
const memoryDir = join13(baseDir, ".claude", "memories");
|
|
1140
1122
|
if (await fileExists(memoryDir)) {
|
|
1141
1123
|
const memoryRules = await parseClaudeMemoryFiles(memoryDir);
|
|
1142
1124
|
rules.push(...memoryRules);
|
|
1143
1125
|
}
|
|
1144
|
-
const settingsPath =
|
|
1126
|
+
const settingsPath = join13(baseDir, ".claude", "settings.json");
|
|
1145
1127
|
if (await fileExists(settingsPath)) {
|
|
1146
1128
|
const settingsResult = await parseClaudeSettings(settingsPath);
|
|
1147
1129
|
if (settingsResult.ignorePatterns) {
|
|
@@ -1198,7 +1180,7 @@ async function parseClaudeMemoryFiles(memoryDir) {
|
|
|
1198
1180
|
const files = await readdir2(memoryDir);
|
|
1199
1181
|
for (const file of files) {
|
|
1200
1182
|
if (file.endsWith(".md")) {
|
|
1201
|
-
const filePath =
|
|
1183
|
+
const filePath = join13(memoryDir, file);
|
|
1202
1184
|
const content = await readFileContent(filePath);
|
|
1203
1185
|
if (content.trim()) {
|
|
1204
1186
|
const filename = basename2(file, ".md");
|
|
@@ -1260,11 +1242,11 @@ async function parseClaudeSettings(settingsPath) {
|
|
|
1260
1242
|
}
|
|
1261
1243
|
|
|
1262
1244
|
// src/parsers/cline.ts
|
|
1263
|
-
import { join as
|
|
1245
|
+
import { join as join14 } from "path";
|
|
1264
1246
|
async function parseClineConfiguration(baseDir = process.cwd()) {
|
|
1265
1247
|
const errors = [];
|
|
1266
1248
|
const rules = [];
|
|
1267
|
-
const clineFilePath =
|
|
1249
|
+
const clineFilePath = join14(baseDir, ".cline", "instructions.md");
|
|
1268
1250
|
if (await fileExists(clineFilePath)) {
|
|
1269
1251
|
try {
|
|
1270
1252
|
const content = await readFileContent(clineFilePath);
|
|
@@ -1287,14 +1269,14 @@ async function parseClineConfiguration(baseDir = process.cwd()) {
|
|
|
1287
1269
|
errors.push(`Failed to parse .cline/instructions.md: ${errorMessage}`);
|
|
1288
1270
|
}
|
|
1289
1271
|
}
|
|
1290
|
-
const clinerulesDirPath =
|
|
1272
|
+
const clinerulesDirPath = join14(baseDir, ".clinerules");
|
|
1291
1273
|
if (await fileExists(clinerulesDirPath)) {
|
|
1292
1274
|
try {
|
|
1293
1275
|
const { readdir: readdir2 } = await import("fs/promises");
|
|
1294
1276
|
const files = await readdir2(clinerulesDirPath);
|
|
1295
1277
|
for (const file of files) {
|
|
1296
1278
|
if (file.endsWith(".md")) {
|
|
1297
|
-
const filePath =
|
|
1279
|
+
const filePath = join14(clinerulesDirPath, file);
|
|
1298
1280
|
try {
|
|
1299
1281
|
const content = await readFileContent(filePath);
|
|
1300
1282
|
if (content.trim()) {
|
|
@@ -1330,12 +1312,12 @@ async function parseClineConfiguration(baseDir = process.cwd()) {
|
|
|
1330
1312
|
}
|
|
1331
1313
|
|
|
1332
1314
|
// src/parsers/copilot.ts
|
|
1333
|
-
import { basename as basename3, join as
|
|
1315
|
+
import { basename as basename3, join as join15 } from "path";
|
|
1334
1316
|
import matter2 from "gray-matter";
|
|
1335
1317
|
async function parseCopilotConfiguration(baseDir = process.cwd()) {
|
|
1336
1318
|
const errors = [];
|
|
1337
1319
|
const rules = [];
|
|
1338
|
-
const copilotFilePath =
|
|
1320
|
+
const copilotFilePath = join15(baseDir, ".github", "copilot-instructions.md");
|
|
1339
1321
|
if (await fileExists(copilotFilePath)) {
|
|
1340
1322
|
try {
|
|
1341
1323
|
const rawContent = await readFileContent(copilotFilePath);
|
|
@@ -1360,14 +1342,14 @@ async function parseCopilotConfiguration(baseDir = process.cwd()) {
|
|
|
1360
1342
|
errors.push(`Failed to parse copilot-instructions.md: ${errorMessage}`);
|
|
1361
1343
|
}
|
|
1362
1344
|
}
|
|
1363
|
-
const instructionsDir =
|
|
1345
|
+
const instructionsDir = join15(baseDir, ".github", "instructions");
|
|
1364
1346
|
if (await fileExists(instructionsDir)) {
|
|
1365
1347
|
try {
|
|
1366
1348
|
const { readdir: readdir2 } = await import("fs/promises");
|
|
1367
1349
|
const files = await readdir2(instructionsDir);
|
|
1368
1350
|
for (const file of files) {
|
|
1369
1351
|
if (file.endsWith(".instructions.md")) {
|
|
1370
|
-
const filePath =
|
|
1352
|
+
const filePath = join15(instructionsDir, file);
|
|
1371
1353
|
const rawContent = await readFileContent(filePath);
|
|
1372
1354
|
const parsed = matter2(rawContent);
|
|
1373
1355
|
const content = parsed.content.trim();
|
|
@@ -1402,7 +1384,7 @@ async function parseCopilotConfiguration(baseDir = process.cwd()) {
|
|
|
1402
1384
|
}
|
|
1403
1385
|
|
|
1404
1386
|
// src/parsers/cursor.ts
|
|
1405
|
-
import { basename as basename4, join as
|
|
1387
|
+
import { basename as basename4, join as join16 } from "path";
|
|
1406
1388
|
import matter3 from "gray-matter";
|
|
1407
1389
|
import { DEFAULT_SCHEMA, FAILSAFE_SCHEMA, load } from "js-yaml";
|
|
1408
1390
|
var customMatterOptions = {
|
|
@@ -1507,7 +1489,7 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
|
|
|
1507
1489
|
const rules = [];
|
|
1508
1490
|
let ignorePatterns;
|
|
1509
1491
|
let mcpServers;
|
|
1510
|
-
const cursorFilePath =
|
|
1492
|
+
const cursorFilePath = join16(baseDir, ".cursorrules");
|
|
1511
1493
|
if (await fileExists(cursorFilePath)) {
|
|
1512
1494
|
try {
|
|
1513
1495
|
const rawContent = await readFileContent(cursorFilePath);
|
|
@@ -1528,14 +1510,14 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
|
|
|
1528
1510
|
errors.push(`Failed to parse .cursorrules file: ${errorMessage}`);
|
|
1529
1511
|
}
|
|
1530
1512
|
}
|
|
1531
|
-
const cursorRulesDir =
|
|
1513
|
+
const cursorRulesDir = join16(baseDir, ".cursor", "rules");
|
|
1532
1514
|
if (await fileExists(cursorRulesDir)) {
|
|
1533
1515
|
try {
|
|
1534
1516
|
const { readdir: readdir2 } = await import("fs/promises");
|
|
1535
1517
|
const files = await readdir2(cursorRulesDir);
|
|
1536
1518
|
for (const file of files) {
|
|
1537
1519
|
if (file.endsWith(".mdc")) {
|
|
1538
|
-
const filePath =
|
|
1520
|
+
const filePath = join16(cursorRulesDir, file);
|
|
1539
1521
|
try {
|
|
1540
1522
|
const rawContent = await readFileContent(filePath);
|
|
1541
1523
|
const parsed = matter3(rawContent, customMatterOptions);
|
|
@@ -1564,7 +1546,7 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
|
|
|
1564
1546
|
if (rules.length === 0) {
|
|
1565
1547
|
errors.push("No Cursor configuration files found (.cursorrules or .cursor/rules/*.mdc)");
|
|
1566
1548
|
}
|
|
1567
|
-
const cursorIgnorePath =
|
|
1549
|
+
const cursorIgnorePath = join16(baseDir, ".cursorignore");
|
|
1568
1550
|
if (await fileExists(cursorIgnorePath)) {
|
|
1569
1551
|
try {
|
|
1570
1552
|
const content = await readFileContent(cursorIgnorePath);
|
|
@@ -1577,7 +1559,7 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
|
|
|
1577
1559
|
errors.push(`Failed to parse .cursorignore: ${errorMessage}`);
|
|
1578
1560
|
}
|
|
1579
1561
|
}
|
|
1580
|
-
const cursorMcpPath =
|
|
1562
|
+
const cursorMcpPath = join16(baseDir, ".cursor", "mcp.json");
|
|
1581
1563
|
if (await fileExists(cursorMcpPath)) {
|
|
1582
1564
|
try {
|
|
1583
1565
|
const content = await readFileContent(cursorMcpPath);
|
|
@@ -1599,13 +1581,13 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
|
|
|
1599
1581
|
}
|
|
1600
1582
|
|
|
1601
1583
|
// src/parsers/geminicli.ts
|
|
1602
|
-
import { basename as basename5, join as
|
|
1584
|
+
import { basename as basename5, join as join17 } from "path";
|
|
1603
1585
|
async function parseGeminiConfiguration(baseDir = process.cwd()) {
|
|
1604
1586
|
const errors = [];
|
|
1605
1587
|
const rules = [];
|
|
1606
1588
|
let ignorePatterns;
|
|
1607
1589
|
let mcpServers;
|
|
1608
|
-
const geminiFilePath =
|
|
1590
|
+
const geminiFilePath = join17(baseDir, "GEMINI.md");
|
|
1609
1591
|
if (!await fileExists(geminiFilePath)) {
|
|
1610
1592
|
errors.push("GEMINI.md file not found");
|
|
1611
1593
|
return { rules, errors };
|
|
@@ -1616,12 +1598,12 @@ async function parseGeminiConfiguration(baseDir = process.cwd()) {
|
|
|
1616
1598
|
if (mainRule) {
|
|
1617
1599
|
rules.push(mainRule);
|
|
1618
1600
|
}
|
|
1619
|
-
const memoryDir =
|
|
1601
|
+
const memoryDir = join17(baseDir, ".gemini", "memories");
|
|
1620
1602
|
if (await fileExists(memoryDir)) {
|
|
1621
1603
|
const memoryRules = await parseGeminiMemoryFiles(memoryDir);
|
|
1622
1604
|
rules.push(...memoryRules);
|
|
1623
1605
|
}
|
|
1624
|
-
const settingsPath =
|
|
1606
|
+
const settingsPath = join17(baseDir, ".gemini", "settings.json");
|
|
1625
1607
|
if (await fileExists(settingsPath)) {
|
|
1626
1608
|
const settingsResult = await parseGeminiSettings(settingsPath);
|
|
1627
1609
|
if (settingsResult.ignorePatterns) {
|
|
@@ -1632,7 +1614,7 @@ async function parseGeminiConfiguration(baseDir = process.cwd()) {
|
|
|
1632
1614
|
}
|
|
1633
1615
|
errors.push(...settingsResult.errors);
|
|
1634
1616
|
}
|
|
1635
|
-
const aiexcludePath =
|
|
1617
|
+
const aiexcludePath = join17(baseDir, ".aiexclude");
|
|
1636
1618
|
if (await fileExists(aiexcludePath)) {
|
|
1637
1619
|
const aiexcludePatterns = await parseAiexclude(aiexcludePath);
|
|
1638
1620
|
if (aiexcludePatterns.length > 0) {
|
|
@@ -1685,7 +1667,7 @@ async function parseGeminiMemoryFiles(memoryDir) {
|
|
|
1685
1667
|
const files = await readdir2(memoryDir);
|
|
1686
1668
|
for (const file of files) {
|
|
1687
1669
|
if (file.endsWith(".md")) {
|
|
1688
|
-
const filePath =
|
|
1670
|
+
const filePath = join17(memoryDir, file);
|
|
1689
1671
|
const content = await readFileContent(filePath);
|
|
1690
1672
|
if (content.trim()) {
|
|
1691
1673
|
const filename = basename5(file, ".md");
|
|
@@ -1737,11 +1719,11 @@ async function parseAiexclude(aiexcludePath) {
|
|
|
1737
1719
|
}
|
|
1738
1720
|
|
|
1739
1721
|
// src/parsers/roo.ts
|
|
1740
|
-
import { join as
|
|
1722
|
+
import { join as join18 } from "path";
|
|
1741
1723
|
async function parseRooConfiguration(baseDir = process.cwd()) {
|
|
1742
1724
|
const errors = [];
|
|
1743
1725
|
const rules = [];
|
|
1744
|
-
const rooFilePath =
|
|
1726
|
+
const rooFilePath = join18(baseDir, ".roo", "instructions.md");
|
|
1745
1727
|
if (await fileExists(rooFilePath)) {
|
|
1746
1728
|
try {
|
|
1747
1729
|
const content = await readFileContent(rooFilePath);
|
|
@@ -1764,14 +1746,14 @@ async function parseRooConfiguration(baseDir = process.cwd()) {
|
|
|
1764
1746
|
errors.push(`Failed to parse .roo/instructions.md: ${errorMessage}`);
|
|
1765
1747
|
}
|
|
1766
1748
|
}
|
|
1767
|
-
const rooRulesDir =
|
|
1749
|
+
const rooRulesDir = join18(baseDir, ".roo", "rules");
|
|
1768
1750
|
if (await fileExists(rooRulesDir)) {
|
|
1769
1751
|
try {
|
|
1770
1752
|
const { readdir: readdir2 } = await import("fs/promises");
|
|
1771
1753
|
const files = await readdir2(rooRulesDir);
|
|
1772
1754
|
for (const file of files) {
|
|
1773
1755
|
if (file.endsWith(".md")) {
|
|
1774
|
-
const filePath =
|
|
1756
|
+
const filePath = join18(rooRulesDir, file);
|
|
1775
1757
|
try {
|
|
1776
1758
|
const content = await readFileContent(filePath);
|
|
1777
1759
|
if (content.trim()) {
|
|
@@ -1872,7 +1854,7 @@ async function importConfiguration(options) {
|
|
|
1872
1854
|
if (rules.length === 0 && !ignorePatterns && !mcpServers) {
|
|
1873
1855
|
return { success: false, rulesCreated: 0, errors };
|
|
1874
1856
|
}
|
|
1875
|
-
const rulesDirPath =
|
|
1857
|
+
const rulesDirPath = join19(baseDir, rulesDir);
|
|
1876
1858
|
try {
|
|
1877
1859
|
const { mkdir: mkdir3 } = await import("fs/promises");
|
|
1878
1860
|
await mkdir3(rulesDirPath, { recursive: true });
|
|
@@ -1886,7 +1868,7 @@ async function importConfiguration(options) {
|
|
|
1886
1868
|
try {
|
|
1887
1869
|
const baseFilename = `${tool}__${rule.filename}`;
|
|
1888
1870
|
const filename = await generateUniqueFilename(rulesDirPath, baseFilename);
|
|
1889
|
-
const filePath =
|
|
1871
|
+
const filePath = join19(rulesDirPath, `${filename}.md`);
|
|
1890
1872
|
const content = generateRuleFileContent(rule);
|
|
1891
1873
|
await writeFileContent(filePath, content);
|
|
1892
1874
|
rulesCreated++;
|
|
@@ -1901,7 +1883,7 @@ async function importConfiguration(options) {
|
|
|
1901
1883
|
let ignoreFileCreated = false;
|
|
1902
1884
|
if (ignorePatterns && ignorePatterns.length > 0) {
|
|
1903
1885
|
try {
|
|
1904
|
-
const rulesyncignorePath =
|
|
1886
|
+
const rulesyncignorePath = join19(baseDir, ".rulesyncignore");
|
|
1905
1887
|
const ignoreContent = `${ignorePatterns.join("\n")}
|
|
1906
1888
|
`;
|
|
1907
1889
|
await writeFileContent(rulesyncignorePath, ignoreContent);
|
|
@@ -1917,7 +1899,7 @@ async function importConfiguration(options) {
|
|
|
1917
1899
|
let mcpFileCreated = false;
|
|
1918
1900
|
if (mcpServers && Object.keys(mcpServers).length > 0) {
|
|
1919
1901
|
try {
|
|
1920
|
-
const mcpPath =
|
|
1902
|
+
const mcpPath = join19(baseDir, rulesDir, ".mcp.json");
|
|
1921
1903
|
const mcpContent = `${JSON.stringify({ mcpServers }, null, 2)}
|
|
1922
1904
|
`;
|
|
1923
1905
|
await writeFileContent(mcpPath, mcpContent);
|
|
@@ -1945,7 +1927,7 @@ function generateRuleFileContent(rule) {
|
|
|
1945
1927
|
async function generateUniqueFilename(rulesDir, baseFilename) {
|
|
1946
1928
|
let filename = baseFilename;
|
|
1947
1929
|
let counter = 1;
|
|
1948
|
-
while (await fileExists(
|
|
1930
|
+
while (await fileExists(join19(rulesDir, `${filename}.md`))) {
|
|
1949
1931
|
filename = `${baseFilename}-${counter}`;
|
|
1950
1932
|
counter++;
|
|
1951
1933
|
}
|
|
@@ -2010,7 +1992,7 @@ async function importCommand(options = {}) {
|
|
|
2010
1992
|
}
|
|
2011
1993
|
|
|
2012
1994
|
// src/cli/commands/init.ts
|
|
2013
|
-
import { join as
|
|
1995
|
+
import { join as join20 } from "path";
|
|
2014
1996
|
async function initCommand() {
|
|
2015
1997
|
const aiRulesDir = ".rulesync";
|
|
2016
1998
|
console.log("Initializing rulesync...");
|
|
@@ -2140,7 +2122,7 @@ globs: ["src/api/**/*.ts", "src/services/**/*.ts", "src/models/**/*.ts"]
|
|
|
2140
2122
|
}
|
|
2141
2123
|
];
|
|
2142
2124
|
for (const file of sampleFiles) {
|
|
2143
|
-
const filepath =
|
|
2125
|
+
const filepath = join20(aiRulesDir, file.filename);
|
|
2144
2126
|
if (!await fileExists(filepath)) {
|
|
2145
2127
|
await writeFileContent(filepath, file.content);
|
|
2146
2128
|
console.log(`Created ${filepath}`);
|
|
@@ -2283,7 +2265,7 @@ async function watchCommand() {
|
|
|
2283
2265
|
|
|
2284
2266
|
// src/cli/index.ts
|
|
2285
2267
|
var program = new Command();
|
|
2286
|
-
program.name("rulesync").description("Unified AI rules management CLI tool").version("0.
|
|
2268
|
+
program.name("rulesync").description("Unified AI rules management CLI tool").version("0.43.0");
|
|
2287
2269
|
program.command("init").description("Initialize rulesync in current directory").action(initCommand);
|
|
2288
2270
|
program.command("add <filename>").description("Add a new rule file").action(addCommand);
|
|
2289
2271
|
program.command("gitignore").description("Add generated files to .gitignore").action(gitignoreCommand);
|