@aiready/cli 0.9.18 → 0.9.20

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
 
2
2
  
3
- > @aiready/cli@0.9.18 build /Users/pengcao/projects/aiready/packages/cli
3
+ > @aiready/cli@0.9.20 build /Users/pengcao/projects/aiready/packages/cli
4
4
  > tsup src/index.ts src/cli.ts --format cjs,esm
5
5
 
6
6
  CLI Building entry: src/cli.ts, src/index.ts
@@ -9,10 +9,10 @@
9
9
  CLI Target: es2020
10
10
  CJS Build start
11
11
  ESM Build start
12
- CJS dist/cli.js 62.33 KB
13
12
  CJS dist/index.js 4.93 KB
14
- CJS ⚡️ Build success in 17ms
13
+ CJS dist/cli.js 65.17 KB
14
+ CJS ⚡️ Build success in 19ms
15
+ ESM dist/cli.mjs 57.52 KB
15
16
  ESM dist/index.mjs 138.00 B
16
- ESM dist/cli.mjs 54.86 KB
17
17
  ESM dist/chunk-5GZDRZ3T.mjs 4.17 KB
18
- ESM ⚡️ Build success in 18ms
18
+ ESM ⚡️ Build success in 20ms
@@ -1,17 +1,17 @@
1
1
 
2
2
  
3
- > @aiready/cli@0.9.18 test /Users/pengcao/projects/aiready/packages/cli
3
+ > @aiready/cli@0.9.20 test /Users/pengcao/projects/aiready/packages/cli
4
4
  > vitest run
5
5
 
6
6
  [?25l
7
7
   RUN  v4.0.18 /Users/pengcao/projects/aiready/packages/cli
8
8
 
9
- ✓ dist/__tests__/cli.test.js (3 tests) 7ms
10
9
  ✓ src/__tests__/cli.test.ts (3 tests) 2ms
10
+ ✓ dist/__tests__/cli.test.js (3 tests) 15ms
11
11
 
12
12
   Test Files  2 passed (2)
13
13
   Tests  6 passed (6)
14
-  Start at  13:51:03
15
-  Duration  1.26s (transform 279ms, setup 0ms, import 1.82s, tests 9ms, environment 0ms)
14
+  Start at  00:26:59
15
+  Duration  4.25s (transform 1.20s, setup 0ms, import 5.70s, tests 17ms, environment 0ms)
16
16
 
17
17
  [?25h
package/dist/cli.js CHANGED
@@ -157,7 +157,7 @@ VERSION: ${packageJson.version}
157
157
  DOCUMENTATION: https://aiready.dev/docs/cli
158
158
  GITHUB: https://github.com/caopengau/aiready-cli
159
159
  LANDING: https://github.com/caopengau/aiready-landing`);
160
- program.command("scan").description("Run comprehensive AI-readiness analysis (patterns + context + consistency)").argument("[directory]", "Directory to analyze", ".").option("-t, --tools <tools>", "Tools to run (comma-separated: patterns,context,consistency)", "patterns,context,consistency").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)").option("--score", "Calculate and display AI Readiness Score (0-100) with breakdown").option("--weights <weights>", "Custom scoring weights (patterns:40,context:35,consistency:25)").option("--threshold <score>", "Fail CI/CD if score below threshold (0-100)").addHelpText("after", `
160
+ program.command("scan").description("Run comprehensive AI-readiness analysis (patterns + context + consistency)").argument("[directory]", "Directory to analyze", ".").option("-t, --tools <tools>", "Tools to run (comma-separated: patterns,context,consistency)", "patterns,context,consistency").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", "json").option("--output-file <path>", "Output file path (for json)").option("--score", "Calculate and display AI Readiness Score (0-100) with breakdown").option("--weights <weights>", "Custom scoring weights (patterns:40,context:35,consistency:25)").option("--threshold <score>", "Fail CI/CD if score below threshold (0-100)").addHelpText("after", `
161
161
  EXAMPLES:
162
162
  $ aiready scan # Analyze all tools
163
163
  $ aiready scan --tools patterns,context # Skip consistency
@@ -173,7 +173,7 @@ EXAMPLES:
173
173
  include: void 0,
174
174
  exclude: void 0,
175
175
  output: {
176
- format: "console",
176
+ format: "json",
177
177
  file: void 0
178
178
  }
179
179
  };
