codebase-onboarder 0.5.0 → 0.7.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 (448) hide show
  1. package/README.md +196 -47
  2. package/bin/build-if-source.js +23 -0
  3. package/cli/commands.d.ts +26 -0
  4. package/cli/commands.js +836 -811
  5. package/cli/commands.js.map +1 -0
  6. package/cli/explorer/advanced.d.ts +14 -0
  7. package/cli/explorer/advanced.js +449 -0
  8. package/cli/explorer/advanced.js.map +1 -0
  9. package/cli/explorer/app.d.ts +1 -0
  10. package/cli/explorer/app.js +396 -229
  11. package/cli/explorer/app.js.map +1 -0
  12. package/cli/explorer/assistant.d.ts +9 -0
  13. package/cli/explorer/assistant.js +58 -0
  14. package/cli/explorer/assistant.js.map +1 -0
  15. package/cli/explorer/commands.d.ts +17 -0
  16. package/cli/explorer/commands.js +400 -162
  17. package/cli/explorer/commands.js.map +1 -0
  18. package/cli/explorer/featureViews.d.ts +124 -0
  19. package/cli/explorer/featureViews.js +179 -0
  20. package/cli/explorer/featureViews.js.map +1 -0
  21. package/cli/explorer/github.d.ts +21 -0
  22. package/cli/explorer/github.js +73 -0
  23. package/cli/explorer/github.js.map +1 -0
  24. package/cli/explorer/graphs.d.ts +7 -0
  25. package/cli/explorer/graphs.js +173 -0
  26. package/cli/explorer/graphs.js.map +1 -0
  27. package/cli/explorer/picker.d.ts +22 -0
  28. package/cli/explorer/picker.js +150 -0
  29. package/cli/explorer/picker.js.map +1 -0
  30. package/cli/explorer/report.d.ts +85 -0
  31. package/cli/explorer/report.js +60 -0
  32. package/cli/explorer/report.js.map +1 -0
  33. package/cli/explorer/session.d.ts +449 -0
  34. package/cli/explorer/session.js +184 -95
  35. package/cli/explorer/session.js.map +1 -0
  36. package/cli/explorer/views.d.ts +19 -0
  37. package/cli/explorer/views.js +496 -412
  38. package/cli/explorer/views.js.map +1 -0
  39. package/cli/explorer/wrap.d.ts +1 -0
  40. package/cli/explorer/wrap.js +33 -0
  41. package/cli/explorer/wrap.js.map +1 -0
  42. package/cli/main.d.ts +1 -0
  43. package/cli/main.js +235 -138
  44. package/cli/main.js.map +1 -0
  45. package/cli/prompt.d.ts +5 -0
  46. package/cli/prompt.js +84 -80
  47. package/cli/prompt.js.map +1 -0
  48. package/cli/scanOptions.d.ts +2 -0
  49. package/cli/scanOptions.js +11 -0
  50. package/cli/scanOptions.js.map +1 -0
  51. package/cli/ui.d.ts +33 -0
  52. package/cli/ui.js +55 -35
  53. package/cli/ui.js.map +1 -0
  54. package/cli/wizard.d.ts +187 -0
  55. package/cli/wizard.js +304 -284
  56. package/cli/wizard.js.map +1 -0
  57. package/package.json +15 -4
  58. package/public/app.d.ts +1 -0
  59. package/public/app.js +1057 -1009
  60. package/public/app.js.map +1 -0
  61. package/public/browser.d.ts +68 -0
  62. package/public/browser.js +2 -0
  63. package/public/browser.js.map +1 -0
  64. package/public/index.html +30 -13
  65. package/public/js/about.d.ts +6 -0
  66. package/public/js/about.js +165 -172
  67. package/public/js/about.js.map +1 -0
  68. package/public/js/aiDraft.d.ts +3 -0
  69. package/public/js/aiDraft.js +116 -124
  70. package/public/js/aiDraft.js.map +1 -0
  71. package/public/js/analysisPanel.d.ts +7 -0
  72. package/public/js/analysisPanel.js +166 -166
  73. package/public/js/analysisPanel.js.map +1 -0
  74. package/public/js/analysisReport.d.ts +38 -0
  75. package/public/js/analysisReport.js +105 -110
  76. package/public/js/analysisReport.js.map +1 -0
  77. package/public/js/api.d.ts +26 -0
  78. package/public/js/api.js +176 -167
  79. package/public/js/api.js.map +1 -0
  80. package/public/js/atlas.d.ts +6 -0
  81. package/public/js/atlas.js +50 -58
  82. package/public/js/atlas.js.map +1 -0
  83. package/public/js/blameView.d.ts +2 -0
  84. package/public/js/blameView.js +36 -37
  85. package/public/js/blameView.js.map +1 -0
  86. package/public/js/codeTab.d.ts +3 -0
  87. package/public/js/codeTab.js +175 -180
  88. package/public/js/codeTab.js.map +1 -0
  89. package/public/js/codeViewer.d.ts +5 -0
  90. package/public/js/codeViewer.js +162 -150
  91. package/public/js/codeViewer.js.map +1 -0
  92. package/public/js/components/FileChip.d.ts +9 -0
  93. package/public/js/components/FileChip.js +45 -49
  94. package/public/js/components/FileChip.js.map +1 -0
  95. package/public/js/components/MetricSparkline.d.ts +7 -0
  96. package/public/js/components/MetricSparkline.js +35 -44
  97. package/public/js/components/MetricSparkline.js.map +1 -0
  98. package/public/js/components/RiskBadge.d.ts +9 -0
  99. package/public/js/components/RiskBadge.js +55 -63
  100. package/public/js/components/RiskBadge.js.map +1 -0
  101. package/public/js/deepAnalysisView.d.ts +99 -0
  102. package/public/js/deepAnalysisView.js +282 -302
  103. package/public/js/deepAnalysisView.js.map +1 -0
  104. package/public/js/diagramPane.d.ts +8 -0
  105. package/public/js/diagramPane.js +133 -135
  106. package/public/js/diagramPane.js.map +1 -0
  107. package/public/js/diffView.d.ts +6 -0
  108. package/public/js/diffView.js +222 -209
  109. package/public/js/diffView.js.map +1 -0
  110. package/public/js/docsView.d.ts +2 -0
  111. package/public/js/docsView.js +441 -442
  112. package/public/js/docsView.js.map +1 -0
  113. package/public/js/dom.d.ts +189 -0
  114. package/public/js/dom.js +5 -0
  115. package/public/js/dom.js.map +1 -0
  116. package/public/js/fileSourceBrowser.d.ts +5 -0
  117. package/public/js/fileSourceBrowser.js +33 -41
  118. package/public/js/fileSourceBrowser.js.map +1 -0
  119. package/public/js/flameGraph.d.ts +5 -0
  120. package/public/js/flameGraph.js +62 -72
  121. package/public/js/flameGraph.js.map +1 -0
  122. package/public/js/forceGraph.d.ts +10 -0
  123. package/public/js/forceGraph.js +581 -556
  124. package/public/js/forceGraph.js.map +1 -0
  125. package/public/js/heatmap.d.ts +4 -0
  126. package/public/js/heatmap.js +142 -164
  127. package/public/js/heatmap.js.map +1 -0
  128. package/public/js/highlight.d.ts +2 -0
  129. package/public/js/highlight.js +116 -107
  130. package/public/js/highlight.js.map +1 -0
  131. package/public/js/html.d.ts +2 -0
  132. package/public/js/html.js +14 -44
  133. package/public/js/html.js.map +1 -0
  134. package/public/js/insightsView.d.ts +5 -0
  135. package/public/js/insightsView.js +93 -111
  136. package/public/js/insightsView.js.map +1 -0
  137. package/public/js/inspector.d.ts +37 -0
  138. package/public/js/inspector.js +416 -445
  139. package/public/js/inspector.js.map +1 -0
  140. package/public/js/llm.d.ts +113 -0
  141. package/public/js/llm.js +233 -272
  142. package/public/js/llm.js.map +1 -0
  143. package/public/js/mapView.d.ts +3 -0
  144. package/public/js/mapView.js +121 -120
  145. package/public/js/mapView.js.map +1 -0
  146. package/public/js/markdown.d.ts +3 -0
  147. package/public/js/markdown.js +84 -91
  148. package/public/js/markdown.js.map +1 -0
  149. package/public/js/mindmap.d.ts +32 -0
  150. package/public/js/mindmap.js +244 -259
  151. package/public/js/mindmap.js.map +1 -0
  152. package/public/js/mobilePanels.d.ts +5 -0
  153. package/public/js/mobilePanels.js +50 -0
  154. package/public/js/mobilePanels.js.map +1 -0
  155. package/public/js/repoFiles.d.ts +12 -0
  156. package/public/js/repoFiles.js +28 -27
  157. package/public/js/repoFiles.js.map +1 -0
  158. package/public/js/reviewView.d.ts +28 -0
  159. package/public/js/reviewView.js +256 -0
  160. package/public/js/reviewView.js.map +1 -0
  161. package/public/js/sbomView.d.ts +3 -0
  162. package/public/js/sbomView.js +59 -67
  163. package/public/js/sbomView.js.map +1 -0
  164. package/public/js/scanCache.d.ts +16 -0
  165. package/public/js/scanCache.js +67 -65
  166. package/public/js/scanCache.js.map +1 -0
  167. package/public/js/search.d.ts +7 -0
  168. package/public/js/search.js +868 -800
  169. package/public/js/search.js.map +1 -0
  170. package/public/js/searchLocal.d.ts +42 -0
  171. package/public/js/searchLocal.js +87 -0
  172. package/public/js/searchLocal.js.map +1 -0
  173. package/public/js/serverSettings.d.ts +5 -0
  174. package/public/js/serverSettings.js +153 -164
  175. package/public/js/serverSettings.js.map +1 -0
  176. package/public/js/state.d.ts +798 -0
  177. package/public/js/state.js +114 -122
  178. package/public/js/state.js.map +1 -0
  179. package/public/js/tour.d.ts +6 -0
  180. package/public/js/tour.js +5 -7
  181. package/public/js/tour.js.map +1 -0
  182. package/public/js/transitions.d.ts +2 -0
  183. package/public/js/transitions.js +9 -10
  184. package/public/js/transitions.js.map +1 -0
  185. package/public/js/tree.d.ts +12 -0
  186. package/public/js/tree.js +157 -154
  187. package/public/js/tree.js.map +1 -0
  188. package/public/js/workflowsView.d.ts +3 -0
  189. package/public/js/workflowsView.js +15 -22
  190. package/public/js/workflowsView.js.map +1 -0
  191. package/public/search.css +495 -0
  192. package/public/styles.css +163 -346
  193. package/public/workbench.css +166 -0
  194. package/server/apiAuth.d.ts +3 -0
  195. package/server/apiAuth.js +18 -20
  196. package/server/apiAuth.js.map +1 -0
  197. package/server/apiDiff.d.ts +2 -0
  198. package/server/apiDiff.js +20 -18
  199. package/server/apiDiff.js.map +1 -0
  200. package/server/apiDocs.d.ts +2 -0
  201. package/server/apiDocs.js +38 -40
  202. package/server/apiDocs.js.map +1 -0
  203. package/server/apiFile.d.ts +1 -0
  204. package/server/apiFile.js +14 -17
  205. package/server/apiFile.js.map +1 -0
  206. package/server/apiGitBlame.d.ts +2 -0
  207. package/server/apiGitBlame.js +50 -51
  208. package/server/apiGitBlame.js.map +1 -0
  209. package/server/apiMcp.d.ts +4 -0
  210. package/server/apiMcp.js +64 -66
  211. package/server/apiMcp.js.map +1 -0
  212. package/server/apiReview.d.ts +2 -0
  213. package/server/apiReview.js +45 -0
  214. package/server/apiReview.js.map +1 -0
  215. package/server/apiScan.d.ts +5 -0
  216. package/server/apiScan.js +90 -71
  217. package/server/apiScan.js.map +1 -0
  218. package/server/apiSearch.d.ts +33 -0
  219. package/server/apiSearch.js +184 -200
  220. package/server/apiSearch.js.map +1 -0
  221. package/server/apiSettings.d.ts +4 -0
  222. package/server/apiSettings.js +84 -85
  223. package/server/apiSettings.js.map +1 -0
  224. package/server/apiTools.d.ts +3 -0
  225. package/server/apiTools.js +55 -56
  226. package/server/apiTools.js.map +1 -0
  227. package/server/auth.d.ts +5 -0
  228. package/server/auth.js +54 -51
  229. package/server/auth.js.map +1 -0
  230. package/server/config.d.ts +42 -0
  231. package/server/config.js +189 -194
  232. package/server/config.js.map +1 -0
  233. package/server/daemon.d.ts +12 -0
  234. package/server/daemon.js +124 -114
  235. package/server/daemon.js.map +1 -0
  236. package/server/fileSourceNode.d.ts +2 -0
  237. package/server/fileSourceNode.js +34 -36
  238. package/server/fileSourceNode.js.map +1 -0
  239. package/server/gitClone.d.ts +9 -0
  240. package/server/gitClone.js +59 -61
  241. package/server/gitClone.js.map +1 -0
  242. package/server/gitDiff.d.ts +36 -0
  243. package/server/gitDiff.js +271 -169
  244. package/server/gitDiff.js.map +1 -0
  245. package/server/gitHistory.d.ts +12 -0
  246. package/server/gitHistory.js +80 -80
  247. package/server/gitHistory.js.map +1 -0
  248. package/server/gitRemote.d.ts +12 -0
  249. package/server/gitRemote.js +60 -0
  250. package/server/gitRemote.js.map +1 -0
  251. package/server/htmlText.d.ts +4 -0
  252. package/server/htmlText.js +16 -23
  253. package/server/htmlText.js.map +1 -0
  254. package/server/http.d.ts +5 -0
  255. package/server/http.js +27 -30
  256. package/server/http.js.map +1 -0
  257. package/server/httpGuards.d.ts +2 -0
  258. package/server/httpGuards.js +47 -43
  259. package/server/httpGuards.js.map +1 -0
  260. package/server/https.d.ts +34 -0
  261. package/server/https.js +121 -111
  262. package/server/https.js.map +1 -0
  263. package/server/index.d.ts +40 -0
  264. package/server/index.js +139 -147
  265. package/server/index.js.map +1 -0
  266. package/server/layout.d.ts +26 -0
  267. package/server/layout.js +59 -37
  268. package/server/layout.js.map +1 -0
  269. package/server/llmProxy.d.ts +3 -0
  270. package/server/llmProxy.js +137 -135
  271. package/server/llmProxy.js.map +1 -0
  272. package/server/logger.d.ts +38 -0
  273. package/server/logger.js +198 -211
  274. package/server/logger.js.map +1 -0
  275. package/server/mcp/analysis.d.ts +29 -0
  276. package/server/mcp/analysis.js +99 -120
  277. package/server/mcp/analysis.js.map +1 -0
  278. package/server/mcp/http.d.ts +9 -0
  279. package/server/mcp/http.js +128 -137
  280. package/server/mcp/http.js.map +1 -0
  281. package/server/mcp/runner.d.ts +68 -0
  282. package/server/mcp/runner.js +208 -218
  283. package/server/mcp/runner.js.map +1 -0
  284. package/server/mcp/server.d.ts +29 -0
  285. package/server/mcp/server.js +166 -179
  286. package/server/mcp/server.js.map +1 -0
  287. package/server/mcp/standalone.d.ts +1 -0
  288. package/server/mcp/standalone.js +8 -10
  289. package/server/mcp/standalone.js.map +1 -0
  290. package/server/mcp/tools.d.ts +2020 -0
  291. package/server/mcp/tools.js +523 -574
  292. package/server/mcp/tools.js.map +1 -0
  293. package/server/paths.d.ts +3 -0
  294. package/server/paths.js +17 -15
  295. package/server/paths.js.map +1 -0
  296. package/server/pidfile.d.ts +9 -0
  297. package/server/pidfile.js +43 -45
  298. package/server/pidfile.js.map +1 -0
  299. package/server/review.d.ts +5 -0
  300. package/server/review.js +35 -0
  301. package/server/review.js.map +1 -0
  302. package/server/router.d.ts +33 -0
  303. package/server/router.js +191 -197
  304. package/server/router.js.map +1 -0
  305. package/server/scanRequest.d.ts +3 -0
  306. package/server/scanRequest.js +36 -0
  307. package/server/scanRequest.js.map +1 -0
  308. package/server/searchIndex.d.ts +17 -0
  309. package/server/searchIndex.js +83 -80
  310. package/server/searchIndex.js.map +1 -0
  311. package/server/sessions.d.ts +14 -0
  312. package/server/sessions.js +62 -70
  313. package/server/sessions.js.map +1 -0
  314. package/server/startup.d.ts +118 -0
  315. package/server/startup.js +143 -148
  316. package/server/startup.js.map +1 -0
  317. package/server/static.d.ts +6 -0
  318. package/server/static.js +28 -31
  319. package/server/static.js.map +1 -0
  320. package/server/tools/install.d.ts +4 -0
  321. package/server/tools/install.js +176 -185
  322. package/server/tools/install.js.map +1 -0
  323. package/server/tools/parse.d.ts +8 -0
  324. package/server/tools/parse.js +112 -114
  325. package/server/tools/parse.js.map +1 -0
  326. package/server/tools/platform.d.ts +9 -0
  327. package/server/tools/platform.js +49 -47
  328. package/server/tools/platform.js.map +1 -0
  329. package/server/tools/registry.d.ts +137 -0
  330. package/server/tools/registry.js +173 -172
  331. package/server/tools/registry.js.map +1 -0
  332. package/server/tools/scan.d.ts +34 -0
  333. package/server/tools/scan.js +89 -98
  334. package/server/tools/scan.js.map +1 -0
  335. package/server/tools.d.ts +34 -0
  336. package/server/tools.js +94 -107
  337. package/server/tools.js.map +1 -0
  338. package/server/tunnel.d.ts +29 -0
  339. package/server/tunnel.js +61 -66
  340. package/server/tunnel.js.map +1 -0
  341. package/shared/analyzer/docs.d.ts +16 -0
  342. package/shared/analyzer/docs.js +99 -93
  343. package/shared/analyzer/docs.js.map +1 -0
  344. package/shared/analyzer/explainLocal.d.ts +4 -0
  345. package/shared/analyzer/explainLocal.js +122 -142
  346. package/shared/analyzer/explainLocal.js.map +1 -0
  347. package/shared/analyzer/github.d.ts +37 -0
  348. package/shared/analyzer/github.js +79 -0
  349. package/shared/analyzer/github.js.map +1 -0
  350. package/shared/analyzer/graph.d.ts +41 -0
  351. package/shared/analyzer/graph.js +393 -400
  352. package/shared/analyzer/graph.js.map +1 -0
  353. package/shared/analyzer/health.d.ts +33 -0
  354. package/shared/analyzer/health.js +197 -193
  355. package/shared/analyzer/health.js.map +1 -0
  356. package/shared/analyzer/history.d.ts +29 -0
  357. package/shared/analyzer/history.js +110 -111
  358. package/shared/analyzer/history.js.map +1 -0
  359. package/shared/analyzer/jsonConfig.d.ts +2 -0
  360. package/shared/analyzer/jsonConfig.js +61 -0
  361. package/shared/analyzer/jsonConfig.js.map +1 -0
  362. package/shared/analyzer/languages/csharp.d.ts +26 -0
  363. package/shared/analyzer/languages/csharp.js +83 -88
  364. package/shared/analyzer/languages/csharp.js.map +1 -0
  365. package/shared/analyzer/languages/generic.d.ts +23 -0
  366. package/shared/analyzer/languages/generic.js +94 -105
  367. package/shared/analyzer/languages/generic.js.map +1 -0
  368. package/shared/analyzer/languages/go.d.ts +24 -0
  369. package/shared/analyzer/languages/go.js +45 -54
  370. package/shared/analyzer/languages/go.js.map +1 -0
  371. package/shared/analyzer/languages/index.d.ts +6 -0
  372. package/shared/analyzer/languages/index.js +19 -20
  373. package/shared/analyzer/languages/index.js.map +1 -0
  374. package/shared/analyzer/languages/java.d.ts +20 -0
  375. package/shared/analyzer/languages/java.js +73 -78
  376. package/shared/analyzer/languages/java.js.map +1 -0
  377. package/shared/analyzer/languages/javascript.d.ts +12 -0
  378. package/shared/analyzer/languages/javascript.js +258 -209
  379. package/shared/analyzer/languages/javascript.js.map +1 -0
  380. package/shared/analyzer/languages/python.d.ts +23 -0
  381. package/shared/analyzer/languages/python.js +92 -100
  382. package/shared/analyzer/languages/python.js.map +1 -0
  383. package/shared/analyzer/languages/rust.d.ts +17 -0
  384. package/shared/analyzer/languages/rust.js +96 -98
  385. package/shared/analyzer/languages/rust.js.map +1 -0
  386. package/shared/analyzer/languages/typescript.d.ts +11 -0
  387. package/shared/analyzer/languages/typescript.js +117 -116
  388. package/shared/analyzer/languages/typescript.js.map +1 -0
  389. package/shared/analyzer/licenses.d.ts +18 -0
  390. package/shared/analyzer/licenses.js +130 -133
  391. package/shared/analyzer/licenses.js.map +1 -0
  392. package/shared/analyzer/metrics.d.ts +20 -0
  393. package/shared/analyzer/metrics.js +112 -104
  394. package/shared/analyzer/metrics.js.map +1 -0
  395. package/shared/analyzer/pathUtil.d.ts +10 -0
  396. package/shared/analyzer/pathUtil.js +29 -35
  397. package/shared/analyzer/pathUtil.js.map +1 -0
  398. package/shared/analyzer/patterns.d.ts +11 -0
  399. package/shared/analyzer/patterns.js +231 -253
  400. package/shared/analyzer/patterns.js.map +1 -0
  401. package/shared/analyzer/scan.d.ts +62 -0
  402. package/shared/analyzer/scan.js +451 -442
  403. package/shared/analyzer/scan.js.map +1 -0
  404. package/shared/analyzer/scanControl.d.ts +11 -0
  405. package/shared/analyzer/scanControl.js +62 -0
  406. package/shared/analyzer/scanControl.js.map +1 -0
  407. package/shared/analyzer/security.d.ts +18 -0
  408. package/shared/analyzer/security.js +159 -159
  409. package/shared/analyzer/security.js.map +1 -0
  410. package/shared/analyzer/services.d.ts +5 -0
  411. package/shared/analyzer/services.js +141 -138
  412. package/shared/analyzer/services.js.map +1 -0
  413. package/shared/analyzer/stack.d.ts +6 -0
  414. package/shared/analyzer/stack.js +134 -141
  415. package/shared/analyzer/stack.js.map +1 -0
  416. package/shared/analyzer/tour.d.ts +2 -0
  417. package/shared/analyzer/tour.js +45 -53
  418. package/shared/analyzer/tour.js.map +1 -0
  419. package/shared/analyzer/util.d.ts +8 -0
  420. package/shared/analyzer/util.js +130 -76
  421. package/shared/analyzer/util.js.map +1 -0
  422. package/shared/analyzer/workflows.d.ts +8 -0
  423. package/shared/analyzer/workflows.js +139 -134
  424. package/shared/analyzer/workflows.js.map +1 -0
  425. package/shared/contracts.d.ts +250 -0
  426. package/shared/contracts.js +2 -0
  427. package/shared/contracts.js.map +1 -0
  428. package/shared/diagram/aiFacts.d.ts +35 -0
  429. package/shared/diagram/aiFacts.js +102 -105
  430. package/shared/diagram/aiFacts.js.map +1 -0
  431. package/shared/diagram/aiMermaid.d.ts +5 -0
  432. package/shared/diagram/aiMermaid.js +46 -45
  433. package/shared/diagram/aiMermaid.js.map +1 -0
  434. package/shared/diagram/atlas.d.ts +16 -0
  435. package/shared/diagram/atlas.js +51 -60
  436. package/shared/diagram/atlas.js.map +1 -0
  437. package/shared/diagram/mermaid.d.ts +43 -0
  438. package/shared/diagram/mermaid.js +348 -370
  439. package/shared/diagram/mermaid.js.map +1 -0
  440. package/shared/review/contracts.d.ts +95 -0
  441. package/shared/review/contracts.js +2 -0
  442. package/shared/review/contracts.js.map +1 -0
  443. package/shared/review/review.d.ts +10 -0
  444. package/shared/review/review.js +184 -0
  445. package/shared/review/review.js.map +1 -0
  446. package/shared/search/query.d.ts +32 -0
  447. package/shared/search/query.js +395 -388
  448. package/shared/search/query.js.map +1 -0
