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