rulesync 0.47.0 → 0.49.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.ja.md +6 -1
- package/README.md +6 -1
- package/dist/augmentcode-KTXT5GMO.js +9 -0
- package/dist/{chunk-7UVBAWYG.js → chunk-6MFEIYHN.js} +1 -1
- package/dist/chunk-6TAURCQP.js +111 -0
- package/dist/{chunk-P6KQZULZ.js → chunk-G5LLOIO4.js} +1 -1
- package/dist/{chunk-7ZIUEZZQ.js → chunk-IBJGN3JQ.js} +2 -0
- package/dist/{chunk-L2JTXZZB.js → chunk-MCADLVGY.js} +1 -1
- package/dist/{chunk-JWN6GRG6.js → chunk-QNHGYRJT.js} +1 -1
- package/dist/{chunk-D365OP7N.js → chunk-R5HFWJ5L.js} +1 -1
- package/dist/{chunk-BY6RI77W.js → chunk-XHNIEO22.js} +1 -1
- package/dist/{chunk-OTCCHS7Q.js → chunk-YVRWBSCK.js} +1 -1
- package/dist/{claudecode-Y3GIXDUN.js → claudecode-OE4TSKPS.js} +2 -2
- package/dist/{cline-NS3OPXM2.js → cline-CWLQS5CV.js} +2 -2
- package/dist/{copilot-QN2SC7Y2.js → copilot-PVZDRAU5.js} +2 -2
- package/dist/{cursor-DV2IS7JF.js → cursor-ZZV7AY6H.js} +2 -2
- package/dist/{geminicli-MRYTLT2T.js → geminicli-RSXOWFEN.js} +2 -2
- package/dist/index.cjs +922 -551
- package/dist/index.js +889 -575
- package/dist/{kiro-S5TSM7VW.js → kiro-YRKVCDIQ.js} +2 -2
- package/dist/{roo-NWLD3YYN.js → roo-HCRGELWQ.js} +2 -2
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,30 +1,33 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
generateRooMcp
|
|
4
|
+
} from "./chunk-MCADLVGY.js";
|
|
5
|
+
import {
|
|
6
|
+
generateAugmentcodeMcp
|
|
7
|
+
} from "./chunk-6TAURCQP.js";
|
|
2
8
|
import {
|
|
3
9
|
generateClaudeMcp
|
|
4
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-YVRWBSCK.js";
|
|
5
11
|
import {
|
|
6
12
|
generateClineMcp
|
|
7
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-XHNIEO22.js";
|
|
8
14
|
import {
|
|
9
15
|
generateCopilotMcp
|
|
10
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-G5LLOIO4.js";
|
|
11
17
|
import {
|
|
12
18
|
generateCursorMcp
|
|
13
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-6MFEIYHN.js";
|
|
14
20
|
import {
|
|
15
21
|
generateGeminiCliMcp
|
|
16
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-QNHGYRJT.js";
|
|
17
23
|
import {
|
|
18
24
|
generateKiroMcp
|
|
19
|
-
} from "./chunk-
|
|
20
|
-
import {
|
|
21
|
-
generateRooMcp
|
|
22
|
-
} from "./chunk-L2JTXZZB.js";
|
|
25
|
+
} from "./chunk-R5HFWJ5L.js";
|
|
23
26
|
import {
|
|
24
27
|
RulesyncTargetsSchema,
|
|
25
28
|
ToolTargetSchema,
|
|
26
29
|
ToolTargetsSchema
|
|
27
|
-
} from "./chunk-
|
|
30
|
+
} from "./chunk-IBJGN3JQ.js";
|
|
28
31
|
|
|
29
32
|
// src/cli/index.ts
|
|
30
33
|
import { Command } from "commander";
|
|
@@ -38,6 +41,8 @@ function getDefaultConfig() {
|
|
|
38
41
|
return {
|
|
39
42
|
aiRulesDir: ".rulesync",
|
|
40
43
|
outputPaths: {
|
|
44
|
+
augmentcode: ".",
|
|
45
|
+
"augmentcode-legacy": ".",
|
|
41
46
|
copilot: ".github/instructions",
|
|
42
47
|
cursor: ".cursor/rules",
|
|
43
48
|
cline: ".clinerules",
|
|
@@ -47,7 +52,16 @@ function getDefaultConfig() {
|
|
|
47
52
|
kiro: ".kiro/steering"
|
|
48
53
|
},
|
|
49
54
|
watchEnabled: false,
|
|
50
|
-
defaultTargets: [
|
|
55
|
+
defaultTargets: [
|
|
56
|
+
"augmentcode",
|
|
57
|
+
"copilot",
|
|
58
|
+
"cursor",
|
|
59
|
+
"cline",
|
|
60
|
+
"claudecode",
|
|
61
|
+
"roo",
|
|
62
|
+
"geminicli",
|
|
63
|
+
"kiro"
|
|
64
|
+
]
|
|
51
65
|
};
|
|
52
66
|
}
|
|
53
67
|
function resolveTargets(targets, config) {
|
|
@@ -94,13 +108,219 @@ async function addCommand(filename) {
|
|
|
94
108
|
}
|
|
95
109
|
}
|
|
96
110
|
|
|
97
|
-
// src/generators/ignore/
|
|
111
|
+
// src/generators/ignore/augmentcode.ts
|
|
98
112
|
import { join as join2 } from "path";
|
|
113
|
+
async function generateAugmentCodeIgnoreFiles(rules, config, baseDir) {
|
|
114
|
+
const outputs = [];
|
|
115
|
+
const augmentignoreContent = generateAugmentignoreContent(rules);
|
|
116
|
+
const outputPath = baseDir || process.cwd();
|
|
117
|
+
const filepath = join2(outputPath, ".augmentignore");
|
|
118
|
+
outputs.push({
|
|
119
|
+
tool: "augmentcode",
|
|
120
|
+
filepath,
|
|
121
|
+
content: augmentignoreContent
|
|
122
|
+
});
|
|
123
|
+
return outputs;
|
|
124
|
+
}
|
|
125
|
+
function generateAugmentignoreContent(rules) {
|
|
126
|
+
const lines = [
|
|
127
|
+
"# Generated by rulesync - AugmentCode ignore patterns",
|
|
128
|
+
"# AugmentCode uses a two-tier approach: .gitignore first, then .augmentignore",
|
|
129
|
+
"# This file provides Augment-specific exclusions and re-inclusions",
|
|
130
|
+
""
|
|
131
|
+
];
|
|
132
|
+
lines.push(
|
|
133
|
+
"# Security and Secrets (critical exclusions)",
|
|
134
|
+
"# Environment files",
|
|
135
|
+
".env*",
|
|
136
|
+
"",
|
|
137
|
+
"# Private keys and certificates",
|
|
138
|
+
"*.pem",
|
|
139
|
+
"*.key",
|
|
140
|
+
"*.p12",
|
|
141
|
+
"*.crt",
|
|
142
|
+
"*.der",
|
|
143
|
+
"",
|
|
144
|
+
"# SSH keys",
|
|
145
|
+
"id_rsa*",
|
|
146
|
+
"id_dsa*",
|
|
147
|
+
"",
|
|
148
|
+
"# AWS credentials",
|
|
149
|
+
".aws/",
|
|
150
|
+
"aws-exports.js",
|
|
151
|
+
"",
|
|
152
|
+
"# API keys and tokens",
|
|
153
|
+
"**/apikeys/",
|
|
154
|
+
"**/*_token*",
|
|
155
|
+
"**/*_secret*",
|
|
156
|
+
""
|
|
157
|
+
);
|
|
158
|
+
lines.push(
|
|
159
|
+
"# Build Artifacts and Dependencies",
|
|
160
|
+
"# Build outputs",
|
|
161
|
+
"dist/",
|
|
162
|
+
"build/",
|
|
163
|
+
"out/",
|
|
164
|
+
"target/",
|
|
165
|
+
"",
|
|
166
|
+
"# Dependencies",
|
|
167
|
+
"node_modules/",
|
|
168
|
+
"venv/",
|
|
169
|
+
"*.egg-info/",
|
|
170
|
+
"",
|
|
171
|
+
"# Logs",
|
|
172
|
+
"*.log",
|
|
173
|
+
"logs/",
|
|
174
|
+
"",
|
|
175
|
+
"# Temporary files",
|
|
176
|
+
"*.tmp",
|
|
177
|
+
"*.swp",
|
|
178
|
+
"*.swo",
|
|
179
|
+
"*~",
|
|
180
|
+
""
|
|
181
|
+
);
|
|
182
|
+
lines.push(
|
|
183
|
+
"# Large Files and Media",
|
|
184
|
+
"# Binary files",
|
|
185
|
+
"*.jar",
|
|
186
|
+
"*.png",
|
|
187
|
+
"*.jpg",
|
|
188
|
+
"*.jpeg",
|
|
189
|
+
"*.gif",
|
|
190
|
+
"*.mp4",
|
|
191
|
+
"*.avi",
|
|
192
|
+
"*.zip",
|
|
193
|
+
"*.tar.gz",
|
|
194
|
+
"*.rar",
|
|
195
|
+
"",
|
|
196
|
+
"# Database files",
|
|
197
|
+
"*.sqlite",
|
|
198
|
+
"*.db",
|
|
199
|
+
"*.mdb",
|
|
200
|
+
"",
|
|
201
|
+
"# Data files",
|
|
202
|
+
"*.csv",
|
|
203
|
+
"*.tsv",
|
|
204
|
+
"*.xlsx",
|
|
205
|
+
""
|
|
206
|
+
);
|
|
207
|
+
lines.push(
|
|
208
|
+
"# Performance Optimization",
|
|
209
|
+
"# Exclude files that are too large for effective AI processing",
|
|
210
|
+
"**/*.{mp4,avi,mov,mkv}",
|
|
211
|
+
"**/*.{zip,tar,gz,rar}",
|
|
212
|
+
"**/*.{pdf,doc,docx}",
|
|
213
|
+
"**/logs/**/*.log",
|
|
214
|
+
"",
|
|
215
|
+
"# But include small configuration files",
|
|
216
|
+
"!**/config.{json,yaml,yml}",
|
|
217
|
+
""
|
|
218
|
+
);
|
|
219
|
+
const rulePatterns = extractIgnorePatternsFromRules(rules);
|
|
220
|
+
if (rulePatterns.length > 0) {
|
|
221
|
+
lines.push("# Project-specific patterns from rulesync rules");
|
|
222
|
+
lines.push(...rulePatterns);
|
|
223
|
+
lines.push("");
|
|
224
|
+
}
|
|
225
|
+
lines.push(
|
|
226
|
+
"# Team Collaboration",
|
|
227
|
+
"# Exclude personal IDE settings",
|
|
228
|
+
".vscode/settings.json",
|
|
229
|
+
".idea/workspace.xml",
|
|
230
|
+
"",
|
|
231
|
+
"# But include shared team settings",
|
|
232
|
+
"!.vscode/extensions.json",
|
|
233
|
+
"!.idea/codeStyles/",
|
|
234
|
+
"",
|
|
235
|
+
"# Exclude test fixtures with sensitive data",
|
|
236
|
+
"tests/fixtures/real-data/**",
|
|
237
|
+
"",
|
|
238
|
+
"# Re-include important documentation",
|
|
239
|
+
"!vendor/*/README.md",
|
|
240
|
+
"!third-party/*/LICENSE",
|
|
241
|
+
""
|
|
242
|
+
);
|
|
243
|
+
return lines.join("\n");
|
|
244
|
+
}
|
|
245
|
+
function extractIgnorePatternsFromRules(rules) {
|
|
246
|
+
const patterns = [];
|
|
247
|
+
for (const rule of rules) {
|
|
248
|
+
if (rule.frontmatter.globs && rule.frontmatter.globs.length > 0) {
|
|
249
|
+
for (const glob of rule.frontmatter.globs) {
|
|
250
|
+
if (shouldExcludeFromAugmentCode(glob)) {
|
|
251
|
+
patterns.push(`# Exclude: ${rule.frontmatter.description}`);
|
|
252
|
+
patterns.push(glob);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
const contentPatterns = extractAugmentCodeIgnorePatternsFromContent(rule.content);
|
|
257
|
+
patterns.push(...contentPatterns);
|
|
258
|
+
}
|
|
259
|
+
return patterns;
|
|
260
|
+
}
|
|
261
|
+
function shouldExcludeFromAugmentCode(glob) {
|
|
262
|
+
const excludePatterns = [
|
|
263
|
+
// Large generated files that slow indexing
|
|
264
|
+
"**/assets/generated/**",
|
|
265
|
+
"**/public/build/**",
|
|
266
|
+
// Test fixtures with potentially sensitive data
|
|
267
|
+
"**/tests/fixtures/**",
|
|
268
|
+
"**/test/fixtures/**",
|
|
269
|
+
"**/*.fixture.*",
|
|
270
|
+
// Build outputs that provide little value for AI context
|
|
271
|
+
"**/dist/**",
|
|
272
|
+
"**/build/**",
|
|
273
|
+
"**/coverage/**",
|
|
274
|
+
// Configuration that might contain sensitive data
|
|
275
|
+
"**/config/production/**",
|
|
276
|
+
"**/config/secrets/**",
|
|
277
|
+
"**/deploy/prod/**",
|
|
278
|
+
// Internal documentation
|
|
279
|
+
"**/internal-docs/**",
|
|
280
|
+
"**/proprietary/**",
|
|
281
|
+
"**/personal-notes/**",
|
|
282
|
+
"**/private/**",
|
|
283
|
+
"**/confidential/**"
|
|
284
|
+
];
|
|
285
|
+
return excludePatterns.some((pattern) => {
|
|
286
|
+
const regex = new RegExp(pattern.replace(/\*\*/g, ".*").replace(/\*/g, "[^/]*"));
|
|
287
|
+
return regex.test(glob);
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
function extractAugmentCodeIgnorePatternsFromContent(content) {
|
|
291
|
+
const patterns = [];
|
|
292
|
+
const lines = content.split("\n");
|
|
293
|
+
for (const line of lines) {
|
|
294
|
+
const trimmed = line.trim();
|
|
295
|
+
if (trimmed.startsWith("# AUGMENT_IGNORE:") || trimmed.startsWith("# augmentignore:")) {
|
|
296
|
+
const pattern = trimmed.replace(/^# (AUGMENT_IGNORE|augmentignore):\s*/, "").trim();
|
|
297
|
+
if (pattern) {
|
|
298
|
+
patterns.push(pattern);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
if (trimmed.startsWith("# AUGMENT_INCLUDE:") || trimmed.startsWith("# augmentinclude:")) {
|
|
302
|
+
const pattern = trimmed.replace(/^# (AUGMENT_INCLUDE|augmentinclude):\s*/, "").trim();
|
|
303
|
+
if (pattern) {
|
|
304
|
+
patterns.push(`!${pattern}`);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
if (trimmed.includes("large file") || trimmed.includes("binary") || trimmed.includes("media")) {
|
|
308
|
+
const matches = trimmed.match(/['"`]([^'"`]+\.(mp4|avi|zip|tar\.gz|rar|pdf|doc|xlsx))['"`]/g);
|
|
309
|
+
if (matches) {
|
|
310
|
+
patterns.push(...matches.map((m) => m.replace(/['"`]/g, "")));
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
return patterns;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// src/generators/ignore/kiro.ts
|
|
318
|
+
import { join as join3 } from "path";
|
|
99
319
|
async function generateKiroIgnoreFiles(rules, config, baseDir) {
|
|
100
320
|
const outputs = [];
|
|
101
321
|
const aiignoreContent = generateAiignoreContent(rules);
|
|
102
322
|
const outputPath = baseDir || process.cwd();
|
|
103
|
-
const filepath =
|
|
323
|
+
const filepath = join3(outputPath, ".aiignore");
|
|
104
324
|
outputs.push({
|
|
105
325
|
tool: "kiro",
|
|
106
326
|
filepath,
|
|
@@ -140,7 +360,7 @@ function generateAiignoreContent(rules) {
|
|
|
140
360
|
".env*",
|
|
141
361
|
""
|
|
142
362
|
);
|
|
143
|
-
const rulePatterns =
|
|
363
|
+
const rulePatterns = extractIgnorePatternsFromRules2(rules);
|
|
144
364
|
if (rulePatterns.length > 0) {
|
|
145
365
|
lines.push("# Project-specific exclusions from rulesync rules");
|
|
146
366
|
lines.push(...rulePatterns);
|
|
@@ -148,7 +368,7 @@ function generateAiignoreContent(rules) {
|
|
|
148
368
|
}
|
|
149
369
|
return lines.join("\n");
|
|
150
370
|
}
|
|
151
|
-
function
|
|
371
|
+
function extractIgnorePatternsFromRules2(rules) {
|
|
152
372
|
const patterns = [];
|
|
153
373
|
for (const rule of rules) {
|
|
154
374
|
if (rule.frontmatter.globs && rule.frontmatter.globs.length > 0) {
|
|
@@ -209,23 +429,19 @@ function extractIgnorePatternsFromContent(content) {
|
|
|
209
429
|
return patterns;
|
|
210
430
|
}
|
|
211
431
|
|
|
212
|
-
// src/generators/rules/
|
|
213
|
-
import { join as
|
|
432
|
+
// src/generators/rules/augmentcode.ts
|
|
433
|
+
import { join as join7 } from "path";
|
|
214
434
|
|
|
215
|
-
// src/
|
|
216
|
-
import {
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
}),
|
|
222
|
-
{ deny: [] }
|
|
223
|
-
)
|
|
224
|
-
});
|
|
435
|
+
// src/generators/rules/shared-helpers.ts
|
|
436
|
+
import { join as join6 } from "path";
|
|
437
|
+
|
|
438
|
+
// src/utils/ignore.ts
|
|
439
|
+
import { join as join5 } from "path";
|
|
440
|
+
import micromatch from "micromatch";
|
|
225
441
|
|
|
226
442
|
// src/utils/file.ts
|
|
227
443
|
import { mkdir as mkdir2, readdir, readFile, rm, stat, writeFile as writeFile2 } from "fs/promises";
|
|
228
|
-
import { dirname, join as
|
|
444
|
+
import { dirname, join as join4 } from "path";
|
|
229
445
|
async function ensureDir(dirPath) {
|
|
230
446
|
try {
|
|
231
447
|
await stat(dirPath);
|
|
@@ -251,7 +467,7 @@ async function fileExists(filepath) {
|
|
|
251
467
|
async function findFiles(dir, extension = ".md") {
|
|
252
468
|
try {
|
|
253
469
|
const files = await readdir(dir);
|
|
254
|
-
return files.filter((file) => file.endsWith(extension)).map((file) =>
|
|
470
|
+
return files.filter((file) => file.endsWith(extension)).map((file) => join4(dir, file));
|
|
255
471
|
} catch {
|
|
256
472
|
return [];
|
|
257
473
|
}
|
|
@@ -291,14 +507,12 @@ async function removeClaudeGeneratedFiles() {
|
|
|
291
507
|
}
|
|
292
508
|
|
|
293
509
|
// src/utils/ignore.ts
|
|
294
|
-
import { join as join4 } from "path";
|
|
295
|
-
import micromatch from "micromatch";
|
|
296
510
|
var cachedIgnorePatterns = null;
|
|
297
511
|
async function loadIgnorePatterns(baseDir = process.cwd()) {
|
|
298
512
|
if (cachedIgnorePatterns) {
|
|
299
513
|
return cachedIgnorePatterns;
|
|
300
514
|
}
|
|
301
|
-
const ignorePath =
|
|
515
|
+
const ignorePath = join5(baseDir, ".rulesyncignore");
|
|
302
516
|
if (!await fileExists(ignorePath)) {
|
|
303
517
|
cachedIgnorePatterns = { patterns: [] };
|
|
304
518
|
return cachedIgnorePatterns;
|
|
@@ -341,29 +555,167 @@ function filterIgnoredFiles(files, ignorePatterns) {
|
|
|
341
555
|
return files.filter((file) => !isFileIgnored(file, ignorePatterns));
|
|
342
556
|
}
|
|
343
557
|
|
|
558
|
+
// src/generators/rules/shared-helpers.ts
|
|
559
|
+
function resolveOutputDir(config, tool, baseDir) {
|
|
560
|
+
return baseDir ? join6(baseDir, config.outputPaths[tool]) : config.outputPaths[tool];
|
|
561
|
+
}
|
|
562
|
+
function createOutputsArray() {
|
|
563
|
+
return [];
|
|
564
|
+
}
|
|
565
|
+
function addOutput(outputs, tool, config, baseDir, relativePath, content) {
|
|
566
|
+
const outputDir = resolveOutputDir(config, tool, baseDir);
|
|
567
|
+
outputs.push({
|
|
568
|
+
tool,
|
|
569
|
+
filepath: join6(outputDir, relativePath),
|
|
570
|
+
content
|
|
571
|
+
});
|
|
572
|
+
}
|
|
573
|
+
async function generateRulesConfig(rules, config, generatorConfig, baseDir) {
|
|
574
|
+
const outputs = [];
|
|
575
|
+
for (const rule of rules) {
|
|
576
|
+
const content = generatorConfig.generateContent(rule);
|
|
577
|
+
const outputDir = resolveOutputDir(config, generatorConfig.tool, baseDir);
|
|
578
|
+
const filepath = generatorConfig.pathResolver ? generatorConfig.pathResolver(rule, outputDir) : join6(outputDir, `${rule.filename}${generatorConfig.fileExtension}`);
|
|
579
|
+
outputs.push({
|
|
580
|
+
tool: generatorConfig.tool,
|
|
581
|
+
filepath,
|
|
582
|
+
content
|
|
583
|
+
});
|
|
584
|
+
}
|
|
585
|
+
const ignorePatterns = await loadIgnorePatterns(baseDir);
|
|
586
|
+
if (ignorePatterns.patterns.length > 0) {
|
|
587
|
+
const ignorePath = baseDir ? join6(baseDir, generatorConfig.ignoreFileName) : generatorConfig.ignoreFileName;
|
|
588
|
+
const ignoreContent = generateIgnoreFile(ignorePatterns.patterns, generatorConfig.tool);
|
|
589
|
+
outputs.push({
|
|
590
|
+
tool: generatorConfig.tool,
|
|
591
|
+
filepath: ignorePath,
|
|
592
|
+
content: ignoreContent
|
|
593
|
+
});
|
|
594
|
+
}
|
|
595
|
+
return outputs;
|
|
596
|
+
}
|
|
597
|
+
function generateIgnoreFile(patterns, tool) {
|
|
598
|
+
const lines = [
|
|
599
|
+
"# Generated by rulesync from .rulesyncignore",
|
|
600
|
+
"# This file is automatically generated. Do not edit manually."
|
|
601
|
+
];
|
|
602
|
+
if (tool === "copilot") {
|
|
603
|
+
lines.push("# Note: .copilotignore is not officially supported by GitHub Copilot.");
|
|
604
|
+
lines.push("# This file is for use with community tools like copilotignore-vscode extension.");
|
|
605
|
+
}
|
|
606
|
+
lines.push("");
|
|
607
|
+
lines.push(...patterns);
|
|
608
|
+
return lines.join("\n");
|
|
609
|
+
}
|
|
610
|
+
async function generateComplexRulesConfig(rules, config, generatorConfig, baseDir) {
|
|
611
|
+
const unifiedConfig = {
|
|
612
|
+
tool: generatorConfig.tool,
|
|
613
|
+
fileExtension: generatorConfig.fileExtension,
|
|
614
|
+
ignoreFileName: generatorConfig.ignoreFileName,
|
|
615
|
+
generateContent: generatorConfig.generateContent,
|
|
616
|
+
pathResolver: generatorConfig.getOutputPath
|
|
617
|
+
};
|
|
618
|
+
return generateRulesConfig(rules, config, unifiedConfig, baseDir);
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
// src/generators/rules/augmentcode.ts
|
|
622
|
+
async function generateAugmentcodeConfig(rules, config, baseDir) {
|
|
623
|
+
const outputs = createOutputsArray();
|
|
624
|
+
rules.forEach((rule) => {
|
|
625
|
+
addOutput(
|
|
626
|
+
outputs,
|
|
627
|
+
"augmentcode",
|
|
628
|
+
config,
|
|
629
|
+
baseDir,
|
|
630
|
+
join7(".augment", "rules", `${rule.filename}.md`),
|
|
631
|
+
generateRuleFile(rule)
|
|
632
|
+
);
|
|
633
|
+
});
|
|
634
|
+
return outputs;
|
|
635
|
+
}
|
|
636
|
+
function generateRuleFile(rule) {
|
|
637
|
+
const lines = [];
|
|
638
|
+
lines.push("---");
|
|
639
|
+
let ruleType = "manual";
|
|
640
|
+
let description = rule.frontmatter.description;
|
|
641
|
+
if (rule.filename.endsWith("-always")) {
|
|
642
|
+
ruleType = "always";
|
|
643
|
+
description = "";
|
|
644
|
+
} else if (rule.filename.endsWith("-auto")) {
|
|
645
|
+
ruleType = "auto";
|
|
646
|
+
}
|
|
647
|
+
lines.push(`type: ${ruleType}`);
|
|
648
|
+
lines.push(`description: "${description}"`);
|
|
649
|
+
if (rule.frontmatter.tags && Array.isArray(rule.frontmatter.tags) && rule.frontmatter.tags.length > 0) {
|
|
650
|
+
lines.push(`tags: [${rule.frontmatter.tags.map((tag) => `"${tag}"`).join(", ")}]`);
|
|
651
|
+
}
|
|
652
|
+
lines.push("---");
|
|
653
|
+
lines.push("");
|
|
654
|
+
lines.push(rule.content.trim());
|
|
655
|
+
return lines.join("\n");
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
// src/generators/rules/augmentcode-legacy.ts
|
|
659
|
+
async function generateAugmentcodeLegacyConfig(rules, config, baseDir) {
|
|
660
|
+
const outputs = createOutputsArray();
|
|
661
|
+
if (rules.length > 0) {
|
|
662
|
+
addOutput(
|
|
663
|
+
outputs,
|
|
664
|
+
"augmentcode-legacy",
|
|
665
|
+
config,
|
|
666
|
+
baseDir,
|
|
667
|
+
".augment-guidelines",
|
|
668
|
+
generateLegacyGuidelinesFile(rules)
|
|
669
|
+
);
|
|
670
|
+
}
|
|
671
|
+
return outputs;
|
|
672
|
+
}
|
|
673
|
+
function generateLegacyGuidelinesFile(allRules) {
|
|
674
|
+
const lines = [];
|
|
675
|
+
for (const rule of allRules) {
|
|
676
|
+
lines.push(rule.content.trim());
|
|
677
|
+
lines.push("");
|
|
678
|
+
}
|
|
679
|
+
return lines.join("\n").trim();
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
// src/generators/rules/claudecode.ts
|
|
683
|
+
import { join as join8 } from "path";
|
|
684
|
+
|
|
685
|
+
// src/types/claudecode.ts
|
|
686
|
+
import { z } from "zod/mini";
|
|
687
|
+
var ClaudeSettingsSchema = z.looseObject({
|
|
688
|
+
permissions: z._default(
|
|
689
|
+
z.looseObject({
|
|
690
|
+
deny: z._default(z.array(z.string()), [])
|
|
691
|
+
}),
|
|
692
|
+
{ deny: [] }
|
|
693
|
+
)
|
|
694
|
+
});
|
|
695
|
+
|
|
344
696
|
// src/generators/rules/claudecode.ts
|
|
345
697
|
async function generateClaudecodeConfig(rules, config, baseDir) {
|
|
346
698
|
const outputs = [];
|
|
347
699
|
const rootRules = rules.filter((r) => r.frontmatter.root === true);
|
|
348
700
|
const detailRules = rules.filter((r) => r.frontmatter.root === false);
|
|
349
701
|
const claudeMdContent = generateClaudeMarkdown(rootRules, detailRules);
|
|
350
|
-
const claudeOutputDir = baseDir ?
|
|
702
|
+
const claudeOutputDir = baseDir ? join8(baseDir, config.outputPaths.claudecode) : config.outputPaths.claudecode;
|
|
351
703
|
outputs.push({
|
|
352
704
|
tool: "claudecode",
|
|
353
|
-
filepath:
|
|
705
|
+
filepath: join8(claudeOutputDir, "CLAUDE.md"),
|
|
354
706
|
content: claudeMdContent
|
|
355
707
|
});
|
|
356
708
|
for (const rule of detailRules) {
|
|
357
709
|
const memoryContent = generateMemoryFile(rule);
|
|
358
710
|
outputs.push({
|
|
359
711
|
tool: "claudecode",
|
|
360
|
-
filepath:
|
|
712
|
+
filepath: join8(claudeOutputDir, ".claude", "memories", `${rule.filename}.md`),
|
|
361
713
|
content: memoryContent
|
|
362
714
|
});
|
|
363
715
|
}
|
|
364
716
|
const ignorePatterns = await loadIgnorePatterns(baseDir);
|
|
365
717
|
if (ignorePatterns.patterns.length > 0) {
|
|
366
|
-
const settingsPath = baseDir ?
|
|
718
|
+
const settingsPath = baseDir ? join8(baseDir, ".claude", "settings.json") : join8(".claude", "settings.json");
|
|
367
719
|
await updateClaudeSettings(settingsPath, ignorePatterns.patterns);
|
|
368
720
|
}
|
|
369
721
|
return outputs;
|
|
@@ -427,70 +779,38 @@ async function updateClaudeSettings(settingsPath, ignorePatterns) {
|
|
|
427
779
|
}
|
|
428
780
|
|
|
429
781
|
// src/generators/rules/cline.ts
|
|
430
|
-
import { join as join6 } from "path";
|
|
431
782
|
async function generateClineConfig(rules, config, baseDir) {
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
const filepath = join6(outputDir, `${rule.filename}.md`);
|
|
437
|
-
outputs.push({
|
|
438
|
-
tool: "cline",
|
|
439
|
-
filepath,
|
|
440
|
-
content
|
|
441
|
-
});
|
|
442
|
-
}
|
|
443
|
-
const ignorePatterns = await loadIgnorePatterns(baseDir);
|
|
444
|
-
if (ignorePatterns.patterns.length > 0) {
|
|
445
|
-
const clineIgnorePath = baseDir ? join6(baseDir, ".clineignore") : ".clineignore";
|
|
446
|
-
const clineIgnoreContent = generateClineIgnore(ignorePatterns.patterns);
|
|
447
|
-
outputs.push({
|
|
783
|
+
return generateRulesConfig(
|
|
784
|
+
rules,
|
|
785
|
+
config,
|
|
786
|
+
{
|
|
448
787
|
tool: "cline",
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
function generateClineMarkdown(rule) {
|
|
456
|
-
return rule.content.trim();
|
|
457
|
-
}
|
|
458
|
-
function generateClineIgnore(patterns) {
|
|
459
|
-
const lines = [
|
|
460
|
-
"# Generated by rulesync from .rulesyncignore",
|
|
461
|
-
"# This file is automatically generated. Do not edit manually.",
|
|
462
|
-
"",
|
|
463
|
-
...patterns
|
|
464
|
-
];
|
|
465
|
-
return lines.join("\n");
|
|
788
|
+
fileExtension: ".md",
|
|
789
|
+
ignoreFileName: ".clineignore",
|
|
790
|
+
generateContent: (rule) => rule.content.trim()
|
|
791
|
+
},
|
|
792
|
+
baseDir
|
|
793
|
+
);
|
|
466
794
|
}
|
|
467
795
|
|
|
468
796
|
// src/generators/rules/copilot.ts
|
|
469
|
-
import { join as
|
|
797
|
+
import { join as join9 } from "path";
|
|
470
798
|
async function generateCopilotConfig(rules, config, baseDir) {
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
const outputDir = baseDir ? join7(baseDir, config.outputPaths.copilot) : config.outputPaths.copilot;
|
|
476
|
-
const filepath = join7(outputDir, `${baseFilename}.instructions.md`);
|
|
477
|
-
outputs.push({
|
|
478
|
-
tool: "copilot",
|
|
479
|
-
filepath,
|
|
480
|
-
content
|
|
481
|
-
});
|
|
482
|
-
}
|
|
483
|
-
const ignorePatterns = await loadIgnorePatterns(baseDir);
|
|
484
|
-
if (ignorePatterns.patterns.length > 0) {
|
|
485
|
-
const copilotIgnorePath = baseDir ? join7(baseDir, ".copilotignore") : ".copilotignore";
|
|
486
|
-
const copilotIgnoreContent = generateCopilotIgnore(ignorePatterns.patterns);
|
|
487
|
-
outputs.push({
|
|
799
|
+
return generateComplexRulesConfig(
|
|
800
|
+
rules,
|
|
801
|
+
config,
|
|
802
|
+
{
|
|
488
803
|
tool: "copilot",
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
804
|
+
fileExtension: ".instructions.md",
|
|
805
|
+
ignoreFileName: ".copilotignore",
|
|
806
|
+
generateContent: generateCopilotMarkdown,
|
|
807
|
+
getOutputPath: (rule, outputDir) => {
|
|
808
|
+
const baseFilename = rule.filename.replace(/\.md$/, "");
|
|
809
|
+
return join9(outputDir, `${baseFilename}.instructions.md`);
|
|
810
|
+
}
|
|
811
|
+
},
|
|
812
|
+
baseDir
|
|
813
|
+
);
|
|
494
814
|
}
|
|
495
815
|
function generateCopilotMarkdown(rule) {
|
|
496
816
|
const lines = [];
|
|
@@ -505,43 +825,24 @@ function generateCopilotMarkdown(rule) {
|
|
|
505
825
|
lines.push(rule.content);
|
|
506
826
|
return lines.join("\n");
|
|
507
827
|
}
|
|
508
|
-
function generateCopilotIgnore(patterns) {
|
|
509
|
-
const lines = [
|
|
510
|
-
"# Generated by rulesync from .rulesyncignore",
|
|
511
|
-
"# This file is automatically generated. Do not edit manually.",
|
|
512
|
-
"# Note: .copilotignore is not officially supported by GitHub Copilot.",
|
|
513
|
-
"# This file is for use with community tools like copilotignore-vscode extension.",
|
|
514
|
-
"",
|
|
515
|
-
...patterns
|
|
516
|
-
];
|
|
517
|
-
return lines.join("\n");
|
|
518
|
-
}
|
|
519
828
|
|
|
520
829
|
// src/generators/rules/cursor.ts
|
|
521
|
-
import { join as
|
|
830
|
+
import { join as join10 } from "path";
|
|
522
831
|
async function generateCursorConfig(rules, config, baseDir) {
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
const filepath = join8(outputDir, `${rule.filename}.mdc`);
|
|
528
|
-
outputs.push({
|
|
529
|
-
tool: "cursor",
|
|
530
|
-
filepath,
|
|
531
|
-
content
|
|
532
|
-
});
|
|
533
|
-
}
|
|
534
|
-
const ignorePatterns = await loadIgnorePatterns(baseDir);
|
|
535
|
-
if (ignorePatterns.patterns.length > 0) {
|
|
536
|
-
const cursorIgnorePath = baseDir ? join8(baseDir, ".cursorignore") : ".cursorignore";
|
|
537
|
-
const cursorIgnoreContent = generateCursorIgnore(ignorePatterns.patterns);
|
|
538
|
-
outputs.push({
|
|
832
|
+
return generateComplexRulesConfig(
|
|
833
|
+
rules,
|
|
834
|
+
config,
|
|
835
|
+
{
|
|
539
836
|
tool: "cursor",
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
837
|
+
fileExtension: ".mdc",
|
|
838
|
+
ignoreFileName: ".cursorignore",
|
|
839
|
+
generateContent: generateCursorMarkdown,
|
|
840
|
+
getOutputPath: (rule, outputDir) => {
|
|
841
|
+
return join10(outputDir, `${rule.filename}.mdc`);
|
|
842
|
+
}
|
|
843
|
+
},
|
|
844
|
+
baseDir
|
|
845
|
+
);
|
|
545
846
|
}
|
|
546
847
|
function generateCursorMarkdown(rule) {
|
|
547
848
|
const lines = [];
|
|
@@ -595,26 +896,17 @@ function determineCursorRuleType(frontmatter) {
|
|
|
595
896
|
}
|
|
596
897
|
return "intelligently";
|
|
597
898
|
}
|
|
598
|
-
function generateCursorIgnore(patterns) {
|
|
599
|
-
const lines = [
|
|
600
|
-
"# Generated by rulesync from .rulesyncignore",
|
|
601
|
-
"# This file is automatically generated. Do not edit manually.",
|
|
602
|
-
"",
|
|
603
|
-
...patterns
|
|
604
|
-
];
|
|
605
|
-
return lines.join("\n");
|
|
606
|
-
}
|
|
607
899
|
|
|
608
900
|
// src/generators/rules/geminicli.ts
|
|
609
|
-
import { join as
|
|
901
|
+
import { join as join11 } from "path";
|
|
610
902
|
async function generateGeminiConfig(rules, config, baseDir) {
|
|
611
903
|
const outputs = [];
|
|
612
904
|
const rootRule = rules.find((rule) => rule.frontmatter.root === true);
|
|
613
905
|
const memoryRules = rules.filter((rule) => rule.frontmatter.root === false);
|
|
614
906
|
for (const rule of memoryRules) {
|
|
615
907
|
const content = generateGeminiMemoryMarkdown(rule);
|
|
616
|
-
const outputDir = baseDir ?
|
|
617
|
-
const filepath =
|
|
908
|
+
const outputDir = baseDir ? join11(baseDir, config.outputPaths.geminicli) : config.outputPaths.geminicli;
|
|
909
|
+
const filepath = join11(outputDir, `${rule.filename}.md`);
|
|
618
910
|
outputs.push({
|
|
619
911
|
tool: "geminicli",
|
|
620
912
|
filepath,
|
|
@@ -622,7 +914,7 @@ async function generateGeminiConfig(rules, config, baseDir) {
|
|
|
622
914
|
});
|
|
623
915
|
}
|
|
624
916
|
const rootContent = generateGeminiRootMarkdown(rootRule, memoryRules, baseDir);
|
|
625
|
-
const rootFilepath = baseDir ?
|
|
917
|
+
const rootFilepath = baseDir ? join11(baseDir, "GEMINI.md") : "GEMINI.md";
|
|
626
918
|
outputs.push({
|
|
627
919
|
tool: "geminicli",
|
|
628
920
|
filepath: rootFilepath,
|
|
@@ -630,7 +922,7 @@ async function generateGeminiConfig(rules, config, baseDir) {
|
|
|
630
922
|
});
|
|
631
923
|
const ignorePatterns = await loadIgnorePatterns(baseDir);
|
|
632
924
|
if (ignorePatterns.patterns.length > 0) {
|
|
633
|
-
const aiexcludePath = baseDir ?
|
|
925
|
+
const aiexcludePath = baseDir ? join11(baseDir, ".aiexclude") : ".aiexclude";
|
|
634
926
|
const aiexcludeContent = generateAiexclude(ignorePatterns.patterns);
|
|
635
927
|
outputs.push({
|
|
636
928
|
tool: "geminicli",
|
|
@@ -678,13 +970,13 @@ function generateAiexclude(patterns) {
|
|
|
678
970
|
}
|
|
679
971
|
|
|
680
972
|
// src/generators/rules/kiro.ts
|
|
681
|
-
import { join as
|
|
973
|
+
import { join as join12 } from "path";
|
|
682
974
|
async function generateKiroConfig(rules, config, baseDir) {
|
|
683
975
|
const outputs = [];
|
|
684
976
|
for (const rule of rules) {
|
|
685
977
|
const content = generateKiroMarkdown(rule);
|
|
686
|
-
const outputDir = baseDir ?
|
|
687
|
-
const filepath =
|
|
978
|
+
const outputDir = baseDir ? join12(baseDir, config.outputPaths.kiro) : config.outputPaths.kiro;
|
|
979
|
+
const filepath = join12(outputDir, `${rule.filename}.md`);
|
|
688
980
|
outputs.push({
|
|
689
981
|
tool: "kiro",
|
|
690
982
|
filepath,
|
|
@@ -698,47 +990,23 @@ function generateKiroMarkdown(rule) {
|
|
|
698
990
|
}
|
|
699
991
|
|
|
700
992
|
// src/generators/rules/roo.ts
|
|
701
|
-
import { join as join11 } from "path";
|
|
702
993
|
async function generateRooConfig(rules, config, baseDir) {
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
const filepath = join11(outputDir, `${rule.filename}.md`);
|
|
708
|
-
outputs.push({
|
|
709
|
-
tool: "roo",
|
|
710
|
-
filepath,
|
|
711
|
-
content
|
|
712
|
-
});
|
|
713
|
-
}
|
|
714
|
-
const ignorePatterns = await loadIgnorePatterns(baseDir);
|
|
715
|
-
if (ignorePatterns.patterns.length > 0) {
|
|
716
|
-
const rooIgnorePath = baseDir ? join11(baseDir, ".rooignore") : ".rooignore";
|
|
717
|
-
const rooIgnoreContent = generateRooIgnore(ignorePatterns.patterns);
|
|
718
|
-
outputs.push({
|
|
994
|
+
return generateRulesConfig(
|
|
995
|
+
rules,
|
|
996
|
+
config,
|
|
997
|
+
{
|
|
719
998
|
tool: "roo",
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
function generateRooMarkdown(rule) {
|
|
727
|
-
return rule.content.trim();
|
|
728
|
-
}
|
|
729
|
-
function generateRooIgnore(patterns) {
|
|
730
|
-
const lines = [
|
|
731
|
-
"# Generated by rulesync from .rulesyncignore",
|
|
732
|
-
"# This file is automatically generated. Do not edit manually.",
|
|
733
|
-
"",
|
|
734
|
-
...patterns
|
|
735
|
-
];
|
|
736
|
-
return lines.join("\n");
|
|
999
|
+
fileExtension: ".md",
|
|
1000
|
+
ignoreFileName: ".rooignore",
|
|
1001
|
+
generateContent: (rule) => rule.content.trim()
|
|
1002
|
+
},
|
|
1003
|
+
baseDir
|
|
1004
|
+
);
|
|
737
1005
|
}
|
|
738
1006
|
|
|
739
1007
|
// src/core/generator.ts
|
|
740
1008
|
async function generateConfigurations(rules, config, targetTools, baseDir) {
|
|
741
|
-
const outputs =
|
|
1009
|
+
const outputs = createOutputsArray();
|
|
742
1010
|
const toolsToGenerate = targetTools || config.defaultTargets;
|
|
743
1011
|
const rootFiles = rules.filter((rule) => rule.frontmatter.root === true);
|
|
744
1012
|
if (rootFiles.length === 0) {
|
|
@@ -767,6 +1035,20 @@ function filterRulesForTool(rules, tool, config) {
|
|
|
767
1035
|
}
|
|
768
1036
|
async function generateForTool(tool, rules, config, baseDir) {
|
|
769
1037
|
switch (tool) {
|
|
1038
|
+
case "augmentcode": {
|
|
1039
|
+
const augmentRulesOutputs = await generateAugmentcodeConfig(rules, config, baseDir);
|
|
1040
|
+
const augmentIgnoreOutputs = await generateAugmentCodeIgnoreFiles(rules, config, baseDir);
|
|
1041
|
+
return [...augmentRulesOutputs, ...augmentIgnoreOutputs];
|
|
1042
|
+
}
|
|
1043
|
+
case "augmentcode-legacy": {
|
|
1044
|
+
const augmentLegacyRulesOutputs = await generateAugmentcodeLegacyConfig(
|
|
1045
|
+
rules,
|
|
1046
|
+
config,
|
|
1047
|
+
baseDir
|
|
1048
|
+
);
|
|
1049
|
+
const augmentIgnoreOutputs = await generateAugmentCodeIgnoreFiles(rules, config, baseDir);
|
|
1050
|
+
return [...augmentLegacyRulesOutputs, ...augmentIgnoreOutputs];
|
|
1051
|
+
}
|
|
770
1052
|
case "copilot":
|
|
771
1053
|
return generateCopilotConfig(rules, config, baseDir);
|
|
772
1054
|
case "cursor":
|
|
@@ -841,7 +1123,8 @@ var RuleFrontmatterSchema = z4.object({
|
|
|
841
1123
|
targets: RulesyncTargetsSchema,
|
|
842
1124
|
description: z4.string(),
|
|
843
1125
|
globs: z4.array(z4.string()),
|
|
844
|
-
cursorRuleType: z4.optional(z4.enum(["always", "manual", "specificFiles", "intelligently"]))
|
|
1126
|
+
cursorRuleType: z4.optional(z4.enum(["always", "manual", "specificFiles", "intelligently"])),
|
|
1127
|
+
tags: z4.optional(z4.array(z4.string()))
|
|
845
1128
|
});
|
|
846
1129
|
var ParsedRuleSchema = z4.object({
|
|
847
1130
|
frontmatter: RuleFrontmatterSchema,
|
|
@@ -1000,6 +1283,16 @@ async function generateMcpConfigs(projectRoot, baseDir) {
|
|
|
1000
1283
|
return results;
|
|
1001
1284
|
}
|
|
1002
1285
|
const generators = [
|
|
1286
|
+
{
|
|
1287
|
+
tool: "augmentcode-project",
|
|
1288
|
+
path: path3.join(targetRoot, ".mcp.json"),
|
|
1289
|
+
generate: () => generateAugmentcodeMcp(config)
|
|
1290
|
+
},
|
|
1291
|
+
{
|
|
1292
|
+
tool: "augmentcode-legacy-project",
|
|
1293
|
+
path: path3.join(targetRoot, ".mcp.json"),
|
|
1294
|
+
generate: () => generateAugmentcodeMcp(config)
|
|
1295
|
+
},
|
|
1003
1296
|
{
|
|
1004
1297
|
tool: "claude-project",
|
|
1005
1298
|
path: path3.join(targetRoot, ".mcp.json"),
|
|
@@ -1040,7 +1333,7 @@ async function generateMcpConfigs(projectRoot, baseDir) {
|
|
|
1040
1333
|
try {
|
|
1041
1334
|
const content = generator.generate();
|
|
1042
1335
|
const parsed = JSON.parse(content);
|
|
1043
|
-
if (generator.tool.includes("claude") || generator.tool.includes("cline") || generator.tool.includes("cursor") || generator.tool.includes("gemini") || generator.tool.includes("kiro") || generator.tool.includes("roo")) {
|
|
1336
|
+
if (generator.tool.includes("augmentcode") || generator.tool.includes("claude") || generator.tool.includes("cline") || generator.tool.includes("cursor") || generator.tool.includes("gemini") || generator.tool.includes("kiro") || generator.tool.includes("roo")) {
|
|
1044
1337
|
if (!parsed.mcpServers || Object.keys(parsed.mcpServers).length === 0) {
|
|
1045
1338
|
results.push({
|
|
1046
1339
|
tool: generator.tool,
|
|
@@ -1159,11 +1452,10 @@ Generating configurations for base directory: ${baseDir}`);
|
|
|
1159
1452
|
console.warn("\u26A0\uFE0F No configurations generated");
|
|
1160
1453
|
return;
|
|
1161
1454
|
}
|
|
1162
|
-
console.log(`
|
|
1163
|
-
\u{1F389} Successfully generated ${totalOutputs} configuration file(s)!`);
|
|
1164
1455
|
if (options.verbose) {
|
|
1165
1456
|
console.log("\nGenerating MCP configurations...");
|
|
1166
1457
|
}
|
|
1458
|
+
let totalMcpOutputs = 0;
|
|
1167
1459
|
for (const baseDir of baseDirs) {
|
|
1168
1460
|
const mcpResults = await generateMcpConfigs(
|
|
1169
1461
|
process.cwd(),
|
|
@@ -1178,6 +1470,7 @@ Generating configurations for base directory: ${baseDir}`);
|
|
|
1178
1470
|
for (const result of mcpResults) {
|
|
1179
1471
|
if (result.status === "success") {
|
|
1180
1472
|
console.log(`\u2705 Generated ${result.tool} MCP configuration: ${result.path}`);
|
|
1473
|
+
totalMcpOutputs++;
|
|
1181
1474
|
} else if (result.status === "error") {
|
|
1182
1475
|
console.error(`\u274C Failed to generate ${result.tool} MCP configuration: ${result.error}`);
|
|
1183
1476
|
} else if (options.verbose && result.status === "skipped") {
|
|
@@ -1185,6 +1478,13 @@ Generating configurations for base directory: ${baseDir}`);
|
|
|
1185
1478
|
}
|
|
1186
1479
|
}
|
|
1187
1480
|
}
|
|
1481
|
+
const totalGenerated = totalOutputs + totalMcpOutputs;
|
|
1482
|
+
if (totalGenerated > 0) {
|
|
1483
|
+
console.log(
|
|
1484
|
+
`
|
|
1485
|
+
\u{1F389} All done! Generated ${totalGenerated} file(s) total (${totalOutputs} configurations + ${totalMcpOutputs} MCP configurations)`
|
|
1486
|
+
);
|
|
1487
|
+
}
|
|
1188
1488
|
} catch (error) {
|
|
1189
1489
|
console.error("\u274C Failed to generate configurations:", error);
|
|
1190
1490
|
process.exit(1);
|
|
@@ -1193,9 +1493,9 @@ Generating configurations for base directory: ${baseDir}`);
|
|
|
1193
1493
|
|
|
1194
1494
|
// src/cli/commands/gitignore.ts
|
|
1195
1495
|
import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync } from "fs";
|
|
1196
|
-
import { join as
|
|
1496
|
+
import { join as join15 } from "path";
|
|
1197
1497
|
var gitignoreCommand = async () => {
|
|
1198
|
-
const gitignorePath =
|
|
1498
|
+
const gitignorePath = join15(process.cwd(), ".gitignore");
|
|
1199
1499
|
const rulesFilesToIgnore = [
|
|
1200
1500
|
"# Generated by rulesync - AI tool configuration files",
|
|
1201
1501
|
"**/.github/copilot-instructions.md",
|
|
@@ -1213,7 +1513,10 @@ var gitignoreCommand = async () => {
|
|
|
1213
1513
|
"**/.gemini/memories/",
|
|
1214
1514
|
"**/.aiexclude",
|
|
1215
1515
|
"**/.aiignore",
|
|
1516
|
+
"**/.augmentignore",
|
|
1216
1517
|
"**/.kiro/steering/",
|
|
1518
|
+
"**/.augment/rules/",
|
|
1519
|
+
"**/.augment-guidelines",
|
|
1217
1520
|
"**/.mcp.json",
|
|
1218
1521
|
"!.rulesync/.mcp.json",
|
|
1219
1522
|
"**/.cursor/mcp.json",
|
|
@@ -1232,54 +1535,292 @@ var gitignoreCommand = async () => {
|
|
|
1232
1535
|
linesToAdd.push(rule);
|
|
1233
1536
|
}
|
|
1234
1537
|
}
|
|
1235
|
-
if (linesToAdd.length === 0) {
|
|
1236
|
-
console.log("\u2705 .gitignore is already up to date");
|
|
1237
|
-
return;
|
|
1238
|
-
}
|
|
1239
|
-
const newContent = gitignoreContent ? `${gitignoreContent.trimEnd()}
|
|
1240
|
-
|
|
1241
|
-
${linesToAdd.join("\n")}
|
|
1242
|
-
` : `${linesToAdd.join("\n")}
|
|
1243
|
-
`;
|
|
1244
|
-
writeFileSync(gitignorePath, newContent);
|
|
1245
|
-
console.log(`\u2705 Added ${linesToAdd.length} rules to .gitignore:`);
|
|
1246
|
-
for (const line of linesToAdd) {
|
|
1247
|
-
if (!line.startsWith("#")) {
|
|
1248
|
-
console.log(` ${line}`);
|
|
1538
|
+
if (linesToAdd.length === 0) {
|
|
1539
|
+
console.log("\u2705 .gitignore is already up to date");
|
|
1540
|
+
return;
|
|
1541
|
+
}
|
|
1542
|
+
const newContent = gitignoreContent ? `${gitignoreContent.trimEnd()}
|
|
1543
|
+
|
|
1544
|
+
${linesToAdd.join("\n")}
|
|
1545
|
+
` : `${linesToAdd.join("\n")}
|
|
1546
|
+
`;
|
|
1547
|
+
writeFileSync(gitignorePath, newContent);
|
|
1548
|
+
console.log(`\u2705 Added ${linesToAdd.length} rules to .gitignore:`);
|
|
1549
|
+
for (const line of linesToAdd) {
|
|
1550
|
+
if (!line.startsWith("#")) {
|
|
1551
|
+
console.log(` ${line}`);
|
|
1552
|
+
}
|
|
1553
|
+
}
|
|
1554
|
+
};
|
|
1555
|
+
|
|
1556
|
+
// src/core/importer.ts
|
|
1557
|
+
import { join as join20 } from "path";
|
|
1558
|
+
import matter5 from "gray-matter";
|
|
1559
|
+
|
|
1560
|
+
// src/parsers/augmentcode.ts
|
|
1561
|
+
import { basename as basename2, join as join16 } from "path";
|
|
1562
|
+
import matter2 from "gray-matter";
|
|
1563
|
+
|
|
1564
|
+
// src/utils/parser-helpers.ts
|
|
1565
|
+
function createParseResult() {
|
|
1566
|
+
return { rules: [], errors: [] };
|
|
1567
|
+
}
|
|
1568
|
+
function addError(result, error) {
|
|
1569
|
+
result.errors.push(error);
|
|
1570
|
+
}
|
|
1571
|
+
function addRule(result, rule) {
|
|
1572
|
+
if (!result.rules) {
|
|
1573
|
+
result.rules = [];
|
|
1574
|
+
}
|
|
1575
|
+
result.rules.push(rule);
|
|
1576
|
+
}
|
|
1577
|
+
function addRules(result, rules) {
|
|
1578
|
+
if (!result.rules) {
|
|
1579
|
+
result.rules = [];
|
|
1580
|
+
}
|
|
1581
|
+
result.rules.push(...rules);
|
|
1582
|
+
}
|
|
1583
|
+
function handleParseError(error, context) {
|
|
1584
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1585
|
+
return `${context}: ${errorMessage}`;
|
|
1586
|
+
}
|
|
1587
|
+
async function safeReadFile(operation, errorContext) {
|
|
1588
|
+
try {
|
|
1589
|
+
const result = await operation();
|
|
1590
|
+
return { success: true, result };
|
|
1591
|
+
} catch (error) {
|
|
1592
|
+
return {
|
|
1593
|
+
success: false,
|
|
1594
|
+
error: handleParseError(error, errorContext)
|
|
1595
|
+
};
|
|
1596
|
+
}
|
|
1597
|
+
}
|
|
1598
|
+
|
|
1599
|
+
// src/parsers/augmentcode.ts
|
|
1600
|
+
async function parseAugmentcodeConfiguration(baseDir = process.cwd()) {
|
|
1601
|
+
const result = createParseResult();
|
|
1602
|
+
const rulesDir = join16(baseDir, ".augment", "rules");
|
|
1603
|
+
if (await fileExists(rulesDir)) {
|
|
1604
|
+
const rulesResult = await parseAugmentRules(rulesDir);
|
|
1605
|
+
addRules(result, rulesResult.rules);
|
|
1606
|
+
result.errors.push(...rulesResult.errors);
|
|
1607
|
+
} else {
|
|
1608
|
+
addError(result, "No AugmentCode configuration found. Expected .augment/rules/ directory.");
|
|
1609
|
+
}
|
|
1610
|
+
return { rules: result.rules || [], errors: result.errors };
|
|
1611
|
+
}
|
|
1612
|
+
async function parseAugmentRules(rulesDir) {
|
|
1613
|
+
const rules = [];
|
|
1614
|
+
const errors = [];
|
|
1615
|
+
try {
|
|
1616
|
+
const { readdir: readdir2 } = await import("fs/promises");
|
|
1617
|
+
const files = await readdir2(rulesDir);
|
|
1618
|
+
for (const file of files) {
|
|
1619
|
+
if (file.endsWith(".md") || file.endsWith(".mdc")) {
|
|
1620
|
+
const filePath = join16(rulesDir, file);
|
|
1621
|
+
try {
|
|
1622
|
+
const rawContent = await readFileContent(filePath);
|
|
1623
|
+
const parsed = matter2(rawContent);
|
|
1624
|
+
const frontmatterData = parsed.data;
|
|
1625
|
+
const ruleType = frontmatterData.type || "manual";
|
|
1626
|
+
const description = frontmatterData.description || "";
|
|
1627
|
+
const tags = Array.isArray(frontmatterData.tags) ? frontmatterData.tags : void 0;
|
|
1628
|
+
const isRoot = ruleType === "always";
|
|
1629
|
+
const filename = basename2(file, file.endsWith(".mdc") ? ".mdc" : ".md");
|
|
1630
|
+
const frontmatter = {
|
|
1631
|
+
root: isRoot,
|
|
1632
|
+
targets: ["augmentcode"],
|
|
1633
|
+
description,
|
|
1634
|
+
globs: ["**/*"],
|
|
1635
|
+
// AugmentCode doesn't use specific globs in the same way
|
|
1636
|
+
...tags && { tags }
|
|
1637
|
+
};
|
|
1638
|
+
rules.push({
|
|
1639
|
+
frontmatter,
|
|
1640
|
+
content: parsed.content.trim(),
|
|
1641
|
+
filename: `augmentcode-${ruleType}-${filename}`,
|
|
1642
|
+
filepath: filePath
|
|
1643
|
+
});
|
|
1644
|
+
} catch (error) {
|
|
1645
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1646
|
+
errors.push(`Failed to parse ${filePath}: ${errorMessage}`);
|
|
1647
|
+
}
|
|
1648
|
+
}
|
|
1649
|
+
}
|
|
1650
|
+
} catch (error) {
|
|
1651
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1652
|
+
errors.push(`Failed to read .augment/rules/ directory: ${errorMessage}`);
|
|
1653
|
+
}
|
|
1654
|
+
return { rules, errors };
|
|
1655
|
+
}
|
|
1656
|
+
|
|
1657
|
+
// src/parsers/augmentcode-legacy.ts
|
|
1658
|
+
import { join as join17 } from "path";
|
|
1659
|
+
async function parseAugmentcodeLegacyConfiguration(baseDir = process.cwd()) {
|
|
1660
|
+
const result = createParseResult();
|
|
1661
|
+
const guidelinesPath = join17(baseDir, ".augment-guidelines");
|
|
1662
|
+
if (await fileExists(guidelinesPath)) {
|
|
1663
|
+
const guidelinesResult = await parseAugmentGuidelines(guidelinesPath);
|
|
1664
|
+
if (guidelinesResult.rule) {
|
|
1665
|
+
addRule(result, guidelinesResult.rule);
|
|
1666
|
+
}
|
|
1667
|
+
result.errors.push(...guidelinesResult.errors);
|
|
1668
|
+
} else {
|
|
1669
|
+
addError(
|
|
1670
|
+
result,
|
|
1671
|
+
"No AugmentCode legacy configuration found. Expected .augment-guidelines file."
|
|
1672
|
+
);
|
|
1673
|
+
}
|
|
1674
|
+
return { rules: result.rules || [], errors: result.errors };
|
|
1675
|
+
}
|
|
1676
|
+
async function parseAugmentGuidelines(guidelinesPath) {
|
|
1677
|
+
const parseResult = await safeReadFile(async () => {
|
|
1678
|
+
const content = await readFileContent(guidelinesPath);
|
|
1679
|
+
if (content.trim()) {
|
|
1680
|
+
const frontmatter = {
|
|
1681
|
+
root: true,
|
|
1682
|
+
// Legacy guidelines become root rules
|
|
1683
|
+
targets: ["augmentcode-legacy"],
|
|
1684
|
+
description: "Legacy AugmentCode guidelines",
|
|
1685
|
+
globs: ["**/*"]
|
|
1686
|
+
};
|
|
1687
|
+
return {
|
|
1688
|
+
frontmatter,
|
|
1689
|
+
content: content.trim(),
|
|
1690
|
+
filename: "augmentcode-legacy-guidelines",
|
|
1691
|
+
filepath: guidelinesPath
|
|
1692
|
+
};
|
|
1693
|
+
}
|
|
1694
|
+
return null;
|
|
1695
|
+
}, "Failed to parse .augment-guidelines");
|
|
1696
|
+
if (parseResult.success) {
|
|
1697
|
+
return { rule: parseResult.result || null, errors: [] };
|
|
1698
|
+
} else {
|
|
1699
|
+
return { rule: null, errors: [parseResult.error || "Unknown error"] };
|
|
1700
|
+
}
|
|
1701
|
+
}
|
|
1702
|
+
|
|
1703
|
+
// src/parsers/shared-helpers.ts
|
|
1704
|
+
import { basename as basename3, join as join18 } from "path";
|
|
1705
|
+
import matter3 from "gray-matter";
|
|
1706
|
+
async function parseConfigurationFiles(baseDir = process.cwd(), config) {
|
|
1707
|
+
const errors = [];
|
|
1708
|
+
const rules = [];
|
|
1709
|
+
if (config.mainFile) {
|
|
1710
|
+
const mainFilePath = join18(baseDir, config.mainFile.path);
|
|
1711
|
+
if (await fileExists(mainFilePath)) {
|
|
1712
|
+
try {
|
|
1713
|
+
const rawContent = await readFileContent(mainFilePath);
|
|
1714
|
+
let content;
|
|
1715
|
+
let frontmatter;
|
|
1716
|
+
if (config.mainFile.useFrontmatter) {
|
|
1717
|
+
const parsed = matter3(rawContent);
|
|
1718
|
+
content = parsed.content.trim();
|
|
1719
|
+
frontmatter = {
|
|
1720
|
+
root: false,
|
|
1721
|
+
targets: [config.tool],
|
|
1722
|
+
description: config.mainFile.description,
|
|
1723
|
+
globs: ["**/*"]
|
|
1724
|
+
};
|
|
1725
|
+
} else {
|
|
1726
|
+
content = rawContent.trim();
|
|
1727
|
+
frontmatter = {
|
|
1728
|
+
root: false,
|
|
1729
|
+
targets: [config.tool],
|
|
1730
|
+
description: config.mainFile.description,
|
|
1731
|
+
globs: ["**/*"]
|
|
1732
|
+
};
|
|
1733
|
+
}
|
|
1734
|
+
if (content) {
|
|
1735
|
+
rules.push({
|
|
1736
|
+
frontmatter,
|
|
1737
|
+
content,
|
|
1738
|
+
filename: `${config.tool}-instructions`,
|
|
1739
|
+
filepath: mainFilePath
|
|
1740
|
+
});
|
|
1741
|
+
}
|
|
1742
|
+
} catch (error) {
|
|
1743
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1744
|
+
errors.push(`Failed to parse ${config.mainFile.path}: ${errorMessage}`);
|
|
1745
|
+
}
|
|
1746
|
+
}
|
|
1747
|
+
}
|
|
1748
|
+
if (config.directories) {
|
|
1749
|
+
for (const dirConfig of config.directories) {
|
|
1750
|
+
const dirPath = join18(baseDir, dirConfig.directory);
|
|
1751
|
+
if (await fileExists(dirPath)) {
|
|
1752
|
+
try {
|
|
1753
|
+
const { readdir: readdir2 } = await import("fs/promises");
|
|
1754
|
+
const files = await readdir2(dirPath);
|
|
1755
|
+
for (const file of files) {
|
|
1756
|
+
if (file.endsWith(dirConfig.filePattern)) {
|
|
1757
|
+
const filePath = join18(dirPath, file);
|
|
1758
|
+
try {
|
|
1759
|
+
const rawContent = await readFileContent(filePath);
|
|
1760
|
+
let content;
|
|
1761
|
+
if (dirConfig.filePattern === ".instructions.md") {
|
|
1762
|
+
const parsed = matter3(rawContent);
|
|
1763
|
+
content = parsed.content.trim();
|
|
1764
|
+
} else {
|
|
1765
|
+
content = rawContent.trim();
|
|
1766
|
+
}
|
|
1767
|
+
if (content) {
|
|
1768
|
+
const filename = file.replace(new RegExp(`\\${dirConfig.filePattern}$`), "");
|
|
1769
|
+
const frontmatter = {
|
|
1770
|
+
root: false,
|
|
1771
|
+
targets: [config.tool],
|
|
1772
|
+
description: `${dirConfig.description}: ${filename}`,
|
|
1773
|
+
globs: ["**/*"]
|
|
1774
|
+
};
|
|
1775
|
+
rules.push({
|
|
1776
|
+
frontmatter,
|
|
1777
|
+
content,
|
|
1778
|
+
filename: `${config.tool}-${filename}`,
|
|
1779
|
+
filepath: filePath
|
|
1780
|
+
});
|
|
1781
|
+
}
|
|
1782
|
+
} catch (error) {
|
|
1783
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1784
|
+
errors.push(`Failed to parse ${filePath}: ${errorMessage}`);
|
|
1785
|
+
}
|
|
1786
|
+
}
|
|
1787
|
+
}
|
|
1788
|
+
} catch (error) {
|
|
1789
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1790
|
+
errors.push(`Failed to parse ${dirConfig.directory} files: ${errorMessage}`);
|
|
1791
|
+
}
|
|
1792
|
+
}
|
|
1249
1793
|
}
|
|
1250
1794
|
}
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
// src/parsers/claudecode.ts
|
|
1258
|
-
import { basename as basename2, join as join15 } from "path";
|
|
1259
|
-
async function parseClaudeConfiguration(baseDir = process.cwd()) {
|
|
1795
|
+
if (rules.length === 0) {
|
|
1796
|
+
errors.push(config.errorMessage);
|
|
1797
|
+
}
|
|
1798
|
+
return { rules, errors };
|
|
1799
|
+
}
|
|
1800
|
+
async function parseMemoryBasedConfiguration(baseDir = process.cwd(), config) {
|
|
1260
1801
|
const errors = [];
|
|
1261
1802
|
const rules = [];
|
|
1262
1803
|
let ignorePatterns;
|
|
1263
1804
|
let mcpServers;
|
|
1264
|
-
const
|
|
1265
|
-
if (!await fileExists(
|
|
1266
|
-
errors.push(
|
|
1805
|
+
const mainFilePath = join18(baseDir, config.mainFileName);
|
|
1806
|
+
if (!await fileExists(mainFilePath)) {
|
|
1807
|
+
errors.push(`${config.mainFileName} file not found`);
|
|
1267
1808
|
return { rules, errors };
|
|
1268
1809
|
}
|
|
1269
1810
|
try {
|
|
1270
|
-
const
|
|
1271
|
-
const mainRule =
|
|
1811
|
+
const mainContent = await readFileContent(mainFilePath);
|
|
1812
|
+
const mainRule = parseMainFile(mainContent, mainFilePath, config);
|
|
1272
1813
|
if (mainRule) {
|
|
1273
1814
|
rules.push(mainRule);
|
|
1274
1815
|
}
|
|
1275
|
-
const memoryDir =
|
|
1816
|
+
const memoryDir = join18(baseDir, config.memoryDirPath);
|
|
1276
1817
|
if (await fileExists(memoryDir)) {
|
|
1277
|
-
const memoryRules = await
|
|
1818
|
+
const memoryRules = await parseMemoryFiles(memoryDir, config);
|
|
1278
1819
|
rules.push(...memoryRules);
|
|
1279
1820
|
}
|
|
1280
|
-
const settingsPath =
|
|
1821
|
+
const settingsPath = join18(baseDir, config.settingsPath);
|
|
1281
1822
|
if (await fileExists(settingsPath)) {
|
|
1282
|
-
const settingsResult = await
|
|
1823
|
+
const settingsResult = await parseSettingsFile(settingsPath, config.tool);
|
|
1283
1824
|
if (settingsResult.ignorePatterns) {
|
|
1284
1825
|
ignorePatterns = settingsResult.ignorePatterns;
|
|
1285
1826
|
}
|
|
@@ -1288,9 +1829,18 @@ async function parseClaudeConfiguration(baseDir = process.cwd()) {
|
|
|
1288
1829
|
}
|
|
1289
1830
|
errors.push(...settingsResult.errors);
|
|
1290
1831
|
}
|
|
1832
|
+
if (config.additionalIgnoreFile) {
|
|
1833
|
+
const additionalIgnorePath = join18(baseDir, config.additionalIgnoreFile.path);
|
|
1834
|
+
if (await fileExists(additionalIgnorePath)) {
|
|
1835
|
+
const additionalPatterns = await config.additionalIgnoreFile.parser(additionalIgnorePath);
|
|
1836
|
+
if (additionalPatterns.length > 0) {
|
|
1837
|
+
ignorePatterns = ignorePatterns ? [...ignorePatterns, ...additionalPatterns] : additionalPatterns;
|
|
1838
|
+
}
|
|
1839
|
+
}
|
|
1840
|
+
}
|
|
1291
1841
|
} catch (error) {
|
|
1292
1842
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1293
|
-
errors.push(`Failed to parse
|
|
1843
|
+
errors.push(`Failed to parse ${config.tool} configuration: ${errorMessage}`);
|
|
1294
1844
|
}
|
|
1295
1845
|
return {
|
|
1296
1846
|
rules,
|
|
@@ -1299,7 +1849,7 @@ async function parseClaudeConfiguration(baseDir = process.cwd()) {
|
|
|
1299
1849
|
...mcpServers && { mcpServers }
|
|
1300
1850
|
};
|
|
1301
1851
|
}
|
|
1302
|
-
function
|
|
1852
|
+
function parseMainFile(content, filepath, config) {
|
|
1303
1853
|
const lines = content.split("\n");
|
|
1304
1854
|
let contentStartIndex = 0;
|
|
1305
1855
|
if (lines.some((line) => line.includes("| Document | Description | File Patterns |"))) {
|
|
@@ -1316,38 +1866,38 @@ function parseClaudeMainFile(content, filepath) {
|
|
|
1316
1866
|
}
|
|
1317
1867
|
const frontmatter = {
|
|
1318
1868
|
root: false,
|
|
1319
|
-
targets: [
|
|
1320
|
-
description:
|
|
1869
|
+
targets: [config.tool],
|
|
1870
|
+
description: config.mainDescription,
|
|
1321
1871
|
globs: ["**/*"]
|
|
1322
1872
|
};
|
|
1323
1873
|
return {
|
|
1324
1874
|
frontmatter,
|
|
1325
1875
|
content: mainContent,
|
|
1326
|
-
filename:
|
|
1876
|
+
filename: `${config.filenamePrefix}-main`,
|
|
1327
1877
|
filepath
|
|
1328
1878
|
};
|
|
1329
1879
|
}
|
|
1330
|
-
async function
|
|
1880
|
+
async function parseMemoryFiles(memoryDir, config) {
|
|
1331
1881
|
const rules = [];
|
|
1332
1882
|
try {
|
|
1333
1883
|
const { readdir: readdir2 } = await import("fs/promises");
|
|
1334
1884
|
const files = await readdir2(memoryDir);
|
|
1335
1885
|
for (const file of files) {
|
|
1336
1886
|
if (file.endsWith(".md")) {
|
|
1337
|
-
const filePath =
|
|
1887
|
+
const filePath = join18(memoryDir, file);
|
|
1338
1888
|
const content = await readFileContent(filePath);
|
|
1339
1889
|
if (content.trim()) {
|
|
1340
|
-
const filename =
|
|
1890
|
+
const filename = basename3(file, ".md");
|
|
1341
1891
|
const frontmatter = {
|
|
1342
1892
|
root: false,
|
|
1343
|
-
targets: [
|
|
1344
|
-
description:
|
|
1893
|
+
targets: [config.tool],
|
|
1894
|
+
description: `${config.memoryDescription}: ${filename}`,
|
|
1345
1895
|
globs: ["**/*"]
|
|
1346
1896
|
};
|
|
1347
1897
|
rules.push({
|
|
1348
1898
|
frontmatter,
|
|
1349
1899
|
content: content.trim(),
|
|
1350
|
-
filename:
|
|
1900
|
+
filename: `${config.filenamePrefix}-memory-${filename}`,
|
|
1351
1901
|
filepath: filePath
|
|
1352
1902
|
});
|
|
1353
1903
|
}
|
|
@@ -1357,14 +1907,14 @@ async function parseClaudeMemoryFiles(memoryDir) {
|
|
|
1357
1907
|
}
|
|
1358
1908
|
return rules;
|
|
1359
1909
|
}
|
|
1360
|
-
async function
|
|
1910
|
+
async function parseSettingsFile(settingsPath, tool) {
|
|
1361
1911
|
const errors = [];
|
|
1362
1912
|
let ignorePatterns;
|
|
1363
1913
|
let mcpServers;
|
|
1364
1914
|
try {
|
|
1365
1915
|
const content = await readFileContent(settingsPath);
|
|
1366
1916
|
const settings = JSON.parse(content);
|
|
1367
|
-
if (typeof settings === "object" && settings !== null && "permissions" in settings) {
|
|
1917
|
+
if (tool === "claudecode" && typeof settings === "object" && settings !== null && "permissions" in settings) {
|
|
1368
1918
|
const permissions = settings.permissions;
|
|
1369
1919
|
if (typeof permissions !== "object" || permissions === null) {
|
|
1370
1920
|
return { ignorePatterns: [], errors: [] };
|
|
@@ -1396,151 +1946,62 @@ async function parseClaudeSettings(settingsPath) {
|
|
|
1396
1946
|
};
|
|
1397
1947
|
}
|
|
1398
1948
|
|
|
1949
|
+
// src/parsers/claudecode.ts
|
|
1950
|
+
async function parseClaudeConfiguration(baseDir = process.cwd()) {
|
|
1951
|
+
return parseMemoryBasedConfiguration(baseDir, {
|
|
1952
|
+
tool: "claudecode",
|
|
1953
|
+
mainFileName: "CLAUDE.md",
|
|
1954
|
+
memoryDirPath: ".claude/memories",
|
|
1955
|
+
settingsPath: ".claude/settings.json",
|
|
1956
|
+
mainDescription: "Main Claude Code configuration",
|
|
1957
|
+
memoryDescription: "Memory file",
|
|
1958
|
+
filenamePrefix: "claude"
|
|
1959
|
+
});
|
|
1960
|
+
}
|
|
1961
|
+
|
|
1399
1962
|
// src/parsers/cline.ts
|
|
1400
|
-
import { join as join16 } from "path";
|
|
1401
1963
|
async function parseClineConfiguration(baseDir = process.cwd()) {
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
};
|
|
1415
|
-
rules.push({
|
|
1416
|
-
frontmatter,
|
|
1417
|
-
content: content.trim(),
|
|
1418
|
-
filename: "cline-instructions",
|
|
1419
|
-
filepath: clineFilePath
|
|
1420
|
-
});
|
|
1421
|
-
}
|
|
1422
|
-
} catch (error) {
|
|
1423
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1424
|
-
errors.push(`Failed to parse .cline/instructions.md: ${errorMessage}`);
|
|
1425
|
-
}
|
|
1426
|
-
}
|
|
1427
|
-
const clinerulesDirPath = join16(baseDir, ".clinerules");
|
|
1428
|
-
if (await fileExists(clinerulesDirPath)) {
|
|
1429
|
-
try {
|
|
1430
|
-
const { readdir: readdir2 } = await import("fs/promises");
|
|
1431
|
-
const files = await readdir2(clinerulesDirPath);
|
|
1432
|
-
for (const file of files) {
|
|
1433
|
-
if (file.endsWith(".md")) {
|
|
1434
|
-
const filePath = join16(clinerulesDirPath, file);
|
|
1435
|
-
try {
|
|
1436
|
-
const content = await readFileContent(filePath);
|
|
1437
|
-
if (content.trim()) {
|
|
1438
|
-
const filename = file.replace(".md", "");
|
|
1439
|
-
const frontmatter = {
|
|
1440
|
-
root: false,
|
|
1441
|
-
targets: ["cline"],
|
|
1442
|
-
description: `Cline rule: ${filename}`,
|
|
1443
|
-
globs: ["**/*"]
|
|
1444
|
-
};
|
|
1445
|
-
rules.push({
|
|
1446
|
-
frontmatter,
|
|
1447
|
-
content: content.trim(),
|
|
1448
|
-
filename: `cline-${filename}`,
|
|
1449
|
-
filepath: filePath
|
|
1450
|
-
});
|
|
1451
|
-
}
|
|
1452
|
-
} catch (error) {
|
|
1453
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1454
|
-
errors.push(`Failed to parse ${filePath}: ${errorMessage}`);
|
|
1455
|
-
}
|
|
1456
|
-
}
|
|
1964
|
+
return parseConfigurationFiles(baseDir, {
|
|
1965
|
+
tool: "cline",
|
|
1966
|
+
mainFile: {
|
|
1967
|
+
path: ".cline/instructions.md",
|
|
1968
|
+
useFrontmatter: false,
|
|
1969
|
+
description: "Cline instructions"
|
|
1970
|
+
},
|
|
1971
|
+
directories: [
|
|
1972
|
+
{
|
|
1973
|
+
directory: ".clinerules",
|
|
1974
|
+
filePattern: ".md",
|
|
1975
|
+
description: "Cline rule"
|
|
1457
1976
|
}
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
}
|
|
1462
|
-
}
|
|
1463
|
-
if (rules.length === 0) {
|
|
1464
|
-
errors.push("No Cline configuration files found (.cline/instructions.md or .clinerules/*.md)");
|
|
1465
|
-
}
|
|
1466
|
-
return { rules, errors };
|
|
1977
|
+
],
|
|
1978
|
+
errorMessage: "No Cline configuration files found (.cline/instructions.md or .clinerules/*.md)"
|
|
1979
|
+
});
|
|
1467
1980
|
}
|
|
1468
1981
|
|
|
1469
1982
|
// src/parsers/copilot.ts
|
|
1470
|
-
import { basename as basename3, join as join17 } from "path";
|
|
1471
|
-
import matter2 from "gray-matter";
|
|
1472
1983
|
async function parseCopilotConfiguration(baseDir = process.cwd()) {
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
description: "GitHub Copilot instructions",
|
|
1486
|
-
globs: ["**/*"]
|
|
1487
|
-
};
|
|
1488
|
-
rules.push({
|
|
1489
|
-
frontmatter,
|
|
1490
|
-
content,
|
|
1491
|
-
filename: "copilot-instructions",
|
|
1492
|
-
filepath: copilotFilePath
|
|
1493
|
-
});
|
|
1494
|
-
}
|
|
1495
|
-
} catch (error) {
|
|
1496
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1497
|
-
errors.push(`Failed to parse copilot-instructions.md: ${errorMessage}`);
|
|
1498
|
-
}
|
|
1499
|
-
}
|
|
1500
|
-
const instructionsDir = join17(baseDir, ".github", "instructions");
|
|
1501
|
-
if (await fileExists(instructionsDir)) {
|
|
1502
|
-
try {
|
|
1503
|
-
const { readdir: readdir2 } = await import("fs/promises");
|
|
1504
|
-
const files = await readdir2(instructionsDir);
|
|
1505
|
-
for (const file of files) {
|
|
1506
|
-
if (file.endsWith(".instructions.md")) {
|
|
1507
|
-
const filePath = join17(instructionsDir, file);
|
|
1508
|
-
const rawContent = await readFileContent(filePath);
|
|
1509
|
-
const parsed = matter2(rawContent);
|
|
1510
|
-
const content = parsed.content.trim();
|
|
1511
|
-
if (content) {
|
|
1512
|
-
const filename = basename3(file, ".instructions.md");
|
|
1513
|
-
const frontmatter = {
|
|
1514
|
-
root: false,
|
|
1515
|
-
targets: ["copilot"],
|
|
1516
|
-
description: `Copilot instruction: ${filename}`,
|
|
1517
|
-
globs: ["**/*"]
|
|
1518
|
-
};
|
|
1519
|
-
rules.push({
|
|
1520
|
-
frontmatter,
|
|
1521
|
-
content,
|
|
1522
|
-
filename: `copilot-${filename}`,
|
|
1523
|
-
filepath: filePath
|
|
1524
|
-
});
|
|
1525
|
-
}
|
|
1526
|
-
}
|
|
1984
|
+
return parseConfigurationFiles(baseDir, {
|
|
1985
|
+
tool: "copilot",
|
|
1986
|
+
mainFile: {
|
|
1987
|
+
path: ".github/copilot-instructions.md",
|
|
1988
|
+
useFrontmatter: true,
|
|
1989
|
+
description: "GitHub Copilot instructions"
|
|
1990
|
+
},
|
|
1991
|
+
directories: [
|
|
1992
|
+
{
|
|
1993
|
+
directory: ".github/instructions",
|
|
1994
|
+
filePattern: ".instructions.md",
|
|
1995
|
+
description: "Copilot instruction"
|
|
1527
1996
|
}
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
}
|
|
1532
|
-
}
|
|
1533
|
-
if (rules.length === 0) {
|
|
1534
|
-
errors.push(
|
|
1535
|
-
"No Copilot configuration files found (.github/copilot-instructions.md or .github/instructions/*.instructions.md)"
|
|
1536
|
-
);
|
|
1537
|
-
}
|
|
1538
|
-
return { rules, errors };
|
|
1997
|
+
],
|
|
1998
|
+
errorMessage: "No Copilot configuration files found (.github/copilot-instructions.md or .github/instructions/*.instructions.md)"
|
|
1999
|
+
});
|
|
1539
2000
|
}
|
|
1540
2001
|
|
|
1541
2002
|
// src/parsers/cursor.ts
|
|
1542
|
-
import { basename as basename4, join as
|
|
1543
|
-
import
|
|
2003
|
+
import { basename as basename4, join as join19 } from "path";
|
|
2004
|
+
import matter4 from "gray-matter";
|
|
1544
2005
|
import { DEFAULT_SCHEMA, FAILSAFE_SCHEMA, load } from "js-yaml";
|
|
1545
2006
|
import { z as z5 } from "zod/mini";
|
|
1546
2007
|
var customMatterOptions = {
|
|
@@ -1664,11 +2125,11 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
|
|
|
1664
2125
|
const rules = [];
|
|
1665
2126
|
let ignorePatterns;
|
|
1666
2127
|
let mcpServers;
|
|
1667
|
-
const cursorFilePath =
|
|
2128
|
+
const cursorFilePath = join19(baseDir, ".cursorrules");
|
|
1668
2129
|
if (await fileExists(cursorFilePath)) {
|
|
1669
2130
|
try {
|
|
1670
2131
|
const rawContent = await readFileContent(cursorFilePath);
|
|
1671
|
-
const parsed =
|
|
2132
|
+
const parsed = matter4(rawContent, customMatterOptions);
|
|
1672
2133
|
const content = parsed.content.trim();
|
|
1673
2134
|
if (content) {
|
|
1674
2135
|
const frontmatter = convertCursorMdcFrontmatter(parsed.data, "cursorrules");
|
|
@@ -1685,17 +2146,17 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
|
|
|
1685
2146
|
errors.push(`Failed to parse .cursorrules file: ${errorMessage}`);
|
|
1686
2147
|
}
|
|
1687
2148
|
}
|
|
1688
|
-
const cursorRulesDir =
|
|
2149
|
+
const cursorRulesDir = join19(baseDir, ".cursor", "rules");
|
|
1689
2150
|
if (await fileExists(cursorRulesDir)) {
|
|
1690
2151
|
try {
|
|
1691
2152
|
const { readdir: readdir2 } = await import("fs/promises");
|
|
1692
2153
|
const files = await readdir2(cursorRulesDir);
|
|
1693
2154
|
for (const file of files) {
|
|
1694
2155
|
if (file.endsWith(".mdc")) {
|
|
1695
|
-
const filePath =
|
|
2156
|
+
const filePath = join19(cursorRulesDir, file);
|
|
1696
2157
|
try {
|
|
1697
2158
|
const rawContent = await readFileContent(filePath);
|
|
1698
|
-
const parsed =
|
|
2159
|
+
const parsed = matter4(rawContent, customMatterOptions);
|
|
1699
2160
|
const content = parsed.content.trim();
|
|
1700
2161
|
if (content) {
|
|
1701
2162
|
const filename = basename4(file, ".mdc");
|
|
@@ -1721,7 +2182,7 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
|
|
|
1721
2182
|
if (rules.length === 0) {
|
|
1722
2183
|
errors.push("No Cursor configuration files found (.cursorrules or .cursor/rules/*.mdc)");
|
|
1723
2184
|
}
|
|
1724
|
-
const cursorIgnorePath =
|
|
2185
|
+
const cursorIgnorePath = join19(baseDir, ".cursorignore");
|
|
1725
2186
|
if (await fileExists(cursorIgnorePath)) {
|
|
1726
2187
|
try {
|
|
1727
2188
|
const content = await readFileContent(cursorIgnorePath);
|
|
@@ -1734,7 +2195,7 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
|
|
|
1734
2195
|
errors.push(`Failed to parse .cursorignore: ${errorMessage}`);
|
|
1735
2196
|
}
|
|
1736
2197
|
}
|
|
1737
|
-
const cursorMcpPath =
|
|
2198
|
+
const cursorMcpPath = join19(baseDir, ".cursor", "mcp.json");
|
|
1738
2199
|
if (await fileExists(cursorMcpPath)) {
|
|
1739
2200
|
try {
|
|
1740
2201
|
const content = await readFileContent(cursorMcpPath);
|
|
@@ -1757,134 +2218,6 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
|
|
|
1757
2218
|
}
|
|
1758
2219
|
|
|
1759
2220
|
// src/parsers/geminicli.ts
|
|
1760
|
-
import { basename as basename5, join as join19 } from "path";
|
|
1761
|
-
async function parseGeminiConfiguration(baseDir = process.cwd()) {
|
|
1762
|
-
const errors = [];
|
|
1763
|
-
const rules = [];
|
|
1764
|
-
let ignorePatterns;
|
|
1765
|
-
let mcpServers;
|
|
1766
|
-
const geminiFilePath = join19(baseDir, "GEMINI.md");
|
|
1767
|
-
if (!await fileExists(geminiFilePath)) {
|
|
1768
|
-
errors.push("GEMINI.md file not found");
|
|
1769
|
-
return { rules, errors };
|
|
1770
|
-
}
|
|
1771
|
-
try {
|
|
1772
|
-
const geminiContent = await readFileContent(geminiFilePath);
|
|
1773
|
-
const mainRule = parseGeminiMainFile(geminiContent, geminiFilePath);
|
|
1774
|
-
if (mainRule) {
|
|
1775
|
-
rules.push(mainRule);
|
|
1776
|
-
}
|
|
1777
|
-
const memoryDir = join19(baseDir, ".gemini", "memories");
|
|
1778
|
-
if (await fileExists(memoryDir)) {
|
|
1779
|
-
const memoryRules = await parseGeminiMemoryFiles(memoryDir);
|
|
1780
|
-
rules.push(...memoryRules);
|
|
1781
|
-
}
|
|
1782
|
-
const settingsPath = join19(baseDir, ".gemini", "settings.json");
|
|
1783
|
-
if (await fileExists(settingsPath)) {
|
|
1784
|
-
const settingsResult = await parseGeminiSettings(settingsPath);
|
|
1785
|
-
if (settingsResult.ignorePatterns) {
|
|
1786
|
-
ignorePatterns = settingsResult.ignorePatterns;
|
|
1787
|
-
}
|
|
1788
|
-
if (settingsResult.mcpServers) {
|
|
1789
|
-
mcpServers = settingsResult.mcpServers;
|
|
1790
|
-
}
|
|
1791
|
-
errors.push(...settingsResult.errors);
|
|
1792
|
-
}
|
|
1793
|
-
const aiexcludePath = join19(baseDir, ".aiexclude");
|
|
1794
|
-
if (await fileExists(aiexcludePath)) {
|
|
1795
|
-
const aiexcludePatterns = await parseAiexclude(aiexcludePath);
|
|
1796
|
-
if (aiexcludePatterns.length > 0) {
|
|
1797
|
-
ignorePatterns = ignorePatterns ? [...ignorePatterns, ...aiexcludePatterns] : aiexcludePatterns;
|
|
1798
|
-
}
|
|
1799
|
-
}
|
|
1800
|
-
} catch (error) {
|
|
1801
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1802
|
-
errors.push(`Failed to parse Gemini configuration: ${errorMessage}`);
|
|
1803
|
-
}
|
|
1804
|
-
return {
|
|
1805
|
-
rules,
|
|
1806
|
-
errors,
|
|
1807
|
-
...ignorePatterns && { ignorePatterns },
|
|
1808
|
-
...mcpServers && { mcpServers }
|
|
1809
|
-
};
|
|
1810
|
-
}
|
|
1811
|
-
function parseGeminiMainFile(content, filepath) {
|
|
1812
|
-
const lines = content.split("\n");
|
|
1813
|
-
let contentStartIndex = 0;
|
|
1814
|
-
if (lines.some((line) => line.includes("| Document | Description | File Patterns |"))) {
|
|
1815
|
-
const tableEndIndex = lines.findIndex(
|
|
1816
|
-
(line, index) => index > 0 && line.trim() === "" && lines[index - 1]?.includes("|") && !lines[index + 1]?.includes("|")
|
|
1817
|
-
);
|
|
1818
|
-
if (tableEndIndex !== -1) {
|
|
1819
|
-
contentStartIndex = tableEndIndex + 1;
|
|
1820
|
-
}
|
|
1821
|
-
}
|
|
1822
|
-
const mainContent = lines.slice(contentStartIndex).join("\n").trim();
|
|
1823
|
-
if (!mainContent) {
|
|
1824
|
-
return null;
|
|
1825
|
-
}
|
|
1826
|
-
const frontmatter = {
|
|
1827
|
-
root: false,
|
|
1828
|
-
targets: ["geminicli"],
|
|
1829
|
-
description: "Main Gemini CLI configuration",
|
|
1830
|
-
globs: ["**/*"]
|
|
1831
|
-
};
|
|
1832
|
-
return {
|
|
1833
|
-
frontmatter,
|
|
1834
|
-
content: mainContent,
|
|
1835
|
-
filename: "gemini-main",
|
|
1836
|
-
filepath
|
|
1837
|
-
};
|
|
1838
|
-
}
|
|
1839
|
-
async function parseGeminiMemoryFiles(memoryDir) {
|
|
1840
|
-
const rules = [];
|
|
1841
|
-
try {
|
|
1842
|
-
const { readdir: readdir2 } = await import("fs/promises");
|
|
1843
|
-
const files = await readdir2(memoryDir);
|
|
1844
|
-
for (const file of files) {
|
|
1845
|
-
if (file.endsWith(".md")) {
|
|
1846
|
-
const filePath = join19(memoryDir, file);
|
|
1847
|
-
const content = await readFileContent(filePath);
|
|
1848
|
-
if (content.trim()) {
|
|
1849
|
-
const filename = basename5(file, ".md");
|
|
1850
|
-
const frontmatter = {
|
|
1851
|
-
root: false,
|
|
1852
|
-
targets: ["geminicli"],
|
|
1853
|
-
description: `Memory file: ${filename}`,
|
|
1854
|
-
globs: ["**/*"]
|
|
1855
|
-
};
|
|
1856
|
-
rules.push({
|
|
1857
|
-
frontmatter,
|
|
1858
|
-
content: content.trim(),
|
|
1859
|
-
filename: `gemini-memory-${filename}`,
|
|
1860
|
-
filepath: filePath
|
|
1861
|
-
});
|
|
1862
|
-
}
|
|
1863
|
-
}
|
|
1864
|
-
}
|
|
1865
|
-
} catch {
|
|
1866
|
-
}
|
|
1867
|
-
return rules;
|
|
1868
|
-
}
|
|
1869
|
-
async function parseGeminiSettings(settingsPath) {
|
|
1870
|
-
const errors = [];
|
|
1871
|
-
let mcpServers;
|
|
1872
|
-
try {
|
|
1873
|
-
const content = await readFileContent(settingsPath);
|
|
1874
|
-
const settings = JSON.parse(content);
|
|
1875
|
-
const parseResult = RulesyncMcpConfigSchema.safeParse(settings);
|
|
1876
|
-
if (parseResult.success && Object.keys(parseResult.data.mcpServers).length > 0) {
|
|
1877
|
-
mcpServers = parseResult.data.mcpServers;
|
|
1878
|
-
}
|
|
1879
|
-
} catch (error) {
|
|
1880
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1881
|
-
errors.push(`Failed to parse settings.json: ${errorMessage}`);
|
|
1882
|
-
}
|
|
1883
|
-
return {
|
|
1884
|
-
errors,
|
|
1885
|
-
...mcpServers && { mcpServers }
|
|
1886
|
-
};
|
|
1887
|
-
}
|
|
1888
2221
|
async function parseAiexclude(aiexcludePath) {
|
|
1889
2222
|
try {
|
|
1890
2223
|
const content = await readFileContent(aiexcludePath);
|
|
@@ -1894,75 +2227,40 @@ async function parseAiexclude(aiexcludePath) {
|
|
|
1894
2227
|
return [];
|
|
1895
2228
|
}
|
|
1896
2229
|
}
|
|
2230
|
+
async function parseGeminiConfiguration(baseDir = process.cwd()) {
|
|
2231
|
+
return parseMemoryBasedConfiguration(baseDir, {
|
|
2232
|
+
tool: "geminicli",
|
|
2233
|
+
mainFileName: "GEMINI.md",
|
|
2234
|
+
memoryDirPath: ".gemini/memories",
|
|
2235
|
+
settingsPath: ".gemini/settings.json",
|
|
2236
|
+
mainDescription: "Main Gemini CLI configuration",
|
|
2237
|
+
memoryDescription: "Memory file",
|
|
2238
|
+
filenamePrefix: "gemini",
|
|
2239
|
+
additionalIgnoreFile: {
|
|
2240
|
+
path: ".aiexclude",
|
|
2241
|
+
parser: parseAiexclude
|
|
2242
|
+
}
|
|
2243
|
+
});
|
|
2244
|
+
}
|
|
1897
2245
|
|
|
1898
2246
|
// src/parsers/roo.ts
|
|
1899
|
-
import { join as join20 } from "path";
|
|
1900
2247
|
async function parseRooConfiguration(baseDir = process.cwd()) {
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
};
|
|
1914
|
-
rules.push({
|
|
1915
|
-
frontmatter,
|
|
1916
|
-
content: content.trim(),
|
|
1917
|
-
filename: "roo-instructions",
|
|
1918
|
-
filepath: rooFilePath
|
|
1919
|
-
});
|
|
1920
|
-
}
|
|
1921
|
-
} catch (error) {
|
|
1922
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1923
|
-
errors.push(`Failed to parse .roo/instructions.md: ${errorMessage}`);
|
|
1924
|
-
}
|
|
1925
|
-
}
|
|
1926
|
-
const rooRulesDir = join20(baseDir, ".roo", "rules");
|
|
1927
|
-
if (await fileExists(rooRulesDir)) {
|
|
1928
|
-
try {
|
|
1929
|
-
const { readdir: readdir2 } = await import("fs/promises");
|
|
1930
|
-
const files = await readdir2(rooRulesDir);
|
|
1931
|
-
for (const file of files) {
|
|
1932
|
-
if (file.endsWith(".md")) {
|
|
1933
|
-
const filePath = join20(rooRulesDir, file);
|
|
1934
|
-
try {
|
|
1935
|
-
const content = await readFileContent(filePath);
|
|
1936
|
-
if (content.trim()) {
|
|
1937
|
-
const filename = file.replace(".md", "");
|
|
1938
|
-
const frontmatter = {
|
|
1939
|
-
root: false,
|
|
1940
|
-
targets: ["roo"],
|
|
1941
|
-
description: `Roo rule: ${filename}`,
|
|
1942
|
-
globs: ["**/*"]
|
|
1943
|
-
};
|
|
1944
|
-
rules.push({
|
|
1945
|
-
frontmatter,
|
|
1946
|
-
content: content.trim(),
|
|
1947
|
-
filename: `roo-${filename}`,
|
|
1948
|
-
filepath: filePath
|
|
1949
|
-
});
|
|
1950
|
-
}
|
|
1951
|
-
} catch (error) {
|
|
1952
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1953
|
-
errors.push(`Failed to parse ${filePath}: ${errorMessage}`);
|
|
1954
|
-
}
|
|
1955
|
-
}
|
|
2248
|
+
return parseConfigurationFiles(baseDir, {
|
|
2249
|
+
tool: "roo",
|
|
2250
|
+
mainFile: {
|
|
2251
|
+
path: ".roo/instructions.md",
|
|
2252
|
+
useFrontmatter: false,
|
|
2253
|
+
description: "Roo Code instructions"
|
|
2254
|
+
},
|
|
2255
|
+
directories: [
|
|
2256
|
+
{
|
|
2257
|
+
directory: ".roo/rules",
|
|
2258
|
+
filePattern: ".md",
|
|
2259
|
+
description: "Roo rule"
|
|
1956
2260
|
}
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
}
|
|
1961
|
-
}
|
|
1962
|
-
if (rules.length === 0) {
|
|
1963
|
-
errors.push("No Roo Code configuration files found (.roo/instructions.md or .roo/rules/*.md)");
|
|
1964
|
-
}
|
|
1965
|
-
return { rules, errors };
|
|
2261
|
+
],
|
|
2262
|
+
errorMessage: "No Roo Code configuration files found (.roo/instructions.md or .roo/rules/*.md)"
|
|
2263
|
+
});
|
|
1966
2264
|
}
|
|
1967
2265
|
|
|
1968
2266
|
// src/core/importer.ts
|
|
@@ -1977,6 +2275,18 @@ async function importConfiguration(options) {
|
|
|
1977
2275
|
}
|
|
1978
2276
|
try {
|
|
1979
2277
|
switch (tool) {
|
|
2278
|
+
case "augmentcode": {
|
|
2279
|
+
const augmentResult = await parseAugmentcodeConfiguration(baseDir);
|
|
2280
|
+
rules = augmentResult.rules;
|
|
2281
|
+
errors.push(...augmentResult.errors);
|
|
2282
|
+
break;
|
|
2283
|
+
}
|
|
2284
|
+
case "augmentcode-legacy": {
|
|
2285
|
+
const augmentLegacyResult = await parseAugmentcodeLegacyConfiguration(baseDir);
|
|
2286
|
+
rules = augmentLegacyResult.rules;
|
|
2287
|
+
errors.push(...augmentLegacyResult.errors);
|
|
2288
|
+
break;
|
|
2289
|
+
}
|
|
1980
2290
|
case "claudecode": {
|
|
1981
2291
|
const claudeResult = await parseClaudeConfiguration(baseDir);
|
|
1982
2292
|
rules = claudeResult.rules;
|
|
@@ -2031,7 +2341,7 @@ async function importConfiguration(options) {
|
|
|
2031
2341
|
if (rules.length === 0 && !ignorePatterns && !mcpServers) {
|
|
2032
2342
|
return { success: false, rulesCreated: 0, errors };
|
|
2033
2343
|
}
|
|
2034
|
-
const rulesDirPath =
|
|
2344
|
+
const rulesDirPath = join20(baseDir, rulesDir);
|
|
2035
2345
|
try {
|
|
2036
2346
|
const { mkdir: mkdir3 } = await import("fs/promises");
|
|
2037
2347
|
await mkdir3(rulesDirPath, { recursive: true });
|
|
@@ -2045,7 +2355,7 @@ async function importConfiguration(options) {
|
|
|
2045
2355
|
try {
|
|
2046
2356
|
const baseFilename = `${tool}__${rule.filename}`;
|
|
2047
2357
|
const filename = await generateUniqueFilename(rulesDirPath, baseFilename);
|
|
2048
|
-
const filePath =
|
|
2358
|
+
const filePath = join20(rulesDirPath, `${filename}.md`);
|
|
2049
2359
|
const content = generateRuleFileContent(rule);
|
|
2050
2360
|
await writeFileContent(filePath, content);
|
|
2051
2361
|
rulesCreated++;
|
|
@@ -2060,7 +2370,7 @@ async function importConfiguration(options) {
|
|
|
2060
2370
|
let ignoreFileCreated = false;
|
|
2061
2371
|
if (ignorePatterns && ignorePatterns.length > 0) {
|
|
2062
2372
|
try {
|
|
2063
|
-
const rulesyncignorePath =
|
|
2373
|
+
const rulesyncignorePath = join20(baseDir, ".rulesyncignore");
|
|
2064
2374
|
const ignoreContent = `${ignorePatterns.join("\n")}
|
|
2065
2375
|
`;
|
|
2066
2376
|
await writeFileContent(rulesyncignorePath, ignoreContent);
|
|
@@ -2076,7 +2386,7 @@ async function importConfiguration(options) {
|
|
|
2076
2386
|
let mcpFileCreated = false;
|
|
2077
2387
|
if (mcpServers && Object.keys(mcpServers).length > 0) {
|
|
2078
2388
|
try {
|
|
2079
|
-
const mcpPath =
|
|
2389
|
+
const mcpPath = join20(baseDir, rulesDir, ".mcp.json");
|
|
2080
2390
|
const mcpContent = `${JSON.stringify({ mcpServers }, null, 2)}
|
|
2081
2391
|
`;
|
|
2082
2392
|
await writeFileContent(mcpPath, mcpContent);
|
|
@@ -2098,13 +2408,13 @@ async function importConfiguration(options) {
|
|
|
2098
2408
|
};
|
|
2099
2409
|
}
|
|
2100
2410
|
function generateRuleFileContent(rule) {
|
|
2101
|
-
const frontmatter =
|
|
2411
|
+
const frontmatter = matter5.stringify("", rule.frontmatter);
|
|
2102
2412
|
return frontmatter + rule.content;
|
|
2103
2413
|
}
|
|
2104
2414
|
async function generateUniqueFilename(rulesDir, baseFilename) {
|
|
2105
2415
|
let filename = baseFilename;
|
|
2106
2416
|
let counter = 1;
|
|
2107
|
-
while (await fileExists(
|
|
2417
|
+
while (await fileExists(join20(rulesDir, `${filename}.md`))) {
|
|
2108
2418
|
filename = `${baseFilename}-${counter}`;
|
|
2109
2419
|
counter++;
|
|
2110
2420
|
}
|
|
@@ -2114,6 +2424,8 @@ async function generateUniqueFilename(rulesDir, baseFilename) {
|
|
|
2114
2424
|
// src/cli/commands/import.ts
|
|
2115
2425
|
async function importCommand(options = {}) {
|
|
2116
2426
|
const tools = [];
|
|
2427
|
+
if (options.augmentcode) tools.push("augmentcode");
|
|
2428
|
+
if (options.augmentcodeLegacy) tools.push("augmentcode-legacy");
|
|
2117
2429
|
if (options.claudecode) tools.push("claudecode");
|
|
2118
2430
|
if (options.cursor) tools.push("cursor");
|
|
2119
2431
|
if (options.copilot) tools.push("copilot");
|
|
@@ -2122,7 +2434,7 @@ async function importCommand(options = {}) {
|
|
|
2122
2434
|
if (options.geminicli) tools.push("geminicli");
|
|
2123
2435
|
if (tools.length === 0) {
|
|
2124
2436
|
console.error(
|
|
2125
|
-
"\u274C Please specify one tool to import from (--claudecode, --cursor, --copilot, --cline, --roo, --geminicli)"
|
|
2437
|
+
"\u274C Please specify one tool to import from (--augmentcode, --augmentcodeLegacy, --claudecode, --cursor, --copilot, --cline, --roo, --geminicli)"
|
|
2126
2438
|
);
|
|
2127
2439
|
process.exit(1);
|
|
2128
2440
|
}
|
|
@@ -2169,7 +2481,7 @@ async function importCommand(options = {}) {
|
|
|
2169
2481
|
}
|
|
2170
2482
|
|
|
2171
2483
|
// src/cli/commands/init.ts
|
|
2172
|
-
import { join as
|
|
2484
|
+
import { join as join21 } from "path";
|
|
2173
2485
|
async function initCommand() {
|
|
2174
2486
|
const aiRulesDir = ".rulesync";
|
|
2175
2487
|
console.log("Initializing rulesync...");
|
|
@@ -2216,7 +2528,7 @@ globs: ["**/*"]
|
|
|
2216
2528
|
- Follow single responsibility principle
|
|
2217
2529
|
`
|
|
2218
2530
|
};
|
|
2219
|
-
const filepath =
|
|
2531
|
+
const filepath = join21(aiRulesDir, sampleFile.filename);
|
|
2220
2532
|
if (!await fileExists(filepath)) {
|
|
2221
2533
|
await writeFileContent(filepath, sampleFile.content);
|
|
2222
2534
|
console.log(`Created ${filepath}`);
|
|
@@ -2360,16 +2672,18 @@ async function watchCommand() {
|
|
|
2360
2672
|
|
|
2361
2673
|
// src/cli/index.ts
|
|
2362
2674
|
var program = new Command();
|
|
2363
|
-
program.name("rulesync").description("Unified AI rules management CLI tool").version("0.
|
|
2675
|
+
program.name("rulesync").description("Unified AI rules management CLI tool").version("0.49.0");
|
|
2364
2676
|
program.command("init").description("Initialize rulesync in current directory").action(initCommand);
|
|
2365
2677
|
program.command("add <filename>").description("Add a new rule file").action(addCommand);
|
|
2366
2678
|
program.command("gitignore").description("Add generated files to .gitignore").action(gitignoreCommand);
|
|
2367
|
-
program.command("import").description("Import configurations from AI tools to rulesync format").option("--claudecode", "Import from Claude Code (CLAUDE.md)").option("--cursor", "Import from Cursor (.cursorrules)").option("--copilot", "Import from GitHub Copilot (.github/copilot-instructions.md)").option("--cline", "Import from Cline (.cline/instructions.md)").option("--roo", "Import from Roo Code (.roo/instructions.md)").option("--geminicli", "Import from Gemini CLI (GEMINI.md)").option("-v, --verbose", "Verbose output").action(importCommand);
|
|
2368
|
-
program.command("generate").description("Generate configuration files for AI tools").option("--copilot", "Generate only for GitHub Copilot").option("--cursor", "Generate only for Cursor").option("--cline", "Generate only for Cline").option("--claudecode", "Generate only for Claude Code").option("--roo", "Generate only for Roo Code").option("--geminicli", "Generate only for Gemini CLI").option("--kiro", "Generate only for Kiro IDE").option("--delete", "Delete all existing files in output directories before generating").option(
|
|
2679
|
+
program.command("import").description("Import configurations from AI tools to rulesync format").option("--augmentcode", "Import from AugmentCode (.augment/rules/)").option("--augmentcodeLegacy", "Import from AugmentCode legacy format (.augment-guidelines)").option("--claudecode", "Import from Claude Code (CLAUDE.md)").option("--cursor", "Import from Cursor (.cursorrules)").option("--copilot", "Import from GitHub Copilot (.github/copilot-instructions.md)").option("--cline", "Import from Cline (.cline/instructions.md)").option("--roo", "Import from Roo Code (.roo/instructions.md)").option("--geminicli", "Import from Gemini CLI (GEMINI.md)").option("-v, --verbose", "Verbose output").action(importCommand);
|
|
2680
|
+
program.command("generate").description("Generate configuration files for AI tools").option("--augmentcode", "Generate only for AugmentCode").option("--augmentcodeLegacy", "Generate only for AugmentCode legacy format").option("--copilot", "Generate only for GitHub Copilot").option("--cursor", "Generate only for Cursor").option("--cline", "Generate only for Cline").option("--claudecode", "Generate only for Claude Code").option("--roo", "Generate only for Roo Code").option("--geminicli", "Generate only for Gemini CLI").option("--kiro", "Generate only for Kiro IDE").option("--delete", "Delete all existing files in output directories before generating").option(
|
|
2369
2681
|
"-b, --base-dir <paths>",
|
|
2370
2682
|
"Base directories to generate files (comma-separated for multiple paths)"
|
|
2371
2683
|
).option("-v, --verbose", "Verbose output").action(async (options) => {
|
|
2372
2684
|
const tools = [];
|
|
2685
|
+
if (options.augmentcode) tools.push("augmentcode");
|
|
2686
|
+
if (options.augmentcodeLegacy) tools.push("augmentcode-legacy");
|
|
2373
2687
|
if (options.copilot) tools.push("copilot");
|
|
2374
2688
|
if (options.cursor) tools.push("cursor");
|
|
2375
2689
|
if (options.cline) tools.push("cline");
|