rulesync 0.64.0 → 0.67.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 +87 -42
  2. package/dist/{amazonqcli-WVGYACHI.js → amazonqcli-PWXCSRAN.js} +2 -2
  3. package/dist/{augmentcode-DTHPPXWO.js → augmentcode-4AFYW4BU.js} +2 -2
  4. package/dist/{chunk-FVPZQEWP.js → chunk-35VMCHXQ.js} +1 -1
  5. package/dist/{chunk-EID75W45.js → chunk-3QGD3CH5.js} +1 -1
  6. package/dist/{chunk-LURFNGH4.js → chunk-5GKH5TQ4.js} +1 -1
  7. package/dist/{chunk-KKWJVA56.js → chunk-7BIZ5Y6F.js} +1 -1
  8. package/dist/{chunk-HHJIL3YZ.js → chunk-7QVQO6MQ.js} +1 -1
  9. package/dist/{chunk-TQOL7OKY.js → chunk-B2HD24KC.js} +1 -1
  10. package/dist/{chunk-LYVES5YR.js → chunk-CS7AV6JT.js} +2 -0
  11. package/dist/{chunk-DMTCLQ4T.js → chunk-KONQNQY3.js} +1 -1
  12. package/dist/{chunk-JX55DU6Y.js → chunk-OARJESSZ.js} +1 -1
  13. package/dist/{chunk-6LSN7HSJ.js → chunk-V36ICGOY.js} +14 -1
  14. package/dist/{chunk-TBXG53FV.js → chunk-WCON5BAI.js} +1 -1
  15. package/dist/{chunk-YPJW7Z5M.js → chunk-WFOWHPBC.js} +1 -1
  16. package/dist/{chunk-4NAQ5HL4.js → chunk-WYYQXVHC.js} +1 -1
  17. package/dist/{chunk-E2J3UBBK.js → chunk-YZUDL4GW.js} +1 -1
  18. package/dist/chunk-ZMGXHLYP.js +17 -0
  19. package/dist/{claudecode-SSYLLUXX.js → claudecode-OC7VHCF6.js} +3 -3
  20. package/dist/{cline-5EUGKNZ6.js → cline-A4KFSAQE.js} +3 -3
  21. package/dist/{codexcli-IGM2ADYK.js → codexcli-YP3X7FWB.js} +3 -3
  22. package/dist/{copilot-HSQO7ZCJ.js → copilot-3LIMXQ7O.js} +2 -2
  23. package/dist/{cursor-ZB3XNGBK.js → cursor-QV72CDJC.js} +3 -3
  24. package/dist/{geminicli-FNRKH5GX.js → geminicli-XPSJJS65.js} +3 -3
  25. package/dist/index.cjs +2048 -492
  26. package/dist/index.js +2006 -498
  27. package/dist/{junie-3YGOSOGF.js → junie-265XIW43.js} +3 -3
  28. package/dist/{kiro-B6WZNLY4.js → kiro-6OHFHN5X.js} +2 -2
  29. package/dist/{opencode-SZETJ62M.js → opencode-C5QAYVJ5.js} +2 -2
  30. package/dist/qwencode-5RR24UW6.js +10 -0
  31. package/dist/{roo-KLTWVAKE.js → roo-5IXVBUHD.js} +3 -3
  32. package/dist/{windsurf-IZEKUAID.js → windsurf-DVQUJJJ5.js} +3 -3
  33. package/package.json +2 -1
package/dist/index.cjs CHANGED
@@ -35,12 +35,13 @@ function isToolTarget(target) {
35
35
  if (!target) return false;
36
36
  return ALL_TOOL_TARGETS.some((validTarget) => validTarget === target);
37
37
  }
38
- var import_mini2, ALL_TOOL_TARGETS, ToolTargetSchema, ToolTargetsSchema, WildcardTargetSchema, RulesyncTargetsSchema;
38
+ var import_mini, ALL_TOOL_TARGETS, ToolTargetSchema, ToolTargetsSchema, WildcardTargetSchema, RulesyncTargetsSchema;
39
39
  var init_tool_targets = __esm({
40
40
  "src/types/tool-targets.ts"() {
41
41
  "use strict";
42
- import_mini2 = require("zod/mini");
42
+ import_mini = require("zod/mini");
43
43
  ALL_TOOL_TARGETS = [
44
+ "agentsmd",
44
45
  "amazonqcli",
45
46
  "augmentcode",
46
47
  "augmentcode-legacy",
@@ -50,16 +51,17 @@ var init_tool_targets = __esm({
50
51
  "claudecode",
51
52
  "codexcli",
52
53
  "opencode",
54
+ "qwencode",
53
55
  "roo",
54
56
  "geminicli",
55
57
  "kiro",
56
58
  "junie",
57
59
  "windsurf"
58
60
  ];
59
- ToolTargetSchema = import_mini2.z.enum(ALL_TOOL_TARGETS);
60
- ToolTargetsSchema = import_mini2.z.array(ToolTargetSchema);
61
- WildcardTargetSchema = import_mini2.z.tuple([import_mini2.z.literal("*")]);
62
- RulesyncTargetsSchema = import_mini2.z.union([ToolTargetsSchema, WildcardTargetSchema]);
61
+ ToolTargetSchema = import_mini.z.enum(ALL_TOOL_TARGETS);
62
+ ToolTargetsSchema = import_mini.z.array(ToolTargetSchema);
63
+ WildcardTargetSchema = import_mini.z.tuple([import_mini.z.literal("*")]);
64
+ RulesyncTargetsSchema = import_mini.z.union([ToolTargetsSchema, WildcardTargetSchema]);
63
65
  }
64
66
  });
65
67
 
@@ -161,8 +163,8 @@ async function findRuleFiles(aiRulesDir) {
161
163
  newLocationFiles.map((file) => file.split("/").pop()?.replace(/\.md$/, ""))
162
164
  );
163
165
  const filteredLegacyFiles = legacyLocationFiles.filter((file) => {
164
- const basename6 = file.split("/").pop()?.replace(/\.md$/, "");
165
- return !newLocationBasenames.has(basename6);
166
+ const basename7 = file.split("/").pop()?.replace(/\.md$/, "");
167
+ return !newLocationBasenames.has(basename7);
166
168
  });
167
169
  return [...newLocationFiles, ...filteredLegacyFiles];
168
170
  }
@@ -796,6 +798,19 @@ var init_shared_factory = __esm({
796
798
  $schema: SCHEMA_URLS.OPENCODE,
797
799
  mcp: servers
798
800
  })
801
+ },
802
+ qwencode: {
803
+ target: "qwencode",
804
+ configPaths: [".qwen/settings.json"],
805
+ serverTransform: (server) => {
806
+ const { targets: _, ...serverConfig } = server;
807
+ const qwenServer = { ...serverConfig };
808
+ if (server.env) {
809
+ qwenServer.env = server.env;
810
+ }
811
+ return qwenServer;
812
+ },
813
+ configWrapper: configWrappers.mcpServers
799
814
  }
800
815
  };
801
816
  cursorMcpGenerator = createMcpGenerator("cursor");
@@ -1212,6 +1227,25 @@ var init_kiro = __esm({
1212
1227
  }
1213
1228
  });
1214
1229
 
1230
+ // src/generators/mcp/qwencode.ts
1231
+ var qwencode_exports = {};
1232
+ __export(qwencode_exports, {
1233
+ generateQwenCodeMcp: () => generateQwenCodeMcp,
1234
+ generateQwenCodeMcpConfiguration: () => generateQwenCodeMcpConfiguration
1235
+ });
1236
+ function generateQwenCodeMcp(config) {
1237
+ return generateMcpFromRegistry("qwencode", config);
1238
+ }
1239
+ function generateQwenCodeMcpConfiguration(mcpServers, baseDir = "") {
1240
+ return generateMcpConfigurationFilesFromRegistry("qwencode", mcpServers, baseDir);
1241
+ }
1242
+ var init_qwencode = __esm({
1243
+ "src/generators/mcp/qwencode.ts"() {
1244
+ "use strict";
1245
+ init_shared_factory();
1246
+ }
1247
+ });
1248
+
1215
1249
  // src/generators/mcp/roo.ts
1216
1250
  var roo_exports = {};
