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/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-OTCCHS7Q.js";
10
+ } from "./chunk-YVRWBSCK.js";
5
11
  import {
6
12
  generateClineMcp
7
- } from "./chunk-BY6RI77W.js";
13
+ } from "./chunk-XHNIEO22.js";
8
14
  import {
9
15
  generateCopilotMcp
10
- } from "./chunk-P6KQZULZ.js";
16
+ } from "./chunk-G5LLOIO4.js";
11
17
  import {
12
18
  generateCursorMcp
13
- } from "./chunk-7UVBAWYG.js";
19
+ } from "./chunk-6MFEIYHN.js";
14
20
  import {
15
21
  generateGeminiCliMcp
16
- } from "./chunk-JWN6GRG6.js";
22
+ } from "./chunk-QNHGYRJT.js";
17
23
  import {
18
24
  generateKiroMcp
19
- } from "./chunk-D365OP7N.js";
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-7ZIUEZZQ.js";
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: ["copilot", "cursor", "cline", "claudecode", "roo", "geminicli", "kiro"]
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/kiro.ts
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 = join2(outputPath, ".aiignore");
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 = extractIgnorePatternsFromRules(rules);
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 extractIgnorePatternsFromRules(rules) {
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/claudecode.ts
213
- import { join as join5 } from "path";
432
+ // src/generators/rules/augmentcode.ts
433
+ import { join as join7 } from "path";
214
434
 
215
- // src/types/claudecode.ts
216
- import { z } from "zod/mini";
217
- var ClaudeSettingsSchema = z.looseObject({
218
- permissions: z._default(
219
- z.looseObject({
220
- deny: z._default(z.array(z.string()), [])
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 join3 } from "path";
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) => join3(dir, 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 = join4(baseDir, ".rulesyncignore");
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 ? join5(baseDir, config.outputPaths.claudecode) : config.outputPaths.claudecode;
702
+ const claudeOutputDir = baseDir ? join8(baseDir, config.outputPaths.claudecode) : config.outputPaths.claudecode;
351
703
  outputs.push({
352
704
  tool: "claudecode",
353
- filepath: join5(claudeOutputDir, "CLAUDE.md"),
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: join5(claudeOutputDir, ".claude", "memories", `${rule.filename}.md`),
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 ? join5(baseDir, ".claude", "settings.json") : join5(".claude", "settings.json");
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
- const outputs = [];
433
- for (const rule of rules) {
434
- const content = generateClineMarkdown(rule);
435
- const outputDir = baseDir ? join6(baseDir, config.outputPaths.cline) : config.outputPaths.cline;
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
- filepath: clineIgnorePath,
450
- content: clineIgnoreContent
451
- });
452
- }
453
- return outputs;
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 join7 } from "path";
797
+ import { join as join9 } from "path";
470
798
  async function generateCopilotConfig(rules, config, baseDir) {
471
- const outputs = [];
472
- for (const rule of rules) {
473
- const content = generateCopilotMarkdown(rule);
474
- const baseFilename = rule.filename.replace(/\.md$/, "");
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
- filepath: copilotIgnorePath,
490
- content: copilotIgnoreContent
491
- });
492
- }
493
- return outputs;
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 join8 } from "path";
830
+ import { join as join10 } from "path";
522
831
  async function generateCursorConfig(rules, config, baseDir) {
523
- const outputs = [];
524
- for (const rule of rules) {
525
- const content = generateCursorMarkdown(rule);
526
- const outputDir = baseDir ? join8(baseDir, config.outputPaths.cursor) : config.outputPaths.cursor;
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
- filepath: cursorIgnorePath,
541
- content: cursorIgnoreContent
542
- });
543
- }
544
- return outputs;
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 join9 } from "path";
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 ? join9(baseDir, config.outputPaths.geminicli) : config.outputPaths.geminicli;
617
- const filepath = join9(outputDir, `${rule.filename}.md`);
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 ? join9(baseDir, "GEMINI.md") : "GEMINI.md";
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 ? join9(baseDir, ".aiexclude") : ".aiexclude";
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 join10 } from "path";
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 ? join10(baseDir, config.outputPaths.kiro) : config.outputPaths.kiro;
687
- const filepath = join10(outputDir, `${rule.filename}.md`);
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
- const outputs = [];
704
- for (const rule of rules) {
705
- const content = generateRooMarkdown(rule);
706
- const outputDir = baseDir ? join11(baseDir, config.outputPaths.roo) : config.outputPaths.roo;
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
- filepath: rooIgnorePath,
721
- content: rooIgnoreContent
722
- });
723
- }
724
- return outputs;
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 join14 } from "path";
1496
+ import { join as join15 } from "path";
1204
1497
  var gitignoreCommand = async () => {
1205
- const gitignorePath = join14(process.cwd(), ".gitignore");
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
- // src/core/importer.ts
1261
- import { join as join21 } from "path";
1262
- import matter4 from "gray-matter";
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 claudeFilePath = join15(baseDir, "CLAUDE.md");
1272
- if (!await fileExists(claudeFilePath)) {
1273
- errors.push("CLAUDE.md file not found");
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 claudeContent = await readFileContent(claudeFilePath);
1278
- const mainRule = parseClaudeMainFile(claudeContent, claudeFilePath);
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 = join15(baseDir, ".claude", "memories");
1816
+ const memoryDir = join18(baseDir, config.memoryDirPath);
1283
1817
  if (await fileExists(memoryDir)) {
1284
- const memoryRules = await parseClaudeMemoryFiles(memoryDir);
1818
+ const memoryRules = await parseMemoryFiles(memoryDir, config);
1285
1819
  rules.push(...memoryRules);
1286
1820
  }
1287
- const settingsPath = join15(baseDir, ".claude", "settings.json");
1821
+ const settingsPath = join18(baseDir, config.settingsPath);
1288
1822
  if (await fileExists(settingsPath)) {
1289
- const settingsResult = await parseClaudeSettings(settingsPath);
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 Claude configuration: ${errorMessage}`);
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 parseClaudeMainFile(content, filepath) {
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: ["claudecode"],
1327
- description: "Main Claude Code configuration",
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: "claude-main",
1876
+ filename: `${config.filenamePrefix}-main`,
1334
1877
  filepath
1335
1878
  };
1336
1879
  }
1337
- async function parseClaudeMemoryFiles(memoryDir) {
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 = join15(memoryDir, file);
1887
+ const filePath = join18(memoryDir, file);
1345
1888
  const content = await readFileContent(filePath);
1346
1889
  if (content.trim()) {
1347
- const filename = basename2(file, ".md");
1890
+ const filename = basename3(file, ".md");
1348
1891
  const frontmatter = {
1349
1892
  root: false,
1350
- targets: ["claudecode"],
1351
- description: `Memory file: ${filename}`,
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: `claude-memory-${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 parseClaudeSettings(settingsPath) {
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
- const errors = [];
1410
- const rules = [];
1411
- const clineFilePath = join16(baseDir, ".cline", "instructions.md");
1412
- if (await fileExists(clineFilePath)) {
1413
- try {
1414
- const content = await readFileContent(clineFilePath);
1415
- if (content.trim()) {
1416
- const frontmatter = {
1417
- root: false,
1418
- targets: ["cline"],
1419
- description: "Cline instructions",
1420
- globs: ["**/*"]
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
- } catch (error) {
1466
- const errorMessage = error instanceof Error ? error.message : String(error);
1467
- errors.push(`Failed to parse .clinerules files: ${errorMessage}`);
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
- const errors = [];
1481
- const rules = [];
1482
- const copilotFilePath = join17(baseDir, ".github", "copilot-instructions.md");
1483
- if (await fileExists(copilotFilePath)) {
1484
- try {
1485
- const rawContent = await readFileContent(copilotFilePath);
1486
- const parsed = matter2(rawContent);
1487
- const content = parsed.content.trim();
1488
- if (content) {
1489
- const frontmatter = {
1490
- root: false,
1491
- targets: ["copilot"],
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
- } catch (error) {
1536
- const errorMessage = error instanceof Error ? error.message : String(error);
1537
- errors.push(`Failed to parse .github/instructions files: ${errorMessage}`);
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 join18 } from "path";
1550
- import matter3 from "gray-matter";
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 = join18(baseDir, ".cursorrules");
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 = matter3(rawContent, customMatterOptions);
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 = join18(baseDir, ".cursor", "rules");
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 = join18(cursorRulesDir, file);
2156
+ const filePath = join19(cursorRulesDir, file);
1703
2157
  try {
1704
2158
  const rawContent = await readFileContent(filePath);
1705
- const parsed = matter3(rawContent, customMatterOptions);
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 = join18(baseDir, ".cursorignore");
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 = join18(baseDir, ".cursor", "mcp.json");
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
- const errors = [];
1909
- const rules = [];
1910
- const rooFilePath = join20(baseDir, ".roo", "instructions.md");
1911
- if (await fileExists(rooFilePath)) {
1912
- try {
1913
- const content = await readFileContent(rooFilePath);
1914
- if (content.trim()) {
1915
- const frontmatter = {
1916
- root: false,
1917
- targets: ["roo"],
1918
- description: "Roo Code instructions",
1919
- globs: ["**/*"]
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
- } catch (error) {
1965
- const errorMessage = error instanceof Error ? error.message : String(error);
1966
- errors.push(`Failed to parse .roo/rules files: ${errorMessage}`);
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 = join21(baseDir, rulesDir);
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 = join21(rulesDirPath, `${filename}.md`);
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 = join21(baseDir, ".rulesyncignore");
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 = join21(baseDir, rulesDir, ".mcp.json");
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 = matter4.stringify("", rule.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(join21(rulesDir, `${filename}.md`))) {
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 join22 } from "path";
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 = join22(aiRulesDir, sampleFile.filename);
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.48.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");