rulesync 0.54.0 → 0.56.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.ja.md +67 -5
  2. package/README.md +77 -6
  3. package/dist/{augmentcode-MJYD2Y4S.js → augmentcode-HIZIQG2W.js} +2 -2
  4. package/dist/chunk-7E4U4YAB.js +17 -0
  5. package/dist/{chunk-D7XQ4OHK.js → chunk-7UBF4OLN.js} +1 -1
  6. package/dist/{chunk-VI6SBYFB.js → chunk-AUUSMVCT.js} +2 -1
  7. package/dist/chunk-J3TBR5EP.js +292 -0
  8. package/dist/{chunk-OXKDEZJK.js → chunk-KUGTKMNW.js} +1 -1
  9. package/dist/{chunk-QVPD6ENS.js → chunk-LXTA7DBA.js} +1 -1
  10. package/dist/chunk-OA473EXZ.js +17 -0
  11. package/dist/{chunk-BEPSWIZC.js → chunk-PCATT4UZ.js} +1 -1
  12. package/dist/chunk-VKNCBVZF.js +17 -0
  13. package/dist/chunk-VNT6AHHO.js +17 -0
  14. package/dist/chunk-W2WU253H.js +17 -0
  15. package/dist/chunk-WAX2UANS.js +61 -0
  16. package/dist/{chunk-ORNO5MOO.js → chunk-YTU3SCQO.js} +1 -1
  17. package/dist/{claudecode-CKGUHLRR.js → claudecode-VVI2PTKI.js} +3 -3
  18. package/dist/{cline-Z5C656VR.js → cline-BJLFSLEB.js} +3 -3
  19. package/dist/{codexcli-VFUJKSIJ.js → codexcli-LKWQB3V3.js} +3 -3
  20. package/dist/{copilot-4WQS5TA7.js → copilot-MOR3HHJX.js} +2 -2
  21. package/dist/{cursor-HOB2F2V2.js → cursor-2BVUO64T.js} +3 -2
  22. package/dist/{geminicli-XTMQTIU2.js → geminicli-5YFMKRFL.js} +3 -2
  23. package/dist/index.cjs +898 -493
  24. package/dist/index.js +669 -337
  25. package/dist/{junie-AN6CR7DD.js → junie-5TDJPUXX.js} +3 -2
  26. package/dist/{kiro-PTUZOHQ2.js → kiro-YDHXY2MA.js} +2 -2
  27. package/dist/{roo-WOMS36KU.js → roo-L3QTTIPO.js} +2 -2
  28. package/dist/windsurf-PXDRIQ76.js +10 -0
  29. package/package.json +1 -1
  30. package/dist/chunk-3PHMFVXP.js +0 -66
  31. package/dist/chunk-OY6BYYIX.js +0 -63
  32. package/dist/chunk-PPAQWVXX.js +0 -94
  33. package/dist/chunk-TJKD6LEW.js +0 -90
  34. package/dist/chunk-UHANRG2O.js +0 -54
  35. package/dist/chunk-UZCJNUXO.js +0 -67