@@ -392,6 +392,7 @@ EXAMPLES:
392
392
  const outputPath = (0, import_core.resolveOutputPath)(userOutputFile, defaultFilename, resolvedDir);
393
393
  const outputData = { ...results, scoring: scoringResult };
394
394
  (0, import_core.handleJSONOutput)(outputData, outputPath, `\u2705 Report saved to ${outputPath}`);
395
+ warnIfGraphCapExceeded(outputData, resolvedDir);
395
396
  }
396
397
  } catch (error) {
397
398
  (0, import_core.handleCLIError)(error, "Analysis");
@@ -1001,6 +1002,50 @@ function findLatestScanReport(dirPath) {
1001
1002
  const sortedFiles = files.map((f) => ({ name: f, path: (0, import_path2.resolve)(aireadyDir, f), mtime: statSync((0, import_path2.resolve)(aireadyDir, f)).mtime })).sort((a, b) => b.mtime.getTime() - a.mtime.getTime());
1002
1003
  return sortedFiles[0].path;
1003
1004
  }
1005
+ function warnIfGraphCapExceeded(report, dirPath) {
1006
+ try {
1007
+ const { loadConfig } = require("@aiready/core");
1008
+ const { existsSync: existsSync2, readFileSync: readFileSync2 } = require("fs");
1009
+ const { resolve } = require("path");
1010
+ let graphConfig = { maxNodes: 400, maxEdges: 600 };
1011
+ const configPath = resolve(dirPath, "aiready.json");
1012
+ if (existsSync2(configPath)) {
1013
+ try {
1014
+ const rawConfig = JSON.parse(readFileSync2(configPath, "utf8"));
1015
+ if (rawConfig.visualizer?.graph) {
1016
+ graphConfig = {
1017
+ maxNodes: rawConfig.visualizer.graph.maxNodes ?? graphConfig.maxNodes,
1018
+ maxEdges: rawConfig.visualizer.graph.maxEdges ?? graphConfig.maxEdges
1019
+ };
1020
+ }
1021
+ } catch (e) {
1022
+ }
1023
+ }
1024
+ const nodeCount = (report.context?.length || 0) + (report.patterns?.length || 0);
1025
+ const edgeCount = report.context?.reduce((sum, ctx) => {
1026
+ const relCount = ctx.relatedFiles?.length || 0;
1027
+ const depCount = ctx.dependencies?.length || 0;
1028
+ return sum + relCount + depCount;
1029
+ }, 0) || 0;
1030
+ if (nodeCount > graphConfig.maxNodes || edgeCount > graphConfig.maxEdges) {
1031
+ console.log("");
1032
+ console.log(import_chalk.default.yellow(`\u26A0\uFE0F Graph may be truncated at visualization time:`));
1033
+ if (nodeCount > graphConfig.maxNodes) {
1034
+ console.log(import_chalk.default.dim(` \u2022 Nodes: ${nodeCount} > limit ${graphConfig.maxNodes}`));
1035
+ }
1036
+ if (edgeCount > graphConfig.maxEdges) {
1037
+ console.log(import_chalk.default.dim(` \u2022 Edges: ${edgeCount} > limit ${graphConfig.maxEdges}`));
1038
+ }
1039
+ console.log(import_chalk.default.dim(` To increase limits, add to aiready.json:`));
1040
+ console.log(import_chalk.default.dim(` {`));
1041
+ console.log(import_chalk.default.dim(` "visualizer": {`));
1042
+ console.log(import_chalk.default.dim(` "graph": { "maxNodes": 2000, "maxEdges": 5000 }`));
1043
+ console.log(import_chalk.default.dim(` }`));
1044
+ console.log(import_chalk.default.dim(` }`));
1045
+ }
1046
+ } catch (e) {
1047
+ }
1048
+ }
1004
1049
  async function handleVisualize(directory, options) {
1005
1050
  try {
1006
1051
  const dirPath = (0, import_path2.resolve)(process.cwd(), directory || ".");
@@ -1023,6 +1068,22 @@ Or specify a custom report:
1023
1068
  }
1024
1069
  const raw = (0, import_fs2.readFileSync)(reportPath, "utf8");
1025
1070
  const report = JSON.parse(raw);
1071
+ const configPath = (0, import_path2.resolve)(dirPath, "aiready.json");
1072
+ let graphConfig = { maxNodes: 400, maxEdges: 600 };
1073
+ if ((0, import_fs2.existsSync)(configPath)) {
1074
+ try {
1075
+ const rawConfig = JSON.parse((0, import_fs2.readFileSync)(configPath, "utf8"));
1076
+ if (rawConfig.visualizer?.graph) {
1077
+ graphConfig = {
1078
+ maxNodes: rawConfig.visualizer.graph.maxNodes ?? graphConfig.maxNodes,
1079
+ maxEdges: rawConfig.visualizer.graph.maxEdges ?? graphConfig.maxEdges
1080
+ };
1081
+ }
1082
+ } catch (e) {
1083
+ }
1084
+ }
1085
+ const envVisualizerConfig = JSON.stringify(graphConfig);
1086
+ process.env.AIREADY_VISUALIZER_CONFIG = envVisualizerConfig;
1026
1087
  console.log("Building graph from report...");
1027
1088
  const { GraphBuilder } = await import("@aiready/visualizer/graph");
1028
1089
  const graph = GraphBuilder.buildFromReport(report, dirPath);
@@ -1087,7 +1148,11 @@ Or specify a custom report:
1087
1148
  if (watchTimeout) clearTimeout(watchTimeout);
1088
1149
  watchTimeout = setTimeout(copyReportToViz, 100);
1089
1150
  });
