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
@@ -0,0 +1 @@
1
+ {"version":3,"file":"llm.js","sourceRoot":"","sources":["../../src/public/js/llm.ts"],"names":[],"mappings":"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","sourcesContent":["// LLM settings and prompt assembly. The key lives in localStorage and only\n// ever travels to the local server, which forwards it to whatever\n// OpenAI-compatible endpoint the user pointed at.\n\nconst STORE_KEY = 'onboarder.llm.v1';\n\n// No endpoint, model, or key ships with the repo. Anything saved in the\n// API-key drawer overrides these empty defaults (localStorage wins); without\n// a saved endpoint the app stays on the offline graph explanations.\nexport const DEFAULT_SETTINGS = {\n baseUrl: '',\n apiKey: '',\n model: '',\n};\n\nexport function getSettings() {\n try {\n return { ...DEFAULT_SETTINGS, ...JSON.parse(localStorage.getItem(STORE_KEY) || '{}') };\n } catch {\n return { ...DEFAULT_SETTINGS };\n }\n}\n\nexport function saveSettings(settings) {\n localStorage.setItem(STORE_KEY, JSON.stringify({\n baseUrl: (settings.baseUrl || '').trim().replace(/\\/+$/, ''),\n apiKey: settings.apiKey || '',\n model: (settings.model || '').trim(),\n }));\n}\n\nexport function isConfigured() {\n const s = getSettings();\n return Boolean(s.baseUrl && s.model);\n}\n\n// Azure OpenAI speaks a slightly different dialect than the rest: api-key\n// header (handled server-side) and no `reasoning` request parameter.\nexport function isAzureHost(baseUrl) {\n return /\\.openai\\.azure\\.com|\\.cognitiveservices\\.azure\\.com/i.test(baseUrl || '');\n}\n\nconst SYSTEM_PROMPT = [\n 'You are a senior engineer walking a new teammate through a codebase they have never seen.',\n 'Write like a person, not a product page: plain sentences, concrete names, no hype, no bullet-point cosplay unless a list is genuinely clearer.',\n 'Keep it under 180 words. Refer to files and functions by their real names in backticks.',\n 'If something looks risky to change, say so plainly.',\n].join(' ');\n\nexport function fileMessages({ repoName, overview, path, role, fanIn, fanOut, importers, imports, functions, exports_, source }) {\n const context = [\n `Repository: ${repoName}`,\n overview ? `Big picture: ${overview}` : '',\n '',\n `File: ${path} (role: ${role})`,\n `Imported by ${fanIn} files: ${importers.slice(0, 8).join(', ') || 'none'}`,\n `Imports ${fanOut} files: ${imports.slice(0, 8).join(', ') || 'none'}`,\n functions?.length ? `Functions: ${functions.slice(0, 14).join(', ')}` : '',\n exports_?.length ? `Exports: ${exports_.slice(0, 10).join(', ')}` : '',\n ].filter(Boolean).join('\\n');\n\n const user = source\n ? `${context}\\n\\nHere is the source, possibly truncated:\\n\\`\\`\\`\\n${source.slice(0, 12000)}\\n\\`\\`\\`\\n\\nExplain what this file does, how it fits into the repo, and what breaks if it is deleted.`\n : `${context}\\n\\n(Source was not available.) Explain what this file most likely does, how it fits into the repo, and what breaks if it is deleted. Say what you are inferring versus what the graph proves.`;\n\n return [\n { role: 'system', content: SYSTEM_PROMPT },\n { role: 'user', content: user },\n ];\n}\n\nexport function overviewMessages({ repoName, overview, entries, hubs, services, cycles, orphans, languages }) {\n const user = [\n `Repository: ${repoName}`,\n `Languages: ${languages}`,\n overview ? `Static notes: ${overview}` : '',\n entries?.length ? `Entry points: ${entries.slice(0, 5).join(', ')}` : '',\n hubs?.length ? `Most-depended-on files: ${hubs.slice(0, 5).map((h) => h.path + ' (' + h.fanIn + ')').join(', ')}` : '',\n services?.length ? `Services: ${services.map((s) => s.name).join(', ')}` : '',\n cycles?.length ? `${cycles.length} circular dependencies` : '',\n orphans?.length ? `${orphans.length} files imported by nothing` : '',\n '',\n 'Give a new teammate the orientation you would give on their first day: what this thing is, where to start reading, and which files to treat with respect.',\n ].filter(Boolean).join('\\n');\n\n return [\n { role: 'system', content: SYSTEM_PROMPT },\n { role: 'user', content: user },\n ];\n}\n\n// Diagram generation prompts. The model gets compact graph facts and returns\n// a Mermaid flowchart with a caption; the theme is injected by us afterwards\n// so AI drafts look exactly like the hand-drawn ones.\n\nconst DIAGRAM_PROMPT = [\n 'You turn codebase facts into Mermaid flowcharts that teach.',\n 'Output format, exactly: first line `%% caption: <one honest sentence about what the diagram shows>`, then the flowchart source. Nothing else — no fences, no commentary, no sign-off.',\n 'Rules: start with `flowchart LR` or `flowchart TD` on its own line; use short alphanumeric ids (a1, b2); put every label in \"double quotes\"; at most 36 nodes; group with subgraphs where it helps; A --> B means A depends on B; no click, style or linkStyle statements.',\n 'Where you can infer what a group of files is FOR, name the cluster by its purpose instead of repeating folder names.',\n].join(' ');\n\nexport function diagramMessages({ repoName, kind, facts }) {\n const user = [\n `Repository: ${repoName}`,\n `Diagram to draw: ${kind}`,\n 'Facts:',\n facts.join('\\n'),\n '',\n `Draw the most useful ${kind} for someone who has never seen this codebase.`,\n ].join('\\n');\n\n return [\n { role: 'system', content: DIAGRAM_PROMPT },\n { role: 'user', content: user },\n ];\n}\n\n// ---- documentation prompts -------------------------------------------------\n\nconst DOC_SYSTEM = [\n 'You write documentation for a codebase as a senior engineer would: concrete, calm, specific.',\n 'Plain sentences. Real names. No hype, no filler, no \"this file plays a crucial role\".',\n].join(' ');\n\n// Detailed reference notes for one file or folder, opened as a tab in Docs.\nconst FILE_DOC_SYSTEM = [\n 'You write detailed reference notes for one file in a codebase, as a senior engineer would for a new teammate.',\n 'Concrete and specific: real names, real numbers, no filler. Answer in Markdown with exactly these sections, in this order:',\n '## Purpose',\n '## How it fits',\n '## Key parts',\n '## Handle with care',\n 'Keep \"Key parts\" a short bullet list of the functions or exports that matter. Under 350 words total. If a section honestly has nothing to say, one line saying so.',\n].join('\\n');\n\nconst FOLDER_DOC_SYSTEM = [\n 'You write detailed reference notes for one folder in a codebase, as a senior engineer would for a new teammate.',\n 'Concrete and specific: real names, no filler. Answer in Markdown with exactly these sections, in this order:',\n '## Purpose',\n '## How the parts cooperate',\n '## The files that matter',\n '## Handle with care',\n 'Under 300 words total. Say what you infer from names and import facts versus what you know.',\n].join('\\n');\n\nexport function fileDocMessages({ repoName, overview, file }) {\n const blocks = [\n `Repository: ${repoName}`,\n overview ? `Big picture: ${overview}` : '',\n `File: ${file.path} (role: ${file.role})`,\n `Imported by ${file.fanIn} files: ${file.importers.slice(0, 8).join(', ') || 'none'}`,\n `Imports ${file.fanOut} files: ${file.imports.slice(0, 8).join(', ') || 'none'}`,\n file.functions?.length ? `Functions: ${file.functions.slice(0, 14).join(', ')}` : '',\n file.exports_?.length ? `Exports: ${file.exports_.slice(0, 10).join(', ')}` : '',\n file.source\n ? `Source, possibly truncated:\\n\\`\\`\\`\\n${file.source.slice(0, 12000)}\\n\\`\\`\\``\n : '(Source unavailable — write from the graph facts and say what you are inferring.)',\n ].filter(Boolean);\n return [\n { role: 'system', content: FILE_DOC_SYSTEM },\n { role: 'user', content: blocks.join('\\n') },\n ];\n}\n\nexport function folderDocDetailedMessages({ repoName, folder, subfolders, files }) {\n const lines = files.map((f) =>\n `- ${f.name} — ${f.fanIn} dependents, pulls in ${f.fanOut}` +\n (f.role !== 'module' ? `, role: ${f.role}` : '') +\n (f.functions.length ? `, functions: ${f.functions.slice(0, 5).join(', ')}` : '')\n );\n const user = [\n `Repository: ${repoName}`,\n `Folder: ${folder || '(repo root)'}`,\n subfolders?.length ? `Subfolders: ${subfolders.join(', ')}` : '',\n 'Files with import-graph facts:',\n lines.join('\\n') || '(no files directly here)',\n ].filter(Boolean).join('\\n');\n return [\n { role: 'system', content: FOLDER_DOC_SYSTEM },\n { role: 'user', content: user },\n ];\n}\n\nexport function repoDocMessages({ repoName, overview, readmeExcerpt, entries, hubs }) {\n const user = [\n `Repository: ${repoName}`,\n overview ? `Static analysis notes: ${overview}` : '',\n entries?.length ? `Entry points: ${entries.slice(0, 5).join(', ')}` : '',\n hubs?.length ? `Most depended-on files: ${hubs.slice(0, 5).map((h) => h.path + ' (' + h.fanIn + ')').join(', ')}` : '',\n readmeExcerpt ? `The project's own README begins:\\n\"\"\"\\n${readmeExcerpt.slice(0, 1500)}\\n\"\"\"` : '(No README found.)',\n '',\n 'Write 3–4 sentences of high-level overview for a brand-new teammate: what this project is, how it is organized at the top level, and where to start reading. Plain prose only — no headings, no bullets.',\n ].filter(Boolean).join('\\n');\n\n return [\n { role: 'system', content: DOC_SYSTEM },\n { role: 'user', content: user },\n ];\n}\n\nexport function folderDocMessages({ repoName, folder, subfolders, files }) {\n const fileLines = files.map((f) => {\n if (!f.parsed) return `- ${f.name} — not parsed (asset or data)`;\n const bits = [`${f.fanIn} dependents`, `pulls in ${f.fanOut}`];\n if (f.role !== 'module') bits.push('role: ' + f.role);\n if (f.functions.length) bits.push('functions: ' + f.functions.slice(0, 5).join(', '));\n return `- ${f.name} — ${bits.join(', ')}`;\n });\n\n const user = [\n `Repository: ${repoName}`,\n `Folder under review: ${folder || '(repo root)'}`,\n subfolders?.length ? `Subfolders inside it: ${subfolders.join(', ')}` : '',\n 'Files (with import-graph facts):',\n fileLines.join('\\n') || '(no files directly here)',\n '',\n 'Write complete documentation for this folder, in exactly this format, no preamble, no fences:',\n 'FOLDER: <3-5 sentences — what lives here, why it exists, how the parts cooperate, and what a newcomer should read first>',\n ...files.filter((f) => f.parsed).map((f) => `FILE: ${f.name}: <2-3 sentences — what this file is for, who it talks to, and what to handle with care>`),\n ].filter(Boolean).join('\\n');\n\n return [\n { role: 'system', content: DOC_SYSTEM },\n { role: 'user', content: user },\n ];\n}\n\nconst QUESTION_PROMPT = [\n 'You are a senior engineer answering a teammate\\'s question about a codebase.',\n 'Answer directly and concretely, citing real file and function names in backticks.',\n 'If the answer is not in the graph facts or source you were given, say so plainly and name the file you would open next to find out.',\n 'Keep it under 200 words unless the question genuinely needs more.',\n].join(' ');\n\n// A free-form question. When the user has a file selected, its facts and\n// source ride along; otherwise the model reasons from the repo overview.\nexport function questionMessages({ repoName, overview, question, file }) {\n const blocks = [\n `Repository: ${repoName}`,\n overview ? `Big picture: ${overview}` : '',\n ];\n\n if (file) {\n blocks.push(\n `The user is currently looking at: ${file.path} (role: ${file.role})`,\n `Imported by ${file.fanIn} files: ${file.importers.slice(0, 8).join(', ') || 'none'}`,\n `Imports ${file.fanOut} files: ${file.imports.slice(0, 8).join(', ') || 'none'}`\n );\n if (file.functions?.length) blocks.push(`Functions: ${file.functions.slice(0, 14).join(', ')}`);\n if (file.exports_?.length) blocks.push(`Exports: ${file.exports_.slice(0, 10).join(', ')}`);\n if (file.source) {\n blocks.push(`Source of ${file.path}, possibly truncated:\\n\\`\\`\\`\\n${file.source.slice(0, 12000)}\\n\\`\\`\\``);\n } else {\n blocks.push('(Source for that file was not available — answer from the graph facts and say so.)');\n }\n }\n\n blocks.push(`Question: ${question}`);\n\n return [\n { role: 'system', content: QUESTION_PROMPT },\n { role: 'user', content: blocks.filter(Boolean).join('\\n') },\n ];\n}\n\n\n// Used by the About tab to summarize a detected framework/library: the model\n// reasons from the official docs excerpt (when fetched) plus the repo's own\n// context. docsText may be empty — the prompt says so and falls back to\n// general knowledge.\nexport function stackSummaryMessages({ repoName, item, docsText, repoNote }) {\n const system =\n 'You are a senior developer writing compact field notes for a codebase onboarding tool. Be specific and honest; distinguish between what the docs state and what you infer. No headings, no \"Sure\" / \"Absolutely\" openers, no filler.';\n const declared = item.version\n ? `The project declares ${item.name} ${item.version} (${item.category}, ${item.lang}${item.dev ? ', dev dependency' : ''}).`\n : `The project uses ${item.name} (${item.category}, ${item.lang}).`;\n const user = [\n `Repository: ${repoName}.`,\n declared,\n `Repo context: ${repoNote}`,\n docsText\n ? `Official docs excerpt:\\n${docsText}`\n : 'No official docs could be fetched for this dependency — rely on your own knowledge and say so in one short clause.',\n 'In 2-4 sentences: what this is, why the project likely chose it, and anything a new teammate should know about using it here.',\n ].join('\\n\\n');\n return [{ role: 'system', content: system }, { role: 'user', content: user }];\n}\n\n// The Deep Analysis report, read back by the model. Two jobs in one builder: a\n// report is either *explained* (no question — prioritize what to fix) or asked\n// about (\"is the eval finding actually reachable?\"). The engine list is part of\n// the context on purpose: what the engines could not see is often the real\n// answer, and a model that does not know a tool was missing will invent\n// confidence it has not earned.\nconst ANALYSIS_SYSTEM = [\n 'You are a senior engineer walking the owner of a codebase through a static-analysis report.',\n 'Be concrete and calm. Name real files, rules and line numbers in backticks.',\n 'Prioritize: what must be fixed now, what can wait, and what is noise. Say plainly when a finding looks like a false positive and why.',\n 'Never invent a finding that is not in the list you were given. If the list is empty, say so — and say what that does and does not prove.',\n 'If some engines did not run or are not installed, say what that leaves unchecked.',\n 'Keep it under 220 words unless the question genuinely needs more.',\n].join(' ');\n\nexport function analysisMessages({ repoName, grade, score, counts, engines, findings, question }) {\n const engineLines = (engines || []).map((e) => {\n if (e.ran) return `- ${e.label}: ran, ${e.findings} finding(s) in ${e.ms}ms`;\n if (e.failed) return `- ${e.label}: ran but failed — ${e.failure || 'no reason given'}`;\n if (e.available) return `- ${e.label}: installed but not run`;\n return `- ${e.label}: NOT INSTALLED (${e.reason || 'unknown'})`;\n });\n\n const findingLines = (findings || []).slice(0, 40).map(\n (f) => `- [${f.severity}] ${f.path}:${f.line} — ${f.message} (${f.tool} · ${f.rule})`,\n );\n\n const user = [\n `Repository: ${repoName}`,\n grade ? `Built-in security grade: ${grade} (${score}/100)` : '',\n counts\n ? `Findings: ${counts.total} total — ${counts.critical} critical, ${counts.high} high, ${counts.medium} medium, ${counts.low} low. ${counts.external} came from external engines, ${counts.builtin} from the built-in scanner.`\n : '',\n engineLines.length ? `Engines:\\n${engineLines.join('\\n')}` : '',\n findingLines.length\n ? `Findings, worst first:\\n${findingLines.join('\\n')}`\n : 'No findings were reported by anything.',\n question\n ? `Question: ${question}`\n : 'Explain this report: what matters most, what to fix first, and what the engines could not see.',\n ].filter(Boolean).join('\\n\\n');\n\n return [\n { role: 'system', content: ANALYSIS_SYSTEM },\n { role: 'user', content: user },\n ];\n}\n\nexport async function streamTokens(container, text) {\n container.innerHTML = '';\n for (let i = 0; i < text.length; i++) {\n container.innerHTML += text[i];\n await new Promise(r => setTimeout(r, 10));\n }\n}\n\n"]}
@@ -0,0 +1,3 @@
1
+ export declare function initMap(options: any): void;
2
+ export declare function renderMap(fit: any): void;
3
+ export declare function fileSummary(role: any, fanIn: any, fanOut: any, file: any): string;
@@ -10,150 +10,151 @@
10
10
  // the reader is looking rather than jump. And clicking a file "peeks" — it fills
