@chappibunny/repolens 1.6.0 → 1.6.1

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/README.md CHANGED
@@ -25,7 +25,7 @@ RepoLens scans your repository, generates living architecture documentation, and
25
25
 
26
26
  > **Try it now** — no installation required. Run `npx @chappibunny/repolens demo` on any repo for an instant local preview.
27
27
 
28
- [![RepoLens Demo](https://img.youtube.com/vi/hNd7xim_rTE/maxresdefault.jpg)](https://youtu.be/hNd7xim_rTE)
28
+ [![RepoLens Demo](https://img.youtube.com/vi/Lpyg0dGsiws/maxresdefault.jpg)](https://youtu.be/Lpyg0dGsiws)
29
29
 
30
30
  ▶️ *Click to watch on YouTube*
31
31
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chappibunny/repolens",
3
- "version": "1.6.0",
3
+ "version": "1.6.1",
4
4
  "description": "AI-assisted documentation intelligence system for technical and non-technical audiences",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -64,10 +64,8 @@
64
64
  "js-yaml": "^4.1.0",
65
65
  "node-fetch": "^3.3.2"
66
66
  },
67
- "optionalDependencies": {
68
- "@mermaid-js/mermaid-cli": "^11.12.0"
69
- },
70
67
  "devDependencies": {
68
+ "tinyexec": "1.0.2",
71
69
  "vitest": "^4.0.18"
72
70
  }
73
71
  }
@@ -736,20 +736,31 @@ function inferFlowsFromDepGraph(depGraph) {
736
736
 
737
737
  function describeRoot(root) {
738
738
  const lower = root.toLowerCase().replace(/\/$/, "");
739
+ // Check the last segment for nested paths like src/core, src/analyzers
740
+ const lastSeg = lower.split("/").pop();
739
741
  if (/^src$|^lib$/.test(lower)) return "Application source code";
740
- if (/^test|^__test|^spec/.test(lower)) return "Test suites";
741
- if (/^doc/.test(lower)) return "Documentation";
742
- if (/^bin$|^scripts?$/.test(lower)) return "CLI entry points and scripts";
743
- if (/^config/.test(lower)) return "Configuration files";
744
- if (/^public$|^static$|^assets$/.test(lower)) return "Static assets";
745
- if (/^dist$|^build$|^out$/.test(lower)) return "Build output";
746
- if (/^\.github$/.test(lower)) return "GitHub Actions and workflows";
747
- if (/^api$/.test(lower)) return "API definitions";
748
- if (/^components?$/.test(lower)) return "Shared UI components";
749
- if (/^pages?$|^views?$|^screens?$/.test(lower)) return "Application pages/views";
750
- if (/^utils?$|^helpers?$/.test(lower)) return "Utility functions";
751
- if (/^services?$/.test(lower)) return "Service layer";
752
- if (/^hooks?$/.test(lower)) return "Custom hooks";
742
+ if (/^test|^__test|^spec/.test(lastSeg)) return "Test suites";
743
+ if (/^doc/.test(lastSeg)) return "Documentation";
744
+ if (/^bin$|^scripts?$/.test(lastSeg)) return "CLI entry points and scripts";
745
+ if (/^config/.test(lastSeg)) return "Configuration files";
746
+ if (/^public$|^static$|^assets$/.test(lastSeg)) return "Static assets";
747
+ if (/^dist$|^build$|^out$/.test(lastSeg)) return "Build output";
748
+ if (/^\.github$/.test(lastSeg)) return "GitHub Actions and workflows";
749
+ if (/^api$|^endpoint/.test(lastSeg)) return "API definitions";
750
+ if (/^components?$|^ui$/.test(lastSeg)) return "Shared UI components";
751
+ if (/^pages?$|^views?$|^screens?$/.test(lastSeg)) return "Application pages/views";
752
+ if (/^utils?$|^helpers?$/.test(lastSeg)) return "Utility functions";
753
+ if (/^services?$/.test(lastSeg)) return "Service layer";
754
+ if (/^hooks?$/.test(lastSeg)) return "Custom hooks";
755
+ if (/^core$|^kernel$|^engine$/.test(lastSeg)) return "Core logic and foundations";
756
+ if (/^analyz/.test(lastSeg)) return "Code analysis and detection";
757
+ if (/^render/.test(lastSeg)) return "Rendering and output formatting";
758
+ if (/^publish/.test(lastSeg)) return "Publishing and distribution";
759
+ if (/^deliver/.test(lastSeg)) return "Content delivery";
760
+ if (/^integrat/.test(lastSeg)) return "Third-party integrations";
761
+ if (/^plugin/.test(lastSeg)) return "Plugin and extension system";
762
+ if (/^ai$|^ml$|^llm$/.test(lastSeg)) return "AI/ML features and providers";
763
+ if (/^middleware/.test(lastSeg)) return "Middleware pipeline";
753
764
  return "Project files";
754
765
  }
755
766
 
@@ -97,12 +97,28 @@ export function buildAIContext(scanResult, config) {
97
97
  function inferModuleType(modulePath) {
98
98
  const lower = modulePath.toLowerCase();
99
99
 
100
- if (lower.includes("api")) return "api";
101
- if (lower.includes("component")) return "ui";
102
- if (lower.includes("lib") || lower.includes("util")) return "library";
100
+ if (lower.includes("test") || lower.includes("spec") || lower.includes("__test")) return "test";
101
+ if (lower.includes("api") || lower.includes("endpoint")) return "api";
102
+ if (lower.includes("component") || lower.includes("widget")) return "ui";
103
+ if (lower.includes("lib") || lower.includes("util") || lower.includes("helper") || lower.includes("common") || lower.includes("shared")) return "library";
103
104
  if (lower.includes("hook")) return "hooks";
104
- if (lower.includes("store") || lower.includes("state")) return "state";
105
- if (lower.includes("page") || lower.includes("route")) return "route";
105
+ if (lower.includes("store") || lower.includes("state") || lower.includes("redux") || lower.includes("zustand")) return "state";
106
+ if (lower.includes("page") || lower.includes("route") || lower.includes("view")) return "route";
107
+ if (lower.includes("config") || lower.includes("setting") || lower.includes("env")) return "config";
108
+ if (lower.includes("core") || lower.includes("kernel") || lower.includes("foundation")) return "core";
109
+ if (lower.includes("render") || lower.includes("template") || lower.includes("format")) return "renderer";
110
+ if (lower.includes("publish") || lower.includes("output") || lower.includes("export")) return "publisher";
111
+ if (lower.includes("analyz") || lower.includes("inspect") || lower.includes("detect")) return "analyzer";
112
+ if (lower.includes("plugin") || lower.includes("extension") || lower.includes("addon")) return "plugin";
113
+ if (lower.includes("deliver") || lower.includes("dispatch") || lower.includes("send")) return "delivery";
114
+ if (lower.includes("doc") || lower.includes("generate")) return "documentation";
115
+ if (lower.includes("integrat") || lower.includes("connect") || lower.includes("adapter")) return "integration";
116
+ if (lower.includes("cli") || lower.includes("command") || lower.includes("bin")) return "cli";
117
+ if (lower.includes("ai") || lower.includes("ml") || lower.includes("model") || lower.includes("prompt")) return "ai";
118
+ if (lower.includes("auth") || lower.includes("login") || lower.includes("session")) return "auth";
119
+ if (lower.includes("data") || lower.includes("db") || lower.includes("model") || lower.includes("schema")) return "data";
120
+ if (lower.includes("middleware")) return "middleware";
121
+ if (lower.includes("service")) return "service";
106
122
  if (lower.includes("app")) return "app";
107
123
 
108
124
  return "other";
@@ -110,21 +126,31 @@ function inferModuleType(modulePath) {
110
126
 
111
127
  function inferArchitecturalPatterns(modules) {
112
128
  const patterns = [];
129
+ const keys = modules.map(m => m.key.toLowerCase());
113
130
 
114
- const hasAppRouter = modules.some(m => m.key.includes("app/"));
115
- const hasPagesRouter = modules.some(m => m.key.includes("pages/"));
116
- const hasComponents = modules.some(m => m.key.includes("component"));
117
- const hasLib = modules.some(m => m.key.includes("lib"));
118
- const hasHooks = modules.some(m => m.key.includes("hook"));
119
- const hasStore = modules.some(m => m.key.includes("store") || m.key.includes("state"));
120
- const hasApi = modules.some(m => m.key.includes("api"));
121
-
122
- if (hasAppRouter) patterns.push("Next.js App Router");
123
- if (hasPagesRouter) patterns.push("Next.js Pages Router");
124
- if (hasComponents && hasLib) patterns.push("Layered component architecture");
125
- if (hasHooks) patterns.push("React hooks pattern");
126
- if (hasStore) patterns.push("Centralized state management");
127
- if (hasApi) patterns.push("API route pattern");
131
+ const has = (keyword) => keys.some(k => k.includes(keyword));
132
+
133
+ // Web framework patterns
134
+ if (has("app/")) patterns.push("Next.js App Router");
135
+ if (has("pages/")) patterns.push("Next.js Pages Router");
136
+ if (has("component") && has("lib")) patterns.push("Layered component architecture");
137
+ if (has("hook")) patterns.push("React hooks pattern");
138
+ if (has("store") || has("state") || has("redux") || has("zustand")) patterns.push("Centralized state management");
139
+
140
+ // General patterns
141
+ if (has("api") || has("endpoint")) patterns.push("API route pattern");
142
+ if (has("core") || has("kernel")) patterns.push("Core/kernel architecture");
143
+ if (has("plugin") || has("extension")) patterns.push("Plugin system");
144
+ if (has("middleware")) patterns.push("Middleware pipeline");
145
+ if (has("render") || has("template")) patterns.push("Renderer pipeline");
146
+ if (has("publish") || has("output")) patterns.push("Multi-output publishing");
147
+ if (has("analyz") || has("detect") || has("inspect")) patterns.push("Analysis pipeline");
148
+ if (has("cli") || has("command") || has("bin")) patterns.push("CLI tool architecture");
149
+ if (has("util") || has("helper") || has("lib")) patterns.push("Shared utility layer");
150
+ if (has("integrat") || has("adapter") || has("connect")) patterns.push("Integration adapters");
151
+ if (has("ai") || has("prompt") || has("provider")) patterns.push("AI/LLM integration");
152
+ if (has("deliver") || has("dispatch")) patterns.push("Delivery pipeline");
153
+ if (has("test") || has("spec")) patterns.push("Dedicated test infrastructure");
128
154
 
129
155
  return patterns;
130
156
  }
@@ -32,7 +32,7 @@ const DEFAULT_DOMAIN_HINTS = [
32
32
  description: "Payment processing and subscription management"
33
33
  },
34
34
  {
35
- match: ["api", "endpoint", "route", "handler", "controller", "middleware"],
35
+ match: ["api", "endpoint", "handler", "controller", "middleware"],
36
36
  domain: "API Layer",
37
37
  description: "Backend API endpoints and request handling"
38
38
  },
@@ -75,6 +75,61 @@ const DEFAULT_DOMAIN_HINTS = [
75
75
  match: ["job", "queue", "worker", "cron", "task", "scheduler", "background"],
76
76
  domain: "Background Jobs",
77
77
  description: "Background processing, job queues, and scheduled tasks"
78
+ },
79
+ {
80
+ match: ["core", "kernel", "foundation", "engine"],
81
+ domain: "Core Engine",
82
+ description: "Core business logic and foundational modules"
83
+ },
84
+ {
85
+ match: ["render", "template", "format", "output"],
86
+ domain: "Rendering & Output",
87
+ description: "Content rendering, formatting, and output generation"
88
+ },
89
+ {
90
+ match: ["publish", "deploy", "release", "distribute"],
91
+ domain: "Publishing & Delivery",
92
+ description: "Content and artifact publishing, deployment, and distribution"
93
+ },
94
+ {
95
+ match: ["analyz", "inspect", "detect", "lint", "scan", "parse"],
96
+ domain: "Analysis & Detection",
97
+ description: "Code analysis, pattern detection, and static inspection"
98
+ },
99
+ {
100
+ match: ["plugin", "extension", "addon", "module"],
101
+ domain: "Plugin System",
102
+ description: "Extensibility framework, plugins, and add-ons"
103
+ },
104
+ {
105
+ match: ["deliver", "dispatch", "send", "transport"],
106
+ domain: "Delivery",
107
+ description: "Content delivery and distribution channels"
108
+ },
109
+ {
110
+ match: ["doc", "generate", "markdown", "readme"],
111
+ domain: "Documentation",
112
+ description: "Documentation generation and management"
113
+ },
114
+ {
115
+ match: ["integrat", "connect", "adapter", "bridge", "gateway"],
116
+ domain: "Integrations",
117
+ description: "Third-party service integrations and adapters"
118
+ },
119
+ {
120
+ match: ["cli", "command", "bin", "terminal", "shell", "prompt"],
121
+ domain: "CLI & Commands",
122
+ description: "Command-line interface and terminal commands"
123
+ },
124
+ {
125
+ match: ["ai", "ml", "llm", "gpt", "openai", "anthropic", "gemini"],
126
+ domain: "AI & Machine Learning",
127
+ description: "AI/ML integration, LLM providers, and intelligent features"
128
+ },
129
+ {
130
+ match: ["service", "provider", "client", "sdk"],
131
+ domain: "Services",
132
+ description: "Service layer, providers, and external client SDKs"
78
133
  }
79
134
  ];
80
135
 
package/src/core/scan.js CHANGED
@@ -29,13 +29,22 @@ function isExpressRoute(content) {
29
29
  }
30
30
 
31
31
  function isReactRouterFile(content) {
32
- // Detect React Router patterns
33
- return content.includes("<Route") || content.includes("createBrowserRouter") || content.includes("createRoutesFromElements");
32
+ // Detect React Router patterns — require import evidence, not just string mentions
33
+ const hasImport = /import\s+.*?from\s+['"]react-router/.test(content)
34
+ || /require\s*\(\s*['"]react-router/.test(content);
35
+ const hasJSX = /<Route\s/.test(content);
36
+ const hasFactory = /createBrowserRouter\s*\(/.test(content)
37
+ || /createRoutesFromElements\s*\(/.test(content);
38
+ return hasImport || hasJSX || hasFactory;
34
39
  }
35
40
 
36
41
  function isVueRouterFile(content) {
37
- // Detect Vue Router patterns
38
- return (content.includes("createRouter") || content.includes("VueRouter")) && content.includes("routes");
42
+ // Detect Vue Router patterns — require import evidence, not just string mentions
43
+ const hasImport = /import\s+.*?from\s+['"]vue-router/.test(content)
44
+ || /require\s*\(\s*['"]vue-router/.test(content);
45
+ const hasConstructor = /new\s+VueRouter\s*\(/.test(content);
46
+ const hasFactory = /createRouter\s*\(/.test(content) && hasImport;
47
+ return hasImport || hasConstructor || hasFactory;
39
48
  }
40
49
 
41
50
  function isNextPage(file) {
@@ -159,6 +168,10 @@ async function extractRepoMetadata(repoRoot) {
159
168
  if (allDeps["nestjs"] || allDeps["@nestjs/core"]) metadata.frameworks.push("NestJS");
160
169
  if (allDeps["svelte"]) metadata.frameworks.push("Svelte");
161
170
  if (allDeps["solid-js"]) metadata.frameworks.push("Solid");
171
+ if (allDeps["hono"]) metadata.frameworks.push("Hono");
172
+ if (allDeps["koa"]) metadata.frameworks.push("Koa");
173
+ if (allDeps["hapi"] || allDeps["@hapi/hapi"]) metadata.frameworks.push("Hapi");
174
+ if (allDeps["electron"]) metadata.frameworks.push("Electron");
162
175
 
163
176
  // Detect test frameworks
164
177
  if (allDeps["vitest"]) metadata.testFrameworks.push("Vitest");
@@ -166,6 +179,7 @@ async function extractRepoMetadata(repoRoot) {
166
179
  if (allDeps["mocha"]) metadata.testFrameworks.push("Mocha");
167
180
  if (allDeps["playwright"]) metadata.testFrameworks.push("Playwright");
168
181
  if (allDeps["cypress"]) metadata.testFrameworks.push("Cypress");
182
+ if (allDeps["ava"]) metadata.testFrameworks.push("Ava");
169
183
 
170
184
  // Detect build tools
171
185
  if (allDeps["vite"]) metadata.buildTools.push("Vite");
@@ -173,9 +187,25 @@ async function extractRepoMetadata(repoRoot) {
173
187
  if (allDeps["rollup"]) metadata.buildTools.push("Rollup");
174
188
  if (allDeps["esbuild"]) metadata.buildTools.push("esbuild");
175
189
  if (allDeps["turbo"]) metadata.buildTools.push("Turborepo");
190
+ if (allDeps["tsup"]) metadata.buildTools.push("tsup");
191
+ if (allDeps["swc"] || allDeps["@swc/core"]) metadata.buildTools.push("SWC");
192
+ if (allDeps["parcel"]) metadata.buildTools.push("Parcel");
176
193
 
177
- // Detect TypeScript
194
+ // Detect languages
178
195
  if (allDeps["typescript"]) metadata.languages.add("TypeScript");
196
+
197
+ // Infer JavaScript if package.json exists (any npm project uses JS/Node)
198
+ metadata.languages.add("JavaScript");
199
+
200
+ // Detect Node.js runtime indicators
201
+ const hasNodeEngines = pkg.engines && pkg.engines.node;
202
+ const hasBin = pkg.bin != null;
203
+ const hasNodeDeps = allDeps["node-fetch"] || allDeps["fs-extra"] || allDeps["dotenv"]
204
+ || allDeps["commander"] || allDeps["yargs"] || allDeps["chalk"]
205
+ || allDeps["inquirer"] || allDeps["ora"] || allDeps["execa"];
206
+ if (hasNodeEngines || hasBin || hasNodeDeps || pkg.type === "module") {
207
+ metadata.languages.add("Node.js");
208
+ }
179
209
  } catch {
180
210
  // No package.json or invalid JSON
181
211
  }
@@ -241,30 +271,29 @@ function extractExpressRoutes(content) {
241
271
 
242
272
  function extractReactRoutes(content, file) {
243
273
  const routes = [];
274
+ const lines = content.split("\n");
244
275
 
245
276
  // Match <Route path="..." />
246
277
  const routePattern = /<Route\s+[^>]*path\s*=\s*['"`]([^'"`]+)['"`][^>]*\/?>/gi;
247
278
  let match;
248
279
 
249
280
  while ((match = routePattern.exec(content)) !== null) {
250
- const [, path] = match;
251
- routes.push({
252
- file,
253
- path,
254
- framework: "React Router"
255
- });
281
+ const [, routePath] = match;
282
+ if (isValidRoutePath(routePath)) {
283
+ routes.push({ file, path: routePath, framework: "React Router" });
284
+ }
256
285
  }
257
286
 
258
- // Match path: "..." in route objects
259
- const objectPattern = /path\s*:\s*['"`]([^'"`]+)['"`]/gi;
260
- while ((match = objectPattern.exec(content)) !== null) {
261
- const [, path] = match;
262
- if (!routes.some(r => r.path === path)) {
263
- routes.push({
264
- file,
265
- path,
266
- framework: "React Router"
267
- });
287
+ // Match path: "..." in route objects (skip comment lines)
288
+ for (const line of lines) {
289
+ const trimmed = line.trim();
290
+ if (trimmed.startsWith("//") || trimmed.startsWith("*") || trimmed.startsWith("/*")) continue;
291
+ const objectMatch = /path\s*:\s*['"`]([^'"`]+)['"`]/i.exec(trimmed);
292
+ if (objectMatch) {
293
+ const routePath = objectMatch[1];
294
+ if (isValidRoutePath(routePath) && !routes.some(r => r.path === routePath)) {
295
+ routes.push({ file, path: routePath, framework: "React Router" });
296
+ }
268
297
  }
269
298
  }
270
299
 
@@ -273,23 +302,31 @@ function extractReactRoutes(content, file) {
273
302
 
274
303
  function extractVueRoutes(content, file) {
275
304
  const routes = [];
276
-
277
- // Match path: '...' or path: "..." in Vue router definitions
278
- const pathPattern = /path\s*:\s*['"`]([^'"`]+)['"`]/gi;
279
- let match;
280
-
281
- while ((match = pathPattern.exec(content)) !== null) {
282
- const [, path] = match;
283
- routes.push({
284
- file,
285
- path,
286
- framework: "Vue Router"
287
- });
305
+ const lines = content.split("\n");
306
+
307
+ // Match path: '...' or path: "..." in Vue router definitions (skip comment lines)
308
+ for (const line of lines) {
309
+ const trimmed = line.trim();
310
+ if (trimmed.startsWith("//") || trimmed.startsWith("*") || trimmed.startsWith("/*")) continue;
311
+ const match = /path\s*:\s*['"`]([^'"`]+)['"`]/i.exec(trimmed);
312
+ if (match) {
313
+ const routePath = match[1];
314
+ if (isValidRoutePath(routePath) && !routes.some(r => r.path === routePath)) {
315
+ routes.push({ file, path: routePath, framework: "Vue Router" });
316
+ }
317
+ }
288
318
  }
289
319
 
290
320
  return routes;
291
321
  }
292
322
 
323
+ function isValidRoutePath(p) {
324
+ // Filter out placeholder/documentation strings and non-path values
325
+ if (!p || p === "..." || p === "*" || p.length > 200) return false;
326
+ // Must look like a URL path (starts with / or is a relative segment)
327
+ return p.startsWith("/") || /^[a-zA-Z0-9]/.test(p);
328
+ }
329
+
293
330
  export async function scanRepo(cfg) {
294
331
  const repoRoot = cfg.__repoRoot;
295
332
 
@@ -208,7 +208,7 @@ async function generateDocument(docPlan, context) {
208
208
  return renderApiSurfaceOriginal(config, scanResult);
209
209
 
210
210
  case "data_flows":
211
- return await generateDataFlows(flows, aiContext, { depGraph, scanResult: hookScanResult });
211
+ return await generateDataFlows(flows, aiContext, { depGraph, scanResult });
212
212
 
213
213
  case "arch_diff":
214
214
  if (!diffData) {
@@ -90,15 +90,23 @@ function buildModuleGraph(modules, depGraph) {
90
90
 
91
91
  /**
92
92
  * Find which module a file belongs to.
93
+ * Edge keys from the dep graph are extensionless (e.g. "src/core/config")
94
+ * while module keys may include extensions (e.g. "src/core/config.js").
95
+ * We try both direct match and extension-stripped match.
93
96
  */
94
97
  function findModuleForFile(fileKey, modules) {
95
98
  const normalized = fileKey.replace(/\\/g, "/");
96
- // Find the most specific (longest) matching module key
97
99
  let bestMatch = null;
98
100
  for (const mod of modules) {
99
- if (normalized.startsWith(mod.key) || normalized.startsWith(mod.key + "/")) {
100
- if (!bestMatch || mod.key.length > bestMatch.length) {
101
- bestMatch = mod.key;
101
+ const modKey = mod.key;
102
+ // Direct match (with or without extension)
103
+ if (normalized === modKey || normalized === modKey.replace(/\.[^/.]+$/, "")) {
104
+ return modKey;
105
+ }
106
+ // Prefix match: file is inside this module
107
+ if (normalized.startsWith(modKey + "/") || normalized.startsWith(modKey.replace(/\.[^/.]+$/, "") + "/")) {
108
+ if (!bestMatch || modKey.length > bestMatch.length) {
109
+ bestMatch = modKey;
102
110
  }
103
111
  }
104
112
  }
@@ -107,13 +115,25 @@ function findModuleForFile(fileKey, modules) {
107
115
 
108
116
  function categorizeModule(key) {
109
117
  const normalized = key.toLowerCase();
110
- if (normalized.includes("core")) return "core";
111
- if (normalized.includes("publisher")) return "integration";
112
- if (normalized.includes("renderer")) return "business";
113
- if (normalized.includes("delivery")) return "integration";
114
- if (normalized.includes("util")) return "util";
115
- if (normalized.includes("test")) return "test";
116
- if (normalized.includes("bin")) return "cli";
118
+ if (normalized.includes("test") || normalized.includes("spec")) return "test";
119
+ if (normalized.includes("core") || normalized.includes("kernel")) return "core";
120
+ if (normalized.includes("analyz") || normalized.includes("detect") || normalized.includes("inspect")) return "analyzer";
121
+ if (normalized.includes("render") || normalized.includes("format") || normalized.includes("template")) return "renderer";
122
+ if (normalized.includes("publish") || normalized.includes("output")) return "publisher";
123
+ if (normalized.includes("deliver") || normalized.includes("dispatch")) return "delivery";
124
+ if (normalized.includes("integrat") || normalized.includes("connect") || normalized.includes("adapter")) return "integration";
125
+ if (normalized.includes("util") || normalized.includes("helper") || normalized.includes("lib") || normalized.includes("common")) return "util";
126
+ if (normalized.includes("ai") || normalized.includes("llm") || normalized.includes("prompt")) return "ai";
127
+ if (normalized.includes("doc") || normalized.includes("generate")) return "docs";
128
+ if (normalized.includes("plugin") || normalized.includes("extension")) return "plugin";
129
+ if (normalized.includes("config") || normalized.includes("setting")) return "config";
130
+ if (normalized.includes("cli") || normalized.includes("bin") || normalized.includes("command")) return "cli";
131
+ if (normalized.includes("api") || normalized.includes("endpoint")) return "api";
132
+ if (normalized.includes("component") || normalized.includes("ui")) return "ui";
133
+ if (normalized.includes("page") || normalized.includes("route") || normalized.includes("view")) return "page";
134
+ if (normalized.includes("store") || normalized.includes("state")) return "state";
135
+ if (normalized.includes("middleware")) return "middleware";
136
+ if (normalized.includes("service")) return "service";
117
137
  return "other";
118
138
  }
119
139
 
@@ -122,8 +142,21 @@ function generateUnicodeArchitectureDiagram(nodes, relationships) {
122
142
  const categories = {
123
143
  cli: { icon: "🎯", label: "CLI Entry", nodes: [] },
124
144
  core: { icon: "⚙️", label: "Core Logic", nodes: [] },
125
- business: { icon: "📋", label: "Business Logic", nodes: [] },
145
+ config: { icon: "🔧", label: "Configuration", nodes: [] },
146
+ analyzer: { icon: "🔍", label: "Analysis", nodes: [] },
147
+ ai: { icon: "🤖", label: "AI / ML", nodes: [] },
148
+ docs: { icon: "📝", label: "Documentation", nodes: [] },
149
+ renderer: { icon: "📋", label: "Rendering", nodes: [] },
150
+ publisher: { icon: "📤", label: "Publishing", nodes: [] },
151
+ delivery: { icon: "📬", label: "Delivery", nodes: [] },
126
152
  integration: { icon: "🔌", label: "Integration", nodes: [] },
153
+ plugin: { icon: "🧩", label: "Plugins", nodes: [] },
154
+ api: { icon: "🌐", label: "API Layer", nodes: [] },
155
+ ui: { icon: "🖼️", label: "UI Components", nodes: [] },
156
+ page: { icon: "📄", label: "Pages / Routes", nodes: [] },
157
+ state: { icon: "💾", label: "State Management", nodes: [] },
158
+ middleware: { icon: "🔀", label: "Middleware", nodes: [] },
159
+ service: { icon: "⚡", label: "Services", nodes: [] },
127
160
  util: { icon: "🛠️", label: "Utilities", nodes: [] },
128
161
  test: { icon: "✅", label: "Testing", nodes: [] },
129
162
  other: { icon: "📦", label: "Other", nodes: [] }
@@ -235,5 +268,53 @@ export function renderSystemMap(scan, config, depGraph) {
235
268
  ""
236
269
  ];
237
270
 
271
+ // Add key connections summary when we have relationships
272
+ if (relationships.length > 0) {
273
+ lines.push("---", "", "## Key Connections", "");
274
+
275
+ // Find most-depended-on modules (highest in-degree)
276
+ const inDegree = new Map();
277
+ const outDegree = new Map();
278
+ for (const rel of relationships) {
279
+ inDegree.set(rel.to, (inDegree.get(rel.to) || 0) + (rel.weight || 1));
280
+ outDegree.set(rel.from, (outDegree.get(rel.from) || 0) + (rel.weight || 1));
281
+ }
282
+
283
+ const topDeps = [...inDegree.entries()]
284
+ .sort((a, b) => b[1] - a[1])
285
+ .slice(0, 5);
286
+
287
+ if (topDeps.length > 0) {
288
+ lines.push("**Most depended-on modules** (highest import count):", "");
289
+ lines.push("| Module | Imported by |");
290
+ lines.push("|--------|------------|");
291
+ for (const [nodeId, count] of topDeps) {
292
+ const node = nodes.find(n => n.id === nodeId);
293
+ if (node) {
294
+ lines.push(`| \`${node.label}\` | ${count} module${count !== 1 ? "s" : ""} |`);
295
+ }
296
+ }
297
+ lines.push("");
298
+ }
299
+
300
+ // Find modules with highest out-degree (most dependencies)
301
+ const topConsumers = [...outDegree.entries()]
302
+ .sort((a, b) => b[1] - a[1])
303
+ .slice(0, 5);
304
+
305
+ if (topConsumers.length > 0) {
306
+ lines.push("**Most dependent modules** (highest dependency count):", "");
307
+ lines.push("| Module | Depends on |");
308
+ lines.push("|--------|-----------|");
309
+ for (const [nodeId, count] of topConsumers) {
310
+ const node = nodes.find(n => n.id === nodeId);
311
+ if (node) {
312
+ lines.push(`| \`${node.label}\` | ${count} module${count !== 1 ? "s" : ""} |`);
313
+ }
314
+ }
315
+ lines.push("");
316
+ }
317
+ }
318
+
238
319
  return lines.join("\n");
239
320
  }