@aiready/cli 0.14.26 → 0.15.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/cli.mjs CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  __require,
4
4
  analyzeUnified,
5
5
  scoreUnified
6
- } from "./chunk-SK6WW6HW.mjs";
6
+ } from "./chunk-HX6H3VOE.mjs";
7
7
 
8
8
  // src/cli.ts
9
9
  import { Command } from "commander";
@@ -549,7 +549,8 @@ var SCAN_DEFAULTS = {
549
549
  "testability-index",
550
550
  "doc-drift",
551
551
  "dependency-health",
552
- "change-amplification"
552
+ "change-amplification",
553
+ "contract-enforcement"
553
554
  ],
554
555
  include: void 0,
555
556
  exclude: void 0,
@@ -784,6 +785,28 @@ async function scanAction(directory, options) {
784
785
  finalOptions,
785
786
  results
786
787
  );
788
+ const isCI = options.ci ?? process.env.CI === "true";
789
+ if (!isCI) {
790
+ console.log(
791
+ chalk6.dim(
792
+ "\n\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"
793
+ )
794
+ );
795
+ console.log(chalk6.bold("\u{1F4C8} Want to see the full interactive report?"));
796
+ console.log(
797
+ chalk6.cyan(
798
+ ` Upload this report to: ${chalk6.bold("https://platform.getaiready.dev")}`
799
+ )
800
+ );
801
+ console.log(
802
+ chalk6.dim(" Or run: ") + chalk6.white(`aiready upload ${outputPath}`)
803
+ );
804
+ console.log(
805
+ chalk6.dim(
806
+ "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"
807
+ )
808
+ );
809
+ }
787
810
  } catch (error) {
788
811
  handleCLIError3(error, "Analysis");
789
812
  }
