rulesync 0.63.0 → 0.65.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 (35) hide show
  1. package/README.md +21 -3
  2. package/dist/amazonqcli-MW7XTVPN.js +9 -0
  3. package/dist/{augmentcode-HIZIQG2W.js → augmentcode-WCZCL7VR.js} +2 -2
  4. package/dist/{chunk-KUGTKMNW.js → chunk-4NWMCTN5.js} +5 -2
  5. package/dist/chunk-6AXPFPKI.js +17 -0
  6. package/dist/{chunk-LXTA7DBA.js → chunk-6SLEITCQ.js} +1 -1
  7. package/dist/chunk-DM2B7XUB.js +210 -0
  8. package/dist/{chunk-UEAYL4NT.js → chunk-FL5BF6JM.js} +1 -1
  9. package/dist/{chunk-4PSTOKKD.js → chunk-GIAQWZQ4.js} +1 -1
  10. package/dist/{chunk-YTU3SCQO.js → chunk-I4NVS7GE.js} +9 -3
  11. package/dist/{chunk-GQTMTBX4.js → chunk-JXOLLTNV.js} +89 -4
  12. package/dist/{chunk-NETSYSMD.js → chunk-LTWEI4PW.js} +1 -1
  13. package/dist/{chunk-AUUSMVCT.js → chunk-M2AUM37M.js} +3 -0
  14. package/dist/{chunk-M7NL7G7A.js → chunk-N6DASHJL.js} +1 -1
  15. package/dist/chunk-TX2CE4RR.js +17 -0
  16. package/dist/{chunk-2CW2KFB3.js → chunk-UGY5ALND.js} +1 -1
  17. package/dist/chunk-VRWNZTGW.js +17 -0
  18. package/dist/{chunk-U4PLVMCG.js → chunk-YC2BC7Z2.js} +1 -1
  19. package/dist/{claudecode-YTEFACCT.js → claudecode-RZSJPPBU.js} +3 -3
  20. package/dist/{cline-CKNUDEA3.js → cline-JTWWBQQ4.js} +3 -3
  21. package/dist/{codexcli-7SDGYI7D.js → codexcli-ATMFGRJR.js} +3 -3
  22. package/dist/{copilot-MOR3HHJX.js → copilot-H3CLGKDP.js} +2 -2
  23. package/dist/{cursor-YJGH7W24.js → cursor-ZUN5RZU6.js} +3 -3
  24. package/dist/{geminicli-E7KZTZ2G.js → geminicli-Q5HPIQCU.js} +3 -3
  25. package/dist/index.cjs +1213 -619
  26. package/dist/index.js +806 -488
  27. package/dist/{junie-5LEQU4BO.js → junie-JCLVC3MI.js} +3 -3
  28. package/dist/{kiro-YDHXY2MA.js → kiro-CNF6433S.js} +2 -2
  29. package/dist/opencode-EBS3CED2.js +17 -0
  30. package/dist/qwencode-JIT6KW7E.js +10 -0
  31. package/dist/{roo-L3QTTIPO.js → roo-KBTRH4TZ.js} +3 -2
  32. package/dist/{windsurf-4P6HEUBV.js → windsurf-ZAAWL6JJ.js} +3 -3
  33. package/package.json +2 -1
  34. package/dist/chunk-MDYDKNXQ.js +0 -61
  35. package/dist/chunk-PCATT4UZ.js +0 -78
package/dist/index.js CHANGED
@@ -1,35 +1,40 @@
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-6AXPFPKI.js";
3
+ import "./chunk-N6DASHJL.js";
4
+ import "./chunk-6SLEITCQ.js";
5
+ import "./chunk-TX2CE4RR.js";
6
+ import "./chunk-VRWNZTGW.js";
7
+ import "./chunk-LTWEI4PW.js";
8
+ import {
9
+ ensureDir,
10
+ fileExists,
11
+ findFiles,
12
+ findRuleFiles,
13
+ logger,
14
+ readFileContent,
15
+ removeClaudeGeneratedFiles,
16
+ removeDirectory,
17
+ resolvePath,
18
+ writeFileContent
19
+ } from "./chunk-DM2B7XUB.js";
20
+ import "./chunk-I4NVS7GE.js";
21
+ import "./chunk-YC2BC7Z2.js";
22
+ import "./chunk-FL5BF6JM.js";
23
+ import "./chunk-UGY5ALND.js";
24
+ import "./chunk-4NWMCTN5.js";
25
+ import "./chunk-GIAQWZQ4.js";
26
+ import "./chunk-JXOLLTNV.js";
14
27
  import {
15
28
  ALL_TOOL_TARGETS,
16
29
  RulesyncTargetsSchema,
17
30
  ToolTargetSchema,
18
31
  ToolTargetsSchema,
19
32
  isToolTarget
20
- } from "./chunk-AUUSMVCT.js";
33
+ } from "./chunk-M2AUM37M.js";
21
34
 
22
35
  // src/cli/index.ts
23
36
  import { Command } from "commander";
24
37
 
25
- // src/cli/commands/add.ts
26
- import { mkdir, writeFile } from "fs/promises";
27
- import * as path from "path";
28
-
29
- // src/utils/config-loader.ts
30
- import { loadConfig as loadC12Config } from "c12";
31
- import { $ZodError } from "zod/v4/core";
32
-
33
38
  // src/types/claudecode.ts
34
39
  import { z } from "zod/mini";
