rulesync 0.61.0 → 0.63.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.
package/dist/index.cjs CHANGED
@@ -35,11 +35,11 @@ function isToolTarget(target) {
35
35
  if (!target) return false;
36
36
  return ALL_TOOL_TARGETS.some((validTarget) => validTarget === target);
37
37
  }
38
- var import_mini, ALL_TOOL_TARGETS, ToolTargetSchema, ToolTargetsSchema, WildcardTargetSchema, RulesyncTargetsSchema;
38
+ var import_mini2, 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_mini = require("zod/mini");
42
+ import_mini2 = require("zod/mini");
43
43
  ALL_TOOL_TARGETS = [
44
44
  "augmentcode",
45
45
  "augmentcode-legacy",
@@ -54,10 +54,10 @@ var init_tool_targets = __esm({
54
54
  "junie",
55
55
  "windsurf"
56
56
  ];
57
- ToolTargetSchema = import_mini.z.enum(ALL_TOOL_TARGETS);
58
- ToolTargetsSchema = import_mini.z.array(ToolTargetSchema);
59
- WildcardTargetSchema = import_mini.z.tuple([import_mini.z.literal("*")]);
60
- RulesyncTargetsSchema = import_mini.z.union([ToolTargetsSchema, WildcardTargetSchema]);
57
+ ToolTargetSchema = import_mini2.z.enum(ALL_TOOL_TARGETS);
58
+ ToolTargetsSchema = import_mini2.z.array(ToolTargetSchema);
59
+ WildcardTargetSchema = import_mini2.z.tuple([import_mini2.z.literal("*")]);
60
+ RulesyncTargetsSchema = import_mini2.z.union([ToolTargetsSchema, WildcardTargetSchema]);
61
61
  }
62
62
  });
63
63
 
@@ -257,7 +257,8 @@ function generateMcpConfigurationFilesFromRegistry(tool, mcpServers, baseDir = "
257
257
  return generateMcpConfigurationFiles(mcpServers, generatorConfig, baseDir);
258
258
  }
