@chappibunny/repolens 1.9.2 → 1.9.3

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