repowiki-core 0.1.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.
Files changed (157) hide show
  1. package/dist/analyzer/api-analyzer.d.ts +22 -0
  2. package/dist/analyzer/api-analyzer.d.ts.map +1 -0
  3. package/dist/analyzer/api-analyzer.js +272 -0
  4. package/dist/analyzer/api-analyzer.js.map +1 -0
  5. package/dist/analyzer/config-analyzer.d.ts +18 -0
  6. package/dist/analyzer/config-analyzer.d.ts.map +1 -0
  7. package/dist/analyzer/config-analyzer.js +200 -0
  8. package/dist/analyzer/config-analyzer.js.map +1 -0
  9. package/dist/analyzer/database-analyzer.d.ts +24 -0
  10. package/dist/analyzer/database-analyzer.d.ts.map +1 -0
  11. package/dist/analyzer/database-analyzer.js +391 -0
  12. package/dist/analyzer/database-analyzer.js.map +1 -0
  13. package/dist/analyzer/index.d.ts +10 -0
  14. package/dist/analyzer/index.d.ts.map +1 -0
  15. package/dist/analyzer/index.js +10 -0
  16. package/dist/analyzer/index.js.map +1 -0
  17. package/dist/analyzer/module-analyzer.d.ts +20 -0
  18. package/dist/analyzer/module-analyzer.d.ts.map +1 -0
  19. package/dist/analyzer/module-analyzer.js +252 -0
  20. package/dist/analyzer/module-analyzer.js.map +1 -0
  21. package/dist/analyzer/workflow-analyzer.d.ts +19 -0
  22. package/dist/analyzer/workflow-analyzer.d.ts.map +1 -0
  23. package/dist/analyzer/workflow-analyzer.js +165 -0
  24. package/dist/analyzer/workflow-analyzer.js.map +1 -0
  25. package/dist/detector/dependency-detector.d.ts +50 -0
  26. package/dist/detector/dependency-detector.d.ts.map +1 -0
  27. package/dist/detector/dependency-detector.js +326 -0
  28. package/dist/detector/dependency-detector.js.map +1 -0
  29. package/dist/detector/entrypoint-detector.d.ts +30 -0
  30. package/dist/detector/entrypoint-detector.d.ts.map +1 -0
  31. package/dist/detector/entrypoint-detector.js +240 -0
  32. package/dist/detector/entrypoint-detector.js.map +1 -0
  33. package/dist/detector/index.d.ts +10 -0
  34. package/dist/detector/index.d.ts.map +1 -0
  35. package/dist/detector/index.js +10 -0
  36. package/dist/detector/index.js.map +1 -0
  37. package/dist/detector/tech-stack-detector.d.ts +41 -0
  38. package/dist/detector/tech-stack-detector.d.ts.map +1 -0
  39. package/dist/detector/tech-stack-detector.js +300 -0
  40. package/dist/detector/tech-stack-detector.js.map +1 -0
  41. package/dist/generator/index.d.ts +9 -0
  42. package/dist/generator/index.d.ts.map +1 -0
  43. package/dist/generator/index.js +9 -0
  44. package/dist/generator/index.js.map +1 -0
  45. package/dist/generator/markdown-generator.d.ts +71 -0
  46. package/dist/generator/markdown-generator.d.ts.map +1 -0
  47. package/dist/generator/markdown-generator.js +235 -0
  48. package/dist/generator/markdown-generator.js.map +1 -0
  49. package/dist/generator/mermaid-generator.d.ts +30 -0
  50. package/dist/generator/mermaid-generator.d.ts.map +1 -0
  51. package/dist/generator/mermaid-generator.js +297 -0
  52. package/dist/generator/mermaid-generator.js.map +1 -0
  53. package/dist/generator/sidebar-generator.d.ts +10 -0
  54. package/dist/generator/sidebar-generator.d.ts.map +1 -0
  55. package/dist/generator/sidebar-generator.js +120 -0
  56. package/dist/generator/sidebar-generator.js.map +1 -0
  57. package/dist/generator/wiki-generator.d.ts +45 -0
  58. package/dist/generator/wiki-generator.d.ts.map +1 -0
  59. package/dist/generator/wiki-generator.js +217 -0
  60. package/dist/generator/wiki-generator.js.map +1 -0
  61. package/dist/index.d.ts +12 -0
  62. package/dist/index.d.ts.map +1 -0
  63. package/dist/index.js +12 -0
  64. package/dist/index.js.map +1 -0
  65. package/dist/llm/auth-manager.d.ts +50 -0
  66. package/dist/llm/auth-manager.d.ts.map +1 -0
  67. package/dist/llm/auth-manager.js +172 -0
  68. package/dist/llm/auth-manager.js.map +1 -0
  69. package/dist/llm/index.d.ts +10 -0
  70. package/dist/llm/index.d.ts.map +1 -0
  71. package/dist/llm/index.js +9 -0
  72. package/dist/llm/index.js.map +1 -0
  73. package/dist/llm/llm-client.d.ts +132 -0
  74. package/dist/llm/llm-client.d.ts.map +1 -0
  75. package/dist/llm/llm-client.js +308 -0
  76. package/dist/llm/llm-client.js.map +1 -0
  77. package/dist/llm/prompt-manager.d.ts +67 -0
  78. package/dist/llm/prompt-manager.d.ts.map +1 -0
  79. package/dist/llm/prompt-manager.js +283 -0
  80. package/dist/llm/prompt-manager.js.map +1 -0
  81. package/dist/models/analysis-result.d.ts +425 -0
  82. package/dist/models/analysis-result.d.ts.map +1 -0
  83. package/dist/models/analysis-result.js +34 -0
  84. package/dist/models/analysis-result.js.map +1 -0
  85. package/dist/models/analysis-types.d.ts +223 -0
  86. package/dist/models/analysis-types.d.ts.map +1 -0
  87. package/dist/models/analysis-types.js +95 -0
  88. package/dist/models/analysis-types.js.map +1 -0
  89. package/dist/models/file-reference.d.ts +62 -0
  90. package/dist/models/file-reference.d.ts.map +1 -0
  91. package/dist/models/file-reference.js +34 -0
  92. package/dist/models/file-reference.js.map +1 -0
  93. package/dist/models/index.d.ts +10 -0
  94. package/dist/models/index.d.ts.map +1 -0
  95. package/dist/models/index.js +10 -0
  96. package/dist/models/index.js.map +1 -0
  97. package/dist/models/project-profile.d.ts +48 -0
  98. package/dist/models/project-profile.d.ts.map +1 -0
  99. package/dist/models/project-profile.js +26 -0
  100. package/dist/models/project-profile.js.map +1 -0
  101. package/dist/models/wiki-page.d.ts +57 -0
  102. package/dist/models/wiki-page.d.ts.map +1 -0
  103. package/dist/models/wiki-page.js +19 -0
  104. package/dist/models/wiki-page.js.map +1 -0
  105. package/dist/pipeline.d.ts +30 -0
  106. package/dist/pipeline.d.ts.map +1 -0
  107. package/dist/pipeline.js +159 -0
  108. package/dist/pipeline.js.map +1 -0
  109. package/dist/scanner/file-scanner.d.ts +27 -0
  110. package/dist/scanner/file-scanner.d.ts.map +1 -0
  111. package/dist/scanner/file-scanner.js +149 -0
  112. package/dist/scanner/file-scanner.js.map +1 -0
  113. package/dist/scanner/ignore-rules.d.ts +31 -0
  114. package/dist/scanner/ignore-rules.d.ts.map +1 -0
  115. package/dist/scanner/ignore-rules.js +98 -0
  116. package/dist/scanner/ignore-rules.js.map +1 -0
  117. package/dist/scanner/index.d.ts +8 -0
  118. package/dist/scanner/index.d.ts.map +1 -0
  119. package/dist/scanner/index.js +8 -0
  120. package/dist/scanner/index.js.map +1 -0
  121. package/dist/scanner/tree-builder.d.ts +20 -0
  122. package/dist/scanner/tree-builder.d.ts.map +1 -0
  123. package/dist/scanner/tree-builder.js +118 -0
  124. package/dist/scanner/tree-builder.js.map +1 -0
  125. package/package.json +34 -0
  126. package/src/analyzer/api-analyzer.ts +324 -0
  127. package/src/analyzer/config-analyzer.ts +209 -0
  128. package/src/analyzer/database-analyzer.ts +468 -0
  129. package/src/analyzer/index.ts +26 -0
  130. package/src/analyzer/module-analyzer.ts +308 -0
  131. package/src/analyzer/workflow-analyzer.ts +190 -0
  132. package/src/detector/dependency-detector.ts +390 -0
  133. package/src/detector/entrypoint-detector.ts +270 -0
  134. package/src/detector/index.ts +21 -0
  135. package/src/detector/tech-stack-detector.ts +377 -0
  136. package/src/generator/index.ts +36 -0
  137. package/src/generator/markdown-generator.ts +277 -0
  138. package/src/generator/mermaid-generator.ts +340 -0
  139. package/src/generator/sidebar-generator.ts +134 -0
  140. package/src/generator/wiki-generator.ts +281 -0
  141. package/src/index.ts +12 -0
  142. package/src/llm/auth-manager.ts +207 -0
  143. package/src/llm/index.ts +21 -0
  144. package/src/llm/llm-client.ts +417 -0
  145. package/src/llm/prompt-manager.ts +325 -0
  146. package/src/models/analysis-result.ts +44 -0
  147. package/src/models/analysis-types.ts +121 -0
  148. package/src/models/file-reference.ts +41 -0
  149. package/src/models/index.ts +44 -0
  150. package/src/models/project-profile.ts +29 -0
  151. package/src/models/wiki-page.ts +23 -0
  152. package/src/pipeline.ts +225 -0
  153. package/src/scanner/file-scanner.ts +192 -0
  154. package/src/scanner/ignore-rules.ts +112 -0
  155. package/src/scanner/index.ts +19 -0
  156. package/src/scanner/tree-builder.ts +156 -0
  157. package/tsconfig.json +8 -0