11
11
  // the inspector and leaves the map where it is — while clicking a folder name
12
12
  // navigates away to the Files view. Both are deliberate.
13
-
14
13
  import { roleOf, factIndex } from '/shared/analyzer/graph.js';
15
14
  import { pathFilter } from '/shared/analyzer/pathUtil.js';
16
15
  import { buildSpecs, layoutMindMap, renderMindMap } from './mindmap.js';
17
16
  import { pruneTree } from './tree.js';
18
17
  import { state, focusFile, focusFolder } from './state.js';
19
-
20
18
  // Opening every folder at once on a big repo lays out thousands of cells in one
21
19
  // frame. Past this many files the button refuses and says why.
22
20
  const OPEN_ALL_LIMIT = 700;
23
-
24
- let host = null; // #canvasContent
21
+ let host = null; // #canvasContent
25
22
  let toggleBtn = null; // #mmToggleBtn — "Open folders" / "Close all"
26
23
  let hooks = {
27
- onSyncInspector: () => {},
28
- onRenderSidebar: () => {},
29
- onOpenFile: () => {},
30
- onOpenFolder: () => {},
31
- onToast: () => {},
32
- onFit: () => {},
24
+ onSyncInspector: (..._args) => { },
25
+ onRenderSidebar: (..._args) => { },
26
+ onOpenFile: (..._args) => { },
27
+ onOpenFolder: (..._args) => { },
28
+ onToast: (..._args) => { },
29
+ onFit: (..._args) => { },
33
30
  };
