autosnippet 3.0.10 → 3.0.13

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 (56) hide show
  1. package/bin/cli.js +64 -1
  2. package/config/default.json +9 -0
  3. package/dashboard/dist/assets/{index-I2ySoCmF.js → index-Bnm26ulL.js} +47 -47
  4. package/dashboard/dist/index.html +1 -1
  5. package/lib/cli/SetupService.js +92 -5
  6. package/lib/cli/UpgradeService.js +14 -5
  7. package/lib/core/discovery/GenericDiscoverer.js +4 -28
  8. package/lib/external/mcp/handlers/bootstrap/base-dimensions.js +246 -0
  9. package/lib/external/mcp/handlers/bootstrap/pipeline/checkpoint.js +80 -0
  10. package/lib/external/mcp/handlers/bootstrap/pipeline/dimension-configs.js +275 -0
  11. package/lib/external/mcp/handlers/bootstrap/pipeline/noAiFallback.js +600 -0
  12. package/lib/external/mcp/handlers/bootstrap/pipeline/orchestrator.js +125 -342
  13. package/lib/external/mcp/handlers/bootstrap/refine.js +362 -0
  14. package/lib/external/mcp/handlers/bootstrap.js +6 -590
  15. package/lib/external/mcp/handlers/browse.js +119 -9
  16. package/lib/external/mcp/handlers/guard.js +25 -6
  17. package/lib/external/mcp/handlers/search.js +56 -24
  18. package/lib/http/routes/guardRules.js +9 -17
  19. package/lib/injection/ServiceContainer.js +12 -3
  20. package/lib/platform/ios/xcode/XcodeImportResolver.js +434 -0
  21. package/lib/platform/ios/xcode/XcodeIntegration.js +40 -659
  22. package/lib/platform/ios/xcode/XcodeWriteUtils.js +220 -0
  23. package/lib/service/chat/ChatAgent.js +39 -418
  24. package/lib/service/chat/ChatAgentPrompts.js +149 -0
  25. package/lib/service/chat/ChatAgentTasks.js +297 -0
  26. package/lib/service/chat/tools/_shared.js +61 -0
  27. package/lib/service/chat/tools/ai-analysis.js +284 -0
  28. package/lib/service/chat/tools/ast-graph.js +681 -0
  29. package/lib/service/chat/tools/composite.js +496 -0
  30. package/lib/service/chat/tools/guard.js +265 -0
  31. package/lib/service/chat/tools/index.js +250 -0
  32. package/lib/service/chat/tools/infrastructure.js +222 -0
  33. package/lib/service/chat/tools/knowledge-graph.js +234 -0
  34. package/lib/service/chat/tools/lifecycle.js +469 -0
  35. package/lib/service/chat/tools/project-access.js +923 -0
  36. package/lib/service/chat/tools/query.js +264 -0
  37. package/lib/service/chat/tools.js +14 -3994
  38. package/lib/service/cursor/AgentInstructionsGenerator.js +395 -0
  39. package/lib/service/cursor/CursorDeliveryPipeline.js +70 -11
  40. package/lib/service/cursor/FileProtection.js +116 -0
  41. package/lib/service/cursor/KnowledgeCompressor.js +61 -11
  42. package/lib/service/cursor/SkillsSyncer.js +5 -3
  43. package/lib/service/cursor/TopicClassifier.js +19 -3
  44. package/lib/service/guard/ExclusionManager.js +26 -2
  45. package/lib/service/guard/GuardCheckEngine.js +38 -370
  46. package/lib/service/guard/GuardCodeChecks.js +362 -0
  47. package/lib/service/guard/GuardCrossFileChecks.js +307 -0
  48. package/lib/service/guard/GuardPatternUtils.js +180 -0
  49. package/lib/service/guard/GuardService.js +80 -38
  50. package/lib/service/module/ModuleService.js +1 -0
  51. package/lib/service/search/SearchEngine.js +10 -2
  52. package/lib/service/wiki/WikiGenerator.js +226 -1532
  53. package/lib/service/wiki/WikiRenderers.js +1878 -0
  54. package/lib/service/wiki/WikiUtils.js +907 -0
  55. package/lib/shared/LanguageService.js +299 -0
  56. package/package.json +1 -1
