@oh-my-pi/pi-tui 18.2.3 → 18.2.5

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 (750) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/README.md +43 -0
  3. package/dist/types/app-keybindings.d.ts +395 -0
  4. package/dist/types/apps/autoresearch-dashboard.d.ts +43 -0
  5. package/dist/types/apps/autoresearch-data.d.ts +15 -0
  6. package/dist/types/apps/cleanse-board.d.ts +66 -0
  7. package/dist/types/apps/cleanse-picker.d.ts +23 -0
  8. package/dist/types/apps/debug/log-formatting.d.ts +6 -0
  9. package/dist/types/apps/debug/log-viewer.d.ts +81 -0
  10. package/dist/types/apps/debug/protocol-probe.d.ts +42 -0
  11. package/dist/types/apps/debug/raw-sse-buffer.d.ts +58 -0
  12. package/dist/types/apps/debug/raw-sse.d.ts +26 -0
  13. package/dist/types/apps/debug/terminal-info.d.ts +34 -0
  14. package/dist/types/apps/debug/viewer-frame.d.ts +52 -0
  15. package/dist/types/apps/git/avatar.d.ts +9 -0
  16. package/dist/types/apps/git/colors.d.ts +49 -0
  17. package/dist/types/apps/git/diff-pane.d.ts +188 -0
  18. package/dist/types/apps/git/git-tui.d.ts +66 -0
  19. package/dist/types/apps/git/sidebar.d.ts +149 -0
  20. package/dist/types/apps/git/state.d.ts +117 -0
  21. package/dist/types/apps/if-bench-board.d.ts +75 -0
  22. package/dist/types/apps/live-visualizer.d.ts +33 -0
  23. package/dist/types/apps/ps-data.d.ts +51 -0
  24. package/dist/types/apps/ps-top.d.ts +33 -0
  25. package/dist/types/apps/session-picker.d.ts +26 -0
  26. package/dist/types/apps/setup-model-picker.d.ts +13 -0
  27. package/dist/types/apps/standalone-picker.d.ts +42 -0
  28. package/dist/types/autocomplete.d.ts +4 -0
  29. package/dist/types/chat/advisor-message.d.ts +11 -0
  30. package/dist/types/chat/assistant-message.d.ts +106 -0
  31. package/dist/types/chat/background-tan-message.d.ts +9 -0
  32. package/dist/types/chat/bash-execution.d.ts +47 -0
  33. package/dist/types/chat/cache-invalidation-marker.d.ts +50 -0
  34. package/dist/types/chat/chat-transcript-builder.d.ts +50 -0
  35. package/dist/types/chat/collab-prompt-message.d.ts +10 -0
  36. package/dist/types/chat/compaction-summary-message.d.ts +54 -0
  37. package/dist/types/chat/custom-message.d.ts +10 -0
  38. package/dist/types/chat/display-preferences.d.ts +13 -0
  39. package/dist/types/chat/eval-execution.d.ts +29 -0
  40. package/dist/types/chat/execution-shared.d.ts +51 -0
  41. package/dist/types/chat/extension-types.d.ts +28 -0
  42. package/dist/types/chat/hook-message.d.ts +10 -0
  43. package/dist/types/chat/image-loading.d.ts +46 -0
  44. package/dist/types/chat/late-diagnostics-message.d.ts +21 -0
  45. package/dist/types/chat/messages.d.ts +189 -0
  46. package/dist/types/chat/reaction.d.ts +30 -0
  47. package/dist/types/chat/read-target.d.ts +4 -0
  48. package/dist/types/chat/read-tool-group.d.ts +67 -0
  49. package/dist/types/chat/served-model-marker.d.ts +47 -0
  50. package/dist/types/chat/skill-message.d.ts +33 -0
  51. package/dist/types/chat/skill-title-input.d.ts +14 -0
  52. package/dist/types/chat/stripped-tool-calls-placeholder.d.ts +12 -0
  53. package/dist/types/chat/thinking-display.d.ts +12 -0
  54. package/dist/types/chat/todo-reminder.d.ts +13 -0
  55. package/dist/types/chat/tool-execution.d.ts +138 -0
  56. package/dist/types/chat/transcript-browser.d.ts +93 -0
  57. package/dist/types/chat/transcript-entry.d.ts +45 -0
  58. package/dist/types/chat/transcript-outline.d.ts +86 -0
  59. package/dist/types/chat/transcript-render-helpers.d.ts +92 -0
  60. package/dist/types/chat/ttsr-notification.d.ts +23 -0
  61. package/dist/types/chat/user-message.d.ts +52 -0
  62. package/dist/types/chrome/chat-block.d.ts +64 -0
  63. package/dist/types/chrome/collab-qrcode.d.ts +28 -0
  64. package/dist/types/chrome/context-thresholds.d.ts +10 -0
  65. package/dist/types/chrome/countdown-timer.d.ts +14 -0
  66. package/dist/types/chrome/diff.d.ts +14 -0
  67. package/dist/types/chrome/dynamic-border.d.ts +16 -0
  68. package/dist/types/chrome/error-block.d.ts +17 -0
  69. package/dist/types/chrome/form-theme.d.ts +3 -0
  70. package/dist/types/chrome/format.d.ts +12 -0
  71. package/dist/types/chrome/index.d.ts +20 -0
  72. package/dist/types/chrome/keybinding-hints.d.ts +40 -0
  73. package/dist/types/chrome/live-board.d.ts +23 -0
  74. package/dist/types/chrome/local-date.d.ts +4 -0
  75. package/dist/types/chrome/message-divider.d.ts +22 -0
  76. package/dist/types/chrome/message-frame.d.ts +52 -0
  77. package/dist/types/chrome/message-notice.d.ts +35 -0
  78. package/dist/types/chrome/overlay-box.d.ts +74 -0
  79. package/dist/types/chrome/qrcode.d.ts +48 -0
  80. package/dist/types/chrome/segment-track.d.ts +27 -0
  81. package/dist/types/chrome/select-list-mouse-routing.d.ts +4 -0
  82. package/dist/types/chrome/selector-helpers.d.ts +52 -0
  83. package/dist/types/chrome/shared.d.ts +5 -0
  84. package/dist/types/chrome/streaming-panel.d.ts +19 -0
  85. package/dist/types/chrome/tool-activity.d.ts +17 -0
  86. package/dist/types/chrome/transcript-container.d.ts +129 -0
  87. package/dist/types/chrome/transcript-status.d.ts +12 -0
  88. package/dist/types/chrome/visual-truncate.d.ts +19 -0
  89. package/dist/types/components/disclosure.d.ts +42 -0
  90. package/dist/types/components/form.d.ts +130 -0
  91. package/dist/types/components/image.d.ts +18 -16
  92. package/dist/types/components/key-value-list.d.ts +24 -0
  93. package/dist/types/components/layout/geometry.d.ts +73 -0
  94. package/dist/types/components/layout/row.d.ts +55 -0
  95. package/dist/types/components/layout/split-pane.d.ts +80 -0
  96. package/dist/types/components/layout/stack.d.ts +50 -0
  97. package/dist/types/components/menu-selection.d.ts +71 -0
  98. package/dist/types/components/metric.d.ts +44 -0
  99. package/dist/types/components/progress-bar.d.ts +36 -0
  100. package/dist/types/components/scroll-view.d.ts +53 -19
  101. package/dist/types/components/scroll-viewport.d.ts +46 -0
  102. package/dist/types/components/section.d.ts +19 -0
  103. package/dist/types/components/select-list.d.ts +46 -2
  104. package/dist/types/components/settings-list.d.ts +20 -0
  105. package/dist/types/components/table.d.ts +32 -0
  106. package/dist/types/components/tree-view.d.ts +130 -0
  107. package/dist/types/components/wizard-step.d.ts +60 -0
  108. package/dist/types/hotkeys-markdown.d.ts +7 -0
  109. package/dist/types/index.d.ts +17 -0
  110. package/dist/types/keybinding-matchers.d.ts +35 -0
  111. package/dist/types/keys.d.ts +2 -2
  112. package/dist/types/lang-from-path.d.ts +9 -0
  113. package/dist/types/overlays/advisor-config.d.ts +114 -0
  114. package/dist/types/overlays/agent-activity.d.ts +41 -0
  115. package/dist/types/overlays/agent-hub-projection.d.ts +33 -0
  116. package/dist/types/overlays/agent-hub-renderer.d.ts +53 -0
  117. package/dist/types/overlays/agent-hub-types.d.ts +99 -0
  118. package/dist/types/overlays/agent-hub.d.ts +126 -0
  119. package/dist/types/overlays/agent-transcript-viewer.d.ts +71 -0
  120. package/dist/types/overlays/agents-hub.d.ts +72 -0
  121. package/dist/types/overlays/ask-dialog.d.ts +88 -0
  122. package/dist/types/overlays/bordered-loader.d.ts +11 -0
  123. package/dist/types/overlays/btw-history-panel.d.ts +29 -0
  124. package/dist/types/overlays/btw-history.d.ts +17 -0
  125. package/dist/types/overlays/btw-panel.d.ts +26 -0
  126. package/dist/types/overlays/cleanse-panel.d.ts +36 -0
  127. package/dist/types/overlays/codex-reset-fireworks.d.ts +52 -0
  128. package/dist/types/overlays/composer-shape-preview.d.ts +47 -0
  129. package/dist/types/overlays/composer-shape-registry.d.ts +50 -0
  130. package/dist/types/overlays/copy-selector.d.ts +48 -0
  131. package/dist/types/overlays/copy-targets.d.ts +63 -0
  132. package/dist/types/overlays/error-banner.d.ts +13 -0
  133. package/dist/types/overlays/extensions/display-text.d.ts +10 -0
  134. package/dist/types/overlays/extensions/extension-dashboard.d.ts +63 -0
  135. package/dist/types/overlays/extensions/extension-list.d.ts +54 -0
  136. package/dist/types/overlays/extensions/inspector-model.d.ts +109 -0
  137. package/dist/types/overlays/extensions/inspector-panel.d.ts +17 -0
  138. package/dist/types/overlays/extensions/live-tool-session.d.ts +28 -0
  139. package/dist/types/overlays/extensions/mcp-runtime.d.ts +107 -0
  140. package/dist/types/overlays/extensions/state-manager.d.ts +42 -0
  141. package/dist/types/overlays/extensions/types.d.ts +165 -0
  142. package/dist/types/overlays/history-search.d.ts +16 -0
  143. package/dist/types/overlays/hook-editor.d.ts +44 -0
  144. package/dist/types/overlays/hook-input.d.ts +20 -0
  145. package/dist/types/overlays/hook-selector.d.ts +71 -0
  146. package/dist/types/overlays/hub-frame.d.ts +77 -0
  147. package/dist/types/overlays/login-dialog.d.ts +43 -0
  148. package/dist/types/overlays/logout-account-selector.d.ts +15 -0
  149. package/dist/types/overlays/mcp-add-wizard.d.ts +87 -0
  150. package/dist/types/overlays/model-browser.d.ts +213 -0
  151. package/dist/types/overlays/model-hub.d.ts +72 -0
  152. package/dist/types/overlays/model-picker.d.ts +74 -0
  153. package/dist/types/overlays/model-selector.d.ts +52 -0
  154. package/dist/types/overlays/move-overlay.d.ts +39 -0
  155. package/dist/types/overlays/oauth-selector.d.ts +41 -0
  156. package/dist/types/overlays/omfg-panel.d.ts +20 -0
  157. package/dist/types/overlays/pause-screen.d.ts +43 -0
  158. package/dist/types/overlays/plan-review-overlay.d.ts +89 -0
  159. package/dist/types/overlays/plan-save-overlay.d.ts +19 -0
  160. package/dist/types/overlays/plan-toc.d.ts +41 -0
  161. package/dist/types/overlays/plugin-selector.d.ts +28 -0
  162. package/dist/types/overlays/plugin-settings.d.ts +180 -0
  163. package/dist/types/overlays/queue-mode-selector.d.ts +11 -0
  164. package/dist/types/overlays/reset-usage-selector.d.ts +23 -0
  165. package/dist/types/overlays/rewind-selector.d.ts +61 -0
  166. package/dist/types/overlays/running-subagent-badge.d.ts +6 -0
  167. package/dist/types/overlays/session-account-selector.d.ts +24 -0
  168. package/dist/types/overlays/session-info-overlay.d.ts +17 -0
  169. package/dist/types/overlays/session-observer-registry.d.ts +77 -0
  170. package/dist/types/overlays/session-selector.d.ts +131 -0
  171. package/dist/types/overlays/settings-defs.d.ts +119 -0
  172. package/dist/types/overlays/settings-selector.d.ts +73 -0
  173. package/dist/types/overlays/show-images-selector.d.ts +11 -0
  174. package/dist/types/overlays/snapcompact-shape-preview.d.ts +31 -0
  175. package/dist/types/overlays/theme-selector.d.ts +12 -0
  176. package/dist/types/overlays/thinking-selector.d.ts +12 -0
  177. package/dist/types/overlays/tiny-title-download-progress.d.ts +21 -0
  178. package/dist/types/overlays/tree-selector.d.ts +92 -0
  179. package/dist/types/overlays/usage-dashboard.d.ts +94 -0
  180. package/dist/types/overlays/usage-display.d.ts +5 -0
  181. package/dist/types/overlays/usage-row.d.ts +17 -0
  182. package/dist/types/prompt/attachment-chips.d.ts +19 -0
  183. package/dist/types/prompt/composer-attachments.d.ts +71 -0
  184. package/dist/types/prompt/composer-cache.d.ts +37 -0
  185. package/dist/types/prompt/composer.d.ts +159 -0
  186. package/dist/types/prompt/custom-editor.d.ts +261 -0
  187. package/dist/types/prompt/editor-top-gap.d.ts +18 -0
  188. package/dist/types/prompt/emoji-autocomplete.d.ts +16 -0
  189. package/dist/types/prompt/github-ref-autocomplete.d.ts +35 -0
  190. package/dist/types/prompt/gradient-highlight.d.ts +32 -0
  191. package/dist/types/prompt/image-format.d.ts +4 -0
  192. package/dist/types/prompt/image-references.d.ts +26 -0
  193. package/dist/types/prompt/interactive-context-helpers.d.ts +43 -0
  194. package/dist/types/prompt/internal-url-autocomplete.d.ts +56 -0
  195. package/dist/types/prompt/macos-spelling.d.ts +44 -0
  196. package/dist/types/prompt/magic-keyword-boundary.d.ts +9 -0
  197. package/dist/types/prompt/magic-keywords.d.ts +26 -0
  198. package/dist/types/prompt/markdown-prose.d.ts +15 -0
  199. package/dist/types/prompt/model-mention-autocomplete.d.ts +25 -0
  200. package/dist/types/prompt/model-mention-syntax.d.ts +24 -0
  201. package/dist/types/prompt/orchestrate.d.ts +13 -0
  202. package/dist/types/prompt/prompt-action-autocomplete.d.ts +56 -0
  203. package/dist/types/prompt/queue-input.d.ts +8 -0
  204. package/dist/types/prompt/skill-tokens.d.ts +13 -0
  205. package/dist/types/prompt/tools-markdown.d.ts +12 -0
  206. package/dist/types/prompt/ultrathink.d.ts +13 -0
  207. package/dist/types/prompt/usage-amounts.d.ts +26 -0
  208. package/dist/types/prompt/video.d.ts +18 -0
  209. package/dist/types/prompt/welcome.d.ts +83 -0
  210. package/dist/types/prompt/workflow.d.ts +13 -0
  211. package/dist/types/render/code-cell.d.ts +51 -0
  212. package/dist/types/render/file-list.d.ts +22 -0
  213. package/dist/types/render/hyperlink.d.ts +80 -0
  214. package/dist/types/render/index.d.ts +16 -0
  215. package/dist/types/render/output-block.d.ts +54 -0
  216. package/dist/types/render/output-pane.d.ts +80 -0
  217. package/dist/types/render/render-utils.d.ts +312 -0
  218. package/dist/types/render/sixel.d.ts +21 -0
  219. package/dist/types/render/status-line.d.ts +23 -0
  220. package/dist/types/render/tool-card.d.ts +71 -0
  221. package/dist/types/render/tree-list.d.ts +29 -0
  222. package/dist/types/render/types.d.ts +17 -0
  223. package/dist/types/render/utils.d.ts +43 -0
  224. package/dist/types/render/width-aware-text.d.ts +23 -0
  225. package/dist/types/setup/lazy.d.ts +3 -0
  226. package/dist/types/setup/scenes/composer.d.ts +3 -0
  227. package/dist/types/setup/scenes/glyph.d.ts +3 -0
  228. package/dist/types/setup/scenes/model.d.ts +3 -0
  229. package/dist/types/setup/scenes/outro.d.ts +2 -0
  230. package/dist/types/setup/scenes/providers.d.ts +3 -0
  231. package/dist/types/setup/scenes/sign-in.d.ts +21 -0
  232. package/dist/types/setup/scenes/splash.d.ts +11 -0
  233. package/dist/types/setup/scenes/theme.d.ts +3 -0
  234. package/dist/types/setup/scenes/types.d.ts +110 -0
  235. package/dist/types/setup/scenes/web-search.d.ts +22 -0
  236. package/dist/types/setup/setup-version.d.ts +11 -0
  237. package/dist/types/setup/startup-splash.d.ts +9 -0
  238. package/dist/types/setup/wizard-overlay.d.ts +16 -0
  239. package/dist/types/setup/wizard.d.ts +25 -0
  240. package/dist/types/status-line/component.d.ts +182 -0
  241. package/dist/types/status-line/context-usage.d.ts +156 -0
  242. package/dist/types/status-line/footer.d.ts +30 -0
  243. package/dist/types/status-line/git-utils.d.ts +22 -0
  244. package/dist/types/status-line/host.d.ts +129 -0
  245. package/dist/types/status-line/index.d.ts +6 -0
  246. package/dist/types/status-line/loop.d.ts +18 -0
  247. package/dist/types/status-line/metrics.d.ts +21 -0
  248. package/dist/types/status-line/presets.d.ts +3 -0
  249. package/dist/types/status-line/schema.d.ts +15 -0
  250. package/dist/types/status-line/segments.d.ts +5 -0
  251. package/dist/types/status-line/separators.d.ts +3 -0
  252. package/dist/types/status-line/types.d.ts +236 -0
  253. package/dist/types/stdin-buffer.d.ts +4 -0
  254. package/dist/types/theme/color.d.ts +24 -0
  255. package/dist/types/theme/defaults/index.d.ts +9489 -0
  256. package/dist/types/theme/index.d.ts +4 -0
  257. package/dist/types/theme/loader.d.ts +23 -0
  258. package/dist/types/theme/mermaid-cache.d.ts +26 -0
  259. package/dist/types/theme/schema-validation.d.ts +3 -0
  260. package/dist/types/theme/schema.d.ts +25 -0
  261. package/dist/types/theme/session-color.d.ts +43 -0
  262. package/dist/types/theme/shimmer.d.ts +44 -0
  263. package/dist/types/theme/symbols.d.ts +34 -0
  264. package/dist/types/theme/theme-class.d.ts +299 -0
  265. package/dist/types/theme/theme.d.ts +111 -0
  266. package/dist/types/theme/tui-adapters.d.ts +30 -0
  267. package/dist/types/thinking.d.ts +165 -0
  268. package/dist/types/tools/agent-tree.d.ts +36 -0
  269. package/dist/types/tools/ask.d.ts +67 -0
  270. package/dist/types/tools/ast-edit.d.ts +53 -0
  271. package/dist/types/tools/ast-grep.d.ts +52 -0
  272. package/dist/types/tools/autoresearch.d.ts +156 -0
  273. package/dist/types/tools/bash-interactive.d.ts +28 -0
  274. package/dist/types/tools/bash.d.ts +103 -0
  275. package/dist/types/tools/conflict-detect.d.ts +43 -0
  276. package/dist/types/tools/debug.d.ts +60 -0
  277. package/dist/types/tools/default-renderer.d.ts +24 -0
  278. package/dist/types/tools/edit.d.ts +158 -0
  279. package/dist/types/tools/eval-format/code-mode-declarations.d.ts +5 -0
  280. package/dist/types/tools/eval-format/index.d.ts +5 -0
  281. package/dist/types/tools/eval-format/javascript.d.ts +2 -0
  282. package/dist/types/tools/eval-format/python.d.ts +2 -0
  283. package/dist/types/tools/eval.d.ts +94 -0
  284. package/dist/types/tools/fetch.d.ts +42 -0
  285. package/dist/types/tools/gh-format.d.ts +8 -0
  286. package/dist/types/tools/github.d.ts +144 -0
  287. package/dist/types/tools/glob.d.ts +44 -0
  288. package/dist/types/tools/goal.d.ts +42 -0
  289. package/dist/types/tools/grep.d.ts +64 -0
  290. package/dist/types/tools/grouped-file-output.d.ts +71 -0
  291. package/dist/types/tools/hashline-format.d.ts +28 -0
  292. package/dist/types/tools/hub.d.ts +340 -0
  293. package/dist/types/tools/index.d.ts +4 -0
  294. package/dist/types/tools/json-tree.d.ts +59 -0
  295. package/dist/types/tools/line-ranges.d.ts +29 -0
  296. package/dist/types/tools/list-limit.d.ts +15 -0
  297. package/dist/types/tools/lsp.d.ts +72 -0
  298. package/dist/types/tools/match-line-format.d.ts +11 -0
  299. package/dist/types/tools/mcp.d.ts +81 -0
  300. package/dist/types/tools/memory.d.ts +67 -0
  301. package/dist/types/tools/output-meta.d.ts +151 -0
  302. package/dist/types/tools/read.d.ts +139 -0
  303. package/dist/types/tools/renderer.d.ts +98 -0
  304. package/dist/types/tools/report-tool-issue.d.ts +8 -0
  305. package/dist/types/tools/resolve.d.ts +47 -0
  306. package/dist/types/tools/streaming-output.d.ts +282 -0
  307. package/dist/types/tools/subprocess.d.ts +24 -0
  308. package/dist/types/tools/task-repair-args.d.ts +53 -0
  309. package/dist/types/tools/task-yield-assembly.d.ts +20 -0
  310. package/dist/types/tools/task.d.ts +425 -0
  311. package/dist/types/tools/terminal-output.d.ts +8 -0
  312. package/dist/types/tools/think.d.ts +13 -0
  313. package/dist/types/tools/todo.d.ts +118 -0
  314. package/dist/types/tools/tool-errors.d.ts +7 -0
  315. package/dist/types/tools/vibe.d.ts +120 -0
  316. package/dist/types/tools/web-search.d.ts +195 -0
  317. package/dist/types/tools/write.d.ts +76 -0
  318. package/dist/types/tools/xd-url.d.ts +13 -0
  319. package/dist/types/tools/xdev.d.ts +52 -0
  320. package/dist/types/tui.d.ts +18 -0
  321. package/dist/types/utils.d.ts +2 -0
  322. package/package.json +37 -3
  323. package/src/app-keybindings.ts +760 -0
  324. package/src/apps/autoresearch-dashboard.ts +500 -0
  325. package/src/apps/autoresearch-data.ts +64 -0
  326. package/src/apps/cleanse-board.ts +341 -0
  327. package/src/apps/cleanse-picker.ts +57 -0
  328. package/src/apps/debug/log-formatting.ts +53 -0
  329. package/src/apps/debug/log-viewer.ts +868 -0
  330. package/src/apps/debug/protocol-probe.ts +267 -0
  331. package/src/apps/debug/raw-sse-buffer.ts +449 -0
  332. package/src/apps/debug/raw-sse.ts +282 -0
  333. package/src/apps/debug/terminal-info.ts +115 -0
  334. package/src/apps/debug/viewer-frame.ts +174 -0
  335. package/src/apps/git/avatar.ts +52 -0
  336. package/src/apps/git/colors.ts +109 -0
  337. package/src/apps/git/diff-pane.ts +1512 -0
  338. package/src/apps/git/git-tui.ts +889 -0
  339. package/src/apps/git/sidebar.ts +1204 -0
  340. package/src/apps/git/state.ts +113 -0
  341. package/src/apps/if-bench-board.ts +324 -0
  342. package/src/apps/live-visualizer.ts +238 -0
  343. package/src/apps/ps-data.ts +116 -0
  344. package/src/apps/ps-top.ts +422 -0
  345. package/src/apps/session-picker.ts +95 -0
  346. package/src/apps/setup-model-picker.ts +21 -0
  347. package/src/apps/standalone-picker.ts +111 -0
  348. package/src/autocomplete.ts +9 -13
  349. package/src/chat/advisor-message.ts +175 -0
  350. package/src/chat/assistant-message.ts +1214 -0
  351. package/src/chat/background-tan-message.ts +35 -0
  352. package/src/chat/bash-execution.ts +394 -0
  353. package/src/chat/cache-invalidation-marker.ts +94 -0
  354. package/src/chat/chat-transcript-builder.ts +593 -0
  355. package/src/chat/collab-prompt-message.ts +34 -0
  356. package/src/chat/compaction-summary-message.ts +261 -0
  357. package/src/chat/custom-message.ts +23 -0
  358. package/src/chat/display-preferences.ts +24 -0
  359. package/src/chat/eval-execution.ts +172 -0
  360. package/src/chat/execution-shared.ts +113 -0
  361. package/src/chat/extension-types.ts +46 -0
  362. package/src/chat/hook-message.ts +20 -0
  363. package/src/chat/image-loading.ts +122 -0
  364. package/src/chat/late-diagnostics-message.ts +92 -0
  365. package/src/chat/messages.ts +305 -0
  366. package/src/chat/reaction.ts +65 -0
  367. package/src/chat/read-target.ts +7 -0
  368. package/src/chat/read-tool-group.ts +873 -0
  369. package/src/chat/served-model-marker.ts +91 -0
  370. package/src/chat/skill-message.ts +220 -0
  371. package/src/chat/skill-title-input.ts +41 -0
  372. package/src/chat/stripped-tool-calls-placeholder.ts +35 -0
  373. package/src/chat/thinking-display.ts +312 -0
  374. package/src/chat/todo-reminder.ts +33 -0
  375. package/src/chat/tool-execution.ts +1395 -0
  376. package/src/chat/transcript-browser.ts +268 -0
  377. package/src/chat/transcript-entry.ts +96 -0
  378. package/src/chat/transcript-outline.ts +260 -0
  379. package/src/chat/transcript-render-helpers.ts +315 -0
  380. package/src/chat/ttsr-notification.ts +123 -0
  381. package/src/chat/user-message.ts +287 -0
  382. package/src/chrome/chat-block.ts +110 -0
  383. package/src/chrome/collab-qrcode.ts +74 -0
  384. package/src/chrome/context-thresholds.ts +85 -0
  385. package/src/chrome/countdown-timer.ts +74 -0
  386. package/src/chrome/diff.ts +266 -0
  387. package/src/chrome/dynamic-border.ts +36 -0
  388. package/src/chrome/error-block.ts +63 -0
  389. package/src/chrome/form-theme.ts +10 -0
  390. package/src/chrome/format.ts +66 -0
  391. package/src/chrome/index.ts +21 -0
  392. package/src/chrome/keybinding-hints.ts +55 -0
  393. package/src/chrome/live-board.ts +123 -0
  394. package/src/chrome/local-date.ts +20 -0
  395. package/src/chrome/message-divider.ts +51 -0
  396. package/src/chrome/message-frame.ts +164 -0
  397. package/src/chrome/message-notice.ts +103 -0
  398. package/src/chrome/overlay-box.ts +301 -0
  399. package/src/chrome/qrcode.ts +539 -0
  400. package/src/chrome/segment-track.ts +86 -0
  401. package/src/chrome/select-list-mouse-routing.ts +11 -0
  402. package/src/chrome/selector-helpers.ts +119 -0
  403. package/src/chrome/shared.ts +27 -0
  404. package/src/chrome/streaming-panel.ts +72 -0
  405. package/src/chrome/tool-activity.ts +44 -0
  406. package/src/chrome/transcript-container.ts +902 -0
  407. package/src/chrome/transcript-status.ts +27 -0
  408. package/src/chrome/visual-truncate.ts +70 -0
  409. package/src/components/disclosure.ts +235 -0
  410. package/src/components/editor.ts +10 -12
  411. package/src/components/form.ts +566 -0
  412. package/src/components/image.ts +80 -53
  413. package/src/components/input.ts +6 -27
  414. package/src/components/key-value-list.ts +88 -0
  415. package/src/components/layout/geometry.ts +202 -0
  416. package/src/components/layout/row.ts +283 -0
  417. package/src/components/layout/split-pane.ts +349 -0
  418. package/src/components/layout/stack.ts +261 -0
  419. package/src/components/markdown.ts +16 -12
  420. package/src/components/menu-selection.ts +248 -0
  421. package/src/components/metric.ts +132 -0
  422. package/src/components/progress-bar.ts +140 -0
  423. package/src/components/scroll-view.ts +372 -70
  424. package/src/components/scroll-viewport.ts +154 -0
  425. package/src/components/section.ts +77 -0
  426. package/src/components/select-list.ts +238 -159
  427. package/src/components/settings-list.ts +103 -80
  428. package/src/components/table.ts +127 -0
  429. package/src/components/tree-view.ts +481 -0
  430. package/src/components/wizard-step.ts +229 -0
  431. package/src/deccara.ts +1 -3
  432. package/src/hotkeys-markdown.ts +100 -0
  433. package/src/index.ts +18 -0
  434. package/src/keybinding-matchers.ts +99 -0
  435. package/src/keys.ts +2 -2
  436. package/src/lang-from-path.ts +252 -0
  437. package/src/overlays/advisor-config.ts +761 -0
  438. package/src/overlays/agent-activity.ts +113 -0
  439. package/src/overlays/agent-hub-projection.ts +250 -0
  440. package/src/overlays/agent-hub-renderer.ts +281 -0
  441. package/src/overlays/agent-hub-types.ts +74 -0
  442. package/src/overlays/agent-hub.ts +1562 -0
  443. package/src/overlays/agent-transcript-viewer.ts +669 -0
  444. package/src/overlays/agents-hub.ts +1203 -0
  445. package/src/overlays/ask-dialog.ts +1202 -0
  446. package/src/overlays/bordered-loader.ts +41 -0
  447. package/src/overlays/btw-history-panel.ts +591 -0
  448. package/src/overlays/btw-history.ts +33 -0
  449. package/src/overlays/btw-panel.ts +167 -0
  450. package/src/overlays/cleanse-panel.ts +167 -0
  451. package/src/overlays/codex-reset-fireworks.ts +375 -0
  452. package/src/overlays/composer-shape-preview.ts +137 -0
  453. package/src/overlays/composer-shape-registry.ts +82 -0
  454. package/src/overlays/copy-selector.ts +618 -0
  455. package/src/overlays/copy-targets.ts +210 -0
  456. package/src/overlays/error-banner.ts +39 -0
  457. package/src/overlays/extensions/display-text.ts +39 -0
  458. package/src/overlays/extensions/extension-dashboard.ts +645 -0
  459. package/src/overlays/extensions/extension-list.ts +667 -0
  460. package/src/overlays/extensions/inspector-model.ts +484 -0
  461. package/src/overlays/extensions/inspector-panel.ts +669 -0
  462. package/src/overlays/extensions/live-tool-session.ts +67 -0
  463. package/src/overlays/extensions/mcp-runtime.ts +289 -0
  464. package/src/overlays/extensions/state-manager.ts +337 -0
  465. package/src/overlays/extensions/types.ts +208 -0
  466. package/src/overlays/history-search.ts +268 -0
  467. package/src/overlays/hook-editor.ts +272 -0
  468. package/src/overlays/hook-input.ts +84 -0
  469. package/src/overlays/hook-selector.ts +635 -0
  470. package/src/overlays/hub-frame.ts +262 -0
  471. package/src/overlays/login-dialog.ts +231 -0
  472. package/src/overlays/logout-account-selector.ts +115 -0
  473. package/src/overlays/mcp-add-wizard.ts +1436 -0
  474. package/src/overlays/model-browser.ts +1176 -0
  475. package/src/overlays/model-hub.ts +2279 -0
  476. package/src/overlays/model-picker.ts +285 -0
  477. package/src/overlays/model-selector.ts +107 -0
  478. package/src/overlays/move-overlay.ts +197 -0
  479. package/src/overlays/oauth-selector.ts +446 -0
  480. package/src/overlays/omfg-panel.ts +136 -0
  481. package/src/overlays/pause-screen.ts +198 -0
  482. package/src/overlays/plan-review-overlay.ts +1227 -0
  483. package/src/overlays/plan-save-overlay.ts +74 -0
  484. package/src/overlays/plan-toc.ts +139 -0
  485. package/src/overlays/plugin-selector.ts +97 -0
  486. package/src/overlays/plugin-settings.ts +839 -0
  487. package/src/overlays/queue-mode-selector.ts +55 -0
  488. package/src/overlays/reset-usage-selector.ts +170 -0
  489. package/src/overlays/rewind-selector.ts +499 -0
  490. package/src/overlays/running-subagent-badge.ts +19 -0
  491. package/src/overlays/session-account-selector.ts +68 -0
  492. package/src/overlays/session-info-overlay.ts +96 -0
  493. package/src/overlays/session-observer-registry.ts +289 -0
  494. package/src/overlays/session-selector.ts +1137 -0
  495. package/src/overlays/settings-defs.ts +315 -0
  496. package/src/overlays/settings-selector.ts +1415 -0
  497. package/src/overlays/show-images-selector.ts +44 -0
  498. package/src/overlays/snapcompact-shape-preview-doc.md +14 -0
  499. package/src/overlays/snapcompact-shape-preview.ts +192 -0
  500. package/src/overlays/theme-selector.ts +62 -0
  501. package/src/overlays/thinking-selector.ts +51 -0
  502. package/src/overlays/tiny-title-download-progress.ts +102 -0
  503. package/src/overlays/tree-selector.ts +1121 -0
  504. package/src/overlays/usage-dashboard.ts +629 -0
  505. package/src/overlays/usage-display.ts +45 -0
  506. package/src/overlays/usage-row.ts +118 -0
  507. package/src/prompt/attachment-chips.ts +201 -0
  508. package/src/prompt/composer-attachments.ts +280 -0
  509. package/src/prompt/composer-cache.ts +304 -0
  510. package/src/prompt/composer.ts +934 -0
  511. package/src/prompt/custom-editor.ts +1392 -0
  512. package/src/prompt/data/emojis.json +1 -0
  513. package/src/prompt/editor-top-gap.ts +28 -0
  514. package/src/prompt/emoji-autocomplete.ts +285 -0
  515. package/src/prompt/github-ref-autocomplete.ts +75 -0
  516. package/src/prompt/gradient-highlight.ts +101 -0
  517. package/src/prompt/image-format.ts +28 -0
  518. package/src/prompt/image-references.ts +102 -0
  519. package/src/prompt/interactive-context-helpers.ts +126 -0
  520. package/src/prompt/internal-url-autocomplete.ts +155 -0
  521. package/src/prompt/macos-spelling.ts +456 -0
  522. package/src/prompt/magic-keyword-boundary.ts +23 -0
  523. package/src/prompt/magic-keywords.ts +42 -0
  524. package/src/prompt/markdown-prose.ts +245 -0
  525. package/src/prompt/model-mention-autocomplete.ts +94 -0
  526. package/src/prompt/model-mention-syntax.ts +41 -0
  527. package/src/prompt/orchestrate.ts +26 -0
  528. package/src/prompt/prompt-action-autocomplete.ts +337 -0
  529. package/src/prompt/queue-input.ts +132 -0
  530. package/src/prompt/skill-tokens.ts +36 -0
  531. package/src/prompt/tips.txt +27 -0
  532. package/src/prompt/tools-markdown.ts +29 -0
  533. package/src/prompt/ultrathink.ts +26 -0
  534. package/src/prompt/usage-amounts.ts +99 -0
  535. package/src/prompt/video.ts +47 -0
  536. package/src/prompt/welcome.ts +605 -0
  537. package/src/prompt/workflow.ts +26 -0
  538. package/src/render/code-cell.ts +244 -0
  539. package/src/render/file-list.ts +58 -0
  540. package/src/render/hyperlink.ts +192 -0
  541. package/src/render/index.ts +17 -0
  542. package/src/render/output-block.ts +267 -0
  543. package/src/render/output-pane.ts +299 -0
  544. package/src/render/render-utils.ts +1182 -0
  545. package/src/render/sixel.ts +69 -0
  546. package/src/render/status-line.ts +55 -0
  547. package/src/render/tool-card.ts +272 -0
  548. package/src/render/tree-list.ts +188 -0
  549. package/src/render/types.ts +19 -0
  550. package/src/render/utils.ts +109 -0
  551. package/src/render/width-aware-text.ts +60 -0
  552. package/src/setup/lazy.ts +17 -0
  553. package/src/setup/scenes/composer.ts +127 -0
  554. package/src/setup/scenes/glyph.ts +120 -0
  555. package/src/setup/scenes/model.ts +138 -0
  556. package/src/setup/scenes/outro.ts +24 -0
  557. package/src/setup/scenes/providers.ts +107 -0
  558. package/src/setup/scenes/sign-in.ts +373 -0
  559. package/src/setup/scenes/splash.ts +192 -0
  560. package/src/setup/scenes/theme.ts +343 -0
  561. package/src/setup/scenes/types.ts +111 -0
  562. package/src/setup/scenes/web-search.ts +165 -0
  563. package/src/setup/setup-version.ts +11 -0
  564. package/src/setup/startup-splash.ts +107 -0
  565. package/src/setup/wizard-overlay.ts +319 -0
  566. package/src/setup/wizard.ts +101 -0
  567. package/src/status-line/component.ts +3003 -0
  568. package/src/status-line/context-usage.ts +688 -0
  569. package/src/status-line/footer.ts +323 -0
  570. package/src/status-line/git-utils.ts +42 -0
  571. package/src/status-line/host.ts +97 -0
  572. package/src/status-line/index.ts +6 -0
  573. package/src/status-line/loop.ts +28 -0
  574. package/src/status-line/metrics.ts +94 -0
  575. package/src/status-line/presets.ts +107 -0
  576. package/src/status-line/schema.ts +57 -0
  577. package/src/status-line/segments.ts +934 -0
  578. package/src/status-line/separators.ts +55 -0
  579. package/src/status-line/types.ts +227 -0
  580. package/src/stdin-buffer.ts +4 -2
  581. package/src/theme/color.ts +95 -0
  582. package/src/theme/dark.json +95 -0
  583. package/src/theme/defaults/alabaster.json +93 -0
  584. package/src/theme/defaults/amethyst.json +96 -0
  585. package/src/theme/defaults/anthracite.json +93 -0
  586. package/src/theme/defaults/basalt.json +91 -0
  587. package/src/theme/defaults/birch.json +95 -0
  588. package/src/theme/defaults/dark-abyss.json +91 -0
  589. package/src/theme/defaults/dark-arctic.json +104 -0
  590. package/src/theme/defaults/dark-aurora.json +95 -0
  591. package/src/theme/defaults/dark-catppuccin.json +107 -0
  592. package/src/theme/defaults/dark-cavern.json +91 -0
  593. package/src/theme/defaults/dark-celestial.json +106 -0
  594. package/src/theme/defaults/dark-copper.json +95 -0
  595. package/src/theme/defaults/dark-cosmos.json +90 -0
  596. package/src/theme/defaults/dark-cyberpunk.json +102 -0
  597. package/src/theme/defaults/dark-dracula.json +98 -0
  598. package/src/theme/defaults/dark-eclipse.json +91 -0
  599. package/src/theme/defaults/dark-ember.json +95 -0
  600. package/src/theme/defaults/dark-equinox.json +90 -0
  601. package/src/theme/defaults/dark-forest.json +96 -0
  602. package/src/theme/defaults/dark-github.json +105 -0
  603. package/src/theme/defaults/dark-gruvbox.json +112 -0
  604. package/src/theme/defaults/dark-lavender.json +95 -0
  605. package/src/theme/defaults/dark-lunar.json +89 -0
  606. package/src/theme/defaults/dark-midnight.json +95 -0
  607. package/src/theme/defaults/dark-monochrome.json +94 -0
  608. package/src/theme/defaults/dark-monokai.json +98 -0
  609. package/src/theme/defaults/dark-nebula.json +90 -0
  610. package/src/theme/defaults/dark-nord.json +98 -0
  611. package/src/theme/defaults/dark-ocean.json +101 -0
  612. package/src/theme/defaults/dark-one.json +100 -0
  613. package/src/theme/defaults/dark-poimandres.json +115 -0
  614. package/src/theme/defaults/dark-rainforest.json +91 -0
  615. package/src/theme/defaults/dark-reef.json +91 -0
  616. package/src/theme/defaults/dark-retro.json +92 -0
  617. package/src/theme/defaults/dark-rose-pine.json +96 -0
  618. package/src/theme/defaults/dark-sakura.json +95 -0
  619. package/src/theme/defaults/dark-slate.json +95 -0
  620. package/src/theme/defaults/dark-solarized.json +97 -0
  621. package/src/theme/defaults/dark-solstice.json +90 -0
  622. package/src/theme/defaults/dark-starfall.json +91 -0
  623. package/src/theme/defaults/dark-sunset.json +99 -0
  624. package/src/theme/defaults/dark-swamp.json +90 -0
  625. package/src/theme/defaults/dark-synthwave.json +103 -0
  626. package/src/theme/defaults/dark-taiga.json +91 -0
  627. package/src/theme/defaults/dark-terminal.json +95 -0
  628. package/src/theme/defaults/dark-tokyo-night.json +101 -0
  629. package/src/theme/defaults/dark-tundra.json +91 -0
  630. package/src/theme/defaults/dark-twilight.json +91 -0
  631. package/src/theme/defaults/dark-volcanic.json +91 -0
  632. package/src/theme/defaults/graphite.json +92 -0
  633. package/src/theme/defaults/index.ts +201 -0
  634. package/src/theme/defaults/light-arctic.json +107 -0
  635. package/src/theme/defaults/light-aurora-day.json +91 -0
  636. package/src/theme/defaults/light-canyon.json +91 -0
  637. package/src/theme/defaults/light-catppuccin.json +106 -0
  638. package/src/theme/defaults/light-cirrus.json +90 -0
  639. package/src/theme/defaults/light-coral.json +95 -0
  640. package/src/theme/defaults/light-cyberpunk.json +96 -0
  641. package/src/theme/defaults/light-dawn.json +90 -0
  642. package/src/theme/defaults/light-dunes.json +91 -0
  643. package/src/theme/defaults/light-eucalyptus.json +95 -0
  644. package/src/theme/defaults/light-forest.json +100 -0
  645. package/src/theme/defaults/light-frost.json +95 -0
  646. package/src/theme/defaults/light-github.json +115 -0
  647. package/src/theme/defaults/light-glacier.json +91 -0
  648. package/src/theme/defaults/light-gruvbox.json +108 -0
  649. package/src/theme/defaults/light-haze.json +90 -0
  650. package/src/theme/defaults/light-honeycomb.json +95 -0
  651. package/src/theme/defaults/light-lagoon.json +91 -0
  652. package/src/theme/defaults/light-lavender.json +95 -0
  653. package/src/theme/defaults/light-meadow.json +91 -0
  654. package/src/theme/defaults/light-mint.json +95 -0
  655. package/src/theme/defaults/light-monochrome.json +101 -0
  656. package/src/theme/defaults/light-ocean.json +99 -0
  657. package/src/theme/defaults/light-one.json +99 -0
  658. package/src/theme/defaults/light-opal.json +91 -0
  659. package/src/theme/defaults/light-orchard.json +91 -0
  660. package/src/theme/defaults/light-paper.json +95 -0
  661. package/src/theme/defaults/light-poimandres.json +115 -0
  662. package/src/theme/defaults/light-prism.json +90 -0
  663. package/src/theme/defaults/light-retro.json +98 -0
  664. package/src/theme/defaults/light-sand.json +95 -0
  665. package/src/theme/defaults/light-savanna.json +91 -0
  666. package/src/theme/defaults/light-solarized.json +102 -0
  667. package/src/theme/defaults/light-soleil.json +90 -0
  668. package/src/theme/defaults/light-sunset.json +99 -0
  669. package/src/theme/defaults/light-synthwave.json +98 -0
  670. package/src/theme/defaults/light-tokyo-night.json +111 -0
  671. package/src/theme/defaults/light-wetland.json +91 -0
  672. package/src/theme/defaults/light-zenith.json +89 -0
  673. package/src/theme/defaults/limestone.json +94 -0
  674. package/src/theme/defaults/mahogany.json +97 -0
  675. package/src/theme/defaults/marble.json +93 -0
  676. package/src/theme/defaults/obsidian.json +91 -0
  677. package/src/theme/defaults/onyx.json +91 -0
  678. package/src/theme/defaults/pearl.json +93 -0
  679. package/src/theme/defaults/porcelain.json +91 -0
  680. package/src/theme/defaults/quartz.json +96 -0
  681. package/src/theme/defaults/sandstone.json +95 -0
  682. package/src/theme/defaults/titanium.json +90 -0
  683. package/src/theme/index.ts +5 -0
  684. package/src/theme/light.json +93 -0
  685. package/src/theme/loader.ts +200 -0
  686. package/src/theme/mermaid-cache.ts +92 -0
  687. package/src/theme/schema-validation.ts +119 -0
  688. package/src/theme/schema.ts +168 -0
  689. package/src/theme/session-color.ts +253 -0
  690. package/src/theme/shimmer.ts +307 -0
  691. package/src/theme/symbols.ts +1433 -0
  692. package/src/theme/theme-class.ts +787 -0
  693. package/src/theme/theme-schema.json +463 -0
  694. package/src/theme/theme.ts +810 -0
  695. package/src/theme/tui-adapters.ts +339 -0
  696. package/src/thinking.ts +380 -0
  697. package/src/tools/agent-tree.ts +128 -0
  698. package/src/tools/ask.ts +482 -0
  699. package/src/tools/ast-edit.ts +210 -0
  700. package/src/tools/ast-grep.ts +171 -0
  701. package/src/tools/autoresearch.ts +287 -0
  702. package/src/tools/bash-interactive.ts +295 -0
  703. package/src/tools/bash.ts +468 -0
  704. package/src/tools/conflict-detect.ts +77 -0
  705. package/src/tools/debug.ts +167 -0
  706. package/src/tools/default-renderer.ts +172 -0
  707. package/src/tools/edit.ts +1258 -0
  708. package/src/tools/eval-format/code-mode-declarations.ts +61 -0
  709. package/src/tools/eval-format/index.ts +16 -0
  710. package/src/tools/eval-format/javascript.ts +952 -0
  711. package/src/tools/eval-format/python.ts +544 -0
  712. package/src/tools/eval.ts +835 -0
  713. package/src/tools/fetch.ts +226 -0
  714. package/src/tools/gh-format.ts +18 -0
  715. package/src/tools/github.ts +686 -0
  716. package/src/tools/glob.ts +213 -0
  717. package/src/tools/goal.ts +153 -0
  718. package/src/tools/grep.ts +385 -0
  719. package/src/tools/grouped-file-output.ts +211 -0
  720. package/src/tools/hashline-format.ts +59 -0
  721. package/src/tools/hub.ts +1488 -0
  722. package/src/tools/index.ts +71 -0
  723. package/src/tools/json-tree.ts +388 -0
  724. package/src/tools/line-ranges.ts +98 -0
  725. package/src/tools/list-limit.ts +43 -0
  726. package/src/tools/lsp.ts +702 -0
  727. package/src/tools/match-line-format.ts +20 -0
  728. package/src/tools/mcp.ts +309 -0
  729. package/src/tools/memory.ts +220 -0
  730. package/src/tools/output-meta.ts +367 -0
  731. package/src/tools/read.ts +628 -0
  732. package/src/tools/renderer.ts +107 -0
  733. package/src/tools/report-tool-issue.ts +13 -0
  734. package/src/tools/resolve.ts +139 -0
  735. package/src/tools/streaming-output.ts +1576 -0
  736. package/src/tools/subprocess.ts +39 -0
  737. package/src/tools/task-repair-args.ts +118 -0
  738. package/src/tools/task-yield-assembly.ts +147 -0
  739. package/src/tools/task.ts +2110 -0
  740. package/src/tools/terminal-output.ts +153 -0
  741. package/src/tools/think.ts +28 -0
  742. package/src/tools/todo.ts +580 -0
  743. package/src/tools/tool-errors.ts +15 -0
  744. package/src/tools/vibe.ts +420 -0
  745. package/src/tools/web-search.ts +409 -0
  746. package/src/tools/write.ts +511 -0
  747. package/src/tools/xd-url.ts +28 -0
  748. package/src/tools/xdev.ts +201 -0
  749. package/src/tui.ts +70 -8
  750. package/src/utils.ts +8 -0
