@chappibunny/repolens 1.9.2 → 1.9.4

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.
@@ -9,9 +9,6 @@ import {
9
9
  createArchitectureOverviewPrompt,
10
10
  createDataFlowsPrompt,
11
11
  createDeveloperOnboardingPrompt,
12
- createModuleSummaryPrompt,
13
- createRouteSummaryPrompt,
14
- createAPIDocumentationPrompt,
15
12
  AI_SCHEMAS,
16
13
  renderStructuredToMarkdown,
17
14
  } from "./prompts.js";
@@ -83,7 +80,14 @@ async function generateWithStructuredFallback(key, promptText, maxTokens, fallba
83
80
  return fallbackFn();
84
81
  }
85
82
 
86
- return sanitizeAIOutput(result.text);
83
+ // Guard against empty AI responses — fall back to deterministic
84
+ const text = sanitizeAIOutput(result.text);
85
+ if (!text || text.trim().length === 0) {
86
+ warn("AI returned empty response, using fallback");
87
+ return fallbackFn();
88
+ }
89
+
90
+ return text;
87
91
  }
88
92
 
89
93
  export async function generateExecutiveSummary(context, enrichment = {}, config) {
@@ -152,208 +156,387 @@ function getFallbackExecutiveSummary(context, enrichment = {}) {
152
156
  const { depGraph, flows } = enrichment;
153
157
  const frameworkList = context.techStack.frameworks.join(", ") || "general-purpose";
154
158
  const languageList = context.techStack.languages.join(", ") || "multiple languages";
155
- const domainSummary = context.domains.slice(0, 5).map(d => d.name).join(", ");
156
159
  const testFrameworks = context.techStack.testFrameworks || [];
157
160
  const isCLI = (context.patterns || []).some(p => p.toLowerCase().includes("cli"));
161
+ const projectName = context.project.name || "This system";
158
162
 
159
- // Build the "what it does" line based on project type
160
- let interfaceLine;
161
- if (isCLI) {
162
- interfaceLine = "It operates as a **command-line tool**, interacting through terminal commands rather than a web interface.";
163
- } else {
164
- const parts = [];
165
- if (context.project.apiRoutesDetected > 0) parts.push(`exposes **${context.project.apiRoutesDetected} API endpoint${context.project.apiRoutesDetected === 1 ? "" : "s"}**`);
166
- if (context.project.pagesDetected > 0) parts.push(`serves **${context.project.pagesDetected} application page${context.project.pagesDetected === 1 ? "" : "s"}** to end users`);
167
- interfaceLine = parts.length > 0 ? `It ${parts.join(" and ")}.` : "";
168
- }
163
+ // Calculate maturity indicators
164
+ const hasTests = testFrameworks.length > 0;
165
+ const hasBuildTools = context.techStack.buildTools.length > 0;
166
+ const hasCleanDeps = depGraph?.stats?.cycles === 0;
167
+ const maturityScore = [hasTests, hasBuildTools, hasCleanDeps].filter(Boolean).length;
168
+ const maturityLabel = maturityScore >= 3 ? "Production-grade" : maturityScore >= 2 ? "Well-structured" : "Early-stage";
169
+
170
+ // Determine project type for better descriptions
171
+ const projectType = isCLI ? "command-line tool" :
172
+ (context.project.apiRoutesDetected > 0 && context.project.pagesDetected > 0) ? "full-stack application" :
173
+ context.project.apiRoutesDetected > 0 ? "API service" :
174
+ context.project.pagesDetected > 0 ? "web application" : "software system";
169
175
 
170
176
  let output = `# Executive Summary
171
177
 
172
- ## What This System Does
178
+ ## At a Glance
179
+
180
+ | Aspect | Summary |
181
+ |--------|---------|
182
+ | **What** | ${projectName} is a ${maturityLabel.toLowerCase()} ${projectType} |
183
+ | **Built With** | ${frameworkList} + ${languageList} |
184
+ | **Scale** | ${context.project.modulesDetected} modules across ${context.project.filesScanned} files |
185
+ | **Maturity** | ${maturityLabel} (${maturityScore}/3 indicators) |
173
186
 
174
- ${context.project.name} is a ${frameworkList} application built with ${languageList}. The codebase contains **${context.project.modulesDetected} modules** spread across **${context.project.filesScanned} files**, organized into ${context.domains.length} functional domain${context.domains.length === 1 ? "" : "s"}.
187
+ ---
175
188
 
176
- ${interfaceLine}
189
+ ## What This System Does
177
190
 
178
- ## Primary Functional Areas
191
+ **${projectName}** is a ${frameworkList} ${projectType} built with ${languageList}. `;
179
192
 
180
- The application is organized around the following business domains:
193
+ if (isCLI) {
194
+ output += `It operates as a **command-line interface**, enabling users to interact with the system through terminal commands. This architecture is optimized for automation, scripting, and integration into CI/CD pipelines.`;
195
+ } else if (context.project.apiRoutesDetected > 0 && context.project.pagesDetected > 0) {
196
+ output += `It provides both a **user-facing interface** (${context.project.pagesDetected} pages) and a **programmatic API** (${context.project.apiRoutesDetected} endpoints), serving as a complete solution for end users and system integrations alike.`;
197
+ } else if (context.project.apiRoutesDetected > 0) {
198
+ output += `It exposes **${context.project.apiRoutesDetected} API endpoint${context.project.apiRoutesDetected === 1 ? "" : "s"}**, designed for programmatic access by client applications, mobile apps, or external services.`;
199
+ } else if (context.project.pagesDetected > 0) {
200
+ output += `It delivers **${context.project.pagesDetected} application page${context.project.pagesDetected === 1 ? "" : "s"}** to end users through a web interface.`;
201
+ } else {
202
+ output += `The codebase contains **${context.project.modulesDetected} modules** organized into ${context.domains.length} functional domain${context.domains.length === 1 ? "" : "s"}.`;
203
+ }
181
204
 
182
- | Domain | Modules | Description |
183
- |--------|---------|-------------|
184
- ${context.domains.map(d => `| ${d.name} | ${d.moduleCount} | ${d.description || "—"} |`).join("\n")}
205
+ output += `\n\n## Business Capabilities
185
206
 
186
- ## Technology Profile
207
+ The system is organized around **${context.domains.length} functional domains**, each representing a distinct business capability:\n\n`;
187
208
 
188
- | Category | Details |
189
- |----------|---------|
190
- | Frameworks | ${context.techStack.frameworks.join(", ") || (isCLI ? "N/A (CLI tool)" : "Not detected")} |
191
- | Languages | ${context.techStack.languages.join(", ") || "Not detected"} |
192
- | Build Tools | ${context.techStack.buildTools.join(", ") || (isCLI ? "N/A (CLI tool)" : "Not detected")} |
193
- ${testFrameworks.length > 0 ? `| Test Frameworks | ${testFrameworks.join(", ")} |\n` : ""}`;
209
+ // Premium domain table with better descriptions
210
+ const topDomains = context.domains.slice(0, 6);
211
+ output += `| Domain | Capability | Scale |\n`;
212
+ output += `|--------|------------|-------|\n`;
213
+ for (const d of topDomains) {
214
+ const desc = d.description || inferDomainCapability(d.name);
215
+ output += `| **${d.name}** | ${desc} | ${d.moduleCount} modules, ${d.fileCount} files |\n`;
216
+ }
217
+ if (context.domains.length > 6) {
218
+ output += `| *+${context.domains.length - 6} more* | Additional functional areas | — |\n`;
219
+ }
194
220
 
195
- // Module type breakdown
221
+ output += `\n## Technical Foundation\n\n`;
222
+ output += `| Layer | Technologies | Purpose |\n`;
223
+ output += `|-------|--------------|----------|\n`;
224
+ if (context.techStack.frameworks.length > 0) {
225
+ output += `| **Application** | ${context.techStack.frameworks.join(", ")} | Core runtime framework |\n`;
226
+ }
227
+ output += `| **Language** | ${context.techStack.languages.join(", ") || "Not detected"} | Primary development language |\n`;
228
+ if (context.techStack.buildTools.length > 0) {
229
+ output += `| **Build** | ${context.techStack.buildTools.join(", ")} | Compilation and bundling |\n`;
230
+ }
231
+ if (testFrameworks.length > 0) {
232
+ output += `| **Testing** | ${testFrameworks.join(", ")} | Quality assurance |\n`;
233
+ }
234
+
235
+ // Module composition breakdown
196
236
  const typeGroups = groupModulesByType(context.topModules);
197
237
  if (Object.keys(typeGroups).length > 1) {
198
238
  output += `\n## Module Composition\n\n`;
199
- output += `| Layer | Modules | Examples |\n`;
200
- output += `|-------|---------|----------|\n`;
239
+ output += `The codebase follows a **layered architecture** with clear separation of concerns:\n\n`;
240
+ output += `| Layer | Count | Key Modules | Responsibility |\n`;
241
+ output += `|-------|-------|-------------|----------------|\n`;
201
242
  for (const [type, mods] of Object.entries(typeGroups)) {
202
- const examples = mods.slice(0, 3).map(m => `\`${m.key}\``).join(", ");
203
- output += `| ${formatModuleType(type)} | ${mods.length} | ${examples} |\n`;
243
+ const examples = mods.slice(0, 2).map(m => `\`${m.key.split("/").pop()}\``).join(", ");
244
+ const responsibility = getLayerResponsibility(type);
245
+ output += `| **${formatModuleType(type)}** | ${mods.length} | ${examples} | ${responsibility} |\n`;
204
246
  }
205
247
  }
206
248
 
207
- // Monorepo info
249
+ // Monorepo structure
208
250
  if (context.monorepo) {
209
251
  output += `\n## Monorepo Structure\n\n`;
210
- output += `This is a **${context.monorepo.tool}** monorepo containing **${context.monorepo.packageCount} package${context.monorepo.packageCount === 1 ? "" : "s"}**:\n\n`;
211
- output += context.monorepo.packages.slice(0, 10).map(p => `- \`${p.name}\` (${p.path})`).join("\n");
212
- output += "\n";
252
+ output += `This is a **${context.monorepo.tool}** monorepo containing **${context.monorepo.packageCount} packages**. This structure enables:\n\n`;
253
+ output += `- Independent versioning and deployment of packages\n`;
254
+ output += `- Shared code reuse across internal projects\n`;
255
+ output += `- Coordinated development with clear ownership boundaries\n\n`;
256
+ output += `**Key packages:**\n\n`;
257
+ for (const p of context.monorepo.packages.slice(0, 6)) {
258
+ output += `- \`${p.name}\` — ${p.path}\n`;
259
+ }
213
260
  }
214
261
 
215
- // Dependency health
262
+ // Health assessment section
263
+ output += `\n## Codebase Health\n\n`;
264
+
216
265
  if (depGraph?.stats) {
217
266
  const stats = depGraph.stats;
218
- output += `\n## Codebase Health\n\n`;
219
- output += `| Metric | Value |\n`;
220
- output += `|--------|-------|\n`;
221
- output += `| Internal imports | ${stats.totalEdges} |\n`;
222
- output += `| External dependencies | ${stats.externalDeps} |\n`;
223
- output += `| Circular dependencies | ${stats.cycles} |\n`;
224
- output += `| Orphan files | ${stats.orphanFiles} |\n`;
267
+ const healthScore = calculateHealthScore(stats, context);
268
+ const healthEmoji = healthScore >= 80 ? "🟢" : healthScore >= 60 ? "🟡" : "🔴";
269
+
270
+ output += `**Overall Health: ${healthEmoji} ${healthScore}/100**\n\n`;
271
+ output += `| Metric | Value | Status |\n`;
272
+ output += `|--------|-------|--------|\n`;
273
+ output += `| Internal imports | ${stats.totalEdges} edges | ${stats.totalEdges > 0 ? "✅ Connected" : "⚠️ Isolated"} |\n`;
274
+ output += `| External Packages | ${stats.externalDeps} | ${stats.externalDeps < 50 ? "✅ Manageable" : stats.externalDeps < 100 ? "⚠️ Consider audit" : "🔴 High dependency count"} |\n`;
275
+ output += `| Circular Dependencies | ${stats.cycles === 0 ? "cycle-free" : stats.cycles} | ${stats.cycles === 0 ? "✅ None" : `🔴 ${stats.cycles} detected`} |\n`;
276
+ output += `| Orphan Files | ${stats.orphanFiles} | ${stats.orphanFiles === 0 ? "✅ All connected" : `⚠️ ${stats.orphanFiles} unused`} |\n`;
277
+
278
+ // Key insights
279
+ output += `\n### Key Insights\n\n`;
225
280
  if (stats.cycles === 0) {
226
- output += `\nThe dependency graph is **cycle-free**, indicating clean module boundaries.\n`;
281
+ output += `- **Clean module boundaries** No circular dependencies detected, enabling safe refactoring and testing.\n`;
227
282
  } else {
228
- output += `\n⚠️ **${stats.cycles} circular dependenc${stats.cycles === 1 ? "y" : "ies"}** detected these may complicate refactoring and testing.\n`;
283
+ output += `- 🔴 **Architectural concern** — ${stats.cycles} circular dependenc${stats.cycles === 1 ? "y" : "ies"} detected. These create tight coupling and make testing difficult.\n`;
284
+ }
285
+ if (stats.hubs && stats.hubs.length > 0) {
286
+ const topHub = stats.hubs[0];
287
+ if (topHub.importedBy > stats.totalFiles * 0.3) {
288
+ output += `- ⚠️ **Coupling hotspot** — \`${topHub.key}\` is imported by ${topHub.importedBy} files (${Math.round(topHub.importedBy / stats.totalFiles * 100)}% of codebase). Consider splitting.\n`;
289
+ } else {
290
+ output += `- ✅ **Healthy coupling** — Most-imported module (\`${topHub.key}\`) serves ${topHub.importedBy} files without excessive centralization.\n`;
291
+ }
292
+ }
293
+ if (stats.orphanFiles > stats.totalFiles * 0.1) {
294
+ output += `- ⚠️ **Potential dead code** — ${stats.orphanFiles} files (${Math.round(stats.orphanFiles / stats.totalFiles * 100)}%) are not imported anywhere.\n`;
229
295
  }
230
296
  }
231
297
 
232
- // Data flows (filter out test file flows for exec summary)
298
+ // Data flows summary
233
299
  const summaryFlows = (flows || []).filter(f => !f.name?.toLowerCase().includes('test'));
234
300
  if (summaryFlows.length > 0) {
235
301
  output += `\n## Key Data Flows\n\n`;
236
- output += `${summaryFlows.length} data flow${summaryFlows.length === 1 ? "" : "s"} identified:\n\n`;
237
- for (const flow of summaryFlows) {
238
- output += `- **${flow.name}**${flow.critical ? " (critical)" : ""} — ${flow.description}\n`;
302
+ output += `The system processes information through **${summaryFlows.length} identified flow${summaryFlows.length === 1 ? "" : "s"}**:\n\n`;
303
+ for (const flow of summaryFlows.slice(0, 5)) {
304
+ const criticality = flow.critical ? " (critical)" : "";
305
+ output += `- **${flow.name}**${criticality} — ${flow.description}\n`;
239
306
  }
240
307
  }
241
308
 
242
- output += `\n## Key Observations
243
-
244
- The codebase follows ${context.patterns.length > 0 ? context.patterns.join(", ") : "standard"} architectural patterns. ${domainSummary ? `The core functional areas — ${domainSummary} — account for the majority of the application logic.` : ""}
309
+ // Strategic observations
310
+ output += `\n## Strategic Observations\n\n`;
311
+
312
+ const observations = [];
313
+ if (context.patterns.length > 0) {
314
+ observations.push(`The codebase follows **${context.patterns.join(", ")}** architectural patterns, providing a familiar structure for developers.`);
315
+ }
316
+ if (context.domains.length >= 5) {
317
+ observations.push(`With ${context.domains.length} distinct domains, the system has clear separation of concerns suitable for team-based development.`);
318
+ }
319
+ if (hasTests && hasCleanDeps) {
320
+ observations.push(`Strong engineering practices are in place: automated testing and clean dependency boundaries support ongoing development.`);
321
+ }
322
+ if (isCLI) {
323
+ observations.push(`As a CLI tool, this system is automation-friendly and can integrate into existing workflows and pipelines.`);
324
+ }
325
+
326
+ for (const obs of observations) {
327
+ output += `- ${obs}\n`;
328
+ }
245
329
 
246
- ---
330
+ output += `\n---
247
331
 
248
- *This summary was generated from repository structure analysis. Set \`GITHUB_TOKEN\` to auto-enable AI-enhanced summaries via GitHub Models (free) \u2014 adds plain-language explanations for non-technical readers, strategic recommendations, and risk assessments.*`;
332
+ *This executive summary was generated from comprehensive repository analysis including dependency graphs, data flow detection, and architectural pattern recognition. Enable AI enhancement with \`GITHUB_TOKEN\` for strategic recommendations, risk assessments, and plain-language explanations tailored for non-technical stakeholders.*`;
249
333
 
250
334
  return output;
251
335
  }
252
336
 
253
337
  function getFallbackSystemOverview(context, enrichment = {}) {
254
338
  const { depGraph } = enrichment;
255
- const sizeLabel = context.project.modulesDetected > 50 ? "large-scale" :
256
- context.project.modulesDetected > 20 ? "medium-sized" : "focused";
257
339
  const testFrameworks = context.techStack.testFrameworks || [];
258
340
  const isCLI = (context.patterns || []).some(p => p.toLowerCase().includes("cli"));
341
+ const projectName = context.project.name || "This system";
342
+
343
+ // Calculate scale classification
344
+ const scaleLevel = context.project.modulesDetected > 100 ? "enterprise" :
345
+ context.project.modulesDetected > 50 ? "large" :
346
+ context.project.modulesDetected > 20 ? "medium" : "small";
347
+ const scaleDesc = {
348
+ enterprise: "an enterprise-scale codebase with significant complexity",
349
+ large: "a large codebase with multiple interconnected subsystems",
350
+ medium: "a medium-sized codebase with clear structure",
351
+ small: "a focused codebase with a clear purpose"
352
+ }[scaleLevel];
259
353
 
260
354
  let output = `# System Overview
261
355
 
262
- ## Repository Snapshot
356
+ ## The Big Picture
357
+
358
+ **${projectName}** is ${scaleDesc}, containing **${context.project.modulesDetected} modules** spread across **${formatFileScale(context.project.filesScanned).split(' (')[0]}**.
359
+
360
+ | Dimension | Details |
361
+ |-----------|---------|
362
+ | **Total Files** | ${context.project.filesScanned} |
363
+ | **Module Count** | ${context.project.modulesDetected} |
364
+ | **Functional Domains** | ${context.domains.length} |`;
365
+
366
+ if (context.project.pagesDetected > 0) {
367
+ output += `\n| **User-Facing Pages** | ${context.project.pagesDetected} |`;
368
+ }
369
+ if (context.project.apiRoutesDetected > 0) {
370
+ output += `\n| **API Endpoints** | ${context.project.apiRoutesDetected} |`;
371
+ }
372
+ if (isCLI) {
373
+ output += `\n| **Interface Type** | Command-Line Tool |`;
374
+ }
375
+
376
+ output += `
263
377
 
264
- This is a ${sizeLabel} codebase organized into **${context.project.modulesDetected} modules** across **${context.project.filesScanned} files**.
378
+ ---
265
379
 
266
- | Metric | Value |
267
- |--------|-------|
268
- | Files scanned | ${context.project.filesScanned} |
269
- | Modules | ${context.project.modulesDetected} |
270
- ${context.project.pagesDetected > 0 ? `| Application pages | ${context.project.pagesDetected} |\n` : ""}${context.project.apiRoutesDetected > 0 ? `| API endpoints | ${context.project.apiRoutesDetected} |\n` : ""}
271
380
  ## Technology Stack
272
381
 
273
- | Category | Technologies |
274
- |----------|-------------|
275
- | Frameworks | ${context.techStack.frameworks.join(", ") || (isCLI ? "N/A (CLI tool)" : "Not detected")} |
276
- | Languages | ${context.techStack.languages.join(", ") || "Not detected"} |
277
- | Build Tools | ${context.techStack.buildTools.join(", ") || (isCLI ? "N/A (CLI tool)" : "Not detected")} |
278
- ${testFrameworks.length > 0 ? `| Test Frameworks | ${testFrameworks.join(", ")} |\n` : ""}
279
- ## Detected Patterns
382
+ The system is built on a modern technology foundation:\n\n`;
280
383
 
281
- ${context.patterns.length > 0 ? context.patterns.map(p => `- ${p}`).join("\n") : "No specific architectural patterns were detected. This typically means the project uses a straightforward directory-based organization."}
282
- `;
384
+ output += `| Layer | Technologies | Purpose |\n`;
385
+ output += `|-------|-------------|----------|\n`;
386
+
387
+ if (context.techStack.frameworks.length > 0) {
388
+ const frameworkPurpose = inferFrameworkPurpose(context.techStack.frameworks);
389
+ output += `| **Runtime** | ${context.techStack.frameworks.join(", ")} | ${frameworkPurpose} |\n`;
390
+ }
391
+
392
+ output += `| **Language** | ${context.techStack.languages.join(", ") || "Not detected"} | Primary development language |\n`;
393
+
394
+ if (context.techStack.buildTools.length > 0) {
395
+ output += `| **Build** | ${context.techStack.buildTools.join(", ")} | Compilation and asset processing |\n`;
396
+ }
397
+
398
+ if (testFrameworks.length > 0) {
399
+ output += `| **Quality** | ${testFrameworks.join(", ")} | Automated testing |\n`;
400
+ }
283
401
 
284
- // Module type breakdown
402
+ // Architecture patterns
403
+ output += `\n## Architecture Patterns\n\n`;
404
+ if (context.patterns.length > 0) {
405
+ output += `The codebase follows **${context.patterns.length} identified architectural pattern${context.patterns.length === 1 ? '' : 's'}**:\n\n`;
406
+ for (const pattern of context.patterns) {
407
+ const patternExplanation = explainPattern(pattern);
408
+ output += `- **${pattern}** — ${patternExplanation}\n`;
409
+ }
410
+ } else {
411
+ output += `No specific architectural patterns were detected. The project follows a pragmatic, directory-based organization typical of ${scaleLevel}-scale projects.\n`;
412
+ }
413
+
414
+ // Module architecture breakdown
285
415
  const typeGroups = groupModulesByType(context.topModules);
286
416
  if (Object.keys(typeGroups).length > 1) {
287
417
  output += `\n## Module Architecture\n\n`;
288
- output += `The codebase is organized into the following module layers:\n\n`;
418
+ output += `The system is organized into **${Object.keys(typeGroups).length} architectural layers**:\n\n`;
419
+ output += `| Layer | Modules | Key Examples | Responsibility |\n`;
420
+ output += `|-------|---------|--------------|----------------|\n`;
289
421
  for (const [type, mods] of Object.entries(typeGroups)) {
290
- const moduleList = mods.slice(0, 5).map(m => `\`${m.key}\` (${m.fileCount} files)`).join(", ");
291
- output += `- **${formatModuleType(type)}** (${mods.length}): ${moduleList}\n`;
422
+ const examples = mods.slice(0, 2).map(m => `\`${m.key.split("/").pop()}\``).join(", ");
423
+ const responsibility = getLayerResponsibility(type);
424
+ output += `| **${formatModuleType(type)}** | ${mods.length} | ${examples} | ${responsibility} |\n`;
425
+ }
426
+
427
+ output += `\n### Layer Details\n\n`;
428
+ for (const [type, mods] of Object.entries(typeGroups)) {
429
+ output += `**${formatModuleType(type)}** — ${mods.length} module${mods.length === 1 ? '' : 's'}\n\n`;
430
+ for (const m of mods.slice(0, 4)) {
431
+ const desc = describeOnboardingModule(m.key);
432
+ output += `- \`${m.key}\` — ${m.fileCount} files — ${desc}\n`;
433
+ }
434
+ if (mods.length > 4) {
435
+ output += `- *+${mods.length - 4} more modules*\n`;
436
+ }
437
+ output += `\n`;
292
438
  }
293
439
  }
294
440
 
295
- output += `\n## Dominant Domains
296
-
297
- The following domains represent the largest areas of the codebase by file count:
298
-
299
- | Rank | Domain | Files |
300
- |------|--------|-------|
301
- ${context.domains.slice(0, 5).map((d, i) => `| ${i + 1} | ${d.name} | ${d.fileCount} |`).join("\n")}
302
- `;
441
+ // Dominant domains with rich descriptions
442
+ output += `## Functional Domains\n\n`;
443
+ output += `The following domains represent the largest functional areas by file count:\n\n`;
444
+ output += `| Rank | Domain | Scale | Business Capability |\n`;
445
+ output += `|------|--------|-------|---------------------|\n`;
446
+ for (const [i, d] of context.domains.slice(0, 6).entries()) {
447
+ const capability = d.description || inferDomainCapability(d.name);
448
+ output += `| ${i + 1} | **${d.name}** | ${d.fileCount} files | ${capability} |\n`;
449
+ }
450
+ if (context.domains.length > 6) {
451
+ output += `| — | *+${context.domains.length - 6} more* | — | Additional domains |\n`;
452
+ }
303
453
 
304
- // Route highlights
454
+ // Route summary with better organization
305
455
  const routes = context.routes || {};
306
456
  const pages = routes.pages || [];
307
457
  const apis = routes.apis || [];
458
+
308
459
  if (pages.length > 0 || apis.length > 0) {
309
460
  output += `\n## Route Summary\n\n`;
461
+ const routeSummary = describeRoutePattern(context.routes);
462
+ if (routeSummary) {
463
+ output += `The system surfaces ${routeSummary}.\n\n`;
464
+ }
465
+
310
466
  if (pages.length > 0) {
311
- output += `**Application Pages** (${pages.length} total):\n\n`;
467
+ output += `### User-Facing Pages (${pages.length})\n\n`;
468
+ output += `| Path | Description |\n`;
469
+ output += `|------|-------------|\n`;
312
470
  for (const p of pages.slice(0, 8)) {
313
- output += `- \`${p.path}\`\n`;
471
+ const pageName = p.path.split('/').pop() || 'root';
472
+ const desc = inferPageDescription(pageName);
473
+ output += `| \`${p.path}\` | ${desc} |\n`;
474
+ }
475
+ if (pages.length > 8) {
476
+ output += `| *+${pages.length - 8} more* | Additional pages |\n`;
314
477
  }
315
- if (pages.length > 8) output += `- … and ${pages.length - 8} more\n`;
316
478
  output += "\n";
317
479
  }
480
+
318
481
  if (apis.length > 0) {
319
- output += `**API Endpoints** (${apis.length} total):\n\n`;
482
+ output += `### API Endpoints (${apis.length})\n\n`;
483
+ output += `| Endpoint | Methods | Purpose |\n`;
484
+ output += `|----------|---------|----------|\n`;
320
485
  for (const a of apis.slice(0, 8)) {
321
- const methods = a.methods ? ` [${a.methods.join(", ")}]` : "";
322
- output += `- \`${a.path}\`${methods}\n`;
486
+ const methods = a.methods?.join(", ") || "";
487
+ const purpose = inferAPIEndpointPurpose(a.path);
488
+ output += `| \`${a.path}\` | ${methods} | ${purpose} |\n`;
489
+ }
490
+ if (apis.length > 8) {
491
+ output += `| *+${apis.length - 8} more* | — | Additional endpoints |\n`;
323
492
  }
324
- if (apis.length > 8) output += `- … and ${apis.length - 8} more\n`;
325
493
  output += "\n";
326
494
  }
327
495
  }
328
496
 
329
- // Dependency graph highlights
497
+ // Dependency graph with rich analysis
330
498
  if (depGraph?.stats) {
331
499
  const stats = depGraph.stats;
500
+ const healthScore = calculateHealthScore(stats, context);
501
+ const healthEmoji = healthScore >= 80 ? "🟢" : healthScore >= 60 ? "🟡" : "🔴";
502
+
332
503
  output += `## Dependency Graph\n\n`;
333
- output += `| Metric | Value |\n`;
334
- output += `|--------|-------|\n`;
335
- output += `| Internal imports | ${stats.totalEdges} |\n`;
336
- output += `| External packages | ${stats.externalDeps} |\n`;
337
- output += `| Circular dependencies | ${stats.cycles} |\n`;
338
- output += `| Orphan files | ${stats.orphanFiles} |\n`;
504
+ output += `**Codebase Health: ${healthEmoji} ${healthScore}/100**\n\n`;
505
+ output += `| Metric | Value | Assessment |\n`;
506
+ output += `|--------|-------|------------|\n`;
507
+ output += `| Internal Imports | ${stats.totalEdges} | ${stats.totalEdges > 0 ? "Modules are connected" : "No internal imports"} |\n`;
508
+ output += `| External Packages | ${stats.externalDeps} | ${stats.externalDeps < 50 ? "Lean dependencies" : stats.externalDeps < 100 ? "Moderate dependencies" : "Many dependencies"} |\n`;
509
+ output += `| Circular Dependencies | ${stats.cycles} | ${stats.cycles === 0 ? "✅ Clean" : "⚠️ Needs attention"} |\n`;
510
+ output += `| Orphan Files | ${stats.orphanFiles} | ${stats.orphanFiles === 0 ? "✅ All connected" : `⚠️ ${stats.orphanFiles} unused`} |\n`;
511
+
339
512
  if (stats.hubs && stats.hubs.length > 0) {
340
513
  output += `\n**Hub modules** (most imported):\n\n`;
341
514
  for (const hub of stats.hubs.slice(0, 5)) {
342
515
  output += `- \`${hub.key}\` — imported by ${hub.importedBy} files\n`;
343
516
  }
517
+ output += "\n";
344
518
  }
345
- output += "\n";
346
519
  }
347
520
 
348
- // Monorepo
521
+ // Monorepo structure
349
522
  if (context.monorepo) {
350
- output += `## Monorepo\n\n`;
351
- output += `Managed by **${context.monorepo.tool}** with **${context.monorepo.packageCount} packages**.\n\n`;
523
+ output += `## Monorepo Structure\n\n`;
524
+ output += `This is a **${context.monorepo.tool}** monorepo containing **${context.monorepo.packageCount} packages**:\n\n`;
525
+ output += `| Package | Location | Description |\n`;
526
+ output += `|---------|----------|-------------|\n`;
527
+ for (const pkg of context.monorepo.packages.slice(0, 8)) {
528
+ const pkgDesc = inferPackageDescription(pkg.name);
529
+ output += `| \`${pkg.name}\` | \`${pkg.path}\` | ${pkgDesc} |\n`;
530
+ }
531
+ if (context.monorepo.packages.length > 8) {
532
+ output += `| *+${context.monorepo.packages.length - 8} more* | — | Additional packages |\n`;
533
+ }
534
+ output += "\n";
352
535
  }
353
536
 
354
537
  output += `---
355
538
 
356
- *This overview was generated from repository structure analysis. Set \`GITHUB_TOKEN\` to auto-enable AI via GitHub Models (free) \u2014 adds narrative context, architectural rationale, and technology trade-off analysis.*`;
539
+ *This system overview was generated from comprehensive repository analysis. Enable AI enhancement with \`GITHUB_TOKEN\` for narrative context, architectural rationale, technology trade-off analysis, and scalability recommendations.*`;
357
540
 
358
541
  return output;
359
542
  }
@@ -363,24 +546,50 @@ function getFallbackBusinessDomains(context, enrichment = {}) {
363
546
  const routes = context.routes || {};
364
547
  const pages = routes.pages || [];
365
548
  const apis = routes.apis || [];
549
+ const projectName = context.project.name || "The system";
550
+
551
+ let output = `# Business Domains
552
+
553
+ ## Overview
554
+
555
+ ${projectName} is organized into **${context.domains.length} functional domain${context.domains.length === 1 ? '' : 's'}**, each representing a distinct area of business capability. This document maps the technical structure to business-oriented functional areas.
556
+
557
+ **Quick Reference:**
558
+
559
+ | Domain | Modules | Files | Primary Capability |
560
+ |--------|---------|-------|-------------------|
561
+ ${context.domains.map(d => `| ${d.name} | ${d.moduleCount} | ${d.fileCount} | ${inferDomainCapability(d.name).split('.')[0]} |`).join("\n")}
562
+
563
+ ---
564
+
565
+ `;
366
566
 
367
- let output = `# Business Domains\n\n`;
368
- output += `> This document maps the codebase into business-oriented functional areas. Each domain represents a distinct area of responsibility.\n\n`;
369
- output += `**Total domains identified:** ${context.domains.length}\n\n`;
370
- output += `---\n\n`;
371
-
372
- for (const domain of context.domains) {
373
- output += `## ${domain.name}\n\n`;
374
- output += `${domain.description || "This domain covers a distinct functional area of the application."}\n\n`;
375
- output += `| Metric | Value |\n`;
376
- output += `|--------|-------|\n`;
377
- output += `| Modules | ${domain.moduleCount} |\n`;
378
- output += `| Files | ${domain.fileCount} |\n\n`;
567
+ // Detailed domain breakdowns
568
+ for (const [index, domain] of context.domains.entries()) {
569
+ const capability = domain.description || inferDomainCapability(domain.name);
570
+ const sizeLabel = domain.fileCount > 50 ? "major" : domain.fileCount > 20 ? "significant" : "focused";
571
+
572
+ output += `## ${index + 1}. ${domain.name}\n\n`;
573
+ output += `### Business Capability\n\n`;
574
+ output += `${capability}\n\n`;
575
+ output += `This is a **${sizeLabel} domain** with ${domain.moduleCount} module${domain.moduleCount === 1 ? '' : 's'} and ${domain.fileCount} source file${domain.fileCount === 1 ? '' : 's'}.\n\n`;
576
+
577
+ // Key modules with descriptions
379
578
  if (domain.topModules?.length > 0) {
380
- output += `**Key modules:** ${domain.topModules.slice(0, 5).map(m => `\`${m}\``).join(", ")}\n\n`;
579
+ output += `### Key Modules\n\n`;
580
+ output += `| Module | Purpose |\n`;
581
+ output += `|--------|----------|\n`;
582
+ for (const mod of domain.topModules.slice(0, 5)) {
583
+ const modPurpose = describeOnboardingModule(mod);
584
+ output += `| \`${mod}\` | ${modPurpose} |\n`;
585
+ }
586
+ if (domain.topModules.length > 5) {
587
+ output += `| *+${domain.topModules.length - 5} more* | Additional modules |\n`;
588
+ }
589
+ output += "\n";
381
590
  }
382
591
 
383
- // Match routes to this domain by checking if any domain module paths appear in routes
592
+ // Match routes to this domain
384
593
  const domainModules = domain.topModules || [];
385
594
  const domainPages = pages.filter(p =>
386
595
  domainModules.some(m => p.path.toLowerCase().includes(m.toLowerCase().split("/").pop()))
@@ -390,29 +599,73 @@ function getFallbackBusinessDomains(context, enrichment = {}) {
390
599
  );
391
600
 
392
601
  if (domainPages.length > 0 || domainApis.length > 0) {
393
- output += `**Routes in this domain:**\n\n`;
394
- for (const p of domainPages.slice(0, 5)) {
395
- output += `- 📄 \`${p.path}\`\n`;
602
+ output += `### User-Facing Interfaces\n\n`;
603
+ if (domainPages.length > 0) {
604
+ output += `**Pages:**\n`;
605
+ for (const p of domainPages.slice(0, 5)) {
606
+ const pageName = p.path.split('/').pop() || 'root';
607
+ output += `- 📄 \`${p.path}\` — ${inferPageDescription(pageName)}\n`;
608
+ }
609
+ output += "\n";
396
610
  }
397
- for (const a of domainApis.slice(0, 5)) {
398
- const methods = a.methods ? ` [${a.methods.join(", ")}]` : "";
399
- output += `- 🔌 \`${a.path}\`${methods}\n`;
611
+ if (domainApis.length > 0) {
612
+ output += `**API Endpoints:**\n`;
613
+ for (const a of domainApis.slice(0, 5)) {
614
+ const methods = a.methods ? `[${a.methods.join(", ")}]` : "";
615
+ const purpose = inferAPIEndpointPurpose(a.path);
616
+ output += `- 🔌 \`${a.path}\` ${methods} — ${purpose}\n`;
617
+ }
618
+ output += "\n";
400
619
  }
401
- output += "\n";
402
620
  }
621
+
622
+ // Business impact assessment
623
+ const impactLevel = domain.fileCount > 50 ? "high" : domain.fileCount > 20 ? "medium" : "low";
624
+ output += `### Business Impact\n\n`;
625
+ output += `- **Change Frequency Impact:** ${impactLevel.charAt(0).toUpperCase() + impactLevel.slice(1)} — Changes here affect ${domain.fileCount} file${domain.fileCount === 1 ? '' : 's'}\n`;
626
+ output += `- **Domain Complexity:** ${domain.moduleCount > 5 ? "Complex (multiple modules)" : domain.moduleCount > 2 ? "Moderate" : "Simple"}\n`;
627
+ if (impactLevel === "high") {
628
+ output += `- **Recommendation:** Ensure comprehensive testing before modifying this domain\n`;
629
+ }
630
+ output += "\n---\n\n";
403
631
  }
404
632
 
405
- // Cross-domain dependency insights
633
+ // Cross-domain dependency insights with richer analysis
406
634
  if (depGraph?.stats?.hubs && depGraph.stats.hubs.length > 0) {
407
635
  output += `## Cross-Domain Dependencies\n\n`;
408
- output += `The following modules are heavily imported across the codebase, likely serving as shared infrastructure:\n\n`;
409
- for (const hub of depGraph.stats.hubs.slice(0, 5)) {
410
- output += `- \`${hub.key}\` — imported by ${hub.importedBy} modules\n`;
636
+ output += `The following modules serve as **shared infrastructure** across multiple domains:\n\n`;
637
+ output += `| Module | Consumers | Role |\n`;
638
+ output += `|--------|-----------|------|\n`;
639
+ for (const hub of depGraph.stats.hubs.slice(0, 6)) {
640
+ const consumers = hub.importedBy;
641
+ const role = describeOnboardingModule(hub.key);
642
+ output += `| \`${hub.key}\` | ${consumers} modules | ${role} |\n`;
643
+ }
644
+ output += `\n`;
645
+
646
+ output += `### Cross-Domain Integration Points\n\n`;
647
+ output += `These modules facilitate communication and data sharing between domains:\n\n`;
648
+ for (const hub of depGraph.stats.hubs.slice(0, 3)) {
649
+ output += `- **\`${hub.key}\`** serves as a central integration point, imported by ${hub.importedBy} different modules. Changes to this module have wide-ranging impact and should be carefully coordinated.\n`;
411
650
  }
412
651
  output += "\n";
413
652
  }
414
653
 
415
- output += `---\n\n*Domain mapping is based on directory naming conventions. Set \`GITHUB_TOKEN\` to auto-enable AI via GitHub Models (free) \u2014 adds business-language descriptions, stakeholder impact analysis, and cross-domain relationship narratives.*`;
654
+ // Domain health assessment
655
+ output += `## Domain Health Summary\n\n`;
656
+ const totalFiles = context.domains.reduce((sum, d) => sum + d.fileCount, 0);
657
+ const avgFilesPerDomain = Math.round(totalFiles / context.domains.length);
658
+ const largestDomain = context.domains[0];
659
+ const domainConcentration = largestDomain ? Math.round((largestDomain.fileCount / totalFiles) * 100) : 0;
660
+
661
+ output += `| Metric | Value | Assessment |\n`;
662
+ output += `|--------|-------|------------|\n`;
663
+ output += `| Total Domains | ${context.domains.length} | ${context.domains.length < 3 ? "Consider finer-grained domain separation" : context.domains.length > 12 ? "Consider consolidating related domains" : "Well-balanced"} |\n`;
664
+ output += `| Avg Files/Domain | ${avgFilesPerDomain} | ${avgFilesPerDomain > 100 ? "Domains may be too large" : "Appropriate size"} |\n`;
665
+ output += `| Largest Domain | ${largestDomain?.name || "—"} (${largestDomain?.fileCount || 0} files) | ${domainConcentration > 50 ? "Consider breaking down" : "Reasonable"} |\n`;
666
+ output += `| Domain Concentration | ${domainConcentration}% in largest | ${domainConcentration > 60 ? "⚠️ High concentration" : "✅ Well distributed"} |\n`;
667
+
668
+ output += `\n---\n\n*Domain mapping is based on directory naming conventions and code organization patterns. Enable AI enhancement with \`GITHUB_TOKEN\` for business-language descriptions, stakeholder impact analysis, strategic recommendations, and cross-domain relationship narratives.*`;
416
669
 
417
670
  return output;
418
671
  }
@@ -420,90 +673,180 @@ function getFallbackBusinessDomains(context, enrichment = {}) {
420
673
  function getFallbackArchitectureOverview(context, enrichment = {}) {
421
674
  const { depGraph, driftResult } = enrichment;
422
675
  const isCLI = (context.patterns || []).some(p => p.toLowerCase().includes("cli"));
423
- const patternDesc = context.patterns.length > 0
424
- ? `The detected architectural patterns are **${context.patterns.join(", ")}**. These patterns shape how data and control flow through the system.`
425
- : "No specific architectural patterns were detected. The project appears to follow a straightforward directory-based organization.";
676
+ const projectName = context.project.name || "This system";
677
+ const testFrameworks = context.techStack.testFrameworks || [];
426
678
 
427
679
  let output = `# Architecture Overview
428
680
 
429
- ## Architectural Style
681
+ ## Architectural Philosophy
430
682
 
431
- ${patternDesc}
432
-
433
- ## System Layers
683
+ `;
434
684
 
435
- The codebase is organized into ${context.domains.length} functional layers, each encapsulating a distinct area of responsibility:
685
+ if (context.patterns.length > 0) {
686
+ output += `**${projectName}** follows a **${context.patterns[0]}** architecture`;
687
+ if (context.patterns.length > 1) {
688
+ output += ` with elements of **${context.patterns.slice(1).join(", ")}**`;
689
+ }
690
+ output += `.\n\n`;
691
+
692
+ output += `### Pattern Analysis\n\n`;
693
+ for (const pattern of context.patterns) {
694
+ const explanation = explainPattern(pattern);
695
+ const benefits = getBenefitsForPattern(pattern);
696
+ output += `**${pattern}**\n\n`;
697
+ output += `${explanation}\n\n`;
698
+ output += `*Benefits:* ${benefits}\n\n`;
699
+ }
700
+ } else {
701
+ output += `The codebase follows a **pragmatic, directory-based organization** without a specific named architectural pattern. This is a common approach for ${context.project.modulesDetected < 30 ? "smaller" : "organically grown"} projects that prioritize simplicity over formalism.\n\n`;
702
+ output += `### Structural Approach\n\n`;
703
+ output += `- Files are grouped by type or feature\n`;
704
+ output += `- No strict layer enforcement\n`;
705
+ output += `- Flexibility for rapid development\n\n`;
706
+ }
436
707
 
437
- | Layer | Description |
438
- |-------|-------------|
439
- ${context.domains.slice(0, 8).map(d => `| **${d.name}** | ${d.description || "Handles a distinct functional concern"} |`).join("\n")}
440
- `;
708
+ // System layers with rich descriptions
709
+ output += `## System Layers\n\n`;
710
+ output += `The system is organized into **${context.domains.length} functional layer${context.domains.length === 1 ? '' : 's'}**, each encapsulating a distinct area of responsibility:\n\n`;
711
+
712
+ output += `| Layer | Responsibility | Scale | Key Insight |\n`;
713
+ output += `|-------|----------------|-------|-------------|\n`;
714
+ for (const d of context.domains.slice(0, 8)) {
715
+ const responsibility = d.description || inferDomainCapability(d.name);
716
+ const insight = getLayerInsight(d.name, d.fileCount, d.moduleCount);
717
+ output += `| **${d.name}** | ${responsibility.split('.')[0]} | ${d.fileCount} files | ${insight} |\n`;
718
+ }
719
+ if (context.domains.length > 8) {
720
+ output += `| *+${context.domains.length - 8} more* | Additional layers | — | — |\n`;
721
+ }
722
+ output += `\n`;
441
723
 
442
- // Module layer breakdown by type
724
+ // Module architecture
443
725
  const typeGroups = groupModulesByType(context.topModules);
444
- if (Object.keys(typeGroups).length > 1) {
445
- output += `\n## Module Layers\n\n`;
446
- output += `Modules are classified into architectural layers based on their role:\n\n`;
726
+ if (Object.keys(typeGroups).length > 0) {
727
+ output += `## Module Layers\n\n`;
728
+ output += `Modules are classified into **${Object.keys(typeGroups).length} architectural role${Object.keys(typeGroups).length === 1 ? '' : 's'}**:\n\n`;
729
+
447
730
  for (const [type, mods] of Object.entries(typeGroups)) {
448
- output += `### ${formatModuleType(type)}\n\n`;
731
+ const role = formatModuleType(type);
732
+ const responsibility = getLayerResponsibility(type);
733
+ output += `### ${role}\n\n`;
734
+ output += `**Role:** ${responsibility}\n\n`;
735
+ output += `| Module | Files | Description |\n`;
736
+ output += `|--------|-------|-------------|\n`;
449
737
  for (const m of mods.slice(0, 5)) {
450
- output += `- \`${m.key}\` (${m.fileCount} files)\n`;
738
+ const desc = describeOnboardingModule(m.key);
739
+ output += `| \`${m.key}\` | ${m.fileCount} | ${desc} |\n`;
740
+ }
741
+ if (mods.length > 5) {
742
+ output += `| *+${mods.length - 5} more* | — | Additional modules |\n`;
451
743
  }
452
744
  output += "\n";
453
745
  }
454
746
  }
455
747
 
456
- output += `## Technology Stack
457
-
458
- | Category | Technologies |
459
- |----------|-------------|
460
- | Frameworks | ${context.techStack.frameworks.join(", ") || (isCLI ? "N/A (CLI tool)" : "Not detected")} |
461
- | Languages | ${context.techStack.languages.join(", ") || "Not detected"} |
462
- | Build Tools | ${context.techStack.buildTools.join(", ") || (isCLI ? "N/A (CLI tool)" : "Not detected")} |
463
-
464
- ## Scale & Complexity
748
+ // Technology stack with architectural implications
749
+ output += `## Technology Foundation\n\n`;
750
+ output += `| Layer | Technologies | Architectural Implication |\n`;
751
+ output += `|-------|-------------|---------------------------|\n`;
752
+
753
+ if (context.techStack.frameworks.length > 0) {
754
+ const frameworkImpl = getFrameworkArchitecturalImplication(context.techStack.frameworks);
755
+ output += `| **Application** | ${context.techStack.frameworks.join(", ")} | ${frameworkImpl} |\n`;
756
+ }
757
+ output += `| **Language** | ${context.techStack.languages.join(", ") || "Not detected"} | ${getLanguageImplication(context.techStack.languages)} |\n`;
758
+ if (context.techStack.buildTools.length > 0) {
759
+ output += `| **Build** | ${context.techStack.buildTools.join(", ")} | Modern bundling and optimization |\n`;
760
+ }
761
+ if (testFrameworks.length > 0) {
762
+ output += `| **Quality** | ${testFrameworks.join(", ")} | Enables confident refactoring |\n`;
763
+ }
465
764
 
466
- The repository comprises **${context.project.filesScanned} files** organized into **${context.project.modulesDetected} modules**.${context.project.apiRoutesDetected > 0 ? ` It exposes **${context.project.apiRoutesDetected} API endpoint${context.project.apiRoutesDetected === 1 ? "" : "s"}**.` : ""}${context.project.pagesDetected > 0 ? ` It serves **${context.project.pagesDetected} application page${context.project.pagesDetected === 1 ? "" : "s"}**.` : ""}${isCLI ? " It operates as a command-line tool." : ""}
467
- `;
765
+ // Scale and complexity analysis
766
+ output += `\n## Scale & Complexity Analysis\n\n`;
767
+ const complexityLevel = context.project.modulesDetected > 100 ? "high" :
768
+ context.project.modulesDetected > 50 ? "medium-high" :
769
+ context.project.modulesDetected > 20 ? "medium" : "low";
770
+
771
+ output += `| Dimension | Value | Assessment |\n`;
772
+ output += `|-----------|-------|------------|\n`;
773
+ output += `| **Total Files** | ${context.project.filesScanned} | ${formatFileScale(context.project.filesScanned).split('(')[1]?.replace(')', '') || "—"} |\n`;
774
+ output += `| **Module Count** | ${context.project.modulesDetected} | ${complexityLevel} complexity |\n`;
775
+ output += `| **Domain Spread** | ${context.domains.length} domains | ${context.domains.length > 10 ? "High specialization" : context.domains.length > 5 ? "Good separation" : "Focused scope"} |\n`;
776
+ if (context.project.apiRoutesDetected > 0) {
777
+ output += `| **API Surface** | ${context.project.apiRoutesDetected} endpoints | ${context.project.apiRoutesDetected > 50 ? "Large API" : context.project.apiRoutesDetected > 20 ? "Medium API" : "Compact API"} |\n`;
778
+ }
779
+ if (context.project.pagesDetected > 0) {
780
+ output += `| **UI Surface** | ${context.project.pagesDetected} pages | ${context.project.pagesDetected > 30 ? "Feature-rich UI" : context.project.pagesDetected > 10 ? "Moderate UI" : "Focused UI"} |\n`;
781
+ }
782
+ if (isCLI) {
783
+ output += `| **Interface** | CLI | Terminal-driven interaction |\n`;
784
+ }
468
785
 
469
- // Dependency graph health
786
+ // Dependency health with deep analysis
470
787
  if (depGraph?.stats) {
471
788
  const stats = depGraph.stats;
789
+ const healthScore = calculateHealthScore(stats, context);
790
+ const healthEmoji = healthScore >= 80 ? "🟢" : healthScore >= 60 ? "🟡" : "🔴";
791
+
472
792
  output += `\n## Dependency Health\n\n`;
473
- output += `| Metric | Value |\n`;
474
- output += `|--------|-------|\n`;
475
- output += `| Import edges | ${stats.totalEdges} |\n`;
476
- output += `| External packages | ${stats.externalDeps} |\n`;
477
- output += `| Circular dependencies | ${stats.cycles} |\n`;
478
- output += `| Orphan files | ${stats.orphanFiles} |\n\n`;
479
-
480
- // Strengths
793
+ output += `**Architecture Health Score: ${healthEmoji} ${healthScore}/100**\n\n`;
794
+
795
+ output += `| Metric | Value | Status | Recommendation |\n`;
796
+ output += `|--------|-------|--------|----------------|\n`;
797
+ output += `| Import Edges | ${stats.totalEdges} | ${stats.totalEdges > 0 ? "✅" : "⚠️"} | ${stats.totalEdges > 0 ? "Healthy module connectivity" : "Consider connecting modules"} |\n`;
798
+ output += `| External Packages | ${stats.externalDeps} | ${stats.externalDeps < 50 ? "✅" : stats.externalDeps < 100 ? "⚠️" : "🔴"} | ${stats.externalDeps >= 100 ? "Audit for unused dependencies" : stats.externalDeps >= 50 ? "Review dependency necessity" : "Lean dependency set"} |\n`;
799
+ output += `| Circular Deps | ${stats.cycles} | ${stats.cycles === 0 ? "✅" : "🔴"} | ${stats.cycles === 0 ? "Clean module boundaries" : "Refactor to break cycles"} |\n`;
800
+ output += `| Orphan Files | ${stats.orphanFiles} | ${stats.orphanFiles === 0 ? "✅" : "⚠️"} | ${stats.orphanFiles === 0 ? "All code is connected" : "Review for dead code"} |\n`;
801
+
802
+ // Architectural strengths and concerns
803
+ output += `\n### Architectural Strengths\n\n`;
481
804
  const strengths = [];
805
+ if (stats.cycles === 0) {
806
+ strengths.push("**Clean module boundaries** — No circular dependencies detected. This enables safe refactoring, isolated testing, and clear ownership boundaries.");
807
+ }
808
+ if (stats.orphanFiles === 0) {
809
+ strengths.push("**Fully connected codebase** — Every file is part of the dependency graph. No dead code or forgotten modules.");
810
+ }
811
+ if (stats.hubs && stats.hubs.length > 0 && stats.hubs[0].importedBy < stats.totalFiles * 0.3) {
812
+ strengths.push("**Balanced coupling** — No single module dominates the import graph. This reduces change risk and enables parallel development.");
813
+ }
814
+ if (testFrameworks.length > 0) {
815
+ strengths.push("**Testing infrastructure** — Automated testing with " + testFrameworks.join(", ") + " supports confident evolution.");
816
+ }
817
+
818
+ for (const s of strengths) {
819
+ output += `- ${s}\n`;
820
+ }
821
+ if (strengths.length === 0) {
822
+ output += `- *No major architectural strengths identified — consider improving test coverage and reducing circular dependencies.*\n`;
823
+ }
824
+
825
+ // Concerns
482
826
  const concerns = [];
483
- if (stats.cycles === 0) strengths.push("No circular dependencies — clean module boundaries");
484
- if (stats.orphanFiles === 0) strengths.push("No orphan files all code is connected");
485
- if (stats.hubs && stats.hubs.length > 0 && stats.hubs[0].importedBy < stats.totalFiles * 0.3)
486
- strengths.push("No excessively coupled hubs");
487
-
488
- if (stats.cycles > 0) concerns.push(`${stats.cycles} circular dependenc${stats.cycles === 1 ? "y" : "ies"} — may hinder testing and refactoring`);
489
- if (stats.orphanFiles > stats.totalFiles * 0.2)
490
- concerns.push(`High orphan file ratio (${stats.orphanFiles}/${stats.totalFiles}) — possible dead code`);
491
- if (stats.hubs && stats.hubs.length > 0 && stats.hubs[0].importedBy >= stats.totalFiles * 0.3)
492
- concerns.push(`\`${stats.hubs[0].key}\` is imported by ${stats.hubs[0].importedBy} files — high coupling risk`);
493
-
494
- if (strengths.length > 0) {
495
- output += `**Strengths:**\n\n`;
496
- for (const s of strengths) output += `- ✅ ${s}\n`;
497
- output += "\n";
827
+ if (stats.cycles > 0) {
828
+ concerns.push(`**Circular dependencies detected** — ${stats.cycles} circular dependenc${stats.cycles === 1 ? 'y' : 'ies'} create tight coupling, complicate testing, and hinder refactoring. Priority: High.`);
498
829
  }
830
+ if (stats.orphanFiles > stats.totalFiles * 0.2) {
831
+ concerns.push(`**High orphan file ratio** — ${stats.orphanFiles}/${stats.totalFiles} files (${Math.round(stats.orphanFiles / stats.totalFiles * 100)}%) are not imported. Consider removing dead code. Priority: Medium.`);
832
+ }
833
+ if (stats.hubs && stats.hubs.length > 0 && stats.hubs[0].importedBy >= stats.totalFiles * 0.3) {
834
+ concerns.push(`**Central coupling hotspot** — \`${stats.hubs[0].key}\` is imported by ${stats.hubs[0].importedBy} files (${Math.round(stats.hubs[0].importedBy / stats.totalFiles * 100)}%). Consider breaking into smaller modules. Priority: Medium.`);
835
+ }
836
+ if (stats.externalDeps > 100) {
837
+ concerns.push(`**Heavy external dependencies** — ${stats.externalDeps} packages increase security surface and maintenance burden. Audit regularly. Priority: Low.`);
838
+ }
839
+
499
840
  if (concerns.length > 0) {
500
- output += `**Concerns:**\n\n`;
501
- for (const c of concerns) output += `- ⚠️ ${c}\n`;
502
- output += "\n";
841
+ output += `\n### Concerns\n\n`;
842
+ for (const c of concerns) {
843
+ output += `- ${c}\n`;
844
+ }
503
845
  }
504
846
 
847
+ // Hub modules
505
848
  if (stats.hubs && stats.hubs.length > 0) {
506
- output += `**Hub modules** (most imported):\n\n`;
849
+ output += `\n**Hub modules** (most imported):\n\n`;
507
850
  for (const hub of stats.hubs.slice(0, 5)) {
508
851
  output += `- \`${hub.key}\` — imported by ${hub.importedBy} files\n`;
509
852
  }
@@ -511,38 +854,64 @@ The repository comprises **${context.project.filesScanned} files** organized int
511
854
  }
512
855
  }
513
856
 
514
- // Drift summary
857
+ // Architecture drift
515
858
  if (driftResult?.drifts && driftResult.drifts.length > 0) {
516
- output += `## Architecture Drift\n\n`;
517
- output += `${driftResult.drifts.length} drift${driftResult.drifts.length === 1 ? "" : "s"} detected since last baseline:\n\n`;
518
- for (const drift of driftResult.drifts.slice(0, 5)) {
519
- output += `- **${drift.type}**: ${drift.description || drift.message || "Change detected"}\n`;
859
+ output += `## Architecture Drift Analysis\n\n`;
860
+ output += `**${driftResult.drifts.length} drift${driftResult.drifts.length === 1 ? '' : 's'} detected** since the last baseline:\n\n`;
861
+ output += `| Type | Description | Severity |\n`;
862
+ output += `|------|-------------|----------|\n`;
863
+ for (const drift of driftResult.drifts.slice(0, 6)) {
864
+ const severity = drift.severity || "medium";
865
+ const severityEmoji = severity === "high" ? "🔴" : severity === "medium" ? "🟡" : "🟢";
866
+ output += `| ${drift.type} | ${drift.description || drift.message || "Change detected"} | ${severityEmoji} ${severity} |\n`;
867
+ }
868
+ if (driftResult.drifts.length > 6) {
869
+ output += `| *+${driftResult.drifts.length - 6} more* | Additional drifts | — |\n`;
520
870
  }
521
871
  output += "\n";
522
872
  }
523
873
 
524
- // Monorepo
874
+ // Monorepo architecture
525
875
  if (context.monorepo) {
526
876
  output += `## Monorepo Architecture\n\n`;
527
- output += `This project is a **${context.monorepo.tool}** monorepo with **${context.monorepo.packageCount} packages**:\n\n`;
877
+ output += `This project uses a **${context.monorepo.tool}** monorepo with **${context.monorepo.packageCount} packages**.\n\n`;
878
+ output += `### Package Structure\n\n`;
879
+ output += `| Package | Location | Architectural Role |\n`;
880
+ output += `|---------|----------|--------------------|\n`;
528
881
  for (const pkg of context.monorepo.packages.slice(0, 10)) {
529
- output += `- \`${pkg.name}\` — ${pkg.path}\n`;
882
+ const role = inferPackageDescription(pkg.name);
883
+ output += `| \`${pkg.name}\` | \`${pkg.path}\` | ${role} |\n`;
884
+ }
885
+ if (context.monorepo.packages.length > 10) {
886
+ output += `| *+${context.monorepo.packages.length - 10} more* | — | — |\n`;
530
887
  }
531
888
  output += "\n";
889
+
890
+ output += `### Monorepo Benefits\n\n`;
891
+ output += `- **Shared infrastructure** — Common tooling and configurations\n`;
892
+ output += `- **Atomic changes** — Cross-package updates in single commits\n`;
893
+ output += `- **Independent deployment** — Each package can be released separately\n`;
894
+ output += `- **Clear ownership** — Package boundaries define team responsibilities\n\n`;
532
895
  }
533
896
 
534
897
  output += `---
535
898
 
536
- *This architecture overview was generated from repository structure analysis. Set \`GITHUB_TOKEN\` to auto-enable AI via GitHub Models (free) \u2014 adds architectural narratives, design rationale, scalability analysis, and refactoring recommendations.*`;
899
+ *This architecture overview was generated from comprehensive dependency graph analysis and pattern detection. Enable AI enhancement with \`GITHUB_TOKEN\` for architectural narratives, design rationale explanations, scalability analysis, and refactoring recommendations.*`;
537
900
 
538
901
  return output;
539
902
  }
540
903
 
541
904
  function getFallbackDataFlows(flows, context, enrichment = {}) {
542
905
  const { depGraph, scanResult } = enrichment;
906
+ const projectName = context.project.name || "The system";
543
907
 
544
- let output = `# Data Flows\n\n`;
545
- output += `> Data flows describe how information moves through the system — from external inputs through processing layers to storage or presentation.\n\n`;
908
+ let output = `# Data Flows
909
+
910
+ ## Understanding Data Movement
911
+
912
+ Data flows describe how information moves through **${projectName}** — from external inputs through processing layers to storage or presentation. Understanding these flows is essential for debugging, performance optimization, and architectural decisions.
913
+
914
+ `;
546
915
 
547
916
  // Combine heuristic flows with dep-graph-derived flows, filtering out test file flows
548
917
  const allFlows = [...(flows || [])].filter(f => !f.name?.toLowerCase().includes('test'));
@@ -558,52 +927,187 @@ function getFallbackDataFlows(flows, context, enrichment = {}) {
558
927
  }
559
928
 
560
929
  if (allFlows.length === 0) {
561
- output += `No data flows were detected. This typically means the system uses straightforward request–response patterns without distinct multi-step pipelines.\n\n`;
562
- output += `---\n\n*Set \`GITHUB_TOKEN\` to auto-enable AI via GitHub Models (free) \u2014 AI can infer data flows from import patterns even when heuristics find none.*`;
930
+ output += `## Flow Detection Results
931
+
932
+ No data flows were detected in the codebase. This typically indicates one of the following:
933
+
934
+ 1. **Simple request-response pattern** — The system uses straightforward HTTP request/response patterns without complex pipelines
935
+ 2. **Event-driven architecture** — Data flows through event listeners that are harder to detect statically
936
+ 3. **External orchestration** — Flow control happens outside this codebase (e.g., in a parent service or message queue)
937
+
938
+ ### Recommendations
939
+
940
+ To help RepoLens detect flows more accurately, consider:
941
+
942
+ - Adding descriptive naming conventions (e.g., \`processOrder\`, \`handlePayment\`)
943
+ - Using consistent file patterns for flow entry points
944
+ - Documenting flows explicitly in code comments
945
+
946
+ `;
947
+
948
+ // Even without flows, show import network info
949
+ if (depGraph?.stats) {
950
+ output += `## Import Network Analysis
951
+
952
+ Even without identified flows, we can understand data movement through the import graph:
953
+
954
+ | Metric | Value | Insight |
955
+ |--------|-------|---------|
956
+ | Internal Imports | ${depGraph.stats.totalEdges} | Data can flow along ${depGraph.stats.totalEdges} import edges |
957
+ | Total Files | ${depGraph.stats.totalFiles} | ${depGraph.stats.totalFiles} potential processing points |
958
+ | Hub Modules | ${depGraph.stats.hubs?.length || 0} | Central integration points |
959
+
960
+ `;
961
+ if (depGraph.stats.hubs && depGraph.stats.hubs.length > 0) {
962
+ output += `### Likely Integration Points\n\n`;
963
+ output += `These highly-imported modules are probable data aggregation points:\n\n`;
964
+ for (const hub of depGraph.stats.hubs.slice(0, 5)) {
965
+ const shortName = hub.key.split("/").pop();
966
+ const role = describeOnboardingModule(hub.key);
967
+ output += `- **\`${shortName}\`** — ${hub.importedBy} consumers — ${role}\n`;
968
+ }
969
+ output += "\n";
970
+ }
971
+ }
972
+
973
+ output += `---
974
+
975
+ *Flow detection is based on naming conventions and import patterns. Enable AI enhancement with \`GITHUB_TOKEN\` for intelligent flow inference from code structure, even when explicit patterns are absent.*`;
563
976
  return output;
564
977
  }
565
978
 
566
- output += `**${allFlows.length} flow${allFlows.length === 1 ? "" : "s"} detected** in the codebase.\n\n---\n\n`;
567
-
979
+ // Flow summary table
980
+ output += `## Flow Summary\n\n`;
981
+ output += `**${allFlows.length} data flow${allFlows.length === 1 ? '' : 's'}** identified in the codebase:\n\n`;
982
+ output += `| Flow | Type | Complexity | Critical |\n`;
983
+ output += `|------|------|------------|----------|\n`;
568
984
  for (const flow of allFlows) {
569
- output += `## ${flow.name}\n\n`;
985
+ const stepCount = flow.steps?.length || 0;
986
+ const complexity = stepCount > 5 ? "High" : stepCount > 2 ? "Medium" : "Low";
987
+ const critical = flow.critical ? "🔴 Yes" : "—";
988
+ output += `| ${flow.name} | ${flow.type || "Integration"} | ${complexity} (${stepCount} steps) | ${critical} |\n`;
989
+ }
990
+ output += "\n---\n\n";
991
+
992
+ // Detailed flow documentation
993
+ for (const [index, flow] of allFlows.entries()) {
994
+ const stepCount = flow.steps?.length || 0;
995
+ const criticality = flow.critical ? "🔴 **CRITICAL FLOW**" : "";
996
+
997
+ output += `## ${index + 1}. ${flow.name} ${criticality}\n\n`;
998
+ output += `### Description\n\n`;
570
999
  output += `${flow.description}\n\n`;
1000
+
1001
+ // Flow characteristics
1002
+ output += `### Characteristics\n\n`;
1003
+ output += `| Property | Value |\n`;
1004
+ output += `|----------|-------|\n`;
1005
+ output += `| Steps | ${stepCount} |\n`;
1006
+ output += `| Modules Involved | ${(flow.modules || []).length} |\n`;
1007
+ output += `| Criticality | ${flow.critical ? "Critical" : "Standard"} |\n`;
1008
+
1009
+ // Step-by-step breakdown
571
1010
  if (flow.steps && flow.steps.length > 0) {
572
- output += `| Step | Action |\n`;
573
- output += `|------|--------|\n`;
574
- output += flow.steps.map((s, i) => `| ${i + 1} | ${s} |`).join("\n");
575
- output += `\n\n`;
1011
+ output += `\n### Processing Steps\n\n`;
1012
+ output += `| Step | Action | Purpose |\n`;
1013
+ output += `|------|--------|----------|\n`;
1014
+ for (const [i, step] of flow.steps.entries()) {
1015
+ const purpose = inferStepPurpose(step);
1016
+ output += `| ${i + 1} | ${step} | ${purpose} |\n`;
1017
+ }
1018
+ output += "\n";
1019
+
1020
+ // Flow diagram (text-based)
1021
+ output += `### Flow Diagram\n\n`;
1022
+ output += "```\n";
1023
+ for (const [i, step] of flow.steps.entries()) {
1024
+ const shortStep = step.length > 50 ? step.substring(0, 47) + "..." : step;
1025
+ if (i === 0) {
1026
+ output += `┌─ START ─┐\n`;
1027
+ output += `│ ${shortStep.padEnd(50)} │\n`;
1028
+ } else if (i === flow.steps.length - 1) {
1029
+ output += `│ ↓\n`;
1030
+ output += `│ ${shortStep.padEnd(50)} │\n`;
1031
+ output += `└─ END ───┘\n`;
1032
+ } else {
1033
+ output += `│ ↓\n`;
1034
+ output += `│ ${shortStep.padEnd(50)} │\n`;
1035
+ }
1036
+ }
1037
+ output += "```\n\n";
576
1038
  }
1039
+
1040
+ // Involved modules
577
1041
  if (flow.modules && flow.modules.length > 0) {
578
- output += `**Involved modules:** ${flow.modules.map(m => `\`${m}\``).join(", ")}\n\n`;
1042
+ output += `### Involved Modules\n\n`;
1043
+ output += `| Module | Role |\n`;
1044
+ output += `|--------|------|\n`;
1045
+ for (const mod of flow.modules.slice(0, 8)) {
1046
+ const shortMod = mod.split("/").pop();
1047
+ const role = describeOnboardingModule(mod);
1048
+ output += `| \`${shortMod}\` | ${role} |\n`;
1049
+ }
1050
+ if (flow.modules.length > 8) {
1051
+ output += `| *+${flow.modules.length - 8} more* | Additional modules |\n`;
1052
+ }
1053
+ output += "\n";
579
1054
  }
580
1055
 
581
- // Add dependency context if available
1056
+ // Dependency context
582
1057
  if (scanResult && flow.modules) {
583
1058
  const deps = identifyFlowDependencies(flow, scanResult);
584
- if (deps.sharedLibraries.length > 0) {
585
- output += `**Shared libraries used:** ${deps.sharedLibraries.map(m => `\`${m}\``).join(", ")}\n\n`;
586
- }
587
- if (deps.externalDependencies.length > 0) {
588
- output += `**External services:** ${deps.externalDependencies.join(", ")}\n\n`;
1059
+ if (deps.sharedLibraries.length > 0 || deps.externalDependencies.length > 0) {
1060
+ output += `### Dependencies\n\n`;
1061
+ if (deps.sharedLibraries.length > 0) {
1062
+ output += `**Shared libraries used:** ${deps.sharedLibraries.map(m => `\`${m}\``).join(", ")}\n\n`;
1063
+ }
1064
+ if (deps.externalDependencies.length > 0) {
1065
+ output += `**External services:** ${deps.externalDependencies.join(", ")}\n\n`;
1066
+ }
589
1067
  }
590
1068
  }
1069
+
1070
+ // Flow insights
1071
+ output += `### Insights\n\n`;
1072
+ if (flow.critical) {
1073
+ output += `- ⚠️ **Critical path** — Failures here have significant business impact\n`;
1074
+ output += `- 📊 Consider adding monitoring and alerting\n`;
1075
+ output += `- 🧪 Ensure comprehensive test coverage\n`;
1076
+ } else {
1077
+ output += `- Standard integration flow with ${stepCount} processing step${stepCount === 1 ? '' : 's'}\n`;
1078
+ }
1079
+ output += "\n---\n\n";
591
1080
  }
592
1081
 
593
- // Import chain summary
1082
+ // Import network analysis
594
1083
  if (depGraph?.stats) {
595
1084
  output += `## Import Network\n\n`;
596
1085
  output += `The system has **${depGraph.stats.totalEdges} internal import edges** connecting ${depGraph.stats.totalFiles} source files.\n\n`;
1086
+
1087
+ output += `### Network Topology\n\n`;
1088
+ output += `| Metric | Value | Meaning |\n`;
1089
+ output += `|--------|-------|----------|\n`;
1090
+ output += `| Total Edges | ${depGraph.stats.totalEdges} | Direct import relationships |\n`;
1091
+ output += `| Hub Count | ${depGraph.stats.hubs?.length || 0} | Central integration points |\n`;
1092
+ output += `| Avg Connections | ~${Math.round(depGraph.stats.totalEdges / Math.max(depGraph.stats.totalFiles, 1) * 10) / 10} per file | Coupling density |\n`;
1093
+
597
1094
  if (depGraph.stats.hubs && depGraph.stats.hubs.length > 0) {
598
- output += `Key integration points (most referenced modules):\n\n`;
1095
+ output += `\n### Key Integration Points\n\n`;
1096
+ output += `These modules aggregate data from multiple sources:\n\n`;
1097
+ output += `| Hub | Inbound | Role |\n`;
1098
+ output += `|-----|---------|------|\n`;
599
1099
  for (const hub of depGraph.stats.hubs.slice(0, 5)) {
600
- output += `- \`${hub.key}\` — referenced by ${hub.importedBy} files\n`;
1100
+ const shortName = hub.key.split("/").pop();
1101
+ const role = describeOnboardingModule(hub.key);
1102
+ output += `| \`${shortName}\` | ${hub.importedBy} files | ${role} |\n`;
601
1103
  }
602
1104
  output += "\n";
603
1105
  }
604
1106
  }
605
1107
 
606
- output += `---\n\n*Flow detection is based on naming conventions and import patterns. Set \`GITHUB_TOKEN\` to auto-enable AI via GitHub Models (free) \u2014 adds end-to-end flow narratives, failure mode analysis, and data transformation descriptions.*`;
1108
+ output += `---
1109
+
1110
+ *Flow detection uses naming conventions, import patterns, and dependency graph analysis. Enable AI enhancement with \`GITHUB_TOKEN\` for end-to-end flow narratives, failure mode analysis, data transformation descriptions, and performance bottleneck identification.*`;
607
1111
 
608
1112
  return output;
609
1113
  }
@@ -614,108 +1118,254 @@ function getFallbackDeveloperOnboarding(context, enrichment = {}) {
614
1118
  const languageList = context.techStack.languages.join(", ") || "standard languages";
615
1119
  const testFrameworks = context.techStack.testFrameworks || [];
616
1120
  const isCLI = (context.patterns || []).some(p => p.toLowerCase().includes("cli"));
1121
+ const projectName = context.project.name || "this project";
617
1122
  const routes = context.routes || {};
618
1123
  const pages = routes.pages || [];
619
1124
  const apis = routes.apis || [];
620
1125
 
621
- let output = `# Developer Onboarding
1126
+ let output = `# Developer Onboarding Guide
1127
+
1128
+ ## Welcome to ${projectName}! 👋
622
1129
 
623
- ## Welcome
1130
+ This guide will help you get up to speed quickly. **${projectName}** is a ${frameworkList} project built with ${languageList}, containing **${context.project.modulesDetected} modules** across **${context.project.filesScanned} files**.
624
1131
 
625
- This guide helps new contributors get oriented in the **${context.project.name}** repository. The project is built with ${frameworkList} and ${languageList}, containing **${context.project.modulesDetected} modules** across **${context.project.filesScanned} files**.
1132
+ ### What You'll Learn
1133
+
1134
+ 1. ✅ Repository structure and organization
1135
+ 2. ✅ Technology stack and tools
1136
+ 3. ✅ Key modules to understand first
1137
+ 4. ✅ How data flows through the system
1138
+ 5. ✅ How to start contributing
1139
+
1140
+ ---
1141
+
1142
+ ## Quick Reference Card
1143
+
1144
+ | Aspect | Details |
1145
+ |--------|---------|
1146
+ | **Languages** | ${languageList} |
1147
+ | **Frameworks** | ${frameworkList} |
1148
+ | **Build Tools** | ${context.techStack.buildTools.join(", ") || "Standard toolchain"} |
1149
+ ${testFrameworks.length > 0 ? `| **Testing** | ${testFrameworks.join(", ")} |\n` : ""}| **Size** | ${context.project.modulesDetected} modules, ${context.project.filesScanned} files |
1150
+ | **Domains** | ${context.domains.length} functional areas |
1151
+ ${isCLI ? "| **Type** | Command-line interface |\n" : ""}${context.project.apiRoutesDetected > 0 ? `| **API** | ${context.project.apiRoutesDetected} endpoints |\n` : ""}${context.project.pagesDetected > 0 ? `| **UI** | ${context.project.pagesDetected} pages |\n` : ""}
1152
+
1153
+ ---
626
1154
 
627
1155
  ## Repository Structure
628
1156
 
629
- The top-level directory is organized as follows:
1157
+ Understanding the directory layout is your first step to navigating the codebase:
1158
+
1159
+ | Directory | Purpose | Start Here? |
1160
+ |-----------|---------|-------------|
1161
+ ${context.repoRoots.map(root => {
1162
+ const purpose = describeRoot(root);
1163
+ const startHere = shouldStartHere(root) ? "⭐ Yes" : "—";
1164
+ return `| \`${root}\` | ${purpose} | ${startHere} |`;
1165
+ }).join("\n")}
1166
+
1167
+ ### Where to Begin
630
1168
 
631
- | Directory | Purpose |
632
- |-----------|---------|
633
- ${context.repoRoots.map(root => `| \`${root}\` | ${describeRoot(root)} |`).join("\n")}
634
1169
  `;
1170
+
1171
+ const entryPoints = context.repoRoots.filter(r => shouldStartHere(r));
1172
+ if (entryPoints.length > 0) {
1173
+ output += `Focus on these directories first:\n\n`;
1174
+ for (const ep of entryPoints) {
1175
+ const purpose = describeRoot(ep);
1176
+ output += `1. **\`${ep}\`** — ${purpose}\n`;
1177
+ }
1178
+ } else {
1179
+ output += `Start by exploring the main \`src/\` directory or look for entry point files like \`index.js\`, \`main.js\`, or \`app.js\`.\n`;
1180
+ }
635
1181
 
636
1182
  // Monorepo navigation
637
1183
  if (context.monorepo) {
638
- output += `\n## Monorepo Navigation\n\n`;
639
- output += `This is a **${context.monorepo.tool}** monorepo. Key packages:\n\n`;
1184
+ output += `\n---\n\n## Monorepo Navigation\n\n`;
1185
+ output += `This is a **${context.monorepo.tool}** monorepo with ${context.monorepo.packageCount} packages. Here's how to navigate:\n\n`;
1186
+ output += `| Package | Location | What It Does |\n`;
1187
+ output += `|---------|----------|-------------|\n`;
640
1188
  for (const pkg of context.monorepo.packages.slice(0, 10)) {
641
- output += `- \`${pkg.name}\` — \`${pkg.path}\`\n`;
1189
+ const desc = inferPackageDescription(pkg.name);
1190
+ output += `| \`${pkg.name}\` | \`${pkg.path}\` | ${desc} |\n`;
642
1191
  }
643
- output += "\n";
1192
+ if (context.monorepo.packages.length > 10) {
1193
+ output += `| *+${context.monorepo.packages.length - 10} more* | — | Additional packages |\n`;
1194
+ }
1195
+ output += `\n**Pro tip:** Each package can typically be developed independently. Check the package's own \`README.md\` for specific setup instructions.\n`;
644
1196
  }
645
1197
 
646
- output += `## Technology Stack
647
-
648
- | Category | Technologies |
649
- |----------|-------------|
650
- | Frameworks | ${context.techStack.frameworks.join(", ") || (isCLI ? "N/A (CLI tool)" : "Not detected")} |
651
- | Languages | ${context.techStack.languages.join(", ") || "Not detected"} |
652
- | Build Tools | ${context.techStack.buildTools.join(", ") || (isCLI ? "N/A (CLI tool)" : "Not detected")} |
653
- ${testFrameworks.length > 0 ? `| Test Frameworks | ${testFrameworks.join(", ")} |\n` : ""}
654
- ## Largest Modules
1198
+ // Technology deep-dive
1199
+ output += `\n---\n\n## Technology Stack\n\n`;
1200
+ output += `| Layer | Technologies | What to Learn |\n`;
1201
+ output += `|-------|-------------|---------------|\n`;
1202
+
1203
+ if (context.techStack.frameworks.length > 0) {
1204
+ const frameworks = context.techStack.frameworks;
1205
+ const learnTip = getFrameworkLearningTip(frameworks);
1206
+ output += `| **Framework** | ${frameworks.join(", ")} | ${learnTip} |\n`;
1207
+ }
1208
+ output += `| **Language** | ${languageList} | ${getLanguageLearningTip(context.techStack.languages)} |\n`;
1209
+ if (context.techStack.buildTools.length > 0) {
1210
+ output += `| **Build** | ${context.techStack.buildTools.join(", ")} | Understand the build pipeline |\n`;
1211
+ }
1212
+ if (testFrameworks.length > 0) {
1213
+ output += `| **Testing** | ${testFrameworks.join(", ")} | Run existing tests to understand behavior |\n`;
1214
+ }
655
1215
 
656
- These are the primary modules by file count — good starting points for understanding the system:
1216
+ // Core modules to understand
1217
+ output += `\n---\n\n## Core Modules to Understand\n\n`;
1218
+ output += `These are the most important modules by size and centrality. Understanding them will unlock the rest of the codebase:\n\n`;
1219
+ output += `| Priority | Module | Files | Type | Why It Matters |\n`;
1220
+ output += `|----------|--------|-------|------|----------------|\n`;
1221
+
1222
+ const priorityModules = context.topModules.slice(0, 10);
1223
+ for (const [i, m] of priorityModules.entries()) {
1224
+ const priority = i < 3 ? "⭐ High" : i < 6 ? "Medium" : "Low";
1225
+ const whyMatters = getModuleImportance(m.key, m.type, m.fileCount);
1226
+ output += `| ${priority} | \`${m.key}\` | ${m.fileCount} | ${formatModuleType(m.type)} | ${whyMatters} |\n`;
1227
+ }
657
1228
 
658
- | Module | Files | Type |
659
- |--------|-------|------|
660
- ${context.topModules.slice(0, 10).map(m => `| \`${m.key}\` | ${m.fileCount} | ${formatModuleType(m.type)} |`).join("\n")}
661
- `;
1229
+ // Hub modules (most imported)
1230
+ if (depGraph?.stats?.hubs && depGraph.stats.hubs.length > 0) {
1231
+ output += `\n### Key Integration Points\n\n`;
1232
+ output += `These modules are imported by many other files. Changes here have wide impact:\n\n`;
1233
+ for (const hub of depGraph.stats.hubs.slice(0, 5)) {
1234
+ const role = describeOnboardingModule(hub.key);
1235
+ output += `- **\`${hub.key}\`** — Used by ${hub.importedBy} files — ${role}\n`;
1236
+ }
1237
+ output += `\n⚠️ **Caution:** Be extra careful when modifying these files. Consider the ripple effects.\n`;
1238
+ }
662
1239
 
663
- // Key routes to explore
1240
+ // Routes exploration
664
1241
  if (pages.length > 0 || apis.length > 0) {
665
- output += `\n## Key Routes\n\n`;
1242
+ output += `\n---\n\n## Key Routes\n\n`;
1243
+ output += `Understanding the routing structure helps you trace user interactions through the system.\n\n`;
1244
+
666
1245
  if (pages.length > 0) {
667
- output += `**Pages to explore:**\n\n`;
668
- for (const p of pages.slice(0, 5)) {
669
- output += `- \`${p.path}\` — ${p.file}\n`;
1246
+ output += `### User-Facing Pages\n\n`;
1247
+ output += `| Path | File | What It Does |\n`;
1248
+ output += `|------|------|--------------|\n`;
1249
+ for (const p of pages.slice(0, 6)) {
1250
+ const pageName = p.path.split('/').pop() || 'root';
1251
+ const desc = inferPageDescription(pageName);
1252
+ output += `| \`${p.path}\` | ${p.file} | ${desc} |\n`;
1253
+ }
1254
+ if (pages.length > 6) {
1255
+ output += `| *+${pages.length - 6} more* | — | Additional pages |\n`;
670
1256
  }
671
1257
  output += "\n";
672
1258
  }
1259
+
673
1260
  if (apis.length > 0) {
674
- output += `**API endpoints:**\n\n`;
675
- for (const a of apis.slice(0, 5)) {
676
- const methods = a.methods ? ` [${a.methods.join(", ")}]` : "";
677
- output += `- \`${a.path}\`${methods} ${a.file}\n`;
1261
+ output += `### API Endpoints\n\n`;
1262
+ output += `| Endpoint | Methods | File | Purpose |\n`;
1263
+ output += `|----------|---------|------|---------|\n`;
1264
+ for (const a of apis.slice(0, 6)) {
1265
+ const methods = a.methods?.join(", ") || "—";
1266
+ const purpose = inferAPIEndpointPurpose(a.path);
1267
+ output += `| \`${a.path}\` | ${methods} | ${a.file} | ${purpose} |\n`;
1268
+ }
1269
+ if (apis.length > 6) {
1270
+ output += `| *+${apis.length - 6} more* | — | — | Additional endpoints |\n`;
678
1271
  }
679
1272
  output += "\n";
680
1273
  }
681
1274
  }
682
1275
 
683
- // Data flows overview (filter out test flows)
1276
+ // Data flows for understanding behavior
684
1277
  const onboardingFlows = (flows || []).filter(f => !f.name?.toLowerCase().includes('test'));
685
1278
  if (onboardingFlows.length > 0) {
686
- output += `## How Data Flows\n\n`;
687
- output += `Understanding these flows will help you see how the system works end-to-end:\n\n`;
688
- for (const flow of onboardingFlows) {
689
- output += `- **${flow.name}** — ${flow.description}\n`;
1279
+ output += `---\n\n## How Data Flows\n\n`;
1280
+ output += `Understanding these flows helps you see how the system processes information end-to-end:\n\n`;
1281
+ output += `| Flow | Description | Key Modules |\n`;
1282
+ output += `|------|-------------|-------------|\n`;
1283
+ for (const flow of onboardingFlows.slice(0, 5)) {
1284
+ const keyModules = (flow.modules || []).slice(0, 2).map(m => `\`${m.split("/").pop()}\``).join(", ") || "—";
1285
+ output += `| **${flow.name}** | ${flow.description.substring(0, 60)}${flow.description.length > 60 ? "..." : ""} | ${keyModules} |\n`;
690
1286
  }
691
- output += "\n";
1287
+ output += `\n**Debugging tip:** When investigating issues, trace the data flow from input to output to identify where problems occur.\n`;
692
1288
  }
693
1289
 
694
- // Dependency orientation
695
- if (depGraph?.stats?.hubs && depGraph.stats.hubs.length > 0) {
696
- output += `## Key Integration Points\n\n`;
697
- output += `These modules are the most widely imported — changes to them may have broad impact:\n\n`;
698
- for (const hub of depGraph.stats.hubs.slice(0, 5)) {
699
- output += `- \`${hub.key}\` imported by ${hub.importedBy} files\n`;
700
- }
701
- output += "\n";
1290
+ // Getting started checklist
1291
+ output += `\n---\n\n## Getting Started Checklist\n\n`;
1292
+ output += `Follow these steps to set up your development environment:\n\n`;
1293
+
1294
+ let stepNum = 1;
1295
+ output += `### ${stepNum++}. Clone and Install\n\n`;
1296
+ output += "```bash\n";
1297
+ output += `git clone <repository-url>\n`;
1298
+ output += `cd ${projectName.replace(/[^a-zA-Z0-9-_]/g, '-').toLowerCase()}\n`;
1299
+ output += `npm install # or yarn, pnpm\n`;
1300
+ output += "```\n\n";
1301
+
1302
+ output += `### ${stepNum++}. Explore the Structure\n\n`;
1303
+ output += `- Review the directory structure above\n`;
1304
+ output += `- Open the \`README.md\` for project-specific instructions\n`;
1305
+ output += `- Look for \`CONTRIBUTING.md\` for contribution guidelines\n\n`;
1306
+
1307
+ output += `### ${stepNum++}. Understand Core Modules\n\n`;
1308
+ output += `Start with the high-priority modules listed above. Read the code and comments to understand:\n\n`;
1309
+ output += `- What problem each module solves\n`;
1310
+ output += `- How modules interact with each other\n`;
1311
+ output += `- What data structures are used\n\n`;
1312
+
1313
+ if (testFrameworks.length > 0) {
1314
+ output += `### ${stepNum++}. Run the Tests\n\n`;
1315
+ output += "```bash\n";
1316
+ output += `npm test # or: npx ${testFrameworks[0].toLowerCase()} run\n`;
1317
+ output += "```\n\n";
1318
+ output += `Running tests helps you:\n`;
1319
+ output += `- Verify your setup is correct\n`;
1320
+ output += `- See expected behavior documented in test cases\n`;
1321
+ output += `- Understand module interfaces through test examples\n\n`;
702
1322
  }
703
1323
 
704
- output += `## Getting Started
1324
+ output += `### ${stepNum++}. Make Your First Change\n\n`;
1325
+ output += `Start small:\n\n`;
1326
+ output += `1. Find a small bug or typo to fix\n`;
1327
+ output += `2. Make the change in a feature branch\n`;
1328
+ output += `3. Run tests to ensure nothing breaks\n`;
1329
+ output += `4. Submit a pull request for review\n\n`;
705
1330
 
706
- 1. Clone the repository and install dependencies
707
- 2. Review the top-level directories above to understand the project layout
708
- 3. Start with the largest modules listed above — they contain the core functionality
709
- 4. Check for a \`README.md\` or \`CONTRIBUTING.md\` file for project-specific setup instructions`;
1331
+ // Common pitfalls
1332
+ output += `---\n\n## Common Pitfalls\n\n`;
1333
+ output += `New contributors often run into these issues:\n\n`;
1334
+
1335
+ const pitfalls = [];
1336
+ if (depGraph?.stats?.cycles > 0) {
1337
+ pitfalls.push(`**Circular dependencies** — This codebase has ${depGraph.stats.cycles} circular import${depGraph.stats.cycles === 1 ? '' : 's'}. Be careful not to introduce more.`);
1338
+ }
1339
+ if (depGraph?.stats?.hubs?.length > 0 && depGraph.stats.hubs[0].importedBy >= depGraph.stats.totalFiles * 0.3) {
1340
+ pitfalls.push(`**High-impact modules** — \`${depGraph.stats.hubs[0].key.split("/").pop()}\` is used by ${Math.round(depGraph.stats.hubs[0].importedBy / depGraph.stats.totalFiles * 100)}% of the codebase. Changes here need thorough testing.`);
1341
+ }
1342
+ if (context.monorepo) {
1343
+ pitfalls.push(`**Monorepo boundaries** — Remember to build dependent packages when making changes.`);
1344
+ }
1345
+ pitfalls.push(`**Environment setup** — Check for \`.env.example\` files and ensure you have all required environment variables.`);
1346
+ pitfalls.push(`**Branch naming** — Follow the team's branch naming conventions (usually \`feature/\`, \`fix/\`, \`docs/\`).`);
1347
+
1348
+ for (const pitfall of pitfalls) {
1349
+ output += `- ${pitfall}\n`;
1350
+ }
710
1351
 
711
- // Test framework quickstart
712
- if (testFrameworks.length > 0) {
713
- output += `\n5. Run tests with your ${testFrameworks[0]} setup to verify everything works`;
1352
+ // Resources
1353
+ output += `\n---\n\n## Additional Resources\n\n`;
1354
+ output += `| Resource | Purpose |\n`;
1355
+ output += `|----------|----------|\n`;
1356
+ output += `| \`README.md\` | Project overview and quick start |\n`;
1357
+ output += `| \`CONTRIBUTING.md\` | Contribution guidelines (if exists) |\n`;
1358
+ output += `| \`CHANGELOG.md\` | Recent changes and release history |\n`;
1359
+ output += `| \`package.json\` | Available scripts and dependencies |\n`;
1360
+ if (context.monorepo) {
1361
+ output += `| Root \`package.json\` | Monorepo workspace configuration |\n`;
714
1362
  }
715
1363
 
716
- output += `\n\n---
1364
+ output += `\n---
717
1365
 
718
- *This onboarding guide was generated from repository structure analysis. Set \`GITHUB_TOKEN\` to auto-enable AI via GitHub Models (free) \u2014 adds narrative walkthroughs, common pitfalls, debugging tips, and contribution guidelines.*`;
1366
+ *This onboarding guide was generated from comprehensive repository analysis. Enable AI enhancement with \`GITHUB_TOKEN\` for narrative walkthroughs, common pitfall documentation, debugging tips, architecture deep-dives, and personalized contribution recommendations.*
1367
+
1368
+ **Welcome to the team! Happy coding! 🚀**`;
719
1369
 
720
1370
  return output;
721
1371
  }
@@ -831,3 +1481,305 @@ function describeOnboardingModule(key) {
831
1481
  if (/analyz/.test(lower)) return "Code analysis and inference";
832
1482
  return "Core application module";
833
1483
  }
1484
+
1485
+ // Helper: infer business capability from domain name
1486
+ function inferDomainCapability(name) {
1487
+ const lower = name.toLowerCase();
1488
+ if (/auth|identity|login|sso/.test(lower)) return "User identity verification and access control";
1489
+ if (/payment|billing|checkout|stripe/.test(lower)) return "Payment processing and financial transactions";
1490
+ if (/analytic|report|metric|dashboard/.test(lower)) return "Data analysis and business intelligence";
1491
+ if (/content|cms|article|post|blog/.test(lower)) return "Content creation and editorial workflow";
1492
+ if (/search|discovery|find|browse/.test(lower)) return "Search and content discovery";
1493
+ if (/notif|alert|email|sms|push/.test(lower)) return "User notifications and messaging";
1494
+ if (/api|endpoint|rest|graphql/.test(lower)) return "External API interface layer";
1495
+ if (/ui|component|widget|button/.test(lower)) return "Reusable visual interface elements";
1496
+ if (/hook|state|redux|store/.test(lower)) return "Application state and data flow management";
1497
+ if (/util|helper|shared|common/.test(lower)) return "Cross-cutting utility functions";
1498
+ if (/data|database|model|schema/.test(lower)) return "Data persistence and modeling";
1499
+ if (/config|setting|env/.test(lower)) return "Application configuration";
1500
+ if (/test|spec|mock/.test(lower)) return "Quality assurance and testing";
1501
+ if (/job|queue|worker|task/.test(lower)) return "Background processing and async tasks";
1502
+ if (/user|profile|account/.test(lower)) return "User profiles and preferences";
1503
+ if (/order|cart|product|inventory/.test(lower)) return "E-commerce and inventory management";
1504
+ if (/media|image|video|upload/.test(lower)) return "Media handling and file uploads";
1505
+ if (/cache|redis|memcache/.test(lower)) return "Performance optimization and caching";
1506
+ if (/security|audit|log/.test(lower)) return "Security monitoring and audit trails";
1507
+ if (/ai|ml|llm|model/.test(lower)) return "AI/ML capabilities and inference";
1508
+ if (/plugin|extension|addon/.test(lower)) return "Extensibility and plugin architecture";
1509
+ if (/render|template|view/.test(lower)) return "Output generation and templating";
1510
+ if (/publish|deploy|release/.test(lower)) return "Content distribution and deployment";
1511
+ if (/integrate|connect|sync/.test(lower)) return "Third-party system integration";
1512
+ return "Supporting business operations";
1513
+ }
1514
+
1515
+ // Helper: describe layer responsibility
1516
+ function getLayerResponsibility(type) {
1517
+ const responsibilities = {
1518
+ api: "Handles HTTP requests, input validation, and response formatting",
1519
+ ui: "Renders user interface elements and manages component state",
1520
+ library: "Provides shared utilities and abstractions used across modules",
1521
+ hooks: "Encapsulates reusable stateful logic and side effects",
1522
+ state: "Manages application-wide data synchronization",
1523
+ route: "Maps URL paths to page components and handles navigation",
1524
+ app: "Orchestrates application initialization and core bootstrapping",
1525
+ other: "General purpose functionality",
1526
+ };
1527
+ return responsibilities[type] || "Supporting application functionality";
1528
+ }
1529
+
1530
+ // Helper: calculate codebase health score
1531
+ function calculateHealthScore(stats, context) {
1532
+ let score = 100;
1533
+
1534
+ // Circular dependencies are a major concern
1535
+ if (stats.cycles > 0) score -= Math.min(30, stats.cycles * 10);
1536
+
1537
+ // Orphan files indicate potential dead code
1538
+ const orphanRatio = stats.orphanFiles / Math.max(stats.totalFiles, 1);
1539
+ if (orphanRatio > 0.2) score -= 20;
1540
+ else if (orphanRatio > 0.1) score -= 10;
1541
+
1542
+ // Too many external dependencies
1543
+ if (stats.externalDeps > 100) score -= 15;
1544
+ else if (stats.externalDeps > 50) score -= 5;
1545
+
1546
+ // No test framework detected
1547
+ const hasTests = (context.techStack.testFrameworks || []).length > 0;
1548
+ if (!hasTests) score -= 10;
1549
+
1550
+ // Give bonus for clean, connected codebase
1551
+ if (stats.cycles === 0 && stats.totalEdges > 0) score = Math.min(100, score + 5);
1552
+
1553
+ return Math.max(0, Math.min(100, score));
1554
+ }
1555
+
1556
+ // Helper: format file count with proper granularity descriptions
1557
+ function formatFileScale(count) {
1558
+ if (count < 20) return `${count} files (small project)`;
1559
+ if (count < 100) return `${count} files (medium project)`;
1560
+ if (count < 500) return `${count} files (large project)`;
1561
+ return `${count} files (enterprise scale)`;
1562
+ }
1563
+
1564
+ // Helper: describe route patterns
1565
+ function describeRoutePattern(routes) {
1566
+ if (!routes) return null;
1567
+
1568
+ // Handle routes object with pages and apis arrays
1569
+ const pages = routes.pages || [];
1570
+ const apis = routes.apis || [];
1571
+
1572
+ const patterns = [];
1573
+ if (apis.length > 0) {
1574
+ const methods = [...new Set(apis.map(r => r.methods || []).flat().filter(Boolean))];
1575
+ patterns.push(`${apis.length} API endpoint${apis.length === 1 ? '' : 's'}${methods.length > 0 ? ` (${methods.join(', ')})` : ''}`);
1576
+ }
1577
+ if (pages.length > 0) {
1578
+ patterns.push(`${pages.length} page route${pages.length === 1 ? '' : 's'}`);
1579
+ }
1580
+
1581
+ return patterns.length > 0 ? patterns.join(' and ') : null;
1582
+ }
1583
+
1584
+ // Helper: explain what a framework is used for
1585
+ function inferFrameworkPurpose(frameworks) {
1586
+ const purposes = [];
1587
+ for (const fw of frameworks) {
1588
+ const lower = fw.toLowerCase();
1589
+ if (/next\.?js/i.test(lower)) purposes.push("Full-stack React with SSR/SSG");
1590
+ else if (/react/i.test(lower)) purposes.push("Component-based UI rendering");
1591
+ else if (/vue/i.test(lower)) purposes.push("Progressive frontend framework");
1592
+ else if (/express/i.test(lower)) purposes.push("HTTP server and middleware");
1593
+ else if (/fastify/i.test(lower)) purposes.push("High-performance HTTP server");
1594
+ else if (/nest/i.test(lower)) purposes.push("Enterprise Node.js framework");
1595
+ else if (/angular/i.test(lower)) purposes.push("Opinionated frontend platform");
1596
+ else if (/svelte/i.test(lower)) purposes.push("Compile-time reactive UI");
1597
+ }
1598
+ return purposes.length > 0 ? purposes.join(", ") : "Application runtime";
1599
+ }
1600
+
1601
+ // Helper: explain an architectural pattern
1602
+ function explainPattern(pattern) {
1603
+ const lower = pattern.toLowerCase();
1604
+ if (/cli/i.test(lower)) return "The system operates as a command-line interface, accepting user input through terminal commands and producing structured output.";
1605
+ if (/monorepo/i.test(lower)) return "Multiple related packages are managed in a single repository, enabling coordinated development and shared tooling.";
1606
+ if (/microservice/i.test(lower)) return "The system is decomposed into independently deployable services communicating over network protocols.";
1607
+ if (/mvc|model.?view/i.test(lower)) return "The application separates data (Model), presentation (View), and business logic (Controller) into distinct layers.";
1608
+ if (/spa|single.?page/i.test(lower)) return "The UI is rendered client-side as a single-page application, with dynamic content updates without full page reloads.";
1609
+ if (/api.?first/i.test(lower)) return "The system is designed API-first, with a well-defined interface contract that drives both backend and frontend development.";
1610
+ if (/layered/i.test(lower)) return "The codebase is organized into horizontal layers (e.g., presentation, business, data) with clear boundaries.";
1611
+ if (/modular/i.test(lower)) return "Functionality is divided into self-contained modules with explicit interfaces and minimal coupling.";
1612
+ if (/plugin/i.test(lower)) return "The system supports extensibility through a plugin architecture allowing third-party additions.";
1613
+ return "A structured approach to organizing code and managing complexity.";
1614
+ }
1615
+
1616
+ // Helper: get benefits of a pattern
1617
+ function getBenefitsForPattern(pattern) {
1618
+ const lower = pattern.toLowerCase();
1619
+ if (/cli/i.test(lower)) return "Automation-friendly, scriptable, integrates with CI/CD pipelines";
1620
+ if (/monorepo/i.test(lower)) return "Shared tooling, atomic cross-package changes, unified versioning";
1621
+ if (/microservice/i.test(lower)) return "Independent deployment, tech flexibility, team autonomy";
1622
+ if (/mvc|model.?view/i.test(lower)) return "Separation of concerns, testability, parallel development";
1623
+ if (/spa|single.?page/i.test(lower)) return "Fast navigation, rich interactivity, offline capabilities";
1624
+ if (/api.?first/i.test(lower)) return "Clear contracts, parallel development, documentation-driven";
1625
+ if (/layered/i.test(lower)) return "Clear responsibilities, easier testing, technology swapping";
1626
+ if (/modular/i.test(lower)) return "Reusability, isolated testing, team ownership boundaries";
1627
+ if (/plugin/i.test(lower)) return "Extensibility, community contributions, core stability";
1628
+ return "Structured organization, maintainability, clarity";
1629
+ }
1630
+
1631
+ // Helper: insight for a layer/domain
1632
+ function getLayerInsight(name, fileCount, moduleCount) {
1633
+ const lower = name.toLowerCase();
1634
+ if (fileCount > 100) return "Large domain — consider breaking down";
1635
+ if (moduleCount === 1 && fileCount > 30) return "Single large module — evaluate decomposition";
1636
+ if (/util|helper|common|shared/i.test(lower)) return "Shared infrastructure — high reuse";
1637
+ if (/test/i.test(lower)) return "Quality assurance — critical for refactoring";
1638
+ if (/api|endpoint|route/i.test(lower)) return "External interface — versioning important";
1639
+ if (/auth/i.test(lower)) return "Security-critical — extra review needed";
1640
+ return "Standard domain";
1641
+ }
1642
+
1643
+ // Helper: architectural implication of frameworks
1644
+ function getFrameworkArchitecturalImplication(frameworks) {
1645
+ for (const fw of frameworks) {
1646
+ const lower = fw.toLowerCase();
1647
+ if (/next\.?js/i.test(lower)) return "File-based routing, hybrid rendering strategies";
1648
+ if (/react/i.test(lower)) return "Component tree, unidirectional data flow";
1649
+ if (/vue/i.test(lower)) return "Reactive data binding, component composition";
1650
+ if (/express|fastify/i.test(lower)) return "Middleware pipeline, request/response cycle";
1651
+ if (/nest/i.test(lower)) return "Decorators, dependency injection, modules";
1652
+ if (/angular/i.test(lower)) return "Module system, dependency injection, RxJS";
1653
+ }
1654
+ return "Standard application patterns";
1655
+ }
1656
+
1657
+ // Helper: architectural implication of languages
1658
+ function getLanguageImplication(languages) {
1659
+ const langs = (languages || []).map(l => l.toLowerCase());
1660
+ if (langs.includes("typescript")) return "Static typing enables tooling and refactoring confidence";
1661
+ if (langs.includes("javascript")) return "Dynamic typing, flexible but requires discipline";
1662
+ if (langs.includes("python")) return "Readable syntax, rich ecosystem";
1663
+ if (langs.includes("go")) return "Fast compilation, strong concurrency support";
1664
+ if (langs.includes("rust")) return "Memory safety, zero-cost abstractions";
1665
+ return "Standard language patterns";
1666
+ }
1667
+
1668
+ // Helper: describe a page from its name
1669
+ function inferPageDescription(pageName) {
1670
+ const lower = (pageName || "").toLowerCase();
1671
+ if (/^index$|^home$|^\/$/.test(lower)) return "Main entry page";
1672
+ if (/login|signin/i.test(lower)) return "User authentication";
1673
+ if (/register|signup/i.test(lower)) return "User registration";
1674
+ if (/dashboard/i.test(lower)) return "User dashboard overview";
1675
+ if (/settings/i.test(lower)) return "User preferences and settings";
1676
+ if (/profile/i.test(lower)) return "User profile management";
1677
+ if (/admin/i.test(lower)) return "Administrative interface";
1678
+ if (/search/i.test(lower)) return "Search functionality";
1679
+ if (/about/i.test(lower)) return "About/information page";
1680
+ if (/contact/i.test(lower)) return "Contact form or information";
1681
+ if (/help|faq|support/i.test(lower)) return "Help and support";
1682
+ if (/blog|post|article/i.test(lower)) return "Content display";
1683
+ if (/cart|checkout/i.test(lower)) return "Shopping cart or checkout";
1684
+ if (/order/i.test(lower)) return "Order management";
1685
+ if (/product/i.test(lower)) return "Product display";
1686
+ if (/404|error|not.?found/i.test(lower)) return "Error handling page";
1687
+ return "Application page";
1688
+ }
1689
+
1690
+ // Helper: describe an API endpoint from its path
1691
+ function inferAPIEndpointPurpose(path) {
1692
+ const lower = (path || "").toLowerCase();
1693
+ if (/\/auth\/|\/login|\/signin/i.test(lower)) return "Authentication";
1694
+ if (/\/user/i.test(lower)) return "User management";
1695
+ if (/\/admin/i.test(lower)) return "Administrative operations";
1696
+ if (/\/search/i.test(lower)) return "Search functionality";
1697
+ if (/\/upload|\/file/i.test(lower)) return "File operations";
1698
+ if (/\/payment|\/checkout|\/order/i.test(lower)) return "Transaction processing";
1699
+ if (/\/webhook/i.test(lower)) return "External integrations";
1700
+ if (/\/health|\/status|\/ping/i.test(lower)) return "Health monitoring";
1701
+ if (/\/config|\/setting/i.test(lower)) return "Configuration";
1702
+ if (/\/analytic|\/metric|\/stat/i.test(lower)) return "Analytics and metrics";
1703
+ if (/\/notify|\/email|\/sms/i.test(lower)) return "Notifications";
1704
+ if (/\/export|\/download/i.test(lower)) return "Data export";
1705
+ if (/\/import/i.test(lower)) return "Data import";
1706
+ return "Data operations";
1707
+ }
1708
+
1709
+ // Helper: describe a package from its name
1710
+ function inferPackageDescription(name) {
1711
+ const lower = (name || "").toLowerCase();
1712
+ if (/core|main|base/i.test(lower)) return "Core functionality and shared code";
1713
+ if (/cli|command/i.test(lower)) return "Command-line interface";
1714
+ if (/api|server|backend/i.test(lower)) return "Backend/API server";
1715
+ if (/web|app|frontend|client/i.test(lower)) return "Frontend application";
1716
+ if (/ui|component/i.test(lower)) return "Shared UI components";
1717
+ if (/util|common|shared|lib/i.test(lower)) return "Utility libraries";
1718
+ if (/config/i.test(lower)) return "Configuration management";
1719
+ if (/test|spec/i.test(lower)) return "Testing utilities";
1720
+ if (/doc/i.test(lower)) return "Documentation";
1721
+ if (/type|schema/i.test(lower)) return "Type definitions and schemas";
1722
+ if (/plugin|extension/i.test(lower)) return "Extension/plugin support";
1723
+ return "Package functionality";
1724
+ }
1725
+
1726
+ // Helper: infer the purpose of a flow step
1727
+ function inferStepPurpose(step) {
1728
+ const lower = (step || "").toLowerCase();
1729
+ if (/import|load|read|fetch|get/i.test(lower)) return "Data acquisition";
1730
+ if (/valid|check|verify/i.test(lower)) return "Input validation";
1731
+ if (/transform|convert|map|parse/i.test(lower)) return "Data transformation";
1732
+ if (/save|write|store|persist|create|update|delete/i.test(lower)) return "Data persistence";
1733
+ if (/send|emit|dispatch|notify|publish/i.test(lower)) return "Event/message dispatch";
1734
+ if (/render|display|show|format/i.test(lower)) return "Output generation";
1735
+ if (/auth|login|permission/i.test(lower)) return "Security check";
1736
+ if (/cache|memo/i.test(lower)) return "Performance optimization";
1737
+ if (/log|track|metric/i.test(lower)) return "Observability";
1738
+ return "Processing step";
1739
+ }
1740
+
1741
+ // Helper: determine if a directory is a good starting point
1742
+ function shouldStartHere(root) {
1743
+ const lower = (root || "").toLowerCase().replace(/\/$/, "");
1744
+ const lastSeg = lower.split("/").pop();
1745
+ // Good starting points
1746
+ if (/^src$|^lib$|^app$/i.test(lastSeg)) return true;
1747
+ if (/^core$|^main$/.test(lastSeg)) return true;
1748
+ // Not entry points
1749
+ if (/^test|^spec|^__test|^doc|^dist|^build|^node_module|^\./.test(lastSeg)) return false;
1750
+ return false;
1751
+ }
1752
+
1753
+ // Helper: framework learning tip
1754
+ function getFrameworkLearningTip(frameworks) {
1755
+ for (const fw of frameworks) {
1756
+ const lower = fw.toLowerCase();
1757
+ if (/next\.?js/i.test(lower)) return "Learn the pages/ directory structure and data fetching methods";
1758
+ if (/react/i.test(lower)) return "Understand component lifecycle and hooks";
1759
+ if (/vue/i.test(lower)) return "Learn the Options API or Composition API patterns";
1760
+ if (/express/i.test(lower)) return "Understand middleware chains and route handling";
1761
+ if (/nest/i.test(lower)) return "Learn decorators, modules, and dependency injection";
1762
+ if (/angular/i.test(lower)) return "Understand modules, components, and services";
1763
+ }
1764
+ return "Review the framework documentation";
1765
+ }
1766
+
1767
+ // Helper: language learning tip
1768
+ function getLanguageLearningTip(languages) {
1769
+ const langs = (languages || []).map(l => l.toLowerCase());
1770
+ if (langs.includes("typescript")) return "Strong typing — check types when debugging";
1771
+ if (langs.includes("javascript")) return "Dynamic typing — use console.log for runtime inspection";
1772
+ if (langs.includes("python")) return "Read docstrings and type hints";
1773
+ return "Review language idioms used in the codebase";
1774
+ }
1775
+
1776
+ // Helper: explain why a module matters for onboarding
1777
+ function getModuleImportance(key, type, fileCount) {
1778
+ if (type === "api") return "External interface — understand inputs/outputs";
1779
+ if (type === "app" || type === "state") return "Central logic — understand data flow";
1780
+ if (type === "ui") return "User experience — see visible behavior";
1781
+ if (type === "library") return "Shared code — reused throughout";
1782
+ if (type === "hooks") return "Business logic — encapsulates state patterns";
1783
+ if (fileCount > 20) return "Large module — core functionality";
1784
+ return "Key system component";
1785
+ }