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