34
-
35
31
  export function initMap(options) {
36
- host = options.host;
37
- toggleBtn = options.toggleBtn;
38
- hooks = { ...hooks, ...options };
39
-
40
- toggleBtn.addEventListener('click', () => {
41
- if (state.mm.expanded.size > 1) {
42
- state.mm.expanded = new Set(['dir:']);
43
- renderMap(true);
44
- return;
45
- }
46
- if (state.scan.allFiles.length > OPEN_ALL_LIMIT) {
47
- return hooks.onToast('Too many files to open at once — open folders as you go.');
48
- }
49
- const open = (node) => {
50
- state.mm.expanded.add('dir:' + node.path);
51
- for (const d of node.dirs.values()) open(d);
52
- };
53
- open(state.treeData);
54
- renderMap(true);
55
- hooks.onToast('Every folder is open. Files stay shut until you click them.');
56
- });
32
+ host = options.host;
33
+ toggleBtn = options.toggleBtn;
34
+ hooks = { ...hooks, ...options };
35
+ toggleBtn.addEventListener('click', () => {
36
+ if (state.mm.expanded.size > 1) {
37
+ state.mm.expanded = new Set(['dir:']);
38
+ renderMap(true);
39
+ return;
40
+ }
41
+ if (state.scan.allFiles.length > OPEN_ALL_LIMIT) {
42
+ return hooks.onToast('Too many files to open at once — open folders as you go.');
43
+ }
44
+ const open = (node) => {
45
+ state.mm.expanded.add('dir:' + node.path);
46
+ for (const d of node.dirs.values())
47
+ open(d);
48
+ };
49
+ open(state.treeData);
50
+ renderMap(true);
51
+ hooks.onToast('Every folder is open. Files stay shut until you click them.');
52
+ });
57
53
  }
