rulesync 0.47.0 → 0.49.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/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,
@@ -1159,11 +1452,10 @@ Generating configurations for base directory: ${baseDir}`);
1159
1452
  console.warn("\u26A0\uFE0F No configurations generated");
1160
1453
  return;
1161
1454
  }
1162
- console.log(`
1163
- \u{1F389} Successfully generated ${totalOutputs} configuration file(s)!`);
1164
1455
  if (options.verbose) {
1165
1456
  console.log("\nGenerating MCP configurations...");
1166
1457
  }
1458
+ let totalMcpOutputs = 0;
1167
1459
  for (const baseDir of baseDirs) {
1168
1460
  const mcpResults = await generateMcpConfigs(
1169
1461
  process.cwd(),
@@ -1178,6 +1470,7 @@ Generating configurations for base directory: ${baseDir}`);
1178
1470
  for (const result of mcpResults) {
1179
1471
  if (result.status === "success") {
1180
1472
  console.log(`\u2705 Generated ${result.tool} MCP configuration: ${result.path}`);
1473
+ totalMcpOutputs++;
1181
1474
  } else if (result.status === "error") {
1182
1475
  console.error(`\u274C Failed to generate ${result.tool} MCP configuration: ${result.error}`);
1183
1476
  } else if (options.verbose && result.status === "skipped") {
@@ -1185,6 +1478,13 @@ Generating configurations for base directory: ${baseDir}`);
1185
1478
  }
1186
1479
  }
1187
1480
  }
1481
+ const totalGenerated = totalOutputs + totalMcpOutputs;
1482
+ if (totalGenerated > 0) {
1483
+ console.log(
1484
+ `
1485
+ \u{1F389} All done! Generated ${totalGenerated} file(s) total (${totalOutputs} configurations + ${totalMcpOutputs} MCP configurations)`
1486
+ );
1487
+ }
1188
1488
  } catch (error) {
1189
1489
  console.error("\u274C Failed to generate configurations:", error);
1190
1490
  process.exit(1);
@@ -1193,9 +1493,9 @@ Generating configurations for base directory: ${baseDir}`);
1193
1493
 
1194
1494
  // src/cli/commands/gitignore.ts
1195
1495
  import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync } from "fs";
