rulesync 0.57.0 → 0.59.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,38 +1,16 @@
1
1
  #!/usr/bin/env node
2
- import {
3
- generateJunieMcp
4
- } from "./chunk-VNT6AHHO.js";
5
- import {
6
- generateKiroMcp
7
- } from "./chunk-LXTA7DBA.js";
8
- import {
9
- generateRooMcp
10
- } from "./chunk-PCATT4UZ.js";
11
- import {
12
- generateWindsurfMcp
13
- } from "./chunk-7E4U4YAB.js";
14
- import {
15
- generateAugmentcodeMcp
16
- } from "./chunk-YTU3SCQO.js";
17
- import {
18
- generateClaudeMcp
19
- } from "./chunk-VKNCBVZF.js";
20
- import {
21
- generateClineMcp
22
- } from "./chunk-W2WU253H.js";
23
- import {
24
- generateCodexMcp
25
- } from "./chunk-7UBF4OLN.js";
26
- import {
27
- generateCopilotMcp
28
- } from "./chunk-KUGTKMNW.js";
29
- import {
30
- generateCursorMcp
31
- } from "./chunk-OA473EXZ.js";
32
- import {
33
- generateGeminiCliMcp
34
- } from "./chunk-WAX2UANS.js";
35
- import "./chunk-J3TBR5EP.js";
2
+ import "./chunk-S3GB3VQK.js";
3
+ import "./chunk-LXTA7DBA.js";
4
+ import "./chunk-PCATT4UZ.js";
5
+ import "./chunk-3YQ42A7G.js";
6
+ import "./chunk-YTU3SCQO.js";
7
+ import "./chunk-EG7LNNMW.js";
8
+ import "./chunk-VYO76WDU.js";
9
+ import "./chunk-DJWXF2WO.js";
10
+ import "./chunk-KUGTKMNW.js";
11
+ import "./chunk-LIV53UU5.js";
12
+ import "./chunk-5SRMJNDW.js";
13
+ import "./chunk-OCK47GE7.js";
36
14
  import {
37
15
  ALL_TOOL_TARGETS,
38
16
  RulesyncTargetsSchema,
@@ -129,128 +107,137 @@ var ClaudeSettingsSchema = z.looseObject({
129
107
  )
130
108
  });
131
109
 
132
- // src/types/config.ts
110
+ // src/types/commands.ts
133
111
  import { z as z2 } from "zod/mini";
