rulesync 2.0.0 → 2.1.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/README.md CHANGED
@@ -3,6 +3,7 @@
3
3
  [![CI](https://github.com/dyoshikawa/rulesync/actions/workflows/ci.yml/badge.svg)](https://github.com/dyoshikawa/rulesync/actions/workflows/ci.yml)
4
4
  [![npm version](https://img.shields.io/npm/v/rulesync)](https://www.npmjs.com/package/rulesync)
5
5
  [![npm downloads](https://img.shields.io/npm/dt/rulesync)](https://www.npmjs.com/package/rulesync)
6
+ [![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/dyoshikawa/rulesync)
6
7
 
7
8
  A Node.js CLI tool that automatically generates configuration files for various AI development tools from unified AI rule files. Features selective generation, comprehensive import/export capabilities, and supports major AI development tools with rules, commands, MCP, ignore files, and subagents. Uses the recommended `.rulesync/rules/*.md` structure, with full backward compatibility for legacy `.rulesync/*.md` layouts.
8
9
 
@@ -57,7 +58,7 @@ Rulesync supports both **generation** and **import** for All of the major AI cod
57
58
  | Tool | rules | ignore | mcp | commands | subagents |
58
59
  |------------------------|:-----:|:------:|:-----:|:--------:|:---------:|
59
60
  | AGENTS.md | ✅ | | | | |
60
- | Claude Code | ✅ 🌏 | ✅ | ✅ | ✅ | ✅ |
61
+ | Claude Code | ✅ 🌏 | ✅ | ✅ | ✅ 🌏 | ✅ |
61
62
  | Codex CLI | ✅ 🌏 | ✅ | | 🎮 | 🎮 |
62
63
  | Gemini CLI | ✅ | ✅ | | ✅ | 🎮 |
63
64
  | GitHub Copilot | ✅ | | ✅ | 🎮 | 🎮 |
@@ -73,8 +74,9 @@ Rulesync supports both **generation** and **import** for All of the major AI cod
73
74
  | Windsurf | ✅ | ✅ | | | |
74
75
  | Warp | ✅ | | | | |
75
76
 
76
- 🌏: Supports global(means user scope) mode (Experimental Feature)
77
- 🎮: Simulated Commands/Subagents (Experimental Feature)
77
+ * ✅: Supports project mode
78
+ * 🌏: Supports global mode (Experimental Feature)
79
+ * 🎮: Supports Simulated Commands/Subagents (Experimental Feature)
78
80
 
79
81
  ## Why Rulesync?
80
82
 
@@ -94,7 +96,7 @@ Avoid lock-in completely. If you decide to stop using Rulesync, you can continue
94
96
  Apply consistent rules across all AI tools, improving code quality and development experience for the entire team.
95
97
 
96
98
  ### 🌏 **Global Mode**
97
- You can use global(also, called as user scope) mode via Rulesync by enabling `--experimental-global` option.
99
+ You can use global mode via Rulesync by enabling `--experimental-global` option.
98
100
 
99
101
  ### 🎮 **Simulated Commands and Subagents**
100
102
  Simulated commands and subagents are experimental features that allow you to generate simulated commands and subagents for copilot, cursor and codexcli. This is useful for shortening your prompts.
@@ -279,9 +281,9 @@ credentials/
279
281
 
280
282
  ## Global Mode(Experimental Feature)
281
283
 
282
- You can use global(also, called as user scope) mode via Rulesync by enabling `--experimental-global` option.
284
+ You can use global mode via Rulesync by enabling `--experimental-global` option. It can also be called as user scope mode.
283
285
 
284
- Currently, only supports rules generation. Import for global files is still not supported.
286
+ Currently, supports rules and commands generation for Claude Code. Import for global files is supported for rules and commands.
285
287
 
286
288
  1. Create an any name directory. For example, if you prefer `~/.aiglobal`, run the following command.
287
289
  ```bash
@@ -314,8 +316,9 @@ Currently, only supports rules generation. Import for global files is still not
314
316
 
315
317
  > [!WARNING]
316
318
  > Currently, when in the directory enabled global mode:
317
- > * `rulesync.jsonc` only supports `global`, `features`, `delete` and `verbose`. `Features` can be set `"rules"` only. Other parameters are ignored.
319
+ > * `rulesync.jsonc` only supports `global`, `features`, `delete` and `verbose`. `Features` can be set `"rules"` and `"commands"`. Other parameters are ignored.
318
320
  > * `rules/*.md` only supports single file has `root: true`, and frontmatter parameters without `root` are ignored.
321
+ > * Only Claude Code is supported for global mode commands.
319
322
 
320
323
  ## Simulate Commands and Subagents(Experimental Feature)
321
324
 
package/dist/index.cjs CHANGED
@@ -119,6 +119,7 @@ async function directoryExists(dirPath) {
119
119
  }
120
120
  }
121
121
  async function readFileContent(filepath) {
122
+ logger.debug(`Reading file: ${filepath}`);
122
123
  return (0, import_promises.readFile)(filepath, "utf-8");
123
124
  }
124
125
  async function writeFileContent(filepath, content) {
@@ -543,6 +544,11 @@ var ClaudecodeCommand = class _ClaudecodeCommand extends ToolCommand {
543
544
  relativeDirPath: ".claude/commands"
544
545
  };
545
546
  }
547
+ static getSettablePathsGlobal() {
548
+ return {
549
+ relativeDirPath: (0, import_node_path4.join)(".claude", "commands")
550
+ };
551
+ }
546
552
  getBody() {
547
553
  return this.body;
548
554
  }
@@ -556,7 +562,8 @@ var ClaudecodeCommand = class _ClaudecodeCommand extends ToolCommand {
556
562
  };
557
563
  const fileContent = stringifyFrontmatter(this.body, rulesyncFrontmatter);
558
564
  return new RulesyncCommand({
559
- baseDir: this.baseDir,
565
+ baseDir: ".",
566
+ // RulesyncCommand baseDir is always the project root directory
560
567
  frontmatter: rulesyncFrontmatter,
561
568
  body: this.body,
562
569
  relativeDirPath: RulesyncCommand.getSettablePaths().relativeDirPath,
@@ -568,18 +575,20 @@ var ClaudecodeCommand = class _ClaudecodeCommand extends ToolCommand {
568
575
  static fromRulesyncCommand({
569
576
  baseDir = ".",
570
577
  rulesyncCommand,
571
- validate = true
578
+ validate = true,
579
+ global = false
572
580
  }) {
573
581
  const rulesyncFrontmatter = rulesyncCommand.getFrontmatter();
574
582
  const claudecodeFrontmatter = {
575
583
  description: rulesyncFrontmatter.description
576
584
  };
577
585
  const body = rulesyncCommand.getBody();
586
+ const paths = global ? this.getSettablePathsGlobal() : this.getSettablePaths();
578
587
  return new _ClaudecodeCommand({
579
588
  baseDir,
580
589
  frontmatter: claudecodeFrontmatter,
581
590
  body,
582
- relativeDirPath: _ClaudecodeCommand.getSettablePaths().relativeDirPath,
591
+ relativeDirPath: paths.relativeDirPath,
583
592
  relativeFilePath: rulesyncCommand.getRelativeFilePath(),
584
593
  validate
585
594
  });
@@ -604,13 +613,11 @@ var ClaudecodeCommand = class _ClaudecodeCommand extends ToolCommand {
604
613
  static async fromFile({
605
614
  baseDir = ".",
606
615
  relativeFilePath,
607
- validate = true
616
+ validate = true,
617
+ global = false
608
618
  }) {
609
- const filePath = (0, import_node_path4.join)(
610
- baseDir,
611
- _ClaudecodeCommand.getSettablePaths().relativeDirPath,
612
- relativeFilePath
613
- );
619
+ const paths = global ? this.getSettablePathsGlobal() : this.getSettablePaths();
620
+ const filePath = (0, import_node_path4.join)(baseDir, paths.relativeDirPath, relativeFilePath);
614
621
  const fileContent = await readFileContent(filePath);
615
622
  const { frontmatter, body: content } = parseFrontmatter(fileContent);
616
623
  const result = ClaudecodeCommandFrontmatterSchema.safeParse(frontmatter);
@@ -619,7 +626,7 @@ var ClaudecodeCommand = class _ClaudecodeCommand extends ToolCommand {
619
626
  }
620
627
  return new _ClaudecodeCommand({
621
628
  baseDir,
622
- relativeDirPath: _ClaudecodeCommand.getSettablePaths().relativeDirPath,
629
+ relativeDirPath: paths.relativeDirPath,
623
630
  relativeFilePath: (0, import_node_path4.basename)(relativeFilePath),
624
631
  frontmatter: result.data,
625
632
  body: content.trim(),
@@ -833,7 +840,8 @@ var CursorCommand = class _CursorCommand extends ToolCommand {
833
840
  description: ""
834
841
  };
835
842
  return new RulesyncCommand({
836
- baseDir: this.baseDir,
843
+ baseDir: ".",
844
+ // RulesyncCommand baseDir is always the project root directory
837
845
  frontmatter: rulesyncFrontmatter,
838
846
  body: this.getFileContent(),
839
847
  relativeDirPath: RulesyncCommand.getSettablePaths().relativeDirPath,
@@ -939,7 +947,8 @@ var GeminiCliCommand = class _GeminiCliCommand extends ToolCommand {
939
947
  };
940
948
  const fileContent = stringifyFrontmatter(this.body, rulesyncFrontmatter);
941
949
  return new RulesyncCommand({
942
- baseDir: this.baseDir,
950
+ baseDir: ".",
951
+ // RulesyncCommand baseDir is always the project root directory
943
952
  frontmatter: rulesyncFrontmatter,
944
953
  body: this.body,
945
954
  relativeDirPath: RulesyncCommand.getSettablePaths().relativeDirPath,
@@ -1047,7 +1056,8 @@ var RooCommand = class _RooCommand extends ToolCommand {
1047
1056
  };
1048
1057
  const fileContent = stringifyFrontmatter(this.body, rulesyncFrontmatter);
1049
1058
  return new RulesyncCommand({
1050
- baseDir: this.baseDir,
1059
+ baseDir: ".",
1060
+ // RulesyncCommand baseDir is always the project root directory
1051
1061
  frontmatter: rulesyncFrontmatter,
1052
1062
  body: this.body,
1053
1063
  relativeDirPath: RulesyncCommand.getSettablePaths().relativeDirPath,
@@ -1131,12 +1141,15 @@ var CommandsProcessorToolTargetSchema = import_mini8.z.enum(commandsProcessorToo
1131
1141
  var commandsProcessorToolTargetsSimulated = ["copilot", "codexcli"];
1132
1142
  var CommandsProcessor = class extends FeatureProcessor {
1133
1143
  toolTarget;
1144
+ global;
1134
1145
  constructor({
1135
1146
  baseDir = process.cwd(),
1136
- toolTarget
1147
+ toolTarget,
1148
+ global = false
1137
1149
  }) {
1138
1150
  super({ baseDir });
1139
1151
  this.toolTarget = CommandsProcessorToolTargetSchema.parse(toolTarget);
1152
+ this.global = global;
1140
1153
  }
1141
1154
  async convertRulesyncFilesToToolFiles(rulesyncFiles) {
1142
1155
  const rulesyncCommands = rulesyncFiles.filter(
@@ -1150,7 +1163,8 @@ var CommandsProcessor = class extends FeatureProcessor {
1150
1163
  }
1151
1164
  return ClaudecodeCommand.fromRulesyncCommand({
1152
1165
  baseDir: this.baseDir,
1153
- rulesyncCommand
1166
+ rulesyncCommand,
1167
+ global: this.global
1154
1168
  });
1155
1169
  case "geminicli":
1156
1170
  if (!GeminiCliCommand.isTargetedByRulesyncCommand(rulesyncCommand)) {
@@ -1262,17 +1276,36 @@ var CommandsProcessor = class extends FeatureProcessor {
1262
1276
  commandFilePaths.map((path2) => {
1263
1277
  switch (toolTarget) {
1264
1278
  case "claudecode":
1265
- return ClaudecodeCommand.fromFile({ relativeFilePath: (0, import_node_path11.basename)(path2) });
1279
+ return ClaudecodeCommand.fromFile({
1280
+ baseDir: this.baseDir,
1281
+ relativeFilePath: (0, import_node_path11.basename)(path2),
1282
+ global: this.global
1283
+ });
1266
1284
  case "geminicli":
1267
- return GeminiCliCommand.fromFile({ relativeFilePath: (0, import_node_path11.basename)(path2) });
1285
+ return GeminiCliCommand.fromFile({
1286
+ baseDir: this.baseDir,
1287
+ relativeFilePath: (0, import_node_path11.basename)(path2)
1288
+ });
1268
1289
  case "roo":
1269
- return RooCommand.fromFile({ relativeFilePath: (0, import_node_path11.basename)(path2) });
1290
+ return RooCommand.fromFile({
1291
+ baseDir: this.baseDir,
1292
+ relativeFilePath: (0, import_node_path11.basename)(path2)
1293
+ });
1270
1294
  case "copilot":
1271
- return CopilotCommand.fromFile({ relativeFilePath: (0, import_node_path11.basename)(path2) });
1295
+ return CopilotCommand.fromFile({
1296
+ baseDir: this.baseDir,
1297
+ relativeFilePath: (0, import_node_path11.basename)(path2)
1298
+ });
1272
1299
  case "cursor":
1273
- return CursorCommand.fromFile({ relativeFilePath: (0, import_node_path11.basename)(path2) });
1300
+ return CursorCommand.fromFile({
1301
+ baseDir: this.baseDir,
1302
+ relativeFilePath: (0, import_node_path11.basename)(path2)
1303
+ });
1274
1304
  case "codexcli":
1275
- return CodexCliCommand.fromFile({ relativeFilePath: (0, import_node_path11.basename)(path2) });
1305
+ return CodexCliCommand.fromFile({
1306
+ baseDir: this.baseDir,
1307
+ relativeFilePath: (0, import_node_path11.basename)(path2)
1308
+ });
1276
1309
  default:
1277
1310
  throw new Error(`Unsupported tool target: ${toolTarget}`);
1278
1311
  }
@@ -1295,9 +1328,10 @@ var CommandsProcessor = class extends FeatureProcessor {
1295
1328
  * Load Claude Code command configurations from .claude/commands/ directory
1296
1329
  */
1297
1330
  async loadClaudecodeCommands() {
1331
+ const paths = this.global ? ClaudecodeCommand.getSettablePathsGlobal() : ClaudecodeCommand.getSettablePaths();
1298
1332
  return await this.loadToolCommandDefault({
1299
1333
  toolTarget: "claudecode",
1300
- relativeDirPath: ClaudecodeCommand.getSettablePaths().relativeDirPath,
1334
+ relativeDirPath: paths.relativeDirPath,
1301
1335
  extension: "md"
1302
1336
  });
1303
1337
  }
@@ -1358,6 +1392,9 @@ var CommandsProcessor = class extends FeatureProcessor {
1358
1392
  static getToolTargetsSimulated() {
1359
1393
  return commandsProcessorToolTargetsSimulated;
1360
1394
  }
1395
+ static getToolTargetsGlobal() {
1396
+ return ["claudecode"];
1397
+ }
1361
1398
  };
1362
1399
 
1363
1400
  // src/config/config-resolver.ts
@@ -1740,7 +1777,7 @@ var ClaudecodeIgnore = class _ClaudecodeIgnore extends ToolIgnore {
1740
1777
  ...existingJsonValue,
1741
1778
  permissions: {
1742
1779
  ...existingJsonValue.permissions,
1743
- deny: (0, import_es_toolkit.uniq)([...existingJsonValue.permissions?.deny ?? [], ...deniedValues].sort())
1780
+ deny: (0, import_es_toolkit.uniq)([...existingJsonValue.permissions?.deny ?? [], ...deniedValues].toSorted())
1744
1781
  }
1745
1782
  };
1746
1783
  return new _ClaudecodeIgnore({
@@ -3872,7 +3909,8 @@ var ToolRule = class extends ToolFile {
3872
3909
  }
3873
3910
  toRulesyncRuleDefault() {
3874
3911
  return new RulesyncRule({
3875
- baseDir: this.getBaseDir(),
3912
+ baseDir: ".",
3913
+ // RulesyncRule baseDir is always the project root directory
3876
3914
  relativeDirPath: ".rulesync/rules",
3877
3915
  relativeFilePath: this.getRelativeFilePath(),
3878
3916
  frontmatter: {
@@ -4046,7 +4084,8 @@ var AugmentcodeLegacyRule = class _AugmentcodeLegacyRule extends ToolRule {
4046
4084
  globs: this.isRoot() ? ["**/*"] : []
4047
4085
  };
4048
4086
  return new RulesyncRule({
4049
- baseDir: this.getBaseDir(),
4087
+ baseDir: ".",
4088
+ // RulesyncRule baseDir is always the project root directory
4050
4089
  frontmatter: rulesyncFrontmatter,
4051
4090
  body: this.getFileContent(),
4052
4091
  relativeDirPath: ".rulesync/rules",
@@ -6203,22 +6242,20 @@ async function generateCommands(config) {
6203
6242
  logger.debug("Skipping command file generation (not in --features)");
6204
6243
  return 0;
6205
6244
  }
6206
- if (config.getExperimentalGlobal()) {
6207
- logger.debug("Skipping command file generation (not supported in global mode)");
6208
- return 0;
6209
- }
6210
6245
  let totalCommandOutputs = 0;
6211
6246
  logger.info("Generating command files...");
6247
+ const toolTargets = config.getExperimentalGlobal() ? (0, import_es_toolkit2.intersection)(config.getTargets(), CommandsProcessor.getToolTargetsGlobal()) : (0, import_es_toolkit2.intersection)(
6248
+ config.getTargets(),
6249
+ CommandsProcessor.getToolTargets({
6250
+ includeSimulated: config.getExperimentalSimulateCommands()
6251
+ })
6252
+ );
6212
6253
  for (const baseDir of config.getBaseDirs()) {
6213
- for (const toolTarget of (0, import_es_toolkit2.intersection)(
6214
- config.getTargets(),
6215
- CommandsProcessor.getToolTargets({
6216
- includeSimulated: config.getExperimentalSimulateCommands()
6217
- })
6218
- )) {
6254
+ for (const toolTarget of toolTargets) {
6219
6255
  const processor = new CommandsProcessor({
6220
6256
  baseDir,
6221
- toolTarget
6257
+ toolTarget,
6258
+ global: config.getExperimentalGlobal()
6222
6259
  });
6223
6260
  if (config.getDelete()) {
6224
6261
  const oldToolFiles = await processor.loadToolFilesToDelete();
@@ -6382,7 +6419,7 @@ async function importRules(config, tool) {
6382
6419
  return 0;
6383
6420
  }
6384
6421
  const rulesProcessor = new RulesProcessor({
6385
- baseDir: ".",
6422
+ baseDir: config.getBaseDirs()[0] ?? ".",
6386
6423
  toolTarget: tool,
6387
6424
  global
6388
6425
  });
@@ -6409,7 +6446,7 @@ async function importIgnore(config, tool) {
6409
6446
  return 0;
6410
6447
  }
6411
6448
  const ignoreProcessor = new IgnoreProcessor({
6412
- baseDir: ".",
6449
+ baseDir: config.getBaseDirs()[0] ?? ".",
6413
6450
  toolTarget: tool
6414
6451
  });
6415
6452
  const toolFiles = await ignoreProcessor.loadToolFiles();
@@ -6438,7 +6475,7 @@ async function importMcp(config, tool) {
6438
6475
  return 0;
6439
6476
  }
6440
6477
  const mcpProcessor = new McpProcessor({
6441
- baseDir: ".",
6478
+ baseDir: config.getBaseDirs()[0] ?? ".",
6442
6479
  toolTarget: tool
6443
6480
  });
6444
6481
  const toolFiles = await mcpProcessor.loadToolFiles();
@@ -6456,17 +6493,18 @@ async function importCommands(config, tool) {
6456
6493
  if (!config.getFeatures().includes("commands")) {
6457
6494
  return 0;
6458
6495
  }
6459
- if (config.getExperimentalGlobal()) {
6460
- logger.debug("Skipping command file import (not supported in global mode)");
6461
- return 0;
6462
- }
6463
- const supportedTargets = CommandsProcessor.getToolTargets({ includeSimulated: false });
6496
+ const global = config.getExperimentalGlobal();
6497
+ const supportedTargets = global ? CommandsProcessor.getToolTargetsGlobal() : CommandsProcessor.getToolTargets({ includeSimulated: false });
6464
6498
  if (!supportedTargets.includes(tool)) {
6499
+ if (global) {
6500
+ logger.debug(`${tool} is not supported for commands in global mode`);
6501
+ }
6465
6502
  return 0;
6466
6503
  }
6467
6504
  const commandsProcessor = new CommandsProcessor({
6468
- baseDir: ".",
6469
- toolTarget: tool
6505
+ baseDir: config.getBaseDirs()[0] ?? ".",
6506
+ toolTarget: tool,
6507
+ global
6470
6508
  });
6471
6509
  const toolFiles = await commandsProcessor.loadToolFiles();
6472
6510
  if (toolFiles.length === 0) {
@@ -6492,7 +6530,7 @@ async function importSubagents(config, tool) {
6492
6530
  return 0;
6493
6531
  }
6494
6532
  const subagentsProcessor = new SubagentsProcessor({
6495
- baseDir: ".",
6533
+ baseDir: config.getBaseDirs()[0] ?? ".",
6496
6534
  toolTarget: tool
6497
6535
  });
6498
6536
  const toolFiles = await subagentsProcessor.loadToolFiles();
@@ -6592,7 +6630,7 @@ globs: ["**/*"]
6592
6630
  }
6593
6631
 
6594
6632
  // src/cli/index.ts
6595
- var getVersion = () => "2.0.0";
6633
+ var getVersion = () => "2.1.0";
6596
6634
  var main = async () => {
6597
6635
  const program = new import_commander.Command();
6598
6636
  const version = getVersion();
@@ -6622,7 +6660,8 @@ var main = async () => {
6622
6660
  targets: options.targets,
6623
6661
  features: options.features,
6624
6662
  verbose: options.verbose,
6625
- configPath: options.config
6663
+ configPath: options.config,
6664
+ experimentalGlobal: options.experimentalGlobal
6626
6665
  });
6627
6666
  } catch (error) {
6628
6667
  logger.error(error instanceof Error ? error.message : String(error));
package/dist/index.js CHANGED
@@ -96,6 +96,7 @@ async function directoryExists(dirPath) {
96
96
  }
97
97
  }
98
98
  async function readFileContent(filepath) {
99
+ logger.debug(`Reading file: ${filepath}`);
99
100
  return readFile(filepath, "utf-8");
100
101
  }
101
102
  async function writeFileContent(filepath, content) {
@@ -520,6 +521,11 @@ var ClaudecodeCommand = class _ClaudecodeCommand extends ToolCommand {
520
521
  relativeDirPath: ".claude/commands"
521
522
  };
522
523
  }
524
+ static getSettablePathsGlobal() {
525
+ return {
526
+ relativeDirPath: join3(".claude", "commands")
527
+ };
528
+ }
523
529
  getBody() {
524
530
  return this.body;
525
531
  }
@@ -533,7 +539,8 @@ var ClaudecodeCommand = class _ClaudecodeCommand extends ToolCommand {
533
539
  };
534
540
  const fileContent = stringifyFrontmatter(this.body, rulesyncFrontmatter);
535
541
  return new RulesyncCommand({
536
- baseDir: this.baseDir,
542
+ baseDir: ".",
543
+ // RulesyncCommand baseDir is always the project root directory
537
544
  frontmatter: rulesyncFrontmatter,
538
545
  body: this.body,
539
546
  relativeDirPath: RulesyncCommand.getSettablePaths().relativeDirPath,
@@ -545,18 +552,20 @@ var ClaudecodeCommand = class _ClaudecodeCommand extends ToolCommand {
545
552
  static fromRulesyncCommand({
546
553
  baseDir = ".",
547
554
  rulesyncCommand,
548
- validate = true
555
+ validate = true,
556
+ global = false
549
557
  }) {
550
558
  const rulesyncFrontmatter = rulesyncCommand.getFrontmatter();
551
559
  const claudecodeFrontmatter = {
552
560
  description: rulesyncFrontmatter.description
553
561
  };
554
562
  const body = rulesyncCommand.getBody();
563
+ const paths = global ? this.getSettablePathsGlobal() : this.getSettablePaths();
555
564
  return new _ClaudecodeCommand({
556
565
  baseDir,
557
566
  frontmatter: claudecodeFrontmatter,
558
567
  body,
559
- relativeDirPath: _ClaudecodeCommand.getSettablePaths().relativeDirPath,
568
+ relativeDirPath: paths.relativeDirPath,
560
569
  relativeFilePath: rulesyncCommand.getRelativeFilePath(),
561
570
  validate
562
571
  });
@@ -581,13 +590,11 @@ var ClaudecodeCommand = class _ClaudecodeCommand extends ToolCommand {
581
590
  static async fromFile({
582
591
  baseDir = ".",
583
592
  relativeFilePath,
584
- validate = true
593
+ validate = true,
594
+ global = false
585
595
  }) {
586
- const filePath = join3(
587
- baseDir,
588
- _ClaudecodeCommand.getSettablePaths().relativeDirPath,
589
- relativeFilePath
590
- );
596
+ const paths = global ? this.getSettablePathsGlobal() : this.getSettablePaths();
597
+ const filePath = join3(baseDir, paths.relativeDirPath, relativeFilePath);
591
598
  const fileContent = await readFileContent(filePath);
592
599
  const { frontmatter, body: content } = parseFrontmatter(fileContent);
593
600
  const result = ClaudecodeCommandFrontmatterSchema.safeParse(frontmatter);
@@ -596,7 +603,7 @@ var ClaudecodeCommand = class _ClaudecodeCommand extends ToolCommand {
596
603
  }
597
604
  return new _ClaudecodeCommand({
598
605
  baseDir,
599
- relativeDirPath: _ClaudecodeCommand.getSettablePaths().relativeDirPath,
606
+ relativeDirPath: paths.relativeDirPath,
600
607
  relativeFilePath: basename3(relativeFilePath),
601
608
  frontmatter: result.data,
602
609
  body: content.trim(),
@@ -810,7 +817,8 @@ var CursorCommand = class _CursorCommand extends ToolCommand {
810
817
  description: ""
811
818
  };
812
819
  return new RulesyncCommand({
813
- baseDir: this.baseDir,
820
+ baseDir: ".",
821
+ // RulesyncCommand baseDir is always the project root directory
814
822
  frontmatter: rulesyncFrontmatter,
815
823
  body: this.getFileContent(),
816
824
  relativeDirPath: RulesyncCommand.getSettablePaths().relativeDirPath,
@@ -916,7 +924,8 @@ var GeminiCliCommand = class _GeminiCliCommand extends ToolCommand {
916
924
  };
917
925
  const fileContent = stringifyFrontmatter(this.body, rulesyncFrontmatter);
918
926
  return new RulesyncCommand({
919
- baseDir: this.baseDir,
927
+ baseDir: ".",
928
+ // RulesyncCommand baseDir is always the project root directory
920
929
  frontmatter: rulesyncFrontmatter,
921
930
  body: this.body,
922
931
  relativeDirPath: RulesyncCommand.getSettablePaths().relativeDirPath,
@@ -1024,7 +1033,8 @@ var RooCommand = class _RooCommand extends ToolCommand {
1024
1033
  };
1025
1034
  const fileContent = stringifyFrontmatter(this.body, rulesyncFrontmatter);
1026
1035
  return new RulesyncCommand({
1027
- baseDir: this.baseDir,
1036
+ baseDir: ".",
1037
+ // RulesyncCommand baseDir is always the project root directory
1028
1038
  frontmatter: rulesyncFrontmatter,
1029
1039
  body: this.body,
1030
1040
  relativeDirPath: RulesyncCommand.getSettablePaths().relativeDirPath,
@@ -1108,12 +1118,15 @@ var CommandsProcessorToolTargetSchema = z8.enum(commandsProcessorToolTargets);
1108
1118
  var commandsProcessorToolTargetsSimulated = ["copilot", "codexcli"];
1109
1119
  var CommandsProcessor = class extends FeatureProcessor {
1110
1120
  toolTarget;
1121
+ global;
1111
1122
  constructor({
1112
1123
  baseDir = process.cwd(),
1113
- toolTarget
1124
+ toolTarget,
1125
+ global = false
1114
1126
  }) {
1115
1127
  super({ baseDir });
1116
1128
  this.toolTarget = CommandsProcessorToolTargetSchema.parse(toolTarget);
1129
+ this.global = global;
1117
1130
  }
1118
1131
  async convertRulesyncFilesToToolFiles(rulesyncFiles) {
1119
1132
  const rulesyncCommands = rulesyncFiles.filter(
@@ -1127,7 +1140,8 @@ var CommandsProcessor = class extends FeatureProcessor {
1127
1140
  }
1128
1141
  return ClaudecodeCommand.fromRulesyncCommand({
1129
1142
  baseDir: this.baseDir,
1130
- rulesyncCommand
1143
+ rulesyncCommand,
1144
+ global: this.global
1131
1145
  });
1132
1146
  case "geminicli":
1133
1147
  if (!GeminiCliCommand.isTargetedByRulesyncCommand(rulesyncCommand)) {
@@ -1239,17 +1253,36 @@ var CommandsProcessor = class extends FeatureProcessor {
1239
1253
  commandFilePaths.map((path2) => {
1240
1254
  switch (toolTarget) {
1241
1255
  case "claudecode":
1242
- return ClaudecodeCommand.fromFile({ relativeFilePath: basename10(path2) });
1256
+ return ClaudecodeCommand.fromFile({
1257
+ baseDir: this.baseDir,
1258
+ relativeFilePath: basename10(path2),
1259
+ global: this.global
1260
+ });
1243
1261
  case "geminicli":
1244
- return GeminiCliCommand.fromFile({ relativeFilePath: basename10(path2) });
1262
+ return GeminiCliCommand.fromFile({
1263
+ baseDir: this.baseDir,
1264
+ relativeFilePath: basename10(path2)
1265
+ });
1245
1266
  case "roo":
1246
- return RooCommand.fromFile({ relativeFilePath: basename10(path2) });
1267
+ return RooCommand.fromFile({
1268
+ baseDir: this.baseDir,
1269
+ relativeFilePath: basename10(path2)
1270
+ });
1247
1271
  case "copilot":
1248
- return CopilotCommand.fromFile({ relativeFilePath: basename10(path2) });
1272
+ return CopilotCommand.fromFile({
1273
+ baseDir: this.baseDir,
1274
+ relativeFilePath: basename10(path2)
1275
+ });
1249
1276
  case "cursor":
1250
- return CursorCommand.fromFile({ relativeFilePath: basename10(path2) });
1277
+ return CursorCommand.fromFile({
1278
+ baseDir: this.baseDir,
1279
+ relativeFilePath: basename10(path2)
1280
+ });
1251
1281
  case "codexcli":
1252
- return CodexCliCommand.fromFile({ relativeFilePath: basename10(path2) });
1282
+ return CodexCliCommand.fromFile({
1283
+ baseDir: this.baseDir,
1284
+ relativeFilePath: basename10(path2)
1285
+ });
1253
1286
  default:
1254
1287
  throw new Error(`Unsupported tool target: ${toolTarget}`);
1255
1288
  }
@@ -1272,9 +1305,10 @@ var CommandsProcessor = class extends FeatureProcessor {
1272
1305
  * Load Claude Code command configurations from .claude/commands/ directory
1273
1306
  */
1274
1307
  async loadClaudecodeCommands() {
1308
+ const paths = this.global ? ClaudecodeCommand.getSettablePathsGlobal() : ClaudecodeCommand.getSettablePaths();
1275
1309
  return await this.loadToolCommandDefault({
1276
1310
  toolTarget: "claudecode",
1277
- relativeDirPath: ClaudecodeCommand.getSettablePaths().relativeDirPath,
1311
+ relativeDirPath: paths.relativeDirPath,
1278
1312
  extension: "md"
1279
1313
  });
1280
1314
  }
@@ -1335,6 +1369,9 @@ var CommandsProcessor = class extends FeatureProcessor {
1335
1369
  static getToolTargetsSimulated() {
1336
1370
  return commandsProcessorToolTargetsSimulated;
1337
1371
  }
1372
+ static getToolTargetsGlobal() {
1373
+ return ["claudecode"];
1374
+ }
1338
1375
  };
1339
1376
 
1340
1377
  // src/config/config-resolver.ts
@@ -1717,7 +1754,7 @@ var ClaudecodeIgnore = class _ClaudecodeIgnore extends ToolIgnore {
1717
1754
  ...existingJsonValue,
1718
1755
  permissions: {
1719
1756
  ...existingJsonValue.permissions,
1720
- deny: uniq([...existingJsonValue.permissions?.deny ?? [], ...deniedValues].sort())
1757
+ deny: uniq([...existingJsonValue.permissions?.deny ?? [], ...deniedValues].toSorted())
1721
1758
  }
1722
1759
  };
1723
1760
  return new _ClaudecodeIgnore({
@@ -3849,7 +3886,8 @@ var ToolRule = class extends ToolFile {
3849
3886
  }
3850
3887
  toRulesyncRuleDefault() {
3851
3888
  return new RulesyncRule({
3852
- baseDir: this.getBaseDir(),
3889
+ baseDir: ".",
3890
+ // RulesyncRule baseDir is always the project root directory
3853
3891
  relativeDirPath: ".rulesync/rules",
3854
3892
  relativeFilePath: this.getRelativeFilePath(),
3855
3893
  frontmatter: {
@@ -4023,7 +4061,8 @@ var AugmentcodeLegacyRule = class _AugmentcodeLegacyRule extends ToolRule {
4023
4061
  globs: this.isRoot() ? ["**/*"] : []
4024
4062
  };
4025
4063
  return new RulesyncRule({
4026
- baseDir: this.getBaseDir(),
4064
+ baseDir: ".",
4065
+ // RulesyncRule baseDir is always the project root directory
4027
4066
  frontmatter: rulesyncFrontmatter,
4028
4067
  body: this.getFileContent(),
4029
4068
  relativeDirPath: ".rulesync/rules",
@@ -6180,22 +6219,20 @@ async function generateCommands(config) {
6180
6219
  logger.debug("Skipping command file generation (not in --features)");
6181
6220
  return 0;
6182
6221
  }
6183
- if (config.getExperimentalGlobal()) {
6184
- logger.debug("Skipping command file generation (not supported in global mode)");
6185
- return 0;
6186
- }
6187
6222
  let totalCommandOutputs = 0;
6188
6223
  logger.info("Generating command files...");
6224
+ const toolTargets = config.getExperimentalGlobal() ? intersection(config.getTargets(), CommandsProcessor.getToolTargetsGlobal()) : intersection(
6225
+ config.getTargets(),
6226
+ CommandsProcessor.getToolTargets({
6227
+ includeSimulated: config.getExperimentalSimulateCommands()
6228
+ })
6229
+ );
6189
6230
  for (const baseDir of config.getBaseDirs()) {
6190
- for (const toolTarget of intersection(
6191
- config.getTargets(),
6192
- CommandsProcessor.getToolTargets({
6193
- includeSimulated: config.getExperimentalSimulateCommands()
6194
- })
6195
- )) {
6231
+ for (const toolTarget of toolTargets) {
6196
6232
  const processor = new CommandsProcessor({
6197
6233
  baseDir,
6198
- toolTarget
6234
+ toolTarget,
6235
+ global: config.getExperimentalGlobal()
6199
6236
  });
6200
6237
  if (config.getDelete()) {
6201
6238
  const oldToolFiles = await processor.loadToolFilesToDelete();
@@ -6359,7 +6396,7 @@ async function importRules(config, tool) {
6359
6396
  return 0;
6360
6397
  }
6361
6398
  const rulesProcessor = new RulesProcessor({
6362
- baseDir: ".",
6399
+ baseDir: config.getBaseDirs()[0] ?? ".",
6363
6400
  toolTarget: tool,
6364
6401
  global
6365
6402
  });
@@ -6386,7 +6423,7 @@ async function importIgnore(config, tool) {
6386
6423
  return 0;
6387
6424
  }
6388
6425
  const ignoreProcessor = new IgnoreProcessor({
6389
- baseDir: ".",
6426
+ baseDir: config.getBaseDirs()[0] ?? ".",
6390
6427
  toolTarget: tool
6391
6428
  });
6392
6429
  const toolFiles = await ignoreProcessor.loadToolFiles();
@@ -6415,7 +6452,7 @@ async function importMcp(config, tool) {
6415
6452
  return 0;
6416
6453
  }
6417
6454
  const mcpProcessor = new McpProcessor({
6418
- baseDir: ".",
6455
+ baseDir: config.getBaseDirs()[0] ?? ".",
6419
6456
  toolTarget: tool
6420
6457
  });
6421
6458
  const toolFiles = await mcpProcessor.loadToolFiles();
@@ -6433,17 +6470,18 @@ async function importCommands(config, tool) {
6433
6470
  if (!config.getFeatures().includes("commands")) {
6434
6471
  return 0;
6435
6472
  }
6436
- if (config.getExperimentalGlobal()) {
6437
- logger.debug("Skipping command file import (not supported in global mode)");
6438
- return 0;
6439
- }
6440
- const supportedTargets = CommandsProcessor.getToolTargets({ includeSimulated: false });
6473
+ const global = config.getExperimentalGlobal();
6474
+ const supportedTargets = global ? CommandsProcessor.getToolTargetsGlobal() : CommandsProcessor.getToolTargets({ includeSimulated: false });
6441
6475
  if (!supportedTargets.includes(tool)) {
6476
+ if (global) {
6477
+ logger.debug(`${tool} is not supported for commands in global mode`);
6478
+ }
6442
6479
  return 0;
6443
6480
  }
6444
6481
  const commandsProcessor = new CommandsProcessor({
6445
- baseDir: ".",
6446
- toolTarget: tool
6482
+ baseDir: config.getBaseDirs()[0] ?? ".",
6483
+ toolTarget: tool,
6484
+ global
6447
6485
  });
6448
6486
  const toolFiles = await commandsProcessor.loadToolFiles();
6449
6487
  if (toolFiles.length === 0) {
@@ -6469,7 +6507,7 @@ async function importSubagents(config, tool) {
6469
6507
  return 0;
6470
6508
  }
6471
6509
  const subagentsProcessor = new SubagentsProcessor({
6472
- baseDir: ".",
6510
+ baseDir: config.getBaseDirs()[0] ?? ".",
6473
6511
  toolTarget: tool
6474
6512
  });
6475
6513
  const toolFiles = await subagentsProcessor.loadToolFiles();
@@ -6569,7 +6607,7 @@ globs: ["**/*"]
6569
6607
  }
6570
6608
 
6571
6609
  // src/cli/index.ts
6572
- var getVersion = () => "2.0.0";
6610
+ var getVersion = () => "2.1.0";
6573
6611
  var main = async () => {
6574
6612
  const program = new Command();
6575
6613
  const version = getVersion();
@@ -6599,7 +6637,8 @@ var main = async () => {
6599
6637
  targets: options.targets,
6600
6638
  features: options.features,
6601
6639
  verbose: options.verbose,
6602
- configPath: options.config
6640
+ configPath: options.config,
6641
+ experimentalGlobal: options.experimentalGlobal
6603
6642
  });
6604
6643
  } catch (error) {
6605
6644
  logger.error(error instanceof Error ? error.message : String(error));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rulesync",
3
- "version": "2.0.0",
3
+ "version": "2.1.0",
4
4
  "description": "Unified AI rules management CLI tool that generates configuration files for various AI development tools",
5
5
  "keywords": [
6
6
  "ai",
@@ -49,34 +49,34 @@
49
49
  "zod": "4.1.11"
50
50
  },
51
51
  "devDependencies": {
52
- "@anthropic-ai/claude-code": "1.0.124",
52
+ "@anthropic-ai/claude-agent-sdk": "0.1.2",
53
53
  "@biomejs/biome": "2.2.4",
54
54
  "@eslint/js": "9.36.0",
55
55
  "@secretlint/secretlint-rule-preset-recommend": "11.2.4",
56
56
  "@tsconfig/node24": "24.0.1",
57
57
  "@types/js-yaml": "4.0.9",
58
58
  "@types/micromatch": "4.0.9",
59
- "@types/node": "24.5.2",
60
- "@typescript/native-preview": "7.0.0-dev.20250924.1",
59
+ "@types/node": "24.6.2",
60
+ "@typescript/native-preview": "7.0.0-dev.20251001.1",
61
61
  "@vitest/coverage-v8": "3.2.4",
62
62
  "cspell": "9.2.1",
63
63
  "eslint": "9.36.0",
64
64
  "eslint-plugin-import": "2.32.0",
65
65
  "eslint-plugin-no-type-assertion": "1.3.0",
66
- "eslint-plugin-oxlint": "1.18.0",
67
- "eslint-plugin-strict-dependencies": "1.3.24",
66
+ "eslint-plugin-oxlint": "1.19.0",
67
+ "eslint-plugin-strict-dependencies": "1.3.26",
68
68
  "eslint-plugin-zod-import": "0.3.0",
69
- "knip": "5.64.0",
70
- "lint-staged": "16.2.0",
69
+ "knip": "5.64.1",
70
+ "lint-staged": "16.2.3",
71
71
  "o3-search-mcp": "0.0.9",
72
- "oxlint": "1.18.0",
72
+ "oxlint": "1.19.0",
73
73
  "secretlint": "11.2.4",
74
74
  "simple-git-hooks": "2.13.1",
75
75
  "sort-package-json": "3.4.0",
76
76
  "tsup": "8.5.0",
77
- "tsx": "4.20.5",
78
- "typescript": "5.9.2",
79
- "typescript-eslint": "8.44.1",
77
+ "tsx": "4.20.6",
78
+ "typescript": "5.9.3",
79
+ "typescript-eslint": "8.45.0",
80
80
  "vitest": "3.2.4"
81
81
  },
82
82
  "engines": {