rulesync 0.62.0 → 0.63.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/dist/index.cjs +315 -273
- package/dist/index.js +297 -256
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -41,12 +41,20 @@ var ClaudeSettingsSchema = z.looseObject({
|
|
|
41
41
|
)
|
|
42
42
|
});
|
|
43
43
|
|
|
44
|
-
// src/types/
|
|
44
|
+
// src/types/shared.ts
|
|
45
45
|
import { z as z2 } from "zod/mini";
|
|
46
|
-
var
|
|
46
|
+
var OutputSchema = z2.object({
|
|
47
|
+
tool: ToolTargetSchema,
|
|
48
|
+
filepath: z2.string(),
|
|
49
|
+
content: z2.string()
|
|
50
|
+
});
|
|
51
|
+
var BaseFrontmatterSchema = z2.object({
|
|
47
52
|
description: z2.optional(z2.string())
|
|
48
53
|
});
|
|
49
54
|
|
|
55
|
+
// src/types/commands.ts
|
|
56
|
+
var CommandFrontmatterSchema = BaseFrontmatterSchema;
|
|
57
|
+
|
|
50
58
|
// src/types/config.ts
|
|
51
59
|
import { z as z3 } from "zod/mini";
|
|
52
60
|
var ConfigSchema = z3.object({
|
|
@@ -159,11 +167,6 @@ var RuleFrontmatterSchema = z6.object({
|
|
|
159
167
|
windsurfOutputFormat: z6.optional(z6.enum(["single-file", "directory"])),
|
|
160
168
|
tags: z6.optional(z6.array(z6.string()))
|
|
161
169
|
});
|
|
162
|
-
var GeneratedOutputSchema = z6.object({
|
|
163
|
-
tool: ToolTargetSchema,
|
|
164
|
-
filepath: z6.string(),
|
|
165
|
-
content: z6.string()
|
|
166
|
-
});
|
|
167
170
|
var GenerateOptionsSchema = z6.object({
|
|
168
171
|
targetTools: z6.optional(ToolTargetsSchema),
|
|
169
172
|
outputDir: z6.optional(z6.string()),
|
|
@@ -388,6 +391,50 @@ function mergeWithCliOptions(config, cliOptions) {
|
|
|
388
391
|
return merged;
|
|
389
392
|
}
|
|
390
393
|
|
|
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
|
+
|
|
391
438
|
// src/cli/commands/add.ts
|
|
392
439
|
function sanitizeFilename(filename) {
|
|
393
440
|
return filename.endsWith(".md") ? filename.slice(0, -3) : filename;
|
|
@@ -417,11 +464,11 @@ async function addCommand(filename, options = {}) {
|
|
|
417
464
|
await mkdir(rulesDir, { recursive: true });
|
|
418
465
|
const template = generateRuleTemplate(sanitizedFilename);
|
|
419
466
|
await writeFile(filePath, template, "utf8");
|
|
420
|
-
|
|
421
|
-
|
|
467
|
+
logger.success(`Created rule file: ${filePath}`);
|
|
468
|
+
logger.log(`\u{1F4DD} Edit the file to customize your rules.`);
|
|
422
469
|
} catch (error) {
|
|
423
|
-
|
|
424
|
-
|
|
470
|
+
logger.error(
|
|
471
|
+
`Failed to create rule file: ${error instanceof Error ? error.message : String(error)}`
|
|
425
472
|
);
|
|
426
473
|
process.exit(3);
|
|
427
474
|
}
|
|
@@ -513,7 +560,7 @@ async function findRuleFiles(aiRulesDir) {
|
|
|
513
560
|
async function removeDirectory(dirPath) {
|
|
514
561
|
const dangerousPaths = [".", "/", "~", "src", "node_modules"];
|
|
515
562
|
if (dangerousPaths.includes(dirPath) || dirPath === "") {
|
|
516
|
-
|
|
563
|
+
logger.warn(`Skipping deletion of dangerous path: ${dirPath}`);
|
|
517
564
|
return;
|
|
518
565
|
}
|
|
519
566
|
try {
|
|
@@ -521,7 +568,7 @@ async function removeDirectory(dirPath) {
|
|
|
521
568
|
await rm(dirPath, { recursive: true, force: true });
|
|
522
569
|
}
|
|
523
570
|
} catch (error) {
|
|
524
|
-
|
|
571
|
+
logger.warn(`Failed to remove directory ${dirPath}:`, error);
|
|
525
572
|
}
|
|
526
573
|
}
|
|
527
574
|
async function removeFile(filepath) {
|
|
@@ -530,7 +577,7 @@ async function removeFile(filepath) {
|
|
|
530
577
|
await rm(filepath);
|
|
531
578
|
}
|
|
532
579
|
} catch (error) {
|
|
533
|
-
|
|
580
|
+
logger.warn(`Failed to remove file ${filepath}:`, error);
|
|
534
581
|
}
|
|
535
582
|
}
|
|
536
583
|
async function removeClaudeGeneratedFiles() {
|
|
@@ -544,50 +591,6 @@ async function removeClaudeGeneratedFiles() {
|
|
|
544
591
|
}
|
|
545
592
|
}
|
|
546
593
|
|
|
547
|
-
// src/utils/logger.ts
|
|
548
|
-
import { consola } from "consola";
|
|
549
|
-
var Logger = class {
|
|
550
|
-
_verbose = false;
|
|
551
|
-
console = consola.withDefaults({
|
|
552
|
-
tag: "rulesync"
|
|
553
|
-
});
|
|
554
|
-
setVerbose(verbose) {
|
|
555
|
-
this._verbose = verbose;
|
|
556
|
-
}
|
|
557
|
-
get verbose() {
|
|
558
|
-
return this._verbose;
|
|
559
|
-
}
|
|
560
|
-
// Regular log (always shown, regardless of verbose)
|
|
561
|
-
log(message, ...args) {
|
|
562
|
-
this.console.log(message, ...args);
|
|
563
|
-
}
|
|
564
|
-
// Info level (shown only in verbose mode)
|
|
565
|
-
info(message, ...args) {
|
|
566
|
-
if (this._verbose) {
|
|
567
|
-
this.console.info(message, ...args);
|
|
568
|
-
}
|
|
569
|
-
}
|
|
570
|
-
// Success (always shown)
|
|
571
|
-
success(message, ...args) {
|
|
572
|
-
this.console.success(message, ...args);
|
|
573
|
-
}
|
|
574
|
-
// Warning (always shown)
|
|
575
|
-
warn(message, ...args) {
|
|
576
|
-
this.console.warn(message, ...args);
|
|
577
|
-
}
|
|
578
|
-
// Error (always shown)
|
|
579
|
-
error(message, ...args) {
|
|
580
|
-
this.console.error(message, ...args);
|
|
581
|
-
}
|
|
582
|
-
// Debug level (shown only in verbose mode)
|
|
583
|
-
debug(message, ...args) {
|
|
584
|
-
if (this._verbose) {
|
|
585
|
-
this.console.debug(message, ...args);
|
|
586
|
-
}
|
|
587
|
-
}
|
|
588
|
-
};
|
|
589
|
-
var logger = new Logger();
|
|
590
|
-
|
|
591
594
|
// src/cli/commands/config.ts
|
|
592
595
|
async function configCommand(options = {}) {
|
|
593
596
|
if (options.init) {
|
|
@@ -799,25 +802,68 @@ export default config;
|
|
|
799
802
|
}
|
|
800
803
|
|
|
801
804
|
// src/cli/commands/generate.ts
|
|
802
|
-
import { join as
|
|
805
|
+
import { join as join13 } from "path";
|
|
803
806
|
|
|
804
807
|
// src/core/command-generator.ts
|
|
805
|
-
import { join as
|
|
808
|
+
import { join as join4 } from "path";
|
|
806
809
|
|
|
807
|
-
// src/generators
|
|
810
|
+
// src/utils/command-generators.ts
|
|
808
811
|
import { join as join3 } from "path";
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
812
|
+
function generateYamlFrontmatter(command, options = {}) {
|
|
813
|
+
const frontmatter = ["---"];
|
|
814
|
+
if (options.includeDescription !== false && command.frontmatter.description) {
|
|
815
|
+
frontmatter.push(`description: ${command.frontmatter.description}`);
|
|
816
|
+
}
|
|
817
|
+
if (options.additionalFields) {
|
|
818
|
+
for (const field of options.additionalFields) {
|
|
819
|
+
frontmatter.push(`${field.key}: ${field.value}`);
|
|
815
820
|
}
|
|
816
|
-
|
|
817
|
-
|
|
821
|
+
}
|
|
822
|
+
frontmatter.push("---");
|
|
823
|
+
return frontmatter;
|
|
824
|
+
}
|
|
825
|
+
function buildCommandContent(command, frontmatterOptions) {
|
|
826
|
+
const frontmatter = generateYamlFrontmatter(command, frontmatterOptions);
|
|
827
|
+
return `${frontmatter.join("\n")}
|
|
818
828
|
|
|
819
829
|
${command.content.trim()}
|
|
820
830
|
`;
|
|
831
|
+
}
|
|
832
|
+
function getFlattenedCommandPath(filename, baseDir, subdir) {
|
|
833
|
+
const flattenedName = filename.replace(/\//g, "-");
|
|
834
|
+
return join3(baseDir, subdir, `${flattenedName}.md`);
|
|
835
|
+
}
|
|
836
|
+
function getHierarchicalCommandPath(filename, baseDir, subdir, extension = "md") {
|
|
837
|
+
const nameWithoutExt = filename.replace(/\.[^/.]+$/, "");
|
|
838
|
+
const fileWithExt = `${nameWithoutExt}.${extension}`;
|
|
839
|
+
return join3(baseDir, subdir, fileWithExt);
|
|
840
|
+
}
|
|
841
|
+
function escapeTomlString(str) {
|
|
842
|
+
return str.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/\t/g, "\\t");
|
|
843
|
+
}
|
|
844
|
+
var syntaxConverters = {
|
|
845
|
+
/**
|
|
846
|
+
* Convert Claude Code syntax to Gemini CLI syntax
|
|
847
|
+
*/
|
|
848
|
+
toGeminiCli(content) {
|
|
849
|
+
let converted = content;
|
|
850
|
+
converted = converted.replace(/\$ARGUMENTS/g, "{{args}}");
|
|
851
|
+
converted = converted.replace(/!`([^`]+)`/g, "!{$1}");
|
|
852
|
+
return converted.trim();
|
|
853
|
+
},
|
|
854
|
+
/**
|
|
855
|
+
* Convert to Roo Code syntax (currently identical to Claude Code)
|
|
856
|
+
*/
|
|
857
|
+
toRooCode(content) {
|
|
858
|
+
return content.trim();
|
|
859
|
+
}
|
|
860
|
+
};
|
|
861
|
+
|
|
862
|
+
// src/generators/commands/claudecode.ts
|
|
863
|
+
var ClaudeCodeCommandGenerator = class {
|
|
864
|
+
generate(command, outputDir) {
|
|
865
|
+
const filepath = this.getOutputPath(command.filename, outputDir);
|
|
866
|
+
const content = buildCommandContent(command);
|
|
821
867
|
return {
|
|
822
868
|
tool: "claudecode",
|
|
823
869
|
filepath,
|
|
@@ -825,20 +871,18 @@ ${command.content.trim()}
|
|
|
825
871
|
};
|
|
826
872
|
}
|
|
827
873
|
getOutputPath(filename, baseDir) {
|
|
828
|
-
|
|
829
|
-
return join3(baseDir, ".claude", "commands", `${flattenedName}.md`);
|
|
874
|
+
return getFlattenedCommandPath(filename, baseDir, ".claude/commands");
|
|
830
875
|
}
|
|
831
876
|
};
|
|
832
877
|
|
|
833
878
|
// src/generators/commands/geminicli.ts
|
|
834
|
-
import { join as join4 } from "path";
|
|
835
879
|
var GeminiCliCommandGenerator = class {
|
|
836
880
|
generate(command, outputDir) {
|
|
837
881
|
const filepath = this.getOutputPath(command.filename, outputDir);
|
|
838
|
-
const convertedContent =
|
|
882
|
+
const convertedContent = syntaxConverters.toGeminiCli(command.content);
|
|
839
883
|
const tomlLines = [];
|
|
840
884
|
if (command.frontmatter.description) {
|
|
841
|
-
tomlLines.push(`description = "${
|
|
885
|
+
tomlLines.push(`description = "${escapeTomlString(command.frontmatter.description)}"`);
|
|
842
886
|
tomlLines.push("");
|
|
843
887
|
}
|
|
844
888
|
tomlLines.push(`prompt = """${convertedContent}"""`);
|
|
@@ -850,35 +894,15 @@ var GeminiCliCommandGenerator = class {
|
|
|
850
894
|
};
|
|
851
895
|
}
|
|
852
896
|
getOutputPath(filename, baseDir) {
|
|
853
|
-
|
|
854
|
-
const filenameWithExt = tomlFilename.endsWith(".toml") ? tomlFilename : `${tomlFilename}.toml`;
|
|
855
|
-
return join4(baseDir, ".gemini", "commands", filenameWithExt);
|
|
856
|
-
}
|
|
857
|
-
convertSyntax(content) {
|
|
858
|
-
let converted = content;
|
|
859
|
-
converted = converted.replace(/\$ARGUMENTS/g, "{{args}}");
|
|
860
|
-
converted = converted.replace(/!`([^`]+)`/g, "!{$1}");
|
|
861
|
-
return converted.trim();
|
|
862
|
-
}
|
|
863
|
-
escapeTomlString(str) {
|
|
864
|
-
return str.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/\t/g, "\\t");
|
|
897
|
+
return getHierarchicalCommandPath(filename, baseDir, ".gemini/commands", "toml");
|
|
865
898
|
}
|
|
866
899
|
};
|
|
867
900
|
|
|
868
901
|
// src/generators/commands/roo.ts
|
|
869
|
-
import { join as join5 } from "path";
|
|
870
902
|
var RooCommandGenerator = class {
|
|
871
903
|
generate(command, outputDir) {
|
|
872
904
|
const filepath = this.getOutputPath(command.filename, outputDir);
|
|
873
|
-
const
|
|
874
|
-
if (command.frontmatter.description) {
|
|
875
|
-
frontmatter.push(`description: ${command.frontmatter.description}`);
|
|
876
|
-
}
|
|
877
|
-
frontmatter.push("---");
|
|
878
|
-
const content = `${frontmatter.join("\n")}
|
|
879
|
-
|
|
880
|
-
${command.content.trim()}
|
|
881
|
-
`;
|
|
905
|
+
const content = buildCommandContent(command);
|
|
882
906
|
return {
|
|
883
907
|
tool: "roo",
|
|
884
908
|
filepath,
|
|
@@ -886,8 +910,7 @@ ${command.content.trim()}
|
|
|
886
910
|
};
|
|
887
911
|
}
|
|
888
912
|
getOutputPath(filename, baseDir) {
|
|
889
|
-
|
|
890
|
-
return join5(baseDir, ".roo", "commands", `${flattenedName}.md`);
|
|
913
|
+
return getFlattenedCommandPath(filename, baseDir, ".roo/commands");
|
|
891
914
|
}
|
|
892
915
|
};
|
|
893
916
|
|
|
@@ -903,7 +926,26 @@ function getCommandGenerator(tool) {
|
|
|
903
926
|
|
|
904
927
|
// src/core/command-parser.ts
|
|
905
928
|
import { basename } from "path";
|
|
929
|
+
|
|
930
|
+
// src/utils/frontmatter.ts
|
|
906
931
|
import matter from "gray-matter";
|
|
932
|
+
function parseFrontmatter(content, options) {
|
|
933
|
+
const parsed = matter(content, options?.matterOptions);
|
|
934
|
+
return {
|
|
935
|
+
content: parsed.content.trim(),
|
|
936
|
+
data: parsed.data || {}
|
|
937
|
+
};
|
|
938
|
+
}
|
|
939
|
+
function extractArrayField(data, key, defaultValue = []) {
|
|
940
|
+
const value = data[key];
|
|
941
|
+
return Array.isArray(value) ? value : defaultValue;
|
|
942
|
+
}
|
|
943
|
+
function extractStringField(data, key, defaultValue) {
|
|
944
|
+
const value = data[key];
|
|
945
|
+
return typeof value === "string" ? value : defaultValue;
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
// src/core/command-parser.ts
|
|
907
949
|
async function parseCommandsFromDirectory(commandsDir) {
|
|
908
950
|
const commandFiles = await findFiles(commandsDir, ".md");
|
|
909
951
|
const commands = [];
|
|
@@ -918,14 +960,14 @@ async function parseCommandsFromDirectory(commandsDir) {
|
|
|
918
960
|
}
|
|
919
961
|
}
|
|
920
962
|
if (errors.length > 0) {
|
|
921
|
-
|
|
963
|
+
logger.warn(`Command parsing errors:
|
|
922
964
|
${errors.join("\n")}`);
|
|
923
965
|
}
|
|
924
966
|
return commands;
|
|
925
967
|
}
|
|
926
968
|
async function parseCommandFile(filepath) {
|
|
927
969
|
const content = await readFileContent(filepath);
|
|
928
|
-
const parsed =
|
|
970
|
+
const parsed = parseFrontmatter(content);
|
|
929
971
|
try {
|
|
930
972
|
const validatedData = CommandFrontmatterSchema.parse(parsed.data);
|
|
931
973
|
const filename = basename(filepath, ".md");
|
|
@@ -946,7 +988,7 @@ async function parseCommandFile(filepath) {
|
|
|
946
988
|
|
|
947
989
|
// src/core/command-generator.ts
|
|
948
990
|
async function generateCommands(projectRoot, baseDir, targets) {
|
|
949
|
-
const commandsDir =
|
|
991
|
+
const commandsDir = join4(projectRoot, ".rulesync", "commands");
|
|
950
992
|
if (!await fileExists(commandsDir)) {
|
|
951
993
|
return [];
|
|
952
994
|
}
|
|
@@ -970,8 +1012,8 @@ async function generateCommands(projectRoot, baseDir, targets) {
|
|
|
970
1012
|
outputs.push(output);
|
|
971
1013
|
} catch (error) {
|
|
972
1014
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
973
|
-
|
|
974
|
-
|
|
1015
|
+
logger.error(
|
|
1016
|
+
`Failed to generate ${target} command for ${command.filename}: ${errorMessage}`
|
|
975
1017
|
);
|
|
976
1018
|
}
|
|
977
1019
|
}
|
|
@@ -980,7 +1022,7 @@ async function generateCommands(projectRoot, baseDir, targets) {
|
|
|
980
1022
|
}
|
|
981
1023
|
|
|
982
1024
|
// src/generators/ignore/shared-factory.ts
|
|
983
|
-
import { join as
|
|
1025
|
+
import { join as join5 } from "path";
|
|
984
1026
|
|
|
985
1027
|
// src/generators/ignore/shared-helpers.ts
|
|
986
1028
|
function extractIgnorePatternsFromRules(rules) {
|
|
@@ -1103,7 +1145,7 @@ function generateIgnoreFile(rules, config, ignoreConfig, baseDir) {
|
|
|
1103
1145
|
const outputs = [];
|
|
1104
1146
|
const content = generateIgnoreContent(rules, ignoreConfig);
|
|
1105
1147
|
const outputPath = baseDir || process.cwd();
|
|
1106
|
-
const filepath =
|
|
1148
|
+
const filepath = join5(outputPath, ignoreConfig.filename);
|
|
1107
1149
|
outputs.push({
|
|
1108
1150
|
tool: ignoreConfig.tool,
|
|
1109
1151
|
filepath,
|
|
@@ -1691,20 +1733,20 @@ function generateWindsurfIgnore(rules, config, baseDir) {
|
|
|
1691
1733
|
}
|
|
1692
1734
|
|
|
1693
1735
|
// src/generators/rules/augmentcode.ts
|
|
1694
|
-
import { join as
|
|
1736
|
+
import { join as join8 } from "path";
|
|
1695
1737
|
|
|
1696
1738
|
// src/generators/rules/shared-helpers.ts
|
|
1697
|
-
import { join as
|
|
1739
|
+
import { join as join7 } from "path";
|
|
1698
1740
|
|
|
1699
1741
|
// src/utils/ignore.ts
|
|
1700
|
-
import { join as
|
|
1742
|
+
import { join as join6 } from "path";
|
|
1701
1743
|
import micromatch from "micromatch";
|
|
1702
1744
|
var cachedIgnorePatterns = null;
|
|
1703
1745
|
async function loadIgnorePatterns(baseDir = process.cwd()) {
|
|
1704
1746
|
if (cachedIgnorePatterns) {
|
|
1705
1747
|
return cachedIgnorePatterns;
|
|
1706
1748
|
}
|
|
1707
|
-
const ignorePath =
|
|
1749
|
+
const ignorePath = join6(baseDir, ".rulesyncignore");
|
|
1708
1750
|
if (!await fileExists(ignorePath)) {
|
|
1709
1751
|
cachedIgnorePatterns = { patterns: [] };
|
|
1710
1752
|
return cachedIgnorePatterns;
|
|
@@ -1715,7 +1757,7 @@ async function loadIgnorePatterns(baseDir = process.cwd()) {
|
|
|
1715
1757
|
cachedIgnorePatterns = { patterns };
|
|
1716
1758
|
return cachedIgnorePatterns;
|
|
1717
1759
|
} catch (error) {
|
|
1718
|
-
|
|
1760
|
+
logger.warn(`Failed to read .rulesyncignore: ${error}`);
|
|
1719
1761
|
cachedIgnorePatterns = { patterns: [] };
|
|
1720
1762
|
return cachedIgnorePatterns;
|
|
1721
1763
|
}
|
|
@@ -1758,7 +1800,7 @@ function addOutput(outputs, tool, config, baseDir, relativePath, content) {
|
|
|
1758
1800
|
const outputDir = resolveOutputDir(config, tool, baseDir);
|
|
1759
1801
|
outputs.push({
|
|
1760
1802
|
tool,
|
|
1761
|
-
filepath:
|
|
1803
|
+
filepath: join7(outputDir, relativePath),
|
|
1762
1804
|
content
|
|
1763
1805
|
});
|
|
1764
1806
|
}
|
|
@@ -1767,7 +1809,7 @@ async function generateRulesConfig(rules, config, generatorConfig, baseDir) {
|
|
|
1767
1809
|
for (const rule of rules) {
|
|
1768
1810
|
const content = generatorConfig.generateContent(rule);
|
|
1769
1811
|
const outputDir = resolveOutputDir(config, generatorConfig.tool, baseDir);
|
|
1770
|
-
const filepath = generatorConfig.pathResolver ? generatorConfig.pathResolver(rule, outputDir) :
|
|
1812
|
+
const filepath = generatorConfig.pathResolver ? generatorConfig.pathResolver(rule, outputDir) : join7(outputDir, `${rule.filename}${generatorConfig.fileExtension}`);
|
|
1771
1813
|
outputs.push({
|
|
1772
1814
|
tool: generatorConfig.tool,
|
|
1773
1815
|
filepath,
|
|
@@ -1795,7 +1837,7 @@ async function generateComplexRules(rules, config, generatorConfig, baseDir) {
|
|
|
1795
1837
|
for (const rule of detailRules) {
|
|
1796
1838
|
const content = generatorConfig.generateDetailContent(rule);
|
|
1797
1839
|
const filepath = resolvePath(
|
|
1798
|
-
|
|
1840
|
+
join7(generatorConfig.detailSubDir, `${rule.filename}.md`),
|
|
1799
1841
|
baseDir
|
|
1800
1842
|
);
|
|
1801
1843
|
outputs.push({
|
|
@@ -1858,7 +1900,7 @@ async function generateAugmentcodeConfig(rules, config, baseDir) {
|
|
|
1858
1900
|
"augmentcode",
|
|
1859
1901
|
config,
|
|
1860
1902
|
baseDir,
|
|
1861
|
-
|
|
1903
|
+
join8(".augment", "rules", `${rule.filename}.md`),
|
|
1862
1904
|
generateRuleFile(rule)
|
|
1863
1905
|
);
|
|
1864
1906
|
});
|
|
@@ -1911,7 +1953,7 @@ function generateLegacyGuidelinesFile(allRules) {
|
|
|
1911
1953
|
}
|
|
1912
1954
|
|
|
1913
1955
|
// src/generators/rules/claudecode.ts
|
|
1914
|
-
import { join as
|
|
1956
|
+
import { join as join9 } from "path";
|
|
1915
1957
|
async function generateClaudecodeConfig(rules, config, baseDir) {
|
|
1916
1958
|
const generatorConfig = {
|
|
1917
1959
|
tool: "claudecode",
|
|
@@ -1923,7 +1965,7 @@ async function generateClaudecodeConfig(rules, config, baseDir) {
|
|
|
1923
1965
|
generateDetailContent: generateMemoryFile,
|
|
1924
1966
|
detailSubDir: ".claude/memories",
|
|
1925
1967
|
updateAdditionalConfig: async (ignorePatterns, baseDir2) => {
|
|
1926
|
-
const settingsPath = resolvePath(
|
|
1968
|
+
const settingsPath = resolvePath(join9(".claude", "settings.json"), baseDir2);
|
|
1927
1969
|
await updateClaudeSettings(settingsPath, ignorePatterns);
|
|
1928
1970
|
return [];
|
|
1929
1971
|
}
|
|
@@ -1960,7 +2002,7 @@ async function updateClaudeSettings(settingsPath, ignorePatterns) {
|
|
|
1960
2002
|
const content = await readFileContent(settingsPath);
|
|
1961
2003
|
rawSettings = JSON.parse(content);
|
|
1962
2004
|
} catch {
|
|
1963
|
-
|
|
2005
|
+
logger.warn(`Failed to parse existing ${settingsPath}, creating new settings`);
|
|
1964
2006
|
rawSettings = {};
|
|
1965
2007
|
}
|
|
1966
2008
|
}
|
|
@@ -1983,11 +2025,11 @@ async function updateClaudeSettings(settingsPath, ignorePatterns) {
|
|
|
1983
2025
|
settings.permissions.deny = Array.from(new Set(filteredDeny));
|
|
1984
2026
|
const jsonContent = JSON.stringify(settings, null, 2);
|
|
1985
2027
|
await writeFileContent(settingsPath, jsonContent);
|
|
1986
|
-
|
|
2028
|
+
logger.success(`Updated Claude Code settings: ${settingsPath}`);
|
|
1987
2029
|
}
|
|
1988
2030
|
|
|
1989
2031
|
// src/generators/rules/generator-registry.ts
|
|
1990
|
-
import { join as
|
|
2032
|
+
import { join as join10 } from "path";
|
|
1991
2033
|
function determineCursorRuleType(frontmatter) {
|
|
1992
2034
|
if (frontmatter.cursorRuleType) {
|
|
1993
2035
|
return frontmatter.cursorRuleType;
|
|
@@ -2067,7 +2109,7 @@ var GENERATOR_REGISTRY = {
|
|
|
2067
2109
|
},
|
|
2068
2110
|
pathResolver: (rule, outputDir) => {
|
|
2069
2111
|
const baseFilename = rule.filename.replace(/\.md$/, "");
|
|
2070
|
-
return
|
|
2112
|
+
return join10(outputDir, `${baseFilename}.instructions.md`);
|
|
2071
2113
|
}
|
|
2072
2114
|
},
|
|
2073
2115
|
cursor: {
|
|
@@ -2107,7 +2149,7 @@ var GENERATOR_REGISTRY = {
|
|
|
2107
2149
|
return lines.join("\n");
|
|
2108
2150
|
},
|
|
2109
2151
|
pathResolver: (rule, outputDir) => {
|
|
2110
|
-
return
|
|
2152
|
+
return join10(outputDir, `${rule.filename}.mdc`);
|
|
2111
2153
|
}
|
|
2112
2154
|
},
|
|
2113
2155
|
codexcli: {
|
|
@@ -2143,10 +2185,10 @@ var GENERATOR_REGISTRY = {
|
|
|
2143
2185
|
pathResolver: (rule, outputDir) => {
|
|
2144
2186
|
const outputFormat = rule.frontmatter.windsurfOutputFormat || "directory";
|
|
2145
2187
|
if (outputFormat === "single-file") {
|
|
2146
|
-
return
|
|
2188
|
+
return join10(outputDir, ".windsurf-rules");
|
|
2147
2189
|
} else {
|
|
2148
|
-
const rulesDir =
|
|
2149
|
-
return
|
|
2190
|
+
const rulesDir = join10(outputDir, ".windsurf", "rules");
|
|
2191
|
+
return join10(rulesDir, `${rule.filename}.md`);
|
|
2150
2192
|
}
|
|
2151
2193
|
}
|
|
2152
2194
|
},
|
|
@@ -2264,7 +2306,7 @@ async function generateCodexConfig(rules, config, baseDir) {
|
|
|
2264
2306
|
const concatenatedContent = generateConcatenatedCodexContent(sortedRules);
|
|
2265
2307
|
if (concatenatedContent.trim()) {
|
|
2266
2308
|
const outputDir = resolveOutputDir(config, "codexcli", baseDir);
|
|
2267
|
-
const filepath = `${outputDir}/
|
|
2309
|
+
const filepath = `${outputDir}/AGENTS.md`;
|
|
2268
2310
|
outputs.push({
|
|
2269
2311
|
tool: "codexcli",
|
|
2270
2312
|
filepath,
|
|
@@ -2370,14 +2412,12 @@ async function generateConfigurations(rules, config, targetTools, baseDir) {
|
|
|
2370
2412
|
const toolsToGenerate = targetTools || config.defaultTargets;
|
|
2371
2413
|
const rootFiles = rules.filter((rule) => rule.frontmatter.root === true);
|
|
2372
2414
|
if (rootFiles.length === 0) {
|
|
2373
|
-
|
|
2374
|
-
"\u26A0\uFE0F Warning: No files with 'root: true' found. This may result in incomplete configurations."
|
|
2375
|
-
);
|
|
2415
|
+
logger.warn("No files with 'root: true' found. This may result in incomplete configurations.");
|
|
2376
2416
|
}
|
|
2377
2417
|
for (const tool of toolsToGenerate) {
|
|
2378
2418
|
const relevantRules = filterRulesForTool(rules, tool, config);
|
|
2379
2419
|
if (relevantRules.length === 0) {
|
|
2380
|
-
|
|
2420
|
+
logger.warn(`No rules found for tool: ${tool}`);
|
|
2381
2421
|
continue;
|
|
2382
2422
|
}
|
|
2383
2423
|
const toolOutputs = await generateForTool(tool, relevantRules, config, baseDir);
|
|
@@ -2442,14 +2482,13 @@ async function generateForTool(tool, rules, config, baseDir) {
|
|
|
2442
2482
|
return [...windsurfRulesOutputs, ...windsurfIgnoreOutputs];
|
|
2443
2483
|
}
|
|
2444
2484
|
default:
|
|
2445
|
-
|
|
2485
|
+
logger.warn(`Unknown tool: ${tool}`);
|
|
2446
2486
|
return null;
|
|
2447
2487
|
}
|
|
2448
2488
|
}
|
|
2449
2489
|
|
|
2450
2490
|
// src/core/parser.ts
|
|
2451
2491
|
import { basename as basename2 } from "path";
|
|
2452
|
-
import matter2 from "gray-matter";
|
|
2453
2492
|
async function parseRulesFromDirectory(aiRulesDir) {
|
|
2454
2493
|
const ignorePatterns = await loadIgnorePatterns();
|
|
2455
2494
|
const allRuleFiles = await findRuleFiles(aiRulesDir);
|
|
@@ -2457,7 +2496,7 @@ async function parseRulesFromDirectory(aiRulesDir) {
|
|
|
2457
2496
|
const rules = [];
|
|
2458
2497
|
const errors = [];
|
|
2459
2498
|
if (ignorePatterns.patterns.length > 0) {
|
|
2460
|
-
|
|
2499
|
+
logger.info(`Loaded ${ignorePatterns.patterns.length} ignore patterns from .rulesyncignore`);
|
|
2461
2500
|
}
|
|
2462
2501
|
for (const filepath of ruleFiles) {
|
|
2463
2502
|
try {
|
|
@@ -2483,7 +2522,7 @@ ${errors.join("\n")}`);
|
|
|
2483
2522
|
}
|
|
2484
2523
|
async function parseRuleFile(filepath) {
|
|
2485
2524
|
const content = await readFileContent(filepath);
|
|
2486
|
-
const parsed =
|
|
2525
|
+
const parsed = parseFrontmatter(content);
|
|
2487
2526
|
try {
|
|
2488
2527
|
const validatedData = RuleFrontmatterSchema.parse(parsed.data);
|
|
2489
2528
|
const frontmatter = {
|
|
@@ -2708,7 +2747,7 @@ async function generateCommand(options = {}) {
|
|
|
2708
2747
|
logger.info("Deleting existing output directories...");
|
|
2709
2748
|
const targetTools = config.defaultTargets;
|
|
2710
2749
|
const deleteTasks = [];
|
|
2711
|
-
const commandsDir =
|
|
2750
|
+
const commandsDir = join13(config.aiRulesDir, "commands");
|
|
2712
2751
|
const hasCommands = await fileExists(commandsDir);
|
|
2713
2752
|
let hasCommandFiles = false;
|
|
2714
2753
|
if (hasCommands) {
|
|
@@ -2723,12 +2762,12 @@ async function generateCommand(options = {}) {
|
|
|
2723
2762
|
for (const tool of targetTools) {
|
|
2724
2763
|
switch (tool) {
|
|
2725
2764
|
case "augmentcode":
|
|
2726
|
-
deleteTasks.push(removeDirectory(
|
|
2727
|
-
deleteTasks.push(removeDirectory(
|
|
2765
|
+
deleteTasks.push(removeDirectory(join13(".augment", "rules")));
|
|
2766
|
+
deleteTasks.push(removeDirectory(join13(".augment", "ignore")));
|
|
2728
2767
|
break;
|
|
2729
2768
|
case "augmentcode-legacy":
|
|
2730
2769
|
deleteTasks.push(removeClaudeGeneratedFiles());
|
|
2731
|
-
deleteTasks.push(removeDirectory(
|
|
2770
|
+
deleteTasks.push(removeDirectory(join13(".augment", "ignore")));
|
|
2732
2771
|
break;
|
|
2733
2772
|
case "copilot":
|
|
2734
2773
|
deleteTasks.push(removeDirectory(config.outputPaths.copilot));
|
|
@@ -2742,19 +2781,19 @@ async function generateCommand(options = {}) {
|
|
|
2742
2781
|
case "claudecode":
|
|
2743
2782
|
deleteTasks.push(removeClaudeGeneratedFiles());
|
|
2744
2783
|
if (hasCommandFiles) {
|
|
2745
|
-
deleteTasks.push(removeDirectory(
|
|
2784
|
+
deleteTasks.push(removeDirectory(join13(".claude", "commands")));
|
|
2746
2785
|
}
|
|
2747
2786
|
break;
|
|
2748
2787
|
case "roo":
|
|
2749
2788
|
deleteTasks.push(removeDirectory(config.outputPaths.roo));
|
|
2750
2789
|
if (hasCommandFiles) {
|
|
2751
|
-
deleteTasks.push(removeDirectory(
|
|
2790
|
+
deleteTasks.push(removeDirectory(join13(".roo", "commands")));
|
|
2752
2791
|
}
|
|
2753
2792
|
break;
|
|
2754
2793
|
case "geminicli":
|
|
2755
2794
|
deleteTasks.push(removeDirectory(config.outputPaths.geminicli));
|
|
2756
2795
|
if (hasCommandFiles) {
|
|
2757
|
-
deleteTasks.push(removeDirectory(
|
|
2796
|
+
deleteTasks.push(removeDirectory(join13(".gemini", "commands")));
|
|
2758
2797
|
}
|
|
2759
2798
|
break;
|
|
2760
2799
|
case "kiro":
|
|
@@ -2853,9 +2892,9 @@ Generating configurations for base directory: ${baseDir}`);
|
|
|
2853
2892
|
|
|
2854
2893
|
// src/cli/commands/gitignore.ts
|
|
2855
2894
|
import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
2856
|
-
import { join as
|
|
2895
|
+
import { join as join14 } from "path";
|
|
2857
2896
|
var gitignoreCommand = async () => {
|
|
2858
|
-
const gitignorePath =
|
|
2897
|
+
const gitignorePath = join14(process.cwd(), ".gitignore");
|
|
2859
2898
|
const rulesFilesToIgnore = [
|
|
2860
2899
|
"# Generated by rulesync - AI tool configuration files",
|
|
2861
2900
|
"**/.github/copilot-instructions.md",
|
|
@@ -2867,7 +2906,7 @@ var gitignoreCommand = async () => {
|
|
|
2867
2906
|
"**/CLAUDE.md",
|
|
2868
2907
|
"**/.claude/memories/",
|
|
2869
2908
|
"**/.claude/commands/",
|
|
2870
|
-
"**/
|
|
2909
|
+
"**/AGENTS.md",
|
|
2871
2910
|
"**/.codexignore",
|
|
2872
2911
|
"**/.roo/rules/",
|
|
2873
2912
|
"**/.rooignore",
|
|
@@ -2903,7 +2942,7 @@ var gitignoreCommand = async () => {
|
|
|
2903
2942
|
}
|
|
2904
2943
|
}
|
|
2905
2944
|
if (linesToAdd.length === 0) {
|
|
2906
|
-
|
|
2945
|
+
logger.success(".gitignore is already up to date");
|
|
2907
2946
|
return;
|
|
2908
2947
|
}
|
|
2909
2948
|
const newContent = gitignoreContent ? `${gitignoreContent.trimEnd()}
|
|
@@ -2912,21 +2951,20 @@ ${linesToAdd.join("\n")}
|
|
|
2912
2951
|
` : `${linesToAdd.join("\n")}
|
|
2913
2952
|
`;
|
|
2914
2953
|
writeFileSync2(gitignorePath, newContent);
|
|
2915
|
-
|
|
2954
|
+
logger.success(`Added ${linesToAdd.length} rules to .gitignore:`);
|
|
2916
2955
|
for (const line of linesToAdd) {
|
|
2917
2956
|
if (!line.startsWith("#")) {
|
|
2918
|
-
|
|
2957
|
+
logger.log(` ${line}`);
|
|
2919
2958
|
}
|
|
2920
2959
|
}
|
|
2921
2960
|
};
|
|
2922
2961
|
|
|
2923
2962
|
// src/core/importer.ts
|
|
2924
|
-
import { join as
|
|
2925
|
-
import
|
|
2963
|
+
import { join as join21 } from "path";
|
|
2964
|
+
import matter2 from "gray-matter";
|
|
2926
2965
|
|
|
2927
2966
|
// src/parsers/augmentcode.ts
|
|
2928
|
-
import { basename as basename3, join as
|
|
2929
|
-
import matter3 from "gray-matter";
|
|
2967
|
+
import { basename as basename3, join as join15 } from "path";
|
|
2930
2968
|
|
|
2931
2969
|
// src/utils/parser-helpers.ts
|
|
2932
2970
|
function createParseResult() {
|
|
@@ -2974,7 +3012,7 @@ async function parseAugmentcodeLegacyConfiguration(baseDir = process.cwd()) {
|
|
|
2974
3012
|
async function parseUnifiedAugmentcode(baseDir, config) {
|
|
2975
3013
|
const result = createParseResult();
|
|
2976
3014
|
if (config.rulesDir) {
|
|
2977
|
-
const rulesDir =
|
|
3015
|
+
const rulesDir = join15(baseDir, config.rulesDir);
|
|
2978
3016
|
if (await fileExists(rulesDir)) {
|
|
2979
3017
|
const rulesResult = await parseAugmentRules(rulesDir, config);
|
|
2980
3018
|
addRules(result, rulesResult.rules);
|
|
@@ -2987,7 +3025,7 @@ async function parseUnifiedAugmentcode(baseDir, config) {
|
|
|
2987
3025
|
}
|
|
2988
3026
|
}
|
|
2989
3027
|
if (config.legacyFilePath) {
|
|
2990
|
-
const legacyPath =
|
|
3028
|
+
const legacyPath = join15(baseDir, config.legacyFilePath);
|
|
2991
3029
|
if (await fileExists(legacyPath)) {
|
|
2992
3030
|
const legacyResult = await parseAugmentGuidelines(legacyPath, config);
|
|
2993
3031
|
if (legacyResult.rule) {
|
|
@@ -3011,14 +3049,13 @@ async function parseAugmentRules(rulesDir, config) {
|
|
|
3011
3049
|
const files = await readdir2(rulesDir);
|
|
3012
3050
|
for (const file of files) {
|
|
3013
3051
|
if (file.endsWith(".md") || file.endsWith(".mdc")) {
|
|
3014
|
-
const filePath =
|
|
3052
|
+
const filePath = join15(rulesDir, file);
|
|
3015
3053
|
try {
|
|
3016
3054
|
const rawContent = await readFileContent(filePath);
|
|
3017
|
-
const parsed =
|
|
3018
|
-
const
|
|
3019
|
-
const
|
|
3020
|
-
const
|
|
3021
|
-
const tags = Array.isArray(frontmatterData.tags) ? frontmatterData.tags : void 0;
|
|
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");
|
|
3022
3059
|
const isRoot = ruleType === "always";
|
|
3023
3060
|
const filename = basename3(file, file.endsWith(".mdc") ? ".mdc" : ".md");
|
|
3024
3061
|
const frontmatter = {
|
|
@@ -3027,7 +3064,7 @@ async function parseAugmentRules(rulesDir, config) {
|
|
|
3027
3064
|
description,
|
|
3028
3065
|
globs: ["**/*"],
|
|
3029
3066
|
// AugmentCode doesn't use specific globs in the same way
|
|
3030
|
-
...tags && { tags }
|
|
3067
|
+
...tags.length > 0 && { tags }
|
|
3031
3068
|
};
|
|
3032
3069
|
rules.push({
|
|
3033
3070
|
frontmatter,
|
|
@@ -3078,8 +3115,7 @@ async function parseAugmentGuidelines(guidelinesPath, config) {
|
|
|
3078
3115
|
}
|
|
3079
3116
|
|
|
3080
3117
|
// src/parsers/shared-helpers.ts
|
|
3081
|
-
import { basename as basename4, join as
|
|
3082
|
-
import matter4 from "gray-matter";
|
|
3118
|
+
import { basename as basename4, join as join16 } from "path";
|
|
3083
3119
|
async function parseConfigurationFiles(baseDir = process.cwd(), config) {
|
|
3084
3120
|
const errors = [];
|
|
3085
3121
|
const rules = [];
|
|
@@ -3092,16 +3128,18 @@ async function parseConfigurationFiles(baseDir = process.cwd(), config) {
|
|
|
3092
3128
|
let content;
|
|
3093
3129
|
let frontmatter;
|
|
3094
3130
|
if (mainFile.useFrontmatter) {
|
|
3095
|
-
const parsed =
|
|
3096
|
-
content = parsed.content
|
|
3097
|
-
const parsedFrontmatter = parsed.data;
|
|
3131
|
+
const parsed = parseFrontmatter(rawContent);
|
|
3132
|
+
content = parsed.content;
|
|
3098
3133
|
frontmatter = {
|
|
3099
3134
|
root: mainFile.isRoot ?? false,
|
|
3100
3135
|
targets: [config.tool],
|
|
3101
|
-
description:
|
|
3102
|
-
globs:
|
|
3103
|
-
...parsedFrontmatter.tags && { tags: parsedFrontmatter.tags }
|
|
3136
|
+
description: extractStringField(parsed.data, "description", mainFile.description),
|
|
3137
|
+
globs: extractArrayField(parsed.data, "globs", ["**/*"])
|
|
3104
3138
|
};
|
|
3139
|
+
const tags = extractArrayField(parsed.data, "tags");
|
|
3140
|
+
if (tags.length > 0) {
|
|
3141
|
+
frontmatter.tags = tags;
|
|
3142
|
+
}
|
|
3105
3143
|
} else {
|
|
3106
3144
|
content = rawContent.trim();
|
|
3107
3145
|
frontmatter = {
|
|
@@ -3134,23 +3172,29 @@ async function parseConfigurationFiles(baseDir = process.cwd(), config) {
|
|
|
3134
3172
|
const files = await readdir2(dirPath);
|
|
3135
3173
|
for (const file of files) {
|
|
3136
3174
|
if (file.endsWith(dirConfig.filePattern)) {
|
|
3137
|
-
const filePath =
|
|
3175
|
+
const filePath = join16(dirPath, file);
|
|
3138
3176
|
const fileResult = await safeAsyncOperation(async () => {
|
|
3139
3177
|
const rawContent = await readFileContent(filePath);
|
|
3140
3178
|
let content;
|
|
3141
3179
|
let frontmatter;
|
|
3142
3180
|
const filename = file.replace(new RegExp(`\\${dirConfig.filePattern}$`), "");
|
|
3143
3181
|
if (dirConfig.filePattern === ".instructions.md") {
|
|
3144
|
-
const parsed =
|
|
3145
|
-
content = parsed.content
|
|
3146
|
-
const parsedFrontmatter = parsed.data;
|
|
3182
|
+
const parsed = parseFrontmatter(rawContent);
|
|
3183
|
+
content = parsed.content;
|
|
3147
3184
|
frontmatter = {
|
|
3148
3185
|
root: false,
|
|
3149
3186
|
targets: [config.tool],
|
|
3150
|
-
description:
|
|
3151
|
-
|
|
3152
|
-
|
|
3187
|
+
description: extractStringField(
|
|
3188
|
+
parsed.data,
|
|
3189
|
+
"description",
|
|
3190
|
+
`${dirConfig.description}: ${filename}`
|
|
3191
|
+
),
|
|
3192
|
+
globs: extractArrayField(parsed.data, "globs", ["**/*"])
|
|
3153
3193
|
};
|
|
3194
|
+
const tags = extractArrayField(parsed.data, "tags");
|
|
3195
|
+
if (tags.length > 0) {
|
|
3196
|
+
frontmatter.tags = tags;
|
|
3197
|
+
}
|
|
3154
3198
|
} else {
|
|
3155
3199
|
content = rawContent.trim();
|
|
3156
3200
|
frontmatter = {
|
|
@@ -3279,7 +3323,7 @@ async function parseMemoryFiles(memoryDir, config) {
|
|
|
3279
3323
|
const files = await readdir2(memoryDir);
|
|
3280
3324
|
for (const file of files) {
|
|
3281
3325
|
if (file.endsWith(".md")) {
|
|
3282
|
-
const filePath =
|
|
3326
|
+
const filePath = join16(memoryDir, file);
|
|
3283
3327
|
const content = await readFileContent(filePath);
|
|
3284
3328
|
if (content.trim()) {
|
|
3285
3329
|
const filename = basename4(file, ".md");
|
|
@@ -3309,20 +3353,19 @@ async function parseCommandsFiles(commandsDir, config) {
|
|
|
3309
3353
|
const files = await readdir2(commandsDir);
|
|
3310
3354
|
for (const file of files) {
|
|
3311
3355
|
if (file.endsWith(".md")) {
|
|
3312
|
-
const filePath =
|
|
3356
|
+
const filePath = join16(commandsDir, file);
|
|
3313
3357
|
const content = await readFileContent(filePath);
|
|
3314
3358
|
if (content.trim()) {
|
|
3315
3359
|
const filename = basename4(file, ".md");
|
|
3316
3360
|
let frontmatter;
|
|
3317
3361
|
let ruleContent;
|
|
3318
3362
|
try {
|
|
3319
|
-
const parsed =
|
|
3320
|
-
ruleContent = parsed.content
|
|
3321
|
-
const parsedFrontmatter = parsed.data;
|
|
3363
|
+
const parsed = parseFrontmatter(content);
|
|
3364
|
+
ruleContent = parsed.content;
|
|
3322
3365
|
frontmatter = {
|
|
3323
3366
|
root: false,
|
|
3324
3367
|
targets: [config.tool],
|
|
3325
|
-
description:
|
|
3368
|
+
description: extractStringField(parsed.data, "description", `Command: ${filename}`),
|
|
3326
3369
|
globs: ["**/*"]
|
|
3327
3370
|
};
|
|
3328
3371
|
} catch {
|
|
@@ -3423,7 +3466,7 @@ async function parseClineConfiguration(baseDir = process.cwd()) {
|
|
|
3423
3466
|
}
|
|
3424
3467
|
|
|
3425
3468
|
// src/parsers/codexcli.ts
|
|
3426
|
-
import { join as
|
|
3469
|
+
import { join as join17 } from "path";
|
|
3427
3470
|
|
|
3428
3471
|
// src/parsers/copilot.ts
|
|
3429
3472
|
async function parseCopilotConfiguration(baseDir = process.cwd()) {
|
|
@@ -3446,8 +3489,7 @@ async function parseCopilotConfiguration(baseDir = process.cwd()) {
|
|
|
3446
3489
|
}
|
|
3447
3490
|
|
|
3448
3491
|
// src/parsers/cursor.ts
|
|
3449
|
-
import { basename as basename5, join as
|
|
3450
|
-
import matter5 from "gray-matter";
|
|
3492
|
+
import { basename as basename5, join as join18 } from "path";
|
|
3451
3493
|
import { DEFAULT_SCHEMA, FAILSAFE_SCHEMA, load } from "js-yaml";
|
|
3452
3494
|
import { z as z7 } from "zod/mini";
|
|
3453
3495
|
var customMatterOptions = {
|
|
@@ -3571,12 +3613,12 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
|
|
|
3571
3613
|
const rules = [];
|
|
3572
3614
|
let ignorePatterns;
|
|
3573
3615
|
let mcpServers;
|
|
3574
|
-
const cursorFilePath =
|
|
3616
|
+
const cursorFilePath = join18(baseDir, ".cursorrules");
|
|
3575
3617
|
if (await fileExists(cursorFilePath)) {
|
|
3576
3618
|
try {
|
|
3577
3619
|
const rawContent = await readFileContent(cursorFilePath);
|
|
3578
|
-
const parsed =
|
|
3579
|
-
const content = parsed.content
|
|
3620
|
+
const parsed = parseFrontmatter(rawContent, { matterOptions: customMatterOptions });
|
|
3621
|
+
const content = parsed.content;
|
|
3580
3622
|
if (content) {
|
|
3581
3623
|
const frontmatter = convertCursorMdcFrontmatter(parsed.data, "cursorrules");
|
|
3582
3624
|
frontmatter.targets = ["cursor"];
|
|
@@ -3592,18 +3634,18 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
|
|
|
3592
3634
|
errors.push(`Failed to parse .cursorrules file: ${errorMessage}`);
|
|
3593
3635
|
}
|
|
3594
3636
|
}
|
|
3595
|
-
const cursorRulesDir =
|
|
3637
|
+
const cursorRulesDir = join18(baseDir, ".cursor", "rules");
|
|
3596
3638
|
if (await fileExists(cursorRulesDir)) {
|
|
3597
3639
|
try {
|
|
3598
3640
|
const { readdir: readdir2 } = await import("fs/promises");
|
|
3599
3641
|
const files = await readdir2(cursorRulesDir);
|
|
3600
3642
|
for (const file of files) {
|
|
3601
3643
|
if (file.endsWith(".mdc")) {
|
|
3602
|
-
const filePath =
|
|
3644
|
+
const filePath = join18(cursorRulesDir, file);
|
|
3603
3645
|
try {
|
|
3604
3646
|
const rawContent = await readFileContent(filePath);
|
|
3605
|
-
const parsed =
|
|
3606
|
-
const content = parsed.content
|
|
3647
|
+
const parsed = parseFrontmatter(rawContent, { matterOptions: customMatterOptions });
|
|
3648
|
+
const content = parsed.content;
|
|
3607
3649
|
if (content) {
|
|
3608
3650
|
const filename = basename5(file, ".mdc");
|
|
3609
3651
|
const frontmatter = convertCursorMdcFrontmatter(parsed.data, filename);
|
|
@@ -3628,7 +3670,7 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
|
|
|
3628
3670
|
if (rules.length === 0) {
|
|
3629
3671
|
errors.push("No Cursor configuration files found (.cursorrules or .cursor/rules/*.mdc)");
|
|
3630
3672
|
}
|
|
3631
|
-
const cursorIgnorePath =
|
|
3673
|
+
const cursorIgnorePath = join18(baseDir, ".cursorignore");
|
|
3632
3674
|
if (await fileExists(cursorIgnorePath)) {
|
|
3633
3675
|
try {
|
|
3634
3676
|
const content = await readFileContent(cursorIgnorePath);
|
|
@@ -3641,7 +3683,7 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
|
|
|
3641
3683
|
errors.push(`Failed to parse .cursorignore: ${errorMessage}`);
|
|
3642
3684
|
}
|
|
3643
3685
|
}
|
|
3644
|
-
const cursorMcpPath =
|
|
3686
|
+
const cursorMcpPath = join18(baseDir, ".cursor", "mcp.json");
|
|
3645
3687
|
if (await fileExists(cursorMcpPath)) {
|
|
3646
3688
|
try {
|
|
3647
3689
|
const content = await readFileContent(cursorMcpPath);
|
|
@@ -3691,11 +3733,11 @@ async function parseGeminiConfiguration(baseDir = process.cwd()) {
|
|
|
3691
3733
|
}
|
|
3692
3734
|
|
|
3693
3735
|
// src/parsers/junie.ts
|
|
3694
|
-
import { join as
|
|
3736
|
+
import { join as join19 } from "path";
|
|
3695
3737
|
async function parseJunieConfiguration(baseDir = process.cwd()) {
|
|
3696
3738
|
const errors = [];
|
|
3697
3739
|
const rules = [];
|
|
3698
|
-
const guidelinesPath =
|
|
3740
|
+
const guidelinesPath = join19(baseDir, ".junie", "guidelines.md");
|
|
3699
3741
|
if (!await fileExists(guidelinesPath)) {
|
|
3700
3742
|
errors.push(".junie/guidelines.md file not found");
|
|
3701
3743
|
return { rules, errors };
|
|
@@ -3748,8 +3790,7 @@ async function parseRooConfiguration(baseDir = process.cwd()) {
|
|
|
3748
3790
|
|
|
3749
3791
|
// src/parsers/windsurf.ts
|
|
3750
3792
|
import { readFile as readFile2 } from "fs/promises";
|
|
3751
|
-
import { join as
|
|
3752
|
-
import matter6 from "gray-matter";
|
|
3793
|
+
import { join as join20 } from "path";
|
|
3753
3794
|
|
|
3754
3795
|
// src/core/importer.ts
|
|
3755
3796
|
async function importConfiguration(options) {
|
|
@@ -3765,7 +3806,7 @@ async function importConfiguration(options) {
|
|
|
3765
3806
|
let ignorePatterns;
|
|
3766
3807
|
let mcpServers;
|
|
3767
3808
|
if (verbose) {
|
|
3768
|
-
|
|
3809
|
+
logger.log(`Importing ${tool} configuration from ${baseDir}...`);
|
|
3769
3810
|
}
|
|
3770
3811
|
try {
|
|
3771
3812
|
switch (tool) {
|
|
@@ -3841,7 +3882,7 @@ async function importConfiguration(options) {
|
|
|
3841
3882
|
if (rules.length === 0 && !ignorePatterns && !mcpServers) {
|
|
3842
3883
|
return { success: false, rulesCreated: 0, errors };
|
|
3843
3884
|
}
|
|
3844
|
-
const rulesDirPath =
|
|
3885
|
+
const rulesDirPath = join21(baseDir, rulesDir);
|
|
3845
3886
|
try {
|
|
3846
3887
|
const { mkdir: mkdir3 } = await import("fs/promises");
|
|
3847
3888
|
await mkdir3(rulesDirPath, { recursive: true });
|
|
@@ -3856,22 +3897,22 @@ async function importConfiguration(options) {
|
|
|
3856
3897
|
const baseFilename = rule.filename;
|
|
3857
3898
|
let targetDir = rulesDirPath;
|
|
3858
3899
|
if (rule.type === "command") {
|
|
3859
|
-
targetDir =
|
|
3900
|
+
targetDir = join21(rulesDirPath, "commands");
|
|
3860
3901
|
const { mkdir: mkdir3 } = await import("fs/promises");
|
|
3861
3902
|
await mkdir3(targetDir, { recursive: true });
|
|
3862
3903
|
} else {
|
|
3863
3904
|
if (!useLegacyLocation) {
|
|
3864
|
-
targetDir =
|
|
3905
|
+
targetDir = join21(rulesDirPath, "rules");
|
|
3865
3906
|
const { mkdir: mkdir3 } = await import("fs/promises");
|
|
3866
3907
|
await mkdir3(targetDir, { recursive: true });
|
|
3867
3908
|
}
|
|
3868
3909
|
}
|
|
3869
|
-
const filePath =
|
|
3910
|
+
const filePath = join21(targetDir, `${baseFilename}.md`);
|
|
3870
3911
|
const content = generateRuleFileContent(rule);
|
|
3871
3912
|
await writeFileContent(filePath, content);
|
|
3872
3913
|
rulesCreated++;
|
|
3873
3914
|
if (verbose) {
|
|
3874
|
-
|
|
3915
|
+
logger.success(`Created rule file: ${filePath}`);
|
|
3875
3916
|
}
|
|
3876
3917
|
} catch (error) {
|
|
3877
3918
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
@@ -3881,13 +3922,13 @@ async function importConfiguration(options) {
|
|
|
3881
3922
|
let ignoreFileCreated = false;
|
|
3882
3923
|
if (ignorePatterns && ignorePatterns.length > 0) {
|
|
3883
3924
|
try {
|
|
3884
|
-
const rulesyncignorePath =
|
|
3925
|
+
const rulesyncignorePath = join21(baseDir, ".rulesyncignore");
|
|
3885
3926
|
const ignoreContent = `${ignorePatterns.join("\n")}
|
|
3886
3927
|
`;
|
|
3887
3928
|
await writeFileContent(rulesyncignorePath, ignoreContent);
|
|
3888
3929
|
ignoreFileCreated = true;
|
|
3889
3930
|
if (verbose) {
|
|
3890
|
-
|
|
3931
|
+
logger.success(`Created .rulesyncignore with ${ignorePatterns.length} patterns`);
|
|
3891
3932
|
}
|
|
3892
3933
|
} catch (error) {
|
|
3893
3934
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
@@ -3897,13 +3938,13 @@ async function importConfiguration(options) {
|
|
|
3897
3938
|
let mcpFileCreated = false;
|
|
3898
3939
|
if (mcpServers && Object.keys(mcpServers).length > 0) {
|
|
3899
3940
|
try {
|
|
3900
|
-
const mcpPath =
|
|
3941
|
+
const mcpPath = join21(baseDir, rulesDir, ".mcp.json");
|
|
3901
3942
|
const mcpContent = `${JSON.stringify({ mcpServers }, null, 2)}
|
|
3902
3943
|
`;
|
|
3903
3944
|
await writeFileContent(mcpPath, mcpContent);
|
|
3904
3945
|
mcpFileCreated = true;
|
|
3905
3946
|
if (verbose) {
|
|
3906
|
-
|
|
3947
|
+
logger.success(`Created .mcp.json with ${Object.keys(mcpServers).length} servers`);
|
|
3907
3948
|
}
|
|
3908
3949
|
} catch (error) {
|
|
3909
3950
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
@@ -3924,10 +3965,10 @@ function generateRuleFileContent(rule) {
|
|
|
3924
3965
|
description: rule.frontmatter.description,
|
|
3925
3966
|
targets: rule.frontmatter.targets
|
|
3926
3967
|
};
|
|
3927
|
-
const frontmatter2 =
|
|
3968
|
+
const frontmatter2 = matter2.stringify("", simplifiedFrontmatter);
|
|
3928
3969
|
return frontmatter2 + rule.content;
|
|
3929
3970
|
}
|
|
3930
|
-
const frontmatter =
|
|
3971
|
+
const frontmatter = matter2.stringify("", rule.frontmatter);
|
|
3931
3972
|
return frontmatter + rule.content;
|
|
3932
3973
|
}
|
|
3933
3974
|
|
|
@@ -3993,23 +4034,23 @@ async function importCommand(options = {}) {
|
|
|
3993
4034
|
}
|
|
3994
4035
|
|
|
3995
4036
|
// src/cli/commands/init.ts
|
|
3996
|
-
import { join as
|
|
4037
|
+
import { join as join22 } from "path";
|
|
3997
4038
|
async function initCommand(options = {}) {
|
|
3998
4039
|
const configResult = await loadConfig();
|
|
3999
4040
|
const config = configResult.config;
|
|
4000
4041
|
const aiRulesDir = config.aiRulesDir;
|
|
4001
|
-
|
|
4042
|
+
logger.log("Initializing rulesync...");
|
|
4002
4043
|
await ensureDir(aiRulesDir);
|
|
4003
4044
|
const useLegacy = options.legacy ?? config.legacy ?? false;
|
|
4004
|
-
const rulesDir = useLegacy ? aiRulesDir :
|
|
4045
|
+
const rulesDir = useLegacy ? aiRulesDir : join22(aiRulesDir, "rules");
|
|
4005
4046
|
if (!useLegacy) {
|
|
4006
4047
|
await ensureDir(rulesDir);
|
|
4007
4048
|
}
|
|
4008
4049
|
await createSampleFiles(rulesDir);
|
|
4009
|
-
|
|
4010
|
-
|
|
4011
|
-
|
|
4012
|
-
|
|
4050
|
+
logger.success("rulesync initialized successfully!");
|
|
4051
|
+
logger.log("\nNext steps:");
|
|
4052
|
+
logger.log(`1. Edit rule files in ${rulesDir}/`);
|
|
4053
|
+
logger.log("2. Run 'rulesync generate' to create configuration files");
|
|
4013
4054
|
}
|
|
4014
4055
|
async function createSampleFiles(rulesDir) {
|
|
4015
4056
|
const sampleFile = {
|
|
@@ -4047,36 +4088,36 @@ globs: ["**/*"]
|
|
|
4047
4088
|
- Follow single responsibility principle
|
|
4048
4089
|
`
|
|
4049
4090
|
};
|
|
4050
|
-
const filepath =
|
|
4091
|
+
const filepath = join22(rulesDir, sampleFile.filename);
|
|
4051
4092
|
if (!await fileExists(filepath)) {
|
|
4052
4093
|
await writeFileContent(filepath, sampleFile.content);
|
|
4053
|
-
|
|
4094
|
+
logger.success(`Created ${filepath}`);
|
|
4054
4095
|
} else {
|
|
4055
|
-
|
|
4096
|
+
logger.log(`Skipped ${filepath} (already exists)`);
|
|
4056
4097
|
}
|
|
4057
4098
|
}
|
|
4058
4099
|
|
|
4059
4100
|
// src/cli/commands/status.ts
|
|
4060
4101
|
async function statusCommand() {
|
|
4061
4102
|
const config = getDefaultConfig();
|
|
4062
|
-
|
|
4063
|
-
|
|
4103
|
+
logger.log("rulesync Status");
|
|
4104
|
+
logger.log("===============");
|
|
4064
4105
|
const rulesyncExists = await fileExists(config.aiRulesDir);
|
|
4065
|
-
|
|
4106
|
+
logger.log(`
|
|
4066
4107
|
\u{1F4C1} .rulesync directory: ${rulesyncExists ? "\u2705 Found" : "\u274C Not found"}`);
|
|
4067
4108
|
if (!rulesyncExists) {
|
|
4068
|
-
|
|
4109
|
+
logger.log("\n\u{1F4A1} Run 'rulesync init' to get started");
|
|
4069
4110
|
return;
|
|
4070
4111
|
}
|
|
4071
4112
|
try {
|
|
4072
4113
|
const rules = await parseRulesFromDirectory(config.aiRulesDir);
|
|
4073
|
-
|
|
4114
|
+
logger.log(`
|
|
4074
4115
|
\u{1F4CB} Rules: ${rules.length} total`);
|
|
4075
4116
|
if (rules.length > 0) {
|
|
4076
4117
|
const rootRules = rules.filter((r) => r.frontmatter.root).length;
|
|
4077
4118
|
const nonRootRules = rules.length - rootRules;
|
|
4078
|
-
|
|
4079
|
-
|
|
4119
|
+
logger.log(` - Root rules: ${rootRules}`);
|
|
4120
|
+
logger.log(` - Non-root rules: ${nonRootRules}`);
|
|
4080
4121
|
const targetCounts = { copilot: 0, cursor: 0, cline: 0, claudecode: 0, roo: 0 };
|
|
4081
4122
|
for (const rule of rules) {
|
|
4082
4123
|
const targets = rule.frontmatter.targets[0] === "*" ? config.defaultTargets : rule.frontmatter.targets;
|
|
@@ -4088,63 +4129,63 @@ async function statusCommand() {
|
|
|
4088
4129
|
else if (target === "roo") targetCounts.roo++;
|
|
4089
4130
|
}
|
|
4090
4131
|
}
|
|
4091
|
-
|
|
4092
|
-
|
|
4093
|
-
|
|
4094
|
-
|
|
4095
|
-
|
|
4096
|
-
|
|
4132
|
+
logger.log("\n\u{1F3AF} Target tool coverage:");
|
|
4133
|
+
logger.log(` - Copilot: ${targetCounts.copilot} rules`);
|
|
4134
|
+
logger.log(` - Cursor: ${targetCounts.cursor} rules`);
|
|
4135
|
+
logger.log(` - Cline: ${targetCounts.cline} rules`);
|
|
4136
|
+
logger.log(` - Claude Code: ${targetCounts.claudecode} rules`);
|
|
4137
|
+
logger.log(` - Roo: ${targetCounts.roo} rules`);
|
|
4097
4138
|
}
|
|
4098
|
-
|
|
4139
|
+
logger.log("\n\u{1F4E4} Generated files:");
|
|
4099
4140
|
for (const [tool, outputPath] of Object.entries(config.outputPaths)) {
|
|
4100
4141
|
const outputExists = await fileExists(outputPath);
|
|
4101
|
-
|
|
4142
|
+
logger.log(` - ${tool}: ${outputExists ? "\u2705 Generated" : "\u274C Not found"}`);
|
|
4102
4143
|
}
|
|
4103
4144
|
if (rules.length > 0) {
|
|
4104
|
-
|
|
4145
|
+
logger.log("\n\u{1F4A1} Run 'rulesync generate' to update configuration files");
|
|
4105
4146
|
}
|
|
4106
4147
|
} catch (error) {
|
|
4107
|
-
|
|
4148
|
+
logger.error("\nFailed to get status:", error);
|
|
4108
4149
|
}
|
|
4109
4150
|
}
|
|
4110
4151
|
|
|
4111
4152
|
// src/cli/commands/validate.ts
|
|
4112
4153
|
async function validateCommand() {
|
|
4113
4154
|
const config = getDefaultConfig();
|
|
4114
|
-
|
|
4155
|
+
logger.log("Validating rulesync configuration...");
|
|
4115
4156
|
if (!await fileExists(config.aiRulesDir)) {
|
|
4116
|
-
|
|
4157
|
+
logger.error(".rulesync directory not found. Run 'rulesync init' first.");
|
|
4117
4158
|
process.exit(1);
|
|
4118
4159
|
}
|
|
4119
4160
|
try {
|
|
4120
4161
|
const rules = await parseRulesFromDirectory(config.aiRulesDir);
|
|
4121
4162
|
if (rules.length === 0) {
|
|
4122
|
-
|
|
4163
|
+
logger.warn("No rules found in .rulesync directory");
|
|
4123
4164
|
return;
|
|
4124
4165
|
}
|
|
4125
|
-
|
|
4166
|
+
logger.log(`Found ${rules.length} rule(s), validating...`);
|
|
4126
4167
|
const validation = await validateRules(rules);
|
|
4127
4168
|
if (validation.warnings.length > 0) {
|
|
4128
|
-
|
|
4169
|
+
logger.log("\n\u26A0\uFE0F Warnings:");
|
|
4129
4170
|
for (const warning of validation.warnings) {
|
|
4130
|
-
|
|
4171
|
+
logger.log(` - ${warning}`);
|
|
4131
4172
|
}
|
|
4132
4173
|
}
|
|
4133
4174
|
if (validation.errors.length > 0) {
|
|
4134
|
-
|
|
4175
|
+
logger.log("\nErrors:");
|
|
4135
4176
|
for (const error of validation.errors) {
|
|
4136
|
-
|
|
4177
|
+
logger.log(` - ${error}`);
|
|
4137
4178
|
}
|
|
4138
4179
|
}
|
|
4139
4180
|
if (validation.isValid) {
|
|
4140
|
-
|
|
4181
|
+
logger.success("\nAll rules are valid!");
|
|
4141
4182
|
} else {
|
|
4142
|
-
|
|
4143
|
-
|
|
4183
|
+
logger.log(`
|
|
4184
|
+
Validation failed with ${validation.errors.length} error(s)`);
|
|
4144
4185
|
process.exit(1);
|
|
4145
4186
|
}
|
|
4146
4187
|
} catch (error) {
|
|
4147
|
-
|
|
4188
|
+
logger.error("Failed to validate rules:", error);
|
|
4148
4189
|
process.exit(1);
|
|
4149
4190
|
}
|
|
4150
4191
|
}
|
|
@@ -4153,8 +4194,8 @@ async function validateCommand() {
|
|
|
4153
4194
|
import { watch } from "chokidar";
|
|
4154
4195
|
async function watchCommand() {
|
|
4155
4196
|
const config = getDefaultConfig();
|
|
4156
|
-
|
|
4157
|
-
|
|
4197
|
+
logger.log("\u{1F440} Watching for changes in .rulesync directory...");
|
|
4198
|
+
logger.log("Press Ctrl+C to stop watching");
|
|
4158
4199
|
await generateCommand({ verbose: false });
|
|
4159
4200
|
const watcher = watch(`${config.aiRulesDir}/**/*.md`, {
|
|
4160
4201
|
ignoreInitial: true,
|
|
@@ -4164,26 +4205,26 @@ async function watchCommand() {
|
|
|
4164
4205
|
const handleChange = async (path5) => {
|
|
4165
4206
|
if (isGenerating) return;
|
|
4166
4207
|
isGenerating = true;
|
|
4167
|
-
|
|
4208
|
+
logger.log(`
|
|
4168
4209
|
\u{1F4DD} Detected change in ${path5}`);
|
|
4169
4210
|
try {
|
|
4170
4211
|
await generateCommand({ verbose: false });
|
|
4171
|
-
|
|
4212
|
+
logger.success("Regenerated configuration files");
|
|
4172
4213
|
} catch (error) {
|
|
4173
|
-
|
|
4214
|
+
logger.error("Failed to regenerate:", error);
|
|
4174
4215
|
} finally {
|
|
4175
4216
|
isGenerating = false;
|
|
4176
4217
|
}
|
|
4177
4218
|
};
|
|
4178
4219
|
watcher.on("change", handleChange).on("add", handleChange).on("unlink", (path5) => {
|
|
4179
|
-
|
|
4220
|
+
logger.log(`
|
|
4180
4221
|
\u{1F5D1}\uFE0F Removed ${path5}`);
|
|
4181
4222
|
handleChange(path5);
|
|
4182
4223
|
}).on("error", (error) => {
|
|
4183
|
-
|
|
4224
|
+
logger.error("Watcher error:", error);
|
|
4184
4225
|
});
|
|
4185
4226
|
process.on("SIGINT", () => {
|
|
4186
|
-
|
|
4227
|
+
logger.log("\n\n\u{1F44B} Stopping watcher...");
|
|
4187
4228
|
watcher.close();
|
|
4188
4229
|
process.exit(0);
|
|
4189
4230
|
});
|
|
@@ -4191,7 +4232,7 @@ async function watchCommand() {
|
|
|
4191
4232
|
|
|
4192
4233
|
// src/cli/index.ts
|
|
4193
4234
|
var program = new Command();
|
|
4194
|
-
program.name("rulesync").description("Unified AI rules management CLI tool").version("0.
|
|
4235
|
+
program.name("rulesync").description("Unified AI rules management CLI tool").version("0.63.0");
|
|
4195
4236
|
program.command("init").description("Initialize rulesync in current directory").option("--legacy", "Use legacy file location (.rulesync/*.md instead of .rulesync/rules/*.md)").action(initCommand);
|
|
4196
4237
|
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);
|
|
4197
4238
|
program.command("gitignore").description("Add generated files to .gitignore").action(gitignoreCommand);
|