1196
- import { join as join14 } from "path";
1496
+ import { join as join15 } from "path";
1197
1497
  var gitignoreCommand = async () => {
1198
- const gitignorePath = join14(process.cwd(), ".gitignore");
1498
+ const gitignorePath = join15(process.cwd(), ".gitignore");
1199
1499
  const rulesFilesToIgnore = [
1200
1500
  "# Generated by rulesync - AI tool configuration files",
1201
1501
  "**/.github/copilot-instructions.md",
@@ -1213,7 +1513,10 @@ var gitignoreCommand = async () => {
1213
1513
  "**/.gemini/memories/",
1214
1514
  "**/.aiexclude",
1215
1515
  "**/.aiignore",
1516
+ "**/.augmentignore",
1216
1517
  "**/.kiro/steering/",
1518
+ "**/.augment/rules/",
1519
+ "**/.augment-guidelines",
1217
1520
  "**/.mcp.json",
1218
1521
  "!.rulesync/.mcp.json",
1219
1522
  "**/.cursor/mcp.json",
@@ -1232,54 +1535,292 @@ var gitignoreCommand = async () => {
1232
1535
  linesToAdd.push(rule);
1233
1536
  }
1234
1537
  }
1235
- if (linesToAdd.length === 0) {
1236
- console.log("\u2705 .gitignore is already up to date");
1237
- return;
1238
- }
1239
- const newContent = gitignoreContent ? `${gitignoreContent.trimEnd()}
1240
-
1241
- ${linesToAdd.join("\n")}
1242
- ` : `${linesToAdd.join("\n")}
1243
- `;
1244
- writeFileSync(gitignorePath, newContent);
1245
- console.log(`\u2705 Added ${linesToAdd.length} rules to .gitignore:`);
1246
- for (const line of linesToAdd) {
1247
- if (!line.startsWith("#")) {
1248
- console.log(` ${line}`);
1538
+ if (linesToAdd.length === 0) {
1539
+ console.log("\u2705 .gitignore is already up to date");
1540
+ return;
1541
+ }
1542
+ const newContent = gitignoreContent ? `${gitignoreContent.trimEnd()}
1543
+
1544
+ ${linesToAdd.join("\n")}
1545
+ ` : `${linesToAdd.join("\n")}
1546
+ `;
1547
+ writeFileSync(gitignorePath, newContent);
1548
+ console.log(`\u2705 Added ${linesToAdd.length} rules to .gitignore:`);
1549
+ for (const line of linesToAdd) {
1550
+ if (!line.startsWith("#")) {
1551
+ console.log(` ${line}`);
1552
+ }
1553
+ }
1554
+ };
1555
+
1556
+ // src/core/importer.ts
1557
+ import { join as join20 } from "path";
1558
+ import matter5 from "gray-matter";
1559
+
1560
+ // src/parsers/augmentcode.ts
1561
+ import { basename as basename2, join as join16 } from "path";
1562
+ import matter2 from "gray-matter";
1563
+
1564
+ // src/utils/parser-helpers.ts
1565
+ function createParseResult() {
1566
+ return { rules: [], errors: [] };
1567
+ }
1568
+ function addError(result, error) {
1569
+ result.errors.push(error);
1570
+ }
1571
+ function addRule(result, rule) {
1572
+ if (!result.rules) {
1573
+ result.rules = [];
1574
+ }
1575
+ result.rules.push(rule);
1576
+ }
1577
+ function addRules(result, rules) {
1578
+ if (!result.rules) {
1579
+ result.rules = [];
1580
+ }
1581
+ result.rules.push(...rules);
1582
+ }
1583
+ function handleParseError(error, context) {
1584
+ const errorMessage = error instanceof Error ? error.message : String(error);
1585
+ return `${context}: ${errorMessage}`;
1586
+ }
1587
+ async function safeReadFile(operation, errorContext) {
1588
+ try {
1589
+ const result = await operation();
1590
+ return { success: true, result };
1591
+ } catch (error) {
1592
+ return {
1593
+ success: false,
1594
+ error: handleParseError(error, errorContext)
1595
+ };
1596
+ }
1597
+ }
1598
+
1599
+ // src/parsers/augmentcode.ts
1600
+ async function parseAugmentcodeConfiguration(baseDir = process.cwd()) {
1601
+ const result = createParseResult();
1602
+ const rulesDir = join16(baseDir, ".augment", "rules");
1603
+ if (await fileExists(rulesDir)) {
1604
+ const rulesResult = await parseAugmentRules(rulesDir);
1605
+ addRules(result, rulesResult.rules);
1606
+ result.errors.push(...rulesResult.errors);
1607
+ } else {
1608
+ addError(result, "No AugmentCode configuration found. Expected .augment/rules/ directory.");
1609
+ }
1610
+ return { rules: result.rules || [], errors: result.errors };
1611
+ }
1612
+ async function parseAugmentRules(rulesDir) {
1613
+ const rules = [];
1614
+ const errors = [];
1615
+ try {
1616
+ const { readdir: readdir2 } = await import("fs/promises");
1617
+ const files = await readdir2(rulesDir);
1618
+ for (const file of files) {
1619
+ if (file.endsWith(".md") || file.endsWith(".mdc")) {
1620
+ const filePath = join16(rulesDir, file);
1621
+ try {
1622
+ const rawContent = await readFileContent(filePath);
1623
+ const parsed = matter2(rawContent);
1624
+ const frontmatterData = parsed.data;
1625
+ const ruleType = frontmatterData.type || "manual";
1626
+ const description = frontmatterData.description || "";
1627
+ const tags = Array.isArray(frontmatterData.tags) ? frontmatterData.tags : void 0;
1628
+ const isRoot = ruleType === "always";
1629
+ const filename = basename2(file, file.endsWith(".mdc") ? ".mdc" : ".md");
1630
+ const frontmatter = {
1631
+ root: isRoot,
1632
+ targets: ["augmentcode"],
1633
+ description,
1634
+ globs: ["**/*"],
1635
+ // AugmentCode doesn't use specific globs in the same way
1636
+ ...tags && { tags }
1637
+ };
1638
+ rules.push({
1639
+ frontmatter,
1640
+ content: parsed.content.trim(),
1641
+ filename: `augmentcode-${ruleType}-${filename}`,
1642
+ filepath: filePath
1643
+ });
1644
+ } catch (error) {
1645
+ const errorMessage = error instanceof Error ? error.message : String(error);
1646
+ errors.push(`Failed to parse ${filePath}: ${errorMessage}`);
1647
+ }
1648
+ }
1649
+ }
1650
+ } catch (error) {
1651
+ const errorMessage = error instanceof Error ? error.message : String(error);
1652
+ errors.push(`Failed to read .augment/rules/ directory: ${errorMessage}`);
1653
+ }
1654
+ return { rules, errors };
1655
+ }
1656
+
1657
+ // src/parsers/augmentcode-legacy.ts
1658
+ import { join as join17 } from "path";
1659
+ async function parseAugmentcodeLegacyConfiguration(baseDir = process.cwd()) {
1660
+ const result = createParseResult();
1661
+ const guidelinesPath = join17(baseDir, ".augment-guidelines");
1662
+ if (await fileExists(guidelinesPath)) {
1663
+ const guidelinesResult = await parseAugmentGuidelines(guidelinesPath);
1664
+ if (guidelinesResult.rule) {
1665
+ addRule(result, guidelinesResult.rule);
1666
+ }
1667
+ result.errors.push(...guidelinesResult.errors);
1668
+ } else {
1669
+ addError(
1670
+ result,
1671
+ "No AugmentCode legacy configuration found. Expected .augment-guidelines file."
1672
+ );
1673
+ }
1674
+ return { rules: result.rules || [], errors: result.errors };
1675
+ }
1676
+ async function parseAugmentGuidelines(guidelinesPath) {
1677
+ const parseResult = await safeReadFile(async () => {
1678
+ const content = await readFileContent(guidelinesPath);
1679
+ if (content.trim()) {
1680
+ const frontmatter = {
1681
+ root: true,
1682
+ // Legacy guidelines become root rules
1683
+ targets: ["augmentcode-legacy"],
1684
+ description: "Legacy AugmentCode guidelines",
1685
+ globs: ["**/*"]
1686
+ };
1687
+ return {
1688
+ frontmatter,
1689
+ content: content.trim(),
1690
+ filename: "augmentcode-legacy-guidelines",
1691
+ filepath: guidelinesPath
1692
+ };
1693
+ }
1694
+ return null;
1695
+ }, "Failed to parse .augment-guidelines");
1696
+ if (parseResult.success) {
1697
+ return { rule: parseResult.result || null, errors: [] };
1698
+ } else {
1699
+ return { rule: null, errors: [parseResult.error || "Unknown error"] };
1700
+ }
1701
+ }
1702
+
1703
+ // src/parsers/shared-helpers.ts
1704
+ import { basename as basename3, join as join18 } from "path";
1705
+ import matter3 from "gray-matter";
1706
+ async function parseConfigurationFiles(baseDir = process.cwd(), config) {
1707
+ const errors = [];
1708
+ const rules = [];
1709
+ if (config.mainFile) {
1710
+ const mainFilePath = join18(baseDir, config.mainFile.path);
1711
+ if (await fileExists(mainFilePath)) {
1712
+ try {
1713
+ const rawContent = await readFileContent(mainFilePath);
1714
+ let content;
1715
+ let frontmatter;
1716
+ if (config.mainFile.useFrontmatter) {
1717
+ const parsed = matter3(rawContent);
1718
+ content = parsed.content.trim();
1719
+ frontmatter = {
1720
+ root: false,
1721
+ targets: [config.tool],
1722
+ description: config.mainFile.description,
1723
+ globs: ["**/*"]
1724
+ };
1725
+ } else {
1726
+ content = rawContent.trim();
1727
+ frontmatter = {
1728
+ root: false,
1729
+ targets: [config.tool],
1730
+ description: config.mainFile.description,
1731
+ globs: ["**/*"]
1732
+ };
1733
+ }
1734
+ if (content) {
1735
+ rules.push({
1736
+ frontmatter,
1737
+ content,
1738
+ filename: `${config.tool}-instructions`,
1739
+ filepath: mainFilePath
1740
+ });
1741
+ }
1742
+ } catch (error) {
1743
+ const errorMessage = error instanceof Error ? error.message : String(error);
1744
+ errors.push(`Failed to parse ${config.mainFile.path}: ${errorMessage}`);
1745
+ }
1746
+ }
1747
+ }
1748
+ if (config.directories) {
1749
+ for (const dirConfig of config.directories) {
1750
+ const dirPath = join18(baseDir, dirConfig.directory);
1751
+ if (await fileExists(dirPath)) {
1752
+ try {
1753
+ const { readdir: readdir2 } = await import("fs/promises");
1754
+ const files = await readdir2(dirPath);
1755
+ for (const file of files) {
1756
+ if (file.endsWith(dirConfig.filePattern)) {
1757
+ const filePath = join18(dirPath, file);
1758
+ try {
1759
+ const rawContent = await readFileContent(filePath);
1760
+ let content;
1761
+ if (dirConfig.filePattern === ".instructions.md") {
1762
+ const parsed = matter3(rawContent);
1763
+ content = parsed.content.trim();
1764
+ } else {
1765
+ content = rawContent.trim();
1766
+ }
1767
+ if (content) {
1768
+ const filename = file.replace(new RegExp(`\\${dirConfig.filePattern}$`), "");
1769
+ const frontmatter = {
1770
+ root: false,
1771
+ targets: [config.tool],
1772
+ description: `${dirConfig.description}: ${filename}`,
1773
+ globs: ["**/*"]
1774
+ };
1775
+ rules.push({
1776
+ frontmatter,
1777
+ content,
1778
+ filename: `${config.tool}-${filename}`,
1779
+ filepath: filePath
1780
+ });
1781
+ }
1782
+ } catch (error) {
1783
+ const errorMessage = error instanceof Error ? error.message : String(error);
1784
+ errors.push(`Failed to parse ${filePath}: ${errorMessage}`);
1785
+ }
1786
+ }
1787
+ }
1788
+ } catch (error) {
1789
+ const errorMessage = error instanceof Error ? error.message : String(error);
1790
+ errors.push(`Failed to parse ${dirConfig.directory} files: ${errorMessage}`);
1791
+ }
1792
+ }
1249
1793
  }
1250
1794
  }
1251
- };
1252
-
1253
- // src/core/importer.ts
1254
- import { join as join21 } from "path";
1255
- import matter4 from "gray-matter";
1256
-
1257
- // src/parsers/claudecode.ts
1258
- import { basename as basename2, join as join15 } from "path";
1259
- async function parseClaudeConfiguration(baseDir = process.cwd()) {
1795
+ if (rules.length === 0) {
1796
+ errors.push(config.errorMessage);
1797
+ }
1798
+ return { rules, errors };
1799
+ }
1800
+ async function parseMemoryBasedConfiguration(baseDir = process.cwd(), config) {
1260
1801
  const errors = [];
1261
1802
  const rules = [];
1262
1803
  let ignorePatterns;
1263
1804
  let mcpServers;
1264
- const claudeFilePath = join15(baseDir, "CLAUDE.md");
1265
- if (!await fileExists(claudeFilePath)) {
1266
- 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`);
1267
1808
  return { rules, errors };
1268
1809
  }
1269
1810
  try {
1270
- const claudeContent = await readFileContent(claudeFilePath);
1271
- const mainRule = parseClaudeMainFile(claudeContent, claudeFilePath);
1811
+ const mainContent = await readFileContent(mainFilePath);
1812
+ const mainRule = parseMainFile(mainContent, mainFilePath, config);
1272
1813
  if (mainRule) {
1273
1814
  rules.push(mainRule);
1274
1815
  }
1275
- const memoryDir = join15(baseDir, ".claude", "memories");
1816
+ const memoryDir = join18(baseDir, config.memoryDirPath);
1276
1817
  if (await fileExists(memoryDir)) {
1277
- const memoryRules = await parseClaudeMemoryFiles(memoryDir);
1818
+ const memoryRules = await parseMemoryFiles(memoryDir, config);
1278
1819
  rules.push(...memoryRules);
1279
1820
  }
1280
- const settingsPath = join15(baseDir, ".claude", "settings.json");
1821
+ const settingsPath = join18(baseDir, config.settingsPath);
1281
1822
  if (await fileExists(settingsPath)) {
1282
- const settingsResult = await parseClaudeSettings(settingsPath);
1823
+ const settingsResult = await parseSettingsFile(settingsPath, config.tool);
1283
1824
  if (settingsResult.ignorePatterns) {
1284
1825
  ignorePatterns = settingsResult.ignorePatterns;
1285
1826
  }
@@ -1288,9 +1829,18 @@ async function parseClaudeConfiguration(baseDir = process.cwd()) {
1288
1829
  }
1289
1830
  errors.push(...settingsResult.errors);
1290
1831
  }
1832
+ if (config.additionalIgnoreFile) {
1833
+ const additionalIgnorePath = join18(baseDir, config.additionalIgnoreFile.path);
1834
+ if (await fileExists(additionalIgnorePath)) {
1835
+ const additionalPatterns = await config.additionalIgnoreFile.parser(additionalIgnorePath);
1836
+ if (additionalPatterns.length > 0) {
1837
+ ignorePatterns = ignorePatterns ? [...ignorePatterns, ...additionalPatterns] : additionalPatterns;
1838
+ }
1839
+ }
1840
+ }
1291
1841
  } catch (error) {
1292
1842
  const errorMessage = error instanceof Error ? error.message : String(error);
1293
- errors.push(`Failed to parse Claude configuration: ${errorMessage}`);
1843
+ errors.push(`Failed to parse ${config.tool} configuration: ${errorMessage}`);
1294
1844
  }
1295
1845
  return {
1296
1846
  rules,
@@ -1299,7 +1849,7 @@ async function parseClaudeConfiguration(baseDir = process.cwd()) {
1299
1849
  ...mcpServers && { mcpServers }
1300
1850
  };
1301
1851
  }
1302
- function parseClaudeMainFile(content, filepath) {
1852
+ function parseMainFile(content, filepath, config) {
1303
1853
  const lines = content.split("\n");
1304
1854
  let contentStartIndex = 0;
1305
1855
  if (lines.some((line) => line.includes("| Document | Description | File Patterns |"))) {
@@ -1316,38 +1866,38 @@ function parseClaudeMainFile(content, filepath) {
1316
1866
  }
1317
1867
  const frontmatter = {
1318
1868
  root: false,
1319
- targets: ["claudecode"],
1320
- description: "Main Claude Code configuration",
1869
+ targets: [config.tool],
1870
+ description: config.mainDescription,
1321
1871
  globs: ["**/*"]
1322
1872
  };
1323
1873
  return {
1324
1874
  frontmatter,
1325
1875
  content: mainContent,
1326
- filename: "claude-main",
1876
+ filename: `${config.filenamePrefix}-main`,
1327
1877
  filepath
1328
1878
  };
1329
1879
  }
1330
- async function parseClaudeMemoryFiles(memoryDir) {
1880
+ async function parseMemoryFiles(memoryDir, config) {
1331
1881
  const rules = [];
1332
1882
  try {
1333
1883
  const { readdir: readdir2 } = await import("fs/promises");
1334
1884
  const files = await readdir2(memoryDir);
1335
1885
  for (const file of files) {
1336
1886
  if (file.endsWith(".md")) {
1337
- const filePath = join15(memoryDir, file);
1887
+ const filePath = join18(memoryDir, file);
1338
1888
  const content = await readFileContent(filePath);
1339
1889
  if (content.trim()) {
1340
- const filename = basename2(file, ".md");
1890
+ const filename = basename3(file, ".md");
1341
1891
  const frontmatter = {
1342
1892
  root: false,
1343
- targets: ["claudecode"],
1344
- description: `Memory file: ${filename}`,
1893
+ targets: [config.tool],
1894
+ description: `${config.memoryDescription}: ${filename}`,
1345
1895
  globs: ["**/*"]
1346
1896
  };
1347
1897
  rules.push({
1348
1898
  frontmatter,
1349
1899
  content: content.trim(),
1350
- filename: `claude-memory-${filename}`,
1900
+ filename: `${config.filenamePrefix}-memory-${filename}`,
1351
1901
  filepath: filePath
1352
1902
  });
1353
1903
  }
@@ -1357,14 +1907,14 @@ async function parseClaudeMemoryFiles(memoryDir) {
1357
1907
  }
1358
1908
  return rules;
1359
1909
  }
1360
- async function parseClaudeSettings(settingsPath) {
1910
+ async function parseSettingsFile(settingsPath, tool) {
1361
1911
  const errors = [];
1362
1912
  let ignorePatterns;
1363
1913
  let mcpServers;
1364
1914
  try {
1365
1915
  const content = await readFileContent(settingsPath);
1366
1916
  const settings = JSON.parse(content);
1367
- if (typeof settings === "object" && settings !== null && "permissions" in settings) {
1917
+ if (tool === "claudecode" && typeof settings === "object" && settings !== null && "permissions" in settings) {
1368
1918
  const permissions = settings.permissions;
1369
1919
  if (typeof permissions !== "object" || permissions === null) {
1370
1920
  return { ignorePatterns: [], errors: [] };
@@ -1396,151 +1946,62 @@ async function parseClaudeSettings(settingsPath) {
1396
1946
  };
1397
1947
  }
1398
1948
 
1949
+ // src/parsers/claudecode.ts
1950
+ async function parseClaudeConfiguration(baseDir = process.cwd()) {
1951
+ return parseMemoryBasedConfiguration(baseDir, {
1952
+ tool: "claudecode",
1953
+ mainFileName: "CLAUDE.md",
1954
+ memoryDirPath: ".claude/memories",
1955
+ settingsPath: ".claude/settings.json",
1956
+ mainDescription: "Main Claude Code configuration",
1957
+ memoryDescription: "Memory file",
1958
+ filenamePrefix: "claude"
1959
+ });
1960
+ }
1961
+
1399
1962
  // src/parsers/cline.ts
1400
- import { join as join16 } from "path";
1401
1963
  async function parseClineConfiguration(baseDir = process.cwd()) {
1402
- const errors = [];
1403
- const rules = [];
1404
- const clineFilePath = join16(baseDir, ".cline", "instructions.md");
1405
- if (await fileExists(clineFilePath)) {
1406
- try {
1407
- const content = await readFileContent(clineFilePath);
1408
- if (content.trim()) {
1409
- const frontmatter = {
1410
- root: false,
1411
- targets: ["cline"],
1412
- description: "Cline instructions",
1413
- globs: ["**/*"]
1414
- };
1415
- rules.push({
1416
- frontmatter,
1417
- content: content.trim(),
1418
- filename: "cline-instructions",
1419
- filepath: clineFilePath
1420
- });
1421
- }
1422
- } catch (error) {
1423
- const errorMessage = error instanceof Error ? error.message : String(error);
1424
- errors.push(`Failed to parse .cline/instructions.md: ${errorMessage}`);
1425
- }
1426
- }
1427
- const clinerulesDirPath = join16(baseDir, ".clinerules");
1428
- if (await fileExists(clinerulesDirPath)) {
1429
- try {
1430
- const { readdir: readdir2 } = await import("fs/promises");
1431
- const files = await readdir2(clinerulesDirPath);
1432
- for (const file of files) {
1433
- if (file.endsWith(".md")) {
1434
- const filePath = join16(clinerulesDirPath, file);
1435
- try {
1436
- const content = await readFileContent(filePath);
1437
- if (content.trim()) {
1438
- const filename = file.replace(".md", "");
1439
- const frontmatter = {
1440
- root: false,
1441
- targets: ["cline"],
1442
- description: `Cline rule: ${filename}`,
1443
- globs: ["**/*"]
1444
- };
1445
- rules.push({
1446
- frontmatter,
1447
- content: content.trim(),
1448
- filename: `cline-${filename}`,
1449
- filepath: filePath
1450
- });
1451
- }
1452
- } catch (error) {
1453
- const errorMessage = error instanceof Error ? error.message : String(error);
1454
- errors.push(`Failed to parse ${filePath}: ${errorMessage}`);
1455
- }
1456
- }
1964
+ return parseConfigurationFiles(baseDir, {
1965
+ tool: "cline",
1966
+ mainFile: {
1967
+ path: ".cline/instructions.md",
1968
+ useFrontmatter: false,
1969
+ description: "Cline instructions"
1970
+ },
1971
+ directories: [
1972
+ {
1973
+ directory: ".clinerules",
1974
+ filePattern: ".md",
1975
+ description: "Cline rule"
1457
1976
  }
1458
- } catch (error) {
1459
- const errorMessage = error instanceof Error ? error.message : String(error);
1460
- errors.push(`Failed to parse .clinerules files: ${errorMessage}`);
1461
- }
1462
- }
1463
- if (rules.length === 0) {
1464
- errors.push("No Cline configuration files found (.cline/instructions.md or .clinerules/*.md)");
1465
- }
1466
- return { rules, errors };
1977
+ ],
1978
+ errorMessage: "No Cline configuration files found (.cline/instructions.md or .clinerules/*.md)"
1979
+ });
1467
1980
  }
1468
1981
 
1469
1982
  // src/parsers/copilot.ts
1470
- import { basename as basename3, join as join17 } from "path";
1471
- import matter2 from "gray-matter";
1472
1983
  async function parseCopilotConfiguration(baseDir = process.cwd()) {
1473
- const errors = [];
1474
- const rules = [];
1475
- const copilotFilePath = join17(baseDir, ".github", "copilot-instructions.md");
1476
- if (await fileExists(copilotFilePath)) {
1477
- try {
1478
- const rawContent = await readFileContent(copilotFilePath);
1479
- const parsed = matter2(rawContent);
1480
- const content = parsed.content.trim();
1481
- if (content) {
1482
- const frontmatter = {
1483
- root: false,
1484
- targets: ["copilot"],
1485
- description: "GitHub Copilot instructions",
1486
- globs: ["**/*"]
1487
- };
1488
- rules.push({
1489
- frontmatter,
1490
- content,
1491
- filename: "copilot-instructions",
1492
- filepath: copilotFilePath
1493
- });
1494
- }
1495
- } catch (error) {
1496
- const errorMessage = error instanceof Error ? error.message : String(error);
1497
- errors.push(`Failed to parse copilot-instructions.md: ${errorMessage}`);
1498
- }
1499
- }
1500
- const instructionsDir = join17(baseDir, ".github", "instructions");
1501
- if (await fileExists(instructionsDir)) {
1502
- try {
1503
- const { readdir: readdir2 } = await import("fs/promises");
1504
- const files = await readdir2(instructionsDir);
1505
- for (const file of files) {
1506
- if (file.endsWith(".instructions.md")) {
1507
- const filePath = join17(instructionsDir, file);
1508
- const rawContent = await readFileContent(filePath);
1509
- const parsed = matter2(rawContent);
1510
- const content = parsed.content.trim();
1511
- if (content) {
1512
- const filename = basename3(file, ".instructions.md");
1513
- const frontmatter = {
1514
- root: false,
1515
- targets: ["copilot"],
1516
- description: `Copilot instruction: ${filename}`,
1517
- globs: ["**/*"]
1518
- };
1519
- rules.push({
1520
- frontmatter,
1521
- content,
1522
- filename: `copilot-${filename}`,
1523
- filepath: filePath
1524
- });
1525
- }
1526
- }
1984
+ return parseConfigurationFiles(baseDir, {
1985
+ tool: "copilot",
1986
+ mainFile: {
1987
+ path: ".github/copilot-instructions.md",
1988
+ useFrontmatter: true,
1989
+ description: "GitHub Copilot instructions"
1990
+ },
1991
+ directories: [
1992
+ {
1993
+ directory: ".github/instructions",
1994
+ filePattern: ".instructions.md",
1995
+ description: "Copilot instruction"
1527
1996
  }
1528
- } catch (error) {
1529
- const errorMessage = error instanceof Error ? error.message : String(error);
1530
- errors.push(`Failed to parse .github/instructions files: ${errorMessage}`);
1531
- }
1532
- }
1533
- if (rules.length === 0) {
1534
- errors.push(
1535
- "No Copilot configuration files found (.github/copilot-instructions.md or .github/instructions/*.instructions.md)"
1536
- );
1537
- }
1538
- return { rules, errors };
1997
+ ],
1998
+ errorMessage: "No Copilot configuration files found (.github/copilot-instructions.md or .github/instructions/*.instructions.md)"
1999
+ });
1539
2000
  }
1540
2001
 
1541
2002
  // src/parsers/cursor.ts
1542
- import { basename as basename4, join as join18 } from "path";
1543
- import matter3 from "gray-matter";
2003
+ import { basename as basename4, join as join19 } from "path";
2004
+ import matter4 from "gray-matter";
1544
2005
  import { DEFAULT_SCHEMA, FAILSAFE_SCHEMA, load } from "js-yaml";
1545
2006
  import { z as z5 } from "zod/mini";
1546
2007
  var customMatterOptions = {
@@ -1664,11 +2125,11 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
1664
2125
  const rules = [];
1665
2126
  let ignorePatterns;
1666
2127
  let mcpServers;
1667
- const cursorFilePath = join18(baseDir, ".cursorrules");
2128
+ const cursorFilePath = join19(baseDir, ".cursorrules");
1668
2129
  if (await fileExists(cursorFilePath)) {
1669
2130
  try {
1670
2131
  const rawContent = await readFileContent(cursorFilePath);
1671
- const parsed = matter3(rawContent, customMatterOptions);
2132
+ const parsed = matter4(rawContent, customMatterOptions);
1672
2133
  const content = parsed.content.trim();
1673
2134
  if (content) {
1674
2135
  const frontmatter = convertCursorMdcFrontmatter(parsed.data, "cursorrules");
@@ -1685,17 +2146,17 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
1685
2146
  errors.push(`Failed to parse .cursorrules file: ${errorMessage}`);
1686
2147
  }
1687
2148
  }
1688
- const cursorRulesDir = join18(baseDir, ".cursor", "rules");
2149
+ const cursorRulesDir = join19(baseDir, ".cursor", "rules");
1689
2150
  if (await fileExists(cursorRulesDir)) {
1690
2151
  try {
1691
2152
  const { readdir: readdir2 } = await import("fs/promises");
1692
2153
  const files = await readdir2(cursorRulesDir);
1693
2154
  for (const file of files) {
1694
2155
  if (file.endsWith(".mdc")) {
1695
- const filePath = join18(cursorRulesDir, file);
2156
+ const filePath = join19(cursorRulesDir, file);
1696
2157
  try {
1697
2158
  const rawContent = await readFileContent(filePath);
1698
- const parsed = matter3(rawContent, customMatterOptions);
2159
+ const parsed = matter4(rawContent, customMatterOptions);
1699
2160
  const content = parsed.content.trim();
1700
2161
  if (content) {
1701
2162
  const filename = basename4(file, ".mdc");
@@ -1721,7 +2182,7 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
1721
2182
  if (rules.length === 0) {
1722
2183
  errors.push("No Cursor configuration files found (.cursorrules or .cursor/rules/*.mdc)");
1723
2184
  }
1724
- const cursorIgnorePath = join18(baseDir, ".cursorignore");
2185
+ const cursorIgnorePath = join19(baseDir, ".cursorignore");
1725
2186
  if (await fileExists(cursorIgnorePath)) {
1726
2187
  try {
1727
2188
  const content = await readFileContent(cursorIgnorePath);
@@ -1734,7 +2195,7 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
1734
2195
  errors.push(`Failed to parse .cursorignore: ${errorMessage}`);
1735
2196
  }
1736
2197
  }
1737
- const cursorMcpPath = join18(baseDir, ".cursor", "mcp.json");
2198
+ const cursorMcpPath = join19(baseDir, ".cursor", "mcp.json");
1738
2199
  if (await fileExists(cursorMcpPath)) {
1739
2200
  try {
1740
2201
  const content = await readFileContent(cursorMcpPath);
@@ -1757,134 +2218,6 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
1757
2218
  }
1758
2219
 
1759
2220
  // src/parsers/geminicli.ts
1760
- import { basename as basename5, join as join19 } from "path";
1761
- async function parseGeminiConfiguration(baseDir = process.cwd()) {
1762
- const errors = [];
1763
- const rules = [];
1764
- let ignorePatterns;
1765
- let mcpServers;
1766
- const geminiFilePath = join19(baseDir, "GEMINI.md");
1767
- if (!await fileExists(geminiFilePath)) {
1768
- errors.push("GEMINI.md file not found");
1769
- return { rules, errors };
1770
- }
1771
- try {
1772
- const geminiContent = await readFileContent(geminiFilePath);
1773
- const mainRule = parseGeminiMainFile(geminiContent, geminiFilePath);
1774
- if (mainRule) {
1775
- rules.push(mainRule);
1776
- }
1777
- const memoryDir = join19(baseDir, ".gemini", "memories");
1778
- if (await fileExists(memoryDir)) {
1779
- const memoryRules = await parseGeminiMemoryFiles(memoryDir);
1780
- rules.push(...memoryRules);
1781
- }
1782
- const settingsPath = join19(baseDir, ".gemini", "settings.json");
1783
- if (await fileExists(settingsPath)) {
1784
- const settingsResult = await parseGeminiSettings(settingsPath);
1785
- if (settingsResult.ignorePatterns) {
1786
- ignorePatterns = settingsResult.ignorePatterns;
1787
- }
1788
- if (settingsResult.mcpServers) {
1789
- mcpServers = settingsResult.mcpServers;
1790
- }
1791
- errors.push(...settingsResult.errors);
1792
- }
1793
- const aiexcludePath = join19(baseDir, ".aiexclude");
1794
- if (await fileExists(aiexcludePath)) {
1795
- const aiexcludePatterns = await parseAiexclude(aiexcludePath);
1796
- if (aiexcludePatterns.length > 0) {
1797
- ignorePatterns = ignorePatterns ? [...ignorePatterns, ...aiexcludePatterns] : aiexcludePatterns;
1798
- }
1799
- }
1800
- } catch (error) {
1801
- const errorMessage = error instanceof Error ? error.message : String(error);
1802
- errors.push(`Failed to parse Gemini configuration: ${errorMessage}`);
1803
- }
1804
- return {
1805
- rules,
1806
- errors,
1807
- ...ignorePatterns && { ignorePatterns },
1808
- ...mcpServers && { mcpServers }
1809
- };
1810
- }
1811
- function parseGeminiMainFile(content, filepath) {
1812
- const lines = content.split("\n");
1813
- let contentStartIndex = 0;
1814
- if (lines.some((line) => line.includes("| Document | Description | File Patterns |"))) {
1815
- const tableEndIndex = lines.findIndex(
1816
- (line, index) => index > 0 && line.trim() === "" && lines[index - 1]?.includes("|") && !lines[index + 1]?.includes("|")
1817
- );
1818
- if (tableEndIndex !== -1) {
1819
- contentStartIndex = tableEndIndex + 1;
1820
- }
1821
- }
1822
- const mainContent = lines.slice(contentStartIndex).join("\n").trim();
1823
- if (!mainContent) {
1824
- return null;
1825
- }
1826
- const frontmatter = {
1827
- root: false,
1828
- targets: ["geminicli"],
1829
- description: "Main Gemini CLI configuration",
1830
- globs: ["**/*"]
1831
- };
1832
- return {
1833
- frontmatter,
1834
- content: mainContent,
1835
- filename: "gemini-main",
1836
- filepath
1837
- };
1838
- }
1839
- async function parseGeminiMemoryFiles(memoryDir) {
1840
- const rules = [];
1841
- try {
1842
- const { readdir: readdir2 } = await import("fs/promises");
1843
- const files = await readdir2(memoryDir);
1844
- for (const file of files) {
1845
- if (file.endsWith(".md")) {
1846
- const filePath = join19(memoryDir, file);
1847
- const content = await readFileContent(filePath);
1848
- if (content.trim()) {
1849
- const filename = basename5(file, ".md");
1850
- const frontmatter = {
1851
- root: false,
1852
- targets: ["geminicli"],
1853
- description: `Memory file: ${filename}`,
1854
- globs: ["**/*"]
1855
- };
1856
- rules.push({
1857
- frontmatter,
1858
- content: content.trim(),
1859
- filename: `gemini-memory-${filename}`,
1860
- filepath: filePath
1861
- });
1862
- }
1863
- }
1864
- }
1865
- } catch {
1866
- }
1867
- return rules;
1868
- }
1869
- async function parseGeminiSettings(settingsPath) {
1870
- const errors = [];
1871
- let mcpServers;
1872
- try {
1873
- const content = await readFileContent(settingsPath);
1874
- const settings = JSON.parse(content);
1875
- const parseResult = RulesyncMcpConfigSchema.safeParse(settings);
1876
- if (parseResult.success && Object.keys(parseResult.data.mcpServers).length > 0) {
1877
- mcpServers = parseResult.data.mcpServers;
1878
- }
1879
- } catch (error) {
1880
- const errorMessage = error instanceof Error ? error.message : String(error);
1881
- errors.push(`Failed to parse settings.json: ${errorMessage}`);
1882
- }
1883
- return {
1884
- errors,
1885
- ...mcpServers && { mcpServers }
1886
- };
1887
- }
1888
2221
  async function parseAiexclude(aiexcludePath) {
1889
2222
  try {
1890
2223
  const content = await readFileContent(aiexcludePath);
@@ -1894,75 +2227,40 @@ async function parseAiexclude(aiexcludePath) {
1894
2227
  return [];
1895
2228
  }
1896
2229
  }
2230
+ async function parseGeminiConfiguration(baseDir = process.cwd()) {
2231
+ return parseMemoryBasedConfiguration(baseDir, {
2232
+ tool: "geminicli",
2233
+ mainFileName: "GEMINI.md",
2234
+ memoryDirPath: ".gemini/memories",
2235
+ settingsPath: ".gemini/settings.json",
2236
+ mainDescription: "Main Gemini CLI configuration",
2237
+ memoryDescription: "Memory file",
2238
+ filenamePrefix: "gemini",
2239
+ additionalIgnoreFile: {
2240
+ path: ".aiexclude",
2241
+ parser: parseAiexclude
2242
+ }
2243
+ });
2244
+ }
1897
2245
 
1898
2246
  // src/parsers/roo.ts
1899
- import { join as join20 } from "path";
1900
2247
  async function parseRooConfiguration(baseDir = process.cwd()) {
1901
- const errors = [];
1902
- const rules = [];
1903
- const rooFilePath = join20(baseDir, ".roo", "instructions.md");
1904
- if (await fileExists(rooFilePath)) {
1905
- try {
1906
- const content = await readFileContent(rooFilePath);
1907
- if (content.trim()) {
1908
- const frontmatter = {
1909
- root: false,
1910
- targets: ["roo"],
1911
- description: "Roo Code instructions",
1912
- globs: ["**/*"]
1913
- };
1914
- rules.push({
1915
- frontmatter,
1916
- content: content.trim(),
1917
- filename: "roo-instructions",
1918
- filepath: rooFilePath
1919
- });
1920
- }
1921
- } catch (error) {
1922
- const errorMessage = error instanceof Error ? error.message : String(error);
1923
- errors.push(`Failed to parse .roo/instructions.md: ${errorMessage}`);
1924
- }
1925
- }
1926
- const rooRulesDir = join20(baseDir, ".roo", "rules");
1927
- if (await fileExists(rooRulesDir)) {
1928
- try {
1929
- const { readdir: readdir2 } = await import("fs/promises");
1930
- const files = await readdir2(rooRulesDir);
1931
- for (const file of files) {
1932
- if (file.endsWith(".md")) {
1933
- const filePath = join20(rooRulesDir, file);
1934
- try {
1935
- const content = await readFileContent(filePath);
1936
- if (content.trim()) {
1937
- const filename = file.replace(".md", "");
1938
- const frontmatter = {
1939
- root: false,
1940
- targets: ["roo"],
1941
- description: `Roo rule: ${filename}`,
1942
- globs: ["**/*"]
1943
- };
1944
- rules.push({
1945
- frontmatter,
1946
- content: content.trim(),
1947
- filename: `roo-${filename}`,
1948
- filepath: filePath
1949
- });
1950
- }
1951
- } catch (error) {
1952
- const errorMessage = error instanceof Error ? error.message : String(error);
1953
- errors.push(`Failed to parse ${filePath}: ${errorMessage}`);
1954
- }
1955
- }
2248
+ return parseConfigurationFiles(baseDir, {
2249
+ tool: "roo",
2250
+ mainFile: {
2251
+ path: ".roo/instructions.md",
2252
+ useFrontmatter: false,
2253
+ description: "Roo Code instructions"
2254
+ },
2255
+ directories: [
2256
+ {
2257
+ directory: ".roo/rules",
2258
+ filePattern: ".md",
2259
+ description: "Roo rule"
1956
2260
  }
1957
- } catch (error) {
1958
- const errorMessage = error instanceof Error ? error.message : String(error);
1959
- errors.push(`Failed to parse .roo/rules files: ${errorMessage}`);
1960
- }
1961
- }
1962
- if (rules.length === 0) {
1963
- errors.push("No Roo Code configuration files found (.roo/instructions.md or .roo/rules/*.md)");
1964
- }
1965
- return { rules, errors };
2261
+ ],
2262
+ errorMessage: "No Roo Code configuration files found (.roo/instructions.md or .roo/rules/*.md)"
2263
+ });
1966
2264
  }
1967
2265
 
1968
2266
  // src/core/importer.ts
@@ -1977,6 +2275,18 @@ async function importConfiguration(options) {
1977
2275
  }
1978
2276
  try {
1979
2277
  switch (tool) {
2278
+ case "augmentcode": {
2279
+ const augmentResult = await parseAugmentcodeConfiguration(baseDir);
2280
+ rules = augmentResult.rules;
2281
+ errors.push(...augmentResult.errors);
2282
+ break;
2283
+ }
2284
+ case "augmentcode-legacy": {
2285
+ const augmentLegacyResult = await parseAugmentcodeLegacyConfiguration(baseDir);
2286
+ rules = augmentLegacyResult.rules;
2287
+ errors.push(...augmentLegacyResult.errors);
2288
+ break;
2289
+ }
1980
2290
  case "claudecode": {
1981
2291
  const claudeResult = await parseClaudeConfiguration(baseDir);
1982
2292
  rules = claudeResult.rules;
@@ -2031,7 +2341,7 @@ async function importConfiguration(options) {
2031
2341
  if (rules.length === 0 && !ignorePatterns && !mcpServers) {
2032
2342
  return { success: false, rulesCreated: 0, errors };
2033
2343
  }
2034
- const rulesDirPath = join21(baseDir, rulesDir);
2344
+ const rulesDirPath = join20(baseDir, rulesDir);
2035
2345
  try {
2036
2346
  const { mkdir: mkdir3 } = await import("fs/promises");
2037
2347
  await mkdir3(rulesDirPath, { recursive: true });
@@ -2045,7 +2355,7 @@ async function importConfiguration(options) {
2045
2355
  try {
2046
2356
  const baseFilename = `${tool}__${rule.filename}`;
2047
2357
  const filename = await generateUniqueFilename(rulesDirPath, baseFilename);
2048
- const filePath = join21(rulesDirPath, `${filename}.md`);
2358
+ const filePath = join20(rulesDirPath, `${filename}.md`);
2049
2359
  const content = generateRuleFileContent(rule);
2050
2360
  await writeFileContent(filePath, content);
2051
2361
  rulesCreated++;
@@ -2060,7 +2370,7 @@ async function importConfiguration(options) {
2060
2370
  let ignoreFileCreated = false;
2061
2371
  if (ignorePatterns && ignorePatterns.length > 0) {
2062
2372
  try {
2063
- const rulesyncignorePath = join21(baseDir, ".rulesyncignore");
2373
+ const rulesyncignorePath = join20(baseDir, ".rulesyncignore");
2064
2374
  const ignoreContent = `${ignorePatterns.join("\n")}
2065
2375
  `;
2066
2376
  await writeFileContent(rulesyncignorePath, ignoreContent);
@@ -2076,7 +2386,7 @@ async function importConfiguration(options) {
2076
2386
  let mcpFileCreated = false;
2077
2387
  if (mcpServers && Object.keys(mcpServers).length > 0) {
2078
2388
  try {
2079
- const mcpPath = join21(baseDir, rulesDir, ".mcp.json");
2389
+ const mcpPath = join20(baseDir, rulesDir, ".mcp.json");
2080
2390
  const mcpContent = `${JSON.stringify({ mcpServers }, null, 2)}
2081
2391
  `;
2082
2392
  await writeFileContent(mcpPath, mcpContent);
@@ -2098,13 +2408,13 @@ async function importConfiguration(options) {
2098
2408
  };
2099
2409
  }
2100
2410
  function generateRuleFileContent(rule) {
2101
- const frontmatter = matter4.stringify("", rule.frontmatter);
2411
+ const frontmatter = matter5.stringify("", rule.frontmatter);
2102
2412
  return frontmatter + rule.content;
2103
2413
  }
2104
2414
  async function generateUniqueFilename(rulesDir, baseFilename) {
2105
2415
  let filename = baseFilename;
2106
2416
  let counter = 1;
2107
- while (await fileExists(join21(rulesDir, `${filename}.md`))) {
2417
+ while (await fileExists(join20(rulesDir, `${filename}.md`))) {
2108
2418
  filename = `${baseFilename}-${counter}`;
2109
2419
  counter++;
2110
2420
  }
@@ -2114,6 +2424,8 @@ async function generateUniqueFilename(rulesDir, baseFilename) {
2114
2424
  // src/cli/commands/import.ts
2115
2425
  async function importCommand(options = {}) {
2116
2426
  const tools = [];
2427
+ if (options.augmentcode) tools.push("augmentcode");
2428
+ if (options.augmentcodeLegacy) tools.push("augmentcode-legacy");
2117
2429
  if (options.claudecode) tools.push("claudecode");
2118
2430
  if (options.cursor) tools.push("cursor");
2119
2431
  if (options.copilot) tools.push("copilot");
@@ -2122,7 +2434,7 @@ async function importCommand(options = {}) {
2122
2434
  if (options.geminicli) tools.push("geminicli");
2123
2435
  if (tools.length === 0) {
2124
2436
  console.error(
2125
- "\u274C Please specify one tool to import from (--claudecode, --cursor, --copilot, --cline, --roo, --geminicli)"
2437
+ "\u274C Please specify one tool to import from (--augmentcode, --augmentcodeLegacy, --claudecode, --cursor, --copilot, --cline, --roo, --geminicli)"
2126
2438
  );
2127
2439
  process.exit(1);
2128
2440
  }
@@ -2169,7 +2481,7 @@ async function importCommand(options = {}) {
2169
2481
  }
2170
2482
 
2171
2483
  // src/cli/commands/init.ts
2172
- import { join as join22 } from "path";
2484
+ import { join as join21 } from "path";
2173
2485
  async function initCommand() {
2174
2486
  const aiRulesDir = ".rulesync";
2175
2487
  console.log("Initializing rulesync...");
@@ -2216,7 +2528,7 @@ globs: ["**/*"]
2216
2528
  - Follow single responsibility principle
2217
2529
  `
2218
2530
  };
2219
- const filepath = join22(aiRulesDir, sampleFile.filename);
2531
+ const filepath = join21(aiRulesDir, sampleFile.filename);
2220
2532
  if (!await fileExists(filepath)) {
2221
2533
  await writeFileContent(filepath, sampleFile.content);
2222
2534
  console.log(`Created ${filepath}`);
@@ -2360,16 +2672,18 @@ async function watchCommand() {
2360
2672
 
2361
2673
  // src/cli/index.ts
2362
2674
  var program = new Command();
2363
- program.name("rulesync").description("Unified AI rules management CLI tool").version("0.47.0");
2675
+ program.name("rulesync").description("Unified AI rules management CLI tool").version("0.49.0");
2364
2676
  program.command("init").description("Initialize rulesync in current directory").action(initCommand);
2365
2677
  program.command("add <filename>").description("Add a new rule file").action(addCommand);
2366
2678
  program.command("gitignore").description("Add generated files to .gitignore").action(gitignoreCommand);
2367
- program.command("import").description("Import configurations from AI tools to rulesync format").option("--claudecode", "Import from Claude Code (CLAUDE.md)").option("--cursor", "Import from Cursor (.cursorrules)").option("--copilot", "Import from GitHub Copilot (.github/copilot-instructions.md)").option("--cline", "Import from Cline (.cline/instructions.md)").option("--roo", "Import from Roo Code (.roo/instructions.md)").option("--geminicli", "Import from Gemini CLI (GEMINI.md)").option("-v, --verbose", "Verbose output").action(importCommand);
2368
- program.command("generate").description("Generate configuration files for AI tools").option("--copilot", "Generate only for GitHub Copilot").option("--cursor", "Generate only for Cursor").option("--cline", "Generate only for Cline").option("--claudecode", "Generate only for Claude Code").option("--roo", "Generate only for Roo Code").option("--geminicli", "Generate only for Gemini CLI").option("--kiro", "Generate only for Kiro IDE").option("--delete", "Delete all existing files in output directories before generating").option(
2679
+ program.command("import").description("Import configurations from AI tools to rulesync format").option("--augmentcode", "Import from AugmentCode (.augment/rules/)").option("--augmentcodeLegacy", "Import from AugmentCode legacy format (.augment-guidelines)").option("--claudecode", "Import from Claude Code (CLAUDE.md)").option("--cursor", "Import from Cursor (.cursorrules)").option("--copilot", "Import from GitHub Copilot (.github/copilot-instructions.md)").option("--cline", "Import from Cline (.cline/instructions.md)").option("--roo", "Import from Roo Code (.roo/instructions.md)").option("--geminicli", "Import from Gemini CLI (GEMINI.md)").option("-v, --verbose", "Verbose output").action(importCommand);
2680
+ program.command("generate").description("Generate configuration files for AI tools").option("--augmentcode", "Generate only for AugmentCode").option("--augmentcodeLegacy", "Generate only for AugmentCode legacy format").option("--copilot", "Generate only for GitHub Copilot").option("--cursor", "Generate only for Cursor").option("--cline", "Generate only for Cline").option("--claudecode", "Generate only for Claude Code").option("--roo", "Generate only for Roo Code").option("--geminicli", "Generate only for Gemini CLI").option("--kiro", "Generate only for Kiro IDE").option("--delete", "Delete all existing files in output directories before generating").option(
2369
2681
  "-b, --base-dir <paths>",
2370
2682
  "Base directories to generate files (comma-separated for multiple paths)"
2371
2683
  ).option("-v, --verbose", "Verbose output").action(async (options) => {
2372
2684
  const tools = [];
2685
+ if (options.augmentcode) tools.push("augmentcode");
2686
+ if (options.augmentcodeLegacy) tools.push("augmentcode-legacy");
2373
2687
  if (options.copilot) tools.push("copilot");
2374
2688
  if (options.cursor) tools.push("cursor");
2375
2689
  if (options.cline) tools.push("cline");