1090
- const envForSpawn = { ...process.env, AIREADY_REPORT_PATH: reportPath };
1151
+ const envForSpawn = {
1152
+ ...process.env,
1153
+ AIREADY_REPORT_PATH: reportPath,
1154
+ AIREADY_VISUALIZER_CONFIG: envVisualizerConfig
1155
+ };
1091
1156
  const vite = spawn("pnpm", ["run", "dev:web"], { cwd: spawnCwd, stdio: "inherit", shell: true, env: envForSpawn });
1092
1157
  const onExit = () => {
1093
1158
  try {
package/dist/cli.mjs CHANGED
@@ -62,7 +62,7 @@ VERSION: ${packageJson.version}
62
62
  DOCUMENTATION: https://aiready.dev/docs/cli
63
63
  GITHUB: https://github.com/caopengau/aiready-cli
64
64
  LANDING: https://github.com/caopengau/aiready-landing`);
65
- program.command("scan").description("Run comprehensive AI-readiness analysis (patterns + context + consistency)").argument("[directory]", "Directory to analyze", ".").option("-t, --tools <tools>", "Tools to run (comma-separated: patterns,context,consistency)", "patterns,context,consistency").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)").option("--score", "Calculate and display AI Readiness Score (0-100) with breakdown").option("--weights <weights>", "Custom scoring weights (patterns:40,context:35,consistency:25)").option("--threshold <score>", "Fail CI/CD if score below threshold (0-100)").addHelpText("after", `
65
+ program.command("scan").description("Run comprehensive AI-readiness analysis (patterns + context + consistency)").argument("[directory]", "Directory to analyze", ".").option("-t, --tools <tools>", "Tools to run (comma-separated: patterns,context,consistency)", "patterns,context,consistency").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", "json").option("--output-file <path>", "Output file path (for json)").option("--score", "Calculate and display AI Readiness Score (0-100) with breakdown").option("--weights <weights>", "Custom scoring weights (patterns:40,context:35,consistency:25)").option("--threshold <score>", "Fail CI/CD if score below threshold (0-100)").addHelpText("after", `
66
66
  EXAMPLES:
67
67
  $ aiready scan # Analyze all tools
68
68
  $ aiready scan --tools patterns,context # Skip consistency
@@ -78,7 +78,7 @@ EXAMPLES:
78
78
  include: void 0,
79
79
  exclude: void 0,
80
80
  output: {
81
- format: "console",
81
+ format: "json",
82
82
  file: void 0
83
83
  }
84
84
  };
@@ -297,6 +297,7 @@ EXAMPLES:
297
297
  const outputPath = resolveOutputPath(userOutputFile, defaultFilename, resolvedDir);
298
298
  const outputData = { ...results, scoring: scoringResult };
299
299
  handleJSONOutput(outputData, outputPath, `\u2705 Report saved to ${outputPath}`);
300
+ warnIfGraphCapExceeded(outputData, resolvedDir);
300
301
  }
