rulesync 0.57.0 → 0.58.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (5) hide show
  1. package/README.ja.md +171 -18
  2. package/README.md +67 -727
  3. package/dist/index.cjs +447 -212
  4. package/dist/index.js +447 -212
  5. package/package.json +19 -19
package/dist/index.cjs CHANGED
@@ -771,132 +771,141 @@ var ClaudeSettingsSchema = import_mini2.z.looseObject({
771
771
  )
772
772
  });
773
773
 
774
- // src/types/config.ts
774
+ // src/types/commands.ts
775
775
  var import_mini3 = require("zod/mini");
776
+ var CommandFrontmatterSchema = import_mini3.z.object({
777
+ description: import_mini3.z.optional(import_mini3.z.string())
778
+ });
779
+
780
+ // src/types/config.ts
781
+ var import_mini4 = require("zod/mini");
776
782
  init_tool_targets();
777
- var ConfigSchema = import_mini3.z.object({
778
- aiRulesDir: import_mini3.z.string(),
779
- outputPaths: import_mini3.z.record(ToolTargetSchema, import_mini3.z.string()),
780
- watchEnabled: import_mini3.z.boolean(),
781
- defaultTargets: ToolTargetsSchema
783
+ var ConfigSchema = import_mini4.z.object({
784
+ aiRulesDir: import_mini4.z.string(),
785
+ outputPaths: import_mini4.z.record(ToolTargetSchema, import_mini4.z.string()),
786
+ watchEnabled: import_mini4.z.boolean(),
787
+ defaultTargets: ToolTargetsSchema,
788
+ claudecodeCommands: import_mini4.z.optional(import_mini4.z.string()),
789
+ geminicliCommands: import_mini4.z.optional(import_mini4.z.string())
782
790
  });
783
791
 
784
792
  // src/types/config-options.ts
785
- var import_mini4 = require("zod/mini");
793
+ var import_mini5 = require("zod/mini");
786
794
  init_tool_targets();
787
- var OutputPathsSchema = import_mini4.z.object({
788
- augmentcode: import_mini4.z.optional(import_mini4.z.string()),
789
- "augmentcode-legacy": import_mini4.z.optional(import_mini4.z.string()),
790
- copilot: import_mini4.z.optional(import_mini4.z.string()),
791
- cursor: import_mini4.z.optional(import_mini4.z.string()),
792
- cline: import_mini4.z.optional(import_mini4.z.string()),
793
- claudecode: import_mini4.z.optional(import_mini4.z.string()),
794
- codexcli: import_mini4.z.optional(import_mini4.z.string()),
795
- roo: import_mini4.z.optional(import_mini4.z.string()),
796
- geminicli: import_mini4.z.optional(import_mini4.z.string()),
797
- kiro: import_mini4.z.optional(import_mini4.z.string()),
798
- junie: import_mini4.z.optional(import_mini4.z.string()),
799
- windsurf: import_mini4.z.optional(import_mini4.z.string())
795
+ var OutputPathsSchema = import_mini5.z.object({
796
+ augmentcode: import_mini5.z.optional(import_mini5.z.string()),
797
+ "augmentcode-legacy": import_mini5.z.optional(import_mini5.z.string()),
798
+ copilot: import_mini5.z.optional(import_mini5.z.string()),
799
+ cursor: import_mini5.z.optional(import_mini5.z.string()),
800
+ cline: import_mini5.z.optional(import_mini5.z.string()),
801
+ claudecode: import_mini5.z.optional(import_mini5.z.string()),
802
+ codexcli: import_mini5.z.optional(import_mini5.z.string()),
803
+ roo: import_mini5.z.optional(import_mini5.z.string()),
804
+ geminicli: import_mini5.z.optional(import_mini5.z.string()),
805
+ kiro: import_mini5.z.optional(import_mini5.z.string()),
806
+ junie: import_mini5.z.optional(import_mini5.z.string()),
807
+ windsurf: import_mini5.z.optional(import_mini5.z.string())
800
808
  });
801
- var ConfigOptionsSchema = import_mini4.z.object({
802
- aiRulesDir: import_mini4.z.optional(import_mini4.z.string()),
803
- outputPaths: import_mini4.z.optional(OutputPathsSchema),
804
- watchEnabled: import_mini4.z.optional(import_mini4.z.boolean()),
805
- defaultTargets: import_mini4.z.optional(ToolTargetsSchema),
806
- targets: import_mini4.z.optional(import_mini4.z.array(ToolTargetSchema)),
807
- exclude: import_mini4.z.optional(import_mini4.z.array(ToolTargetSchema)),
808
- verbose: import_mini4.z.optional(import_mini4.z.boolean()),
809
- delete: import_mini4.z.optional(import_mini4.z.boolean()),
810
- baseDir: import_mini4.z.optional(import_mini4.z.union([import_mini4.z.string(), import_mini4.z.array(import_mini4.z.string())])),
811
- watch: import_mini4.z.optional(
812
- import_mini4.z.object({
813
- enabled: import_mini4.z.optional(import_mini4.z.boolean()),
814
- interval: import_mini4.z.optional(import_mini4.z.number()),
815
- ignore: import_mini4.z.optional(import_mini4.z.array(import_mini4.z.string()))
809
+ var ConfigOptionsSchema = import_mini5.z.object({
810
+ aiRulesDir: import_mini5.z.optional(import_mini5.z.string()),
811
+ outputPaths: import_mini5.z.optional(OutputPathsSchema),
812
+ watchEnabled: import_mini5.z.optional(import_mini5.z.boolean()),
813
+ defaultTargets: import_mini5.z.optional(ToolTargetsSchema),
814
+ targets: import_mini5.z.optional(import_mini5.z.array(ToolTargetSchema)),
815
+ exclude: import_mini5.z.optional(import_mini5.z.array(ToolTargetSchema)),
816
+ verbose: import_mini5.z.optional(import_mini5.z.boolean()),
817
+ delete: import_mini5.z.optional(import_mini5.z.boolean()),
818
+ baseDir: import_mini5.z.optional(import_mini5.z.union([import_mini5.z.string(), import_mini5.z.array(import_mini5.z.string())])),
819
+ watch: import_mini5.z.optional(
820
+ import_mini5.z.object({
821
+ enabled: import_mini5.z.optional(import_mini5.z.boolean()),
822
+ interval: import_mini5.z.optional(import_mini5.z.number()),
823
+ ignore: import_mini5.z.optional(import_mini5.z.array(import_mini5.z.string()))
816
824
  })
817
825
  )
818
826
  });
819
- var MergedConfigSchema = import_mini4.z.object({
820
- aiRulesDir: import_mini4.z.string(),
821
- outputPaths: import_mini4.z.record(ToolTargetSchema, import_mini4.z.string()),
822
- watchEnabled: import_mini4.z.boolean(),
827
+ var MergedConfigSchema = import_mini5.z.object({
828
+ aiRulesDir: import_mini5.z.string(),
829
+ outputPaths: import_mini5.z.record(ToolTargetSchema, import_mini5.z.string()),
830
+ watchEnabled: import_mini5.z.boolean(),
823
831
  defaultTargets: ToolTargetsSchema,
824
- targets: import_mini4.z.optional(import_mini4.z.array(ToolTargetSchema)),
825
- exclude: import_mini4.z.optional(import_mini4.z.array(ToolTargetSchema)),
826
- verbose: import_mini4.z.optional(import_mini4.z.boolean()),
827
- delete: import_mini4.z.optional(import_mini4.z.boolean()),
828
- baseDir: import_mini4.z.optional(import_mini4.z.union([import_mini4.z.string(), import_mini4.z.array(import_mini4.z.string())])),
829
- configPath: import_mini4.z.optional(import_mini4.z.string()),
830
- watch: import_mini4.z.optional(
831
- import_mini4.z.object({
832
- enabled: import_mini4.z.optional(import_mini4.z.boolean()),
833
- interval: import_mini4.z.optional(import_mini4.z.number()),
834
- ignore: import_mini4.z.optional(import_mini4.z.array(import_mini4.z.string()))
832
+ targets: import_mini5.z.optional(import_mini5.z.array(ToolTargetSchema)),
833
+ exclude: import_mini5.z.optional(import_mini5.z.array(ToolTargetSchema)),
834
+ verbose: import_mini5.z.optional(import_mini5.z.boolean()),
835
+ delete: import_mini5.z.optional(import_mini5.z.boolean()),
836
+ baseDir: import_mini5.z.optional(import_mini5.z.union([import_mini5.z.string(), import_mini5.z.array(import_mini5.z.string())])),
837
+ configPath: import_mini5.z.optional(import_mini5.z.string()),
838
+ watch: import_mini5.z.optional(
839
+ import_mini5.z.object({
840
+ enabled: import_mini5.z.optional(import_mini5.z.boolean()),
841
+ interval: import_mini5.z.optional(import_mini5.z.number()),
842
+ ignore: import_mini5.z.optional(import_mini5.z.array(import_mini5.z.string()))
835
843
  })
836
844
  )
837
845
  });
838
846
 
839
847
  // src/types/mcp.ts
840
- var import_mini5 = require("zod/mini");
848
+ var import_mini6 = require("zod/mini");
841
849
  init_tool_targets();
842
- var McpTransportTypeSchema = import_mini5.z.enum(["stdio", "sse", "http"]);
843
- var McpServerBaseSchema = import_mini5.z.object({
844
- command: import_mini5.z.optional(import_mini5.z.string()),
845
- args: import_mini5.z.optional(import_mini5.z.array(import_mini5.z.string())),
846
- url: import_mini5.z.optional(import_mini5.z.string()),
847
- httpUrl: import_mini5.z.optional(import_mini5.z.string()),
848
- env: import_mini5.z.optional(import_mini5.z.record(import_mini5.z.string(), import_mini5.z.string())),
849
- disabled: import_mini5.z.optional(import_mini5.z.boolean()),
850
- networkTimeout: import_mini5.z.optional(import_mini5.z.number()),
851
- timeout: import_mini5.z.optional(import_mini5.z.number()),
852
- trust: import_mini5.z.optional(import_mini5.z.boolean()),
853
- cwd: import_mini5.z.optional(import_mini5.z.string()),
854
- transport: import_mini5.z.optional(McpTransportTypeSchema),
855
- type: import_mini5.z.optional(import_mini5.z.enum(["sse", "streamable-http"])),
856
- alwaysAllow: import_mini5.z.optional(import_mini5.z.array(import_mini5.z.string())),
857
- tools: import_mini5.z.optional(import_mini5.z.array(import_mini5.z.string())),
858
- kiroAutoApprove: import_mini5.z.optional(import_mini5.z.array(import_mini5.z.string())),
859
- kiroAutoBlock: import_mini5.z.optional(import_mini5.z.array(import_mini5.z.string())),
860
- headers: import_mini5.z.optional(import_mini5.z.record(import_mini5.z.string(), import_mini5.z.string()))
850
+ var McpTransportTypeSchema = import_mini6.z.enum(["stdio", "sse", "http"]);
851
+ var McpServerBaseSchema = import_mini6.z.object({
852
+ command: import_mini6.z.optional(import_mini6.z.string()),
853
+ args: import_mini6.z.optional(import_mini6.z.array(import_mini6.z.string())),
854
+ url: import_mini6.z.optional(import_mini6.z.string()),
855
+ httpUrl: import_mini6.z.optional(import_mini6.z.string()),
856
+ env: import_mini6.z.optional(import_mini6.z.record(import_mini6.z.string(), import_mini6.z.string())),
857
+ disabled: import_mini6.z.optional(import_mini6.z.boolean()),
858
+ networkTimeout: import_mini6.z.optional(import_mini6.z.number()),
859
+ timeout: import_mini6.z.optional(import_mini6.z.number()),
860
+ trust: import_mini6.z.optional(import_mini6.z.boolean()),
861
+ cwd: import_mini6.z.optional(import_mini6.z.string()),
862
+ transport: import_mini6.z.optional(McpTransportTypeSchema),
863
+ type: import_mini6.z.optional(import_mini6.z.enum(["sse", "streamable-http"])),
864
+ alwaysAllow: import_mini6.z.optional(import_mini6.z.array(import_mini6.z.string())),
865
+ tools: import_mini6.z.optional(import_mini6.z.array(import_mini6.z.string())),
866
+ kiroAutoApprove: import_mini6.z.optional(import_mini6.z.array(import_mini6.z.string())),
867
+ kiroAutoBlock: import_mini6.z.optional(import_mini6.z.array(import_mini6.z.string())),
868
+ headers: import_mini6.z.optional(import_mini6.z.record(import_mini6.z.string(), import_mini6.z.string()))
861
869
  });
862
- var RulesyncMcpServerSchema = import_mini5.z.extend(McpServerBaseSchema, {
863
- targets: import_mini5.z.optional(RulesyncTargetsSchema)
870
+ var RulesyncMcpServerSchema = import_mini6.z.extend(McpServerBaseSchema, {
871
+ targets: import_mini6.z.optional(RulesyncTargetsSchema)
864
872
  });
865
- var McpConfigSchema = import_mini5.z.object({
866
- mcpServers: import_mini5.z.record(import_mini5.z.string(), McpServerBaseSchema)
873
+ var McpConfigSchema = import_mini6.z.object({
874
+ mcpServers: import_mini6.z.record(import_mini6.z.string(), McpServerBaseSchema)
867
875
  });
868
- var RulesyncMcpConfigSchema = import_mini5.z.object({
869
- mcpServers: import_mini5.z.record(import_mini5.z.string(), RulesyncMcpServerSchema)
876
+ var RulesyncMcpConfigSchema = import_mini6.z.object({
877
+ mcpServers: import_mini6.z.record(import_mini6.z.string(), RulesyncMcpServerSchema)
870
878
  });
871
879
 
872
880
  // src/types/rules.ts
873
- var import_mini6 = require("zod/mini");
881
+ var import_mini7 = require("zod/mini");
874
882
  init_tool_targets();
875
- var RuleFrontmatterSchema = import_mini6.z.object({
876
- root: import_mini6.z.optional(import_mini6.z.boolean()),
877
- targets: import_mini6.z.optional(RulesyncTargetsSchema),
878
- description: import_mini6.z.optional(import_mini6.z.string()),
879
- globs: import_mini6.z.optional(import_mini6.z.array(import_mini6.z.string())),
880
- cursorRuleType: import_mini6.z.optional(import_mini6.z.enum(["always", "manual", "specificFiles", "intelligently"])),
881
- windsurfActivationMode: import_mini6.z.optional(import_mini6.z.enum(["always", "manual", "model-decision", "glob"])),
882
- windsurfOutputFormat: import_mini6.z.optional(import_mini6.z.enum(["single-file", "directory"])),
883
- tags: import_mini6.z.optional(import_mini6.z.array(import_mini6.z.string()))
883
+ var RuleFrontmatterSchema = import_mini7.z.object({
884
+ root: import_mini7.z.optional(import_mini7.z.boolean()),
885
+ targets: import_mini7.z.optional(RulesyncTargetsSchema),
886
+ description: import_mini7.z.optional(import_mini7.z.string()),
887
+ globs: import_mini7.z.optional(import_mini7.z.array(import_mini7.z.string())),
888
+ cursorRuleType: import_mini7.z.optional(import_mini7.z.enum(["always", "manual", "specificFiles", "intelligently"])),
889
+ windsurfActivationMode: import_mini7.z.optional(import_mini7.z.enum(["always", "manual", "model-decision", "glob"])),
890
+ windsurfOutputFormat: import_mini7.z.optional(import_mini7.z.enum(["single-file", "directory"])),
891
+ tags: import_mini7.z.optional(import_mini7.z.array(import_mini7.z.string()))
884
892
  });
885
- var ParsedRuleSchema = import_mini6.z.object({
893
+ var ParsedRuleSchema = import_mini7.z.object({
886
894
  frontmatter: RuleFrontmatterSchema,
887
- content: import_mini6.z.string(),
888
- filename: import_mini6.z.string(),
889
- filepath: import_mini6.z.string()
895
+ content: import_mini7.z.string(),
896
+ filename: import_mini7.z.string(),
897
+ filepath: import_mini7.z.string(),
898
+ type: import_mini7.z.optional(import_mini7.z.enum(["rule", "command"]))
890
899
  });
891
- var GeneratedOutputSchema = import_mini6.z.object({
900
+ var GeneratedOutputSchema = import_mini7.z.object({
892
901
  tool: ToolTargetSchema,
893
- filepath: import_mini6.z.string(),
894
- content: import_mini6.z.string()
902
+ filepath: import_mini7.z.string(),
903
+ content: import_mini7.z.string()
895
904
  });
896
- var GenerateOptionsSchema = import_mini6.z.object({
897
- targetTools: import_mini6.z.optional(ToolTargetsSchema),
898
- outputDir: import_mini6.z.optional(import_mini6.z.string()),
899
- watch: import_mini6.z.optional(import_mini6.z.boolean())
905
+ var GenerateOptionsSchema = import_mini7.z.object({
906
+ targetTools: import_mini7.z.optional(ToolTargetsSchema),
907
+ outputDir: import_mini7.z.optional(import_mini7.z.string()),
908
+ watch: import_mini7.z.optional(import_mini7.z.boolean())
900
909
  });
901
910
 
902
911
  // src/types/index.ts
@@ -1191,28 +1200,6 @@ async function removeClaudeGeneratedFiles() {
1191
1200
  }
1192
1201
  }
1193
1202
 
1194
- // src/utils/rules.ts
1195
- function isToolSpecificRule(rule, targetTool) {
1196
- const filename = rule.filename;
1197
- const toolPatterns = {
1198
- "augmentcode-legacy": /^specification-augmentcode-legacy-/i,
1199
- augmentcode: /^specification-augmentcode-/i,
1200
- copilot: /^specification-copilot-/i,
1201
- cursor: /^specification-cursor-/i,
1202
- cline: /^specification-cline-/i,
1203
- claudecode: /^specification-claudecode-/i,
1204
- roo: /^specification-roo-/i,
1205
- geminicli: /^specification-geminicli-/i,
1206
- kiro: /^specification-kiro-/i
1207
- };
1208
- for (const [tool, pattern] of Object.entries(toolPatterns)) {
1209
- if (pattern.test(filename)) {
1210
- return tool === targetTool;
1211
- }
1212
- }
1213
- return true;
1214
- }
1215
-
1216
1203
  // src/cli/commands/config.ts
1217
1204
  async function configCommand(options = {}) {
1218
1205
  if (options.init) {
@@ -1424,10 +1411,165 @@ export default config;
1424
1411
  }
1425
1412
 
1426
1413
  // src/cli/commands/generate.ts
1427
- var import_node_path10 = require("path");
1414
+ var import_node_path14 = require("path");
1428
1415
 
1429
- // src/generators/ignore/shared-factory.ts
1416
+ // src/core/command-generator.ts
1417
+ var import_node_path6 = require("path");
1418
+
1419
+ // src/generators/commands/claudecode.ts
1430
1420
  var import_node_path3 = require("path");
1421
+ var ClaudeCodeCommandGenerator = class {
1422
+ generate(command, outputDir) {
1423
+ const filepath = this.getOutputPath(command.filename, outputDir);
1424
+ const frontmatter = ["---"];
1425
+ if (command.frontmatter.description) {
1426
+ frontmatter.push(`description: ${command.frontmatter.description}`);
1427
+ }
1428
+ frontmatter.push("---");
1429
+ const content = `${frontmatter.join("\n")}
1430
+
1431
+ ${command.content.trim()}
1432
+ `;
1433
+ return {
1434
+ tool: "claudecode",
1435
+ filepath,
1436
+ content
1437
+ };
1438
+ }
1439
+ getOutputPath(filename, baseDir) {
1440
+ const flattenedName = filename.replace(/\//g, "-");
1441
+ return (0, import_node_path3.join)(baseDir, ".claude", "commands", `${flattenedName}.md`);
1442
+ }
1443
+ };
1444
+
1445
+ // src/generators/commands/geminicli.ts
1446
+ var import_node_path4 = require("path");
1447
+ var GeminiCliCommandGenerator = class {
1448
+ generate(command, outputDir) {
1449
+ const filepath = this.getOutputPath(command.filename, outputDir);
1450
+ const convertedContent = this.convertSyntax(command.content);
1451
+ const tomlLines = [];
1452
+ if (command.frontmatter.description) {
1453
+ tomlLines.push(`description = "${this.escapeTomlString(command.frontmatter.description)}"`);
1454
+ tomlLines.push("");
1455
+ }
1456
+ tomlLines.push(`prompt = """${convertedContent}"""`);
1457
+ const content = tomlLines.join("\n") + "\n";
1458
+ return {
1459
+ tool: "geminicli",
1460
+ filepath,
1461
+ content
1462
+ };
1463
+ }
1464
+ getOutputPath(filename, baseDir) {
1465
+ const tomlFilename = filename.replace(/\.md$/, ".toml");
1466
+ const filenameWithExt = tomlFilename.endsWith(".toml") ? tomlFilename : `${tomlFilename}.toml`;
1467
+ return (0, import_node_path4.join)(baseDir, ".gemini", "commands", filenameWithExt);
1468
+ }
1469
+ convertSyntax(content) {
1470
+ let converted = content;
1471
+ converted = converted.replace(/\$ARGUMENTS/g, "{{args}}");
1472
+ converted = converted.replace(/!`([^`]+)`/g, "!{$1}");
1473
+ const atSyntaxMatches = converted.match(/@[^\s]+/g);
1474
+ if (atSyntaxMatches) {
1475
+ console.warn(
1476
+ `\u26A0\uFE0F Warning: @ syntax found (${atSyntaxMatches.join(", ")}). Gemini CLI does not support file content injection. Consider using shell commands or remove these references.`
1477
+ );
1478
+ }
1479
+ return converted.trim();
1480
+ }
1481
+ escapeTomlString(str) {
1482
+ return str.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/\t/g, "\\t");
1483
+ }
1484
+ };
1485
+
1486
+ // src/generators/commands/index.ts
1487
+ var commandGenerators = {
1488
+ claudecode: new ClaudeCodeCommandGenerator(),
1489
+ geminicli: new GeminiCliCommandGenerator()
1490
+ };
1491
+ function getCommandGenerator(tool) {
1492
+ return commandGenerators[tool];
1493
+ }
1494
+
1495
+ // src/core/command-parser.ts
1496
+ var import_node_path5 = require("path");
1497
+ var import_gray_matter = __toESM(require("gray-matter"), 1);
1498
+ async function parseCommandsFromDirectory(commandsDir) {
1499
+ const commandFiles = await findFiles(commandsDir, ".md");
1500
+ const commands = [];
1501
+ const errors = [];
1502
+ for (const filepath of commandFiles) {
1503
+ try {
1504
+ const command = await parseCommandFile(filepath);
1505
+ commands.push(command);
1506
+ } catch (error) {
1507
+ const errorMessage = error instanceof Error ? error.message : String(error);
1508
+ errors.push(`Failed to parse command file ${filepath}: ${errorMessage}`);
1509
+ }
1510
+ }
1511
+ if (errors.length > 0) {
1512
+ console.warn(`\u26A0\uFE0F Command parsing errors:
1513
+ ${errors.join("\n")}`);
1514
+ }
1515
+ return commands;
1516
+ }
1517
+ async function parseCommandFile(filepath) {
1518
+ const content = await readFileContent(filepath);
1519
+ const parsed = (0, import_gray_matter.default)(content);
1520
+ try {
1521
+ const validatedData = CommandFrontmatterSchema.parse(parsed.data);
1522
+ const filename = (0, import_node_path5.basename)(filepath, ".md");
1523
+ return {
1524
+ frontmatter: {
1525
+ description: validatedData.description
1526
+ },
1527
+ content: parsed.content,
1528
+ filename,
1529
+ filepath
1530
+ };
1531
+ } catch (error) {
1532
+ throw new Error(
1533
+ `Invalid frontmatter in ${filepath}: ${error instanceof Error ? error.message : String(error)}`
1534
+ );
1535
+ }
1536
+ }
1537
+
1538
+ // src/core/command-generator.ts
1539
+ async function generateCommands(projectRoot, baseDir, targets) {
1540
+ const commandsDir = (0, import_node_path6.join)(projectRoot, ".rulesync", "commands");
1541
+ if (!await fileExists(commandsDir)) {
1542
+ return [];
1543
+ }
1544
+ const commands = await parseCommandsFromDirectory(commandsDir);
1545
+ if (commands.length === 0) {
1546
+ return [];
1547
+ }
1548
+ const outputs = [];
1549
+ const outputDir = baseDir || projectRoot;
1550
+ const supportedTargets = targets.filter((target) => ["claudecode", "geminicli"].includes(target));
1551
+ for (const target of supportedTargets) {
1552
+ const generator = getCommandGenerator(target);
1553
+ if (!generator) {
1554
+ continue;
1555
+ }
1556
+ for (const command of commands) {
1557
+ try {
1558
+ const output = generator.generate(command, outputDir);
1559
+ outputs.push(output);
1560
+ } catch (error) {
1561
+ const errorMessage = error instanceof Error ? error.message : String(error);
1562
+ console.error(
1563
+ `\u274C Failed to generate ${target} command for ${command.filename}: ${errorMessage}`
1564
+ );
1565
+ }
1566
+ }
1567
+ }
1568
+ return outputs;
1569
+ }
1570
+
1571
+ // src/generators/ignore/shared-factory.ts
1572
+ var import_node_path7 = require("path");
1431
1573
 
1432
1574
  // src/generators/ignore/shared-helpers.ts
1433
1575
  function extractIgnorePatternsFromRules(rules) {
@@ -1545,7 +1687,7 @@ function generateIgnoreFile(rules, config, ignoreConfig, baseDir) {
1545
1687
  const outputs = [];
1546
1688
  const content = generateIgnoreContent(rules, ignoreConfig);
1547
1689
  const outputPath = baseDir || process.cwd();
1548
- const filepath = (0, import_node_path3.join)(outputPath, ignoreConfig.filename);
1690
+ const filepath = (0, import_node_path7.join)(outputPath, ignoreConfig.filename);
1549
1691
  outputs.push({
1550
1692
  tool: ignoreConfig.tool,
1551
1693
  filepath,
@@ -2133,20 +2275,20 @@ function generateWindsurfIgnore(rules, config, baseDir) {
2133
2275
  }
2134
2276
 
2135
2277
  // src/generators/rules/augmentcode.ts
2136
- var import_node_path6 = require("path");
2278
+ var import_node_path10 = require("path");
2137
2279
 
2138
2280
  // src/generators/rules/shared-helpers.ts
2139
- var import_node_path5 = require("path");
2281
+ var import_node_path9 = require("path");
2140
2282
 
2141
2283
  // src/utils/ignore.ts
2142
- var import_node_path4 = require("path");
2284
+ var import_node_path8 = require("path");
2143
2285
  var import_micromatch = __toESM(require("micromatch"), 1);
2144
2286
  var cachedIgnorePatterns = null;
2145
2287
  async function loadIgnorePatterns(baseDir = process.cwd()) {
2146
2288
  if (cachedIgnorePatterns) {
2147
2289
  return cachedIgnorePatterns;
2148
2290
  }
2149
- const ignorePath = (0, import_node_path4.join)(baseDir, ".rulesyncignore");
2291
+ const ignorePath = (0, import_node_path8.join)(baseDir, ".rulesyncignore");
2150
2292
  if (!await fileExists(ignorePath)) {
2151
2293
  cachedIgnorePatterns = { patterns: [] };
2152
2294
  return cachedIgnorePatterns;
@@ -2200,7 +2342,7 @@ function addOutput(outputs, tool, config, baseDir, relativePath, content) {
2200
2342
  const outputDir = resolveOutputDir(config, tool, baseDir);
2201
2343
  outputs.push({
2202
2344
  tool,
2203
- filepath: (0, import_node_path5.join)(outputDir, relativePath),
2345
+ filepath: (0, import_node_path9.join)(outputDir, relativePath),
2204
2346
  content
2205
2347
  });
2206
2348
  }
@@ -2209,7 +2351,7 @@ async function generateRulesConfig(rules, config, generatorConfig, baseDir) {
2209
2351
  for (const rule of rules) {
2210
2352
  const content = generatorConfig.generateContent(rule);
2211
2353
  const outputDir = resolveOutputDir(config, generatorConfig.tool, baseDir);
2212
- const filepath = generatorConfig.pathResolver ? generatorConfig.pathResolver(rule, outputDir) : (0, import_node_path5.join)(outputDir, `${rule.filename}${generatorConfig.fileExtension}`);
2354
+ const filepath = generatorConfig.pathResolver ? generatorConfig.pathResolver(rule, outputDir) : (0, import_node_path9.join)(outputDir, `${rule.filename}${generatorConfig.fileExtension}`);
2213
2355
  outputs.push({
2214
2356
  tool: generatorConfig.tool,
2215
2357
  filepath,
@@ -2217,7 +2359,7 @@ async function generateRulesConfig(rules, config, generatorConfig, baseDir) {
2217
2359
  });
2218
2360
  }
2219
2361
  const ignorePatterns = await loadIgnorePatterns(baseDir);
2220
- if (ignorePatterns.patterns.length > 0) {
2362
+ if (ignorePatterns.patterns.length > 0 && generatorConfig.ignoreFileName) {
2221
2363
  const ignorePath = resolvePath(generatorConfig.ignoreFileName, baseDir);
2222
2364
  const ignoreContent = generateIgnoreFile2(ignorePatterns.patterns, generatorConfig.tool);
2223
2365
  outputs.push({
@@ -2237,7 +2379,7 @@ async function generateComplexRules(rules, config, generatorConfig, baseDir) {
2237
2379
  for (const rule of detailRules) {
2238
2380
  const content = generatorConfig.generateDetailContent(rule);
2239
2381
  const filepath = resolvePath(
2240
- (0, import_node_path5.join)(generatorConfig.detailSubDir, `${rule.filename}.md`),
2382
+ (0, import_node_path9.join)(generatorConfig.detailSubDir, `${rule.filename}.md`),
2241
2383
  baseDir
2242
2384
  );
2243
2385
  outputs.push({
@@ -2258,13 +2400,15 @@ async function generateComplexRules(rules, config, generatorConfig, baseDir) {
2258
2400
  }
2259
2401
  const ignorePatterns = await loadIgnorePatterns(baseDir);
2260
2402
  if (ignorePatterns.patterns.length > 0) {
2261
- const ignorePath = resolvePath(generatorConfig.ignoreFileName, baseDir);
2262
- const ignoreContent = generateIgnoreFile2(ignorePatterns.patterns, generatorConfig.tool);
2263
- outputs.push({
2264
- tool: generatorConfig.tool,
2265
- filepath: ignorePath,
2266
- content: ignoreContent
2267
- });
2403
+ if (generatorConfig.ignoreFileName) {
2404
+ const ignorePath = resolvePath(generatorConfig.ignoreFileName, baseDir);
2405
+ const ignoreContent = generateIgnoreFile2(ignorePatterns.patterns, generatorConfig.tool);
2406
+ outputs.push({
2407
+ tool: generatorConfig.tool,
2408
+ filepath: ignorePath,
2409
+ content: ignoreContent
2410
+ });
2411
+ }
2268
2412
  if (generatorConfig.updateAdditionalConfig) {
2269
2413
  const additionalOutputs = await generatorConfig.updateAdditionalConfig(
2270
2414
  ignorePatterns.patterns,
@@ -2298,7 +2442,7 @@ async function generateAugmentcodeConfig(rules, config, baseDir) {
2298
2442
  "augmentcode",
2299
2443
  config,
2300
2444
  baseDir,
2301
- (0, import_node_path6.join)(".augment", "rules", `${rule.filename}.md`),
2445
+ (0, import_node_path10.join)(".augment", "rules", `${rule.filename}.md`),
2302
2446
  generateRuleFile(rule)
2303
2447
  );
2304
2448
  });
@@ -2351,19 +2495,19 @@ function generateLegacyGuidelinesFile(allRules) {
2351
2495
  }
2352
2496
 
2353
2497
  // src/generators/rules/claudecode.ts
2354
- var import_node_path7 = require("path");
2498
+ var import_node_path11 = require("path");
2355
2499
  async function generateClaudecodeConfig(rules, config, baseDir) {
2356
2500
  const generatorConfig = {
2357
2501
  tool: "claudecode",
2358
2502
  fileExtension: ".md",
2359
- ignoreFileName: ".aiignore",
2503
+ // ignoreFileName omitted - Claude Code uses settings.json permissions.deny instead of ignore files
2360
2504
  generateContent: generateMemoryFile,
2361
2505
  generateRootContent: generateClaudeMarkdown,
2362
2506
  rootFilePath: "CLAUDE.md",
2363
2507
  generateDetailContent: generateMemoryFile,
2364
2508
  detailSubDir: ".claude/memories",
2365
2509
  updateAdditionalConfig: async (ignorePatterns, baseDir2) => {
2366
- const settingsPath = resolvePath((0, import_node_path7.join)(".claude", "settings.json"), baseDir2);
2510
+ const settingsPath = resolvePath((0, import_node_path11.join)(".claude", "settings.json"), baseDir2);
2367
2511
  await updateClaudeSettings(settingsPath, ignorePatterns);
2368
2512
  return [];
2369
2513
  }
@@ -2427,7 +2571,7 @@ async function updateClaudeSettings(settingsPath, ignorePatterns) {
2427
2571
  }
2428
2572
 
2429
2573
  // src/generators/rules/generator-registry.ts
2430
- var import_node_path8 = require("path");
2574
+ var import_node_path12 = require("path");
2431
2575
  function determineCursorRuleType(frontmatter) {
2432
2576
  if (frontmatter.cursorRuleType) {
2433
2577
  return frontmatter.cursorRuleType;
@@ -2507,7 +2651,7 @@ var GENERATOR_REGISTRY = {
2507
2651
  },
2508
2652
  pathResolver: (rule, outputDir) => {
2509
2653
  const baseFilename = rule.filename.replace(/\.md$/, "");
2510
- return (0, import_node_path8.join)(outputDir, `${baseFilename}.instructions.md`);
2654
+ return (0, import_node_path12.join)(outputDir, `${baseFilename}.instructions.md`);
2511
2655
  }
2512
2656
  },
2513
2657
  cursor: {
@@ -2547,7 +2691,7 @@ var GENERATOR_REGISTRY = {
2547
2691
  return lines.join("\n");
2548
2692
  },
2549
2693
  pathResolver: (rule, outputDir) => {
2550
- return (0, import_node_path8.join)(outputDir, `${rule.filename}.mdc`);
2694
+ return (0, import_node_path12.join)(outputDir, `${rule.filename}.mdc`);
2551
2695
  }
2552
2696
  },
2553
2697
  codexcli: {
@@ -2583,10 +2727,10 @@ var GENERATOR_REGISTRY = {
2583
2727
  pathResolver: (rule, outputDir) => {
2584
2728
  const outputFormat = rule.frontmatter.windsurfOutputFormat || "directory";
2585
2729
  if (outputFormat === "single-file") {
2586
- return (0, import_node_path8.join)(outputDir, ".windsurf-rules");
2730
+ return (0, import_node_path12.join)(outputDir, ".windsurf-rules");
2587
2731
  } else {
2588
- const rulesDir = (0, import_node_path8.join)(outputDir, ".windsurf", "rules");
2589
- return (0, import_node_path8.join)(rulesDir, `${rule.filename}.md`);
2732
+ const rulesDir = (0, import_node_path12.join)(outputDir, ".windsurf", "rules");
2733
+ return (0, import_node_path12.join)(rulesDir, `${rule.filename}.md`);
2590
2734
  }
2591
2735
  }
2592
2736
  },
@@ -2850,7 +2994,7 @@ function filterRulesForTool(rules, tool, config) {
2850
2994
  if (!targets.includes(tool)) {
2851
2995
  return false;
2852
2996
  }
2853
- return isToolSpecificRule(rule, tool);
2997
+ return true;
2854
2998
  });
2855
2999
  }
2856
3000
  async function generateForTool(tool, rules, config, baseDir) {
@@ -2905,8 +3049,8 @@ async function generateForTool(tool, rules, config, baseDir) {
2905
3049
  }
2906
3050
 
2907
3051
  // src/core/parser.ts
2908
- var import_node_path9 = require("path");
2909
- var import_gray_matter = __toESM(require("gray-matter"), 1);
3052
+ var import_node_path13 = require("path");
3053
+ var import_gray_matter2 = __toESM(require("gray-matter"), 1);
2910
3054
  async function parseRulesFromDirectory(aiRulesDir) {
2911
3055
  const ignorePatterns = await loadIgnorePatterns();
2912
3056
  const allRuleFiles = await findFiles(aiRulesDir, ".md");
@@ -2940,7 +3084,7 @@ ${errors.join("\n")}`);
2940
3084
  }
2941
3085
  async function parseRuleFile(filepath) {
2942
3086
  const content = await readFileContent(filepath);
2943
- const parsed = (0, import_gray_matter.default)(content);
3087
+ const parsed = (0, import_gray_matter2.default)(content);
2944
3088
  try {
2945
3089
  const validatedData = RuleFrontmatterSchema.parse(parsed.data);
2946
3090
  const frontmatter = {
@@ -2959,7 +3103,7 @@ async function parseRuleFile(filepath) {
2959
3103
  },
2960
3104
  ...validatedData.tags !== void 0 && { tags: validatedData.tags }
2961
3105
  };
2962
- const filename = (0, import_node_path9.basename)(filepath, ".md");
3106
+ const filename = (0, import_node_path13.basename)(filepath, ".md");
2963
3107
  return {
2964
3108
  frontmatter,
2965
3109
  content: parsed.content,
@@ -3262,12 +3406,12 @@ async function generateCommand(options = {}) {
3262
3406
  for (const tool of targetTools) {
3263
3407
  switch (tool) {
3264
3408
  case "augmentcode":
3265
- deleteTasks.push(removeDirectory((0, import_node_path10.join)(".augment", "rules")));
3266
- deleteTasks.push(removeDirectory((0, import_node_path10.join)(".augment", "ignore")));
3409
+ deleteTasks.push(removeDirectory((0, import_node_path14.join)(".augment", "rules")));
3410
+ deleteTasks.push(removeDirectory((0, import_node_path14.join)(".augment", "ignore")));
3267
3411
  break;
3268
3412
  case "augmentcode-legacy":
3269
3413
  deleteTasks.push(removeClaudeGeneratedFiles());
3270
- deleteTasks.push(removeDirectory((0, import_node_path10.join)(".augment", "ignore")));
3414
+ deleteTasks.push(removeDirectory((0, import_node_path14.join)(".augment", "ignore")));
3271
3415
  break;
3272
3416
  case "copilot":
3273
3417
  deleteTasks.push(removeDirectory(config.outputPaths.copilot));
@@ -3280,12 +3424,14 @@ async function generateCommand(options = {}) {
3280
3424
  break;
3281
3425
  case "claudecode":
3282
3426
  deleteTasks.push(removeClaudeGeneratedFiles());
3427
+ deleteTasks.push(removeDirectory((0, import_node_path14.join)(".claude", "commands")));
3283
3428
  break;
3284
3429
  case "roo":
3285
3430
  deleteTasks.push(removeDirectory(config.outputPaths.roo));
3286
3431
  break;
3287
3432
  case "geminicli":
3288
3433
  deleteTasks.push(removeDirectory(config.outputPaths.geminicli));
3434
+ deleteTasks.push(removeDirectory((0, import_node_path14.join)(".gemini", "commands")));
3289
3435
  break;
3290
3436
  case "kiro":
3291
3437
  deleteTasks.push(removeDirectory(config.outputPaths.kiro));
@@ -3350,11 +3496,37 @@ Generating configurations for base directory: ${baseDir}`);
3350
3496
  }
3351
3497
  }
3352
3498
  }
3353
- const totalGenerated = totalOutputs + totalMcpOutputs;
3499
+ if (config.verbose) {
3500
+ console.log("\nGenerating command files...");
3501
+ }
3502
+ let totalCommandOutputs = 0;
3503
+ for (const baseDir of baseDirs) {
3504
+ const commandResults = await generateCommands(
3505
+ process.cwd(),
3506
+ baseDir === process.cwd() ? void 0 : baseDir,
3507
+ config.defaultTargets
3508
+ );
3509
+ if (commandResults.length === 0) {
3510
+ if (config.verbose) {
3511
+ console.log(`No commands found for ${baseDir}`);
3512
+ }
3513
+ continue;
3514
+ }
3515
+ for (const result of commandResults) {
3516
+ await writeFileContent(result.filepath, result.content);
3517
+ console.log(`\u2705 Generated ${result.tool} command: ${result.filepath}`);
3518
+ totalCommandOutputs++;
3519
+ }
3520
+ }
3521
+ const totalGenerated = totalOutputs + totalMcpOutputs + totalCommandOutputs;
3354
3522
  if (totalGenerated > 0) {
3523
+ const parts = [];
3524
+ if (totalOutputs > 0) parts.push(`${totalOutputs} configurations`);
3525
+ if (totalMcpOutputs > 0) parts.push(`${totalMcpOutputs} MCP configurations`);
3526
+ if (totalCommandOutputs > 0) parts.push(`${totalCommandOutputs} commands`);
3355
3527
  console.log(
3356
3528
  `
3357
- \u{1F389} All done! Generated ${totalGenerated} file(s) total (${totalOutputs} configurations + ${totalMcpOutputs} MCP configurations)`
3529
+ \u{1F389} All done! Generated ${totalGenerated} file(s) total (${parts.join(" + ")})`
3358
3530
  );
3359
3531
  }
3360
3532
  } catch (error) {
@@ -3365,9 +3537,9 @@ Generating configurations for base directory: ${baseDir}`);
3365
3537
 
3366
3538
  // src/cli/commands/gitignore.ts
3367
3539
  var import_node_fs2 = require("fs");
3368
- var import_node_path11 = require("path");
3540
+ var import_node_path15 = require("path");
3369
3541
  var gitignoreCommand = async () => {
3370
- const gitignorePath = (0, import_node_path11.join)(process.cwd(), ".gitignore");
3542
+ const gitignorePath = (0, import_node_path15.join)(process.cwd(), ".gitignore");
3371
3543
  const rulesFilesToIgnore = [
3372
3544
  "# Generated by rulesync - AI tool configuration files",
3373
3545
  "**/.github/copilot-instructions.md",
@@ -3378,6 +3550,7 @@ var gitignoreCommand = async () => {
3378
3550
  "**/.clineignore",
3379
3551
  "**/CLAUDE.md",
3380
3552
  "**/.claude/memories/",
3553
+ "**/.claude/commands/",
3381
3554
  "**/codex.md",
3382
3555
  "**/.codexignore",
3383
3556
  "**/.roo/rules/",
@@ -3385,6 +3558,7 @@ var gitignoreCommand = async () => {
3385
3558
  "**/.copilotignore",
3386
3559
  "**/GEMINI.md",
3387
3560
  "**/.gemini/memories/",
3561
+ "**/.gemini/commands/",
3388
3562
  "**/.aiexclude",
3389
3563
  "**/.aiignore",
3390
3564
  "**/.augmentignore",
@@ -3431,12 +3605,12 @@ ${linesToAdd.join("\n")}
3431
3605
  };
3432
3606
 
3433
3607
  // src/core/importer.ts
3434
- var import_node_path18 = require("path");
3435
- var import_gray_matter6 = __toESM(require("gray-matter"), 1);
3608
+ var import_node_path22 = require("path");
3609
+ var import_gray_matter7 = __toESM(require("gray-matter"), 1);
3436
3610
 
3437
3611
  // src/parsers/augmentcode.ts
3438
- var import_node_path12 = require("path");
3439
- var import_gray_matter2 = __toESM(require("gray-matter"), 1);
3612
+ var import_node_path16 = require("path");
3613
+ var import_gray_matter3 = __toESM(require("gray-matter"), 1);
3440
3614
 
3441
3615
  // src/utils/parser-helpers.ts
3442
3616
  function createParseResult() {
@@ -3484,7 +3658,7 @@ async function parseAugmentcodeLegacyConfiguration(baseDir = process.cwd()) {
3484
3658
  async function parseUnifiedAugmentcode(baseDir, config) {
3485
3659
  const result = createParseResult();
3486
3660
  if (config.rulesDir) {
3487
- const rulesDir = (0, import_node_path12.join)(baseDir, config.rulesDir);
3661
+ const rulesDir = (0, import_node_path16.join)(baseDir, config.rulesDir);
3488
3662
  if (await fileExists(rulesDir)) {
3489
3663
  const rulesResult = await parseAugmentRules(rulesDir, config);
3490
3664
  addRules(result, rulesResult.rules);
@@ -3497,7 +3671,7 @@ async function parseUnifiedAugmentcode(baseDir, config) {
3497
3671
  }
3498
3672
  }
3499
3673
  if (config.legacyFilePath) {
3500
- const legacyPath = (0, import_node_path12.join)(baseDir, config.legacyFilePath);
3674
+ const legacyPath = (0, import_node_path16.join)(baseDir, config.legacyFilePath);
3501
3675
  if (await fileExists(legacyPath)) {
3502
3676
  const legacyResult = await parseAugmentGuidelines(legacyPath, config);
3503
3677
  if (legacyResult.rule) {
@@ -3521,16 +3695,16 @@ async function parseAugmentRules(rulesDir, config) {
3521
3695
  const files = await readdir2(rulesDir);
3522
3696
  for (const file of files) {
3523
3697
  if (file.endsWith(".md") || file.endsWith(".mdc")) {
3524
- const filePath = (0, import_node_path12.join)(rulesDir, file);
3698
+ const filePath = (0, import_node_path16.join)(rulesDir, file);
3525
3699
  try {
3526
3700
  const rawContent = await readFileContent(filePath);
3527
- const parsed = (0, import_gray_matter2.default)(rawContent);
3701
+ const parsed = (0, import_gray_matter3.default)(rawContent);
3528
3702
  const frontmatterData = parsed.data;
3529
3703
  const ruleType = frontmatterData.type || "manual";
3530
3704
  const description = frontmatterData.description || "";
3531
3705
  const tags = Array.isArray(frontmatterData.tags) ? frontmatterData.tags : void 0;
3532
3706
  const isRoot = ruleType === "always";
3533
- const filename = (0, import_node_path12.basename)(file, file.endsWith(".mdc") ? ".mdc" : ".md");
3707
+ const filename = (0, import_node_path16.basename)(file, file.endsWith(".mdc") ? ".mdc" : ".md");
3534
3708
  const frontmatter = {
3535
3709
  root: isRoot,
3536
3710
  targets: [config.targetName],
@@ -3588,8 +3762,8 @@ async function parseAugmentGuidelines(guidelinesPath, config) {
3588
3762
  }
3589
3763
 
3590
3764
  // src/parsers/shared-helpers.ts
3591
- var import_node_path13 = require("path");
3592
- var import_gray_matter3 = __toESM(require("gray-matter"), 1);
3765
+ var import_node_path17 = require("path");
3766
+ var import_gray_matter4 = __toESM(require("gray-matter"), 1);
3593
3767
  async function parseConfigurationFiles(baseDir = process.cwd(), config) {
3594
3768
  const errors = [];
3595
3769
  const rules = [];
@@ -3602,7 +3776,7 @@ async function parseConfigurationFiles(baseDir = process.cwd(), config) {
3602
3776
  let content;
3603
3777
  let frontmatter;
3604
3778
  if (mainFile.useFrontmatter) {
3605
- const parsed = (0, import_gray_matter3.default)(rawContent);
3779
+ const parsed = (0, import_gray_matter4.default)(rawContent);
3606
3780
  content = parsed.content.trim();
3607
3781
  const parsedFrontmatter = parsed.data;
3608
3782
  frontmatter = {
@@ -3644,14 +3818,14 @@ async function parseConfigurationFiles(baseDir = process.cwd(), config) {
3644
3818
  const files = await readdir2(dirPath);
3645
3819
  for (const file of files) {
3646
3820
  if (file.endsWith(dirConfig.filePattern)) {
3647
- const filePath = (0, import_node_path13.join)(dirPath, file);
3821
+ const filePath = (0, import_node_path17.join)(dirPath, file);
3648
3822
  const fileResult = await safeAsyncOperation(async () => {
3649
3823
  const rawContent = await readFileContent(filePath);
3650
3824
  let content;
3651
3825
  let frontmatter;
3652
3826
  const filename = file.replace(new RegExp(`\\${dirConfig.filePattern}$`), "");
3653
3827
  if (dirConfig.filePattern === ".instructions.md") {
3654
- const parsed = (0, import_gray_matter3.default)(rawContent);
3828
+ const parsed = (0, import_gray_matter4.default)(rawContent);
3655
3829
  content = parsed.content.trim();
3656
3830
  const parsedFrontmatter = parsed.data;
3657
3831
  frontmatter = {
@@ -3717,6 +3891,13 @@ async function parseMemoryBasedConfiguration(baseDir = process.cwd(), config) {
3717
3891
  const memoryRules = await parseMemoryFiles(memoryDir, config);
3718
3892
  rules.push(...memoryRules);
3719
3893
  }
3894
+ if (config.commandsDirPath) {
3895
+ const commandsDir = resolvePath(config.commandsDirPath, baseDir);
3896
+ if (await fileExists(commandsDir)) {
3897
+ const commandsRules = await parseCommandsFiles(commandsDir, config);
3898
+ rules.push(...commandsRules);
3899
+ }
3900
+ }
3720
3901
  const settingsPath = resolvePath(config.settingsPath, baseDir);
3721
3902
  if (await fileExists(settingsPath)) {
3722
3903
  const settingsResult = await parseSettingsFile(settingsPath, config.tool);
@@ -3782,10 +3963,10 @@ async function parseMemoryFiles(memoryDir, config) {
3782
3963
  const files = await readdir2(memoryDir);
3783
3964
  for (const file of files) {
3784
3965
  if (file.endsWith(".md")) {
3785
- const filePath = (0, import_node_path13.join)(memoryDir, file);
3966
+ const filePath = (0, import_node_path17.join)(memoryDir, file);
3786
3967
  const content = await readFileContent(filePath);
3787
3968
  if (content.trim()) {
3788
- const filename = (0, import_node_path13.basename)(file, ".md");
3969
+ const filename = (0, import_node_path17.basename)(file, ".md");
3789
3970
  const frontmatter = {
3790
3971
  root: false,
3791
3972
  targets: [config.tool],
@@ -3805,6 +3986,54 @@ async function parseMemoryFiles(memoryDir, config) {
3805
3986
  }
3806
3987
  return rules;
3807
3988
  }
3989
+ async function parseCommandsFiles(commandsDir, config) {
3990
+ const rules = [];
3991
+ try {
3992
+ const { readdir: readdir2 } = await import("fs/promises");
3993
+ const files = await readdir2(commandsDir);
3994
+ for (const file of files) {
3995
+ if (file.endsWith(".md")) {
3996
+ const filePath = (0, import_node_path17.join)(commandsDir, file);
3997
+ const content = await readFileContent(filePath);
3998
+ if (content.trim()) {
3999
+ const filename = (0, import_node_path17.basename)(file, ".md");
4000
+ let frontmatter;
4001
+ let ruleContent;
4002
+ try {
4003
+ const parsed = (0, import_gray_matter4.default)(content);
4004
+ ruleContent = parsed.content.trim();
4005
+ const parsedFrontmatter = parsed.data;
4006
+ frontmatter = {
4007
+ root: false,
4008
+ targets: [config.tool],
4009
+ description: parsedFrontmatter.description || `Command: ${filename}`,
4010
+ globs: ["**/*"]
4011
+ };
4012
+ } catch {
4013
+ ruleContent = content.trim();
4014
+ frontmatter = {
4015
+ root: false,
4016
+ targets: [config.tool],
4017
+ description: `Command: ${filename}`,
4018
+ globs: ["**/*"]
4019
+ };
4020
+ }
4021
+ if (ruleContent) {
4022
+ rules.push({
4023
+ frontmatter,
4024
+ content: ruleContent,
4025
+ filename,
4026
+ filepath: filePath,
4027
+ type: "command"
4028
+ });
4029
+ }
4030
+ }
4031
+ }
4032
+ }
4033
+ } catch {
4034
+ }
4035
+ return rules;
4036
+ }
3808
4037
  async function parseSettingsFile(settingsPath, tool) {
3809
4038
  const errors = [];
3810
4039
  let ignorePatterns;
@@ -3852,7 +4081,8 @@ async function parseClaudeConfiguration(baseDir = process.cwd()) {
3852
4081
  settingsPath: ".claude/settings.json",
3853
4082
  mainDescription: "Main Claude Code configuration",
3854
4083
  memoryDescription: "Memory file",
3855
- filenamePrefix: "claude"
4084
+ filenamePrefix: "claude",
4085
+ commandsDirPath: ".claude/commands"
3856
4086
  });
3857
4087
  }
3858
4088
 
@@ -3877,7 +4107,7 @@ async function parseClineConfiguration(baseDir = process.cwd()) {
3877
4107
  }
3878
4108
 
3879
4109
  // src/parsers/codexcli.ts
3880
- var import_node_path14 = require("path");
4110
+ var import_node_path18 = require("path");
3881
4111
 
3882
4112
  // src/parsers/copilot.ts
3883
4113
  async function parseCopilotConfiguration(baseDir = process.cwd()) {
@@ -3900,10 +4130,10 @@ async function parseCopilotConfiguration(baseDir = process.cwd()) {
3900
4130
  }
3901
4131
 
3902
4132
  // src/parsers/cursor.ts
3903
- var import_node_path15 = require("path");
3904
- var import_gray_matter4 = __toESM(require("gray-matter"), 1);
4133
+ var import_node_path19 = require("path");
4134
+ var import_gray_matter5 = __toESM(require("gray-matter"), 1);
3905
4135
  var import_js_yaml = require("js-yaml");
3906
- var import_mini7 = require("zod/mini");
4136
+ var import_mini8 = require("zod/mini");
3907
4137
  var customMatterOptions = {
3908
4138
  engines: {
3909
4139
  yaml: {
@@ -3931,7 +4161,7 @@ var customMatterOptions = {
3931
4161
  }
3932
4162
  };
3933
4163
  function convertCursorMdcFrontmatter(cursorFrontmatter, _filename) {
3934
- const FrontmatterSchema = import_mini7.z.record(import_mini7.z.string(), import_mini7.z.unknown());
4164
+ const FrontmatterSchema = import_mini8.z.record(import_mini8.z.string(), import_mini8.z.unknown());
3935
4165
  const parseResult = FrontmatterSchema.safeParse(cursorFrontmatter);
3936
4166
  if (!parseResult.success) {
3937
4167
  return {
@@ -4025,11 +4255,11 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
4025
4255
  const rules = [];
4026
4256
  let ignorePatterns;
4027
4257
  let mcpServers;
4028
- const cursorFilePath = (0, import_node_path15.join)(baseDir, ".cursorrules");
4258
+ const cursorFilePath = (0, import_node_path19.join)(baseDir, ".cursorrules");
4029
4259
  if (await fileExists(cursorFilePath)) {
4030
4260
  try {
4031
4261
  const rawContent = await readFileContent(cursorFilePath);
4032
- const parsed = (0, import_gray_matter4.default)(rawContent, customMatterOptions);
4262
+ const parsed = (0, import_gray_matter5.default)(rawContent, customMatterOptions);
4033
4263
  const content = parsed.content.trim();
4034
4264
  if (content) {
4035
4265
  const frontmatter = convertCursorMdcFrontmatter(parsed.data, "cursorrules");
@@ -4046,20 +4276,20 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
4046
4276
  errors.push(`Failed to parse .cursorrules file: ${errorMessage}`);
4047
4277
  }
4048
4278
  }
4049
- const cursorRulesDir = (0, import_node_path15.join)(baseDir, ".cursor", "rules");
4279
+ const cursorRulesDir = (0, import_node_path19.join)(baseDir, ".cursor", "rules");
4050
4280
  if (await fileExists(cursorRulesDir)) {
4051
4281
  try {
4052
4282
  const { readdir: readdir2 } = await import("fs/promises");
4053
4283
  const files = await readdir2(cursorRulesDir);
4054
4284
  for (const file of files) {
4055
4285
  if (file.endsWith(".mdc")) {
4056
- const filePath = (0, import_node_path15.join)(cursorRulesDir, file);
4286
+ const filePath = (0, import_node_path19.join)(cursorRulesDir, file);
4057
4287
  try {
4058
4288
  const rawContent = await readFileContent(filePath);
4059
- const parsed = (0, import_gray_matter4.default)(rawContent, customMatterOptions);
4289
+ const parsed = (0, import_gray_matter5.default)(rawContent, customMatterOptions);
4060
4290
  const content = parsed.content.trim();
4061
4291
  if (content) {
4062
- const filename = (0, import_node_path15.basename)(file, ".mdc");
4292
+ const filename = (0, import_node_path19.basename)(file, ".mdc");
4063
4293
  const frontmatter = convertCursorMdcFrontmatter(parsed.data, filename);
4064
4294
  rules.push({
4065
4295
  frontmatter,
@@ -4082,7 +4312,7 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
4082
4312
  if (rules.length === 0) {
4083
4313
  errors.push("No Cursor configuration files found (.cursorrules or .cursor/rules/*.mdc)");
4084
4314
  }
4085
- const cursorIgnorePath = (0, import_node_path15.join)(baseDir, ".cursorignore");
4315
+ const cursorIgnorePath = (0, import_node_path19.join)(baseDir, ".cursorignore");
4086
4316
  if (await fileExists(cursorIgnorePath)) {
4087
4317
  try {
4088
4318
  const content = await readFileContent(cursorIgnorePath);
@@ -4095,7 +4325,7 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
4095
4325
  errors.push(`Failed to parse .cursorignore: ${errorMessage}`);
4096
4326
  }
4097
4327
  }
4098
- const cursorMcpPath = (0, import_node_path15.join)(baseDir, ".cursor", "mcp.json");
4328
+ const cursorMcpPath = (0, import_node_path19.join)(baseDir, ".cursor", "mcp.json");
4099
4329
  if (await fileExists(cursorMcpPath)) {
4100
4330
  try {
4101
4331
  const content = await readFileContent(cursorMcpPath);
@@ -4139,16 +4369,17 @@ async function parseGeminiConfiguration(baseDir = process.cwd()) {
4139
4369
  additionalIgnoreFile: {
4140
4370
  path: ".aiexclude",
4141
4371
  parser: parseAiexclude
4142
- }
4372
+ },
4373
+ commandsDirPath: ".gemini/commands"
4143
4374
  });
4144
4375
  }
4145
4376
 
4146
4377
  // src/parsers/junie.ts
4147
- var import_node_path16 = require("path");
4378
+ var import_node_path20 = require("path");
4148
4379
  async function parseJunieConfiguration(baseDir = process.cwd()) {
4149
4380
  const errors = [];
4150
4381
  const rules = [];
4151
- const guidelinesPath = (0, import_node_path16.join)(baseDir, ".junie", "guidelines.md");
4382
+ const guidelinesPath = (0, import_node_path20.join)(baseDir, ".junie", "guidelines.md");
4152
4383
  if (!await fileExists(guidelinesPath)) {
4153
4384
  errors.push(".junie/guidelines.md file not found");
4154
4385
  return { rules, errors };
@@ -4201,8 +4432,8 @@ async function parseRooConfiguration(baseDir = process.cwd()) {
4201
4432
 
4202
4433
  // src/parsers/windsurf.ts
4203
4434
  var import_promises3 = require("fs/promises");
4204
- var import_node_path17 = require("path");
4205
- var import_gray_matter5 = __toESM(require("gray-matter"), 1);
4435
+ var import_node_path21 = require("path");
4436
+ var import_gray_matter6 = __toESM(require("gray-matter"), 1);
4206
4437
 
4207
4438
  // src/core/importer.ts
4208
4439
  async function importConfiguration(options) {
@@ -4288,7 +4519,7 @@ async function importConfiguration(options) {
4288
4519
  if (rules.length === 0 && !ignorePatterns && !mcpServers) {
4289
4520
  return { success: false, rulesCreated: 0, errors };
4290
4521
  }
4291
- const rulesDirPath = (0, import_node_path18.join)(baseDir, rulesDir);
4522
+ const rulesDirPath = (0, import_node_path22.join)(baseDir, rulesDir);
4292
4523
  try {
4293
4524
  const { mkdir: mkdir3 } = await import("fs/promises");
4294
4525
  await mkdir3(rulesDirPath, { recursive: true });
@@ -4301,8 +4532,13 @@ async function importConfiguration(options) {
4301
4532
  for (const rule of rules) {
4302
4533
  try {
4303
4534
  const baseFilename = rule.filename;
4304
- const filename = await generateUniqueFilename(rulesDirPath, baseFilename);
4305
- const filePath = (0, import_node_path18.join)(rulesDirPath, `${filename}.md`);
4535
+ let targetDir = rulesDirPath;
4536
+ if (rule.type === "command") {
4537
+ targetDir = (0, import_node_path22.join)(rulesDirPath, "commands");
4538
+ const { mkdir: mkdir3 } = await import("fs/promises");
4539
+ await mkdir3(targetDir, { recursive: true });
4540
+ }
4541
+ const filePath = (0, import_node_path22.join)(targetDir, `${baseFilename}.md`);
4306
4542
  const content = generateRuleFileContent(rule);
4307
4543
  await writeFileContent(filePath, content);
4308
4544
  rulesCreated++;
@@ -4317,7 +4553,7 @@ async function importConfiguration(options) {
4317
4553
  let ignoreFileCreated = false;
4318
4554
  if (ignorePatterns && ignorePatterns.length > 0) {
4319
4555
  try {
4320
- const rulesyncignorePath = (0, import_node_path18.join)(baseDir, ".rulesyncignore");
4556
+ const rulesyncignorePath = (0, import_node_path22.join)(baseDir, ".rulesyncignore");
4321
4557
  const ignoreContent = `${ignorePatterns.join("\n")}
4322
4558
  `;
4323
4559
  await writeFileContent(rulesyncignorePath, ignoreContent);
@@ -4333,7 +4569,7 @@ async function importConfiguration(options) {
4333
4569
  let mcpFileCreated = false;
4334
4570
  if (mcpServers && Object.keys(mcpServers).length > 0) {
4335
4571
  try {
4336
- const mcpPath = (0, import_node_path18.join)(baseDir, rulesDir, ".mcp.json");
4572
+ const mcpPath = (0, import_node_path22.join)(baseDir, rulesDir, ".mcp.json");
4337
4573
  const mcpContent = `${JSON.stringify({ mcpServers }, null, 2)}
4338
4574
  `;
4339
4575
  await writeFileContent(mcpPath, mcpContent);
@@ -4355,17 +4591,16 @@ async function importConfiguration(options) {
4355
4591
  };
4356
4592
  }
4357
4593
  function generateRuleFileContent(rule) {
4358
- const frontmatter = import_gray_matter6.default.stringify("", rule.frontmatter);
4359
- return frontmatter + rule.content;
4360
- }
4361
- async function generateUniqueFilename(rulesDir, baseFilename) {
4362
- let filename = baseFilename;
4363
- let counter = 1;
4364
- while (await fileExists((0, import_node_path18.join)(rulesDir, `${filename}.md`))) {
4365
- filename = `${baseFilename}-${counter}`;
4366
- counter++;
4594
+ if (rule.type === "command") {
4595
+ const simplifiedFrontmatter = {
4596
+ description: rule.frontmatter.description,
4597
+ targets: rule.frontmatter.targets
4598
+ };
4599
+ const frontmatter2 = import_gray_matter7.default.stringify("", simplifiedFrontmatter);
4600
+ return frontmatter2 + rule.content;
4367
4601
  }
4368
- return filename;
4602
+ const frontmatter = import_gray_matter7.default.stringify("", rule.frontmatter);
4603
+ return frontmatter + rule.content;
4369
4604
  }
4370
4605
 
4371
4606
  // src/cli/commands/import.ts
@@ -4428,7 +4663,7 @@ async function importCommand(options = {}) {
4428
4663
  }
4429
4664
 
4430
4665
  // src/cli/commands/init.ts
4431
- var import_node_path19 = require("path");
4666
+ var import_node_path23 = require("path");
4432
4667
  async function initCommand() {
4433
4668
  const aiRulesDir = ".rulesync";
4434
4669
  console.log("Initializing rulesync...");
@@ -4475,7 +4710,7 @@ globs: ["**/*"]
4475
4710
  - Follow single responsibility principle
4476
4711
  `
4477
4712
  };
4478
- const filepath = (0, import_node_path19.join)(aiRulesDir, sampleFile.filename);
4713
+ const filepath = (0, import_node_path23.join)(aiRulesDir, sampleFile.filename);
4479
4714
  if (!await fileExists(filepath)) {
4480
4715
  await writeFileContent(filepath, sampleFile.content);
4481
4716
  console.log(`Created ${filepath}`);
@@ -4619,7 +4854,7 @@ async function watchCommand() {
4619
4854
 
4620
4855
  // src/cli/index.ts
4621
4856
  var program = new import_commander.Command();
4622
- program.name("rulesync").description("Unified AI rules management CLI tool").version("0.57.0");
4857
+ program.name("rulesync").description("Unified AI rules management CLI tool").version("0.58.0");
4623
4858
  program.command("init").description("Initialize rulesync in current directory").action(initCommand);
4624
4859
  program.command("add <filename>").description("Add a new rule file").action(addCommand);
4625
4860
  program.command("gitignore").description("Add generated files to .gitignore").action(gitignoreCommand);