58
-
59
54
  // The filter as the map needs it: a folder whose own name matches keeps its whole
60
55
  // branch, so searching for a folder shows you what is in it.
61
56
  function visibleTree() {
62
- const include = pathFilter(state.filters);
63
- const needle = state.filters.text.trim().toLowerCase();
64
- const matchesFolder = needle ? (p) => p.toLowerCase().includes(needle) : null;
65
- return pruneTree(state.treeData, include, matchesFolder);
57
+ const include = pathFilter(state.filters);
58
+ const needle = state.filters.text.trim().toLowerCase();
59
+ const matchesFolder = needle ? (p) => p.toLowerCase().includes(needle) : null;
60
+ return pruneTree(state.treeData, include, matchesFolder);
66
61
  }
67
-
68
62
  export function renderMap(fit) {
69
- const { scan, facts } = state;
70
- const fileByPath = new Map(scan.files.map((f) => [f.path, f]));
71
-
72
- const rootSpec = buildSpecs({
73
- root: visibleTree(),
74
- expanded: state.mm.expanded,
75
- rootSummary: {
76
- name: scan.name,
77
- summary: `${scan.stats.filesParsed} code files · ${scan.stats.edgeCount} connections`,
78
- },
79
- folderInfo: (node) => {
80
- let summary = `${node.files.length} ${node.files.length === 1 ? 'file' : 'files'}`;
81
- if (node.dirs.size) summary += ` · ${node.dirs.size} ${node.dirs.size === 1 ? 'folder' : 'folders'}`;
82
- return { summary, cls: '' };
83
- },
84
- fileInfo: (path) => {
85
- const f = fileByPath.get(path);
86
- // A path with no scan entry was walked but never parsed — listed, greyed,
87
- // and given no neighbours rather than left out of the map entirely.
88
- if (!f) return { parsed: false, summary: 'not parsed', neighbors: [] };
89
- const fin = facts.fanIn[path] || 0;
90
- const fout = facts.fanOut[path] || 0;
91
- const role = roleOf(path, facts);
92
- const cls = [
93
- role === 'entry' ? 'is-entry' : '',
94
- role === 'hub' ? 'is-hub' : '',
95
- factIndex(facts).inCycle.has(path) ? 'is-cycle' : '',
96
- ].filter(Boolean).join(' ');
97
- return {
98
- parsed: true,
99
- summary: fileSummary(role, fin, fout, f),
100
- cls,
101
- neighbors: [
102
- ...(facts.importsOf[path] || []).map((p) => ({ path: p, relation: 'pulled in' })),
103
- ...(facts.importers[path] || []).map((p) => ({ path: p, relation: 'leans on it' })),
104
- ],
105
- };
106
- },
107
- });
108
-
109
- const layout = layoutMindMap(rootSpec, state.mm.expanded);
110
- renderMindMap(host, layout, state.mm.expanded, {
111
- onToggle: (cell) => {
112
- if (state.mm.expanded.has(cell.id)) state.mm.expanded.delete(cell.id);
113
- else state.mm.expanded.add(cell.id);
114
- renderMap(false); // keep the pan position — the map grows around you
115
- peek(cell);
116
- },
117
- onNavigate: (cell) => {
118
- if (cell.navFolder) hooks.onOpenFolder(cell.navFolder);
119
- else if (cell.kind === 'ghost' || cell.kind === 'more') {
120
- // A ghost stands for a file outside the current branch; there is nothing
121
- // to expand, so the only sensible click is to go and open it.
122
- if (cell.nav) hooks.onOpenFile(cell.nav);
123
- } else if (cell.nav) peek(cell);
124
- },
125
- });
126
-
127
- // One button, two duties: it says what a click will do next.
128
- toggleBtn.textContent = state.mm.expanded.size > 1 ? 'Close all' : 'Open folders';
129
- if (fit) hooks.onFit();
63
+ const { scan, facts } = state;
64
+ const fileByPath = new Map(scan.files.map((f) => [f.path, f]));
65
+ const rootSpec = buildSpecs({
66
+ root: visibleTree(),
67
+ expanded: state.mm.expanded,
68
+ rootSummary: {
69
+ name: scan.name,
70
+ summary: `${scan.stats.filesParsed} code files · ${scan.stats.edgeCount} connections`,
71
+ },
72
+ folderInfo: (node) => {
73
+ let summary = `${node.files.length} ${node.files.length === 1 ? 'file' : 'files'}`;
74
+ if (node.dirs.size)
75
+ summary += ` · ${node.dirs.size} ${node.dirs.size === 1 ? 'folder' : 'folders'}`;
76
+ return { summary, cls: '' };
77
+ },
78
+ fileInfo: (path) => {
79
+ const f = fileByPath.get(path);
80
+ // A path with no scan entry was walked but never parsed — listed, greyed,
81
+ // and given no neighbours rather than left out of the map entirely.
82
+ if (!f)
83
+ return { parsed: false, summary: 'not parsed', neighbors: [] };
84
+ const fin = facts.fanIn[path] || 0;
85
+ const fout = facts.fanOut[path] || 0;
86
+ const role = roleOf(path, facts);
87
+ const cls = [
88
+ role === 'entry' ? 'is-entry' : '',
89
+ role === 'hub' ? 'is-hub' : '',
90
+ factIndex(facts).inCycle.has(path) ? 'is-cycle' : '',
91
+ ].filter(Boolean).join(' ');
92
+ return {
93
+ parsed: true,
94
+ summary: fileSummary(role, fin, fout, f),
95
+ cls,
96
+ neighbors: [
97
+ ...(facts.importsOf[path] || []).map((p) => ({ path: p, relation: 'pulled in' })),
98
+ ...(facts.importers[path] || []).map((p) => ({ path: p, relation: 'leans on it' })),
99
+ ],
100
+ };
101
+ },
102
+ });
103
+ const layout = layoutMindMap(rootSpec, state.mm.expanded);
104
+ renderMindMap(host, layout, state.mm.expanded, {
105
+ onToggle: (cell) => {
106
+ if (state.mm.expanded.has(cell.id))
107
+ state.mm.expanded.delete(cell.id);
108
+ else
109
+ state.mm.expanded.add(cell.id);
110
+ renderMap(false); // keep the pan position — the map grows around you
111
+ peek(cell);
112
+ },
113
+ onNavigate: (cell) => {
114
+ if (cell.navFolder)
115
+ hooks.onOpenFolder(cell.navFolder);
116
+ else if (cell.kind === 'ghost' || cell.kind === 'more') {
117
+ // A ghost stands for a file outside the current branch; there is nothing
118
+ // to expand, so the only sensible click is to go and open it.
119
+ if (cell.nav)
120
+ hooks.onOpenFile(cell.nav);
121
+ }
122
+ else if (cell.nav)
123
+ peek(cell);
124
+ },
125
+ });
126
+ // One button, two duties: it says what a click will do next.
127
+ toggleBtn.textContent = state.mm.expanded.size > 1 ? 'Close all' : 'Open folders';
128
+ if (fit)
129
+ hooks.onFit();
130
130
  }
