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/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,11 @@ 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 escapedDescription = rule.frontmatter.description.replace(/"/g, '\\"');
593
+ const globsText = rule.frontmatter.globs.join(",");
588
594
  lines.push(
589
- `| @.claude/memories/${rule.filename}.md | ${rule.frontmatter.description} | ${globsText} |`
595
+ `@.claude/memories/${rule.filename}.md description: "${escapedDescription}" globs: "${globsText}"`
590
596
  );
591
597
  }
592
598
  lines.push("");
@@ -977,6 +983,78 @@ async function generateForTool(tool, rules, config, baseDir) {
977
983
  // src/core/parser.ts
978
984
  var import_node_path10 = require("path");
979
985
  var import_gray_matter = __toESM(require("gray-matter"), 1);
986
+
987
+ // src/types/config.ts
988
+ var import_v4_mini3 = require("zod/v4-mini");
989
+ init_tool_targets();
990
+ var ConfigSchema = import_v4_mini3.z.object({
991
+ aiRulesDir: import_v4_mini3.z.string(),
992
+ outputPaths: import_v4_mini3.z.record(ToolTargetSchema, import_v4_mini3.z.string()),
993
+ watchEnabled: import_v4_mini3.z.boolean(),
994
+ defaultTargets: ToolTargetsSchema
995
+ });
996
+
997
+ // src/types/mcp.ts
998
+ var import_v4_mini4 = require("zod/v4-mini");
999
+ init_tool_targets();
1000
+ var McpTransportTypeSchema = import_v4_mini4.z.enum(["stdio", "sse", "http"]);
1001
+ var McpServerBaseSchema = import_v4_mini4.z.object({
1002
+ command: import_v4_mini4.z.optional(import_v4_mini4.z.string()),
1003
+ args: import_v4_mini4.z.optional(import_v4_mini4.z.array(import_v4_mini4.z.string())),
1004
+ url: import_v4_mini4.z.optional(import_v4_mini4.z.string()),
1005
+ httpUrl: import_v4_mini4.z.optional(import_v4_mini4.z.string()),
1006
+ env: import_v4_mini4.z.optional(import_v4_mini4.z.record(import_v4_mini4.z.string(), import_v4_mini4.z.string())),
1007
+ disabled: import_v4_mini4.z.optional(import_v4_mini4.z.boolean()),
1008
+ networkTimeout: import_v4_mini4.z.optional(import_v4_mini4.z.number()),
1009
+ timeout: import_v4_mini4.z.optional(import_v4_mini4.z.number()),
1010
+ trust: import_v4_mini4.z.optional(import_v4_mini4.z.boolean()),
1011
+ cwd: import_v4_mini4.z.optional(import_v4_mini4.z.string()),
1012
+ transport: import_v4_mini4.z.optional(McpTransportTypeSchema),
1013
+ type: import_v4_mini4.z.optional(import_v4_mini4.z.enum(["sse", "streamable-http"])),
1014
+ alwaysAllow: import_v4_mini4.z.optional(import_v4_mini4.z.array(import_v4_mini4.z.string())),
1015
+ tools: import_v4_mini4.z.optional(import_v4_mini4.z.array(import_v4_mini4.z.string()))
1016
+ });
1017
+ var RulesyncMcpServerSchema = import_v4_mini4.z.extend(McpServerBaseSchema, {
1018
+ targets: import_v4_mini4.z.optional(RulesyncTargetsSchema)
1019
+ });
1020
+ var McpConfigSchema = import_v4_mini4.z.object({
1021
+ mcpServers: import_v4_mini4.z.record(import_v4_mini4.z.string(), McpServerBaseSchema)
1022
+ });
1023
+ var RulesyncMcpConfigSchema = import_v4_mini4.z.object({
1024
+ mcpServers: import_v4_mini4.z.record(import_v4_mini4.z.string(), RulesyncMcpServerSchema)
1025
+ });
1026
+
1027
+ // src/types/rules.ts
1028
+ var import_v4_mini5 = require("zod/v4-mini");
1029
+ init_tool_targets();
1030
+ var RuleFrontmatterSchema = import_v4_mini5.z.object({
1031
+ root: import_v4_mini5.z.boolean(),
1032
+ targets: RulesyncTargetsSchema,
1033
+ description: import_v4_mini5.z.string(),
1034
+ globs: import_v4_mini5.z.array(import_v4_mini5.z.string()),
1035
+ cursorRuleType: import_v4_mini5.z.optional(import_v4_mini5.z.enum(["always", "manual", "specificFiles", "intelligently"]))
1036
+ });
1037
+ var ParsedRuleSchema = import_v4_mini5.z.object({
1038
+ frontmatter: RuleFrontmatterSchema,
1039
+ content: import_v4_mini5.z.string(),
1040
+ filename: import_v4_mini5.z.string(),
1041
+ filepath: import_v4_mini5.z.string()
1042
+ });
1043
+ var GeneratedOutputSchema = import_v4_mini5.z.object({
1044
+ tool: ToolTargetSchema,
1045
+ filepath: import_v4_mini5.z.string(),
1046
+ content: import_v4_mini5.z.string()
1047
+ });
1048
+ var GenerateOptionsSchema = import_v4_mini5.z.object({
1049
+ targetTools: import_v4_mini5.z.optional(ToolTargetsSchema),
1050
+ outputDir: import_v4_mini5.z.optional(import_v4_mini5.z.string()),
1051
+ watch: import_v4_mini5.z.optional(import_v4_mini5.z.boolean())
1052
+ });
1053
+
1054
+ // src/types/index.ts
1055
+ init_tool_targets();
1056
+
1057
+ // src/core/parser.ts
980
1058
  async function parseRulesFromDirectory(aiRulesDir) {
981
1059
  const ignorePatterns = await loadIgnorePatterns();
982
1060
  const ruleFiles = await findFiles(aiRulesDir, ".md", ignorePatterns.patterns);
@@ -1010,84 +1088,20 @@ ${errors.join("\n")}`);
1010
1088
  async function parseRuleFile(filepath) {
1011
1089
  const content = await readFileContent(filepath);
1012
1090
  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)) {
1091
+ try {
1092
+ const frontmatter = RuleFrontmatterSchema.parse(parsed.data);
1093
+ const filename = (0, import_node_path10.basename)(filepath, ".md");
1094
+ return {
1095
+ frontmatter,
1096
+ content: parsed.content,
1097
+ filename,
1098
+ filepath
1099
+ };
1100
+ } catch (error) {
1080
1101
  throw new Error(
1081
- `Invalid "globs" field in ${filepath}: must be an array, got ${typeof obj.globs}`
1102
+ `Invalid frontmatter in ${filepath}: ${error instanceof Error ? error.message : String(error)}`
1082
1103
  );
1083
1104
  }
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
1105
  }
1092
1106
 
1093
1107
  // src/core/validator.ts
@@ -1166,13 +1180,11 @@ function parseMcpConfig(projectRoot) {
1166
1180
  rawConfig.mcpServers = rawConfig.servers;
1167
1181
  delete rawConfig.servers;
1168
1182
  }
1169
- if (!rawConfig.mcpServers || typeof rawConfig.mcpServers !== "object") {
1170
- throw new Error("Invalid mcp.json: 'mcpServers' field must be an object");
1171
- }
1172
1183
  if (rawConfig.tools) {
1173
1184
  delete rawConfig.tools;
1174
1185
  }
1175
- return { mcpServers: rawConfig.mcpServers };
1186
+ const validatedConfig = RulesyncMcpConfigSchema.parse(rawConfig);
1187
+ return validatedConfig;
1176
1188
  } catch (error) {
1177
1189
  throw new Error(
1178
1190
  `Failed to parse mcp.json: ${error instanceof Error ? error.message : String(error)}`
@@ -1545,6 +1557,9 @@ async function parseClaudeSettings(settingsPath) {
1545
1557
  const settings = JSON.parse(content);
1546
1558
  if (typeof settings === "object" && settings !== null && "permissions" in settings) {
1547
1559
  const permissions = settings.permissions;
1560
+ if (typeof permissions !== "object" || permissions === null) {
1561
+ return { ignorePatterns: [], errors: [] };
1562
+ }
1548
1563
  if (permissions && "deny" in permissions && Array.isArray(permissions.deny)) {
1549
1564
  const readPatterns = permissions.deny.filter(
1550
1565
  (rule) => typeof rule === "string" && rule.startsWith("Read(") && rule.endsWith(")")
@@ -1557,11 +1572,9 @@ async function parseClaudeSettings(settingsPath) {
1557
1572
  }
1558
1573
  }
1559
1574
  }
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
- }
1575
+ const parseResult = RulesyncMcpConfigSchema.safeParse(settings);
1576
+ if (parseResult.success && Object.keys(parseResult.data.mcpServers).length > 0) {
1577
+ mcpServers = parseResult.data.mcpServers;
1565
1578
  }
1566
1579
  } catch (error) {
1567
1580
  const errorMessage = error instanceof Error ? error.message : String(error);
@@ -1720,16 +1733,25 @@ async function parseCopilotConfiguration(baseDir = process.cwd()) {
1720
1733
  var import_node_path15 = require("path");
1721
1734
  var import_gray_matter3 = __toESM(require("gray-matter"), 1);
1722
1735
  var import_js_yaml = require("js-yaml");
1736
+ var import_v4_mini6 = require("zod/v4-mini");
1723
1737
  var customMatterOptions = {
1724
1738
  engines: {
1725
1739
  yaml: {
1726
1740
  parse: (str) => {
1727
1741
  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 });
1742
+ const preprocessed = str.replace(/^(\s*globs:\s*)\*\s*$/gm, '$1"*"').replace(/^(\s*globs:\s*)([^\s"'[\n][^"'[\n]*?)(\s*)$/gm, '$1"$2"$3');
1743
+ const result = (0, import_js_yaml.load)(preprocessed, { schema: import_js_yaml.DEFAULT_SCHEMA });
1744
+ if (typeof result === "object" && result !== null) {
1745
+ return result;
1746
+ }
1747
+ throw new Error("Failed to parse YAML: result is not an object");
1730
1748
  } catch (error) {
1731
1749
  try {
1732
- return (0, import_js_yaml.load)(str, { schema: import_js_yaml.FAILSAFE_SCHEMA });
1750
+ const result = (0, import_js_yaml.load)(str, { schema: import_js_yaml.FAILSAFE_SCHEMA });
1751
+ if (typeof result === "object" && result !== null) {
1752
+ return result;
1753
+ }
1754
+ throw new Error("Failed to parse YAML: result is not an object");
1733
1755
  } catch {
1734
1756
  throw error;
1735
1757
  }
@@ -1739,7 +1761,18 @@ var customMatterOptions = {
1739
1761
  }
1740
1762
  };
1741
1763
  function convertCursorMdcFrontmatter(cursorFrontmatter, _filename) {
1742
- const frontmatter = cursorFrontmatter;
1764
+ const FrontmatterSchema = import_v4_mini6.z.record(import_v4_mini6.z.string(), import_v4_mini6.z.unknown());
1765
+ const parseResult = FrontmatterSchema.safeParse(cursorFrontmatter);
1766
+ if (!parseResult.success) {
1767
+ return {
1768
+ root: false,
1769
+ targets: ["*"],
1770
+ description: "",
1771
+ globs: [],
1772
+ cursorRuleType: "manual"
1773
+ };
1774
+ }
1775
+ const frontmatter = parseResult.data;
1743
1776
  const description = normalizeValue(frontmatter?.description);
1744
1777
  const globs = normalizeGlobsValue(frontmatter?.globs);
1745
1778
  const alwaysApply = frontmatter?.alwaysApply === true || frontmatter?.alwaysApply === "true";
@@ -1774,7 +1807,7 @@ function convertCursorMdcFrontmatter(cursorFrontmatter, _filename) {
1774
1807
  return {
1775
1808
  root: false,
1776
1809
  targets: ["*"],
1777
- description,
1810
+ description: description || "",
1778
1811
  globs: [],
1779
1812
  cursorRuleType: "intelligently"
1780
1813
  };
@@ -1897,8 +1930,9 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
1897
1930
  try {
1898
1931
  const content = await readFileContent(cursorMcpPath);
1899
1932
  const mcp = JSON.parse(content);
1900
- if (mcp.mcpServers && Object.keys(mcp.mcpServers).length > 0) {
1901
- mcpServers = mcp.mcpServers;
1933
+ const parseResult = RulesyncMcpConfigSchema.safeParse(mcp);
1934
+ if (parseResult.success && Object.keys(parseResult.data.mcpServers).length > 0) {
1935
+ mcpServers = parseResult.data.mcpServers;
1902
1936
  }
1903
1937
  } catch (error) {
1904
1938
  const errorMessage = error instanceof Error ? error.message : String(error);
@@ -2029,8 +2063,9 @@ async function parseGeminiSettings(settingsPath) {
2029
2063
  try {
2030
2064
  const content = await readFileContent(settingsPath);
2031
2065
  const settings = JSON.parse(content);
2032
- if (settings.mcpServers && Object.keys(settings.mcpServers).length > 0) {
2033
- mcpServers = settings.mcpServers;
2066
+ const parseResult = RulesyncMcpConfigSchema.safeParse(settings);
2067
+ if (parseResult.success && Object.keys(parseResult.data.mcpServers).length > 0) {
2068
+ mcpServers = parseResult.data.mcpServers;
2034
2069
  }
2035
2070
  } catch (error) {
2036
2071
  const errorMessage = error instanceof Error ? error.message : String(error);
@@ -2337,14 +2372,13 @@ async function initCommand() {
2337
2372
  console.log("2. Run 'rulesync generate' to create configuration files");
2338
2373
  }
2339
2374
  async function createSampleFiles(aiRulesDir) {
2340
- const sampleFiles = [
2341
- {
2342
- filename: "overview.md",
2343
- content: `---
2375
+ const sampleFile = {
2376
+ filename: "overview.md",
2377
+ content: `---
2344
2378
  root: true
2345
2379
  targets: ["*"]
2346
2380
  description: "Project overview and general development guidelines"
2347
- globs: ["**/*.ts", "**/*.js", "**/*.tsx", "**/*.jsx"]
2381
+ globs: ["**/*"]
2348
2382
  ---
2349
2383
 
2350
2384
  # Project Overview
@@ -2372,96 +2406,13 @@ globs: ["**/*.ts", "**/*.js", "**/*.tsx", "**/*.jsx"]
2372
2406
  - Implement proper error handling
2373
2407
  - Follow single responsibility principle
2374
2408
  `
2375
- },
2376
- {
2377
- filename: "frontend.md",
2378
- content: `---
2379
- root: false
2380
- targets: ["*"]
2381
- description: "Frontend development rules and best practices"
2382
- globs: ["src/components/**/*.tsx", "src/pages/**/*.tsx", "**/*.css", "**/*.scss"]
2383
- ---
2384
-
2385
- # Frontend Development Rules
2386
-
2387
- ## React Components
2388
-
2389
- - Use functional components with hooks
2390
- - Follow PascalCase naming for components
2391
- - Use TypeScript interfaces for props
2392
- - Implement proper error boundaries
2393
-
2394
- ## Styling
2395
-
2396
- - Use CSS modules or styled-components
2397
- - Follow BEM methodology for CSS classes
2398
- - Prefer flexbox and grid for layouts
2399
- - Use semantic HTML elements
2400
-
2401
- ## State Management
2402
-
2403
- - Use React hooks for local state
2404
- - Consider Redux or Zustand for global state
2405
- - Avoid prop drilling with context API
2406
- - Keep state as close to where it's used as possible
2407
-
2408
- ## Performance
2409
-
2410
- - Use React.memo for expensive components
2411
- - Implement lazy loading for routes
2412
- - Optimize images and assets
2413
- - Use proper key props in lists
2414
- `
2415
- },
2416
- {
2417
- filename: "backend.md",
2418
- content: `---
2419
- root: false
2420
- targets: ["*"]
2421
- description: "Backend development rules and API guidelines"
2422
- globs: ["src/api/**/*.ts", "src/services/**/*.ts", "src/models/**/*.ts"]
2423
- ---
2424
-
2425
- # Backend Development Rules
2426
-
2427
- ## API Design
2428
-
2429
- - Follow RESTful conventions
2430
- - Use consistent HTTP status codes
2431
- - Implement proper error handling with meaningful messages
2432
- - Use API versioning when necessary
2433
-
2434
- ## Database
2435
-
2436
- - Use proper indexing for performance
2437
- - Implement database migrations
2438
- - Follow naming conventions for tables and columns
2439
- - Use transactions for data consistency
2440
-
2441
- ## Security
2442
-
2443
- - Validate all input data
2444
- - Use proper authentication and authorization
2445
- - Implement rate limiting
2446
- - Sanitize database queries to prevent SQL injection
2447
-
2448
- ## Code Organization
2449
-
2450
- - Use service layer pattern
2451
- - Implement proper logging
2452
- - Use environment variables for configuration
2453
- - Write comprehensive tests for business logic
2454
- `
2455
- }
2456
- ];
2457
- for (const file of sampleFiles) {
2458
- const filepath = (0, import_node_path19.join)(aiRulesDir, file.filename);
2459
- if (!await fileExists(filepath)) {
2460
- await writeFileContent(filepath, file.content);
2461
- console.log(`Created ${filepath}`);
2462
- } else {
2463
- console.log(`Skipped ${filepath} (already exists)`);
2464
- }
2409
+ };
2410
+ const filepath = (0, import_node_path19.join)(aiRulesDir, sampleFile.filename);
2411
+ if (!await fileExists(filepath)) {
2412
+ await writeFileContent(filepath, sampleFile.content);
2413
+ console.log(`Created ${filepath}`);
2414
+ } else {
2415
+ console.log(`Skipped ${filepath} (already exists)`);
2465
2416
  }
2466
2417
  }
