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.js CHANGED
@@ -1,16 +1,16 @@
1
1
  #!/usr/bin/env node
2
- import "./chunk-U63N3YDS.js";
2
+ import "./chunk-M7NL7G7A.js";
3
3
  import "./chunk-LXTA7DBA.js";
4
4
  import "./chunk-PCATT4UZ.js";
5
- import "./chunk-ICMPPX55.js";
5
+ import "./chunk-NETSYSMD.js";
6
6
  import "./chunk-YTU3SCQO.js";
7
- import "./chunk-3ZLMXJTX.js";
8
- import "./chunk-74TYZWHJ.js";
9
- import "./chunk-C5LFJFPS.js";
7
+ import "./chunk-U4PLVMCG.js";
8
+ import "./chunk-UEAYL4NT.js";
9
+ import "./chunk-2CW2KFB3.js";
10
10
  import "./chunk-KUGTKMNW.js";
11
- import "./chunk-DA3XULAD.js";
12
- import "./chunk-LRYVNLH5.js";
13
- import "./chunk-2KYIOT5S.js";
11
+ import "./chunk-4PSTOKKD.js";
12
+ import "./chunk-MDYDKNXQ.js";
13
+ import "./chunk-GQTMTBX4.js";
14
14
  import {
15
15
  ALL_TOOL_TARGETS,
16
16
  RulesyncTargetsSchema,
@@ -26,75 +26,9 @@ import { Command } from "commander";
26
26
  import { mkdir, writeFile } from "fs/promises";
27
27
  import * as path from "path";
28
28
 
29
- // src/utils/config.ts
30
- function getDefaultConfig() {
31
- return {
32
- aiRulesDir: ".rulesync",
33
- outputPaths: {
34
- augmentcode: ".",
35
- "augmentcode-legacy": ".",
36
- copilot: ".github/instructions",
37
- cursor: ".cursor/rules",
38
- cline: ".clinerules",
39
- claudecode: ".",
40
- codexcli: ".",
41
- roo: ".roo/rules",
42
- geminicli: ".gemini/memories",
43
- kiro: ".kiro/steering",
44
- junie: ".",
45
- windsurf: "."
46
- },
47
- watchEnabled: false,
48
- defaultTargets: ALL_TOOL_TARGETS.filter((tool) => tool !== "augmentcode-legacy")
49
- };
50
- }
51
- function resolveTargets(targets, config) {
52
- if (targets.length === 1 && targets[0] === "*") {
53
- return config.defaultTargets;
54
- }
55
- const validatedTargets = ToolTargetsSchema.parse(targets);
56
- return validatedTargets;
57
- }
58
-
59
- // src/cli/commands/add.ts
60
- function sanitizeFilename(filename) {
61
- return filename.endsWith(".md") ? filename.slice(0, -3) : filename;
62
- }
63
- function generateRuleTemplate(filename) {
64
- return `---
65
- root: false
66
- targets: ["*"]
67
- description: "Rules for ${filename}"
68
- globs: []
69
- ---
70
-
71
- # ${filename.charAt(0).toUpperCase() + filename.slice(1)} Rules
72
-
73
- Add your rules here.
74
- `;
75
- }
76
- async function addCommand(filename) {
77
- try {
78
- const config = getDefaultConfig();
79
- const sanitizedFilename = sanitizeFilename(filename);
80
- const rulesDir = config.aiRulesDir;
81
- const filePath = path.join(rulesDir, `${sanitizedFilename}.md`);
82
- await mkdir(rulesDir, { recursive: true });
83
- const template = generateRuleTemplate(sanitizedFilename);
84
- await writeFile(filePath, template, "utf8");
85
- console.log(`\u2705 Created rule file: ${filePath}`);
86
- console.log(`\u{1F4DD} Edit the file to customize your rules.`);
87
- } catch (error) {
88
- console.error(
89
- `\u274C Failed to create rule file: ${error instanceof Error ? error.message : String(error)}`
90
- );
91
- process.exit(3);
92
- }
93
- }
94
-
95
- // src/cli/commands/config.ts
96
- import { writeFileSync } from "fs";
97
- import path2 from "path";
29
+ // src/utils/config-loader.ts
30
+ import { loadConfig as loadC12Config } from "c12";
31
+ import { $ZodError } from "zod/v4/core";
98
32
 
99
33
  // src/types/claudecode.ts
100
34
  import { z } from "zod/mini";
@@ -121,7 +55,8 @@ var ConfigSchema = z3.object({
121
55
  watchEnabled: z3.boolean(),
122
56
  defaultTargets: ToolTargetsSchema,
123
57
  claudecodeCommands: z3.optional(z3.string()),
124
- geminicliCommands: z3.optional(z3.string())
58
+ geminicliCommands: z3.optional(z3.string()),
59
+ legacy: z3.optional(z3.boolean())
125
60
  });
126
61
 
127
62
  // src/types/config-options.ts
@@ -150,6 +85,7 @@ var ConfigOptionsSchema = z4.object({
150
85
  verbose: z4.optional(z4.boolean()),
151
86
  delete: z4.optional(z4.boolean()),
152
87
  baseDir: z4.optional(z4.union([z4.string(), z4.array(z4.string())])),
88
+ legacy: z4.optional(z4.boolean()),
153
89
  watch: z4.optional(
154
90
  z4.object({
155
91
  enabled: z4.optional(z4.boolean()),
@@ -169,6 +105,7 @@ var MergedConfigSchema = z4.object({
169
105
  delete: z4.optional(z4.boolean()),
170
106
  baseDir: z4.optional(z4.union([z4.string(), z4.array(z4.string())])),
171
107
  configPath: z4.optional(z4.string()),
108
+ legacy: z4.optional(z4.boolean()),
172
109
  watch: z4.optional(
173
110
  z4.object({
174
111
  enabled: z4.optional(z4.boolean()),
@@ -233,9 +170,38 @@ var GenerateOptionsSchema = z6.object({
233
170
  watch: z6.optional(z6.boolean())
234
171
  });
235
172
 
173
+ // src/utils/config.ts
174
+ function getDefaultConfig() {
175
+ return {
176
+ aiRulesDir: ".rulesync",
177
+ outputPaths: {
178
+ augmentcode: ".",
179
+ "augmentcode-legacy": ".",
180
+ copilot: ".github/instructions",
181
+ cursor: ".cursor/rules",
182
+ cline: ".clinerules",
183
+ claudecode: ".",
184
+ codexcli: ".",
185
+ roo: ".roo/rules",
186
+ geminicli: ".gemini/memories",
187
+ kiro: ".kiro/steering",
188
+ junie: ".",
189
+ windsurf: "."
190
+ },
191
+ watchEnabled: false,
192
+ defaultTargets: ALL_TOOL_TARGETS.filter((tool) => tool !== "augmentcode-legacy"),
193
+ legacy: false
194
+ };
195
+ }
196
+ function resolveTargets(targets, config) {
197
+ if (targets.length === 1 && targets[0] === "*") {
198
+ return config.defaultTargets;
199
+ }
200
+ const validatedTargets = ToolTargetsSchema.parse(targets);
201
+ return validatedTargets;
202
+ }
203
+
236
204
  // src/utils/config-loader.ts
237
- import { loadConfig as loadC12Config } from "c12";
238
- import { $ZodError } from "zod/v4/core";
239
205
  var MODULE_NAME = "rulesync";
240
206
  async function loadConfig(options = {}) {
241
207
  const defaultConfig = getDefaultConfig();
@@ -422,6 +388,49 @@ function mergeWithCliOptions(config, cliOptions) {
422
388
  return merged;
423
389
  }
424
390
 
391
+ // src/cli/commands/add.ts
392
+ function sanitizeFilename(filename) {
393
+ return filename.endsWith(".md") ? filename.slice(0, -3) : filename;
394
+ }
395
+ function generateRuleTemplate(filename) {
396
+ return `---
397
+ root: false
398
+ targets: ["*"]
399
+ description: "Rules for ${filename}"
400
+ globs: []
401
+ ---
402
+
403
+ # ${filename.charAt(0).toUpperCase() + filename.slice(1)} Rules
404
+
405
+ Add your rules here.
406
+ `;
407
+ }
408
+ async function addCommand(filename, options = {}) {
409
+ try {
410
+ const configResult = await loadConfig();
411
+ const config = configResult.config;
412
+ const sanitizedFilename = sanitizeFilename(filename);
413
+ const aiRulesDir = config.aiRulesDir;
414
+ const useLegacy = options.legacy ?? config.legacy ?? false;
415
+ const rulesDir = useLegacy ? aiRulesDir : path.join(aiRulesDir, "rules");
416
+ const filePath = path.join(rulesDir, `${sanitizedFilename}.md`);
417
+ await mkdir(rulesDir, { recursive: true });
418
+ const template = generateRuleTemplate(sanitizedFilename);
419
+ await writeFile(filePath, template, "utf8");
420
+ console.log(`\u2705 Created rule file: ${filePath}`);
421
+ console.log(`\u{1F4DD} Edit the file to customize your rules.`);
422
+ } catch (error) {
423
+ console.error(
424
+ `\u274C Failed to create rule file: ${error instanceof Error ? error.message : String(error)}`
425
+ );
426
+ process.exit(3);
427
+ }
428
+ }
429
+
430
+ // src/cli/commands/config.ts
431
+ import { writeFileSync } from "fs";
432
+ import path2 from "path";
433
+
425
434
  // src/utils/error.ts
426
435
  function getErrorMessage(error) {
427
436
  return error instanceof Error ? error.message : String(error);
@@ -488,6 +497,19 @@ async function findFiles(dir, extension = ".md") {
488
497
  return [];
489
498
  }
490
499
  }
500
+ async function findRuleFiles(aiRulesDir) {
501
+ const rulesDir = join2(aiRulesDir, "rules");
502
+ const newLocationFiles = await findFiles(rulesDir, ".md");
503
+ const legacyLocationFiles = await findFiles(aiRulesDir, ".md");
504
+ const newLocationBasenames = new Set(
505
+ newLocationFiles.map((file) => file.split("/").pop()?.replace(/\.md$/, ""))
506
+ );
507
+ const filteredLegacyFiles = legacyLocationFiles.filter((file) => {
508
+ const basename6 = file.split("/").pop()?.replace(/\.md$/, "");
509
+ return !newLocationBasenames.has(basename6);
510
+ });
511
+ return [...newLocationFiles, ...filteredLegacyFiles];
512
+ }
491
513
  async function removeDirectory(dirPath) {
492
514
  const dangerousPaths = [".", "/", "~", "src", "node_modules"];
493
515
  if (dangerousPaths.includes(dirPath) || dirPath === "") {
@@ -522,6 +544,50 @@ async function removeClaudeGeneratedFiles() {
522
544
  }
523
545
  }
524
546
 
547
+ // src/utils/logger.ts
548
+ import { consola } from "consola";
549
+ var Logger = class {
550
+ _verbose = false;
551
+ console = consola.withDefaults({
552
+ tag: "rulesync"
553
+ });
554
+ setVerbose(verbose) {
555
+ this._verbose = verbose;
556
+ }
557
+ get verbose() {
558
+ return this._verbose;
559
+ }
560
+ // Regular log (always shown, regardless of verbose)
561
+ log(message, ...args) {
562
+ this.console.log(message, ...args);
563
+ }
564
+ // Info level (shown only in verbose mode)
565
+ info(message, ...args) {
566
+ if (this._verbose) {
567
+ this.console.info(message, ...args);
568
+ }
569
+ }
570
+ // Success (always shown)
571
+ success(message, ...args) {
572
+ this.console.success(message, ...args);
573
+ }
574
+ // Warning (always shown)
575
+ warn(message, ...args) {
576
+ this.console.warn(message, ...args);
577
+ }
578
+ // Error (always shown)
579
+ error(message, ...args) {
580
+ this.console.error(message, ...args);
581
+ }
582
+ // Debug level (shown only in verbose mode)
583
+ debug(message, ...args) {
584
+ if (this._verbose) {
585
+ this.console.debug(message, ...args);
586
+ }
587
+ }
588
+ };
589
+ var logger = new Logger();
590
+
525
591
  // src/cli/commands/config.ts
526
592
  async function configCommand(options = {}) {
527
593
  if (options.init) {
@@ -531,50 +597,50 @@ async function configCommand(options = {}) {
531
597
  await showConfig();
532
598
  }
533
599
  async function showConfig() {
534
- console.log("Loading configuration...\n");
600
+ logger.log("Loading configuration...\n");
535
601
  try {
536
602
  const result = await loadConfig();
537
603
  if (result.isEmpty) {
538
- console.log("No configuration file found. Using default configuration.\n");
604
+ logger.log("No configuration file found. Using default configuration.\n");
539
605
  } else {
540
- console.log(`Configuration loaded from: ${result.filepath}
606
+ logger.log(`Configuration loaded from: ${result.filepath}
541
607
  `);
542
608
  }
543
- console.log("Current configuration:");
544
- console.log("=====================");
609
+ logger.log("Current configuration:");
610
+ logger.log("=====================");
545
611
  const config = result.config;
546
- console.log(`
612
+ logger.log(`
547
613
  AI Rules Directory: ${config.aiRulesDir}`);
548
- console.log(`
614
+ logger.log(`
549
615
  Default Targets: ${config.defaultTargets.join(", ")}`);
550
616
  if (config.exclude && config.exclude.length > 0) {
551
- console.log(`Excluded Targets: ${config.exclude.join(", ")}`);
617
+ logger.log(`Excluded Targets: ${config.exclude.join(", ")}`);
552
618
  }
553
- console.log("\nOutput Paths:");
619
+ logger.log("\nOutput Paths:");
554
620
  for (const [tool, outputPath] of Object.entries(config.outputPaths)) {
555
- console.log(` ${tool}: ${outputPath}`);
621
+ logger.log(` ${tool}: ${outputPath}`);
556
622
  }
557
623
  if (config.baseDir) {
558
624
  const dirs = Array.isArray(config.baseDir) ? config.baseDir : [config.baseDir];
559
- console.log(`
625
+ logger.log(`
560
626
  Base Directories: ${dirs.join(", ")}`);
561
627
  }
562
- console.log(`
628
+ logger.log(`
563
629
  Verbose: ${config.verbose || false}`);
564
- console.log(`Delete before generate: ${config.delete || false}`);
630
+ logger.log(`Delete before generate: ${config.delete || false}`);
565
631
  if (config.watch) {
566
- console.log("\nWatch Configuration:");
567
- console.log(` Enabled: ${config.watch.enabled || false}`);
632
+ logger.log("\nWatch Configuration:");
633
+ logger.log(` Enabled: ${config.watch.enabled || false}`);
568
634
  if (config.watch.interval) {
569
- console.log(` Interval: ${config.watch.interval}ms`);
635
+ logger.log(` Interval: ${config.watch.interval}ms`);
570
636
  }
571
637
  if (config.watch.ignore && config.watch.ignore.length > 0) {
572
- console.log(` Ignore patterns: ${config.watch.ignore.join(", ")}`);
638
+ logger.log(` Ignore patterns: ${config.watch.ignore.join(", ")}`);
573
639
  }
574
640
  }
575
- console.log("\nTip: Use 'rulesync config init' to create a configuration file.");
641
+ logger.log("\nTip: Use 'rulesync config init' to create a configuration file.");
576
642
  } catch (error) {
577
- console.error(
643
+ logger.error(
578
644
  "\u274C Failed to load configuration:",
579
645
  error instanceof Error ? error.message : String(error)
580
646
  );
@@ -595,7 +661,7 @@ async function initConfig(options) {
595
661
  const validFormats = Object.keys(FORMAT_CONFIG);
596
662
  const selectedFormat = options.format || "jsonc";
597
663
  if (!validFormats.includes(selectedFormat)) {
598
- console.error(
664
+ logger.error(
599
665
  `\u274C Invalid format: ${selectedFormat}. Valid formats are: ${validFormats.join(", ")}`
600
666
  );
601
667
  process.exit(1);
@@ -611,7 +677,7 @@ async function initConfig(options) {
611
677
  if (result.success) {
612
678
  validTargets.push(result.data);
613
679
  } else {
614
- console.error(`\u274C Invalid target: ${target}`);
680
+ logger.error(`\u274C Invalid target: ${target}`);
615
681
  process.exit(1);
616
682
  }
617
683
  }
@@ -625,7 +691,7 @@ async function initConfig(options) {
625
691
  if (result.success) {
626
692
  validExcludes.push(result.data);
627
693
  } else {
628
- console.error(`\u274C Invalid exclude target: ${exclude}`);
694
+ logger.error(`\u274C Invalid exclude target: ${exclude}`);
629
695
  process.exit(1);
630
696
  }
631
697
  }
@@ -648,18 +714,18 @@ async function initConfig(options) {
648
714
  try {
649
715
  const fs2 = await import("fs/promises");
650
716
  await fs2.access(filepath);
651
- console.error(`\u274C Configuration file already exists: ${filepath}`);
652
- console.log("Remove the existing file or choose a different format.");
717
+ logger.error(`\u274C Configuration file already exists: ${filepath}`);
718
+ logger.log("Remove the existing file or choose a different format.");
653
719
  process.exit(1);
654
720
  } catch {
655
721
  }
656
722
  try {
657
723
  writeFileSync(filepath, content, "utf-8");
658
- console.log(`\u2705 Created configuration file: ${filepath}`);
659
- console.log("\nYou can now customize the configuration to fit your needs.");
660
- console.log("Run 'rulesync generate' to use the new configuration.");
724
+ logger.success(`Created configuration file: ${filepath}`);
725
+ logger.log("\nYou can now customize the configuration to fit your needs.");
726
+ logger.log("Run 'rulesync generate' to use the new configuration.");
661
727
  } catch (error) {
662
- console.error(
728
+ logger.error(
663
729
  `\u274C Failed to create configuration file: ${error instanceof Error ? error.message : String(error)}`
664
730
  );
665
731
  process.exit(1);
@@ -792,12 +858,6 @@ var GeminiCliCommandGenerator = class {
792
858
  let converted = content;
793
859
  converted = converted.replace(/\$ARGUMENTS/g, "{{args}}");
794
860
  converted = converted.replace(/!`([^`]+)`/g, "!{$1}");
795
- const atSyntaxMatches = converted.match(/@[^\s]+/g);
796
- if (atSyntaxMatches) {
797
- console.warn(
798
- `\u26A0\uFE0F Warning: @ syntax found (${atSyntaxMatches.join(", ")}). Gemini CLI does not support file content injection. Consider using shell commands or remove these references.`
799
- );
800
- }
801
861
  return converted.trim();
802
862
  }
803
863
  escapeTomlString(str) {
@@ -2392,7 +2452,7 @@ import { basename as basename2 } from "path";
2392
2452
  import matter2 from "gray-matter";
2393
2453
  async function parseRulesFromDirectory(aiRulesDir) {
2394
2454
  const ignorePatterns = await loadIgnorePatterns();
2395
- const allRuleFiles = await findFiles(aiRulesDir, ".md");
2455
+ const allRuleFiles = await findRuleFiles(aiRulesDir);
2396
2456
  const ruleFiles = filterIgnoredFiles(allRuleFiles, ignorePatterns.patterns);
2397
2457
  const rules = [];
2398
2458
  const errors = [];
@@ -2548,22 +2608,22 @@ async function generateMcpConfigurations(mcpConfig, baseDir, targetTools) {
2548
2608
  servers,
2549
2609
  dir
2550
2610
  ),
2551
- claudecode: async (servers, dir) => (await import("./claudecode-XKHMZT7R.js")).generateClaudeMcpConfiguration(
2611
+ claudecode: async (servers, dir) => (await import("./claudecode-YTEFACCT.js")).generateClaudeMcpConfiguration(
2552
2612
  servers,
2553
2613
  dir
2554
2614
  ),
2555
2615
  copilot: async (servers, dir) => (await import("./copilot-MOR3HHJX.js")).generateCopilotMcpConfiguration(servers, dir),
2556
- cursor: async (servers, dir) => (await import("./cursor-WWHUW5AD.js")).generateCursorMcpConfiguration(servers, dir),
2557
- cline: async (servers, dir) => (await import("./cline-FNWPJ7K4.js")).generateClineMcpConfiguration(servers, dir),
2558
- codexcli: async (servers, dir) => (await import("./codexcli-FDFHY66P.js")).generateCodexMcpConfiguration(servers, dir),
2616
+ cursor: async (servers, dir) => (await import("./cursor-YJGH7W24.js")).generateCursorMcpConfiguration(servers, dir),
2617
+ cline: async (servers, dir) => (await import("./cline-CKNUDEA3.js")).generateClineMcpConfiguration(servers, dir),
2618
+ codexcli: async (servers, dir) => (await import("./codexcli-7SDGYI7D.js")).generateCodexMcpConfiguration(servers, dir),
2559
2619
  roo: async (servers, dir) => (await import("./roo-L3QTTIPO.js")).generateRooMcpConfiguration(servers, dir),
2560
- geminicli: async (servers, dir) => (await import("./geminicli-7TIDQ62D.js")).generateGeminiCliMcpConfiguration(
2620
+ geminicli: async (servers, dir) => (await import("./geminicli-E7KZTZ2G.js")).generateGeminiCliMcpConfiguration(
2561
2621
  servers,
2562
2622
  dir
2563
2623
  ),
2564
2624
  kiro: async (servers, dir) => (await import("./kiro-YDHXY2MA.js")).generateKiroMcpConfiguration(servers, dir),
2565
- junie: async (servers, dir) => (await import("./junie-VMNDWBNB.js")).generateJunieMcpConfiguration(servers, dir),
2566
- windsurf: async (servers, dir) => (await import("./windsurf-KOSK4MZJ.js")).generateWindsurfMcpConfiguration(
2625
+ junie: async (servers, dir) => (await import("./junie-5LEQU4BO.js")).generateJunieMcpConfiguration(servers, dir),
2626
+ windsurf: async (servers, dir) => (await import("./windsurf-4P6HEUBV.js")).generateWindsurfMcpConfiguration(
2567
2627
  servers,
2568
2628
  dir
2569
2629
  )
@@ -2598,6 +2658,7 @@ async function generateCommand(options = {}) {
2598
2658
  ...options.baseDirs !== void 0 && { baseDirs: options.baseDirs }
2599
2659
  };
2600
2660
  const config = mergeWithCliOptions(configResult.config, cliOptions);
2661
+ logger.setVerbose(config.verbose || false);
2601
2662
  if (options.tools && options.tools.length > 0) {
2602
2663
  const configTargets = config.defaultTargets;
2603
2664
  const cliTools = options.tools;
@@ -2606,18 +2667,18 @@ async function generateCommand(options = {}) {
2606
2667
  const notInConfig = cliTools.filter((tool) => !configTargetsSet.has(tool));
2607
2668
  const notInCli = configTargets.filter((tool) => !cliToolsSet.has(tool));
2608
2669
  if (notInConfig.length > 0 || notInCli.length > 0) {
2609
- console.warn("\u26A0\uFE0F Warning: CLI tool selection differs from configuration!");
2610
- console.warn(` Config targets: ${configTargets.join(", ")}`);
2611
- console.warn(` CLI specified: ${cliTools.join(", ")}`);
2670
+ logger.warn("\u26A0\uFE0F Warning: CLI tool selection differs from configuration!");
2671
+ logger.warn(` Config targets: ${configTargets.join(", ")}`);
2672
+ logger.warn(` CLI specified: ${cliTools.join(", ")}`);
2612
2673
  if (notInConfig.length > 0) {
2613
- console.warn(` Tools specified but not in config: ${notInConfig.join(", ")}`);
2674
+ logger.warn(` Tools specified but not in config: ${notInConfig.join(", ")}`);
2614
2675
  }
2615
2676
  if (notInCli.length > 0) {
2616
- console.warn(` Tools in config but not specified: ${notInCli.join(", ")}`);
2677
+ logger.warn(` Tools in config but not specified: ${notInCli.join(", ")}`);
2617
2678
  }
2618
- console.warn("\n The configuration file targets will be used.");
2619
- console.warn(" To change targets, update your rulesync config file.");
2620
- console.warn("");
2679
+ logger.warn("\n The configuration file targets will be used.");
2680
+ logger.warn(" To change targets, update your rulesync config file.");
2681
+ logger.warn("");
2621
2682
  }
2622
2683
  }
2623
2684
  let baseDirs;
@@ -2628,33 +2689,37 @@ async function generateCommand(options = {}) {
2628
2689
  } else {
2629
2690
  baseDirs = [process.cwd()];
2630
2691
  }
2631
- if (config.verbose && configResult.filepath) {
2632
- console.log(`Loaded configuration from: ${configResult.filepath}`);
2633
- }
2634
- console.log("Generating configuration files...");
2692
+ logger.info(`Loaded configuration from: ${configResult.filepath}`);
2693
+ logger.log("Generating configuration files...");
2635
2694
  if (!await fileExists(config.aiRulesDir)) {
2636
- console.error("\u274C .rulesync directory not found. Run 'rulesync init' first.");
2695
+ logger.error("\u274C .rulesync directory not found. Run 'rulesync init' first.");
2637
2696
  process.exit(1);
2638
2697
  }
2639
2698
  try {
2640
- if (config.verbose) {
2641
- console.log(`Parsing rules from ${config.aiRulesDir}...`);
2642
- }
2699
+ logger.info(`Parsing rules from ${config.aiRulesDir}...`);
2643
2700
  const rules = await parseRulesFromDirectory(config.aiRulesDir);
2644
2701
  if (rules.length === 0) {
2645
- console.warn("\u26A0\uFE0F No rules found in .rulesync directory");
2702
+ logger.warn("\u26A0\uFE0F No rules found in .rulesync directory");
2646
2703
  return;
2647
2704
  }
2648
- if (config.verbose) {
2649
- console.log(`Found ${rules.length} rule(s)`);
2650
- console.log(`Base directories: ${baseDirs.join(", ")}`);
2651
- }
2705
+ logger.info(`Found ${rules.length} rule(s)`);
2706
+ logger.info(`Base directories: ${baseDirs.join(", ")}`);
2652
2707
  if (config.delete) {
2653
- if (config.verbose) {
2654
- console.log("Deleting existing output directories...");
2655
- }
2708
+ logger.info("Deleting existing output directories...");
2656
2709
  const targetTools = config.defaultTargets;
2657
2710
  const deleteTasks = [];
2711
+ const commandsDir = join15(config.aiRulesDir, "commands");
2712
+ const hasCommands = await fileExists(commandsDir);
2713
+ let hasCommandFiles = false;
2714
+ if (hasCommands) {
2715
+ const { readdir: readdir2 } = await import("fs/promises");
2716
+ try {
2717
+ const files = await readdir2(commandsDir);
2718
+ hasCommandFiles = files.some((file) => file.endsWith(".md"));
2719
+ } catch {
2720
+ hasCommandFiles = false;
2721
+ }
2722
+ }
2658
2723
  for (const tool of targetTools) {
2659
2724
  switch (tool) {
2660
2725
  case "augmentcode":
@@ -2676,15 +2741,21 @@ async function generateCommand(options = {}) {
2676
2741
  break;
2677
2742
  case "claudecode":
2678
2743
  deleteTasks.push(removeClaudeGeneratedFiles());
2679
- deleteTasks.push(removeDirectory(join15(".claude", "commands")));
2744
+ if (hasCommandFiles) {
2745
+ deleteTasks.push(removeDirectory(join15(".claude", "commands")));
2746
+ }
2680
2747
  break;
2681
2748
  case "roo":
2682
2749
  deleteTasks.push(removeDirectory(config.outputPaths.roo));
2683
- deleteTasks.push(removeDirectory(join15(".roo", "commands")));
2750
+ if (hasCommandFiles) {
2751
+ deleteTasks.push(removeDirectory(join15(".roo", "commands")));
2752
+ }
2684
2753
  break;
2685
2754
  case "geminicli":
2686
2755
  deleteTasks.push(removeDirectory(config.outputPaths.geminicli));
2687
- deleteTasks.push(removeDirectory(join15(".gemini", "commands")));
2756
+ if (hasCommandFiles) {
2757
+ deleteTasks.push(removeDirectory(join15(".gemini", "commands")));
2758
+ }
2688
2759
  break;
2689
2760
  case "kiro":
2690
2761
  deleteTasks.push(removeDirectory(config.outputPaths.kiro));
@@ -2695,44 +2766,34 @@ async function generateCommand(options = {}) {
2695
2766
  }
2696
2767
  }
2697
2768
  await Promise.all(deleteTasks);
2698
- if (config.verbose) {
2699
- console.log("Deleted existing output directories");
2700
- }
2769
+ logger.info("Deleted existing output directories");
2701
2770
  }
2702
2771
  let totalOutputs = 0;
2703
2772
  for (const baseDir of baseDirs) {
2704
- if (config.verbose) {
2705
- console.log(`
2773
+ logger.info(`
2706
2774
  Generating configurations for base directory: ${baseDir}`);
2707
- }
2708
2775
  const outputs = await generateConfigurations(rules, config, config.defaultTargets, baseDir);
2709
2776
  if (outputs.length === 0) {
2710
- if (config.verbose) {
2711
- console.warn(`\u26A0\uFE0F No configurations generated for ${baseDir}`);
2712
- }
2777
+ logger.warn(`\u26A0\uFE0F No configurations generated for ${baseDir}`);
2713
2778
  continue;
2714
2779
  }
2715
2780
  for (const output of outputs) {
2716
2781
  await writeFileContent(output.filepath, output.content);
2717
- console.log(`\u2705 Generated ${output.tool} configuration: ${output.filepath}`);
2782
+ logger.success(`Generated ${output.tool} configuration: ${output.filepath}`);
2718
2783
  }
2719
2784
  totalOutputs += outputs.length;
2720
2785
  }
2721
2786
  if (totalOutputs === 0) {
2722
- console.warn("\u26A0\uFE0F No configurations generated");
2787
+ logger.warn("\u26A0\uFE0F No configurations generated");
2723
2788
  return;
2724
2789
  }
2725
- if (config.verbose) {
2726
- console.log("\nGenerating MCP configurations...");
2727
- }
2790
+ logger.info("\nGenerating MCP configurations...");
2728
2791
  let totalMcpOutputs = 0;
2729
2792
  for (const baseDir of baseDirs) {
2730
2793
  try {
2731
2794
  const mcpConfig = parseMcpConfig(process.cwd());
2732
2795
  if (!mcpConfig || !mcpConfig.mcpServers || Object.keys(mcpConfig.mcpServers).length === 0) {
2733
- if (config.verbose) {
2734
- console.log(`No MCP configuration found for ${baseDir}`);
2735
- }
2796
+ logger.info(`No MCP configuration found for ${baseDir}`);
2736
2797
  continue;
2737
2798
  }
2738
2799
  const mcpResults = await generateMcpConfigurations(
@@ -2741,27 +2802,21 @@ Generating configurations for base directory: ${baseDir}`);
2741
2802
  config.defaultTargets
2742
2803
  );
2743
2804
  if (mcpResults.length === 0) {
2744
- if (config.verbose) {
2745
- console.log(`No MCP configurations generated for ${baseDir}`);
2746
- }
2805
+ logger.info(`No MCP configurations generated for ${baseDir}`);
2747
2806
  continue;
2748
2807
  }
2749
2808
  for (const result of mcpResults) {
2750
2809
  await writeFileContent(result.filepath, result.content);
2751
- console.log(`\u2705 Generated ${result.tool} MCP configuration: ${result.filepath}`);
2810
+ logger.success(`Generated ${result.tool} MCP configuration: ${result.filepath}`);
2752
2811
  totalMcpOutputs++;
2753
2812
  }
2754
2813
  } catch (error) {
2755
- if (config.verbose) {
2756
- console.error(
2757
- `\u274C Failed to generate MCP configurations: ${error instanceof Error ? error.message : String(error)}`
2758
- );
2759
- }
2814
+ logger.error(
2815
+ `\u274C Failed to generate MCP configurations: ${error instanceof Error ? error.message : String(error)}`
2816
+ );
2760
2817
  }
2761
2818
  }
2762
- if (config.verbose) {
2763
- console.log("\nGenerating command files...");
2764
- }
2819
+ logger.info("\nGenerating command files...");
2765
2820
  let totalCommandOutputs = 0;
2766
2821
  for (const baseDir of baseDirs) {
2767
2822
  const commandResults = await generateCommands(
@@ -2770,14 +2825,12 @@ Generating configurations for base directory: ${baseDir}`);
2770
2825
  config.defaultTargets
2771
2826
  );
2772
2827
  if (commandResults.length === 0) {
2773
- if (config.verbose) {
2774
- console.log(`No commands found for ${baseDir}`);
2775
- }
2828
+ logger.info(`No commands found for ${baseDir}`);
2776
2829
  continue;
2777
2830
  }
2778
2831
  for (const result of commandResults) {
2779
2832
  await writeFileContent(result.filepath, result.content);
2780
- console.log(`\u2705 Generated ${result.tool} command: ${result.filepath}`);
2833
+ logger.success(`Generated ${result.tool} command: ${result.filepath}`);
2781
2834
  totalCommandOutputs++;
2782
2835
  }
2783
2836
  }
@@ -2787,13 +2840,13 @@ Generating configurations for base directory: ${baseDir}`);
2787
2840
  if (totalOutputs > 0) parts.push(`${totalOutputs} configurations`);
2788
2841
  if (totalMcpOutputs > 0) parts.push(`${totalMcpOutputs} MCP configurations`);
2789
2842
  if (totalCommandOutputs > 0) parts.push(`${totalCommandOutputs} commands`);
2790
- console.log(
2843
+ logger.success(
2791
2844
  `
2792
2845
  \u{1F389} All done! Generated ${totalGenerated} file(s) total (${parts.join(" + ")})`
2793
2846
  );
2794
2847
  }
2795
2848
  } catch (error) {
2796
- console.error("\u274C Failed to generate configurations:", error);
2849
+ logger.error("\u274C Failed to generate configurations:", error);
2797
2850
  process.exit(1);
2798
2851
  }
2799
2852
  }
@@ -3700,7 +3753,13 @@ import matter6 from "gray-matter";
3700
3753
 
3701
3754
  // src/core/importer.ts
3702
3755
  async function importConfiguration(options) {
3703
- const { tool, baseDir = process.cwd(), rulesDir = ".rulesync", verbose = false } = options;
3756
+ const {
3757
+ tool,
3758
+ baseDir = process.cwd(),
3759
+ rulesDir = ".rulesync",
3760
+ verbose = false,
3761
+ useLegacyLocation = false
3762
+ } = options;
3704
3763
  const errors = [];
3705
3764
  let rules = [];
3706
3765
  let ignorePatterns;
@@ -3800,6 +3859,12 @@ async function importConfiguration(options) {
3800
3859
  targetDir = join23(rulesDirPath, "commands");
3801
3860
  const { mkdir: mkdir3 } = await import("fs/promises");
3802
3861
  await mkdir3(targetDir, { recursive: true });
3862
+ } else {
3863
+ if (!useLegacyLocation) {
3864
+ targetDir = join23(rulesDirPath, "rules");
3865
+ const { mkdir: mkdir3 } = await import("fs/promises");
3866
+ await mkdir3(targetDir, { recursive: true });
3867
+ }
3803
3868
  }
3804
3869
  const filePath = join23(targetDir, `${baseFilename}.md`);
3805
3870
  const content = generateRuleFileContent(rule);
@@ -3868,6 +3933,7 @@ function generateRuleFileContent(rule) {
3868
3933
 
3869
3934
  // src/cli/commands/import.ts
3870
3935
  async function importCommand(options = {}) {
3936
+ logger.setVerbose(options.verbose || false);
3871
3937
  const tools = [];
3872
3938
  if (options.augmentcode) tools.push("augmentcode");
3873
3939
  if (options["augmentcode-legacy"]) tools.push("augmentcode-legacy");
@@ -3878,66 +3944,74 @@ async function importCommand(options = {}) {
3878
3944
  if (options.roo) tools.push("roo");
3879
3945
  if (options.geminicli) tools.push("geminicli");
3880
3946
  if (tools.length === 0) {
3881
- console.error(
3947
+ logger.error(
3882
3948
  "\u274C Please specify one tool to import from (--augmentcode, --augmentcode-legacy, --claudecode, --cursor, --copilot, --cline, --roo, --geminicli)"
3883
3949
  );
3884
3950
  process.exit(1);
3885
3951
  }
3886
3952
  if (tools.length > 1) {
3887
- console.error(
3953
+ logger.error(
3888
3954
  "\u274C Only one tool can be specified at a time. Please run the import command separately for each tool."
3889
3955
  );
3890
3956
  process.exit(1);
3891
3957
  }
3892
3958
  const tool = tools[0];
3893
3959
  if (!tool) {
3894
- console.error("Error: No tool specified");
3960
+ logger.error("Error: No tool specified");
3895
3961
  process.exit(1);
3896
3962
  }
3897
- console.log(`Importing configuration files from ${tool}...`);
3963
+ logger.log(`Importing configuration files from ${tool}...`);
3898
3964
  try {
3899
3965
  const result = await importConfiguration({
3900
3966
  tool,
3901
- verbose: options.verbose ?? false
3967
+ verbose: options.verbose ?? false,
3968
+ useLegacyLocation: options.legacy ?? false
3902
3969
  });
3903
3970
  if (result.success) {
3904
- console.log(`\u2705 Imported ${result.rulesCreated} rule(s) from ${tool}`);
3971
+ logger.success(`Imported ${result.rulesCreated} rule(s) from ${tool}`);
3905
3972
  if (result.ignoreFileCreated) {
3906
- console.log("\u2705 Created .rulesyncignore file from ignore patterns");
3973
+ logger.success("Created .rulesyncignore file from ignore patterns");
3907
3974
  }
3908
3975
  if (result.mcpFileCreated) {
3909
- console.log("\u2705 Created .rulesync/.mcp.json file from MCP configuration");
3976
+ logger.success("Created .rulesync/.mcp.json file from MCP configuration");
3910
3977
  }
3911
- console.log("You can now run 'rulesync generate' to create tool-specific configurations.");
3978
+ logger.log("You can now run 'rulesync generate' to create tool-specific configurations.");
3912
3979
  } else if (result.errors.length > 0) {
3913
- console.warn(`\u26A0\uFE0F Failed to import from ${tool}: ${result.errors[0]}`);
3914
- if (options.verbose && result.errors.length > 1) {
3915
- console.log("\nDetailed errors:");
3980
+ logger.warn(`\u26A0\uFE0F Failed to import from ${tool}: ${result.errors[0]}`);
3981
+ if (result.errors.length > 1) {
3982
+ logger.info("\nDetailed errors:");
3916
3983
  for (const error of result.errors) {
3917
- console.log(` - ${error}`);
3984
+ logger.info(` - ${error}`);
3918
3985
  }
3919
3986
  }
3920
3987
  }
3921
3988
  } catch (error) {
3922
3989
  const errorMessage = error instanceof Error ? error.message : String(error);
3923
- console.error(`\u274C Error importing from ${tool}: ${errorMessage}`);
3990
+ logger.error(`\u274C Error importing from ${tool}: ${errorMessage}`);
3924
3991
  process.exit(1);
3925
3992
  }
3926
3993
  }
3927
3994
 
3928
3995
  // src/cli/commands/init.ts
3929
3996
  import { join as join24 } from "path";
3930
- async function initCommand() {
3931
- const aiRulesDir = ".rulesync";
3997
+ async function initCommand(options = {}) {
3998
+ const configResult = await loadConfig();
3999
+ const config = configResult.config;
4000
+ const aiRulesDir = config.aiRulesDir;
3932
4001
  console.log("Initializing rulesync...");
3933
4002
  await ensureDir(aiRulesDir);
3934
- await createSampleFiles(aiRulesDir);
4003
+ const useLegacy = options.legacy ?? config.legacy ?? false;
4004
+ const rulesDir = useLegacy ? aiRulesDir : join24(aiRulesDir, "rules");
4005
+ if (!useLegacy) {
4006
+ await ensureDir(rulesDir);
4007
+ }
4008
+ await createSampleFiles(rulesDir);
3935
4009
  console.log("\u2705 rulesync initialized successfully!");
3936
4010
  console.log("\nNext steps:");
3937
- console.log("1. Edit rule files in .rulesync/");
4011
+ console.log(`1. Edit rule files in ${rulesDir}/`);
3938
4012
  console.log("2. Run 'rulesync generate' to create configuration files");
3939
4013
  }
3940
- async function createSampleFiles(aiRulesDir) {
4014
+ async function createSampleFiles(rulesDir) {
3941
4015
  const sampleFile = {
3942
4016
  filename: "overview.md",
3943
4017
  content: `---
@@ -3973,7 +4047,7 @@ globs: ["**/*"]
3973
4047
  - Follow single responsibility principle
3974
4048
  `
3975
4049
  };
3976
- const filepath = join24(aiRulesDir, sampleFile.filename);
4050
+ const filepath = join24(rulesDir, sampleFile.filename);
3977
4051
  if (!await fileExists(filepath)) {
3978
4052
  await writeFileContent(filepath, sampleFile.content);
3979
4053
  console.log(`Created ${filepath}`);
@@ -4117,11 +4191,11 @@ async function watchCommand() {
4117
4191
 
4118
4192
  // src/cli/index.ts
4119
4193
  var program = new Command();
4120
- program.name("rulesync").description("Unified AI rules management CLI tool").version("0.61.0");
4121
- program.command("init").description("Initialize rulesync in current directory").action(initCommand);
4122
- program.command("add <filename>").description("Add a new rule file").action(addCommand);
4194
+ program.name("rulesync").description("Unified AI rules management CLI tool").version("0.62.0");
4195
+ program.command("init").description("Initialize rulesync in current directory").option("--legacy", "Use legacy file location (.rulesync/*.md instead of .rulesync/rules/*.md)").action(initCommand);
4196
+ 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);
4123
4197
  program.command("gitignore").description("Add generated files to .gitignore").action(gitignoreCommand);
4124
- 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);
4198
+ 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);
4125
4199
  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(
4126
4200
  "-b, --base-dir <paths>",
4127
4201
  "Base directories to generate files (comma-separated for multiple paths)"