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/README.ja.md +171 -18
- package/README.md +67 -727
- package/dist/{chunk-7E4U4YAB.js → chunk-3YQ42A7G.js} +1 -1
- package/dist/{chunk-WAX2UANS.js → chunk-5SRMJNDW.js} +1 -1
- package/dist/{chunk-7UBF4OLN.js → chunk-DJWXF2WO.js} +1 -1
- package/dist/{chunk-VKNCBVZF.js → chunk-EG7LNNMW.js} +1 -1
- package/dist/{chunk-OA473EXZ.js → chunk-LIV53UU5.js} +1 -1
- package/dist/{chunk-J3TBR5EP.js → chunk-OCK47GE7.js} +1 -1
- package/dist/{chunk-VNT6AHHO.js → chunk-S3GB3VQK.js} +1 -1
- package/dist/{chunk-W2WU253H.js → chunk-VYO76WDU.js} +1 -1
- package/dist/{claudecode-VVI2PTKI.js → claudecode-CRSXMPS5.js} +2 -2
- package/dist/{cline-BJLFSLEB.js → cline-O67TEUFW.js} +2 -2
- package/dist/{codexcli-LKWQB3V3.js → codexcli-S7VDKBI2.js} +2 -2
- package/dist/{cursor-2BVUO64T.js → cursor-EXX2Q5MB.js} +2 -2
- package/dist/{geminicli-5YFMKRFL.js → geminicli-K3FKXDKP.js} +2 -2
- package/dist/index.cjs +871 -340
- package/dist/index.js +522 -373
- package/dist/{junie-5TDJPUXX.js → junie-4SNWC452.js} +2 -2
- package/dist/{windsurf-PXDRIQ76.js → windsurf-NVCRKFHF.js} +2 -2
- package/package.json +19 -19
package/dist/index.js
CHANGED
|
@@ -1,38 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
import
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
import
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
import
|
|
12
|
-
|
|
13
|
-
|
|
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/
|
|
110
|
+
// src/types/commands.ts
|
|
133
111
|
import { z as z2 } from "zod/mini";
|
|
134
|
-
var
|
|
135
|
-
|
|
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
|
|
116
|
+
// src/types/config.ts
|
|
142
117
|
import { z as z3 } from "zod/mini";
|
|
143
|
-
var
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
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 =
|
|
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:
|
|
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 =
|
|
176
|
-
aiRulesDir:
|
|
177
|
-
outputPaths:
|
|
178
|
-
watchEnabled:
|
|
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:
|
|
181
|
-
exclude:
|
|
182
|
-
verbose:
|
|
183
|
-
delete:
|
|
184
|
-
baseDir:
|
|
185
|
-
configPath:
|
|
186
|
-
watch:
|
|
187
|
-
|
|
188
|
-
enabled:
|
|
189
|
-
interval:
|
|
190
|
-
ignore:
|
|
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
|
|
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:
|
|
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 =
|
|
218
|
-
targets:
|
|
203
|
+
var RulesyncMcpServerSchema = z5.extend(McpServerBaseSchema, {
|
|
204
|
+
targets: z5.optional(RulesyncTargetsSchema)
|
|
219
205
|
});
|
|
220
|
-
var McpConfigSchema =
|
|
221
|
-
mcpServers:
|
|
206
|
+
var McpConfigSchema = z5.object({
|
|
207
|
+
mcpServers: z5.record(z5.string(), McpServerBaseSchema)
|
|
222
208
|
});
|
|
223
|
-
var RulesyncMcpConfigSchema =
|
|
224
|
-
mcpServers:
|
|
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
|
|
229
|
-
var RuleFrontmatterSchema =
|
|
230
|
-
root:
|
|
231
|
-
targets:
|
|
232
|
-
description:
|
|
233
|
-
globs:
|
|
234
|
-
cursorRuleType:
|
|
235
|
-
windsurfActivationMode:
|
|
236
|
-
windsurfOutputFormat:
|
|
237
|
-
tags:
|
|
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 =
|
|
225
|
+
var ParsedRuleSchema = z6.object({
|
|
240
226
|
frontmatter: RuleFrontmatterSchema,
|
|
241
|
-
content:
|
|
242
|
-
filename:
|
|
243
|
-
filepath:
|
|
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 =
|
|
232
|
+
var GeneratedOutputSchema = z6.object({
|
|
246
233
|
tool: ToolTargetSchema,
|
|
247
|
-
filepath:
|
|
248
|
-
content:
|
|
234
|
+
filepath: z6.string(),
|
|
235
|
+
content: z6.string()
|
|
249
236
|
});
|
|
250
|
-
var GenerateOptionsSchema =
|
|
251
|
-
targetTools:
|
|
252
|
-
outputDir:
|
|
253
|
-
watch:
|
|
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
|
|
743
|
+
import { join as join14 } from "path";
|
|
779
744
|
|
|
780
|
-
// src/
|
|
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 =
|
|
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
|
|
1607
|
+
import { join as join9 } from "path";
|
|
1488
1608
|
|
|
1489
1609
|
// src/generators/rules/shared-helpers.ts
|
|
1490
|
-
import { join as
|
|
1610
|
+
import { join as join8 } from "path";
|
|
1491
1611
|
|
|
1492
1612
|
// src/utils/ignore.ts
|
|
1493
|
-
import { join as
|
|
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 =
|
|
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:
|
|
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) :
|
|
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
|
-
|
|
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
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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(
|
|
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
|
|
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
|
|
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
|
|
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
|
|
2059
|
+
return join11(outputDir, ".windsurf-rules");
|
|
1938
2060
|
} else {
|
|
1939
|
-
const rulesDir =
|
|
1940
|
-
return
|
|
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
|
|
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
|
|
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 =
|
|
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 =
|
|
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
|
|
2409
|
-
const
|
|
2410
|
-
const
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
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
|
|
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(
|
|
2604
|
-
deleteTasks.push(removeDirectory(
|
|
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(
|
|
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
|
-
|
|
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 (
|
|
2761
|
+
if (commandResults.length === 0) {
|
|
2675
2762
|
if (config.verbose) {
|
|
2676
|
-
console.log(`No
|
|
2763
|
+
console.log(`No commands found for ${baseDir}`);
|
|
2677
2764
|
}
|
|
2678
2765
|
continue;
|
|
2679
2766
|
}
|
|
2680
|
-
for (const result of
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
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 (${
|
|
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
|
|
2792
|
+
import { join as join15 } from "path";
|
|
2707
2793
|
var gitignoreCommand = async () => {
|
|
2708
|
-
const gitignorePath =
|
|
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
|
|
2773
|
-
import
|
|
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
|
|
2777
|
-
import
|
|
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 =
|
|
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 =
|
|
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 =
|
|
2950
|
+
const filePath = join16(rulesDir, file);
|
|
2863
2951
|
try {
|
|
2864
2952
|
const rawContent = await readFileContent(filePath);
|
|
2865
|
-
const parsed =
|
|
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 =
|
|
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
|
|
2930
|
-
import
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
3218
|
+
const filePath = join17(memoryDir, file);
|
|
3124
3219
|
const content = await readFileContent(filePath);
|
|
3125
3220
|
if (content.trim()) {
|
|
3126
|
-
const filename =
|
|
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
|
|
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
|
|
3242
|
-
import
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
3538
|
+
const filePath = join19(cursorRulesDir, file);
|
|
3395
3539
|
try {
|
|
3396
3540
|
const rawContent = await readFileContent(filePath);
|
|
3397
|
-
const parsed =
|
|
3541
|
+
const parsed = matter5(rawContent, customMatterOptions);
|
|
3398
3542
|
const content = parsed.content.trim();
|
|
3399
3543
|
if (content) {
|
|
3400
|
-
const filename =
|
|
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 =
|
|
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 =
|
|
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
|
|
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 =
|
|
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
|
|
3543
|
-
import
|
|
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 =
|
|
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
|
-
|
|
3643
|
-
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
3697
|
-
|
|
3698
|
-
|
|
3699
|
-
|
|
3700
|
-
|
|
3701
|
-
|
|
3702
|
-
|
|
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
|
-
|
|
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
|
|
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 =
|
|
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.
|
|
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);
|