@@ -0,0 +1,377 @@
1
+ /**
2
+ * @module detector/tech-stack-detector
3
+ * 技术栈探测器
4
+ *
5
+ * 通过分析项目中的配置文件(package.json、requirements.txt、docker-compose.yml 等)
6
+ * 以及文件扩展名分布,自动识别项目使用的编程语言、框架、包管理器、数据库和服务。
7
+ */
8
+
9
+ import * as fs from 'node:fs/promises';
10
+ import * as path from 'node:path';
11
+ import type { FileNode } from '../models/index.js';
12
+
13
+ // ---------------------------------------------------------------------------
14
+ // 公开接口
15
+ // ---------------------------------------------------------------------------
16
+
17
+ /** 技术栈检测结果 */
18
+ export interface TechStackResult {
19
+ /** 检测到的编程语言列表 */
20
+ languages: string[];
21
+ /** 检测到的框架列表 */
22
+ frameworks: string[];
23
+ /** 检测到的包管理器列表 */
24
+ packageManagers: string[];
25
+ /** 检测到的数据库列表 */
26
+ databases: string[];
27
+ /** 检测到的服务/中间件列表 */
28
+ services: string[];
29
+ /** 参与检测的配置文件列表 */
30
+ configFiles: string[];
31
+ }
32
+
33
+ // ---------------------------------------------------------------------------
34
+ // 匹配规则
35
+ // ---------------------------------------------------------------------------
36
+
37
+ /** Node.js 依赖名 → 框架显示名 */
38
+ const NODE_FRAMEWORK_MAP: Record<string, string> = {
39
+ next: 'Next.js',
40
+ react: 'React',
41
+ vue: 'Vue.js',
42
+ '@angular/core': 'Angular',
43
+ vite: 'Vite',
44
+ express: 'Express',
45
+ '@nestjs/core': 'NestJS',
46
+ fastify: 'Fastify',
47
+ };
48
+
49
+ /** Python 包名关键字 → 框架显示名 */
50
+ const PYTHON_FRAMEWORK_MAP: Record<string, string> = {
51
+ fastapi: 'FastAPI',
52
+ django: 'Django',
53
+ flask: 'Flask',
54
+ langchain: 'LangChain',
55
+ langgraph: 'LangGraph',
56
+ };
57
+
58
+ /** docker-compose 服务镜像/名称关键字 → 数据库/服务显示名 */
59
+ const DOCKER_SERVICE_MAP: Record<string, { name: string; kind: 'database' | 'service' }> = {
60
+ postgres: { name: 'PostgreSQL', kind: 'database' },
61
+ mysql: { name: 'MySQL', kind: 'database' },
62
+ redis: { name: 'Redis', kind: 'database' },
63
+ mongo: { name: 'MongoDB', kind: 'database' },
64
+ minio: { name: 'MinIO', kind: 'service' },
65
+ rabbitmq: { name: 'RabbitMQ', kind: 'service' },
66
+ elasticsearch: { name: 'Elasticsearch', kind: 'service' },
67
+ };
68
+
69
+ /** 锁文件 → 包管理器名称 */
70
+ const LOCKFILE_MAP: Record<string, string> = {
71
+ 'package-lock.json': 'npm',
72
+ 'yarn.lock': 'yarn',
73
+ 'pnpm-lock.yaml': 'pnpm',
74
+ };
75
+
76
+ /** 文件扩展名 → 语言名称(用于统计文件数量) */
77
+ const EXTENSION_LANGUAGE_MAP: Record<string, string> = {
78
+ '.ts': 'TypeScript',
79
+ '.tsx': 'TypeScript',
80
+ '.js': 'JavaScript',
81
+ '.jsx': 'JavaScript',
82
+ '.py': 'Python',
83
+ '.rs': 'Rust',
84
+ '.go': 'Go',
85
+ '.java': 'Java',
86
+ '.kt': 'Kotlin',
87
+ '.swift': 'Swift',
88
+ '.rb': 'Ruby',
89
+ '.php': 'PHP',
90
+ '.cs': 'C#',
91
+ '.cpp': 'C++',
92
+ '.c': 'C',
93
+ '.dart': 'Dart',
94
+ '.scala': 'Scala',
95
+ '.lua': 'Lua',
96
+ '.zig': 'Zig',
97
+ };
98
+
99
+ /** 需要达到的最低文件数量阈值,才会报告该语言 */
100
+ const LANGUAGE_FILE_THRESHOLD = 5;
101
+
102
+ // ---------------------------------------------------------------------------
103
+ // 辅助函数
104
+ // ---------------------------------------------------------------------------
105
+
106
+ /**
107
+ * 安全读取文件内容,文件不存在时返回 null。
108
+ * @param filePath 要读取的文件绝对路径
109
+ */
110
+ async function safeReadFile(filePath: string): Promise<string | null> {
111
+ try {
112
+ return await fs.readFile(filePath, 'utf-8');
113
+ } catch {
114
+ return null;
115
+ }
116
+ }
117
+
118
+ /**
119
+ * 在文件节点列表中查找指定相对路径的文件是否存在。
120
+ * @param files 文件节点列表
121
+ * @param relativePath 待查找的相对路径
122
+ */
123
+ function hasFile(files: FileNode[], relativePath: string): boolean {
124
+ const normalized = relativePath.replace(/\\/g, '/');
125
+ return files.some(
126
+ (f) => f.nodeType === 'file' && f.relativePath.replace(/\\/g, '/') === normalized,
127
+ );
128
+ }
129
+
130
+ /**
131
+ * 在文件节点列表中查找指定相对路径前缀的目录是否存在。
132
+ * @param files 文件节点列表
133
+ * @param dirPrefix 待查找的目录前缀(如 "agents/")
134
+ */
135
+ function hasDirectory(files: FileNode[], dirPrefix: string): boolean {
136
+ const normalized = dirPrefix.replace(/\\/g, '/');
137
+ return files.some((f) => {
138
+ const rel = f.relativePath.replace(/\\/g, '/');
139
+ return rel === normalized.replace(/\/$/, '') || rel.startsWith(normalized);
140
+ });
141
+ }
142
+
143
+ /**
144
+ * 对结果数组进行去重。
145
+ */
146
+ function unique(arr: string[]): string[] {
147
+ return [...new Set(arr)];
148
+ }
149
+
150
+ // ---------------------------------------------------------------------------
151
+ // 检测子流程
152
+ // ---------------------------------------------------------------------------
153
+
154
+ /**
155
+ * 从 package.json 中检测 Node.js 框架和包管理器。
156
+ */
157
+ async function detectFromPackageJson(
158
+ rootPath: string,
159
+ files: FileNode[],
160
+ result: TechStackResult,
161
+ ): Promise<void> {
162
+ const content = await safeReadFile(path.join(rootPath, 'package.json'));
163
+ if (!content) return;
164
+
165
+ result.configFiles.push('package.json');
166
+
167
+ let pkg: Record<string, unknown>;
168
+ try {
169
+ pkg = JSON.parse(content) as Record<string, unknown>;
170
+ } catch {
171
+ return;
172
+ }
173
+
174
+ // 合并 dependencies 和 devDependencies
175
+ const deps: Record<string, unknown> = {
176
+ ...((pkg.dependencies as Record<string, unknown>) ?? {}),
177
+ ...((pkg.devDependencies as Record<string, unknown>) ?? {}),
178
+ };
179
+
180
+ for (const [depName, frameworkName] of Object.entries(NODE_FRAMEWORK_MAP)) {
181
+ if (depName in deps) {
182
+ result.frameworks.push(frameworkName);
183
+ }
184
+ }
185
+
186
+ // 检测包管理器(通过锁文件)
187
+ for (const [lockFile, managerName] of Object.entries(LOCKFILE_MAP)) {
188
+ if (hasFile(files, lockFile)) {
189
+ result.packageManagers.push(managerName);
190
+ }
191
+ }
192
+ }
193
+
194
+ /**
195
+ * 从 requirements.txt 中检测 Python 框架。
196
+ */
197
+ async function detectFromRequirementsTxt(
198
+ rootPath: string,
199
+ result: TechStackResult,
200
+ ): Promise<void> {
201
+ const content = await safeReadFile(path.join(rootPath, 'requirements.txt'));
202
+ if (!content) return;
203
+
204
+ result.configFiles.push('requirements.txt');
205
+ const lower = content.toLowerCase();
206
+
207
+ for (const [keyword, frameworkName] of Object.entries(PYTHON_FRAMEWORK_MAP)) {
208
+ if (lower.includes(keyword)) {
209
+ result.frameworks.push(frameworkName);
210
+ }
211
+ }
212
+ }
213
+
214
+ /**
215
+ * 从 pyproject.toml 中检测 Python 框架。
216
+ */
217
+ async function detectFromPyprojectToml(
218
+ rootPath: string,
219
+ result: TechStackResult,
220
+ ): Promise<void> {
221
+ const content = await safeReadFile(path.join(rootPath, 'pyproject.toml'));
222
+ if (!content) return;
223
+
224
+ result.configFiles.push('pyproject.toml');
225
+ const lower = content.toLowerCase();
226
+
227
+ for (const [keyword, frameworkName] of Object.entries(PYTHON_FRAMEWORK_MAP)) {
228
+ if (lower.includes(keyword)) {
229
+ result.frameworks.push(frameworkName);
230
+ }
231
+ }
232
+ }
233
+
234
+ /**
235
+ * 从 docker-compose 配置中检测数据库和服务。
236
+ * 同时检测 docker-compose.yml 和 docker-compose.yaml 两种命名。
237
+ */
238
+ async function detectFromDockerCompose(
239
+ rootPath: string,
240
+ result: TechStackResult,
241
+ ): Promise<void> {
242
+ const candidates = ['docker-compose.yml', 'docker-compose.yaml'];
243
+
244
+ for (const fileName of candidates) {
245
+ const content = await safeReadFile(path.join(rootPath, fileName));
246
+ if (!content) continue;
247
+
248
+ result.configFiles.push(fileName);
249
+ const lower = content.toLowerCase();
250
+
251
+ for (const [keyword, info] of Object.entries(DOCKER_SERVICE_MAP)) {
252
+ if (lower.includes(keyword)) {
253
+ if (info.kind === 'database') {
254
+ result.databases.push(info.name);
255
+ } else {
256
+ result.services.push(info.name);
257
+ }
258
+ }
259
+ }
260
+ }
261
+ }
262
+
263
+ /**
264
+ * 通过 Cargo.toml / go.mod 检测 Rust / Go 语言。
265
+ */
266
+ async function detectFromLanguageManifests(
267
+ rootPath: string,
268
+ result: TechStackResult,
269
+ ): Promise<void> {
270
+ const cargoContent = await safeReadFile(path.join(rootPath, 'Cargo.toml'));
271
+ if (cargoContent) {
272
+ result.languages.push('Rust');
273
+ result.configFiles.push('Cargo.toml');
274
+ }
275
+
276
+ const goModContent = await safeReadFile(path.join(rootPath, 'go.mod'));
277
+ if (goModContent) {
278
+ result.languages.push('Go');
279
+ result.configFiles.push('go.mod');
280
+ }
281
+ }
282
+
283
+ /**
284
+ * 根据文件扩展名分布统计语言,仅报告文件数超过阈值的语言。
285
+ */
286
+ function detectLanguagesFromExtensions(files: FileNode[], result: TechStackResult): void {
287
+ const counts = new Map<string, number>();
288
+
289
+ for (const file of files) {
290
+ if (file.nodeType !== 'file') continue;
291
+ const ext = path.extname(file.relativePath).toLowerCase();
292
+ const lang = EXTENSION_LANGUAGE_MAP[ext];
293
+ if (lang) {
294
+ counts.set(lang, (counts.get(lang) ?? 0) + 1);
295
+ }
296
+ }
297
+
298
+ for (const [lang, count] of counts) {
299
+ if (count >= LANGUAGE_FILE_THRESHOLD) {
300
+ result.languages.push(lang);
301
+ }
302
+ }
303
+ }
304
+
305
+ /**
306
+ * 根据目录结构模式检测架构特征(Agent 架构、Workflow 编排等)。
307
+ */
308
+ function detectDirectoryPatterns(files: FileNode[], result: TechStackResult): void {
309
+ // Agent 架构:app/agents 或 agents/
310
+ if (hasDirectory(files, 'app/agents/') || hasDirectory(files, 'agents/')) {
311
+ result.frameworks.push('Agent Architecture');
312
+ }
313
+
314
+ // Workflow 编排:workflows/
315
+ if (hasDirectory(files, 'workflows/')) {
316
+ result.frameworks.push('Workflow Orchestration');
317
+ }
318
+ }
319
+
320
+ // ---------------------------------------------------------------------------
321
+ // 主入口
322
+ // ---------------------------------------------------------------------------
323
+
324
+ /**
325
+ * 检测项目技术栈。
326
+ *
327
+ * 通过读取配置文件内容和分析文件扩展名分布,自动识别项目使用的
328
+ * 编程语言、框架、包管理器、数据库和服务。
329
+ *
330
+ * @param rootPath 项目根目录的绝对路径
331
+ * @param files FileScanner 输出的文件节点列表
332
+ * @returns 技术栈检测结果
333
+ *
334
+ * @example
335
+ * ```ts
336
+ * const result = await detectTechStack('/path/to/project', files);
337
+ * console.log(result.frameworks); // ['Next.js', 'FastAPI']
338
+ * ```
339
+ */
340
+ export async function detectTechStack(
341
+ rootPath: string,
342
+ files: FileNode[],
343
+ ): Promise<TechStackResult> {
344
+ const result: TechStackResult = {
345
+ languages: [],
346
+ frameworks: [],
347
+ packageManagers: [],
348
+ databases: [],
349
+ services: [],
350
+ configFiles: [],
351
+ };
352
+
353
+ // 并行执行所有检测子流程
354
+ await Promise.all([
355
+ detectFromPackageJson(rootPath, files, result),
356
+ detectFromRequirementsTxt(rootPath, result),
357
+ detectFromPyprojectToml(rootPath, result),
358
+ detectFromDockerCompose(rootPath, result),
359
+ detectFromLanguageManifests(rootPath, result),
360
+ ]);
361
+
362
+ // 基于文件扩展名统计语言(同步操作)
363
+ detectLanguagesFromExtensions(files, result);
364
+
365
+ // 目录模式检测(同步操作)
366
+ detectDirectoryPatterns(files, result);
367
+
368
+ // 去重
369
+ result.languages = unique(result.languages);
370
+ result.frameworks = unique(result.frameworks);
371
+ result.packageManagers = unique(result.packageManagers);
372
+ result.databases = unique(result.databases);
373
+ result.services = unique(result.services);
374
+ result.configFiles = unique(result.configFiles);
375
+
376
+ return result;
377
+ }
@@ -0,0 +1,36 @@
1
+ /**
2
+ * generator 模块入口
3
+ * 负责将静态分析结果组装、排版为符合规范的 Markdown 格式 Wiki 文档,并生成 Mermaid 关系图表。
4
+ */
5
+
6
+ export {
7
+ generateTable,
8
+ formatSourceRef,
9
+ formatSectionSource,
10
+ formatDiagramSource,
11
+ formatCiteBlock,
12
+ formatToc,
13
+ formatFileTable,
14
+ wrapMermaid,
15
+ formatTroubleshootingTable,
16
+ assembleWikiPage,
17
+ } from './markdown-generator.js';
18
+
19
+ export {
20
+ generateArchitectureDiagram,
21
+ generateERDiagram,
22
+ generateDependencyDiagram,
23
+ generateApiDiagram,
24
+ generateTechStackDiagram,
25
+ } from './mermaid-generator.js';
26
+
27
+ export {
28
+ generateSidebar,
29
+ generateHome,
30
+ } from './sidebar-generator.js';
31
+
32
+ export {
33
+ WikiGenerator,
34
+ type WikiGeneratorConfig,
35
+ type PlannedPage,
36
+ } from './wiki-generator.js';
@@ -0,0 +1,277 @@
1
+ import { type SourceReference } from '../models/index.js';
2
+
3
+ /**
4
+ * Markdown 文档生成工具集
5
+ * 提供格式化表格、源码引用链接、页面结构等工具函数
6
+ */
7
+
8
+ /**
9
+ * 生成 Markdown 表格
10
+ */
11
+ export function generateTable(headers: string[], rows: string[][]): string {
12
+ if (rows.length === 0) return '';
13
+
14
+ const headerLine = `| ${headers.join(' | ')} |`;
15
+ const separatorLine = `| ${headers.map(() => '---').join(' | ')} |`;
16
+ const dataLines = rows.map(row => `| ${row.join(' | ')} |`);
17
+
18
+ return [headerLine, separatorLine, ...dataLines].join('\n');
19
+ }
20
+
21
+ /**
22
+ * 生成源码引用链接
23
+ * 格式: [file.py:1-50](file://project/path/to/file.py#L1-L50)
24
+ */
25
+ export function formatSourceRef(
26
+ ref: SourceReference,
27
+ projectRoot: string
28
+ ): string {
29
+ const fileName = ref.filePath.split('/').pop() || ref.filePath;
30
+ const label = `${fileName}:${ref.startLine}-${ref.endLine}`;
31
+ const normalizedRoot = projectRoot.replace(/\\/g, '/');
32
+ const normalizedPath = ref.filePath.replace(/\\/g, '/');
33
+ return `[${label}](file://${normalizedRoot}/${normalizedPath}#L${ref.startLine}-L${ref.endLine})`;
34
+ }
35
+
36
+ /**
37
+ * 生成源码引用块(章节来源)
38
+ */
39
+ export function formatSectionSource(refs: SourceReference[], projectRoot: string): string {
40
+ if (refs.length === 0) return '';
41
+
42
+ const lines = [
43
+ '',
44
+ '> **章节来源**',
45
+ ...refs.map(ref => `> - ${formatSourceRef(ref, projectRoot)}`),
46
+ ''
47
+ ];
48
+ return lines.join('\n');
49
+ }
50
+
51
+ /**
52
+ * 生成图表来源引用
53
+ */
54
+ export function formatDiagramSource(refs: SourceReference[], projectRoot: string): string {
55
+ if (refs.length === 0) return '';
56
+
57
+ const lines = [
58
+ '',
59
+ '> **图表来源**',
60
+ ...refs.map(ref => `> - ${formatSourceRef(ref, projectRoot)}`),
61
+ ''
62
+ ];
63
+ return lines.join('\n');
64
+ }
65
+
66
+ /**
67
+ * 生成 <cite> 引用块,列出所有被引用的源码文件
68
+ */
69
+ export function formatCiteBlock(refs: SourceReference[], projectRoot: string): string {
70
+ if (refs.length === 0) return '';
71
+
72
+ // 去重文件路径
73
+ const uniqueFiles = [...new Set(refs.map(r => r.filePath))];
74
+ const normalizedRoot = projectRoot.replace(/\\/g, '/');
75
+
76
+ const lines = [
77
+ '<cite>',
78
+ '**本文引用的文件**',
79
+ ...uniqueFiles.map(fp => {
80
+ const normalized = fp.replace(/\\/g, '/');
81
+ return `- [${fp}](file://${normalizedRoot}/${normalized})`;
82
+ }),
83
+ '</cite>',
84
+ ''
85
+ ];
86
+ return lines.join('\n');
87
+ }
88
+
89
+ /**
90
+ * 生成目录(Table of Contents)
91
+ */
92
+ export function formatToc(headings: string[]): string {
93
+ const lines = [
94
+ '## 目录',
95
+ '',
96
+ ...headings.map((h, i) => `${i + 1}. [${h}](#${headingToAnchor(h)})`)
97
+ ];
98
+ return lines.join('\n');
99
+ }
100
+
101
+ /**
102
+ * 将标题转换为 Markdown 锚点
103
+ */
104
+ function headingToAnchor(heading: string): string {
105
+ return heading
106
+ .toLowerCase()
107
+ .replace(/[^\w\u4e00-\u9fff\s-]/g, '')
108
+ .replace(/\s+/g, '-')
109
+ .replace(/-+/g, '-')
110
+ .replace(/^-|-$/g, '');
111
+ }
112
+
113
+ /**
114
+ * 生成文件列表表格
115
+ */
116
+ export function formatFileTable(
117
+ files: Array<{ path: string; description: string }>
118
+ ): string {
119
+ const headers = ['文件', '作用'];
120
+ const rows = files.map(f => [`\`${f.path}\``, f.description]);
121
+ return generateTable(headers, rows);
122
+ }
123
+
124
+ /**
125
+ * 包裹 Mermaid 代码块
126
+ */
127
+ export function wrapMermaid(code: string): string {
128
+ return `\`\`\`mermaid\n${code}\n\`\`\``;
129
+ }
130
+
131
+ /**
132
+ * 生成故障排查表格
133
+ */
134
+ export function formatTroubleshootingTable(
135
+ issues: Array<{ problem: string; cause: string; resolution: string }>
136
+ ): string {
137
+ const headers = ['问题', '可能原因', '排查方式'];
138
+ const rows = issues.map(i => [i.problem, i.cause, i.resolution]);
139
+ return generateTable(headers, rows);
140
+ }
141
+
142
+ /**
143
+ * 组装完整的 Wiki 页面
144
+ * 将各部分内容按照标准模板结构拼装
145
+ */
146
+ export function assembleWikiPage(sections: {
147
+ title: string;
148
+ citeRefs: SourceReference[];
149
+ projectRoot: string;
150
+ introduction: string;
151
+ projectStructure?: string;
152
+ projectStructureDiagram?: string;
153
+ coreComponents?: string;
154
+ architectureOverview?: string;
155
+ architectureDiagram?: string;
156
+ detailedAnalysis?: string;
157
+ dependencyAnalysis?: string;
158
+ dependencyDiagram?: string;
159
+ troubleshooting?: string;
160
+ conclusion?: string;
161
+ appendix?: string;
162
+ }): string {
163
+ const parts: string[] = [];
164
+
165
+ // 1. 标题
166
+ parts.push(`# ${sections.title}`);
167
+ parts.push('');
168
+
169
+ // 2. cite 引用块
170
+ if (sections.citeRefs.length > 0) {
171
+ parts.push(formatCiteBlock(sections.citeRefs, sections.projectRoot));
172
+ }
173
+
174
+ // 收集目录项
175
+ const tocItems: string[] = [];
176
+ if (sections.introduction) tocItems.push('引言');
177
+ if (sections.projectStructure) tocItems.push('项目结构与关系');
178
+ if (sections.coreComponents) tocItems.push('核心组件总览');
179
+ if (sections.architectureOverview) tocItems.push('架构总览');
180
+ if (sections.detailedAnalysis) tocItems.push('详细组件分析');
181
+ if (sections.dependencyAnalysis) tocItems.push('依赖分析');
182
+ if (sections.troubleshooting) tocItems.push('故障排查指南');
183
+ if (sections.conclusion) tocItems.push('结论');
184
+ if (sections.appendix) tocItems.push('附录');
185
+
186
+ // 3. 目录
187
+ if (tocItems.length > 0) {
188
+ parts.push(formatToc(tocItems));
189
+ parts.push('');
190
+ }
191
+
192
+ // 4. 引言
193
+ if (sections.introduction) {
194
+ parts.push('## 引言');
195
+ parts.push('');
196
+ parts.push(sections.introduction);
197
+ parts.push('');
198
+ }
199
+
200
+ // 5. 项目结构
201
+ if (sections.projectStructure) {
202
+ parts.push('## 项目结构与关系');
203
+ parts.push('');
204
+ parts.push(sections.projectStructure);
205
+ if (sections.projectStructureDiagram) {
206
+ parts.push('');
207
+ parts.push(wrapMermaid(sections.projectStructureDiagram));
208
+ }
209
+ parts.push('');
210
+ }
211
+
212
+ // 6. 核心组件
213
+ if (sections.coreComponents) {
214
+ parts.push('## 核心组件总览');
215
+ parts.push('');
216
+ parts.push(sections.coreComponents);
217
+ parts.push('');
218
+ }
219
+
220
+ // 7. 架构总览
221
+ if (sections.architectureOverview) {
222
+ parts.push('## 架构总览');
223
+ parts.push('');
224
+ parts.push(sections.architectureOverview);
225
+ if (sections.architectureDiagram) {
226
+ parts.push('');
227
+ parts.push(wrapMermaid(sections.architectureDiagram));
228
+ }
229
+ parts.push('');
230
+ }
231
+
232
+ // 8. 详细组件分析
233
+ if (sections.detailedAnalysis) {
234
+ parts.push('## 详细组件分析');
235
+ parts.push('');
236
+ parts.push(sections.detailedAnalysis);
237
+ parts.push('');
238
+ }
239
+
240
+ // 9. 依赖分析
241
+ if (sections.dependencyAnalysis) {
242
+ parts.push('## 依赖分析');
243
+ parts.push('');
244
+ parts.push(sections.dependencyAnalysis);
245
+ if (sections.dependencyDiagram) {
246
+ parts.push('');
247
+ parts.push(wrapMermaid(sections.dependencyDiagram));
248
+ }
249
+ parts.push('');
250
+ }
251
+
252
+ // 10. 故障排查
253
+ if (sections.troubleshooting) {
254
+ parts.push('## 故障排查指南');
255
+ parts.push('');
256
+ parts.push(sections.troubleshooting);
257
+ parts.push('');
258
+ }
259
+
260
+ // 11. 结论
261
+ if (sections.conclusion) {
262
+ parts.push('## 结论');
263
+ parts.push('');
264
+ parts.push(sections.conclusion);
265
+ parts.push('');
266
+ }
267
+
268
+ // 12. 附录
269
+ if (sections.appendix) {
270
+ parts.push('## 附录');
271
+ parts.push('');
272
+ parts.push(sections.appendix);
273
+ parts.push('');
274
+ }
275
+
276
+ return parts.join('\n');
277
+ }