rulesync 0.42.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-HMMPZV7X.js → chunk-B3627VQY.js} +8 -6
- package/dist/{chunk-3NRSCDLQ.js → chunk-RL3TE3EZ.js} +5 -5
- package/dist/{chunk-2SPL7QTK.js → chunk-TCK42GOL.js} +1 -1
- package/dist/chunk-THWXK5Z2.js +35 -0
- package/dist/{chunk-X3FEMISQ.js → chunk-XHRBWFGI.js} +1 -1
- package/dist/{chunk-D3YGI36J.js → chunk-ZMAL5LIX.js} +7 -2
- package/dist/{claude-3YGZIO5F.js → claudecode-ITHKV345.js} +2 -2
- package/dist/{cline-ERYW7TOO.js → cline-PKE6TYNJ.js} +2 -2
- package/dist/{copilot-NLSI3ID7.js → copilot-5JP6D4NO.js} +2 -2
- package/dist/{cursor-CX55HMO4.js → cursor-RHFDG6T2.js} +2 -2
- package/dist/{geminicli-XHQR4RCQ.js → geminicli-B3FFO5WV.js} +2 -2
- package/dist/index.cjs +133 -152
- package/dist/index.js +106 -104
- package/dist/{roo-OKE7XF3B.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,11 +816,11 @@ async function validateRule(rule) {
|
|
|
814
816
|
}
|
|
815
817
|
|
|
816
818
|
// src/core/mcp-generator.ts
|
|
817
|
-
import path3 from "path";
|
|
819
|
+
import * as path3 from "path";
|
|
818
820
|
|
|
819
821
|
// src/core/mcp-parser.ts
|
|
820
|
-
import fs from "fs";
|
|
821
|
-
import path2 from "path";
|
|
822
|
+
import * as fs from "fs";
|
|
823
|
+
import * as path2 from "path";
|
|
822
824
|
function parseMcpConfig(projectRoot) {
|
|
823
825
|
const mcpPath = path2.join(projectRoot, ".rulesync", ".mcp.json");
|
|
824
826
|
if (!fs.existsSync(mcpPath)) {
|
|
@@ -1038,10 +1040,10 @@ Generating configurations for base directory: ${baseDir}`);
|
|
|
1038
1040
|
}
|
|
1039
1041
|
|
|
1040
1042
|
// src/cli/commands/gitignore.ts
|
|
1041
|
-
import { existsSync, readFileSync, writeFileSync } from "fs";
|
|
1042
|
-
import { join as
|
|
1043
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync } from "fs";
|
|
1044
|
+
import { join as join12 } from "path";
|
|
1043
1045
|
var gitignoreCommand = async () => {
|
|
1044
|
-
const gitignorePath =
|
|
1046
|
+
const gitignorePath = join12(process.cwd(), ".gitignore");
|
|
1045
1047
|
const rulesFilesToIgnore = [
|
|
1046
1048
|
"# Generated by rulesync - AI tool configuration files",
|
|
1047
1049
|
"**/.github/copilot-instructions.md",
|
|
@@ -1067,8 +1069,8 @@ var gitignoreCommand = async () => {
|
|
|
1067
1069
|
"**/.roo/mcp.json"
|
|
1068
1070
|
];
|
|
1069
1071
|
let gitignoreContent = "";
|
|
1070
|
-
if (
|
|
1071
|
-
gitignoreContent =
|
|
1072
|
+
if (existsSync2(gitignorePath)) {
|
|
1073
|
+
gitignoreContent = readFileSync2(gitignorePath, "utf-8");
|
|
1072
1074
|
}
|
|
1073
1075
|
const linesToAdd = [];
|
|
1074
1076
|
for (const rule of rulesFilesToIgnore) {
|
|
@@ -1095,17 +1097,17 @@ ${linesToAdd.join("\n")}
|
|
|
1095
1097
|
};
|
|
1096
1098
|
|
|
1097
1099
|
// src/core/importer.ts
|
|
1098
|
-
import { join as
|
|
1100
|
+
import { join as join19 } from "path";
|
|
1099
1101
|
import matter4 from "gray-matter";
|
|
1100
1102
|
|
|
1101
1103
|
// src/parsers/claudecode.ts
|
|
1102
|
-
import { basename as basename2, join as
|
|
1104
|
+
import { basename as basename2, join as join13 } from "path";
|
|
1103
1105
|
async function parseClaudeConfiguration(baseDir = process.cwd()) {
|
|
1104
1106
|
const errors = [];
|
|
1105
1107
|
const rules = [];
|
|
1106
1108
|
let ignorePatterns;
|
|
1107
1109
|
let mcpServers;
|
|
1108
|
-
const claudeFilePath =
|
|
1110
|
+
const claudeFilePath = join13(baseDir, "CLAUDE.md");
|
|
1109
1111
|
if (!await fileExists(claudeFilePath)) {
|
|
1110
1112
|
errors.push("CLAUDE.md file not found");
|
|
1111
1113
|
return { rules, errors };
|
|
@@ -1116,12 +1118,12 @@ async function parseClaudeConfiguration(baseDir = process.cwd()) {
|
|
|
1116
1118
|
if (mainRule) {
|
|
1117
1119
|
rules.push(mainRule);
|
|
1118
1120
|
}
|
|
1119
|
-
const memoryDir =
|
|
1121
|
+
const memoryDir = join13(baseDir, ".claude", "memories");
|
|
1120
1122
|
if (await fileExists(memoryDir)) {
|
|
1121
1123
|
const memoryRules = await parseClaudeMemoryFiles(memoryDir);
|
|
1122
1124
|
rules.push(...memoryRules);
|
|
1123
1125
|
}
|
|
1124
|
-
const settingsPath =
|
|
1126
|
+
const settingsPath = join13(baseDir, ".claude", "settings.json");
|
|
1125
1127
|
if (await fileExists(settingsPath)) {
|
|
1126
1128
|
const settingsResult = await parseClaudeSettings(settingsPath);
|
|
1127
1129
|
if (settingsResult.ignorePatterns) {
|
|
@@ -1178,7 +1180,7 @@ async function parseClaudeMemoryFiles(memoryDir) {
|
|
|
1178
1180
|
const files = await readdir2(memoryDir);
|
|
1179
1181
|
for (const file of files) {
|
|
1180
1182
|
if (file.endsWith(".md")) {
|
|
1181
|
-
const filePath =
|
|
1183
|
+
const filePath = join13(memoryDir, file);
|
|
1182
1184
|
const content = await readFileContent(filePath);
|
|
1183
1185
|
if (content.trim()) {
|
|
1184
1186
|
const filename = basename2(file, ".md");
|
|
@@ -1240,11 +1242,11 @@ async function parseClaudeSettings(settingsPath) {
|
|
|
1240
1242
|
}
|
|
1241
1243
|
|
|
1242
1244
|
// src/parsers/cline.ts
|
|
1243
|
-
import { join as
|
|
1245
|
+
import { join as join14 } from "path";
|
|
1244
1246
|
async function parseClineConfiguration(baseDir = process.cwd()) {
|
|
1245
1247
|
const errors = [];
|
|
1246
1248
|
const rules = [];
|
|
1247
|
-
const clineFilePath =
|
|
1249
|
+
const clineFilePath = join14(baseDir, ".cline", "instructions.md");
|
|
1248
1250
|
if (await fileExists(clineFilePath)) {
|
|
1249
1251
|
try {
|
|
1250
1252
|
const content = await readFileContent(clineFilePath);
|
|
@@ -1267,14 +1269,14 @@ async function parseClineConfiguration(baseDir = process.cwd()) {
|
|
|
1267
1269
|
errors.push(`Failed to parse .cline/instructions.md: ${errorMessage}`);
|
|
1268
1270
|
}
|
|
1269
1271
|
}
|
|
1270
|
-
const clinerulesDirPath =
|
|
1272
|
+
const clinerulesDirPath = join14(baseDir, ".clinerules");
|
|
1271
1273
|
if (await fileExists(clinerulesDirPath)) {
|
|
1272
1274
|
try {
|
|
1273
1275
|
const { readdir: readdir2 } = await import("fs/promises");
|
|
1274
1276
|
const files = await readdir2(clinerulesDirPath);
|
|
1275
1277
|
for (const file of files) {
|
|
1276
1278
|
if (file.endsWith(".md")) {
|
|
1277
|
-
const filePath =
|
|
1279
|
+
const filePath = join14(clinerulesDirPath, file);
|
|
1278
1280
|
try {
|
|
1279
1281
|
const content = await readFileContent(filePath);
|
|
1280
1282
|
if (content.trim()) {
|
|
@@ -1310,12 +1312,12 @@ async function parseClineConfiguration(baseDir = process.cwd()) {
|
|
|
1310
1312
|
}
|
|
1311
1313
|
|
|
1312
1314
|
// src/parsers/copilot.ts
|
|
1313
|
-
import { basename as basename3, join as
|
|
1315
|
+
import { basename as basename3, join as join15 } from "path";
|
|
1314
1316
|
import matter2 from "gray-matter";
|
|
1315
1317
|
async function parseCopilotConfiguration(baseDir = process.cwd()) {
|
|
1316
1318
|
const errors = [];
|
|
1317
1319
|
const rules = [];
|
|
1318
|
-
const copilotFilePath =
|
|
1320
|
+
const copilotFilePath = join15(baseDir, ".github", "copilot-instructions.md");
|
|
1319
1321
|
if (await fileExists(copilotFilePath)) {
|
|
1320
1322
|
try {
|
|
1321
1323
|
const rawContent = await readFileContent(copilotFilePath);
|
|
@@ -1340,14 +1342,14 @@ async function parseCopilotConfiguration(baseDir = process.cwd()) {
|
|
|
1340
1342
|
errors.push(`Failed to parse copilot-instructions.md: ${errorMessage}`);
|
|
1341
1343
|
}
|
|
1342
1344
|
}
|
|
1343
|
-
const instructionsDir =
|
|
1345
|
+
const instructionsDir = join15(baseDir, ".github", "instructions");
|
|
1344
1346
|
if (await fileExists(instructionsDir)) {
|
|
1345
1347
|
try {
|
|
1346
1348
|
const { readdir: readdir2 } = await import("fs/promises");
|
|
1347
1349
|
const files = await readdir2(instructionsDir);
|
|
1348
1350
|
for (const file of files) {
|
|
1349
1351
|
if (file.endsWith(".instructions.md")) {
|
|
1350
|
-
const filePath =
|
|
1352
|
+
const filePath = join15(instructionsDir, file);
|
|
1351
1353
|
const rawContent = await readFileContent(filePath);
|
|
1352
1354
|
const parsed = matter2(rawContent);
|
|
1353
1355
|
const content = parsed.content.trim();
|
|
@@ -1382,7 +1384,7 @@ async function parseCopilotConfiguration(baseDir = process.cwd()) {
|
|
|
1382
1384
|
}
|
|
1383
1385
|
|
|
1384
1386
|
// src/parsers/cursor.ts
|
|
1385
|
-
import { basename as basename4, join as
|
|
1387
|
+
import { basename as basename4, join as join16 } from "path";
|
|
1386
1388
|
import matter3 from "gray-matter";
|
|
1387
1389
|
import { DEFAULT_SCHEMA, FAILSAFE_SCHEMA, load } from "js-yaml";
|
|
1388
1390
|
var customMatterOptions = {
|
|
@@ -1487,7 +1489,7 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
|
|
|
1487
1489
|
const rules = [];
|
|
1488
1490
|
let ignorePatterns;
|
|
1489
1491
|
let mcpServers;
|
|
1490
|
-
const cursorFilePath =
|
|
1492
|
+
const cursorFilePath = join16(baseDir, ".cursorrules");
|
|
1491
1493
|
if (await fileExists(cursorFilePath)) {
|
|
1492
1494
|
try {
|
|
1493
1495
|
const rawContent = await readFileContent(cursorFilePath);
|
|
@@ -1508,14 +1510,14 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
|
|
|
1508
1510
|
errors.push(`Failed to parse .cursorrules file: ${errorMessage}`);
|
|
1509
1511
|
}
|
|
1510
1512
|
}
|
|
1511
|
-
const cursorRulesDir =
|
|
1513
|
+
const cursorRulesDir = join16(baseDir, ".cursor", "rules");
|
|
1512
1514
|
if (await fileExists(cursorRulesDir)) {
|
|
1513
1515
|
try {
|
|
1514
1516
|
const { readdir: readdir2 } = await import("fs/promises");
|
|
1515
1517
|
const files = await readdir2(cursorRulesDir);
|
|
1516
1518
|
for (const file of files) {
|
|
1517
1519
|
if (file.endsWith(".mdc")) {
|
|
1518
|
-
const filePath =
|
|
1520
|
+
const filePath = join16(cursorRulesDir, file);
|
|
1519
1521
|
try {
|
|
1520
1522
|
const rawContent = await readFileContent(filePath);
|
|
1521
1523
|
const parsed = matter3(rawContent, customMatterOptions);
|
|
@@ -1544,7 +1546,7 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
|
|
|
1544
1546
|
if (rules.length === 0) {
|
|
1545
1547
|
errors.push("No Cursor configuration files found (.cursorrules or .cursor/rules/*.mdc)");
|
|
1546
1548
|
}
|
|
1547
|
-
const cursorIgnorePath =
|
|
1549
|
+
const cursorIgnorePath = join16(baseDir, ".cursorignore");
|
|
1548
1550
|
if (await fileExists(cursorIgnorePath)) {
|
|
1549
1551
|
try {
|
|
1550
1552
|
const content = await readFileContent(cursorIgnorePath);
|
|
@@ -1557,7 +1559,7 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
|
|
|
1557
1559
|
errors.push(`Failed to parse .cursorignore: ${errorMessage}`);
|
|
1558
1560
|
}
|
|
1559
1561
|
}
|
|
1560
|
-
const cursorMcpPath =
|
|
1562
|
+
const cursorMcpPath = join16(baseDir, ".cursor", "mcp.json");
|
|
1561
1563
|
if (await fileExists(cursorMcpPath)) {
|
|
1562
1564
|
try {
|
|
1563
1565
|
const content = await readFileContent(cursorMcpPath);
|
|
@@ -1579,13 +1581,13 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
|
|
|
1579
1581
|
}
|
|
1580
1582
|
|
|
1581
1583
|
// src/parsers/geminicli.ts
|
|
1582
|
-
import { basename as basename5, join as
|
|
1584
|
+
import { basename as basename5, join as join17 } from "path";
|
|
1583
1585
|
async function parseGeminiConfiguration(baseDir = process.cwd()) {
|
|
1584
1586
|
const errors = [];
|
|
1585
1587
|
const rules = [];
|
|
1586
1588
|
let ignorePatterns;
|
|
1587
1589
|
let mcpServers;
|
|
1588
|
-
const geminiFilePath =
|
|
1590
|
+
const geminiFilePath = join17(baseDir, "GEMINI.md");
|
|
1589
1591
|
if (!await fileExists(geminiFilePath)) {
|
|
1590
1592
|
errors.push("GEMINI.md file not found");
|
|
1591
1593
|
return { rules, errors };
|
|
@@ -1596,12 +1598,12 @@ async function parseGeminiConfiguration(baseDir = process.cwd()) {
|
|
|
1596
1598
|
if (mainRule) {
|
|
1597
1599
|
rules.push(mainRule);
|
|
1598
1600
|
}
|
|
1599
|
-
const memoryDir =
|
|
1601
|
+
const memoryDir = join17(baseDir, ".gemini", "memories");
|
|
1600
1602
|
if (await fileExists(memoryDir)) {
|
|
1601
1603
|
const memoryRules = await parseGeminiMemoryFiles(memoryDir);
|
|
1602
1604
|
rules.push(...memoryRules);
|
|
1603
1605
|
}
|
|
1604
|
-
const settingsPath =
|
|
1606
|
+
const settingsPath = join17(baseDir, ".gemini", "settings.json");
|
|
1605
1607
|
if (await fileExists(settingsPath)) {
|
|
1606
1608
|
const settingsResult = await parseGeminiSettings(settingsPath);
|
|
1607
1609
|
if (settingsResult.ignorePatterns) {
|
|
@@ -1612,7 +1614,7 @@ async function parseGeminiConfiguration(baseDir = process.cwd()) {
|
|
|
1612
1614
|
}
|
|
1613
1615
|
errors.push(...settingsResult.errors);
|
|
1614
1616
|
}
|
|
1615
|
-
const aiexcludePath =
|
|
1617
|
+
const aiexcludePath = join17(baseDir, ".aiexclude");
|
|
1616
1618
|
if (await fileExists(aiexcludePath)) {
|
|
1617
1619
|
const aiexcludePatterns = await parseAiexclude(aiexcludePath);
|
|
1618
1620
|
if (aiexcludePatterns.length > 0) {
|
|
@@ -1665,7 +1667,7 @@ async function parseGeminiMemoryFiles(memoryDir) {
|
|
|
1665
1667
|
const files = await readdir2(memoryDir);
|
|
1666
1668
|
for (const file of files) {
|
|
1667
1669
|
if (file.endsWith(".md")) {
|
|
1668
|
-
const filePath =
|
|
1670
|
+
const filePath = join17(memoryDir, file);
|
|
1669
1671
|
const content = await readFileContent(filePath);
|
|
1670
1672
|
if (content.trim()) {
|
|
1671
1673
|
const filename = basename5(file, ".md");
|
|
@@ -1717,11 +1719,11 @@ async function parseAiexclude(aiexcludePath) {
|
|
|
1717
1719
|
}
|
|
1718
1720
|
|
|
1719
1721
|
// src/parsers/roo.ts
|
|
1720
|
-
import { join as
|
|
1722
|
+
import { join as join18 } from "path";
|
|
1721
1723
|
async function parseRooConfiguration(baseDir = process.cwd()) {
|
|
1722
1724
|
const errors = [];
|
|
1723
1725
|
const rules = [];
|
|
1724
|
-
const rooFilePath =
|
|
1726
|
+
const rooFilePath = join18(baseDir, ".roo", "instructions.md");
|
|
1725
1727
|
if (await fileExists(rooFilePath)) {
|
|
1726
1728
|
try {
|
|
1727
1729
|
const content = await readFileContent(rooFilePath);
|
|
@@ -1744,14 +1746,14 @@ async function parseRooConfiguration(baseDir = process.cwd()) {
|
|
|
1744
1746
|
errors.push(`Failed to parse .roo/instructions.md: ${errorMessage}`);
|
|
1745
1747
|
}
|
|
1746
1748
|
}
|
|
1747
|
-
const rooRulesDir =
|
|
1749
|
+
const rooRulesDir = join18(baseDir, ".roo", "rules");
|
|
1748
1750
|
if (await fileExists(rooRulesDir)) {
|
|
1749
1751
|
try {
|
|
1750
1752
|
const { readdir: readdir2 } = await import("fs/promises");
|
|
1751
1753
|
const files = await readdir2(rooRulesDir);
|
|
1752
1754
|
for (const file of files) {
|
|
1753
1755
|
if (file.endsWith(".md")) {
|
|
1754
|
-
const filePath =
|
|
1756
|
+
const filePath = join18(rooRulesDir, file);
|
|
1755
1757
|
try {
|
|
1756
1758
|
const content = await readFileContent(filePath);
|
|
1757
1759
|
if (content.trim()) {
|
|
@@ -1852,7 +1854,7 @@ async function importConfiguration(options) {
|
|
|
1852
1854
|
if (rules.length === 0 && !ignorePatterns && !mcpServers) {
|
|
1853
1855
|
return { success: false, rulesCreated: 0, errors };
|
|
1854
1856
|
}
|
|
1855
|
-
const rulesDirPath =
|
|
1857
|
+
const rulesDirPath = join19(baseDir, rulesDir);
|
|
1856
1858
|
try {
|
|
1857
1859
|
const { mkdir: mkdir3 } = await import("fs/promises");
|
|
1858
1860
|
await mkdir3(rulesDirPath, { recursive: true });
|
|
@@ -1866,7 +1868,7 @@ async function importConfiguration(options) {
|
|
|
1866
1868
|
try {
|
|
1867
1869
|
const baseFilename = `${tool}__${rule.filename}`;
|
|
1868
1870
|
const filename = await generateUniqueFilename(rulesDirPath, baseFilename);
|
|
1869
|
-
const filePath =
|
|
1871
|
+
const filePath = join19(rulesDirPath, `${filename}.md`);
|
|
1870
1872
|
const content = generateRuleFileContent(rule);
|
|
1871
1873
|
await writeFileContent(filePath, content);
|
|
1872
1874
|
rulesCreated++;
|
|
@@ -1881,7 +1883,7 @@ async function importConfiguration(options) {
|
|
|
1881
1883
|
let ignoreFileCreated = false;
|
|
1882
1884
|
if (ignorePatterns && ignorePatterns.length > 0) {
|
|
1883
1885
|
try {
|
|
1884
|
-
const rulesyncignorePath =
|
|
1886
|
+
const rulesyncignorePath = join19(baseDir, ".rulesyncignore");
|
|
1885
1887
|
const ignoreContent = `${ignorePatterns.join("\n")}
|
|
1886
1888
|
`;
|
|
1887
1889
|
await writeFileContent(rulesyncignorePath, ignoreContent);
|
|
@@ -1897,7 +1899,7 @@ async function importConfiguration(options) {
|
|
|
1897
1899
|
let mcpFileCreated = false;
|
|
1898
1900
|
if (mcpServers && Object.keys(mcpServers).length > 0) {
|
|
1899
1901
|
try {
|
|
1900
|
-
const mcpPath =
|
|
1902
|
+
const mcpPath = join19(baseDir, rulesDir, ".mcp.json");
|
|
1901
1903
|
const mcpContent = `${JSON.stringify({ mcpServers }, null, 2)}
|
|
1902
1904
|
`;
|
|
1903
1905
|
await writeFileContent(mcpPath, mcpContent);
|
|
@@ -1925,7 +1927,7 @@ function generateRuleFileContent(rule) {
|
|
|
1925
1927
|
async function generateUniqueFilename(rulesDir, baseFilename) {
|
|
1926
1928
|
let filename = baseFilename;
|
|
1927
1929
|
let counter = 1;
|
|
1928
|
-
while (await fileExists(
|
|
1930
|
+
while (await fileExists(join19(rulesDir, `${filename}.md`))) {
|
|
1929
1931
|
filename = `${baseFilename}-${counter}`;
|
|
1930
1932
|
counter++;
|
|
1931
1933
|
}
|
|
@@ -1990,7 +1992,7 @@ async function importCommand(options = {}) {
|
|
|
1990
1992
|
}
|
|
1991
1993
|
|
|
1992
1994
|
// src/cli/commands/init.ts
|
|
1993
|
-
import { join as
|
|
1995
|
+
import { join as join20 } from "path";
|
|
1994
1996
|
async function initCommand() {
|
|
1995
1997
|
const aiRulesDir = ".rulesync";
|
|
1996
1998
|
console.log("Initializing rulesync...");
|
|
@@ -2120,7 +2122,7 @@ globs: ["src/api/**/*.ts", "src/services/**/*.ts", "src/models/**/*.ts"]
|
|
|
2120
2122
|
}
|
|
2121
2123
|
];
|
|
2122
2124
|
for (const file of sampleFiles) {
|
|
2123
|
-
const filepath =
|
|
2125
|
+
const filepath = join20(aiRulesDir, file.filename);
|
|
2124
2126
|
if (!await fileExists(filepath)) {
|
|
2125
2127
|
await writeFileContent(filepath, file.content);
|
|
2126
2128
|
console.log(`Created ${filepath}`);
|
|
@@ -2263,7 +2265,7 @@ async function watchCommand() {
|
|
|
2263
2265
|
|
|
2264
2266
|
// src/cli/index.ts
|
|
2265
2267
|
var program = new Command();
|
|
2266
|
-
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");
|
|
2267
2269
|
program.command("init").description("Initialize rulesync in current directory").action(initCommand);
|
|
2268
2270
|
program.command("add <filename>").description("Add a new rule file").action(addCommand);
|
|
2269
2271
|
program.command("gitignore").description("Add generated files to .gitignore").action(gitignoreCommand);
|