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
@@ -1,3 +1,4 @@
1
+ import { scanOptionsFromFlags } from '../scanOptions.js';
1
2
  // The interactive session.
2
3
  //
3
4
  // This is a prompt, not a full-screen TUI, and that is a deliberate choice.
@@ -14,282 +15,448 @@
14
15
  // hanging forever waiting for input nobody is there to type.
15
16
  // * Flags → env → config precedence. `NO_COLOR` and `COLUMNS` are honored
16
17
  // before anything is drawn, so a redirect gets plain, fitted text.
17
-
18
18
  import readline from 'node:readline';
19
+ import readlinePromises from 'node:readline/promises';
19
20
  import fs from 'node:fs';
20
21
  import path from 'node:path';
21
-
22
- import { openRepo } from './session.js';
23
- import { CLEAR, EXIT, commandNames, helpText, lookup, tokenize } from './commands.js';
24
- import { overview } from './views.js';
22
+ import { openRepo, openRepoOrClone, isGitUrl, remoteUrlFor, closeRemote, searchRepo, explainRepoOverview } from './session.js';
23
+ import { fetchRepoFacts } from './github.js';
24
+ import { CLEAR, EXIT, commandNames, completer, helpText, lookup, shellEscape, tokenize } from './commands.js';
25
+ import { overview, github as githubView } from './views.js';
25
26
  import { bold, cyan, dim, ok, bad, paint } from '../ui.js';
26
27
  import { configPath, readSettings, serverUrls } from '../../server/config.js';
27
28
  import { readPidFile, pidIsAlive } from '../../server/pidfile.js';
28
-
29
- const VERSION = JSON.parse(
30
- fs.readFileSync(new URL('../../package.json', import.meta.url), 'utf8')
31
- ).version;
32
-
29
+ import { expandHome } from '../../server/paths.js';
30
+ import { SourcePicker } from './picker.js';
31
+ import { getGitDiff } from '../../server/gitDiff.js';
32
+ import { runExternalAnalysis, toolsStatus } from '../../server/tools/scan.js';
33
+ import { formatDiff, formatDeepAnalysis, formatEngines } from './featureViews.js';
34
+ import { askConfig, askModel, askSetupMessage } from './assistant.js';
35
+ import { wrapText } from './wrap.js';
36
+ const VERSION = JSON.parse(fs.readFileSync(new URL('../../package.json', import.meta.url), 'utf8')).version;
33
37
  // The one-time note. `onboarder` used to start a web server; now it opens this.
34
38
  // Someone who upgrades and has muscle memory for the old thing deserves to be
35
39
  // told once where the server went, and then never again.
36
40
  const HINT_MARKER = 'explorer-hint-v1';