@@ -1779,8 +1802,79 @@ async function testabilityAction(directory, options) {
1779
1802
  // src/commands/change-amplification.ts
1780
1803
  import { changeAmplificationAction } from "@aiready/change-amplification/dist/cli.js";
1781
1804
 
1782
- // src/commands/bug.ts
1805
+ // src/commands/contract-enforcement.ts
1783
1806
  import chalk16 from "chalk";
1807
+ async function contractEnforcementAction(directory, options) {
1808
+ return await executeToolAction(directory, options, {
1809
+ toolName: "contract-enforcement",
1810
+ label: "Contract enforcement analysis",
1811
+ emoji: "\u{1F6E1}\uFE0F",
1812
+ defaults: {
1813
+ minChainDepth: 3,
1814
+ include: void 0,
1815
+ exclude: void 0,
1816
+ output: { format: "console", file: void 0 }
1817
+ },
1818
+ getCliOptions: (opts) => ({
1819
+ minChainDepth: opts.minChainDepth ? parseInt(opts.minChainDepth, 10) : void 0
1820
+ }),
1821
+ importTool: async () => {
1822
+ const tool = await import("@aiready/contract-enforcement");
1823
+ return {
1824
+ analyze: tool.analyzeContractEnforcement,
1825
+ generateSummary: (report) => report.summary,
1826
+ calculateScore: tool.calculateContractEnforcementScore
1827
+ };
1828
+ },
1829
+ renderConsole: ({ results, summary, score }) => {
1830
+ renderToolHeader(
1831
+ "Contract Enforcement",
1832
+ "\u{1F6E1}\uFE0F",
1833
+ score?.score || 0,
1834
+ summary.rating
1835
+ );
1836
+ const rawData = results.rawData || results;
1837
+ console.log(
1838
+ chalk16.dim(
1839
+ ` Patterns: ${summary.totalDefensivePatterns} (${summary.defensiveDensity}/kLOC) | ${summary.sourceFiles} files scanned`
1840
+ )
1841
+ );
1842
+ const dims = summary.dimensions;
1843
+ if (dims) {
1844
+ const entries = [
1845
+ ["Type Escape Hatches", dims.typeEscapeHatchScore],
1846
+ ["Fallback Cascades", dims.fallbackCascadeScore],
1847
+ ["Error Transparency", dims.errorTransparencyScore],
1848
+ ["Boundary Validation", dims.boundaryValidationScore]
1849
+ ];
1850
+ for (const [name, val] of entries) {
1851
+ const color = val >= 80 ? chalk16.green : val >= 60 ? chalk16.yellow : chalk16.red;
1852
+ console.log(chalk16.dim(` ${name}: ${color(val + "/100")}`));
1853
+ }
1854
+ }
1855
+ if (summary.totalDefensivePatterns > 0 && rawData["as-any"] !== void 0) {
1856
+ const breakdown = [
1857
+ rawData["as-any"] && `as-any: ${rawData["as-any"]}`,
1858
+ rawData["as-unknown"] && `as-unknown: ${rawData["as-unknown"]}`,
1859
+ rawData["deep-optional-chain"] && `deep-?.: ${rawData["deep-optional-chain"]}`,
1860
+ rawData["nullish-literal-default"] && `?? literal: ${rawData["nullish-literal-default"]}`,
1861
+ rawData["swallowed-error"] && `swallowed-error: ${rawData["swallowed-error"]}`,
1862
+ rawData["env-fallback"] && `env-fallback: ${rawData["env-fallback"]}`,
1863
+ rawData["unnecessary-guard"] && `guard-clause: ${rawData["unnecessary-guard"]}`,
1864
+ rawData["any-parameter"] && `any-param: ${rawData["any-parameter"]}`,
1865
+ rawData["any-return"] && `any-return: ${rawData["any-return"]}`
1866
+ ].filter(Boolean).join(" | ");
1867
+ console.log(chalk16.dim(` ${breakdown}`));
1868
+ }
1869
+ if (score) {
1870
+ renderToolScoreFooter(score);
1871
+ }
1872
+ }
1873
+ });
1874
+ }
1875
+
1876
+ // src/commands/bug.ts
1877
+ import chalk17 from "chalk";
1784
1878
  import { execSync } from "child_process";
1785
1879
  async function bugAction(message, options) {
1786
1880
  const repoUrl = "https://github.com/caopengau/aiready-cli";
@@ -1798,35 +1892,35 @@ Generated via AIReady CLI 'bug' command.
1798
1892
  Type: ${type}
1799
1893
  `.trim();
1800
1894
  if (options.submit) {
1801
- console.log(chalk16.blue("\u{1F680} Submitting issue via GitHub CLI...\n"));
1895
+ console.log(chalk17.blue("\u{1F680} Submitting issue via GitHub CLI...\n"));
1802
1896
  try {
1803
1897
  execSync("gh auth status", { stdio: "ignore" });
1804
1898
  const command = `gh issue create --repo ${repoSlug} --title ${JSON.stringify(title)} --body ${JSON.stringify(body)} --label ${label}`;
1805
1899
  const output = execSync(command, { encoding: "utf8" }).trim();
1806
- console.log(chalk16.green("\u2705 Issue Created Successfully!"));
1807
- console.log(chalk16.cyan(output));
1900
+ console.log(chalk17.green("\u2705 Issue Created Successfully!"));
1901
+ console.log(chalk17.cyan(output));
1808
1902
  return;
1809
1903
  } catch {
1810
- console.error(chalk16.red("\n\u274C Failed to submit via gh CLI."));
1904
+ console.error(chalk17.red("\n\u274C Failed to submit via gh CLI."));
1811
1905
  console.log(
1812
- chalk16.yellow(
1906
+ chalk17.yellow(
1813
1907
  ' Make sure gh is installed and run "gh auth login".\n'
1814
1908
  )
1815
1909
  );
1816
- console.log(chalk16.dim(" Falling back to URL generation..."));
1910
+ console.log(chalk17.dim(" Falling back to URL generation..."));
1817
1911
  }
1818
1912
  }
1819
1913
  const template = type === "bug" ? "bug_report.md" : type === "feature" ? "feature_request.md" : "new_metric_idea.md";
1820
1914
  const fullUrl = `${repoUrl}/issues/new?title=${encodeURIComponent(title)}&body=${encodeURIComponent(body)}&labels=${label}&template=${template}`;
1821
- console.log(chalk16.green("\u{1F680} Issue Draft Prepared!\n"));
1822
- console.log(chalk16.bold("Title: ") + title);
1823
- console.log(chalk16.bold("Type: ") + type);
1824
- console.log(chalk16.bold("\nClick the link below to submit this issue:"));
1825
- console.log(chalk16.cyan(fullUrl));
1826
- console.log(chalk16.bold("\n\u{1F916} [FOR AI AGENTS]:"));
1827
- console.log(chalk16.dim(" You have successfully prepared a report."));
1915
+ console.log(chalk17.green("\u{1F680} Issue Draft Prepared!\n"));
1916
+ console.log(chalk17.bold("Title: ") + title);
1917
+ console.log(chalk17.bold("Type: ") + type);
1918
+ console.log(chalk17.bold("\nClick the link below to submit this issue:"));
1919
+ console.log(chalk17.cyan(fullUrl));
1920
+ console.log(chalk17.bold("\n\u{1F916} [FOR AI AGENTS]:"));
1921
+ console.log(chalk17.dim(" You have successfully prepared a report."));
1828
1922
  console.log(
1829
- chalk16.dim(
1923
+ chalk17.dim(
1830
1924
  " Please present the URL above to the user so they can finalize the submission."
1831
1925
  )
1832
1926
  );
@@ -1835,14 +1929,14 @@ Type: ${type}
1835
1929
  const bugUrl = `${repoUrl}/issues/new?template=bug_report.md`;
1836
1930
  const featureUrl = `${repoUrl}/issues/new?template=feature_request.md`;
1837
1931
  const metricUrl = `${repoUrl}/issues/new?template=new_metric_idea.md`;
1838
- console.log(chalk16.blue("\u{1F4AC} Feedback & Bug Reports\n"));
1839
- console.log(` Report a Bug: ${chalk16.cyan(bugUrl)}`);
1840
- console.log(` Request a Feature: ${chalk16.cyan(featureUrl)}`);
1841
- console.log(` Suggest a Metric: ${chalk16.cyan(metricUrl)}`);
1842
- console.log(chalk16.bold("\n\u{1F916} [FOR AI AGENTS]:"));
1843
- console.log(chalk16.dim(" To prepare a specific report, run:"));
1932
+ console.log(chalk17.blue("\u{1F4AC} Feedback & Bug Reports\n"));
1933
+ console.log(` Report a Bug: ${chalk17.cyan(bugUrl)}`);
1934
+ console.log(` Request a Feature: ${chalk17.cyan(featureUrl)}`);
1935
+ console.log(` Suggest a Metric: ${chalk17.cyan(metricUrl)}`);
1936
+ console.log(chalk17.bold("\n\u{1F916} [FOR AI AGENTS]:"));
1937
+ console.log(chalk17.dim(" To prepare a specific report, run:"));
1844
1938
  console.log(
1845
- chalk16.cyan(
1939
+ chalk17.cyan(
1846
1940
  ' aiready bug "your description here" --type bug|feature|metric'
1847
1941
  )
1848
1942
  );
@@ -1855,6 +1949,97 @@ EXAMPLES:
1855
1949
  $ aiready bug "Fix typo in scan output" --submit # Submit directly via gh CLI
1856
1950
  `;
1857
1951
 
1952
+ // src/commands/remediate.ts
1953
+ import chalk18 from "chalk";
1954
+ import { resolve as resolvePath6 } from "path";
1955
+ import { existsSync as existsSync4, readdirSync } from "fs";
1956
+ import { printTerminalHeader as printTerminalHeader3 } from "@aiready/core";
1957
+ async function remediateAction(directory, options) {
1958
+ const resolvedDir = resolvePath6(process.cwd(), directory || ".");
1959
+ const serverUrl = options.server || "https://platform.getaiready.dev";
1960
+ printTerminalHeader3("AIREADY REMEDIATION SWARM");
1961
+ console.log(chalk18.cyan("\u{1F916} Initializing local remediation agent..."));
1962
+ let reportPath = options.report;
1963
+ if (!reportPath) {
1964
+ const aireadyDir = resolvePath6(resolvedDir, ".aiready");
1965
+ if (existsSync4(aireadyDir)) {
1966
+ const files = readdirSync(aireadyDir).filter((f) => f.startsWith("aiready-report-") && f.endsWith(".json")).sort().reverse();
1967
+ if (files.length > 0) {
1968
+ reportPath = resolvePath6(aireadyDir, files[0]);
1969
+ console.log(chalk18.dim(`\u{1F4C2} Using latest report: ${files[0]}`));
1970
+ }
1971
+ }
1972
+ }
1973
+ if (!reportPath || !existsSync4(reportPath)) {
1974
+ console.log(chalk18.yellow("\n\u26A0\uFE0F No AIReady report found."));
1975
+ console.log(
1976
+ chalk18.dim(
1977
+ " Remediation requires a recent scan report to identify issues."
1978
+ )
1979
+ );
1980
+ console.log(chalk18.white(` Run ${chalk18.bold("aiready scan")} first.
1981
+ `));
1982
+ return;
1983
+ }
1984
+ console.log(chalk18.green("\n\u2705 Analysis data loaded."));
1985
+ console.log(chalk18.bold("\n\u{1F680} Remediation Strategy:"));
1986
+ if (options.tool === "patterns" || !options.tool) {
1987
+ console.log(
1988
+ chalk18.white(
1989
+ ` \u2022 ${chalk18.bold("Pattern Consolidation")}: Suggested refactors for 95%+ similar code blocks.`
1990
+ )
1991
+ );
1992
+ }
1993
+ if (options.tool === "consistency" || !options.tool) {
1994
+ console.log(
1995
+ chalk18.white(
1996
+ ` \u2022 ${chalk18.bold("Naming Alignment")}: Automated TSDoc generation and constant extraction.`
1997
+ )
1998
+ );
1999
+ }
2000
+ if (options.tool === "context" || !options.tool) {
2001
+ console.log(
2002
+ chalk18.white(
2003
+ ` \u2022 ${chalk18.bold("Context Optimization")}: Barrel file cleanup and import flattening.`
2004
+ )
2005
+ );
2006
+ }
2007
+ console.log(
2008
+ chalk18.dim(
2009
+ "\n\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"
2010
+ )
2011
+ );
2012
+ console.log(chalk18.bold("\u2728 Use the Platform Swarm for Automated Fixes"));
2013
+ console.log(
2014
+ chalk18.cyan(` The high-performance Remediation Swarm is available at:`)
2015
+ );
2016
+ console.log(chalk18.white(` ${chalk18.bold(`${serverUrl}/remediate`)}`));
2017
+ console.log(
2018
+ chalk18.dim(
2019
+ "\n The swarm uses specialized agents to safely refactor your code,"
2020
+ )
2021
+ );
2022
+ console.log(
2023
+ chalk18.dim(" ensuring every change improves your AI-readiness score.")
2024
+ );
2025
+ console.log(
2026
+ chalk18.dim(
2027
+ "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"
2028
+ )
2029
+ );
2030
+ console.log(
2031
+ chalk18.dim(
2032
+ '\n\u{1F4A1} Next Version: Local "aiready fix" command for minor documentation issues.'
2033
+ )
2034
+ );
2035
+ }
2036
+ var REMEDIATE_HELP_TEXT = `
2037
+ EXAMPLES:
2038
+ $ aiready remediate # See remediation options for latest report
2039
+ $ aiready remediate --tool patterns # Focus on pattern consolidation
2040
+ $ aiready remediate --report ./custom-report.json
2041
+ `;
2042
+
1858
2043
  // src/cli.ts
1859
2044
  var getDirname = () => {
1860
2045
  if (typeof __dirname !== "undefined") return __dirname;
@@ -2005,9 +2190,19 @@ program.command("change-amplification").description("Analyze graph metrics for c
2005
2190
  program.command("testability").description("Analyze test coverage and AI readiness").argument("[directory]", "Directory to analyze", ".").option("--min-coverage <ratio>", "Minimum acceptable coverage ratio", "0.3").option("--include <patterns>", "File patterns to include (comma-separated)").option("--exclude <patterns>", "File patterns to exclude (comma-separated)").option("-o, --output <format>", "Output format: console, json", "console").option("--output-file <path>", "Output file path (for json)").action(async (directory, options) => {
2006
2191
  await testabilityAction(directory, options);
2007
2192
  });
2193
+ program.command("contract").description("Analyze structural contract enforcement and defensive coding").argument("[directory]", "Directory to analyze", ".").option(
2194
+ "--min-chain-depth <depth>",
2195
+ "Minimum optional chain depth to flag",
2196
+ "3"
2197
+ ).option("--include <patterns>", "File patterns to include (comma-separated)").option("--exclude <patterns>", "File patterns to exclude (comma-separated)").option("-o, --output <format>", "Output format: console, json", "console").option("--output-file <path>", "Output file path (for json)").action(async (directory, options) => {
2198
+ await contractEnforcementAction(directory, options);
2199
+ });
2008
2200
  program.command("upload").description("Upload an AIReady report JSON to the platform").argument("<file>", "Report JSON file to upload").option("--api-key <key>", "Platform API key").option("--repo-id <id>", "Platform repository ID (optional)").option("--server <url>", "Custom platform URL").addHelpText("after", UPLOAD_HELP_TEXT).action(async (file, options) => {
2009
2201
  await uploadAction(file, options);
2010
2202
  });
2203
+ program.command("remediate").description("Suggest AI-ready refactors based on a scan report").argument("[directory]", "Directory to remediate", ".").option("-r, --report <path>", "AIReady report JSON file").option("-t, --tool <name>", "Filter by tool: patterns, context, consistency").option("--server <url>", "Custom platform URL").addHelpText("after", REMEDIATE_HELP_TEXT).action(async (directory, options) => {
2204
+ await remediateAction(directory, options);
2205
+ });
2011
2206
  program.command("bug").description("Report a bug or provide feedback (Agent-friendly)").argument("[message]", "Short description of the issue").option("-t, --type <type>", "Issue type: bug, feature, metric", "bug").option("--submit", "Submit the issue directly using the GitHub CLI (gh)").addHelpText("after", BUG_HELP_TEXT).action(async (message, options) => {
2012
2207
  await bugAction(message, options);
2013
2208
  });
package/dist/index.js CHANGED
@@ -41,6 +41,7 @@ var TOOL_PACKAGE_MAP = {
41
41
  [import_core.ToolName.DocDrift]: "@aiready/doc-drift",
42
42
  [import_core.ToolName.DependencyHealth]: "@aiready/deps",
43
43
  [import_core.ToolName.ChangeAmplification]: "@aiready/change-amplification",
44
+ [import_core.ToolName.ContractEnforcement]: "@aiready/contract-enforcement",
44
45
  // Aliases handled by registry
45
46
  patterns: "@aiready/pattern-detect",
46
47
  duplicates: "@aiready/pattern-detect",
@@ -51,7 +52,8 @@ var TOOL_PACKAGE_MAP = {
51
52
  grounding: "@aiready/agent-grounding",
52
53
  testability: "@aiready/testability",
53
54
  "deps-health": "@aiready/deps",
54
- "change-amp": "@aiready/change-amplification"
55
+ "change-amp": "@aiready/change-amplification",
56
+ contract: "@aiready/contract-enforcement"
55
57
  };
56
58
  var UnifiedOrchestrator = class {
57
59
  /**
@@ -200,7 +202,8 @@ var UnifiedOrchestrator = class {
200
202
  "testability-index": ["testabilityIndex", "testability"],
201
203
  "doc-drift": ["docDrift"],
202
204
  "dependency-health": ["dependencyHealth", "deps"],
203
- "change-amplification": ["changeAmplification"]
205
+ "change-amplification": ["changeAmplification"],
206
+ "contract-enforcement": ["contractEnforcement", "contract"]
204
207
  };
205
208
  for (const [kebabKey, aliases] of Object.entries(keyMappings)) {
206
209
  if (result[kebabKey]) {
package/dist/index.mjs CHANGED
@@ -5,7 +5,7 @@ import {
5
5
  analyzeUnified,
6
6
  generateUnifiedSummary,
7
7
  scoreUnified
8
- } from "./chunk-SK6WW6HW.mjs";
8
+ } from "./chunk-HX6H3VOE.mjs";
9
9
  export {
10
10
  ScoringOrchestrator,
11
11
  TOOL_PACKAGE_MAP,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aiready/cli",
3
- "version": "0.14.26",
3
+ "version": "0.15.0",
4
4
  "description": "Assess and improve your codebase's AI-readiness. Get an AI Readiness Score (0-100) and detect semantic duplicates, context fragmentation, and naming inconsistencies.",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -24,17 +24,18 @@
24
24
  "dependencies": {
25
25
  "chalk": "^5.3.0",
26
26
  "commander": "^14.0.0",
27
- "@aiready/agent-grounding": "0.13.23",
28
- "@aiready/context-analyzer": "0.21.27",
29
- "@aiready/consistency": "0.20.23",
30
- "@aiready/core": "0.23.24",
31
- "@aiready/deps": "0.13.24",
32
- "@aiready/doc-drift": "0.13.23",
33
- "@aiready/change-amplification": "0.13.23",
34
- "@aiready/ai-signal-clarity": "0.13.24",
35
- "@aiready/visualizer": "0.6.23",
36
- "@aiready/pattern-detect": "0.16.23",
37
- "@aiready/testability": "0.6.23"
27
+ "@aiready/agent-grounding": "0.14.0",
28
+ "@aiready/consistency": "0.21.0",
29
+ "@aiready/deps": "0.14.0",
30
+ "@aiready/context-analyzer": "0.22.0",
31
+ "@aiready/core": "0.24.0",
32
+ "@aiready/doc-drift": "0.14.0",
33
+ "@aiready/change-amplification": "0.14.0",
34
+ "@aiready/contract-enforcement": "0.2.0",
35
+ "@aiready/ai-signal-clarity": "0.14.0",
36
+ "@aiready/pattern-detect": "0.17.0",
37
+ "@aiready/testability": "0.7.0",
38
+ "@aiready/visualizer": "0.7.0"
38
39
  },
39
40
  "devDependencies": {
40
41
  "@types/node": "^24.0.0",