@@ -15,223 +15,205 @@
15
15
  // Every tool declares `path` in its schema, and every handler resolves it through
16
16
  // `analyzeRepository`, so a client that forgets the path gets a sentence that says
17
17
  // so rather than a validation error with no context.
18
-
19
- import {
20
- analyzeRepository, cachedRoots, fileIn, readFileText, filesUnder, parsedFilesUnder,
21
- ToolError, scanIndex, baseName, explainFile, explainFolder,
22
- docFileRow, fileFactsLine, fileStaticDoc, folderStaticDoc, searchDocuments,
23
- } from './analysis.js';
18
+ import { analyzeRepository, cachedRoots, fileIn, readFileText, filesUnder, parsedFilesUnder, ToolError, scanIndex, baseName, explainFile, explainFolder, docFileRow, fileFactsLine, fileStaticDoc, folderStaticDoc, searchDocuments, } from './analysis.js';
24
19
  import { buildTourStops } from '../../shared/analyzer/tour.js';
25
20
  import { runExternalAnalysis, toolsStatus } from '../tools/scan.js';
26
21
  import { TOOL_DEFS } from '../tools/registry.js';
27
-
28
22
  const PATH_PROPERTY = {
29
- type: 'string',
30
- description:
31
- 'Absolute path to the repository on this machine (for example /Users/you/code/my-app). '
32
- + 'The path returned by onboarder_scan is the one to reuse. Omit it to use the last repository scanned.',
23
+ type: 'string',
24
+ description: 'Absolute path to the repository on this machine (for example /Users/you/code/my-app). '
25
+ + 'The path returned by onboarder_scan is the one to reuse. Omit it to use the last repository scanned.',
33
26
  };