37
-
38
- export async function runExplore({ target = '.', flags = {}, out = console.log, err = console.error, version = VERSION } = {}) {
39
- // The guard. `stdin` matters as much as `stdout`: a session with no input
40
- // source is a hang, and a hang in CI is worse than any error.
41
- if (!process.stdin.isTTY || !process.stdout.isTTY) {
42
- out('');
43
- out(' The terminal explorer needs an interactive terminal.');
44
- out(dim(' In a script or a pipe, use these instead:'));
45
- out(dim(' onboarder start start the web UI'));
46
- out(dim(' onboarder start background start it detached'));
47
- out(dim(' onboarder status | stop manage a running one'));
48
- out(dim(' onboarder --help everything else'));
41
+ export async function runExplore({ target = null, flags = {}, out = console.log, err = console.error, version = VERSION } = {}) {
42
+ // The guard. `stdin` matters as much as `stdout`: a session with no input
43
+ // source is a hang, and a hang in CI is worse than any error.
44
+ if (!process.stdin.isTTY || !process.stdout.isTTY) {
45
+ out('');
46
+ out(' The terminal explorer needs an interactive terminal.');
47
+ out(dim(' In a script or a pipe, use these instead:'));
48
+ out(dim(' onboarder start start the web UI'));
49
+ out(dim(' onboarder start background start it detached'));
50
+ out(dim(' onboarder status | stop manage a running one'));
51
+ out(dim(' onboarder --help everything else'));
52
+ out('');
53
+ return 0;
54
+ }
55
+ if (!target) {
56
+ target = await chooseInitialSource(out);
57
+ if (!target)
58
+ return 0;
59
+ }
60
+ let repo;
61
+ try {
62
+ // The launch argument accepts a URL for the same reason `cd` does: someone
63
+ // who has never seen this repo should be able to name it and read it. A
64
+ // silent ten-second clone looks like a hang, so the URL case says what it
65
+ // is doing before it starts.
66
+ if (isGitUrl(target))
67
+ out(dim(` cloning ${target}…`));
68
+ repo = await openRepoOrClone(target, { ...scanOptionsFromFlags(flags), onProgress: progressReporter(out) });
69
+ }
70
+ catch (e) {
71
+ err(' ' + bad((e.message || String(e))));
72
+ err(dim(' Point it at a folder: onboarder explore /path/to/repo'));
73
+ err(dim(' …or a git URL: onboarder explore https://github.com/org/repo'));
74
+ return 1;
75
+ }
49
76
  out('');
50
- return 0;
51
- }
52
-
53
- let repo;
54
- try {
55
- repo = await openRepo(target, { onProgress: progressReporter(out) });
56
- } catch (e) {
57
- err(' ' + bad((e.message || String(e))));
58
- err(dim(' Point it at a folder: onboarder explore /path/to/repo'));
59
- return 1;
60
- }
61
-
62
- out('');
63
- out(overview(repo));
64
- out('');
65
- if (await shouldShowHint(flags)) {
66
- out(dim(' Note: `onboarder` used to start the web server. That is now `onboarder start`'));
67
- out(dim(' (or the `web` command here) — this session reads the repo directly,'));
68
- out(dim(' so it needs no server and works offline.'));
77
+ out(overview(repo));
69
78
  out('');
70
- }
71
- return session({ repo, flags, out, err, version });
79
+ if (await shouldShowHint(flags)) {
80
+ out(dim(' Note: `onboarder` used to start the web server. That is now `onboarder start`'));
81
+ out(dim(' (or the `web` command here) — this session reads the repo directly,'));
82
+ out(dim(' so it needs no server and works offline.'));
83
+ out('');
84
+ }
85
+ return session({ repo, flags, out, err, version });
86
+ }
87
+ async function chooseInitialSource(out) {
88
+ const picker = new SourcePicker();
89
+ const rl = readlinePromises.createInterface({ input: process.stdin, output: process.stdout });
90
+ try {
91
+ out(await picker.view());
92
+ for (;;) {
93
+ const result = await picker.choose(await rl.question(picker.prompt));
94
+ if (result.message)
95
+ out(result.message);
96
+ if (result.done)
97
+ return result.target;
98
+ }
99
+ }
100
+ catch {
101
+ return null;
102
+ }
103
+ finally {
104
+ rl.close();
105
+ }
72
106
  }
73
-
74
107
  // Scanning a large monorepo is the one genuinely slow thing this does, so it
75
108
  // says so. A silent eight seconds is indistinguishable from a hang.
76
109
  function progressReporter(out) {
77
- let last = 0;
78
- return ({ phase, done }) => {
79
- if (!process.stdout.isTTY) return;
80
- const now = Date.now();
81
- if (phase === 'parse' && done && now - last > 400) {
82
- last = now;
83
- out(dim(`\r scanning… ${done} files`));
84
- }
85
- };
110
+ let last = 0;
111
+ return (progress) => {
112
+ if (!process.stdout.isTTY)
113
+ return;
114
+ const now = Date.now();
115
+ if (progress.phase === 'parse' && progress.done && now - last > 400) {
116
+ last = now;
117
+ out(dim(`\r scanning… ${progress.done} files`));
118
+ }
119
+ };
86
120
  }
87
-
88
121
  async function shouldShowHint(flags) {
89
- if (process.env.ONBOARDER_NO_HINT) return false;
90
- try {
91
- const marker = path.join(path.dirname(flags.config || configPath()), HINT_MARKER);
92
- const seen = await fs.promises.stat(marker).then(() => true).catch(() => false);
93
- if (seen) return false;
94
- await fs.promises.mkdir(path.dirname(marker), { recursive: true });
95
- await fs.promises.writeFile(marker, 'shown\n');
96
- return true;
97
- } catch {
98
- // A read-only config home is not a reason to nag on every launch.
99
- return false;
100
- }
122
+ if (process.env.ONBOARDER_NO_HINT)
123
+ return false;
124
+ try {
125
+ const marker = path.join(path.dirname(flags.config || configPath()), HINT_MARKER);
126
+ const seen = await fs.promises.stat(marker).then(() => true).catch(() => false);
127
+ if (seen)
128
+ return false;
129
+ await fs.promises.mkdir(path.dirname(marker), { recursive: true });
130
+ await fs.promises.writeFile(marker, 'shown\n');
131
+ return true;
132
+ }
133
+ catch {
134
+ // A read-only config home is not a reason to nag on every launch.
135
+ return false;
136
+ }
101
137
  }
102
-
103
138
  // The read loop. Commands are queued rather than awaited inline, because
104
139
  // readline emits the next line while a slow command is still running and two
105
140
  // interleaved `cd`s would leave the session pointing at a repo nobody asked
106
141
  // for.
107
142
  function session({ repo, flags, out, err, version }) {
108
- const ctx = {
109
- repo,
110
- version,
111
- flags,
112
- help: (topic) => helpText(ctx, topic),
113
- rescan: () => reload(ctx, ctx.repo.root),
114
- loadRepo: (where) => reload(ctx, where),
115
- web: () => startWeb(ctx),
116
- };
117
-
118
- const rl = readline.createInterface({
119
- input: process.stdin,
120
- output: process.stdout,
121
- prompt: promptFor(repo),
122
- terminal: true,
123
- historySize: 200,
124
- });
125
-
126
- let queue = Promise.resolve();
127
- let closed = false;
128
- let interrupts = 0;
129
-
130
- rl.on('line', (line) => {
131
- queue = queue.then(() => handleLine(ctx, line, rl, out, err));
132
- });
133
-
134
- // Ctrl-D on an empty line is the universal "I'm done". On a line with text,
135
- // readline handles it itself; this only sees the empty-line case.
136
- //
137
- // Closing does NOT end the session. A piped or fast-typed sequence of commands
138
- // can already be queued when the last line arrives, and resolving here would
139
- // throw those away mid-flight — which is exactly what happens to `tour` when
140
- // its input is followed immediately by `exit`. The exit waits on the queue.
141
- rl.on('close', () => {
142
- closed = true;
143
- process.stdout.off('resize', onResize);
144
- });
145
-
146
- // Ctrl-C twice leaves. Once clears the line, which is what readline already
147
- // does, and matches every other REPL people use.
148
- rl.on('SIGINT', () => {
149
- if (closed) return;
150
- interrupts++;
151
- if (interrupts >= 2) {
152
- rl.close();
153
- return;
154
- }
155
- out(dim('\n Ctrl-C again to leave.'));
156
- rl.setPrompt(promptFor(ctx.repo));
157
- rl.prompt();
158
- });
159
-
160
- // A resize redraws rather than leaving the prompt stranded mid-wrap.
161
- const onResize = () => {
162
- if (!closed) rl.write(null, { ctrl: true, name: 'l' });
163
- };
164
- process.stdout.on('resize', onResize);
165
-
166
- queue = queue.then(() => rl.prompt());
167
-
168
- return new Promise((resolve) => {
169
- const poll = setInterval(() => {
170
- if (!closed) return;
171
- clearInterval(poll);
172
- // Drain whatever is still running, then say goodbye. A command that throws
173
- // has already been caught in `handleLine`, so this cannot reject.
174
- queue.then(() => {
175
- out('');
176
- out(dim(' bye.'));
177
- resolve(0);
178
- });
179
- }, 20);
180
- });
143
+ const ctx = {
144
+ picker: null,
145
+ repo,
146
+ version,
147
+ flags,
148
+ help: (topic) => helpText(ctx, topic),
149
+ rescan: () => reload(ctx, ctx.repo.root),
150
+ loadRepo: (where) => reload(ctx, where),
151
+ selectSource: async () => {
152
+ ctx.picker = new SourcePicker(ctx.repo.cloneDir ? process.cwd() : ctx.repo.root);
153
+ return ctx.picker.view();
154
+ },
155
+ github: () => askGithub(ctx),
156
+ web: () => startWeb(ctx),
157
+ diff: async (base = 'HEAD', head = '', file = '') => formatDiff(await getGitDiff(ctx.repo.root, { base, head, file }), ctx.repo.facts.importers || {}, ctx.repo.scan.files.length, file),
158
+ engines: () => formatEngines(toolsStatus()),
159
+ deep: async (tool) => {
160
+ const known = toolsStatus();
161
+ if (tool !== 'all' && !Object.hasOwn(known, tool)) {
162
+ return ' Unknown engine: ' + tool + '. Run `engines` to see the available names.';
163
+ }
164
+ return formatDeepAnalysis(await runExternalAnalysis(ctx.repo.root, tool === 'all' ? {} : { tools: [tool] }));
165
+ },
166
+ ask: async (question) => {
167
+ if (!question.trim())
168
+ return ' Ask a question, for example: ask Where should I start?';
169
+ const config = askConfig();
170
+ if (!config)
171
+ return askSetupMessage();
172
+ const hits = searchRepo(ctx.repo, question, { limit: 8 });
173
+ const lines = [
174
+ explainRepoOverview(ctx.repo),
175
+ `Health: ${ctx.repo.health.grade} (${ctx.repo.health.score}/100).`,
176
+ `Entries: ${ctx.repo.facts.entries.slice(0, 10).join(', ') || 'none detected'}.`,
177
+ `Hubs: ${ctx.repo.facts.hubs.slice(0, 10).map((hub) => hub.path).join(', ') || 'none detected'}.`,
178
+ 'Search matches:',
179
+ ...(hits.results || []).map((hit) => `${hit.path}${hit.line ? ':' + hit.line : ''} ${hit.snippet || ''}`),
180
+ ];
181
+ return '\n AI answer\n' + wrapText(await askModel(question, lines.join('\n'), config), ' ');
182
+ },
183
+ };
184
+ const rl = readline.createInterface({
185
+ input: process.stdin,
186
+ output: process.stdout,
187
+ prompt: promptFor(repo),
188
+ terminal: true,
189
+ historySize: 200,
190
+ // The completer closes over the *current* repo rather than the one captured
191
+ // here, so after a `cd` it completes paths in the new repository. Reading
192
+ // `ctx.repo` at completion time is the whole trick.
193
+ completer: (line) => completer(ctx.repo)(line),
194
+ });
195
+ let queue = Promise.resolve();
196
+ let closed = false;
197
+ let interrupts = 0;
198
+ let onResize = null;
199
+ // One poisoned promise must not end the session. `handleLine` catches its own
200
+ // errors, but anything thrown outside it — a prompt write to a closed stream,
201
+ // an error in a command's argument handling — would otherwise reject this
202
+ // chain and silently swallow every command typed afterwards. The session would
203
+ // look alive and do nothing, which is the worst failure mode a REPL has.
204
+ rl.on('line', (line) => {
205
+ queue = queue
206
+ .then(() => handleLine(ctx, line, rl, out, err))
207
+ .catch((e) => {
208
+ err(' ' + bad((e?.message || String(e))));
209
+ if (!closed) {
210
+ try {
211
+ rl.setPrompt(promptFor(ctx.repo));
212
+ rl.prompt();
213
+ }
214
+ catch {
215
+ // The stream is gone; the close handler finishes the session.
216
+ }
217
+ }
218
+ });
219
+ });
220
+ // Ctrl-D on an empty line is the universal "I'm done". On a line with text,
221
+ // readline handles it itself; this only sees the empty-line case.
222
+ //
223
+ // Closing does NOT end the session. A piped or fast-typed sequence of commands
224
+ // can already be queued when the last line arrives, and resolving here would
225
+ // throw those away mid-flight — which is exactly what happens to `tour` when
226
+ // its input is followed immediately by `exit`. The exit waits on the queue.
227
+ rl.on('close', () => {
228
+ closed = true;
229
+ // The listener is removed on every exit path, including the interval one
230
+ // below, so a session that ends by any route leaves stdout as it found it.
231
+ if (onResize)
232
+ process.stdout.off('resize', onResize);
233
+ });
234
+ // Ctrl-C twice leaves. Once clears the line, which is what readline already
235
+ // does, and matches every other REPL people use.
236
+ rl.on('SIGINT', () => {
237
+ if (closed)
238
+ return;
239
+ if (ctx.picker) {
240
+ ctx.picker = null;
241
+ out(dim('\n Selection cancelled.'));
242
+ rl.setPrompt(promptFor(ctx.repo));
243
+ rl.prompt();
244
+ return;
245
+ }
246
+ interrupts++;
247
+ if (interrupts >= 2) {
248
+ rl.close();
249
+ return;
250
+ }
251
+ out(dim('\n Ctrl-C again to leave.'));
252
+ rl.setPrompt(promptFor(ctx.repo));
253
+ rl.prompt();
254
+ });
255
+ // A resize redraws rather than leaving the prompt stranded mid-wrap.
256
+ onResize = () => {
257
+ if (!closed)
258
+ rl.write(null, { ctrl: true, name: 'l' });
259
+ };
260
+ process.stdout.on('resize', onResize);
261
+ queue = queue.then(() => rl.prompt());
262
+ return new Promise((resolve) => {
263
+ const poll = setInterval(() => {
264
+ if (!closed)
265
+ return;
266
+ clearInterval(poll);
267
+ // Drain whatever is still running, then say goodbye. A command that throws
268
+ // has already been caught in `handleLine`, so this cannot reject.
269
+ queue.then(() => {
270
+ out('');
271
+ out(dim(' bye.'));
272
+ // A clone this session made is a temp directory, and the session is the
273
+ // only thing that knows about it. Removing it last — after the last
274
+ // command has finished reading files out of it — is the only ordering
275
+ // that cannot pull the ground out from under a command still running.
276
+ closeRemote(ctx.repo).catch((..._args) => { });
277
+ resolve(0);
278
+ });
279
+ }, 20);
280
+ });
181
281
  }
182
-
183
282
  function promptFor(repo) {
184
- return paint(' ', 'cyan') + bold(repo.name.slice(0, 24)) + paint(' > ', 'gray');
283
+ return paint(' ', 'cyan') + bold(repo.name.slice(0, 24)) + paint(' > ', 'gray');
185
284
  }
186
-
187
285
  async function handleLine(ctx, line, rl, out, err) {
188
- const words = tokenize(line);
189
- if (!words.length) {
190
- rl.prompt();
191
- return;
192
- }
193
- const [name, ...args] = words;
194
- const cmd = lookup(name);
195
-
196
- if (!cmd) {
197
- // The nearest command by edit distance is almost always what was meant, and
198
- // a one-line "did you mean" beats a paragraph about `help`.
199
- const near = nearest(name);
200
- err(' ' + bad('Unknown command: ' + name) + (near ? dim(' did you mean `' + near + '`?') : dim(' try `help`')));
201
- rl.prompt();
202
- return;
203
- }
204
-
205
- try {
206
- const result = await cmd.run(ctx, args);
207
- if (result === EXIT) {
208
- rl.close();
209
- return;
286
+ if (ctx.picker) {
287
+ const result = await ctx.picker.choose(line);
288
+ if (result.done) {
289
+ ctx.picker = null;
290
+ if (result.target) {
291
+ try {
292
+ out(await reload(ctx, result.target));
293
+ }
294
+ catch (e) {
295
+ err(' ' + bad(e.message || String(e)));
296
+ }
297
+ }
298
+ else if (result.message)
299
+ out(dim(result.message));
300
+ }
301
+ else if (result.message)
302
+ out(result.message);
303
+ rl.setPrompt(ctx.picker ? ctx.picker.prompt : promptFor(ctx.repo));
304
+ rl.prompt();
305
+ return;
306
+ }
307
+ const words = tokenize(line);
308
+ if (!words.length) {
309
+ rl.prompt();
310
+ return;
311
+ }
312
+ const [name, ...args] = words;
313
+ // `!cmd` runs a shell command from inside the session and prints its output.
314
+ // It is the only way out to a shell, which is the point: the set of things
315
+ // that can happen here stays small enough to remember.
316
+ if (name.startsWith('!') && name.length > 1) {
317
+ const result = await shellEscape(line.replace(/^!\s*/, ''), ctx.repo.root);
318
+ if (result)
319
+ out(result);
320
+ rl.setPrompt(promptFor(ctx.repo));
321
+ rl.prompt();
322
+ return;
323
+ }
324
+ const cmd = lookup(name);
325
+ if (!cmd) {
326
+ // The nearest command by edit distance is almost always what was meant, and
327
+ // a one-line "did you mean" beats a paragraph about `help`.
328
+ const near = nearest(name);
329
+ err(' ' + bad('Unknown command: ' + name) + (near ? dim(' did you mean `' + near + '`?') : dim(' try `help`')));
330
+ rl.prompt();
331
+ return;
210
332
  }
211
- if (result === CLEAR) {
212
- out('\x1b[2J\x1b[3J\x1b[H');
213
- } else if (result) {
214
- out(result);
333
+ try {
334
+ const result = await cmd.run(ctx, args);
335
+ if (result === EXIT) {
336
+ rl.close();
337
+ return;
338
+ }
339
+ if (result === CLEAR) {
340
+ out('\x1b[2J\x1b[3J\x1b[H');
341
+ }
342
+ else if (result) {
343
+ out(result);
344
+ }
215
345
  }
216
- } catch (e) {
217
- err(' ' + bad((e.message || String(e))));
218
- }
219
- // The prompt carries the repo name, and `cd` can change which repo that is.
220
- // Re-reading it after every command is cheaper than tracking which commands
221
- // swap the repo, and it cannot go stale.
222
- rl.setPrompt(promptFor(ctx.repo));
223
- rl.prompt();
346
+ catch (e) {
347
+ err(' ' + bad((e.message || String(e))));
348
+ }
349
+ // The prompt carries the repo name, and `cd` can change which repo that is.
350
+ // Re-reading it after every command is cheaper than tracking which commands
351
+ // swap the repo, and it cannot go stale.
352
+ rl.setPrompt(ctx.picker ? ctx.picker.prompt : promptFor(ctx.repo));
353
+ rl.prompt();
224
354
  }
225
-
226
355
  // Swap the loaded repo. Used by `cd` and `rescan`; the scan is the slow part,
227
356
  // so the new name reaches the prompt only after it succeeds.
228
357
  async function reload(ctx, where) {
229
- const target = String(where || '').trim();
230
- if (!target) return ' cd needs a folder — `cd ../other-repo`.';
231
- const next = await openRepo(target, { onProgress: () => {} });
232
- ctx.repo = next;
233
- return '\n' + overview(next);
358
+ const target = String(where || '').trim();
359
+ if (!target) {
360
+ return ' cd needs a folder or a URL — `cd ../other-repo`, `cd https://github.com/org/repo`.';
361
+ }
362
+ // `rescan` re-reads the folder the session is already in. Re-cloning the URL
363
+ // to get the same bytes back would be slow and would change the temp
364
+ // directory out from under the session, so a target that resolves to the
365
+ // current root takes the local path even when the repo remembers a URL.
366
+ const reloadingCurrent = !isGitUrl(target) && path.resolve(expandHome(target)) === path.resolve(ctx.repo.root);
367
+ const previous = ctx.repo;
368
+ const options = scanOptionsFromFlags(ctx.flags || {});
369
+ const next = reloadingCurrent
370
+ ? await openRepo(previous.root, options)
371
+ : await openRepoOrClone(target, options);
372
+ // A reload of the current folder produces a repo that has never heard of the
373
+ // URL it was cloned from: `openRepo` reads a directory, and a directory does
374
+ // not know where it came from. Carrying the three fields across is what keeps
375
+ // `rescan` from silently demoting a clone to a nameless temp folder — the
376
+ // prompt, `about` and `github` all read them.
377
+ if (reloadingCurrent) {
378
+ next.gitUrl = previous.gitUrl;
379
+ next.cloneDir = previous.cloneDir;
380
+ next.tempId = previous.tempId;
381
+ if (previous.name !== next.name && !previous.cloneDir)
382
+ next.name = previous.name;
383
+ }
384
+ // The old clone is only dropped once the new one has loaded. Doing it the
385
+ // other way round means a typo in a URL leaves you with nothing loaded *and*
386
+ // the repo you were reading deleted from under you.
387
+ if (!reloadingCurrent)
388
+ await closeRemote(previous);
389
+ ctx.repo = next;
390
+ return '\n' + overview(next);
391
+ }
392
+ // Ask GitHub about the loaded repo and print the answer. Network failures are
393
+ // the expected case, not the exception — this is a command someone types on a
394
+ // plane — so every one of them comes back as a line of text.
395
+ async function askGithub(ctx) {
396
+ const url = await remoteUrlFor(ctx.repo);
397
+ if (!url) {
398
+ return githubView(ctx.repo, { ok: false, reason: 'This folder has no GitHub remote — there is nothing to ask about.' });
399
+ }
400
+ return githubView(ctx.repo, await fetchRepoFacts(url));
234
401
  }
235
-
236
402
  // The bridge between the two surfaces. If the server is already up this just
237
403
  // prints where it is; if not, it starts it detached, so the person keeps their
238
404
  // session. The URL is the same one `onboarder start` would print, because it
239
405
  // comes from the same `serverUrls` the CLI and the banner use.
240
406
  async function startWeb(ctx) {
241
- const file = ctx.flags.config || configPath();
242
- const settings = await readSettings(file).catch(() => null);
243
- const urls = serverUrls(settings || undefined);
244
- const recorded = readPidFile(file);
245
-
246
- if (recorded && pidIsAlive(recorded)) {
247
- return [' ' + ok('Already running.') + dim(` PID ${recorded.pid}`), ' ' + cyan(urls.local)].join('\n');
248
- }
249
-
250
- const { runStartBackground } = await import('../commands.js');
251
- const code = await runStartBackground({
252
- flags: { ...ctx.flags, json: true },
253
- out: () => {},
254
- err: () => {},
255
- });
256
- if (code !== 0) return ' ' + bad('Could not start the web UI.') + dim(' Try `onboarder doctor`.');
257
- return [
258
- ' ' + ok('Web UI started in the background.'),
259
- ' ' + cyan(urls.local),
260
- dim(' onboarder logs -f to watch it · onboarder stop to shut it down'),
261
- ].join('\n');
407
+ const file = ctx.flags.config || configPath();
408
+ const settings = await readSettings(file).catch(() => null);
409
+ const urls = serverUrls(settings || undefined);
410
+ const recorded = readPidFile(file);
411
+ if (recorded && pidIsAlive(recorded)) {
412
+ return [' ' + ok('Already running.') + dim(` PID ${recorded}`), ' ' + cyan(urls.local)].join('\n');
413
+ }
414
+ const { runStartBackground } = await import('../commands.js');
415
+ const code = await runStartBackground({
416
+ // `nonInteractive` is not optional here. With no config on disk,
417
+ // `runStartBackground` hands off to `runSetup`, which opens its own readline
418
+ // on the same stdin this session is already reading — the wizard and the
419
+ // explorer would then compete for keystrokes, and the explorer could process
420
+ // a wizard answer as a command. Inside a session the server either starts
421
+ // from the config that exists or reports that there is none; the person can
422
+ // run `onboarder setup` deliberately if they want the wizard.
423
+ flags: { ...ctx.flags, json: true, nonInteractive: true },
424
+ out: (..._args) => { },
425
+ err: (..._args) => { },
426
+ });
427
+ if (code !== 0)
428
+ return ' ' + bad('Could not start the web UI.') + dim(' Try `onboarder doctor`.');
429
+ return [
430
+ ' ' + ok('Web UI started in the background.'),
431
+ ' ' + cyan(urls.local),
432
+ dim(' onboarder logs -f to watch it · onboarder stop to shut it down'),
433
+ ].join('\n');
262
434
  }
263
-
264
435
  // Cheap "did you mean": plain Levenshtein over the command names, with a
265
436
  // distance cap of 2 so a wildly misspelled word gets "try `help`" instead of a
266
437
  // confident wrong suggestion. A wrong suggestion is worse than none.
267
438
  function nearest(word) {
268
- const w = String(word).toLowerCase();
269
- let best = null;
270
- let bestScore = Infinity;
271
- for (const name of commandNames()) {
272
- const d = distance(w, name);
273
- if (d < bestScore) {
274
- bestScore = d;
275
- best = name;
439
+ const w = String(word).toLowerCase();
440
+ let best = null;
441
+ let bestScore = Infinity;
442
+ for (const name of commandNames()) {
443
+ const d = distance(w, name);
444
+ if (d < bestScore) {
445
+ bestScore = d;
446
+ best = name;
447
+ }
276
448
  }
277
- }
278
- return bestScore <= 2 ? best : null;
449
+ return bestScore <= 2 ? best : null;
279
450
  }
280
-
281
451
  function distance(a, b) {
282
- let prev = Array.from({ length: b.length + 1 }, (_, i) => i);
283
- for (let i = 1; i <= a.length; i++) {
284
- const cur = [i];
285
- for (let j = 1; j <= b.length; j++) {
286
- cur[j] = Math.min(
287
- prev[j] + 1,
288
- cur[j - 1] + 1,
289
- prev[j - 1] + (a[i - 1] === b[j - 1] ? 0 : 1),
290
- );
452
+ let prev = Array.from({ length: b.length + 1 }, (_, i) => i);
453
+ for (let i = 1; i <= a.length; i++) {
454
+ const cur = [i];
455
+ for (let j = 1; j <= b.length; j++) {
456
+ cur[j] = Math.min(prev[j] + 1, cur[j - 1] + 1, prev[j - 1] + (a[i - 1] === b[j - 1] ? 0 : 1));
457
+ }
458
+ prev = cur;
291
459
  }
292
- prev = cur;
293
- }
294
- return prev[b.length];
460
+ return prev[b.length];
295
461
  }
462
+ //# sourceMappingURL=app.js.map