131
-
132
131
  // Fills the inspector for a cell without leaving the map.
133
132
  function peek(cell) {
134
- if (cell.kind === 'folder' || cell.kind === 'root') {
135
- focusFolder(cell.id.slice(4)); // strip the `dir:` prefix
136
- hooks.onSyncInspector();
137
- } else if (cell.nav) {
138
- focusFile(cell.nav);
139
- hooks.onSyncInspector();
140
- hooks.onRenderSidebar();
141
- }
133
+ if (cell.kind === 'folder' || cell.kind === 'root') {
134
+ focusFolder(cell.id.slice(4)); // strip the `dir:` prefix
135
+ hooks.onSyncInspector();
136
+ }
137
+ else if (cell.nav) {
138
+ focusFile(cell.nav);
139
+ hooks.onSyncInspector();
140
+ hooks.onRenderSidebar();
141
+ }
142
142
  }
143
-
144
143
  // The line under a file's name. Each role gets the number that matters for it:
145
144
  // an entry point is interesting for what it pulls in, a hub for what leans on it.
146
145
  export function fileSummary(role, fanIn, fanOut, file) {
147
- switch (role) {
148
- case 'entry': return `entry point · pulls in ${fanOut}`;
149
- case 'hub': return `hub · ${fanIn} dependents`;
150
- case 'leaf': return `leaf · ${fanIn} dependent${fanIn === 1 ? '' : 's'}`;
151
- case 'test': return 'test file';
152
- case 'config': return 'configuration';
153
- default: {
154
- let s = `${fanOut} out · ${fanIn} in`;
155
- if (file.functions.length) s += ` · ${file.functions.length} fn`;
156
- return s;
146
+ switch (role) {
147
+ case 'entry': return `entry point · pulls in ${fanOut}`;
148
+ case 'hub': return `hub · ${fanIn} dependents`;
149
+ case 'leaf': return `leaf · ${fanIn} dependent${fanIn === 1 ? '' : 's'}`;
150
+ case 'test': return 'test file';
151
+ case 'config': return 'configuration';
152
+ default: {
153
+ let s = `${fanOut} out · ${fanIn} in`;
154
+ if (file.functions.length)
155
+ s += ` · ${file.functions.length} fn`;
156
+ return s;
157
+ }
157
158
  }
158
- }
159
159
  }
160
+ //# sourceMappingURL=mapView.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mapView.js","sourceRoot":"","sources":["../../src/public/js/mapView.ts"],"names":[],"mappings":"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","sourcesContent":["// The Map view: the repo as an expanding tree map rather than a flowchart.\n//\n// This is the landing view after a scan, and the only one that is not Mermaid.\n// Cells are laid out by mindmap.js from a spec built here — the spec is where the\n// graph's numbers become the one-line summaries under each name, which is the\n// whole value of the view and the reason this glue is worth its own module.\n//\n// Two behaviours worth knowing before changing anything. Expanding a folder\n// re-renders *without* re-fitting, because the map is meant to grow around where\n// the reader is looking rather than jump. And clicking a file \"peeks\" — it fills\n// the inspector and leaves the map where it is — while clicking a folder name\n// navigates away to the Files view. Both are deliberate.\n\nimport { roleOf, factIndex } from '/shared/analyzer/graph.js';\nimport { pathFilter } from '/shared/analyzer/pathUtil.js';\nimport { buildSpecs, layoutMindMap, renderMindMap } from './mindmap.js';\nimport { pruneTree } from './tree.js';\nimport { state, focusFile, focusFolder } from './state.js';\n\n// Opening every folder at once on a big repo lays out thousands of cells in one\n// frame. Past this many files the button refuses and says why.\nconst OPEN_ALL_LIMIT = 700;\n\nlet host = null; // #canvasContent\nlet toggleBtn = null; // #mmToggleBtn — \"Open folders\" / \"Close all\"\nlet hooks = {\n onSyncInspector: (..._args: unknown[]) => {},\n onRenderSidebar: (..._args: unknown[]) => {},\n onOpenFile: (..._args: unknown[]) => {},\n onOpenFolder: (..._args: unknown[]) => {},\n onToast: (..._args: unknown[]) => {},\n onFit: (..._args: unknown[]) => {},\n};\n\nexport function initMap(options) {\n host = options.host;\n toggleBtn = options.toggleBtn;\n hooks = { ...hooks, ...options };\n\n toggleBtn.addEventListener('click', () => {\n if (state.mm.expanded.size > 1) {\n state.mm.expanded = new Set(['dir:']);\n renderMap(true);\n return;\n }\n if (state.scan.allFiles.length > OPEN_ALL_LIMIT) {\n return hooks.onToast('Too many files to open at once — open folders as you go.');\n }\n const open = (node) => {\n state.mm.expanded.add('dir:' + node.path);\n for (const d of node.dirs.values()) open(d);\n };\n open(state.treeData);\n renderMap(true);\n hooks.onToast('Every folder is open. Files stay shut until you click them.');\n });\n}\n\n// The filter as the map needs it: a folder whose own name matches keeps its whole\n// branch, so searching for a folder shows you what is in it.\nfunction visibleTree() {\n const include = pathFilter(state.filters);\n const needle = state.filters.text.trim().toLowerCase();\n const matchesFolder = needle ? (p) => p.toLowerCase().includes(needle) : null;\n return pruneTree(state.treeData, include, matchesFolder);\n}\n\nexport function renderMap(fit) {\n const { scan, facts } = state;\n const fileByPath = new Map(scan.files.map((f) => [f.path, f]));\n\n const rootSpec = buildSpecs({\n root: visibleTree(),\n expanded: state.mm.expanded,\n rootSummary: {\n name: scan.name,\n summary: `${scan.stats.filesParsed} code files · ${scan.stats.edgeCount} connections`,\n },\n folderInfo: (node) => {\n let summary = `${node.files.length} ${node.files.length === 1 ? 'file' : 'files'}`;\n if (node.dirs.size) summary += ` · ${node.dirs.size} ${node.dirs.size === 1 ? 'folder' : 'folders'}`;\n return { summary, cls: '' };\n },\n fileInfo: (path) => {\n const f = fileByPath.get(path);\n // A path with no scan entry was walked but never parsed — listed, greyed,\n // and given no neighbours rather than left out of the map entirely.\n if (!f) return { parsed: false, summary: 'not parsed', neighbors: [] };\n const fin = facts.fanIn[path] || 0;\n const fout = facts.fanOut[path] || 0;\n const role = roleOf(path, facts);\n const cls = [\n role === 'entry' ? 'is-entry' : '',\n role === 'hub' ? 'is-hub' : '',\n factIndex(facts).inCycle.has(path) ? 'is-cycle' : '',\n ].filter(Boolean).join(' ');\n return {\n parsed: true,\n summary: fileSummary(role, fin, fout, f),\n cls,\n neighbors: [\n ...(facts.importsOf[path] || []).map((p) => ({ path: p, relation: 'pulled in' })),\n ...(facts.importers[path] || []).map((p) => ({ path: p, relation: 'leans on it' })),\n ],\n };\n },\n });\n\n const layout = layoutMindMap(rootSpec, state.mm.expanded);\n renderMindMap(host, layout, state.mm.expanded, {\n onToggle: (cell) => {\n if (state.mm.expanded.has(cell.id)) state.mm.expanded.delete(cell.id);\n else state.mm.expanded.add(cell.id);\n renderMap(false); // keep the pan position — the map grows around you\n peek(cell);\n },\n onNavigate: (cell) => {\n if (cell.navFolder) hooks.onOpenFolder(cell.navFolder);\n else if (cell.kind === 'ghost' || cell.kind === 'more') {\n // A ghost stands for a file outside the current branch; there is nothing\n // to expand, so the only sensible click is to go and open it.\n if (cell.nav) hooks.onOpenFile(cell.nav);\n } else if (cell.nav) peek(cell);\n },\n });\n\n // One button, two duties: it says what a click will do next.\n toggleBtn.textContent = state.mm.expanded.size > 1 ? 'Close all' : 'Open folders';\n if (fit) hooks.onFit();\n}\n\n// Fills the inspector for a cell without leaving the map.\nfunction peek(cell) {\n if (cell.kind === 'folder' || cell.kind === 'root') {\n focusFolder(cell.id.slice(4)); // strip the `dir:` prefix\n hooks.onSyncInspector();\n } else if (cell.nav) {\n focusFile(cell.nav);\n hooks.onSyncInspector();\n hooks.onRenderSidebar();\n }\n}\n\n// The line under a file's name. Each role gets the number that matters for it:\n// an entry point is interesting for what it pulls in, a hub for what leans on it.\nexport function fileSummary(role, fanIn, fanOut, file) {\n switch (role) {\n case 'entry': return `entry point · pulls in ${fanOut}`;\n case 'hub': return `hub · ${fanIn} dependents`;\n case 'leaf': return `leaf · ${fanIn} dependent${fanIn === 1 ? '' : 's'}`;\n case 'test': return 'test file';\n case 'config': return 'configuration';\n default: {\n let s = `${fanOut} out · ${fanIn} in`;\n if (file.functions.length) s += ` · ${file.functions.length} fn`;\n return s;\n }\n }\n}\n"]}
@@ -0,0 +1,3 @@
1
+ export declare function mdRender(text: any): string;
2
+ export declare function mdLite(text: any): string;
3
+ export declare function mdInline(text: any): any;
@@ -1,119 +1,112 @@
1
1
  // A small, honest Markdown renderer for READMEs: headings to h3, bold,
2
2
  // italic, inline code, fenced code blocks, lists, links, paragraphs. HTML is
3
3
  // escaped before anything else — a README is untrusted input like any other.
4
-
5
4
  import { escapeHtml } from './html.js';
6
-
7
5
  export function mdRender(text) {
8
- const lines = escapeHtml(String(text || '')).split('\n');
9
- const html = [];
10
- let para = [];
11
- let list = [];
12
- let inCode = false;
13
- let code = [];
14
-
15
- const flushPara = () => {
16
- if (!para.length) return;
17
- html.push('<p>' + para.map(inline).join('<br>') + '</p>');
18
- para = [];
19
- };
20
- const flushList = () => {
21
- if (!list.length) return;
22
- html.push('<ul>' + list.map((item) => '<li>' + inline(item) + '</li>').join('') + '</ul>');
23
- list = [];
24
- };
25
- const flushCode = () => {
26
- html.push('<pre><code>' + code.join('\n') + '</code></pre>');
27
- code = [];
28
- };
29
-
30
- for (const line of lines) {
31
- if (/^```/.test(line.trim())) {
32
- if (inCode) {
33
- inCode = false;
34
- flushCode();
35
- } else {
36
- flushPara();
37
- flushList();
38
- inCode = true;
39
- }
40
- continue;
41
- }
42
- if (inCode) {
43
- code.push(line);
44
- continue;
45
- }
46
-
47
- const heading = line.match(/^(#{1,3})\s+(.+)$/);
48
- if (heading) {
49
- flushPara();
50
- flushList();
51
- const level = heading[1].length;
52
- html.push(`<h${level} class="doc-h">${inline(heading[2].trim())}</h${level}>`);
53
- continue;
6
+ const lines = escapeHtml(String(text || '')).split('\n');
7
+ const html = [];
8
+ let para = [];
9
+ let list = [];
10
+ let inCode = false;
11
+ let code = [];
12
+ const flushPara = () => {
13
+ if (!para.length)
14
+ return;
15
+ html.push('<p>' + para.map(inline).join('<br>') + '</p>');
16
+ para = [];
17
+ };
18
+ const flushList = () => {
19
+ if (!list.length)
20
+ return;
21
+ html.push('<ul>' + list.map((item) => '<li>' + inline(item) + '</li>').join('') + '</ul>');
22
+ list = [];
23
+ };
24
+ const flushCode = () => {
25
+ html.push('<pre><code>' + code.join('\n') + '</code></pre>');
26
+ code = [];
27
+ };
28
+ for (const line of lines) {
29
+ if (/^```/.test(line.trim())) {
30
+ if (inCode) {
31
+ inCode = false;
32
+ flushCode();
33
+ }
34
+ else {
35
+ flushPara();
36
+ flushList();
37
+ inCode = true;
38
+ }
39
+ continue;
40
+ }
41
+ if (inCode) {
42
+ code.push(line);
43
+ continue;
44
+ }
45
+ const heading = line.match(/^(#{1,3})\s+(.+)$/);
46
+ if (heading) {
47
+ flushPara();
48
+ flushList();
49
+ const level = heading[1].length;
50
+ html.push(`<h${level} class="doc-h">${inline(heading[2].trim())}</h${level}>`);
51
+ continue;
52
+ }
53
+ const bullet = line.match(/^\s*(?:[-*]|\d+\.)\s+(.+)$/);
54
+ if (bullet) {
55
+ flushPara();
56
+ list.push(bullet[1].trim());
57
+ continue;
58
+ }
59
+ if (!line.trim()) {
60
+ flushPara();
61
+ flushList();
62
+ continue;
63
+ }
64
+ para.push(line.trim());
54
65
  }
55
-
56
- const bullet = line.match(/^\s*(?:[-*]|\d+\.)\s+(.+)$/);
57
- if (bullet) {
58
- flushPara();
59
- list.push(bullet[1].trim());
60
- continue;
61
- }
62
-
63
- if (!line.trim()) {
64
- flushPara();
65
- flushList();
66
- continue;
67
- }
68
- para.push(line.trim());
69
- }
70
- if (inCode) flushCode();
71
- flushPara();
72
- flushList();
73
- return html.join('\n');
66
+ if (inCode)
67
+ flushCode();
68
+ flushPara();
69
+ flushList();
70
+ return html.join('\n');
74
71
  }
75
-
76
72
  // Everything reaching here has already been through escapeHtml() in mdRender,
77
73
  // so `text`, `label` and `url` hold no live markup and no bare quotes — which
78
74
  // is what makes it safe to drop `url` straight into an attribute below.
79
75
  // Do not add a caller that skips that step, and do not escape again here:
80
76
  // double-escaping turns a legitimate `&amp;` in a URL into `&amp;amp;`.
81
77
  function inline(text) {
82
- return text
83
- .replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>')
84
- .replace(/\*([^*]+)\*/g, '<em>$1</em>')
85
- .replace(/`([^`]+)`/g, '<code>$1</code>')
86
- .replace(/\[([^\]]+)\]\(([^)\s]+)\)/g, (m, label, url) => {
87
- // Only http(s) becomes a link — a README is free to write
88
- // `[click](javascript:…)` and we are not obliged to honour it.
89
- if (/^https?:\/\//i.test(url)) {
90
- return `<a href="${url}" target="_blank" rel="noopener noreferrer">${label}</a>`;
91
- }
92
- return `<span class="doc-dead-link" title="${url}">${label}</span>`;
78
+ return text
79
+ .replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>')
80
+ .replace(/\*([^*]+)\*/g, '<em>$1</em>')
81
+ .replace(/`([^`]+)`/g, '<code>$1</code>')
82
+ .replace(/\[([^\]]+)\]\(([^)\s]+)\)/g, (m, label, url) => {
83
+ // Only http(s) becomes a link — a README is free to write
84
+ // `[click](javascript:…)` and we are not obliged to honour it.
85
+ if (/^https?:\/\//i.test(url)) {
86
+ return `<a href="${url}" target="_blank" rel="noopener noreferrer">${label}</a>`;
87
+ }
88
+ return `<span class="doc-dead-link" title="${url}">${label}</span>`;
93
89
  });
94
90
  }