34
-
35
27
  const LIMIT_PROPERTY = {
36
- type: 'integer',
37
- description: 'Maximum number of results. Defaults to 20, capped at 200.',
38
- minimum: 1,
39
- maximum: 200,
28
+ type: 'integer',
29
+ description: 'Maximum number of results. Defaults to 20, capped at 200.',
30
+ minimum: 1,
31
+ maximum: 200,
40
32
  };
41
-
42
33
  // A client that never scans anything still gets a useful answer from the path it
43
34
  // happens to send, and a client that scanned something and then omits the path
44
35
  // gets the one it scanned. `lastRoot` is that memory.
45
36
  let lastRoot = null;
46
-
47
37
  async function resolve({ path: p }) {
48
- const root = String(p || '').trim() || lastRoot;
49
- if (!root) {
50
- throw new ToolError('No repository yet — call onboarder_scan with a path first.');
51
- }
52
- const analysis = await analyzeRepository(root);
53
- lastRoot = analysis.root;
54
- return analysis;
38
+ const root = String(p || '').trim() || lastRoot;
39
+ if (!root) {
40
+ throw new ToolError('No repository yet — call onboarder_scan with a path first.');
41
+ }
42
+ const analysis = await analyzeRepository(root);
43
+ lastRoot = analysis.root;
44
+ return analysis;
55
45
  }
56
-
57
46
  // Scans are expensive and the cache holds a few; a tool that takes a limit should
58
47
  // not be able to be talked into returning the whole repository.
59
48
  function limitOf(value, fallback = 20, max = 200) {
60
- const n = Number(value);
61
- if (!Number.isFinite(n)) return fallback;
62
- return Math.max(1, Math.min(max, Math.floor(n)));
49
+ const n = Number(value);
50
+ if (!Number.isFinite(n))
51
+ return fallback;
52
+ return Math.max(1, Math.min(max, Math.floor(n)));
63
53
  }
64
-
65
54
  // ---- the payloads -----------------------------------------------------------
66
55
  //
67
56
  // Each of these is a view's data, flattened into plain JSON. The point is that an
68
57
  // agent can act on the answer without knowing this codebase: a hub is a path and a
69
58
  // count, not an index into an array the tool kept to itself.