package/dist/index.cjs CHANGED
@@ -47,7 +47,8 @@ var init_tool_targets = __esm({
47
47
  "roo",
48
48
  "geminicli",
49
49
  "kiro",
50
- "junie"
50
+ "junie",
51
+ "windsurf"
51
52
  ];
52
53
  ToolTargetSchema = import_mini.z.enum(ALL_TOOL_TARGETS);
53
54
  ToolTargetsSchema = import_mini.z.array(ToolTargetSchema);
@@ -147,11 +148,72 @@ function generateMcpConfig(config, toolConfig) {
147
148
  const finalConfig = toolConfig.configWrapper(servers);
148
149
  return JSON.stringify(finalConfig, null, 2);
149
150
  }
150
- var configWrappers;
151
+ function generateMcpFromRegistry(tool, config) {
152
+ const generatorConfig = MCP_GENERATOR_REGISTRY[tool];
153
+ if (!generatorConfig) {
154
+ throw new Error(`No MCP generator configuration found for tool: ${tool}`);
155
+ }
156
+ return generateMcpConfig(config, generatorConfig);
157
+ }
158
+ var serverTransforms, configWrappers, MCP_GENERATOR_REGISTRY;
151
159
  var init_shared_factory = __esm({
152
160
  "src/generators/mcp/shared-factory.ts"() {
153
161
  "use strict";
154
162
  init_mcp_helpers();
163
+ serverTransforms = {
164
+ /**
165
+ * Basic server transformation (command, args, env, url handling)
166
+ */
167
+ basic: (server) => {
168
+ const result = {};
169
+ if (server.command) {
170
+ result.command = server.command;
171
+ if (server.args) result.args = server.args;
172
+ } else if (server.url || server.httpUrl) {
173
+ const url = server.httpUrl || server.url;
174
+ if (url) result.url = url;
175
+ }
176
+ if (server.env) {
177
+ result.env = server.env;
178
+ }
179
+ return result;
180
+ },
181
+ /**
182
+ * Extended server transformation (includes disabled, alwaysAllow, etc.)
183
+ */
184
+ extended: (server) => {
185
+ const result = serverTransforms.basic(server);
186
+ if (server.disabled !== void 0) {
187
+ result.disabled = server.disabled;
188
+ }
189
+ if (server.alwaysAllow) {
190
+ result.alwaysAllow = server.alwaysAllow;
191
+ }
192
+ if (server.networkTimeout !== void 0) {
193
+ result.networkTimeout = server.networkTimeout;
194
+ }
195
+ if (server.tools) {
196
+ result.tools = server.tools;
197
+ }
198
+ if (server.timeout !== void 0) {
199
+ result.timeout = server.timeout;
200
+ }
201
+ if (server.trust !== void 0) {
202
+ result.trust = server.trust;
203
+ }
204
+ if (server.headers) {
205
+ result.headers = server.headers;
206
+ }
207
+ return result;
208
+ },
209
+ /**
210
+ * Remove rulesync-specific properties from server config
211
+ */
212
+ cleanRulesyncProps: (server) => {
213
+ const { targets: _, transport: _transport, ...cleanServer } = server;
214
+ return { ...cleanServer };
215
+ }
216
+ };
155
217
  configWrappers = {
156
218
  /**
157
219
  * Standard mcpServers wrapper
@@ -166,37 +228,141 @@ var init_shared_factory = __esm({
166
228
  servers
167
229
  })
168
230
  };
231
+ MCP_GENERATOR_REGISTRY = {
232
+ claudecode: {
233
+ target: "claudecode",
234
+ configPaths: [".claude/settings.json"],
235
+ serverTransform: (server) => {
236
+ const claudeServer = {};
237
+ if (server.command) {
238
+ claudeServer.command = server.command;
239
+ if (server.args) claudeServer.args = server.args;
240
+ } else if (server.url || server.httpUrl) {
241
+ const url = server.httpUrl || server.url;
242
+ if (url) {
243
+ claudeServer.url = url;
244
+ }
245
+ if (server.httpUrl) {
246
+ claudeServer.transport = "http";
247
+ } else if (server.transport === "sse") {
248
+ claudeServer.transport = "sse";
249
+ }
250
+ }
251
+ if (server.env) {
252
+ claudeServer.env = server.env;
253
+ }
254
+ return claudeServer;
255
+ },
256
+ configWrapper: configWrappers.mcpServers
257
+ },
258
+ cursor: {
259
+ target: "cursor",
260
+ configPaths: [".cursor/mcp.json"],
261
+ serverTransform: (server) => {
262
+ const cursorServer = {};
263
+ if (server.command) {
264
+ cursorServer.command = server.command;
265
+ if (server.args) cursorServer.args = server.args;
266
+ } else if (server.url || server.httpUrl) {
267
+ const url = server.httpUrl || server.url;
268
+ if (url) {
269
+ cursorServer.url = url;
270
+ }
271
+ if (server.httpUrl || server.transport === "http") {
272
+ cursorServer.type = "streamable-http";
273
+ } else if (server.transport === "sse" || server.type === "sse") {
274
+ cursorServer.type = "sse";
275
+ }
276
+ }
277
+ if (server.env) {
278
+ cursorServer.env = server.env;
279
+ }
280
+ if (server.cwd) {
281
+ cursorServer.cwd = server.cwd;
282
+ }
283
+ return cursorServer;
284
+ },
285
+ configWrapper: configWrappers.mcpServers
286
+ },
287
+ windsurf: {
288
+ target: "windsurf",
289
+ configPaths: ["mcp_config.json"],
290
+ serverTransform: (server) => {
291
+ const windsurfServer = {};
292
+ if (server.command) {
293
+ windsurfServer.command = server.command;
294
+ if (server.args) windsurfServer.args = server.args;
295
+ } else if (server.url || server.httpUrl) {
296
+ const url = server.httpUrl || server.url;
297
+ if (url) {
298
+ windsurfServer.serverUrl = url;
299
+ }
300
+ }
301
+ if (server.env) {
302
+ windsurfServer.env = server.env;
303
+ }
304
+ if (server.cwd) {
305
+ windsurfServer.cwd = server.cwd;
306
+ }
307
+ return windsurfServer;
308
+ },
309
+ configWrapper: configWrappers.mcpServers
310
+ },
311
+ junie: {
312
+ target: "junie",
313
+ configPaths: [".junie/mcp-config.json"],
314
+ serverTransform: (server, serverName) => {
315
+ const junieServer = {
316
+ name: serverName
317
+ };
318
+ if (server.command) {
319
+ junieServer.command = server.command;
320
+ if (server.args) junieServer.args = server.args;
321
+ } else if (server.url || server.httpUrl) {
322
+ if (server.httpUrl) {
323
+ junieServer.httpUrl = server.httpUrl;
324
+ } else if (server.url) {
325
+ junieServer.url = server.url;
326
+ }
327
+ }
328
+ if (server.env) {
329
+ junieServer.env = server.env;
330
+ }
331
+ if (server.cwd) {
332
+ junieServer.workingDirectory = server.cwd;
333
+ }
334
+ if (server.timeout !== void 0) {
335
+ junieServer.timeout = server.timeout;
336
+ }
337
+ if (server.trust !== void 0) {
338
+ junieServer.trust = server.trust;
339
+ }
340
+ if (server.transport) {
341
+ if (String(server.transport) === "streamable-http") {
342
+ junieServer.transport = "http";
343
+ } else if (server.transport === "stdio" || server.transport === "http" || server.transport === "sse") {
344
+ junieServer.transport = server.transport;
345
+ }
346
+ } else if (server.command) {
347
+ junieServer.transport = "stdio";
348
+ }
349
+ return junieServer;
350
+ },
351
+ configWrapper: configWrappers.mcpServers
352
+ },
353
+ cline: {
354
+ target: "cline",
355
+ configPaths: [".cline/mcp.json"],
356
+ serverTransform: serverTransforms.extended,
357
+ configWrapper: configWrappers.mcpServers
358
+ }
359
+ };
169
360
  }
170
361
  });
171
362
 
172
363
  // src/generators/mcp/claudecode.ts
173
364
  function generateClaudeMcp(config) {
174
- return generateMcpConfig(config, {
175
- target: "claudecode",
176
- configPaths: [".claude/settings.json"],
177
- serverTransform: (server) => {
178
- const claudeServer = {};
179
- if (server.command) {
180
- claudeServer.command = server.command;
181
- if (server.args) claudeServer.args = server.args;
182
- } else if (server.url || server.httpUrl) {
183
- const url = server.httpUrl || server.url;
184
- if (url) {
185
- claudeServer.url = url;
186
- }
187
- if (server.httpUrl) {
188
- claudeServer.transport = "http";
189
- } else if (server.transport === "sse") {
190
- claudeServer.transport = "sse";
191
- }
192
- }
193
- if (server.env) {
194
- claudeServer.env = server.env;
195
- }
196
- return claudeServer;
197
- },
198
- configWrapper: configWrappers.mcpServers
199
- });
365
+ return generateMcpFromRegistry("claudecode", config);
200
366
  }
201
367
  var init_claudecode = __esm({
202
368
  "src/generators/mcp/claudecode.ts"() {
@@ -207,33 +373,7 @@ var init_claudecode = __esm({
207
373
 
208
374
  // src/generators/mcp/cline.ts
209
375
  function generateClineMcp(config) {
210
- return generateMcpConfig(config, {
211
- target: "cline",
212
- configPaths: [".cline/mcp.json"],
213
- serverTransform: (server) => {
214
- const clineServer = {};
215
- if (server.command) {
216
- clineServer.command = server.command;
217
- if (server.args) clineServer.args = server.args;
218
- } else if (server.url) {
219
- clineServer.url = server.url;
220
- }
221
- if (server.env) {
222
- clineServer.env = server.env;
223
- }
224
- if (server.disabled !== void 0) {
225
- clineServer.disabled = server.disabled;
226
- }
227
- if (server.alwaysAllow) {
228
- clineServer.alwaysAllow = server.alwaysAllow;
229
- }
230
- if (server.networkTimeout !== void 0) {
231
- clineServer.networkTimeout = server.networkTimeout;
232
- }
233
- return clineServer;
234
- },
235
- configWrapper: configWrappers.mcpServers
236
- });
376
+ return generateMcpFromRegistry("cline", config);
237
377
  }
238
378
  var init_cline = __esm({
239
379
  "src/generators/mcp/cline.ts"() {
@@ -370,137 +510,61 @@ var init_copilot = __esm({
370
510
 
371
511
  // src/generators/mcp/cursor.ts
372
512
  function generateCursorMcp(config) {
373
- const cursorConfig = {
374
- mcpServers: {}
375
- };
376
- for (const [serverName, server] of Object.entries(config.mcpServers)) {
377
- if (!shouldIncludeServer(server, "cursor")) continue;
378
- const cursorServer = {};
379
- if (server.command) {
380
- cursorServer.command = server.command;
381
- if (server.args) cursorServer.args = server.args;
382
- } else if (server.url || server.httpUrl) {
383
- const url = server.httpUrl || server.url;
384
- if (url) {
385
- cursorServer.url = url;
386
- }
387
- if (server.httpUrl || server.transport === "http") {
388
- cursorServer.type = "streamable-http";
389
- } else if (server.transport === "sse" || server.type === "sse") {
390
- cursorServer.type = "sse";
391
- }
392
- }
393
- if (server.env) {
394
- cursorServer.env = server.env;
395
- }
396
- if (server.cwd) {
397
- cursorServer.cwd = server.cwd;
398
- }
399
- cursorConfig.mcpServers[serverName] = cursorServer;
400
- }
401
- return JSON.stringify(cursorConfig, null, 2);
513
+ return generateMcpFromRegistry("cursor", config);
402
514
  }
403
515
  var init_cursor = __esm({
404
516
  "src/generators/mcp/cursor.ts"() {
405
517
  "use strict";
406
- init_mcp_helpers();
518
+ init_shared_factory();
407
519
  }
408
520
  });
409
521
 
410
522
  // src/generators/mcp/geminicli.ts
411
523
  function generateGeminiCliMcp(config) {
412
- const geminiSettings = {
413
- mcpServers: {}
414
- };
415
- for (const [serverName, server] of Object.entries(config.mcpServers)) {
416
- if (!shouldIncludeServer(server, "geminicli")) continue;
417
- const geminiServer = {};
418
- if (server.command) {
419
- geminiServer.command = server.command;
420
- if (server.args) geminiServer.args = server.args;
421
- } else if (server.url || server.httpUrl) {
422
- if (server.httpUrl) {
423
- geminiServer.httpUrl = server.httpUrl;
424
- } else if (server.url) {
425
- geminiServer.url = server.url;
426
- }
427
- }
428
- if (server.env) {
429
- geminiServer.env = {};
430
- for (const [key, value] of Object.entries(server.env)) {
431
- if (value.startsWith("${") && value.endsWith("}")) {
432
- geminiServer.env[key] = value;
433
- } else {
434
- geminiServer.env[key] = `\${${value}}`;
524
+ return generateMcpConfig(config, {
525
+ target: "geminicli",
526
+ configPaths: [".gemini/settings.json"],
527
+ serverTransform: (server) => {
528
+ const geminiServer = {};
529
+ if (server.command) {
530
+ geminiServer.command = server.command;
531
+ if (server.args) geminiServer.args = server.args;
532
+ } else if (server.url || server.httpUrl) {
533
+ if (server.httpUrl) {
534
+ geminiServer.httpUrl = server.httpUrl;
535
+ } else if (server.url) {
536
+ geminiServer.url = server.url;
435
537
  }
436
538
  }
437
- }
438
- if (server.timeout !== void 0) {
439
- geminiServer.timeout = server.timeout;
440
- }
441
- if (server.trust !== void 0) {
442
- geminiServer.trust = server.trust;
443
- }
444
- geminiSettings.mcpServers[serverName] = geminiServer;
445
- }
446
- return JSON.stringify(geminiSettings, null, 2);
539
+ if (server.env) {
540
+ geminiServer.env = server.env;
541
+ }
542
+ if (server.timeout !== void 0) {
543
+ geminiServer.timeout = server.timeout;
544
+ }
545
+ if (server.trust !== void 0) {
546
+ geminiServer.trust = server.trust;
547
+ }
548
+ return geminiServer;
549
+ },
550
+ configWrapper: configWrappers.mcpServers
551
+ });
447
552
  }
448
553
  var init_geminicli = __esm({
449
554
  "src/generators/mcp/geminicli.ts"() {
450
555
  "use strict";
451
- init_mcp_helpers();
556
+ init_shared_factory();
452
557
  }
453
558
  });
454
559
 
455
560
  // src/generators/mcp/junie.ts
456
561
  function generateJunieMcp(config) {
457
- const junieConfig = {
458
- mcpServers: {}
459
- };
460
- for (const [serverName, server] of Object.entries(config.mcpServers)) {
461
- if (!shouldIncludeServer(server, "junie")) continue;
462
- const junieServer = {
463
- name: serverName
464
- };
465
- if (server.command) {
466
- junieServer.command = server.command;
467
- if (server.args) junieServer.args = server.args;
468
- } else if (server.url || server.httpUrl) {
469
- if (server.httpUrl) {
470
- junieServer.httpUrl = server.httpUrl;
471
- } else if (server.url) {
472
- junieServer.url = server.url;
473
- }
474
- }
475
- if (server.env) {
476
- junieServer.env = server.env;
477
- }
478
- if (server.cwd) {
479
- junieServer.workingDirectory = server.cwd;
480
- }
481
- if (server.timeout !== void 0) {
482
- junieServer.timeout = server.timeout;
483
- }
484
- if (server.trust !== void 0) {
485
- junieServer.trust = server.trust;
486
- }
487
- if (server.transport) {
488
- if (String(server.transport) === "streamable-http") {
489
- junieServer.transport = "http";
490
- } else if (server.transport === "stdio" || server.transport === "http" || server.transport === "sse") {
491
- junieServer.transport = server.transport;
492
- }
493
- } else if (server.command) {
494
- junieServer.transport = "stdio";
495
- }
496
- junieConfig.mcpServers[serverName] = junieServer;
497
- }
498
- return JSON.stringify(junieConfig, null, 2);
562
+ return generateMcpFromRegistry("junie", config);
499
563
  }
500
564
  var init_junie = __esm({
501
565
  "src/generators/mcp/junie.ts"() {
502
566
  "use strict";
503
- init_mcp_helpers();
567
+ init_shared_factory();
504
568
  }
505
569
  });
506
570
 
@@ -607,6 +671,17 @@ var init_roo = __esm({
607
671
  }
608
672
  });
609
673
 
674
+ // src/generators/mcp/windsurf.ts
675
+ function generateWindsurfMcp(config) {
676
+ return generateMcpFromRegistry("windsurf", config);
677
+ }
678
+ var init_windsurf = __esm({
679
+ "src/generators/mcp/windsurf.ts"() {
680
+ "use strict";
681
+ init_shared_factory();
682
+ }
683
+ });
684
+
610
685
  // src/cli/index.ts
611
686
  var import_commander = require("commander");
612
687
 
@@ -630,7 +705,8 @@ function getDefaultConfig() {
630
705
  roo: ".roo/rules",
631
706
  geminicli: ".gemini/memories",
632
707
  kiro: ".kiro/steering",
633
- junie: "."
708
+ junie: ".",
709
+ windsurf: "."
634
710
  },
635
711
  watchEnabled: false,
636
712
  defaultTargets: ALL_TOOL_TARGETS.filter((tool) => tool !== "augmentcode-legacy")
@@ -719,7 +795,8 @@ var OutputPathsSchema = import_mini4.z.object({
719
795
  roo: import_mini4.z.optional(import_mini4.z.string()),
720
796
  geminicli: import_mini4.z.optional(import_mini4.z.string()),
721
797
  kiro: import_mini4.z.optional(import_mini4.z.string()),
722
- junie: import_mini4.z.optional(import_mini4.z.string())
798
+ junie: import_mini4.z.optional(import_mini4.z.string()),
799
+ windsurf: import_mini4.z.optional(import_mini4.z.string())
723
800
  });
724
801
  var ConfigOptionsSchema = import_mini4.z.object({
725
802
  aiRulesDir: import_mini4.z.optional(import_mini4.z.string()),
@@ -801,6 +878,8 @@ var RuleFrontmatterSchema = import_mini6.z.object({
801
878
  description: import_mini6.z.string(),
802
879
  globs: import_mini6.z.array(import_mini6.z.string()),
803
880
  cursorRuleType: import_mini6.z.optional(import_mini6.z.enum(["always", "manual", "specificFiles", "intelligently"])),
881
+ windsurfActivationMode: import_mini6.z.optional(import_mini6.z.enum(["always", "manual", "model-decision", "glob"])),
882
+ windsurfOutputFormat: import_mini6.z.optional(import_mini6.z.enum(["single-file", "directory"])),
804
883
  tags: import_mini6.z.optional(import_mini6.z.array(import_mini6.z.string()))
805
884
  });
806
885
  var ParsedRuleSchema = import_mini6.z.object({
@@ -1012,6 +1091,36 @@ function mergeWithCliOptions(config, cliOptions) {
1012
1091
  return merged;
1013
1092
  }
1014
1093
 
1094
+ // src/utils/error.ts
1095
+ function getErrorMessage(error) {
1096
+ return error instanceof Error ? error.message : String(error);
1097
+ }
1098
+ function formatErrorWithContext(error, context) {
1099
+ const errorMessage = getErrorMessage(error);
1100
+ return `${context}: ${errorMessage}`;
1101
+ }
1102
+ function createErrorResult(error, context) {
1103
+ const errorMessage = context ? formatErrorWithContext(error, context) : getErrorMessage(error);
1104
+ return {
1105
+ success: false,
1106
+ error: errorMessage
1107
+ };
1108
+ }
1109
+ function createSuccessResult(result) {
1110
+ return {
1111
+ success: true,
1112
+ result
1113
+ };
1114
+ }
1115
+ async function safeAsyncOperation(operation, errorContext) {
1116
+ try {
1117
+ const result = await operation();
1118
+ return createSuccessResult(result);
1119
+ } catch (error) {
1120
+ return createErrorResult(error, errorContext);
1121
+ }
1122
+ }
1123
+
1015
1124
  // src/utils/file.ts
1016
1125
  var import_promises2 = require("fs/promises");
1017
1126
  var import_node_path = require("path");
@@ -1022,6 +1131,9 @@ async function ensureDir(dirPath) {
1022
1131
  await (0, import_promises2.mkdir)(dirPath, { recursive: true });
1023
1132
  }
1024
1133
  }
1134
+ function resolvePath(relativePath, baseDir) {
1135
+ return baseDir ? (0, import_node_path.join)(baseDir, relativePath) : relativePath;
1136
+ }
1025
1137
  async function readFileContent(filepath) {
1026
1138
  return (0, import_promises2.readFile)(filepath, "utf-8");
1027
1139
  }
@@ -1312,7 +1424,7 @@ export default config;
1312
1424
  }
1313
1425
 
1314
1426
  // src/cli/commands/generate.ts
1315
- var import_node_path12 = require("path");
1427
+ var import_node_path10 = require("path");
1316
1428
 
1317
1429
  // src/generators/ignore/shared-factory.ts
1318
1430
  var import_node_path3 = require("path");
@@ -1872,6 +1984,131 @@ var ignoreConfigs = {
1872
1984
  ],
1873
1985
  includeCommonPatterns: false,
1874
1986
  projectPatternsHeader: "# \u2500\u2500\u2500\u2500\u2500 Project-specific patterns from rulesync rules \u2500\u2500\u2500\u2500\u2500"
1987
+ },
1988
+ windsurf: {
1989
+ tool: "windsurf",
1990
+ filename: ".codeiumignore",
1991
+ header: [
1992
+ "# Generated by rulesync - Windsurf AI Code Editor ignore file",
1993
+ "# This file controls which files are excluded from Cascade AI analysis and context",
1994
+ "# Uses same syntax as .gitignore patterns",
1995
+ "# Note: Git-ignored files are automatically excluded by Windsurf"
1996
+ ],
1997
+ corePatterns: [
1998
+ "# \u2500\u2500\u2500\u2500\u2500 Security & Credentials (Critical) \u2500\u2500\u2500\u2500\u2500",
1999
+ "# Environment files",
2000
+ ".env*",
2001
+ "!.env.example",
2002
+ "",
2003
+ "# Private keys and certificates",
2004
+ "*.pem",
2005
+ "*.key",
2006
+ "*.crt",
2007
+ "*.p12",
2008
+ "*.pfx",
2009
+ "*.der",
2010
+ "",
2011
+ "# SSH keys",
2012
+ "id_rsa*",
2013
+ "id_dsa*",
2014
+ "*.ppk",
2015
+ "",
2016
+ "# API keys and tokens",
2017
+ "**/apikeys/",
2018
+ "**/*_token*",
2019
+ "**/*_secret*",
2020
+ "**/*api_key*",
2021
+ "",
2022
+ "# Cloud provider credentials",
2023
+ "aws-credentials.json",
2024
+ "gcp-service-account*.json",
2025
+ "azure-credentials.json",
2026
+ "",
2027
+ "# \u2500\u2500\u2500\u2500\u2500 Database & Configuration Files \u2500\u2500\u2500\u2500\u2500",
2028
+ "# Database files",
2029
+ "*.db",
2030
+ "*.sqlite",
2031
+ "*.sqlite3",
2032
+ "",
2033
+ "# Configuration files with secrets",
2034
+ "config/secrets/",
2035
+ "**/database.yml",
2036
+ "",
2037
+ "# \u2500\u2500\u2500\u2500\u2500 Build Artifacts & Dependencies \u2500\u2500\u2500\u2500\u2500",
2038
+ "# Build outputs",
2039
+ "dist/",
2040
+ "build/",
2041
+ "out/",
2042
+ "target/",
2043
+ "",
2044
+ "# Dependencies (already auto-excluded but reinforced)",
2045
+ "node_modules/",
2046
+ ".pnpm-store/",
2047
+ ".yarn/",
2048
+ "vendor/",
2049
+ "",
2050
+ "# \u2500\u2500\u2500\u2500\u2500 Cache & Temporary Files \u2500\u2500\u2500\u2500\u2500",
2051
+ "# Cache directories",
2052
+ ".cache/",
2053
+ ".parcel-cache/",
2054
+ ".next/cache/",
2055
+ "",
2056
+ "# Temporary files",
2057
+ "*.tmp",
2058
+ "*.swp",
2059
+ "*.swo",
2060
+ "*~",
2061
+ "",
2062
+ "# \u2500\u2500\u2500\u2500\u2500 Large Data Files \u2500\u2500\u2500\u2500\u2500",
2063
+ "# Data files",
2064
+ "*.csv",
2065
+ "*.xlsx",
2066
+ "*.json",
2067
+ "data/",
2068
+ "datasets/",
2069
+ "",
2070
+ "# Media files",
2071
+ "*.mp4",
2072
+ "*.avi",
2073
+ "*.mov",
2074
+ "*.png",
2075
+ "*.jpg",
2076
+ "*.jpeg",
2077
+ "*.gif",
2078
+ "",
2079
+ "# Archives",
2080
+ "*.zip",
2081
+ "*.tar.gz",
2082
+ "*.rar",
2083
+ "",
2084
+ "# \u2500\u2500\u2500\u2500\u2500 IDE & Editor Files \u2500\u2500\u2500\u2500\u2500",
2085
+ "# IDE settings (personal)",
2086
+ ".vscode/settings.json",
2087
+ ".idea/",
2088
+ "",
2089
+ "# Editor temporary files",
2090
+ "*.swp",
2091
+ "*.swo",
2092
+ "",
2093
+ "# \u2500\u2500\u2500\u2500\u2500 Test Coverage & Logs \u2500\u2500\u2500\u2500\u2500",
2094
+ "# Test coverage reports",
2095
+ "coverage/",
2096
+ ".nyc_output/",
2097
+ "",
2098
+ "# Logs",
2099
+ "*.log",
2100
+ "",
2101
+ "# \u2500\u2500\u2500\u2500\u2500 Re-include Important Files \u2500\u2500\u2500\u2500\u2500",
2102
+ "# Allow configuration examples",
2103
+ "!.env.example",
2104
+ "!config/*.example.*",
2105
+ "",
2106
+ "# Allow documentation",
2107
+ "!docs/**/*.md",
2108
+ "!README.md"
2109
+ ],
2110
+ includeCommonPatterns: false,
2111
+ projectPatternsHeader: "# \u2500\u2500\u2500\u2500\u2500 Project-specific patterns from rulesync rules \u2500\u2500\u2500\u2500\u2500"
1875
2112
  }
1876
2113
  };
1877
2114
 
@@ -1890,7 +2127,12 @@ async function generateKiroIgnoreFiles(rules, config, baseDir) {
1890
2127
  return generateIgnoreFile(rules, config, ignoreConfigs.kiro, baseDir);
1891
2128
  }
1892
2129
 
1893
- // src/generators/rules/augmentcode.ts
2130
+ // src/generators/ignore/windsurf.ts
2131
+ function generateWindsurfIgnore(rules, config, baseDir) {
2132
+ return generateIgnoreFile(rules, config, ignoreConfigs.windsurf, baseDir);
2133
+ }
2134
+
2135
+ // src/generators/rules/augmentcode.ts
1894
2136
  var import_node_path6 = require("path");
1895
2137
 
1896
2138
  // src/generators/rules/shared-helpers.ts
@@ -1949,7 +2191,7 @@ function filterIgnoredFiles(files, ignorePatterns) {
1949
2191
 
1950
2192
  // src/generators/rules/shared-helpers.ts
1951
2193
  function resolveOutputDir(config, tool, baseDir) {
1952
- return baseDir ? (0, import_node_path5.join)(baseDir, config.outputPaths[tool]) : config.outputPaths[tool];
2194
+ return resolvePath(config.outputPaths[tool], baseDir);
1953
2195
  }
1954
2196
  function createOutputsArray() {
1955
2197
  return [];
@@ -1976,7 +2218,7 @@ async function generateRulesConfig(rules, config, generatorConfig, baseDir) {
1976
2218
  }
1977
2219
  const ignorePatterns = await loadIgnorePatterns(baseDir);
1978
2220
  if (ignorePatterns.patterns.length > 0) {
1979
- const ignorePath = baseDir ? (0, import_node_path5.join)(baseDir, generatorConfig.ignoreFileName) : generatorConfig.ignoreFileName;
2221
+ const ignorePath = resolvePath(generatorConfig.ignoreFileName, baseDir);
1980
2222
  const ignoreContent = generateIgnoreFile2(ignorePatterns.patterns, generatorConfig.tool);
1981
2223
  outputs.push({
1982
2224
  tool: generatorConfig.tool,
@@ -1994,7 +2236,10 @@ async function generateComplexRules(rules, config, generatorConfig, baseDir) {
1994
2236
  if (generatorConfig.generateDetailContent && generatorConfig.detailSubDir) {
1995
2237
  for (const rule of detailRules) {
1996
2238
  const content = generatorConfig.generateDetailContent(rule);
1997
- const filepath = baseDir ? (0, import_node_path5.join)(baseDir, generatorConfig.detailSubDir, `${rule.filename}.md`) : (0, import_node_path5.join)(generatorConfig.detailSubDir, `${rule.filename}.md`);
2239
+ const filepath = resolvePath(
2240
+ (0, import_node_path5.join)(generatorConfig.detailSubDir, `${rule.filename}.md`),
2241
+ baseDir
2242
+ );
1998
2243
  outputs.push({
1999
2244
  tool: generatorConfig.tool,
2000
2245
  filepath,
@@ -2004,7 +2249,7 @@ async function generateComplexRules(rules, config, generatorConfig, baseDir) {
2004
2249
  }
2005
2250
  if (generatorConfig.generateRootContent && generatorConfig.rootFilePath) {
2006
2251
  const rootContent = generatorConfig.generateRootContent(rootRule, detailRules, baseDir);
2007
- const rootFilepath = baseDir ? (0, import_node_path5.join)(baseDir, generatorConfig.rootFilePath) : generatorConfig.rootFilePath;
2252
+ const rootFilepath = resolvePath(generatorConfig.rootFilePath, baseDir);
2008
2253
  outputs.push({
2009
2254
  tool: generatorConfig.tool,
2010
2255
  filepath: rootFilepath,
@@ -2013,7 +2258,7 @@ async function generateComplexRules(rules, config, generatorConfig, baseDir) {
2013
2258
  }
2014
2259
  const ignorePatterns = await loadIgnorePatterns(baseDir);
2015
2260
  if (ignorePatterns.patterns.length > 0) {
2016
- const ignorePath = baseDir ? (0, import_node_path5.join)(baseDir, generatorConfig.ignoreFileName) : generatorConfig.ignoreFileName;
2261
+ const ignorePath = resolvePath(generatorConfig.ignoreFileName, baseDir);
2017
2262
  const ignoreContent = generateIgnoreFile2(ignorePatterns.patterns, generatorConfig.tool);
2018
2263
  outputs.push({
2019
2264
  tool: generatorConfig.tool,
@@ -2043,16 +2288,6 @@ function generateIgnoreFile2(patterns, tool) {
2043
2288
  lines.push(...patterns);
2044
2289
  return lines.join("\n");
2045
2290
  }
2046
- async function generateComplexRulesConfig(rules, config, generatorConfig, baseDir) {
2047
- const unifiedConfig = {
2048
- tool: generatorConfig.tool,
2049
- fileExtension: generatorConfig.fileExtension,
2050
- ignoreFileName: generatorConfig.ignoreFileName,
2051
- generateContent: generatorConfig.generateContent,
2052
- pathResolver: generatorConfig.getOutputPath
2053
- };
2054
- return generateRulesConfig(rules, config, unifiedConfig, baseDir);
2055
- }
2056
2291
 
2057
2292
  // src/generators/rules/augmentcode.ts
2058
2293
  async function generateAugmentcodeConfig(rules, config, baseDir) {
@@ -2118,32 +2353,24 @@ function generateLegacyGuidelinesFile(allRules) {
2118
2353
  // src/generators/rules/claudecode.ts
2119
2354
  var import_node_path7 = require("path");
2120
2355
  async function generateClaudecodeConfig(rules, config, baseDir) {
2121
- const outputs = [];
2122
- const rootRules = rules.filter((r) => r.frontmatter.root === true);
2123
- const detailRules = rules.filter((r) => r.frontmatter.root === false);
2124
- const claudeMdContent = generateClaudeMarkdown(rootRules, detailRules);
2125
- const claudeOutputDir = baseDir ? (0, import_node_path7.join)(baseDir, config.outputPaths.claudecode) : config.outputPaths.claudecode;
2126
- outputs.push({
2356
+ const generatorConfig = {
2127
2357
  tool: "claudecode",
2128
- filepath: (0, import_node_path7.join)(claudeOutputDir, "CLAUDE.md"),
2129
- content: claudeMdContent
2130
- });
2131
- for (const rule of detailRules) {
2132
- const memoryContent = generateMemoryFile(rule);
2133
- outputs.push({
2134
- tool: "claudecode",
2135
- filepath: (0, import_node_path7.join)(claudeOutputDir, ".claude", "memories", `${rule.filename}.md`),
2136
- content: memoryContent
2137
- });
2138
- }
2139
- const ignorePatterns = await loadIgnorePatterns(baseDir);
2140
- if (ignorePatterns.patterns.length > 0) {
2141
- const settingsPath = baseDir ? (0, import_node_path7.join)(baseDir, ".claude", "settings.json") : (0, import_node_path7.join)(".claude", "settings.json");
2142
- await updateClaudeSettings(settingsPath, ignorePatterns.patterns);
2143
- }
2144
- return outputs;
2358
+ fileExtension: ".md",
2359
+ ignoreFileName: ".aiignore",
2360
+ generateContent: generateMemoryFile,
2361
+ generateRootContent: generateClaudeMarkdown,
2362
+ rootFilePath: "CLAUDE.md",
2363
+ generateDetailContent: generateMemoryFile,
2364
+ detailSubDir: ".claude/memories",
2365
+ updateAdditionalConfig: async (ignorePatterns, baseDir2) => {
2366
+ const settingsPath = resolvePath((0, import_node_path7.join)(".claude", "settings.json"), baseDir2);
2367
+ await updateClaudeSettings(settingsPath, ignorePatterns);
2368
+ return [];
2369
+ }
2370
+ };
2371
+ return generateComplexRules(rules, config, generatorConfig, baseDir);
2145
2372
  }
2146
- function generateClaudeMarkdown(rootRules, detailRules) {
2373
+ function generateClaudeMarkdown(rootRule, detailRules) {
2147
2374
  const lines = [];
2148
2375
  if (detailRules.length > 0) {
2149
2376
  lines.push("Please also reference the following documents as needed:");
@@ -2157,11 +2384,9 @@ function generateClaudeMarkdown(rootRules, detailRules) {
2157
2384
  }
2158
2385
  lines.push("");
2159
2386
  }
2160
- if (rootRules.length > 0) {
2161
- for (const rule of rootRules) {
2162
- lines.push(rule.content);
2163
- lines.push("");
2164
- }
2387
+ if (rootRule) {
2388
+ lines.push(rootRule.content);
2389
+ lines.push("");
2165
2390
  }
2166
2391
  return lines.join("\n");
2167
2392
  }
@@ -2201,134 +2426,8 @@ async function updateClaudeSettings(settingsPath, ignorePatterns) {
2201
2426
  console.log(`\u2705 Updated Claude Code settings: ${settingsPath}`);
2202
2427
  }
2203
2428
 
2204
- // src/generators/rules/cline.ts
2205
- async function generateClineConfig(rules, config, baseDir) {
2206
- return generateRulesConfig(
2207
- rules,
2208
- config,
2209
- {
2210
- tool: "cline",
2211
- fileExtension: ".md",
2212
- ignoreFileName: ".clineignore",
2213
- generateContent: (rule) => rule.content.trim()
2214
- },
2215
- baseDir
2216
- );
2217
- }
2218
-
2219
- // src/generators/rules/codexcli.ts
2220
- async function generateCodexConfig(rules, config, baseDir) {
2221
- const generatorConfig = {
2222
- tool: "codexcli",
2223
- fileExtension: ".md",
2224
- ignoreFileName: ".codexignore",
2225
- generateContent: generateCodexInstructionsMarkdown,
2226
- pathResolver: (rule, outputDir) => {
2227
- if (rule.frontmatter.root === true) {
2228
- return `${outputDir}/codex.md`;
2229
- }
2230
- return `${outputDir}/${rule.filename}.md`;
2231
- }
2232
- };
2233
- return generateRulesConfig(rules, config, generatorConfig, baseDir);
2234
- }
2235
- function generateCodexInstructionsMarkdown(rule) {
2236
- const lines = [];
2237
- if (rule.frontmatter.root === false && rule.frontmatter.description && rule.frontmatter.description !== "Main instructions" && !rule.frontmatter.description.includes("Project-level Codex CLI instructions")) {
2238
- lines.push(`<!-- ${rule.frontmatter.description} -->`);
2239
- if (rule.content.trim()) {
2240
- lines.push("");
2241
- }
2242
- }
2243
- const content = rule.content.trim();
2244
- if (content) {
2245
- lines.push(content);
2246
- }
2247
- return lines.join("\n");
2248
- }
2249
-
2250
- // src/generators/rules/copilot.ts
2429
+ // src/generators/rules/generator-registry.ts
2251
2430
  var import_node_path8 = require("path");
2252
- async function generateCopilotConfig(rules, config, baseDir) {
2253
- return generateComplexRulesConfig(
2254
- rules,
2255
- config,
2256
- {
2257
- tool: "copilot",
2258
- fileExtension: ".instructions.md",
2259
- ignoreFileName: ".copilotignore",
2260
- generateContent: generateCopilotMarkdown,
2261
- getOutputPath: (rule, outputDir) => {
2262
- const baseFilename = rule.filename.replace(/\.md$/, "");
2263
- return (0, import_node_path8.join)(outputDir, `${baseFilename}.instructions.md`);
2264
- }
2265
- },
2266
- baseDir
2267
- );
2268
- }
2269
- function generateCopilotMarkdown(rule) {
2270
- const lines = [];
2271
- lines.push("---");
2272
- lines.push(`description: "${rule.frontmatter.description}"`);
2273
- if (rule.frontmatter.globs.length > 0) {
2274
- lines.push(`applyTo: "${rule.frontmatter.globs.join(", ")}"`);
2275
- } else {
2276
- lines.push('applyTo: "**"');
2277
- }
2278
- lines.push("---");
2279
- lines.push(rule.content);
2280
- return lines.join("\n");
2281
- }
2282
-
2283
- // src/generators/rules/cursor.ts
2284
- var import_node_path9 = require("path");
2285
- async function generateCursorConfig(rules, config, baseDir) {
2286
- return generateComplexRulesConfig(
2287
- rules,
2288
- config,
2289
- {
2290
- tool: "cursor",
2291
- fileExtension: ".mdc",
2292
- ignoreFileName: ".cursorignore",
2293
- generateContent: generateCursorMarkdown,
2294
- getOutputPath: (rule, outputDir) => {
2295
- return (0, import_node_path9.join)(outputDir, `${rule.filename}.mdc`);
2296
- }
2297
- },
2298
- baseDir
2299
- );
2300
- }
2301
- function generateCursorMarkdown(rule) {
2302
- const lines = [];
2303
- const ruleType = determineCursorRuleType(rule.frontmatter);
2304
- lines.push("---");
2305
- switch (ruleType) {
2306
- case "always":
2307
- lines.push("description:");
2308
- lines.push("globs:");
2309
- lines.push("alwaysApply: true");
2310
- break;
2311
- case "manual":
2312
- lines.push("description:");
2313
- lines.push("globs:");
2314
- lines.push("alwaysApply: false");
2315
- break;
2316
- case "specificFiles":
2317
- lines.push("description:");
2318
- lines.push(`globs: ${rule.frontmatter.globs.join(",")}`);
2319
- lines.push("alwaysApply: false");
2320
- break;
2321
- case "intelligently":
2322
- lines.push(`description: ${rule.frontmatter.description}`);
2323
- lines.push("globs:");
2324
- lines.push("alwaysApply: false");
2325
- break;
2326
- }
2327
- lines.push("---");
2328
- lines.push("");
2329
- lines.push(rule.content);
2330
- return lines.join("\n");
2331
- }
2332
2431
  function determineCursorRuleType(frontmatter) {
2333
2432
  if (frontmatter.cursorRuleType) {
2334
2433
  return frontmatter.cursorRuleType;
@@ -2350,6 +2449,293 @@ function determineCursorRuleType(frontmatter) {
2350
2449
  }
2351
2450
  return "intelligently";
2352
2451
  }
2452
+ var GENERATOR_REGISTRY = {
2453
+ // Simple generators - generate one file per rule
2454
+ cline: {
2455
+ type: "simple",
2456
+ tool: "cline",
2457
+ fileExtension: ".md",
2458
+ ignoreFileName: ".clineignore",
2459
+ generateContent: (rule) => rule.content.trim()
2460
+ },
2461
+ roo: {
2462
+ type: "simple",
2463
+ tool: "roo",
2464
+ fileExtension: ".md",
2465
+ ignoreFileName: ".rooignore",
2466
+ generateContent: (rule) => rule.content.trim()
2467
+ },
2468
+ kiro: {
2469
+ type: "simple",
2470
+ tool: "kiro",
2471
+ fileExtension: ".md",
2472
+ ignoreFileName: ".kiroignore",
2473
+ generateContent: (rule) => rule.content.trim()
2474
+ },
2475
+ augmentcode: {
2476
+ type: "simple",
2477
+ tool: "augmentcode",
2478
+ fileExtension: ".md",
2479
+ ignoreFileName: ".aiignore",
2480
+ generateContent: (rule) => rule.content.trim()
2481
+ },
2482
+ "augmentcode-legacy": {
2483
+ type: "simple",
2484
+ tool: "augmentcode-legacy",
2485
+ fileExtension: ".md",
2486
+ ignoreFileName: ".aiignore",
2487
+ generateContent: (rule) => rule.content.trim()
2488
+ },
2489
+ // Complex generators with custom content formatting
2490
+ copilot: {
2491
+ type: "simple",
2492
+ tool: "copilot",
2493
+ fileExtension: ".instructions.md",
2494
+ ignoreFileName: ".copilotignore",
2495
+ generateContent: (rule) => {
2496
+ const lines = [];
2497
+ lines.push("---");
2498
+ lines.push(`description: "${rule.frontmatter.description}"`);
2499
+ if (rule.frontmatter.globs.length > 0) {
2500
+ lines.push(`applyTo: "${rule.frontmatter.globs.join(", ")}"`);
2501
+ } else {
2502
+ lines.push('applyTo: "**"');
2503
+ }
2504
+ lines.push("---");
2505
+ lines.push(rule.content);
2506
+ return lines.join("\n");
2507
+ },
2508
+ pathResolver: (rule, outputDir) => {
2509
+ const baseFilename = rule.filename.replace(/\.md$/, "");
2510
+ return (0, import_node_path8.join)(outputDir, `${baseFilename}.instructions.md`);
2511
+ }
2512
+ },
2513
+ cursor: {
2514
+ type: "simple",
2515
+ tool: "cursor",
2516
+ fileExtension: ".mdc",
2517
+ ignoreFileName: ".cursorignore",
2518
+ generateContent: (rule) => {
2519
+ const lines = [];
2520
+ const ruleType = determineCursorRuleType(rule.frontmatter);
2521
+ lines.push("---");
2522
+ switch (ruleType) {
2523
+ case "always":
2524
+ lines.push("description:");
2525
+ lines.push("globs:");
2526
+ lines.push("alwaysApply: true");
2527
+ break;
2528
+ case "manual":
2529
+ lines.push("description:");
2530
+ lines.push("globs:");
2531
+ lines.push("alwaysApply: false");
2532
+ break;
2533
+ case "specificFiles":
2534
+ lines.push("description:");
2535
+ lines.push(`globs: ${rule.frontmatter.globs.join(",")}`);
2536
+ lines.push("alwaysApply: false");
2537
+ break;
2538
+ case "intelligently":
2539
+ lines.push(`description: ${rule.frontmatter.description}`);
2540
+ lines.push("globs:");
2541
+ lines.push("alwaysApply: false");
2542
+ break;
2543
+ }
2544
+ lines.push("---");
2545
+ lines.push("");
2546
+ lines.push(rule.content);
2547
+ return lines.join("\n");
2548
+ },
2549
+ pathResolver: (rule, outputDir) => {
2550
+ return (0, import_node_path8.join)(outputDir, `${rule.filename}.mdc`);
2551
+ }
2552
+ },
2553
+ codexcli: {
2554
+ type: "simple",
2555
+ tool: "codexcli",
2556
+ fileExtension: ".md",
2557
+ ignoreFileName: ".codexignore",
2558
+ generateContent: (rule) => rule.content.trim()
2559
+ },
2560
+ windsurf: {
2561
+ type: "simple",
2562
+ tool: "windsurf",
2563
+ fileExtension: ".md",
2564
+ ignoreFileName: ".codeiumignore",
2565
+ generateContent: (rule) => {
2566
+ const lines = [];
2567
+ const activationMode = rule.frontmatter.windsurfActivationMode;
2568
+ const globPattern = rule.frontmatter.globs?.[0];
2569
+ if (activationMode || globPattern) {
2570
+ lines.push("---");
2571
+ if (activationMode) {
2572
+ lines.push(`activation: ${activationMode}`);
2573
+ }
2574
+ if (globPattern && activationMode === "glob") {
2575
+ lines.push(`pattern: "${globPattern}"`);
2576
+ }
2577
+ lines.push("---");
2578
+ lines.push("");
2579
+ }
2580
+ lines.push(rule.content.trim());
2581
+ return lines.join("\n");
2582
+ },
2583
+ pathResolver: (rule, outputDir) => {
2584
+ const outputFormat = rule.frontmatter.windsurfOutputFormat || "directory";
2585
+ if (outputFormat === "single-file") {
2586
+ return (0, import_node_path8.join)(outputDir, ".windsurf-rules");
2587
+ } else {
2588
+ const rulesDir = (0, import_node_path8.join)(outputDir, ".windsurf", "rules");
2589
+ return (0, import_node_path8.join)(rulesDir, `${rule.filename}.md`);
2590
+ }
2591
+ }
2592
+ },
2593
+ // Complex generators with root + detail pattern
2594
+ claudecode: {
2595
+ type: "complex",
2596
+ tool: "claudecode",
2597
+ fileExtension: ".md",
2598
+ ignoreFileName: ".aiignore",
2599
+ generateContent: (rule) => {
2600
+ const lines = [];
2601
+ if (rule.frontmatter.description) {
2602
+ lines.push(`# ${rule.frontmatter.description}
2603
+ `);
2604
+ }
2605
+ lines.push(rule.content.trim());
2606
+ return lines.join("\n");
2607
+ }
2608
+ // NOTE: Claude Code specific logic is handled in the actual generator file
2609
+ // due to complex settings.json manipulation requirements
2610
+ },
2611
+ geminicli: {
2612
+ type: "complex",
2613
+ tool: "geminicli",
2614
+ fileExtension: ".md",
2615
+ ignoreFileName: ".aiexclude",
2616
+ generateContent: (rule) => {
2617
+ const lines = [];
2618
+ if (rule.frontmatter.description) {
2619
+ lines.push(`# ${rule.frontmatter.description}
2620
+ `);
2621
+ }
2622
+ lines.push(rule.content.trim());
2623
+ return lines.join("\n");
2624
+ }
2625
+ // Complex generation handled by existing generator
2626
+ },
2627
+ junie: {
2628
+ type: "complex",
2629
+ tool: "junie",
2630
+ fileExtension: ".md",
2631
+ ignoreFileName: ".aiignore",
2632
+ generateContent: (rule) => {
2633
+ const lines = [];
2634
+ if (rule.frontmatter.description) {
2635
+ lines.push(`# ${rule.frontmatter.description}
2636
+ `);
2637
+ }
2638
+ lines.push(rule.content.trim());
2639
+ return lines.join("\n");
2640
+ }
2641
+ // Complex generation handled by existing generator
2642
+ }
2643
+ };
2644
+ async function generateFromRegistry(tool, rules, config, baseDir) {
2645
+ const generatorConfig = GENERATOR_REGISTRY[tool];
2646
+ if (!generatorConfig) {
2647
+ throw new Error(`No generator configuration found for tool: ${tool}`);
2648
+ }
2649
+ if (generatorConfig.type === "simple") {
2650
+ const ruleConfig = {
2651
+ tool: generatorConfig.tool,
2652
+ fileExtension: generatorConfig.fileExtension,
2653
+ ignoreFileName: generatorConfig.ignoreFileName,
2654
+ generateContent: generatorConfig.generateContent,
2655
+ ...generatorConfig.pathResolver && { pathResolver: generatorConfig.pathResolver }
2656
+ };
2657
+ return generateRulesConfig(rules, config, ruleConfig, baseDir);
2658
+ } else {
2659
+ const enhancedConfig = {
2660
+ tool: generatorConfig.tool,
2661
+ fileExtension: generatorConfig.fileExtension,
2662
+ ignoreFileName: generatorConfig.ignoreFileName,
2663
+ generateContent: generatorConfig.generateContent,
2664
+ ...generatorConfig.generateRootContent && {
2665
+ generateRootContent: generatorConfig.generateRootContent
2666
+ },
2667
+ ...generatorConfig.rootFilePath && { rootFilePath: generatorConfig.rootFilePath },
2668
+ ...generatorConfig.generateDetailContent && {
2669
+ generateDetailContent: generatorConfig.generateDetailContent
2670
+ },
2671
+ ...generatorConfig.detailSubDir && { detailSubDir: generatorConfig.detailSubDir },
2672
+ ...generatorConfig.updateAdditionalConfig && {
2673
+ updateAdditionalConfig: generatorConfig.updateAdditionalConfig
2674
+ }
2675
+ };
2676
+ return generateComplexRules(rules, config, enhancedConfig, baseDir);
2677
+ }
2678
+ }
2679
+
2680
+ // src/generators/rules/cline.ts
2681
+ async function generateClineConfig(rules, config, baseDir) {
2682
+ return generateFromRegistry("cline", rules, config, baseDir);
2683
+ }
2684
+
2685
+ // src/generators/rules/codexcli.ts
2686
+ async function generateCodexConfig(rules, config, baseDir) {
2687
+ const outputs = [];
2688
+ if (rules.length === 0) {
2689
+ return outputs;
2690
+ }
2691
+ const sortedRules = [...rules].sort((a, b) => {
2692
+ if (a.frontmatter.root === true && b.frontmatter.root !== true) return -1;
2693
+ if (a.frontmatter.root !== true && b.frontmatter.root === true) return 1;
2694
+ return 0;
2695
+ });
2696
+ const concatenatedContent = generateConcatenatedCodexContent(sortedRules);
2697
+ if (concatenatedContent.trim()) {
2698
+ const outputDir = resolveOutputDir(config, "codexcli", baseDir);
2699
+ const filepath = `${outputDir}/codex.md`;
2700
+ outputs.push({
2701
+ tool: "codexcli",
2702
+ filepath,
2703
+ content: concatenatedContent
2704
+ });
2705
+ }
2706
+ const ignorePatterns = await loadIgnorePatterns(baseDir);
2707
+ if (ignorePatterns.patterns.length > 0) {
2708
+ const ignorePath = resolvePath(".codexignore", baseDir);
2709
+ const ignoreContent = generateIgnoreFile2(ignorePatterns.patterns, "codexcli");
2710
+ outputs.push({
2711
+ tool: "codexcli",
2712
+ filepath: ignorePath,
2713
+ content: ignoreContent
2714
+ });
2715
+ }
2716
+ return outputs;
2717
+ }
2718
+ function generateConcatenatedCodexContent(rules) {
2719
+ const sections = [];
2720
+ for (const rule of rules) {
2721
+ const content = rule.content.trim();
2722
+ if (!content) {
2723
+ continue;
2724
+ }
2725
+ sections.push(content);
2726
+ }
2727
+ return sections.join("\n\n---\n\n");
2728
+ }
2729
+
2730
+ // src/generators/rules/copilot.ts
2731
+ async function generateCopilotConfig(rules, config, baseDir) {
2732
+ return generateFromRegistry("copilot", rules, config, baseDir);
2733
+ }
2734
+
2735
+ // src/generators/rules/cursor.ts
2736
+ async function generateCursorConfig(rules, config, baseDir) {
2737
+ return generateFromRegistry("cursor", rules, config, baseDir);
2738
+ }
2353
2739
 
2354
2740
  // src/generators/rules/geminicli.ts
2355
2741
  async function generateGeminiConfig(rules, config, baseDir) {
@@ -2421,38 +2807,18 @@ function generateGuidelinesMarkdown(rootRule, detailRules) {
2421
2807
  }
2422
2808
 
2423
2809
  // src/generators/rules/kiro.ts
2424
- var import_node_path10 = require("path");
2425
2810
  async function generateKiroConfig(rules, config, baseDir) {
2426
- const outputs = [];
2427
- for (const rule of rules) {
2428
- const content = generateKiroMarkdown(rule);
2429
- const outputDir = baseDir ? (0, import_node_path10.join)(baseDir, config.outputPaths.kiro) : config.outputPaths.kiro;
2430
- const filepath = (0, import_node_path10.join)(outputDir, `${rule.filename}.md`);
2431
- outputs.push({
2432
- tool: "kiro",
2433
- filepath,
2434
- content
2435
- });
2436
- }
2437
- return outputs;
2438
- }
2439
- function generateKiroMarkdown(rule) {
2440
- return rule.content.trim();
2811
+ return generateFromRegistry("kiro", rules, config, baseDir);
2441
2812
  }
2442
2813
 
2443
2814
  // src/generators/rules/roo.ts
2444
2815
  async function generateRooConfig(rules, config, baseDir) {
2445
- return generateRulesConfig(
2446
- rules,
2447
- config,
2448
- {
2449
- tool: "roo",
2450
- fileExtension: ".md",
2451
- ignoreFileName: ".rooignore",
2452
- generateContent: (rule) => rule.content.trim()
2453
- },
2454
- baseDir
2455
- );
2816
+ return generateFromRegistry("roo", rules, config, baseDir);
2817
+ }
2818
+
2819
+ // src/generators/rules/windsurf.ts
2820
+ async function generateWindsurfConfig(rules, config, baseDir) {
2821
+ return generateFromRegistry("windsurf", rules, config, baseDir);
2456
2822
  }
2457
2823
 
2458
2824
  // src/core/generator.ts
@@ -2527,6 +2893,11 @@ async function generateForTool(tool, rules, config, baseDir) {
2527
2893
  const kiroIgnoreOutputs = await generateKiroIgnoreFiles(rules, config, baseDir);
2528
2894
  return [...kiroRulesOutputs, ...kiroIgnoreOutputs];
2529
2895
  }
2896
+ case "windsurf": {
2897
+ const windsurfRulesOutputs = await generateWindsurfConfig(rules, config, baseDir);
2898
+ const windsurfIgnoreOutputs = await generateWindsurfIgnore(rules, config, baseDir);
2899
+ return [...windsurfRulesOutputs, ...windsurfIgnoreOutputs];
2900
+ }
2530
2901
  default:
2531
2902
  console.warn(`Unknown tool: ${tool}`);
2532
2903
  return null;
@@ -2534,7 +2905,7 @@ async function generateForTool(tool, rules, config, baseDir) {
2534
2905
  }
2535
2906
 
2536
2907
  // src/core/parser.ts
2537
- var import_node_path11 = require("path");
2908
+ var import_node_path9 = require("path");
2538
2909
  var import_gray_matter = __toESM(require("gray-matter"), 1);
2539
2910
  async function parseRulesFromDirectory(aiRulesDir) {
2540
2911
  const ignorePatterns = await loadIgnorePatterns();
@@ -2572,7 +2943,7 @@ async function parseRuleFile(filepath) {
2572
2943
  const parsed = (0, import_gray_matter.default)(content);
2573
2944
  try {
2574
2945
  const frontmatter = RuleFrontmatterSchema.parse(parsed.data);
2575
- const filename = (0, import_node_path11.basename)(filepath, ".md");
2946
+ const filename = (0, import_node_path9.basename)(filepath, ".md");
2576
2947
  return {
2577
2948
  frontmatter,
2578
2949
  content: parsed.content,
@@ -2650,6 +3021,7 @@ init_geminicli();
2650
3021
  init_junie();
2651
3022
  init_kiro();
2652
3023
  init_roo();
3024
+ init_windsurf();
2653
3025
 
2654
3026
  // src/core/mcp-parser.ts
2655
3027
  var fs = __toESM(require("fs"), 1);
@@ -2741,6 +3113,11 @@ async function generateMcpConfigs(projectRoot, baseDir, targetTools) {
2741
3113
  tool: "roo-project",
2742
3114
  path: path4.join(targetRoot, ".roo", "mcp.json"),
2743
3115
  generate: () => generateRooMcp(config)
3116
+ },
3117
+ {
3118
+ tool: "windsurf-project",
3119
+ path: path4.join(targetRoot, "mcp_config.json"),
3120
+ generate: () => generateWindsurfMcp(config)
2744
3121
  }
2745
3122
  ];
2746
3123
  const filteredGenerators = targetTools ? generators.filter((g) => {
@@ -2757,7 +3134,7 @@ async function generateMcpConfigs(projectRoot, baseDir, targetTools) {
2757
3134
  try {
2758
3135
  const content = generator.generate();
2759
3136
  const parsed = JSON.parse(content);
2760
- if (generator.tool.includes("augmentcode") || generator.tool.includes("claude") || generator.tool.includes("cline") || generator.tool.includes("codexcli") || generator.tool.includes("cursor") || generator.tool.includes("gemini") || generator.tool.includes("junie") || generator.tool.includes("kiro") || generator.tool.includes("roo")) {
3137
+ if (generator.tool.includes("augmentcode") || generator.tool.includes("claude") || generator.tool.includes("cline") || generator.tool.includes("codexcli") || generator.tool.includes("cursor") || generator.tool.includes("gemini") || generator.tool.includes("junie") || generator.tool.includes("kiro") || generator.tool.includes("roo") || generator.tool.includes("windsurf")) {
2761
3138
  if (!parsed.mcpServers || Object.keys(parsed.mcpServers).length === 0) {
2762
3139
  results.push({
2763
3140
  tool: generator.tool,
@@ -2869,12 +3246,12 @@ async function generateCommand(options = {}) {
2869
3246
  for (const tool of targetTools) {
2870
3247
  switch (tool) {
2871
3248
  case "augmentcode":
2872
- deleteTasks.push(removeDirectory((0, import_node_path12.join)(".augment", "rules")));
2873
- deleteTasks.push(removeDirectory((0, import_node_path12.join)(".augment", "ignore")));
3249
+ deleteTasks.push(removeDirectory((0, import_node_path10.join)(".augment", "rules")));
3250
+ deleteTasks.push(removeDirectory((0, import_node_path10.join)(".augment", "ignore")));
2874
3251
  break;
2875
3252
  case "augmentcode-legacy":
2876
3253
  deleteTasks.push(removeClaudeGeneratedFiles());
2877
- deleteTasks.push(removeDirectory((0, import_node_path12.join)(".augment", "ignore")));
3254
+ deleteTasks.push(removeDirectory((0, import_node_path10.join)(".augment", "ignore")));
2878
3255
  break;
2879
3256
  case "copilot":
2880
3257
  deleteTasks.push(removeDirectory(config.outputPaths.copilot));
@@ -2897,6 +3274,9 @@ async function generateCommand(options = {}) {
2897
3274
  case "kiro":
2898
3275
  deleteTasks.push(removeDirectory(config.outputPaths.kiro));
2899
3276
  break;
3277
+ case "windsurf":
3278
+ deleteTasks.push(removeDirectory(config.outputPaths.windsurf));
3279
+ break;
2900
3280
  }
2901
3281
  }
2902
3282
  await Promise.all(deleteTasks);
@@ -2969,9 +3349,9 @@ Generating configurations for base directory: ${baseDir}`);
2969
3349
 
2970
3350
  // src/cli/commands/gitignore.ts
2971
3351
  var import_node_fs2 = require("fs");
2972
- var import_node_path13 = require("path");
3352
+ var import_node_path11 = require("path");
2973
3353
  var gitignoreCommand = async () => {
2974
- const gitignorePath = (0, import_node_path13.join)(process.cwd(), ".gitignore");
3354
+ const gitignorePath = (0, import_node_path11.join)(process.cwd(), ".gitignore");
2975
3355
  const rulesFilesToIgnore = [
2976
3356
  "# Generated by rulesync - AI tool configuration files",
2977
3357
  "**/.github/copilot-instructions.md",
@@ -3035,11 +3415,11 @@ ${linesToAdd.join("\n")}
3035
3415
  };
3036
3416
 
3037
3417
  // src/core/importer.ts
3038
- var import_node_path20 = require("path");
3039
- var import_gray_matter5 = __toESM(require("gray-matter"), 1);
3418
+ var import_node_path18 = require("path");
3419
+ var import_gray_matter6 = __toESM(require("gray-matter"), 1);
3040
3420
 
3041
3421
  // src/parsers/augmentcode.ts
3042
- var import_node_path14 = require("path");
3422
+ var import_node_path12 = require("path");
3043
3423
  var import_gray_matter2 = __toESM(require("gray-matter"), 1);
3044
3424
 
3045
3425
  // src/utils/parser-helpers.ts
@@ -3061,36 +3441,63 @@ function addRules(result, rules) {
3061
3441
  }
3062
3442
  result.rules.push(...rules);
3063
3443
  }
3064
- function handleParseError(error, context) {
3065
- const errorMessage = error instanceof Error ? error.message : String(error);
3066
- return `${context}: ${errorMessage}`;
3067
- }
3068
3444
  async function safeReadFile(operation, errorContext) {
3069
3445
  try {
3070
3446
  const result = await operation();
3071
- return { success: true, result };
3447
+ return createSuccessResult(result);
3072
3448
  } catch (error) {
3073
- return {
3074
- success: false,
3075
- error: handleParseError(error, errorContext)
3076
- };
3449
+ return createErrorResult(error, errorContext);
3077
3450
  }
3078
3451
  }
3079
3452
 
3080
3453
  // src/parsers/augmentcode.ts
3081
3454
  async function parseAugmentcodeConfiguration(baseDir = process.cwd()) {
3455
+ return parseUnifiedAugmentcode(baseDir, {
3456
+ rulesDir: ".augment/rules",
3457
+ targetName: "augmentcode",
3458
+ filenamePrefix: "augmentcode"
3459
+ });
3460
+ }
3461
+ async function parseAugmentcodeLegacyConfiguration(baseDir = process.cwd()) {
3462
+ return parseUnifiedAugmentcode(baseDir, {
3463
+ legacyFilePath: ".augment-guidelines",
3464
+ targetName: "augmentcode-legacy",
3465
+ filenamePrefix: "augmentcode-legacy"
3466
+ });
3467
+ }
3468
+ async function parseUnifiedAugmentcode(baseDir, config) {
3082
3469
  const result = createParseResult();
3083
- const rulesDir = (0, import_node_path14.join)(baseDir, ".augment", "rules");
3084
- if (await fileExists(rulesDir)) {
3085
- const rulesResult = await parseAugmentRules(rulesDir);
3086
- addRules(result, rulesResult.rules);
3087
- result.errors.push(...rulesResult.errors);
3088
- } else {
3089
- addError(result, "No AugmentCode configuration found. Expected .augment/rules/ directory.");
3470
+ if (config.rulesDir) {
3471
+ const rulesDir = (0, import_node_path12.join)(baseDir, config.rulesDir);
3472
+ if (await fileExists(rulesDir)) {
3473
+ const rulesResult = await parseAugmentRules(rulesDir, config);
3474
+ addRules(result, rulesResult.rules);
3475
+ result.errors.push(...rulesResult.errors);
3476
+ } else {
3477
+ addError(
3478
+ result,
3479
+ `No AugmentCode configuration found. Expected ${config.rulesDir} directory.`
3480
+ );
3481
+ }
3482
+ }
3483
+ if (config.legacyFilePath) {
3484
+ const legacyPath = (0, import_node_path12.join)(baseDir, config.legacyFilePath);
3485
+ if (await fileExists(legacyPath)) {
3486
+ const legacyResult = await parseAugmentGuidelines(legacyPath, config);
3487
+ if (legacyResult.rule) {
3488
+ addRule(result, legacyResult.rule);
3489
+ }
3490
+ result.errors.push(...legacyResult.errors);
3491
+ } else {
3492
+ addError(
3493
+ result,
3494
+ `No AugmentCode legacy configuration found. Expected ${config.legacyFilePath} file.`
3495
+ );
3496
+ }
3090
3497
  }
3091
3498
  return { rules: result.rules || [], errors: result.errors };
3092
3499
  }
3093
- async function parseAugmentRules(rulesDir) {
3500
+ async function parseAugmentRules(rulesDir, config) {
3094
3501
  const rules = [];
3095
3502
  const errors = [];
3096
3503
  try {
@@ -3098,7 +3505,7 @@ async function parseAugmentRules(rulesDir) {
3098
3505
  const files = await readdir2(rulesDir);
3099
3506
  for (const file of files) {
3100
3507
  if (file.endsWith(".md") || file.endsWith(".mdc")) {
3101
- const filePath = (0, import_node_path14.join)(rulesDir, file);
3508
+ const filePath = (0, import_node_path12.join)(rulesDir, file);
3102
3509
  try {
3103
3510
  const rawContent = await readFileContent(filePath);
3104
3511
  const parsed = (0, import_gray_matter2.default)(rawContent);
@@ -3107,10 +3514,10 @@ async function parseAugmentRules(rulesDir) {
3107
3514
  const description = frontmatterData.description || "";
3108
3515
  const tags = Array.isArray(frontmatterData.tags) ? frontmatterData.tags : void 0;
3109
3516
  const isRoot = ruleType === "always";
3110
- const filename = (0, import_node_path14.basename)(file, file.endsWith(".mdc") ? ".mdc" : ".md");
3517
+ const filename = (0, import_node_path12.basename)(file, file.endsWith(".mdc") ? ".mdc" : ".md");
3111
3518
  const frontmatter = {
3112
3519
  root: isRoot,
3113
- targets: ["augmentcode"],
3520
+ targets: [config.targetName],
3114
3521
  description,
3115
3522
  globs: ["**/*"],
3116
3523
  // AugmentCode doesn't use specific globs in the same way
@@ -3119,7 +3526,7 @@ async function parseAugmentRules(rulesDir) {
3119
3526
  rules.push({
3120
3527
  frontmatter,
3121
3528
  content: parsed.content.trim(),
3122
- filename: `augmentcode-${ruleType}-${filename}`,
3529
+ filename: `${config.filenamePrefix}-${ruleType}-${filename}`,
3123
3530
  filepath: filePath
3124
3531
  });
3125
3532
  } catch (error) {
@@ -3130,50 +3537,33 @@ async function parseAugmentRules(rulesDir) {
3130
3537
  }
3131
3538
  } catch (error) {
3132
3539
  const errorMessage = error instanceof Error ? error.message : String(error);
3133
- errors.push(`Failed to read .augment/rules/ directory: ${errorMessage}`);
3540
+ errors.push(`Failed to read ${config.rulesDir || rulesDir} directory: ${errorMessage}`);
3134
3541
  }
3135
3542
  return { rules, errors };
3136
3543
  }
3137
-
3138
- // src/parsers/augmentcode-legacy.ts
3139
- var import_node_path15 = require("path");
3140
- async function parseAugmentcodeLegacyConfiguration(baseDir = process.cwd()) {
3141
- const result = createParseResult();
3142
- const guidelinesPath = (0, import_node_path15.join)(baseDir, ".augment-guidelines");
3143
- if (await fileExists(guidelinesPath)) {
3144
- const guidelinesResult = await parseAugmentGuidelines(guidelinesPath);
3145
- if (guidelinesResult.rule) {
3146
- addRule(result, guidelinesResult.rule);
3147
- }
3148
- result.errors.push(...guidelinesResult.errors);
3149
- } else {
3150
- addError(
3151
- result,
3152
- "No AugmentCode legacy configuration found. Expected .augment-guidelines file."
3153
- );
3154
- }
3155
- return { rules: result.rules || [], errors: result.errors };
3156
- }
3157
- async function parseAugmentGuidelines(guidelinesPath) {
3158
- const parseResult = await safeReadFile(async () => {
3159
- const content = await readFileContent(guidelinesPath);
3160
- if (content.trim()) {
3161
- const frontmatter = {
3162
- root: true,
3163
- // Legacy guidelines become root rules
3164
- targets: ["augmentcode-legacy"],
3165
- description: "Legacy AugmentCode guidelines",
3166
- globs: ["**/*"]
3167
- };
3168
- return {
3169
- frontmatter,
3170
- content: content.trim(),
3171
- filename: "augmentcode-legacy-guidelines",
3172
- filepath: guidelinesPath
3173
- };
3174
- }
3175
- return null;
3176
- }, "Failed to parse .augment-guidelines");
3544
+ async function parseAugmentGuidelines(guidelinesPath, config) {
3545
+ const parseResult = await safeReadFile(
3546
+ async () => {
3547
+ const content = await readFileContent(guidelinesPath);
3548
+ if (content.trim()) {
3549
+ const frontmatter = {
3550
+ root: true,
3551
+ // Legacy guidelines become root rules
3552
+ targets: [config.targetName],
3553
+ description: "Legacy AugmentCode guidelines",
3554
+ globs: ["**/*"]
3555
+ };
3556
+ return {
3557
+ frontmatter,
3558
+ content: content.trim(),
3559
+ filename: `${config.filenamePrefix}-guidelines`,
3560
+ filepath: guidelinesPath
3561
+ };
3562
+ }
3563
+ return null;
3564
+ },
3565
+ `Failed to parse ${config.legacyFilePath || guidelinesPath}`
3566
+ );
3177
3567
  if (parseResult.success) {
3178
3568
  return { rule: parseResult.result || null, errors: [] };
3179
3569
  } else {
@@ -3182,33 +3572,36 @@ async function parseAugmentGuidelines(guidelinesPath) {
3182
3572
  }
3183
3573
 
3184
3574
  // src/parsers/shared-helpers.ts
3185
- var import_node_path16 = require("path");
3575
+ var import_node_path13 = require("path");
3186
3576
  var import_gray_matter3 = __toESM(require("gray-matter"), 1);
3187
3577
  async function parseConfigurationFiles(baseDir = process.cwd(), config) {
3188
3578
  const errors = [];
3189
3579
  const rules = [];
3190
3580
  if (config.mainFile) {
3191
- const mainFilePath = (0, import_node_path16.join)(baseDir, config.mainFile.path);
3581
+ const mainFile = config.mainFile;
3582
+ const mainFilePath = resolvePath(mainFile.path, baseDir);
3192
3583
  if (await fileExists(mainFilePath)) {
3193
- try {
3584
+ const result = await safeAsyncOperation(async () => {
3194
3585
  const rawContent = await readFileContent(mainFilePath);
3195
3586
  let content;
3196
3587
  let frontmatter;
3197
- if (config.mainFile.useFrontmatter) {
3588
+ if (mainFile.useFrontmatter) {
3198
3589
  const parsed = (0, import_gray_matter3.default)(rawContent);
3199
3590
  content = parsed.content.trim();
3591
+ const parsedFrontmatter = parsed.data;
3200
3592
  frontmatter = {
3201
- root: false,
3593
+ root: mainFile.isRoot ?? false,
3202
3594
  targets: [config.tool],
3203
- description: config.mainFile.description,
3204
- globs: ["**/*"]
3595
+ description: parsedFrontmatter.description || mainFile.description,
3596
+ globs: Array.isArray(parsedFrontmatter.globs) ? parsedFrontmatter.globs : ["**/*"],
3597
+ ...parsedFrontmatter.tags && { tags: parsedFrontmatter.tags }
3205
3598
  };
3206
3599
  } else {
3207
3600
  content = rawContent.trim();
3208
3601
  frontmatter = {
3209
- root: false,
3602
+ root: mainFile.isRoot ?? false,
3210
3603
  targets: [config.tool],
3211
- description: config.mainFile.description,
3604
+ description: mainFile.description,
3212
3605
  globs: ["**/*"]
3213
3606
  };
3214
3607
  }
@@ -3216,43 +3609,52 @@ async function parseConfigurationFiles(baseDir = process.cwd(), config) {
3216
3609
  rules.push({
3217
3610
  frontmatter,
3218
3611
  content,
3219
- filename: "instructions",
3612
+ filename: mainFile.filenameOverride || "instructions",
3220
3613
  filepath: mainFilePath
3221
3614
  });
3222
3615
  }
3223
- } catch (error) {
3224
- const errorMessage = error instanceof Error ? error.message : String(error);
3225
- errors.push(`Failed to parse ${config.mainFile.path}: ${errorMessage}`);
3616
+ }, `Failed to parse ${mainFile.path}`);
3617
+ if (!result.success) {
3618
+ errors.push(result.error);
3226
3619
  }
3227
3620
  }
3228
3621
  }
3229
3622
  if (config.directories) {
3230
3623
  for (const dirConfig of config.directories) {
3231
- const dirPath = (0, import_node_path16.join)(baseDir, dirConfig.directory);
3624
+ const dirPath = resolvePath(dirConfig.directory, baseDir);
3232
3625
  if (await fileExists(dirPath)) {
3233
- try {
3626
+ const result = await safeAsyncOperation(async () => {
3234
3627
  const { readdir: readdir2 } = await import("fs/promises");
3235
3628
  const files = await readdir2(dirPath);
3236
3629
  for (const file of files) {
3237
3630
  if (file.endsWith(dirConfig.filePattern)) {
3238
- const filePath = (0, import_node_path16.join)(dirPath, file);
3239
- try {
3631
+ const filePath = (0, import_node_path13.join)(dirPath, file);
3632
+ const fileResult = await safeAsyncOperation(async () => {
3240
3633
  const rawContent = await readFileContent(filePath);
3241
3634
  let content;
3635
+ let frontmatter;
3636
+ const filename = file.replace(new RegExp(`\\${dirConfig.filePattern}$`), "");
3242
3637
  if (dirConfig.filePattern === ".instructions.md") {
3243
3638
  const parsed = (0, import_gray_matter3.default)(rawContent);
3244
3639
  content = parsed.content.trim();
3640
+ const parsedFrontmatter = parsed.data;
3641
+ frontmatter = {
3642
+ root: false,
3643
+ targets: [config.tool],
3644
+ description: parsedFrontmatter.description || `${dirConfig.description}: ${filename}`,
3645
+ globs: Array.isArray(parsedFrontmatter.globs) ? parsedFrontmatter.globs : ["**/*"],
3646
+ ...parsedFrontmatter.tags && { tags: parsedFrontmatter.tags }
3647
+ };
3245
3648
  } else {
3246
3649
  content = rawContent.trim();
3247
- }
3248
- if (content) {
3249
- const filename = file.replace(new RegExp(`\\${dirConfig.filePattern}$`), "");
3250
- const frontmatter = {
3650
+ frontmatter = {
3251
3651
  root: false,
3252
3652
  targets: [config.tool],
3253
3653
  description: `${dirConfig.description}: ${filename}`,
3254
3654
  globs: ["**/*"]
3255
3655
  };
3656
+ }
3657
+ if (content) {
3256
3658
  rules.push({
3257
3659
  frontmatter,
3258
3660
  content,
@@ -3260,15 +3662,15 @@ async function parseConfigurationFiles(baseDir = process.cwd(), config) {
3260
3662
  filepath: filePath
3261
3663
  });
3262
3664
  }
3263
- } catch (error) {
3264
- const errorMessage = error instanceof Error ? error.message : String(error);
3265
- errors.push(`Failed to parse ${filePath}: ${errorMessage}`);
3665
+ }, `Failed to parse ${filePath}`);
3666
+ if (!fileResult.success) {
3667
+ errors.push(fileResult.error);
3266
3668
  }
3267
3669
  }
3268
3670
  }
3269
- } catch (error) {
3270
- const errorMessage = error instanceof Error ? error.message : String(error);
3271
- errors.push(`Failed to parse ${dirConfig.directory} files: ${errorMessage}`);
3671
+ }, `Failed to parse ${dirConfig.directory} files`);
3672
+ if (!result.success) {
3673
+ errors.push(result.error);
3272
3674
  }
3273
3675
  }
3274
3676
  }
@@ -3283,7 +3685,7 @@ async function parseMemoryBasedConfiguration(baseDir = process.cwd(), config) {
3283
3685
  const rules = [];
3284
3686
  let ignorePatterns;
3285
3687
  let mcpServers;
3286
- const mainFilePath = (0, import_node_path16.join)(baseDir, config.mainFileName);
3688
+ const mainFilePath = resolvePath(config.mainFileName, baseDir);
3287
3689
  if (!await fileExists(mainFilePath)) {
3288
3690
  errors.push(`${config.mainFileName} file not found`);
3289
3691
  return { rules, errors };
@@ -3294,12 +3696,12 @@ async function parseMemoryBasedConfiguration(baseDir = process.cwd(), config) {
3294
3696
  if (mainRule) {
3295
3697
  rules.push(mainRule);
3296
3698
  }
3297
- const memoryDir = (0, import_node_path16.join)(baseDir, config.memoryDirPath);
3699
+ const memoryDir = resolvePath(config.memoryDirPath, baseDir);
3298
3700
  if (await fileExists(memoryDir)) {
3299
3701
  const memoryRules = await parseMemoryFiles(memoryDir, config);
3300
3702
  rules.push(...memoryRules);
3301
3703
  }
3302
- const settingsPath = (0, import_node_path16.join)(baseDir, config.settingsPath);
3704
+ const settingsPath = resolvePath(config.settingsPath, baseDir);
3303
3705
  if (await fileExists(settingsPath)) {
3304
3706
  const settingsResult = await parseSettingsFile(settingsPath, config.tool);
3305
3707
  if (settingsResult.ignorePatterns) {
@@ -3311,7 +3713,7 @@ async function parseMemoryBasedConfiguration(baseDir = process.cwd(), config) {
3311
3713
  errors.push(...settingsResult.errors);
3312
3714
  }
3313
3715
  if (config.additionalIgnoreFile) {
3314
- const additionalIgnorePath = (0, import_node_path16.join)(baseDir, config.additionalIgnoreFile.path);
3716
+ const additionalIgnorePath = resolvePath(config.additionalIgnoreFile.path, baseDir);
3315
3717
  if (await fileExists(additionalIgnorePath)) {
3316
3718
  const additionalPatterns = await config.additionalIgnoreFile.parser(additionalIgnorePath);
3317
3719
  if (additionalPatterns.length > 0) {
@@ -3320,8 +3722,7 @@ async function parseMemoryBasedConfiguration(baseDir = process.cwd(), config) {
3320
3722
  }
3321
3723
  }
3322
3724
  } catch (error) {
3323
- const errorMessage = error instanceof Error ? error.message : String(error);
3324
- errors.push(`Failed to parse ${config.tool} configuration: ${errorMessage}`);
3725
+ errors.push(`Failed to parse ${config.tool} configuration: ${getErrorMessage(error)}`);
3325
3726
  }
3326
3727
  return {
3327
3728
  rules,
@@ -3365,10 +3766,10 @@ async function parseMemoryFiles(memoryDir, config) {
3365
3766
  const files = await readdir2(memoryDir);
3366
3767
  for (const file of files) {
3367
3768
  if (file.endsWith(".md")) {
3368
- const filePath = (0, import_node_path16.join)(memoryDir, file);
3769
+ const filePath = (0, import_node_path13.join)(memoryDir, file);
3369
3770
  const content = await readFileContent(filePath);
3370
3771
  if (content.trim()) {
3371
- const filename = (0, import_node_path16.basename)(file, ".md");
3772
+ const filename = (0, import_node_path13.basename)(file, ".md");
3372
3773
  const frontmatter = {
3373
3774
  root: false,
3374
3775
  targets: [config.tool],
@@ -3417,8 +3818,7 @@ async function parseSettingsFile(settingsPath, tool) {
3417
3818
  mcpServers = parseResult.data.mcpServers;
3418
3819
  }
3419
3820
  } catch (error) {
3420
- const errorMessage = error instanceof Error ? error.message : String(error);
3421
- errors.push(`Failed to parse settings.json: ${errorMessage}`);
3821
+ errors.push(`Failed to parse settings.json: ${getErrorMessage(error)}`);
3422
3822
  }
3423
3823
  return {
3424
3824
  errors,
@@ -3461,7 +3861,7 @@ async function parseClineConfiguration(baseDir = process.cwd()) {
3461
3861
  }
3462
3862
 
3463
3863
  // src/parsers/codexcli.ts
3464
- var import_node_path17 = require("path");
3864
+ var import_node_path14 = require("path");
3465
3865
 
3466
3866
  // src/parsers/copilot.ts
3467
3867
  async function parseCopilotConfiguration(baseDir = process.cwd()) {
@@ -3484,7 +3884,7 @@ async function parseCopilotConfiguration(baseDir = process.cwd()) {
3484
3884
  }
3485
3885
 
3486
3886
  // src/parsers/cursor.ts
3487
- var import_node_path18 = require("path");
3887
+ var import_node_path15 = require("path");
3488
3888
  var import_gray_matter4 = __toESM(require("gray-matter"), 1);
3489
3889
  var import_js_yaml = require("js-yaml");
3490
3890
  var import_mini7 = require("zod/mini");
@@ -3609,7 +4009,7 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
3609
4009
  const rules = [];
3610
4010
  let ignorePatterns;
3611
4011
  let mcpServers;
3612
- const cursorFilePath = (0, import_node_path18.join)(baseDir, ".cursorrules");
4012
+ const cursorFilePath = (0, import_node_path15.join)(baseDir, ".cursorrules");
3613
4013
  if (await fileExists(cursorFilePath)) {
3614
4014
  try {
3615
4015
  const rawContent = await readFileContent(cursorFilePath);
@@ -3630,20 +4030,20 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
3630
4030
  errors.push(`Failed to parse .cursorrules file: ${errorMessage}`);
3631
4031
  }
3632
4032
  }
3633
- const cursorRulesDir = (0, import_node_path18.join)(baseDir, ".cursor", "rules");
4033
+ const cursorRulesDir = (0, import_node_path15.join)(baseDir, ".cursor", "rules");
3634
4034
  if (await fileExists(cursorRulesDir)) {
3635
4035
  try {
3636
4036
  const { readdir: readdir2 } = await import("fs/promises");
3637
4037
  const files = await readdir2(cursorRulesDir);
3638
4038
  for (const file of files) {
3639
4039
  if (file.endsWith(".mdc")) {
3640
- const filePath = (0, import_node_path18.join)(cursorRulesDir, file);
4040
+ const filePath = (0, import_node_path15.join)(cursorRulesDir, file);
3641
4041
  try {
3642
4042
  const rawContent = await readFileContent(filePath);
3643
4043
  const parsed = (0, import_gray_matter4.default)(rawContent, customMatterOptions);
3644
4044
  const content = parsed.content.trim();
3645
4045
  if (content) {
3646
- const filename = (0, import_node_path18.basename)(file, ".mdc");
4046
+ const filename = (0, import_node_path15.basename)(file, ".mdc");
3647
4047
  const frontmatter = convertCursorMdcFrontmatter(parsed.data, filename);
3648
4048
  rules.push({
3649
4049
  frontmatter,
@@ -3666,7 +4066,7 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
3666
4066
  if (rules.length === 0) {
3667
4067
  errors.push("No Cursor configuration files found (.cursorrules or .cursor/rules/*.mdc)");
3668
4068
  }
3669
- const cursorIgnorePath = (0, import_node_path18.join)(baseDir, ".cursorignore");
4069
+ const cursorIgnorePath = (0, import_node_path15.join)(baseDir, ".cursorignore");
3670
4070
  if (await fileExists(cursorIgnorePath)) {
3671
4071
  try {
3672
4072
  const content = await readFileContent(cursorIgnorePath);
@@ -3679,7 +4079,7 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
3679
4079
  errors.push(`Failed to parse .cursorignore: ${errorMessage}`);
3680
4080
  }
3681
4081
  }
3682
- const cursorMcpPath = (0, import_node_path18.join)(baseDir, ".cursor", "mcp.json");
4082
+ const cursorMcpPath = (0, import_node_path15.join)(baseDir, ".cursor", "mcp.json");
3683
4083
  if (await fileExists(cursorMcpPath)) {
3684
4084
  try {
3685
4085
  const content = await readFileContent(cursorMcpPath);
@@ -3728,11 +4128,11 @@ async function parseGeminiConfiguration(baseDir = process.cwd()) {
3728
4128
  }
3729
4129
 
3730
4130
  // src/parsers/junie.ts
3731
- var import_node_path19 = require("path");
4131
+ var import_node_path16 = require("path");
3732
4132
  async function parseJunieConfiguration(baseDir = process.cwd()) {
3733
4133
  const errors = [];
3734
4134
  const rules = [];
3735
- const guidelinesPath = (0, import_node_path19.join)(baseDir, ".junie", "guidelines.md");
4135
+ const guidelinesPath = (0, import_node_path16.join)(baseDir, ".junie", "guidelines.md");
3736
4136
  if (!await fileExists(guidelinesPath)) {
3737
4137
  errors.push(".junie/guidelines.md file not found");
3738
4138
  return { rules, errors };
@@ -3783,6 +4183,11 @@ async function parseRooConfiguration(baseDir = process.cwd()) {
3783
4183
  });
3784
4184
  }
3785
4185
 
4186
+ // src/parsers/windsurf.ts
4187
+ var import_promises3 = require("fs/promises");
4188
+ var import_node_path17 = require("path");
4189
+ var import_gray_matter5 = __toESM(require("gray-matter"), 1);
4190
+
3786
4191
  // src/core/importer.ts
3787
4192
  async function importConfiguration(options) {
3788
4193
  const { tool, baseDir = process.cwd(), rulesDir = ".rulesync", verbose = false } = options;
@@ -3867,7 +4272,7 @@ async function importConfiguration(options) {
3867
4272
  if (rules.length === 0 && !ignorePatterns && !mcpServers) {
3868
4273
  return { success: false, rulesCreated: 0, errors };
3869
4274
  }
3870
- const rulesDirPath = (0, import_node_path20.join)(baseDir, rulesDir);
4275
+ const rulesDirPath = (0, import_node_path18.join)(baseDir, rulesDir);
3871
4276
  try {
3872
4277
  const { mkdir: mkdir3 } = await import("fs/promises");
3873
4278
  await mkdir3(rulesDirPath, { recursive: true });
@@ -3881,7 +4286,7 @@ async function importConfiguration(options) {
3881
4286
  try {
3882
4287
  const baseFilename = rule.filename;
3883
4288
  const filename = await generateUniqueFilename(rulesDirPath, baseFilename);
3884
- const filePath = (0, import_node_path20.join)(rulesDirPath, `${filename}.md`);
4289
+ const filePath = (0, import_node_path18.join)(rulesDirPath, `${filename}.md`);
3885
4290
  const content = generateRuleFileContent(rule);
3886
4291
  await writeFileContent(filePath, content);
3887
4292
  rulesCreated++;
@@ -3896,7 +4301,7 @@ async function importConfiguration(options) {
3896
4301
  let ignoreFileCreated = false;
3897
4302
  if (ignorePatterns && ignorePatterns.length > 0) {
3898
4303
  try {
3899
- const rulesyncignorePath = (0, import_node_path20.join)(baseDir, ".rulesyncignore");
4304
+ const rulesyncignorePath = (0, import_node_path18.join)(baseDir, ".rulesyncignore");
3900
4305
  const ignoreContent = `${ignorePatterns.join("\n")}
3901
4306
  `;
3902
4307
  await writeFileContent(rulesyncignorePath, ignoreContent);
@@ -3912,7 +4317,7 @@ async function importConfiguration(options) {
3912
4317
  let mcpFileCreated = false;
3913
4318
  if (mcpServers && Object.keys(mcpServers).length > 0) {
3914
4319
  try {
3915
- const mcpPath = (0, import_node_path20.join)(baseDir, rulesDir, ".mcp.json");
4320
+ const mcpPath = (0, import_node_path18.join)(baseDir, rulesDir, ".mcp.json");
3916
4321
  const mcpContent = `${JSON.stringify({ mcpServers }, null, 2)}
3917
4322
  `;
3918
4323
  await writeFileContent(mcpPath, mcpContent);
@@ -3934,13 +4339,13 @@ async function importConfiguration(options) {
3934
4339
  };
3935
4340
  }
3936
4341
  function generateRuleFileContent(rule) {
3937
- const frontmatter = import_gray_matter5.default.stringify("", rule.frontmatter);
4342
+ const frontmatter = import_gray_matter6.default.stringify("", rule.frontmatter);
3938
4343
  return frontmatter + rule.content;
3939
4344
  }
3940
4345
  async function generateUniqueFilename(rulesDir, baseFilename) {
3941
4346
  let filename = baseFilename;
3942
4347
  let counter = 1;
3943
- while (await fileExists((0, import_node_path20.join)(rulesDir, `${filename}.md`))) {
4348
+ while (await fileExists((0, import_node_path18.join)(rulesDir, `${filename}.md`))) {
3944
4349
  filename = `${baseFilename}-${counter}`;
3945
4350
  counter++;
3946
4351
  }
@@ -4007,7 +4412,7 @@ async function importCommand(options = {}) {
4007
4412
  }
4008
4413
 
4009
4414
  // src/cli/commands/init.ts
4010
- var import_node_path21 = require("path");
4415
+ var import_node_path19 = require("path");
4011
4416
  async function initCommand() {
4012
4417
  const aiRulesDir = ".rulesync";
4013
4418
  console.log("Initializing rulesync...");
@@ -4054,7 +4459,7 @@ globs: ["**/*"]
4054
4459
  - Follow single responsibility principle
4055
4460
  `
4056
4461
  };
4057
- const filepath = (0, import_node_path21.join)(aiRulesDir, sampleFile.filename);
4462
+ const filepath = (0, import_node_path19.join)(aiRulesDir, sampleFile.filename);
4058
4463
  if (!await fileExists(filepath)) {
4059
4464
  await writeFileContent(filepath, sampleFile.content);
4060
4465
  console.log(`Created ${filepath}`);
@@ -4198,7 +4603,7 @@ async function watchCommand() {
4198
4603
 
4199
4604
  // src/cli/index.ts
4200
4605
  var program = new import_commander.Command();
4201
- program.name("rulesync").description("Unified AI rules management CLI tool").version("0.54.0");
4606
+ program.name("rulesync").description("Unified AI rules management CLI tool").version("0.56.0");
4202
4607
  program.command("init").description("Initialize rulesync in current directory").action(initCommand);
4203
4608
  program.command("add <filename>").description("Add a new rule file").action(addCommand);
4204
4609
  program.command("gitignore").description("Add generated files to .gitignore").action(gitignoreCommand);