95
-
96
-
97
91
  // Markdown-lite: for AI answers and the static explanations, which use only
98
92
  // **bold** and `code`. Escape everything first, then restore those two — so a
99
93
  // model that echoes markup back at us can't inject it. Lives here rather than
100
94
  // beside its callers in the view layer because it is pure text work, and being
101
95
  // pure is what makes it testable.
102
96
  export function mdLite(text) {
103
- return escapeHtml(String(text ?? ''))
104
- .split(/\n\n+/)
105
- .map((para) => `<p>${inlineMarks(para).replace(/\n/g, '<br>')}</p>`)
106
- .join('');
97
+ return escapeHtml(String(text ?? ''))
98
+ .split(/\n\n+/)
99
+ .map((para) => `<p>${inlineMarks(para).replace(/\n/g, '<br>')}</p>`)
100
+ .join('');
107
101
  }
108
-
109
102
  // The same two marks without the paragraph wrapper, for card bodies and
110
103
  // single-line captions.
111
104
  export function mdInline(text) {
112
- return inlineMarks(escapeHtml(String(text ?? '')));
105
+ return inlineMarks(escapeHtml(String(text ?? '')));
113
106
  }
114
-
115
107
  function inlineMarks(safe) {
116
- return safe
117
- .replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>')
118
- .replace(/`([^`]+)`/g, '<code>$1</code>');
108
+ return safe
109
+ .replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>')
110
+ .replace(/`([^`]+)`/g, '<code>$1</code>');
119
111
  }
