rulesync 0.63.0 → 0.64.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.md +15 -3
- package/dist/amazonqcli-WVGYACHI.js +9 -0
- package/dist/{augmentcode-HIZIQG2W.js → augmentcode-DTHPPXWO.js} +2 -2
- package/dist/{chunk-M7NL7G7A.js → chunk-4NAQ5HL4.js} +1 -1
- package/dist/{chunk-GQTMTBX4.js → chunk-6LSN7HSJ.js} +76 -4
- package/dist/chunk-DMTCLQ4T.js +17 -0
- package/dist/{chunk-YTU3SCQO.js → chunk-E2J3UBBK.js} +9 -3
- package/dist/{chunk-LXTA7DBA.js → chunk-EID75W45.js} +1 -1
- package/dist/{chunk-U4PLVMCG.js → chunk-FVPZQEWP.js} +1 -1
- package/dist/{chunk-NETSYSMD.js → chunk-HHJIL3YZ.js} +1 -1
- package/dist/{chunk-UEAYL4NT.js → chunk-JX55DU6Y.js} +1 -1
- package/dist/{chunk-KUGTKMNW.js → chunk-KKWJVA56.js} +5 -2
- package/dist/chunk-LURFNGH4.js +17 -0
- package/dist/{chunk-AUUSMVCT.js → chunk-LYVES5YR.js} +2 -0
- package/dist/{chunk-4PSTOKKD.js → chunk-TBXG53FV.js} +1 -1
- package/dist/{chunk-2CW2KFB3.js → chunk-TQOL7OKY.js} +1 -1
- package/dist/chunk-YPJW7Z5M.js +210 -0
- package/dist/{claudecode-YTEFACCT.js → claudecode-SSYLLUXX.js} +3 -3
- package/dist/{cline-CKNUDEA3.js → cline-5EUGKNZ6.js} +3 -3
- package/dist/{codexcli-7SDGYI7D.js → codexcli-IGM2ADYK.js} +3 -3
- package/dist/{copilot-MOR3HHJX.js → copilot-HSQO7ZCJ.js} +2 -2
- package/dist/{cursor-YJGH7W24.js → cursor-ZB3XNGBK.js} +3 -3
- package/dist/{geminicli-E7KZTZ2G.js → geminicli-FNRKH5GX.js} +3 -3
- package/dist/index.cjs +912 -504
- package/dist/index.js +647 -482
- package/dist/{junie-5LEQU4BO.js → junie-3YGOSOGF.js} +3 -3
- package/dist/{kiro-YDHXY2MA.js → kiro-B6WZNLY4.js} +2 -2
- package/dist/opencode-SZETJ62M.js +17 -0
- package/dist/{roo-L3QTTIPO.js → roo-KLTWVAKE.js} +3 -2
- package/dist/{windsurf-4P6HEUBV.js → windsurf-IZEKUAID.js} +3 -3
- package/package.json +2 -1
- package/dist/chunk-MDYDKNXQ.js +0 -61
- package/dist/chunk-PCATT4UZ.js +0 -78
package/dist/index.js
CHANGED
|
@@ -1,23 +1,35 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import "./chunk-
|
|
3
|
-
import "./chunk-
|
|
4
|
-
import "./chunk-
|
|
5
|
-
import "./chunk-
|
|
6
|
-
import "./chunk-
|
|
7
|
-
import
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
2
|
+
import "./chunk-DMTCLQ4T.js";
|
|
3
|
+
import "./chunk-4NAQ5HL4.js";
|
|
4
|
+
import "./chunk-EID75W45.js";
|
|
5
|
+
import "./chunk-LURFNGH4.js";
|
|
6
|
+
import "./chunk-HHJIL3YZ.js";
|
|
7
|
+
import {
|
|
8
|
+
ensureDir,
|
|
9
|
+
fileExists,
|
|
10
|
+
findFiles,
|
|
11
|
+
findRuleFiles,
|
|
12
|
+
logger,
|
|
13
|
+
readFileContent,
|
|
14
|
+
removeClaudeGeneratedFiles,
|
|
15
|
+
removeDirectory,
|
|
16
|
+
resolvePath,
|
|
17
|
+
writeFileContent
|
|
18
|
+
} from "./chunk-YPJW7Z5M.js";
|
|
19
|
+
import "./chunk-E2J3UBBK.js";
|
|
20
|
+
import "./chunk-FVPZQEWP.js";
|
|
21
|
+
import "./chunk-JX55DU6Y.js";
|
|
22
|
+
import "./chunk-TQOL7OKY.js";
|
|
23
|
+
import "./chunk-KKWJVA56.js";
|
|
24
|
+
import "./chunk-TBXG53FV.js";
|
|
25
|
+
import "./chunk-6LSN7HSJ.js";
|
|
14
26
|
import {
|
|
15
27
|
ALL_TOOL_TARGETS,
|
|
16
28
|
RulesyncTargetsSchema,
|
|
17
29
|
ToolTargetSchema,
|
|
18
30
|
ToolTargetsSchema,
|
|
19
31
|
isToolTarget
|
|
20
|
-
} from "./chunk-
|
|
32
|
+
} from "./chunk-LYVES5YR.js";
|
|
21
33
|
|
|
22
34
|
// src/cli/index.ts
|
|
23
35
|
import { Command } from "commander";
|
|
@@ -70,6 +82,7 @@ var ConfigSchema = z3.object({
|
|
|
70
82
|
// src/types/config-options.ts
|
|
71
83
|
import { z as z4 } from "zod/mini";
|
|
72
84
|
var OutputPathsSchema = z4.object({
|
|
85
|
+
amazonqcli: z4.optional(z4.string()),
|
|
73
86
|
augmentcode: z4.optional(z4.string()),
|
|
74
87
|
"augmentcode-legacy": z4.optional(z4.string()),
|
|
75
88
|
copilot: z4.optional(z4.string()),
|
|
@@ -77,6 +90,7 @@ var OutputPathsSchema = z4.object({
|
|
|
77
90
|
cline: z4.optional(z4.string()),
|
|
78
91
|
claudecode: z4.optional(z4.string()),
|
|
79
92
|
codexcli: z4.optional(z4.string()),
|
|
93
|
+
opencode: z4.optional(z4.string()),
|
|
80
94
|
roo: z4.optional(z4.string()),
|
|
81
95
|
geminicli: z4.optional(z4.string()),
|
|
82
96
|
kiro: z4.optional(z4.string()),
|
|
@@ -127,7 +141,7 @@ var MergedConfigSchema = z4.object({
|
|
|
127
141
|
import { z as z5 } from "zod/mini";
|
|
128
142
|
var McpTransportTypeSchema = z5.enum(["stdio", "sse", "http"]);
|
|
129
143
|
var McpServerBaseSchema = z5.object({
|
|
130
|
-
command: z5.optional(z5.string()),
|
|
144
|
+
command: z5.optional(z5.union([z5.string(), z5.array(z5.string())])),
|
|
131
145
|
args: z5.optional(z5.array(z5.string())),
|
|
132
146
|
url: z5.optional(z5.string()),
|
|
133
147
|
httpUrl: z5.optional(z5.string()),
|
|
@@ -178,6 +192,7 @@ function getDefaultConfig() {
|
|
|
178
192
|
return {
|
|
179
193
|
aiRulesDir: ".rulesync",
|
|
180
194
|
outputPaths: {
|
|
195
|
+
amazonqcli: ".amazonq/rules",
|
|
181
196
|
augmentcode: ".",
|
|
182
197
|
"augmentcode-legacy": ".",
|
|
183
198
|
copilot: ".github/instructions",
|
|
@@ -185,6 +200,7 @@ function getDefaultConfig() {
|
|
|
185
200
|
cline: ".clinerules",
|
|
186
201
|
claudecode: ".",
|
|
187
202
|
codexcli: ".",
|
|
203
|
+
opencode: ".",
|
|
188
204
|
roo: ".roo/rules",
|
|
189
205
|
geminicli: ".gemini/memories",
|
|
190
206
|
kiro: ".kiro/steering",
|
|
@@ -391,50 +407,6 @@ function mergeWithCliOptions(config, cliOptions) {
|
|
|
391
407
|
return merged;
|
|
392
408
|
}
|
|
393
409
|
|
|
394
|
-
// src/utils/logger.ts
|
|
395
|
-
import { consola } from "consola";
|
|
396
|
-
var Logger = class {
|
|
397
|
-
_verbose = false;
|
|
398
|
-
console = consola.withDefaults({
|
|
399
|
-
tag: "rulesync"
|
|
400
|
-
});
|
|
401
|
-
setVerbose(verbose) {
|
|
402
|
-
this._verbose = verbose;
|
|
403
|
-
}
|
|
404
|
-
get verbose() {
|
|
405
|
-
return this._verbose;
|
|
406
|
-
}
|
|
407
|
-
// Regular log (always shown, regardless of verbose)
|
|
408
|
-
log(message, ...args) {
|
|
409
|
-
this.console.log(message, ...args);
|
|
410
|
-
}
|
|
411
|
-
// Info level (shown only in verbose mode)
|
|
412
|
-
info(message, ...args) {
|
|
413
|
-
if (this._verbose) {
|
|
414
|
-
this.console.info(message, ...args);
|
|
415
|
-
}
|
|
416
|
-
}
|
|
417
|
-
// Success (always shown)
|
|
418
|
-
success(message, ...args) {
|
|
419
|
-
this.console.success(message, ...args);
|
|
420
|
-
}
|
|
421
|
-
// Warning (always shown)
|
|
422
|
-
warn(message, ...args) {
|
|
423
|
-
this.console.warn(message, ...args);
|
|
424
|
-
}
|
|
425
|
-
// Error (always shown)
|
|
426
|
-
error(message, ...args) {
|
|
427
|
-
this.console.error(message, ...args);
|
|
428
|
-
}
|
|
429
|
-
// Debug level (shown only in verbose mode)
|
|
430
|
-
debug(message, ...args) {
|
|
431
|
-
if (this._verbose) {
|
|
432
|
-
this.console.debug(message, ...args);
|
|
433
|
-
}
|
|
434
|
-
}
|
|
435
|
-
};
|
|
436
|
-
var logger = new Logger();
|
|
437
|
-
|
|
438
410
|
// src/cli/commands/add.ts
|
|
439
411
|
function sanitizeFilename(filename) {
|
|
440
412
|
return filename.endsWith(".md") ? filename.slice(0, -3) : filename;
|
|
@@ -508,89 +480,6 @@ async function safeAsyncOperation(operation, errorContext) {
|
|
|
508
480
|
}
|
|
509
481
|
}
|
|
510
482
|
|
|
511
|
-
// src/utils/file.ts
|
|
512
|
-
import { mkdir as mkdir2, readdir, readFile, rm, stat, writeFile as writeFile2 } from "fs/promises";
|
|
513
|
-
import { dirname, join as join2 } from "path";
|
|
514
|
-
async function ensureDir(dirPath) {
|
|
515
|
-
try {
|
|
516
|
-
await stat(dirPath);
|
|
517
|
-
} catch {
|
|
518
|
-
await mkdir2(dirPath, { recursive: true });
|
|
519
|
-
}
|
|
520
|
-
}
|
|
521
|
-
function resolvePath(relativePath, baseDir) {
|
|
522
|
-
return baseDir ? join2(baseDir, relativePath) : relativePath;
|
|
523
|
-
}
|
|
524
|
-
async function readFileContent(filepath) {
|
|
525
|
-
return readFile(filepath, "utf-8");
|
|
526
|
-
}
|
|
527
|
-
async function writeFileContent(filepath, content) {
|
|
528
|
-
await ensureDir(dirname(filepath));
|
|
529
|
-
await writeFile2(filepath, content, "utf-8");
|
|
530
|
-
}
|
|
531
|
-
async function fileExists(filepath) {
|
|
532
|
-
try {
|
|
533
|
-
await stat(filepath);
|
|
534
|
-
return true;
|
|
535
|
-
} catch {
|
|
536
|
-
return false;
|
|
537
|
-
}
|
|
538
|
-
}
|
|
539
|
-
async function findFiles(dir, extension = ".md") {
|
|
540
|
-
try {
|
|
541
|
-
const files = await readdir(dir);
|
|
542
|
-
return files.filter((file) => file.endsWith(extension)).map((file) => join2(dir, file));
|
|
543
|
-
} catch {
|
|
544
|
-
return [];
|
|
545
|
-
}
|
|
546
|
-
}
|
|
547
|
-
async function findRuleFiles(aiRulesDir) {
|
|
548
|
-
const rulesDir = join2(aiRulesDir, "rules");
|
|
549
|
-
const newLocationFiles = await findFiles(rulesDir, ".md");
|
|
550
|
-
const legacyLocationFiles = await findFiles(aiRulesDir, ".md");
|
|
551
|
-
const newLocationBasenames = new Set(
|
|
552
|
-
newLocationFiles.map((file) => file.split("/").pop()?.replace(/\.md$/, ""))
|
|
553
|
-
);
|
|
554
|
-
const filteredLegacyFiles = legacyLocationFiles.filter((file) => {
|
|
555
|
-
const basename6 = file.split("/").pop()?.replace(/\.md$/, "");
|
|
556
|
-
return !newLocationBasenames.has(basename6);
|
|
557
|
-
});
|
|
558
|
-
return [...newLocationFiles, ...filteredLegacyFiles];
|
|
559
|
-
}
|
|
560
|
-
async function removeDirectory(dirPath) {
|
|
561
|
-
const dangerousPaths = [".", "/", "~", "src", "node_modules"];
|
|
562
|
-
if (dangerousPaths.includes(dirPath) || dirPath === "") {
|
|
563
|
-
logger.warn(`Skipping deletion of dangerous path: ${dirPath}`);
|
|
564
|
-
return;
|
|
565
|
-
}
|
|
566
|
-
try {
|
|
567
|
-
if (await fileExists(dirPath)) {
|
|
568
|
-
await rm(dirPath, { recursive: true, force: true });
|
|
569
|
-
}
|
|
570
|
-
} catch (error) {
|
|
571
|
-
logger.warn(`Failed to remove directory ${dirPath}:`, error);
|
|
572
|
-
}
|
|
573
|
-
}
|
|
574
|
-
async function removeFile(filepath) {
|
|
575
|
-
try {
|
|
576
|
-
if (await fileExists(filepath)) {
|
|
577
|
-
await rm(filepath);
|
|
578
|
-
}
|
|
579
|
-
} catch (error) {
|
|
580
|
-
logger.warn(`Failed to remove file ${filepath}:`, error);
|
|
581
|
-
}
|
|
582
|
-
}
|
|
583
|
-
async function removeClaudeGeneratedFiles() {
|
|
584
|
-
const filesToRemove = ["CLAUDE.md", ".claude/memories"];
|
|
585
|
-
for (const fileOrDir of filesToRemove) {
|
|
586
|
-
if (fileOrDir.endsWith("/memories")) {
|
|
587
|
-
await removeDirectory(fileOrDir);
|
|
588
|
-
} else {
|
|
589
|
-
await removeFile(fileOrDir);
|
|
590
|
-
}
|
|
591
|
-
}
|
|
592
|
-
}
|
|
593
|
-
|
|
594
483
|
// src/cli/commands/config.ts
|
|
595
484
|
async function configCommand(options = {}) {
|
|
596
485
|
if (options.init) {
|
|
@@ -802,25 +691,25 @@ export default config;
|
|
|
802
691
|
}
|
|
803
692
|
|
|
804
693
|
// src/cli/commands/generate.ts
|
|
805
|
-
import { join as
|
|
694
|
+
import { join as join12 } from "path";
|
|
806
695
|
|
|
807
696
|
// src/core/command-generator.ts
|
|
808
|
-
import { join as
|
|
697
|
+
import { join as join3 } from "path";
|
|
809
698
|
|
|
810
699
|
// src/utils/command-generators.ts
|
|
811
|
-
import { join as
|
|
812
|
-
function generateYamlFrontmatter(command, options
|
|
813
|
-
const
|
|
814
|
-
if (options
|
|
815
|
-
|
|
700
|
+
import { join as join2 } from "path";
|
|
701
|
+
function generateYamlFrontmatter(command, options) {
|
|
702
|
+
const frontmatterLines = ["---"];
|
|
703
|
+
if (options?.includeDescription && command.frontmatter.description) {
|
|
704
|
+
frontmatterLines.push(`description: ${command.frontmatter.description}`);
|
|
816
705
|
}
|
|
817
|
-
if (options
|
|
706
|
+
if (options?.additionalFields) {
|
|
818
707
|
for (const field of options.additionalFields) {
|
|
819
|
-
|
|
708
|
+
frontmatterLines.push(`${field.key}: ${field.value}`);
|
|
820
709
|
}
|
|
821
710
|
}
|
|
822
|
-
|
|
823
|
-
return
|
|
711
|
+
frontmatterLines.push("---");
|
|
712
|
+
return frontmatterLines;
|
|
824
713
|
}
|
|
825
714
|
function buildCommandContent(command, frontmatterOptions) {
|
|
826
715
|
const frontmatter = generateYamlFrontmatter(command, frontmatterOptions);
|
|
@@ -831,12 +720,12 @@ ${command.content.trim()}
|
|
|
831
720
|
}
|
|
832
721
|
function getFlattenedCommandPath(filename, baseDir, subdir) {
|
|
833
722
|
const flattenedName = filename.replace(/\//g, "-");
|
|
834
|
-
return
|
|
723
|
+
return join2(baseDir, subdir, `${flattenedName}.md`);
|
|
835
724
|
}
|
|
836
725
|
function getHierarchicalCommandPath(filename, baseDir, subdir, extension = "md") {
|
|
837
726
|
const nameWithoutExt = filename.replace(/\.[^/.]+$/, "");
|
|
838
727
|
const fileWithExt = `${nameWithoutExt}.${extension}`;
|
|
839
|
-
return
|
|
728
|
+
return join2(baseDir, subdir, fileWithExt);
|
|
840
729
|
}
|
|
841
730
|
function escapeTomlString(str) {
|
|
842
731
|
return str.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/\t/g, "\\t");
|
|
@@ -859,26 +748,75 @@ var syntaxConverters = {
|
|
|
859
748
|
}
|
|
860
749
|
};
|
|
861
750
|
|
|
862
|
-
// src/generators/commands/
|
|
863
|
-
var
|
|
751
|
+
// src/generators/commands/base.ts
|
|
752
|
+
var BaseCommandGenerator = class {
|
|
753
|
+
/**
|
|
754
|
+
* Generate command output for the specified tool
|
|
755
|
+
*/
|
|
864
756
|
generate(command, outputDir) {
|
|
865
757
|
const filepath = this.getOutputPath(command.filename, outputDir);
|
|
866
|
-
const content =
|
|
758
|
+
const content = this.processContent(command);
|
|
867
759
|
return {
|
|
868
|
-
tool:
|
|
760
|
+
tool: this.getToolName(),
|
|
869
761
|
filepath,
|
|
870
762
|
content
|
|
871
763
|
};
|
|
872
764
|
}
|
|
765
|
+
/**
|
|
766
|
+
* Get the output path for the command file
|
|
767
|
+
* Override this method if custom path logic is needed
|
|
768
|
+
*/
|
|
873
769
|
getOutputPath(filename, baseDir) {
|
|
874
|
-
|
|
770
|
+
if (this.supportsHierarchy()) {
|
|
771
|
+
return getHierarchicalCommandPath(
|
|
772
|
+
filename,
|
|
773
|
+
baseDir,
|
|
774
|
+
this.getCommandsDirectory(),
|
|
775
|
+
this.getFileExtension()
|
|
776
|
+
);
|
|
777
|
+
} else {
|
|
778
|
+
return getFlattenedCommandPath(filename, baseDir, this.getCommandsDirectory());
|
|
779
|
+
}
|
|
875
780
|
}
|
|
781
|
+
/**
|
|
782
|
+
* Whether this tool supports hierarchical directory structure
|
|
783
|
+
* Override to return true for tools that support nested commands
|
|
784
|
+
*/
|
|
785
|
+
supportsHierarchy() {
|
|
786
|
+
return false;
|
|
787
|
+
}
|
|
788
|
+
/**
|
|
789
|
+
* Get file extension for the target tool
|
|
790
|
+
* Override if tool uses different extension than .md
|
|
791
|
+
*/
|
|
792
|
+
getFileExtension() {
|
|
793
|
+
return "md";
|
|
794
|
+
}
|
|
795
|
+
};
|
|
796
|
+
|
|
797
|
+
// src/generators/commands/claudecode.ts
|
|
798
|
+
var ClaudeCodeCommandGenerator = class extends BaseCommandGenerator {
|
|
799
|
+
getToolName() {
|
|
800
|
+
return "claudecode";
|
|
801
|
+
}
|
|
802
|
+
getCommandsDirectory() {
|
|
803
|
+
return ".claude/commands";
|
|
804
|
+
}
|
|
805
|
+
processContent(command) {
|
|
806
|
+
return buildCommandContent(command, { includeDescription: true });
|
|
807
|
+
}
|
|
808
|
+
// Uses flattened structure by default (supportsHierarchy returns false)
|
|
876
809
|
};
|
|
877
810
|
|
|
878
811
|
// src/generators/commands/geminicli.ts
|
|
879
|
-
var GeminiCliCommandGenerator = class {
|
|
880
|
-
|
|
881
|
-
|
|
812
|
+
var GeminiCliCommandGenerator = class extends BaseCommandGenerator {
|
|
813
|
+
getToolName() {
|
|
814
|
+
return "geminicli";
|
|
815
|
+
}
|
|
816
|
+
getCommandsDirectory() {
|
|
817
|
+
return ".gemini/commands";
|
|
818
|
+
}
|
|
819
|
+
processContent(command) {
|
|
882
820
|
const convertedContent = syntaxConverters.toGeminiCli(command.content);
|
|
883
821
|
const tomlLines = [];
|
|
884
822
|
if (command.frontmatter.description) {
|
|
@@ -886,32 +824,28 @@ var GeminiCliCommandGenerator = class {
|
|
|
886
824
|
tomlLines.push("");
|
|
887
825
|
}
|
|
888
826
|
tomlLines.push(`prompt = """${convertedContent}"""`);
|
|
889
|
-
|
|
890
|
-
return {
|
|
891
|
-
tool: "geminicli",
|
|
892
|
-
filepath,
|
|
893
|
-
content
|
|
894
|
-
};
|
|
827
|
+
return tomlLines.join("\n") + "\n";
|
|
895
828
|
}
|
|
896
|
-
|
|
897
|
-
return
|
|
829
|
+
supportsHierarchy() {
|
|
830
|
+
return true;
|
|
831
|
+
}
|
|
832
|
+
getFileExtension() {
|
|
833
|
+
return "toml";
|
|
898
834
|
}
|
|
899
835
|
};
|
|
900
836
|
|
|
901
837
|
// src/generators/commands/roo.ts
|
|
902
|
-
var RooCommandGenerator = class {
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
const content = buildCommandContent(command);
|
|
906
|
-
return {
|
|
907
|
-
tool: "roo",
|
|
908
|
-
filepath,
|
|
909
|
-
content
|
|
910
|
-
};
|
|
838
|
+
var RooCommandGenerator = class extends BaseCommandGenerator {
|
|
839
|
+
getToolName() {
|
|
840
|
+
return "roo";
|
|
911
841
|
}
|
|
912
|
-
|
|
913
|
-
return
|
|
842
|
+
getCommandsDirectory() {
|
|
843
|
+
return ".roo/commands";
|
|
844
|
+
}
|
|
845
|
+
processContent(command) {
|
|
846
|
+
return buildCommandContent(command, { includeDescription: true });
|
|
914
847
|
}
|
|
848
|
+
// Uses flattened structure by default (supportsHierarchy returns false)
|
|
915
849
|
};
|
|
916
850
|
|
|
917
851
|
// src/generators/commands/index.ts
|
|
@@ -988,7 +922,7 @@ async function parseCommandFile(filepath) {
|
|
|
988
922
|
|
|
989
923
|
// src/core/command-generator.ts
|
|
990
924
|
async function generateCommands(projectRoot, baseDir, targets) {
|
|
991
|
-
const commandsDir =
|
|
925
|
+
const commandsDir = join3(projectRoot, ".rulesync", "commands");
|
|
992
926
|
if (!await fileExists(commandsDir)) {
|
|
993
927
|
return [];
|
|
994
928
|
}
|
|
@@ -1022,7 +956,7 @@ async function generateCommands(projectRoot, baseDir, targets) {
|
|
|
1022
956
|
}
|
|
1023
957
|
|
|
1024
958
|
// src/generators/ignore/shared-factory.ts
|
|
1025
|
-
import { join as
|
|
959
|
+
import { join as join4 } from "path";
|
|
1026
960
|
|
|
1027
961
|
// src/generators/ignore/shared-helpers.ts
|
|
1028
962
|
function extractIgnorePatternsFromRules(rules) {
|
|
@@ -1145,7 +1079,7 @@ function generateIgnoreFile(rules, config, ignoreConfig, baseDir) {
|
|
|
1145
1079
|
const outputs = [];
|
|
1146
1080
|
const content = generateIgnoreContent(rules, ignoreConfig);
|
|
1147
1081
|
const outputPath = baseDir || process.cwd();
|
|
1148
|
-
const filepath =
|
|
1082
|
+
const filepath = join4(outputPath, ignoreConfig.filename);
|
|
1149
1083
|
outputs.push({
|
|
1150
1084
|
tool: ignoreConfig.tool,
|
|
1151
1085
|
filepath,
|
|
@@ -1732,21 +1666,18 @@ function generateWindsurfIgnore(rules, config, baseDir) {
|
|
|
1732
1666
|
return generateIgnoreFile(rules, config, ignoreConfigs.windsurf, baseDir);
|
|
1733
1667
|
}
|
|
1734
1668
|
|
|
1735
|
-
// src/generators/rules/augmentcode.ts
|
|
1736
|
-
import { join as join8 } from "path";
|
|
1737
|
-
|
|
1738
1669
|
// src/generators/rules/shared-helpers.ts
|
|
1739
|
-
import { join as
|
|
1670
|
+
import { join as join6 } from "path";
|
|
1740
1671
|
|
|
1741
1672
|
// src/utils/ignore.ts
|
|
1742
|
-
import { join as
|
|
1673
|
+
import { join as join5 } from "path";
|
|
1743
1674
|
import micromatch from "micromatch";
|
|
1744
1675
|
var cachedIgnorePatterns = null;
|
|
1745
1676
|
async function loadIgnorePatterns(baseDir = process.cwd()) {
|
|
1746
1677
|
if (cachedIgnorePatterns) {
|
|
1747
1678
|
return cachedIgnorePatterns;
|
|
1748
1679
|
}
|
|
1749
|
-
const ignorePath =
|
|
1680
|
+
const ignorePath = join5(baseDir, ".rulesyncignore");
|
|
1750
1681
|
if (!await fileExists(ignorePath)) {
|
|
1751
1682
|
cachedIgnorePatterns = { patterns: [] };
|
|
1752
1683
|
return cachedIgnorePatterns;
|
|
@@ -1800,7 +1731,7 @@ function addOutput(outputs, tool, config, baseDir, relativePath, content) {
|
|
|
1800
1731
|
const outputDir = resolveOutputDir(config, tool, baseDir);
|
|
1801
1732
|
outputs.push({
|
|
1802
1733
|
tool,
|
|
1803
|
-
filepath:
|
|
1734
|
+
filepath: join6(outputDir, relativePath),
|
|
1804
1735
|
content
|
|
1805
1736
|
});
|
|
1806
1737
|
}
|
|
@@ -1809,7 +1740,7 @@ async function generateRulesConfig(rules, config, generatorConfig, baseDir) {
|
|
|
1809
1740
|
for (const rule of rules) {
|
|
1810
1741
|
const content = generatorConfig.generateContent(rule);
|
|
1811
1742
|
const outputDir = resolveOutputDir(config, generatorConfig.tool, baseDir);
|
|
1812
|
-
const filepath = generatorConfig.pathResolver ? generatorConfig.pathResolver(rule, outputDir) :
|
|
1743
|
+
const filepath = generatorConfig.pathResolver ? generatorConfig.pathResolver(rule, outputDir) : join6(outputDir, `${rule.filename}${generatorConfig.fileExtension}`);
|
|
1813
1744
|
outputs.push({
|
|
1814
1745
|
tool: generatorConfig.tool,
|
|
1815
1746
|
filepath,
|
|
@@ -1837,7 +1768,7 @@ async function generateComplexRules(rules, config, generatorConfig, baseDir) {
|
|
|
1837
1768
|
for (const rule of detailRules) {
|
|
1838
1769
|
const content = generatorConfig.generateDetailContent(rule);
|
|
1839
1770
|
const filepath = resolvePath(
|
|
1840
|
-
|
|
1771
|
+
join6(generatorConfig.detailSubDir, `${rule.filename}.md`),
|
|
1841
1772
|
baseDir
|
|
1842
1773
|
);
|
|
1843
1774
|
outputs.push({
|
|
@@ -1891,7 +1822,61 @@ function generateIgnoreFile2(patterns, tool) {
|
|
|
1891
1822
|
return lines.join("\n");
|
|
1892
1823
|
}
|
|
1893
1824
|
|
|
1825
|
+
// src/generators/rules/amazonqcli.ts
|
|
1826
|
+
async function generateAmazonqcliConfig(rules, config, baseDir) {
|
|
1827
|
+
const generatorConfig = {
|
|
1828
|
+
tool: "amazonqcli",
|
|
1829
|
+
fileExtension: ".md",
|
|
1830
|
+
generateContent: generateRuleFile,
|
|
1831
|
+
generateRootContent: generateMainRulesFile,
|
|
1832
|
+
rootFilePath: ".amazonq/rules/main.md",
|
|
1833
|
+
generateDetailContent: generateRuleFile,
|
|
1834
|
+
detailSubDir: ".amazonq/rules"
|
|
1835
|
+
};
|
|
1836
|
+
return generateComplexRules(rules, config, generatorConfig, baseDir);
|
|
1837
|
+
}
|
|
1838
|
+
function generateMainRulesFile(rootRule, detailRules) {
|
|
1839
|
+
const lines = [];
|
|
1840
|
+
if (detailRules.length > 0) {
|
|
1841
|
+
lines.push("# Amazon Q Developer CLI Project Rules");
|
|
1842
|
+
lines.push("");
|
|
1843
|
+
lines.push("This file contains the main project rules. See also:");
|
|
1844
|
+
lines.push("");
|
|
1845
|
+
for (const rule of detailRules) {
|
|
1846
|
+
lines.push(`- ${rule.filename}.md: ${rule.frontmatter.description}`);
|
|
1847
|
+
}
|
|
1848
|
+
lines.push("");
|
|
1849
|
+
}
|
|
1850
|
+
if (rootRule) {
|
|
1851
|
+
if (detailRules.length > 0) {
|
|
1852
|
+
lines.push("## Overview");
|
|
1853
|
+
lines.push("");
|
|
1854
|
+
}
|
|
1855
|
+
lines.push(rootRule.content);
|
|
1856
|
+
lines.push("");
|
|
1857
|
+
} else if (detailRules.length === 0) {
|
|
1858
|
+
lines.push("# Amazon Q Developer CLI Project Rules");
|
|
1859
|
+
lines.push("");
|
|
1860
|
+
lines.push("This file contains project-specific rules and context for Amazon Q Developer CLI.");
|
|
1861
|
+
lines.push("");
|
|
1862
|
+
lines.push("## Development Standards");
|
|
1863
|
+
lines.push("");
|
|
1864
|
+
lines.push("Add your project-specific development standards here.");
|
|
1865
|
+
lines.push("");
|
|
1866
|
+
}
|
|
1867
|
+
return lines.join("\n").trim() + "\n";
|
|
1868
|
+
}
|
|
1869
|
+
function generateRuleFile(rule) {
|
|
1870
|
+
const lines = [];
|
|
1871
|
+
lines.push(`# ${rule.frontmatter.description || rule.filename}`);
|
|
1872
|
+
lines.push("");
|
|
1873
|
+
lines.push(rule.content.trim());
|
|
1874
|
+
lines.push("");
|
|
1875
|
+
return lines.join("\n");
|
|
1876
|
+
}
|
|
1877
|
+
|
|
1894
1878
|
// src/generators/rules/augmentcode.ts
|
|
1879
|
+
import { join as join7 } from "path";
|
|
1895
1880
|
async function generateAugmentcodeConfig(rules, config, baseDir) {
|
|
1896
1881
|
const outputs = createOutputsArray();
|
|
1897
1882
|
rules.forEach((rule) => {
|
|
@@ -1900,13 +1885,13 @@ async function generateAugmentcodeConfig(rules, config, baseDir) {
|
|
|
1900
1885
|
"augmentcode",
|
|
1901
1886
|
config,
|
|
1902
1887
|
baseDir,
|
|
1903
|
-
|
|
1904
|
-
|
|
1888
|
+
join7(".augment", "rules", `${rule.filename}.md`),
|
|
1889
|
+
generateRuleFile2(rule)
|
|
1905
1890
|
);
|
|
1906
1891
|
});
|
|
1907
1892
|
return outputs;
|
|
1908
1893
|
}
|
|
1909
|
-
function
|
|
1894
|
+
function generateRuleFile2(rule) {
|
|
1910
1895
|
const lines = [];
|
|
1911
1896
|
lines.push("---");
|
|
1912
1897
|
let ruleType = "manual";
|
|
@@ -1953,7 +1938,7 @@ function generateLegacyGuidelinesFile(allRules) {
|
|
|
1953
1938
|
}
|
|
1954
1939
|
|
|
1955
1940
|
// src/generators/rules/claudecode.ts
|
|
1956
|
-
import { join as
|
|
1941
|
+
import { join as join8 } from "path";
|
|
1957
1942
|
async function generateClaudecodeConfig(rules, config, baseDir) {
|
|
1958
1943
|
const generatorConfig = {
|
|
1959
1944
|
tool: "claudecode",
|
|
@@ -1965,7 +1950,7 @@ async function generateClaudecodeConfig(rules, config, baseDir) {
|
|
|
1965
1950
|
generateDetailContent: generateMemoryFile,
|
|
1966
1951
|
detailSubDir: ".claude/memories",
|
|
1967
1952
|
updateAdditionalConfig: async (ignorePatterns, baseDir2) => {
|
|
1968
|
-
const settingsPath = resolvePath(
|
|
1953
|
+
const settingsPath = resolvePath(join8(".claude", "settings.json"), baseDir2);
|
|
1969
1954
|
await updateClaudeSettings(settingsPath, ignorePatterns);
|
|
1970
1955
|
return [];
|
|
1971
1956
|
}
|
|
@@ -2029,7 +2014,7 @@ async function updateClaudeSettings(settingsPath, ignorePatterns) {
|
|
|
2029
2014
|
}
|
|
2030
2015
|
|
|
2031
2016
|
// src/generators/rules/generator-registry.ts
|
|
2032
|
-
import { join as
|
|
2017
|
+
import { join as join9 } from "path";
|
|
2033
2018
|
function determineCursorRuleType(frontmatter) {
|
|
2034
2019
|
if (frontmatter.cursorRuleType) {
|
|
2035
2020
|
return frontmatter.cursorRuleType;
|
|
@@ -2053,6 +2038,22 @@ function determineCursorRuleType(frontmatter) {
|
|
|
2053
2038
|
}
|
|
2054
2039
|
var GENERATOR_REGISTRY = {
|
|
2055
2040
|
// Simple generators - generate one file per rule
|
|
2041
|
+
amazonqcli: {
|
|
2042
|
+
type: "complex",
|
|
2043
|
+
tool: "amazonqcli",
|
|
2044
|
+
fileExtension: ".md",
|
|
2045
|
+
// ignoreFileName omitted - Amazon Q CLI doesn't have native ignore file support yet
|
|
2046
|
+
generateContent: (rule) => {
|
|
2047
|
+
const lines = [];
|
|
2048
|
+
if (rule.frontmatter.description) {
|
|
2049
|
+
lines.push(`# ${rule.frontmatter.description}
|
|
2050
|
+
`);
|
|
2051
|
+
}
|
|
2052
|
+
lines.push(rule.content.trim());
|
|
2053
|
+
return lines.join("\n");
|
|
2054
|
+
}
|
|
2055
|
+
// Complex generation handled by existing generator
|
|
2056
|
+
},
|
|
2056
2057
|
cline: {
|
|
2057
2058
|
type: "simple",
|
|
2058
2059
|
tool: "cline",
|
|
@@ -2109,7 +2110,7 @@ var GENERATOR_REGISTRY = {
|
|
|
2109
2110
|
},
|
|
2110
2111
|
pathResolver: (rule, outputDir) => {
|
|
2111
2112
|
const baseFilename = rule.filename.replace(/\.md$/, "");
|
|
2112
|
-
return
|
|
2113
|
+
return join9(outputDir, `${baseFilename}.instructions.md`);
|
|
2113
2114
|
}
|
|
2114
2115
|
},
|
|
2115
2116
|
cursor: {
|
|
@@ -2149,7 +2150,7 @@ var GENERATOR_REGISTRY = {
|
|
|
2149
2150
|
return lines.join("\n");
|
|
2150
2151
|
},
|
|
2151
2152
|
pathResolver: (rule, outputDir) => {
|
|
2152
|
-
return
|
|
2153
|
+
return join9(outputDir, `${rule.filename}.mdc`);
|
|
2153
2154
|
}
|
|
2154
2155
|
},
|
|
2155
2156
|
codexcli: {
|
|
@@ -2185,10 +2186,10 @@ var GENERATOR_REGISTRY = {
|
|
|
2185
2186
|
pathResolver: (rule, outputDir) => {
|
|
2186
2187
|
const outputFormat = rule.frontmatter.windsurfOutputFormat || "directory";
|
|
2187
2188
|
if (outputFormat === "single-file") {
|
|
2188
|
-
return
|
|
2189
|
+
return join9(outputDir, ".windsurf-rules");
|
|
2189
2190
|
} else {
|
|
2190
|
-
const rulesDir =
|
|
2191
|
-
return
|
|
2191
|
+
const rulesDir = join9(outputDir, ".windsurf", "rules");
|
|
2192
|
+
return join9(rulesDir, `${rule.filename}.md`);
|
|
2192
2193
|
}
|
|
2193
2194
|
}
|
|
2194
2195
|
},
|
|
@@ -2219,6 +2220,22 @@ var GENERATOR_REGISTRY = {
|
|
|
2219
2220
|
const lines = [];
|
|
2220
2221
|
if (rule.frontmatter.description) {
|
|
2221
2222
|
lines.push(`# ${rule.frontmatter.description}
|
|
2223
|
+
`);
|
|
2224
|
+
}
|
|
2225
|
+
lines.push(rule.content.trim());
|
|
2226
|
+
return lines.join("\n");
|
|
2227
|
+
}
|
|
2228
|
+
// Complex generation handled by existing generator
|
|
2229
|
+
},
|
|
2230
|
+
opencode: {
|
|
2231
|
+
type: "complex",
|
|
2232
|
+
tool: "opencode",
|
|
2233
|
+
fileExtension: ".md",
|
|
2234
|
+
// ignoreFileName omitted - OpenCode doesn't use dedicated ignore files
|
|
2235
|
+
generateContent: (rule) => {
|
|
2236
|
+
const lines = [];
|
|
2237
|
+
if (rule.frontmatter.description) {
|
|
2238
|
+
lines.push(`# ${rule.frontmatter.description}
|
|
2222
2239
|
`);
|
|
2223
2240
|
}
|
|
2224
2241
|
lines.push(rule.content.trim());
|
|
@@ -2261,8 +2278,8 @@ async function generateFromRegistry(tool, rules, config, baseDir) {
|
|
|
2261
2278
|
const enhancedConfig = {
|
|
2262
2279
|
tool: generatorConfig.tool,
|
|
2263
2280
|
fileExtension: generatorConfig.fileExtension,
|
|
2264
|
-
ignoreFileName: generatorConfig.ignoreFileName,
|
|
2265
2281
|
generateContent: generatorConfig.generateContent,
|
|
2282
|
+
...generatorConfig.ignoreFileName && { ignoreFileName: generatorConfig.ignoreFileName },
|
|
2266
2283
|
...generatorConfig.generateRootContent && {
|
|
2267
2284
|
generateRootContent: generatorConfig.generateRootContent
|
|
2268
2285
|
},
|
|
@@ -2292,49 +2309,89 @@ var generateWindsurfConfig = createSimpleGenerator("windsurf");
|
|
|
2292
2309
|
var generateKiroConfig = createSimpleGenerator("kiro");
|
|
2293
2310
|
var generateRooConfig = createSimpleGenerator("roo");
|
|
2294
2311
|
|
|
2312
|
+
// src/utils/xml-document-generator.ts
|
|
2313
|
+
import { XMLBuilder } from "fast-xml-parser";
|
|
2314
|
+
function generateRootMarkdownWithXmlDocs(rootRule, memoryRules, config) {
|
|
2315
|
+
const lines = [];
|
|
2316
|
+
if (memoryRules.length > 0) {
|
|
2317
|
+
lines.push(
|
|
2318
|
+
"Please also reference the following documents as needed. In this case, `@` stands for the project root directory."
|
|
2319
|
+
);
|
|
2320
|
+
lines.push("");
|
|
2321
|
+
const documentsData = {
|
|
2322
|
+
Documents: {
|
|
2323
|
+
Document: memoryRules.map((rule) => {
|
|
2324
|
+
const relativePath = `@${config.memorySubDir}/${rule.filename}.md`;
|
|
2325
|
+
const document = {
|
|
2326
|
+
Path: relativePath,
|
|
2327
|
+
Description: rule.frontmatter.description
|
|
2328
|
+
};
|
|
2329
|
+
if (rule.frontmatter.globs.length > 0) {
|
|
2330
|
+
document.FilePatterns = rule.frontmatter.globs.join(", ");
|
|
2331
|
+
}
|
|
2332
|
+
return document;
|
|
2333
|
+
})
|
|
2334
|
+
}
|
|
2335
|
+
};
|
|
2336
|
+
const builder = new XMLBuilder({
|
|
2337
|
+
format: true,
|
|
2338
|
+
ignoreAttributes: false,
|
|
2339
|
+
suppressEmptyNode: false
|
|
2340
|
+
});
|
|
2341
|
+
const xmlContent = builder.build(documentsData);
|
|
2342
|
+
lines.push(xmlContent);
|
|
2343
|
+
lines.push("");
|
|
2344
|
+
lines.push("");
|
|
2345
|
+
}
|
|
2346
|
+
if (rootRule) {
|
|
2347
|
+
lines.push(rootRule.content.trim());
|
|
2348
|
+
} else if (memoryRules.length === 0) {
|
|
2349
|
+
lines.push(`# ${config.fallbackTitle}`);
|
|
2350
|
+
lines.push("");
|
|
2351
|
+
lines.push("No configuration rules have been defined yet.");
|
|
2352
|
+
}
|
|
2353
|
+
return lines.join("\n");
|
|
2354
|
+
}
|
|
2355
|
+
|
|
2295
2356
|
// src/generators/rules/codexcli.ts
|
|
2296
2357
|
async function generateCodexConfig(rules, config, baseDir) {
|
|
2297
2358
|
const outputs = [];
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
const sortedRules = [...rules].sort((a, b) => {
|
|
2302
|
-
if (a.frontmatter.root === true && b.frontmatter.root !== true) return -1;
|
|
2303
|
-
if (a.frontmatter.root !== true && b.frontmatter.root === true) return 1;
|
|
2304
|
-
return 0;
|
|
2305
|
-
});
|
|
2306
|
-
const concatenatedContent = generateConcatenatedCodexContent(sortedRules);
|
|
2307
|
-
if (concatenatedContent.trim()) {
|
|
2308
|
-
const outputDir = resolveOutputDir(config, "codexcli", baseDir);
|
|
2309
|
-
const filepath = `${outputDir}/AGENTS.md`;
|
|
2310
|
-
outputs.push({
|
|
2311
|
-
tool: "codexcli",
|
|
2312
|
-
filepath,
|
|
2313
|
-
content: concatenatedContent
|
|
2314
|
-
});
|
|
2315
|
-
}
|
|
2316
|
-
const ignorePatterns = await loadIgnorePatterns(baseDir);
|
|
2317
|
-
if (ignorePatterns.patterns.length > 0) {
|
|
2318
|
-
const ignorePath = resolvePath(".codexignore", baseDir);
|
|
2319
|
-
const ignoreContent = generateIgnoreFile2(ignorePatterns.patterns, "codexcli");
|
|
2320
|
-
outputs.push({
|
|
2359
|
+
const nonEmptyRules = rules.filter((rule) => rule.content.trim().length > 0);
|
|
2360
|
+
if (nonEmptyRules.length > 0) {
|
|
2361
|
+
const generatorConfig = {
|
|
2321
2362
|
tool: "codexcli",
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2363
|
+
fileExtension: ".md",
|
|
2364
|
+
ignoreFileName: ".codexignore",
|
|
2365
|
+
generateContent: generateCodexMemoryMarkdown,
|
|
2366
|
+
generateDetailContent: generateCodexMemoryMarkdown,
|
|
2367
|
+
generateRootContent: generateCodexRootMarkdown,
|
|
2368
|
+
rootFilePath: "AGENTS.md",
|
|
2369
|
+
detailSubDir: ".codex/memories"
|
|
2370
|
+
};
|
|
2371
|
+
const ruleOutputs = await generateComplexRules(nonEmptyRules, config, generatorConfig, baseDir);
|
|
2372
|
+
outputs.push(...ruleOutputs);
|
|
2373
|
+
} else {
|
|
2374
|
+
const ignorePatterns = await loadIgnorePatterns(baseDir);
|
|
2375
|
+
if (ignorePatterns.patterns.length > 0) {
|
|
2376
|
+
const ignorePath = resolvePath(".codexignore", baseDir);
|
|
2377
|
+
const ignoreContent = generateIgnoreFile2(ignorePatterns.patterns, "codexcli");
|
|
2378
|
+
outputs.push({
|
|
2379
|
+
tool: "codexcli",
|
|
2380
|
+
filepath: ignorePath,
|
|
2381
|
+
content: ignoreContent
|
|
2382
|
+
});
|
|
2383
|
+
}
|
|
2325
2384
|
}
|
|
2326
2385
|
return outputs;
|
|
2327
2386
|
}
|
|
2328
|
-
function
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
}
|
|
2337
|
-
return sections.join("\n\n---\n\n");
|
|
2387
|
+
function generateCodexMemoryMarkdown(rule) {
|
|
2388
|
+
return rule.content.trim();
|
|
2389
|
+
}
|
|
2390
|
+
function generateCodexRootMarkdown(rootRule, memoryRules, _baseDir) {
|
|
2391
|
+
return generateRootMarkdownWithXmlDocs(rootRule, memoryRules, {
|
|
2392
|
+
memorySubDir: ".codex/memories",
|
|
2393
|
+
fallbackTitle: "OpenAI Codex CLI Configuration"
|
|
2394
|
+
});
|
|
2338
2395
|
}
|
|
2339
2396
|
|
|
2340
2397
|
// src/generators/rules/geminicli.ts
|
|
@@ -2355,28 +2412,10 @@ function generateGeminiMemoryMarkdown(rule) {
|
|
|
2355
2412
|
return rule.content.trim();
|
|
2356
2413
|
}
|
|
2357
2414
|
function generateGeminiRootMarkdown(rootRule, memoryRules, _baseDir) {
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
lines.push("| Document | Description | File Patterns |");
|
|
2363
|
-
lines.push("|----------|-------------|---------------|");
|
|
2364
|
-
for (const rule of memoryRules) {
|
|
2365
|
-
const relativePath = `@.gemini/memories/${rule.filename}.md`;
|
|
2366
|
-
const filePatterns = rule.frontmatter.globs.length > 0 ? rule.frontmatter.globs.join(", ") : "-";
|
|
2367
|
-
lines.push(`| ${relativePath} | ${rule.frontmatter.description} | ${filePatterns} |`);
|
|
2368
|
-
}
|
|
2369
|
-
lines.push("");
|
|
2370
|
-
lines.push("");
|
|
2371
|
-
}
|
|
2372
|
-
if (rootRule) {
|
|
2373
|
-
lines.push(rootRule.content.trim());
|
|
2374
|
-
} else if (memoryRules.length === 0) {
|
|
2375
|
-
lines.push("# Gemini CLI Configuration");
|
|
2376
|
-
lines.push("");
|
|
2377
|
-
lines.push("No configuration rules have been defined yet.");
|
|
2378
|
-
}
|
|
2379
|
-
return lines.join("\n");
|
|
2415
|
+
return generateRootMarkdownWithXmlDocs(rootRule, memoryRules, {
|
|
2416
|
+
memorySubDir: ".gemini/memories",
|
|
2417
|
+
fallbackTitle: "Gemini CLI Configuration"
|
|
2418
|
+
});
|
|
2380
2419
|
}
|
|
2381
2420
|
|
|
2382
2421
|
// src/generators/rules/junie.ts
|
|
@@ -2406,6 +2445,30 @@ function generateGuidelinesMarkdown(rootRule, detailRules) {
|
|
|
2406
2445
|
return lines.join("\n").trim();
|
|
2407
2446
|
}
|
|
2408
2447
|
|
|
2448
|
+
// src/generators/rules/opencode.ts
|
|
2449
|
+
async function generateOpenCodeConfig(rules, config, baseDir) {
|
|
2450
|
+
const generatorConfig = {
|
|
2451
|
+
tool: "opencode",
|
|
2452
|
+
fileExtension: ".md",
|
|
2453
|
+
// ignoreFileName omitted - OpenCode doesn't use dedicated ignore files
|
|
2454
|
+
generateContent: generateOpenCodeMarkdown,
|
|
2455
|
+
generateDetailContent: generateOpenCodeMarkdown,
|
|
2456
|
+
generateRootContent: generateOpenCodeRootMarkdown,
|
|
2457
|
+
rootFilePath: "AGENTS.md",
|
|
2458
|
+
detailSubDir: ".opencode/memories"
|
|
2459
|
+
};
|
|
2460
|
+
return generateComplexRules(rules, config, generatorConfig, baseDir);
|
|
2461
|
+
}
|
|
2462
|
+
function generateOpenCodeMarkdown(rule) {
|
|
2463
|
+
return rule.content.trim();
|
|
2464
|
+
}
|
|
2465
|
+
function generateOpenCodeRootMarkdown(rootRule, memoryRules, _baseDir) {
|
|
2466
|
+
return generateRootMarkdownWithXmlDocs(rootRule, memoryRules, {
|
|
2467
|
+
memorySubDir: ".opencode/memories",
|
|
2468
|
+
fallbackTitle: "OpenCode Configuration"
|
|
2469
|
+
});
|
|
2470
|
+
}
|
|
2471
|
+
|
|
2409
2472
|
// src/core/generator.ts
|
|
2410
2473
|
async function generateConfigurations(rules, config, targetTools, baseDir) {
|
|
2411
2474
|
const outputs = createOutputsArray();
|
|
@@ -2438,6 +2501,8 @@ function filterRulesForTool(rules, tool, config) {
|
|
|
2438
2501
|
}
|
|
2439
2502
|
async function generateForTool(tool, rules, config, baseDir) {
|
|
2440
2503
|
switch (tool) {
|
|
2504
|
+
case "amazonqcli":
|
|
2505
|
+
return await generateAmazonqcliConfig(rules, config, baseDir);
|
|
2441
2506
|
case "augmentcode": {
|
|
2442
2507
|
const augmentRulesOutputs = await generateAugmentcodeConfig(rules, config, baseDir);
|
|
2443
2508
|
const augmentIgnoreOutputs = await generateAugmentCodeIgnoreFiles(rules, config, baseDir);
|
|
@@ -2476,6 +2541,8 @@ async function generateForTool(tool, rules, config, baseDir) {
|
|
|
2476
2541
|
const kiroIgnoreOutputs = await generateKiroIgnoreFiles(rules, config, baseDir);
|
|
2477
2542
|
return [...kiroRulesOutputs, ...kiroIgnoreOutputs];
|
|
2478
2543
|
}
|
|
2544
|
+
case "opencode":
|
|
2545
|
+
return generateOpenCodeConfig(rules, config, baseDir);
|
|
2479
2546
|
case "windsurf": {
|
|
2480
2547
|
const windsurfRulesOutputs = await generateWindsurfConfig(rules, config, baseDir);
|
|
2481
2548
|
const windsurfIgnoreOutputs = await generateWindsurfIgnore(rules, config, baseDir);
|
|
@@ -2639,30 +2706,63 @@ function parseMcpConfig(projectRoot) {
|
|
|
2639
2706
|
async function generateMcpConfigurations(mcpConfig, baseDir, targetTools) {
|
|
2640
2707
|
const outputs = [];
|
|
2641
2708
|
const toolMap = {
|
|
2642
|
-
|
|
2709
|
+
amazonqcli: async (servers, dir) => {
|
|
2710
|
+
const config = {
|
|
2711
|
+
aiRulesDir: ".rulesync",
|
|
2712
|
+
outputPaths: {
|
|
2713
|
+
amazonqcli: ".amazonq/rules",
|
|
2714
|
+
augmentcode: ".",
|
|
2715
|
+
"augmentcode-legacy": ".",
|
|
2716
|
+
copilot: ".github/instructions",
|
|
2717
|
+
cursor: ".cursor/rules",
|
|
2718
|
+
cline: ".clinerules",
|
|
2719
|
+
claudecode: ".",
|
|
2720
|
+
codexcli: ".",
|
|
2721
|
+
opencode: ".",
|
|
2722
|
+
roo: ".roo/rules",
|
|
2723
|
+
geminicli: ".gemini/memories",
|
|
2724
|
+
kiro: ".kiro/steering",
|
|
2725
|
+
junie: ".",
|
|
2726
|
+
windsurf: "."
|
|
2727
|
+
},
|
|
2728
|
+
watchEnabled: false,
|
|
2729
|
+
defaultTargets: []
|
|
2730
|
+
};
|
|
2731
|
+
const results = await (await import("./amazonqcli-WVGYACHI.js")).generateAmazonqcliMcp(
|
|
2732
|
+
servers,
|
|
2733
|
+
config,
|
|
2734
|
+
dir
|
|
2735
|
+
);
|
|
2736
|
+
return results.map((result) => ({ filepath: result.filepath, content: result.content }));
|
|
2737
|
+
},
|
|
2738
|
+
augmentcode: async (servers, dir) => (await import("./augmentcode-DTHPPXWO.js")).generateAugmentcodeMcpConfiguration(
|
|
2739
|
+
servers,
|
|
2740
|
+
dir
|
|
2741
|
+
),
|
|
2742
|
+
"augmentcode-legacy": async (servers, dir) => (await import("./augmentcode-DTHPPXWO.js")).generateAugmentcodeMcpConfiguration(
|
|
2643
2743
|
servers,
|
|
2644
2744
|
dir
|
|
2645
2745
|
),
|
|
2646
|
-
|
|
2746
|
+
claudecode: async (servers, dir) => (await import("./claudecode-SSYLLUXX.js")).generateClaudeMcpConfiguration(
|
|
2647
2747
|
servers,
|
|
2648
2748
|
dir
|
|
2649
2749
|
),
|
|
2650
|
-
|
|
2750
|
+
copilot: async (servers, dir) => (await import("./copilot-HSQO7ZCJ.js")).generateCopilotMcpConfiguration(servers, dir),
|
|
2751
|
+
cursor: async (servers, dir) => (await import("./cursor-ZB3XNGBK.js")).generateCursorMcpConfiguration(servers, dir),
|
|
2752
|
+
cline: async (servers, dir) => (await import("./cline-5EUGKNZ6.js")).generateClineMcpConfiguration(servers, dir),
|
|
2753
|
+
codexcli: async (servers, dir) => (await import("./codexcli-IGM2ADYK.js")).generateCodexMcpConfiguration(servers, dir),
|
|
2754
|
+
opencode: async (servers, dir) => (await import("./opencode-SZETJ62M.js")).generateOpenCodeMcpConfiguration(
|
|
2651
2755
|
servers,
|
|
2652
2756
|
dir
|
|
2653
2757
|
),
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
cline: async (servers, dir) => (await import("./cline-CKNUDEA3.js")).generateClineMcpConfiguration(servers, dir),
|
|
2657
|
-
codexcli: async (servers, dir) => (await import("./codexcli-7SDGYI7D.js")).generateCodexMcpConfiguration(servers, dir),
|
|
2658
|
-
roo: async (servers, dir) => (await import("./roo-L3QTTIPO.js")).generateRooMcpConfiguration(servers, dir),
|
|
2659
|
-
geminicli: async (servers, dir) => (await import("./geminicli-E7KZTZ2G.js")).generateGeminiCliMcpConfiguration(
|
|
2758
|
+
roo: async (servers, dir) => (await import("./roo-KLTWVAKE.js")).generateRooMcpConfiguration(servers, dir),
|
|
2759
|
+
geminicli: async (servers, dir) => (await import("./geminicli-FNRKH5GX.js")).generateGeminiCliMcpConfiguration(
|
|
2660
2760
|
servers,
|
|
2661
2761
|
dir
|
|
2662
2762
|
),
|
|
2663
|
-
kiro: async (servers, dir) => (await import("./kiro-
|
|
2664
|
-
junie: async (servers, dir) => (await import("./junie-
|
|
2665
|
-
windsurf: async (servers, dir) => (await import("./windsurf-
|
|
2763
|
+
kiro: async (servers, dir) => (await import("./kiro-B6WZNLY4.js")).generateKiroMcpConfiguration(servers, dir),
|
|
2764
|
+
junie: async (servers, dir) => (await import("./junie-3YGOSOGF.js")).generateJunieMcpConfiguration(servers, dir),
|
|
2765
|
+
windsurf: async (servers, dir) => (await import("./windsurf-IZEKUAID.js")).generateWindsurfMcpConfiguration(
|
|
2666
2766
|
servers,
|
|
2667
2767
|
dir
|
|
2668
2768
|
)
|
|
@@ -2747,13 +2847,13 @@ async function generateCommand(options = {}) {
|
|
|
2747
2847
|
logger.info("Deleting existing output directories...");
|
|
2748
2848
|
const targetTools = config.defaultTargets;
|
|
2749
2849
|
const deleteTasks = [];
|
|
2750
|
-
const commandsDir =
|
|
2850
|
+
const commandsDir = join12(config.aiRulesDir, "commands");
|
|
2751
2851
|
const hasCommands = await fileExists(commandsDir);
|
|
2752
2852
|
let hasCommandFiles = false;
|
|
2753
2853
|
if (hasCommands) {
|
|
2754
|
-
const { readdir
|
|
2854
|
+
const { readdir } = await import("fs/promises");
|
|
2755
2855
|
try {
|
|
2756
|
-
const files = await
|
|
2856
|
+
const files = await readdir(commandsDir);
|
|
2757
2857
|
hasCommandFiles = files.some((file) => file.endsWith(".md"));
|
|
2758
2858
|
} catch {
|
|
2759
2859
|
hasCommandFiles = false;
|
|
@@ -2762,12 +2862,12 @@ async function generateCommand(options = {}) {
|
|
|
2762
2862
|
for (const tool of targetTools) {
|
|
2763
2863
|
switch (tool) {
|
|
2764
2864
|
case "augmentcode":
|
|
2765
|
-
deleteTasks.push(removeDirectory(
|
|
2766
|
-
deleteTasks.push(removeDirectory(
|
|
2865
|
+
deleteTasks.push(removeDirectory(join12(".augment", "rules")));
|
|
2866
|
+
deleteTasks.push(removeDirectory(join12(".augment", "ignore")));
|
|
2767
2867
|
break;
|
|
2768
2868
|
case "augmentcode-legacy":
|
|
2769
2869
|
deleteTasks.push(removeClaudeGeneratedFiles());
|
|
2770
|
-
deleteTasks.push(removeDirectory(
|
|
2870
|
+
deleteTasks.push(removeDirectory(join12(".augment", "ignore")));
|
|
2771
2871
|
break;
|
|
2772
2872
|
case "copilot":
|
|
2773
2873
|
deleteTasks.push(removeDirectory(config.outputPaths.copilot));
|
|
@@ -2781,24 +2881,27 @@ async function generateCommand(options = {}) {
|
|
|
2781
2881
|
case "claudecode":
|
|
2782
2882
|
deleteTasks.push(removeClaudeGeneratedFiles());
|
|
2783
2883
|
if (hasCommandFiles) {
|
|
2784
|
-
deleteTasks.push(removeDirectory(
|
|
2884
|
+
deleteTasks.push(removeDirectory(join12(".claude", "commands")));
|
|
2785
2885
|
}
|
|
2786
2886
|
break;
|
|
2787
2887
|
case "roo":
|
|
2788
2888
|
deleteTasks.push(removeDirectory(config.outputPaths.roo));
|
|
2789
2889
|
if (hasCommandFiles) {
|
|
2790
|
-
deleteTasks.push(removeDirectory(
|
|
2890
|
+
deleteTasks.push(removeDirectory(join12(".roo", "commands")));
|
|
2791
2891
|
}
|
|
2792
2892
|
break;
|
|
2793
2893
|
case "geminicli":
|
|
2794
2894
|
deleteTasks.push(removeDirectory(config.outputPaths.geminicli));
|
|
2795
2895
|
if (hasCommandFiles) {
|
|
2796
|
-
deleteTasks.push(removeDirectory(
|
|
2896
|
+
deleteTasks.push(removeDirectory(join12(".gemini", "commands")));
|
|
2797
2897
|
}
|
|
2798
2898
|
break;
|
|
2799
2899
|
case "kiro":
|
|
2800
2900
|
deleteTasks.push(removeDirectory(config.outputPaths.kiro));
|
|
2801
2901
|
break;
|
|
2902
|
+
case "opencode":
|
|
2903
|
+
deleteTasks.push(removeDirectory(config.outputPaths.opencode));
|
|
2904
|
+
break;
|
|
2802
2905
|
case "windsurf":
|
|
2803
2906
|
deleteTasks.push(removeDirectory(config.outputPaths.windsurf));
|
|
2804
2907
|
break;
|
|
@@ -2892,11 +2995,13 @@ Generating configurations for base directory: ${baseDir}`);
|
|
|
2892
2995
|
|
|
2893
2996
|
// src/cli/commands/gitignore.ts
|
|
2894
2997
|
import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
2895
|
-
import { join as
|
|
2998
|
+
import { join as join13 } from "path";
|
|
2896
2999
|
var gitignoreCommand = async () => {
|
|
2897
|
-
const gitignorePath =
|
|
3000
|
+
const gitignorePath = join13(process.cwd(), ".gitignore");
|
|
2898
3001
|
const rulesFilesToIgnore = [
|
|
2899
3002
|
"# Generated by rulesync - AI tool configuration files",
|
|
3003
|
+
"**/.amazonq/rules/",
|
|
3004
|
+
"**/.amazonq/mcp.json",
|
|
2900
3005
|
"**/.github/copilot-instructions.md",
|
|
2901
3006
|
"**/.github/instructions/",
|
|
2902
3007
|
"**/.cursor/rules/",
|
|
@@ -2922,6 +3027,9 @@ var gitignoreCommand = async () => {
|
|
|
2922
3027
|
"**/.augment-guidelines",
|
|
2923
3028
|
"**/.junie/guidelines.md",
|
|
2924
3029
|
"**/.noai",
|
|
3030
|
+
"**/.opencode/memories/",
|
|
3031
|
+
"**/.opencode/commands/",
|
|
3032
|
+
"**/opencode.json",
|
|
2925
3033
|
"**/.mcp.json",
|
|
2926
3034
|
"!.rulesync/.mcp.json",
|
|
2927
3035
|
"**/.cursor/mcp.json",
|
|
@@ -2960,162 +3068,11 @@ ${linesToAdd.join("\n")}
|
|
|
2960
3068
|
};
|
|
2961
3069
|
|
|
2962
3070
|
// src/core/importer.ts
|
|
2963
|
-
import { join as
|
|
3071
|
+
import { join as join20 } from "path";
|
|
2964
3072
|
import matter2 from "gray-matter";
|
|
2965
3073
|
|
|
2966
|
-
// src/parsers/augmentcode.ts
|
|
2967
|
-
import { basename as basename3, join as join15 } from "path";
|
|
2968
|
-
|
|
2969
|
-
// src/utils/parser-helpers.ts
|
|
2970
|
-
function createParseResult() {
|
|
2971
|
-
return { rules: [], errors: [] };
|
|
2972
|
-
}
|
|
2973
|
-
function addError(result, error) {
|
|
2974
|
-
result.errors.push(error);
|
|
2975
|
-
}
|
|
2976
|
-
function addRule(result, rule) {
|
|
2977
|
-
if (!result.rules) {
|
|
2978
|
-
result.rules = [];
|
|
2979
|
-
}
|
|
2980
|
-
result.rules.push(rule);
|
|
2981
|
-
}
|
|
2982
|
-
function addRules(result, rules) {
|
|
2983
|
-
if (!result.rules) {
|
|
2984
|
-
result.rules = [];
|
|
2985
|
-
}
|
|
2986
|
-
result.rules.push(...rules);
|
|
2987
|
-
}
|
|
2988
|
-
async function safeReadFile(operation, errorContext) {
|
|
2989
|
-
try {
|
|
2990
|
-
const result = await operation();
|
|
2991
|
-
return createSuccessResult(result);
|
|
2992
|
-
} catch (error) {
|
|
2993
|
-
return createErrorResult(error, errorContext);
|
|
2994
|
-
}
|
|
2995
|
-
}
|
|
2996
|
-
|
|
2997
|
-
// src/parsers/augmentcode.ts
|
|
2998
|
-
async function parseAugmentcodeConfiguration(baseDir = process.cwd()) {
|
|
2999
|
-
return parseUnifiedAugmentcode(baseDir, {
|
|
3000
|
-
rulesDir: ".augment/rules",
|
|
3001
|
-
targetName: "augmentcode",
|
|
3002
|
-
filenamePrefix: "augmentcode"
|
|
3003
|
-
});
|
|
3004
|
-
}
|
|
3005
|
-
async function parseAugmentcodeLegacyConfiguration(baseDir = process.cwd()) {
|
|
3006
|
-
return parseUnifiedAugmentcode(baseDir, {
|
|
3007
|
-
legacyFilePath: ".augment-guidelines",
|
|
3008
|
-
targetName: "augmentcode-legacy",
|
|
3009
|
-
filenamePrefix: "augmentcode-legacy"
|
|
3010
|
-
});
|
|
3011
|
-
}
|
|
3012
|
-
async function parseUnifiedAugmentcode(baseDir, config) {
|
|
3013
|
-
const result = createParseResult();
|
|
3014
|
-
if (config.rulesDir) {
|
|
3015
|
-
const rulesDir = join15(baseDir, config.rulesDir);
|
|
3016
|
-
if (await fileExists(rulesDir)) {
|
|
3017
|
-
const rulesResult = await parseAugmentRules(rulesDir, config);
|
|
3018
|
-
addRules(result, rulesResult.rules);
|
|
3019
|
-
result.errors.push(...rulesResult.errors);
|
|
3020
|
-
} else {
|
|
3021
|
-
addError(
|
|
3022
|
-
result,
|
|
3023
|
-
`No AugmentCode configuration found. Expected ${config.rulesDir} directory.`
|
|
3024
|
-
);
|
|
3025
|
-
}
|
|
3026
|
-
}
|
|
3027
|
-
if (config.legacyFilePath) {
|
|
3028
|
-
const legacyPath = join15(baseDir, config.legacyFilePath);
|
|
3029
|
-
if (await fileExists(legacyPath)) {
|
|
3030
|
-
const legacyResult = await parseAugmentGuidelines(legacyPath, config);
|
|
3031
|
-
if (legacyResult.rule) {
|
|
3032
|
-
addRule(result, legacyResult.rule);
|
|
3033
|
-
}
|
|
3034
|
-
result.errors.push(...legacyResult.errors);
|
|
3035
|
-
} else {
|
|
3036
|
-
addError(
|
|
3037
|
-
result,
|
|
3038
|
-
`No AugmentCode legacy configuration found. Expected ${config.legacyFilePath} file.`
|
|
3039
|
-
);
|
|
3040
|
-
}
|
|
3041
|
-
}
|
|
3042
|
-
return { rules: result.rules || [], errors: result.errors };
|
|
3043
|
-
}
|
|
3044
|
-
async function parseAugmentRules(rulesDir, config) {
|
|
3045
|
-
const rules = [];
|
|
3046
|
-
const errors = [];
|
|
3047
|
-
try {
|
|
3048
|
-
const { readdir: readdir2 } = await import("fs/promises");
|
|
3049
|
-
const files = await readdir2(rulesDir);
|
|
3050
|
-
for (const file of files) {
|
|
3051
|
-
if (file.endsWith(".md") || file.endsWith(".mdc")) {
|
|
3052
|
-
const filePath = join15(rulesDir, file);
|
|
3053
|
-
try {
|
|
3054
|
-
const rawContent = await readFileContent(filePath);
|
|
3055
|
-
const parsed = parseFrontmatter(rawContent);
|
|
3056
|
-
const ruleType = extractStringField(parsed.data, "type", "manual");
|
|
3057
|
-
const description = extractStringField(parsed.data, "description", "");
|
|
3058
|
-
const tags = extractArrayField(parsed.data, "tags");
|
|
3059
|
-
const isRoot = ruleType === "always";
|
|
3060
|
-
const filename = basename3(file, file.endsWith(".mdc") ? ".mdc" : ".md");
|
|
3061
|
-
const frontmatter = {
|
|
3062
|
-
root: isRoot,
|
|
3063
|
-
targets: [config.targetName],
|
|
3064
|
-
description,
|
|
3065
|
-
globs: ["**/*"],
|
|
3066
|
-
// AugmentCode doesn't use specific globs in the same way
|
|
3067
|
-
...tags.length > 0 && { tags }
|
|
3068
|
-
};
|
|
3069
|
-
rules.push({
|
|
3070
|
-
frontmatter,
|
|
3071
|
-
content: parsed.content.trim(),
|
|
3072
|
-
filename: `${config.filenamePrefix}-${ruleType}-${filename}`,
|
|
3073
|
-
filepath: filePath
|
|
3074
|
-
});
|
|
3075
|
-
} catch (error) {
|
|
3076
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
3077
|
-
errors.push(`Failed to parse ${filePath}: ${errorMessage}`);
|
|
3078
|
-
}
|
|
3079
|
-
}
|
|
3080
|
-
}
|
|
3081
|
-
} catch (error) {
|
|
3082
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
3083
|
-
errors.push(`Failed to read ${config.rulesDir || rulesDir} directory: ${errorMessage}`);
|
|
3084
|
-
}
|
|
3085
|
-
return { rules, errors };
|
|
3086
|
-
}
|
|
3087
|
-
async function parseAugmentGuidelines(guidelinesPath, config) {
|
|
3088
|
-
const parseResult = await safeReadFile(
|
|
3089
|
-
async () => {
|
|
3090
|
-
const content = await readFileContent(guidelinesPath);
|
|
3091
|
-
if (content.trim()) {
|
|
3092
|
-
const frontmatter = {
|
|
3093
|
-
root: true,
|
|
3094
|
-
// Legacy guidelines become root rules
|
|
3095
|
-
targets: [config.targetName],
|
|
3096
|
-
description: "Legacy AugmentCode guidelines",
|
|
3097
|
-
globs: ["**/*"]
|
|
3098
|
-
};
|
|
3099
|
-
return {
|
|
3100
|
-
frontmatter,
|
|
3101
|
-
content: content.trim(),
|
|
3102
|
-
filename: `${config.filenamePrefix}-guidelines`,
|
|
3103
|
-
filepath: guidelinesPath
|
|
3104
|
-
};
|
|
3105
|
-
}
|
|
3106
|
-
return null;
|
|
3107
|
-
},
|
|
3108
|
-
`Failed to parse ${config.legacyFilePath || guidelinesPath}`
|
|
3109
|
-
);
|
|
3110
|
-
if (parseResult.success) {
|
|
3111
|
-
return { rule: parseResult.result || null, errors: [] };
|
|
3112
|
-
} else {
|
|
3113
|
-
return { rule: null, errors: [parseResult.error || "Unknown error"] };
|
|
3114
|
-
}
|
|
3115
|
-
}
|
|
3116
|
-
|
|
3117
3074
|
// src/parsers/shared-helpers.ts
|
|
3118
|
-
import { basename as
|
|
3075
|
+
import { basename as basename3, join as join14 } from "path";
|
|
3119
3076
|
async function parseConfigurationFiles(baseDir = process.cwd(), config) {
|
|
3120
3077
|
const errors = [];
|
|
3121
3078
|
const rules = [];
|
|
@@ -3168,11 +3125,11 @@ async function parseConfigurationFiles(baseDir = process.cwd(), config) {
|
|
|
3168
3125
|
const dirPath = resolvePath(dirConfig.directory, baseDir);
|
|
3169
3126
|
if (await fileExists(dirPath)) {
|
|
3170
3127
|
const result = await safeAsyncOperation(async () => {
|
|
3171
|
-
const { readdir
|
|
3172
|
-
const files = await
|
|
3128
|
+
const { readdir } = await import("fs/promises");
|
|
3129
|
+
const files = await readdir(dirPath);
|
|
3173
3130
|
for (const file of files) {
|
|
3174
3131
|
if (file.endsWith(dirConfig.filePattern)) {
|
|
3175
|
-
const filePath =
|
|
3132
|
+
const filePath = join14(dirPath, file);
|
|
3176
3133
|
const fileResult = await safeAsyncOperation(async () => {
|
|
3177
3134
|
const rawContent = await readFileContent(filePath);
|
|
3178
3135
|
let content;
|
|
@@ -3319,14 +3276,14 @@ function parseMainFile(content, filepath, config) {
|
|
|
3319
3276
|
async function parseMemoryFiles(memoryDir, config) {
|
|
3320
3277
|
const rules = [];
|
|
3321
3278
|
try {
|
|
3322
|
-
const { readdir
|
|
3323
|
-
const files = await
|
|
3279
|
+
const { readdir } = await import("fs/promises");
|
|
3280
|
+
const files = await readdir(memoryDir);
|
|
3324
3281
|
for (const file of files) {
|
|
3325
3282
|
if (file.endsWith(".md")) {
|
|
3326
|
-
const filePath =
|
|
3283
|
+
const filePath = join14(memoryDir, file);
|
|
3327
3284
|
const content = await readFileContent(filePath);
|
|
3328
3285
|
if (content.trim()) {
|
|
3329
|
-
const filename =
|
|
3286
|
+
const filename = basename3(file, ".md");
|
|
3330
3287
|
const frontmatter = {
|
|
3331
3288
|
root: false,
|
|
3332
3289
|
targets: [config.tool],
|
|
@@ -3349,14 +3306,14 @@ async function parseMemoryFiles(memoryDir, config) {
|
|
|
3349
3306
|
async function parseCommandsFiles(commandsDir, config) {
|
|
3350
3307
|
const rules = [];
|
|
3351
3308
|
try {
|
|
3352
|
-
const { readdir
|
|
3353
|
-
const files = await
|
|
3309
|
+
const { readdir } = await import("fs/promises");
|
|
3310
|
+
const files = await readdir(commandsDir);
|
|
3354
3311
|
for (const file of files) {
|
|
3355
3312
|
if (file.endsWith(".md")) {
|
|
3356
|
-
const filePath =
|
|
3313
|
+
const filePath = join14(commandsDir, file);
|
|
3357
3314
|
const content = await readFileContent(filePath);
|
|
3358
3315
|
if (content.trim()) {
|
|
3359
|
-
const filename =
|
|
3316
|
+
const filename = basename3(file, ".md");
|
|
3360
3317
|
let frontmatter;
|
|
3361
3318
|
let ruleContent;
|
|
3362
3319
|
try {
|
|
@@ -3431,6 +3388,170 @@ async function parseSettingsFile(settingsPath, tool) {
|
|
|
3431
3388
|
};
|
|
3432
3389
|
}
|
|
3433
3390
|
|
|
3391
|
+
// src/parsers/amazonqcli.ts
|
|
3392
|
+
async function parseAmazonqcliConfiguration(baseDir = process.cwd()) {
|
|
3393
|
+
return parseMemoryBasedConfiguration(baseDir, {
|
|
3394
|
+
tool: "amazonqcli",
|
|
3395
|
+
mainFileName: ".amazonq/rules/main.md",
|
|
3396
|
+
memoryDirPath: ".amazonq/rules",
|
|
3397
|
+
settingsPath: ".amazonq/mcp.json",
|
|
3398
|
+
mainDescription: "Main Amazon Q Developer CLI configuration",
|
|
3399
|
+
memoryDescription: "Amazon Q rule",
|
|
3400
|
+
filenamePrefix: "amazonq"
|
|
3401
|
+
});
|
|
3402
|
+
}
|
|
3403
|
+
|
|
3404
|
+
// src/parsers/augmentcode.ts
|
|
3405
|
+
import { basename as basename4, join as join15 } from "path";
|
|
3406
|
+
|
|
3407
|
+
// src/utils/parser-helpers.ts
|
|
3408
|
+
function createParseResult() {
|
|
3409
|
+
return { rules: [], errors: [] };
|
|
3410
|
+
}
|
|
3411
|
+
function addError(result, error) {
|
|
3412
|
+
result.errors.push(error);
|
|
3413
|
+
}
|
|
3414
|
+
function addRule(result, rule) {
|
|
3415
|
+
if (!result.rules) {
|
|
3416
|
+
result.rules = [];
|
|
3417
|
+
}
|
|
3418
|
+
result.rules.push(rule);
|
|
3419
|
+
}
|
|
3420
|
+
function addRules(result, rules) {
|
|
3421
|
+
if (!result.rules) {
|
|
3422
|
+
result.rules = [];
|
|
3423
|
+
}
|
|
3424
|
+
result.rules.push(...rules);
|
|
3425
|
+
}
|
|
3426
|
+
async function safeReadFile(operation, errorContext) {
|
|
3427
|
+
try {
|
|
3428
|
+
const result = await operation();
|
|
3429
|
+
return createSuccessResult(result);
|
|
3430
|
+
} catch (error) {
|
|
3431
|
+
return createErrorResult(error, errorContext);
|
|
3432
|
+
}
|
|
3433
|
+
}
|
|
3434
|
+
|
|
3435
|
+
// src/parsers/augmentcode.ts
|
|
3436
|
+
async function parseAugmentcodeConfiguration(baseDir = process.cwd()) {
|
|
3437
|
+
return parseUnifiedAugmentcode(baseDir, {
|
|
3438
|
+
rulesDir: ".augment/rules",
|
|
3439
|
+
targetName: "augmentcode",
|
|
3440
|
+
filenamePrefix: "augmentcode"
|
|
3441
|
+
});
|
|
3442
|
+
}
|
|
3443
|
+
async function parseAugmentcodeLegacyConfiguration(baseDir = process.cwd()) {
|
|
3444
|
+
return parseUnifiedAugmentcode(baseDir, {
|
|
3445
|
+
legacyFilePath: ".augment-guidelines",
|
|
3446
|
+
targetName: "augmentcode-legacy",
|
|
3447
|
+
filenamePrefix: "augmentcode-legacy"
|
|
3448
|
+
});
|
|
3449
|
+
}
|
|
3450
|
+
async function parseUnifiedAugmentcode(baseDir, config) {
|
|
3451
|
+
const result = createParseResult();
|
|
3452
|
+
if (config.rulesDir) {
|
|
3453
|
+
const rulesDir = join15(baseDir, config.rulesDir);
|
|
3454
|
+
if (await fileExists(rulesDir)) {
|
|
3455
|
+
const rulesResult = await parseAugmentRules(rulesDir, config);
|
|
3456
|
+
addRules(result, rulesResult.rules);
|
|
3457
|
+
result.errors.push(...rulesResult.errors);
|
|
3458
|
+
} else {
|
|
3459
|
+
addError(
|
|
3460
|
+
result,
|
|
3461
|
+
`No AugmentCode configuration found. Expected ${config.rulesDir} directory.`
|
|
3462
|
+
);
|
|
3463
|
+
}
|
|
3464
|
+
}
|
|
3465
|
+
if (config.legacyFilePath) {
|
|
3466
|
+
const legacyPath = join15(baseDir, config.legacyFilePath);
|
|
3467
|
+
if (await fileExists(legacyPath)) {
|
|
3468
|
+
const legacyResult = await parseAugmentGuidelines(legacyPath, config);
|
|
3469
|
+
if (legacyResult.rule) {
|
|
3470
|
+
addRule(result, legacyResult.rule);
|
|
3471
|
+
}
|
|
3472
|
+
result.errors.push(...legacyResult.errors);
|
|
3473
|
+
} else {
|
|
3474
|
+
addError(
|
|
3475
|
+
result,
|
|
3476
|
+
`No AugmentCode legacy configuration found. Expected ${config.legacyFilePath} file.`
|
|
3477
|
+
);
|
|
3478
|
+
}
|
|
3479
|
+
}
|
|
3480
|
+
return { rules: result.rules || [], errors: result.errors };
|
|
3481
|
+
}
|
|
3482
|
+
async function parseAugmentRules(rulesDir, config) {
|
|
3483
|
+
const rules = [];
|
|
3484
|
+
const errors = [];
|
|
3485
|
+
try {
|
|
3486
|
+
const { readdir } = await import("fs/promises");
|
|
3487
|
+
const files = await readdir(rulesDir);
|
|
3488
|
+
for (const file of files) {
|
|
3489
|
+
if (file.endsWith(".md") || file.endsWith(".mdc")) {
|
|
3490
|
+
const filePath = join15(rulesDir, file);
|
|
3491
|
+
try {
|
|
3492
|
+
const rawContent = await readFileContent(filePath);
|
|
3493
|
+
const parsed = parseFrontmatter(rawContent);
|
|
3494
|
+
const ruleType = extractStringField(parsed.data, "type", "manual");
|
|
3495
|
+
const description = extractStringField(parsed.data, "description", "");
|
|
3496
|
+
const tags = extractArrayField(parsed.data, "tags");
|
|
3497
|
+
const isRoot = ruleType === "always";
|
|
3498
|
+
const filename = basename4(file, file.endsWith(".mdc") ? ".mdc" : ".md");
|
|
3499
|
+
const frontmatter = {
|
|
3500
|
+
root: isRoot,
|
|
3501
|
+
targets: [config.targetName],
|
|
3502
|
+
description,
|
|
3503
|
+
globs: ["**/*"],
|
|
3504
|
+
// AugmentCode doesn't use specific globs in the same way
|
|
3505
|
+
...tags.length > 0 && { tags }
|
|
3506
|
+
};
|
|
3507
|
+
rules.push({
|
|
3508
|
+
frontmatter,
|
|
3509
|
+
content: parsed.content.trim(),
|
|
3510
|
+
filename: `${config.filenamePrefix}-${ruleType}-${filename}`,
|
|
3511
|
+
filepath: filePath
|
|
3512
|
+
});
|
|
3513
|
+
} catch (error) {
|
|
3514
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
3515
|
+
errors.push(`Failed to parse ${filePath}: ${errorMessage}`);
|
|
3516
|
+
}
|
|
3517
|
+
}
|
|
3518
|
+
}
|
|
3519
|
+
} catch (error) {
|
|
3520
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
3521
|
+
errors.push(`Failed to read ${config.rulesDir || rulesDir} directory: ${errorMessage}`);
|
|
3522
|
+
}
|
|
3523
|
+
return { rules, errors };
|
|
3524
|
+
}
|
|
3525
|
+
async function parseAugmentGuidelines(guidelinesPath, config) {
|
|
3526
|
+
const parseResult = await safeReadFile(
|
|
3527
|
+
async () => {
|
|
3528
|
+
const content = await readFileContent(guidelinesPath);
|
|
3529
|
+
if (content.trim()) {
|
|
3530
|
+
const frontmatter = {
|
|
3531
|
+
root: true,
|
|
3532
|
+
// Legacy guidelines become root rules
|
|
3533
|
+
targets: [config.targetName],
|
|
3534
|
+
description: "Legacy AugmentCode guidelines",
|
|
3535
|
+
globs: ["**/*"]
|
|
3536
|
+
};
|
|
3537
|
+
return {
|
|
3538
|
+
frontmatter,
|
|
3539
|
+
content: content.trim(),
|
|
3540
|
+
filename: `${config.filenamePrefix}-guidelines`,
|
|
3541
|
+
filepath: guidelinesPath
|
|
3542
|
+
};
|
|
3543
|
+
}
|
|
3544
|
+
return null;
|
|
3545
|
+
},
|
|
3546
|
+
`Failed to parse ${config.legacyFilePath || guidelinesPath}`
|
|
3547
|
+
);
|
|
3548
|
+
if (parseResult.success) {
|
|
3549
|
+
return { rule: parseResult.result || null, errors: [] };
|
|
3550
|
+
} else {
|
|
3551
|
+
return { rule: null, errors: [parseResult.error || "Unknown error"] };
|
|
3552
|
+
}
|
|
3553
|
+
}
|
|
3554
|
+
|
|
3434
3555
|
// src/parsers/claudecode.ts
|
|
3435
3556
|
async function parseClaudeConfiguration(baseDir = process.cwd()) {
|
|
3436
3557
|
return parseMemoryBasedConfiguration(baseDir, {
|
|
@@ -3466,7 +3587,7 @@ async function parseClineConfiguration(baseDir = process.cwd()) {
|
|
|
3466
3587
|
}
|
|
3467
3588
|
|
|
3468
3589
|
// src/parsers/codexcli.ts
|
|
3469
|
-
import { join as
|
|
3590
|
+
import { join as join16 } from "path";
|
|
3470
3591
|
|
|
3471
3592
|
// src/parsers/copilot.ts
|
|
3472
3593
|
async function parseCopilotConfiguration(baseDir = process.cwd()) {
|
|
@@ -3489,7 +3610,7 @@ async function parseCopilotConfiguration(baseDir = process.cwd()) {
|
|
|
3489
3610
|
}
|
|
3490
3611
|
|
|
3491
3612
|
// src/parsers/cursor.ts
|
|
3492
|
-
import { basename as basename5, join as
|
|
3613
|
+
import { basename as basename5, join as join17 } from "path";
|
|
3493
3614
|
import { DEFAULT_SCHEMA, FAILSAFE_SCHEMA, load } from "js-yaml";
|
|
3494
3615
|
import { z as z7 } from "zod/mini";
|
|
3495
3616
|
var customMatterOptions = {
|
|
@@ -3613,7 +3734,7 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
|
|
|
3613
3734
|
const rules = [];
|
|
3614
3735
|
let ignorePatterns;
|
|
3615
3736
|
let mcpServers;
|
|
3616
|
-
const cursorFilePath =
|
|
3737
|
+
const cursorFilePath = join17(baseDir, ".cursorrules");
|
|
3617
3738
|
if (await fileExists(cursorFilePath)) {
|
|
3618
3739
|
try {
|
|
3619
3740
|
const rawContent = await readFileContent(cursorFilePath);
|
|
@@ -3634,14 +3755,14 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
|
|
|
3634
3755
|
errors.push(`Failed to parse .cursorrules file: ${errorMessage}`);
|
|
3635
3756
|
}
|
|
3636
3757
|
}
|
|
3637
|
-
const cursorRulesDir =
|
|
3758
|
+
const cursorRulesDir = join17(baseDir, ".cursor", "rules");
|
|
3638
3759
|
if (await fileExists(cursorRulesDir)) {
|
|
3639
3760
|
try {
|
|
3640
|
-
const { readdir
|
|
3641
|
-
const files = await
|
|
3761
|
+
const { readdir } = await import("fs/promises");
|
|
3762
|
+
const files = await readdir(cursorRulesDir);
|
|
3642
3763
|
for (const file of files) {
|
|
3643
3764
|
if (file.endsWith(".mdc")) {
|
|
3644
|
-
const filePath =
|
|
3765
|
+
const filePath = join17(cursorRulesDir, file);
|
|
3645
3766
|
try {
|
|
3646
3767
|
const rawContent = await readFileContent(filePath);
|
|
3647
3768
|
const parsed = parseFrontmatter(rawContent, { matterOptions: customMatterOptions });
|
|
@@ -3670,7 +3791,7 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
|
|
|
3670
3791
|
if (rules.length === 0) {
|
|
3671
3792
|
errors.push("No Cursor configuration files found (.cursorrules or .cursor/rules/*.mdc)");
|
|
3672
3793
|
}
|
|
3673
|
-
const cursorIgnorePath =
|
|
3794
|
+
const cursorIgnorePath = join17(baseDir, ".cursorignore");
|
|
3674
3795
|
if (await fileExists(cursorIgnorePath)) {
|
|
3675
3796
|
try {
|
|
3676
3797
|
const content = await readFileContent(cursorIgnorePath);
|
|
@@ -3683,7 +3804,7 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
|
|
|
3683
3804
|
errors.push(`Failed to parse .cursorignore: ${errorMessage}`);
|
|
3684
3805
|
}
|
|
3685
3806
|
}
|
|
3686
|
-
const cursorMcpPath =
|
|
3807
|
+
const cursorMcpPath = join17(baseDir, ".cursor", "mcp.json");
|
|
3687
3808
|
if (await fileExists(cursorMcpPath)) {
|
|
3688
3809
|
try {
|
|
3689
3810
|
const content = await readFileContent(cursorMcpPath);
|
|
@@ -3733,11 +3854,11 @@ async function parseGeminiConfiguration(baseDir = process.cwd()) {
|
|
|
3733
3854
|
}
|
|
3734
3855
|
|
|
3735
3856
|
// src/parsers/junie.ts
|
|
3736
|
-
import { join as
|
|
3857
|
+
import { join as join18 } from "path";
|
|
3737
3858
|
async function parseJunieConfiguration(baseDir = process.cwd()) {
|
|
3738
3859
|
const errors = [];
|
|
3739
3860
|
const rules = [];
|
|
3740
|
-
const guidelinesPath =
|
|
3861
|
+
const guidelinesPath = join18(baseDir, ".junie", "guidelines.md");
|
|
3741
3862
|
if (!await fileExists(guidelinesPath)) {
|
|
3742
3863
|
errors.push(".junie/guidelines.md file not found");
|
|
3743
3864
|
return { rules, errors };
|
|
@@ -3768,6 +3889,32 @@ async function parseJunieConfiguration(baseDir = process.cwd()) {
|
|
|
3768
3889
|
return { rules, errors };
|
|
3769
3890
|
}
|
|
3770
3891
|
|
|
3892
|
+
// src/parsers/opencode.ts
|
|
3893
|
+
async function parseOpCodeIgnore(opcodeignorePath) {
|
|
3894
|
+
try {
|
|
3895
|
+
const content = await readFileContent(opcodeignorePath);
|
|
3896
|
+
const patterns = content.split("\n").map((line) => line.trim()).filter((line) => line && !line.startsWith("#"));
|
|
3897
|
+
return patterns;
|
|
3898
|
+
} catch {
|
|
3899
|
+
return [];
|
|
3900
|
+
}
|
|
3901
|
+
}
|
|
3902
|
+
async function parseOpenCodeConfiguration(baseDir = process.cwd()) {
|
|
3903
|
+
return parseMemoryBasedConfiguration(baseDir, {
|
|
3904
|
+
tool: "opencode",
|
|
3905
|
+
mainFileName: "AGENTS.md",
|
|
3906
|
+
memoryDirPath: ".opencode/memories",
|
|
3907
|
+
settingsPath: "opencode.json",
|
|
3908
|
+
mainDescription: "Main OpenCode configuration",
|
|
3909
|
+
memoryDescription: "Memory file",
|
|
3910
|
+
filenamePrefix: "opencode",
|
|
3911
|
+
additionalIgnoreFile: {
|
|
3912
|
+
path: ".opcodeignore",
|
|
3913
|
+
parser: parseOpCodeIgnore
|
|
3914
|
+
}
|
|
3915
|
+
});
|
|
3916
|
+
}
|
|
3917
|
+
|
|
3771
3918
|
// src/parsers/roo.ts
|
|
3772
3919
|
async function parseRooConfiguration(baseDir = process.cwd()) {
|
|
3773
3920
|
return parseConfigurationFiles(baseDir, {
|
|
@@ -3789,8 +3936,8 @@ async function parseRooConfiguration(baseDir = process.cwd()) {
|
|
|
3789
3936
|
}
|
|
3790
3937
|
|
|
3791
3938
|
// src/parsers/windsurf.ts
|
|
3792
|
-
import { readFile
|
|
3793
|
-
import { join as
|
|
3939
|
+
import { readFile } from "fs/promises";
|
|
3940
|
+
import { join as join19 } from "path";
|
|
3794
3941
|
|
|
3795
3942
|
// src/core/importer.ts
|
|
3796
3943
|
async function importConfiguration(options) {
|
|
@@ -3810,6 +3957,13 @@ async function importConfiguration(options) {
|
|
|
3810
3957
|
}
|
|
3811
3958
|
try {
|
|
3812
3959
|
switch (tool) {
|
|
3960
|
+
case "amazonqcli": {
|
|
3961
|
+
const amazonqResult = await parseAmazonqcliConfiguration(baseDir);
|
|
3962
|
+
rules = amazonqResult.rules;
|
|
3963
|
+
errors.push(...amazonqResult.errors);
|
|
3964
|
+
mcpServers = amazonqResult.mcpServers;
|
|
3965
|
+
break;
|
|
3966
|
+
}
|
|
3813
3967
|
case "augmentcode": {
|
|
3814
3968
|
const augmentResult = await parseAugmentcodeConfiguration(baseDir);
|
|
3815
3969
|
rules = augmentResult.rules;
|
|
@@ -3870,6 +4024,14 @@ async function importConfiguration(options) {
|
|
|
3870
4024
|
errors.push(...junieResult.errors);
|
|
3871
4025
|
break;
|
|
3872
4026
|
}
|
|
4027
|
+
case "opencode": {
|
|
4028
|
+
const opencodeResult = await parseOpenCodeConfiguration(baseDir);
|
|
4029
|
+
rules = opencodeResult.rules;
|
|
4030
|
+
errors.push(...opencodeResult.errors);
|
|
4031
|
+
ignorePatterns = opencodeResult.ignorePatterns;
|
|
4032
|
+
mcpServers = opencodeResult.mcpServers;
|
|
4033
|
+
break;
|
|
4034
|
+
}
|
|
3873
4035
|
default:
|
|
3874
4036
|
errors.push(`Unsupported tool: ${tool}`);
|
|
3875
4037
|
return { success: false, rulesCreated: 0, errors };
|
|
@@ -3882,10 +4044,10 @@ async function importConfiguration(options) {
|
|
|
3882
4044
|
if (rules.length === 0 && !ignorePatterns && !mcpServers) {
|
|
3883
4045
|
return { success: false, rulesCreated: 0, errors };
|
|
3884
4046
|
}
|
|
3885
|
-
const rulesDirPath =
|
|
4047
|
+
const rulesDirPath = join20(baseDir, rulesDir);
|
|
3886
4048
|
try {
|
|
3887
|
-
const { mkdir:
|
|
3888
|
-
await
|
|
4049
|
+
const { mkdir: mkdir2 } = await import("fs/promises");
|
|
4050
|
+
await mkdir2(rulesDirPath, { recursive: true });
|
|
3889
4051
|
} catch (error) {
|
|
3890
4052
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
3891
4053
|
errors.push(`Failed to create rules directory: ${errorMessage}`);
|
|
@@ -3897,17 +4059,17 @@ async function importConfiguration(options) {
|
|
|
3897
4059
|
const baseFilename = rule.filename;
|
|
3898
4060
|
let targetDir = rulesDirPath;
|
|
3899
4061
|
if (rule.type === "command") {
|
|
3900
|
-
targetDir =
|
|
3901
|
-
const { mkdir:
|
|
3902
|
-
await
|
|
4062
|
+
targetDir = join20(rulesDirPath, "commands");
|
|
4063
|
+
const { mkdir: mkdir2 } = await import("fs/promises");
|
|
4064
|
+
await mkdir2(targetDir, { recursive: true });
|
|
3903
4065
|
} else {
|
|
3904
4066
|
if (!useLegacyLocation) {
|
|
3905
|
-
targetDir =
|
|
3906
|
-
const { mkdir:
|
|
3907
|
-
await
|
|
4067
|
+
targetDir = join20(rulesDirPath, "rules");
|
|
4068
|
+
const { mkdir: mkdir2 } = await import("fs/promises");
|
|
4069
|
+
await mkdir2(targetDir, { recursive: true });
|
|
3908
4070
|
}
|
|
3909
4071
|
}
|
|
3910
|
-
const filePath =
|
|
4072
|
+
const filePath = join20(targetDir, `${baseFilename}.md`);
|
|
3911
4073
|
const content = generateRuleFileContent(rule);
|
|
3912
4074
|
await writeFileContent(filePath, content);
|
|
3913
4075
|
rulesCreated++;
|
|
@@ -3922,7 +4084,7 @@ async function importConfiguration(options) {
|
|
|
3922
4084
|
let ignoreFileCreated = false;
|
|
3923
4085
|
if (ignorePatterns && ignorePatterns.length > 0) {
|
|
3924
4086
|
try {
|
|
3925
|
-
const rulesyncignorePath =
|
|
4087
|
+
const rulesyncignorePath = join20(baseDir, ".rulesyncignore");
|
|
3926
4088
|
const ignoreContent = `${ignorePatterns.join("\n")}
|
|
3927
4089
|
`;
|
|
3928
4090
|
await writeFileContent(rulesyncignorePath, ignoreContent);
|
|
@@ -3938,7 +4100,7 @@ async function importConfiguration(options) {
|
|
|
3938
4100
|
let mcpFileCreated = false;
|
|
3939
4101
|
if (mcpServers && Object.keys(mcpServers).length > 0) {
|
|
3940
4102
|
try {
|
|
3941
|
-
const mcpPath =
|
|
4103
|
+
const mcpPath = join20(baseDir, rulesDir, ".mcp.json");
|
|
3942
4104
|
const mcpContent = `${JSON.stringify({ mcpServers }, null, 2)}
|
|
3943
4105
|
`;
|
|
3944
4106
|
await writeFileContent(mcpPath, mcpContent);
|
|
@@ -3976,6 +4138,7 @@ function generateRuleFileContent(rule) {
|
|
|
3976
4138
|
async function importCommand(options = {}) {
|
|
3977
4139
|
logger.setVerbose(options.verbose || false);
|
|
3978
4140
|
const tools = [];
|
|
4141
|
+
if (options.amazonqcli) tools.push("amazonqcli");
|
|
3979
4142
|
if (options.augmentcode) tools.push("augmentcode");
|
|
3980
4143
|
if (options["augmentcode-legacy"]) tools.push("augmentcode-legacy");
|
|
3981
4144
|
if (options.claudecode) tools.push("claudecode");
|
|
@@ -3984,9 +4147,10 @@ async function importCommand(options = {}) {
|
|
|
3984
4147
|
if (options.cline) tools.push("cline");
|
|
3985
4148
|
if (options.roo) tools.push("roo");
|
|
3986
4149
|
if (options.geminicli) tools.push("geminicli");
|
|
4150
|
+
if (options.opencode) tools.push("opencode");
|
|
3987
4151
|
if (tools.length === 0) {
|
|
3988
4152
|
logger.error(
|
|
3989
|
-
"\u274C Please specify one tool to import from (--augmentcode, --augmentcode-legacy, --claudecode, --cursor, --copilot, --cline, --roo, --geminicli)"
|
|
4153
|
+
"\u274C Please specify one tool to import from (--amazonqcli, --augmentcode, --augmentcode-legacy, --claudecode, --cursor, --copilot, --cline, --roo, --geminicli, --opencode)"
|
|
3990
4154
|
);
|
|
3991
4155
|
process.exit(1);
|
|
3992
4156
|
}
|
|
@@ -4034,7 +4198,7 @@ async function importCommand(options = {}) {
|
|
|
4034
4198
|
}
|
|
4035
4199
|
|
|
4036
4200
|
// src/cli/commands/init.ts
|
|
4037
|
-
import { join as
|
|
4201
|
+
import { join as join21 } from "path";
|
|
4038
4202
|
async function initCommand(options = {}) {
|
|
4039
4203
|
const configResult = await loadConfig();
|
|
4040
4204
|
const config = configResult.config;
|
|
@@ -4042,7 +4206,7 @@ async function initCommand(options = {}) {
|
|
|
4042
4206
|
logger.log("Initializing rulesync...");
|
|
4043
4207
|
await ensureDir(aiRulesDir);
|
|
4044
4208
|
const useLegacy = options.legacy ?? config.legacy ?? false;
|
|
4045
|
-
const rulesDir = useLegacy ? aiRulesDir :
|
|
4209
|
+
const rulesDir = useLegacy ? aiRulesDir : join21(aiRulesDir, "rules");
|
|
4046
4210
|
if (!useLegacy) {
|
|
4047
4211
|
await ensureDir(rulesDir);
|
|
4048
4212
|
}
|
|
@@ -4088,7 +4252,7 @@ globs: ["**/*"]
|
|
|
4088
4252
|
- Follow single responsibility principle
|
|
4089
4253
|
`
|
|
4090
4254
|
};
|
|
4091
|
-
const filepath =
|
|
4255
|
+
const filepath = join21(rulesDir, sampleFile.filename);
|
|
4092
4256
|
if (!await fileExists(filepath)) {
|
|
4093
4257
|
await writeFileContent(filepath, sampleFile.content);
|
|
4094
4258
|
logger.success(`Created ${filepath}`);
|
|
@@ -4232,12 +4396,12 @@ async function watchCommand() {
|
|
|
4232
4396
|
|
|
4233
4397
|
// src/cli/index.ts
|
|
4234
4398
|
var program = new Command();
|
|
4235
|
-
program.name("rulesync").description("Unified AI rules management CLI tool").version("0.
|
|
4399
|
+
program.name("rulesync").description("Unified AI rules management CLI tool").version("0.64.0");
|
|
4236
4400
|
program.command("init").description("Initialize rulesync in current directory").option("--legacy", "Use legacy file location (.rulesync/*.md instead of .rulesync/rules/*.md)").action(initCommand);
|
|
4237
4401
|
program.command("add <filename>").description("Add a new rule file").option("--legacy", "Use legacy file location (.rulesync/*.md instead of .rulesync/rules/*.md)").action(addCommand);
|
|
4238
4402
|
program.command("gitignore").description("Add generated files to .gitignore").action(gitignoreCommand);
|
|
4239
|
-
program.command("import").description("Import configurations from AI tools to rulesync format").option("--augmentcode", "Import from AugmentCode (.augment/rules/)").option("--augmentcode-legacy", "Import from AugmentCode legacy format (.augment-guidelines)").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("--junie", "Import from JetBrains Junie (.junie/guidelines.md)").option("-v, --verbose", "Verbose output").option("--legacy", "Use legacy file location (.rulesync/*.md instead of .rulesync/rules/*.md)").action(importCommand);
|
|
4240
|
-
program.command("generate").description("Generate configuration files for AI tools").option("--augmentcode", "Generate only for AugmentCode").option("--augmentcode-legacy", "Generate only for AugmentCode legacy format").option("--copilot", "Generate only for GitHub Copilot").option("--cursor", "Generate only for Cursor").option("--cline", "Generate only for Cline").option("--codexcli", "Generate only for OpenAI Codex CLI").option("--claudecode", "Generate only for Claude Code").option("--roo", "Generate only for Roo Code").option("--geminicli", "Generate only for Gemini CLI").option("--junie", "Generate only for JetBrains Junie").option("--kiro", "Generate only for Kiro IDE").option("--windsurf", "Generate only for Windsurf").option("--delete", "Delete all existing files in output directories before generating").option(
|
|
4403
|
+
program.command("import").description("Import configurations from AI tools to rulesync format").option("--augmentcode", "Import from AugmentCode (.augment/rules/)").option("--augmentcode-legacy", "Import from AugmentCode legacy format (.augment-guidelines)").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("--junie", "Import from JetBrains Junie (.junie/guidelines.md)").option("--opencode", "Import from OpenCode (AGENTS.md)").option("-v, --verbose", "Verbose output").option("--legacy", "Use legacy file location (.rulesync/*.md instead of .rulesync/rules/*.md)").action(importCommand);
|
|
4404
|
+
program.command("generate").description("Generate configuration files for AI tools").option("--augmentcode", "Generate only for AugmentCode").option("--augmentcode-legacy", "Generate only for AugmentCode legacy format").option("--copilot", "Generate only for GitHub Copilot").option("--cursor", "Generate only for Cursor").option("--cline", "Generate only for Cline").option("--codexcli", "Generate only for OpenAI Codex CLI").option("--claudecode", "Generate only for Claude Code").option("--roo", "Generate only for Roo Code").option("--geminicli", "Generate only for Gemini CLI").option("--junie", "Generate only for JetBrains Junie").option("--kiro", "Generate only for Kiro IDE").option("--opencode", "Generate only for OpenCode").option("--windsurf", "Generate only for Windsurf").option("--delete", "Delete all existing files in output directories before generating").option(
|
|
4241
4405
|
"-b, --base-dir <paths>",
|
|
4242
4406
|
"Base directories to generate files (comma-separated for multiple paths)"
|
|
4243
4407
|
).option("-v, --verbose", "Verbose output").option("-c, --config <path>", "Path to configuration file").option("--no-config", "Disable configuration file loading").action(async (options) => {
|
|
@@ -4253,6 +4417,7 @@ program.command("generate").description("Generate configuration files for AI too
|
|
|
4253
4417
|
if (options.geminicli) tools.push("geminicli");
|
|
4254
4418
|
if (options.junie) tools.push("junie");
|
|
4255
4419
|
if (options.kiro) tools.push("kiro");
|
|
4420
|
+
if (options.opencode) tools.push("opencode");
|
|
4256
4421
|
if (options.windsurf) tools.push("windsurf");
|
|
4257
4422
|
const generateOptions = {
|
|
4258
4423
|
verbose: options.verbose,
|