rulesync 0.63.0 → 0.64.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.
Files changed (33) hide show
  1. package/README.md +15 -3
  2. package/dist/amazonqcli-WVGYACHI.js +9 -0
  3. package/dist/{augmentcode-HIZIQG2W.js → augmentcode-DTHPPXWO.js} +2 -2
  4. package/dist/{chunk-M7NL7G7A.js → chunk-4NAQ5HL4.js} +1 -1
  5. package/dist/{chunk-GQTMTBX4.js → chunk-6LSN7HSJ.js} +76 -4
  6. package/dist/chunk-DMTCLQ4T.js +17 -0
  7. package/dist/{chunk-YTU3SCQO.js → chunk-E2J3UBBK.js} +9 -3
  8. package/dist/{chunk-LXTA7DBA.js → chunk-EID75W45.js} +1 -1
  9. package/dist/{chunk-U4PLVMCG.js → chunk-FVPZQEWP.js} +1 -1
  10. package/dist/{chunk-NETSYSMD.js → chunk-HHJIL3YZ.js} +1 -1
  11. package/dist/{chunk-UEAYL4NT.js → chunk-JX55DU6Y.js} +1 -1
  12. package/dist/{chunk-KUGTKMNW.js → chunk-KKWJVA56.js} +5 -2
  13. package/dist/chunk-LURFNGH4.js +17 -0
  14. package/dist/{chunk-AUUSMVCT.js → chunk-LYVES5YR.js} +2 -0
  15. package/dist/{chunk-4PSTOKKD.js → chunk-TBXG53FV.js} +1 -1
  16. package/dist/{chunk-2CW2KFB3.js → chunk-TQOL7OKY.js} +1 -1
  17. package/dist/chunk-YPJW7Z5M.js +210 -0
  18. package/dist/{claudecode-YTEFACCT.js → claudecode-SSYLLUXX.js} +3 -3
  19. package/dist/{cline-CKNUDEA3.js → cline-5EUGKNZ6.js} +3 -3
  20. package/dist/{codexcli-7SDGYI7D.js → codexcli-IGM2ADYK.js} +3 -3
  21. package/dist/{copilot-MOR3HHJX.js → copilot-HSQO7ZCJ.js} +2 -2
  22. package/dist/{cursor-YJGH7W24.js → cursor-ZB3XNGBK.js} +3 -3
  23. package/dist/{geminicli-E7KZTZ2G.js → geminicli-FNRKH5GX.js} +3 -3
  24. package/dist/index.cjs +912 -504
  25. package/dist/index.js +647 -482
  26. package/dist/{junie-5LEQU4BO.js → junie-3YGOSOGF.js} +3 -3
  27. package/dist/{kiro-YDHXY2MA.js → kiro-B6WZNLY4.js} +2 -2
  28. package/dist/opencode-SZETJ62M.js +17 -0
  29. package/dist/{roo-L3QTTIPO.js → roo-KLTWVAKE.js} +3 -2
  30. package/dist/{windsurf-4P6HEUBV.js → windsurf-IZEKUAID.js} +3 -3
  31. package/package.json +2 -1
  32. package/dist/chunk-MDYDKNXQ.js +0 -61
  33. package/dist/chunk-PCATT4UZ.js +0 -78
package/dist/index.js CHANGED
@@ -1,23 +1,35 @@
1
1
  #!/usr/bin/env node
2
- import "./chunk-M7NL7G7A.js";
3
- import "./chunk-LXTA7DBA.js";
4
- import "./chunk-PCATT4UZ.js";
5
- import "./chunk-NETSYSMD.js";
6
- import "./chunk-YTU3SCQO.js";
7
- import "./chunk-U4PLVMCG.js";
8
- import "./chunk-UEAYL4NT.js";
9
- import "./chunk-2CW2KFB3.js";
10
- import "./chunk-KUGTKMNW.js";
11
- import "./chunk-4PSTOKKD.js";
12
- import "./chunk-MDYDKNXQ.js";
13
- import "./chunk-GQTMTBX4.js";
2
+ import "./chunk-DMTCLQ4T.js";
3
+ import "./chunk-4NAQ5HL4.js";
4
+ import "./chunk-EID75W45.js";
5
+ import "./chunk-LURFNGH4.js";
6
+ import "./chunk-HHJIL3YZ.js";
7
+ import {
8
+ ensureDir,
9
+ fileExists,
10
+ findFiles,
11
+ findRuleFiles,
12
+ logger,
13
+ readFileContent,
14
+ removeClaudeGeneratedFiles,
15
+ removeDirectory,
16
+ resolvePath,
17
+ writeFileContent
18
+ } from "./chunk-YPJW7Z5M.js";
19
+ import "./chunk-E2J3UBBK.js";
20
+ import "./chunk-FVPZQEWP.js";
21
+ import "./chunk-JX55DU6Y.js";
22
+ import "./chunk-TQOL7OKY.js";
23
+ import "./chunk-KKWJVA56.js";
24
+ import "./chunk-TBXG53FV.js";
25
+ import "./chunk-6LSN7HSJ.js";
14
26
  import {
15
27
  ALL_TOOL_TARGETS,
16
28
  RulesyncTargetsSchema,
17
29
  ToolTargetSchema,
18
30
  ToolTargetsSchema,
19
31
  isToolTarget
20
- } from "./chunk-AUUSMVCT.js";
32
+ } from "./chunk-LYVES5YR.js";
21
33
 
22
34
  // src/cli/index.ts
23
35
  import { Command } from "commander";
