autosnippet 3.3.6 → 3.3.8

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 (275) hide show
  1. package/README.md +1 -0
  2. package/dashboard/dist/assets/icons-BMNb0V6L.js +1 -0
  3. package/dashboard/dist/assets/index-DHJ1Dj7u.css +1 -0
  4. package/dashboard/dist/assets/index-DV8biUkH.js +112 -0
  5. package/dashboard/dist/index.html +3 -3
  6. package/dist/bin/cli.js +8 -4
  7. package/dist/lib/agent/AgentRuntime.d.ts +2 -2
  8. package/dist/lib/agent/AgentRuntime.js +26 -18
  9. package/dist/lib/agent/core/ChatAgentPrompts.js +57 -21
  10. package/dist/lib/agent/core/LoopContext.d.ts +1 -0
  11. package/dist/lib/agent/core/ToolExecutionPipeline.js +13 -0
  12. package/dist/lib/agent/domain/ChatAgentTasks.js +4 -0
  13. package/dist/lib/agent/forced-summary.js +7 -2
  14. package/dist/lib/agent/memory/ActiveContext.d.ts +0 -2
  15. package/dist/lib/agent/memory/ActiveContext.js +0 -2
  16. package/dist/lib/agent/memory/MemoryEmbeddingStore.d.ts +49 -0
  17. package/dist/lib/agent/memory/MemoryEmbeddingStore.js +159 -0
  18. package/dist/lib/agent/memory/MemoryRetriever.d.ts +2 -0
  19. package/dist/lib/agent/memory/MemoryRetriever.js +25 -11
  20. package/dist/lib/agent/memory/MemoryStore.d.ts +8 -41
  21. package/dist/lib/agent/memory/MemoryStore.js +196 -261
  22. package/dist/lib/agent/memory/PersistentMemory.d.ts +2 -0
  23. package/dist/lib/agent/memory/PersistentMemory.js +4 -5
  24. package/dist/lib/agent/memory/SessionStore.d.ts +0 -2
  25. package/dist/lib/agent/memory/SessionStore.js +0 -2
  26. package/dist/lib/agent/tools/ast-graph.js +21 -19
  27. package/dist/lib/agent/tools/infrastructure.js +3 -2
  28. package/dist/lib/agent/tools/project-access.d.ts +2 -2
  29. package/dist/lib/agent/tools/project-access.js +5 -4
  30. package/dist/lib/bootstrap.js +2 -1
  31. package/dist/lib/cli/AiScanService.js +8 -21
  32. package/dist/lib/cli/KnowledgeSyncService.d.ts +7 -37
  33. package/dist/lib/cli/KnowledgeSyncService.js +23 -51
  34. package/dist/lib/core/ast/ProjectGraph.js +5 -27
  35. package/dist/lib/core/discovery/ConfigWatcher.d.ts +64 -0
  36. package/dist/lib/core/discovery/ConfigWatcher.js +336 -0
  37. package/dist/lib/core/discovery/CustomConfigDiscoverer.d.ts +28 -0
  38. package/dist/lib/core/discovery/CustomConfigDiscoverer.js +1303 -0
  39. package/dist/lib/core/discovery/DiscovererPreference.d.ts +44 -0
  40. package/dist/lib/core/discovery/DiscovererPreference.js +141 -0
  41. package/dist/lib/core/discovery/DiscovererRegistry.d.ts +10 -1
  42. package/dist/lib/core/discovery/DiscovererRegistry.js +42 -2
  43. package/dist/lib/core/discovery/ProjectDiscoverer.d.ts +19 -0
  44. package/dist/lib/core/discovery/index.d.ts +2 -0
  45. package/dist/lib/core/discovery/index.js +4 -0
  46. package/dist/lib/core/discovery/parsers/CMakeParser.d.ts +32 -0
  47. package/dist/lib/core/discovery/parsers/CMakeParser.js +148 -0
  48. package/dist/lib/core/discovery/parsers/GradleDslParser.d.ts +43 -0
  49. package/dist/lib/core/discovery/parsers/GradleDslParser.js +171 -0
  50. package/dist/lib/core/discovery/parsers/JsonConfigParser.d.ts +45 -0
  51. package/dist/lib/core/discovery/parsers/JsonConfigParser.js +122 -0
  52. package/dist/lib/core/discovery/parsers/RubyDslParser.d.ts +49 -0
  53. package/dist/lib/core/discovery/parsers/RubyDslParser.js +282 -0
  54. package/dist/lib/core/discovery/parsers/StarlarkParser.d.ts +33 -0
  55. package/dist/lib/core/discovery/parsers/StarlarkParser.js +229 -0
  56. package/dist/lib/core/discovery/parsers/YamlConfigParser.d.ts +37 -0
  57. package/dist/lib/core/discovery/parsers/YamlConfigParser.js +212 -0
  58. package/dist/lib/domain/dimension/DimensionRegistry.d.ts +0 -2
  59. package/dist/lib/domain/dimension/DimensionRegistry.js +0 -2
  60. package/dist/lib/domain/dimension/DimensionSop.js +44 -33
  61. package/dist/lib/domain/dimension/UnifiedDimension.d.ts +0 -2
  62. package/dist/lib/domain/dimension/UnifiedDimension.js +0 -2
  63. package/dist/lib/domain/knowledge/KnowledgeEntry.d.ts +7 -1
  64. package/dist/lib/domain/knowledge/KnowledgeEntry.js +17 -3
  65. package/dist/lib/domain/knowledge/Lifecycle.d.ts +26 -0
  66. package/dist/lib/domain/knowledge/Lifecycle.js +42 -0
  67. package/dist/lib/domain/knowledge/index.d.ts +2 -1
  68. package/dist/lib/domain/knowledge/index.js +1 -1
  69. package/dist/lib/external/ai/AiProvider.d.ts +12 -0
  70. package/dist/lib/external/ai/AiProvider.js +24 -0
  71. package/dist/lib/external/ai/AiProviderManager.d.ts +101 -0
  72. package/dist/lib/external/ai/AiProviderManager.js +193 -0
  73. package/dist/lib/external/ai/providers/ClaudeProvider.js +11 -0
  74. package/dist/lib/external/ai/providers/GoogleGeminiProvider.js +18 -0
  75. package/dist/lib/external/ai/providers/MockProvider.d.ts +21 -3
  76. package/dist/lib/external/ai/providers/MockProvider.js +290 -14
  77. package/dist/lib/external/ai/providers/OpenAiProvider.js +16 -0
  78. package/dist/lib/external/lark/LarkTransport.d.ts +5 -1
  79. package/dist/lib/external/lark/LarkTransport.js +10 -2
  80. package/dist/lib/external/mcp/handlers/bootstrap/pipeline/BootstrapSnapshot.d.ts +2 -1
  81. package/dist/lib/external/mcp/handlers/bootstrap/pipeline/BootstrapSnapshot.js +102 -153
  82. package/dist/lib/external/mcp/handlers/bootstrap/pipeline/mock-pipeline.d.ts +20 -0
  83. package/dist/lib/external/mcp/handlers/bootstrap/pipeline/mock-pipeline.js +432 -0
  84. package/dist/lib/external/mcp/handlers/bootstrap/pipeline/orchestrator.js +49 -24
  85. package/dist/lib/external/mcp/handlers/bootstrap/refine.js +8 -0
  86. package/dist/lib/external/mcp/handlers/bootstrap/shared/bootstrap-phases.d.ts +1 -1
  87. package/dist/lib/external/mcp/handlers/bootstrap/shared/bootstrap-phases.js +41 -37
  88. package/dist/lib/external/mcp/handlers/bootstrap-external.d.ts +9 -0
  89. package/dist/lib/external/mcp/handlers/bootstrap-external.js +3 -1
  90. package/dist/lib/external/mcp/handlers/bootstrap-internal.js +2 -0
  91. package/dist/lib/external/mcp/handlers/consolidated.js +2 -1
  92. package/dist/lib/external/mcp/handlers/dimension-complete-external.js +9 -4
  93. package/dist/lib/external/mcp/handlers/evolve-external.d.ts +1 -0
  94. package/dist/lib/external/mcp/handlers/evolve-external.js +18 -18
  95. package/dist/lib/external/mcp/handlers/guard.js +15 -24
  96. package/dist/lib/external/mcp/handlers/knowledge.js +5 -4
  97. package/dist/lib/external/mcp/handlers/panorama.js +9 -9
  98. package/dist/lib/external/mcp/handlers/rescan-external.js +7 -6
  99. package/dist/lib/external/mcp/handlers/rescan-internal.js +9 -5
  100. package/dist/lib/external/mcp/handlers/search.js +3 -1
  101. package/dist/lib/external/mcp/handlers/skill.js +4 -4
  102. package/dist/lib/external/mcp/handlers/structure.js +8 -12
  103. package/dist/lib/external/mcp/handlers/system.js +10 -34
  104. package/dist/lib/http/routes/ai.js +109 -30
  105. package/dist/lib/http/routes/candidates.js +11 -4
  106. package/dist/lib/http/routes/commands.js +10 -1
  107. package/dist/lib/http/routes/guardReport.js +3 -5
  108. package/dist/lib/http/routes/health.js +11 -0
  109. package/dist/lib/http/routes/modules.js +27 -0
  110. package/dist/lib/http/routes/panorama.js +12 -12
  111. package/dist/lib/http/routes/recipes.js +66 -8
  112. package/dist/lib/http/routes/remote.js +3 -13
  113. package/dist/lib/http/routes/search.js +11 -8
  114. package/dist/lib/http/utils/routeHelpers.js +2 -1
  115. package/dist/lib/infrastructure/audit/AuditLogger.d.ts +20 -3
  116. package/dist/lib/infrastructure/audit/AuditStore.d.ts +28 -29
  117. package/dist/lib/infrastructure/audit/AuditStore.js +81 -88
  118. package/dist/lib/infrastructure/database/drizzle/schema.d.ts +180 -2
  119. package/dist/lib/infrastructure/database/drizzle/schema.js +23 -3
  120. package/dist/lib/injection/ServiceContainer.d.ts +6 -5
  121. package/dist/lib/injection/ServiceContainer.js +18 -31
  122. package/dist/lib/injection/ServiceMap.d.ts +22 -0
  123. package/dist/lib/injection/modules/AiModule.d.ts +6 -9
  124. package/dist/lib/injection/modules/AiModule.js +82 -39
  125. package/dist/lib/injection/modules/AppModule.js +2 -1
  126. package/dist/lib/injection/modules/GuardModule.js +5 -5
  127. package/dist/lib/injection/modules/InfraModule.js +60 -0
  128. package/dist/lib/injection/modules/KnowledgeModule.js +86 -51
  129. package/dist/lib/injection/modules/PanoramaModule.js +16 -10
  130. package/dist/lib/injection/modules/VectorModule.js +3 -0
  131. package/dist/lib/repository/audit/AuditRepository.d.ts +107 -0
  132. package/dist/lib/repository/audit/AuditRepository.js +272 -0
  133. package/dist/lib/repository/base/RepositoryBase.d.ts +46 -0
  134. package/dist/lib/repository/base/RepositoryBase.js +32 -0
  135. package/dist/lib/repository/bootstrap/BootstrapRepository.d.ts +94 -0
  136. package/dist/lib/repository/bootstrap/BootstrapRepository.js +246 -0
  137. package/dist/lib/repository/code/CodeEntityRepository.d.ts +91 -0
  138. package/dist/lib/repository/code/CodeEntityRepository.js +361 -0
  139. package/dist/lib/repository/delivery/DeliveryRepoAdapter.d.ts +39 -0
  140. package/dist/lib/repository/delivery/DeliveryRepoAdapter.js +23 -0
  141. package/dist/lib/repository/evolution/LifecycleEventRepository.d.ts +51 -0
  142. package/dist/lib/repository/evolution/LifecycleEventRepository.js +119 -0
  143. package/dist/lib/repository/evolution/ProposalRepository.d.ts +9 -12
  144. package/dist/lib/repository/evolution/ProposalRepository.js +114 -57
  145. package/dist/lib/repository/guard/GuardViolationRepository.d.ts +104 -0
  146. package/dist/lib/repository/guard/GuardViolationRepository.js +217 -0
  147. package/dist/lib/repository/knowledge/KnowledgeEdgeRepository.d.ts +129 -0
  148. package/dist/lib/repository/knowledge/KnowledgeEdgeRepository.js +475 -0
  149. package/dist/lib/repository/knowledge/KnowledgeFileStore.d.ts +39 -0
  150. package/dist/lib/repository/knowledge/KnowledgeFileStore.js +12 -0
  151. package/dist/lib/repository/knowledge/KnowledgeRepository.impl.d.ts +295 -11
  152. package/dist/lib/repository/knowledge/KnowledgeRepository.impl.js +608 -13
  153. package/dist/lib/repository/knowledge/KnowledgeUnitOfWork.d.ts +61 -0
  154. package/dist/lib/repository/knowledge/KnowledgeUnitOfWork.js +156 -0
  155. package/dist/lib/repository/memory/MemoryRepository.d.ts +90 -0
  156. package/dist/lib/repository/memory/MemoryRepository.js +260 -0
  157. package/dist/lib/repository/search/SearchRepoAdapter.d.ts +92 -0
  158. package/dist/lib/repository/search/SearchRepoAdapter.js +124 -0
  159. package/dist/lib/repository/session/SessionRepository.d.ts +46 -0
  160. package/dist/lib/repository/session/SessionRepository.js +110 -0
  161. package/dist/lib/repository/sourceref/RecipeSourceRefRepository.d.ts +66 -0
  162. package/dist/lib/repository/sourceref/RecipeSourceRefRepository.js +182 -0
  163. package/dist/lib/repository/sync/SyncRepoAdapter.d.ts +58 -0
  164. package/dist/lib/repository/sync/SyncRepoAdapter.js +58 -0
  165. package/dist/lib/service/bootstrap/UiStartupTasks.js +5 -6
  166. package/dist/lib/service/bootstrap/bootstrap-event-types.d.ts +0 -1
  167. package/dist/lib/service/bootstrap/bootstrap-event-types.js +0 -1
  168. package/dist/lib/service/cleanup/CleanupService.d.ts +54 -7
  169. package/dist/lib/service/cleanup/CleanupService.js +291 -40
  170. package/dist/lib/service/delivery/CursorDeliveryPipeline.js +6 -8
  171. package/dist/lib/service/evolution/ConsolidationAdvisor.d.ts +4 -9
  172. package/dist/lib/service/evolution/ConsolidationAdvisor.js +34 -70
  173. package/dist/lib/service/evolution/ContentPatcher.d.ts +4 -12
  174. package/dist/lib/service/evolution/ContentPatcher.js +48 -19
  175. package/dist/lib/service/evolution/ContradictionDetector.d.ts +3 -7
  176. package/dist/lib/service/evolution/ContradictionDetector.js +17 -24
  177. package/dist/lib/service/evolution/DecayDetector.d.ts +10 -9
  178. package/dist/lib/service/evolution/DecayDetector.js +63 -57
  179. package/dist/lib/service/evolution/EnhancementSuggester.d.ts +3 -9
  180. package/dist/lib/service/evolution/EnhancementSuggester.js +42 -86
  181. package/dist/lib/service/evolution/KnowledgeMetabolism.d.ts +4 -4
  182. package/dist/lib/service/evolution/KnowledgeMetabolism.js +102 -71
  183. package/dist/lib/service/evolution/ProposalExecutor.d.ts +5 -12
  184. package/dist/lib/service/evolution/ProposalExecutor.js +64 -69
  185. package/dist/lib/service/evolution/RecipeLifecycleSupervisor.d.ts +9 -14
  186. package/dist/lib/service/evolution/RecipeLifecycleSupervisor.js +94 -155
  187. package/dist/lib/service/evolution/RecipeRelevanceAuditor.d.ts +4 -1
  188. package/dist/lib/service/evolution/RecipeRelevanceAuditor.js +50 -49
  189. package/dist/lib/service/evolution/RedundancyAnalyzer.d.ts +3 -7
  190. package/dist/lib/service/evolution/RedundancyAnalyzer.js +15 -22
  191. package/dist/lib/service/evolution/StagingManager.d.ts +6 -15
  192. package/dist/lib/service/evolution/StagingManager.js +37 -95
  193. package/dist/lib/service/evolution/createSupersedeProposal.d.ts +1 -1
  194. package/dist/lib/service/evolution/createSupersedeProposal.js +7 -8
  195. package/dist/lib/service/guard/CoverageAnalyzer.d.ts +3 -7
  196. package/dist/lib/service/guard/CoverageAnalyzer.js +9 -11
  197. package/dist/lib/service/guard/GuardCheckEngine.d.ts +3 -0
  198. package/dist/lib/service/guard/GuardCheckEngine.js +14 -22
  199. package/dist/lib/service/guard/ReverseGuard.d.ts +4 -7
  200. package/dist/lib/service/guard/ReverseGuard.js +21 -31
  201. package/dist/lib/service/guard/ViolationsStore.d.ts +15 -21
  202. package/dist/lib/service/guard/ViolationsStore.js +75 -69
  203. package/dist/lib/service/knowledge/CodeEntityGraph.d.ts +45 -63
  204. package/dist/lib/service/knowledge/CodeEntityGraph.js +418 -496
  205. package/dist/lib/service/knowledge/ConfidenceRouter.js +18 -9
  206. package/dist/lib/service/knowledge/KnowledgeFileWriter.d.ts +2 -1
  207. package/dist/lib/service/knowledge/KnowledgeGraphService.d.ts +18 -60
  208. package/dist/lib/service/knowledge/KnowledgeGraphService.js +58 -109
  209. package/dist/lib/service/knowledge/KnowledgeService.d.ts +15 -1
  210. package/dist/lib/service/knowledge/KnowledgeService.js +97 -46
  211. package/dist/lib/service/knowledge/RecipeProductionGateway.d.ts +0 -2
  212. package/dist/lib/service/knowledge/RecipeProductionGateway.js +0 -2
  213. package/dist/lib/service/knowledge/SourceRefReconciler.d.ts +5 -13
  214. package/dist/lib/service/knowledge/SourceRefReconciler.js +58 -78
  215. package/dist/lib/service/module/ModuleService.js +10 -19
  216. package/dist/lib/service/panorama/CouplingAnalyzer.d.ts +14 -3
  217. package/dist/lib/service/panorama/CouplingAnalyzer.js +137 -32
  218. package/dist/lib/service/panorama/DimensionAnalyzer.d.ts +7 -4
  219. package/dist/lib/service/panorama/DimensionAnalyzer.js +94 -33
  220. package/dist/lib/service/panorama/LayerInferrer.d.ts +16 -1
  221. package/dist/lib/service/panorama/LayerInferrer.js +118 -1
  222. package/dist/lib/service/panorama/ModuleDiscoverer.d.ts +14 -4
  223. package/dist/lib/service/panorama/ModuleDiscoverer.js +209 -61
  224. package/dist/lib/service/panorama/PanoramaAggregator.d.ts +15 -4
  225. package/dist/lib/service/panorama/PanoramaAggregator.js +128 -62
  226. package/dist/lib/service/panorama/PanoramaScanner.d.ts +5 -1
  227. package/dist/lib/service/panorama/PanoramaScanner.js +60 -31
  228. package/dist/lib/service/panorama/PanoramaService.d.ts +11 -8
  229. package/dist/lib/service/panorama/PanoramaService.js +49 -69
  230. package/dist/lib/service/panorama/PanoramaTypes.d.ts +41 -0
  231. package/dist/lib/service/panorama/RoleRefiner.d.ts +10 -5
  232. package/dist/lib/service/panorama/RoleRefiner.js +92 -282
  233. package/dist/lib/service/panorama/TechStackProfiler.d.ts +13 -0
  234. package/dist/lib/service/panorama/TechStackProfiler.js +79 -0
  235. package/dist/lib/service/quality/QualityScorer.d.ts +45 -26
  236. package/dist/lib/service/quality/QualityScorer.js +157 -83
  237. package/dist/lib/service/search/SearchEngine.d.ts +1 -0
  238. package/dist/lib/service/search/SearchEngine.js +32 -37
  239. package/dist/lib/service/signal/HitRecorder.js +5 -5
  240. package/dist/lib/service/skills/RuleRecallStrategy.js +7 -3
  241. package/dist/lib/service/skills/SignalCollector.d.ts +6 -8
  242. package/dist/lib/service/skills/SignalCollector.js +34 -60
  243. package/dist/lib/service/skills/SkillAdvisor.d.ts +7 -13
  244. package/dist/lib/service/skills/SkillAdvisor.js +30 -79
  245. package/dist/lib/service/vector/ContextualEnricher.d.ts +1 -0
  246. package/dist/lib/service/vector/ContextualEnricher.js +4 -0
  247. package/dist/lib/service/vector/SyncCoordinator.d.ts +3 -1
  248. package/dist/lib/service/vector/SyncCoordinator.js +25 -3
  249. package/dist/lib/service/vector/VectorService.d.ts +2 -0
  250. package/dist/lib/service/vector/VectorService.js +3 -0
  251. package/dist/lib/service/wiki/WikiGenerator.js +1 -1
  252. package/dist/lib/shared/LanguageProfiles.d.ts +109 -0
  253. package/dist/lib/shared/LanguageProfiles.js +939 -0
  254. package/dist/lib/shared/LanguageService.d.ts +6 -0
  255. package/dist/lib/shared/LanguageService.js +19 -0
  256. package/dist/lib/shared/constants.d.ts +19 -19
  257. package/dist/lib/shared/constants.js +10 -10
  258. package/dist/lib/shared/developer-identity.d.ts +18 -0
  259. package/dist/lib/shared/developer-identity.js +62 -0
  260. package/dist/lib/shared/schemas/http-requests.d.ts +8 -17
  261. package/dist/lib/shared/schemas/http-requests.js +9 -6
  262. package/dist/lib/shared/schemas/mcp-tools.d.ts +1 -1
  263. package/dist/lib/types/knowledge-wire.d.ts +1 -0
  264. package/dist/lib/types/project-snapshot-builder.d.ts +0 -1
  265. package/dist/lib/types/project-snapshot-builder.js +0 -1
  266. package/dist/lib/types/project-snapshot.d.ts +0 -1
  267. package/dist/lib/types/project-snapshot.js +0 -1
  268. package/dist/lib/types/snapshot-views.d.ts +0 -2
  269. package/dist/lib/types/snapshot-views.js +0 -1
  270. package/package.json +2 -1
  271. package/dashboard/dist/assets/icons-D1aVZYFW.js +0 -1
  272. package/dashboard/dist/assets/index-CxHOu8Hd.css +0 -1
  273. package/dashboard/dist/assets/index-DDdAOpYT.js +0 -128
  274. package/dist/lib/repository/base/BaseRepository.d.ts +0 -53
  275. package/dist/lib/repository/base/BaseRepository.js +0 -226
