rulesync 0.44.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 +381 -266
- package/dist/index.js +301 -236
- 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.cjs
CHANGED
|
@@ -27,22 +27,23 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
27
27
|
));
|
|
28
28
|
|
|
29
29
|
// src/types/tool-targets.ts
|
|
30
|
-
var
|
|
30
|
+
var import_mini, ToolTargetSchema, ToolTargetsSchema, WildcardTargetSchema, RulesyncTargetsSchema;
|
|
31
31
|
var init_tool_targets = __esm({
|
|
32
32
|
"src/types/tool-targets.ts"() {
|
|
33
33
|
"use strict";
|
|
34
|
-
|
|
35
|
-
ToolTargetSchema =
|
|
34
|
+
import_mini = require("zod/mini");
|
|
35
|
+
ToolTargetSchema = import_mini.z.enum([
|
|
36
36
|
"copilot",
|
|
37
37
|
"cursor",
|
|
38
38
|
"cline",
|
|
39
39
|
"claudecode",
|
|
40
40
|
"roo",
|
|
41
|
-
"geminicli"
|
|
41
|
+
"geminicli",
|
|
42
|
+
"kiro"
|
|
42
43
|
]);
|
|
43
|
-
ToolTargetsSchema =
|
|
44
|
-
WildcardTargetSchema =
|
|
45
|
-
RulesyncTargetsSchema =
|
|
44
|
+
ToolTargetsSchema = import_mini.z.array(ToolTargetSchema);
|
|
45
|
+
WildcardTargetSchema = import_mini.z.tuple([import_mini.z.literal("*")]);
|
|
46
|
+
RulesyncTargetsSchema = import_mini.z.union([ToolTargetsSchema, WildcardTargetSchema]);
|
|
46
47
|
}
|
|
47
48
|
});
|
|
48
49
|
|
|
@@ -294,6 +295,57 @@ var init_geminicli = __esm({
|
|
|
294
295
|
}
|
|
295
296
|
});
|
|
296
297
|
|
|
298
|
+
// src/generators/mcp/kiro.ts
|
|
299
|
+
function generateKiroMcp(config) {
|
|
300
|
+
const kiroConfig = {
|
|
301
|
+
mcpServers: {}
|
|
302
|
+
};
|
|
303
|
+
for (const [serverName, server] of Object.entries(config.mcpServers)) {
|
|
304
|
+
if (!shouldIncludeServer(server, "kiro")) continue;
|
|
305
|
+
const kiroServer = {};
|
|
306
|
+
if (server.command) {
|
|
307
|
+
kiroServer.command = server.command;
|
|
308
|
+
if (server.args) kiroServer.args = server.args;
|
|
309
|
+
} else if (server.url || server.httpUrl) {
|
|
310
|
+
const url = server.httpUrl || server.url;
|
|
311
|
+
if (url) {
|
|
312
|
+
kiroServer.url = url;
|
|
313
|
+
}
|
|
314
|
+
if (server.httpUrl || server.transport === "http") {
|
|
315
|
+
kiroServer.transport = "streamable-http";
|
|
316
|
+
} else if (server.transport === "sse" || server.type === "sse") {
|
|
317
|
+
kiroServer.transport = "sse";
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
if (server.env) {
|
|
321
|
+
kiroServer.env = server.env;
|
|
322
|
+
}
|
|
323
|
+
if (server.timeout) {
|
|
324
|
+
kiroServer.timeout = server.timeout;
|
|
325
|
+
}
|
|
326
|
+
if (server.disabled !== void 0) {
|
|
327
|
+
kiroServer.disabled = server.disabled;
|
|
328
|
+
}
|
|
329
|
+
if (server.transport) {
|
|
330
|
+
kiroServer.transport = server.transport;
|
|
331
|
+
}
|
|
332
|
+
if (server.kiroAutoApprove) {
|
|
333
|
+
kiroServer.autoApprove = server.kiroAutoApprove;
|
|
334
|
+
}
|
|
335
|
+
if (server.kiroAutoBlock) {
|
|
336
|
+
kiroServer.autoBlock = server.kiroAutoBlock;
|
|
337
|
+
}
|
|
338
|
+
kiroConfig.mcpServers[serverName] = kiroServer;
|
|
339
|
+
}
|
|
340
|
+
return JSON.stringify(kiroConfig, null, 2);
|
|
341
|
+
}
|
|
342
|
+
var init_kiro = __esm({
|
|
343
|
+
"src/generators/mcp/kiro.ts"() {
|
|
344
|
+
"use strict";
|
|
345
|
+
init_mcp_helpers();
|
|
346
|
+
}
|
|
347
|
+
});
|
|
348
|
+
|
|
297
349
|
// src/generators/mcp/roo.ts
|
|
298
350
|
function generateRooMcp(config) {
|
|
299
351
|
const rooConfig = {
|
|
@@ -364,10 +416,11 @@ function getDefaultConfig() {
|
|
|
364
416
|
cline: ".clinerules",
|
|
365
417
|
claudecode: ".",
|
|
366
418
|
roo: ".roo/rules",
|
|
367
|
-
geminicli: ".gemini/memories"
|
|
419
|
+
geminicli: ".gemini/memories",
|
|
420
|
+
kiro: ".kiro/steering"
|
|
368
421
|
},
|
|
369
422
|
watchEnabled: false,
|
|
370
|
-
defaultTargets: ["copilot", "cursor", "cline", "claudecode", "roo", "geminicli"]
|
|
423
|
+
defaultTargets: ["copilot", "cursor", "cline", "claudecode", "roo", "geminicli", "kiro"]
|
|
371
424
|
};
|
|
372
425
|
}
|
|
373
426
|
function resolveTargets(targets, config) {
|
|
@@ -414,27 +467,138 @@ async function addCommand(filename) {
|
|
|
414
467
|
}
|
|
415
468
|
}
|
|
416
469
|
|
|
470
|
+
// src/generators/ignore/kiro.ts
|
|
471
|
+
var import_node_path = require("path");
|
|
472
|
+
async function generateKiroIgnoreFiles(rules, config, baseDir) {
|
|
473
|
+
const outputs = [];
|
|
474
|
+
const aiignoreContent = generateAiignoreContent(rules);
|
|
475
|
+
const outputPath = baseDir || process.cwd();
|
|
476
|
+
const filepath = (0, import_node_path.join)(outputPath, ".aiignore");
|
|
477
|
+
outputs.push({
|
|
478
|
+
tool: "kiro",
|
|
479
|
+
filepath,
|
|
480
|
+
content: aiignoreContent
|
|
481
|
+
});
|
|
482
|
+
return outputs;
|
|
483
|
+
}
|
|
484
|
+
function generateAiignoreContent(rules) {
|
|
485
|
+
const lines = [
|
|
486
|
+
"# Generated by rulesync - Kiro AI-specific exclusions",
|
|
487
|
+
"# This file excludes files that can be in Git but shouldn't be read by the AI",
|
|
488
|
+
""
|
|
489
|
+
];
|
|
490
|
+
lines.push(
|
|
491
|
+
"# Data files AI shouldn't process",
|
|
492
|
+
"*.csv",
|
|
493
|
+
"*.tsv",
|
|
494
|
+
"*.sqlite",
|
|
495
|
+
"*.db",
|
|
496
|
+
"",
|
|
497
|
+
"# Large binary files",
|
|
498
|
+
"*.zip",
|
|
499
|
+
"*.tar.gz",
|
|
500
|
+
"*.rar",
|
|
501
|
+
"",
|
|
502
|
+
"# Sensitive documentation",
|
|
503
|
+
"internal-docs/",
|
|
504
|
+
"confidential/",
|
|
505
|
+
"",
|
|
506
|
+
"# Test data that might confuse AI",
|
|
507
|
+
"test/fixtures/large-*.json",
|
|
508
|
+
"benchmark-results/",
|
|
509
|
+
"",
|
|
510
|
+
"# Reinforce critical exclusions from .gitignore",
|
|
511
|
+
"*.pem",
|
|
512
|
+
"*.key",
|
|
513
|
+
".env*",
|
|
514
|
+
""
|
|
515
|
+
);
|
|
516
|
+
const rulePatterns = extractIgnorePatternsFromRules(rules);
|
|
517
|
+
if (rulePatterns.length > 0) {
|
|
518
|
+
lines.push("# Project-specific exclusions from rulesync rules");
|
|
519
|
+
lines.push(...rulePatterns);
|
|
520
|
+
lines.push("");
|
|
521
|
+
}
|
|
522
|
+
return lines.join("\n");
|
|
523
|
+
}
|
|
524
|
+
function extractIgnorePatternsFromRules(rules) {
|
|
525
|
+
const patterns = [];
|
|
526
|
+
for (const rule of rules) {
|
|
527
|
+
if (rule.frontmatter.globs && rule.frontmatter.globs.length > 0) {
|
|
528
|
+
for (const glob of rule.frontmatter.globs) {
|
|
529
|
+
if (shouldExcludeFromAI(glob)) {
|
|
530
|
+
patterns.push(`# Exclude: ${rule.frontmatter.description}`);
|
|
531
|
+
patterns.push(glob);
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
const contentPatterns = extractIgnorePatternsFromContent(rule.content);
|
|
536
|
+
patterns.push(...contentPatterns);
|
|
537
|
+
}
|
|
538
|
+
return patterns;
|
|
539
|
+
}
|
|
540
|
+
function shouldExcludeFromAI(glob) {
|
|
541
|
+
const excludePatterns = [
|
|
542
|
+
// Test and fixture files that might be large or confusing
|
|
543
|
+
"**/test/fixtures/**",
|
|
544
|
+
"**/tests/fixtures/**",
|
|
545
|
+
"**/*.fixture.*",
|
|
546
|
+
// Build and generated files
|
|
547
|
+
"**/dist/**",
|
|
548
|
+
"**/build/**",
|
|
549
|
+
"**/coverage/**",
|
|
550
|
+
// Configuration that might contain sensitive data
|
|
551
|
+
"**/config/production/**",
|
|
552
|
+
"**/config/prod/**",
|
|
553
|
+
"**/*.prod.*",
|
|
554
|
+
// Documentation that might be sensitive
|
|
555
|
+
"**/internal/**",
|
|
556
|
+
"**/private/**",
|
|
557
|
+
"**/confidential/**"
|
|
558
|
+
];
|
|
559
|
+
return excludePatterns.some((pattern) => {
|
|
560
|
+
const regex = new RegExp(pattern.replace(/\*\*/g, ".*").replace(/\*/g, "[^/]*"));
|
|
561
|
+
return regex.test(glob);
|
|
562
|
+
});
|
|
563
|
+
}
|
|
564
|
+
function extractIgnorePatternsFromContent(content) {
|
|
565
|
+
const patterns = [];
|
|
566
|
+
const lines = content.split("\n");
|
|
567
|
+
for (const line of lines) {
|
|
568
|
+
const trimmed = line.trim();
|
|
569
|
+
if (trimmed.startsWith("# IGNORE:") || trimmed.startsWith("# aiignore:")) {
|
|
570
|
+
const pattern = trimmed.replace(/^# (IGNORE|aiignore):\s*/, "").trim();
|
|
571
|
+
if (pattern) {
|
|
572
|
+
patterns.push(pattern);
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
if (trimmed.includes("exclude") || trimmed.includes("ignore")) {
|
|
576
|
+
const matches = trimmed.match(/['"`]([^'"`]+\.(log|tmp|cache|temp))['"`]/g);
|
|
577
|
+
if (matches) {
|
|
578
|
+
patterns.push(...matches.map((m) => m.replace(/['"`]/g, "")));
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
return patterns;
|
|
583
|
+
}
|
|
584
|
+
|
|
417
585
|
// src/generators/rules/claudecode.ts
|
|
418
586
|
var import_node_path4 = require("path");
|
|
419
587
|
|
|
420
588
|
// src/types/claudecode.ts
|
|
421
|
-
var
|
|
422
|
-
var ClaudeSettingsSchema =
|
|
423
|
-
permissions:
|
|
424
|
-
|
|
425
|
-
deny:
|
|
589
|
+
var import_mini2 = require("zod/mini");
|
|
590
|
+
var ClaudeSettingsSchema = import_mini2.z.looseObject({
|
|
591
|
+
permissions: import_mini2.z._default(
|
|
592
|
+
import_mini2.z.looseObject({
|
|
593
|
+
deny: import_mini2.z._default(import_mini2.z.array(import_mini2.z.string()), [])
|
|
426
594
|
}),
|
|
427
595
|
{ deny: [] }
|
|
428
596
|
)
|
|
429
597
|
});
|
|
430
598
|
|
|
431
599
|
// src/utils/file.ts
|
|
432
|
-
var import_promises3 = require("fs/promises");
|
|
433
|
-
var import_node_path3 = require("path");
|
|
434
|
-
|
|
435
|
-
// src/utils/file-ops.ts
|
|
436
600
|
var import_promises2 = require("fs/promises");
|
|
437
|
-
var
|
|
601
|
+
var import_node_path2 = require("path");
|
|
438
602
|
async function ensureDir(dirPath) {
|
|
439
603
|
try {
|
|
440
604
|
await (0, import_promises2.stat)(dirPath);
|
|
@@ -446,7 +610,7 @@ async function readFileContent(filepath) {
|
|
|
446
610
|
return (0, import_promises2.readFile)(filepath, "utf-8");
|
|
447
611
|
}
|
|
448
612
|
async function writeFileContent(filepath, content) {
|
|
449
|
-
await ensureDir((0,
|
|
613
|
+
await ensureDir((0, import_node_path2.dirname)(filepath));
|
|
450
614
|
await (0, import_promises2.writeFile)(filepath, content, "utf-8");
|
|
451
615
|
}
|
|
452
616
|
async function fileExists(filepath) {
|
|
@@ -457,16 +621,57 @@ async function fileExists(filepath) {
|
|
|
457
621
|
return false;
|
|
458
622
|
}
|
|
459
623
|
}
|
|
624
|
+
async function findFiles(dir, extension = ".md") {
|
|
625
|
+
try {
|
|
626
|
+
const files = await (0, import_promises2.readdir)(dir);
|
|
627
|
+
return files.filter((file) => file.endsWith(extension)).map((file) => (0, import_node_path2.join)(dir, file));
|
|
628
|
+
} catch {
|
|
629
|
+
return [];
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
async function removeDirectory(dirPath) {
|
|
633
|
+
const dangerousPaths = [".", "/", "~", "src", "node_modules"];
|
|
634
|
+
if (dangerousPaths.includes(dirPath) || dirPath === "") {
|
|
635
|
+
console.warn(`Skipping deletion of dangerous path: ${dirPath}`);
|
|
636
|
+
return;
|
|
637
|
+
}
|
|
638
|
+
try {
|
|
639
|
+
if (await fileExists(dirPath)) {
|
|
640
|
+
await (0, import_promises2.rm)(dirPath, { recursive: true, force: true });
|
|
641
|
+
}
|
|
642
|
+
} catch (error) {
|
|
643
|
+
console.warn(`Failed to remove directory ${dirPath}:`, error);
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
async function removeFile(filepath) {
|
|
647
|
+
try {
|
|
648
|
+
if (await fileExists(filepath)) {
|
|
649
|
+
await (0, import_promises2.rm)(filepath);
|
|
650
|
+
}
|
|
651
|
+
} catch (error) {
|
|
652
|
+
console.warn(`Failed to remove file ${filepath}:`, error);
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
async function removeClaudeGeneratedFiles() {
|
|
656
|
+
const filesToRemove = ["CLAUDE.md", ".claude/memories"];
|
|
657
|
+
for (const fileOrDir of filesToRemove) {
|
|
658
|
+
if (fileOrDir.endsWith("/memories")) {
|
|
659
|
+
await removeDirectory(fileOrDir);
|
|
660
|
+
} else {
|
|
661
|
+
await removeFile(fileOrDir);
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
}
|
|
460
665
|
|
|
461
666
|
// src/utils/ignore.ts
|
|
462
|
-
var
|
|
667
|
+
var import_node_path3 = require("path");
|
|
463
668
|
var import_micromatch = __toESM(require("micromatch"), 1);
|
|
464
669
|
var cachedIgnorePatterns = null;
|
|
465
670
|
async function loadIgnorePatterns(baseDir = process.cwd()) {
|
|
466
671
|
if (cachedIgnorePatterns) {
|
|
467
672
|
return cachedIgnorePatterns;
|
|
468
673
|
}
|
|
469
|
-
const ignorePath = (0,
|
|
674
|
+
const ignorePath = (0, import_node_path3.join)(baseDir, ".rulesyncignore");
|
|
470
675
|
if (!await fileExists(ignorePath)) {
|
|
471
676
|
cachedIgnorePatterns = { patterns: [] };
|
|
472
677
|
return cachedIgnorePatterns;
|
|
@@ -509,53 +714,6 @@ function filterIgnoredFiles(files, ignorePatterns) {
|
|
|
509
714
|
return files.filter((file) => !isFileIgnored(file, ignorePatterns));
|
|
510
715
|
}
|
|
511
716
|
|
|
512
|
-
// src/utils/file.ts
|
|
513
|
-
async function findFiles(dir, extension = ".md", ignorePatterns) {
|
|
514
|
-
try {
|
|
515
|
-
const files = await (0, import_promises3.readdir)(dir);
|
|
516
|
-
const filtered = files.filter((file) => file.endsWith(extension)).map((file) => (0, import_node_path3.join)(dir, file));
|
|
517
|
-
if (ignorePatterns && ignorePatterns.length > 0) {
|
|
518
|
-
return filterIgnoredFiles(filtered, ignorePatterns);
|
|
519
|
-
}
|
|
520
|
-
return filtered;
|
|
521
|
-
} catch {
|
|
522
|
-
return [];
|
|
523
|
-
}
|
|
524
|
-
}
|
|
525
|
-
async function removeDirectory(dirPath) {
|
|
526
|
-
const dangerousPaths = [".", "/", "~", "src", "node_modules"];
|
|
527
|
-
if (dangerousPaths.includes(dirPath) || dirPath === "") {
|
|
528
|
-
console.warn(`Skipping deletion of dangerous path: ${dirPath}`);
|
|
529
|
-
return;
|
|
530
|
-
}
|
|
531
|
-
try {
|
|
532
|
-
if (await fileExists(dirPath)) {
|
|
533
|
-
await (0, import_promises3.rm)(dirPath, { recursive: true, force: true });
|
|
534
|
-
}
|
|
535
|
-
} catch (error) {
|
|
536
|
-
console.warn(`Failed to remove directory ${dirPath}:`, error);
|
|
537
|
-
}
|
|
538
|
-
}
|
|
539
|
-
async function removeFile(filepath) {
|
|
540
|
-
try {
|
|
541
|
-
if (await fileExists(filepath)) {
|
|
542
|
-
await (0, import_promises3.rm)(filepath);
|
|
543
|
-
}
|
|
544
|
-
} catch (error) {
|
|
545
|
-
console.warn(`Failed to remove file ${filepath}:`, error);
|
|
546
|
-
}
|
|
547
|
-
}
|
|
548
|
-
async function removeClaudeGeneratedFiles() {
|
|
549
|
-
const filesToRemove = ["CLAUDE.md", ".claude/memories"];
|
|
550
|
-
for (const fileOrDir of filesToRemove) {
|
|
551
|
-
if (fileOrDir.endsWith("/memories")) {
|
|
552
|
-
await removeDirectory(fileOrDir);
|
|
553
|
-
} else {
|
|
554
|
-
await removeFile(fileOrDir);
|
|
555
|
-
}
|
|
556
|
-
}
|
|
557
|
-
}
|
|
558
|
-
|
|
559
717
|
// src/generators/rules/claudecode.ts
|
|
560
718
|
async function generateClaudecodeConfig(rules, config, baseDir) {
|
|
561
719
|
const outputs = [];
|
|
@@ -589,9 +747,10 @@ function generateClaudeMarkdown(rootRules, detailRules) {
|
|
|
589
747
|
lines.push("Please also reference the following documents as needed:");
|
|
590
748
|
lines.push("");
|
|
591
749
|
for (const rule of detailRules) {
|
|
592
|
-
const
|
|
750
|
+
const escapedDescription = rule.frontmatter.description.replace(/"/g, '\\"');
|
|
751
|
+
const globsText = rule.frontmatter.globs.join(",");
|
|
593
752
|
lines.push(
|
|
594
|
-
`@.claude/memories/${rule.filename}.md ${
|
|
753
|
+
`@.claude/memories/${rule.filename}.md description: "${escapedDescription}" globs: "${globsText}"`
|
|
595
754
|
);
|
|
596
755
|
}
|
|
597
756
|
lines.push("");
|
|
@@ -891,14 +1050,34 @@ function generateAiexclude(patterns) {
|
|
|
891
1050
|
return lines.join("\n");
|
|
892
1051
|
}
|
|
893
1052
|
|
|
894
|
-
// src/generators/rules/
|
|
1053
|
+
// src/generators/rules/kiro.ts
|
|
895
1054
|
var import_node_path9 = require("path");
|
|
1055
|
+
async function generateKiroConfig(rules, config, baseDir) {
|
|
1056
|
+
const outputs = [];
|
|
1057
|
+
for (const rule of rules) {
|
|
1058
|
+
const content = generateKiroMarkdown(rule);
|
|
1059
|
+
const outputDir = baseDir ? (0, import_node_path9.join)(baseDir, config.outputPaths.kiro) : config.outputPaths.kiro;
|
|
1060
|
+
const filepath = (0, import_node_path9.join)(outputDir, `${rule.filename}.md`);
|
|
1061
|
+
outputs.push({
|
|
1062
|
+
tool: "kiro",
|
|
1063
|
+
filepath,
|
|
1064
|
+
content
|
|
1065
|
+
});
|
|
1066
|
+
}
|
|
1067
|
+
return outputs;
|
|
1068
|
+
}
|
|
1069
|
+
function generateKiroMarkdown(rule) {
|
|
1070
|
+
return rule.content.trim();
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
// src/generators/rules/roo.ts
|
|
1074
|
+
var import_node_path10 = require("path");
|
|
896
1075
|
async function generateRooConfig(rules, config, baseDir) {
|
|
897
1076
|
const outputs = [];
|
|
898
1077
|
for (const rule of rules) {
|
|
899
1078
|
const content = generateRooMarkdown(rule);
|
|
900
|
-
const outputDir = baseDir ? (0,
|
|
901
|
-
const filepath = (0,
|
|
1079
|
+
const outputDir = baseDir ? (0, import_node_path10.join)(baseDir, config.outputPaths.roo) : config.outputPaths.roo;
|
|
1080
|
+
const filepath = (0, import_node_path10.join)(outputDir, `${rule.filename}.md`);
|
|
902
1081
|
outputs.push({
|
|
903
1082
|
tool: "roo",
|
|
904
1083
|
filepath,
|
|
@@ -907,7 +1086,7 @@ async function generateRooConfig(rules, config, baseDir) {
|
|
|
907
1086
|
}
|
|
908
1087
|
const ignorePatterns = await loadIgnorePatterns(baseDir);
|
|
909
1088
|
if (ignorePatterns.patterns.length > 0) {
|
|
910
|
-
const rooIgnorePath = baseDir ? (0,
|
|
1089
|
+
const rooIgnorePath = baseDir ? (0, import_node_path10.join)(baseDir, ".rooignore") : ".rooignore";
|
|
911
1090
|
const rooIgnoreContent = generateRooIgnore(ignorePatterns.patterns);
|
|
912
1091
|
outputs.push({
|
|
913
1092
|
tool: "roo",
|
|
@@ -973,6 +1152,11 @@ async function generateForTool(tool, rules, config, baseDir) {
|
|
|
973
1152
|
return generateRooConfig(rules, config, baseDir);
|
|
974
1153
|
case "geminicli":
|
|
975
1154
|
return generateGeminiConfig(rules, config, baseDir);
|
|
1155
|
+
case "kiro": {
|
|
1156
|
+
const kiroRulesOutputs = await generateKiroConfig(rules, config, baseDir);
|
|
1157
|
+
const kiroIgnoreOutputs = await generateKiroIgnoreFiles(rules, config, baseDir);
|
|
1158
|
+
return [...kiroRulesOutputs, ...kiroIgnoreOutputs];
|
|
1159
|
+
}
|
|
976
1160
|
default:
|
|
977
1161
|
console.warn(`Unknown tool: ${tool}`);
|
|
978
1162
|
return null;
|
|
@@ -980,74 +1164,76 @@ async function generateForTool(tool, rules, config, baseDir) {
|
|
|
980
1164
|
}
|
|
981
1165
|
|
|
982
1166
|
// src/core/parser.ts
|
|
983
|
-
var
|
|
1167
|
+
var import_node_path11 = require("path");
|
|
984
1168
|
var import_gray_matter = __toESM(require("gray-matter"), 1);
|
|
985
1169
|
|
|
986
1170
|
// src/types/config.ts
|
|
987
|
-
var
|
|
1171
|
+
var import_mini3 = require("zod/mini");
|
|
988
1172
|
init_tool_targets();
|
|
989
|
-
var ConfigSchema =
|
|
990
|
-
aiRulesDir:
|
|
991
|
-
outputPaths:
|
|
992
|
-
watchEnabled:
|
|
1173
|
+
var ConfigSchema = import_mini3.z.object({
|
|
1174
|
+
aiRulesDir: import_mini3.z.string(),
|
|
1175
|
+
outputPaths: import_mini3.z.record(ToolTargetSchema, import_mini3.z.string()),
|
|
1176
|
+
watchEnabled: import_mini3.z.boolean(),
|
|
993
1177
|
defaultTargets: ToolTargetsSchema
|
|
994
1178
|
});
|
|
995
1179
|
|
|
996
1180
|
// src/types/mcp.ts
|
|
997
|
-
var
|
|
1181
|
+
var import_mini4 = require("zod/mini");
|
|
998
1182
|
init_tool_targets();
|
|
999
|
-
var McpTransportTypeSchema =
|
|
1000
|
-
var McpServerBaseSchema =
|
|
1001
|
-
command:
|
|
1002
|
-
args:
|
|
1003
|
-
url:
|
|
1004
|
-
httpUrl:
|
|
1005
|
-
env:
|
|
1006
|
-
disabled:
|
|
1007
|
-
networkTimeout:
|
|
1008
|
-
timeout:
|
|
1009
|
-
trust:
|
|
1010
|
-
cwd:
|
|
1011
|
-
transport:
|
|
1012
|
-
type:
|
|
1013
|
-
alwaysAllow:
|
|
1014
|
-
tools:
|
|
1183
|
+
var McpTransportTypeSchema = import_mini4.z.enum(["stdio", "sse", "http"]);
|
|
1184
|
+
var McpServerBaseSchema = import_mini4.z.object({
|
|
1185
|
+
command: import_mini4.z.optional(import_mini4.z.string()),
|
|
1186
|
+
args: import_mini4.z.optional(import_mini4.z.array(import_mini4.z.string())),
|
|
1187
|
+
url: import_mini4.z.optional(import_mini4.z.string()),
|
|
1188
|
+
httpUrl: import_mini4.z.optional(import_mini4.z.string()),
|
|
1189
|
+
env: import_mini4.z.optional(import_mini4.z.record(import_mini4.z.string(), import_mini4.z.string())),
|
|
1190
|
+
disabled: import_mini4.z.optional(import_mini4.z.boolean()),
|
|
1191
|
+
networkTimeout: import_mini4.z.optional(import_mini4.z.number()),
|
|
1192
|
+
timeout: import_mini4.z.optional(import_mini4.z.number()),
|
|
1193
|
+
trust: import_mini4.z.optional(import_mini4.z.boolean()),
|
|
1194
|
+
cwd: import_mini4.z.optional(import_mini4.z.string()),
|
|
1195
|
+
transport: import_mini4.z.optional(McpTransportTypeSchema),
|
|
1196
|
+
type: import_mini4.z.optional(import_mini4.z.enum(["sse", "streamable-http"])),
|
|
1197
|
+
alwaysAllow: import_mini4.z.optional(import_mini4.z.array(import_mini4.z.string())),
|
|
1198
|
+
tools: import_mini4.z.optional(import_mini4.z.array(import_mini4.z.string())),
|
|
1199
|
+
kiroAutoApprove: import_mini4.z.optional(import_mini4.z.array(import_mini4.z.string())),
|
|
1200
|
+
kiroAutoBlock: import_mini4.z.optional(import_mini4.z.array(import_mini4.z.string()))
|
|
1015
1201
|
});
|
|
1016
|
-
var RulesyncMcpServerSchema =
|
|
1017
|
-
targets:
|
|
1202
|
+
var RulesyncMcpServerSchema = import_mini4.z.extend(McpServerBaseSchema, {
|
|
1203
|
+
targets: import_mini4.z.optional(RulesyncTargetsSchema)
|
|
1018
1204
|
});
|
|
1019
|
-
var McpConfigSchema =
|
|
1020
|
-
mcpServers:
|
|
1205
|
+
var McpConfigSchema = import_mini4.z.object({
|
|
1206
|
+
mcpServers: import_mini4.z.record(import_mini4.z.string(), McpServerBaseSchema)
|
|
1021
1207
|
});
|
|
1022
|
-
var RulesyncMcpConfigSchema =
|
|
1023
|
-
mcpServers:
|
|
1208
|
+
var RulesyncMcpConfigSchema = import_mini4.z.object({
|
|
1209
|
+
mcpServers: import_mini4.z.record(import_mini4.z.string(), RulesyncMcpServerSchema)
|
|
1024
1210
|
});
|
|
1025
1211
|
|
|
1026
1212
|
// src/types/rules.ts
|
|
1027
|
-
var
|
|
1213
|
+
var import_mini5 = require("zod/mini");
|
|
1028
1214
|
init_tool_targets();
|
|
1029
|
-
var RuleFrontmatterSchema =
|
|
1030
|
-
root:
|
|
1215
|
+
var RuleFrontmatterSchema = import_mini5.z.object({
|
|
1216
|
+
root: import_mini5.z.boolean(),
|
|
1031
1217
|
targets: RulesyncTargetsSchema,
|
|
1032
|
-
description:
|
|
1033
|
-
globs:
|
|
1034
|
-
cursorRuleType:
|
|
1218
|
+
description: import_mini5.z.string(),
|
|
1219
|
+
globs: import_mini5.z.array(import_mini5.z.string()),
|
|
1220
|
+
cursorRuleType: import_mini5.z.optional(import_mini5.z.enum(["always", "manual", "specificFiles", "intelligently"]))
|
|
1035
1221
|
});
|
|
1036
|
-
var ParsedRuleSchema =
|
|
1222
|
+
var ParsedRuleSchema = import_mini5.z.object({
|
|
1037
1223
|
frontmatter: RuleFrontmatterSchema,
|
|
1038
|
-
content:
|
|
1039
|
-
filename:
|
|
1040
|
-
filepath:
|
|
1224
|
+
content: import_mini5.z.string(),
|
|
1225
|
+
filename: import_mini5.z.string(),
|
|
1226
|
+
filepath: import_mini5.z.string()
|
|
1041
1227
|
});
|
|
1042
|
-
var GeneratedOutputSchema =
|
|
1228
|
+
var GeneratedOutputSchema = import_mini5.z.object({
|
|
1043
1229
|
tool: ToolTargetSchema,
|
|
1044
|
-
filepath:
|
|
1045
|
-
content:
|
|
1230
|
+
filepath: import_mini5.z.string(),
|
|
1231
|
+
content: import_mini5.z.string()
|
|
1046
1232
|
});
|
|
1047
|
-
var GenerateOptionsSchema =
|
|
1048
|
-
targetTools:
|
|
1049
|
-
outputDir:
|
|
1050
|
-
watch:
|
|
1233
|
+
var GenerateOptionsSchema = import_mini5.z.object({
|
|
1234
|
+
targetTools: import_mini5.z.optional(ToolTargetsSchema),
|
|
1235
|
+
outputDir: import_mini5.z.optional(import_mini5.z.string()),
|
|
1236
|
+
watch: import_mini5.z.optional(import_mini5.z.boolean())
|
|
1051
1237
|
});
|
|
1052
1238
|
|
|
1053
1239
|
// src/types/index.ts
|
|
@@ -1056,7 +1242,8 @@ init_tool_targets();
|
|
|
1056
1242
|
// src/core/parser.ts
|
|
1057
1243
|
async function parseRulesFromDirectory(aiRulesDir) {
|
|
1058
1244
|
const ignorePatterns = await loadIgnorePatterns();
|
|
1059
|
-
const
|
|
1245
|
+
const allRuleFiles = await findFiles(aiRulesDir, ".md");
|
|
1246
|
+
const ruleFiles = filterIgnoredFiles(allRuleFiles, ignorePatterns.patterns);
|
|
1060
1247
|
const rules = [];
|
|
1061
1248
|
const errors = [];
|
|
1062
1249
|
if (ignorePatterns.patterns.length > 0) {
|
|
@@ -1089,7 +1276,7 @@ async function parseRuleFile(filepath) {
|
|
|
1089
1276
|
const parsed = (0, import_gray_matter.default)(content);
|
|
1090
1277
|
try {
|
|
1091
1278
|
const frontmatter = RuleFrontmatterSchema.parse(parsed.data);
|
|
1092
|
-
const filename = (0,
|
|
1279
|
+
const filename = (0, import_node_path11.basename)(filepath, ".md");
|
|
1093
1280
|
return {
|
|
1094
1281
|
frontmatter,
|
|
1095
1282
|
content: parsed.content,
|
|
@@ -1162,6 +1349,7 @@ init_cline();
|
|
|
1162
1349
|
init_copilot();
|
|
1163
1350
|
init_cursor();
|
|
1164
1351
|
init_geminicli();
|
|
1352
|
+
init_kiro();
|
|
1165
1353
|
init_roo();
|
|
1166
1354
|
|
|
1167
1355
|
// src/core/mcp-parser.ts
|
|
@@ -1225,6 +1413,11 @@ async function generateMcpConfigs(projectRoot, baseDir) {
|
|
|
1225
1413
|
path: path3.join(targetRoot, ".gemini", "settings.json"),
|
|
1226
1414
|
generate: () => generateGeminiCliMcp(config)
|
|
1227
1415
|
},
|
|
1416
|
+
{
|
|
1417
|
+
tool: "kiro-project",
|
|
1418
|
+
path: path3.join(targetRoot, ".kiro", "mcp.json"),
|
|
1419
|
+
generate: () => generateKiroMcp(config)
|
|
1420
|
+
},
|
|
1228
1421
|
{
|
|
1229
1422
|
tool: "roo-project",
|
|
1230
1423
|
path: path3.join(targetRoot, ".roo", "mcp.json"),
|
|
@@ -1235,7 +1428,7 @@ async function generateMcpConfigs(projectRoot, baseDir) {
|
|
|
1235
1428
|
try {
|
|
1236
1429
|
const content = generator.generate();
|
|
1237
1430
|
const parsed = JSON.parse(content);
|
|
1238
|
-
if (generator.tool.includes("claude") || generator.tool.includes("cline") || generator.tool.includes("cursor") || generator.tool.includes("gemini") || generator.tool.includes("roo")) {
|
|
1431
|
+
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")) {
|
|
1239
1432
|
if (!parsed.mcpServers || Object.keys(parsed.mcpServers).length === 0) {
|
|
1240
1433
|
results.push({
|
|
1241
1434
|
tool: generator.tool,
|
|
@@ -1321,6 +1514,9 @@ async function generateCommand(options = {}) {
|
|
|
1321
1514
|
case "geminicli":
|
|
1322
1515
|
deleteTasks.push(removeDirectory(config.outputPaths.geminicli));
|
|
1323
1516
|
break;
|
|
1517
|
+
case "kiro":
|
|
1518
|
+
deleteTasks.push(removeDirectory(config.outputPaths.kiro));
|
|
1519
|
+
break;
|
|
1324
1520
|
}
|
|
1325
1521
|
}
|
|
1326
1522
|
await Promise.all(deleteTasks);
|
|
@@ -1385,9 +1581,9 @@ Generating configurations for base directory: ${baseDir}`);
|
|
|
1385
1581
|
|
|
1386
1582
|
// src/cli/commands/gitignore.ts
|
|
1387
1583
|
var import_node_fs = require("fs");
|
|
1388
|
-
var
|
|
1584
|
+
var import_node_path12 = require("path");
|
|
1389
1585
|
var gitignoreCommand = async () => {
|
|
1390
|
-
const gitignorePath = (0,
|
|
1586
|
+
const gitignorePath = (0, import_node_path12.join)(process.cwd(), ".gitignore");
|
|
1391
1587
|
const rulesFilesToIgnore = [
|
|
1392
1588
|
"# Generated by rulesync - AI tool configuration files",
|
|
1393
1589
|
"**/.github/copilot-instructions.md",
|
|
@@ -1404,6 +1600,8 @@ var gitignoreCommand = async () => {
|
|
|
1404
1600
|
"**/GEMINI.md",
|
|
1405
1601
|
"**/.gemini/memories/",
|
|
1406
1602
|
"**/.aiexclude",
|
|
1603
|
+
"**/.aiignore",
|
|
1604
|
+
"**/.kiro/steering/",
|
|
1407
1605
|
"**/.mcp.json",
|
|
1408
1606
|
"!.rulesync/.mcp.json",
|
|
1409
1607
|
"**/.cursor/mcp.json",
|
|
@@ -1441,17 +1639,17 @@ ${linesToAdd.join("\n")}
|
|
|
1441
1639
|
};
|
|
1442
1640
|
|
|
1443
1641
|
// src/core/importer.ts
|
|
1444
|
-
var
|
|
1642
|
+
var import_node_path19 = require("path");
|
|
1445
1643
|
var import_gray_matter4 = __toESM(require("gray-matter"), 1);
|
|
1446
1644
|
|
|
1447
1645
|
// src/parsers/claudecode.ts
|
|
1448
|
-
var
|
|
1646
|
+
var import_node_path13 = require("path");
|
|
1449
1647
|
async function parseClaudeConfiguration(baseDir = process.cwd()) {
|
|
1450
1648
|
const errors = [];
|
|
1451
1649
|
const rules = [];
|
|
1452
1650
|
let ignorePatterns;
|
|
1453
1651
|
let mcpServers;
|
|
1454
|
-
const claudeFilePath = (0,
|
|
1652
|
+
const claudeFilePath = (0, import_node_path13.join)(baseDir, "CLAUDE.md");
|
|
1455
1653
|
if (!await fileExists(claudeFilePath)) {
|
|
1456
1654
|
errors.push("CLAUDE.md file not found");
|
|
1457
1655
|
return { rules, errors };
|
|
@@ -1462,12 +1660,12 @@ async function parseClaudeConfiguration(baseDir = process.cwd()) {
|
|
|
1462
1660
|
if (mainRule) {
|
|
1463
1661
|
rules.push(mainRule);
|
|
1464
1662
|
}
|
|
1465
|
-
const memoryDir = (0,
|
|
1663
|
+
const memoryDir = (0, import_node_path13.join)(baseDir, ".claude", "memories");
|
|
1466
1664
|
if (await fileExists(memoryDir)) {
|
|
1467
1665
|
const memoryRules = await parseClaudeMemoryFiles(memoryDir);
|
|
1468
1666
|
rules.push(...memoryRules);
|
|
1469
1667
|
}
|
|
1470
|
-
const settingsPath = (0,
|
|
1668
|
+
const settingsPath = (0, import_node_path13.join)(baseDir, ".claude", "settings.json");
|
|
1471
1669
|
if (await fileExists(settingsPath)) {
|
|
1472
1670
|
const settingsResult = await parseClaudeSettings(settingsPath);
|
|
1473
1671
|
if (settingsResult.ignorePatterns) {
|
|
@@ -1524,10 +1722,10 @@ async function parseClaudeMemoryFiles(memoryDir) {
|
|
|
1524
1722
|
const files = await readdir2(memoryDir);
|
|
1525
1723
|
for (const file of files) {
|
|
1526
1724
|
if (file.endsWith(".md")) {
|
|
1527
|
-
const filePath = (0,
|
|
1725
|
+
const filePath = (0, import_node_path13.join)(memoryDir, file);
|
|
1528
1726
|
const content = await readFileContent(filePath);
|
|
1529
1727
|
if (content.trim()) {
|
|
1530
|
-
const filename = (0,
|
|
1728
|
+
const filename = (0, import_node_path13.basename)(file, ".md");
|
|
1531
1729
|
const frontmatter = {
|
|
1532
1730
|
root: false,
|
|
1533
1731
|
targets: ["claudecode"],
|
|
@@ -1587,11 +1785,11 @@ async function parseClaudeSettings(settingsPath) {
|
|
|
1587
1785
|
}
|
|
1588
1786
|
|
|
1589
1787
|
// src/parsers/cline.ts
|
|
1590
|
-
var
|
|
1788
|
+
var import_node_path14 = require("path");
|
|
1591
1789
|
async function parseClineConfiguration(baseDir = process.cwd()) {
|
|
1592
1790
|
const errors = [];
|
|
1593
1791
|
const rules = [];
|
|
1594
|
-
const clineFilePath = (0,
|
|
1792
|
+
const clineFilePath = (0, import_node_path14.join)(baseDir, ".cline", "instructions.md");
|
|
1595
1793
|
if (await fileExists(clineFilePath)) {
|
|
1596
1794
|
try {
|
|
1597
1795
|
const content = await readFileContent(clineFilePath);
|
|
@@ -1614,14 +1812,14 @@ async function parseClineConfiguration(baseDir = process.cwd()) {
|
|
|
1614
1812
|
errors.push(`Failed to parse .cline/instructions.md: ${errorMessage}`);
|
|
1615
1813
|
}
|
|
1616
1814
|
}
|
|
1617
|
-
const clinerulesDirPath = (0,
|
|
1815
|
+
const clinerulesDirPath = (0, import_node_path14.join)(baseDir, ".clinerules");
|
|
1618
1816
|
if (await fileExists(clinerulesDirPath)) {
|
|
1619
1817
|
try {
|
|
1620
1818
|
const { readdir: readdir2 } = await import("fs/promises");
|
|
1621
1819
|
const files = await readdir2(clinerulesDirPath);
|
|
1622
1820
|
for (const file of files) {
|
|
1623
1821
|
if (file.endsWith(".md")) {
|
|
1624
|
-
const filePath = (0,
|
|
1822
|
+
const filePath = (0, import_node_path14.join)(clinerulesDirPath, file);
|
|
1625
1823
|
try {
|
|
1626
1824
|
const content = await readFileContent(filePath);
|
|
1627
1825
|
if (content.trim()) {
|
|
@@ -1657,12 +1855,12 @@ async function parseClineConfiguration(baseDir = process.cwd()) {
|
|
|
1657
1855
|
}
|
|
1658
1856
|
|
|
1659
1857
|
// src/parsers/copilot.ts
|
|
1660
|
-
var
|
|
1858
|
+
var import_node_path15 = require("path");
|
|
1661
1859
|
var import_gray_matter2 = __toESM(require("gray-matter"), 1);
|
|
1662
1860
|
async function parseCopilotConfiguration(baseDir = process.cwd()) {
|
|
1663
1861
|
const errors = [];
|
|
1664
1862
|
const rules = [];
|
|
1665
|
-
const copilotFilePath = (0,
|
|
1863
|
+
const copilotFilePath = (0, import_node_path15.join)(baseDir, ".github", "copilot-instructions.md");
|
|
1666
1864
|
if (await fileExists(copilotFilePath)) {
|
|
1667
1865
|
try {
|
|
1668
1866
|
const rawContent = await readFileContent(copilotFilePath);
|
|
@@ -1687,19 +1885,19 @@ async function parseCopilotConfiguration(baseDir = process.cwd()) {
|
|
|
1687
1885
|
errors.push(`Failed to parse copilot-instructions.md: ${errorMessage}`);
|
|
1688
1886
|
}
|
|
1689
1887
|
}
|
|
1690
|
-
const instructionsDir = (0,
|
|
1888
|
+
const instructionsDir = (0, import_node_path15.join)(baseDir, ".github", "instructions");
|
|
1691
1889
|
if (await fileExists(instructionsDir)) {
|
|
1692
1890
|
try {
|
|
1693
1891
|
const { readdir: readdir2 } = await import("fs/promises");
|
|
1694
1892
|
const files = await readdir2(instructionsDir);
|
|
1695
1893
|
for (const file of files) {
|
|
1696
1894
|
if (file.endsWith(".instructions.md")) {
|
|
1697
|
-
const filePath = (0,
|
|
1895
|
+
const filePath = (0, import_node_path15.join)(instructionsDir, file);
|
|
1698
1896
|
const rawContent = await readFileContent(filePath);
|
|
1699
1897
|
const parsed = (0, import_gray_matter2.default)(rawContent);
|
|
1700
1898
|
const content = parsed.content.trim();
|
|
1701
1899
|
if (content) {
|
|
1702
|
-
const filename = (0,
|
|
1900
|
+
const filename = (0, import_node_path15.basename)(file, ".instructions.md");
|
|
1703
1901
|
const frontmatter = {
|
|
1704
1902
|
root: false,
|
|
1705
1903
|
targets: ["copilot"],
|
|
@@ -1729,10 +1927,10 @@ async function parseCopilotConfiguration(baseDir = process.cwd()) {
|
|
|
1729
1927
|
}
|
|
1730
1928
|
|
|
1731
1929
|
// src/parsers/cursor.ts
|
|
1732
|
-
var
|
|
1930
|
+
var import_node_path16 = require("path");
|
|
1733
1931
|
var import_gray_matter3 = __toESM(require("gray-matter"), 1);
|
|
1734
1932
|
var import_js_yaml = require("js-yaml");
|
|
1735
|
-
var
|
|
1933
|
+
var import_mini6 = require("zod/mini");
|
|
1736
1934
|
var customMatterOptions = {
|
|
1737
1935
|
engines: {
|
|
1738
1936
|
yaml: {
|
|
@@ -1760,7 +1958,7 @@ var customMatterOptions = {
|
|
|
1760
1958
|
}
|
|
1761
1959
|
};
|
|
1762
1960
|
function convertCursorMdcFrontmatter(cursorFrontmatter, _filename) {
|
|
1763
|
-
const FrontmatterSchema =
|
|
1961
|
+
const FrontmatterSchema = import_mini6.z.record(import_mini6.z.string(), import_mini6.z.unknown());
|
|
1764
1962
|
const parseResult = FrontmatterSchema.safeParse(cursorFrontmatter);
|
|
1765
1963
|
if (!parseResult.success) {
|
|
1766
1964
|
return {
|
|
@@ -1854,7 +2052,7 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
|
|
|
1854
2052
|
const rules = [];
|
|
1855
2053
|
let ignorePatterns;
|
|
1856
2054
|
let mcpServers;
|
|
1857
|
-
const cursorFilePath = (0,
|
|
2055
|
+
const cursorFilePath = (0, import_node_path16.join)(baseDir, ".cursorrules");
|
|
1858
2056
|
if (await fileExists(cursorFilePath)) {
|
|
1859
2057
|
try {
|
|
1860
2058
|
const rawContent = await readFileContent(cursorFilePath);
|
|
@@ -1875,20 +2073,20 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
|
|
|
1875
2073
|
errors.push(`Failed to parse .cursorrules file: ${errorMessage}`);
|
|
1876
2074
|
}
|
|
1877
2075
|
}
|
|
1878
|
-
const cursorRulesDir = (0,
|
|
2076
|
+
const cursorRulesDir = (0, import_node_path16.join)(baseDir, ".cursor", "rules");
|
|
1879
2077
|
if (await fileExists(cursorRulesDir)) {
|
|
1880
2078
|
try {
|
|
1881
2079
|
const { readdir: readdir2 } = await import("fs/promises");
|
|
1882
2080
|
const files = await readdir2(cursorRulesDir);
|
|
1883
2081
|
for (const file of files) {
|
|
1884
2082
|
if (file.endsWith(".mdc")) {
|
|
1885
|
-
const filePath = (0,
|
|
2083
|
+
const filePath = (0, import_node_path16.join)(cursorRulesDir, file);
|
|
1886
2084
|
try {
|
|
1887
2085
|
const rawContent = await readFileContent(filePath);
|
|
1888
2086
|
const parsed = (0, import_gray_matter3.default)(rawContent, customMatterOptions);
|
|
1889
2087
|
const content = parsed.content.trim();
|
|
1890
2088
|
if (content) {
|
|
1891
|
-
const filename = (0,
|
|
2089
|
+
const filename = (0, import_node_path16.basename)(file, ".mdc");
|
|
1892
2090
|
const frontmatter = convertCursorMdcFrontmatter(parsed.data, filename);
|
|
1893
2091
|
rules.push({
|
|
1894
2092
|
frontmatter,
|
|
@@ -1911,7 +2109,7 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
|
|
|
1911
2109
|
if (rules.length === 0) {
|
|
1912
2110
|
errors.push("No Cursor configuration files found (.cursorrules or .cursor/rules/*.mdc)");
|
|
1913
2111
|
}
|
|
1914
|
-
const cursorIgnorePath = (0,
|
|
2112
|
+
const cursorIgnorePath = (0, import_node_path16.join)(baseDir, ".cursorignore");
|
|
1915
2113
|
if (await fileExists(cursorIgnorePath)) {
|
|
1916
2114
|
try {
|
|
1917
2115
|
const content = await readFileContent(cursorIgnorePath);
|
|
@@ -1924,7 +2122,7 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
|
|
|
1924
2122
|
errors.push(`Failed to parse .cursorignore: ${errorMessage}`);
|
|
1925
2123
|
}
|
|
1926
2124
|
}
|
|
1927
|
-
const cursorMcpPath = (0,
|
|
2125
|
+
const cursorMcpPath = (0, import_node_path16.join)(baseDir, ".cursor", "mcp.json");
|
|
1928
2126
|
if (await fileExists(cursorMcpPath)) {
|
|
1929
2127
|
try {
|
|
1930
2128
|
const content = await readFileContent(cursorMcpPath);
|
|
@@ -1947,13 +2145,13 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
|
|
|
1947
2145
|
}
|
|
1948
2146
|
|
|
1949
2147
|
// src/parsers/geminicli.ts
|
|
1950
|
-
var
|
|
2148
|
+
var import_node_path17 = require("path");
|
|
1951
2149
|
async function parseGeminiConfiguration(baseDir = process.cwd()) {
|
|
1952
2150
|
const errors = [];
|
|
1953
2151
|
const rules = [];
|
|
1954
2152
|
let ignorePatterns;
|
|
1955
2153
|
let mcpServers;
|
|
1956
|
-
const geminiFilePath = (0,
|
|
2154
|
+
const geminiFilePath = (0, import_node_path17.join)(baseDir, "GEMINI.md");
|
|
1957
2155
|
if (!await fileExists(geminiFilePath)) {
|
|
1958
2156
|
errors.push("GEMINI.md file not found");
|
|
1959
2157
|
return { rules, errors };
|
|
@@ -1964,12 +2162,12 @@ async function parseGeminiConfiguration(baseDir = process.cwd()) {
|
|
|
1964
2162
|
if (mainRule) {
|
|
1965
2163
|
rules.push(mainRule);
|
|
1966
2164
|
}
|
|
1967
|
-
const memoryDir = (0,
|
|
2165
|
+
const memoryDir = (0, import_node_path17.join)(baseDir, ".gemini", "memories");
|
|
1968
2166
|
if (await fileExists(memoryDir)) {
|
|
1969
2167
|
const memoryRules = await parseGeminiMemoryFiles(memoryDir);
|
|
1970
2168
|
rules.push(...memoryRules);
|
|
1971
2169
|
}
|
|
1972
|
-
const settingsPath = (0,
|
|
2170
|
+
const settingsPath = (0, import_node_path17.join)(baseDir, ".gemini", "settings.json");
|
|
1973
2171
|
if (await fileExists(settingsPath)) {
|
|
1974
2172
|
const settingsResult = await parseGeminiSettings(settingsPath);
|
|
1975
2173
|
if (settingsResult.ignorePatterns) {
|
|
@@ -1980,7 +2178,7 @@ async function parseGeminiConfiguration(baseDir = process.cwd()) {
|
|
|
1980
2178
|
}
|
|
1981
2179
|
errors.push(...settingsResult.errors);
|
|
1982
2180
|
}
|
|
1983
|
-
const aiexcludePath = (0,
|
|
2181
|
+
const aiexcludePath = (0, import_node_path17.join)(baseDir, ".aiexclude");
|
|
1984
2182
|
if (await fileExists(aiexcludePath)) {
|
|
1985
2183
|
const aiexcludePatterns = await parseAiexclude(aiexcludePath);
|
|
1986
2184
|
if (aiexcludePatterns.length > 0) {
|
|
@@ -2033,10 +2231,10 @@ async function parseGeminiMemoryFiles(memoryDir) {
|
|
|
2033
2231
|
const files = await readdir2(memoryDir);
|
|
2034
2232
|
for (const file of files) {
|
|
2035
2233
|
if (file.endsWith(".md")) {
|
|
2036
|
-
const filePath = (0,
|
|
2234
|
+
const filePath = (0, import_node_path17.join)(memoryDir, file);
|
|
2037
2235
|
const content = await readFileContent(filePath);
|
|
2038
2236
|
if (content.trim()) {
|
|
2039
|
-
const filename = (0,
|
|
2237
|
+
const filename = (0, import_node_path17.basename)(file, ".md");
|
|
2040
2238
|
const frontmatter = {
|
|
2041
2239
|
root: false,
|
|
2042
2240
|
targets: ["geminicli"],
|
|
@@ -2086,11 +2284,11 @@ async function parseAiexclude(aiexcludePath) {
|
|
|
2086
2284
|
}
|
|
2087
2285
|
|
|
2088
2286
|
// src/parsers/roo.ts
|
|
2089
|
-
var
|
|
2287
|
+
var import_node_path18 = require("path");
|
|
2090
2288
|
async function parseRooConfiguration(baseDir = process.cwd()) {
|
|
2091
2289
|
const errors = [];
|
|
2092
2290
|
const rules = [];
|
|
2093
|
-
const rooFilePath = (0,
|
|
2291
|
+
const rooFilePath = (0, import_node_path18.join)(baseDir, ".roo", "instructions.md");
|
|
2094
2292
|
if (await fileExists(rooFilePath)) {
|
|
2095
2293
|
try {
|
|
2096
2294
|
const content = await readFileContent(rooFilePath);
|
|
@@ -2113,14 +2311,14 @@ async function parseRooConfiguration(baseDir = process.cwd()) {
|
|
|
2113
2311
|
errors.push(`Failed to parse .roo/instructions.md: ${errorMessage}`);
|
|
2114
2312
|
}
|
|
2115
2313
|
}
|
|
2116
|
-
const rooRulesDir = (0,
|
|
2314
|
+
const rooRulesDir = (0, import_node_path18.join)(baseDir, ".roo", "rules");
|
|
2117
2315
|
if (await fileExists(rooRulesDir)) {
|
|
2118
2316
|
try {
|
|
2119
2317
|
const { readdir: readdir2 } = await import("fs/promises");
|
|
2120
2318
|
const files = await readdir2(rooRulesDir);
|
|
2121
2319
|
for (const file of files) {
|
|
2122
2320
|
if (file.endsWith(".md")) {
|
|
2123
|
-
const filePath = (0,
|
|
2321
|
+
const filePath = (0, import_node_path18.join)(rooRulesDir, file);
|
|
2124
2322
|
try {
|
|
2125
2323
|
const content = await readFileContent(filePath);
|
|
2126
2324
|
if (content.trim()) {
|
|
@@ -2221,7 +2419,7 @@ async function importConfiguration(options) {
|
|
|
2221
2419
|
if (rules.length === 0 && !ignorePatterns && !mcpServers) {
|
|
2222
2420
|
return { success: false, rulesCreated: 0, errors };
|
|
2223
2421
|
}
|
|
2224
|
-
const rulesDirPath = (0,
|
|
2422
|
+
const rulesDirPath = (0, import_node_path19.join)(baseDir, rulesDir);
|
|
2225
2423
|
try {
|
|
2226
2424
|
const { mkdir: mkdir3 } = await import("fs/promises");
|
|
2227
2425
|
await mkdir3(rulesDirPath, { recursive: true });
|
|
@@ -2235,7 +2433,7 @@ async function importConfiguration(options) {
|
|
|
2235
2433
|
try {
|
|
2236
2434
|
const baseFilename = `${tool}__${rule.filename}`;
|
|
2237
2435
|
const filename = await generateUniqueFilename(rulesDirPath, baseFilename);
|
|
2238
|
-
const filePath = (0,
|
|
2436
|
+
const filePath = (0, import_node_path19.join)(rulesDirPath, `${filename}.md`);
|
|
2239
2437
|
const content = generateRuleFileContent(rule);
|
|
2240
2438
|
await writeFileContent(filePath, content);
|
|
2241
2439
|
rulesCreated++;
|
|
@@ -2250,7 +2448,7 @@ async function importConfiguration(options) {
|
|
|
2250
2448
|
let ignoreFileCreated = false;
|
|
2251
2449
|
if (ignorePatterns && ignorePatterns.length > 0) {
|
|
2252
2450
|
try {
|
|
2253
|
-
const rulesyncignorePath = (0,
|
|
2451
|
+
const rulesyncignorePath = (0, import_node_path19.join)(baseDir, ".rulesyncignore");
|
|
2254
2452
|
const ignoreContent = `${ignorePatterns.join("\n")}
|
|
2255
2453
|
`;
|
|
2256
2454
|
await writeFileContent(rulesyncignorePath, ignoreContent);
|
|
@@ -2266,7 +2464,7 @@ async function importConfiguration(options) {
|
|
|
2266
2464
|
let mcpFileCreated = false;
|
|
2267
2465
|
if (mcpServers && Object.keys(mcpServers).length > 0) {
|
|
2268
2466
|
try {
|
|
2269
|
-
const mcpPath = (0,
|
|
2467
|
+
const mcpPath = (0, import_node_path19.join)(baseDir, rulesDir, ".mcp.json");
|
|
2270
2468
|
const mcpContent = `${JSON.stringify({ mcpServers }, null, 2)}
|
|
2271
2469
|
`;
|
|
2272
2470
|
await writeFileContent(mcpPath, mcpContent);
|
|
@@ -2280,7 +2478,7 @@ async function importConfiguration(options) {
|
|
|
2280
2478
|
}
|
|
2281
2479
|
}
|
|
2282
2480
|
return {
|
|
2283
|
-
success: rulesCreated > 0 || ignoreFileCreated || mcpFileCreated,
|
|
2481
|
+
success: errors.length === 0 && (rulesCreated > 0 || ignoreFileCreated || mcpFileCreated),
|
|
2284
2482
|
rulesCreated,
|
|
2285
2483
|
errors,
|
|
2286
2484
|
ignoreFileCreated,
|
|
@@ -2294,7 +2492,7 @@ function generateRuleFileContent(rule) {
|
|
|
2294
2492
|
async function generateUniqueFilename(rulesDir, baseFilename) {
|
|
2295
2493
|
let filename = baseFilename;
|
|
2296
2494
|
let counter = 1;
|
|
2297
|
-
while (await fileExists((0,
|
|
2495
|
+
while (await fileExists((0, import_node_path19.join)(rulesDir, `${filename}.md`))) {
|
|
2298
2496
|
filename = `${baseFilename}-${counter}`;
|
|
2299
2497
|
counter++;
|
|
2300
2498
|
}
|
|
@@ -2359,7 +2557,7 @@ async function importCommand(options = {}) {
|
|
|
2359
2557
|
}
|
|
2360
2558
|
|
|
2361
2559
|
// src/cli/commands/init.ts
|
|
2362
|
-
var
|
|
2560
|
+
var import_node_path20 = require("path");
|
|
2363
2561
|
async function initCommand() {
|
|
2364
2562
|
const aiRulesDir = ".rulesync";
|
|
2365
2563
|
console.log("Initializing rulesync...");
|
|
@@ -2371,14 +2569,13 @@ async function initCommand() {
|
|
|
2371
2569
|
console.log("2. Run 'rulesync generate' to create configuration files");
|
|
2372
2570
|
}
|
|
2373
2571
|
async function createSampleFiles(aiRulesDir) {
|
|
2374
|
-
const
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
content: `---
|
|
2572
|
+
const sampleFile = {
|
|
2573
|
+
filename: "overview.md",
|
|
2574
|
+
content: `---
|
|
2378
2575
|
root: true
|
|
2379
2576
|
targets: ["*"]
|
|
2380
2577
|
description: "Project overview and general development guidelines"
|
|
2381
|
-
globs: ["
|
|
2578
|
+
globs: ["**/*"]
|
|
2382
2579
|
---
|
|
2383
2580
|
|
|
2384
2581
|
# Project Overview
|
|
@@ -2406,96 +2603,13 @@ globs: ["**/*.ts", "**/*.js", "**/*.tsx", "**/*.jsx"]
|
|
|
2406
2603
|
- Implement proper error handling
|
|
2407
2604
|
- Follow single responsibility principle
|
|
2408
2605
|
`
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
globs: ["src/components/**/*.tsx", "src/pages/**/*.tsx", "**/*.css", "**/*.scss"]
|
|
2417
|
-
---
|
|
2418
|
-
|
|
2419
|
-
# Frontend Development Rules
|
|
2420
|
-
|
|
2421
|
-
## React Components
|
|
2422
|
-
|
|
2423
|
-
- Use functional components with hooks
|
|
2424
|
-
- Follow PascalCase naming for components
|
|
2425
|
-
- Use TypeScript interfaces for props
|
|
2426
|
-
- Implement proper error boundaries
|
|
2427
|
-
|
|
2428
|
-
## Styling
|
|
2429
|
-
|
|
2430
|
-
- Use CSS modules or styled-components
|
|
2431
|
-
- Follow BEM methodology for CSS classes
|
|
2432
|
-
- Prefer flexbox and grid for layouts
|
|
2433
|
-
- Use semantic HTML elements
|
|
2434
|
-
|
|
2435
|
-
## State Management
|
|
2436
|
-
|
|
2437
|
-
- Use React hooks for local state
|
|
2438
|
-
- Consider Redux or Zustand for global state
|
|
2439
|
-
- Avoid prop drilling with context API
|
|
2440
|
-
- Keep state as close to where it's used as possible
|
|
2441
|
-
|
|
2442
|
-
## Performance
|
|
2443
|
-
|
|
2444
|
-
- Use React.memo for expensive components
|
|
2445
|
-
- Implement lazy loading for routes
|
|
2446
|
-
- Optimize images and assets
|
|
2447
|
-
- Use proper key props in lists
|
|
2448
|
-
`
|
|
2449
|
-
},
|
|
2450
|
-
{
|
|
2451
|
-
filename: "backend.md",
|
|
2452
|
-
content: `---
|
|
2453
|
-
root: false
|
|
2454
|
-
targets: ["*"]
|
|
2455
|
-
description: "Backend development rules and API guidelines"
|
|
2456
|
-
globs: ["src/api/**/*.ts", "src/services/**/*.ts", "src/models/**/*.ts"]
|
|
2457
|
-
---
|
|
2458
|
-
|
|
2459
|
-
# Backend Development Rules
|
|
2460
|
-
|
|
2461
|
-
## API Design
|
|
2462
|
-
|
|
2463
|
-
- Follow RESTful conventions
|
|
2464
|
-
- Use consistent HTTP status codes
|
|
2465
|
-
- Implement proper error handling with meaningful messages
|
|
2466
|
-
- Use API versioning when necessary
|
|
2467
|
-
|
|
2468
|
-
## Database
|
|
2469
|
-
|
|
2470
|
-
- Use proper indexing for performance
|
|
2471
|
-
- Implement database migrations
|
|
2472
|
-
- Follow naming conventions for tables and columns
|
|
2473
|
-
- Use transactions for data consistency
|
|
2474
|
-
|
|
2475
|
-
## Security
|
|
2476
|
-
|
|
2477
|
-
- Validate all input data
|
|
2478
|
-
- Use proper authentication and authorization
|
|
2479
|
-
- Implement rate limiting
|
|
2480
|
-
- Sanitize database queries to prevent SQL injection
|
|
2481
|
-
|
|
2482
|
-
## Code Organization
|
|
2483
|
-
|
|
2484
|
-
- Use service layer pattern
|
|
2485
|
-
- Implement proper logging
|
|
2486
|
-
- Use environment variables for configuration
|
|
2487
|
-
- Write comprehensive tests for business logic
|
|
2488
|
-
`
|
|
2489
|
-
}
|
|
2490
|
-
];
|
|
2491
|
-
for (const file of sampleFiles) {
|
|
2492
|
-
const filepath = (0, import_node_path19.join)(aiRulesDir, file.filename);
|
|
2493
|
-
if (!await fileExists(filepath)) {
|
|
2494
|
-
await writeFileContent(filepath, file.content);
|
|
2495
|
-
console.log(`Created ${filepath}`);
|
|
2496
|
-
} else {
|
|
2497
|
-
console.log(`Skipped ${filepath} (already exists)`);
|
|
2498
|
-
}
|
|
2606
|
+
};
|
|
2607
|
+
const filepath = (0, import_node_path20.join)(aiRulesDir, sampleFile.filename);
|
|
2608
|
+
if (!await fileExists(filepath)) {
|
|
2609
|
+
await writeFileContent(filepath, sampleFile.content);
|
|
2610
|
+
console.log(`Created ${filepath}`);
|
|
2611
|
+
} else {
|
|
2612
|
+
console.log(`Skipped ${filepath} (already exists)`);
|
|
2499
2613
|
}
|
|
2500
2614
|
}
|
|
2501
2615
|
|
|
@@ -2634,12 +2748,12 @@ async function watchCommand() {
|
|
|
2634
2748
|
|
|
2635
2749
|
// src/cli/index.ts
|
|
2636
2750
|
var program = new import_commander.Command();
|
|
2637
|
-
program.name("rulesync").description("Unified AI rules management CLI tool").version("0.
|
|
2751
|
+
program.name("rulesync").description("Unified AI rules management CLI tool").version("0.47.0");
|
|
2638
2752
|
program.command("init").description("Initialize rulesync in current directory").action(initCommand);
|
|
2639
2753
|
program.command("add <filename>").description("Add a new rule file").action(addCommand);
|
|
2640
2754
|
program.command("gitignore").description("Add generated files to .gitignore").action(gitignoreCommand);
|
|
2641
2755
|
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);
|
|
2642
|
-
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(
|
|
2756
|
+
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(
|
|
2643
2757
|
"-b, --base-dir <paths>",
|
|
2644
2758
|
"Base directories to generate files (comma-separated for multiple paths)"
|
|
2645
2759
|
).option("-v, --verbose", "Verbose output").action(async (options) => {
|
|
@@ -2650,6 +2764,7 @@ program.command("generate").description("Generate configuration files for AI too
|
|
|
2650
2764
|
if (options.claudecode) tools.push("claudecode");
|
|
2651
2765
|
if (options.roo) tools.push("roo");
|
|
2652
2766
|
if (options.geminicli) tools.push("geminicli");
|
|
2767
|
+
if (options.kiro) tools.push("kiro");
|
|
2653
2768
|
const generateOptions = {
|
|
2654
2769
|
verbose: options.verbose,
|
|
2655
2770
|
delete: options.delete
|