rulesync 0.5.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -3
- package/dist/index.js +73 -51
- package/dist/index.mjs +70 -48
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -11,6 +11,7 @@ A Node.js CLI tool that automatically generates configuration files for various
|
|
|
11
11
|
- **Cursor Project Rules** (`.cursor/rules/*.mdc`)
|
|
12
12
|
- **Cline Rules** (`.clinerules/*.md`)
|
|
13
13
|
- **Claude Code Memory** (`./CLAUDE.md` + `.claude/memories/*.md`)
|
|
14
|
+
- **Roo Code Rules** (`.roo/rules/*.md`)
|
|
14
15
|
|
|
15
16
|
## Installation
|
|
16
17
|
|
|
@@ -165,7 +166,7 @@ Define metadata in front matter for each Markdown file:
|
|
|
165
166
|
```markdown
|
|
166
167
|
---
|
|
167
168
|
root: true # or false
|
|
168
|
-
targets: ["*"] # or [copilot, cursor, cline,
|
|
169
|
+
targets: ["*"] # or [copilot, cursor, cline, claude, roo]
|
|
169
170
|
description: "TypeScript coding rules"
|
|
170
171
|
globs: ["**/*.ts", "**/*.tsx"]
|
|
171
172
|
---
|
|
@@ -197,6 +198,7 @@ Each AI tool handles rule levels differently:
|
|
|
197
198
|
| **Cursor** | `ruletype: always` | `ruletype: autoattached` | Detail rules without globs use `ruletype: agentrequested` |
|
|
198
199
|
| **GitHub Copilot** | Standard format | Standard format | All rules use same format with frontmatter |
|
|
199
200
|
| **Cline** | Standard format | Standard format | All rules use plain Markdown format |
|
|
201
|
+
| **Roo Code** | Standard format | Standard format | All rules use plain Markdown format with description header |
|
|
200
202
|
|
|
201
203
|
### 3. Generate Configuration Files
|
|
202
204
|
|
|
@@ -209,6 +211,7 @@ rulesync generate --copilot
|
|
|
209
211
|
rulesync generate --cursor
|
|
210
212
|
rulesync generate --cline
|
|
211
213
|
rulesync generate --claude
|
|
214
|
+
rulesync generate --roo
|
|
212
215
|
|
|
213
216
|
# Clean build (delete existing files first)
|
|
214
217
|
rulesync generate --delete
|
|
@@ -225,7 +228,7 @@ rulesync generate --delete --verbose
|
|
|
225
228
|
|
|
226
229
|
- `--delete`: Remove all existing generated files before creating new ones
|
|
227
230
|
- `--verbose`: Show detailed output during generation process
|
|
228
|
-
- `--copilot`, `--cursor`, `--cline`, `--claude`: Generate only for specified tools
|
|
231
|
+
- `--copilot`, `--cursor`, `--cline`, `--claude`, `--roo`: Generate only for specified tools
|
|
229
232
|
|
|
230
233
|
### 4. Other Commands
|
|
231
234
|
|
|
@@ -291,7 +294,7 @@ This project follows TypeScript-first development with clean architecture princi
|
|
|
291
294
|
```markdown
|
|
292
295
|
---
|
|
293
296
|
root: false
|
|
294
|
-
targets: ["copilot", "cursor"]
|
|
297
|
+
targets: ["copilot", "cursor", "roo"]
|
|
295
298
|
description: "TypeScript coding standards"
|
|
296
299
|
globs: ["**/*.ts", "**/*.tsx"]
|
|
297
300
|
---
|
|
@@ -311,6 +314,7 @@ globs: ["**/*.ts", "**/*.tsx"]
|
|
|
311
314
|
| **Cursor** | `.cursor/rules/*.mdc` | MDC (YAML header + Markdown) | Root: `ruletype: always`<br>Non-root: `ruletype: autoattached`<br>Non-root without globs: `ruletype: agentrequested` |
|
|
312
315
|
| **Cline** | `.clinerules/*.md` | Plain Markdown | Both levels use same format |
|
|
313
316
|
| **Claude Code** | `./CLAUDE.md` (root)<br>`.claude/memories/*.md` (non-root) | Plain Markdown | Root goes to CLAUDE.md<br>Non-root go to separate memory files<br>CLAUDE.md includes `@filename` references |
|
|
317
|
+
| **Roo Code** | `.roo/rules/*.md` | Plain Markdown | Both levels use same format with description header |
|
|
314
318
|
|
|
315
319
|
## Validation
|
|
316
320
|
|
package/dist/index.js
CHANGED
|
@@ -51,8 +51,10 @@ async function generateClaudeConfig(rules, config) {
|
|
|
51
51
|
function generateClaudeMarkdown(rootRules, detailRules) {
|
|
52
52
|
const lines = [];
|
|
53
53
|
if (detailRules.length > 0) {
|
|
54
|
+
lines.push("Please also reference the following documents as needed:");
|
|
55
|
+
lines.push("");
|
|
54
56
|
for (const rule of detailRules) {
|
|
55
|
-
lines.push(
|
|
57
|
+
lines.push(`@.claude/memories/${rule.filename}.md`);
|
|
56
58
|
}
|
|
57
59
|
lines.push("");
|
|
58
60
|
}
|
|
@@ -71,38 +73,14 @@ function generateClaudeMarkdown(rootRules, detailRules) {
|
|
|
71
73
|
}
|
|
72
74
|
function formatRuleForClaude(rule) {
|
|
73
75
|
const lines = [];
|
|
74
|
-
lines.push(`### ${rule.
|
|
76
|
+
lines.push(`### ${rule.frontmatter.description}`);
|
|
75
77
|
lines.push("");
|
|
76
|
-
if (rule.frontmatter.description) {
|
|
77
|
-
lines.push(`**Description:** ${rule.frontmatter.description}`);
|
|
78
|
-
lines.push("");
|
|
79
|
-
}
|
|
80
|
-
if (rule.frontmatter.globs && rule.frontmatter.globs.length > 0) {
|
|
81
|
-
lines.push(`**File patterns:** ${rule.frontmatter.globs.join(", ")}`);
|
|
82
|
-
lines.push("");
|
|
83
|
-
}
|
|
84
78
|
lines.push(rule.content);
|
|
85
79
|
lines.push("");
|
|
86
80
|
return lines;
|
|
87
81
|
}
|
|
88
82
|
function generateMemoryFile(rule) {
|
|
89
|
-
|
|
90
|
-
lines.push("Please also refer to the following files as needed:");
|
|
91
|
-
lines.push("");
|
|
92
|
-
lines.push("---");
|
|
93
|
-
lines.push("");
|
|
94
|
-
lines.push(`# ${rule.filename}`);
|
|
95
|
-
lines.push("");
|
|
96
|
-
if (rule.frontmatter.description) {
|
|
97
|
-
lines.push(`**Description:** ${rule.frontmatter.description}`);
|
|
98
|
-
lines.push("");
|
|
99
|
-
}
|
|
100
|
-
if (rule.frontmatter.globs && rule.frontmatter.globs.length > 0) {
|
|
101
|
-
lines.push(`**File patterns:** ${rule.frontmatter.globs.join(", ")}`);
|
|
102
|
-
lines.push("");
|
|
103
|
-
}
|
|
104
|
-
lines.push(rule.content);
|
|
105
|
-
return lines.join("\n");
|
|
83
|
+
return rule.content;
|
|
106
84
|
}
|
|
107
85
|
|
|
108
86
|
// src/generators/cline.ts
|
|
@@ -121,15 +99,7 @@ async function generateClineConfig(rules, config) {
|
|
|
121
99
|
return outputs;
|
|
122
100
|
}
|
|
123
101
|
function generateClineMarkdown(rule) {
|
|
124
|
-
|
|
125
|
-
lines.push(`# ${rule.frontmatter.description}`);
|
|
126
|
-
lines.push("");
|
|
127
|
-
if (rule.frontmatter.globs.length > 0) {
|
|
128
|
-
lines.push(`**Applies to files:** ${rule.frontmatter.globs.join(", ")}`);
|
|
129
|
-
lines.push("");
|
|
130
|
-
}
|
|
131
|
-
lines.push(rule.content);
|
|
132
|
-
return lines.join("\n");
|
|
102
|
+
return rule.content;
|
|
133
103
|
}
|
|
134
104
|
|
|
135
105
|
// src/generators/copilot.ts
|
|
@@ -158,7 +128,6 @@ function generateCopilotMarkdown(rule) {
|
|
|
158
128
|
lines.push('applyTo: "**"');
|
|
159
129
|
}
|
|
160
130
|
lines.push("---");
|
|
161
|
-
lines.push("");
|
|
162
131
|
lines.push(rule.content);
|
|
163
132
|
return lines.join("\n");
|
|
164
133
|
}
|
|
@@ -195,7 +164,27 @@ function generateCursorMarkdown(rule) {
|
|
|
195
164
|
}
|
|
196
165
|
lines.push(`ruletype: ${ruletype}`);
|
|
197
166
|
lines.push("---");
|
|
198
|
-
lines.push(
|
|
167
|
+
lines.push(rule.content);
|
|
168
|
+
return lines.join("\n");
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// src/generators/roo.ts
|
|
172
|
+
var import_node_path5 = require("path");
|
|
173
|
+
async function generateRooConfig(rules, config) {
|
|
174
|
+
const outputs = [];
|
|
175
|
+
for (const rule of rules) {
|
|
176
|
+
const content = generateRooMarkdown(rule);
|
|
177
|
+
const filepath = (0, import_node_path5.join)(config.outputPaths.roo, `${rule.filename}.md`);
|
|
178
|
+
outputs.push({
|
|
179
|
+
tool: "roo",
|
|
180
|
+
filepath,
|
|
181
|
+
content
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
return outputs;
|
|
185
|
+
}
|
|
186
|
+
function generateRooMarkdown(rule) {
|
|
187
|
+
const lines = [];
|
|
199
188
|
lines.push(rule.content);
|
|
200
189
|
return lines.join("\n");
|
|
201
190
|
}
|
|
@@ -208,10 +197,11 @@ function getDefaultConfig() {
|
|
|
208
197
|
copilot: ".github/instructions",
|
|
209
198
|
cursor: ".cursor/rules",
|
|
210
199
|
cline: ".clinerules",
|
|
211
|
-
claude: "."
|
|
200
|
+
claude: ".",
|
|
201
|
+
roo: ".roo/rules"
|
|
212
202
|
},
|
|
213
203
|
watchEnabled: false,
|
|
214
|
-
defaultTargets: ["copilot", "cursor", "cline", "claude"]
|
|
204
|
+
defaultTargets: ["copilot", "cursor", "cline", "claude", "roo"]
|
|
215
205
|
};
|
|
216
206
|
}
|
|
217
207
|
function resolveTargets(targets, config) {
|
|
@@ -223,7 +213,7 @@ function resolveTargets(targets, config) {
|
|
|
223
213
|
|
|
224
214
|
// src/utils/file.ts
|
|
225
215
|
var import_promises = require("fs/promises");
|
|
226
|
-
var
|
|
216
|
+
var import_node_path6 = require("path");
|
|
227
217
|
async function ensureDir(dirPath) {
|
|
228
218
|
try {
|
|
229
219
|
await (0, import_promises.stat)(dirPath);
|
|
@@ -235,13 +225,13 @@ async function readFileContent(filepath) {
|
|
|
235
225
|
return (0, import_promises.readFile)(filepath, "utf-8");
|
|
236
226
|
}
|
|
237
227
|
async function writeFileContent(filepath, content) {
|
|
238
|
-
await ensureDir((0,
|
|
228
|
+
await ensureDir((0, import_node_path6.dirname)(filepath));
|
|
239
229
|
await (0, import_promises.writeFile)(filepath, content, "utf-8");
|
|
240
230
|
}
|
|
241
231
|
async function findFiles(dir, extension = ".md") {
|
|
242
232
|
try {
|
|
243
233
|
const files = await (0, import_promises.readdir)(dir);
|
|
244
|
-
return files.filter((file) => file.endsWith(extension)).map((file) => (0,
|
|
234
|
+
return files.filter((file) => file.endsWith(extension)).map((file) => (0, import_node_path6.join)(dir, file));
|
|
245
235
|
} catch {
|
|
246
236
|
return [];
|
|
247
237
|
}
|
|
@@ -255,6 +245,11 @@ async function fileExists(filepath) {
|
|
|
255
245
|
}
|
|
256
246
|
}
|
|
257
247
|
async function removeDirectory(dirPath) {
|
|
248
|
+
const dangerousPaths = [".", "/", "~", "src", "node_modules"];
|
|
249
|
+
if (dangerousPaths.includes(dirPath) || dirPath === "") {
|
|
250
|
+
console.warn(`Skipping deletion of dangerous path: ${dirPath}`);
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
258
253
|
try {
|
|
259
254
|
if (await fileExists(dirPath)) {
|
|
260
255
|
await (0, import_promises.rm)(dirPath, { recursive: true, force: true });
|
|
@@ -263,6 +258,25 @@ async function removeDirectory(dirPath) {
|
|
|
263
258
|
console.warn(`Failed to remove directory ${dirPath}:`, error);
|
|
264
259
|
}
|
|
265
260
|
}
|
|
261
|
+
async function removeFile(filepath) {
|
|
262
|
+
try {
|
|
263
|
+
if (await fileExists(filepath)) {
|
|
264
|
+
await (0, import_promises.rm)(filepath);
|
|
265
|
+
}
|
|
266
|
+
} catch (error) {
|
|
267
|
+
console.warn(`Failed to remove file ${filepath}:`, error);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
async function removeClaudeGeneratedFiles() {
|
|
271
|
+
const filesToRemove = ["CLAUDE.md", ".claude/memories"];
|
|
272
|
+
for (const fileOrDir of filesToRemove) {
|
|
273
|
+
if (fileOrDir.endsWith("/memories")) {
|
|
274
|
+
await removeDirectory(fileOrDir);
|
|
275
|
+
} else {
|
|
276
|
+
await removeFile(fileOrDir);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
266
280
|
|
|
267
281
|
// src/core/generator.ts
|
|
268
282
|
async function generateConfigurations(rules, config, targetTools) {
|
|
@@ -297,6 +311,8 @@ async function generateForTool(tool, rules, config) {
|
|
|
297
311
|
return generateClineConfig(rules, config);
|
|
298
312
|
case "claude":
|
|
299
313
|
return await generateClaudeConfig(rules, config);
|
|
314
|
+
case "roo":
|
|
315
|
+
return generateRooConfig(rules, config);
|
|
300
316
|
default:
|
|
301
317
|
console.warn(`Unknown tool: ${tool}`);
|
|
302
318
|
return null;
|
|
@@ -304,7 +320,7 @@ async function generateForTool(tool, rules, config) {
|
|
|
304
320
|
}
|
|
305
321
|
|
|
306
322
|
// src/core/parser.ts
|
|
307
|
-
var
|
|
323
|
+
var import_node_path7 = require("path");
|
|
308
324
|
var import_gray_matter = __toESM(require("gray-matter"));
|
|
309
325
|
async function parseRulesFromDirectory(aiRulesDir) {
|
|
310
326
|
const ruleFiles = await findFiles(aiRulesDir);
|
|
@@ -324,7 +340,7 @@ async function parseRuleFile(filepath) {
|
|
|
324
340
|
const parsed = (0, import_gray_matter.default)(content);
|
|
325
341
|
validateFrontmatter(parsed.data, filepath);
|
|
326
342
|
const frontmatter = parsed.data;
|
|
327
|
-
const filename = (0,
|
|
343
|
+
const filename = (0, import_node_path7.basename)(filepath, ".md");
|
|
328
344
|
return {
|
|
329
345
|
frontmatter,
|
|
330
346
|
content: parsed.content,
|
|
@@ -452,7 +468,10 @@ async function generateCommand(options = {}) {
|
|
|
452
468
|
deleteTasks.push(removeDirectory(config.outputPaths.cline));
|
|
453
469
|
break;
|
|
454
470
|
case "claude":
|
|
455
|
-
deleteTasks.push(
|
|
471
|
+
deleteTasks.push(removeClaudeGeneratedFiles());
|
|
472
|
+
break;
|
|
473
|
+
case "roo":
|
|
474
|
+
deleteTasks.push(removeDirectory(config.outputPaths.roo));
|
|
456
475
|
break;
|
|
457
476
|
}
|
|
458
477
|
}
|
|
@@ -480,15 +499,18 @@ async function generateCommand(options = {}) {
|
|
|
480
499
|
|
|
481
500
|
// src/cli/commands/gitignore.ts
|
|
482
501
|
var import_node_fs = require("fs");
|
|
483
|
-
var
|
|
502
|
+
var import_node_path8 = require("path");
|
|
484
503
|
var gitignoreCommand = async () => {
|
|
485
|
-
const gitignorePath = (0,
|
|
504
|
+
const gitignorePath = (0, import_node_path8.join)(process.cwd(), ".gitignore");
|
|
486
505
|
const rulesFilesToIgnore = [
|
|
487
506
|
"# Generated by rulesync - AI tool configuration files",
|
|
507
|
+
".github/copilot-instructions.md",
|
|
488
508
|
".github/instructions/",
|
|
489
509
|
".cursor/rules/",
|
|
490
510
|
".clinerules/",
|
|
491
|
-
"CLAUDE.md"
|
|
511
|
+
"CLAUDE.md",
|
|
512
|
+
".claude/memories/",
|
|
513
|
+
".roo/rules/"
|
|
492
514
|
];
|
|
493
515
|
let gitignoreContent = "";
|
|
494
516
|
if ((0, import_node_fs.existsSync)(gitignorePath)) {
|
|
@@ -519,7 +541,7 @@ ${linesToAdd.join("\n")}
|
|
|
519
541
|
};
|
|
520
542
|
|
|
521
543
|
// src/cli/commands/init.ts
|
|
522
|
-
var
|
|
544
|
+
var import_node_path9 = require("path");
|
|
523
545
|
async function initCommand() {
|
|
524
546
|
const aiRulesDir = ".rulesync";
|
|
525
547
|
console.log("Initializing rulesync...");
|
|
@@ -609,7 +631,7 @@ globs: ["src/**/*.ts"]
|
|
|
609
631
|
}
|
|
610
632
|
];
|
|
611
633
|
for (const file of sampleFiles) {
|
|
612
|
-
const filepath = (0,
|
|
634
|
+
const filepath = (0, import_node_path9.join)(aiRulesDir, file.filename);
|
|
613
635
|
if (!await fileExists(filepath)) {
|
|
614
636
|
await writeFileContent(filepath, file.content);
|
|
615
637
|
console.log(`Created ${filepath}`);
|
package/dist/index.mjs
CHANGED
|
@@ -28,8 +28,10 @@ async function generateClaudeConfig(rules, config) {
|
|
|
28
28
|
function generateClaudeMarkdown(rootRules, detailRules) {
|
|
29
29
|
const lines = [];
|
|
30
30
|
if (detailRules.length > 0) {
|
|
31
|
+
lines.push("Please also reference the following documents as needed:");
|
|
32
|
+
lines.push("");
|
|
31
33
|
for (const rule of detailRules) {
|
|
32
|
-
lines.push(
|
|
34
|
+
lines.push(`@.claude/memories/${rule.filename}.md`);
|
|
33
35
|
}
|
|
34
36
|
lines.push("");
|
|
35
37
|
}
|
|
@@ -48,38 +50,14 @@ function generateClaudeMarkdown(rootRules, detailRules) {
|
|
|
48
50
|
}
|
|
49
51
|
function formatRuleForClaude(rule) {
|
|
50
52
|
const lines = [];
|
|
51
|
-
lines.push(`### ${rule.
|
|
53
|
+
lines.push(`### ${rule.frontmatter.description}`);
|
|
52
54
|
lines.push("");
|
|
53
|
-
if (rule.frontmatter.description) {
|
|
54
|
-
lines.push(`**Description:** ${rule.frontmatter.description}`);
|
|
55
|
-
lines.push("");
|
|
56
|
-
}
|
|
57
|
-
if (rule.frontmatter.globs && rule.frontmatter.globs.length > 0) {
|
|
58
|
-
lines.push(`**File patterns:** ${rule.frontmatter.globs.join(", ")}`);
|
|
59
|
-
lines.push("");
|
|
60
|
-
}
|
|
61
55
|
lines.push(rule.content);
|
|
62
56
|
lines.push("");
|
|
63
57
|
return lines;
|
|
64
58
|
}
|
|
65
59
|
function generateMemoryFile(rule) {
|
|
66
|
-
|
|
67
|
-
lines.push("Please also refer to the following files as needed:");
|
|
68
|
-
lines.push("");
|
|
69
|
-
lines.push("---");
|
|
70
|
-
lines.push("");
|
|
71
|
-
lines.push(`# ${rule.filename}`);
|
|
72
|
-
lines.push("");
|
|
73
|
-
if (rule.frontmatter.description) {
|
|
74
|
-
lines.push(`**Description:** ${rule.frontmatter.description}`);
|
|
75
|
-
lines.push("");
|
|
76
|
-
}
|
|
77
|
-
if (rule.frontmatter.globs && rule.frontmatter.globs.length > 0) {
|
|
78
|
-
lines.push(`**File patterns:** ${rule.frontmatter.globs.join(", ")}`);
|
|
79
|
-
lines.push("");
|
|
80
|
-
}
|
|
81
|
-
lines.push(rule.content);
|
|
82
|
-
return lines.join("\n");
|
|
60
|
+
return rule.content;
|
|
83
61
|
}
|
|
84
62
|
|
|
85
63
|
// src/generators/cline.ts
|
|
@@ -98,15 +76,7 @@ async function generateClineConfig(rules, config) {
|
|
|
98
76
|
return outputs;
|
|
99
77
|
}
|
|
100
78
|
function generateClineMarkdown(rule) {
|
|
101
|
-
|
|
102
|
-
lines.push(`# ${rule.frontmatter.description}`);
|
|
103
|
-
lines.push("");
|
|
104
|
-
if (rule.frontmatter.globs.length > 0) {
|
|
105
|
-
lines.push(`**Applies to files:** ${rule.frontmatter.globs.join(", ")}`);
|
|
106
|
-
lines.push("");
|
|
107
|
-
}
|
|
108
|
-
lines.push(rule.content);
|
|
109
|
-
return lines.join("\n");
|
|
79
|
+
return rule.content;
|
|
110
80
|
}
|
|
111
81
|
|
|
112
82
|
// src/generators/copilot.ts
|
|
@@ -135,7 +105,6 @@ function generateCopilotMarkdown(rule) {
|
|
|
135
105
|
lines.push('applyTo: "**"');
|
|
136
106
|
}
|
|
137
107
|
lines.push("---");
|
|
138
|
-
lines.push("");
|
|
139
108
|
lines.push(rule.content);
|
|
140
109
|
return lines.join("\n");
|
|
141
110
|
}
|
|
@@ -172,7 +141,27 @@ function generateCursorMarkdown(rule) {
|
|
|
172
141
|
}
|
|
173
142
|
lines.push(`ruletype: ${ruletype}`);
|
|
174
143
|
lines.push("---");
|
|
175
|
-
lines.push(
|
|
144
|
+
lines.push(rule.content);
|
|
145
|
+
return lines.join("\n");
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// src/generators/roo.ts
|
|
149
|
+
import { join as join5 } from "path";
|
|
150
|
+
async function generateRooConfig(rules, config) {
|
|
151
|
+
const outputs = [];
|
|
152
|
+
for (const rule of rules) {
|
|
153
|
+
const content = generateRooMarkdown(rule);
|
|
154
|
+
const filepath = join5(config.outputPaths.roo, `${rule.filename}.md`);
|
|
155
|
+
outputs.push({
|
|
156
|
+
tool: "roo",
|
|
157
|
+
filepath,
|
|
158
|
+
content
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
return outputs;
|
|
162
|
+
}
|
|
163
|
+
function generateRooMarkdown(rule) {
|
|
164
|
+
const lines = [];
|
|
176
165
|
lines.push(rule.content);
|
|
177
166
|
return lines.join("\n");
|
|
178
167
|
}
|
|
@@ -185,10 +174,11 @@ function getDefaultConfig() {
|
|
|
185
174
|
copilot: ".github/instructions",
|
|
186
175
|
cursor: ".cursor/rules",
|
|
187
176
|
cline: ".clinerules",
|
|
188
|
-
claude: "."
|
|
177
|
+
claude: ".",
|
|
178
|
+
roo: ".roo/rules"
|
|
189
179
|
},
|
|
190
180
|
watchEnabled: false,
|
|
191
|
-
defaultTargets: ["copilot", "cursor", "cline", "claude"]
|
|
181
|
+
defaultTargets: ["copilot", "cursor", "cline", "claude", "roo"]
|
|
192
182
|
};
|
|
193
183
|
}
|
|
194
184
|
function resolveTargets(targets, config) {
|
|
@@ -200,7 +190,7 @@ function resolveTargets(targets, config) {
|
|
|
200
190
|
|
|
201
191
|
// src/utils/file.ts
|
|
202
192
|
import { mkdir, readdir, readFile, rm, stat, writeFile } from "fs/promises";
|
|
203
|
-
import { dirname, join as
|
|
193
|
+
import { dirname, join as join6 } from "path";
|
|
204
194
|
async function ensureDir(dirPath) {
|
|
205
195
|
try {
|
|
206
196
|
await stat(dirPath);
|
|
@@ -218,7 +208,7 @@ async function writeFileContent(filepath, content) {
|
|
|
218
208
|
async function findFiles(dir, extension = ".md") {
|
|
219
209
|
try {
|
|
220
210
|
const files = await readdir(dir);
|
|
221
|
-
return files.filter((file) => file.endsWith(extension)).map((file) =>
|
|
211
|
+
return files.filter((file) => file.endsWith(extension)).map((file) => join6(dir, file));
|
|
222
212
|
} catch {
|
|
223
213
|
return [];
|
|
224
214
|
}
|
|
@@ -232,6 +222,11 @@ async function fileExists(filepath) {
|
|
|
232
222
|
}
|
|
233
223
|
}
|
|
234
224
|
async function removeDirectory(dirPath) {
|
|
225
|
+
const dangerousPaths = [".", "/", "~", "src", "node_modules"];
|
|
226
|
+
if (dangerousPaths.includes(dirPath) || dirPath === "") {
|
|
227
|
+
console.warn(`Skipping deletion of dangerous path: ${dirPath}`);
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
235
230
|
try {
|
|
236
231
|
if (await fileExists(dirPath)) {
|
|
237
232
|
await rm(dirPath, { recursive: true, force: true });
|
|
@@ -240,6 +235,25 @@ async function removeDirectory(dirPath) {
|
|
|
240
235
|
console.warn(`Failed to remove directory ${dirPath}:`, error);
|
|
241
236
|
}
|
|
242
237
|
}
|
|
238
|
+
async function removeFile(filepath) {
|
|
239
|
+
try {
|
|
240
|
+
if (await fileExists(filepath)) {
|
|
241
|
+
await rm(filepath);
|
|
242
|
+
}
|
|
243
|
+
} catch (error) {
|
|
244
|
+
console.warn(`Failed to remove file ${filepath}:`, error);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
async function removeClaudeGeneratedFiles() {
|
|
248
|
+
const filesToRemove = ["CLAUDE.md", ".claude/memories"];
|
|
249
|
+
for (const fileOrDir of filesToRemove) {
|
|
250
|
+
if (fileOrDir.endsWith("/memories")) {
|
|
251
|
+
await removeDirectory(fileOrDir);
|
|
252
|
+
} else {
|
|
253
|
+
await removeFile(fileOrDir);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
243
257
|
|
|
244
258
|
// src/core/generator.ts
|
|
245
259
|
async function generateConfigurations(rules, config, targetTools) {
|
|
@@ -274,6 +288,8 @@ async function generateForTool(tool, rules, config) {
|
|
|
274
288
|
return generateClineConfig(rules, config);
|
|
275
289
|
case "claude":
|
|
276
290
|
return await generateClaudeConfig(rules, config);
|
|
291
|
+
case "roo":
|
|
292
|
+
return generateRooConfig(rules, config);
|
|
277
293
|
default:
|
|
278
294
|
console.warn(`Unknown tool: ${tool}`);
|
|
279
295
|
return null;
|
|
@@ -429,7 +445,10 @@ async function generateCommand(options = {}) {
|
|
|
429
445
|
deleteTasks.push(removeDirectory(config.outputPaths.cline));
|
|
430
446
|
break;
|
|
431
447
|
case "claude":
|
|
432
|
-
deleteTasks.push(
|
|
448
|
+
deleteTasks.push(removeClaudeGeneratedFiles());
|
|
449
|
+
break;
|
|
450
|
+
case "roo":
|
|
451
|
+
deleteTasks.push(removeDirectory(config.outputPaths.roo));
|
|
433
452
|
break;
|
|
434
453
|
}
|
|
435
454
|
}
|
|
@@ -457,15 +476,18 @@ async function generateCommand(options = {}) {
|
|
|
457
476
|
|
|
458
477
|
// src/cli/commands/gitignore.ts
|
|
459
478
|
import { existsSync, readFileSync, writeFileSync } from "fs";
|
|
460
|
-
import { join as
|
|
479
|
+
import { join as join7 } from "path";
|
|
461
480
|
var gitignoreCommand = async () => {
|
|
462
|
-
const gitignorePath =
|
|
481
|
+
const gitignorePath = join7(process.cwd(), ".gitignore");
|
|
463
482
|
const rulesFilesToIgnore = [
|
|
464
483
|
"# Generated by rulesync - AI tool configuration files",
|
|
484
|
+
".github/copilot-instructions.md",
|
|
465
485
|
".github/instructions/",
|
|
466
486
|
".cursor/rules/",
|
|
467
487
|
".clinerules/",
|
|
468
|
-
"CLAUDE.md"
|
|
488
|
+
"CLAUDE.md",
|
|
489
|
+
".claude/memories/",
|
|
490
|
+
".roo/rules/"
|
|
469
491
|
];
|
|
470
492
|
let gitignoreContent = "";
|
|
471
493
|
if (existsSync(gitignorePath)) {
|
|
@@ -496,7 +518,7 @@ ${linesToAdd.join("\n")}
|
|
|
496
518
|
};
|
|
497
519
|
|
|
498
520
|
// src/cli/commands/init.ts
|
|
499
|
-
import { join as
|
|
521
|
+
import { join as join8 } from "path";
|
|
500
522
|
async function initCommand() {
|
|
501
523
|
const aiRulesDir = ".rulesync";
|
|
502
524
|
console.log("Initializing rulesync...");
|
|
@@ -586,7 +608,7 @@ globs: ["src/**/*.ts"]
|
|
|
586
608
|
}
|
|
587
609
|
];
|
|
588
610
|
for (const file of sampleFiles) {
|
|
589
|
-
const filepath =
|
|
611
|
+
const filepath = join8(aiRulesDir, file.filename);
|
|
590
612
|
if (!await fileExists(filepath)) {
|
|
591
613
|
await writeFileContent(filepath, file.content);
|
|
592
614
|
console.log(`Created ${filepath}`);
|
package/package.json
CHANGED