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
@@ -6,7 +6,7 @@
6
6
  import Logger from '../logging/Logger.js';
7
7
 
8
8
  export class PluginManager {
9
- #plugins; // Map<name, { plugin, enabled, priority, meta }>
9
+ #plugins; // Map<name, { plugin, enabled, priority, meta }>
10
10
  #logger;
11
11
 
12
12
  constructor() {
@@ -37,7 +37,11 @@ export class PluginManager {
37
37
  unregister(name) {
38
38
  const entry = this.#plugins.get(name);
39
39
  if (entry?.plugin?.destroy) {
40
- try { entry.plugin.destroy(); } catch { /* silent */ }
40
+ try {
41
+ entry.plugin.destroy();
42
+ } catch {
43
+ /* silent */
44
+ }
41
45
  }
42
46
  this.#plugins.delete(name);
43
47
  }
@@ -47,7 +51,9 @@ export class PluginManager {
47
51
  */
48
52
  setEnabled(name, enabled) {
49
53
  const entry = this.#plugins.get(name);
50
- if (entry) entry.enabled = enabled;
54
+ if (entry) {
55
+ entry.enabled = enabled;
56
+ }
51
57
  }
52
58
 
53
59
  /**
@@ -56,7 +62,9 @@ export class PluginManager {
56
62
  async initAll(context = {}) {
57
63
  const sorted = this.#getSorted();
58
64
  for (const { name, entry } of sorted) {
59
- if (!entry.enabled || !entry.plugin.init) continue;
65
+ if (!entry.enabled || !entry.plugin.init) {
66
+ continue;
67
+ }
60
68
  try {
61
69
  await entry.plugin.init(context);
62
70
  this.#logger.debug(`[PluginManager] 初始化: ${name}`);
@@ -70,9 +78,13 @@ export class PluginManager {
70
78
  * 销毁所有插件
71
79
  */
72
80
  async destroyAll() {
73
- for (const [name, entry] of this.#plugins) {
81
+ for (const [_name, entry] of this.#plugins) {
74
82
  if (entry.plugin.destroy) {
75
- try { await entry.plugin.destroy(); } catch { /* silent */ }
83
+ try {
84
+ await entry.plugin.destroy();
85
+ } catch {
86
+ /* silent */
87
+ }
76
88
  }
77
89
  }
78
90
  this.#plugins.clear();
@@ -84,7 +96,11 @@ export class PluginManager {
84
96
  getEnabled() {
85
97
  return this.#getSorted()
86
98
  .filter(({ entry }) => entry.enabled)
87
- .map(({ name, entry }) => ({ name, priority: entry.priority, description: entry.description }));
99
+ .map(({ name, entry }) => ({
100
+ name,
101
+ priority: entry.priority,
102
+ description: entry.description,
103
+ }));
88
104
  }
89
105
 
90
106
  /**
@@ -102,7 +118,9 @@ export class PluginManager {
102
118
  async callHook(hookName, ...args) {
103
119
  const results = [];
104
120
  for (const { name, entry } of this.#getSorted()) {
105
- if (!entry.enabled || !entry.plugin[hookName]) continue;
121
+ if (!entry.enabled || !entry.plugin[hookName]) {
122
+ continue;
123
+ }
106
124
  try {
107
125
  const result = await entry.plugin[hookName](...args);
108
126
  results.push({ name, result });
@@ -17,8 +17,8 @@ export class RealtimeService {
17
17
  methods: ['GET', 'POST'],
18
18
  },
19
19
  transports: ['websocket', 'polling'],
20
- pingInterval: 25000, // 25s 心跳间隔(默认值,显式声明)
21
- pingTimeout: 20000, // 20s 超时(默认值)
20
+ pingInterval: 25000, // 25s 心跳间隔(默认值,显式声明)
21
+ pingTimeout: 20000, // 20s 超时(默认值)
22
22
  });
23
23
 
24
24
  this.setupEventHandlers();
@@ -23,7 +23,9 @@ export function chunk(content, metadata = {}, options = {}) {
23
23
  overlapTokens = DEFAULT_OVERLAP_TOKENS,
24
24
  } = options;
25
25
 
26
- if (!content || content.trim().length === 0) return [];
26
+ if (!content || content.trim().length === 0) {
27
+ return [];
28
+ }
27
29
 
28
30
  const tokens = estimateTokens(content);
29
31
 
@@ -86,7 +88,7 @@ function chunkBySection(content, metadata, maxChunkTokens) {
86
88
  buffer = section;
87
89
  continue;
88
90
  }
89
- const combined = buffer.content + '\n' + section.content;
91
+ const combined = `${buffer.content}\n${section.content}`;
90
92
  if (estimateTokens(combined) <= maxChunkTokens) {
91
93
  buffer = { title: buffer.title, content: combined };
92
94
  } else {
@@ -94,7 +96,9 @@ function chunkBySection(content, metadata, maxChunkTokens) {
94
96
  buffer = section;
95
97
  }
96
98
  }
97
- if (buffer) merged.push(buffer);
99
+ if (buffer) {
100
+ merged.push(buffer);
101
+ }
98
102
 
99
103
  // 对超大段落做 fixed 分割
100
104
  const results = [];
@@ -105,7 +109,12 @@ function chunkBySection(content, metadata, maxChunkTokens) {
105
109
  for (const sub of subChunks) {
106
110
  results.push({
107
111
  content: sub.content,
108
- metadata: { ...metadata, ...sub.metadata, sectionTitle: section.title, chunkIndex: results.length },
112
+ metadata: {
113
+ ...metadata,
114
+ ...sub.metadata,
115
+ sectionTitle: section.title,
116
+ chunkIndex: results.length,
117
+ },
109
118
  });
110
119
  }
111
120
  } else {
@@ -117,7 +126,9 @@ function chunkBySection(content, metadata, maxChunkTokens) {
117
126
  }
118
127
 
119
128
  // 设置 totalChunks
120
- for (const chunk of results) chunk.metadata.totalChunks = results.length;
129
+ for (const chunk of results) {
130
+ chunk.metadata.totalChunks = results.length;
131
+ }
121
132
  return results;
122
133
  }
123
134
 
@@ -152,10 +163,14 @@ function chunkFixed(content, metadata, maxChunkTokens, overlapTokens) {
152
163
  const nextStart = end - overlapChars;
153
164
  // 确保至少前进 1 字符,防止 overlap >= maxChars 时无限循环
154
165
  start = nextStart > start ? nextStart : end;
155
- if (start >= content.length) break;
166
+ if (start >= content.length) {
167
+ break;
168
+ }
156
169
  }
157
170
 
158
- for (const chunk of results) chunk.metadata.totalChunks = results.length;
171
+ for (const chunk of results) {
172
+ chunk.metadata.totalChunks = results.length;
173
+ }
159
174
  return results;
160
175
  }
161
176
 
@@ -3,17 +3,26 @@
3
3
  * scan → chunk → detect incremental changes (sourceHash) → embed via AI → batch upsert
4
4
  */
5
5
 
6
- import { readdirSync, readFileSync, statSync, existsSync } from 'node:fs';
7
- import { join, relative, extname } from 'node:path';
8
6
  import { createHash } from 'node:crypto';
9
- import { chunk, estimateTokens } from './Chunker.js';
10
-
11
- const SCANNABLE_EXTENSIONS = new Set(['.md', '.markdown', '.txt', '.swift', '.js', '.ts', '.py']);
7
+ import { existsSync, readdirSync, readFileSync } from 'node:fs';
8
+ import { extname, join, relative } from 'node:path';
9
+ import { LanguageService } from '../../shared/LanguageService.js';
10
+ import { chunk } from './Chunker.js';
11
+
12
+ const SCANNABLE_EXTENSIONS = new Set([
13
+ '.md', '.markdown', '.txt',
14
+ '.swift', '.m', '.h',
15
+ '.js', '.ts', '.jsx', '.tsx',
16
+ '.py',
17
+ '.java', '.kt',
18
+ '.go',
19
+ '.rs', '.rb',
20
+ ]);
12
21
 
13
22
  export class IndexingPipeline {
14
- #vectorStore; // VectorStore 实例
15
- #aiProvider; // AiProvider 实例 (可选, 用于 embedding)
16
- #scanDirs; // 要扫描的目录
23
+ #vectorStore; // VectorStore 实例
24
+ #aiProvider; // AiProvider 实例 (可选, 用于 embedding)
25
+ #scanDirs; // 要扫描的目录
17
26
  #projectRoot;
18
27
 
19
28
  constructor(options = {}) {
@@ -23,8 +32,12 @@ export class IndexingPipeline {
23
32
  this.#projectRoot = options.projectRoot || process.cwd();
24
33
  }
25
34
 
26
- setVectorStore(store) { this.#vectorStore = store; }
27
- setAiProvider(provider) { this.#aiProvider = provider; }
35
+ setVectorStore(store) {
36
+ this.#vectorStore = store;
37
+ }
38
+ setAiProvider(provider) {
39
+ this.#aiProvider = provider;
40
+ }
28
41
 
29
42
  /**
30
43
  * 运行完整索引管线
@@ -35,7 +48,9 @@ export class IndexingPipeline {
35
48
  const { force = false, dryRun = false } = options;
36
49
  const stats = { scanned: 0, chunked: 0, embedded: 0, upserted: 0, skipped: 0, errors: 0 };
37
50
 
38
- if (!this.#vectorStore) throw new Error('VectorStore not set');
51
+ if (!this.#vectorStore) {
52
+ throw new Error('VectorStore not set');
53
+ }
39
54
 
40
55
  // 1. 扫描文件
41
56
  const files = this.scan();
@@ -53,7 +68,7 @@ export class IndexingPipeline {
53
68
 
54
69
  // 增量检测:hash 未变时跳过
55
70
  if (!force) {
56
- const existing = await this.#vectorStore.getById(baseId + '_0');
71
+ const existing = await this.#vectorStore.getById(`${baseId}_0`);
57
72
  if (existing?.metadata?.sourceHash === hash) {
58
73
  stats.skipped++;
59
74
  continue;
@@ -79,7 +94,9 @@ export class IndexingPipeline {
79
94
  try {
80
95
  vector = await this.#aiProvider.embed(chunkItem.content);
81
96
  stats.embedded++;
82
- } catch { /* embed optional */ }
97
+ } catch {
98
+ /* embed optional */
99
+ }
83
100
  }
84
101
 
85
102
  batch.push({
@@ -92,14 +109,16 @@ export class IndexingPipeline {
92
109
 
93
110
  // 清理旧 chunk (如果文件 chunk 数减少)
94
111
  for (const existId of existingIds) {
95
- if (existId.startsWith(baseId + '_')) {
112
+ if (existId.startsWith(`${baseId}_`)) {
96
113
  const idx = parseInt(existId.split('_').pop(), 10);
97
114
  if (idx >= chunks.length) {
98
- if (!dryRun) await this.#vectorStore.remove(existId);
115
+ if (!dryRun) {
116
+ await this.#vectorStore.remove(existId);
117
+ }
99
118
  }
100
119
  }
101
120
  }
102
- } catch (error) {
121
+ } catch (_error) {
103
122
  stats.errors++;
104
123
  }
105
124
  }
@@ -122,7 +141,9 @@ export class IndexingPipeline {
122
141
 
123
142
  for (const dir of this.#scanDirs) {
124
143
  const absDir = join(this.#projectRoot, dir);
125
- if (!existsSync(absDir)) continue;
144
+ if (!existsSync(absDir)) {
145
+ continue;
146
+ }
126
147
  this.#walkDir(absDir, files);
127
148
  }
128
149
 
@@ -152,7 +173,9 @@ export class IndexingPipeline {
152
173
  for (const entry of entries) {
153
174
  const fullPath = join(dir, entry.name);
154
175
  if (entry.isDirectory()) {
155
- if (entry.name.startsWith('.') || entry.name === 'node_modules') continue;
176
+ if (entry.name.startsWith('.') || entry.name === 'node_modules') {
177
+ continue;
178
+ }
156
179
  this.#walkDir(fullPath, files);
157
180
  } else if (entry.isFile()) {
158
181
  const ext = extname(entry.name).toLowerCase();
@@ -165,12 +188,13 @@ export class IndexingPipeline {
165
188
  }
166
189
  }
167
190
  }
168
- } catch { /* skip unreadable dirs */ }
191
+ } catch {
192
+ /* skip unreadable dirs */
193
+ }
169
194
  }
170
195
 
171
196
  #detectLanguage(filePath) {
172
- const ext = extname(filePath).toLowerCase();
173
- const map = { '.swift': 'swift', '.js': 'javascript', '.ts': 'typescript', '.py': 'python', '.md': 'markdown' };
174
- return map[ext] || 'text';
197
+ const lang = LanguageService.inferLang(filePath);
198
+ return lang === 'unknown' ? 'text' : lang;
175
199
  }
176
200
  }
@@ -4,15 +4,15 @@
4
4
  * 支持余弦相似度搜索、混合搜索(向量 70% + 关键词 30%)
5
5
  */
6
6
 
7
- import { VectorStore } from './VectorStore.js';
8
- import { writeFileSync, readFileSync, mkdirSync, existsSync, statSync } from 'node:fs';
9
- import { join, dirname } from 'node:path';
7
+ import { existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from 'node:fs';
8
+ import { dirname, join } from 'node:path';
10
9
  import pathGuard from '../../shared/PathGuard.js';
11
10
  import { cosineSimilarity } from '../../shared/similarity.js';
11
+ import { VectorStore } from './VectorStore.js';
12
12
 
13
13
  export class JsonVectorAdapter extends VectorStore {
14
14
  #indexPath;
15
- #data; // Map<id, { id, content, vector, metadata }>
15
+ #data; // Map<id, { id, content, vector, metadata }>
16
16
  #dirty;
17
17
 
18
18
  constructor(projectRoot, options = {}) {
@@ -36,7 +36,9 @@ export class JsonVectorAdapter extends VectorStore {
36
36
  }
37
37
 
38
38
  async upsert(item) {
39
- if (!item?.id) throw new Error('Item must have an id');
39
+ if (!item?.id) {
40
+ throw new Error('Item must have an id');
41
+ }
40
42
  this.#data.set(item.id, {
41
43
  id: item.id,
42
44
  content: item.content || '',
@@ -50,7 +52,9 @@ export class JsonVectorAdapter extends VectorStore {
50
52
 
51
53
  async batchUpsert(items) {
52
54
  for (const item of items) {
53
- if (!item?.id) continue;
55
+ if (!item?.id) {
56
+ continue;
57
+ }
54
58
  this.#data.set(item.id, {
55
59
  id: item.id,
56
60
  content: item.content || '',
@@ -79,7 +83,9 @@ export class JsonVectorAdapter extends VectorStore {
79
83
  async searchVector(queryVector, options = {}) {
80
84
  const { topK = 10, filter = null, minScore = 0 } = options;
81
85
 
82
- if (!queryVector || queryVector.length === 0) return [];
86
+ if (!queryVector || queryVector.length === 0) {
87
+ return [];
88
+ }
83
89
 
84
90
  let candidates = [...this.#data.values()];
85
91
 
@@ -90,12 +96,12 @@ export class JsonVectorAdapter extends VectorStore {
90
96
 
91
97
  // 计算余弦相似度
92
98
  const scored = candidates
93
- .filter(item => item.vector && item.vector.length > 0)
94
- .map(item => ({
99
+ .filter((item) => item.vector && item.vector.length > 0)
100
+ .map((item) => ({
95
101
  item,
96
102
  score: this.#cosineSimilarity(queryVector, item.vector),
97
103
  }))
98
- .filter(result => result.score >= minScore)
104
+ .filter((result) => result.score >= minScore)
99
105
  .sort((a, b) => b.score - a.score)
100
106
  .slice(0, topK);
101
107
 
@@ -109,35 +115,38 @@ export class JsonVectorAdapter extends VectorStore {
109
115
  const { topK = 10, filter = null } = options;
110
116
 
111
117
  let candidates = [...this.#data.values()];
112
- if (filter) candidates = this.#applyFilter(candidates, filter);
118
+ if (filter) {
119
+ candidates = this.#applyFilter(candidates, filter);
120
+ }
113
121
 
114
- const scored = candidates.map(item => {
115
- // 向量分数
116
- let vectorScore = 0;
117
- if (queryVector && queryVector.length > 0 && item.vector && item.vector.length > 0) {
118
- vectorScore = this.#cosineSimilarity(queryVector, item.vector);
119
- }
122
+ const scored = candidates
123
+ .map((item) => {
124
+ // 向量分数
125
+ let vectorScore = 0;
126
+ if (queryVector && queryVector.length > 0 && item.vector && item.vector.length > 0) {
127
+ vectorScore = this.#cosineSimilarity(queryVector, item.vector);
128
+ }
120
129
 
121
- // 关键词分数
122
- let keywordScore = 0;
123
- if (queryText) {
124
- const text = (item.content || '').toLowerCase();
125
- const query = queryText.toLowerCase();
126
- const words = query.split(/\s+/);
127
- const hits = words.filter(w => text.includes(w)).length;
128
- keywordScore = words.length > 0 ? hits / words.length : 0;
129
- }
130
+ // 关键词分数
131
+ let keywordScore = 0;
132
+ if (queryText) {
133
+ const text = (item.content || '').toLowerCase();
134
+ const query = queryText.toLowerCase();
135
+ const words = query.split(/\s+/);
136
+ const hits = words.filter((w) => text.includes(w)).length;
137
+ keywordScore = words.length > 0 ? hits / words.length : 0;
138
+ }
130
139
 
131
- return {
132
- item,
133
- score: vectorScore * 0.7 + keywordScore * 0.3,
134
- vectorScore,
135
- keywordScore,
136
- };
137
- })
138
- .filter(r => r.score > 0)
139
- .sort((a, b) => b.score - a.score)
140
- .slice(0, topK);
140
+ return {
141
+ item,
142
+ score: vectorScore * 0.7 + keywordScore * 0.3,
143
+ vectorScore,
144
+ keywordScore,
145
+ };
146
+ })
147
+ .filter((r) => r.score > 0)
148
+ .sort((a, b) => b.score - a.score)
149
+ .slice(0, topK);
141
150
 
142
151
  return scored;
143
152
  }
@@ -148,7 +157,7 @@ export class JsonVectorAdapter extends VectorStore {
148
157
  */
149
158
  async query(queryVector, topK = 10) {
150
159
  const results = await this.searchVector(queryVector, { topK });
151
- return results.map(r => ({
160
+ return results.map((r) => ({
152
161
  id: r.item.id,
153
162
  similarity: r.score,
154
163
  score: r.score,
@@ -177,31 +186,47 @@ export class JsonVectorAdapter extends VectorStore {
177
186
  if (existsSync(this.#indexPath)) {
178
187
  indexSize = statSync(this.#indexPath).size;
179
188
  }
180
- } catch { /* ignore */ }
189
+ } catch {
190
+ /* ignore */
191
+ }
181
192
 
182
193
  return {
183
194
  count: this.#data.size,
184
195
  indexSize,
185
196
  indexPath: this.#indexPath,
186
- hasVectors: [...this.#data.values()].filter(d => d.vector?.length > 0).length,
197
+ hasVectors: [...this.#data.values()].filter((d) => d.vector?.length > 0).length,
187
198
  };
188
199
  }
189
200
 
190
201
  // --- 私有方法 ---
191
202
 
192
203
  #applyFilter(items, filter) {
193
- return items.filter(item => {
204
+ return items.filter((item) => {
194
205
  const meta = item.metadata || {};
195
- if (filter.type && meta.type !== filter.type) return false;
196
- if (filter.category && meta.category !== filter.category) return false;
197
- if (filter.language && meta.language !== filter.language) return false;
198
- if (filter.sourcePath && !meta.sourcePath?.includes(filter.sourcePath)) return false;
199
- if (filter.module && meta.module !== filter.module) return false;
206
+ if (filter.type && meta.type !== filter.type) {
207
+ return false;
208
+ }
209
+ if (filter.category && meta.category !== filter.category) {
210
+ return false;
211
+ }
212
+ if (filter.language && meta.language !== filter.language) {
213
+ return false;
214
+ }
215
+ if (filter.sourcePath && !meta.sourcePath?.includes(filter.sourcePath)) {
216
+ return false;
217
+ }
218
+ if (filter.module && meta.module !== filter.module) {
219
+ return false;
220
+ }
200
221
  if (filter.tags && Array.isArray(filter.tags)) {
201
222
  const itemTags = meta.tags || [];
202
- if (!filter.tags.some(t => itemTags.includes(t))) return false;
223
+ if (!filter.tags.some((t) => itemTags.includes(t))) {
224
+ return false;
225
+ }
226
+ }
227
+ if (filter.deprecated === false && meta.deprecated) {
228
+ return false;
203
229
  }
204
- if (filter.deprecated === false && meta.deprecated) return false;
205
230
  return true;
206
231
  });
207
232
  }
@@ -212,12 +237,16 @@ export class JsonVectorAdapter extends VectorStore {
212
237
 
213
238
  #load() {
214
239
  try {
215
- if (!existsSync(this.#indexPath)) return;
240
+ if (!existsSync(this.#indexPath)) {
241
+ return;
242
+ }
216
243
  const raw = readFileSync(this.#indexPath, 'utf-8');
217
244
  const items = JSON.parse(raw);
218
245
  if (Array.isArray(items)) {
219
246
  for (const item of items) {
220
- if (item?.id) this.#data.set(item.id, item);
247
+ if (item?.id) {
248
+ this.#data.set(item.id, item);
249
+ }
221
250
  }
222
251
  } else if (typeof items === 'object') {
223
252
  // 兼容旧格式 { id: item }
@@ -225,18 +254,26 @@ export class JsonVectorAdapter extends VectorStore {
225
254
  this.#data.set(id, { ...item, id });
226
255
  }
227
256
  }
228
- } catch { /* silent: start empty */ }
257
+ } catch {
258
+ /* silent: start empty */
259
+ }
229
260
  }
230
261
 
231
262
  #autoSave() {
232
- if (!this.#dirty) return;
263
+ if (!this.#dirty) {
264
+ return;
265
+ }
233
266
  try {
234
267
  const dir = dirname(this.#indexPath);
235
268
  pathGuard.assertProjectWriteSafe(dir);
236
- if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
269
+ if (!existsSync(dir)) {
270
+ mkdirSync(dir, { recursive: true });
271
+ }
237
272
  const items = [...this.#data.values()];
238
273
  writeFileSync(this.#indexPath, JSON.stringify(items, null, 2));
239
274
  this.#dirty = false;
240
- } catch { /* silent */ }
275
+ } catch {
276
+ /* silent */
277
+ }
241
278
  }
242
279
  }
@@ -8,13 +8,17 @@ export class VectorStore {
8
8
  * 初始化存储
9
9
  * @returns {Promise<void>}
10
10
  */
11
- async init() { throw new Error('Not implemented: init()'); }
11
+ async init() {
12
+ throw new Error('Not implemented: init()');
13
+ }
12
14
 
13
15
  /**
14
16
  * 插入或更新文档
15
17
  * @param {{ id: string, content: string, vector: number[], metadata: object }} item
16
18
  */
17
- async upsert(item) { throw new Error('Not implemented: upsert()'); }
19
+ async upsert(item) {
20
+ throw new Error('Not implemented: upsert()');
21
+ }
18
22
 
19
23
  /**
20
24
  * 批量 upsert
@@ -25,7 +29,7 @@ export class VectorStore {
25
29
  const BATCH_SIZE = 50;
26
30
  for (let i = 0; i < items.length; i += BATCH_SIZE) {
27
31
  const batch = items.slice(i, i + BATCH_SIZE);
28
- await Promise.all(batch.map(item => this.upsert(item)));
32
+ await Promise.all(batch.map((item) => this.upsert(item)));
29
33
  }
30
34
  }
31
35
 
@@ -33,14 +37,18 @@ export class VectorStore {
33
37
  * 删除文档
34
38
  * @param {string} id
35
39
  */
36
- async remove(id) { throw new Error('Not implemented: remove()'); }
40
+ async remove(id) {
41
+ throw new Error('Not implemented: remove()');
42
+ }
37
43
 
38
44
  /**
39
45
  * 按 ID 获取
40
46
  * @param {string} id
41
47
  * @returns {object|null}
42
48
  */
43
- async getById(id) { throw new Error('Not implemented: getById()'); }
49
+ async getById(id) {
50
+ throw new Error('Not implemented: getById()');
51
+ }
44
52
 
45
53
  /**
46
54
  * 向量相似度搜索
@@ -48,29 +56,39 @@ export class VectorStore {
48
56
  * @param {object} options — { topK, filter, minScore }
49
57
  * @returns {Array<{ item: object, score: number }>}
50
58
  */
51
- async searchVector(queryVector, options = {}) { throw new Error('Not implemented: searchVector()'); }
59
+ async searchVector(queryVector, options = {}) {
60
+ throw new Error('Not implemented: searchVector()');
61
+ }
52
62
 
53
63
  /**
54
64
  * 按过滤条件搜索
55
65
  * @param {object} filter — { type, category, language, tags, ... }
56
66
  * @returns {Array}
57
67
  */
58
- async searchByFilter(filter) { throw new Error('Not implemented: searchByFilter()'); }
68
+ async searchByFilter(filter) {
69
+ throw new Error('Not implemented: searchByFilter()');
70
+ }
59
71
 
60
72
  /**
61
73
  * 列出所有 ID
62
74
  * @returns {string[]}
63
75
  */
64
- async listIds() { throw new Error('Not implemented: listIds()'); }
76
+ async listIds() {
77
+ throw new Error('Not implemented: listIds()');
78
+ }
65
79
 
66
80
  /**
67
81
  * 清空存储
68
82
  */
69
- async clear() { throw new Error('Not implemented: clear()'); }
83
+ async clear() {
84
+ throw new Error('Not implemented: clear()');
85
+ }
70
86
 
71
87
  /**
72
88
  * 获取统计信息
73
89
  * @returns {{ count: number, indexSize: number }}
74
90
  */
75
- async getStats() { throw new Error('Not implemented: getStats()'); }
91
+ async getStats() {
92
+ throw new Error('Not implemented: getStats()');
93
+ }
76
94
  }