70
-
71
59
  function overviewPayload(a) {
72
- const { scan, facts, manifest, health, security } = a;
73
- return {
74
- root: a.root,
75
- name: scan.name,
76
- summary: a.overview,
77
- stats: {
78
- files: scan.stats.filesParsed,
79
- filesSeen: scan.stats.filesTotal,
80
- languages: scan.stats.languages,
81
- connections: scan.stats.edgeCount,
82
- importsResolved: scan.stats.imports?.confidence ?? null,
83
- },
84
- entryPoints: facts.entries.slice(0, 10),
85
- hubs: facts.hubs.slice(0, 10).map((h) => ({ path: h.path, dependents: h.fanIn, dependencies: h.fanOut })),
86
- cycles: facts.cycles.slice(0, 5),
87
- orphanCount: facts.orphans.length,
88
- deadExportCount: (facts.deadExports || []).length,
89
- services: (manifest.services || []).map((s) => ({ name: s.name, kind: s.kind, port: s.port ?? null })),
90
- // A repo's dependencies are a different fact from its services — `express` is
91
- // a library, not a thing this app runs — so they are named apart. They are
92
- // here, rather than only in `onboarder_dependencies`, because "what does this
93
- // project use" is a first-scan question and an extra round trip for it would
94
- // be a tool the agent has to learn exists.
95
- packageName: manifest.packageName || null,
96
- dependencies: Object.entries(manifest.deps?.npm || {})
97
- .map(([name, version]) => ({ name, version })),
98
- devDependencies: Object.keys(manifest.deps?.dev || {}),
99
- health: { score: health.score, grade: health.grade },
100
- security: { score: security.score, grade: security.grade, findings: security.total },
101
- caveats: a.caveats,
102
- };
60
+ const { scan, facts, manifest, health, security } = a;
61
+ return {
62
+ root: a.root,
63
+ name: scan.name,
64
+ summary: a.overview,
65
+ stats: {
66
+ files: scan.stats.filesParsed,
67
+ filesSeen: scan.stats.filesTotal,
68
+ languages: scan.stats.languages,
69
+ connections: scan.stats.edgeCount,
70
+ importsResolved: scan.stats.imports?.confidence ?? null,
71
+ },
72
+ entryPoints: facts.entries.slice(0, 10),
73
+ hubs: facts.hubs.slice(0, 10).map((h) => ({ path: h.path, dependents: h.fanIn, dependencies: h.fanOut })),
74
+ cycles: facts.cycles.slice(0, 5),
75
+ orphanCount: facts.orphans.length,
76
+ deadExportCount: (facts.deadExports || []).length,
77
+ services: (manifest.services || []).map((s) => ({ name: s.name, kind: s.kind, port: s.port ?? null })),
78
+ // A repo's dependencies are a different fact from its services — `express` is
79
+ // a library, not a thing this app runs — so they are named apart. They are
80
+ // here, rather than only in `onboarder_dependencies`, because "what does this
81
+ // project use" is a first-scan question and an extra round trip for it would
82
+ // be a tool the agent has to learn exists.
83
+ packageName: manifest.packageName || null,
84
+ dependencies: Object.entries(manifest.deps?.npm || {})
85
+ .map(([name, version]) => ({ name, version })),
86
+ devDependencies: Object.keys(manifest.deps?.dev || {}),
87
+ health: { score: health.score, grade: health.grade },
88
+ security: { score: security.score, grade: security.grade, findings: security.total },
89
+ caveats: a.caveats,
90
+ };
103
91
  }
