rulesync 0.61.0 → 0.62.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_mini3, 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_mini3 = 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_mini3.z.enum(ALL_TOOL_TARGETS);
58
+ ToolTargetsSchema = import_mini3.z.array(ToolTargetSchema);
59
+ WildcardTargetSchema = import_mini3.z.tuple([import_mini3.z.literal("*")]);
60
+ RulesyncTargetsSchema = import_mini3.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,92 +1056,25 @@ 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
1074
  // src/types/commands.ts
1141
- var import_mini3 = require("zod/mini");
1142
- var CommandFrontmatterSchema = import_mini3.z.object({
1143
- description: import_mini3.z.optional(import_mini3.z.string())
1075
+ var import_mini2 = require("zod/mini");
1076
+ var CommandFrontmatterSchema = import_mini2.z.object({
1077
+ description: import_mini2.z.optional(import_mini2.z.string())
1144
1078
  });
1145
1079
 
1146
1080
  // src/types/config.ts
@@ -1152,7 +1086,8 @@ var ConfigSchema = import_mini4.z.object({
1152
1086
  watchEnabled: import_mini4.z.boolean(),
1153
1087
  defaultTargets: ToolTargetsSchema,
1154
1088
  claudecodeCommands: import_mini4.z.optional(import_mini4.z.string()),
1155
- geminicliCommands: import_mini4.z.optional(import_mini4.z.string())
1089
+ geminicliCommands: import_mini4.z.optional(import_mini4.z.string()),
1090
+ legacy: import_mini4.z.optional(import_mini4.z.boolean())
1156
1091
  });
1157
1092
 
1158
1093
  // src/types/config-options.ts
@@ -1182,6 +1117,7 @@ var ConfigOptionsSchema = import_mini5.z.object({
1182
1117
  verbose: import_mini5.z.optional(import_mini5.z.boolean()),
1183
1118
  delete: import_mini5.z.optional(import_mini5.z.boolean()),
1184
1119
  baseDir: import_mini5.z.optional(import_mini5.z.union([import_mini5.z.string(), import_mini5.z.array(import_mini5.z.string())])),
1120
+ legacy: import_mini5.z.optional(import_mini5.z.boolean()),
1185
1121
  watch: import_mini5.z.optional(
1186
1122
  import_mini5.z.object({
1187
1123
  enabled: import_mini5.z.optional(import_mini5.z.boolean()),
@@ -1201,6 +1137,7 @@ var MergedConfigSchema = import_mini5.z.object({
1201
1137
  delete: import_mini5.z.optional(import_mini5.z.boolean()),
1202
1138
  baseDir: import_mini5.z.optional(import_mini5.z.union([import_mini5.z.string(), import_mini5.z.array(import_mini5.z.string())])),
1203
1139
  configPath: import_mini5.z.optional(import_mini5.z.string()),
1140
+ legacy: import_mini5.z.optional(import_mini5.z.boolean()),
1204
1141
  watch: import_mini5.z.optional(
1205
1142
  import_mini5.z.object({
1206
1143
  enabled: import_mini5.z.optional(import_mini5.z.boolean()),
@@ -1270,9 +1207,39 @@ var GenerateOptionsSchema = import_mini7.z.object({
1270
1207
  // src/types/index.ts
1271
1208
  init_tool_targets();
1272
1209
 
1210
+ // src/utils/config.ts
1211
+ init_tool_targets();
1212
+ function getDefaultConfig() {
1213
+ return {
1214
+ aiRulesDir: ".rulesync",
1215
+ outputPaths: {
1216
+ augmentcode: ".",
1217
+ "augmentcode-legacy": ".",
1218
+ copilot: ".github/instructions",
1219
+ cursor: ".cursor/rules",
1220
+ cline: ".clinerules",
1221
+ claudecode: ".",
1222
+ codexcli: ".",
1223
+ roo: ".roo/rules",
1224
+ geminicli: ".gemini/memories",
1225
+ kiro: ".kiro/steering",
1226
+ junie: ".",
1227
+ windsurf: "."
1228
+ },
1229
+ watchEnabled: false,
1230
+ defaultTargets: ALL_TOOL_TARGETS.filter((tool) => tool !== "augmentcode-legacy"),
1231
+ legacy: false
1232
+ };
1233
+ }
1234
+ function resolveTargets(targets, config) {
1235
+ if (targets.length === 1 && targets[0] === "*") {
1236
+ return config.defaultTargets;
1237
+ }
1238
+ const validatedTargets = ToolTargetsSchema.parse(targets);
1239
+ return validatedTargets;
1240
+ }
1241
+
1273
1242
  // src/utils/config-loader.ts
1274
- var import_c12 = require("c12");
1275
- var import_core = require("zod/v4/core");
1276
1243
  var MODULE_NAME = "rulesync";
1277
1244
  async function loadConfig(options = {}) {
1278
1245
  const defaultConfig = getDefaultConfig();
@@ -1459,6 +1426,49 @@ function mergeWithCliOptions(config, cliOptions) {
1459
1426
  return merged;
1460
1427
  }
1461
1428
 
1429
+ // src/cli/commands/add.ts
1430
+ function sanitizeFilename(filename) {
1431
+ return filename.endsWith(".md") ? filename.slice(0, -3) : filename;
1432
+ }
1433
+ function generateRuleTemplate(filename) {
1434
+ return `---
1435
+ root: false
1436
+ targets: ["*"]
1437
+ description: "Rules for ${filename}"
1438
+ globs: []
1439
+ ---
1440
+
1441
+ # ${filename.charAt(0).toUpperCase() + filename.slice(1)} Rules
1442
+
1443
+ Add your rules here.
1444
+ `;
1445
+ }
1446
+ async function addCommand(filename, options = {}) {
1447
+ try {
1448
+ const configResult = await loadConfig();
1449
+ const config = configResult.config;
1450
+ const sanitizedFilename = sanitizeFilename(filename);
1451
+ const aiRulesDir = config.aiRulesDir;
1452
+ const useLegacy = options.legacy ?? config.legacy ?? false;
1453
+ const rulesDir = useLegacy ? aiRulesDir : path.join(aiRulesDir, "rules");
1454
+ const filePath = path.join(rulesDir, `${sanitizedFilename}.md`);
1455
+ await (0, import_promises.mkdir)(rulesDir, { recursive: true });
1456
+ const template = generateRuleTemplate(sanitizedFilename);
1457
+ await (0, import_promises.writeFile)(filePath, template, "utf8");
1458
+ console.log(`\u2705 Created rule file: ${filePath}`);
1459
+ console.log(`\u{1F4DD} Edit the file to customize your rules.`);
1460
+ } catch (error) {
1461
+ console.error(
1462
+ `\u274C Failed to create rule file: ${error instanceof Error ? error.message : String(error)}`
1463
+ );
1464
+ process.exit(3);
1465
+ }
1466
+ }
1467
+
1468
+ // src/cli/commands/config.ts
1469
+ var import_node_fs = require("fs");
1470
+ var import_node_path2 = __toESM(require("path"), 1);
1471
+
1462
1472
  // src/utils/error.ts
1463
1473
  function getErrorMessage(error) {
1464
1474
  return error instanceof Error ? error.message : String(error);
@@ -1525,6 +1535,19 @@ async function findFiles(dir, extension = ".md") {
1525
1535
  return [];
1526
1536
  }
1527
1537
  }
1538
+ async function findRuleFiles(aiRulesDir) {
1539
+ const rulesDir = (0, import_node_path.join)(aiRulesDir, "rules");
1540
+ const newLocationFiles = await findFiles(rulesDir, ".md");
1541
+ const legacyLocationFiles = await findFiles(aiRulesDir, ".md");
1542
+ const newLocationBasenames = new Set(
1543
+ newLocationFiles.map((file) => file.split("/").pop()?.replace(/\.md$/, ""))
1544
+ );
1545
+ const filteredLegacyFiles = legacyLocationFiles.filter((file) => {
1546
+ const basename6 = file.split("/").pop()?.replace(/\.md$/, "");
1547
+ return !newLocationBasenames.has(basename6);
1548
+ });
1549
+ return [...newLocationFiles, ...filteredLegacyFiles];
1550
+ }
1528
1551
  async function removeDirectory(dirPath) {
1529
1552
  const dangerousPaths = [".", "/", "~", "src", "node_modules"];
1530
1553
  if (dangerousPaths.includes(dirPath) || dirPath === "") {
@@ -1559,6 +1582,50 @@ async function removeClaudeGeneratedFiles() {
1559
1582
  }
1560
1583
  }
1561
1584
 
1585
+ // src/utils/logger.ts
1586
+ var import_consola = require("consola");
1587
+ var Logger = class {
1588
+ _verbose = false;
1589
+ console = import_consola.consola.withDefaults({
1590
+ tag: "rulesync"
1591
+ });
1592
+ setVerbose(verbose) {
1593
+ this._verbose = verbose;
1594
+ }
1595
+ get verbose() {
1596
+ return this._verbose;
1597
+ }
1598
+ // Regular log (always shown, regardless of verbose)
1599
+ log(message, ...args) {
1600
+ this.console.log(message, ...args);
1601
+ }
1602
+ // Info level (shown only in verbose mode)
1603
+ info(message, ...args) {
1604
+ if (this._verbose) {
1605
+ this.console.info(message, ...args);
1606
+ }
1607
+ }
1608
+ // Success (always shown)
1609
+ success(message, ...args) {
1610
+ this.console.success(message, ...args);
1611
+ }
1612
+ // Warning (always shown)
1613
+ warn(message, ...args) {
1614
+ this.console.warn(message, ...args);
1615
+ }
1616
+ // Error (always shown)
1617
+ error(message, ...args) {
1618
+ this.console.error(message, ...args);
1619
+ }
1620
+ // Debug level (shown only in verbose mode)
1621
+ debug(message, ...args) {
1622
+ if (this._verbose) {
1623
+ this.console.debug(message, ...args);
1624
+ }
1625
+ }
1626
+ };
1627
+ var logger = new Logger();
1628
+
1562
1629
  // src/cli/commands/config.ts
1563
1630
  async function configCommand(options = {}) {
1564
1631
  if (options.init) {
@@ -1568,50 +1635,50 @@ async function configCommand(options = {}) {
1568
1635
  await showConfig();
1569
1636
  }
1570
1637
  async function showConfig() {
1571
- console.log("Loading configuration...\n");
1638
+ logger.log("Loading configuration...\n");
1572
1639
  try {
1573
1640
  const result = await loadConfig();
1574
1641
  if (result.isEmpty) {
1575
- console.log("No configuration file found. Using default configuration.\n");
1642
+ logger.log("No configuration file found. Using default configuration.\n");
1576
1643
  } else {
1577
- console.log(`Configuration loaded from: ${result.filepath}
1644
+ logger.log(`Configuration loaded from: ${result.filepath}
1578
1645
  `);
1579
1646
  }
1580
- console.log("Current configuration:");
1581
- console.log("=====================");
1647
+ logger.log("Current configuration:");
1648
+ logger.log("=====================");
1582
1649
  const config = result.config;
1583
- console.log(`
1650
+ logger.log(`
1584
1651
  AI Rules Directory: ${config.aiRulesDir}`);
1585
- console.log(`
1652
+ logger.log(`
1586
1653
  Default Targets: ${config.defaultTargets.join(", ")}`);
1587
1654
  if (config.exclude && config.exclude.length > 0) {
1588
- console.log(`Excluded Targets: ${config.exclude.join(", ")}`);
1655
+ logger.log(`Excluded Targets: ${config.exclude.join(", ")}`);
1589
1656
  }
1590
- console.log("\nOutput Paths:");
1657
+ logger.log("\nOutput Paths:");
1591
1658
  for (const [tool, outputPath] of Object.entries(config.outputPaths)) {
1592
- console.log(` ${tool}: ${outputPath}`);
1659
+ logger.log(` ${tool}: ${outputPath}`);
1593
1660
  }
1594
1661
  if (config.baseDir) {
1595
1662
  const dirs = Array.isArray(config.baseDir) ? config.baseDir : [config.baseDir];
1596
- console.log(`
1663
+ logger.log(`
1597
1664
  Base Directories: ${dirs.join(", ")}`);
1598
1665
  }
1599
- console.log(`
1666
+ logger.log(`
1600
1667
  Verbose: ${config.verbose || false}`);
1601
- console.log(`Delete before generate: ${config.delete || false}`);
1668
+ logger.log(`Delete before generate: ${config.delete || false}`);
1602
1669
  if (config.watch) {
1603
- console.log("\nWatch Configuration:");
1604
- console.log(` Enabled: ${config.watch.enabled || false}`);
1670
+ logger.log("\nWatch Configuration:");
1671
+ logger.log(` Enabled: ${config.watch.enabled || false}`);
1605
1672
  if (config.watch.interval) {
1606
- console.log(` Interval: ${config.watch.interval}ms`);
1673
+ logger.log(` Interval: ${config.watch.interval}ms`);
1607
1674
  }
1608
1675
  if (config.watch.ignore && config.watch.ignore.length > 0) {
1609
- console.log(` Ignore patterns: ${config.watch.ignore.join(", ")}`);
1676
+ logger.log(` Ignore patterns: ${config.watch.ignore.join(", ")}`);
1610
1677
  }
1611
1678
  }
1612
- console.log("\nTip: Use 'rulesync config init' to create a configuration file.");
1679
+ logger.log("\nTip: Use 'rulesync config init' to create a configuration file.");
1613
1680
  } catch (error) {
1614
- console.error(
1681
+ logger.error(
1615
1682
  "\u274C Failed to load configuration:",
1616
1683
  error instanceof Error ? error.message : String(error)
1617
1684
  );
@@ -1632,7 +1699,7 @@ async function initConfig(options) {
1632
1699
  const validFormats = Object.keys(FORMAT_CONFIG);
1633
1700
  const selectedFormat = options.format || "jsonc";
1634
1701
  if (!validFormats.includes(selectedFormat)) {
1635
- console.error(
1702
+ logger.error(
1636
1703
  `\u274C Invalid format: ${selectedFormat}. Valid formats are: ${validFormats.join(", ")}`
1637
1704
  );
1638
1705
  process.exit(1);
@@ -1648,7 +1715,7 @@ async function initConfig(options) {
1648
1715
  if (result.success) {
1649
1716
  validTargets.push(result.data);
1650
1717
  } else {
1651
- console.error(`\u274C Invalid target: ${target}`);
1718
+ logger.error(`\u274C Invalid target: ${target}`);
1652
1719
  process.exit(1);
1653
1720
  }
1654
1721
  }
@@ -1662,7 +1729,7 @@ async function initConfig(options) {
1662
1729
  if (result.success) {
1663
1730
  validExcludes.push(result.data);
1664
1731
  } else {
1665
- console.error(`\u274C Invalid exclude target: ${exclude}`);
1732
+ logger.error(`\u274C Invalid exclude target: ${exclude}`);
1666
1733
  process.exit(1);
1667
1734
  }
1668
1735
  }
@@ -1685,18 +1752,18 @@ async function initConfig(options) {
1685
1752
  try {
1686
1753
  const fs2 = await import("fs/promises");
1687
1754
  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.");
1755
+ logger.error(`\u274C Configuration file already exists: ${filepath}`);
1756
+ logger.log("Remove the existing file or choose a different format.");
1690
1757
  process.exit(1);
1691
1758
  } catch {
1692
1759
  }
1693
1760
  try {
1694
1761
  (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.");
1762
+ logger.success(`Created configuration file: ${filepath}`);
1763
+ logger.log("\nYou can now customize the configuration to fit your needs.");
1764
+ logger.log("Run 'rulesync generate' to use the new configuration.");
1698
1765
  } catch (error) {
1699
- console.error(
1766
+ logger.error(
1700
1767
  `\u274C Failed to create configuration file: ${error instanceof Error ? error.message : String(error)}`
1701
1768
  );
1702
1769
  process.exit(1);
@@ -1829,12 +1896,6 @@ var GeminiCliCommandGenerator = class {
1829
1896
  let converted = content;
1830
1897
  converted = converted.replace(/\$ARGUMENTS/g, "{{args}}");
1831
1898
  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
1899
  return converted.trim();
1839
1900
  }
1840
1901
  escapeTomlString(str) {
@@ -3429,7 +3490,7 @@ var import_node_path14 = require("path");
3429
3490
  var import_gray_matter2 = __toESM(require("gray-matter"), 1);
3430
3491
  async function parseRulesFromDirectory(aiRulesDir) {
3431
3492
  const ignorePatterns = await loadIgnorePatterns();
3432
- const allRuleFiles = await findFiles(aiRulesDir, ".md");
3493
+ const allRuleFiles = await findRuleFiles(aiRulesDir);
3433
3494
  const ruleFiles = filterIgnoredFiles(allRuleFiles, ignorePatterns.patterns);
3434
3495
  const rules = [];
3435
3496
  const errors = [];
@@ -3648,6 +3709,7 @@ async function generateCommand(options = {}) {
3648
3709
  ...options.baseDirs !== void 0 && { baseDirs: options.baseDirs }
3649
3710
  };
3650
3711
  const config = mergeWithCliOptions(configResult.config, cliOptions);
3712
+ logger.setVerbose(config.verbose || false);
3651
3713
  if (options.tools && options.tools.length > 0) {
3652
3714
  const configTargets = config.defaultTargets;
3653
3715
  const cliTools = options.tools;
@@ -3656,18 +3718,18 @@ async function generateCommand(options = {}) {
3656
3718
  const notInConfig = cliTools.filter((tool) => !configTargetsSet.has(tool));
3657
3719
  const notInCli = configTargets.filter((tool) => !cliToolsSet.has(tool));
3658
3720
  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(", ")}`);
3721
+ logger.warn("\u26A0\uFE0F Warning: CLI tool selection differs from configuration!");
3722
+ logger.warn(` Config targets: ${configTargets.join(", ")}`);
3723
+ logger.warn(` CLI specified: ${cliTools.join(", ")}`);
3662
3724
  if (notInConfig.length > 0) {
3663
- console.warn(` Tools specified but not in config: ${notInConfig.join(", ")}`);
3725
+ logger.warn(` Tools specified but not in config: ${notInConfig.join(", ")}`);
3664
3726
  }
3665
3727
  if (notInCli.length > 0) {
3666
- console.warn(` Tools in config but not specified: ${notInCli.join(", ")}`);
3728
+ logger.warn(` Tools in config but not specified: ${notInCli.join(", ")}`);
3667
3729
  }
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("");
3730
+ logger.warn("\n The configuration file targets will be used.");
3731
+ logger.warn(" To change targets, update your rulesync config file.");
3732
+ logger.warn("");
3671
3733
  }
3672
3734
  }
3673
3735
  let baseDirs;
@@ -3678,33 +3740,37 @@ async function generateCommand(options = {}) {
3678
3740
  } else {
3679
3741
  baseDirs = [process.cwd()];
3680
3742
  }
3681
- if (config.verbose && configResult.filepath) {
3682
- console.log(`Loaded configuration from: ${configResult.filepath}`);
3683
- }
3684
- console.log("Generating configuration files...");
3743
+ logger.info(`Loaded configuration from: ${configResult.filepath}`);
3744
+ logger.log("Generating configuration files...");
3685
3745
  if (!await fileExists(config.aiRulesDir)) {
3686
- console.error("\u274C .rulesync directory not found. Run 'rulesync init' first.");
3746
+ logger.error("\u274C .rulesync directory not found. Run 'rulesync init' first.");
3687
3747
  process.exit(1);
3688
3748
  }
3689
3749
  try {
3690
- if (config.verbose) {
3691
- console.log(`Parsing rules from ${config.aiRulesDir}...`);
3692
- }
3750
+ logger.info(`Parsing rules from ${config.aiRulesDir}...`);
3693
3751
  const rules = await parseRulesFromDirectory(config.aiRulesDir);
3694
3752
  if (rules.length === 0) {
3695
- console.warn("\u26A0\uFE0F No rules found in .rulesync directory");
3753
+ logger.warn("\u26A0\uFE0F No rules found in .rulesync directory");
3696
3754
  return;
3697
3755
  }
3698
- if (config.verbose) {
3699
- console.log(`Found ${rules.length} rule(s)`);
3700
- console.log(`Base directories: ${baseDirs.join(", ")}`);
3701
- }
3756
+ logger.info(`Found ${rules.length} rule(s)`);
3757
+ logger.info(`Base directories: ${baseDirs.join(", ")}`);
3702
3758
  if (config.delete) {
3703
- if (config.verbose) {
3704
- console.log("Deleting existing output directories...");
3705
- }
3759
+ logger.info("Deleting existing output directories...");
3706
3760
  const targetTools = config.defaultTargets;
3707
3761
  const deleteTasks = [];
3762
+ const commandsDir = (0, import_node_path15.join)(config.aiRulesDir, "commands");
3763
+ const hasCommands = await fileExists(commandsDir);
3764
+ let hasCommandFiles = false;
3765
+ if (hasCommands) {
3766
+ const { readdir: readdir2 } = await import("fs/promises");
3767
+ try {
3768
+ const files = await readdir2(commandsDir);
3769
+ hasCommandFiles = files.some((file) => file.endsWith(".md"));
3770
+ } catch {
3771
+ hasCommandFiles = false;
3772
+ }
3773
+ }
3708
3774
  for (const tool of targetTools) {
3709
3775
  switch (tool) {
3710
3776
  case "augmentcode":
@@ -3726,15 +3792,21 @@ async function generateCommand(options = {}) {
3726
3792
  break;
3727
3793
  case "claudecode":
3728
3794
  deleteTasks.push(removeClaudeGeneratedFiles());
3729
- deleteTasks.push(removeDirectory((0, import_node_path15.join)(".claude", "commands")));
3795
+ if (hasCommandFiles) {
3796
+ deleteTasks.push(removeDirectory((0, import_node_path15.join)(".claude", "commands")));
3797
+ }
3730
3798
  break;
3731
3799
  case "roo":
3732
3800
  deleteTasks.push(removeDirectory(config.outputPaths.roo));
3733
- deleteTasks.push(removeDirectory((0, import_node_path15.join)(".roo", "commands")));
3801
+ if (hasCommandFiles) {
3802
+ deleteTasks.push(removeDirectory((0, import_node_path15.join)(".roo", "commands")));
3803
+ }
3734
3804
  break;
3735
3805
  case "geminicli":
3736
3806
  deleteTasks.push(removeDirectory(config.outputPaths.geminicli));
3737
- deleteTasks.push(removeDirectory((0, import_node_path15.join)(".gemini", "commands")));
3807
+ if (hasCommandFiles) {
3808
+ deleteTasks.push(removeDirectory((0, import_node_path15.join)(".gemini", "commands")));
3809
+ }
3738
3810
  break;
3739
3811
  case "kiro":
3740
3812
  deleteTasks.push(removeDirectory(config.outputPaths.kiro));
@@ -3745,44 +3817,34 @@ async function generateCommand(options = {}) {
3745
3817
  }
3746
3818
  }
3747
3819
  await Promise.all(deleteTasks);
3748
- if (config.verbose) {
3749
- console.log("Deleted existing output directories");
3750
- }
3820
+ logger.info("Deleted existing output directories");
3751
3821
  }
3752
3822
  let totalOutputs = 0;
3753
3823
  for (const baseDir of baseDirs) {
3754
- if (config.verbose) {
3755
- console.log(`
3824
+ logger.info(`
3756
3825
  Generating configurations for base directory: ${baseDir}`);
3757
- }
3758
3826
  const outputs = await generateConfigurations(rules, config, config.defaultTargets, baseDir);
3759
3827
  if (outputs.length === 0) {
3760
- if (config.verbose) {
3761
- console.warn(`\u26A0\uFE0F No configurations generated for ${baseDir}`);
3762
- }
3828
+ logger.warn(`\u26A0\uFE0F No configurations generated for ${baseDir}`);
3763
3829
  continue;
3764
3830
  }
3765
3831
  for (const output of outputs) {
3766
3832
  await writeFileContent(output.filepath, output.content);
3767
- console.log(`\u2705 Generated ${output.tool} configuration: ${output.filepath}`);
3833
+ logger.success(`Generated ${output.tool} configuration: ${output.filepath}`);
3768
3834
  }
3769
3835
  totalOutputs += outputs.length;
3770
3836
  }
3771
3837
  if (totalOutputs === 0) {
3772
- console.warn("\u26A0\uFE0F No configurations generated");
3838
+ logger.warn("\u26A0\uFE0F No configurations generated");
3773
3839
  return;
3774
3840
  }
3775
- if (config.verbose) {
3776
- console.log("\nGenerating MCP configurations...");
3777
- }
3841
+ logger.info("\nGenerating MCP configurations...");
3778
3842
  let totalMcpOutputs = 0;
3779
3843
  for (const baseDir of baseDirs) {
3780
3844
  try {
3781
3845
  const mcpConfig = parseMcpConfig(process.cwd());
3782
3846
  if (!mcpConfig || !mcpConfig.mcpServers || Object.keys(mcpConfig.mcpServers).length === 0) {
3783
- if (config.verbose) {
3784
- console.log(`No MCP configuration found for ${baseDir}`);
3785
- }
3847
+ logger.info(`No MCP configuration found for ${baseDir}`);
3786
3848
  continue;
3787
3849
  }
3788
3850
  const mcpResults = await generateMcpConfigurations(
@@ -3791,27 +3853,21 @@ Generating configurations for base directory: ${baseDir}`);
3791
3853
  config.defaultTargets
3792
3854
  );
3793
3855
  if (mcpResults.length === 0) {
3794
- if (config.verbose) {
3795
- console.log(`No MCP configurations generated for ${baseDir}`);
3796
- }
3856
+ logger.info(`No MCP configurations generated for ${baseDir}`);
3797
3857
  continue;
3798
3858
  }
3799
3859
  for (const result of mcpResults) {
3800
3860
  await writeFileContent(result.filepath, result.content);
3801
- console.log(`\u2705 Generated ${result.tool} MCP configuration: ${result.filepath}`);
3861
+ logger.success(`Generated ${result.tool} MCP configuration: ${result.filepath}`);
3802
3862
  totalMcpOutputs++;
3803
3863
  }
3804
3864
  } 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
- }
3865
+ logger.error(
3866
+ `\u274C Failed to generate MCP configurations: ${error instanceof Error ? error.message : String(error)}`
3867
+ );
3810
3868
  }
3811
3869
  }
3812
- if (config.verbose) {
3813
- console.log("\nGenerating command files...");
3814
- }
3870
+ logger.info("\nGenerating command files...");
3815
3871
  let totalCommandOutputs = 0;
3816
3872
  for (const baseDir of baseDirs) {
3817
3873
  const commandResults = await generateCommands(
@@ -3820,14 +3876,12 @@ Generating configurations for base directory: ${baseDir}`);
3820
3876
  config.defaultTargets
3821
3877
  );
3822
3878
  if (commandResults.length === 0) {
3823
- if (config.verbose) {
3824
- console.log(`No commands found for ${baseDir}`);
3825
- }
3879
+ logger.info(`No commands found for ${baseDir}`);
3826
3880
  continue;
3827
3881
  }
3828
3882
  for (const result of commandResults) {
3829
3883
  await writeFileContent(result.filepath, result.content);
3830
- console.log(`\u2705 Generated ${result.tool} command: ${result.filepath}`);
3884
+ logger.success(`Generated ${result.tool} command: ${result.filepath}`);
3831
3885
  totalCommandOutputs++;
3832
3886
  }
3833
3887
  }
@@ -3837,13 +3891,13 @@ Generating configurations for base directory: ${baseDir}`);
3837
3891
  if (totalOutputs > 0) parts.push(`${totalOutputs} configurations`);
3838
3892
  if (totalMcpOutputs > 0) parts.push(`${totalMcpOutputs} MCP configurations`);
3839
3893
  if (totalCommandOutputs > 0) parts.push(`${totalCommandOutputs} commands`);
3840
- console.log(
3894
+ logger.success(
3841
3895
  `
3842
3896
  \u{1F389} All done! Generated ${totalGenerated} file(s) total (${parts.join(" + ")})`
3843
3897
  );
3844
3898
  }
3845
3899
  } catch (error) {
3846
- console.error("\u274C Failed to generate configurations:", error);
3900
+ logger.error("\u274C Failed to generate configurations:", error);
3847
3901
  process.exit(1);
3848
3902
  }
3849
3903
  }
@@ -4750,7 +4804,13 @@ var import_gray_matter6 = __toESM(require("gray-matter"), 1);
4750
4804
 
4751
4805
  // src/core/importer.ts
4752
4806
  async function importConfiguration(options) {
4753
- const { tool, baseDir = process.cwd(), rulesDir = ".rulesync", verbose = false } = options;
4807
+ const {
4808
+ tool,
4809
+ baseDir = process.cwd(),
4810
+ rulesDir = ".rulesync",
4811
+ verbose = false,
4812
+ useLegacyLocation = false
4813
+ } = options;
4754
4814
  const errors = [];
4755
4815
  let rules = [];
4756
4816
  let ignorePatterns;
@@ -4850,6 +4910,12 @@ async function importConfiguration(options) {
4850
4910
  targetDir = (0, import_node_path23.join)(rulesDirPath, "commands");
4851
4911
  const { mkdir: mkdir3 } = await import("fs/promises");
4852
4912
  await mkdir3(targetDir, { recursive: true });
4913
+ } else {
4914
+ if (!useLegacyLocation) {
4915
+ targetDir = (0, import_node_path23.join)(rulesDirPath, "rules");
4916
+ const { mkdir: mkdir3 } = await import("fs/promises");
4917
+ await mkdir3(targetDir, { recursive: true });
4918
+ }
4853
4919
  }
4854
4920
  const filePath = (0, import_node_path23.join)(targetDir, `${baseFilename}.md`);
4855
4921
  const content = generateRuleFileContent(rule);
@@ -4918,6 +4984,7 @@ function generateRuleFileContent(rule) {
4918
4984
 
4919
4985
  // src/cli/commands/import.ts
4920
4986
  async function importCommand(options = {}) {
4987
+ logger.setVerbose(options.verbose || false);
4921
4988
  const tools = [];
4922
4989
  if (options.augmentcode) tools.push("augmentcode");
4923
4990
  if (options["augmentcode-legacy"]) tools.push("augmentcode-legacy");
@@ -4928,66 +4995,74 @@ async function importCommand(options = {}) {
4928
4995
  if (options.roo) tools.push("roo");
4929
4996
  if (options.geminicli) tools.push("geminicli");
4930
4997
  if (tools.length === 0) {
4931
- console.error(
4998
+ logger.error(
4932
4999
  "\u274C Please specify one tool to import from (--augmentcode, --augmentcode-legacy, --claudecode, --cursor, --copilot, --cline, --roo, --geminicli)"
4933
5000
  );
4934
5001
  process.exit(1);
4935
5002
  }
4936
5003
  if (tools.length > 1) {
4937
- console.error(
5004
+ logger.error(
4938
5005
  "\u274C Only one tool can be specified at a time. Please run the import command separately for each tool."
4939
5006
  );
4940
5007
  process.exit(1);
4941
5008
  }
4942
5009
  const tool = tools[0];
4943
5010
  if (!tool) {
4944
- console.error("Error: No tool specified");
5011
+ logger.error("Error: No tool specified");
4945
5012
  process.exit(1);
4946
5013
  }
4947
- console.log(`Importing configuration files from ${tool}...`);
5014
+ logger.log(`Importing configuration files from ${tool}...`);
4948
5015
  try {
4949
5016
  const result = await importConfiguration({
4950
5017
  tool,
4951
- verbose: options.verbose ?? false
5018
+ verbose: options.verbose ?? false,
5019
+ useLegacyLocation: options.legacy ?? false
4952
5020
  });
4953
5021
  if (result.success) {
4954
- console.log(`\u2705 Imported ${result.rulesCreated} rule(s) from ${tool}`);
5022
+ logger.success(`Imported ${result.rulesCreated} rule(s) from ${tool}`);
4955
5023
  if (result.ignoreFileCreated) {
4956
- console.log("\u2705 Created .rulesyncignore file from ignore patterns");
5024
+ logger.success("Created .rulesyncignore file from ignore patterns");
4957
5025
  }
4958
5026
  if (result.mcpFileCreated) {
4959
- console.log("\u2705 Created .rulesync/.mcp.json file from MCP configuration");
5027
+ logger.success("Created .rulesync/.mcp.json file from MCP configuration");
4960
5028
  }
4961
- console.log("You can now run 'rulesync generate' to create tool-specific configurations.");
5029
+ logger.log("You can now run 'rulesync generate' to create tool-specific configurations.");
4962
5030
  } 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:");
5031
+ logger.warn(`\u26A0\uFE0F Failed to import from ${tool}: ${result.errors[0]}`);
5032
+ if (result.errors.length > 1) {
5033
+ logger.info("\nDetailed errors:");
4966
5034
  for (const error of result.errors) {
4967
- console.log(` - ${error}`);
5035
+ logger.info(` - ${error}`);
4968
5036
  }
4969
5037
  }
4970
5038
  }
4971
5039
  } catch (error) {
4972
5040
  const errorMessage = error instanceof Error ? error.message : String(error);
4973
- console.error(`\u274C Error importing from ${tool}: ${errorMessage}`);
5041
+ logger.error(`\u274C Error importing from ${tool}: ${errorMessage}`);
4974
5042
  process.exit(1);
4975
5043
  }
4976
5044
  }
4977
5045
 
4978
5046
  // src/cli/commands/init.ts
4979
5047
  var import_node_path24 = require("path");
4980
- async function initCommand() {
4981
- const aiRulesDir = ".rulesync";
5048
+ async function initCommand(options = {}) {
5049
+ const configResult = await loadConfig();
5050
+ const config = configResult.config;
5051
+ const aiRulesDir = config.aiRulesDir;
4982
5052
  console.log("Initializing rulesync...");
4983
5053
  await ensureDir(aiRulesDir);
4984
- await createSampleFiles(aiRulesDir);
5054
+ const useLegacy = options.legacy ?? config.legacy ?? false;
5055
+ const rulesDir = useLegacy ? aiRulesDir : (0, import_node_path24.join)(aiRulesDir, "rules");
5056
+ if (!useLegacy) {
5057
+ await ensureDir(rulesDir);
5058
+ }
5059
+ await createSampleFiles(rulesDir);
4985
5060
  console.log("\u2705 rulesync initialized successfully!");
4986
5061
  console.log("\nNext steps:");
4987
- console.log("1. Edit rule files in .rulesync/");
5062
+ console.log(`1. Edit rule files in ${rulesDir}/`);
4988
5063
  console.log("2. Run 'rulesync generate' to create configuration files");
4989
5064
  }
4990
- async function createSampleFiles(aiRulesDir) {
5065
+ async function createSampleFiles(rulesDir) {
4991
5066
  const sampleFile = {
4992
5067
  filename: "overview.md",
4993
5068
  content: `---
@@ -5023,7 +5098,7 @@ globs: ["**/*"]
5023
5098
  - Follow single responsibility principle
5024
5099
  `
5025
5100
  };
5026
- const filepath = (0, import_node_path24.join)(aiRulesDir, sampleFile.filename);
5101
+ const filepath = (0, import_node_path24.join)(rulesDir, sampleFile.filename);
5027
5102
  if (!await fileExists(filepath)) {
5028
5103
  await writeFileContent(filepath, sampleFile.content);
5029
5104
  console.log(`Created ${filepath}`);
@@ -5167,11 +5242,11 @@ async function watchCommand() {
5167
5242
 
5168
5243
  // src/cli/index.ts
5169
5244
  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);
5245
+ program.name("rulesync").description("Unified AI rules management CLI tool").version("0.62.0");
5246
+ program.command("init").description("Initialize rulesync in current directory").option("--legacy", "Use legacy file location (.rulesync/*.md instead of .rulesync/rules/*.md)").action(initCommand);
5247
+ 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
5248
  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);
5249
+ 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
5250
  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
5251
  "-b, --base-dir <paths>",
5177
5252
  "Base directories to generate files (comma-separated for multiple paths)"