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.
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  shouldIncludeServer
3
- } from "./chunk-THWXK5Z2.js";
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[serverName] = claudeServer;
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[serverName] = claudeServer;
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-THWXK5Z2.js";
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-THWXK5Z2.js";
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-THWXK5Z2.js";
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 || typeof server !== "object") {
43
+ if (!shouldIncludeServer(server, "cursor")) {
44
44
  continue;
45
45
  }
46
- const serverObj = server;
47
- if (!shouldIncludeServer(serverObj, "cursor")) {
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-THWXK5Z2.js";
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 || typeof server !== "object") {
56
+ if (!shouldIncludeServer(server, "roo")) {
57
57
  continue;
58
58
  }
59
- const serverObj = server;
60
- if (!shouldIncludeServer(serverObj, "roo")) {
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,6 +1,6 @@
1
1
  import {
2
2
  shouldIncludeServer
3
- } from "./chunk-THWXK5Z2.js";
3
+ } from "./chunk-I5XVU7C6.js";
4
4
 
5
5
  // src/generators/mcp/copilot.ts
6
6
  function generateCopilotMcp(config, target) {
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  generateClaudeMcp,
3
3
  generateClaudeMcpConfiguration
4
- } from "./chunk-B3627VQY.js";
5
- import "./chunk-THWXK5Z2.js";
4
+ } from "./chunk-22GWBUIP.js";
5
+ import "./chunk-I5XVU7C6.js";
6
6
  export {
7
7
  generateClaudeMcp,
8
8
  generateClaudeMcpConfiguration
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  generateClineMcp,
3
3
  generateClineMcpConfiguration
4
- } from "./chunk-XHRBWFGI.js";
5
- import "./chunk-THWXK5Z2.js";
4
+ } from "./chunk-BD37M3ZH.js";
5
+ import "./chunk-I5XVU7C6.js";
6
6
  export {
7
7
  generateClineMcp,
8
8
  generateClineMcpConfiguration
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  generateCopilotMcp,
3
3
  generateCopilotMcpConfiguration
4
- } from "./chunk-2FR4Z37J.js";
5
- import "./chunk-THWXK5Z2.js";
4
+ } from "./chunk-ZORSPGDD.js";
5
+ import "./chunk-I5XVU7C6.js";
6
6
  export {
7
7
  generateCopilotMcp,
8
8
  generateCopilotMcpConfiguration
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  generateCursorMcp,
3
3
  generateCursorMcpConfiguration
4
- } from "./chunk-ZMAL5LIX.js";
5
- import "./chunk-THWXK5Z2.js";
4
+ } from "./chunk-FAZT3ILF.js";
5
+ import "./chunk-I5XVU7C6.js";
6
6
  export {
7
7
  generateCursorMcp,
8
8
  generateCursorMcpConfiguration
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  generateGeminiCliMcp,
3
3
  generateGeminiCliMcpConfiguration
4
- } from "./chunk-TCK42GOL.js";
5
- import "./chunk-THWXK5Z2.js";
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 import_v42, ToolTargetSchema, ToolTargetsSchema, WildcardTargetSchema, RulesyncTargetsSchema;
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
- import_v42 = require("zod/v4");
35
- ToolTargetSchema = import_v42.z.enum([
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 = import_v42.z.array(ToolTargetSchema);
44
- WildcardTargetSchema = import_v42.z.tuple([import_v42.z.literal("*")]);
45
- RulesyncTargetsSchema = import_v42.z.union([ToolTargetsSchema, WildcardTargetSchema]);
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[serverName] = claudeServer;
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
- return targets;
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 import_v4 = require("zod/v4");
418
- var ClaudeSettingsSchema = import_v4.z.looseObject({
419
- permissions: import_v4.z.looseObject({
420
- deny: import_v4.z.array(import_v4.z.string()).default([])
421
- }).default({ deny: [] })
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
- `| @.claude/memories/${rule.filename}.md | ${rule.frontmatter.description} | ${globsText} |`
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
- validateFrontmatter(parsed.data, filepath);
1014
- const frontmatter = parsed.data;
1015
- const filename = (0, import_node_path10.basename)(filepath, ".md");
1016
- return {
1017
- frontmatter,
1018
- content: parsed.content,
1019
- filename,
1020
- filepath
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 "globs" field in ${filepath}: must be an array, got ${typeof obj.globs}`
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
- return { mcpServers: rawConfig.mcpServers };
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
- if (typeof settings === "object" && settings !== null && "mcpServers" in settings) {
1561
- const servers = settings.mcpServers;
1562
- if (servers && typeof servers === "object" && Object.keys(servers).length > 0) {
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
- let preprocessed = str.replace(/^(\s*globs:\s*)\*\s*$/gm, '$1"*"').replace(/^(\s*globs:\s*)([^\s"'[\n][^"'[\n]*?)(\s*)$/gm, '$1"$2"$3');
1729
- return (0, import_js_yaml.load)(preprocessed, { schema: import_js_yaml.DEFAULT_SCHEMA });
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
- return (0, import_js_yaml.load)(str, { schema: import_js_yaml.FAILSAFE_SCHEMA });
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 frontmatter = cursorFrontmatter;
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
- if (mcp.mcpServers && Object.keys(mcp.mcpServers).length > 0) {
1901
- mcpServers = mcp.mcpServers;
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
- if (settings.mcpServers && Object.keys(settings.mcpServers).length > 0) {
2033
- mcpServers = settings.mcpServers;
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 in targetCounts) {
2494
- targetCounts[target]++;
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.43.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-B3627VQY.js";
4
+ } from "./chunk-22GWBUIP.js";
5
5
  import {
6
6
  generateClineMcp
7
- } from "./chunk-XHRBWFGI.js";
7
+ } from "./chunk-BD37M3ZH.js";
8
8
  import {
9
9
  generateCopilotMcp
10
- } from "./chunk-2FR4Z37J.js";
10
+ } from "./chunk-ZORSPGDD.js";
11
11
  import {
12
12
  generateCursorMcp
13
- } from "./chunk-ZMAL5LIX.js";
13
+ } from "./chunk-FAZT3ILF.js";
14
14
  import {
15
15
  generateGeminiCliMcp
16
- } from "./chunk-TCK42GOL.js";
16
+ } from "./chunk-DCSO5MY7.js";
17
17
  import {
18
18
  generateRooMcp
19
- } from "./chunk-RL3TE3EZ.js";
20
- import "./chunk-THWXK5Z2.js";
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
- return targets;
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.looseObject({
95
- deny: z.array(z.string()).default([])
96
- }).default({ deny: [] })
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
- `| @.claude/memories/${rule.filename}.md | ${rule.frontmatter.description} | ${globsText} |`
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
- validateFrontmatter(parsed.data, filepath);
689
- const frontmatter = parsed.data;
690
- const filename = basename(filepath, ".md");
691
- return {
692
- frontmatter,
693
- content: parsed.content,
694
- filename,
695
- filepath
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 "globs" field in ${filepath}: must be an array, got ${typeof obj.globs}`
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
- return { mcpServers: rawConfig.mcpServers };
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
- if (typeof settings === "object" && settings !== null && "mcpServers" in settings) {
1228
- const servers = settings.mcpServers;
1229
- if (servers && typeof servers === "object" && Object.keys(servers).length > 0) {
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
- let preprocessed = str.replace(/^(\s*globs:\s*)\*\s*$/gm, '$1"*"').replace(/^(\s*globs:\s*)([^\s"'[\n][^"'[\n]*?)(\s*)$/gm, '$1"$2"$3');
1396
- return load(preprocessed, { schema: DEFAULT_SCHEMA });
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
- return load(str, { schema: FAILSAFE_SCHEMA });
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 frontmatter = cursorFrontmatter;
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
- if (mcp.mcpServers && Object.keys(mcp.mcpServers).length > 0) {
1568
- mcpServers = mcp.mcpServers;
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
- if (settings.mcpServers && Object.keys(settings.mcpServers).length > 0) {
1700
- mcpServers = settings.mcpServers;
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 in targetCounts) {
2161
- targetCounts[target]++;
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.43.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);
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  generateRooMcp,
3
3
  generateRooMcpConfiguration
4
- } from "./chunk-RL3TE3EZ.js";
5
- import "./chunk-THWXK5Z2.js";
4
+ } from "./chunk-PJUNIIF4.js";
5
+ import "./chunk-I5XVU7C6.js";
6
6
  export {
7
7
  generateRooMcp,
8
8
  generateRooMcpConfiguration
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rulesync",
3
- "version": "0.43.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 lint && pnpm run typecheck",
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
- "fix": "pnpm run bcheck:fix && pnpm run lint:fix",
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
- "lint": "oxlint . --max-warnings 0",
84
- "lint:fix": "oxlint . --fix --max-warnings 0",
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",