sdl-mcp 0.6.0

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 (496) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +132 -0
  3. package/config/benchmark.ci.config.json +86 -0
  4. package/config/benchmark.ci.windows.config.json +86 -0
  5. package/config/benchmark.config.example.json +86 -0
  6. package/config/benchmark.config.json +86 -0
  7. package/config/sdlmcp.config.example.json +139 -0
  8. package/config/sdlmcp.config.json +48 -0
  9. package/config/sdlmcp.config.schema.json +381 -0
  10. package/dist/agent/evidence.d.ts +18 -0
  11. package/dist/agent/evidence.d.ts.map +1 -0
  12. package/dist/agent/evidence.js +107 -0
  13. package/dist/agent/evidence.js.map +1 -0
  14. package/dist/agent/executor.d.ts +28 -0
  15. package/dist/agent/executor.d.ts.map +1 -0
  16. package/dist/agent/executor.js +261 -0
  17. package/dist/agent/executor.js.map +1 -0
  18. package/dist/agent/index.d.ts +6 -0
  19. package/dist/agent/index.d.ts.map +1 -0
  20. package/dist/agent/index.js +6 -0
  21. package/dist/agent/index.js.map +1 -0
  22. package/dist/agent/orchestrator.d.ts +14 -0
  23. package/dist/agent/orchestrator.d.ts.map +1 -0
  24. package/dist/agent/orchestrator.js +108 -0
  25. package/dist/agent/orchestrator.js.map +1 -0
  26. package/dist/agent/planner.d.ts +21 -0
  27. package/dist/agent/planner.d.ts.map +1 -0
  28. package/dist/agent/planner.js +153 -0
  29. package/dist/agent/planner.js.map +1 -0
  30. package/dist/agent/types.d.ts +86 -0
  31. package/dist/agent/types.d.ts.map +1 -0
  32. package/dist/agent/types.js +2 -0
  33. package/dist/agent/types.js.map +1 -0
  34. package/dist/benchmark/index.d.ts +4 -0
  35. package/dist/benchmark/index.d.ts.map +1 -0
  36. package/dist/benchmark/index.js +4 -0
  37. package/dist/benchmark/index.js.map +1 -0
  38. package/dist/benchmark/regression.d.ts +40 -0
  39. package/dist/benchmark/regression.d.ts.map +1 -0
  40. package/dist/benchmark/regression.js +199 -0
  41. package/dist/benchmark/regression.js.map +1 -0
  42. package/dist/benchmark/smoothing.d.ts +40 -0
  43. package/dist/benchmark/smoothing.d.ts.map +1 -0
  44. package/dist/benchmark/smoothing.js +121 -0
  45. package/dist/benchmark/smoothing.js.map +1 -0
  46. package/dist/benchmark/threshold.d.ts +64 -0
  47. package/dist/benchmark/threshold.d.ts.map +1 -0
  48. package/dist/benchmark/threshold.js +173 -0
  49. package/dist/benchmark/threshold.js.map +1 -0
  50. package/dist/cli/argParsing.d.ts +34 -0
  51. package/dist/cli/argParsing.d.ts.map +1 -0
  52. package/dist/cli/argParsing.js +329 -0
  53. package/dist/cli/argParsing.js.map +1 -0
  54. package/dist/cli/commands/benchmark.d.ts +16 -0
  55. package/dist/cli/commands/benchmark.d.ts.map +1 -0
  56. package/dist/cli/commands/benchmark.js +412 -0
  57. package/dist/cli/commands/benchmark.js.map +1 -0
  58. package/dist/cli/commands/doctor.d.ts +3 -0
  59. package/dist/cli/commands/doctor.d.ts.map +1 -0
  60. package/dist/cli/commands/doctor.js +218 -0
  61. package/dist/cli/commands/doctor.js.map +1 -0
  62. package/dist/cli/commands/export.d.ts +12 -0
  63. package/dist/cli/commands/export.d.ts.map +1 -0
  64. package/dist/cli/commands/export.js +70 -0
  65. package/dist/cli/commands/export.js.map +1 -0
  66. package/dist/cli/commands/import.d.ts +10 -0
  67. package/dist/cli/commands/import.d.ts.map +1 -0
  68. package/dist/cli/commands/import.js +40 -0
  69. package/dist/cli/commands/import.js.map +1 -0
  70. package/dist/cli/commands/index.d.ts +3 -0
  71. package/dist/cli/commands/index.d.ts.map +1 -0
  72. package/dist/cli/commands/index.js +89 -0
  73. package/dist/cli/commands/index.js.map +1 -0
  74. package/dist/cli/commands/init.d.ts +3 -0
  75. package/dist/cli/commands/init.d.ts.map +1 -0
  76. package/dist/cli/commands/init.js +221 -0
  77. package/dist/cli/commands/init.js.map +1 -0
  78. package/dist/cli/commands/pull.d.ts +11 -0
  79. package/dist/cli/commands/pull.d.ts.map +1 -0
  80. package/dist/cli/commands/pull.js +47 -0
  81. package/dist/cli/commands/pull.js.map +1 -0
  82. package/dist/cli/commands/serve.d.ts +3 -0
  83. package/dist/cli/commands/serve.d.ts.map +1 -0
  84. package/dist/cli/commands/serve.js +72 -0
  85. package/dist/cli/commands/serve.js.map +1 -0
  86. package/dist/cli/commands/version.d.ts +3 -0
  87. package/dist/cli/commands/version.d.ts.map +1 -0
  88. package/dist/cli/commands/version.js +24 -0
  89. package/dist/cli/commands/version.js.map +1 -0
  90. package/dist/cli/index.d.ts +3 -0
  91. package/dist/cli/index.d.ts.map +1 -0
  92. package/dist/cli/index.js +213 -0
  93. package/dist/cli/index.js.map +1 -0
  94. package/dist/cli/logging.d.ts +10 -0
  95. package/dist/cli/logging.d.ts.map +1 -0
  96. package/dist/cli/logging.js +48 -0
  97. package/dist/cli/logging.js.map +1 -0
  98. package/dist/cli/transport/http.d.ts +3 -0
  99. package/dist/cli/transport/http.d.ts.map +1 -0
  100. package/dist/cli/transport/http.js +106 -0
  101. package/dist/cli/transport/http.js.map +1 -0
  102. package/dist/cli/transport/stdio.d.ts +3 -0
  103. package/dist/cli/transport/stdio.d.ts.map +1 -0
  104. package/dist/cli/transport/stdio.js +6 -0
  105. package/dist/cli/transport/stdio.js.map +1 -0
  106. package/dist/cli/types.d.ts +41 -0
  107. package/dist/cli/types.d.ts.map +1 -0
  108. package/dist/cli/types.js +2 -0
  109. package/dist/cli/types.js.map +1 -0
  110. package/dist/code/gate.d.ts +11 -0
  111. package/dist/code/gate.d.ts.map +1 -0
  112. package/dist/code/gate.js +134 -0
  113. package/dist/code/gate.js.map +1 -0
  114. package/dist/code/hotpath.d.ts +17 -0
  115. package/dist/code/hotpath.d.ts.map +1 -0
  116. package/dist/code/hotpath.js +200 -0
  117. package/dist/code/hotpath.js.map +1 -0
  118. package/dist/code/redact.d.ts +19 -0
  119. package/dist/code/redact.d.ts.map +1 -0
  120. package/dist/code/redact.js +92 -0
  121. package/dist/code/redact.js.map +1 -0
  122. package/dist/code/skeleton.d.ts +32 -0
  123. package/dist/code/skeleton.d.ts.map +1 -0
  124. package/dist/code/skeleton.js +610 -0
  125. package/dist/code/skeleton.js.map +1 -0
  126. package/dist/code/windows.d.ts +18 -0
  127. package/dist/code/windows.d.ts.map +1 -0
  128. package/dist/code/windows.js +236 -0
  129. package/dist/code/windows.js.map +1 -0
  130. package/dist/config/constants.d.ts +340 -0
  131. package/dist/config/constants.d.ts.map +1 -0
  132. package/dist/config/constants.js +379 -0
  133. package/dist/config/constants.js.map +1 -0
  134. package/dist/config/loadConfig.d.ts +3 -0
  135. package/dist/config/loadConfig.d.ts.map +1 -0
  136. package/dist/config/loadConfig.js +71 -0
  137. package/dist/config/loadConfig.js.map +1 -0
  138. package/dist/config/types.d.ts +509 -0
  139. package/dist/config/types.d.ts.map +1 -0
  140. package/dist/config/types.js +139 -0
  141. package/dist/config/types.js.map +1 -0
  142. package/dist/db/db.d.ts +4 -0
  143. package/dist/db/db.d.ts.map +1 -0
  144. package/dist/db/db.js +47 -0
  145. package/dist/db/db.js.map +1 -0
  146. package/dist/db/migrations.d.ts +15 -0
  147. package/dist/db/migrations.d.ts.map +1 -0
  148. package/dist/db/migrations.js +81 -0
  149. package/dist/db/migrations.js.map +1 -0
  150. package/dist/db/queries.d.ts +440 -0
  151. package/dist/db/queries.d.ts.map +1 -0
  152. package/dist/db/queries.js +1054 -0
  153. package/dist/db/queries.js.map +1 -0
  154. package/dist/db/schema.d.ts +132 -0
  155. package/dist/db/schema.d.ts.map +1 -0
  156. package/dist/db/schema.js +2 -0
  157. package/dist/db/schema.js.map +1 -0
  158. package/dist/delta/blastRadius.d.ts +24 -0
  159. package/dist/delta/blastRadius.d.ts.map +1 -0
  160. package/dist/delta/blastRadius.js +239 -0
  161. package/dist/delta/blastRadius.js.map +1 -0
  162. package/dist/delta/diff.d.ts +16 -0
  163. package/dist/delta/diff.d.ts.map +1 -0
  164. package/dist/delta/diff.js +236 -0
  165. package/dist/delta/diff.js.map +1 -0
  166. package/dist/delta/versioning.d.ts +11 -0
  167. package/dist/delta/versioning.d.ts.map +1 -0
  168. package/dist/delta/versioning.js +68 -0
  169. package/dist/delta/versioning.js.map +1 -0
  170. package/dist/graph/buildGraph.d.ts +12 -0
  171. package/dist/graph/buildGraph.d.ts.map +1 -0
  172. package/dist/graph/buildGraph.js +81 -0
  173. package/dist/graph/buildGraph.js.map +1 -0
  174. package/dist/graph/cache.d.ts +111 -0
  175. package/dist/graph/cache.d.ts.map +1 -0
  176. package/dist/graph/cache.js +237 -0
  177. package/dist/graph/cache.js.map +1 -0
  178. package/dist/graph/metrics.d.ts +12 -0
  179. package/dist/graph/metrics.d.ts.map +1 -0
  180. package/dist/graph/metrics.js +253 -0
  181. package/dist/graph/metrics.js.map +1 -0
  182. package/dist/graph/minHeap.d.ts +91 -0
  183. package/dist/graph/minHeap.d.ts.map +1 -0
  184. package/dist/graph/minHeap.js +159 -0
  185. package/dist/graph/minHeap.js.map +1 -0
  186. package/dist/graph/overview.d.ts +22 -0
  187. package/dist/graph/overview.d.ts.map +1 -0
  188. package/dist/graph/overview.js +379 -0
  189. package/dist/graph/overview.js.map +1 -0
  190. package/dist/graph/score.d.ts +27 -0
  191. package/dist/graph/score.d.ts.map +1 -0
  192. package/dist/graph/score.js +214 -0
  193. package/dist/graph/score.js.map +1 -0
  194. package/dist/graph/slice.d.ts +51 -0
  195. package/dist/graph/slice.d.ts.map +1 -0
  196. package/dist/graph/slice.js +1047 -0
  197. package/dist/graph/slice.js.map +1 -0
  198. package/dist/graph/sliceCache.d.ts +33 -0
  199. package/dist/graph/sliceCache.d.ts.map +1 -0
  200. package/dist/graph/sliceCache.js +140 -0
  201. package/dist/graph/sliceCache.js.map +1 -0
  202. package/dist/indexer/adapter/BaseAdapter.d.ts +29 -0
  203. package/dist/indexer/adapter/BaseAdapter.d.ts.map +1 -0
  204. package/dist/indexer/adapter/BaseAdapter.js +97 -0
  205. package/dist/indexer/adapter/BaseAdapter.js.map +1 -0
  206. package/dist/indexer/adapter/LanguageAdapter.d.ts +20 -0
  207. package/dist/indexer/adapter/LanguageAdapter.d.ts.map +1 -0
  208. package/dist/indexer/adapter/LanguageAdapter.js +2 -0
  209. package/dist/indexer/adapter/LanguageAdapter.js.map +1 -0
  210. package/dist/indexer/adapter/adapters.d.ts +57 -0
  211. package/dist/indexer/adapter/adapters.d.ts.map +1 -0
  212. package/dist/indexer/adapter/adapters.js +129 -0
  213. package/dist/indexer/adapter/adapters.js.map +1 -0
  214. package/dist/indexer/adapter/c.d.ts +18 -0
  215. package/dist/indexer/adapter/c.d.ts.map +1 -0
  216. package/dist/indexer/adapter/c.js +443 -0
  217. package/dist/indexer/adapter/c.js.map +1 -0
  218. package/dist/indexer/adapter/cpp.d.ts +18 -0
  219. package/dist/indexer/adapter/cpp.d.ts.map +1 -0
  220. package/dist/indexer/adapter/cpp.js +493 -0
  221. package/dist/indexer/adapter/cpp.js.map +1 -0
  222. package/dist/indexer/adapter/csharp.d.ts +48 -0
  223. package/dist/indexer/adapter/csharp.d.ts.map +1 -0
  224. package/dist/indexer/adapter/csharp.js +638 -0
  225. package/dist/indexer/adapter/csharp.js.map +1 -0
  226. package/dist/indexer/adapter/go.d.ts +14 -0
  227. package/dist/indexer/adapter/go.d.ts.map +1 -0
  228. package/dist/indexer/adapter/go.js +400 -0
  229. package/dist/indexer/adapter/go.js.map +1 -0
  230. package/dist/indexer/adapter/index.d.ts +14 -0
  231. package/dist/indexer/adapter/index.d.ts.map +1 -0
  232. package/dist/indexer/adapter/index.js +13 -0
  233. package/dist/indexer/adapter/index.js.map +1 -0
  234. package/dist/indexer/adapter/java.d.ts +14 -0
  235. package/dist/indexer/adapter/java.d.ts.map +1 -0
  236. package/dist/indexer/adapter/java.js +391 -0
  237. package/dist/indexer/adapter/java.js.map +1 -0
  238. package/dist/indexer/adapter/kotlin.d.ts +18 -0
  239. package/dist/indexer/adapter/kotlin.d.ts.map +1 -0
  240. package/dist/indexer/adapter/kotlin.js +599 -0
  241. package/dist/indexer/adapter/kotlin.js.map +1 -0
  242. package/dist/indexer/adapter/php.d.ts +18 -0
  243. package/dist/indexer/adapter/php.d.ts.map +1 -0
  244. package/dist/indexer/adapter/php.js +574 -0
  245. package/dist/indexer/adapter/php.js.map +1 -0
  246. package/dist/indexer/adapter/plugin/index.d.ts +3 -0
  247. package/dist/indexer/adapter/plugin/index.d.ts.map +1 -0
  248. package/dist/indexer/adapter/plugin/index.js +3 -0
  249. package/dist/indexer/adapter/plugin/index.js.map +1 -0
  250. package/dist/indexer/adapter/plugin/loader.d.ts +13 -0
  251. package/dist/indexer/adapter/plugin/loader.d.ts.map +1 -0
  252. package/dist/indexer/adapter/plugin/loader.js +154 -0
  253. package/dist/indexer/adapter/plugin/loader.js.map +1 -0
  254. package/dist/indexer/adapter/plugin/types.d.ts +76 -0
  255. package/dist/indexer/adapter/plugin/types.d.ts.map +1 -0
  256. package/dist/indexer/adapter/plugin/types.js +47 -0
  257. package/dist/indexer/adapter/plugin/types.js.map +1 -0
  258. package/dist/indexer/adapter/python.d.ts +14 -0
  259. package/dist/indexer/adapter/python.d.ts.map +1 -0
  260. package/dist/indexer/adapter/python.js +511 -0
  261. package/dist/indexer/adapter/python.js.map +1 -0
  262. package/dist/indexer/adapter/registry.d.ts +17 -0
  263. package/dist/indexer/adapter/registry.d.ts.map +1 -0
  264. package/dist/indexer/adapter/registry.js +134 -0
  265. package/dist/indexer/adapter/registry.js.map +1 -0
  266. package/dist/indexer/adapter/rust.d.ts +18 -0
  267. package/dist/indexer/adapter/rust.d.ts.map +1 -0
  268. package/dist/indexer/adapter/rust.js +709 -0
  269. package/dist/indexer/adapter/rust.js.map +1 -0
  270. package/dist/indexer/adapter/shell.d.ts +18 -0
  271. package/dist/indexer/adapter/shell.d.ts.map +1 -0
  272. package/dist/indexer/adapter/shell.js +345 -0
  273. package/dist/indexer/adapter/shell.js.map +1 -0
  274. package/dist/indexer/adapter/typescript.d.ts +14 -0
  275. package/dist/indexer/adapter/typescript.d.ts.map +1 -0
  276. package/dist/indexer/adapter/typescript.js +31 -0
  277. package/dist/indexer/adapter/typescript.js.map +1 -0
  278. package/dist/indexer/configEdges.d.ts +30 -0
  279. package/dist/indexer/configEdges.d.ts.map +1 -0
  280. package/dist/indexer/configEdges.js +260 -0
  281. package/dist/indexer/configEdges.js.map +1 -0
  282. package/dist/indexer/fileScanner.d.ts +16 -0
  283. package/dist/indexer/fileScanner.d.ts.map +1 -0
  284. package/dist/indexer/fileScanner.js +90 -0
  285. package/dist/indexer/fileScanner.js.map +1 -0
  286. package/dist/indexer/fingerprints.d.ts +23 -0
  287. package/dist/indexer/fingerprints.d.ts.map +1 -0
  288. package/dist/indexer/fingerprints.js +97 -0
  289. package/dist/indexer/fingerprints.js.map +1 -0
  290. package/dist/indexer/indexer.d.ts +58 -0
  291. package/dist/indexer/indexer.d.ts.map +1 -0
  292. package/dist/indexer/indexer.js +1229 -0
  293. package/dist/indexer/indexer.js.map +1 -0
  294. package/dist/indexer/summaries.d.ts +5 -0
  295. package/dist/indexer/summaries.d.ts.map +1 -0
  296. package/dist/indexer/summaries.js +331 -0
  297. package/dist/indexer/summaries.js.map +1 -0
  298. package/dist/indexer/treesitter/extractCalls.d.ts +40 -0
  299. package/dist/indexer/treesitter/extractCalls.d.ts.map +1 -0
  300. package/dist/indexer/treesitter/extractCalls.js +561 -0
  301. package/dist/indexer/treesitter/extractCalls.js.map +1 -0
  302. package/dist/indexer/treesitter/extractImports.d.ts +12 -0
  303. package/dist/indexer/treesitter/extractImports.d.ts.map +1 -0
  304. package/dist/indexer/treesitter/extractImports.js +184 -0
  305. package/dist/indexer/treesitter/extractImports.js.map +1 -0
  306. package/dist/indexer/treesitter/extractSymbols.d.ts +24 -0
  307. package/dist/indexer/treesitter/extractSymbols.d.ts.map +1 -0
  308. package/dist/indexer/treesitter/extractSymbols.js +389 -0
  309. package/dist/indexer/treesitter/extractSymbols.js.map +1 -0
  310. package/dist/indexer/treesitter/grammarLoader.d.ts +6 -0
  311. package/dist/indexer/treesitter/grammarLoader.d.ts.map +1 -0
  312. package/dist/indexer/treesitter/grammarLoader.js +125 -0
  313. package/dist/indexer/treesitter/grammarLoader.js.map +1 -0
  314. package/dist/indexer/treesitter/symbolUtils.d.ts +4 -0
  315. package/dist/indexer/treesitter/symbolUtils.d.ts.map +1 -0
  316. package/dist/indexer/treesitter/symbolUtils.js +26 -0
  317. package/dist/indexer/treesitter/symbolUtils.js.map +1 -0
  318. package/dist/indexer/treesitter/tsTreesitter.d.ts +9 -0
  319. package/dist/indexer/treesitter/tsTreesitter.d.ts.map +1 -0
  320. package/dist/indexer/treesitter/tsTreesitter.js +54 -0
  321. package/dist/indexer/treesitter/tsTreesitter.js.map +1 -0
  322. package/dist/indexer/treesitter/types.d.ts +19 -0
  323. package/dist/indexer/treesitter/types.d.ts.map +1 -0
  324. package/dist/indexer/treesitter/types.js +7 -0
  325. package/dist/indexer/treesitter/types.js.map +1 -0
  326. package/dist/indexer/ts/tsParser.d.ts +20 -0
  327. package/dist/indexer/ts/tsParser.d.ts.map +1 -0
  328. package/dist/indexer/ts/tsParser.js +114 -0
  329. package/dist/indexer/ts/tsParser.js.map +1 -0
  330. package/dist/indexer/worker.d.ts +2 -0
  331. package/dist/indexer/worker.d.ts.map +1 -0
  332. package/dist/indexer/worker.js +59 -0
  333. package/dist/indexer/worker.js.map +1 -0
  334. package/dist/indexer/workerPool.d.ts +20 -0
  335. package/dist/indexer/workerPool.d.ts.map +1 -0
  336. package/dist/indexer/workerPool.js +93 -0
  337. package/dist/indexer/workerPool.js.map +1 -0
  338. package/dist/main.d.ts +2 -0
  339. package/dist/main.d.ts.map +1 -0
  340. package/dist/main.js +111 -0
  341. package/dist/main.js.map +1 -0
  342. package/dist/mcp/errors.d.ts +40 -0
  343. package/dist/mcp/errors.d.ts.map +1 -0
  344. package/dist/mcp/errors.js +66 -0
  345. package/dist/mcp/errors.js.map +1 -0
  346. package/dist/mcp/resources.d.ts +24 -0
  347. package/dist/mcp/resources.d.ts.map +1 -0
  348. package/dist/mcp/resources.js +140 -0
  349. package/dist/mcp/resources.js.map +1 -0
  350. package/dist/mcp/telemetry.d.ts +67 -0
  351. package/dist/mcp/telemetry.d.ts.map +1 -0
  352. package/dist/mcp/telemetry.js +128 -0
  353. package/dist/mcp/telemetry.js.map +1 -0
  354. package/dist/mcp/tools/agent.d.ts +241 -0
  355. package/dist/mcp/tools/agent.d.ts.map +1 -0
  356. package/dist/mcp/tools/agent.js +113 -0
  357. package/dist/mcp/tools/agent.js.map +1 -0
  358. package/dist/mcp/tools/code.d.ts +32 -0
  359. package/dist/mcp/tools/code.d.ts.map +1 -0
  360. package/dist/mcp/tools/code.js +388 -0
  361. package/dist/mcp/tools/code.js.map +1 -0
  362. package/dist/mcp/tools/delta.d.ts +12 -0
  363. package/dist/mcp/tools/delta.d.ts.map +1 -0
  364. package/dist/mcp/tools/delta.js +82 -0
  365. package/dist/mcp/tools/delta.js.map +1 -0
  366. package/dist/mcp/tools/index.d.ts +3 -0
  367. package/dist/mcp/tools/index.d.ts.map +1 -0
  368. package/dist/mcp/tools/index.js +35 -0
  369. package/dist/mcp/tools/index.js.map +1 -0
  370. package/dist/mcp/tools/policy.d.ts +20 -0
  371. package/dist/mcp/tools/policy.d.ts.map +1 -0
  372. package/dist/mcp/tools/policy.js +67 -0
  373. package/dist/mcp/tools/policy.js.map +1 -0
  374. package/dist/mcp/tools/prRisk.d.ts +34 -0
  375. package/dist/mcp/tools/prRisk.d.ts.map +1 -0
  376. package/dist/mcp/tools/prRisk.js +304 -0
  377. package/dist/mcp/tools/prRisk.js.map +1 -0
  378. package/dist/mcp/tools/repo.d.ts +46 -0
  379. package/dist/mcp/tools/repo.d.ts.map +1 -0
  380. package/dist/mcp/tools/repo.js +224 -0
  381. package/dist/mcp/tools/repo.js.map +1 -0
  382. package/dist/mcp/tools/slice.d.ts +44 -0
  383. package/dist/mcp/tools/slice.d.ts.map +1 -0
  384. package/dist/mcp/tools/slice.js +688 -0
  385. package/dist/mcp/tools/slice.js.map +1 -0
  386. package/dist/mcp/tools/symbol.d.ts +21 -0
  387. package/dist/mcp/tools/symbol.d.ts.map +1 -0
  388. package/dist/mcp/tools/symbol.js +416 -0
  389. package/dist/mcp/tools/symbol.js.map +1 -0
  390. package/dist/mcp/tools.d.ts +6003 -0
  391. package/dist/mcp/tools.d.ts.map +1 -0
  392. package/dist/mcp/tools.js +932 -0
  393. package/dist/mcp/tools.js.map +1 -0
  394. package/dist/mcp/types.d.ts +500 -0
  395. package/dist/mcp/types.d.ts.map +1 -0
  396. package/dist/mcp/types.js +2 -0
  397. package/dist/mcp/types.js.map +1 -0
  398. package/dist/policy/engine.d.ts +21 -0
  399. package/dist/policy/engine.d.ts.map +1 -0
  400. package/dist/policy/engine.js +477 -0
  401. package/dist/policy/engine.js.map +1 -0
  402. package/dist/policy/types.d.ts +73 -0
  403. package/dist/policy/types.d.ts.map +1 -0
  404. package/dist/policy/types.js +12 -0
  405. package/dist/policy/types.js.map +1 -0
  406. package/dist/server.d.ts +17 -0
  407. package/dist/server.d.ts.map +1 -0
  408. package/dist/server.js +136 -0
  409. package/dist/server.js.map +1 -0
  410. package/dist/sync/pull.d.ts +4 -0
  411. package/dist/sync/pull.d.ts.map +1 -0
  412. package/dist/sync/pull.js +113 -0
  413. package/dist/sync/pull.js.map +1 -0
  414. package/dist/sync/sync.d.ts +6 -0
  415. package/dist/sync/sync.d.ts.map +1 -0
  416. package/dist/sync/sync.js +244 -0
  417. package/dist/sync/sync.js.map +1 -0
  418. package/dist/sync/types.d.ts +136 -0
  419. package/dist/sync/types.d.ts.map +1 -0
  420. package/dist/sync/types.js +2 -0
  421. package/dist/sync/types.js.map +1 -0
  422. package/dist/ts/diagnostics.d.ts +61 -0
  423. package/dist/ts/diagnostics.d.ts.map +1 -0
  424. package/dist/ts/diagnostics.js +259 -0
  425. package/dist/ts/diagnostics.js.map +1 -0
  426. package/dist/ts/mapping.d.ts +27 -0
  427. package/dist/ts/mapping.d.ts.map +1 -0
  428. package/dist/ts/mapping.js +68 -0
  429. package/dist/ts/mapping.js.map +1 -0
  430. package/dist/util/asyncFs.d.ts +103 -0
  431. package/dist/util/asyncFs.d.ts.map +1 -0
  432. package/dist/util/asyncFs.js +133 -0
  433. package/dist/util/asyncFs.js.map +1 -0
  434. package/dist/util/concurrency.d.ts +77 -0
  435. package/dist/util/concurrency.d.ts.map +1 -0
  436. package/dist/util/concurrency.js +146 -0
  437. package/dist/util/concurrency.js.map +1 -0
  438. package/dist/util/depLabels.d.ts +3 -0
  439. package/dist/util/depLabels.d.ts.map +1 -0
  440. package/dist/util/depLabels.js +19 -0
  441. package/dist/util/depLabels.js.map +1 -0
  442. package/dist/util/findPackageRoot.d.ts +7 -0
  443. package/dist/util/findPackageRoot.d.ts.map +1 -0
  444. package/dist/util/findPackageRoot.js +21 -0
  445. package/dist/util/findPackageRoot.js.map +1 -0
  446. package/dist/util/hashing.d.ts +9 -0
  447. package/dist/util/hashing.d.ts.map +1 -0
  448. package/dist/util/hashing.js +37 -0
  449. package/dist/util/hashing.js.map +1 -0
  450. package/dist/util/logger.d.ts +14 -0
  451. package/dist/util/logger.d.ts.map +1 -0
  452. package/dist/util/logger.js +51 -0
  453. package/dist/util/logger.js.map +1 -0
  454. package/dist/util/paths.d.ts +6 -0
  455. package/dist/util/paths.d.ts.map +1 -0
  456. package/dist/util/paths.js +79 -0
  457. package/dist/util/paths.js.map +1 -0
  458. package/dist/util/time.d.ts +4 -0
  459. package/dist/util/time.d.ts.map +1 -0
  460. package/dist/util/time.js +20 -0
  461. package/dist/util/time.js.map +1 -0
  462. package/dist/util/tokenize.d.ts +5 -0
  463. package/dist/util/tokenize.d.ts.map +1 -0
  464. package/dist/util/tokenize.js +30 -0
  465. package/dist/util/tokenize.js.map +1 -0
  466. package/dist/util/truncation.d.ts +38 -0
  467. package/dist/util/truncation.d.ts.map +1 -0
  468. package/dist/util/truncation.js +259 -0
  469. package/dist/util/truncation.js.map +1 -0
  470. package/migrations/.gitkeep +1 -0
  471. package/migrations/0001_init.sql +61 -0
  472. package/migrations/0002_edges_indexes.sql +2 -0
  473. package/migrations/0003_versions.sql +22 -0
  474. package/migrations/0004_metrics_audit.sql +23 -0
  475. package/migrations/0005_slice_handles.sql +15 -0
  476. package/migrations/0006_content_addressed.sql +29 -0
  477. package/migrations/0007_add_language_to_symbols.sql +28 -0
  478. package/migrations/0008_performance_indexes.sql +21 -0
  479. package/migrations/0009_fix_edge_fks.sql +37 -0
  480. package/migrations/0010_standardize_fks.sql +35 -0
  481. package/migrations/0011_add_directory_column.sql +20 -0
  482. package/migrations/0012_symbol_references.sql +16 -0
  483. package/migrations/0013_sync_artifacts.sql +17 -0
  484. package/package.json +107 -0
  485. package/templates/CLAUDE.md.template +179 -0
  486. package/templates/README.md +228 -0
  487. package/templates/claude-code.json +54 -0
  488. package/templates/codex.json +55 -0
  489. package/templates/gemini.json +66 -0
  490. package/templates/opencode.json +90 -0
  491. package/templates/plugin-template/LICENSE +21 -0
  492. package/templates/plugin-template/README.md +309 -0
  493. package/templates/plugin-template/index.ts +160 -0
  494. package/templates/plugin-template/package.json +31 -0
  495. package/templates/plugin-template/test/plugin.test.ts +186 -0
  496. package/templates/plugin-template/tsconfig.json +21 -0
