rulesync 0.43.0 → 0.44.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 +151 -115
- package/dist/index.js +145 -114
- package/dist/{roo-UOTKEOH7.js → roo-DRA2SU4L.js} +2 -2
- package/package.json +12 -5
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
shouldIncludeServer
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-I5XVU7C6.js";
|
|
4
4
|
|
|
5
5
|
// src/generators/mcp/claudecode.ts
|
|
6
6
|
function generateClaudeMcp(config) {
|
|
@@ -30,7 +30,9 @@ function generateClaudeMcp(config) {
|
|
|
30
30
|
if (server.env) {
|
|
31
31
|
claudeServer.env = server.env;
|
|
32
32
|
}
|
|
33
|
-
claudeSettings.mcpServers
|
|
33
|
+
if (claudeSettings.mcpServers) {
|
|
34
|
+
claudeSettings.mcpServers[serverName] = claudeServer;
|
|
35
|
+
}
|
|
34
36
|
}
|
|
35
37
|
return JSON.stringify(claudeSettings, null, 2);
|
|
36
38
|
}
|
|
@@ -44,15 +46,17 @@ function generateClaudeMcpConfiguration(mcpServers, baseDir = "") {
|
|
|
44
46
|
continue;
|
|
45
47
|
}
|
|
46
48
|
const { targets: _, transport, ...serverConfig } = server;
|
|
47
|
-
const claudeServer = serverConfig;
|
|
49
|
+
const claudeServer = { ...serverConfig };
|
|
48
50
|
if (serverConfig.httpUrl !== void 0) {
|
|
49
51
|
claudeServer.url = serverConfig.httpUrl;
|
|
50
52
|
delete claudeServer.httpUrl;
|
|
51
53
|
}
|
|
52
|
-
if (transport && transport !== "stdio") {
|
|
54
|
+
if (transport && transport !== "stdio" && (transport === "sse" || transport === "http")) {
|
|
53
55
|
claudeServer.transport = transport;
|
|
54
56
|
}
|
|
55
|
-
settings.mcpServers
|
|
57
|
+
if (settings.mcpServers) {
|
|
58
|
+
settings.mcpServers[serverName] = claudeServer;
|
|
59
|
+
}
|
|
56
60
|
}
|
|
57
61
|
return [
|
|
58
62
|
{
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
shouldIncludeServer
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-I5XVU7C6.js";
|
|
4
4
|
|
|
5
5
|
// src/generators/mcp/cline.ts
|
|
6
6
|
function generateClineMcp(config) {
|
|
@@ -45,7 +45,7 @@ function generateClineMcpConfiguration(mcpServers, baseDir = "") {
|
|
|
45
45
|
continue;
|
|
46
46
|
}
|
|
47
47
|
const { targets: _, ...serverConfig } = server;
|
|
48
|
-
config.mcpServers[serverName] = serverConfig;
|
|
48
|
+
config.mcpServers[serverName] = { ...serverConfig };
|
|
49
49
|
}
|
|
50
50
|
return [
|
|
51
51
|
{
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
shouldIncludeServer
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-I5XVU7C6.js";
|
|
4
4
|
|
|
5
5
|
// src/generators/mcp/geminicli.ts
|
|
6
6
|
function generateGeminiCliMcp(config) {
|
|
@@ -50,7 +50,7 @@ function generateGeminiCliMcpConfiguration(mcpServers, baseDir = "") {
|
|
|
50
50
|
continue;
|
|
51
51
|
}
|
|
52
52
|
const { targets: _, ...serverConfig } = server;
|
|
53
|
-
config.mcpServers[serverName] = serverConfig;
|
|
53
|
+
config.mcpServers[serverName] = { ...serverConfig };
|
|
54
54
|
}
|
|
55
55
|
return [
|
|
56
56
|
{
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
shouldIncludeServer
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-I5XVU7C6.js";
|
|
4
4
|
|
|
5
5
|
// src/generators/mcp/cursor.ts
|
|
6
6
|
function generateCursorMcp(config) {
|
|
@@ -40,15 +40,11 @@ function generateCursorMcpConfiguration(mcpServers, baseDir = "") {
|
|
|
40
40
|
mcpServers: {}
|
|
41
41
|
};
|
|
42
42
|
for (const [serverName, server] of Object.entries(mcpServers)) {
|
|
43
|
-
if (!server
|
|
43
|
+
if (!shouldIncludeServer(server, "cursor")) {
|
|
44
44
|
continue;
|
|
45
45
|
}
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
continue;
|
|
49
|
-
}
|
|
50
|
-
const { targets: _targets, ...serverConfig } = serverObj;
|
|
51
|
-
const cursorServer = serverConfig;
|
|
46
|
+
const { targets: _targets, ...serverConfig } = server;
|
|
47
|
+
const cursorServer = { ...serverConfig };
|
|
52
48
|
if (serverConfig.httpUrl !== void 0) {
|
|
53
49
|
cursorServer.url = serverConfig.httpUrl;
|
|
54
50
|
delete cursorServer.httpUrl;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/types/tool-targets.ts
|
|
2
|
-
import { z } from "zod/v4";
|
|
2
|
+
import { z } from "zod/v4-mini";
|
|
3
3
|
var ToolTargetSchema = z.enum([
|
|
4
4
|
"copilot",
|
|
5
5
|
"cursor",
|
|
@@ -31,5 +31,8 @@ function shouldIncludeServer(server, targetTool) {
|
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
export {
|
|
34
|
+
ToolTargetSchema,
|
|
35
|
+
ToolTargetsSchema,
|
|
36
|
+
RulesyncTargetsSchema,
|
|
34
37
|
shouldIncludeServer
|
|
35
38
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
shouldIncludeServer
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-I5XVU7C6.js";
|
|
4
4
|
|
|
5
5
|
// src/generators/mcp/roo.ts
|
|
6
6
|
function generateRooMcp(config) {
|
|
@@ -53,15 +53,11 @@ function generateRooMcpConfiguration(mcpServers, baseDir = "") {
|
|
|
53
53
|
mcpServers: {}
|
|
54
54
|
};
|
|
55
55
|
for (const [serverName, server] of Object.entries(mcpServers)) {
|
|
56
|
-
if (!server
|
|
56
|
+
if (!shouldIncludeServer(server, "roo")) {
|
|
57
57
|
continue;
|
|
58
58
|
}
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
continue;
|
|
62
|
-
}
|
|
63
|
-
const { targets: _targets, ...serverConfig } = serverObj;
|
|
64
|
-
const rooServer = serverConfig;
|
|
59
|
+
const { targets: _targets, ...serverConfig } = server;
|
|
60
|
+
const rooServer = { ...serverConfig };
|
|
65
61
|
if (serverConfig.httpUrl !== void 0 && serverConfig.url !== void 0) {
|
|
66
62
|
rooServer.url = serverConfig.httpUrl;
|
|
67
63
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
generateGeminiCliMcp,
|
|
3
3
|
generateGeminiCliMcpConfiguration
|
|
4
|
-
} from "./chunk-
|
|
5
|
-
import "./chunk-
|
|
4
|
+
} from "./chunk-DCSO5MY7.js";
|
|
5
|
+
import "./chunk-I5XVU7C6.js";
|
|
6
6
|
export {
|
|
7
7
|
generateGeminiCliMcp,
|
|
8
8
|
generateGeminiCliMcpConfiguration
|
package/dist/index.cjs
CHANGED
|
@@ -27,12 +27,12 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
27
27
|
));
|
|
28
28
|
|
|
29
29
|
// src/types/tool-targets.ts
|
|
30
|
-
var
|
|
30
|
+
var import_v4_mini, ToolTargetSchema, ToolTargetsSchema, WildcardTargetSchema, RulesyncTargetsSchema;
|
|
31
31
|
var init_tool_targets = __esm({
|
|
32
32
|
"src/types/tool-targets.ts"() {
|
|
33
33
|
"use strict";
|
|
34
|
-
|
|
35
|
-
ToolTargetSchema =
|
|
34
|
+
import_v4_mini = require("zod/v4-mini");
|
|
35
|
+
ToolTargetSchema = import_v4_mini.z.enum([
|
|
36
36
|
"copilot",
|
|
37
37
|
"cursor",
|
|
38
38
|
"cline",
|
|
@@ -40,9 +40,9 @@ var init_tool_targets = __esm({
|
|
|
40
40
|
"roo",
|
|
41
41
|
"geminicli"
|
|
42
42
|
]);
|
|
43
|
-
ToolTargetsSchema =
|
|
44
|
-
WildcardTargetSchema =
|
|
45
|
-
RulesyncTargetsSchema =
|
|
43
|
+
ToolTargetsSchema = import_v4_mini.z.array(ToolTargetSchema);
|
|
44
|
+
WildcardTargetSchema = import_v4_mini.z.tuple([import_v4_mini.z.literal("*")]);
|
|
45
|
+
RulesyncTargetsSchema = import_v4_mini.z.union([ToolTargetsSchema, WildcardTargetSchema]);
|
|
46
46
|
}
|
|
47
47
|
});
|
|
48
48
|
|
|
@@ -98,7 +98,9 @@ function generateClaudeMcp(config) {
|
|
|
98
98
|
if (server.env) {
|
|
99
99
|
claudeServer.env = server.env;
|
|
100
100
|
}
|
|
101
|
-
claudeSettings.mcpServers
|
|
101
|
+
if (claudeSettings.mcpServers) {
|
|
102
|
+
claudeSettings.mcpServers[serverName] = claudeServer;
|
|
103
|
+
}
|
|
102
104
|
}
|
|
103
105
|
return JSON.stringify(claudeSettings, null, 2);
|
|
104
106
|
}
|
|
@@ -352,6 +354,7 @@ var import_promises = require("fs/promises");
|
|
|
352
354
|
var path = __toESM(require("path"), 1);
|
|
353
355
|
|
|
354
356
|
// src/utils/config.ts
|
|
357
|
+
init_tool_targets();
|
|
355
358
|
function getDefaultConfig() {
|
|
356
359
|
return {
|
|
357
360
|
aiRulesDir: ".rulesync",
|
|
@@ -368,10 +371,11 @@ function getDefaultConfig() {
|
|
|
368
371
|
};
|
|
369
372
|
}
|
|
370
373
|
function resolveTargets(targets, config) {
|
|
371
|
-
if (targets[0] === "*") {
|
|
374
|
+
if (targets.length === 1 && targets[0] === "*") {
|
|
372
375
|
return config.defaultTargets;
|
|
373
376
|
}
|
|
374
|
-
|
|
377
|
+
const validatedTargets = ToolTargetsSchema.parse(targets);
|
|
378
|
+
return validatedTargets;
|
|
375
379
|
}
|
|
376
380
|
|
|
377
381
|
// src/cli/commands/add.ts
|
|
@@ -414,11 +418,14 @@ async function addCommand(filename) {
|
|
|
414
418
|
var import_node_path4 = require("path");
|
|
415
419
|
|
|
416
420
|
// src/types/claudecode.ts
|
|
417
|
-
var
|
|
418
|
-
var ClaudeSettingsSchema =
|
|
419
|
-
permissions:
|
|
420
|
-
|
|
421
|
-
|
|
421
|
+
var import_v4_mini2 = require("zod/v4-mini");
|
|
422
|
+
var ClaudeSettingsSchema = import_v4_mini2.z.looseObject({
|
|
423
|
+
permissions: import_v4_mini2.z._default(
|
|
424
|
+
import_v4_mini2.z.looseObject({
|
|
425
|
+
deny: import_v4_mini2.z._default(import_v4_mini2.z.array(import_v4_mini2.z.string()), [])
|
|
426
|
+
}),
|
|
427
|
+
{ deny: [] }
|
|
428
|
+
)
|
|
422
429
|
});
|
|
423
430
|
|
|
424
431
|
// src/utils/file.ts
|
|
@@ -581,12 +588,10 @@ function generateClaudeMarkdown(rootRules, detailRules) {
|
|
|
581
588
|
if (detailRules.length > 0) {
|
|
582
589
|
lines.push("Please also reference the following documents as needed:");
|
|
583
590
|
lines.push("");
|
|
584
|
-
lines.push("| Document | Description | File Patterns |");
|
|
585
|
-
lines.push("|----------|-------------|---------------|");
|
|
586
591
|
for (const rule of detailRules) {
|
|
587
|
-
const globsText = rule.frontmatter.globs.length > 0 ? rule.frontmatter.globs.join(", ") : "
|
|
592
|
+
const globsText = rule.frontmatter.globs.length > 0 ? rule.frontmatter.globs.join(", ") : "";
|
|
588
593
|
lines.push(
|
|
589
|
-
|
|
594
|
+
`@.claude/memories/${rule.filename}.md ${rule.frontmatter.description} ${globsText}`.trim()
|
|
590
595
|
);
|
|
591
596
|
}
|
|
592
597
|
lines.push("");
|
|
@@ -977,6 +982,78 @@ async function generateForTool(tool, rules, config, baseDir) {
|
|
|
977
982
|
// src/core/parser.ts
|
|
978
983
|
var import_node_path10 = require("path");
|
|
979
984
|
var import_gray_matter = __toESM(require("gray-matter"), 1);
|
|
985
|
+
|
|
986
|
+
// src/types/config.ts
|
|
987
|
+
var import_v4_mini3 = require("zod/v4-mini");
|
|
988
|
+
init_tool_targets();
|
|
989
|
+
var ConfigSchema = import_v4_mini3.z.object({
|
|
990
|
+
aiRulesDir: import_v4_mini3.z.string(),
|
|
991
|
+
outputPaths: import_v4_mini3.z.record(ToolTargetSchema, import_v4_mini3.z.string()),
|
|
992
|
+
watchEnabled: import_v4_mini3.z.boolean(),
|
|
993
|
+
defaultTargets: ToolTargetsSchema
|
|
994
|
+
});
|
|
995
|
+
|
|
996
|
+
// src/types/mcp.ts
|
|
997
|
+
var import_v4_mini4 = require("zod/v4-mini");
|
|
998
|
+
init_tool_targets();
|
|
999
|
+
var McpTransportTypeSchema = import_v4_mini4.z.enum(["stdio", "sse", "http"]);
|
|
1000
|
+
var McpServerBaseSchema = import_v4_mini4.z.object({
|
|
1001
|
+
command: import_v4_mini4.z.optional(import_v4_mini4.z.string()),
|
|
1002
|
+
args: import_v4_mini4.z.optional(import_v4_mini4.z.array(import_v4_mini4.z.string())),
|
|
1003
|
+
url: import_v4_mini4.z.optional(import_v4_mini4.z.string()),
|
|
1004
|
+
httpUrl: import_v4_mini4.z.optional(import_v4_mini4.z.string()),
|
|
1005
|
+
env: import_v4_mini4.z.optional(import_v4_mini4.z.record(import_v4_mini4.z.string(), import_v4_mini4.z.string())),
|
|
1006
|
+
disabled: import_v4_mini4.z.optional(import_v4_mini4.z.boolean()),
|
|
1007
|
+
networkTimeout: import_v4_mini4.z.optional(import_v4_mini4.z.number()),
|
|
1008
|
+
timeout: import_v4_mini4.z.optional(import_v4_mini4.z.number()),
|
|
1009
|
+
trust: import_v4_mini4.z.optional(import_v4_mini4.z.boolean()),
|
|
1010
|
+
cwd: import_v4_mini4.z.optional(import_v4_mini4.z.string()),
|
|
1011
|
+
transport: import_v4_mini4.z.optional(McpTransportTypeSchema),
|
|
1012
|
+
type: import_v4_mini4.z.optional(import_v4_mini4.z.enum(["sse", "streamable-http"])),
|
|
1013
|
+
alwaysAllow: import_v4_mini4.z.optional(import_v4_mini4.z.array(import_v4_mini4.z.string())),
|
|
1014
|
+
tools: import_v4_mini4.z.optional(import_v4_mini4.z.array(import_v4_mini4.z.string()))
|
|
1015
|
+
});
|
|
1016
|
+
var RulesyncMcpServerSchema = import_v4_mini4.z.extend(McpServerBaseSchema, {
|
|
1017
|
+
targets: import_v4_mini4.z.optional(RulesyncTargetsSchema)
|
|
1018
|
+
});
|
|
1019
|
+
var McpConfigSchema = import_v4_mini4.z.object({
|
|
1020
|
+
mcpServers: import_v4_mini4.z.record(import_v4_mini4.z.string(), McpServerBaseSchema)
|
|
1021
|
+
});
|
|
1022
|
+
var RulesyncMcpConfigSchema = import_v4_mini4.z.object({
|
|
1023
|
+
mcpServers: import_v4_mini4.z.record(import_v4_mini4.z.string(), RulesyncMcpServerSchema)
|
|
1024
|
+
});
|
|
1025
|
+
|
|
1026
|
+
// src/types/rules.ts
|
|
1027
|
+
var import_v4_mini5 = require("zod/v4-mini");
|
|
1028
|
+
init_tool_targets();
|
|
1029
|
+
var RuleFrontmatterSchema = import_v4_mini5.z.object({
|
|
1030
|
+
root: import_v4_mini5.z.boolean(),
|
|
1031
|
+
targets: RulesyncTargetsSchema,
|
|
1032
|
+
description: import_v4_mini5.z.string(),
|
|
1033
|
+
globs: import_v4_mini5.z.array(import_v4_mini5.z.string()),
|
|
1034
|
+
cursorRuleType: import_v4_mini5.z.optional(import_v4_mini5.z.enum(["always", "manual", "specificFiles", "intelligently"]))
|
|
1035
|
+
});
|
|
1036
|
+
var ParsedRuleSchema = import_v4_mini5.z.object({
|
|
1037
|
+
frontmatter: RuleFrontmatterSchema,
|
|
1038
|
+
content: import_v4_mini5.z.string(),
|
|
1039
|
+
filename: import_v4_mini5.z.string(),
|
|
1040
|
+
filepath: import_v4_mini5.z.string()
|
|
1041
|
+
});
|
|
1042
|
+
var GeneratedOutputSchema = import_v4_mini5.z.object({
|
|
1043
|
+
tool: ToolTargetSchema,
|
|
1044
|
+
filepath: import_v4_mini5.z.string(),
|
|
1045
|
+
content: import_v4_mini5.z.string()
|
|
1046
|
+
});
|
|
1047
|
+
var GenerateOptionsSchema = import_v4_mini5.z.object({
|
|
1048
|
+
targetTools: import_v4_mini5.z.optional(ToolTargetsSchema),
|
|
1049
|
+
outputDir: import_v4_mini5.z.optional(import_v4_mini5.z.string()),
|
|
1050
|
+
watch: import_v4_mini5.z.optional(import_v4_mini5.z.boolean())
|
|
1051
|
+
});
|
|
1052
|
+
|
|
1053
|
+
// src/types/index.ts
|
|
1054
|
+
init_tool_targets();
|
|
1055
|
+
|
|
1056
|
+
// src/core/parser.ts
|
|
980
1057
|
async function parseRulesFromDirectory(aiRulesDir) {
|
|
981
1058
|
const ignorePatterns = await loadIgnorePatterns();
|
|
982
1059
|
const ruleFiles = await findFiles(aiRulesDir, ".md", ignorePatterns.patterns);
|
|
@@ -1010,84 +1087,20 @@ ${errors.join("\n")}`);
|
|
|
1010
1087
|
async function parseRuleFile(filepath) {
|
|
1011
1088
|
const content = await readFileContent(filepath);
|
|
1012
1089
|
const parsed = (0, import_gray_matter.default)(content);
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
}
|
|
1023
|
-
function validateFrontmatter(data, filepath) {
|
|
1024
|
-
if (!data || typeof data !== "object") {
|
|
1025
|
-
if (!data) {
|
|
1026
|
-
throw new Error(
|
|
1027
|
-
`Missing frontmatter in ${filepath}: file must contain YAML frontmatter with required fields (root, targets, description, globs)`
|
|
1028
|
-
);
|
|
1029
|
-
}
|
|
1030
|
-
throw new Error(`Invalid frontmatter in ${filepath}: frontmatter must be a valid YAML object`);
|
|
1031
|
-
}
|
|
1032
|
-
const obj = data;
|
|
1033
|
-
if (Object.keys(obj).length === 0) {
|
|
1034
|
-
throw new Error(
|
|
1035
|
-
`Missing frontmatter in ${filepath}: file must contain YAML frontmatter with required fields (root, targets, description, globs)`
|
|
1036
|
-
);
|
|
1037
|
-
}
|
|
1038
|
-
if (obj.root === void 0) {
|
|
1039
|
-
throw new Error(`Missing required field "root" in ${filepath}: must be true or false`);
|
|
1040
|
-
}
|
|
1041
|
-
if (typeof obj.root !== "boolean") {
|
|
1042
|
-
throw new Error(
|
|
1043
|
-
`Invalid "root" field in ${filepath}: must be a boolean (true or false), got ${typeof obj.root}`
|
|
1044
|
-
);
|
|
1045
|
-
}
|
|
1046
|
-
if (obj.targets === void 0) {
|
|
1047
|
-
throw new Error(
|
|
1048
|
-
`Missing required field "targets" in ${filepath}: must be an array like ["*"] or ["copilot", "cursor"]`
|
|
1049
|
-
);
|
|
1050
|
-
}
|
|
1051
|
-
if (!Array.isArray(obj.targets)) {
|
|
1052
|
-
throw new Error(
|
|
1053
|
-
`Invalid "targets" field in ${filepath}: must be an array, got ${typeof obj.targets}`
|
|
1054
|
-
);
|
|
1055
|
-
}
|
|
1056
|
-
const validTargets = ["copilot", "cursor", "cline", "claudecode", "roo", "geminicli", "*"];
|
|
1057
|
-
for (const target of obj.targets) {
|
|
1058
|
-
if (typeof target !== "string" || !validTargets.includes(target)) {
|
|
1059
|
-
throw new Error(
|
|
1060
|
-
`Invalid target "${target}" in ${filepath}: must be one of ${validTargets.join(", ")}`
|
|
1061
|
-
);
|
|
1062
|
-
}
|
|
1063
|
-
}
|
|
1064
|
-
if (obj.description === void 0) {
|
|
1065
|
-
throw new Error(
|
|
1066
|
-
`Missing required field "description" in ${filepath}: must be a descriptive string`
|
|
1067
|
-
);
|
|
1068
|
-
}
|
|
1069
|
-
if (typeof obj.description !== "string") {
|
|
1070
|
-
throw new Error(
|
|
1071
|
-
`Invalid "description" field in ${filepath}: must be a string, got ${typeof obj.description}`
|
|
1072
|
-
);
|
|
1073
|
-
}
|
|
1074
|
-
if (obj.globs === void 0) {
|
|
1075
|
-
throw new Error(
|
|
1076
|
-
`Missing required field "globs" in ${filepath}: must be an array of file patterns like ["**/*.ts"]`
|
|
1077
|
-
);
|
|
1078
|
-
}
|
|
1079
|
-
if (!Array.isArray(obj.globs)) {
|
|
1090
|
+
try {
|
|
1091
|
+
const frontmatter = RuleFrontmatterSchema.parse(parsed.data);
|
|
1092
|
+
const filename = (0, import_node_path10.basename)(filepath, ".md");
|
|
1093
|
+
return {
|
|
1094
|
+
frontmatter,
|
|
1095
|
+
content: parsed.content,
|
|
1096
|
+
filename,
|
|
1097
|
+
filepath
|
|
1098
|
+
};
|
|
1099
|
+
} catch (error) {
|
|
1080
1100
|
throw new Error(
|
|
1081
|
-
`Invalid
|
|
1101
|
+
`Invalid frontmatter in ${filepath}: ${error instanceof Error ? error.message : String(error)}`
|
|
1082
1102
|
);
|
|
1083
1103
|
}
|
|
1084
|
-
for (const glob of obj.globs) {
|
|
1085
|
-
if (typeof glob !== "string") {
|
|
1086
|
-
throw new Error(
|
|
1087
|
-
`Invalid glob pattern in ${filepath}: all globs must be strings, got ${typeof glob}`
|
|
1088
|
-
);
|
|
1089
|
-
}
|
|
1090
|
-
}
|
|
1091
1104
|
}
|
|
1092
1105
|
|
|
1093
1106
|
// src/core/validator.ts
|
|
@@ -1166,13 +1179,11 @@ function parseMcpConfig(projectRoot) {
|
|
|
1166
1179
|
rawConfig.mcpServers = rawConfig.servers;
|
|
1167
1180
|
delete rawConfig.servers;
|
|
1168
1181
|
}
|
|
1169
|
-
if (!rawConfig.mcpServers || typeof rawConfig.mcpServers !== "object") {
|
|
1170
|
-
throw new Error("Invalid mcp.json: 'mcpServers' field must be an object");
|
|
1171
|
-
}
|
|
1172
1182
|
if (rawConfig.tools) {
|
|
1173
1183
|
delete rawConfig.tools;
|
|
1174
1184
|
}
|
|
1175
|
-
|
|
1185
|
+
const validatedConfig = RulesyncMcpConfigSchema.parse(rawConfig);
|
|
1186
|
+
return validatedConfig;
|
|
1176
1187
|
} catch (error) {
|
|
1177
1188
|
throw new Error(
|
|
1178
1189
|
`Failed to parse mcp.json: ${error instanceof Error ? error.message : String(error)}`
|
|
@@ -1545,6 +1556,9 @@ async function parseClaudeSettings(settingsPath) {
|
|
|
1545
1556
|
const settings = JSON.parse(content);
|
|
1546
1557
|
if (typeof settings === "object" && settings !== null && "permissions" in settings) {
|
|
1547
1558
|
const permissions = settings.permissions;
|
|
1559
|
+
if (typeof permissions !== "object" || permissions === null) {
|
|
1560
|
+
return { ignorePatterns: [], errors: [] };
|
|
1561
|
+
}
|
|
1548
1562
|
if (permissions && "deny" in permissions && Array.isArray(permissions.deny)) {
|
|
1549
1563
|
const readPatterns = permissions.deny.filter(
|
|
1550
1564
|
(rule) => typeof rule === "string" && rule.startsWith("Read(") && rule.endsWith(")")
|
|
@@ -1557,11 +1571,9 @@ async function parseClaudeSettings(settingsPath) {
|
|
|
1557
1571
|
}
|
|
1558
1572
|
}
|
|
1559
1573
|
}
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
mcpServers = servers;
|
|
1564
|
-
}
|
|
1574
|
+
const parseResult = RulesyncMcpConfigSchema.safeParse(settings);
|
|
1575
|
+
if (parseResult.success && Object.keys(parseResult.data.mcpServers).length > 0) {
|
|
1576
|
+
mcpServers = parseResult.data.mcpServers;
|
|
1565
1577
|
}
|
|
1566
1578
|
} catch (error) {
|
|
1567
1579
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
@@ -1720,16 +1732,25 @@ async function parseCopilotConfiguration(baseDir = process.cwd()) {
|
|
|
1720
1732
|
var import_node_path15 = require("path");
|
|
1721
1733
|
var import_gray_matter3 = __toESM(require("gray-matter"), 1);
|
|
1722
1734
|
var import_js_yaml = require("js-yaml");
|
|
1735
|
+
var import_v4_mini6 = require("zod/v4-mini");
|
|
1723
1736
|
var customMatterOptions = {
|
|
1724
1737
|
engines: {
|
|
1725
1738
|
yaml: {
|
|
1726
1739
|
parse: (str) => {
|
|
1727
1740
|
try {
|
|
1728
|
-
|
|
1729
|
-
|
|
1741
|
+
const preprocessed = str.replace(/^(\s*globs:\s*)\*\s*$/gm, '$1"*"').replace(/^(\s*globs:\s*)([^\s"'[\n][^"'[\n]*?)(\s*)$/gm, '$1"$2"$3');
|
|
1742
|
+
const result = (0, import_js_yaml.load)(preprocessed, { schema: import_js_yaml.DEFAULT_SCHEMA });
|
|
1743
|
+
if (typeof result === "object" && result !== null) {
|
|
1744
|
+
return result;
|
|
1745
|
+
}
|
|
1746
|
+
throw new Error("Failed to parse YAML: result is not an object");
|
|
1730
1747
|
} catch (error) {
|
|
1731
1748
|
try {
|
|
1732
|
-
|
|
1749
|
+
const result = (0, import_js_yaml.load)(str, { schema: import_js_yaml.FAILSAFE_SCHEMA });
|
|
1750
|
+
if (typeof result === "object" && result !== null) {
|
|
1751
|
+
return result;
|
|
1752
|
+
}
|
|
1753
|
+
throw new Error("Failed to parse YAML: result is not an object");
|
|
1733
1754
|
} catch {
|
|
1734
1755
|
throw error;
|
|
1735
1756
|
}
|
|
@@ -1739,7 +1760,18 @@ var customMatterOptions = {
|
|
|
1739
1760
|
}
|
|
1740
1761
|
};
|
|
1741
1762
|
function convertCursorMdcFrontmatter(cursorFrontmatter, _filename) {
|
|
1742
|
-
const
|
|
1763
|
+
const FrontmatterSchema = import_v4_mini6.z.record(import_v4_mini6.z.string(), import_v4_mini6.z.unknown());
|
|
1764
|
+
const parseResult = FrontmatterSchema.safeParse(cursorFrontmatter);
|
|
1765
|
+
if (!parseResult.success) {
|
|
1766
|
+
return {
|
|
1767
|
+
root: false,
|
|
1768
|
+
targets: ["*"],
|
|
1769
|
+
description: "",
|
|
1770
|
+
globs: [],
|
|
1771
|
+
cursorRuleType: "manual"
|
|
1772
|
+
};
|
|
1773
|
+
}
|
|
1774
|
+
const frontmatter = parseResult.data;
|
|
1743
1775
|
const description = normalizeValue(frontmatter?.description);
|
|
1744
1776
|
const globs = normalizeGlobsValue(frontmatter?.globs);
|
|
1745
1777
|
const alwaysApply = frontmatter?.alwaysApply === true || frontmatter?.alwaysApply === "true";
|
|
@@ -1774,7 +1806,7 @@ function convertCursorMdcFrontmatter(cursorFrontmatter, _filename) {
|
|
|
1774
1806
|
return {
|
|
1775
1807
|
root: false,
|
|
1776
1808
|
targets: ["*"],
|
|
1777
|
-
description,
|
|
1809
|
+
description: description || "",
|
|
1778
1810
|
globs: [],
|
|
1779
1811
|
cursorRuleType: "intelligently"
|
|
1780
1812
|
};
|
|
@@ -1897,8 +1929,9 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
|
|
|
1897
1929
|
try {
|
|
1898
1930
|
const content = await readFileContent(cursorMcpPath);
|
|
1899
1931
|
const mcp = JSON.parse(content);
|
|
1900
|
-
|
|
1901
|
-
|
|
1932
|
+
const parseResult = RulesyncMcpConfigSchema.safeParse(mcp);
|
|
1933
|
+
if (parseResult.success && Object.keys(parseResult.data.mcpServers).length > 0) {
|
|
1934
|
+
mcpServers = parseResult.data.mcpServers;
|
|
1902
1935
|
}
|
|
1903
1936
|
} catch (error) {
|
|
1904
1937
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
@@ -2029,8 +2062,9 @@ async function parseGeminiSettings(settingsPath) {
|
|
|
2029
2062
|
try {
|
|
2030
2063
|
const content = await readFileContent(settingsPath);
|
|
2031
2064
|
const settings = JSON.parse(content);
|
|
2032
|
-
|
|
2033
|
-
|
|
2065
|
+
const parseResult = RulesyncMcpConfigSchema.safeParse(settings);
|
|
2066
|
+
if (parseResult.success && Object.keys(parseResult.data.mcpServers).length > 0) {
|
|
2067
|
+
mcpServers = parseResult.data.mcpServers;
|
|
2034
2068
|
}
|
|
2035
2069
|
} catch (error) {
|
|
2036
2070
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
@@ -2490,9 +2524,11 @@ async function statusCommand() {
|
|
|
2490
2524
|
for (const rule of rules) {
|
|
2491
2525
|
const targets = rule.frontmatter.targets[0] === "*" ? config.defaultTargets : rule.frontmatter.targets;
|
|
2492
2526
|
for (const target of targets) {
|
|
2493
|
-
if (target
|
|
2494
|
-
|
|
2495
|
-
|
|
2527
|
+
if (target === "copilot") targetCounts.copilot++;
|
|
2528
|
+
else if (target === "cursor") targetCounts.cursor++;
|
|
2529
|
+
else if (target === "cline") targetCounts.cline++;
|
|
2530
|
+
else if (target === "claudecode") targetCounts.claudecode++;
|
|
2531
|
+
else if (target === "roo") targetCounts.roo++;
|
|
2496
2532
|
}
|
|
2497
2533
|
}
|
|
2498
2534
|
console.log("\n\u{1F3AF} Target tool coverage:");
|
|
@@ -2598,7 +2634,7 @@ async function watchCommand() {
|
|
|
2598
2634
|
|
|
2599
2635
|
// src/cli/index.ts
|
|
2600
2636
|
var program = new import_commander.Command();
|
|
2601
|
-
program.name("rulesync").description("Unified AI rules management CLI tool").version("0.
|
|
2637
|
+
program.name("rulesync").description("Unified AI rules management CLI tool").version("0.44.0");
|
|
2602
2638
|
program.command("init").description("Initialize rulesync in current directory").action(initCommand);
|
|
2603
2639
|
program.command("add <filename>").description("Add a new rule file").action(addCommand);
|
|
2604
2640
|
program.command("gitignore").description("Add generated files to .gitignore").action(gitignoreCommand);
|
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,10 @@ 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 globsText = rule.frontmatter.globs.length > 0 ? rule.frontmatter.globs.join(", ") : "
|
|
268
|
+
const globsText = rule.frontmatter.globs.length > 0 ? rule.frontmatter.globs.join(", ") : "";
|
|
263
269
|
lines.push(
|
|
264
|
-
|
|
270
|
+
`@.claude/memories/${rule.filename}.md ${rule.frontmatter.description} ${globsText}`.trim()
|
|
265
271
|
);
|
|
266
272
|
}
|
|
267
273
|
lines.push("");
|
|
@@ -652,6 +658,72 @@ async function generateForTool(tool, rules, config, baseDir) {
|
|
|
652
658
|
// src/core/parser.ts
|
|
653
659
|
import { basename } from "path";
|
|
654
660
|
import matter from "gray-matter";
|
|
661
|
+
|
|
662
|
+
// src/types/config.ts
|
|
663
|
+
import { z as z2 } from "zod/v4-mini";
|
|
664
|
+
var ConfigSchema = z2.object({
|
|
665
|
+
aiRulesDir: z2.string(),
|
|
666
|
+
outputPaths: z2.record(ToolTargetSchema, z2.string()),
|
|
667
|
+
watchEnabled: z2.boolean(),
|
|
668
|
+
defaultTargets: ToolTargetsSchema
|
|
669
|
+
});
|
|
670
|
+
|
|
671
|
+
// src/types/mcp.ts
|
|
672
|
+
import { z as z3 } from "zod/v4-mini";
|
|
673
|
+
var McpTransportTypeSchema = z3.enum(["stdio", "sse", "http"]);
|
|
674
|
+
var McpServerBaseSchema = z3.object({
|
|
675
|
+
command: z3.optional(z3.string()),
|
|
676
|
+
args: z3.optional(z3.array(z3.string())),
|
|
677
|
+
url: z3.optional(z3.string()),
|
|
678
|
+
httpUrl: z3.optional(z3.string()),
|
|
679
|
+
env: z3.optional(z3.record(z3.string(), z3.string())),
|
|
680
|
+
disabled: z3.optional(z3.boolean()),
|
|
681
|
+
networkTimeout: z3.optional(z3.number()),
|
|
682
|
+
timeout: z3.optional(z3.number()),
|
|
683
|
+
trust: z3.optional(z3.boolean()),
|
|
684
|
+
cwd: z3.optional(z3.string()),
|
|
685
|
+
transport: z3.optional(McpTransportTypeSchema),
|
|
686
|
+
type: z3.optional(z3.enum(["sse", "streamable-http"])),
|
|
687
|
+
alwaysAllow: z3.optional(z3.array(z3.string())),
|
|
688
|
+
tools: z3.optional(z3.array(z3.string()))
|
|
689
|
+
});
|
|
690
|
+
var RulesyncMcpServerSchema = z3.extend(McpServerBaseSchema, {
|
|
691
|
+
targets: z3.optional(RulesyncTargetsSchema)
|
|
692
|
+
});
|
|
693
|
+
var McpConfigSchema = z3.object({
|
|
694
|
+
mcpServers: z3.record(z3.string(), McpServerBaseSchema)
|
|
695
|
+
});
|
|
696
|
+
var RulesyncMcpConfigSchema = z3.object({
|
|
697
|
+
mcpServers: z3.record(z3.string(), RulesyncMcpServerSchema)
|
|
698
|
+
});
|
|
699
|
+
|
|
700
|
+
// src/types/rules.ts
|
|
701
|
+
import { z as z4 } from "zod/v4-mini";
|
|
702
|
+
var RuleFrontmatterSchema = z4.object({
|
|
703
|
+
root: z4.boolean(),
|
|
704
|
+
targets: RulesyncTargetsSchema,
|
|
705
|
+
description: z4.string(),
|
|
706
|
+
globs: z4.array(z4.string()),
|
|
707
|
+
cursorRuleType: z4.optional(z4.enum(["always", "manual", "specificFiles", "intelligently"]))
|
|
708
|
+
});
|
|
709
|
+
var ParsedRuleSchema = z4.object({
|
|
710
|
+
frontmatter: RuleFrontmatterSchema,
|
|
711
|
+
content: z4.string(),
|
|
712
|
+
filename: z4.string(),
|
|
713
|
+
filepath: z4.string()
|
|
714
|
+
});
|
|
715
|
+
var GeneratedOutputSchema = z4.object({
|
|
716
|
+
tool: ToolTargetSchema,
|
|
717
|
+
filepath: z4.string(),
|
|
718
|
+
content: z4.string()
|
|
719
|
+
});
|
|
720
|
+
var GenerateOptionsSchema = z4.object({
|
|
721
|
+
targetTools: z4.optional(ToolTargetsSchema),
|
|
722
|
+
outputDir: z4.optional(z4.string()),
|
|
723
|
+
watch: z4.optional(z4.boolean())
|
|
724
|
+
});
|
|
725
|
+
|
|
726
|
+
// src/core/parser.ts
|
|
655
727
|
async function parseRulesFromDirectory(aiRulesDir) {
|
|
656
728
|
const ignorePatterns = await loadIgnorePatterns();
|
|
657
729
|
const ruleFiles = await findFiles(aiRulesDir, ".md", ignorePatterns.patterns);
|
|
@@ -685,84 +757,20 @@ ${errors.join("\n")}`);
|
|
|
685
757
|
async function parseRuleFile(filepath) {
|
|
686
758
|
const content = await readFileContent(filepath);
|
|
687
759
|
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)) {
|
|
760
|
+
try {
|
|
761
|
+
const frontmatter = RuleFrontmatterSchema.parse(parsed.data);
|
|
762
|
+
const filename = basename(filepath, ".md");
|
|
763
|
+
return {
|
|
764
|
+
frontmatter,
|
|
765
|
+
content: parsed.content,
|
|
766
|
+
filename,
|
|
767
|
+
filepath
|
|
768
|
+
};
|
|
769
|
+
} catch (error) {
|
|
755
770
|
throw new Error(
|
|
756
|
-
`Invalid
|
|
771
|
+
`Invalid frontmatter in ${filepath}: ${error instanceof Error ? error.message : String(error)}`
|
|
757
772
|
);
|
|
758
773
|
}
|
|
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
774
|
}
|
|
767
775
|
|
|
768
776
|
// src/core/validator.ts
|
|
@@ -833,13 +841,11 @@ function parseMcpConfig(projectRoot) {
|
|
|
833
841
|
rawConfig.mcpServers = rawConfig.servers;
|
|
834
842
|
delete rawConfig.servers;
|
|
835
843
|
}
|
|
836
|
-
if (!rawConfig.mcpServers || typeof rawConfig.mcpServers !== "object") {
|
|
837
|
-
throw new Error("Invalid mcp.json: 'mcpServers' field must be an object");
|
|
838
|
-
}
|
|
839
844
|
if (rawConfig.tools) {
|
|
840
845
|
delete rawConfig.tools;
|
|
841
846
|
}
|
|
842
|
-
|
|
847
|
+
const validatedConfig = RulesyncMcpConfigSchema.parse(rawConfig);
|
|
848
|
+
return validatedConfig;
|
|
843
849
|
} catch (error) {
|
|
844
850
|
throw new Error(
|
|
845
851
|
`Failed to parse mcp.json: ${error instanceof Error ? error.message : String(error)}`
|
|
@@ -1212,6 +1218,9 @@ async function parseClaudeSettings(settingsPath) {
|
|
|
1212
1218
|
const settings = JSON.parse(content);
|
|
1213
1219
|
if (typeof settings === "object" && settings !== null && "permissions" in settings) {
|
|
1214
1220
|
const permissions = settings.permissions;
|
|
1221
|
+
if (typeof permissions !== "object" || permissions === null) {
|
|
1222
|
+
return { ignorePatterns: [], errors: [] };
|
|
1223
|
+
}
|
|
1215
1224
|
if (permissions && "deny" in permissions && Array.isArray(permissions.deny)) {
|
|
1216
1225
|
const readPatterns = permissions.deny.filter(
|
|
1217
1226
|
(rule) => typeof rule === "string" && rule.startsWith("Read(") && rule.endsWith(")")
|
|
@@ -1224,11 +1233,9 @@ async function parseClaudeSettings(settingsPath) {
|
|
|
1224
1233
|
}
|
|
1225
1234
|
}
|
|
1226
1235
|
}
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
mcpServers = servers;
|
|
1231
|
-
}
|
|
1236
|
+
const parseResult = RulesyncMcpConfigSchema.safeParse(settings);
|
|
1237
|
+
if (parseResult.success && Object.keys(parseResult.data.mcpServers).length > 0) {
|
|
1238
|
+
mcpServers = parseResult.data.mcpServers;
|
|
1232
1239
|
}
|
|
1233
1240
|
} catch (error) {
|
|
1234
1241
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
@@ -1387,16 +1394,25 @@ async function parseCopilotConfiguration(baseDir = process.cwd()) {
|
|
|
1387
1394
|
import { basename as basename4, join as join16 } from "path";
|
|
1388
1395
|
import matter3 from "gray-matter";
|
|
1389
1396
|
import { DEFAULT_SCHEMA, FAILSAFE_SCHEMA, load } from "js-yaml";
|
|
1397
|
+
import { z as z5 } from "zod/v4-mini";
|
|
1390
1398
|
var customMatterOptions = {
|
|
1391
1399
|
engines: {
|
|
1392
1400
|
yaml: {
|
|
1393
1401
|
parse: (str) => {
|
|
1394
1402
|
try {
|
|
1395
|
-
|
|
1396
|
-
|
|
1403
|
+
const preprocessed = str.replace(/^(\s*globs:\s*)\*\s*$/gm, '$1"*"').replace(/^(\s*globs:\s*)([^\s"'[\n][^"'[\n]*?)(\s*)$/gm, '$1"$2"$3');
|
|
1404
|
+
const result = load(preprocessed, { schema: DEFAULT_SCHEMA });
|
|
1405
|
+
if (typeof result === "object" && result !== null) {
|
|
1406
|
+
return result;
|
|
1407
|
+
}
|
|
1408
|
+
throw new Error("Failed to parse YAML: result is not an object");
|
|
1397
1409
|
} catch (error) {
|
|
1398
1410
|
try {
|
|
1399
|
-
|
|
1411
|
+
const result = load(str, { schema: FAILSAFE_SCHEMA });
|
|
1412
|
+
if (typeof result === "object" && result !== null) {
|
|
1413
|
+
return result;
|
|
1414
|
+
}
|
|
1415
|
+
throw new Error("Failed to parse YAML: result is not an object");
|
|
1400
1416
|
} catch {
|
|
1401
1417
|
throw error;
|
|
1402
1418
|
}
|
|
@@ -1406,7 +1422,18 @@ var customMatterOptions = {
|
|
|
1406
1422
|
}
|
|
1407
1423
|
};
|
|
1408
1424
|
function convertCursorMdcFrontmatter(cursorFrontmatter, _filename) {
|
|
1409
|
-
const
|
|
1425
|
+
const FrontmatterSchema = z5.record(z5.string(), z5.unknown());
|
|
1426
|
+
const parseResult = FrontmatterSchema.safeParse(cursorFrontmatter);
|
|
1427
|
+
if (!parseResult.success) {
|
|
1428
|
+
return {
|
|
1429
|
+
root: false,
|
|
1430
|
+
targets: ["*"],
|
|
1431
|
+
description: "",
|
|
1432
|
+
globs: [],
|
|
1433
|
+
cursorRuleType: "manual"
|
|
1434
|
+
};
|
|
1435
|
+
}
|
|
1436
|
+
const frontmatter = parseResult.data;
|
|
1410
1437
|
const description = normalizeValue(frontmatter?.description);
|
|
1411
1438
|
const globs = normalizeGlobsValue(frontmatter?.globs);
|
|
1412
1439
|
const alwaysApply = frontmatter?.alwaysApply === true || frontmatter?.alwaysApply === "true";
|
|
@@ -1441,7 +1468,7 @@ function convertCursorMdcFrontmatter(cursorFrontmatter, _filename) {
|
|
|
1441
1468
|
return {
|
|
1442
1469
|
root: false,
|
|
1443
1470
|
targets: ["*"],
|
|
1444
|
-
description,
|
|
1471
|
+
description: description || "",
|
|
1445
1472
|
globs: [],
|
|
1446
1473
|
cursorRuleType: "intelligently"
|
|
1447
1474
|
};
|
|
@@ -1564,8 +1591,9 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
|
|
|
1564
1591
|
try {
|
|
1565
1592
|
const content = await readFileContent(cursorMcpPath);
|
|
1566
1593
|
const mcp = JSON.parse(content);
|
|
1567
|
-
|
|
1568
|
-
|
|
1594
|
+
const parseResult = RulesyncMcpConfigSchema.safeParse(mcp);
|
|
1595
|
+
if (parseResult.success && Object.keys(parseResult.data.mcpServers).length > 0) {
|
|
1596
|
+
mcpServers = parseResult.data.mcpServers;
|
|
1569
1597
|
}
|
|
1570
1598
|
} catch (error) {
|
|
1571
1599
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
@@ -1696,8 +1724,9 @@ async function parseGeminiSettings(settingsPath) {
|
|
|
1696
1724
|
try {
|
|
1697
1725
|
const content = await readFileContent(settingsPath);
|
|
1698
1726
|
const settings = JSON.parse(content);
|
|
1699
|
-
|
|
1700
|
-
|
|
1727
|
+
const parseResult = RulesyncMcpConfigSchema.safeParse(settings);
|
|
1728
|
+
if (parseResult.success && Object.keys(parseResult.data.mcpServers).length > 0) {
|
|
1729
|
+
mcpServers = parseResult.data.mcpServers;
|
|
1701
1730
|
}
|
|
1702
1731
|
} catch (error) {
|
|
1703
1732
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
@@ -2157,9 +2186,11 @@ async function statusCommand() {
|
|
|
2157
2186
|
for (const rule of rules) {
|
|
2158
2187
|
const targets = rule.frontmatter.targets[0] === "*" ? config.defaultTargets : rule.frontmatter.targets;
|
|
2159
2188
|
for (const target of targets) {
|
|
2160
|
-
if (target
|
|
2161
|
-
|
|
2162
|
-
|
|
2189
|
+
if (target === "copilot") targetCounts.copilot++;
|
|
2190
|
+
else if (target === "cursor") targetCounts.cursor++;
|
|
2191
|
+
else if (target === "cline") targetCounts.cline++;
|
|
2192
|
+
else if (target === "claudecode") targetCounts.claudecode++;
|
|
2193
|
+
else if (target === "roo") targetCounts.roo++;
|
|
2163
2194
|
}
|
|
2164
2195
|
}
|
|
2165
2196
|
console.log("\n\u{1F3AF} Target tool coverage:");
|
|
@@ -2265,7 +2296,7 @@ async function watchCommand() {
|
|
|
2265
2296
|
|
|
2266
2297
|
// src/cli/index.ts
|
|
2267
2298
|
var program = new Command();
|
|
2268
|
-
program.name("rulesync").description("Unified AI rules management CLI tool").version("0.
|
|
2299
|
+
program.name("rulesync").description("Unified AI rules management CLI tool").version("0.44.0");
|
|
2269
2300
|
program.command("init").description("Initialize rulesync in current directory").action(initCommand);
|
|
2270
2301
|
program.command("add <filename>").description("Add a new rule file").action(addCommand);
|
|
2271
2302
|
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.44.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",
|