@@ -70,6 +82,7 @@ var ConfigSchema = z3.object({
70
82
  // src/types/config-options.ts
71
83
  import { z as z4 } from "zod/mini";
72
84
  var OutputPathsSchema = z4.object({
85
+ amazonqcli: z4.optional(z4.string()),
73
86
  augmentcode: z4.optional(z4.string()),
74
87
  "augmentcode-legacy": z4.optional(z4.string()),
75
88
  copilot: z4.optional(z4.string()),
@@ -77,6 +90,7 @@ var OutputPathsSchema = z4.object({
77
90
  cline: z4.optional(z4.string()),
78
91
  claudecode: z4.optional(z4.string()),
79
92
  codexcli: z4.optional(z4.string()),
93
+ opencode: z4.optional(z4.string()),
80
94
  roo: z4.optional(z4.string()),
81
95
  geminicli: z4.optional(z4.string()),
82
96
  kiro: z4.optional(z4.string()),
@@ -127,7 +141,7 @@ var MergedConfigSchema = z4.object({
127
141
  import { z as z5 } from "zod/mini";
128
142
  var McpTransportTypeSchema = z5.enum(["stdio", "sse", "http"]);
129
143
  var McpServerBaseSchema = z5.object({
130
- command: z5.optional(z5.string()),
144
+ command: z5.optional(z5.union([z5.string(), z5.array(z5.string())])),
131
145
  args: z5.optional(z5.array(z5.string())),
132
146
  url: z5.optional(z5.string()),
133
147
  httpUrl: z5.optional(z5.string()),
@@ -178,6 +192,7 @@ function getDefaultConfig() {
178
192
  return {
179
193
  aiRulesDir: ".rulesync",
180
194
  outputPaths: {
195
+ amazonqcli: ".amazonq/rules",
181
196
  augmentcode: ".",
182
197
  "augmentcode-legacy": ".",
183
198
  copilot: ".github/instructions",
@@ -185,6 +200,7 @@ function getDefaultConfig() {
185
200
  cline: ".clinerules",
186
201
  claudecode: ".",
187
202
  codexcli: ".",
203
+ opencode: ".",
188
204
  roo: ".roo/rules",
189
205
  geminicli: ".gemini/memories",
190
206
  kiro: ".kiro/steering",
@@ -391,50 +407,6 @@ function mergeWithCliOptions(config, cliOptions) {
391
407
  return merged;
392
408
  }
393
409
 
394
- // src/utils/logger.ts
395
- import { consola } from "consola";
396
- var Logger = class {
397
- _verbose = false;
398
- console = consola.withDefaults({
399
- tag: "rulesync"
400
- });
401
- setVerbose(verbose) {
402
- this._verbose = verbose;
403
- }
404
- get verbose() {
405
- return this._verbose;
406
- }
407
- // Regular log (always shown, regardless of verbose)
408
- log(message, ...args) {
409
- this.console.log(message, ...args);
410
- }
411
- // Info level (shown only in verbose mode)
412
- info(message, ...args) {
413
- if (this._verbose) {
414
- this.console.info(message, ...args);
415
- }
416
- }
417
- // Success (always shown)
418
- success(message, ...args) {
419
- this.console.success(message, ...args);
420
- }
421
- // Warning (always shown)
422
- warn(message, ...args) {
423
- this.console.warn(message, ...args);
424
- }
425
- // Error (always shown)
426
- error(message, ...args) {
427
- this.console.error(message, ...args);
428
- }
429
- // Debug level (shown only in verbose mode)
430
- debug(message, ...args) {
431
- if (this._verbose) {
432
- this.console.debug(message, ...args);
433
- }
434
- }
435
- };
436
- var logger = new Logger();
437
-
438
410
  // src/cli/commands/add.ts
439
411
  function sanitizeFilename(filename) {
440
412
  return filename.endsWith(".md") ? filename.slice(0, -3) : filename;
@@ -508,89 +480,6 @@ async function safeAsyncOperation(operation, errorContext) {
508
480
  }
509
481
  }
510
482
 
511
- // src/utils/file.ts
512
- import { mkdir as mkdir2, readdir, readFile, rm, stat, writeFile as writeFile2 } from "fs/promises";
513
- import { dirname, join as join2 } from "path";
514
- async function ensureDir(dirPath) {
515
- try {
516
- await stat(dirPath);
517
- } catch {
518
- await mkdir2(dirPath, { recursive: true });
519
- }
520
- }
521
- function resolvePath(relativePath, baseDir) {
522
- return baseDir ? join2(baseDir, relativePath) : relativePath;
523
- }
524
- async function readFileContent(filepath) {
525
- return readFile(filepath, "utf-8");
526
- }
527
- async function writeFileContent(filepath, content) {
528
- await ensureDir(dirname(filepath));
529
- await writeFile2(filepath, content, "utf-8");
530
- }
531
- async function fileExists(filepath) {
532
- try {
533
- await stat(filepath);
534
- return true;
535
- } catch {
536
- return false;
537
- }
538
- }
539
- async function findFiles(dir, extension = ".md") {
540
- try {
541
- const files = await readdir(dir);
542
- return files.filter((file) => file.endsWith(extension)).map((file) => join2(dir, file));
543
- } catch {
544
- return [];
545
- }
546
- }
547
- async function findRuleFiles(aiRulesDir) {
548
- const rulesDir = join2(aiRulesDir, "rules");
549
- const newLocationFiles = await findFiles(rulesDir, ".md");
550
- const legacyLocationFiles = await findFiles(aiRulesDir, ".md");
551
- const newLocationBasenames = new Set(
552
- newLocationFiles.map((file) => file.split("/").pop()?.replace(/\.md$/, ""))
553
- );
554
- const filteredLegacyFiles = legacyLocationFiles.filter((file) => {
555
- const basename6 = file.split("/").pop()?.replace(/\.md$/, "");
556
- return !newLocationBasenames.has(basename6);
557
- });
558
- return [...newLocationFiles, ...filteredLegacyFiles];
559
- }
560
- async function removeDirectory(dirPath) {
561
- const dangerousPaths = [".", "/", "~", "src", "node_modules"];
562
- if (dangerousPaths.includes(dirPath) || dirPath === "") {
563
- logger.warn(`Skipping deletion of dangerous path: ${dirPath}`);
564
- return;
565
- }
566
- try {
567
- if (await fileExists(dirPath)) {
568
- await rm(dirPath, { recursive: true, force: true });
569
- }
570
- } catch (error) {
571
- logger.warn(`Failed to remove directory ${dirPath}:`, error);
572
- }
573
- }
574
- async function removeFile(filepath) {
575
- try {
576
- if (await fileExists(filepath)) {
577
- await rm(filepath);
578
- }
579
- } catch (error) {
580
- logger.warn(`Failed to remove file ${filepath}:`, error);
581
- }
582
- }
583
- async function removeClaudeGeneratedFiles() {
584
- const filesToRemove = ["CLAUDE.md", ".claude/memories"];
585
- for (const fileOrDir of filesToRemove) {
586
- if (fileOrDir.endsWith("/memories")) {
587
- await removeDirectory(fileOrDir);
588
- } else {
589
- await removeFile(fileOrDir);
590
- }
591
- }
592
- }
593
-
594
483
  // src/cli/commands/config.ts
595
484
  async function configCommand(options = {}) {
596
485
  if (options.init) {
@@ -802,25 +691,25 @@ export default config;
802
691
  }
803
692
 
804
693
  // src/cli/commands/generate.ts
805
- import { join as join13 } from "path";
694
+ import { join as join12 } from "path";
806
695
 
807
696
  // src/core/command-generator.ts
808
- import { join as join4 } from "path";
697
+ import { join as join3 } from "path";
809
698
 
810
699
  // src/utils/command-generators.ts
811
- import { join as join3 } from "path";
812
- function generateYamlFrontmatter(command, options = {}) {
813
- const frontmatter = ["---"];
814
- if (options.includeDescription !== false && command.frontmatter.description) {
815
- frontmatter.push(`description: ${command.frontmatter.description}`);
700
+ import { join as join2 } from "path";
701
+ function generateYamlFrontmatter(command, options) {
702
+ const frontmatterLines = ["---"];
703
+ if (options?.includeDescription && command.frontmatter.description) {
704
+ frontmatterLines.push(`description: ${command.frontmatter.description}`);
816
705
  }
817
- if (options.additionalFields) {
706
+ if (options?.additionalFields) {
818
707
  for (const field of options.additionalFields) {
819
- frontmatter.push(`${field.key}: ${field.value}`);
708
+ frontmatterLines.push(`${field.key}: ${field.value}`);
820
709
  }
821
710
  }
822
- frontmatter.push("---");
823
- return frontmatter;
711
+ frontmatterLines.push("---");
712
+ return frontmatterLines;
824
713
  }
825
714
  function buildCommandContent(command, frontmatterOptions) {
826
715
  const frontmatter = generateYamlFrontmatter(command, frontmatterOptions);
@@ -831,12 +720,12 @@ ${command.content.trim()}
831
720
  }
832
721
  function getFlattenedCommandPath(filename, baseDir, subdir) {
833
722
  const flattenedName = filename.replace(/\//g, "-");
834
- return join3(baseDir, subdir, `${flattenedName}.md`);
723
+ return join2(baseDir, subdir, `${flattenedName}.md`);
835
724
  }
836
725
  function getHierarchicalCommandPath(filename, baseDir, subdir, extension = "md") {
837
726
  const nameWithoutExt = filename.replace(/\.[^/.]+$/, "");
838
727
  const fileWithExt = `${nameWithoutExt}.${extension}`;
839
- return join3(baseDir, subdir, fileWithExt);
728
+ return join2(baseDir, subdir, fileWithExt);
840
729
  }
841
730
  function escapeTomlString(str) {
842
731
  return str.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/\t/g, "\\t");
@@ -859,26 +748,75 @@ var syntaxConverters = {
859
748
  }
860
749
  };
861
750
 
862
- // src/generators/commands/claudecode.ts
863
- var ClaudeCodeCommandGenerator = class {
751
+ // src/generators/commands/base.ts
752
+ var BaseCommandGenerator = class {
753
+ /**
754
+ * Generate command output for the specified tool
755
+ */
864
756
  generate(command, outputDir) {
865
757
  const filepath = this.getOutputPath(command.filename, outputDir);
866
- const content = buildCommandContent(command);
758
+ const content = this.processContent(command);
867
759
  return {
868
- tool: "claudecode",
760
+ tool: this.getToolName(),
869
761
  filepath,
870
762
  content
871
763
  };
872
764
  }
765
+ /**
766
+ * Get the output path for the command file
767
+ * Override this method if custom path logic is needed
768
+ */
873
769
  getOutputPath(filename, baseDir) {
874
- return getFlattenedCommandPath(filename, baseDir, ".claude/commands");
770
+ if (this.supportsHierarchy()) {
771
+ return getHierarchicalCommandPath(
772
+ filename,
773
+ baseDir,
774
+ this.getCommandsDirectory(),
775
+ this.getFileExtension()
776
+ );
777
+ } else {
778
+ return getFlattenedCommandPath(filename, baseDir, this.getCommandsDirectory());
779
+ }
875
780
  }
781
+ /**
782
+ * Whether this tool supports hierarchical directory structure
783
+ * Override to return true for tools that support nested commands
784
+ */
785
+ supportsHierarchy() {
786
+ return false;
787
+ }
788
+ /**
789
+ * Get file extension for the target tool
790
+ * Override if tool uses different extension than .md
791
+ */
792
+ getFileExtension() {
793
+ return "md";
794
+ }
795
+ };
796
+
797
+ // src/generators/commands/claudecode.ts
798
+ var ClaudeCodeCommandGenerator = class extends BaseCommandGenerator {
799
+ getToolName() {
800
+ return "claudecode";
801
+ }
802
+ getCommandsDirectory() {
803
+ return ".claude/commands";
804
+ }
805
+ processContent(command) {
806
+ return buildCommandContent(command, { includeDescription: true });
807
+ }
808
+ // Uses flattened structure by default (supportsHierarchy returns false)
876
809
  };
877
810
 
878
811
  // src/generators/commands/geminicli.ts
879
- var GeminiCliCommandGenerator = class {
880
- generate(command, outputDir) {
881
- const filepath = this.getOutputPath(command.filename, outputDir);
812
+ var GeminiCliCommandGenerator = class extends BaseCommandGenerator {
813
+ getToolName() {
814
+ return "geminicli";
815
+ }
816
+ getCommandsDirectory() {
817
+ return ".gemini/commands";
818
+ }
819
+ processContent(command) {
882
820
  const convertedContent = syntaxConverters.toGeminiCli(command.content);
883
821
  const tomlLines = [];
884
822
  if (command.frontmatter.description) {
@@ -886,32 +824,28 @@ var GeminiCliCommandGenerator = class {
886
824
  tomlLines.push("");
887
825
  }
888
826
  tomlLines.push(`prompt = """${convertedContent}"""`);
889
- const content = tomlLines.join("\n") + "\n";
890
- return {
891
- tool: "geminicli",
892
- filepath,
893
- content
894
- };
827
+ return tomlLines.join("\n") + "\n";
895
828
  }
896
- getOutputPath(filename, baseDir) {
897
- return getHierarchicalCommandPath(filename, baseDir, ".gemini/commands", "toml");
829
+ supportsHierarchy() {
830
+ return true;
831
+ }
832
+ getFileExtension() {
833
+ return "toml";
898
834
  }
899
835
  };
900
836
 
901
837
  // src/generators/commands/roo.ts
902
- var RooCommandGenerator = class {
903
- generate(command, outputDir) {
904
- const filepath = this.getOutputPath(command.filename, outputDir);
905
- const content = buildCommandContent(command);
906
- return {
907
- tool: "roo",
908
- filepath,
909
- content
910
- };
838
+ var RooCommandGenerator = class extends BaseCommandGenerator {
839
+ getToolName() {
840
+ return "roo";
911
841
  }
912
- getOutputPath(filename, baseDir) {
913
- return getFlattenedCommandPath(filename, baseDir, ".roo/commands");
842
+ getCommandsDirectory() {
843
+ return ".roo/commands";
844
+ }
845
+ processContent(command) {
846
+ return buildCommandContent(command, { includeDescription: true });
914
847
  }
848
+ // Uses flattened structure by default (supportsHierarchy returns false)
915
849
  };
916
850
 
917
851
  // src/generators/commands/index.ts
@@ -988,7 +922,7 @@ async function parseCommandFile(filepath) {
988
922
 
989
923
  // src/core/command-generator.ts
990
924
  async function generateCommands(projectRoot, baseDir, targets) {
991
- const commandsDir = join4(projectRoot, ".rulesync", "commands");
925
+ const commandsDir = join3(projectRoot, ".rulesync", "commands");
992
926
  if (!await fileExists(commandsDir)) {
993
927
  return [];
994
928
  }
@@ -1022,7 +956,7 @@ async function generateCommands(projectRoot, baseDir, targets) {
1022
956
  }
1023
957
 
1024
958
  // src/generators/ignore/shared-factory.ts
1025
- import { join as join5 } from "path";
959
+ import { join as join4 } from "path";
1026
960
 
1027
961
  // src/generators/ignore/shared-helpers.ts
1028
962
  function extractIgnorePatternsFromRules(rules) {
@@ -1145,7 +1079,7 @@ function generateIgnoreFile(rules, config, ignoreConfig, baseDir) {
1145
1079
  const outputs = [];
1146
1080
  const content = generateIgnoreContent(rules, ignoreConfig);
1147
1081
  const outputPath = baseDir || process.cwd();
1148
- const filepath = join5(outputPath, ignoreConfig.filename);
1082
+ const filepath = join4(outputPath, ignoreConfig.filename);
1149
1083
  outputs.push({
1150
1084
  tool: ignoreConfig.tool,
1151
1085
  filepath,
@@ -1732,21 +1666,18 @@ function generateWindsurfIgnore(rules, config, baseDir) {
1732
1666
  return generateIgnoreFile(rules, config, ignoreConfigs.windsurf, baseDir);
1733
1667
  }
1734
1668
 
1735
- // src/generators/rules/augmentcode.ts
1736
- import { join as join8 } from "path";
1737
-
1738
1669
  // src/generators/rules/shared-helpers.ts
1739
- import { join as join7 } from "path";
1670
+ import { join as join6 } from "path";
1740
1671
 
1741
1672
  // src/utils/ignore.ts
1742
- import { join as join6 } from "path";
1673
+ import { join as join5 } from "path";
1743
1674
  import micromatch from "micromatch";
1744
1675
  var cachedIgnorePatterns = null;
1745
1676
  async function loadIgnorePatterns(baseDir = process.cwd()) {
1746
1677
  if (cachedIgnorePatterns) {
1747
1678
  return cachedIgnorePatterns;
1748
1679
  }
1749
- const ignorePath = join6(baseDir, ".rulesyncignore");
1680
+ const ignorePath = join5(baseDir, ".rulesyncignore");
1750
1681
  if (!await fileExists(ignorePath)) {
1751
1682
  cachedIgnorePatterns = { patterns: [] };
1752
1683
  return cachedIgnorePatterns;
@@ -1800,7 +1731,7 @@ function addOutput(outputs, tool, config, baseDir, relativePath, content) {
1800
1731
  const outputDir = resolveOutputDir(config, tool, baseDir);
1801
1732
  outputs.push({
1802
1733
  tool,
1803
- filepath: join7(outputDir, relativePath),
1734
+ filepath: join6(outputDir, relativePath),
1804
1735
  content
1805
1736
  });
1806
1737
  }
@@ -1809,7 +1740,7 @@ async function generateRulesConfig(rules, config, generatorConfig, baseDir) {
1809
1740
  for (const rule of rules) {
1810
1741
  const content = generatorConfig.generateContent(rule);
1811
1742
  const outputDir = resolveOutputDir(config, generatorConfig.tool, baseDir);
1812
- const filepath = generatorConfig.pathResolver ? generatorConfig.pathResolver(rule, outputDir) : join7(outputDir, `${rule.filename}${generatorConfig.fileExtension}`);
1743
+ const filepath = generatorConfig.pathResolver ? generatorConfig.pathResolver(rule, outputDir) : join6(outputDir, `${rule.filename}${generatorConfig.fileExtension}`);
1813
1744
  outputs.push({
1814
1745
  tool: generatorConfig.tool,
1815
1746
  filepath,
@@ -1837,7 +1768,7 @@ async function generateComplexRules(rules, config, generatorConfig, baseDir) {
1837
1768
  for (const rule of detailRules) {
1838
1769
  const content = generatorConfig.generateDetailContent(rule);
1839
1770
  const filepath = resolvePath(
1840
- join7(generatorConfig.detailSubDir, `${rule.filename}.md`),
1771
+ join6(generatorConfig.detailSubDir, `${rule.filename}.md`),
1841
1772
  baseDir
1842
1773
  );
1843
1774
  outputs.push({
@@ -1891,7 +1822,61 @@ function generateIgnoreFile2(patterns, tool) {
1891
1822
  return lines.join("\n");
1892
1823
  }
1893
1824
 
1825
+ // src/generators/rules/amazonqcli.ts
1826
+ async function generateAmazonqcliConfig(rules, config, baseDir) {
1827
+ const generatorConfig = {
1828
+ tool: "amazonqcli",
1829
+ fileExtension: ".md",
1830
+ generateContent: generateRuleFile,
1831
+ generateRootContent: generateMainRulesFile,
1832
+ rootFilePath: ".amazonq/rules/main.md",
1833
+ generateDetailContent: generateRuleFile,
1834
+ detailSubDir: ".amazonq/rules"
1835
+ };
1836
+ return generateComplexRules(rules, config, generatorConfig, baseDir);
1837
+ }
1838
+ function generateMainRulesFile(rootRule, detailRules) {
1839
+ const lines = [];
1840
+ if (detailRules.length > 0) {
1841
+ lines.push("# Amazon Q Developer CLI Project Rules");
1842
+ lines.push("");
1843
+ lines.push("This file contains the main project rules. See also:");
1844
+ lines.push("");
1845
+ for (const rule of detailRules) {
1846
+ lines.push(`- ${rule.filename}.md: ${rule.frontmatter.description}`);
1847
+ }
1848
+ lines.push("");
1849
+ }
1850
+ if (rootRule) {
1851
+ if (detailRules.length > 0) {
1852
+ lines.push("## Overview");
1853
+ lines.push("");
1854
+ }
1855
+ lines.push(rootRule.content);
1856
+ lines.push("");
1857
+ } else if (detailRules.length === 0) {
1858
+ lines.push("# Amazon Q Developer CLI Project Rules");
1859
+ lines.push("");
1860
+ lines.push("This file contains project-specific rules and context for Amazon Q Developer CLI.");
1861
+ lines.push("");
1862
+ lines.push("## Development Standards");
1863
+ lines.push("");
1864
+ lines.push("Add your project-specific development standards here.");
1865
+ lines.push("");
1866
+ }
1867
+ return lines.join("\n").trim() + "\n";
1868
+ }
1869
+ function generateRuleFile(rule) {
1870
+ const lines = [];
1871
+ lines.push(`# ${rule.frontmatter.description || rule.filename}`);
1872
+ lines.push("");
1873
+ lines.push(rule.content.trim());
1874
+ lines.push("");
1875
+ return lines.join("\n");
1876
+ }
1877
+
1894
1878
  // src/generators/rules/augmentcode.ts
1879
+ import { join as join7 } from "path";
1895
1880
  async function generateAugmentcodeConfig(rules, config, baseDir) {
1896
1881
  const outputs = createOutputsArray();
1897
1882
  rules.forEach((rule) => {
@@ -1900,13 +1885,13 @@ async function generateAugmentcodeConfig(rules, config, baseDir) {
1900
1885
  "augmentcode",
1901
1886
  config,
1902
1887
  baseDir,
1903
- join8(".augment", "rules", `${rule.filename}.md`),
1904
- generateRuleFile(rule)
1888
+ join7(".augment", "rules", `${rule.filename}.md`),
1889
+ generateRuleFile2(rule)
1905
1890
  );
1906
1891
  });
1907
1892
  return outputs;
1908
1893
  }
1909
- function generateRuleFile(rule) {
1894
+ function generateRuleFile2(rule) {
1910
1895
  const lines = [];
1911
1896
  lines.push("---");
1912
1897
  let ruleType = "manual";
@@ -1953,7 +1938,7 @@ function generateLegacyGuidelinesFile(allRules) {
1953
1938
  }
1954
1939
 
1955
1940
  // src/generators/rules/claudecode.ts
1956
- import { join as join9 } from "path";
1941
+ import { join as join8 } from "path";
1957
1942
  async function generateClaudecodeConfig(rules, config, baseDir) {
1958
1943
  const generatorConfig = {
1959
1944
  tool: "claudecode",
@@ -1965,7 +1950,7 @@ async function generateClaudecodeConfig(rules, config, baseDir) {
1965
1950
  generateDetailContent: generateMemoryFile,
1966
1951
  detailSubDir: ".claude/memories",
1967
1952
  updateAdditionalConfig: async (ignorePatterns, baseDir2) => {
1968
- const settingsPath = resolvePath(join9(".claude", "settings.json"), baseDir2);
1953
+ const settingsPath = resolvePath(join8(".claude", "settings.json"), baseDir2);
1969
1954
  await updateClaudeSettings(settingsPath, ignorePatterns);
1970
1955
  return [];
1971
1956
  }
@@ -2029,7 +2014,7 @@ async function updateClaudeSettings(settingsPath, ignorePatterns) {
2029
2014
  }
2030
2015
 
2031
2016
  // src/generators/rules/generator-registry.ts
2032
- import { join as join10 } from "path";
2017
+ import { join as join9 } from "path";
2033
2018
  function determineCursorRuleType(frontmatter) {
2034
2019
  if (frontmatter.cursorRuleType) {
2035
2020
  return frontmatter.cursorRuleType;
@@ -2053,6 +2038,22 @@ function determineCursorRuleType(frontmatter) {
2053
2038
  }
2054
2039
  var GENERATOR_REGISTRY = {
2055
2040
  // Simple generators - generate one file per rule
2041
+ amazonqcli: {
2042
+ type: "complex",
2043
+ tool: "amazonqcli",
2044
+ fileExtension: ".md",
2045
+ // ignoreFileName omitted - Amazon Q CLI doesn't have native ignore file support yet
2046
+ generateContent: (rule) => {
2047
+ const lines = [];
2048
+ if (rule.frontmatter.description) {
2049
+ lines.push(`# ${rule.frontmatter.description}
2050
+ `);
2051
+ }
2052
+ lines.push(rule.content.trim());
2053
+ return lines.join("\n");
2054
+ }
2055
+ // Complex generation handled by existing generator
2056
+ },
2056
2057
  cline: {
2057
2058
  type: "simple",
2058
2059
  tool: "cline",
@@ -2109,7 +2110,7 @@ var GENERATOR_REGISTRY = {
2109
2110
  },
2110
2111
  pathResolver: (rule, outputDir) => {
2111
2112
  const baseFilename = rule.filename.replace(/\.md$/, "");
2112
- return join10(outputDir, `${baseFilename}.instructions.md`);
2113
+ return join9(outputDir, `${baseFilename}.instructions.md`);
2113
2114
  }
2114
2115
  },
2115
2116
  cursor: {
@@ -2149,7 +2150,7 @@ var GENERATOR_REGISTRY = {
2149
2150
  return lines.join("\n");
2150
2151
  },
2151
2152
  pathResolver: (rule, outputDir) => {
2152
- return join10(outputDir, `${rule.filename}.mdc`);
2153
+ return join9(outputDir, `${rule.filename}.mdc`);
2153
2154
  }
2154
2155
  },
2155
2156
  codexcli: {
@@ -2185,10 +2186,10 @@ var GENERATOR_REGISTRY = {
2185
2186
  pathResolver: (rule, outputDir) => {
2186
2187
  const outputFormat = rule.frontmatter.windsurfOutputFormat || "directory";
2187
2188
  if (outputFormat === "single-file") {
2188
- return join10(outputDir, ".windsurf-rules");
2189
+ return join9(outputDir, ".windsurf-rules");
2189
2190
  } else {
2190
- const rulesDir = join10(outputDir, ".windsurf", "rules");
2191
- return join10(rulesDir, `${rule.filename}.md`);
2191
+ const rulesDir = join9(outputDir, ".windsurf", "rules");
2192
+ return join9(rulesDir, `${rule.filename}.md`);
2192
2193
  }
2193
2194
  }
2194
2195
  },
@@ -2219,6 +2220,22 @@ var GENERATOR_REGISTRY = {
2219
2220
  const lines = [];
2220
2221
  if (rule.frontmatter.description) {
2221
2222
  lines.push(`# ${rule.frontmatter.description}
2223
+ `);
2224
+ }
2225
+ lines.push(rule.content.trim());
2226
+ return lines.join("\n");
2227
+ }
2228
+ // Complex generation handled by existing generator
2229
+ },
2230
+ opencode: {
2231
+ type: "complex",
2232
+ tool: "opencode",
2233
+ fileExtension: ".md",
2234
+ // ignoreFileName omitted - OpenCode doesn't use dedicated ignore files
2235
+ generateContent: (rule) => {
2236
+ const lines = [];
2237
+ if (rule.frontmatter.description) {
2238
+ lines.push(`# ${rule.frontmatter.description}
2222
2239
  `);
2223
2240
  }
2224
2241
  lines.push(rule.content.trim());
@@ -2261,8 +2278,8 @@ async function generateFromRegistry(tool, rules, config, baseDir) {
2261
2278
  const enhancedConfig = {
2262
2279
  tool: generatorConfig.tool,
2263
2280
  fileExtension: generatorConfig.fileExtension,
2264
- ignoreFileName: generatorConfig.ignoreFileName,
2265
2281
  generateContent: generatorConfig.generateContent,
2282
+ ...generatorConfig.ignoreFileName && { ignoreFileName: generatorConfig.ignoreFileName },
2266
2283
  ...generatorConfig.generateRootContent && {
2267
2284
  generateRootContent: generatorConfig.generateRootContent
2268
2285
  },
@@ -2292,49 +2309,89 @@ var generateWindsurfConfig = createSimpleGenerator("windsurf");
2292
2309
  var generateKiroConfig = createSimpleGenerator("kiro");
2293
2310
  var generateRooConfig = createSimpleGenerator("roo");
2294
2311
 
2312
+ // src/utils/xml-document-generator.ts
2313
+ import { XMLBuilder } from "fast-xml-parser";
2314
+ function generateRootMarkdownWithXmlDocs(rootRule, memoryRules, config) {
2315
+ const lines = [];
2316
+ if (memoryRules.length > 0) {
2317
+ lines.push(
2318
+ "Please also reference the following documents as needed. In this case, `@` stands for the project root directory."
2319
+ );
2320
+ lines.push("");
2321
+ const documentsData = {
2322
+ Documents: {
2323
+ Document: memoryRules.map((rule) => {
2324
+ const relativePath = `@${config.memorySubDir}/${rule.filename}.md`;
2325
+ const document = {
2326
+ Path: relativePath,
2327
+ Description: rule.frontmatter.description
2328
+ };
2329
+ if (rule.frontmatter.globs.length > 0) {
2330
+ document.FilePatterns = rule.frontmatter.globs.join(", ");
2331
+ }
2332
+ return document;
2333
+ })
2334
+ }
2335
+ };
2336
+ const builder = new XMLBuilder({
2337
+ format: true,
2338
+ ignoreAttributes: false,
2339
+ suppressEmptyNode: false
2340
+ });
2341
+ const xmlContent = builder.build(documentsData);
2342
+ lines.push(xmlContent);
2343
+ lines.push("");
2344
+ lines.push("");
2345
+ }
2346
+ if (rootRule) {
2347
+ lines.push(rootRule.content.trim());
2348
+ } else if (memoryRules.length === 0) {
2349
+ lines.push(`# ${config.fallbackTitle}`);
2350
+ lines.push("");
2351
+ lines.push("No configuration rules have been defined yet.");
2352
+ }
2353
+ return lines.join("\n");
2354
+ }
2355
+
2295
2356
  // src/generators/rules/codexcli.ts
2296
2357
  async function generateCodexConfig(rules, config, baseDir) {
2297
2358
  const outputs = [];
2298
- if (rules.length === 0) {
2299
- return outputs;
2300
- }
2301
- const sortedRules = [...rules].sort((a, b) => {
2302
- if (a.frontmatter.root === true && b.frontmatter.root !== true) return -1;
2303
- if (a.frontmatter.root !== true && b.frontmatter.root === true) return 1;
2304
- return 0;
2305
- });
2306
- const concatenatedContent = generateConcatenatedCodexContent(sortedRules);
2307
- if (concatenatedContent.trim()) {
2308
- const outputDir = resolveOutputDir(config, "codexcli", baseDir);
2309
- const filepath = `${outputDir}/AGENTS.md`;
2310
- outputs.push({
2311
- tool: "codexcli",
2312
- filepath,
2313
- content: concatenatedContent
2314
- });
2315
- }
2316
- const ignorePatterns = await loadIgnorePatterns(baseDir);
2317
- if (ignorePatterns.patterns.length > 0) {
2318
- const ignorePath = resolvePath(".codexignore", baseDir);
2319
- const ignoreContent = generateIgnoreFile2(ignorePatterns.patterns, "codexcli");
2320
- outputs.push({
2359
+ const nonEmptyRules = rules.filter((rule) => rule.content.trim().length > 0);
2360
+ if (nonEmptyRules.length > 0) {
2361
+ const generatorConfig = {
2321
2362
  tool: "codexcli",
2322
- filepath: ignorePath,
2323
- content: ignoreContent
2324
- });
2363
+ fileExtension: ".md",
2364
+ ignoreFileName: ".codexignore",
2365
+ generateContent: generateCodexMemoryMarkdown,
2366
+ generateDetailContent: generateCodexMemoryMarkdown,
2367
+ generateRootContent: generateCodexRootMarkdown,
2368
+ rootFilePath: "AGENTS.md",
2369
+ detailSubDir: ".codex/memories"
2370
+ };
2371
+ const ruleOutputs = await generateComplexRules(nonEmptyRules, config, generatorConfig, baseDir);
2372
+ outputs.push(...ruleOutputs);
2373
+ } else {
2374
+ const ignorePatterns = await loadIgnorePatterns(baseDir);
2375
+ if (ignorePatterns.patterns.length > 0) {
2376
+ const ignorePath = resolvePath(".codexignore", baseDir);
2377
+ const ignoreContent = generateIgnoreFile2(ignorePatterns.patterns, "codexcli");
2378
+ outputs.push({
2379
+ tool: "codexcli",
2380
+ filepath: ignorePath,
2381
+ content: ignoreContent
2382
+ });
2383
+ }
2325
2384
  }
2326
2385
  return outputs;
2327
2386
  }
2328
- function generateConcatenatedCodexContent(rules) {
2329
- const sections = [];
2330
- for (const rule of rules) {
2331
- const content = rule.content.trim();
2332
- if (!content) {
2333
- continue;
2334
- }
2335
- sections.push(content);
2336
- }
2337
- return sections.join("\n\n---\n\n");
2387
+ function generateCodexMemoryMarkdown(rule) {
2388
+ return rule.content.trim();
2389
+ }
2390
+ function generateCodexRootMarkdown(rootRule, memoryRules, _baseDir) {
2391
+ return generateRootMarkdownWithXmlDocs(rootRule, memoryRules, {
2392
+ memorySubDir: ".codex/memories",
2393
+ fallbackTitle: "OpenAI Codex CLI Configuration"
2394
+ });
2338
2395
  }
2339
2396
 
2340
2397
  // src/generators/rules/geminicli.ts
@@ -2355,28 +2412,10 @@ function generateGeminiMemoryMarkdown(rule) {
2355
2412
  return rule.content.trim();
2356
2413
  }
2357
2414
  function generateGeminiRootMarkdown(rootRule, memoryRules, _baseDir) {
2358
- const lines = [];
2359
- if (memoryRules.length > 0) {
2360
- lines.push("Please also reference the following documents as needed:");
2361
- lines.push("");
2362
- lines.push("| Document | Description | File Patterns |");
2363
- lines.push("|----------|-------------|---------------|");
2364
- for (const rule of memoryRules) {
2365
- const relativePath = `@.gemini/memories/${rule.filename}.md`;
2366
- const filePatterns = rule.frontmatter.globs.length > 0 ? rule.frontmatter.globs.join(", ") : "-";
2367
- lines.push(`| ${relativePath} | ${rule.frontmatter.description} | ${filePatterns} |`);
2368
- }
2369
- lines.push("");
2370
- lines.push("");
2371
- }
2372
- if (rootRule) {
2373
- lines.push(rootRule.content.trim());
2374
- } else if (memoryRules.length === 0) {
2375
- lines.push("# Gemini CLI Configuration");
2376
- lines.push("");
2377
- lines.push("No configuration rules have been defined yet.");
2378
- }
2379
- return lines.join("\n");
2415
+ return generateRootMarkdownWithXmlDocs(rootRule, memoryRules, {
2416
+ memorySubDir: ".gemini/memories",
2417
+ fallbackTitle: "Gemini CLI Configuration"
2418
+ });
2380
2419
  }
2381
2420
 
2382
2421
  // src/generators/rules/junie.ts
@@ -2406,6 +2445,30 @@ function generateGuidelinesMarkdown(rootRule, detailRules) {
2406
2445
  return lines.join("\n").trim();
2407
2446
  }
2408
2447
 
2448
+ // src/generators/rules/opencode.ts
2449
+ async function generateOpenCodeConfig(rules, config, baseDir) {
2450
+ const generatorConfig = {
2451
+ tool: "opencode",
2452
+ fileExtension: ".md",
2453
+ // ignoreFileName omitted - OpenCode doesn't use dedicated ignore files
2454
+ generateContent: generateOpenCodeMarkdown,
2455
+ generateDetailContent: generateOpenCodeMarkdown,
2456
+ generateRootContent: generateOpenCodeRootMarkdown,
2457
+ rootFilePath: "AGENTS.md",
2458
+ detailSubDir: ".opencode/memories"
2459
+ };
2460
+ return generateComplexRules(rules, config, generatorConfig, baseDir);
2461
+ }
2462
+ function generateOpenCodeMarkdown(rule) {
2463
+ return rule.content.trim();
2464
+ }
2465
+ function generateOpenCodeRootMarkdown(rootRule, memoryRules, _baseDir) {
2466
+ return generateRootMarkdownWithXmlDocs(rootRule, memoryRules, {
2467
+ memorySubDir: ".opencode/memories",
2468
+ fallbackTitle: "OpenCode Configuration"
2469
+ });
2470
+ }
2471
+
2409
2472
  // src/core/generator.ts
2410
2473
  async function generateConfigurations(rules, config, targetTools, baseDir) {
2411
2474
  const outputs = createOutputsArray();
@@ -2438,6 +2501,8 @@ function filterRulesForTool(rules, tool, config) {
2438
2501
  }
2439
2502
  async function generateForTool(tool, rules, config, baseDir) {
2440
2503
  switch (tool) {
2504
+ case "amazonqcli":
2505
+ return await generateAmazonqcliConfig(rules, config, baseDir);
2441
2506
  case "augmentcode": {
2442
2507
  const augmentRulesOutputs = await generateAugmentcodeConfig(rules, config, baseDir);
2443
2508
  const augmentIgnoreOutputs = await generateAugmentCodeIgnoreFiles(rules, config, baseDir);
@@ -2476,6 +2541,8 @@ async function generateForTool(tool, rules, config, baseDir) {
2476
2541
  const kiroIgnoreOutputs = await generateKiroIgnoreFiles(rules, config, baseDir);
2477
2542
  return [...kiroRulesOutputs, ...kiroIgnoreOutputs];
2478
2543
  }
2544
+ case "opencode":
2545
+ return generateOpenCodeConfig(rules, config, baseDir);
2479
2546
  case "windsurf": {
2480
2547
  const windsurfRulesOutputs = await generateWindsurfConfig(rules, config, baseDir);
2481
2548
  const windsurfIgnoreOutputs = await generateWindsurfIgnore(rules, config, baseDir);
@@ -2639,30 +2706,63 @@ function parseMcpConfig(projectRoot) {
2639
2706
  async function generateMcpConfigurations(mcpConfig, baseDir, targetTools) {
2640
2707
  const outputs = [];
2641
2708
  const toolMap = {
2642
- augmentcode: async (servers, dir) => (await import("./augmentcode-HIZIQG2W.js")).generateAugmentcodeMcpConfiguration(
2709
+ amazonqcli: async (servers, dir) => {
2710
+ const config = {
2711
+ aiRulesDir: ".rulesync",
2712
+ outputPaths: {
2713
+ amazonqcli: ".amazonq/rules",
2714
+ augmentcode: ".",
2715
+ "augmentcode-legacy": ".",
2716
+ copilot: ".github/instructions",
2717
+ cursor: ".cursor/rules",
2718
+ cline: ".clinerules",
2719
+ claudecode: ".",
2720
+ codexcli: ".",
2721
+ opencode: ".",
2722
+ roo: ".roo/rules",
2723
+ geminicli: ".gemini/memories",
2724
+ kiro: ".kiro/steering",
2725
+ junie: ".",
2726
+ windsurf: "."
2727
+ },
2728
+ watchEnabled: false,
2729
+ defaultTargets: []
2730
+ };
2731
+ const results = await (await import("./amazonqcli-WVGYACHI.js")).generateAmazonqcliMcp(
2732
+ servers,
2733
+ config,
2734
+ dir
2735
+ );
2736
+ return results.map((result) => ({ filepath: result.filepath, content: result.content }));
2737
+ },
2738
+ augmentcode: async (servers, dir) => (await import("./augmentcode-DTHPPXWO.js")).generateAugmentcodeMcpConfiguration(
2739
+ servers,
2740
+ dir
2741
+ ),
2742
+ "augmentcode-legacy": async (servers, dir) => (await import("./augmentcode-DTHPPXWO.js")).generateAugmentcodeMcpConfiguration(
2643
2743
  servers,
2644
2744
  dir
2645
2745
  ),
2646
- "augmentcode-legacy": async (servers, dir) => (await import("./augmentcode-HIZIQG2W.js")).generateAugmentcodeMcpConfiguration(
2746
+ claudecode: async (servers, dir) => (await import("./claudecode-SSYLLUXX.js")).generateClaudeMcpConfiguration(
2647
2747
  servers,
2648
2748
  dir
2649
2749
  ),
2650
- claudecode: async (servers, dir) => (await import("./claudecode-YTEFACCT.js")).generateClaudeMcpConfiguration(
2750
+ copilot: async (servers, dir) => (await import("./copilot-HSQO7ZCJ.js")).generateCopilotMcpConfiguration(servers, dir),
2751
+ cursor: async (servers, dir) => (await import("./cursor-ZB3XNGBK.js")).generateCursorMcpConfiguration(servers, dir),
2752
+ cline: async (servers, dir) => (await import("./cline-5EUGKNZ6.js")).generateClineMcpConfiguration(servers, dir),
2753
+ codexcli: async (servers, dir) => (await import("./codexcli-IGM2ADYK.js")).generateCodexMcpConfiguration(servers, dir),
2754
+ opencode: async (servers, dir) => (await import("./opencode-SZETJ62M.js")).generateOpenCodeMcpConfiguration(
2651
2755
  servers,
2652
2756
  dir
2653
2757
  ),
2654
- copilot: async (servers, dir) => (await import("./copilot-MOR3HHJX.js")).generateCopilotMcpConfiguration(servers, dir),
2655
- cursor: async (servers, dir) => (await import("./cursor-YJGH7W24.js")).generateCursorMcpConfiguration(servers, dir),
2656
- cline: async (servers, dir) => (await import("./cline-CKNUDEA3.js")).generateClineMcpConfiguration(servers, dir),
2657
- codexcli: async (servers, dir) => (await import("./codexcli-7SDGYI7D.js")).generateCodexMcpConfiguration(servers, dir),
2658
- roo: async (servers, dir) => (await import("./roo-L3QTTIPO.js")).generateRooMcpConfiguration(servers, dir),
2659
- geminicli: async (servers, dir) => (await import("./geminicli-E7KZTZ2G.js")).generateGeminiCliMcpConfiguration(
2758
+ roo: async (servers, dir) => (await import("./roo-KLTWVAKE.js")).generateRooMcpConfiguration(servers, dir),
2759
+ geminicli: async (servers, dir) => (await import("./geminicli-FNRKH5GX.js")).generateGeminiCliMcpConfiguration(
2660
2760
  servers,
2661
2761
  dir
2662
2762
  ),
2663
- kiro: async (servers, dir) => (await import("./kiro-YDHXY2MA.js")).generateKiroMcpConfiguration(servers, dir),
2664
- junie: async (servers, dir) => (await import("./junie-5LEQU4BO.js")).generateJunieMcpConfiguration(servers, dir),
2665
- windsurf: async (servers, dir) => (await import("./windsurf-4P6HEUBV.js")).generateWindsurfMcpConfiguration(
2763
+ kiro: async (servers, dir) => (await import("./kiro-B6WZNLY4.js")).generateKiroMcpConfiguration(servers, dir),
2764
+ junie: async (servers, dir) => (await import("./junie-3YGOSOGF.js")).generateJunieMcpConfiguration(servers, dir),
2765
+ windsurf: async (servers, dir) => (await import("./windsurf-IZEKUAID.js")).generateWindsurfMcpConfiguration(
2666
2766
  servers,
2667
2767
  dir
2668
2768
  )
@@ -2747,13 +2847,13 @@ async function generateCommand(options = {}) {
2747
2847
  logger.info("Deleting existing output directories...");
2748
2848
  const targetTools = config.defaultTargets;
2749
2849
  const deleteTasks = [];
2750
- const commandsDir = join13(config.aiRulesDir, "commands");
2850
+ const commandsDir = join12(config.aiRulesDir, "commands");
2751
2851
  const hasCommands = await fileExists(commandsDir);
2752
2852
  let hasCommandFiles = false;
2753
2853
  if (hasCommands) {
2754
- const { readdir: readdir2 } = await import("fs/promises");
2854
+ const { readdir } = await import("fs/promises");
2755
2855
  try {
2756
- const files = await readdir2(commandsDir);
2856
+ const files = await readdir(commandsDir);
2757
2857
  hasCommandFiles = files.some((file) => file.endsWith(".md"));
2758
2858
  } catch {
2759
2859
  hasCommandFiles = false;
@@ -2762,12 +2862,12 @@ async function generateCommand(options = {}) {
2762
2862
  for (const tool of targetTools) {
2763
2863
  switch (tool) {
2764
2864
  case "augmentcode":
2765
- deleteTasks.push(removeDirectory(join13(".augment", "rules")));
2766
- deleteTasks.push(removeDirectory(join13(".augment", "ignore")));
2865
+ deleteTasks.push(removeDirectory(join12(".augment", "rules")));
2866
+ deleteTasks.push(removeDirectory(join12(".augment", "ignore")));
2767
2867
  break;
2768
2868
  case "augmentcode-legacy":
2769
2869
  deleteTasks.push(removeClaudeGeneratedFiles());
2770
- deleteTasks.push(removeDirectory(join13(".augment", "ignore")));
2870
+ deleteTasks.push(removeDirectory(join12(".augment", "ignore")));
2771
2871
  break;
2772
2872
  case "copilot":
2773
2873
  deleteTasks.push(removeDirectory(config.outputPaths.copilot));
@@ -2781,24 +2881,27 @@ async function generateCommand(options = {}) {
2781
2881
  case "claudecode":
2782
2882
  deleteTasks.push(removeClaudeGeneratedFiles());
2783
2883
  if (hasCommandFiles) {
2784
- deleteTasks.push(removeDirectory(join13(".claude", "commands")));
2884
+ deleteTasks.push(removeDirectory(join12(".claude", "commands")));
2785
2885
  }
2786
2886
  break;
2787
2887
  case "roo":
2788
2888
  deleteTasks.push(removeDirectory(config.outputPaths.roo));
2789
2889
  if (hasCommandFiles) {
2790
- deleteTasks.push(removeDirectory(join13(".roo", "commands")));
2890
+ deleteTasks.push(removeDirectory(join12(".roo", "commands")));
2791
2891
  }
2792
2892
  break;
2793
2893
  case "geminicli":
2794
2894
  deleteTasks.push(removeDirectory(config.outputPaths.geminicli));
2795
2895
  if (hasCommandFiles) {
2796
- deleteTasks.push(removeDirectory(join13(".gemini", "commands")));
2896
+ deleteTasks.push(removeDirectory(join12(".gemini", "commands")));
2797
2897
  }
2798
2898
  break;
2799
2899
  case "kiro":
2800
2900
  deleteTasks.push(removeDirectory(config.outputPaths.kiro));
2801
2901
  break;
2902
+ case "opencode":
2903
+ deleteTasks.push(removeDirectory(config.outputPaths.opencode));
2904
+ break;
2802
2905
  case "windsurf":
2803
2906
  deleteTasks.push(removeDirectory(config.outputPaths.windsurf));
2804
2907
  break;
@@ -2892,11 +2995,13 @@ Generating configurations for base directory: ${baseDir}`);
2892
2995
 
2893
2996
  // src/cli/commands/gitignore.ts
2894
2997
  import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
2895
- import { join as join14 } from "path";
2998
+ import { join as join13 } from "path";
2896
2999
  var gitignoreCommand = async () => {
2897
- const gitignorePath = join14(process.cwd(), ".gitignore");
3000
+ const gitignorePath = join13(process.cwd(), ".gitignore");
2898
3001
  const rulesFilesToIgnore = [
2899
3002
  "# Generated by rulesync - AI tool configuration files",
3003
+ "**/.amazonq/rules/",
3004
+ "**/.amazonq/mcp.json",
2900
3005
  "**/.github/copilot-instructions.md",
2901
3006
  "**/.github/instructions/",
2902
3007
  "**/.cursor/rules/",
@@ -2922,6 +3027,9 @@ var gitignoreCommand = async () => {
2922
3027
  "**/.augment-guidelines",
2923
3028
  "**/.junie/guidelines.md",
2924
3029
  "**/.noai",
3030
+ "**/.opencode/memories/",
3031
+ "**/.opencode/commands/",
3032
+ "**/opencode.json",
2925
3033
  "**/.mcp.json",
2926
3034
  "!.rulesync/.mcp.json",
2927
3035
  "**/.cursor/mcp.json",
@@ -2960,162 +3068,11 @@ ${linesToAdd.join("\n")}
2960
3068
  };
2961
3069
 
2962
3070
  // src/core/importer.ts
2963
- import { join as join21 } from "path";
3071
+ import { join as join20 } from "path";
2964
3072
  import matter2 from "gray-matter";
2965
3073
 
2966
- // src/parsers/augmentcode.ts
2967
- import { basename as basename3, join as join15 } from "path";
2968
-
2969
- // src/utils/parser-helpers.ts
2970
- function createParseResult() {
2971
- return { rules: [], errors: [] };
2972
- }
2973
- function addError(result, error) {
2974
- result.errors.push(error);
2975
- }
2976
- function addRule(result, rule) {
2977
- if (!result.rules) {
2978
- result.rules = [];
2979
- }
2980
- result.rules.push(rule);
2981
- }
2982
- function addRules(result, rules) {
2983
- if (!result.rules) {
2984
- result.rules = [];
2985
- }
2986
- result.rules.push(...rules);
2987
- }
2988
- async function safeReadFile(operation, errorContext) {
2989
- try {
2990
- const result = await operation();
2991
- return createSuccessResult(result);
2992
- } catch (error) {
2993
- return createErrorResult(error, errorContext);
2994
- }
2995
- }
2996
-
2997
- // src/parsers/augmentcode.ts
2998
- async function parseAugmentcodeConfiguration(baseDir = process.cwd()) {
2999
- return parseUnifiedAugmentcode(baseDir, {
3000
- rulesDir: ".augment/rules",
3001
- targetName: "augmentcode",
3002
- filenamePrefix: "augmentcode"
3003
- });
3004
- }
3005
- async function parseAugmentcodeLegacyConfiguration(baseDir = process.cwd()) {
3006
- return parseUnifiedAugmentcode(baseDir, {
3007
- legacyFilePath: ".augment-guidelines",
3008
- targetName: "augmentcode-legacy",
3009
- filenamePrefix: "augmentcode-legacy"
3010
- });
3011
- }
3012
- async function parseUnifiedAugmentcode(baseDir, config) {
3013
- const result = createParseResult();
3014
- if (config.rulesDir) {
3015
- const rulesDir = join15(baseDir, config.rulesDir);
3016
- if (await fileExists(rulesDir)) {
3017
- const rulesResult = await parseAugmentRules(rulesDir, config);
3018
- addRules(result, rulesResult.rules);
3019
- result.errors.push(...rulesResult.errors);
3020
- } else {
3021
- addError(
3022
- result,
3023
- `No AugmentCode configuration found. Expected ${config.rulesDir} directory.`
3024
- );
3025
- }
3026
- }
3027
- if (config.legacyFilePath) {
3028
- const legacyPath = join15(baseDir, config.legacyFilePath);
3029
- if (await fileExists(legacyPath)) {
3030
- const legacyResult = await parseAugmentGuidelines(legacyPath, config);
3031
- if (legacyResult.rule) {
3032
- addRule(result, legacyResult.rule);
3033
- }
3034
- result.errors.push(...legacyResult.errors);
3035
- } else {
3036
- addError(
3037
- result,
3038
- `No AugmentCode legacy configuration found. Expected ${config.legacyFilePath} file.`
3039
- );
3040
- }
3041
- }
3042
- return { rules: result.rules || [], errors: result.errors };
3043
- }
3044
- async function parseAugmentRules(rulesDir, config) {
3045
- const rules = [];
3046
- const errors = [];
3047
- try {
3048
- const { readdir: readdir2 } = await import("fs/promises");
3049
- const files = await readdir2(rulesDir);
3050
- for (const file of files) {
3051
- if (file.endsWith(".md") || file.endsWith(".mdc")) {
3052
- const filePath = join15(rulesDir, file);
3053
- try {
3054
- const rawContent = await readFileContent(filePath);
3055
- const parsed = parseFrontmatter(rawContent);
3056
- const ruleType = extractStringField(parsed.data, "type", "manual");
3057
- const description = extractStringField(parsed.data, "description", "");
3058
- const tags = extractArrayField(parsed.data, "tags");
3059
- const isRoot = ruleType === "always";
3060
- const filename = basename3(file, file.endsWith(".mdc") ? ".mdc" : ".md");
3061
- const frontmatter = {
3062
- root: isRoot,
3063
- targets: [config.targetName],
3064
- description,
3065
- globs: ["**/*"],
3066
- // AugmentCode doesn't use specific globs in the same way
3067
- ...tags.length > 0 && { tags }
3068
- };
3069
- rules.push({
3070
- frontmatter,
3071
- content: parsed.content.trim(),
3072
- filename: `${config.filenamePrefix}-${ruleType}-${filename}`,
3073
- filepath: filePath
3074
- });
3075
- } catch (error) {
3076
- const errorMessage = error instanceof Error ? error.message : String(error);
3077
- errors.push(`Failed to parse ${filePath}: ${errorMessage}`);
3078
- }
3079
- }
3080
- }
3081
- } catch (error) {
3082
- const errorMessage = error instanceof Error ? error.message : String(error);
3083
- errors.push(`Failed to read ${config.rulesDir || rulesDir} directory: ${errorMessage}`);
3084
- }
3085
- return { rules, errors };
3086
- }
3087
- async function parseAugmentGuidelines(guidelinesPath, config) {
3088
- const parseResult = await safeReadFile(
3089
- async () => {
3090
- const content = await readFileContent(guidelinesPath);
3091
- if (content.trim()) {
3092
- const frontmatter = {
3093
- root: true,
3094
- // Legacy guidelines become root rules
3095
- targets: [config.targetName],
3096
- description: "Legacy AugmentCode guidelines",
3097
- globs: ["**/*"]
3098
- };
3099
- return {
3100
- frontmatter,
3101
- content: content.trim(),
3102
- filename: `${config.filenamePrefix}-guidelines`,
3103
- filepath: guidelinesPath
3104
- };
3105
- }
3106
- return null;
3107
- },
3108
- `Failed to parse ${config.legacyFilePath || guidelinesPath}`
3109
- );
3110
- if (parseResult.success) {
3111
- return { rule: parseResult.result || null, errors: [] };
3112
- } else {
3113
- return { rule: null, errors: [parseResult.error || "Unknown error"] };
3114
- }
3115
- }
3116
-
3117
3074
  // src/parsers/shared-helpers.ts
3118
- import { basename as basename4, join as join16 } from "path";
3075
+ import { basename as basename3, join as join14 } from "path";
3119
3076
  async function parseConfigurationFiles(baseDir = process.cwd(), config) {
3120
3077
  const errors = [];
3121
3078
  const rules = [];
@@ -3168,11 +3125,11 @@ async function parseConfigurationFiles(baseDir = process.cwd(), config) {
3168
3125
  const dirPath = resolvePath(dirConfig.directory, baseDir);
3169
3126
  if (await fileExists(dirPath)) {
3170
3127
  const result = await safeAsyncOperation(async () => {
3171
- const { readdir: readdir2 } = await import("fs/promises");
3172
- const files = await readdir2(dirPath);
3128
+ const { readdir } = await import("fs/promises");
3129
+ const files = await readdir(dirPath);
3173
3130
  for (const file of files) {
3174
3131
  if (file.endsWith(dirConfig.filePattern)) {
3175
- const filePath = join16(dirPath, file);
3132
+ const filePath = join14(dirPath, file);
3176
3133
  const fileResult = await safeAsyncOperation(async () => {
3177
3134
  const rawContent = await readFileContent(filePath);
3178
3135
  let content;
@@ -3319,14 +3276,14 @@ function parseMainFile(content, filepath, config) {
3319
3276
  async function parseMemoryFiles(memoryDir, config) {
3320
3277
  const rules = [];
3321
3278
  try {
3322
- const { readdir: readdir2 } = await import("fs/promises");
3323
- const files = await readdir2(memoryDir);
3279
+ const { readdir } = await import("fs/promises");
3280
+ const files = await readdir(memoryDir);
3324
3281
  for (const file of files) {
3325
3282
  if (file.endsWith(".md")) {
3326
- const filePath = join16(memoryDir, file);
3283
+ const filePath = join14(memoryDir, file);
3327
3284
  const content = await readFileContent(filePath);
3328
3285
  if (content.trim()) {
3329
- const filename = basename4(file, ".md");
3286
+ const filename = basename3(file, ".md");
3330
3287
  const frontmatter = {
3331
3288
  root: false,
3332
3289
  targets: [config.tool],
@@ -3349,14 +3306,14 @@ async function parseMemoryFiles(memoryDir, config) {
3349
3306
  async function parseCommandsFiles(commandsDir, config) {
3350
3307
  const rules = [];
3351
3308
  try {
3352
- const { readdir: readdir2 } = await import("fs/promises");
3353
- const files = await readdir2(commandsDir);
3309
+ const { readdir } = await import("fs/promises");
3310
+ const files = await readdir(commandsDir);
3354
3311
  for (const file of files) {
3355
3312
  if (file.endsWith(".md")) {
3356
- const filePath = join16(commandsDir, file);
3313
+ const filePath = join14(commandsDir, file);
3357
3314
  const content = await readFileContent(filePath);
3358
3315
  if (content.trim()) {
3359
- const filename = basename4(file, ".md");
3316
+ const filename = basename3(file, ".md");
3360
3317
  let frontmatter;
3361
3318
  let ruleContent;
3362
3319
  try {
@@ -3431,6 +3388,170 @@ async function parseSettingsFile(settingsPath, tool) {
3431
3388
  };
3432
3389
  }
3433
3390
 
3391
+ // src/parsers/amazonqcli.ts
3392
+ async function parseAmazonqcliConfiguration(baseDir = process.cwd()) {
3393
+ return parseMemoryBasedConfiguration(baseDir, {
3394
+ tool: "amazonqcli",
3395
+ mainFileName: ".amazonq/rules/main.md",
3396
+ memoryDirPath: ".amazonq/rules",
3397
+ settingsPath: ".amazonq/mcp.json",
3398
+ mainDescription: "Main Amazon Q Developer CLI configuration",
3399
+ memoryDescription: "Amazon Q rule",
3400
+ filenamePrefix: "amazonq"
3401
+ });
3402
+ }
3403
+
3404
+ // src/parsers/augmentcode.ts
3405
+ import { basename as basename4, join as join15 } from "path";
3406
+
3407
+ // src/utils/parser-helpers.ts
3408
+ function createParseResult() {
3409
+ return { rules: [], errors: [] };
3410
+ }
3411
+ function addError(result, error) {
3412
+ result.errors.push(error);
3413
+ }
3414
+ function addRule(result, rule) {
3415
+ if (!result.rules) {
3416
+ result.rules = [];
3417
+ }
3418
+ result.rules.push(rule);
3419
+ }
3420
+ function addRules(result, rules) {
3421
+ if (!result.rules) {
3422
+ result.rules = [];
3423
+ }
3424
+ result.rules.push(...rules);
3425
+ }
3426
+ async function safeReadFile(operation, errorContext) {
3427
+ try {
3428
+ const result = await operation();
3429
+ return createSuccessResult(result);
3430
+ } catch (error) {
3431
+ return createErrorResult(error, errorContext);
3432
+ }
3433
+ }
3434
+
3435
+ // src/parsers/augmentcode.ts
3436
+ async function parseAugmentcodeConfiguration(baseDir = process.cwd()) {
3437
+ return parseUnifiedAugmentcode(baseDir, {
3438
+ rulesDir: ".augment/rules",
3439
+ targetName: "augmentcode",
3440
+ filenamePrefix: "augmentcode"
3441
+ });
3442
+ }
3443
+ async function parseAugmentcodeLegacyConfiguration(baseDir = process.cwd()) {
3444
+ return parseUnifiedAugmentcode(baseDir, {
3445
+ legacyFilePath: ".augment-guidelines",
3446
+ targetName: "augmentcode-legacy",
3447
+ filenamePrefix: "augmentcode-legacy"
3448
+ });
3449
+ }
3450
+ async function parseUnifiedAugmentcode(baseDir, config) {
3451
+ const result = createParseResult();
3452
+ if (config.rulesDir) {
3453
+ const rulesDir = join15(baseDir, config.rulesDir);
3454
+ if (await fileExists(rulesDir)) {
3455
+ const rulesResult = await parseAugmentRules(rulesDir, config);
3456
+ addRules(result, rulesResult.rules);
3457
+ result.errors.push(...rulesResult.errors);
3458
+ } else {
3459
+ addError(
3460
+ result,
3461
+ `No AugmentCode configuration found. Expected ${config.rulesDir} directory.`
3462
+ );
3463
+ }
3464
+ }
3465
+ if (config.legacyFilePath) {
3466
+ const legacyPath = join15(baseDir, config.legacyFilePath);
3467
+ if (await fileExists(legacyPath)) {
3468
+ const legacyResult = await parseAugmentGuidelines(legacyPath, config);
3469
+ if (legacyResult.rule) {
3470
+ addRule(result, legacyResult.rule);
3471
+ }
3472
+ result.errors.push(...legacyResult.errors);
3473
+ } else {
3474
+ addError(
3475
+ result,
3476
+ `No AugmentCode legacy configuration found. Expected ${config.legacyFilePath} file.`
3477
+ );
3478
+ }
3479
+ }
3480
+ return { rules: result.rules || [], errors: result.errors };
3481
+ }
3482
+ async function parseAugmentRules(rulesDir, config) {
3483
+ const rules = [];
3484
+ const errors = [];
3485
+ try {
3486
+ const { readdir } = await import("fs/promises");
3487
+ const files = await readdir(rulesDir);
3488
+ for (const file of files) {
3489
+ if (file.endsWith(".md") || file.endsWith(".mdc")) {
3490
+ const filePath = join15(rulesDir, file);
3491
+ try {
3492
+ const rawContent = await readFileContent(filePath);
3493
+ const parsed = parseFrontmatter(rawContent);
3494
+ const ruleType = extractStringField(parsed.data, "type", "manual");
3495
+ const description = extractStringField(parsed.data, "description", "");
3496
+ const tags = extractArrayField(parsed.data, "tags");
3497
+ const isRoot = ruleType === "always";
3498
+ const filename = basename4(file, file.endsWith(".mdc") ? ".mdc" : ".md");
3499
+ const frontmatter = {
3500
+ root: isRoot,
3501
+ targets: [config.targetName],
3502
+ description,
3503
+ globs: ["**/*"],
3504
+ // AugmentCode doesn't use specific globs in the same way
3505
+ ...tags.length > 0 && { tags }
3506
+ };
3507
+ rules.push({
3508
+ frontmatter,
3509
+ content: parsed.content.trim(),
3510
+ filename: `${config.filenamePrefix}-${ruleType}-${filename}`,
3511
+ filepath: filePath
3512
+ });
3513
+ } catch (error) {
3514
+ const errorMessage = error instanceof Error ? error.message : String(error);
3515
+ errors.push(`Failed to parse ${filePath}: ${errorMessage}`);
3516
+ }
3517
+ }
3518
+ }
3519
+ } catch (error) {
3520
+ const errorMessage = error instanceof Error ? error.message : String(error);
3521
+ errors.push(`Failed to read ${config.rulesDir || rulesDir} directory: ${errorMessage}`);
3522
+ }
3523
+ return { rules, errors };
3524
+ }
3525
+ async function parseAugmentGuidelines(guidelinesPath, config) {
3526
+ const parseResult = await safeReadFile(
3527
+ async () => {
3528
+ const content = await readFileContent(guidelinesPath);
3529
+ if (content.trim()) {
3530
+ const frontmatter = {
3531
+ root: true,
3532
+ // Legacy guidelines become root rules
3533
+ targets: [config.targetName],
3534
+ description: "Legacy AugmentCode guidelines",
3535
+ globs: ["**/*"]
3536
+ };
3537
+ return {
3538
+ frontmatter,
3539
+ content: content.trim(),
3540
+ filename: `${config.filenamePrefix}-guidelines`,
3541
+ filepath: guidelinesPath
3542
+ };
3543
+ }
3544
+ return null;
3545
+ },
3546
+ `Failed to parse ${config.legacyFilePath || guidelinesPath}`
3547
+ );
3548
+ if (parseResult.success) {
3549
+ return { rule: parseResult.result || null, errors: [] };
3550
+ } else {
3551
+ return { rule: null, errors: [parseResult.error || "Unknown error"] };
3552
+ }
3553
+ }
3554
+
3434
3555
  // src/parsers/claudecode.ts
3435
3556
  async function parseClaudeConfiguration(baseDir = process.cwd()) {
3436
3557
  return parseMemoryBasedConfiguration(baseDir, {
@@ -3466,7 +3587,7 @@ async function parseClineConfiguration(baseDir = process.cwd()) {
3466
3587
  }
3467
3588
 
3468
3589
  // src/parsers/codexcli.ts
3469
- import { join as join17 } from "path";
3590
+ import { join as join16 } from "path";
3470
3591
 
3471
3592
  // src/parsers/copilot.ts
3472
3593
  async function parseCopilotConfiguration(baseDir = process.cwd()) {
@@ -3489,7 +3610,7 @@ async function parseCopilotConfiguration(baseDir = process.cwd()) {
3489
3610
  }
3490
3611
 
3491
3612
  // src/parsers/cursor.ts
3492
- import { basename as basename5, join as join18 } from "path";
3613
+ import { basename as basename5, join as join17 } from "path";
3493
3614
  import { DEFAULT_SCHEMA, FAILSAFE_SCHEMA, load } from "js-yaml";
3494
3615
  import { z as z7 } from "zod/mini";
3495
3616
  var customMatterOptions = {
@@ -3613,7 +3734,7 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
3613
3734
  const rules = [];
3614
3735
  let ignorePatterns;
3615
3736
  let mcpServers;
3616
- const cursorFilePath = join18(baseDir, ".cursorrules");
3737
+ const cursorFilePath = join17(baseDir, ".cursorrules");
3617
3738
  if (await fileExists(cursorFilePath)) {
3618
3739
  try {
3619
3740
  const rawContent = await readFileContent(cursorFilePath);
@@ -3634,14 +3755,14 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
3634
3755
  errors.push(`Failed to parse .cursorrules file: ${errorMessage}`);
3635
3756
  }
3636
3757
  }
3637
- const cursorRulesDir = join18(baseDir, ".cursor", "rules");
3758
+ const cursorRulesDir = join17(baseDir, ".cursor", "rules");
3638
3759
  if (await fileExists(cursorRulesDir)) {
3639
3760
  try {
3640
- const { readdir: readdir2 } = await import("fs/promises");
3641
- const files = await readdir2(cursorRulesDir);
3761
+ const { readdir } = await import("fs/promises");
3762
+ const files = await readdir(cursorRulesDir);
3642
3763
  for (const file of files) {
3643
3764
  if (file.endsWith(".mdc")) {
3644
- const filePath = join18(cursorRulesDir, file);
3765
+ const filePath = join17(cursorRulesDir, file);
3645
3766
  try {
3646
3767
  const rawContent = await readFileContent(filePath);
3647
3768
  const parsed = parseFrontmatter(rawContent, { matterOptions: customMatterOptions });
@@ -3670,7 +3791,7 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
3670
3791
  if (rules.length === 0) {
3671
3792
  errors.push("No Cursor configuration files found (.cursorrules or .cursor/rules/*.mdc)");
3672
3793
  }
3673
- const cursorIgnorePath = join18(baseDir, ".cursorignore");
3794
+ const cursorIgnorePath = join17(baseDir, ".cursorignore");
3674
3795
  if (await fileExists(cursorIgnorePath)) {
3675
3796
  try {
3676
3797
  const content = await readFileContent(cursorIgnorePath);
@@ -3683,7 +3804,7 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
3683
3804
  errors.push(`Failed to parse .cursorignore: ${errorMessage}`);
3684
3805
  }
3685
3806
  }
3686
- const cursorMcpPath = join18(baseDir, ".cursor", "mcp.json");
3807
+ const cursorMcpPath = join17(baseDir, ".cursor", "mcp.json");
3687
3808
  if (await fileExists(cursorMcpPath)) {
3688
3809
  try {
3689
3810
  const content = await readFileContent(cursorMcpPath);
@@ -3733,11 +3854,11 @@ async function parseGeminiConfiguration(baseDir = process.cwd()) {
3733
3854
  }
3734
3855
 
3735
3856
  // src/parsers/junie.ts
3736
- import { join as join19 } from "path";
3857
+ import { join as join18 } from "path";
3737
3858
  async function parseJunieConfiguration(baseDir = process.cwd()) {
3738
3859
  const errors = [];
3739
3860
  const rules = [];
3740
- const guidelinesPath = join19(baseDir, ".junie", "guidelines.md");
3861
+ const guidelinesPath = join18(baseDir, ".junie", "guidelines.md");
3741
3862
  if (!await fileExists(guidelinesPath)) {
3742
3863
  errors.push(".junie/guidelines.md file not found");
3743
3864
  return { rules, errors };
@@ -3768,6 +3889,32 @@ async function parseJunieConfiguration(baseDir = process.cwd()) {
3768
3889
  return { rules, errors };
3769
3890
  }
3770
3891
 
3892
+ // src/parsers/opencode.ts
3893
+ async function parseOpCodeIgnore(opcodeignorePath) {
3894
+ try {
3895
+ const content = await readFileContent(opcodeignorePath);
3896
+ const patterns = content.split("\n").map((line) => line.trim()).filter((line) => line && !line.startsWith("#"));
3897
+ return patterns;
3898
+ } catch {
3899
+ return [];
3900
+ }
3901
+ }
3902
+ async function parseOpenCodeConfiguration(baseDir = process.cwd()) {
3903
+ return parseMemoryBasedConfiguration(baseDir, {
3904
+ tool: "opencode",
3905
+ mainFileName: "AGENTS.md",
3906
+ memoryDirPath: ".opencode/memories",
3907
+ settingsPath: "opencode.json",
3908
+ mainDescription: "Main OpenCode configuration",
3909
+ memoryDescription: "Memory file",
3910
+ filenamePrefix: "opencode",
3911
+ additionalIgnoreFile: {
3912
+ path: ".opcodeignore",
3913
+ parser: parseOpCodeIgnore
3914
+ }
3915
+ });
3916
+ }
3917
+
3771
3918
  // src/parsers/roo.ts
3772
3919
  async function parseRooConfiguration(baseDir = process.cwd()) {
3773
3920
  return parseConfigurationFiles(baseDir, {
@@ -3789,8 +3936,8 @@ async function parseRooConfiguration(baseDir = process.cwd()) {
3789
3936
  }
3790
3937
 
3791
3938
  // src/parsers/windsurf.ts
3792
- import { readFile as readFile2 } from "fs/promises";
3793
- import { join as join20 } from "path";
3939
+ import { readFile } from "fs/promises";
3940
+ import { join as join19 } from "path";
3794
3941
 
3795
3942
  // src/core/importer.ts
3796
3943
  async function importConfiguration(options) {
@@ -3810,6 +3957,13 @@ async function importConfiguration(options) {
3810
3957
  }
3811
3958
  try {
3812
3959
  switch (tool) {
3960
+ case "amazonqcli": {
3961
+ const amazonqResult = await parseAmazonqcliConfiguration(baseDir);
3962
+ rules = amazonqResult.rules;
3963
+ errors.push(...amazonqResult.errors);
3964
+ mcpServers = amazonqResult.mcpServers;
3965
+ break;
3966
+ }
3813
3967
  case "augmentcode": {
3814
3968
  const augmentResult = await parseAugmentcodeConfiguration(baseDir);
3815
3969
  rules = augmentResult.rules;
@@ -3870,6 +4024,14 @@ async function importConfiguration(options) {
3870
4024
  errors.push(...junieResult.errors);
3871
4025
  break;
3872
4026
  }
4027
+ case "opencode": {
4028
+ const opencodeResult = await parseOpenCodeConfiguration(baseDir);
4029
+ rules = opencodeResult.rules;
4030
+ errors.push(...opencodeResult.errors);
4031
+ ignorePatterns = opencodeResult.ignorePatterns;
4032
+ mcpServers = opencodeResult.mcpServers;
4033
+ break;
4034
+ }
3873
4035
  default:
3874
4036
  errors.push(`Unsupported tool: ${tool}`);
3875
4037
  return { success: false, rulesCreated: 0, errors };
@@ -3882,10 +4044,10 @@ async function importConfiguration(options) {
3882
4044
  if (rules.length === 0 && !ignorePatterns && !mcpServers) {
3883
4045
  return { success: false, rulesCreated: 0, errors };
3884
4046
  }
3885
- const rulesDirPath = join21(baseDir, rulesDir);
4047
+ const rulesDirPath = join20(baseDir, rulesDir);
3886
4048
  try {
3887
- const { mkdir: mkdir3 } = await import("fs/promises");
3888
- await mkdir3(rulesDirPath, { recursive: true });
4049
+ const { mkdir: mkdir2 } = await import("fs/promises");
4050
+ await mkdir2(rulesDirPath, { recursive: true });
3889
4051
  } catch (error) {
3890
4052
  const errorMessage = error instanceof Error ? error.message : String(error);
3891
4053
  errors.push(`Failed to create rules directory: ${errorMessage}`);
@@ -3897,17 +4059,17 @@ async function importConfiguration(options) {
3897
4059
  const baseFilename = rule.filename;
3898
4060
  let targetDir = rulesDirPath;
3899
4061
  if (rule.type === "command") {
3900
- targetDir = join21(rulesDirPath, "commands");
3901
- const { mkdir: mkdir3 } = await import("fs/promises");
3902
- await mkdir3(targetDir, { recursive: true });
4062
+ targetDir = join20(rulesDirPath, "commands");
4063
+ const { mkdir: mkdir2 } = await import("fs/promises");
4064
+ await mkdir2(targetDir, { recursive: true });
3903
4065
  } else {
3904
4066
  if (!useLegacyLocation) {
3905
- targetDir = join21(rulesDirPath, "rules");
3906
- const { mkdir: mkdir3 } = await import("fs/promises");
3907
- await mkdir3(targetDir, { recursive: true });
4067
+ targetDir = join20(rulesDirPath, "rules");
4068
+ const { mkdir: mkdir2 } = await import("fs/promises");
4069
+ await mkdir2(targetDir, { recursive: true });
3908
4070
  }
3909
4071
  }
3910
- const filePath = join21(targetDir, `${baseFilename}.md`);
4072
+ const filePath = join20(targetDir, `${baseFilename}.md`);
3911
4073
  const content = generateRuleFileContent(rule);
3912
4074
  await writeFileContent(filePath, content);
3913
4075
  rulesCreated++;
@@ -3922,7 +4084,7 @@ async function importConfiguration(options) {
3922
4084
  let ignoreFileCreated = false;
3923
4085
  if (ignorePatterns && ignorePatterns.length > 0) {
3924
4086
  try {
3925
- const rulesyncignorePath = join21(baseDir, ".rulesyncignore");
4087
+ const rulesyncignorePath = join20(baseDir, ".rulesyncignore");
3926
4088
  const ignoreContent = `${ignorePatterns.join("\n")}
3927
4089
  `;
3928
4090
  await writeFileContent(rulesyncignorePath, ignoreContent);
@@ -3938,7 +4100,7 @@ async function importConfiguration(options) {
3938
4100
  let mcpFileCreated = false;
3939
4101
  if (mcpServers && Object.keys(mcpServers).length > 0) {
3940
4102
  try {
3941
- const mcpPath = join21(baseDir, rulesDir, ".mcp.json");
4103
+ const mcpPath = join20(baseDir, rulesDir, ".mcp.json");
3942
4104
  const mcpContent = `${JSON.stringify({ mcpServers }, null, 2)}
3943
4105
  `;
3944
4106
  await writeFileContent(mcpPath, mcpContent);
@@ -3976,6 +4138,7 @@ function generateRuleFileContent(rule) {
3976
4138
  async function importCommand(options = {}) {
3977
4139
  logger.setVerbose(options.verbose || false);
3978
4140
  const tools = [];
4141
+ if (options.amazonqcli) tools.push("amazonqcli");
3979
4142
  if (options.augmentcode) tools.push("augmentcode");
3980
4143
  if (options["augmentcode-legacy"]) tools.push("augmentcode-legacy");
3981
4144
  if (options.claudecode) tools.push("claudecode");
@@ -3984,9 +4147,10 @@ async function importCommand(options = {}) {
3984
4147
  if (options.cline) tools.push("cline");
3985
4148
  if (options.roo) tools.push("roo");
3986
4149
  if (options.geminicli) tools.push("geminicli");
4150
+ if (options.opencode) tools.push("opencode");
3987
4151
  if (tools.length === 0) {
3988
4152
  logger.error(
3989
- "\u274C Please specify one tool to import from (--augmentcode, --augmentcode-legacy, --claudecode, --cursor, --copilot, --cline, --roo, --geminicli)"
4153
+ "\u274C Please specify one tool to import from (--amazonqcli, --augmentcode, --augmentcode-legacy, --claudecode, --cursor, --copilot, --cline, --roo, --geminicli, --opencode)"
3990
4154
  );
3991
4155
  process.exit(1);
3992
4156
  }
@@ -4034,7 +4198,7 @@ async function importCommand(options = {}) {
4034
4198
  }
4035
4199
 
4036
4200
  // src/cli/commands/init.ts
4037
- import { join as join22 } from "path";
4201
+ import { join as join21 } from "path";
4038
4202
  async function initCommand(options = {}) {
4039
4203
  const configResult = await loadConfig();
4040
4204
  const config = configResult.config;
@@ -4042,7 +4206,7 @@ async function initCommand(options = {}) {
4042
4206
  logger.log("Initializing rulesync...");
4043
4207
  await ensureDir(aiRulesDir);
4044
4208
  const useLegacy = options.legacy ?? config.legacy ?? false;
4045
- const rulesDir = useLegacy ? aiRulesDir : join22(aiRulesDir, "rules");
4209
+ const rulesDir = useLegacy ? aiRulesDir : join21(aiRulesDir, "rules");
4046
4210
  if (!useLegacy) {
4047
4211
  await ensureDir(rulesDir);
4048
4212
  }
@@ -4088,7 +4252,7 @@ globs: ["**/*"]
4088
4252
  - Follow single responsibility principle
4089
4253
  `
4090
4254
  };
4091
- const filepath = join22(rulesDir, sampleFile.filename);
4255
+ const filepath = join21(rulesDir, sampleFile.filename);
4092
4256
  if (!await fileExists(filepath)) {
4093
4257
  await writeFileContent(filepath, sampleFile.content);
4094
4258
  logger.success(`Created ${filepath}`);
@@ -4232,12 +4396,12 @@ async function watchCommand() {
4232
4396
 
4233
4397
  // src/cli/index.ts
4234
4398
  var program = new Command();
4235
- program.name("rulesync").description("Unified AI rules management CLI tool").version("0.63.0");
4399
+ program.name("rulesync").description("Unified AI rules management CLI tool").version("0.64.0");
4236
4400
  program.command("init").description("Initialize rulesync in current directory").option("--legacy", "Use legacy file location (.rulesync/*.md instead of .rulesync/rules/*.md)").action(initCommand);
4237
4401
  program.command("add <filename>").description("Add a new rule file").option("--legacy", "Use legacy file location (.rulesync/*.md instead of .rulesync/rules/*.md)").action(addCommand);
4238
4402
  program.command("gitignore").description("Add generated files to .gitignore").action(gitignoreCommand);
4239
- program.command("import").description("Import configurations from AI tools to rulesync format").option("--augmentcode", "Import from AugmentCode (.augment/rules/)").option("--augmentcode-legacy", "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("--junie", "Import from JetBrains Junie (.junie/guidelines.md)").option("-v, --verbose", "Verbose output").option("--legacy", "Use legacy file location (.rulesync/*.md instead of .rulesync/rules/*.md)").action(importCommand);
4240
- program.command("generate").description("Generate configuration files for AI tools").option("--augmentcode", "Generate only for AugmentCode").option("--augmentcode-legacy", "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("--codexcli", "Generate only for OpenAI Codex CLI").option("--claudecode", "Generate only for Claude Code").option("--roo", "Generate only for Roo Code").option("--geminicli", "Generate only for Gemini CLI").option("--junie", "Generate only for JetBrains Junie").option("--kiro", "Generate only for Kiro IDE").option("--windsurf", "Generate only for Windsurf").option("--delete", "Delete all existing files in output directories before generating").option(
4403
+ program.command("import").description("Import configurations from AI tools to rulesync format").option("--augmentcode", "Import from AugmentCode (.augment/rules/)").option("--augmentcode-legacy", "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("--junie", "Import from JetBrains Junie (.junie/guidelines.md)").option("--opencode", "Import from OpenCode (AGENTS.md)").option("-v, --verbose", "Verbose output").option("--legacy", "Use legacy file location (.rulesync/*.md instead of .rulesync/rules/*.md)").action(importCommand);
4404
+ program.command("generate").description("Generate configuration files for AI tools").option("--augmentcode", "Generate only for AugmentCode").option("--augmentcode-legacy", "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("--codexcli", "Generate only for OpenAI Codex CLI").option("--claudecode", "Generate only for Claude Code").option("--roo", "Generate only for Roo Code").option("--geminicli", "Generate only for Gemini CLI").option("--junie", "Generate only for JetBrains Junie").option("--kiro", "Generate only for Kiro IDE").option("--opencode", "Generate only for OpenCode").option("--windsurf", "Generate only for Windsurf").option("--delete", "Delete all existing files in output directories before generating").option(
4241
4405
  "-b, --base-dir <paths>",
4242
4406
  "Base directories to generate files (comma-separated for multiple paths)"
4243
4407
  ).option("-v, --verbose", "Verbose output").option("-c, --config <path>", "Path to configuration file").option("--no-config", "Disable configuration file loading").action(async (options) => {
@@ -4253,6 +4417,7 @@ program.command("generate").description("Generate configuration files for AI too
4253
4417
  if (options.geminicli) tools.push("geminicli");
4254
4418
  if (options.junie) tools.push("junie");
4255
4419
  if (options.kiro) tools.push("kiro");
4420
+ if (options.opencode) tools.push("opencode");
4256
4421
  if (options.windsurf) tools.push("windsurf");
4257
4422
  const generateOptions = {
4258
4423
  verbose: options.verbose,