112
+ //# sourceMappingURL=markdown.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"markdown.js","sourceRoot":"","sources":["../../src/public/js/markdown.ts"],"names":[],"mappings":"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","sourcesContent":["// A small, honest Markdown renderer for READMEs: headings to h3, bold,\n// italic, inline code, fenced code blocks, lists, links, paragraphs. HTML is\n// escaped before anything else — a README is untrusted input like any other.\n\nimport { escapeHtml } from './html.js';\n\nexport function mdRender(text) {\n const lines = escapeHtml(String(text || '')).split('\\n');\n const html = [];\n let para = [];\n let list = [];\n let inCode = false;\n let code = [];\n\n const flushPara = () => {\n if (!para.length) return;\n html.push('<p>' + para.map(inline).join('<br>') + '</p>');\n para = [];\n };\n const flushList = () => {\n if (!list.length) return;\n html.push('<ul>' + list.map((item) => '<li>' + inline(item) + '</li>').join('') + '</ul>');\n list = [];\n };\n const flushCode = () => {\n html.push('<pre><code>' + code.join('\\n') + '</code></pre>');\n code = [];\n };\n\n for (const line of lines) {\n if (/^```/.test(line.trim())) {\n if (inCode) {\n inCode = false;\n flushCode();\n } else {\n flushPara();\n flushList();\n inCode = true;\n }\n continue;\n }\n if (inCode) {\n code.push(line);\n continue;\n }\n\n const heading = line.match(/^(#{1,3})\\s+(.+)$/);\n if (heading) {\n flushPara();\n flushList();\n const level = heading[1].length;\n html.push(`<h${level} class=\"doc-h\">${inline(heading[2].trim())}</h${level}>`);\n continue;\n }\n\n const bullet = line.match(/^\\s*(?:[-*]|\\d+\\.)\\s+(.+)$/);\n if (bullet) {\n flushPara();\n list.push(bullet[1].trim());\n continue;\n }\n\n if (!line.trim()) {\n flushPara();\n flushList();\n continue;\n }\n para.push(line.trim());\n }\n if (inCode) flushCode();\n flushPara();\n flushList();\n return html.join('\\n');\n}\n\n// Everything reaching here has already been through escapeHtml() in mdRender,\n// so `text`, `label` and `url` hold no live markup and no bare quotes — which\n// is what makes it safe to drop `url` straight into an attribute below.\n// Do not add a caller that skips that step, and do not escape again here:\n// double-escaping turns a legitimate `&amp;` in a URL into `&amp;amp;`.\nfunction inline(text) {\n return text\n .replace(/\\*\\*([^*]+)\\*\\*/g, '<strong>$1</strong>')\n .replace(/\\*([^*]+)\\*/g, '<em>$1</em>')\n .replace(/`([^`]+)`/g, '<code>$1</code>')\n .replace(/\\[([^\\]]+)\\]\\(([^)\\s]+)\\)/g, (m, label, url) => {\n // Only http(s) becomes a link — a README is free to write\n // `[click](javascript:…)` and we are not obliged to honour it.\n if (/^https?:\\/\\//i.test(url)) {\n return `<a href=\"${url}\" target=\"_blank\" rel=\"noopener noreferrer\">${label}</a>`;\n }\n return `<span class=\"doc-dead-link\" title=\"${url}\">${label}</span>`;\n });\n}\n\n\n// Markdown-lite: for AI answers and the static explanations, which use only\n// **bold** and `code`. Escape everything first, then restore those two — so a\n// model that echoes markup back at us can't inject it. Lives here rather than\n// beside its callers in the view layer because it is pure text work, and being\n// pure is what makes it testable.\nexport function mdLite(text) {\n return escapeHtml(String(text ?? ''))\n .split(/\\n\\n+/)\n .map((para) => `<p>${inlineMarks(para).replace(/\\n/g, '<br>')}</p>`)\n .join('');\n}\n\n// The same two marks without the paragraph wrapper, for card bodies and\n// single-line captions.\nexport function mdInline(text) {\n return inlineMarks(escapeHtml(String(text ?? '')));\n}\n\nfunction inlineMarks(safe) {\n return safe\n .replace(/\\*\\*([^*]+)\\*\\*/g, '<strong>$1</strong>')\n .replace(/`([^`]+)`/g, '<code>$1</code>');\n}\n"]}