@@ -0,0 +1,1229 @@
1
+ import { watch } from "fs";
2
+ import { createRequire } from "module";
3
+ import { dirname, join, relative, resolve } from "path";
4
+ import { platform } from "process";
5
+ import os from "os";
6
+ import { getRepo, upsertFile, deleteSymbolsByFileWithEdges, deleteFileTransaction, upsertSymbolTransaction, createEdgeTransaction, createSnapshotTransaction, getFilesByRepo, getFileByRepoPath, getSymbolsByFile, getSymbolsByFileLite, getSymbolsByRepoForSnapshot, getEdgesByRepo, getSymbolsByRepo, getSymbol, getFile, deleteSymbolReferencesByFileId, insertSymbolReference, } from "../db/queries.js";
7
+ import { getDb } from "../db/db.js";
8
+ import { scanRepository } from "./fileScanner.js";
9
+ import { generateAstFingerprint, generateSymbolId } from "./fingerprints.js";
10
+ import { WATCH_DEBOUNCE_MS, WATCH_STABILITY_THRESHOLD_MS, WATCH_POLL_INTERVAL_MS, WATCHER_ERROR_MAX_COUNT, } from "../config/constants.js";
11
+ import { hashContent } from "../util/hashing.js";
12
+ import { normalizePath } from "../util/paths.js";
13
+ import { generateSummary, extractInvariants, extractSideEffects, } from "./summaries.js";
14
+ import { updateMetricsForRepo } from "../graph/metrics.js";
15
+ import { extractConfigEdgesFromTree } from "./configEdges.js";
16
+ import { loadConfig } from "../config/loadConfig.js";
17
+ import { createTsCallResolver } from "./ts/tsParser.js";
18
+ import { getAdapterForExtension } from "./adapter/registry.js";
19
+ import { ParserWorkerPool } from "./workerPool.js";
20
+ import { logger } from "../util/logger.js";
21
+ import { readFileAsync, existsAsync } from "../util/asyncFs.js";
22
+ const require = createRequire(import.meta.url);
23
+ async function processFile(params) {
24
+ const { repoId, repoRoot, fileMeta, languages, mode, existingFile, symbolIndex, pendingCallEdges, createdCallEdges, tsResolver, config, allSymbolsByName, onProgress: _onProgress, workerPool, } = params;
25
+ try {
26
+ const filePath = join(repoRoot, fileMeta.path);
27
+ const content = await readFileAsync(filePath, "utf-8");
28
+ const contentHash = hashContent(content);
29
+ const ext = fileMeta.path.split(".").pop() || "";
30
+ const extWithDot = `.${ext}`;
31
+ if (mode === "incremental" &&
32
+ existingFile &&
33
+ existingFile.content_hash === contentHash) {
34
+ return {
35
+ symbolsIndexed: 0,
36
+ edgesCreated: 0,
37
+ changed: false,
38
+ configEdges: [],
39
+ };
40
+ }
41
+ if (!languages.includes(ext)) {
42
+ logger.debug(`Language ${ext} not in enabled languages, skipping ${fileMeta.path}`);
43
+ if (existingFile) {
44
+ deleteSymbolsByFileWithEdges(existingFile.file_id);
45
+ }
46
+ upsertFile({
47
+ repo_id: repoId,
48
+ rel_path: fileMeta.path,
49
+ content_hash: contentHash,
50
+ language: ext,
51
+ byte_size: fileMeta.size,
52
+ last_indexed_at: new Date().toISOString(),
53
+ });
54
+ return {
55
+ symbolsIndexed: 0,
56
+ edgesCreated: 0,
57
+ changed: true,
58
+ configEdges: [],
59
+ };
60
+ }
61
+ const adapter = getAdapterForExtension(extWithDot);
62
+ if (!adapter) {
63
+ logger.debug(`No adapter found for ${extWithDot}, skipping ${fileMeta.path}`);
64
+ if (existingFile) {
65
+ deleteSymbolsByFileWithEdges(existingFile.file_id);
66
+ }
67
+ upsertFile({
68
+ repo_id: repoId,
69
+ rel_path: fileMeta.path,
70
+ content_hash: contentHash,
71
+ language: ext,
72
+ byte_size: fileMeta.size,
73
+ last_indexed_at: new Date().toISOString(),
74
+ });
75
+ return {
76
+ symbolsIndexed: 0,
77
+ edgesCreated: 0,
78
+ changed: true,
79
+ configEdges: [],
80
+ };
81
+ }
82
+ let symbolsWithNodeIds = [];
83
+ let imports = [];
84
+ let calls = [];
85
+ let parseError = null;
86
+ let tree = null;
87
+ try {
88
+ if (workerPool) {
89
+ try {
90
+ const result = await workerPool.parse(filePath, content, extWithDot);
91
+ symbolsWithNodeIds = result.symbols;
92
+ imports = result.imports;
93
+ calls = result.calls;
94
+ // tree remains null when using worker pool - fingerprinting will use empty string
95
+ }
96
+ catch (workerError) {
97
+ parseError =
98
+ workerError instanceof Error
99
+ ? workerError
100
+ : new Error(String(workerError));
101
+ logger.warn(`Worker pool parse failed for ${fileMeta.path}, falling back to sync: ${parseError.message}`);
102
+ }
103
+ }
104
+ if (parseError || !workerPool) {
105
+ tree = adapter.parse(content, filePath);
106
+ if (!tree) {
107
+ if (existingFile) {
108
+ deleteSymbolsByFileWithEdges(existingFile.file_id);
109
+ }
110
+ upsertFile({
111
+ repo_id: repoId,
112
+ rel_path: fileMeta.path,
113
+ content_hash: contentHash,
114
+ language: adapter.languageId,
115
+ byte_size: fileMeta.size,
116
+ last_indexed_at: new Date().toISOString(),
117
+ });
118
+ return {
119
+ symbolsIndexed: 0,
120
+ edgesCreated: 0,
121
+ changed: true,
122
+ configEdges: [],
123
+ };
124
+ }
125
+ let extractedSymbols;
126
+ try {
127
+ extractedSymbols = adapter.extractSymbols(tree, content, filePath);
128
+ }
129
+ catch (error) {
130
+ logger.warn(`Partial parse error for ${fileMeta.path}: ${error}, extracting available symbols`);
131
+ extractedSymbols = [];
132
+ }
133
+ imports = adapter.extractImports(tree, content, filePath);
134
+ symbolsWithNodeIds = extractedSymbols.map((symbol) => ({
135
+ nodeId: symbol.nodeId,
136
+ kind: symbol.kind,
137
+ name: symbol.name,
138
+ exported: symbol.exported,
139
+ range: symbol.range,
140
+ signature: symbol.signature,
141
+ visibility: symbol.visibility,
142
+ }));
143
+ calls = adapter.extractCalls(tree, content, filePath, symbolsWithNodeIds);
144
+ }
145
+ }
146
+ catch (error) {
147
+ logger.error(`Fatal parse error for ${fileMeta.path}: ${error}`);
148
+ return {
149
+ symbolsIndexed: 0,
150
+ edgesCreated: 0,
151
+ changed: false,
152
+ configEdges: [],
153
+ };
154
+ }
155
+ const symbolsIndexed = symbolsWithNodeIds.length;
156
+ let edgesCreated = 0;
157
+ const existingSymbolsById = new Map();
158
+ if (existingFile) {
159
+ const existingSymbols = getSymbolsByFile(existingFile.file_id);
160
+ for (const symbol of existingSymbols) {
161
+ existingSymbolsById.set(symbol.symbol_id, symbol);
162
+ }
163
+ }
164
+ const fileRecord = {
165
+ repo_id: repoId,
166
+ rel_path: fileMeta.path,
167
+ content_hash: contentHash,
168
+ language: ext,
169
+ byte_size: fileMeta.size,
170
+ last_indexed_at: new Date().toISOString(),
171
+ };
172
+ upsertFile(fileRecord);
173
+ const file = getFileByRepoPath(repoId, fileMeta.path);
174
+ if (!file) {
175
+ return { symbolsIndexed, edgesCreated, changed: true, configEdges: [] };
176
+ }
177
+ if (existingFile) {
178
+ deleteSymbolsByFileWithEdges(existingFile.file_id);
179
+ deleteSymbolReferencesByFileId(existingFile.file_id);
180
+ }
181
+ if (isTestFile(fileMeta.path, languages)) {
182
+ extractSymbolReferences(content, repoId, file.file_id);
183
+ }
184
+ const exportSymbols = symbolsWithNodeIds.filter((symbol) => symbol.exported);
185
+ const edgeSourceSymbols = exportSymbols.length > 0 ? exportSymbols : symbolsWithNodeIds;
186
+ const extensions = languages.map((lang) => `.${lang}`);
187
+ const importResolution = await resolveImportTargets(repoId, repoRoot, fileMeta.path, imports, extensions, adapter.languageId);
188
+ const importTargets = importResolution.targets;
189
+ const symbolDetails = symbolsWithNodeIds.map((extractedSymbol) => {
190
+ // When using worker pool, tree is null - use empty fingerprint
191
+ // The fingerprint is still useful for change detection but not critical
192
+ let astFingerprint = "";
193
+ if (tree) {
194
+ const astNode = tree.rootNode
195
+ .descendantsOfType(extractedSymbol.kind === "function"
196
+ ? "function_declaration"
197
+ : extractedSymbol.kind === "class"
198
+ ? "class_declaration"
199
+ : extractedSymbol.kind === "interface"
200
+ ? "interface_declaration"
201
+ : extractedSymbol.kind === "type"
202
+ ? "type_alias_declaration"
203
+ : extractedSymbol.kind === "method"
204
+ ? "method_definition"
205
+ : extractedSymbol.kind === "variable"
206
+ ? "variable_declaration"
207
+ : "ambient_statement")
208
+ .find((node) => {
209
+ const nameNode = node.childForFieldName("name");
210
+ return nameNode?.text === extractedSymbol.name;
211
+ });
212
+ astFingerprint = astNode ? generateAstFingerprint(astNode) : "";
213
+ }
214
+ const symbolId = generateSymbolId(repoId, fileMeta.path, extractedSymbol.kind, extractedSymbol.name, astFingerprint);
215
+ return {
216
+ extractedSymbol,
217
+ astFingerprint,
218
+ symbolId,
219
+ };
220
+ });
221
+ const nodeIdToSymbolId = new Map();
222
+ const nameToSymbolIds = new Map();
223
+ for (const detail of symbolDetails) {
224
+ nodeIdToSymbolId.set(detail.extractedSymbol.nodeId, detail.symbolId);
225
+ const existing = nameToSymbolIds.get(detail.extractedSymbol.name) ?? [];
226
+ existing.push(detail.symbolId);
227
+ nameToSymbolIds.set(detail.extractedSymbol.name, existing);
228
+ }
229
+ for (const detail of symbolDetails) {
230
+ const extractedSymbol = detail.extractedSymbol;
231
+ const symbolId = detail.symbolId;
232
+ const existingSymbol = existingSymbolsById.get(symbolId);
233
+ let summary = existingSymbol?.summary ?? null;
234
+ if (summary === null) {
235
+ summary = generateSummary(extractedSymbol, content);
236
+ }
237
+ let invariantsJson = existingSymbol?.invariants_json ?? null;
238
+ if (invariantsJson === null) {
239
+ const invariants = extractInvariants(extractedSymbol, content);
240
+ invariantsJson =
241
+ invariants.length > 0 ? JSON.stringify(invariants) : null;
242
+ }
243
+ let sideEffectsJson = existingSymbol?.side_effects_json ?? null;
244
+ if (sideEffectsJson === null) {
245
+ const sideEffects = extractSideEffects(extractedSymbol, content);
246
+ sideEffectsJson =
247
+ sideEffects.length > 0 ? JSON.stringify(sideEffects) : null;
248
+ }
249
+ const symbol = {
250
+ symbol_id: symbolId,
251
+ repo_id: repoId,
252
+ file_id: file.file_id,
253
+ kind: extractedSymbol.kind,
254
+ name: extractedSymbol.name,
255
+ exported: extractedSymbol.exported ? 1 : 0,
256
+ visibility: extractedSymbol.visibility || null,
257
+ language: adapter.languageId,
258
+ range_start_line: extractedSymbol.range.startLine,
259
+ range_start_col: extractedSymbol.range.startCol,
260
+ range_end_line: extractedSymbol.range.endLine,
261
+ range_end_col: extractedSymbol.range.endCol,
262
+ ast_fingerprint: detail.astFingerprint,
263
+ signature_json: extractedSymbol.signature
264
+ ? JSON.stringify(extractedSymbol.signature)
265
+ : null,
266
+ summary,
267
+ invariants_json: invariantsJson,
268
+ side_effects_json: sideEffectsJson,
269
+ updated_at: new Date().toISOString(),
270
+ };
271
+ upsertSymbolTransaction(symbol);
272
+ if (symbolIndex) {
273
+ addToSymbolIndex(symbolIndex, fileMeta.path, symbol.symbol_id, symbol.name, symbol.kind);
274
+ }
275
+ if (edgeSourceSymbols.some((s) => s.nodeId === extractedSymbol.nodeId)) {
276
+ for (const target of importTargets) {
277
+ const edge = {
278
+ repo_id: repoId,
279
+ from_symbol_id: symbolId,
280
+ to_symbol_id: target.symbolId,
281
+ type: "import",
282
+ weight: 0.6,
283
+ provenance: `import:${target.provenance}`,
284
+ created_at: new Date().toISOString(),
285
+ };
286
+ createEdgeTransaction(edge);
287
+ edgesCreated++;
288
+ }
289
+ }
290
+ for (const call of calls) {
291
+ if (call.callerNodeId !== extractedSymbol.nodeId) {
292
+ continue;
293
+ }
294
+ const resolved = resolveCallTarget(call, nodeIdToSymbolId, nameToSymbolIds, importResolution.importedNameToSymbolIds, importResolution.namespaceImports);
295
+ if (!resolved) {
296
+ continue;
297
+ }
298
+ // 36-1.3: Handle both resolved and unresolved edges
299
+ if (resolved.isResolved && resolved.symbolId) {
300
+ // Fully resolved edge
301
+ const edgeKey = `${symbolId}->${resolved.symbolId}`;
302
+ if (createdCallEdges && createdCallEdges.has(edgeKey)) {
303
+ continue;
304
+ }
305
+ const edge = {
306
+ repo_id: repoId,
307
+ from_symbol_id: symbolId,
308
+ to_symbol_id: resolved.symbolId,
309
+ type: "call",
310
+ weight: 1.0,
311
+ provenance: `call:${call.calleeIdentifier}`,
312
+ created_at: new Date().toISOString(),
313
+ };
314
+ createEdgeTransaction(edge);
315
+ createdCallEdges?.add(edgeKey);
316
+ edgesCreated++;
317
+ }
318
+ else if (resolved.targetName) {
319
+ // 36-1.3: Unresolved edge - still useful for graph traversal
320
+ // Use a placeholder symbol ID that encodes the unresolved target
321
+ const unresolvedTargetId = `unresolved:call:${resolved.targetName}`;
322
+ const edgeKey = `${symbolId}->${unresolvedTargetId}`;
323
+ if (createdCallEdges && createdCallEdges.has(edgeKey)) {
324
+ continue;
325
+ }
326
+ const edge = {
327
+ repo_id: repoId,
328
+ from_symbol_id: symbolId,
329
+ to_symbol_id: unresolvedTargetId,
330
+ type: "call",
331
+ weight: 0.5, // Lower weight for unresolved edges
332
+ provenance: `unresolved-call:${call.calleeIdentifier}${resolved.candidateCount ? `:candidates=${resolved.candidateCount}` : ""}`,
333
+ created_at: new Date().toISOString(),
334
+ };
335
+ createEdgeTransaction(edge);
336
+ createdCallEdges?.add(edgeKey);
337
+ edgesCreated++;
338
+ }
339
+ }
340
+ }
341
+ if (tsResolver && symbolIndex && pendingCallEdges && createdCallEdges) {
342
+ const tsCalls = tsResolver.getResolvedCalls(fileMeta.path);
343
+ for (const tsCall of tsCalls) {
344
+ const callerNodeId = findEnclosingSymbolByRange(tsCall.caller, symbolDetails);
345
+ if (!callerNodeId)
346
+ continue;
347
+ const fromSymbolId = nodeIdToSymbolId.get(callerNodeId);
348
+ if (!fromSymbolId)
349
+ continue;
350
+ const toSymbolId = resolveSymbolIdFromIndex(symbolIndex, repoId, tsCall.callee.filePath, tsCall.callee.name, tsCall.callee.kind, adapter.languageId);
351
+ if (toSymbolId) {
352
+ const edgeKey = `${fromSymbolId}->${toSymbolId}`;
353
+ if (createdCallEdges.has(edgeKey))
354
+ continue;
355
+ createEdgeTransaction({
356
+ repo_id: repoId,
357
+ from_symbol_id: fromSymbolId,
358
+ to_symbol_id: toSymbolId,
359
+ type: "call",
360
+ weight: 1.0,
361
+ provenance: `ts-call:${tsCall.callee.name}`,
362
+ created_at: new Date().toISOString(),
363
+ });
364
+ createdCallEdges.add(edgeKey);
365
+ edgesCreated++;
366
+ }
367
+ else {
368
+ pendingCallEdges.push({
369
+ fromSymbolId,
370
+ toFile: tsCall.callee.filePath,
371
+ toName: tsCall.callee.name,
372
+ toKind: tsCall.callee.kind,
373
+ provenance: `ts-call:${tsCall.callee.name}`,
374
+ callerLanguage: adapter.languageId,
375
+ });
376
+ }
377
+ }
378
+ }
379
+ let configEdges = [];
380
+ // Config edges require the AST tree - skip when using worker pool (tree is null)
381
+ if (config && allSymbolsByName && tree) {
382
+ const fileSymbols = getSymbolsByFile(file.file_id);
383
+ configEdges = extractConfigEdgesFromTree({
384
+ repoId,
385
+ repoRoot,
386
+ config,
387
+ tree,
388
+ fileSymbols,
389
+ allSymbolsByName,
390
+ });
391
+ }
392
+ return { symbolsIndexed, edgesCreated, changed: true, configEdges };
393
+ }
394
+ catch (error) {
395
+ console.error(`Error processing file ${fileMeta.path}:`, error);
396
+ return {
397
+ symbolsIndexed: 0,
398
+ edgesCreated: 0,
399
+ changed: false,
400
+ configEdges: [],
401
+ };
402
+ }
403
+ }
404
+ function isTestFile(relPath, languages) {
405
+ const ext = relPath.split(".").pop() || "";
406
+ if (!languages.includes(ext))
407
+ return false;
408
+ const fileName = relPath.split("/").pop() || relPath.split("\\").pop() || "";
409
+ const hasTestSuffix = fileName.includes(".test.") || fileName.includes(".spec.");
410
+ const isInTestDir = relPath.includes("/tests/") ||
411
+ relPath.includes("\\tests\\") ||
412
+ relPath.includes("/__tests__/") ||
413
+ relPath.includes("\\__tests__\\");
414
+ return hasTestSuffix || isInTestDir;
415
+ }
416
+ function extractSymbolReferences(content, repoId, fileId) {
417
+ const tokens = content.match(/[A-Za-z_][A-Za-z0-9_]*/g);
418
+ if (!tokens)
419
+ return;
420
+ const uniqueTokens = new Set(tokens);
421
+ const createdAt = new Date().toISOString();
422
+ for (const token of uniqueTokens) {
423
+ insertSymbolReference({
424
+ repo_id: repoId,
425
+ symbol_name: token,
426
+ file_id: fileId,
427
+ line_number: null,
428
+ created_at: createdAt,
429
+ });
430
+ }
431
+ }
432
+ async function resolveImportTargets(repoId, repoRoot, importerRelPath, imports, extensions, importerLanguage) {
433
+ const targets = [];
434
+ const importedNameToSymbolIds = new Map();
435
+ const namespaceImports = new Map();
436
+ for (const imp of imports) {
437
+ const resolvedPath = await resolveImportToPath(repoRoot, importerRelPath, imp.specifier, extensions);
438
+ const importedNames = new Set();
439
+ if (imp.defaultImport)
440
+ importedNames.add(imp.defaultImport);
441
+ for (const name of imp.imports)
442
+ importedNames.add(name);
443
+ if (importedNames.size === 0) {
444
+ importedNames.add("*");
445
+ }
446
+ if (!resolvedPath) {
447
+ for (const name of importedNames) {
448
+ targets.push({
449
+ symbolId: `unresolved:${imp.specifier}:${name}`,
450
+ provenance: `${imp.specifier}:${name}`,
451
+ });
452
+ }
453
+ if (imp.namespaceImport) {
454
+ targets.push({
455
+ symbolId: `unresolved:${imp.specifier}:* as ${imp.namespaceImport}`,
456
+ provenance: `${imp.specifier}:* as ${imp.namespaceImport}`,
457
+ });
458
+ }
459
+ continue;
460
+ }
461
+ const targetFile = getFileByRepoPath(repoId, resolvedPath);
462
+ if (!targetFile) {
463
+ for (const name of importedNames) {
464
+ targets.push({
465
+ symbolId: `unresolved:${resolvedPath}:${name}`,
466
+ provenance: `${resolvedPath}:${name}`,
467
+ });
468
+ }
469
+ if (imp.namespaceImport) {
470
+ targets.push({
471
+ symbolId: `unresolved:${resolvedPath}:* as ${imp.namespaceImport}`,
472
+ provenance: `${resolvedPath}:* as ${imp.namespaceImport}`,
473
+ });
474
+ }
475
+ continue;
476
+ }
477
+ // ML-D.1: Language-aware import resolution
478
+ // Cross-language imports are resolved when possible
479
+ if (targetFile.language !== importerLanguage) {
480
+ for (const name of importedNames) {
481
+ targets.push({
482
+ symbolId: `unresolved:${resolvedPath}:${name}`,
483
+ provenance: `cross-language:${targetFile.language}->${importerLanguage}:${name}`,
484
+ });
485
+ }
486
+ if (imp.namespaceImport) {
487
+ targets.push({
488
+ symbolId: `unresolved:${resolvedPath}:* as ${imp.namespaceImport}`,
489
+ provenance: `cross-language:${targetFile.language}->${importerLanguage}:* as ${imp.namespaceImport}`,
490
+ });
491
+ }
492
+ }
493
+ const targetSymbols = getSymbolsByFileLite(targetFile.file_id).filter((symbol) => symbol.exported === 1);
494
+ for (const name of importedNames) {
495
+ if (name.startsWith("*")) {
496
+ targets.push({
497
+ symbolId: `unresolved:${resolvedPath}:${name}`,
498
+ provenance: `${resolvedPath}:${name}`,
499
+ });
500
+ continue;
501
+ }
502
+ let match = targetSymbols.find((symbol) => symbol.name === name);
503
+ if (!match && imp.defaultImport === name && targetSymbols.length === 1) {
504
+ match = targetSymbols[0];
505
+ }
506
+ if (match) {
507
+ targets.push({
508
+ symbolId: match.symbol_id,
509
+ provenance: `${resolvedPath}:${name}`,
510
+ });
511
+ const existing = importedNameToSymbolIds.get(name) ?? [];
512
+ existing.push(match.symbol_id);
513
+ importedNameToSymbolIds.set(name, existing);
514
+ }
515
+ else {
516
+ targets.push({
517
+ symbolId: `unresolved:${resolvedPath}:${name}`,
518
+ provenance: `${resolvedPath}:${name}`,
519
+ });
520
+ }
521
+ }
522
+ if (imp.namespaceImport) {
523
+ const namespaceMap = new Map();
524
+ for (const symbol of targetSymbols) {
525
+ namespaceMap.set(symbol.name, symbol.symbol_id);
526
+ targets.push({
527
+ symbolId: symbol.symbol_id,
528
+ provenance: `${resolvedPath}:*`,
529
+ });
530
+ }
531
+ if (namespaceMap.size > 0) {
532
+ namespaceImports.set(imp.namespaceImport, namespaceMap);
533
+ }
534
+ else {
535
+ targets.push({
536
+ symbolId: `unresolved:${resolvedPath}:* as ${imp.namespaceImport}`,
537
+ provenance: `${resolvedPath}:* as ${imp.namespaceImport}`,
538
+ });
539
+ }
540
+ }
541
+ }
542
+ return { targets, importedNameToSymbolIds, namespaceImports };
543
+ }
544
+ async function resolveImportToPath(repoRoot, importerRelPath, specifier, extensions) {
545
+ if (!specifier.startsWith("./") && !specifier.startsWith("../")) {
546
+ return null;
547
+ }
548
+ const importerDir = dirname(importerRelPath);
549
+ const baseRelPath = normalizePath(join(importerDir, specifier));
550
+ const baseAbsPath = resolve(repoRoot, baseRelPath);
551
+ const hasExtension = extensions.some((ext) => baseRelPath.endsWith(ext));
552
+ const candidates = [];
553
+ if (hasExtension) {
554
+ candidates.push(baseRelPath);
555
+ }
556
+ else {
557
+ for (const ext of extensions) {
558
+ candidates.push(`${baseRelPath}${ext}`);
559
+ }
560
+ for (const ext of extensions) {
561
+ candidates.push(normalizePath(join(baseRelPath, `index${ext}`)));
562
+ }
563
+ }
564
+ for (const relPath of candidates) {
565
+ const absPath = resolve(repoRoot, relPath);
566
+ if (await existsAsync(absPath)) {
567
+ return normalizePath(relPath);
568
+ }
569
+ }
570
+ if (hasExtension && (await existsAsync(baseAbsPath))) {
571
+ return normalizePath(baseRelPath);
572
+ }
573
+ return null;
574
+ }
575
+ function resolveCallTarget(call, nodeIdToSymbolId, nameToSymbolIds, importedNameToSymbolIds, namespaceImports) {
576
+ // Already resolved to a specific symbol
577
+ if (call.calleeSymbolId && nodeIdToSymbolId.has(call.calleeSymbolId)) {
578
+ return {
579
+ symbolId: nodeIdToSymbolId.get(call.calleeSymbolId) ?? null,
580
+ isResolved: true,
581
+ };
582
+ }
583
+ const candidateId = call.calleeSymbolId ?? call.calleeIdentifier;
584
+ if (!candidateId) {
585
+ return null;
586
+ }
587
+ const cleaned = candidateId.replace(/^new\s+/, "");
588
+ if (cleaned.includes(".")) {
589
+ const parts = cleaned.split(".");
590
+ const prefix = parts[0];
591
+ const member = parts[parts.length - 1];
592
+ const namespace = namespaceImports.get(prefix);
593
+ if (namespace && namespace.has(member)) {
594
+ return {
595
+ symbolId: namespace.get(member) ?? null,
596
+ isResolved: true,
597
+ };
598
+ }
599
+ }
600
+ const identifier = extractLastIdentifier(candidateId);
601
+ if (!identifier) {
602
+ return null;
603
+ }
604
+ // Check imported names first
605
+ const importedCandidates = importedNameToSymbolIds.get(identifier);
606
+ if (importedCandidates && importedCandidates.length === 1) {
607
+ return {
608
+ symbolId: importedCandidates[0],
609
+ isResolved: true,
610
+ };
611
+ }
612
+ // 36-1.3: Handle ambiguous imported names - create unresolved edge
613
+ if (importedCandidates && importedCandidates.length > 1) {
614
+ return {
615
+ symbolId: null,
616
+ isResolved: false,
617
+ candidateCount: importedCandidates.length,
618
+ targetName: identifier,
619
+ };
620
+ }
621
+ // Check local symbols
622
+ const candidates = nameToSymbolIds.get(identifier);
623
+ if (candidates && candidates.length === 1) {
624
+ return {
625
+ symbolId: candidates[0],
626
+ isResolved: true,
627
+ };
628
+ }
629
+ // 36-1.3: Handle ambiguous local names - create unresolved edge
630
+ if (candidates && candidates.length > 1) {
631
+ return {
632
+ symbolId: null,
633
+ isResolved: false,
634
+ candidateCount: candidates.length,
635
+ targetName: identifier,
636
+ };
637
+ }
638
+ // No candidates at all - still create an unresolved edge if we have a name
639
+ // This helps with external calls that might be resolved later
640
+ if (identifier && call.callType !== "dynamic") {
641
+ return {
642
+ symbolId: null,
643
+ isResolved: false,
644
+ candidateCount: 0,
645
+ targetName: identifier,
646
+ };
647
+ }
648
+ return null;
649
+ }
650
+ function extractLastIdentifier(text) {
651
+ const cleaned = text.replace(/^new\s+/, "");
652
+ const parts = cleaned.split(".");
653
+ const last = parts[parts.length - 1]?.trim();
654
+ return last || null;
655
+ }
656
+ function addToSymbolIndex(index, filePath, symbolId, name, kind) {
657
+ const fileKey = normalizePath(filePath);
658
+ let fileEntry = index.get(fileKey);
659
+ if (!fileEntry) {
660
+ fileEntry = new Map();
661
+ index.set(fileKey, fileEntry);
662
+ }
663
+ let nameEntry = fileEntry.get(name);
664
+ if (!nameEntry) {
665
+ nameEntry = new Map();
666
+ fileEntry.set(name, nameEntry);
667
+ }
668
+ const kindEntry = nameEntry.get(kind) ?? [];
669
+ kindEntry.push(symbolId);
670
+ nameEntry.set(kind, kindEntry);
671
+ }
672
+ function resolveSymbolIdFromIndex(index, repoId, filePath, name, kind, callerLanguage) {
673
+ const fileEntry = index.get(normalizePath(filePath));
674
+ if (!fileEntry)
675
+ return null;
676
+ const nameEntry = fileEntry.get(name);
677
+ if (!nameEntry)
678
+ return null;
679
+ const candidates = nameEntry.get(kind);
680
+ if (!candidates || candidates.length !== 1)
681
+ return null;
682
+ // ML-D.1: Language-aware symbol resolution
683
+ // If callerLanguage is provided, check if the target file is the same language
684
+ if (callerLanguage) {
685
+ const targetFile = getFileByRepoPath(repoId, filePath);
686
+ if (targetFile && targetFile.language !== callerLanguage) {
687
+ // Cross-language call - return null to mark as unresolved
688
+ return null;
689
+ }
690
+ }
691
+ return candidates[0];
692
+ }
693
+ function resolvePendingCallEdges(pending, index, created, repoId) {
694
+ for (const edge of pending) {
695
+ const toSymbolId = resolveSymbolIdFromIndex(index, repoId, edge.toFile, edge.toName, edge.toKind, edge.callerLanguage);
696
+ if (!toSymbolId) {
697
+ continue;
698
+ }
699
+ const edgeKey = `${edge.fromSymbolId}->${toSymbolId}`;
700
+ if (created.has(edgeKey)) {
701
+ continue;
702
+ }
703
+ createEdgeTransaction({
704
+ repo_id: repoId,
705
+ from_symbol_id: edge.fromSymbolId,
706
+ to_symbol_id: toSymbolId,
707
+ type: "call",
708
+ weight: 1.0,
709
+ provenance: edge.provenance,
710
+ created_at: new Date().toISOString(),
711
+ });
712
+ created.add(edgeKey);
713
+ }
714
+ }
715
+ function cleanupUnresolvedEdges(repoId) {
716
+ const allEdges = getEdgesByRepo(repoId);
717
+ const unresolvedEdges = allEdges.filter((edge) => edge.to_symbol_id.startsWith("unresolved:"));
718
+ const database = getDb();
719
+ const deleteEdgeStmt = database.prepare("DELETE FROM edges WHERE from_symbol_id = ? AND to_symbol_id = ?");
720
+ // IE-K.3: Node.js built-ins to skip
721
+ const nodeBuiltins = new Set([
722
+ "assert",
723
+ "async_hooks",
724
+ "buffer",
725
+ "child_process",
726
+ "cluster",
727
+ "console",
728
+ "crypto",
729
+ "dgram",
730
+ "dns",
731
+ "domain",
732
+ "events",
733
+ "fs",
734
+ "http",
735
+ "http2",
736
+ "https",
737
+ "inspector",
738
+ "module",
739
+ "net",
740
+ "os",
741
+ "path",
742
+ "perf_hooks",
743
+ "process",
744
+ "punycode",
745
+ "querystring",
746
+ "readline",
747
+ "repl",
748
+ "stream",
749
+ "string_decoder",
750
+ "sys",
751
+ "timers",
752
+ "tls",
753
+ "trace_events",
754
+ "tty",
755
+ "url",
756
+ "util",
757
+ "v8",
758
+ "vm",
759
+ "worker_threads",
760
+ "zlib",
761
+ ]);
762
+ // IE-K.3: Check if unresolved edge points to external package
763
+ const isExternalPackage = (target, edgeType) => {
764
+ // Import edges: unresolved:package:name (e.g., unresolved:tree-sitter:Parser)
765
+ if (edgeType === "import") {
766
+ const parts = target.split(":");
767
+ if (parts.length >= 3) {
768
+ const packagePath = parts[1];
769
+ // Skip if not relative path (i.e., external package)
770
+ if (!packagePath.startsWith("./") &&
771
+ !packagePath.startsWith("../") &&
772
+ !packagePath.startsWith("/")) {
773
+ return true;
774
+ }
775
+ }
776
+ }
777
+ // Call edges: unresolved:call:name or unresolved:call:package:name
778
+ // Check if name matches known patterns (Node.js built-ins or external packages)
779
+ if (target.startsWith("unresolved:call:")) {
780
+ const namePart = target.slice("unresolved:call:".length);
781
+ // Skip if name is a Node.js builtin
782
+ if (nodeBuiltins.has(namePart)) {
783
+ return true;
784
+ }
785
+ // Skip if name contains package-like pattern (e.g., "tree-sitter:Parser")
786
+ if (namePart.includes(":") &&
787
+ !namePart.startsWith("./") &&
788
+ !namePart.startsWith("../")) {
789
+ return true;
790
+ }
791
+ }
792
+ return false;
793
+ };
794
+ // Cache repo symbols for call edge resolution
795
+ let repoSymbols = null;
796
+ const getRepoSymbolsCached = () => {
797
+ if (!repoSymbols) {
798
+ repoSymbols = getSymbolsByRepo(repoId);
799
+ }
800
+ return repoSymbols;
801
+ };
802
+ // Cache symbol-to-file mapping
803
+ const symbolToFile = new Map();
804
+ const getSymbolFile = (symbolId) => {
805
+ if (symbolToFile.has(symbolId)) {
806
+ return symbolToFile.get(symbolId) ?? null;
807
+ }
808
+ const symbol = getSymbol(symbolId);
809
+ if (!symbol) {
810
+ symbolToFile.set(symbolId, null);
811
+ return null;
812
+ }
813
+ const file = getFile(symbol.file_id);
814
+ symbolToFile.set(symbolId, file ?? null);
815
+ return file ?? null;
816
+ };
817
+ for (const edge of unresolvedEdges) {
818
+ const target = edge.to_symbol_id;
819
+ // IE-K.3: Skip external package edges (don't try to resolve, don't warn)
820
+ if (isExternalPackage(target, edge.type)) {
821
+ continue;
822
+ }
823
+ let matchingSymbolId;
824
+ // Format 1: unresolved:call:functionName - simple call edge
825
+ const callMatch = target.match(/^unresolved:call:(.+)$/);
826
+ if (callMatch) {
827
+ const targetName = callMatch[1];
828
+ const match = getRepoSymbolsCached().find((sym) => {
829
+ if (sym.name === targetName)
830
+ return true;
831
+ if (targetName.includes(":")) {
832
+ const parts = targetName.split(":");
833
+ return parts.some((part) => sym.name === part);
834
+ }
835
+ return false;
836
+ });
837
+ matchingSymbolId = match?.symbol_id;
838
+ }
839
+ // Format 2: unresolved:path/to/file.js:symbolName - import edge with file path
840
+ // Skip namespace imports (* as X) and star imports (*)
841
+ if (!callMatch && !target.includes(":*")) {
842
+ // Parse: unresolved:path:symbolName (last colon separates path from symbol)
843
+ const lastColon = target.lastIndexOf(":");
844
+ if (lastColon > 11) {
845
+ // "unresolved:".length = 11
846
+ const pathPart = target.slice(11, lastColon);
847
+ const symbolName = target.slice(lastColon + 1);
848
+ // Get the source file to resolve relative paths
849
+ const sourceFile = getSymbolFile(edge.from_symbol_id);
850
+ if (sourceFile &&
851
+ (pathPart.startsWith("./") || pathPart.startsWith("../"))) {
852
+ // Resolve relative path from source file's directory
853
+ const sourceDir = dirname(sourceFile.rel_path);
854
+ const joinedPath = join(sourceDir, pathPart);
855
+ const normalizedJoined = normalizePath(joinedPath);
856
+ // Try multiple path variants for better matching
857
+ const pathVariants = [
858
+ // Normalized path with original extension
859
+ normalizedJoined,
860
+ // .js -> .ts conversion
861
+ normalizedJoined.replace(/\.js$/, ".ts"),
862
+ // .jsx -> .tsx conversion
863
+ normalizedJoined.replace(/\.jsx$/, ".tsx"),
864
+ // Try with .ts extension if no extension
865
+ !normalizedJoined.match(/\.(js|ts|jsx|tsx)$/)
866
+ ? `${normalizedJoined}.ts`
867
+ : normalizedJoined,
868
+ // Try with .js extension if no extension
869
+ !normalizedJoined.match(/\.(js|ts|jsx|tsx)$/)
870
+ ? `${normalizedJoined}.js`
871
+ : normalizedJoined,
872
+ // Try index.ts (with and without trailing slash)
873
+ normalizedJoined.replace(/\.(js|ts|jsx|tsx)$/, "") + "/index.ts",
874
+ // Try index.js
875
+ normalizedJoined.replace(/\.(js|ts|jsx|tsx)$/, "") + "/index.js",
876
+ // Try removing any extension and keeping as directory
877
+ normalizedJoined.replace(/\.(js|ts|jsx|tsx)$/, ""),
878
+ ];
879
+ // Remove duplicates from variants
880
+ const uniqueVariants = [...new Set(pathVariants)];
881
+ for (const variant of uniqueVariants) {
882
+ const targetFile = getFileByRepoPath(repoId, variant);
883
+ if (targetFile) {
884
+ // Find exported symbol by name in that file
885
+ const fileSymbols = getSymbolsByFileLite(targetFile.file_id).filter((s) => s.exported === 1);
886
+ const match = fileSymbols.find((s) => s.name === symbolName);
887
+ if (match) {
888
+ matchingSymbolId = match.symbol_id;
889
+ break;
890
+ }
891
+ // Fallback: if single export and looking for default
892
+ if (!matchingSymbolId && fileSymbols.length === 1) {
893
+ matchingSymbolId = fileSymbols[0].symbol_id;
894
+ break;
895
+ }
896
+ }
897
+ // IE-K.2: Try case-insensitive matching on Windows
898
+ if (!matchingSymbolId && platform === "win32") {
899
+ const allFiles = getFilesByRepo(repoId);
900
+ const caseInsensitiveMatch = allFiles.find((f) => f.rel_path.toLowerCase() === variant.toLowerCase());
901
+ if (caseInsensitiveMatch) {
902
+ const fileSymbols = getSymbolsByFileLite(caseInsensitiveMatch.file_id).filter((s) => s.exported === 1);
903
+ const match = fileSymbols.find((s) => s.name === symbolName);
904
+ if (match) {
905
+ matchingSymbolId = match.symbol_id;
906
+ break;
907
+ }
908
+ // Fallback: if single export and looking for default
909
+ if (!matchingSymbolId && fileSymbols.length === 1) {
910
+ matchingSymbolId = fileSymbols[0].symbol_id;
911
+ break;
912
+ }
913
+ }
914
+ }
915
+ }
916
+ }
917
+ else if (!pathPart.startsWith("./") && !pathPart.startsWith("../")) {
918
+ // Non-relative import (node_modules, etc.) - skip
919
+ continue;
920
+ }
921
+ }
922
+ }
923
+ if (matchingSymbolId) {
924
+ deleteEdgeStmt.run(edge.from_symbol_id, edge.to_symbol_id);
925
+ createEdgeTransaction({
926
+ repo_id: edge.repo_id,
927
+ from_symbol_id: edge.from_symbol_id,
928
+ to_symbol_id: matchingSymbolId,
929
+ type: edge.type,
930
+ weight: edge.type === "import" ? 0.6 : 1.0,
931
+ provenance: edge.provenance,
932
+ created_at: new Date().toISOString(),
933
+ });
934
+ }
935
+ }
936
+ }
937
+ function findEnclosingSymbolByRange(range, symbols) {
938
+ let bestMatch = null;
939
+ for (const detail of symbols) {
940
+ const symRange = detail.extractedSymbol.range;
941
+ const nodeLine = range.startLine;
942
+ const nodeCol = range.startCol;
943
+ if (nodeLine < symRange.startLine || nodeLine > symRange.endLine) {
944
+ continue;
945
+ }
946
+ if (nodeLine === symRange.startLine && nodeCol < symRange.startCol) {
947
+ continue;
948
+ }
949
+ if (nodeLine === symRange.endLine && nodeCol > symRange.endCol) {
950
+ continue;
951
+ }
952
+ const size = symRange.endLine -
953
+ symRange.startLine +
954
+ (symRange.endCol - symRange.startCol);
955
+ if (!bestMatch || size < bestMatch.size) {
956
+ bestMatch = { nodeId: detail.extractedSymbol.nodeId, size };
957
+ }
958
+ }
959
+ return bestMatch?.nodeId ?? null;
960
+ }
961
+ export async function indexRepo(repoId, mode, onProgress) {
962
+ const startTime = Date.now();
963
+ const repoRow = getRepo(repoId);
964
+ if (!repoRow) {
965
+ throw new Error(`Repository ${repoId} not found`);
966
+ }
967
+ const config = JSON.parse(repoRow.config_json);
968
+ onProgress?.({ stage: "scanning", current: 0, total: 0 });
969
+ const files = await scanRepository(repoRow.root_path, config);
970
+ onProgress?.({ stage: "parsing", current: 0, total: files.length });
971
+ const appConfig = loadConfig();
972
+ const concurrency = Math.max(1, Math.min(appConfig.indexing?.concurrency ?? 4, files.length || 1));
973
+ const workerPool = new ParserWorkerPool(appConfig.indexing?.workerPoolSize ?? os.cpus().length - 1);
974
+ const existingFiles = getFilesByRepo(repoId);
975
+ const existingByPath = new Map(existingFiles.map((file) => [file.rel_path, file]));
976
+ const scannedPaths = new Set(files.map((file) => file.path));
977
+ let removedFiles = 0;
978
+ for (const file of existingFiles) {
979
+ if (!scannedPaths.has(file.rel_path)) {
980
+ deleteFileTransaction(file.file_id);
981
+ removedFiles++;
982
+ }
983
+ }
984
+ const symbolIndex = new Map();
985
+ const pendingCallEdges = [];
986
+ const createdCallEdges = new Set();
987
+ const tsResolver = createTsCallResolver(repoRow.root_path, files);
988
+ const allSymbolsByName = new Map();
989
+ const repoSymbols = getSymbolsByRepo(repoId);
990
+ for (const symbol of repoSymbols) {
991
+ const byName = allSymbolsByName.get(symbol.name) ?? [];
992
+ byName.push(symbol);
993
+ allSymbolsByName.set(symbol.name, byName);
994
+ }
995
+ let filesProcessed = 0;
996
+ let changedFiles = 0;
997
+ let totalSymbolsIndexed = 0;
998
+ let totalEdgesCreated = 0;
999
+ let configEdgesCreated = 0;
1000
+ const allConfigEdges = [];
1001
+ const changedFileIds = new Set();
1002
+ let nextIndex = 0;
1003
+ const updateProgress = (currentFile) => {
1004
+ onProgress?.({
1005
+ stage: "indexing",
1006
+ current: Math.min(filesProcessed, files.length),
1007
+ total: files.length,
1008
+ currentFile,
1009
+ });
1010
+ };
1011
+ const runWorker = async () => {
1012
+ // eslint-disable-next-line no-constant-condition
1013
+ while (true) {
1014
+ const index = nextIndex++;
1015
+ if (index >= files.length) {
1016
+ return;
1017
+ }
1018
+ const file = files[index];
1019
+ updateProgress(file.path);
1020
+ try {
1021
+ const result = await processFile({
1022
+ repoId,
1023
+ repoRoot: repoRow.root_path,
1024
+ fileMeta: file,
1025
+ languages: config.languages,
1026
+ mode,
1027
+ existingFile: existingByPath.get(file.path),
1028
+ symbolIndex,
1029
+ pendingCallEdges,
1030
+ createdCallEdges,
1031
+ tsResolver,
1032
+ config,
1033
+ allSymbolsByName,
1034
+ onProgress,
1035
+ workerPool,
1036
+ });
1037
+ filesProcessed++;
1038
+ if (result.changed) {
1039
+ changedFiles++;
1040
+ const fileRecord = getFileByRepoPath(repoId, file.path);
1041
+ if (fileRecord) {
1042
+ changedFileIds.add(fileRecord.file_id);
1043
+ }
1044
+ }
1045
+ totalSymbolsIndexed += result.symbolsIndexed;
1046
+ totalEdgesCreated += result.edgesCreated;
1047
+ allConfigEdges.push(...result.configEdges);
1048
+ }
1049
+ catch (error) {
1050
+ filesProcessed++;
1051
+ console.error(`Error processing file ${file.path}:`, error);
1052
+ }
1053
+ }
1054
+ };
1055
+ const workers = Array.from({ length: Math.min(concurrency, files.length || 1) }, () => runWorker());
1056
+ await Promise.all(workers);
1057
+ onProgress?.({
1058
+ stage: "finalizing",
1059
+ current: files.length,
1060
+ total: files.length,
1061
+ });
1062
+ changedFiles += removedFiles;
1063
+ resolvePendingCallEdges(pendingCallEdges, symbolIndex, createdCallEdges, repoId);
1064
+ cleanupUnresolvedEdges(repoId);
1065
+ const configWeight = appConfig.slice?.edgeWeights?.config !== undefined
1066
+ ? appConfig.slice.edgeWeights.config
1067
+ : 0.8;
1068
+ for (const edge of allConfigEdges) {
1069
+ createEdgeTransaction({
1070
+ repo_id: repoId,
1071
+ from_symbol_id: edge.fromSymbolId,
1072
+ to_symbol_id: edge.toSymbolId,
1073
+ type: "config",
1074
+ weight: edge.weight ?? configWeight,
1075
+ provenance: edge.provenance ?? "config",
1076
+ created_at: new Date().toISOString(),
1077
+ });
1078
+ configEdgesCreated++;
1079
+ }
1080
+ const versionId = `v${Date.now()}`;
1081
+ const version = {
1082
+ version_id: versionId,
1083
+ repo_id: repoId,
1084
+ created_at: new Date().toISOString(),
1085
+ reason: mode === "full" ? "Full index" : "Incremental index",
1086
+ prev_version_hash: null,
1087
+ version_hash: null,
1088
+ };
1089
+ const symbols = getSymbolsByRepoForSnapshot(repoId);
1090
+ const snapshots = symbols.map((symbol) => ({
1091
+ version_id: versionId,
1092
+ symbol_id: symbol.symbol_id,
1093
+ ast_fingerprint: symbol.ast_fingerprint,
1094
+ signature_json: symbol.signature_json,
1095
+ summary: symbol.summary,
1096
+ invariants_json: symbol.invariants_json,
1097
+ side_effects_json: symbol.side_effects_json,
1098
+ }));
1099
+ createSnapshotTransaction(version, snapshots);
1100
+ const durationMs = Date.now() - startTime;
1101
+ const changedFileIdsParam = mode === "incremental" && changedFileIds.size > 0
1102
+ ? changedFileIds
1103
+ : undefined;
1104
+ await updateMetricsForRepo(repoId, changedFileIdsParam);
1105
+ // Shutdown worker pool to release resources
1106
+ await workerPool.shutdown();
1107
+ return {
1108
+ versionId,
1109
+ filesProcessed,
1110
+ changedFiles,
1111
+ removedFiles,
1112
+ symbolsIndexed: totalSymbolsIndexed,
1113
+ edgesCreated: totalEdgesCreated + configEdgesCreated,
1114
+ durationMs,
1115
+ };
1116
+ }
1117
+ function loadChokidar() {
1118
+ try {
1119
+ return require("chokidar");
1120
+ }
1121
+ catch {
1122
+ return null;
1123
+ }
1124
+ }
1125
+ const watcherErrors = [];
1126
+ export function watchRepository(repoId) {
1127
+ const repoRow = getRepo(repoId);
1128
+ if (!repoRow) {
1129
+ throw new Error(`Repository ${repoId} not found`);
1130
+ }
1131
+ const config = JSON.parse(repoRow.config_json);
1132
+ const extensions = config.languages.map((lang) => `.${lang}`);
1133
+ const reindex = async (filePath) => {
1134
+ try {
1135
+ process.stderr.write(`[sdl-mcp] File change detected: ${filePath}\n`);
1136
+ await indexRepo(repoId, "incremental");
1137
+ }
1138
+ catch (error) {
1139
+ process.stderr.write(`[sdl-mcp] Failed incremental index for ${filePath}: ${error}\n`);
1140
+ }
1141
+ };
1142
+ const pending = new Map();
1143
+ const schedule = (filePath) => {
1144
+ const existing = pending.get(filePath);
1145
+ if (existing) {
1146
+ clearTimeout(existing);
1147
+ }
1148
+ pending.set(filePath, setTimeout(() => {
1149
+ pending.delete(filePath);
1150
+ void reindex(filePath);
1151
+ }, WATCH_DEBOUNCE_MS));
1152
+ };
1153
+ const chokidar = loadChokidar();
1154
+ if (chokidar) {
1155
+ const watcher = chokidar.watch(repoRow.root_path, {
1156
+ ignored: config.ignore,
1157
+ ignoreInitial: true,
1158
+ awaitWriteFinish: {
1159
+ stabilityThreshold: WATCH_STABILITY_THRESHOLD_MS,
1160
+ pollInterval: WATCH_POLL_INTERVAL_MS,
1161
+ },
1162
+ });
1163
+ watcher.on("error", (error) => {
1164
+ const errorMsg = `[sdl-mcp] File watcher error: ${error}`;
1165
+ process.stderr.write(`${errorMsg}
1166
+ `);
1167
+ watcherErrors.push(`${new Date().toISOString()} - ${errorMsg}`);
1168
+ if (watcherErrors.length > WATCHER_ERROR_MAX_COUNT) {
1169
+ watcherErrors.shift();
1170
+ }
1171
+ });
1172
+ const handler = (filePath) => {
1173
+ const relPath = normalizePath(relative(repoRow.root_path, filePath));
1174
+ if (shouldIgnorePath(relPath, config.ignore)) {
1175
+ return;
1176
+ }
1177
+ if (!matchesExtensions(relPath, extensions)) {
1178
+ return;
1179
+ }
1180
+ schedule(relPath);
1181
+ };
1182
+ watcher.on("add", handler);
1183
+ watcher.on("change", handler);
1184
+ watcher.on("unlink", handler);
1185
+ return {
1186
+ close: async () => {
1187
+ await watcher.close();
1188
+ },
1189
+ };
1190
+ }
1191
+ const watcher = watch(repoRow.root_path, { recursive: true }, (_eventType, filename) => {
1192
+ if (!filename)
1193
+ return;
1194
+ const relPath = normalizePath(filename.toString());
1195
+ if (shouldIgnorePath(relPath, config.ignore)) {
1196
+ return;
1197
+ }
1198
+ if (!matchesExtensions(relPath, extensions)) {
1199
+ return;
1200
+ }
1201
+ schedule(relPath);
1202
+ });
1203
+ return {
1204
+ close: async () => {
1205
+ watcher.close();
1206
+ },
1207
+ };
1208
+ }
1209
+ function matchesExtensions(path, extensions) {
1210
+ return extensions.some((ext) => path.endsWith(ext));
1211
+ }
1212
+ function shouldIgnorePath(path, ignorePatterns) {
1213
+ const normalized = normalizePath(path);
1214
+ for (const pattern of ignorePatterns) {
1215
+ const token = pattern
1216
+ .replace(/\*\*\//g, "")
1217
+ .replace(/\/\*\*/g, "")
1218
+ .replace(/\*/g, "")
1219
+ .replace(/\/+/g, "/")
1220
+ .trim();
1221
+ if (!token)
1222
+ continue;
1223
+ if (normalized.includes(token)) {
1224
+ return true;
1225
+ }
1226
+ }
1227
+ return false;
1228
+ }
1229
+ //# sourceMappingURL=indexer.js.map