2467
2418
 
@@ -2490,9 +2441,11 @@ async function statusCommand() {
2490
2441
  for (const rule of rules) {
2491
2442
  const targets = rule.frontmatter.targets[0] === "*" ? config.defaultTargets : rule.frontmatter.targets;
2492
2443
  for (const target of targets) {
2493
- if (target in targetCounts) {
2494
- targetCounts[target]++;
2495
- }
2444
+ if (target === "copilot") targetCounts.copilot++;
2445
+ else if (target === "cursor") targetCounts.cursor++;
2446
+ else if (target === "cline") targetCounts.cline++;
2447
+ else if (target === "claudecode") targetCounts.claudecode++;
2448
+ else if (target === "roo") targetCounts.roo++;
2496
2449
  }
2497
2450
  }
2498
2451
  console.log("\n\u{1F3AF} Target tool coverage:");
@@ -2598,7 +2551,7 @@ async function watchCommand() {
2598
2551
 
2599
2552
  // src/cli/index.ts
2600
2553
  var program = new import_commander.Command();
2601
- program.name("rulesync").description("Unified AI rules management CLI tool").version("0.43.0");
2554
+ program.name("rulesync").description("Unified AI rules management CLI tool").version("0.45.0");
2602
2555
  program.command("init").description("Initialize rulesync in current directory").action(initCommand);
2603
2556
  program.command("add <filename>").description("Add a new rule file").action(addCommand);
2604
2557
  program.command("gitignore").description("Add generated files to .gitignore").action(gitignoreCommand);