@colbymchenry/codegraph 0.7.10 → 0.9.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 (362) hide show
  1. package/README.md +70 -54
  2. package/npm-shim.js +43 -0
  3. package/package.json +13 -51
  4. package/LICENSE +0 -21
  5. package/dist/bin/codegraph.d.ts +0 -21
  6. package/dist/bin/codegraph.d.ts.map +0 -1
  7. package/dist/bin/codegraph.js +0 -1232
  8. package/dist/bin/codegraph.js.map +0 -1
  9. package/dist/bin/node-version-check.d.ts +0 -20
  10. package/dist/bin/node-version-check.d.ts.map +0 -1
  11. package/dist/bin/node-version-check.js +0 -42
  12. package/dist/bin/node-version-check.js.map +0 -1
  13. package/dist/bin/uninstall.d.ts +0 -14
  14. package/dist/bin/uninstall.d.ts.map +0 -1
  15. package/dist/bin/uninstall.js +0 -36
  16. package/dist/bin/uninstall.js.map +0 -1
  17. package/dist/config.d.ts +0 -51
  18. package/dist/config.d.ts.map +0 -1
  19. package/dist/config.js +0 -321
  20. package/dist/config.js.map +0 -1
  21. package/dist/context/formatter.d.ts +0 -30
  22. package/dist/context/formatter.d.ts.map +0 -1
  23. package/dist/context/formatter.js +0 -244
  24. package/dist/context/formatter.js.map +0 -1
  25. package/dist/context/index.d.ts +0 -97
  26. package/dist/context/index.d.ts.map +0 -1
  27. package/dist/context/index.js +0 -1048
  28. package/dist/context/index.js.map +0 -1
  29. package/dist/db/index.d.ts +0 -72
  30. package/dist/db/index.d.ts.map +0 -1
  31. package/dist/db/index.js +0 -200
  32. package/dist/db/index.js.map +0 -1
  33. package/dist/db/migrations.d.ts +0 -44
  34. package/dist/db/migrations.d.ts.map +0 -1
  35. package/dist/db/migrations.js +0 -131
  36. package/dist/db/migrations.js.map +0 -1
  37. package/dist/db/queries.d.ts +0 -253
  38. package/dist/db/queries.d.ts.map +0 -1
  39. package/dist/db/queries.js +0 -1207
  40. package/dist/db/queries.js.map +0 -1
  41. package/dist/db/schema.sql +0 -151
  42. package/dist/db/sqlite-adapter.d.ts +0 -52
  43. package/dist/db/sqlite-adapter.d.ts.map +0 -1
  44. package/dist/db/sqlite-adapter.js +0 -237
  45. package/dist/db/sqlite-adapter.js.map +0 -1
  46. package/dist/directory.d.ts +0 -57
  47. package/dist/directory.d.ts.map +0 -1
  48. package/dist/directory.js +0 -264
  49. package/dist/directory.js.map +0 -1
  50. package/dist/errors.d.ts +0 -136
  51. package/dist/errors.d.ts.map +0 -1
  52. package/dist/errors.js +0 -219
  53. package/dist/errors.js.map +0 -1
  54. package/dist/extraction/dfm-extractor.d.ts +0 -31
  55. package/dist/extraction/dfm-extractor.d.ts.map +0 -1
  56. package/dist/extraction/dfm-extractor.js +0 -151
  57. package/dist/extraction/dfm-extractor.js.map +0 -1
  58. package/dist/extraction/grammars.d.ts +0 -78
  59. package/dist/extraction/grammars.d.ts.map +0 -1
  60. package/dist/extraction/grammars.js +0 -322
  61. package/dist/extraction/grammars.js.map +0 -1
  62. package/dist/extraction/index.d.ts +0 -130
  63. package/dist/extraction/index.d.ts.map +0 -1
  64. package/dist/extraction/index.js +0 -1279
  65. package/dist/extraction/index.js.map +0 -1
  66. package/dist/extraction/languages/c-cpp.d.ts +0 -4
  67. package/dist/extraction/languages/c-cpp.d.ts.map +0 -1
  68. package/dist/extraction/languages/c-cpp.js +0 -126
  69. package/dist/extraction/languages/c-cpp.js.map +0 -1
  70. package/dist/extraction/languages/csharp.d.ts +0 -3
  71. package/dist/extraction/languages/csharp.d.ts.map +0 -1
  72. package/dist/extraction/languages/csharp.js +0 -72
  73. package/dist/extraction/languages/csharp.js.map +0 -1
  74. package/dist/extraction/languages/dart.d.ts +0 -3
  75. package/dist/extraction/languages/dart.d.ts.map +0 -1
  76. package/dist/extraction/languages/dart.js +0 -192
  77. package/dist/extraction/languages/dart.js.map +0 -1
  78. package/dist/extraction/languages/go.d.ts +0 -3
  79. package/dist/extraction/languages/go.d.ts.map +0 -1
  80. package/dist/extraction/languages/go.js +0 -58
  81. package/dist/extraction/languages/go.js.map +0 -1
  82. package/dist/extraction/languages/index.d.ts +0 -10
  83. package/dist/extraction/languages/index.d.ts.map +0 -1
  84. package/dist/extraction/languages/index.js +0 -45
  85. package/dist/extraction/languages/index.js.map +0 -1
  86. package/dist/extraction/languages/java.d.ts +0 -3
  87. package/dist/extraction/languages/java.d.ts.map +0 -1
  88. package/dist/extraction/languages/java.js +0 -64
  89. package/dist/extraction/languages/java.js.map +0 -1
  90. package/dist/extraction/languages/javascript.d.ts +0 -3
  91. package/dist/extraction/languages/javascript.d.ts.map +0 -1
  92. package/dist/extraction/languages/javascript.js +0 -90
  93. package/dist/extraction/languages/javascript.js.map +0 -1
  94. package/dist/extraction/languages/kotlin.d.ts +0 -3
  95. package/dist/extraction/languages/kotlin.d.ts.map +0 -1
  96. package/dist/extraction/languages/kotlin.js +0 -253
  97. package/dist/extraction/languages/kotlin.js.map +0 -1
  98. package/dist/extraction/languages/pascal.d.ts +0 -3
  99. package/dist/extraction/languages/pascal.d.ts.map +0 -1
  100. package/dist/extraction/languages/pascal.js +0 -66
  101. package/dist/extraction/languages/pascal.js.map +0 -1
  102. package/dist/extraction/languages/php.d.ts +0 -3
  103. package/dist/extraction/languages/php.d.ts.map +0 -1
  104. package/dist/extraction/languages/php.js +0 -107
  105. package/dist/extraction/languages/php.js.map +0 -1
  106. package/dist/extraction/languages/python.d.ts +0 -3
  107. package/dist/extraction/languages/python.d.ts.map +0 -1
  108. package/dist/extraction/languages/python.js +0 -56
  109. package/dist/extraction/languages/python.js.map +0 -1
  110. package/dist/extraction/languages/ruby.d.ts +0 -3
  111. package/dist/extraction/languages/ruby.d.ts.map +0 -1
  112. package/dist/extraction/languages/ruby.js +0 -114
  113. package/dist/extraction/languages/ruby.js.map +0 -1
  114. package/dist/extraction/languages/rust.d.ts +0 -3
  115. package/dist/extraction/languages/rust.d.ts.map +0 -1
  116. package/dist/extraction/languages/rust.js +0 -109
  117. package/dist/extraction/languages/rust.js.map +0 -1
  118. package/dist/extraction/languages/scala.d.ts +0 -3
  119. package/dist/extraction/languages/scala.d.ts.map +0 -1
  120. package/dist/extraction/languages/scala.js +0 -139
  121. package/dist/extraction/languages/scala.js.map +0 -1
  122. package/dist/extraction/languages/swift.d.ts +0 -3
  123. package/dist/extraction/languages/swift.d.ts.map +0 -1
  124. package/dist/extraction/languages/swift.js +0 -91
  125. package/dist/extraction/languages/swift.js.map +0 -1
  126. package/dist/extraction/languages/typescript.d.ts +0 -3
  127. package/dist/extraction/languages/typescript.d.ts.map +0 -1
  128. package/dist/extraction/languages/typescript.js +0 -129
  129. package/dist/extraction/languages/typescript.js.map +0 -1
  130. package/dist/extraction/liquid-extractor.d.ts +0 -52
  131. package/dist/extraction/liquid-extractor.d.ts.map +0 -1
  132. package/dist/extraction/liquid-extractor.js +0 -313
  133. package/dist/extraction/liquid-extractor.js.map +0 -1
  134. package/dist/extraction/parse-worker.d.ts +0 -8
  135. package/dist/extraction/parse-worker.d.ts.map +0 -1
  136. package/dist/extraction/parse-worker.js +0 -94
  137. package/dist/extraction/parse-worker.js.map +0 -1
  138. package/dist/extraction/svelte-extractor.d.ts +0 -56
  139. package/dist/extraction/svelte-extractor.d.ts.map +0 -1
  140. package/dist/extraction/svelte-extractor.js +0 -272
  141. package/dist/extraction/svelte-extractor.js.map +0 -1
  142. package/dist/extraction/tree-sitter-helpers.d.ts +0 -28
  143. package/dist/extraction/tree-sitter-helpers.d.ts.map +0 -1
  144. package/dist/extraction/tree-sitter-helpers.js +0 -103
  145. package/dist/extraction/tree-sitter-helpers.js.map +0 -1
  146. package/dist/extraction/tree-sitter-types.d.ts +0 -179
  147. package/dist/extraction/tree-sitter-types.d.ts.map +0 -1
  148. package/dist/extraction/tree-sitter-types.js +0 -10
  149. package/dist/extraction/tree-sitter-types.js.map +0 -1
  150. package/dist/extraction/tree-sitter.d.ts +0 -233
  151. package/dist/extraction/tree-sitter.d.ts.map +0 -1
  152. package/dist/extraction/tree-sitter.js +0 -2393
  153. package/dist/extraction/tree-sitter.js.map +0 -1
  154. package/dist/extraction/vue-extractor.d.ts +0 -36
  155. package/dist/extraction/vue-extractor.d.ts.map +0 -1
  156. package/dist/extraction/vue-extractor.js +0 -163
  157. package/dist/extraction/vue-extractor.js.map +0 -1
  158. package/dist/extraction/wasm/tree-sitter-pascal.wasm +0 -0
  159. package/dist/extraction/wasm/tree-sitter-scala.wasm +0 -0
  160. package/dist/graph/index.d.ts +0 -8
  161. package/dist/graph/index.d.ts.map +0 -1
  162. package/dist/graph/index.js +0 -13
  163. package/dist/graph/index.js.map +0 -1
  164. package/dist/graph/queries.d.ts +0 -106
  165. package/dist/graph/queries.d.ts.map +0 -1
  166. package/dist/graph/queries.js +0 -366
  167. package/dist/graph/queries.js.map +0 -1
  168. package/dist/graph/traversal.d.ts +0 -127
  169. package/dist/graph/traversal.d.ts.map +0 -1
  170. package/dist/graph/traversal.js +0 -493
  171. package/dist/graph/traversal.js.map +0 -1
  172. package/dist/index.d.ts +0 -447
  173. package/dist/index.d.ts.map +0 -1
  174. package/dist/index.js +0 -825
  175. package/dist/index.js.map +0 -1
  176. package/dist/installer/claude-md-template.d.ts +0 -14
  177. package/dist/installer/claude-md-template.d.ts.map +0 -1
  178. package/dist/installer/claude-md-template.js +0 -21
  179. package/dist/installer/claude-md-template.js.map +0 -1
  180. package/dist/installer/config-writer.d.ts +0 -29
  181. package/dist/installer/config-writer.d.ts.map +0 -1
  182. package/dist/installer/config-writer.js +0 -109
  183. package/dist/installer/config-writer.js.map +0 -1
  184. package/dist/installer/index.d.ts +0 -53
  185. package/dist/installer/index.d.ts.map +0 -1
  186. package/dist/installer/index.js +0 -338
  187. package/dist/installer/index.js.map +0 -1
  188. package/dist/installer/instructions-template.d.ts +0 -28
  189. package/dist/installer/instructions-template.d.ts.map +0 -1
  190. package/dist/installer/instructions-template.js +0 -63
  191. package/dist/installer/instructions-template.js.map +0 -1
  192. package/dist/installer/targets/claude.d.ts +0 -27
  193. package/dist/installer/targets/claude.d.ts.map +0 -1
  194. package/dist/installer/targets/claude.js +0 -246
  195. package/dist/installer/targets/claude.js.map +0 -1
  196. package/dist/installer/targets/codex.d.ts +0 -18
  197. package/dist/installer/targets/codex.d.ts.map +0 -1
  198. package/dist/installer/targets/codex.js +0 -185
  199. package/dist/installer/targets/codex.js.map +0 -1
  200. package/dist/installer/targets/cursor.d.ts +0 -35
  201. package/dist/installer/targets/cursor.d.ts.map +0 -1
  202. package/dist/installer/targets/cursor.js +0 -229
  203. package/dist/installer/targets/cursor.js.map +0 -1
  204. package/dist/installer/targets/opencode.d.ts +0 -30
  205. package/dist/installer/targets/opencode.d.ts.map +0 -1
  206. package/dist/installer/targets/opencode.js +0 -235
  207. package/dist/installer/targets/opencode.js.map +0 -1
  208. package/dist/installer/targets/registry.d.ts +0 -35
  209. package/dist/installer/targets/registry.d.ts.map +0 -1
  210. package/dist/installer/targets/registry.js +0 -83
  211. package/dist/installer/targets/registry.js.map +0 -1
  212. package/dist/installer/targets/shared.d.ts +0 -77
  213. package/dist/installer/targets/shared.d.ts.map +0 -1
  214. package/dist/installer/targets/shared.js +0 -246
  215. package/dist/installer/targets/shared.js.map +0 -1
  216. package/dist/installer/targets/toml.d.ts +0 -52
  217. package/dist/installer/targets/toml.d.ts.map +0 -1
  218. package/dist/installer/targets/toml.js +0 -147
  219. package/dist/installer/targets/toml.js.map +0 -1
  220. package/dist/installer/targets/types.d.ts +0 -116
  221. package/dist/installer/targets/types.d.ts.map +0 -1
  222. package/dist/installer/targets/types.js +0 -16
  223. package/dist/installer/targets/types.js.map +0 -1
  224. package/dist/mcp/index.d.ts +0 -86
  225. package/dist/mcp/index.d.ts.map +0 -1
  226. package/dist/mcp/index.js +0 -355
  227. package/dist/mcp/index.js.map +0 -1
  228. package/dist/mcp/server-instructions.d.ts +0 -19
  229. package/dist/mcp/server-instructions.d.ts.map +0 -1
  230. package/dist/mcp/server-instructions.js +0 -59
  231. package/dist/mcp/server-instructions.js.map +0 -1
  232. package/dist/mcp/tools.d.ts +0 -200
  233. package/dist/mcp/tools.d.ts.map +0 -1
  234. package/dist/mcp/tools.js +0 -1319
  235. package/dist/mcp/tools.js.map +0 -1
  236. package/dist/mcp/transport.d.ts +0 -89
  237. package/dist/mcp/transport.d.ts.map +0 -1
  238. package/dist/mcp/transport.js +0 -170
  239. package/dist/mcp/transport.js.map +0 -1
  240. package/dist/resolution/frameworks/cargo-workspace.d.ts +0 -18
  241. package/dist/resolution/frameworks/cargo-workspace.d.ts.map +0 -1
  242. package/dist/resolution/frameworks/cargo-workspace.js +0 -225
  243. package/dist/resolution/frameworks/cargo-workspace.js.map +0 -1
  244. package/dist/resolution/frameworks/csharp.d.ts +0 -8
  245. package/dist/resolution/frameworks/csharp.d.ts.map +0 -1
  246. package/dist/resolution/frameworks/csharp.js +0 -213
  247. package/dist/resolution/frameworks/csharp.js.map +0 -1
  248. package/dist/resolution/frameworks/express.d.ts +0 -8
  249. package/dist/resolution/frameworks/express.d.ts.map +0 -1
  250. package/dist/resolution/frameworks/express.js +0 -225
  251. package/dist/resolution/frameworks/express.js.map +0 -1
  252. package/dist/resolution/frameworks/go.d.ts +0 -8
  253. package/dist/resolution/frameworks/go.d.ts.map +0 -1
  254. package/dist/resolution/frameworks/go.js +0 -158
  255. package/dist/resolution/frameworks/go.js.map +0 -1
  256. package/dist/resolution/frameworks/index.d.ts +0 -41
  257. package/dist/resolution/frameworks/index.d.ts.map +0 -1
  258. package/dist/resolution/frameworks/index.js +0 -129
  259. package/dist/resolution/frameworks/index.js.map +0 -1
  260. package/dist/resolution/frameworks/java.d.ts +0 -8
  261. package/dist/resolution/frameworks/java.d.ts.map +0 -1
  262. package/dist/resolution/frameworks/java.js +0 -177
  263. package/dist/resolution/frameworks/java.js.map +0 -1
  264. package/dist/resolution/frameworks/laravel.d.ts +0 -13
  265. package/dist/resolution/frameworks/laravel.d.ts.map +0 -1
  266. package/dist/resolution/frameworks/laravel.js +0 -248
  267. package/dist/resolution/frameworks/laravel.js.map +0 -1
  268. package/dist/resolution/frameworks/python.d.ts +0 -10
  269. package/dist/resolution/frameworks/python.d.ts.map +0 -1
  270. package/dist/resolution/frameworks/python.js +0 -278
  271. package/dist/resolution/frameworks/python.js.map +0 -1
  272. package/dist/resolution/frameworks/react.d.ts +0 -8
  273. package/dist/resolution/frameworks/react.d.ts.map +0 -1
  274. package/dist/resolution/frameworks/react.js +0 -272
  275. package/dist/resolution/frameworks/react.js.map +0 -1
  276. package/dist/resolution/frameworks/ruby.d.ts +0 -8
  277. package/dist/resolution/frameworks/ruby.d.ts.map +0 -1
  278. package/dist/resolution/frameworks/ruby.js +0 -198
  279. package/dist/resolution/frameworks/ruby.js.map +0 -1
  280. package/dist/resolution/frameworks/rust.d.ts +0 -8
  281. package/dist/resolution/frameworks/rust.d.ts.map +0 -1
  282. package/dist/resolution/frameworks/rust.js +0 -207
  283. package/dist/resolution/frameworks/rust.js.map +0 -1
  284. package/dist/resolution/frameworks/svelte.d.ts +0 -9
  285. package/dist/resolution/frameworks/svelte.d.ts.map +0 -1
  286. package/dist/resolution/frameworks/svelte.js +0 -249
  287. package/dist/resolution/frameworks/svelte.js.map +0 -1
  288. package/dist/resolution/frameworks/swift.d.ts +0 -10
  289. package/dist/resolution/frameworks/swift.d.ts.map +0 -1
  290. package/dist/resolution/frameworks/swift.js +0 -376
  291. package/dist/resolution/frameworks/swift.js.map +0 -1
  292. package/dist/resolution/frameworks/vue.d.ts +0 -9
  293. package/dist/resolution/frameworks/vue.d.ts.map +0 -1
  294. package/dist/resolution/frameworks/vue.js +0 -306
  295. package/dist/resolution/frameworks/vue.js.map +0 -1
  296. package/dist/resolution/import-resolver.d.ts +0 -40
  297. package/dist/resolution/import-resolver.d.ts.map +0 -1
  298. package/dist/resolution/import-resolver.js +0 -663
  299. package/dist/resolution/import-resolver.js.map +0 -1
  300. package/dist/resolution/index.d.ts +0 -106
  301. package/dist/resolution/index.d.ts.map +0 -1
  302. package/dist/resolution/index.js +0 -709
  303. package/dist/resolution/index.js.map +0 -1
  304. package/dist/resolution/name-matcher.d.ts +0 -32
  305. package/dist/resolution/name-matcher.d.ts.map +0 -1
  306. package/dist/resolution/name-matcher.js +0 -384
  307. package/dist/resolution/name-matcher.js.map +0 -1
  308. package/dist/resolution/path-aliases.d.ts +0 -68
  309. package/dist/resolution/path-aliases.d.ts.map +0 -1
  310. package/dist/resolution/path-aliases.js +0 -238
  311. package/dist/resolution/path-aliases.js.map +0 -1
  312. package/dist/resolution/strip-comments.d.ts +0 -27
  313. package/dist/resolution/strip-comments.d.ts.map +0 -1
  314. package/dist/resolution/strip-comments.js +0 -441
  315. package/dist/resolution/strip-comments.js.map +0 -1
  316. package/dist/resolution/types.d.ts +0 -172
  317. package/dist/resolution/types.d.ts.map +0 -1
  318. package/dist/resolution/types.js +0 -8
  319. package/dist/resolution/types.js.map +0 -1
  320. package/dist/search/query-parser.d.ts +0 -57
  321. package/dist/search/query-parser.d.ts.map +0 -1
  322. package/dist/search/query-parser.js +0 -177
  323. package/dist/search/query-parser.js.map +0 -1
  324. package/dist/search/query-utils.d.ts +0 -53
  325. package/dist/search/query-utils.d.ts.map +0 -1
  326. package/dist/search/query-utils.js +0 -347
  327. package/dist/search/query-utils.js.map +0 -1
  328. package/dist/sync/index.d.ts +0 -13
  329. package/dist/sync/index.d.ts.map +0 -1
  330. package/dist/sync/index.js +0 -17
  331. package/dist/sync/index.js.map +0 -1
  332. package/dist/sync/watcher.d.ts +0 -81
  333. package/dist/sync/watcher.d.ts.map +0 -1
  334. package/dist/sync/watcher.js +0 -184
  335. package/dist/sync/watcher.js.map +0 -1
  336. package/dist/types.d.ts +0 -423
  337. package/dist/types.d.ts.map +0 -1
  338. package/dist/types.js +0 -256
  339. package/dist/types.js.map +0 -1
  340. package/dist/ui/glyphs.d.ts +0 -42
  341. package/dist/ui/glyphs.d.ts.map +0 -1
  342. package/dist/ui/glyphs.js +0 -78
  343. package/dist/ui/glyphs.js.map +0 -1
  344. package/dist/ui/shimmer-progress.d.ts +0 -11
  345. package/dist/ui/shimmer-progress.d.ts.map +0 -1
  346. package/dist/ui/shimmer-progress.js +0 -90
  347. package/dist/ui/shimmer-progress.js.map +0 -1
  348. package/dist/ui/shimmer-worker.d.ts +0 -2
  349. package/dist/ui/shimmer-worker.d.ts.map +0 -1
  350. package/dist/ui/shimmer-worker.js +0 -118
  351. package/dist/ui/shimmer-worker.js.map +0 -1
  352. package/dist/ui/types.d.ts +0 -17
  353. package/dist/ui/types.d.ts.map +0 -1
  354. package/dist/ui/types.js +0 -3
  355. package/dist/ui/types.js.map +0 -1
  356. package/dist/utils.d.ts +0 -205
  357. package/dist/utils.d.ts.map +0 -1
  358. package/dist/utils.js +0 -549
  359. package/dist/utils.js.map +0 -1
  360. package/scripts/local-install.sh +0 -41
  361. package/scripts/patch-tree-sitter-dart.js +0 -112
  362. package/scripts/release.sh +0 -70