301
302
  } catch (error) {
302
303
  handleCLIError(error, "Analysis");
@@ -906,6 +907,50 @@ function findLatestScanReport(dirPath) {
906
907
  const sortedFiles = files.map((f) => ({ name: f, path: resolvePath(aireadyDir, f), mtime: statSync(resolvePath(aireadyDir, f)).mtime })).sort((a, b) => b.mtime.getTime() - a.mtime.getTime());
907
908
  return sortedFiles[0].path;
908
909
  }
910
+ function warnIfGraphCapExceeded(report, dirPath) {
911
+ try {
912
+ const { loadConfig } = __require("@aiready/core");
913
+ const { existsSync: existsSync2, readFileSync: readFileSync2 } = __require("fs");
914
+ const { resolve } = __require("path");
915
+ let graphConfig = { maxNodes: 400, maxEdges: 600 };
916
+ const configPath = resolve(dirPath, "aiready.json");
917
+ if (existsSync2(configPath)) {
918
+ try {
919
+ const rawConfig = JSON.parse(readFileSync2(configPath, "utf8"));
920
+ if (rawConfig.visualizer?.graph) {
921
+ graphConfig = {
922
+ maxNodes: rawConfig.visualizer.graph.maxNodes ?? graphConfig.maxNodes,
923
+ maxEdges: rawConfig.visualizer.graph.maxEdges ?? graphConfig.maxEdges
924
+ };
925
+ }
926
+ } catch (e) {
927
+ }
928
+ }
929
+ const nodeCount = (report.context?.length || 0) + (report.patterns?.length || 0);
930
+ const edgeCount = report.context?.reduce((sum, ctx) => {
931
+ const relCount = ctx.relatedFiles?.length || 0;
932
+ const depCount = ctx.dependencies?.length || 0;
933
+ return sum + relCount + depCount;
934
+ }, 0) || 0;
935
+ if (nodeCount > graphConfig.maxNodes || edgeCount > graphConfig.maxEdges) {
936
+ console.log("");
937
+ console.log(chalk.yellow(`\u26A0\uFE0F Graph may be truncated at visualization time:`));
938
+ if (nodeCount > graphConfig.maxNodes) {
939
+ console.log(chalk.dim(` \u2022 Nodes: ${nodeCount} > limit ${graphConfig.maxNodes}`));
940
+ }
941
+ if (edgeCount > graphConfig.maxEdges) {
942
+ console.log(chalk.dim(` \u2022 Edges: ${edgeCount} > limit ${graphConfig.maxEdges}`));
943
+ }
944
+ console.log(chalk.dim(` To increase limits, add to aiready.json:`));
945
+ console.log(chalk.dim(` {`));
946
+ console.log(chalk.dim(` "visualizer": {`));
947
+ console.log(chalk.dim(` "graph": { "maxNodes": 2000, "maxEdges": 5000 }`));
948
+ console.log(chalk.dim(` }`));
949
+ console.log(chalk.dim(` }`));
950
+ }
951
+ } catch (e) {
952
+ }
953
+ }
909
954
  async function handleVisualize(directory, options) {
910
955
  try {
911
956
  const dirPath = resolvePath(process.cwd(), directory || ".");
@@ -928,6 +973,22 @@ Or specify a custom report:
928
973
  }
929
974
  const raw = readFileSync(reportPath, "utf8");
930
975
  const report = JSON.parse(raw);
976
+ const configPath = resolvePath(dirPath, "aiready.json");
977
+ let graphConfig = { maxNodes: 400, maxEdges: 600 };
978
+ if (existsSync(configPath)) {
979
+ try {
980
+ const rawConfig = JSON.parse(readFileSync(configPath, "utf8"));
981
+ if (rawConfig.visualizer?.graph) {
982
+ graphConfig = {
983
+ maxNodes: rawConfig.visualizer.graph.maxNodes ?? graphConfig.maxNodes,
984
+ maxEdges: rawConfig.visualizer.graph.maxEdges ?? graphConfig.maxEdges
985
+ };
986
+ }
987
+ } catch (e) {
988
+ }
989
+ }
990
+ const envVisualizerConfig = JSON.stringify(graphConfig);
991
+ process.env.AIREADY_VISUALIZER_CONFIG = envVisualizerConfig;
931
992
  console.log("Building graph from report...");
932
993
  const { GraphBuilder } = await import("@aiready/visualizer/graph");
933
994
  const graph = GraphBuilder.buildFromReport(report, dirPath);
@@ -992,7 +1053,11 @@ Or specify a custom report:
992
1053
  if (watchTimeout) clearTimeout(watchTimeout);
993
1054
  watchTimeout = setTimeout(copyReportToViz, 100);
994
1055
  });
