rulesync 0.43.0 → 0.45.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/{chunk-B3627VQY.js → chunk-22GWBUIP.js} +9 -5
- package/dist/{chunk-XHRBWFGI.js → chunk-BD37M3ZH.js} +2 -2
- package/dist/{chunk-TCK42GOL.js → chunk-DCSO5MY7.js} +2 -2
- package/dist/{chunk-ZMAL5LIX.js → chunk-FAZT3ILF.js} +4 -8
- package/dist/{chunk-THWXK5Z2.js → chunk-I5XVU7C6.js} +4 -1
- package/dist/{chunk-RL3TE3EZ.js → chunk-PJUNIIF4.js} +4 -8
- package/dist/{chunk-2FR4Z37J.js → chunk-ZORSPGDD.js} +1 -1
- package/dist/{claudecode-ITHKV345.js → claudecode-KSK2BEI7.js} +2 -2
- package/dist/{cline-PKE6TYNJ.js → cline-T5YVGYBF.js} +2 -2
- package/dist/{copilot-5JP6D4NO.js → copilot-UDCWNUAH.js} +2 -2
- package/dist/{cursor-RHFDG6T2.js → cursor-KPV6OVST.js} +2 -2
- package/dist/{geminicli-B3FFO5WV.js → geminicli-2DC5F34J.js} +2 -2
- package/dist/index.cjs +163 -210
- package/dist/index.js +157 -209
- package/dist/{roo-UOTKEOH7.js → roo-DRA2SU4L.js} +2 -2
- package/package.json +12 -5
package/dist/index.js
CHANGED
|
@@ -1,23 +1,27 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
generateClaudeMcp
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-22GWBUIP.js";
|
|
5
5
|
import {
|
|
6
6
|
generateClineMcp
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-BD37M3ZH.js";
|
|
8
8
|
import {
|
|
9
9
|
generateCopilotMcp
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-ZORSPGDD.js";
|
|
11
11
|
import {
|
|
12
12
|
generateCursorMcp
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-FAZT3ILF.js";
|
|
14
14
|
import {
|
|
15
15
|
generateGeminiCliMcp
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-DCSO5MY7.js";
|
|
17
17
|
import {
|
|
18
18
|
generateRooMcp
|
|
19
|
-
} from "./chunk-
|
|
20
|
-
import
|
|
19
|
+
} from "./chunk-PJUNIIF4.js";
|
|
20
|
+
import {
|
|
21
|
+
RulesyncTargetsSchema,
|
|
22
|
+
ToolTargetSchema,
|
|
23
|
+
ToolTargetsSchema
|
|
24
|
+
} from "./chunk-I5XVU7C6.js";
|
|
21
25
|
|
|
22
26
|
// src/cli/index.ts
|
|
23
27
|
import { Command } from "commander";
|
|
@@ -43,10 +47,11 @@ function getDefaultConfig() {
|
|
|
43
47
|
};
|
|
44
48
|
}
|
|
45
49
|
function resolveTargets(targets, config) {
|
|
46
|
-
if (targets[0] === "*") {
|
|
50
|
+
if (targets.length === 1 && targets[0] === "*") {
|
|
47
51
|
return config.defaultTargets;
|
|
48
52
|
}
|
|
49
|
-
|
|
53
|
+
const validatedTargets = ToolTargetsSchema.parse(targets);
|
|
54
|
+
return validatedTargets;
|
|
50
55
|
}
|
|
51
56
|
|
|
52
57
|
// src/cli/commands/add.ts
|
|
@@ -89,11 +94,14 @@ async function addCommand(filename) {
|
|
|
89
94
|
import { join as join4 } from "path";
|
|
90
95
|
|
|
91
96
|
// src/types/claudecode.ts
|
|
92
|
-
import { z } from "zod/v4";
|
|
97
|
+
import { z } from "zod/v4-mini";
|
|
93
98
|
var ClaudeSettingsSchema = z.looseObject({
|
|
94
|
-
permissions: z.
|
|
95
|
-
|
|
96
|
-
|
|
99
|
+
permissions: z._default(
|
|
100
|
+
z.looseObject({
|
|
101
|
+
deny: z._default(z.array(z.string()), [])
|
|
102
|
+
}),
|
|
103
|
+
{ deny: [] }
|
|
104
|
+
)
|
|
97
105
|
});
|
|
98
106
|
|
|
99
107
|
// src/utils/file.ts
|
|
@@ -256,12 +264,11 @@ function generateClaudeMarkdown(rootRules, detailRules) {
|
|
|
256
264
|
if (detailRules.length > 0) {
|
|
257
265
|
lines.push("Please also reference the following documents as needed:");
|
|
258
266
|
lines.push("");
|
|
259
|
-
lines.push("| Document | Description | File Patterns |");
|
|
260
|
-
lines.push("|----------|-------------|---------------|");
|
|
261
267
|
for (const rule of detailRules) {
|
|
262
|
-
const
|
|
268
|
+
const escapedDescription = rule.frontmatter.description.replace(/"/g, '\\"');
|
|
269
|
+
const globsText = rule.frontmatter.globs.join(",");
|
|
263
270
|
lines.push(
|
|
264
|
-
|
|
271
|
+
`@.claude/memories/${rule.filename}.md description: "${escapedDescription}" globs: "${globsText}"`
|
|
265
272
|
);
|
|
266
273
|
}
|
|
267
274
|
lines.push("");
|
|
@@ -652,6 +659,72 @@ async function generateForTool(tool, rules, config, baseDir) {
|
|
|
652
659
|
// src/core/parser.ts
|
|
653
660
|
import { basename } from "path";
|
|
654
661
|
import matter from "gray-matter";
|
|
662
|
+
|
|
663
|
+
// src/types/config.ts
|
|
664
|
+
import { z as z2 } from "zod/v4-mini";
|
|
665
|
+
var ConfigSchema = z2.object({
|
|
666
|
+
aiRulesDir: z2.string(),
|
|
667
|
+
outputPaths: z2.record(ToolTargetSchema, z2.string()),
|
|
668
|
+
watchEnabled: z2.boolean(),
|
|
669
|
+
defaultTargets: ToolTargetsSchema
|
|
670
|
+
});
|
|
671
|
+
|
|
672
|
+
// src/types/mcp.ts
|
|
673
|
+
import { z as z3 } from "zod/v4-mini";
|
|
674
|
+
var McpTransportTypeSchema = z3.enum(["stdio", "sse", "http"]);
|
|
675
|
+
var McpServerBaseSchema = z3.object({
|
|
676
|
+
command: z3.optional(z3.string()),
|
|
677
|
+
args: z3.optional(z3.array(z3.string())),
|
|
678
|
+
url: z3.optional(z3.string()),
|
|
679
|
+
httpUrl: z3.optional(z3.string()),
|
|
680
|
+
env: z3.optional(z3.record(z3.string(), z3.string())),
|
|
681
|
+
disabled: z3.optional(z3.boolean()),
|
|
682
|
+
networkTimeout: z3.optional(z3.number()),
|
|
683
|
+
timeout: z3.optional(z3.number()),
|
|
684
|
+
trust: z3.optional(z3.boolean()),
|
|
685
|
+
cwd: z3.optional(z3.string()),
|
|
686
|
+
transport: z3.optional(McpTransportTypeSchema),
|
|
687
|
+
type: z3.optional(z3.enum(["sse", "streamable-http"])),
|
|
688
|
+
alwaysAllow: z3.optional(z3.array(z3.string())),
|
|
689
|
+
tools: z3.optional(z3.array(z3.string()))
|
|
690
|
+
});
|
|
691
|
+
var RulesyncMcpServerSchema = z3.extend(McpServerBaseSchema, {
|
|
692
|
+
targets: z3.optional(RulesyncTargetsSchema)
|
|
693
|
+
});
|
|
694
|
+
var McpConfigSchema = z3.object({
|
|
695
|
+
mcpServers: z3.record(z3.string(), McpServerBaseSchema)
|
|
696
|
+
});
|
|
697
|
+
var RulesyncMcpConfigSchema = z3.object({
|
|
698
|
+
mcpServers: z3.record(z3.string(), RulesyncMcpServerSchema)
|
|
699
|
+
});
|
|
700
|
+
|
|
701
|
+
// src/types/rules.ts
|
|
702
|
+
import { z as z4 } from "zod/v4-mini";
|
|
703
|
+
var RuleFrontmatterSchema = z4.object({
|
|
704
|
+
root: z4.boolean(),
|
|
705
|
+
targets: RulesyncTargetsSchema,
|
|
706
|
+
description: z4.string(),
|
|
707
|
+
globs: z4.array(z4.string()),
|
|
708
|
+
cursorRuleType: z4.optional(z4.enum(["always", "manual", "specificFiles", "intelligently"]))
|
|
709
|
+
});
|
|
710
|
+
var ParsedRuleSchema = z4.object({
|
|
711
|
+
frontmatter: RuleFrontmatterSchema,
|
|
712
|
+
content: z4.string(),
|
|
713
|
+
filename: z4.string(),
|
|
714
|
+
filepath: z4.string()
|
|
715
|
+
});
|
|
716
|
+
var GeneratedOutputSchema = z4.object({
|
|
717
|
+
tool: ToolTargetSchema,
|
|
718
|
+
filepath: z4.string(),
|
|
719
|
+
content: z4.string()
|
|
720
|
+
});
|
|
721
|
+
var GenerateOptionsSchema = z4.object({
|
|
722
|
+
targetTools: z4.optional(ToolTargetsSchema),
|
|
723
|
+
outputDir: z4.optional(z4.string()),
|
|
724
|
+
watch: z4.optional(z4.boolean())
|
|
725
|
+
});
|
|
726
|
+
|
|
727
|
+
// src/core/parser.ts
|
|
655
728
|
async function parseRulesFromDirectory(aiRulesDir) {
|
|
656
729
|
const ignorePatterns = await loadIgnorePatterns();
|
|
657
730
|
const ruleFiles = await findFiles(aiRulesDir, ".md", ignorePatterns.patterns);
|
|
@@ -685,84 +758,20 @@ ${errors.join("\n")}`);
|
|
|
685
758
|
async function parseRuleFile(filepath) {
|
|
686
759
|
const content = await readFileContent(filepath);
|
|
687
760
|
const parsed = matter(content);
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
}
|
|
698
|
-
function validateFrontmatter(data, filepath) {
|
|
699
|
-
if (!data || typeof data !== "object") {
|
|
700
|
-
if (!data) {
|
|
701
|
-
throw new Error(
|
|
702
|
-
`Missing frontmatter in ${filepath}: file must contain YAML frontmatter with required fields (root, targets, description, globs)`
|
|
703
|
-
);
|
|
704
|
-
}
|
|
705
|
-
throw new Error(`Invalid frontmatter in ${filepath}: frontmatter must be a valid YAML object`);
|
|
706
|
-
}
|
|
707
|
-
const obj = data;
|
|
708
|
-
if (Object.keys(obj).length === 0) {
|
|
709
|
-
throw new Error(
|
|
710
|
-
`Missing frontmatter in ${filepath}: file must contain YAML frontmatter with required fields (root, targets, description, globs)`
|
|
711
|
-
);
|
|
712
|
-
}
|
|
713
|
-
if (obj.root === void 0) {
|
|
714
|
-
throw new Error(`Missing required field "root" in ${filepath}: must be true or false`);
|
|
715
|
-
}
|
|
716
|
-
if (typeof obj.root !== "boolean") {
|
|
717
|
-
throw new Error(
|
|
718
|
-
`Invalid "root" field in ${filepath}: must be a boolean (true or false), got ${typeof obj.root}`
|
|
719
|
-
);
|
|
720
|
-
}
|
|
721
|
-
if (obj.targets === void 0) {
|
|
722
|
-
throw new Error(
|
|
723
|
-
`Missing required field "targets" in ${filepath}: must be an array like ["*"] or ["copilot", "cursor"]`
|
|
724
|
-
);
|
|
725
|
-
}
|
|
726
|
-
if (!Array.isArray(obj.targets)) {
|
|
727
|
-
throw new Error(
|
|
728
|
-
`Invalid "targets" field in ${filepath}: must be an array, got ${typeof obj.targets}`
|
|
729
|
-
);
|
|
730
|
-
}
|
|
731
|
-
const validTargets = ["copilot", "cursor", "cline", "claudecode", "roo", "geminicli", "*"];
|
|
732
|
-
for (const target of obj.targets) {
|
|
733
|
-
if (typeof target !== "string" || !validTargets.includes(target)) {
|
|
734
|
-
throw new Error(
|
|
735
|
-
`Invalid target "${target}" in ${filepath}: must be one of ${validTargets.join(", ")}`
|
|
736
|
-
);
|
|
737
|
-
}
|
|
738
|
-
}
|
|
739
|
-
if (obj.description === void 0) {
|
|
740
|
-
throw new Error(
|
|
741
|
-
`Missing required field "description" in ${filepath}: must be a descriptive string`
|
|
742
|
-
);
|
|
743
|
-
}
|
|
744
|
-
if (typeof obj.description !== "string") {
|
|
745
|
-
throw new Error(
|
|
746
|
-
`Invalid "description" field in ${filepath}: must be a string, got ${typeof obj.description}`
|
|
747
|
-
);
|
|
748
|
-
}
|
|
749
|
-
if (obj.globs === void 0) {
|
|
750
|
-
throw new Error(
|
|
751
|
-
`Missing required field "globs" in ${filepath}: must be an array of file patterns like ["**/*.ts"]`
|
|
752
|
-
);
|
|
753
|
-
}
|
|
754
|
-
if (!Array.isArray(obj.globs)) {
|
|
761
|
+
try {
|
|
762
|
+
const frontmatter = RuleFrontmatterSchema.parse(parsed.data);
|
|
763
|
+
const filename = basename(filepath, ".md");
|
|
764
|
+
return {
|
|
765
|
+
frontmatter,
|
|
766
|
+
content: parsed.content,
|
|
767
|
+
filename,
|
|
768
|
+
filepath
|
|
769
|
+
};
|
|
770
|
+
} catch (error) {
|
|
755
771
|
throw new Error(
|
|
756
|
-
`Invalid
|
|
772
|
+
`Invalid frontmatter in ${filepath}: ${error instanceof Error ? error.message : String(error)}`
|
|
757
773
|
);
|
|
758
774
|
}
|
|
759
|
-
for (const glob of obj.globs) {
|
|
760
|
-
if (typeof glob !== "string") {
|
|
761
|
-
throw new Error(
|
|
762
|
-
`Invalid glob pattern in ${filepath}: all globs must be strings, got ${typeof glob}`
|
|
763
|
-
);
|
|
764
|
-
}
|
|
765
|
-
}
|
|
766
775
|
}
|
|
767
776
|
|
|
768
777
|
// src/core/validator.ts
|
|
@@ -833,13 +842,11 @@ function parseMcpConfig(projectRoot) {
|
|
|
833
842
|
rawConfig.mcpServers = rawConfig.servers;
|
|
834
843
|
delete rawConfig.servers;
|
|
835
844
|
}
|
|
836
|
-
if (!rawConfig.mcpServers || typeof rawConfig.mcpServers !== "object") {
|
|
837
|
-
throw new Error("Invalid mcp.json: 'mcpServers' field must be an object");
|
|
838
|
-
}
|
|
839
845
|
if (rawConfig.tools) {
|
|
840
846
|
delete rawConfig.tools;
|
|
841
847
|
}
|
|
842
|
-
|
|
848
|
+
const validatedConfig = RulesyncMcpConfigSchema.parse(rawConfig);
|
|
849
|
+
return validatedConfig;
|
|
843
850
|
} catch (error) {
|
|
844
851
|
throw new Error(
|
|
845
852
|
`Failed to parse mcp.json: ${error instanceof Error ? error.message : String(error)}`
|
|
@@ -1212,6 +1219,9 @@ async function parseClaudeSettings(settingsPath) {
|
|
|
1212
1219
|
const settings = JSON.parse(content);
|
|
1213
1220
|
if (typeof settings === "object" && settings !== null && "permissions" in settings) {
|
|
1214
1221
|
const permissions = settings.permissions;
|
|
1222
|
+
if (typeof permissions !== "object" || permissions === null) {
|
|
1223
|
+
return { ignorePatterns: [], errors: [] };
|
|
1224
|
+
}
|
|
1215
1225
|
if (permissions && "deny" in permissions && Array.isArray(permissions.deny)) {
|
|
1216
1226
|
const readPatterns = permissions.deny.filter(
|
|
1217
1227
|
(rule) => typeof rule === "string" && rule.startsWith("Read(") && rule.endsWith(")")
|
|
@@ -1224,11 +1234,9 @@ async function parseClaudeSettings(settingsPath) {
|
|
|
1224
1234
|
}
|
|
1225
1235
|
}
|
|
1226
1236
|
}
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
mcpServers = servers;
|
|
1231
|
-
}
|
|
1237
|
+
const parseResult = RulesyncMcpConfigSchema.safeParse(settings);
|
|
1238
|
+
if (parseResult.success && Object.keys(parseResult.data.mcpServers).length > 0) {
|
|
1239
|
+
mcpServers = parseResult.data.mcpServers;
|
|
1232
1240
|
}
|
|
1233
1241
|
} catch (error) {
|
|
1234
1242
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
@@ -1387,16 +1395,25 @@ async function parseCopilotConfiguration(baseDir = process.cwd()) {
|
|
|
1387
1395
|
import { basename as basename4, join as join16 } from "path";
|
|
1388
1396
|
import matter3 from "gray-matter";
|
|
1389
1397
|
import { DEFAULT_SCHEMA, FAILSAFE_SCHEMA, load } from "js-yaml";
|
|
1398
|
+
import { z as z5 } from "zod/v4-mini";
|
|
1390
1399
|
var customMatterOptions = {
|
|
1391
1400
|
engines: {
|
|
1392
1401
|
yaml: {
|
|
1393
1402
|
parse: (str) => {
|
|
1394
1403
|
try {
|
|
1395
|
-
|
|
1396
|
-
|
|
1404
|
+
const preprocessed = str.replace(/^(\s*globs:\s*)\*\s*$/gm, '$1"*"').replace(/^(\s*globs:\s*)([^\s"'[\n][^"'[\n]*?)(\s*)$/gm, '$1"$2"$3');
|
|
1405
|
+
const result = load(preprocessed, { schema: DEFAULT_SCHEMA });
|
|
1406
|
+
if (typeof result === "object" && result !== null) {
|
|
1407
|
+
return result;
|
|
1408
|
+
}
|
|
1409
|
+
throw new Error("Failed to parse YAML: result is not an object");
|
|
1397
1410
|
} catch (error) {
|
|
1398
1411
|
try {
|
|
1399
|
-
|
|
1412
|
+
const result = load(str, { schema: FAILSAFE_SCHEMA });
|
|
1413
|
+
if (typeof result === "object" && result !== null) {
|
|
1414
|
+
return result;
|
|
1415
|
+
}
|
|
1416
|
+
throw new Error("Failed to parse YAML: result is not an object");
|
|
1400
1417
|
} catch {
|
|
1401
1418
|
throw error;
|
|
1402
1419
|
}
|
|
@@ -1406,7 +1423,18 @@ var customMatterOptions = {
|
|
|
1406
1423
|
}
|
|
1407
1424
|
};
|
|
1408
1425
|
function convertCursorMdcFrontmatter(cursorFrontmatter, _filename) {
|
|
1409
|
-
const
|
|
1426
|
+
const FrontmatterSchema = z5.record(z5.string(), z5.unknown());
|
|
1427
|
+
const parseResult = FrontmatterSchema.safeParse(cursorFrontmatter);
|
|
1428
|
+
if (!parseResult.success) {
|
|
1429
|
+
return {
|
|
1430
|
+
root: false,
|
|
1431
|
+
targets: ["*"],
|
|
1432
|
+
description: "",
|
|
1433
|
+
globs: [],
|
|
1434
|
+
cursorRuleType: "manual"
|
|
1435
|
+
};
|
|
1436
|
+
}
|
|
1437
|
+
const frontmatter = parseResult.data;
|
|
1410
1438
|
const description = normalizeValue(frontmatter?.description);
|
|
1411
1439
|
const globs = normalizeGlobsValue(frontmatter?.globs);
|
|
1412
1440
|
const alwaysApply = frontmatter?.alwaysApply === true || frontmatter?.alwaysApply === "true";
|
|
@@ -1441,7 +1469,7 @@ function convertCursorMdcFrontmatter(cursorFrontmatter, _filename) {
|
|
|
1441
1469
|
return {
|
|
1442
1470
|
root: false,
|
|
1443
1471
|
targets: ["*"],
|
|
1444
|
-
description,
|
|
1472
|
+
description: description || "",
|
|
1445
1473
|
globs: [],
|
|
1446
1474
|
cursorRuleType: "intelligently"
|
|
1447
1475
|
};
|
|
@@ -1564,8 +1592,9 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
|
|
|
1564
1592
|
try {
|
|
1565
1593
|
const content = await readFileContent(cursorMcpPath);
|
|
1566
1594
|
const mcp = JSON.parse(content);
|
|
1567
|
-
|
|
1568
|
-
|
|
1595
|
+
const parseResult = RulesyncMcpConfigSchema.safeParse(mcp);
|
|
1596
|
+
if (parseResult.success && Object.keys(parseResult.data.mcpServers).length > 0) {
|
|
1597
|
+
mcpServers = parseResult.data.mcpServers;
|
|
1569
1598
|
}
|
|
1570
1599
|
} catch (error) {
|
|
1571
1600
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
@@ -1696,8 +1725,9 @@ async function parseGeminiSettings(settingsPath) {
|
|
|
1696
1725
|
try {
|
|
1697
1726
|
const content = await readFileContent(settingsPath);
|
|
1698
1727
|
const settings = JSON.parse(content);
|
|
1699
|
-
|
|
1700
|
-
|
|
1728
|
+
const parseResult = RulesyncMcpConfigSchema.safeParse(settings);
|
|
1729
|
+
if (parseResult.success && Object.keys(parseResult.data.mcpServers).length > 0) {
|
|
1730
|
+
mcpServers = parseResult.data.mcpServers;
|
|
1701
1731
|
}
|
|
1702
1732
|
} catch (error) {
|
|
1703
1733
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
@@ -2004,14 +2034,13 @@ async function initCommand() {
|
|
|
2004
2034
|
console.log("2. Run 'rulesync generate' to create configuration files");
|
|
2005
2035
|
}
|
|
2006
2036
|
async function createSampleFiles(aiRulesDir) {
|
|
2007
|
-
const
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
content: `---
|
|
2037
|
+
const sampleFile = {
|
|
2038
|
+
filename: "overview.md",
|
|
2039
|
+
content: `---
|
|
2011
2040
|
root: true
|
|
2012
2041
|
targets: ["*"]
|
|
2013
2042
|
description: "Project overview and general development guidelines"
|
|
2014
|
-
globs: ["
|
|
2043
|
+
globs: ["**/*"]
|
|
2015
2044
|
---
|
|
2016
2045
|
|
|
2017
2046
|
# Project Overview
|
|
@@ -2039,96 +2068,13 @@ globs: ["**/*.ts", "**/*.js", "**/*.tsx", "**/*.jsx"]
|
|
|
2039
2068
|
- Implement proper error handling
|
|
2040
2069
|
- Follow single responsibility principle
|
|
2041
2070
|
`
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
globs: ["src/components/**/*.tsx", "src/pages/**/*.tsx", "**/*.css", "**/*.scss"]
|
|
2050
|
-
---
|
|
2051
|
-
|
|
2052
|
-
# Frontend Development Rules
|
|
2053
|
-
|
|
2054
|
-
## React Components
|
|
2055
|
-
|
|
2056
|
-
- Use functional components with hooks
|
|
2057
|
-
- Follow PascalCase naming for components
|
|
2058
|
-
- Use TypeScript interfaces for props
|
|
2059
|
-
- Implement proper error boundaries
|
|
2060
|
-
|
|
2061
|
-
## Styling
|
|
2062
|
-
|
|
2063
|
-
- Use CSS modules or styled-components
|
|
2064
|
-
- Follow BEM methodology for CSS classes
|
|
2065
|
-
- Prefer flexbox and grid for layouts
|
|
2066
|
-
- Use semantic HTML elements
|
|
2067
|
-
|
|
2068
|
-
## State Management
|
|
2069
|
-
|
|
2070
|
-
- Use React hooks for local state
|
|
2071
|
-
- Consider Redux or Zustand for global state
|
|
2072
|
-
- Avoid prop drilling with context API
|
|
2073
|
-
- Keep state as close to where it's used as possible
|
|
2074
|
-
|
|
2075
|
-
## Performance
|
|
2076
|
-
|
|
2077
|
-
- Use React.memo for expensive components
|
|
2078
|
-
- Implement lazy loading for routes
|
|
2079
|
-
- Optimize images and assets
|
|
2080
|
-
- Use proper key props in lists
|
|
2081
|
-
`
|
|
2082
|
-
},
|
|
2083
|
-
{
|
|
2084
|
-
filename: "backend.md",
|
|
2085
|
-
content: `---
|
|
2086
|
-
root: false
|
|
2087
|
-
targets: ["*"]
|
|
2088
|
-
description: "Backend development rules and API guidelines"
|
|
2089
|
-
globs: ["src/api/**/*.ts", "src/services/**/*.ts", "src/models/**/*.ts"]
|
|
2090
|
-
---
|
|
2091
|
-
|
|
2092
|
-
# Backend Development Rules
|
|
2093
|
-
|
|
2094
|
-
## API Design
|
|
2095
|
-
|
|
2096
|
-
- Follow RESTful conventions
|
|
2097
|
-
- Use consistent HTTP status codes
|
|
2098
|
-
- Implement proper error handling with meaningful messages
|
|
2099
|
-
- Use API versioning when necessary
|
|
2100
|
-
|
|
2101
|
-
## Database
|
|
2102
|
-
|
|
2103
|
-
- Use proper indexing for performance
|
|
2104
|
-
- Implement database migrations
|
|
2105
|
-
- Follow naming conventions for tables and columns
|
|
2106
|
-
- Use transactions for data consistency
|
|
2107
|
-
|
|
2108
|
-
## Security
|
|
2109
|
-
|
|
2110
|
-
- Validate all input data
|
|
2111
|
-
- Use proper authentication and authorization
|
|
2112
|
-
- Implement rate limiting
|
|
2113
|
-
- Sanitize database queries to prevent SQL injection
|
|
2114
|
-
|
|
2115
|
-
## Code Organization
|
|
2116
|
-
|
|
2117
|
-
- Use service layer pattern
|
|
2118
|
-
- Implement proper logging
|
|
2119
|
-
- Use environment variables for configuration
|
|
2120
|
-
- Write comprehensive tests for business logic
|
|
2121
|
-
`
|
|
2122
|
-
}
|
|
2123
|
-
];
|
|
2124
|
-
for (const file of sampleFiles) {
|
|
2125
|
-
const filepath = join20(aiRulesDir, file.filename);
|
|
2126
|
-
if (!await fileExists(filepath)) {
|
|
2127
|
-
await writeFileContent(filepath, file.content);
|
|
2128
|
-
console.log(`Created ${filepath}`);
|
|
2129
|
-
} else {
|
|
2130
|
-
console.log(`Skipped ${filepath} (already exists)`);
|
|
2131
|
-
}
|
|
2071
|
+
};
|
|
2072
|
+
const filepath = join20(aiRulesDir, sampleFile.filename);
|
|
2073
|
+
if (!await fileExists(filepath)) {
|
|
2074
|
+
await writeFileContent(filepath, sampleFile.content);
|
|
2075
|
+
console.log(`Created ${filepath}`);
|
|
2076
|
+
} else {
|
|
2077
|
+
console.log(`Skipped ${filepath} (already exists)`);
|
|
2132
2078
|
}
|
|
2133
2079
|
}
|
|
2134
2080
|
|
|
@@ -2157,9 +2103,11 @@ async function statusCommand() {
|
|
|
2157
2103
|
for (const rule of rules) {
|
|
2158
2104
|
const targets = rule.frontmatter.targets[0] === "*" ? config.defaultTargets : rule.frontmatter.targets;
|
|
2159
2105
|
for (const target of targets) {
|
|
2160
|
-
if (target
|
|
2161
|
-
|
|
2162
|
-
|
|
2106
|
+
if (target === "copilot") targetCounts.copilot++;
|
|
2107
|
+
else if (target === "cursor") targetCounts.cursor++;
|
|
2108
|
+
else if (target === "cline") targetCounts.cline++;
|
|
2109
|
+
else if (target === "claudecode") targetCounts.claudecode++;
|
|
2110
|
+
else if (target === "roo") targetCounts.roo++;
|
|
2163
2111
|
}
|
|
2164
2112
|
}
|
|
2165
2113
|
console.log("\n\u{1F3AF} Target tool coverage:");
|
|
@@ -2265,7 +2213,7 @@ async function watchCommand() {
|
|
|
2265
2213
|
|
|
2266
2214
|
// src/cli/index.ts
|
|
2267
2215
|
var program = new Command();
|
|
2268
|
-
program.name("rulesync").description("Unified AI rules management CLI tool").version("0.
|
|
2216
|
+
program.name("rulesync").description("Unified AI rules management CLI tool").version("0.45.0");
|
|
2269
2217
|
program.command("init").description("Initialize rulesync in current directory").action(initCommand);
|
|
2270
2218
|
program.command("add <filename>").description("Add a new rule file").action(addCommand);
|
|
2271
2219
|
program.command("gitignore").description("Add generated files to .gitignore").action(gitignoreCommand);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rulesync",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.45.0",
|
|
4
4
|
"description": "Unified AI rules management CLI tool that generates configuration files for various AI development tools",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -46,6 +46,7 @@
|
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
48
|
"@biomejs/biome": "2.0.0",
|
|
49
|
+
"@eslint/js": "9.30.1",
|
|
49
50
|
"@secretlint/secretlint-rule-preset-recommend": "10.1.0",
|
|
50
51
|
"@tsconfig/node24": "24.0.1",
|
|
51
52
|
"@types/js-yaml": "4.0.9",
|
|
@@ -54,6 +55,9 @@
|
|
|
54
55
|
"@typescript/native-preview": "7.0.0-dev.20250623.1",
|
|
55
56
|
"@vitest/coverage-v8": "3.2.4",
|
|
56
57
|
"cspell": "9.1.2",
|
|
58
|
+
"eslint": "^9.30.1",
|
|
59
|
+
"eslint-plugin-no-type-assertion": "1.3.0",
|
|
60
|
+
"eslint-plugin-oxlint": "1.5.0",
|
|
57
61
|
"lint-staged": "16.1.2",
|
|
58
62
|
"o3-search-mcp": "0.0.3",
|
|
59
63
|
"oxlint": "1.4.0",
|
|
@@ -63,6 +67,7 @@
|
|
|
63
67
|
"tsup": "8.5.0",
|
|
64
68
|
"tsx": "4.20.3",
|
|
65
69
|
"typescript": "5.8.3",
|
|
70
|
+
"typescript-eslint": "^8.35.1",
|
|
66
71
|
"vitest": "3.2.4"
|
|
67
72
|
},
|
|
68
73
|
"engines": {
|
|
@@ -75,13 +80,15 @@
|
|
|
75
80
|
"bcheck": "biome check src/",
|
|
76
81
|
"bcheck:fix": "biome check --write src/",
|
|
77
82
|
"build": "tsup src/cli/index.ts --format cjs,esm --dts --clean",
|
|
78
|
-
"check": "pnpm run bcheck && pnpm run
|
|
83
|
+
"check": "pnpm run bcheck && pnpm run oxlint && pnpm run eslint && pnpm run typecheck",
|
|
79
84
|
"cspell": "cspell \"**/*\"",
|
|
80
85
|
"dev": "tsx src/cli/index.ts",
|
|
81
|
-
"
|
|
86
|
+
"eslint": "eslint . --max-warnings 0 --cache",
|
|
87
|
+
"eslint:fix": "eslint . --fix --max-warnings 0 --cache",
|
|
88
|
+
"fix": "pnpm run bcheck:fix && pnpm run oxlint:fix && pnpm run eslint:fix",
|
|
82
89
|
"generate": "pnpm run dev generate",
|
|
83
|
-
"
|
|
84
|
-
"
|
|
90
|
+
"oxlint": "oxlint . --max-warnings 0",
|
|
91
|
+
"oxlint:fix": "oxlint . --fix --max-warnings 0",
|
|
85
92
|
"secretlint": "secretlint \"**/*\"",
|
|
86
93
|
"sort": "sort-package-json",
|
|
87
94
|
"test": "vitest run --silent",
|