@akon-labs/nexus-cli 0.1.9 → 0.1.11-rc.2

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 (369) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/LICENSE +1 -0
  3. package/README.md +3 -2
  4. package/dist/app-bridge.bundle.js +67 -0
  5. package/dist/cli/require-login.d.ts.map +1 -1
  6. package/dist/cli/require-login.js +2 -2
  7. package/dist/cli/require-login.js.map +1 -1
  8. package/dist/cli.js +20866 -1882
  9. package/dist/core/agent-session.d.ts +3 -1
  10. package/dist/core/agent-session.d.ts.map +1 -1
  11. package/dist/core/agent-session.js +5 -1
  12. package/dist/core/agent-session.js.map +1 -1
  13. package/dist/core/auto-model.d.ts +1 -1
  14. package/dist/core/auto-model.d.ts.map +1 -1
  15. package/dist/core/auto-model.js +1 -1
  16. package/dist/core/auto-model.js.map +1 -1
  17. package/dist/core/extensions/index.d.ts +1 -1
  18. package/dist/core/extensions/index.d.ts.map +1 -1
  19. package/dist/core/extensions/index.js.map +1 -1
  20. package/dist/core/extensions/loader.d.ts.map +1 -1
  21. package/dist/core/extensions/loader.js +6 -0
  22. package/dist/core/extensions/loader.js.map +1 -1
  23. package/dist/core/extensions/runner.d.ts +7 -1
  24. package/dist/core/extensions/runner.d.ts.map +1 -1
  25. package/dist/core/extensions/runner.js +14 -0
  26. package/dist/core/extensions/runner.js.map +1 -1
  27. package/dist/core/extensions/types.d.ts +18 -0
  28. package/dist/core/extensions/types.d.ts.map +1 -1
  29. package/dist/core/extensions/types.js.map +1 -1
  30. package/dist/core/model-resolver.d.ts +2 -2
  31. package/dist/core/model-resolver.d.ts.map +1 -1
  32. package/dist/core/model-resolver.js +3 -3
  33. package/dist/core/model-resolver.js.map +1 -1
  34. package/dist/core/package-manager.d.ts +15 -0
  35. package/dist/core/package-manager.d.ts.map +1 -1
  36. package/dist/core/package-manager.js +50 -0
  37. package/dist/core/package-manager.js.map +1 -1
  38. package/dist/core/sdk.d.ts.map +1 -1
  39. package/dist/core/sdk.js +1 -1
  40. package/dist/core/sdk.js.map +1 -1
  41. package/dist/core/slash-commands.d.ts.map +1 -1
  42. package/dist/core/slash-commands.js +3 -3
  43. package/dist/core/slash-commands.js.map +1 -1
  44. package/dist/core/system-prompt.d.ts.map +1 -1
  45. package/dist/core/system-prompt.js +1 -1
  46. package/dist/core/system-prompt.js.map +1 -1
  47. package/dist/core/tools/find.d.ts.map +1 -1
  48. package/dist/core/tools/find.js +1 -1
  49. package/dist/core/tools/find.js.map +1 -1
  50. package/dist/core/tools/grep.d.ts.map +1 -1
  51. package/dist/core/tools/grep.js +1 -1
  52. package/dist/core/tools/grep.js.map +1 -1
  53. package/dist/core/tools/index.d.ts +1 -1
  54. package/dist/core/tools/index.d.ts.map +1 -1
  55. package/dist/core/tools/index.js +5 -5
  56. package/dist/core/tools/index.js.map +1 -1
  57. package/dist/core/tools/websearch.d.ts.map +1 -1
  58. package/dist/core/tools/websearch.js +3 -3
  59. package/dist/core/tools/websearch.js.map +1 -1
  60. package/dist/extensions/gitnexus/config.d.ts +15 -1
  61. package/dist/extensions/gitnexus/config.d.ts.map +1 -1
  62. package/dist/extensions/gitnexus/config.js +86 -2
  63. package/dist/extensions/gitnexus/config.js.map +1 -1
  64. package/dist/extensions/gitnexus/gateway.d.ts +6 -6
  65. package/dist/extensions/gitnexus/gateway.d.ts.map +1 -1
  66. package/dist/extensions/gitnexus/gateway.js +4 -4
  67. package/dist/extensions/gitnexus/gateway.js.map +1 -1
  68. package/dist/extensions/gitnexus/index.d.ts.map +1 -1
  69. package/dist/extensions/gitnexus/index.js +6 -4
  70. package/dist/extensions/gitnexus/index.js.map +1 -1
  71. package/dist/extensions/gitnexus/observatory.d.ts.map +1 -1
  72. package/dist/extensions/gitnexus/observatory.js +2 -2
  73. package/dist/extensions/gitnexus/observatory.js.map +1 -1
  74. package/dist/extensions/index.d.ts.map +1 -1
  75. package/dist/extensions/index.js +3 -0
  76. package/dist/extensions/index.js.map +1 -1
  77. package/dist/extensions/mcp/abort.d.ts +3 -0
  78. package/dist/extensions/mcp/abort.d.ts.map +1 -0
  79. package/dist/extensions/mcp/abort.js +41 -0
  80. package/dist/extensions/mcp/abort.js.map +1 -0
  81. package/dist/extensions/mcp/agent-dir.d.ts +6 -0
  82. package/dist/extensions/mcp/agent-dir.d.ts.map +1 -0
  83. package/dist/extensions/mcp/agent-dir.js +18 -0
  84. package/dist/extensions/mcp/agent-dir.js.map +1 -0
  85. package/dist/extensions/mcp/agent-plugin-loader.d.ts +9 -0
  86. package/dist/extensions/mcp/agent-plugin-loader.d.ts.map +1 -0
  87. package/dist/extensions/mcp/agent-plugin-loader.js +384 -0
  88. package/dist/extensions/mcp/agent-plugin-loader.js.map +1 -0
  89. package/dist/extensions/mcp/app-bridge.bundle.js +67 -0
  90. package/dist/extensions/mcp/commands.d.ts +47 -0
  91. package/dist/extensions/mcp/commands.d.ts.map +1 -0
  92. package/dist/extensions/mcp/commands.js +821 -0
  93. package/dist/extensions/mcp/commands.js.map +1 -0
  94. package/dist/extensions/mcp/config.d.ts +123 -38
  95. package/dist/extensions/mcp/config.d.ts.map +1 -1
  96. package/dist/extensions/mcp/config.js +1035 -70
  97. package/dist/extensions/mcp/config.js.map +1 -1
  98. package/dist/extensions/mcp/consent-manager.d.ts +14 -0
  99. package/dist/extensions/mcp/consent-manager.d.ts.map +1 -0
  100. package/dist/extensions/mcp/consent-manager.js +60 -0
  101. package/dist/extensions/mcp/consent-manager.js.map +1 -0
  102. package/dist/extensions/mcp/direct-tools.d.ts +11 -0
  103. package/dist/extensions/mcp/direct-tools.d.ts.map +1 -0
  104. package/dist/extensions/mcp/direct-tools.js +562 -0
  105. package/dist/extensions/mcp/direct-tools.js.map +1 -0
  106. package/dist/extensions/mcp/elicitation-handler.d.ts +18 -0
  107. package/dist/extensions/mcp/elicitation-handler.d.ts.map +1 -0
  108. package/dist/extensions/mcp/elicitation-handler.js +320 -0
  109. package/dist/extensions/mcp/elicitation-handler.js.map +1 -0
  110. package/dist/extensions/mcp/error-signal.d.ts +16 -0
  111. package/dist/extensions/mcp/error-signal.d.ts.map +1 -0
  112. package/dist/extensions/mcp/error-signal.js +22 -0
  113. package/dist/extensions/mcp/error-signal.js.map +1 -0
  114. package/dist/extensions/mcp/errors.d.ts +104 -0
  115. package/dist/extensions/mcp/errors.d.ts.map +1 -0
  116. package/dist/extensions/mcp/errors.js +171 -0
  117. package/dist/extensions/mcp/errors.js.map +1 -0
  118. package/dist/extensions/mcp/glimpse-ui.d.ts +10 -0
  119. package/dist/extensions/mcp/glimpse-ui.d.ts.map +1 -0
  120. package/dist/extensions/mcp/glimpse-ui.js +67 -0
  121. package/dist/extensions/mcp/glimpse-ui.js.map +1 -0
  122. package/dist/extensions/mcp/host-html-template.d.ts +17 -0
  123. package/dist/extensions/mcp/host-html-template.d.ts.map +1 -0
  124. package/dist/extensions/mcp/host-html-template.js +442 -0
  125. package/dist/extensions/mcp/host-html-template.js.map +1 -0
  126. package/dist/extensions/mcp/index.d.ts +20 -15
  127. package/dist/extensions/mcp/index.d.ts.map +1 -1
  128. package/dist/extensions/mcp/index.js +1113 -82
  129. package/dist/extensions/mcp/index.js.map +1 -1
  130. package/dist/extensions/mcp/init.d.ts +28 -0
  131. package/dist/extensions/mcp/init.d.ts.map +1 -0
  132. package/dist/extensions/mcp/init.js +631 -0
  133. package/dist/extensions/mcp/init.js.map +1 -0
  134. package/dist/extensions/mcp/json-schema-validator.d.ts +3 -0
  135. package/dist/extensions/mcp/json-schema-validator.d.ts.map +1 -0
  136. package/dist/extensions/mcp/json-schema-validator.js +53 -0
  137. package/dist/extensions/mcp/json-schema-validator.js.map +1 -0
  138. package/dist/extensions/mcp/lifecycle.d.ts +56 -0
  139. package/dist/extensions/mcp/lifecycle.d.ts.map +1 -0
  140. package/dist/extensions/mcp/lifecycle.js +395 -0
  141. package/dist/extensions/mcp/lifecycle.js.map +1 -0
  142. package/dist/extensions/mcp/logger.d.ts +52 -0
  143. package/dist/extensions/mcp/logger.d.ts.map +1 -0
  144. package/dist/extensions/mcp/logger.js +134 -0
  145. package/dist/extensions/mcp/logger.js.map +1 -0
  146. package/dist/extensions/mcp/mcp-auth-flow.d.ts +117 -0
  147. package/dist/extensions/mcp/mcp-auth-flow.d.ts.map +1 -0
  148. package/dist/extensions/mcp/mcp-auth-flow.js +869 -0
  149. package/dist/extensions/mcp/mcp-auth-flow.js.map +1 -0
  150. package/dist/extensions/mcp/mcp-auth.d.ts +162 -0
  151. package/dist/extensions/mcp/mcp-auth.d.ts.map +1 -0
  152. package/dist/extensions/mcp/mcp-auth.js +897 -0
  153. package/dist/extensions/mcp/mcp-auth.js.map +1 -0
  154. package/dist/extensions/mcp/mcp-bearer-store.d.ts +25 -0
  155. package/dist/extensions/mcp/mcp-bearer-store.d.ts.map +1 -0
  156. package/dist/extensions/mcp/mcp-bearer-store.js +350 -0
  157. package/dist/extensions/mcp/mcp-bearer-store.js.map +1 -0
  158. package/dist/extensions/mcp/mcp-callback-server.d.ts +54 -0
  159. package/dist/extensions/mcp/mcp-callback-server.d.ts.map +1 -0
  160. package/dist/extensions/mcp/mcp-callback-server.js +454 -0
  161. package/dist/extensions/mcp/mcp-callback-server.js.map +1 -0
  162. package/dist/extensions/mcp/mcp-code.d.ts +48 -0
  163. package/dist/extensions/mcp/mcp-code.d.ts.map +1 -0
  164. package/dist/extensions/mcp/mcp-code.js +358 -0
  165. package/dist/extensions/mcp/mcp-code.js.map +1 -0
  166. package/dist/extensions/mcp/mcp-keyring-helper.cjs +89 -0
  167. package/dist/extensions/mcp/mcp-oauth-provider.d.ts +135 -0
  168. package/dist/extensions/mcp/mcp-oauth-provider.d.ts.map +1 -0
  169. package/dist/extensions/mcp/mcp-oauth-provider.js +491 -0
  170. package/dist/extensions/mcp/mcp-oauth-provider.js.map +1 -0
  171. package/dist/extensions/mcp/mcp-output-guard.d.ts +59 -0
  172. package/dist/extensions/mcp/mcp-output-guard.d.ts.map +1 -0
  173. package/dist/extensions/mcp/mcp-output-guard.js +343 -0
  174. package/dist/extensions/mcp/mcp-output-guard.js.map +1 -0
  175. package/dist/extensions/mcp/mcp-panel.d.ts +71 -0
  176. package/dist/extensions/mcp/mcp-panel.d.ts.map +1 -0
  177. package/dist/extensions/mcp/mcp-panel.js +973 -0
  178. package/dist/extensions/mcp/mcp-panel.js.map +1 -0
  179. package/dist/extensions/mcp/mcp-probe.d.ts +7 -0
  180. package/dist/extensions/mcp/mcp-probe.d.ts.map +1 -0
  181. package/dist/extensions/mcp/mcp-probe.js +155 -0
  182. package/dist/extensions/mcp/mcp-probe.js.map +1 -0
  183. package/dist/extensions/mcp/mcp-script-worker.mjs +130 -0
  184. package/dist/extensions/mcp/mcp-setup-panel.d.ts +146 -0
  185. package/dist/extensions/mcp/mcp-setup-panel.d.ts.map +1 -0
  186. package/dist/extensions/mcp/mcp-setup-panel.js +1104 -0
  187. package/dist/extensions/mcp/mcp-setup-panel.js.map +1 -0
  188. package/dist/extensions/mcp/mcp-status.d.ts +12 -0
  189. package/dist/extensions/mcp/mcp-status.d.ts.map +1 -0
  190. package/dist/extensions/mcp/mcp-status.js +98 -0
  191. package/dist/extensions/mcp/mcp-status.js.map +1 -0
  192. package/dist/extensions/mcp/mcp-trace.d.ts +95 -0
  193. package/dist/extensions/mcp/mcp-trace.d.ts.map +1 -0
  194. package/dist/extensions/mcp/mcp-trace.js +252 -0
  195. package/dist/extensions/mcp/mcp-trace.js.map +1 -0
  196. package/dist/extensions/mcp/metadata-cache.d.ts +20 -0
  197. package/dist/extensions/mcp/metadata-cache.d.ts.map +1 -0
  198. package/dist/extensions/mcp/metadata-cache.js +318 -0
  199. package/dist/extensions/mcp/metadata-cache.js.map +1 -0
  200. package/dist/extensions/mcp/npx-resolver.d.ts +7 -0
  201. package/dist/extensions/mcp/npx-resolver.d.ts.map +1 -0
  202. package/dist/extensions/mcp/npx-resolver.js +505 -0
  203. package/dist/extensions/mcp/npx-resolver.js.map +1 -0
  204. package/dist/extensions/mcp/oauth.d.ts +21 -0
  205. package/dist/extensions/mcp/oauth.d.ts.map +1 -0
  206. package/dist/extensions/mcp/oauth.js +15 -0
  207. package/dist/extensions/mcp/oauth.js.map +1 -0
  208. package/dist/extensions/mcp/onboarding-state.d.ts +13 -0
  209. package/dist/extensions/mcp/onboarding-state.d.ts.map +1 -0
  210. package/dist/extensions/mcp/onboarding-state.js +65 -0
  211. package/dist/extensions/mcp/onboarding-state.js.map +1 -0
  212. package/dist/extensions/mcp/package-mcp-loader.d.ts +3 -0
  213. package/dist/extensions/mcp/package-mcp-loader.d.ts.map +1 -0
  214. package/dist/extensions/mcp/package-mcp-loader.js +170 -0
  215. package/dist/extensions/mcp/package-mcp-loader.js.map +1 -0
  216. package/dist/extensions/mcp/panel-keys.d.ts +21 -0
  217. package/dist/extensions/mcp/panel-keys.d.ts.map +1 -0
  218. package/dist/extensions/mcp/panel-keys.js +29 -0
  219. package/dist/extensions/mcp/panel-keys.js.map +1 -0
  220. package/dist/extensions/mcp/prompts.d.ts +63 -0
  221. package/dist/extensions/mcp/prompts.d.ts.map +1 -0
  222. package/dist/extensions/mcp/prompts.js +297 -0
  223. package/dist/extensions/mcp/prompts.js.map +1 -0
  224. package/dist/extensions/mcp/proxy-modes.d.ts +15 -0
  225. package/dist/extensions/mcp/proxy-modes.d.ts.map +1 -0
  226. package/dist/extensions/mcp/proxy-modes.js +1421 -0
  227. package/dist/extensions/mcp/proxy-modes.js.map +1 -0
  228. package/dist/extensions/mcp/request-headers-command.d.ts +11 -0
  229. package/dist/extensions/mcp/request-headers-command.d.ts.map +1 -0
  230. package/dist/extensions/mcp/request-headers-command.js +334 -0
  231. package/dist/extensions/mcp/request-headers-command.js.map +1 -0
  232. package/dist/extensions/mcp/resource-tools.d.ts +2 -0
  233. package/dist/extensions/mcp/resource-tools.d.ts.map +1 -0
  234. package/dist/extensions/mcp/resource-tools.js +15 -0
  235. package/dist/extensions/mcp/resource-tools.js.map +1 -0
  236. package/dist/extensions/mcp/runtime-owner.d.ts +14 -0
  237. package/dist/extensions/mcp/runtime-owner.d.ts.map +1 -0
  238. package/dist/extensions/mcp/runtime-owner.js +84 -0
  239. package/dist/extensions/mcp/runtime-owner.js.map +1 -0
  240. package/dist/extensions/mcp/sampling-handler.d.ts +18 -0
  241. package/dist/extensions/mcp/sampling-handler.d.ts.map +1 -0
  242. package/dist/extensions/mcp/sampling-handler.js +229 -0
  243. package/dist/extensions/mcp/sampling-handler.js.map +1 -0
  244. package/dist/extensions/mcp/search-ranking.d.ts +25 -0
  245. package/dist/extensions/mcp/search-ranking.d.ts.map +1 -0
  246. package/dist/extensions/mcp/search-ranking.js +234 -0
  247. package/dist/extensions/mcp/search-ranking.js.map +1 -0
  248. package/dist/extensions/mcp/server-manager.d.ts +116 -0
  249. package/dist/extensions/mcp/server-manager.d.ts.map +1 -0
  250. package/dist/extensions/mcp/server-manager.js +1052 -0
  251. package/dist/extensions/mcp/server-manager.js.map +1 -0
  252. package/dist/extensions/mcp/session-recovery.d.ts +27 -0
  253. package/dist/extensions/mcp/session-recovery.d.ts.map +1 -0
  254. package/dist/extensions/mcp/session-recovery.js +140 -0
  255. package/dist/extensions/mcp/session-recovery.js.map +1 -0
  256. package/dist/extensions/mcp/skills/mcp-scripting/SKILL.md +38 -0
  257. package/dist/extensions/mcp/state.d.ts +62 -0
  258. package/dist/extensions/mcp/state.d.ts.map +1 -0
  259. package/dist/extensions/mcp/state.js +2 -0
  260. package/dist/extensions/mcp/state.js.map +1 -0
  261. package/dist/extensions/mcp/tool-approval.d.ts +11 -0
  262. package/dist/extensions/mcp/tool-approval.d.ts.map +1 -0
  263. package/dist/extensions/mcp/tool-approval.js +156 -0
  264. package/dist/extensions/mcp/tool-approval.js.map +1 -0
  265. package/dist/extensions/mcp/tool-metadata.d.ts +11 -0
  266. package/dist/extensions/mcp/tool-metadata.d.ts.map +1 -0
  267. package/dist/extensions/mcp/tool-metadata.js +264 -0
  268. package/dist/extensions/mcp/tool-metadata.js.map +1 -0
  269. package/dist/extensions/mcp/tool-registrar.d.ts +21 -0
  270. package/dist/extensions/mcp/tool-registrar.d.ts.map +1 -0
  271. package/dist/extensions/mcp/tool-registrar.js +229 -0
  272. package/dist/extensions/mcp/tool-registrar.js.map +1 -0
  273. package/dist/extensions/mcp/tool-result-renderer.d.ts +83 -0
  274. package/dist/extensions/mcp/tool-result-renderer.d.ts.map +1 -0
  275. package/dist/extensions/mcp/tool-result-renderer.js +345 -0
  276. package/dist/extensions/mcp/tool-result-renderer.js.map +1 -0
  277. package/dist/extensions/mcp/ts-shape.d.ts +3 -0
  278. package/dist/extensions/mcp/ts-shape.d.ts.map +1 -0
  279. package/dist/extensions/mcp/ts-shape.js +160 -0
  280. package/dist/extensions/mcp/ts-shape.js.map +1 -0
  281. package/dist/extensions/mcp/types.d.ts +521 -0
  282. package/dist/extensions/mcp/types.d.ts.map +1 -0
  283. package/dist/extensions/mcp/types.js +294 -0
  284. package/dist/extensions/mcp/types.js.map +1 -0
  285. package/dist/extensions/mcp/ui-app-bridge-helpers.d.ts +7 -0
  286. package/dist/extensions/mcp/ui-app-bridge-helpers.d.ts.map +1 -0
  287. package/dist/extensions/mcp/ui-app-bridge-helpers.js +37 -0
  288. package/dist/extensions/mcp/ui-app-bridge-helpers.js.map +1 -0
  289. package/dist/extensions/mcp/ui-resource-handler.d.ts +18 -0
  290. package/dist/extensions/mcp/ui-resource-handler.d.ts.map +1 -0
  291. package/dist/extensions/mcp/ui-resource-handler.js +210 -0
  292. package/dist/extensions/mcp/ui-resource-handler.js.map +1 -0
  293. package/dist/extensions/mcp/ui-server.d.ts +52 -0
  294. package/dist/extensions/mcp/ui-server.d.ts.map +1 -0
  295. package/dist/extensions/mcp/ui-server.js +740 -0
  296. package/dist/extensions/mcp/ui-server.js.map +1 -0
  297. package/dist/extensions/mcp/ui-session.d.ts +40 -0
  298. package/dist/extensions/mcp/ui-session.d.ts.map +1 -0
  299. package/dist/extensions/mcp/ui-session.js +477 -0
  300. package/dist/extensions/mcp/ui-session.js.map +1 -0
  301. package/dist/extensions/mcp/ui-stream-types.d.ts +105 -0
  302. package/dist/extensions/mcp/ui-stream-types.d.ts.map +1 -0
  303. package/dist/extensions/mcp/ui-stream-types.js +61 -0
  304. package/dist/extensions/mcp/ui-stream-types.js.map +1 -0
  305. package/dist/extensions/mcp/ui-tool-visibility.d.ts +5 -0
  306. package/dist/extensions/mcp/ui-tool-visibility.d.ts.map +1 -0
  307. package/dist/extensions/mcp/ui-tool-visibility.js +27 -0
  308. package/dist/extensions/mcp/ui-tool-visibility.js.map +1 -0
  309. package/dist/extensions/mcp/unix-socket-transport.d.ts +15 -0
  310. package/dist/extensions/mcp/unix-socket-transport.d.ts.map +1 -0
  311. package/dist/extensions/mcp/unix-socket-transport.js +85 -0
  312. package/dist/extensions/mcp/unix-socket-transport.js.map +1 -0
  313. package/dist/extensions/mcp/utils.d.ts +32 -0
  314. package/dist/extensions/mcp/utils.d.ts.map +1 -0
  315. package/dist/extensions/mcp/utils.js +336 -0
  316. package/dist/extensions/mcp/utils.js.map +1 -0
  317. package/dist/extensions/subagent/index.d.ts +62 -1
  318. package/dist/extensions/subagent/index.d.ts.map +1 -1
  319. package/dist/extensions/subagent/index.js +195 -9
  320. package/dist/extensions/subagent/index.js.map +1 -1
  321. package/dist/extensions/subagent-render.d.ts +3 -0
  322. package/dist/extensions/subagent-render.d.ts.map +1 -0
  323. package/dist/extensions/subagent-render.js +39 -0
  324. package/dist/extensions/subagent-render.js.map +1 -0
  325. package/dist/extensions/web-access.d.ts.map +1 -1
  326. package/dist/extensions/web-access.js +1 -1
  327. package/dist/extensions/web-access.js.map +1 -1
  328. package/dist/index.d.ts +2 -0
  329. package/dist/index.d.ts.map +1 -1
  330. package/dist/index.js +20992 -2003
  331. package/dist/index.js.map +1 -1
  332. package/dist/mcp-keyring-helper.cjs +89 -0
  333. package/dist/mcp-script-worker.mjs +130 -0
  334. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  335. package/dist/modes/interactive/components/model-selector.js +1 -1
  336. package/dist/modes/interactive/components/model-selector.js.map +1 -1
  337. package/dist/modes/interactive/components/response-separator.d.ts +1 -1
  338. package/dist/modes/interactive/components/response-separator.d.ts.map +1 -1
  339. package/dist/modes/interactive/components/response-separator.js +1 -1
  340. package/dist/modes/interactive/components/response-separator.js.map +1 -1
  341. package/dist/modes/interactive/components/tool-execution.d.ts +3 -2
  342. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  343. package/dist/modes/interactive/components/tool-execution.js +12 -2
  344. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  345. package/dist/modes/interactive/interactive-mode.d.ts +2 -0
  346. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  347. package/dist/modes/interactive/interactive-mode.js +14 -8
  348. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  349. package/dist/rpc-entry.js +20866 -1882
  350. package/dist/skills/mcp-scripting/SKILL.md +38 -0
  351. package/dist/utils/hidden-spawn.d.ts +29 -0
  352. package/dist/utils/hidden-spawn.d.ts.map +1 -0
  353. package/dist/utils/hidden-spawn.js +101 -0
  354. package/dist/utils/hidden-spawn.js.map +1 -0
  355. package/dist/vendor/ai.js +110 -35
  356. package/docs/mcp.md +178 -0
  357. package/docs/pi-subagents.md +75 -0
  358. package/docs/quickstart.md +1 -1
  359. package/docs/usage.md +2 -1
  360. package/examples/extensions/pi-subagents-worker-policy.ts +86 -0
  361. package/native/win32/build.mjs +95 -0
  362. package/native/win32/prebuilds/win32-x64/hidden-spawn.exe +0 -0
  363. package/native/win32/src/hidden-spawn.c +157 -0
  364. package/npm-shrinkwrap.json +728 -6
  365. package/package.json +21 -6
  366. package/dist/extensions/mcp/client.d.ts +0 -86
  367. package/dist/extensions/mcp/client.d.ts.map +0 -1
  368. package/dist/extensions/mcp/client.js +0 -275
  369. package/dist/extensions/mcp/client.js.map +0 -1
