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.cjs CHANGED
@@ -41,6 +41,7 @@ var init_tool_targets = __esm({
41
41
  "use strict";
42
42
  import_mini2 = require("zod/mini");
43
43
  ALL_TOOL_TARGETS = [
44
+ "amazonqcli",
44
45
  "augmentcode",
45
46
  "augmentcode-legacy",
46
47
  "copilot",
@@ -48,6 +49,7 @@ var init_tool_targets = __esm({
48
49
  "cline",
49
50
  "claudecode",
50
51
  "codexcli",
52
+ "opencode",
51
53
  "roo",
52
54
  "geminicli",
53
55
  "kiro",
@@ -61,6 +63,152 @@ var init_tool_targets = __esm({
61
63
  }
62
64
  });
63
65
 
66
+ // src/utils/logger.ts
67
+ var import_consola, Logger, logger;
68
+ var init_logger = __esm({
69
+ "src/utils/logger.ts"() {
70
+ "use strict";
71
+ import_consola = require("consola");
72
+ Logger = class {
73
+ _verbose = false;
74
+ console = import_consola.consola.withDefaults({
75
+ tag: "rulesync"
76
+ });
77
+ setVerbose(verbose) {
78
+ this._verbose = verbose;
79
+ }
80
+ get verbose() {
81
+ return this._verbose;
82
+ }
83
+ // Regular log (always shown, regardless of verbose)
84
+ log(message, ...args) {
85
+ this.console.log(message, ...args);
86
+ }
87
+ // Info level (shown only in verbose mode)
88
+ info(message, ...args) {
89
+ if (this._verbose) {
90
+ this.console.info(message, ...args);
91
+ }
92
+ }
93
+ // Success (always shown)
94
+ success(message, ...args) {
95
+ this.console.success(message, ...args);
96
+ }
97
+ // Warning (always shown)
98
+ warn(message, ...args) {
99
+ this.console.warn(message, ...args);
100
+ }
101
+ // Error (always shown)
102
+ error(message, ...args) {
103
+ this.console.error(message, ...args);
104
+ }
105
+ // Debug level (shown only in verbose mode)
106
+ debug(message, ...args) {
107
+ if (this._verbose) {
108
+ this.console.debug(message, ...args);
109
+ }
110
+ }
111
+ };
112
+ logger = new Logger();
113
+ }
114
+ });
115
+
116
+ // src/utils/file.ts
117
+ async function ensureDir(dirPath) {
118
+ try {
119
+ await (0, import_promises2.stat)(dirPath);
120
+ } catch {
121
+ await (0, import_promises2.mkdir)(dirPath, { recursive: true });
122
+ }
123
+ }
124
+ function resolvePath(relativePath, baseDir) {
125
+ if (!baseDir) return relativePath;
126
+ const resolved = (0, import_node_path.resolve)(baseDir, relativePath);
127
+ const rel = (0, import_node_path.relative)(baseDir, resolved);
128
+ if (rel.startsWith("..") || (0, import_node_path.resolve)(resolved) !== resolved) {
129
+ throw new Error(`Path traversal detected: ${relativePath}`);
130
+ }
131
+ return resolved;
132
+ }
133
+ async function readFileContent(filepath) {
134
+ return (0, import_promises2.readFile)(filepath, "utf-8");
135
+ }
136
+ async function writeFileContent(filepath, content) {
137
+ await ensureDir((0, import_node_path.dirname)(filepath));
138
+ await (0, import_promises2.writeFile)(filepath, content, "utf-8");
139
+ }
140
+ async function fileExists(filepath) {
141
+ try {
142
+ await (0, import_promises2.stat)(filepath);
143
+ return true;
144
+ } catch {
145
+ return false;
146
+ }
147
+ }
148
+ async function findFiles(dir, extension = ".md") {
149
+ try {
150
+ const files = await (0, import_promises2.readdir)(dir);
151
+ return files.filter((file) => file.endsWith(extension)).map((file) => (0, import_node_path.join)(dir, file));
152
+ } catch {
153
+ return [];
154
+ }
155
+ }
156
+ async function findRuleFiles(aiRulesDir) {
157
+ const rulesDir = (0, import_node_path.join)(aiRulesDir, "rules");
158
+ const newLocationFiles = await findFiles(rulesDir, ".md");
159
+ const legacyLocationFiles = await findFiles(aiRulesDir, ".md");
160
+ const newLocationBasenames = new Set(
161
+ newLocationFiles.map((file) => file.split("/").pop()?.replace(/\.md$/, ""))
162
+ );
163
+ const filteredLegacyFiles = legacyLocationFiles.filter((file) => {
164
+ const basename6 = file.split("/").pop()?.replace(/\.md$/, "");
165
+ return !newLocationBasenames.has(basename6);
166
+ });
167
+ return [...newLocationFiles, ...filteredLegacyFiles];
168
+ }
169
+ async function removeDirectory(dirPath) {
170
+ const dangerousPaths = [".", "/", "~", "src", "node_modules"];
171
+ if (dangerousPaths.includes(dirPath) || dirPath === "") {
172
+ logger.warn(`Skipping deletion of dangerous path: ${dirPath}`);
173
+ return;
174
+ }
175
+ try {
176
+ if (await fileExists(dirPath)) {
177
+ await (0, import_promises2.rm)(dirPath, { recursive: true, force: true });
178
+ }
179
+ } catch (error) {
180
+ logger.warn(`Failed to remove directory ${dirPath}:`, error);
181
+ }
182
+ }
183
+ async function removeFile(filepath) {
184
+ try {
185
+ if (await fileExists(filepath)) {
186
+ await (0, import_promises2.rm)(filepath);
187
+ }
188
+ } catch (error) {
189
+ logger.warn(`Failed to remove file ${filepath}:`, error);
190
+ }
191
+ }
192
+ async function removeClaudeGeneratedFiles() {
193
+ const filesToRemove = ["CLAUDE.md", ".claude/memories"];
194
+ for (const fileOrDir of filesToRemove) {
195
+ if (fileOrDir.endsWith("/memories")) {
196
+ await removeDirectory(fileOrDir);
197
+ } else {
198
+ await removeFile(fileOrDir);
199
+ }
200
+ }
201
+ }
202
+ var import_promises2, import_node_path;
203
+ var init_file = __esm({
204
+ "src/utils/file.ts"() {
205
+ "use strict";
206
+ import_promises2 = require("fs/promises");
207
+ import_node_path = require("path");
208
+ init_logger();
209
+ }
210
+ });
211
+
64
212
  // src/utils/mcp-helpers.ts
65
213
  function shouldIncludeServer(server, targetTool) {
66
214
  if (!server.targets || server.targets.length === 0) {
@@ -85,6 +233,75 @@ var init_mcp_helpers = __esm({
85
233
  }
86
234
  });
87
235
 
236
+ // src/generators/mcp/amazonqcli.ts
237
+ var amazonqcli_exports = {};
238
+ __export(amazonqcli_exports, {
239
+ generateAmazonqcliMcp: () => generateAmazonqcliMcp,
240
+ generateAmazonqcliMcpString: () => generateAmazonqcliMcpString
241
+ });
242
+ async function generateAmazonqcliMcp(mcpServers, config, baseDir) {
243
+ const outputs = [];
244
+ const configPaths = [
245
+ ".amazonq/mcp.json"
246
+ // Workspace configuration
247
+ // Note: Global configuration is ~/.aws/amazonq/mcp.json but is user-specific
248
+ // According to precautions.md, we should not create user-level files
249
+ ];
250
+ for (const configPath of configPaths) {
251
+ const filepath = resolvePath(configPath, baseDir);
252
+ const content = generateAmazonqcliMcpConfig({ mcpServers });
253
+ outputs.push({
254
+ tool: "amazonqcli",
255
+ filepath,
256
+ content
257
+ });
258
+ }
259
+ return outputs;
260
+ }
261
+ function generateAmazonqcliMcpString(config) {
262
+ return generateAmazonqcliMcpConfig(config);
263
+ }
264
+ function generateAmazonqcliMcpConfig(config) {
265
+ const servers = {};
266
+ for (const [serverName, server] of Object.entries(config.mcpServers)) {
267
+ if (!shouldIncludeServer(server, "amazonqcli")) {
268
+ continue;
269
+ }
270
+ const amazonqServer = {};
271
+ if (server.command) {
272
+ amazonqServer.command = server.command;
273
+ if (server.args) {
274
+ amazonqServer.args = server.args;
275
+ }
276
+ }
277
+ if (server.env) {
278
+ amazonqServer.env = server.env;
279
+ }
280
+ if (server.timeout !== void 0) {
281
+ amazonqServer.timeout = server.timeout;
282
+ }
283
+ if (server.disabled !== void 0) {
284
+ amazonqServer.disabled = server.disabled;
285
+ }
286
+ if (server.alwaysAllow) {
287
+ amazonqServer.autoApprove = server.alwaysAllow;
288
+ }
289
+ servers[serverName] = amazonqServer;
290
+ }
291
+ const finalConfig = {
292
+ mcpServers: servers
293
+ };
294
+ return `${JSON.stringify(finalConfig, null, 2)}
295
+ `;
296
+ }
297
+ var init_amazonqcli = __esm({
298
+ "src/generators/mcp/amazonqcli.ts"() {
299
+ "use strict";
300
+ init_file();
301
+ init_mcp_helpers();
302
+ }
303
+ });
304
+
88
305
  // src/generators/mcp/augmentcode.ts
89
306
  var augmentcode_exports = {};
90
307
  __export(augmentcode_exports, {
@@ -104,7 +321,10 @@ function generateAugmentcodeMcp(config) {
104
321
  name: serverName
105
322
  };
106
323
  if (server.command) {
107
- augmentServer.command = server.command;
324
+ const command = Array.isArray(server.command) ? server.command[0] : server.command;
325
+ if (command) {
326
+ augmentServer.command = command;
327
+ }
108
328
  if (server.args) {
109
329
  augmentServer.args = server.args;
110
330
  }
@@ -152,7 +372,10 @@ function generateAugmentcodeMcpConfiguration(mcpServers, baseDir = "") {
152
372
  const { targets: _, ...serverConfig } = server;
153
373
  const augmentServer = {};
154
374
  if (serverConfig.command) {
155
- augmentServer.command = serverConfig.command;
375
+ const command = Array.isArray(serverConfig.command) ? serverConfig.command[0] : serverConfig.command;
376
+ if (command) {
377
+ augmentServer.command = command;
378
+ }
156
379
  if (serverConfig.args) {
157
380
  augmentServer.args = serverConfig.args;
158
381
  }
@@ -199,6 +422,17 @@ var init_augmentcode = __esm({
199
422
  }
200
423
  });
201
424
 
425
+ // src/constants/schemas.ts
426
+ var SCHEMA_URLS;
427
+ var init_schemas = __esm({
428
+ "src/constants/schemas.ts"() {
429
+ "use strict";
430
+ SCHEMA_URLS = {
431
+ OPENCODE: "https://opencode.ai/config.json"
432
+ };
433
+ }
434
+ });
435
+
202
436
  // src/generators/mcp/shared-factory.ts
203
437
  function generateMcpConfig(config, toolConfig) {
204
438
  const servers = {};
@@ -248,7 +482,7 @@ function generateMcpConfigurationFilesFromRegistry(tool, mcpServers, baseDir = "
248
482
  if (tool === "junie") {
249
483
  return generateJunieMcpConfigurationFiles(mcpServers, baseDir);
250
484
  }
251
- const customTools = ["copilot", "augmentcode", "roo", "codexcli", "kiro", "geminicli"];
485
+ const customTools = ["copilot", "augmentcode", "codexcli", "kiro"];
252
486
  if (customTools.includes(tool)) {
253
487
  throw new Error(
254
488
  `Tool ${tool} uses custom configuration logic - use its specific generator function instead`
@@ -297,6 +531,7 @@ var serverTransforms, configWrappers, MCP_GENERATOR_REGISTRY, cursorMcpGenerator
297
531
  var init_shared_factory = __esm({
298
532
  "src/generators/mcp/shared-factory.ts"() {
299
533
  "use strict";
534
+ init_schemas();
300
535
  init_mcp_helpers();
301
536
  serverTransforms = {
302
537
  /**
@@ -307,7 +542,8 @@ var init_shared_factory = __esm({
307
542
  if (server.command) {
308
543
  result.command = server.command;
309
544
  if (server.args) result.args = server.args;
310
- } else if (server.url || server.httpUrl) {
545
+ }
546
+ if (server.url || server.httpUrl) {
311
547
  const url = server.httpUrl || server.url;
312
548
  if (url) result.url = url;
313
549
  }
@@ -316,6 +552,25 @@ var init_shared_factory = __esm({
316
552
  }
317
553
  return result;
318
554
  },
555
+ /**
556
+ * Roo-specific server transformation (preserves httpUrl, transport, type, etc.)
557
+ */
558
+ roo: (server) => {
559
+ const result = serverTransforms.extended(server);
560
+ if (server.httpUrl) {
561
+ if (!server.url) {
562
+ result.httpUrl = server.httpUrl;
563
+ delete result.url;
564
+ }
565
+ }
566
+ if (server.transport) {
567
+ result.transport = server.transport;
568
+ }
569
+ if (server.type) {
570
+ result.type = server.type;
571
+ }
572
+ return result;
573
+ },
319
574
  /**
320
575
  * Extended server transformation (includes disabled, alwaysAllow, etc.)
321
576
  */
@@ -367,6 +622,12 @@ var init_shared_factory = __esm({
367
622
  })
368
623
  };
369
624
  MCP_GENERATOR_REGISTRY = {
625
+ roo: {
626
+ target: "roo",
627
+ configPaths: [".roo/mcp.json"],
628
+ serverTransform: serverTransforms.roo,
629
+ configWrapper: configWrappers.mcpServers
630
+ },
370
631
  claudecode: {
371
632
  target: "claudecode",
372
633
  configPaths: [".mcp.json"],
@@ -493,6 +754,48 @@ var init_shared_factory = __esm({
493
754
  configPaths: [".cline/mcp.json"],
494
755
  serverTransform: serverTransforms.extended,
495
756
  configWrapper: configWrappers.mcpServers
757
+ },
758
+ geminicli: {
759
+ target: "geminicli",
760
+ configPaths: [".gemini/settings.json"],
761
+ serverTransform: (server) => {
762
+ const { targets: _, ...serverConfig } = server;
763
+ const geminiServer = { ...serverConfig };
764
+ if (server.env) {
765
+ geminiServer.env = server.env;
766
+ }
767
+ return geminiServer;
768
+ },
769
+ configWrapper: configWrappers.mcpServers
770
+ },
771
+ opencode: {
772
+ target: "opencode",
773
+ configPaths: ["opencode.json"],
774
+ serverTransform: (server) => {
775
+ const opencodeServer = {};
776
+ if (server.command) {
777
+ opencodeServer.type = "local";
778
+ opencodeServer.command = Array.isArray(server.command) ? server.command : [server.command];
779
+ if (server.args) opencodeServer.args = server.args;
780
+ if (server.env) opencodeServer.environment = server.env;
781
+ if (server.cwd) opencodeServer.cwd = server.cwd;
782
+ } else if (server.url || server.httpUrl) {
783
+ opencodeServer.type = "remote";
784
+ const url = server.httpUrl || server.url;
785
+ if (url) opencodeServer.url = url;
786
+ if (server.headers) opencodeServer.headers = server.headers;
787
+ }
788
+ if (server.disabled !== void 0) {
789
+ opencodeServer.enabled = !server.disabled;
790
+ } else {
791
+ opencodeServer.enabled = true;
792
+ }
793
+ return opencodeServer;
794
+ },
795
+ configWrapper: (servers) => ({
796
+ $schema: SCHEMA_URLS.OPENCODE,
797
+ mcp: servers
798
+ })
496
799
  }
497
800
  };
498
801
  cursorMcpGenerator = createMcpGenerator("cursor");
@@ -696,7 +999,10 @@ function generateCopilotMcp(config, target) {
696
999
  if (!shouldIncludeServer(server, "copilot")) continue;
697
1000
  const copilotServer = {};
698
1001
  if (server.command) {
699
- copilotServer.command = server.command;
1002
+ const command = Array.isArray(server.command) ? server.command[0] : server.command;
1003
+ if (command) {
1004
+ copilotServer.command = command;
1005
+ }
700
1006
  if (server.args) copilotServer.args = server.args;
701
1007
  } else if (server.url || server.httpUrl) {
702
1008
  const url = server.httpUrl || server.url;
@@ -786,53 +1092,10 @@ __export(geminicli_exports, {
786
1092
  generateGeminiCliMcpConfiguration: () => generateGeminiCliMcpConfiguration
787
1093
  });
788
1094
  function generateGeminiCliMcp(config) {
789
- return generateMcpConfig(config, {
790
- target: "geminicli",
791
- configPaths: [".gemini/settings.json"],
792
- serverTransform: (server) => {
793
- const geminiServer = {};
794
- if (server.command) {
795
- geminiServer.command = server.command;
796
- if (server.args) geminiServer.args = server.args;
797
- } else if (server.url || server.httpUrl) {
798
- if (server.httpUrl) {
799
- geminiServer.httpUrl = server.httpUrl;
800
- } else if (server.url) {
801
- geminiServer.url = server.url;
802
- }
803
- }
804
- if (server.env) {
805
- geminiServer.env = server.env;
806
- }
807
- if (server.timeout !== void 0) {
808
- geminiServer.timeout = server.timeout;
809
- }
810
- if (server.trust !== void 0) {
811
- geminiServer.trust = server.trust;
812
- }
813
- return geminiServer;
814
- },
815
- configWrapper: configWrappers.mcpServers
816
- });
1095
+ return generateMcpFromRegistry("geminicli", config);
817
1096
  }
818
1097
  function generateGeminiCliMcpConfiguration(mcpServers, baseDir = "") {
819
- return generateMcpConfigurationFiles(
820
- mcpServers,
821
- {
822
- target: "geminicli",
823
- configPaths: [".gemini/settings.json"],
824
- serverTransform: (server) => {
825
- const { targets: _, ...serverConfig } = server;
826
- const geminiServer = { ...serverConfig };
827
- if (server.env) {
828
- geminiServer.env = server.env;
829
- }
830
- return geminiServer;
831
- },
832
- configWrapper: configWrappers.mcpServers
833
- },
834
- baseDir
835
- );
1098
+ return generateMcpConfigurationFilesFromRegistry("geminicli", mcpServers, baseDir);
836
1099
  }
837
1100
  var init_geminicli = __esm({
838
1101
  "src/generators/mcp/geminicli.ts"() {
@@ -956,77 +1219,15 @@ __export(roo_exports, {
956
1219
  generateRooMcpConfiguration: () => generateRooMcpConfiguration
957
1220
  });
958
1221
  function generateRooMcp(config) {
959
- const rooConfig = {
960
- mcpServers: {}
961
- };
962
- for (const [serverName, server] of Object.entries(config.mcpServers)) {
963
- if (!shouldIncludeServer(server, "roo")) continue;
964
- const rooServer = {};
965
- if (server.command) {
966
- rooServer.command = server.command;
967
- if (server.args) rooServer.args = server.args;
968
- } else if (server.url || server.httpUrl) {
969
- const url = server.httpUrl || server.url;
970
- if (url) {
971
- rooServer.url = url;
972
- }
973
- if (server.httpUrl || server.transport === "http") {
974
- rooServer.type = "streamable-http";
975
- } else if (server.transport === "sse" || server.type === "sse") {
976
- rooServer.type = "sse";
977
- }
978
- }
979
- if (server.env) {
980
- rooServer.env = {};
981
- for (const [key, value] of Object.entries(server.env)) {
982
- if (value.startsWith("${env:") && value.endsWith("}")) {
983
- rooServer.env[key] = value;
984
- } else {
985
- rooServer.env[key] = `\${env:${value}}`;
986
- }
987
- }
988
- }
989
- if (server.disabled !== void 0) {
990
- rooServer.disabled = server.disabled;
991
- }
992
- if (server.alwaysAllow) {
993
- rooServer.alwaysAllow = server.alwaysAllow;
994
- }
995
- if (server.networkTimeout !== void 0) {
996
- rooServer.networkTimeout = Math.max(3e4, Math.min(3e5, server.networkTimeout));
997
- }
998
- rooConfig.mcpServers[serverName] = rooServer;
999
- }
1000
- return JSON.stringify(rooConfig, null, 2);
1222
+ return generateMcpFromRegistry("roo", config);
1001
1223
  }
1002
1224
  function generateRooMcpConfiguration(mcpServers, baseDir = "") {
1003
- const filepath = baseDir ? `${baseDir}/.roo/mcp.json` : ".roo/mcp.json";
1004
- const config = {
1005
- mcpServers: {}
1006
- };
1007
- for (const [serverName, server] of Object.entries(mcpServers)) {
1008
- if (!shouldIncludeServer(server, "roo")) {
1009
- continue;
1010
- }
1011
- const { targets: _targets, ...serverConfig } = server;
1012
- const rooServer = { ...serverConfig };
1013
- if (serverConfig.httpUrl !== void 0 && serverConfig.url !== void 0) {
1014
- rooServer.url = serverConfig.httpUrl;
1015
- }
1016
- config.mcpServers[serverName] = rooServer;
1017
- }
1018
- return [
1019
- {
1020
- filepath,
1021
- content: `${JSON.stringify(config, null, 2)}
1022
- `
1023
- }
1024
- ];
1225
+ return generateMcpConfigurationFilesFromRegistry("roo", mcpServers, baseDir);
1025
1226
  }
1026
1227
  var init_roo = __esm({
1027
1228
  "src/generators/mcp/roo.ts"() {
1028
1229
  "use strict";
1029
- init_mcp_helpers();
1230
+ init_shared_factory();
1030
1231
  }
1031
1232
  });
1032
1233
 
@@ -1049,6 +1250,25 @@ var init_windsurf = __esm({
1049
1250
  }
1050
1251
  });
1051
1252
 
1253
+ // src/generators/mcp/opencode.ts
1254
+ var opencode_exports = {};
1255
+ __export(opencode_exports, {
1256
+ generateOpenCodeMcp: () => generateOpenCodeMcp,
1257
+ generateOpenCodeMcpConfiguration: () => generateOpenCodeMcpConfiguration
1258
+ });
1259
+ function generateOpenCodeMcp(config) {
1260
+ return generateMcpFromRegistry("opencode", config);
1261
+ }
1262
+ function generateOpenCodeMcpConfiguration(mcpServers, baseDir = "") {
1263
+ return generateMcpConfigurationFilesFromRegistry("opencode", mcpServers, baseDir);
1264
+ }
1265
+ var init_opencode = __esm({
1266
+ "src/generators/mcp/opencode.ts"() {
1267
+ "use strict";
1268
+ init_shared_factory();
1269
+ }
1270
+ });
1271
+
1052
1272
  // src/cli/index.ts
1053
1273
  var import_commander = require("commander");
1054
1274
 
@@ -1103,6 +1323,7 @@ var ConfigSchema = import_mini4.z.object({
1103
1323
  var import_mini5 = require("zod/mini");
1104
1324
  init_tool_targets();
1105
1325
  var OutputPathsSchema = import_mini5.z.object({
1326
+ amazonqcli: import_mini5.z.optional(import_mini5.z.string()),
1106
1327
  augmentcode: import_mini5.z.optional(import_mini5.z.string()),
1107
1328
  "augmentcode-legacy": import_mini5.z.optional(import_mini5.z.string()),
1108
1329
  copilot: import_mini5.z.optional(import_mini5.z.string()),
@@ -1110,6 +1331,7 @@ var OutputPathsSchema = import_mini5.z.object({
1110
1331
  cline: import_mini5.z.optional(import_mini5.z.string()),
1111
1332
  claudecode: import_mini5.z.optional(import_mini5.z.string()),
1112
1333
  codexcli: import_mini5.z.optional(import_mini5.z.string()),
1334
+ opencode: import_mini5.z.optional(import_mini5.z.string()),
1113
1335
  roo: import_mini5.z.optional(import_mini5.z.string()),
1114
1336
  geminicli: import_mini5.z.optional(import_mini5.z.string()),
1115
1337
  kiro: import_mini5.z.optional(import_mini5.z.string()),
@@ -1161,7 +1383,7 @@ var import_mini6 = require("zod/mini");
1161
1383
  init_tool_targets();
1162
1384
  var McpTransportTypeSchema = import_mini6.z.enum(["stdio", "sse", "http"]);
1163
1385
  var McpServerBaseSchema = import_mini6.z.object({
1164
- command: import_mini6.z.optional(import_mini6.z.string()),
1386
+ command: import_mini6.z.optional(import_mini6.z.union([import_mini6.z.string(), import_mini6.z.array(import_mini6.z.string())])),
1165
1387
  args: import_mini6.z.optional(import_mini6.z.array(import_mini6.z.string())),
1166
1388
  url: import_mini6.z.optional(import_mini6.z.string()),
1167
1389
  httpUrl: import_mini6.z.optional(import_mini6.z.string()),
@@ -1217,6 +1439,7 @@ function getDefaultConfig() {
1217
1439
  return {
1218
1440
  aiRulesDir: ".rulesync",
1219
1441
  outputPaths: {
1442
+ amazonqcli: ".amazonq/rules",
1220
1443
  augmentcode: ".",
1221
1444
  "augmentcode-legacy": ".",
1222
1445
  copilot: ".github/instructions",
@@ -1224,6 +1447,7 @@ function getDefaultConfig() {
1224
1447
  cline: ".clinerules",
1225
1448
  claudecode: ".",
1226
1449
  codexcli: ".",
1450
+ opencode: ".",
1227
1451
  roo: ".roo/rules",
1228
1452
  geminicli: ".gemini/memories",
1229
1453
  kiro: ".kiro/steering",
@@ -1430,51 +1654,8 @@ function mergeWithCliOptions(config, cliOptions) {
1430
1654
  return merged;
1431
1655
  }
1432
1656
 
1433
- // src/utils/logger.ts
1434
- var import_consola = require("consola");
1435
- var Logger = class {
1436
- _verbose = false;
1437
- console = import_consola.consola.withDefaults({
1438
- tag: "rulesync"
1439
- });
1440
- setVerbose(verbose) {
1441
- this._verbose = verbose;
1442
- }
1443
- get verbose() {
1444
- return this._verbose;
1445
- }
1446
- // Regular log (always shown, regardless of verbose)
1447
- log(message, ...args) {
1448
- this.console.log(message, ...args);
1449
- }
1450
- // Info level (shown only in verbose mode)
1451
- info(message, ...args) {
1452
- if (this._verbose) {
1453
- this.console.info(message, ...args);
1454
- }
1455
- }
1456
- // Success (always shown)
1457
- success(message, ...args) {
1458
- this.console.success(message, ...args);
1459
- }
1460
- // Warning (always shown)
1461
- warn(message, ...args) {
1462
- this.console.warn(message, ...args);
1463
- }
1464
- // Error (always shown)
1465
- error(message, ...args) {
1466
- this.console.error(message, ...args);
1467
- }
1468
- // Debug level (shown only in verbose mode)
1469
- debug(message, ...args) {
1470
- if (this._verbose) {
1471
- this.console.debug(message, ...args);
1472
- }
1473
- }
1474
- };
1475
- var logger = new Logger();
1476
-
1477
1657
  // src/cli/commands/add.ts
1658
+ init_logger();
1478
1659
  function sanitizeFilename(filename) {
1479
1660
  return filename.endsWith(".md") ? filename.slice(0, -3) : filename;
1480
1661
  }
@@ -1518,6 +1699,7 @@ var import_node_fs = require("fs");
1518
1699
  var import_node_path2 = __toESM(require("path"), 1);
1519
1700
 
1520
1701
  // src/utils/error.ts
1702
+ init_logger();
1521
1703
  function getErrorMessage(error) {
1522
1704
  return error instanceof Error ? error.message : String(error);
1523
1705
  }
@@ -1547,90 +1729,11 @@ async function safeAsyncOperation(operation, errorContext) {
1547
1729
  }
1548
1730
  }
1549
1731
 
1550
- // src/utils/file.ts
1551
- var import_promises2 = require("fs/promises");
1552
- var import_node_path = require("path");
1553
- async function ensureDir(dirPath) {
1554
- try {
1555
- await (0, import_promises2.stat)(dirPath);
1556
- } catch {
1557
- await (0, import_promises2.mkdir)(dirPath, { recursive: true });
1558
- }
1559
- }
1560
- function resolvePath(relativePath, baseDir) {
1561
- return baseDir ? (0, import_node_path.join)(baseDir, relativePath) : relativePath;
1562
- }
1563
- async function readFileContent(filepath) {
1564
- return (0, import_promises2.readFile)(filepath, "utf-8");
1565
- }
1566
- async function writeFileContent(filepath, content) {
1567
- await ensureDir((0, import_node_path.dirname)(filepath));
1568
- await (0, import_promises2.writeFile)(filepath, content, "utf-8");
1569
- }
1570
- async function fileExists(filepath) {
1571
- try {
1572
- await (0, import_promises2.stat)(filepath);
1573
- return true;
1574
- } catch {
1575
- return false;
1576
- }
1577
- }
1578
- async function findFiles(dir, extension = ".md") {
1579
- try {
1580
- const files = await (0, import_promises2.readdir)(dir);
1581
- return files.filter((file) => file.endsWith(extension)).map((file) => (0, import_node_path.join)(dir, file));
1582
- } catch {
1583
- return [];
1584
- }
1585
- }
1586
- async function findRuleFiles(aiRulesDir) {
1587
- const rulesDir = (0, import_node_path.join)(aiRulesDir, "rules");
1588
- const newLocationFiles = await findFiles(rulesDir, ".md");
1589
- const legacyLocationFiles = await findFiles(aiRulesDir, ".md");
1590
- const newLocationBasenames = new Set(
1591
- newLocationFiles.map((file) => file.split("/").pop()?.replace(/\.md$/, ""))
1592
- );
1593
- const filteredLegacyFiles = legacyLocationFiles.filter((file) => {
1594
- const basename6 = file.split("/").pop()?.replace(/\.md$/, "");
1595
- return !newLocationBasenames.has(basename6);
1596
- });
1597
- return [...newLocationFiles, ...filteredLegacyFiles];
1598
- }
1599
- async function removeDirectory(dirPath) {
1600
- const dangerousPaths = [".", "/", "~", "src", "node_modules"];
1601
- if (dangerousPaths.includes(dirPath) || dirPath === "") {
1602
- logger.warn(`Skipping deletion of dangerous path: ${dirPath}`);
1603
- return;
1604
- }
1605
- try {
1606
- if (await fileExists(dirPath)) {
1607
- await (0, import_promises2.rm)(dirPath, { recursive: true, force: true });
1608
- }
1609
- } catch (error) {
1610
- logger.warn(`Failed to remove directory ${dirPath}:`, error);
1611
- }
1612
- }
1613
- async function removeFile(filepath) {
1614
- try {
1615
- if (await fileExists(filepath)) {
1616
- await (0, import_promises2.rm)(filepath);
1617
- }
1618
- } catch (error) {
1619
- logger.warn(`Failed to remove file ${filepath}:`, error);
1620
- }
1621
- }
1622
- async function removeClaudeGeneratedFiles() {
1623
- const filesToRemove = ["CLAUDE.md", ".claude/memories"];
1624
- for (const fileOrDir of filesToRemove) {
1625
- if (fileOrDir.endsWith("/memories")) {
1626
- await removeDirectory(fileOrDir);
1627
- } else {
1628
- await removeFile(fileOrDir);
1629
- }
1630
- }
1631
- }
1732
+ // src/utils/index.ts
1733
+ init_file();
1632
1734
 
1633
1735
  // src/cli/commands/config.ts
1736
+ init_logger();
1634
1737
  async function configCommand(options = {}) {
1635
1738
  if (options.init) {
1636
1739
  await initConfig(options);
@@ -1848,18 +1951,18 @@ var import_node_path5 = require("path");
1848
1951
 
1849
1952
  // src/utils/command-generators.ts
1850
1953
  var import_node_path3 = require("path");
1851
- function generateYamlFrontmatter(command, options = {}) {
1852
- const frontmatter = ["---"];
1853
- if (options.includeDescription !== false && command.frontmatter.description) {
1854
- frontmatter.push(`description: ${command.frontmatter.description}`);
1954
+ function generateYamlFrontmatter(command, options) {
1955
+ const frontmatterLines = ["---"];
1956
+ if (options?.includeDescription && command.frontmatter.description) {
1957
+ frontmatterLines.push(`description: ${command.frontmatter.description}`);
1855
1958
  }
1856
- if (options.additionalFields) {
1959
+ if (options?.additionalFields) {
1857
1960
  for (const field of options.additionalFields) {
1858
- frontmatter.push(`${field.key}: ${field.value}`);
1961
+ frontmatterLines.push(`${field.key}: ${field.value}`);
1859
1962
  }
1860
1963
  }
1861
- frontmatter.push("---");
1862
- return frontmatter;
1964
+ frontmatterLines.push("---");
1965
+ return frontmatterLines;
1863
1966
  }
1864
1967
  function buildCommandContent(command, frontmatterOptions) {
1865
1968
  const frontmatter = generateYamlFrontmatter(command, frontmatterOptions);
@@ -1898,26 +2001,75 @@ var syntaxConverters = {
1898
2001
  }
1899
2002
  };
1900
2003
 
1901
- // src/generators/commands/claudecode.ts
1902
- var ClaudeCodeCommandGenerator = class {
2004
+ // src/generators/commands/base.ts
2005
+ var BaseCommandGenerator = class {
2006
+ /**
2007
+ * Generate command output for the specified tool
2008
+ */
1903
2009
  generate(command, outputDir) {
1904
2010
  const filepath = this.getOutputPath(command.filename, outputDir);
1905
- const content = buildCommandContent(command);
2011
+ const content = this.processContent(command);
1906
2012
  return {
1907
- tool: "claudecode",
2013
+ tool: this.getToolName(),
1908
2014
  filepath,
1909
2015
  content
1910
2016
  };
1911
2017
  }
2018
+ /**
2019
+ * Get the output path for the command file
2020
+ * Override this method if custom path logic is needed
2021
+ */
1912
2022
  getOutputPath(filename, baseDir) {
1913
- return getFlattenedCommandPath(filename, baseDir, ".claude/commands");
2023
+ if (this.supportsHierarchy()) {
2024
+ return getHierarchicalCommandPath(
2025
+ filename,
2026
+ baseDir,
2027
+ this.getCommandsDirectory(),
2028
+ this.getFileExtension()
2029
+ );
2030
+ } else {
2031
+ return getFlattenedCommandPath(filename, baseDir, this.getCommandsDirectory());
2032
+ }
2033
+ }
2034
+ /**
2035
+ * Whether this tool supports hierarchical directory structure
2036
+ * Override to return true for tools that support nested commands
2037
+ */
2038
+ supportsHierarchy() {
2039
+ return false;
2040
+ }
2041
+ /**
2042
+ * Get file extension for the target tool
2043
+ * Override if tool uses different extension than .md
2044
+ */
2045
+ getFileExtension() {
2046
+ return "md";
2047
+ }
2048
+ };
2049
+
2050
+ // src/generators/commands/claudecode.ts
2051
+ var ClaudeCodeCommandGenerator = class extends BaseCommandGenerator {
2052
+ getToolName() {
2053
+ return "claudecode";
2054
+ }
2055
+ getCommandsDirectory() {
2056
+ return ".claude/commands";
1914
2057
  }
2058
+ processContent(command) {
2059
+ return buildCommandContent(command, { includeDescription: true });
2060
+ }
2061
+ // Uses flattened structure by default (supportsHierarchy returns false)
1915
2062
  };
1916
2063
 
1917
2064
  // src/generators/commands/geminicli.ts
1918
- var GeminiCliCommandGenerator = class {
1919
- generate(command, outputDir) {
1920
- const filepath = this.getOutputPath(command.filename, outputDir);
2065
+ var GeminiCliCommandGenerator = class extends BaseCommandGenerator {
2066
+ getToolName() {
2067
+ return "geminicli";
2068
+ }
2069
+ getCommandsDirectory() {
2070
+ return ".gemini/commands";
2071
+ }
2072
+ processContent(command) {
1921
2073
  const convertedContent = syntaxConverters.toGeminiCli(command.content);
1922
2074
  const tomlLines = [];
1923
2075
  if (command.frontmatter.description) {
@@ -1925,32 +2077,28 @@ var GeminiCliCommandGenerator = class {
1925
2077
  tomlLines.push("");
1926
2078
  }
1927
2079
  tomlLines.push(`prompt = """${convertedContent}"""`);
1928
- const content = tomlLines.join("\n") + "\n";
1929
- return {
1930
- tool: "geminicli",
1931
- filepath,
1932
- content
1933
- };
2080
+ return tomlLines.join("\n") + "\n";
1934
2081
  }
1935
- getOutputPath(filename, baseDir) {
1936
- return getHierarchicalCommandPath(filename, baseDir, ".gemini/commands", "toml");
2082
+ supportsHierarchy() {
2083
+ return true;
2084
+ }
2085
+ getFileExtension() {
2086
+ return "toml";
1937
2087
  }
1938
2088
  };
1939
2089
 
1940
2090
  // src/generators/commands/roo.ts
1941
- var RooCommandGenerator = class {
1942
- generate(command, outputDir) {
1943
- const filepath = this.getOutputPath(command.filename, outputDir);
1944
- const content = buildCommandContent(command);
1945
- return {
1946
- tool: "roo",
1947
- filepath,
1948
- content
1949
- };
2091
+ var RooCommandGenerator = class extends BaseCommandGenerator {
2092
+ getToolName() {
2093
+ return "roo";
1950
2094
  }
1951
- getOutputPath(filename, baseDir) {
1952
- return getFlattenedCommandPath(filename, baseDir, ".roo/commands");
2095
+ getCommandsDirectory() {
2096
+ return ".roo/commands";
2097
+ }
2098
+ processContent(command) {
2099
+ return buildCommandContent(command, { includeDescription: true });
1953
2100
  }
2101
+ // Uses flattened structure by default (supportsHierarchy returns false)
1954
2102
  };
1955
2103
 
1956
2104
  // src/generators/commands/index.ts
@@ -1963,6 +2111,10 @@ function getCommandGenerator(tool) {
1963
2111
  return commandGenerators[tool];
1964
2112
  }
1965
2113
 
2114
+ // src/core/command-generator.ts
2115
+ init_file();
2116
+ init_logger();
2117
+
1966
2118
  // src/core/command-parser.ts
1967
2119
  var import_node_path4 = require("path");
1968
2120
 
@@ -1985,6 +2137,7 @@ function extractStringField(data, key, defaultValue) {
1985
2137
  }
1986
2138
 
1987
2139
  // src/core/command-parser.ts
2140
+ init_logger();
1988
2141
  async function parseCommandsFromDirectory(commandsDir) {
1989
2142
  const commandFiles = await findFiles(commandsDir, ".md");
1990
2143
  const commands = [];
@@ -2771,15 +2924,15 @@ function generateWindsurfIgnore(rules, config, baseDir) {
2771
2924
  return generateIgnoreFile(rules, config, ignoreConfigs.windsurf, baseDir);
2772
2925
  }
2773
2926
 
2774
- // src/generators/rules/augmentcode.ts
2775
- var import_node_path9 = require("path");
2776
-
2777
2927
  // src/generators/rules/shared-helpers.ts
2778
2928
  var import_node_path8 = require("path");
2929
+ init_file();
2779
2930
 
2780
2931
  // src/utils/ignore.ts
2781
2932
  var import_node_path7 = require("path");
2782
2933
  var import_micromatch = __toESM(require("micromatch"), 1);
2934
+ init_file();
2935
+ init_logger();
2783
2936
  var cachedIgnorePatterns = null;
2784
2937
  async function loadIgnorePatterns(baseDir = process.cwd()) {
2785
2938
  if (cachedIgnorePatterns) {
@@ -2930,7 +3083,61 @@ function generateIgnoreFile2(patterns, tool) {
2930
3083
  return lines.join("\n");
2931
3084
  }
2932
3085
 
3086
+ // src/generators/rules/amazonqcli.ts
3087
+ async function generateAmazonqcliConfig(rules, config, baseDir) {
3088
+ const generatorConfig = {
3089
+ tool: "amazonqcli",
3090
+ fileExtension: ".md",
3091
+ generateContent: generateRuleFile,
3092
+ generateRootContent: generateMainRulesFile,
3093
+ rootFilePath: ".amazonq/rules/main.md",
3094
+ generateDetailContent: generateRuleFile,
3095
+ detailSubDir: ".amazonq/rules"
3096
+ };
3097
+ return generateComplexRules(rules, config, generatorConfig, baseDir);
3098
+ }
3099
+ function generateMainRulesFile(rootRule, detailRules) {
3100
+ const lines = [];
3101
+ if (detailRules.length > 0) {
3102
+ lines.push("# Amazon Q Developer CLI Project Rules");
3103
+ lines.push("");
3104
+ lines.push("This file contains the main project rules. See also:");
3105
+ lines.push("");
3106
+ for (const rule of detailRules) {
3107
+ lines.push(`- ${rule.filename}.md: ${rule.frontmatter.description}`);
3108
+ }
3109
+ lines.push("");
3110
+ }
3111
+ if (rootRule) {
3112
+ if (detailRules.length > 0) {
3113
+ lines.push("## Overview");
3114
+ lines.push("");
3115
+ }
3116
+ lines.push(rootRule.content);
3117
+ lines.push("");
3118
+ } else if (detailRules.length === 0) {
3119
+ lines.push("# Amazon Q Developer CLI Project Rules");
3120
+ lines.push("");
3121
+ lines.push("This file contains project-specific rules and context for Amazon Q Developer CLI.");
3122
+ lines.push("");
3123
+ lines.push("## Development Standards");
3124
+ lines.push("");
3125
+ lines.push("Add your project-specific development standards here.");
3126
+ lines.push("");
3127
+ }
3128
+ return lines.join("\n").trim() + "\n";
3129
+ }
3130
+ function generateRuleFile(rule) {
3131
+ const lines = [];
3132
+ lines.push(`# ${rule.frontmatter.description || rule.filename}`);
3133
+ lines.push("");
3134
+ lines.push(rule.content.trim());
3135
+ lines.push("");
3136
+ return lines.join("\n");
3137
+ }
3138
+
2933
3139
  // src/generators/rules/augmentcode.ts
3140
+ var import_node_path9 = require("path");
2934
3141
  async function generateAugmentcodeConfig(rules, config, baseDir) {
2935
3142
  const outputs = createOutputsArray();
2936
3143
  rules.forEach((rule) => {
@@ -2940,12 +3147,12 @@ async function generateAugmentcodeConfig(rules, config, baseDir) {
2940
3147
  config,
2941
3148
  baseDir,
2942
3149
  (0, import_node_path9.join)(".augment", "rules", `${rule.filename}.md`),
2943
- generateRuleFile(rule)
3150
+ generateRuleFile2(rule)
2944
3151
  );
2945
3152
  });
2946
3153
  return outputs;
2947
3154
  }
2948
- function generateRuleFile(rule) {
3155
+ function generateRuleFile2(rule) {
2949
3156
  const lines = [];
2950
3157
  lines.push("---");
2951
3158
  let ruleType = "manual";
@@ -2993,6 +3200,8 @@ function generateLegacyGuidelinesFile(allRules) {
2993
3200
 
2994
3201
  // src/generators/rules/claudecode.ts
2995
3202
  var import_node_path10 = require("path");
3203
+ init_file();
3204
+ init_logger();
2996
3205
  async function generateClaudecodeConfig(rules, config, baseDir) {
2997
3206
  const generatorConfig = {
2998
3207
  tool: "claudecode",
@@ -3092,6 +3301,22 @@ function determineCursorRuleType(frontmatter) {
3092
3301
  }
3093
3302
  var GENERATOR_REGISTRY = {
3094
3303
  // Simple generators - generate one file per rule
3304
+ amazonqcli: {
3305
+ type: "complex",
3306
+ tool: "amazonqcli",
3307
+ fileExtension: ".md",
3308
+ // ignoreFileName omitted - Amazon Q CLI doesn't have native ignore file support yet
3309
+ generateContent: (rule) => {
3310
+ const lines = [];
3311
+ if (rule.frontmatter.description) {
3312
+ lines.push(`# ${rule.frontmatter.description}
3313
+ `);
3314
+ }
3315
+ lines.push(rule.content.trim());
3316
+ return lines.join("\n");
3317
+ }
3318
+ // Complex generation handled by existing generator
3319
+ },
3095
3320
  cline: {
3096
3321
  type: "simple",
3097
3322
  tool: "cline",
@@ -3258,6 +3483,22 @@ var GENERATOR_REGISTRY = {
3258
3483
  const lines = [];
3259
3484
  if (rule.frontmatter.description) {
3260
3485
  lines.push(`# ${rule.frontmatter.description}
3486
+ `);
3487
+ }
3488
+ lines.push(rule.content.trim());
3489
+ return lines.join("\n");
3490
+ }
3491
+ // Complex generation handled by existing generator
3492
+ },
3493
+ opencode: {
3494
+ type: "complex",
3495
+ tool: "opencode",
3496
+ fileExtension: ".md",
3497
+ // ignoreFileName omitted - OpenCode doesn't use dedicated ignore files
3498
+ generateContent: (rule) => {
3499
+ const lines = [];
3500
+ if (rule.frontmatter.description) {
3501
+ lines.push(`# ${rule.frontmatter.description}
3261
3502
  `);
3262
3503
  }
3263
3504
  lines.push(rule.content.trim());
@@ -3300,8 +3541,8 @@ async function generateFromRegistry(tool, rules, config, baseDir) {
3300
3541
  const enhancedConfig = {
3301
3542
  tool: generatorConfig.tool,
3302
3543
  fileExtension: generatorConfig.fileExtension,
3303
- ignoreFileName: generatorConfig.ignoreFileName,
3304
3544
  generateContent: generatorConfig.generateContent,
3545
+ ...generatorConfig.ignoreFileName && { ignoreFileName: generatorConfig.ignoreFileName },
3305
3546
  ...generatorConfig.generateRootContent && {
3306
3547
  generateRootContent: generatorConfig.generateRootContent
3307
3548
  },
@@ -3332,48 +3573,91 @@ var generateKiroConfig = createSimpleGenerator("kiro");
3332
3573
  var generateRooConfig = createSimpleGenerator("roo");
3333
3574
 
3334
3575
  // src/generators/rules/codexcli.ts
3335
- async function generateCodexConfig(rules, config, baseDir) {
3336
- const outputs = [];
3337
- if (rules.length === 0) {
3338
- return outputs;
3339
- }
3340
- const sortedRules = [...rules].sort((a, b) => {
3341
- if (a.frontmatter.root === true && b.frontmatter.root !== true) return -1;
3342
- if (a.frontmatter.root !== true && b.frontmatter.root === true) return 1;
3343
- return 0;
3344
- });
3345
- const concatenatedContent = generateConcatenatedCodexContent(sortedRules);
3346
- if (concatenatedContent.trim()) {
3347
- const outputDir = resolveOutputDir(config, "codexcli", baseDir);
3348
- const filepath = `${outputDir}/AGENTS.md`;
3349
- outputs.push({
3350
- tool: "codexcli",
3351
- filepath,
3352
- content: concatenatedContent
3576
+ init_file();
3577
+
3578
+ // src/utils/xml-document-generator.ts
3579
+ var import_fast_xml_parser = require("fast-xml-parser");
3580
+ function generateRootMarkdownWithXmlDocs(rootRule, memoryRules, config) {
3581
+ const lines = [];
3582
+ if (memoryRules.length > 0) {
3583
+ lines.push(
3584
+ "Please also reference the following documents as needed. In this case, `@` stands for the project root directory."
3585
+ );
3586
+ lines.push("");
3587
+ const documentsData = {
3588
+ Documents: {
3589
+ Document: memoryRules.map((rule) => {
3590
+ const relativePath = `@${config.memorySubDir}/${rule.filename}.md`;
3591
+ const document = {
3592
+ Path: relativePath,
3593
+ Description: rule.frontmatter.description
3594
+ };
3595
+ if (rule.frontmatter.globs.length > 0) {
3596
+ document.FilePatterns = rule.frontmatter.globs.join(", ");
3597
+ }
3598
+ return document;
3599
+ })
3600
+ }
3601
+ };
3602
+ const builder = new import_fast_xml_parser.XMLBuilder({
3603
+ format: true,
3604
+ ignoreAttributes: false,
3605
+ suppressEmptyNode: false
3353
3606
  });
3607
+ const xmlContent = builder.build(documentsData);
3608
+ lines.push(xmlContent);
3609
+ lines.push("");
3610
+ lines.push("");
3354
3611
  }
3355
- const ignorePatterns = await loadIgnorePatterns(baseDir);
3356
- if (ignorePatterns.patterns.length > 0) {
3357
- const ignorePath = resolvePath(".codexignore", baseDir);
3358
- const ignoreContent = generateIgnoreFile2(ignorePatterns.patterns, "codexcli");
3359
- outputs.push({
3360
- tool: "codexcli",
3361
- filepath: ignorePath,
3362
- content: ignoreContent
3363
- });
3612
+ if (rootRule) {
3613
+ lines.push(rootRule.content.trim());
3614
+ } else if (memoryRules.length === 0) {
3615
+ lines.push(`# ${config.fallbackTitle}`);
3616
+ lines.push("");
3617
+ lines.push("No configuration rules have been defined yet.");
3364
3618
  }
3365
- return outputs;
3619
+ return lines.join("\n");
3366
3620
  }
3367
- function generateConcatenatedCodexContent(rules) {
3368
- const sections = [];
3369
- for (const rule of rules) {
3370
- const content = rule.content.trim();
3371
- if (!content) {
3372
- continue;
3621
+
3622
+ // src/generators/rules/codexcli.ts
3623
+ async function generateCodexConfig(rules, config, baseDir) {
3624
+ const outputs = [];
3625
+ const nonEmptyRules = rules.filter((rule) => rule.content.trim().length > 0);
3626
+ if (nonEmptyRules.length > 0) {
3627
+ const generatorConfig = {
3628
+ tool: "codexcli",
3629
+ fileExtension: ".md",
3630
+ ignoreFileName: ".codexignore",
3631
+ generateContent: generateCodexMemoryMarkdown,
3632
+ generateDetailContent: generateCodexMemoryMarkdown,
3633
+ generateRootContent: generateCodexRootMarkdown,
3634
+ rootFilePath: "AGENTS.md",
3635
+ detailSubDir: ".codex/memories"
3636
+ };
3637
+ const ruleOutputs = await generateComplexRules(nonEmptyRules, config, generatorConfig, baseDir);
3638
+ outputs.push(...ruleOutputs);
3639
+ } else {
3640
+ const ignorePatterns = await loadIgnorePatterns(baseDir);
3641
+ if (ignorePatterns.patterns.length > 0) {
3642
+ const ignorePath = resolvePath(".codexignore", baseDir);
3643
+ const ignoreContent = generateIgnoreFile2(ignorePatterns.patterns, "codexcli");
3644
+ outputs.push({
3645
+ tool: "codexcli",
3646
+ filepath: ignorePath,
3647
+ content: ignoreContent
3648
+ });
3373
3649
  }
3374
- sections.push(content);
3375
3650
  }
3376
- return sections.join("\n\n---\n\n");
3651
+ return outputs;
3652
+ }
3653
+ function generateCodexMemoryMarkdown(rule) {
3654
+ return rule.content.trim();
3655
+ }
3656
+ function generateCodexRootMarkdown(rootRule, memoryRules, _baseDir) {
3657
+ return generateRootMarkdownWithXmlDocs(rootRule, memoryRules, {
3658
+ memorySubDir: ".codex/memories",
3659
+ fallbackTitle: "OpenAI Codex CLI Configuration"
3660
+ });
3377
3661
  }
3378
3662
 
3379
3663
  // src/generators/rules/geminicli.ts
@@ -3394,28 +3678,10 @@ function generateGeminiMemoryMarkdown(rule) {
3394
3678
  return rule.content.trim();
3395
3679
  }
3396
3680
  function generateGeminiRootMarkdown(rootRule, memoryRules, _baseDir) {
3397
- const lines = [];
3398
- if (memoryRules.length > 0) {
3399
- lines.push("Please also reference the following documents as needed:");
3400
- lines.push("");
3401
- lines.push("| Document | Description | File Patterns |");
3402
- lines.push("|----------|-------------|---------------|");
3403
- for (const rule of memoryRules) {
3404
- const relativePath = `@.gemini/memories/${rule.filename}.md`;
3405
- const filePatterns = rule.frontmatter.globs.length > 0 ? rule.frontmatter.globs.join(", ") : "-";
3406
- lines.push(`| ${relativePath} | ${rule.frontmatter.description} | ${filePatterns} |`);
3407
- }
3408
- lines.push("");
3409
- lines.push("");
3410
- }
3411
- if (rootRule) {
3412
- lines.push(rootRule.content.trim());
3413
- } else if (memoryRules.length === 0) {
3414
- lines.push("# Gemini CLI Configuration");
3415
- lines.push("");
3416
- lines.push("No configuration rules have been defined yet.");
3417
- }
3418
- return lines.join("\n");
3681
+ return generateRootMarkdownWithXmlDocs(rootRule, memoryRules, {
3682
+ memorySubDir: ".gemini/memories",
3683
+ fallbackTitle: "Gemini CLI Configuration"
3684
+ });
3419
3685
  }
3420
3686
 
3421
3687
  // src/generators/rules/junie.ts
@@ -3445,8 +3711,33 @@ function generateGuidelinesMarkdown(rootRule, detailRules) {
3445
3711
  return lines.join("\n").trim();
3446
3712
  }
3447
3713
 
3448
- // src/core/generator.ts
3449
- async function generateConfigurations(rules, config, targetTools, baseDir) {
3714
+ // src/generators/rules/opencode.ts
3715
+ async function generateOpenCodeConfig(rules, config, baseDir) {
3716
+ const generatorConfig = {
3717
+ tool: "opencode",
3718
+ fileExtension: ".md",
3719
+ // ignoreFileName omitted - OpenCode doesn't use dedicated ignore files
3720
+ generateContent: generateOpenCodeMarkdown,
3721
+ generateDetailContent: generateOpenCodeMarkdown,
3722
+ generateRootContent: generateOpenCodeRootMarkdown,
3723
+ rootFilePath: "AGENTS.md",
3724
+ detailSubDir: ".opencode/memories"
3725
+ };
3726
+ return generateComplexRules(rules, config, generatorConfig, baseDir);
3727
+ }
3728
+ function generateOpenCodeMarkdown(rule) {
3729
+ return rule.content.trim();
3730
+ }
3731
+ function generateOpenCodeRootMarkdown(rootRule, memoryRules, _baseDir) {
3732
+ return generateRootMarkdownWithXmlDocs(rootRule, memoryRules, {
3733
+ memorySubDir: ".opencode/memories",
3734
+ fallbackTitle: "OpenCode Configuration"
3735
+ });
3736
+ }
3737
+
3738
+ // src/core/generator.ts
3739
+ init_logger();
3740
+ async function generateConfigurations(rules, config, targetTools, baseDir) {
3450
3741
  const outputs = createOutputsArray();
3451
3742
  const toolsToGenerate = targetTools || config.defaultTargets;
3452
3743
  const rootFiles = rules.filter((rule) => rule.frontmatter.root === true);
@@ -3477,6 +3768,8 @@ function filterRulesForTool(rules, tool, config) {
3477
3768
  }
3478
3769
  async function generateForTool(tool, rules, config, baseDir) {
3479
3770
  switch (tool) {
3771
+ case "amazonqcli":
3772
+ return await generateAmazonqcliConfig(rules, config, baseDir);
3480
3773
  case "augmentcode": {
3481
3774
  const augmentRulesOutputs = await generateAugmentcodeConfig(rules, config, baseDir);
3482
3775
  const augmentIgnoreOutputs = await generateAugmentCodeIgnoreFiles(rules, config, baseDir);
@@ -3515,6 +3808,8 @@ async function generateForTool(tool, rules, config, baseDir) {
3515
3808
  const kiroIgnoreOutputs = await generateKiroIgnoreFiles(rules, config, baseDir);
3516
3809
  return [...kiroRulesOutputs, ...kiroIgnoreOutputs];
3517
3810
  }
3811
+ case "opencode":
3812
+ return generateOpenCodeConfig(rules, config, baseDir);
3518
3813
  case "windsurf": {
3519
3814
  const windsurfRulesOutputs = await generateWindsurfConfig(rules, config, baseDir);
3520
3815
  const windsurfIgnoreOutputs = await generateWindsurfIgnore(rules, config, baseDir);
@@ -3528,6 +3823,7 @@ async function generateForTool(tool, rules, config, baseDir) {
3528
3823
 
3529
3824
  // src/core/parser.ts
3530
3825
  var import_node_path12 = require("path");
3826
+ init_logger();
3531
3827
  async function parseRulesFromDirectory(aiRulesDir) {
3532
3828
  const ignorePatterns = await loadIgnorePatterns();
3533
3829
  const allRuleFiles = await findRuleFiles(aiRulesDir);
@@ -3648,6 +3944,7 @@ async function validateRule(rule) {
3648
3944
  var path4 = __toESM(require("path"), 1);
3649
3945
 
3650
3946
  // src/generators/mcp/index.ts
3947
+ init_amazonqcli();
3651
3948
  init_augmentcode();
3652
3949
  init_claudecode();
3653
3950
  init_cline();
@@ -3660,6 +3957,9 @@ init_kiro();
3660
3957
  init_roo();
3661
3958
  init_windsurf();
3662
3959
 
3960
+ // src/core/mcp-generator.ts
3961
+ init_file();
3962
+
3663
3963
  // src/core/mcp-parser.ts
3664
3964
  var fs = __toESM(require("fs"), 1);
3665
3965
  var path3 = __toESM(require("path"), 1);
@@ -3691,6 +3991,35 @@ function parseMcpConfig(projectRoot) {
3691
3991
  async function generateMcpConfigurations(mcpConfig, baseDir, targetTools) {
3692
3992
  const outputs = [];
3693
3993
  const toolMap = {
3994
+ amazonqcli: async (servers, dir) => {
3995
+ const config = {
3996
+ aiRulesDir: ".rulesync",
3997
+ outputPaths: {
3998
+ amazonqcli: ".amazonq/rules",
3999
+ augmentcode: ".",
4000
+ "augmentcode-legacy": ".",
4001
+ copilot: ".github/instructions",
4002
+ cursor: ".cursor/rules",
4003
+ cline: ".clinerules",
4004
+ claudecode: ".",
4005
+ codexcli: ".",
4006
+ opencode: ".",
4007
+ roo: ".roo/rules",
4008
+ geminicli: ".gemini/memories",
4009
+ kiro: ".kiro/steering",
4010
+ junie: ".",
4011
+ windsurf: "."
4012
+ },
4013
+ watchEnabled: false,
4014
+ defaultTargets: []
4015
+ };
4016
+ const results = await (await Promise.resolve().then(() => (init_amazonqcli(), amazonqcli_exports))).generateAmazonqcliMcp(
4017
+ servers,
4018
+ config,
4019
+ dir
4020
+ );
4021
+ return results.map((result) => ({ filepath: result.filepath, content: result.content }));
4022
+ },
3694
4023
  augmentcode: async (servers, dir) => (await Promise.resolve().then(() => (init_augmentcode(), augmentcode_exports))).generateAugmentcodeMcpConfiguration(
3695
4024
  servers,
3696
4025
  dir
@@ -3707,6 +4036,10 @@ async function generateMcpConfigurations(mcpConfig, baseDir, targetTools) {
3707
4036
  cursor: async (servers, dir) => (await Promise.resolve().then(() => (init_cursor(), cursor_exports))).generateCursorMcpConfiguration(servers, dir),
3708
4037
  cline: async (servers, dir) => (await Promise.resolve().then(() => (init_cline(), cline_exports))).generateClineMcpConfiguration(servers, dir),
3709
4038
  codexcli: async (servers, dir) => (await Promise.resolve().then(() => (init_codexcli(), codexcli_exports))).generateCodexMcpConfiguration(servers, dir),
4039
+ opencode: async (servers, dir) => (await Promise.resolve().then(() => (init_opencode(), opencode_exports))).generateOpenCodeMcpConfiguration(
4040
+ servers,
4041
+ dir
4042
+ ),
3710
4043
  roo: async (servers, dir) => (await Promise.resolve().then(() => (init_roo(), roo_exports))).generateRooMcpConfiguration(servers, dir),
3711
4044
  geminicli: async (servers, dir) => (await Promise.resolve().then(() => (init_geminicli(), geminicli_exports))).generateGeminiCliMcpConfiguration(
3712
4045
  servers,
@@ -3736,6 +4069,7 @@ async function generateMcpConfigurations(mcpConfig, baseDir, targetTools) {
3736
4069
  }
3737
4070
 
3738
4071
  // src/cli/commands/generate.ts
4072
+ init_logger();
3739
4073
  async function generateCommand(options = {}) {
3740
4074
  const configLoaderOptions = {
3741
4075
  ...options.config !== void 0 && { configPath: options.config },
@@ -3851,6 +4185,9 @@ async function generateCommand(options = {}) {
3851
4185
  case "kiro":
3852
4186
  deleteTasks.push(removeDirectory(config.outputPaths.kiro));
3853
4187
  break;
4188
+ case "opencode":
4189
+ deleteTasks.push(removeDirectory(config.outputPaths.opencode));
4190
+ break;
3854
4191
  case "windsurf":
3855
4192
  deleteTasks.push(removeDirectory(config.outputPaths.windsurf));
3856
4193
  break;
@@ -3945,10 +4282,13 @@ Generating configurations for base directory: ${baseDir}`);
3945
4282
  // src/cli/commands/gitignore.ts
3946
4283
  var import_node_fs2 = require("fs");
3947
4284
  var import_node_path14 = require("path");
4285
+ init_logger();
3948
4286
  var gitignoreCommand = async () => {
3949
4287
  const gitignorePath = (0, import_node_path14.join)(process.cwd(), ".gitignore");
3950
4288
  const rulesFilesToIgnore = [
3951
4289
  "# Generated by rulesync - AI tool configuration files",
4290
+ "**/.amazonq/rules/",
4291
+ "**/.amazonq/mcp.json",
3952
4292
  "**/.github/copilot-instructions.md",
3953
4293
  "**/.github/instructions/",
3954
4294
  "**/.cursor/rules/",
@@ -3974,6 +4314,9 @@ var gitignoreCommand = async () => {
3974
4314
  "**/.augment-guidelines",
3975
4315
  "**/.junie/guidelines.md",
3976
4316
  "**/.noai",
4317
+ "**/.opencode/memories/",
4318
+ "**/.opencode/commands/",
4319
+ "**/opencode.json",
3977
4320
  "**/.mcp.json",
3978
4321
  "!.rulesync/.mcp.json",
3979
4322
  "**/.cursor/mcp.json",
@@ -4015,159 +4358,9 @@ ${linesToAdd.join("\n")}
4015
4358
  var import_node_path21 = require("path");
4016
4359
  var import_gray_matter2 = __toESM(require("gray-matter"), 1);
4017
4360
 
4018
- // src/parsers/augmentcode.ts
4019
- var import_node_path15 = require("path");
4020
-
4021
- // src/utils/parser-helpers.ts
4022
- function createParseResult() {
4023
- return { rules: [], errors: [] };
4024
- }
4025
- function addError(result, error) {
4026
- result.errors.push(error);
4027
- }
4028
- function addRule(result, rule) {
4029
- if (!result.rules) {
4030
- result.rules = [];
4031
- }
4032
- result.rules.push(rule);
4033
- }
4034
- function addRules(result, rules) {
4035
- if (!result.rules) {
4036
- result.rules = [];
4037
- }
4038
- result.rules.push(...rules);
4039
- }
4040
- async function safeReadFile(operation, errorContext) {
4041
- try {
4042
- const result = await operation();
4043
- return createSuccessResult(result);
4044
- } catch (error) {
4045
- return createErrorResult(error, errorContext);
4046
- }
4047
- }
4048
-
4049
- // src/parsers/augmentcode.ts
4050
- async function parseAugmentcodeConfiguration(baseDir = process.cwd()) {
4051
- return parseUnifiedAugmentcode(baseDir, {
4052
- rulesDir: ".augment/rules",
4053
- targetName: "augmentcode",
4054
- filenamePrefix: "augmentcode"
4055
- });
4056
- }
4057
- async function parseAugmentcodeLegacyConfiguration(baseDir = process.cwd()) {
4058
- return parseUnifiedAugmentcode(baseDir, {
4059
- legacyFilePath: ".augment-guidelines",
4060
- targetName: "augmentcode-legacy",
4061
- filenamePrefix: "augmentcode-legacy"
4062
- });
4063
- }
4064
- async function parseUnifiedAugmentcode(baseDir, config) {
4065
- const result = createParseResult();
4066
- if (config.rulesDir) {
4067
- const rulesDir = (0, import_node_path15.join)(baseDir, config.rulesDir);
4068
- if (await fileExists(rulesDir)) {
4069
- const rulesResult = await parseAugmentRules(rulesDir, config);
4070
- addRules(result, rulesResult.rules);
4071
- result.errors.push(...rulesResult.errors);
4072
- } else {
4073
- addError(
4074
- result,
4075
- `No AugmentCode configuration found. Expected ${config.rulesDir} directory.`
4076
- );
4077
- }
4078
- }
4079
- if (config.legacyFilePath) {
4080
- const legacyPath = (0, import_node_path15.join)(baseDir, config.legacyFilePath);
4081
- if (await fileExists(legacyPath)) {
4082
- const legacyResult = await parseAugmentGuidelines(legacyPath, config);
4083
- if (legacyResult.rule) {
4084
- addRule(result, legacyResult.rule);
4085
- }
4086
- result.errors.push(...legacyResult.errors);
4087
- } else {
4088
- addError(
4089
- result,
4090
- `No AugmentCode legacy configuration found. Expected ${config.legacyFilePath} file.`
4091
- );
4092
- }
4093
- }
4094
- return { rules: result.rules || [], errors: result.errors };
4095
- }
4096
- async function parseAugmentRules(rulesDir, config) {
4097
- const rules = [];
4098
- const errors = [];
4099
- try {
4100
- const { readdir: readdir2 } = await import("fs/promises");
4101
- const files = await readdir2(rulesDir);
4102
- for (const file of files) {
4103
- if (file.endsWith(".md") || file.endsWith(".mdc")) {
4104
- const filePath = (0, import_node_path15.join)(rulesDir, file);
4105
- try {
4106
- const rawContent = await readFileContent(filePath);
4107
- const parsed = parseFrontmatter(rawContent);
4108
- const ruleType = extractStringField(parsed.data, "type", "manual");
4109
- const description = extractStringField(parsed.data, "description", "");
4110
- const tags = extractArrayField(parsed.data, "tags");
4111
- const isRoot = ruleType === "always";
4112
- const filename = (0, import_node_path15.basename)(file, file.endsWith(".mdc") ? ".mdc" : ".md");
4113
- const frontmatter = {
4114
- root: isRoot,
4115
- targets: [config.targetName],
4116
- description,
4117
- globs: ["**/*"],
4118
- // AugmentCode doesn't use specific globs in the same way
4119
- ...tags.length > 0 && { tags }
4120
- };
4121
- rules.push({
4122
- frontmatter,
4123
- content: parsed.content.trim(),
4124
- filename: `${config.filenamePrefix}-${ruleType}-${filename}`,
4125
- filepath: filePath
4126
- });
4127
- } catch (error) {
4128
- const errorMessage = error instanceof Error ? error.message : String(error);
4129
- errors.push(`Failed to parse ${filePath}: ${errorMessage}`);
4130
- }
4131
- }
4132
- }
4133
- } catch (error) {
4134
- const errorMessage = error instanceof Error ? error.message : String(error);
4135
- errors.push(`Failed to read ${config.rulesDir || rulesDir} directory: ${errorMessage}`);
4136
- }
4137
- return { rules, errors };
4138
- }
4139
- async function parseAugmentGuidelines(guidelinesPath, config) {
4140
- const parseResult = await safeReadFile(
4141
- async () => {
4142
- const content = await readFileContent(guidelinesPath);
4143
- if (content.trim()) {
4144
- const frontmatter = {
4145
- root: true,
4146
- // Legacy guidelines become root rules
4147
- targets: [config.targetName],
4148
- description: "Legacy AugmentCode guidelines",
4149
- globs: ["**/*"]
4150
- };
4151
- return {
4152
- frontmatter,
4153
- content: content.trim(),
4154
- filename: `${config.filenamePrefix}-guidelines`,
4155
- filepath: guidelinesPath
4156
- };
4157
- }
4158
- return null;
4159
- },
4160
- `Failed to parse ${config.legacyFilePath || guidelinesPath}`
4161
- );
4162
- if (parseResult.success) {
4163
- return { rule: parseResult.result || null, errors: [] };
4164
- } else {
4165
- return { rule: null, errors: [parseResult.error || "Unknown error"] };
4166
- }
4167
- }
4168
-
4169
4361
  // src/parsers/shared-helpers.ts
4170
- var import_node_path16 = require("path");
4362
+ var import_node_path15 = require("path");
4363
+ init_file();
4171
4364
  async function parseConfigurationFiles(baseDir = process.cwd(), config) {
4172
4365
  const errors = [];
4173
4366
  const rules = [];
@@ -4224,7 +4417,7 @@ async function parseConfigurationFiles(baseDir = process.cwd(), config) {
4224
4417
  const files = await readdir2(dirPath);
4225
4418
  for (const file of files) {
4226
4419
  if (file.endsWith(dirConfig.filePattern)) {
4227
- const filePath = (0, import_node_path16.join)(dirPath, file);
4420
+ const filePath = (0, import_node_path15.join)(dirPath, file);
4228
4421
  const fileResult = await safeAsyncOperation(async () => {
4229
4422
  const rawContent = await readFileContent(filePath);
4230
4423
  let content;
@@ -4375,10 +4568,10 @@ async function parseMemoryFiles(memoryDir, config) {
4375
4568
  const files = await readdir2(memoryDir);
4376
4569
  for (const file of files) {
4377
4570
  if (file.endsWith(".md")) {
4378
- const filePath = (0, import_node_path16.join)(memoryDir, file);
4571
+ const filePath = (0, import_node_path15.join)(memoryDir, file);
4379
4572
  const content = await readFileContent(filePath);
4380
4573
  if (content.trim()) {
4381
- const filename = (0, import_node_path16.basename)(file, ".md");
4574
+ const filename = (0, import_node_path15.basename)(file, ".md");
4382
4575
  const frontmatter = {
4383
4576
  root: false,
4384
4577
  targets: [config.tool],
@@ -4405,10 +4598,10 @@ async function parseCommandsFiles(commandsDir, config) {
4405
4598
  const files = await readdir2(commandsDir);
4406
4599
  for (const file of files) {
4407
4600
  if (file.endsWith(".md")) {
4408
- const filePath = (0, import_node_path16.join)(commandsDir, file);
4601
+ const filePath = (0, import_node_path15.join)(commandsDir, file);
4409
4602
  const content = await readFileContent(filePath);
4410
4603
  if (content.trim()) {
4411
- const filename = (0, import_node_path16.basename)(file, ".md");
4604
+ const filename = (0, import_node_path15.basename)(file, ".md");
4412
4605
  let frontmatter;
4413
4606
  let ruleContent;
4414
4607
  try {
@@ -4483,6 +4676,170 @@ async function parseSettingsFile(settingsPath, tool) {
4483
4676
  };
4484
4677
  }
4485
4678
 
4679
+ // src/parsers/amazonqcli.ts
4680
+ async function parseAmazonqcliConfiguration(baseDir = process.cwd()) {
4681
+ return parseMemoryBasedConfiguration(baseDir, {
4682
+ tool: "amazonqcli",
4683
+ mainFileName: ".amazonq/rules/main.md",
4684
+ memoryDirPath: ".amazonq/rules",
4685
+ settingsPath: ".amazonq/mcp.json",
4686
+ mainDescription: "Main Amazon Q Developer CLI configuration",
4687
+ memoryDescription: "Amazon Q rule",
4688
+ filenamePrefix: "amazonq"
4689
+ });
4690
+ }
4691
+
4692
+ // src/parsers/augmentcode.ts
4693
+ var import_node_path16 = require("path");
4694
+
4695
+ // src/utils/parser-helpers.ts
4696
+ function createParseResult() {
4697
+ return { rules: [], errors: [] };
4698
+ }
4699
+ function addError(result, error) {
4700
+ result.errors.push(error);
4701
+ }
4702
+ function addRule(result, rule) {
4703
+ if (!result.rules) {
4704
+ result.rules = [];
4705
+ }
4706
+ result.rules.push(rule);
4707
+ }
4708
+ function addRules(result, rules) {
4709
+ if (!result.rules) {
4710
+ result.rules = [];
4711
+ }
4712
+ result.rules.push(...rules);
4713
+ }
4714
+ async function safeReadFile(operation, errorContext) {
4715
+ try {
4716
+ const result = await operation();
4717
+ return createSuccessResult(result);
4718
+ } catch (error) {
4719
+ return createErrorResult(error, errorContext);
4720
+ }
4721
+ }
4722
+
4723
+ // src/parsers/augmentcode.ts
4724
+ async function parseAugmentcodeConfiguration(baseDir = process.cwd()) {
4725
+ return parseUnifiedAugmentcode(baseDir, {
4726
+ rulesDir: ".augment/rules",
4727
+ targetName: "augmentcode",
4728
+ filenamePrefix: "augmentcode"
4729
+ });
4730
+ }
4731
+ async function parseAugmentcodeLegacyConfiguration(baseDir = process.cwd()) {
4732
+ return parseUnifiedAugmentcode(baseDir, {
4733
+ legacyFilePath: ".augment-guidelines",
4734
+ targetName: "augmentcode-legacy",
4735
+ filenamePrefix: "augmentcode-legacy"
4736
+ });
4737
+ }
4738
+ async function parseUnifiedAugmentcode(baseDir, config) {
4739
+ const result = createParseResult();
4740
+ if (config.rulesDir) {
4741
+ const rulesDir = (0, import_node_path16.join)(baseDir, config.rulesDir);
4742
+ if (await fileExists(rulesDir)) {
4743
+ const rulesResult = await parseAugmentRules(rulesDir, config);
4744
+ addRules(result, rulesResult.rules);
4745
+ result.errors.push(...rulesResult.errors);
4746
+ } else {
4747
+ addError(
4748
+ result,
4749
+ `No AugmentCode configuration found. Expected ${config.rulesDir} directory.`
4750
+ );
4751
+ }
4752
+ }
4753
+ if (config.legacyFilePath) {
4754
+ const legacyPath = (0, import_node_path16.join)(baseDir, config.legacyFilePath);
4755
+ if (await fileExists(legacyPath)) {
4756
+ const legacyResult = await parseAugmentGuidelines(legacyPath, config);
4757
+ if (legacyResult.rule) {
4758
+ addRule(result, legacyResult.rule);
4759
+ }
4760
+ result.errors.push(...legacyResult.errors);
4761
+ } else {
4762
+ addError(
4763
+ result,
4764
+ `No AugmentCode legacy configuration found. Expected ${config.legacyFilePath} file.`
4765
+ );
4766
+ }
4767
+ }
4768
+ return { rules: result.rules || [], errors: result.errors };
4769
+ }
4770
+ async function parseAugmentRules(rulesDir, config) {
4771
+ const rules = [];
4772
+ const errors = [];
4773
+ try {
4774
+ const { readdir: readdir2 } = await import("fs/promises");
4775
+ const files = await readdir2(rulesDir);
4776
+ for (const file of files) {
4777
+ if (file.endsWith(".md") || file.endsWith(".mdc")) {
4778
+ const filePath = (0, import_node_path16.join)(rulesDir, file);
4779
+ try {
4780
+ const rawContent = await readFileContent(filePath);
4781
+ const parsed = parseFrontmatter(rawContent);
4782
+ const ruleType = extractStringField(parsed.data, "type", "manual");
4783
+ const description = extractStringField(parsed.data, "description", "");
4784
+ const tags = extractArrayField(parsed.data, "tags");
4785
+ const isRoot = ruleType === "always";
4786
+ const filename = (0, import_node_path16.basename)(file, file.endsWith(".mdc") ? ".mdc" : ".md");
4787
+ const frontmatter = {
4788
+ root: isRoot,
4789
+ targets: [config.targetName],
4790
+ description,
4791
+ globs: ["**/*"],
4792
+ // AugmentCode doesn't use specific globs in the same way
4793
+ ...tags.length > 0 && { tags }
4794
+ };
4795
+ rules.push({
4796
+ frontmatter,
4797
+ content: parsed.content.trim(),
4798
+ filename: `${config.filenamePrefix}-${ruleType}-${filename}`,
4799
+ filepath: filePath
4800
+ });
4801
+ } catch (error) {
4802
+ const errorMessage = error instanceof Error ? error.message : String(error);
4803
+ errors.push(`Failed to parse ${filePath}: ${errorMessage}`);
4804
+ }
4805
+ }
4806
+ }
4807
+ } catch (error) {
4808
+ const errorMessage = error instanceof Error ? error.message : String(error);
4809
+ errors.push(`Failed to read ${config.rulesDir || rulesDir} directory: ${errorMessage}`);
4810
+ }
4811
+ return { rules, errors };
4812
+ }
4813
+ async function parseAugmentGuidelines(guidelinesPath, config) {
4814
+ const parseResult = await safeReadFile(
4815
+ async () => {
4816
+ const content = await readFileContent(guidelinesPath);
4817
+ if (content.trim()) {
4818
+ const frontmatter = {
4819
+ root: true,
4820
+ // Legacy guidelines become root rules
4821
+ targets: [config.targetName],
4822
+ description: "Legacy AugmentCode guidelines",
4823
+ globs: ["**/*"]
4824
+ };
4825
+ return {
4826
+ frontmatter,
4827
+ content: content.trim(),
4828
+ filename: `${config.filenamePrefix}-guidelines`,
4829
+ filepath: guidelinesPath
4830
+ };
4831
+ }
4832
+ return null;
4833
+ },
4834
+ `Failed to parse ${config.legacyFilePath || guidelinesPath}`
4835
+ );
4836
+ if (parseResult.success) {
4837
+ return { rule: parseResult.result || null, errors: [] };
4838
+ } else {
4839
+ return { rule: null, errors: [parseResult.error || "Unknown error"] };
4840
+ }
4841
+ }
4842
+
4486
4843
  // src/parsers/claudecode.ts
4487
4844
  async function parseClaudeConfiguration(baseDir = process.cwd()) {
4488
4845
  return parseMemoryBasedConfiguration(baseDir, {
@@ -4820,6 +5177,32 @@ async function parseJunieConfiguration(baseDir = process.cwd()) {
4820
5177
  return { rules, errors };
4821
5178
  }
4822
5179
 
5180
+ // src/parsers/opencode.ts
5181
+ async function parseOpCodeIgnore(opcodeignorePath) {
5182
+ try {
5183
+ const content = await readFileContent(opcodeignorePath);
5184
+ const patterns = content.split("\n").map((line) => line.trim()).filter((line) => line && !line.startsWith("#"));
5185
+ return patterns;
5186
+ } catch {
5187
+ return [];
5188
+ }
5189
+ }
5190
+ async function parseOpenCodeConfiguration(baseDir = process.cwd()) {
5191
+ return parseMemoryBasedConfiguration(baseDir, {
5192
+ tool: "opencode",
5193
+ mainFileName: "AGENTS.md",
5194
+ memoryDirPath: ".opencode/memories",
5195
+ settingsPath: "opencode.json",
5196
+ mainDescription: "Main OpenCode configuration",
5197
+ memoryDescription: "Memory file",
5198
+ filenamePrefix: "opencode",
5199
+ additionalIgnoreFile: {
5200
+ path: ".opcodeignore",
5201
+ parser: parseOpCodeIgnore
5202
+ }
5203
+ });
5204
+ }
5205
+
4823
5206
  // src/parsers/roo.ts
4824
5207
  async function parseRooConfiguration(baseDir = process.cwd()) {
4825
5208
  return parseConfigurationFiles(baseDir, {
@@ -4843,8 +5226,10 @@ async function parseRooConfiguration(baseDir = process.cwd()) {
4843
5226
  // src/parsers/windsurf.ts
4844
5227
  var import_promises3 = require("fs/promises");
4845
5228
  var import_node_path20 = require("path");
5229
+ init_logger();
4846
5230
 
4847
5231
  // src/core/importer.ts
5232
+ init_logger();
4848
5233
  async function importConfiguration(options) {
4849
5234
  const {
4850
5235
  tool,
@@ -4862,6 +5247,13 @@ async function importConfiguration(options) {
4862
5247
  }
4863
5248
  try {
4864
5249
  switch (tool) {
5250
+ case "amazonqcli": {
5251
+ const amazonqResult = await parseAmazonqcliConfiguration(baseDir);
5252
+ rules = amazonqResult.rules;
5253
+ errors.push(...amazonqResult.errors);
5254
+ mcpServers = amazonqResult.mcpServers;
5255
+ break;
5256
+ }
4865
5257
  case "augmentcode": {
4866
5258
  const augmentResult = await parseAugmentcodeConfiguration(baseDir);
4867
5259
  rules = augmentResult.rules;
@@ -4922,6 +5314,14 @@ async function importConfiguration(options) {
4922
5314
  errors.push(...junieResult.errors);
4923
5315
  break;
4924
5316
  }
5317
+ case "opencode": {
5318
+ const opencodeResult = await parseOpenCodeConfiguration(baseDir);
5319
+ rules = opencodeResult.rules;
5320
+ errors.push(...opencodeResult.errors);
5321
+ ignorePatterns = opencodeResult.ignorePatterns;
5322
+ mcpServers = opencodeResult.mcpServers;
5323
+ break;
5324
+ }
4925
5325
  default:
4926
5326
  errors.push(`Unsupported tool: ${tool}`);
4927
5327
  return { success: false, rulesCreated: 0, errors };
@@ -5025,9 +5425,11 @@ function generateRuleFileContent(rule) {
5025
5425
  }
5026
5426
 
5027
5427
  // src/cli/commands/import.ts
5428
+ init_logger();
5028
5429
  async function importCommand(options = {}) {
5029
5430
  logger.setVerbose(options.verbose || false);
5030
5431
  const tools = [];
5432
+ if (options.amazonqcli) tools.push("amazonqcli");
5031
5433
  if (options.augmentcode) tools.push("augmentcode");
5032
5434
  if (options["augmentcode-legacy"]) tools.push("augmentcode-legacy");
5033
5435
  if (options.claudecode) tools.push("claudecode");
@@ -5036,9 +5438,10 @@ async function importCommand(options = {}) {
5036
5438
  if (options.cline) tools.push("cline");
5037
5439
  if (options.roo) tools.push("roo");
5038
5440
  if (options.geminicli) tools.push("geminicli");
5441
+ if (options.opencode) tools.push("opencode");
5039
5442
  if (tools.length === 0) {
5040
5443
  logger.error(
5041
- "\u274C Please specify one tool to import from (--augmentcode, --augmentcode-legacy, --claudecode, --cursor, --copilot, --cline, --roo, --geminicli)"
5444
+ "\u274C Please specify one tool to import from (--amazonqcli, --augmentcode, --augmentcode-legacy, --claudecode, --cursor, --copilot, --cline, --roo, --geminicli, --opencode)"
5042
5445
  );
5043
5446
  process.exit(1);
5044
5447
  }
@@ -5087,6 +5490,7 @@ async function importCommand(options = {}) {
5087
5490
 
5088
5491
  // src/cli/commands/init.ts
5089
5492
  var import_node_path22 = require("path");
5493
+ init_logger();
5090
5494
  async function initCommand(options = {}) {
5091
5495
  const configResult = await loadConfig();
5092
5496
  const config = configResult.config;
@@ -5150,6 +5554,7 @@ globs: ["**/*"]
5150
5554
  }
5151
5555
 
5152
5556
  // src/cli/commands/status.ts
5557
+ init_logger();
5153
5558
  async function statusCommand() {
5154
5559
  const config = getDefaultConfig();
5155
5560
  logger.log("rulesync Status");
@@ -5202,6 +5607,7 @@ async function statusCommand() {
5202
5607
  }
5203
5608
 
5204
5609
  // src/cli/commands/validate.ts
5610
+ init_logger();
5205
5611
  async function validateCommand() {
5206
5612
  const config = getDefaultConfig();
5207
5613
  logger.log("Validating rulesync configuration...");
@@ -5244,6 +5650,7 @@ Validation failed with ${validation.errors.length} error(s)`);
5244
5650
 
5245
5651
  // src/cli/commands/watch.ts
5246
5652
  var import_chokidar = require("chokidar");
5653
+ init_logger();
5247
5654
  async function watchCommand() {
5248
5655
  const config = getDefaultConfig();
5249
5656
  logger.log("\u{1F440} Watching for changes in .rulesync directory...");
@@ -5284,12 +5691,12 @@ async function watchCommand() {
5284
5691
 
5285
5692
  // src/cli/index.ts
5286
5693
  var program = new import_commander.Command();
5287
- program.name("rulesync").description("Unified AI rules management CLI tool").version("0.63.0");
5694
+ program.name("rulesync").description("Unified AI rules management CLI tool").version("0.64.0");
5288
5695
  program.command("init").description("Initialize rulesync in current directory").option("--legacy", "Use legacy file location (.rulesync/*.md instead of .rulesync/rules/*.md)").action(initCommand);
5289
5696
  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);
5290
5697
  program.command("gitignore").description("Add generated files to .gitignore").action(gitignoreCommand);
5291
- 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);
5292
- 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(
5698
+ 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);
5699
+ 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(
5293
5700
  "-b, --base-dir <paths>",
5294
5701
  "Base directories to generate files (comma-separated for multiple paths)"
5295
5702
  ).option("-v, --verbose", "Verbose output").option("-c, --config <path>", "Path to configuration file").option("--no-config", "Disable configuration file loading").action(async (options) => {
@@ -5305,6 +5712,7 @@ program.command("generate").description("Generate configuration files for AI too
5305
5712
  if (options.geminicli) tools.push("geminicli");
5306
5713
  if (options.junie) tools.push("junie");
5307
5714
  if (options.kiro) tools.push("kiro");
5715
+ if (options.opencode) tools.push("opencode");
5308
5716
  if (options.windsurf) tools.push("windsurf");
5309
5717
  const generateOptions = {
5310
5718
  verbose: options.verbose,