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
@@ -0,0 +1,466 @@
1
+ /**
2
+ * @module NodeDiscoverer
3
+ * @description TypeScript / JavaScript 项目结构发现器
4
+ *
5
+ * 检测信号: package.json, tsconfig.json, node_modules/
6
+ * 支持: 单包、Monorepo (npm/pnpm/yarn workspaces, lerna)
7
+ */
8
+
9
+ import { existsSync, readdirSync, readFileSync } from 'node:fs';
10
+ import { basename, extname, join, relative, resolve } from 'node:path';
11
+ import { inferLang } from '../../external/mcp/handlers/LanguageExtensions.js';
12
+ import { ProjectDiscoverer } from './ProjectDiscoverer.js';
13
+
14
+ const SOURCE_EXTENSIONS = new Set([
15
+ '.ts',
16
+ '.tsx',
17
+ '.js',
18
+ '.jsx',
19
+ '.mjs',
20
+ '.cjs',
21
+ '.vue',
22
+ '.svelte',
23
+ ]);
24
+ const EXCLUDE_DIRS = new Set([
25
+ 'node_modules',
26
+ '.git',
27
+ 'dist',
28
+ 'build',
29
+ 'out',
30
+ '.next',
31
+ '.nuxt',
32
+ 'coverage',
33
+ '.turbo',
34
+ '.cache',
35
+ ]);
36
+
37
+ export class NodeDiscoverer extends ProjectDiscoverer {
38
+ #projectRoot = null;
39
+ #packageJson = null;
40
+ #targets = [];
41
+ #depGraph = { nodes: [], edges: [] };
42
+
43
+ get id() {
44
+ return 'node';
45
+ }
46
+ get displayName() {
47
+ return 'Node.js (npm/pnpm/yarn)';
48
+ }
49
+
50
+ async detect(projectRoot) {
51
+ let confidence = 0;
52
+ const reasons = [];
53
+
54
+ if (existsSync(join(projectRoot, 'package.json'))) {
55
+ confidence = 0.9;
56
+ reasons.push('package.json exists');
57
+ }
58
+ if (existsSync(join(projectRoot, 'tsconfig.json'))) {
59
+ confidence = Math.max(confidence, 0.9);
60
+ confidence += 0.05;
61
+ reasons.push('tsconfig.json exists');
62
+ }
63
+ if (existsSync(join(projectRoot, 'node_modules'))) {
64
+ confidence += 0.05;
65
+ reasons.push('node_modules/ exists');
66
+ }
67
+
68
+ // ── 降低 confidence:当检测到其他生态的强标记时 ──────────
69
+ // 这些项目使用 package.json 仅作为前端/工具链辅助,主语言在别的生态
70
+ if (confidence > 0) {
71
+ // Ruby 生态:Gemfile/Rakefile 的存在几乎确定是 Rails/Sinatra 项目
72
+ // 即使有 tsconfig.json 也只是说明前端构建链使用 TS(Ember/React),
73
+ // 主语言仍然是 Ruby,因此始终重度降级
74
+ const rubyMarkers = ['Gemfile', 'Rakefile'];
75
+ const hasRubyMarker = rubyMarkers.some((f) => existsSync(join(projectRoot, f)));
76
+ if (hasRubyMarker) {
77
+ confidence *= 0.05;
78
+ reasons.push('Ruby marker found (Gemfile/Rakefile) — confidence heavily reduced');
79
+ } else {
80
+ // 其他生态标记:tsconfig 存在则可能是全栈项目,保留较高 confidence
81
+ const otherMarkers = [
82
+ { files: ['Cargo.toml'], lang: 'Rust' },
83
+ { files: ['go.mod'], lang: 'Go' },
84
+ ];
85
+ for (const marker of otherMarkers) {
86
+ if (marker.files.some((f) => existsSync(join(projectRoot, f)))) {
87
+ const hasTsConfig = existsSync(join(projectRoot, 'tsconfig.json'));
88
+ if (hasTsConfig) {
89
+ confidence *= 0.5;
90
+ reasons.push(
91
+ `${marker.lang} marker found (but tsconfig present) — confidence moderately reduced`
92
+ );
93
+ } else {
94
+ confidence *= 0.05;
95
+ reasons.push(
96
+ `${marker.lang} marker found — confidence heavily reduced`
97
+ );
98
+ }
99
+ break;
100
+ }
101
+ }
102
+ }
103
+ }
104
+
105
+ return {
106
+ match: confidence > 0,
107
+ confidence: Math.min(confidence, 1.0),
108
+ reason: reasons.join(', ') || 'No Node.js markers found',
109
+ };
110
+ }
111
+
112
+ async load(projectRoot) {
113
+ this.#projectRoot = projectRoot;
114
+ this.#targets = [];
115
+ this.#depGraph = { nodes: [], edges: [] };
116
+
117
+ // 读取 package.json
118
+ const pkgPath = join(projectRoot, 'package.json');
119
+ if (existsSync(pkgPath)) {
120
+ try {
121
+ this.#packageJson = JSON.parse(readFileSync(pkgPath, 'utf8'));
122
+ } catch {
123
+ this.#packageJson = {};
124
+ }
125
+ } else {
126
+ this.#packageJson = {};
127
+ }
128
+
129
+ // 检测 monorepo workspaces
130
+ const workspacePaths = this.#resolveWorkspaces(projectRoot);
131
+
132
+ if (workspacePaths.length > 0) {
133
+ // Monorepo 模式: 每个 workspace 是一个 Target
134
+ for (const wsPath of workspacePaths) {
135
+ const wsAbsPath = resolve(projectRoot, wsPath);
136
+ if (!existsSync(wsAbsPath)) {
137
+ continue;
138
+ }
139
+ const wsPkgPath = join(wsAbsPath, 'package.json');
140
+ let wsPkg = {};
141
+ if (existsSync(wsPkgPath)) {
142
+ try {
143
+ wsPkg = JSON.parse(readFileSync(wsPkgPath, 'utf8'));
144
+ } catch {
145
+ /* skip */
146
+ }
147
+ }
148
+
149
+ const framework = this.#detectFramework(wsPkg);
150
+ const name = wsPkg.name || basename(wsPath);
151
+ const type = this.#inferTargetType(wsPkg);
152
+
153
+ this.#targets.push({
154
+ name,
155
+ path: wsAbsPath,
156
+ type,
157
+ language: 'typescript',
158
+ framework,
159
+ metadata: { packageJson: wsPkg },
160
+ });
161
+ this.#depGraph.nodes.push(name);
162
+ }
163
+
164
+ // 构建 workspace 间依赖
165
+ this.#buildWorkspaceDeps(workspacePaths);
166
+ } else {
167
+ // 单包模式
168
+ const framework = this.#detectFramework(this.#packageJson);
169
+ const name = this.#packageJson.name || basename(projectRoot);
170
+ const type = this.#inferTargetType(this.#packageJson);
171
+
172
+ this.#targets.push({
173
+ name,
174
+ path: projectRoot,
175
+ type,
176
+ language: 'typescript',
177
+ framework,
178
+ metadata: { packageJson: this.#packageJson },
179
+ });
180
+ this.#depGraph.nodes.push(name);
181
+ }
182
+
183
+ // 添加外部依赖到依赖图
184
+ this.#addExternalDeps();
185
+ }
186
+
187
+ async listTargets() {
188
+ return this.#targets;
189
+ }
190
+
191
+ async getTargetFiles(target) {
192
+ const targetPath =
193
+ typeof target === 'string'
194
+ ? this.#targets.find((t) => t.name === target)?.path || this.#projectRoot
195
+ : target.path;
196
+
197
+ if (!targetPath || !existsSync(targetPath)) {
198
+ return [];
199
+ }
200
+
201
+ const files = [];
202
+ this.#collectFiles(targetPath, targetPath, files);
203
+ return files;
204
+ }
205
+
206
+ async getDependencyGraph() {
207
+ return this.#depGraph;
208
+ }
209
+
210
+ // ── 内部实现 ──
211
+
212
+ #resolveWorkspaces(projectRoot) {
213
+ const paths = [];
214
+
215
+ // npm/yarn workspaces (from package.json)
216
+ const workspaces = this.#packageJson.workspaces;
217
+ if (workspaces) {
218
+ const patterns = Array.isArray(workspaces) ? workspaces : workspaces.packages || [];
219
+ for (const pattern of patterns) {
220
+ // 简单 glob 展开: "packages/*" → 列出子目录
221
+ if (pattern.endsWith('/*') || pattern.endsWith('/**')) {
222
+ const dir = pattern.replace(/\/\*\*?$/, '');
223
+ const absDir = resolve(projectRoot, dir);
224
+ if (existsSync(absDir)) {
225
+ try {
226
+ const entries = readdirSync(absDir, { withFileTypes: true });
227
+ for (const entry of entries) {
228
+ if (entry.isDirectory() && !entry.name.startsWith('.')) {
229
+ paths.push(join(dir, entry.name));
230
+ }
231
+ }
232
+ } catch {
233
+ /* skip */
234
+ }
235
+ }
236
+ } else {
237
+ paths.push(pattern);
238
+ }
239
+ }
240
+ }
241
+
242
+ // pnpm-workspace.yaml
243
+ const pnpmWsPath = join(projectRoot, 'pnpm-workspace.yaml');
244
+ if (paths.length === 0 && existsSync(pnpmWsPath)) {
245
+ try {
246
+ const content = readFileSync(pnpmWsPath, 'utf8');
247
+ const pkgMatches = content.matchAll(/^\s*-\s*['"]?([^'"#\n]+)['"]?/gm);
248
+ for (const m of pkgMatches) {
249
+ const pattern = m[1].trim();
250
+ if (pattern.endsWith('/*') || pattern.endsWith('/**')) {
251
+ const dir = pattern.replace(/\/\*\*?$/, '');
252
+ const absDir = resolve(projectRoot, dir);
253
+ if (existsSync(absDir)) {
254
+ try {
255
+ const entries = readdirSync(absDir, { withFileTypes: true });
256
+ for (const entry of entries) {
257
+ if (entry.isDirectory() && !entry.name.startsWith('.')) {
258
+ paths.push(join(dir, entry.name));
259
+ }
260
+ }
261
+ } catch {
262
+ /* skip */
263
+ }
264
+ }
265
+ } else {
266
+ paths.push(pattern);
267
+ }
268
+ }
269
+ } catch {
270
+ /* skip */
271
+ }
272
+ }
273
+
274
+ // lerna.json
275
+ const lernaPath = join(projectRoot, 'lerna.json');
276
+ if (paths.length === 0 && existsSync(lernaPath)) {
277
+ try {
278
+ const lerna = JSON.parse(readFileSync(lernaPath, 'utf8'));
279
+ const patterns = lerna.packages || ['packages/*'];
280
+ for (const pattern of patterns) {
281
+ if (pattern.endsWith('/*') || pattern.endsWith('/**')) {
282
+ const dir = pattern.replace(/\/\*\*?$/, '');
283
+ const absDir = resolve(projectRoot, dir);
284
+ if (existsSync(absDir)) {
285
+ try {
286
+ const entries = readdirSync(absDir, { withFileTypes: true });
287
+ for (const entry of entries) {
288
+ if (entry.isDirectory() && !entry.name.startsWith('.')) {
289
+ paths.push(join(dir, entry.name));
290
+ }
291
+ }
292
+ } catch {
293
+ /* skip */
294
+ }
295
+ }
296
+ } else {
297
+ paths.push(pattern);
298
+ }
299
+ }
300
+ } catch {
301
+ /* skip */
302
+ }
303
+ }
304
+
305
+ return paths;
306
+ }
307
+
308
+ #detectFramework(pkg) {
309
+ if (!pkg) {
310
+ return null;
311
+ }
312
+ const deps = { ...(pkg.dependencies || {}), ...(pkg.devDependencies || {}) };
313
+
314
+ if (deps.next) {
315
+ return 'nextjs';
316
+ }
317
+ if (deps.nuxt || deps.nuxt3) {
318
+ return 'nuxt';
319
+ }
320
+ if (deps['@angular/core']) {
321
+ return 'angular';
322
+ }
323
+ if (deps.svelte) {
324
+ return 'svelte';
325
+ }
326
+ if (deps['react-native']) {
327
+ return 'react-native';
328
+ }
329
+ if (deps.react || deps['react-dom']) {
330
+ return 'react';
331
+ }
332
+ if (deps.vue) {
333
+ return 'vue';
334
+ }
335
+ if (deps['@nestjs/core']) {
336
+ return 'nestjs';
337
+ }
338
+ if (deps.electron) {
339
+ return 'electron';
340
+ }
341
+ if (deps.express) {
342
+ return 'node-server';
343
+ }
344
+ if (deps.fastify) {
345
+ return 'node-server';
346
+ }
347
+ if (deps.koa) {
348
+ return 'node-server';
349
+ }
350
+ if (deps.hono) {
351
+ return 'node-server';
352
+ }
353
+ return null;
354
+ }
355
+
356
+ #inferTargetType(pkg) {
357
+ if (!pkg) {
358
+ return 'library';
359
+ }
360
+ const deps = { ...(pkg.dependencies || {}), ...(pkg.devDependencies || {}) };
361
+
362
+ if (pkg.bin) {
363
+ return 'executable';
364
+ }
365
+ if (deps.jest || deps.mocha || deps.vitest) {
366
+ // Has test runner but check if it's primary purpose
367
+ if (pkg.name?.includes('test')) {
368
+ return 'test';
369
+ }
370
+ }
371
+ if (deps.react || deps.vue || deps['@angular/core'] || deps.svelte) {
372
+ return 'app';
373
+ }
374
+ if (deps.express || deps.fastify || deps.koa || deps['@nestjs/core']) {
375
+ return 'app';
376
+ }
377
+ if (deps.electron) {
378
+ return 'app';
379
+ }
380
+ return 'library';
381
+ }
382
+
383
+ #collectFiles(dir, rootDir, files, depth = 0) {
384
+ if (depth > 15) {
385
+ return; // 防止过深递归
386
+ }
387
+ try {
388
+ const entries = readdirSync(dir, { withFileTypes: true });
389
+ for (const entry of entries) {
390
+ if (entry.name.startsWith('.')) {
391
+ continue;
392
+ }
393
+ if (EXCLUDE_DIRS.has(entry.name)) {
394
+ continue;
395
+ }
396
+
397
+ const fullPath = join(dir, entry.name);
398
+
399
+ if (entry.isDirectory()) {
400
+ this.#collectFiles(fullPath, rootDir, files, depth + 1);
401
+ } else if (entry.isFile()) {
402
+ const ext = extname(entry.name);
403
+ if (SOURCE_EXTENSIONS.has(ext)) {
404
+ const lang = inferLang(entry.name) || 'javascript';
405
+ files.push({
406
+ name: entry.name,
407
+ path: fullPath,
408
+ relativePath: relative(rootDir, fullPath),
409
+ language: lang,
410
+ });
411
+ }
412
+ }
413
+ }
414
+ } catch {
415
+ /* skip unreadable dirs */
416
+ }
417
+ }
418
+
419
+ #buildWorkspaceDeps(workspacePaths) {
420
+ // 收集所有 workspace 包名
421
+ const nameToPath = new Map();
422
+ for (const t of this.#targets) {
423
+ nameToPath.set(t.name, t.path);
424
+ }
425
+
426
+ for (const t of this.#targets) {
427
+ const pkg = t.metadata?.packageJson;
428
+ if (!pkg) {
429
+ continue;
430
+ }
431
+ const allDeps = { ...(pkg.dependencies || {}), ...(pkg.devDependencies || {}) };
432
+ for (const depName of Object.keys(allDeps)) {
433
+ if (nameToPath.has(depName)) {
434
+ this.#depGraph.edges.push({
435
+ from: t.name,
436
+ to: depName,
437
+ type: pkg.devDependencies?.[depName] ? 'dev_depends_on' : 'depends_on',
438
+ });
439
+ }
440
+ }
441
+ }
442
+ }
443
+
444
+ #addExternalDeps() {
445
+ if (!this.#packageJson) {
446
+ return;
447
+ }
448
+ const deps = this.#packageJson.dependencies || {};
449
+ const devDeps = this.#packageJson.devDependencies || {};
450
+ const rootName = this.#targets[0]?.name;
451
+ if (!rootName) {
452
+ return;
453
+ }
454
+
455
+ for (const dep of Object.keys(deps)) {
456
+ if (!this.#depGraph.nodes.includes(dep)) {
457
+ this.#depGraph.edges.push({ from: rootName, to: dep, type: 'depends_on' });
458
+ }
459
+ }
460
+ for (const dep of Object.keys(devDeps)) {
461
+ if (!this.#depGraph.nodes.includes(dep)) {
462
+ this.#depGraph.edges.push({ from: rootName, to: dep, type: 'dev_depends_on' });
463
+ }
464
+ }
465
+ }
466
+ }
@@ -0,0 +1,93 @@
1
+ /**
2
+ * @module ProjectDiscoverer
3
+ * @description 项目结构发现器 — 统一接口定义
4
+ *
5
+ * 每个实现负责一种构建系统/包管理器的解析。
6
+ * Bootstrap Phase 1 通过 DiscovererRegistry 自动选择匹配的实现。
7
+ */
8
+
9
+ /**
10
+ * @typedef {object} DiscoveredTarget
11
+ * @property {string} name — 模块/Target 名称
12
+ * @property {string} path — 模块根目录绝对路径
13
+ * @property {string} type — 'library'|'executable'|'test'|'app'|'package'
14
+ * @property {string} [language] — 主语言
15
+ * @property {string} [framework] — 检测到的框架 (react/vue/express/django/spring/...)
16
+ * @property {object} [metadata] — 构建系统特有元数据
17
+ */
18
+
19
+ /**
20
+ * @typedef {object} DiscoveredFile
21
+ * @property {string} name — 文件名
22
+ * @property {string} path — 绝对路径
23
+ * @property {string} relativePath — 相对于项目根的路径
24
+ * @property {string} language — 推断语言
25
+ */
26
+
27
+ /**
28
+ * @typedef {object} DependencyEdge
29
+ * @property {string} from — 源模块名
30
+ * @property {string} to — 目标模块名
31
+ * @property {string} type — 'depends_on'|'dev_depends_on'|'peer_depends_on'
32
+ */
33
+
34
+ export class ProjectDiscoverer {
35
+ /**
36
+ * 检测此 Discoverer 是否适用于给定项目
37
+ * @param {string} projectRoot
38
+ * @returns {Promise<{ match: boolean, confidence: number, reason: string }>}
39
+ */
40
+ async detect(projectRoot) {
41
+ throw new Error('Not implemented');
42
+ }
43
+
44
+ /**
45
+ * 加载项目结构(解析配置文件、构建依赖图)
46
+ * @param {string} projectRoot
47
+ * @returns {Promise<void>}
48
+ */
49
+ async load(projectRoot) {
50
+ throw new Error('Not implemented');
51
+ }
52
+
53
+ /**
54
+ * 列出所有 Target/模块
55
+ * @returns {Promise<DiscoveredTarget[]>}
56
+ */
57
+ async listTargets() {
58
+ throw new Error('Not implemented');
59
+ }
60
+
61
+ /**
62
+ * 获取指定 Target 下的源码文件列表
63
+ * @param {DiscoveredTarget|string} target
64
+ * @returns {Promise<DiscoveredFile[]>}
65
+ */
66
+ async getTargetFiles(target) {
67
+ throw new Error('Not implemented');
68
+ }
69
+
70
+ /**
71
+ * 获取模块间依赖关系图
72
+ * @returns {Promise<{ nodes: string[], edges: DependencyEdge[] }>}
73
+ */
74
+ async getDependencyGraph() {
75
+ throw new Error('Not implemented');
76
+ }
77
+
78
+ /**
79
+ * Discoverer 标识
80
+ * @returns {string} 如 'spm', 'node', 'python', 'gradle', 'maven', 'generic'
81
+ */
82
+ get id() {
83
+ throw new Error('Not implemented');
84
+ }
85
+
86
+ /**
87
+ * 人类可读名称
88
+ * @returns {string}
89
+ */
90
+ get displayName() {
91
+ throw new Error('Not implemented');
92
+ }
93
+ }