995
- const envForSpawn = { ...process.env, AIREADY_REPORT_PATH: reportPath };
1056
+ const envForSpawn = {
1057
+ ...process.env,
1058
+ AIREADY_REPORT_PATH: reportPath,
1059
+ AIREADY_VISUALIZER_CONFIG: envVisualizerConfig
1060
+ };
996
1061
  const vite = spawn("pnpm", ["run", "dev:web"], { cwd: spawnCwd, stdio: "inherit", shell: true, env: envForSpawn });
997
1062
  const onExit = () => {
998
1063
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aiready/cli",
3
- "version": "0.9.18",
3
+ "version": "0.9.20",
4
4
  "description": "Unified CLI for AIReady analysis tools",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -11,11 +11,11 @@
11
11
  "dependencies": {
12
12
  "commander": "^14.0.0",
13
13
  "chalk": "^5.3.0",
14
- "@aiready/visualizer": "0.1.21",
15
- "@aiready/consistency": "0.8.17",
16
- "@aiready/pattern-detect": "0.11.17",
17
- "@aiready/context-analyzer": "0.9.17",
18
- "@aiready/core": "0.9.17"
14
+ "@aiready/visualizer": "0.1.25",
15
+ "@aiready/consistency": "0.8.19",
16
+ "@aiready/context-analyzer": "0.9.19",
17
+ "@aiready/core": "0.9.19",
18
+ "@aiready/pattern-detect": "0.11.19"
19
19
  },
20
20
  "devDependencies": {
21
21
  "tsup": "^8.3.5",
package/src/cli.ts CHANGED
@@ -77,7 +77,7 @@ program
77
77
  .option('-t, --tools <tools>', 'Tools to run (comma-separated: patterns,context,consistency)', 'patterns,context,consistency')
78
78
  .option('--include <patterns>', 'File patterns to include (comma-separated)')
79
79
  .option('--exclude <patterns>', 'File patterns to exclude (comma-separated)')
80
- .option('-o, --output <format>', 'Output format: console, json', 'console')
80
+ .option('-o, --output <format>', 'Output format: console, json', 'json')
81
81
  .option('--output-file <path>', 'Output file path (for json)')
82
82
  .option('--score', 'Calculate and display AI Readiness Score (0-100) with breakdown')
83
83
  .option('--weights <weights>', 'Custom scoring weights (patterns:40,context:35,consistency:25)')
@@ -103,7 +103,7 @@ EXAMPLES:
103
103
  include: undefined,
104
104
  exclude: undefined,
105
105
  output: {
106
- format: 'console',
106
+ format: 'json',
107
107
  file: undefined,
108
108
  },
109
109
  };
@@ -381,6 +381,9 @@ EXAMPLES:
381
381
  const outputPath = resolveOutputPath(userOutputFile, defaultFilename, resolvedDir);
382
382
  const outputData = { ...results, scoring: scoringResult };
383
383
  handleJSONOutput(outputData, outputPath, `✅ Report saved to ${outputPath}`);
384
+
385
+ // Warn if graph caps may be exceeded
386
+ warnIfGraphCapExceeded(outputData, resolvedDir);
384
387
  }
385
388
  } catch (error) {
386
389
  handleCLIError(error, 'Analysis');
@@ -1131,6 +1134,61 @@ program
1131
1134
  return sortedFiles[0].path;
1132
1135
  }
1133
1136
 
