@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.
- package/CHANGELOG.md +36 -0
- package/package.json +1 -1
- package/src/ai/generate-sections.js +1224 -276
|
@@ -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
|
-
//
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
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
|
-
##
|
|
174
|
+
## At a Glance
|
|
173
175
|
|
|
174
|
-
|
|
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
|
-
${
|
|
187
|
+
**${projectName}** is a ${frameworkList} ${projectType} built with ${languageList}. `;
|
|
177
188
|
|
|
178
|
-
|
|
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
|
-
|
|
201
|
+
output += `\n\n## Business Capabilities
|
|
181
202
|
|
|
182
|
-
|
|
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
|
-
|
|
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
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
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
|
|
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 +=
|
|
200
|
-
output +=
|
|
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,
|
|
203
|
-
|
|
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
|
|
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}
|
|
211
|
-
output +=
|
|
212
|
-
output +=
|
|
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
|
-
//
|
|
258
|
+
// Health assessment section
|
|
259
|
+
output += `\n## Codebase Health\n\n`;
|
|
260
|
+
|
|
216
261
|
if (depGraph?.stats) {
|
|
217
262
|
const stats = depGraph.stats;
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
output +=
|
|
222
|
-
output += `|
|
|
223
|
-
output +=
|
|
224
|
-
output += `|
|
|
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 +=
|
|
277
|
+
output += `- ✅ **Clean module boundaries** — No circular dependencies detected, enabling safe refactoring and testing.\n`;
|
|
227
278
|
} else {
|
|
228
|
-
output +=
|
|
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
|
|
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 +=
|
|
237
|
-
for (const flow of summaryFlows) {
|
|
238
|
-
|
|
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
|
-
|
|
243
|
-
|
|
244
|
-
|
|
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
|
|
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
|
-
##
|
|
352
|
+
## The Big Picture
|
|
263
353
|
|
|
264
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
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
|
|
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
|
|
291
|
-
|
|
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
|
-
|
|
296
|
-
|
|
297
|
-
The following domains represent the largest areas
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
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
|
|
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 +=
|
|
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
|
-
|
|
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 +=
|
|
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
|
|
322
|
-
|
|
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
|
|
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 +=
|
|
334
|
-
output +=
|
|
335
|
-
output +=
|
|
336
|
-
output += `|
|
|
337
|
-
output += `|
|
|
338
|
-
output += `|
|
|
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 += `
|
|
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
|
|
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
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
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 +=
|
|
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
|
|
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 +=
|
|
394
|
-
|
|
395
|
-
output +=
|
|
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
|
-
|
|
398
|
-
|
|
399
|
-
|
|
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
|
|
409
|
-
|
|
410
|
-
|
|
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
|
-
|
|
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
|
|
424
|
-
|
|
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
|
|
430
|
-
|
|
431
|
-
${patternDesc}
|
|
677
|
+
## Architectural Philosophy
|
|
432
678
|
|
|
433
|
-
|
|
679
|
+
`;
|
|
434
680
|
|
|
435
|
-
|
|
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
|
-
|
|
438
|
-
|
|
439
|
-
|
|
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
|
|
720
|
+
// Module architecture
|
|
443
721
|
const typeGroups = groupModulesByType(context.topModules);
|
|
444
|
-
if (Object.keys(typeGroups).length >
|
|
445
|
-
output +=
|
|
446
|
-
output += `Modules are classified into architectural
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
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
|
-
|
|
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
|
|
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 +=
|
|
474
|
-
|
|
475
|
-
output += `|
|
|
476
|
-
output +=
|
|
477
|
-
output += `|
|
|
478
|
-
output += `|
|
|
479
|
-
|
|
480
|
-
|
|
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
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
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 +=
|
|
501
|
-
for (const c of concerns)
|
|
502
|
-
|
|
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 +=
|
|
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
|
-
//
|
|
853
|
+
// Architecture drift
|
|
515
854
|
if (driftResult?.drifts && driftResult.drifts.length > 0) {
|
|
516
|
-
output += `## Architecture Drift\n\n`;
|
|
517
|
-
output +=
|
|
518
|
-
|
|
519
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
545
|
-
|
|
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 +=
|
|
562
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 +=
|
|
573
|
-
output +=
|
|
574
|
-
output +=
|
|
575
|
-
|
|
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 +=
|
|
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
|
-
//
|
|
1052
|
+
// Dependency context
|
|
582
1053
|
if (scanResult && flow.modules) {
|
|
583
1054
|
const deps = identifyFlowDependencies(flow, scanResult);
|
|
584
|
-
if (deps.sharedLibraries.length > 0) {
|
|
585
|
-
output +=
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
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
|
|
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 +=
|
|
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
|
-
|
|
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 +=
|
|
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
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
1185
|
+
const desc = inferPackageDescription(pkg.name);
|
|
1186
|
+
output += `| \`${pkg.name}\` | \`${pkg.path}\` | ${desc} |\n`;
|
|
642
1187
|
}
|
|
643
|
-
|
|
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
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
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
|
-
|
|
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
|
-
|
|
659
|
-
|
|
660
|
-
|
|
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
|
-
//
|
|
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 +=
|
|
668
|
-
|
|
669
|
-
|
|
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 +=
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
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
|
|
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 +=
|
|
687
|
-
output += `Understanding these flows
|
|
688
|
-
|
|
689
|
-
|
|
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 +=
|
|
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
|
-
//
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
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 +=
|
|
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
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
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
|
-
//
|
|
712
|
-
|
|
713
|
-
|
|
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
|
|
1360
|
+
output += `\n---
|
|
717
1361
|
|
|
718
|
-
*This onboarding guide was generated from repository
|
|
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
|
+
}
|