35
40
  var ClaudeSettingsSchema = z.looseObject({
@@ -70,6 +75,7 @@ var ConfigSchema = z3.object({
70
75
  // src/types/config-options.ts
71
76
  import { z as z4 } from "zod/mini";
72
77
  var OutputPathsSchema = z4.object({
78
+ amazonqcli: z4.optional(z4.string()),
73
79
  augmentcode: z4.optional(z4.string()),
74
80
  "augmentcode-legacy": z4.optional(z4.string()),
75
81
  copilot: z4.optional(z4.string()),
@@ -77,6 +83,8 @@ var OutputPathsSchema = z4.object({
77
83
  cline: z4.optional(z4.string()),
78
84
  claudecode: z4.optional(z4.string()),
79
85
  codexcli: z4.optional(z4.string()),
86
+ opencode: z4.optional(z4.string()),
87
+ qwencode: z4.optional(z4.string()),
80
88
  roo: z4.optional(z4.string()),
81
89
  geminicli: z4.optional(z4.string()),
82
90
  kiro: z4.optional(z4.string()),
@@ -127,7 +135,7 @@ var MergedConfigSchema = z4.object({
127
135
  import { z as z5 } from "zod/mini";
128
136
  var McpTransportTypeSchema = z5.enum(["stdio", "sse", "http"]);
129
137
  var McpServerBaseSchema = z5.object({
130
- command: z5.optional(z5.string()),
138
+ command: z5.optional(z5.union([z5.string(), z5.array(z5.string())])),
131
139
  args: z5.optional(z5.array(z5.string())),
132
140
  url: z5.optional(z5.string()),
133
141
  httpUrl: z5.optional(z5.string()),
@@ -173,11 +181,20 @@ var GenerateOptionsSchema = z6.object({
173
181
  watch: z6.optional(z6.boolean())
174
182
  });
175
183
 
184
+ // src/cli/commands/add.ts
185
+ import { mkdir, writeFile } from "fs/promises";
186
+ import * as path from "path";
187
+
188
+ // src/utils/config-loader.ts
189
+ import { loadConfig as loadC12Config } from "c12";
190
+ import { $ZodError } from "zod/v4/core";
191
+
176
192
  // src/utils/config.ts
177
193
  function getDefaultConfig() {
178
194
  return {
179
195
  aiRulesDir: ".rulesync",
180
196
  outputPaths: {
197
+ amazonqcli: ".amazonq/rules",
181
198
  augmentcode: ".",
182
199
  "augmentcode-legacy": ".",
183
200
  copilot: ".github/instructions",
@@ -185,6 +202,8 @@ function getDefaultConfig() {
185
202
  cline: ".clinerules",
186
203
  claudecode: ".",
187
204
  codexcli: ".",
205
+ opencode: ".",
206
+ qwencode: ".qwen/memories",
188
207
  roo: ".roo/rules",
189
208
  geminicli: ".gemini/memories",
190
209
  kiro: ".kiro/steering",
@@ -391,50 +410,6 @@ function mergeWithCliOptions(config, cliOptions) {
391
410
  return merged;
392
411
  }
393
412
 
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
413
  // src/cli/commands/add.ts
439
414
  function sanitizeFilename(filename) {
440
415
  return filename.endsWith(".md") ? filename.slice(0, -3) : filename;
@@ -508,89 +483,6 @@ async function safeAsyncOperation(operation, errorContext) {
508
483
  }
509
484
  }
510
485
 
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
486
  // src/cli/commands/config.ts
595
487
  async function configCommand(options = {}) {
596
488
  if (options.init) {
@@ -805,22 +697,22 @@ export default config;
805
697
  import { join as join13 } from "path";
806
698
 
807
699
  // src/core/command-generator.ts
808
- import { join as join4 } from "path";
700
+ import { join as join3 } from "path";
809
701
 
810
702
  // 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}`);
703
+ import { join as join2 } from "path";
704
+ function generateYamlFrontmatter(command, options) {
705
+ const frontmatterLines = ["---"];
706
+ if (options?.includeDescription && command.frontmatter.description) {
707
+ frontmatterLines.push(`description: ${command.frontmatter.description}`);
816
708
  }
817
- if (options.additionalFields) {
709
+ if (options?.additionalFields) {
818
710
  for (const field of options.additionalFields) {
819
- frontmatter.push(`${field.key}: ${field.value}`);
711
+ frontmatterLines.push(`${field.key}: ${field.value}`);
820
712
  }
821
713
  }
822
- frontmatter.push("---");
823
- return frontmatter;
714
+ frontmatterLines.push("---");
715
+ return frontmatterLines;
824
716
  }
825
717
  function buildCommandContent(command, frontmatterOptions) {
826
718
  const frontmatter = generateYamlFrontmatter(command, frontmatterOptions);
@@ -831,12 +723,12 @@ ${command.content.trim()}
831
723
  }
832
724
  function getFlattenedCommandPath(filename, baseDir, subdir) {
833
725
  const flattenedName = filename.replace(/\//g, "-");
834
- return join3(baseDir, subdir, `${flattenedName}.md`);
726
+ return join2(baseDir, subdir, `${flattenedName}.md`);
835
727
  }
836
728
  function getHierarchicalCommandPath(filename, baseDir, subdir, extension = "md") {
837
729
  const nameWithoutExt = filename.replace(/\.[^/.]+$/, "");
838
730
  const fileWithExt = `${nameWithoutExt}.${extension}`;
839
- return join3(baseDir, subdir, fileWithExt);
731
+ return join2(baseDir, subdir, fileWithExt);
840
732
  }
841
733
  function escapeTomlString(str) {
842
734
  return str.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/\t/g, "\\t");
@@ -859,26 +751,75 @@ var syntaxConverters = {
859
751
  }
860
752
  };
861
753
 
862
- // src/generators/commands/claudecode.ts
863
- var ClaudeCodeCommandGenerator = class {
754
+ // src/generators/commands/base.ts
755
+ var BaseCommandGenerator = class {
756
+ /**
757
+ * Generate command output for the specified tool
758
+ */
864
759
  generate(command, outputDir) {
865
760
  const filepath = this.getOutputPath(command.filename, outputDir);
866
- const content = buildCommandContent(command);
761
+ const content = this.processContent(command);
867
762
  return {
868
- tool: "claudecode",
763
+ tool: this.getToolName(),
869
764
  filepath,
870
765
  content
871
766
  };
872
767
  }
768
+ /**
769
+ * Get the output path for the command file
770
+ * Override this method if custom path logic is needed
771
+ */
873
772
  getOutputPath(filename, baseDir) {
874
- return getFlattenedCommandPath(filename, baseDir, ".claude/commands");
773
+ if (this.supportsHierarchy()) {
774
+ return getHierarchicalCommandPath(
775
+ filename,
776
+ baseDir,
777
+ this.getCommandsDirectory(),
778
+ this.getFileExtension()
779
+ );
780
+ } else {
781
+ return getFlattenedCommandPath(filename, baseDir, this.getCommandsDirectory());
782
+ }
783
+ }
784
+ /**
785
+ * Whether this tool supports hierarchical directory structure
786
+ * Override to return true for tools that support nested commands
787
+ */
788
+ supportsHierarchy() {
789
+ return false;
790
+ }
791
+ /**
792
+ * Get file extension for the target tool
793
+ * Override if tool uses different extension than .md
794
+ */
795
+ getFileExtension() {
796
+ return "md";
797
+ }
798
+ };
799
+
800
+ // src/generators/commands/claudecode.ts
801
+ var ClaudeCodeCommandGenerator = class extends BaseCommandGenerator {
802
+ getToolName() {
803
+ return "claudecode";
804
+ }
805
+ getCommandsDirectory() {
806
+ return ".claude/commands";
807
+ }
808
+ processContent(command) {
809
+ return buildCommandContent(command, { includeDescription: true });
875
810
  }
811
+ // Uses flattened structure by default (supportsHierarchy returns false)
876
812
  };
877
813
 
878
814
  // src/generators/commands/geminicli.ts
879
- var GeminiCliCommandGenerator = class {
880
- generate(command, outputDir) {
881
- const filepath = this.getOutputPath(command.filename, outputDir);
815
+ var GeminiCliCommandGenerator = class extends BaseCommandGenerator {
816
+ getToolName() {
817
+ return "geminicli";
818
+ }
819
+ getCommandsDirectory() {
820
+ return ".gemini/commands";
821
+ }
822
+ processContent(command) {
882
823
  const convertedContent = syntaxConverters.toGeminiCli(command.content);
883
824
  const tomlLines = [];
884
825
  if (command.frontmatter.description) {
@@ -886,32 +827,28 @@ var GeminiCliCommandGenerator = class {
886
827
  tomlLines.push("");
887
828
  }
888
829
  tomlLines.push(`prompt = """${convertedContent}"""`);
889
- const content = tomlLines.join("\n") + "\n";
890
- return {
891
- tool: "geminicli",
892
- filepath,
893
- content
894
- };
830
+ return tomlLines.join("\n") + "\n";
895
831
  }
896
- getOutputPath(filename, baseDir) {
897
- return getHierarchicalCommandPath(filename, baseDir, ".gemini/commands", "toml");
832
+ supportsHierarchy() {
833
+ return true;
834
+ }
835
+ getFileExtension() {
836
+ return "toml";
898
837
  }
899
838
  };
900
839
 
901
840
  // 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
- };
841
+ var RooCommandGenerator = class extends BaseCommandGenerator {
842
+ getToolName() {
843
+ return "roo";
911
844
  }
912
- getOutputPath(filename, baseDir) {
913
- return getFlattenedCommandPath(filename, baseDir, ".roo/commands");
845
+ getCommandsDirectory() {
846
+ return ".roo/commands";
914
847
  }
848
+ processContent(command) {
849
+ return buildCommandContent(command, { includeDescription: true });
850
+ }
851
+ // Uses flattened structure by default (supportsHierarchy returns false)
915
852
  };
916
853
 
917
854
  // src/generators/commands/index.ts
@@ -988,7 +925,7 @@ async function parseCommandFile(filepath) {
988
925
 
989
926
  // src/core/command-generator.ts
990
927
  async function generateCommands(projectRoot, baseDir, targets) {
991
- const commandsDir = join4(projectRoot, ".rulesync", "commands");
928
+ const commandsDir = join3(projectRoot, ".rulesync", "commands");
992
929
  if (!await fileExists(commandsDir)) {
993
930
  return [];
994
931
  }
@@ -1022,7 +959,7 @@ async function generateCommands(projectRoot, baseDir, targets) {
1022
959
  }
1023
960
 
1024
961
  // src/generators/ignore/shared-factory.ts
1025
- import { join as join5 } from "path";
962
+ import { join as join4 } from "path";
1026
963
 
1027
964
  // src/generators/ignore/shared-helpers.ts
1028
965
  function extractIgnorePatternsFromRules(rules) {
@@ -1145,7 +1082,7 @@ function generateIgnoreFile(rules, config, ignoreConfig, baseDir) {
1145
1082
  const outputs = [];
1146
1083
  const content = generateIgnoreContent(rules, ignoreConfig);
1147
1084
  const outputPath = baseDir || process.cwd();
1148
- const filepath = join5(outputPath, ignoreConfig.filename);
1085
+ const filepath = join4(outputPath, ignoreConfig.filename);
1149
1086
  outputs.push({
1150
1087
  tool: ignoreConfig.tool,
1151
1088
  filepath,
@@ -1727,14 +1664,72 @@ async function generateKiroIgnoreFiles(rules, config, baseDir) {
1727
1664
  return generateIgnoreFile(rules, config, ignoreConfigs.kiro, baseDir);
1728
1665
  }
1729
1666
 
1667
+ // src/generators/ignore/qwencode.ts
1668
+ import { join as join5 } from "path";
1669
+ function extractQwenCodeFileFilteringPatterns(content) {
1670
+ const filtering = {};
1671
+ const configBlocks = content.match(/```(?:json|javascript)\s*\n([\s\S]*?)\n```/g);
1672
+ if (configBlocks) {
1673
+ for (const block of configBlocks) {
1674
+ try {
1675
+ const jsonContent = block.replace(/```(?:json|javascript)\s*\n/, "").replace(/\n```$/, "");
1676
+ const parsed = JSON.parse(jsonContent);
1677
+ if (parsed.fileFiltering) {
1678
+ Object.assign(filtering, parsed.fileFiltering);
1679
+ }
1680
+ } catch {
1681
+ }
1682
+ }
1683
+ }
1684
+ if (content.includes("respectGitIgnore")) {
1685
+ if (content.includes("respectGitIgnore: false") || content.includes('"respectGitIgnore": false')) {
1686
+ filtering.respectGitIgnore = false;
1687
+ } else {
1688
+ filtering.respectGitIgnore = true;
1689
+ }
1690
+ }
1691
+ if (content.includes("enableRecursiveFileSearch")) {
1692
+ if (content.includes("enableRecursiveFileSearch: false") || content.includes('"enableRecursiveFileSearch": false')) {
1693
+ filtering.enableRecursiveFileSearch = false;
1694
+ } else {
1695
+ filtering.enableRecursiveFileSearch = true;
1696
+ }
1697
+ }
1698
+ return Object.keys(filtering).length > 0 ? filtering : void 0;
1699
+ }
1700
+ function generateQwenCodeConfiguration(rules) {
1701
+ const config = {};
1702
+ config.fileFiltering = {
1703
+ respectGitIgnore: true,
1704
+ enableRecursiveFileSearch: true
1705
+ };
1706
+ for (const rule of rules) {
1707
+ const ruleFiltering = extractQwenCodeFileFilteringPatterns(rule.content);
1708
+ if (ruleFiltering) {
1709
+ Object.assign(config.fileFiltering, ruleFiltering);
1710
+ }
1711
+ }
1712
+ return config;
1713
+ }
1714
+ async function generateQwenCodeIgnoreFiles(rules, config, baseDir) {
1715
+ const outputs = [];
1716
+ const outputPath = baseDir || process.cwd();
1717
+ const qwenConfig = generateQwenCodeConfiguration(rules);
1718
+ const settingsPath = join5(outputPath, ".qwen", "settings.json");
1719
+ outputs.push({
1720
+ tool: "qwencode",
1721
+ filepath: settingsPath,
1722
+ content: `${JSON.stringify(qwenConfig, null, 2)}
1723
+ `
1724
+ });
1725
+ return outputs;
1726
+ }
1727
+
1730
1728
  // src/generators/ignore/windsurf.ts
1731
1729
  function generateWindsurfIgnore(rules, config, baseDir) {
1732
1730
  return generateIgnoreFile(rules, config, ignoreConfigs.windsurf, baseDir);
1733
1731
  }
1734
1732
 
1735
- // src/generators/rules/augmentcode.ts
1736
- import { join as join8 } from "path";
1737
-
1738
1733
  // src/generators/rules/shared-helpers.ts
1739
1734
  import { join as join7 } from "path";
1740
1735
 
@@ -1891,7 +1886,61 @@ function generateIgnoreFile2(patterns, tool) {
1891
1886
  return lines.join("\n");
1892
1887
  }
1893
1888
 
1889
+ // src/generators/rules/amazonqcli.ts
1890
+ async function generateAmazonqcliConfig(rules, config, baseDir) {
1891
+ const generatorConfig = {
1892
+ tool: "amazonqcli",
1893
+ fileExtension: ".md",
1894
+ generateContent: generateRuleFile,
1895
+ generateRootContent: generateMainRulesFile,
1896
+ rootFilePath: ".amazonq/rules/main.md",
1897
+ generateDetailContent: generateRuleFile,
1898
+ detailSubDir: ".amazonq/rules"
1899
+ };
1900
+ return generateComplexRules(rules, config, generatorConfig, baseDir);
1901
+ }
1902
+ function generateMainRulesFile(rootRule, detailRules) {
1903
+ const lines = [];
1904
+ if (detailRules.length > 0) {
1905
+ lines.push("# Amazon Q Developer CLI Project Rules");
1906
+ lines.push("");
1907
+ lines.push("This file contains the main project rules. See also:");
1908
+ lines.push("");
1909
+ for (const rule of detailRules) {
1910
+ lines.push(`- ${rule.filename}.md: ${rule.frontmatter.description}`);
1911
+ }
1912
+ lines.push("");
1913
+ }
1914
+ if (rootRule) {
1915
+ if (detailRules.length > 0) {
1916
+ lines.push("## Overview");
1917
+ lines.push("");
1918
+ }
1919
+ lines.push(rootRule.content);
1920
+ lines.push("");
1921
+ } else if (detailRules.length === 0) {
1922
+ lines.push("# Amazon Q Developer CLI Project Rules");
1923
+ lines.push("");
1924
+ lines.push("This file contains project-specific rules and context for Amazon Q Developer CLI.");
1925
+ lines.push("");
1926
+ lines.push("## Development Standards");
1927
+ lines.push("");
1928
+ lines.push("Add your project-specific development standards here.");
1929
+ lines.push("");
1930
+ }
1931
+ return lines.join("\n").trim() + "\n";
1932
+ }
1933
+ function generateRuleFile(rule) {
1934
+ const lines = [];
1935
+ lines.push(`# ${rule.frontmatter.description || rule.filename}`);
1936
+ lines.push("");
1937
+ lines.push(rule.content.trim());
1938
+ lines.push("");
1939
+ return lines.join("\n");
1940
+ }
1941
+
1894
1942
  // src/generators/rules/augmentcode.ts
1943
+ import { join as join8 } from "path";
1895
1944
  async function generateAugmentcodeConfig(rules, config, baseDir) {
1896
1945
  const outputs = createOutputsArray();
1897
1946
  rules.forEach((rule) => {
@@ -1901,12 +1950,12 @@ async function generateAugmentcodeConfig(rules, config, baseDir) {
1901
1950
  config,
1902
1951
  baseDir,
1903
1952
  join8(".augment", "rules", `${rule.filename}.md`),
1904
- generateRuleFile(rule)
1953
+ generateRuleFile2(rule)
1905
1954
  );
1906
1955
  });
1907
1956
  return outputs;
1908
1957
  }
1909
- function generateRuleFile(rule) {
1958
+ function generateRuleFile2(rule) {
1910
1959
  const lines = [];
1911
1960
  lines.push("---");
1912
1961
  let ruleType = "manual";
@@ -2053,6 +2102,22 @@ function determineCursorRuleType(frontmatter) {
2053
2102
  }
2054
2103
  var GENERATOR_REGISTRY = {
2055
2104
  // Simple generators - generate one file per rule
2105
+ amazonqcli: {
2106
+ type: "complex",
2107
+ tool: "amazonqcli",
2108
+ fileExtension: ".md",
2109
+ // ignoreFileName omitted - Amazon Q CLI doesn't have native ignore file support yet
2110
+ generateContent: (rule) => {
2111
+ const lines = [];
2112
+ if (rule.frontmatter.description) {
2113
+ lines.push(`# ${rule.frontmatter.description}
2114
+ `);
2115
+ }
2116
+ lines.push(rule.content.trim());
2117
+ return lines.join("\n");
2118
+ }
2119
+ // Complex generation handled by existing generator
2120
+ },
2056
2121
  cline: {
2057
2122
  type: "simple",
2058
2123
  tool: "cline",
@@ -2219,6 +2284,22 @@ var GENERATOR_REGISTRY = {
2219
2284
  const lines = [];
2220
2285
  if (rule.frontmatter.description) {
2221
2286
  lines.push(`# ${rule.frontmatter.description}
2287
+ `);
2288
+ }
2289
+ lines.push(rule.content.trim());
2290
+ return lines.join("\n");
2291
+ }
2292
+ // Complex generation handled by existing generator
2293
+ },
2294
+ opencode: {
2295
+ type: "complex",
2296
+ tool: "opencode",
2297
+ fileExtension: ".md",
2298
+ // ignoreFileName omitted - OpenCode doesn't use dedicated ignore files
2299
+ generateContent: (rule) => {
2300
+ const lines = [];
2301
+ if (rule.frontmatter.description) {
2302
+ lines.push(`# ${rule.frontmatter.description}
2222
2303
  `);
2223
2304
  }
2224
2305
  lines.push(rule.content.trim());
@@ -2235,6 +2316,22 @@ var GENERATOR_REGISTRY = {
2235
2316
  const lines = [];
2236
2317
  if (rule.frontmatter.description) {
2237
2318
  lines.push(`# ${rule.frontmatter.description}
2319
+ `);
2320
+ }
2321
+ lines.push(rule.content.trim());
2322
+ return lines.join("\n");
2323
+ }
2324
+ // Complex generation handled by existing generator
2325
+ },
2326
+ qwencode: {
2327
+ type: "complex",
2328
+ tool: "qwencode",
2329
+ fileExtension: ".md",
2330
+ // ignoreFileName omitted - Qwen Code uses git-aware filtering instead of dedicated ignore files
2331
+ generateContent: (rule) => {
2332
+ const lines = [];
2333
+ if (rule.frontmatter.description) {
2334
+ lines.push(`# ${rule.frontmatter.description}
2238
2335
  `);
2239
2336
  }
2240
2337
  lines.push(rule.content.trim());
@@ -2261,8 +2358,8 @@ async function generateFromRegistry(tool, rules, config, baseDir) {
2261
2358
  const enhancedConfig = {
2262
2359
  tool: generatorConfig.tool,
2263
2360
  fileExtension: generatorConfig.fileExtension,
2264
- ignoreFileName: generatorConfig.ignoreFileName,
2265
2361
  generateContent: generatorConfig.generateContent,
2362
+ ...generatorConfig.ignoreFileName && { ignoreFileName: generatorConfig.ignoreFileName },
2266
2363
  ...generatorConfig.generateRootContent && {
2267
2364
  generateRootContent: generatorConfig.generateRootContent
2268
2365
  },
@@ -2291,53 +2388,94 @@ var generateCopilotConfig = createSimpleGenerator("copilot");
2291
2388
  var generateWindsurfConfig = createSimpleGenerator("windsurf");
2292
2389
  var generateKiroConfig = createSimpleGenerator("kiro");
2293
2390
  var generateRooConfig = createSimpleGenerator("roo");
2391
+ var generateQwencodeConfig = createSimpleGenerator("qwencode");
2294
2392
 
2295
- // src/generators/rules/codexcli.ts
2296
- async function generateCodexConfig(rules, config, baseDir) {
2297
- 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({
2321
- tool: "codexcli",
2322
- filepath: ignorePath,
2323
- content: ignoreContent
2393
+ // src/utils/xml-document-generator.ts
2394
+ import { XMLBuilder } from "fast-xml-parser";
2395
+ function generateRootMarkdownWithXmlDocs(rootRule, memoryRules, config) {
2396
+ const lines = [];
2397
+ if (memoryRules.length > 0) {
2398
+ lines.push(
2399
+ "Please also reference the following documents as needed. In this case, `@` stands for the project root directory."
2400
+ );
2401
+ lines.push("");
2402
+ const documentsData = {
2403
+ Documents: {
2404
+ Document: memoryRules.map((rule) => {
2405
+ const relativePath = `@${config.memorySubDir}/${rule.filename}.md`;
2406
+ const document = {
2407
+ Path: relativePath,
2408
+ Description: rule.frontmatter.description
2409
+ };
2410
+ if (rule.frontmatter.globs.length > 0) {
2411
+ document.FilePatterns = rule.frontmatter.globs.join(", ");
2412
+ }
2413
+ return document;
2414
+ })
2415
+ }
2416
+ };
2417
+ const builder = new XMLBuilder({
2418
+ format: true,
2419
+ ignoreAttributes: false,
2420
+ suppressEmptyNode: false
2324
2421
  });
2422
+ const xmlContent = builder.build(documentsData);
2423
+ lines.push(xmlContent);
2424
+ lines.push("");
2425
+ lines.push("");
2325
2426
  }
2326
- return outputs;
2327
- }
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);
2427
+ if (rootRule) {
2428
+ lines.push(rootRule.content.trim());
2429
+ } else if (memoryRules.length === 0) {
2430
+ lines.push(`# ${config.fallbackTitle}`);
2431
+ lines.push("");
2432
+ lines.push("No configuration rules have been defined yet.");
2336
2433
  }
2337
- return sections.join("\n\n---\n\n");
2434
+ return lines.join("\n");
2338
2435
  }
2339
2436
 
2340
- // src/generators/rules/geminicli.ts
2437
+ // src/generators/rules/codexcli.ts
2438
+ async function generateCodexConfig(rules, config, baseDir) {
2439
+ const outputs = [];
2440
+ const nonEmptyRules = rules.filter((rule) => rule.content.trim().length > 0);
2441
+ if (nonEmptyRules.length > 0) {
2442
+ const generatorConfig = {
2443
+ tool: "codexcli",
2444
+ fileExtension: ".md",
2445
+ ignoreFileName: ".codexignore",
2446
+ generateContent: generateCodexMemoryMarkdown,
2447
+ generateDetailContent: generateCodexMemoryMarkdown,
2448
+ generateRootContent: generateCodexRootMarkdown,
2449
+ rootFilePath: "AGENTS.md",
2450
+ detailSubDir: ".codex/memories"
2451
+ };
2452
+ const ruleOutputs = await generateComplexRules(nonEmptyRules, config, generatorConfig, baseDir);
2453
+ outputs.push(...ruleOutputs);
2454
+ } else {
2455
+ const ignorePatterns = await loadIgnorePatterns(baseDir);
2456
+ if (ignorePatterns.patterns.length > 0) {
2457
+ const ignorePath = resolvePath(".codexignore", baseDir);
2458
+ const ignoreContent = generateIgnoreFile2(ignorePatterns.patterns, "codexcli");
2459
+ outputs.push({
2460
+ tool: "codexcli",
2461
+ filepath: ignorePath,
2462
+ content: ignoreContent
2463
+ });
2464
+ }
2465
+ }
2466
+ return outputs;
2467
+ }
2468
+ function generateCodexMemoryMarkdown(rule) {
2469
+ return rule.content.trim();
2470
+ }
2471
+ function generateCodexRootMarkdown(rootRule, memoryRules, _baseDir) {
2472
+ return generateRootMarkdownWithXmlDocs(rootRule, memoryRules, {
2473
+ memorySubDir: ".codex/memories",
2474
+ fallbackTitle: "OpenAI Codex CLI Configuration"
2475
+ });
2476
+ }
2477
+
2478
+ // src/generators/rules/geminicli.ts
2341
2479
  async function generateGeminiConfig(rules, config, baseDir) {
2342
2480
  const generatorConfig = {
2343
2481
  tool: "geminicli",
@@ -2355,28 +2493,10 @@ function generateGeminiMemoryMarkdown(rule) {
2355
2493
  return rule.content.trim();
2356
2494
  }
2357
2495
  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");
2496
+ return generateRootMarkdownWithXmlDocs(rootRule, memoryRules, {
2497
+ memorySubDir: ".gemini/memories",
2498
+ fallbackTitle: "Gemini CLI Configuration"
2499
+ });
2380
2500
  }
2381
2501
 
2382
2502
  // src/generators/rules/junie.ts
@@ -2406,6 +2526,54 @@ function generateGuidelinesMarkdown(rootRule, detailRules) {
2406
2526
  return lines.join("\n").trim();
2407
2527
  }
2408
2528
 
2529
+ // src/generators/rules/opencode.ts
2530
+ async function generateOpenCodeConfig(rules, config, baseDir) {
2531
+ const generatorConfig = {
2532
+ tool: "opencode",
2533
+ fileExtension: ".md",
2534
+ // ignoreFileName omitted - OpenCode doesn't use dedicated ignore files
2535
+ generateContent: generateOpenCodeMarkdown,
2536
+ generateDetailContent: generateOpenCodeMarkdown,
2537
+ generateRootContent: generateOpenCodeRootMarkdown,
2538
+ rootFilePath: "AGENTS.md",
2539
+ detailSubDir: ".opencode/memories"
2540
+ };
2541
+ return generateComplexRules(rules, config, generatorConfig, baseDir);
2542
+ }
2543
+ function generateOpenCodeMarkdown(rule) {
2544
+ return rule.content.trim();
2545
+ }
2546
+ function generateOpenCodeRootMarkdown(rootRule, memoryRules, _baseDir) {
2547
+ return generateRootMarkdownWithXmlDocs(rootRule, memoryRules, {
2548
+ memorySubDir: ".opencode/memories",
2549
+ fallbackTitle: "OpenCode Configuration"
2550
+ });
2551
+ }
2552
+
2553
+ // src/generators/rules/qwencode.ts
2554
+ async function generateQwencodeConfig2(rules, config, baseDir) {
2555
+ const generatorConfig = {
2556
+ tool: "qwencode",
2557
+ fileExtension: ".md",
2558
+ // ignoreFileName omitted - Qwen Code uses git-aware filtering instead of dedicated ignore files
2559
+ generateContent: generateQwenMemoryMarkdown,
2560
+ generateDetailContent: generateQwenMemoryMarkdown,
2561
+ generateRootContent: generateQwenRootMarkdown,
2562
+ rootFilePath: "QWEN.md",
2563
+ detailSubDir: ".qwen/memories"
2564
+ };
2565
+ return generateComplexRules(rules, config, generatorConfig, baseDir);
2566
+ }
2567
+ function generateQwenMemoryMarkdown(rule) {
2568
+ return rule.content.trim();
2569
+ }
2570
+ function generateQwenRootMarkdown(rootRule, memoryRules, _baseDir) {
2571
+ return generateRootMarkdownWithXmlDocs(rootRule, memoryRules, {
2572
+ memorySubDir: ".qwen/memories",
2573
+ fallbackTitle: "Qwen Code Configuration"
2574
+ });
2575
+ }
2576
+
2409
2577
  // src/core/generator.ts
2410
2578
  async function generateConfigurations(rules, config, targetTools, baseDir) {
2411
2579
  const outputs = createOutputsArray();
@@ -2438,6 +2606,8 @@ function filterRulesForTool(rules, tool, config) {
2438
2606
  }
2439
2607
  async function generateForTool(tool, rules, config, baseDir) {
2440
2608
  switch (tool) {
2609
+ case "amazonqcli":
2610
+ return await generateAmazonqcliConfig(rules, config, baseDir);
2441
2611
  case "augmentcode": {
2442
2612
  const augmentRulesOutputs = await generateAugmentcodeConfig(rules, config, baseDir);
2443
2613
  const augmentIgnoreOutputs = await generateAugmentCodeIgnoreFiles(rules, config, baseDir);
@@ -2476,6 +2646,13 @@ async function generateForTool(tool, rules, config, baseDir) {
2476
2646
  const kiroIgnoreOutputs = await generateKiroIgnoreFiles(rules, config, baseDir);
2477
2647
  return [...kiroRulesOutputs, ...kiroIgnoreOutputs];
2478
2648
  }
2649
+ case "opencode":
2650
+ return generateOpenCodeConfig(rules, config, baseDir);
2651
+ case "qwencode": {
2652
+ const qwenRulesOutputs = await generateQwencodeConfig2(rules, config, baseDir);
2653
+ const qwenIgnoreOutputs = await generateQwenCodeIgnoreFiles(rules, config, baseDir);
2654
+ return [...qwenRulesOutputs, ...qwenIgnoreOutputs];
2655
+ }
2479
2656
  case "windsurf": {
2480
2657
  const windsurfRulesOutputs = await generateWindsurfConfig(rules, config, baseDir);
2481
2658
  const windsurfIgnoreOutputs = await generateWindsurfIgnore(rules, config, baseDir);
@@ -2639,30 +2816,68 @@ function parseMcpConfig(projectRoot) {
2639
2816
  async function generateMcpConfigurations(mcpConfig, baseDir, targetTools) {
2640
2817
  const outputs = [];
2641
2818
  const toolMap = {
2642
- augmentcode: async (servers, dir) => (await import("./augmentcode-HIZIQG2W.js")).generateAugmentcodeMcpConfiguration(
2819
+ amazonqcli: async (servers, dir) => {
2820
+ const config = {
2821
+ aiRulesDir: ".rulesync",
2822
+ outputPaths: {
2823
+ amazonqcli: ".amazonq/rules",
2824
+ augmentcode: ".",
2825
+ "augmentcode-legacy": ".",
2826
+ copilot: ".github/instructions",
2827
+ cursor: ".cursor/rules",
2828
+ cline: ".clinerules",
2829
+ claudecode: ".",
2830
+ codexcli: ".",
2831
+ opencode: ".",
2832
+ qwencode: ".qwen/memories",
2833
+ roo: ".roo/rules",
2834
+ geminicli: ".gemini/memories",
2835
+ kiro: ".kiro/steering",
2836
+ junie: ".",
2837
+ windsurf: "."
2838
+ },
2839
+ watchEnabled: false,
2840
+ defaultTargets: []
2841
+ };
2842
+ const results = await (await import("./amazonqcli-MW7XTVPN.js")).generateAmazonqcliMcp(
2843
+ servers,
2844
+ config,
2845
+ dir
2846
+ );
2847
+ return results.map((result) => ({ filepath: result.filepath, content: result.content }));
2848
+ },
2849
+ augmentcode: async (servers, dir) => (await import("./augmentcode-WCZCL7VR.js")).generateAugmentcodeMcpConfiguration(
2850
+ servers,
2851
+ dir
2852
+ ),
2853
+ "augmentcode-legacy": async (servers, dir) => (await import("./augmentcode-WCZCL7VR.js")).generateAugmentcodeMcpConfiguration(
2854
+ servers,
2855
+ dir
2856
+ ),
2857
+ claudecode: async (servers, dir) => (await import("./claudecode-RZSJPPBU.js")).generateClaudeMcpConfiguration(
2643
2858
  servers,
2644
2859
  dir
2645
2860
  ),
2646
- "augmentcode-legacy": async (servers, dir) => (await import("./augmentcode-HIZIQG2W.js")).generateAugmentcodeMcpConfiguration(
2861
+ copilot: async (servers, dir) => (await import("./copilot-H3CLGKDP.js")).generateCopilotMcpConfiguration(servers, dir),
2862
+ cursor: async (servers, dir) => (await import("./cursor-ZUN5RZU6.js")).generateCursorMcpConfiguration(servers, dir),
2863
+ cline: async (servers, dir) => (await import("./cline-JTWWBQQ4.js")).generateClineMcpConfiguration(servers, dir),
2864
+ codexcli: async (servers, dir) => (await import("./codexcli-ATMFGRJR.js")).generateCodexMcpConfiguration(servers, dir),
2865
+ opencode: async (servers, dir) => (await import("./opencode-EBS3CED2.js")).generateOpenCodeMcpConfiguration(
2647
2866
  servers,
2648
2867
  dir
2649
2868
  ),
2650
- claudecode: async (servers, dir) => (await import("./claudecode-YTEFACCT.js")).generateClaudeMcpConfiguration(
2869
+ roo: async (servers, dir) => (await import("./roo-KBTRH4TZ.js")).generateRooMcpConfiguration(servers, dir),
2870
+ geminicli: async (servers, dir) => (await import("./geminicli-Q5HPIQCU.js")).generateGeminiCliMcpConfiguration(
2651
2871
  servers,
2652
2872
  dir
2653
2873
  ),
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(
2874
+ kiro: async (servers, dir) => (await import("./kiro-CNF6433S.js")).generateKiroMcpConfiguration(servers, dir),
2875
+ junie: async (servers, dir) => (await import("./junie-JCLVC3MI.js")).generateJunieMcpConfiguration(servers, dir),
2876
+ qwencode: async (servers, dir) => (await import("./qwencode-JIT6KW7E.js")).generateQwenCodeMcpConfiguration(
2660
2877
  servers,
2661
2878
  dir
2662
2879
  ),
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(
2880
+ windsurf: async (servers, dir) => (await import("./windsurf-ZAAWL6JJ.js")).generateWindsurfMcpConfiguration(
2666
2881
  servers,
2667
2882
  dir
2668
2883
  )
@@ -2691,35 +2906,40 @@ async function generateCommand(options = {}) {
2691
2906
  };
2692
2907
  const configResult = await loadConfig(configLoaderOptions);
2693
2908
  const cliOptions = {
2694
- ...options.tools !== void 0 && { tools: options.tools },
2909
+ tools: options.tools,
2695
2910
  ...options.verbose !== void 0 && { verbose: options.verbose },
2696
2911
  ...options.delete !== void 0 && { delete: options.delete },
2697
2912
  ...options.baseDirs !== void 0 && { baseDirs: options.baseDirs }
2698
2913
  };
2699
2914
  const config = mergeWithCliOptions(configResult.config, cliOptions);
2700
- logger.setVerbose(config.verbose || false);
2701
- if (options.tools && options.tools.length > 0) {
2702
- const configTargets = config.defaultTargets;
2703
- const cliTools = options.tools;
2704
- const cliToolsSet = new Set(cliTools);
2705
- const configTargetsSet = new Set(configTargets);
2706
- const notInConfig = cliTools.filter((tool) => !configTargetsSet.has(tool));
2707
- const notInCli = configTargets.filter((tool) => !cliToolsSet.has(tool));
2708
- if (notInConfig.length > 0 || notInCli.length > 0) {
2709
- logger.warn("\u26A0\uFE0F Warning: CLI tool selection differs from configuration!");
2710
- logger.warn(` Config targets: ${configTargets.join(", ")}`);
2711
- logger.warn(` CLI specified: ${cliTools.join(", ")}`);
2712
- if (notInConfig.length > 0) {
2713
- logger.warn(` Tools specified but not in config: ${notInConfig.join(", ")}`);
2714
- }
2715
- if (notInCli.length > 0) {
2716
- logger.warn(` Tools in config but not specified: ${notInCli.join(", ")}`);
2717
- }
2718
- logger.warn("\n The configuration file targets will be used.");
2719
- logger.warn(" To change targets, update your rulesync config file.");
2720
- logger.warn("");
2721
- }
2915
+ if (!config.defaultTargets || config.defaultTargets.length === 0) {
2916
+ const errorMessage = `\u274C Error: At least one tool must be specified.
2917
+
2918
+ Available tools:
2919
+ --augmentcode Generate for AugmentCode
2920
+ --augmentcode-legacy Generate for AugmentCode legacy format
2921
+ --copilot Generate for GitHub Copilot
2922
+ --cursor Generate for Cursor
2923
+ --cline Generate for Cline
2924
+ --codexcli Generate for OpenAI Codex CLI
2925
+ --claudecode Generate for Claude Code
2926
+ --roo Generate for Roo Code
2927
+ --geminicli Generate for Gemini CLI
2928
+ --junie Generate for JetBrains Junie
2929
+ --qwencode Generate for Qwen Code
2930
+ --kiro Generate for Kiro IDE
2931
+ --opencode Generate for OpenCode
2932
+ --windsurf Generate for Windsurf
2933
+
2934
+ Example:
2935
+ rulesync generate --copilot --cursor
2936
+
2937
+ Or specify tools in rulesync.jsonc:
2938
+ "tools": ["copilot", "cursor"]`;
2939
+ logger.error(errorMessage);
2940
+ process.exit(1);
2722
2941
  }
2942
+ logger.setVerbose(config.verbose || false);
2723
2943
  let baseDirs;
2724
2944
  if (config.baseDir) {
2725
2945
  baseDirs = Array.isArray(config.baseDir) ? config.baseDir : [config.baseDir];
@@ -2751,9 +2971,9 @@ async function generateCommand(options = {}) {
2751
2971
  const hasCommands = await fileExists(commandsDir);
2752
2972
  let hasCommandFiles = false;
2753
2973
  if (hasCommands) {
2754
- const { readdir: readdir2 } = await import("fs/promises");
2974
+ const { readdir } = await import("fs/promises");
2755
2975
  try {
2756
- const files = await readdir2(commandsDir);
2976
+ const files = await readdir(commandsDir);
2757
2977
  hasCommandFiles = files.some((file) => file.endsWith(".md"));
2758
2978
  } catch {
2759
2979
  hasCommandFiles = false;
@@ -2799,6 +3019,12 @@ async function generateCommand(options = {}) {
2799
3019
  case "kiro":
2800
3020
  deleteTasks.push(removeDirectory(config.outputPaths.kiro));
2801
3021
  break;
3022
+ case "opencode":
3023
+ deleteTasks.push(removeDirectory(config.outputPaths.opencode));
3024
+ break;
3025
+ case "qwencode":
3026
+ deleteTasks.push(removeDirectory(config.outputPaths.qwencode));
3027
+ break;
2802
3028
  case "windsurf":
2803
3029
  deleteTasks.push(removeDirectory(config.outputPaths.windsurf));
2804
3030
  break;
@@ -2897,6 +3123,8 @@ var gitignoreCommand = async () => {
2897
3123
  const gitignorePath = join14(process.cwd(), ".gitignore");
2898
3124
  const rulesFilesToIgnore = [
2899
3125
  "# Generated by rulesync - AI tool configuration files",
3126
+ "**/.amazonq/rules/",
3127
+ "**/.amazonq/mcp.json",
2900
3128
  "**/.github/copilot-instructions.md",
2901
3129
  "**/.github/instructions/",
2902
3130
  "**/.cursor/rules/",
@@ -2914,6 +3142,8 @@ var gitignoreCommand = async () => {
2914
3142
  "**/GEMINI.md",
2915
3143
  "**/.gemini/memories/",
2916
3144
  "**/.gemini/commands/",
3145
+ "**/QWEN.md",
3146
+ "**/.qwen/memories/",
2917
3147
  "**/.aiexclude",
2918
3148
  "**/.aiignore",
2919
3149
  "**/.augmentignore",
@@ -2922,6 +3152,9 @@ var gitignoreCommand = async () => {
2922
3152
  "**/.augment-guidelines",
2923
3153
  "**/.junie/guidelines.md",
2924
3154
  "**/.noai",
3155
+ "**/.opencode/memories/",
3156
+ "**/.opencode/commands/",
3157
+ "**/opencode.json",
2925
3158
  "**/.mcp.json",
2926
3159
  "!.rulesync/.mcp.json",
2927
3160
  "**/.cursor/mcp.json",
@@ -2929,6 +3162,7 @@ var gitignoreCommand = async () => {
2929
3162
  "**/.vscode/mcp.json",
2930
3163
  "**/.codex/mcp-config.json",
2931
3164
  "**/.gemini/settings.json",
3165
+ "**/.qwen/settings.json",
2932
3166
  "**/.roo/mcp.json"
2933
3167
  ];
2934
3168
  let gitignoreContent = "";
@@ -2963,159 +3197,8 @@ ${linesToAdd.join("\n")}
2963
3197
  import { join as join21 } from "path";
2964
3198
  import matter2 from "gray-matter";
2965
3199
 
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
3200
  // src/parsers/shared-helpers.ts
3118
- import { basename as basename4, join as join16 } from "path";
3201
+ import { basename as basename3, join as join15 } from "path";
3119
3202
  async function parseConfigurationFiles(baseDir = process.cwd(), config) {
3120
3203
  const errors = [];
3121
3204
  const rules = [];
@@ -3168,11 +3251,11 @@ async function parseConfigurationFiles(baseDir = process.cwd(), config) {
3168
3251
  const dirPath = resolvePath(dirConfig.directory, baseDir);
3169
3252
  if (await fileExists(dirPath)) {
3170
3253
  const result = await safeAsyncOperation(async () => {
3171
- const { readdir: readdir2 } = await import("fs/promises");
3172
- const files = await readdir2(dirPath);
3254
+ const { readdir } = await import("fs/promises");
3255
+ const files = await readdir(dirPath);
3173
3256
  for (const file of files) {
3174
3257
  if (file.endsWith(dirConfig.filePattern)) {
3175
- const filePath = join16(dirPath, file);
3258
+ const filePath = join15(dirPath, file);
3176
3259
  const fileResult = await safeAsyncOperation(async () => {
3177
3260
  const rawContent = await readFileContent(filePath);
3178
3261
  let content;
@@ -3319,14 +3402,14 @@ function parseMainFile(content, filepath, config) {
3319
3402
  async function parseMemoryFiles(memoryDir, config) {
3320
3403
  const rules = [];
3321
3404
  try {
3322
- const { readdir: readdir2 } = await import("fs/promises");
3323
- const files = await readdir2(memoryDir);
3405
+ const { readdir } = await import("fs/promises");
3406
+ const files = await readdir(memoryDir);
3324
3407
  for (const file of files) {
3325
3408
  if (file.endsWith(".md")) {
3326
- const filePath = join16(memoryDir, file);
3409
+ const filePath = join15(memoryDir, file);
3327
3410
  const content = await readFileContent(filePath);
3328
3411
  if (content.trim()) {
3329
- const filename = basename4(file, ".md");
3412
+ const filename = basename3(file, ".md");
3330
3413
  const frontmatter = {
3331
3414
  root: false,
3332
3415
  targets: [config.tool],
@@ -3349,14 +3432,14 @@ async function parseMemoryFiles(memoryDir, config) {
3349
3432
  async function parseCommandsFiles(commandsDir, config) {
3350
3433
  const rules = [];
3351
3434
  try {
3352
- const { readdir: readdir2 } = await import("fs/promises");
3353
- const files = await readdir2(commandsDir);
3435
+ const { readdir } = await import("fs/promises");
3436
+ const files = await readdir(commandsDir);
3354
3437
  for (const file of files) {
3355
3438
  if (file.endsWith(".md")) {
3356
- const filePath = join16(commandsDir, file);
3439
+ const filePath = join15(commandsDir, file);
3357
3440
  const content = await readFileContent(filePath);
3358
3441
  if (content.trim()) {
3359
- const filename = basename4(file, ".md");
3442
+ const filename = basename3(file, ".md");
3360
3443
  let frontmatter;
3361
3444
  let ruleContent;
3362
3445
  try {
@@ -3431,6 +3514,170 @@ async function parseSettingsFile(settingsPath, tool) {
3431
3514
  };
3432
3515
  }
3433
3516
 
3517
+ // src/parsers/amazonqcli.ts
3518
+ async function parseAmazonqcliConfiguration(baseDir = process.cwd()) {
3519
+ return parseMemoryBasedConfiguration(baseDir, {
3520
+ tool: "amazonqcli",
3521
+ mainFileName: ".amazonq/rules/main.md",
3522
+ memoryDirPath: ".amazonq/rules",
3523
+ settingsPath: ".amazonq/mcp.json",
3524
+ mainDescription: "Main Amazon Q Developer CLI configuration",
3525
+ memoryDescription: "Amazon Q rule",
3526
+ filenamePrefix: "amazonq"
3527
+ });
3528
+ }
3529
+
3530
+ // src/parsers/augmentcode.ts
3531
+ import { basename as basename4, join as join16 } from "path";
3532
+
3533
+ // src/utils/parser-helpers.ts
3534
+ function createParseResult() {
3535
+ return { rules: [], errors: [] };
3536
+ }
3537
+ function addError(result, error) {
3538
+ result.errors.push(error);
3539
+ }
3540
+ function addRule(result, rule) {
3541
+ if (!result.rules) {
3542
+ result.rules = [];
3543
+ }
3544
+ result.rules.push(rule);
3545
+ }
3546
+ function addRules(result, rules) {
3547
+ if (!result.rules) {
3548
+ result.rules = [];
3549
+ }
3550
+ result.rules.push(...rules);
3551
+ }
3552
+ async function safeReadFile(operation, errorContext) {
3553
+ try {
3554
+ const result = await operation();
3555
+ return createSuccessResult(result);
3556
+ } catch (error) {
3557
+ return createErrorResult(error, errorContext);
3558
+ }
3559
+ }
3560
+
3561
+ // src/parsers/augmentcode.ts
3562
+ async function parseAugmentcodeConfiguration(baseDir = process.cwd()) {
3563
+ return parseUnifiedAugmentcode(baseDir, {
3564
+ rulesDir: ".augment/rules",
3565
+ targetName: "augmentcode",
3566
+ filenamePrefix: "augmentcode"
3567
+ });
3568
+ }
3569
+ async function parseAugmentcodeLegacyConfiguration(baseDir = process.cwd()) {
3570
+ return parseUnifiedAugmentcode(baseDir, {
3571
+ legacyFilePath: ".augment-guidelines",
3572
+ targetName: "augmentcode-legacy",
3573
+ filenamePrefix: "augmentcode-legacy"
3574
+ });
3575
+ }
3576
+ async function parseUnifiedAugmentcode(baseDir, config) {
3577
+ const result = createParseResult();
3578
+ if (config.rulesDir) {
3579
+ const rulesDir = join16(baseDir, config.rulesDir);
3580
+ if (await fileExists(rulesDir)) {
3581
+ const rulesResult = await parseAugmentRules(rulesDir, config);
3582
+ addRules(result, rulesResult.rules);
3583
+ result.errors.push(...rulesResult.errors);
3584
+ } else {
3585
+ addError(
3586
+ result,
3587
+ `No AugmentCode configuration found. Expected ${config.rulesDir} directory.`
3588
+ );
3589
+ }
3590
+ }
3591
+ if (config.legacyFilePath) {
3592
+ const legacyPath = join16(baseDir, config.legacyFilePath);
3593
+ if (await fileExists(legacyPath)) {
3594
+ const legacyResult = await parseAugmentGuidelines(legacyPath, config);
3595
+ if (legacyResult.rule) {
3596
+ addRule(result, legacyResult.rule);
3597
+ }
3598
+ result.errors.push(...legacyResult.errors);
3599
+ } else {
3600
+ addError(
3601
+ result,
3602
+ `No AugmentCode legacy configuration found. Expected ${config.legacyFilePath} file.`
3603
+ );
3604
+ }
3605
+ }
3606
+ return { rules: result.rules || [], errors: result.errors };
3607
+ }
3608
+ async function parseAugmentRules(rulesDir, config) {
3609
+ const rules = [];
3610
+ const errors = [];
3611
+ try {
3612
+ const { readdir } = await import("fs/promises");
3613
+ const files = await readdir(rulesDir);
3614
+ for (const file of files) {
3615
+ if (file.endsWith(".md") || file.endsWith(".mdc")) {
3616
+ const filePath = join16(rulesDir, file);
3617
+ try {
3618
+ const rawContent = await readFileContent(filePath);
3619
+ const parsed = parseFrontmatter(rawContent);
3620
+ const ruleType = extractStringField(parsed.data, "type", "manual");
3621
+ const description = extractStringField(parsed.data, "description", "");
3622
+ const tags = extractArrayField(parsed.data, "tags");
3623
+ const isRoot = ruleType === "always";
3624
+ const filename = basename4(file, file.endsWith(".mdc") ? ".mdc" : ".md");
3625
+ const frontmatter = {
3626
+ root: isRoot,
3627
+ targets: [config.targetName],
3628
+ description,
3629
+ globs: ["**/*"],
3630
+ // AugmentCode doesn't use specific globs in the same way
3631
+ ...tags.length > 0 && { tags }
3632
+ };
3633
+ rules.push({
3634
+ frontmatter,
3635
+ content: parsed.content.trim(),
3636
+ filename: `${config.filenamePrefix}-${ruleType}-${filename}`,
3637
+ filepath: filePath
3638
+ });
3639
+ } catch (error) {
3640
+ const errorMessage = error instanceof Error ? error.message : String(error);
3641
+ errors.push(`Failed to parse ${filePath}: ${errorMessage}`);
3642
+ }
3643
+ }
3644
+ }
3645
+ } catch (error) {
3646
+ const errorMessage = error instanceof Error ? error.message : String(error);
3647
+ errors.push(`Failed to read ${config.rulesDir || rulesDir} directory: ${errorMessage}`);
3648
+ }
3649
+ return { rules, errors };
3650
+ }
3651
+ async function parseAugmentGuidelines(guidelinesPath, config) {
3652
+ const parseResult = await safeReadFile(
3653
+ async () => {
3654
+ const content = await readFileContent(guidelinesPath);
3655
+ if (content.trim()) {
3656
+ const frontmatter = {
3657
+ root: true,
3658
+ // Legacy guidelines become root rules
3659
+ targets: [config.targetName],
3660
+ description: "Legacy AugmentCode guidelines",
3661
+ globs: ["**/*"]
3662
+ };
3663
+ return {
3664
+ frontmatter,
3665
+ content: content.trim(),
3666
+ filename: `${config.filenamePrefix}-guidelines`,
3667
+ filepath: guidelinesPath
3668
+ };
3669
+ }
3670
+ return null;
3671
+ },
3672
+ `Failed to parse ${config.legacyFilePath || guidelinesPath}`
3673
+ );
3674
+ if (parseResult.success) {
3675
+ return { rule: parseResult.result || null, errors: [] };
3676
+ } else {
3677
+ return { rule: null, errors: [parseResult.error || "Unknown error"] };
3678
+ }
3679
+ }
3680
+
3434
3681
  // src/parsers/claudecode.ts
3435
3682
  async function parseClaudeConfiguration(baseDir = process.cwd()) {
3436
3683
  return parseMemoryBasedConfiguration(baseDir, {
@@ -3637,8 +3884,8 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
3637
3884
  const cursorRulesDir = join18(baseDir, ".cursor", "rules");
3638
3885
  if (await fileExists(cursorRulesDir)) {
3639
3886
  try {
3640
- const { readdir: readdir2 } = await import("fs/promises");
3641
- const files = await readdir2(cursorRulesDir);
3887
+ const { readdir } = await import("fs/promises");
3888
+ const files = await readdir(cursorRulesDir);
3642
3889
  for (const file of files) {
3643
3890
  if (file.endsWith(".mdc")) {
3644
3891
  const filePath = join18(cursorRulesDir, file);
@@ -3768,6 +4015,48 @@ async function parseJunieConfiguration(baseDir = process.cwd()) {
3768
4015
  return { rules, errors };
3769
4016
  }
3770
4017
 
4018
+ // src/parsers/opencode.ts
4019
+ async function parseOpCodeIgnore(opcodeignorePath) {
4020
+ try {
4021
+ const content = await readFileContent(opcodeignorePath);
4022
+ const patterns = content.split("\n").map((line) => line.trim()).filter((line) => line && !line.startsWith("#"));
4023
+ return patterns;
4024
+ } catch {
4025
+ return [];
4026
+ }
4027
+ }
4028
+ async function parseOpenCodeConfiguration(baseDir = process.cwd()) {
4029
+ return parseMemoryBasedConfiguration(baseDir, {
4030
+ tool: "opencode",
4031
+ mainFileName: "AGENTS.md",
4032
+ memoryDirPath: ".opencode/memories",
4033
+ settingsPath: "opencode.json",
4034
+ mainDescription: "Main OpenCode configuration",
4035
+ memoryDescription: "Memory file",
4036
+ filenamePrefix: "opencode",
4037
+ additionalIgnoreFile: {
4038
+ path: ".opcodeignore",
4039
+ parser: parseOpCodeIgnore
4040
+ }
4041
+ });
4042
+ }
4043
+
4044
+ // src/parsers/qwencode.ts
4045
+ async function parseQwenConfiguration(baseDir = process.cwd()) {
4046
+ return parseMemoryBasedConfiguration(baseDir, {
4047
+ tool: "qwencode",
4048
+ mainFileName: "QWEN.md",
4049
+ memoryDirPath: ".qwen/memories",
4050
+ settingsPath: ".qwen/settings.json",
4051
+ mainDescription: "Main Qwen Code configuration",
4052
+ memoryDescription: "Memory file",
4053
+ filenamePrefix: "qwen",
4054
+ // Qwen Code uses git-aware filtering instead of dedicated ignore files
4055
+ // additionalIgnoreFile is omitted
4056
+ commandsDirPath: ".qwen/commands"
4057
+ });
4058
+ }
4059
+
3771
4060
  // src/parsers/roo.ts
3772
4061
  async function parseRooConfiguration(baseDir = process.cwd()) {
3773
4062
  return parseConfigurationFiles(baseDir, {
@@ -3789,7 +4078,7 @@ async function parseRooConfiguration(baseDir = process.cwd()) {
3789
4078
  }
3790
4079
 
3791
4080
  // src/parsers/windsurf.ts
3792
- import { readFile as readFile2 } from "fs/promises";
4081
+ import { readFile } from "fs/promises";
3793
4082
  import { join as join20 } from "path";
3794
4083
 
3795
4084
  // src/core/importer.ts
@@ -3810,6 +4099,13 @@ async function importConfiguration(options) {
3810
4099
  }
3811
4100
  try {
3812
4101
  switch (tool) {
4102
+ case "amazonqcli": {
4103
+ const amazonqResult = await parseAmazonqcliConfiguration(baseDir);
4104
+ rules = amazonqResult.rules;
4105
+ errors.push(...amazonqResult.errors);
4106
+ mcpServers = amazonqResult.mcpServers;
4107
+ break;
4108
+ }
3813
4109
  case "augmentcode": {
3814
4110
  const augmentResult = await parseAugmentcodeConfiguration(baseDir);
3815
4111
  rules = augmentResult.rules;
@@ -3870,6 +4166,21 @@ async function importConfiguration(options) {
3870
4166
  errors.push(...junieResult.errors);
3871
4167
  break;
3872
4168
  }
4169
+ case "opencode": {
4170
+ const opencodeResult = await parseOpenCodeConfiguration(baseDir);
4171
+ rules = opencodeResult.rules;
4172
+ errors.push(...opencodeResult.errors);
4173
+ ignorePatterns = opencodeResult.ignorePatterns;
4174
+ mcpServers = opencodeResult.mcpServers;
4175
+ break;
4176
+ }
4177
+ case "qwencode": {
4178
+ const qwenResult = await parseQwenConfiguration(baseDir);
4179
+ rules = qwenResult.rules;
4180
+ errors.push(...qwenResult.errors);
4181
+ mcpServers = qwenResult.mcpServers;
4182
+ break;
4183
+ }
3873
4184
  default:
3874
4185
  errors.push(`Unsupported tool: ${tool}`);
3875
4186
  return { success: false, rulesCreated: 0, errors };
@@ -3884,8 +4195,8 @@ async function importConfiguration(options) {
3884
4195
  }
3885
4196
  const rulesDirPath = join21(baseDir, rulesDir);
3886
4197
  try {
3887
- const { mkdir: mkdir3 } = await import("fs/promises");
3888
- await mkdir3(rulesDirPath, { recursive: true });
4198
+ const { mkdir: mkdir2 } = await import("fs/promises");
4199
+ await mkdir2(rulesDirPath, { recursive: true });
3889
4200
  } catch (error) {
3890
4201
  const errorMessage = error instanceof Error ? error.message : String(error);
3891
4202
  errors.push(`Failed to create rules directory: ${errorMessage}`);
@@ -3898,13 +4209,13 @@ async function importConfiguration(options) {
3898
4209
  let targetDir = rulesDirPath;
3899
4210
  if (rule.type === "command") {
3900
4211
  targetDir = join21(rulesDirPath, "commands");
3901
- const { mkdir: mkdir3 } = await import("fs/promises");
3902
- await mkdir3(targetDir, { recursive: true });
4212
+ const { mkdir: mkdir2 } = await import("fs/promises");
4213
+ await mkdir2(targetDir, { recursive: true });
3903
4214
  } else {
3904
4215
  if (!useLegacyLocation) {
3905
4216
  targetDir = join21(rulesDirPath, "rules");
3906
- const { mkdir: mkdir3 } = await import("fs/promises");
3907
- await mkdir3(targetDir, { recursive: true });
4217
+ const { mkdir: mkdir2 } = await import("fs/promises");
4218
+ await mkdir2(targetDir, { recursive: true });
3908
4219
  }
3909
4220
  }
3910
4221
  const filePath = join21(targetDir, `${baseFilename}.md`);
@@ -3976,6 +4287,7 @@ function generateRuleFileContent(rule) {
3976
4287
  async function importCommand(options = {}) {
3977
4288
  logger.setVerbose(options.verbose || false);
3978
4289
  const tools = [];
4290
+ if (options.amazonqcli) tools.push("amazonqcli");
3979
4291
  if (options.augmentcode) tools.push("augmentcode");
3980
4292
  if (options["augmentcode-legacy"]) tools.push("augmentcode-legacy");
3981
4293
  if (options.claudecode) tools.push("claudecode");
@@ -3984,9 +4296,11 @@ async function importCommand(options = {}) {
3984
4296
  if (options.cline) tools.push("cline");
3985
4297
  if (options.roo) tools.push("roo");
3986
4298
  if (options.geminicli) tools.push("geminicli");
4299
+ if (options.qwencode) tools.push("qwencode");
4300
+ if (options.opencode) tools.push("opencode");
3987
4301
  if (tools.length === 0) {
3988
4302
  logger.error(
3989
- "\u274C Please specify one tool to import from (--augmentcode, --augmentcode-legacy, --claudecode, --cursor, --copilot, --cline, --roo, --geminicli)"
4303
+ "\u274C Please specify one tool to import from (--amazonqcli, --augmentcode, --augmentcode-legacy, --claudecode, --cursor, --copilot, --cline, --roo, --geminicli, --qwencode, --opencode)"
3990
4304
  );
3991
4305
  process.exit(1);
3992
4306
  }
@@ -4232,37 +4546,41 @@ async function watchCommand() {
4232
4546
 
4233
4547
  // src/cli/index.ts
4234
4548
  var program = new Command();
4235
- program.name("rulesync").description("Unified AI rules management CLI tool").version("0.63.0");
4549
+ program.name("rulesync").description("Unified AI rules management CLI tool").version("0.65.0");
4236
4550
  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
4551
  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
4552
  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(
4553
+ 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("--qwencode", "Import from Qwen Code (QWEN.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);
4554
+ program.command("generate").description("Generate configuration files for AI tools").option("--all", "Generate for all supported 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("--qwencode", "Generate only for Qwen Code").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
4555
  "-b, --base-dir <paths>",
4242
4556
  "Base directories to generate files (comma-separated for multiple paths)"
4243
4557
  ).option("-v, --verbose", "Verbose output").option("-c, --config <path>", "Path to configuration file").option("--no-config", "Disable configuration file loading").action(async (options) => {
4244
4558
  const tools = [];
4245
- if (options.augmentcode) tools.push("augmentcode");
4246
- if (options["augmentcode-legacy"]) tools.push("augmentcode-legacy");
4247
- if (options.copilot) tools.push("copilot");
4248
- if (options.cursor) tools.push("cursor");
4249
- if (options.cline) tools.push("cline");
4250
- if (options.codexcli) tools.push("codexcli");
4251
- if (options.claudecode) tools.push("claudecode");
4252
- if (options.roo) tools.push("roo");
4253
- if (options.geminicli) tools.push("geminicli");
4254
- if (options.junie) tools.push("junie");
4255
- if (options.kiro) tools.push("kiro");
4256
- if (options.windsurf) tools.push("windsurf");
4559
+ if (options.all) {
4560
+ tools.push(...ALL_TOOL_TARGETS);
4561
+ } else {
4562
+ if (options.augmentcode) tools.push("augmentcode");
4563
+ if (options["augmentcode-legacy"]) tools.push("augmentcode-legacy");
4564
+ if (options.copilot) tools.push("copilot");
4565
+ if (options.cursor) tools.push("cursor");
4566
+ if (options.cline) tools.push("cline");
4567
+ if (options.codexcli) tools.push("codexcli");
4568
+ if (options.claudecode) tools.push("claudecode");
4569
+ if (options.roo) tools.push("roo");
4570
+ if (options.geminicli) tools.push("geminicli");
4571
+ if (options.junie) tools.push("junie");
4572
+ if (options.qwencode) tools.push("qwencode");
4573
+ if (options.kiro) tools.push("kiro");
4574
+ if (options.opencode) tools.push("opencode");
4575
+ if (options.windsurf) tools.push("windsurf");
4576
+ }
4257
4577
  const generateOptions = {
4258
4578
  verbose: options.verbose,
4579
+ tools: tools.length > 0 ? tools : void 0,
4259
4580
  delete: options.delete,
4260
4581
  config: options.config,
4261
4582
  noConfig: options.noConfig
4262
4583
  };
4263
- if (tools.length > 0) {
4264
- generateOptions.tools = tools;
4265
- }
4266
4584
  if (options.baseDir) {
4267
4585
  generateOptions.baseDirs = options.baseDir.split(",").map((dir) => dir.trim()).filter((dir) => dir.length > 0);
4268
4586
  }