259
259
  function generateJunieMcpConfigurationFiles(mcpServers, baseDir = "") {
260
- const filepath = baseDir ? `${baseDir}/.junie/mcp-config.json` : ".junie/mcp-config.json";
260
+ const junieMcpPath = ".junie/mcp/mcp.json";
261
+ const filepath = baseDir ? `${baseDir}/${junieMcpPath}` : junieMcpPath;
261
262
  const config = {
262
263
  mcpServers: {}
263
264
  };
@@ -1055,94 +1056,36 @@ var import_commander = require("commander");
1055
1056
  var import_promises = require("fs/promises");
1056
1057
  var path = __toESM(require("path"), 1);
1057
1058
 
1058
- // src/utils/config.ts
1059
- init_tool_targets();
1060
- function getDefaultConfig() {
1061
- return {
1062
- aiRulesDir: ".rulesync",
1063
- outputPaths: {
1064
- augmentcode: ".",
1065
- "augmentcode-legacy": ".",
1066
- copilot: ".github/instructions",
1067
- cursor: ".cursor/rules",
1068
- cline: ".clinerules",
1069
- claudecode: ".",
1070
- codexcli: ".",
1071
- roo: ".roo/rules",
1072
- geminicli: ".gemini/memories",
1073
- kiro: ".kiro/steering",
1074
- junie: ".",
1075
- windsurf: "."
1076
- },
1077
- watchEnabled: false,
1078
- defaultTargets: ALL_TOOL_TARGETS.filter((tool) => tool !== "augmentcode-legacy")
1079
- };
1080
- }
1081
- function resolveTargets(targets, config) {
1082
- if (targets.length === 1 && targets[0] === "*") {
1083
- return config.defaultTargets;
1084
- }
1085
- const validatedTargets = ToolTargetsSchema.parse(targets);
1086
- return validatedTargets;
1087
- }
1088
-
1089
- // src/cli/commands/add.ts
1090
- function sanitizeFilename(filename) {
1091
- return filename.endsWith(".md") ? filename.slice(0, -3) : filename;
1092
- }
1093
- function generateRuleTemplate(filename) {
1094
- return `---
1095
- root: false
1096
- targets: ["*"]
1097
- description: "Rules for ${filename}"
1098
- globs: []
1099
- ---
1100
-
1101
- # ${filename.charAt(0).toUpperCase() + filename.slice(1)} Rules
1102
-
1103
- Add your rules here.
1104
- `;
1105
- }
1106
- async function addCommand(filename) {
1107
- try {
1108
- const config = getDefaultConfig();
1109
- const sanitizedFilename = sanitizeFilename(filename);
1110
- const rulesDir = config.aiRulesDir;
1111
- const filePath = path.join(rulesDir, `${sanitizedFilename}.md`);
1112
- await (0, import_promises.mkdir)(rulesDir, { recursive: true });
1113
- const template = generateRuleTemplate(sanitizedFilename);
1114
- await (0, import_promises.writeFile)(filePath, template, "utf8");
1115
- console.log(`\u2705 Created rule file: ${filePath}`);
1116
- console.log(`\u{1F4DD} Edit the file to customize your rules.`);
1117
- } catch (error) {
1118
- console.error(
1119
- `\u274C Failed to create rule file: ${error instanceof Error ? error.message : String(error)}`
1120
- );
1121
- process.exit(3);
1122
- }
1123
- }
1124
-
1125
- // src/cli/commands/config.ts
1126
- var import_node_fs = require("fs");
1127
- var import_node_path2 = __toESM(require("path"), 1);
1059
+ // src/utils/config-loader.ts
1060
+ var import_c12 = require("c12");
1061
+ var import_core = require("zod/v4/core");
1128
1062
 
1129
1063
  // src/types/claudecode.ts
1130
- var import_mini2 = require("zod/mini");
1131
- var ClaudeSettingsSchema = import_mini2.z.looseObject({
1132
- permissions: import_mini2.z._default(
1133
- import_mini2.z.looseObject({
1134
- deny: import_mini2.z._default(import_mini2.z.array(import_mini2.z.string()), [])
1064
+ var import_mini = require("zod/mini");
1065
+ var ClaudeSettingsSchema = import_mini.z.looseObject({
1066
+ permissions: import_mini.z._default(
1067
+ import_mini.z.looseObject({
1068
+ deny: import_mini.z._default(import_mini.z.array(import_mini.z.string()), [])
1135
1069
  }),
1136
1070
  { deny: [] }
1137
1071
  )
1138
1072
  });
1139
1073
 
1140
- // src/types/commands.ts
1074
+ // src/types/shared.ts
1141
1075
  var import_mini3 = require("zod/mini");
1142
- var CommandFrontmatterSchema = import_mini3.z.object({
1076
+ init_tool_targets();
1077
+ var OutputSchema = import_mini3.z.object({
1078
+ tool: ToolTargetSchema,
1079
+ filepath: import_mini3.z.string(),
1080
+ content: import_mini3.z.string()
1081
+ });
1082
+ var BaseFrontmatterSchema = import_mini3.z.object({
1143
1083
  description: import_mini3.z.optional(import_mini3.z.string())
1144
1084
  });
1145
1085
 
1086
+ // src/types/commands.ts
1087
+ var CommandFrontmatterSchema = BaseFrontmatterSchema;
1088
+
1146
1089
  // src/types/config.ts
1147
1090
  var import_mini4 = require("zod/mini");
1148
1091
  init_tool_targets();
@@ -1152,7 +1095,8 @@ var ConfigSchema = import_mini4.z.object({
1152
1095
  watchEnabled: import_mini4.z.boolean(),
1153
1096
  defaultTargets: ToolTargetsSchema,
1154
1097
  claudecodeCommands: import_mini4.z.optional(import_mini4.z.string()),
1155
- geminicliCommands: import_mini4.z.optional(import_mini4.z.string())
1098
+ geminicliCommands: import_mini4.z.optional(import_mini4.z.string()),
1099
+ legacy: import_mini4.z.optional(import_mini4.z.boolean())
1156
1100
  });
1157
1101
 
1158
1102
  // src/types/config-options.ts
@@ -1182,6 +1126,7 @@ var ConfigOptionsSchema = import_mini5.z.object({
1182
1126
  verbose: import_mini5.z.optional(import_mini5.z.boolean()),
1183
1127
  delete: import_mini5.z.optional(import_mini5.z.boolean()),
1184
1128
  baseDir: import_mini5.z.optional(import_mini5.z.union([import_mini5.z.string(), import_mini5.z.array(import_mini5.z.string())])),
1129
+ legacy: import_mini5.z.optional(import_mini5.z.boolean()),
1185
1130
  watch: import_mini5.z.optional(
1186
1131
  import_mini5.z.object({
1187
1132
  enabled: import_mini5.z.optional(import_mini5.z.boolean()),
@@ -1201,6 +1146,7 @@ var MergedConfigSchema = import_mini5.z.object({
1201
1146
  delete: import_mini5.z.optional(import_mini5.z.boolean()),
1202
1147
  baseDir: import_mini5.z.optional(import_mini5.z.union([import_mini5.z.string(), import_mini5.z.array(import_mini5.z.string())])),
1203
1148
  configPath: import_mini5.z.optional(import_mini5.z.string()),
1149
+ legacy: import_mini5.z.optional(import_mini5.z.boolean()),
1204
1150
  watch: import_mini5.z.optional(
1205
1151
  import_mini5.z.object({
1206
1152
  enabled: import_mini5.z.optional(import_mini5.z.boolean()),
@@ -1256,11 +1202,6 @@ var RuleFrontmatterSchema = import_mini7.z.object({
1256
1202
  windsurfOutputFormat: import_mini7.z.optional(import_mini7.z.enum(["single-file", "directory"])),
1257
1203
  tags: import_mini7.z.optional(import_mini7.z.array(import_mini7.z.string()))
1258
1204
  });
1259
- var GeneratedOutputSchema = import_mini7.z.object({
1260
- tool: ToolTargetSchema,
1261
- filepath: import_mini7.z.string(),
1262
- content: import_mini7.z.string()
1263
- });
1264
1205
  var GenerateOptionsSchema = import_mini7.z.object({
1265
1206
  targetTools: import_mini7.z.optional(ToolTargetsSchema),
1266
1207
  outputDir: import_mini7.z.optional(import_mini7.z.string()),
@@ -1270,9 +1211,39 @@ var GenerateOptionsSchema = import_mini7.z.object({
1270
1211
  // src/types/index.ts
1271
1212
  init_tool_targets();
1272
1213
 
1214
+ // src/utils/config.ts
1215
+ init_tool_targets();
1216
+ function getDefaultConfig() {
1217
+ return {
1218
+ aiRulesDir: ".rulesync",
1219
+ outputPaths: {
1220
+ augmentcode: ".",
1221
+ "augmentcode-legacy": ".",
1222
+ copilot: ".github/instructions",
1223
+ cursor: ".cursor/rules",
1224
+ cline: ".clinerules",
1225
+ claudecode: ".",
1226
+ codexcli: ".",
1227
+ roo: ".roo/rules",
1228
+ geminicli: ".gemini/memories",
1229
+ kiro: ".kiro/steering",
1230
+ junie: ".",
1231
+ windsurf: "."
1232
+ },
1233
+ watchEnabled: false,
1234
+ defaultTargets: ALL_TOOL_TARGETS.filter((tool) => tool !== "augmentcode-legacy"),
1235
+ legacy: false
1236
+ };
1237
+ }
1238
+ function resolveTargets(targets, config) {
1239
+ if (targets.length === 1 && targets[0] === "*") {
1240
+ return config.defaultTargets;
1241
+ }
1242
+ const validatedTargets = ToolTargetsSchema.parse(targets);
1243
+ return validatedTargets;
1244
+ }
1245
+
1273
1246
  // src/utils/config-loader.ts
1274
- var import_c12 = require("c12");
1275
- var import_core = require("zod/v4/core");
1276
1247
  var MODULE_NAME = "rulesync";
1277
1248
  async function loadConfig(options = {}) {
1278
1249
  const defaultConfig = getDefaultConfig();
@@ -1459,6 +1430,93 @@ function mergeWithCliOptions(config, cliOptions) {
1459
1430
  return merged;
1460
1431
  }
1461
1432
 
1433
+ // src/utils/logger.ts
1434
+ var import_consola = require("consola");
1435
+ var Logger = class {
1436
+ _verbose = false;
1437
+ console = import_consola.consola.withDefaults({
1438
+ tag: "rulesync"
1439
+ });
1440
+ setVerbose(verbose) {
1441
+ this._verbose = verbose;
1442
+ }
1443
+ get verbose() {
1444
+ return this._verbose;
1445
+ }
1446
+ // Regular log (always shown, regardless of verbose)
1447
+ log(message, ...args) {
1448
+ this.console.log(message, ...args);
1449
+ }
1450
+ // Info level (shown only in verbose mode)
1451
+ info(message, ...args) {
1452
+ if (this._verbose) {
1453
+ this.console.info(message, ...args);
1454
+ }
1455
+ }
1456
+ // Success (always shown)
1457
+ success(message, ...args) {
1458
+ this.console.success(message, ...args);
1459
+ }
1460
+ // Warning (always shown)
1461
+ warn(message, ...args) {
1462
+ this.console.warn(message, ...args);
1463
+ }
1464
+ // Error (always shown)
1465
+ error(message, ...args) {
1466
+ this.console.error(message, ...args);
1467
+ }
1468
+ // Debug level (shown only in verbose mode)
1469
+ debug(message, ...args) {
1470
+ if (this._verbose) {
1471
+ this.console.debug(message, ...args);
1472
+ }
1473
+ }
1474
+ };
1475
+ var logger = new Logger();
1476
+
1477
+ // src/cli/commands/add.ts
1478
+ function sanitizeFilename(filename) {
1479
+ return filename.endsWith(".md") ? filename.slice(0, -3) : filename;
1480
+ }
1481
+ function generateRuleTemplate(filename) {
1482
+ return `---
1483
+ root: false
1484
+ targets: ["*"]
1485
+ description: "Rules for ${filename}"
1486
+ globs: []
1487
+ ---
1488
+
1489
+ # ${filename.charAt(0).toUpperCase() + filename.slice(1)} Rules
1490
+
1491
+ Add your rules here.
1492
+ `;
1493
+ }
1494
+ async function addCommand(filename, options = {}) {
1495
+ try {
1496
+ const configResult = await loadConfig();
1497
+ const config = configResult.config;
1498
+ const sanitizedFilename = sanitizeFilename(filename);
1499
+ const aiRulesDir = config.aiRulesDir;
1500
+ const useLegacy = options.legacy ?? config.legacy ?? false;
1501
+ const rulesDir = useLegacy ? aiRulesDir : path.join(aiRulesDir, "rules");
1502
+ const filePath = path.join(rulesDir, `${sanitizedFilename}.md`);
1503
+ await (0, import_promises.mkdir)(rulesDir, { recursive: true });
1504
+ const template = generateRuleTemplate(sanitizedFilename);
1505
+ await (0, import_promises.writeFile)(filePath, template, "utf8");
1506
+ logger.success(`Created rule file: ${filePath}`);
1507
+ logger.log(`\u{1F4DD} Edit the file to customize your rules.`);
1508
+ } catch (error) {
1509
+ logger.error(
1510
+ `Failed to create rule file: ${error instanceof Error ? error.message : String(error)}`
1511
+ );
1512
+ process.exit(3);
1513
+ }
1514
+ }
1515
+
1516
+ // src/cli/commands/config.ts
1517
+ var import_node_fs = require("fs");
1518
+ var import_node_path2 = __toESM(require("path"), 1);
1519
+
1462
1520
  // src/utils/error.ts
1463
1521
  function getErrorMessage(error) {
1464
1522
  return error instanceof Error ? error.message : String(error);
@@ -1525,10 +1583,23 @@ async function findFiles(dir, extension = ".md") {
1525
1583
  return [];
1526
1584
  }
1527
1585
  }
1586
+ async function findRuleFiles(aiRulesDir) {
1587
+ const rulesDir = (0, import_node_path.join)(aiRulesDir, "rules");
1588
+ const newLocationFiles = await findFiles(rulesDir, ".md");
1589
+ const legacyLocationFiles = await findFiles(aiRulesDir, ".md");
1590
+ const newLocationBasenames = new Set(
1591
+ newLocationFiles.map((file) => file.split("/").pop()?.replace(/\.md$/, ""))
1592
+ );
1593
+ const filteredLegacyFiles = legacyLocationFiles.filter((file) => {
1594
+ const basename6 = file.split("/").pop()?.replace(/\.md$/, "");
1595
+ return !newLocationBasenames.has(basename6);
1596
+ });
1597
+ return [...newLocationFiles, ...filteredLegacyFiles];
1598
+ }
1528
1599
  async function removeDirectory(dirPath) {
1529
1600
  const dangerousPaths = [".", "/", "~", "src", "node_modules"];
1530
1601
  if (dangerousPaths.includes(dirPath) || dirPath === "") {
1531
- console.warn(`Skipping deletion of dangerous path: ${dirPath}`);
1602
+ logger.warn(`Skipping deletion of dangerous path: ${dirPath}`);
1532
1603
  return;
1533
1604
  }
1534
1605
  try {
@@ -1536,7 +1607,7 @@ async function removeDirectory(dirPath) {
1536
1607
  await (0, import_promises2.rm)(dirPath, { recursive: true, force: true });
1537
1608
  }
1538
1609
  } catch (error) {
1539
- console.warn(`Failed to remove directory ${dirPath}:`, error);
1610
+ logger.warn(`Failed to remove directory ${dirPath}:`, error);
1540
1611
  }
1541
1612
  }
1542
1613
  async function removeFile(filepath) {
@@ -1545,7 +1616,7 @@ async function removeFile(filepath) {
1545
1616
  await (0, import_promises2.rm)(filepath);
1546
1617
  }
1547
1618
  } catch (error) {
1548
- console.warn(`Failed to remove file ${filepath}:`, error);
1619
+ logger.warn(`Failed to remove file ${filepath}:`, error);
1549
1620
  }
1550
1621
  }
1551
1622
  async function removeClaudeGeneratedFiles() {
@@ -1568,50 +1639,50 @@ async function configCommand(options = {}) {
1568
1639
  await showConfig();
1569
1640
  }
1570
1641
  async function showConfig() {
1571
- console.log("Loading configuration...\n");
1642
+ logger.log("Loading configuration...\n");
1572
1643
  try {
1573
1644
  const result = await loadConfig();
1574
1645
  if (result.isEmpty) {
1575
- console.log("No configuration file found. Using default configuration.\n");
1646
+ logger.log("No configuration file found. Using default configuration.\n");
1576
1647
  } else {
1577
- console.log(`Configuration loaded from: ${result.filepath}
1648
+ logger.log(`Configuration loaded from: ${result.filepath}
1578
1649
  `);
1579
1650
  }
1580
- console.log("Current configuration:");
1581
- console.log("=====================");
1651
+ logger.log("Current configuration:");
1652
+ logger.log("=====================");
1582
1653
  const config = result.config;
1583
- console.log(`
1654
+ logger.log(`
1584
1655
  AI Rules Directory: ${config.aiRulesDir}`);
1585
- console.log(`
1656
+ logger.log(`
1586
1657
  Default Targets: ${config.defaultTargets.join(", ")}`);
1587
1658
  if (config.exclude && config.exclude.length > 0) {
1588
- console.log(`Excluded Targets: ${config.exclude.join(", ")}`);
1659
+ logger.log(`Excluded Targets: ${config.exclude.join(", ")}`);
1589
1660
  }
1590
- console.log("\nOutput Paths:");
1661
+ logger.log("\nOutput Paths:");
1591
1662
  for (const [tool, outputPath] of Object.entries(config.outputPaths)) {
1592
- console.log(` ${tool}: ${outputPath}`);
1663
+ logger.log(` ${tool}: ${outputPath}`);
1593
1664
  }
1594
1665
  if (config.baseDir) {
1595
1666
  const dirs = Array.isArray(config.baseDir) ? config.baseDir : [config.baseDir];
1596
- console.log(`
1667
+ logger.log(`
1597
1668
  Base Directories: ${dirs.join(", ")}`);
1598
1669
  }
1599
- console.log(`
1670
+ logger.log(`
1600
1671
  Verbose: ${config.verbose || false}`);
1601
- console.log(`Delete before generate: ${config.delete || false}`);
1672
+ logger.log(`Delete before generate: ${config.delete || false}`);
1602
1673
  if (config.watch) {
1603
- console.log("\nWatch Configuration:");
1604
- console.log(` Enabled: ${config.watch.enabled || false}`);
1674
+ logger.log("\nWatch Configuration:");
1675
+ logger.log(` Enabled: ${config.watch.enabled || false}`);
1605
1676
  if (config.watch.interval) {
1606
- console.log(` Interval: ${config.watch.interval}ms`);
1677
+ logger.log(` Interval: ${config.watch.interval}ms`);
1607
1678
  }
1608
1679
  if (config.watch.ignore && config.watch.ignore.length > 0) {
1609
- console.log(` Ignore patterns: ${config.watch.ignore.join(", ")}`);
1680
+ logger.log(` Ignore patterns: ${config.watch.ignore.join(", ")}`);
1610
1681
  }
1611
1682
  }
1612
- console.log("\nTip: Use 'rulesync config init' to create a configuration file.");
1683
+ logger.log("\nTip: Use 'rulesync config init' to create a configuration file.");
1613
1684
  } catch (error) {
1614
- console.error(
1685
+ logger.error(
1615
1686
  "\u274C Failed to load configuration:",
1616
1687
  error instanceof Error ? error.message : String(error)
1617
1688
  );
@@ -1632,7 +1703,7 @@ async function initConfig(options) {
1632
1703
  const validFormats = Object.keys(FORMAT_CONFIG);
1633
1704
  const selectedFormat = options.format || "jsonc";
1634
1705
  if (!validFormats.includes(selectedFormat)) {
1635
- console.error(
1706
+ logger.error(
1636
1707
  `\u274C Invalid format: ${selectedFormat}. Valid formats are: ${validFormats.join(", ")}`
1637
1708
  );
1638
1709
  process.exit(1);
@@ -1648,7 +1719,7 @@ async function initConfig(options) {
1648
1719
  if (result.success) {
1649
1720
  validTargets.push(result.data);
1650
1721
  } else {
1651
- console.error(`\u274C Invalid target: ${target}`);
1722
+ logger.error(`\u274C Invalid target: ${target}`);
1652
1723
  process.exit(1);
1653
1724
  }
1654
1725
  }
@@ -1662,7 +1733,7 @@ async function initConfig(options) {
1662
1733
  if (result.success) {
1663
1734
  validExcludes.push(result.data);
1664
1735
  } else {
1665
- console.error(`\u274C Invalid exclude target: ${exclude}`);
1736
+ logger.error(`\u274C Invalid exclude target: ${exclude}`);
1666
1737
  process.exit(1);
1667
1738
  }
1668
1739
  }
@@ -1685,18 +1756,18 @@ async function initConfig(options) {
1685
1756
  try {
1686
1757
  const fs2 = await import("fs/promises");
1687
1758
  await fs2.access(filepath);
1688
- console.error(`\u274C Configuration file already exists: ${filepath}`);
1689
- console.log("Remove the existing file or choose a different format.");
1759
+ logger.error(`\u274C Configuration file already exists: ${filepath}`);
1760
+ logger.log("Remove the existing file or choose a different format.");
1690
1761
  process.exit(1);
1691
1762
  } catch {
1692
1763
  }
1693
1764
  try {
1694
1765
  (0, import_node_fs.writeFileSync)(filepath, content, "utf-8");
1695
- console.log(`\u2705 Created configuration file: ${filepath}`);
1696
- console.log("\nYou can now customize the configuration to fit your needs.");
1697
- console.log("Run 'rulesync generate' to use the new configuration.");
1766
+ logger.success(`Created configuration file: ${filepath}`);
1767
+ logger.log("\nYou can now customize the configuration to fit your needs.");
1768
+ logger.log("Run 'rulesync generate' to use the new configuration.");
1698
1769
  } catch (error) {
1699
- console.error(
1770
+ logger.error(
1700
1771
  `\u274C Failed to create configuration file: ${error instanceof Error ? error.message : String(error)}`
1701
1772
  );
1702
1773
  process.exit(1);
@@ -1770,25 +1841,68 @@ export default config;
1770
1841
  }
1771
1842
 
1772
1843
  // src/cli/commands/generate.ts
1773
- var import_node_path15 = require("path");
1844
+ var import_node_path13 = require("path");
1774
1845
 
1775
1846
  // src/core/command-generator.ts
1776
- var import_node_path7 = require("path");
1847
+ var import_node_path5 = require("path");
1777
1848
 
1778
- // src/generators/commands/claudecode.ts
1849
+ // src/utils/command-generators.ts
1779
1850
  var import_node_path3 = require("path");
1780
- var ClaudeCodeCommandGenerator = class {
1781
- generate(command, outputDir) {
1782
- const filepath = this.getOutputPath(command.filename, outputDir);
1783
- const frontmatter = ["---"];
1784
- if (command.frontmatter.description) {
1785
- frontmatter.push(`description: ${command.frontmatter.description}`);
1851
+ function generateYamlFrontmatter(command, options = {}) {
1852
+ const frontmatter = ["---"];
1853
+ if (options.includeDescription !== false && command.frontmatter.description) {
1854
+ frontmatter.push(`description: ${command.frontmatter.description}`);
1855
+ }
1856
+ if (options.additionalFields) {
1857
+ for (const field of options.additionalFields) {
1858
+ frontmatter.push(`${field.key}: ${field.value}`);
1786
1859
  }
1787
- frontmatter.push("---");
1788
- const content = `${frontmatter.join("\n")}
1860
+ }
1861
+ frontmatter.push("---");
1862
+ return frontmatter;
1863
+ }
1864
+ function buildCommandContent(command, frontmatterOptions) {
1865
+ const frontmatter = generateYamlFrontmatter(command, frontmatterOptions);
1866
+ return `${frontmatter.join("\n")}
1789
1867
 
1790
1868
  ${command.content.trim()}
1791
1869
  `;
1870
+ }
1871
+ function getFlattenedCommandPath(filename, baseDir, subdir) {
1872
+ const flattenedName = filename.replace(/\//g, "-");
1873
+ return (0, import_node_path3.join)(baseDir, subdir, `${flattenedName}.md`);
1874
+ }
1875
+ function getHierarchicalCommandPath(filename, baseDir, subdir, extension = "md") {
1876
+ const nameWithoutExt = filename.replace(/\.[^/.]+$/, "");
1877
+ const fileWithExt = `${nameWithoutExt}.${extension}`;
1878
+ return (0, import_node_path3.join)(baseDir, subdir, fileWithExt);
1879
+ }
1880
+ function escapeTomlString(str) {
1881
+ return str.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/\t/g, "\\t");
1882
+ }
1883
+ var syntaxConverters = {
1884
+ /**
1885
+ * Convert Claude Code syntax to Gemini CLI syntax
1886
+ */
1887
+ toGeminiCli(content) {
1888
+ let converted = content;
1889
+ converted = converted.replace(/\$ARGUMENTS/g, "{{args}}");
1890
+ converted = converted.replace(/!`([^`]+)`/g, "!{$1}");
1891
+ return converted.trim();
1892
+ },
1893
+ /**
1894
+ * Convert to Roo Code syntax (currently identical to Claude Code)
1895
+ */
1896
+ toRooCode(content) {
1897
+ return content.trim();
1898
+ }
1899
+ };
1900
+
1901
+ // src/generators/commands/claudecode.ts
1902
+ var ClaudeCodeCommandGenerator = class {
1903
+ generate(command, outputDir) {
1904
+ const filepath = this.getOutputPath(command.filename, outputDir);
1905
+ const content = buildCommandContent(command);
1792
1906
  return {
1793
1907
  tool: "claudecode",
1794
1908
  filepath,
@@ -1796,20 +1910,18 @@ ${command.content.trim()}
1796
1910
  };
1797
1911
  }
1798
1912
  getOutputPath(filename, baseDir) {
1799
- const flattenedName = filename.replace(/\//g, "-");
1800
- return (0, import_node_path3.join)(baseDir, ".claude", "commands", `${flattenedName}.md`);
1913
+ return getFlattenedCommandPath(filename, baseDir, ".claude/commands");
1801
1914
  }
1802
1915
  };
1803
1916
 
1804
1917
  // src/generators/commands/geminicli.ts
1805
- var import_node_path4 = require("path");
1806
1918
  var GeminiCliCommandGenerator = class {
1807
1919
  generate(command, outputDir) {
1808
1920
  const filepath = this.getOutputPath(command.filename, outputDir);
1809
- const convertedContent = this.convertSyntax(command.content);
1921
+ const convertedContent = syntaxConverters.toGeminiCli(command.content);
1810
1922
  const tomlLines = [];
1811
1923
  if (command.frontmatter.description) {
1812
- tomlLines.push(`description = "${this.escapeTomlString(command.frontmatter.description)}"`);
1924
+ tomlLines.push(`description = "${escapeTomlString(command.frontmatter.description)}"`);
1813
1925
  tomlLines.push("");
1814
1926
  }
1815
1927
  tomlLines.push(`prompt = """${convertedContent}"""`);
@@ -1821,41 +1933,15 @@ var GeminiCliCommandGenerator = class {
1821
1933
  };
1822
1934
  }
1823
1935
  getOutputPath(filename, baseDir) {
1824
- const tomlFilename = filename.replace(/\.md$/, ".toml");
1825
- const filenameWithExt = tomlFilename.endsWith(".toml") ? tomlFilename : `${tomlFilename}.toml`;
1826
- return (0, import_node_path4.join)(baseDir, ".gemini", "commands", filenameWithExt);
1827
- }
1828
- convertSyntax(content) {
1829
- let converted = content;
1830
- converted = converted.replace(/\$ARGUMENTS/g, "{{args}}");
1831
- converted = converted.replace(/!`([^`]+)`/g, "!{$1}");
1832
- const atSyntaxMatches = converted.match(/@[^\s]+/g);
1833
- if (atSyntaxMatches) {
1834
- console.warn(
1835
- `\u26A0\uFE0F Warning: @ syntax found (${atSyntaxMatches.join(", ")}). Gemini CLI does not support file content injection. Consider using shell commands or remove these references.`
1836
- );
1837
- }
1838
- return converted.trim();
1839
- }
1840
- escapeTomlString(str) {
1841
- return str.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/\t/g, "\\t");
1936
+ return getHierarchicalCommandPath(filename, baseDir, ".gemini/commands", "toml");
1842
1937
  }
1843
1938
  };
1844
1939
 
1845
1940
  // src/generators/commands/roo.ts
1846
- var import_node_path5 = require("path");
1847
1941
  var RooCommandGenerator = class {
1848
1942
  generate(command, outputDir) {
1849
1943
  const filepath = this.getOutputPath(command.filename, outputDir);
1850
- const frontmatter = ["---"];
1851
- if (command.frontmatter.description) {
1852
- frontmatter.push(`description: ${command.frontmatter.description}`);
1853
- }
1854
- frontmatter.push("---");
1855
- const content = `${frontmatter.join("\n")}
1856
-
1857
- ${command.content.trim()}
1858
- `;
1944
+ const content = buildCommandContent(command);
1859
1945
  return {
1860
1946
  tool: "roo",
1861
1947
  filepath,
@@ -1863,8 +1949,7 @@ ${command.content.trim()}
1863
1949
  };
1864
1950
  }
1865
1951
  getOutputPath(filename, baseDir) {
1866
- const flattenedName = filename.replace(/\//g, "-");
1867
- return (0, import_node_path5.join)(baseDir, ".roo", "commands", `${flattenedName}.md`);
1952
+ return getFlattenedCommandPath(filename, baseDir, ".roo/commands");
1868
1953
  }
1869
1954
  };
1870
1955
 
@@ -1879,8 +1964,27 @@ function getCommandGenerator(tool) {
1879
1964
  }
1880
1965
 
1881
1966
  // src/core/command-parser.ts
1882
- var import_node_path6 = require("path");
1967
+ var import_node_path4 = require("path");
1968
+
1969
+ // src/utils/frontmatter.ts
1883
1970
  var import_gray_matter = __toESM(require("gray-matter"), 1);
1971
+ function parseFrontmatter(content, options) {
1972
+ const parsed = (0, import_gray_matter.default)(content, options?.matterOptions);
1973
+ return {
1974
+ content: parsed.content.trim(),
1975
+ data: parsed.data || {}
1976
+ };
1977
+ }
1978
+ function extractArrayField(data, key, defaultValue = []) {
1979
+ const value = data[key];
1980
+ return Array.isArray(value) ? value : defaultValue;
1981
+ }
1982
+ function extractStringField(data, key, defaultValue) {
1983
+ const value = data[key];
1984
+ return typeof value === "string" ? value : defaultValue;
1985
+ }
1986
+
1987
+ // src/core/command-parser.ts
1884
1988
  async function parseCommandsFromDirectory(commandsDir) {
1885
1989
  const commandFiles = await findFiles(commandsDir, ".md");
1886
1990
  const commands = [];
@@ -1895,17 +1999,17 @@ async function parseCommandsFromDirectory(commandsDir) {
1895
1999
  }
1896
2000
  }
1897
2001
  if (errors.length > 0) {
1898
- console.warn(`\u26A0\uFE0F Command parsing errors:
2002
+ logger.warn(`Command parsing errors:
1899
2003
  ${errors.join("\n")}`);
1900
2004
  }
1901
2005
  return commands;
1902
2006
  }
1903
2007
  async function parseCommandFile(filepath) {
1904
2008
  const content = await readFileContent(filepath);
1905
- const parsed = (0, import_gray_matter.default)(content);
2009
+ const parsed = parseFrontmatter(content);
1906
2010
  try {
1907
2011
  const validatedData = CommandFrontmatterSchema.parse(parsed.data);
1908
- const filename = (0, import_node_path6.basename)(filepath, ".md");
2012
+ const filename = (0, import_node_path4.basename)(filepath, ".md");
1909
2013
  return {
1910
2014
  frontmatter: {
1911
2015
  description: validatedData.description
@@ -1923,7 +2027,7 @@ async function parseCommandFile(filepath) {
1923
2027
 
1924
2028
  // src/core/command-generator.ts
1925
2029
  async function generateCommands(projectRoot, baseDir, targets) {
1926
- const commandsDir = (0, import_node_path7.join)(projectRoot, ".rulesync", "commands");
2030
+ const commandsDir = (0, import_node_path5.join)(projectRoot, ".rulesync", "commands");
1927
2031
  if (!await fileExists(commandsDir)) {
1928
2032
  return [];
1929
2033
  }
@@ -1947,8 +2051,8 @@ async function generateCommands(projectRoot, baseDir, targets) {
1947
2051
  outputs.push(output);
1948
2052
  } catch (error) {
1949
2053
  const errorMessage = error instanceof Error ? error.message : String(error);
1950
- console.error(
1951
- `\u274C Failed to generate ${target} command for ${command.filename}: ${errorMessage}`
2054
+ logger.error(
2055
+ `Failed to generate ${target} command for ${command.filename}: ${errorMessage}`
1952
2056
  );
1953
2057
  }
1954
2058
  }
@@ -1957,7 +2061,7 @@ async function generateCommands(projectRoot, baseDir, targets) {
1957
2061
  }
1958
2062
 
1959
2063
  // src/generators/ignore/shared-factory.ts
1960
- var import_node_path8 = require("path");
2064
+ var import_node_path6 = require("path");
1961
2065
 
1962
2066
  // src/generators/ignore/shared-helpers.ts
1963
2067
  function extractIgnorePatternsFromRules(rules) {
@@ -2080,7 +2184,7 @@ function generateIgnoreFile(rules, config, ignoreConfig, baseDir) {
2080
2184
  const outputs = [];
2081
2185
  const content = generateIgnoreContent(rules, ignoreConfig);
2082
2186
  const outputPath = baseDir || process.cwd();
2083
- const filepath = (0, import_node_path8.join)(outputPath, ignoreConfig.filename);
2187
+ const filepath = (0, import_node_path6.join)(outputPath, ignoreConfig.filename);
2084
2188
  outputs.push({
2085
2189
  tool: ignoreConfig.tool,
2086
2190
  filepath,
@@ -2668,20 +2772,20 @@ function generateWindsurfIgnore(rules, config, baseDir) {
2668
2772
  }
2669
2773
 
2670
2774
  // src/generators/rules/augmentcode.ts
2671
- var import_node_path11 = require("path");
2775
+ var import_node_path9 = require("path");
2672
2776
 
2673
2777
  // src/generators/rules/shared-helpers.ts
2674
- var import_node_path10 = require("path");
2778
+ var import_node_path8 = require("path");
2675
2779
 
2676
2780
  // src/utils/ignore.ts
2677
- var import_node_path9 = require("path");
2781
+ var import_node_path7 = require("path");
2678
2782
  var import_micromatch = __toESM(require("micromatch"), 1);
2679
2783
  var cachedIgnorePatterns = null;
2680
2784
  async function loadIgnorePatterns(baseDir = process.cwd()) {
2681
2785
  if (cachedIgnorePatterns) {
2682
2786
  return cachedIgnorePatterns;
2683
2787
  }
2684
- const ignorePath = (0, import_node_path9.join)(baseDir, ".rulesyncignore");
2788
+ const ignorePath = (0, import_node_path7.join)(baseDir, ".rulesyncignore");
2685
2789
  if (!await fileExists(ignorePath)) {
2686
2790
  cachedIgnorePatterns = { patterns: [] };
2687
2791
  return cachedIgnorePatterns;
@@ -2692,7 +2796,7 @@ async function loadIgnorePatterns(baseDir = process.cwd()) {
2692
2796
  cachedIgnorePatterns = { patterns };
2693
2797
  return cachedIgnorePatterns;
2694
2798
  } catch (error) {
2695
- console.warn(`Failed to read .rulesyncignore: ${error}`);
2799
+ logger.warn(`Failed to read .rulesyncignore: ${error}`);
2696
2800
  cachedIgnorePatterns = { patterns: [] };
2697
2801
  return cachedIgnorePatterns;
2698
2802
  }
@@ -2735,7 +2839,7 @@ function addOutput(outputs, tool, config, baseDir, relativePath, content) {
2735
2839
  const outputDir = resolveOutputDir(config, tool, baseDir);
2736
2840
  outputs.push({
2737
2841
  tool,
2738
- filepath: (0, import_node_path10.join)(outputDir, relativePath),
2842
+ filepath: (0, import_node_path8.join)(outputDir, relativePath),
2739
2843
  content
2740
2844
  });
2741
2845
  }
@@ -2744,7 +2848,7 @@ async function generateRulesConfig(rules, config, generatorConfig, baseDir) {
2744
2848
  for (const rule of rules) {
2745
2849
  const content = generatorConfig.generateContent(rule);
2746
2850
  const outputDir = resolveOutputDir(config, generatorConfig.tool, baseDir);
2747
- const filepath = generatorConfig.pathResolver ? generatorConfig.pathResolver(rule, outputDir) : (0, import_node_path10.join)(outputDir, `${rule.filename}${generatorConfig.fileExtension}`);
2851
+ const filepath = generatorConfig.pathResolver ? generatorConfig.pathResolver(rule, outputDir) : (0, import_node_path8.join)(outputDir, `${rule.filename}${generatorConfig.fileExtension}`);
2748
2852
  outputs.push({
2749
2853
  tool: generatorConfig.tool,
2750
2854
  filepath,
@@ -2772,7 +2876,7 @@ async function generateComplexRules(rules, config, generatorConfig, baseDir) {
2772
2876
  for (const rule of detailRules) {
2773
2877
  const content = generatorConfig.generateDetailContent(rule);
2774
2878
  const filepath = resolvePath(
2775
- (0, import_node_path10.join)(generatorConfig.detailSubDir, `${rule.filename}.md`),
2879
+ (0, import_node_path8.join)(generatorConfig.detailSubDir, `${rule.filename}.md`),
2776
2880
  baseDir
2777
2881
  );
2778
2882
  outputs.push({
@@ -2835,7 +2939,7 @@ async function generateAugmentcodeConfig(rules, config, baseDir) {
2835
2939
  "augmentcode",
2836
2940
  config,
2837
2941
  baseDir,
2838
- (0, import_node_path11.join)(".augment", "rules", `${rule.filename}.md`),
2942
+ (0, import_node_path9.join)(".augment", "rules", `${rule.filename}.md`),
2839
2943
  generateRuleFile(rule)
2840
2944
  );
2841
2945
  });
@@ -2888,7 +2992,7 @@ function generateLegacyGuidelinesFile(allRules) {
2888
2992
  }
2889
2993
 
2890
2994
  // src/generators/rules/claudecode.ts
2891
- var import_node_path12 = require("path");
2995
+ var import_node_path10 = require("path");
2892
2996
  async function generateClaudecodeConfig(rules, config, baseDir) {
2893
2997
  const generatorConfig = {
2894
2998
  tool: "claudecode",
@@ -2900,7 +3004,7 @@ async function generateClaudecodeConfig(rules, config, baseDir) {
2900
3004
  generateDetailContent: generateMemoryFile,
2901
3005
  detailSubDir: ".claude/memories",
2902
3006
  updateAdditionalConfig: async (ignorePatterns, baseDir2) => {
2903
- const settingsPath = resolvePath((0, import_node_path12.join)(".claude", "settings.json"), baseDir2);
3007
+ const settingsPath = resolvePath((0, import_node_path10.join)(".claude", "settings.json"), baseDir2);
2904
3008
  await updateClaudeSettings(settingsPath, ignorePatterns);
2905
3009
  return [];
2906
3010
  }
@@ -2937,7 +3041,7 @@ async function updateClaudeSettings(settingsPath, ignorePatterns) {
2937
3041
  const content = await readFileContent(settingsPath);
2938
3042
  rawSettings = JSON.parse(content);
2939
3043
  } catch {
2940
- console.warn(`Failed to parse existing ${settingsPath}, creating new settings`);
3044
+ logger.warn(`Failed to parse existing ${settingsPath}, creating new settings`);
2941
3045
  rawSettings = {};
2942
3046
  }
2943
3047
  }
@@ -2960,11 +3064,11 @@ async function updateClaudeSettings(settingsPath, ignorePatterns) {
2960
3064
  settings.permissions.deny = Array.from(new Set(filteredDeny));
2961
3065
  const jsonContent = JSON.stringify(settings, null, 2);
2962
3066
  await writeFileContent(settingsPath, jsonContent);
2963
- console.log(`\u2705 Updated Claude Code settings: ${settingsPath}`);
3067
+ logger.success(`Updated Claude Code settings: ${settingsPath}`);
2964
3068
  }
2965
3069
 
2966
3070
  // src/generators/rules/generator-registry.ts
2967
- var import_node_path13 = require("path");
3071
+ var import_node_path11 = require("path");
2968
3072
  function determineCursorRuleType(frontmatter) {
2969
3073
  if (frontmatter.cursorRuleType) {
2970
3074
  return frontmatter.cursorRuleType;
@@ -3044,7 +3148,7 @@ var GENERATOR_REGISTRY = {
3044
3148
  },
3045
3149
  pathResolver: (rule, outputDir) => {
3046
3150
  const baseFilename = rule.filename.replace(/\.md$/, "");
3047
- return (0, import_node_path13.join)(outputDir, `${baseFilename}.instructions.md`);
3151
+ return (0, import_node_path11.join)(outputDir, `${baseFilename}.instructions.md`);
3048
3152
  }
3049
3153
  },
3050
3154
  cursor: {
@@ -3084,7 +3188,7 @@ var GENERATOR_REGISTRY = {
3084
3188
  return lines.join("\n");
3085
3189
  },
3086
3190
  pathResolver: (rule, outputDir) => {
3087
- return (0, import_node_path13.join)(outputDir, `${rule.filename}.mdc`);
3191
+ return (0, import_node_path11.join)(outputDir, `${rule.filename}.mdc`);
3088
3192
  }
3089
3193
  },
3090
3194
  codexcli: {
@@ -3120,10 +3224,10 @@ var GENERATOR_REGISTRY = {
3120
3224
  pathResolver: (rule, outputDir) => {
3121
3225
  const outputFormat = rule.frontmatter.windsurfOutputFormat || "directory";
3122
3226
  if (outputFormat === "single-file") {
3123
- return (0, import_node_path13.join)(outputDir, ".windsurf-rules");
3227
+ return (0, import_node_path11.join)(outputDir, ".windsurf-rules");
3124
3228
  } else {
3125
- const rulesDir = (0, import_node_path13.join)(outputDir, ".windsurf", "rules");
3126
- return (0, import_node_path13.join)(rulesDir, `${rule.filename}.md`);
3229
+ const rulesDir = (0, import_node_path11.join)(outputDir, ".windsurf", "rules");
3230
+ return (0, import_node_path11.join)(rulesDir, `${rule.filename}.md`);
3127
3231
  }
3128
3232
  }
3129
3233
  },
@@ -3241,7 +3345,7 @@ async function generateCodexConfig(rules, config, baseDir) {
3241
3345
  const concatenatedContent = generateConcatenatedCodexContent(sortedRules);
3242
3346
  if (concatenatedContent.trim()) {
3243
3347
  const outputDir = resolveOutputDir(config, "codexcli", baseDir);
3244
- const filepath = `${outputDir}/codex.md`;
3348
+ const filepath = `${outputDir}/AGENTS.md`;
3245
3349
  outputs.push({
3246
3350
  tool: "codexcli",
3247
3351
  filepath,
@@ -3347,14 +3451,12 @@ async function generateConfigurations(rules, config, targetTools, baseDir) {
3347
3451
  const toolsToGenerate = targetTools || config.defaultTargets;
3348
3452
  const rootFiles = rules.filter((rule) => rule.frontmatter.root === true);
3349
3453
  if (rootFiles.length === 0) {
3350
- console.warn(
3351
- "\u26A0\uFE0F Warning: No files with 'root: true' found. This may result in incomplete configurations."
3352
- );
3454
+ logger.warn("No files with 'root: true' found. This may result in incomplete configurations.");
3353
3455
  }
3354
3456
  for (const tool of toolsToGenerate) {
3355
3457
  const relevantRules = filterRulesForTool(rules, tool, config);
3356
3458
  if (relevantRules.length === 0) {
3357
- console.warn(`No rules found for tool: ${tool}`);
3459
+ logger.warn(`No rules found for tool: ${tool}`);
3358
3460
  continue;
3359
3461
  }
3360
3462
  const toolOutputs = await generateForTool(tool, relevantRules, config, baseDir);
@@ -3419,22 +3521,21 @@ async function generateForTool(tool, rules, config, baseDir) {
3419
3521
  return [...windsurfRulesOutputs, ...windsurfIgnoreOutputs];
3420
3522
  }
3421
3523
  default:
3422
- console.warn(`Unknown tool: ${tool}`);
3524
+ logger.warn(`Unknown tool: ${tool}`);
3423
3525
  return null;
3424
3526
  }
3425
3527
  }
3426
3528
 
3427
3529
  // src/core/parser.ts
3428
- var import_node_path14 = require("path");
3429
- var import_gray_matter2 = __toESM(require("gray-matter"), 1);
3530
+ var import_node_path12 = require("path");
3430
3531
  async function parseRulesFromDirectory(aiRulesDir) {
3431
3532
  const ignorePatterns = await loadIgnorePatterns();
3432
- const allRuleFiles = await findFiles(aiRulesDir, ".md");
3533
+ const allRuleFiles = await findRuleFiles(aiRulesDir);
3433
3534
  const ruleFiles = filterIgnoredFiles(allRuleFiles, ignorePatterns.patterns);
3434
3535
  const rules = [];
3435
3536
  const errors = [];
3436
3537
  if (ignorePatterns.patterns.length > 0) {
3437
- console.log(`Loaded ${ignorePatterns.patterns.length} ignore patterns from .rulesyncignore`);
3538
+ logger.info(`Loaded ${ignorePatterns.patterns.length} ignore patterns from .rulesyncignore`);
3438
3539
  }
3439
3540
  for (const filepath of ruleFiles) {
3440
3541
  try {
@@ -3460,7 +3561,7 @@ ${errors.join("\n")}`);
3460
3561
  }
3461
3562
  async function parseRuleFile(filepath) {
3462
3563
  const content = await readFileContent(filepath);
3463
- const parsed = (0, import_gray_matter2.default)(content);
3564
+ const parsed = parseFrontmatter(content);
3464
3565
  try {
3465
3566
  const validatedData = RuleFrontmatterSchema.parse(parsed.data);
3466
3567
  const frontmatter = {
@@ -3479,7 +3580,7 @@ async function parseRuleFile(filepath) {
3479
3580
  },
3480
3581
  ...validatedData.tags !== void 0 && { tags: validatedData.tags }
3481
3582
  };
3482
- const filename = (0, import_node_path14.basename)(filepath, ".md");
3583
+ const filename = (0, import_node_path12.basename)(filepath, ".md");
3483
3584
  return {
3484
3585
  frontmatter,
3485
3586
  content: parsed.content,
@@ -3648,6 +3749,7 @@ async function generateCommand(options = {}) {
3648
3749
  ...options.baseDirs !== void 0 && { baseDirs: options.baseDirs }
3649
3750
  };
3650
3751
  const config = mergeWithCliOptions(configResult.config, cliOptions);
3752
+ logger.setVerbose(config.verbose || false);
3651
3753
  if (options.tools && options.tools.length > 0) {
3652
3754
  const configTargets = config.defaultTargets;
3653
3755
  const cliTools = options.tools;
@@ -3656,18 +3758,18 @@ async function generateCommand(options = {}) {
3656
3758
  const notInConfig = cliTools.filter((tool) => !configTargetsSet.has(tool));
3657
3759
  const notInCli = configTargets.filter((tool) => !cliToolsSet.has(tool));
3658
3760
  if (notInConfig.length > 0 || notInCli.length > 0) {
3659
- console.warn("\u26A0\uFE0F Warning: CLI tool selection differs from configuration!");
3660
- console.warn(` Config targets: ${configTargets.join(", ")}`);
3661
- console.warn(` CLI specified: ${cliTools.join(", ")}`);
3761
+ logger.warn("\u26A0\uFE0F Warning: CLI tool selection differs from configuration!");
3762
+ logger.warn(` Config targets: ${configTargets.join(", ")}`);
3763
+ logger.warn(` CLI specified: ${cliTools.join(", ")}`);
3662
3764
  if (notInConfig.length > 0) {
3663
- console.warn(` Tools specified but not in config: ${notInConfig.join(", ")}`);
3765
+ logger.warn(` Tools specified but not in config: ${notInConfig.join(", ")}`);
3664
3766
  }
3665
3767
  if (notInCli.length > 0) {
3666
- console.warn(` Tools in config but not specified: ${notInCli.join(", ")}`);
3768
+ logger.warn(` Tools in config but not specified: ${notInCli.join(", ")}`);
3667
3769
  }
3668
- console.warn("\n The configuration file targets will be used.");
3669
- console.warn(" To change targets, update your rulesync config file.");
3670
- console.warn("");
3770
+ logger.warn("\n The configuration file targets will be used.");
3771
+ logger.warn(" To change targets, update your rulesync config file.");
3772
+ logger.warn("");
3671
3773
  }
3672
3774
  }
3673
3775
  let baseDirs;
@@ -3678,42 +3780,46 @@ async function generateCommand(options = {}) {
3678
3780
  } else {
3679
3781
  baseDirs = [process.cwd()];
3680
3782
  }
3681
- if (config.verbose && configResult.filepath) {
3682
- console.log(`Loaded configuration from: ${configResult.filepath}`);
3683
- }
3684
- console.log("Generating configuration files...");
3783
+ logger.info(`Loaded configuration from: ${configResult.filepath}`);
3784
+ logger.log("Generating configuration files...");
3685
3785
  if (!await fileExists(config.aiRulesDir)) {
3686
- console.error("\u274C .rulesync directory not found. Run 'rulesync init' first.");
3786
+ logger.error("\u274C .rulesync directory not found. Run 'rulesync init' first.");
3687
3787
  process.exit(1);
3688
3788
  }
3689
3789
  try {
3690
- if (config.verbose) {
3691
- console.log(`Parsing rules from ${config.aiRulesDir}...`);
3692
- }
3790
+ logger.info(`Parsing rules from ${config.aiRulesDir}...`);
3693
3791
  const rules = await parseRulesFromDirectory(config.aiRulesDir);
3694
3792
  if (rules.length === 0) {
3695
- console.warn("\u26A0\uFE0F No rules found in .rulesync directory");
3793
+ logger.warn("\u26A0\uFE0F No rules found in .rulesync directory");
3696
3794
  return;
3697
3795
  }
3698
- if (config.verbose) {
3699
- console.log(`Found ${rules.length} rule(s)`);
3700
- console.log(`Base directories: ${baseDirs.join(", ")}`);
3701
- }
3796
+ logger.info(`Found ${rules.length} rule(s)`);
3797
+ logger.info(`Base directories: ${baseDirs.join(", ")}`);
3702
3798
  if (config.delete) {
3703
- if (config.verbose) {
3704
- console.log("Deleting existing output directories...");
3705
- }
3799
+ logger.info("Deleting existing output directories...");
3706
3800
  const targetTools = config.defaultTargets;
3707
3801
  const deleteTasks = [];
3802
+ const commandsDir = (0, import_node_path13.join)(config.aiRulesDir, "commands");
3803
+ const hasCommands = await fileExists(commandsDir);
3804
+ let hasCommandFiles = false;
3805
+ if (hasCommands) {
3806
+ const { readdir: readdir2 } = await import("fs/promises");
3807
+ try {
3808
+ const files = await readdir2(commandsDir);
3809
+ hasCommandFiles = files.some((file) => file.endsWith(".md"));
3810
+ } catch {
3811
+ hasCommandFiles = false;
3812
+ }
3813
+ }
3708
3814
  for (const tool of targetTools) {
3709
3815
  switch (tool) {
3710
3816
  case "augmentcode":
3711
- deleteTasks.push(removeDirectory((0, import_node_path15.join)(".augment", "rules")));
3712
- deleteTasks.push(removeDirectory((0, import_node_path15.join)(".augment", "ignore")));
3817
+ deleteTasks.push(removeDirectory((0, import_node_path13.join)(".augment", "rules")));
3818
+ deleteTasks.push(removeDirectory((0, import_node_path13.join)(".augment", "ignore")));
3713
3819
  break;
3714
3820
  case "augmentcode-legacy":
3715
3821
  deleteTasks.push(removeClaudeGeneratedFiles());
3716
- deleteTasks.push(removeDirectory((0, import_node_path15.join)(".augment", "ignore")));
3822
+ deleteTasks.push(removeDirectory((0, import_node_path13.join)(".augment", "ignore")));
3717
3823
  break;
3718
3824
  case "copilot":
3719
3825
  deleteTasks.push(removeDirectory(config.outputPaths.copilot));
@@ -3726,15 +3832,21 @@ async function generateCommand(options = {}) {
3726
3832
  break;
3727
3833
  case "claudecode":
3728
3834
  deleteTasks.push(removeClaudeGeneratedFiles());
3729
- deleteTasks.push(removeDirectory((0, import_node_path15.join)(".claude", "commands")));
3835
+ if (hasCommandFiles) {
3836
+ deleteTasks.push(removeDirectory((0, import_node_path13.join)(".claude", "commands")));
3837
+ }
3730
3838
  break;
3731
3839
  case "roo":
3732
3840
  deleteTasks.push(removeDirectory(config.outputPaths.roo));
3733
- deleteTasks.push(removeDirectory((0, import_node_path15.join)(".roo", "commands")));
3841
+ if (hasCommandFiles) {
3842
+ deleteTasks.push(removeDirectory((0, import_node_path13.join)(".roo", "commands")));
3843
+ }
3734
3844
  break;
3735
3845
  case "geminicli":
3736
3846
  deleteTasks.push(removeDirectory(config.outputPaths.geminicli));
3737
- deleteTasks.push(removeDirectory((0, import_node_path15.join)(".gemini", "commands")));
3847
+ if (hasCommandFiles) {
3848
+ deleteTasks.push(removeDirectory((0, import_node_path13.join)(".gemini", "commands")));
3849
+ }
3738
3850
  break;
3739
3851
  case "kiro":
3740
3852
  deleteTasks.push(removeDirectory(config.outputPaths.kiro));
@@ -3745,44 +3857,34 @@ async function generateCommand(options = {}) {
3745
3857
  }
3746
3858
  }
3747
3859
  await Promise.all(deleteTasks);
3748
- if (config.verbose) {
3749
- console.log("Deleted existing output directories");
3750
- }
3860
+ logger.info("Deleted existing output directories");
3751
3861
  }
3752
3862
  let totalOutputs = 0;
3753
3863
  for (const baseDir of baseDirs) {
3754
- if (config.verbose) {
3755
- console.log(`
3864
+ logger.info(`
3756
3865
  Generating configurations for base directory: ${baseDir}`);
3757
- }
3758
3866
  const outputs = await generateConfigurations(rules, config, config.defaultTargets, baseDir);
3759
3867
  if (outputs.length === 0) {
3760
- if (config.verbose) {
3761
- console.warn(`\u26A0\uFE0F No configurations generated for ${baseDir}`);
3762
- }
3868
+ logger.warn(`\u26A0\uFE0F No configurations generated for ${baseDir}`);
3763
3869
  continue;
3764
3870
  }
3765
3871
  for (const output of outputs) {
3766
3872
  await writeFileContent(output.filepath, output.content);
3767
- console.log(`\u2705 Generated ${output.tool} configuration: ${output.filepath}`);
3873
+ logger.success(`Generated ${output.tool} configuration: ${output.filepath}`);
3768
3874
  }
3769
3875
  totalOutputs += outputs.length;
3770
3876
  }
3771
3877
  if (totalOutputs === 0) {
3772
- console.warn("\u26A0\uFE0F No configurations generated");
3878
+ logger.warn("\u26A0\uFE0F No configurations generated");
3773
3879
  return;
3774
3880
  }
3775
- if (config.verbose) {
3776
- console.log("\nGenerating MCP configurations...");
3777
- }
3881
+ logger.info("\nGenerating MCP configurations...");
3778
3882
  let totalMcpOutputs = 0;
3779
3883
  for (const baseDir of baseDirs) {
3780
3884
  try {
3781
3885
  const mcpConfig = parseMcpConfig(process.cwd());
3782
3886
  if (!mcpConfig || !mcpConfig.mcpServers || Object.keys(mcpConfig.mcpServers).length === 0) {
3783
- if (config.verbose) {
3784
- console.log(`No MCP configuration found for ${baseDir}`);
3785
- }
3887
+ logger.info(`No MCP configuration found for ${baseDir}`);
3786
3888
  continue;
3787
3889
  }
3788
3890
  const mcpResults = await generateMcpConfigurations(
@@ -3791,27 +3893,21 @@ Generating configurations for base directory: ${baseDir}`);
3791
3893
  config.defaultTargets
3792
3894
  );
3793
3895
  if (mcpResults.length === 0) {
3794
- if (config.verbose) {
3795
- console.log(`No MCP configurations generated for ${baseDir}`);
3796
- }
3896
+ logger.info(`No MCP configurations generated for ${baseDir}`);
3797
3897
  continue;
3798
3898
  }
3799
3899
  for (const result of mcpResults) {
3800
3900
  await writeFileContent(result.filepath, result.content);
3801
- console.log(`\u2705 Generated ${result.tool} MCP configuration: ${result.filepath}`);
3901
+ logger.success(`Generated ${result.tool} MCP configuration: ${result.filepath}`);
3802
3902
  totalMcpOutputs++;
3803
3903
  }
3804
3904
  } catch (error) {
3805
- if (config.verbose) {
3806
- console.error(
3807
- `\u274C Failed to generate MCP configurations: ${error instanceof Error ? error.message : String(error)}`
3808
- );
3809
- }
3905
+ logger.error(
3906
+ `\u274C Failed to generate MCP configurations: ${error instanceof Error ? error.message : String(error)}`
3907
+ );
3810
3908
  }
3811
3909
  }
3812
- if (config.verbose) {
3813
- console.log("\nGenerating command files...");
3814
- }
3910
+ logger.info("\nGenerating command files...");
3815
3911
  let totalCommandOutputs = 0;
3816
3912
  for (const baseDir of baseDirs) {
3817
3913
  const commandResults = await generateCommands(
@@ -3820,14 +3916,12 @@ Generating configurations for base directory: ${baseDir}`);
3820
3916
  config.defaultTargets
3821
3917
  );
3822
3918
  if (commandResults.length === 0) {
3823
- if (config.verbose) {
3824
- console.log(`No commands found for ${baseDir}`);
3825
- }
3919
+ logger.info(`No commands found for ${baseDir}`);
3826
3920
  continue;
3827
3921
  }
3828
3922
  for (const result of commandResults) {
3829
3923
  await writeFileContent(result.filepath, result.content);
3830
- console.log(`\u2705 Generated ${result.tool} command: ${result.filepath}`);
3924
+ logger.success(`Generated ${result.tool} command: ${result.filepath}`);
3831
3925
  totalCommandOutputs++;
3832
3926
  }
3833
3927
  }
@@ -3837,22 +3931,22 @@ Generating configurations for base directory: ${baseDir}`);
3837
3931
  if (totalOutputs > 0) parts.push(`${totalOutputs} configurations`);
3838
3932
  if (totalMcpOutputs > 0) parts.push(`${totalMcpOutputs} MCP configurations`);
3839
3933
  if (totalCommandOutputs > 0) parts.push(`${totalCommandOutputs} commands`);
3840
- console.log(
3934
+ logger.success(
3841
3935
  `
3842
3936
  \u{1F389} All done! Generated ${totalGenerated} file(s) total (${parts.join(" + ")})`
3843
3937
  );
3844
3938
  }
3845
3939
  } catch (error) {
3846
- console.error("\u274C Failed to generate configurations:", error);
3940
+ logger.error("\u274C Failed to generate configurations:", error);
3847
3941
  process.exit(1);
3848
3942
  }
3849
3943
  }
3850
3944
 
3851
3945
  // src/cli/commands/gitignore.ts
3852
3946
  var import_node_fs2 = require("fs");
3853
- var import_node_path16 = require("path");
3947
+ var import_node_path14 = require("path");
3854
3948
  var gitignoreCommand = async () => {
3855
- const gitignorePath = (0, import_node_path16.join)(process.cwd(), ".gitignore");
3949
+ const gitignorePath = (0, import_node_path14.join)(process.cwd(), ".gitignore");
3856
3950
  const rulesFilesToIgnore = [
3857
3951
  "# Generated by rulesync - AI tool configuration files",
3858
3952
  "**/.github/copilot-instructions.md",
@@ -3864,7 +3958,7 @@ var gitignoreCommand = async () => {
3864
3958
  "**/CLAUDE.md",
3865
3959
  "**/.claude/memories/",
3866
3960
  "**/.claude/commands/",
3867
- "**/codex.md",
3961
+ "**/AGENTS.md",
3868
3962
  "**/.codexignore",
3869
3963
  "**/.roo/rules/",
3870
3964
  "**/.rooignore",
@@ -3900,7 +3994,7 @@ var gitignoreCommand = async () => {
3900
3994
  }
3901
3995
  }
3902
3996
  if (linesToAdd.length === 0) {
3903
- console.log("\u2705 .gitignore is already up to date");
3997
+ logger.success(".gitignore is already up to date");
3904
3998
  return;
3905
3999
  }
3906
4000
  const newContent = gitignoreContent ? `${gitignoreContent.trimEnd()}
@@ -3909,21 +4003,20 @@ ${linesToAdd.join("\n")}
3909
4003
  ` : `${linesToAdd.join("\n")}
3910
4004
  `;
3911
4005
  (0, import_node_fs2.writeFileSync)(gitignorePath, newContent);
3912
- console.log(`\u2705 Added ${linesToAdd.length} rules to .gitignore:`);
4006
+ logger.success(`Added ${linesToAdd.length} rules to .gitignore:`);
3913
4007
  for (const line of linesToAdd) {
3914
4008
  if (!line.startsWith("#")) {
3915
- console.log(` ${line}`);
4009
+ logger.log(` ${line}`);
3916
4010
  }
3917
4011
  }
3918
4012
  };
3919
4013
 
3920
4014
  // src/core/importer.ts
3921
- var import_node_path23 = require("path");
3922
- var import_gray_matter7 = __toESM(require("gray-matter"), 1);
4015
+ var import_node_path21 = require("path");
4016
+ var import_gray_matter2 = __toESM(require("gray-matter"), 1);
3923
4017
 
3924
4018
  // src/parsers/augmentcode.ts
3925
- var import_node_path17 = require("path");
3926
- var import_gray_matter3 = __toESM(require("gray-matter"), 1);
4019
+ var import_node_path15 = require("path");
3927
4020
 
3928
4021
  // src/utils/parser-helpers.ts
3929
4022
  function createParseResult() {
@@ -3971,7 +4064,7 @@ async function parseAugmentcodeLegacyConfiguration(baseDir = process.cwd()) {
3971
4064
  async function parseUnifiedAugmentcode(baseDir, config) {
3972
4065
  const result = createParseResult();
3973
4066
  if (config.rulesDir) {
3974
- const rulesDir = (0, import_node_path17.join)(baseDir, config.rulesDir);
4067
+ const rulesDir = (0, import_node_path15.join)(baseDir, config.rulesDir);
3975
4068
  if (await fileExists(rulesDir)) {
3976
4069
  const rulesResult = await parseAugmentRules(rulesDir, config);
3977
4070
  addRules(result, rulesResult.rules);
@@ -3984,7 +4077,7 @@ async function parseUnifiedAugmentcode(baseDir, config) {
3984
4077
  }
3985
4078
  }
3986
4079
  if (config.legacyFilePath) {
3987
- const legacyPath = (0, import_node_path17.join)(baseDir, config.legacyFilePath);
4080
+ const legacyPath = (0, import_node_path15.join)(baseDir, config.legacyFilePath);
3988
4081
  if (await fileExists(legacyPath)) {
3989
4082
  const legacyResult = await parseAugmentGuidelines(legacyPath, config);
3990
4083
  if (legacyResult.rule) {
@@ -4008,23 +4101,22 @@ async function parseAugmentRules(rulesDir, config) {
4008
4101
  const files = await readdir2(rulesDir);
4009
4102
  for (const file of files) {
4010
4103
  if (file.endsWith(".md") || file.endsWith(".mdc")) {
4011
- const filePath = (0, import_node_path17.join)(rulesDir, file);
4104
+ const filePath = (0, import_node_path15.join)(rulesDir, file);
4012
4105
  try {
4013
4106
  const rawContent = await readFileContent(filePath);
4014
- const parsed = (0, import_gray_matter3.default)(rawContent);
4015
- const frontmatterData = parsed.data;
4016
- const ruleType = frontmatterData.type || "manual";
4017
- const description = frontmatterData.description || "";
4018
- const tags = Array.isArray(frontmatterData.tags) ? frontmatterData.tags : void 0;
4107
+ const parsed = parseFrontmatter(rawContent);
4108
+ const ruleType = extractStringField(parsed.data, "type", "manual");
4109
+ const description = extractStringField(parsed.data, "description", "");
4110
+ const tags = extractArrayField(parsed.data, "tags");
4019
4111
  const isRoot = ruleType === "always";
4020
- const filename = (0, import_node_path17.basename)(file, file.endsWith(".mdc") ? ".mdc" : ".md");
4112
+ const filename = (0, import_node_path15.basename)(file, file.endsWith(".mdc") ? ".mdc" : ".md");
4021
4113
  const frontmatter = {
4022
4114
  root: isRoot,
4023
4115
  targets: [config.targetName],
4024
4116
  description,
4025
4117
  globs: ["**/*"],
4026
4118
  // AugmentCode doesn't use specific globs in the same way
4027
- ...tags && { tags }
4119
+ ...tags.length > 0 && { tags }
4028
4120
  };
4029
4121
  rules.push({
4030
4122
  frontmatter,
@@ -4075,8 +4167,7 @@ async function parseAugmentGuidelines(guidelinesPath, config) {
4075
4167
  }
4076
4168
 
4077
4169
  // src/parsers/shared-helpers.ts
4078
- var import_node_path18 = require("path");
4079
- var import_gray_matter4 = __toESM(require("gray-matter"), 1);
4170
+ var import_node_path16 = require("path");
4080
4171
  async function parseConfigurationFiles(baseDir = process.cwd(), config) {
4081
4172
  const errors = [];
4082
4173
  const rules = [];
@@ -4089,16 +4180,18 @@ async function parseConfigurationFiles(baseDir = process.cwd(), config) {
4089
4180
  let content;
4090
4181
  let frontmatter;
4091
4182
  if (mainFile.useFrontmatter) {
4092
- const parsed = (0, import_gray_matter4.default)(rawContent);
4093
- content = parsed.content.trim();
4094
- const parsedFrontmatter = parsed.data;
4183
+ const parsed = parseFrontmatter(rawContent);
4184
+ content = parsed.content;
4095
4185
  frontmatter = {
4096
4186
  root: mainFile.isRoot ?? false,
4097
4187
  targets: [config.tool],
4098
- description: parsedFrontmatter.description || mainFile.description,
4099
- globs: Array.isArray(parsedFrontmatter.globs) ? parsedFrontmatter.globs : ["**/*"],
4100
- ...parsedFrontmatter.tags && { tags: parsedFrontmatter.tags }
4188
+ description: extractStringField(parsed.data, "description", mainFile.description),
4189
+ globs: extractArrayField(parsed.data, "globs", ["**/*"])
4101
4190
  };
4191
+ const tags = extractArrayField(parsed.data, "tags");
4192
+ if (tags.length > 0) {
4193
+ frontmatter.tags = tags;
4194
+ }
4102
4195
  } else {
4103
4196
  content = rawContent.trim();
4104
4197
  frontmatter = {
@@ -4131,23 +4224,29 @@ async function parseConfigurationFiles(baseDir = process.cwd(), config) {
4131
4224
  const files = await readdir2(dirPath);
4132
4225
  for (const file of files) {
4133
4226
  if (file.endsWith(dirConfig.filePattern)) {
4134
- const filePath = (0, import_node_path18.join)(dirPath, file);
4227
+ const filePath = (0, import_node_path16.join)(dirPath, file);
4135
4228
  const fileResult = await safeAsyncOperation(async () => {
4136
4229
  const rawContent = await readFileContent(filePath);
4137
4230
  let content;
4138
4231
  let frontmatter;
4139
4232
  const filename = file.replace(new RegExp(`\\${dirConfig.filePattern}$`), "");
4140
4233
  if (dirConfig.filePattern === ".instructions.md") {
4141
- const parsed = (0, import_gray_matter4.default)(rawContent);
4142
- content = parsed.content.trim();
4143
- const parsedFrontmatter = parsed.data;
4234
+ const parsed = parseFrontmatter(rawContent);
4235
+ content = parsed.content;
4144
4236
  frontmatter = {
4145
4237
  root: false,
4146
4238
  targets: [config.tool],
4147
- description: parsedFrontmatter.description || `${dirConfig.description}: ${filename}`,
4148
- globs: Array.isArray(parsedFrontmatter.globs) ? parsedFrontmatter.globs : ["**/*"],
4149
- ...parsedFrontmatter.tags && { tags: parsedFrontmatter.tags }
4239
+ description: extractStringField(
4240
+ parsed.data,
4241
+ "description",
4242
+ `${dirConfig.description}: ${filename}`
4243
+ ),
4244
+ globs: extractArrayField(parsed.data, "globs", ["**/*"])
4150
4245
  };
4246
+ const tags = extractArrayField(parsed.data, "tags");
4247
+ if (tags.length > 0) {
4248
+ frontmatter.tags = tags;
4249
+ }
4151
4250
  } else {
4152
4251
  content = rawContent.trim();
4153
4252
  frontmatter = {
@@ -4276,10 +4375,10 @@ async function parseMemoryFiles(memoryDir, config) {
4276
4375
  const files = await readdir2(memoryDir);
4277
4376
  for (const file of files) {
4278
4377
  if (file.endsWith(".md")) {
4279
- const filePath = (0, import_node_path18.join)(memoryDir, file);
4378
+ const filePath = (0, import_node_path16.join)(memoryDir, file);
4280
4379
  const content = await readFileContent(filePath);
4281
4380
  if (content.trim()) {
4282
- const filename = (0, import_node_path18.basename)(file, ".md");
4381
+ const filename = (0, import_node_path16.basename)(file, ".md");
4283
4382
  const frontmatter = {
4284
4383
  root: false,
4285
4384
  targets: [config.tool],
@@ -4306,20 +4405,19 @@ async function parseCommandsFiles(commandsDir, config) {
4306
4405
  const files = await readdir2(commandsDir);
4307
4406
  for (const file of files) {
4308
4407
  if (file.endsWith(".md")) {
4309
- const filePath = (0, import_node_path18.join)(commandsDir, file);
4408
+ const filePath = (0, import_node_path16.join)(commandsDir, file);
4310
4409
  const content = await readFileContent(filePath);
4311
4410
  if (content.trim()) {
4312
- const filename = (0, import_node_path18.basename)(file, ".md");
4411
+ const filename = (0, import_node_path16.basename)(file, ".md");
4313
4412
  let frontmatter;
4314
4413
  let ruleContent;
4315
4414
  try {
4316
- const parsed = (0, import_gray_matter4.default)(content);
4317
- ruleContent = parsed.content.trim();
4318
- const parsedFrontmatter = parsed.data;
4415
+ const parsed = parseFrontmatter(content);
4416
+ ruleContent = parsed.content;
4319
4417
  frontmatter = {
4320
4418
  root: false,
4321
4419
  targets: [config.tool],
4322
- description: parsedFrontmatter.description || `Command: ${filename}`,
4420
+ description: extractStringField(parsed.data, "description", `Command: ${filename}`),
4323
4421
  globs: ["**/*"]
4324
4422
  };
4325
4423
  } catch {
@@ -4420,7 +4518,7 @@ async function parseClineConfiguration(baseDir = process.cwd()) {
4420
4518
  }
4421
4519
 
4422
4520
  // src/parsers/codexcli.ts
4423
- var import_node_path19 = require("path");
4521
+ var import_node_path17 = require("path");
4424
4522
 
4425
4523
  // src/parsers/copilot.ts
4426
4524
  async function parseCopilotConfiguration(baseDir = process.cwd()) {
@@ -4443,8 +4541,7 @@ async function parseCopilotConfiguration(baseDir = process.cwd()) {
4443
4541
  }
4444
4542
 
4445
4543
  // src/parsers/cursor.ts
4446
- var import_node_path20 = require("path");
4447
- var import_gray_matter5 = __toESM(require("gray-matter"), 1);
4544
+ var import_node_path18 = require("path");
4448
4545
  var import_js_yaml = require("js-yaml");
4449
4546
  var import_mini8 = require("zod/mini");
4450
4547
  var customMatterOptions = {
@@ -4568,12 +4665,12 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
4568
4665
  const rules = [];
4569
4666
  let ignorePatterns;
4570
4667
  let mcpServers;
4571
- const cursorFilePath = (0, import_node_path20.join)(baseDir, ".cursorrules");
4668
+ const cursorFilePath = (0, import_node_path18.join)(baseDir, ".cursorrules");
4572
4669
  if (await fileExists(cursorFilePath)) {
4573
4670
  try {
4574
4671
  const rawContent = await readFileContent(cursorFilePath);
4575
- const parsed = (0, import_gray_matter5.default)(rawContent, customMatterOptions);
4576
- const content = parsed.content.trim();
4672
+ const parsed = parseFrontmatter(rawContent, { matterOptions: customMatterOptions });
4673
+ const content = parsed.content;
4577
4674
  if (content) {
4578
4675
  const frontmatter = convertCursorMdcFrontmatter(parsed.data, "cursorrules");
4579
4676
  frontmatter.targets = ["cursor"];
@@ -4589,20 +4686,20 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
4589
4686
  errors.push(`Failed to parse .cursorrules file: ${errorMessage}`);
4590
4687
  }
4591
4688
  }
4592
- const cursorRulesDir = (0, import_node_path20.join)(baseDir, ".cursor", "rules");
4689
+ const cursorRulesDir = (0, import_node_path18.join)(baseDir, ".cursor", "rules");
4593
4690
  if (await fileExists(cursorRulesDir)) {
4594
4691
  try {
4595
4692
  const { readdir: readdir2 } = await import("fs/promises");
4596
4693
  const files = await readdir2(cursorRulesDir);
4597
4694
  for (const file of files) {
4598
4695
  if (file.endsWith(".mdc")) {
4599
- const filePath = (0, import_node_path20.join)(cursorRulesDir, file);
4696
+ const filePath = (0, import_node_path18.join)(cursorRulesDir, file);
4600
4697
  try {
4601
4698
  const rawContent = await readFileContent(filePath);
4602
- const parsed = (0, import_gray_matter5.default)(rawContent, customMatterOptions);
4603
- const content = parsed.content.trim();
4699
+ const parsed = parseFrontmatter(rawContent, { matterOptions: customMatterOptions });
4700
+ const content = parsed.content;
4604
4701
  if (content) {
4605
- const filename = (0, import_node_path20.basename)(file, ".mdc");
4702
+ const filename = (0, import_node_path18.basename)(file, ".mdc");
4606
4703
  const frontmatter = convertCursorMdcFrontmatter(parsed.data, filename);
4607
4704
  rules.push({
4608
4705
  frontmatter,
@@ -4625,7 +4722,7 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
4625
4722
  if (rules.length === 0) {
4626
4723
  errors.push("No Cursor configuration files found (.cursorrules or .cursor/rules/*.mdc)");
4627
4724
  }
4628
- const cursorIgnorePath = (0, import_node_path20.join)(baseDir, ".cursorignore");
4725
+ const cursorIgnorePath = (0, import_node_path18.join)(baseDir, ".cursorignore");
4629
4726
  if (await fileExists(cursorIgnorePath)) {
4630
4727
  try {
4631
4728
  const content = await readFileContent(cursorIgnorePath);
@@ -4638,7 +4735,7 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
4638
4735
  errors.push(`Failed to parse .cursorignore: ${errorMessage}`);
4639
4736
  }
4640
4737
  }
4641
- const cursorMcpPath = (0, import_node_path20.join)(baseDir, ".cursor", "mcp.json");
4738
+ const cursorMcpPath = (0, import_node_path18.join)(baseDir, ".cursor", "mcp.json");
4642
4739
  if (await fileExists(cursorMcpPath)) {
4643
4740
  try {
4644
4741
  const content = await readFileContent(cursorMcpPath);
@@ -4688,11 +4785,11 @@ async function parseGeminiConfiguration(baseDir = process.cwd()) {
4688
4785
  }
4689
4786
 
4690
4787
  // src/parsers/junie.ts
4691
- var import_node_path21 = require("path");
4788
+ var import_node_path19 = require("path");
4692
4789
  async function parseJunieConfiguration(baseDir = process.cwd()) {
4693
4790
  const errors = [];
4694
4791
  const rules = [];
4695
- const guidelinesPath = (0, import_node_path21.join)(baseDir, ".junie", "guidelines.md");
4792
+ const guidelinesPath = (0, import_node_path19.join)(baseDir, ".junie", "guidelines.md");
4696
4793
  if (!await fileExists(guidelinesPath)) {
4697
4794
  errors.push(".junie/guidelines.md file not found");
4698
4795
  return { rules, errors };
@@ -4745,18 +4842,23 @@ async function parseRooConfiguration(baseDir = process.cwd()) {
4745
4842
 
4746
4843
  // src/parsers/windsurf.ts
4747
4844
  var import_promises3 = require("fs/promises");
4748
- var import_node_path22 = require("path");
4749
- var import_gray_matter6 = __toESM(require("gray-matter"), 1);
4845
+ var import_node_path20 = require("path");
4750
4846
 
4751
4847
  // src/core/importer.ts
4752
4848
  async function importConfiguration(options) {
4753
- const { tool, baseDir = process.cwd(), rulesDir = ".rulesync", verbose = false } = options;
4849
+ const {
4850
+ tool,
4851
+ baseDir = process.cwd(),
4852
+ rulesDir = ".rulesync",
4853
+ verbose = false,
4854
+ useLegacyLocation = false
4855
+ } = options;
4754
4856
  const errors = [];
4755
4857
  let rules = [];
4756
4858
  let ignorePatterns;
4757
4859
  let mcpServers;
4758
4860
  if (verbose) {
4759
- console.log(`Importing ${tool} configuration from ${baseDir}...`);
4861
+ logger.log(`Importing ${tool} configuration from ${baseDir}...`);
4760
4862
  }
4761
4863
  try {
4762
4864
  switch (tool) {
@@ -4832,7 +4934,7 @@ async function importConfiguration(options) {
4832
4934
  if (rules.length === 0 && !ignorePatterns && !mcpServers) {
4833
4935
  return { success: false, rulesCreated: 0, errors };
4834
4936
  }
4835
- const rulesDirPath = (0, import_node_path23.join)(baseDir, rulesDir);
4937
+ const rulesDirPath = (0, import_node_path21.join)(baseDir, rulesDir);
4836
4938
  try {
4837
4939
  const { mkdir: mkdir3 } = await import("fs/promises");
4838
4940
  await mkdir3(rulesDirPath, { recursive: true });
@@ -4847,16 +4949,22 @@ async function importConfiguration(options) {
4847
4949
  const baseFilename = rule.filename;
4848
4950
  let targetDir = rulesDirPath;
4849
4951
  if (rule.type === "command") {
4850
- targetDir = (0, import_node_path23.join)(rulesDirPath, "commands");
4952
+ targetDir = (0, import_node_path21.join)(rulesDirPath, "commands");
4851
4953
  const { mkdir: mkdir3 } = await import("fs/promises");
4852
4954
  await mkdir3(targetDir, { recursive: true });
4955
+ } else {
4956
+ if (!useLegacyLocation) {
4957
+ targetDir = (0, import_node_path21.join)(rulesDirPath, "rules");
4958
+ const { mkdir: mkdir3 } = await import("fs/promises");
4959
+ await mkdir3(targetDir, { recursive: true });
4960
+ }
4853
4961
  }
4854
- const filePath = (0, import_node_path23.join)(targetDir, `${baseFilename}.md`);
4962
+ const filePath = (0, import_node_path21.join)(targetDir, `${baseFilename}.md`);
4855
4963
  const content = generateRuleFileContent(rule);
4856
4964
  await writeFileContent(filePath, content);
4857
4965
  rulesCreated++;
4858
4966
  if (verbose) {
4859
- console.log(`\u2705 Created rule file: ${filePath}`);
4967
+ logger.success(`Created rule file: ${filePath}`);
4860
4968
  }
4861
4969
  } catch (error) {
4862
4970
  const errorMessage = error instanceof Error ? error.message : String(error);
@@ -4866,13 +4974,13 @@ async function importConfiguration(options) {
4866
4974
  let ignoreFileCreated = false;
4867
4975
  if (ignorePatterns && ignorePatterns.length > 0) {
4868
4976
  try {
4869
- const rulesyncignorePath = (0, import_node_path23.join)(baseDir, ".rulesyncignore");
4977
+ const rulesyncignorePath = (0, import_node_path21.join)(baseDir, ".rulesyncignore");
4870
4978
  const ignoreContent = `${ignorePatterns.join("\n")}
4871
4979
  `;
4872
4980
  await writeFileContent(rulesyncignorePath, ignoreContent);
4873
4981
  ignoreFileCreated = true;
4874
4982
  if (verbose) {
4875
- console.log(`\u2705 Created .rulesyncignore with ${ignorePatterns.length} patterns`);
4983
+ logger.success(`Created .rulesyncignore with ${ignorePatterns.length} patterns`);
4876
4984
  }
4877
4985
  } catch (error) {
4878
4986
  const errorMessage = error instanceof Error ? error.message : String(error);
@@ -4882,13 +4990,13 @@ async function importConfiguration(options) {
4882
4990
  let mcpFileCreated = false;
4883
4991
  if (mcpServers && Object.keys(mcpServers).length > 0) {
4884
4992
  try {
4885
- const mcpPath = (0, import_node_path23.join)(baseDir, rulesDir, ".mcp.json");
4993
+ const mcpPath = (0, import_node_path21.join)(baseDir, rulesDir, ".mcp.json");
4886
4994
  const mcpContent = `${JSON.stringify({ mcpServers }, null, 2)}
4887
4995
  `;
4888
4996
  await writeFileContent(mcpPath, mcpContent);
4889
4997
  mcpFileCreated = true;
4890
4998
  if (verbose) {
4891
- console.log(`\u2705 Created .mcp.json with ${Object.keys(mcpServers).length} servers`);
4999
+ logger.success(`Created .mcp.json with ${Object.keys(mcpServers).length} servers`);
4892
5000
  }
4893
5001
  } catch (error) {
4894
5002
  const errorMessage = error instanceof Error ? error.message : String(error);
@@ -4909,15 +5017,16 @@ function generateRuleFileContent(rule) {
4909
5017
  description: rule.frontmatter.description,
4910
5018
  targets: rule.frontmatter.targets
4911
5019
  };
4912
- const frontmatter2 = import_gray_matter7.default.stringify("", simplifiedFrontmatter);
5020
+ const frontmatter2 = import_gray_matter2.default.stringify("", simplifiedFrontmatter);
4913
5021
  return frontmatter2 + rule.content;
4914
5022
  }
4915
- const frontmatter = import_gray_matter7.default.stringify("", rule.frontmatter);
5023
+ const frontmatter = import_gray_matter2.default.stringify("", rule.frontmatter);
4916
5024
  return frontmatter + rule.content;
4917
5025
  }
4918
5026
 
4919
5027
  // src/cli/commands/import.ts
4920
5028
  async function importCommand(options = {}) {
5029
+ logger.setVerbose(options.verbose || false);
4921
5030
  const tools = [];
4922
5031
  if (options.augmentcode) tools.push("augmentcode");
4923
5032
  if (options["augmentcode-legacy"]) tools.push("augmentcode-legacy");
@@ -4928,66 +5037,74 @@ async function importCommand(options = {}) {
4928
5037
  if (options.roo) tools.push("roo");
4929
5038
  if (options.geminicli) tools.push("geminicli");
4930
5039
  if (tools.length === 0) {
4931
- console.error(
5040
+ logger.error(
4932
5041
  "\u274C Please specify one tool to import from (--augmentcode, --augmentcode-legacy, --claudecode, --cursor, --copilot, --cline, --roo, --geminicli)"
4933
5042
  );
4934
5043
  process.exit(1);
4935
5044
  }
4936
5045
  if (tools.length > 1) {
4937
- console.error(
5046
+ logger.error(
4938
5047
  "\u274C Only one tool can be specified at a time. Please run the import command separately for each tool."
4939
5048
  );
4940
5049
  process.exit(1);
4941
5050
  }
4942
5051
  const tool = tools[0];
4943
5052
  if (!tool) {
4944
- console.error("Error: No tool specified");
5053
+ logger.error("Error: No tool specified");
4945
5054
  process.exit(1);
4946
5055
  }
4947
- console.log(`Importing configuration files from ${tool}...`);
5056
+ logger.log(`Importing configuration files from ${tool}...`);
4948
5057
  try {
4949
5058
  const result = await importConfiguration({
4950
5059
  tool,
4951
- verbose: options.verbose ?? false
5060
+ verbose: options.verbose ?? false,
5061
+ useLegacyLocation: options.legacy ?? false
4952
5062
  });
4953
5063
  if (result.success) {
4954
- console.log(`\u2705 Imported ${result.rulesCreated} rule(s) from ${tool}`);
5064
+ logger.success(`Imported ${result.rulesCreated} rule(s) from ${tool}`);
4955
5065
  if (result.ignoreFileCreated) {
4956
- console.log("\u2705 Created .rulesyncignore file from ignore patterns");
5066
+ logger.success("Created .rulesyncignore file from ignore patterns");
4957
5067
  }
4958
5068
  if (result.mcpFileCreated) {
4959
- console.log("\u2705 Created .rulesync/.mcp.json file from MCP configuration");
5069
+ logger.success("Created .rulesync/.mcp.json file from MCP configuration");
4960
5070
  }
4961
- console.log("You can now run 'rulesync generate' to create tool-specific configurations.");
5071
+ logger.log("You can now run 'rulesync generate' to create tool-specific configurations.");
4962
5072
  } else if (result.errors.length > 0) {
4963
- console.warn(`\u26A0\uFE0F Failed to import from ${tool}: ${result.errors[0]}`);
4964
- if (options.verbose && result.errors.length > 1) {
4965
- console.log("\nDetailed errors:");
5073
+ logger.warn(`\u26A0\uFE0F Failed to import from ${tool}: ${result.errors[0]}`);
5074
+ if (result.errors.length > 1) {
5075
+ logger.info("\nDetailed errors:");
4966
5076
  for (const error of result.errors) {
4967
- console.log(` - ${error}`);
5077
+ logger.info(` - ${error}`);
4968
5078
  }
4969
5079
  }
4970
5080
  }
4971
5081
  } catch (error) {
4972
5082
  const errorMessage = error instanceof Error ? error.message : String(error);
4973
- console.error(`\u274C Error importing from ${tool}: ${errorMessage}`);
5083
+ logger.error(`\u274C Error importing from ${tool}: ${errorMessage}`);
4974
5084
  process.exit(1);
4975
5085
  }
4976
5086
  }
4977
5087
 
4978
5088
  // src/cli/commands/init.ts
4979
- var import_node_path24 = require("path");
4980
- async function initCommand() {
4981
- const aiRulesDir = ".rulesync";
4982
- console.log("Initializing rulesync...");
5089
+ var import_node_path22 = require("path");
5090
+ async function initCommand(options = {}) {
5091
+ const configResult = await loadConfig();
5092
+ const config = configResult.config;
5093
+ const aiRulesDir = config.aiRulesDir;
5094
+ logger.log("Initializing rulesync...");
4983
5095
  await ensureDir(aiRulesDir);
4984
- await createSampleFiles(aiRulesDir);
4985
- console.log("\u2705 rulesync initialized successfully!");
4986
- console.log("\nNext steps:");
4987
- console.log("1. Edit rule files in .rulesync/");
4988
- console.log("2. Run 'rulesync generate' to create configuration files");
4989
- }
4990
- async function createSampleFiles(aiRulesDir) {
5096
+ const useLegacy = options.legacy ?? config.legacy ?? false;
5097
+ const rulesDir = useLegacy ? aiRulesDir : (0, import_node_path22.join)(aiRulesDir, "rules");
5098
+ if (!useLegacy) {
5099
+ await ensureDir(rulesDir);
5100
+ }
5101
+ await createSampleFiles(rulesDir);
5102
+ logger.success("rulesync initialized successfully!");
5103
+ logger.log("\nNext steps:");
5104
+ logger.log(`1. Edit rule files in ${rulesDir}/`);
5105
+ logger.log("2. Run 'rulesync generate' to create configuration files");
5106
+ }
5107
+ async function createSampleFiles(rulesDir) {
4991
5108
  const sampleFile = {
4992
5109
  filename: "overview.md",
4993
5110
  content: `---
@@ -5023,36 +5140,36 @@ globs: ["**/*"]
5023
5140
  - Follow single responsibility principle
5024
5141
  `
5025
5142
  };
5026
- const filepath = (0, import_node_path24.join)(aiRulesDir, sampleFile.filename);
5143
+ const filepath = (0, import_node_path22.join)(rulesDir, sampleFile.filename);
5027
5144
  if (!await fileExists(filepath)) {
5028
5145
  await writeFileContent(filepath, sampleFile.content);
5029
- console.log(`Created ${filepath}`);
5146
+ logger.success(`Created ${filepath}`);
5030
5147
  } else {
5031
- console.log(`Skipped ${filepath} (already exists)`);
5148
+ logger.log(`Skipped ${filepath} (already exists)`);
5032
5149
  }
5033
5150
  }
5034
5151
 
5035
5152
  // src/cli/commands/status.ts
5036
5153
  async function statusCommand() {
5037
5154
  const config = getDefaultConfig();
5038
- console.log("rulesync Status");
5039
- console.log("===============");
5155
+ logger.log("rulesync Status");
5156
+ logger.log("===============");
5040
5157
  const rulesyncExists = await fileExists(config.aiRulesDir);
5041
- console.log(`
5158
+ logger.log(`
5042
5159
  \u{1F4C1} .rulesync directory: ${rulesyncExists ? "\u2705 Found" : "\u274C Not found"}`);
5043
5160
  if (!rulesyncExists) {
5044
- console.log("\n\u{1F4A1} Run 'rulesync init' to get started");
5161
+ logger.log("\n\u{1F4A1} Run 'rulesync init' to get started");
5045
5162
  return;
5046
5163
  }
5047
5164
  try {
5048
5165
  const rules = await parseRulesFromDirectory(config.aiRulesDir);
5049
- console.log(`
5166
+ logger.log(`
5050
5167
  \u{1F4CB} Rules: ${rules.length} total`);
5051
5168
  if (rules.length > 0) {
5052
5169
  const rootRules = rules.filter((r) => r.frontmatter.root).length;
5053
5170
  const nonRootRules = rules.length - rootRules;
5054
- console.log(` - Root rules: ${rootRules}`);
5055
- console.log(` - Non-root rules: ${nonRootRules}`);
5171
+ logger.log(` - Root rules: ${rootRules}`);
5172
+ logger.log(` - Non-root rules: ${nonRootRules}`);
5056
5173
  const targetCounts = { copilot: 0, cursor: 0, cline: 0, claudecode: 0, roo: 0 };
5057
5174
  for (const rule of rules) {
5058
5175
  const targets = rule.frontmatter.targets[0] === "*" ? config.defaultTargets : rule.frontmatter.targets;
@@ -5064,63 +5181,63 @@ async function statusCommand() {
5064
5181
  else if (target === "roo") targetCounts.roo++;
5065
5182
  }
5066
5183
  }
5067
- console.log("\n\u{1F3AF} Target tool coverage:");
5068
- console.log(` - Copilot: ${targetCounts.copilot} rules`);
5069
- console.log(` - Cursor: ${targetCounts.cursor} rules`);
5070
- console.log(` - Cline: ${targetCounts.cline} rules`);
5071
- console.log(` - Claude Code: ${targetCounts.claudecode} rules`);
5072
- console.log(` - Roo: ${targetCounts.roo} rules`);
5073
- }
5074
- console.log("\n\u{1F4E4} Generated files:");
5184
+ logger.log("\n\u{1F3AF} Target tool coverage:");
5185
+ logger.log(` - Copilot: ${targetCounts.copilot} rules`);
5186
+ logger.log(` - Cursor: ${targetCounts.cursor} rules`);
5187
+ logger.log(` - Cline: ${targetCounts.cline} rules`);
5188
+ logger.log(` - Claude Code: ${targetCounts.claudecode} rules`);
5189
+ logger.log(` - Roo: ${targetCounts.roo} rules`);
5190
+ }
5191
+ logger.log("\n\u{1F4E4} Generated files:");
5075
5192
  for (const [tool, outputPath] of Object.entries(config.outputPaths)) {
5076
5193
  const outputExists = await fileExists(outputPath);
5077
- console.log(` - ${tool}: ${outputExists ? "\u2705 Generated" : "\u274C Not found"}`);
5194
+ logger.log(` - ${tool}: ${outputExists ? "\u2705 Generated" : "\u274C Not found"}`);
5078
5195
  }
5079
5196
  if (rules.length > 0) {
5080
- console.log("\n\u{1F4A1} Run 'rulesync generate' to update configuration files");
5197
+ logger.log("\n\u{1F4A1} Run 'rulesync generate' to update configuration files");
5081
5198
  }
5082
5199
  } catch (error) {
5083
- console.error("\n\u274C Failed to get status:", error);
5200
+ logger.error("\nFailed to get status:", error);
5084
5201
  }
5085
5202
  }
5086
5203
 
5087
5204
  // src/cli/commands/validate.ts
5088
5205
  async function validateCommand() {
5089
5206
  const config = getDefaultConfig();
5090
- console.log("Validating rulesync configuration...");
5207
+ logger.log("Validating rulesync configuration...");
5091
5208
  if (!await fileExists(config.aiRulesDir)) {
5092
- console.error("\u274C .rulesync directory not found. Run 'rulesync init' first.");
5209
+ logger.error(".rulesync directory not found. Run 'rulesync init' first.");
5093
5210
  process.exit(1);
5094
5211
  }
5095
5212
  try {
5096
5213
  const rules = await parseRulesFromDirectory(config.aiRulesDir);
5097
5214
  if (rules.length === 0) {
5098
- console.warn("\u26A0\uFE0F No rules found in .rulesync directory");
5215
+ logger.warn("No rules found in .rulesync directory");
5099
5216
  return;
5100
5217
  }
5101
- console.log(`Found ${rules.length} rule(s), validating...`);
5218
+ logger.log(`Found ${rules.length} rule(s), validating...`);
5102
5219
  const validation = await validateRules(rules);
5103
5220
  if (validation.warnings.length > 0) {
5104
- console.log("\n\u26A0\uFE0F Warnings:");
5221
+ logger.log("\n\u26A0\uFE0F Warnings:");
5105
5222
  for (const warning of validation.warnings) {
5106
- console.log(` - ${warning}`);
5223
+ logger.log(` - ${warning}`);
5107
5224
  }
5108
5225
  }
5109
5226
  if (validation.errors.length > 0) {
5110
- console.log("\n\u274C Errors:");
5227
+ logger.log("\nErrors:");
5111
5228
  for (const error of validation.errors) {
5112
- console.log(` - ${error}`);
5229
+ logger.log(` - ${error}`);
5113
5230
  }
5114
5231
  }
5115
5232
  if (validation.isValid) {
5116
- console.log("\n\u2705 All rules are valid!");
5233
+ logger.success("\nAll rules are valid!");
5117
5234
  } else {
5118
- console.log(`
5119
- \u274C Validation failed with ${validation.errors.length} error(s)`);
5235
+ logger.log(`
5236
+ Validation failed with ${validation.errors.length} error(s)`);
5120
5237
  process.exit(1);
5121
5238
  }
5122
5239
  } catch (error) {
5123
- console.error("\u274C Failed to validate rules:", error);
5240
+ logger.error("Failed to validate rules:", error);
5124
5241
  process.exit(1);
5125
5242
  }
5126
5243
  }
@@ -5129,8 +5246,8 @@ async function validateCommand() {
5129
5246
  var import_chokidar = require("chokidar");
5130
5247
  async function watchCommand() {
5131
5248
  const config = getDefaultConfig();
5132
- console.log("\u{1F440} Watching for changes in .rulesync directory...");
5133
- console.log("Press Ctrl+C to stop watching");
5249
+ logger.log("\u{1F440} Watching for changes in .rulesync directory...");
5250
+ logger.log("Press Ctrl+C to stop watching");
5134
5251
  await generateCommand({ verbose: false });
5135
5252
  const watcher = (0, import_chokidar.watch)(`${config.aiRulesDir}/**/*.md`, {
5136
5253
  ignoreInitial: true,
@@ -5140,26 +5257,26 @@ async function watchCommand() {
5140
5257
  const handleChange = async (path5) => {
5141
5258
  if (isGenerating) return;
5142
5259
  isGenerating = true;
5143
- console.log(`
5260
+ logger.log(`
5144
5261
  \u{1F4DD} Detected change in ${path5}`);
5145
5262
  try {
5146
5263
  await generateCommand({ verbose: false });
5147
- console.log("\u2705 Regenerated configuration files");
5264
+ logger.success("Regenerated configuration files");
5148
5265
  } catch (error) {
5149
- console.error("\u274C Failed to regenerate:", error);
5266
+ logger.error("Failed to regenerate:", error);
5150
5267
  } finally {
5151
5268
  isGenerating = false;
5152
5269
  }
5153
5270
  };
5154
5271
  watcher.on("change", handleChange).on("add", handleChange).on("unlink", (path5) => {
5155
- console.log(`
5272
+ logger.log(`
5156
5273
  \u{1F5D1}\uFE0F Removed ${path5}`);
5157
5274
  handleChange(path5);
5158
5275
  }).on("error", (error) => {
5159
- console.error("\u274C Watcher error:", error);
5276
+ logger.error("Watcher error:", error);
5160
5277
  });
5161
5278
  process.on("SIGINT", () => {
5162
- console.log("\n\n\u{1F44B} Stopping watcher...");
5279
+ logger.log("\n\n\u{1F44B} Stopping watcher...");
5163
5280
  watcher.close();
5164
5281
  process.exit(0);
5165
5282
  });
@@ -5167,11 +5284,11 @@ async function watchCommand() {
5167
5284
 
5168
5285
  // src/cli/index.ts
5169
5286
  var program = new import_commander.Command();
5170
- program.name("rulesync").description("Unified AI rules management CLI tool").version("0.61.0");
5171
- program.command("init").description("Initialize rulesync in current directory").action(initCommand);
5172
- program.command("add <filename>").description("Add a new rule file").action(addCommand);
5287
+ program.name("rulesync").description("Unified AI rules management CLI tool").version("0.63.0");
5288
+ program.command("init").description("Initialize rulesync in current directory").option("--legacy", "Use legacy file location (.rulesync/*.md instead of .rulesync/rules/*.md)").action(initCommand);
5289
+ 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);
5173
5290
  program.command("gitignore").description("Add generated files to .gitignore").action(gitignoreCommand);
5174
- program.command("import").description("Import configurations from AI tools to rulesync format").option("--augmentcode", "Import from AugmentCode (.augment/rules/)").option("--augmentcode-legacy", "Import from AugmentCode legacy format (.augment-guidelines)").option("--claudecode", "Import from Claude Code (CLAUDE.md)").option("--cursor", "Import from Cursor (.cursorrules)").option("--copilot", "Import from GitHub Copilot (.github/copilot-instructions.md)").option("--cline", "Import from Cline (.cline/instructions.md)").option("--roo", "Import from Roo Code (.roo/instructions.md)").option("--geminicli", "Import from Gemini CLI (GEMINI.md)").option("--junie", "Import from JetBrains Junie (.junie/guidelines.md)").option("-v, --verbose", "Verbose output").action(importCommand);
5291
+ program.command("import").description("Import configurations from AI tools to rulesync format").option("--augmentcode", "Import from AugmentCode (.augment/rules/)").option("--augmentcode-legacy", "Import from AugmentCode legacy format (.augment-guidelines)").option("--claudecode", "Import from Claude Code (CLAUDE.md)").option("--cursor", "Import from Cursor (.cursorrules)").option("--copilot", "Import from GitHub Copilot (.github/copilot-instructions.md)").option("--cline", "Import from Cline (.cline/instructions.md)").option("--roo", "Import from Roo Code (.roo/instructions.md)").option("--geminicli", "Import from Gemini CLI (GEMINI.md)").option("--junie", "Import from JetBrains Junie (.junie/guidelines.md)").option("-v, --verbose", "Verbose output").option("--legacy", "Use legacy file location (.rulesync/*.md instead of .rulesync/rules/*.md)").action(importCommand);
5175
5292
  program.command("generate").description("Generate configuration files for AI tools").option("--augmentcode", "Generate only for AugmentCode").option("--augmentcode-legacy", "Generate only for AugmentCode legacy format").option("--copilot", "Generate only for GitHub Copilot").option("--cursor", "Generate only for Cursor").option("--cline", "Generate only for Cline").option("--codexcli", "Generate only for OpenAI Codex CLI").option("--claudecode", "Generate only for Claude Code").option("--roo", "Generate only for Roo Code").option("--geminicli", "Generate only for Gemini CLI").option("--junie", "Generate only for JetBrains Junie").option("--kiro", "Generate only for Kiro IDE").option("--windsurf", "Generate only for Windsurf").option("--delete", "Delete all existing files in output directories before generating").option(
5176
5293
  "-b, --base-dir <paths>",
5177
5294
  "Base directories to generate files (comma-separated for multiple paths)"