@@ -0,0 +1,1104 @@
1
+ import { matchesKey, truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
2
+ import { getConfigDirName } from "./agent-dir.js";
3
+ import { KNOWN_SERVER_PRESETS, } from "./config.js";
4
+ import { createPanelKeys } from "./panel-keys.js";
5
+ const DEFAULT_THEME = {
6
+ border: "2",
7
+ title: "36",
8
+ selected: "32",
9
+ hint: "2",
10
+ success: "32",
11
+ warning: "33",
12
+ muted: "2;3",
13
+ };
14
+ const MIN_PANEL_WIDTH = 24;
15
+ const COMPACT_WIDTH = 60;
16
+ const COMPACT_ACTION_ROWS = 7;
17
+ const DESKTOP_PREVIEW_WIDTH = 74;
18
+ function fg(code, text) {
19
+ return code ? `\x1b[${code}m${text}\x1b[0m` : text;
20
+ }
21
+ function wrapText(text, width) {
22
+ if (width <= 8)
23
+ return [text];
24
+ const words = text.split(/\s+/).filter(Boolean);
25
+ const lines = [];
26
+ let current = "";
27
+ for (const word of words) {
28
+ const candidate = current ? `${current} ${word}` : word;
29
+ if (visibleWidth(candidate) <= width) {
30
+ current = candidate;
31
+ continue;
32
+ }
33
+ if (current)
34
+ lines.push(current);
35
+ current = word;
36
+ }
37
+ if (current)
38
+ lines.push(current);
39
+ return lines.length > 0 ? lines : [""];
40
+ }
41
+ const SETUP_TABS = [
42
+ { id: "presets", label: "Presets" },
43
+ { id: "custom", label: "Custom" },
44
+ { id: "installed", label: "Installed" },
45
+ ];
46
+ /** Server-type tag shown next to a preset row; color must survive any terminal, so it's always plain colored text. */
47
+ /** Color a JSON-ish line: keys cyan, string values green, braces/brackets dim. Safe only for short, unwrapped lines. */
48
+ function colorizeJsonLine(line) {
49
+ let result = "";
50
+ let i = 0;
51
+ while (i < line.length) {
52
+ const ch = line[i];
53
+ if (ch === '"') {
54
+ let end = i + 1;
55
+ while (end < line.length && line[end] !== '"')
56
+ end++;
57
+ const str = line.slice(i, Math.min(end + 1, line.length));
58
+ const isKey = line
59
+ .slice(i + str.length)
60
+ .trimStart()
61
+ .startsWith(":");
62
+ result += fg(isKey ? "36" : "32", str);
63
+ i += str.length;
64
+ continue;
65
+ }
66
+ if (ch === "{" || ch === "}" || ch === "[" || ch === "]") {
67
+ result += fg("2", ch);
68
+ i++;
69
+ continue;
70
+ }
71
+ result += ch;
72
+ i++;
73
+ }
74
+ return result;
75
+ }
76
+ /** Diff-line color by prefix, or undefined for a context line that needs no color. */
77
+ function diffLineColor(line) {
78
+ if (line.startsWith("+++") || line.startsWith("---"))
79
+ return "2";
80
+ if (line.startsWith("+"))
81
+ return "32";
82
+ if (line.startsWith("-"))
83
+ return "31";
84
+ return undefined;
85
+ }
86
+ /** Color `/mcp add` command syntax: the command itself cyan, --flags yellow, <placeholders> dim italic. Leaves spacing untouched. */
87
+ function colorizeCommandLine(line) {
88
+ return line.replace(/(\/mcp add\b|--[a-zA-Z-]+|<[^>]+>|\[[^\]]+\])/g, (match) => {
89
+ if (match.startsWith("<") || match.startsWith("["))
90
+ return fg("2;3", match);
91
+ if (match.startsWith("--"))
92
+ return fg("33", match);
93
+ return fg("36", match);
94
+ });
95
+ }
96
+ /** Per-preset accent color for the name, so the list scans by identity, not just by type. 256-color codes, distinct from the type-tag palette above. */
97
+ const PRESET_ACCENT_COLORS = {
98
+ deepwiki: "38;5;111", // soft blue
99
+ context7: "38;5;80", // teal
100
+ notion: "38;5;141", // purple
101
+ github: "38;5;215", // orange
102
+ "chrome-devtools": "38;5;211", // pink
103
+ };
104
+ function presetAccent(preset) {
105
+ return PRESET_ACCENT_COLORS[preset.id] ?? "37";
106
+ }
107
+ export class McpSetupPanel {
108
+ screen;
109
+ activeTab;
110
+ actionCursor = 0;
111
+ importCursor = 0;
112
+ pathCursor = 0;
113
+ advancedCursor = 0;
114
+ selectedImports = new Set();
115
+ busy = false;
116
+ notice = null;
117
+ pendingAction;
118
+ maxHeight;
119
+ tui;
120
+ t = DEFAULT_THEME;
121
+ keys;
122
+ inactivityTimeout = null;
123
+ discovery;
124
+ callbacks;
125
+ options;
126
+ /** Mutable copy of options.installedServers, updated in-place after a successful add so the Installed tab and Presets "active" tag reflect it immediately, without needing to close and reopen the panel. */
127
+ installedServers;
128
+ done;
129
+ static INACTIVITY_MS = 60_000;
130
+ constructor(discovery, callbacks, options, tui, done) {
131
+ this.discovery = discovery;
132
+ this.callbacks = callbacks;
133
+ this.options = options;
134
+ this.installedServers = [...options.installedServers];
135
+ this.done = done;
136
+ this.tui = tui;
137
+ this.keys = createPanelKeys(options.keybindings);
138
+ this.screen = options.mode;
139
+ this.activeTab = "presets";
140
+ for (const entry of discovery.imports) {
141
+ this.selectedImports.add(entry.kind);
142
+ }
143
+ this.resetInactivityTimeout();
144
+ }
145
+ resetInactivityTimeout() {
146
+ if (this.inactivityTimeout)
147
+ clearTimeout(this.inactivityTimeout);
148
+ this.inactivityTimeout = setTimeout(() => {
149
+ this.cleanup();
150
+ this.done();
151
+ }, McpSetupPanel.INACTIVITY_MS);
152
+ }
153
+ cleanup() {
154
+ if (this.inactivityTimeout) {
155
+ clearTimeout(this.inactivityTimeout);
156
+ this.inactivityTimeout = null;
157
+ }
158
+ }
159
+ /** The old "Setup" tab's contents — now a sub-screen opened from Custom's "Advanced options...", not a permanent top-level tab. */
160
+ getAdvancedActions() {
161
+ const actions = [];
162
+ if (this.discovery.imports.length > 0) {
163
+ actions.push({
164
+ id: "adopt-imports",
165
+ label: "Adopt detected compatibility imports",
166
+ description: `Choose which host-specific MCP configs Nexus should import into its own override file. ${this.discovery.imports.length} source${this.discovery.imports.length === 1 ? "" : "s"} found.`,
167
+ });
168
+ }
169
+ actions.push({
170
+ id: "view-example",
171
+ label: "View example `.nexus/mcp.json`",
172
+ description: "Preview a working Nexus MCP config you can paste or adapt.",
173
+ });
174
+ if (!this.discovery.sources.some((source) => source.id === "nexus-project" && source.exists)) {
175
+ actions.push({
176
+ id: "scaffold-project",
177
+ label: "Scaffold project `.nexus/mcp.json`",
178
+ description: "Write a minimal Nexus project MCP config, then reload Nexus.",
179
+ });
180
+ }
181
+ actions.push({
182
+ id: "show-precedence",
183
+ label: "Explain config precedence",
184
+ description: "Show the read order and where Nexus writes compatibility settings.",
185
+ });
186
+ if (this.getDetectedPaths().length > 0) {
187
+ actions.push({
188
+ id: "open-paths",
189
+ label: "Open detected config paths",
190
+ description: "Browse the actual config files that Nexus discovered on this machine.",
191
+ });
192
+ }
193
+ return actions;
194
+ }
195
+ getPresetsTabActions() {
196
+ const actions = [];
197
+ for (const preset of KNOWN_SERVER_PRESETS) {
198
+ actions.push({ id: "add-known-server", label: preset.name, description: preset.summary, preset });
199
+ }
200
+ if (!this.discovery.repoPrompt.configured &&
201
+ this.discovery.repoPrompt.executablePath &&
202
+ this.discovery.repoPrompt.targetPath &&
203
+ this.discovery.repoPrompt.entry &&
204
+ this.discovery.repoPrompt.serverName) {
205
+ actions.push({
206
+ id: "add-repoprompt",
207
+ label: "Add RepoPrompt to Nexus MCP config",
208
+ description: "Write an MCP entry for RepoPrompt to the recommended Nexus target, then reload MCP in-session.",
209
+ });
210
+ }
211
+ return actions;
212
+ }
213
+ getCustomTabActions() {
214
+ return [
215
+ {
216
+ id: "add-custom-stdio",
217
+ label: "Add a local server (stdio)...",
218
+ description: "Runs a command on your machine, e.g. an npx-installed MCP server.",
219
+ },
220
+ {
221
+ id: "add-custom-url",
222
+ label: "Add a remote server (URL)...",
223
+ description: "Connects to an MCP server over HTTP.",
224
+ },
225
+ {
226
+ id: "open-advanced",
227
+ label: "Advanced options...",
228
+ description: "Import from other tools, scaffold an empty config, or check how config precedence works.",
229
+ },
230
+ ];
231
+ }
232
+ getInstalledTabActions() {
233
+ const servers = this.installedServers;
234
+ if (servers.length === 0) {
235
+ return [
236
+ {
237
+ id: "view-installed-server",
238
+ label: "No servers configured yet.",
239
+ description: "Add one from the Presets or Custom tab.",
240
+ },
241
+ ];
242
+ }
243
+ return servers.map((server) => ({
244
+ id: "view-installed-server",
245
+ label: server.name,
246
+ description: server.disabled
247
+ ? "Disabled in the effective config."
248
+ : server.authCapable && !server.signedIn
249
+ ? "Active, but not signed in yet."
250
+ : "Active in the effective config.",
251
+ installed: server,
252
+ }));
253
+ }
254
+ /**
255
+ * Single-glyph prefix instead of a tag chip: ✓ for a preset you already have
256
+ * (dim if you've disabled it), ✓/⊘ for an Installed-tab row's active state.
257
+ * No mark at all for a preset you don't have yet, to keep the list scannable.
258
+ */
259
+ getRowMark(action) {
260
+ if (action.preset) {
261
+ const installed = this.installedServers.find((server) => server.name === action.preset.id);
262
+ return installed ? fg(installed.disabled ? this.t.muted : this.t.success, "✓ ") : " ";
263
+ }
264
+ if (action.installed) {
265
+ if (action.installed.disabled)
266
+ return fg(this.t.muted, "⊘ ");
267
+ if (action.installed.authCapable && !action.installed.signedIn)
268
+ return fg(this.t.warning, "! ");
269
+ return fg(this.t.success, "✓ ");
270
+ }
271
+ return "";
272
+ }
273
+ getActions() {
274
+ switch (this.activeTab) {
275
+ case "presets":
276
+ return this.getPresetsTabActions();
277
+ case "custom":
278
+ return this.getCustomTabActions();
279
+ case "installed":
280
+ return this.getInstalledTabActions();
281
+ }
282
+ }
283
+ cycleTab(direction) {
284
+ const order = SETUP_TABS.map((tab) => tab.id);
285
+ const index = order.indexOf(this.activeTab);
286
+ this.activeTab = order[(index + direction + order.length) % order.length];
287
+ this.actionCursor = 0;
288
+ this.notice = null;
289
+ this.tui.requestRender();
290
+ }
291
+ getDetectedPaths() {
292
+ const paths = [
293
+ ...this.discovery.sources.filter((source) => source.exists).map((source) => source.path),
294
+ ...this.discovery.imports.map((entry) => entry.path),
295
+ ];
296
+ return [...new Set(paths)];
297
+ }
298
+ handleInput(data) {
299
+ this.resetInactivityTimeout();
300
+ if (!this.busy)
301
+ this.notice = null;
302
+ if (matchesKey(data, "ctrl+c")) {
303
+ this.cleanup();
304
+ this.done();
305
+ return;
306
+ }
307
+ if (matchesKey(data, "escape")) {
308
+ if (this.screen === "imports" || this.screen === "paths") {
309
+ // Both are only reachable from the Advanced sub-screen now — back out one level, not all the way to the tabs.
310
+ this.screen = "advanced";
311
+ this.tui.requestRender();
312
+ return;
313
+ }
314
+ if (this.screen === "confirm-preset" || this.screen === "advanced") {
315
+ this.pendingAction = undefined;
316
+ this.screen = this.discovery.hasAnyConfig ? "setup" : "empty";
317
+ this.tui.requestRender();
318
+ return;
319
+ }
320
+ this.cleanup();
321
+ this.done();
322
+ return;
323
+ }
324
+ if (this.busy)
325
+ return;
326
+ if (this.screen === "imports") {
327
+ this.handleImportsInput(data);
328
+ return;
329
+ }
330
+ if (this.screen === "paths") {
331
+ this.handlePathsInput(data);
332
+ return;
333
+ }
334
+ if (this.screen === "confirm-preset") {
335
+ this.handleConfirmPresetInput(data);
336
+ return;
337
+ }
338
+ if (this.screen === "advanced") {
339
+ this.handleAdvancedInput(data);
340
+ return;
341
+ }
342
+ if (matchesKey(data, "left")) {
343
+ this.cycleTab(-1);
344
+ return;
345
+ }
346
+ if (matchesKey(data, "right")) {
347
+ this.cycleTab(1);
348
+ return;
349
+ }
350
+ const actions = this.getActions();
351
+ if (this.keys.selectUp(data)) {
352
+ this.actionCursor = Math.max(0, this.actionCursor - 1);
353
+ this.tui.requestRender();
354
+ return;
355
+ }
356
+ if (this.keys.selectDown(data)) {
357
+ this.actionCursor = Math.min(actions.length - 1, this.actionCursor + 1);
358
+ this.tui.requestRender();
359
+ return;
360
+ }
361
+ if (data === "o" || data === "O") {
362
+ this.openSelectedDocs();
363
+ return;
364
+ }
365
+ if ((data === "a" || data === "A") && this.activeTab === "installed") {
366
+ this.authenticateSelected();
367
+ return;
368
+ }
369
+ if (this.keys.selectConfirm(data)) {
370
+ const selected = this.getSelectedAction();
371
+ if (selected)
372
+ void this.runAction(selected);
373
+ }
374
+ }
375
+ handleImportsInput(data) {
376
+ const imports = this.discovery.imports;
377
+ if (this.keys.selectUp(data)) {
378
+ this.importCursor = Math.max(0, this.importCursor - 1);
379
+ this.tui.requestRender();
380
+ return;
381
+ }
382
+ if (this.keys.selectDown(data)) {
383
+ this.importCursor = Math.min(imports.length - 1, this.importCursor + 1);
384
+ this.tui.requestRender();
385
+ return;
386
+ }
387
+ if (matchesKey(data, "space")) {
388
+ const current = imports[this.importCursor];
389
+ if (!current)
390
+ return;
391
+ if (this.selectedImports.has(current.kind)) {
392
+ this.selectedImports.delete(current.kind);
393
+ }
394
+ else {
395
+ this.selectedImports.add(current.kind);
396
+ }
397
+ this.tui.requestRender();
398
+ return;
399
+ }
400
+ if (this.keys.selectConfirm(data)) {
401
+ void this.applySelectedImports();
402
+ }
403
+ }
404
+ handlePathsInput(data) {
405
+ const paths = this.getDetectedPaths();
406
+ if (this.keys.selectUp(data)) {
407
+ this.pathCursor = Math.max(0, this.pathCursor - 1);
408
+ this.tui.requestRender();
409
+ return;
410
+ }
411
+ if (this.keys.selectDown(data)) {
412
+ this.pathCursor = Math.min(paths.length - 1, this.pathCursor + 1);
413
+ this.tui.requestRender();
414
+ return;
415
+ }
416
+ if (this.keys.selectConfirm(data)) {
417
+ const selected = paths[this.pathCursor];
418
+ if (!selected)
419
+ return;
420
+ void this.runBusy(async () => {
421
+ await this.callbacks.openPath(selected);
422
+ this.notice = { text: `Opened ${selected}`, tone: "success" };
423
+ });
424
+ }
425
+ }
426
+ handleAdvancedInput(data) {
427
+ const actions = this.getAdvancedActions();
428
+ if (this.keys.selectUp(data)) {
429
+ this.advancedCursor = Math.max(0, this.advancedCursor - 1);
430
+ this.tui.requestRender();
431
+ return;
432
+ }
433
+ if (this.keys.selectDown(data)) {
434
+ this.advancedCursor = Math.min(actions.length - 1, this.advancedCursor + 1);
435
+ this.tui.requestRender();
436
+ return;
437
+ }
438
+ if (this.keys.selectConfirm(data)) {
439
+ const selected = actions[this.advancedCursor];
440
+ if (selected)
441
+ void this.runAction(selected);
442
+ }
443
+ }
444
+ handleConfirmPresetInput(data) {
445
+ if (data === "o" || data === "O") {
446
+ this.openSelectedDocs();
447
+ return;
448
+ }
449
+ if (this.keys.selectConfirm(data)) {
450
+ void this.confirmPendingAction();
451
+ }
452
+ }
453
+ /** Getter used by openSelectedDocs while the confirm screen is up (no action-list cursor there). */
454
+ getSelectedAction() {
455
+ if (this.screen === "confirm-preset")
456
+ return this.pendingAction;
457
+ const actions = this.getActions();
458
+ return actions[this.actionCursor];
459
+ }
460
+ /** Add or replace an entry so the Installed tab and Presets "active" tag update immediately, without closing the panel. */
461
+ upsertInstalledServer(entry) {
462
+ const index = this.installedServers.findIndex((server) => server.name === entry.name);
463
+ if (index === -1)
464
+ this.installedServers.push(entry);
465
+ else
466
+ this.installedServers[index] = entry;
467
+ }
468
+ async confirmPendingAction() {
469
+ const action = this.pendingAction;
470
+ if (!action)
471
+ return;
472
+ if (action.id === "add-repoprompt") {
473
+ await this.runBusy(async () => {
474
+ const result = await this.callbacks.addRepoPrompt();
475
+ this.callbacks.markSetupCompleted();
476
+ this.upsertInstalledServer({
477
+ name: result.serverName,
478
+ disabled: false,
479
+ kind: "stdio",
480
+ authCapable: false,
481
+ signedIn: false,
482
+ });
483
+ this.notice = {
484
+ text: `Added ${result.serverName} to ${result.path}. Nexus will reload after this panel closes.`,
485
+ tone: "success",
486
+ };
487
+ });
488
+ }
489
+ else if (action.id === "add-known-server" && action.preset) {
490
+ const preset = action.preset;
491
+ const authCapable = preset.entry.auth === "oauth";
492
+ await this.runBusy(async () => {
493
+ const result = await this.callbacks.addKnownServer(preset);
494
+ this.callbacks.markSetupCompleted();
495
+ const kind = preset.entry.command ? "stdio" : "http";
496
+ this.upsertInstalledServer({
497
+ name: result.serverName,
498
+ disabled: false,
499
+ kind,
500
+ authCapable,
501
+ signedIn: false,
502
+ });
503
+ if (!authCapable) {
504
+ this.notice = {
505
+ text: `Added ${result.serverName} to ${result.path}. Nexus will reload after this panel closes.`,
506
+ tone: "success",
507
+ };
508
+ return;
509
+ }
510
+ // Sign in right now, in the same flow — this is the whole point of a preset:
511
+ // don't make the user hunt down a separate command after the fact.
512
+ this.notice = { text: `Added ${result.serverName}. Opening your browser to sign in…`, tone: "muted" };
513
+ this.tui.requestRender();
514
+ const authResult = await this.callbacks.authenticateServer(result.serverName, preset.entry);
515
+ this.upsertInstalledServer({
516
+ name: result.serverName,
517
+ disabled: false,
518
+ kind,
519
+ authCapable,
520
+ signedIn: authResult.ok,
521
+ });
522
+ this.notice = authResult.ok
523
+ ? {
524
+ text: `Added and signed in to ${result.serverName} (${result.path}). Nexus will reload after this panel closes.`,
525
+ tone: "success",
526
+ }
527
+ : {
528
+ text: `Added ${result.serverName} to ${result.path}, but sign-in didn't finish: ${authResult.message} Retry anytime from the Installed tab (press "a").`,
529
+ tone: "warning",
530
+ };
531
+ });
532
+ }
533
+ this.pendingAction = undefined;
534
+ this.screen = this.discovery.hasAnyConfig ? "setup" : "empty";
535
+ this.tui.requestRender();
536
+ }
537
+ async runAction(action) {
538
+ if (action.id === "adopt-imports") {
539
+ this.screen = "imports";
540
+ this.importCursor = 0;
541
+ this.tui.requestRender();
542
+ return;
543
+ }
544
+ if (action.id === "open-paths") {
545
+ this.screen = "paths";
546
+ this.pathCursor = 0;
547
+ this.tui.requestRender();
548
+ return;
549
+ }
550
+ if (action.id === "scaffold-project") {
551
+ await this.runBusy(async () => {
552
+ const result = await this.callbacks.scaffoldProjectConfig();
553
+ this.callbacks.markSetupCompleted();
554
+ this.notice = {
555
+ text: `Wrote starter config to ${result.path}. Nexus will reload after this panel closes.`,
556
+ tone: "success",
557
+ };
558
+ });
559
+ return;
560
+ }
561
+ if (action.id === "add-repoprompt" || (action.id === "add-known-server" && action.preset)) {
562
+ this.pendingAction = action;
563
+ this.screen = "confirm-preset";
564
+ this.tui.requestRender();
565
+ return;
566
+ }
567
+ if (action.id === "add-custom-stdio" || action.id === "add-custom-url") {
568
+ const template = action.id === "add-custom-stdio" ? "/mcp add my-server -- " : "/mcp add my-server --url ";
569
+ this.callbacks.seedEditor(template);
570
+ this.cleanup();
571
+ this.done();
572
+ return;
573
+ }
574
+ if (action.id === "open-advanced") {
575
+ this.screen = "advanced";
576
+ this.advancedCursor = 0;
577
+ this.tui.requestRender();
578
+ return;
579
+ }
580
+ if (action.id === "view-installed-server" && action.installed) {
581
+ const server = action.installed;
582
+ const nextDisabled = !server.disabled;
583
+ await this.runBusy(async () => {
584
+ const result = await this.callbacks.toggleServerDisabled(server.name, nextDisabled);
585
+ this.upsertInstalledServer({ ...server, disabled: nextDisabled });
586
+ this.notice = {
587
+ text: `${nextDisabled ? "Disabled" : "Enabled"} "${server.name}" in ${result.path} — run /reload to apply.`,
588
+ tone: "success",
589
+ };
590
+ });
591
+ return;
592
+ }
593
+ this.notice = {
594
+ text: "Review the details below. Press Enter on an action with a side effect to apply it.",
595
+ tone: "muted",
596
+ };
597
+ this.tui.requestRender();
598
+ }
599
+ async applySelectedImports() {
600
+ const selected = this.discovery.imports
601
+ .filter((entry) => this.selectedImports.has(entry.kind))
602
+ .map((entry) => entry.kind);
603
+ if (selected.length === 0) {
604
+ this.notice = { text: "Select at least one compatibility import first.", tone: "warning" };
605
+ this.tui.requestRender();
606
+ return;
607
+ }
608
+ await this.runBusy(async () => {
609
+ const result = await this.callbacks.adoptImports(selected);
610
+ this.callbacks.markSetupCompleted();
611
+ this.notice =
612
+ result.added.length > 0
613
+ ? {
614
+ text: `Added ${result.added.join(", ")} to ${result.path}. Nexus will reload after this panel closes.`,
615
+ tone: "success",
616
+ }
617
+ : { text: `No changes needed in ${result.path}.`, tone: "muted" };
618
+ this.screen = "advanced";
619
+ this.advancedCursor = 0;
620
+ });
621
+ }
622
+ openSelectedDocs() {
623
+ const selected = this.getSelectedAction();
624
+ const docsUrl = selected?.preset?.docsUrl;
625
+ if (!docsUrl) {
626
+ this.notice = { text: "No docs link for this item.", tone: "muted" };
627
+ this.tui.requestRender();
628
+ return;
629
+ }
630
+ void this.runBusy(async () => {
631
+ await this.callbacks.openPath(docsUrl);
632
+ this.notice = { text: `Opened ${docsUrl}`, tone: "success" };
633
+ });
634
+ }
635
+ authenticateSelected() {
636
+ const selected = this.getSelectedAction();
637
+ const server = selected?.installed;
638
+ if (!server)
639
+ return;
640
+ if (!server.authCapable) {
641
+ this.notice = { text: `"${server.name}" isn't configured for OAuth sign-in.`, tone: "muted" };
642
+ this.tui.requestRender();
643
+ return;
644
+ }
645
+ void this.runBusy(async () => {
646
+ this.notice = { text: `Opening your browser to sign in to "${server.name}"…`, tone: "muted" };
647
+ this.tui.requestRender();
648
+ const result = await this.callbacks.authenticateServer(server.name);
649
+ this.upsertInstalledServer({ ...server, signedIn: result.ok });
650
+ this.notice = { text: result.message, tone: result.ok ? "success" : "warning" };
651
+ });
652
+ }
653
+ async runBusy(fn) {
654
+ this.busy = true;
655
+ this.notice = { text: "Working...", tone: "muted" };
656
+ this.tui.requestRender();
657
+ try {
658
+ await fn();
659
+ }
660
+ catch (error) {
661
+ this.notice = {
662
+ text: error instanceof Error ? error.message : String(error),
663
+ tone: "warning",
664
+ };
665
+ }
666
+ finally {
667
+ this.busy = false;
668
+ this.tui.requestRender();
669
+ }
670
+ }
671
+ render(width) {
672
+ const panelW = Math.max(MIN_PANEL_WIDTH, width);
673
+ const innerW = panelW - 2;
674
+ const contentW = this.contentWidth(innerW);
675
+ const lines = [];
676
+ const border = fg(this.t.border, "─".repeat(innerW));
677
+ lines.push(`┌${border}┐`);
678
+ lines.push(this.padLine(fg(this.t.title, "MCP setup"), innerW));
679
+ for (const line of wrapText(this.discoverySummaryLine(), contentW)) {
680
+ lines.push(this.padLine(line, innerW));
681
+ }
682
+ for (const line of wrapText(this.secondarySummaryLine(), contentW)) {
683
+ lines.push(this.padLine(fg(this.t.muted, line), innerW));
684
+ }
685
+ lines.push(this.padLine("", innerW));
686
+ if (this.notice) {
687
+ const tone = this.notice.tone === "success"
688
+ ? this.t.success
689
+ : this.notice.tone === "warning"
690
+ ? this.t.warning
691
+ : this.t.hint;
692
+ for (const line of wrapText(this.notice.text, contentW)) {
693
+ lines.push(this.padLine(fg(tone, line), innerW));
694
+ }
695
+ lines.push(this.padLine("", innerW));
696
+ }
697
+ lines.push(`├${border}┤`);
698
+ if (this.screen === "imports") {
699
+ lines.push(...this.renderImports(innerW));
700
+ }
701
+ else if (this.screen === "paths") {
702
+ lines.push(...this.renderPaths(innerW));
703
+ }
704
+ else if (this.screen === "confirm-preset") {
705
+ lines.push(...this.renderConfirmPreset(innerW));
706
+ }
707
+ else if (this.screen === "advanced") {
708
+ lines.push(...this.renderAdvanced(innerW));
709
+ }
710
+ else {
711
+ lines.push(...this.renderActions(innerW));
712
+ }
713
+ lines.push(`└${border}┘`);
714
+ return this.clampHeight(lines, innerW, border);
715
+ }
716
+ setMaxHeight(lines) {
717
+ this.maxHeight = lines;
718
+ }
719
+ /**
720
+ * The overlay host may cap total rows and, absent this hook, would slice
721
+ * blindly from the bottom — cutting off the closing border and leaving the
722
+ * box looking broken. Truncate the content instead, keeping the header and
723
+ * action list (the interactive part) and dropping preview/diff lines first.
724
+ */
725
+ clampHeight(lines, innerW, _border) {
726
+ if (this.maxHeight === undefined || lines.length <= this.maxHeight)
727
+ return lines;
728
+ const budget = Math.max(5, this.maxHeight);
729
+ // The last line is the closing border, the one before it is the hint/controls
730
+ // line — both must always stay visible, or the panel loses its own instructions.
731
+ const closing = lines[lines.length - 1];
732
+ const footer = lines[lines.length - 2];
733
+ const head = lines.slice(0, Math.max(1, budget - 3));
734
+ const hidden = lines.length - head.length - 2;
735
+ if (hidden <= 0)
736
+ return lines;
737
+ return [
738
+ ...head,
739
+ this.padLine(fg(this.t.muted, `… ${hidden} more line${hidden === 1 ? "" : "s"} hidden to fit the window`), innerW),
740
+ footer,
741
+ closing,
742
+ ];
743
+ }
744
+ renderTabBar() {
745
+ return SETUP_TABS.map((tab) => tab.id === this.activeTab ? fg(this.t.selected, `[ ${tab.label} ]`) : fg(this.t.muted, ` ${tab.label} `)).join(" ");
746
+ }
747
+ renderActions(innerW) {
748
+ const lines = [];
749
+ const actions = this.getActions();
750
+ const compact = innerW < COMPACT_WIDTH;
751
+ const { start, end } = compact ? this.visibleActionRange(actions.length) : { start: 0, end: actions.length };
752
+ lines.push(this.padLine(this.renderTabBar(), innerW));
753
+ lines.push(this.padLine("", innerW));
754
+ if (start > 0) {
755
+ lines.push(this.padLine(fg(this.t.muted, `… ${start} more above`), innerW));
756
+ }
757
+ for (let index = start; index < end; index++) {
758
+ const action = actions[index];
759
+ if (!action)
760
+ continue;
761
+ const selected = index === this.actionCursor;
762
+ const cursor = selected ? fg(this.t.selected, "›") : " ";
763
+ const mark = this.getRowMark(action);
764
+ const label = truncateToWidth(action.label, Math.max(1, this.contentWidth(innerW) - 2 - visibleWidth(mark)));
765
+ const installedColor = action.installed
766
+ ? action.installed.disabled
767
+ ? this.t.muted
768
+ : action.installed.authCapable && !action.installed.signedIn
769
+ ? this.t.warning
770
+ : this.t.success
771
+ : undefined;
772
+ const labelRendered = action.preset
773
+ ? fg(presetAccent(action.preset), label)
774
+ : installedColor
775
+ ? fg(installedColor, label)
776
+ : label;
777
+ lines.push(this.padLine(`${cursor} ${mark}${labelRendered}`, innerW));
778
+ }
779
+ if (end < actions.length) {
780
+ lines.push(this.padLine(fg(this.t.muted, `… ${actions.length - end} more below`), innerW));
781
+ }
782
+ lines.push(this.padLine("", innerW));
783
+ // Presets stay a clean, scannable list — no inline summary/diff while browsing.
784
+ // The write preview only shows on the confirm screen, after pressing Enter.
785
+ if (this.activeTab !== "presets") {
786
+ const preview = this.getActionPreview(this.getSelectedAction(), this.previewWidth(innerW));
787
+ for (const line of preview) {
788
+ lines.push(this.padLine(line, innerW));
789
+ }
790
+ lines.push(this.padLine("", innerW));
791
+ }
792
+ const docsHint = this.activeTab === "presets" ? " · o docs" : "";
793
+ const confirmHint = this.activeTab === "presets" ? " · enter to review & confirm" : "";
794
+ const checkHint = this.activeTab === "presets" && this.installedServers.length > 0 ? " · ✓ already added" : "";
795
+ const authHint = this.activeTab === "installed" && this.installedServers.some((server) => server.authCapable)
796
+ ? " · a sign in"
797
+ : "";
798
+ const hint = compact
799
+ ? `Enter select · ←/→ tabs${docsHint} · Esc back`
800
+ : `Enter selects${confirmHint}, ←/→ switch tabs${docsHint}${checkHint}${authHint}, Esc goes back, Ctrl+C closes.`;
801
+ lines.push(this.padLine(fg(this.t.muted, hint), innerW));
802
+ return lines;
803
+ }
804
+ renderImports(innerW) {
805
+ const lines = [];
806
+ lines.push(this.padLine("Select compatibility imports. Space toggles, Enter saves, Esc goes back.", innerW));
807
+ lines.push(this.padLine("", innerW));
808
+ for (let index = 0; index < this.discovery.imports.length; index++) {
809
+ const entry = this.discovery.imports[index];
810
+ if (!entry)
811
+ continue;
812
+ const selected = this.selectedImports.has(entry.kind) ? "[x]" : "[ ]";
813
+ const cursor = index === this.importCursor ? fg(this.t.selected, "›") : " ";
814
+ lines.push(this.padLine(`${cursor} ${selected} ${entry.kind} ${entry.path}`, innerW));
815
+ }
816
+ lines.push(this.padLine("", innerW));
817
+ const selected = this.discovery.imports
818
+ .filter((entry) => this.selectedImports.has(entry.kind))
819
+ .map((entry) => entry.kind);
820
+ const preview = this.callbacks.previewImports(selected);
821
+ for (const line of this.formatWritePreview("Compatibility import write preview", preview, [], this.previewWidth(innerW))) {
822
+ lines.push(this.padLine(line, innerW));
823
+ }
824
+ return lines;
825
+ }
826
+ renderPaths(innerW) {
827
+ const lines = [];
828
+ lines.push(this.padLine("Select a detected config path to open. Enter opens it, Esc goes back.", innerW));
829
+ lines.push(this.padLine("", innerW));
830
+ const paths = this.getDetectedPaths();
831
+ for (let index = 0; index < paths.length; index++) {
832
+ const cursor = index === this.pathCursor ? fg(this.t.selected, "›") : " ";
833
+ const path = paths[index];
834
+ if (path !== undefined)
835
+ lines.push(this.padLine(`${cursor} ${path}`, innerW));
836
+ }
837
+ return lines;
838
+ }
839
+ renderAdvanced(innerW) {
840
+ const lines = [];
841
+ const actions = this.getAdvancedActions();
842
+ lines.push(this.padLine(`${fg(this.t.muted, "Custom")} > ${fg(this.t.title, "Advanced options")}`, innerW));
843
+ lines.push(this.padLine("", innerW));
844
+ if (actions.length === 0) {
845
+ lines.push(this.padLine(fg(this.t.muted, "Nothing here right now — nice, config is already tidy."), innerW));
846
+ }
847
+ for (let index = 0; index < actions.length; index++) {
848
+ const action = actions[index];
849
+ if (!action)
850
+ continue;
851
+ const cursor = index === this.advancedCursor ? fg(this.t.selected, "›") : " ";
852
+ const label = truncateToWidth(action.label, Math.max(1, this.contentWidth(innerW) - 2));
853
+ lines.push(this.padLine(`${cursor} ${label}`, innerW));
854
+ }
855
+ lines.push(this.padLine("", innerW));
856
+ // This is the one screen where showing the full explanation on highlight is the point —
857
+ // you only ever get here by deliberately asking for "Advanced options".
858
+ const preview = this.getActionPreview(actions[this.advancedCursor], this.previewWidth(innerW));
859
+ for (const line of preview) {
860
+ lines.push(this.padLine(line, innerW));
861
+ }
862
+ lines.push(this.padLine("", innerW));
863
+ lines.push(this.padLine(fg(this.t.muted, "Enter selects, Esc back to Custom, Ctrl+C closes."), innerW));
864
+ return lines;
865
+ }
866
+ renderConfirmPreset(innerW) {
867
+ const lines = [];
868
+ const action = this.pendingAction;
869
+ const name = action?.preset?.name ?? (action?.id === "add-repoprompt" ? "RepoPrompt" : "this server");
870
+ lines.push(this.padLine(fg(this.t.title, `Confirm: add ${name}`), innerW));
871
+ lines.push(this.padLine("", innerW));
872
+ const preview = this.getActionPreview(action, this.previewWidth(innerW));
873
+ for (const line of preview) {
874
+ lines.push(this.padLine(line, innerW));
875
+ }
876
+ lines.push(this.padLine("", innerW));
877
+ const docsHint = action?.preset?.docsUrl ? " · o docs" : "";
878
+ lines.push(this.padLine(fg(this.t.muted, `Enter writes this file${docsHint} · Esc goes back`), innerW));
879
+ return lines;
880
+ }
881
+ discoverySummaryLine() {
882
+ if (!this.discovery.hasAnyConfig) {
883
+ return fg(this.t.warning, this.options.onboardingState.setupCompleted
884
+ ? "No MCP servers are active right now."
885
+ : "No MCP config is active yet.");
886
+ }
887
+ if (this.discovery.totalServerCount === 0 &&
888
+ (this.discovery.imports.length > 0 || !!this.discovery.repoPrompt.executablePath)) {
889
+ return fg(this.t.warning, "Nexus found MCP-related setup options, but none are active in Nexus yet.");
890
+ }
891
+ const configuredSources = this.discovery.sources.filter((source) => source.serverCount > 0).length;
892
+ return fg(this.t.hint, `Detected ${this.discovery.totalServerCount} configured servers across ${configuredSources} Nexus source${configuredSources === 1 ? "" : "s"}.`);
893
+ }
894
+ secondarySummaryLine() {
895
+ const hostNote = this.discovery.hostConfigs.length > 0
896
+ ? ` Host discovery is ${this.discovery.hostConfigDiscovery}; ${this.discovery.hostConfigs.length} host source${this.discovery.hostConfigs.length === 1 ? "" : "s"} detected.`
897
+ : "";
898
+ const conflictNote = this.discovery.conflicts.length > 0
899
+ ? ` ${this.discovery.conflicts.length} same-name conflict${this.discovery.conflicts.length === 1 ? "" : "s"} reported.`
900
+ : "";
901
+ if (!this.discovery.hasAnyConfig) {
902
+ return `Create .nexus/mcp.json, adopt host imports, or quick-add RepoPrompt from this screen.${hostNote}${conflictNote}`;
903
+ }
904
+ if (this.discovery.totalServerCount === 0 && this.discovery.imports.length > 0) {
905
+ return `Detected ${this.discovery.imports.length} compatibility import source${this.discovery.imports.length === 1 ? "" : "s"}. Adopt them into Nexus or inspect the underlying files.${hostNote}${conflictNote}`;
906
+ }
907
+ return `Nexus MCP files contain server definitions, compatibility imports, and MCP-specific overrides.${hostNote}${conflictNote}`;
908
+ }
909
+ visibleActionRange(total) {
910
+ if (total <= COMPACT_ACTION_ROWS)
911
+ return { start: 0, end: total };
912
+ const half = Math.floor(COMPACT_ACTION_ROWS / 2);
913
+ const start = Math.min(Math.max(0, this.actionCursor - half), Math.max(0, total - COMPACT_ACTION_ROWS));
914
+ return { start, end: Math.min(total, start + COMPACT_ACTION_ROWS) };
915
+ }
916
+ contentWidth(innerW) {
917
+ return Math.max(8, innerW - 4);
918
+ }
919
+ previewWidth(innerW) {
920
+ return Math.max(12, Math.min(DESKTOP_PREVIEW_WIDTH, this.contentWidth(innerW)));
921
+ }
922
+ getActionPreview(action, previewW = DESKTOP_PREVIEW_WIDTH) {
923
+ switch (action?.id) {
924
+ case "adopt-imports":
925
+ return this.formatWritePreview("Compatibility import write preview", this.callbacks.previewImports(this.discovery.imports
926
+ .filter((entry) => this.selectedImports.has(entry.kind))
927
+ .map((entry) => entry.kind)), [
928
+ `Detected imports: ${this.discovery.imports.map((entry) => `${entry.kind} (${entry.serverCount} servers)`).join(", ")}`,
929
+ "Selected imports are written to the Nexus global MCP configuration shown above.",
930
+ ], previewW);
931
+ case "view-example":
932
+ return [
933
+ ...this.formatPreview(["Example Nexus `.nexus/mcp.json`:"], previewW),
934
+ // Truncate, don't word-wrap: wrapText would collapse JSON's indentation.
935
+ ...[
936
+ "{",
937
+ ' "mcpServers": {',
938
+ ' "chrome-devtools": {',
939
+ ' "command": "npx",',
940
+ ' "args": ["-y", "chrome-devtools-mcp@1.6.0"]',
941
+ " }",
942
+ " }",
943
+ "}",
944
+ ].map((line) => colorizeJsonLine(truncateToWidth(line, previewW, "…"))),
945
+ ...this.formatPreview([
946
+ "",
947
+ "Use Scaffold project `.nexus/mcp.json` when you want a safe empty shell instead of a live example server.",
948
+ ], previewW),
949
+ ];
950
+ case "show-precedence": {
951
+ const hostNote = this.discovery.hostConfigDiscovery === "off"
952
+ ? "Configs from other tools (Cursor, Claude Code, VS Code, etc.) are NOT being read right now — Nexus only looks at its own two files above. You can turn that on if you'd rather not redefine servers you already set up elsewhere."
953
+ : this.discovery.hostConfigDiscovery === "prompt"
954
+ ? "Nexus will ask before reading configs from other tools (Cursor, Claude Code, VS Code, etc.). If you say yes, they're used as a fallback — only for a server name that isn't already defined in either file above."
955
+ : "Nexus is also reading configs from other tools (Cursor, Claude Code, VS Code, etc.) as a fallback — they only apply to a server name that isn't already defined in either file above.";
956
+ const conflictNote = this.discovery.conflicts.length === 0
957
+ ? "Right now no server name is defined in more than one place, so there's nothing actually being overridden."
958
+ : `Right now ${this.discovery.conflicts.length} server name${this.discovery.conflicts.length === 1 ? " is" : "s are"} defined in more than one place — whichever file is read last wins, and the loser is silently ignored:`;
959
+ return this.formatPreview([
960
+ "Nexus can load MCP servers from a few different places, so when the same server name shows up twice, something has to decide which definition actually gets used.",
961
+ "",
962
+ "It reads, in this order — each one can override a server name defined before it:",
963
+ " 1. Global — ~/.config/nexus/mcp.json (applies to every project on this machine)",
964
+ ` 2. Project — ${getConfigDirName()}/mcp.json (just this project; wins over global for the same name)`,
965
+ "",
966
+ hostNote,
967
+ "",
968
+ "Everything you add from this panel (a preset, a custom server) gets written to the project file, so it's scoped to this project and takes priority over anything global.",
969
+ "",
970
+ conflictNote,
971
+ ...this.discovery.conflicts
972
+ .slice(0, 8)
973
+ .map((conflict) => ` ${conflict.serverName}: ${conflict.sources.map((source) => source.path).join(" then ")} — "${conflict.winner.path}" wins`),
974
+ ], previewW);
975
+ }
976
+ case "open-paths":
977
+ return this.formatPreview(this.getDetectedPaths().length > 0
978
+ ? ["Detected paths:", ...this.getDetectedPaths()]
979
+ : ["No config paths were detected."], previewW);
980
+ case "add-repoprompt": {
981
+ const repoPrompt = this.discovery.repoPrompt;
982
+ const preview = this.callbacks.previewRepoPrompt();
983
+ if (!preview) {
984
+ return this.formatPreview(["RepoPrompt is not available to add from this setup screen."], previewW);
985
+ }
986
+ return this.formatWritePreview("RepoPrompt write preview", preview, [
987
+ `Executable: ${repoPrompt.executablePath ?? "not found"}`,
988
+ `Target: ${repoPrompt.targetPath ?? "n/a"}`,
989
+ `Server name: ${repoPrompt.serverName ?? "repoprompt"}`,
990
+ ], previewW);
991
+ }
992
+ case "add-known-server": {
993
+ const preset = action.preset;
994
+ if (!preset)
995
+ return this.formatPreview(["Known server preset is unavailable."], previewW);
996
+ return this.formatWritePreview(`${preset.name} write preview`, this.callbacks.previewKnownServer(preset), [preset.summary, ...(preset.docsUrl ? [`Docs: ${preset.docsUrl}`, 'Press "o" to open docs.'] : [])], previewW);
997
+ }
998
+ case "scaffold-project":
999
+ return this.formatWritePreview("Starter project `.nexus/mcp.json` write preview", this.callbacks.previewStarterProject(), [
1000
+ `This creates ${getConfigDirName()}/mcp.json right here in this project — the file everything you add from the Presets or Custom tab actually gets written into. If you already have one, this action doesn't show up (check the Installed tab instead).`,
1001
+ "",
1002
+ `It starts out empty on purpose (just an empty "mcpServers" list). A fake placeholder server would fail the moment Nexus tried to connect to it on your very next reload, so this just gives you a valid, empty file to add real servers into afterward.`,
1003
+ ], previewW);
1004
+ case "add-custom-stdio":
1005
+ return [
1006
+ ...this.formatPreview(["Runs a command on your machine over stdio — e.g. an npx-installed MCP server."], previewW),
1007
+ "",
1008
+ colorizeCommandLine(truncateToWidth("/mcp add <name> -- <command> [args...]", previewW, "…")),
1009
+ "",
1010
+ fg(this.t.muted, "Example (real, runnable):"),
1011
+ colorizeCommandLine(truncateToWidth("/mcp add chrome-devtools -- npx -y chrome-devtools-mcp@1.6.0", previewW, "…")),
1012
+ "",
1013
+ ...this.formatPreview([
1014
+ 'Enter fills in "/mcp add my-server -- " into the chat input below — finish typing there.',
1015
+ "Run `/mcp add` with no arguments (from chat) for the full flag list.",
1016
+ ], previewW),
1017
+ ];
1018
+ case "add-custom-url":
1019
+ return [
1020
+ ...this.formatPreview(["Connects to a remote MCP server over HTTP."], previewW),
1021
+ "",
1022
+ colorizeCommandLine(truncateToWidth("/mcp add <name> --url <url> [--header K=V] [--auth oauth|bearer] [--bearer-env VAR]", previewW, "…")),
1023
+ "",
1024
+ fg(this.t.muted, "Example (real, runnable):"),
1025
+ colorizeCommandLine(truncateToWidth("/mcp add context7 --url https://mcp.context7.com/mcp", previewW, "…")),
1026
+ "",
1027
+ ...this.formatPreview([
1028
+ 'Enter fills in "/mcp add my-server --url " into the chat input below — finish typing there.',
1029
+ "Run `/mcp add` with no arguments (from chat) for the full flag list.",
1030
+ ], previewW),
1031
+ ];
1032
+ case "view-installed-server": {
1033
+ const server = action.installed;
1034
+ if (!server)
1035
+ return this.formatPreview(["Add a server from the Presets or Custom tab to see it here."], previewW);
1036
+ return this.formatPreview([
1037
+ `${server.name} — ${server.kind === "stdio" ? "local (stdio)" : "remote (HTTP)"}, currently ${server.disabled ? "disabled" : "active"}${server.authCapable ? (server.signedIn ? ", signed in" : ", NOT signed in yet") : ""}.`,
1038
+ "",
1039
+ `Press Enter to ${server.disabled ? "enable" : "disable"} it here. Takes effect after /reload.`,
1040
+ ...(server.authCapable
1041
+ ? [
1042
+ "",
1043
+ server.signedIn
1044
+ ? 'Press "a" to sign in again (e.g. to switch accounts). This re-opens your browser.'
1045
+ : 'Press "a" to sign in now — opens your browser for this server\'s OAuth login. Tokens refresh automatically after that; you should not need to repeat this unless access is revoked.',
1046
+ ]
1047
+ : []),
1048
+ ], previewW);
1049
+ }
1050
+ default:
1051
+ return this.formatPreview(["Close the setup flow."], previewW);
1052
+ }
1053
+ }
1054
+ formatPreview(lines, width = DESKTOP_PREVIEW_WIDTH) {
1055
+ const preview = [];
1056
+ for (const line of lines) {
1057
+ preview.push(...wrapText(line, width));
1058
+ }
1059
+ return preview;
1060
+ }
1061
+ formatWritePreview(title, preview, intro = [], width = DESKTOP_PREVIEW_WIDTH) {
1062
+ const lines = [];
1063
+ for (const line of intro) {
1064
+ lines.push(...wrapText(line, width));
1065
+ }
1066
+ if (intro.length > 0)
1067
+ lines.push("");
1068
+ lines.push(...wrapText(`${title}: ${preview.path}`, width));
1069
+ lines.push(...wrapText(preview.existed
1070
+ ? "Existing file detected. Showing exact before/after diff."
1071
+ : "New file will be created. Showing exact content diff.", width));
1072
+ lines.push("");
1073
+ const diffLines = preview.diffText.split("\n");
1074
+ const maxLines = 12;
1075
+ const shown = diffLines.slice(0, maxLines);
1076
+ for (const line of shown) {
1077
+ // Truncate, don't word-wrap: wrapText collapses whitespace and would
1078
+ // destroy JSON's nested indentation.
1079
+ const color = diffLineColor(line);
1080
+ const truncated = truncateToWidth(line, width, "…");
1081
+ lines.push(color ? fg(color, truncated) : colorizeJsonLine(truncated));
1082
+ }
1083
+ if (diffLines.length > maxLines) {
1084
+ lines.push(...wrapText(`… ${diffLines.length - maxLines} more diff line${diffLines.length - maxLines === 1 ? "" : "s"}`, width));
1085
+ }
1086
+ return lines;
1087
+ }
1088
+ padLine(text, innerW) {
1089
+ const inset = 2;
1090
+ const contentW = Math.max(0, innerW - inset * 2);
1091
+ const fitted = truncateToWidth(text, contentW, "…", true);
1092
+ const plainWidth = visibleWidth(fitted);
1093
+ const padding = Math.max(0, contentW - plainWidth);
1094
+ return `│${" ".repeat(inset)}${fitted}${" ".repeat(padding)}${" ".repeat(inset)}│`;
1095
+ }
1096
+ invalidate() { }
1097
+ dispose() {
1098
+ this.cleanup();
1099
+ }
1100
+ }
1101
+ export function createMcpSetupPanel(discovery, callbacks, options, tui, done) {
1102
+ return new McpSetupPanel(discovery, callbacks, options, tui, done);
1103
+ }
1104
+ //# sourceMappingURL=mcp-setup-panel.js.map