rulesync 0.65.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 +86 -47
  2. package/dist/{amazonqcli-MW7XTVPN.js → amazonqcli-PWXCSRAN.js} +2 -2
  3. package/dist/{augmentcode-WCZCL7VR.js → augmentcode-4AFYW4BU.js} +2 -2
  4. package/dist/{chunk-YC2BC7Z2.js → chunk-35VMCHXQ.js} +1 -1
  5. package/dist/{chunk-6SLEITCQ.js → chunk-3QGD3CH5.js} +1 -1
  6. package/dist/{chunk-VRWNZTGW.js → chunk-5GKH5TQ4.js} +1 -1
  7. package/dist/{chunk-4NWMCTN5.js → chunk-7BIZ5Y6F.js} +1 -1
  8. package/dist/{chunk-LTWEI4PW.js → chunk-7QVQO6MQ.js} +1 -1
  9. package/dist/{chunk-UGY5ALND.js → chunk-B2HD24KC.js} +1 -1
  10. package/dist/{chunk-M2AUM37M.js → chunk-CS7AV6JT.js} +1 -0
  11. package/dist/{chunk-6AXPFPKI.js → chunk-KONQNQY3.js} +1 -1
  12. package/dist/{chunk-FL5BF6JM.js → chunk-OARJESSZ.js} +1 -1
  13. package/dist/{chunk-JXOLLTNV.js → chunk-V36ICGOY.js} +1 -1
  14. package/dist/{chunk-GIAQWZQ4.js → chunk-WCON5BAI.js} +1 -1
  15. package/dist/{chunk-DM2B7XUB.js → chunk-WFOWHPBC.js} +1 -1
  16. package/dist/{chunk-N6DASHJL.js → chunk-WYYQXVHC.js} +1 -1
  17. package/dist/{chunk-I4NVS7GE.js → chunk-YZUDL4GW.js} +1 -1
  18. package/dist/{chunk-TX2CE4RR.js → chunk-ZMGXHLYP.js} +1 -1
  19. package/dist/{claudecode-RZSJPPBU.js → claudecode-OC7VHCF6.js} +3 -3
  20. package/dist/{cline-JTWWBQQ4.js → cline-A4KFSAQE.js} +3 -3
  21. package/dist/{codexcli-ATMFGRJR.js → codexcli-YP3X7FWB.js} +3 -3
  22. package/dist/{copilot-H3CLGKDP.js → copilot-3LIMXQ7O.js} +2 -2
  23. package/dist/{cursor-ZUN5RZU6.js → cursor-QV72CDJC.js} +3 -3
  24. package/dist/{geminicli-Q5HPIQCU.js → geminicli-XPSJJS65.js} +3 -3
  25. package/dist/index.cjs +1888 -518
  26. package/dist/index.js +1866 -511
  27. package/dist/{junie-JCLVC3MI.js → junie-265XIW43.js} +3 -3
  28. package/dist/{kiro-CNF6433S.js → kiro-6OHFHN5X.js} +2 -2
  29. package/dist/{opencode-EBS3CED2.js → opencode-C5QAYVJ5.js} +2 -2
  30. package/dist/{qwencode-JIT6KW7E.js → qwencode-5RR24UW6.js} +3 -3
  31. package/dist/{roo-KBTRH4TZ.js → roo-5IXVBUHD.js} +3 -3
  32. package/dist/{windsurf-ZAAWL6JJ.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",
@@ -57,10 +58,10 @@ var init_tool_targets = __esm({
57
58
  "junie",
58
59
  "windsurf"
59
60
  ];
60
- ToolTargetSchema = import_mini2.z.enum(ALL_TOOL_TARGETS);
61
- ToolTargetsSchema = import_mini2.z.array(ToolTargetSchema);
62
- WildcardTargetSchema = import_mini2.z.tuple([import_mini2.z.literal("*")]);
63
- 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]);
64
65
  }
65
66
  });
66
67
 
@@ -162,8 +163,8 @@ async function findRuleFiles(aiRulesDir) {
162
163
  newLocationFiles.map((file) => file.split("/").pop()?.replace(/\.md$/, ""))
163
164
  );
164
165
  const filteredLegacyFiles = legacyLocationFiles.filter((file) => {
165
- const basename6 = file.split("/").pop()?.replace(/\.md$/, "");
166
- return !newLocationBasenames.has(basename6);
166
+ const basename7 = file.split("/").pop()?.replace(/\.md$/, "");
167
+ return !newLocationBasenames.has(basename7);
167
168
  });
168
169
  return [...newLocationFiles, ...filteredLegacyFiles];
169
170
  }
@@ -1305,103 +1306,120 @@ var init_opencode = __esm({
1305
1306
  // src/cli/index.ts
1306
1307
  var import_commander = require("commander");
1307
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
+
1378
+ // src/cli/commands/add.ts
1379
+ var import_promises = require("fs/promises");
1380
+ var path = __toESM(require("path"), 1);
1381
+
1382
+ // src/utils/config-loader.ts
1383
+ var import_c12 = require("c12");
1384
+ var import_core = require("zod/v4/core");
1385
+
1308
1386
  // src/types/claudecode.ts
1309
- var import_mini = require("zod/mini");
1310
- var ClaudeSettingsSchema = import_mini.z.looseObject({
1311
- permissions: import_mini.z._default(
1312
- import_mini.z.looseObject({
1313
- 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()), [])
1314
1392
  }),
1315
1393
  { deny: [] }
1316
1394
  )
1317
1395
  });
1318
1396
 
1319
1397
  // src/types/shared.ts
1320
- var import_mini3 = require("zod/mini");
1398
+ var import_mini4 = require("zod/mini");
1321
1399
  init_tool_targets();
