rulesync 0.48.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 +913 -549
- package/dist/index.js +880 -573
- 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,
|
|
@@ -1200,9 +1493,9 @@ Generating configurations for base directory: ${baseDir}`);
|
|
|
1200
1493
|
|
|
1201
1494
|
// src/cli/commands/gitignore.ts
|
|
1202
1495
|
import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync } from "fs";
|
|
1203
|
-
import { join as
|
|
1496
|
+
import { join as join15 } from "path";
|
|
1204
1497
|
var gitignoreCommand = async () => {
|
|
1205
|
-
const gitignorePath =
|
|
1498
|
+
const gitignorePath = join15(process.cwd(), ".gitignore");
|
|
1206
1499
|
const rulesFilesToIgnore = [
|
|
1207
1500
|
"# Generated by rulesync - AI tool configuration files",
|
|
1208
1501
|
"**/.github/copilot-instructions.md",
|
|
@@ -1220,7 +1513,10 @@ var gitignoreCommand = async () => {
|
|
|
1220
1513
|
"**/.gemini/memories/",
|
|
1221
1514
|
"**/.aiexclude",
|
|
1222
1515
|
"**/.aiignore",
|
|
1516
|
+
"**/.augmentignore",
|
|
1223
1517
|
"**/.kiro/steering/",
|
|
1518
|
+
"**/.augment/rules/",
|
|
1519
|
+
"**/.augment-guidelines",
|
|
1224
1520
|
"**/.mcp.json",
|
|
1225
1521
|
"!.rulesync/.mcp.json",
|
|
1226
1522
|
"**/.cursor/mcp.json",
|
|
@@ -1239,54 +1535,292 @@ var gitignoreCommand = async () => {
|
|
|
1239
1535
|
linesToAdd.push(rule);
|
|
1240
1536
|
}
|
|
1241
1537
|
}
|
|
1242
|
-
if (linesToAdd.length === 0) {
|
|
1243
|
-
console.log("\u2705 .gitignore is already up to date");
|
|
1244
|
-
return;
|
|
1245
|
-
}
|
|
1246
|
-
const newContent = gitignoreContent ? `${gitignoreContent.trimEnd()}
|
|
1247
|
-
|
|
1248
|
-
${linesToAdd.join("\n")}
|
|
1249
|
-
` : `${linesToAdd.join("\n")}
|
|
1250
|
-
`;
|
|
1251
|
-
writeFileSync(gitignorePath, newContent);
|
|
1252
|
-
console.log(`\u2705 Added ${linesToAdd.length} rules to .gitignore:`);
|
|
1253
|
-
for (const line of linesToAdd) {
|
|
1254
|
-
if (!line.startsWith("#")) {
|
|
1255
|
-
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
|
+
}
|
|
1256
1793
|
}
|
|
1257
1794
|
}
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
// src/parsers/claudecode.ts
|
|
1265
|
-
import { basename as basename2, join as join15 } from "path";
|
|
1266
|
-
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) {
|
|
1267
1801
|
const errors = [];
|
|
1268
1802
|
const rules = [];
|
|
1269
1803
|
let ignorePatterns;
|
|
1270
1804
|
let mcpServers;
|
|
1271
|
-
const
|
|
1272
|
-
if (!await fileExists(
|
|
1273
|
-
errors.push(
|
|
1805
|
+
const mainFilePath = join18(baseDir, config.mainFileName);
|
|
1806
|
+
if (!await fileExists(mainFilePath)) {
|
|
1807
|
+
errors.push(`${config.mainFileName} file not found`);
|
|
1274
1808
|
return { rules, errors };
|
|
1275
1809
|
}
|
|
1276
1810
|
try {
|
|
1277
|
-
const
|
|
1278
|
-
const mainRule =
|
|
1811
|
+
const mainContent = await readFileContent(mainFilePath);
|
|
1812
|
+
const mainRule = parseMainFile(mainContent, mainFilePath, config);
|
|
1279
1813
|
if (mainRule) {
|
|
1280
1814
|
rules.push(mainRule);
|
|
1281
1815
|
}
|
|
1282
|
-
const memoryDir =
|
|
1816
|
+
const memoryDir = join18(baseDir, config.memoryDirPath);
|
|
1283
1817
|
if (await fileExists(memoryDir)) {
|
|
1284
|
-
const memoryRules = await
|
|
1818
|
+
const memoryRules = await parseMemoryFiles(memoryDir, config);
|
|
1285
1819
|
rules.push(...memoryRules);
|
|
1286
1820
|
}
|
|
1287
|
-
const settingsPath =
|
|
1821
|
+
const settingsPath = join18(baseDir, config.settingsPath);
|
|
1288
1822
|
if (await fileExists(settingsPath)) {
|
|
1289
|
-
const settingsResult = await
|
|
1823
|
+
const settingsResult = await parseSettingsFile(settingsPath, config.tool);
|
|
1290
1824
|
if (settingsResult.ignorePatterns) {
|
|
1291
1825
|
ignorePatterns = settingsResult.ignorePatterns;
|
|
1292
1826
|
}
|
|
@@ -1295,9 +1829,18 @@ async function parseClaudeConfiguration(baseDir = process.cwd()) {
|
|
|
1295
1829
|
}
|
|
1296
1830
|
errors.push(...settingsResult.errors);
|
|
1297
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
|
+
}
|
|
1298
1841
|
} catch (error) {
|
|
1299
1842
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1300
|
-
errors.push(`Failed to parse
|
|
1843
|
+
errors.push(`Failed to parse ${config.tool} configuration: ${errorMessage}`);
|
|
1301
1844
|
}
|
|
1302
1845
|
return {
|
|
1303
1846
|
rules,
|
|
@@ -1306,7 +1849,7 @@ async function parseClaudeConfiguration(baseDir = process.cwd()) {
|
|
|
1306
1849
|
...mcpServers && { mcpServers }
|
|
1307
1850
|
};
|
|
1308
1851
|
}
|
|
1309
|
-
function
|
|
1852
|
+
function parseMainFile(content, filepath, config) {
|
|
1310
1853
|
const lines = content.split("\n");
|
|
1311
1854
|
let contentStartIndex = 0;
|
|
1312
1855
|
if (lines.some((line) => line.includes("| Document | Description | File Patterns |"))) {
|
|
@@ -1323,38 +1866,38 @@ function parseClaudeMainFile(content, filepath) {
|
|
|
1323
1866
|
}
|
|
1324
1867
|
const frontmatter = {
|
|
1325
1868
|
root: false,
|
|
1326
|
-
targets: [
|
|
1327
|
-
description:
|
|
1869
|
+
targets: [config.tool],
|
|
1870
|
+
description: config.mainDescription,
|
|
1328
1871
|
globs: ["**/*"]
|
|
1329
1872
|
};
|
|
1330
1873
|
return {
|
|
1331
1874
|
frontmatter,
|
|
1332
1875
|
content: mainContent,
|
|
1333
|
-
filename:
|
|
1876
|
+
filename: `${config.filenamePrefix}-main`,
|
|
1334
1877
|
filepath
|
|
1335
1878
|
};
|
|
1336
1879
|
}
|
|
1337
|
-
async function
|
|
1880
|
+
async function parseMemoryFiles(memoryDir, config) {
|
|
1338
1881
|
const rules = [];
|
|
1339
1882
|
try {
|
|
1340
1883
|
const { readdir: readdir2 } = await import("fs/promises");
|
|
1341
1884
|
const files = await readdir2(memoryDir);
|
|
1342
1885
|
for (const file of files) {
|
|
1343
1886
|
if (file.endsWith(".md")) {
|
|
1344
|
-
const filePath =
|
|
1887
|
+
const filePath = join18(memoryDir, file);
|
|
1345
1888
|
const content = await readFileContent(filePath);
|
|
1346
1889
|
if (content.trim()) {
|
|
1347
|
-
const filename =
|
|
1890
|
+
const filename = basename3(file, ".md");
|
|
1348
1891
|
const frontmatter = {
|
|
1349
1892
|
root: false,
|
|
1350
|
-
targets: [
|
|
1351
|
-
description:
|
|
1893
|
+
targets: [config.tool],
|
|
1894
|
+
description: `${config.memoryDescription}: ${filename}`,
|
|
1352
1895
|
globs: ["**/*"]
|
|
1353
1896
|
};
|
|
1354
1897
|
rules.push({
|
|
1355
1898
|
frontmatter,
|
|
1356
1899
|
content: content.trim(),
|
|
1357
|
-
filename:
|
|
1900
|
+
filename: `${config.filenamePrefix}-memory-${filename}`,
|
|
1358
1901
|
filepath: filePath
|
|
1359
1902
|
});
|
|
1360
1903
|
}
|
|
@@ -1364,14 +1907,14 @@ async function parseClaudeMemoryFiles(memoryDir) {
|
|
|
1364
1907
|
}
|
|
1365
1908
|
return rules;
|
|
1366
1909
|
}
|
|
1367
|
-
async function
|
|
1910
|
+
async function parseSettingsFile(settingsPath, tool) {
|
|
1368
1911
|
const errors = [];
|
|
1369
1912
|
let ignorePatterns;
|
|
1370
1913
|
let mcpServers;
|
|
1371
1914
|
try {
|
|
1372
1915
|
const content = await readFileContent(settingsPath);
|
|
1373
1916
|
const settings = JSON.parse(content);
|
|
1374
|
-
if (typeof settings === "object" && settings !== null && "permissions" in settings) {
|
|
1917
|
+
if (tool === "claudecode" && typeof settings === "object" && settings !== null && "permissions" in settings) {
|
|
1375
1918
|
const permissions = settings.permissions;
|
|
1376
1919
|
if (typeof permissions !== "object" || permissions === null) {
|
|
1377
1920
|
return { ignorePatterns: [], errors: [] };
|
|
@@ -1403,151 +1946,62 @@ async function parseClaudeSettings(settingsPath) {
|
|
|
1403
1946
|
};
|
|
1404
1947
|
}
|
|
1405
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
|
+
|
|
1406
1962
|
// src/parsers/cline.ts
|
|
1407
|
-
import { join as join16 } from "path";
|
|
1408
1963
|
async function parseClineConfiguration(baseDir = process.cwd()) {
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
};
|
|
1422
|
-
rules.push({
|
|
1423
|
-
frontmatter,
|
|
1424
|
-
content: content.trim(),
|
|
1425
|
-
filename: "cline-instructions",
|
|
1426
|
-
filepath: clineFilePath
|
|
1427
|
-
});
|
|
1428
|
-
}
|
|
1429
|
-
} catch (error) {
|
|
1430
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1431
|
-
errors.push(`Failed to parse .cline/instructions.md: ${errorMessage}`);
|
|
1432
|
-
}
|
|
1433
|
-
}
|
|
1434
|
-
const clinerulesDirPath = join16(baseDir, ".clinerules");
|
|
1435
|
-
if (await fileExists(clinerulesDirPath)) {
|
|
1436
|
-
try {
|
|
1437
|
-
const { readdir: readdir2 } = await import("fs/promises");
|
|
1438
|
-
const files = await readdir2(clinerulesDirPath);
|
|
1439
|
-
for (const file of files) {
|
|
1440
|
-
if (file.endsWith(".md")) {
|
|
1441
|
-
const filePath = join16(clinerulesDirPath, file);
|
|
1442
|
-
try {
|
|
1443
|
-
const content = await readFileContent(filePath);
|
|
1444
|
-
if (content.trim()) {
|
|
1445
|
-
const filename = file.replace(".md", "");
|
|
1446
|
-
const frontmatter = {
|
|
1447
|
-
root: false,
|
|
1448
|
-
targets: ["cline"],
|
|
1449
|
-
description: `Cline rule: ${filename}`,
|
|
1450
|
-
globs: ["**/*"]
|
|
1451
|
-
};
|
|
1452
|
-
rules.push({
|
|
1453
|
-
frontmatter,
|
|
1454
|
-
content: content.trim(),
|
|
1455
|
-
filename: `cline-${filename}`,
|
|
1456
|
-
filepath: filePath
|
|
1457
|
-
});
|
|
1458
|
-
}
|
|
1459
|
-
} catch (error) {
|
|
1460
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1461
|
-
errors.push(`Failed to parse ${filePath}: ${errorMessage}`);
|
|
1462
|
-
}
|
|
1463
|
-
}
|
|
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"
|
|
1464
1976
|
}
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
}
|
|
1469
|
-
}
|
|
1470
|
-
if (rules.length === 0) {
|
|
1471
|
-
errors.push("No Cline configuration files found (.cline/instructions.md or .clinerules/*.md)");
|
|
1472
|
-
}
|
|
1473
|
-
return { rules, errors };
|
|
1977
|
+
],
|
|
1978
|
+
errorMessage: "No Cline configuration files found (.cline/instructions.md or .clinerules/*.md)"
|
|
1979
|
+
});
|
|
1474
1980
|
}
|
|
1475
1981
|
|
|
1476
1982
|
// src/parsers/copilot.ts
|
|
1477
|
-
import { basename as basename3, join as join17 } from "path";
|
|
1478
|
-
import matter2 from "gray-matter";
|
|
1479
1983
|
async function parseCopilotConfiguration(baseDir = process.cwd()) {
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
description: "GitHub Copilot instructions",
|
|
1493
|
-
globs: ["**/*"]
|
|
1494
|
-
};
|
|
1495
|
-
rules.push({
|
|
1496
|
-
frontmatter,
|
|
1497
|
-
content,
|
|
1498
|
-
filename: "copilot-instructions",
|
|
1499
|
-
filepath: copilotFilePath
|
|
1500
|
-
});
|
|
1501
|
-
}
|
|
1502
|
-
} catch (error) {
|
|
1503
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1504
|
-
errors.push(`Failed to parse copilot-instructions.md: ${errorMessage}`);
|
|
1505
|
-
}
|
|
1506
|
-
}
|
|
1507
|
-
const instructionsDir = join17(baseDir, ".github", "instructions");
|
|
1508
|
-
if (await fileExists(instructionsDir)) {
|
|
1509
|
-
try {
|
|
1510
|
-
const { readdir: readdir2 } = await import("fs/promises");
|
|
1511
|
-
const files = await readdir2(instructionsDir);
|
|
1512
|
-
for (const file of files) {
|
|
1513
|
-
if (file.endsWith(".instructions.md")) {
|
|
1514
|
-
const filePath = join17(instructionsDir, file);
|
|
1515
|
-
const rawContent = await readFileContent(filePath);
|
|
1516
|
-
const parsed = matter2(rawContent);
|
|
1517
|
-
const content = parsed.content.trim();
|
|
1518
|
-
if (content) {
|
|
1519
|
-
const filename = basename3(file, ".instructions.md");
|
|
1520
|
-
const frontmatter = {
|
|
1521
|
-
root: false,
|
|
1522
|
-
targets: ["copilot"],
|
|
1523
|
-
description: `Copilot instruction: ${filename}`,
|
|
1524
|
-
globs: ["**/*"]
|
|
1525
|
-
};
|
|
1526
|
-
rules.push({
|
|
1527
|
-
frontmatter,
|
|
1528
|
-
content,
|
|
1529
|
-
filename: `copilot-${filename}`,
|
|
1530
|
-
filepath: filePath
|
|
1531
|
-
});
|
|
1532
|
-
}
|
|
1533
|
-
}
|
|
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"
|
|
1534
1996
|
}
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
}
|
|
1539
|
-
}
|
|
1540
|
-
if (rules.length === 0) {
|
|
1541
|
-
errors.push(
|
|
1542
|
-
"No Copilot configuration files found (.github/copilot-instructions.md or .github/instructions/*.instructions.md)"
|
|
1543
|
-
);
|
|
1544
|
-
}
|
|
1545
|
-
return { rules, errors };
|
|
1997
|
+
],
|
|
1998
|
+
errorMessage: "No Copilot configuration files found (.github/copilot-instructions.md or .github/instructions/*.instructions.md)"
|
|
1999
|
+
});
|
|
1546
2000
|
}
|
|
1547
2001
|
|
|
1548
2002
|
// src/parsers/cursor.ts
|
|
1549
|
-
import { basename as basename4, join as
|
|
1550
|
-
import
|
|
2003
|
+
import { basename as basename4, join as join19 } from "path";
|
|
2004
|
+
import matter4 from "gray-matter";
|
|
1551
2005
|
import { DEFAULT_SCHEMA, FAILSAFE_SCHEMA, load } from "js-yaml";
|
|
1552
2006
|
import { z as z5 } from "zod/mini";
|
|
1553
2007
|
var customMatterOptions = {
|
|
@@ -1671,11 +2125,11 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
|
|
|
1671
2125
|
const rules = [];
|
|
1672
2126
|
let ignorePatterns;
|
|
1673
2127
|
let mcpServers;
|
|
1674
|
-
const cursorFilePath =
|
|
2128
|
+
const cursorFilePath = join19(baseDir, ".cursorrules");
|
|
1675
2129
|
if (await fileExists(cursorFilePath)) {
|
|
1676
2130
|
try {
|
|
1677
2131
|
const rawContent = await readFileContent(cursorFilePath);
|
|
1678
|
-
const parsed =
|
|
2132
|
+
const parsed = matter4(rawContent, customMatterOptions);
|
|
1679
2133
|
const content = parsed.content.trim();
|
|
1680
2134
|
if (content) {
|
|
1681
2135
|
const frontmatter = convertCursorMdcFrontmatter(parsed.data, "cursorrules");
|
|
@@ -1692,17 +2146,17 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
|
|
|
1692
2146
|
errors.push(`Failed to parse .cursorrules file: ${errorMessage}`);
|
|
1693
2147
|
}
|
|
1694
2148
|
}
|
|
1695
|
-
const cursorRulesDir =
|
|
2149
|
+
const cursorRulesDir = join19(baseDir, ".cursor", "rules");
|
|
1696
2150
|
if (await fileExists(cursorRulesDir)) {
|
|
1697
2151
|
try {
|
|
1698
2152
|
const { readdir: readdir2 } = await import("fs/promises");
|
|
1699
2153
|
const files = await readdir2(cursorRulesDir);
|
|
1700
2154
|
for (const file of files) {
|
|
1701
2155
|
if (file.endsWith(".mdc")) {
|
|
1702
|
-
const filePath =
|
|
2156
|
+
const filePath = join19(cursorRulesDir, file);
|
|
1703
2157
|
try {
|
|
1704
2158
|
const rawContent = await readFileContent(filePath);
|
|
1705
|
-
const parsed =
|
|
2159
|
+
const parsed = matter4(rawContent, customMatterOptions);
|
|
1706
2160
|
const content = parsed.content.trim();
|
|
1707
2161
|
if (content) {
|
|
1708
2162
|
const filename = basename4(file, ".mdc");
|
|
@@ -1728,7 +2182,7 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
|
|
|
1728
2182
|
if (rules.length === 0) {
|
|
1729
2183
|
errors.push("No Cursor configuration files found (.cursorrules or .cursor/rules/*.mdc)");
|
|
1730
2184
|
}
|
|
1731
|
-
const cursorIgnorePath =
|
|
2185
|
+
const cursorIgnorePath = join19(baseDir, ".cursorignore");
|
|
1732
2186
|
if (await fileExists(cursorIgnorePath)) {
|
|
1733
2187
|
try {
|
|
1734
2188
|
const content = await readFileContent(cursorIgnorePath);
|
|
@@ -1741,7 +2195,7 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
|
|
|
1741
2195
|
errors.push(`Failed to parse .cursorignore: ${errorMessage}`);
|
|
1742
2196
|
}
|
|
1743
2197
|
}
|
|
1744
|
-
const cursorMcpPath =
|
|
2198
|
+
const cursorMcpPath = join19(baseDir, ".cursor", "mcp.json");
|
|
1745
2199
|
if (await fileExists(cursorMcpPath)) {
|
|
1746
2200
|
try {
|
|
1747
2201
|
const content = await readFileContent(cursorMcpPath);
|
|
@@ -1764,134 +2218,6 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
|
|
|
1764
2218
|
}
|
|
1765
2219
|
|
|
1766
2220
|
// src/parsers/geminicli.ts
|
|
1767
|
-
import { basename as basename5, join as join19 } from "path";
|
|
1768
|
-
async function parseGeminiConfiguration(baseDir = process.cwd()) {
|
|
1769
|
-
const errors = [];
|
|
1770
|
-
const rules = [];
|
|
1771
|
-
let ignorePatterns;
|
|
1772
|
-
let mcpServers;
|
|
1773
|
-
const geminiFilePath = join19(baseDir, "GEMINI.md");
|
|
1774
|
-
if (!await fileExists(geminiFilePath)) {
|
|
1775
|
-
errors.push("GEMINI.md file not found");
|
|
1776
|
-
return { rules, errors };
|
|
1777
|
-
}
|
|
1778
|
-
try {
|
|
1779
|
-
const geminiContent = await readFileContent(geminiFilePath);
|
|
1780
|
-
const mainRule = parseGeminiMainFile(geminiContent, geminiFilePath);
|
|
1781
|
-
if (mainRule) {
|
|
1782
|
-
rules.push(mainRule);
|
|
1783
|
-
}
|
|
1784
|
-
const memoryDir = join19(baseDir, ".gemini", "memories");
|
|
1785
|
-
if (await fileExists(memoryDir)) {
|
|
1786
|
-
const memoryRules = await parseGeminiMemoryFiles(memoryDir);
|
|
1787
|
-
rules.push(...memoryRules);
|
|
1788
|
-
}
|
|
1789
|
-
const settingsPath = join19(baseDir, ".gemini", "settings.json");
|
|
1790
|
-
if (await fileExists(settingsPath)) {
|
|
1791
|
-
const settingsResult = await parseGeminiSettings(settingsPath);
|
|
1792
|
-
if (settingsResult.ignorePatterns) {
|
|
1793
|
-
ignorePatterns = settingsResult.ignorePatterns;
|
|
1794
|
-
}
|
|
1795
|
-
if (settingsResult.mcpServers) {
|
|
1796
|
-
mcpServers = settingsResult.mcpServers;
|
|
1797
|
-
}
|
|
1798
|
-
errors.push(...settingsResult.errors);
|
|
1799
|
-
}
|
|
1800
|
-
const aiexcludePath = join19(baseDir, ".aiexclude");
|
|
1801
|
-
if (await fileExists(aiexcludePath)) {
|
|
1802
|
-
const aiexcludePatterns = await parseAiexclude(aiexcludePath);
|
|
1803
|
-
if (aiexcludePatterns.length > 0) {
|
|
1804
|
-
ignorePatterns = ignorePatterns ? [...ignorePatterns, ...aiexcludePatterns] : aiexcludePatterns;
|
|
1805
|
-
}
|
|
1806
|
-
}
|
|
1807
|
-
} catch (error) {
|
|
1808
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1809
|
-
errors.push(`Failed to parse Gemini configuration: ${errorMessage}`);
|
|
1810
|
-
}
|
|
1811
|
-
return {
|
|
1812
|
-
rules,
|
|
1813
|
-
errors,
|
|
1814
|
-
...ignorePatterns && { ignorePatterns },
|
|
1815
|
-
...mcpServers && { mcpServers }
|
|
1816
|
-
};
|
|
1817
|
-
}
|
|
1818
|
-
function parseGeminiMainFile(content, filepath) {
|
|
1819
|
-
const lines = content.split("\n");
|
|
1820
|
-
let contentStartIndex = 0;
|
|
1821
|
-
if (lines.some((line) => line.includes("| Document | Description | File Patterns |"))) {
|
|
1822
|
-
const tableEndIndex = lines.findIndex(
|
|
1823
|
-
(line, index) => index > 0 && line.trim() === "" && lines[index - 1]?.includes("|") && !lines[index + 1]?.includes("|")
|
|
1824
|
-
);
|
|
1825
|
-
if (tableEndIndex !== -1) {
|
|
1826
|
-
contentStartIndex = tableEndIndex + 1;
|
|
1827
|
-
}
|
|
1828
|
-
}
|
|
1829
|
-
const mainContent = lines.slice(contentStartIndex).join("\n").trim();
|
|
1830
|
-
if (!mainContent) {
|
|
1831
|
-
return null;
|
|
1832
|
-
}
|
|
1833
|
-
const frontmatter = {
|
|
1834
|
-
root: false,
|
|
1835
|
-
targets: ["geminicli"],
|
|
1836
|
-
description: "Main Gemini CLI configuration",
|
|
1837
|
-
globs: ["**/*"]
|
|
1838
|
-
};
|
|
1839
|
-
return {
|
|
1840
|
-
frontmatter,
|
|
1841
|
-
content: mainContent,
|
|
1842
|
-
filename: "gemini-main",
|
|
1843
|
-
filepath
|
|
1844
|
-
};
|
|
1845
|
-
}
|
|
1846
|
-
async function parseGeminiMemoryFiles(memoryDir) {
|
|
1847
|
-
const rules = [];
|
|
1848
|
-
try {
|
|
1849
|
-
const { readdir: readdir2 } = await import("fs/promises");
|
|
1850
|
-
const files = await readdir2(memoryDir);
|
|
1851
|
-
for (const file of files) {
|
|
1852
|
-
if (file.endsWith(".md")) {
|
|
1853
|
-
const filePath = join19(memoryDir, file);
|
|
1854
|
-
const content = await readFileContent(filePath);
|
|
1855
|
-
if (content.trim()) {
|
|
1856
|
-
const filename = basename5(file, ".md");
|
|
1857
|
-
const frontmatter = {
|
|
1858
|
-
root: false,
|
|
1859
|
-
targets: ["geminicli"],
|
|
1860
|
-
description: `Memory file: ${filename}`,
|
|
1861
|
-
globs: ["**/*"]
|
|
1862
|
-
};
|
|
1863
|
-
rules.push({
|
|
1864
|
-
frontmatter,
|
|
1865
|
-
content: content.trim(),
|
|
1866
|
-
filename: `gemini-memory-${filename}`,
|
|
1867
|
-
filepath: filePath
|
|
1868
|
-
});
|
|
1869
|
-
}
|
|
1870
|
-
}
|
|
1871
|
-
}
|
|
1872
|
-
} catch {
|
|
1873
|
-
}
|
|
1874
|
-
return rules;
|
|
1875
|
-
}
|
|
1876
|
-
async function parseGeminiSettings(settingsPath) {
|
|
1877
|
-
const errors = [];
|
|
1878
|
-
let mcpServers;
|
|
1879
|
-
try {
|
|
1880
|
-
const content = await readFileContent(settingsPath);
|
|
1881
|
-
const settings = JSON.parse(content);
|
|
1882
|
-
const parseResult = RulesyncMcpConfigSchema.safeParse(settings);
|
|
1883
|
-
if (parseResult.success && Object.keys(parseResult.data.mcpServers).length > 0) {
|
|
1884
|
-
mcpServers = parseResult.data.mcpServers;
|
|
1885
|
-
}
|
|
1886
|
-
} catch (error) {
|
|
1887
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1888
|
-
errors.push(`Failed to parse settings.json: ${errorMessage}`);
|
|
1889
|
-
}
|
|
1890
|
-
return {
|
|
1891
|
-
errors,
|
|
1892
|
-
...mcpServers && { mcpServers }
|
|
1893
|
-
};
|
|
1894
|
-
}
|
|
1895
2221
|
async function parseAiexclude(aiexcludePath) {
|
|
1896
2222
|
try {
|
|
1897
2223
|
const content = await readFileContent(aiexcludePath);
|
|
@@ -1901,75 +2227,40 @@ async function parseAiexclude(aiexcludePath) {
|
|
|
1901
2227
|
return [];
|
|
1902
2228
|
}
|
|
1903
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
|
+
}
|
|
1904
2245
|
|
|
1905
2246
|
// src/parsers/roo.ts
|
|
1906
|
-
import { join as join20 } from "path";
|
|
1907
2247
|
async function parseRooConfiguration(baseDir = process.cwd()) {
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
};
|
|
1921
|
-
rules.push({
|
|
1922
|
-
frontmatter,
|
|
1923
|
-
content: content.trim(),
|
|
1924
|
-
filename: "roo-instructions",
|
|
1925
|
-
filepath: rooFilePath
|
|
1926
|
-
});
|
|
1927
|
-
}
|
|
1928
|
-
} catch (error) {
|
|
1929
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1930
|
-
errors.push(`Failed to parse .roo/instructions.md: ${errorMessage}`);
|
|
1931
|
-
}
|
|
1932
|
-
}
|
|
1933
|
-
const rooRulesDir = join20(baseDir, ".roo", "rules");
|
|
1934
|
-
if (await fileExists(rooRulesDir)) {
|
|
1935
|
-
try {
|
|
1936
|
-
const { readdir: readdir2 } = await import("fs/promises");
|
|
1937
|
-
const files = await readdir2(rooRulesDir);
|
|
1938
|
-
for (const file of files) {
|
|
1939
|
-
if (file.endsWith(".md")) {
|
|
1940
|
-
const filePath = join20(rooRulesDir, file);
|
|
1941
|
-
try {
|
|
1942
|
-
const content = await readFileContent(filePath);
|
|
1943
|
-
if (content.trim()) {
|
|
1944
|
-
const filename = file.replace(".md", "");
|
|
1945
|
-
const frontmatter = {
|
|
1946
|
-
root: false,
|
|
1947
|
-
targets: ["roo"],
|
|
1948
|
-
description: `Roo rule: ${filename}`,
|
|
1949
|
-
globs: ["**/*"]
|
|
1950
|
-
};
|
|
1951
|
-
rules.push({
|
|
1952
|
-
frontmatter,
|
|
1953
|
-
content: content.trim(),
|
|
1954
|
-
filename: `roo-${filename}`,
|
|
1955
|
-
filepath: filePath
|
|
1956
|
-
});
|
|
1957
|
-
}
|
|
1958
|
-
} catch (error) {
|
|
1959
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1960
|
-
errors.push(`Failed to parse ${filePath}: ${errorMessage}`);
|
|
1961
|
-
}
|
|
1962
|
-
}
|
|
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"
|
|
1963
2260
|
}
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
}
|
|
1968
|
-
}
|
|
1969
|
-
if (rules.length === 0) {
|
|
1970
|
-
errors.push("No Roo Code configuration files found (.roo/instructions.md or .roo/rules/*.md)");
|
|
1971
|
-
}
|
|
1972
|
-
return { rules, errors };
|
|
2261
|
+
],
|
|
2262
|
+
errorMessage: "No Roo Code configuration files found (.roo/instructions.md or .roo/rules/*.md)"
|
|
2263
|
+
});
|
|
1973
2264
|
}
|
|
1974
2265
|
|
|
1975
2266
|
// src/core/importer.ts
|
|
@@ -1984,6 +2275,18 @@ async function importConfiguration(options) {
|
|
|
1984
2275
|
}
|
|
1985
2276
|
try {
|
|
1986
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
|
+
}
|
|
1987
2290
|
case "claudecode": {
|
|
1988
2291
|
const claudeResult = await parseClaudeConfiguration(baseDir);
|
|
1989
2292
|
rules = claudeResult.rules;
|
|
@@ -2038,7 +2341,7 @@ async function importConfiguration(options) {
|
|
|
2038
2341
|
if (rules.length === 0 && !ignorePatterns && !mcpServers) {
|
|
2039
2342
|
return { success: false, rulesCreated: 0, errors };
|
|
2040
2343
|
}
|
|
2041
|
-
const rulesDirPath =
|
|
2344
|
+
const rulesDirPath = join20(baseDir, rulesDir);
|
|
2042
2345
|
try {
|
|
2043
2346
|
const { mkdir: mkdir3 } = await import("fs/promises");
|
|
2044
2347
|
await mkdir3(rulesDirPath, { recursive: true });
|
|
@@ -2052,7 +2355,7 @@ async function importConfiguration(options) {
|
|
|
2052
2355
|
try {
|
|
2053
2356
|
const baseFilename = `${tool}__${rule.filename}`;
|
|
2054
2357
|
const filename = await generateUniqueFilename(rulesDirPath, baseFilename);
|
|
2055
|
-
const filePath =
|
|
2358
|
+
const filePath = join20(rulesDirPath, `${filename}.md`);
|
|
2056
2359
|
const content = generateRuleFileContent(rule);
|
|
2057
2360
|
await writeFileContent(filePath, content);
|
|
2058
2361
|
rulesCreated++;
|
|
@@ -2067,7 +2370,7 @@ async function importConfiguration(options) {
|
|
|
2067
2370
|
let ignoreFileCreated = false;
|
|
2068
2371
|
if (ignorePatterns && ignorePatterns.length > 0) {
|
|
2069
2372
|
try {
|
|
2070
|
-
const rulesyncignorePath =
|
|
2373
|
+
const rulesyncignorePath = join20(baseDir, ".rulesyncignore");
|
|
2071
2374
|
const ignoreContent = `${ignorePatterns.join("\n")}
|
|
2072
2375
|
`;
|
|
2073
2376
|
await writeFileContent(rulesyncignorePath, ignoreContent);
|
|
@@ -2083,7 +2386,7 @@ async function importConfiguration(options) {
|
|
|
2083
2386
|
let mcpFileCreated = false;
|
|
2084
2387
|
if (mcpServers && Object.keys(mcpServers).length > 0) {
|
|
2085
2388
|
try {
|
|
2086
|
-
const mcpPath =
|
|
2389
|
+
const mcpPath = join20(baseDir, rulesDir, ".mcp.json");
|
|
2087
2390
|
const mcpContent = `${JSON.stringify({ mcpServers }, null, 2)}
|
|
2088
2391
|
`;
|
|
2089
2392
|
await writeFileContent(mcpPath, mcpContent);
|
|
@@ -2105,13 +2408,13 @@ async function importConfiguration(options) {
|
|
|
2105
2408
|
};
|
|
2106
2409
|
}
|
|
2107
2410
|
function generateRuleFileContent(rule) {
|
|
2108
|
-
const frontmatter =
|
|
2411
|
+
const frontmatter = matter5.stringify("", rule.frontmatter);
|
|
2109
2412
|
return frontmatter + rule.content;
|
|
2110
2413
|
}
|
|
2111
2414
|
async function generateUniqueFilename(rulesDir, baseFilename) {
|
|
2112
2415
|
let filename = baseFilename;
|
|
2113
2416
|
let counter = 1;
|
|
2114
|
-
while (await fileExists(
|
|
2417
|
+
while (await fileExists(join20(rulesDir, `${filename}.md`))) {
|
|
2115
2418
|
filename = `${baseFilename}-${counter}`;
|
|
2116
2419
|
counter++;
|
|
2117
2420
|
}
|
|
@@ -2121,6 +2424,8 @@ async function generateUniqueFilename(rulesDir, baseFilename) {
|
|
|
2121
2424
|
// src/cli/commands/import.ts
|
|
2122
2425
|
async function importCommand(options = {}) {
|
|
2123
2426
|
const tools = [];
|
|
2427
|
+
if (options.augmentcode) tools.push("augmentcode");
|
|
2428
|
+
if (options.augmentcodeLegacy) tools.push("augmentcode-legacy");
|
|
2124
2429
|
if (options.claudecode) tools.push("claudecode");
|
|
2125
2430
|
if (options.cursor) tools.push("cursor");
|
|
2126
2431
|
if (options.copilot) tools.push("copilot");
|
|
@@ -2129,7 +2434,7 @@ async function importCommand(options = {}) {
|
|
|
2129
2434
|
if (options.geminicli) tools.push("geminicli");
|
|
2130
2435
|
if (tools.length === 0) {
|
|
2131
2436
|
console.error(
|
|
2132
|
-
"\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)"
|
|
2133
2438
|
);
|
|
2134
2439
|
process.exit(1);
|
|
2135
2440
|
}
|
|
@@ -2176,7 +2481,7 @@ async function importCommand(options = {}) {
|
|
|
2176
2481
|
}
|
|
2177
2482
|
|
|
2178
2483
|
// src/cli/commands/init.ts
|
|
2179
|
-
import { join as
|
|
2484
|
+
import { join as join21 } from "path";
|
|
2180
2485
|
async function initCommand() {
|
|
2181
2486
|
const aiRulesDir = ".rulesync";
|
|
2182
2487
|
console.log("Initializing rulesync...");
|
|
@@ -2223,7 +2528,7 @@ globs: ["**/*"]
|
|
|
2223
2528
|
- Follow single responsibility principle
|
|
2224
2529
|
`
|
|
2225
2530
|
};
|
|
2226
|
-
const filepath =
|
|
2531
|
+
const filepath = join21(aiRulesDir, sampleFile.filename);
|
|
2227
2532
|
if (!await fileExists(filepath)) {
|
|
2228
2533
|
await writeFileContent(filepath, sampleFile.content);
|
|
2229
2534
|
console.log(`Created ${filepath}`);
|
|
@@ -2367,16 +2672,18 @@ async function watchCommand() {
|
|
|
2367
2672
|
|
|
2368
2673
|
// src/cli/index.ts
|
|
2369
2674
|
var program = new Command();
|
|
2370
|
-
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");
|
|
2371
2676
|
program.command("init").description("Initialize rulesync in current directory").action(initCommand);
|
|
2372
2677
|
program.command("add <filename>").description("Add a new rule file").action(addCommand);
|
|
2373
2678
|
program.command("gitignore").description("Add generated files to .gitignore").action(gitignoreCommand);
|
|
2374
|
-
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);
|
|
2375
|
-
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(
|
|
2376
2681
|
"-b, --base-dir <paths>",
|
|
2377
2682
|
"Base directories to generate files (comma-separated for multiple paths)"
|
|
2378
2683
|
).option("-v, --verbose", "Verbose output").action(async (options) => {
|
|
2379
2684
|
const tools = [];
|
|
2685
|
+
if (options.augmentcode) tools.push("augmentcode");
|
|
2686
|
+
if (options.augmentcodeLegacy) tools.push("augmentcode-legacy");
|
|
2380
2687
|
if (options.copilot) tools.push("copilot");
|
|
2381
2688
|
if (options.cursor) tools.push("cursor");
|
|
2382
2689
|
if (options.cline) tools.push("cline");
|