rulesync 0.45.0 → 0.47.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 +62 -2
- package/README.md +62 -2
- package/dist/{chunk-FAZT3ILF.js → chunk-7UVBAWYG.js} +1 -1
- package/dist/{chunk-I5XVU7C6.js → chunk-7ZIUEZZQ.js} +3 -2
- package/dist/{chunk-BD37M3ZH.js → chunk-BY6RI77W.js} +1 -1
- package/dist/chunk-D365OP7N.js +86 -0
- package/dist/{chunk-DCSO5MY7.js → chunk-JWN6GRG6.js} +1 -1
- package/dist/{chunk-PJUNIIF4.js → chunk-L2JTXZZB.js} +1 -1
- package/dist/{chunk-22GWBUIP.js → chunk-OTCCHS7Q.js} +1 -1
- package/dist/{chunk-ZORSPGDD.js → chunk-P6KQZULZ.js} +1 -1
- package/dist/{claudecode-KSK2BEI7.js → claudecode-Y3GIXDUN.js} +2 -2
- package/dist/{cline-T5YVGYBF.js → cline-NS3OPXM2.js} +2 -2
- package/dist/{copilot-UDCWNUAH.js → copilot-QN2SC7Y2.js} +2 -2
- package/dist/{cursor-KPV6OVST.js → cursor-DV2IS7JF.js} +2 -2
- package/dist/{geminicli-2DC5F34J.js → geminicli-MRYTLT2T.js} +2 -2
- package/dist/index.cjs +368 -170
- package/dist/index.js +288 -140
- package/dist/kiro-S5TSM7VW.js +9 -0
- package/dist/{roo-DRA2SU4L.js → roo-NWLD3YYN.js} +2 -2
- package/package.json +4 -3
package/dist/index.js
CHANGED
|
@@ -1,27 +1,30 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
generateClaudeMcp
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-OTCCHS7Q.js";
|
|
5
5
|
import {
|
|
6
6
|
generateClineMcp
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-BY6RI77W.js";
|
|
8
8
|
import {
|
|
9
9
|
generateCopilotMcp
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-P6KQZULZ.js";
|
|
11
11
|
import {
|
|
12
12
|
generateCursorMcp
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-7UVBAWYG.js";
|
|
14
14
|
import {
|
|
15
15
|
generateGeminiCliMcp
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-JWN6GRG6.js";
|
|
17
|
+
import {
|
|
18
|
+
generateKiroMcp
|
|
19
|
+
} from "./chunk-D365OP7N.js";
|
|
17
20
|
import {
|
|
18
21
|
generateRooMcp
|
|
19
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-L2JTXZZB.js";
|
|
20
23
|
import {
|
|
21
24
|
RulesyncTargetsSchema,
|
|
22
25
|
ToolTargetSchema,
|
|
23
26
|
ToolTargetsSchema
|
|
24
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-7ZIUEZZQ.js";
|
|
25
28
|
|
|
26
29
|
// src/cli/index.ts
|
|
27
30
|
import { Command } from "commander";
|
|
@@ -40,10 +43,11 @@ function getDefaultConfig() {
|
|
|
40
43
|
cline: ".clinerules",
|
|
41
44
|
claudecode: ".",
|
|
42
45
|
roo: ".roo/rules",
|
|
43
|
-
geminicli: ".gemini/memories"
|
|
46
|
+
geminicli: ".gemini/memories",
|
|
47
|
+
kiro: ".kiro/steering"
|
|
44
48
|
},
|
|
45
49
|
watchEnabled: false,
|
|
46
|
-
defaultTargets: ["copilot", "cursor", "cline", "claudecode", "roo", "geminicli"]
|
|
50
|
+
defaultTargets: ["copilot", "cursor", "cline", "claudecode", "roo", "geminicli", "kiro"]
|
|
47
51
|
};
|
|
48
52
|
}
|
|
49
53
|
function resolveTargets(targets, config) {
|
|
@@ -90,11 +94,126 @@ async function addCommand(filename) {
|
|
|
90
94
|
}
|
|
91
95
|
}
|
|
92
96
|
|
|
97
|
+
// src/generators/ignore/kiro.ts
|
|
98
|
+
import { join as join2 } from "path";
|
|
99
|
+
async function generateKiroIgnoreFiles(rules, config, baseDir) {
|
|
100
|
+
const outputs = [];
|
|
101
|
+
const aiignoreContent = generateAiignoreContent(rules);
|
|
102
|
+
const outputPath = baseDir || process.cwd();
|
|
103
|
+
const filepath = join2(outputPath, ".aiignore");
|
|
104
|
+
outputs.push({
|
|
105
|
+
tool: "kiro",
|
|
106
|
+
filepath,
|
|
107
|
+
content: aiignoreContent
|
|
108
|
+
});
|
|
109
|
+
return outputs;
|
|
110
|
+
}
|
|
111
|
+
function generateAiignoreContent(rules) {
|
|
112
|
+
const lines = [
|
|
113
|
+
"# Generated by rulesync - Kiro AI-specific exclusions",
|
|
114
|
+
"# This file excludes files that can be in Git but shouldn't be read by the AI",
|
|
115
|
+
""
|
|
116
|
+
];
|
|
117
|
+
lines.push(
|
|
118
|
+
"# Data files AI shouldn't process",
|
|
119
|
+
"*.csv",
|
|
120
|
+
"*.tsv",
|
|
121
|
+
"*.sqlite",
|
|
122
|
+
"*.db",
|
|
123
|
+
"",
|
|
124
|
+
"# Large binary files",
|
|
125
|
+
"*.zip",
|
|
126
|
+
"*.tar.gz",
|
|
127
|
+
"*.rar",
|
|
128
|
+
"",
|
|
129
|
+
"# Sensitive documentation",
|
|
130
|
+
"internal-docs/",
|
|
131
|
+
"confidential/",
|
|
132
|
+
"",
|
|
133
|
+
"# Test data that might confuse AI",
|
|
134
|
+
"test/fixtures/large-*.json",
|
|
135
|
+
"benchmark-results/",
|
|
136
|
+
"",
|
|
137
|
+
"# Reinforce critical exclusions from .gitignore",
|
|
138
|
+
"*.pem",
|
|
139
|
+
"*.key",
|
|
140
|
+
".env*",
|
|
141
|
+
""
|
|
142
|
+
);
|
|
143
|
+
const rulePatterns = extractIgnorePatternsFromRules(rules);
|
|
144
|
+
if (rulePatterns.length > 0) {
|
|
145
|
+
lines.push("# Project-specific exclusions from rulesync rules");
|
|
146
|
+
lines.push(...rulePatterns);
|
|
147
|
+
lines.push("");
|
|
148
|
+
}
|
|
149
|
+
return lines.join("\n");
|
|
150
|
+
}
|
|
151
|
+
function extractIgnorePatternsFromRules(rules) {
|
|
152
|
+
const patterns = [];
|
|
153
|
+
for (const rule of rules) {
|
|
154
|
+
if (rule.frontmatter.globs && rule.frontmatter.globs.length > 0) {
|
|
155
|
+
for (const glob of rule.frontmatter.globs) {
|
|
156
|
+
if (shouldExcludeFromAI(glob)) {
|
|
157
|
+
patterns.push(`# Exclude: ${rule.frontmatter.description}`);
|
|
158
|
+
patterns.push(glob);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
const contentPatterns = extractIgnorePatternsFromContent(rule.content);
|
|
163
|
+
patterns.push(...contentPatterns);
|
|
164
|
+
}
|
|
165
|
+
return patterns;
|
|
166
|
+
}
|
|
167
|
+
function shouldExcludeFromAI(glob) {
|
|
168
|
+
const excludePatterns = [
|
|
169
|
+
// Test and fixture files that might be large or confusing
|
|
170
|
+
"**/test/fixtures/**",
|
|
171
|
+
"**/tests/fixtures/**",
|
|
172
|
+
"**/*.fixture.*",
|
|
173
|
+
// Build and generated files
|
|
174
|
+
"**/dist/**",
|
|
175
|
+
"**/build/**",
|
|
176
|
+
"**/coverage/**",
|
|
177
|
+
// Configuration that might contain sensitive data
|
|
178
|
+
"**/config/production/**",
|
|
179
|
+
"**/config/prod/**",
|
|
180
|
+
"**/*.prod.*",
|
|
181
|
+
// Documentation that might be sensitive
|
|
182
|
+
"**/internal/**",
|
|
183
|
+
"**/private/**",
|
|
184
|
+
"**/confidential/**"
|
|
185
|
+
];
|
|
186
|
+
return excludePatterns.some((pattern) => {
|
|
187
|
+
const regex = new RegExp(pattern.replace(/\*\*/g, ".*").replace(/\*/g, "[^/]*"));
|
|
188
|
+
return regex.test(glob);
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
function extractIgnorePatternsFromContent(content) {
|
|
192
|
+
const patterns = [];
|
|
193
|
+
const lines = content.split("\n");
|
|
194
|
+
for (const line of lines) {
|
|
195
|
+
const trimmed = line.trim();
|
|
196
|
+
if (trimmed.startsWith("# IGNORE:") || trimmed.startsWith("# aiignore:")) {
|
|
197
|
+
const pattern = trimmed.replace(/^# (IGNORE|aiignore):\s*/, "").trim();
|
|
198
|
+
if (pattern) {
|
|
199
|
+
patterns.push(pattern);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
if (trimmed.includes("exclude") || trimmed.includes("ignore")) {
|
|
203
|
+
const matches = trimmed.match(/['"`]([^'"`]+\.(log|tmp|cache|temp))['"`]/g);
|
|
204
|
+
if (matches) {
|
|
205
|
+
patterns.push(...matches.map((m) => m.replace(/['"`]/g, "")));
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
return patterns;
|
|
210
|
+
}
|
|
211
|
+
|
|
93
212
|
// src/generators/rules/claudecode.ts
|
|
94
|
-
import { join as
|
|
213
|
+
import { join as join5 } from "path";
|
|
95
214
|
|
|
96
215
|
// src/types/claudecode.ts
|
|
97
|
-
import { z } from "zod/
|
|
216
|
+
import { z } from "zod/mini";
|
|
98
217
|
var ClaudeSettingsSchema = z.looseObject({
|
|
99
218
|
permissions: z._default(
|
|
100
219
|
z.looseObject({
|
|
@@ -105,12 +224,8 @@ var ClaudeSettingsSchema = z.looseObject({
|
|
|
105
224
|
});
|
|
106
225
|
|
|
107
226
|
// src/utils/file.ts
|
|
108
|
-
import { readdir, rm } from "fs/promises";
|
|
109
|
-
import { join as join3 } from "path";
|
|
110
|
-
|
|
111
|
-
// src/utils/file-ops.ts
|
|
112
|
-
import { mkdir as mkdir2, readFile, stat, writeFile as writeFile2 } from "fs/promises";
|
|
113
|
-
import { dirname } from "path";
|
|
227
|
+
import { mkdir as mkdir2, readdir, readFile, rm, stat, writeFile as writeFile2 } from "fs/promises";
|
|
228
|
+
import { dirname, join as join3 } from "path";
|
|
114
229
|
async function ensureDir(dirPath) {
|
|
115
230
|
try {
|
|
116
231
|
await stat(dirPath);
|
|
@@ -133,16 +248,57 @@ async function fileExists(filepath) {
|
|
|
133
248
|
return false;
|
|
134
249
|
}
|
|
135
250
|
}
|
|
251
|
+
async function findFiles(dir, extension = ".md") {
|
|
252
|
+
try {
|
|
253
|
+
const files = await readdir(dir);
|
|
254
|
+
return files.filter((file) => file.endsWith(extension)).map((file) => join3(dir, file));
|
|
255
|
+
} catch {
|
|
256
|
+
return [];
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
async function removeDirectory(dirPath) {
|
|
260
|
+
const dangerousPaths = [".", "/", "~", "src", "node_modules"];
|
|
261
|
+
if (dangerousPaths.includes(dirPath) || dirPath === "") {
|
|
262
|
+
console.warn(`Skipping deletion of dangerous path: ${dirPath}`);
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
try {
|
|
266
|
+
if (await fileExists(dirPath)) {
|
|
267
|
+
await rm(dirPath, { recursive: true, force: true });
|
|
268
|
+
}
|
|
269
|
+
} catch (error) {
|
|
270
|
+
console.warn(`Failed to remove directory ${dirPath}:`, error);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
async function removeFile(filepath) {
|
|
274
|
+
try {
|
|
275
|
+
if (await fileExists(filepath)) {
|
|
276
|
+
await rm(filepath);
|
|
277
|
+
}
|
|
278
|
+
} catch (error) {
|
|
279
|
+
console.warn(`Failed to remove file ${filepath}:`, error);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
async function removeClaudeGeneratedFiles() {
|
|
283
|
+
const filesToRemove = ["CLAUDE.md", ".claude/memories"];
|
|
284
|
+
for (const fileOrDir of filesToRemove) {
|
|
285
|
+
if (fileOrDir.endsWith("/memories")) {
|
|
286
|
+
await removeDirectory(fileOrDir);
|
|
287
|
+
} else {
|
|
288
|
+
await removeFile(fileOrDir);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
136
292
|
|
|
137
293
|
// src/utils/ignore.ts
|
|
138
|
-
import { join as
|
|
294
|
+
import { join as join4 } from "path";
|
|
139
295
|
import micromatch from "micromatch";
|
|
140
296
|
var cachedIgnorePatterns = null;
|
|
141
297
|
async function loadIgnorePatterns(baseDir = process.cwd()) {
|
|
142
298
|
if (cachedIgnorePatterns) {
|
|
143
299
|
return cachedIgnorePatterns;
|
|
144
300
|
}
|
|
145
|
-
const ignorePath =
|
|
301
|
+
const ignorePath = join4(baseDir, ".rulesyncignore");
|
|
146
302
|
if (!await fileExists(ignorePath)) {
|
|
147
303
|
cachedIgnorePatterns = { patterns: [] };
|
|
148
304
|
return cachedIgnorePatterns;
|
|
@@ -185,76 +341,29 @@ function filterIgnoredFiles(files, ignorePatterns) {
|
|
|
185
341
|
return files.filter((file) => !isFileIgnored(file, ignorePatterns));
|
|
186
342
|
}
|
|
187
343
|
|
|
188
|
-
// src/utils/file.ts
|
|
189
|
-
async function findFiles(dir, extension = ".md", ignorePatterns) {
|
|
190
|
-
try {
|
|
191
|
-
const files = await readdir(dir);
|
|
192
|
-
const filtered = files.filter((file) => file.endsWith(extension)).map((file) => join3(dir, file));
|
|
193
|
-
if (ignorePatterns && ignorePatterns.length > 0) {
|
|
194
|
-
return filterIgnoredFiles(filtered, ignorePatterns);
|
|
195
|
-
}
|
|
196
|
-
return filtered;
|
|
197
|
-
} catch {
|
|
198
|
-
return [];
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
async function removeDirectory(dirPath) {
|
|
202
|
-
const dangerousPaths = [".", "/", "~", "src", "node_modules"];
|
|
203
|
-
if (dangerousPaths.includes(dirPath) || dirPath === "") {
|
|
204
|
-
console.warn(`Skipping deletion of dangerous path: ${dirPath}`);
|
|
205
|
-
return;
|
|
206
|
-
}
|
|
207
|
-
try {
|
|
208
|
-
if (await fileExists(dirPath)) {
|
|
209
|
-
await rm(dirPath, { recursive: true, force: true });
|
|
210
|
-
}
|
|
211
|
-
} catch (error) {
|
|
212
|
-
console.warn(`Failed to remove directory ${dirPath}:`, error);
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
async function removeFile(filepath) {
|
|
216
|
-
try {
|
|
217
|
-
if (await fileExists(filepath)) {
|
|
218
|
-
await rm(filepath);
|
|
219
|
-
}
|
|
220
|
-
} catch (error) {
|
|
221
|
-
console.warn(`Failed to remove file ${filepath}:`, error);
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
async function removeClaudeGeneratedFiles() {
|
|
225
|
-
const filesToRemove = ["CLAUDE.md", ".claude/memories"];
|
|
226
|
-
for (const fileOrDir of filesToRemove) {
|
|
227
|
-
if (fileOrDir.endsWith("/memories")) {
|
|
228
|
-
await removeDirectory(fileOrDir);
|
|
229
|
-
} else {
|
|
230
|
-
await removeFile(fileOrDir);
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
|
|
235
344
|
// src/generators/rules/claudecode.ts
|
|
236
345
|
async function generateClaudecodeConfig(rules, config, baseDir) {
|
|
237
346
|
const outputs = [];
|
|
238
347
|
const rootRules = rules.filter((r) => r.frontmatter.root === true);
|
|
239
348
|
const detailRules = rules.filter((r) => r.frontmatter.root === false);
|
|
240
349
|
const claudeMdContent = generateClaudeMarkdown(rootRules, detailRules);
|
|
241
|
-
const claudeOutputDir = baseDir ?
|
|
350
|
+
const claudeOutputDir = baseDir ? join5(baseDir, config.outputPaths.claudecode) : config.outputPaths.claudecode;
|
|
242
351
|
outputs.push({
|
|
243
352
|
tool: "claudecode",
|
|
244
|
-
filepath:
|
|
353
|
+
filepath: join5(claudeOutputDir, "CLAUDE.md"),
|
|
245
354
|
content: claudeMdContent
|
|
246
355
|
});
|
|
247
356
|
for (const rule of detailRules) {
|
|
248
357
|
const memoryContent = generateMemoryFile(rule);
|
|
249
358
|
outputs.push({
|
|
250
359
|
tool: "claudecode",
|
|
251
|
-
filepath:
|
|
360
|
+
filepath: join5(claudeOutputDir, ".claude", "memories", `${rule.filename}.md`),
|
|
252
361
|
content: memoryContent
|
|
253
362
|
});
|
|
254
363
|
}
|
|
255
364
|
const ignorePatterns = await loadIgnorePatterns(baseDir);
|
|
256
365
|
if (ignorePatterns.patterns.length > 0) {
|
|
257
|
-
const settingsPath = baseDir ?
|
|
366
|
+
const settingsPath = baseDir ? join5(baseDir, ".claude", "settings.json") : join5(".claude", "settings.json");
|
|
258
367
|
await updateClaudeSettings(settingsPath, ignorePatterns.patterns);
|
|
259
368
|
}
|
|
260
369
|
return outputs;
|
|
@@ -318,13 +427,13 @@ async function updateClaudeSettings(settingsPath, ignorePatterns) {
|
|
|
318
427
|
}
|
|
319
428
|
|
|
320
429
|
// src/generators/rules/cline.ts
|
|
321
|
-
import { join as
|
|
430
|
+
import { join as join6 } from "path";
|
|
322
431
|
async function generateClineConfig(rules, config, baseDir) {
|
|
323
432
|
const outputs = [];
|
|
324
433
|
for (const rule of rules) {
|
|
325
434
|
const content = generateClineMarkdown(rule);
|
|
326
|
-
const outputDir = baseDir ?
|
|
327
|
-
const filepath =
|
|
435
|
+
const outputDir = baseDir ? join6(baseDir, config.outputPaths.cline) : config.outputPaths.cline;
|
|
436
|
+
const filepath = join6(outputDir, `${rule.filename}.md`);
|
|
328
437
|
outputs.push({
|
|
329
438
|
tool: "cline",
|
|
330
439
|
filepath,
|
|
@@ -333,7 +442,7 @@ async function generateClineConfig(rules, config, baseDir) {
|
|
|
333
442
|
}
|
|
334
443
|
const ignorePatterns = await loadIgnorePatterns(baseDir);
|
|
335
444
|
if (ignorePatterns.patterns.length > 0) {
|
|
336
|
-
const clineIgnorePath = baseDir ?
|
|
445
|
+
const clineIgnorePath = baseDir ? join6(baseDir, ".clineignore") : ".clineignore";
|
|
337
446
|
const clineIgnoreContent = generateClineIgnore(ignorePatterns.patterns);
|
|
338
447
|
outputs.push({
|
|
339
448
|
tool: "cline",
|
|
@@ -357,14 +466,14 @@ function generateClineIgnore(patterns) {
|
|
|
357
466
|
}
|
|
358
467
|
|
|
359
468
|
// src/generators/rules/copilot.ts
|
|
360
|
-
import { join as
|
|
469
|
+
import { join as join7 } from "path";
|
|
361
470
|
async function generateCopilotConfig(rules, config, baseDir) {
|
|
362
471
|
const outputs = [];
|
|
363
472
|
for (const rule of rules) {
|
|
364
473
|
const content = generateCopilotMarkdown(rule);
|
|
365
474
|
const baseFilename = rule.filename.replace(/\.md$/, "");
|
|
366
|
-
const outputDir = baseDir ?
|
|
367
|
-
const filepath =
|
|
475
|
+
const outputDir = baseDir ? join7(baseDir, config.outputPaths.copilot) : config.outputPaths.copilot;
|
|
476
|
+
const filepath = join7(outputDir, `${baseFilename}.instructions.md`);
|
|
368
477
|
outputs.push({
|
|
369
478
|
tool: "copilot",
|
|
370
479
|
filepath,
|
|
@@ -373,7 +482,7 @@ async function generateCopilotConfig(rules, config, baseDir) {
|
|
|
373
482
|
}
|
|
374
483
|
const ignorePatterns = await loadIgnorePatterns(baseDir);
|
|
375
484
|
if (ignorePatterns.patterns.length > 0) {
|
|
376
|
-
const copilotIgnorePath = baseDir ?
|
|
485
|
+
const copilotIgnorePath = baseDir ? join7(baseDir, ".copilotignore") : ".copilotignore";
|
|
377
486
|
const copilotIgnoreContent = generateCopilotIgnore(ignorePatterns.patterns);
|
|
378
487
|
outputs.push({
|
|
379
488
|
tool: "copilot",
|
|
@@ -409,13 +518,13 @@ function generateCopilotIgnore(patterns) {
|
|
|
409
518
|
}
|
|
410
519
|
|
|
411
520
|
// src/generators/rules/cursor.ts
|
|
412
|
-
import { join as
|
|
521
|
+
import { join as join8 } from "path";
|
|
413
522
|
async function generateCursorConfig(rules, config, baseDir) {
|
|
414
523
|
const outputs = [];
|
|
415
524
|
for (const rule of rules) {
|
|
416
525
|
const content = generateCursorMarkdown(rule);
|
|
417
|
-
const outputDir = baseDir ?
|
|
418
|
-
const filepath =
|
|
526
|
+
const outputDir = baseDir ? join8(baseDir, config.outputPaths.cursor) : config.outputPaths.cursor;
|
|
527
|
+
const filepath = join8(outputDir, `${rule.filename}.mdc`);
|
|
419
528
|
outputs.push({
|
|
420
529
|
tool: "cursor",
|
|
421
530
|
filepath,
|
|
@@ -424,7 +533,7 @@ async function generateCursorConfig(rules, config, baseDir) {
|
|
|
424
533
|
}
|
|
425
534
|
const ignorePatterns = await loadIgnorePatterns(baseDir);
|
|
426
535
|
if (ignorePatterns.patterns.length > 0) {
|
|
427
|
-
const cursorIgnorePath = baseDir ?
|
|
536
|
+
const cursorIgnorePath = baseDir ? join8(baseDir, ".cursorignore") : ".cursorignore";
|
|
428
537
|
const cursorIgnoreContent = generateCursorIgnore(ignorePatterns.patterns);
|
|
429
538
|
outputs.push({
|
|
430
539
|
tool: "cursor",
|
|
@@ -497,15 +606,15 @@ function generateCursorIgnore(patterns) {
|
|
|
497
606
|
}
|
|
498
607
|
|
|
499
608
|
// src/generators/rules/geminicli.ts
|
|
500
|
-
import { join as
|
|
609
|
+
import { join as join9 } from "path";
|
|
501
610
|
async function generateGeminiConfig(rules, config, baseDir) {
|
|
502
611
|
const outputs = [];
|
|
503
612
|
const rootRule = rules.find((rule) => rule.frontmatter.root === true);
|
|
504
613
|
const memoryRules = rules.filter((rule) => rule.frontmatter.root === false);
|
|
505
614
|
for (const rule of memoryRules) {
|
|
506
615
|
const content = generateGeminiMemoryMarkdown(rule);
|
|
507
|
-
const outputDir = baseDir ?
|
|
508
|
-
const filepath =
|
|
616
|
+
const outputDir = baseDir ? join9(baseDir, config.outputPaths.geminicli) : config.outputPaths.geminicli;
|
|
617
|
+
const filepath = join9(outputDir, `${rule.filename}.md`);
|
|
509
618
|
outputs.push({
|
|
510
619
|
tool: "geminicli",
|
|
511
620
|
filepath,
|
|
@@ -513,7 +622,7 @@ async function generateGeminiConfig(rules, config, baseDir) {
|
|
|
513
622
|
});
|
|
514
623
|
}
|
|
515
624
|
const rootContent = generateGeminiRootMarkdown(rootRule, memoryRules, baseDir);
|
|
516
|
-
const rootFilepath = baseDir ?
|
|
625
|
+
const rootFilepath = baseDir ? join9(baseDir, "GEMINI.md") : "GEMINI.md";
|
|
517
626
|
outputs.push({
|
|
518
627
|
tool: "geminicli",
|
|
519
628
|
filepath: rootFilepath,
|
|
@@ -521,7 +630,7 @@ async function generateGeminiConfig(rules, config, baseDir) {
|
|
|
521
630
|
});
|
|
522
631
|
const ignorePatterns = await loadIgnorePatterns(baseDir);
|
|
523
632
|
if (ignorePatterns.patterns.length > 0) {
|
|
524
|
-
const aiexcludePath = baseDir ?
|
|
633
|
+
const aiexcludePath = baseDir ? join9(baseDir, ".aiexclude") : ".aiexclude";
|
|
525
634
|
const aiexcludeContent = generateAiexclude(ignorePatterns.patterns);
|
|
526
635
|
outputs.push({
|
|
527
636
|
tool: "geminicli",
|
|
@@ -568,14 +677,34 @@ function generateAiexclude(patterns) {
|
|
|
568
677
|
return lines.join("\n");
|
|
569
678
|
}
|
|
570
679
|
|
|
680
|
+
// src/generators/rules/kiro.ts
|
|
681
|
+
import { join as join10 } from "path";
|
|
682
|
+
async function generateKiroConfig(rules, config, baseDir) {
|
|
683
|
+
const outputs = [];
|
|
684
|
+
for (const rule of rules) {
|
|
685
|
+
const content = generateKiroMarkdown(rule);
|
|
686
|
+
const outputDir = baseDir ? join10(baseDir, config.outputPaths.kiro) : config.outputPaths.kiro;
|
|
687
|
+
const filepath = join10(outputDir, `${rule.filename}.md`);
|
|
688
|
+
outputs.push({
|
|
689
|
+
tool: "kiro",
|
|
690
|
+
filepath,
|
|
691
|
+
content
|
|
692
|
+
});
|
|
693
|
+
}
|
|
694
|
+
return outputs;
|
|
695
|
+
}
|
|
696
|
+
function generateKiroMarkdown(rule) {
|
|
697
|
+
return rule.content.trim();
|
|
698
|
+
}
|
|
699
|
+
|
|
571
700
|
// src/generators/rules/roo.ts
|
|
572
|
-
import { join as
|
|
701
|
+
import { join as join11 } from "path";
|
|
573
702
|
async function generateRooConfig(rules, config, baseDir) {
|
|
574
703
|
const outputs = [];
|
|
575
704
|
for (const rule of rules) {
|
|
576
705
|
const content = generateRooMarkdown(rule);
|
|
577
|
-
const outputDir = baseDir ?
|
|
578
|
-
const filepath =
|
|
706
|
+
const outputDir = baseDir ? join11(baseDir, config.outputPaths.roo) : config.outputPaths.roo;
|
|
707
|
+
const filepath = join11(outputDir, `${rule.filename}.md`);
|
|
579
708
|
outputs.push({
|
|
580
709
|
tool: "roo",
|
|
581
710
|
filepath,
|
|
@@ -584,7 +713,7 @@ async function generateRooConfig(rules, config, baseDir) {
|
|
|
584
713
|
}
|
|
585
714
|
const ignorePatterns = await loadIgnorePatterns(baseDir);
|
|
586
715
|
if (ignorePatterns.patterns.length > 0) {
|
|
587
|
-
const rooIgnorePath = baseDir ?
|
|
716
|
+
const rooIgnorePath = baseDir ? join11(baseDir, ".rooignore") : ".rooignore";
|
|
588
717
|
const rooIgnoreContent = generateRooIgnore(ignorePatterns.patterns);
|
|
589
718
|
outputs.push({
|
|
590
719
|
tool: "roo",
|
|
@@ -650,6 +779,11 @@ async function generateForTool(tool, rules, config, baseDir) {
|
|
|
650
779
|
return generateRooConfig(rules, config, baseDir);
|
|
651
780
|
case "geminicli":
|
|
652
781
|
return generateGeminiConfig(rules, config, baseDir);
|
|
782
|
+
case "kiro": {
|
|
783
|
+
const kiroRulesOutputs = await generateKiroConfig(rules, config, baseDir);
|
|
784
|
+
const kiroIgnoreOutputs = await generateKiroIgnoreFiles(rules, config, baseDir);
|
|
785
|
+
return [...kiroRulesOutputs, ...kiroIgnoreOutputs];
|
|
786
|
+
}
|
|
653
787
|
default:
|
|
654
788
|
console.warn(`Unknown tool: ${tool}`);
|
|
655
789
|
return null;
|
|
@@ -661,7 +795,7 @@ import { basename } from "path";
|
|
|
661
795
|
import matter from "gray-matter";
|
|
662
796
|
|
|
663
797
|
// src/types/config.ts
|
|
664
|
-
import { z as z2 } from "zod/
|
|
798
|
+
import { z as z2 } from "zod/mini";
|
|
665
799
|
var ConfigSchema = z2.object({
|
|
666
800
|
aiRulesDir: z2.string(),
|
|
667
801
|
outputPaths: z2.record(ToolTargetSchema, z2.string()),
|
|
@@ -670,7 +804,7 @@ var ConfigSchema = z2.object({
|
|
|
670
804
|
});
|
|
671
805
|
|
|
672
806
|
// src/types/mcp.ts
|
|
673
|
-
import { z as z3 } from "zod/
|
|
807
|
+
import { z as z3 } from "zod/mini";
|
|
674
808
|
var McpTransportTypeSchema = z3.enum(["stdio", "sse", "http"]);
|
|
675
809
|
var McpServerBaseSchema = z3.object({
|
|
676
810
|
command: z3.optional(z3.string()),
|
|
@@ -686,7 +820,9 @@ var McpServerBaseSchema = z3.object({
|
|
|
686
820
|
transport: z3.optional(McpTransportTypeSchema),
|
|
687
821
|
type: z3.optional(z3.enum(["sse", "streamable-http"])),
|
|
688
822
|
alwaysAllow: z3.optional(z3.array(z3.string())),
|
|
689
|
-
tools: z3.optional(z3.array(z3.string()))
|
|
823
|
+
tools: z3.optional(z3.array(z3.string())),
|
|
824
|
+
kiroAutoApprove: z3.optional(z3.array(z3.string())),
|
|
825
|
+
kiroAutoBlock: z3.optional(z3.array(z3.string()))
|
|
690
826
|
});
|
|
691
827
|
var RulesyncMcpServerSchema = z3.extend(McpServerBaseSchema, {
|
|
692
828
|
targets: z3.optional(RulesyncTargetsSchema)
|
|
@@ -699,7 +835,7 @@ var RulesyncMcpConfigSchema = z3.object({
|
|
|
699
835
|
});
|
|
700
836
|
|
|
701
837
|
// src/types/rules.ts
|
|
702
|
-
import { z as z4 } from "zod/
|
|
838
|
+
import { z as z4 } from "zod/mini";
|
|
703
839
|
var RuleFrontmatterSchema = z4.object({
|
|
704
840
|
root: z4.boolean(),
|
|
705
841
|
targets: RulesyncTargetsSchema,
|
|
@@ -727,7 +863,8 @@ var GenerateOptionsSchema = z4.object({
|
|
|
727
863
|
// src/core/parser.ts
|
|
728
864
|
async function parseRulesFromDirectory(aiRulesDir) {
|
|
729
865
|
const ignorePatterns = await loadIgnorePatterns();
|
|
730
|
-
const
|
|
866
|
+
const allRuleFiles = await findFiles(aiRulesDir, ".md");
|
|
867
|
+
const ruleFiles = filterIgnoredFiles(allRuleFiles, ignorePatterns.patterns);
|
|
731
868
|
const rules = [];
|
|
732
869
|
const errors = [];
|
|
733
870
|
if (ignorePatterns.patterns.length > 0) {
|
|
@@ -888,6 +1025,11 @@ async function generateMcpConfigs(projectRoot, baseDir) {
|
|
|
888
1025
|
path: path3.join(targetRoot, ".gemini", "settings.json"),
|
|
889
1026
|
generate: () => generateGeminiCliMcp(config)
|
|
890
1027
|
},
|
|
1028
|
+
{
|
|
1029
|
+
tool: "kiro-project",
|
|
1030
|
+
path: path3.join(targetRoot, ".kiro", "mcp.json"),
|
|
1031
|
+
generate: () => generateKiroMcp(config)
|
|
1032
|
+
},
|
|
891
1033
|
{
|
|
892
1034
|
tool: "roo-project",
|
|
893
1035
|
path: path3.join(targetRoot, ".roo", "mcp.json"),
|
|
@@ -898,7 +1040,7 @@ async function generateMcpConfigs(projectRoot, baseDir) {
|
|
|
898
1040
|
try {
|
|
899
1041
|
const content = generator.generate();
|
|
900
1042
|
const parsed = JSON.parse(content);
|
|
901
|
-
if (generator.tool.includes("claude") || generator.tool.includes("cline") || generator.tool.includes("cursor") || generator.tool.includes("gemini") || generator.tool.includes("roo")) {
|
|
1043
|
+
if (generator.tool.includes("claude") || generator.tool.includes("cline") || generator.tool.includes("cursor") || generator.tool.includes("gemini") || generator.tool.includes("kiro") || generator.tool.includes("roo")) {
|
|
902
1044
|
if (!parsed.mcpServers || Object.keys(parsed.mcpServers).length === 0) {
|
|
903
1045
|
results.push({
|
|
904
1046
|
tool: generator.tool,
|
|
@@ -984,6 +1126,9 @@ async function generateCommand(options = {}) {
|
|
|
984
1126
|
case "geminicli":
|
|
985
1127
|
deleteTasks.push(removeDirectory(config.outputPaths.geminicli));
|
|
986
1128
|
break;
|
|
1129
|
+
case "kiro":
|
|
1130
|
+
deleteTasks.push(removeDirectory(config.outputPaths.kiro));
|
|
1131
|
+
break;
|
|
987
1132
|
}
|
|
988
1133
|
}
|
|
989
1134
|
await Promise.all(deleteTasks);
|
|
@@ -1048,9 +1193,9 @@ Generating configurations for base directory: ${baseDir}`);
|
|
|
1048
1193
|
|
|
1049
1194
|
// src/cli/commands/gitignore.ts
|
|
1050
1195
|
import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync } from "fs";
|
|
1051
|
-
import { join as
|
|
1196
|
+
import { join as join14 } from "path";
|
|
1052
1197
|
var gitignoreCommand = async () => {
|
|
1053
|
-
const gitignorePath =
|
|
1198
|
+
const gitignorePath = join14(process.cwd(), ".gitignore");
|
|
1054
1199
|
const rulesFilesToIgnore = [
|
|
1055
1200
|
"# Generated by rulesync - AI tool configuration files",
|
|
1056
1201
|
"**/.github/copilot-instructions.md",
|
|
@@ -1067,6 +1212,8 @@ var gitignoreCommand = async () => {
|
|
|
1067
1212
|
"**/GEMINI.md",
|
|
1068
1213
|
"**/.gemini/memories/",
|
|
1069
1214
|
"**/.aiexclude",
|
|
1215
|
+
"**/.aiignore",
|
|
1216
|
+
"**/.kiro/steering/",
|
|
1070
1217
|
"**/.mcp.json",
|
|
1071
1218
|
"!.rulesync/.mcp.json",
|
|
1072
1219
|
"**/.cursor/mcp.json",
|
|
@@ -1104,17 +1251,17 @@ ${linesToAdd.join("\n")}
|
|
|
1104
1251
|
};
|
|
1105
1252
|
|
|
1106
1253
|
// src/core/importer.ts
|
|
1107
|
-
import { join as
|
|
1254
|
+
import { join as join21 } from "path";
|
|
1108
1255
|
import matter4 from "gray-matter";
|
|
1109
1256
|
|
|
1110
1257
|
// src/parsers/claudecode.ts
|
|
1111
|
-
import { basename as basename2, join as
|
|
1258
|
+
import { basename as basename2, join as join15 } from "path";
|
|
1112
1259
|
async function parseClaudeConfiguration(baseDir = process.cwd()) {
|
|
1113
1260
|
const errors = [];
|
|
1114
1261
|
const rules = [];
|
|
1115
1262
|
let ignorePatterns;
|
|
1116
1263
|
let mcpServers;
|
|
1117
|
-
const claudeFilePath =
|
|
1264
|
+
const claudeFilePath = join15(baseDir, "CLAUDE.md");
|
|
1118
1265
|
if (!await fileExists(claudeFilePath)) {
|
|
1119
1266
|
errors.push("CLAUDE.md file not found");
|
|
1120
1267
|
return { rules, errors };
|
|
@@ -1125,12 +1272,12 @@ async function parseClaudeConfiguration(baseDir = process.cwd()) {
|
|
|
1125
1272
|
if (mainRule) {
|
|
1126
1273
|
rules.push(mainRule);
|
|
1127
1274
|
}
|
|
1128
|
-
const memoryDir =
|
|
1275
|
+
const memoryDir = join15(baseDir, ".claude", "memories");
|
|
1129
1276
|
if (await fileExists(memoryDir)) {
|
|
1130
1277
|
const memoryRules = await parseClaudeMemoryFiles(memoryDir);
|
|
1131
1278
|
rules.push(...memoryRules);
|
|
1132
1279
|
}
|
|
1133
|
-
const settingsPath =
|
|
1280
|
+
const settingsPath = join15(baseDir, ".claude", "settings.json");
|
|
1134
1281
|
if (await fileExists(settingsPath)) {
|
|
1135
1282
|
const settingsResult = await parseClaudeSettings(settingsPath);
|
|
1136
1283
|
if (settingsResult.ignorePatterns) {
|
|
@@ -1187,7 +1334,7 @@ async function parseClaudeMemoryFiles(memoryDir) {
|
|
|
1187
1334
|
const files = await readdir2(memoryDir);
|
|
1188
1335
|
for (const file of files) {
|
|
1189
1336
|
if (file.endsWith(".md")) {
|
|
1190
|
-
const filePath =
|
|
1337
|
+
const filePath = join15(memoryDir, file);
|
|
1191
1338
|
const content = await readFileContent(filePath);
|
|
1192
1339
|
if (content.trim()) {
|
|
1193
1340
|
const filename = basename2(file, ".md");
|
|
@@ -1250,11 +1397,11 @@ async function parseClaudeSettings(settingsPath) {
|
|
|
1250
1397
|
}
|
|
1251
1398
|
|
|
1252
1399
|
// src/parsers/cline.ts
|
|
1253
|
-
import { join as
|
|
1400
|
+
import { join as join16 } from "path";
|
|
1254
1401
|
async function parseClineConfiguration(baseDir = process.cwd()) {
|
|
1255
1402
|
const errors = [];
|
|
1256
1403
|
const rules = [];
|
|
1257
|
-
const clineFilePath =
|
|
1404
|
+
const clineFilePath = join16(baseDir, ".cline", "instructions.md");
|
|
1258
1405
|
if (await fileExists(clineFilePath)) {
|
|
1259
1406
|
try {
|
|
1260
1407
|
const content = await readFileContent(clineFilePath);
|
|
@@ -1277,14 +1424,14 @@ async function parseClineConfiguration(baseDir = process.cwd()) {
|
|
|
1277
1424
|
errors.push(`Failed to parse .cline/instructions.md: ${errorMessage}`);
|
|
1278
1425
|
}
|
|
1279
1426
|
}
|
|
1280
|
-
const clinerulesDirPath =
|
|
1427
|
+
const clinerulesDirPath = join16(baseDir, ".clinerules");
|
|
1281
1428
|
if (await fileExists(clinerulesDirPath)) {
|
|
1282
1429
|
try {
|
|
1283
1430
|
const { readdir: readdir2 } = await import("fs/promises");
|
|
1284
1431
|
const files = await readdir2(clinerulesDirPath);
|
|
1285
1432
|
for (const file of files) {
|
|
1286
1433
|
if (file.endsWith(".md")) {
|
|
1287
|
-
const filePath =
|
|
1434
|
+
const filePath = join16(clinerulesDirPath, file);
|
|
1288
1435
|
try {
|
|
1289
1436
|
const content = await readFileContent(filePath);
|
|
1290
1437
|
if (content.trim()) {
|
|
@@ -1320,12 +1467,12 @@ async function parseClineConfiguration(baseDir = process.cwd()) {
|
|
|
1320
1467
|
}
|
|
1321
1468
|
|
|
1322
1469
|
// src/parsers/copilot.ts
|
|
1323
|
-
import { basename as basename3, join as
|
|
1470
|
+
import { basename as basename3, join as join17 } from "path";
|
|
1324
1471
|
import matter2 from "gray-matter";
|
|
1325
1472
|
async function parseCopilotConfiguration(baseDir = process.cwd()) {
|
|
1326
1473
|
const errors = [];
|
|
1327
1474
|
const rules = [];
|
|
1328
|
-
const copilotFilePath =
|
|
1475
|
+
const copilotFilePath = join17(baseDir, ".github", "copilot-instructions.md");
|
|
1329
1476
|
if (await fileExists(copilotFilePath)) {
|
|
1330
1477
|
try {
|
|
1331
1478
|
const rawContent = await readFileContent(copilotFilePath);
|
|
@@ -1350,14 +1497,14 @@ async function parseCopilotConfiguration(baseDir = process.cwd()) {
|
|
|
1350
1497
|
errors.push(`Failed to parse copilot-instructions.md: ${errorMessage}`);
|
|
1351
1498
|
}
|
|
1352
1499
|
}
|
|
1353
|
-
const instructionsDir =
|
|
1500
|
+
const instructionsDir = join17(baseDir, ".github", "instructions");
|
|
1354
1501
|
if (await fileExists(instructionsDir)) {
|
|
1355
1502
|
try {
|
|
1356
1503
|
const { readdir: readdir2 } = await import("fs/promises");
|
|
1357
1504
|
const files = await readdir2(instructionsDir);
|
|
1358
1505
|
for (const file of files) {
|
|
1359
1506
|
if (file.endsWith(".instructions.md")) {
|
|
1360
|
-
const filePath =
|
|
1507
|
+
const filePath = join17(instructionsDir, file);
|
|
1361
1508
|
const rawContent = await readFileContent(filePath);
|
|
1362
1509
|
const parsed = matter2(rawContent);
|
|
1363
1510
|
const content = parsed.content.trim();
|
|
@@ -1392,10 +1539,10 @@ async function parseCopilotConfiguration(baseDir = process.cwd()) {
|
|
|
1392
1539
|
}
|
|
1393
1540
|
|
|
1394
1541
|
// src/parsers/cursor.ts
|
|
1395
|
-
import { basename as basename4, join as
|
|
1542
|
+
import { basename as basename4, join as join18 } from "path";
|
|
1396
1543
|
import matter3 from "gray-matter";
|
|
1397
1544
|
import { DEFAULT_SCHEMA, FAILSAFE_SCHEMA, load } from "js-yaml";
|
|
1398
|
-
import { z as z5 } from "zod/
|
|
1545
|
+
import { z as z5 } from "zod/mini";
|
|
1399
1546
|
var customMatterOptions = {
|
|
1400
1547
|
engines: {
|
|
1401
1548
|
yaml: {
|
|
@@ -1517,7 +1664,7 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
|
|
|
1517
1664
|
const rules = [];
|
|
1518
1665
|
let ignorePatterns;
|
|
1519
1666
|
let mcpServers;
|
|
1520
|
-
const cursorFilePath =
|
|
1667
|
+
const cursorFilePath = join18(baseDir, ".cursorrules");
|
|
1521
1668
|
if (await fileExists(cursorFilePath)) {
|
|
1522
1669
|
try {
|
|
1523
1670
|
const rawContent = await readFileContent(cursorFilePath);
|
|
@@ -1538,14 +1685,14 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
|
|
|
1538
1685
|
errors.push(`Failed to parse .cursorrules file: ${errorMessage}`);
|
|
1539
1686
|
}
|
|
1540
1687
|
}
|
|
1541
|
-
const cursorRulesDir =
|
|
1688
|
+
const cursorRulesDir = join18(baseDir, ".cursor", "rules");
|
|
1542
1689
|
if (await fileExists(cursorRulesDir)) {
|
|
1543
1690
|
try {
|
|
1544
1691
|
const { readdir: readdir2 } = await import("fs/promises");
|
|
1545
1692
|
const files = await readdir2(cursorRulesDir);
|
|
1546
1693
|
for (const file of files) {
|
|
1547
1694
|
if (file.endsWith(".mdc")) {
|
|
1548
|
-
const filePath =
|
|
1695
|
+
const filePath = join18(cursorRulesDir, file);
|
|
1549
1696
|
try {
|
|
1550
1697
|
const rawContent = await readFileContent(filePath);
|
|
1551
1698
|
const parsed = matter3(rawContent, customMatterOptions);
|
|
@@ -1574,7 +1721,7 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
|
|
|
1574
1721
|
if (rules.length === 0) {
|
|
1575
1722
|
errors.push("No Cursor configuration files found (.cursorrules or .cursor/rules/*.mdc)");
|
|
1576
1723
|
}
|
|
1577
|
-
const cursorIgnorePath =
|
|
1724
|
+
const cursorIgnorePath = join18(baseDir, ".cursorignore");
|
|
1578
1725
|
if (await fileExists(cursorIgnorePath)) {
|
|
1579
1726
|
try {
|
|
1580
1727
|
const content = await readFileContent(cursorIgnorePath);
|
|
@@ -1587,7 +1734,7 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
|
|
|
1587
1734
|
errors.push(`Failed to parse .cursorignore: ${errorMessage}`);
|
|
1588
1735
|
}
|
|
1589
1736
|
}
|
|
1590
|
-
const cursorMcpPath =
|
|
1737
|
+
const cursorMcpPath = join18(baseDir, ".cursor", "mcp.json");
|
|
1591
1738
|
if (await fileExists(cursorMcpPath)) {
|
|
1592
1739
|
try {
|
|
1593
1740
|
const content = await readFileContent(cursorMcpPath);
|
|
@@ -1610,13 +1757,13 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
|
|
|
1610
1757
|
}
|
|
1611
1758
|
|
|
1612
1759
|
// src/parsers/geminicli.ts
|
|
1613
|
-
import { basename as basename5, join as
|
|
1760
|
+
import { basename as basename5, join as join19 } from "path";
|
|
1614
1761
|
async function parseGeminiConfiguration(baseDir = process.cwd()) {
|
|
1615
1762
|
const errors = [];
|
|
1616
1763
|
const rules = [];
|
|
1617
1764
|
let ignorePatterns;
|
|
1618
1765
|
let mcpServers;
|
|
1619
|
-
const geminiFilePath =
|
|
1766
|
+
const geminiFilePath = join19(baseDir, "GEMINI.md");
|
|
1620
1767
|
if (!await fileExists(geminiFilePath)) {
|
|
1621
1768
|
errors.push("GEMINI.md file not found");
|
|
1622
1769
|
return { rules, errors };
|
|
@@ -1627,12 +1774,12 @@ async function parseGeminiConfiguration(baseDir = process.cwd()) {
|
|
|
1627
1774
|
if (mainRule) {
|
|
1628
1775
|
rules.push(mainRule);
|
|
1629
1776
|
}
|
|
1630
|
-
const memoryDir =
|
|
1777
|
+
const memoryDir = join19(baseDir, ".gemini", "memories");
|
|
1631
1778
|
if (await fileExists(memoryDir)) {
|
|
1632
1779
|
const memoryRules = await parseGeminiMemoryFiles(memoryDir);
|
|
1633
1780
|
rules.push(...memoryRules);
|
|
1634
1781
|
}
|
|
1635
|
-
const settingsPath =
|
|
1782
|
+
const settingsPath = join19(baseDir, ".gemini", "settings.json");
|
|
1636
1783
|
if (await fileExists(settingsPath)) {
|
|
1637
1784
|
const settingsResult = await parseGeminiSettings(settingsPath);
|
|
1638
1785
|
if (settingsResult.ignorePatterns) {
|
|
@@ -1643,7 +1790,7 @@ async function parseGeminiConfiguration(baseDir = process.cwd()) {
|
|
|
1643
1790
|
}
|
|
1644
1791
|
errors.push(...settingsResult.errors);
|
|
1645
1792
|
}
|
|
1646
|
-
const aiexcludePath =
|
|
1793
|
+
const aiexcludePath = join19(baseDir, ".aiexclude");
|
|
1647
1794
|
if (await fileExists(aiexcludePath)) {
|
|
1648
1795
|
const aiexcludePatterns = await parseAiexclude(aiexcludePath);
|
|
1649
1796
|
if (aiexcludePatterns.length > 0) {
|
|
@@ -1696,7 +1843,7 @@ async function parseGeminiMemoryFiles(memoryDir) {
|
|
|
1696
1843
|
const files = await readdir2(memoryDir);
|
|
1697
1844
|
for (const file of files) {
|
|
1698
1845
|
if (file.endsWith(".md")) {
|
|
1699
|
-
const filePath =
|
|
1846
|
+
const filePath = join19(memoryDir, file);
|
|
1700
1847
|
const content = await readFileContent(filePath);
|
|
1701
1848
|
if (content.trim()) {
|
|
1702
1849
|
const filename = basename5(file, ".md");
|
|
@@ -1749,11 +1896,11 @@ async function parseAiexclude(aiexcludePath) {
|
|
|
1749
1896
|
}
|
|
1750
1897
|
|
|
1751
1898
|
// src/parsers/roo.ts
|
|
1752
|
-
import { join as
|
|
1899
|
+
import { join as join20 } from "path";
|
|
1753
1900
|
async function parseRooConfiguration(baseDir = process.cwd()) {
|
|
1754
1901
|
const errors = [];
|
|
1755
1902
|
const rules = [];
|
|
1756
|
-
const rooFilePath =
|
|
1903
|
+
const rooFilePath = join20(baseDir, ".roo", "instructions.md");
|
|
1757
1904
|
if (await fileExists(rooFilePath)) {
|
|
1758
1905
|
try {
|
|
1759
1906
|
const content = await readFileContent(rooFilePath);
|
|
@@ -1776,14 +1923,14 @@ async function parseRooConfiguration(baseDir = process.cwd()) {
|
|
|
1776
1923
|
errors.push(`Failed to parse .roo/instructions.md: ${errorMessage}`);
|
|
1777
1924
|
}
|
|
1778
1925
|
}
|
|
1779
|
-
const rooRulesDir =
|
|
1926
|
+
const rooRulesDir = join20(baseDir, ".roo", "rules");
|
|
1780
1927
|
if (await fileExists(rooRulesDir)) {
|
|
1781
1928
|
try {
|
|
1782
1929
|
const { readdir: readdir2 } = await import("fs/promises");
|
|
1783
1930
|
const files = await readdir2(rooRulesDir);
|
|
1784
1931
|
for (const file of files) {
|
|
1785
1932
|
if (file.endsWith(".md")) {
|
|
1786
|
-
const filePath =
|
|
1933
|
+
const filePath = join20(rooRulesDir, file);
|
|
1787
1934
|
try {
|
|
1788
1935
|
const content = await readFileContent(filePath);
|
|
1789
1936
|
if (content.trim()) {
|
|
@@ -1884,7 +2031,7 @@ async function importConfiguration(options) {
|
|
|
1884
2031
|
if (rules.length === 0 && !ignorePatterns && !mcpServers) {
|
|
1885
2032
|
return { success: false, rulesCreated: 0, errors };
|
|
1886
2033
|
}
|
|
1887
|
-
const rulesDirPath =
|
|
2034
|
+
const rulesDirPath = join21(baseDir, rulesDir);
|
|
1888
2035
|
try {
|
|
1889
2036
|
const { mkdir: mkdir3 } = await import("fs/promises");
|
|
1890
2037
|
await mkdir3(rulesDirPath, { recursive: true });
|
|
@@ -1898,7 +2045,7 @@ async function importConfiguration(options) {
|
|
|
1898
2045
|
try {
|
|
1899
2046
|
const baseFilename = `${tool}__${rule.filename}`;
|
|
1900
2047
|
const filename = await generateUniqueFilename(rulesDirPath, baseFilename);
|
|
1901
|
-
const filePath =
|
|
2048
|
+
const filePath = join21(rulesDirPath, `${filename}.md`);
|
|
1902
2049
|
const content = generateRuleFileContent(rule);
|
|
1903
2050
|
await writeFileContent(filePath, content);
|
|
1904
2051
|
rulesCreated++;
|
|
@@ -1913,7 +2060,7 @@ async function importConfiguration(options) {
|
|
|
1913
2060
|
let ignoreFileCreated = false;
|
|
1914
2061
|
if (ignorePatterns && ignorePatterns.length > 0) {
|
|
1915
2062
|
try {
|
|
1916
|
-
const rulesyncignorePath =
|
|
2063
|
+
const rulesyncignorePath = join21(baseDir, ".rulesyncignore");
|
|
1917
2064
|
const ignoreContent = `${ignorePatterns.join("\n")}
|
|
1918
2065
|
`;
|
|
1919
2066
|
await writeFileContent(rulesyncignorePath, ignoreContent);
|
|
@@ -1929,7 +2076,7 @@ async function importConfiguration(options) {
|
|
|
1929
2076
|
let mcpFileCreated = false;
|
|
1930
2077
|
if (mcpServers && Object.keys(mcpServers).length > 0) {
|
|
1931
2078
|
try {
|
|
1932
|
-
const mcpPath =
|
|
2079
|
+
const mcpPath = join21(baseDir, rulesDir, ".mcp.json");
|
|
1933
2080
|
const mcpContent = `${JSON.stringify({ mcpServers }, null, 2)}
|
|
1934
2081
|
`;
|
|
1935
2082
|
await writeFileContent(mcpPath, mcpContent);
|
|
@@ -1943,7 +2090,7 @@ async function importConfiguration(options) {
|
|
|
1943
2090
|
}
|
|
1944
2091
|
}
|
|
1945
2092
|
return {
|
|
1946
|
-
success: rulesCreated > 0 || ignoreFileCreated || mcpFileCreated,
|
|
2093
|
+
success: errors.length === 0 && (rulesCreated > 0 || ignoreFileCreated || mcpFileCreated),
|
|
1947
2094
|
rulesCreated,
|
|
1948
2095
|
errors,
|
|
1949
2096
|
ignoreFileCreated,
|
|
@@ -1957,7 +2104,7 @@ function generateRuleFileContent(rule) {
|
|
|
1957
2104
|
async function generateUniqueFilename(rulesDir, baseFilename) {
|
|
1958
2105
|
let filename = baseFilename;
|
|
1959
2106
|
let counter = 1;
|
|
1960
|
-
while (await fileExists(
|
|
2107
|
+
while (await fileExists(join21(rulesDir, `${filename}.md`))) {
|
|
1961
2108
|
filename = `${baseFilename}-${counter}`;
|
|
1962
2109
|
counter++;
|
|
1963
2110
|
}
|
|
@@ -2022,7 +2169,7 @@ async function importCommand(options = {}) {
|
|
|
2022
2169
|
}
|
|
2023
2170
|
|
|
2024
2171
|
// src/cli/commands/init.ts
|
|
2025
|
-
import { join as
|
|
2172
|
+
import { join as join22 } from "path";
|
|
2026
2173
|
async function initCommand() {
|
|
2027
2174
|
const aiRulesDir = ".rulesync";
|
|
2028
2175
|
console.log("Initializing rulesync...");
|
|
@@ -2069,7 +2216,7 @@ globs: ["**/*"]
|
|
|
2069
2216
|
- Follow single responsibility principle
|
|
2070
2217
|
`
|
|
2071
2218
|
};
|
|
2072
|
-
const filepath =
|
|
2219
|
+
const filepath = join22(aiRulesDir, sampleFile.filename);
|
|
2073
2220
|
if (!await fileExists(filepath)) {
|
|
2074
2221
|
await writeFileContent(filepath, sampleFile.content);
|
|
2075
2222
|
console.log(`Created ${filepath}`);
|
|
@@ -2213,12 +2360,12 @@ async function watchCommand() {
|
|
|
2213
2360
|
|
|
2214
2361
|
// src/cli/index.ts
|
|
2215
2362
|
var program = new Command();
|
|
2216
|
-
program.name("rulesync").description("Unified AI rules management CLI tool").version("0.
|
|
2363
|
+
program.name("rulesync").description("Unified AI rules management CLI tool").version("0.47.0");
|
|
2217
2364
|
program.command("init").description("Initialize rulesync in current directory").action(initCommand);
|
|
2218
2365
|
program.command("add <filename>").description("Add a new rule file").action(addCommand);
|
|
2219
2366
|
program.command("gitignore").description("Add generated files to .gitignore").action(gitignoreCommand);
|
|
2220
2367
|
program.command("import").description("Import configurations from AI tools to rulesync format").option("--claudecode", "Import from Claude Code (CLAUDE.md)").option("--cursor", "Import from Cursor (.cursorrules)").option("--copilot", "Import from GitHub Copilot (.github/copilot-instructions.md)").option("--cline", "Import from Cline (.cline/instructions.md)").option("--roo", "Import from Roo Code (.roo/instructions.md)").option("--geminicli", "Import from Gemini CLI (GEMINI.md)").option("-v, --verbose", "Verbose output").action(importCommand);
|
|
2221
|
-
program.command("generate").description("Generate configuration files for AI tools").option("--copilot", "Generate only for GitHub Copilot").option("--cursor", "Generate only for Cursor").option("--cline", "Generate only for Cline").option("--claudecode", "Generate only for Claude Code").option("--roo", "Generate only for Roo Code").option("--geminicli", "Generate only for Gemini CLI").option("--delete", "Delete all existing files in output directories before generating").option(
|
|
2368
|
+
program.command("generate").description("Generate configuration files for AI tools").option("--copilot", "Generate only for GitHub Copilot").option("--cursor", "Generate only for Cursor").option("--cline", "Generate only for Cline").option("--claudecode", "Generate only for Claude Code").option("--roo", "Generate only for Roo Code").option("--geminicli", "Generate only for Gemini CLI").option("--kiro", "Generate only for Kiro IDE").option("--delete", "Delete all existing files in output directories before generating").option(
|
|
2222
2369
|
"-b, --base-dir <paths>",
|
|
2223
2370
|
"Base directories to generate files (comma-separated for multiple paths)"
|
|
2224
2371
|
).option("-v, --verbose", "Verbose output").action(async (options) => {
|
|
@@ -2229,6 +2376,7 @@ program.command("generate").description("Generate configuration files for AI too
|
|
|
2229
2376
|
if (options.claudecode) tools.push("claudecode");
|
|
2230
2377
|
if (options.roo) tools.push("roo");
|
|
2231
2378
|
if (options.geminicli) tools.push("geminicli");
|
|
2379
|
+
if (options.kiro) tools.push("kiro");
|
|
2232
2380
|
const generateOptions = {
|
|
2233
2381
|
verbose: options.verbose,
|
|
2234
2382
|
delete: options.delete
|