@nghyane/arcane 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (738) hide show
  1. package/CHANGELOG.md +3 -0
  2. package/README.md +12 -0
  3. package/examples/README.md +21 -0
  4. package/examples/custom-tools/README.md +109 -0
  5. package/examples/custom-tools/hello/index.ts +20 -0
  6. package/examples/custom-tools/todo/index.ts +206 -0
  7. package/examples/extensions/README.md +143 -0
  8. package/examples/extensions/api-demo.ts +89 -0
  9. package/examples/extensions/chalk-logger.ts +25 -0
  10. package/examples/extensions/hello.ts +32 -0
  11. package/examples/extensions/pirate.ts +43 -0
  12. package/examples/extensions/plan-mode.ts +550 -0
  13. package/examples/extensions/reload-runtime.ts +37 -0
  14. package/examples/extensions/todo.ts +296 -0
  15. package/examples/extensions/tools.ts +144 -0
  16. package/examples/extensions/with-deps/index.ts +35 -0
  17. package/examples/extensions/with-deps/package-lock.json +31 -0
  18. package/examples/extensions/with-deps/package.json +16 -0
  19. package/examples/hooks/README.md +56 -0
  20. package/examples/hooks/auto-commit-on-exit.ts +48 -0
  21. package/examples/hooks/confirm-destructive.ts +58 -0
  22. package/examples/hooks/custom-compaction.ts +116 -0
  23. package/examples/hooks/dirty-repo-guard.ts +51 -0
  24. package/examples/hooks/file-trigger.ts +40 -0
  25. package/examples/hooks/git-checkpoint.ts +52 -0
  26. package/examples/hooks/handoff.ts +150 -0
  27. package/examples/hooks/permission-gate.ts +33 -0
  28. package/examples/hooks/protected-paths.ts +29 -0
  29. package/examples/hooks/qna.ts +119 -0
  30. package/examples/hooks/status-line.ts +39 -0
  31. package/examples/sdk/01-minimal.ts +21 -0
  32. package/examples/sdk/02-custom-model.ts +49 -0
  33. package/examples/sdk/03-custom-prompt.ts +43 -0
  34. package/examples/sdk/04-skills.ts +43 -0
  35. package/examples/sdk/06-extensions.ts +80 -0
  36. package/examples/sdk/06-hooks.ts +61 -0
  37. package/examples/sdk/07-context-files.ts +35 -0
  38. package/examples/sdk/08-prompt-templates.ts +36 -0
  39. package/examples/sdk/08-slash-commands.ts +41 -0
  40. package/examples/sdk/09-api-keys-and-oauth.ts +54 -0
  41. package/examples/sdk/11-sessions.ts +47 -0
  42. package/examples/sdk/README.md +150 -0
  43. package/package.json +464 -0
  44. package/scripts/format-prompts.ts +184 -0
  45. package/scripts/generate-docs-index.ts +40 -0
  46. package/scripts/generate-template.ts +32 -0
  47. package/src/bun-imports.d.ts +22 -0
  48. package/src/capability/context-file.ts +39 -0
  49. package/src/capability/extension-module.ts +33 -0
  50. package/src/capability/extension.ts +47 -0
  51. package/src/capability/fs.ts +89 -0
  52. package/src/capability/hook.ts +39 -0
  53. package/src/capability/index.ts +432 -0
  54. package/src/capability/instruction.ts +36 -0
  55. package/src/capability/mcp.ts +60 -0
  56. package/src/capability/prompt.ts +34 -0
  57. package/src/capability/rule.ts +223 -0
  58. package/src/capability/settings.ts +34 -0
  59. package/src/capability/skill.ts +48 -0
  60. package/src/capability/slash-command.ts +39 -0
  61. package/src/capability/ssh.ts +41 -0
  62. package/src/capability/system-prompt.ts +34 -0
  63. package/src/capability/tool.ts +37 -0
  64. package/src/capability/types.ts +156 -0
  65. package/src/cli/args.ts +259 -0
  66. package/src/cli/config-cli.ts +357 -0
  67. package/src/cli/file-processor.ts +124 -0
  68. package/src/cli/grep-cli.ts +152 -0
  69. package/src/cli/jupyter-cli.ts +106 -0
  70. package/src/cli/list-models.ts +103 -0
  71. package/src/cli/plugin-cli.ts +661 -0
  72. package/src/cli/session-picker.ts +42 -0
  73. package/src/cli/setup-cli.ts +376 -0
  74. package/src/cli/shell-cli.ts +174 -0
  75. package/src/cli/ssh-cli.ts +179 -0
  76. package/src/cli/stats-cli.ts +197 -0
  77. package/src/cli/update-cli.ts +286 -0
  78. package/src/cli/web-search-cli.ts +143 -0
  79. package/src/cli.ts +65 -0
  80. package/src/commands/commit.ts +36 -0
  81. package/src/commands/config.ts +51 -0
  82. package/src/commands/grep.ts +41 -0
  83. package/src/commands/jupyter.ts +32 -0
  84. package/src/commands/launch.ts +139 -0
  85. package/src/commands/plugin.ts +70 -0
  86. package/src/commands/setup.ts +42 -0
  87. package/src/commands/shell.ts +29 -0
  88. package/src/commands/ssh.ts +60 -0
  89. package/src/commands/stats.ts +29 -0
  90. package/src/commands/update.ts +21 -0
  91. package/src/commands/web-search.ts +42 -0
  92. package/src/commit/agentic/agent.ts +311 -0
  93. package/src/commit/agentic/fallback.ts +96 -0
  94. package/src/commit/agentic/index.ts +359 -0
  95. package/src/commit/agentic/prompts/analyze-file.md +22 -0
  96. package/src/commit/agentic/prompts/session-user.md +25 -0
  97. package/src/commit/agentic/prompts/split-confirm.md +1 -0
  98. package/src/commit/agentic/prompts/system.md +38 -0
  99. package/src/commit/agentic/state.ts +69 -0
  100. package/src/commit/agentic/tools/analyze-file.ts +118 -0
  101. package/src/commit/agentic/tools/git-file-diff.ts +194 -0
  102. package/src/commit/agentic/tools/git-hunk.ts +50 -0
  103. package/src/commit/agentic/tools/git-overview.ts +84 -0
  104. package/src/commit/agentic/tools/index.ts +56 -0
  105. package/src/commit/agentic/tools/propose-changelog.ts +128 -0
  106. package/src/commit/agentic/tools/propose-commit.ts +154 -0
  107. package/src/commit/agentic/tools/recent-commits.ts +81 -0
  108. package/src/commit/agentic/tools/split-commit.ts +280 -0
  109. package/src/commit/agentic/topo-sort.ts +44 -0
  110. package/src/commit/agentic/trivial.ts +51 -0
  111. package/src/commit/agentic/validation.ts +200 -0
  112. package/src/commit/analysis/conventional.ts +165 -0
  113. package/src/commit/analysis/index.ts +4 -0
  114. package/src/commit/analysis/scope.ts +242 -0
  115. package/src/commit/analysis/summary.ts +112 -0
  116. package/src/commit/analysis/validation.ts +66 -0
  117. package/src/commit/changelog/detect.ts +37 -0
  118. package/src/commit/changelog/generate.ts +110 -0
  119. package/src/commit/changelog/index.ts +234 -0
  120. package/src/commit/changelog/parse.ts +44 -0
  121. package/src/commit/cli.ts +93 -0
  122. package/src/commit/git/diff.ts +148 -0
  123. package/src/commit/git/errors.ts +9 -0
  124. package/src/commit/git/index.ts +211 -0
  125. package/src/commit/git/operations.ts +54 -0
  126. package/src/commit/index.ts +5 -0
  127. package/src/commit/map-reduce/index.ts +64 -0
  128. package/src/commit/map-reduce/map-phase.ts +178 -0
  129. package/src/commit/map-reduce/reduce-phase.ts +145 -0
  130. package/src/commit/map-reduce/utils.ts +9 -0
  131. package/src/commit/message.ts +11 -0
  132. package/src/commit/model-selection.ts +69 -0
  133. package/src/commit/pipeline.ts +243 -0
  134. package/src/commit/prompts/analysis-system.md +148 -0
  135. package/src/commit/prompts/analysis-user.md +38 -0
  136. package/src/commit/prompts/changelog-system.md +50 -0
  137. package/src/commit/prompts/changelog-user.md +18 -0
  138. package/src/commit/prompts/file-observer-system.md +24 -0
  139. package/src/commit/prompts/file-observer-user.md +8 -0
  140. package/src/commit/prompts/reduce-system.md +50 -0
  141. package/src/commit/prompts/reduce-user.md +17 -0
  142. package/src/commit/prompts/summary-retry.md +3 -0
  143. package/src/commit/prompts/summary-system.md +38 -0
  144. package/src/commit/prompts/summary-user.md +13 -0
  145. package/src/commit/prompts/types-description.md +2 -0
  146. package/src/commit/types.ts +109 -0
  147. package/src/commit/utils/exclusions.ts +42 -0
  148. package/src/config/file-lock.ts +121 -0
  149. package/src/config/keybindings.ts +280 -0
  150. package/src/config/model-registry.ts +1140 -0
  151. package/src/config/model-resolver.ts +812 -0
  152. package/src/config/prompt-templates.ts +526 -0
  153. package/src/config/resolve-config-value.ts +92 -0
  154. package/src/config/settings-schema.ts +1236 -0
  155. package/src/config/settings.ts +706 -0
  156. package/src/config.ts +414 -0
  157. package/src/cursor.ts +239 -0
  158. package/src/debug/index.ts +431 -0
  159. package/src/debug/log-formatting.ts +60 -0
  160. package/src/debug/log-viewer.ts +903 -0
  161. package/src/debug/profiler.ts +158 -0
  162. package/src/debug/report-bundle.ts +366 -0
  163. package/src/debug/system-info.ts +112 -0
  164. package/src/discovery/agents-md.ts +68 -0
  165. package/src/discovery/agents.ts +199 -0
  166. package/src/discovery/builtin.ts +815 -0
  167. package/src/discovery/claude-plugins.ts +205 -0
  168. package/src/discovery/claude.ts +506 -0
  169. package/src/discovery/cline.ts +83 -0
  170. package/src/discovery/codex.ts +532 -0
  171. package/src/discovery/cursor.ts +218 -0
  172. package/src/discovery/gemini.ts +395 -0
  173. package/src/discovery/github.ts +117 -0
  174. package/src/discovery/helpers.ts +698 -0
  175. package/src/discovery/index.ts +89 -0
  176. package/src/discovery/mcp-json.ts +156 -0
  177. package/src/discovery/opencode.ts +394 -0
  178. package/src/discovery/ssh.ts +160 -0
  179. package/src/discovery/vscode.ts +103 -0
  180. package/src/discovery/windsurf.ts +145 -0
  181. package/src/exa/company.ts +57 -0
  182. package/src/exa/index.ts +62 -0
  183. package/src/exa/linkedin.ts +57 -0
  184. package/src/exa/mcp-client.ts +289 -0
  185. package/src/exa/render.ts +244 -0
  186. package/src/exa/researcher.ts +89 -0
  187. package/src/exa/search.ts +330 -0
  188. package/src/exa/types.ts +166 -0
  189. package/src/exa/websets.ts +247 -0
  190. package/src/exec/bash-executor.ts +184 -0
  191. package/src/exec/exec.ts +53 -0
  192. package/src/export/custom-share.ts +65 -0
  193. package/src/export/html/index.ts +162 -0
  194. package/src/export/html/template.css +889 -0
  195. package/src/export/html/template.generated.ts +2 -0
  196. package/src/export/html/template.html +45 -0
  197. package/src/export/html/template.js +1329 -0
  198. package/src/export/html/template.macro.ts +24 -0
  199. package/src/export/html/vendor/highlight.min.js +1213 -0
  200. package/src/export/html/vendor/marked.min.js +6 -0
  201. package/src/export/ttsr.ts +434 -0
  202. package/src/extensibility/custom-commands/bundled/review/index.ts +433 -0
  203. package/src/extensibility/custom-commands/index.ts +15 -0
  204. package/src/extensibility/custom-commands/loader.ts +231 -0
  205. package/src/extensibility/custom-commands/types.ts +111 -0
  206. package/src/extensibility/custom-tools/index.ts +22 -0
  207. package/src/extensibility/custom-tools/loader.ts +235 -0
  208. package/src/extensibility/custom-tools/types.ts +226 -0
  209. package/src/extensibility/custom-tools/wrapper.ts +45 -0
  210. package/src/extensibility/extensions/index.ts +136 -0
  211. package/src/extensibility/extensions/loader.ts +520 -0
  212. package/src/extensibility/extensions/runner.ts +774 -0
  213. package/src/extensibility/extensions/types.ts +1293 -0
  214. package/src/extensibility/extensions/wrapper.ts +188 -0
  215. package/src/extensibility/hooks/index.ts +16 -0
  216. package/src/extensibility/hooks/loader.ts +273 -0
  217. package/src/extensibility/hooks/runner.ts +441 -0
  218. package/src/extensibility/hooks/tool-wrapper.ts +106 -0
  219. package/src/extensibility/hooks/types.ts +817 -0
  220. package/src/extensibility/plugins/doctor.ts +65 -0
  221. package/src/extensibility/plugins/git-url.ts +281 -0
  222. package/src/extensibility/plugins/index.ts +33 -0
  223. package/src/extensibility/plugins/installer.ts +192 -0
  224. package/src/extensibility/plugins/loader.ts +338 -0
  225. package/src/extensibility/plugins/manager.ts +716 -0
  226. package/src/extensibility/plugins/parser.ts +105 -0
  227. package/src/extensibility/plugins/types.ts +190 -0
  228. package/src/extensibility/skills.ts +385 -0
  229. package/src/extensibility/slash-commands.ts +287 -0
  230. package/src/extensibility/tool-proxy.ts +25 -0
  231. package/src/index.ts +275 -0
  232. package/src/internal-urls/agent-protocol.ts +136 -0
  233. package/src/internal-urls/artifact-protocol.ts +97 -0
  234. package/src/internal-urls/docs-index.generated.ts +54 -0
  235. package/src/internal-urls/docs-protocol.ts +84 -0
  236. package/src/internal-urls/index.ts +31 -0
  237. package/src/internal-urls/json-query.ts +126 -0
  238. package/src/internal-urls/memory-protocol.ts +133 -0
  239. package/src/internal-urls/router.ts +70 -0
  240. package/src/internal-urls/rule-protocol.ts +55 -0
  241. package/src/internal-urls/skill-protocol.ts +111 -0
  242. package/src/internal-urls/types.ts +52 -0
  243. package/src/ipy/executor.ts +556 -0
  244. package/src/ipy/gateway-coordinator.ts +426 -0
  245. package/src/ipy/kernel.ts +892 -0
  246. package/src/ipy/modules.ts +109 -0
  247. package/src/ipy/prelude.py +831 -0
  248. package/src/ipy/prelude.ts +3 -0
  249. package/src/ipy/runtime.ts +222 -0
  250. package/src/lsp/client.ts +867 -0
  251. package/src/lsp/clients/biome-client.ts +202 -0
  252. package/src/lsp/clients/index.ts +50 -0
  253. package/src/lsp/clients/lsp-linter-client.ts +93 -0
  254. package/src/lsp/clients/swiftlint-client.ts +120 -0
  255. package/src/lsp/config.ts +397 -0
  256. package/src/lsp/defaults.json +464 -0
  257. package/src/lsp/edits.ts +109 -0
  258. package/src/lsp/index.ts +1268 -0
  259. package/src/lsp/lspmux.ts +250 -0
  260. package/src/lsp/render.ts +689 -0
  261. package/src/lsp/types.ts +414 -0
  262. package/src/lsp/utils.ts +549 -0
  263. package/src/main.ts +773 -0
  264. package/src/mcp/client.ts +239 -0
  265. package/src/mcp/config-writer.ts +215 -0
  266. package/src/mcp/config.ts +363 -0
  267. package/src/mcp/index.ts +55 -0
  268. package/src/mcp/json-rpc.ts +84 -0
  269. package/src/mcp/loader.ts +124 -0
  270. package/src/mcp/manager.ts +490 -0
  271. package/src/mcp/oauth-discovery.ts +274 -0
  272. package/src/mcp/oauth-flow.ts +229 -0
  273. package/src/mcp/render.ts +123 -0
  274. package/src/mcp/tool-bridge.ts +372 -0
  275. package/src/mcp/tool-cache.ts +121 -0
  276. package/src/mcp/transports/http.ts +332 -0
  277. package/src/mcp/transports/index.ts +6 -0
  278. package/src/mcp/transports/stdio.ts +281 -0
  279. package/src/mcp/types.ts +248 -0
  280. package/src/memories/index.ts +1099 -0
  281. package/src/memories/storage.ts +563 -0
  282. package/src/modes/components/agent-dashboard.ts +1130 -0
  283. package/src/modes/components/assistant-message.ts +144 -0
  284. package/src/modes/components/bash-execution.ts +218 -0
  285. package/src/modes/components/bordered-loader.ts +41 -0
  286. package/src/modes/components/branch-summary-message.ts +45 -0
  287. package/src/modes/components/codemode-group.ts +369 -0
  288. package/src/modes/components/compaction-summary-message.ts +51 -0
  289. package/src/modes/components/countdown-timer.ts +46 -0
  290. package/src/modes/components/custom-editor.ts +181 -0
  291. package/src/modes/components/custom-message.ts +91 -0
  292. package/src/modes/components/diff.ts +186 -0
  293. package/src/modes/components/dynamic-border.ts +25 -0
  294. package/src/modes/components/extensions/extension-dashboard.ts +325 -0
  295. package/src/modes/components/extensions/extension-list.ts +484 -0
  296. package/src/modes/components/extensions/index.ts +9 -0
  297. package/src/modes/components/extensions/inspector-panel.ts +321 -0
  298. package/src/modes/components/extensions/state-manager.ts +586 -0
  299. package/src/modes/components/extensions/types.ts +191 -0
  300. package/src/modes/components/footer.ts +315 -0
  301. package/src/modes/components/history-search.ts +157 -0
  302. package/src/modes/components/hook-editor.ts +101 -0
  303. package/src/modes/components/hook-input.ts +72 -0
  304. package/src/modes/components/hook-message.ts +100 -0
  305. package/src/modes/components/hook-selector.ts +155 -0
  306. package/src/modes/components/index.ts +41 -0
  307. package/src/modes/components/keybinding-hints.ts +65 -0
  308. package/src/modes/components/login-dialog.ts +164 -0
  309. package/src/modes/components/mcp-add-wizard.ts +1295 -0
  310. package/src/modes/components/model-selector.ts +625 -0
  311. package/src/modes/components/oauth-selector.ts +210 -0
  312. package/src/modes/components/plugin-settings.ts +477 -0
  313. package/src/modes/components/python-execution.ts +196 -0
  314. package/src/modes/components/queue-mode-selector.ts +56 -0
  315. package/src/modes/components/read-tool-group.ts +119 -0
  316. package/src/modes/components/session-selector.ts +242 -0
  317. package/src/modes/components/settings-defs.ts +340 -0
  318. package/src/modes/components/settings-selector.ts +529 -0
  319. package/src/modes/components/show-images-selector.ts +45 -0
  320. package/src/modes/components/skill-message.ts +90 -0
  321. package/src/modes/components/status-line/index.ts +4 -0
  322. package/src/modes/components/status-line/presets.ts +94 -0
  323. package/src/modes/components/status-line/segments.ts +352 -0
  324. package/src/modes/components/status-line/separators.ts +55 -0
  325. package/src/modes/components/status-line/types.ts +75 -0
  326. package/src/modes/components/status-line-segment-editor.ts +354 -0
  327. package/src/modes/components/status-line.ts +421 -0
  328. package/src/modes/components/theme-selector.ts +63 -0
  329. package/src/modes/components/thinking-selector.ts +64 -0
  330. package/src/modes/components/todo-display.ts +115 -0
  331. package/src/modes/components/todo-reminder.ts +40 -0
  332. package/src/modes/components/tool-execution.ts +703 -0
  333. package/src/modes/components/tree-selector.ts +904 -0
  334. package/src/modes/components/ttsr-notification.ts +80 -0
  335. package/src/modes/components/user-message-selector.ts +146 -0
  336. package/src/modes/components/user-message.ts +22 -0
  337. package/src/modes/components/visual-truncate.ts +63 -0
  338. package/src/modes/components/welcome.ts +247 -0
  339. package/src/modes/controllers/command-controller.ts +1120 -0
  340. package/src/modes/controllers/event-controller.ts +479 -0
  341. package/src/modes/controllers/extension-ui-controller.ts +778 -0
  342. package/src/modes/controllers/input-controller.ts +671 -0
  343. package/src/modes/controllers/mcp-command-controller.ts +1315 -0
  344. package/src/modes/controllers/selector-controller.ts +712 -0
  345. package/src/modes/controllers/ssh-command-controller.ts +452 -0
  346. package/src/modes/index.ts +15 -0
  347. package/src/modes/interactive-mode.ts +1027 -0
  348. package/src/modes/print-mode.ts +191 -0
  349. package/src/modes/rpc/rpc-client.ts +583 -0
  350. package/src/modes/rpc/rpc-mode.ts +700 -0
  351. package/src/modes/rpc/rpc-types.ts +236 -0
  352. package/src/modes/theme/dark.json +95 -0
  353. package/src/modes/theme/defaults/alabaster.json +93 -0
  354. package/src/modes/theme/defaults/amethyst.json +96 -0
  355. package/src/modes/theme/defaults/anthracite.json +93 -0
  356. package/src/modes/theme/defaults/basalt.json +91 -0
  357. package/src/modes/theme/defaults/birch.json +95 -0
  358. package/src/modes/theme/defaults/dark-abyss.json +91 -0
  359. package/src/modes/theme/defaults/dark-arctic.json +104 -0
  360. package/src/modes/theme/defaults/dark-aurora.json +95 -0
  361. package/src/modes/theme/defaults/dark-catppuccin.json +107 -0
  362. package/src/modes/theme/defaults/dark-cavern.json +91 -0
  363. package/src/modes/theme/defaults/dark-copper.json +95 -0
  364. package/src/modes/theme/defaults/dark-cosmos.json +90 -0
  365. package/src/modes/theme/defaults/dark-cyberpunk.json +102 -0
  366. package/src/modes/theme/defaults/dark-dracula.json +98 -0
  367. package/src/modes/theme/defaults/dark-eclipse.json +91 -0
  368. package/src/modes/theme/defaults/dark-ember.json +95 -0
  369. package/src/modes/theme/defaults/dark-equinox.json +90 -0
  370. package/src/modes/theme/defaults/dark-forest.json +96 -0
  371. package/src/modes/theme/defaults/dark-github.json +105 -0
  372. package/src/modes/theme/defaults/dark-gruvbox.json +112 -0
  373. package/src/modes/theme/defaults/dark-lavender.json +95 -0
  374. package/src/modes/theme/defaults/dark-lunar.json +89 -0
  375. package/src/modes/theme/defaults/dark-midnight.json +95 -0
  376. package/src/modes/theme/defaults/dark-monochrome.json +94 -0
  377. package/src/modes/theme/defaults/dark-monokai.json +98 -0
  378. package/src/modes/theme/defaults/dark-nebula.json +90 -0
  379. package/src/modes/theme/defaults/dark-nord.json +97 -0
  380. package/src/modes/theme/defaults/dark-ocean.json +101 -0
  381. package/src/modes/theme/defaults/dark-one.json +100 -0
  382. package/src/modes/theme/defaults/dark-rainforest.json +91 -0
  383. package/src/modes/theme/defaults/dark-reef.json +91 -0
  384. package/src/modes/theme/defaults/dark-retro.json +92 -0
  385. package/src/modes/theme/defaults/dark-rose-pine.json +96 -0
  386. package/src/modes/theme/defaults/dark-sakura.json +95 -0
  387. package/src/modes/theme/defaults/dark-slate.json +95 -0
  388. package/src/modes/theme/defaults/dark-solarized.json +97 -0
  389. package/src/modes/theme/defaults/dark-solstice.json +90 -0
  390. package/src/modes/theme/defaults/dark-starfall.json +91 -0
  391. package/src/modes/theme/defaults/dark-sunset.json +99 -0
  392. package/src/modes/theme/defaults/dark-swamp.json +90 -0
  393. package/src/modes/theme/defaults/dark-synthwave.json +103 -0
  394. package/src/modes/theme/defaults/dark-taiga.json +91 -0
  395. package/src/modes/theme/defaults/dark-terminal.json +95 -0
  396. package/src/modes/theme/defaults/dark-tokyo-night.json +101 -0
  397. package/src/modes/theme/defaults/dark-tundra.json +91 -0
  398. package/src/modes/theme/defaults/dark-twilight.json +91 -0
  399. package/src/modes/theme/defaults/dark-volcanic.json +91 -0
  400. package/src/modes/theme/defaults/graphite.json +92 -0
  401. package/src/modes/theme/defaults/index.ts +195 -0
  402. package/src/modes/theme/defaults/light-arctic.json +107 -0
  403. package/src/modes/theme/defaults/light-aurora-day.json +91 -0
  404. package/src/modes/theme/defaults/light-canyon.json +91 -0
  405. package/src/modes/theme/defaults/light-catppuccin.json +106 -0
  406. package/src/modes/theme/defaults/light-cirrus.json +90 -0
  407. package/src/modes/theme/defaults/light-coral.json +95 -0
  408. package/src/modes/theme/defaults/light-cyberpunk.json +96 -0
  409. package/src/modes/theme/defaults/light-dawn.json +90 -0
  410. package/src/modes/theme/defaults/light-dunes.json +91 -0
  411. package/src/modes/theme/defaults/light-eucalyptus.json +95 -0
  412. package/src/modes/theme/defaults/light-forest.json +100 -0
  413. package/src/modes/theme/defaults/light-frost.json +95 -0
  414. package/src/modes/theme/defaults/light-github.json +115 -0
  415. package/src/modes/theme/defaults/light-glacier.json +91 -0
  416. package/src/modes/theme/defaults/light-gruvbox.json +108 -0
  417. package/src/modes/theme/defaults/light-haze.json +90 -0
  418. package/src/modes/theme/defaults/light-honeycomb.json +95 -0
  419. package/src/modes/theme/defaults/light-lagoon.json +91 -0
  420. package/src/modes/theme/defaults/light-lavender.json +95 -0
  421. package/src/modes/theme/defaults/light-meadow.json +91 -0
  422. package/src/modes/theme/defaults/light-mint.json +95 -0
  423. package/src/modes/theme/defaults/light-monochrome.json +101 -0
  424. package/src/modes/theme/defaults/light-ocean.json +99 -0
  425. package/src/modes/theme/defaults/light-one.json +99 -0
  426. package/src/modes/theme/defaults/light-opal.json +91 -0
  427. package/src/modes/theme/defaults/light-orchard.json +91 -0
  428. package/src/modes/theme/defaults/light-paper.json +95 -0
  429. package/src/modes/theme/defaults/light-prism.json +90 -0
  430. package/src/modes/theme/defaults/light-retro.json +98 -0
  431. package/src/modes/theme/defaults/light-sand.json +95 -0
  432. package/src/modes/theme/defaults/light-savanna.json +91 -0
  433. package/src/modes/theme/defaults/light-solarized.json +102 -0
  434. package/src/modes/theme/defaults/light-soleil.json +90 -0
  435. package/src/modes/theme/defaults/light-sunset.json +99 -0
  436. package/src/modes/theme/defaults/light-synthwave.json +98 -0
  437. package/src/modes/theme/defaults/light-tokyo-night.json +111 -0
  438. package/src/modes/theme/defaults/light-wetland.json +91 -0
  439. package/src/modes/theme/defaults/light-zenith.json +89 -0
  440. package/src/modes/theme/defaults/limestone.json +94 -0
  441. package/src/modes/theme/defaults/mahogany.json +97 -0
  442. package/src/modes/theme/defaults/marble.json +93 -0
  443. package/src/modes/theme/defaults/obsidian.json +91 -0
  444. package/src/modes/theme/defaults/onyx.json +91 -0
  445. package/src/modes/theme/defaults/pearl.json +93 -0
  446. package/src/modes/theme/defaults/porcelain.json +91 -0
  447. package/src/modes/theme/defaults/quartz.json +96 -0
  448. package/src/modes/theme/defaults/sandstone.json +95 -0
  449. package/src/modes/theme/defaults/titanium.json +90 -0
  450. package/src/modes/theme/light.json +93 -0
  451. package/src/modes/theme/mermaid-cache.ts +111 -0
  452. package/src/modes/theme/theme-schema.json +429 -0
  453. package/src/modes/theme/theme.ts +2333 -0
  454. package/src/modes/types.ts +216 -0
  455. package/src/modes/utils/ui-helpers.ts +529 -0
  456. package/src/patch/applicator.ts +1482 -0
  457. package/src/patch/diff.ts +425 -0
  458. package/src/patch/fuzzy.ts +784 -0
  459. package/src/patch/hashline.ts +972 -0
  460. package/src/patch/index.ts +964 -0
  461. package/src/patch/normalize.ts +397 -0
  462. package/src/patch/normative.ts +72 -0
  463. package/src/patch/parser.ts +532 -0
  464. package/src/patch/shared.ts +400 -0
  465. package/src/patch/types.ts +292 -0
  466. package/src/priority.json +35 -0
  467. package/src/prompts/agents/explore.md +48 -0
  468. package/src/prompts/agents/frontmatter.md +9 -0
  469. package/src/prompts/agents/init.md +36 -0
  470. package/src/prompts/agents/librarian.md +53 -0
  471. package/src/prompts/agents/oracle.md +51 -0
  472. package/src/prompts/agents/reviewer.md +70 -0
  473. package/src/prompts/agents/task.md +14 -0
  474. package/src/prompts/compaction/branch-summary-context.md +5 -0
  475. package/src/prompts/compaction/branch-summary-preamble.md +2 -0
  476. package/src/prompts/compaction/branch-summary.md +30 -0
  477. package/src/prompts/compaction/compaction-short-summary.md +9 -0
  478. package/src/prompts/compaction/compaction-summary-context.md +5 -0
  479. package/src/prompts/compaction/compaction-summary.md +38 -0
  480. package/src/prompts/compaction/compaction-turn-prefix.md +17 -0
  481. package/src/prompts/compaction/compaction-update-summary.md +45 -0
  482. package/src/prompts/memories/consolidation.md +30 -0
  483. package/src/prompts/memories/read_path.md +11 -0
  484. package/src/prompts/memories/stage_one_input.md +6 -0
  485. package/src/prompts/memories/stage_one_system.md +21 -0
  486. package/src/prompts/review-request.md +64 -0
  487. package/src/prompts/system/agent-creation-architect.md +65 -0
  488. package/src/prompts/system/agent-creation-user.md +6 -0
  489. package/src/prompts/system/custom-system-prompt.md +68 -0
  490. package/src/prompts/system/file-operations.md +10 -0
  491. package/src/prompts/system/subagent-submit-reminder.md +11 -0
  492. package/src/prompts/system/subagent-system-prompt.md +31 -0
  493. package/src/prompts/system/subagent-user-prompt.md +8 -0
  494. package/src/prompts/system/summarization-system.md +3 -0
  495. package/src/prompts/system/system-prompt.md +300 -0
  496. package/src/prompts/system/title-system.md +2 -0
  497. package/src/prompts/system/ttsr-interrupt.md +7 -0
  498. package/src/prompts/system/web-search.md +28 -0
  499. package/src/prompts/tools/ask.md +44 -0
  500. package/src/prompts/tools/bash.md +24 -0
  501. package/src/prompts/tools/browser.md +33 -0
  502. package/src/prompts/tools/calculator.md +12 -0
  503. package/src/prompts/tools/explore.md +29 -0
  504. package/src/prompts/tools/fetch.md +16 -0
  505. package/src/prompts/tools/find.md +18 -0
  506. package/src/prompts/tools/gemini-image.md +23 -0
  507. package/src/prompts/tools/grep.md +28 -0
  508. package/src/prompts/tools/hashline.md +232 -0
  509. package/src/prompts/tools/librarian.md +24 -0
  510. package/src/prompts/tools/lsp.md +28 -0
  511. package/src/prompts/tools/oracle.md +26 -0
  512. package/src/prompts/tools/patch.md +74 -0
  513. package/src/prompts/tools/python.md +66 -0
  514. package/src/prompts/tools/read.md +36 -0
  515. package/src/prompts/tools/replace.md +38 -0
  516. package/src/prompts/tools/reviewer.md +41 -0
  517. package/src/prompts/tools/ssh.md +51 -0
  518. package/src/prompts/tools/task-summary.md +28 -0
  519. package/src/prompts/tools/task.md +275 -0
  520. package/src/prompts/tools/todo-write.md +65 -0
  521. package/src/prompts/tools/undo-edit.md +7 -0
  522. package/src/prompts/tools/web-search.md +19 -0
  523. package/src/prompts/tools/write.md +18 -0
  524. package/src/sdk.ts +1287 -0
  525. package/src/secrets/index.ts +116 -0
  526. package/src/secrets/obfuscator.ts +269 -0
  527. package/src/secrets/regex.ts +21 -0
  528. package/src/session/agent-session.ts +4669 -0
  529. package/src/session/agent-storage.ts +621 -0
  530. package/src/session/artifacts.ts +132 -0
  531. package/src/session/auth-storage.ts +1433 -0
  532. package/src/session/blob-store.ts +103 -0
  533. package/src/session/compaction/branch-summarization.ts +315 -0
  534. package/src/session/compaction/compaction.ts +864 -0
  535. package/src/session/compaction/index.ts +7 -0
  536. package/src/session/compaction/pruning.ts +91 -0
  537. package/src/session/compaction/utils.ts +171 -0
  538. package/src/session/history-storage.ts +170 -0
  539. package/src/session/messages.ts +317 -0
  540. package/src/session/session-manager.ts +2276 -0
  541. package/src/session/session-storage.ts +342 -0
  542. package/src/session/streaming-output.ts +565 -0
  543. package/src/slash-commands/builtin-registry.ts +439 -0
  544. package/src/ssh/config-writer.ts +183 -0
  545. package/src/ssh/connection-manager.ts +444 -0
  546. package/src/ssh/ssh-executor.ts +127 -0
  547. package/src/ssh/sshfs-mount.ts +135 -0
  548. package/src/stt/downloader.ts +71 -0
  549. package/src/stt/index.ts +3 -0
  550. package/src/stt/recorder.ts +351 -0
  551. package/src/stt/setup.ts +52 -0
  552. package/src/stt/stt-controller.ts +160 -0
  553. package/src/stt/transcribe.py +70 -0
  554. package/src/stt/transcriber.ts +91 -0
  555. package/src/system-prompt.ts +685 -0
  556. package/src/task/agents.ts +155 -0
  557. package/src/task/batch.ts +102 -0
  558. package/src/task/commands.ts +134 -0
  559. package/src/task/discovery.ts +126 -0
  560. package/src/task/executor.ts +908 -0
  561. package/src/task/index.ts +223 -0
  562. package/src/task/output-manager.ts +107 -0
  563. package/src/task/parallel.ts +84 -0
  564. package/src/task/render.ts +326 -0
  565. package/src/task/subprocess-tool-registry.ts +88 -0
  566. package/src/task/template.ts +32 -0
  567. package/src/task/types.ts +144 -0
  568. package/src/tools/ask.ts +523 -0
  569. package/src/tools/bash-interactive.ts +419 -0
  570. package/src/tools/bash-interceptor.ts +105 -0
  571. package/src/tools/bash-normalize.ts +107 -0
  572. package/src/tools/bash-skill-urls.ts +177 -0
  573. package/src/tools/bash.ts +347 -0
  574. package/src/tools/browser.ts +1374 -0
  575. package/src/tools/calculator.ts +537 -0
  576. package/src/tools/context.ts +39 -0
  577. package/src/tools/explore.ts +23 -0
  578. package/src/tools/fetch.ts +1091 -0
  579. package/src/tools/find.ts +540 -0
  580. package/src/tools/fs-cache-invalidation.ts +28 -0
  581. package/src/tools/gemini-image.ts +907 -0
  582. package/src/tools/grep.ts +489 -0
  583. package/src/tools/index.ts +337 -0
  584. package/src/tools/json-tree.ts +231 -0
  585. package/src/tools/jtd-to-json-schema.ts +247 -0
  586. package/src/tools/jtd-to-typescript.ts +198 -0
  587. package/src/tools/librarian.ts +33 -0
  588. package/src/tools/list-limit.ts +40 -0
  589. package/src/tools/notebook.ts +287 -0
  590. package/src/tools/oracle.ts +40 -0
  591. package/src/tools/output-meta.ts +459 -0
  592. package/src/tools/output-utils.ts +63 -0
  593. package/src/tools/path-utils.ts +116 -0
  594. package/src/tools/puppeteer/00_stealth_tampering.txt +63 -0
  595. package/src/tools/puppeteer/01_stealth_activity.txt +20 -0
  596. package/src/tools/puppeteer/02_stealth_hairline.txt +11 -0
  597. package/src/tools/puppeteer/03_stealth_botd.txt +384 -0
  598. package/src/tools/puppeteer/04_stealth_iframe.txt +81 -0
  599. package/src/tools/puppeteer/05_stealth_webgl.txt +75 -0
  600. package/src/tools/puppeteer/06_stealth_screen.txt +72 -0
  601. package/src/tools/puppeteer/07_stealth_fonts.txt +97 -0
  602. package/src/tools/puppeteer/08_stealth_audio.txt +51 -0
  603. package/src/tools/puppeteer/09_stealth_locale.txt +46 -0
  604. package/src/tools/puppeteer/10_stealth_plugins.txt +206 -0
  605. package/src/tools/puppeteer/11_stealth_hardware.txt +8 -0
  606. package/src/tools/puppeteer/12_stealth_codecs.txt +40 -0
  607. package/src/tools/puppeteer/13_stealth_worker.txt +74 -0
  608. package/src/tools/python.ts +1118 -0
  609. package/src/tools/read.ts +1193 -0
  610. package/src/tools/render-utils.ts +680 -0
  611. package/src/tools/renderers.ts +60 -0
  612. package/src/tools/reviewer-tool.ts +41 -0
  613. package/src/tools/ssh.ts +326 -0
  614. package/src/tools/subagent-tool.ts +169 -0
  615. package/src/tools/submit-result.ts +152 -0
  616. package/src/tools/todo-write.ts +255 -0
  617. package/src/tools/tool-errors.ts +92 -0
  618. package/src/tools/tool-result.ts +86 -0
  619. package/src/tools/undo-edit.ts +145 -0
  620. package/src/tools/undo-history.ts +22 -0
  621. package/src/tools/write.ts +274 -0
  622. package/src/tui/code-cell.ts +108 -0
  623. package/src/tui/file-list.ts +47 -0
  624. package/src/tui/index.ts +11 -0
  625. package/src/tui/output-block.ts +144 -0
  626. package/src/tui/status-line.ts +39 -0
  627. package/src/tui/tree-list.ts +53 -0
  628. package/src/tui/types.ts +16 -0
  629. package/src/tui/utils.ts +116 -0
  630. package/src/utils/changelog.ts +98 -0
  631. package/src/utils/event-bus.ts +33 -0
  632. package/src/utils/external-editor.ts +59 -0
  633. package/src/utils/file-display-mode.ts +36 -0
  634. package/src/utils/file-mentions.ts +384 -0
  635. package/src/utils/frontmatter.ts +101 -0
  636. package/src/utils/fuzzy.ts +108 -0
  637. package/src/utils/ignore-files.ts +119 -0
  638. package/src/utils/image-convert.ts +27 -0
  639. package/src/utils/image-resize.ts +236 -0
  640. package/src/utils/mime.ts +30 -0
  641. package/src/utils/open.ts +20 -0
  642. package/src/utils/shell-snapshot.ts +199 -0
  643. package/src/utils/timings.ts +26 -0
  644. package/src/utils/title-generator.ts +167 -0
  645. package/src/utils/tools-manager.ts +362 -0
  646. package/src/web/scrapers/artifacthub.ts +215 -0
  647. package/src/web/scrapers/arxiv.ts +88 -0
  648. package/src/web/scrapers/aur.ts +175 -0
  649. package/src/web/scrapers/biorxiv.ts +141 -0
  650. package/src/web/scrapers/bluesky.ts +284 -0
  651. package/src/web/scrapers/brew.ts +177 -0
  652. package/src/web/scrapers/cheatsh.ts +78 -0
  653. package/src/web/scrapers/chocolatey.ts +158 -0
  654. package/src/web/scrapers/choosealicense.ts +110 -0
  655. package/src/web/scrapers/cisa-kev.ts +100 -0
  656. package/src/web/scrapers/clojars.ts +180 -0
  657. package/src/web/scrapers/coingecko.ts +184 -0
  658. package/src/web/scrapers/crates-io.ts +128 -0
  659. package/src/web/scrapers/crossref.ts +149 -0
  660. package/src/web/scrapers/devto.ts +177 -0
  661. package/src/web/scrapers/discogs.ts +307 -0
  662. package/src/web/scrapers/discourse.ts +221 -0
  663. package/src/web/scrapers/dockerhub.ts +160 -0
  664. package/src/web/scrapers/fdroid.ts +158 -0
  665. package/src/web/scrapers/firefox-addons.ts +214 -0
  666. package/src/web/scrapers/flathub.ts +239 -0
  667. package/src/web/scrapers/github-gist.ts +68 -0
  668. package/src/web/scrapers/github.ts +490 -0
  669. package/src/web/scrapers/gitlab.ts +456 -0
  670. package/src/web/scrapers/go-pkg.ts +275 -0
  671. package/src/web/scrapers/hackage.ts +94 -0
  672. package/src/web/scrapers/hackernews.ts +208 -0
  673. package/src/web/scrapers/hex.ts +121 -0
  674. package/src/web/scrapers/huggingface.ts +385 -0
  675. package/src/web/scrapers/iacr.ts +86 -0
  676. package/src/web/scrapers/index.ts +249 -0
  677. package/src/web/scrapers/jetbrains-marketplace.ts +169 -0
  678. package/src/web/scrapers/lemmy.ts +220 -0
  679. package/src/web/scrapers/lobsters.ts +186 -0
  680. package/src/web/scrapers/mastodon.ts +310 -0
  681. package/src/web/scrapers/maven.ts +152 -0
  682. package/src/web/scrapers/mdn.ts +172 -0
  683. package/src/web/scrapers/metacpan.ts +253 -0
  684. package/src/web/scrapers/musicbrainz.ts +272 -0
  685. package/src/web/scrapers/npm.ts +114 -0
  686. package/src/web/scrapers/nuget.ts +205 -0
  687. package/src/web/scrapers/nvd.ts +243 -0
  688. package/src/web/scrapers/ollama.ts +265 -0
  689. package/src/web/scrapers/open-vsx.ts +119 -0
  690. package/src/web/scrapers/opencorporates.ts +275 -0
  691. package/src/web/scrapers/openlibrary.ts +319 -0
  692. package/src/web/scrapers/orcid.ts +298 -0
  693. package/src/web/scrapers/osv.ts +192 -0
  694. package/src/web/scrapers/packagist.ts +174 -0
  695. package/src/web/scrapers/pub-dev.ts +185 -0
  696. package/src/web/scrapers/pubmed.ts +177 -0
  697. package/src/web/scrapers/pypi.ts +129 -0
  698. package/src/web/scrapers/rawg.ts +124 -0
  699. package/src/web/scrapers/readthedocs.ts +125 -0
  700. package/src/web/scrapers/reddit.ts +104 -0
  701. package/src/web/scrapers/repology.ts +262 -0
  702. package/src/web/scrapers/rfc.ts +209 -0
  703. package/src/web/scrapers/rubygems.ts +117 -0
  704. package/src/web/scrapers/searchcode.ts +217 -0
  705. package/src/web/scrapers/sec-edgar.ts +274 -0
  706. package/src/web/scrapers/semantic-scholar.ts +190 -0
  707. package/src/web/scrapers/snapcraft.ts +200 -0
  708. package/src/web/scrapers/sourcegraph.ts +373 -0
  709. package/src/web/scrapers/spdx.ts +121 -0
  710. package/src/web/scrapers/spotify.ts +217 -0
  711. package/src/web/scrapers/stackoverflow.ts +124 -0
  712. package/src/web/scrapers/terraform.ts +304 -0
  713. package/src/web/scrapers/tldr.ts +51 -0
  714. package/src/web/scrapers/twitter.ts +97 -0
  715. package/src/web/scrapers/types.ts +200 -0
  716. package/src/web/scrapers/utils.ts +142 -0
  717. package/src/web/scrapers/vimeo.ts +152 -0
  718. package/src/web/scrapers/vscode-marketplace.ts +195 -0
  719. package/src/web/scrapers/w3c.ts +163 -0
  720. package/src/web/scrapers/wikidata.ts +357 -0
  721. package/src/web/scrapers/wikipedia.ts +95 -0
  722. package/src/web/scrapers/youtube.ts +312 -0
  723. package/src/web/search/auth.ts +178 -0
  724. package/src/web/search/index.ts +598 -0
  725. package/src/web/search/provider.ts +77 -0
  726. package/src/web/search/providers/anthropic.ts +284 -0
  727. package/src/web/search/providers/base.ts +22 -0
  728. package/src/web/search/providers/brave.ts +165 -0
  729. package/src/web/search/providers/codex.ts +377 -0
  730. package/src/web/search/providers/exa.ts +158 -0
  731. package/src/web/search/providers/gemini.ts +437 -0
  732. package/src/web/search/providers/jina.ts +99 -0
  733. package/src/web/search/providers/kimi.ts +196 -0
  734. package/src/web/search/providers/perplexity.ts +546 -0
  735. package/src/web/search/providers/synthetic.ts +136 -0
  736. package/src/web/search/providers/zai.ts +352 -0
  737. package/src/web/search/render.ts +299 -0
  738. package/src/web/search/types.ts +437 -0