package/dist/mcp/tools.js DELETED
@@ -1,1319 +0,0 @@
1
- "use strict";
2
- /**
3
- * MCP Tool Definitions
4
- *
5
- * Defines the tools exposed by the CodeGraph MCP server.
6
- */
7
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
8
- if (k2 === undefined) k2 = k;
9
- var desc = Object.getOwnPropertyDescriptor(m, k);
10
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
11
- desc = { enumerable: true, get: function() { return m[k]; } };
12
- }
13
- Object.defineProperty(o, k2, desc);
14
- }) : (function(o, m, k, k2) {
15
- if (k2 === undefined) k2 = k;
16
- o[k2] = m[k];
17
- }));
18
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
19
- Object.defineProperty(o, "default", { enumerable: true, value: v });
20
- }) : function(o, v) {
21
- o["default"] = v;
22
- });
23
- var __importStar = (this && this.__importStar) || (function () {
24
- var ownKeys = function(o) {
25
- ownKeys = Object.getOwnPropertyNames || function (o) {
26
- var ar = [];
27
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
28
- return ar;
29
- };
30
- return ownKeys(o);
31
- };
32
- return function (mod) {
33
- if (mod && mod.__esModule) return mod;
34
- var result = {};
35
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
36
- __setModuleDefault(result, mod);
37
- return result;
38
- };
39
- })();
40
- Object.defineProperty(exports, "__esModule", { value: true });
41
- exports.ToolHandler = exports.tools = void 0;
42
- exports.getExploreBudget = getExploreBudget;
43
- const index_1 = __importStar(require("../index"));
44
- const crypto_1 = require("crypto");
45
- const fs_1 = require("fs");
46
- const utils_1 = require("../utils");
47
- const os_1 = require("os");
48
- const path_1 = require("path");
49
- const db_1 = require("../db");
50
- /** Maximum output length to prevent context bloat (characters) */
51
- const MAX_OUTPUT_LENGTH = 15000;
52
- /**
53
- * Rust path roots that have no file-system equivalent — `crate` is the
54
- * current crate, `super` is the parent module, `self` is the current
55
- * module. Used by `matchesSymbol` to strip these before file-path
56
- * matching so `crate::configurator::stage_apply::run` resolves the
57
- * same as `configurator::stage_apply::run`.
58
- */
59
- const RUST_PATH_PREFIXES = new Set(['crate', 'super', 'self']);
60
- /** Last `::` / `.` / `/`-separated segment of a qualified symbol. */
61
- function lastQualifierPart(symbol) {
62
- const parts = symbol.split(/::|[./]/).filter((p) => p.length > 0);
63
- return parts[parts.length - 1] ?? symbol;
64
- }
65
- /**
66
- * Calculate the recommended number of codegraph_explore calls based on project size.
67
- * Larger codebases need more exploration calls to cover their surface area,
68
- * but smaller ones should use fewer to avoid unnecessary overhead.
69
- */
70
- function getExploreBudget(fileCount) {
71
- if (fileCount < 500)
72
- return 1;
73
- if (fileCount < 5000)
74
- return 2;
75
- if (fileCount < 15000)
76
- return 3;
77
- if (fileCount < 25000)
78
- return 4;
79
- return 5;
80
- }
81
- /**
82
- * Mark a Claude session as having consulted MCP tools.
83
- * This enables Grep/Glob/Bash commands that would otherwise be blocked.
84
- */
85
- function markSessionConsulted(sessionId) {
86
- try {
87
- const hash = (0, crypto_1.createHash)('md5').update(sessionId).digest('hex').slice(0, 16);
88
- const markerPath = (0, path_1.join)((0, os_1.tmpdir)(), `codegraph-consulted-${hash}`);
89
- (0, fs_1.writeFileSync)(markerPath, new Date().toISOString(), 'utf8');
90
- }
91
- catch {
92
- // Silently fail - don't break MCP on marker write failure
93
- }
94
- }
95
- /**
96
- * Common projectPath property for cross-project queries
97
- */
98
- const projectPathProperty = {
99
- type: 'string',
100
- description: 'Path to a different project with .codegraph/ initialized. If omitted, uses current project. Use this to query other codebases.',
101
- };
102
- /**
103
- * All CodeGraph MCP tools
104
- *
105
- * Designed for minimal context usage - use codegraph_context as the primary tool,
106
- * and only use other tools for targeted follow-up queries.
107
- *
108
- * All tools support cross-project queries via the optional `projectPath` parameter.
109
- */
110
- exports.tools = [
111
- {
112
- name: 'codegraph_search',
113
- description: 'Quick symbol search by name. Returns locations only (no code). Use codegraph_context instead for comprehensive task context.',
114
- inputSchema: {
115
- type: 'object',
116
- properties: {
117
- query: {
118
- type: 'string',
119
- description: 'Symbol name or partial name (e.g., "auth", "signIn", "UserService")',
120
- },
121
- kind: {
122
- type: 'string',
123
- description: 'Filter by node kind',
124
- enum: ['function', 'method', 'class', 'interface', 'type', 'variable', 'route', 'component'],
125
- },
126
- limit: {
127
- type: 'number',
128
- description: 'Maximum results (default: 10)',
129
- default: 10,
130
- },
131
- projectPath: projectPathProperty,
132
- },
133
- required: ['query'],
134
- },
135
- },
136
- {
137
- name: 'codegraph_context',
138
- description: 'PRIMARY TOOL: Build comprehensive context for a task. Returns entry points, related symbols, and key code - often enough to understand the codebase without additional tool calls. NOTE: This provides CODE context, not product requirements. For new features, still clarify UX/behavior questions with the user before implementing.',
139
- inputSchema: {
140
- type: 'object',
141
- properties: {
142
- task: {
143
- type: 'string',
144
- description: 'Description of the task, bug, or feature to build context for',
145
- },
146
- maxNodes: {
147
- type: 'number',
148
- description: 'Maximum symbols to include (default: 20)',
149
- default: 20,
150
- },
151
- includeCode: {
152
- type: 'boolean',
153
- description: 'Include code snippets for key symbols (default: true)',
154
- default: true,
155
- },
156
- projectPath: projectPathProperty,
157
- },
158
- required: ['task'],
159
- },
160
- },
161
- {
162
- name: 'codegraph_callers',
163
- description: 'Find all functions/methods that call a specific symbol. Useful for understanding usage patterns and impact of changes.',
164
- inputSchema: {
165
- type: 'object',
166
- properties: {
167
- symbol: {
168
- type: 'string',
169
- description: 'Name of the function, method, or class to find callers for',
170
- },
171
- limit: {
172
- type: 'number',
173
- description: 'Maximum number of callers to return (default: 20)',
174
- default: 20,
175
- },
176
- projectPath: projectPathProperty,
177
- },
178
- required: ['symbol'],
179
- },
180
- },
181
- {
182
- name: 'codegraph_callees',
183
- description: 'Find all functions/methods that a specific symbol calls. Useful for understanding dependencies and code flow.',
184
- inputSchema: {
185
- type: 'object',
186
- properties: {
187
- symbol: {
188
- type: 'string',
189
- description: 'Name of the function, method, or class to find callees for',
190
- },
191
- limit: {
192
- type: 'number',
193
- description: 'Maximum number of callees to return (default: 20)',
194
- default: 20,
195
- },
196
- projectPath: projectPathProperty,
197
- },
198
- required: ['symbol'],
199
- },
200
- },
201
- {
202
- name: 'codegraph_impact',
203
- description: 'Analyze the impact radius of changing a symbol. Shows what code could be affected by modifications.',
204
- inputSchema: {
205
- type: 'object',
206
- properties: {
207
- symbol: {
208
- type: 'string',
209
- description: 'Name of the symbol to analyze impact for',
210
- },
211
- depth: {
212
- type: 'number',
213
- description: 'How many levels of dependencies to traverse (default: 2)',
214
- default: 2,
215
- },
216
- projectPath: projectPathProperty,
217
- },
218
- required: ['symbol'],
219
- },
220
- },
221
- {
222
- name: 'codegraph_node',
223
- description: 'Get detailed information about a specific code symbol. Use includeCode=true only when you need the full source code - otherwise just get location and signature to minimize context usage.',
224
- inputSchema: {
225
- type: 'object',
226
- properties: {
227
- symbol: {
228
- type: 'string',
229
- description: 'Name of the symbol to get details for',
230
- },
231
- includeCode: {
232
- type: 'boolean',
233
- description: 'Include full source code (default: false to minimize context)',
234
- default: false,
235
- },
236
- projectPath: projectPathProperty,
237
- },
238
- required: ['symbol'],
239
- },
240
- },
241
- {
242
- name: 'codegraph_explore',
243
- description: 'Deep exploration tool — returns comprehensive context for a topic in a SINGLE call. Groups all relevant source code by file (contiguous sections, not snippets), includes a relationship map, and uses deeper graph traversal. Designed to replace multiple codegraph_node + file Read calls. Use this instead of codegraph_context when you need thorough understanding. IMPORTANT: Use specific symbol names, file names, or short code terms in your query — NOT natural language sentences. Before calling this, use codegraph_search to discover relevant symbol names, then include those names in your query. Bad: "how are agent prompts loaded and passed to the CLI". Good: "readAgentsFromDirectory createClaudeSession chat-manager agents.ts".',
244
- inputSchema: {
245
- type: 'object',
246
- properties: {
247
- query: {
248
- type: 'string',
249
- description: 'Symbol names, file names, or short code terms to explore (e.g., "AuthService loginUser session-manager", "GraphTraverser BFS impact traversal.ts"). Use codegraph_search first to find relevant names.',
250
- },
251
- maxFiles: {
252
- type: 'number',
253
- description: 'Maximum number of files to include source code from (default: 12)',
254
- default: 12,
255
- },
256
- projectPath: projectPathProperty,
257
- },
258
- required: ['query'],
259
- },
260
- },
261
- {
262
- name: 'codegraph_status',
263
- description: 'Get the status of the CodeGraph index, including statistics about indexed files, nodes, and edges.',
264
- inputSchema: {
265
- type: 'object',
266
- properties: {
267
- projectPath: projectPathProperty,
268
- },
269
- },
270
- },
271
- {
272
- name: 'codegraph_files',
273
- description: 'REQUIRED for file/folder exploration. Get the project file structure from the CodeGraph index. Returns a tree view of all indexed files with metadata (language, symbol count). Much faster than Glob/filesystem scanning. Use this FIRST when exploring project structure, finding files, or understanding codebase organization.',
274
- inputSchema: {
275
- type: 'object',
276
- properties: {
277
- path: {
278
- type: 'string',
279
- description: 'Filter to files under this directory path (e.g., "src/components"). Returns all files if not specified.',
280
- },
281
- pattern: {
282
- type: 'string',
283
- description: 'Filter files matching this glob pattern (e.g., "*.tsx", "**/*.test.ts")',
284
- },
285
- format: {
286
- type: 'string',
287
- description: 'Output format: "tree" (hierarchical, default), "flat" (simple list), "grouped" (by language)',
288
- enum: ['tree', 'flat', 'grouped'],
289
- default: 'tree',
290
- },
291
- includeMetadata: {
292
- type: 'boolean',
293
- description: 'Include file metadata like language and symbol count (default: true)',
294
- default: true,
295
- },
296
- maxDepth: {
297
- type: 'number',
298
- description: 'Maximum directory depth to show (default: unlimited)',
299
- },
300
- projectPath: projectPathProperty,
301
- },
302
- },
303
- },
304
- ];
305
- /**
306
- * Tool handler that executes tools against a CodeGraph instance
307
- *
308
- * Supports cross-project queries via the projectPath parameter.
309
- * Other projects are opened on-demand and cached for performance.
310
- */
311
- class ToolHandler {
312
- cg;
313
- // Cache of opened CodeGraph instances for cross-project queries
314
- projectCache = new Map();
315
- constructor(cg) {
316
- this.cg = cg;
317
- }
318
- /**
319
- * Update the default CodeGraph instance (e.g. after lazy initialization)
320
- */
321
- setDefaultCodeGraph(cg) {
322
- this.cg = cg;
323
- }
324
- /**
325
- * Whether a default CodeGraph instance is available
326
- */
327
- hasDefaultCodeGraph() {
328
- return this.cg !== null;
329
- }
330
- /**
331
- * Get tool definitions with dynamic descriptions based on project size.
332
- * The codegraph_explore tool description includes a budget recommendation
333
- * scaled to the number of indexed files.
334
- */
335
- getTools() {
336
- if (!this.cg)
337
- return exports.tools;
338
- try {
339
- const stats = this.cg.getStats();
340
- const budget = getExploreBudget(stats.fileCount);
341
- return exports.tools.map(tool => {
342
- if (tool.name === 'codegraph_explore') {
343
- return {
344
- ...tool,
345
- description: `${tool.description} Budget: make at most ${budget} calls for this project (${stats.fileCount.toLocaleString()} files indexed).`,
346
- };
347
- }
348
- return tool;
349
- });
350
- }
351
- catch {
352
- return exports.tools;
353
- }
354
- }
355
- /**
356
- * Get CodeGraph instance for a project
357
- *
358
- * If projectPath is provided, opens that project's CodeGraph (cached).
359
- * Otherwise returns the default CodeGraph instance.
360
- *
361
- * Walks up parent directories to find the nearest .codegraph/ folder,
362
- * similar to how git finds .git/ directories.
363
- */
364
- getCodeGraph(projectPath) {
365
- if (!projectPath) {
366
- if (!this.cg) {
367
- throw new Error('CodeGraph not initialized for this project. Run \'codegraph init\' first.');
368
- }
369
- return this.cg;
370
- }
371
- // Check cache first (using original path as key)
372
- if (this.projectCache.has(projectPath)) {
373
- return this.projectCache.get(projectPath);
374
- }
375
- // Walk up parent directories to find nearest .codegraph/
376
- const resolvedRoot = (0, index_1.findNearestCodeGraphRoot)(projectPath);
377
- if (!resolvedRoot) {
378
- throw new Error(`CodeGraph not initialized in ${projectPath}. Run 'codegraph init' in that project first.`);
379
- }
380
- // Check if we already have this resolved root cached (different path, same project)
381
- if (this.projectCache.has(resolvedRoot)) {
382
- const cg = this.projectCache.get(resolvedRoot);
383
- // Cache under original path too for faster future lookups
384
- this.projectCache.set(projectPath, cg);
385
- return cg;
386
- }
387
- // Open and cache under both paths
388
- const cg = index_1.default.openSync(resolvedRoot);
389
- this.projectCache.set(resolvedRoot, cg);
390
- if (projectPath !== resolvedRoot) {
391
- this.projectCache.set(projectPath, cg);
392
- }
393
- return cg;
394
- }
395
- /**
396
- * Close all cached project connections
397
- */
398
- closeAll() {
399
- for (const cg of this.projectCache.values()) {
400
- cg.close();
401
- }
402
- this.projectCache.clear();
403
- }
404
- /**
405
- * Validate that a value is a non-empty string
406
- */
407
- validateString(value, name) {
408
- if (typeof value !== 'string' || value.length === 0) {
409
- return this.errorResult(`${name} must be a non-empty string`);
410
- }
411
- return value;
412
- }
413
- /**
414
- * Execute a tool by name
415
- */
416
- async execute(toolName, args) {
417
- try {
418
- switch (toolName) {
419
- case 'codegraph_search':
420
- return await this.handleSearch(args);
421
- case 'codegraph_context':
422
- return await this.handleContext(args);
423
- case 'codegraph_callers':
424
- return await this.handleCallers(args);
425
- case 'codegraph_callees':
426
- return await this.handleCallees(args);
427
- case 'codegraph_impact':
428
- return await this.handleImpact(args);
429
- case 'codegraph_explore':
430
- return await this.handleExplore(args);
431
- case 'codegraph_node':
432
- return await this.handleNode(args);
433
- case 'codegraph_status':
434
- return await this.handleStatus(args);
435
- case 'codegraph_files':
436
- return await this.handleFiles(args);
437
- default:
438
- return this.errorResult(`Unknown tool: ${toolName}`);
439
- }
440
- }
441
- catch (err) {
442
- return this.errorResult(`Tool execution failed: ${err instanceof Error ? err.message : String(err)}`);
443
- }
444
- }
445
- /**
446
- * Handle codegraph_search
447
- */
448
- async handleSearch(args) {
449
- const query = this.validateString(args.query, 'query');
450
- if (typeof query !== 'string')
451
- return query;
452
- const cg = this.getCodeGraph(args.projectPath);
453
- const kind = args.kind;
454
- const rawLimit = Number(args.limit) || 10;
455
- const limit = (0, utils_1.clamp)(rawLimit, 1, 100);
456
- const results = cg.searchNodes(query, {
457
- limit,
458
- kinds: kind ? [kind] : undefined,
459
- });
460
- if (results.length === 0) {
461
- return this.textResult(`No results found for "${query}"`);
462
- }
463
- const formatted = this.formatSearchResults(results);
464
- return this.textResult(this.truncateOutput(formatted));
465
- }
466
- /**
467
- * Handle codegraph_context
468
- */
469
- async handleContext(args) {
470
- const task = this.validateString(args.task, 'task');
471
- if (typeof task !== 'string')
472
- return task;
473
- // Mark session as consulted (enables Grep/Glob/Bash)
474
- const sessionId = process.env.CLAUDE_SESSION_ID;
475
- if (sessionId) {
476
- markSessionConsulted(sessionId);
477
- }
478
- const cg = this.getCodeGraph(args.projectPath);
479
- const maxNodes = args.maxNodes || 20;
480
- const includeCode = args.includeCode !== false;
481
- const context = await cg.buildContext(task, {
482
- maxNodes,
483
- includeCode,
484
- format: 'markdown',
485
- });
486
- // Detect if this looks like a feature request (vs bug fix or exploration)
487
- const isFeatureQuery = this.looksLikeFeatureRequest(task);
488
- const reminder = isFeatureQuery
489
- ? '\n\n⚠️ **Ask user:** UX preferences, edge cases, acceptance criteria'
490
- : '';
491
- // buildContext returns string when format is 'markdown'
492
- if (typeof context === 'string') {
493
- return this.textResult(context + reminder);
494
- }
495
- // If it returns TaskContext, format it
496
- return this.textResult(this.formatTaskContext(context) + reminder);
497
- }
498
- /**
499
- * Heuristic to detect if a query looks like a feature request
500
- */
501
- looksLikeFeatureRequest(task) {
502
- const featureKeywords = [
503
- 'add', 'create', 'implement', 'build', 'enable', 'allow',
504
- 'new feature', 'support for', 'ability to', 'want to',
505
- 'should be able', 'need to add', 'swap', 'edit', 'modify'
506
- ];
507
- const bugKeywords = [
508
- 'fix', 'bug', 'error', 'broken', 'crash', 'issue', 'problem',
509
- 'not working', 'fails', 'undefined', 'null'
510
- ];
511
- const explorationKeywords = [
512
- 'how does', 'where is', 'what is', 'find', 'show me',
513
- 'explain', 'understand', 'explore'
514
- ];
515
- const lowerTask = task.toLowerCase();
516
- // If it's clearly a bug or exploration, not a feature
517
- if (bugKeywords.some(k => lowerTask.includes(k)))
518
- return false;
519
- if (explorationKeywords.some(k => lowerTask.includes(k)))
520
- return false;
521
- // If it matches feature keywords, it's likely a feature request
522
- return featureKeywords.some(k => lowerTask.includes(k));
523
- }
524
- /**
525
- * Handle codegraph_callers
526
- */
527
- async handleCallers(args) {
528
- const symbol = this.validateString(args.symbol, 'symbol');
529
- if (typeof symbol !== 'string')
530
- return symbol;
531
- const cg = this.getCodeGraph(args.projectPath);
532
- const limit = (0, utils_1.clamp)(args.limit || 20, 1, 100);
533
- const allMatches = this.findAllSymbols(cg, symbol);
534
- if (allMatches.nodes.length === 0) {
535
- return this.textResult(`Symbol "${symbol}" not found in the codebase`);
536
- }
537
- // Aggregate callers across all matching symbols
538
- const seen = new Set();
539
- const allCallers = [];
540
- for (const node of allMatches.nodes) {
541
- for (const c of cg.getCallers(node.id)) {
542
- if (!seen.has(c.node.id)) {
543
- seen.add(c.node.id);
544
- allCallers.push(c.node);
545
- }
546
- }
547
- }
548
- if (allCallers.length === 0) {
549
- return this.textResult(`No callers found for "${symbol}"${allMatches.note}`);
550
- }
551
- const formatted = this.formatNodeList(allCallers.slice(0, limit), `Callers of ${symbol}`) + allMatches.note;
552
- return this.textResult(this.truncateOutput(formatted));
553
- }
554
- /**
555
- * Handle codegraph_callees
556
- */
557
- async handleCallees(args) {
558
- const symbol = this.validateString(args.symbol, 'symbol');
559
- if (typeof symbol !== 'string')
560
- return symbol;
561
- const cg = this.getCodeGraph(args.projectPath);
562
- const limit = (0, utils_1.clamp)(args.limit || 20, 1, 100);
563
- const allMatches = this.findAllSymbols(cg, symbol);
564
- if (allMatches.nodes.length === 0) {
565
- return this.textResult(`Symbol "${symbol}" not found in the codebase`);
566
- }
567
- // Aggregate callees across all matching symbols
568
- const seen = new Set();
569
- const allCallees = [];
570
- for (const node of allMatches.nodes) {
571
- for (const c of cg.getCallees(node.id)) {
572
- if (!seen.has(c.node.id)) {
573
- seen.add(c.node.id);
574
- allCallees.push(c.node);
575
- }
576
- }
577
- }
578
- if (allCallees.length === 0) {
579
- return this.textResult(`No callees found for "${symbol}"${allMatches.note}`);
580
- }
581
- const formatted = this.formatNodeList(allCallees.slice(0, limit), `Callees of ${symbol}`) + allMatches.note;
582
- return this.textResult(this.truncateOutput(formatted));
583
- }
584
- /**
585
- * Handle codegraph_impact
586
- */
587
- async handleImpact(args) {
588
- const symbol = this.validateString(args.symbol, 'symbol');
589
- if (typeof symbol !== 'string')
590
- return symbol;
591
- const cg = this.getCodeGraph(args.projectPath);
592
- const depth = (0, utils_1.clamp)(args.depth || 2, 1, 10);
593
- const allMatches = this.findAllSymbols(cg, symbol);
594
- if (allMatches.nodes.length === 0) {
595
- return this.textResult(`Symbol "${symbol}" not found in the codebase`);
596
- }
597
- // Aggregate impact across all matching symbols
598
- const mergedNodes = new Map();
599
- const mergedEdges = [];
600
- const seenEdges = new Set();
601
- for (const node of allMatches.nodes) {
602
- const impact = cg.getImpactRadius(node.id, depth);
603
- for (const [id, n] of impact.nodes) {
604
- mergedNodes.set(id, n);
605
- }
606
- for (const e of impact.edges) {
607
- const key = `${e.source}->${e.target}:${e.kind}`;
608
- if (!seenEdges.has(key)) {
609
- seenEdges.add(key);
610
- mergedEdges.push(e);
611
- }
612
- }
613
- }
614
- const mergedImpact = {
615
- nodes: mergedNodes,
616
- edges: mergedEdges,
617
- roots: allMatches.nodes.map(n => n.id),
618
- };
619
- const formatted = this.formatImpact(symbol, mergedImpact) + allMatches.note;
620
- return this.textResult(this.truncateOutput(formatted));
621
- }
622
- /** Maximum output for explore tool — sized to stay under MCP client token limits (~10k tokens) */
623
- static EXPLORE_MAX_OUTPUT = 35000;
624
- /**
625
- * Handle codegraph_explore — deep exploration in a single call
626
- *
627
- * Strategy: find relevant symbols via graph traversal, group by file,
628
- * then read contiguous file sections covering all symbols per file.
629
- * This replaces multiple codegraph_node + Read calls.
630
- */
631
- async handleExplore(args) {
632
- const query = this.validateString(args.query, 'query');
633
- if (typeof query !== 'string')
634
- return query;
635
- const cg = this.getCodeGraph(args.projectPath);
636
- const maxFiles = (0, utils_1.clamp)(args.maxFiles || 12, 1, 20);
637
- const projectRoot = cg.getProjectRoot();
638
- // Step 1: Find relevant context with generous parameters.
639
- // Use a large maxNodes budget — explore has its own 35k char output limit
640
- // that prevents context bloat, so more nodes just means better coverage
641
- // across entry points (especially for large files like Svelte components).
642
- const subgraph = await cg.findRelevantContext(query, {
643
- searchLimit: 8,
644
- traversalDepth: 3,
645
- maxNodes: 200,
646
- minScore: 0.2,
647
- });
648
- if (subgraph.nodes.size === 0) {
649
- return this.textResult(`No relevant code found for "${query}"`);
650
- }
651
- // Step 2: Group nodes by file, score by relevance
652
- const fileGroups = new Map();
653
- const entryNodeIds = new Set(subgraph.roots);
654
- // Build a set of nodes directly connected to entry points (depth 1)
655
- const connectedToEntry = new Set();
656
- for (const edge of subgraph.edges) {
657
- if (entryNodeIds.has(edge.source))
658
- connectedToEntry.add(edge.target);
659
- if (entryNodeIds.has(edge.target))
660
- connectedToEntry.add(edge.source);
661
- }
662
- for (const node of subgraph.nodes.values()) {
663
- // Skip import/export nodes — they add noise without information
664
- if (node.kind === 'import' || node.kind === 'export')
665
- continue;
666
- const group = fileGroups.get(node.filePath) || { nodes: [], score: 0 };
667
- group.nodes.push(node);
668
- // Score: entry point nodes worth 10, directly connected worth 3, others worth 1
669
- if (entryNodeIds.has(node.id)) {
670
- group.score += 10;
671
- }
672
- else if (connectedToEntry.has(node.id)) {
673
- group.score += 3;
674
- }
675
- else {
676
- group.score += 1;
677
- }
678
- fileGroups.set(node.filePath, group);
679
- }
680
- // Only include files that have entry points or nodes directly connected to entry points
681
- const relevantFiles = [...fileGroups.entries()].filter(([, group]) => group.score >= 3);
682
- // Extract query terms for relevance checking
683
- const queryTerms = query.toLowerCase().split(/\s+/).filter(t => t.length >= 3);
684
- // Sort files: highest relevance first, deprioritize low-value files
685
- const sortedFiles = relevantFiles.sort((a, b) => {
686
- const aPath = a[0].toLowerCase();
687
- const bPath = b[0].toLowerCase();
688
- // Check if any node name or file path relates to query terms
689
- const hasQueryRelevance = (filePath, nodes) => {
690
- const fp = filePath.toLowerCase();
691
- if (queryTerms.some(t => fp.includes(t)))
692
- return true;
693
- return nodes.some(n => queryTerms.some(t => n.name.toLowerCase().includes(t)));
694
- };
695
- const aRelevant = hasQueryRelevance(aPath, a[1].nodes);
696
- const bRelevant = hasQueryRelevance(bPath, b[1].nodes);
697
- if (aRelevant !== bRelevant)
698
- return aRelevant ? -1 : 1;
699
- // Deprioritize test files, icon files, and i18n files
700
- const isLowValue = (p) => /\/(tests?|__tests?__|spec)\//i.test(p) ||
701
- /\bicons?\b/i.test(p) ||
702
- /\bi18n\b/i.test(p);
703
- const aLow = isLowValue(aPath);
704
- const bLow = isLowValue(bPath);
705
- if (aLow !== bLow)
706
- return aLow ? 1 : -1;
707
- if (a[1].score !== b[1].score)
708
- return b[1].score - a[1].score;
709
- return b[1].nodes.length - a[1].nodes.length;
710
- });
711
- // Step 3: Build relationship map
712
- const lines = [
713
- `## Exploration: ${query}`,
714
- '',
715
- `Found ${subgraph.nodes.size} symbols across ${fileGroups.size} files.`,
716
- '',
717
- ];
718
- // Relationship map — show how symbols connect
719
- const significantEdges = subgraph.edges.filter(e => e.kind !== 'contains' // skip contains — it's implied by file grouping
720
- );
721
- if (significantEdges.length > 0) {
722
- lines.push('### Relationships');
723
- lines.push('');
724
- // Group edges by kind for readability
725
- const byKind = new Map();
726
- for (const edge of significantEdges) {
727
- const sourceNode = subgraph.nodes.get(edge.source);
728
- const targetNode = subgraph.nodes.get(edge.target);
729
- if (!sourceNode || !targetNode)
730
- continue;
731
- const group = byKind.get(edge.kind) || [];
732
- group.push({ source: sourceNode.name, target: targetNode.name });
733
- byKind.set(edge.kind, group);
734
- }
735
- for (const [kind, edges] of byKind) {
736
- // Show up to 15 relationships per kind
737
- const shown = edges.slice(0, 15);
738
- lines.push(`**${kind}:**`);
739
- for (const e of shown) {
740
- lines.push(`- ${e.source} → ${e.target}`);
741
- }
742
- if (edges.length > 15) {
743
- lines.push(`- ... and ${edges.length - 15} more`);
744
- }
745
- lines.push('');
746
- }
747
- }
748
- // Step 4: Read contiguous file sections
749
- lines.push('### Source Code');
750
- lines.push('');
751
- let totalChars = lines.join('\n').length;
752
- let filesIncluded = 0;
753
- for (const [filePath, group] of sortedFiles) {
754
- if (filesIncluded >= maxFiles)
755
- break;
756
- if (totalChars > ToolHandler.EXPLORE_MAX_OUTPUT * 0.9)
757
- break;
758
- const absPath = (0, utils_1.validatePathWithinRoot)(projectRoot, filePath);
759
- if (!absPath || !(0, fs_1.existsSync)(absPath))
760
- continue;
761
- let fileContent;
762
- try {
763
- fileContent = (0, fs_1.readFileSync)(absPath, 'utf-8');
764
- }
765
- catch {
766
- continue;
767
- }
768
- const fileLines = fileContent.split('\n');
769
- const lang = group.nodes[0]?.language || '';
770
- // Cluster nearby symbols to avoid reading huge gaps between distant symbols.
771
- // Sort by start line, then merge overlapping/adjacent ranges (within 15 lines).
772
- // Include both node ranges AND edge source locations so template sections
773
- // with component usages/calls are covered (not just script block symbols).
774
- const ranges = group.nodes
775
- .filter(n => n.startLine > 0 && n.endLine > 0)
776
- // Skip file/component nodes that span the entire file — they'd create one giant cluster
777
- .filter(n => !(n.kind === 'component' && n.startLine === 1 && n.endLine >= fileLines.length - 1))
778
- .map(n => ({ start: n.startLine, end: n.endLine, name: n.name, kind: n.kind }));
779
- // Add edge source locations in this file — captures template references
780
- // (component usages, event handlers) that aren't nodes themselves.
781
- // Query edges directly from the DB (not just the subgraph) because BFS
782
- // traversal may have pruned template reference targets due to node budget.
783
- const edgeLines = new Set(); // dedup by "line:name"
784
- for (const node of group.nodes) {
785
- const outgoing = cg.getOutgoingEdges(node.id);
786
- for (const edge of outgoing) {
787
- if (!edge.line || edge.line <= 0 || edge.kind === 'contains')
788
- continue;
789
- const key = `${edge.line}:${edge.target}`;
790
- if (edgeLines.has(key))
791
- continue;
792
- edgeLines.add(key);
793
- // Look up target name from subgraph first, fall back to edge kind
794
- const targetNode = subgraph.nodes.get(edge.target);
795
- const targetName = targetNode?.name ?? edge.kind;
796
- ranges.push({ start: edge.line, end: edge.line, name: targetName, kind: edge.kind });
797
- }
798
- }
799
- ranges.sort((a, b) => a.start - b.start);
800
- if (ranges.length === 0)
801
- continue;
802
- const GAP_THRESHOLD = 15; // merge sections within 15 lines of each other
803
- const clusters = [];
804
- let current = { start: ranges[0].start, end: ranges[0].end, symbols: [`${ranges[0].name}(${ranges[0].kind})`] };
805
- for (let i = 1; i < ranges.length; i++) {
806
- const r = ranges[i];
807
- if (r.start <= current.end + GAP_THRESHOLD) {
808
- current.end = Math.max(current.end, r.end);
809
- current.symbols.push(`${r.name}(${r.kind})`);
810
- }
811
- else {
812
- clusters.push(current);
813
- current = { start: r.start, end: r.end, symbols: [`${r.name}(${r.kind})`] };
814
- }
815
- }
816
- clusters.push(current);
817
- // Build file section output from clusters
818
- const contextPadding = 3;
819
- let fileSection = '';
820
- const allSymbols = [];
821
- for (const cluster of clusters) {
822
- const startIdx = Math.max(0, cluster.start - 1 - contextPadding);
823
- const endIdx = Math.min(fileLines.length, cluster.end + contextPadding);
824
- const section = fileLines.slice(startIdx, endIdx).join('\n');
825
- if (fileSection.length > 0) {
826
- fileSection += '\n\n// ... (gap) ...\n\n';
827
- }
828
- fileSection += section;
829
- allSymbols.push(...cluster.symbols);
830
- }
831
- // Skip if this section would blow the output limit
832
- if (totalChars + fileSection.length + 200 > ToolHandler.EXPLORE_MAX_OUTPUT) {
833
- const budget = ToolHandler.EXPLORE_MAX_OUTPUT - totalChars - 200;
834
- if (budget < 500)
835
- break;
836
- const trimmed = fileSection.slice(0, budget) + '\n// ... trimmed ...';
837
- lines.push(`#### ${filePath} — ${allSymbols.join(', ')}`);
838
- lines.push('');
839
- lines.push('```' + lang);
840
- lines.push(trimmed);
841
- lines.push('```');
842
- lines.push('');
843
- totalChars += trimmed.length + 200;
844
- filesIncluded++;
845
- break;
846
- }
847
- lines.push(`#### ${filePath} — ${allSymbols.join(', ')}`);
848
- lines.push('');
849
- lines.push('```' + lang);
850
- lines.push(fileSection);
851
- lines.push('```');
852
- lines.push('');
853
- totalChars += fileSection.length + 200;
854
- filesIncluded++;
855
- }
856
- // Add remaining files as references (from both relevant and peripheral files)
857
- const remainingRelevant = sortedFiles.slice(filesIncluded);
858
- const peripheralFiles = [...fileGroups.entries()]
859
- .filter(([, group]) => group.score < 3)
860
- .sort((a, b) => b[1].score - a[1].score);
861
- const remainingFiles = [...remainingRelevant, ...peripheralFiles];
862
- if (remainingFiles.length > 0) {
863
- lines.push('### Additional relevant files (not shown)');
864
- lines.push('');
865
- for (const [filePath, group] of remainingFiles.slice(0, 10)) {
866
- const symbols = group.nodes.map(n => `${n.name}:${n.startLine}`).join(', ');
867
- lines.push(`- ${filePath}: ${symbols}`);
868
- }
869
- if (remainingFiles.length > 10) {
870
- lines.push(`- ... and ${remainingFiles.length - 10} more files`);
871
- }
872
- }
873
- // Add completeness signal so agents know they don't need to re-read these files
874
- lines.push('');
875
- lines.push('---');
876
- lines.push(`> **Complete source code is included above for ${filesIncluded} files.** You do NOT need to re-read these files — the relevant sections are already shown in full. Only use Read/Grep for files listed under "Additional relevant files" if you need more detail.`);
877
- // Add explore budget note based on project size
878
- try {
879
- const stats = cg.getStats();
880
- const budget = getExploreBudget(stats.fileCount);
881
- lines.push('');
882
- lines.push(`> **Explore budget: ${budget} calls max for this project (${stats.fileCount.toLocaleString()} files indexed).** Stop exploring and synthesize your answer once you've used ${budget} calls — do NOT make additional explore calls beyond this budget.`);
883
- }
884
- catch {
885
- // Stats unavailable — skip budget note
886
- }
887
- return this.textResult(lines.join('\n'));
888
- }
889
- /**
890
- * Handle codegraph_node
891
- */
892
- async handleNode(args) {
893
- const symbol = this.validateString(args.symbol, 'symbol');
894
- if (typeof symbol !== 'string')
895
- return symbol;
896
- const cg = this.getCodeGraph(args.projectPath);
897
- // Default to false to minimize context usage
898
- const includeCode = args.includeCode === true;
899
- const match = this.findSymbol(cg, symbol);
900
- if (!match) {
901
- return this.textResult(`Symbol "${symbol}" not found in the codebase`);
902
- }
903
- let code = null;
904
- if (includeCode) {
905
- code = await cg.getCode(match.node.id);
906
- }
907
- const formatted = this.formatNodeDetails(match.node, code) + match.note;
908
- return this.textResult(this.truncateOutput(formatted));
909
- }
910
- /**
911
- * Handle codegraph_status
912
- */
913
- async handleStatus(args) {
914
- const cg = this.getCodeGraph(args.projectPath);
915
- const stats = cg.getStats();
916
- const lines = [
917
- '## CodeGraph Status',
918
- '',
919
- `**Files indexed:** ${stats.fileCount}`,
920
- `**Total nodes:** ${stats.nodeCount}`,
921
- `**Total edges:** ${stats.edgeCount}`,
922
- `**Database size:** ${(stats.dbSizeBytes / 1024 / 1024).toFixed(2)} MB`,
923
- ];
924
- // Surface the active SQLite backend. Without this, users on the
925
- // silent WASM fallback (better-sqlite3 install failed) see "slow"
926
- // indexing and DB-lock errors with no signal of why.
927
- const backend = cg.getBackend();
928
- if (backend === 'native') {
929
- lines.push(`**Backend:** native (better-sqlite3)`);
930
- }
931
- else {
932
- lines.push(`**Backend:** ⚠ wasm (better-sqlite3 unavailable) — ` +
933
- `5-10x slower than native. Fix: ${db_1.WASM_FALLBACK_FIX_RECIPE}`);
934
- }
935
- lines.push('', '### Nodes by Kind:');
936
- for (const [kind, count] of Object.entries(stats.nodesByKind)) {
937
- if (count > 0) {
938
- lines.push(`- ${kind}: ${count}`);
939
- }
940
- }
941
- lines.push('', '### Languages:');
942
- for (const [lang, count] of Object.entries(stats.filesByLanguage)) {
943
- if (count > 0) {
944
- lines.push(`- ${lang}: ${count}`);
945
- }
946
- }
947
- return this.textResult(lines.join('\n'));
948
- }
949
- /**
950
- * Handle codegraph_files - get project file structure from the index
951
- */
952
- async handleFiles(args) {
953
- const cg = this.getCodeGraph(args.projectPath);
954
- const pathFilter = args.path;
955
- const pattern = args.pattern;
956
- const format = args.format || 'tree';
957
- const includeMetadata = args.includeMetadata !== false;
958
- const maxDepth = args.maxDepth != null ? (0, utils_1.clamp)(args.maxDepth, 1, 20) : undefined;
959
- // Get all files from the index
960
- const allFiles = cg.getFiles();
961
- if (allFiles.length === 0) {
962
- return this.textResult('No files indexed. Run `codegraph index` first.');
963
- }
964
- // Filter by path prefix
965
- let files = pathFilter
966
- ? allFiles.filter(f => f.path.startsWith(pathFilter) || f.path.startsWith('./' + pathFilter))
967
- : allFiles;
968
- // Filter by glob pattern
969
- if (pattern) {
970
- const regex = this.globToRegex(pattern);
971
- files = files.filter(f => regex.test(f.path));
972
- }
973
- if (files.length === 0) {
974
- return this.textResult(`No files found matching the criteria.`);
975
- }
976
- // Format output
977
- let output;
978
- switch (format) {
979
- case 'flat':
980
- output = this.formatFilesFlat(files, includeMetadata);
981
- break;
982
- case 'grouped':
983
- output = this.formatFilesGrouped(files, includeMetadata);
984
- break;
985
- case 'tree':
986
- default:
987
- output = this.formatFilesTree(files, includeMetadata, maxDepth);
988
- break;
989
- }
990
- return this.textResult(this.truncateOutput(output));
991
- }
992
- /**
993
- * Convert glob pattern to regex
994
- */
995
- globToRegex(pattern) {
996
- const escaped = pattern
997
- .replace(/[.+^${}()|[\]\\]/g, '\\$&') // Escape special regex chars except * and ?
998
- .replace(/\*\*/g, '{{GLOBSTAR}}') // Temp placeholder for **
999
- .replace(/\*/g, '[^/]*') // * matches anything except /
1000
- .replace(/\?/g, '[^/]') // ? matches single char except /
1001
- .replace(/\{\{GLOBSTAR\}\}/g, '.*'); // ** matches anything including /
1002
- return new RegExp(escaped);
1003
- }
1004
- /**
1005
- * Format files as a flat list
1006
- */
1007
- formatFilesFlat(files, includeMetadata) {
1008
- const lines = [`## Files (${files.length})`, ''];
1009
- for (const file of files.sort((a, b) => a.path.localeCompare(b.path))) {
1010
- if (includeMetadata) {
1011
- lines.push(`- ${file.path} (${file.language}, ${file.nodeCount} symbols)`);
1012
- }
1013
- else {
1014
- lines.push(`- ${file.path}`);
1015
- }
1016
- }
1017
- return lines.join('\n');
1018
- }
1019
- /**
1020
- * Format files grouped by language
1021
- */
1022
- formatFilesGrouped(files, includeMetadata) {
1023
- const byLang = new Map();
1024
- for (const file of files) {
1025
- const existing = byLang.get(file.language) || [];
1026
- existing.push(file);
1027
- byLang.set(file.language, existing);
1028
- }
1029
- const lines = [`## Files by Language (${files.length} total)`, ''];
1030
- // Sort languages by file count (descending)
1031
- const sortedLangs = [...byLang.entries()].sort((a, b) => b[1].length - a[1].length);
1032
- for (const [lang, langFiles] of sortedLangs) {
1033
- lines.push(`### ${lang} (${langFiles.length})`);
1034
- for (const file of langFiles.sort((a, b) => a.path.localeCompare(b.path))) {
1035
- if (includeMetadata) {
1036
- lines.push(`- ${file.path} (${file.nodeCount} symbols)`);
1037
- }
1038
- else {
1039
- lines.push(`- ${file.path}`);
1040
- }
1041
- }
1042
- lines.push('');
1043
- }
1044
- return lines.join('\n');
1045
- }
1046
- /**
1047
- * Format files as a tree structure
1048
- */
1049
- formatFilesTree(files, includeMetadata, maxDepth) {
1050
- const root = { name: '', children: new Map() };
1051
- for (const file of files) {
1052
- const parts = file.path.split('/');
1053
- let current = root;
1054
- for (let i = 0; i < parts.length; i++) {
1055
- const part = parts[i];
1056
- if (!part)
1057
- continue;
1058
- if (!current.children.has(part)) {
1059
- current.children.set(part, { name: part, children: new Map() });
1060
- }
1061
- current = current.children.get(part);
1062
- // If this is the last part, it's a file
1063
- if (i === parts.length - 1) {
1064
- current.file = { language: file.language, nodeCount: file.nodeCount };
1065
- }
1066
- }
1067
- }
1068
- // Render tree
1069
- const lines = [`## Project Structure (${files.length} files)`, ''];
1070
- const renderNode = (node, prefix, isLast, depth) => {
1071
- if (maxDepth !== undefined && depth > maxDepth)
1072
- return;
1073
- const connector = isLast ? '└── ' : '├── ';
1074
- const childPrefix = isLast ? ' ' : '│ ';
1075
- if (node.name) {
1076
- let line = prefix + connector + node.name;
1077
- if (node.file && includeMetadata) {
1078
- line += ` (${node.file.language}, ${node.file.nodeCount} symbols)`;
1079
- }
1080
- lines.push(line);
1081
- }
1082
- const children = [...node.children.values()];
1083
- // Sort: directories first, then files, both alphabetically
1084
- children.sort((a, b) => {
1085
- const aIsDir = a.children.size > 0 && !a.file;
1086
- const bIsDir = b.children.size > 0 && !b.file;
1087
- if (aIsDir !== bIsDir)
1088
- return aIsDir ? -1 : 1;
1089
- return a.name.localeCompare(b.name);
1090
- });
1091
- for (let i = 0; i < children.length; i++) {
1092
- const child = children[i];
1093
- const nextPrefix = node.name ? prefix + childPrefix : prefix;
1094
- renderNode(child, nextPrefix, i === children.length - 1, depth + 1);
1095
- }
1096
- };
1097
- renderNode(root, '', true, 0);
1098
- return lines.join('\n');
1099
- }
1100
- // =========================================================================
1101
- // Symbol resolution helpers
1102
- // =========================================================================
1103
- /**
1104
- * Find a symbol by name, handling disambiguation when multiple matches exist.
1105
- * Returns the best match and a note about alternatives if any.
1106
- */
1107
- /**
1108
- * Check if a node matches a symbol query.
1109
- *
1110
- * Accepts simple names (`run`) and three flavors of qualifier:
1111
- * - dotted `Session.request` (TS/JS/Python)
1112
- * - colon-pair `stage_apply::run` (Rust, C++, Ruby)
1113
- * - slash `configurator/stage_apply` (path-ish)
1114
- *
1115
- * Multi-level qualifiers compose: `crate::configurator::stage_apply::run`
1116
- * works. Rust path prefixes (`crate`, `super`, `self`) are stripped so
1117
- * the canonical `crate::module::symbol` form resolves.
1118
- *
1119
- * Resolution order, last part must always equal `node.name`:
1120
- * 1. Suffix-match against `qualifiedName` (handles class-scoped methods
1121
- * where the extractor builds the qualified name from the AST stack)
1122
- * 2. File-path containment (handles file-derived modules in Rust/
1123
- * Python — `stage_apply::run` matches a `run` in `stage_apply.rs`)
1124
- */
1125
- matchesSymbol(node, symbol) {
1126
- // Simple name match
1127
- if (node.name === symbol)
1128
- return true;
1129
- // File basename match (e.g., "product-card" matches "product-card.liquid")
1130
- if (node.kind === 'file' && node.name.replace(/\.[^.]+$/, '') === symbol)
1131
- return true;
1132
- // Qualified-name lookups: split on any supported separator. `\w` keeps
1133
- // identifier chars (incl. `_`) intact; everything else is treated as
1134
- // a separator we tolerate.
1135
- if (!/[.\/]|::/.test(symbol))
1136
- return false;
1137
- const parts = symbol.split(/::|[./]/).filter((p) => p.length > 0);
1138
- if (parts.length < 2)
1139
- return false;
1140
- const lastPart = parts[parts.length - 1];
1141
- if (node.name !== lastPart)
1142
- return false;
1143
- // Stage 1: qualified-name suffix match. The extractor joins the
1144
- // semantic hierarchy with `::`, so `Session.request` and
1145
- // `Session::request` both become `Session::request` here.
1146
- const colonSuffix = parts.join('::');
1147
- if (node.qualifiedName.includes(colonSuffix))
1148
- return true;
1149
- // Stage 2: file-path containment. Rust modules and Python packages
1150
- // are not in `qualifiedName` — they're encoded in the file path. So
1151
- // `stage_apply::run` matches a `run` in any file whose path
1152
- // contains a `stage_apply` segment (with or without an extension).
1153
- //
1154
- // Filter out Rust path prefixes that have no file-system equivalent.
1155
- const containerHints = parts.slice(0, -1).filter((p) => !RUST_PATH_PREFIXES.has(p));
1156
- if (containerHints.length === 0)
1157
- return false;
1158
- const segments = node.filePath.split('/').filter((s) => s.length > 0);
1159
- return containerHints.every((hint) => segments.some((seg) => seg === hint || seg.replace(/\.[^.]+$/, '') === hint));
1160
- }
1161
- findSymbol(cg, symbol) {
1162
- // Use higher limit for qualified lookups (e.g., "Session.request",
1163
- // "stage_apply::run") since the target may rank lower in FTS when
1164
- // there are many partial matches across the qualifier parts.
1165
- const isQualified = /[.\/]|::/.test(symbol);
1166
- const limit = isQualified ? 50 : 10;
1167
- let results = cg.searchNodes(symbol, { limit });
1168
- // FTS strips colons as a special char, so `stage_apply::run` searches
1169
- // for the literal `stage_applyrun` and finds nothing. Re-search by
1170
- // the bare last part and let `matchesSymbol` filter by qualifier.
1171
- if (isQualified && results.length === 0) {
1172
- const tail = lastQualifierPart(symbol);
1173
- if (tail && tail !== symbol)
1174
- results = cg.searchNodes(tail, { limit });
1175
- }
1176
- if (results.length === 0 || !results[0]) {
1177
- return null;
1178
- }
1179
- const exactMatches = results.filter(r => this.matchesSymbol(r.node, symbol));
1180
- if (exactMatches.length === 1) {
1181
- return { node: exactMatches[0].node, note: '' };
1182
- }
1183
- if (exactMatches.length > 1) {
1184
- // Multiple exact matches - pick first, note the others
1185
- const picked = exactMatches[0].node;
1186
- const others = exactMatches.slice(1).map(r => `${r.node.name} (${r.node.kind}) at ${r.node.filePath}:${r.node.startLine}`);
1187
- const note = `\n\n> **Note:** ${exactMatches.length} symbols named "${symbol}". Showing results for \`${picked.filePath}:${picked.startLine}\`. Others: ${others.join(', ')}`;
1188
- return { node: picked, note };
1189
- }
1190
- // No exact match. For qualified lookups, don't silently fall back
1191
- // to a fuzzy result — the user typed a specific qualifier, and
1192
- // resolving `stage_apply::nonexistent_fn` to the unrelated
1193
- // `stage_apply.rs` file would be actively misleading (#173).
1194
- if (isQualified)
1195
- return null;
1196
- return { node: results[0].node, note: '' };
1197
- }
1198
- /**
1199
- * Find ALL symbols matching a name. Used by callers/callees/impact to aggregate
1200
- * results across all matching symbols (e.g., multiple classes with an `execute` method).
1201
- */
1202
- findAllSymbols(cg, symbol) {
1203
- let results = cg.searchNodes(symbol, { limit: 50 });
1204
- // Mirror the fallback in `findSymbol` for qualified queries — FTS
1205
- // strips colons, so a module-qualified lookup needs a second pass
1206
- // by the bare last part.
1207
- if (results.length === 0 && /[.\/]|::/.test(symbol)) {
1208
- const tail = lastQualifierPart(symbol);
1209
- if (tail && tail !== symbol)
1210
- results = cg.searchNodes(tail, { limit: 50 });
1211
- }
1212
- if (results.length === 0) {
1213
- return { nodes: [], note: '' };
1214
- }
1215
- const exactMatches = results.filter(r => this.matchesSymbol(r.node, symbol));
1216
- if (exactMatches.length <= 1) {
1217
- const node = exactMatches[0]?.node ?? results[0].node;
1218
- return { nodes: [node], note: '' };
1219
- }
1220
- const locations = exactMatches.map(r => `${r.node.kind} at ${r.node.filePath}:${r.node.startLine}`);
1221
- const note = `\n\n> **Note:** Aggregated results across ${exactMatches.length} symbols named "${symbol}": ${locations.join(', ')}`;
1222
- return { nodes: exactMatches.map(r => r.node), note };
1223
- }
1224
- /**
1225
- * Truncate output if it exceeds the maximum length
1226
- */
1227
- truncateOutput(text) {
1228
- if (text.length <= MAX_OUTPUT_LENGTH)
1229
- return text;
1230
- const truncated = text.slice(0, MAX_OUTPUT_LENGTH);
1231
- const lastNewline = truncated.lastIndexOf('\n');
1232
- const cutPoint = lastNewline > MAX_OUTPUT_LENGTH * 0.8 ? lastNewline : MAX_OUTPUT_LENGTH;
1233
- return truncated.slice(0, cutPoint) + '\n\n... (output truncated)';
1234
- }
1235
- // =========================================================================
1236
- // Formatting helpers (compact by default to reduce context usage)
1237
- // =========================================================================
1238
- formatSearchResults(results) {
1239
- const lines = [`## Search Results (${results.length} found)`, ''];
1240
- for (const result of results) {
1241
- const { node } = result;
1242
- const location = node.startLine ? `:${node.startLine}` : '';
1243
- // Compact format: one line per result with key info
1244
- lines.push(`### ${node.name} (${node.kind})`);
1245
- lines.push(`${node.filePath}${location}`);
1246
- if (node.signature)
1247
- lines.push(`\`${node.signature}\``);
1248
- lines.push('');
1249
- }
1250
- return lines.join('\n');
1251
- }
1252
- formatNodeList(nodes, title) {
1253
- const lines = [`## ${title} (${nodes.length} found)`, ''];
1254
- for (const node of nodes) {
1255
- const location = node.startLine ? `:${node.startLine}` : '';
1256
- // Compact: just name, kind, location
1257
- lines.push(`- ${node.name} (${node.kind}) - ${node.filePath}${location}`);
1258
- }
1259
- return lines.join('\n');
1260
- }
1261
- formatImpact(symbol, impact) {
1262
- const nodeCount = impact.nodes.size;
1263
- // Compact format: just list affected symbols grouped by file
1264
- const lines = [
1265
- `## Impact: "${symbol}" affects ${nodeCount} symbols`,
1266
- '',
1267
- ];
1268
- // Group by file
1269
- const byFile = new Map();
1270
- for (const node of impact.nodes.values()) {
1271
- const existing = byFile.get(node.filePath) || [];
1272
- existing.push(node);
1273
- byFile.set(node.filePath, existing);
1274
- }
1275
- for (const [file, nodes] of byFile) {
1276
- lines.push(`**${file}:**`);
1277
- // Compact: inline list
1278
- const nodeList = nodes.map(n => `${n.name}:${n.startLine}`).join(', ');
1279
- lines.push(nodeList);
1280
- lines.push('');
1281
- }
1282
- return lines.join('\n');
1283
- }
1284
- formatNodeDetails(node, code) {
1285
- const location = node.startLine ? `:${node.startLine}` : '';
1286
- const lines = [
1287
- `## ${node.name} (${node.kind})`,
1288
- '',
1289
- `**Location:** ${node.filePath}${location}`,
1290
- ];
1291
- if (node.signature) {
1292
- lines.push(`**Signature:** \`${node.signature}\``);
1293
- }
1294
- // Only include docstring if it's short and useful
1295
- if (node.docstring && node.docstring.length < 200) {
1296
- lines.push('', node.docstring);
1297
- }
1298
- if (code) {
1299
- lines.push('', '```' + node.language, code, '```');
1300
- }
1301
- return lines.join('\n');
1302
- }
1303
- formatTaskContext(context) {
1304
- return context.summary || 'No context found';
1305
- }
1306
- textResult(text) {
1307
- return {
1308
- content: [{ type: 'text', text }],
1309
- };
1310
- }
1311
- errorResult(message) {
1312
- return {
1313
- content: [{ type: 'text', text: `Error: ${message}` }],
1314
- isError: true,
1315
- };
1316
- }
1317
- }
1318
- exports.ToolHandler = ToolHandler;
1319
- //# sourceMappingURL=tools.js.map