autosnippet 3.0.0 → 3.0.2

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 (290) hide show
  1. package/README.md +230 -324
  2. package/bin/api-server.js +1 -1
  3. package/bin/cli.js +204 -244
  4. package/bin/mcp-server.js +5 -3
  5. package/config/knowledge-base.config.js +132 -132
  6. package/dashboard/dist/assets/{icons-CEfgGaZi.js → icons-Cdq22n2i.js} +95 -100
  7. package/dashboard/dist/assets/index-ClkyPkDX.js +133 -0
  8. package/dashboard/dist/assets/index-t4QrJwv1.css +1 -0
  9. package/dashboard/dist/index.html +3 -3
  10. package/lib/bootstrap.js +8 -8
  11. package/lib/cli/AiScanService.js +86 -40
  12. package/lib/cli/KnowledgeSyncService.js +113 -74
  13. package/lib/cli/SetupService.js +439 -277
  14. package/lib/cli/UpgradeService.js +63 -100
  15. package/lib/core/AstAnalyzer.js +276 -597
  16. package/lib/core/ast/ProjectGraph.js +101 -40
  17. package/lib/core/ast/ensure-grammars.js +232 -0
  18. package/lib/core/ast/index.js +115 -0
  19. package/lib/core/ast/lang-dart.js +661 -0
  20. package/lib/core/ast/lang-go.js +530 -0
  21. package/lib/core/ast/lang-java.js +435 -0
  22. package/lib/core/ast/lang-javascript.js +272 -0
  23. package/lib/core/ast/lang-kotlin.js +423 -0
  24. package/lib/core/ast/lang-objc.js +388 -0
  25. package/lib/core/ast/lang-python.js +371 -0
  26. package/lib/core/ast/lang-swift.js +337 -0
  27. package/lib/core/ast/lang-typescript.js +503 -0
  28. package/lib/core/capability/CapabilityProbe.js +18 -9
  29. package/lib/core/constitution/Constitution.js +2 -3
  30. package/lib/core/constitution/ConstitutionValidator.js +65 -24
  31. package/lib/core/discovery/DartDiscoverer.js +534 -0
  32. package/lib/core/discovery/DiscovererRegistry.js +83 -0
  33. package/lib/core/discovery/GenericDiscoverer.js +225 -0
  34. package/lib/core/discovery/GoDiscoverer.js +541 -0
  35. package/lib/core/discovery/JvmDiscoverer.js +506 -0
  36. package/lib/core/discovery/NodeDiscoverer.js +466 -0
  37. package/lib/core/discovery/ProjectDiscoverer.js +93 -0
  38. package/lib/core/discovery/PythonDiscoverer.js +338 -0
  39. package/lib/core/discovery/SpmDiscoverer.js +5 -0
  40. package/lib/core/discovery/index.js +53 -0
  41. package/lib/core/enhancement/EnhancementPack.js +71 -0
  42. package/lib/core/enhancement/EnhancementRegistry.js +47 -0
  43. package/lib/core/enhancement/android-enhancement.js +102 -0
  44. package/lib/core/enhancement/django-enhancement.js +70 -0
  45. package/lib/core/enhancement/fastapi-enhancement.js +63 -0
  46. package/lib/core/enhancement/go-grpc-enhancement.js +152 -0
  47. package/lib/core/enhancement/go-web-enhancement.js +201 -0
  48. package/lib/core/enhancement/index.js +65 -0
  49. package/lib/core/enhancement/node-server-enhancement.js +88 -0
  50. package/lib/core/enhancement/react-enhancement.js +86 -0
  51. package/lib/core/enhancement/spring-enhancement.js +112 -0
  52. package/lib/core/enhancement/vue-enhancement.js +96 -0
  53. package/lib/core/gateway/Gateway.js +8 -9
  54. package/lib/core/gateway/GatewayActionRegistry.js +1 -1
  55. package/lib/core/permission/PermissionManager.js +12 -8
  56. package/lib/domain/index.js +13 -9
  57. package/lib/domain/knowledge/KnowledgeEntry.js +111 -101
  58. package/lib/domain/knowledge/KnowledgeRepository.js +0 -1
  59. package/lib/domain/knowledge/Lifecycle.js +22 -22
  60. package/lib/domain/knowledge/index.js +9 -12
  61. package/lib/domain/knowledge/values/Constraints.js +31 -21
  62. package/lib/domain/knowledge/values/Content.js +21 -13
  63. package/lib/domain/knowledge/values/Quality.js +31 -18
  64. package/lib/domain/knowledge/values/Reasoning.js +20 -12
  65. package/lib/domain/knowledge/values/Relations.js +37 -25
  66. package/lib/domain/knowledge/values/Stats.js +18 -12
  67. package/lib/domain/knowledge/values/index.js +4 -3
  68. package/lib/domain/snippet/Snippet.js +35 -10
  69. package/lib/external/ai/AiFactory.js +48 -16
  70. package/lib/external/ai/AiProvider.js +184 -90
  71. package/lib/external/ai/providers/ClaudeProvider.js +25 -12
  72. package/lib/external/ai/providers/GoogleGeminiProvider.js +59 -30
  73. package/lib/external/ai/providers/MockProvider.js +9 -3
  74. package/lib/external/ai/providers/OpenAiProvider.js +51 -29
  75. package/lib/external/mcp/McpServer.js +66 -36
  76. package/lib/external/mcp/errorHandler.js +23 -11
  77. package/lib/external/mcp/handlers/LanguageExtensions.js +138 -53
  78. package/lib/external/mcp/handlers/TargetClassifier.js +52 -16
  79. package/lib/external/mcp/handlers/bootstrap/pipeline/BootstrapSnapshot.js +81 -20
  80. package/lib/external/mcp/handlers/bootstrap/pipeline/EpisodicMemory.js +71 -42
  81. package/lib/external/mcp/handlers/bootstrap/pipeline/IncrementalBootstrap.js +9 -17
  82. package/lib/external/mcp/handlers/bootstrap/pipeline/ToolResultCache.js +14 -9
  83. package/lib/external/mcp/handlers/bootstrap/pipeline/dimension-context.js +15 -7
  84. package/lib/external/mcp/handlers/bootstrap/pipeline/orchestrator.js +352 -153
  85. package/lib/external/mcp/handlers/bootstrap/pipeline/tier-scheduler.js +52 -12
  86. package/lib/external/mcp/handlers/bootstrap/skills.js +143 -39
  87. package/lib/external/mcp/handlers/bootstrap.js +691 -168
  88. package/lib/external/mcp/handlers/browse.js +66 -22
  89. package/lib/external/mcp/handlers/candidate.js +118 -35
  90. package/lib/external/mcp/handlers/consolidated.js +49 -17
  91. package/lib/external/mcp/handlers/guard.js +104 -39
  92. package/lib/external/mcp/handlers/knowledge.js +60 -36
  93. package/lib/external/mcp/handlers/search.js +43 -14
  94. package/lib/external/mcp/handlers/skill.js +120 -45
  95. package/lib/external/mcp/handlers/structure.js +240 -86
  96. package/lib/external/mcp/handlers/system.js +42 -12
  97. package/lib/external/mcp/handlers/wiki.js +58 -33
  98. package/lib/external/mcp/tools.js +306 -123
  99. package/lib/http/HttpServer.js +72 -47
  100. package/lib/http/middleware/RateLimiter.js +5 -3
  101. package/lib/http/middleware/errorHandler.js +6 -1
  102. package/lib/http/middleware/requestLogger.js +14 -3
  103. package/lib/http/middleware/roleResolver.js +30 -23
  104. package/lib/http/routes/ai.js +387 -265
  105. package/lib/http/routes/auth.js +81 -61
  106. package/lib/http/routes/candidates.js +430 -320
  107. package/lib/http/routes/commands.js +289 -189
  108. package/lib/http/routes/extract.js +158 -125
  109. package/lib/http/routes/guardRules.js +309 -217
  110. package/lib/http/routes/knowledge.js +213 -154
  111. package/lib/http/routes/modules.js +578 -0
  112. package/lib/http/routes/monitoring.js +6 -6
  113. package/lib/http/routes/recipes.js +104 -93
  114. package/lib/http/routes/search.js +361 -305
  115. package/lib/http/routes/skills.js +145 -98
  116. package/lib/http/routes/snippets.js +42 -30
  117. package/lib/http/routes/spm.js +3 -405
  118. package/lib/http/routes/violations.js +113 -93
  119. package/lib/http/routes/wiki.js +211 -170
  120. package/lib/http/utils/routeHelpers.js +3 -1
  121. package/lib/http/utils/sse-sessions.js +16 -6
  122. package/lib/http/utils/sse.js +15 -5
  123. package/lib/infrastructure/audit/AuditLogger.js +5 -2
  124. package/lib/infrastructure/audit/AuditStore.js +10 -7
  125. package/lib/infrastructure/cache/CacheService.js +3 -1
  126. package/lib/infrastructure/cache/GraphCache.js +8 -4
  127. package/lib/infrastructure/cache/UnifiedCacheAdapter.js +1 -1
  128. package/lib/infrastructure/config/ConfigLoader.js +9 -5
  129. package/lib/infrastructure/config/Defaults.js +30 -10
  130. package/lib/infrastructure/config/Paths.js +28 -8
  131. package/lib/infrastructure/config/TriggerSymbol.js +22 -10
  132. package/lib/infrastructure/database/DatabaseConnection.js +15 -10
  133. package/lib/infrastructure/database/migrations/001_initial_schema.js +0 -1
  134. package/lib/infrastructure/external/ClipboardManager.js +6 -2
  135. package/lib/infrastructure/external/NativeUi.js +50 -43
  136. package/lib/infrastructure/external/OpenBrowser.js +14 -17
  137. package/lib/infrastructure/external/XcodeAutomation.js +14 -258
  138. package/lib/infrastructure/logging/Logger.js +46 -30
  139. package/lib/infrastructure/monitoring/ErrorTracker.js +7 -5
  140. package/lib/infrastructure/monitoring/PerformanceMonitor.js +12 -4
  141. package/lib/infrastructure/paths/HeaderResolver.js +25 -9
  142. package/lib/infrastructure/paths/PathFinder.js +34 -12
  143. package/lib/infrastructure/plugin/PluginManager.js +26 -8
  144. package/lib/infrastructure/realtime/RealtimeService.js +2 -2
  145. package/lib/infrastructure/vector/Chunker.js +22 -7
  146. package/lib/infrastructure/vector/IndexingPipeline.js +46 -22
  147. package/lib/infrastructure/vector/JsonVectorAdapter.js +90 -53
  148. package/lib/infrastructure/vector/VectorStore.js +28 -10
  149. package/lib/injection/ServiceContainer.js +247 -93
  150. package/lib/platform/ios/index.js +63 -0
  151. package/lib/platform/ios/routes/spm.js +437 -0
  152. package/lib/platform/ios/snippet/PlaceholderConverter.js +55 -0
  153. package/lib/platform/ios/snippet/XcodeCodec.js +112 -0
  154. package/lib/{service → platform/ios}/spm/DependencyGraph.js +41 -17
  155. package/lib/{service → platform/ios}/spm/PackageSwiftParser.js +41 -14
  156. package/lib/{service → platform/ios}/spm/PolicyEngine.js +9 -4
  157. package/lib/platform/ios/spm/SpmDiscoverer.js +122 -0
  158. package/lib/{service → platform/ios}/spm/SpmService.js +385 -127
  159. package/lib/{service/automation → platform/ios/xcode}/SaveEventFilter.js +8 -7
  160. package/lib/platform/ios/xcode/XcodeAutomation.js +350 -0
  161. package/lib/{service/automation → platform/ios/xcode}/XcodeIntegration.js +325 -145
  162. package/lib/repository/base/BaseRepository.js +7 -9
  163. package/lib/repository/knowledge/KnowledgeRepository.impl.js +98 -75
  164. package/lib/repository/token/TokenUsageStore.js +4 -2
  165. package/lib/service/automation/ActionPipeline.js +1 -1
  166. package/lib/service/automation/AutomationOrchestrator.js +8 -4
  167. package/lib/service/automation/ContextCollector.js +7 -5
  168. package/lib/service/automation/DirectiveDetector.js +23 -16
  169. package/lib/service/automation/FileWatcher.js +112 -56
  170. package/lib/service/automation/TriggerResolver.js +6 -4
  171. package/lib/service/automation/handlers/AlinkHandler.js +24 -12
  172. package/lib/service/automation/handlers/CreateHandler.js +19 -20
  173. package/lib/service/automation/handlers/DraftHandler.js +14 -8
  174. package/lib/service/automation/handlers/GuardHandler.js +93 -63
  175. package/lib/service/automation/handlers/HeaderHandler.js +1 -6
  176. package/lib/service/automation/handlers/SearchHandler.js +155 -88
  177. package/lib/service/bootstrap/BootstrapTaskManager.js +77 -35
  178. package/lib/service/candidate/SimilarityService.js +25 -9
  179. package/lib/service/chat/AnalystAgent.js +50 -24
  180. package/lib/service/chat/CandidateGuardrail.js +143 -17
  181. package/lib/service/chat/ChatAgent.js +759 -243
  182. package/lib/service/chat/ContextWindow.js +116 -71
  183. package/lib/service/chat/ConversationStore.js +77 -36
  184. package/lib/service/chat/EpisodicConsolidator.js +47 -23
  185. package/lib/service/chat/HandoffProtocol.js +98 -22
  186. package/lib/service/chat/Memory.js +34 -14
  187. package/lib/service/chat/ProducerAgent.js +40 -20
  188. package/lib/service/chat/ProjectSemanticMemory.js +109 -78
  189. package/lib/service/chat/ReasoningLayer.js +148 -70
  190. package/lib/service/chat/ReasoningTrace.js +44 -32
  191. package/lib/service/chat/TaskPipeline.js +39 -19
  192. package/lib/service/chat/ToolRegistry.js +48 -29
  193. package/lib/service/chat/WorkingMemory.js +44 -18
  194. package/lib/service/chat/tools.js +1096 -494
  195. package/lib/service/context/RecipeExtractor.js +132 -51
  196. package/lib/service/cursor/CursorDeliveryPipeline.js +82 -37
  197. package/lib/service/cursor/KnowledgeCompressor.js +25 -22
  198. package/lib/service/cursor/RulesGenerator.js +13 -7
  199. package/lib/service/cursor/SkillsSyncer.js +77 -27
  200. package/lib/service/cursor/TokenBudget.js +2 -2
  201. package/lib/service/cursor/TopicClassifier.js +54 -20
  202. package/lib/service/guard/ComplianceReporter.js +55 -43
  203. package/lib/service/guard/ExclusionManager.js +67 -29
  204. package/lib/service/guard/GuardCheckEngine.js +381 -86
  205. package/lib/service/guard/GuardFeedbackLoop.js +22 -10
  206. package/lib/service/guard/GuardService.js +29 -19
  207. package/lib/service/guard/RuleLearner.js +55 -23
  208. package/lib/service/guard/SourceFileCollector.js +27 -20
  209. package/lib/service/guard/ViolationsStore.js +43 -38
  210. package/lib/service/knowledge/CodeEntityGraph.js +147 -82
  211. package/lib/service/knowledge/ConfidenceRouter.js +12 -10
  212. package/lib/service/knowledge/KnowledgeFileWriter.js +147 -56
  213. package/lib/service/knowledge/KnowledgeGraphService.js +81 -34
  214. package/lib/service/knowledge/KnowledgeService.js +222 -112
  215. package/lib/service/module/ModuleService.js +969 -0
  216. package/lib/service/quality/FeedbackCollector.js +27 -15
  217. package/lib/service/quality/QualityScorer.js +78 -24
  218. package/lib/service/recipe/RecipeCandidateValidator.js +110 -44
  219. package/lib/service/recipe/RecipeParser.js +78 -45
  220. package/lib/service/search/CoarseRanker.js +43 -28
  221. package/lib/service/search/CrossEncoderReranker.js +32 -21
  222. package/lib/service/search/InvertedIndex.js +21 -7
  223. package/lib/service/search/MultiSignalRanker.js +90 -28
  224. package/lib/service/search/RetrievalFunnel.js +45 -24
  225. package/lib/service/search/SearchEngine.js +255 -103
  226. package/lib/service/skills/EventAggregator.js +32 -15
  227. package/lib/service/skills/SignalCollector.js +140 -64
  228. package/lib/service/skills/SkillAdvisor.js +79 -42
  229. package/lib/service/skills/SkillHooks.js +16 -14
  230. package/lib/service/snippet/PlaceholderConverter.js +5 -0
  231. package/lib/service/snippet/SnippetFactory.js +116 -99
  232. package/lib/service/snippet/SnippetInstaller.js +234 -62
  233. package/lib/service/snippet/codecs/SnippetCodec.js +67 -0
  234. package/lib/service/snippet/codecs/VSCodeCodec.js +102 -0
  235. package/lib/service/snippet/codecs/XcodeCodec.js +5 -0
  236. package/lib/service/wiki/WikiGenerator.js +637 -263
  237. package/lib/shared/DimensionCopyRegistry.js +472 -0
  238. package/lib/shared/LanguageService.js +399 -0
  239. package/lib/shared/PathGuard.js +45 -28
  240. package/lib/shared/RecipeReadinessChecker.js +72 -12
  241. package/lib/shared/constants.js +41 -41
  242. package/lib/shared/errors/BaseError.js +2 -2
  243. package/lib/shared/errors/index.js +4 -4
  244. package/lib/shared/similarity.js +25 -8
  245. package/lib/shared/token-utils.js +6 -2
  246. package/lib/shared/utils/common.js +12 -4
  247. package/package.json +49 -13
  248. package/scripts/bench-real-projects.mjs +256 -0
  249. package/scripts/build-native-ui.js +30 -30
  250. package/scripts/clear-old-vector-index.js +5 -35
  251. package/scripts/clear-vector-cache.js +7 -37
  252. package/scripts/collect-test-project-stats.mjs +160 -0
  253. package/scripts/diagnose-mcp.js +41 -32
  254. package/scripts/ensure-parse-package.js +6 -9
  255. package/scripts/generate-recipe-drafts.js +116 -77
  256. package/scripts/init-db.js +3 -20
  257. package/scripts/init-snippets.js +305 -0
  258. package/scripts/init-vector-db.js +173 -170
  259. package/scripts/install-cursor-skill.js +148 -104
  260. package/scripts/install-full.js +8 -21
  261. package/scripts/install-vscode-copilot.js +146 -145
  262. package/scripts/migrate-md-to-knowledge.mjs +139 -151
  263. package/scripts/postinstall-safe.js +5 -17
  264. package/scripts/recipe-audit.js +106 -82
  265. package/scripts/release.js +283 -323
  266. package/scripts/setup-mcp-config.js +60 -52
  267. package/scripts/verify-context-api.js +20 -20
  268. package/skills/autosnippet-analysis/SKILL.md +10 -6
  269. package/skills/autosnippet-candidates/SKILL.md +27 -26
  270. package/skills/autosnippet-coldstart/SKILL.md +555 -38
  271. package/skills/autosnippet-concepts/SKILL.md +349 -337
  272. package/skills/autosnippet-create/SKILL.md +5 -5
  273. package/skills/autosnippet-reference-dart/SKILL.md +543 -0
  274. package/skills/autosnippet-reference-go/SKILL.md +539 -0
  275. package/skills/autosnippet-reference-java/SKILL.md +534 -0
  276. package/skills/autosnippet-reference-jsts/SKILL.md +41 -9
  277. package/skills/autosnippet-reference-kotlin/SKILL.md +526 -0
  278. package/skills/autosnippet-reference-objc/SKILL.md +29 -6
  279. package/skills/autosnippet-reference-python/SKILL.md +800 -0
  280. package/skills/autosnippet-reference-swift/SKILL.md +70 -14
  281. package/skills/autosnippet-structure/SKILL.md +4 -4
  282. package/templates/cursor-rules/autosnippet-conventions.mdc +2 -2
  283. package/templates/recipes-setup/README.md +2 -2
  284. package/templates/recipes-setup/_template.md +1 -1
  285. package/dashboard/dist/assets/index-Bun3ld_J.css +0 -1
  286. package/dashboard/dist/assets/index-_Sk_Dmg3.js +0 -143
  287. package/resources/asd-entry/main.swift +0 -159
  288. package/scripts/build-asd-entry.js +0 -51
  289. package/scripts/init-xcode-snippets.js +0 -311
  290. package/template.json +0 -39