1322
- var OutputSchema = import_mini3.z.object({
1400
+ var OutputSchema = import_mini4.z.object({
1323
1401
  tool: ToolTargetSchema,
1324
- filepath: import_mini3.z.string(),
1325
- content: import_mini3.z.string()
1402
+ filepath: import_mini4.z.string(),
1403
+ content: import_mini4.z.string()
1326
1404
  });
1327
- var BaseFrontmatterSchema = import_mini3.z.object({
1328
- 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())
1329
1407
  });
1330
1408
 
1331
1409
  // src/types/commands.ts
1332
1410
  var CommandFrontmatterSchema = BaseFrontmatterSchema;
1333
1411
 
1334
1412
  // src/types/config.ts
1335
- var import_mini4 = require("zod/mini");
1336
- init_tool_targets();
1337
- var ConfigSchema = import_mini4.z.object({
1338
- aiRulesDir: import_mini4.z.string(),
1339
- outputPaths: import_mini4.z.record(ToolTargetSchema, import_mini4.z.string()),
1340
- watchEnabled: import_mini4.z.boolean(),
1341
- defaultTargets: ToolTargetsSchema,
1342
- claudecodeCommands: import_mini4.z.optional(import_mini4.z.string()),
1343
- geminicliCommands: import_mini4.z.optional(import_mini4.z.string()),
1344
- legacy: import_mini4.z.optional(import_mini4.z.boolean())
1345
- });
1346
-
1347
- // src/types/config-options.ts
1348
1413
  var import_mini5 = require("zod/mini");
1349
1414
  init_tool_targets();
1350
- var OutputPathsSchema = import_mini5.z.object({
1351
- amazonqcli: import_mini5.z.optional(import_mini5.z.string()),
1352
- augmentcode: import_mini5.z.optional(import_mini5.z.string()),
1353
- "augmentcode-legacy": import_mini5.z.optional(import_mini5.z.string()),
1354
- copilot: import_mini5.z.optional(import_mini5.z.string()),
1355
- cursor: import_mini5.z.optional(import_mini5.z.string()),
1356
- cline: import_mini5.z.optional(import_mini5.z.string()),
1357
- claudecode: import_mini5.z.optional(import_mini5.z.string()),
1358
- codexcli: import_mini5.z.optional(import_mini5.z.string()),
1359
- opencode: import_mini5.z.optional(import_mini5.z.string()),
1360
- qwencode: import_mini5.z.optional(import_mini5.z.string()),
1361
- roo: import_mini5.z.optional(import_mini5.z.string()),
1362
- geminicli: import_mini5.z.optional(import_mini5.z.string()),
1363
- kiro: import_mini5.z.optional(import_mini5.z.string()),
1364
- junie: import_mini5.z.optional(import_mini5.z.string()),
1365
- windsurf: import_mini5.z.optional(import_mini5.z.string())
1366
- });
1367
- var ConfigOptionsSchema = import_mini5.z.object({
1368
- aiRulesDir: import_mini5.z.optional(import_mini5.z.string()),
1369
- outputPaths: import_mini5.z.optional(OutputPathsSchema),
1370
- watchEnabled: import_mini5.z.optional(import_mini5.z.boolean()),
1371
- defaultTargets: import_mini5.z.optional(ToolTargetsSchema),
1372
- targets: import_mini5.z.optional(import_mini5.z.array(ToolTargetSchema)),
1373
- exclude: import_mini5.z.optional(import_mini5.z.array(ToolTargetSchema)),
1374
- verbose: import_mini5.z.optional(import_mini5.z.boolean()),
1375
- delete: import_mini5.z.optional(import_mini5.z.boolean()),
1376
- baseDir: import_mini5.z.optional(import_mini5.z.union([import_mini5.z.string(), import_mini5.z.array(import_mini5.z.string())])),
1377
- legacy: import_mini5.z.optional(import_mini5.z.boolean()),
1378
- watch: import_mini5.z.optional(
1379
- import_mini5.z.object({
1380
- enabled: import_mini5.z.optional(import_mini5.z.boolean()),
1381
- interval: import_mini5.z.optional(import_mini5.z.number()),
1382
- ignore: import_mini5.z.optional(import_mini5.z.array(import_mini5.z.string()))
1383
- })
1384
- )
1385
- });
1386
- var MergedConfigSchema = import_mini5.z.object({
1415
+ var ConfigSchema = import_mini5.z.object({
1387
1416
  aiRulesDir: import_mini5.z.string(),
1388
1417
  outputPaths: import_mini5.z.record(ToolTargetSchema, import_mini5.z.string()),
1389
1418
  watchEnabled: import_mini5.z.boolean(),
1390
1419
  defaultTargets: ToolTargetsSchema,
1391
- targets: import_mini5.z.optional(import_mini5.z.array(ToolTargetSchema)),
1392
- exclude: import_mini5.z.optional(import_mini5.z.array(ToolTargetSchema)),
1393
- verbose: import_mini5.z.optional(import_mini5.z.boolean()),
1394
- delete: import_mini5.z.optional(import_mini5.z.boolean()),
1395
- baseDir: import_mini5.z.optional(import_mini5.z.union([import_mini5.z.string(), import_mini5.z.array(import_mini5.z.string())])),
1396
- configPath: import_mini5.z.optional(import_mini5.z.string()),
1397
- legacy: import_mini5.z.optional(import_mini5.z.boolean()),
1398
- watch: import_mini5.z.optional(
1399
- import_mini5.z.object({
1400
- enabled: import_mini5.z.optional(import_mini5.z.boolean()),
1401
- interval: import_mini5.z.optional(import_mini5.z.number()),
1402
- ignore: import_mini5.z.optional(import_mini5.z.array(import_mini5.z.string()))
1403
- })
1404
- )
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())
1405
1423
  });
1406
1424
 
1407
1425
  // src/types/mcp.ts
@@ -1459,20 +1477,13 @@ var GenerateOptionsSchema = import_mini7.z.object({
1459
1477
  // src/types/index.ts
1460
1478
  init_tool_targets();
1461
1479
 
1462
- // src/cli/commands/add.ts
1463
- var import_promises = require("fs/promises");
1464
- var path = __toESM(require("path"), 1);
1465
-
1466
- // src/utils/config-loader.ts
1467
- var import_c12 = require("c12");
1468
- var import_core = require("zod/v4/core");
1469
-
1470
1480
  // src/utils/config.ts
1471
1481
  init_tool_targets();
1472
1482
  function getDefaultConfig() {
1473
1483
  return {
1474
1484
  aiRulesDir: ".rulesync",
1475
1485
  outputPaths: {
1486
+ agentsmd: ".agents/memories",
1476
1487
  amazonqcli: ".amazonq/rules",
1477
1488
  augmentcode: ".",
1478
1489
  "augmentcode-legacy": ".",
@@ -1590,6 +1601,11 @@ function generateMinimalConfig(options) {
1590
1601
  if (comma) lines[lines.length - 1] += comma;
1591
1602
  lines.push(` "exclude": ${JSON.stringify(options.exclude)}`);
1592
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
+ }
1593
1609
  if (options.aiRulesDir) {
1594
1610
  const comma = lines.length > 1 ? "," : "";
1595
1611
  if (comma) lines[lines.length - 1] += comma;
@@ -1630,6 +1646,7 @@ function generateMinimalConfig(options) {
1630
1646
  function generateSampleConfig(options) {
1631
1647
  const targets = options?.targets || ALL_TOOL_TARGETS;
1632
1648
  const excludeValue = options?.exclude ? JSON.stringify(options.exclude) : null;
1649
+ const featuresValue = options?.features || ["rules", "commands", "mcp", "ignore", "subagents"];
1633
1650
  const aiRulesDir = options?.aiRulesDir || null;
1634
1651
  const baseDir = options?.baseDir || null;
1635
1652
  const deleteFlag = options?.delete || false;
@@ -1641,6 +1658,10 @@ function generateSampleConfig(options) {
1641
1658
 
1642
1659
  // Tools to exclude from generation (overrides targets)
1643
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)},
1644
1665
  ${aiRulesDir ? `
1645
1666
  // Directory containing AI rule files
1646
1667
  "aiRulesDir": "${aiRulesDir}",` : ""}
@@ -1671,23 +1692,6 @@ function generateSampleConfig(options) {
1671
1692
  }
1672
1693
  `;
1673
1694
  }
1674
- function mergeWithCliOptions(config, cliOptions) {
1675
- const merged = { ...config };
1676
- if (cliOptions.verbose !== void 0) {
1677
- merged.verbose = cliOptions.verbose;
1678
- }
1679
- if (cliOptions.delete !== void 0) {
1680
- merged.delete = cliOptions.delete;
1681
- }
1682
- if (cliOptions.baseDirs && cliOptions.baseDirs.length > 0) {
1683
- merged.baseDir = cliOptions.baseDirs;
1684
- }
1685
- if (cliOptions.tools && cliOptions.tools.length > 0) {
1686
- merged.defaultTargets = cliOptions.tools;
1687
- merged.exclude = void 0;
1688
- }
1689
- return merged;
1690
- }
1691
1695
 
1692
1696
  // src/cli/commands/add.ts
1693
1697
  init_logger();
@@ -1796,6 +1800,11 @@ Default Targets: ${config.defaultTargets.join(", ")}`);
1796
1800
  if (config.exclude && config.exclude.length > 0) {
1797
1801
  logger.log(`Excluded Targets: ${config.exclude.join(", ")}`);
1798
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
+ }
1799
1808
  logger.log("\nOutput Paths:");
1800
1809
  for (const [tool, outputPath] of Object.entries(config.outputPaths)) {
1801
1810
  logger.log(` ${tool}: ${outputPath}`);
@@ -1926,6 +1935,10 @@ const config: ConfigOptions = {
1926
1935
  // Available: ${ALL_TOOL_TARGETS.join(", ")}
1927
1936
  targets: ${JSON.stringify(ALL_TOOL_TARGETS)},
1928
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
+
1929
1942
  // Custom output paths for specific tools
1930
1943
  // outputPaths: {
1931
1944
  // copilot: ".github/copilot-instructions.md",
@@ -1947,6 +1960,9 @@ export default config;`;
1947
1960
  if (options.exclude) {
1948
1961
  configLines.push(` exclude: ${JSON.stringify(options.exclude)}`);
1949
1962
  }
1963
+ if (options.features) {
1964
+ configLines.push(` features: ${JSON.stringify(options.features)}`);
1965
+ }
1950
1966
  if (options.aiRulesDir) {
1951
1967
  configLines.push(` aiRulesDir: "${options.aiRulesDir}"`);
1952
1968
  }
@@ -1979,7 +1995,7 @@ export default config;
1979
1995
  }
1980
1996
 
1981
1997
  // src/cli/commands/generate.ts
1982
- var import_node_path14 = require("path");
1998
+ var import_node_path18 = require("path");
1983
1999
 
1984
2000
  // src/core/command-generator.ts
1985
2001
  var import_node_path5 = require("path");
@@ -2248,8 +2264,475 @@ async function generateCommands(projectRoot, baseDir, targets) {
2248
2264
  return outputs;
2249
2265
  }
2250
2266
 
2251
- // 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
2252
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");
2253
2736
 
2254
2737
  // src/generators/ignore/shared-helpers.ts
2255
2738
  function extractIgnorePatternsFromRules(rules) {
@@ -2372,7 +2855,7 @@ function generateIgnoreFile(rules, config, ignoreConfig, baseDir) {
2372
2855
  const outputs = [];
2373
2856
  const content = generateIgnoreContent(rules, ignoreConfig);
2374
2857
  const outputPath = baseDir || process.cwd();
2375
- const filepath = (0, import_node_path6.join)(outputPath, ignoreConfig.filename);
2858
+ const filepath = (0, import_node_path7.join)(outputPath, ignoreConfig.filename);
2376
2859
  outputs.push({
2377
2860
  tool: ignoreConfig.tool,
2378
2861
  filepath,
@@ -2955,7 +3438,7 @@ async function generateKiroIgnoreFiles(rules, config, baseDir) {
2955
3438
  }
2956
3439
 
2957
3440
  // src/generators/ignore/qwencode.ts
2958
- var import_node_path7 = require("path");
3441
+ var import_node_path8 = require("path");
2959
3442
  function extractQwenCodeFileFilteringPatterns(content) {
2960
3443
  const filtering = {};
2961
3444
  const configBlocks = content.match(/```(?:json|javascript)\s*\n([\s\S]*?)\n```/g);
@@ -3005,7 +3488,7 @@ async function generateQwenCodeIgnoreFiles(rules, config, baseDir) {
3005
3488
  const outputs = [];
3006
3489
  const outputPath = baseDir || process.cwd();
3007
3490
  const qwenConfig = generateQwenCodeConfiguration(rules);
3008
- const settingsPath = (0, import_node_path7.join)(outputPath, ".qwen", "settings.json");
3491
+ const settingsPath = (0, import_node_path8.join)(outputPath, ".qwen", "settings.json");
3009
3492
  outputs.push({
3010
3493
  tool: "qwencode",
3011
3494
  filepath: settingsPath,
@@ -3020,12 +3503,48 @@ function generateWindsurfIgnore(rules, config, baseDir) {
3020
3503
  return generateIgnoreFile(rules, config, ignoreConfigs.windsurf, baseDir);
3021
3504
  }
3022
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
+
3023
3542
  // src/generators/rules/shared-helpers.ts
3024
- var import_node_path9 = require("path");
3543
+ var import_node_path10 = require("path");
3025
3544
  init_file();
3026
3545
 
3027
3546
  // src/utils/ignore.ts
3028
- var import_node_path8 = require("path");
3547
+ var import_node_path9 = require("path");
3029
3548
  var import_micromatch = __toESM(require("micromatch"), 1);
3030
3549
  init_file();
3031
3550
  init_logger();
@@ -3034,7 +3553,7 @@ async function loadIgnorePatterns(baseDir = process.cwd()) {
3034
3553
  if (cachedIgnorePatterns) {
3035
3554
  return cachedIgnorePatterns;
3036
3555
  }
3037
- const ignorePath = (0, import_node_path8.join)(baseDir, ".rulesyncignore");
3556
+ const ignorePath = (0, import_node_path9.join)(baseDir, ".rulesyncignore");
3038
3557
  if (!await fileExists(ignorePath)) {
3039
3558
  cachedIgnorePatterns = { patterns: [] };
3040
3559
  return cachedIgnorePatterns;
@@ -3088,21 +3607,35 @@ function addOutput(outputs, tool, config, baseDir, relativePath, content) {
3088
3607
  const outputDir = resolveOutputDir(config, tool, baseDir);
3089
3608
  outputs.push({
3090
3609
  tool,
3091
- filepath: (0, import_node_path9.join)(outputDir, relativePath),
3610
+ filepath: (0, import_node_path10.join)(outputDir, relativePath),
3092
3611
  content
3093
3612
  });
3094
3613
  }
3095
3614
  async function generateRulesConfig(rules, config, generatorConfig, baseDir) {
3096
3615
  const outputs = [];
3097
- for (const rule of rules) {
3098
- const content = generatorConfig.generateContent(rule);
3099
- const outputDir = resolveOutputDir(config, generatorConfig.tool, baseDir);
3100
- const filepath = generatorConfig.pathResolver ? generatorConfig.pathResolver(rule, outputDir) : (0, import_node_path9.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);
3101
3623
  outputs.push({
3102
3624
  tool: generatorConfig.tool,
3103
3625
  filepath,
3104
3626
  content
3105
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
+ }
3106
3639
  }
3107
3640
  const ignorePatterns = await loadIgnorePatterns(baseDir);
3108
3641
  if (ignorePatterns.patterns.length > 0 && generatorConfig.ignoreFileName) {
@@ -3125,7 +3658,7 @@ async function generateComplexRules(rules, config, generatorConfig, baseDir) {
3125
3658
  for (const rule of detailRules) {
3126
3659
  const content = generatorConfig.generateDetailContent(rule);
3127
3660
  const filepath = resolvePath(
3128
- (0, import_node_path9.join)(generatorConfig.detailSubDir, `${rule.filename}.md`),
3661
+ (0, import_node_path10.join)(generatorConfig.detailSubDir, `${rule.filename}.md`),
3129
3662
  baseDir
3130
3663
  );
3131
3664
  outputs.push({
@@ -3233,7 +3766,7 @@ function generateRuleFile(rule) {
3233
3766
  }
3234
3767
 
3235
3768
  // src/generators/rules/augmentcode.ts
3236
- var import_node_path10 = require("path");
3769
+ var import_node_path11 = require("path");
3237
3770
  async function generateAugmentcodeConfig(rules, config, baseDir) {
3238
3771
  const outputs = createOutputsArray();
3239
3772
  rules.forEach((rule) => {
@@ -3242,7 +3775,7 @@ async function generateAugmentcodeConfig(rules, config, baseDir) {
3242
3775
  "augmentcode",
3243
3776
  config,
3244
3777
  baseDir,
3245
- (0, import_node_path10.join)(".augment", "rules", `${rule.filename}.md`),
3778
+ (0, import_node_path11.join)(".augment", "rules", `${rule.filename}.md`),
3246
3779
  generateRuleFile2(rule)
3247
3780
  );
3248
3781
  });
@@ -3295,7 +3828,7 @@ function generateLegacyGuidelinesFile(allRules) {
3295
3828
  }
3296
3829
 
3297
3830
  // src/generators/rules/claudecode.ts
3298
- var import_node_path11 = require("path");
3831
+ var import_node_path12 = require("path");
3299
3832
  init_file();
3300
3833
  init_logger();
3301
3834
  async function generateClaudecodeConfig(rules, config, baseDir) {
@@ -3309,7 +3842,7 @@ async function generateClaudecodeConfig(rules, config, baseDir) {
3309
3842
  generateDetailContent: generateMemoryFile,
3310
3843
  detailSubDir: ".claude/memories",
3311
3844
  updateAdditionalConfig: async (ignorePatterns, baseDir2) => {
3312
- const settingsPath = resolvePath((0, import_node_path11.join)(".claude", "settings.json"), baseDir2);
3845
+ const settingsPath = resolvePath((0, import_node_path12.join)(".claude", "settings.json"), baseDir2);
3313
3846
  await updateClaudeSettings(settingsPath, ignorePatterns);
3314
3847
  return [];
3315
3848
  }
@@ -3373,7 +3906,7 @@ async function updateClaudeSettings(settingsPath, ignorePatterns) {
3373
3906
  }
3374
3907
 
3375
3908
  // src/generators/rules/generator-registry.ts
3376
- var import_node_path12 = require("path");
3909
+ var import_node_path13 = require("path");
3377
3910
  function determineCursorRuleType(frontmatter) {
3378
3911
  if (frontmatter.cursorRuleType) {
3379
3912
  return frontmatter.cursorRuleType;
@@ -3397,6 +3930,15 @@ function determineCursorRuleType(frontmatter) {
3397
3930
  }
3398
3931
  var GENERATOR_REGISTRY = {
3399
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
+ },
3400
3942
  amazonqcli: {
3401
3943
  type: "complex",
3402
3944
  tool: "amazonqcli",
@@ -3469,7 +4011,7 @@ var GENERATOR_REGISTRY = {
3469
4011
  },
3470
4012
  pathResolver: (rule, outputDir) => {
3471
4013
  const baseFilename = rule.filename.replace(/\.md$/, "");
3472
- return (0, import_node_path12.join)(outputDir, `${baseFilename}.instructions.md`);
4014
+ return (0, import_node_path13.join)(outputDir, `${baseFilename}.instructions.md`);
3473
4015
  }
3474
4016
  },
3475
4017
  cursor: {
@@ -3509,7 +4051,7 @@ var GENERATOR_REGISTRY = {
3509
4051
  return lines.join("\n");
3510
4052
  },
3511
4053
  pathResolver: (rule, outputDir) => {
3512
- return (0, import_node_path12.join)(outputDir, `${rule.filename}.mdc`);
4054
+ return (0, import_node_path13.join)(outputDir, `${rule.filename}.mdc`);
3513
4055
  }
3514
4056
  },
3515
4057
  codexcli: {
@@ -3545,10 +4087,10 @@ var GENERATOR_REGISTRY = {
3545
4087
  pathResolver: (rule, outputDir) => {
3546
4088
  const outputFormat = rule.frontmatter.windsurfOutputFormat || "directory";
3547
4089
  if (outputFormat === "single-file") {
3548
- return (0, import_node_path12.join)(outputDir, ".windsurf-rules");
4090
+ return (0, import_node_path13.join)(outputDir, ".windsurf-rules");
3549
4091
  } else {
3550
- const rulesDir = (0, import_node_path12.join)(outputDir, ".windsurf", "rules");
3551
- return (0, import_node_path12.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`);
3552
4094
  }
3553
4095
  }
3554
4096
  },
@@ -3685,8 +4227,58 @@ var generateKiroConfig = createSimpleGenerator("kiro");
3685
4227
  var generateRooConfig = createSimpleGenerator("roo");
3686
4228
  var generateQwencodeConfig = createSimpleGenerator("qwencode");
3687
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
+ }
4267
+
3688
4268
  // src/generators/rules/codexcli.ts
3689
- 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
+ }
3690
4282
 
3691
4283
  // src/utils/xml-document-generator.ts
3692
4284
  var import_fast_xml_parser = require("fast-xml-parser");
@@ -3732,47 +4324,6 @@ function generateRootMarkdownWithXmlDocs(rootRule, memoryRules, config) {
3732
4324
  return lines.join("\n");
3733
4325
  }
3734
4326
 
3735
- // src/generators/rules/codexcli.ts
3736
- async function generateCodexConfig(rules, config, baseDir) {
3737
- const outputs = [];
3738
- const nonEmptyRules = rules.filter((rule) => rule.content.trim().length > 0);
3739
- if (nonEmptyRules.length > 0) {
3740
- const generatorConfig = {
3741
- tool: "codexcli",
3742
- fileExtension: ".md",
3743
- ignoreFileName: ".codexignore",
3744
- generateContent: generateCodexMemoryMarkdown,
3745
- generateDetailContent: generateCodexMemoryMarkdown,
3746
- generateRootContent: generateCodexRootMarkdown,
3747
- rootFilePath: "AGENTS.md",
3748
- detailSubDir: ".codex/memories"
3749
- };
3750
- const ruleOutputs = await generateComplexRules(nonEmptyRules, config, generatorConfig, baseDir);
3751
- outputs.push(...ruleOutputs);
3752
- } else {
3753
- const ignorePatterns = await loadIgnorePatterns(baseDir);
3754
- if (ignorePatterns.patterns.length > 0) {
3755
- const ignorePath = resolvePath(".codexignore", baseDir);
3756
- const ignoreContent = generateIgnoreFile2(ignorePatterns.patterns, "codexcli");
3757
- outputs.push({
3758
- tool: "codexcli",
3759
- filepath: ignorePath,
3760
- content: ignoreContent
3761
- });
3762
- }
3763
- }
3764
- return outputs;
3765
- }
3766
- function generateCodexMemoryMarkdown(rule) {
3767
- return rule.content.trim();
3768
- }
3769
- function generateCodexRootMarkdown(rootRule, memoryRules, _baseDir) {
3770
- return generateRootMarkdownWithXmlDocs(rootRule, memoryRules, {
3771
- memorySubDir: ".codex/memories",
3772
- fallbackTitle: "OpenAI Codex CLI Configuration"
3773
- });
3774
- }
3775
-
3776
4327
  // src/generators/rules/geminicli.ts
3777
4328
  async function generateGeminiConfig(rules, config, baseDir) {
3778
4329
  const generatorConfig = {
@@ -3830,23 +4381,17 @@ async function generateOpenCodeConfig(rules, config, baseDir) {
3830
4381
  tool: "opencode",
3831
4382
  fileExtension: ".md",
3832
4383
  // ignoreFileName omitted - OpenCode doesn't use dedicated ignore files
3833
- generateContent: generateOpenCodeMarkdown,
3834
- generateDetailContent: generateOpenCodeMarkdown,
3835
- 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
+ }),
3836
4390
  rootFilePath: "AGENTS.md",
3837
4391
  detailSubDir: ".opencode/memories"
3838
4392
  };
3839
4393
  return generateComplexRules(rules, config, generatorConfig, baseDir);
3840
4394
  }
3841
- function generateOpenCodeMarkdown(rule) {
3842
- return rule.content.trim();
3843
- }
3844
- function generateOpenCodeRootMarkdown(rootRule, memoryRules, _baseDir) {
3845
- return generateRootMarkdownWithXmlDocs(rootRule, memoryRules, {
3846
- memorySubDir: ".opencode/memories",
3847
- fallbackTitle: "OpenCode Configuration"
3848
- });
3849
- }
3850
4395
 
3851
4396
  // src/generators/rules/qwencode.ts
3852
4397
  async function generateQwencodeConfig2(rules, config, baseDir) {
@@ -3905,6 +4450,8 @@ function filterRulesForTool(rules, tool, config) {
3905
4450
  }
3906
4451
  async function generateForTool(tool, rules, config, baseDir) {
3907
4452
  switch (tool) {
4453
+ case "agentsmd":
4454
+ return await generateAgentsMdConfig(rules, config, baseDir);
3908
4455
  case "amazonqcli":
3909
4456
  return await generateAmazonqcliConfig(rules, config, baseDir);
3910
4457
  case "augmentcode": {
@@ -3964,7 +4511,7 @@ async function generateForTool(tool, rules, config, baseDir) {
3964
4511
  }
3965
4512
 
3966
4513
  // src/core/parser.ts
3967
- var import_node_path13 = require("path");
4514
+ var import_node_path14 = require("path");
3968
4515
  init_logger();
3969
4516
  async function parseRulesFromDirectory(aiRulesDir) {
3970
4517
  const ignorePatterns = await loadIgnorePatterns();
@@ -4018,7 +4565,7 @@ async function parseRuleFile(filepath) {
4018
4565
  },
4019
4566
  ...validatedData.tags !== void 0 && { tags: validatedData.tags }
4020
4567
  };
4021
- const filename = (0, import_node_path13.basename)(filepath, ".md");
4568
+ const filename = (0, import_node_path14.basename)(filepath, ".md");
4022
4569
  return {
4023
4570
  frontmatter,
4024
4571
  content: parsed.content,
@@ -4134,10 +4681,12 @@ function parseMcpConfig(projectRoot) {
4134
4681
  async function generateMcpConfigurations(mcpConfig, baseDir, targetTools) {
4135
4682
  const outputs = [];
4136
4683
  const toolMap = {
4684
+ agentsmd: async () => [],
4137
4685
  amazonqcli: async (servers, dir) => {
4138
4686
  const config = {
4139
4687
  aiRulesDir: ".rulesync",
4140
4688
  outputPaths: {
4689
+ agentsmd: ".agents/memories",
4141
4690
  amazonqcli: ".amazonq/rules",
4142
4691
  augmentcode: ".",
4143
4692
  "augmentcode-legacy": ".",
@@ -4216,231 +4765,594 @@ async function generateMcpConfigurations(mcpConfig, baseDir, targetTools) {
4216
4765
  return outputs;
4217
4766
  }
4218
4767
 
4219
- // 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
4220
4772
  init_logger();
4221
- async function generateCommand(options = {}) {
4222
- const configLoaderOptions = {
4223
- ...options.config !== void 0 && { configPath: options.config },
4224
- ...options.noConfig !== void 0 && { noConfig: options.noConfig }
4225
- };
4226
- const configResult = await loadConfig(configLoaderOptions);
4227
- const cliOptions = {
4228
- tools: options.tools,
4229
- ...options.verbose !== void 0 && { verbose: options.verbose },
4230
- ...options.delete !== void 0 && { delete: options.delete },
4231
- ...options.baseDirs !== void 0 && { baseDirs: options.baseDirs }
4232
- };
4233
- const config = mergeWithCliOptions(configResult.config, cliOptions);
4234
- if (!config.defaultTargets || config.defaultTargets.length === 0) {
4235
- const errorMessage = `\u274C Error: At least one tool must be specified.
4236
4773
 
4237
- Available tools:
4238
- --augmentcode Generate for AugmentCode
4239
- --augmentcode-legacy Generate for AugmentCode legacy format
4240
- --copilot Generate for GitHub Copilot
4241
- --cursor Generate for Cursor
4242
- --cline Generate for Cline
4243
- --codexcli Generate for OpenAI Codex CLI
4244
- --claudecode Generate for Claude Code
4245
- --roo Generate for Roo Code
4246
- --geminicli Generate for Gemini CLI
4247
- --junie Generate for JetBrains Junie
4248
- --qwencode Generate for Qwen Code
4249
- --kiro Generate for Kiro IDE
4250
- --opencode Generate for OpenCode
4251
- --windsurf Generate for Windsurf
4252
-
4253
- Example:
4254
- rulesync generate --copilot --cursor
4255
-
4256
- Or specify tools in rulesync.jsonc:
4257
- "tools": ["copilot", "cursor"]`;
4258
- logger.error(errorMessage);
4259
- process.exit(1);
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
+ }
4260
4820
  }
4261
- logger.setVerbose(config.verbose || false);
4262
- let baseDirs;
4263
- if (config.baseDir) {
4264
- baseDirs = Array.isArray(config.baseDir) ? config.baseDir : [config.baseDir];
4265
- } else if (options.baseDirs) {
4266
- baseDirs = options.baseDirs;
4267
- } else {
4268
- baseDirs = [process.cwd()];
4821
+ };
4822
+
4823
+ // src/generators/subagents/claudecode.ts
4824
+ var ClaudeCodeSubagentGenerator = class extends BaseSubagentGenerator {
4825
+ getToolName() {
4826
+ return "claudecode";
4269
4827
  }
4270
- logger.info(`Loaded configuration from: ${configResult.filepath}`);
4271
- logger.log("Generating configuration files...");
4272
- if (!await fileExists(config.aiRulesDir)) {
4273
- logger.error("\u274C .rulesync directory not found. Run 'rulesync init' first.");
4274
- process.exit(1);
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;
4275
4911
  }
4912
+ }
4913
+ async function parseSubagentsFromDirectory(agentsDir) {
4276
4914
  try {
4277
- logger.info(`Parsing rules from ${config.aiRulesDir}...`);
4278
- const rules = await parseRulesFromDirectory(config.aiRulesDir);
4279
- if (rules.length === 0) {
4280
- logger.warn("\u26A0\uFE0F No rules found in .rulesync directory");
4281
- return;
4915
+ if (!await fileExists(agentsDir)) {
4916
+ logger.debug(`Agents directory does not exist: ${agentsDir}`);
4917
+ return [];
4282
4918
  }
4283
- logger.info(`Found ${rules.length} rule(s)`);
4284
- logger.info(`Base directories: ${baseDirs.join(", ")}`);
4285
- if (config.delete) {
4286
- logger.info("Deleting existing output directories...");
4287
- const targetTools = config.defaultTargets;
4288
- const deleteTasks = [];
4289
- const commandsDir = (0, import_node_path14.join)(config.aiRulesDir, "commands");
4290
- const hasCommands = await fileExists(commandsDir);
4291
- let hasCommandFiles = false;
4292
- if (hasCommands) {
4293
- const { readdir: readdir2 } = await import("fs/promises");
4294
- try {
4295
- const files = await readdir2(commandsDir);
4296
- hasCommandFiles = files.some((file) => file.endsWith(".md"));
4297
- } catch {
4298
- hasCommandFiles = false;
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 [];
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 = [];
4944
+ try {
4945
+ parsedSubagents = await parseSubagentsFromDirectory(subagentsDir);
4946
+ if (parsedSubagents.length > 0) {
4947
+ logger.debug(`Found ${parsedSubagents.length} subagent files in ${subagentsDir}`);
4948
+ }
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
+ }
4299
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;
5234
+ }
5235
+ } else {
5236
+ logger.info("\nSkipping rule generation (not in --features)");
4300
5237
  }
4301
- for (const tool of targetTools) {
4302
- switch (tool) {
4303
- case "augmentcode":
4304
- deleteTasks.push(removeDirectory((0, import_node_path14.join)(".augment", "rules")));
4305
- deleteTasks.push(removeDirectory((0, import_node_path14.join)(".augment", "ignore")));
4306
- break;
4307
- case "augmentcode-legacy":
4308
- deleteTasks.push(removeClaudeGeneratedFiles());
4309
- deleteTasks.push(removeDirectory((0, import_node_path14.join)(".augment", "ignore")));
4310
- break;
4311
- case "copilot":
4312
- deleteTasks.push(removeDirectory(config.outputPaths.copilot));
4313
- break;
4314
- case "cursor":
4315
- deleteTasks.push(removeDirectory(config.outputPaths.cursor));
4316
- break;
4317
- case "cline":
4318
- deleteTasks.push(removeDirectory(config.outputPaths.cline));
4319
- break;
4320
- case "claudecode":
4321
- deleteTasks.push(removeClaudeGeneratedFiles());
4322
- if (hasCommandFiles) {
4323
- deleteTasks.push(removeDirectory((0, import_node_path14.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;
4324
5247
  }
4325
- break;
4326
- case "roo":
4327
- deleteTasks.push(removeDirectory(config.outputPaths.roo));
4328
- if (hasCommandFiles) {
4329
- deleteTasks.push(removeDirectory((0, import_node_path14.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;
4330
5256
  }
4331
- break;
4332
- case "geminicli":
4333
- deleteTasks.push(removeDirectory(config.outputPaths.geminicli));
4334
- if (hasCommandFiles) {
4335
- deleteTasks.push(removeDirectory((0, import_node_path14.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++;
4336
5261
  }
4337
- break;
4338
- case "kiro":
4339
- deleteTasks.push(removeDirectory(config.outputPaths.kiro));
4340
- break;
4341
- case "opencode":
4342
- deleteTasks.push(removeDirectory(config.outputPaths.opencode));
4343
- break;
4344
- case "qwencode":
4345
- deleteTasks.push(removeDirectory(config.outputPaths.qwencode));
4346
- break;
4347
- case "windsurf":
4348
- deleteTasks.push(removeDirectory(config.outputPaths.windsurf));
4349
- break;
5262
+ } catch (error) {
5263
+ logger.error(
5264
+ `\u274C Failed to generate MCP configurations: ${error instanceof Error ? error.message : String(error)}`
5265
+ );
5266
+ }
4350
5267
  }
5268
+ } else {
5269
+ logger.info("\nSkipping MCP configuration generation (not in --features)");
4351
5270
  }
4352
- await Promise.all(deleteTasks);
4353
- logger.info("Deleted existing output directories");
4354
- }
4355
- let totalOutputs = 0;
4356
- for (const baseDir of baseDirs) {
4357
- logger.info(`
4358
- Generating configurations for base directory: ${baseDir}`);
4359
- const outputs = await generateConfigurations(rules, config, config.defaultTargets, baseDir);
4360
- if (outputs.length === 0) {
4361
- logger.warn(`\u26A0\uFE0F No configurations generated for ${baseDir}`);
4362
- 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)");
4363
5292
  }
4364
- for (const output of outputs) {
4365
- await writeFileContent(output.filepath, output.content);
4366
- 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)");
4367
5299
  }
4368
- totalOutputs += outputs.length;
4369
- }
4370
- if (totalOutputs === 0) {
4371
- logger.warn("\u26A0\uFE0F No configurations generated");
4372
- return;
4373
- }
4374
- logger.info("\nGenerating MCP configurations...");
4375
- let totalMcpOutputs = 0;
4376
- for (const baseDir of baseDirs) {
4377
- try {
4378
- const mcpConfig = parseMcpConfig(process.cwd());
4379
- if (!mcpConfig || !mcpConfig.mcpServers || Object.keys(mcpConfig.mcpServers).length === 0) {
4380
- logger.info(`No MCP configuration found for ${baseDir}`);
4381
- continue;
4382
- }
4383
- const mcpResults = await generateMcpConfigurations(
4384
- mcpConfig,
4385
- baseDir === process.cwd() ? "." : baseDir,
4386
- config.defaultTargets
4387
- );
4388
- if (mcpResults.length === 0) {
4389
- logger.info(`No MCP configurations generated for ${baseDir}`);
4390
- continue;
4391
- }
4392
- for (const result of mcpResults) {
4393
- await writeFileContent(result.filepath, result.content);
4394
- logger.success(`Generated ${result.tool} MCP configuration: ${result.filepath}`);
4395
- 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
+ }
4396
5319
  }
4397
- } catch (error) {
4398
- logger.error(
4399
- `\u274C Failed to generate MCP configurations: ${error instanceof Error ? error.message : String(error)}`
4400
- );
5320
+ } else {
5321
+ logger.info("\nSkipping subagent file generation (not in --features)");
4401
5322
  }
4402
- }
4403
- logger.info("\nGenerating command files...");
4404
- let totalCommandOutputs = 0;
4405
- for (const baseDir of baseDirs) {
4406
- const commandResults = await generateCommands(
4407
- process.cwd(),
4408
- baseDir === process.cwd() ? void 0 : baseDir,
4409
- config.defaultTargets
4410
- );
4411
- if (commandResults.length === 0) {
4412
- logger.info(`No commands found for ${baseDir}`);
4413
- continue;
4414
- }
4415
- for (const result of commandResults) {
4416
- await writeFileContent(result.filepath, result.content);
4417
- logger.success(`Generated ${result.tool} command: ${result.filepath}`);
4418
- totalCommandOutputs++;
4419
- }
4420
- }
4421
- const totalGenerated = totalOutputs + totalMcpOutputs + totalCommandOutputs;
4422
- if (totalGenerated > 0) {
4423
- const parts = [];
4424
- if (totalOutputs > 0) parts.push(`${totalOutputs} configurations`);
4425
- if (totalMcpOutputs > 0) parts.push(`${totalMcpOutputs} MCP configurations`);
4426
- if (totalCommandOutputs > 0) parts.push(`${totalCommandOutputs} commands`);
4427
- logger.success(
4428
- `
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
+ `
4429
5337
  \u{1F389} All done! Generated ${totalGenerated} file(s) total (${parts.join(" + ")})`
4430
- );
5338
+ );
5339
+ }
5340
+ } catch (error) {
5341
+ logger.error("\u274C Failed to generate configurations:", error);
5342
+ process.exit(1);
4431
5343
  }
4432
5344
  } catch (error) {
4433
- logger.error("\u274C Failed to generate configurations:", error);
5345
+ logger.error("\u274C Failed to resolve configuration:", error);
4434
5346
  process.exit(1);
4435
5347
  }
4436
5348
  }
4437
5349
 
4438
5350
  // src/cli/commands/gitignore.ts
4439
5351
  var import_node_fs2 = require("fs");
4440
- var import_node_path15 = require("path");
5352
+ var import_node_path19 = require("path");
4441
5353
  init_logger();
4442
5354
  var gitignoreCommand = async () => {
4443
- const gitignorePath = (0, import_node_path15.join)(process.cwd(), ".gitignore");
5355
+ const gitignorePath = (0, import_node_path19.join)(process.cwd(), ".gitignore");
4444
5356
  const rulesFilesToIgnore = [
4445
5357
  "# Generated by rulesync - AI tool configuration files",
4446
5358
  "**/.amazonq/rules/",
@@ -4454,7 +5366,9 @@ var gitignoreCommand = async () => {
4454
5366
  "**/CLAUDE.md",
4455
5367
  "**/.claude/memories/",
4456
5368
  "**/.claude/commands/",
5369
+ "**/.claude/agents/",
4457
5370
  "**/AGENTS.md",
5371
+ "**/.agents/",
4458
5372
  "**/.codexignore",
4459
5373
  "**/.roo/rules/",
4460
5374
  "**/.rooignore",
@@ -4514,11 +5428,155 @@ ${linesToAdd.join("\n")}
4514
5428
  };
4515
5429
 
4516
5430
  // src/core/importer.ts
4517
- var import_node_path22 = require("path");
4518
- 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
+ }
4519
5577
 
4520
5578
  // src/parsers/shared-helpers.ts
4521
- var import_node_path16 = require("path");
5579
+ var import_node_path21 = require("path");
4522
5580
  init_file();
4523
5581
  async function parseConfigurationFiles(baseDir = process.cwd(), config) {
4524
5582
  const errors = [];
@@ -4572,11 +5630,11 @@ async function parseConfigurationFiles(baseDir = process.cwd(), config) {
4572
5630
  const dirPath = resolvePath(dirConfig.directory, baseDir);
4573
5631
  if (await fileExists(dirPath)) {
4574
5632
  const result = await safeAsyncOperation(async () => {
4575
- const { readdir: readdir2 } = await import("fs/promises");
4576
- const files = await readdir2(dirPath);
5633
+ const { readdir: readdir3 } = await import("fs/promises");
5634
+ const files = await readdir3(dirPath);
4577
5635
  for (const file of files) {
4578
5636
  if (file.endsWith(dirConfig.filePattern)) {
4579
- const filePath = (0, import_node_path16.join)(dirPath, file);
5637
+ const filePath = (0, import_node_path21.join)(dirPath, file);
4580
5638
  const fileResult = await safeAsyncOperation(async () => {
4581
5639
  const rawContent = await readFileContent(filePath);
4582
5640
  let content;
@@ -4723,14 +5781,14 @@ function parseMainFile(content, filepath, config) {
4723
5781
  async function parseMemoryFiles(memoryDir, config) {
4724
5782
  const rules = [];
4725
5783
  try {
4726
- const { readdir: readdir2 } = await import("fs/promises");
4727
- const files = await readdir2(memoryDir);
5784
+ const { readdir: readdir3 } = await import("fs/promises");
5785
+ const files = await readdir3(memoryDir);
4728
5786
  for (const file of files) {
4729
5787
  if (file.endsWith(".md")) {
4730
- const filePath = (0, import_node_path16.join)(memoryDir, file);
5788
+ const filePath = (0, import_node_path21.join)(memoryDir, file);
4731
5789
  const content = await readFileContent(filePath);
4732
5790
  if (content.trim()) {
4733
- const filename = (0, import_node_path16.basename)(file, ".md");
5791
+ const filename = (0, import_node_path21.basename)(file, ".md");
4734
5792
  const frontmatter = {
4735
5793
  root: false,
4736
5794
  targets: [config.tool],
@@ -4753,14 +5811,14 @@ async function parseMemoryFiles(memoryDir, config) {
4753
5811
  async function parseCommandsFiles(commandsDir, config) {
4754
5812
  const rules = [];
4755
5813
  try {
4756
- const { readdir: readdir2 } = await import("fs/promises");
4757
- const files = await readdir2(commandsDir);
5814
+ const { readdir: readdir3 } = await import("fs/promises");
5815
+ const files = await readdir3(commandsDir);
4758
5816
  for (const file of files) {
4759
5817
  if (file.endsWith(".md")) {
4760
- const filePath = (0, import_node_path16.join)(commandsDir, file);
5818
+ const filePath = (0, import_node_path21.join)(commandsDir, file);
4761
5819
  const content = await readFileContent(filePath);
4762
5820
  if (content.trim()) {
4763
- const filename = (0, import_node_path16.basename)(file, ".md");
5821
+ const filename = (0, import_node_path21.basename)(file, ".md");
4764
5822
  let frontmatter;
4765
5823
  let ruleContent;
4766
5824
  try {
@@ -4849,7 +5907,7 @@ async function parseAmazonqcliConfiguration(baseDir = process.cwd()) {
4849
5907
  }
4850
5908
 
4851
5909
  // src/parsers/augmentcode.ts
4852
- var import_node_path17 = require("path");
5910
+ var import_node_path22 = require("path");
4853
5911
 
4854
5912
  // src/utils/parser-helpers.ts
4855
5913
  function createParseResult() {
@@ -4897,7 +5955,7 @@ async function parseAugmentcodeLegacyConfiguration(baseDir = process.cwd()) {
4897
5955
  async function parseUnifiedAugmentcode(baseDir, config) {
4898
5956
  const result = createParseResult();
4899
5957
  if (config.rulesDir) {
4900
- const rulesDir = (0, import_node_path17.join)(baseDir, config.rulesDir);
5958
+ const rulesDir = (0, import_node_path22.join)(baseDir, config.rulesDir);
4901
5959
  if (await fileExists(rulesDir)) {
4902
5960
  const rulesResult = await parseAugmentRules(rulesDir, config);
4903
5961
  addRules(result, rulesResult.rules);
@@ -4910,7 +5968,7 @@ async function parseUnifiedAugmentcode(baseDir, config) {
4910
5968
  }
4911
5969
  }
4912
5970
  if (config.legacyFilePath) {
4913
- const legacyPath = (0, import_node_path17.join)(baseDir, config.legacyFilePath);
5971
+ const legacyPath = (0, import_node_path22.join)(baseDir, config.legacyFilePath);
4914
5972
  if (await fileExists(legacyPath)) {
4915
5973
  const legacyResult = await parseAugmentGuidelines(legacyPath, config);
4916
5974
  if (legacyResult.rule) {
@@ -4930,11 +5988,11 @@ async function parseAugmentRules(rulesDir, config) {
4930
5988
  const rules = [];
4931
5989
  const errors = [];
4932
5990
  try {
4933
- const { readdir: readdir2 } = await import("fs/promises");
4934
- const files = await readdir2(rulesDir);
5991
+ const { readdir: readdir3 } = await import("fs/promises");
5992
+ const files = await readdir3(rulesDir);
4935
5993
  for (const file of files) {
4936
5994
  if (file.endsWith(".md") || file.endsWith(".mdc")) {
4937
- const filePath = (0, import_node_path17.join)(rulesDir, file);
5995
+ const filePath = (0, import_node_path22.join)(rulesDir, file);
4938
5996
  try {
4939
5997
  const rawContent = await readFileContent(filePath);
4940
5998
  const parsed = parseFrontmatter(rawContent);
@@ -4942,7 +6000,7 @@ async function parseAugmentRules(rulesDir, config) {
4942
6000
  const description = extractStringField(parsed.data, "description", "");
4943
6001
  const tags = extractArrayField(parsed.data, "tags");
4944
6002
  const isRoot = ruleType === "always";
4945
- const filename = (0, import_node_path17.basename)(file, file.endsWith(".mdc") ? ".mdc" : ".md");
6003
+ const filename = (0, import_node_path22.basename)(file, file.endsWith(".mdc") ? ".mdc" : ".md");
4946
6004
  const frontmatter = {
4947
6005
  root: isRoot,
4948
6006
  targets: [config.targetName],
@@ -5000,8 +6058,9 @@ async function parseAugmentGuidelines(guidelinesPath, config) {
5000
6058
  }
5001
6059
 
5002
6060
  // src/parsers/claudecode.ts
6061
+ init_file();
5003
6062
  async function parseClaudeConfiguration(baseDir = process.cwd()) {
5004
- return parseMemoryBasedConfiguration(baseDir, {
6063
+ const memoryResult = await parseMemoryBasedConfiguration(baseDir, {
5005
6064
  tool: "claudecode",
5006
6065
  mainFileName: "CLAUDE.md",
5007
6066
  memoryDirPath: ".claude/memories",
@@ -5011,6 +6070,24 @@ async function parseClaudeConfiguration(baseDir = process.cwd()) {
5011
6070
  filenamePrefix: "claude",
5012
6071
  commandsDirPath: ".claude/commands"
5013
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;
5014
6091
  }
5015
6092
 
5016
6093
  // src/parsers/cline.ts
@@ -5034,7 +6111,7 @@ async function parseClineConfiguration(baseDir = process.cwd()) {
5034
6111
  }
5035
6112
 
5036
6113
  // src/parsers/codexcli.ts
5037
- var import_node_path18 = require("path");
6114
+ var import_node_path23 = require("path");
5038
6115
 
5039
6116
  // src/parsers/copilot.ts
5040
6117
  async function parseCopilotConfiguration(baseDir = process.cwd()) {
@@ -5057,9 +6134,9 @@ async function parseCopilotConfiguration(baseDir = process.cwd()) {
5057
6134
  }
5058
6135
 
5059
6136
  // src/parsers/cursor.ts
5060
- var import_node_path19 = require("path");
6137
+ var import_node_path24 = require("path");
5061
6138
  var import_js_yaml = require("js-yaml");
5062
- var import_mini8 = require("zod/mini");
6139
+ var import_mini9 = require("zod/mini");
5063
6140
  var customMatterOptions = {
5064
6141
  engines: {
5065
6142
  yaml: {
@@ -5087,7 +6164,7 @@ var customMatterOptions = {
5087
6164
  }
5088
6165
  };
5089
6166
  function convertCursorMdcFrontmatter(cursorFrontmatter, _filename) {
5090
- 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());
5091
6168
  const parseResult = FrontmatterSchema.safeParse(cursorFrontmatter);
5092
6169
  if (!parseResult.success) {
5093
6170
  return {
@@ -5181,7 +6258,7 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
5181
6258
  const rules = [];
5182
6259
  let ignorePatterns;
5183
6260
  let mcpServers;
5184
- const cursorFilePath = (0, import_node_path19.join)(baseDir, ".cursorrules");
6261
+ const cursorFilePath = (0, import_node_path24.join)(baseDir, ".cursorrules");
5185
6262
  if (await fileExists(cursorFilePath)) {
5186
6263
  try {
5187
6264
  const rawContent = await readFileContent(cursorFilePath);
@@ -5202,20 +6279,20 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
5202
6279
  errors.push(`Failed to parse .cursorrules file: ${errorMessage}`);
5203
6280
  }
5204
6281
  }
5205
- const cursorRulesDir = (0, import_node_path19.join)(baseDir, ".cursor", "rules");
6282
+ const cursorRulesDir = (0, import_node_path24.join)(baseDir, ".cursor", "rules");
5206
6283
  if (await fileExists(cursorRulesDir)) {
5207
6284
  try {
5208
- const { readdir: readdir2 } = await import("fs/promises");
5209
- const files = await readdir2(cursorRulesDir);
6285
+ const { readdir: readdir3 } = await import("fs/promises");
6286
+ const files = await readdir3(cursorRulesDir);
5210
6287
  for (const file of files) {
5211
6288
  if (file.endsWith(".mdc")) {
5212
- const filePath = (0, import_node_path19.join)(cursorRulesDir, file);
6289
+ const filePath = (0, import_node_path24.join)(cursorRulesDir, file);
5213
6290
  try {
5214
6291
  const rawContent = await readFileContent(filePath);
5215
6292
  const parsed = parseFrontmatter(rawContent, { matterOptions: customMatterOptions });
5216
6293
  const content = parsed.content;
5217
6294
  if (content) {
5218
- const filename = (0, import_node_path19.basename)(file, ".mdc");
6295
+ const filename = (0, import_node_path24.basename)(file, ".mdc");
5219
6296
  const frontmatter = convertCursorMdcFrontmatter(parsed.data, filename);
5220
6297
  rules.push({
5221
6298
  frontmatter,
@@ -5238,7 +6315,7 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
5238
6315
  if (rules.length === 0) {
5239
6316
  errors.push("No Cursor configuration files found (.cursorrules or .cursor/rules/*.mdc)");
5240
6317
  }
5241
- const cursorIgnorePath = (0, import_node_path19.join)(baseDir, ".cursorignore");
6318
+ const cursorIgnorePath = (0, import_node_path24.join)(baseDir, ".cursorignore");
5242
6319
  if (await fileExists(cursorIgnorePath)) {
5243
6320
  try {
5244
6321
  const content = await readFileContent(cursorIgnorePath);
@@ -5251,7 +6328,7 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
5251
6328
  errors.push(`Failed to parse .cursorignore: ${errorMessage}`);
5252
6329
  }
5253
6330
  }
5254
- const cursorMcpPath = (0, import_node_path19.join)(baseDir, ".cursor", "mcp.json");
6331
+ const cursorMcpPath = (0, import_node_path24.join)(baseDir, ".cursor", "mcp.json");
5255
6332
  if (await fileExists(cursorMcpPath)) {
5256
6333
  try {
5257
6334
  const content = await readFileContent(cursorMcpPath);
@@ -5274,6 +6351,7 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
5274
6351
  }
5275
6352
 
5276
6353
  // src/parsers/geminicli.ts
6354
+ var import_node_path25 = require("path");
5277
6355
  async function parseAiexclude(aiexcludePath) {
5278
6356
  try {
5279
6357
  const content = await readFileContent(aiexcludePath);
@@ -5283,8 +6361,51 @@ async function parseAiexclude(aiexcludePath) {
5283
6361
  return [];
5284
6362
  }
5285
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
+ }
5286
6407
  async function parseGeminiConfiguration(baseDir = process.cwd()) {
5287
- return parseMemoryBasedConfiguration(baseDir, {
6408
+ const result = await parseMemoryBasedConfiguration(baseDir, {
5288
6409
  tool: "geminicli",
5289
6410
  mainFileName: "GEMINI.md",
5290
6411
  memoryDirPath: ".gemini/memories",
@@ -5295,17 +6416,23 @@ async function parseGeminiConfiguration(baseDir = process.cwd()) {
5295
6416
  additionalIgnoreFile: {
5296
6417
  path: ".aiexclude",
5297
6418
  parser: parseAiexclude
5298
- },
5299
- commandsDirPath: ".gemini/commands"
6419
+ }
6420
+ // commandsDirPath is removed - Gemini uses .toml files which need special handling
5300
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;
5301
6428
  }
5302
6429
 
5303
6430
  // src/parsers/junie.ts
5304
- var import_node_path20 = require("path");
6431
+ var import_node_path26 = require("path");
5305
6432
  async function parseJunieConfiguration(baseDir = process.cwd()) {
5306
6433
  const errors = [];
5307
6434
  const rules = [];
5308
- const guidelinesPath = (0, import_node_path20.join)(baseDir, ".junie", "guidelines.md");
6435
+ const guidelinesPath = (0, import_node_path26.join)(baseDir, ".junie", "guidelines.md");
5309
6436
  if (!await fileExists(guidelinesPath)) {
5310
6437
  errors.push(".junie/guidelines.md file not found");
5311
6438
  return { rules, errors };
@@ -5399,8 +6526,8 @@ async function parseRooConfiguration(baseDir = process.cwd()) {
5399
6526
  }
5400
6527
 
5401
6528
  // src/parsers/windsurf.ts
5402
- var import_promises3 = require("fs/promises");
5403
- var import_node_path21 = require("path");
6529
+ var import_promises4 = require("fs/promises");
6530
+ var import_node_path27 = require("path");
5404
6531
  init_logger();
5405
6532
 
5406
6533
  // src/core/importer.ts
@@ -5408,6 +6535,8 @@ init_logger();
5408
6535
  async function importConfiguration(options) {
5409
6536
  const {
5410
6537
  tool,
6538
+ features = ["rules", "commands", "mcp", "ignore", "subagents"],
6539
+ // Default to all features for backward compatibility
5411
6540
  baseDir = process.cwd(),
5412
6541
  rulesDir = ".rulesync",
5413
6542
  verbose = false,
@@ -5417,11 +6546,18 @@ async function importConfiguration(options) {
5417
6546
  let rules = [];
5418
6547
  let ignorePatterns;
5419
6548
  let mcpServers;
6549
+ let subagents;
5420
6550
  if (verbose) {
5421
6551
  logger.log(`Importing ${tool} configuration from ${baseDir}...`);
5422
6552
  }
5423
6553
  try {
5424
6554
  switch (tool) {
6555
+ case "agentsmd": {
6556
+ const agentsmdResult = await parseAgentsMdConfiguration(baseDir);
6557
+ rules = agentsmdResult.rules;
6558
+ errors.push(...agentsmdResult.errors);
6559
+ break;
6560
+ }
5425
6561
  case "amazonqcli": {
5426
6562
  const amazonqResult = await parseAmazonqcliConfiguration(baseDir);
5427
6563
  rules = amazonqResult.rules;
@@ -5447,6 +6583,7 @@ async function importConfiguration(options) {
5447
6583
  errors.push(...claudeResult.errors);
5448
6584
  ignorePatterns = claudeResult.ignorePatterns;
5449
6585
  mcpServers = claudeResult.mcpServers;
6586
+ subagents = claudeResult.subagents;
5450
6587
  break;
5451
6588
  }
5452
6589
  case "cursor": {
@@ -5513,10 +6650,20 @@ async function importConfiguration(options) {
5513
6650
  errors.push(`Failed to parse ${tool} configuration: ${errorMessage}`);
5514
6651
  return { success: false, rulesCreated: 0, errors };
5515
6652
  }
5516
- if (rules.length === 0 && !ignorePatterns && !mcpServers) {
6653
+ if (rules.length === 0 && !ignorePatterns && !mcpServers && !subagents) {
5517
6654
  return { success: false, rulesCreated: 0, errors };
5518
6655
  }
5519
- const rulesDirPath = (0, import_node_path22.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);
5520
6667
  try {
5521
6668
  const { mkdir: mkdir3 } = await import("fs/promises");
5522
6669
  await mkdir3(rulesDirPath, { recursive: true });
@@ -5526,37 +6673,49 @@ async function importConfiguration(options) {
5526
6673
  return { success: false, rulesCreated: 0, errors };
5527
6674
  }
5528
6675
  let rulesCreated = 0;
5529
- for (const rule of rules) {
5530
- try {
5531
- const baseFilename = rule.filename;
5532
- let targetDir = rulesDirPath;
5533
- if (rule.type === "command") {
5534
- targetDir = (0, import_node_path22.join)(rulesDirPath, "commands");
5535
- const { mkdir: mkdir3 } = await import("fs/promises");
5536
- await mkdir3(targetDir, { recursive: true });
5537
- } else {
5538
- if (!useLegacyLocation) {
5539
- targetDir = (0, import_node_path22.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");
5540
6686
  const { mkdir: mkdir3 } = await import("fs/promises");
5541
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
+ }
5542
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}`);
5543
6708
  }
5544
- const filePath = (0, import_node_path22.join)(targetDir, `${baseFilename}.md`);
5545
- const content = generateRuleFileContent(rule);
5546
- await writeFileContent(filePath, content);
5547
- rulesCreated++;
5548
- if (verbose) {
5549
- logger.success(`Created rule file: ${filePath}`);
5550
- }
5551
- } catch (error) {
5552
- const errorMessage = error instanceof Error ? error.message : String(error);
5553
- 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)`);
5554
6713
  }
5555
6714
  }
5556
6715
  let ignoreFileCreated = false;
5557
- if (ignorePatterns && ignorePatterns.length > 0) {
6716
+ if (ignoreEnabled && ignorePatterns && ignorePatterns.length > 0) {
5558
6717
  try {
5559
- const rulesyncignorePath = (0, import_node_path22.join)(baseDir, ".rulesyncignore");
6718
+ const rulesyncignorePath = (0, import_node_path28.join)(baseDir, ".rulesyncignore");
5560
6719
  const ignoreContent = `${ignorePatterns.join("\n")}
5561
6720
  `;
5562
6721
  await writeFileContent(rulesyncignorePath, ignoreContent);
@@ -5568,11 +6727,13 @@ async function importConfiguration(options) {
5568
6727
  const errorMessage = error instanceof Error ? error.message : String(error);
5569
6728
  errors.push(`Failed to create .rulesyncignore: ${errorMessage}`);
5570
6729
  }
6730
+ } else if (verbose && ignorePatterns && ignorePatterns.length > 0 && !ignoreEnabled) {
6731
+ logger.log(`Skipping ignore patterns (ignore feature not enabled)`);
5571
6732
  }
5572
6733
  let mcpFileCreated = false;
5573
- if (mcpServers && Object.keys(mcpServers).length > 0) {
6734
+ if (mcpEnabled && mcpServers && Object.keys(mcpServers).length > 0) {
5574
6735
  try {
5575
- const mcpPath = (0, import_node_path22.join)(baseDir, rulesDir, ".mcp.json");
6736
+ const mcpPath = (0, import_node_path28.join)(baseDir, rulesDir, ".mcp.json");
5576
6737
  const mcpContent = `${JSON.stringify({ mcpServers }, null, 2)}
5577
6738
  `;
5578
6739
  await writeFileContent(mcpPath, mcpContent);
@@ -5584,14 +6745,51 @@ async function importConfiguration(options) {
5584
6745
  const errorMessage = error instanceof Error ? error.message : String(error);
5585
6746
  errors.push(`Failed to create .mcp.json: ${errorMessage}`);
5586
6747
  }
6748
+ } else if (verbose && mcpServers && Object.keys(mcpServers).length > 0 && !mcpEnabled) {
6749
+ logger.log(`Skipping MCP configuration (mcp feature not enabled)`);
5587
6750
  }
5588
- return {
5589
- 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),
5590
6784
  rulesCreated,
5591
6785
  errors,
5592
6786
  ignoreFileCreated,
5593
6787
  mcpFileCreated
5594
6788
  };
6789
+ if (subagentsCreated > 0) {
6790
+ result.subagentsCreated = subagentsCreated;
6791
+ }
6792
+ return result;
5595
6793
  }
5596
6794
  function generateRuleFileContent(rule) {
5597
6795
  if (rule.type === "command") {
@@ -5599,70 +6797,109 @@ function generateRuleFileContent(rule) {
5599
6797
  description: rule.frontmatter.description,
5600
6798
  targets: rule.frontmatter.targets
5601
6799
  };
5602
- const frontmatter2 = import_gray_matter2.default.stringify("", simplifiedFrontmatter);
6800
+ const frontmatter2 = import_gray_matter3.default.stringify("", simplifiedFrontmatter);
5603
6801
  return frontmatter2 + rule.content;
5604
6802
  }
5605
- const frontmatter = import_gray_matter2.default.stringify("", rule.frontmatter);
6803
+ const frontmatter = import_gray_matter3.default.stringify("", rule.frontmatter);
5606
6804
  return frontmatter + rule.content;
5607
6805
  }
6806
+ function generateSubagentFileContent(subagent) {
6807
+ const frontmatter = import_gray_matter3.default.stringify("", subagent.frontmatter);
6808
+ return frontmatter + subagent.content;
6809
+ }
5608
6810
 
5609
6811
  // src/cli/commands/import.ts
5610
6812
  init_logger();
5611
6813
  async function importCommand(options = {}) {
5612
6814
  logger.setVerbose(options.verbose || false);
5613
- const tools = [];
5614
- if (options.amazonqcli) tools.push("amazonqcli");
5615
- if (options.augmentcode) tools.push("augmentcode");
5616
- if (options["augmentcode-legacy"]) tools.push("augmentcode-legacy");
5617
- if (options.claudecode) tools.push("claudecode");
5618
- if (options.cursor) tools.push("cursor");
5619
- if (options.copilot) tools.push("copilot");
5620
- if (options.cline) tools.push("cline");
5621
- if (options.roo) tools.push("roo");
5622
- if (options.geminicli) tools.push("geminicli");
5623
- if (options.qwencode) tools.push("qwencode");
5624
- if (options.opencode) tools.push("opencode");
5625
- if (tools.length === 0) {
5626
- logger.error(
5627
- "\u274C Please specify one tool to import from (--amazonqcli, --augmentcode, --augmentcode-legacy, --claudecode, --cursor, --copilot, --cline, --roo, --geminicli, --qwencode, --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"
5628
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");
5629
6851
  process.exit(1);
5630
6852
  }
5631
6853
  if (tools.length > 1) {
5632
6854
  logger.error(
5633
- "\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:`
5634
6859
  );
6860
+ for (const tool2 of tools) {
6861
+ logger.info(` rulesync import --targets ${tool2}`);
6862
+ }
5635
6863
  process.exit(1);
5636
6864
  }
5637
6865
  const tool = tools[0];
5638
6866
  if (!tool) {
5639
- logger.error("Error: No tool specified");
6867
+ logger.error("\u274C Unexpected error: No tool selected");
5640
6868
  process.exit(1);
5641
6869
  }
5642
6870
  logger.log(`Importing configuration files from ${tool}...`);
5643
6871
  try {
5644
6872
  const result = await importConfiguration({
5645
6873
  tool,
6874
+ features: normalizedFeatures,
5646
6875
  verbose: options.verbose ?? false,
5647
6876
  useLegacyLocation: options.legacy ?? false
5648
6877
  });
5649
6878
  if (result.success) {
5650
- logger.success(`Imported ${result.rulesCreated} rule(s) from ${tool}`);
6879
+ logger.success(`\u2705 Imported ${result.rulesCreated} rule(s) from ${tool}`);
5651
6880
  if (result.ignoreFileCreated) {
5652
- logger.success("Created .rulesyncignore file from ignore patterns");
6881
+ logger.success(" Created .rulesyncignore file from ignore patterns");
5653
6882
  }
5654
6883
  if (result.mcpFileCreated) {
5655
- 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`);
5656
6888
  }
6889
+ logger.success(`
6890
+ \u{1F389} Successfully imported from ${tool}`);
5657
6891
  logger.log("You can now run 'rulesync generate' to create tool-specific configurations.");
5658
6892
  } else if (result.errors.length > 0) {
5659
6893
  logger.warn(`\u26A0\uFE0F Failed to import from ${tool}: ${result.errors[0]}`);
5660
6894
  if (result.errors.length > 1) {
5661
- logger.info("\nDetailed errors:");
6895
+ logger.info(" Detailed errors:");
5662
6896
  for (const error of result.errors) {
5663
- logger.info(` - ${error}`);
6897
+ logger.info(` - ${error}`);
5664
6898
  }
5665
6899
  }
6900
+ logger.error(`
6901
+ \u274C Failed to import from ${tool}.`);
6902
+ process.exit(1);
5666
6903
  }
5667
6904
  } catch (error) {
5668
6905
  const errorMessage = error instanceof Error ? error.message : String(error);
@@ -5672,7 +6909,7 @@ async function importCommand(options = {}) {
5672
6909
  }
5673
6910
 
5674
6911
  // src/cli/commands/init.ts
5675
- var import_node_path23 = require("path");
6912
+ var import_node_path29 = require("path");
5676
6913
  init_logger();
5677
6914
  async function initCommand(options = {}) {
5678
6915
  const configResult = await loadConfig();
@@ -5681,7 +6918,7 @@ async function initCommand(options = {}) {
5681
6918
  logger.log("Initializing rulesync...");
5682
6919
  await ensureDir(aiRulesDir);
5683
6920
  const useLegacy = options.legacy ?? config.legacy ?? false;
5684
- const rulesDir = useLegacy ? aiRulesDir : (0, import_node_path23.join)(aiRulesDir, "rules");
6921
+ const rulesDir = useLegacy ? aiRulesDir : (0, import_node_path29.join)(aiRulesDir, "rules");
5685
6922
  if (!useLegacy) {
5686
6923
  await ensureDir(rulesDir);
5687
6924
  }
@@ -5727,7 +6964,7 @@ globs: ["**/*"]
5727
6964
  - Follow single responsibility principle
5728
6965
  `
5729
6966
  };
5730
- const filepath = (0, import_node_path23.join)(rulesDir, sampleFile.filename);
6967
+ const filepath = (0, import_node_path29.join)(rulesDir, sampleFile.filename);
5731
6968
  if (!await fileExists(filepath)) {
5732
6969
  await writeFileContent(filepath, sampleFile.content);
5733
6970
  logger.success(`Created ${filepath}`);
@@ -5844,11 +7081,11 @@ async function watchCommand() {
5844
7081
  persistent: true
5845
7082
  });
5846
7083
  let isGenerating = false;
5847
- const handleChange = async (path5) => {
7084
+ const handleChange = async (path8) => {
5848
7085
  if (isGenerating) return;
5849
7086
  isGenerating = true;
5850
7087
  logger.log(`
5851
- \u{1F4DD} Detected change in ${path5}`);
7088
+ \u{1F4DD} Detected change in ${path8}`);
5852
7089
  try {
5853
7090
  await generateCommand({ verbose: false });
5854
7091
  logger.success("Regenerated configuration files");
@@ -5858,10 +7095,10 @@ async function watchCommand() {
5858
7095
  isGenerating = false;
5859
7096
  }
5860
7097
  };
5861
- watcher.on("change", handleChange).on("add", handleChange).on("unlink", (path5) => {
7098
+ watcher.on("change", handleChange).on("add", handleChange).on("unlink", (path8) => {
5862
7099
  logger.log(`
5863
- \u{1F5D1}\uFE0F Removed ${path5}`);
5864
- handleChange(path5);
7100
+ \u{1F5D1}\uFE0F Removed ${path8}`);
7101
+ handleChange(path8);
5865
7102
  }).on("error", (error) => {
5866
7103
  logger.error("Watcher error:", error);
5867
7104
  });
@@ -5872,47 +7109,180 @@ async function watchCommand() {
5872
7109
  });
5873
7110
  }
5874
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
+
5875
7205
  // src/cli/index.ts
5876
7206
  var program = new import_commander.Command();
5877
- program.name("rulesync").description("Unified AI rules management CLI tool").version("0.65.0");
7207
+ program.name("rulesync").description("Unified AI rules management CLI tool").version("0.67.0");
5878
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);
5879
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);
5880
7210
  program.command("gitignore").description("Add generated files to .gitignore").action(gitignoreCommand);
5881
- program.command("import").description("Import configurations from AI tools to rulesync format").option("--augmentcode", "Import from AugmentCode (.augment/rules/)").option("--augmentcode-legacy", "Import from AugmentCode legacy format (.augment-guidelines)").option("--claudecode", "Import from Claude Code (CLAUDE.md)").option("--cursor", "Import from Cursor (.cursorrules)").option("--copilot", "Import from GitHub Copilot (.github/copilot-instructions.md)").option("--cline", "Import from Cline (.cline/instructions.md)").option("--roo", "Import from Roo Code (.roo/instructions.md)").option("--geminicli", "Import from Gemini CLI (GEMINI.md)").option("--junie", "Import from JetBrains Junie (.junie/guidelines.md)").option("--qwencode", "Import from Qwen Code (QWEN.md)").option("--opencode", "Import from OpenCode (AGENTS.md)").option("-v, --verbose", "Verbose output").option("--legacy", "Use legacy file location (.rulesync/*.md instead of .rulesync/rules/*.md)").action(importCommand);
5882
- program.command("generate").description("Generate configuration files for AI tools").option("--all", "Generate for all supported AI tools").option("--augmentcode", "Generate only for AugmentCode").option("--augmentcode-legacy", "Generate only for AugmentCode legacy format").option("--copilot", "Generate only for GitHub Copilot").option("--cursor", "Generate only for Cursor").option("--cline", "Generate only for Cline").option("--codexcli", "Generate only for OpenAI Codex CLI").option("--claudecode", "Generate only for Claude Code").option("--roo", "Generate only for Roo Code").option("--geminicli", "Generate only for Gemini CLI").option("--junie", "Generate only for JetBrains Junie").option("--qwencode", "Generate only for Qwen Code").option("--kiro", "Generate only for Kiro IDE").option("--opencode", "Generate only for OpenCode").option("--windsurf", "Generate only for Windsurf").option("--delete", "Delete all existing files in output directories before generating").option(
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(
5883
7259
  "-b, --base-dir <paths>",
5884
7260
  "Base directories to generate files (comma-separated for multiple paths)"
5885
7261
  ).option("-v, --verbose", "Verbose output").option("-c, --config <path>", "Path to configuration file").option("--no-config", "Disable configuration file loading").action(async (options) => {
5886
- const tools = [];
5887
- if (options.all) {
5888
- tools.push(...ALL_TOOL_TARGETS);
5889
- } else {
5890
- if (options.augmentcode) tools.push("augmentcode");
5891
- if (options["augmentcode-legacy"]) tools.push("augmentcode-legacy");
5892
- if (options.copilot) tools.push("copilot");
5893
- if (options.cursor) tools.push("cursor");
5894
- if (options.cline) tools.push("cline");
5895
- if (options.codexcli) tools.push("codexcli");
5896
- if (options.claudecode) tools.push("claudecode");
5897
- if (options.roo) tools.push("roo");
5898
- if (options.geminicli) tools.push("geminicli");
5899
- if (options.junie) tools.push("junie");
5900
- if (options.qwencode) tools.push("qwencode");
5901
- if (options.kiro) tools.push("kiro");
5902
- if (options.opencode) tools.push("opencode");
5903
- if (options.windsurf) tools.push("windsurf");
5904
- }
5905
- const generateOptions = {
5906
- verbose: options.verbose,
5907
- tools: tools.length > 0 ? tools : void 0,
5908
- delete: options.delete,
5909
- config: options.config,
5910
- noConfig: options.noConfig
5911
- };
5912
- if (options.baseDir) {
5913
- 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);
5914
7285
  }
5915
- await generateCommand(generateOptions);
5916
7286
  });
5917
7287
  program.command("validate").description("Validate rulesync configuration").action(validateCommand);
5918
7288
  program.command("status").description("Show current status of rulesync").action(statusCommand);