@@ -0,0 +1,1878 @@
1
+ /**
2
+ * WikiRenderers.js — Wiki 文档渲染函数
3
+ *
4
+ * 从 WikiGenerator.js 中提取的 Markdown 渲染器和 AI Prompt 构建函数。
5
+ * 所有函数均为无状态纯函数(不依赖 class 实例)。
6
+ *
7
+ * @module WikiRenderers
8
+ */
9
+
10
+ import path from 'node:path';
11
+ import { LanguageService } from '../../shared/LanguageService.js';
12
+ import {
13
+ slug,
14
+ mermaidId,
15
+ getModuleSourceFiles,
16
+ getInheritanceRoots,
17
+ inferModulePurpose,
18
+ getLangTerms,
19
+ } from './WikiUtils.js';
20
+
21
+ // Re-export BUILD_SYSTEM_MARKERS for renderGettingStarted internals
22
+ import { BUILD_SYSTEM_MARKERS } from './WikiUtils.js';
23
+
24
+ // ═══ AI Prompt 构建 ════════════════════════════════════════
25
+
26
+ /**
27
+ * 为特定主题构建 AI 撰写 prompt (V3 AI-first 核心)
28
+ *
29
+ * 关键区别: 不是润色骨架,而是提供丰富数据让 AI 写完整文章
30
+ *
31
+ * @param {object} topic
32
+ * @param {object} data - { projectInfo, astInfo, moduleInfo, knowledgeInfo }
33
+ * @param {boolean} isZh
34
+ * @param {object|null} codeEntityGraph
35
+ * @returns {string}
36
+ */
37
+ export function buildArticlePrompt(topic, data, isZh, codeEntityGraph) {
38
+ const { projectInfo, astInfo, moduleInfo, knowledgeInfo } = data;
39
+ const parts = [];
40
+ const langTerms = getLangTerms(projectInfo.primaryLanguage || 'unknown');
41
+ const tl = isZh ? langTerms.typeLabel.zh : langTerms.typeLabel.en;
42
+ const il = isZh ? langTerms.interfaceLabel.zh : langTerms.interfaceLabel.en;
43
+
44
+ // 公共项目上下文
45
+ parts.push(`# 项目: ${projectInfo.name}`);
46
+ parts.push(
47
+ `源文件数: ${projectInfo.sourceFiles.length}, 模块: ${moduleInfo.targets.length}, 活跃知识条目: ${knowledgeInfo.recipes.length}`
48
+ );
49
+ if (projectInfo.languages) {
50
+ parts.push(
51
+ `语言分布: ${Object.entries(projectInfo.languages)
52
+ .sort((a, b) => b[1] - a[1])
53
+ .map(([l, c]) => `${l}(${c})`)
54
+ .join(', ')}`
55
+ );
56
+ }
57
+ parts.push('');
58
+
59
+ switch (topic.type) {
60
+ case 'overview': {
61
+ parts.push('## 任务: 撰写项目概述文档');
62
+ parts.push('');
63
+
64
+ // 项目类型
65
+ const buildTypes = (projectInfo.buildSystems || []).map((b) => b.buildTool);
66
+ if (projectInfo.hasPackageSwift && !buildTypes.some((t) => t.includes('SPM'))) {
67
+ buildTypes.push('SPM');
68
+ }
69
+ if (projectInfo.hasPodfile && !buildTypes.includes('CocoaPods')) {
70
+ buildTypes.push('CocoaPods');
71
+ }
72
+ if (projectInfo.hasXcodeproj) {
73
+ buildTypes.push('Xcode Project');
74
+ }
75
+ if (buildTypes.length > 0) {
76
+ parts.push(`构建系统: ${buildTypes.join(' + ')}`);
77
+ }
78
+ parts.push('');
79
+
80
+ // 模块结构
81
+ if (moduleInfo.targets.length > 0) {
82
+ parts.push('### 模块列表');
83
+ for (const t of moduleInfo.targets) {
84
+ const files = getModuleSourceFiles(t, projectInfo);
85
+ const cls = astInfo.classNamesByModule?.[t.name]?.length || 0;
86
+ const deps = (t.dependencies || t.info?.dependencies || []).map((d) =>
87
+ typeof d === 'string' ? d : d.name
88
+ );
89
+ parts.push(
90
+ `- ${t.name} (${t.type || 'target'}): ${files.length} 文件, ${cls} 个类型${deps.length > 0 ? `, 依赖: ${deps.join(', ')}` : ''}`
91
+ );
92
+ }
93
+ parts.push('');
94
+ }
95
+
96
+ // AST 概况
97
+ if (astInfo.overview) {
98
+ parts.push('### 代码规模');
99
+ parts.push(
100
+ `${tl}: ${astInfo.overview.totalClasses || 0}, ${il}: ${astInfo.overview.totalProtocols || 0}, 方法: ${astInfo.overview.totalMethods || 0}`
101
+ );
102
+ parts.push('');
103
+ }
104
+
105
+ // 可用的其他文档(用于导航链接)
106
+ const otherTopics = (topic._allTopics || []).filter((t) => t.type !== 'overview');
107
+ if (otherTopics.length > 0) {
108
+ parts.push('### 需要包含的导航链接');
109
+ for (const t of otherTopics) {
110
+ parts.push(`- [${t.title}](${t.path})`);
111
+ }
112
+ parts.push('');
113
+ }
114
+
115
+ parts.push('要求: 撰写完整的项目概述文档。');
116
+ parts.push(
117
+ '包含: 项目简介(解释项目做什么)、模块总览(表格形式)、技术栈分析、核心数据指标、文档导航索引。'
118
+ );
119
+ parts.push('不要只列数据 — 要解释项目的定位、各模块的职责和协作关系。');
120
+ break;
121
+ }
122
+
123
+ case 'architecture': {
124
+ parts.push('## 任务: 撰写架构分析文档');
125
+ parts.push('');
126
+
127
+ if (moduleInfo.targets.length > 0) {
128
+ parts.push('### 模块及依赖关系');
129
+ for (const t of moduleInfo.targets) {
130
+ const deps = (t.dependencies || t.info?.dependencies || []).map((d) =>
131
+ typeof d === 'string' ? d : d.name
132
+ );
133
+ parts.push(
134
+ `- ${t.name} (${t.type || 'target'})${deps.length > 0 ? ` → 依赖: ${deps.join(', ')}` : ''}`
135
+ );
136
+ }
137
+ parts.push('');
138
+ }
139
+
140
+ if (astInfo.overview?.topLevelModules?.length > 0) {
141
+ parts.push(`### 顶层模块: ${astInfo.overview.topLevelModules.join(', ')}`);
142
+ const cpm = astInfo.overview.classesPerModule || {};
143
+ for (const mod of astInfo.overview.topLevelModules) {
144
+ parts.push(` ${mod}: ${cpm[mod] || 0} 个类`);
145
+ }
146
+ parts.push('');
147
+ }
148
+
149
+ if (astInfo.overview?.entryPoints?.length > 0) {
150
+ parts.push(`### 入口点: ${astInfo.overview.entryPoints.join(', ')}`);
151
+ parts.push('');
152
+ }
153
+
154
+ const roots = getInheritanceRoots(codeEntityGraph);
155
+ if (roots.length > 0) {
156
+ parts.push('### 核心继承关系');
157
+ for (const r of roots.slice(0, 10)) {
158
+ parts.push(`- ${r.name} → ${(r.children || []).slice(0, 5).join(', ')}`);
159
+ }
160
+ parts.push('');
161
+ }
162
+
163
+ parts.push('要求: 撰写架构分析文档。');
164
+ parts.push(
165
+ '包含: 模块依赖图(使用 Mermaid graph TD 语法)、分层架构分析(解释每层的职责)、模块间协作关系、架构设计决策阐述。'
166
+ );
167
+ parts.push('用 Mermaid 绘制依赖关系图和继承层次图。分析为什么采用这种架构。');
168
+ break;
169
+ }
170
+
171
+ case 'module': {
172
+ const md = topic._moduleData;
173
+ const target = md.target;
174
+ const moduleFiles = md.moduleFiles;
175
+ const moduleClasses = astInfo.classNamesByModule?.[target.name] || [];
176
+ const moduleProtocols = astInfo.protocolNamesByModule?.[target.name] || [];
177
+ const deps = target.dependencies || target.info?.dependencies || [];
178
+
179
+ parts.push(`## 任务: 撰写 "${target.name}" 模块的深度文档`);
180
+ parts.push('');
181
+ parts.push('### 模块基本信息');
182
+ parts.push(`- 类型: ${target.type || 'target'}`);
183
+ const tPath = target.path || target.info?.path;
184
+ if (tPath) {
185
+ parts.push(`- 路径: ${tPath}`);
186
+ }
187
+ if (target.packageName) {
188
+ parts.push(`- 所属包: ${target.packageName}`);
189
+ }
190
+ parts.push(`- 源文件: ${moduleFiles.length} 个`);
191
+ parts.push(`- ${tl}: ${moduleClasses.length} 个`);
192
+ parts.push(`- ${il}: ${moduleProtocols.length} 个`);
193
+ parts.push('');
194
+
195
+ if (deps.length > 0) {
196
+ parts.push(
197
+ `### 依赖: ${deps.map((d) => (typeof d === 'string' ? d : d.name)).join(', ')}`
198
+ );
199
+ parts.push('');
200
+ }
201
+
202
+ if (moduleClasses.length > 0) {
203
+ parts.push(`### 类型列表: ${moduleClasses.slice(0, 30).join(', ')}`);
204
+ parts.push('');
205
+ }
206
+
207
+ if (moduleProtocols.length > 0) {
208
+ parts.push(`### ${il}列表: ${moduleProtocols.slice(0, 20).join(', ')}`);
209
+ parts.push('');
210
+ }
211
+
212
+ // 关键源文件名(帮助 AI 推断模块功能)
213
+ if (moduleFiles.length > 0) {
214
+ const keyFiles = moduleFiles.slice(0, 25).map((f) => path.basename(f));
215
+ parts.push(`### 关键源文件: ${keyFiles.join(', ')}`);
216
+ parts.push('');
217
+ }
218
+
219
+ // 相关 recipes
220
+ const related = knowledgeInfo.recipes.filter((r) => {
221
+ const json = r.toJSON ? r.toJSON() : r;
222
+ return (
223
+ json.moduleName === target.name ||
224
+ json.tags?.includes(target.name) ||
225
+ json.title?.includes(target.name)
226
+ );
227
+ });
228
+ if (related.length > 0) {
229
+ parts.push(`### 相关知识条目 (${related.length})`);
230
+ for (const r of related.slice(0, 10)) {
231
+ const json = r.toJSON ? r.toJSON() : r;
232
+ parts.push(`- ${json.title}: ${json.description || ''}`);
233
+ if (json.reasoning?.whyStandard) {
234
+ parts.push(` 为什么: ${json.reasoning.whyStandard}`);
235
+ }
236
+ }
237
+ parts.push('');
238
+ }
239
+
240
+ parts.push('要求: 撰写模块深度分析文档。');
241
+ parts.push(
242
+ '包含: 模块职责说明(从文件名和类名推断功能意图)、核心类型分析(不是简单罗列而是解释每个类的角色)、依赖关系分析、设计模式识别。'
243
+ );
244
+ parts.push('如果能推断出数据流或协作关系,请用 Mermaid 图表展示。');
245
+ break;
246
+ }
247
+
248
+ case 'getting-started': {
249
+ parts.push('## 任务: 撰写快速上手指南');
250
+ parts.push('');
251
+
252
+ // 列出检测到的构建系统
253
+ const bs = projectInfo.buildSystems || [];
254
+ if (bs.length > 0) {
255
+ parts.push(`构建系统: ${bs.map((b) => b.buildTool).join(', ')}`);
256
+ } else {
257
+ // 兼容旧数据
258
+ if (projectInfo.hasPackageSwift) parts.push('构建系统: Swift Package Manager');
259
+ if (projectInfo.hasPodfile) parts.push('构建系统: CocoaPods');
260
+ if (projectInfo.hasXcodeproj) parts.push('构建系统: Xcode Project');
261
+ }
262
+ parts.push('');
263
+
264
+ if (moduleInfo.targets.length > 0) {
265
+ const mainTargets = moduleInfo.targets.filter((t) => t.type !== 'test');
266
+ const testTargets = moduleInfo.targets.filter((t) => t.type === 'test');
267
+ if (mainTargets.length > 0) {
268
+ parts.push(`主要 Target: ${mainTargets.map((t) => t.name).join(', ')}`);
269
+ }
270
+ if (testTargets.length > 0) {
271
+ parts.push(`测试 Target: ${testTargets.map((t) => t.name).join(', ')}`);
272
+ }
273
+ parts.push('');
274
+ }
275
+
276
+ if (astInfo.overview?.entryPoints?.length > 0) {
277
+ parts.push(`入口点: ${astInfo.overview.entryPoints.join(', ')}`);
278
+ parts.push('');
279
+ }
280
+
281
+ parts.push('要求: 撰写开发者快速上手指南。');
282
+ parts.push(
283
+ '包含: 环境要求、项目获取、依赖安装、构建步骤(具体命令)、运行测试、项目目录结构说明。'
284
+ );
285
+ parts.push('语句清晰,步骤明确,适合新人阅读。');
286
+ break;
287
+ }
288
+
289
+ case 'patterns': {
290
+ parts.push('## 任务: 撰写代码模式与最佳实践文档');
291
+ parts.push('');
292
+
293
+ const groups = {};
294
+ for (const r of knowledgeInfo.recipes) {
295
+ const json = r.toJSON ? r.toJSON() : r;
296
+ const cat = json.category || 'Other';
297
+ if (!groups[cat]) {
298
+ groups[cat] = [];
299
+ }
300
+ groups[cat].push(json);
301
+ }
302
+
303
+ for (const [cat, items] of Object.entries(groups).sort()) {
304
+ parts.push(`### ${cat} (${items.length} 条)`);
305
+ for (const item of items.slice(0, 8)) {
306
+ parts.push(`- ${item.title}: ${item.description || 'N/A'}`);
307
+ if (item.doClause) {
308
+ parts.push(` 应当: ${item.doClause}`);
309
+ }
310
+ if (item.dontClause) {
311
+ parts.push(` 避免: ${item.dontClause}`);
312
+ }
313
+ if (item.content?.pattern) {
314
+ parts.push(` 代码片段: ${item.content.pattern.slice(0, 200)}`);
315
+ }
316
+ }
317
+ parts.push('');
318
+ }
319
+
320
+ parts.push('要求: 撰写代码模式文档。对每个分类进行总结分析,解释模式的意义和应用场景。');
321
+ parts.push(
322
+ '不要只列出条目 — 为每个分类写一段总结,解释该类模式的整体意图。附带代码示例(从数据中取)。'
323
+ );
324
+ break;
325
+ }
326
+
327
+ case 'pattern-category': {
328
+ const pd = topic._patternData;
329
+ parts.push(`## 任务: 撰写 "${pd.category}" 分类的代码模式文档`);
330
+ parts.push('');
331
+
332
+ for (const item of pd.recipes) {
333
+ parts.push(`### ${item.title}`);
334
+ if (item.description) {
335
+ parts.push(`描述: ${item.description}`);
336
+ }
337
+ if (item.doClause) {
338
+ parts.push(`应当: ${item.doClause}`);
339
+ }
340
+ if (item.dontClause) {
341
+ parts.push(`避免: ${item.dontClause}`);
342
+ }
343
+ if (item.reasoning?.whyStandard) {
344
+ parts.push(`原因: ${item.reasoning.whyStandard}`);
345
+ }
346
+ if (item.content?.pattern) {
347
+ parts.push('代码:');
348
+ parts.push('```');
349
+ parts.push(item.content.pattern.slice(0, 500));
350
+ parts.push('```');
351
+ }
352
+ parts.push('');
353
+ }
354
+
355
+ parts.push('要求: 撰写该分类的详细代码模式文档。');
356
+ parts.push(
357
+ '先写一段总结性概述,然后对每个模式做分析,解释为什么要遵循,给出正确和错误的对比示例。'
358
+ );
359
+ break;
360
+ }
361
+
362
+ case 'reference': {
363
+ parts.push(`## 任务: 撰写${il}参考文档`);
364
+ parts.push('');
365
+
366
+ const protoByModule = astInfo.protocolNamesByModule || {};
367
+ for (const [mod, protos] of Object.entries(protoByModule).sort()) {
368
+ if (protos.length > 0) {
369
+ parts.push(`### ${mod} 模块: ${protos.join(', ')}`);
370
+ }
371
+ }
372
+ parts.push('');
373
+ parts.push(
374
+ `总计: ${astInfo.protocols.length} 个${il}, ${astInfo.classes.length} 个${tl}`
375
+ );
376
+ parts.push('');
377
+ parts.push(
378
+ `要求: 撰写${il}参考文档。按模块分组,分析每个${il}的用途和意义,描述${il}之间的关系和设计意图。`
379
+ );
380
+ break;
381
+ }
382
+
383
+ case 'folder-overview': {
384
+ const profiles = topic._folderProfiles || [];
385
+ parts.push('## 任务: 撰写项目文件夹结构分析文档');
386
+ parts.push('');
387
+ parts.push('注意: 本项目的代码实体(类/函数/协议等)无法通过 AST 自动提取,');
388
+ parts.push('因此以「文件夹画像」方式进行结构分析。');
389
+ parts.push('');
390
+ parts.push(`### 发现 ${profiles.length} 个重要文件夹`);
391
+ parts.push('');
392
+ for (const fp of profiles) {
393
+ parts.push(`#### ${fp.relPath}`);
394
+ parts.push(`- 源文件: ${fp.fileCount} 个, 总大小: ${(fp.totalSize / 1024).toFixed(1)}KB`);
395
+ parts.push(`- 语言分布: ${Object.entries(fp.langBreakdown).map(([l, c]) => `${l}(${c})`).join(', ')}`);
396
+ if (fp.entryPoints.length > 0) {
397
+ parts.push(`- 入口文件: ${fp.entryPoints.join(', ')}`);
398
+ }
399
+ if (fp.namingPatterns.length > 0) {
400
+ parts.push(`- 命名约定: ${fp.namingPatterns.join(', ')}`);
401
+ }
402
+ if (fp.imports.length > 0) {
403
+ parts.push(`- 依赖引用: ${fp.imports.join(', ')}`);
404
+ }
405
+ if (fp.purpose) {
406
+ parts.push(`- 推断功能: ${fp.purpose.zh || fp.purpose.en || '-'}`);
407
+ }
408
+ if (fp.readme) {
409
+ parts.push(`- README 摘要: ${fp.readme.slice(0, 200)}`);
410
+ }
411
+ if (fp.headerComments.length > 0) {
412
+ parts.push(`- 代码注释: ${fp.headerComments.join('; ')}`);
413
+ }
414
+ parts.push('');
415
+ }
416
+ parts.push('要求: 撰写项目结构分析文档。');
417
+ parts.push('重点分析:');
418
+ parts.push('1. 项目整体架构分层 — 从文件夹结构推断项目架构(MVC/分层/微服务等)');
419
+ parts.push('2. 各文件夹的职责与协作关系 — 从文件命名和 import 关系推断');
420
+ parts.push('3. 用 Mermaid graph TD 画出文件夹之间的依赖关系图');
421
+ parts.push('4. 从命名约定分析团队编码规范');
422
+ parts.push('5. 对照文件夹的文件分布特征,评估项目的工程化程度');
423
+ break;
424
+ }
425
+
426
+ case 'folder-profile': {
427
+ const fp = topic._folderProfile;
428
+ parts.push(`## 任务: 撰写 "${fp.name}" 目录的深度分析文档`);
429
+ parts.push('');
430
+ parts.push('注意: 本项目的代码实体无法通过 AST 提取,以下分析基于文件夹画像。');
431
+ parts.push('');
432
+ parts.push('### 目录信息');
433
+ parts.push(`- 路径: ${fp.relPath}`);
434
+ parts.push(`- 源文件: ${fp.fileCount} 个`);
435
+ parts.push(`- 总大小: ${(fp.totalSize / 1024).toFixed(1)}KB`);
436
+ parts.push(`- 语言分布: ${Object.entries(fp.langBreakdown).map(([l, c]) => `${l}(${c})`).join(', ')}`);
437
+ parts.push('');
438
+
439
+ if (fp.entryPoints.length > 0) {
440
+ parts.push(`### 入口文件: ${fp.entryPoints.join(', ')}`);
441
+ parts.push('');
442
+ }
443
+
444
+ if (fp.readme) {
445
+ parts.push('### 目录 README');
446
+ parts.push(fp.readme.slice(0, 500));
447
+ parts.push('');
448
+ }
449
+
450
+ if (fp.fileNames.length > 0) {
451
+ parts.push(`### 文件列表 (${fp.fileNames.length} 个)`);
452
+ parts.push(fp.fileNames.slice(0, 40).join(', '));
453
+ parts.push('');
454
+ }
455
+
456
+ if (fp.namingPatterns.length > 0) {
457
+ parts.push(`### 命名约定: ${fp.namingPatterns.join(', ')}`);
458
+ parts.push('');
459
+ }
460
+
461
+ if (fp.imports.length > 0) {
462
+ parts.push(`### 依赖引用: ${fp.imports.join(', ')}`);
463
+ parts.push('');
464
+ }
465
+
466
+ if (fp.headerComments.length > 0) {
467
+ parts.push('### 关键文件注释');
468
+ for (const hc of fp.headerComments) {
469
+ parts.push(`- ${hc}`);
470
+ }
471
+ parts.push('');
472
+ }
473
+
474
+ parts.push('要求: 撰写该目录的深度分析文档。');
475
+ parts.push('包含: 目录职责推断(从文件名和注释推断)、文件组织分析、命名规范评估、');
476
+ parts.push('依赖关系分析(从 import 推断)、关键文件说明。');
477
+ parts.push('从文件命名模式推断出这个目录承担的功能角色和设计意图。');
478
+ break;
479
+ }
480
+ }
481
+
482
+ return parts.join('\n');
483
+ }
484
+
485
+ /**
486
+ * 构建非 AI 降级的丰富模板内容
487
+ * 即使没有 AI,也要产出有意义的内容 (不是只有列表罗列)
488
+ *
489
+ * @param {object} topic
490
+ * @param {object} data - { projectInfo, astInfo, moduleInfo, knowledgeInfo }
491
+ * @param {boolean} isZh
492
+ * @param {object|null} codeEntityGraph
493
+ * @returns {string}
494
+ */
495
+ export function buildFallbackArticle(topic, data, isZh, codeEntityGraph) {
496
+ const { projectInfo, astInfo, moduleInfo, knowledgeInfo } = data;
497
+
498
+ switch (topic.type) {
499
+ case 'overview':
500
+ return renderIndex(
501
+ projectInfo,
502
+ astInfo,
503
+ moduleInfo,
504
+ knowledgeInfo,
505
+ isZh,
506
+ topic._allTopics
507
+ );
508
+ case 'architecture':
509
+ return renderArchitecture(projectInfo, astInfo, moduleInfo, isZh, codeEntityGraph);
510
+ case 'getting-started':
511
+ return renderGettingStarted(projectInfo, moduleInfo, astInfo, isZh);
512
+ case 'module':
513
+ return renderModule(
514
+ topic._moduleData.target,
515
+ astInfo,
516
+ knowledgeInfo,
517
+ isZh,
518
+ projectInfo
519
+ );
520
+ case 'patterns':
521
+ return renderPatterns(knowledgeInfo, isZh);
522
+ case 'pattern-category':
523
+ return renderPatternCategory(topic._patternData, isZh);
524
+ case 'reference':
525
+ return renderProtocolReference(astInfo, isZh, projectInfo);
526
+ case 'folder-overview':
527
+ return renderFolderOverview(topic._folderProfiles, projectInfo, isZh);
528
+ case 'folder-profile':
529
+ return renderFolderProfile(topic._folderProfile, projectInfo, isZh);
530
+ default:
531
+ return '';
532
+ }
533
+ }
534
+
535
+ // ═══ Markdown 渲染器 ═══════════════════════════════════════
536
+
537
+ /**
538
+ * 渲染项目概述页 (index.md)
539
+ */
540
+ export function renderIndex(project, ast, modules, knowledge, isZh, allTopics) {
541
+ const title = isZh ? '项目概述' : 'Project Overview';
542
+ const langTerms = getLangTerms(project.primaryLanguage || 'unknown');
543
+ const tl = isZh ? langTerms.typeLabel.zh : langTerms.typeLabel.en;
544
+ const il = isZh ? langTerms.interfaceLabel.zh : langTerms.interfaceLabel.en;
545
+
546
+ const lines = [
547
+ `# ${project.name} — ${title}`,
548
+ '',
549
+ `> ${isZh ? '本文档由 AutoSnippet Repo Wiki 自动生成' : 'Auto-generated by AutoSnippet Repo Wiki'}`,
550
+ `> ${isZh ? '生成时间' : 'Generated at'}: ${new Date().toISOString()}`,
551
+ '',
552
+ ];
553
+
554
+ // ── 项目简介 ──
555
+ lines.push(`## ${isZh ? '简介' : 'Introduction'}`);
556
+ lines.push('');
557
+
558
+ // 从 buildSystems 或 legacy 字段推断项目类型标签
559
+ const types = [];
560
+ if (project.buildSystems?.length > 0) {
561
+ for (const bs of project.buildSystems) {
562
+ types.push(bs.buildTool);
563
+ }
564
+ } else {
565
+ if (project.hasPackageSwift) types.push('SPM');
566
+ if (project.hasPodfile) types.push('CocoaPods');
567
+ if (project.hasXcodeproj) types.push('Xcode Project');
568
+ }
569
+ const projectTypeLabel = types.join(' + ') || (project.primaryLanguage ? LanguageService.displayName(project.primaryLanguage) : 'Software');
570
+
571
+ const overview = ast.overview || {};
572
+ const mainTargets = modules.targets.filter((t) => t.type !== 'test');
573
+ const testTargets = modules.targets.filter((t) => t.type === 'test');
574
+
575
+ if (isZh) {
576
+ lines.push(
577
+ `**${project.name}** 是一个 ${projectTypeLabel} 项目,` +
578
+ `包含 ${project.sourceFiles.length} 个源文件` +
579
+ (overview.totalClasses ? `、${overview.totalClasses} 个${tl}` : '') +
580
+ (overview.totalProtocols ? `、${overview.totalProtocols} 个${il}` : '') +
581
+ `。`
582
+ );
583
+ if (mainTargets.length > 0) {
584
+ lines.push(
585
+ `项目由 ${mainTargets.length} 个功能模块组成` +
586
+ (testTargets.length > 0 ? `,配备 ${testTargets.length} 个测试模块` : '') +
587
+ `。`
588
+ );
589
+ }
590
+ } else {
591
+ lines.push(
592
+ `**${project.name}** is a ${projectTypeLabel} project ` +
593
+ `containing ${project.sourceFiles.length} source files` +
594
+ (overview.totalClasses ? `, ${overview.totalClasses} ${tl}` : '') +
595
+ (overview.totalProtocols ? `, ${overview.totalProtocols} ${il}` : '') +
596
+ `.`
597
+ );
598
+ if (mainTargets.length > 0) {
599
+ lines.push(
600
+ `The project consists of ${mainTargets.length} functional modules` +
601
+ (testTargets.length > 0 ? ` with ${testTargets.length} test modules` : '') +
602
+ `.`
603
+ );
604
+ }
605
+ }
606
+ lines.push('');
607
+
608
+ // ── 模块总览 ──
609
+ if (modules.targets.length > 0) {
610
+ lines.push(`## ${isZh ? '模块总览' : 'Module Overview'}`);
611
+ lines.push('');
612
+ lines.push(
613
+ `| ${isZh ? '模块' : 'Module'} | ${isZh ? '类型' : 'Type'} | ${isZh ? '源文件' : 'Files'} | ${tl} | ${il} |`
614
+ );
615
+ lines.push('|--------|------|--------|--------|----------|');
616
+ for (const t of modules.targets) {
617
+ const moduleFiles = getModuleSourceFiles(t, project);
618
+ const classCount = ast.classNamesByModule?.[t.name]?.length || 0;
619
+ const protoCount = ast.protocolNamesByModule?.[t.name]?.length || 0;
620
+ const hasDoc = allTopics?.some(
621
+ (tp) => tp.type === 'module' && tp._moduleData?.target.name === t.name
622
+ );
623
+ const nameCol = hasDoc ? `[${t.name}](modules/${slug(t.name)}.md)` : t.name;
624
+ lines.push(
625
+ `| ${nameCol} | ${t.type || 'target'} | ${moduleFiles.length || '-'} | ${classCount || '-'} | ${protoCount || '-'} |`
626
+ );
627
+ }
628
+ lines.push('');
629
+ } else if (project.sourceFilesByModule && Object.keys(project.sourceFilesByModule).length >= 2) {
630
+ // 无 moduleService targets → 使用 sourceFilesByModule 推断的模块
631
+ const sfm = project.sourceFilesByModule;
632
+ const sorted = Object.entries(sfm).sort((a, b) => b[1].length - a[1].length);
633
+ lines.push(`## ${isZh ? '模块总览' : 'Module Overview'}`);
634
+ lines.push('');
635
+ lines.push(isZh
636
+ ? `项目代码按目录结构可划分为 ${sorted.length} 个模块:`
637
+ : `The project code is organized into ${sorted.length} modules:`
638
+ );
639
+ lines.push('');
640
+ lines.push(
641
+ `| ${isZh ? '模块' : 'Module'} | ${isZh ? '源文件' : 'Files'} | ${isZh ? '说明' : 'Description'} |`
642
+ );
643
+ lines.push('|--------|--------|------|');
644
+ for (const [modName, modFiles] of sorted.slice(0, 15)) {
645
+ const hasDoc = allTopics?.some(
646
+ (tp) => tp.type === 'module' && tp.title === modName
647
+ );
648
+ const nameCol = hasDoc ? `[${modName}](modules/${slug(modName)}.md)` : modName;
649
+ const purpose = inferModulePurpose(modName, [], [], modFiles);
650
+ const desc = purpose ? (isZh ? purpose.zh : purpose.en) : '-';
651
+ lines.push(`| ${nameCol} | ${modFiles.length} | ${desc} |`);
652
+ }
653
+ lines.push('');
654
+ }
655
+
656
+ // ── 技术栈 ──
657
+ lines.push(`## ${isZh ? '技术栈' : 'Tech Stack'}`);
658
+ lines.push('');
659
+ if (project.languages && Object.keys(project.languages).length > 0) {
660
+ lines.push(
661
+ `| ${isZh ? '语言' : 'Language'} | ${isZh ? '文件数' : 'Files'} | ${isZh ? '占比' : 'Share'} |`
662
+ );
663
+ lines.push('|--------|-------|------|');
664
+ const total = Object.values(project.languages).reduce((a, b) => a + b, 0);
665
+ for (const [lang, count] of Object.entries(project.languages).sort((a, b) => b[1] - a[1])) {
666
+ const pct = total > 0 ? ((count / total) * 100).toFixed(1) : 0;
667
+ lines.push(`| ${lang} | ${count} | ${pct}% |`);
668
+ }
669
+ lines.push('');
670
+ }
671
+
672
+ // ── 核心数据 ──
673
+ lines.push(`## ${isZh ? '核心数据' : 'Key Metrics'}`);
674
+ lines.push('');
675
+ lines.push(`| ${isZh ? '指标' : 'Metric'} | ${isZh ? '数量' : 'Count'} |`);
676
+ lines.push('|--------|-------|');
677
+ lines.push(`| ${isZh ? '源文件数' : 'Source Files'} | ${project.sourceFiles.length} |`);
678
+ if (overview.totalClasses) {
679
+ lines.push(`| ${tl} | ${overview.totalClasses} |`);
680
+ }
681
+ if (overview.totalProtocols) {
682
+ lines.push(`| ${il} | ${overview.totalProtocols} |`);
683
+ }
684
+ if (overview.totalMethods) {
685
+ lines.push(`| ${isZh ? '方法总数' : 'Methods'} | ${overview.totalMethods} |`);
686
+ }
687
+ if (modules.targets.length > 0) {
688
+ lines.push(`| ${isZh ? '模块数' : 'Modules'} | ${modules.targets.length} |`);
689
+ }
690
+ if (knowledge.recipes.length > 0) {
691
+ lines.push(`| ${isZh ? '知识库条目' : 'KB Recipes'} | ${knowledge.recipes.length} |`);
692
+ }
693
+ lines.push('');
694
+
695
+ // ── 文档导航 (动态,基于实际生成的主题) ──
696
+ const navTopics = (allTopics || []).filter((t) => t.type !== 'overview');
697
+ if (navTopics.length > 0) {
698
+ lines.push('---');
699
+ lines.push('');
700
+ lines.push(`## ${isZh ? '📖 文档导航' : '📖 Documentation'}`);
701
+ lines.push('');
702
+ for (const t of navTopics) {
703
+ lines.push(`- [${t.title}](${t.path})`);
704
+ }
705
+ lines.push('');
706
+ }
707
+
708
+ return lines.join('\n');
709
+ }
710
+
711
+ /**
712
+ * 渲染架构总览文档 (architecture.md)
713
+ *
714
+ * @param {object} project
715
+ * @param {object} ast
716
+ * @param {object} modules
717
+ * @param {boolean} isZh
718
+ * @param {object|null} codeEntityGraph
719
+ */
720
+ export function renderArchitecture(project, ast, modules, isZh, codeEntityGraph) {
721
+ const lines = [
722
+ `# ${isZh ? '架构总览' : 'Architecture Overview'}`,
723
+ '',
724
+ `> ${isZh ? '本文档由 AutoSnippet Repo Wiki 自动生成' : 'Auto-generated by AutoSnippet Repo Wiki'}`,
725
+ '',
726
+ ];
727
+
728
+ // 依赖图 (Mermaid)
729
+ if (modules.targets.length > 0) {
730
+ lines.push(`## ${isZh ? '模块依赖图' : 'Module Dependency Graph'}`);
731
+ lines.push('');
732
+ lines.push('```mermaid');
733
+ lines.push('graph TD');
734
+
735
+ // 渲染 target 节点和依赖边
736
+ const rendered = new Set();
737
+ for (const target of modules.targets) {
738
+ const sid = mermaidId(target.name);
739
+ if (!rendered.has(sid)) {
740
+ const shape =
741
+ target.type === 'test'
742
+ ? `${sid}[["${target.name} (Test)"]]`
743
+ : `${sid}["${target.name}"]`;
744
+ lines.push(` ${shape}`);
745
+ rendered.add(sid);
746
+ }
747
+ }
748
+
749
+ // 如果有依赖图数据,渲染边
750
+ if (modules.depGraph) {
751
+ const edges = modules.depGraph.edges || [];
752
+ for (const edge of Array.isArray(edges) ? edges : []) {
753
+ if (edge.from && edge.to) {
754
+ const fromId = mermaidId(edge.from.split('::').pop() || edge.from);
755
+ const toId = mermaidId(edge.to.split('::').pop() || edge.to);
756
+ lines.push(` ${fromId} --> ${toId}`);
757
+ }
758
+ }
759
+ }
760
+
761
+ lines.push('```');
762
+ lines.push('');
763
+ } else if (project.sourceFilesByModule && Object.keys(project.sourceFilesByModule).length >= 2) {
764
+ // 无 moduleService → 从 sourceFilesByModule 推断模块结构图
765
+ const sfm = project.sourceFilesByModule;
766
+ // 只显示有实质内容的模块(>= 2 个文件),避免单文件噪声
767
+ const sorted = Object.entries(sfm)
768
+ .filter(([, files]) => files.length >= 2)
769
+ .sort((a, b) => b[1].length - a[1].length);
770
+ const topModules = sorted.slice(0, 15);
771
+
772
+ lines.push(`## ${isZh ? '模块结构图' : 'Module Structure'}`);
773
+ lines.push('');
774
+ lines.push('```mermaid');
775
+ lines.push('graph TD');
776
+ lines.push(` Root["${project.name}"]`);
777
+ for (const [modName] of topModules) {
778
+ const sid = mermaidId(modName);
779
+ lines.push(` ${sid}["${modName}"]`);
780
+ lines.push(` Root --> ${sid}`);
781
+ }
782
+ lines.push('```');
783
+ lines.push('');
784
+
785
+ // 模块详情表
786
+ lines.push(`## ${isZh ? '模块详情' : 'Module Details'}`);
787
+ lines.push('');
788
+ lines.push(
789
+ `| ${isZh ? '模块' : 'Module'} | ${isZh ? '源文件数' : 'Files'} | ${isZh ? '说明' : 'Description'} |`
790
+ );
791
+ lines.push('|--------|-------|------|');
792
+ for (const [modName, modFiles] of topModules) {
793
+ const purpose = inferModulePurpose(modName, [], [], modFiles);
794
+ const desc = purpose ? (isZh ? purpose.zh : purpose.en) : '-';
795
+ lines.push(`| ${modName} | ${modFiles.length} | ${desc} |`);
796
+ }
797
+ lines.push('');
798
+ }
799
+
800
+ // 分层架构
801
+ if (ast.overview) {
802
+ const modules = ast.overview.topLevelModules || [];
803
+ if (modules.length > 0) {
804
+ lines.push(`## ${isZh ? '顶层模块' : 'Top-Level Modules'}`);
805
+ lines.push('');
806
+ lines.push(`| ${isZh ? '模块' : 'Module'} | ${isZh ? '类数量' : 'Classes'} |`);
807
+ lines.push('|--------|---------|');
808
+ const cpm = ast.overview.classesPerModule || {};
809
+ for (const mod of modules) {
810
+ lines.push(`| ${mod} | ${cpm[mod] || 0} |`);
811
+ }
812
+ lines.push('');
813
+ }
814
+
815
+ // 入口点
816
+ if (ast.overview.entryPoints?.length > 0) {
817
+ lines.push(`## ${isZh ? '入口点' : 'Entry Points'}`);
818
+ lines.push('');
819
+ for (const ep of ast.overview.entryPoints) {
820
+ lines.push(`- \`${ep}\``);
821
+ }
822
+ lines.push('');
823
+ }
824
+ }
825
+
826
+ // 继承层次 (from CodeEntityGraph)
827
+ if (codeEntityGraph) {
828
+ try {
829
+ const topClasses = getInheritanceRoots(codeEntityGraph);
830
+ if (topClasses.length > 0) {
831
+ lines.push(`## ${isZh ? '核心继承层次' : 'Key Inheritance Hierarchy'}`);
832
+ lines.push('');
833
+ lines.push('```mermaid');
834
+ lines.push('classDiagram');
835
+ for (const root of topClasses.slice(0, 20)) {
836
+ lines.push(` class ${mermaidId(root.name)}`);
837
+ for (const child of root.children || []) {
838
+ lines.push(` ${mermaidId(root.name)} <|-- ${mermaidId(child)}`);
839
+ }
840
+ }
841
+ lines.push('```');
842
+ lines.push('');
843
+ }
844
+ } catch {
845
+ /* non-critical */
846
+ }
847
+ }
848
+
849
+ lines.push(`[← ${isZh ? '返回概述' : 'Back to Overview'}](index.md)`);
850
+ lines.push('');
851
+ return lines.join('\n');
852
+ }
853
+
854
+ /**
855
+ * 渲染模块详情文档 (modules/{name}.md)
856
+ */
857
+ export function renderModule(target, ast, knowledge, isZh, projectInfo) {
858
+ const langTerms = getLangTerms(projectInfo?.primaryLanguage || 'unknown');
859
+ const tl = isZh ? langTerms.typeLabel.zh : langTerms.typeLabel.en;
860
+ const il = isZh ? langTerms.interfaceLabel.zh : langTerms.interfaceLabel.en;
861
+ const lines = [
862
+ `# ${target.name}`,
863
+ '',
864
+ `> ${isZh ? '模块文档 — 由 AutoSnippet Repo Wiki 自动生成' : 'Module doc — Auto-generated by AutoSnippet Repo Wiki'}`,
865
+ '',
866
+ ];
867
+
868
+ // 收集模块数据
869
+ const moduleFiles = projectInfo ? getModuleSourceFiles(target, projectInfo) : [];
870
+ const moduleClasses = ast.classNamesByModule?.[target.name] || [];
871
+ const moduleProtocols = ast.protocolNamesByModule?.[target.name] || [];
872
+ const deps = target.dependencies || target.info?.dependencies || [];
873
+
874
+ // ── 模块概述 ──
875
+ lines.push(`## ${isZh ? '概述' : 'Overview'}`);
876
+ lines.push('');
877
+
878
+ // 推断模块功能 (基于名称和内容)
879
+ const purpose = inferModulePurpose(
880
+ target.name,
881
+ moduleClasses,
882
+ moduleProtocols,
883
+ moduleFiles
884
+ );
885
+ if (purpose) {
886
+ lines.push(
887
+ isZh
888
+ ? `**${target.name}** ${purpose.zh},包含 ${moduleFiles.length} 个源文件、${moduleClasses.length} 个${tl}${moduleProtocols.length > 0 ? `、${moduleProtocols.length} 个${il}` : ''}。`
889
+ : `**${target.name}** ${purpose.en}, containing ${moduleFiles.length} source files, ${moduleClasses.length} ${tl}${moduleProtocols.length > 0 ? `, ${moduleProtocols.length} ${il}` : ''}.`
890
+ );
891
+ } else {
892
+ lines.push(
893
+ isZh
894
+ ? `**${target.name}** 是项目中的一个 ${target.type || 'target'} 模块,包含 ${moduleFiles.length} 个源文件、${moduleClasses.length} 个${tl}。`
895
+ : `**${target.name}** is a ${target.type || 'target'} module in the project, containing ${moduleFiles.length} source files and ${moduleClasses.length} ${tl}.`
896
+ );
897
+ }
898
+ lines.push('');
899
+
900
+ // ── 模块信息表 ──
901
+ lines.push(`| ${isZh ? '属性' : 'Property'} | ${isZh ? '值' : 'Value'} |`);
902
+ lines.push('|--------|------|');
903
+ lines.push(`| ${isZh ? '类型' : 'Type'} | ${target.type || 'target'} |`);
904
+ if (target.packageName) {
905
+ lines.push(`| ${isZh ? '所属包' : 'Package'} | ${target.packageName} |`);
906
+ }
907
+ if (target.path || target.info?.path) {
908
+ lines.push(`| ${isZh ? '路径' : 'Path'} | \`${target.path || target.info.path}\` |`);
909
+ }
910
+ if (moduleFiles.length > 0) {
911
+ lines.push(`| ${isZh ? '源文件数' : 'Source Files'} | ${moduleFiles.length} |`);
912
+ }
913
+ if (moduleClasses.length > 0) {
914
+ lines.push(`| ${tl} | ${moduleClasses.length} |`);
915
+ }
916
+ if (moduleProtocols.length > 0) {
917
+ lines.push(`| ${il} | ${moduleProtocols.length} |`);
918
+ }
919
+ if (deps.length > 0) {
920
+ lines.push(`| ${isZh ? '依赖数' : 'Dependencies'} | ${deps.length} |`);
921
+ }
922
+ lines.push('');
923
+
924
+ // ── 依赖 ──
925
+ if (deps.length > 0) {
926
+ lines.push(`## ${isZh ? '依赖关系' : 'Dependencies'}`);
927
+ lines.push('');
928
+ lines.push(
929
+ isZh
930
+ ? `${target.name} 依赖以下 ${deps.length} 个模块:`
931
+ : `${target.name} depends on ${deps.length} module(s):`
932
+ );
933
+ lines.push('');
934
+ for (const dep of deps) {
935
+ const depName = typeof dep === 'string' ? dep : dep.name || String(dep);
936
+ lines.push(`- \`${depName}\``);
937
+ }
938
+ lines.push('');
939
+ }
940
+
941
+ // ── 核心类型分析 ──
942
+ if (moduleClasses.length > 0 || moduleProtocols.length > 0) {
943
+ lines.push(`## ${isZh ? '核心类型' : 'Core Types'}`);
944
+ lines.push('');
945
+
946
+ if (moduleProtocols.length > 0) {
947
+ lines.push(`### ${il} (${moduleProtocols.length})`);
948
+ lines.push('');
949
+ lines.push(
950
+ isZh
951
+ ? `${target.name} 定义了 ${moduleProtocols.length} 个${il},用于规范模块的接口边界:`
952
+ : `${target.name} defines ${moduleProtocols.length} ${il} establishing the module's interface contracts:`
953
+ );
954
+ lines.push('');
955
+ const sorted = [...moduleProtocols].sort();
956
+ for (const p of sorted.slice(0, 20)) {
957
+ lines.push(`- \`${p}\``);
958
+ }
959
+ if (sorted.length > 20) {
960
+ lines.push(
961
+ `- ... ${isZh ? `还有 ${sorted.length - 20} 个` : `and ${sorted.length - 20} more`}`
962
+ );
963
+ }
964
+ lines.push('');
965
+ }
966
+
967
+ if (moduleClasses.length > 0) {
968
+ lines.push(`### ${tl} (${moduleClasses.length})`);
969
+ lines.push('');
970
+ const sorted = [...moduleClasses].sort();
971
+ for (const c of sorted.slice(0, 30)) {
972
+ lines.push(`- \`${c}\``);
973
+ }
974
+ if (sorted.length > 30) {
975
+ lines.push(
976
+ `- ... ${isZh ? `还有 ${sorted.length - 30} 个` : `and ${sorted.length - 30} more`}`
977
+ );
978
+ }
979
+ lines.push('');
980
+ }
981
+ }
982
+
983
+ // ── 源文件分布 ──
984
+ if (moduleFiles.length > 0) {
985
+ lines.push(`## ${isZh ? '源文件分布' : 'Source File Distribution'}`);
986
+ lines.push('');
987
+
988
+ // 按语言统计
989
+ const langCount = {};
990
+ for (const f of moduleFiles) {
991
+ const ext = path.extname(f);
992
+ const lang = LanguageService.displayNameFromExt(ext);
993
+ langCount[lang] = (langCount[lang] || 0) + 1;
994
+ }
995
+
996
+ lines.push(`| ${isZh ? '语言' : 'Language'} | ${isZh ? '文件数' : 'Files'} |`);
997
+ lines.push('|--------|-------|');
998
+ for (const [lang, count] of Object.entries(langCount).sort((a, b) => b - a)) {
999
+ lines.push(`| ${lang} | ${count} |`);
1000
+ }
1001
+ lines.push('');
1002
+ }
1003
+
1004
+ // ── 该模块相关的 Recipes ──
1005
+ if (knowledge.recipes.length > 0) {
1006
+ const related = knowledge.recipes.filter((r) => {
1007
+ const json = r.toJSON ? r.toJSON() : r;
1008
+ return (
1009
+ json.moduleName === target.name ||
1010
+ json.tags?.includes(target.name) ||
1011
+ json.title?.includes(target.name)
1012
+ );
1013
+ });
1014
+ if (related.length > 0) {
1015
+ lines.push(`## ${isZh ? '相关知识条目' : 'Related Recipes'}`);
1016
+ lines.push('');
1017
+ lines.push(
1018
+ isZh
1019
+ ? `团队知识库中有 ${related.length} 条与 ${target.name} 相关的条目:`
1020
+ : `The team knowledge base contains ${related.length} entries related to ${target.name}:`
1021
+ );
1022
+ lines.push('');
1023
+ for (const r of related) {
1024
+ const json = r.toJSON ? r.toJSON() : r;
1025
+ lines.push(`### ${json.title}`);
1026
+ lines.push('');
1027
+ if (json.description) {
1028
+ lines.push(json.description);
1029
+ }
1030
+ if (json.doClause) {
1031
+ lines.push(`\n**${isZh ? '✅ 应当' : '✅ Do'}**: ${json.doClause}`);
1032
+ }
1033
+ if (json.dontClause) {
1034
+ lines.push(`**${isZh ? '❌ 避免' : "❌ Don't"}**: ${json.dontClause}`);
1035
+ }
1036
+ lines.push('');
1037
+ }
1038
+ }
1039
+ }
1040
+
1041
+ lines.push(`[← ${isZh ? '返回概述' : 'Back to Overview'}](../index.md)`);
1042
+ lines.push('');
1043
+ return lines.join('\n');
1044
+ }
1045
+
1046
+ /**
1047
+ * 渲染代码模式文档 (patterns.md)
1048
+ */
1049
+ export function renderPatterns(knowledge, isZh) {
1050
+ const lines = [
1051
+ `# ${isZh ? '代码模式与最佳实践' : 'Code Patterns & Best Practices'}`,
1052
+ '',
1053
+ `> ${isZh ? '团队沉淀的代码模式与最佳实践(来自 AutoSnippet 知识库)' : 'Code patterns and best practices from AutoSnippet knowledge base'}`,
1054
+ '',
1055
+ ];
1056
+
1057
+ // 按 category 分组
1058
+ const groups = {};
1059
+ for (const r of knowledge.recipes) {
1060
+ const json = r.toJSON ? r.toJSON() : r;
1061
+ const cat = json.category || 'Other';
1062
+ if (!groups[cat]) {
1063
+ groups[cat] = [];
1064
+ }
1065
+ groups[cat].push(json);
1066
+ }
1067
+
1068
+ // 总结
1069
+ const totalRecipes = knowledge.recipes.length;
1070
+ const catCount = Object.keys(groups).length;
1071
+ lines.push(
1072
+ isZh
1073
+ ? `本项目团队在 ${catCount} 个分类下共沉淀了 **${totalRecipes}** 条代码模式和最佳实践。以下按分类进行展示和分析。`
1074
+ : `The team has accumulated **${totalRecipes}** code patterns across ${catCount} categories. Below they are organized and analyzed by category.`
1075
+ );
1076
+ lines.push('');
1077
+
1078
+ for (const [cat, items] of Object.entries(groups).sort()) {
1079
+ lines.push(`## ${cat} (${items.length})`);
1080
+ lines.push('');
1081
+
1082
+ // 分类概述
1083
+ lines.push(
1084
+ isZh
1085
+ ? `${cat} 分类包含 ${items.length} 条规则,覆盖了该领域的核心规范。`
1086
+ : `The ${cat} category contains ${items.length} rules covering core conventions in this area.`
1087
+ );
1088
+ lines.push('');
1089
+
1090
+ for (const item of items) {
1091
+ lines.push(`### ${item.title}`);
1092
+ lines.push('');
1093
+ if (item.description) {
1094
+ lines.push(item.description);
1095
+ lines.push('');
1096
+ }
1097
+ if (item.content?.pattern) {
1098
+ lines.push(`\`\`\`${item.language || 'text'}`);
1099
+ lines.push(item.content.pattern);
1100
+ lines.push('```');
1101
+ lines.push('');
1102
+ }
1103
+ if (item.doClause) {
1104
+ lines.push(`**${isZh ? '✅ 应当' : '✅ Do'}**: ${item.doClause}`);
1105
+ lines.push('');
1106
+ }
1107
+ if (item.dontClause) {
1108
+ lines.push(`**${isZh ? '❌ 避免' : "❌ Don't"}**: ${item.dontClause}`);
1109
+ lines.push('');
1110
+ }
1111
+ if (item.reasoning?.whyStandard) {
1112
+ lines.push(`> ${isZh ? '💡 原因' : '💡 Rationale'}: ${item.reasoning.whyStandard}`);
1113
+ lines.push('');
1114
+ }
1115
+ }
1116
+ }
1117
+
1118
+ lines.push(`[← ${isZh ? '返回概述' : 'Back to Overview'}](index.md)`);
1119
+ lines.push('');
1120
+ return lines.join('\n');
1121
+ }
1122
+
1123
+ // ═══ V3 新增渲染器 ════════════════════════════════════════
1124
+
1125
+ /**
1126
+ * 快速上手指南 (非 AI 降级模板)
1127
+ */
1128
+ export function renderGettingStarted(project, modules, ast, isZh) {
1129
+ const lines = [
1130
+ `# ${isZh ? '快速上手' : 'Getting Started'}`,
1131
+ '',
1132
+ `> ${isZh ? '本文档由 AutoSnippet Repo Wiki 自动生成' : 'Auto-generated by AutoSnippet Repo Wiki'}`,
1133
+ '',
1134
+ ];
1135
+
1136
+ // 从 buildSystems 或 legacy 字段推断
1137
+ const bs = project.buildSystems || [];
1138
+ const ecoSet = new Set(bs.map((b) => b.eco));
1139
+
1140
+ // ── 环境要求 (按检测到的生态系统动态生成) ──
1141
+ lines.push(`## ${isZh ? '环境要求' : 'Prerequisites'}`);
1142
+ lines.push('');
1143
+ if (ecoSet.has('spm') || project.hasPackageSwift) {
1144
+ lines.push(isZh ? '- Swift 5.5+ (推荐 Swift 5.9+)' : '- Swift 5.5+ (Swift 5.9+ recommended)');
1145
+ lines.push(isZh ? '- Xcode 14+' : '- Xcode 14+');
1146
+ const hasCocoaPods = bs.some((b) => b.buildTool === 'CocoaPods');
1147
+ if (hasCocoaPods || project.hasPodfile) {
1148
+ lines.push(isZh ? '- CocoaPods 1.10+' : '- CocoaPods 1.10+');
1149
+ }
1150
+ }
1151
+ if (project.hasXcodeproj) {
1152
+ lines.push(isZh ? '- Xcode (最新稳定版)' : '- Xcode (latest stable version)');
1153
+ // Xcode 项目额外环境提示
1154
+ if (!ecoSet.has('spm') && !project.hasPackageSwift && !project.hasPodfile) {
1155
+ lines.push(isZh ? '- macOS (建议最新版本)' : '- macOS (latest version recommended)');
1156
+ lines.push(isZh ? '- Apple Developer Account (如需真机调试)' : '- Apple Developer Account (for device testing)');
1157
+ }
1158
+ }
1159
+ if (ecoSet.has('node')) {
1160
+ lines.push(isZh ? '- Node.js 18+ (推荐 20 LTS)' : '- Node.js 18+ (20 LTS recommended)');
1161
+ const hasYarn = bs.some((b) => b.buildTool === 'Yarn');
1162
+ const hasPnpm = bs.some((b) => b.buildTool === 'pnpm');
1163
+ lines.push(hasYarn ? '- Yarn' : hasPnpm ? '- pnpm' : '- npm');
1164
+ }
1165
+ if (ecoSet.has('python')) {
1166
+ lines.push(isZh ? '- Python 3.8+' : '- Python 3.8+');
1167
+ const hasPipenv = bs.some((b) => b.buildTool === 'Pipenv');
1168
+ const hasPoetry = bs.some((b) => b.buildTool === 'Poetry');
1169
+ if (hasPipenv) lines.push('- Pipenv');
1170
+ else if (hasPoetry) lines.push('- Poetry');
1171
+ }
1172
+ if (ecoSet.has('go')) {
1173
+ lines.push(isZh ? '- Go 1.21+' : '- Go 1.21+');
1174
+ }
1175
+ if (ecoSet.has('rust')) {
1176
+ lines.push(isZh ? '- Rust (最新 stable)' : '- Rust (latest stable)');
1177
+ lines.push('- Cargo');
1178
+ }
1179
+ if (ecoSet.has('jvm')) {
1180
+ const hasGradle = bs.some((b) => b.buildTool?.startsWith('Gradle'));
1181
+ lines.push(isZh ? '- JDK 17+' : '- JDK 17+');
1182
+ lines.push(hasGradle ? '- Gradle' : '- Maven');
1183
+ }
1184
+ if (ecoSet.has('dart')) {
1185
+ lines.push(isZh ? '- Flutter / Dart SDK' : '- Flutter / Dart SDK');
1186
+ }
1187
+ if (ecoSet.has('dotnet')) {
1188
+ lines.push(isZh ? '- .NET 6+ SDK' : '- .NET 6+ SDK');
1189
+ }
1190
+ if (ecoSet.has('ruby')) {
1191
+ lines.push(isZh ? '- Ruby 3.0+' : '- Ruby 3.0+');
1192
+ lines.push('- Bundler');
1193
+ }
1194
+ lines.push('');
1195
+
1196
+ // ── 项目目录结构 ──
1197
+ lines.push(`## ${isZh ? '项目结构' : 'Project Structure'}`);
1198
+ lines.push('');
1199
+ lines.push('```');
1200
+ lines.push(`${project.name}/`);
1201
+ if (modules.targets.length > 0) {
1202
+ const mainTargets = modules.targets.filter((t) => t.type !== 'test');
1203
+ const testTargets = modules.targets.filter((t) => t.type === 'test');
1204
+ if (mainTargets.length > 0) {
1205
+ const srcDir = ecoSet.has('spm') || project.hasPackageSwift ? 'Sources' : 'src';
1206
+ lines.push(`├── ${srcDir}/`);
1207
+ for (let i = 0; i < mainTargets.length; i++) {
1208
+ const prefix =
1209
+ i === mainTargets.length - 1 && testTargets.length === 0 ? '│ └──' : '│ ├──';
1210
+ lines.push(`${prefix} ${mainTargets[i].name}/`);
1211
+ }
1212
+ }
1213
+ if (testTargets.length > 0) {
1214
+ const testDir = ecoSet.has('spm') || project.hasPackageSwift ? 'Tests' : 'test';
1215
+ lines.push(`├── ${testDir}/`);
1216
+ for (let i = 0; i < testTargets.length; i++) {
1217
+ const prefix = i === testTargets.length - 1 ? '│ └──' : '│ ├──';
1218
+ lines.push(`${prefix} ${testTargets[i].name}/`);
1219
+ }
1220
+ }
1221
+ }
1222
+ // 显示构建配置文件
1223
+ for (const b of bs) {
1224
+ const marker = BUILD_SYSTEM_FILES[b.buildTool];
1225
+ if (marker) lines.push(`├── ${marker}`);
1226
+ }
1227
+ // legacy 兜底
1228
+ if (bs.length === 0) {
1229
+ if (project.hasPackageSwift) lines.push('├── Package.swift');
1230
+ if (project.hasPodfile) lines.push('├── Podfile');
1231
+ }
1232
+ lines.push('```');
1233
+ lines.push('');
1234
+
1235
+ // ── 构建步骤 (按检测到的生态系统动态生成) ──
1236
+ lines.push(`## ${isZh ? '构建与运行' : 'Build & Run'}`);
1237
+ lines.push('');
1238
+
1239
+ for (const b of bs) {
1240
+ _pushBuildSteps(lines, b, project.name, isZh);
1241
+ }
1242
+
1243
+ // legacy 兜底 — 如果没有检测到 buildSystems
1244
+ if (bs.length === 0) {
1245
+ if (project.hasPackageSwift) {
1246
+ _pushBuildSteps(lines, { eco: 'spm', buildTool: 'SPM' }, project.name, isZh);
1247
+ }
1248
+ if (project.hasPodfile) {
1249
+ _pushBuildSteps(lines, { eco: 'spm', buildTool: 'CocoaPods' }, project.name, isZh);
1250
+ }
1251
+ // Xcode 项目兜底 (无 SPM / CocoaPods)
1252
+ if (!project.hasPackageSwift && !project.hasPodfile && project.hasXcodeproj) {
1253
+ _pushBuildSteps(lines, { eco: 'xcode', buildTool: 'Xcode' }, project.name, isZh);
1254
+ }
1255
+ }
1256
+
1257
+ // ── 源文件统计 (增强无 moduleService 场景) ──
1258
+ if (modules.targets.length === 0 && project.sourceFilesByModule) {
1259
+ const sfm = project.sourceFilesByModule;
1260
+ const modEntries = Object.entries(sfm).sort((a, b) => b[1].length - a[1].length);
1261
+ if (modEntries.length > 0) {
1262
+ lines.push(`## ${isZh ? '项目模块概览' : 'Module Overview'}`);
1263
+ lines.push('');
1264
+ lines.push(
1265
+ `| ${isZh ? '模块' : 'Module'} | ${isZh ? '源文件数' : 'Files'} | ${isZh ? '说明' : 'Description'} |`
1266
+ );
1267
+ lines.push('|--------|-------|------|');
1268
+ for (const [modName, modFiles] of modEntries.slice(0, 15)) {
1269
+ const purpose = inferModulePurpose(modName, [], [], modFiles);
1270
+ const desc = purpose ? (isZh ? purpose.zh : purpose.en) : '-';
1271
+ lines.push(`| ${modName} | ${modFiles.length} | ${desc} |`);
1272
+ }
1273
+ lines.push('');
1274
+ }
1275
+ }
1276
+
1277
+ // ── 模块说明 ──
1278
+ if (modules.targets.length > 0) {
1279
+ const mainTargets = modules.targets.filter((t) => t.type !== 'test');
1280
+ if (mainTargets.length > 0) {
1281
+ lines.push(`## ${isZh ? '核心模块' : 'Core Modules'}`);
1282
+ lines.push('');
1283
+ lines.push(
1284
+ `| ${isZh ? '模块' : 'Module'} | ${isZh ? '类型' : 'Type'} | ${isZh ? '类型数' : 'Types'} | ${isZh ? '说明' : 'Description'} |`
1285
+ );
1286
+ lines.push('|--------|------|--------|------|');
1287
+ for (const t of mainTargets) {
1288
+ const cls = (ast.classNamesByModule?.[t.name] || []).length;
1289
+ const purpose = inferModulePurpose(
1290
+ t.name,
1291
+ ast.classNamesByModule?.[t.name] || [],
1292
+ ast.protocolNamesByModule?.[t.name] || [],
1293
+ []
1294
+ );
1295
+ const desc = purpose ? (isZh ? purpose.zh : purpose.en) : '-';
1296
+ lines.push(`| ${t.name} | ${t.type || 'library'} | ${cls} | ${desc} |`);
1297
+ }
1298
+ lines.push('');
1299
+ }
1300
+ }
1301
+
1302
+ lines.push(`[← ${isZh ? '返回概述' : 'Back to Overview'}](index.md)`);
1303
+ lines.push('');
1304
+ return lines.join('\n');
1305
+ }
1306
+
1307
+ /* 构建配置文件名映射 (用于目录树显示) — 从 LanguageService.buildSystemMarkers 动态派生 */
1308
+ const BUILD_SYSTEM_FILES = Object.fromEntries(
1309
+ LanguageService.buildSystemMarkers.map((m) => [m.buildTool, m.file])
1310
+ );
1311
+
1312
+ /**
1313
+ * 按生态系统输出构建步骤
1314
+ * @private
1315
+ */
1316
+ function _pushBuildSteps(lines, buildSys, projectName, isZh) {
1317
+ const { eco, buildTool } = buildSys;
1318
+
1319
+ lines.push(`### ${isZh ? `使用 ${buildTool}` : `Using ${buildTool}`}`);
1320
+ lines.push('');
1321
+ lines.push('```bash');
1322
+ lines.push(isZh ? '# 获取项目' : '# Clone the project');
1323
+ lines.push(`git clone <repository-url>`);
1324
+ lines.push(`cd ${projectName}`);
1325
+ lines.push('');
1326
+
1327
+ switch (eco) {
1328
+ case 'spm':
1329
+ if (buildTool === 'CocoaPods') {
1330
+ lines.push('pod install');
1331
+ lines.push('open *.xcworkspace');
1332
+ } else {
1333
+ lines.push(isZh ? '# 解析依赖' : '# Resolve dependencies');
1334
+ lines.push('swift package resolve');
1335
+ lines.push('');
1336
+ lines.push(isZh ? '# 构建' : '# Build');
1337
+ lines.push('swift build');
1338
+ lines.push('');
1339
+ lines.push(isZh ? '# 运行测试' : '# Run tests');
1340
+ lines.push('swift test');
1341
+ }
1342
+ break;
1343
+ case 'node':
1344
+ if (buildTool === 'Yarn') {
1345
+ lines.push('yarn install');
1346
+ lines.push('yarn build');
1347
+ lines.push('yarn test');
1348
+ } else if (buildTool === 'pnpm') {
1349
+ lines.push('pnpm install');
1350
+ lines.push('pnpm build');
1351
+ lines.push('pnpm test');
1352
+ } else {
1353
+ lines.push('npm install');
1354
+ lines.push('npm run build');
1355
+ lines.push('npm test');
1356
+ }
1357
+ break;
1358
+ case 'python':
1359
+ if (buildTool === 'Poetry') {
1360
+ lines.push('poetry install');
1361
+ lines.push('poetry run pytest');
1362
+ } else if (buildTool === 'Pipenv') {
1363
+ lines.push('pipenv install');
1364
+ lines.push('pipenv run pytest');
1365
+ } else {
1366
+ lines.push('pip install -r requirements.txt');
1367
+ lines.push('pytest');
1368
+ }
1369
+ break;
1370
+ case 'go':
1371
+ lines.push('go mod download');
1372
+ lines.push('go build ./...');
1373
+ lines.push('go test ./...');
1374
+ break;
1375
+ case 'rust':
1376
+ lines.push('cargo build');
1377
+ lines.push('cargo test');
1378
+ break;
1379
+ case 'jvm':
1380
+ if (buildTool?.startsWith('Gradle')) {
1381
+ lines.push('./gradlew build');
1382
+ lines.push('./gradlew test');
1383
+ } else {
1384
+ lines.push('mvn install');
1385
+ lines.push('mvn test');
1386
+ }
1387
+ break;
1388
+ case 'dart':
1389
+ lines.push('flutter pub get');
1390
+ lines.push('flutter run');
1391
+ lines.push('flutter test');
1392
+ break;
1393
+ case 'dotnet':
1394
+ lines.push('dotnet restore');
1395
+ lines.push('dotnet build');
1396
+ lines.push('dotnet test');
1397
+ break;
1398
+ case 'ruby':
1399
+ lines.push('bundle install');
1400
+ break;
1401
+ case 'xcode':
1402
+ lines.push(isZh ? '# 使用 Xcode 打开项目' : '# Open with Xcode');
1403
+ lines.push(`open *.xcodeproj 2>/dev/null || open *.xcworkspace`);
1404
+ lines.push('');
1405
+ lines.push(isZh ? '# 或通过命令行构建' : '# Or build via command line');
1406
+ lines.push(`xcodebuild -project *.xcodeproj -scheme ${projectName} build`);
1407
+ break;
1408
+ default:
1409
+ lines.push(isZh ? '# 请查阅项目 README' : '# Please refer to the project README');
1410
+ break;
1411
+ }
1412
+
1413
+ lines.push('```');
1414
+ lines.push('');
1415
+ }
1416
+
1417
+ /**
1418
+ * 按分类拆分的代码模式文档
1419
+ */
1420
+ export function renderPatternCategory(patternData, isZh) {
1421
+ const { category, recipes } = patternData;
1422
+ const lines = [
1423
+ `# ${category}`,
1424
+ '',
1425
+ `> ${isZh ? `${category} 分类下的 ${recipes.length} 条代码模式(来自 AutoSnippet 知识库)` : `${recipes.length} code patterns in ${category} category (from AutoSnippet KB)`}`,
1426
+ '',
1427
+ ];
1428
+
1429
+ // 分类概述
1430
+ lines.push(
1431
+ isZh
1432
+ ? `本文档收录了 ${category} 分类下的 ${recipes.length} 条代码模式和规范,这些规则由团队在开发实践中总结沉淀。`
1433
+ : `This document covers ${recipes.length} code patterns and conventions in the ${category} category, distilled from team development practices.`
1434
+ );
1435
+ lines.push('');
1436
+
1437
+ for (const item of recipes) {
1438
+ lines.push(`## ${item.title}`);
1439
+ lines.push('');
1440
+ if (item.description) {
1441
+ lines.push(item.description);
1442
+ lines.push('');
1443
+ }
1444
+ if (item.doClause) {
1445
+ lines.push(`**${isZh ? '✅ 应当' : '✅ Do'}**: ${item.doClause}`);
1446
+ lines.push('');
1447
+ }
1448
+ if (item.dontClause) {
1449
+ lines.push(`**${isZh ? '❌ 避免' : "❌ Don't"}**: ${item.dontClause}`);
1450
+ lines.push('');
1451
+ }
1452
+ if (item.content?.pattern) {
1453
+ lines.push(`\`\`\`${item.language || 'text'}`);
1454
+ lines.push(item.content.pattern);
1455
+ lines.push('```');
1456
+ lines.push('');
1457
+ }
1458
+ if (item.reasoning?.whyStandard) {
1459
+ lines.push(`> ${isZh ? '💡 原因' : '💡 Rationale'}: ${item.reasoning.whyStandard}`);
1460
+ lines.push('');
1461
+ }
1462
+ }
1463
+
1464
+ lines.push(`[← ${isZh ? '返回概述' : 'Back to Overview'}](../index.md)`);
1465
+ lines.push('');
1466
+ return lines.join('\n');
1467
+ }
1468
+
1469
+ /**
1470
+ * 协议参考文档
1471
+ */
1472
+ export function renderProtocolReference(ast, isZh, projectInfo) {
1473
+ const langTerms = getLangTerms(projectInfo?.primaryLanguage || 'unknown');
1474
+ const il = isZh ? langTerms.interfaceLabel.zh : langTerms.interfaceLabel.en;
1475
+
1476
+ const lines = [
1477
+ `# ${isZh ? `${il}参考` : `${il} Reference`}`,
1478
+ '',
1479
+ `> ${isZh ? `项目中定义的 ${ast.protocols.length} 个${il}` : `${ast.protocols.length} ${il} defined in the project`}`,
1480
+ '',
1481
+ ];
1482
+
1483
+ lines.push(
1484
+ isZh
1485
+ ? `${il}定义了类型需要遵循的接口契约。本项目共定义了 ${ast.protocols.length} 个${il},以下按模块分组展示。`
1486
+ : `${il} define interface contracts that types must conform to. This project defines ${ast.protocols.length} ${il}, organized by module below.`
1487
+ );
1488
+ lines.push('');
1489
+
1490
+ // 按模块分组
1491
+ const protoByModule = ast.protocolNamesByModule || {};
1492
+ const grouped = new Set();
1493
+
1494
+ for (const [mod, protos] of Object.entries(protoByModule).sort()) {
1495
+ if (protos.length === 0) {
1496
+ continue;
1497
+ }
1498
+ lines.push(`## ${mod}`);
1499
+ lines.push('');
1500
+ lines.push(
1501
+ isZh
1502
+ ? `${mod} 模块定义了 ${protos.length} 个${il}:`
1503
+ : `${mod} module defines ${protos.length} ${il}:`
1504
+ );
1505
+ lines.push('');
1506
+ for (const p of protos.sort()) {
1507
+ lines.push(`- \`${p}\``);
1508
+ grouped.add(p);
1509
+ }
1510
+ lines.push('');
1511
+ }
1512
+
1513
+ // 未分组的接口类型
1514
+ const ungrouped = ast.protocols.filter((p) => !grouped.has(p));
1515
+ if (ungrouped.length > 0) {
1516
+ lines.push(`## ${isZh ? `其他${il}` : `Other ${il}`}`);
1517
+ lines.push('');
1518
+ for (const p of ungrouped.sort()) {
1519
+ lines.push(`- \`${p}\``);
1520
+ }
1521
+ lines.push('');
1522
+ }
1523
+
1524
+ lines.push(`[← ${isZh ? '返回概述' : 'Back to Overview'}](index.md)`);
1525
+ lines.push('');
1526
+ return lines.join('\n');
1527
+ }
1528
+
1529
+ // ═══ Folder Profile 渲染器 (AST 不可用时的降级策略) ═══════
1530
+
1531
+ /**
1532
+ * 渲染文件夹结构总览 (folder-structure.md)
1533
+ *
1534
+ * @param {import('./WikiUtils.js').FolderProfile[]} profiles
1535
+ * @param {object} projectInfo
1536
+ * @param {boolean} isZh
1537
+ * @returns {string}
1538
+ */
1539
+ export function renderFolderOverview(profiles, projectInfo, isZh) {
1540
+ const lines = [
1541
+ `# ${isZh ? '项目结构分析' : 'Project Structure Analysis'}`,
1542
+ '',
1543
+ `> ${isZh ? '本文档由 AutoSnippet Repo Wiki 自动生成(基于文件夹画像分析)' : 'Auto-generated by AutoSnippet Repo Wiki (folder profiling mode)'}`,
1544
+ '',
1545
+ ];
1546
+
1547
+ // 说明为什么是文件夹分析模式
1548
+ lines.push(
1549
+ isZh
1550
+ ? `> 💡 本项目的主要语言 (${LanguageService.displayName(projectInfo.primaryLanguage)}) 暂不支持深度 AST 解析,因此使用文件夹画像分析来代替。`
1551
+ : `> 💡 The project's primary language (${LanguageService.displayName(projectInfo.primaryLanguage)}) does not support deep AST analysis yet, so folder profiling is used instead.`
1552
+ );
1553
+ lines.push('');
1554
+
1555
+ // ── 结构鸟瞰 (Mermaid) ──
1556
+ lines.push(`## ${isZh ? '结构鸟瞰' : 'Structure Overview'}`);
1557
+ lines.push('');
1558
+ lines.push('```mermaid');
1559
+ lines.push('graph TD');
1560
+ lines.push(` Root["${projectInfo.name}"]`);
1561
+
1562
+ // 只显示深度 = 1 的顶层文件夹
1563
+ const topLevel = profiles.filter(fp => fp.depth === 1);
1564
+ const deeper = profiles.filter(fp => fp.depth > 1);
1565
+
1566
+ for (const fp of topLevel) {
1567
+ const sid = mermaidId(fp.name);
1568
+ lines.push(` ${sid}["${fp.name} (${fp.fileCount})"]`);
1569
+ lines.push(` Root --> ${sid}`);
1570
+ }
1571
+
1572
+ // 画 import 关系边
1573
+ const folderNames = new Set(profiles.map(fp => fp.name));
1574
+ for (const fp of profiles) {
1575
+ const fromId = mermaidId(fp.name);
1576
+ for (const imp of fp.imports) {
1577
+ if (folderNames.has(imp) && imp !== fp.name) {
1578
+ lines.push(` ${fromId} -.-> ${mermaidId(imp)}`);
1579
+ }
1580
+ }
1581
+ }
1582
+
1583
+ lines.push('```');
1584
+ lines.push('');
1585
+
1586
+ // ── 文件夹总览表 ──
1587
+ lines.push(`## ${isZh ? '文件夹总览' : 'Folder Overview'}`);
1588
+ lines.push('');
1589
+ lines.push(
1590
+ `| ${isZh ? '文件夹' : 'Folder'} | ${isZh ? '路径' : 'Path'} | ${isZh ? '文件数' : 'Files'} | ${isZh ? '大小' : 'Size'} | ${isZh ? '语言' : 'Languages'} | ${isZh ? '说明' : 'Description'} |`
1591
+ );
1592
+ lines.push('|--------|------|-------|------|------|------|');
1593
+
1594
+ for (const fp of profiles) {
1595
+ const hasDoc = fp.fileCount >= 5;
1596
+ const folderDocSlug = slug(fp.relPath.replaceAll('/', '-'));
1597
+ const nameCol = hasDoc ? `[${fp.relPath}](folders/${folderDocSlug}.md)` : fp.relPath;
1598
+ const sizeStr = fp.totalSize > 1024 * 1024
1599
+ ? `${(fp.totalSize / 1024 / 1024).toFixed(1)}MB`
1600
+ : `${(fp.totalSize / 1024).toFixed(1)}KB`;
1601
+ const langs = Object.entries(fp.langBreakdown)
1602
+ .sort((a, b) => b[1] - a[1])
1603
+ .slice(0, 3)
1604
+ .map(([l]) => l)
1605
+ .join(', ');
1606
+ const desc = fp.purpose
1607
+ ? (isZh ? fp.purpose.zh : fp.purpose.en)
1608
+ : '-';
1609
+ lines.push(`| ${nameCol} | \`${fp.relPath}\` | ${fp.fileCount} | ${sizeStr} | ${langs} | ${desc} |`);
1610
+ }
1611
+ lines.push('');
1612
+
1613
+ // ── 命名约定总结 ──
1614
+ const allPatterns = {};
1615
+ for (const fp of profiles) {
1616
+ for (const p of fp.namingPatterns) {
1617
+ allPatterns[p] = (allPatterns[p] || 0) + 1;
1618
+ }
1619
+ }
1620
+ const commonPatterns = Object.entries(allPatterns)
1621
+ .filter(([, c]) => c >= 2)
1622
+ .sort((a, b) => b[1] - a[1]);
1623
+
1624
+ if (commonPatterns.length > 0) {
1625
+ lines.push(`## ${isZh ? '命名约定' : 'Naming Conventions'}`);
1626
+ lines.push('');
1627
+ lines.push(
1628
+ isZh
1629
+ ? '通过分析文件命名模式,检测到以下在多个文件夹中出现的约定:'
1630
+ : 'The following naming conventions were detected across multiple folders:'
1631
+ );
1632
+ lines.push('');
1633
+ for (const [pattern, count] of commonPatterns) {
1634
+ lines.push(`- **${pattern}** — ${isZh ? `出现在 ${count} 个文件夹` : `found in ${count} folders`}`);
1635
+ }
1636
+ lines.push('');
1637
+ }
1638
+
1639
+ // ── 依赖关系 ──
1640
+ const allImports = [];
1641
+ for (const fp of profiles) {
1642
+ for (const imp of fp.imports) {
1643
+ if (folderNames.has(imp) && imp !== fp.name) {
1644
+ allImports.push({ from: fp.name, to: imp });
1645
+ }
1646
+ }
1647
+ }
1648
+
1649
+ if (allImports.length > 0) {
1650
+ lines.push(`## ${isZh ? '文件夹间依赖' : 'Inter-Folder Dependencies'}`);
1651
+ lines.push('');
1652
+ lines.push(
1653
+ isZh
1654
+ ? '通过分析 import/require 语句推断的文件夹间引用关系:'
1655
+ : 'Dependencies between folders inferred from import/require statements:'
1656
+ );
1657
+ lines.push('');
1658
+ for (const dep of allImports) {
1659
+ lines.push(`- \`${dep.from}\` → \`${dep.to}\``);
1660
+ }
1661
+ lines.push('');
1662
+ }
1663
+
1664
+ lines.push(`[← ${isZh ? '返回概述' : 'Back to Overview'}](index.md)`);
1665
+ lines.push('');
1666
+ return lines.join('\n');
1667
+ }
1668
+
1669
+ /**
1670
+ * 渲染单个文件夹的深度画像文档 (folders/{name}.md)
1671
+ *
1672
+ * @param {import('./WikiUtils.js').FolderProfile} fp
1673
+ * @param {object} projectInfo
1674
+ * @param {boolean} isZh
1675
+ * @returns {string}
1676
+ */
1677
+ export function renderFolderProfile(fp, projectInfo, isZh) {
1678
+ const lines = [
1679
+ `# ${fp.name}`,
1680
+ '',
1681
+ `> ${isZh ? '文件夹画像文档 — 由 AutoSnippet Repo Wiki 自动生成' : 'Folder profile doc — Auto-generated by AutoSnippet Repo Wiki'}`,
1682
+ '',
1683
+ ];
1684
+
1685
+ // ── 概述 ──
1686
+ lines.push(`## ${isZh ? '概述' : 'Overview'}`);
1687
+ lines.push('');
1688
+
1689
+ const purposeStr = fp.purpose
1690
+ ? (isZh ? fp.purpose.zh : fp.purpose.en)
1691
+ : (isZh ? '通过文件夹画像分析推断其功能' : 'functionality inferred from folder profiling');
1692
+
1693
+ if (isZh) {
1694
+ lines.push(
1695
+ `**${fp.name}** 位于 \`${fp.relPath}\`,${purposeStr}。` +
1696
+ `包含 ${fp.fileCount} 个源文件,总大小 ${(fp.totalSize / 1024).toFixed(1)}KB。`
1697
+ );
1698
+ } else {
1699
+ lines.push(
1700
+ `**${fp.name}** is located at \`${fp.relPath}\`, ${purposeStr}. ` +
1701
+ `Contains ${fp.fileCount} source files totaling ${(fp.totalSize / 1024).toFixed(1)}KB.`
1702
+ );
1703
+ }
1704
+ lines.push('');
1705
+
1706
+ // ── 信息表 ──
1707
+ lines.push(`| ${isZh ? '属性' : 'Property'} | ${isZh ? '值' : 'Value'} |`);
1708
+ lines.push('|--------|------|');
1709
+ lines.push(`| ${isZh ? '路径' : 'Path'} | \`${fp.relPath}\` |`);
1710
+ lines.push(`| ${isZh ? '源文件数' : 'Source Files'} | ${fp.fileCount} |`);
1711
+ const sizeStr = fp.totalSize > 1024 * 1024
1712
+ ? `${(fp.totalSize / 1024 / 1024).toFixed(1)}MB`
1713
+ : `${(fp.totalSize / 1024).toFixed(1)}KB`;
1714
+ lines.push(`| ${isZh ? '总大小' : 'Total Size'} | ${sizeStr} |`);
1715
+ if (fp.entryPoints.length > 0) {
1716
+ lines.push(`| ${isZh ? '入口文件' : 'Entry Points'} | ${fp.entryPoints.join(', ')} |`);
1717
+ }
1718
+ lines.push('');
1719
+
1720
+ // ── README (如果有) ──
1721
+ if (fp.readme) {
1722
+ lines.push(`## ${isZh ? '目录说明' : 'Directory README'}`);
1723
+ lines.push('');
1724
+ lines.push('> ' + fp.readme.split('\n').filter(l => l.trim()).slice(0, 5).join('\n> '));
1725
+ lines.push('');
1726
+ }
1727
+
1728
+ // ── 语言分布 ──
1729
+ lines.push(`## ${isZh ? '语言分布' : 'Language Distribution'}`);
1730
+ lines.push('');
1731
+ lines.push(`| ${isZh ? '语言' : 'Language'} | ${isZh ? '文件数' : 'Files'} | ${isZh ? '占比' : 'Share'} |`);
1732
+ lines.push('|--------|-------|------|');
1733
+ const total = Object.values(fp.langBreakdown).reduce((a, b) => a + b, 0);
1734
+ for (const [lang, count] of Object.entries(fp.langBreakdown).sort((a, b) => b[1] - a[1])) {
1735
+ const pct = total > 0 ? ((count / total) * 100).toFixed(0) : 0;
1736
+ lines.push(`| ${lang} | ${count} | ${pct}% |`);
1737
+ }
1738
+ lines.push('');
1739
+
1740
+ // ── 文件列表 (分类展示) ──
1741
+ lines.push(`## ${isZh ? '文件列表' : 'File Listing'}`);
1742
+ lines.push('');
1743
+
1744
+ // 按类别分: 入口文件 / 大文件 / 普通文件
1745
+ if (fp.entryPoints.length > 0) {
1746
+ lines.push(`### ${isZh ? '🎯 入口文件' : '🎯 Entry Points'}`);
1747
+ lines.push('');
1748
+ for (const ep of fp.entryPoints) {
1749
+ lines.push(`- \`${ep}\``);
1750
+ }
1751
+ lines.push('');
1752
+ }
1753
+
1754
+ if (fp.keyFiles.length > 0) {
1755
+ lines.push(`### ${isZh ? '📌 关键文件' : '📌 Key Files'}`);
1756
+ lines.push('');
1757
+ lines.push(
1758
+ isZh
1759
+ ? '以下文件体积最大或作为入口文件,可能包含核心逻辑:'
1760
+ : 'These files are the largest or serve as entry points, likely containing core logic:'
1761
+ );
1762
+ lines.push('');
1763
+ for (const kf of fp.keyFiles) {
1764
+ lines.push(`- \`${path.basename(kf)}\``);
1765
+ }
1766
+ lines.push('');
1767
+ }
1768
+
1769
+ // 所有文件列表 (截断)
1770
+ const maxDisplay = 50;
1771
+ lines.push(`### ${isZh ? '全部文件' : 'All Files'} (${fp.fileNames.length})`);
1772
+ lines.push('');
1773
+ for (const fn of fp.fileNames.slice(0, maxDisplay)) {
1774
+ lines.push(`- \`${fn}\``);
1775
+ }
1776
+ if (fp.fileNames.length > maxDisplay) {
1777
+ lines.push(
1778
+ `- ... ${isZh ? `还有 ${fp.fileNames.length - maxDisplay} 个文件` : `and ${fp.fileNames.length - maxDisplay} more files`}`
1779
+ );
1780
+ }
1781
+ lines.push('');
1782
+
1783
+ // ── 命名约定 ──
1784
+ if (fp.namingPatterns.length > 0) {
1785
+ lines.push(`## ${isZh ? '命名约定' : 'Naming Conventions'}`);
1786
+ lines.push('');
1787
+ lines.push(
1788
+ isZh
1789
+ ? '通过分析文件命名模式,检测到以下约定:'
1790
+ : 'Detected naming conventions from file name analysis:'
1791
+ );
1792
+ lines.push('');
1793
+ for (const p of fp.namingPatterns) {
1794
+ lines.push(`- **${p}**`);
1795
+ }
1796
+ lines.push('');
1797
+ }
1798
+
1799
+ // ── 依赖关系 ──
1800
+ if (fp.imports.length > 0) {
1801
+ lines.push(`## ${isZh ? '依赖引用' : 'Dependencies'}`);
1802
+ lines.push('');
1803
+ lines.push(
1804
+ isZh
1805
+ ? `通过分析 import/require 语句,\`${fp.name}\` 引用了以下模块/目录:`
1806
+ : `From import/require analysis, \`${fp.name}\` references the following modules/directories:`
1807
+ );
1808
+ lines.push('');
1809
+ for (const imp of fp.imports) {
1810
+ lines.push(`- \`${imp}\``);
1811
+ }
1812
+ lines.push('');
1813
+ }
1814
+
1815
+ // ── 代码注释摘要 ──
1816
+ if (fp.headerComments.length > 0) {
1817
+ lines.push(`## ${isZh ? '代码注释摘要' : 'Code Comments Summary'}`);
1818
+ lines.push('');
1819
+ lines.push(
1820
+ isZh
1821
+ ? '从关键文件头部提取的注释信息:'
1822
+ : 'Comments extracted from key file headers:'
1823
+ );
1824
+ lines.push('');
1825
+ for (const hc of fp.headerComments) {
1826
+ lines.push(`- ${hc}`);
1827
+ }
1828
+ lines.push('');
1829
+ }
1830
+
1831
+ lines.push(`[← ${isZh ? '返回结构分析' : 'Back to Structure Analysis'}](../folder-structure.md) | [← ${isZh ? '返回概述' : 'Back to Overview'}](../index.md)`);
1832
+ lines.push('');
1833
+ return lines.join('\n');
1834
+ }
1835
+
1836
+ // ═══ V3 AI 系统 Prompt ═══════════════════════════════════
1837
+
1838
+ /**
1839
+ * 构建 AI 系统 Prompt (V3 — 撰写完整文章,非润色骨架)
1840
+ */
1841
+ export function buildAiSystemPrompt(isZh) {
1842
+ if (isZh) {
1843
+ return [
1844
+ '你是 AutoSnippet Repo Wiki 文档撰写专家。',
1845
+ '',
1846
+ '任务: 基于代码分析数据,撰写高质量、有深度的项目文档。',
1847
+ '',
1848
+ '写作原则:',
1849
+ '1. 所有类名、文件名、数字必须来自提供的数据,严禁编造',
1850
+ '2. 不要简单罗列数据 — 要分析和解释,描述"为什么这样设计"、"模块的职责是什么"',
1851
+ '3. 从文件名和类名推断功能意图,给出有见地的分析',
1852
+ '4. 用自然语言连贯行文,包含过渡段落和总结性描述',
1853
+ '5. 合理使用 Mermaid 图表(graph TD / classDiagram)、表格、代码块来辅助说明',
1854
+ '6. 用中文撰写',
1855
+ '7. 输出纯 Markdown,不要包裹在代码块中',
1856
+ '8. 每篇文章以一级标题 (#) 开始,结构清晰',
1857
+ '9. 篇幅适中:300-2000 字(根据主题复杂度调整)',
1858
+ '10. 文末包含返回链接: [← 返回概述](index.md) 或 [← 返回概述](../index.md)',
1859
+ ].join('\n');
1860
+ }
1861
+ return [
1862
+ 'You are the AutoSnippet Repo Wiki documentation expert.',
1863
+ '',
1864
+ 'Task: Write high-quality, insightful project documentation based on code analysis data.',
1865
+ '',
1866
+ 'Writing principles:',
1867
+ '1. All class names, file names, and numbers must come from the provided data — never fabricate',
1868
+ '2. Do not simply list data — analyze and explain: describe design rationale, module responsibilities',
1869
+ '3. Infer functional intent from file names and class names, provide insightful analysis',
1870
+ '4. Write coherent prose with transition paragraphs and summaries',
1871
+ '5. Use Mermaid diagrams (graph TD / classDiagram), tables, and code blocks judiciously',
1872
+ '6. Write in English',
1873
+ '7. Output pure Markdown — do not wrap in code blocks',
1874
+ '8. Start each article with a level-1 heading (#), maintain clear structure',
1875
+ '9. Appropriate length: 300-2000 words (adjust by topic complexity)',
1876
+ '10. End with a back link: [← Back to Overview](index.md) or [← Back to Overview](../index.md)',
1877
+ ].join('\n');
1878
+ }