@chappibunny/repolens 1.3.1 โ 1.5.0
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 +38 -0
- package/LICENSE +21 -0
- package/README.md +27 -2
- package/package.json +1 -1
- package/src/ai/generate-sections.js +77 -97
- package/src/ai/prompts.js +175 -10
- package/src/ai/provider.js +213 -7
- package/src/analyzers/codeowners.js +146 -0
- package/src/analyzers/context-builder.js +11 -1
- package/src/analyzers/domain-inference.js +37 -22
- package/src/analyzers/monorepo-detector.js +155 -0
- package/src/cli.js +2 -2
- package/src/core/scan.js +5 -0
- package/src/docs/generate-doc-set.js +14 -4
- package/src/doctor.js +51 -0
- package/src/integrations/discord.js +3 -3
- package/src/publishers/confluence.js +9 -2
- package/src/publishers/index.js +16 -3
- package/src/publishers/markdown.js +10 -1
- package/src/publishers/notion.js +95 -20
- package/src/renderers/render.js +48 -5
- package/src/renderers/renderDiff.js +18 -0
- package/src/renderers/renderMap.js +75 -60
- package/src/utils/doc-cache.js +78 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,44 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to RepoLens will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## 1.5.0
|
|
6
|
+
|
|
7
|
+
### ๐ New Features (Tier 3 โ Differentiation)
|
|
8
|
+
|
|
9
|
+
- **Document caching**: Hash-based caching skips redundant API calls for unchanged documents. Notion, Confluence, and GitHub Wiki publishers now receive only changed pages; Markdown always gets the full set. Cache persists in `.repolens/doc-hashes.json`.
|
|
10
|
+
- **Structured AI output**: AI sections now request JSON-mode responses with schema validation. If JSON parsing or schema validation fails, a single re-prompt is attempted before falling back to plain-text AI, then deterministic generation. All 6 AI document types have JSON schemas and Markdown renderers.
|
|
11
|
+
- **Multi-provider AI**: Added native adapters for Anthropic (Messages API) and Google Gemini alongside existing OpenAI-compatible support. Set `REPOLENS_AI_PROVIDER` to `anthropic`, `google`, or `openai_compatible` (default). Azure OpenAI uses the OpenAI-compatible adapter.
|
|
12
|
+
- **Monorepo awareness**: Automatic detection of npm/yarn workspaces, pnpm workspaces, and Lerna configurations. Scan results include workspace metadata. System Overview renderer shows package inventory table. AI context includes monorepo structure.
|
|
13
|
+
- **CODEOWNERS integration**: Parses `CODEOWNERS` / `.github/CODEOWNERS` / `docs/CODEOWNERS` files. Maps file ownership to modules via last-match-wins pattern matching. Module Catalog now displays an "Owners" column when CODEOWNERS is present. Ownership data is included in artifacts.
|
|
14
|
+
|
|
15
|
+
### ๐ Test Coverage
|
|
16
|
+
|
|
17
|
+
- **219 tests** passing across **17 test files** (up from 188/16).
|
|
18
|
+
- New `tests/tier3.test.js` with 31 tests covering caching, monorepo detection, CODEOWNERS parsing, multi-provider AI config, and structured output rendering.
|
|
19
|
+
|
|
20
|
+
## 1.4.0
|
|
21
|
+
|
|
22
|
+
### ๐ Bug Fixes (Tier 1 โ Production)
|
|
23
|
+
|
|
24
|
+
- **Confluence CDATA injection**: Code blocks containing `]]>` no longer break Confluence XML storage format. Applied standard CDATA escape pattern (`]]]]><](https://www.npmjs.com/package/@chappibunny/repolens)
|
|
12
|
+
[](https://marketplace.visualstudio.com/items?itemName=CHAPIBUNNY.repolens-architecture)
|
|
12
13
|
[](https://github.com/CHAPIBUNNY/repolens/actions)
|
|
13
14
|
[](LICENSE)
|
|
14
15
|
|
|
@@ -16,7 +17,7 @@
|
|
|
16
17
|
|
|
17
18
|
RepoLens scans your repository, generates living architecture documentation, and publishes it to Notion, Confluence, GitHub Wiki, or Markdown โ automatically on every push. Engineers get technical docs. Stakeholders get readable system overviews. Nobody writes a word.
|
|
18
19
|
|
|
19
|
-
> Stable as of v1.0 โ [API guarantees](STABILITY.md) ยท [Security hardened](SECURITY.md) ยท v1.
|
|
20
|
+
> Stable as of v1.0 โ [API guarantees](STABILITY.md) ยท [Security hardened](SECURITY.md) ยท v1.5.0
|
|
20
21
|
|
|
21
22
|
---
|
|
22
23
|
|
|
@@ -113,6 +114,28 @@ For alternative methods, see [INSTALLATION.md](INSTALLATION.md).
|
|
|
113
114
|
|
|
114
115
|
---
|
|
115
116
|
|
|
117
|
+
## ๐จ VS Code Extension
|
|
118
|
+
|
|
119
|
+
**View your architecture directly in VS Code** โ browse modules, visualize dependencies, and explore your codebase structure without leaving the editor.
|
|
120
|
+
|
|
121
|
+
**Install from Marketplace:**
|
|
122
|
+
```
|
|
123
|
+
ext install CHAPIBUNNY.repolens-architecture
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
[**โ Get it on Visual Studio Marketplace**](https://marketplace.visualstudio.com/items?itemName=CHAPIBUNNY.repolens-architecture)
|
|
127
|
+
|
|
128
|
+
**Features:**
|
|
129
|
+
- ๐๏ธ **Architecture Explorer** โ Tree view of your system structure
|
|
130
|
+
- ๐ **Dependency Visualizer** โ Interactive dependency graphs
|
|
131
|
+
- ๐ **Module Browser** โ Navigate modules by domain and function
|
|
132
|
+
- ๐ **Command Palette** โ Quick access to architecture insights
|
|
133
|
+
- ๐ **System Metrics** โ Real-time architecture health indicators
|
|
134
|
+
|
|
135
|
+
The extension reads your `.repolens.yml` configuration and provides an interactive UI for exploring the documentation that RepoLens generates.
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
116
139
|
## ๐ Onboarding Guide
|
|
117
140
|
|
|
118
141
|
Step-by-step setup for publishers, AI features, Notion, Confluence, GitHub Wiki, Discord, and CI/CD automation.
|
|
@@ -238,9 +261,11 @@ When you open a pull request, RepoLens posts:
|
|
|
238
261
|
|
|
239
262
|
v1.0+ features complete โ CLI, config schema, and plugin interface are frozen.
|
|
240
263
|
|
|
264
|
+
**Completed:**
|
|
265
|
+
- [x] VS Code extension ([available on Marketplace](https://marketplace.visualstudio.com/items?itemName=CHAPIBUNNY.repolens-architecture))
|
|
266
|
+
|
|
241
267
|
**Next:**
|
|
242
268
|
- [ ] Obsidian publisher
|
|
243
|
-
- [ ] VS Code extension
|
|
244
269
|
- [ ] GitHub App
|
|
245
270
|
|
|
246
271
|
See [ROADMAP.md](ROADMAP.md) for detailed planning.
|
package/package.json
CHANGED
|
@@ -11,129 +11,109 @@ import {
|
|
|
11
11
|
createDeveloperOnboardingPrompt,
|
|
12
12
|
createModuleSummaryPrompt,
|
|
13
13
|
createRouteSummaryPrompt,
|
|
14
|
-
createAPIDocumentationPrompt
|
|
14
|
+
createAPIDocumentationPrompt,
|
|
15
|
+
AI_SCHEMAS,
|
|
16
|
+
renderStructuredToMarkdown,
|
|
15
17
|
} from "./prompts.js";
|
|
16
18
|
import { info, warn } from "../utils/logger.js";
|
|
17
19
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
20
|
+
/**
|
|
21
|
+
* Try structured JSON mode first, fall back to plain-text AI, then deterministic.
|
|
22
|
+
*/
|
|
23
|
+
async function generateWithStructuredFallback(key, promptText, maxTokens, fallbackFn) {
|
|
24
|
+
if (!isAIEnabled()) return fallbackFn();
|
|
25
|
+
|
|
26
|
+
const schema = AI_SCHEMAS[key];
|
|
27
|
+
|
|
28
|
+
// Try structured JSON mode
|
|
29
|
+
if (schema) {
|
|
30
|
+
info(`Generating ${key} with structured AI...`);
|
|
31
|
+
const jsonPrompt = promptText + `\n\nRespond ONLY with a JSON object matching this schema: ${JSON.stringify({ required: schema.required })}. No markdown, no explanation โ just the JSON object.`;
|
|
32
|
+
|
|
33
|
+
const result = await generateText({
|
|
34
|
+
system: SYSTEM_PROMPT,
|
|
35
|
+
user: jsonPrompt,
|
|
36
|
+
maxTokens,
|
|
37
|
+
jsonMode: true,
|
|
38
|
+
jsonSchema: schema,
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
if (result.success && result.parsed) {
|
|
42
|
+
const md = renderStructuredToMarkdown(key, result.parsed);
|
|
43
|
+
if (md) return md;
|
|
44
|
+
}
|
|
45
|
+
// If structured mode failed, fall through to plain-text
|
|
46
|
+
warn(`Structured AI failed for ${key}, trying plain-text mode...`);
|
|
21
47
|
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
48
|
+
|
|
49
|
+
// Plain-text AI fallback
|
|
50
|
+
info(`Generating ${key} with AI...`);
|
|
25
51
|
const result = await generateText({
|
|
26
52
|
system: SYSTEM_PROMPT,
|
|
27
|
-
user:
|
|
28
|
-
maxTokens
|
|
53
|
+
user: promptText,
|
|
54
|
+
maxTokens,
|
|
29
55
|
});
|
|
30
|
-
|
|
56
|
+
|
|
31
57
|
if (!result.success) {
|
|
32
58
|
warn("AI generation failed, using fallback");
|
|
33
|
-
return
|
|
59
|
+
return fallbackFn();
|
|
34
60
|
}
|
|
35
|
-
|
|
61
|
+
|
|
36
62
|
return result.text;
|
|
37
63
|
}
|
|
38
64
|
|
|
65
|
+
export async function generateExecutiveSummary(context) {
|
|
66
|
+
return generateWithStructuredFallback(
|
|
67
|
+
"executive_summary",
|
|
68
|
+
createExecutiveSummaryPrompt(context),
|
|
69
|
+
1500,
|
|
70
|
+
() => getFallbackExecutiveSummary(context),
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
|
|
39
74
|
export async function generateSystemOverview(context) {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
const result = await generateText({
|
|
47
|
-
system: SYSTEM_PROMPT,
|
|
48
|
-
user: createSystemOverviewPrompt(context),
|
|
49
|
-
maxTokens: 1200
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
if (!result.success) {
|
|
53
|
-
return getFallbackSystemOverview(context);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
return result.text;
|
|
75
|
+
return generateWithStructuredFallback(
|
|
76
|
+
"system_overview",
|
|
77
|
+
createSystemOverviewPrompt(context),
|
|
78
|
+
1200,
|
|
79
|
+
() => getFallbackSystemOverview(context),
|
|
80
|
+
);
|
|
57
81
|
}
|
|
58
82
|
|
|
59
83
|
export async function generateBusinessDomains(context) {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
const result = await generateText({
|
|
67
|
-
system: SYSTEM_PROMPT,
|
|
68
|
-
user: createBusinessDomainsPrompt(context),
|
|
69
|
-
maxTokens: 2000
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
if (!result.success) {
|
|
73
|
-
return getFallbackBusinessDomains(context);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
return result.text;
|
|
84
|
+
return generateWithStructuredFallback(
|
|
85
|
+
"business_domains",
|
|
86
|
+
createBusinessDomainsPrompt(context),
|
|
87
|
+
2000,
|
|
88
|
+
() => getFallbackBusinessDomains(context),
|
|
89
|
+
);
|
|
77
90
|
}
|
|
78
91
|
|
|
79
92
|
export async function generateArchitectureOverview(context) {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
const result = await generateText({
|
|
87
|
-
system: SYSTEM_PROMPT,
|
|
88
|
-
user: createArchitectureOverviewPrompt(context),
|
|
89
|
-
maxTokens: 1800
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
if (!result.success) {
|
|
93
|
-
return getFallbackArchitectureOverview(context);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
return result.text;
|
|
93
|
+
return generateWithStructuredFallback(
|
|
94
|
+
"architecture_overview",
|
|
95
|
+
createArchitectureOverviewPrompt(context),
|
|
96
|
+
1800,
|
|
97
|
+
() => getFallbackArchitectureOverview(context),
|
|
98
|
+
);
|
|
97
99
|
}
|
|
98
100
|
|
|
99
101
|
export async function generateDataFlows(flows, context) {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
const result = await generateText({
|
|
107
|
-
system: SYSTEM_PROMPT,
|
|
108
|
-
user: createDataFlowsPrompt(flows, context),
|
|
109
|
-
maxTokens: 1800
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
if (!result.success) {
|
|
113
|
-
return getFallbackDataFlows(flows);
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
return result.text;
|
|
102
|
+
return generateWithStructuredFallback(
|
|
103
|
+
"data_flows",
|
|
104
|
+
createDataFlowsPrompt(flows, context),
|
|
105
|
+
1800,
|
|
106
|
+
() => getFallbackDataFlows(flows),
|
|
107
|
+
);
|
|
117
108
|
}
|
|
118
109
|
|
|
119
110
|
export async function generateDeveloperOnboarding(context) {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
const result = await generateText({
|
|
127
|
-
system: SYSTEM_PROMPT,
|
|
128
|
-
user: createDeveloperOnboardingPrompt(context),
|
|
129
|
-
maxTokens: 2200
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
if (!result.success) {
|
|
133
|
-
return getFallbackDeveloperOnboarding(context);
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
return result.text;
|
|
111
|
+
return generateWithStructuredFallback(
|
|
112
|
+
"developer_onboarding",
|
|
113
|
+
createDeveloperOnboardingPrompt(context),
|
|
114
|
+
2200,
|
|
115
|
+
() => getFallbackDeveloperOnboarding(context),
|
|
116
|
+
);
|
|
137
117
|
}
|
|
138
118
|
|
|
139
119
|
// Fallback generators (deterministic, no AI)
|
package/src/ai/prompts.js
CHANGED
|
@@ -1,5 +1,48 @@
|
|
|
1
1
|
// Strict prompt templates for AI-generated documentation sections
|
|
2
2
|
|
|
3
|
+
const MAX_CONTEXT_CHARS = 12000; // ~3000 tokens, safe for all models
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Truncate a context object to fit within token limits.
|
|
7
|
+
* Prunes large arrays (routes, modules, domains) to keep context compact.
|
|
8
|
+
*/
|
|
9
|
+
function truncateContext(context) {
|
|
10
|
+
let json = JSON.stringify(context, null, 2);
|
|
11
|
+
if (json.length <= MAX_CONTEXT_CHARS) return json;
|
|
12
|
+
|
|
13
|
+
// Progressively shrink: reduce array sizes
|
|
14
|
+
const trimmed = { ...context };
|
|
15
|
+
|
|
16
|
+
// Trim routes
|
|
17
|
+
if (trimmed.routes) {
|
|
18
|
+
if (trimmed.routes.pages && trimmed.routes.pages.length > 15) {
|
|
19
|
+
trimmed.routes = { ...trimmed.routes, pages: trimmed.routes.pages.slice(0, 15) };
|
|
20
|
+
}
|
|
21
|
+
if (trimmed.routes.apis && trimmed.routes.apis.length > 15) {
|
|
22
|
+
trimmed.routes = { ...trimmed.routes, apis: trimmed.routes.apis.slice(0, 15) };
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Trim domains
|
|
27
|
+
if (Array.isArray(trimmed.domains) && trimmed.domains.length > 8) {
|
|
28
|
+
trimmed.domains = trimmed.domains.slice(0, 8);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Trim top modules
|
|
32
|
+
if (Array.isArray(trimmed.topModules) && trimmed.topModules.length > 10) {
|
|
33
|
+
trimmed.topModules = trimmed.topModules.slice(0, 10);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
json = JSON.stringify(trimmed, null, 2);
|
|
37
|
+
|
|
38
|
+
// Final hard truncation if still over limit
|
|
39
|
+
if (json.length > MAX_CONTEXT_CHARS) {
|
|
40
|
+
json = json.slice(0, MAX_CONTEXT_CHARS) + "\n... (context truncated for token limit)";
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return json;
|
|
44
|
+
}
|
|
45
|
+
|
|
3
46
|
export const SYSTEM_PROMPT = `You are a senior software architect and technical writer.
|
|
4
47
|
Your job is to turn structured repository analysis into clear documentation.
|
|
5
48
|
|
|
@@ -20,7 +63,7 @@ export function createExecutiveSummaryPrompt(context) {
|
|
|
20
63
|
return `Write an executive summary for a mixed audience of technical and non-technical readers.
|
|
21
64
|
|
|
22
65
|
Use this context:
|
|
23
|
-
${
|
|
66
|
+
${truncateContext(context)}
|
|
24
67
|
|
|
25
68
|
Requirements:
|
|
26
69
|
- Explain what the system appears to do based on the modules and routes.
|
|
@@ -53,7 +96,7 @@ export function createSystemOverviewPrompt(context) {
|
|
|
53
96
|
return `Write a system overview for a mixed audience.
|
|
54
97
|
|
|
55
98
|
Use this context:
|
|
56
|
-
${
|
|
99
|
+
${truncateContext(context)}
|
|
57
100
|
|
|
58
101
|
Requirements:
|
|
59
102
|
- Provide a concise, high-level orientation to the codebase.
|
|
@@ -81,7 +124,7 @@ export function createBusinessDomainsPrompt(context) {
|
|
|
81
124
|
return `Write business domain documentation for a mixed audience, especially non-technical readers.
|
|
82
125
|
|
|
83
126
|
Use this context:
|
|
84
|
-
${
|
|
127
|
+
${truncateContext(context)}
|
|
85
128
|
|
|
86
129
|
Requirements:
|
|
87
130
|
- Translate codebase structure into business language.
|
|
@@ -113,7 +156,7 @@ export function createArchitectureOverviewPrompt(context) {
|
|
|
113
156
|
return `Write an architecture overview for engineers, architects, and technical PMs.
|
|
114
157
|
|
|
115
158
|
Use this context:
|
|
116
|
-
${
|
|
159
|
+
${truncateContext(context)}
|
|
117
160
|
|
|
118
161
|
Requirements:
|
|
119
162
|
- Explain the layered architecture based on observable patterns.
|
|
@@ -147,10 +190,10 @@ export function createDataFlowsPrompt(flows, context) {
|
|
|
147
190
|
return `Write data flow documentation for a mixed audience.
|
|
148
191
|
|
|
149
192
|
Use this flow information:
|
|
150
|
-
${
|
|
193
|
+
${truncateContext(flows)}
|
|
151
194
|
|
|
152
195
|
And this context:
|
|
153
|
-
${
|
|
196
|
+
${truncateContext(context)}
|
|
154
197
|
|
|
155
198
|
Requirements:
|
|
156
199
|
- Explain major information flows in plain language.
|
|
@@ -179,7 +222,7 @@ export function createDeveloperOnboardingPrompt(context) {
|
|
|
179
222
|
return `Write developer onboarding documentation to help new engineers get productive quickly.
|
|
180
223
|
|
|
181
224
|
Use this context:
|
|
182
|
-
${
|
|
225
|
+
${truncateContext(context)}
|
|
183
226
|
|
|
184
227
|
Requirements:
|
|
185
228
|
- Guide new developers through the codebase structure.
|
|
@@ -218,7 +261,7 @@ Type: ${module.type}
|
|
|
218
261
|
Domain: ${module.domain}
|
|
219
262
|
|
|
220
263
|
Additional context:
|
|
221
|
-
${
|
|
264
|
+
${truncateContext(context)}
|
|
222
265
|
|
|
223
266
|
Requirements:
|
|
224
267
|
- Explain the module's likely purpose.
|
|
@@ -252,7 +295,7 @@ File: ${route.file}
|
|
|
252
295
|
Type: ${route.type}
|
|
253
296
|
|
|
254
297
|
Additional context:
|
|
255
|
-
${
|
|
298
|
+
${truncateContext(context)}
|
|
256
299
|
|
|
257
300
|
Requirements:
|
|
258
301
|
- Explain the user purpose of this route.
|
|
@@ -283,7 +326,7 @@ API: ${api.methods.join(", ")} ${api.path}
|
|
|
283
326
|
File: ${api.file}
|
|
284
327
|
|
|
285
328
|
Additional context:
|
|
286
|
-
${
|
|
329
|
+
${truncateContext(context)}
|
|
287
330
|
|
|
288
331
|
Requirements:
|
|
289
332
|
- Explain the purpose in plain language and technical language.
|
|
@@ -310,3 +353,125 @@ Dependencies:
|
|
|
310
353
|
Risks:
|
|
311
354
|
[if applicable]`;
|
|
312
355
|
}
|
|
356
|
+
|
|
357
|
+
// --- JSON schemas for structured AI output ---
|
|
358
|
+
|
|
359
|
+
export const AI_SCHEMAS = {
|
|
360
|
+
executive_summary: {
|
|
361
|
+
required: ["whatItDoes", "whoItServes", "coreCapabilities", "mainAreas", "risks"],
|
|
362
|
+
description: "Executive summary for mixed audience",
|
|
363
|
+
},
|
|
364
|
+
system_overview: {
|
|
365
|
+
required: ["snapshot", "layers", "domains", "patterns", "observations"],
|
|
366
|
+
description: "High-level system overview",
|
|
367
|
+
},
|
|
368
|
+
business_domains: {
|
|
369
|
+
required: ["domains"],
|
|
370
|
+
description: "Business domain breakdown",
|
|
371
|
+
},
|
|
372
|
+
architecture_overview: {
|
|
373
|
+
required: ["style", "layers", "strengths", "weaknesses"],
|
|
374
|
+
description: "Architecture overview for engineers",
|
|
375
|
+
},
|
|
376
|
+
data_flows: {
|
|
377
|
+
required: ["flows"],
|
|
378
|
+
description: "Data flow documentation",
|
|
379
|
+
},
|
|
380
|
+
developer_onboarding: {
|
|
381
|
+
required: ["startHere", "mainFolders", "coreFlows", "complexityHotspots"],
|
|
382
|
+
description: "Developer onboarding guide",
|
|
383
|
+
},
|
|
384
|
+
};
|
|
385
|
+
|
|
386
|
+
/**
|
|
387
|
+
* Render a structured JSON response into Markdown for the given document type.
|
|
388
|
+
*/
|
|
389
|
+
export function renderStructuredToMarkdown(key, parsed) {
|
|
390
|
+
switch (key) {
|
|
391
|
+
case "executive_summary":
|
|
392
|
+
return renderExecutiveSummaryJSON(parsed);
|
|
393
|
+
case "system_overview":
|
|
394
|
+
return renderSystemOverviewJSON(parsed);
|
|
395
|
+
case "business_domains":
|
|
396
|
+
return renderBusinessDomainsJSON(parsed);
|
|
397
|
+
case "architecture_overview":
|
|
398
|
+
return renderArchitectureOverviewJSON(parsed);
|
|
399
|
+
case "data_flows":
|
|
400
|
+
return renderDataFlowsJSON(parsed);
|
|
401
|
+
case "developer_onboarding":
|
|
402
|
+
return renderDeveloperOnboardingJSON(parsed);
|
|
403
|
+
default:
|
|
404
|
+
return null;
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
function renderExecutiveSummaryJSON(d) {
|
|
409
|
+
let md = `# Executive Summary\n\n`;
|
|
410
|
+
md += `## What This System Does\n\n${d.whatItDoes}\n\n`;
|
|
411
|
+
md += `## Who It Serves\n\n${d.whoItServes}\n\n`;
|
|
412
|
+
md += `## Core Capabilities\n\n`;
|
|
413
|
+
if (Array.isArray(d.coreCapabilities)) {
|
|
414
|
+
md += d.coreCapabilities.map(c => `- ${c}`).join("\n") + "\n\n";
|
|
415
|
+
} else {
|
|
416
|
+
md += `${d.coreCapabilities}\n\n`;
|
|
417
|
+
}
|
|
418
|
+
md += `## Main System Areas\n\n${Array.isArray(d.mainAreas) ? d.mainAreas.map(a => `- **${a.name || a}**${a.description ? `: ${a.description}` : ""}`).join("\n") : d.mainAreas}\n\n`;
|
|
419
|
+
if (d.dependencies) md += `## Key Dependencies\n\n${Array.isArray(d.dependencies) ? d.dependencies.map(dep => `- ${dep}`).join("\n") : d.dependencies}\n\n`;
|
|
420
|
+
md += `## Operational and Architectural Risks\n\n${Array.isArray(d.risks) ? d.risks.map(r => `- ${r}`).join("\n") : d.risks}\n\n`;
|
|
421
|
+
if (d.focusAreas) md += `## Recommended Focus Areas\n\n${Array.isArray(d.focusAreas) ? d.focusAreas.map(f => `- ${f}`).join("\n") : d.focusAreas}\n`;
|
|
422
|
+
return md;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
function renderSystemOverviewJSON(d) {
|
|
426
|
+
let md = `# System Overview\n\n`;
|
|
427
|
+
md += `## Repository Snapshot\n\n${d.snapshot}\n\n`;
|
|
428
|
+
md += `## Main Architectural Layers\n\n${Array.isArray(d.layers) ? d.layers.map(l => `- **${l.name || l}**${l.description ? `: ${l.description}` : ""}`).join("\n") : d.layers}\n\n`;
|
|
429
|
+
md += `## Dominant Domains\n\n${Array.isArray(d.domains) ? d.domains.map(dom => `- ${dom}`).join("\n") : d.domains}\n\n`;
|
|
430
|
+
md += `## Main Technology Patterns\n\n${Array.isArray(d.patterns) ? d.patterns.map(p => `- ${p}`).join("\n") : d.patterns}\n\n`;
|
|
431
|
+
md += `## Key Observations\n\n${Array.isArray(d.observations) ? d.observations.map(o => `- ${o}`).join("\n") : d.observations}\n`;
|
|
432
|
+
return md;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
function renderBusinessDomainsJSON(d) {
|
|
436
|
+
let md = `# Business Domains\n\n`;
|
|
437
|
+
if (!Array.isArray(d.domains)) return md + d.domains;
|
|
438
|
+
for (const dom of d.domains) {
|
|
439
|
+
md += `## ${dom.name}\n\n${dom.description || ""}\n\n`;
|
|
440
|
+
if (dom.modules) md += `**Key modules:** ${Array.isArray(dom.modules) ? dom.modules.join(", ") : dom.modules}\n\n`;
|
|
441
|
+
if (dom.userFunctionality) md += `**User-visible functionality:** ${dom.userFunctionality}\n\n`;
|
|
442
|
+
if (dom.dependencies) md += `**Dependencies:** ${Array.isArray(dom.dependencies) ? dom.dependencies.join(", ") : dom.dependencies}\n\n`;
|
|
443
|
+
}
|
|
444
|
+
return md;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
function renderArchitectureOverviewJSON(d) {
|
|
448
|
+
let md = `# Architecture Overview\n\n`;
|
|
449
|
+
md += `## Architecture Style\n\n${d.style}\n\n`;
|
|
450
|
+
md += `## Layers\n\n${Array.isArray(d.layers) ? d.layers.map(l => `### ${l.name || l}\n\n${l.description || ""}`).join("\n\n") : d.layers}\n\n`;
|
|
451
|
+
md += `## Architectural Strengths\n\n${Array.isArray(d.strengths) ? d.strengths.map(s => `- ${s}`).join("\n") : d.strengths}\n\n`;
|
|
452
|
+
md += `## Architectural Weaknesses\n\n${Array.isArray(d.weaknesses) ? d.weaknesses.map(w => `- ${w}`).join("\n") : d.weaknesses}\n`;
|
|
453
|
+
return md;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
function renderDataFlowsJSON(d) {
|
|
457
|
+
let md = `# Data Flows\n\n`;
|
|
458
|
+
if (!Array.isArray(d.flows)) return md + d.flows;
|
|
459
|
+
for (const flow of d.flows) {
|
|
460
|
+
md += `## ${flow.name}\n\n${flow.description || ""}\n\n`;
|
|
461
|
+
if (flow.steps) md += `**Steps:**\n${Array.isArray(flow.steps) ? flow.steps.map((s, i) => `${i + 1}. ${s}`).join("\n") : flow.steps}\n\n`;
|
|
462
|
+
if (flow.modules) md += `**Involved modules:** ${Array.isArray(flow.modules) ? flow.modules.join(", ") : flow.modules}\n\n`;
|
|
463
|
+
if (flow.criticalDependencies) md += `**Critical dependencies:** ${flow.criticalDependencies}\n\n`;
|
|
464
|
+
}
|
|
465
|
+
return md;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
function renderDeveloperOnboardingJSON(d) {
|
|
469
|
+
let md = `# Developer Onboarding\n\n`;
|
|
470
|
+
md += `## Start Here\n\n${d.startHere}\n\n`;
|
|
471
|
+
md += `## Main Folders\n\n${Array.isArray(d.mainFolders) ? d.mainFolders.map(f => `- **${f.name || f}**${f.description ? `: ${f.description}` : ""}`).join("\n") : d.mainFolders}\n\n`;
|
|
472
|
+
md += `## Core Product Flows\n\n${Array.isArray(d.coreFlows) ? d.coreFlows.map(f => `- ${f}`).join("\n") : d.coreFlows}\n\n`;
|
|
473
|
+
if (d.importantRoutes) md += `## Important Routes\n\n${Array.isArray(d.importantRoutes) ? d.importantRoutes.map(r => `- ${r}`).join("\n") : d.importantRoutes}\n\n`;
|
|
474
|
+
if (d.sharedLibraries) md += `## Important Shared Libraries\n\n${Array.isArray(d.sharedLibraries) ? d.sharedLibraries.map(l => `- ${l}`).join("\n") : d.sharedLibraries}\n\n`;
|
|
475
|
+
md += `## Known Complexity Hotspots\n\n${Array.isArray(d.complexityHotspots) ? d.complexityHotspots.map(h => `- ${h}`).join("\n") : d.complexityHotspots}\n`;
|
|
476
|
+
return md;
|
|
477
|
+
}
|