1217
1251
  __export(roo_exports, {
@@ -1272,6 +1306,75 @@ var init_opencode = __esm({
1272
1306
  // src/cli/index.ts
1273
1307
  var import_commander = require("commander");
1274
1308
 
1309
+ // src/types/config-options.ts
1310
+ var import_mini2 = require("zod/mini");
1311
+ init_tool_targets();
1312
+ var FEATURE_TYPES = ["rules", "commands", "mcp", "ignore", "subagents"];
1313
+ var FeatureTypeSchema = import_mini2.z.enum(FEATURE_TYPES);
1314
+ var FeaturesSchema = import_mini2.z.union([import_mini2.z.array(FeatureTypeSchema), import_mini2.z.literal("*")]);
1315
+ var OutputPathsSchema = import_mini2.z.object({
1316
+ agentsmd: import_mini2.z.optional(import_mini2.z.string()),
1317
+ amazonqcli: import_mini2.z.optional(import_mini2.z.string()),
1318
+ augmentcode: import_mini2.z.optional(import_mini2.z.string()),
1319
+ "augmentcode-legacy": import_mini2.z.optional(import_mini2.z.string()),
1320
+ copilot: import_mini2.z.optional(import_mini2.z.string()),
1321
+ cursor: import_mini2.z.optional(import_mini2.z.string()),
1322
+ cline: import_mini2.z.optional(import_mini2.z.string()),
1323
+ claudecode: import_mini2.z.optional(import_mini2.z.string()),
1324
+ codexcli: import_mini2.z.optional(import_mini2.z.string()),
1325
+ opencode: import_mini2.z.optional(import_mini2.z.string()),
1326
+ qwencode: import_mini2.z.optional(import_mini2.z.string()),
1327
+ roo: import_mini2.z.optional(import_mini2.z.string()),
1328
+ geminicli: import_mini2.z.optional(import_mini2.z.string()),
1329
+ kiro: import_mini2.z.optional(import_mini2.z.string()),
1330
+ junie: import_mini2.z.optional(import_mini2.z.string()),
1331
+ windsurf: import_mini2.z.optional(import_mini2.z.string())
1332
+ });
1333
+ var ConfigOptionsSchema = import_mini2.z.object({
1334
+ aiRulesDir: import_mini2.z.optional(import_mini2.z.string()),
1335
+ outputPaths: import_mini2.z.optional(OutputPathsSchema),
1336
+ watchEnabled: import_mini2.z.optional(import_mini2.z.boolean()),
1337
+ defaultTargets: import_mini2.z.optional(ToolTargetsSchema),
1338
+ targets: import_mini2.z.optional(import_mini2.z.array(ToolTargetSchema)),
1339
+ exclude: import_mini2.z.optional(import_mini2.z.array(ToolTargetSchema)),
1340
+ features: import_mini2.z.optional(FeaturesSchema),
1341
+ verbose: import_mini2.z.optional(import_mini2.z.boolean()),
1342
+ delete: import_mini2.z.optional(import_mini2.z.boolean()),
1343
+ baseDir: import_mini2.z.optional(import_mini2.z.union([import_mini2.z.string(), import_mini2.z.array(import_mini2.z.string())])),
1344
+ legacy: import_mini2.z.optional(import_mini2.z.boolean()),
1345
+ watch: import_mini2.z.optional(
1346
+ import_mini2.z.object({
1347
+ enabled: import_mini2.z.optional(import_mini2.z.boolean()),
1348
+ interval: import_mini2.z.optional(import_mini2.z.number()),
1349
+ ignore: import_mini2.z.optional(import_mini2.z.array(import_mini2.z.string()))
1350
+ })
1351
+ )
1352
+ });
1353
+ var MergedConfigSchema = import_mini2.z.object({
1354
+ aiRulesDir: import_mini2.z.string(),
1355
+ outputPaths: import_mini2.z.record(ToolTargetSchema, import_mini2.z.string()),
1356
+ watchEnabled: import_mini2.z.boolean(),
1357
+ defaultTargets: ToolTargetsSchema,
1358
+ targets: import_mini2.z.optional(import_mini2.z.array(ToolTargetSchema)),
1359
+ exclude: import_mini2.z.optional(import_mini2.z.array(ToolTargetSchema)),
1360
+ features: import_mini2.z.optional(FeaturesSchema),
1361
+ verbose: import_mini2.z.optional(import_mini2.z.boolean()),
1362
+ delete: import_mini2.z.optional(import_mini2.z.boolean()),
1363
+ baseDir: import_mini2.z.optional(import_mini2.z.union([import_mini2.z.string(), import_mini2.z.array(import_mini2.z.string())])),
1364
+ configPath: import_mini2.z.optional(import_mini2.z.string()),
1365
+ legacy: import_mini2.z.optional(import_mini2.z.boolean()),
1366
+ watch: import_mini2.z.optional(
1367
+ import_mini2.z.object({
1368
+ enabled: import_mini2.z.optional(import_mini2.z.boolean()),
1369
+ interval: import_mini2.z.optional(import_mini2.z.number()),
1370
+ ignore: import_mini2.z.optional(import_mini2.z.array(import_mini2.z.string()))
1371
+ })
1372
+ )
1373
+ });
1374
+
1375
+ // src/cli/index.ts
1376
+ init_logger();
1377
+
1275
1378
  // src/cli/commands/add.ts
1276
1379
  var import_promises = require("fs/promises");
1277
1380
  var path = __toESM(require("path"), 1);
@@ -1281,101 +1384,42 @@ var import_c12 = require("c12");
1281
1384
  var import_core = require("zod/v4/core");
1282
1385
 
1283
1386
  // src/types/claudecode.ts
1284
- var import_mini = require("zod/mini");
1285
- var ClaudeSettingsSchema = import_mini.z.looseObject({
1286
- permissions: import_mini.z._default(
1287
- import_mini.z.looseObject({
1288
- deny: import_mini.z._default(import_mini.z.array(import_mini.z.string()), [])
1387
+ var import_mini3 = require("zod/mini");
1388
+ var ClaudeSettingsSchema = import_mini3.z.looseObject({
1389
+ permissions: import_mini3.z._default(
1390
+ import_mini3.z.looseObject({
1391
+ deny: import_mini3.z._default(import_mini3.z.array(import_mini3.z.string()), [])
1289
1392
  }),
1290
1393
  { deny: [] }
1291
1394
  )
1292
1395
  });
1293
1396
 
1294
1397
  // src/types/shared.ts
1295
- var import_mini3 = require("zod/mini");
1398
+ var import_mini4 = require("zod/mini");
1296
1399
  init_tool_targets();
1297
- var OutputSchema = import_mini3.z.object({
1400
+ var OutputSchema = import_mini4.z.object({
1298
1401
  tool: ToolTargetSchema,
1299
- filepath: import_mini3.z.string(),
1300
- content: import_mini3.z.string()
1402
+ filepath: import_mini4.z.string(),
1403
+ content: import_mini4.z.string()
1301
1404
  });
1302
- var BaseFrontmatterSchema = import_mini3.z.object({
1303
- description: import_mini3.z.optional(import_mini3.z.string())
1405
+ var BaseFrontmatterSchema = import_mini4.z.object({
1406
+ description: import_mini4.z.optional(import_mini4.z.string())
1304
1407
  });
1305
1408
 
1306
1409
  // src/types/commands.ts
1307
1410
  var CommandFrontmatterSchema = BaseFrontmatterSchema;
1308
1411
 
1309
1412
  // src/types/config.ts
1310
- var import_mini4 = require("zod/mini");
1311
- init_tool_targets();
1312
- var ConfigSchema = import_mini4.z.object({
1313
- aiRulesDir: import_mini4.z.string(),
1314
- outputPaths: import_mini4.z.record(ToolTargetSchema, import_mini4.z.string()),
1315
- watchEnabled: import_mini4.z.boolean(),
1316
- defaultTargets: ToolTargetsSchema,
1317
- claudecodeCommands: import_mini4.z.optional(import_mini4.z.string()),
1318
- geminicliCommands: import_mini4.z.optional(import_mini4.z.string()),
1319
- legacy: import_mini4.z.optional(import_mini4.z.boolean())
1320
- });
1321
-
1322
- // src/types/config-options.ts
1323
1413
  var import_mini5 = require("zod/mini");
1324
1414
  init_tool_targets();
1325
- var OutputPathsSchema = import_mini5.z.object({
1326
- amazonqcli: import_mini5.z.optional(import_mini5.z.string()),
1327
- augmentcode: import_mini5.z.optional(import_mini5.z.string()),
1328
- "augmentcode-legacy": import_mini5.z.optional(import_mini5.z.string()),
1329
- copilot: import_mini5.z.optional(import_mini5.z.string()),
1330
- cursor: import_mini5.z.optional(import_mini5.z.string()),
1331
- cline: import_mini5.z.optional(import_mini5.z.string()),
1332
- claudecode: import_mini5.z.optional(import_mini5.z.string()),
1333
- codexcli: import_mini5.z.optional(import_mini5.z.string()),
1334
- opencode: import_mini5.z.optional(import_mini5.z.string()),
1335
- roo: import_mini5.z.optional(import_mini5.z.string()),
1336
- geminicli: import_mini5.z.optional(import_mini5.z.string()),
1337
- kiro: import_mini5.z.optional(import_mini5.z.string()),
1338
- junie: import_mini5.z.optional(import_mini5.z.string()),
1339
- windsurf: import_mini5.z.optional(import_mini5.z.string())
1340
- });
1341
- var ConfigOptionsSchema = import_mini5.z.object({
1342
- aiRulesDir: import_mini5.z.optional(import_mini5.z.string()),
1343
- outputPaths: import_mini5.z.optional(OutputPathsSchema),
1344
- watchEnabled: import_mini5.z.optional(import_mini5.z.boolean()),
1345
- defaultTargets: import_mini5.z.optional(ToolTargetsSchema),
1346
- targets: import_mini5.z.optional(import_mini5.z.array(ToolTargetSchema)),
1347
- exclude: import_mini5.z.optional(import_mini5.z.array(ToolTargetSchema)),
1348
- verbose: import_mini5.z.optional(import_mini5.z.boolean()),
1349
- delete: import_mini5.z.optional(import_mini5.z.boolean()),
1350
- baseDir: import_mini5.z.optional(import_mini5.z.union([import_mini5.z.string(), import_mini5.z.array(import_mini5.z.string())])),
1351
- legacy: import_mini5.z.optional(import_mini5.z.boolean()),
1352
- watch: import_mini5.z.optional(
1353
- import_mini5.z.object({
1354
- enabled: import_mini5.z.optional(import_mini5.z.boolean()),
1355
- interval: import_mini5.z.optional(import_mini5.z.number()),
1356
- ignore: import_mini5.z.optional(import_mini5.z.array(import_mini5.z.string()))
1357
- })
1358
- )
1359
- });
1360
- var MergedConfigSchema = import_mini5.z.object({
1415
+ var ConfigSchema = import_mini5.z.object({
1361
1416
  aiRulesDir: import_mini5.z.string(),
1362
1417
  outputPaths: import_mini5.z.record(ToolTargetSchema, import_mini5.z.string()),
1363
1418
  watchEnabled: import_mini5.z.boolean(),
1364
1419
  defaultTargets: ToolTargetsSchema,
1365
- targets: import_mini5.z.optional(import_mini5.z.array(ToolTargetSchema)),
1366
- exclude: import_mini5.z.optional(import_mini5.z.array(ToolTargetSchema)),
1367
- verbose: import_mini5.z.optional(import_mini5.z.boolean()),
1368
- delete: import_mini5.z.optional(import_mini5.z.boolean()),
1369
- baseDir: import_mini5.z.optional(import_mini5.z.union([import_mini5.z.string(), import_mini5.z.array(import_mini5.z.string())])),
1370
- configPath: import_mini5.z.optional(import_mini5.z.string()),
1371
- legacy: import_mini5.z.optional(import_mini5.z.boolean()),
1372
- watch: import_mini5.z.optional(
1373
- import_mini5.z.object({
1374
- enabled: import_mini5.z.optional(import_mini5.z.boolean()),
1375
- interval: import_mini5.z.optional(import_mini5.z.number()),
1376
- ignore: import_mini5.z.optional(import_mini5.z.array(import_mini5.z.string()))
1377
- })
1378
- )
1420
+ claudecodeCommands: import_mini5.z.optional(import_mini5.z.string()),
1421
+ geminicliCommands: import_mini5.z.optional(import_mini5.z.string()),
1422
+ legacy: import_mini5.z.optional(import_mini5.z.boolean())
1379
1423
  });
1380
1424
 
1381
1425
  // src/types/mcp.ts
@@ -1439,6 +1483,7 @@ function getDefaultConfig() {
1439
1483
  return {
1440
1484
  aiRulesDir: ".rulesync",
1441
1485
  outputPaths: {
1486
+ agentsmd: ".agents/memories",
1442
1487
  amazonqcli: ".amazonq/rules",
1443
1488
  augmentcode: ".",
1444
1489
  "augmentcode-legacy": ".",
@@ -1448,6 +1493,7 @@ function getDefaultConfig() {
1448
1493
  claudecode: ".",
1449
1494
  codexcli: ".",
1450
1495
  opencode: ".",
1496
+ qwencode: ".qwen/memories",
1451
1497
  roo: ".roo/rules",
1452
1498
  geminicli: ".gemini/memories",
1453
1499
  kiro: ".kiro/steering",
@@ -1555,6 +1601,11 @@ function generateMinimalConfig(options) {
1555
1601
  if (comma) lines[lines.length - 1] += comma;
1556
1602
  lines.push(` "exclude": ${JSON.stringify(options.exclude)}`);
1557
1603
  }
1604
+ if (options.features) {
1605
+ const comma = lines.length > 1 ? "," : "";
1606
+ if (comma) lines[lines.length - 1] += comma;
1607
+ lines.push(` "features": ${JSON.stringify(options.features)}`);
1608
+ }
1558
1609
  if (options.aiRulesDir) {
1559
1610
  const comma = lines.length > 1 ? "," : "";
1560
1611
  if (comma) lines[lines.length - 1] += comma;
@@ -1595,6 +1646,7 @@ function generateMinimalConfig(options) {
1595
1646
  function generateSampleConfig(options) {
1596
1647
  const targets = options?.targets || ALL_TOOL_TARGETS;
1597
1648
  const excludeValue = options?.exclude ? JSON.stringify(options.exclude) : null;
1649
+ const featuresValue = options?.features || ["rules", "commands", "mcp", "ignore", "subagents"];
1598
1650
  const aiRulesDir = options?.aiRulesDir || null;
1599
1651
  const baseDir = options?.baseDir || null;
1600
1652
  const deleteFlag = options?.delete || false;
@@ -1606,6 +1658,10 @@ function generateSampleConfig(options) {
1606
1658
 
1607
1659
  // Tools to exclude from generation (overrides targets)
1608
1660
  ${excludeValue ? `"exclude": ${excludeValue},` : '// "exclude": ["roo"],'}
1661
+
1662
+ // Features to generate (rules, commands, mcp, ignore, subagents)
1663
+ // Use "*" to generate all features, or specify an array of features
1664
+ "features": ${JSON.stringify(featuresValue)},
1609
1665
  ${aiRulesDir ? `
1610
1666
  // Directory containing AI rule files
1611
1667
  "aiRulesDir": "${aiRulesDir}",` : ""}
@@ -1636,23 +1692,6 @@ function generateSampleConfig(options) {
1636
1692
  }
1637
1693
  `;
1638
1694
  }
1639
- function mergeWithCliOptions(config, cliOptions) {
1640
- const merged = { ...config };
1641
- if (cliOptions.verbose !== void 0) {
1642
- merged.verbose = cliOptions.verbose;
1643
- }
1644
- if (cliOptions.delete !== void 0) {
1645
- merged.delete = cliOptions.delete;
1646
- }
1647
- if (cliOptions.baseDirs && cliOptions.baseDirs.length > 0) {
1648
- merged.baseDir = cliOptions.baseDirs;
1649
- }
1650
- if (cliOptions.tools && cliOptions.tools.length > 0) {
1651
- merged.defaultTargets = cliOptions.tools;
1652
- merged.exclude = void 0;
1653
- }
1654
- return merged;
1655
- }
1656
1695
 
1657
1696
  // src/cli/commands/add.ts
1658
1697
  init_logger();
@@ -1761,6 +1800,11 @@ Default Targets: ${config.defaultTargets.join(", ")}`);
1761
1800
  if (config.exclude && config.exclude.length > 0) {
1762
1801
  logger.log(`Excluded Targets: ${config.exclude.join(", ")}`);
1763
1802
  }
1803
+ if (config.features) {
1804
+ const featuresDisplay = config.features === "*" ? "*" : Array.isArray(config.features) ? config.features.join(", ") : String(config.features);
1805
+ logger.log(`
1806
+ Features: ${featuresDisplay}`);
1807
+ }
1764
1808
  logger.log("\nOutput Paths:");
1765
1809
  for (const [tool, outputPath] of Object.entries(config.outputPaths)) {
1766
1810
  logger.log(` ${tool}: ${outputPath}`);
@@ -1891,6 +1935,10 @@ const config: ConfigOptions = {
1891
1935
  // Available: ${ALL_TOOL_TARGETS.join(", ")}
1892
1936
  targets: ${JSON.stringify(ALL_TOOL_TARGETS)},
1893
1937
 
1938
+ // Features to generate (rules, commands, mcp, ignore, subagents)
1939
+ // Use "*" to generate all features, or specify an array of features
1940
+ features: ["rules", "commands", "mcp", "ignore", "subagents"],
1941
+
1894
1942
  // Custom output paths for specific tools
1895
1943
  // outputPaths: {
1896
1944
  // copilot: ".github/copilot-instructions.md",
@@ -1912,6 +1960,9 @@ export default config;`;
1912
1960
  if (options.exclude) {
1913
1961
  configLines.push(` exclude: ${JSON.stringify(options.exclude)}`);
1914
1962
  }
1963
+ if (options.features) {
1964
+ configLines.push(` features: ${JSON.stringify(options.features)}`);
1965
+ }
1915
1966
  if (options.aiRulesDir) {
1916
1967
  configLines.push(` aiRulesDir: "${options.aiRulesDir}"`);
1917
1968
  }
@@ -1944,7 +1995,7 @@ export default config;
1944
1995
  }
1945
1996
 
1946
1997
  // src/cli/commands/generate.ts
1947
- var import_node_path13 = require("path");
1998
+ var import_node_path18 = require("path");
1948
1999
 
1949
2000
  // src/core/command-generator.ts
1950
2001
  var import_node_path5 = require("path");
@@ -2213,8 +2264,475 @@ async function generateCommands(projectRoot, baseDir, targets) {
2213
2264
  return outputs;
2214
2265
  }
2215
2266
 
2216
- // src/generators/ignore/shared-factory.ts
2267
+ // src/utils/feature-validator.ts
2268
+ function isFeatureType(value) {
2269
+ return FEATURE_TYPES.some((type) => type === value);
2270
+ }
2271
+ function validateFeatures(features) {
2272
+ if (features === void 0) {
2273
+ return "*";
2274
+ }
2275
+ if (features === "*") {
2276
+ return "*";
2277
+ }
2278
+ if (!Array.isArray(features)) {
2279
+ throw new Error('Features must be an array of feature names or "*"');
2280
+ }
2281
+ if (features.length === 0) {
2282
+ throw new Error('Features array cannot be empty. Use "*" to include all features');
2283
+ }
2284
+ const validFeatures = [];
2285
+ const invalidFeatures = [];
2286
+ for (const feature of features) {
2287
+ if (isFeatureType(feature)) {
2288
+ validFeatures.push(feature);
2289
+ } else {
2290
+ invalidFeatures.push(feature);
2291
+ }
2292
+ }
2293
+ if (invalidFeatures.length > 0) {
2294
+ throw new Error(
2295
+ `Invalid feature types: ${invalidFeatures.join(", ")}. Valid features are: ${FEATURE_TYPES.join(", ")}`
2296
+ );
2297
+ }
2298
+ return [...new Set(validFeatures)];
2299
+ }
2300
+ function expandWildcard() {
2301
+ return [...FEATURE_TYPES];
2302
+ }
2303
+ function normalizeFeatures(features) {
2304
+ if (features === "*" || features === void 0) {
2305
+ return expandWildcard();
2306
+ }
2307
+ return features;
2308
+ }
2309
+
2310
+ // src/core/config/cli-parser.ts
2311
+ var CliParser = class {
2312
+ /**
2313
+ * CLI引数を解析する
2314
+ * @param cliOptions - 生のCLIオプション
2315
+ * @returns 解析されたCliOptions
2316
+ */
2317
+ parse(cliOptions) {
2318
+ const parsed = {};
2319
+ if (cliOptions.tools && Array.isArray(cliOptions.tools) && cliOptions.tools.length > 0) {
2320
+ parsed.tools = cliOptions.tools;
2321
+ }
2322
+ if (cliOptions.features !== void 0) {
2323
+ try {
2324
+ parsed.features = validateFeatures(cliOptions.features);
2325
+ } catch (error) {
2326
+ throw new Error(
2327
+ `Invalid features: ${error instanceof Error ? error.message : String(error)}`
2328
+ );
2329
+ }
2330
+ }
2331
+ if (typeof cliOptions.verbose === "boolean") {
2332
+ parsed.verbose = cliOptions.verbose;
2333
+ }
2334
+ if (typeof cliOptions.delete === "boolean") {
2335
+ parsed.delete = cliOptions.delete;
2336
+ }
2337
+ if (cliOptions.baseDirs && Array.isArray(cliOptions.baseDirs) && cliOptions.baseDirs.length > 0) {
2338
+ parsed.baseDirs = cliOptions.baseDirs;
2339
+ }
2340
+ if (typeof cliOptions.config === "string") {
2341
+ parsed.config = cliOptions.config;
2342
+ }
2343
+ if (typeof cliOptions.noConfig === "boolean") {
2344
+ parsed.noConfig = cliOptions.noConfig;
2345
+ }
2346
+ return parsed;
2347
+ }
2348
+ /**
2349
+ * CLI引数が有効かどうかを検証する
2350
+ * @param cliOptions - 検証するCLIオプション
2351
+ * @returns 検証結果
2352
+ */
2353
+ validate(cliOptions) {
2354
+ const errors = [];
2355
+ if (cliOptions.config && cliOptions.noConfig) {
2356
+ errors.push("--config and --no-config cannot be used together");
2357
+ }
2358
+ if (cliOptions.baseDirs && cliOptions.baseDirs.length === 0) {
2359
+ errors.push("--base-dirs cannot be empty");
2360
+ }
2361
+ if (cliOptions.tools && cliOptions.tools.length === 0) {
2362
+ errors.push("--tools cannot be empty");
2363
+ }
2364
+ return {
2365
+ valid: errors.length === 0,
2366
+ errors
2367
+ };
2368
+ }
2369
+ };
2370
+
2371
+ // src/core/config/config-file-loader.ts
2217
2372
  var import_node_path6 = require("path");
2373
+ var ConfigFileLoader = class {
2374
+ /**
2375
+ * 設定ファイルを読み込む
2376
+ * @param options - 設定ファイルの読み込みオプション
2377
+ * @returns 設定ファイルの読み込み結果
2378
+ */
2379
+ async load(options = {}) {
2380
+ try {
2381
+ return await loadConfig(options);
2382
+ } catch (error) {
2383
+ throw new Error(
2384
+ `Failed to load configuration file: ${error instanceof Error ? error.message : String(error)}`
2385
+ );
2386
+ }
2387
+ }
2388
+ /**
2389
+ * 設定ファイルが存在するかどうかを確認する
2390
+ * @param configPath - 設定ファイルのパス
2391
+ * @returns ファイルが存在するかどうか
2392
+ */
2393
+ async exists(configPath) {
2394
+ try {
2395
+ const { access } = await import("fs/promises");
2396
+ await access(configPath);
2397
+ return true;
2398
+ } catch {
2399
+ return false;
2400
+ }
2401
+ }
2402
+ /**
2403
+ * 設定ファイルのパスを解決する
2404
+ * @param configPath - 指定された設定ファイルのパス
2405
+ * @param workingDirectory - 作業ディレクトリ
2406
+ * @returns 解決された設定ファイルのパス
2407
+ */
2408
+ resolvePath(configPath, workingDirectory) {
2409
+ if (!configPath) {
2410
+ return void 0;
2411
+ }
2412
+ if ((0, import_node_path6.isAbsolute)(configPath)) {
2413
+ return configPath;
2414
+ }
2415
+ const baseDir = workingDirectory || process.cwd();
2416
+ return (0, import_node_path6.resolve)(baseDir, configPath);
2417
+ }
2418
+ };
2419
+
2420
+ // src/core/config/config-merger.ts
2421
+ var ConfigMerger = class {
2422
+ /**
2423
+ * 設定ファイルとCLI引数をマージする
2424
+ * @param fileConfig - 設定ファイルから読み込まれた設定
2425
+ * @param cliOptions - CLI引数から解析された設定
2426
+ * @returns マージされた設定
2427
+ */
2428
+ merge(fileConfig, cliOptions) {
2429
+ const merged = { ...fileConfig };
2430
+ if (cliOptions.verbose !== void 0) {
2431
+ merged.verbose = cliOptions.verbose;
2432
+ }
2433
+ if (cliOptions.delete !== void 0) {
2434
+ merged.delete = cliOptions.delete;
2435
+ }
2436
+ if (cliOptions.baseDirs && cliOptions.baseDirs.length > 0) {
2437
+ merged.baseDir = cliOptions.baseDirs;
2438
+ }
2439
+ if (cliOptions.tools && cliOptions.tools.length > 0) {
2440
+ merged.defaultTargets = cliOptions.tools;
2441
+ merged.exclude = void 0;
2442
+ }
2443
+ if (cliOptions.features !== void 0) {
2444
+ merged.features = cliOptions.features;
2445
+ }
2446
+ return merged;
2447
+ }
2448
+ /**
2449
+ * 設定値のマージメタデータを生成する
2450
+ * @param fileConfig - 設定ファイルの設定
2451
+ * @param cliOptions - CLI引数の設定
2452
+ * @param merged - マージされた設定
2453
+ * @returns マージメタデータ
2454
+ */
2455
+ generateMetadata(fileConfig, cliOptions, merged) {
2456
+ const metadata = {};
2457
+ if (merged.verbose !== void 0) {
2458
+ metadata.verbose = {
2459
+ source: cliOptions.verbose !== void 0 ? "cli_args" /* CLI_ARGS */ : "config_file" /* CONFIG_FILE */,
2460
+ value: merged.verbose
2461
+ };
2462
+ }
2463
+ if (merged.delete !== void 0) {
2464
+ metadata.delete = {
2465
+ source: cliOptions.delete !== void 0 ? "cli_args" /* CLI_ARGS */ : "config_file" /* CONFIG_FILE */,
2466
+ value: merged.delete
2467
+ };
2468
+ }
2469
+ if (merged.baseDir !== void 0) {
2470
+ metadata.baseDir = {
2471
+ source: cliOptions.baseDirs ? "cli_args" /* CLI_ARGS */ : "config_file" /* CONFIG_FILE */,
2472
+ value: merged.baseDir
2473
+ };
2474
+ }
2475
+ metadata.defaultTargets = {
2476
+ source: cliOptions.tools ? "cli_args" /* CLI_ARGS */ : "config_file" /* CONFIG_FILE */,
2477
+ value: merged.defaultTargets
2478
+ };
2479
+ if (merged.exclude !== void 0) {
2480
+ metadata.exclude = {
2481
+ source: "config_file" /* CONFIG_FILE */,
2482
+ // excludeはCLIから直接指定されない
2483
+ value: merged.exclude
2484
+ };
2485
+ }
2486
+ if (merged.features !== void 0) {
2487
+ metadata.features = {
2488
+ source: cliOptions.features !== void 0 ? "cli_args" /* CLI_ARGS */ : "config_file" /* CONFIG_FILE */,
2489
+ value: merged.features
2490
+ };
2491
+ }
2492
+ return metadata;
2493
+ }
2494
+ /**
2495
+ * マージされた設定を検証する
2496
+ * @param config - マージされた設定
2497
+ * @returns 検証結果
2498
+ */
2499
+ validate(config) {
2500
+ const errors = [];
2501
+ if (!config.defaultTargets || config.defaultTargets.length === 0) {
2502
+ errors.push("At least one tool must be specified in targets or CLI arguments");
2503
+ }
2504
+ if (!config.aiRulesDir) {
2505
+ errors.push("aiRulesDir must be specified");
2506
+ }
2507
+ if (!config.outputPaths) {
2508
+ errors.push("outputPaths must be specified");
2509
+ }
2510
+ return {
2511
+ valid: errors.length === 0,
2512
+ errors
2513
+ };
2514
+ }
2515
+ };
2516
+
2517
+ // src/core/config/validators.ts
2518
+ var ConfigValidationError = class extends Error {
2519
+ constructor(message, errors) {
2520
+ super(message);
2521
+ this.errors = errors;
2522
+ this.name = "ConfigValidationError";
2523
+ }
2524
+ };
2525
+ var ConfigValidator = class {
2526
+ /**
2527
+ * CLI引数を検証する
2528
+ * @param cliOptions - 検証するCLI引数
2529
+ * @throws {ConfigValidationError} バリデーションエラーが発生した場合
2530
+ */
2531
+ validateCliOptions(cliOptions) {
2532
+ const errors = [];
2533
+ if (cliOptions.config && cliOptions.noConfig) {
2534
+ errors.push("--config and --no-config cannot be used together");
2535
+ }
2536
+ if (cliOptions.baseDirs && cliOptions.baseDirs.length === 0) {
2537
+ errors.push("--base-dirs cannot be empty");
2538
+ }
2539
+ if (cliOptions.tools && cliOptions.tools.length === 0) {
2540
+ errors.push("--tools cannot be empty");
2541
+ }
2542
+ if (cliOptions.baseDirs) {
2543
+ for (const [index, dir] of cliOptions.baseDirs.entries()) {
2544
+ if (typeof dir !== "string" || dir.trim() === "") {
2545
+ errors.push(`Base directory at index ${index} must be a non-empty string`);
2546
+ }
2547
+ }
2548
+ }
2549
+ if (errors.length > 0) {
2550
+ throw new ConfigValidationError("CLI options validation failed", errors);
2551
+ }
2552
+ }
2553
+ /**
2554
+ * マージされた設定を検証する
2555
+ * @param config - 検証する設定
2556
+ * @throws {ConfigValidationError} バリデーションエラーが発生した場合
2557
+ */
2558
+ validateMergedConfig(config) {
2559
+ const errors = [];
2560
+ if (!config.aiRulesDir || config.aiRulesDir.trim() === "") {
2561
+ errors.push("aiRulesDir must be specified and non-empty");
2562
+ }
2563
+ if (!config.outputPaths) {
2564
+ errors.push("outputPaths must be specified");
2565
+ }
2566
+ if (!config.defaultTargets || config.defaultTargets.length === 0) {
2567
+ errors.push("At least one tool must be specified in defaultTargets");
2568
+ }
2569
+ if (config.watchEnabled === void 0) {
2570
+ errors.push("watchEnabled must be specified");
2571
+ }
2572
+ if (config.baseDir !== void 0) {
2573
+ const dirs = Array.isArray(config.baseDir) ? config.baseDir : [config.baseDir];
2574
+ for (const [index, dir] of dirs.entries()) {
2575
+ if (typeof dir !== "string" || dir.trim() === "") {
2576
+ errors.push(`Base directory at index ${index} must be a non-empty string`);
2577
+ }
2578
+ }
2579
+ }
2580
+ if (config.exclude && config.defaultTargets) {
2581
+ const invalidExcludes = config.exclude.filter(
2582
+ (excludeTool) => !config.defaultTargets.includes(excludeTool)
2583
+ );
2584
+ if (invalidExcludes.length > 0) {
2585
+ errors.push(`Exclude contains tools not in defaultTargets: ${invalidExcludes.join(", ")}`);
2586
+ }
2587
+ }
2588
+ if (errors.length > 0) {
2589
+ throw new ConfigValidationError("Merged configuration validation failed", errors);
2590
+ }
2591
+ }
2592
+ /**
2593
+ * 設定ファイルのパスが有効かどうかを検証する
2594
+ * @param configPath - 設定ファイルのパス
2595
+ * @throws {ConfigValidationError} パスが無効な場合
2596
+ */
2597
+ validateConfigPath(configPath) {
2598
+ if (configPath !== void 0 && typeof configPath !== "string") {
2599
+ throw new ConfigValidationError("Configuration validation failed", [
2600
+ "Config path must be a string"
2601
+ ]);
2602
+ }
2603
+ if (typeof configPath === "string" && configPath.trim() === "") {
2604
+ throw new ConfigValidationError("Configuration validation failed", [
2605
+ "Config path cannot be empty"
2606
+ ]);
2607
+ }
2608
+ }
2609
+ /**
2610
+ * 作業ディレクトリが有効かどうかを検証する
2611
+ * @param workingDirectory - 作業ディレクトリのパス
2612
+ * @throws {ConfigValidationError} パスが無効な場合
2613
+ */
2614
+ validateWorkingDirectory(workingDirectory) {
2615
+ if (workingDirectory !== void 0 && typeof workingDirectory !== "string") {
2616
+ throw new ConfigValidationError("Configuration validation failed", [
2617
+ "Working directory must be a string"
2618
+ ]);
2619
+ }
2620
+ if (typeof workingDirectory === "string" && workingDirectory.trim() === "") {
2621
+ throw new ConfigValidationError("Configuration validation failed", [
2622
+ "Working directory cannot be empty"
2623
+ ]);
2624
+ }
2625
+ }
2626
+ };
2627
+
2628
+ // src/core/config/config-resolver.ts
2629
+ var ConfigResolver = class {
2630
+ cliParser;
2631
+ configLoader;
2632
+ merger;
2633
+ validator;
2634
+ constructor(cliParser, configLoader, merger, validator) {
2635
+ this.cliParser = cliParser || new CliParser();
2636
+ this.configLoader = configLoader || new ConfigFileLoader();
2637
+ this.merger = merger || new ConfigMerger();
2638
+ this.validator = validator || new ConfigValidator();
2639
+ }
2640
+ /**
2641
+ * 設定を解決してマージされた設定を返す
2642
+ * @param options - 設定解決のオプション
2643
+ * @returns 解決された設定と詳細情報
2644
+ */
2645
+ async resolve(options) {
2646
+ try {
2647
+ this.validator.validateWorkingDirectory(options.workingDirectory);
2648
+ const parsedCliOptions = this.cliParser.parse(options.cliOptions);
2649
+ this.validator.validateCliOptions(parsedCliOptions);
2650
+ const configLoaderOptions = {
2651
+ cwd: options.workingDirectory || process.cwd(),
2652
+ ...parsedCliOptions.config && { configPath: parsedCliOptions.config },
2653
+ ...parsedCliOptions.noConfig && { noConfig: parsedCliOptions.noConfig }
2654
+ };
2655
+ this.validator.validateConfigPath(configLoaderOptions.configPath);
2656
+ const configResult = await this.configLoader.load(configLoaderOptions);
2657
+ const mergedConfig = this.merger.merge(configResult.config, parsedCliOptions);
2658
+ this.validator.validateMergedConfig(mergedConfig);
2659
+ const metadata = this.merger.generateMetadata(
2660
+ configResult.config,
2661
+ parsedCliOptions,
2662
+ mergedConfig
2663
+ );
2664
+ const configSource = this.determineConfigSource(configResult.isEmpty, parsedCliOptions);
2665
+ return {
2666
+ value: mergedConfig,
2667
+ source: configSource,
2668
+ metadata
2669
+ };
2670
+ } catch (error) {
2671
+ if (error instanceof ConfigValidationError) {
2672
+ throw error;
2673
+ }
2674
+ throw new Error(
2675
+ `Failed to resolve configuration: ${error instanceof Error ? error.message : String(error)}`
2676
+ );
2677
+ }
2678
+ }
2679
+ /**
2680
+ * 設定の主要なソースを判定する
2681
+ * @param configFileEmpty - 設定ファイルが空かどうか
2682
+ * @param cliOptions - CLI引数
2683
+ * @returns 主要な設定ソース
2684
+ */
2685
+ determineConfigSource(configFileEmpty, cliOptions) {
2686
+ if (cliOptions.tools || cliOptions.verbose !== void 0 || cliOptions.delete !== void 0) {
2687
+ return "cli_args" /* CLI_ARGS */;
2688
+ }
2689
+ if (!configFileEmpty) {
2690
+ return "config_file" /* CONFIG_FILE */;
2691
+ }
2692
+ return "default" /* DEFAULT */;
2693
+ }
2694
+ /**
2695
+ * 設定解決の詳細情報を取得する(デバッグ用)
2696
+ * @param options - 設定解決のオプション
2697
+ * @returns 詳細な解決情報
2698
+ */
2699
+ async resolveWithDetails(options) {
2700
+ const parsedCliOptions = this.cliParser.parse(options.cliOptions);
2701
+ const configLoaderOptions = {
2702
+ cwd: options.workingDirectory || process.cwd(),
2703
+ ...parsedCliOptions.config && { configPath: parsedCliOptions.config },
2704
+ ...parsedCliOptions.noConfig && { noConfig: parsedCliOptions.noConfig }
2705
+ };
2706
+ const configFileResult = await this.configLoader.load(configLoaderOptions);
2707
+ try {
2708
+ const result = await this.resolve(options);
2709
+ return {
2710
+ result,
2711
+ details: {
2712
+ parsedCliOptions,
2713
+ configFileResult
2714
+ }
2715
+ };
2716
+ } catch (error) {
2717
+ const validationErrors = error instanceof ConfigValidationError ? error.errors : [error instanceof Error ? error.message : String(error)];
2718
+ const partialConfig = this.merger.merge(configFileResult.config, parsedCliOptions);
2719
+ return {
2720
+ result: {
2721
+ value: partialConfig,
2722
+ source: this.determineConfigSource(configFileResult.isEmpty, parsedCliOptions)
2723
+ },
2724
+ details: {
2725
+ parsedCliOptions,
2726
+ configFileResult,
2727
+ validationErrors
2728
+ }
2729
+ };
2730
+ }
2731
+ }
2732
+ };
2733
+
2734
+ // src/generators/ignore/shared-factory.ts
2735
+ var import_node_path7 = require("path");
2218
2736
 
2219
2737
  // src/generators/ignore/shared-helpers.ts
2220
2738
  function extractIgnorePatternsFromRules(rules) {
@@ -2337,7 +2855,7 @@ function generateIgnoreFile(rules, config, ignoreConfig, baseDir) {
2337
2855
  const outputs = [];
2338
2856
  const content = generateIgnoreContent(rules, ignoreConfig);
2339
2857
  const outputPath = baseDir || process.cwd();
2340
- const filepath = (0, import_node_path6.join)(outputPath, ignoreConfig.filename);
2858
+ const filepath = (0, import_node_path7.join)(outputPath, ignoreConfig.filename);
2341
2859
  outputs.push({
2342
2860
  tool: ignoreConfig.tool,
2343
2861
  filepath,
@@ -2919,17 +3437,114 @@ async function generateKiroIgnoreFiles(rules, config, baseDir) {
2919
3437
  return generateIgnoreFile(rules, config, ignoreConfigs.kiro, baseDir);
2920
3438
  }
2921
3439
 
3440
+ // src/generators/ignore/qwencode.ts
3441
+ var import_node_path8 = require("path");
3442
+ function extractQwenCodeFileFilteringPatterns(content) {
3443
+ const filtering = {};
3444
+ const configBlocks = content.match(/```(?:json|javascript)\s*\n([\s\S]*?)\n```/g);
3445
+ if (configBlocks) {
3446
+ for (const block of configBlocks) {
3447
+ try {
3448
+ const jsonContent = block.replace(/```(?:json|javascript)\s*\n/, "").replace(/\n```$/, "");
3449
+ const parsed = JSON.parse(jsonContent);
3450
+ if (parsed.fileFiltering) {
3451
+ Object.assign(filtering, parsed.fileFiltering);
3452
+ }
3453
+ } catch {
3454
+ }
3455
+ }
3456
+ }
3457
+ if (content.includes("respectGitIgnore")) {
3458
+ if (content.includes("respectGitIgnore: false") || content.includes('"respectGitIgnore": false')) {
3459
+ filtering.respectGitIgnore = false;
3460
+ } else {
3461
+ filtering.respectGitIgnore = true;
3462
+ }
3463
+ }
3464
+ if (content.includes("enableRecursiveFileSearch")) {
3465
+ if (content.includes("enableRecursiveFileSearch: false") || content.includes('"enableRecursiveFileSearch": false')) {
3466
+ filtering.enableRecursiveFileSearch = false;
3467
+ } else {
3468
+ filtering.enableRecursiveFileSearch = true;
3469
+ }
3470
+ }
3471
+ return Object.keys(filtering).length > 0 ? filtering : void 0;
3472
+ }
3473
+ function generateQwenCodeConfiguration(rules) {
3474
+ const config = {};
3475
+ config.fileFiltering = {
3476
+ respectGitIgnore: true,
3477
+ enableRecursiveFileSearch: true
3478
+ };
3479
+ for (const rule of rules) {
3480
+ const ruleFiltering = extractQwenCodeFileFilteringPatterns(rule.content);
3481
+ if (ruleFiltering) {
3482
+ Object.assign(config.fileFiltering, ruleFiltering);
3483
+ }
3484
+ }
3485
+ return config;
3486
+ }
3487
+ async function generateQwenCodeIgnoreFiles(rules, config, baseDir) {
3488
+ const outputs = [];
3489
+ const outputPath = baseDir || process.cwd();
3490
+ const qwenConfig = generateQwenCodeConfiguration(rules);
3491
+ const settingsPath = (0, import_node_path8.join)(outputPath, ".qwen", "settings.json");
3492
+ outputs.push({
3493
+ tool: "qwencode",
3494
+ filepath: settingsPath,
3495
+ content: `${JSON.stringify(qwenConfig, null, 2)}
3496
+ `
3497
+ });
3498
+ return outputs;
3499
+ }
3500
+
2922
3501
  // src/generators/ignore/windsurf.ts
2923
3502
  function generateWindsurfIgnore(rules, config, baseDir) {
2924
3503
  return generateIgnoreFile(rules, config, ignoreConfigs.windsurf, baseDir);
2925
3504
  }
2926
3505
 
3506
+ // src/generators/rules/agentsmd.ts
3507
+ init_file();
3508
+ async function generateAgentsMdConfig(rules, config, baseDir) {
3509
+ const outputs = [];
3510
+ const nonEmptyRules = rules.filter((rule) => rule.content.trim().length > 0);
3511
+ if (nonEmptyRules.length > 0) {
3512
+ const rootRule = nonEmptyRules.find((rule) => rule.frontmatter.root);
3513
+ const detailRules = nonEmptyRules.filter((rule) => !rule.frontmatter.root);
3514
+ if (rootRule) {
3515
+ const agentsPath = resolvePath("AGENTS.md", baseDir);
3516
+ const agentsContent = generateAgentsMarkdown(rootRule);
3517
+ outputs.push({
3518
+ tool: "agentsmd",
3519
+ filepath: agentsPath,
3520
+ content: agentsContent
3521
+ });
3522
+ }
3523
+ for (const rule of detailRules) {
3524
+ const memoryPath = resolvePath(`.agents/memories/${rule.filename}.md`, baseDir);
3525
+ const memoryContent = generateMemoryMarkdown(rule);
3526
+ outputs.push({
3527
+ tool: "agentsmd",
3528
+ filepath: memoryPath,
3529
+ content: memoryContent
3530
+ });
3531
+ }
3532
+ }
3533
+ return outputs;
3534
+ }
3535
+ function generateAgentsMarkdown(rootRule) {
3536
+ return rootRule.content.trim();
3537
+ }
3538
+ function generateMemoryMarkdown(rule) {
3539
+ return rule.content.trim();
3540
+ }
3541
+
2927
3542
  // src/generators/rules/shared-helpers.ts
2928
- var import_node_path8 = require("path");
3543
+ var import_node_path10 = require("path");
2929
3544
  init_file();
2930
3545
 
2931
3546
  // src/utils/ignore.ts
2932
- var import_node_path7 = require("path");
3547
+ var import_node_path9 = require("path");
2933
3548
  var import_micromatch = __toESM(require("micromatch"), 1);
2934
3549
  init_file();
2935
3550
  init_logger();
@@ -2938,7 +3553,7 @@ async function loadIgnorePatterns(baseDir = process.cwd()) {
2938
3553
  if (cachedIgnorePatterns) {
2939
3554
  return cachedIgnorePatterns;
2940
3555
  }
2941
- const ignorePath = (0, import_node_path7.join)(baseDir, ".rulesyncignore");
3556
+ const ignorePath = (0, import_node_path9.join)(baseDir, ".rulesyncignore");
2942
3557
  if (!await fileExists(ignorePath)) {
2943
3558
  cachedIgnorePatterns = { patterns: [] };
2944
3559
  return cachedIgnorePatterns;
@@ -2992,21 +3607,35 @@ function addOutput(outputs, tool, config, baseDir, relativePath, content) {
2992
3607
  const outputDir = resolveOutputDir(config, tool, baseDir);
2993
3608
  outputs.push({
2994
3609
  tool,
2995
- filepath: (0, import_node_path8.join)(outputDir, relativePath),
3610
+ filepath: (0, import_node_path10.join)(outputDir, relativePath),
2996
3611
  content
2997
3612
  });
2998
3613
  }
2999
3614
  async function generateRulesConfig(rules, config, generatorConfig, baseDir) {
3000
3615
  const outputs = [];
3001
- for (const rule of rules) {
3002
- const content = generatorConfig.generateContent(rule);
3003
- const outputDir = resolveOutputDir(config, generatorConfig.tool, baseDir);
3004
- const filepath = generatorConfig.pathResolver ? generatorConfig.pathResolver(rule, outputDir) : (0, import_node_path8.join)(outputDir, `${rule.filename}${generatorConfig.fileExtension}`);
3616
+ const nonEmptyRules = rules.filter((rule) => rule.content.trim().length > 0);
3617
+ if (nonEmptyRules.length === 0) {
3618
+ return outputs;
3619
+ }
3620
+ if (generatorConfig.singleFileMode && generatorConfig.fileName && generatorConfig.generateCombinedContent) {
3621
+ const filepath = resolvePath(generatorConfig.fileName, baseDir);
3622
+ const content = generatorConfig.generateCombinedContent(nonEmptyRules);
3005
3623
  outputs.push({
3006
3624
  tool: generatorConfig.tool,
3007
3625
  filepath,
3008
3626
  content
3009
3627
  });
3628
+ } else if (generatorConfig.fileExtension) {
3629
+ for (const rule of nonEmptyRules) {
3630
+ const content = generatorConfig.generateContent(rule);
3631
+ const outputDir = resolveOutputDir(config, generatorConfig.tool, baseDir);
3632
+ const filepath = generatorConfig.pathResolver ? generatorConfig.pathResolver(rule, outputDir) : (0, import_node_path10.join)(outputDir, `${rule.filename}${generatorConfig.fileExtension}`);
3633
+ outputs.push({
3634
+ tool: generatorConfig.tool,
3635
+ filepath,
3636
+ content
3637
+ });
3638
+ }
3010
3639
  }
3011
3640
  const ignorePatterns = await loadIgnorePatterns(baseDir);
3012
3641
  if (ignorePatterns.patterns.length > 0 && generatorConfig.ignoreFileName) {
@@ -3029,7 +3658,7 @@ async function generateComplexRules(rules, config, generatorConfig, baseDir) {
3029
3658
  for (const rule of detailRules) {
3030
3659
  const content = generatorConfig.generateDetailContent(rule);
3031
3660
  const filepath = resolvePath(
3032
- (0, import_node_path8.join)(generatorConfig.detailSubDir, `${rule.filename}.md`),
3661
+ (0, import_node_path10.join)(generatorConfig.detailSubDir, `${rule.filename}.md`),
3033
3662
  baseDir
3034
3663
  );
3035
3664
  outputs.push({
@@ -3137,7 +3766,7 @@ function generateRuleFile(rule) {
3137
3766
  }
3138
3767
 
3139
3768
  // src/generators/rules/augmentcode.ts
3140
- var import_node_path9 = require("path");
3769
+ var import_node_path11 = require("path");
3141
3770
  async function generateAugmentcodeConfig(rules, config, baseDir) {
3142
3771
  const outputs = createOutputsArray();
3143
3772
  rules.forEach((rule) => {
@@ -3146,7 +3775,7 @@ async function generateAugmentcodeConfig(rules, config, baseDir) {
3146
3775
  "augmentcode",
3147
3776
  config,
3148
3777
  baseDir,
3149
- (0, import_node_path9.join)(".augment", "rules", `${rule.filename}.md`),
3778
+ (0, import_node_path11.join)(".augment", "rules", `${rule.filename}.md`),
3150
3779
  generateRuleFile2(rule)
3151
3780
  );
3152
3781
  });
@@ -3199,7 +3828,7 @@ function generateLegacyGuidelinesFile(allRules) {
3199
3828
  }
3200
3829
 
3201
3830
  // src/generators/rules/claudecode.ts
3202
- var import_node_path10 = require("path");
3831
+ var import_node_path12 = require("path");
3203
3832
  init_file();
3204
3833
  init_logger();
3205
3834
  async function generateClaudecodeConfig(rules, config, baseDir) {
@@ -3213,7 +3842,7 @@ async function generateClaudecodeConfig(rules, config, baseDir) {
3213
3842
  generateDetailContent: generateMemoryFile,
3214
3843
  detailSubDir: ".claude/memories",
3215
3844
  updateAdditionalConfig: async (ignorePatterns, baseDir2) => {
3216
- const settingsPath = resolvePath((0, import_node_path10.join)(".claude", "settings.json"), baseDir2);
3845
+ const settingsPath = resolvePath((0, import_node_path12.join)(".claude", "settings.json"), baseDir2);
3217
3846
  await updateClaudeSettings(settingsPath, ignorePatterns);
3218
3847
  return [];
3219
3848
  }
@@ -3277,7 +3906,7 @@ async function updateClaudeSettings(settingsPath, ignorePatterns) {
3277
3906
  }
3278
3907
 
3279
3908
  // src/generators/rules/generator-registry.ts
3280
- var import_node_path11 = require("path");
3909
+ var import_node_path13 = require("path");
3281
3910
  function determineCursorRuleType(frontmatter) {
3282
3911
  if (frontmatter.cursorRuleType) {
3283
3912
  return frontmatter.cursorRuleType;
@@ -3301,6 +3930,15 @@ function determineCursorRuleType(frontmatter) {
3301
3930
  }
3302
3931
  var GENERATOR_REGISTRY = {
3303
3932
  // Simple generators - generate one file per rule
3933
+ agentsmd: {
3934
+ type: "complex",
3935
+ tool: "agentsmd",
3936
+ fileExtension: ".md",
3937
+ ignoreFileName: ".agentsignore",
3938
+ generateContent: (rule) => rule.content.trim()
3939
+ // NOTE: AGENTS.md specific logic is handled in the actual generator file
3940
+ // Root rule goes to AGENTS.md, detail rules go to .agents/memories/
3941
+ },
3304
3942
  amazonqcli: {
3305
3943
  type: "complex",
3306
3944
  tool: "amazonqcli",
@@ -3373,7 +4011,7 @@ var GENERATOR_REGISTRY = {
3373
4011
  },
3374
4012
  pathResolver: (rule, outputDir) => {
3375
4013
  const baseFilename = rule.filename.replace(/\.md$/, "");
3376
- return (0, import_node_path11.join)(outputDir, `${baseFilename}.instructions.md`);
4014
+ return (0, import_node_path13.join)(outputDir, `${baseFilename}.instructions.md`);
3377
4015
  }
3378
4016
  },
3379
4017
  cursor: {
@@ -3413,7 +4051,7 @@ var GENERATOR_REGISTRY = {
3413
4051
  return lines.join("\n");
3414
4052
  },
3415
4053
  pathResolver: (rule, outputDir) => {
3416
- return (0, import_node_path11.join)(outputDir, `${rule.filename}.mdc`);
4054
+ return (0, import_node_path13.join)(outputDir, `${rule.filename}.mdc`);
3417
4055
  }
3418
4056
  },
3419
4057
  codexcli: {
@@ -3449,10 +4087,10 @@ var GENERATOR_REGISTRY = {
3449
4087
  pathResolver: (rule, outputDir) => {
3450
4088
  const outputFormat = rule.frontmatter.windsurfOutputFormat || "directory";
3451
4089
  if (outputFormat === "single-file") {
3452
- return (0, import_node_path11.join)(outputDir, ".windsurf-rules");
4090
+ return (0, import_node_path13.join)(outputDir, ".windsurf-rules");
3453
4091
  } else {
3454
- const rulesDir = (0, import_node_path11.join)(outputDir, ".windsurf", "rules");
3455
- return (0, import_node_path11.join)(rulesDir, `${rule.filename}.md`);
4092
+ const rulesDir = (0, import_node_path13.join)(outputDir, ".windsurf", "rules");
4093
+ return (0, import_node_path13.join)(rulesDir, `${rule.filename}.md`);
3456
4094
  }
3457
4095
  }
3458
4096
  },
@@ -3515,6 +4153,22 @@ var GENERATOR_REGISTRY = {
3515
4153
  const lines = [];
3516
4154
  if (rule.frontmatter.description) {
3517
4155
  lines.push(`# ${rule.frontmatter.description}
4156
+ `);
4157
+ }
4158
+ lines.push(rule.content.trim());
4159
+ return lines.join("\n");
4160
+ }
4161
+ // Complex generation handled by existing generator
4162
+ },
4163
+ qwencode: {
4164
+ type: "complex",
4165
+ tool: "qwencode",
4166
+ fileExtension: ".md",
4167
+ // ignoreFileName omitted - Qwen Code uses git-aware filtering instead of dedicated ignore files
4168
+ generateContent: (rule) => {
4169
+ const lines = [];
4170
+ if (rule.frontmatter.description) {
4171
+ lines.push(`# ${rule.frontmatter.description}
3518
4172
  `);
3519
4173
  }
3520
4174
  lines.push(rule.content.trim());
@@ -3571,9 +4225,60 @@ var generateCopilotConfig = createSimpleGenerator("copilot");
3571
4225
  var generateWindsurfConfig = createSimpleGenerator("windsurf");
3572
4226
  var generateKiroConfig = createSimpleGenerator("kiro");
3573
4227
  var generateRooConfig = createSimpleGenerator("roo");
4228
+ var generateQwencodeConfig = createSimpleGenerator("qwencode");
4229
+
4230
+ // src/generators/rules/base-generator.ts
4231
+ async function generateBaseRulesConfig(rules, config, generatorConfig, baseDir) {
4232
+ const unifiedConfig = {
4233
+ tool: generatorConfig.tool,
4234
+ fileName: generatorConfig.fileName,
4235
+ ...generatorConfig.ignoreFileName ? { ignoreFileName: generatorConfig.ignoreFileName } : {},
4236
+ singleFileMode: true,
4237
+ generateCombinedContent: generatorConfig.generateContent,
4238
+ generateContent: () => ""
4239
+ // Not used in single file mode
4240
+ };
4241
+ return generateRulesConfig(rules, config, unifiedConfig, baseDir);
4242
+ }
4243
+ function categorizeRules(rules) {
4244
+ return rules.reduce(
4245
+ (acc, rule) => {
4246
+ if (rule.frontmatter.root) {
4247
+ acc.root.push(rule);
4248
+ } else {
4249
+ acc.detail.push(rule);
4250
+ }
4251
+ return acc;
4252
+ },
4253
+ { root: [], detail: [] }
4254
+ );
4255
+ }
4256
+ function generateMarkdownContent(rules) {
4257
+ const sections = [];
4258
+ const categorized = categorizeRules(rules);
4259
+ if (categorized.root.length > 0) {
4260
+ sections.push(...categorized.root.map((rule) => rule.content.trim()));
4261
+ }
4262
+ if (categorized.detail.length > 0) {
4263
+ sections.push(...categorized.detail.map((rule) => rule.content.trim()));
4264
+ }
4265
+ return sections.join("\n\n").trim();
4266
+ }
3574
4267
 
3575
4268
  // src/generators/rules/codexcli.ts
3576
- init_file();
4269
+ async function generateCodexConfig(rules, config, baseDir) {
4270
+ return generateBaseRulesConfig(
4271
+ rules,
4272
+ config,
4273
+ {
4274
+ fileName: "AGENTS.md",
4275
+ ignoreFileName: ".codexignore",
4276
+ generateContent: generateMarkdownContent,
4277
+ tool: "codexcli"
4278
+ },
4279
+ baseDir
4280
+ );
4281
+ }
3577
4282
 
3578
4283
  // src/utils/xml-document-generator.ts
3579
4284
  var import_fast_xml_parser = require("fast-xml-parser");
@@ -3619,47 +4324,6 @@ function generateRootMarkdownWithXmlDocs(rootRule, memoryRules, config) {
3619
4324
  return lines.join("\n");
3620
4325
  }
3621
4326
 
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
- });
3649
- }
3650
- }
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
- });
3661
- }
3662
-
3663
4327
  // src/generators/rules/geminicli.ts
3664
4328
  async function generateGeminiConfig(rules, config, baseDir) {
3665
4329
  const generatorConfig = {
@@ -3717,21 +4381,39 @@ async function generateOpenCodeConfig(rules, config, baseDir) {
3717
4381
  tool: "opencode",
3718
4382
  fileExtension: ".md",
3719
4383
  // ignoreFileName omitted - OpenCode doesn't use dedicated ignore files
3720
- generateContent: generateOpenCodeMarkdown,
3721
- generateDetailContent: generateOpenCodeMarkdown,
3722
- generateRootContent: generateOpenCodeRootMarkdown,
4384
+ generateContent: (rule) => rule.content.trim(),
4385
+ generateDetailContent: (rule) => rule.content.trim(),
4386
+ generateRootContent: (rootRule, memoryRules) => generateRootMarkdownWithXmlDocs(rootRule, memoryRules, {
4387
+ memorySubDir: ".opencode/memories",
4388
+ fallbackTitle: "OpenCode Configuration"
4389
+ }),
3723
4390
  rootFilePath: "AGENTS.md",
3724
4391
  detailSubDir: ".opencode/memories"
3725
4392
  };
3726
4393
  return generateComplexRules(rules, config, generatorConfig, baseDir);
3727
4394
  }
3728
- function generateOpenCodeMarkdown(rule) {
4395
+
4396
+ // src/generators/rules/qwencode.ts
4397
+ async function generateQwencodeConfig2(rules, config, baseDir) {
4398
+ const generatorConfig = {
4399
+ tool: "qwencode",
4400
+ fileExtension: ".md",
4401
+ // ignoreFileName omitted - Qwen Code uses git-aware filtering instead of dedicated ignore files
4402
+ generateContent: generateQwenMemoryMarkdown,
4403
+ generateDetailContent: generateQwenMemoryMarkdown,
4404
+ generateRootContent: generateQwenRootMarkdown,
4405
+ rootFilePath: "QWEN.md",
4406
+ detailSubDir: ".qwen/memories"
4407
+ };
4408
+ return generateComplexRules(rules, config, generatorConfig, baseDir);
4409
+ }
4410
+ function generateQwenMemoryMarkdown(rule) {
3729
4411
  return rule.content.trim();
3730
4412
  }
3731
- function generateOpenCodeRootMarkdown(rootRule, memoryRules, _baseDir) {
4413
+ function generateQwenRootMarkdown(rootRule, memoryRules, _baseDir) {
3732
4414
  return generateRootMarkdownWithXmlDocs(rootRule, memoryRules, {
3733
- memorySubDir: ".opencode/memories",
3734
- fallbackTitle: "OpenCode Configuration"
4415
+ memorySubDir: ".qwen/memories",
4416
+ fallbackTitle: "Qwen Code Configuration"
3735
4417
  });
3736
4418
  }
3737
4419
 
@@ -3768,6 +4450,8 @@ function filterRulesForTool(rules, tool, config) {
3768
4450
  }
3769
4451
  async function generateForTool(tool, rules, config, baseDir) {
3770
4452
  switch (tool) {
4453
+ case "agentsmd":
4454
+ return await generateAgentsMdConfig(rules, config, baseDir);
3771
4455
  case "amazonqcli":
3772
4456
  return await generateAmazonqcliConfig(rules, config, baseDir);
3773
4457
  case "augmentcode": {
@@ -3810,6 +4494,11 @@ async function generateForTool(tool, rules, config, baseDir) {
3810
4494
  }
3811
4495
  case "opencode":
3812
4496
  return generateOpenCodeConfig(rules, config, baseDir);
4497
+ case "qwencode": {
4498
+ const qwenRulesOutputs = await generateQwencodeConfig2(rules, config, baseDir);
4499
+ const qwenIgnoreOutputs = await generateQwenCodeIgnoreFiles(rules, config, baseDir);
4500
+ return [...qwenRulesOutputs, ...qwenIgnoreOutputs];
4501
+ }
3813
4502
  case "windsurf": {
3814
4503
  const windsurfRulesOutputs = await generateWindsurfConfig(rules, config, baseDir);
3815
4504
  const windsurfIgnoreOutputs = await generateWindsurfIgnore(rules, config, baseDir);
@@ -3822,7 +4511,7 @@ async function generateForTool(tool, rules, config, baseDir) {
3822
4511
  }
3823
4512
 
3824
4513
  // src/core/parser.ts
3825
- var import_node_path12 = require("path");
4514
+ var import_node_path14 = require("path");
3826
4515
  init_logger();
3827
4516
  async function parseRulesFromDirectory(aiRulesDir) {
3828
4517
  const ignorePatterns = await loadIgnorePatterns();
@@ -3876,7 +4565,7 @@ async function parseRuleFile(filepath) {
3876
4565
  },
3877
4566
  ...validatedData.tags !== void 0 && { tags: validatedData.tags }
3878
4567
  };
3879
- const filename = (0, import_node_path12.basename)(filepath, ".md");
4568
+ const filename = (0, import_node_path14.basename)(filepath, ".md");
3880
4569
  return {
3881
4570
  frontmatter,
3882
4571
  content: parsed.content,
@@ -3954,6 +4643,7 @@ init_cursor();
3954
4643
  init_geminicli();
3955
4644
  init_junie();
3956
4645
  init_kiro();
4646
+ init_qwencode();
3957
4647
  init_roo();
3958
4648
  init_windsurf();
3959
4649
 
@@ -3991,10 +4681,12 @@ function parseMcpConfig(projectRoot) {
3991
4681
  async function generateMcpConfigurations(mcpConfig, baseDir, targetTools) {
3992
4682
  const outputs = [];
3993
4683
  const toolMap = {
4684
+ agentsmd: async () => [],
3994
4685
  amazonqcli: async (servers, dir) => {
3995
4686
  const config = {
3996
4687
  aiRulesDir: ".rulesync",
3997
4688
  outputPaths: {
4689
+ agentsmd: ".agents/memories",
3998
4690
  amazonqcli: ".amazonq/rules",
3999
4691
  augmentcode: ".",
4000
4692
  "augmentcode-legacy": ".",
@@ -4004,6 +4696,7 @@ async function generateMcpConfigurations(mcpConfig, baseDir, targetTools) {
4004
4696
  claudecode: ".",
4005
4697
  codexcli: ".",
4006
4698
  opencode: ".",
4699
+ qwencode: ".qwen/memories",
4007
4700
  roo: ".roo/rules",
4008
4701
  geminicli: ".gemini/memories",
4009
4702
  kiro: ".kiro/steering",
@@ -4047,6 +4740,10 @@ async function generateMcpConfigurations(mcpConfig, baseDir, targetTools) {
4047
4740
  ),
4048
4741
  kiro: async (servers, dir) => (await Promise.resolve().then(() => (init_kiro(), kiro_exports))).generateKiroMcpConfiguration(servers, dir),
4049
4742
  junie: async (servers, dir) => (await Promise.resolve().then(() => (init_junie(), junie_exports))).generateJunieMcpConfiguration(servers, dir),
4743
+ qwencode: async (servers, dir) => (await Promise.resolve().then(() => (init_qwencode(), qwencode_exports))).generateQwenCodeMcpConfiguration(
4744
+ servers,
4745
+ dir
4746
+ ),
4050
4747
  windsurf: async (servers, dir) => (await Promise.resolve().then(() => (init_windsurf(), windsurf_exports))).generateWindsurfMcpConfiguration(
4051
4748
  servers,
4052
4749
  dir
@@ -4068,223 +4765,594 @@ async function generateMcpConfigurations(mcpConfig, baseDir, targetTools) {
4068
4765
  return outputs;
4069
4766
  }
4070
4767
 
4071
- // src/cli/commands/generate.ts
4768
+ // src/core/subagent-generator.ts
4769
+ var import_node_path17 = __toESM(require("path"), 1);
4770
+
4771
+ // src/generators/subagents/claudecode.ts
4072
4772
  init_logger();
4073
- async function generateCommand(options = {}) {
4074
- const configLoaderOptions = {
4075
- ...options.config !== void 0 && { configPath: options.config },
4076
- ...options.noConfig !== void 0 && { noConfig: options.noConfig }
4077
- };
4078
- const configResult = await loadConfig(configLoaderOptions);
4079
- const cliOptions = {
4080
- ...options.tools !== void 0 && { tools: options.tools },
4081
- ...options.verbose !== void 0 && { verbose: options.verbose },
4082
- ...options.delete !== void 0 && { delete: options.delete },
4083
- ...options.baseDirs !== void 0 && { baseDirs: options.baseDirs }
4084
- };
4085
- const config = mergeWithCliOptions(configResult.config, cliOptions);
4086
- logger.setVerbose(config.verbose || false);
4087
- if (options.tools && options.tools.length > 0) {
4088
- const configTargets = config.defaultTargets;
4089
- const cliTools = options.tools;
4090
- const cliToolsSet = new Set(cliTools);
4091
- const configTargetsSet = new Set(configTargets);
4092
- const notInConfig = cliTools.filter((tool) => !configTargetsSet.has(tool));
4093
- const notInCli = configTargets.filter((tool) => !cliToolsSet.has(tool));
4094
- if (notInConfig.length > 0 || notInCli.length > 0) {
4095
- logger.warn("\u26A0\uFE0F Warning: CLI tool selection differs from configuration!");
4096
- logger.warn(` Config targets: ${configTargets.join(", ")}`);
4097
- logger.warn(` CLI specified: ${cliTools.join(", ")}`);
4098
- if (notInConfig.length > 0) {
4099
- logger.warn(` Tools specified but not in config: ${notInConfig.join(", ")}`);
4100
- }
4101
- if (notInCli.length > 0) {
4102
- logger.warn(` Tools in config but not specified: ${notInCli.join(", ")}`);
4103
- }
4104
- logger.warn("\n The configuration file targets will be used.");
4105
- logger.warn(" To change targets, update your rulesync config file.");
4106
- logger.warn("");
4107
- }
4108
- }
4109
- let baseDirs;
4110
- if (config.baseDir) {
4111
- baseDirs = Array.isArray(config.baseDir) ? config.baseDir : [config.baseDir];
4112
- } else if (options.baseDirs) {
4113
- baseDirs = options.baseDirs;
4114
- } else {
4115
- baseDirs = [process.cwd()];
4773
+
4774
+ // src/generators/subagents/base.ts
4775
+ var import_node_path15 = __toESM(require("path"), 1);
4776
+ init_file();
4777
+ init_logger();
4778
+ var BaseSubagentGenerator = class {
4779
+ /**
4780
+ * Generate subagent files for the tool
4781
+ */
4782
+ async generate(rules, config, baseDir, parsedSubagents) {
4783
+ const toolName = this.getToolName();
4784
+ const agentsDir = this.getAgentsDirectory();
4785
+ const outputs = [];
4786
+ try {
4787
+ const outputDir = resolvePath(
4788
+ import_node_path15.default.join(config.outputPaths[toolName] ?? "", agentsDir),
4789
+ baseDir
4790
+ );
4791
+ await ensureDir(outputDir);
4792
+ let subagentOutputs;
4793
+ if (parsedSubagents && parsedSubagents.length > 0) {
4794
+ logger.debug(
4795
+ `Generating ${parsedSubagents.length} subagents from parsed data for ${toolName}`
4796
+ );
4797
+ subagentOutputs = this.generateFromParsedSubagents(parsedSubagents);
4798
+ } else {
4799
+ logger.debug(`Generating subagents from rules for ${toolName}`);
4800
+ subagentOutputs = this.generateFromRules(rules);
4801
+ }
4802
+ for (const subagent of subagentOutputs) {
4803
+ const filePath = import_node_path15.default.join(outputDir, subagent.filename);
4804
+ outputs.push({
4805
+ tool: toolName,
4806
+ filepath: filePath,
4807
+ content: subagent.content
4808
+ });
4809
+ }
4810
+ if (outputs.length > 0) {
4811
+ logger.info(`Generated ${outputs.length} subagent files for ${toolName}`);
4812
+ } else {
4813
+ logger.debug(`No subagents generated for ${toolName}`);
4814
+ }
4815
+ return outputs;
4816
+ } catch (error) {
4817
+ logger.error(`Error generating subagents for ${toolName}:`, error);
4818
+ return [];
4819
+ }
4116
4820
  }
4117
- logger.info(`Loaded configuration from: ${configResult.filepath}`);
4118
- logger.log("Generating configuration files...");
4119
- if (!await fileExists(config.aiRulesDir)) {
4120
- logger.error("\u274C .rulesync directory not found. Run 'rulesync init' first.");
4121
- process.exit(1);
4821
+ };
4822
+
4823
+ // src/generators/subagents/claudecode.ts
4824
+ var ClaudeCodeSubagentGenerator = class extends BaseSubagentGenerator {
4825
+ getToolName() {
4826
+ return "claudecode";
4827
+ }
4828
+ getAgentsDirectory() {
4829
+ return ".claude/agents";
4830
+ }
4831
+ generateFromRules(_rules) {
4832
+ logger.debug("Skipping rule-to-subagent conversion (deprecated behavior)");
4833
+ return [];
4834
+ }
4835
+ generateFromParsedSubagents(subagents) {
4836
+ return subagents.map((subagent) => {
4837
+ const frontmatterLines = ["---"];
4838
+ frontmatterLines.push(`name: ${subagent.frontmatter.name}`);
4839
+ frontmatterLines.push(`description: ${subagent.frontmatter.description}`);
4840
+ if (subagent.frontmatter.claudecode?.model) {
4841
+ frontmatterLines.push(`model: ${subagent.frontmatter.claudecode.model}`);
4842
+ }
4843
+ frontmatterLines.push("---");
4844
+ const content = `${frontmatterLines.join("\n")}
4845
+
4846
+ ${subagent.content}`;
4847
+ return {
4848
+ filename: `${subagent.filename}.md`,
4849
+ content
4850
+ };
4851
+ });
4852
+ }
4853
+ processContent(subagent) {
4854
+ const frontmatterLines = ["---"];
4855
+ frontmatterLines.push(`name: ${subagent.frontmatter.name}`);
4856
+ frontmatterLines.push(`description: ${subagent.frontmatter.description}`);
4857
+ if (subagent.frontmatter.claudecode?.model) {
4858
+ frontmatterLines.push(`model: ${subagent.frontmatter.claudecode.model}`);
4859
+ }
4860
+ frontmatterLines.push("---");
4861
+ return `${frontmatterLines.join("\n")}
4862
+
4863
+ ${subagent.content}`;
4864
+ }
4865
+ };
4866
+
4867
+ // src/parsers/subagent-parser.ts
4868
+ var import_promises3 = require("fs/promises");
4869
+ var import_node_path16 = __toESM(require("path"), 1);
4870
+ var import_gray_matter2 = __toESM(require("gray-matter"), 1);
4871
+
4872
+ // src/types/subagent.ts
4873
+ var import_mini8 = require("zod/mini");
4874
+ var ClaudeCodeConfigSchema = import_mini8.z.object({
4875
+ model: import_mini8.z.optional(import_mini8.z.string())
4876
+ });
4877
+ var SubagentFrontmatterSchema = import_mini8.z.object({
4878
+ name: import_mini8.z.string(),
4879
+ description: import_mini8.z.string(),
4880
+ targets: import_mini8.z.optional(import_mini8.z.array(import_mini8.z.string())),
4881
+ // Tool-specific configurations
4882
+ claudecode: import_mini8.z.optional(ClaudeCodeConfigSchema)
4883
+ });
4884
+
4885
+ // src/parsers/subagent-parser.ts
4886
+ init_file();
4887
+ init_logger();
4888
+ async function parseSubagentFile(filepath) {
4889
+ try {
4890
+ const fileContent = await (0, import_promises3.readFile)(filepath, "utf-8");
4891
+ const { data: frontmatter, content } = (0, import_gray_matter2.default)(fileContent);
4892
+ const result = SubagentFrontmatterSchema.safeParse(frontmatter);
4893
+ if (!result.success) {
4894
+ logger.warn(`Invalid frontmatter in ${filepath}: ${result.error.message}`);
4895
+ return null;
4896
+ }
4897
+ const parsedFrontmatter = result.data;
4898
+ if (!parsedFrontmatter.targets) {
4899
+ parsedFrontmatter.targets = ["*"];
4900
+ }
4901
+ const filename = import_node_path16.default.basename(filepath, import_node_path16.default.extname(filepath));
4902
+ return {
4903
+ frontmatter: parsedFrontmatter,
4904
+ content: content.trim(),
4905
+ filename,
4906
+ filepath
4907
+ };
4908
+ } catch (error) {
4909
+ logger.error(`Error parsing subagent file ${filepath}:`, error);
4910
+ return null;
4911
+ }
4912
+ }
4913
+ async function parseSubagentsFromDirectory(agentsDir) {
4914
+ try {
4915
+ if (!await fileExists(agentsDir)) {
4916
+ logger.debug(`Agents directory does not exist: ${agentsDir}`);
4917
+ return [];
4918
+ }
4919
+ const entries = await (0, import_promises3.readdir)(agentsDir);
4920
+ const mdFiles = entries.filter((file) => file.endsWith(".md"));
4921
+ const files = mdFiles.map((file) => import_node_path16.default.join(agentsDir, file));
4922
+ if (files.length === 0) {
4923
+ logger.debug(`No subagent files found in ${agentsDir}`);
4924
+ return [];
4925
+ }
4926
+ logger.debug(`Found ${files.length} subagent files in ${agentsDir}`);
4927
+ const parsePromises = files.map((file) => parseSubagentFile(file));
4928
+ const results = await Promise.all(parsePromises);
4929
+ const subagents = results.filter((result) => result !== null);
4930
+ logger.info(`Successfully parsed ${subagents.length} subagents`);
4931
+ return subagents;
4932
+ } catch (error) {
4933
+ logger.error(`Error parsing subagents from directory ${agentsDir}:`, error);
4934
+ return [];
4122
4935
  }
4936
+ }
4937
+
4938
+ // src/core/subagent-generator.ts
4939
+ init_logger();
4940
+ async function generateSubagents(rulesyncDir, outputDir, tools, rules) {
4941
+ const outputs = [];
4942
+ const subagentsDir = import_node_path17.default.join(rulesyncDir, "subagents");
4943
+ let parsedSubagents = [];
4123
4944
  try {
4124
- logger.info(`Parsing rules from ${config.aiRulesDir}...`);
4125
- const rules = await parseRulesFromDirectory(config.aiRulesDir);
4126
- if (rules.length === 0) {
4127
- logger.warn("\u26A0\uFE0F No rules found in .rulesync directory");
4128
- return;
4945
+ parsedSubagents = await parseSubagentsFromDirectory(subagentsDir);
4946
+ if (parsedSubagents.length > 0) {
4947
+ logger.debug(`Found ${parsedSubagents.length} subagent files in ${subagentsDir}`);
4129
4948
  }
4130
- logger.info(`Found ${rules.length} rule(s)`);
4131
- logger.info(`Base directories: ${baseDirs.join(", ")}`);
4132
- if (config.delete) {
4133
- logger.info("Deleting existing output directories...");
4134
- const targetTools = config.defaultTargets;
4135
- const deleteTasks = [];
4136
- const commandsDir = (0, import_node_path13.join)(config.aiRulesDir, "commands");
4137
- const hasCommands = await fileExists(commandsDir);
4138
- let hasCommandFiles = false;
4139
- if (hasCommands) {
4140
- const { readdir: readdir2 } = await import("fs/promises");
4141
- try {
4142
- const files = await readdir2(commandsDir);
4143
- hasCommandFiles = files.some((file) => file.endsWith(".md"));
4144
- } catch {
4145
- hasCommandFiles = false;
4949
+ } catch (error) {
4950
+ logger.debug(`No subagents directory found or error reading: ${error}`);
4951
+ }
4952
+ if (!tools || tools.length === 0) {
4953
+ return outputs;
4954
+ }
4955
+ const config = {
4956
+ aiRulesDir: rulesyncDir,
4957
+ outputPaths: {
4958
+ agentsmd: ".",
4959
+ amazonqcli: ".",
4960
+ augmentcode: ".augment",
4961
+ "augmentcode-legacy": ".",
4962
+ copilot: ".github",
4963
+ cursor: ".cursor",
4964
+ cline: ".",
4965
+ claudecode: ".",
4966
+ codexcli: ".",
4967
+ opencode: ".",
4968
+ qwencode: ".",
4969
+ roo: ".roo",
4970
+ geminicli: ".gemini",
4971
+ kiro: ".kiro",
4972
+ junie: ".junie",
4973
+ windsurf: ".windsurf"
4974
+ },
4975
+ watchEnabled: false,
4976
+ defaultTargets: tools
4977
+ };
4978
+ for (const tool of tools) {
4979
+ try {
4980
+ let generator;
4981
+ switch (tool) {
4982
+ case "claudecode":
4983
+ generator = new ClaudeCodeSubagentGenerator();
4984
+ break;
4985
+ // Add other tool generators here as they are implemented
4986
+ default:
4987
+ logger.debug(`Subagent generation not yet implemented for ${tool}`);
4988
+ continue;
4989
+ }
4990
+ const generatedOutputs = await generator.generate(
4991
+ parsedSubagents.length > 0 ? [] : rules || [],
4992
+ config,
4993
+ outputDir,
4994
+ parsedSubagents
4995
+ );
4996
+ for (const output of generatedOutputs) {
4997
+ outputs.push({
4998
+ tool,
4999
+ filepath: output.filepath,
5000
+ content: output.content
5001
+ });
5002
+ }
5003
+ } catch (error) {
5004
+ logger.error(`Failed to generate subagents for ${tool}:`, error);
5005
+ }
5006
+ }
5007
+ return outputs;
5008
+ }
5009
+
5010
+ // src/cli/commands/generate.ts
5011
+ init_logger();
5012
+
5013
+ // src/cli/commands/shared-utils.ts
5014
+ init_logger();
5015
+ function showBackwardCompatibilityWarning(commandName, exampleCommand) {
5016
+ const yellow = "\x1B[33m";
5017
+ const gray = "\x1B[90m";
5018
+ const cyan = "\x1B[36m";
5019
+ const reset = "\x1B[0m";
5020
+ logger.warn(
5021
+ `
5022
+ ${yellow}\u26A0\uFE0F Warning: No --features option specified.${reset}
5023
+ ${gray}Currently ${commandName} all features for backward compatibility.${reset}
5024
+ ${gray}In future versions, this behavior may change.${reset}
5025
+ ${gray}Please specify --features explicitly:${reset}
5026
+ ${cyan} ${exampleCommand}${reset}
5027
+ ${gray}Or use --features * to ${commandName.replace("ing", "")} all features.${reset}
5028
+ `
5029
+ );
5030
+ }
5031
+
5032
+ // src/cli/commands/generate.ts
5033
+ async function generateCommand(options = {}) {
5034
+ try {
5035
+ const cliParser = new CliParser();
5036
+ const cliInputs = {};
5037
+ if (options.tools !== void 0) cliInputs.tools = options.tools;
5038
+ if (options.features !== void 0) cliInputs.features = options.features;
5039
+ if (options.verbose !== void 0) cliInputs.verbose = options.verbose;
5040
+ if (options.delete !== void 0) cliInputs.delete = options.delete;
5041
+ if (options.baseDirs !== void 0) cliInputs.baseDirs = options.baseDirs;
5042
+ if (options.config !== void 0) cliInputs.config = options.config;
5043
+ if (options.noConfig !== void 0) cliInputs.noConfig = options.noConfig;
5044
+ const cliOptions = cliParser.parse(cliInputs);
5045
+ const configResolver = new ConfigResolver();
5046
+ const resolutionResult = await configResolver.resolve({
5047
+ cliOptions
5048
+ });
5049
+ const config = resolutionResult.value;
5050
+ let resolvedFeatures;
5051
+ let showWarning = false;
5052
+ if (cliOptions.features !== void 0) {
5053
+ resolvedFeatures = cliOptions.features;
5054
+ } else if (config.features !== void 0) {
5055
+ resolvedFeatures = config.features;
5056
+ } else {
5057
+ resolvedFeatures = "*";
5058
+ showWarning = true;
5059
+ }
5060
+ if (showWarning) {
5061
+ showBackwardCompatibilityWarning(
5062
+ "generating",
5063
+ "rulesync generate --features rules,commands,mcp,ignore"
5064
+ );
5065
+ }
5066
+ const normalizedFeatures = normalizeFeatures(resolvedFeatures);
5067
+ if (!config.defaultTargets || config.defaultTargets.length === 0) {
5068
+ const errorMessage = `\u274C Error: At least one tool must be specified.
5069
+
5070
+ You can specify tools in three ways:
5071
+
5072
+ 1. Use the --targets flag:
5073
+ rulesync generate --targets copilot,cursor
5074
+
5075
+ 2. Use the --all flag to generate for all tools:
5076
+ rulesync generate --all
5077
+
5078
+ 3. Set targets in rulesync.jsonc:
5079
+ {
5080
+ "targets": ["copilot", "cursor"]
5081
+ }
5082
+
5083
+ Available tools:
5084
+ agentsmd, amazonqcli, augmentcode, augmentcode-legacy, copilot, cursor, cline,
5085
+ claudecode, codexcli, opencode, qwencode, roo, geminicli, kiro, junie, windsurf`;
5086
+ logger.error(errorMessage);
5087
+ process.exit(1);
5088
+ }
5089
+ logger.setVerbose(config.verbose || false);
5090
+ let baseDirs;
5091
+ if (config.baseDir) {
5092
+ baseDirs = Array.isArray(config.baseDir) ? config.baseDir : [config.baseDir];
5093
+ } else if (options.baseDirs) {
5094
+ baseDirs = options.baseDirs;
5095
+ } else {
5096
+ baseDirs = [process.cwd()];
5097
+ }
5098
+ logger.info(`Configuration resolved from: ${resolutionResult.source}`);
5099
+ logger.log("Generating configuration files...");
5100
+ if (!await fileExists(config.aiRulesDir)) {
5101
+ logger.error("\u274C .rulesync directory not found. Run 'rulesync init' first.");
5102
+ process.exit(1);
5103
+ }
5104
+ try {
5105
+ logger.info(`Parsing rules from ${config.aiRulesDir}...`);
5106
+ const rules = await parseRulesFromDirectory(config.aiRulesDir);
5107
+ if (rules.length === 0) {
5108
+ logger.warn("\u26A0\uFE0F No rules found in .rulesync directory");
5109
+ return;
5110
+ }
5111
+ logger.info(`Found ${rules.length} rule(s)`);
5112
+ logger.info(`Base directories: ${baseDirs.join(", ")}`);
5113
+ if (config.delete) {
5114
+ logger.info("Deleting existing output directories...");
5115
+ const targetTools = config.defaultTargets;
5116
+ const deleteTasks = [];
5117
+ const commandsDir = (0, import_node_path18.join)(config.aiRulesDir, "commands");
5118
+ const hasCommands = await fileExists(commandsDir);
5119
+ let hasCommandFiles = false;
5120
+ if (hasCommands) {
5121
+ const { readdir: readdir3 } = await import("fs/promises");
5122
+ try {
5123
+ const files = await readdir3(commandsDir);
5124
+ hasCommandFiles = files.some((file) => file.endsWith(".md"));
5125
+ } catch {
5126
+ hasCommandFiles = false;
5127
+ }
5128
+ }
5129
+ for (const tool of targetTools) {
5130
+ switch (tool) {
5131
+ case "augmentcode":
5132
+ if (normalizedFeatures.includes("rules")) {
5133
+ deleteTasks.push(removeDirectory((0, import_node_path18.join)(".augment", "rules")));
5134
+ }
5135
+ if (normalizedFeatures.includes("ignore")) {
5136
+ deleteTasks.push(removeDirectory((0, import_node_path18.join)(".augment", "ignore")));
5137
+ }
5138
+ break;
5139
+ case "augmentcode-legacy":
5140
+ if (normalizedFeatures.includes("rules")) {
5141
+ deleteTasks.push(removeClaudeGeneratedFiles());
5142
+ }
5143
+ if (normalizedFeatures.includes("ignore")) {
5144
+ deleteTasks.push(removeDirectory((0, import_node_path18.join)(".augment", "ignore")));
5145
+ }
5146
+ break;
5147
+ case "copilot":
5148
+ if (normalizedFeatures.includes("rules")) {
5149
+ deleteTasks.push(removeDirectory(config.outputPaths.copilot));
5150
+ }
5151
+ break;
5152
+ case "cursor":
5153
+ if (normalizedFeatures.includes("rules")) {
5154
+ deleteTasks.push(removeDirectory(config.outputPaths.cursor));
5155
+ }
5156
+ break;
5157
+ case "cline":
5158
+ if (normalizedFeatures.includes("rules")) {
5159
+ deleteTasks.push(removeDirectory(config.outputPaths.cline));
5160
+ }
5161
+ break;
5162
+ case "claudecode":
5163
+ if (normalizedFeatures.includes("rules")) {
5164
+ deleteTasks.push(removeClaudeGeneratedFiles());
5165
+ }
5166
+ if (normalizedFeatures.includes("commands") && hasCommandFiles) {
5167
+ deleteTasks.push(removeDirectory((0, import_node_path18.join)(".claude", "commands")));
5168
+ }
5169
+ if (normalizedFeatures.includes("subagents")) {
5170
+ deleteTasks.push(removeDirectory((0, import_node_path18.join)(".claude", "agents")));
5171
+ }
5172
+ break;
5173
+ case "roo":
5174
+ if (normalizedFeatures.includes("rules")) {
5175
+ deleteTasks.push(removeDirectory(config.outputPaths.roo));
5176
+ }
5177
+ if (normalizedFeatures.includes("commands") && hasCommandFiles) {
5178
+ deleteTasks.push(removeDirectory((0, import_node_path18.join)(".roo", "commands")));
5179
+ }
5180
+ break;
5181
+ case "geminicli":
5182
+ if (normalizedFeatures.includes("rules")) {
5183
+ deleteTasks.push(removeDirectory(config.outputPaths.geminicli));
5184
+ }
5185
+ if (normalizedFeatures.includes("commands") && hasCommandFiles) {
5186
+ deleteTasks.push(removeDirectory((0, import_node_path18.join)(".gemini", "commands")));
5187
+ }
5188
+ break;
5189
+ case "kiro":
5190
+ if (normalizedFeatures.includes("rules")) {
5191
+ deleteTasks.push(removeDirectory(config.outputPaths.kiro));
5192
+ }
5193
+ break;
5194
+ case "opencode":
5195
+ if (normalizedFeatures.includes("rules")) {
5196
+ deleteTasks.push(removeDirectory(config.outputPaths.opencode));
5197
+ }
5198
+ break;
5199
+ case "qwencode":
5200
+ if (normalizedFeatures.includes("rules")) {
5201
+ deleteTasks.push(removeDirectory(config.outputPaths.qwencode));
5202
+ }
5203
+ break;
5204
+ case "windsurf":
5205
+ if (normalizedFeatures.includes("rules")) {
5206
+ deleteTasks.push(removeDirectory(config.outputPaths.windsurf));
5207
+ }
5208
+ break;
5209
+ }
5210
+ }
5211
+ await Promise.all(deleteTasks);
5212
+ logger.info("Deleted existing output directories");
5213
+ }
5214
+ let totalOutputs = 0;
5215
+ if (normalizedFeatures.includes("rules")) {
5216
+ for (const baseDir of baseDirs) {
5217
+ logger.info(`
5218
+ Generating rule configurations for base directory: ${baseDir}`);
5219
+ const outputs = await generateConfigurations(
5220
+ rules,
5221
+ config,
5222
+ config.defaultTargets,
5223
+ baseDir
5224
+ );
5225
+ if (outputs.length === 0) {
5226
+ logger.warn(`\u26A0\uFE0F No rule configurations generated for ${baseDir}`);
5227
+ continue;
5228
+ }
5229
+ for (const output of outputs) {
5230
+ await writeFileContent(output.filepath, output.content);
5231
+ logger.success(`Generated ${output.tool} rule configuration: ${output.filepath}`);
5232
+ }
5233
+ totalOutputs += outputs.length;
4146
5234
  }
5235
+ } else {
5236
+ logger.info("\nSkipping rule generation (not in --features)");
4147
5237
  }
4148
- for (const tool of targetTools) {
4149
- switch (tool) {
4150
- case "augmentcode":
4151
- deleteTasks.push(removeDirectory((0, import_node_path13.join)(".augment", "rules")));
4152
- deleteTasks.push(removeDirectory((0, import_node_path13.join)(".augment", "ignore")));
4153
- break;
4154
- case "augmentcode-legacy":
4155
- deleteTasks.push(removeClaudeGeneratedFiles());
4156
- deleteTasks.push(removeDirectory((0, import_node_path13.join)(".augment", "ignore")));
4157
- break;
4158
- case "copilot":
4159
- deleteTasks.push(removeDirectory(config.outputPaths.copilot));
4160
- break;
4161
- case "cursor":
4162
- deleteTasks.push(removeDirectory(config.outputPaths.cursor));
4163
- break;
4164
- case "cline":
4165
- deleteTasks.push(removeDirectory(config.outputPaths.cline));
4166
- break;
4167
- case "claudecode":
4168
- deleteTasks.push(removeClaudeGeneratedFiles());
4169
- if (hasCommandFiles) {
4170
- deleteTasks.push(removeDirectory((0, import_node_path13.join)(".claude", "commands")));
5238
+ let totalMcpOutputs = 0;
5239
+ if (normalizedFeatures.includes("mcp")) {
5240
+ logger.info("\nGenerating MCP configurations...");
5241
+ for (const baseDir of baseDirs) {
5242
+ try {
5243
+ const mcpConfig = parseMcpConfig(process.cwd());
5244
+ if (!mcpConfig || !mcpConfig.mcpServers || Object.keys(mcpConfig.mcpServers).length === 0) {
5245
+ logger.info(`No MCP configuration found for ${baseDir}`);
5246
+ continue;
4171
5247
  }
4172
- break;
4173
- case "roo":
4174
- deleteTasks.push(removeDirectory(config.outputPaths.roo));
4175
- if (hasCommandFiles) {
4176
- deleteTasks.push(removeDirectory((0, import_node_path13.join)(".roo", "commands")));
5248
+ const mcpResults = await generateMcpConfigurations(
5249
+ mcpConfig,
5250
+ baseDir === process.cwd() ? "." : baseDir,
5251
+ config.defaultTargets
5252
+ );
5253
+ if (mcpResults.length === 0) {
5254
+ logger.info(`No MCP configurations generated for ${baseDir}`);
5255
+ continue;
4177
5256
  }
4178
- break;
4179
- case "geminicli":
4180
- deleteTasks.push(removeDirectory(config.outputPaths.geminicli));
4181
- if (hasCommandFiles) {
4182
- deleteTasks.push(removeDirectory((0, import_node_path13.join)(".gemini", "commands")));
5257
+ for (const result of mcpResults) {
5258
+ await writeFileContent(result.filepath, result.content);
5259
+ logger.success(`Generated ${result.tool} MCP configuration: ${result.filepath}`);
5260
+ totalMcpOutputs++;
4183
5261
  }
4184
- break;
4185
- case "kiro":
4186
- deleteTasks.push(removeDirectory(config.outputPaths.kiro));
4187
- break;
4188
- case "opencode":
4189
- deleteTasks.push(removeDirectory(config.outputPaths.opencode));
4190
- break;
4191
- case "windsurf":
4192
- deleteTasks.push(removeDirectory(config.outputPaths.windsurf));
4193
- break;
5262
+ } catch (error) {
5263
+ logger.error(
5264
+ `\u274C Failed to generate MCP configurations: ${error instanceof Error ? error.message : String(error)}`
5265
+ );
5266
+ }
4194
5267
  }
5268
+ } else {
5269
+ logger.info("\nSkipping MCP configuration generation (not in --features)");
4195
5270
  }
4196
- await Promise.all(deleteTasks);
4197
- logger.info("Deleted existing output directories");
4198
- }
4199
- let totalOutputs = 0;
4200
- for (const baseDir of baseDirs) {
4201
- logger.info(`
4202
- Generating configurations for base directory: ${baseDir}`);
4203
- const outputs = await generateConfigurations(rules, config, config.defaultTargets, baseDir);
4204
- if (outputs.length === 0) {
4205
- logger.warn(`\u26A0\uFE0F No configurations generated for ${baseDir}`);
4206
- continue;
5271
+ let totalCommandOutputs = 0;
5272
+ if (normalizedFeatures.includes("commands")) {
5273
+ logger.info("\nGenerating command files...");
5274
+ for (const baseDir of baseDirs) {
5275
+ const commandResults = await generateCommands(
5276
+ process.cwd(),
5277
+ baseDir === process.cwd() ? void 0 : baseDir,
5278
+ config.defaultTargets
5279
+ );
5280
+ if (commandResults.length === 0) {
5281
+ logger.info(`No commands found for ${baseDir}`);
5282
+ continue;
5283
+ }
5284
+ for (const result of commandResults) {
5285
+ await writeFileContent(result.filepath, result.content);
5286
+ logger.success(`Generated ${result.tool} command: ${result.filepath}`);
5287
+ totalCommandOutputs++;
5288
+ }
5289
+ }
5290
+ } else {
5291
+ logger.info("\nSkipping command file generation (not in --features)");
4207
5292
  }
4208
- for (const output of outputs) {
4209
- await writeFileContent(output.filepath, output.content);
4210
- logger.success(`Generated ${output.tool} configuration: ${output.filepath}`);
5293
+ const totalIgnoreOutputs = 0;
5294
+ if (normalizedFeatures.includes("ignore")) {
5295
+ logger.info("\nGenerating ignore files...");
5296
+ logger.info("Ignore file generation is not yet implemented");
5297
+ } else {
5298
+ logger.info("\nSkipping ignore file generation (not in --features)");
4211
5299
  }
4212
- totalOutputs += outputs.length;
4213
- }
4214
- if (totalOutputs === 0) {
4215
- logger.warn("\u26A0\uFE0F No configurations generated");
4216
- return;
4217
- }
4218
- logger.info("\nGenerating MCP configurations...");
4219
- let totalMcpOutputs = 0;
4220
- for (const baseDir of baseDirs) {
4221
- try {
4222
- const mcpConfig = parseMcpConfig(process.cwd());
4223
- if (!mcpConfig || !mcpConfig.mcpServers || Object.keys(mcpConfig.mcpServers).length === 0) {
4224
- logger.info(`No MCP configuration found for ${baseDir}`);
4225
- continue;
4226
- }
4227
- const mcpResults = await generateMcpConfigurations(
4228
- mcpConfig,
4229
- baseDir === process.cwd() ? "." : baseDir,
4230
- config.defaultTargets
4231
- );
4232
- if (mcpResults.length === 0) {
4233
- logger.info(`No MCP configurations generated for ${baseDir}`);
4234
- continue;
4235
- }
4236
- for (const result of mcpResults) {
4237
- await writeFileContent(result.filepath, result.content);
4238
- logger.success(`Generated ${result.tool} MCP configuration: ${result.filepath}`);
4239
- totalMcpOutputs++;
5300
+ let totalSubagentOutputs = 0;
5301
+ if (normalizedFeatures.includes("subagents")) {
5302
+ logger.info("\nGenerating subagent files...");
5303
+ for (const baseDir of baseDirs) {
5304
+ const subagentResults = await generateSubagents(
5305
+ config.aiRulesDir,
5306
+ baseDir === process.cwd() ? void 0 : baseDir,
5307
+ config.defaultTargets,
5308
+ rules
5309
+ );
5310
+ if (subagentResults.length === 0) {
5311
+ logger.info(`No subagents generated for ${baseDir}`);
5312
+ continue;
5313
+ }
5314
+ for (const result of subagentResults) {
5315
+ await writeFileContent(result.filepath, result.content);
5316
+ logger.success(`Generated ${result.tool} subagent: ${result.filepath}`);
5317
+ totalSubagentOutputs++;
5318
+ }
4240
5319
  }
4241
- } catch (error) {
4242
- logger.error(
4243
- `\u274C Failed to generate MCP configurations: ${error instanceof Error ? error.message : String(error)}`
4244
- );
5320
+ } else {
5321
+ logger.info("\nSkipping subagent file generation (not in --features)");
4245
5322
  }
4246
- }
4247
- logger.info("\nGenerating command files...");
4248
- let totalCommandOutputs = 0;
4249
- for (const baseDir of baseDirs) {
4250
- const commandResults = await generateCommands(
4251
- process.cwd(),
4252
- baseDir === process.cwd() ? void 0 : baseDir,
4253
- config.defaultTargets
4254
- );
4255
- if (commandResults.length === 0) {
4256
- logger.info(`No commands found for ${baseDir}`);
4257
- continue;
4258
- }
4259
- for (const result of commandResults) {
4260
- await writeFileContent(result.filepath, result.content);
4261
- logger.success(`Generated ${result.tool} command: ${result.filepath}`);
4262
- totalCommandOutputs++;
4263
- }
4264
- }
4265
- const totalGenerated = totalOutputs + totalMcpOutputs + totalCommandOutputs;
4266
- if (totalGenerated > 0) {
4267
- const parts = [];
4268
- if (totalOutputs > 0) parts.push(`${totalOutputs} configurations`);
4269
- if (totalMcpOutputs > 0) parts.push(`${totalMcpOutputs} MCP configurations`);
4270
- if (totalCommandOutputs > 0) parts.push(`${totalCommandOutputs} commands`);
4271
- logger.success(
4272
- `
5323
+ const totalGenerated = totalOutputs + totalMcpOutputs + totalCommandOutputs + totalIgnoreOutputs + totalSubagentOutputs;
5324
+ if (totalGenerated === 0) {
5325
+ const enabledFeatures = normalizedFeatures.join(", ");
5326
+ logger.warn(`\u26A0\uFE0F No files generated for enabled features: ${enabledFeatures}`);
5327
+ return;
5328
+ }
5329
+ if (totalGenerated > 0) {
5330
+ const parts = [];
5331
+ if (totalOutputs > 0) parts.push(`${totalOutputs} configurations`);
5332
+ if (totalMcpOutputs > 0) parts.push(`${totalMcpOutputs} MCP configurations`);
5333
+ if (totalCommandOutputs > 0) parts.push(`${totalCommandOutputs} commands`);
5334
+ if (totalSubagentOutputs > 0) parts.push(`${totalSubagentOutputs} subagents`);
5335
+ logger.success(
5336
+ `
4273
5337
  \u{1F389} All done! Generated ${totalGenerated} file(s) total (${parts.join(" + ")})`
4274
- );
5338
+ );
5339
+ }
5340
+ } catch (error) {
5341
+ logger.error("\u274C Failed to generate configurations:", error);
5342
+ process.exit(1);
4275
5343
  }
4276
5344
  } catch (error) {
4277
- logger.error("\u274C Failed to generate configurations:", error);
5345
+ logger.error("\u274C Failed to resolve configuration:", error);
4278
5346
  process.exit(1);
4279
5347
  }
4280
5348
  }
4281
5349
 
4282
5350
  // src/cli/commands/gitignore.ts
4283
5351
  var import_node_fs2 = require("fs");
4284
- var import_node_path14 = require("path");
5352
+ var import_node_path19 = require("path");
4285
5353
  init_logger();
4286
5354
  var gitignoreCommand = async () => {
4287
- const gitignorePath = (0, import_node_path14.join)(process.cwd(), ".gitignore");
5355
+ const gitignorePath = (0, import_node_path19.join)(process.cwd(), ".gitignore");
4288
5356
  const rulesFilesToIgnore = [
4289
5357
  "# Generated by rulesync - AI tool configuration files",
4290
5358
  "**/.amazonq/rules/",
@@ -4298,7 +5366,9 @@ var gitignoreCommand = async () => {
4298
5366
  "**/CLAUDE.md",
4299
5367
  "**/.claude/memories/",
4300
5368
  "**/.claude/commands/",
5369
+ "**/.claude/agents/",
4301
5370
  "**/AGENTS.md",
5371
+ "**/.agents/",
4302
5372
  "**/.codexignore",
4303
5373
  "**/.roo/rules/",
4304
5374
  "**/.rooignore",
@@ -4306,6 +5376,8 @@ var gitignoreCommand = async () => {
4306
5376
  "**/GEMINI.md",
4307
5377
  "**/.gemini/memories/",
4308
5378
  "**/.gemini/commands/",
5379
+ "**/QWEN.md",
5380
+ "**/.qwen/memories/",
4309
5381
  "**/.aiexclude",
4310
5382
  "**/.aiignore",
4311
5383
  "**/.augmentignore",
@@ -4324,6 +5396,7 @@ var gitignoreCommand = async () => {
4324
5396
  "**/.vscode/mcp.json",
4325
5397
  "**/.codex/mcp-config.json",
4326
5398
  "**/.gemini/settings.json",
5399
+ "**/.qwen/settings.json",
4327
5400
  "**/.roo/mcp.json"
4328
5401
  ];
4329
5402
  let gitignoreContent = "";
@@ -4355,11 +5428,155 @@ ${linesToAdd.join("\n")}
4355
5428
  };
4356
5429
 
4357
5430
  // src/core/importer.ts
4358
- var import_node_path21 = require("path");
4359
- var import_gray_matter2 = __toESM(require("gray-matter"), 1);
5431
+ var import_node_path28 = require("path");
5432
+ var import_gray_matter3 = __toESM(require("gray-matter"), 1);
5433
+
5434
+ // src/parsers/agentsmd.ts
5435
+ var import_node_path20 = require("path");
5436
+ async function parseAgentsMdConfiguration(baseDir = process.cwd()) {
5437
+ const errors = [];
5438
+ const rules = [];
5439
+ const projectAgentsPath = (0, import_node_path20.join)(baseDir, "AGENTS.md");
5440
+ if (await fileExists(projectAgentsPath)) {
5441
+ try {
5442
+ const content = await readFileContent(projectAgentsPath);
5443
+ if (content.trim()) {
5444
+ const frontmatter = {
5445
+ root: true,
5446
+ targets: ["agentsmd"],
5447
+ description: "Project-level AGENTS.md instructions",
5448
+ globs: ["**/*"]
5449
+ };
5450
+ rules.push({
5451
+ frontmatter,
5452
+ content: content.trim(),
5453
+ filename: "project-instructions",
5454
+ filepath: projectAgentsPath
5455
+ });
5456
+ }
5457
+ } catch (error) {
5458
+ const errorMessage = error instanceof Error ? error.message : String(error);
5459
+ errors.push(`Failed to parse AGENTS.md: ${errorMessage}`);
5460
+ }
5461
+ }
5462
+ const memoriesDir = (0, import_node_path20.join)(baseDir, ".agents", "memories");
5463
+ if (await fileExists(memoriesDir)) {
5464
+ try {
5465
+ const { readdir: readdir3, stat: stat2 } = await import("fs/promises");
5466
+ const memoriesPath = memoriesDir;
5467
+ const memoriesStat = await stat2(memoriesPath);
5468
+ if (memoriesStat.isDirectory()) {
5469
+ const files = await readdir3(memoriesPath);
5470
+ for (const file of files) {
5471
+ if (file.endsWith(".md")) {
5472
+ const filePath = (0, import_node_path20.join)(memoriesPath, file);
5473
+ try {
5474
+ const content = await readFileContent(filePath);
5475
+ if (content.trim()) {
5476
+ const filename = file.replace(/\.md$/, "");
5477
+ const frontmatter = {
5478
+ root: false,
5479
+ targets: ["agentsmd"],
5480
+ description: `AGENTS.md memory: ${filename}`,
5481
+ globs: ["**/*"]
5482
+ };
5483
+ rules.push({
5484
+ frontmatter,
5485
+ content: content.trim(),
5486
+ filename,
5487
+ filepath: filePath
5488
+ });
5489
+ }
5490
+ } catch (error) {
5491
+ const errorMessage = error instanceof Error ? error.message : String(error);
5492
+ errors.push(`Failed to parse memory file ${file}: ${errorMessage}`);
5493
+ }
5494
+ }
5495
+ }
5496
+ }
5497
+ } catch {
5498
+ }
5499
+ }
5500
+ try {
5501
+ const { readdir: readdir3 } = await import("fs/promises");
5502
+ const files = await readdir3(baseDir);
5503
+ for (const file of files) {
5504
+ if (file === "AGENTS.md") continue;
5505
+ if (file.endsWith(".md") && (file.includes("agents") || file.includes("instructions") || file.includes("guidelines") || file.includes("rules"))) {
5506
+ const filePath = (0, import_node_path20.join)(baseDir, file);
5507
+ try {
5508
+ const content = await readFileContent(filePath);
5509
+ if (content.trim()) {
5510
+ const filename = file.replace(/\.md$/, "");
5511
+ const frontmatter = {
5512
+ root: false,
5513
+ targets: ["agentsmd"],
5514
+ description: `AGENTS.md instructions: ${filename}`,
5515
+ globs: ["**/*"]
5516
+ };
5517
+ rules.push({
5518
+ frontmatter,
5519
+ content: content.trim(),
5520
+ filename,
5521
+ filepath: filePath
5522
+ });
5523
+ }
5524
+ } catch (error) {
5525
+ const errorMessage = error instanceof Error ? error.message : String(error);
5526
+ errors.push(`Failed to parse ${file}: ${errorMessage}`);
5527
+ }
5528
+ }
5529
+ }
5530
+ for (const file of files) {
5531
+ const filePath = (0, import_node_path20.join)(baseDir, file);
5532
+ try {
5533
+ const { stat: stat2 } = await import("fs/promises");
5534
+ const stats = await stat2(filePath);
5535
+ if (stats.isDirectory() && !file.startsWith(".") && file !== "node_modules") {
5536
+ const subAgentsPath = (0, import_node_path20.join)(filePath, "AGENTS.md");
5537
+ if (await fileExists(subAgentsPath)) {
5538
+ try {
5539
+ const content = await readFileContent(subAgentsPath);
5540
+ if (content.trim()) {
5541
+ const frontmatter = {
5542
+ root: false,
5543
+ targets: ["agentsmd"],
5544
+ description: `Directory-specific AGENTS.md instructions: ${file}`,
5545
+ globs: [`${file}/**/*`]
5546
+ };
5547
+ rules.push({
5548
+ frontmatter,
5549
+ content: content.trim(),
5550
+ filename: `${file}-agents`,
5551
+ filepath: subAgentsPath
5552
+ });
5553
+ }
5554
+ } catch (error) {
5555
+ const errorMessage = error instanceof Error ? error.message : String(error);
5556
+ errors.push(`Failed to parse ${subAgentsPath}: ${errorMessage}`);
5557
+ }
5558
+ }
5559
+ }
5560
+ } catch {
5561
+ }
5562
+ }
5563
+ } catch (error) {
5564
+ const errorMessage = error instanceof Error ? error.message : String(error);
5565
+ errors.push(`Failed to scan directory for AGENTS.md files: ${errorMessage}`);
5566
+ }
5567
+ if (rules.length === 0) {
5568
+ errors.push(
5569
+ "No AGENTS.md configuration files found. Expected to find AGENTS.md in the project root or memory files in .agents/memories/."
5570
+ );
5571
+ }
5572
+ return {
5573
+ rules,
5574
+ errors
5575
+ };
5576
+ }
4360
5577
 
4361
5578
  // src/parsers/shared-helpers.ts
4362
- var import_node_path15 = require("path");
5579
+ var import_node_path21 = require("path");
4363
5580
  init_file();
4364
5581
  async function parseConfigurationFiles(baseDir = process.cwd(), config) {
4365
5582
  const errors = [];
@@ -4413,11 +5630,11 @@ async function parseConfigurationFiles(baseDir = process.cwd(), config) {
4413
5630
  const dirPath = resolvePath(dirConfig.directory, baseDir);
4414
5631
  if (await fileExists(dirPath)) {
4415
5632
  const result = await safeAsyncOperation(async () => {
4416
- const { readdir: readdir2 } = await import("fs/promises");
4417
- const files = await readdir2(dirPath);
5633
+ const { readdir: readdir3 } = await import("fs/promises");
5634
+ const files = await readdir3(dirPath);
4418
5635
  for (const file of files) {
4419
5636
  if (file.endsWith(dirConfig.filePattern)) {
4420
- const filePath = (0, import_node_path15.join)(dirPath, file);
5637
+ const filePath = (0, import_node_path21.join)(dirPath, file);
4421
5638
  const fileResult = await safeAsyncOperation(async () => {
4422
5639
  const rawContent = await readFileContent(filePath);
4423
5640
  let content;
@@ -4564,14 +5781,14 @@ function parseMainFile(content, filepath, config) {
4564
5781
  async function parseMemoryFiles(memoryDir, config) {
4565
5782
  const rules = [];
4566
5783
  try {
4567
- const { readdir: readdir2 } = await import("fs/promises");
4568
- const files = await readdir2(memoryDir);
5784
+ const { readdir: readdir3 } = await import("fs/promises");
5785
+ const files = await readdir3(memoryDir);
4569
5786
  for (const file of files) {
4570
5787
  if (file.endsWith(".md")) {
4571
- const filePath = (0, import_node_path15.join)(memoryDir, file);
5788
+ const filePath = (0, import_node_path21.join)(memoryDir, file);
4572
5789
  const content = await readFileContent(filePath);
4573
5790
  if (content.trim()) {
4574
- const filename = (0, import_node_path15.basename)(file, ".md");
5791
+ const filename = (0, import_node_path21.basename)(file, ".md");
4575
5792
  const frontmatter = {
4576
5793
  root: false,
4577
5794
  targets: [config.tool],
@@ -4594,14 +5811,14 @@ async function parseMemoryFiles(memoryDir, config) {
4594
5811
  async function parseCommandsFiles(commandsDir, config) {
4595
5812
  const rules = [];
4596
5813
  try {
4597
- const { readdir: readdir2 } = await import("fs/promises");
4598
- const files = await readdir2(commandsDir);
5814
+ const { readdir: readdir3 } = await import("fs/promises");
5815
+ const files = await readdir3(commandsDir);
4599
5816
  for (const file of files) {
4600
5817
  if (file.endsWith(".md")) {
4601
- const filePath = (0, import_node_path15.join)(commandsDir, file);
5818
+ const filePath = (0, import_node_path21.join)(commandsDir, file);
4602
5819
  const content = await readFileContent(filePath);
4603
5820
  if (content.trim()) {
4604
- const filename = (0, import_node_path15.basename)(file, ".md");
5821
+ const filename = (0, import_node_path21.basename)(file, ".md");
4605
5822
  let frontmatter;
4606
5823
  let ruleContent;
4607
5824
  try {
@@ -4690,7 +5907,7 @@ async function parseAmazonqcliConfiguration(baseDir = process.cwd()) {
4690
5907
  }
4691
5908
 
4692
5909
  // src/parsers/augmentcode.ts
4693
- var import_node_path16 = require("path");
5910
+ var import_node_path22 = require("path");
4694
5911
 
4695
5912
  // src/utils/parser-helpers.ts
4696
5913
  function createParseResult() {
@@ -4738,7 +5955,7 @@ async function parseAugmentcodeLegacyConfiguration(baseDir = process.cwd()) {
4738
5955
  async function parseUnifiedAugmentcode(baseDir, config) {
4739
5956
  const result = createParseResult();
4740
5957
  if (config.rulesDir) {
4741
- const rulesDir = (0, import_node_path16.join)(baseDir, config.rulesDir);
5958
+ const rulesDir = (0, import_node_path22.join)(baseDir, config.rulesDir);
4742
5959
  if (await fileExists(rulesDir)) {
4743
5960
  const rulesResult = await parseAugmentRules(rulesDir, config);
4744
5961
  addRules(result, rulesResult.rules);
@@ -4751,7 +5968,7 @@ async function parseUnifiedAugmentcode(baseDir, config) {
4751
5968
  }
4752
5969
  }
4753
5970
  if (config.legacyFilePath) {
4754
- const legacyPath = (0, import_node_path16.join)(baseDir, config.legacyFilePath);
5971
+ const legacyPath = (0, import_node_path22.join)(baseDir, config.legacyFilePath);
4755
5972
  if (await fileExists(legacyPath)) {
4756
5973
  const legacyResult = await parseAugmentGuidelines(legacyPath, config);
4757
5974
  if (legacyResult.rule) {
@@ -4771,11 +5988,11 @@ async function parseAugmentRules(rulesDir, config) {
4771
5988
  const rules = [];
4772
5989
  const errors = [];
4773
5990
  try {
4774
- const { readdir: readdir2 } = await import("fs/promises");
4775
- const files = await readdir2(rulesDir);
5991
+ const { readdir: readdir3 } = await import("fs/promises");
5992
+ const files = await readdir3(rulesDir);
4776
5993
  for (const file of files) {
4777
5994
  if (file.endsWith(".md") || file.endsWith(".mdc")) {
4778
- const filePath = (0, import_node_path16.join)(rulesDir, file);
5995
+ const filePath = (0, import_node_path22.join)(rulesDir, file);
4779
5996
  try {
4780
5997
  const rawContent = await readFileContent(filePath);
4781
5998
  const parsed = parseFrontmatter(rawContent);
@@ -4783,7 +6000,7 @@ async function parseAugmentRules(rulesDir, config) {
4783
6000
  const description = extractStringField(parsed.data, "description", "");
4784
6001
  const tags = extractArrayField(parsed.data, "tags");
4785
6002
  const isRoot = ruleType === "always";
4786
- const filename = (0, import_node_path16.basename)(file, file.endsWith(".mdc") ? ".mdc" : ".md");
6003
+ const filename = (0, import_node_path22.basename)(file, file.endsWith(".mdc") ? ".mdc" : ".md");
4787
6004
  const frontmatter = {
4788
6005
  root: isRoot,
4789
6006
  targets: [config.targetName],
@@ -4841,8 +6058,9 @@ async function parseAugmentGuidelines(guidelinesPath, config) {
4841
6058
  }
4842
6059
 
4843
6060
  // src/parsers/claudecode.ts
6061
+ init_file();
4844
6062
  async function parseClaudeConfiguration(baseDir = process.cwd()) {
4845
- return parseMemoryBasedConfiguration(baseDir, {
6063
+ const memoryResult = await parseMemoryBasedConfiguration(baseDir, {
4846
6064
  tool: "claudecode",
4847
6065
  mainFileName: "CLAUDE.md",
4848
6066
  memoryDirPath: ".claude/memories",
@@ -4852,6 +6070,24 @@ async function parseClaudeConfiguration(baseDir = process.cwd()) {
4852
6070
  filenamePrefix: "claude",
4853
6071
  commandsDirPath: ".claude/commands"
4854
6072
  });
6073
+ const result = {
6074
+ rules: memoryResult.rules,
6075
+ errors: memoryResult.errors
6076
+ };
6077
+ if (memoryResult.ignorePatterns) {
6078
+ result.ignorePatterns = memoryResult.ignorePatterns;
6079
+ }
6080
+ if (memoryResult.mcpServers) {
6081
+ result.mcpServers = memoryResult.mcpServers;
6082
+ }
6083
+ const agentsDir = resolvePath(".claude/agents", baseDir);
6084
+ if (await fileExists(agentsDir)) {
6085
+ const subagents = await parseSubagentsFromDirectory(agentsDir);
6086
+ if (subagents.length > 0) {
6087
+ result.subagents = subagents;
6088
+ }
6089
+ }
6090
+ return result;
4855
6091
  }
4856
6092
 
4857
6093
  // src/parsers/cline.ts
@@ -4875,7 +6111,7 @@ async function parseClineConfiguration(baseDir = process.cwd()) {
4875
6111
  }
4876
6112
 
4877
6113
  // src/parsers/codexcli.ts
4878
- var import_node_path17 = require("path");
6114
+ var import_node_path23 = require("path");
4879
6115
 
4880
6116
  // src/parsers/copilot.ts
4881
6117
  async function parseCopilotConfiguration(baseDir = process.cwd()) {
@@ -4898,9 +6134,9 @@ async function parseCopilotConfiguration(baseDir = process.cwd()) {
4898
6134
  }
4899
6135
 
4900
6136
  // src/parsers/cursor.ts
4901
- var import_node_path18 = require("path");
6137
+ var import_node_path24 = require("path");
4902
6138
  var import_js_yaml = require("js-yaml");
4903
- var import_mini8 = require("zod/mini");
6139
+ var import_mini9 = require("zod/mini");
4904
6140
  var customMatterOptions = {
4905
6141
  engines: {
4906
6142
  yaml: {
@@ -4928,7 +6164,7 @@ var customMatterOptions = {
4928
6164
  }
4929
6165
  };
4930
6166
  function convertCursorMdcFrontmatter(cursorFrontmatter, _filename) {
4931
- const FrontmatterSchema = import_mini8.z.record(import_mini8.z.string(), import_mini8.z.unknown());
6167
+ const FrontmatterSchema = import_mini9.z.record(import_mini9.z.string(), import_mini9.z.unknown());
4932
6168
  const parseResult = FrontmatterSchema.safeParse(cursorFrontmatter);
4933
6169
  if (!parseResult.success) {
4934
6170
  return {
@@ -5022,7 +6258,7 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
5022
6258
  const rules = [];
5023
6259
  let ignorePatterns;
5024
6260
  let mcpServers;
5025
- const cursorFilePath = (0, import_node_path18.join)(baseDir, ".cursorrules");
6261
+ const cursorFilePath = (0, import_node_path24.join)(baseDir, ".cursorrules");
5026
6262
  if (await fileExists(cursorFilePath)) {
5027
6263
  try {
5028
6264
  const rawContent = await readFileContent(cursorFilePath);
@@ -5043,20 +6279,20 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
5043
6279
  errors.push(`Failed to parse .cursorrules file: ${errorMessage}`);
5044
6280
  }
5045
6281
  }
5046
- const cursorRulesDir = (0, import_node_path18.join)(baseDir, ".cursor", "rules");
6282
+ const cursorRulesDir = (0, import_node_path24.join)(baseDir, ".cursor", "rules");
5047
6283
  if (await fileExists(cursorRulesDir)) {
5048
6284
  try {
5049
- const { readdir: readdir2 } = await import("fs/promises");
5050
- const files = await readdir2(cursorRulesDir);
6285
+ const { readdir: readdir3 } = await import("fs/promises");
6286
+ const files = await readdir3(cursorRulesDir);
5051
6287
  for (const file of files) {
5052
6288
  if (file.endsWith(".mdc")) {
5053
- const filePath = (0, import_node_path18.join)(cursorRulesDir, file);
6289
+ const filePath = (0, import_node_path24.join)(cursorRulesDir, file);
5054
6290
  try {
5055
6291
  const rawContent = await readFileContent(filePath);
5056
6292
  const parsed = parseFrontmatter(rawContent, { matterOptions: customMatterOptions });
5057
6293
  const content = parsed.content;
5058
6294
  if (content) {
5059
- const filename = (0, import_node_path18.basename)(file, ".mdc");
6295
+ const filename = (0, import_node_path24.basename)(file, ".mdc");
5060
6296
  const frontmatter = convertCursorMdcFrontmatter(parsed.data, filename);
5061
6297
  rules.push({
5062
6298
  frontmatter,
@@ -5079,7 +6315,7 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
5079
6315
  if (rules.length === 0) {
5080
6316
  errors.push("No Cursor configuration files found (.cursorrules or .cursor/rules/*.mdc)");
5081
6317
  }
5082
- const cursorIgnorePath = (0, import_node_path18.join)(baseDir, ".cursorignore");
6318
+ const cursorIgnorePath = (0, import_node_path24.join)(baseDir, ".cursorignore");
5083
6319
  if (await fileExists(cursorIgnorePath)) {
5084
6320
  try {
5085
6321
  const content = await readFileContent(cursorIgnorePath);
@@ -5092,7 +6328,7 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
5092
6328
  errors.push(`Failed to parse .cursorignore: ${errorMessage}`);
5093
6329
  }
5094
6330
  }
5095
- const cursorMcpPath = (0, import_node_path18.join)(baseDir, ".cursor", "mcp.json");
6331
+ const cursorMcpPath = (0, import_node_path24.join)(baseDir, ".cursor", "mcp.json");
5096
6332
  if (await fileExists(cursorMcpPath)) {
5097
6333
  try {
5098
6334
  const content = await readFileContent(cursorMcpPath);
@@ -5115,6 +6351,7 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
5115
6351
  }
5116
6352
 
5117
6353
  // src/parsers/geminicli.ts
6354
+ var import_node_path25 = require("path");
5118
6355
  async function parseAiexclude(aiexcludePath) {
5119
6356
  try {
5120
6357
  const content = await readFileContent(aiexcludePath);
@@ -5124,8 +6361,51 @@ async function parseAiexclude(aiexcludePath) {
5124
6361
  return [];
5125
6362
  }
5126
6363
  }
6364
+ async function parseGeminiCommands(commandsDir) {
6365
+ const rules = [];
6366
+ try {
6367
+ const { readdir: readdir3 } = await import("fs/promises");
6368
+ const { parse } = await import("smol-toml");
6369
+ const files = await readdir3(commandsDir);
6370
+ for (const file of files) {
6371
+ if (file.endsWith(".toml")) {
6372
+ const filePath = (0, import_node_path25.join)(commandsDir, file);
6373
+ const content = await readFileContent(filePath);
6374
+ if (content.trim()) {
6375
+ const filename = (0, import_node_path25.basename)(file, ".toml");
6376
+ try {
6377
+ const parsed = parse(content);
6378
+ if (typeof parsed !== "object" || parsed === null) {
6379
+ continue;
6380
+ }
6381
+ const commandConfig = parsed;
6382
+ if (typeof commandConfig.prompt === "string") {
6383
+ const description = typeof commandConfig.description === "string" ? commandConfig.description : `Command: ${filename}`;
6384
+ const frontmatter = {
6385
+ root: false,
6386
+ targets: ["geminicli"],
6387
+ description,
6388
+ globs: ["**/*"]
6389
+ };
6390
+ rules.push({
6391
+ frontmatter,
6392
+ content: commandConfig.prompt,
6393
+ filename,
6394
+ filepath: filePath,
6395
+ type: "command"
6396
+ });
6397
+ }
6398
+ } catch {
6399
+ }
6400
+ }
6401
+ }
6402
+ }
6403
+ } catch {
6404
+ }
6405
+ return rules;
6406
+ }
5127
6407
  async function parseGeminiConfiguration(baseDir = process.cwd()) {
5128
- return parseMemoryBasedConfiguration(baseDir, {
6408
+ const result = await parseMemoryBasedConfiguration(baseDir, {
5129
6409
  tool: "geminicli",
5130
6410
  mainFileName: "GEMINI.md",
5131
6411
  memoryDirPath: ".gemini/memories",
@@ -5136,17 +6416,23 @@ async function parseGeminiConfiguration(baseDir = process.cwd()) {
5136
6416
  additionalIgnoreFile: {
5137
6417
  path: ".aiexclude",
5138
6418
  parser: parseAiexclude
5139
- },
5140
- commandsDirPath: ".gemini/commands"
6419
+ }
6420
+ // commandsDirPath is removed - Gemini uses .toml files which need special handling
5141
6421
  });
6422
+ const commandsDir = resolvePath(".gemini/commands", baseDir);
6423
+ if (await fileExists(commandsDir)) {
6424
+ const commandsRules = await parseGeminiCommands(commandsDir);
6425
+ result.rules.push(...commandsRules);
6426
+ }
6427
+ return result;
5142
6428
  }
5143
6429
 
5144
6430
  // src/parsers/junie.ts
5145
- var import_node_path19 = require("path");
6431
+ var import_node_path26 = require("path");
5146
6432
  async function parseJunieConfiguration(baseDir = process.cwd()) {
5147
6433
  const errors = [];
5148
6434
  const rules = [];
5149
- const guidelinesPath = (0, import_node_path19.join)(baseDir, ".junie", "guidelines.md");
6435
+ const guidelinesPath = (0, import_node_path26.join)(baseDir, ".junie", "guidelines.md");
5150
6436
  if (!await fileExists(guidelinesPath)) {
5151
6437
  errors.push(".junie/guidelines.md file not found");
5152
6438
  return { rules, errors };
@@ -5203,6 +6489,22 @@ async function parseOpenCodeConfiguration(baseDir = process.cwd()) {
5203
6489
  });
5204
6490
  }
5205
6491
 
6492
+ // src/parsers/qwencode.ts
6493
+ async function parseQwenConfiguration(baseDir = process.cwd()) {
6494
+ return parseMemoryBasedConfiguration(baseDir, {
6495
+ tool: "qwencode",
6496
+ mainFileName: "QWEN.md",
6497
+ memoryDirPath: ".qwen/memories",
6498
+ settingsPath: ".qwen/settings.json",
6499
+ mainDescription: "Main Qwen Code configuration",
6500
+ memoryDescription: "Memory file",
6501
+ filenamePrefix: "qwen",
6502
+ // Qwen Code uses git-aware filtering instead of dedicated ignore files
6503
+ // additionalIgnoreFile is omitted
6504
+ commandsDirPath: ".qwen/commands"
6505
+ });
6506
+ }
6507
+
5206
6508
  // src/parsers/roo.ts
5207
6509
  async function parseRooConfiguration(baseDir = process.cwd()) {
5208
6510
  return parseConfigurationFiles(baseDir, {
@@ -5224,8 +6526,8 @@ async function parseRooConfiguration(baseDir = process.cwd()) {
5224
6526
  }
5225
6527
 
5226
6528
  // src/parsers/windsurf.ts
5227
- var import_promises3 = require("fs/promises");
5228
- var import_node_path20 = require("path");
6529
+ var import_promises4 = require("fs/promises");
6530
+ var import_node_path27 = require("path");
5229
6531
  init_logger();
5230
6532
 
5231
6533
  // src/core/importer.ts
@@ -5233,6 +6535,8 @@ init_logger();
5233
6535
  async function importConfiguration(options) {
5234
6536
  const {
5235
6537
  tool,
6538
+ features = ["rules", "commands", "mcp", "ignore", "subagents"],
6539
+ // Default to all features for backward compatibility
5236
6540
  baseDir = process.cwd(),
5237
6541
  rulesDir = ".rulesync",
5238
6542
  verbose = false,
@@ -5242,11 +6546,18 @@ async function importConfiguration(options) {
5242
6546
  let rules = [];
5243
6547
  let ignorePatterns;
5244
6548
  let mcpServers;
6549
+ let subagents;
5245
6550
  if (verbose) {
5246
6551
  logger.log(`Importing ${tool} configuration from ${baseDir}...`);
5247
6552
  }
5248
6553
  try {
5249
6554
  switch (tool) {
6555
+ case "agentsmd": {
6556
+ const agentsmdResult = await parseAgentsMdConfiguration(baseDir);
6557
+ rules = agentsmdResult.rules;
6558
+ errors.push(...agentsmdResult.errors);
6559
+ break;
6560
+ }
5250
6561
  case "amazonqcli": {
5251
6562
  const amazonqResult = await parseAmazonqcliConfiguration(baseDir);
5252
6563
  rules = amazonqResult.rules;
@@ -5272,6 +6583,7 @@ async function importConfiguration(options) {
5272
6583
  errors.push(...claudeResult.errors);
5273
6584
  ignorePatterns = claudeResult.ignorePatterns;
5274
6585
  mcpServers = claudeResult.mcpServers;
6586
+ subagents = claudeResult.subagents;
5275
6587
  break;
5276
6588
  }
5277
6589
  case "cursor": {
@@ -5322,6 +6634,13 @@ async function importConfiguration(options) {
5322
6634
  mcpServers = opencodeResult.mcpServers;
5323
6635
  break;
5324
6636
  }
6637
+ case "qwencode": {
6638
+ const qwenResult = await parseQwenConfiguration(baseDir);
6639
+ rules = qwenResult.rules;
6640
+ errors.push(...qwenResult.errors);
6641
+ mcpServers = qwenResult.mcpServers;
6642
+ break;
6643
+ }
5325
6644
  default:
5326
6645
  errors.push(`Unsupported tool: ${tool}`);
5327
6646
  return { success: false, rulesCreated: 0, errors };
@@ -5331,10 +6650,20 @@ async function importConfiguration(options) {
5331
6650
  errors.push(`Failed to parse ${tool} configuration: ${errorMessage}`);
5332
6651
  return { success: false, rulesCreated: 0, errors };
5333
6652
  }
5334
- if (rules.length === 0 && !ignorePatterns && !mcpServers) {
6653
+ if (rules.length === 0 && !ignorePatterns && !mcpServers && !subagents) {
5335
6654
  return { success: false, rulesCreated: 0, errors };
5336
6655
  }
5337
- const rulesDirPath = (0, import_node_path21.join)(baseDir, rulesDir);
6656
+ const rulesEnabled = features.includes("rules") || features.includes("commands");
6657
+ const ignoreEnabled = features.includes("ignore");
6658
+ const mcpEnabled = features.includes("mcp");
6659
+ const subagentsEnabled = features.includes("subagents");
6660
+ if (!rulesEnabled && !ignoreEnabled && !mcpEnabled && !subagentsEnabled) {
6661
+ if (verbose) {
6662
+ logger.log("No relevant features enabled for import");
6663
+ }
6664
+ return { success: false, rulesCreated: 0, errors: ["No features enabled for import"] };
6665
+ }
6666
+ const rulesDirPath = (0, import_node_path28.join)(baseDir, rulesDir);
5338
6667
  try {
5339
6668
  const { mkdir: mkdir3 } = await import("fs/promises");
5340
6669
  await mkdir3(rulesDirPath, { recursive: true });
@@ -5344,37 +6673,49 @@ async function importConfiguration(options) {
5344
6673
  return { success: false, rulesCreated: 0, errors };
5345
6674
  }
5346
6675
  let rulesCreated = 0;
5347
- for (const rule of rules) {
5348
- try {
5349
- const baseFilename = rule.filename;
5350
- let targetDir = rulesDirPath;
5351
- if (rule.type === "command") {
5352
- targetDir = (0, import_node_path21.join)(rulesDirPath, "commands");
5353
- const { mkdir: mkdir3 } = await import("fs/promises");
5354
- await mkdir3(targetDir, { recursive: true });
5355
- } else {
5356
- if (!useLegacyLocation) {
5357
- targetDir = (0, import_node_path21.join)(rulesDirPath, "rules");
6676
+ if (rulesEnabled) {
6677
+ for (const rule of rules) {
6678
+ try {
6679
+ const baseFilename = rule.filename;
6680
+ let targetDir = rulesDirPath;
6681
+ if (rule.type === "command") {
6682
+ if (!features.includes("commands")) {
6683
+ continue;
6684
+ }
6685
+ targetDir = (0, import_node_path28.join)(rulesDirPath, "commands");
5358
6686
  const { mkdir: mkdir3 } = await import("fs/promises");
5359
6687
  await mkdir3(targetDir, { recursive: true });
6688
+ } else {
6689
+ if (!features.includes("rules")) {
6690
+ continue;
6691
+ }
6692
+ if (!useLegacyLocation) {
6693
+ targetDir = (0, import_node_path28.join)(rulesDirPath, "rules");
6694
+ const { mkdir: mkdir3 } = await import("fs/promises");
6695
+ await mkdir3(targetDir, { recursive: true });
6696
+ }
5360
6697
  }
6698
+ const filePath = (0, import_node_path28.join)(targetDir, `${baseFilename}.md`);
6699
+ const content = generateRuleFileContent(rule);
6700
+ await writeFileContent(filePath, content);
6701
+ rulesCreated++;
6702
+ if (verbose) {
6703
+ logger.success(`Created rule file: ${filePath}`);
6704
+ }
6705
+ } catch (error) {
6706
+ const errorMessage = error instanceof Error ? error.message : String(error);
6707
+ errors.push(`Failed to create rule file for ${rule.filename}: ${errorMessage}`);
5361
6708
  }
5362
- const filePath = (0, import_node_path21.join)(targetDir, `${baseFilename}.md`);
5363
- const content = generateRuleFileContent(rule);
5364
- await writeFileContent(filePath, content);
5365
- rulesCreated++;
5366
- if (verbose) {
5367
- logger.success(`Created rule file: ${filePath}`);
5368
- }
5369
- } catch (error) {
5370
- const errorMessage = error instanceof Error ? error.message : String(error);
5371
- errors.push(`Failed to create rule file for ${rule.filename}: ${errorMessage}`);
6709
+ }
6710
+ } else {
6711
+ if (verbose && rules.length > 0) {
6712
+ logger.log(`Skipping ${rules.length} rule(s) (rules/commands features not enabled)`);
5372
6713
  }
5373
6714
  }
5374
6715
  let ignoreFileCreated = false;
5375
- if (ignorePatterns && ignorePatterns.length > 0) {
6716
+ if (ignoreEnabled && ignorePatterns && ignorePatterns.length > 0) {
5376
6717
  try {
5377
- const rulesyncignorePath = (0, import_node_path21.join)(baseDir, ".rulesyncignore");
6718
+ const rulesyncignorePath = (0, import_node_path28.join)(baseDir, ".rulesyncignore");
5378
6719
  const ignoreContent = `${ignorePatterns.join("\n")}
5379
6720
  `;
5380
6721
  await writeFileContent(rulesyncignorePath, ignoreContent);
@@ -5386,11 +6727,13 @@ async function importConfiguration(options) {
5386
6727
  const errorMessage = error instanceof Error ? error.message : String(error);
5387
6728
  errors.push(`Failed to create .rulesyncignore: ${errorMessage}`);
5388
6729
  }
6730
+ } else if (verbose && ignorePatterns && ignorePatterns.length > 0 && !ignoreEnabled) {
6731
+ logger.log(`Skipping ignore patterns (ignore feature not enabled)`);
5389
6732
  }
5390
6733
  let mcpFileCreated = false;
5391
- if (mcpServers && Object.keys(mcpServers).length > 0) {
6734
+ if (mcpEnabled && mcpServers && Object.keys(mcpServers).length > 0) {
5392
6735
  try {
5393
- const mcpPath = (0, import_node_path21.join)(baseDir, rulesDir, ".mcp.json");
6736
+ const mcpPath = (0, import_node_path28.join)(baseDir, rulesDir, ".mcp.json");
5394
6737
  const mcpContent = `${JSON.stringify({ mcpServers }, null, 2)}
5395
6738
  `;
5396
6739
  await writeFileContent(mcpPath, mcpContent);
@@ -5402,14 +6745,51 @@ async function importConfiguration(options) {
5402
6745
  const errorMessage = error instanceof Error ? error.message : String(error);
5403
6746
  errors.push(`Failed to create .mcp.json: ${errorMessage}`);
5404
6747
  }
6748
+ } else if (verbose && mcpServers && Object.keys(mcpServers).length > 0 && !mcpEnabled) {
6749
+ logger.log(`Skipping MCP configuration (mcp feature not enabled)`);
5405
6750
  }
5406
- return {
5407
- success: errors.length === 0 && (rulesCreated > 0 || ignoreFileCreated || mcpFileCreated),
6751
+ let subagentsCreated = 0;
6752
+ if (subagentsEnabled && subagents && subagents.length > 0) {
6753
+ try {
6754
+ const { mkdir: mkdir3 } = await import("fs/promises");
6755
+ const subagentsDir = (0, import_node_path28.join)(baseDir, rulesDir, "subagents");
6756
+ await mkdir3(subagentsDir, { recursive: true });
6757
+ for (const subagent of subagents) {
6758
+ try {
6759
+ const filename = `${subagent.filename}.md`;
6760
+ const filepath = (0, import_node_path28.join)(subagentsDir, filename);
6761
+ const content = generateSubagentFileContent(subagent);
6762
+ await writeFileContent(filepath, content);
6763
+ subagentsCreated++;
6764
+ if (verbose) {
6765
+ logger.success(`Created subagent: ${filename}`);
6766
+ }
6767
+ } catch (error) {
6768
+ const errorMessage = error instanceof Error ? error.message : String(error);
6769
+ errors.push(`Failed to create subagent ${subagent.filename}: ${errorMessage}`);
6770
+ }
6771
+ }
6772
+ if (verbose && subagentsCreated > 0) {
6773
+ logger.success(`Created ${subagentsCreated} subagent files`);
6774
+ }
6775
+ } catch (error) {
6776
+ const errorMessage = error instanceof Error ? error.message : String(error);
6777
+ errors.push(`Failed to create subagents directory: ${errorMessage}`);
6778
+ }
6779
+ } else if (verbose && subagents && subagents.length > 0 && !subagentsEnabled) {
6780
+ logger.log(`Skipping subagents (subagents feature not enabled)`);
6781
+ }
6782
+ const result = {
6783
+ success: errors.length === 0 && (rulesCreated > 0 || ignoreFileCreated || mcpFileCreated || subagentsCreated > 0),
5408
6784
  rulesCreated,
5409
6785
  errors,
5410
6786
  ignoreFileCreated,
5411
6787
  mcpFileCreated
5412
6788
  };
6789
+ if (subagentsCreated > 0) {
6790
+ result.subagentsCreated = subagentsCreated;
6791
+ }
6792
+ return result;
5413
6793
  }
5414
6794
  function generateRuleFileContent(rule) {
5415
6795
  if (rule.type === "command") {
@@ -5417,69 +6797,109 @@ function generateRuleFileContent(rule) {
5417
6797
  description: rule.frontmatter.description,
5418
6798
  targets: rule.frontmatter.targets
5419
6799
  };
5420
- const frontmatter2 = import_gray_matter2.default.stringify("", simplifiedFrontmatter);
6800
+ const frontmatter2 = import_gray_matter3.default.stringify("", simplifiedFrontmatter);
5421
6801
  return frontmatter2 + rule.content;
5422
6802
  }
5423
- const frontmatter = import_gray_matter2.default.stringify("", rule.frontmatter);
6803
+ const frontmatter = import_gray_matter3.default.stringify("", rule.frontmatter);
5424
6804
  return frontmatter + rule.content;
5425
6805
  }
6806
+ function generateSubagentFileContent(subagent) {
6807
+ const frontmatter = import_gray_matter3.default.stringify("", subagent.frontmatter);
6808
+ return frontmatter + subagent.content;
6809
+ }
5426
6810
 
5427
6811
  // src/cli/commands/import.ts
5428
6812
  init_logger();
5429
6813
  async function importCommand(options = {}) {
5430
6814
  logger.setVerbose(options.verbose || false);
5431
- const tools = [];
5432
- if (options.amazonqcli) tools.push("amazonqcli");
5433
- if (options.augmentcode) tools.push("augmentcode");
5434
- if (options["augmentcode-legacy"]) tools.push("augmentcode-legacy");
5435
- if (options.claudecode) tools.push("claudecode");
5436
- if (options.cursor) tools.push("cursor");
5437
- if (options.copilot) tools.push("copilot");
5438
- if (options.cline) tools.push("cline");
5439
- if (options.roo) tools.push("roo");
5440
- if (options.geminicli) tools.push("geminicli");
5441
- if (options.opencode) tools.push("opencode");
5442
- if (tools.length === 0) {
5443
- logger.error(
5444
- "\u274C Please specify one tool to import from (--amazonqcli, --augmentcode, --augmentcode-legacy, --claudecode, --cursor, --copilot, --cline, --roo, --geminicli, --opencode)"
6815
+ let resolvedFeatures;
6816
+ let showWarning = false;
6817
+ if (options.features !== void 0) {
6818
+ resolvedFeatures = options.features;
6819
+ } else {
6820
+ resolvedFeatures = "*";
6821
+ showWarning = true;
6822
+ }
6823
+ if (showWarning) {
6824
+ showBackwardCompatibilityWarning(
6825
+ "importing",
6826
+ "rulesync import --targets cursor,copilot --features rules,mcp,ignore"
5445
6827
  );
6828
+ }
6829
+ const normalizedFeatures = normalizeFeatures(resolvedFeatures);
6830
+ let tools = [];
6831
+ if (options.targets && options.targets.length > 0) {
6832
+ tools = options.targets;
6833
+ } else {
6834
+ if (options.agentsmd) tools.push("agentsmd");
6835
+ if (options.amazonqcli) tools.push("amazonqcli");
6836
+ if (options.augmentcode) tools.push("augmentcode");
6837
+ if (options["augmentcode-legacy"]) tools.push("augmentcode-legacy");
6838
+ if (options.claudecode) tools.push("claudecode");
6839
+ if (options.cursor) tools.push("cursor");
6840
+ if (options.copilot) tools.push("copilot");
6841
+ if (options.cline) tools.push("cline");
6842
+ if (options.roo) tools.push("roo");
6843
+ if (options.geminicli) tools.push("geminicli");
6844
+ if (options.junie) tools.push("junie");
6845
+ if (options.qwencode) tools.push("qwencode");
6846
+ if (options.opencode) tools.push("opencode");
6847
+ }
6848
+ if (tools.length === 0) {
6849
+ logger.error("\u274C Please specify a tool to import from using --targets <tool>.");
6850
+ logger.info("Example: rulesync import --targets cursor");
5446
6851
  process.exit(1);
5447
6852
  }
5448
6853
  if (tools.length > 1) {
5449
6854
  logger.error(
5450
- "\u274C Only one tool can be specified at a time. Please run the import command separately for each tool."
6855
+ `\u274C Import command only supports a single target.
6856
+ You specified: ${tools.join(", ")}
6857
+
6858
+ Please run the command separately for each tool:`
5451
6859
  );
6860
+ for (const tool2 of tools) {
6861
+ logger.info(` rulesync import --targets ${tool2}`);
6862
+ }
5452
6863
  process.exit(1);
5453
6864
  }
5454
6865
  const tool = tools[0];
5455
6866
  if (!tool) {
5456
- logger.error("Error: No tool specified");
6867
+ logger.error("\u274C Unexpected error: No tool selected");
5457
6868
  process.exit(1);
5458
6869
  }
5459
6870
  logger.log(`Importing configuration files from ${tool}...`);
5460
6871
  try {
5461
6872
  const result = await importConfiguration({
5462
6873
  tool,
6874
+ features: normalizedFeatures,
5463
6875
  verbose: options.verbose ?? false,
5464
6876
  useLegacyLocation: options.legacy ?? false
5465
6877
  });
5466
6878
  if (result.success) {
5467
- logger.success(`Imported ${result.rulesCreated} rule(s) from ${tool}`);
6879
+ logger.success(`\u2705 Imported ${result.rulesCreated} rule(s) from ${tool}`);
5468
6880
  if (result.ignoreFileCreated) {
5469
- logger.success("Created .rulesyncignore file from ignore patterns");
6881
+ logger.success(" Created .rulesyncignore file from ignore patterns");
5470
6882
  }
5471
6883
  if (result.mcpFileCreated) {
5472
- logger.success("Created .rulesync/.mcp.json file from MCP configuration");
6884
+ logger.success(" Created .rulesync/.mcp.json file from MCP configuration");
6885
+ }
6886
+ if (result.subagentsCreated) {
6887
+ logger.success(` Created ${result.subagentsCreated} subagent files`);
5473
6888
  }
6889
+ logger.success(`
6890
+ \u{1F389} Successfully imported from ${tool}`);
5474
6891
  logger.log("You can now run 'rulesync generate' to create tool-specific configurations.");
5475
6892
  } else if (result.errors.length > 0) {
5476
6893
  logger.warn(`\u26A0\uFE0F Failed to import from ${tool}: ${result.errors[0]}`);
5477
6894
  if (result.errors.length > 1) {
5478
- logger.info("\nDetailed errors:");
6895
+ logger.info(" Detailed errors:");
5479
6896
  for (const error of result.errors) {
5480
- logger.info(` - ${error}`);
6897
+ logger.info(` - ${error}`);
5481
6898
  }
5482
6899
  }
6900
+ logger.error(`
6901
+ \u274C Failed to import from ${tool}.`);
6902
+ process.exit(1);
5483
6903
  }
5484
6904
  } catch (error) {
5485
6905
  const errorMessage = error instanceof Error ? error.message : String(error);
@@ -5489,7 +6909,7 @@ async function importCommand(options = {}) {
5489
6909
  }
5490
6910
 
5491
6911
  // src/cli/commands/init.ts
5492
- var import_node_path22 = require("path");
6912
+ var import_node_path29 = require("path");
5493
6913
  init_logger();
5494
6914
  async function initCommand(options = {}) {
5495
6915
  const configResult = await loadConfig();
@@ -5498,7 +6918,7 @@ async function initCommand(options = {}) {
5498
6918
  logger.log("Initializing rulesync...");
5499
6919
  await ensureDir(aiRulesDir);
5500
6920
  const useLegacy = options.legacy ?? config.legacy ?? false;
5501
- const rulesDir = useLegacy ? aiRulesDir : (0, import_node_path22.join)(aiRulesDir, "rules");
6921
+ const rulesDir = useLegacy ? aiRulesDir : (0, import_node_path29.join)(aiRulesDir, "rules");
5502
6922
  if (!useLegacy) {
5503
6923
  await ensureDir(rulesDir);
5504
6924
  }
@@ -5544,7 +6964,7 @@ globs: ["**/*"]
5544
6964
  - Follow single responsibility principle
5545
6965
  `
5546
6966
  };
5547
- const filepath = (0, import_node_path22.join)(rulesDir, sampleFile.filename);
6967
+ const filepath = (0, import_node_path29.join)(rulesDir, sampleFile.filename);
5548
6968
  if (!await fileExists(filepath)) {
5549
6969
  await writeFileContent(filepath, sampleFile.content);
5550
6970
  logger.success(`Created ${filepath}`);
@@ -5661,11 +7081,11 @@ async function watchCommand() {
5661
7081
  persistent: true
5662
7082
  });
5663
7083
  let isGenerating = false;
5664
- const handleChange = async (path5) => {
7084
+ const handleChange = async (path8) => {
5665
7085
  if (isGenerating) return;
5666
7086
  isGenerating = true;
5667
7087
  logger.log(`
5668
- \u{1F4DD} Detected change in ${path5}`);
7088
+ \u{1F4DD} Detected change in ${path8}`);
5669
7089
  try {
5670
7090
  await generateCommand({ verbose: false });
5671
7091
  logger.success("Regenerated configuration files");
@@ -5675,10 +7095,10 @@ async function watchCommand() {
5675
7095
  isGenerating = false;
5676
7096
  }
5677
7097
  };
5678
- watcher.on("change", handleChange).on("add", handleChange).on("unlink", (path5) => {
7098
+ watcher.on("change", handleChange).on("add", handleChange).on("unlink", (path8) => {
5679
7099
  logger.log(`
5680
- \u{1F5D1}\uFE0F Removed ${path5}`);
5681
- handleChange(path5);
7100
+ \u{1F5D1}\uFE0F Removed ${path8}`);
7101
+ handleChange(path8);
5682
7102
  }).on("error", (error) => {
5683
7103
  logger.error("Watcher error:", error);
5684
7104
  });
@@ -5689,44 +7109,180 @@ async function watchCommand() {
5689
7109
  });
5690
7110
  }
5691
7111
 
7112
+ // src/cli/utils/targets-parser.ts
7113
+ init_logger();
7114
+ function parseTargets(targetsInput) {
7115
+ if (!targetsInput) {
7116
+ return [];
7117
+ }
7118
+ let targetStrings;
7119
+ if (Array.isArray(targetsInput)) {
7120
+ targetStrings = targetsInput;
7121
+ } else {
7122
+ targetStrings = targetsInput.split(",").map((target) => target.trim()).filter((target) => target.length > 0);
7123
+ }
7124
+ const results = [];
7125
+ const errors = [];
7126
+ let hasWildcard = false;
7127
+ for (const targetString of targetStrings) {
7128
+ if (targetString === "*" || targetString === "all") {
7129
+ hasWildcard = true;
7130
+ results.push(...ALL_TOOL_TARGETS);
7131
+ } else if (isValidToolTarget(targetString)) {
7132
+ results.push(targetString);
7133
+ } else {
7134
+ errors.push(targetString);
7135
+ }
7136
+ }
7137
+ if (hasWildcard && targetStrings.length > 1) {
7138
+ throw new Error(
7139
+ "Cannot use '*' (all tools) with specific tool targets. Use either '--targets *' for all tools, or specify individual tools."
7140
+ );
7141
+ }
7142
+ if (errors.length > 0) {
7143
+ const validTargets = ALL_TOOL_TARGETS.join(", ");
7144
+ throw new Error(
7145
+ `Invalid tool targets: ${errors.join(", ")}. Valid targets are: ${validTargets}, *, all`
7146
+ );
7147
+ }
7148
+ return [...new Set(results)];
7149
+ }
7150
+ function isValidToolTarget(target) {
7151
+ return ALL_TOOL_TARGETS.includes(target);
7152
+ }
7153
+ function checkDeprecatedFlags(options) {
7154
+ const deprecatedTools = [];
7155
+ const flagToToolMap = {
7156
+ agentsmd: "agentsmd",
7157
+ amazonqcli: "amazonqcli",
7158
+ augmentcode: "augmentcode",
7159
+ "augmentcode-legacy": "augmentcode-legacy",
7160
+ copilot: "copilot",
7161
+ cursor: "cursor",
7162
+ cline: "cline",
7163
+ codexcli: "codexcli",
7164
+ claudecode: "claudecode",
7165
+ roo: "roo",
7166
+ geminicli: "geminicli",
7167
+ junie: "junie",
7168
+ qwencode: "qwencode",
7169
+ kiro: "kiro",
7170
+ opencode: "opencode",
7171
+ windsurf: "windsurf"
7172
+ };
7173
+ for (const [flag, tool] of Object.entries(flagToToolMap)) {
7174
+ if (options[flag]) {
7175
+ deprecatedTools.push(tool);
7176
+ }
7177
+ }
7178
+ return deprecatedTools;
7179
+ }
7180
+ function getDeprecationWarning(deprecatedTools, command = "generate") {
7181
+ const toolsStr = deprecatedTools.join(",");
7182
+ return [
7183
+ "\u26A0\uFE0F DEPRECATED: Individual tool flags are deprecated and will be removed in a future version.",
7184
+ ` Current: rulesync ${command} ${deprecatedTools.map((t) => `--${t}`).join(" ")}`,
7185
+ ` New: rulesync ${command} --targets ${toolsStr}`,
7186
+ " Please update your scripts to use the new --targets flag."
7187
+ ].join("\n");
7188
+ }
7189
+ function mergeAndDeduplicateTools(targetsTools, deprecatedTools, allFlag) {
7190
+ if (allFlag) {
7191
+ logger.warn(
7192
+ [
7193
+ "\u26A0\uFE0F DEPRECATED: The --all flag is deprecated and will be removed in a future version.",
7194
+ " Current: rulesync generate --all",
7195
+ " New: rulesync generate --targets *",
7196
+ " Please update your scripts to use the new --targets flag."
7197
+ ].join("\n")
7198
+ );
7199
+ return [...ALL_TOOL_TARGETS];
7200
+ }
7201
+ const allTools = [...targetsTools, ...deprecatedTools];
7202
+ return [...new Set(allTools)];
7203
+ }
7204
+
5692
7205
  // src/cli/index.ts
5693
7206
  var program = new import_commander.Command();
5694
- program.name("rulesync").description("Unified AI rules management CLI tool").version("0.64.0");
7207
+ program.name("rulesync").description("Unified AI rules management CLI tool").version("0.67.0");
5695
7208
  program.command("init").description("Initialize rulesync in current directory").option("--legacy", "Use legacy file location (.rulesync/*.md instead of .rulesync/rules/*.md)").action(initCommand);
5696
7209
  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);
5697
7210
  program.command("gitignore").description("Add generated files to .gitignore").action(gitignoreCommand);
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(
7211
+ program.command("import").description("Import configurations from AI tools to rulesync format").option("-t, --targets <tool>", "Tool to import from (e.g., 'copilot', 'cursor', 'cline')").option(
7212
+ "--features <features>",
7213
+ `Comma-separated list of features to import (${FEATURE_TYPES.join(",")}) or '*' for all`,
7214
+ (value) => {
7215
+ if (value === "*") return "*";
7216
+ return value.split(",").map((f) => f.trim()).filter(Boolean);
7217
+ }
7218
+ ).option("--agentsmd", "[DEPRECATED] Import from AGENTS.md (use --targets agentsmd)").option("--augmentcode", "[DEPRECATED] Import from AugmentCode (use --targets augmentcode)").option(
7219
+ "--augmentcode-legacy",
7220
+ "[DEPRECATED] Import from AugmentCode legacy format (use --targets augmentcode-legacy)"
7221
+ ).option("--claudecode", "[DEPRECATED] Import from Claude Code (use --targets claudecode)").option("--cursor", "[DEPRECATED] Import from Cursor (use --targets cursor)").option("--copilot", "[DEPRECATED] Import from GitHub Copilot (use --targets copilot)").option("--cline", "[DEPRECATED] Import from Cline (use --targets cline)").option("--roo", "[DEPRECATED] Import from Roo Code (use --targets roo)").option("--geminicli", "[DEPRECATED] Import from Gemini CLI (use --targets geminicli)").option("--junie", "[DEPRECATED] Import from JetBrains Junie (use --targets junie)").option("--qwencode", "[DEPRECATED] Import from Qwen Code (use --targets qwencode)").option("--opencode", "[DEPRECATED] Import from OpenCode (use --targets opencode)").option("-v, --verbose", "Verbose output").option("--legacy", "Use legacy file location (.rulesync/*.md instead of .rulesync/rules/*.md)").action(async (options) => {
7222
+ try {
7223
+ let tools = [];
7224
+ const targetsTools = options.targets ? parseTargets(options.targets) : [];
7225
+ const deprecatedTools = checkDeprecatedFlags(options);
7226
+ if (deprecatedTools.length > 0) {
7227
+ logger.warn(getDeprecationWarning(deprecatedTools, "import"));
7228
+ }
7229
+ tools = mergeAndDeduplicateTools(targetsTools, deprecatedTools, false);
7230
+ const importOptions = {
7231
+ ...tools.length > 0 && { targets: tools },
7232
+ ...options.features && { features: options.features },
7233
+ verbose: options.verbose,
7234
+ legacy: options.legacy
7235
+ };
7236
+ await importCommand(importOptions);
7237
+ } catch (error) {
7238
+ logger.error(error instanceof Error ? error.message : String(error));
7239
+ process.exit(1);
7240
+ }
7241
+ });
7242
+ program.command("generate").description("Generate configuration files for AI tools").option("--all", "[DEPRECATED] Generate for all supported AI tools (use --targets * instead)").option(
7243
+ "-t, --targets <tools>",
7244
+ "Comma-separated list of tools to generate for (e.g., 'copilot,cursor,cline' or '*' for all)"
7245
+ ).option(
7246
+ "--features <features>",
7247
+ `Comma-separated list of features to generate (${FEATURE_TYPES.join(",")}) or '*' for all`,
7248
+ (value) => {
7249
+ if (value === "*") return "*";
7250
+ return value.split(",").map((f) => f.trim()).filter(Boolean);
7251
+ }
7252
+ ).option("--agentsmd", "[DEPRECATED] Generate only for AGENTS.md (use --targets agentsmd)").option(
7253
+ "--amazonqcli",
7254
+ "[DEPRECATED] Generate only for Amazon Q Developer CLI (use --targets amazonqcli)"
7255
+ ).option("--augmentcode", "[DEPRECATED] Generate only for AugmentCode (use --targets augmentcode)").option(
7256
+ "--augmentcode-legacy",
7257
+ "[DEPRECATED] Generate only for AugmentCode legacy format (use --targets augmentcode-legacy)"
7258
+ ).option("--copilot", "[DEPRECATED] Generate only for GitHub Copilot (use --targets copilot)").option("--cursor", "[DEPRECATED] Generate only for Cursor (use --targets cursor)").option("--cline", "[DEPRECATED] Generate only for Cline (use --targets cline)").option("--codexcli", "[DEPRECATED] Generate only for OpenAI Codex CLI (use --targets codexcli)").option("--claudecode", "[DEPRECATED] Generate only for Claude Code (use --targets claudecode)").option("--roo", "[DEPRECATED] Generate only for Roo Code (use --targets roo)").option("--geminicli", "[DEPRECATED] Generate only for Gemini CLI (use --targets geminicli)").option("--junie", "[DEPRECATED] Generate only for JetBrains Junie (use --targets junie)").option("--qwencode", "[DEPRECATED] Generate only for Qwen Code (use --targets qwencode)").option("--kiro", "[DEPRECATED] Generate only for Kiro IDE (use --targets kiro)").option("--opencode", "[DEPRECATED] Generate only for OpenCode (use --targets opencode)").option("--windsurf", "[DEPRECATED] Generate only for Windsurf (use --targets windsurf)").option("--delete", "Delete all existing files in output directories before generating").option(
5700
7259
  "-b, --base-dir <paths>",
5701
7260
  "Base directories to generate files (comma-separated for multiple paths)"
5702
7261
  ).option("-v, --verbose", "Verbose output").option("-c, --config <path>", "Path to configuration file").option("--no-config", "Disable configuration file loading").action(async (options) => {
5703
- const tools = [];
5704
- if (options.augmentcode) tools.push("augmentcode");
5705
- if (options["augmentcode-legacy"]) tools.push("augmentcode-legacy");
5706
- if (options.copilot) tools.push("copilot");
5707
- if (options.cursor) tools.push("cursor");
5708
- if (options.cline) tools.push("cline");
5709
- if (options.codexcli) tools.push("codexcli");
5710
- if (options.claudecode) tools.push("claudecode");
5711
- if (options.roo) tools.push("roo");
5712
- if (options.geminicli) tools.push("geminicli");
5713
- if (options.junie) tools.push("junie");
5714
- if (options.kiro) tools.push("kiro");
5715
- if (options.opencode) tools.push("opencode");
5716
- if (options.windsurf) tools.push("windsurf");
5717
- const generateOptions = {
5718
- verbose: options.verbose,
5719
- delete: options.delete,
5720
- config: options.config,
5721
- noConfig: options.noConfig
5722
- };
5723
- if (tools.length > 0) {
5724
- generateOptions.tools = tools;
5725
- }
5726
- if (options.baseDir) {
5727
- generateOptions.baseDirs = options.baseDir.split(",").map((dir) => dir.trim()).filter((dir) => dir.length > 0);
7262
+ try {
7263
+ let tools = [];
7264
+ const targetsTools = options.targets ? parseTargets(options.targets) : [];
7265
+ const deprecatedTools = checkDeprecatedFlags(options);
7266
+ if (deprecatedTools.length > 0) {
7267
+ logger.warn(getDeprecationWarning(deprecatedTools, "generate"));
7268
+ }
7269
+ tools = mergeAndDeduplicateTools(targetsTools, deprecatedTools, options.all === true);
7270
+ const generateOptions = {
7271
+ verbose: options.verbose,
7272
+ tools: tools.length > 0 ? tools : void 0,
7273
+ features: options.features,
7274
+ delete: options.delete,
7275
+ config: options.config,
7276
+ noConfig: options.noConfig
7277
+ };
7278
+ if (options.baseDir) {
7279
+ generateOptions.baseDirs = options.baseDir.split(",").map((dir) => dir.trim()).filter((dir) => dir.length > 0);
7280
+ }
7281
+ await generateCommand(generateOptions);
7282
+ } catch (error) {
7283
+ logger.error(error instanceof Error ? error.message : String(error));
7284
+ process.exit(1);
5728
7285
  }
5729
- await generateCommand(generateOptions);
5730
7286
  });
5731
7287
  program.command("validate").description("Validate rulesync configuration").action(validateCommand);
5732
7288
  program.command("status").description("Show current status of rulesync").action(statusCommand);