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.
- package/README.ja.md +171 -18
- package/README.md +67 -727
- package/dist/index.cjs +447 -212
- package/dist/index.js +447 -212
- package/package.json +19 -19
package/dist/index.js
CHANGED
|
@@ -129,128 +129,137 @@ var ClaudeSettingsSchema = z.looseObject({
|
|
|
129
129
|
)
|
|
130
130
|
});
|
|
131
131
|
|
|
132
|
-
// src/types/
|
|
132
|
+
// src/types/commands.ts
|
|
133
133
|
import { z as z2 } from "zod/mini";
|
|
134
|
-
var
|
|
135
|
-
|
|
136
|
-
outputPaths: z2.record(ToolTargetSchema, z2.string()),
|
|
137
|
-
watchEnabled: z2.boolean(),
|
|
138
|
-
defaultTargets: ToolTargetsSchema
|
|
134
|
+
var CommandFrontmatterSchema = z2.object({
|
|
135
|
+
description: z2.optional(z2.string())
|
|
139
136
|
});
|
|
140
137
|
|
|
141
|
-
// src/types/config
|
|
138
|
+
// src/types/config.ts
|
|
142
139
|
import { z as z3 } from "zod/mini";
|
|
143
|
-
var
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
140
|
+
var ConfigSchema = z3.object({
|
|
141
|
+
aiRulesDir: z3.string(),
|
|
142
|
+
outputPaths: z3.record(ToolTargetSchema, z3.string()),
|
|
143
|
+
watchEnabled: z3.boolean(),
|
|
144
|
+
defaultTargets: ToolTargetsSchema,
|
|
145
|
+
claudecodeCommands: z3.optional(z3.string()),
|
|
146
|
+
geminicliCommands: z3.optional(z3.string())
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
// src/types/config-options.ts
|
|
150
|
+
import { z as z4 } from "zod/mini";
|
|
151
|
+
var OutputPathsSchema = z4.object({
|
|
152
|
+
augmentcode: z4.optional(z4.string()),
|
|
153
|
+
"augmentcode-legacy": z4.optional(z4.string()),
|
|
154
|
+
copilot: z4.optional(z4.string()),
|
|
155
|
+
cursor: z4.optional(z4.string()),
|
|
156
|
+
cline: z4.optional(z4.string()),
|
|
157
|
+
claudecode: z4.optional(z4.string()),
|
|
158
|
+
codexcli: z4.optional(z4.string()),
|
|
159
|
+
roo: z4.optional(z4.string()),
|
|
160
|
+
geminicli: z4.optional(z4.string()),
|
|
161
|
+
kiro: z4.optional(z4.string()),
|
|
162
|
+
junie: z4.optional(z4.string()),
|
|
163
|
+
windsurf: z4.optional(z4.string())
|
|
156
164
|
});
|
|
157
|
-
var ConfigOptionsSchema =
|
|
158
|
-
aiRulesDir:
|
|
159
|
-
outputPaths:
|
|
160
|
-
watchEnabled:
|
|
161
|
-
defaultTargets:
|
|
162
|
-
targets:
|
|
163
|
-
exclude:
|
|
164
|
-
verbose:
|
|
165
|
-
delete:
|
|
166
|
-
baseDir:
|
|
167
|
-
watch:
|
|
168
|
-
|
|
169
|
-
enabled:
|
|
170
|
-
interval:
|
|
171
|
-
ignore:
|
|
165
|
+
var ConfigOptionsSchema = z4.object({
|
|
166
|
+
aiRulesDir: z4.optional(z4.string()),
|
|
167
|
+
outputPaths: z4.optional(OutputPathsSchema),
|
|
168
|
+
watchEnabled: z4.optional(z4.boolean()),
|
|
169
|
+
defaultTargets: z4.optional(ToolTargetsSchema),
|
|
170
|
+
targets: z4.optional(z4.array(ToolTargetSchema)),
|
|
171
|
+
exclude: z4.optional(z4.array(ToolTargetSchema)),
|
|
172
|
+
verbose: z4.optional(z4.boolean()),
|
|
173
|
+
delete: z4.optional(z4.boolean()),
|
|
174
|
+
baseDir: z4.optional(z4.union([z4.string(), z4.array(z4.string())])),
|
|
175
|
+
watch: z4.optional(
|
|
176
|
+
z4.object({
|
|
177
|
+
enabled: z4.optional(z4.boolean()),
|
|
178
|
+
interval: z4.optional(z4.number()),
|
|
179
|
+
ignore: z4.optional(z4.array(z4.string()))
|
|
172
180
|
})
|
|
173
181
|
)
|
|
174
182
|
});
|
|
175
|
-
var MergedConfigSchema =
|
|
176
|
-
aiRulesDir:
|
|
177
|
-
outputPaths:
|
|
178
|
-
watchEnabled:
|
|
183
|
+
var MergedConfigSchema = z4.object({
|
|
184
|
+
aiRulesDir: z4.string(),
|
|
185
|
+
outputPaths: z4.record(ToolTargetSchema, z4.string()),
|
|
186
|
+
watchEnabled: z4.boolean(),
|
|
179
187
|
defaultTargets: ToolTargetsSchema,
|
|
180
|
-
targets:
|
|
181
|
-
exclude:
|
|
182
|
-
verbose:
|
|
183
|
-
delete:
|
|
184
|
-
baseDir:
|
|
185
|
-
configPath:
|
|
186
|
-
watch:
|
|
187
|
-
|
|
188
|
-
enabled:
|
|
189
|
-
interval:
|
|
190
|
-
ignore:
|
|
188
|
+
targets: z4.optional(z4.array(ToolTargetSchema)),
|
|
189
|
+
exclude: z4.optional(z4.array(ToolTargetSchema)),
|
|
190
|
+
verbose: z4.optional(z4.boolean()),
|
|
191
|
+
delete: z4.optional(z4.boolean()),
|
|
192
|
+
baseDir: z4.optional(z4.union([z4.string(), z4.array(z4.string())])),
|
|
193
|
+
configPath: z4.optional(z4.string()),
|
|
194
|
+
watch: z4.optional(
|
|
195
|
+
z4.object({
|
|
196
|
+
enabled: z4.optional(z4.boolean()),
|
|
197
|
+
interval: z4.optional(z4.number()),
|
|
198
|
+
ignore: z4.optional(z4.array(z4.string()))
|
|
191
199
|
})
|
|
192
200
|
)
|
|
193
201
|
});
|
|
194
202
|
|
|
195
203
|
// src/types/mcp.ts
|
|
196
|
-
import { z as
|
|
197
|
-
var McpTransportTypeSchema =
|
|
198
|
-
var McpServerBaseSchema =
|
|
199
|
-
command:
|
|
200
|
-
args:
|
|
201
|
-
url:
|
|
202
|
-
httpUrl:
|
|
203
|
-
env:
|
|
204
|
-
disabled:
|
|
205
|
-
networkTimeout:
|
|
206
|
-
timeout:
|
|
207
|
-
trust:
|
|
208
|
-
cwd:
|
|
209
|
-
transport:
|
|
210
|
-
type:
|
|
211
|
-
alwaysAllow:
|
|
212
|
-
tools:
|
|
213
|
-
kiroAutoApprove:
|
|
214
|
-
kiroAutoBlock:
|
|
215
|
-
headers:
|
|
204
|
+
import { z as z5 } from "zod/mini";
|
|
205
|
+
var McpTransportTypeSchema = z5.enum(["stdio", "sse", "http"]);
|
|
206
|
+
var McpServerBaseSchema = z5.object({
|
|
207
|
+
command: z5.optional(z5.string()),
|
|
208
|
+
args: z5.optional(z5.array(z5.string())),
|
|
209
|
+
url: z5.optional(z5.string()),
|
|
210
|
+
httpUrl: z5.optional(z5.string()),
|
|
211
|
+
env: z5.optional(z5.record(z5.string(), z5.string())),
|
|
212
|
+
disabled: z5.optional(z5.boolean()),
|
|
213
|
+
networkTimeout: z5.optional(z5.number()),
|
|
214
|
+
timeout: z5.optional(z5.number()),
|
|
215
|
+
trust: z5.optional(z5.boolean()),
|
|
216
|
+
cwd: z5.optional(z5.string()),
|
|
217
|
+
transport: z5.optional(McpTransportTypeSchema),
|
|
218
|
+
type: z5.optional(z5.enum(["sse", "streamable-http"])),
|
|
219
|
+
alwaysAllow: z5.optional(z5.array(z5.string())),
|
|
220
|
+
tools: z5.optional(z5.array(z5.string())),
|
|
221
|
+
kiroAutoApprove: z5.optional(z5.array(z5.string())),
|
|
222
|
+
kiroAutoBlock: z5.optional(z5.array(z5.string())),
|
|
223
|
+
headers: z5.optional(z5.record(z5.string(), z5.string()))
|
|
216
224
|
});
|
|
217
|
-
var RulesyncMcpServerSchema =
|
|
218
|
-
targets:
|
|
225
|
+
var RulesyncMcpServerSchema = z5.extend(McpServerBaseSchema, {
|
|
226
|
+
targets: z5.optional(RulesyncTargetsSchema)
|
|
219
227
|
});
|
|
220
|
-
var McpConfigSchema =
|
|
221
|
-
mcpServers:
|
|
228
|
+
var McpConfigSchema = z5.object({
|
|
229
|
+
mcpServers: z5.record(z5.string(), McpServerBaseSchema)
|
|
222
230
|
});
|
|
223
|
-
var RulesyncMcpConfigSchema =
|
|
224
|
-
mcpServers:
|
|
231
|
+
var RulesyncMcpConfigSchema = z5.object({
|
|
232
|
+
mcpServers: z5.record(z5.string(), RulesyncMcpServerSchema)
|
|
225
233
|
});
|
|
226
234
|
|
|
227
235
|
// src/types/rules.ts
|
|
228
|
-
import { z as
|
|
229
|
-
var RuleFrontmatterSchema =
|
|
230
|
-
root:
|
|
231
|
-
targets:
|
|
232
|
-
description:
|
|
233
|
-
globs:
|
|
234
|
-
cursorRuleType:
|
|
235
|
-
windsurfActivationMode:
|
|
236
|
-
windsurfOutputFormat:
|
|
237
|
-
tags:
|
|
236
|
+
import { z as z6 } from "zod/mini";
|
|
237
|
+
var RuleFrontmatterSchema = z6.object({
|
|
238
|
+
root: z6.optional(z6.boolean()),
|
|
239
|
+
targets: z6.optional(RulesyncTargetsSchema),
|
|
240
|
+
description: z6.optional(z6.string()),
|
|
241
|
+
globs: z6.optional(z6.array(z6.string())),
|
|
242
|
+
cursorRuleType: z6.optional(z6.enum(["always", "manual", "specificFiles", "intelligently"])),
|
|
243
|
+
windsurfActivationMode: z6.optional(z6.enum(["always", "manual", "model-decision", "glob"])),
|
|
244
|
+
windsurfOutputFormat: z6.optional(z6.enum(["single-file", "directory"])),
|
|
245
|
+
tags: z6.optional(z6.array(z6.string()))
|
|
238
246
|
});
|
|
239
|
-
var ParsedRuleSchema =
|
|
247
|
+
var ParsedRuleSchema = z6.object({
|
|
240
248
|
frontmatter: RuleFrontmatterSchema,
|
|
241
|
-
content:
|
|
242
|
-
filename:
|
|
243
|
-
filepath:
|
|
249
|
+
content: z6.string(),
|
|
250
|
+
filename: z6.string(),
|
|
251
|
+
filepath: z6.string(),
|
|
252
|
+
type: z6.optional(z6.enum(["rule", "command"]))
|
|
244
253
|
});
|
|
245
|
-
var GeneratedOutputSchema =
|
|
254
|
+
var GeneratedOutputSchema = z6.object({
|
|
246
255
|
tool: ToolTargetSchema,
|
|
247
|
-
filepath:
|
|
248
|
-
content:
|
|
256
|
+
filepath: z6.string(),
|
|
257
|
+
content: z6.string()
|
|
249
258
|
});
|
|
250
|
-
var GenerateOptionsSchema =
|
|
251
|
-
targetTools:
|
|
252
|
-
outputDir:
|
|
253
|
-
watch:
|
|
259
|
+
var GenerateOptionsSchema = z6.object({
|
|
260
|
+
targetTools: z6.optional(ToolTargetsSchema),
|
|
261
|
+
outputDir: z6.optional(z6.string()),
|
|
262
|
+
watch: z6.optional(z6.boolean())
|
|
254
263
|
});
|
|
255
264
|
|
|
256
265
|
// src/utils/config-loader.ts
|
|
@@ -542,28 +551,6 @@ async function removeClaudeGeneratedFiles() {
|
|
|
542
551
|
}
|
|
543
552
|
}
|
|
544
553
|
|
|
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
554
|
// src/cli/commands/config.ts
|
|
568
555
|
async function configCommand(options = {}) {
|
|
569
556
|
if (options.init) {
|
|
@@ -775,10 +762,165 @@ export default config;
|
|
|
775
762
|
}
|
|
776
763
|
|
|
777
764
|
// src/cli/commands/generate.ts
|
|
778
|
-
import { join as
|
|
765
|
+
import { join as join14 } from "path";
|
|
779
766
|
|
|
780
|
-
// src/
|
|
767
|
+
// src/core/command-generator.ts
|
|
768
|
+
import { join as join5 } from "path";
|
|
769
|
+
|
|
770
|
+
// src/generators/commands/claudecode.ts
|
|
781
771
|
import { join as join3 } from "path";
|
|
772
|
+
var ClaudeCodeCommandGenerator = class {
|
|
773
|
+
generate(command, outputDir) {
|
|
774
|
+
const filepath = this.getOutputPath(command.filename, outputDir);
|
|
775
|
+
const frontmatter = ["---"];
|
|
776
|
+
if (command.frontmatter.description) {
|
|
777
|
+
frontmatter.push(`description: ${command.frontmatter.description}`);
|
|
778
|
+
}
|
|
779
|
+
frontmatter.push("---");
|
|
780
|
+
const content = `${frontmatter.join("\n")}
|
|
781
|
+
|
|
782
|
+
${command.content.trim()}
|
|
783
|
+
`;
|
|
784
|
+
return {
|
|
785
|
+
tool: "claudecode",
|
|
786
|
+
filepath,
|
|
787
|
+
content
|
|
788
|
+
};
|
|
789
|
+
}
|
|
790
|
+
getOutputPath(filename, baseDir) {
|
|
791
|
+
const flattenedName = filename.replace(/\//g, "-");
|
|
792
|
+
return join3(baseDir, ".claude", "commands", `${flattenedName}.md`);
|
|
793
|
+
}
|
|
794
|
+
};
|
|
795
|
+
|
|
796
|
+
// src/generators/commands/geminicli.ts
|
|
797
|
+
import { join as join4 } from "path";
|
|
798
|
+
var GeminiCliCommandGenerator = class {
|
|
799
|
+
generate(command, outputDir) {
|
|
800
|
+
const filepath = this.getOutputPath(command.filename, outputDir);
|
|
801
|
+
const convertedContent = this.convertSyntax(command.content);
|
|
802
|
+
const tomlLines = [];
|
|
803
|
+
if (command.frontmatter.description) {
|
|
804
|
+
tomlLines.push(`description = "${this.escapeTomlString(command.frontmatter.description)}"`);
|
|
805
|
+
tomlLines.push("");
|
|
806
|
+
}
|
|
807
|
+
tomlLines.push(`prompt = """${convertedContent}"""`);
|
|
808
|
+
const content = tomlLines.join("\n") + "\n";
|
|
809
|
+
return {
|
|
810
|
+
tool: "geminicli",
|
|
811
|
+
filepath,
|
|
812
|
+
content
|
|
813
|
+
};
|
|
814
|
+
}
|
|
815
|
+
getOutputPath(filename, baseDir) {
|
|
816
|
+
const tomlFilename = filename.replace(/\.md$/, ".toml");
|
|
817
|
+
const filenameWithExt = tomlFilename.endsWith(".toml") ? tomlFilename : `${tomlFilename}.toml`;
|
|
818
|
+
return join4(baseDir, ".gemini", "commands", filenameWithExt);
|
|
819
|
+
}
|
|
820
|
+
convertSyntax(content) {
|
|
821
|
+
let converted = content;
|
|
822
|
+
converted = converted.replace(/\$ARGUMENTS/g, "{{args}}");
|
|
823
|
+
converted = converted.replace(/!`([^`]+)`/g, "!{$1}");
|
|
824
|
+
const atSyntaxMatches = converted.match(/@[^\s]+/g);
|
|
825
|
+
if (atSyntaxMatches) {
|
|
826
|
+
console.warn(
|
|
827
|
+
`\u26A0\uFE0F Warning: @ syntax found (${atSyntaxMatches.join(", ")}). Gemini CLI does not support file content injection. Consider using shell commands or remove these references.`
|
|
828
|
+
);
|
|
829
|
+
}
|
|
830
|
+
return converted.trim();
|
|
831
|
+
}
|
|
832
|
+
escapeTomlString(str) {
|
|
833
|
+
return str.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/\t/g, "\\t");
|
|
834
|
+
}
|
|
835
|
+
};
|
|
836
|
+
|
|
837
|
+
// src/generators/commands/index.ts
|
|
838
|
+
var commandGenerators = {
|
|
839
|
+
claudecode: new ClaudeCodeCommandGenerator(),
|
|
840
|
+
geminicli: new GeminiCliCommandGenerator()
|
|
841
|
+
};
|
|
842
|
+
function getCommandGenerator(tool) {
|
|
843
|
+
return commandGenerators[tool];
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
// src/core/command-parser.ts
|
|
847
|
+
import { basename } from "path";
|
|
848
|
+
import matter from "gray-matter";
|
|
849
|
+
async function parseCommandsFromDirectory(commandsDir) {
|
|
850
|
+
const commandFiles = await findFiles(commandsDir, ".md");
|
|
851
|
+
const commands = [];
|
|
852
|
+
const errors = [];
|
|
853
|
+
for (const filepath of commandFiles) {
|
|
854
|
+
try {
|
|
855
|
+
const command = await parseCommandFile(filepath);
|
|
856
|
+
commands.push(command);
|
|
857
|
+
} catch (error) {
|
|
858
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
859
|
+
errors.push(`Failed to parse command file ${filepath}: ${errorMessage}`);
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
if (errors.length > 0) {
|
|
863
|
+
console.warn(`\u26A0\uFE0F Command parsing errors:
|
|
864
|
+
${errors.join("\n")}`);
|
|
865
|
+
}
|
|
866
|
+
return commands;
|
|
867
|
+
}
|
|
868
|
+
async function parseCommandFile(filepath) {
|
|
869
|
+
const content = await readFileContent(filepath);
|
|
870
|
+
const parsed = matter(content);
|
|
871
|
+
try {
|
|
872
|
+
const validatedData = CommandFrontmatterSchema.parse(parsed.data);
|
|
873
|
+
const filename = basename(filepath, ".md");
|
|
874
|
+
return {
|
|
875
|
+
frontmatter: {
|
|
876
|
+
description: validatedData.description
|
|
877
|
+
},
|
|
878
|
+
content: parsed.content,
|
|
879
|
+
filename,
|
|
880
|
+
filepath
|
|
881
|
+
};
|
|
882
|
+
} catch (error) {
|
|
883
|
+
throw new Error(
|
|
884
|
+
`Invalid frontmatter in ${filepath}: ${error instanceof Error ? error.message : String(error)}`
|
|
885
|
+
);
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
// src/core/command-generator.ts
|
|
890
|
+
async function generateCommands(projectRoot, baseDir, targets) {
|
|
891
|
+
const commandsDir = join5(projectRoot, ".rulesync", "commands");
|
|
892
|
+
if (!await fileExists(commandsDir)) {
|
|
893
|
+
return [];
|
|
894
|
+
}
|
|
895
|
+
const commands = await parseCommandsFromDirectory(commandsDir);
|
|
896
|
+
if (commands.length === 0) {
|
|
897
|
+
return [];
|
|
898
|
+
}
|
|
899
|
+
const outputs = [];
|
|
900
|
+
const outputDir = baseDir || projectRoot;
|
|
901
|
+
const supportedTargets = targets.filter((target) => ["claudecode", "geminicli"].includes(target));
|
|
902
|
+
for (const target of supportedTargets) {
|
|
903
|
+
const generator = getCommandGenerator(target);
|
|
904
|
+
if (!generator) {
|
|
905
|
+
continue;
|
|
906
|
+
}
|
|
907
|
+
for (const command of commands) {
|
|
908
|
+
try {
|
|
909
|
+
const output = generator.generate(command, outputDir);
|
|
910
|
+
outputs.push(output);
|
|
911
|
+
} catch (error) {
|
|
912
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
913
|
+
console.error(
|
|
914
|
+
`\u274C Failed to generate ${target} command for ${command.filename}: ${errorMessage}`
|
|
915
|
+
);
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
return outputs;
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
// src/generators/ignore/shared-factory.ts
|
|
923
|
+
import { join as join6 } from "path";
|
|
782
924
|
|
|
783
925
|
// src/generators/ignore/shared-helpers.ts
|
|
784
926
|
function extractIgnorePatternsFromRules(rules) {
|
|
@@ -896,7 +1038,7 @@ function generateIgnoreFile(rules, config, ignoreConfig, baseDir) {
|
|
|
896
1038
|
const outputs = [];
|
|
897
1039
|
const content = generateIgnoreContent(rules, ignoreConfig);
|
|
898
1040
|
const outputPath = baseDir || process.cwd();
|
|
899
|
-
const filepath =
|
|
1041
|
+
const filepath = join6(outputPath, ignoreConfig.filename);
|
|
900
1042
|
outputs.push({
|
|
901
1043
|
tool: ignoreConfig.tool,
|
|
902
1044
|
filepath,
|
|
@@ -1484,20 +1626,20 @@ function generateWindsurfIgnore(rules, config, baseDir) {
|
|
|
1484
1626
|
}
|
|
1485
1627
|
|
|
1486
1628
|
// src/generators/rules/augmentcode.ts
|
|
1487
|
-
import { join as
|
|
1629
|
+
import { join as join9 } from "path";
|
|
1488
1630
|
|
|
1489
1631
|
// src/generators/rules/shared-helpers.ts
|
|
1490
|
-
import { join as
|
|
1632
|
+
import { join as join8 } from "path";
|
|
1491
1633
|
|
|
1492
1634
|
// src/utils/ignore.ts
|
|
1493
|
-
import { join as
|
|
1635
|
+
import { join as join7 } from "path";
|
|
1494
1636
|
import micromatch from "micromatch";
|
|
1495
1637
|
var cachedIgnorePatterns = null;
|
|
1496
1638
|
async function loadIgnorePatterns(baseDir = process.cwd()) {
|
|
1497
1639
|
if (cachedIgnorePatterns) {
|
|
1498
1640
|
return cachedIgnorePatterns;
|
|
1499
1641
|
}
|
|
1500
|
-
const ignorePath =
|
|
1642
|
+
const ignorePath = join7(baseDir, ".rulesyncignore");
|
|
1501
1643
|
if (!await fileExists(ignorePath)) {
|
|
1502
1644
|
cachedIgnorePatterns = { patterns: [] };
|
|
1503
1645
|
return cachedIgnorePatterns;
|
|
@@ -1551,7 +1693,7 @@ function addOutput(outputs, tool, config, baseDir, relativePath, content) {
|
|
|
1551
1693
|
const outputDir = resolveOutputDir(config, tool, baseDir);
|
|
1552
1694
|
outputs.push({
|
|
1553
1695
|
tool,
|
|
1554
|
-
filepath:
|
|
1696
|
+
filepath: join8(outputDir, relativePath),
|
|
1555
1697
|
content
|
|
1556
1698
|
});
|
|
1557
1699
|
}
|
|
@@ -1560,7 +1702,7 @@ async function generateRulesConfig(rules, config, generatorConfig, baseDir) {
|
|
|
1560
1702
|
for (const rule of rules) {
|
|
1561
1703
|
const content = generatorConfig.generateContent(rule);
|
|
1562
1704
|
const outputDir = resolveOutputDir(config, generatorConfig.tool, baseDir);
|
|
1563
|
-
const filepath = generatorConfig.pathResolver ? generatorConfig.pathResolver(rule, outputDir) :
|
|
1705
|
+
const filepath = generatorConfig.pathResolver ? generatorConfig.pathResolver(rule, outputDir) : join8(outputDir, `${rule.filename}${generatorConfig.fileExtension}`);
|
|
1564
1706
|
outputs.push({
|
|
1565
1707
|
tool: generatorConfig.tool,
|
|
1566
1708
|
filepath,
|
|
@@ -1568,7 +1710,7 @@ async function generateRulesConfig(rules, config, generatorConfig, baseDir) {
|
|
|
1568
1710
|
});
|
|
1569
1711
|
}
|
|
1570
1712
|
const ignorePatterns = await loadIgnorePatterns(baseDir);
|
|
1571
|
-
if (ignorePatterns.patterns.length > 0) {
|
|
1713
|
+
if (ignorePatterns.patterns.length > 0 && generatorConfig.ignoreFileName) {
|
|
1572
1714
|
const ignorePath = resolvePath(generatorConfig.ignoreFileName, baseDir);
|
|
1573
1715
|
const ignoreContent = generateIgnoreFile2(ignorePatterns.patterns, generatorConfig.tool);
|
|
1574
1716
|
outputs.push({
|
|
@@ -1588,7 +1730,7 @@ async function generateComplexRules(rules, config, generatorConfig, baseDir) {
|
|
|
1588
1730
|
for (const rule of detailRules) {
|
|
1589
1731
|
const content = generatorConfig.generateDetailContent(rule);
|
|
1590
1732
|
const filepath = resolvePath(
|
|
1591
|
-
|
|
1733
|
+
join8(generatorConfig.detailSubDir, `${rule.filename}.md`),
|
|
1592
1734
|
baseDir
|
|
1593
1735
|
);
|
|
1594
1736
|
outputs.push({
|
|
@@ -1609,13 +1751,15 @@ async function generateComplexRules(rules, config, generatorConfig, baseDir) {
|
|
|
1609
1751
|
}
|
|
1610
1752
|
const ignorePatterns = await loadIgnorePatterns(baseDir);
|
|
1611
1753
|
if (ignorePatterns.patterns.length > 0) {
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1754
|
+
if (generatorConfig.ignoreFileName) {
|
|
1755
|
+
const ignorePath = resolvePath(generatorConfig.ignoreFileName, baseDir);
|
|
1756
|
+
const ignoreContent = generateIgnoreFile2(ignorePatterns.patterns, generatorConfig.tool);
|
|
1757
|
+
outputs.push({
|
|
1758
|
+
tool: generatorConfig.tool,
|
|
1759
|
+
filepath: ignorePath,
|
|
1760
|
+
content: ignoreContent
|
|
1761
|
+
});
|
|
1762
|
+
}
|
|
1619
1763
|
if (generatorConfig.updateAdditionalConfig) {
|
|
1620
1764
|
const additionalOutputs = await generatorConfig.updateAdditionalConfig(
|
|
1621
1765
|
ignorePatterns.patterns,
|
|
@@ -1649,7 +1793,7 @@ async function generateAugmentcodeConfig(rules, config, baseDir) {
|
|
|
1649
1793
|
"augmentcode",
|
|
1650
1794
|
config,
|
|
1651
1795
|
baseDir,
|
|
1652
|
-
|
|
1796
|
+
join9(".augment", "rules", `${rule.filename}.md`),
|
|
1653
1797
|
generateRuleFile(rule)
|
|
1654
1798
|
);
|
|
1655
1799
|
});
|
|
@@ -1702,19 +1846,19 @@ function generateLegacyGuidelinesFile(allRules) {
|
|
|
1702
1846
|
}
|
|
1703
1847
|
|
|
1704
1848
|
// src/generators/rules/claudecode.ts
|
|
1705
|
-
import { join as
|
|
1849
|
+
import { join as join10 } from "path";
|
|
1706
1850
|
async function generateClaudecodeConfig(rules, config, baseDir) {
|
|
1707
1851
|
const generatorConfig = {
|
|
1708
1852
|
tool: "claudecode",
|
|
1709
1853
|
fileExtension: ".md",
|
|
1710
|
-
ignoreFileName
|
|
1854
|
+
// ignoreFileName omitted - Claude Code uses settings.json permissions.deny instead of ignore files
|
|
1711
1855
|
generateContent: generateMemoryFile,
|
|
1712
1856
|
generateRootContent: generateClaudeMarkdown,
|
|
1713
1857
|
rootFilePath: "CLAUDE.md",
|
|
1714
1858
|
generateDetailContent: generateMemoryFile,
|
|
1715
1859
|
detailSubDir: ".claude/memories",
|
|
1716
1860
|
updateAdditionalConfig: async (ignorePatterns, baseDir2) => {
|
|
1717
|
-
const settingsPath = resolvePath(
|
|
1861
|
+
const settingsPath = resolvePath(join10(".claude", "settings.json"), baseDir2);
|
|
1718
1862
|
await updateClaudeSettings(settingsPath, ignorePatterns);
|
|
1719
1863
|
return [];
|
|
1720
1864
|
}
|
|
@@ -1778,7 +1922,7 @@ async function updateClaudeSettings(settingsPath, ignorePatterns) {
|
|
|
1778
1922
|
}
|
|
1779
1923
|
|
|
1780
1924
|
// src/generators/rules/generator-registry.ts
|
|
1781
|
-
import { join as
|
|
1925
|
+
import { join as join11 } from "path";
|
|
1782
1926
|
function determineCursorRuleType(frontmatter) {
|
|
1783
1927
|
if (frontmatter.cursorRuleType) {
|
|
1784
1928
|
return frontmatter.cursorRuleType;
|
|
@@ -1858,7 +2002,7 @@ var GENERATOR_REGISTRY = {
|
|
|
1858
2002
|
},
|
|
1859
2003
|
pathResolver: (rule, outputDir) => {
|
|
1860
2004
|
const baseFilename = rule.filename.replace(/\.md$/, "");
|
|
1861
|
-
return
|
|
2005
|
+
return join11(outputDir, `${baseFilename}.instructions.md`);
|
|
1862
2006
|
}
|
|
1863
2007
|
},
|
|
1864
2008
|
cursor: {
|
|
@@ -1898,7 +2042,7 @@ var GENERATOR_REGISTRY = {
|
|
|
1898
2042
|
return lines.join("\n");
|
|
1899
2043
|
},
|
|
1900
2044
|
pathResolver: (rule, outputDir) => {
|
|
1901
|
-
return
|
|
2045
|
+
return join11(outputDir, `${rule.filename}.mdc`);
|
|
1902
2046
|
}
|
|
1903
2047
|
},
|
|
1904
2048
|
codexcli: {
|
|
@@ -1934,10 +2078,10 @@ var GENERATOR_REGISTRY = {
|
|
|
1934
2078
|
pathResolver: (rule, outputDir) => {
|
|
1935
2079
|
const outputFormat = rule.frontmatter.windsurfOutputFormat || "directory";
|
|
1936
2080
|
if (outputFormat === "single-file") {
|
|
1937
|
-
return
|
|
2081
|
+
return join11(outputDir, ".windsurf-rules");
|
|
1938
2082
|
} else {
|
|
1939
|
-
const rulesDir =
|
|
1940
|
-
return
|
|
2083
|
+
const rulesDir = join11(outputDir, ".windsurf", "rules");
|
|
2084
|
+
return join11(rulesDir, `${rule.filename}.md`);
|
|
1941
2085
|
}
|
|
1942
2086
|
}
|
|
1943
2087
|
},
|
|
@@ -2201,7 +2345,7 @@ function filterRulesForTool(rules, tool, config) {
|
|
|
2201
2345
|
if (!targets.includes(tool)) {
|
|
2202
2346
|
return false;
|
|
2203
2347
|
}
|
|
2204
|
-
return
|
|
2348
|
+
return true;
|
|
2205
2349
|
});
|
|
2206
2350
|
}
|
|
2207
2351
|
async function generateForTool(tool, rules, config, baseDir) {
|
|
@@ -2256,8 +2400,8 @@ async function generateForTool(tool, rules, config, baseDir) {
|
|
|
2256
2400
|
}
|
|
2257
2401
|
|
|
2258
2402
|
// src/core/parser.ts
|
|
2259
|
-
import { basename } from "path";
|
|
2260
|
-
import
|
|
2403
|
+
import { basename as basename2 } from "path";
|
|
2404
|
+
import matter2 from "gray-matter";
|
|
2261
2405
|
async function parseRulesFromDirectory(aiRulesDir) {
|
|
2262
2406
|
const ignorePatterns = await loadIgnorePatterns();
|
|
2263
2407
|
const allRuleFiles = await findFiles(aiRulesDir, ".md");
|
|
@@ -2291,7 +2435,7 @@ ${errors.join("\n")}`);
|
|
|
2291
2435
|
}
|
|
2292
2436
|
async function parseRuleFile(filepath) {
|
|
2293
2437
|
const content = await readFileContent(filepath);
|
|
2294
|
-
const parsed =
|
|
2438
|
+
const parsed = matter2(content);
|
|
2295
2439
|
try {
|
|
2296
2440
|
const validatedData = RuleFrontmatterSchema.parse(parsed.data);
|
|
2297
2441
|
const frontmatter = {
|
|
@@ -2310,7 +2454,7 @@ async function parseRuleFile(filepath) {
|
|
|
2310
2454
|
},
|
|
2311
2455
|
...validatedData.tags !== void 0 && { tags: validatedData.tags }
|
|
2312
2456
|
};
|
|
2313
|
-
const filename =
|
|
2457
|
+
const filename = basename2(filepath, ".md");
|
|
2314
2458
|
return {
|
|
2315
2459
|
frontmatter,
|
|
2316
2460
|
content: parsed.content,
|
|
@@ -2600,12 +2744,12 @@ async function generateCommand(options = {}) {
|
|
|
2600
2744
|
for (const tool of targetTools) {
|
|
2601
2745
|
switch (tool) {
|
|
2602
2746
|
case "augmentcode":
|
|
2603
|
-
deleteTasks.push(removeDirectory(
|
|
2604
|
-
deleteTasks.push(removeDirectory(
|
|
2747
|
+
deleteTasks.push(removeDirectory(join14(".augment", "rules")));
|
|
2748
|
+
deleteTasks.push(removeDirectory(join14(".augment", "ignore")));
|
|
2605
2749
|
break;
|
|
2606
2750
|
case "augmentcode-legacy":
|
|
2607
2751
|
deleteTasks.push(removeClaudeGeneratedFiles());
|
|
2608
|
-
deleteTasks.push(removeDirectory(
|
|
2752
|
+
deleteTasks.push(removeDirectory(join14(".augment", "ignore")));
|
|
2609
2753
|
break;
|
|
2610
2754
|
case "copilot":
|
|
2611
2755
|
deleteTasks.push(removeDirectory(config.outputPaths.copilot));
|
|
@@ -2618,12 +2762,14 @@ async function generateCommand(options = {}) {
|
|
|
2618
2762
|
break;
|
|
2619
2763
|
case "claudecode":
|
|
2620
2764
|
deleteTasks.push(removeClaudeGeneratedFiles());
|
|
2765
|
+
deleteTasks.push(removeDirectory(join14(".claude", "commands")));
|
|
2621
2766
|
break;
|
|
2622
2767
|
case "roo":
|
|
2623
2768
|
deleteTasks.push(removeDirectory(config.outputPaths.roo));
|
|
2624
2769
|
break;
|
|
2625
2770
|
case "geminicli":
|
|
2626
2771
|
deleteTasks.push(removeDirectory(config.outputPaths.geminicli));
|
|
2772
|
+
deleteTasks.push(removeDirectory(join14(".gemini", "commands")));
|
|
2627
2773
|
break;
|
|
2628
2774
|
case "kiro":
|
|
2629
2775
|
deleteTasks.push(removeDirectory(config.outputPaths.kiro));
|
|
@@ -2688,11 +2834,37 @@ Generating configurations for base directory: ${baseDir}`);
|
|
|
2688
2834
|
}
|
|
2689
2835
|
}
|
|
2690
2836
|
}
|
|
2691
|
-
|
|
2837
|
+
if (config.verbose) {
|
|
2838
|
+
console.log("\nGenerating command files...");
|
|
2839
|
+
}
|
|
2840
|
+
let totalCommandOutputs = 0;
|
|
2841
|
+
for (const baseDir of baseDirs) {
|
|
2842
|
+
const commandResults = await generateCommands(
|
|
2843
|
+
process.cwd(),
|
|
2844
|
+
baseDir === process.cwd() ? void 0 : baseDir,
|
|
2845
|
+
config.defaultTargets
|
|
2846
|
+
);
|
|
2847
|
+
if (commandResults.length === 0) {
|
|
2848
|
+
if (config.verbose) {
|
|
2849
|
+
console.log(`No commands found for ${baseDir}`);
|
|
2850
|
+
}
|
|
2851
|
+
continue;
|
|
2852
|
+
}
|
|
2853
|
+
for (const result of commandResults) {
|
|
2854
|
+
await writeFileContent(result.filepath, result.content);
|
|
2855
|
+
console.log(`\u2705 Generated ${result.tool} command: ${result.filepath}`);
|
|
2856
|
+
totalCommandOutputs++;
|
|
2857
|
+
}
|
|
2858
|
+
}
|
|
2859
|
+
const totalGenerated = totalOutputs + totalMcpOutputs + totalCommandOutputs;
|
|
2692
2860
|
if (totalGenerated > 0) {
|
|
2861
|
+
const parts = [];
|
|
2862
|
+
if (totalOutputs > 0) parts.push(`${totalOutputs} configurations`);
|
|
2863
|
+
if (totalMcpOutputs > 0) parts.push(`${totalMcpOutputs} MCP configurations`);
|
|
2864
|
+
if (totalCommandOutputs > 0) parts.push(`${totalCommandOutputs} commands`);
|
|
2693
2865
|
console.log(
|
|
2694
2866
|
`
|
|
2695
|
-
\u{1F389} All done! Generated ${totalGenerated} file(s) total (${
|
|
2867
|
+
\u{1F389} All done! Generated ${totalGenerated} file(s) total (${parts.join(" + ")})`
|
|
2696
2868
|
);
|
|
2697
2869
|
}
|
|
2698
2870
|
} catch (error) {
|
|
@@ -2703,9 +2875,9 @@ Generating configurations for base directory: ${baseDir}`);
|
|
|
2703
2875
|
|
|
2704
2876
|
// src/cli/commands/gitignore.ts
|
|
2705
2877
|
import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
2706
|
-
import { join as
|
|
2878
|
+
import { join as join15 } from "path";
|
|
2707
2879
|
var gitignoreCommand = async () => {
|
|
2708
|
-
const gitignorePath =
|
|
2880
|
+
const gitignorePath = join15(process.cwd(), ".gitignore");
|
|
2709
2881
|
const rulesFilesToIgnore = [
|
|
2710
2882
|
"# Generated by rulesync - AI tool configuration files",
|
|
2711
2883
|
"**/.github/copilot-instructions.md",
|
|
@@ -2716,6 +2888,7 @@ var gitignoreCommand = async () => {
|
|
|
2716
2888
|
"**/.clineignore",
|
|
2717
2889
|
"**/CLAUDE.md",
|
|
2718
2890
|
"**/.claude/memories/",
|
|
2891
|
+
"**/.claude/commands/",
|
|
2719
2892
|
"**/codex.md",
|
|
2720
2893
|
"**/.codexignore",
|
|
2721
2894
|
"**/.roo/rules/",
|
|
@@ -2723,6 +2896,7 @@ var gitignoreCommand = async () => {
|
|
|
2723
2896
|
"**/.copilotignore",
|
|
2724
2897
|
"**/GEMINI.md",
|
|
2725
2898
|
"**/.gemini/memories/",
|
|
2899
|
+
"**/.gemini/commands/",
|
|
2726
2900
|
"**/.aiexclude",
|
|
2727
2901
|
"**/.aiignore",
|
|
2728
2902
|
"**/.augmentignore",
|
|
@@ -2769,12 +2943,12 @@ ${linesToAdd.join("\n")}
|
|
|
2769
2943
|
};
|
|
2770
2944
|
|
|
2771
2945
|
// src/core/importer.ts
|
|
2772
|
-
import { join as
|
|
2773
|
-
import
|
|
2946
|
+
import { join as join22 } from "path";
|
|
2947
|
+
import matter7 from "gray-matter";
|
|
2774
2948
|
|
|
2775
2949
|
// src/parsers/augmentcode.ts
|
|
2776
|
-
import { basename as
|
|
2777
|
-
import
|
|
2950
|
+
import { basename as basename3, join as join16 } from "path";
|
|
2951
|
+
import matter3 from "gray-matter";
|
|
2778
2952
|
|
|
2779
2953
|
// src/utils/parser-helpers.ts
|
|
2780
2954
|
function createParseResult() {
|
|
@@ -2822,7 +2996,7 @@ async function parseAugmentcodeLegacyConfiguration(baseDir = process.cwd()) {
|
|
|
2822
2996
|
async function parseUnifiedAugmentcode(baseDir, config) {
|
|
2823
2997
|
const result = createParseResult();
|
|
2824
2998
|
if (config.rulesDir) {
|
|
2825
|
-
const rulesDir =
|
|
2999
|
+
const rulesDir = join16(baseDir, config.rulesDir);
|
|
2826
3000
|
if (await fileExists(rulesDir)) {
|
|
2827
3001
|
const rulesResult = await parseAugmentRules(rulesDir, config);
|
|
2828
3002
|
addRules(result, rulesResult.rules);
|
|
@@ -2835,7 +3009,7 @@ async function parseUnifiedAugmentcode(baseDir, config) {
|
|
|
2835
3009
|
}
|
|
2836
3010
|
}
|
|
2837
3011
|
if (config.legacyFilePath) {
|
|
2838
|
-
const legacyPath =
|
|
3012
|
+
const legacyPath = join16(baseDir, config.legacyFilePath);
|
|
2839
3013
|
if (await fileExists(legacyPath)) {
|
|
2840
3014
|
const legacyResult = await parseAugmentGuidelines(legacyPath, config);
|
|
2841
3015
|
if (legacyResult.rule) {
|
|
@@ -2859,16 +3033,16 @@ async function parseAugmentRules(rulesDir, config) {
|
|
|
2859
3033
|
const files = await readdir2(rulesDir);
|
|
2860
3034
|
for (const file of files) {
|
|
2861
3035
|
if (file.endsWith(".md") || file.endsWith(".mdc")) {
|
|
2862
|
-
const filePath =
|
|
3036
|
+
const filePath = join16(rulesDir, file);
|
|
2863
3037
|
try {
|
|
2864
3038
|
const rawContent = await readFileContent(filePath);
|
|
2865
|
-
const parsed =
|
|
3039
|
+
const parsed = matter3(rawContent);
|
|
2866
3040
|
const frontmatterData = parsed.data;
|
|
2867
3041
|
const ruleType = frontmatterData.type || "manual";
|
|
2868
3042
|
const description = frontmatterData.description || "";
|
|
2869
3043
|
const tags = Array.isArray(frontmatterData.tags) ? frontmatterData.tags : void 0;
|
|
2870
3044
|
const isRoot = ruleType === "always";
|
|
2871
|
-
const filename =
|
|
3045
|
+
const filename = basename3(file, file.endsWith(".mdc") ? ".mdc" : ".md");
|
|
2872
3046
|
const frontmatter = {
|
|
2873
3047
|
root: isRoot,
|
|
2874
3048
|
targets: [config.targetName],
|
|
@@ -2926,8 +3100,8 @@ async function parseAugmentGuidelines(guidelinesPath, config) {
|
|
|
2926
3100
|
}
|
|
2927
3101
|
|
|
2928
3102
|
// src/parsers/shared-helpers.ts
|
|
2929
|
-
import { basename as
|
|
2930
|
-
import
|
|
3103
|
+
import { basename as basename4, join as join17 } from "path";
|
|
3104
|
+
import matter4 from "gray-matter";
|
|
2931
3105
|
async function parseConfigurationFiles(baseDir = process.cwd(), config) {
|
|
2932
3106
|
const errors = [];
|
|
2933
3107
|
const rules = [];
|
|
@@ -2940,7 +3114,7 @@ async function parseConfigurationFiles(baseDir = process.cwd(), config) {
|
|
|
2940
3114
|
let content;
|
|
2941
3115
|
let frontmatter;
|
|
2942
3116
|
if (mainFile.useFrontmatter) {
|
|
2943
|
-
const parsed =
|
|
3117
|
+
const parsed = matter4(rawContent);
|
|
2944
3118
|
content = parsed.content.trim();
|
|
2945
3119
|
const parsedFrontmatter = parsed.data;
|
|
2946
3120
|
frontmatter = {
|
|
@@ -2982,14 +3156,14 @@ async function parseConfigurationFiles(baseDir = process.cwd(), config) {
|
|
|
2982
3156
|
const files = await readdir2(dirPath);
|
|
2983
3157
|
for (const file of files) {
|
|
2984
3158
|
if (file.endsWith(dirConfig.filePattern)) {
|
|
2985
|
-
const filePath =
|
|
3159
|
+
const filePath = join17(dirPath, file);
|
|
2986
3160
|
const fileResult = await safeAsyncOperation(async () => {
|
|
2987
3161
|
const rawContent = await readFileContent(filePath);
|
|
2988
3162
|
let content;
|
|
2989
3163
|
let frontmatter;
|
|
2990
3164
|
const filename = file.replace(new RegExp(`\\${dirConfig.filePattern}$`), "");
|
|
2991
3165
|
if (dirConfig.filePattern === ".instructions.md") {
|
|
2992
|
-
const parsed =
|
|
3166
|
+
const parsed = matter4(rawContent);
|
|
2993
3167
|
content = parsed.content.trim();
|
|
2994
3168
|
const parsedFrontmatter = parsed.data;
|
|
2995
3169
|
frontmatter = {
|
|
@@ -3055,6 +3229,13 @@ async function parseMemoryBasedConfiguration(baseDir = process.cwd(), config) {
|
|
|
3055
3229
|
const memoryRules = await parseMemoryFiles(memoryDir, config);
|
|
3056
3230
|
rules.push(...memoryRules);
|
|
3057
3231
|
}
|
|
3232
|
+
if (config.commandsDirPath) {
|
|
3233
|
+
const commandsDir = resolvePath(config.commandsDirPath, baseDir);
|
|
3234
|
+
if (await fileExists(commandsDir)) {
|
|
3235
|
+
const commandsRules = await parseCommandsFiles(commandsDir, config);
|
|
3236
|
+
rules.push(...commandsRules);
|
|
3237
|
+
}
|
|
3238
|
+
}
|
|
3058
3239
|
const settingsPath = resolvePath(config.settingsPath, baseDir);
|
|
3059
3240
|
if (await fileExists(settingsPath)) {
|
|
3060
3241
|
const settingsResult = await parseSettingsFile(settingsPath, config.tool);
|
|
@@ -3120,10 +3301,10 @@ async function parseMemoryFiles(memoryDir, config) {
|
|
|
3120
3301
|
const files = await readdir2(memoryDir);
|
|
3121
3302
|
for (const file of files) {
|
|
3122
3303
|
if (file.endsWith(".md")) {
|
|
3123
|
-
const filePath =
|
|
3304
|
+
const filePath = join17(memoryDir, file);
|
|
3124
3305
|
const content = await readFileContent(filePath);
|
|
3125
3306
|
if (content.trim()) {
|
|
3126
|
-
const filename =
|
|
3307
|
+
const filename = basename4(file, ".md");
|
|
3127
3308
|
const frontmatter = {
|
|
3128
3309
|
root: false,
|
|
3129
3310
|
targets: [config.tool],
|
|
@@ -3143,6 +3324,54 @@ async function parseMemoryFiles(memoryDir, config) {
|
|
|
3143
3324
|
}
|
|
3144
3325
|
return rules;
|
|
3145
3326
|
}
|
|
3327
|
+
async function parseCommandsFiles(commandsDir, config) {
|
|
3328
|
+
const rules = [];
|
|
3329
|
+
try {
|
|
3330
|
+
const { readdir: readdir2 } = await import("fs/promises");
|
|
3331
|
+
const files = await readdir2(commandsDir);
|
|
3332
|
+
for (const file of files) {
|
|
3333
|
+
if (file.endsWith(".md")) {
|
|
3334
|
+
const filePath = join17(commandsDir, file);
|
|
3335
|
+
const content = await readFileContent(filePath);
|
|
3336
|
+
if (content.trim()) {
|
|
3337
|
+
const filename = basename4(file, ".md");
|
|
3338
|
+
let frontmatter;
|
|
3339
|
+
let ruleContent;
|
|
3340
|
+
try {
|
|
3341
|
+
const parsed = matter4(content);
|
|
3342
|
+
ruleContent = parsed.content.trim();
|
|
3343
|
+
const parsedFrontmatter = parsed.data;
|
|
3344
|
+
frontmatter = {
|
|
3345
|
+
root: false,
|
|
3346
|
+
targets: [config.tool],
|
|
3347
|
+
description: parsedFrontmatter.description || `Command: ${filename}`,
|
|
3348
|
+
globs: ["**/*"]
|
|
3349
|
+
};
|
|
3350
|
+
} catch {
|
|
3351
|
+
ruleContent = content.trim();
|
|
3352
|
+
frontmatter = {
|
|
3353
|
+
root: false,
|
|
3354
|
+
targets: [config.tool],
|
|
3355
|
+
description: `Command: ${filename}`,
|
|
3356
|
+
globs: ["**/*"]
|
|
3357
|
+
};
|
|
3358
|
+
}
|
|
3359
|
+
if (ruleContent) {
|
|
3360
|
+
rules.push({
|
|
3361
|
+
frontmatter,
|
|
3362
|
+
content: ruleContent,
|
|
3363
|
+
filename,
|
|
3364
|
+
filepath: filePath,
|
|
3365
|
+
type: "command"
|
|
3366
|
+
});
|
|
3367
|
+
}
|
|
3368
|
+
}
|
|
3369
|
+
}
|
|
3370
|
+
}
|
|
3371
|
+
} catch {
|
|
3372
|
+
}
|
|
3373
|
+
return rules;
|
|
3374
|
+
}
|
|
3146
3375
|
async function parseSettingsFile(settingsPath, tool) {
|
|
3147
3376
|
const errors = [];
|
|
3148
3377
|
let ignorePatterns;
|
|
@@ -3190,7 +3419,8 @@ async function parseClaudeConfiguration(baseDir = process.cwd()) {
|
|
|
3190
3419
|
settingsPath: ".claude/settings.json",
|
|
3191
3420
|
mainDescription: "Main Claude Code configuration",
|
|
3192
3421
|
memoryDescription: "Memory file",
|
|
3193
|
-
filenamePrefix: "claude"
|
|
3422
|
+
filenamePrefix: "claude",
|
|
3423
|
+
commandsDirPath: ".claude/commands"
|
|
3194
3424
|
});
|
|
3195
3425
|
}
|
|
3196
3426
|
|
|
@@ -3215,7 +3445,7 @@ async function parseClineConfiguration(baseDir = process.cwd()) {
|
|
|
3215
3445
|
}
|
|
3216
3446
|
|
|
3217
3447
|
// src/parsers/codexcli.ts
|
|
3218
|
-
import { join as
|
|
3448
|
+
import { join as join18 } from "path";
|
|
3219
3449
|
|
|
3220
3450
|
// src/parsers/copilot.ts
|
|
3221
3451
|
async function parseCopilotConfiguration(baseDir = process.cwd()) {
|
|
@@ -3238,10 +3468,10 @@ async function parseCopilotConfiguration(baseDir = process.cwd()) {
|
|
|
3238
3468
|
}
|
|
3239
3469
|
|
|
3240
3470
|
// src/parsers/cursor.ts
|
|
3241
|
-
import { basename as
|
|
3242
|
-
import
|
|
3471
|
+
import { basename as basename5, join as join19 } from "path";
|
|
3472
|
+
import matter5 from "gray-matter";
|
|
3243
3473
|
import { DEFAULT_SCHEMA, FAILSAFE_SCHEMA, load } from "js-yaml";
|
|
3244
|
-
import { z as
|
|
3474
|
+
import { z as z7 } from "zod/mini";
|
|
3245
3475
|
var customMatterOptions = {
|
|
3246
3476
|
engines: {
|
|
3247
3477
|
yaml: {
|
|
@@ -3269,7 +3499,7 @@ var customMatterOptions = {
|
|
|
3269
3499
|
}
|
|
3270
3500
|
};
|
|
3271
3501
|
function convertCursorMdcFrontmatter(cursorFrontmatter, _filename) {
|
|
3272
|
-
const FrontmatterSchema =
|
|
3502
|
+
const FrontmatterSchema = z7.record(z7.string(), z7.unknown());
|
|
3273
3503
|
const parseResult = FrontmatterSchema.safeParse(cursorFrontmatter);
|
|
3274
3504
|
if (!parseResult.success) {
|
|
3275
3505
|
return {
|
|
@@ -3363,11 +3593,11 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
|
|
|
3363
3593
|
const rules = [];
|
|
3364
3594
|
let ignorePatterns;
|
|
3365
3595
|
let mcpServers;
|
|
3366
|
-
const cursorFilePath =
|
|
3596
|
+
const cursorFilePath = join19(baseDir, ".cursorrules");
|
|
3367
3597
|
if (await fileExists(cursorFilePath)) {
|
|
3368
3598
|
try {
|
|
3369
3599
|
const rawContent = await readFileContent(cursorFilePath);
|
|
3370
|
-
const parsed =
|
|
3600
|
+
const parsed = matter5(rawContent, customMatterOptions);
|
|
3371
3601
|
const content = parsed.content.trim();
|
|
3372
3602
|
if (content) {
|
|
3373
3603
|
const frontmatter = convertCursorMdcFrontmatter(parsed.data, "cursorrules");
|
|
@@ -3384,20 +3614,20 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
|
|
|
3384
3614
|
errors.push(`Failed to parse .cursorrules file: ${errorMessage}`);
|
|
3385
3615
|
}
|
|
3386
3616
|
}
|
|
3387
|
-
const cursorRulesDir =
|
|
3617
|
+
const cursorRulesDir = join19(baseDir, ".cursor", "rules");
|
|
3388
3618
|
if (await fileExists(cursorRulesDir)) {
|
|
3389
3619
|
try {
|
|
3390
3620
|
const { readdir: readdir2 } = await import("fs/promises");
|
|
3391
3621
|
const files = await readdir2(cursorRulesDir);
|
|
3392
3622
|
for (const file of files) {
|
|
3393
3623
|
if (file.endsWith(".mdc")) {
|
|
3394
|
-
const filePath =
|
|
3624
|
+
const filePath = join19(cursorRulesDir, file);
|
|
3395
3625
|
try {
|
|
3396
3626
|
const rawContent = await readFileContent(filePath);
|
|
3397
|
-
const parsed =
|
|
3627
|
+
const parsed = matter5(rawContent, customMatterOptions);
|
|
3398
3628
|
const content = parsed.content.trim();
|
|
3399
3629
|
if (content) {
|
|
3400
|
-
const filename =
|
|
3630
|
+
const filename = basename5(file, ".mdc");
|
|
3401
3631
|
const frontmatter = convertCursorMdcFrontmatter(parsed.data, filename);
|
|
3402
3632
|
rules.push({
|
|
3403
3633
|
frontmatter,
|
|
@@ -3420,7 +3650,7 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
|
|
|
3420
3650
|
if (rules.length === 0) {
|
|
3421
3651
|
errors.push("No Cursor configuration files found (.cursorrules or .cursor/rules/*.mdc)");
|
|
3422
3652
|
}
|
|
3423
|
-
const cursorIgnorePath =
|
|
3653
|
+
const cursorIgnorePath = join19(baseDir, ".cursorignore");
|
|
3424
3654
|
if (await fileExists(cursorIgnorePath)) {
|
|
3425
3655
|
try {
|
|
3426
3656
|
const content = await readFileContent(cursorIgnorePath);
|
|
@@ -3433,7 +3663,7 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
|
|
|
3433
3663
|
errors.push(`Failed to parse .cursorignore: ${errorMessage}`);
|
|
3434
3664
|
}
|
|
3435
3665
|
}
|
|
3436
|
-
const cursorMcpPath =
|
|
3666
|
+
const cursorMcpPath = join19(baseDir, ".cursor", "mcp.json");
|
|
3437
3667
|
if (await fileExists(cursorMcpPath)) {
|
|
3438
3668
|
try {
|
|
3439
3669
|
const content = await readFileContent(cursorMcpPath);
|
|
@@ -3477,16 +3707,17 @@ async function parseGeminiConfiguration(baseDir = process.cwd()) {
|
|
|
3477
3707
|
additionalIgnoreFile: {
|
|
3478
3708
|
path: ".aiexclude",
|
|
3479
3709
|
parser: parseAiexclude
|
|
3480
|
-
}
|
|
3710
|
+
},
|
|
3711
|
+
commandsDirPath: ".gemini/commands"
|
|
3481
3712
|
});
|
|
3482
3713
|
}
|
|
3483
3714
|
|
|
3484
3715
|
// src/parsers/junie.ts
|
|
3485
|
-
import { join as
|
|
3716
|
+
import { join as join20 } from "path";
|
|
3486
3717
|
async function parseJunieConfiguration(baseDir = process.cwd()) {
|
|
3487
3718
|
const errors = [];
|
|
3488
3719
|
const rules = [];
|
|
3489
|
-
const guidelinesPath =
|
|
3720
|
+
const guidelinesPath = join20(baseDir, ".junie", "guidelines.md");
|
|
3490
3721
|
if (!await fileExists(guidelinesPath)) {
|
|
3491
3722
|
errors.push(".junie/guidelines.md file not found");
|
|
3492
3723
|
return { rules, errors };
|
|
@@ -3539,8 +3770,8 @@ async function parseRooConfiguration(baseDir = process.cwd()) {
|
|
|
3539
3770
|
|
|
3540
3771
|
// src/parsers/windsurf.ts
|
|
3541
3772
|
import { readFile as readFile2 } from "fs/promises";
|
|
3542
|
-
import { join as
|
|
3543
|
-
import
|
|
3773
|
+
import { join as join21 } from "path";
|
|
3774
|
+
import matter6 from "gray-matter";
|
|
3544
3775
|
|
|
3545
3776
|
// src/core/importer.ts
|
|
3546
3777
|
async function importConfiguration(options) {
|
|
@@ -3626,7 +3857,7 @@ async function importConfiguration(options) {
|
|
|
3626
3857
|
if (rules.length === 0 && !ignorePatterns && !mcpServers) {
|
|
3627
3858
|
return { success: false, rulesCreated: 0, errors };
|
|
3628
3859
|
}
|
|
3629
|
-
const rulesDirPath =
|
|
3860
|
+
const rulesDirPath = join22(baseDir, rulesDir);
|
|
3630
3861
|
try {
|
|
3631
3862
|
const { mkdir: mkdir3 } = await import("fs/promises");
|
|
3632
3863
|
await mkdir3(rulesDirPath, { recursive: true });
|
|
@@ -3639,8 +3870,13 @@ async function importConfiguration(options) {
|
|
|
3639
3870
|
for (const rule of rules) {
|
|
3640
3871
|
try {
|
|
3641
3872
|
const baseFilename = rule.filename;
|
|
3642
|
-
|
|
3643
|
-
|
|
3873
|
+
let targetDir = rulesDirPath;
|
|
3874
|
+
if (rule.type === "command") {
|
|
3875
|
+
targetDir = join22(rulesDirPath, "commands");
|
|
3876
|
+
const { mkdir: mkdir3 } = await import("fs/promises");
|
|
3877
|
+
await mkdir3(targetDir, { recursive: true });
|
|
3878
|
+
}
|
|
3879
|
+
const filePath = join22(targetDir, `${baseFilename}.md`);
|
|
3644
3880
|
const content = generateRuleFileContent(rule);
|
|
3645
3881
|
await writeFileContent(filePath, content);
|
|
3646
3882
|
rulesCreated++;
|
|
@@ -3655,7 +3891,7 @@ async function importConfiguration(options) {
|
|
|
3655
3891
|
let ignoreFileCreated = false;
|
|
3656
3892
|
if (ignorePatterns && ignorePatterns.length > 0) {
|
|
3657
3893
|
try {
|
|
3658
|
-
const rulesyncignorePath =
|
|
3894
|
+
const rulesyncignorePath = join22(baseDir, ".rulesyncignore");
|
|
3659
3895
|
const ignoreContent = `${ignorePatterns.join("\n")}
|
|
3660
3896
|
`;
|
|
3661
3897
|
await writeFileContent(rulesyncignorePath, ignoreContent);
|
|
@@ -3671,7 +3907,7 @@ async function importConfiguration(options) {
|
|
|
3671
3907
|
let mcpFileCreated = false;
|
|
3672
3908
|
if (mcpServers && Object.keys(mcpServers).length > 0) {
|
|
3673
3909
|
try {
|
|
3674
|
-
const mcpPath =
|
|
3910
|
+
const mcpPath = join22(baseDir, rulesDir, ".mcp.json");
|
|
3675
3911
|
const mcpContent = `${JSON.stringify({ mcpServers }, null, 2)}
|
|
3676
3912
|
`;
|
|
3677
3913
|
await writeFileContent(mcpPath, mcpContent);
|
|
@@ -3693,17 +3929,16 @@ async function importConfiguration(options) {
|
|
|
3693
3929
|
};
|
|
3694
3930
|
}
|
|
3695
3931
|
function generateRuleFileContent(rule) {
|
|
3696
|
-
|
|
3697
|
-
|
|
3698
|
-
|
|
3699
|
-
|
|
3700
|
-
|
|
3701
|
-
|
|
3702
|
-
|
|
3703
|
-
filename = `${baseFilename}-${counter}`;
|
|
3704
|
-
counter++;
|
|
3932
|
+
if (rule.type === "command") {
|
|
3933
|
+
const simplifiedFrontmatter = {
|
|
3934
|
+
description: rule.frontmatter.description,
|
|
3935
|
+
targets: rule.frontmatter.targets
|
|
3936
|
+
};
|
|
3937
|
+
const frontmatter2 = matter7.stringify("", simplifiedFrontmatter);
|
|
3938
|
+
return frontmatter2 + rule.content;
|
|
3705
3939
|
}
|
|
3706
|
-
|
|
3940
|
+
const frontmatter = matter7.stringify("", rule.frontmatter);
|
|
3941
|
+
return frontmatter + rule.content;
|
|
3707
3942
|
}
|
|
3708
3943
|
|
|
3709
3944
|
// src/cli/commands/import.ts
|
|
@@ -3766,7 +4001,7 @@ async function importCommand(options = {}) {
|
|
|
3766
4001
|
}
|
|
3767
4002
|
|
|
3768
4003
|
// src/cli/commands/init.ts
|
|
3769
|
-
import { join as
|
|
4004
|
+
import { join as join23 } from "path";
|
|
3770
4005
|
async function initCommand() {
|
|
3771
4006
|
const aiRulesDir = ".rulesync";
|
|
3772
4007
|
console.log("Initializing rulesync...");
|
|
@@ -3813,7 +4048,7 @@ globs: ["**/*"]
|
|
|
3813
4048
|
- Follow single responsibility principle
|
|
3814
4049
|
`
|
|
3815
4050
|
};
|
|
3816
|
-
const filepath =
|
|
4051
|
+
const filepath = join23(aiRulesDir, sampleFile.filename);
|
|
3817
4052
|
if (!await fileExists(filepath)) {
|
|
3818
4053
|
await writeFileContent(filepath, sampleFile.content);
|
|
3819
4054
|
console.log(`Created ${filepath}`);
|
|
@@ -3957,7 +4192,7 @@ async function watchCommand() {
|
|
|
3957
4192
|
|
|
3958
4193
|
// src/cli/index.ts
|
|
3959
4194
|
var program = new Command();
|
|
3960
|
-
program.name("rulesync").description("Unified AI rules management CLI tool").version("0.
|
|
4195
|
+
program.name("rulesync").description("Unified AI rules management CLI tool").version("0.58.0");
|
|
3961
4196
|
program.command("init").description("Initialize rulesync in current directory").action(initCommand);
|
|
3962
4197
|
program.command("add <filename>").description("Add a new rule file").action(addCommand);
|
|
3963
4198
|
program.command("gitignore").description("Add generated files to .gitignore").action(gitignoreCommand);
|