@@ -0,0 +1,1303 @@
1
+ /**
2
+ * @module CustomConfigDiscoverer
3
+ * @description 自研配置文件发现器 — 识别使用非标准/自研构建系统的项目
4
+ *
5
+ * 两级检测策略:
6
+ * Level 1: 已知自研工具指纹匹配 (confidence 0.70-0.80)
7
+ * Level 2: 启发式目录结构探测 (confidence 0.50-0.65)
8
+ *
9
+ * 当前支持:
10
+ * - Baidu EasyBox (Boxfile + *.boxspec)
11
+ * - Tuist (Project.swift)
12
+ * - XcodeGen (project.yml)
13
+ */
14
+ import { existsSync, readdirSync, readFileSync } from 'node:fs';
15
+ import { extname, join, relative } from 'node:path';
16
+ import { getProjectSpecPath } from '#infra/config/Paths.js';
17
+ import { LanguageService } from '#shared/LanguageService.js';
18
+ import { ProjectDiscoverer, } from './ProjectDiscoverer.js';
19
+ import { parseCMakeProject } from './parsers/CMakeParser.js';
20
+ import { inferConventionRole, parseGradleProject } from './parsers/GradleDslParser.js';
21
+ import { parseFlutterPluginsDeps, parseNxWorkspace, parseReactNativeProject, } from './parsers/JsonConfigParser.js';
22
+ import { parseBoxfile, parseModuleSpec, } from './parsers/RubyDslParser.js';
23
+ import { parseStarlarkBuildFile, RULE_TO_LANGUAGE, } from './parsers/StarlarkParser.js';
24
+ import { parseMelosProject, parseXcodeGenProject, parseXcodeGenTarget, } from './parsers/YamlConfigParser.js';
25
+ const KNOWN_CUSTOM_SYSTEMS = Object.freeze([
26
+ // ── Tier 1: Bazel / Buck2 (Starlark) ──
27
+ {
28
+ id: 'bazel',
29
+ displayName: 'Bazel',
30
+ markers: ['MODULE.bazel', 'WORKSPACE', 'WORKSPACE.bazel'],
31
+ markerStrategy: 'any',
32
+ moduleSpecPattern: 'BUILD.bazel',
33
+ language: Object.freeze([]),
34
+ confidence: 0.85,
35
+ parser: 'starlark',
36
+ },
37
+ {
38
+ id: 'buck2',
39
+ displayName: 'Buck2',
40
+ markers: ['.buckconfig', '.buckroot'],
41
+ markerStrategy: 'any',
42
+ moduleSpecPattern: 'BUCK',
43
+ language: Object.freeze([]),
44
+ confidence: 0.85,
45
+ parser: 'starlark',
46
+ },
47
+ // ── Tier 1: Android Gradle Convention Plugins ──
48
+ {
49
+ id: 'gradle-convention',
50
+ displayName: 'Gradle Convention Plugins',
51
+ markers: ['build-logic/convention/', 'buildSrc/src/main/kotlin/'],
52
+ markerStrategy: 'any',
53
+ moduleSpecPattern: null,
54
+ language: Object.freeze(['kotlin', 'java']),
55
+ confidence: 0.8,
56
+ parser: 'gradle-dsl',
57
+ },
58
+ // ── Tier 1: Flutter Melos ──
59
+ {
60
+ id: 'melos',
61
+ displayName: 'Melos (Flutter Monorepo)',
62
+ markers: ['melos.yaml'],
63
+ moduleSpecPattern: null,
64
+ language: Object.freeze(['dart']),
65
+ confidence: 0.82,
66
+ parser: 'yaml',
67
+ },
68
+ // ── Tier 1: iOS 生态 ──
69
+ {
70
+ id: 'easybox',
71
+ displayName: 'Baidu EasyBox',
72
+ markers: ['Boxfile'],
73
+ moduleSpecPattern: '*.boxspec',
74
+ language: Object.freeze(['objectivec', 'swift']),
75
+ confidence: 0.8,
76
+ parser: 'ruby-dsl',
77
+ },
78
+ {
79
+ id: 'tuist',
80
+ displayName: 'Tuist',
81
+ markers: ['Tuist/Config.swift', 'Project.swift'],
82
+ moduleSpecPattern: null,
83
+ language: Object.freeze(['swift']),
84
+ confidence: 0.8,
85
+ parser: 'swift-dsl',
86
+ },
87
+ {
88
+ id: 'ks-component',
89
+ displayName: 'KSComponent (快手)',
90
+ markers: ['KSPodfile', 'Podfile.ks'],
91
+ markerStrategy: 'any',
92
+ moduleSpecPattern: '*.podspec',
93
+ language: Object.freeze(['swift', 'objectivec']),
94
+ confidence: 0.8,
95
+ parser: 'ruby-dsl',
96
+ },
97
+ {
98
+ id: 'mt-component',
99
+ displayName: 'MTComponent (美团)',
100
+ markers: ['MTModulefile', 'MTConfig.yml'],
101
+ markerStrategy: 'any',
102
+ moduleSpecPattern: '*.podspec',
103
+ language: Object.freeze(['swift', 'objectivec']),
104
+ confidence: 0.78,
105
+ parser: 'ruby-dsl',
106
+ },
107
+ // ── Tier 1: 混合架构 ──
108
+ {
109
+ id: 'flutter-add-to-app',
110
+ displayName: 'Flutter Add-to-App',
111
+ markers: ['.flutter-plugins-dependencies', '.flutter-plugins'],
112
+ markerStrategy: 'any',
113
+ moduleSpecPattern: 'pubspec.yaml',
114
+ language: Object.freeze(['dart']),
115
+ confidence: 0.78,
116
+ parser: 'json-config',
117
+ },
118
+ {
119
+ id: 'react-native-hybrid',
120
+ displayName: 'React Native Hybrid',
121
+ markers: ['metro.config.js', 'metro.config.ts', 'react-native.config.js'],
122
+ markerStrategy: 'any',
123
+ moduleSpecPattern: null,
124
+ language: Object.freeze(['typescript', 'javascript']),
125
+ confidence: 0.78,
126
+ parser: 'json-config',
127
+ },
128
+ {
129
+ id: 'kotlin-multiplatform',
130
+ displayName: 'Kotlin Multiplatform',
131
+ markers: ['shared/build.gradle.kts'],
132
+ moduleSpecPattern: null,
133
+ language: Object.freeze(['kotlin']),
134
+ confidence: 0.78,
135
+ parser: 'gradle-dsl',
136
+ },
137
+ // ── Tier 2: Nx / Pants / CMake ──
138
+ {
139
+ id: 'nx-monorepo',
140
+ displayName: 'Nx Monorepo',
141
+ markers: ['nx.json'],
142
+ moduleSpecPattern: 'project.json',
143
+ language: Object.freeze(['typescript', 'javascript']),
144
+ confidence: 0.8,
145
+ parser: 'json-config',
146
+ },
147
+ {
148
+ id: 'pants',
149
+ displayName: 'Pants Build',
150
+ markers: ['pants.toml'],
151
+ moduleSpecPattern: 'BUILD',
152
+ language: Object.freeze([]),
153
+ confidence: 0.8,
154
+ parser: 'starlark',
155
+ },
156
+ {
157
+ id: 'cmake-multiproject',
158
+ displayName: 'CMake Multi-Project',
159
+ markers: ['CMakeLists.txt'],
160
+ antiMarkers: ['MODULE.bazel', 'WORKSPACE', 'meson.build'],
161
+ moduleSpecPattern: 'CMakeLists.txt',
162
+ language: Object.freeze(['cpp', 'c']),
163
+ confidence: 0.75,
164
+ parser: 'cmake',
165
+ },
166
+ {
167
+ id: 'xcodegen',
168
+ displayName: 'XcodeGen',
169
+ markers: ['project.yml', 'project.yaml'],
170
+ markerStrategy: 'any',
171
+ moduleSpecPattern: null,
172
+ language: Object.freeze(['swift', 'objectivec']),
173
+ confidence: 0.75,
174
+ parser: 'yaml',
175
+ },
176
+ ]);
177
+ const HEURISTIC_SIGNALS = Object.freeze([
178
+ { pattern: /^(Local)?Modules?$/i, type: 'module-dir', boost: 0.15 },
179
+ { pattern: /^Packages$/i, type: 'module-dir', boost: 0.1 },
180
+ { pattern: /^[A-Z]\w+file$/, type: 'custom-dsl', boost: 0.2 },
181
+ { pattern: /\.\w+spec$/, type: 'spec-file', boost: 0.2 },
182
+ { pattern: /\.xcodeproj$/, type: 'xcode', boost: 0.05 },
183
+ ]);
184
+ // 排除已知的标准 Ruby DSL 文件
185
+ const KNOWN_STANDARD_FILES = new Set([
186
+ 'Gemfile',
187
+ 'Podfile',
188
+ 'Fastfile',
189
+ 'Rakefile',
190
+ 'Vagrantfile',
191
+ 'Guardfile',
192
+ 'Brewfile',
193
+ 'Berksfile',
194
+ 'Capfile',
195
+ ]);
196
+ const EXCLUDE_DIRS = new Set([
197
+ 'node_modules',
198
+ '.git',
199
+ '.cursor',
200
+ 'dist',
201
+ 'build',
202
+ 'out',
203
+ '.build',
204
+ 'Pods',
205
+ 'Carthage',
206
+ 'DerivedData',
207
+ '__pycache__',
208
+ '.venv',
209
+ 'venv',
210
+ '.gradle',
211
+ 'coverage',
212
+ '.cache',
213
+ '.easybox',
214
+ ]);
215
+ const SOURCE_EXTENSIONS = new Set(['.m', '.h', '.swift', '.mm', '.c', '.cpp', '.cc']);
216
+ // ── User Custom Systems (boxspec.json) ──────────────
217
+ /**
218
+ * 从 boxspec.json 读取用户自定义配置系统
219
+ *
220
+ * boxspec.json 中可选字段:
221
+ * ```json
222
+ * {
223
+ * "customDiscoverer": {
224
+ * "id": "my-build-tool",
225
+ * "displayName": "MyBuildTool",
226
+ * "markers": ["MyBuildfile"],
227
+ * "moduleSpecPattern": "*.myspec",
228
+ * "language": ["swift"],
229
+ * "confidence": 0.85,
230
+ * "parser": "ruby-dsl"
231
+ * }
232
+ * }
233
+ * ```
234
+ * 或数组形式支持多个自定义系统。
235
+ */
236
+ function loadUserCustomSystems(projectRoot) {
237
+ try {
238
+ const specPath = getProjectSpecPath(projectRoot);
239
+ if (!existsSync(specPath)) {
240
+ return [];
241
+ }
242
+ const raw = JSON.parse(readFileSync(specPath, 'utf-8'));
243
+ const custom = raw?.customDiscoverer;
244
+ if (!custom) {
245
+ return [];
246
+ }
247
+ const items = Array.isArray(custom) ? custom : [custom];
248
+ const results = [];
249
+ for (const item of items) {
250
+ if (!item?.id || !item?.markers || !Array.isArray(item.markers)) {
251
+ continue;
252
+ }
253
+ results.push({
254
+ id: String(item.id),
255
+ displayName: String(item.displayName ?? item.id),
256
+ markers: item.markers.map(String),
257
+ moduleSpecPattern: item.moduleSpecPattern ? String(item.moduleSpecPattern) : null,
258
+ language: Array.isArray(item.language) ? item.language.map(String) : ['swift'],
259
+ confidence: typeof item.confidence === 'number' ? item.confidence : 0.75,
260
+ parser: [
261
+ 'ruby-dsl',
262
+ 'yaml',
263
+ 'swift-dsl',
264
+ 'starlark',
265
+ 'gradle-dsl',
266
+ 'cmake',
267
+ 'json-config',
268
+ ].includes(item.parser)
269
+ ? item.parser
270
+ : 'ruby-dsl',
271
+ markerStrategy: ['all', 'any', 'ordered'].includes(item.markerStrategy)
272
+ ? item.markerStrategy
273
+ : undefined,
274
+ antiMarkers: Array.isArray(item.antiMarkers) ? item.antiMarkers.map(String) : undefined,
275
+ });
276
+ }
277
+ return results;
278
+ }
279
+ catch {
280
+ return [];
281
+ }
282
+ }
283
+ /**
284
+ * 获取合并后的系统配置表:用户自定义 + 内置
285
+ * 用户自定义系统优先匹配
286
+ */
287
+ function getEffectiveSystemProfiles(projectRoot) {
288
+ const userSystems = loadUserCustomSystems(projectRoot);
289
+ if (userSystems.length === 0) {
290
+ return KNOWN_CUSTOM_SYSTEMS;
291
+ }
292
+ return [...userSystems, ...KNOWN_CUSTOM_SYSTEMS];
293
+ }
294
+ // ── CustomConfigDiscoverer ──────────────────────────
295
+ export class CustomConfigDiscoverer extends ProjectDiscoverer {
296
+ #projectRoot = null;
297
+ #matchedSystem = null;
298
+ #parsedConfig = null;
299
+ #moduleSpecs = new Map();
300
+ #targets = [];
301
+ get id() {
302
+ return 'customConfig';
303
+ }
304
+ get displayName() {
305
+ if (this.#matchedSystem) {
306
+ return `Custom Config (${this.#matchedSystem.displayName})`;
307
+ }
308
+ return 'Custom Config (Heuristic)';
309
+ }
310
+ // ── detect ────────────────────────────────────────
311
+ async detect(projectRoot) {
312
+ // Level 1: 已知自研工具指纹匹配(含用户自定义系统)
313
+ const systems = getEffectiveSystemProfiles(projectRoot);
314
+ for (const system of systems) {
315
+ // antiMarkers 排除检查
316
+ if (system.antiMarkers?.some((am) => existsSync(join(projectRoot, am)))) {
317
+ continue;
318
+ }
319
+ const strategy = system.markerStrategy ?? 'all';
320
+ let markerFound = false;
321
+ if (strategy === 'any') {
322
+ markerFound = system.markers.some((marker) => existsSync(join(projectRoot, marker)));
323
+ }
324
+ else {
325
+ // 'all' 和 'ordered' 都要求所有 markers 存在(ordered 未来可扩展)
326
+ markerFound = system.markers.every((marker) => existsSync(join(projectRoot, marker)));
327
+ }
328
+ if (markerFound) {
329
+ return {
330
+ match: true,
331
+ confidence: system.confidence,
332
+ reason: `${system.displayName} detected (${system.markers.join(', ')})`,
333
+ };
334
+ }
335
+ }
336
+ // Level 2: 启发式目录结构探测
337
+ let heuristicScore = 0.35; // 基础分
338
+ const signals = [];
339
+ try {
340
+ const entries = readdirSync(projectRoot, { withFileTypes: true });
341
+ for (const entry of entries) {
342
+ if (entry.name.startsWith('.')) {
343
+ continue;
344
+ }
345
+ for (const signal of HEURISTIC_SIGNALS) {
346
+ if (signal.pattern.test(entry.name)) {
347
+ // 排除已知的标准文件
348
+ if (signal.type === 'custom-dsl' && KNOWN_STANDARD_FILES.has(entry.name)) {
349
+ continue;
350
+ }
351
+ // 对 module-dir 类型,要求目录内有多个子目录
352
+ if (signal.type === 'module-dir' && entry.isDirectory()) {
353
+ const subCount = countSubdirsWithSpecs(join(projectRoot, entry.name));
354
+ if (subCount < 2) {
355
+ continue;
356
+ }
357
+ }
358
+ heuristicScore += signal.boost;
359
+ signals.push(`${entry.name} (${signal.type})`);
360
+ }
361
+ }
362
+ }
363
+ }
364
+ catch {
365
+ /* skip */
366
+ }
367
+ // 限制最高分
368
+ heuristicScore = Math.min(heuristicScore, 0.65);
369
+ if (heuristicScore >= 0.5 && signals.length >= 2) {
370
+ return {
371
+ match: true,
372
+ confidence: heuristicScore,
373
+ reason: `Heuristic signals: ${signals.join(', ')}`,
374
+ };
375
+ }
376
+ return { match: false, confidence: 0, reason: 'No custom config detected' };
377
+ }
378
+ // ── load ──────────────────────────────────────────
379
+ async load(projectRoot) {
380
+ this.#projectRoot = projectRoot;
381
+ this.#parsedConfig = null;
382
+ this.#moduleSpecs.clear();
383
+ this.#targets = [];
384
+ // 确定匹配的系统(含用户自定义系统)
385
+ this.#matchedSystem = null;
386
+ const systems = getEffectiveSystemProfiles(projectRoot);
387
+ for (const system of systems) {
388
+ if (system.antiMarkers?.some((am) => existsSync(join(projectRoot, am)))) {
389
+ continue;
390
+ }
391
+ const strategy = system.markerStrategy ?? 'all';
392
+ const markerFound = strategy === 'any'
393
+ ? system.markers.some((marker) => existsSync(join(projectRoot, marker)))
394
+ : system.markers.every((marker) => existsSync(join(projectRoot, marker)));
395
+ if (markerFound) {
396
+ this.#matchedSystem = system;
397
+ break;
398
+ }
399
+ }
400
+ if (!this.#matchedSystem) {
401
+ this.#loadHeuristic(projectRoot);
402
+ return;
403
+ }
404
+ switch (this.#matchedSystem.parser) {
405
+ case 'ruby-dsl':
406
+ this.#loadRubyDsl(projectRoot);
407
+ break;
408
+ case 'yaml':
409
+ this.#loadYaml(projectRoot);
410
+ break;
411
+ case 'starlark':
412
+ this.#loadStarlark(projectRoot);
413
+ break;
414
+ case 'gradle-dsl':
415
+ this.#loadGradleDsl(projectRoot);
416
+ break;
417
+ case 'cmake':
418
+ this.#loadCMake(projectRoot);
419
+ break;
420
+ case 'json-config':
421
+ this.#loadJsonConfig(projectRoot);
422
+ break;
423
+ default:
424
+ this.#loadHeuristic(projectRoot);
425
+ }
426
+ }
427
+ // ── listTargets ───────────────────────────────────
428
+ async listTargets() {
429
+ return this.#targets;
430
+ }
431
+ // ── getTargetFiles ────────────────────────────────
432
+ async getTargetFiles(target) {
433
+ const targetPath = typeof target === 'string' ? this.#targets.find((t) => t.name === target)?.path : target.path;
434
+ if (!targetPath || !existsSync(targetPath)) {
435
+ return [];
436
+ }
437
+ // 如果有 spec 文件,优先使用 sources 字段定位
438
+ const targetName = typeof target === 'string' ? target : target.name;
439
+ const spec = this.#moduleSpecs.get(targetName);
440
+ let sourceDir = targetPath;
441
+ if (spec?.sources) {
442
+ const specSourceDir = join(targetPath, spec.sources);
443
+ if (existsSync(specSourceDir)) {
444
+ sourceDir = specSourceDir;
445
+ }
446
+ }
447
+ const files = [];
448
+ this.#collectSourceFiles(sourceDir, targetPath, files);
449
+ return files;
450
+ }
451
+ // ── getDependencyGraph ────────────────────────────
452
+ async getDependencyGraph() {
453
+ if (!this.#parsedConfig) {
454
+ return { nodes: this.#targets.map((t) => t.name), edges: [] };
455
+ }
456
+ const config = this.#parsedConfig;
457
+ const nodes = [];
458
+ const edges = [];
459
+ const nodeIds = new Set();
460
+ // 宿主应用节点
461
+ if (config.hostApp) {
462
+ const hostId = config.hostApp.name;
463
+ nodes.push({
464
+ id: hostId,
465
+ label: hostId,
466
+ type: 'host',
467
+ version: config.hostApp.version,
468
+ });
469
+ nodeIds.add(hostId);
470
+ }
471
+ // 遍历所有层级,添加模块节点
472
+ for (const layer of config.layers) {
473
+ for (const mod of layer.modules) {
474
+ if (nodeIds.has(mod.name)) {
475
+ continue;
476
+ }
477
+ nodeIds.add(mod.name);
478
+ nodes.push({
479
+ id: mod.name,
480
+ label: mod.name,
481
+ type: mod.isLocal ? 'local' : 'external',
482
+ layer: layer.name,
483
+ version: mod.version || undefined,
484
+ group: mod.group || undefined,
485
+ fullPath: mod.isLocal && mod.localPath && this.#projectRoot
486
+ ? join(this.#projectRoot, mod.localPath)
487
+ : undefined,
488
+ });
489
+ }
490
+ }
491
+ // 全局依赖
492
+ for (const mod of config.globalDependencies) {
493
+ if (nodeIds.has(mod.name)) {
494
+ continue;
495
+ }
496
+ nodeIds.add(mod.name);
497
+ nodes.push({
498
+ id: mod.name,
499
+ label: mod.name,
500
+ type: mod.isLocal ? 'local' : 'external',
501
+ version: mod.version || undefined,
502
+ group: mod.group || undefined,
503
+ fullPath: mod.isLocal && mod.localPath && this.#projectRoot
504
+ ? join(this.#projectRoot, mod.localPath)
505
+ : undefined,
506
+ });
507
+ }
508
+ // 从 boxspec 依赖声明生成边
509
+ for (const [moduleName, spec] of this.#moduleSpecs) {
510
+ for (const depName of spec.dependencies) {
511
+ // 确保依赖目标存在于节点列表中
512
+ if (!nodeIds.has(depName)) {
513
+ nodeIds.add(depName);
514
+ nodes.push({
515
+ id: depName,
516
+ label: depName,
517
+ type: 'external',
518
+ indirect: true,
519
+ });
520
+ }
521
+ edges.push({
522
+ from: moduleName,
523
+ to: depName,
524
+ type: 'depends_on',
525
+ });
526
+ }
527
+ }
528
+ // 宿主应用 → 所有本地模块的 contains 关系
529
+ if (config.hostApp) {
530
+ for (const layer of config.layers) {
531
+ for (const mod of layer.modules) {
532
+ if (mod.isLocal) {
533
+ edges.push({
534
+ from: config.hostApp.name,
535
+ to: mod.name,
536
+ type: 'contains',
537
+ });
538
+ }
539
+ }
540
+ }
541
+ }
542
+ // 层级元数据
543
+ const layers = config.layers.map((l) => ({
544
+ name: l.name,
545
+ order: l.order,
546
+ accessibleLayers: l.accessibleLayers,
547
+ }));
548
+ return { nodes, edges, layers };
549
+ }
550
+ // ── Private: Ruby DSL 加载 ─────────────────────────
551
+ #loadRubyDsl(projectRoot) {
552
+ // 读取 Boxfile
553
+ const boxfilePath = join(projectRoot, 'Boxfile');
554
+ if (!existsSync(boxfilePath)) {
555
+ return;
556
+ }
557
+ let content;
558
+ try {
559
+ content = readFileSync(boxfilePath, 'utf8');
560
+ }
561
+ catch {
562
+ return;
563
+ }
564
+ // 解析 Boxfile
565
+ this.#parsedConfig = parseBoxfile(content);
566
+ // 尝试合并 Boxfile.local 覆盖
567
+ this.#mergeLocalOverrides(projectRoot);
568
+ // 遍历本地模块,解析 spec 文件
569
+ const allModules = [
570
+ ...this.#parsedConfig.layers.flatMap((l) => l.modules),
571
+ ...this.#parsedConfig.globalDependencies,
572
+ ];
573
+ for (const mod of allModules) {
574
+ if (!mod.isLocal || !mod.localPath) {
575
+ continue;
576
+ }
577
+ const modulePath = join(projectRoot, mod.localPath);
578
+ if (!existsSync(modulePath)) {
579
+ continue;
580
+ }
581
+ // 查找 spec 文件
582
+ const specPath = this.#findSpecFile(modulePath, mod.name);
583
+ if (specPath) {
584
+ try {
585
+ const specContent = readFileSync(specPath, 'utf8');
586
+ const spec = parseModuleSpec(specContent);
587
+ this.#moduleSpecs.set(mod.name, spec);
588
+ }
589
+ catch {
590
+ /* skip unreadable spec */
591
+ }
592
+ }
593
+ }
594
+ // 构建 targets(仅 local 模块 + 宿主应用)
595
+ this.#buildTargets(projectRoot);
596
+ }
597
+ /**
598
+ * 合并 Boxfile.local 中的覆盖配置
599
+ * Boxfile.local 中 :path 覆盖可以将远程依赖切换为本地源码
600
+ */
601
+ #mergeLocalOverrides(projectRoot) {
602
+ const localPath = join(projectRoot, 'Boxfile.local');
603
+ if (!existsSync(localPath)) {
604
+ return;
605
+ }
606
+ try {
607
+ const localContent = readFileSync(localPath, 'utf8');
608
+ const localConfig = parseBoxfile(localContent);
609
+ if (!this.#parsedConfig) {
610
+ return;
611
+ }
612
+ // 合并本地覆盖:将 Boxfile.local 中的 local module 覆盖到主配置
613
+ const allLocalModules = localConfig.layers.flatMap((l) => l.modules);
614
+ for (const localMod of allLocalModules) {
615
+ if (!localMod.isLocal) {
616
+ continue;
617
+ }
618
+ // 查找主配置中的同名模块并覆盖
619
+ const configLayers = this.#parsedConfig.layers;
620
+ for (const layer of configLayers) {
621
+ const existingIdx = layer.modules.findIndex((m) => m.name === localMod.name);
622
+ if (existingIdx >= 0) {
623
+ layer.modules[existingIdx] = { ...layer.modules[existingIdx], ...localMod };
624
+ }
625
+ }
626
+ }
627
+ }
628
+ catch {
629
+ /* skip */
630
+ }
631
+ }
632
+ /**
633
+ * 在模块目录中查找 spec 文件
634
+ * 查找顺序: ModuleName.boxspec → ModuleName.podspec → 任意 *.boxspec → 任意 *.podspec
635
+ */
636
+ #findSpecFile(modulePath, moduleName) {
637
+ // 精确匹配
638
+ for (const ext of ['.boxspec', '.podspec']) {
639
+ const exactPath = join(modulePath, `${moduleName}${ext}`);
640
+ if (existsSync(exactPath)) {
641
+ return exactPath;
642
+ }
643
+ }
644
+ // 模糊匹配
645
+ try {
646
+ const entries = readdirSync(modulePath);
647
+ for (const entry of entries) {
648
+ if (entry.endsWith('.boxspec') || entry.endsWith('.podspec')) {
649
+ return join(modulePath, entry);
650
+ }
651
+ }
652
+ }
653
+ catch {
654
+ /* skip */
655
+ }
656
+ return null;
657
+ }
658
+ /**
659
+ * 从解析结果构建 Target 列表
660
+ * 仅包含本地模块和宿主应用(有源码可收集的目标)
661
+ */
662
+ #buildTargets(projectRoot) {
663
+ if (!this.#parsedConfig) {
664
+ return;
665
+ }
666
+ const config = this.#parsedConfig;
667
+ const primaryLang = this.#matchedSystem?.language[0] || 'objectivec';
668
+ // 宿主应用
669
+ if (config.hostApp) {
670
+ const hostDir = join(projectRoot, config.hostApp.name);
671
+ if (existsSync(hostDir)) {
672
+ this.#targets.push({
673
+ name: config.hostApp.name,
674
+ path: hostDir,
675
+ type: 'application',
676
+ language: primaryLang,
677
+ metadata: {
678
+ layer: 'Application',
679
+ version: config.hostApp.version,
680
+ },
681
+ });
682
+ }
683
+ }
684
+ // 所有层级中的本地模块
685
+ for (const layer of config.layers) {
686
+ for (const mod of layer.modules) {
687
+ if (!mod.isLocal || !mod.localPath) {
688
+ continue;
689
+ }
690
+ const modulePath = join(projectRoot, mod.localPath);
691
+ if (!existsSync(modulePath)) {
692
+ continue;
693
+ }
694
+ this.#targets.push({
695
+ name: mod.name,
696
+ path: modulePath,
697
+ type: 'library',
698
+ language: primaryLang,
699
+ metadata: {
700
+ layer: layer.name,
701
+ version: mod.version,
702
+ group: mod.group,
703
+ specFile: this.#moduleSpecs.has(mod.name),
704
+ },
705
+ });
706
+ }
707
+ }
708
+ // 全局本地模块
709
+ for (const mod of config.globalDependencies) {
710
+ if (!mod.isLocal || !mod.localPath) {
711
+ continue;
712
+ }
713
+ const modulePath = join(projectRoot, mod.localPath);
714
+ if (!existsSync(modulePath)) {
715
+ continue;
716
+ }
717
+ // 避免重复
718
+ if (this.#targets.some((t) => t.name === mod.name)) {
719
+ continue;
720
+ }
721
+ this.#targets.push({
722
+ name: mod.name,
723
+ path: modulePath,
724
+ type: 'library',
725
+ language: primaryLang,
726
+ metadata: {
727
+ version: mod.version,
728
+ group: mod.group,
729
+ specFile: this.#moduleSpecs.has(mod.name),
730
+ },
731
+ });
732
+ }
733
+ }
734
+ // ── Private: YAML 加载 (XcodeGen) ──────────────────
735
+ #loadYaml(projectRoot) {
736
+ const system = this.#matchedSystem;
737
+ // 查找可用的 YAML 配置文件
738
+ let yamlContent = null;
739
+ for (const marker of system.markers) {
740
+ const markerPath = join(projectRoot, marker);
741
+ if (existsSync(markerPath)) {
742
+ try {
743
+ yamlContent = readFileSync(markerPath, 'utf-8');
744
+ break;
745
+ }
746
+ catch {
747
+ /* 跳过不可读文件 */
748
+ }
749
+ }
750
+ }
751
+ if (!yamlContent) {
752
+ this.#loadHeuristic(projectRoot);
753
+ return;
754
+ }
755
+ // Melos 项目走专用加载路径
756
+ if (system.id === 'melos') {
757
+ this.#loadMelos(projectRoot, yamlContent);
758
+ return;
759
+ }
760
+ // 解析 project.yml
761
+ const config = parseXcodeGenProject(yamlContent);
762
+ this.#parsedConfig = config;
763
+ const primaryLang = system.language[0];
764
+ // 遍历 layers → targets
765
+ for (const layer of config.layers) {
766
+ for (const mod of layer.modules) {
767
+ if (!mod.isLocal) {
768
+ continue;
769
+ }
770
+ const modulePath = mod.localPath
771
+ ? join(projectRoot, mod.localPath)
772
+ : join(projectRoot, mod.name);
773
+ this.#targets.push({
774
+ name: mod.name,
775
+ path: modulePath,
776
+ type: layer.name === 'App' ? 'application' : 'library',
777
+ language: primaryLang,
778
+ metadata: {
779
+ layer: layer.name,
780
+ version: mod.version,
781
+ group: mod.group,
782
+ },
783
+ });
784
+ // 为每个 target 构建 ParsedModuleSpec
785
+ const targetSpec = parseXcodeGenTarget(mod.name, yamlContent);
786
+ if (targetSpec) {
787
+ this.#moduleSpecs.set(mod.name, targetSpec);
788
+ }
789
+ }
790
+ }
791
+ // 全局 SPM 包依赖 → targets(标记为外部)
792
+ for (const dep of config.globalDependencies) {
793
+ if (this.#targets.some((t) => t.name === dep.name)) {
794
+ }
795
+ // 外部包不加入 targets,留给 getDependencyGraph 处理
796
+ }
797
+ }
798
+ // ── Private: Melos 加载 ──────────────────────────────
799
+ #loadMelos(projectRoot, yamlContent) {
800
+ const melos = parseMelosProject(yamlContent);
801
+ // 使用 glob 模式扫描 pubspec.yaml 文件
802
+ const pubspecFiles = this.#findBuildFiles(projectRoot, ['pubspec.yaml']);
803
+ for (const pf of pubspecFiles) {
804
+ // 排除根目录 pubspec
805
+ if (pf === join(projectRoot, 'pubspec.yaml')) {
806
+ continue;
807
+ }
808
+ try {
809
+ const content = readFileSync(pf, 'utf-8');
810
+ const nameMatch = content.match(/^name:\s*(\S+)/m);
811
+ if (nameMatch) {
812
+ const modDir = join(pf, '..');
813
+ const relPath = relative(projectRoot, modDir);
814
+ this.#targets.push({
815
+ name: nameMatch[1],
816
+ path: modDir,
817
+ type: 'library',
818
+ language: 'dart',
819
+ metadata: {
820
+ melosProject: melos.name,
821
+ pubspecPath: relative(projectRoot, pf),
822
+ packageDir: relPath,
823
+ },
824
+ });
825
+ }
826
+ }
827
+ catch {
828
+ /* skip */
829
+ }
830
+ }
831
+ }
832
+ // ── Private: Starlark 加载 (Bazel/Buck2/Pants) ──────
833
+ #loadStarlark(projectRoot) {
834
+ const system = this.#matchedSystem;
835
+ const specPattern = system.moduleSpecPattern ?? 'BUILD';
836
+ const buildFileNames = specPattern === 'BUCK' ? ['BUCK'] : ['BUILD.bazel', 'BUILD'];
837
+ // 扫描所有 BUILD 文件
838
+ const buildFiles = this.#findBuildFiles(projectRoot, buildFileNames);
839
+ const allTargets = [];
840
+ const detectedLanguages = new Set();
841
+ for (const buildFile of buildFiles) {
842
+ try {
843
+ const content = readFileSync(buildFile, 'utf-8');
844
+ const parsed = parseStarlarkBuildFile(content);
845
+ const dirRelative = relative(projectRoot, buildFile).replace(/\/[^/]+$/, '') || '.';
846
+ for (const target of parsed.targets) {
847
+ allTargets.push(target);
848
+ // 语言推断
849
+ const lang = RULE_TO_LANGUAGE[target.rule];
850
+ if (lang) {
851
+ detectedLanguages.add(lang);
852
+ }
853
+ const modulePath = join(projectRoot, dirRelative);
854
+ this.#targets.push({
855
+ name: target.name,
856
+ path: modulePath,
857
+ type: target.rule.includes('binary') || target.rule.includes('executable')
858
+ ? 'application'
859
+ : 'library',
860
+ language: lang ?? 'unknown',
861
+ metadata: {
862
+ rule: target.rule,
863
+ visibility: target.visibility,
864
+ buildFile: relative(projectRoot, buildFile),
865
+ },
866
+ });
867
+ }
868
+ }
869
+ catch {
870
+ /* skip unreadable BUILD files */
871
+ }
872
+ }
873
+ }
874
+ #findBuildFiles(dir, names, depth = 0) {
875
+ if (depth > 8) {
876
+ return [];
877
+ }
878
+ const results = [];
879
+ try {
880
+ const entries = readdirSync(dir, { withFileTypes: true });
881
+ for (const entry of entries) {
882
+ if (entry.name.startsWith('.') || EXCLUDE_DIRS.has(entry.name)) {
883
+ continue;
884
+ }
885
+ const fullPath = join(dir, entry.name);
886
+ if (entry.isFile() && names.includes(entry.name)) {
887
+ results.push(fullPath);
888
+ }
889
+ else if (entry.isDirectory()) {
890
+ results.push(...this.#findBuildFiles(fullPath, names, depth + 1));
891
+ }
892
+ }
893
+ }
894
+ catch {
895
+ /* skip */
896
+ }
897
+ return results;
898
+ }
899
+ // ── Private: Gradle DSL 加载 ─────────────────────────
900
+ #loadGradleDsl(projectRoot) {
901
+ // 查找 settings.gradle.kts 或 settings.gradle
902
+ let settingsContent = null;
903
+ for (const name of ['settings.gradle.kts', 'settings.gradle']) {
904
+ const settingsPath = join(projectRoot, name);
905
+ if (existsSync(settingsPath)) {
906
+ try {
907
+ settingsContent = readFileSync(settingsPath, 'utf-8');
908
+ break;
909
+ }
910
+ catch {
911
+ /* skip */
912
+ }
913
+ }
914
+ }
915
+ if (!settingsContent) {
916
+ this.#loadHeuristic(projectRoot);
917
+ return;
918
+ }
919
+ const project = parseGradleProject(settingsContent);
920
+ const primaryLang = this.#matchedSystem?.language[0] || 'kotlin';
921
+ // 解析每个模块的 build.gradle.kts
922
+ for (const mod of project.includedModules) {
923
+ const modulePath = join(projectRoot, mod.directory);
924
+ if (!existsSync(modulePath)) {
925
+ continue;
926
+ }
927
+ // 读取 build.gradle.kts 获取 dependencies 和 plugins
928
+ for (const buildName of ['build.gradle.kts', 'build.gradle']) {
929
+ const buildPath = join(modulePath, buildName);
930
+ if (existsSync(buildPath)) {
931
+ try {
932
+ const buildContent = readFileSync(buildPath, 'utf-8');
933
+ const updatedMod = parseGradleProject(buildContent, mod);
934
+ // 更新 module 的 convention plugin 和 dependencies
935
+ mod.conventionPlugin =
936
+ updatedMod.includedModules[0]?.conventionPlugin ?? mod.conventionPlugin;
937
+ mod.dependencies = updatedMod.includedModules[0]?.dependencies ?? mod.dependencies;
938
+ }
939
+ catch {
940
+ /* skip */
941
+ }
942
+ break;
943
+ }
944
+ }
945
+ const inferredRole = mod.conventionPlugin
946
+ ? inferConventionRole(mod.conventionPlugin)
947
+ : undefined;
948
+ this.#targets.push({
949
+ name: mod.path,
950
+ path: modulePath,
951
+ type: mod.path === ':app' ? 'application' : 'library',
952
+ language: primaryLang,
953
+ metadata: {
954
+ gradlePath: mod.path,
955
+ conventionPlugin: mod.conventionPlugin,
956
+ conventionRole: inferredRole,
957
+ },
958
+ });
959
+ }
960
+ }
961
+ // ── Private: CMake 加载 ──────────────────────────────
962
+ #loadCMake(projectRoot) {
963
+ const cmakePath = join(projectRoot, 'CMakeLists.txt');
964
+ if (!existsSync(cmakePath)) {
965
+ this.#loadHeuristic(projectRoot);
966
+ return;
967
+ }
968
+ let content;
969
+ try {
970
+ content = readFileSync(cmakePath, 'utf-8');
971
+ }
972
+ catch {
973
+ return;
974
+ }
975
+ const project = parseCMakeProject(content);
976
+ const primaryLang = this.#matchedSystem?.language[0] || 'cpp';
977
+ // 主目标
978
+ for (const target of project.targets) {
979
+ this.#targets.push({
980
+ name: target.name,
981
+ path: projectRoot,
982
+ type: target.type === 'executable' ? 'application' : 'library',
983
+ language: primaryLang,
984
+ metadata: {
985
+ cmakeType: target.type,
986
+ },
987
+ });
988
+ }
989
+ // 递归解析子目录的 CMakeLists.txt
990
+ for (const subdir of project.subdirectories) {
991
+ const subdirPath = join(projectRoot, subdir);
992
+ const subdirCmakePath = join(subdirPath, 'CMakeLists.txt');
993
+ if (!existsSync(subdirCmakePath)) {
994
+ continue;
995
+ }
996
+ try {
997
+ const subcontent = readFileSync(subdirCmakePath, 'utf-8');
998
+ const subproject = parseCMakeProject(subcontent);
999
+ for (const target of subproject.targets) {
1000
+ this.#targets.push({
1001
+ name: target.name,
1002
+ path: subdirPath,
1003
+ type: target.type === 'executable' ? 'application' : 'library',
1004
+ language: primaryLang,
1005
+ metadata: {
1006
+ cmakeType: target.type,
1007
+ subdirectory: subdir,
1008
+ },
1009
+ });
1010
+ }
1011
+ }
1012
+ catch {
1013
+ /* skip */
1014
+ }
1015
+ }
1016
+ }
1017
+ // ── Private: JSON Config 加载 (Nx/Flutter/RN) ────────
1018
+ #loadJsonConfig(projectRoot) {
1019
+ const system = this.#matchedSystem;
1020
+ switch (system.id) {
1021
+ case 'nx-monorepo':
1022
+ this.#loadNx(projectRoot);
1023
+ break;
1024
+ case 'flutter-add-to-app':
1025
+ this.#loadFlutterAddToApp(projectRoot);
1026
+ break;
1027
+ case 'react-native-hybrid':
1028
+ this.#loadReactNative(projectRoot);
1029
+ break;
1030
+ default:
1031
+ this.#loadHeuristic(projectRoot);
1032
+ }
1033
+ }
1034
+ #loadNx(projectRoot) {
1035
+ const nxJsonPath = join(projectRoot, 'nx.json');
1036
+ if (!existsSync(nxJsonPath)) {
1037
+ return;
1038
+ }
1039
+ // 扫描所有 project.json 文件
1040
+ const projectJsonFiles = this.#findBuildFiles(projectRoot, ['project.json']);
1041
+ const projects = [];
1042
+ for (const pjFile of projectJsonFiles) {
1043
+ try {
1044
+ const content = readFileSync(pjFile, 'utf-8');
1045
+ const parsed = parseNxWorkspace(content);
1046
+ for (const proj of parsed.projects) {
1047
+ projects.push(proj);
1048
+ const modulePath = join(projectRoot, proj.root);
1049
+ this.#targets.push({
1050
+ name: proj.name,
1051
+ path: modulePath,
1052
+ type: proj.projectType === 'application' ? 'application' : 'library',
1053
+ language: 'typescript',
1054
+ metadata: {
1055
+ tags: proj.tags,
1056
+ nxProjectType: proj.projectType,
1057
+ },
1058
+ });
1059
+ }
1060
+ }
1061
+ catch {
1062
+ /* skip */
1063
+ }
1064
+ }
1065
+ }
1066
+ #loadFlutterAddToApp(projectRoot) {
1067
+ // 解析 .flutter-plugins-dependencies
1068
+ const depsPath = join(projectRoot, '.flutter-plugins-dependencies');
1069
+ if (existsSync(depsPath)) {
1070
+ try {
1071
+ const content = readFileSync(depsPath, 'utf-8');
1072
+ const parsed = parseFlutterPluginsDeps(content);
1073
+ for (const plugin of parsed.plugins) {
1074
+ this.#targets.push({
1075
+ name: plugin.name,
1076
+ path: plugin.path,
1077
+ type: 'library',
1078
+ language: 'dart',
1079
+ metadata: {
1080
+ platform: plugin.platform,
1081
+ bridgeType: 'flutter-engine',
1082
+ },
1083
+ });
1084
+ }
1085
+ }
1086
+ catch {
1087
+ /* skip */
1088
+ }
1089
+ }
1090
+ // 查找嵌入的 pubspec.yaml
1091
+ const pubspecFiles = this.#findBuildFiles(projectRoot, ['pubspec.yaml']);
1092
+ for (const pf of pubspecFiles) {
1093
+ // 排除根目录的 pubspec(交给 DartDiscoverer 处理)
1094
+ if (pf === join(projectRoot, 'pubspec.yaml')) {
1095
+ continue;
1096
+ }
1097
+ try {
1098
+ const content = readFileSync(pf, 'utf-8');
1099
+ const nameMatch = content.match(/^name:\s*(\S+)/m);
1100
+ if (nameMatch) {
1101
+ const modDir = join(pf, '..');
1102
+ this.#targets.push({
1103
+ name: nameMatch[1],
1104
+ path: modDir,
1105
+ type: 'library',
1106
+ language: 'dart',
1107
+ metadata: {
1108
+ pubspecPath: relative(projectRoot, pf),
1109
+ },
1110
+ });
1111
+ }
1112
+ }
1113
+ catch {
1114
+ /* skip */
1115
+ }
1116
+ }
1117
+ }
1118
+ #loadReactNative(projectRoot) {
1119
+ const pkgJsonPath = join(projectRoot, 'package.json');
1120
+ if (!existsSync(pkgJsonPath)) {
1121
+ return;
1122
+ }
1123
+ try {
1124
+ const content = readFileSync(pkgJsonPath, 'utf-8');
1125
+ const parsed = parseReactNativeProject(content);
1126
+ if (parsed.isReactNative) {
1127
+ this.#targets.push({
1128
+ name: parsed.name,
1129
+ path: projectRoot,
1130
+ type: 'application',
1131
+ language: 'typescript',
1132
+ metadata: {
1133
+ rnVersion: parsed.rnVersion,
1134
+ bridgeType: 'native-module',
1135
+ },
1136
+ });
1137
+ }
1138
+ }
1139
+ catch {
1140
+ /* skip */
1141
+ }
1142
+ }
1143
+ // ── Private: 启发式加载 ────────────────────────────
1144
+ #loadHeuristic(projectRoot) {
1145
+ // 扫描根目录中可能包含模块的目录
1146
+ try {
1147
+ const entries = readdirSync(projectRoot, { withFileTypes: true });
1148
+ for (const entry of entries) {
1149
+ if (!entry.isDirectory() || entry.name.startsWith('.') || EXCLUDE_DIRS.has(entry.name)) {
1150
+ continue;
1151
+ }
1152
+ // 检查是否是模块容器目录
1153
+ if (/^(Local)?Modules?$|^Packages$/i.test(entry.name)) {
1154
+ this.#scanModuleDirectory(join(projectRoot, entry.name));
1155
+ }
1156
+ }
1157
+ }
1158
+ catch {
1159
+ /* skip */
1160
+ }
1161
+ }
1162
+ /**
1163
+ * 扫描模块容器目录,每个有 spec 文件或源码的子目录视为一个模块
1164
+ */
1165
+ #scanModuleDirectory(containerDir) {
1166
+ try {
1167
+ const entries = readdirSync(containerDir, { withFileTypes: true });
1168
+ for (const entry of entries) {
1169
+ if (!entry.isDirectory() || entry.name.startsWith('.')) {
1170
+ continue;
1171
+ }
1172
+ const modulePath = join(containerDir, entry.name);
1173
+ // 查找 spec 文件
1174
+ const specPath = this.#findSpecFile(modulePath, entry.name);
1175
+ if (specPath) {
1176
+ try {
1177
+ const specContent = readFileSync(specPath, 'utf8');
1178
+ const spec = parseModuleSpec(specContent);
1179
+ this.#moduleSpecs.set(entry.name, spec);
1180
+ }
1181
+ catch {
1182
+ /* skip */
1183
+ }
1184
+ }
1185
+ // 检查目录是否包含源码文件
1186
+ if (specPath || this.#hasSourceFiles(modulePath)) {
1187
+ this.#targets.push({
1188
+ name: entry.name,
1189
+ path: modulePath,
1190
+ type: 'library',
1191
+ language: 'objectivec',
1192
+ metadata: { specFile: specPath !== null },
1193
+ });
1194
+ }
1195
+ }
1196
+ }
1197
+ catch {
1198
+ /* skip */
1199
+ }
1200
+ }
1201
+ // ── Private: 文件工具 ──────────────────────────────
1202
+ /**
1203
+ * 递归收集源码文件
1204
+ */
1205
+ #collectSourceFiles(dir, rootDir, files, depth = 0) {
1206
+ if (depth > 15 || files.length >= 500) {
1207
+ return;
1208
+ }
1209
+ try {
1210
+ const entries = readdirSync(dir, { withFileTypes: true });
1211
+ for (const entry of entries) {
1212
+ if (entry.name.startsWith('.')) {
1213
+ continue;
1214
+ }
1215
+ if (EXCLUDE_DIRS.has(entry.name)) {
1216
+ continue;
1217
+ }
1218
+ const fullPath = join(dir, entry.name);
1219
+ if (entry.isDirectory()) {
1220
+ this.#collectSourceFiles(fullPath, rootDir, files, depth + 1);
1221
+ }
1222
+ else if (entry.isFile()) {
1223
+ const ext = extname(entry.name);
1224
+ if (SOURCE_EXTENSIONS.has(ext) || LanguageService.sourceExts.has(ext)) {
1225
+ const lang = LanguageService.inferLang(entry.name) || 'unknown';
1226
+ files.push({
1227
+ name: entry.name,
1228
+ path: fullPath,
1229
+ relativePath: relative(rootDir, fullPath),
1230
+ language: lang,
1231
+ });
1232
+ }
1233
+ }
1234
+ if (files.length >= 500) {
1235
+ return;
1236
+ }
1237
+ }
1238
+ }
1239
+ catch {
1240
+ /* skip */
1241
+ }
1242
+ }
1243
+ /**
1244
+ * 检查目录中是否存在源码文件(浅层检查)
1245
+ */
1246
+ #hasSourceFiles(dir, depth = 0) {
1247
+ if (depth > 3) {
1248
+ return false;
1249
+ }
1250
+ try {
1251
+ const entries = readdirSync(dir, { withFileTypes: true });
1252
+ for (const entry of entries) {
1253
+ if (entry.name.startsWith('.')) {
1254
+ continue;
1255
+ }
1256
+ if (entry.isFile()) {
1257
+ const ext = extname(entry.name);
1258
+ if (SOURCE_EXTENSIONS.has(ext)) {
1259
+ return true;
1260
+ }
1261
+ }
1262
+ else if (entry.isDirectory() && !EXCLUDE_DIRS.has(entry.name)) {
1263
+ if (this.#hasSourceFiles(join(dir, entry.name), depth + 1)) {
1264
+ return true;
1265
+ }
1266
+ }
1267
+ }
1268
+ }
1269
+ catch {
1270
+ /* skip */
1271
+ }
1272
+ return false;
1273
+ }
1274
+ }
1275
+ // ── Module-level helpers ────────────────────────────
1276
+ /**
1277
+ * 计算目录下包含 spec 文件的子目录数量
1278
+ */
1279
+ function countSubdirsWithSpecs(containerDir) {
1280
+ let count = 0;
1281
+ try {
1282
+ const entries = readdirSync(containerDir, { withFileTypes: true });
1283
+ for (const entry of entries) {
1284
+ if (!entry.isDirectory() || entry.name.startsWith('.')) {
1285
+ continue;
1286
+ }
1287
+ try {
1288
+ const subEntries = readdirSync(join(containerDir, entry.name));
1289
+ const hasSpec = subEntries.some((e) => e.endsWith('.boxspec') || e.endsWith('.podspec'));
1290
+ if (hasSpec) {
1291
+ count++;
1292
+ }
1293
+ }
1294
+ catch {
1295
+ /* skip */
1296
+ }
1297
+ }
1298
+ }
1299
+ catch {
1300
+ /* skip */
1301
+ }
1302
+ return count;
1303
+ }