1137
+ function warnIfGraphCapExceeded(report: any, dirPath: string) {
1138
+ try {
1139
+ // Use dynamic import and loadConfig to get the raw visualizer config
1140
+ const { loadConfig } = require('@aiready/core');
1141
+
1142
+ // Sync file system check since we can't easily call loadConfig synchronously
1143
+ const { existsSync, readFileSync } = require('fs');
1144
+ const { resolve } = require('path');
1145
+
1146
+ let graphConfig = { maxNodes: 400, maxEdges: 600 };
1147
+
1148
+ // Try to read aiready.json synchronously
1149
+ const configPath = resolve(dirPath, 'aiready.json');
1150
+ if (existsSync(configPath)) {
1151
+ try {
1152
+ const rawConfig = JSON.parse(readFileSync(configPath, 'utf8'));
1153
+ if (rawConfig.visualizer?.graph) {
1154
+ graphConfig = {
1155
+ maxNodes: rawConfig.visualizer.graph.maxNodes ?? graphConfig.maxNodes,
1156
+ maxEdges: rawConfig.visualizer.graph.maxEdges ?? graphConfig.maxEdges,
1157
+ };
1158
+ }
1159
+ } catch (e) {
1160
+ // Silently ignore parse errors and use defaults
1161
+ }
1162
+ }
1163
+
1164
+ const nodeCount = (report.context?.length || 0) + (report.patterns?.length || 0);
1165
+ const edgeCount = report.context?.reduce((sum: number, ctx: any) => {
1166
+ const relCount = ctx.relatedFiles?.length || 0;
1167
+ const depCount = ctx.dependencies?.length || 0;
1168
+ return sum + relCount + depCount;
1169
+ }, 0) || 0;
1170
+
1171
+ if (nodeCount > graphConfig.maxNodes || edgeCount > graphConfig.maxEdges) {
1172
+ console.log('');
1173
+ console.log(chalk.yellow(`⚠️ Graph may be truncated at visualization time:`));
1174
+ if (nodeCount > graphConfig.maxNodes) {
1175
+ console.log(chalk.dim(` • Nodes: ${nodeCount} > limit ${graphConfig.maxNodes}`));
1176
+ }
1177
+ if (edgeCount > graphConfig.maxEdges) {
1178
+ console.log(chalk.dim(` • Edges: ${edgeCount} > limit ${graphConfig.maxEdges}`));
1179
+ }
1180
+ console.log(chalk.dim(` To increase limits, add to aiready.json:`));
1181
+ console.log(chalk.dim(` {`));
1182
+ console.log(chalk.dim(` "visualizer": {`));
1183
+ console.log(chalk.dim(` "graph": { "maxNodes": 2000, "maxEdges": 5000 }`));
1184
+ console.log(chalk.dim(` }`));
1185
+ console.log(chalk.dim(` }`));
1186
+ }
1187
+ } catch (e) {
1188
+ // Silently fail on config read errors
1189
+ }
1190
+ }
1191
+
1134
1192
  async function handleVisualize(directory: string | undefined, options: any) {
1135
1193
  try {
1136
1194
  const dirPath = resolvePath(process.cwd(), directory || '.');
@@ -1152,6 +1210,28 @@ program
1152
1210
  const raw = readFileSync(reportPath, 'utf8');
1153
1211
  const report = JSON.parse(raw);
1154
1212
 
1213
+ // Load config to extract graph caps
1214
+ const configPath = resolvePath(dirPath, 'aiready.json');
1215
+ let graphConfig = { maxNodes: 400, maxEdges: 600 };
1216
+
1217
+ if (existsSync(configPath)) {
1218
+ try {
1219
+ const rawConfig = JSON.parse(readFileSync(configPath, 'utf8'));
1220
+ if (rawConfig.visualizer?.graph) {
1221
+ graphConfig = {
1222
+ maxNodes: rawConfig.visualizer.graph.maxNodes ?? graphConfig.maxNodes,
1223
+ maxEdges: rawConfig.visualizer.graph.maxEdges ?? graphConfig.maxEdges,
1224
+ };
1225
+ }
1226
+ } catch (e) {
1227
+ // Silently ignore parse errors and use defaults
1228
+ }
1229
+ }
1230
+
1231
+ // Store config in env for vite middleware to pass to client
1232
+ const envVisualizerConfig = JSON.stringify(graphConfig);
1233
+ process.env.AIREADY_VISUALIZER_CONFIG = envVisualizerConfig;
1234
+
1155
1235
  console.log("Building graph from report...");
1156
1236
  const { GraphBuilder } = await import('@aiready/visualizer/graph');
1157
1237
  const graph = GraphBuilder.buildFromReport(report, dirPath);
@@ -1233,7 +1313,11 @@ program
1233
1313
  watchTimeout = setTimeout(copyReportToViz, 100);
1234
1314
  });
1235
1315
 
1236
- const envForSpawn = { ...process.env, AIREADY_REPORT_PATH: reportPath };
1316
+ const envForSpawn = {
1317
+ ...process.env,
1318
+ AIREADY_REPORT_PATH: reportPath,
1319
+ AIREADY_VISUALIZER_CONFIG: envVisualizerConfig
1320
+ };
1237
1321
  const vite = spawn("pnpm", ["run", "dev:web"], { cwd: spawnCwd, stdio: "inherit", shell: true, env: envForSpawn });
1238
1322
  const onExit = () => {
1239
1323
  try {