@@ -30,34 +30,35 @@
30
30
  * @module WikiGenerator
31
31
  */
32
32
 
33
+ import { createHash } from 'node:crypto';
33
34
  import fs from 'node:fs';
34
35
  import path from 'node:path';
35
- import { createHash } from 'node:crypto';
36
36
  import Logger from '../../infrastructure/logging/Logger.js';
37
+ import { LanguageService } from '../../shared/LanguageService.js';
37
38
 
38
39
  const logger = Logger.getInstance();
39
40
 
40
41
  // ─── Wiki 生成阶段 ──────────────────────────────────────────
41
42
 
42
43
  export const WikiPhase = Object.freeze({
43
- INIT: 'init', // 初始化 & 自检
44
- SCAN: 'scan', // 扫描项目结构
45
- AST_ANALYZE: 'ast-analyze', // AST 深度分析
46
- SPM_PARSE: 'spm-parse', // SPM 依赖解析
47
- KNOWLEDGE: 'knowledge', // 整合已有 Recipes
48
- GENERATE: 'generate', // 生成 Markdown 骨架
49
- AI_COMPOSE: 'ai-compose', // AI 合成写作增强
50
- SYNC_DOCS: 'sync-docs', // 同步 Cursor 端 MD
51
- DEDUP: 'dedup', // 去重
52
- FINALIZE: 'finalize', // 写入 meta.json
44
+ INIT: 'init', // 初始化 & 自检
45
+ SCAN: 'scan', // 扫描项目结构
46
+ AST_ANALYZE: 'ast-analyze', // AST 深度分析
47
+ SPM_PARSE: 'spm-parse', // SPM 依赖解析
48
+ KNOWLEDGE: 'knowledge', // 整合已有 Recipes
49
+ GENERATE: 'generate', // 生成 Markdown 骨架
50
+ AI_COMPOSE: 'ai-compose', // AI 合成写作增强
51
+ SYNC_DOCS: 'sync-docs', // 同步 Cursor 端 MD
52
+ DEDUP: 'dedup', // 去重
53
+ FINALIZE: 'finalize', // 写入 meta.json
53
54
  });
54
55
 
55
56
  // ─── 默认配置 ────────────────────────────────────────────────
56
57
 