@@ -0,0 +1,1295 @@
1
+ /**
2
+ * MCP Add Wizard Component
3
+ *
4
+ * Interactive multi-step wizard for adding MCP servers.
5
+ */
6
+ import {
7
+ Container,
8
+ Input,
9
+ matchesKey,
10
+ replaceTabs,
11
+ Spacer,
12
+ Text,
13
+ TruncatedText,
14
+ truncateToWidth,
15
+ } from "@nghyane/arcane-tui";
16
+ import { validateServerName } from "../../mcp/config-writer";
17
+ import { analyzeAuthError, discoverOAuthEndpoints } from "../../mcp/oauth-discovery";
18
+ import type { MCPHttpServerConfig, MCPServerConfig, MCPSseServerConfig, MCPStdioServerConfig } from "../../mcp/types";
19
+ import { theme } from "../theme/theme";
20
+ import { DynamicBorder } from "./dynamic-border";
21
+
22
+ type TransportType = "stdio" | "http" | "sse";
23
+ type AuthMethod = "none" | "oauth" | "manual";
24
+ type AuthLocation = "env" | "header";
25
+ type Scope = "user" | "project";
26
+
27
+ type WizardStep =
28
+ | "name"
29
+ | "transport"
30
+ | "command"
31
+ | "args"
32
+ | "url"
33
+ | "auth-method"
34
+ | "oauth-error"
35
+ | "oauth-auth-url"
36
+ | "oauth-token-url"
37
+ | "oauth-client-id"
38
+ | "oauth-client-secret"
39
+ | "oauth-scopes"
40
+ | "apikey"
41
+ | "auth-location"
42
+ | "env-var-name"
43
+ | "header-name"
44
+ | "scope"
45
+ | "confirm";
46
+
47
+ interface WizardState {
48
+ name: string;
49
+ transport: TransportType | null;
50
+ command: string;
51
+ args: string;
52
+ url: string;
53
+ authMethod: AuthMethod;
54
+ oauthAuthUrl: string;
55
+ oauthTokenUrl: string;
56
+ oauthClientId: string;
57
+ oauthClientSecret: string;
58
+ oauthScopes: string;
59
+ oauthCredentialId: string | null;
60
+ apiKey: string;
61
+ authLocation: AuthLocation | null;
62
+ envVarName: string;
63
+ headerName: string;
64
+ scope: Scope | null;
65
+ }
66
+
67
+ /** Max display width for sanitized error/URL text in wizard TUI */
68
+ const MAX_DISPLAY_WIDTH = 120;
69
+
70
+ /** Sanitize a string for TUI display: replace tabs and truncate */
71
+ function sanitize(text: string): string {
72
+ return truncateToWidth(replaceTabs(text), MAX_DISPLAY_WIDTH);
73
+ }
74
+
75
+ export class MCPAddWizard extends Container {
76
+ #currentStep: WizardStep = "name";
77
+ #state: WizardState = {
78
+ name: "",
79
+ transport: null,
80
+ command: "",
81
+ args: "",
82
+ url: "",
83
+ authMethod: "none",
84
+ oauthAuthUrl: "",
85
+ oauthTokenUrl: "",
86
+ oauthClientId: "",
87
+ oauthClientSecret: "",
88
+ oauthScopes: "",
89
+ oauthCredentialId: null,
90
+ apiKey: "",
91
+ authLocation: null,
92
+ envVarName: "API_KEY",
93
+ headerName: "Authorization",
94
+ scope: null,
95
+ };
96
+
97
+ #contentContainer: Container;
98
+ #inputField: Input | null = null;
99
+ #selectedIndex = 0;
100
+ #validationError: string | null = null;
101
+ #onCompleteCallback: (name: string, config: MCPServerConfig, scope: Scope) => void;
102
+ #onCancelCallback: () => void;
103
+ #onOAuthCallback:
104
+ | ((authUrl: string, tokenUrl: string, clientId: string, clientSecret: string, scopes: string) => Promise<string>)
105
+ | null = null;
106
+ #onTestConnectionCallback: ((config: MCPServerConfig) => Promise<void>) | null = null;
107
+ #onRenderCallback: (() => void) | null = null;
108
+
109
+ constructor(
110
+ onComplete: (name: string, config: MCPServerConfig, scope: Scope) => void,
111
+ onCancel: () => void,
112
+ onOAuth?: (
113
+ authUrl: string,
114
+ tokenUrl: string,
115
+ clientId: string,
116
+ clientSecret: string,
117
+ scopes: string,
118
+ ) => Promise<string>,
119
+ onTestConnection?: (config: MCPServerConfig) => Promise<void>,
120
+ onRender?: () => void,
121
+ initialName?: string,
122
+ ) {
123
+ super();
124
+ this.#onCompleteCallback = onComplete;
125
+ this.#onCancelCallback = onCancel;
126
+ this.#onOAuthCallback = onOAuth ?? null;
127
+ this.#onTestConnectionCallback = onTestConnection ?? null;
128
+ this.#onRenderCallback = onRender ?? null;
129
+ if (initialName && initialName.trim().length > 0) {
130
+ this.#state.name = initialName.trim();
131
+ this.#currentStep = "transport";
132
+ }
133
+
134
+ // Add border
135
+ this.addChild(new DynamicBorder());
136
+ this.addChild(new Spacer(1));
137
+
138
+ // Add title
139
+ this.addChild(new TruncatedText(theme.bold("Add MCP Server")));
140
+ this.addChild(new Spacer(1));
141
+
142
+ // Content container for step-specific content
143
+ this.#contentContainer = new Container();
144
+ this.addChild(this.#contentContainer);
145
+
146
+ this.addChild(new Spacer(1));
147
+
148
+ // Add bottom border
149
+ this.addChild(new DynamicBorder());
150
+
151
+ // Render first step
152
+ this.#renderStep();
153
+ }
154
+
155
+ #requestRender(): void {
156
+ this.#onRenderCallback?.();
157
+ }
158
+
159
+ #renderStep(): void {
160
+ this.#contentContainer.clear();
161
+ this.#inputField = null; // Reset input field
162
+
163
+ switch (this.#currentStep) {
164
+ case "name":
165
+ this.#renderNameStep();
166
+ break;
167
+ case "transport":
168
+ this.#renderTransportStep();
169
+ break;
170
+ case "command":
171
+ this.#renderCommandStep();
172
+ break;
173
+ case "args":
174
+ this.#renderArgsStep();
175
+ break;
176
+ case "url":
177
+ this.#renderUrlStep();
178
+ break;
179
+ case "auth-method":
180
+ this.#renderAuthMethodStep();
181
+ break;
182
+ case "oauth-error":
183
+ this.#renderOAuthErrorStep();
184
+ break;
185
+ case "oauth-auth-url":
186
+ this.#renderOAuthAuthUrlStep();
187
+ break;
188
+ case "oauth-token-url":
189
+ this.#renderOAuthTokenUrlStep();
190
+ break;
191
+ case "oauth-client-id":
192
+ this.#renderOAuthClientIdStep();
193
+ break;
194
+ case "oauth-client-secret":
195
+ this.#renderOAuthClientSecretStep();
196
+ break;
197
+ case "oauth-scopes":
198
+ this.#renderOAuthScopesStep();
199
+ break;
200
+ case "apikey":
201
+ this.#renderApiKeyStep();
202
+ break;
203
+ case "auth-location":
204
+ this.#renderAuthLocationStep();
205
+ break;
206
+ case "env-var-name":
207
+ this.#renderEnvVarNameStep();
208
+ break;
209
+ case "header-name":
210
+ this.#renderHeaderNameStep();
211
+ break;
212
+ case "scope":
213
+ this.#renderScopeStep();
214
+ break;
215
+ case "confirm":
216
+ this.#renderConfirmStep();
217
+ break;
218
+ }
219
+ }
220
+
221
+ #renderNameStep(): void {
222
+ this.#contentContainer.addChild(new Text(theme.fg("accent", "Step 1: Server Name")));
223
+ this.#contentContainer.addChild(new Spacer(1));
224
+ this.#contentContainer.addChild(new Text("Enter a unique name for this server:", 0, 0));
225
+ this.#contentContainer.addChild(new Spacer(1));
226
+
227
+ this.#inputField = new Input();
228
+ this.#inputField.setValue(this.#state.name);
229
+ this.#contentContainer.addChild(this.#inputField);
230
+ this.#contentContainer.addChild(new Spacer(1));
231
+
232
+ // Show validation error if any
233
+ if (this.#validationError) {
234
+ this.#contentContainer.addChild(new Text(theme.fg("error", `✗ ${sanitize(this.#validationError)}`), 0, 0));
235
+ this.#contentContainer.addChild(new Spacer(1));
236
+ }
237
+
238
+ this.#contentContainer.addChild(
239
+ new Text(theme.fg("muted", "[Only letters, numbers, dash, underscore, dot]"), 0, 0),
240
+ );
241
+ this.#contentContainer.addChild(new Text(theme.fg("muted", "[Enter to continue, Esc to cancel]"), 0, 0));
242
+ }
243
+
244
+ #renderTransportStep(): void {
245
+ this.#contentContainer.addChild(new Text(theme.fg("accent", "Step 2: Transport Type")));
246
+ this.#contentContainer.addChild(new Spacer(1));
247
+ this.#contentContainer.addChild(new Text("Select the transport type:", 0, 0));
248
+ this.#contentContainer.addChild(new Spacer(1));
249
+
250
+ const options = [
251
+ { value: "stdio" as const, label: "stdio (Local process)" },
252
+ { value: "http" as const, label: "http (HTTP server)" },
253
+ { value: "sse" as const, label: "sse (Server-Sent Events)" },
254
+ ];
255
+
256
+ for (let i = 0; i < options.length; i++) {
257
+ const option = options[i];
258
+ const isSelected = i === this.#selectedIndex;
259
+ const prefix = isSelected ? theme.fg("accent", `${theme.nav.cursor} `) : " ";
260
+ const text = isSelected ? theme.fg("accent", option.label) : option.label;
261
+ this.#contentContainer.addChild(new Text(prefix + text, 0, 0));
262
+ }
263
+
264
+ this.#contentContainer.addChild(new Spacer(1));
265
+ this.#contentContainer.addChild(
266
+ new Text(theme.fg("muted", "[↑↓ to navigate, Enter to select, Esc to cancel]"), 0, 0),
267
+ );
268
+ }
269
+
270
+ #renderCommandStep(): void {
271
+ this.#contentContainer.addChild(new Text(theme.fg("accent", "Step 3: Command")));
272
+ this.#contentContainer.addChild(new Spacer(1));
273
+ this.#contentContainer.addChild(new Text("Enter the command to run:", 0, 0));
274
+ this.#contentContainer.addChild(new Spacer(1));
275
+
276
+ this.#inputField = new Input();
277
+ this.#inputField.setValue(this.#state.command);
278
+ this.#contentContainer.addChild(this.#inputField);
279
+ this.#contentContainer.addChild(new Spacer(1));
280
+ this.#contentContainer.addChild(new Text(theme.fg("muted", "[Enter to continue, Esc to go back]"), 0, 0));
281
+ }
282
+
283
+ #renderArgsStep(): void {
284
+ this.#contentContainer.addChild(new Text(theme.fg("accent", "Step 4: Arguments (Optional)")));
285
+ this.#contentContainer.addChild(new Spacer(1));
286
+ this.#contentContainer.addChild(new Text("Enter command arguments (space-separated):", 0, 0));
287
+ this.#contentContainer.addChild(new Spacer(1));
288
+
289
+ this.#inputField = new Input();
290
+ this.#inputField.setValue(this.#state.args);
291
+ this.#contentContainer.addChild(this.#inputField);
292
+ this.#contentContainer.addChild(new Spacer(1));
293
+ this.#contentContainer.addChild(new Text(theme.fg("muted", "[Press Enter to skip or continue]"), 0, 0));
294
+ }
295
+
296
+ #renderUrlStep(): void {
297
+ this.#contentContainer.addChild(new Text(theme.fg("accent", "Step 3: Server URL")));
298
+ this.#contentContainer.addChild(new Spacer(1));
299
+ this.#contentContainer.addChild(new Text("Enter the server URL:", 0, 0));
300
+ this.#contentContainer.addChild(new Spacer(1));
301
+
302
+ this.#inputField = new Input();
303
+ this.#inputField.setValue(this.#state.url);
304
+ this.#contentContainer.addChild(this.#inputField);
305
+ this.#contentContainer.addChild(new Spacer(1));
306
+
307
+ // Show validation error if any
308
+ if (this.#validationError) {
309
+ this.#contentContainer.addChild(new Text(theme.fg("error", `✗ ${sanitize(this.#validationError)}`), 0, 0));
310
+ this.#contentContainer.addChild(new Spacer(1));
311
+ }
312
+
313
+ this.#contentContainer.addChild(new Text(theme.fg("muted", "[Must start with http:// or https://]"), 0, 0));
314
+ this.#contentContainer.addChild(new Text(theme.fg("muted", "[Enter to continue, Esc to go back]"), 0, 0));
315
+ }
316
+
317
+ #renderAuthLocationStep(): void {
318
+ this.#contentContainer.addChild(new Text(theme.fg("accent", "Step: How to provide the key?")));
319
+ this.#contentContainer.addChild(new Spacer(1));
320
+
321
+ const options = [
322
+ { value: "env" as const, label: "Environment variable" },
323
+ { value: "header" as const, label: "HTTP header" },
324
+ ];
325
+
326
+ for (let i = 0; i < options.length; i++) {
327
+ const option = options[i];
328
+ const isSelected = i === this.#selectedIndex;
329
+ const prefix = isSelected ? theme.fg("accent", `${theme.nav.cursor} `) : " ";
330
+ const text = isSelected ? theme.fg("accent", option.label) : option.label;
331
+ this.#contentContainer.addChild(new Text(prefix + text, 0, 0));
332
+ }
333
+
334
+ this.#contentContainer.addChild(new Spacer(1));
335
+ this.#contentContainer.addChild(
336
+ new Text(theme.fg("muted", "[↑↓ to navigate, Enter to select, Esc to go back]"), 0, 0),
337
+ );
338
+ }
339
+
340
+ #renderEnvVarNameStep(): void {
341
+ this.#contentContainer.addChild(new Text(theme.fg("accent", "Step: Environment Variable Name")));
342
+ this.#contentContainer.addChild(new Spacer(1));
343
+ this.#contentContainer.addChild(new Text("Enter the environment variable name:", 0, 0));
344
+ this.#contentContainer.addChild(new Spacer(1));
345
+
346
+ this.#inputField = new Input();
347
+ this.#inputField.setValue(this.#state.envVarName);
348
+ this.#contentContainer.addChild(this.#inputField);
349
+ this.#contentContainer.addChild(new Spacer(1));
350
+ this.#contentContainer.addChild(new Text(theme.fg("muted", "[Enter to continue, Esc to go back]"), 0, 0));
351
+ }
352
+
353
+ #renderHeaderNameStep(): void {
354
+ this.#contentContainer.addChild(new Text(theme.fg("accent", "Step: HTTP Header Name")));
355
+ this.#contentContainer.addChild(new Spacer(1));
356
+ this.#contentContainer.addChild(new Text("Enter the HTTP header name:", 0, 0));
357
+ this.#contentContainer.addChild(new Spacer(1));
358
+
359
+ this.#inputField = new Input();
360
+ this.#inputField.setValue(this.#state.headerName);
361
+ this.#contentContainer.addChild(this.#inputField);
362
+ this.#contentContainer.addChild(new Spacer(1));
363
+ this.#contentContainer.addChild(new Text(theme.fg("muted", "[Enter to continue, Esc to go back]"), 0, 0));
364
+ }
365
+
366
+ #renderScopeStep(): void {
367
+ this.#contentContainer.addChild(new Text(theme.fg("accent", "Step: Configuration Scope")));
368
+ this.#contentContainer.addChild(new Spacer(1));
369
+
370
+ const options = [
371
+ { value: "user" as const, label: "User level (~/.arcane/mcp.json)" },
372
+ { value: "project" as const, label: "Project level (.arcane/mcp.json)" },
373
+ ];
374
+
375
+ for (let i = 0; i < options.length; i++) {
376
+ const option = options[i];
377
+ const isSelected = i === this.#selectedIndex;
378
+ const prefix = isSelected ? theme.fg("accent", `${theme.nav.cursor} `) : " ";
379
+ const text = isSelected ? theme.fg("accent", option.label) : option.label;
380
+ this.#contentContainer.addChild(new Text(prefix + text, 0, 0));
381
+ }
382
+
383
+ this.#contentContainer.addChild(new Spacer(1));
384
+ this.#contentContainer.addChild(
385
+ new Text(theme.fg("muted", "[↑↓ to navigate, Enter to select, Esc to go back]"), 0, 0),
386
+ );
387
+ }
388
+
389
+ #renderConfirmStep(): void {
390
+ this.#contentContainer.addChild(new Text(theme.fg("accent", "Review Configuration")));
391
+ this.#contentContainer.addChild(new Spacer(1));
392
+
393
+ // Show summary
394
+ this.#contentContainer.addChild(new Text(`Name: ${theme.fg("accent", this.#state.name)}`, 0, 0));
395
+ this.#contentContainer.addChild(new Text(`Type: ${this.#state.transport}`, 0, 0));
396
+
397
+ if (this.#state.transport === "stdio") {
398
+ this.#contentContainer.addChild(new Text(`Command: ${this.#state.command}`, 0, 0));
399
+ if (this.#state.args) {
400
+ this.#contentContainer.addChild(new Text(`Args: ${this.#state.args}`, 0, 0));
401
+ }
402
+ } else {
403
+ this.#contentContainer.addChild(new Text(`URL: ${sanitize(this.#state.url)}`, 0, 0));
404
+ }
405
+
406
+ // Auth info
407
+ if (this.#state.authMethod === "none") {
408
+ this.#contentContainer.addChild(new Text("Auth: None", 0, 0));
409
+ } else if (this.#state.authMethod === "oauth") {
410
+ this.#contentContainer.addChild(new Text("Auth: OAuth (authenticated)", 0, 0));
411
+ } else if (this.#state.authMethod === "manual") {
412
+ if (this.#state.authLocation === "env") {
413
+ this.#contentContainer.addChild(new Text(`Auth: API key via env (${this.#state.envVarName})`, 0, 0));
414
+ } else {
415
+ this.#contentContainer.addChild(new Text(`Auth: API key via header (${this.#state.headerName})`, 0, 0));
416
+ }
417
+ }
418
+
419
+ const scopeLabel = this.#state.scope === "user" ? "User level" : "Project level";
420
+ this.#contentContainer.addChild(new Text(`Scope: ${scopeLabel}`, 0, 0));
421
+
422
+ this.#contentContainer.addChild(new Spacer(1));
423
+ this.#contentContainer.addChild(new Text("Save this configuration?", 0, 0));
424
+ this.#contentContainer.addChild(new Spacer(1));
425
+
426
+ const options = ["Yes", "No"];
427
+ for (let i = 0; i < options.length; i++) {
428
+ const isSelected = i === this.#selectedIndex;
429
+ const prefix = isSelected ? theme.fg("accent", `${theme.nav.cursor} `) : " ";
430
+ const text = isSelected ? theme.fg("accent", options[i]) : options[i];
431
+ this.#contentContainer.addChild(new Text(prefix + text, 0, 0));
432
+ }
433
+
434
+ this.#contentContainer.addChild(new Spacer(1));
435
+ this.#contentContainer.addChild(
436
+ new Text(theme.fg("muted", "[↑↓ to navigate, Enter to select, Esc to go back]"), 0, 0),
437
+ );
438
+ }
439
+
440
+ handleInput(keyData: string): void {
441
+ // Handle Ctrl+C to cancel wizard immediately
442
+ if (keyData === "\x03") {
443
+ // Ctrl+C pressed - cancel wizard
444
+ this.#onCancelCallback();
445
+ return;
446
+ }
447
+
448
+ // Handle Escape (always handled by wizard)
449
+ if (matchesKey(keyData, "escape")) {
450
+ if (this.#currentStep === "name") {
451
+ // Cancel wizard
452
+ this.#onCancelCallback();
453
+ return;
454
+ }
455
+ // Go back to previous step
456
+ this.#goBack();
457
+ return;
458
+ }
459
+
460
+ // If we have an input field, let it handle the input
461
+ if (this.#inputField) {
462
+ // Handle Enter to proceed
463
+ if (matchesKey(keyData, "enter") || matchesKey(keyData, "return") || keyData === "\n") {
464
+ this.#saveInputAndProceed();
465
+ return;
466
+ }
467
+ // Pass all other keys to the input field
468
+ this.#inputField.handleInput(keyData);
469
+ return;
470
+ }
471
+
472
+ // Selector steps - handle Enter
473
+ if (matchesKey(keyData, "enter") || matchesKey(keyData, "return") || keyData === "\n") {
474
+ this.#selectCurrentOption();
475
+ return;
476
+ }
477
+
478
+ // Handle up/down arrows for selectors
479
+ if (matchesKey(keyData, "up")) {
480
+ this.#moveSelection(-1);
481
+ return;
482
+ }
483
+ if (matchesKey(keyData, "down")) {
484
+ this.#moveSelection(1);
485
+ return;
486
+ }
487
+ }
488
+
489
+ #saveInputAndProceed(): void {
490
+ if (!this.#inputField) return;
491
+
492
+ const value = this.#inputField.getValue().trim();
493
+
494
+ switch (this.#currentStep) {
495
+ case "name": {
496
+ // Validate server name
497
+ const nameError = validateServerName(value);
498
+ if (nameError) {
499
+ this.#validationError = nameError;
500
+ this.#renderStep();
501
+ return;
502
+ }
503
+ this.#validationError = null;
504
+ this.#state.name = value;
505
+ this.#currentStep = "transport";
506
+ this.#selectedIndex = 0;
507
+ break;
508
+ }
509
+ case "command":
510
+ if (!value) {
511
+ // Command is required
512
+ return;
513
+ }
514
+ this.#state.command = value;
515
+ this.#currentStep = "args";
516
+ break;
517
+ case "args":
518
+ this.#state.args = value; // Optional
519
+ void this.#testConnectionAndDetectAuth();
520
+ return;
521
+ case "url": {
522
+ // Validate URL
523
+ if (!value) {
524
+ this.#validationError = "URL is required";
525
+ this.#renderStep();
526
+ return;
527
+ }
528
+ let parsedUrl: URL;
529
+ try {
530
+ parsedUrl = new URL(value);
531
+ } catch {
532
+ this.#validationError = "Invalid URL format (must start with http:// or https://)";
533
+ this.#renderStep();
534
+ return;
535
+ }
536
+ if (parsedUrl.protocol !== "http:" && parsedUrl.protocol !== "https:") {
537
+ this.#validationError = "URL must use http:// or https:// scheme";
538
+ this.#renderStep();
539
+ return;
540
+ }
541
+ this.#validationError = null;
542
+ this.#state.url = value;
543
+ void this.#testConnectionAndDetectAuth();
544
+ return;
545
+ }
546
+ case "oauth-auth-url":
547
+ if (!value) return;
548
+ this.#state.oauthAuthUrl = value;
549
+ this.#currentStep = "oauth-token-url";
550
+ break;
551
+ case "oauth-token-url":
552
+ if (!value) return;
553
+ this.#state.oauthTokenUrl = value;
554
+ this.#currentStep = "oauth-client-id";
555
+ break;
556
+ case "oauth-client-id":
557
+ if (!value) return;
558
+ this.#state.oauthClientId = value;
559
+ this.#currentStep = "oauth-client-secret";
560
+ break;
561
+ case "oauth-client-secret":
562
+ this.#state.oauthClientSecret = value; // Optional
563
+ this.#currentStep = "oauth-scopes";
564
+ break;
565
+ case "oauth-scopes":
566
+ this.#state.oauthScopes = value; // Optional
567
+ // Launch OAuth flow
568
+ void this.#launchOAuthFlow();
569
+ return;
570
+ case "apikey":
571
+ if (!value) {
572
+ // API key is required
573
+ return;
574
+ }
575
+ this.#state.apiKey = value;
576
+ // Determine auth location based on transport
577
+ if (this.#state.transport === "stdio") {
578
+ this.#currentStep = "env-var-name";
579
+ } else {
580
+ this.#currentStep = "auth-location";
581
+ this.#selectedIndex = 0;
582
+ }
583
+ break;
584
+ case "env-var-name":
585
+ if (!value) {
586
+ return;
587
+ }
588
+ this.#state.envVarName = value;
589
+ this.#state.authLocation = "env";
590
+ this.#currentStep = "scope";
591
+ this.#selectedIndex = 0;
592
+ break;
593
+ case "header-name":
594
+ if (!value) {
595
+ return;
596
+ }
597
+ this.#state.headerName = value;
598
+ this.#state.authLocation = "header";
599
+ this.#currentStep = "scope";
600
+ this.#selectedIndex = 0;
601
+ break;
602
+ }
603
+
604
+ this.#inputField = null;
605
+ this.#renderStep();
606
+ }
607
+
608
+ #selectCurrentOption(): void {
609
+ switch (this.#currentStep) {
610
+ case "transport": {
611
+ const transports: TransportType[] = ["stdio", "http", "sse"];
612
+ this.#state.transport = transports[this.#selectedIndex];
613
+ this.#currentStep = this.#state.transport === "stdio" ? "command" : "url";
614
+ break;
615
+ }
616
+ case "auth-method": {
617
+ const authMethods: Array<"oauth" | "manual"> = ["oauth", "manual"];
618
+ this.#state.authMethod = authMethods[this.#selectedIndex];
619
+ if (this.#state.authMethod === "oauth") {
620
+ this.#currentStep = "oauth-auth-url";
621
+ } else {
622
+ // manual
623
+ this.#currentStep = "apikey";
624
+ }
625
+ break;
626
+ }
627
+ case "oauth-error":
628
+ if (this.#selectedIndex === 0) {
629
+ void this.#launchOAuthFlow();
630
+ } else {
631
+ this.#currentStep = "oauth-auth-url";
632
+ }
633
+ return;
634
+ case "auth-location": {
635
+ const authLocations: Array<"env" | "header"> = ["env", "header"];
636
+ this.#state.authLocation = authLocations[this.#selectedIndex];
637
+ if (this.#state.authLocation === "env") {
638
+ this.#currentStep = "env-var-name";
639
+ } else {
640
+ this.#currentStep = "header-name";
641
+ }
642
+ break;
643
+ }
644
+ case "scope": {
645
+ const scopes: Scope[] = ["user", "project"];
646
+ this.#state.scope = scopes[this.#selectedIndex];
647
+ this.#currentStep = "confirm";
648
+ this.#selectedIndex = 0;
649
+ break;
650
+ }
651
+ case "confirm": {
652
+ if (this.#selectedIndex === 0) {
653
+ this.#complete();
654
+ return;
655
+ }
656
+ this.#currentStep = "scope";
657
+ this.#selectedIndex = this.#state.scope === "user" ? 0 : 1;
658
+ break;
659
+ }
660
+ }
661
+
662
+ this.#renderStep();
663
+ }
664
+
665
+ #moveSelection(delta: number): void {
666
+ const maxIndex = this.#getMaxIndexForCurrentStep();
667
+ this.#selectedIndex = (this.#selectedIndex + delta + maxIndex + 1) % (maxIndex + 1);
668
+ this.#renderStep();
669
+ this.#requestRender();
670
+ }
671
+
672
+ #getMaxIndexForCurrentStep(): number {
673
+ switch (this.#currentStep) {
674
+ case "transport":
675
+ return 2; // 3 options
676
+ case "auth-method":
677
+ return 1; // 2 options
678
+ case "oauth-error":
679
+ return 1; // 2 options
680
+ case "auth-location":
681
+ return 1; // 2 options
682
+ case "scope":
683
+ return 1; // 2 options
684
+ case "confirm":
685
+ return 1; // 2 options
686
+ default:
687
+ return 0;
688
+ }
689
+ }
690
+
691
+ #goBack(): void {
692
+ // Navigate to previous step
693
+ switch (this.#currentStep) {
694
+ case "transport":
695
+ this.#currentStep = "name";
696
+ break;
697
+ case "command":
698
+ case "url":
699
+ this.#currentStep = "transport";
700
+ this.#selectedIndex = this.#state.transport === "stdio" ? 0 : this.#state.transport === "http" ? 1 : 2;
701
+ break;
702
+ case "args":
703
+ this.#currentStep = "command";
704
+ break;
705
+ case "auth-method":
706
+ // Go back to url or args depending on transport
707
+ if (this.#state.transport === "stdio") {
708
+ this.#currentStep = "args";
709
+ } else {
710
+ this.#currentStep = "url";
711
+ }
712
+ break;
713
+ case "oauth-auth-url":
714
+ case "apikey":
715
+ // Go back to transport-specific connection step
716
+ if (this.#state.transport === "stdio") {
717
+ this.#currentStep = "args";
718
+ } else {
719
+ this.#currentStep = "url";
720
+ }
721
+ break;
722
+ case "auth-location":
723
+ // Go back to API key input
724
+ this.#currentStep = "apikey";
725
+ break;
726
+ case "env-var-name":
727
+ case "header-name":
728
+ // Go back to auth location selection (for HTTP) or directly to apikey (for stdio)
729
+ if (this.#state.transport === "stdio") {
730
+ this.#currentStep = "apikey";
731
+ } else {
732
+ this.#currentStep = "auth-location";
733
+ this.#selectedIndex = this.#state.authLocation === "env" ? 0 : 1;
734
+ }
735
+ break;
736
+ case "oauth-token-url":
737
+ case "oauth-client-id":
738
+ case "oauth-client-secret":
739
+ case "oauth-scopes":
740
+ // Go back through OAuth flow
741
+ if (this.#currentStep === "oauth-token-url") {
742
+ this.#currentStep = "oauth-auth-url";
743
+ } else if (this.#currentStep === "oauth-client-id") {
744
+ this.#currentStep = "oauth-token-url";
745
+ } else if (this.#currentStep === "oauth-client-secret") {
746
+ this.#currentStep = "oauth-client-id";
747
+ } else if (this.#currentStep === "oauth-scopes") {
748
+ this.#currentStep = "oauth-client-secret";
749
+ }
750
+ break;
751
+ case "scope":
752
+ // Go back to last authentication step
753
+ if (this.#state.authMethod === "oauth") {
754
+ this.#currentStep = "oauth-scopes";
755
+ } else {
756
+ // manual - go back to env var name or header name
757
+ if (this.#state.authLocation === "env") {
758
+ this.#currentStep = "env-var-name";
759
+ } else {
760
+ this.#currentStep = "header-name";
761
+ }
762
+ }
763
+ break;
764
+ case "oauth-error":
765
+ this.#currentStep = "oauth-auth-url";
766
+ break;
767
+ case "confirm":
768
+ this.#currentStep = "scope";
769
+ this.#selectedIndex = this.#state.scope === "user" ? 0 : 1;
770
+ break;
771
+ }
772
+
773
+ this.#renderStep();
774
+ }
775
+
776
+ #renderAuthMethodStep(): void {
777
+ this.#contentContainer.addChild(new Text(theme.fg("accent", "Step: Authentication Method")));
778
+ this.#contentContainer.addChild(new Spacer(1));
779
+
780
+ const options = [
781
+ { value: "oauth" as const, label: "OAuth flow (web-based)", desc: "(opens browser)" },
782
+ { value: "manual" as const, label: "Manual API key/token", desc: "(paste or use shell command)" },
783
+ ];
784
+
785
+ for (let i = 0; i < options.length; i++) {
786
+ const option = options[i];
787
+ const isSelected = i === this.#selectedIndex;
788
+ const prefix = isSelected ? theme.fg("accent", `${theme.nav.cursor} `) : " ";
789
+ const text = isSelected ? theme.fg("accent", option.label) : option.label;
790
+ this.#contentContainer.addChild(new Text(prefix + text, 0, 0));
791
+ if (!isSelected) {
792
+ this.#contentContainer.addChild(new Text(` ${theme.fg("dim", option.desc)}`, 0, 0));
793
+ }
794
+ }
795
+
796
+ this.#contentContainer.addChild(new Spacer(1));
797
+ this.#contentContainer.addChild(
798
+ new Text(theme.fg("muted", "[↑↓ to navigate, Enter to select, Esc to go back]"), 0, 0),
799
+ );
800
+ }
801
+
802
+ #renderOAuthAuthUrlStep(): void {
803
+ this.#contentContainer.addChild(new Text(theme.fg("accent", "OAuth: Authorization URL")));
804
+ this.#contentContainer.addChild(new Spacer(1));
805
+ this.#contentContainer.addChild(new Text("Enter the OAuth authorization endpoint:", 0, 0));
806
+ this.#contentContainer.addChild(new Spacer(1));
807
+
808
+ this.#inputField = new Input();
809
+ this.#inputField.setValue(this.#state.oauthAuthUrl);
810
+ this.#contentContainer.addChild(this.#inputField);
811
+ this.#contentContainer.addChild(new Spacer(1));
812
+ this.#contentContainer.addChild(
813
+ new Text(theme.fg("muted", "e.g., https://auth.example.com/oauth/authorize"), 0, 0),
814
+ );
815
+ this.#contentContainer.addChild(new Spacer(1));
816
+ this.#contentContainer.addChild(new Text(theme.fg("muted", "[Enter to continue, Esc to go back]"), 0, 0));
817
+ }
818
+
819
+ #renderOAuthTokenUrlStep(): void {
820
+ this.#contentContainer.addChild(new Text(theme.fg("accent", "OAuth: Token URL")));
821
+ this.#contentContainer.addChild(new Spacer(1));
822
+ this.#contentContainer.addChild(new Text("Enter the OAuth token endpoint:", 0, 0));
823
+ this.#contentContainer.addChild(new Spacer(1));
824
+
825
+ this.#inputField = new Input();
826
+ this.#inputField.setValue(this.#state.oauthTokenUrl);
827
+ this.#contentContainer.addChild(this.#inputField);
828
+ this.#contentContainer.addChild(new Spacer(1));
829
+ this.#contentContainer.addChild(new Text(theme.fg("muted", "e.g., https://auth.example.com/oauth/token"), 0, 0));
830
+ this.#contentContainer.addChild(new Spacer(1));
831
+ this.#contentContainer.addChild(new Text(theme.fg("muted", "[Enter to continue, Esc to go back]"), 0, 0));
832
+ }
833
+
834
+ #renderOAuthClientIdStep(): void {
835
+ this.#contentContainer.addChild(new Text(theme.fg("accent", "OAuth: Client ID")));
836
+ this.#contentContainer.addChild(new Spacer(1));
837
+ this.#contentContainer.addChild(new Text("Enter your OAuth client ID:", 0, 0));
838
+ this.#contentContainer.addChild(new Spacer(1));
839
+
840
+ this.#inputField = new Input();
841
+ this.#inputField.setValue(this.#state.oauthClientId);
842
+ this.#contentContainer.addChild(this.#inputField);
843
+ this.#contentContainer.addChild(new Spacer(1));
844
+ this.#contentContainer.addChild(new Text(theme.fg("muted", "[Enter to continue, Esc to go back]"), 0, 0));
845
+ }
846
+
847
+ #renderOAuthClientSecretStep(): void {
848
+ this.#contentContainer.addChild(new Text(theme.fg("accent", "OAuth: Client Secret (Optional)")));
849
+ this.#contentContainer.addChild(new Spacer(1));
850
+ this.#contentContainer.addChild(new Text("Enter your OAuth client secret:", 0, 0));
851
+ this.#contentContainer.addChild(new Text(theme.fg("muted", "(Leave empty for PKCE-only flows)"), 0, 0));
852
+ this.#contentContainer.addChild(new Spacer(1));
853
+
854
+ this.#inputField = new Input();
855
+ this.#inputField.setValue(this.#state.oauthClientSecret);
856
+ this.#contentContainer.addChild(this.#inputField);
857
+ this.#contentContainer.addChild(new Spacer(1));
858
+ this.#contentContainer.addChild(new Text(theme.fg("muted", "[Enter to continue, Esc to go back]"), 0, 0));
859
+ }
860
+
861
+ #renderOAuthScopesStep(): void {
862
+ this.#contentContainer.addChild(new Text(theme.fg("accent", "OAuth: Scopes (Optional)")));
863
+ this.#contentContainer.addChild(new Spacer(1));
864
+ this.#contentContainer.addChild(new Text("Enter OAuth scopes (space-separated):", 0, 0));
865
+ this.#contentContainer.addChild(new Spacer(1));
866
+
867
+ this.#inputField = new Input();
868
+ this.#inputField.setValue(this.#state.oauthScopes);
869
+ this.#contentContainer.addChild(this.#inputField);
870
+ this.#contentContainer.addChild(new Spacer(1));
871
+ this.#contentContainer.addChild(new Text(theme.fg("muted", "e.g., read write"), 0, 0));
872
+ this.#contentContainer.addChild(new Spacer(1));
873
+ this.#contentContainer.addChild(new Text(theme.fg("muted", "[Enter to continue, Esc to go back]"), 0, 0));
874
+ }
875
+
876
+ #renderOAuthErrorStep(): void {
877
+ this.#contentContainer.addChild(new Text(theme.fg("error", "OAuth authentication failed"), 0, 0));
878
+ this.#contentContainer.addChild(new Spacer(1));
879
+ this.#contentContainer.addChild(new Text("Choose next action:", 0, 0));
880
+ this.#contentContainer.addChild(new Spacer(1));
881
+
882
+ const options = ["Retry OAuth authentication", "Edit OAuth settings"];
883
+ for (let i = 0; i < options.length; i++) {
884
+ const isSelected = i === this.#selectedIndex;
885
+ const prefix = isSelected ? theme.fg("accent", `${theme.nav.cursor} `) : " ";
886
+ const text = isSelected ? theme.fg("accent", options[i]) : options[i];
887
+ this.#contentContainer.addChild(new Text(prefix + text, 0, 0));
888
+ }
889
+
890
+ this.#contentContainer.addChild(new Spacer(1));
891
+ this.#contentContainer.addChild(
892
+ new Text(theme.fg("muted", "[↑↓ to navigate, Enter to select, Esc to go back]"), 0, 0),
893
+ );
894
+ }
895
+
896
+ #renderApiKeyStep(): void {
897
+ this.#contentContainer.addChild(new Text(theme.fg("accent", "API Key Required")));
898
+ this.#contentContainer.addChild(new Spacer(1));
899
+ this.#contentContainer.addChild(new Text("Enter your API key or token:", 0, 0));
900
+ this.#contentContainer.addChild(new Text(theme.fg("muted", "(Supports !command for password manager)"), 0, 0));
901
+ this.#contentContainer.addChild(new Spacer(1));
902
+
903
+ this.#inputField = new Input();
904
+ this.#inputField.setValue(this.#state.apiKey);
905
+ this.#contentContainer.addChild(this.#inputField);
906
+ this.#contentContainer.addChild(new Spacer(1));
907
+ this.#contentContainer.addChild(new Text(theme.fg("muted", "[Enter to continue, Esc to go back]"), 0, 0));
908
+ }
909
+
910
+ /**
911
+ * Test connection and automatically detect if auth is needed.
912
+ */
913
+ async #testConnectionAndDetectAuth(): Promise<void> {
914
+ const testConfig = this.#buildServerConfig();
915
+
916
+ if (!this.#onTestConnectionCallback) {
917
+ // Skip test, go to scope
918
+ this.#currentStep = "scope";
919
+ this.#selectedIndex = 0;
920
+ this.#renderStep();
921
+ return;
922
+ }
923
+
924
+ try {
925
+ // Try to connect - timeout is handled by the transport layer (5 seconds)
926
+ await this.#onTestConnectionCallback(testConfig);
927
+
928
+ // Success! No auth required
929
+ this.#contentContainer.clear();
930
+ this.#contentContainer.addChild(new Text(theme.fg("success", "✓ Connection successful!"), 0, 0));
931
+ this.#contentContainer.addChild(new Spacer(1));
932
+ this.#contentContainer.addChild(new Text("No authentication required", 0, 0));
933
+ this.#contentContainer.addChild(new Spacer(1));
934
+
935
+ setTimeout(() => {
936
+ this.#state.authMethod = "none";
937
+ this.#currentStep = "scope";
938
+ this.#selectedIndex = 0;
939
+ this.#renderStep();
940
+ }, 1000);
941
+ } catch (error) {
942
+ // Connection failed - check if it's an auth error
943
+ const authResult = analyzeAuthError(error as Error);
944
+
945
+ if (authResult.requiresAuth) {
946
+ // Prefer OAuth first: use error metadata, then well-known discovery fallback.
947
+ let oauth = authResult.authType === "oauth" ? (authResult.oauth ?? null) : null;
948
+ if (!oauth && this.#state.transport !== "stdio" && this.#state.url) {
949
+ try {
950
+ oauth = await discoverOAuthEndpoints(this.#state.url);
951
+ } catch {
952
+ // Ignore discovery failures and fallback to manual auth.
953
+ }
954
+ }
955
+
956
+ if (oauth) {
957
+ this.#state.oauthAuthUrl = oauth.authorizationUrl;
958
+ this.#state.oauthTokenUrl = oauth.tokenUrl;
959
+ this.#state.oauthClientId = oauth.clientId || "";
960
+ this.#state.oauthScopes = oauth.scopes || "";
961
+ this.#state.authMethod = "oauth";
962
+
963
+ this.#contentContainer.clear();
964
+ this.#contentContainer.addChild(new Text(theme.fg("success", "✓ OAuth detected"), 0, 0));
965
+ this.#contentContainer.addChild(new Spacer(1));
966
+ this.#contentContainer.addChild(new Text("Launching browser for authorization...", 0, 0));
967
+ this.#contentContainer.addChild(new Spacer(1));
968
+
969
+ void this.#launchOAuthFlow();
970
+ return;
971
+ }
972
+
973
+ // OAuth metadata unavailable: fallback to manual API key.
974
+ this.#contentContainer.clear();
975
+ this.#contentContainer.addChild(new Text(theme.fg("warning", "⚠ Authentication required"), 0, 0));
976
+ this.#contentContainer.addChild(new Spacer(1));
977
+ this.#contentContainer.addChild(new Text("OAuth parameters could not be discovered.", 0, 0));
978
+ this.#contentContainer.addChild(new Text("Provide API key/token manually.", 0, 0));
979
+ this.#contentContainer.addChild(new Spacer(1));
980
+ this.#currentStep = "apikey";
981
+ this.#renderStep();
982
+ } else {
983
+ // Not an auth error - just a connection failure
984
+ const errorMsg = sanitize(error instanceof Error ? error.message : String(error));
985
+ this.#contentContainer.clear();
986
+ this.#contentContainer.addChild(new Text(theme.fg("error", "✗ Connection failed"), 0, 0));
987
+ this.#contentContainer.addChild(new Spacer(1));
988
+ this.#contentContainer.addChild(new Text(errorMsg, 0, 0));
989
+ this.#contentContainer.addChild(new Spacer(1));
990
+ this.#contentContainer.addChild(new Text(theme.fg("muted", "Adding server anyway..."), 0, 0));
991
+
992
+ setTimeout(() => {
993
+ this.#state.authMethod = "none";
994
+ this.#currentStep = "scope";
995
+ this.#selectedIndex = 0;
996
+ this.#renderStep();
997
+ }, 2000);
998
+ }
999
+ }
1000
+ }
1001
+
1002
+ /**
1003
+ * Build a server config from current wizard state for connection testing (no auth).
1004
+ */
1005
+ #buildServerConfig(): MCPServerConfig {
1006
+ return this.#buildServerConfigWithAuth(false);
1007
+ }
1008
+
1009
+ #buildServerConfigWithAuth(includeAuth: boolean): MCPServerConfig {
1010
+ const transport = this.#state.transport ?? "stdio";
1011
+
1012
+ if (transport === "stdio") {
1013
+ const config: MCPStdioServerConfig = {
1014
+ type: "stdio",
1015
+ command: this.#state.command,
1016
+ timeout: 5000,
1017
+ };
1018
+
1019
+ if (this.#state.args) {
1020
+ config.args = this.#state.args.split(/\s+/).filter(Boolean);
1021
+ }
1022
+
1023
+ if (includeAuth && this.#state.authMethod === "oauth" && this.#state.oauthCredentialId) {
1024
+ config.auth = {
1025
+ type: "oauth",
1026
+ credentialId: this.#state.oauthCredentialId,
1027
+ };
1028
+ }
1029
+
1030
+ if (includeAuth && this.#state.authMethod === "manual" && this.#state.apiKey) {
1031
+ config.env = {
1032
+ ...(config.env ?? {}),
1033
+ [this.#state.envVarName || "API_KEY"]: this.#state.apiKey,
1034
+ };
1035
+ }
1036
+
1037
+ return config;
1038
+ }
1039
+
1040
+ // http or sse
1041
+ const config: MCPHttpServerConfig | MCPSseServerConfig = {
1042
+ type: transport,
1043
+ url: this.#state.url,
1044
+ timeout: 5000,
1045
+ };
1046
+
1047
+ if (includeAuth && this.#state.authMethod === "oauth" && this.#state.oauthCredentialId) {
1048
+ config.auth = {
1049
+ type: "oauth",
1050
+ credentialId: this.#state.oauthCredentialId,
1051
+ };
1052
+ }
1053
+
1054
+ if (includeAuth && this.#state.authMethod === "manual" && this.#state.apiKey) {
1055
+ if (this.#state.authLocation === "env") {
1056
+ // For HTTP with env location, store in headers using the env var name as-is
1057
+ config.headers = {
1058
+ ...(config.headers ?? {}),
1059
+ [this.#state.headerName || "Authorization"]: this.#state.apiKey,
1060
+ };
1061
+ } else {
1062
+ const headerName = this.#state.headerName || "Authorization";
1063
+ config.headers = {
1064
+ ...(config.headers ?? {}),
1065
+ [headerName]: this.#state.apiKey,
1066
+ };
1067
+ }
1068
+ }
1069
+
1070
+ return config;
1071
+ }
1072
+
1073
+ async #launchOAuthFlow(): Promise<void> {
1074
+ if (!this.#onOAuthCallback) {
1075
+ this.#contentContainer.clear();
1076
+ this.#contentContainer.addChild(new Text(theme.fg("error", "OAuth flow not available"), 0, 0));
1077
+ this.#renderStep();
1078
+ this.#requestRender();
1079
+ return;
1080
+ }
1081
+
1082
+ // Validate OAuth configuration
1083
+ if (!this.#state.oauthAuthUrl || !this.#state.oauthTokenUrl) {
1084
+ this.#contentContainer.clear();
1085
+ this.#contentContainer.addChild(new Text(theme.fg("error", "OAuth configuration incomplete"), 0, 0));
1086
+ this.#contentContainer.addChild(new Spacer(1));
1087
+ this.#contentContainer.addChild(new Text("Authorization and Token URLs are required.", 0, 0));
1088
+ this.#contentContainer.addChild(new Spacer(1));
1089
+ this.#contentContainer.addChild(new Text(theme.fg("muted", "[Press Esc to go back]"), 0, 0));
1090
+ this.#requestRender();
1091
+ return;
1092
+ }
1093
+
1094
+ // Show "Authenticating..." message
1095
+ this.#contentContainer.clear();
1096
+ this.#contentContainer.addChild(new Text(theme.fg("accent", "OAuth Authentication"), 0, 0));
1097
+ this.#contentContainer.addChild(new Spacer(1));
1098
+ this.#contentContainer.addChild(new Text("Launching OAuth flow...", 0, 0));
1099
+ this.#contentContainer.addChild(new Text(theme.fg("muted", "Browser will open automatically."), 0, 0));
1100
+ this.#contentContainer.addChild(new Spacer(1));
1101
+ this.#contentContainer.addChild(
1102
+ new Text(theme.fg("warning", "If browser doesn't open, copy the URL from chat."), 0, 0),
1103
+ );
1104
+ this.#contentContainer.addChild(new Spacer(1));
1105
+ this.#contentContainer.addChild(new Text(theme.fg("muted", "(Press Esc to cancel)"), 0, 0));
1106
+ this.#requestRender();
1107
+
1108
+ try {
1109
+ // Call OAuth handler
1110
+ const credentialId = await this.#onOAuthCallback(
1111
+ this.#state.oauthAuthUrl,
1112
+ this.#state.oauthTokenUrl,
1113
+ this.#state.oauthClientId,
1114
+ this.#state.oauthClientSecret,
1115
+ this.#state.oauthScopes,
1116
+ );
1117
+
1118
+ // Store credential ID
1119
+ this.#state.oauthCredentialId = credentialId;
1120
+
1121
+ // Show success message
1122
+ this.#contentContainer.clear();
1123
+ this.#contentContainer.addChild(new Text(theme.fg("success", "✓ Authentication successful!"), 0, 0));
1124
+ this.#contentContainer.addChild(new Spacer(1));
1125
+ this.#contentContainer.addChild(new Text(theme.fg("muted", "Running connection health check..."), 0, 0));
1126
+ const healthText = new Text(theme.fg("muted", "| Checking server connection..."), 0, 0);
1127
+ this.#contentContainer.addChild(healthText);
1128
+
1129
+ const spinnerFrames = ["|", "/", "-", "\\"];
1130
+ let spinnerIndex = 0;
1131
+ const spinner = setInterval(() => {
1132
+ healthText.setText(
1133
+ theme.fg("muted", `${spinnerFrames[spinnerIndex % spinnerFrames.length]} Checking server connection...`),
1134
+ );
1135
+ spinnerIndex++;
1136
+ this.#requestRender();
1137
+ }, 120);
1138
+
1139
+ let healthPassed = true;
1140
+ let healthError = "";
1141
+ if (this.#onTestConnectionCallback) {
1142
+ try {
1143
+ const { promise: timeoutPromise, reject: timeoutReject } = Promise.withResolvers<never>();
1144
+ const timer = setTimeout(
1145
+ () => timeoutReject(new Error("Health check timed out after 10 seconds")),
1146
+ 10_000,
1147
+ );
1148
+ try {
1149
+ await Promise.race([
1150
+ this.#onTestConnectionCallback(this.#buildServerConfigWithAuth(true)),
1151
+ timeoutPromise,
1152
+ ]);
1153
+ } finally {
1154
+ clearTimeout(timer);
1155
+ }
1156
+ } catch (error) {
1157
+ healthPassed = false;
1158
+ healthError = sanitize(error instanceof Error ? error.message : String(error));
1159
+ }
1160
+ }
1161
+
1162
+ clearInterval(spinner);
1163
+ if (healthPassed) {
1164
+ healthText.setText(theme.fg("success", "✓ Health check passed"));
1165
+ } else {
1166
+ healthText.setText(theme.fg("warning", "⚠ Health check failed (will still save config)"));
1167
+ this.#contentContainer.addChild(new Spacer(1));
1168
+ this.#contentContainer.addChild(new Text(theme.fg("muted", healthError), 0, 0));
1169
+ }
1170
+ this.#requestRender();
1171
+
1172
+ // Move to scope selection after short delay
1173
+ setTimeout(
1174
+ () => {
1175
+ this.#currentStep = "scope";
1176
+ this.#selectedIndex = 0;
1177
+ this.#renderStep();
1178
+ this.#requestRender();
1179
+ },
1180
+ healthPassed ? 1000 : 2000,
1181
+ );
1182
+ } catch (error) {
1183
+ // Show error with options to retry or go back
1184
+ const errorMsg = sanitize(error instanceof Error ? error.message : String(error));
1185
+ this.#contentContainer.clear();
1186
+ this.#contentContainer.addChild(new Text(theme.fg("error", "✗ OAuth authentication failed"), 0, 0));
1187
+ this.#contentContainer.addChild(new Spacer(1));
1188
+ this.#contentContainer.addChild(new Text(errorMsg, 0, 0));
1189
+ this.#contentContainer.addChild(new Spacer(1));
1190
+
1191
+ // Provide helpful tips based on error type
1192
+ if (errorMsg.includes("timeout") || errorMsg.includes("timed out")) {
1193
+ this.#contentContainer.addChild(
1194
+ new Text(theme.fg("muted", "Tip: Complete authorization faster next time"), 0, 0),
1195
+ );
1196
+ } else if (errorMsg.includes("Invalid OAuth URLs")) {
1197
+ this.#contentContainer.addChild(
1198
+ new Text(theme.fg("muted", "Tip: Check that the OAuth URLs are correct"), 0, 0),
1199
+ );
1200
+ } else if (errorMsg.includes("ECONNREFUSED")) {
1201
+ this.#contentContainer.addChild(
1202
+ new Text(theme.fg("muted", "Tip: Verify the OAuth server is accessible"), 0, 0),
1203
+ );
1204
+ }
1205
+
1206
+ this.#contentContainer.addChild(new Spacer(1));
1207
+ this.#contentContainer.addChild(new Text(`${theme.fg("accent", "→ ")}Retry`, 0, 0));
1208
+ this.#contentContainer.addChild(new Text(" Edit OAuth settings", 0, 0));
1209
+ this.#contentContainer.addChild(new Spacer(1));
1210
+ this.#contentContainer.addChild(
1211
+ new Text(theme.fg("muted", "[↑↓ to navigate, Enter to select, Esc to go back]"), 0, 0),
1212
+ );
1213
+ this.#requestRender();
1214
+
1215
+ // Set up as a selector step
1216
+ this.#selectedIndex = 0;
1217
+ this.#currentStep = "oauth-error";
1218
+ }
1219
+ }
1220
+
1221
+ #complete(): void {
1222
+ if (!this.#state.scope) return;
1223
+
1224
+ // Build the config
1225
+ const config: MCPServerConfig = this.#buildConfig();
1226
+
1227
+ // Call completion callback
1228
+ this.#onCompleteCallback(this.#state.name, config, this.#state.scope);
1229
+ }
1230
+
1231
+ #buildConfig(): MCPServerConfig {
1232
+ if (this.#state.transport === "stdio") {
1233
+ const config: MCPStdioServerConfig = {
1234
+ type: "stdio",
1235
+ command: this.#state.command,
1236
+ };
1237
+
1238
+ if (this.#state.args) {
1239
+ config.args = this.#state.args.split(/\s+/).filter(Boolean);
1240
+ }
1241
+
1242
+ // Add OAuth auth if configured
1243
+ if (this.#state.authMethod === "oauth" && this.#state.oauthCredentialId) {
1244
+ config.auth = {
1245
+ type: "oauth",
1246
+ credentialId: this.#state.oauthCredentialId,
1247
+ };
1248
+ }
1249
+
1250
+ // Add API key to env if manual auth — use user-chosen env var name
1251
+ if (this.#state.authMethod === "manual" && this.#state.apiKey) {
1252
+ const envKey = this.#state.envVarName || "API_KEY";
1253
+ config.env = {
1254
+ [envKey]: this.#state.apiKey,
1255
+ };
1256
+ }
1257
+
1258
+ return config;
1259
+ }
1260
+
1261
+ // HTTP or SSE — use concrete type
1262
+ const config: MCPHttpServerConfig | MCPSseServerConfig = {
1263
+ type: this.#state.transport!,
1264
+ url: this.#state.url,
1265
+ };
1266
+
1267
+ // Add OAuth auth if configured
1268
+ if (this.#state.authMethod === "oauth" && this.#state.oauthCredentialId) {
1269
+ config.auth = {
1270
+ type: "oauth",
1271
+ credentialId: this.#state.oauthCredentialId,
1272
+ };
1273
+ }
1274
+
1275
+ // Add API key using user-chosen header name and auth location
1276
+ if (this.#state.authMethod === "manual" && this.#state.apiKey) {
1277
+ if (this.#state.authLocation === "env") {
1278
+ // Env-based auth for HTTP: store the key in env on the config
1279
+ // HTTP/SSE configs don't have an env field, so use headers as carrier
1280
+ const headerName = this.#state.headerName || "Authorization";
1281
+ config.headers = {
1282
+ [headerName]: this.#state.apiKey,
1283
+ };
1284
+ } else {
1285
+ // Header-based auth: use the user's chosen header name
1286
+ const headerName = this.#state.headerName || "Authorization";
1287
+ config.headers = {
1288
+ [headerName]: this.#state.apiKey,
1289
+ };
1290
+ }
1291
+ }
1292
+
1293
+ return config;
1294
+ }
1295
+ }