@@ -0,0 +1,1436 @@
1
+ /**
2
+ * MCP Add Wizard Component
3
+ *
4
+ * Interactive multi-step wizard for adding MCP servers.
5
+ */
6
+ import { Container, matchesKey, replaceTabs, Spacer, Text, truncateToWidth, wrapTextWithAnsi } from "../index";
7
+ import type { Component } from "../tui";
8
+ import { getMCPConfigPath, getProjectDir } from "@oh-my-pi/pi-utils";
9
+ import { shortenPath } from "../render/render-utils";
10
+ import { getSelectListTheme, theme } from "../theme/theme";
11
+ import { matchesAppInterrupt, matchesSelectDown, matchesSelectUp } from "../keybinding-matchers";
12
+ import { OverlayPanel } from "../chrome/overlay-box";
13
+ import { TextFormField, type FormFieldTheme } from "../components/form";
14
+ import { SelectList } from "../components/select-list";
15
+ import { WizardStep, type WizardStepKind } from "../components/wizard-step";
16
+
17
+ type TransportType = "stdio" | "http" | "sse";
18
+ type AuthMethod = "none" | "oauth" | "manual";
19
+ type AuthLocation = "env" | "header";
20
+ type Scope = "user" | "project";
21
+
22
+ /** Authentication fields produced by the wizard. */
23
+ interface MCPAddWizardAuth {
24
+ type: "oauth";
25
+ credentialId: string;
26
+ tokenUrl?: string;
27
+ clientId?: string;
28
+ clientSecret?: string;
29
+ resource?: string;
30
+ }
31
+
32
+ interface MCPAddWizardConfigBase {
33
+ timeout?: number;
34
+ auth?: MCPAddWizardAuth;
35
+ }
36
+
37
+ interface MCPAddWizardStdioConfig extends MCPAddWizardConfigBase {
38
+ type: "stdio";
39
+ command: string;
40
+ args?: string[];
41
+ env?: Record<string, string>;
42
+ }
43
+
44
+ interface MCPAddWizardRemoteConfig extends MCPAddWizardConfigBase {
45
+ type: "http" | "sse";
46
+ url: string;
47
+ headers?: Record<string, string>;
48
+ }
49
+
50
+ /** Server configuration fields produced for testing and persistence. */
51
+ export type MCPAddWizardConfig = MCPAddWizardStdioConfig | MCPAddWizardRemoteConfig;
52
+
53
+ interface MCPAddWizardOAuthEndpoints {
54
+ authorizationUrl: string;
55
+ tokenUrl: string;
56
+ issuerUrl?: string;
57
+ clientId?: string;
58
+ registrationUrl?: string;
59
+ scopes?: string;
60
+ resource?: string;
61
+ }
62
+
63
+ interface MCPAddWizardAuthDetection {
64
+ requiresAuth: boolean;
65
+ authType?: "oauth" | "apikey" | "unknown";
66
+ oauth?: MCPAddWizardOAuthEndpoints;
67
+ authServerUrl?: string;
68
+ resourceMetadataUrl?: string;
69
+ scopes?: string;
70
+ }
71
+
72
+ /** Host-owned validation and OAuth discovery capabilities. */
73
+ export interface MCPAddWizardDeps {
74
+ validateServerName(name: string): string | undefined;
75
+ analyzeAuthError(error: Error, serverUrl?: string): MCPAddWizardAuthDetection;
76
+ discoverOAuthEndpoints(
77
+ serverUrl: string,
78
+ authServerUrl?: string,
79
+ resourceMetadataUrl?: string,
80
+ options?: { protectedScopes?: string },
81
+ ): Promise<MCPAddWizardOAuthEndpoints | null>;
82
+ fetchResourceMetadataScopes(resourceMetadataUrl: string): Promise<string | undefined>;
83
+ }
84
+
85
+ type MCPWizardStep =
86
+ | "name"
87
+ | "transport"
88
+ | "command"
89
+ | "args"
90
+ | "url"
91
+ | "auth-method"
92
+ | "oauth-error"
93
+ | "oauth-auth-url"
94
+ | "oauth-token-url"
95
+ | "oauth-client-id"
96
+ | "oauth-client-secret"
97
+ | "oauth-scopes"
98
+ | "apikey"
99
+ | "auth-location"
100
+ | "env-var-name"
101
+ | "header-name"
102
+ | "scope"
103
+ | "confirm";
104
+
105
+ /**
106
+ * Result of the wizard's OAuth callback. `credentialId` is mandatory;
107
+ * `clientId` is populated when the OAuth provider performed dynamic client
108
+ * registration (or when the caller pre-supplied it) so the wizard can fold it
109
+ * into the final `mcp.json` entry. Refresh material (including any DCR client
110
+ * secret) is embedded in the stored credential, never written to config files.
111
+ */
112
+ export interface MCPAddWizardOAuthResult {
113
+ credentialId: string;
114
+ clientId?: string;
115
+ resource?: string;
116
+ }
117
+
118
+ interface MCPAddWizardOAuthOptions {
119
+ serverUrl?: string;
120
+ resource?: string;
121
+ stripSameOriginResource?: boolean;
122
+ registrationUrl?: string;
123
+ issuerUrl?: string;
124
+ /**
125
+ * External cancellation source. Aborting it tears down the in-flight OAuth
126
+ * flow and surfaces a neutral cancellation error. The wizard wires its own
127
+ * controller here so Esc cancels the OAuth wait instead of stepping back
128
+ * through the form (the wizard is focused, so the editor's Esc hook does
129
+ * not fire).
130
+ */
131
+ abortSignal?: AbortSignal;
132
+ }
133
+
134
+ interface WizardState {
135
+ name: string;
136
+ transport: TransportType | null;
137
+ command: string;
138
+ args: string;
139
+ url: string;
140
+ authMethod: AuthMethod;
141
+ oauthAuthUrl: string;
142
+ oauthTokenUrl: string;
143
+ oauthRegistrationUrl: string;
144
+ oauthIssuerUrl: string;
145
+ oauthClientId: string;
146
+ oauthClientSecret: string;
147
+ oauthScopes: string;
148
+ oauthResource: string;
149
+ oauthResourceIsFallback: boolean;
150
+ oauthCredentialId: string | null;
151
+ apiKey: string;
152
+ authLocation: AuthLocation | null;
153
+ envVarName: string;
154
+ headerName: string;
155
+ scope: Scope | null;
156
+ }
157
+
158
+ /** Max display width for sanitized error/URL text in wizard TUI */
159
+ const MAX_DISPLAY_WIDTH = 120;
160
+
161
+ /** Sanitize a string for TUI display: replace tabs and truncate */
162
+ function sanitize(text: string): string {
163
+ return truncateToWidth(replaceTabs(text), MAX_DISPLAY_WIDTH);
164
+ }
165
+
166
+ const mcpFormTheme: FormFieldTheme = {
167
+ label: text => text,
168
+ description: text => theme.fg("muted", text),
169
+ error: text => theme.fg("error", text),
170
+ hint: text => theme.fg("muted", text),
171
+ };
172
+
173
+ interface WizardChoiceOption {
174
+ readonly label: string;
175
+ /** Shown dimmed beneath the row while the row is not highlighted. */
176
+ readonly description?: string;
177
+ }
178
+
179
+ export class MCPAddWizard extends OverlayPanel {
180
+ #deps: MCPAddWizardDeps;
181
+ #currentStep: MCPWizardStep = "name";
182
+ #state: WizardState = {
183
+ name: "",
184
+ transport: null,
185
+ command: "",
186
+ args: "",
187
+ url: "",
188
+ authMethod: "none",
189
+ oauthAuthUrl: "",
190
+ oauthTokenUrl: "",
191
+ oauthRegistrationUrl: "",
192
+ oauthIssuerUrl: "",
193
+ oauthClientId: "",
194
+ oauthClientSecret: "",
195
+ oauthScopes: "",
196
+ oauthResource: "",
197
+ oauthResourceIsFallback: false,
198
+ oauthCredentialId: null,
199
+ apiKey: "",
200
+ authLocation: null,
201
+ envVarName: "API_KEY",
202
+ headerName: "Authorization",
203
+ scope: null,
204
+ };
205
+
206
+ #contentContainer: Container;
207
+ #step: WizardStep | null = null;
208
+ #stepKind: WizardStepKind = "input";
209
+ #inputField: TextFormField | null = null;
210
+ #selectedIndex = 0;
211
+ #validationError: string | null = null;
212
+ #oauthErrorLines: readonly string[] | null = null;
213
+ #oauthErrorHeading: { text: string; tone: "error" | "muted" } | null = null;
214
+ #onCompleteCallback: (name: string, config: MCPAddWizardConfig, scope: Scope) => void;
215
+ #onCancelCallback: () => void;
216
+ #onOAuthCallback:
217
+ | ((
218
+ authUrl: string,
219
+ tokenUrl: string,
220
+ clientId: string,
221
+ clientSecret: string,
222
+ scopes: string,
223
+ options?: MCPAddWizardOAuthOptions,
224
+ ) => Promise<MCPAddWizardOAuthResult>)
225
+ | null = null;
226
+ #onTestConnectionCallback: ((config: MCPAddWizardConfig) => Promise<void>) | null = null;
227
+ #onRenderCallback: (() => void) | null = null;
228
+ /**
229
+ * Set while the OAuth callback is in flight; populated by
230
+ * {@link #launchOAuthFlow} and consumed by {@link handleInput} so Esc
231
+ * cancels the OAuth wait instead of stepping back through the form.
232
+ */
233
+ #oauthAbort: AbortController | null = null;
234
+
235
+ constructor(
236
+ deps: MCPAddWizardDeps,
237
+ onComplete: (name: string, config: MCPAddWizardConfig, scope: Scope) => void,
238
+ onCancel: () => void,
239
+ onOAuth?: (
240
+ authUrl: string,
241
+ tokenUrl: string,
242
+ clientId: string,
243
+ clientSecret: string,
244
+ scopes: string,
245
+ options?: MCPAddWizardOAuthOptions,
246
+ ) => Promise<MCPAddWizardOAuthResult>,
247
+ onTestConnection?: (config: MCPAddWizardConfig) => Promise<void>,
248
+ onRender?: () => void,
249
+ initialName?: string,
250
+ ) {
251
+ super("Add MCP Server");
252
+ this.#deps = deps;
253
+ this.#onCompleteCallback = onComplete;
254
+ this.#onCancelCallback = onCancel;
255
+ this.#onOAuthCallback = onOAuth ?? null;
256
+ this.#onTestConnectionCallback = onTestConnection ?? null;
257
+ this.#onRenderCallback = onRender ?? null;
258
+ if (initialName && initialName.trim().length > 0) {
259
+ this.#state.name = initialName.trim();
260
+ this.#currentStep = "transport";
261
+ }
262
+
263
+ this.addChild(new Spacer(1));
264
+
265
+ // Content container for step-specific content
266
+ this.#contentContainer = new Container();
267
+ this.addChild(this.#contentContainer);
268
+
269
+ this.addChild(new Spacer(1));
270
+
271
+ // Render first step
272
+ this.#renderStep();
273
+ }
274
+
275
+ #requestRender(): void {
276
+ this.#onRenderCallback?.();
277
+ }
278
+
279
+ /** Mount a step as the wizard body. Overlay chrome is unbounded, so no height budget applies. */
280
+ #show(step: WizardStep): void {
281
+ step.setMaxHeight(undefined);
282
+ this.#step = step;
283
+ this.#contentContainer.clear();
284
+ this.#contentContainer.addChild(step);
285
+ }
286
+
287
+ #inputStep(options: {
288
+ heading: string;
289
+ prompt: string;
290
+ initial: string;
291
+ hint: string;
292
+ details?: readonly string[];
293
+ optional: boolean;
294
+ error?: string | null;
295
+ }): void {
296
+ this.#stepKind = "input";
297
+ const field = new TextFormField({
298
+ theme: mcpFormTheme,
299
+ label: options.prompt,
300
+ details: options.details?.map(detail => new Text(theme.fg("muted", detail), 0, 0)),
301
+ summary: options.error
302
+ ? [new Text(theme.fg("error", `✗ ${sanitize(options.error)}`), 0, 0), new Spacer(1)]
303
+ : undefined,
304
+ hint: options.hint,
305
+ initialValue: options.initial,
306
+ empty: options.optional ? "submit" : "reject",
307
+ emptyError: " ",
308
+ onSubmit: () => this.#saveInputAndProceed(),
309
+ onCancel: () => this.#cancelInputStep(),
310
+ });
311
+ this.#inputField = field;
312
+ this.#show(
313
+ new WizardStep({
314
+ kind: "input",
315
+ heading: new Text(theme.fg("accent", options.heading), 0, 0),
316
+ content: field,
317
+ }),
318
+ );
319
+ }
320
+
321
+ #choiceStep(options: {
322
+ heading: string;
323
+ headingTone?: "accent" | "error" | "muted";
324
+ intro?: Component;
325
+ choices: readonly WizardChoiceOption[];
326
+ hint: string;
327
+ kind?: WizardStepKind;
328
+ }): void {
329
+ const tone = options.headingTone ?? "accent";
330
+ const items = options.choices.map((choice, index) => ({
331
+ value: String(index),
332
+ label: choice.label,
333
+ description: choice.description,
334
+ }));
335
+ const list = new SelectList(items, MAX_DISPLAY_WIDTH * Math.max(1, items.length), getSelectListTheme(), {
336
+ search: "never",
337
+ renderItem: ({ item, selected, width }) => {
338
+ const prefix = selected ? theme.fg("accent", `${theme.nav.cursor} `) : " ";
339
+ const label = selected ? theme.fg("accent", item.label) : item.label;
340
+ const lines = wrapTextWithAnsi(prefix + label, width);
341
+ if (item.description && !selected) {
342
+ lines.push(...wrapTextWithAnsi(` ${theme.fg("dim", item.description)}`, width));
343
+ }
344
+ return lines;
345
+ },
346
+ });
347
+ list.setSelectedIndex(this.#selectedIndex);
348
+ this.#stepKind = options.kind ?? "choice";
349
+ this.#show(
350
+ new WizardStep({
351
+ kind: this.#stepKind,
352
+ heading: new Text(theme.fg(tone, options.heading), 0, 0),
353
+ intro: options.intro,
354
+ content: list,
355
+ footer: new Text(theme.fg("muted", options.hint), 0, 0),
356
+ }),
357
+ );
358
+ }
359
+
360
+ #asyncStep(
361
+ heading: string,
362
+ headingTone: "accent" | "success" | "warning" | "error" | "muted",
363
+ body: Component,
364
+ footer?: Component,
365
+ ): void {
366
+ this.#stepKind = "async";
367
+ this.#inputField = null;
368
+ this.#show(
369
+ new WizardStep({
370
+ kind: "async",
371
+ heading: new Text(theme.fg(headingTone, heading), 0, 0),
372
+ content: body,
373
+ footer,
374
+ }),
375
+ );
376
+ }
377
+
378
+ #cancelInputStep(): void {
379
+ if (this.#currentStep === "name") {
380
+ this.#onCancelCallback();
381
+ return;
382
+ }
383
+ this.#goBack();
384
+ }
385
+
386
+ #renderStep(): void {
387
+ this.#contentContainer.clear();
388
+ this.#step = null;
389
+ this.#inputField = null; // Reset input field
390
+
391
+ switch (this.#currentStep) {
392
+ case "name":
393
+ this.#renderNameStep();
394
+ break;
395
+ case "transport":
396
+ this.#renderTransportStep();
397
+ break;
398
+ case "command":
399
+ this.#renderCommandStep();
400
+ break;
401
+ case "args":
402
+ this.#renderArgsStep();
403
+ break;
404
+ case "url":
405
+ this.#renderUrlStep();
406
+ break;
407
+ case "auth-method":
408
+ this.#renderAuthMethodStep();
409
+ break;
410
+ case "oauth-error":
411
+ this.#renderOAuthErrorStep();
412
+ break;
413
+ case "oauth-auth-url":
414
+ this.#renderOAuthAuthUrlStep();
415
+ break;
416
+ case "oauth-token-url":
417
+ this.#renderOAuthTokenUrlStep();
418
+ break;
419
+ case "oauth-client-id":
420
+ this.#renderOAuthClientIdStep();
421
+ break;
422
+ case "oauth-client-secret":
423
+ this.#renderOAuthClientSecretStep();
424
+ break;
425
+ case "oauth-scopes":
426
+ this.#renderOAuthScopesStep();
427
+ break;
428
+ case "apikey":
429
+ this.#renderApiKeyStep();
430
+ break;
431
+ case "auth-location":
432
+ this.#renderAuthLocationStep();
433
+ break;
434
+ case "env-var-name":
435
+ this.#renderEnvVarNameStep();
436
+ break;
437
+ case "header-name":
438
+ this.#renderHeaderNameStep();
439
+ break;
440
+ case "scope":
441
+ this.#renderScopeStep();
442
+ break;
443
+ case "confirm":
444
+ this.#renderConfirmStep();
445
+ break;
446
+ }
447
+ }
448
+
449
+ #renderNameStep(): void {
450
+ this.#inputStep({
451
+ heading: "Step 1: Server Name",
452
+ prompt: "Enter a unique name for this server:",
453
+ initial: this.#state.name,
454
+ hint: "[Only letters, numbers, dash, underscore, dot, colon]\n[Enter to continue, Esc to cancel]",
455
+ optional: false,
456
+ error: this.#validationError,
457
+ });
458
+ }
459
+
460
+ #renderTransportStep(): void {
461
+ this.#choiceStep({
462
+ heading: "Step 2: Transport Type",
463
+ intro: new Text("Select the transport type:", 0, 0),
464
+ choices: [
465
+ { label: "stdio (Local process)" },
466
+ { label: "http (HTTP server)" },
467
+ { label: "sse (Server-Sent Events)" },
468
+ ],
469
+ hint: "[↑↓ to navigate, Enter to select, Esc to cancel]",
470
+ });
471
+ }
472
+
473
+ #renderCommandStep(): void {
474
+ this.#inputStep({
475
+ heading: "Step 3: Command",
476
+ prompt: "Enter the command to run:",
477
+ initial: this.#state.command,
478
+ hint: "[Enter to continue, Esc to go back]",
479
+ optional: false,
480
+ });
481
+ }
482
+
483
+ #renderArgsStep(): void {
484
+ this.#inputStep({
485
+ heading: "Step 4: Arguments (Optional)",
486
+ prompt: "Enter command arguments (space-separated):",
487
+ initial: this.#state.args,
488
+ hint: "[Press Enter to skip or continue]",
489
+ optional: true,
490
+ });
491
+ }
492
+
493
+ #renderUrlStep(): void {
494
+ this.#inputStep({
495
+ heading: "Step 3: Server URL",
496
+ prompt: "Enter the server URL:",
497
+ initial: this.#state.url,
498
+ hint: "[Must start with http:// or https://]\n[Enter to continue, Esc to go back]",
499
+ optional: false,
500
+ error: this.#validationError,
501
+ });
502
+ }
503
+
504
+ #renderAuthLocationStep(): void {
505
+ this.#choiceStep({
506
+ heading: "Step: How to provide the key?",
507
+ choices: [{ label: "Environment variable" }, { label: "HTTP header" }],
508
+ hint: "[↑↓ to navigate, Enter to select, Esc to go back]",
509
+ });
510
+ }
511
+
512
+ #renderEnvVarNameStep(): void {
513
+ this.#inputStep({
514
+ heading: "Step: Environment Variable Name",
515
+ prompt: "Enter the environment variable name:",
516
+ initial: this.#state.envVarName,
517
+ hint: "[Enter to continue, Esc to go back]",
518
+ optional: false,
519
+ });
520
+ }
521
+
522
+ #renderHeaderNameStep(): void {
523
+ this.#inputStep({
524
+ heading: "Step: HTTP Header Name",
525
+ prompt: "Enter the HTTP header name:",
526
+ initial: this.#state.headerName,
527
+ hint: "[Enter to continue, Esc to go back]",
528
+ optional: false,
529
+ });
530
+ }
531
+
532
+ #renderScopeStep(): void {
533
+ const cwd = getProjectDir();
534
+ const userPathLabel = shortenPath(getMCPConfigPath("user", cwd));
535
+ const projectPathLabel = shortenPath(getMCPConfigPath("project", cwd));
536
+ this.#choiceStep({
537
+ heading: "Step: Configuration Scope",
538
+ choices: [{ label: `User level (${userPathLabel})` }, { label: `Project level (${projectPathLabel})` }],
539
+ hint: "[↑↓ to navigate, Enter to select, Esc to go back]",
540
+ });
541
+ }
542
+
543
+ #renderConfirmStep(): void {
544
+ const summary = new Container();
545
+ summary.addChild(new Text(`Name: ${theme.fg("accent", this.#state.name)}`, 0, 0));
546
+ summary.addChild(new Text(`Type: ${this.#state.transport}`, 0, 0));
547
+
548
+ if (this.#state.transport === "stdio") {
549
+ summary.addChild(new Text(`Command: ${this.#state.command}`, 0, 0));
550
+ if (this.#state.args) {
551
+ summary.addChild(new Text(`Args: ${this.#state.args}`, 0, 0));
552
+ }
553
+ } else {
554
+ summary.addChild(new Text(`URL: ${sanitize(this.#state.url)}`, 0, 0));
555
+ }
556
+
557
+ // Auth info
558
+ if (this.#state.authMethod === "none") {
559
+ summary.addChild(new Text("Auth: None", 0, 0));
560
+ } else if (this.#state.authMethod === "oauth") {
561
+ summary.addChild(new Text("Auth: OAuth (authenticated)", 0, 0));
562
+ } else if (this.#state.authMethod === "manual") {
563
+ if (this.#state.authLocation === "env") {
564
+ summary.addChild(new Text(`Auth: API key via env (${this.#state.envVarName})`, 0, 0));
565
+ } else {
566
+ summary.addChild(new Text(`Auth: API key via header (${this.#state.headerName})`, 0, 0));
567
+ }
568
+ }
569
+
570
+ const scopeLabel = this.#state.scope === "user" ? "User level" : "Project level";
571
+ summary.addChild(new Text(`Scope: ${scopeLabel}`, 0, 0));
572
+ summary.addChild(new Spacer(1));
573
+ summary.addChild(new Text("Save this configuration?", 0, 0));
574
+
575
+ this.#choiceStep({
576
+ heading: "Review Configuration",
577
+ intro: summary,
578
+ choices: [{ label: "Yes" }, { label: "No" }],
579
+ hint: "[↑↓ to navigate, Enter to select, Esc to go back]",
580
+ kind: "confirm",
581
+ });
582
+ }
583
+
584
+ handleInput(keyData: string): void {
585
+ // While an OAuth callback is being awaited, Esc/Ctrl+C aborts the flow
586
+ // rather than stepping back through the form: the wizard advertises
587
+ // "(Press Esc to cancel)" during the wait, and stepping back would
588
+ // leave the OAuth login orphaned.
589
+ if (this.#oauthAbort && (keyData === "\x03" || matchesAppInterrupt(keyData))) {
590
+ this.#oauthAbort.abort("MCP OAuth flow cancelled by user");
591
+ return;
592
+ }
593
+
594
+ // Handle Ctrl+C to cancel wizard immediately
595
+ if (keyData === "\x03") {
596
+ // Ctrl+C pressed - cancel wizard
597
+ this.#onCancelCallback();
598
+ return;
599
+ }
600
+
601
+ // Input steps own Enter/Escape through the field (submit/back or cancel).
602
+ if (this.#inputField) {
603
+ this.#step?.handleInput(keyData);
604
+ return;
605
+ }
606
+
607
+ // Async steps ignore selection/submit keys. Escape still follows the
608
+ // controller's existing back path when no in-flight OAuth abort owns it.
609
+ if (this.#stepKind === "async" && !matchesAppInterrupt(keyData)) return;
610
+
611
+ // Handle Escape (always handled by wizard)
612
+ if (matchesAppInterrupt(keyData)) {
613
+ if (this.#currentStep === "name") {
614
+ // Cancel wizard
615
+ this.#onCancelCallback();
616
+ return;
617
+ }
618
+ // Go back to previous step
619
+ this.#goBack();
620
+ return;
621
+ }
622
+
623
+ // Selector steps - handle Enter
624
+ if (matchesKey(keyData, "enter") || matchesKey(keyData, "return") || keyData === "\n") {
625
+ this.#selectCurrentOption();
626
+ return;
627
+ }
628
+
629
+ // Handle up/down arrows for selectors
630
+ if (matchesSelectUp(keyData)) {
631
+ this.#moveSelection(-1);
632
+ return;
633
+ }
634
+ if (matchesSelectDown(keyData)) {
635
+ this.#moveSelection(1);
636
+ return;
637
+ }
638
+ }
639
+
640
+ #saveInputAndProceed(): void {
641
+ if (!this.#inputField) return;
642
+
643
+ const value = this.#inputField.getValue().trim();
644
+
645
+ switch (this.#currentStep) {
646
+ case "name": {
647
+ // Validate server name
648
+ const nameError = this.#deps.validateServerName(value);
649
+ if (nameError) {
650
+ this.#validationError = nameError;
651
+ this.#renderStep();
652
+ return;
653
+ }
654
+ this.#validationError = null;
655
+ this.#state.name = value;
656
+ this.#currentStep = "transport";
657
+ this.#selectedIndex = 0;
658
+ break;
659
+ }
660
+ case "command":
661
+ if (!value) {
662
+ // Command is required
663
+ return;
664
+ }
665
+ this.#state.command = value;
666
+ this.#currentStep = "args";
667
+ break;
668
+ case "args":
669
+ this.#state.args = value; // Optional
670
+ void this.#testConnectionAndDetectAuth();
671
+ return;
672
+ case "url": {
673
+ // Validate URL
674
+ if (!value) {
675
+ this.#validationError = "URL is required";
676
+ this.#renderStep();
677
+ return;
678
+ }
679
+ let parsedUrl: URL;
680
+ try {
681
+ parsedUrl = new URL(value);
682
+ } catch {
683
+ this.#validationError = "Invalid URL format (must start with http:// or https://)";
684
+ this.#renderStep();
685
+ return;
686
+ }
687
+ if (parsedUrl.protocol !== "http:" && parsedUrl.protocol !== "https:") {
688
+ this.#validationError = "URL must use http:// or https:// scheme";
689
+ this.#renderStep();
690
+ return;
691
+ }
692
+ this.#validationError = null;
693
+ this.#state.url = value;
694
+ void this.#testConnectionAndDetectAuth();
695
+ return;
696
+ }
697
+ case "oauth-auth-url":
698
+ if (!value) return;
699
+ this.#state.oauthAuthUrl = value;
700
+ this.#currentStep = "oauth-token-url";
701
+ break;
702
+ case "oauth-token-url":
703
+ if (!value) return;
704
+ this.#state.oauthTokenUrl = value;
705
+ this.#currentStep = "oauth-client-id";
706
+ break;
707
+ case "oauth-client-id":
708
+ if (!value) return;
709
+ this.#state.oauthClientId = value;
710
+ this.#currentStep = "oauth-client-secret";
711
+ break;
712
+ case "oauth-client-secret":
713
+ this.#state.oauthClientSecret = value; // Optional
714
+ this.#currentStep = "oauth-scopes";
715
+ break;
716
+ case "oauth-scopes":
717
+ this.#state.oauthScopes = value; // Optional
718
+ // Launch OAuth flow
719
+ void this.#launchOAuthFlow();
720
+ return;
721
+ case "apikey":
722
+ if (!value) {
723
+ // API key is required
724
+ return;
725
+ }
726
+ this.#state.apiKey = value;
727
+ // Determine auth location based on transport
728
+ if (this.#state.transport === "stdio") {
729
+ this.#currentStep = "env-var-name";
730
+ } else {
731
+ this.#currentStep = "auth-location";
732
+ this.#selectedIndex = 0;
733
+ }
734
+ break;
735
+ case "env-var-name":
736
+ if (!value) {
737
+ return;
738
+ }
739
+ this.#state.envVarName = value;
740
+ this.#state.authLocation = "env";
741
+ this.#currentStep = "scope";
742
+ this.#selectedIndex = 0;
743
+ break;
744
+ case "header-name":
745
+ if (!value) {
746
+ return;
747
+ }
748
+ this.#state.headerName = value;
749
+ this.#state.authLocation = "header";
750
+ this.#currentStep = "scope";
751
+ this.#selectedIndex = 0;
752
+ break;
753
+ }
754
+
755
+ this.#inputField = null;
756
+ this.#renderStep();
757
+ }
758
+
759
+ #selectCurrentOption(): void {
760
+ switch (this.#currentStep) {
761
+ case "transport": {
762
+ const transports: TransportType[] = ["stdio", "http", "sse"];
763
+ this.#state.transport = transports[this.#selectedIndex];
764
+ this.#currentStep = this.#state.transport === "stdio" ? "command" : "url";
765
+ break;
766
+ }
767
+ case "auth-method": {
768
+ const authMethods: Array<"oauth" | "manual"> = ["oauth", "manual"];
769
+ this.#state.authMethod = authMethods[this.#selectedIndex];
770
+ if (this.#state.authMethod === "oauth") {
771
+ this.#currentStep = "oauth-auth-url";
772
+ } else {
773
+ // manual
774
+ this.#currentStep = "apikey";
775
+ }
776
+ break;
777
+ }
778
+ case "oauth-error":
779
+ if (this.#selectedIndex === 0) {
780
+ void this.#launchOAuthFlow();
781
+ } else {
782
+ this.#currentStep = "oauth-auth-url";
783
+ }
784
+ return;
785
+ case "auth-location": {
786
+ const authLocations: Array<"env" | "header"> = ["env", "header"];
787
+ this.#state.authLocation = authLocations[this.#selectedIndex];
788
+ if (this.#state.authLocation === "env") {
789
+ this.#currentStep = "env-var-name";
790
+ } else {
791
+ this.#currentStep = "header-name";
792
+ }
793
+ break;
794
+ }
795
+ case "scope": {
796
+ const scopes: Scope[] = ["user", "project"];
797
+ this.#state.scope = scopes[this.#selectedIndex];
798
+ this.#currentStep = "confirm";
799
+ this.#selectedIndex = 0;
800
+ break;
801
+ }
802
+ case "confirm": {
803
+ if (this.#selectedIndex === 0) {
804
+ this.#complete();
805
+ return;
806
+ }
807
+ this.#currentStep = "scope";
808
+ this.#selectedIndex = this.#state.scope === "user" ? 0 : 1;
809
+ break;
810
+ }
811
+ }
812
+
813
+ this.#renderStep();
814
+ }
815
+
816
+ #moveSelection(delta: number): void {
817
+ const maxIndex = this.#getMaxIndexForCurrentStep();
818
+ this.#selectedIndex = (this.#selectedIndex + delta + maxIndex + 1) % (maxIndex + 1);
819
+ this.#renderStep();
820
+ this.#requestRender();
821
+ }
822
+
823
+ #getMaxIndexForCurrentStep(): number {
824
+ switch (this.#currentStep) {
825
+ case "transport":
826
+ return 2; // 3 options
827
+ case "auth-method":
828
+ return 1; // 2 options
829
+ case "oauth-error":
830
+ return 1; // 2 options
831
+ case "auth-location":
832
+ return 1; // 2 options
833
+ case "scope":
834
+ return 1; // 2 options
835
+ case "confirm":
836
+ return 1; // 2 options
837
+ default:
838
+ return 0;
839
+ }
840
+ }
841
+
842
+ #goBack(): void {
843
+ // Navigate to previous step
844
+ switch (this.#currentStep) {
845
+ case "transport":
846
+ this.#currentStep = "name";
847
+ break;
848
+ case "command":
849
+ case "url":
850
+ this.#currentStep = "transport";
851
+ this.#selectedIndex = this.#state.transport === "stdio" ? 0 : this.#state.transport === "http" ? 1 : 2;
852
+ break;
853
+ case "args":
854
+ this.#currentStep = "command";
855
+ break;
856
+ case "auth-method":
857
+ // Go back to url or args depending on transport
858
+ if (this.#state.transport === "stdio") {
859
+ this.#currentStep = "args";
860
+ } else {
861
+ this.#currentStep = "url";
862
+ }
863
+ break;
864
+ case "oauth-auth-url":
865
+ case "apikey":
866
+ // Go back to transport-specific connection step
867
+ if (this.#state.transport === "stdio") {
868
+ this.#currentStep = "args";
869
+ } else {
870
+ this.#currentStep = "url";
871
+ }
872
+ break;
873
+ case "auth-location":
874
+ // Go back to API key input
875
+ this.#currentStep = "apikey";
876
+ break;
877
+ case "env-var-name":
878
+ case "header-name":
879
+ // Go back to auth location selection (for HTTP) or directly to apikey (for stdio)
880
+ if (this.#state.transport === "stdio") {
881
+ this.#currentStep = "apikey";
882
+ } else {
883
+ this.#currentStep = "auth-location";
884
+ this.#selectedIndex = this.#state.authLocation === "env" ? 0 : 1;
885
+ }
886
+ break;
887
+ case "oauth-token-url":
888
+ case "oauth-client-id":
889
+ case "oauth-client-secret":
890
+ case "oauth-scopes":
891
+ // Go back through OAuth flow
892
+ if (this.#currentStep === "oauth-token-url") {
893
+ this.#currentStep = "oauth-auth-url";
894
+ } else if (this.#currentStep === "oauth-client-id") {
895
+ this.#currentStep = "oauth-token-url";
896
+ } else if (this.#currentStep === "oauth-client-secret") {
897
+ this.#currentStep = "oauth-client-id";
898
+ } else if (this.#currentStep === "oauth-scopes") {
899
+ this.#currentStep = "oauth-client-secret";
900
+ }
901
+ break;
902
+ case "scope":
903
+ // Go back to last authentication step
904
+ if (this.#state.authMethod === "oauth") {
905
+ this.#currentStep = "oauth-scopes";
906
+ } else {
907
+ // manual - go back to env var name or header name
908
+ if (this.#state.authLocation === "env") {
909
+ this.#currentStep = "env-var-name";
910
+ } else {
911
+ this.#currentStep = "header-name";
912
+ }
913
+ }
914
+ break;
915
+ case "oauth-error":
916
+ this.#currentStep = "oauth-auth-url";
917
+ break;
918
+ case "confirm":
919
+ this.#currentStep = "scope";
920
+ this.#selectedIndex = this.#state.scope === "user" ? 0 : 1;
921
+ break;
922
+ }
923
+
924
+ this.#renderStep();
925
+ }
926
+
927
+ #renderAuthMethodStep(): void {
928
+ this.#choiceStep({
929
+ heading: "Step: Authentication Method",
930
+ choices: [
931
+ { label: "OAuth flow (web-based)", description: "(opens browser)" },
932
+ { label: "Manual API key/token", description: "(paste or use shell command)" },
933
+ ],
934
+ hint: "[↑↓ to navigate, Enter to select, Esc to go back]",
935
+ });
936
+ }
937
+
938
+ #renderOAuthAuthUrlStep(): void {
939
+ this.#inputStep({
940
+ heading: "OAuth: Authorization URL",
941
+ prompt: "Enter the OAuth authorization endpoint:",
942
+ initial: this.#state.oauthAuthUrl,
943
+ details: ["e.g., https://auth.example.com/oauth/authorize"],
944
+ hint: "[Enter to continue, Esc to go back]",
945
+ optional: false,
946
+ });
947
+ }
948
+
949
+ #renderOAuthTokenUrlStep(): void {
950
+ this.#inputStep({
951
+ heading: "OAuth: Token URL",
952
+ prompt: "Enter the OAuth token endpoint:",
953
+ initial: this.#state.oauthTokenUrl,
954
+ details: ["e.g., https://auth.example.com/oauth/token"],
955
+ hint: "[Enter to continue, Esc to go back]",
956
+ optional: false,
957
+ });
958
+ }
959
+
960
+ #renderOAuthClientIdStep(): void {
961
+ this.#inputStep({
962
+ heading: "OAuth: Client ID",
963
+ prompt: "Enter your OAuth client ID:",
964
+ initial: this.#state.oauthClientId,
965
+ hint: "[Enter to continue, Esc to go back]",
966
+ optional: false,
967
+ });
968
+ }
969
+
970
+ #renderOAuthClientSecretStep(): void {
971
+ this.#inputStep({
972
+ heading: "OAuth: Client Secret (Optional)",
973
+ prompt: "Enter your OAuth client secret:",
974
+ initial: this.#state.oauthClientSecret,
975
+ details: ["(Leave empty for PKCE-only flows)"],
976
+ hint: "[Enter to continue, Esc to go back]",
977
+ optional: true,
978
+ });
979
+ }
980
+
981
+ #renderOAuthScopesStep(): void {
982
+ this.#inputStep({
983
+ heading: "OAuth: Scopes (Optional)",
984
+ prompt: "Enter OAuth scopes (space-separated):",
985
+ initial: this.#state.oauthScopes,
986
+ details: ["e.g., read write"],
987
+ hint: "[Enter to continue, Esc to go back]",
988
+ optional: true,
989
+ });
990
+ }
991
+
992
+ #renderOAuthErrorStep(): void {
993
+ const intro = new Container();
994
+ const errorLines = this.#oauthErrorLines ?? [];
995
+ for (let index = 0; index < errorLines.length; index++) {
996
+ intro.addChild(new Text(errorLines[index] ?? "", 0, 0));
997
+ intro.addChild(new Spacer(1));
998
+ }
999
+ intro.addChild(new Text("Choose next action:", 0, 0));
1000
+ this.#choiceStep({
1001
+ heading: this.#oauthErrorHeading?.text ?? "OAuth authentication failed",
1002
+ headingTone: this.#oauthErrorHeading?.tone ?? "error",
1003
+ intro,
1004
+ choices: [{ label: "Retry OAuth authentication" }, { label: "Edit OAuth settings" }],
1005
+ hint: "[↑↓ to navigate, Enter to select, Esc to go back]",
1006
+ });
1007
+ }
1008
+
1009
+ #renderApiKeyStep(): void {
1010
+ this.#inputStep({
1011
+ heading: "API Key Required",
1012
+ prompt: "Enter your API key or token:",
1013
+ initial: this.#state.apiKey,
1014
+ details: ["(Supports !command for password manager)"],
1015
+ hint: "[Enter to continue, Esc to go back]",
1016
+ optional: false,
1017
+ });
1018
+ }
1019
+
1020
+ /**
1021
+ * Test connection and automatically detect if auth is needed.
1022
+ */
1023
+ async #testConnectionAndDetectAuth(): Promise<void> {
1024
+ const testConfig = this.#buildServerConfig();
1025
+
1026
+ if (!this.#onTestConnectionCallback) {
1027
+ // Skip test, go to scope
1028
+ this.#currentStep = "scope";
1029
+ this.#selectedIndex = 0;
1030
+ this.#renderStep();
1031
+ return;
1032
+ }
1033
+
1034
+ try {
1035
+ // Try to connect - timeout is handled by the transport layer (5 seconds)
1036
+ await this.#onTestConnectionCallback(testConfig);
1037
+
1038
+ // Success! No auth required
1039
+ const successBody = new Container();
1040
+ successBody.addChild(new Text("No authentication required", 0, 0));
1041
+ this.#asyncStep("✓ Connection successful!", "success", successBody);
1042
+
1043
+ setTimeout(() => {
1044
+ this.#state.authMethod = "none";
1045
+ this.#currentStep = "scope";
1046
+ this.#selectedIndex = 0;
1047
+ this.#renderStep();
1048
+ }, 1000);
1049
+ } catch (error) {
1050
+ // Connection failed - check if it's an auth error
1051
+ const authResult = this.#deps.analyzeAuthError(error as Error, this.#state.url);
1052
+
1053
+ if (authResult.requiresAuth) {
1054
+ // Prefer OAuth first: use error metadata, then well-known discovery fallback.
1055
+ let oauth = authResult.authType === "oauth" ? (authResult.oauth ?? null) : null;
1056
+ if (!oauth && this.#state.transport !== "stdio" && this.#state.url) {
1057
+ try {
1058
+ oauth = await this.#deps.discoverOAuthEndpoints(
1059
+ this.#state.url,
1060
+ authResult.authServerUrl,
1061
+ authResult.resourceMetadataUrl,
1062
+ { protectedScopes: authResult.scopes },
1063
+ );
1064
+ } catch {
1065
+ // Ignore discovery failures and fallback to manual auth.
1066
+ }
1067
+ }
1068
+ if (oauth && !oauth.scopes && authResult.resourceMetadataUrl) {
1069
+ // JSON-error-body path skips `discoverOAuthEndpoints` when the body
1070
+ // already carries endpoints, so scopes advertised only in the
1071
+ // protected-resource metadata document never reach the grant.
1072
+ const scopes = await this.#deps.fetchResourceMetadataScopes(authResult.resourceMetadataUrl);
1073
+ if (scopes) oauth = { ...oauth, scopes };
1074
+ }
1075
+
1076
+ if (oauth) {
1077
+ this.#state.oauthAuthUrl = oauth.authorizationUrl;
1078
+ this.#state.oauthTokenUrl = oauth.tokenUrl;
1079
+ this.#state.oauthRegistrationUrl = oauth.registrationUrl || "";
1080
+ this.#state.oauthIssuerUrl = oauth.issuerUrl || "";
1081
+ this.#state.oauthClientId = oauth.clientId || "";
1082
+ this.#state.oauthScopes = oauth.scopes || "";
1083
+ this.#state.oauthResource = oauth.resource || (this.#state.transport === "stdio" ? "" : this.#state.url);
1084
+ this.#state.oauthResourceIsFallback = !oauth.resource && this.#state.transport !== "stdio";
1085
+ this.#state.authMethod = "oauth";
1086
+
1087
+ const oauthBody = new Container();
1088
+ oauthBody.addChild(new Text("Launching browser for authorization...", 0, 0));
1089
+ this.#asyncStep("✓ OAuth detected", "success", oauthBody);
1090
+
1091
+ void this.#launchOAuthFlow();
1092
+ return;
1093
+ }
1094
+
1095
+ // OAuth metadata unavailable: fallback to manual API key.
1096
+ this.#currentStep = "apikey";
1097
+ this.#renderStep();
1098
+ } else {
1099
+ // Not an auth error - just a connection failure
1100
+ const errorMsg = sanitize(error instanceof Error ? error.message : String(error));
1101
+ const failureBody = new Container();
1102
+ failureBody.addChild(new Text(errorMsg, 0, 0));
1103
+ failureBody.addChild(new Spacer(1));
1104
+ failureBody.addChild(new Text(theme.fg("muted", "Adding server anyway..."), 0, 0));
1105
+ this.#asyncStep("✗ Connection failed", "error", failureBody);
1106
+
1107
+ setTimeout(() => {
1108
+ this.#state.authMethod = "none";
1109
+ this.#currentStep = "scope";
1110
+ this.#selectedIndex = 0;
1111
+ this.#renderStep();
1112
+ }, 2000);
1113
+ }
1114
+ }
1115
+ }
1116
+
1117
+ /**
1118
+ * Build a server config from current wizard state for connection testing (no auth).
1119
+ */
1120
+ #buildServerConfig(): MCPAddWizardConfig {
1121
+ return this.#buildServerConfigWithAuth(false);
1122
+ }
1123
+
1124
+ #buildServerConfigWithAuth(includeAuth: boolean): MCPAddWizardConfig {
1125
+ const transport = this.#state.transport ?? "stdio";
1126
+
1127
+ if (transport === "stdio") {
1128
+ const config: MCPAddWizardStdioConfig = {
1129
+ type: "stdio",
1130
+ command: this.#state.command,
1131
+ timeout: 5000,
1132
+ };
1133
+
1134
+ if (this.#state.args) {
1135
+ config.args = this.#state.args.split(/\s+/).filter(Boolean);
1136
+ }
1137
+
1138
+ if (includeAuth && this.#state.authMethod === "oauth" && this.#state.oauthCredentialId) {
1139
+ config.auth = {
1140
+ type: "oauth",
1141
+ credentialId: this.#state.oauthCredentialId,
1142
+ tokenUrl: this.#state.oauthTokenUrl || undefined,
1143
+ resource: this.#state.oauthResource || undefined,
1144
+ clientId: this.#state.oauthClientId || undefined,
1145
+ clientSecret: this.#state.oauthClientSecret || undefined,
1146
+ };
1147
+ }
1148
+
1149
+ if (includeAuth && this.#state.authMethod === "manual" && this.#state.apiKey) {
1150
+ config.env = {
1151
+ ...config.env,
1152
+ [this.#state.envVarName || "API_KEY"]: this.#state.apiKey,
1153
+ };
1154
+ }
1155
+
1156
+ return config;
1157
+ }
1158
+
1159
+ // http or sse
1160
+ const config: MCPAddWizardRemoteConfig = {
1161
+ type: transport,
1162
+ url: this.#state.url,
1163
+ timeout: 5000,
1164
+ };
1165
+
1166
+ if (includeAuth && this.#state.authMethod === "oauth" && this.#state.oauthCredentialId) {
1167
+ config.auth = {
1168
+ type: "oauth",
1169
+ credentialId: this.#state.oauthCredentialId,
1170
+ tokenUrl: this.#state.oauthTokenUrl || undefined,
1171
+ resource: this.#state.oauthResource || undefined,
1172
+ clientId: this.#state.oauthClientId || undefined,
1173
+ clientSecret: this.#state.oauthClientSecret || undefined,
1174
+ };
1175
+ }
1176
+
1177
+ if (includeAuth && this.#state.authMethod === "manual" && this.#state.apiKey) {
1178
+ if (this.#state.authLocation === "env") {
1179
+ // For HTTP with env location, store in headers using the env var name as-is
1180
+ config.headers = {
1181
+ ...config.headers,
1182
+ [this.#state.headerName || "Authorization"]: this.#state.apiKey,
1183
+ };
1184
+ } else {
1185
+ const headerName = this.#state.headerName || "Authorization";
1186
+ config.headers = {
1187
+ ...config.headers,
1188
+ [headerName]: this.#state.apiKey,
1189
+ };
1190
+ }
1191
+ }
1192
+
1193
+ return config;
1194
+ }
1195
+
1196
+ async #launchOAuthFlow(): Promise<void> {
1197
+ if (!this.#onOAuthCallback) {
1198
+ const unavailableBody = new Container();
1199
+ unavailableBody.addChild(new Text("OAuth login cannot start without a host OAuth handler.", 0, 0));
1200
+ this.#asyncStep("OAuth flow not available", "error", unavailableBody);
1201
+ this.#requestRender();
1202
+ return;
1203
+ }
1204
+
1205
+ // Validate OAuth configuration
1206
+ if (!this.#state.oauthAuthUrl || !this.#state.oauthTokenUrl) {
1207
+ const incompleteBody = new Container();
1208
+ incompleteBody.addChild(new Text("Authorization and Token URLs are required.", 0, 0));
1209
+ this.#asyncStep(
1210
+ "OAuth configuration incomplete",
1211
+ "error",
1212
+ incompleteBody,
1213
+ new Text(theme.fg("muted", "[Press Esc to go back]"), 0, 0),
1214
+ );
1215
+ this.#requestRender();
1216
+ return;
1217
+ }
1218
+
1219
+ // Show "Authenticating..." message
1220
+ const authBody = new Container();
1221
+ authBody.addChild(new Text("Launching OAuth flow...", 0, 0));
1222
+ authBody.addChild(new Text(theme.fg("muted", "Browser will open automatically."), 0, 0));
1223
+ authBody.addChild(new Spacer(1));
1224
+ authBody.addChild(new Text(theme.fg("warning", "If browser doesn't open, copy the URL from chat."), 0, 0));
1225
+ this.#asyncStep(
1226
+ "OAuth Authentication",
1227
+ "accent",
1228
+ authBody,
1229
+ new Text(theme.fg("muted", "(Press Esc to cancel)"), 0, 0),
1230
+ );
1231
+ this.#requestRender();
1232
+
1233
+ this.#oauthAbort = new AbortController();
1234
+ try {
1235
+ // Call OAuth handler
1236
+ const oauthResourceIsFallback =
1237
+ this.#state.oauthResourceIsFallback || (!this.#state.oauthResource && this.#state.transport !== "stdio");
1238
+ this.#state.oauthResourceIsFallback = oauthResourceIsFallback;
1239
+ const oauthResource = this.#state.oauthResource || (this.#state.transport === "stdio" ? "" : this.#state.url);
1240
+ const oauthResult = await this.#onOAuthCallback(
1241
+ this.#state.oauthAuthUrl,
1242
+ this.#state.oauthTokenUrl,
1243
+ this.#state.oauthClientId,
1244
+ this.#state.oauthClientSecret,
1245
+ this.#state.oauthScopes,
1246
+ {
1247
+ serverUrl: this.#state.url || undefined,
1248
+ registrationUrl: this.#state.oauthRegistrationUrl || undefined,
1249
+ issuerUrl: this.#state.oauthIssuerUrl || undefined,
1250
+ resource: oauthResource || undefined,
1251
+ stripSameOriginResource: oauthResourceIsFallback,
1252
+ abortSignal: this.#oauthAbort.signal,
1253
+ },
1254
+ );
1255
+
1256
+ // Store credential ID + any dynamically-registered client id. DCR client
1257
+ // secrets stay embedded in the stored credential, never in mcp.json.
1258
+ this.#state.oauthCredentialId = oauthResult.credentialId;
1259
+ if (oauthResult.clientId) this.#state.oauthClientId = oauthResult.clientId;
1260
+ this.#state.oauthResource = oauthResult.resource ?? oauthResource;
1261
+
1262
+ // Show success message
1263
+ const healthBody = new Container();
1264
+ healthBody.addChild(new Text(theme.fg("muted", "Running connection health check..."), 0, 0));
1265
+ const spinnerFrames = theme.spinnerFrames;
1266
+ const initialFrame = spinnerFrames[0] ?? "|";
1267
+ const healthText = new Text(theme.fg("muted", `${initialFrame} Checking server connection...`), 0, 0);
1268
+ healthBody.addChild(healthText);
1269
+ this.#asyncStep("✓ Authentication successful!", "success", healthBody);
1270
+
1271
+ let spinnerIndex = 0;
1272
+ const spinner = setInterval(() => {
1273
+ healthText.setText(
1274
+ theme.fg("muted", `${spinnerFrames[spinnerIndex % spinnerFrames.length]} Checking server connection...`),
1275
+ );
1276
+ spinnerIndex++;
1277
+ this.#requestRender();
1278
+ }, 80);
1279
+
1280
+ let healthPassed = true;
1281
+ let healthError = "";
1282
+ if (this.#onTestConnectionCallback) {
1283
+ try {
1284
+ const { promise: timeoutPromise, reject: timeoutReject } = Promise.withResolvers<never>();
1285
+ const timer = setTimeout(
1286
+ () => timeoutReject(new Error("Health check timed out after 10 seconds")),
1287
+ 10_000,
1288
+ );
1289
+ try {
1290
+ await Promise.race([
1291
+ this.#onTestConnectionCallback(this.#buildServerConfigWithAuth(true)),
1292
+ timeoutPromise,
1293
+ ]);
1294
+ } finally {
1295
+ clearTimeout(timer);
1296
+ }
1297
+ } catch (error) {
1298
+ healthPassed = false;
1299
+ healthError = sanitize(error instanceof Error ? error.message : String(error));
1300
+ }
1301
+ }
1302
+
1303
+ clearInterval(spinner);
1304
+ if (healthPassed) {
1305
+ healthText.setText(theme.fg("success", "✓ Health check passed"));
1306
+ } else {
1307
+ healthText.setText(theme.fg("warning", "⚠ Health check failed (will still save config)"));
1308
+ healthBody.addChild(new Spacer(1));
1309
+ healthBody.addChild(new Text(theme.fg("muted", healthError), 0, 0));
1310
+ }
1311
+ this.#requestRender();
1312
+
1313
+ // Move to scope selection after short delay
1314
+ setTimeout(
1315
+ () => {
1316
+ this.#currentStep = "scope";
1317
+ this.#selectedIndex = 0;
1318
+ this.#renderStep();
1319
+ this.#requestRender();
1320
+ },
1321
+ healthPassed ? 1000 : 2000,
1322
+ );
1323
+ } catch (error) {
1324
+ // User cancellation has its own neutral heading + tip; everything else
1325
+ // keeps the "OAuth authentication failed" framing so the existing tips
1326
+ // stay meaningful. Name-matching avoids importing controller types.
1327
+ const cancelled = error instanceof Error && error.name === "MCPOAuthCancelledError";
1328
+ const errorMsg = sanitize(error instanceof Error ? error.message : String(error));
1329
+ const tipLines: string[] = [errorMsg];
1330
+ if (cancelled) {
1331
+ tipLines.push(theme.fg("muted", "Tip: Choose Retry to launch the browser again."));
1332
+ } else if (errorMsg.includes("timeout") || errorMsg.includes("timed out")) {
1333
+ tipLines.push(theme.fg("muted", "Tip: Complete authorization faster next time"));
1334
+ } else if (errorMsg.includes("Invalid OAuth URLs")) {
1335
+ tipLines.push(theme.fg("muted", "Tip: Check that the OAuth URLs are correct"));
1336
+ } else if (errorMsg.includes("ECONNREFUSED")) {
1337
+ tipLines.push(theme.fg("muted", "Tip: Verify the OAuth server is accessible"));
1338
+ }
1339
+
1340
+ // Set up as a selector step
1341
+ this.#selectedIndex = 0;
1342
+ this.#currentStep = "oauth-error";
1343
+ this.#oauthErrorHeading = cancelled
1344
+ ? { text: "○ OAuth cancelled", tone: "muted" }
1345
+ : { text: "✗ OAuth authentication failed", tone: "error" };
1346
+ this.#oauthErrorLines = tipLines;
1347
+ this.#renderStep();
1348
+ this.#requestRender();
1349
+ } finally {
1350
+ this.#oauthAbort = null;
1351
+ }
1352
+ }
1353
+
1354
+ #complete(): void {
1355
+ if (!this.#state.scope) return;
1356
+
1357
+ // Build the config
1358
+ const config: MCPAddWizardConfig = this.#buildConfig();
1359
+
1360
+ // Call completion callback
1361
+ this.#onCompleteCallback(this.#state.name, config, this.#state.scope);
1362
+ }
1363
+
1364
+ #buildConfig(): MCPAddWizardConfig {
1365
+ if (this.#state.transport === "stdio") {
1366
+ const config: MCPAddWizardStdioConfig = {
1367
+ type: "stdio",
1368
+ command: this.#state.command,
1369
+ };
1370
+
1371
+ if (this.#state.args) {
1372
+ config.args = this.#state.args.split(/\s+/).filter(Boolean);
1373
+ }
1374
+
1375
+ // Add OAuth auth if configured
1376
+ if (this.#state.authMethod === "oauth" && this.#state.oauthCredentialId) {
1377
+ config.auth = {
1378
+ type: "oauth",
1379
+ credentialId: this.#state.oauthCredentialId,
1380
+ tokenUrl: this.#state.oauthTokenUrl || undefined,
1381
+ resource: this.#state.oauthResource || undefined,
1382
+ clientId: this.#state.oauthClientId || undefined,
1383
+ clientSecret: this.#state.oauthClientSecret || undefined,
1384
+ };
1385
+ }
1386
+
1387
+ // Add API key to env if manual auth — use user-chosen env var name
1388
+ if (this.#state.authMethod === "manual" && this.#state.apiKey) {
1389
+ const envKey = this.#state.envVarName || "API_KEY";
1390
+ config.env = {
1391
+ [envKey]: this.#state.apiKey,
1392
+ };
1393
+ }
1394
+
1395
+ return config;
1396
+ }
1397
+
1398
+ // HTTP or SSE — use concrete type
1399
+ const config: MCPAddWizardRemoteConfig = {
1400
+ type: this.#state.transport!,
1401
+ url: this.#state.url,
1402
+ };
1403
+
1404
+ // Add OAuth auth if configured
1405
+ if (this.#state.authMethod === "oauth" && this.#state.oauthCredentialId) {
1406
+ config.auth = {
1407
+ type: "oauth",
1408
+ credentialId: this.#state.oauthCredentialId,
1409
+ tokenUrl: this.#state.oauthTokenUrl || undefined,
1410
+ resource: this.#state.oauthResource || undefined,
1411
+ clientId: this.#state.oauthClientId || undefined,
1412
+ clientSecret: this.#state.oauthClientSecret || undefined,
1413
+ };
1414
+ }
1415
+
1416
+ // Add API key using user-chosen header name and auth location
1417
+ if (this.#state.authMethod === "manual" && this.#state.apiKey) {
1418
+ if (this.#state.authLocation === "env") {
1419
+ // Env-based auth for HTTP: store the key in env on the config
1420
+ // HTTP/SSE configs don't have an env field, so use headers as carrier
1421
+ const headerName = this.#state.headerName || "Authorization";
1422
+ config.headers = {
1423
+ [headerName]: this.#state.apiKey,
1424
+ };
1425
+ } else {
1426
+ // Header-based auth: use the user's chosen header name
1427
+ const headerName = this.#state.headerName || "Authorization";
1428
+ config.headers = {
1429
+ [headerName]: this.#state.apiKey,
1430
+ };
1431
+ }
1432
+ }
1433
+
1434
+ return config;
1435
+ }
1436
+ }