134
- var ConfigSchema = z2.object({
135
- aiRulesDir: z2.string(),
136
- outputPaths: z2.record(ToolTargetSchema, z2.string()),
137
- watchEnabled: z2.boolean(),
138
- defaultTargets: ToolTargetsSchema
112
+ var CommandFrontmatterSchema = z2.object({
113
+ description: z2.optional(z2.string())
139
114
  });
140
115
 
141
- // src/types/config-options.ts
116
+ // src/types/config.ts
142
117
  import { z as z3 } from "zod/mini";
143
- var OutputPathsSchema = z3.object({
144
- augmentcode: z3.optional(z3.string()),
145
- "augmentcode-legacy": z3.optional(z3.string()),
146
- copilot: z3.optional(z3.string()),
147
- cursor: z3.optional(z3.string()),
148
- cline: z3.optional(z3.string()),
149
- claudecode: z3.optional(z3.string()),
150
- codexcli: z3.optional(z3.string()),
151
- roo: z3.optional(z3.string()),
152
- geminicli: z3.optional(z3.string()),
153
- kiro: z3.optional(z3.string()),
154
- junie: z3.optional(z3.string()),
155
- windsurf: z3.optional(z3.string())
118
+ var ConfigSchema = z3.object({
119
+ aiRulesDir: z3.string(),
120
+ outputPaths: z3.record(ToolTargetSchema, z3.string()),
121
+ watchEnabled: z3.boolean(),
122
+ defaultTargets: ToolTargetsSchema,
123
+ claudecodeCommands: z3.optional(z3.string()),
124
+ geminicliCommands: z3.optional(z3.string())
125
+ });
126
+
127
+ // src/types/config-options.ts
128
+ import { z as z4 } from "zod/mini";
129
+ var OutputPathsSchema = z4.object({
130
+ augmentcode: z4.optional(z4.string()),
131
+ "augmentcode-legacy": z4.optional(z4.string()),
132
+ copilot: z4.optional(z4.string()),
133
+ cursor: z4.optional(z4.string()),
134
+ cline: z4.optional(z4.string()),
135
+ claudecode: z4.optional(z4.string()),
136
+ codexcli: z4.optional(z4.string()),
137
+ roo: z4.optional(z4.string()),
138
+ geminicli: z4.optional(z4.string()),
139
+ kiro: z4.optional(z4.string()),
140
+ junie: z4.optional(z4.string()),
141
+ windsurf: z4.optional(z4.string())
156
142
  });
157
- var ConfigOptionsSchema = z3.object({
158
- aiRulesDir: z3.optional(z3.string()),
159
- outputPaths: z3.optional(OutputPathsSchema),
160
- watchEnabled: z3.optional(z3.boolean()),
161
- defaultTargets: z3.optional(ToolTargetsSchema),
162
- targets: z3.optional(z3.array(ToolTargetSchema)),
163
- exclude: z3.optional(z3.array(ToolTargetSchema)),
164
- verbose: z3.optional(z3.boolean()),
165
- delete: z3.optional(z3.boolean()),
166
- baseDir: z3.optional(z3.union([z3.string(), z3.array(z3.string())])),
167
- watch: z3.optional(
168
- z3.object({
169
- enabled: z3.optional(z3.boolean()),
170
- interval: z3.optional(z3.number()),
171
- ignore: z3.optional(z3.array(z3.string()))
143
+ var ConfigOptionsSchema = z4.object({
144
+ aiRulesDir: z4.optional(z4.string()),
145
+ outputPaths: z4.optional(OutputPathsSchema),
146
+ watchEnabled: z4.optional(z4.boolean()),
147
+ defaultTargets: z4.optional(ToolTargetsSchema),
148
+ targets: z4.optional(z4.array(ToolTargetSchema)),
149
+ exclude: z4.optional(z4.array(ToolTargetSchema)),
150
+ verbose: z4.optional(z4.boolean()),
151
+ delete: z4.optional(z4.boolean()),
152
+ baseDir: z4.optional(z4.union([z4.string(), z4.array(z4.string())])),
153
+ watch: z4.optional(
154
+ z4.object({
155
+ enabled: z4.optional(z4.boolean()),
156
+ interval: z4.optional(z4.number()),
157
+ ignore: z4.optional(z4.array(z4.string()))
172
158
  })
173
159
  )
174
160
  });
175
- var MergedConfigSchema = z3.object({
176
- aiRulesDir: z3.string(),
177
- outputPaths: z3.record(ToolTargetSchema, z3.string()),
178
- watchEnabled: z3.boolean(),
161
+ var MergedConfigSchema = z4.object({
162
+ aiRulesDir: z4.string(),
163
+ outputPaths: z4.record(ToolTargetSchema, z4.string()),
164
+ watchEnabled: z4.boolean(),
179
165
  defaultTargets: ToolTargetsSchema,
180
- targets: z3.optional(z3.array(ToolTargetSchema)),
181
- exclude: z3.optional(z3.array(ToolTargetSchema)),
182
- verbose: z3.optional(z3.boolean()),
183
- delete: z3.optional(z3.boolean()),
184
- baseDir: z3.optional(z3.union([z3.string(), z3.array(z3.string())])),
185
- configPath: z3.optional(z3.string()),
186
- watch: z3.optional(
187
- z3.object({
188
- enabled: z3.optional(z3.boolean()),
189
- interval: z3.optional(z3.number()),
190
- ignore: z3.optional(z3.array(z3.string()))
166
+ targets: z4.optional(z4.array(ToolTargetSchema)),
167
+ exclude: z4.optional(z4.array(ToolTargetSchema)),
168
+ verbose: z4.optional(z4.boolean()),
169
+ delete: z4.optional(z4.boolean()),
170
+ baseDir: z4.optional(z4.union([z4.string(), z4.array(z4.string())])),
171
+ configPath: z4.optional(z4.string()),
172
+ watch: z4.optional(
173
+ z4.object({
174
+ enabled: z4.optional(z4.boolean()),
175
+ interval: z4.optional(z4.number()),
176
+ ignore: z4.optional(z4.array(z4.string()))
191
177
  })
192
178
  )
193
179
  });
194
180
 
195
181
  // src/types/mcp.ts
196
- import { z as z4 } from "zod/mini";
197
- var McpTransportTypeSchema = z4.enum(["stdio", "sse", "http"]);
198
- var McpServerBaseSchema = z4.object({
199
- command: z4.optional(z4.string()),
200
- args: z4.optional(z4.array(z4.string())),
201
- url: z4.optional(z4.string()),
202
- httpUrl: z4.optional(z4.string()),
203
- env: z4.optional(z4.record(z4.string(), z4.string())),
204
- disabled: z4.optional(z4.boolean()),
205
- networkTimeout: z4.optional(z4.number()),
206
- timeout: z4.optional(z4.number()),
207
- trust: z4.optional(z4.boolean()),
208
- cwd: z4.optional(z4.string()),
209
- transport: z4.optional(McpTransportTypeSchema),
210
- type: z4.optional(z4.enum(["sse", "streamable-http"])),
211
- alwaysAllow: z4.optional(z4.array(z4.string())),
212
- tools: z4.optional(z4.array(z4.string())),
213
- kiroAutoApprove: z4.optional(z4.array(z4.string())),
214
- kiroAutoBlock: z4.optional(z4.array(z4.string())),
215
- headers: z4.optional(z4.record(z4.string(), z4.string()))
182
+ import { z as z5 } from "zod/mini";
183
+ var McpTransportTypeSchema = z5.enum(["stdio", "sse", "http"]);
184
+ var McpServerBaseSchema = z5.object({
185
+ command: z5.optional(z5.string()),
186
+ args: z5.optional(z5.array(z5.string())),
187
+ url: z5.optional(z5.string()),
188
+ httpUrl: z5.optional(z5.string()),
189
+ env: z5.optional(z5.record(z5.string(), z5.string())),
190
+ disabled: z5.optional(z5.boolean()),
191
+ networkTimeout: z5.optional(z5.number()),
192
+ timeout: z5.optional(z5.number()),
193
+ trust: z5.optional(z5.boolean()),
194
+ cwd: z5.optional(z5.string()),
195
+ transport: z5.optional(McpTransportTypeSchema),
196
+ type: z5.optional(z5.enum(["sse", "streamable-http"])),
197
+ alwaysAllow: z5.optional(z5.array(z5.string())),
198
+ tools: z5.optional(z5.array(z5.string())),
199
+ kiroAutoApprove: z5.optional(z5.array(z5.string())),
200
+ kiroAutoBlock: z5.optional(z5.array(z5.string())),
201
+ headers: z5.optional(z5.record(z5.string(), z5.string()))
216
202
  });
217
- var RulesyncMcpServerSchema = z4.extend(McpServerBaseSchema, {
218
- targets: z4.optional(RulesyncTargetsSchema)
203
+ var RulesyncMcpServerSchema = z5.extend(McpServerBaseSchema, {
204
+ targets: z5.optional(RulesyncTargetsSchema)
219
205
  });
220
- var McpConfigSchema = z4.object({
221
- mcpServers: z4.record(z4.string(), McpServerBaseSchema)
206
+ var McpConfigSchema = z5.object({
207
+ mcpServers: z5.record(z5.string(), McpServerBaseSchema)
222
208
  });
223
- var RulesyncMcpConfigSchema = z4.object({
224
- mcpServers: z4.record(z4.string(), RulesyncMcpServerSchema)
209
+ var RulesyncMcpConfigSchema = z5.object({
210
+ mcpServers: z5.record(z5.string(), RulesyncMcpServerSchema)
225
211
  });
226
212
 
227
213
  // src/types/rules.ts
228
- import { z as z5 } from "zod/mini";
229
- var RuleFrontmatterSchema = z5.object({
230
- root: z5.optional(z5.boolean()),
231
- targets: z5.optional(RulesyncTargetsSchema),
232
- description: z5.optional(z5.string()),
233
- globs: z5.optional(z5.array(z5.string())),
234
- cursorRuleType: z5.optional(z5.enum(["always", "manual", "specificFiles", "intelligently"])),
235
- windsurfActivationMode: z5.optional(z5.enum(["always", "manual", "model-decision", "glob"])),
236
- windsurfOutputFormat: z5.optional(z5.enum(["single-file", "directory"])),
237
- tags: z5.optional(z5.array(z5.string()))
214
+ import { z as z6 } from "zod/mini";
215
+ var RuleFrontmatterSchema = z6.object({
216
+ root: z6.optional(z6.boolean()),
217
+ targets: z6.optional(RulesyncTargetsSchema),
218
+ description: z6.optional(z6.string()),
219
+ globs: z6.optional(z6.array(z6.string())),
220
+ cursorRuleType: z6.optional(z6.enum(["always", "manual", "specificFiles", "intelligently"])),
221
+ windsurfActivationMode: z6.optional(z6.enum(["always", "manual", "model-decision", "glob"])),
222
+ windsurfOutputFormat: z6.optional(z6.enum(["single-file", "directory"])),
223
+ tags: z6.optional(z6.array(z6.string()))
238
224
  });
239
- var ParsedRuleSchema = z5.object({
225
+ var ParsedRuleSchema = z6.object({
240
226
  frontmatter: RuleFrontmatterSchema,
241
- content: z5.string(),
242
- filename: z5.string(),
243
- filepath: z5.string()
227
+ content: z6.string(),
228
+ filename: z6.string(),
229
+ filepath: z6.string(),
230
+ type: z6.optional(z6.enum(["rule", "command"]))
244
231
  });
245
- var GeneratedOutputSchema = z5.object({
232
+ var GeneratedOutputSchema = z6.object({
246
233
  tool: ToolTargetSchema,
247
- filepath: z5.string(),
248
- content: z5.string()
234
+ filepath: z6.string(),
235
+ content: z6.string()
249
236
  });
250
- var GenerateOptionsSchema = z5.object({
251
- targetTools: z5.optional(ToolTargetsSchema),
252
- outputDir: z5.optional(z5.string()),
253
- watch: z5.optional(z5.boolean())
237
+ var GenerateOptionsSchema = z6.object({
238
+ targetTools: z6.optional(ToolTargetsSchema),
239
+ outputDir: z6.optional(z6.string()),
240
+ watch: z6.optional(z6.boolean())
254
241
  });
255
242
 
256
243
  // src/utils/config-loader.ts
@@ -542,28 +529,6 @@ async function removeClaudeGeneratedFiles() {
542
529
  }
543
530
  }
544
531
 
545
- // src/utils/rules.ts
546
- function isToolSpecificRule(rule, targetTool) {
547
- const filename = rule.filename;
548
- const toolPatterns = {
549
- "augmentcode-legacy": /^specification-augmentcode-legacy-/i,
550
- augmentcode: /^specification-augmentcode-/i,
551
- copilot: /^specification-copilot-/i,
552
- cursor: /^specification-cursor-/i,
553
- cline: /^specification-cline-/i,
554
- claudecode: /^specification-claudecode-/i,
555
- roo: /^specification-roo-/i,
556
- geminicli: /^specification-geminicli-/i,
557
- kiro: /^specification-kiro-/i
558
- };
559
- for (const [tool, pattern] of Object.entries(toolPatterns)) {
560
- if (pattern.test(filename)) {
561
- return tool === targetTool;
562
- }
563
- }
564
- return true;
565
- }
566
-
567
532
  // src/cli/commands/config.ts
568
533
  async function configCommand(options = {}) {
569
534
  if (options.init) {
@@ -775,10 +740,165 @@ export default config;
775
740
  }
776
741
 
777
742
  // src/cli/commands/generate.ts
778
- import { join as join11 } from "path";
743
+ import { join as join14 } from "path";
779
744
 
780
- // src/generators/ignore/shared-factory.ts
745
+ // src/core/command-generator.ts
746
+ import { join as join5 } from "path";
747
+
748
+ // src/generators/commands/claudecode.ts
781
749
  import { join as join3 } from "path";
750
+ var ClaudeCodeCommandGenerator = class {
751
+ generate(command, outputDir) {
752
+ const filepath = this.getOutputPath(command.filename, outputDir);
753
+ const frontmatter = ["---"];
754
+ if (command.frontmatter.description) {
755
+ frontmatter.push(`description: ${command.frontmatter.description}`);
756
+ }
757
+ frontmatter.push("---");
758
+ const content = `${frontmatter.join("\n")}
759
+
760
+ ${command.content.trim()}
761
+ `;
762
+ return {
763
+ tool: "claudecode",
764
+ filepath,
765
+ content
766
+ };
767
+ }
768
+ getOutputPath(filename, baseDir) {
769
+ const flattenedName = filename.replace(/\//g, "-");
770
+ return join3(baseDir, ".claude", "commands", `${flattenedName}.md`);
771
+ }
772
+ };
773
+
774
+ // src/generators/commands/geminicli.ts
775
+ import { join as join4 } from "path";
776
+ var GeminiCliCommandGenerator = class {
777
+ generate(command, outputDir) {
778
+ const filepath = this.getOutputPath(command.filename, outputDir);
779
+ const convertedContent = this.convertSyntax(command.content);
780
+ const tomlLines = [];
781
+ if (command.frontmatter.description) {
782
+ tomlLines.push(`description = "${this.escapeTomlString(command.frontmatter.description)}"`);
783
+ tomlLines.push("");
784
+ }
785
+ tomlLines.push(`prompt = """${convertedContent}"""`);
786
+ const content = tomlLines.join("\n") + "\n";
787
+ return {
788
+ tool: "geminicli",
789
+ filepath,
790
+ content
791
+ };
792
+ }
793
+ getOutputPath(filename, baseDir) {
794
+ const tomlFilename = filename.replace(/\.md$/, ".toml");
795
+ const filenameWithExt = tomlFilename.endsWith(".toml") ? tomlFilename : `${tomlFilename}.toml`;
796
+ return join4(baseDir, ".gemini", "commands", filenameWithExt);
797
+ }
798
+ convertSyntax(content) {
799
+ let converted = content;
800
+ converted = converted.replace(/\$ARGUMENTS/g, "{{args}}");
801
+ converted = converted.replace(/!`([^`]+)`/g, "!{$1}");
802
+ const atSyntaxMatches = converted.match(/@[^\s]+/g);
803
+ if (atSyntaxMatches) {
804
+ console.warn(
805
+ `\u26A0\uFE0F Warning: @ syntax found (${atSyntaxMatches.join(", ")}). Gemini CLI does not support file content injection. Consider using shell commands or remove these references.`
806
+ );
807
+ }
808
+ return converted.trim();
809
+ }
810
+ escapeTomlString(str) {
811
+ return str.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/\t/g, "\\t");
812
+ }
813
+ };
814
+
815
+ // src/generators/commands/index.ts
816
+ var commandGenerators = {
817
+ claudecode: new ClaudeCodeCommandGenerator(),
818
+ geminicli: new GeminiCliCommandGenerator()
819
+ };
820
+ function getCommandGenerator(tool) {
821
+ return commandGenerators[tool];
822
+ }
823
+
824
+ // src/core/command-parser.ts
825
+ import { basename } from "path";
826
+ import matter from "gray-matter";
827
+ async function parseCommandsFromDirectory(commandsDir) {
828
+ const commandFiles = await findFiles(commandsDir, ".md");
829
+ const commands = [];
830
+ const errors = [];
831
+ for (const filepath of commandFiles) {
832
+ try {
833
+ const command = await parseCommandFile(filepath);
834
+ commands.push(command);
835
+ } catch (error) {
836
+ const errorMessage = error instanceof Error ? error.message : String(error);
837
+ errors.push(`Failed to parse command file ${filepath}: ${errorMessage}`);
838
+ }
839
+ }
840
+ if (errors.length > 0) {
841
+ console.warn(`\u26A0\uFE0F Command parsing errors:
842
+ ${errors.join("\n")}`);
843
+ }
844
+ return commands;
845
+ }
846
+ async function parseCommandFile(filepath) {
847
+ const content = await readFileContent(filepath);
848
+ const parsed = matter(content);
849
+ try {
850
+ const validatedData = CommandFrontmatterSchema.parse(parsed.data);
851
+ const filename = basename(filepath, ".md");
852
+ return {
853
+ frontmatter: {
854
+ description: validatedData.description
855
+ },
856
+ content: parsed.content,
857
+ filename,
858
+ filepath
859
+ };
860
+ } catch (error) {
861
+ throw new Error(
862
+ `Invalid frontmatter in ${filepath}: ${error instanceof Error ? error.message : String(error)}`
863
+ );
864
+ }
865
+ }
866
+
867
+ // src/core/command-generator.ts
868
+ async function generateCommands(projectRoot, baseDir, targets) {
869
+ const commandsDir = join5(projectRoot, ".rulesync", "commands");
870
+ if (!await fileExists(commandsDir)) {
871
+ return [];
872
+ }
873
+ const commands = await parseCommandsFromDirectory(commandsDir);
874
+ if (commands.length === 0) {
875
+ return [];
876
+ }
877
+ const outputs = [];
878
+ const outputDir = baseDir || projectRoot;
879
+ const supportedTargets = targets.filter((target) => ["claudecode", "geminicli"].includes(target));
880
+ for (const target of supportedTargets) {
881
+ const generator = getCommandGenerator(target);
882
+ if (!generator) {
883
+ continue;
884
+ }
885
+ for (const command of commands) {
886
+ try {
887
+ const output = generator.generate(command, outputDir);
888
+ outputs.push(output);
889
+ } catch (error) {
890
+ const errorMessage = error instanceof Error ? error.message : String(error);
891
+ console.error(
892
+ `\u274C Failed to generate ${target} command for ${command.filename}: ${errorMessage}`
893
+ );
894
+ }
895
+ }
896
+ }
897
+ return outputs;
898
+ }
899
+
900
+ // src/generators/ignore/shared-factory.ts
901
+ import { join as join6 } from "path";
782
902
 
783
903
  // src/generators/ignore/shared-helpers.ts
784
904
  function extractIgnorePatternsFromRules(rules) {
@@ -896,7 +1016,7 @@ function generateIgnoreFile(rules, config, ignoreConfig, baseDir) {
896
1016
  const outputs = [];
897
1017
  const content = generateIgnoreContent(rules, ignoreConfig);
898
1018
  const outputPath = baseDir || process.cwd();
899
- const filepath = join3(outputPath, ignoreConfig.filename);
1019
+ const filepath = join6(outputPath, ignoreConfig.filename);
900
1020
  outputs.push({
901
1021
  tool: ignoreConfig.tool,
902
1022
  filepath,
@@ -1484,20 +1604,20 @@ function generateWindsurfIgnore(rules, config, baseDir) {
1484
1604
  }
1485
1605
 
1486
1606
  // src/generators/rules/augmentcode.ts
1487
- import { join as join6 } from "path";
1607
+ import { join as join9 } from "path";
1488
1608
 
1489
1609
  // src/generators/rules/shared-helpers.ts
1490
- import { join as join5 } from "path";
1610
+ import { join as join8 } from "path";
1491
1611
 
1492
1612
  // src/utils/ignore.ts
1493
- import { join as join4 } from "path";
1613
+ import { join as join7 } from "path";
1494
1614
  import micromatch from "micromatch";
1495
1615
  var cachedIgnorePatterns = null;
1496
1616
  async function loadIgnorePatterns(baseDir = process.cwd()) {
1497
1617
  if (cachedIgnorePatterns) {
1498
1618
  return cachedIgnorePatterns;
1499
1619
  }
1500
- const ignorePath = join4(baseDir, ".rulesyncignore");
1620
+ const ignorePath = join7(baseDir, ".rulesyncignore");
1501
1621
  if (!await fileExists(ignorePath)) {
1502
1622
  cachedIgnorePatterns = { patterns: [] };
1503
1623
  return cachedIgnorePatterns;
@@ -1551,7 +1671,7 @@ function addOutput(outputs, tool, config, baseDir, relativePath, content) {
1551
1671
  const outputDir = resolveOutputDir(config, tool, baseDir);
1552
1672
  outputs.push({
1553
1673
  tool,
1554
- filepath: join5(outputDir, relativePath),
1674
+ filepath: join8(outputDir, relativePath),
1555
1675
  content
1556
1676
  });
1557
1677
  }
@@ -1560,7 +1680,7 @@ async function generateRulesConfig(rules, config, generatorConfig, baseDir) {
1560
1680
  for (const rule of rules) {
1561
1681
  const content = generatorConfig.generateContent(rule);
1562
1682
  const outputDir = resolveOutputDir(config, generatorConfig.tool, baseDir);
1563
- const filepath = generatorConfig.pathResolver ? generatorConfig.pathResolver(rule, outputDir) : join5(outputDir, `${rule.filename}${generatorConfig.fileExtension}`);
1683
+ const filepath = generatorConfig.pathResolver ? generatorConfig.pathResolver(rule, outputDir) : join8(outputDir, `${rule.filename}${generatorConfig.fileExtension}`);
1564
1684
  outputs.push({
1565
1685
  tool: generatorConfig.tool,
1566
1686
  filepath,
@@ -1568,7 +1688,7 @@ async function generateRulesConfig(rules, config, generatorConfig, baseDir) {
1568
1688
  });
1569
1689
  }
1570
1690
  const ignorePatterns = await loadIgnorePatterns(baseDir);
1571
- if (ignorePatterns.patterns.length > 0) {
1691
+ if (ignorePatterns.patterns.length > 0 && generatorConfig.ignoreFileName) {
1572
1692
  const ignorePath = resolvePath(generatorConfig.ignoreFileName, baseDir);
1573
1693
  const ignoreContent = generateIgnoreFile2(ignorePatterns.patterns, generatorConfig.tool);
1574
1694
  outputs.push({
@@ -1588,7 +1708,7 @@ async function generateComplexRules(rules, config, generatorConfig, baseDir) {
1588
1708
  for (const rule of detailRules) {
1589
1709
  const content = generatorConfig.generateDetailContent(rule);
1590
1710
  const filepath = resolvePath(
1591
- join5(generatorConfig.detailSubDir, `${rule.filename}.md`),
1711
+ join8(generatorConfig.detailSubDir, `${rule.filename}.md`),
1592
1712
  baseDir
1593
1713
  );
1594
1714
  outputs.push({
@@ -1609,13 +1729,15 @@ async function generateComplexRules(rules, config, generatorConfig, baseDir) {
1609
1729
  }
1610
1730
  const ignorePatterns = await loadIgnorePatterns(baseDir);
1611
1731
  if (ignorePatterns.patterns.length > 0) {
1612
- const ignorePath = resolvePath(generatorConfig.ignoreFileName, baseDir);
1613
- const ignoreContent = generateIgnoreFile2(ignorePatterns.patterns, generatorConfig.tool);
1614
- outputs.push({
1615
- tool: generatorConfig.tool,
1616
- filepath: ignorePath,
1617
- content: ignoreContent
1618
- });
1732
+ if (generatorConfig.ignoreFileName) {
1733
+ const ignorePath = resolvePath(generatorConfig.ignoreFileName, baseDir);
1734
+ const ignoreContent = generateIgnoreFile2(ignorePatterns.patterns, generatorConfig.tool);
1735
+ outputs.push({
1736
+ tool: generatorConfig.tool,
1737
+ filepath: ignorePath,
1738
+ content: ignoreContent
1739
+ });
1740
+ }
1619
1741
  if (generatorConfig.updateAdditionalConfig) {
1620
1742
  const additionalOutputs = await generatorConfig.updateAdditionalConfig(
1621
1743
  ignorePatterns.patterns,
@@ -1649,7 +1771,7 @@ async function generateAugmentcodeConfig(rules, config, baseDir) {
1649
1771
  "augmentcode",
1650
1772
  config,
1651
1773
  baseDir,
1652
- join6(".augment", "rules", `${rule.filename}.md`),
1774
+ join9(".augment", "rules", `${rule.filename}.md`),
1653
1775
  generateRuleFile(rule)
1654
1776
  );
1655
1777
  });
@@ -1702,19 +1824,19 @@ function generateLegacyGuidelinesFile(allRules) {
1702
1824
  }
1703
1825
 
1704
1826
  // src/generators/rules/claudecode.ts
1705
- import { join as join7 } from "path";
1827
+ import { join as join10 } from "path";
1706
1828
  async function generateClaudecodeConfig(rules, config, baseDir) {
1707
1829
  const generatorConfig = {
1708
1830
  tool: "claudecode",
1709
1831
  fileExtension: ".md",
1710
- ignoreFileName: ".aiignore",
1832
+ // ignoreFileName omitted - Claude Code uses settings.json permissions.deny instead of ignore files
1711
1833
  generateContent: generateMemoryFile,
1712
1834
  generateRootContent: generateClaudeMarkdown,
1713
1835
  rootFilePath: "CLAUDE.md",
1714
1836
  generateDetailContent: generateMemoryFile,
1715
1837
  detailSubDir: ".claude/memories",
1716
1838
  updateAdditionalConfig: async (ignorePatterns, baseDir2) => {
1717
- const settingsPath = resolvePath(join7(".claude", "settings.json"), baseDir2);
1839
+ const settingsPath = resolvePath(join10(".claude", "settings.json"), baseDir2);
1718
1840
  await updateClaudeSettings(settingsPath, ignorePatterns);
1719
1841
  return [];
1720
1842
  }
@@ -1778,7 +1900,7 @@ async function updateClaudeSettings(settingsPath, ignorePatterns) {
1778
1900
  }
1779
1901
 
1780
1902
  // src/generators/rules/generator-registry.ts
1781
- import { join as join8 } from "path";
1903
+ import { join as join11 } from "path";
1782
1904
  function determineCursorRuleType(frontmatter) {
1783
1905
  if (frontmatter.cursorRuleType) {
1784
1906
  return frontmatter.cursorRuleType;
@@ -1858,7 +1980,7 @@ var GENERATOR_REGISTRY = {
1858
1980
  },
1859
1981
  pathResolver: (rule, outputDir) => {
1860
1982
  const baseFilename = rule.filename.replace(/\.md$/, "");
1861
- return join8(outputDir, `${baseFilename}.instructions.md`);
1983
+ return join11(outputDir, `${baseFilename}.instructions.md`);
1862
1984
  }
1863
1985
  },
1864
1986
  cursor: {
@@ -1898,7 +2020,7 @@ var GENERATOR_REGISTRY = {
1898
2020
  return lines.join("\n");
1899
2021
  },
1900
2022
  pathResolver: (rule, outputDir) => {
1901
- return join8(outputDir, `${rule.filename}.mdc`);
2023
+ return join11(outputDir, `${rule.filename}.mdc`);
1902
2024
  }
1903
2025
  },
1904
2026
  codexcli: {
@@ -1934,10 +2056,10 @@ var GENERATOR_REGISTRY = {
1934
2056
  pathResolver: (rule, outputDir) => {
1935
2057
  const outputFormat = rule.frontmatter.windsurfOutputFormat || "directory";
1936
2058
  if (outputFormat === "single-file") {
1937
- return join8(outputDir, ".windsurf-rules");
2059
+ return join11(outputDir, ".windsurf-rules");
1938
2060
  } else {
1939
- const rulesDir = join8(outputDir, ".windsurf", "rules");
1940
- return join8(rulesDir, `${rule.filename}.md`);
2061
+ const rulesDir = join11(outputDir, ".windsurf", "rules");
2062
+ return join11(rulesDir, `${rule.filename}.md`);
1941
2063
  }
1942
2064
  }
1943
2065
  },
@@ -2201,7 +2323,7 @@ function filterRulesForTool(rules, tool, config) {
2201
2323
  if (!targets.includes(tool)) {
2202
2324
  return false;
2203
2325
  }
2204
- return isToolSpecificRule(rule, tool);
2326
+ return true;
2205
2327
  });
2206
2328
  }
2207
2329
  async function generateForTool(tool, rules, config, baseDir) {
@@ -2256,8 +2378,8 @@ async function generateForTool(tool, rules, config, baseDir) {
2256
2378
  }
2257
2379
 
2258
2380
  // src/core/parser.ts
2259
- import { basename } from "path";
2260
- import matter from "gray-matter";
2381
+ import { basename as basename2 } from "path";
2382
+ import matter2 from "gray-matter";
2261
2383
  async function parseRulesFromDirectory(aiRulesDir) {
2262
2384
  const ignorePatterns = await loadIgnorePatterns();
2263
2385
  const allRuleFiles = await findFiles(aiRulesDir, ".md");
@@ -2291,7 +2413,7 @@ ${errors.join("\n")}`);
2291
2413
  }
2292
2414
  async function parseRuleFile(filepath) {
2293
2415
  const content = await readFileContent(filepath);
2294
- const parsed = matter(content);
2416
+ const parsed = matter2(content);
2295
2417
  try {
2296
2418
  const validatedData = RuleFrontmatterSchema.parse(parsed.data);
2297
2419
  const frontmatter = {
@@ -2310,7 +2432,7 @@ async function parseRuleFile(filepath) {
2310
2432
  },
2311
2433
  ...validatedData.tags !== void 0 && { tags: validatedData.tags }
2312
2434
  };
2313
- const filename = basename(filepath, ".md");
2435
+ const filename = basename2(filepath, ".md");
2314
2436
  return {
2315
2437
  frontmatter,
2316
2438
  content: parsed.content,
@@ -2405,125 +2527,51 @@ function parseMcpConfig(projectRoot) {
2405
2527
  }
2406
2528
 
2407
2529
  // src/core/mcp-generator.ts
2408
- async function generateMcpConfigs(projectRoot, baseDir, targetTools) {
2409
- const results = [];
2410
- const targetRoot = baseDir || projectRoot;
2411
- const config = parseMcpConfig(projectRoot);
2412
- if (!config) {
2413
- return results;
2414
- }
2415
- const generators = [
2416
- {
2417
- tool: "augmentcode-project",
2418
- path: path4.join(targetRoot, ".mcp.json"),
2419
- generate: () => generateAugmentcodeMcp(config)
2420
- },
2421
- {
2422
- tool: "augmentcode-legacy-project",
2423
- path: path4.join(targetRoot, ".mcp.json"),
2424
- generate: () => generateAugmentcodeMcp(config)
2425
- },
2426
- {
2427
- tool: "claude-project",
2428
- path: path4.join(targetRoot, ".mcp.json"),
2429
- generate: () => generateClaudeMcp(config)
2430
- },
2431
- {
2432
- tool: "copilot-editor",
2433
- path: path4.join(targetRoot, ".vscode", "mcp.json"),
2434
- generate: () => generateCopilotMcp(config, "editor")
2435
- },
2436
- {
2437
- tool: "cursor-project",
2438
- path: path4.join(targetRoot, ".cursor", "mcp.json"),
2439
- generate: () => generateCursorMcp(config)
2440
- },
2441
- {
2442
- tool: "cline-project",
2443
- path: path4.join(targetRoot, ".cline", "mcp.json"),
2444
- generate: () => generateClineMcp(config)
2445
- },
2446
- {
2447
- tool: "codexcli-project",
2448
- path: path4.join(targetRoot, ".codex", "mcp-config.json"),
2449
- generate: () => generateCodexMcp(config)
2450
- },
2451
- {
2452
- tool: "gemini-project",
2453
- path: path4.join(targetRoot, ".gemini", "settings.json"),
2454
- generate: () => generateGeminiCliMcp(config)
2455
- },
2456
- {
2457
- tool: "junie-project",
2458
- path: path4.join(targetRoot, ".junie", "mcp-config.json"),
2459
- generate: () => generateJunieMcp(config)
2460
- },
2461
- {
2462
- tool: "kiro-project",
2463
- path: path4.join(targetRoot, ".kiro", "mcp.json"),
2464
- generate: () => generateKiroMcp(config)
2465
- },
2466
- {
2467
- tool: "roo-project",
2468
- path: path4.join(targetRoot, ".roo", "mcp.json"),
2469
- generate: () => generateRooMcp(config)
2470
- },
2471
- {
2472
- tool: "windsurf-project",
2473
- path: path4.join(targetRoot, "mcp_config.json"),
2474
- generate: () => generateWindsurfMcp(config)
2475
- }
2476
- ];
2477
- const filteredGenerators = targetTools ? generators.filter((g) => {
2478
- const baseTool = g.tool.split("-")[0];
2479
- if (!isToolTarget(baseTool)) {
2480
- return false;
2481
- }
2482
- if (baseTool === "augmentcode") {
2483
- return targetTools.includes("augmentcode") || targetTools.includes("augmentcode-legacy");
2484
- }
2485
- return targetTools.includes(baseTool);
2486
- }) : generators;
2487
- for (const generator of filteredGenerators) {
2488
- try {
2489
- const content = generator.generate();
2490
- const parsed = JSON.parse(content);
2491
- if (generator.tool.includes("augmentcode") || generator.tool.includes("claude") || generator.tool.includes("cline") || generator.tool.includes("codexcli") || generator.tool.includes("cursor") || generator.tool.includes("gemini") || generator.tool.includes("junie") || generator.tool.includes("kiro") || generator.tool.includes("roo") || generator.tool.includes("windsurf")) {
2492
- if (!parsed.mcpServers || Object.keys(parsed.mcpServers).length === 0) {
2493
- results.push({
2494
- tool: generator.tool,
2495
- path: generator.path,
2496
- status: "skipped"
2497
- });
2498
- continue;
2499
- }
2500
- } else if (generator.tool.includes("copilot")) {
2501
- const key = generator.tool.includes("codingAgent") ? "mcpServers" : "servers";
2502
- if (!parsed[key] || Object.keys(parsed[key]).length === 0) {
2503
- results.push({
2504
- tool: generator.tool,
2505
- path: generator.path,
2506
- status: "skipped"
2507
- });
2508
- continue;
2530
+ async function generateMcpConfigurations(mcpConfig, baseDir, targetTools) {
2531
+ const outputs = [];
2532
+ const toolMap = {
2533
+ augmentcode: async (servers, dir) => (await import("./augmentcode-HIZIQG2W.js")).generateAugmentcodeMcpConfiguration(
2534
+ servers,
2535
+ dir
2536
+ ),
2537
+ "augmentcode-legacy": async (servers, dir) => (await import("./augmentcode-HIZIQG2W.js")).generateAugmentcodeMcpConfiguration(
2538
+ servers,
2539
+ dir
2540
+ ),
2541
+ claudecode: async (servers, dir) => (await import("./claudecode-CRSXMPS5.js")).generateClaudeMcpConfiguration(
2542
+ servers,
2543
+ dir
2544
+ ),
2545
+ copilot: async (servers, dir) => (await import("./copilot-MOR3HHJX.js")).generateCopilotMcpConfiguration(servers, dir),
2546
+ cursor: async (servers, dir) => (await import("./cursor-EXX2Q5MB.js")).generateCursorMcpConfiguration(servers, dir),
2547
+ cline: async (servers, dir) => (await import("./cline-O67TEUFW.js")).generateClineMcpConfiguration(servers, dir),
2548
+ codexcli: async (servers, dir) => (await import("./codexcli-S7VDKBI2.js")).generateCodexMcpConfiguration(servers, dir),
2549
+ roo: async (servers, dir) => (await import("./roo-L3QTTIPO.js")).generateRooMcpConfiguration(servers, dir),
2550
+ geminicli: async (servers, dir) => (await import("./geminicli-K3FKXDKP.js")).generateGeminiCliMcpConfiguration(
2551
+ servers,
2552
+ dir
2553
+ ),
2554
+ kiro: async (servers, dir) => (await import("./kiro-YDHXY2MA.js")).generateKiroMcpConfiguration(servers, dir),
2555
+ junie: async (servers, dir) => (await import("./junie-4SNWC452.js")).generateJunieMcpConfiguration(servers, dir),
2556
+ windsurf: async (servers, dir) => (await import("./windsurf-NVCRKFHF.js")).generateWindsurfMcpConfiguration(
2557
+ servers,
2558
+ dir
2559
+ )
2560
+ };
2561
+ const tools = targetTools || Object.keys(toolMap).filter(isToolTarget);
2562
+ const seenPaths = /* @__PURE__ */ new Set();
2563
+ for (const tool of tools) {
2564
+ if (tool in toolMap) {
2565
+ const results = await toolMap[tool](mcpConfig.mcpServers || {}, baseDir);
2566
+ for (const result of results) {
2567
+ if (!seenPaths.has(result.filepath)) {
2568
+ seenPaths.add(result.filepath);
2569
+ outputs.push({ ...result, tool });
2509
2570
  }
2510
2571
  }
2511
- await writeFileContent(generator.path, content);
2512
- results.push({
2513
- tool: generator.tool,
2514
- path: generator.path,
2515
- status: "success"
2516
- });
2517
- } catch (error) {
2518
- results.push({
2519
- tool: generator.tool,
2520
- path: generator.path,
2521
- status: "error",
2522
- error: error instanceof Error ? error.message : String(error)
2523
- });
2524
2572
  }
2525
2573
  }
2526
- return results;
2574
+ return outputs;
2527
2575
  }
2528
2576
 
2529
2577
  // src/cli/commands/generate.ts
@@ -2600,12 +2648,12 @@ async function generateCommand(options = {}) {
2600
2648
  for (const tool of targetTools) {
2601
2649
  switch (tool) {
2602
2650
  case "augmentcode":
2603
- deleteTasks.push(removeDirectory(join11(".augment", "rules")));
2604
- deleteTasks.push(removeDirectory(join11(".augment", "ignore")));
2651
+ deleteTasks.push(removeDirectory(join14(".augment", "rules")));
2652
+ deleteTasks.push(removeDirectory(join14(".augment", "ignore")));
2605
2653
  break;
2606
2654
  case "augmentcode-legacy":
2607
2655
  deleteTasks.push(removeClaudeGeneratedFiles());
2608
- deleteTasks.push(removeDirectory(join11(".augment", "ignore")));
2656
+ deleteTasks.push(removeDirectory(join14(".augment", "ignore")));
2609
2657
  break;
2610
2658
  case "copilot":
2611
2659
  deleteTasks.push(removeDirectory(config.outputPaths.copilot));
@@ -2618,12 +2666,14 @@ async function generateCommand(options = {}) {
2618
2666
  break;
2619
2667
  case "claudecode":
2620
2668
  deleteTasks.push(removeClaudeGeneratedFiles());
2669
+ deleteTasks.push(removeDirectory(join14(".claude", "commands")));
2621
2670
  break;
2622
2671
  case "roo":
2623
2672
  deleteTasks.push(removeDirectory(config.outputPaths.roo));
2624
2673
  break;
2625
2674
  case "geminicli":
2626
2675
  deleteTasks.push(removeDirectory(config.outputPaths.geminicli));
2676
+ deleteTasks.push(removeDirectory(join14(".gemini", "commands")));
2627
2677
  break;
2628
2678
  case "kiro":
2629
2679
  deleteTasks.push(removeDirectory(config.outputPaths.kiro));
@@ -2666,33 +2716,69 @@ Generating configurations for base directory: ${baseDir}`);
2666
2716
  }
2667
2717
  let totalMcpOutputs = 0;
2668
2718
  for (const baseDir of baseDirs) {
2669
- const mcpResults = await generateMcpConfigs(
2719
+ try {
2720
+ const mcpConfig = parseMcpConfig(process.cwd());
2721
+ if (!mcpConfig || !mcpConfig.mcpServers || Object.keys(mcpConfig.mcpServers).length === 0) {
2722
+ if (config.verbose) {
2723
+ console.log(`No MCP configuration found for ${baseDir}`);
2724
+ }
2725
+ continue;
2726
+ }
2727
+ const mcpResults = await generateMcpConfigurations(
2728
+ mcpConfig,
2729
+ baseDir === process.cwd() ? "." : baseDir,
2730
+ config.defaultTargets
2731
+ );
2732
+ if (mcpResults.length === 0) {
2733
+ if (config.verbose) {
2734
+ console.log(`No MCP configurations generated for ${baseDir}`);
2735
+ }
2736
+ continue;
2737
+ }
2738
+ for (const result of mcpResults) {
2739
+ await writeFileContent(result.filepath, result.content);
2740
+ console.log(`\u2705 Generated ${result.tool} MCP configuration: ${result.filepath}`);
2741
+ totalMcpOutputs++;
2742
+ }
2743
+ } catch (error) {
2744
+ if (config.verbose) {
2745
+ console.error(
2746
+ `\u274C Failed to generate MCP configurations: ${error instanceof Error ? error.message : String(error)}`
2747
+ );
2748
+ }
2749
+ }
2750
+ }
2751
+ if (config.verbose) {
2752
+ console.log("\nGenerating command files...");
2753
+ }
2754
+ let totalCommandOutputs = 0;
2755
+ for (const baseDir of baseDirs) {
2756
+ const commandResults = await generateCommands(
2670
2757
  process.cwd(),
2671
2758
  baseDir === process.cwd() ? void 0 : baseDir,
2672
2759
  config.defaultTargets
2673
2760
  );
2674
- if (mcpResults.length === 0) {
2761
+ if (commandResults.length === 0) {
2675
2762
  if (config.verbose) {
2676
- console.log(`No MCP configuration found for ${baseDir}`);
2763
+ console.log(`No commands found for ${baseDir}`);
2677
2764
  }
2678
2765
  continue;
2679
2766
  }
2680
- for (const result of mcpResults) {
2681
- if (result.status === "success") {
2682
- console.log(`\u2705 Generated ${result.tool} MCP configuration: ${result.path}`);
2683
- totalMcpOutputs++;
2684
- } else if (result.status === "error") {
2685
- console.error(`\u274C Failed to generate ${result.tool} MCP configuration: ${result.error}`);
2686
- } else if (config.verbose && result.status === "skipped") {
2687
- console.log(`\u23ED\uFE0F Skipped ${result.tool} MCP configuration (no servers configured)`);
2688
- }
2767
+ for (const result of commandResults) {
2768
+ await writeFileContent(result.filepath, result.content);
2769
+ console.log(`\u2705 Generated ${result.tool} command: ${result.filepath}`);
2770
+ totalCommandOutputs++;
2689
2771
  }
2690
2772
  }
2691
- const totalGenerated = totalOutputs + totalMcpOutputs;
2773
+ const totalGenerated = totalOutputs + totalMcpOutputs + totalCommandOutputs;
2692
2774
  if (totalGenerated > 0) {
2775
+ const parts = [];
2776
+ if (totalOutputs > 0) parts.push(`${totalOutputs} configurations`);
2777
+ if (totalMcpOutputs > 0) parts.push(`${totalMcpOutputs} MCP configurations`);
2778
+ if (totalCommandOutputs > 0) parts.push(`${totalCommandOutputs} commands`);
2693
2779
  console.log(
2694
2780
  `
2695
- \u{1F389} All done! Generated ${totalGenerated} file(s) total (${totalOutputs} configurations + ${totalMcpOutputs} MCP configurations)`
2781
+ \u{1F389} All done! Generated ${totalGenerated} file(s) total (${parts.join(" + ")})`
2696
2782
  );
2697
2783
  }
2698
2784
  } catch (error) {
@@ -2703,9 +2789,9 @@ Generating configurations for base directory: ${baseDir}`);
2703
2789
 
2704
2790
  // src/cli/commands/gitignore.ts
2705
2791
  import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
2706
- import { join as join12 } from "path";
2792
+ import { join as join15 } from "path";
2707
2793
  var gitignoreCommand = async () => {
2708
- const gitignorePath = join12(process.cwd(), ".gitignore");
2794
+ const gitignorePath = join15(process.cwd(), ".gitignore");
2709
2795
  const rulesFilesToIgnore = [
2710
2796
  "# Generated by rulesync - AI tool configuration files",
2711
2797
  "**/.github/copilot-instructions.md",
@@ -2716,6 +2802,7 @@ var gitignoreCommand = async () => {
2716
2802
  "**/.clineignore",
2717
2803
  "**/CLAUDE.md",
2718
2804
  "**/.claude/memories/",
2805
+ "**/.claude/commands/",
2719
2806
  "**/codex.md",
2720
2807
  "**/.codexignore",
2721
2808
  "**/.roo/rules/",
@@ -2723,6 +2810,7 @@ var gitignoreCommand = async () => {
2723
2810
  "**/.copilotignore",
2724
2811
  "**/GEMINI.md",
2725
2812
  "**/.gemini/memories/",
2813
+ "**/.gemini/commands/",
2726
2814
  "**/.aiexclude",
2727
2815
  "**/.aiignore",
2728
2816
  "**/.augmentignore",
@@ -2769,12 +2857,12 @@ ${linesToAdd.join("\n")}
2769
2857
  };
2770
2858
 
2771
2859
  // src/core/importer.ts
2772
- import { join as join19 } from "path";
2773
- import matter6 from "gray-matter";
2860
+ import { join as join22 } from "path";
2861
+ import matter7 from "gray-matter";
2774
2862
 
2775
2863
  // src/parsers/augmentcode.ts
2776
- import { basename as basename2, join as join13 } from "path";
2777
- import matter2 from "gray-matter";
2864
+ import { basename as basename3, join as join16 } from "path";
2865
+ import matter3 from "gray-matter";
2778
2866
 
2779
2867
  // src/utils/parser-helpers.ts
2780
2868
  function createParseResult() {
@@ -2822,7 +2910,7 @@ async function parseAugmentcodeLegacyConfiguration(baseDir = process.cwd()) {
2822
2910
  async function parseUnifiedAugmentcode(baseDir, config) {
2823
2911
  const result = createParseResult();
2824
2912
  if (config.rulesDir) {
2825
- const rulesDir = join13(baseDir, config.rulesDir);
2913
+ const rulesDir = join16(baseDir, config.rulesDir);
2826
2914
  if (await fileExists(rulesDir)) {
2827
2915
  const rulesResult = await parseAugmentRules(rulesDir, config);
2828
2916
  addRules(result, rulesResult.rules);
@@ -2835,7 +2923,7 @@ async function parseUnifiedAugmentcode(baseDir, config) {
2835
2923
  }
2836
2924
  }
2837
2925
  if (config.legacyFilePath) {
2838
- const legacyPath = join13(baseDir, config.legacyFilePath);
2926
+ const legacyPath = join16(baseDir, config.legacyFilePath);
2839
2927
  if (await fileExists(legacyPath)) {
2840
2928
  const legacyResult = await parseAugmentGuidelines(legacyPath, config);
2841
2929
  if (legacyResult.rule) {
@@ -2859,16 +2947,16 @@ async function parseAugmentRules(rulesDir, config) {
2859
2947
  const files = await readdir2(rulesDir);
2860
2948
  for (const file of files) {
2861
2949
  if (file.endsWith(".md") || file.endsWith(".mdc")) {
2862
- const filePath = join13(rulesDir, file);
2950
+ const filePath = join16(rulesDir, file);
2863
2951
  try {
2864
2952
  const rawContent = await readFileContent(filePath);
2865
- const parsed = matter2(rawContent);
2953
+ const parsed = matter3(rawContent);
2866
2954
  const frontmatterData = parsed.data;
2867
2955
  const ruleType = frontmatterData.type || "manual";
2868
2956
  const description = frontmatterData.description || "";
2869
2957
  const tags = Array.isArray(frontmatterData.tags) ? frontmatterData.tags : void 0;
2870
2958
  const isRoot = ruleType === "always";
2871
- const filename = basename2(file, file.endsWith(".mdc") ? ".mdc" : ".md");
2959
+ const filename = basename3(file, file.endsWith(".mdc") ? ".mdc" : ".md");
2872
2960
  const frontmatter = {
2873
2961
  root: isRoot,
2874
2962
  targets: [config.targetName],
@@ -2926,8 +3014,8 @@ async function parseAugmentGuidelines(guidelinesPath, config) {
2926
3014
  }
2927
3015
 
2928
3016
  // src/parsers/shared-helpers.ts
2929
- import { basename as basename3, join as join14 } from "path";
2930
- import matter3 from "gray-matter";
3017
+ import { basename as basename4, join as join17 } from "path";
3018
+ import matter4 from "gray-matter";
2931
3019
  async function parseConfigurationFiles(baseDir = process.cwd(), config) {
2932
3020
  const errors = [];
2933
3021
  const rules = [];
@@ -2940,7 +3028,7 @@ async function parseConfigurationFiles(baseDir = process.cwd(), config) {
2940
3028
  let content;
2941
3029
  let frontmatter;
2942
3030
  if (mainFile.useFrontmatter) {
2943
- const parsed = matter3(rawContent);
3031
+ const parsed = matter4(rawContent);
2944
3032
  content = parsed.content.trim();
2945
3033
  const parsedFrontmatter = parsed.data;
2946
3034
  frontmatter = {
@@ -2982,14 +3070,14 @@ async function parseConfigurationFiles(baseDir = process.cwd(), config) {
2982
3070
  const files = await readdir2(dirPath);
2983
3071
  for (const file of files) {
2984
3072
  if (file.endsWith(dirConfig.filePattern)) {
2985
- const filePath = join14(dirPath, file);
3073
+ const filePath = join17(dirPath, file);
2986
3074
  const fileResult = await safeAsyncOperation(async () => {
2987
3075
  const rawContent = await readFileContent(filePath);
2988
3076
  let content;
2989
3077
  let frontmatter;
2990
3078
  const filename = file.replace(new RegExp(`\\${dirConfig.filePattern}$`), "");
2991
3079
  if (dirConfig.filePattern === ".instructions.md") {
2992
- const parsed = matter3(rawContent);
3080
+ const parsed = matter4(rawContent);
2993
3081
  content = parsed.content.trim();
2994
3082
  const parsedFrontmatter = parsed.data;
2995
3083
  frontmatter = {
@@ -3055,6 +3143,13 @@ async function parseMemoryBasedConfiguration(baseDir = process.cwd(), config) {
3055
3143
  const memoryRules = await parseMemoryFiles(memoryDir, config);
3056
3144
  rules.push(...memoryRules);
3057
3145
  }
3146
+ if (config.commandsDirPath) {
3147
+ const commandsDir = resolvePath(config.commandsDirPath, baseDir);
3148
+ if (await fileExists(commandsDir)) {
3149
+ const commandsRules = await parseCommandsFiles(commandsDir, config);
3150
+ rules.push(...commandsRules);
3151
+ }
3152
+ }
3058
3153
  const settingsPath = resolvePath(config.settingsPath, baseDir);
3059
3154
  if (await fileExists(settingsPath)) {
3060
3155
  const settingsResult = await parseSettingsFile(settingsPath, config.tool);
@@ -3120,10 +3215,10 @@ async function parseMemoryFiles(memoryDir, config) {
3120
3215
  const files = await readdir2(memoryDir);
3121
3216
  for (const file of files) {
3122
3217
  if (file.endsWith(".md")) {
3123
- const filePath = join14(memoryDir, file);
3218
+ const filePath = join17(memoryDir, file);
3124
3219
  const content = await readFileContent(filePath);
3125
3220
  if (content.trim()) {
3126
- const filename = basename3(file, ".md");
3221
+ const filename = basename4(file, ".md");
3127
3222
  const frontmatter = {
3128
3223
  root: false,
3129
3224
  targets: [config.tool],
@@ -3143,6 +3238,54 @@ async function parseMemoryFiles(memoryDir, config) {
3143
3238
  }
3144
3239
  return rules;
3145
3240
  }
3241
+ async function parseCommandsFiles(commandsDir, config) {
3242
+ const rules = [];
3243
+ try {
3244
+ const { readdir: readdir2 } = await import("fs/promises");
3245
+ const files = await readdir2(commandsDir);
3246
+ for (const file of files) {
3247
+ if (file.endsWith(".md")) {
3248
+ const filePath = join17(commandsDir, file);
3249
+ const content = await readFileContent(filePath);
3250
+ if (content.trim()) {
3251
+ const filename = basename4(file, ".md");
3252
+ let frontmatter;
3253
+ let ruleContent;
3254
+ try {
3255
+ const parsed = matter4(content);
3256
+ ruleContent = parsed.content.trim();
3257
+ const parsedFrontmatter = parsed.data;
3258
+ frontmatter = {
3259
+ root: false,
3260
+ targets: [config.tool],
3261
+ description: parsedFrontmatter.description || `Command: ${filename}`,
3262
+ globs: ["**/*"]
3263
+ };
3264
+ } catch {
3265
+ ruleContent = content.trim();
3266
+ frontmatter = {
3267
+ root: false,
3268
+ targets: [config.tool],
3269
+ description: `Command: ${filename}`,
3270
+ globs: ["**/*"]
3271
+ };
3272
+ }
3273
+ if (ruleContent) {
3274
+ rules.push({
3275
+ frontmatter,
3276
+ content: ruleContent,
3277
+ filename,
3278
+ filepath: filePath,
3279
+ type: "command"
3280
+ });
3281
+ }
3282
+ }
3283
+ }
3284
+ }
3285
+ } catch {
3286
+ }
3287
+ return rules;
3288
+ }
3146
3289
  async function parseSettingsFile(settingsPath, tool) {
3147
3290
  const errors = [];
3148
3291
  let ignorePatterns;
@@ -3190,7 +3333,8 @@ async function parseClaudeConfiguration(baseDir = process.cwd()) {
3190
3333
  settingsPath: ".claude/settings.json",
3191
3334
  mainDescription: "Main Claude Code configuration",
3192
3335
  memoryDescription: "Memory file",
3193
- filenamePrefix: "claude"
3336
+ filenamePrefix: "claude",
3337
+ commandsDirPath: ".claude/commands"
3194
3338
  });
3195
3339
  }
3196
3340
 
@@ -3215,7 +3359,7 @@ async function parseClineConfiguration(baseDir = process.cwd()) {
3215
3359
  }
3216
3360
 
3217
3361
  // src/parsers/codexcli.ts
3218
- import { join as join15 } from "path";
3362
+ import { join as join18 } from "path";
3219
3363
 
3220
3364
  // src/parsers/copilot.ts
3221
3365
  async function parseCopilotConfiguration(baseDir = process.cwd()) {
@@ -3238,10 +3382,10 @@ async function parseCopilotConfiguration(baseDir = process.cwd()) {
3238
3382
  }
3239
3383
 
3240
3384
  // src/parsers/cursor.ts
3241
- import { basename as basename4, join as join16 } from "path";
3242
- import matter4 from "gray-matter";
3385
+ import { basename as basename5, join as join19 } from "path";
3386
+ import matter5 from "gray-matter";
3243
3387
  import { DEFAULT_SCHEMA, FAILSAFE_SCHEMA, load } from "js-yaml";
3244
- import { z as z6 } from "zod/mini";
3388
+ import { z as z7 } from "zod/mini";
3245
3389
  var customMatterOptions = {
3246
3390
  engines: {
3247
3391
  yaml: {
@@ -3269,7 +3413,7 @@ var customMatterOptions = {
3269
3413
  }
3270
3414
  };
3271
3415
  function convertCursorMdcFrontmatter(cursorFrontmatter, _filename) {
3272
- const FrontmatterSchema = z6.record(z6.string(), z6.unknown());
3416
+ const FrontmatterSchema = z7.record(z7.string(), z7.unknown());
3273
3417
  const parseResult = FrontmatterSchema.safeParse(cursorFrontmatter);
3274
3418
  if (!parseResult.success) {
3275
3419
  return {
@@ -3363,11 +3507,11 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
3363
3507
  const rules = [];
3364
3508
  let ignorePatterns;
3365
3509
  let mcpServers;
3366
- const cursorFilePath = join16(baseDir, ".cursorrules");
3510
+ const cursorFilePath = join19(baseDir, ".cursorrules");
3367
3511
  if (await fileExists(cursorFilePath)) {
3368
3512
  try {
3369
3513
  const rawContent = await readFileContent(cursorFilePath);
3370
- const parsed = matter4(rawContent, customMatterOptions);
3514
+ const parsed = matter5(rawContent, customMatterOptions);
3371
3515
  const content = parsed.content.trim();
3372
3516
  if (content) {
3373
3517
  const frontmatter = convertCursorMdcFrontmatter(parsed.data, "cursorrules");
@@ -3384,20 +3528,20 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
3384
3528
  errors.push(`Failed to parse .cursorrules file: ${errorMessage}`);
3385
3529
  }
3386
3530
  }
3387
- const cursorRulesDir = join16(baseDir, ".cursor", "rules");
3531
+ const cursorRulesDir = join19(baseDir, ".cursor", "rules");
3388
3532
  if (await fileExists(cursorRulesDir)) {
3389
3533
  try {
3390
3534
  const { readdir: readdir2 } = await import("fs/promises");
3391
3535
  const files = await readdir2(cursorRulesDir);
3392
3536
  for (const file of files) {
3393
3537
  if (file.endsWith(".mdc")) {
3394
- const filePath = join16(cursorRulesDir, file);
3538
+ const filePath = join19(cursorRulesDir, file);
3395
3539
  try {
3396
3540
  const rawContent = await readFileContent(filePath);
3397
- const parsed = matter4(rawContent, customMatterOptions);
3541
+ const parsed = matter5(rawContent, customMatterOptions);
3398
3542
  const content = parsed.content.trim();
3399
3543
  if (content) {
3400
- const filename = basename4(file, ".mdc");
3544
+ const filename = basename5(file, ".mdc");
3401
3545
  const frontmatter = convertCursorMdcFrontmatter(parsed.data, filename);
3402
3546
  rules.push({
3403
3547
  frontmatter,
@@ -3420,7 +3564,7 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
3420
3564
  if (rules.length === 0) {
3421
3565
  errors.push("No Cursor configuration files found (.cursorrules or .cursor/rules/*.mdc)");
3422
3566
  }
3423
- const cursorIgnorePath = join16(baseDir, ".cursorignore");
3567
+ const cursorIgnorePath = join19(baseDir, ".cursorignore");
3424
3568
  if (await fileExists(cursorIgnorePath)) {
3425
3569
  try {
3426
3570
  const content = await readFileContent(cursorIgnorePath);
@@ -3433,7 +3577,7 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
3433
3577
  errors.push(`Failed to parse .cursorignore: ${errorMessage}`);
3434
3578
  }
3435
3579
  }
3436
- const cursorMcpPath = join16(baseDir, ".cursor", "mcp.json");
3580
+ const cursorMcpPath = join19(baseDir, ".cursor", "mcp.json");
3437
3581
  if (await fileExists(cursorMcpPath)) {
3438
3582
  try {
3439
3583
  const content = await readFileContent(cursorMcpPath);
@@ -3477,16 +3621,17 @@ async function parseGeminiConfiguration(baseDir = process.cwd()) {
3477
3621
  additionalIgnoreFile: {
3478
3622
  path: ".aiexclude",
3479
3623
  parser: parseAiexclude
3480
- }
3624
+ },
3625
+ commandsDirPath: ".gemini/commands"
3481
3626
  });
3482
3627
  }
3483
3628
 
3484
3629
  // src/parsers/junie.ts
3485
- import { join as join17 } from "path";
3630
+ import { join as join20 } from "path";
3486
3631
  async function parseJunieConfiguration(baseDir = process.cwd()) {
3487
3632
  const errors = [];
3488
3633
  const rules = [];
3489
- const guidelinesPath = join17(baseDir, ".junie", "guidelines.md");
3634
+ const guidelinesPath = join20(baseDir, ".junie", "guidelines.md");
3490
3635
  if (!await fileExists(guidelinesPath)) {
3491
3636
  errors.push(".junie/guidelines.md file not found");
3492
3637
  return { rules, errors };
@@ -3539,8 +3684,8 @@ async function parseRooConfiguration(baseDir = process.cwd()) {
3539
3684
 
3540
3685
  // src/parsers/windsurf.ts
3541
3686
  import { readFile as readFile2 } from "fs/promises";
3542
- import { join as join18 } from "path";
3543
- import matter5 from "gray-matter";
3687
+ import { join as join21 } from "path";
3688
+ import matter6 from "gray-matter";
3544
3689
 
3545
3690
  // src/core/importer.ts
3546
3691
  async function importConfiguration(options) {
@@ -3626,7 +3771,7 @@ async function importConfiguration(options) {
3626
3771
  if (rules.length === 0 && !ignorePatterns && !mcpServers) {
3627
3772
  return { success: false, rulesCreated: 0, errors };
3628
3773
  }
3629
- const rulesDirPath = join19(baseDir, rulesDir);
3774
+ const rulesDirPath = join22(baseDir, rulesDir);
3630
3775
  try {
3631
3776
  const { mkdir: mkdir3 } = await import("fs/promises");
3632
3777
  await mkdir3(rulesDirPath, { recursive: true });
@@ -3639,8 +3784,13 @@ async function importConfiguration(options) {
3639
3784
  for (const rule of rules) {
3640
3785
  try {
3641
3786
  const baseFilename = rule.filename;
3642
- const filename = await generateUniqueFilename(rulesDirPath, baseFilename);
3643
- const filePath = join19(rulesDirPath, `${filename}.md`);
3787
+ let targetDir = rulesDirPath;
3788
+ if (rule.type === "command") {
3789
+ targetDir = join22(rulesDirPath, "commands");
3790
+ const { mkdir: mkdir3 } = await import("fs/promises");
3791
+ await mkdir3(targetDir, { recursive: true });
3792
+ }
3793
+ const filePath = join22(targetDir, `${baseFilename}.md`);
3644
3794
  const content = generateRuleFileContent(rule);
3645
3795
  await writeFileContent(filePath, content);
3646
3796
  rulesCreated++;
@@ -3655,7 +3805,7 @@ async function importConfiguration(options) {
3655
3805
  let ignoreFileCreated = false;
3656
3806
  if (ignorePatterns && ignorePatterns.length > 0) {
3657
3807
  try {
3658
- const rulesyncignorePath = join19(baseDir, ".rulesyncignore");
3808
+ const rulesyncignorePath = join22(baseDir, ".rulesyncignore");
3659
3809
  const ignoreContent = `${ignorePatterns.join("\n")}
3660
3810
  `;
3661
3811
  await writeFileContent(rulesyncignorePath, ignoreContent);
@@ -3671,7 +3821,7 @@ async function importConfiguration(options) {
3671
3821
  let mcpFileCreated = false;
3672
3822
  if (mcpServers && Object.keys(mcpServers).length > 0) {
3673
3823
  try {
3674
- const mcpPath = join19(baseDir, rulesDir, ".mcp.json");
3824
+ const mcpPath = join22(baseDir, rulesDir, ".mcp.json");
3675
3825
  const mcpContent = `${JSON.stringify({ mcpServers }, null, 2)}
3676
3826
  `;
3677
3827
  await writeFileContent(mcpPath, mcpContent);
@@ -3693,17 +3843,16 @@ async function importConfiguration(options) {
3693
3843
  };
3694
3844
  }
3695
3845
  function generateRuleFileContent(rule) {
3696
- const frontmatter = matter6.stringify("", rule.frontmatter);
3697
- return frontmatter + rule.content;
3698
- }
3699
- async function generateUniqueFilename(rulesDir, baseFilename) {
3700
- let filename = baseFilename;
3701
- let counter = 1;
3702
- while (await fileExists(join19(rulesDir, `${filename}.md`))) {
3703
- filename = `${baseFilename}-${counter}`;
3704
- counter++;
3846
+ if (rule.type === "command") {
3847
+ const simplifiedFrontmatter = {
3848
+ description: rule.frontmatter.description,
3849
+ targets: rule.frontmatter.targets
3850
+ };
3851
+ const frontmatter2 = matter7.stringify("", simplifiedFrontmatter);
3852
+ return frontmatter2 + rule.content;
3705
3853
  }
3706
- return filename;
3854
+ const frontmatter = matter7.stringify("", rule.frontmatter);
3855
+ return frontmatter + rule.content;
3707
3856
  }
3708
3857
 
3709
3858
  // src/cli/commands/import.ts
@@ -3766,7 +3915,7 @@ async function importCommand(options = {}) {
3766
3915
  }
3767
3916
 
3768
3917
  // src/cli/commands/init.ts
3769
- import { join as join20 } from "path";
3918
+ import { join as join23 } from "path";
3770
3919
  async function initCommand() {
3771
3920
  const aiRulesDir = ".rulesync";
3772
3921
  console.log("Initializing rulesync...");
@@ -3813,7 +3962,7 @@ globs: ["**/*"]
3813
3962
  - Follow single responsibility principle
3814
3963
  `
3815
3964
  };
3816
- const filepath = join20(aiRulesDir, sampleFile.filename);
3965
+ const filepath = join23(aiRulesDir, sampleFile.filename);
3817
3966
  if (!await fileExists(filepath)) {
3818
3967
  await writeFileContent(filepath, sampleFile.content);
3819
3968
  console.log(`Created ${filepath}`);
@@ -3957,7 +4106,7 @@ async function watchCommand() {
3957
4106
 
3958
4107
  // src/cli/index.ts
3959
4108
  var program = new Command();
3960
- program.name("rulesync").description("Unified AI rules management CLI tool").version("0.57.0");
4109
+ program.name("rulesync").description("Unified AI rules management CLI tool").version("0.59.0");
3961
4110
  program.command("init").description("Initialize rulesync in current directory").action(initCommand);
3962
4111
  program.command("add <filename>").description("Add a new rule file").action(addCommand);
3963
4112
  program.command("gitignore").description("Add generated files to .gitignore").action(gitignoreCommand);