57
58
  const DEFAULTS = {
58
- wikiDir: 'AutoSnippet/wiki',
59
- language: 'zh', // 'zh' | 'en'
60
- maxFiles: 500,
59
+ wikiDir: 'AutoSnippet/wiki',
60
+ language: 'zh', // 'zh' | 'en'
61
+ maxFiles: 500,
61
62
  includeRecipes: true,
62
63
  includeDepGraph: true,
63
64
  includeComponents: true,
@@ -69,7 +70,8 @@ export class WikiGenerator {
69
70
  /**
70
71
  * @param {object} deps
71
72
  * @param {string} deps.projectRoot
72
- * @param {import('../../service/spm/SpmService.js').SpmService} [deps.spmService]
73
+ * @param {import('../../service/module/ModuleService.js').ModuleService} [deps.moduleService]
74
+ * @param {import('../../platform/ios/spm/SpmService.js').SpmService} [deps.spmService] — 向后兼容
73
75
  * @param {import('../../service/knowledge/KnowledgeService.js').KnowledgeService} [deps.knowledgeService]
74
76
  * @param {import('../../core/ast/ProjectGraph.js').default} [deps.projectGraph]
75
77
  * @param {import('../../service/knowledge/CodeEntityGraph.js').CodeEntityGraph} [deps.codeEntityGraph]
@@ -78,14 +80,14 @@ export class WikiGenerator {
78
80
  * @param {object} [deps.options]
79
81
  */
80
82
  constructor(deps) {
81
- this.projectRoot = deps.projectRoot;
82
- this.spmService = deps.spmService || null;
83
+ this.projectRoot = deps.projectRoot;
84
+ this.moduleService = deps.moduleService || null;
83
85
  this.knowledgeService = deps.knowledgeService || null;
84
- this.projectGraph = deps.projectGraph || null;
85
- this.codeEntityGraph = deps.codeEntityGraph || null;
86
- this.aiProvider = deps.aiProvider || null;
87
- this.onProgress = deps.onProgress || (() => {});
88
- this.options = { ...DEFAULTS, ...deps.options };
86
+ this.projectGraph = deps.projectGraph || null;
87
+ this.codeEntityGraph = deps.codeEntityGraph || null;
88
+ this.aiProvider = deps.aiProvider || null;
89
+ this.onProgress = deps.onProgress || (() => {});
90
+ this.options = { ...DEFAULTS, ...deps.options };
89
91
 
90
92
  this.wikiDir = path.join(this.projectRoot, this.options.wikiDir);
91
93
  this.metaPath = path.join(this.wikiDir, 'meta.json');
@@ -111,39 +113,53 @@ export class WikiGenerator {
111
113
  // Phase 2: Scan project
112
114
  this._emit(WikiPhase.SCAN, 5, '扫描项目结构...');
113
115
  const projectInfo = await this._scanProject();
114
- if (this._aborted) return this._abortedResult();
116
+ if (this._aborted) {
117
+ return this._abortedResult();
118
+ }
115
119
 
116
120
  // Phase 3: AST analyze
117
121
  this._emit(WikiPhase.AST_ANALYZE, 15, '执行 AST 深度分析...');
118
122
  const astInfo = await this._analyzeAST();
119
- if (this._aborted) return this._abortedResult();
123
+ if (this._aborted) {
124
+ return this._abortedResult();
125
+ }
120
126
 
121
- // Phase 4: SPM parse
122
- this._emit(WikiPhase.SPM_PARSE, 30, '解析 SPM 依赖关系...');
127
+ // Phase 4: Module/SPM parse
128
+ this._emit(WikiPhase.SPM_PARSE, 30, '解析模块依赖关系...');
123
129
  const spmInfo = await this._parseSPM();
124
- if (this._aborted) return this._abortedResult();
130
+ if (this._aborted) {
131
+ return this._abortedResult();
132
+ }
125
133
 
126
134
  // Phase 5: Knowledge integration
127
135
  this._emit(WikiPhase.KNOWLEDGE, 45, '整合知识库 Recipes...');
128
136
  const knowledgeInfo = await this._integrateKnowledge();
129
- if (this._aborted) return this._abortedResult();
137
+ if (this._aborted) {
138
+ return this._abortedResult();
139
+ }
130
140
 
131
141
  // Phase 6: Content-driven topic discovery (V3)
132
142
  this._emit(WikiPhase.GENERATE, 50, '分析项目数据,发现文档主题...');
133
143
  const structuredData = { projectInfo, astInfo, spmInfo, knowledgeInfo };
134
144
  const topics = this._discoverTopics(projectInfo, astInfo, spmInfo, knowledgeInfo);
135
- if (this._aborted) return this._abortedResult();
145
+ if (this._aborted) {
146
+ return this._abortedResult();
147
+ }
136
148
 
137
149
  // Phase 7: AI-first article composition (V3)
138
150
  this._emit(WikiPhase.AI_COMPOSE, 55, `撰写 ${topics.length} 篇文档...`);
139
151
  const files = await this._composeArticles(topics, structuredData);
140
- if (this._aborted) return this._abortedResult();
152
+ if (this._aborted) {
153
+ return this._abortedResult();
154
+ }
141
155
 
142
156
  // Phase 8: Sync Cursor docs
143
157
  this._emit(WikiPhase.SYNC_DOCS, 80, '同步 Cursor 端文档...');
144
158
  const syncedFiles = this._syncCursorDocs();
145
159
  files.push(...syncedFiles);
146
- if (this._aborted) return this._abortedResult();
160
+ if (this._aborted) {
161
+ return this._abortedResult();
162
+ }
147
163
 
148
164
  // Phase 9: Dedup
149
165
  this._emit(WikiPhase.DEDUP, 90, '去重检查...');
@@ -159,7 +175,7 @@ export class WikiGenerator {
159
175
  return {
160
176
  success: true,
161
177
  filesGenerated: files.length,
162
- aiComposed: files.filter(f => f.polished).length,
178
+ aiComposed: files.filter((f) => f.polished).length,
163
179
  syncedDocs: syncedFiles.length,
164
180
  dedup: dedupResult,
165
181
  duration,
@@ -237,20 +253,34 @@ export class WikiGenerator {
237
253
  // 检测项目类型
238
254
  const entries = fs.readdirSync(this.projectRoot, { withFileTypes: true });
239
255
  for (const e of entries) {
240
- if (e.name === 'Package.swift') info.hasPackageSwift = true;
241
- if (e.name === 'Podfile') info.hasPodfile = true;
242
- if (e.name.endsWith('.xcodeproj') || e.name.endsWith('.xcworkspace')) info.hasXcodeproj = true;
256
+ if (e.name === 'Package.swift') {
257
+ info.hasPackageSwift = true;
258
+ }
259
+ if (e.name === 'Podfile') {
260
+ info.hasPodfile = true;
261
+ }
262
+ if (e.name.endsWith('.xcodeproj') || e.name.endsWith('.xcworkspace')) {
263
+ info.hasXcodeproj = true;
264
+ }
243
265
  }
244
266
 
245
267
  // 统计源文件
246
- const extMap = { '.swift': 'Swift', '.m': 'Objective-C', '.h': 'Objective-C Header', '.mm': 'Objective-C++' };
247
- this._walkDir(this.projectRoot, (filePath) => {
248
- const ext = path.extname(filePath);
249
- if (extMap[ext]) {
250
- info.sourceFiles.push(path.relative(this.projectRoot, filePath));
251
- info.languages[extMap[ext]] = (info.languages[extMap[ext]] || 0) + 1;
252
- }
253
- }, this.options.maxFiles);
268
+ const extMap = {};
269
+ for (const ext of LanguageService.sourceExts) {
270
+ extMap[ext] = LanguageService.displayNameFromExt(ext) || ext;
271
+ }
272
+ this._walkDir(
273
+ this.projectRoot,
274
+ (filePath) => {
275
+ const ext = path.extname(filePath);
276
+ if (extMap[ext]) {
277
+ info.sourceFiles.push(path.relative(this.projectRoot, filePath));
278
+ const displayLang = LanguageService.displayNameFromExt(ext);
279
+ info.languages[displayLang] = (info.languages[displayLang] || 0) + 1;
280
+ }
281
+ },
282
+ this.options.maxFiles
283
+ );
254
284
 
255
285
  // 按模块/Target 分组源文件 (SPM 约定: Sources/{ModuleName}/...)
256
286
  info.sourceFilesByModule = {};
@@ -266,7 +296,9 @@ export class WikiGenerator {
266
296
  mod = parts.length > 1 ? parts[0] : null;
267
297
  }
268
298
  if (mod) {
269
- if (!info.sourceFilesByModule[mod]) info.sourceFilesByModule[mod] = [];
299
+ if (!info.sourceFilesByModule[mod]) {
300
+ info.sourceFilesByModule[mod] = [];
301
+ }
270
302
  info.sourceFilesByModule[mod].push(f);
271
303
  }
272
304
  }
@@ -293,7 +325,9 @@ export class WikiGenerator {
293
325
  if (info?.filePath) {
294
326
  const mod = this._inferModuleFromPath(info.filePath);
295
327
  if (mod) {
296
- if (!classNamesByModule[mod]) classNamesByModule[mod] = [];
328
+ if (!classNamesByModule[mod]) {
329
+ classNamesByModule[mod] = [];
330
+ }
297
331
  classNamesByModule[mod].push(name);
298
332
  }
299
333
  }
@@ -304,13 +338,19 @@ export class WikiGenerator {
304
338
  if (info?.filePath) {
305
339
  const mod = this._inferModuleFromPath(info.filePath);
306
340
  if (mod) {
307
- if (!protocolNamesByModule[mod]) protocolNamesByModule[mod] = [];
341
+ if (!protocolNamesByModule[mod]) {
342
+ protocolNamesByModule[mod] = [];
343
+ }
308
344
  protocolNamesByModule[mod].push(name);
309
345
  }
310
346
  }
311
347
  }
312
348
 
313
- this._emit(WikiPhase.AST_ANALYZE, 25, `AST 分析: ${overview.totalClasses} 个类, ${overview.totalProtocols} 个协议`);
349
+ this._emit(
350
+ WikiPhase.AST_ANALYZE,
351
+ 25,
352
+ `AST 分析: ${overview.totalClasses} 个类, ${overview.totalProtocols} 个协议`
353
+ );
314
354
  return {
315
355
  overview,
316
356
  classes: allClasses,
@@ -321,27 +361,40 @@ export class WikiGenerator {
321
361
  }
322
362
 
323
363
  // 没有现成的 ProjectGraph — 返回空壳(不阻塞生成)
324
- return { overview: null, classes: [], protocols: [], classNamesByModule: {}, protocolNamesByModule: {} };
364
+ return {
365
+ overview: null,
366
+ classes: [],
367
+ protocols: [],
368
+ classNamesByModule: {},
369
+ protocolNamesByModule: {},
370
+ };
325
371
  }
326
372
 
327
373
  /**
328
- * SPM 依赖解析
374
+ * 模块依赖解析
375
+ * 通过 moduleService 统一处理所有语言的模块扫描
329
376
  */
330
377
  async _parseSPM() {
331
- if (!this.spmService) return { targets: [], depGraph: null };
378
+ if (!this.moduleService) {
379
+ return { targets: [], depGraph: null };
380
+ }
332
381
 
333
382
  try {
334
- const targets = await this.spmService.listTargets();
383
+ await this.moduleService.load();
384
+ const targets = await this.moduleService.listTargets();
335
385
  let depGraph = null;
336
386
  if (this.options.includeDepGraph) {
337
387
  try {
338
- depGraph = await this.spmService.getDependencyGraph({ level: 'target' });
339
- } catch { /* non-critical */ }
388
+ depGraph = await this.moduleService.getDependencyGraph({ level: 'target' });
389
+ } catch {
390
+ /* non-critical */
391
+ }
340
392
  }
341
- this._emit(WikiPhase.SPM_PARSE, 40, `SPM: ${targets.length} 个 Target`);
342
- return { targets, depGraph };
393
+ const info = this.moduleService.getProjectInfo();
394
+ this._emit(WikiPhase.SPM_PARSE, 40, `模块: ${targets.length} (${info.primaryLanguage})`);
395
+ return { targets, depGraph, projectInfo: info };
343
396
  } catch (err) {
344
- logger.warn('[WikiGenerator] SPM parse failed', { error: err.message });
397
+ logger.warn('[WikiGenerator] ModuleService parse failed', { error: err.message });
345
398
  return { targets: [], depGraph: null };
346
399
  }
347
400
  }
@@ -361,7 +414,7 @@ export class WikiGenerator {
361
414
  offset: 0,
362
415
  });
363
416
  const recipes = result.items || result || [];
364
- const stats = await this.knowledgeService.getStats?.() || null;
417
+ const stats = (await this.knowledgeService.getStats?.()) || null;
365
418
  this._emit(WikiPhase.KNOWLEDGE, 55, `知识库: ${recipes.length} 条活跃 Recipe`);
366
419
  return { recipes: Array.isArray(recipes) ? recipes : [], stats };
367
420
  } catch (err) {
@@ -411,7 +464,8 @@ export class WikiGenerator {
411
464
 
412
465
  // ── 3. 快速上手 (需要构建配置或入口点) ──
413
466
  const hasEntryPoints = (astInfo.overview?.entryPoints?.length || 0) > 0;
414
- const hasBuildSystem = projectInfo.hasPackageSwift || projectInfo.hasPodfile || projectInfo.hasXcodeproj;
467
+ const hasBuildSystem =
468
+ projectInfo.hasPackageSwift || projectInfo.hasPodfile || projectInfo.hasXcodeproj;
415
469
 
416
470
  if (hasEntryPoints || hasBuildSystem) {
417
471
  topics.push({
@@ -434,7 +488,9 @@ export class WikiGenerator {
434
488
  const richness = moduleFiles.length + classCount * 2 + protoCount * 2 + depCount;
435
489
 
436
490
  // 跳过过于单薄的模块 (少于3分不值得独立文档)
437
- if (richness < 3) continue;
491
+ if (richness < 3) {
492
+ continue;
493
+ }
438
494
 
439
495
  topics.push({
440
496
  id: `module-${_slug(target.name)}`,
@@ -452,7 +508,9 @@ export class WikiGenerator {
452
508
  for (const r of knowledgeInfo.recipes) {
453
509
  const json = r.toJSON ? r.toJSON() : r;
454
510
  const cat = json.category || 'Other';
455
- if (!groups[cat]) groups[cat] = [];
511
+ if (!groups[cat]) {
512
+ groups[cat] = [];
513
+ }
456
514
  groups[cat].push(json);
457
515
  }
458
516
 
@@ -470,7 +528,9 @@ export class WikiGenerator {
470
528
  } else {
471
529
  // 按分类拆分为多篇
472
530
  for (const [cat, items] of catEntries) {
473
- if (items.length < 2) continue;
531
+ if (items.length < 2) {
532
+ continue;
533
+ }
474
534
  topics.push({
475
535
  id: `pattern-${_slug(cat)}`,
476
536
  path: `patterns/${_slug(cat)}.md`,
@@ -497,7 +557,9 @@ export class WikiGenerator {
497
557
  // 按优先级排序
498
558
  topics.sort((a, b) => b.priority - a.priority);
499
559
 
500
- logger.info(`[WikiGenerator] Discovered ${topics.length} topics: ${topics.map(t => t.id).join(', ')}`);
560
+ logger.info(
561
+ `[WikiGenerator] Discovered ${topics.length} topics: ${topics.map((t) => t.id).join(', ')}`
562
+ );
501
563
  this._emit(WikiPhase.GENERATE, 55, `发现 ${topics.length} 个文档主题`);
502
564
  return topics;
503
565
  }
@@ -521,20 +583,28 @@ export class WikiGenerator {
521
583
 
522
584
  // 确保必要的子目录存在
523
585
  this._ensureDir(this.wikiDir);
524
- const needsModulesDir = topics.some(t => t.path.startsWith('modules/'));
525
- const needsPatternsDir = topics.some(t => t.path.startsWith('patterns/'));
526
- if (needsModulesDir) this._ensureDir(path.join(this.wikiDir, 'modules'));
527
- if (needsPatternsDir) this._ensureDir(path.join(this.wikiDir, 'patterns'));
586
+ const needsModulesDir = topics.some((t) => t.path.startsWith('modules/'));
587
+ const needsPatternsDir = topics.some((t) => t.path.startsWith('patterns/'));
588
+ if (needsModulesDir) {
589
+ this._ensureDir(path.join(this.wikiDir, 'modules'));
590
+ }
591
+ if (needsPatternsDir) {
592
+ this._ensureDir(path.join(this.wikiDir, 'patterns'));
593
+ }
528
594
 
529
595
  let composed = 0;
530
596
  const systemPrompt = this._buildAiSystemPrompt(isZh);
531
597
 
532
598
  for (let i = 0; i < topics.length; i++) {
533
- if (this._aborted) break;
599
+ if (this._aborted) {
600
+ break;
601
+ }
534
602
 
535
603
  const topic = topics[i];
536
604
  // 将全部主题列表注入 overview,用于生成导航
537
- if (topic.type === 'overview') topic._allTopics = topics;
605
+ if (topic.type === 'overview') {
606
+ topic._allTopics = topics;
607
+ }
538
608
 
539
609
  const progress = 58 + Math.round((i / topics.length) * 22);
540
610
  this._emit(WikiPhase.AI_COMPOSE, progress, `撰写: ${topic.title}`);
@@ -547,7 +617,9 @@ export class WikiGenerator {
547
617
  const prompt = this._buildArticlePrompt(topic, structuredData, isZh);
548
618
  const aiResult = await Promise.race([
549
619
  this.aiProvider.chat(prompt, { systemPrompt, temperature: 0.3, maxTokens: 4096 }),
550
- new Promise((_, reject) => setTimeout(() => reject(new Error('AI compose timeout')), 45_000)),
620
+ new Promise((_, reject) =>
621
+ setTimeout(() => reject(new Error('AI compose timeout')), 45_000)
622
+ ),
551
623
  ]);
552
624
 
553
625
  if (aiResult && typeof aiResult === 'string' && aiResult.length >= MIN_ARTICLE_CHARS) {
@@ -566,7 +638,9 @@ export class WikiGenerator {
566
638
 
567
639
  // === 3. 质量关卡 ===
568
640
  if (!content || content.length < MIN_ARTICLE_CHARS) {
569
- logger.info(`[WikiGenerator] Skipping thin topic: ${topic.id} (${content?.length || 0} chars)`);
641
+ logger.info(
642
+ `[WikiGenerator] Skipping thin topic: ${topic.id} (${content?.length || 0} chars)`
643
+ );
570
644
  continue;
571
645
  }
572
646
 
@@ -579,7 +653,11 @@ export class WikiGenerator {
579
653
  }
580
654
 
581
655
  logger.info(`[WikiGenerator] Composed ${files.length} articles (${composed} AI-enhanced)`);
582
- this._emit(WikiPhase.AI_COMPOSE, 80, `撰写完成: ${files.length} 篇文档 (${composed} 篇 AI 增强)`);
656
+ this._emit(
657
+ WikiPhase.AI_COMPOSE,
658
+ 80,
659
+ `撰写完成: ${files.length} 篇文档 (${composed} 篇 AI 增强)`
660
+ );
583
661
  return files;
584
662
  }
585
663
 
@@ -596,9 +674,16 @@ export class WikiGenerator {
596
674
 
597
675
  // 公共项目上下文
598
676
  parts.push(`# 项目: ${projectInfo.name}`);
599
- parts.push(`源文件数: ${projectInfo.sourceFiles.length}, SPM Targets: ${spmInfo.targets.length}, 活跃知识条目: ${knowledgeInfo.recipes.length}`);
677
+ parts.push(
678
+ `源文件数: ${projectInfo.sourceFiles.length}, SPM Targets: ${spmInfo.targets.length}, 活跃知识条目: ${knowledgeInfo.recipes.length}`
679
+ );
600
680
  if (projectInfo.languages) {
601
- parts.push(`语言分布: ${Object.entries(projectInfo.languages).sort((a, b) => b[1] - a[1]).map(([l, c]) => `${l}(${c})`).join(', ')}`);
681
+ parts.push(
682
+ `语言分布: ${Object.entries(projectInfo.languages)
683
+ .sort((a, b) => b[1] - a[1])
684
+ .map(([l, c]) => `${l}(${c})`)
685
+ .join(', ')}`
686
+ );
602
687
  }
603
688
  parts.push('');
604
689
 
@@ -609,10 +694,18 @@ export class WikiGenerator {
609
694
 
610
695
  // 项目类型
611
696
  const types = [];
612
- if (projectInfo.hasPackageSwift) types.push('SPM');
613
- if (projectInfo.hasPodfile) types.push('CocoaPods');
614
- if (projectInfo.hasXcodeproj) types.push('Xcode Project');
615
- if (types.length > 0) parts.push(`构建系统: ${types.join(' + ')}`);
697
+ if (projectInfo.hasPackageSwift) {
698
+ types.push('SPM');
699
+ }
700
+ if (projectInfo.hasPodfile) {
701
+ types.push('CocoaPods');
702
+ }
703
+ if (projectInfo.hasXcodeproj) {
704
+ types.push('Xcode Project');
705
+ }
706
+ if (types.length > 0) {
707
+ parts.push(`构建系统: ${types.join(' + ')}`);
708
+ }
616
709
  parts.push('');
617
710
 
618
711
  // 模块结构
@@ -621,8 +714,12 @@ export class WikiGenerator {
621
714
  for (const t of spmInfo.targets) {
622
715
  const files = this._getModuleSourceFiles(t, projectInfo);
623
716
  const cls = astInfo.classNamesByModule?.[t.name]?.length || 0;
624
- const deps = (t.dependencies || t.info?.dependencies || []).map(d => typeof d === 'string' ? d : d.name);
625
- parts.push(`- ${t.name} (${t.type || 'target'}): ${files.length} 文件, ${cls} 个类型${deps.length > 0 ? `, 依赖: ${deps.join(', ')}` : ''}`);
717
+ const deps = (t.dependencies || t.info?.dependencies || []).map((d) =>
718
+ typeof d === 'string' ? d : d.name
719
+ );
720
+ parts.push(
721
+ `- ${t.name} (${t.type || 'target'}): ${files.length} 文件, ${cls} 个类型${deps.length > 0 ? `, 依赖: ${deps.join(', ')}` : ''}`
722
+ );
626
723
  }
627
724
  parts.push('');
628
725
  }
@@ -630,12 +727,14 @@ export class WikiGenerator {
630
727
  // AST 概况
631
728
  if (astInfo.overview) {
632
729
  parts.push('### 代码规模');
633
- parts.push(`类/结构体: ${astInfo.overview.totalClasses || 0}, 协议: ${astInfo.overview.totalProtocols || 0}, 方法: ${astInfo.overview.totalMethods || 0}`);
730
+ parts.push(
731
+ `类/结构体: ${astInfo.overview.totalClasses || 0}, 协议: ${astInfo.overview.totalProtocols || 0}, 方法: ${astInfo.overview.totalMethods || 0}`
732
+ );
634
733
  parts.push('');
635
734
  }
636
735
 
637
736
  // 可用的其他文档(用于导航链接)
638
- const otherTopics = (topic._allTopics || []).filter(t => t.type !== 'overview');
737
+ const otherTopics = (topic._allTopics || []).filter((t) => t.type !== 'overview');
639
738
  if (otherTopics.length > 0) {
640
739
  parts.push('### 需要包含的导航链接');
641
740
  for (const t of otherTopics) {
@@ -645,7 +744,9 @@ export class WikiGenerator {
645
744
  }
646
745
 
647
746
  parts.push('要求: 撰写完整的项目概述文档。');
648
- parts.push('包含: 项目简介(解释项目做什么)、模块总览(表格形式)、技术栈分析、核心数据指标、文档导航索引。');
747
+ parts.push(
748
+ '包含: 项目简介(解释项目做什么)、模块总览(表格形式)、技术栈分析、核心数据指标、文档导航索引。'
749
+ );
649
750
  parts.push('不要只列数据 — 要解释项目的定位、各模块的职责和协作关系。');
650
751
  break;
651
752
  }
@@ -657,8 +758,12 @@ export class WikiGenerator {
657
758
  if (spmInfo.targets.length > 0) {
658
759
  parts.push('### 模块及依赖关系');
659
760
  for (const t of spmInfo.targets) {
660
- const deps = (t.dependencies || t.info?.dependencies || []).map(d => typeof d === 'string' ? d : d.name);
661
- parts.push(`- ${t.name} (${t.type || 'target'})${deps.length > 0 ? ` 依赖: ${deps.join(', ')}` : ''}`);
761
+ const deps = (t.dependencies || t.info?.dependencies || []).map((d) =>
762
+ typeof d === 'string' ? d : d.name
763
+ );
764
+ parts.push(
765
+ `- ${t.name} (${t.type || 'target'})${deps.length > 0 ? ` → 依赖: ${deps.join(', ')}` : ''}`
766
+ );
662
767
  }
663
768
  parts.push('');
664
769
  }
@@ -687,7 +792,9 @@ export class WikiGenerator {
687
792
  }
688
793
 
689
794
  parts.push('要求: 撰写架构分析文档。');
690
- parts.push('包含: 模块依赖图(使用 Mermaid graph TD 语法)、分层架构分析(解释每层的职责)、模块间协作关系、架构设计决策阐述。');
795
+ parts.push(
796
+ '包含: 模块依赖图(使用 Mermaid graph TD 语法)、分层架构分析(解释每层的职责)、模块间协作关系、架构设计决策阐述。'
797
+ );
691
798
  parts.push('用 Mermaid 绘制依赖关系图和继承层次图。分析为什么采用这种架构。');
692
799
  break;
693
800
  }
@@ -705,15 +812,21 @@ export class WikiGenerator {
705
812
  parts.push('### 模块基本信息');
706
813
  parts.push(`- 类型: ${target.type || 'target'}`);
707
814
  const tPath = target.path || target.info?.path;
708
- if (tPath) parts.push(`- 路径: ${tPath}`);
709
- if (target.packageName) parts.push(`- 所属包: ${target.packageName}`);
815
+ if (tPath) {
816
+ parts.push(`- 路径: ${tPath}`);
817
+ }
818
+ if (target.packageName) {
819
+ parts.push(`- 所属包: ${target.packageName}`);
820
+ }
710
821
  parts.push(`- 源文件: ${moduleFiles.length} 个`);
711
822
  parts.push(`- 类/结构体: ${moduleClasses.length} 个`);
712
823
  parts.push(`- 协议: ${moduleProtocols.length} 个`);
713
824
  parts.push('');
714
825
 
715
826
  if (deps.length > 0) {
716
- parts.push(`### 依赖: ${deps.map(d => typeof d === 'string' ? d : d.name).join(', ')}`);
827
+ parts.push(
828
+ `### 依赖: ${deps.map((d) => (typeof d === 'string' ? d : d.name)).join(', ')}`
829
+ );
717
830
  parts.push('');
718
831
  }
719
832
 
@@ -729,28 +842,36 @@ export class WikiGenerator {
729
842
 
730
843
  // 关键源文件名(帮助 AI 推断模块功能)
731
844
  if (moduleFiles.length > 0) {
732
- const keyFiles = moduleFiles.slice(0, 25).map(f => path.basename(f));
845
+ const keyFiles = moduleFiles.slice(0, 25).map((f) => path.basename(f));
733
846
  parts.push(`### 关键源文件: ${keyFiles.join(', ')}`);
734
847
  parts.push('');
735
848
  }
736
849
 
737
850
  // 相关 recipes
738
- const related = knowledgeInfo.recipes.filter(r => {
851
+ const related = knowledgeInfo.recipes.filter((r) => {
739
852
  const json = r.toJSON ? r.toJSON() : r;
740
- return json.moduleName === target.name || json.tags?.includes(target.name) || json.title?.includes(target.name);
853
+ return (
854
+ json.moduleName === target.name ||
855
+ json.tags?.includes(target.name) ||
856
+ json.title?.includes(target.name)
857
+ );
741
858
  });
742
859
  if (related.length > 0) {
743
860
  parts.push(`### 相关知识条目 (${related.length})`);
744
861
  for (const r of related.slice(0, 10)) {
745
862
  const json = r.toJSON ? r.toJSON() : r;
746
863
  parts.push(`- ${json.title}: ${json.description || ''}`);
747
- if (json.reasoning?.whyStandard) parts.push(` 为什么: ${json.reasoning.whyStandard}`);
864
+ if (json.reasoning?.whyStandard) {
865
+ parts.push(` 为什么: ${json.reasoning.whyStandard}`);
866
+ }
748
867
  }
749
868
  parts.push('');
750
869
  }
751
870
 
752
871
  parts.push('要求: 撰写模块深度分析文档。');
753
- parts.push('包含: 模块职责说明(从文件名和类名推断功能意图)、核心类型分析(不是简单罗列而是解释每个类的角色)、依赖关系分析、设计模式识别。');
872
+ parts.push(
873
+ '包含: 模块职责说明(从文件名和类名推断功能意图)、核心类型分析(不是简单罗列而是解释每个类的角色)、依赖关系分析、设计模式识别。'
874
+ );
754
875
  parts.push('如果能推断出数据流或协作关系,请用 Mermaid 图表展示。');
755
876
  break;
756
877
  }
@@ -759,16 +880,26 @@ export class WikiGenerator {
759
880
  parts.push('## 任务: 撰写快速上手指南');
760
881
  parts.push('');
761
882
 
762
- if (projectInfo.hasPackageSwift) parts.push('构建系统: Swift Package Manager');
763
- if (projectInfo.hasPodfile) parts.push('构建系统: CocoaPods');
764
- if (projectInfo.hasXcodeproj) parts.push('构建系统: Xcode Project');
883
+ if (projectInfo.hasPackageSwift) {
884
+ parts.push('构建系统: Swift Package Manager');
885
+ }
886
+ if (projectInfo.hasPodfile) {
887
+ parts.push('构建系统: CocoaPods');
888
+ }
889
+ if (projectInfo.hasXcodeproj) {
890
+ parts.push('构建系统: Xcode Project');
891
+ }
765
892
  parts.push('');
766
893
 
767
894
  if (spmInfo.targets.length > 0) {
768
- const mainTargets = spmInfo.targets.filter(t => t.type !== 'test');
769
- const testTargets = spmInfo.targets.filter(t => t.type === 'test');
770
- if (mainTargets.length > 0) parts.push(`主要 Target: ${mainTargets.map(t => t.name).join(', ')}`);
771
- if (testTargets.length > 0) parts.push(`测试 Target: ${testTargets.map(t => t.name).join(', ')}`);
895
+ const mainTargets = spmInfo.targets.filter((t) => t.type !== 'test');
896
+ const testTargets = spmInfo.targets.filter((t) => t.type === 'test');
897
+ if (mainTargets.length > 0) {
898
+ parts.push(`主要 Target: ${mainTargets.map((t) => t.name).join(', ')}`);
899
+ }
900
+ if (testTargets.length > 0) {
901
+ parts.push(`测试 Target: ${testTargets.map((t) => t.name).join(', ')}`);
902
+ }
772
903
  parts.push('');
773
904
  }
774
905
 
@@ -778,7 +909,9 @@ export class WikiGenerator {
778
909
  }
779
910
 
780
911
  parts.push('要求: 撰写开发者快速上手指南。');
781
- parts.push('包含: 环境要求、项目获取、依赖安装、构建步骤(具体命令)、运行测试、项目目录结构说明。');
912
+ parts.push(
913
+ '包含: 环境要求、项目获取、依赖安装、构建步骤(具体命令)、运行测试、项目目录结构说明。'
914
+ );
782
915
  parts.push('语句清晰,步骤明确,适合新人阅读。');
783
916
  break;
784
917
  }
@@ -791,7 +924,9 @@ export class WikiGenerator {
791
924
  for (const r of knowledgeInfo.recipes) {
792
925
  const json = r.toJSON ? r.toJSON() : r;
793
926
  const cat = json.category || 'Other';
794
- if (!groups[cat]) groups[cat] = [];
927
+ if (!groups[cat]) {
928
+ groups[cat] = [];
929
+ }
795
930
  groups[cat].push(json);
796
931
  }
797
932
 
@@ -799,15 +934,23 @@ export class WikiGenerator {
799
934
  parts.push(`### ${cat} (${items.length} 条)`);
800
935
  for (const item of items.slice(0, 8)) {
801
936
  parts.push(`- ${item.title}: ${item.description || 'N/A'}`);
802
- if (item.doClause) parts.push(` 应当: ${item.doClause}`);
803
- if (item.dontClause) parts.push(` 避免: ${item.dontClause}`);
804
- if (item.content?.pattern) parts.push(` 代码片段: ${item.content.pattern.slice(0, 200)}`);
937
+ if (item.doClause) {
938
+ parts.push(` 应当: ${item.doClause}`);
939
+ }
940
+ if (item.dontClause) {
941
+ parts.push(` 避免: ${item.dontClause}`);
942
+ }
943
+ if (item.content?.pattern) {
944
+ parts.push(` 代码片段: ${item.content.pattern.slice(0, 200)}`);
945
+ }
805
946
  }
806
947
  parts.push('');
807
948
  }
808
949
 
809
950
  parts.push('要求: 撰写代码模式文档。对每个分类进行总结分析,解释模式的意义和应用场景。');
810
- parts.push('不要只列出条目 — 为每个分类写一段总结,解释该类模式的整体意图。附带代码示例(从数据中取)。');
951
+ parts.push(
952
+ '不要只列出条目 — 为每个分类写一段总结,解释该类模式的整体意图。附带代码示例(从数据中取)。'
953
+ );
811
954
  break;
812
955
  }
813
956
 
@@ -818,10 +961,18 @@ export class WikiGenerator {
818
961
 
819
962
  for (const item of pd.recipes) {
820
963
  parts.push(`### ${item.title}`);
821
- if (item.description) parts.push(`描述: ${item.description}`);
822
- if (item.doClause) parts.push(`应当: ${item.doClause}`);
823
- if (item.dontClause) parts.push(`避免: ${item.dontClause}`);
824
- if (item.reasoning?.whyStandard) parts.push(`原因: ${item.reasoning.whyStandard}`);
964
+ if (item.description) {
965
+ parts.push(`描述: ${item.description}`);
966
+ }
967
+ if (item.doClause) {
968
+ parts.push(`应当: ${item.doClause}`);
969
+ }
970
+ if (item.dontClause) {
971
+ parts.push(`避免: ${item.dontClause}`);
972
+ }
973
+ if (item.reasoning?.whyStandard) {
974
+ parts.push(`原因: ${item.reasoning.whyStandard}`);
975
+ }
825
976
  if (item.content?.pattern) {
826
977
  parts.push('代码:');
827
978
  parts.push('```');
@@ -832,7 +983,9 @@ export class WikiGenerator {
832
983
  }
833
984
 
834
985
  parts.push('要求: 撰写该分类的详细代码模式文档。');
835
- parts.push('先写一段总结性概述,然后对每个模式做分析,解释为什么要遵循,给出正确和错误的对比示例。');
986
+ parts.push(
987
+ '先写一段总结性概述,然后对每个模式做分析,解释为什么要遵循,给出正确和错误的对比示例。'
988
+ );
836
989
  break;
837
990
  }
838
991
 
@@ -847,9 +1000,13 @@ export class WikiGenerator {
847
1000
  }
848
1001
  }
849
1002
  parts.push('');
850
- parts.push(`总计: ${astInfo.protocols.length} 个协议, ${astInfo.classes.length} 个类/结构体`);
1003
+ parts.push(
1004
+ `总计: ${astInfo.protocols.length} 个协议, ${astInfo.classes.length} 个类/结构体`
1005
+ );
851
1006
  parts.push('');
852
- parts.push('要求: 撰写协议参考文档。按模块分组,分析每个协议的用途和意义,描述协议之间的关系和设计意图。');
1007
+ parts.push(
1008
+ '要求: 撰写协议参考文档。按模块分组,分析每个协议的用途和意义,描述协议之间的关系和设计意图。'
1009
+ );
853
1010
  break;
854
1011
  }
855
1012
  }
@@ -866,13 +1023,26 @@ export class WikiGenerator {
866
1023
 
867
1024
  switch (topic.type) {
868
1025
  case 'overview':
869
- return this._renderIndex(projectInfo, astInfo, spmInfo, knowledgeInfo, isZh, topic._allTopics);
1026
+ return this._renderIndex(
1027
+ projectInfo,
1028
+ astInfo,
1029
+ spmInfo,
1030
+ knowledgeInfo,
1031
+ isZh,
1032
+ topic._allTopics
1033
+ );
870
1034
  case 'architecture':
871
1035
  return this._renderArchitecture(projectInfo, astInfo, spmInfo, isZh);
872
1036
  case 'getting-started':
873
1037
  return this._renderGettingStarted(projectInfo, spmInfo, astInfo, isZh);
874
1038
  case 'module':
875
- return this._renderModule(topic._moduleData.target, astInfo, knowledgeInfo, isZh, projectInfo);
1039
+ return this._renderModule(
1040
+ topic._moduleData.target,
1041
+ astInfo,
1042
+ knowledgeInfo,
1043
+ isZh,
1044
+ projectInfo
1045
+ );
876
1046
  case 'patterns':
877
1047
  return this._renderPatterns(knowledgeInfo, isZh);
878
1048
  case 'pattern-category':
@@ -902,37 +1072,49 @@ export class WikiGenerator {
902
1072
  lines.push('');
903
1073
 
904
1074
  const types = [];
905
- if (project.hasPackageSwift) types.push('SPM (Swift Package Manager)');
906
- if (project.hasPodfile) types.push('CocoaPods');
907
- if (project.hasXcodeproj) types.push('Xcode Project');
1075
+ if (project.hasPackageSwift) {
1076
+ types.push('SPM (Swift Package Manager)');
1077
+ }
1078
+ if (project.hasPodfile) {
1079
+ types.push('CocoaPods');
1080
+ }
1081
+ if (project.hasXcodeproj) {
1082
+ types.push('Xcode Project');
1083
+ }
908
1084
 
909
1085
  const overview = ast.overview || {};
910
- const mainTargets = spm.targets.filter(t => t.type !== 'test');
911
- const testTargets = spm.targets.filter(t => t.type === 'test');
1086
+ const mainTargets = spm.targets.filter((t) => t.type !== 'test');
1087
+ const testTargets = spm.targets.filter((t) => t.type === 'test');
912
1088
 
913
1089
  if (isZh) {
914
- lines.push(`**${project.name}** 是一个 ${types.join(' + ') || 'iOS'} 项目,`
915
- + `包含 ${project.sourceFiles.length} 个源文件`
916
- + (overview.totalClasses ? `、${overview.totalClasses} 个类/结构体` : '')
917
- + (overview.totalProtocols ? `、${overview.totalProtocols} 个协议` : '')
918
- + `。`
1090
+ lines.push(
1091
+ `**${project.name}** 是一个 ${types.join(' + ') || 'iOS'} 项目,` +
1092
+ `包含 ${project.sourceFiles.length} 个源文件` +
1093
+ (overview.totalClasses ? `、${overview.totalClasses} 个类/结构体` : '') +
1094
+ (overview.totalProtocols ? `、${overview.totalProtocols} 个协议` : '') +
1095
+ `。`
919
1096
  );
920
1097
  if (mainTargets.length > 0) {
921
- lines.push(`项目由 ${mainTargets.length} 个功能模块组成`
922
- + (testTargets.length > 0 ? `,配备 ${testTargets.length} 个测试模块` : '')
923
- + `。`);
1098
+ lines.push(
1099
+ `项目由 ${mainTargets.length} 个功能模块组成` +
1100
+ (testTargets.length > 0 ? `,配备 ${testTargets.length} 个测试模块` : '') +
1101
+ `。`
1102
+ );
924
1103
  }
925
1104
  } else {
926
- lines.push(`**${project.name}** is a ${types.join(' + ') || 'iOS'} project `
927
- + `containing ${project.sourceFiles.length} source files`
928
- + (overview.totalClasses ? `, ${overview.totalClasses} classes/structs` : '')
929
- + (overview.totalProtocols ? `, ${overview.totalProtocols} protocols` : '')
930
- + `.`
1105
+ lines.push(
1106
+ `**${project.name}** is a ${types.join(' + ') || 'iOS'} project ` +
1107
+ `containing ${project.sourceFiles.length} source files` +
1108
+ (overview.totalClasses ? `, ${overview.totalClasses} classes/structs` : '') +
1109
+ (overview.totalProtocols ? `, ${overview.totalProtocols} protocols` : '') +
1110
+ `.`
931
1111
  );
932
1112
  if (mainTargets.length > 0) {
933
- lines.push(`The project consists of ${mainTargets.length} functional modules`
934
- + (testTargets.length > 0 ? ` with ${testTargets.length} test modules` : '')
935
- + `.`);
1113
+ lines.push(
1114
+ `The project consists of ${mainTargets.length} functional modules` +
1115
+ (testTargets.length > 0 ? ` with ${testTargets.length} test modules` : '') +
1116
+ `.`
1117
+ );
936
1118
  }
937
1119
  }
938
1120
  lines.push('');
@@ -941,15 +1123,21 @@ export class WikiGenerator {
941
1123
  if (spm.targets.length > 0) {
942
1124
  lines.push(`## ${isZh ? '模块总览' : 'Module Overview'}`);
943
1125
  lines.push('');
944
- lines.push(`| ${isZh ? '模块' : 'Module'} | ${isZh ? '类型' : 'Type'} | ${isZh ? '源文件' : 'Files'} | ${isZh ? '类数' : 'Classes'} | ${isZh ? '协议数' : 'Protocols'} |`);
1126
+ lines.push(
1127
+ `| ${isZh ? '模块' : 'Module'} | ${isZh ? '类型' : 'Type'} | ${isZh ? '源文件' : 'Files'} | ${isZh ? '类数' : 'Classes'} | ${isZh ? '协议数' : 'Protocols'} |`
1128
+ );
945
1129
  lines.push('|--------|------|--------|--------|----------|');
946
1130
  for (const t of spm.targets) {
947
1131
  const moduleFiles = this._getModuleSourceFiles(t, project);
948
1132
  const classCount = ast.classNamesByModule?.[t.name]?.length || 0;
949
1133
  const protoCount = ast.protocolNamesByModule?.[t.name]?.length || 0;
950
- const hasDoc = allTopics?.some(tp => tp.type === 'module' && tp._moduleData?.target.name === t.name);
1134
+ const hasDoc = allTopics?.some(
1135
+ (tp) => tp.type === 'module' && tp._moduleData?.target.name === t.name
1136
+ );
951
1137
  const nameCol = hasDoc ? `[${t.name}](modules/${_slug(t.name)}.md)` : t.name;
952
- lines.push(`| ${nameCol} | ${t.type || 'target'} | ${moduleFiles.length || '-'} | ${classCount || '-'} | ${protoCount || '-'} |`);
1138
+ lines.push(
1139
+ `| ${nameCol} | ${t.type || 'target'} | ${moduleFiles.length || '-'} | ${classCount || '-'} | ${protoCount || '-'} |`
1140
+ );
953
1141
  }
954
1142
  lines.push('');
955
1143
  }
@@ -958,7 +1146,9 @@ export class WikiGenerator {
958
1146
  lines.push(`## ${isZh ? '技术栈' : 'Tech Stack'}`);
959
1147
  lines.push('');
960
1148
  if (project.languages && Object.keys(project.languages).length > 0) {
961
- lines.push(`| ${isZh ? '语言' : 'Language'} | ${isZh ? '文件数' : 'Files'} | ${isZh ? '占比' : 'Share'} |`);
1149
+ lines.push(
1150
+ `| ${isZh ? '语言' : 'Language'} | ${isZh ? '文件数' : 'Files'} | ${isZh ? '占比' : 'Share'} |`
1151
+ );
962
1152
  lines.push('|--------|-------|------|');
963
1153
  const total = Object.values(project.languages).reduce((a, b) => a + b, 0);
964
1154
  for (const [lang, count] of Object.entries(project.languages).sort((a, b) => b[1] - a[1])) {
@@ -974,15 +1164,25 @@ export class WikiGenerator {
974
1164
  lines.push(`| ${isZh ? '指标' : 'Metric'} | ${isZh ? '数量' : 'Count'} |`);
975
1165
  lines.push('|--------|-------|');
976
1166
  lines.push(`| ${isZh ? '源文件数' : 'Source Files'} | ${project.sourceFiles.length} |`);
977
- if (overview.totalClasses) lines.push(`| ${isZh ? '类/结构体' : 'Classes/Structs'} | ${overview.totalClasses} |`);
978
- if (overview.totalProtocols) lines.push(`| ${isZh ? '协议' : 'Protocols'} | ${overview.totalProtocols} |`);
979
- if (overview.totalMethods) lines.push(`| ${isZh ? '方法总数' : 'Methods'} | ${overview.totalMethods} |`);
980
- if (spm.targets.length > 0) lines.push(`| SPM Targets | ${spm.targets.length} |`);
981
- if (knowledge.recipes.length > 0) lines.push(`| ${isZh ? '知识库条目' : 'KB Recipes'} | ${knowledge.recipes.length} |`);
1167
+ if (overview.totalClasses) {
1168
+ lines.push(`| ${isZh ? '类/结构体' : 'Classes/Structs'} | ${overview.totalClasses} |`);
1169
+ }
1170
+ if (overview.totalProtocols) {
1171
+ lines.push(`| ${isZh ? '协议' : 'Protocols'} | ${overview.totalProtocols} |`);
1172
+ }
1173
+ if (overview.totalMethods) {
1174
+ lines.push(`| ${isZh ? '方法总数' : 'Methods'} | ${overview.totalMethods} |`);
1175
+ }
1176
+ if (spm.targets.length > 0) {
1177
+ lines.push(`| SPM Targets | ${spm.targets.length} |`);
1178
+ }
1179
+ if (knowledge.recipes.length > 0) {
1180
+ lines.push(`| ${isZh ? '知识库条目' : 'KB Recipes'} | ${knowledge.recipes.length} |`);
1181
+ }
982
1182
  lines.push('');
983
1183
 
984
1184
  // ── 文档导航 (动态,基于实际生成的主题) ──
985
- const navTopics = (allTopics || []).filter(t => t.type !== 'overview');
1185
+ const navTopics = (allTopics || []).filter((t) => t.type !== 'overview');
986
1186
  if (navTopics.length > 0) {
987
1187
  lines.push('---');
988
1188
  lines.push('');
@@ -1017,7 +1217,10 @@ export class WikiGenerator {
1017
1217
  for (const target of spm.targets) {
1018
1218
  const sid = _mermaidId(target.name);
1019
1219
  if (!rendered.has(sid)) {
1020
- const shape = target.type === 'test' ? `${sid}[["${target.name} (Test)"]]` : `${sid}["${target.name}"]`;
1220
+ const shape =
1221
+ target.type === 'test'
1222
+ ? `${sid}[["${target.name} (Test)"]]`
1223
+ : `${sid}["${target.name}"]`;
1021
1224
  lines.push(` ${shape}`);
1022
1225
  rendered.add(sid);
1023
1226
  }
@@ -1083,7 +1286,9 @@ export class WikiGenerator {
1083
1286
  lines.push('```');
1084
1287
  lines.push('');
1085
1288
  }
1086
- } catch { /* non-critical */ }
1289
+ } catch {
1290
+ /* non-critical */
1291
+ }
1087
1292
  }
1088
1293
 
1089
1294
  lines.push(`[← ${isZh ? '返回概述' : 'Back to Overview'}](index.md)`);
@@ -1110,16 +1315,23 @@ export class WikiGenerator {
1110
1315
  lines.push('');
1111
1316
 
1112
1317
  // 推断模块功能 (基于名称和内容)
1113
- const purpose = this._inferModulePurpose(target.name, moduleClasses, moduleProtocols, moduleFiles);
1318
+ const purpose = this._inferModulePurpose(
1319
+ target.name,
1320
+ moduleClasses,
1321
+ moduleProtocols,
1322
+ moduleFiles
1323
+ );
1114
1324
  if (purpose) {
1115
- lines.push(isZh
1116
- ? `**${target.name}** ${purpose.zh},包含 ${moduleFiles.length} 个源文件、${moduleClasses.length} 个类/结构体${moduleProtocols.length > 0 ? `、${moduleProtocols.length} 个协议` : ''}。`
1117
- : `**${target.name}** ${purpose.en}, containing ${moduleFiles.length} source files, ${moduleClasses.length} classes/structs${moduleProtocols.length > 0 ? `, ${moduleProtocols.length} protocols` : ''}.`
1325
+ lines.push(
1326
+ isZh
1327
+ ? `**${target.name}** ${purpose.zh},包含 ${moduleFiles.length} 个源文件、${moduleClasses.length} 个类/结构体${moduleProtocols.length > 0 ? `、${moduleProtocols.length} 个协议` : ''}。`
1328
+ : `**${target.name}** ${purpose.en}, containing ${moduleFiles.length} source files, ${moduleClasses.length} classes/structs${moduleProtocols.length > 0 ? `, ${moduleProtocols.length} protocols` : ''}.`
1118
1329
  );
1119
1330
  } else {
1120
- lines.push(isZh
1121
- ? `**${target.name}** 是项目中的一个 ${target.type || 'target'} 模块,包含 ${moduleFiles.length} 个源文件、${moduleClasses.length} 个类/结构体。`
1122
- : `**${target.name}** is a ${target.type || 'target'} module in the project, containing ${moduleFiles.length} source files and ${moduleClasses.length} classes/structs.`
1331
+ lines.push(
1332
+ isZh
1333
+ ? `**${target.name}** 是项目中的一个 ${target.type || 'target'} 模块,包含 ${moduleFiles.length} 个源文件、${moduleClasses.length} 个类/结构体。`
1334
+ : `**${target.name}** is a ${target.type || 'target'} module in the project, containing ${moduleFiles.length} source files and ${moduleClasses.length} classes/structs.`
1123
1335
  );
1124
1336
  }
1125
1337
  lines.push('');
@@ -1128,21 +1340,34 @@ export class WikiGenerator {
1128
1340
  lines.push(`| ${isZh ? '属性' : 'Property'} | ${isZh ? '值' : 'Value'} |`);
1129
1341
  lines.push('|--------|------|');
1130
1342
  lines.push(`| ${isZh ? '类型' : 'Type'} | ${target.type || 'target'} |`);
1131
- if (target.packageName) lines.push(`| ${isZh ? '所属包' : 'Package'} | ${target.packageName} |`);
1132
- if (target.path || target.info?.path) lines.push(`| ${isZh ? '路径' : 'Path'} | \`${target.path || target.info.path}\` |`);
1133
- if (moduleFiles.length > 0) lines.push(`| ${isZh ? '源文件数' : 'Source Files'} | ${moduleFiles.length} |`);
1134
- if (moduleClasses.length > 0) lines.push(`| ${isZh ? '类/结构体' : 'Classes/Structs'} | ${moduleClasses.length} |`);
1135
- if (moduleProtocols.length > 0) lines.push(`| ${isZh ? '协议' : 'Protocols'} | ${moduleProtocols.length} |`);
1136
- if (deps.length > 0) lines.push(`| ${isZh ? '依赖数' : 'Dependencies'} | ${deps.length} |`);
1343
+ if (target.packageName) {
1344
+ lines.push(`| ${isZh ? '所属包' : 'Package'} | ${target.packageName} |`);
1345
+ }
1346
+ if (target.path || target.info?.path) {
1347
+ lines.push(`| ${isZh ? '路径' : 'Path'} | \`${target.path || target.info.path}\` |`);
1348
+ }
1349
+ if (moduleFiles.length > 0) {
1350
+ lines.push(`| ${isZh ? '源文件数' : 'Source Files'} | ${moduleFiles.length} |`);
1351
+ }
1352
+ if (moduleClasses.length > 0) {
1353
+ lines.push(`| ${isZh ? '类/结构体' : 'Classes/Structs'} | ${moduleClasses.length} |`);
1354
+ }
1355
+ if (moduleProtocols.length > 0) {
1356
+ lines.push(`| ${isZh ? '协议' : 'Protocols'} | ${moduleProtocols.length} |`);
1357
+ }
1358
+ if (deps.length > 0) {
1359
+ lines.push(`| ${isZh ? '依赖数' : 'Dependencies'} | ${deps.length} |`);
1360
+ }
1137
1361
  lines.push('');
1138
1362
 
1139
1363
  // ── 依赖 ──
1140
1364
  if (deps.length > 0) {
1141
1365
  lines.push(`## ${isZh ? '依赖关系' : 'Dependencies'}`);
1142
1366
  lines.push('');
1143
- lines.push(isZh
1144
- ? `${target.name} 依赖以下 ${deps.length} 个模块:`
1145
- : `${target.name} depends on ${deps.length} module(s):`
1367
+ lines.push(
1368
+ isZh
1369
+ ? `${target.name} 依赖以下 ${deps.length} 个模块:`
1370
+ : `${target.name} depends on ${deps.length} module(s):`
1146
1371
  );
1147
1372
  lines.push('');
1148
1373
  for (const dep of deps) {
@@ -1160,9 +1385,10 @@ export class WikiGenerator {
1160
1385
  if (moduleProtocols.length > 0) {
1161
1386
  lines.push(`### ${isZh ? '协议' : 'Protocols'} (${moduleProtocols.length})`);
1162
1387
  lines.push('');
1163
- lines.push(isZh
1164
- ? `${target.name} 定义了 ${moduleProtocols.length} 个协议,用于规范模块的接口边界:`
1165
- : `${target.name} defines ${moduleProtocols.length} protocols establishing the module's interface contracts:`
1388
+ lines.push(
1389
+ isZh
1390
+ ? `${target.name} 定义了 ${moduleProtocols.length} 个协议,用于规范模块的接口边界:`
1391
+ : `${target.name} defines ${moduleProtocols.length} protocols establishing the module's interface contracts:`
1166
1392
  );
1167
1393
  lines.push('');
1168
1394
  const sorted = [...moduleProtocols].sort();
@@ -1170,7 +1396,9 @@ export class WikiGenerator {
1170
1396
  lines.push(`- \`${p}\``);
1171
1397
  }
1172
1398
  if (sorted.length > 20) {
1173
- lines.push(`- ... ${isZh ? `还有 ${sorted.length - 20} 个` : `and ${sorted.length - 20} more`}`);
1399
+ lines.push(
1400
+ `- ... ${isZh ? `还有 ${sorted.length - 20} 个` : `and ${sorted.length - 20} more`}`
1401
+ );
1174
1402
  }
1175
1403
  lines.push('');
1176
1404
  }
@@ -1183,7 +1411,9 @@ export class WikiGenerator {
1183
1411
  lines.push(`- \`${c}\``);
1184
1412
  }
1185
1413
  if (sorted.length > 30) {
1186
- lines.push(`- ... ${isZh ? `还有 ${sorted.length - 30} 个` : `and ${sorted.length - 30} more`}`);
1414
+ lines.push(
1415
+ `- ... ${isZh ? `还有 ${sorted.length - 30} 个` : `and ${sorted.length - 30} more`}`
1416
+ );
1187
1417
  }
1188
1418
  lines.push('');
1189
1419
  }
@@ -1198,7 +1428,7 @@ export class WikiGenerator {
1198
1428
  const langCount = {};
1199
1429
  for (const f of moduleFiles) {
1200
1430
  const ext = path.extname(f);
1201
- const lang = { '.swift': 'Swift', '.m': 'ObjC', '.h': 'Header', '.mm': 'ObjC++' }[ext] || ext;
1431
+ const lang = LanguageService.displayNameFromExt(ext);
1202
1432
  langCount[lang] = (langCount[lang] || 0) + 1;
1203
1433
  }
1204
1434
 
@@ -1212,27 +1442,36 @@ export class WikiGenerator {
1212
1442
 
1213
1443
  // ── 该模块相关的 Recipes ──
1214
1444
  if (knowledge.recipes.length > 0) {
1215
- const related = knowledge.recipes.filter(r => {
1445
+ const related = knowledge.recipes.filter((r) => {
1216
1446
  const json = r.toJSON ? r.toJSON() : r;
1217
- return json.moduleName === target.name ||
1218
- json.tags?.includes(target.name) ||
1219
- json.title?.includes(target.name);
1447
+ return (
1448
+ json.moduleName === target.name ||
1449
+ json.tags?.includes(target.name) ||
1450
+ json.title?.includes(target.name)
1451
+ );
1220
1452
  });
1221
1453
  if (related.length > 0) {
1222
1454
  lines.push(`## ${isZh ? '相关知识条目' : 'Related Recipes'}`);
1223
1455
  lines.push('');
1224
- lines.push(isZh
1225
- ? `团队知识库中有 ${related.length} 条与 ${target.name} 相关的条目:`
1226
- : `The team knowledge base contains ${related.length} entries related to ${target.name}:`
1456
+ lines.push(
1457
+ isZh
1458
+ ? `团队知识库中有 ${related.length} 条与 ${target.name} 相关的条目:`
1459
+ : `The team knowledge base contains ${related.length} entries related to ${target.name}:`
1227
1460
  );
1228
1461
  lines.push('');
1229
1462
  for (const r of related) {
1230
1463
  const json = r.toJSON ? r.toJSON() : r;
1231
1464
  lines.push(`### ${json.title}`);
1232
1465
  lines.push('');
1233
- if (json.description) lines.push(json.description);
1234
- if (json.doClause) lines.push(`\n**${isZh ? '✅ 应当' : '✅ Do'}**: ${json.doClause}`);
1235
- if (json.dontClause) lines.push(`**${isZh ? '❌ 避免' : "❌ Don't"}**: ${json.dontClause}`);
1466
+ if (json.description) {
1467
+ lines.push(json.description);
1468
+ }
1469
+ if (json.doClause) {
1470
+ lines.push(`\n**${isZh ? '✅ 应当' : '✅ Do'}**: ${json.doClause}`);
1471
+ }
1472
+ if (json.dontClause) {
1473
+ lines.push(`**${isZh ? '❌ 避免' : "❌ Don't"}**: ${json.dontClause}`);
1474
+ }
1236
1475
  lines.push('');
1237
1476
  }
1238
1477
  }
@@ -1249,33 +1488,81 @@ export class WikiGenerator {
1249
1488
  */
1250
1489
  _inferModulePurpose(name, classes, protocols, files) {
1251
1490
  const lower = name.toLowerCase();
1252
- const fileNames = files.map(f => path.basename(f).toLowerCase());
1491
+ const _fileNames = files.map((f) => path.basename(f).toLowerCase());
1253
1492
 
1254
1493
  // 常见模块功能推断规则
1255
1494
  const rules = [
1256
- { match: /network|http|api|client|request|fetch/i, zh: '负责网络通信和 API 调用', en: 'handles network communication and API calls' },
1257
- { match: /ui|view|component|widget|screen|page/i, zh: '提供用户界面组件', en: 'provides user interface components' },
1258
- { match: /model|entity|domain|data/i, zh: '定义数据模型和领域实体', en: 'defines data models and domain entities' },
1259
- { match: /storage|database|cache|persist|core\s*data|realm/i, zh: '负责数据持久化和存储', en: 'manages data persistence and storage' },
1260
- { match: /auth|login|session|token|credential/i, zh: '处理认证授权和会话管理', en: 'handles authentication and session management' },
1261
- { match: /util|helper|extension|common|shared|foundation/i, zh: '提供公共工具类和扩展方法', en: 'provides common utilities and extensions' },
1495
+ {
1496
+ match: /network|http|api|client|request|fetch/i,
1497
+ zh: '负责网络通信和 API 调用',
1498
+ en: 'handles network communication and API calls',
1499
+ },
1500
+ {
1501
+ match: /ui|view|component|widget|screen|page/i,
1502
+ zh: '提供用户界面组件',
1503
+ en: 'provides user interface components',
1504
+ },
1505
+ {
1506
+ match: /model|entity|domain|data/i,
1507
+ zh: '定义数据模型和领域实体',
1508
+ en: 'defines data models and domain entities',
1509
+ },
1510
+ {
1511
+ match: /storage|database|cache|persist|core\s*data|realm/i,
1512
+ zh: '负责数据持久化和存储',
1513
+ en: 'manages data persistence and storage',
1514
+ },
1515
+ {
1516
+ match: /auth|login|session|token|credential/i,
1517
+ zh: '处理认证授权和会话管理',
1518
+ en: 'handles authentication and session management',
1519
+ },
1520
+ {
1521
+ match: /util|helper|extension|common|shared|foundation/i,
1522
+ zh: '提供公共工具类和扩展方法',
1523
+ en: 'provides common utilities and extensions',
1524
+ },
1262
1525
  { match: /test|spec|mock/i, zh: '包含单元测试和 Mock', en: 'contains unit tests and mocks' },
1263
- { match: /router|navigation|coordinator|flow/i, zh: '管理页面路由和导航流', en: 'manages page routing and navigation flow' },
1264
- { match: /config|setting|preference|env/i, zh: '管理应用配置和环境设置', en: 'manages app configuration and environment settings' },
1265
- { match: /log|analytics|track|monitor/i, zh: '提供日志记录和数据分析能力', en: 'provides logging and analytics capabilities' },
1266
- { match: /media|image|video|audio|player/i, zh: '处理多媒体资源', en: 'handles multimedia resources' },
1267
- { match: /service|manager|provider/i, zh: '提供核心业务服务', en: 'provides core business services' },
1526
+ {
1527
+ match: /router|navigation|coordinator|flow/i,
1528
+ zh: '管理页面路由和导航流',
1529
+ en: 'manages page routing and navigation flow',
1530
+ },
1531
+ {
1532
+ match: /config|setting|preference|env/i,
1533
+ zh: '管理应用配置和环境设置',
1534
+ en: 'manages app configuration and environment settings',
1535
+ },
1536
+ {
1537
+ match: /log|analytics|track|monitor/i,
1538
+ zh: '提供日志记录和数据分析能力',
1539
+ en: 'provides logging and analytics capabilities',
1540
+ },
1541
+ {
1542
+ match: /media|image|video|audio|player/i,
1543
+ zh: '处理多媒体资源',
1544
+ en: 'handles multimedia resources',
1545
+ },
1546
+ {
1547
+ match: /service|manager|provider/i,
1548
+ zh: '提供核心业务服务',
1549
+ en: 'provides core business services',
1550
+ },
1268
1551
  ];
1269
1552
 
1270
1553
  // 先按模块名匹配
1271
1554
  for (const rule of rules) {
1272
- if (rule.match.test(lower)) return rule;
1555
+ if (rule.match.test(lower)) {
1556
+ return rule;
1557
+ }
1273
1558
  }
1274
1559
 
1275
1560
  // 再按类名匹配
1276
1561
  const classStr = classes.join(' ');
1277
1562
  for (const rule of rules) {
1278
- if (rule.match.test(classStr)) return rule;
1563
+ if (rule.match.test(classStr)) {
1564
+ return rule;
1565
+ }
1279
1566
  }
1280
1567
 
1281
1568
  return null;
@@ -1296,16 +1583,19 @@ export class WikiGenerator {
1296
1583
  for (const r of knowledge.recipes) {
1297
1584
  const json = r.toJSON ? r.toJSON() : r;
1298
1585
  const cat = json.category || 'Other';
1299
- if (!groups[cat]) groups[cat] = [];
1586
+ if (!groups[cat]) {
1587
+ groups[cat] = [];
1588
+ }
1300
1589
  groups[cat].push(json);
1301
1590
  }
1302
1591
 
1303
1592
  // 总结
1304
1593
  const totalRecipes = knowledge.recipes.length;
1305
1594
  const catCount = Object.keys(groups).length;
1306
- lines.push(isZh
1307
- ? `本项目团队在 ${catCount} 个分类下共沉淀了 **${totalRecipes}** 条代码模式和最佳实践。以下按分类进行展示和分析。`
1308
- : `The team has accumulated **${totalRecipes}** code patterns across ${catCount} categories. Below they are organized and analyzed by category.`
1595
+ lines.push(
1596
+ isZh
1597
+ ? `本项目团队在 ${catCount} 个分类下共沉淀了 **${totalRecipes}** 条代码模式和最佳实践。以下按分类进行展示和分析。`
1598
+ : `The team has accumulated **${totalRecipes}** code patterns across ${catCount} categories. Below they are organized and analyzed by category.`
1309
1599
  );
1310
1600
  lines.push('');
1311
1601
 
@@ -1314,9 +1604,10 @@ export class WikiGenerator {
1314
1604
  lines.push('');
1315
1605
 
1316
1606
  // 分类概述
1317
- lines.push(isZh
1318
- ? `${cat} 分类包含 ${items.length} 条规则,覆盖了该领域的核心规范。`
1319
- : `The ${cat} category contains ${items.length} rules covering core conventions in this area.`
1607
+ lines.push(
1608
+ isZh
1609
+ ? `${cat} 分类包含 ${items.length} 条规则,覆盖了该领域的核心规范。`
1610
+ : `The ${cat} category contains ${items.length} rules covering core conventions in this area.`
1320
1611
  );
1321
1612
  lines.push('');
1322
1613
 
@@ -1328,7 +1619,7 @@ export class WikiGenerator {
1328
1619
  lines.push('');
1329
1620
  }
1330
1621
  if (item.content?.pattern) {
1331
- lines.push('```' + (item.language || 'swift'));
1622
+ lines.push(`\`\`\`${item.language || 'swift'}`);
1332
1623
  lines.push(item.content.pattern);
1333
1624
  lines.push('```');
1334
1625
  lines.push('');
@@ -1387,12 +1678,13 @@ export class WikiGenerator {
1387
1678
  lines.push('```');
1388
1679
  lines.push(`${project.name}/`);
1389
1680
  if (spm.targets.length > 0) {
1390
- const mainTargets = spm.targets.filter(t => t.type !== 'test');
1391
- const testTargets = spm.targets.filter(t => t.type === 'test');
1681
+ const mainTargets = spm.targets.filter((t) => t.type !== 'test');
1682
+ const testTargets = spm.targets.filter((t) => t.type === 'test');
1392
1683
  if (mainTargets.length > 0) {
1393
1684
  lines.push('├── Sources/');
1394
1685
  for (let i = 0; i < mainTargets.length; i++) {
1395
- const prefix = i === mainTargets.length - 1 && testTargets.length === 0 ? '│ └──' : '│ ├──';
1686
+ const prefix =
1687
+ i === mainTargets.length - 1 && testTargets.length === 0 ? '│ └──' : '│ ├──';
1396
1688
  lines.push(`${prefix} ${mainTargets[i].name}/`);
1397
1689
  }
1398
1690
  }
@@ -1404,8 +1696,12 @@ export class WikiGenerator {
1404
1696
  }
1405
1697
  }
1406
1698
  }
1407
- if (project.hasPackageSwift) lines.push('├── Package.swift');
1408
- if (project.hasPodfile) lines.push('├── Podfile');
1699
+ if (project.hasPackageSwift) {
1700
+ lines.push('├── Package.swift');
1701
+ }
1702
+ if (project.hasPodfile) {
1703
+ lines.push('├── Podfile');
1704
+ }
1409
1705
  lines.push('```');
1410
1706
  lines.push('');
1411
1707
 
@@ -1443,15 +1739,22 @@ export class WikiGenerator {
1443
1739
 
1444
1740
  // 模块说明
1445
1741
  if (spm.targets.length > 0) {
1446
- const mainTargets = spm.targets.filter(t => t.type !== 'test');
1742
+ const mainTargets = spm.targets.filter((t) => t.type !== 'test');
1447
1743
  if (mainTargets.length > 0) {
1448
1744
  lines.push(`## ${isZh ? '核心模块' : 'Core Modules'}`);
1449
1745
  lines.push('');
1450
- lines.push(`| ${isZh ? '模块' : 'Module'} | ${isZh ? '类型' : 'Type'} | ${isZh ? '类型数' : 'Types'} | ${isZh ? '说明' : 'Description'} |`);
1746
+ lines.push(
1747
+ `| ${isZh ? '模块' : 'Module'} | ${isZh ? '类型' : 'Type'} | ${isZh ? '类型数' : 'Types'} | ${isZh ? '说明' : 'Description'} |`
1748
+ );
1451
1749
  lines.push('|--------|------|--------|------|');
1452
1750
  for (const t of mainTargets) {
1453
1751
  const cls = (ast.classNamesByModule?.[t.name] || []).length;
1454
- const purpose = this._inferModulePurpose(t.name, ast.classNamesByModule?.[t.name] || [], ast.protocolNamesByModule?.[t.name] || [], []);
1752
+ const purpose = this._inferModulePurpose(
1753
+ t.name,
1754
+ ast.classNamesByModule?.[t.name] || [],
1755
+ ast.protocolNamesByModule?.[t.name] || [],
1756
+ []
1757
+ );
1455
1758
  const desc = purpose ? (isZh ? purpose.zh : purpose.en) : '-';
1456
1759
  lines.push(`| ${t.name} | ${t.type || 'library'} | ${cls} | ${desc} |`);
1457
1760
  }
@@ -1477,9 +1780,10 @@ export class WikiGenerator {
1477
1780
  ];
1478
1781
 
1479
1782
  // 分类概述
1480
- lines.push(isZh
1481
- ? `本文档收录了 ${category} 分类下的 ${recipes.length} 条代码模式和规范,这些规则由团队在开发实践中总结沉淀。`
1482
- : `This document covers ${recipes.length} code patterns and conventions in the ${category} category, distilled from team development practices.`
1783
+ lines.push(
1784
+ isZh
1785
+ ? `本文档收录了 ${category} 分类下的 ${recipes.length} 条代码模式和规范,这些规则由团队在开发实践中总结沉淀。`
1786
+ : `This document covers ${recipes.length} code patterns and conventions in the ${category} category, distilled from team development practices.`
1483
1787
  );
1484
1788
  lines.push('');
1485
1789
 
@@ -1499,7 +1803,7 @@ export class WikiGenerator {
1499
1803
  lines.push('');
1500
1804
  }
1501
1805
  if (item.content?.pattern) {
1502
- lines.push('```' + (item.language || 'swift'));
1806
+ lines.push(`\`\`\`${item.language || 'text'}`);
1503
1807
  lines.push(item.content.pattern);
1504
1808
  lines.push('```');
1505
1809
  lines.push('');
@@ -1526,9 +1830,10 @@ export class WikiGenerator {
1526
1830
  '',
1527
1831
  ];
1528
1832
 
1529
- lines.push(isZh
1530
- ? `协议(Protocol)定义了类型需要遵循的接口契约。本项目共定义了 ${ast.protocols.length} 个协议,以下按模块分组展示。`
1531
- : `Protocols define interface contracts that types must conform to. This project defines ${ast.protocols.length} protocols, organized by module below.`
1833
+ lines.push(
1834
+ isZh
1835
+ ? `协议(Protocol)定义了类型需要遵循的接口契约。本项目共定义了 ${ast.protocols.length} 个协议,以下按模块分组展示。`
1836
+ : `Protocols define interface contracts that types must conform to. This project defines ${ast.protocols.length} protocols, organized by module below.`
1532
1837
  );
1533
1838
  lines.push('');
1534
1839
 
@@ -1537,12 +1842,15 @@ export class WikiGenerator {
1537
1842
  const grouped = new Set();
1538
1843
 
1539
1844
  for (const [mod, protos] of Object.entries(protoByModule).sort()) {
1540
- if (protos.length === 0) continue;
1845
+ if (protos.length === 0) {
1846
+ continue;
1847
+ }
1541
1848
  lines.push(`## ${mod}`);
1542
1849
  lines.push('');
1543
- lines.push(isZh
1544
- ? `${mod} 模块定义了 ${protos.length} 个协议:`
1545
- : `${mod} module defines ${protos.length} protocols:`
1850
+ lines.push(
1851
+ isZh
1852
+ ? `${mod} 模块定义了 ${protos.length} 个协议:`
1853
+ : `${mod} module defines ${protos.length} protocols:`
1546
1854
  );
1547
1855
  lines.push('');
1548
1856
  for (const p of protos.sort()) {
@@ -1553,7 +1861,7 @@ export class WikiGenerator {
1553
1861
  }
1554
1862
 
1555
1863
  // 未分组的协议
1556
- const ungrouped = ast.protocols.filter(p => !grouped.has(p));
1864
+ const ungrouped = ast.protocols.filter((p) => !grouped.has(p));
1557
1865
  if (ungrouped.length > 0) {
1558
1866
  lines.push(`## ${isZh ? '其他协议' : 'Other Protocols'}`);
1559
1867
  lines.push('');
@@ -1627,10 +1935,16 @@ export class WikiGenerator {
1627
1935
  const isZh = this.options.language === 'zh';
1628
1936
 
1629
1937
  // ── Source 1: Channel D devdocs ──
1630
- const devdocsDir = path.join(this.projectRoot, '.cursor', 'skills', 'autosnippet-devdocs', 'references');
1938
+ const devdocsDir = path.join(
1939
+ this.projectRoot,
1940
+ '.cursor',
1941
+ 'skills',
1942
+ 'autosnippet-devdocs',
1943
+ 'references'
1944
+ );
1631
1945
  if (fs.existsSync(devdocsDir)) {
1632
1946
  this._ensureDir(path.join(this.wikiDir, 'documents'));
1633
- const files = fs.readdirSync(devdocsDir).filter(f => f.endsWith('.md'));
1947
+ const files = fs.readdirSync(devdocsDir).filter((f) => f.endsWith('.md'));
1634
1948
  for (const file of files) {
1635
1949
  try {
1636
1950
  const content = fs.readFileSync(path.join(devdocsDir, file), 'utf-8');
@@ -1638,7 +1952,9 @@ export class WikiGenerator {
1638
1952
  const result = this._writeFile(`documents/${file}`, header + content);
1639
1953
  result.source = 'cursor-devdocs';
1640
1954
  synced.push(result);
1641
- } catch { /* skip */ }
1955
+ } catch {
1956
+ /* skip */
1957
+ }
1642
1958
  }
1643
1959
  }
1644
1960
 
@@ -1654,7 +1970,7 @@ export class WikiGenerator {
1654
1970
  * 为同步目录生成索引页
1655
1971
  */
1656
1972
  _generateSyncIndex(synced, isZh) {
1657
- const docFiles = synced.filter(f => f.path.startsWith('documents/'));
1973
+ const docFiles = synced.filter((f) => f.path.startsWith('documents/'));
1658
1974
 
1659
1975
  if (docFiles.length > 0) {
1660
1976
  const lines = [
@@ -1697,9 +2013,15 @@ export class WikiGenerator {
1697
2013
  // 完全相同 hash → 移除后来的
1698
2014
  if (existing.hash === file.hash) {
1699
2015
  const fullPath = path.join(this.wikiDir, file.path);
1700
- try { fs.unlinkSync(fullPath); } catch { /* skip */ }
2016
+ try {
2017
+ fs.unlinkSync(fullPath);
2018
+ } catch {
2019
+ /* skip */
2020
+ }
1701
2021
  removed.push(file.path);
1702
- logger.info(`[WikiGenerator] Dedup: removed ${file.path} (same hash as ${existing.path})`);
2022
+ logger.info(
2023
+ `[WikiGenerator] Dedup: removed ${file.path} (same hash as ${existing.path})`
2024
+ );
1703
2025
  }
1704
2026
  // hash 不同 → 保留两个(不同目录允许同名)
1705
2027
  } else {
@@ -1710,19 +2032,28 @@ export class WikiGenerator {
1710
2032
  // Layer 2: content hash 碰撞(不同文件名但内容相同)
1711
2033
  const hashMap = new Map(); // hash → first file path
1712
2034
  for (const file of files) {
1713
- if (removed.includes(file.path)) continue;
2035
+ if (removed.includes(file.path)) {
2036
+ continue;
2037
+ }
1714
2038
  if (hashMap.has(file.hash)) {
1715
2039
  const firstPath = hashMap.get(file.hash);
1716
2040
  // 优先保留代码生成的(非 synced)
1717
2041
  const isFirstSynced = firstPath.startsWith('documents/') || firstPath.startsWith('skills/');
1718
- const isCurrentSynced = file.path.startsWith('documents/') || file.path.startsWith('skills/');
2042
+ const isCurrentSynced =
2043
+ file.path.startsWith('documents/') || file.path.startsWith('skills/');
1719
2044
 
1720
2045
  if (isCurrentSynced && !isFirstSynced) {
1721
2046
  // 当前是 synced,first 是 codegen → 删除 synced
1722
2047
  const fullPath = path.join(this.wikiDir, file.path);
1723
- try { fs.unlinkSync(fullPath); } catch { /* skip */ }
2048
+ try {
2049
+ fs.unlinkSync(fullPath);
2050
+ } catch {
2051
+ /* skip */
2052
+ }
1724
2053
  removed.push(file.path);
1725
- logger.info(`[WikiGenerator] Dedup: removed synced ${file.path} (same content as ${firstPath})`);
2054
+ logger.info(
2055
+ `[WikiGenerator] Dedup: removed synced ${file.path} (same content as ${firstPath})`
2056
+ );
1726
2057
  }
1727
2058
  // 其他情况保留两个
1728
2059
  } else {
@@ -1750,16 +2081,21 @@ export class WikiGenerator {
1750
2081
 
1751
2082
  /** 从 CodeEntityGraph 提取继承根节点 */
1752
2083
  _getInheritanceRoots() {
1753
- if (!this.codeEntityGraph) return [];
2084
+ if (!this.codeEntityGraph) {
2085
+ return [];
2086
+ }
1754
2087
  try {
1755
2088
  // 尝试查询继承关系
1756
- const entities = this.codeEntityGraph.queryEntities?.({ entityType: 'class', limit: 50 }) || [];
2089
+ const entities =
2090
+ this.codeEntityGraph.queryEntities?.({ entityType: 'class', limit: 50 }) || [];
1757
2091
  const roots = [];
1758
2092
  for (const e of entities) {
1759
- const parents = this.codeEntityGraph.queryEdges?.({ toId: e.entityId, relation: 'inherits' }) || [];
1760
- const children = this.codeEntityGraph.queryEdges?.({ fromId: e.entityId, relation: 'inherits' }) || [];
2093
+ const _parents =
2094
+ this.codeEntityGraph.queryEdges?.({ toId: e.entityId, relation: 'inherits' }) || [];
2095
+ const children =
2096
+ this.codeEntityGraph.queryEdges?.({ fromId: e.entityId, relation: 'inherits' }) || [];
1761
2097
  if (children.length > 0) {
1762
- roots.push({ name: e.name, children: children.map(c => c.toId || c.to_id) });
2098
+ roots.push({ name: e.name, children: children.map((c) => c.toId || c.to_id) });
1763
2099
  }
1764
2100
  }
1765
2101
  return roots.sort((a, b) => (b.children?.length || 0) - (a.children?.length || 0));
@@ -1771,7 +2107,9 @@ export class WikiGenerator {
1771
2107
  _emit(phase, progress, message) {
1772
2108
  try {
1773
2109
  this.onProgress(phase, progress, message);
1774
- } catch { /* non-critical */ }
2110
+ } catch {
2111
+ /* non-critical */
2112
+ }
1775
2113
  }
1776
2114
 
1777
2115
  _ensureDir(dir) {
@@ -1797,7 +2135,7 @@ export class WikiGenerator {
1797
2135
  duration: Date.now() - startTime,
1798
2136
  projectRoot: this.projectRoot,
1799
2137
  language: this.options.language,
1800
- files: files.map(f => ({
2138
+ files: files.map((f) => ({
1801
2139
  path: f.path,
1802
2140
  hash: f.hash,
1803
2141
  size: f.size,
@@ -1813,7 +2151,9 @@ export class WikiGenerator {
1813
2151
 
1814
2152
  _readMeta() {
1815
2153
  try {
1816
- if (!fs.existsSync(this.metaPath)) return null;
2154
+ if (!fs.existsSync(this.metaPath)) {
2155
+ return null;
2156
+ }
1817
2157
  return JSON.parse(fs.readFileSync(this.metaPath, 'utf-8'));
1818
2158
  } catch {
1819
2159
  return null;
@@ -1822,27 +2162,33 @@ export class WikiGenerator {
1822
2162
 
1823
2163
  /** 检测源码是否有变更(简化:对比 sourceHash) */
1824
2164
  _detectChanges(meta) {
1825
- if (!meta?.sourceHash) return true;
2165
+ if (!meta?.sourceHash) {
2166
+ return true;
2167
+ }
1826
2168
  return meta.sourceHash !== this._computeSourceHash();
1827
2169
  }
1828
2170
 
1829
2171
  /** 计算项目源文件的简易 hash(基于文件名列表 + 总大小) */
1830
2172
  _computeSourceHash() {
1831
2173
  try {
1832
- const extSet = new Set(['.swift', '.m', '.h', '.mm']);
2174
+ const extSet = LanguageService.sourceExts;
1833
2175
  let totalSize = 0;
1834
2176
  const names = [];
1835
- this._walkDir(this.projectRoot, (filePath) => {
1836
- const ext = path.extname(filePath);
1837
- if (extSet.has(ext)) {
1838
- const stat = fs.statSync(filePath);
1839
- totalSize += stat.size;
1840
- names.push(path.relative(this.projectRoot, filePath));
1841
- }
1842
- }, 2000);
2177
+ this._walkDir(
2178
+ this.projectRoot,
2179
+ (filePath) => {
2180
+ const ext = path.extname(filePath);
2181
+ if (extSet.has(ext)) {
2182
+ const stat = fs.statSync(filePath);
2183
+ totalSize += stat.size;
2184
+ names.push(path.relative(this.projectRoot, filePath));
2185
+ }
2186
+ },
2187
+ 2000
2188
+ );
1843
2189
 
1844
2190
  names.sort();
1845
- const payload = names.join('\n') + '\n' + totalSize;
2191
+ const payload = `${names.join('\n')}\n${totalSize}`;
1846
2192
  return createHash('sha256').update(payload).digest('hex').slice(0, 16);
1847
2193
  } catch {
1848
2194
  return 'unknown';
@@ -1854,20 +2200,42 @@ export class WikiGenerator {
1854
2200
  */
1855
2201
  _walkDir(dir, callback, maxFiles = 500) {
1856
2202
  const excludeNames = new Set([
1857
- 'Pods', 'Carthage', 'node_modules', '.build', 'build', 'DerivedData',
1858
- 'vendor', '.git', '__tests__', 'Tests', 'AutoSnippet', '.cursor',
2203
+ 'Pods',
2204
+ 'Carthage',
2205
+ 'node_modules',
2206
+ '.build',
2207
+ 'build',
2208
+ 'DerivedData',
2209
+ 'vendor',
2210
+ '.git',
2211
+ '__tests__',
2212
+ 'Tests',
2213
+ 'AutoSnippet',
2214
+ '.cursor',
1859
2215
  ]);
1860
2216
  let count = 0;
1861
2217
 
1862
2218
  const walk = (d) => {
1863
- if (count >= maxFiles) return;
2219
+ if (count >= maxFiles) {
2220
+ return;
2221
+ }
1864
2222
  let entries;
1865
- try { entries = fs.readdirSync(d, { withFileTypes: true }); } catch { return; }
2223
+ try {
2224
+ entries = fs.readdirSync(d, { withFileTypes: true });
2225
+ } catch {
2226
+ return;
2227
+ }
1866
2228
 
1867
2229
  for (const entry of entries) {
1868
- if (count >= maxFiles) return;
1869
- if (excludeNames.has(entry.name)) continue;
1870
- if (entry.name.startsWith('.')) continue;
2230
+ if (count >= maxFiles) {
2231
+ return;
2232
+ }
2233
+ if (excludeNames.has(entry.name)) {
2234
+ continue;
2235
+ }
2236
+ if (entry.name.startsWith('.')) {
2237
+ continue;
2238
+ }
1871
2239
 
1872
2240
  const fullPath = path.join(d, entry.name);
1873
2241
  if (entry.isDirectory()) {
@@ -1909,21 +2277,27 @@ export class WikiGenerator {
1909
2277
  const name = target.name;
1910
2278
 
1911
2279
  // 1. 按模块名直接匹配(最常见: Sources/{name}/ 解析出的 key)
1912
- if (sfm[name]?.length > 0) return sfm[name];
2280
+ if (sfm[name]?.length > 0) {
2281
+ return sfm[name];
2282
+ }
1913
2283
 
1914
2284
  // 2. 通过 target.path 或 target.info.path 匹配
1915
2285
  const targetPath = target.path || target.info?.path;
1916
2286
  if (targetPath) {
1917
- const matched = (projectInfo.sourceFiles || []).filter(f =>
1918
- f.startsWith(targetPath + '/') || f.startsWith(targetPath + path.sep)
2287
+ const matched = (projectInfo.sourceFiles || []).filter(
2288
+ (f) => f.startsWith(`${targetPath}/`) || f.startsWith(targetPath + path.sep)
1919
2289
  );
1920
- if (matched.length > 0) return matched;
2290
+ if (matched.length > 0) {
2291
+ return matched;
2292
+ }
1921
2293
  }
1922
2294
 
1923
2295
  // 3. 大小写不敏感模糊匹配
1924
2296
  const lower = name.toLowerCase();
1925
2297
  for (const [key, files] of Object.entries(sfm)) {
1926
- if (key.toLowerCase() === lower) return files;
2298
+ if (key.toLowerCase() === lower) {
2299
+ return files;
2300
+ }
1927
2301
  }
1928
2302
 
1929
2303
  return [];