104
-
105
92
  function architecturePayload(a) {
106
- const { layersInfo, findings, coupling } = a.patterns;
107
- // `couplingMatrix` returns a folder list and a `Map` keyed `'from->to'`, not a
108
- // list of edges. Flattening it here means the agent gets pairs; leaving it raw
109
- // means it has to know this module's encoding — and a Map does not survive
110
- // `JSON.stringify`, so it would arrive as `{}` rather than as an error.
111
- const pairs = coupling.counts instanceof Map
112
- ? [...coupling.counts.entries()]
113
- : Object.entries(coupling.counts || {});
114
- const edges = pairs
115
- .map(([key, count]) => {
116
- const [from, to] = key.split('->');
117
- return { from, to, count };
93
+ const { layersInfo, findings, coupling } = a.patterns;
94
+ // `couplingMatrix` returns a folder list and a `Map` keyed `'from->to'`, not a
95
+ // list of edges. Flattening it here means the agent gets pairs; leaving it raw
96
+ // means it has to know this module's encoding — and a Map does not survive
97
+ // `JSON.stringify`, so it would arrive as `{}` rather than as an error.
98
+ const pairs = coupling.counts instanceof Map
99
+ ? [...coupling.counts.entries()]
100
+ : Object.entries(coupling.counts || {});
101
+ const edges = pairs
102
+ .map(([key, count]) => {
103
+ const [from, to] = key.split('->');
104
+ return { from, to, count };
118
105
  })
119
- .sort((x, y) => y.count - x.count);
120
-
121
- return {
122
- // A layer here is a list of paths, not an object: depth is the array's index.
123
- layers: layersInfo.layers.map((files, depth) => ({
124
- depth,
125
- fileCount: files.length,
126
- files: files.slice(0, 20),
127
- })),
128
- seeds: layersInfo.seeds,
129
- unreachable: (layersInfo.unreachable || []).slice(0, 20),
130
- patterns: findings.map((p) => ({
131
- id: p.id,
132
- tone: p.tone,
133
- severity: p.severity,
134
- title: p.title,
135
- detail: p.detail,
136
- paths: (p.paths || []).slice(0, 10),
137
- })),
138
- coupling: { folders: coupling.folders, edges: edges.slice(0, 30), max: coupling.max },
139
- services: (a.manifest.services || []).map((s) => ({ name: s.name, kind: s.kind, port: s.port ?? null })),
140
- };
106
+ .sort((x, y) => y.count - x.count);
107
+ return {
108
+ // A layer here is a list of paths, not an object: depth is the array's index.
109
+ layers: layersInfo.layers.map((files, depth) => ({
110
+ depth,
111
+ fileCount: files.length,
112
+ files: files.slice(0, 20),
113
+ })),
114
+ seeds: layersInfo.seeds,
115
+ unreachable: (layersInfo.unreachable || []).slice(0, 20),
116
+ patterns: findings.map((p) => ({
117
+ id: p.id,
118
+ tone: p.tone,
119
+ severity: p.severity,
120
+ title: p.title,
121
+ detail: p.detail,
122
+ paths: (p.paths || []).slice(0, 10),
123
+ })),
124
+ coupling: { folders: coupling.folders, edges: edges.slice(0, 30), max: coupling.max },
125
+ services: (a.manifest.services || []).map((s) => ({ name: s.name, kind: s.kind, port: s.port ?? null })),
126
+ };
141
127
  }
142
-
143
128
  function healthPayload(a, limit) {
144
- return {
145
- root: a.root,
146
- score: a.health.score,
147
- grade: a.health.grade,
148
- totals: a.health.totals,
149
- breakdown: a.health.breakdown,
150
- // `blast` is a transitive-dependency count, exact only on smaller repos;
151
- // `blastExact` says which, because a number that quietly changes meaning is
152
- // worse than no number.
153
- blastExact: a.health.blastExact,
154
- riskiestFiles: a.health.perFile.slice(0, limit).map((f) => ({
155
- path: f.path,
156
- risk: f.risk,
157
- blast: f.blast,
158
- complexity: f.complexity,
159
- loc: f.loc,
160
- fanIn: f.fanIn,
161
- fanOut: f.fanOut,
162
- inCycle: f.inCycle,
163
- })),
164
- };
129
+ return {
130
+ root: a.root,
131
+ score: a.health.score,
132
+ grade: a.health.grade,
133
+ totals: a.health.totals,
134
+ breakdown: a.health.breakdown,
135
+ // `blast` is a transitive-dependency count, exact only on smaller repos;
136
+ // `blastExact` says which, because a number that quietly changes meaning is
137
+ // worse than no number.
138
+ blastExact: a.health.blastExact,
139
+ riskiestFiles: a.health.perFile.slice(0, limit).map((f) => ({
140
+ path: f.path,
141
+ risk: f.risk,
142
+ blast: f.blast,
143
+ complexity: f.complexity,
144
+ loc: f.loc,
145
+ fanIn: f.fanIn,
146
+ fanOut: f.fanOut,
147
+ inCycle: f.inCycle,
148
+ })),
149
+ };
165
150
  }
166
-
167
151
  function securityPayload(a, limit) {
168
- const s = a.security;
169
- // `summarizeSecurity` rolls up per file, keeping each file's own findings. The
170
- // flat list an agent wants is the concatenation, ordered worst-first by file.
171
- const findings = [];
172
- for (const file of s.files || []) {
173
- for (const f of file.findings || []) {
174
- findings.push({
175
- severity: f.severity,
176
- rule: f.rule,
177
- category: f.category,
178
- path: file.path,
179
- line: f.line ?? null,
180
- message: f.message,
181
- excerpt: f.excerpt,
182
- });
152
+ const s = a.security;
153
+ // `summarizeSecurity` rolls up per file, keeping each file's own findings. The
154
+ // flat list an agent wants is the concatenation, ordered worst-first by file.
155
+ const findings = [];
156
+ for (const file of s.files || []) {
157
+ for (const f of file.findings || []) {
158
+ findings.push({
159
+ severity: f.severity,
160
+ rule: f.rule,
161
+ category: f.category,
162
+ path: file.path,
163
+ line: f.line ?? null,
164
+ message: f.message,
165
+ excerpt: f.excerpt,
166
+ });
167
+ }
183
168
  }
184
- }
185
- return {
186
- root: a.root,
187
- score: s.score,
188
- grade: s.grade,
189
- total: s.total,
190
- bySeverity: s.counts,
191
- byCategory: s.byCat,
192
- filesWithFindings: (s.files || []).length,
193
- findings: findings.slice(0, limit),
194
- };
169
+ return {
170
+ root: a.root,
171
+ score: s.score,
172
+ grade: s.grade,
173
+ total: s.total,
174
+ bySeverity: s.counts,
175
+ byCategory: s.byCat,
176
+ filesWithFindings: (s.files || []).length,
177
+ findings: findings.slice(0, limit),
178
+ };
195
179
  }
196
-
197
180
  function historyPayload(a, limit) {
198
- const h = a.history;
199
- if (!h.available) return { available: false, reason: h.reason };
200
- return {
201
- available: true,
202
- commitCount: h.commitCount,
203
- totalCommits: h.totalCommits,
204
- truncated: h.truncated,
205
- firstCommitAt: h.firstCommitAt,
206
- lastCommitAt: h.lastCommitAt,
207
- contributors: (h.authors || []).slice(0, limit).map((c) => ({ name: c.name, commits: c.commits })),
208
- hotspots: (h.perFile || []).slice(0, limit).map((r) => ({
209
- path: r.path, churn: r.churn, authors: r.authors, complexity: r.complexity,
210
- hotspot: r.hotspot, firstSeen: r.firstSeen, lastTouched: r.lastTouched,
211
- })),
212
- soloFiles: h.soloFiles,
213
- // Files that keep changing together without importing each other — the
214
- // coupling the import graph cannot see.
215
- coChanged: (h.coChanged || []).slice(0, 15),
216
- pathsGone: h.pathsGone,
217
- };
181
+ const h = a.history;
182
+ if (!h.available)
183
+ return { available: false, reason: h.reason };
184
+ return {
185
+ available: true,
186
+ commitCount: h.commitCount,
187
+ totalCommits: h.totalCommits,
188
+ truncated: h.truncated,
189
+ firstCommitAt: h.firstCommitAt,
190
+ lastCommitAt: h.lastCommitAt,
191
+ contributors: (h.authors || []).slice(0, limit).map((c) => ({ name: c.name, commits: c.commits })),
192
+ hotspots: (h.perFile || []).slice(0, limit).map((r) => ({
193
+ path: r.path, churn: r.churn, authors: r.authors, complexity: r.complexity,
194
+ hotspot: r.hotspot, firstSeen: r.firstSeen, lastTouched: r.lastTouched,
195
+ })),
196
+ soloFiles: h.soloFiles,
197
+ // Files that keep changing together without importing each other — the
198
+ // coupling the import graph cannot see.
199
+ coChanged: (h.coChanged || []).slice(0, 15),
200
+ pathsGone: h.pathsGone,
201
+ };
218
202
  }
219
-
220
203
  function dependenciesPayload(a) {
221
- return {
222
- languages: a.stack.languages,
223
- packageManagers: a.stack.pm,
224
- dependencies: a.stack.items.map((d) => ({
225
- name: d.name, version: d.version, category: d.category, dev: d.dev, docs: d.docs,
226
- })),
227
- projectLicense: a.scan.licenseReport?.projectLicense || a.scan.license,
228
- licenseCounts: a.scan.licenseReport?.counts || null,
229
- workflows: (a.scan.workflows || []).map((w) => ({
230
- name: w.name, file: w.file || w.path, triggers: w.triggers, jobs: (w.jobs || []).map((j) => j.name),
231
- })),
232
- };
204
+ return {
205
+ languages: a.stack.languages,
206
+ packageManagers: a.stack.pm,
207
+ dependencies: a.stack.items.map((d) => ({
208
+ name: d.name, version: d.version, category: d.category, dev: d.dev, docs: d.docs,
209
+ })),
210
+ projectLicense: a.scan.licenseReport?.projectLicense || a.scan.license,
211
+ licenseCounts: a.scan.licenseReport?.counts || null,
212
+ workflows: (a.scan.workflows || []).map((w) => ({
213
+ name: w.name, file: w.file || w.path, triggers: w.triggers, jobs: (w.jobs || []).map((j) => j.name),
214
+ })),
215
+ };
233
216
  }
234
-
235
217
  // The folder boundary crossing for one folder, derived from the real edge list.
236
218
  //
237
219
  // `facts.folderEdges` is a rollup over top-level folders only — the coupling
@@ -245,439 +227,406 @@ function dependenciesPayload(a) {
245
227
  // side is named at the same depth as the folder, so a report about `server/mcp`
246
228
  // talks about its siblings rather than collapsing to `server`.
247
229
  function folderCrossings(a, folder) {
248
- const prefix = folder ? folder + '/' : '';
249
- const depth = folder ? folder.split('/').length : 1;
250
- const topFolder = (p) => p.split('/')[0];
251
- const other = (p) => (prefix && p.startsWith(prefix)
252
- ? p.slice(prefix.length).split('/').slice(0, depth).join('/')
253
- : p.split('/').slice(0, depth).join('/'));
254
-
255
- const out = new Map();
256
- const into = new Map();
257
- for (const e of a.scan.edges || []) {
258
- const fromIn = !prefix || e.from.startsWith(prefix);
259
- const toIn = !prefix || e.to.startsWith(prefix);
260
- // Every path is "in" the root, so with no prefix both flags are always true
261
- // and nothing counts as a crossing. The root's own coupling is the top-level
262
- // rollup, which is exactly the same question asked one level up.
263
- if (!prefix) {
264
- const from = topFolder(e.from);
265
- const to = topFolder(e.to);
266
- if (from !== to) out.set(to, (out.get(to) || 0) + 1);
267
- continue;
268
- }
269
- if (fromIn === toIn) continue; // an edge entirely inside the folder is not a crossing
270
- if (fromIn) {
271
- const key = other(e.to);
272
- out.set(key, (out.get(key) || 0) + 1);
273
- } else {
274
- const key = other(e.from);
275
- into.set(key, (into.get(key) || 0) + 1);
230
+ const prefix = folder ? folder + '/' : '';
231
+ const depth = folder ? folder.split('/').length : 1;
232
+ const topFolder = (p) => p.split('/')[0];
233
+ const other = (p) => (prefix && p.startsWith(prefix)
234
+ ? p.slice(prefix.length).split('/').slice(0, depth).join('/')
235
+ : p.split('/').slice(0, depth).join('/'));
236
+ const out = new Map();
237
+ const into = new Map();
238
+ for (const e of a.scan.edges || []) {
239
+ const fromIn = !prefix || e.from.startsWith(prefix);
240
+ const toIn = !prefix || e.to.startsWith(prefix);
241
+ // Every path is "in" the root, so with no prefix both flags are always true
242
+ // and nothing counts as a crossing. The root's own coupling is the top-level
243
+ // rollup, which is exactly the same question asked one level up.
244
+ if (!prefix) {
245
+ const from = topFolder(e.from);
246
+ const to = topFolder(e.to);
247
+ if (from !== to)
248
+ out.set(to, (out.get(to) || 0) + 1);
249
+ continue;
250
+ }
251
+ if (fromIn === toIn)
252
+ continue; // an edge entirely inside the folder is not a crossing
253
+ if (fromIn) {
254
+ const key = other(e.to);
255
+ out.set(key, (out.get(key) || 0) + 1);
256
+ }
257
+ else {
258
+ const key = other(e.from);
259
+ into.set(key, (into.get(key) || 0) + 1);
260
+ }
276
261
  }
277
- }
278
- const toList = (m) => [...m.entries()]
279
- .map(([name, count]) => ({ folder: name, count }))
280
- .sort((x, y) => y.count - x.count);
281
- return { imports: toList(out), importedBy: toList(into) };
262
+ const toList = (m) => [...m.entries()]
263
+ .map(([name, count]) => ({ folder: name, count }))
264
+ .sort((x, y) => y.count - x.count);
265
+ return { imports: toList(out), importedBy: toList(into) };
282
266
  }
283
-
284
267
  // The tour is the app's "read this first" list. The ordering lives in
285
268
  // `shared/analyzer/tour.js` so the browser and this server cannot disagree about
286
269
  // which files a newcomer should read.
287
270
  function tourPayload(a) {
288
- return {
289
- root: a.root,
290
- summary: a.overview,
291
- stops: buildTourStops(a.scan, a.facts).map((s, i) => ({
292
- order: i + 1,
293
- path: s.path,
294
- name: baseName(s.path),
295
- why: s.why,
296
- dependents: a.facts.fanIn[s.path] || 0,
297
- dependencies: a.facts.fanOut[s.path] || 0,
298
- })),
299
- };
271
+ return {
272
+ root: a.root,
273
+ summary: a.overview,
274
+ stops: buildTourStops(a.scan, a.facts).map((s, i) => ({
275
+ order: i + 1,
276
+ path: s.path,
277
+ name: baseName(s.path),
278
+ why: s.why,
279
+ dependents: a.facts.fanIn[s.path] || 0,
280
+ dependencies: a.facts.fanOut[s.path] || 0,
281
+ })),
282
+ };
300
283
  }
301
-
302
284
  // ---- the tool table ---------------------------------------------------------
303
-
304
285
  export const TOOLS = [
305
- {
306
- name: 'onboarder_scan',
307
- description:
308
- 'Scan a repository and return everything Onboarder knows about it: structure, entry points, '
309
- + 'hubs, cycles, dependencies, services, health, security, history and a written overview. '
310
- + 'Start here. The returned `root` is what every other tool takes as its `path`.',
311
- inputSchema: {
312
- type: 'object',
313
- properties: {
314
- path: PATH_PROPERTY,
315
- force: { type: 'boolean', description: 'Rescan even if this repository is already cached. Defaults to false.' },
316
- },
317
- required: [],
318
- },
319
- async run(args) {
320
- const a = await analyzeRepository(args.path, { force: !!args.force });
321
- lastRoot = a.root;
322
- return overviewPayload(a);
323
- },
324
- },
325
-
326
- {
327
- name: 'onboarder_rescan',
328
- description:
329
- 'Discard the cached analysis for a repository and scan it again. Use after changing files on disk, '
330
- + 'since every other tool answers from the cached scan.',
331
- inputSchema: { type: 'object', properties: { path: PATH_PROPERTY }, required: [] },
332
- async run(args) {
333
- const a = await analyzeRepository(args.path, { force: true });
334
- lastRoot = a.root;
335
- return overviewPayload(a);
286
+ {
287
+ name: 'onboarder_scan',
288
+ description: 'Scan a repository and return everything Onboarder knows about it: structure, entry points, '
289
+ + 'hubs, cycles, dependencies, services, health, security, history and a written overview. '
290
+ + 'Start here. The returned `root` is what every other tool takes as its `path`.',
291
+ inputSchema: {
292
+ type: 'object',
293
+ properties: {
294
+ path: PATH_PROPERTY,
295
+ force: { type: 'boolean', description: 'Rescan even if this repository is already cached. Defaults to false.' },
296
+ },
297
+ required: [],
298
+ },
299
+ async run(args) {
300
+ const a = await analyzeRepository(args.path, { force: !!args.force });
301
+ lastRoot = a.root;
302
+ return overviewPayload(a);
303
+ },
336
304
  },
337
- },
338
-
339
- {
340
- name: 'onboarder_overview',
341
- description:
342
- 'The repository in one call: written summary, languages, entry points, the files everything depends '
343
- + 'on, dependency cycles, services, and what the scan is unsure about. Cheap, because it reads the '
344
- + 'cached analysis rather than scanning again.',
345
- inputSchema: { type: 'object', properties: { path: PATH_PROPERTY }, required: [] },
346
- async run(args) { return overviewPayload(await resolve(args)); },
347
- },
348
-
349
- {
350
- name: 'onboarder_architecture',
351
- description:
352
- 'How the code is arranged: the dependency layers and which files sit in each, files nothing '
353
- + 'reaches, the architectural patterns detected (hub-and-spoke, cycles, god objects, layer '
354
- + 'violations), which folders import each other, and the services detected.',
355
- inputSchema: { type: 'object', properties: { path: PATH_PROPERTY }, required: [] },
356
- async run(args) { return architecturePayload(await resolve(args)); },
357
- },
358
-
359
- {
360
- name: 'onboarder_tour',
361
- description:
362
- 'A guided reading order for a new contributor: the handful of files worth reading first, each with '
363
- + 'the reason it matters and how many files depend on it. The fastest way to understand an '
364
- + 'unfamiliar repository.',
365
- inputSchema: { type: 'object', properties: { path: PATH_PROPERTY }, required: [] },
366
- async run(args) { return tourPayload(await resolve(args)); },
367
- },
368
-
369
- {
370
- name: 'onboarder_health',
371
- description:
372
- 'Maintainability report: an overall score and letter grade, what the score is made of, and the '
373
- + 'riskiest files with their size, complexity, dependency counts and blast radius.',
374
- inputSchema: {
375
- type: 'object',
376
- properties: { path: PATH_PROPERTY, limit: LIMIT_PROPERTY },
377
- required: [],
305
+ {
306
+ name: 'onboarder_rescan',
307
+ description: 'Discard the cached analysis for a repository and scan it again. Use after changing files on disk, '
308
+ + 'since every other tool answers from the cached scan.',
309
+ inputSchema: { type: 'object', properties: { path: PATH_PROPERTY }, required: [] },
310
+ async run(args) {
311
+ const a = await analyzeRepository(args.path, { force: true });
312
+ lastRoot = a.root;
313
+ return overviewPayload(a);
314
+ },
378
315
  },
379
- async run(args) { return healthPayload(await resolve(args), limitOf(args.limit, 20)); },
380
- },
381
-
382
- {
383
- name: 'onboarder_security',
384
- description:
385
- 'Static security findings from the built-in rules: hardcoded secrets, unsafe calls, injection '
386
- + 'patterns and the rest, ordered by severity. These are the built-in rules only — use '
387
- + 'onboarder_deep_analysis for the external scanners.',
388
- inputSchema: {
389
- type: 'object',
390
- properties: { path: PATH_PROPERTY, limit: LIMIT_PROPERTY },
391
- required: [],
316
+ {
317
+ name: 'onboarder_overview',
318
+ description: 'The repository in one call: written summary, languages, entry points, the files everything depends '
319
+ + 'on, dependency cycles, services, and what the scan is unsure about. Cheap, because it reads the '
320
+ + 'cached analysis rather than scanning again.',
321
+ inputSchema: { type: 'object', properties: { path: PATH_PROPERTY }, required: [] },
322
+ async run(args) { return overviewPayload(await resolve(args)); },
392
323
  },
393
- async run(args) { return securityPayload(await resolve(args), limitOf(args.limit, 50, 500)); },
394
- },
395
-
396
- {
397
- name: 'onboarder_history',
398
- description:
399
- 'What git knows: commit count, contributors, and the files with the highest churn weighted against '
400
- + 'complexity — the change-risk hotspots, plus files only one person has ever touched and files '
401
- + 'that keep changing together without importing each other.',
402
- inputSchema: {
403
- type: 'object',
404
- properties: { path: PATH_PROPERTY, limit: LIMIT_PROPERTY },
405
- required: [],
324
+ {
325
+ name: 'onboarder_architecture',
326
+ description: 'How the code is arranged: the dependency layers and which files sit in each, files nothing '
327
+ + 'reaches, the architectural patterns detected (hub-and-spoke, cycles, god objects, layer '
328
+ + 'violations), which folders import each other, and the services detected.',
329
+ inputSchema: { type: 'object', properties: { path: PATH_PROPERTY }, required: [] },
330
+ async run(args) { return architecturePayload(await resolve(args)); },
406
331
  },
407
- async run(args) { return historyPayload(await resolve(args), limitOf(args.limit, 25)); },
408
- },
409
-
410
- {
411
- name: 'onboarder_dependencies',
412
- description:
413
- 'The dependency inventory: languages, package managers, every declared dependency with version, '
414
- + 'category and documentation link, the project license with its compliance counts, and the CI '
415
- + 'workflows with their triggers and jobs.',
416
- inputSchema: { type: 'object', properties: { path: PATH_PROPERTY }, required: [] },
417
- async run(args) { return dependenciesPayload(await resolve(args)); },
418
- },
419
- {
420
- name: 'onboarder_explain_file',
421
- description:
422
- 'Explain one file: its role in the codebase, what it imports, what imports it, its exports, its '
423
- + 'metrics, and a written summary. Set includeSource to get the file text as well.',
424
- inputSchema: {
425
- type: 'object',
426
- properties: {
427
- path: PATH_PROPERTY,
428
- file: { type: 'string', description: 'Repository-relative path, for example server/router.js.' },
429
- includeSource: { type: 'boolean', description: 'Also return the full file text. Defaults to false.' },
430
- },
431
- required: ['file'],
332
+ {
333
+ name: 'onboarder_tour',
334
+ description: 'A guided reading order for a new contributor: the handful of files worth reading first, each with '
335
+ + 'the reason it matters and how many files depend on it. The fastest way to understand an '
336
+ + 'unfamiliar repository.',
337
+ inputSchema: { type: 'object', properties: { path: PATH_PROPERTY }, required: [] },
338
+ async run(args) { return tourPayload(await resolve(args)); },
432
339
  },
433
- async run(args) {
434
- const a = await resolve(args);
435
- const { rel, file } = fileIn(a, args.file);
436
- return {
437
- path: rel,
438
- role: fileFactsLine(rel, a.scan, a.facts),
439
- documentation: fileStaticDoc(rel, a.scan, a.facts),
440
- row: docFileRow(rel, a.scan, a.facts),
441
- imports: a.facts.importsOf[rel] || [],
442
- importedBy: a.facts.importers[rel] || [],
443
- metrics: {
444
- loc: file.loc,
445
- complexity: file.complexity,
446
- exports: (file.exports || []).length,
447
- functions: (file.functions || []).map((fn) => fn.name),
340
+ {
341
+ name: 'onboarder_health',
342
+ description: 'Maintainability report: an overall score and letter grade, what the score is made of, and the '
343
+ + 'riskiest files with their size, complexity, dependency counts and blast radius.',
344
+ inputSchema: {
345
+ type: 'object',
346
+ properties: { path: PATH_PROPERTY, limit: LIMIT_PROPERTY },
347
+ required: [],
448
348
  },
449
- summary: explainFile(rel, file, a.facts),
450
- // Source last, and only when asked for: it is by far the largest part of
451
- // the answer, and a model that wanted the structure does not need it.
452
- ...(args.includeSource ? { source: (await readFileText(a, rel)).text } : {}),
453
- };
454
- },
455
- },
456
-
457
- {
458
- name: 'onboarder_explain_folder',
459
-
460
- description:
461
- 'Explain one folder: what lives in it, which folders it imports, its entry points and hubs, and a '
462
- + 'written summary of what the folder is for. Pass an empty string for the repository root.',
463
- inputSchema: {
464
- type: 'object',
465
- properties: {
466
- path: PATH_PROPERTY,
467
- folder: { type: 'string', description: 'Repository-relative folder, for example server/mcp. Use "" for the root.' },
468
- },
469
- required: ['folder'],
349
+ async run(args) { return healthPayload(await resolve(args), limitOf(args.limit, 20)); },
470
350
  },
471
- async run(args) {
472
- const a = await resolve(args);
473
- const folder = String(args.folder ?? '').trim();
474
- const paths = filesUnder(a, folder);
475
- if (!paths.length && folder) throw new ToolError(`No files under "${folder}".`);
476
-
477
- const parsed = parsedFilesUnder(a, folder);
478
- if (!parsed.length && folder) {
479
- throw new ToolError(`Nothing under "${folder}" was parsed as source — use onboarder_list_files instead.`);
480
- }
481
- // Subfolders are the directories directly inside `folder`, not every
482
- // directory beneath it: a tree two levels down is noise in a summary.
483
- const depth = folder ? folder.split('/').length : 0;
484
- const subfolders = [...new Set(paths
485
- .map((p) => p.split('/').slice(0, -1).join('/'))
486
- .filter((d) => d && d.split('/').length === depth + 1))];
487
-
488
- return {
489
- folder: folder || '.',
490
- fileCount: paths.length,
491
- parsedFileCount: parsed.length,
492
- subfolders: subfolders.slice(0, 40),
493
- files: parsed.map((f) => docFileRow(f.path, a.scan, a.facts)),
494
- // Which folders this one imports, and which import it. Counts are import
495
- // edges, so `server/mcp → shared/analyzer: 6` means six files in here
496
- // reach in there — not six files in there.
497
- ...folderCrossings(a, folder),
498
- documentation: folderStaticDoc(folder, a.scan, a.facts, subfolders.length),
499
- summary: explainFolder(folder, a.scan, a.facts),
500
- };
351
+ {
352
+ name: 'onboarder_security',
353
+ description: 'Static security findings from the built-in rules: hardcoded secrets, unsafe calls, injection '
354
+ + 'patterns and the rest, ordered by severity. These are the built-in rules only — use '
355
+ + 'onboarder_deep_analysis for the external scanners.',
356
+ inputSchema: {
357
+ type: 'object',
358
+ properties: { path: PATH_PROPERTY, limit: LIMIT_PROPERTY },
359
+ required: [],
360
+ },
361
+ async run(args) { return securityPayload(await resolve(args), limitOf(args.limit, 50, 500)); },
501
362
  },
502
- },
503
-
504
- {
505
- name: 'onboarder_read_file',
506
- description:
507
- 'The text of one file inside the repository. Paths outside the repository are refused, and files over '
508
- + '200 KB are refused rather than truncated — use onboarder_search to find the part you need.',
509
- inputSchema: {
510
- type: 'object',
511
- properties: {
512
- path: PATH_PROPERTY,
513
- file: { type: 'string', description: 'Repository-relative path.' },
514
- },
515
- required: ['file'],
363
+ {
364
+ name: 'onboarder_history',
365
+ description: 'What git knows: commit count, contributors, and the files with the highest churn weighted against '
366
+ + 'complexity — the change-risk hotspots, plus files only one person has ever touched and files '
367
+ + 'that keep changing together without importing each other.',
368
+ inputSchema: {
369
+ type: 'object',
370
+ properties: { path: PATH_PROPERTY, limit: LIMIT_PROPERTY },
371
+ required: [],
372
+ },
373
+ async run(args) { return historyPayload(await resolve(args), limitOf(args.limit, 25)); },
516
374
  },
517
- async run(args) {
518
- const a = await resolve(args);
519
- return readFileText(a, args.file);
375
+ {
376
+ name: 'onboarder_dependencies',
377
+ description: 'The dependency inventory: languages, package managers, every declared dependency with version, '
378
+ + 'category and documentation link, the project license with its compliance counts, and the CI '
379
+ + 'workflows with their triggers and jobs.',
380
+ inputSchema: { type: 'object', properties: { path: PATH_PROPERTY }, required: [] },
381
+ async run(args) { return dependenciesPayload(await resolve(args)); },
520
382
  },
521
- },
522
-
523
- {
524
- name: 'onboarder_list_files',
525
- description:
526
- 'List the files in the repository, or in one folder. Filter by a substring of the path, by extension, '
527
- + 'and by whether the file is a test.',
528
- inputSchema: {
529
- type: 'object',
530
- properties: {
531
- path: PATH_PROPERTY,
532
- folder: { type: 'string', description: 'Repository-relative folder. Use "" for the whole repository.' },
533
- contains: { type: 'string', description: 'Keep only paths containing this text.' },
534
- extension: { type: 'string', description: 'Keep only this extension, with or without the dot, e.g. "js".' },
535
- tests: { type: 'boolean', description: 'true keeps only test files, false excludes them.' },
536
- limit: LIMIT_PROPERTY,
537
- },
538
- required: [],
383
+ {
384
+ name: 'onboarder_explain_file',
385
+ description: 'Explain one file: its role in the codebase, what it imports, what imports it, its exports, its '
386
+ + 'metrics, and a written summary. Set includeSource to get the file text as well.',
387
+ inputSchema: {
388
+ type: 'object',
389
+ properties: {
390
+ path: PATH_PROPERTY,
391
+ file: { type: 'string', description: 'Repository-relative path, for example server/router.js.' },
392
+ includeSource: { type: 'boolean', description: 'Also return the full file text. Defaults to false.' },
393
+ },
394
+ required: ['file'],
395
+ },
396
+ async run(args) {
397
+ const a = await resolve(args);
398
+ const { rel, file } = fileIn(a, args.file);
399
+ return {
400
+ path: rel,
401
+ role: fileFactsLine(rel, a.scan, a.facts),
402
+ documentation: fileStaticDoc(rel, a.scan, a.facts),
403
+ row: docFileRow(rel, a.scan, a.facts),
404
+ imports: a.facts.importsOf[rel] || [],
405
+ importedBy: a.facts.importers[rel] || [],
406
+ metrics: {
407
+ loc: file.loc,
408
+ complexity: file.complexity,
409
+ exports: (file.exports || []).length,
410
+ functions: (file.functions || []).map((fn) => fn.name),
411
+ },
412
+ summary: explainFile(rel, file, a.facts),
413
+ // Source last, and only when asked for: it is by far the largest part of
414
+ // the answer, and a model that wanted the structure does not need it.
415
+ ...(args.includeSource ? { source: (await readFileText(a, rel)).text } : {}),
416
+ };
417
+ },
539
418
  },
540
- async run(args) {
541
- const a = await resolve(args);
542
- const limit = limitOf(args.limit, 200);
543
- const folder = String(args.folder ?? '').trim();
544
- const needle = String(args.contains || '').toLowerCase();
545
- const ext = String(args.extension || '').trim().replace(/^\./, '').toLowerCase();
546
- const isTest = (p) => /(^|\/)(tests?|__tests__)\//i.test(p) || /\.(test|spec)\./i.test(p);
547
-
548
- // A folder listing spans subfolders and includes assets: "what is in here"
549
- // is a question about the repository, not about what the parser understood.
550
- let files = filesUnder(a, folder);
551
- if (needle) files = files.filter((p) => p.toLowerCase().includes(needle));
552
- if (ext) files = files.filter((p) => p.toLowerCase().endsWith('.' + ext));
553
- if (args.tests === true) files = files.filter(isTest);
554
- if (args.tests === false) files = files.filter((p) => !isTest(p));
555
-
556
- return {
557
- root: a.root,
558
- folder: folder || '.',
559
- total: files.length,
560
- truncated: files.length > limit,
561
- // `allFiles` rows are sometimes just a path string rather than a parsed
562
- // file object, so both are mapped to their path and nothing else.
563
- files: files.slice(0, limit).map((f) => (typeof f === 'string' ? f : f.path)),
564
- };
419
+ {
420
+ name: 'onboarder_explain_folder',
421
+ description: 'Explain one folder: what lives in it, which folders it imports, its entry points and hubs, and a '
422
+ + 'written summary of what the folder is for. Pass an empty string for the repository root.',
423
+ inputSchema: {
424
+ type: 'object',
425
+ properties: {
426
+ path: PATH_PROPERTY,
427
+ folder: { type: 'string', description: 'Repository-relative folder, for example server/mcp. Use "" for the root.' },
428
+ },
429
+ required: ['folder'],
430
+ },
431
+ async run(args) {
432
+ const a = await resolve(args);
433
+ const folder = String(args.folder ?? '').trim();
434
+ const paths = filesUnder(a, folder);
435
+ if (!paths.length && folder)
436
+ throw new ToolError(`No files under "${folder}".`);
437
+ const parsed = parsedFilesUnder(a, folder);
438
+ if (!parsed.length && folder) {
439
+ throw new ToolError(`Nothing under "${folder}" was parsed as source — use onboarder_list_files instead.`);
440
+ }
441
+ // Subfolders are the directories directly inside `folder`, not every
442
+ // directory beneath it: a tree two levels down is noise in a summary.
443
+ const depth = folder ? folder.split('/').length : 0;
444
+ const subfolders = [...new Set(paths
445
+ .map((p) => p.split('/').slice(0, -1).join('/'))
446
+ .filter((d) => d && d.split('/').length === depth + 1))];
447
+ return {
448
+ folder: folder || '.',
449
+ fileCount: paths.length,
450
+ parsedFileCount: parsed.length,
451
+ subfolders: subfolders.slice(0, 40),
452
+ files: parsed.map((f) => docFileRow(f.path, a.scan, a.facts)),
453
+ // Which folders this one imports, and which import it. Counts are import
454
+ // edges, so `server/mcp → shared/analyzer: 6` means six files in here
455
+ // reach in there — not six files in there.
456
+ ...folderCrossings(a, folder),
457
+ documentation: folderStaticDoc(folder, a.scan, a.facts, subfolders.length),
458
+ summary: explainFolder(folder, a.scan, a.facts),
459
+ };
460
+ },
565
461
  },
566
- },
567
-
568
- {
569
- name: 'onboarder_search',
570
- description:
571
- "Search the repository the way the app's search palette does: bare words rank by relevance, and the "
572
- + 'full query language works too — path:, ext:, is:test, is:source, "exact phrases", -exclude, '
573
- + '/regex/, and case: for case sensitivity.',
574
- inputSchema: {
575
- type: 'object',
576
- properties: {
577
- path: PATH_PROPERTY,
578
- query: { type: 'string', description: 'The search query.' },
579
- limit: LIMIT_PROPERTY,
580
- caseSensitive: { type: 'boolean', description: 'Match case exactly. Defaults to false.' },
581
- },
582
- required: ['query'],
462
+ {
463
+ name: 'onboarder_read_file',
464
+ description: 'The text of one file inside the repository. Paths outside the repository are refused, and files over '
465
+ + '200 KB are refused rather than truncated — use onboarder_search to find the part you need.',
466
+ inputSchema: {
467
+ type: 'object',
468
+ properties: {
469
+ path: PATH_PROPERTY,
470
+ file: { type: 'string', description: 'Repository-relative path.' },
471
+ },
472
+ required: ['file'],
473
+ },
474
+ async run(args) {
475
+ const a = await resolve(args);
476
+ return readFileText(a, args.file);
477
+ },
583
478
  },
584
- async run(args) {
585
- const a = await resolve(args);
586
- const out = searchDocuments(a.searchIndex, String(args.query || ''), {
587
- limit: limitOf(args.limit, 20),
588
- caseSensitive: !!args.caseSensitive,
589
- });
590
- // A regex that would not compile comes back as `error` with zero results.
591
- // Passing that through as-is would tell the agent the code is not there,
592
- // which is the one conclusion it must not draw from a typo in its own
593
- // query — so it is raised as a tool error the model can see and correct.
594
- if (out.error) throw new ToolError(out.error);
595
- return {
596
- query: out.query,
597
- total: out.total,
598
- indexed: out.indexed,
599
- results: out.results.map((r) => ({ path: r.path, line: r.line, snippet: r.snippet })),
600
- };
479
+ {
480
+ name: 'onboarder_list_files',
481
+ description: 'List the files in the repository, or in one folder. Filter by a substring of the path, by extension, '
482
+ + 'and by whether the file is a test.',
483
+ inputSchema: {
484
+ type: 'object',
485
+ properties: {
486
+ path: PATH_PROPERTY,
487
+ folder: { type: 'string', description: 'Repository-relative folder. Use "" for the whole repository.' },
488
+ contains: { type: 'string', description: 'Keep only paths containing this text.' },
489
+ extension: { type: 'string', description: 'Keep only this extension, with or without the dot, e.g. "js".' },
490
+ tests: { type: 'boolean', description: 'true keeps only test files, false excludes them.' },
491
+ limit: LIMIT_PROPERTY,
492
+ },
493
+ required: [],
494
+ },
495
+ async run(args) {
496
+ const a = await resolve(args);
497
+ const limit = limitOf(args.limit, 200);
498
+ const folder = String(args.folder ?? '').trim();
499
+ const needle = String(args.contains || '').toLowerCase();
500
+ const ext = String(args.extension || '').trim().replace(/^\./, '').toLowerCase();
501
+ const isTest = (p) => /(^|\/)(tests?|__tests__)\//i.test(p) || /\.(test|spec)\./i.test(p);
502
+ // A folder listing spans subfolders and includes assets: "what is in here"
503
+ // is a question about the repository, not about what the parser understood.
504
+ let files = filesUnder(a, folder);
505
+ if (needle)
506
+ files = files.filter((p) => p.toLowerCase().includes(needle));
507
+ if (ext)
508
+ files = files.filter((p) => p.toLowerCase().endsWith('.' + ext));
509
+ if (args.tests === true)
510
+ files = files.filter(isTest);
511
+ if (args.tests === false)
512
+ files = files.filter((p) => !isTest(p));
513
+ return {
514
+ root: a.root,
515
+ folder: folder || '.',
516
+ total: files.length,
517
+ truncated: files.length > limit,
518
+ // `allFiles` rows are sometimes just a path string rather than a parsed
519
+ // file object, so both are mapped to their path and nothing else.
520
+ files: files.slice(0, limit).map((f) => (typeof f === 'string' ? f : f.path)),
521
+ };
522
+ },
601
523
  },
602
- },
603
-
604
- {
605
- name: 'onboarder_deep_analysis',
606
- description:
607
- 'Run the optional external analyzers (semgrep, gitleaks, knip, and the rest) over the repository and '
608
- + 'return their findings normalized. Slower than the built-in tools, and it only covers the analyzers '
609
- + 'that are actually installed. Call onboarder_analyzer_status first to see which those are.',
610
- inputSchema: {
611
- type: 'object',
612
- properties: {
613
- path: PATH_PROPERTY,
614
- tools: {
615
- type: 'array',
616
- items: { type: 'string', enum: TOOL_DEFS.map((d) => d.id) },
617
- description: 'Which analyzers to run. Omit to run every installed one.',
524
+ {
525
+ name: 'onboarder_search',
526
+ description: "Search the repository the way the app's search palette does: bare words rank by relevance, and the "
527
+ + 'full query language works too — path:, ext:, is:test, is:source, "exact phrases", -exclude, '
528
+ + '/regex/, and case: for case sensitivity.',
529
+ inputSchema: {
530
+ type: 'object',
531
+ properties: {
532
+ path: PATH_PROPERTY,
533
+ query: { type: 'string', description: 'The search query.' },
534
+ limit: LIMIT_PROPERTY,
535
+ caseSensitive: { type: 'boolean', description: 'Match case exactly. Defaults to false.' },
536
+ },
537
+ required: ['query'],
618
538
  },
619
- kinds: {
620
- type: 'array',
621
- items: { type: 'string', enum: ['security', 'dead-code'] },
622
- description: 'Narrow to a purpose.',
539
+ async run(args) {
540
+ const a = await resolve(args);
541
+ const out = searchDocuments(a.searchIndex, String(args.query || ''), {
542
+ limit: limitOf(args.limit, 20),
543
+ caseSensitive: !!args.caseSensitive,
544
+ });
545
+ // A regex that would not compile comes back as `error` with zero results.
546
+ // Passing that through as-is would tell the agent the code is not there,
547
+ // which is the one conclusion it must not draw from a typo in its own
548
+ // query — so it is raised as a tool error the model can see and correct.
549
+ if (out.error)
550
+ throw new ToolError(out.error);
551
+ return {
552
+ query: out.query,
553
+ total: out.total,
554
+ indexed: out.indexed,
555
+ results: out.results.map((r) => ({ path: r.path, line: r.line, snippet: r.snippet })),
556
+ };
623
557
  },
624
- },
625
- required: [],
626
558
  },
627
- async run(args) {
628
- const a = await resolve(args);
629
- return runExternalAnalysis(a.root, { tools: args.tools, kinds: args.kinds });
559
+ {
560
+ name: 'onboarder_deep_analysis',
561
+ description: 'Run the optional external analyzers (semgrep, gitleaks, knip, and the rest) over the repository and '
562
+ + 'return their findings normalized. Slower than the built-in tools, and it only covers the analyzers '
563
+ + 'that are actually installed. Call onboarder_analyzer_status first to see which those are.',
564
+ inputSchema: {
565
+ type: 'object',
566
+ properties: {
567
+ path: PATH_PROPERTY,
568
+ tools: {
569
+ type: 'array',
570
+ items: { type: 'string', enum: TOOL_DEFS.map((d) => d.id) },
571
+ description: 'Which analyzers to run. Omit to run every installed one.',
572
+ },
573
+ kinds: {
574
+ type: 'array',
575
+ items: { type: 'string', enum: ['security', 'dead-code'] },
576
+ description: 'Narrow to a purpose.',
577
+ },
578
+ },
579
+ required: [],
580
+ },
581
+ async run(args) {
582
+ const a = await resolve(args);
583
+ return runExternalAnalysis(a.root, { tools: args.tools, kinds: args.kinds });
584
+ },
630
585
  },
631
- },
632
-
633
- {
634
- name: 'onboarder_analyzer_status',
635
- description:
636
- 'Which external analyzers are installed on this machine, what each one is for, and how to install the '
637
- + 'ones that are missing. Takes no arguments.',
638
- inputSchema: { type: 'object', properties: {}, required: [] },
639
- async run() {
640
- // `toolsStatus` is keyed by id — the shape the engines panel and the HTTP
641
- // route both expect. It becomes a list here so an agent does not have to
642
- // know that, but the same fields survive, so this and `/api/tools` cannot
643
- // disagree about whether semgrep is installed.
644
- return { analyzers: Object.values(toolsStatus()) };
586
+ {
587
+ name: 'onboarder_analyzer_status',
588
+ description: 'Which external analyzers are installed on this machine, what each one is for, and how to install the '
589
+ + 'ones that are missing. Takes no arguments.',
590
+ inputSchema: { type: 'object', properties: {}, required: [] },
591
+ async run() {
592
+ // `toolsStatus` is keyed by id — the shape the engines panel and the HTTP
593
+ // route both expect. It becomes a list here so an agent does not have to
594
+ // know that, but the same fields survive, so this and `/api/tools` cannot
595
+ // disagree about whether semgrep is installed.
596
+ return { analyzers: Object.values(toolsStatus()) };
597
+ },
645
598
  },
646
- },
647
599
  ];
648
-
649
600
  const BY_NAME = new Map(TOOLS.map((t) => [t.name, t]));
650
-
651
601
  // The MCP wire shape, which is not the same as the internal one: the handler
652
602
  // stays off the table, and the schema is named `inputSchema` exactly as the spec
653
603
  // requires.
654
604
  export function toolDefinitions() {
655
- return TOOLS.map((t) => ({
656
- name: t.name,
657
- description: t.description,
658
- inputSchema: t.inputSchema,
659
- }));
605
+ return TOOLS.map((t) => ({
606
+ name: t.name,
607
+ description: t.description,
608
+ inputSchema: t.inputSchema,
609
+ }));
660
610
  }
661
-
662
611
  export function findTool(name) {
663
- return BY_NAME.get(name) || null;
612
+ return BY_NAME.get(name) || null;
664
613
  }
665
-
666
614
  export async function callTool(name, args) {
667
- const tool = findTool(name);
668
- if (!tool) throw new ToolError(`No tool called "${name}".`);
669
- return await tool.run(args || {});
615
+ const tool = findTool(name);
616
+ if (!tool)
617
+ throw new ToolError(`No tool called "${name}".`);
618
+ return await tool.run(args || {});
670
619
  }
671
-
672
620
  // What the top-bar button polls. The runner fills in `running`, `pid` and the
673
621
  // rest; this is the part that is the same either way.
674
622
  export function mcpStatus() {
675
- return {
676
- transport: 'stdio',
677
- toolCount: TOOLS.length,
678
- tools: TOOLS.map((t) => t.name),
679
- cachedRepositories: cachedRoots(),
680
- lastRoot,
681
- analyzers: toolsStatus(),
682
- };
623
+ return {
624
+ transport: 'stdio',
625
+ toolCount: TOOLS.length,
626
+ tools: TOOLS.map((t) => t.name),
627
+ cachedRepositories: cachedRoots(),
628
+ lastRoot,
629
+ analyzers: toolsStatus(),
630
+ };
683
631
  }
632
+ //# sourceMappingURL=tools.js.map